Вы также заявляете это int
в Си?int
- это 32-разрядный тип в ABI System V x86-64, который вы используете.
Но вы оперируете qword long
операндами после сообщениякомпилятор старших 32 битов arg не имеет значения.Поэтому, когда вы передаете -5
как int
arg, вы получаете 0x00000000fffffffb
в RDI, который является 64-разрядным положительным целым числом дополнения 2, 4294967291. (Это предполагает, что старшие биты равны нулю, например, если вызывающийиспользуется mov $-5, %edi
, как компилятор C с постоянным аргументом. Но вы можете получить произвольный мусор, если приведете long
к int
: вызывающая сторона будет считать, что эта функция игнорирует старшие биты, как объявлено.)
Вы забыли mov %rdi, %rax
на случай, если аргумент положительный.(Затем выполните условное neg %rax
) Или, на самом деле, mov %edi, %eax
/ neg %eax
.
Таким образом, ваша функция оставляет RAX неизменным, когда RDI положителен.(Но так как вызывающий, вероятно, был неоптимизирован C из gcc -O0
, он также содержит копию аргумента. Это объясняет поведение abs(-5)
=> -5
, а не полуслучайный мусор, который вы быожидайте от ошибки, подобной этой).
Соглашение о вызовах x86-64 System V не требует от вызывающего абонента расширять узкие аргументы или возвращать значения до полной ширины регистра,поэтому ваш cmp
/ jl
зависит от того, какой мусор оставил вызывающий в верхней половине RDI.
( Требуется расширение знака или нуля при добавлении 32-битного смещения к указателю дляx86-64 ABI? ).Существует недокументированное поведение знака или нуля, расширяющее узкие аргументы до 32-разрядных, но не до 64.
Естественный размер / размер по умолчанию для большинства операций - это 64-разрядный размер операнда, 64-разрядный адресразмер, с неявным расширением нуля до 64-битного при записи 32-битного регистра.Используйте 32-битный размер операнда, если у вас нет особой причины для использования 64-битного кода (например, для указателей).
Посмотрите, что обычно делают компиляторы;они обычно используют cmov
для abs()
.См. Вывод компилятора для long foo(long x) { return std::abs(x); }
из компилятора C ++ с включенной оптимизацией, например, https://godbolt.org/z/I3NSIZ для long
и int
версий.
# clang7.0 -O3
foo(int): # @foo(int)
movl %edi, %eax
negl %eax # neg sets flags according to eax = 0-eax
cmovll %edi, %eax # copy the original if that made it negative
retq
# gcc7.3 -O3
foo(int):
movl %edi, %edx
movl %edi, %eax
sarl $31, %edx
xorl %edx, %eax # 2's complement identity hack. Maybe nice with slow cmov
subl %edx, %eax
ret
Но, к сожалению, gcc не переключается наСмов даже с -march=skylake
, где это 1 моп.