Use*_*291 10 c gcc continue inline-assembly while-loop
这很好用:
#include <stdio.h>
int main(){
volatile int abort_counter = 0;
volatile int i = 0;
while (i < 100000000) {
__asm__ ("xbegin ABORT");
i++;
__asm__ ("xend");
__asm__ ("ABORT:");
++abort_counter;
}
printf("%d\n", i);
printf("nof abort-retries: %d\n",abort_counter-i);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但是,我最初写的是
#include <stdio.h>
int main(){
volatile int abort_counter = 0;
volatile int i = 0;
while (i < 100000000) {
__asm__ ("xbegin ABORT");
i++;
__asm__ ("xend");
continue;
__asm__ ("ABORT:");
++abort_counter;
}
printf("%d\n", i);
printf("nof abort-retries: %d\n",abort_counter);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但这导致了
/tmp/cchmn6a6.o: In function `main':
rtm_simple.c:(.text+0x1a): undefined reference to `ABORT'
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)
为什么?
(编译gcc rtm_simple.c -o rtm_simple.)
你可能会欺骗它:
continue;
reachable:
__asm__ ("ABORT:");
++abort_counter;
}
printf("%d\n", i);
printf("nof abort-retries: %d\n",abort_counter);
if (abort_counter < 0) goto reachable;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在goto与标签告诉GCC的代码可达,abort_counter是挥发性应防止GCC从能够优化goto掉.
您在此代码中收到错误的原因:
__asm__ ("xbegin ABORT");
i++;
__asm__ ("xend");
continue;
__asm__ ("ABORT:");
++abort_counter;
Run Code Online (Sandbox Code Playgroud)
是因为编译器在continue语句之后看到了所有内容,直到块结束(while循环)为死代码.GCC不了解特定的asm块的作用,因此它不知道该标签ABORT是用过的__asm__ ("xbegin ABORT");.通过消除死代码,跳转目标被消除,当链接器试图解析标签时,它已经消失(未定义).
作为另一个答案的替代 - 从GCC 4.5开始(在CLANG中仍然不支持),您可以使用带有asm goto语句的扩展程序集:
转到标签
asm goto允许汇编代码跳转到一个或多个C标签.asm goto语句中的GotoLabels部分包含汇编代码可能跳转到的所有C标签的逗号分隔列表.GCC假定asm执行落到下一个语句(如果不是这种情况,请考虑在asm语句之后使用__builtin_unreachable内在函数).可以通过使用热标签属性和冷标签属性来改进asm goto的优化(请参阅标签属性).
代码可能是这样编写的:
while (i < 100000000) {
__asm__ goto("xbegin %l0"
: /* no outputs */
: /* no inputs */
: "eax" /* EAX clobbered with status if an abort were to occur */
: ABORT); /* List of C Goto labels used */
i++;
__asm__ ("xend");
continue;
ABORT:
++abort_counter;
}
Run Code Online (Sandbox Code Playgroud)
由于编译器现在知道内联汇编可以将标签ABORT用作跳转目标,因此它不能仅仅将其优化.同样,使用此方法我们不需要将ABORT标签放在装配块内,可以使用正常的C标签来定义它.
使用上面的代码挑剔:虽然__asm__ ("xend");它是易失性的,因为它是一个基本的asm语句,编译器可以重新排序并将它放在它之前i++,这不是你想要的.您可以使用伪约束,使编译器认为它依赖于变量中的值,i例如:
__asm__ ("xend" :: "rm"(i));
Run Code Online (Sandbox Code Playgroud)
这将确保i++;将放置在此程序集块之前,因为编译器现在会认为我们的asm块依赖于其中的值i.在海湾合作委员会的文件有这样一段话:
请注意,即使是易失性的asm指令也可以相对于其他代码移动,包括跨跳转指令.[snip]为了使它工作,你需要在asm中添加一个人工依赖,引用你不想移动的代码中的变量
还有另一个替代方案应该适用于GCC/ICC/CLANG,那就是重写逻辑.abort_counter如果事务中止,您可以增加程序集模板内部.您将其作为输入和输出约束传递.您还可以使用GCC的本地标签来定义唯一标签:
本地标签
本地标签与本地符号不同.本地标签可以帮助编译器和程序员临时使用名称.它们创建的符号在输入源代码的整个范围内保证是唯一的,并且可以通过简单的符号来引用.要定义本地标签,请写入"N:"形式的标签(其中N表示任何非负整数).要引用该标签的最新先前定义,请使用与定义标签时相同的数字来写入"Nb".要引用本地标签的下一个定义,请写入'Nf'.'b'代表"向后",'f'代表"向前".
循环的代码可能如下所示:
while (i < 100000000) {
__asm__ __volatile__ ("xbegin 1f" : "+rm"(i) ::
: "eax");
/* EAX is a clobber since aborted transaction will set status */
/* 1f is the local label 1: in the second asm block below */
/* The "+rm"(i) constraint is a false dependency to ensure
this asm block will always appear before the i++ statement */
i++;
__asm__ __volatile__ ("xend\n\t"
"jmp 2f\n" /* jump to end of asm block, didn't abort */
"1:\n\t" /* This is the abort label that xbegin points at */
"incl %0\n" /* Increment the abort counter */
"2:" /* Label for the bottom of the asm block */
: "+rm"(abort_counter)
: "rm"(i)); /* The "rm"(i) constraint is a false dependency to ensure
this asm block will always appear after the i++ statement */
}
Run Code Online (Sandbox Code Playgroud)
如果您的编译器支持它(GCC 4.8.x +),请使用GCC的事务内在函数.这有助于完全消除内联汇编的使用,并且可以减少代码中可能出错的向量.