Сборка x86: инструкция INC и DEC и флаг переполнения

В сборке x86 флаг переполнения устанавливается, когда add или же sub операция над целочисленными значениями со знаком переполняется, и флаг переноса устанавливается, когда операция над целочисленными значениями без знака переполняется.

Однако когда дело доходит до inc а также dec В инструкции ситуация выглядит несколько иначе. Согласно этому сайту, inc инструкция не влияет на флаг переноса вообще.

Но я не могу найти информацию о том, как inc а также dec влияют на флаг переполнения, если вообще.

Делать inc или же dec установить флаг переполнения при возникновении целочисленного переполнения? И одинаково ли это поведение для целых чисел со знаком и без знака?

============================= РЕДАКТИРОВАТЬ ==================== =========

Итак, по сути, консенсус здесь заключается в том, что INC и DEC должны вести себя так же, как ADD и SUB, с точки зрения установки флагов, за исключением флага переноса. Это также то, что говорится в руководстве Intel.

Проблема в том, что я не могу воспроизвести это поведение на практике, когда речь идет о целых числах без знака.

Рассмотрим следующий код сборки (используя встроенную сборку GCC, чтобы упростить вывод результатов).

int8_t ovf = 0;

__asm__
(
    "movb $-128, %%bh;"
    "decb %%bh;"
    "seto %b0;"
    : "=g"(ovf)
    :
    : "%bh"
);

printf("Overflow flag: %d\n", ovf);

Здесь мы уменьшаем 8-битное значение со знаком -128. Поскольку -128 является наименьшим возможным значением, переполнение неизбежно. Как и ожидалось, это распечатывает: Overflow flag: 1

Но когда мы делаем то же самое со значением без знака, поведение не такое, как я ожидаю:

int8_t ovf = 0;

__asm__
(
    "movb $255, %%bh;"
    "incb %%bh;"
    "seto %b0;"
    : "=g"(ovf)
    :
    : "%bh"
);

printf("Overflow flag: %d\n", ovf);

Здесь я увеличиваю 8-битное значение без знака на 255. Поскольку 255 является наибольшим возможным значением, переполнение неизбежно. Тем не менее, это печатает: Overflow flag: 0,

А? Почему он не установил флаг переполнения в этом случае?

7 ответов

Решение

Флаг переполнения устанавливается, когда операция вызывает изменение знака. Ваш код очень близок Мне удалось установить флаг OF с помощью следующего (VC++) кода:

char ovf = 0;

_asm {
    mov bh, 127
    inc bh
    seto ovf
}
cout << "ovf: " << int(ovf) << endl;

Когда BH увеличивается, MSB изменяется с 0 на 1, вызывая установку OF.

Это также устанавливает OF:

char ovf = 0;

_asm {
    mov bh, 128
    dec bh
    seto ovf
}
cout << "ovf: " << int(ovf) << endl;

Имейте в виду, что процессор не различает числа со знаком и без знака. Когда вы используете арифметику дополнения 2, вы можете иметь один набор инструкций, который обрабатывает оба. Если вы хотите проверить переполнение без знака, вам нужно использовать флаг переноса. Поскольку INC/DEC не влияет на флаг переноса, для этого случая необходимо использовать ADD/SUB.

Попробуйте изменить свой тест, чтобы передать число, а не жестко его кодировать, а затем создайте цикл, в котором все 256 чисел пытаются найти то, которое влияет на флаг, если оно есть. Или попросите asm выполнить цикл и выйти из него, когда он достигнет флага и или когда он обернется к числу, с которого он начал (начните с чего-то, кроме 0x00, 0x7f, 0x80 или 0xFF).

РЕДАКТИРОВАТЬ

.globl inc
вкл:
    mov $33, %eax

Топ:
    вкл.% al
    Джо сделано
    JMP топ

сделанный:
    RET
.globl dec
декабрь:
    mov $33, %eax

topx:
    dec% al
    Джо Донекс
    JMP Topx

donex:
    RET

