我正在处理一些代码,其中将条件标志从内联汇编块中输出并将其用作在调用C代码中进行分支的条件非常理想。我不想存储标志(这将是无用且低效的;已经有更有效的方法来实现结果),而是直接使用标志。是否有任何方法可以使用GNU C内联汇编约束来实现此目的?我对适用于多个指令集体系结构的方法感兴趣,旨在将其与体系结构的LL / SC样式原子产生的条件标志一起使用。当然,另一个明显的用例(与我所做的分开)将允许外部C代码根据内联汇编中操作的进位标志进行分支。
从GCC6开始,在x86平台上,你实际上可以使用"=@ccCOND"
作为输出(其中COND
是任何有效的x86条件码)。
示例最初来自此处,经过David的建议进行了清理:
int variable_test_bit(long n, volatile const unsigned long *addr)
{
int oldbit;
asm volatile("bt %[value],%[bit]"
: "=@ccc" (oldbit)
: [value] "m" (*addr), [bit] "Jr" (n));
return oldbit;
}
__GCC_ASM_FLAG_OUTPUTS__
。I
是否正确。在某些平台上,长整型可以是64位,而I仅适用于值0..31。我也不清楚为什么我们需要去除const
和volatile
。它是一个输入参数,所以const不应该是问题。它是一个m
,所以volatile是隐含的。我还可能会添加符号名称([bit]
和[value]
)。这不影响功能,但对于我们习惯英特尔格式的人来说可能更清晰。 - David Wohlferdoldbit
与其他两个整数相加时生成一个adc
指令... - chtz*addr
。 bt
的内存操作形式要慢得多,特别是使用寄存器源(Haswell上的10个uops与reg,reg相比为1)。 此外,寄存器源将允许访问除操作数告诉gcc的qword之外的内存,因此您确实需要类似于此的内容:[value]“m”(*(const unsigned long (*) [])addr)
告诉gcc它可能会访问相对于addr
的任何位置。 - Peter Cordesbt*
指令具有疯狂的CISC位串语义:http://felixcloutier.com/x86/BT.html,并且对于寄存器源,这是通过微码处理的。最好让编译器将其加载到寄存器中。 - Peter Cordesasm goto
。然而,它确实允许消除asm外部的分支。思路是:static inline int foo(...)
{
__asm__ goto ( " .... ; cond_jmp %l[ret0]" : : "r"(...) ...
: "clobbers" : ret0 );
return 1;
ret0:
return 0;
}
if (foo(...)) ... else ...
时,汇编块中的条件跳转指向else
分支,即使在抽象机器级别上涉及返回值。if( foo(...) ) {} else {}
时,它确实会进行优化。编译器实际上从未将1
或0
放入寄存器中,只是将asm goto
用作if
分支。但是,你强制选择哪一侧是执行的,哪一侧是跳过的。可能gcc6条件代码输出至少会进行同样的优化,除非你的代码具有早期退出条件(因此条件分支不是你的汇编语句中的最后一条指令)。在这种情况下,这仍然很有用,+1。 - Peter Cordes很遗憾,GCC不支持在asm语句之外访问条件标志。如果您不想设置值,则必须将条件分支移动到asm语句中。这意味着要么使用您已经发现的asm goto labels,要么也将分支目标带入您的asm语句。
您还可以检查一下GCC的旧式__sync atomic builtins或较新的memory model based atomics是否提供了您所使用的原子指令所需的功能。
dmb sy
屏障(与总线上的所有外部硬件同步),而不是期望的dmb ish
(仅与 CPU 核心同步)。在其他一些平台上,它们会生成库调用而不是内联代码,或者在使用旧的-march
时生成与某些较新的目标不兼容的代码,而不是允许我们需要针对这些目标运行时分支多个变体。 - R.. GitHub STOP HELPING ICE