Как установить или снять флаг переполнения в сборке x86?

Я хочу написать простой код (или алгоритм), чтобы установить / очистить флаг переполнения. Для установки OF я знаю, что могу использовать подписанные значения. Но как я могу это очистить?

2 ответа

Решение

Есть много возможных решений.

Например, test al, al очистит OF пометить, не затрагивая содержимое регистра.


Или, если вы не хотите влиять на другие флаги, вы можете просто изменить *FLAGS регистр. Например, в 32-битной версии это будет выглядеть так:

pushfd                   ; Push EFLAGS onto the stack
and dword [esp], ~0x800  ; Clear bit 11 (OF)
popfd                    ; Pop the modified result back into EFLAGS

Изменить: Изменено or al, al в test al, al по рекомендации Peter Cordes. (Эффекты такие же, но последний лучше по соображениям производительности)

Общее решение (инкр. / Дек.):

При условии:

  • у вас есть реестр, содержание которого вас не интересует,
  • ты хочешь сохранить CF-Флаг

Скажите, что регистр al. (работает w/ r/8, r/16, r/32, r/64)

; set OF-Flag, preserve CF
mov al, 0x7F
inc al

; set OF-Flag, preserve CF
mov al, 0x0
inc al

Источник:Intel Documentation INC, стр.551.

Альтернативно (adox):

Другой подход, если вы можете предположить:

  • ан adx включенный процессор (вы проверяете флаги процессора с grep adx /proc/cpuinfo)

Скажите, что регистр eax. (нужен r64/r32)

; clear OF-Flag, preserve CF
mov eax, 0x0
adox eax, eax

; set OF-Flag, preserve CF
mov eax, 0xFFFFFFFF
adox eax, eax 

Примечание: не пытайтесь заменить mov с участием xor (или аналогичный), так как это очистит CF

Источник:Intel Documentation ADOX стр.150

popf довольно медленный (например, один на 20 циклов на Skylake); если вам нужно очистить или установить OF, то в идеале сделайте это как побочный эффект инструкции ALU, особенно той, которую вы собираетесь использовать в любом случае для полезного вычисления, которое, как вы знаете, не будет или будет переполнено. (Тот, который будет переполнен, обычно труднее найти, в отличие от CF, где вы всегда можете просто sub вместо add с константой, которая оборачивает почти все вокруг для всех входов, кроме очень маленького диапазона).

Если вам нужно установить / очистить только OF, не влияя на другие коды условий по какой-либо причине, тогда да, pushf / popf это путь lahf / sahf не получает OF, потому что OF - это бит 11 в EFLAGS, вне минимума 8.


test al,al (или любой другой, тот же регистр) очищает OF и CF, так же, как сравнивая / вычитая ноль. Другие флаги удобно устанавливать в соответствии со значением.

xor eax,eax очищает EAX и очищает OF/SF/CF, устанавливает ZF / PF. В любом случае вам часто нужен обнуляемый регистр, так что если вам нужно очистить OF (например, для начала adox цепочка расширенной точности), затем убейте 2 зайцев одним выстрелом и расположите ваш код так, чтобы последняя инструкция по установке флага была обнулением xor.

В x86-64 вы также можете доверять, используя add по указателю + длина не пересекает середину виртуального адресного пространства без знака и, таким образом, очищает OF, Но это предположение может нарушить будущие процессоры с полностью 64-битными виртуальными адресами, потому что тогда не будет дырки в виртуальном адресном пространстве вокруг границы со знаком-переносом, поэтому один непрерывный массив может охватить его. И это уже может произойти в 32-битном коде, работающем под 64-битным ядром или 32-битным ядром, которое не использует ядро ​​2G:2G: разделение виртуального адресного пространства пользователем.


xor eax, eax / cmp al, -128 устанавливает OF, и занимает всего 4 байта кода. Это, вероятно, самый дешевый способ, и в отличие от sub или что-то еще, он не записывает какие-либо частичные регистры (или любые полные регистры). Это все еще оставляет EAX обнуленным.

0 - -128 оборачивает к -128 , т.е. подписано ОФ. 8-битное целое число дополнения 2 может представлять только значения из -128..+127, Наиболее отрицательное число является частным случаем и не имеет правильного обратного. Это его собственное абсолютное значение / отрицательное, или, точнее, эти функции переполняются. (Или вы можете трактовать операцию абсолютного значения как вход со знаком и вывод без знака, поэтому результат равен +128, то есть 0x80. X86 не имеет инструкции целочисленного abs (подготовьте -x, затем test/cmov), но с SSSE3 оно имеет векторное целое число pabsb)

Для любого известного значения в AL, кроме -1, есть cmp al, imm8 это будет набор OF. Для любого значения от 0.127, cmp al, -128 обертывания. Для любого значения от -2..-128, cmp al, +127 обертывания и, таким образом, наборы OF. За -1 вычитание 127 приведет вас только к -128. Вычитание -128 приносит вам +127. К сожалению, я не думаю, что есть способ с одной инструкцией для установки OF без известного значения в регистре.

Это не должно быть al, но есть 2-байтовая специальная кодировка cmp al,imm8, Другие 8- или 32-битные регистры могут использовать обычную 3-байтовую кодировку.


Без засорения каких-либо регистров и без известных констант, это 6 байтов:

push   rax
xor    eax,eax
cmp    al, -128
pop    rax

Это забивает другие коды условий, но это быстрее, чем pushf / popf, Обычно вы можете что-то заткнуть, иначе вы не можете стучать в стек.


Toggle OF

setno al              # OF=0 -> AL=1           OF=1 -> AL=0
cmp   al, -127        # 1 - -127 = 128 = -128     0 - -127 = +127
Другие вопросы по тегам