将标志寄存器用作布尔值 return 是否被认为是不好的做法?

Is it considered bad practice to use the flags register as a boolean return value?

我正在用 x86 汇编程序编写一些程序,将 ZF 修改为 return 布尔值的一种方式,所以我可以这样做:

call is_value_correct
jz not_correct

我想知道这是否被认为是不好的做法,因为一些编码标准规定应该在 AX 寄存器中 returned 简单值。

如果它能让你的代码 运行 更快 and/or 整体更小,那就去做吧。 这不是坏习惯。

手工编写 asm 的一个优点是能够使用自定义调用约定函数,即使它们不是私有辅助函数或宏。这包括能够在不同的寄存器中“return”多个值,基本上可以做任何你想做的事。

与任何自定义调用约定一样,您需要做的就是用注释记录下来。下面是一个示例,说明如何使用一组特定的、有意非标准的东西来编写此类评论。

# inputs:   foo in EAX (zero-extended into RAX), bar in EDI
# pre-requisites: DF=0
# clobbers: RCX, RDX, AL (but not the rest of EAX)
# returns:  AL = something,  ZF = something else
my_func:
   ...
   setc al
   ...
   something that sets ZF
   ret

如果您愿意为了风格或可读性而牺牲效率,您可能不应该在 2018 年首先使用 asm 编写,因为大多数时候编译器能够生成良好的 asm,而且您很少需要编写您自己的引导扇区或其他任何东西。 (即性能是留给手写 asm 的主要用例,只有当你全力以赴追求性能时才合适。)

是的,手写的 asm 可能会变得不可读/无法维护,但如果在具有合理语义的情况下仔细完成,this 优化不会使您代码乱七八糟。


甚至有这样做的先例:x86-64 OS X system calls use CF as the error/no-error status,与 rax return 值分开。与 Linux 不同,其中错误由 RAX return 值从 -4095 到 -1 指示,尽管它们使用相同的 x86-64 System V ABI / 调用约定。

一些 DOS int 0x21 系统调用和 PC BIOS int 0x10 函数具有类似的标志-return 系统。它允许调用者在错误时分支,因此它节省了代码大小(测试或 cmp)并避免了需要带内错误信号。


内联汇编

顺便说一句,在 内联 汇编中,不是编写整个函数,你可以而且应该在 FLAGS 中输出一些东西,而不是浪费在寄存器中实现布尔值的指令,特别是如果编译器只是要再次 test 它。 Condition-code output constraints are supported since GCC6, see this answer 举个例子。

但是如果您想使用 C 来调用具有自定义调用约定的函数,那么从 asm 语句中安全地 call 一个函数并不容易。您需要告诉编译器每个被调用破坏的寄存器都被破坏了。包括 st0..7、mm0..7、x/y/zmm0..31 和 k0..7(如果支持 AVX-512,否则编译器甚至不知道这些 reg 名称)。或者只有那些你手写的 asm 函数实际上破坏了,如果你想承诺保持约束与函数的实际实现同步。

call本身做了一个推,踩到了红色区域。请参阅 以获取应该安全的示例。