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 .

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

Флаг переполнения устанавливается, когда операция вызывает изменение знака. Ваш код очень близок. Я смог установить флаг 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 для этого случая.

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

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

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

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

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

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

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

 Топ:
     inc% al
     Джо сделал
     jmp top

 сделанный:
     RET

 .globl dec
 Декабрь:
     mov $ 33,% eax

 topx:
     dec% al
     jo donex
     jmp topx

 donex:
     RET

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

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

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

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

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

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

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

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



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

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

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