Зачем использовать 'divl' при выполнении деления int/unsigned int

Я тестировал этот код в 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?


person Yang Bo    schedule 18.11.2012    source источник
comment
stackoverflow.com/ вопросы/12489098/   -  person Orwell    schedule 18.11.2012
comment
@Orwell: этот вопрос объясняет, что делают эти инструкции, а не то, почему компилятор предпочел одну другой. Правильным ответом в этом случае будет цитата из стандарта C/C++ с подробным описанием преобразования/продвижения типов во время математических операций (деление целых чисел со знаком и без знака).   -  person DCoder    schedule 18.11.2012
comment
Я вижу 3 divl и только один idivl в вашей вставленной сборке. Ваши комментарии в C предполагают, что это должно быть 2 и 2...   -  person Damien_The_Unbeliever    schedule 18.11.2012
comment
Компилятор использует беззнаковое деление, когда речь идет о беззнаковых значениях. В чем сюрприз?   -  person Bo Persson    schedule 18.11.2012


Ответы (1)


Поскольку типы a и c имеют одинаковый ранг преобразования, но a является знаковым, а c беззнаковым, a перед делением преобразуется в unsigned int как в a / c, так и в c / a.

Таким образом, компилятор выдает беззнаковую инструкцию деления div для этих случаев (а также c / d, где оба операнда беззнаковые).

Умножение a * c также является умножением без знака. В этом случае компилятору может сойти с рук использование инструкции умножения со знаком imull, потому что усеченный результат идентичен независимо от того, используется ли mull или imull - различаются только флаги, и сгенерированный код их не проверяет.

person caf    schedule 18.11.2012
comment
Это не продвигается, это конвертируется. Преобразование обычно более неожиданно, чем продвижение (например, -1 преобразуется в 4294967295, чего не может быть при повышении). - person anatolyg; 18.11.2012
comment
Я пишу код для проверки, это так, int преобразуется в unsigned int перед делением... printf(%d\n, ((unsigned int)10)/-1); выведет НОЛЬ, что кажется очень странным! caf вы правы, большое спасибо! - person Yang Bo; 19.11.2012
comment
Кстати, такое преобразование определенно вызовет неожиданное поведение, могу ли я узнать, почему C решил сделать преобразование таким образом? Почему бы не преобразовать оба в подписанные, мы все равно расширили биты в регистре. - person Yang Bo; 19.11.2012