Сборка x86: инструкция INC и DEC и флаг переполнения - PullRequest
13 голосов
/ 13 октября 2010

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

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

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

Do 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

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

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 ]

16 голосов
/ 13 октября 2010

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

char ovf = 0;

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

При увеличении ЧД значение 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.

3 голосов
/ 14 октября 2010

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

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

3 голосов
/ 13 октября 2010

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

EDIT

.globl inc
inc:
    mov $33, %eax

top:
    inc %al
    jo done
    jmp top

done:
    ret

.globl dec
dec:
    mov $33, %eax

topx:
    dec %al
    jo donex
    jmp topx

donex:
    ret

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

3 голосов
/ 13 октября 2010

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

Смотрите соответствующее руководство Справочник по набору инструкций, A-M . Каждая инструкция точно документирована.

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

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

2 голосов
/ 13 октября 2010

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

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

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

0 голосов
/ 25 апреля 2013

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. Оба они имеют разные биты знака, поэтому переполнение невозможно (переполнение возможно только при основном добавлении 2 чисел одного знака, но переполнение 2 числами другого знака невозможно, потому что они никогда не приведет к выходу за пределы возможного значения со знаком). Результат - 0x00, и он не устанавливает флаг переполнения, потому что 255+1, или, точнее, -1+1 дает 0, что, очевидно, правильно для знаковой арифметики.

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

0 голосов
/ 28 января 2011

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

...