Подтвердить что ты не робот

X86 Сборка: флаг INC и DEC и флаг переполнения

В сборке 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

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

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.

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

4b9b3361

Ответ 1

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

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 для этого случая.

Ответ 2

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

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

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

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

Ответ 3

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

ИЗМЕНИТЬ

.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, я подозреваю, что проблема в том, как вы используете встроенный ассемблер.

Ответ 4

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

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

Ответ 6

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

Ответ 7

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 числа, добавляемые/вычитаемые, должны иметь знаковый бит с тем же значением, а затем результат должен иметь бит знака со значением, отличным от них.