Inc переполняется при переходе от 0x7F к 0x80. dec переполняется при переходе от 0x80 к 0x7F, я подозреваю, что проблема в том, как вы используете встроенный ассемблер.

Как указывалось во многих других ответах, INC а также DEC не влияют на CF, в то время как ADD а также SUB делать.

Однако еще не сказано, что это может повлиять на производительность. Не то чтобы вас это обычно беспокоило, если вы не пытаетесь оптимизировать ад из рутины, но по сути не устанавливаете CF Значит это INC / DEC только запись в часть регистра флагов, что может привести к частичной остановке регистра флагов, см. Справочное руководство по оптимизации архитектур Intel 64 и IA-32 или руководства по оптимизации Agner Fog.

Руководства разработчика программного обеспечения для архитектур Intel® 64 и IA-32

Посмотрите на соответствующий справочник инструкций по эксплуатации , AM. Каждая инструкция точно документирована.

Вот раздел INC о затронутых флагах:

Флаг CF не затрагивается. Флаги OF, SZ, ZF, AZ и PF устанавливаются в соответствии с результатом.

CPU/ALU способен обрабатывать только двоичные числа без знака, а затем использует OF, CF, AF, SF, ZF и т. Д., Чтобы вы могли решить, использовать ли его в качестве числа со знаком (OF), числа без знака. (CF) или номер BCD (AF).



Что касается вашей проблемы, не забудьте считать сами двоичные числа беззнаковыми.

Кроме того, переполнение и OF требуют 3 числа: входной номер, второй номер для использования в арифметике и номер результата.

Переполнение активируется только в том случае, если первое и второе числа имеют одинаковое значение для знакового бита (самый старший бит), а результат имеет другой знак. Например, добавление 2 отрицательных чисел привело к положительному числу, или добавление 2 положительных чисел привело к отрицательному числу:

if( (Sign_Num1==Sign_Num2) && (Sign_Result!=Sign_Num1) ) OF=1;
else OF=0;


Для вашей первой проблемы вы используете -128 как первый номер. Второе число неявно -1 Используется инструкцией DEC. Таким образом, у нас действительно есть двоичные числа 0x80 а также 0xFF, Оба они имеют бит знака, установленный в 1. Результат 0x7F Это число с битом знака, установленным в 0. Мы получили 2 начальных числа с одним и тем же знаком и результат с другим знаком, поэтому мы указываем переполнение. -128-1 привело к 127 и, таким образом, флаг переполнения устанавливается для указания неверного подписанного результата.





Для вашей второй проблемы вы используете 255 как первый номер. Второе число неявно 1, используемый инструкцией INC. Таким образом, у нас действительно есть двоичные числа 0xFF а также 0x01, Оба они имеют разные биты знака, поэтому переполнение невозможно (переполнение возможно только при сложении двух чисел с одним и тем же знаком, но переполнение двумя числами с разными знаками невозможно, потому что они никогда не приведет к выходу за пределы возможного значения со знаком). Результат 0x00 и не устанавливает флаг переполнения, потому что 255+1 или, точнее, -1+1 дает 0, что, очевидно, правильно для знаковой арифметики.

Помните, что для того, чтобы установить флаг переполнения, 2 числа, которые должны быть добавлены / вычтены, должны иметь бит знака с одинаковым значением, а затем результат должен иметь бит знака со значением, отличным от них.

За исключением флага переноса inc устанавливает флаги так же, как и при добавлении операнда 1.

Тот факт, что inc не влияет на флаг переноса, очень важен.

http://oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-2.html

Процессор устанавливает соответствующие флаги для результатов этих инструкций (add, adc, dec, inc, sbb, sub) для случаев со знаком и без знака, т. Е. Два разных результата флага для каждой операции. Альтернативой было бы иметь два набора инструкций, где один устанавливает флаги, связанные со знаком, а другой - связанные без знака. Если выдающий компилятор использует переменные без знака в операции, он будет проверять перенос и ноль (jc, jnc, jb, jbe и т. Д.), Если он подписан, он проверяет переполнение, знак и ноль (jo, jno, jg, jng, jl, jle и т. Д.).

Другие вопросы по тегам