Как установить или снять флаг переполнения в сборке 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