Я тестировал этот код в X86.
void func()
{
int a, b;
unsigned int c, d;
int ret;
ret = a / b; // This line use idivl, expected
ret = c / d; // this line use idivl, expected
ret = a / c; // this line use divl..., surprised
ret = c / a; // this line use divl..., supriised
ret = a * c; // this line use imull, expected
}
Я вставляю код сборки здесь:
func:
pushl %ebp
movl %esp, %ebp
subl $36, %esp
movl -4(%ebp), %eax
movl %eax, %edx
sarl $31, %edx
idivl -8(%ebp)
movl %eax, -20(%ebp)
movl -12(%ebp), %eax
movl $0, %edx
divl -16(%ebp)
movl %eax, -20(%ebp)
movl -4(%ebp), %eax
movl $0, %edx
divl -12(%ebp)
movl %eax, -20(%ebp)
movl -4(%ebp), %eax
movl %eax, -36(%ebp)
movl -12(%ebp), %eax
movl $0, %edx
divl -36(%ebp)
movl %eax, -20(%ebp)
movl -4(%ebp), %eax
imull -12(%ebp), %eax
movl %eax, -20(%ebp)
leave
ret
Скажите, пожалуйста, почему для разделения между int и unsigned int используется divl, а не idivl?
Так как типы a
а также c
имеют одинаковый рейтинг конверсии, но a
подписан и c
без знака, a
преобразуется в unsigned int
до разделения, в обоих a / c
а также c / a
,
Таким образом, компилятор выдает беззнаковую инструкцию деления div
для этих случаев (а также c / d
где оба операнда не подписаны).
Умножение a * c
также является беззнаковым умножением. В этом случае компилятор может использовать подписанную инструкцию умножения imull
потому что усеченный результат одинаков независимо от того, mull
или же imull
используется — отличаются только флаги, и сгенерированный код их не проверяет.
Других решений пока нет …