这个问题更多是出于好奇而不是必要:
是否有可能以if ( !boolvar ) { ...
某种方式重写 c 代码,以便将其编译为 1 个 cpu 指令?
我试过在理论层面上思考这个问题,这就是我想出的:
if ( !boolvar ) { ...
需要首先否定变量,然后根据该 -> 2 条指令进行分支(否定 + 分支)
if ( boolvar == false ) { ...
需要将 false 的值加载到寄存器中,然后根据该值进行分支 -> 2 条指令(加载 + 分支)
if ( boolvar != true ) { ...
需要将 true 的值加载到寄存器中,然后根据 -> 2 条指令进行分支(“branch-if-not-equal”)(加载 +“branch-if-not-equal”)
我的假设错了吗?有什么我忽略的吗?
我知道我可以生成程序的中间 asm 版本,但我不知道如何以某种方式使用它,因此我可以一方面打开编译器优化,同时没有if
优化掉一个空语句(或者有if 语句与其内容一起优化,给出一些非通用的答案)
PS:当然,我也为此搜索了 google 和 SO,但是搜索词如此短,我真的找不到任何有用的东西
PPS:我可以使用语义等效的版本,该版本不是语法等效的,例如不使用if
.
编辑:如果我对发出的 asm 指令的假设是错误的,请随时纠正我。
Edit2:我实际上在大约 15 年前学习了 asm,并在大约 5 年前为 alpha 架构重新学习了它,但我希望我的问题仍然足够清楚,可以弄清楚我在问什么。此外,如果有助于找到一个好的答案,您可以自由假设消费者 CPU 中常见的任何类型的处理器扩展,直到 AVX2(截至撰写本文时的当前 Haswell CPU)。
在我的文章的最后,它会说明为什么你不应该以这种行为为目标(在 x86 上)。
正如 Jerry Coffin 所写,x86 中的大多数跳转取决于标志寄存器。
但有一个例外:如果/寄存器为零j*cxz
则跳转的指令集。为了实现这一点,您需要确保您使用寄存器。您可以通过将其专门分配给该寄存器来实现ecx
rcx
boolvar
ecx
register int boolvar asm ("ecx");
Run Code Online (Sandbox Code Playgroud)
但到目前为止,并非所有编译器都使用该j*cxz
指令集。有一个标志可以icc
让它这样做,但通常不建议这样做。Intel手册指出两条指令
test ecx, ecx
jz ...
Run Code Online (Sandbox Code Playgroud)
处理器速度更快。
之所以如此,是因为 x86 是 CISC(复杂)指令集。在实际的硬件中,处理器会将汇编中显示为一条指令的复杂指令拆分为多个微指令,然后以 RISC 风格执行。这就是为什么并非所有指令都需要相同的执行时间,有时多个小指令比一个大指令更快的原因。
test
和jz
是单个微指令,但jecxz
无论如何都会被分解为这两个微指令。
指令集存在的唯一原因j*cxz
是如果您想在不修改标志寄存器的情况下进行条件跳转。