C ``继续``断开标签放置
这很好:C ``继续``断开标签放置,c,gcc,while-loop,inline-assembly,continue,C,Gcc,While Loop,Inline Assembly,Continue,这很好: #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
#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;
}
为什么?
(使用
gcc rtm\u simple.c-o rtm\u 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;
}
继续;
可达成的:
__asm_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;
++中止计数器;
}
printf(“%d\n”,i);
printf(“nof中止重试:%d\n”,中止计数器);
如果(abort_计数器<0)转到可到达;
返回0;
}
带有标签的goto
告诉gcc代码是可访问的,并且abort\u计数器是易失性的,这会阻止gcc优化goto
。此代码中出现错误的原因:
__asm__ ("xbegin ABORT");
i++;
__asm__ ("xend");
continue;
__asm__ ("ABORT:");
++abort_counter;
这是因为编译器将continue
语句之后直到块(while循环)结束的所有内容都视为死代码。GCC不了解特定asm块的功能,因此它不知道标签ABORT
在\uuu asm\uuu(“xbegin ABORT”)中使用代码>。通过消除死代码,跳转目标被消除,当链接器尝试解析标签时,跳转目标消失(未定义)
作为另一个答案的替代方案-从GCC 4.5开始(在CLANG中仍然不受支持),您可以将扩展程序集与以下语句一起使用:
转到标签
asm goto允许汇编代码跳转到一个或多个C标签。asm goto语句中的GotoLabels部分包含一个逗号分隔的列表,其中列出了汇编代码可能跳转到的所有C标签。GCC假定ASM执行失败到下一个语句(如果不是这样的话,请考虑使用ASM语句之后的Y.BuuTuxIn不可访问的内部)。通过使用热标签和冷标签属性(请参阅标签属性),可以改进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;
}
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 */
}
这将确保i++由于编译器现在认为asm块依赖于i
中的值,因此code>将放在此程序集块之前。政府这样说:
请注意,即使是易失性asm指令也可以相对于其他代码移动,包括跨跳转指令。[snip]要使其正常工作,您需要向asm添加一个人工依赖项,引用代码中您不希望移动的变量
还有另一种方法应该适用于GCC/ICC/CLANG,那就是修改逻辑。如果事务中止,可以在程序集模板内增加abort\u计数器
。您可以将其作为输入和输出约束传入。您还可以使用GCC的本地标签来定义唯一的标签:
本地标签
本地标签与本地符号不同。本地标签帮助编译器和程序员临时使用名称。它们创建的符号保证在输入源代码的整个范围内都是唯一的,并且可以通过简单的符号来引用。若要定义本地标签,请编写格式为“N:”的标签(其中N表示任何非负整数)。若要参考该标签的最新先前定义,请使用定义标签时使用的相同编号写入“Nb”。要参考本地标签的下一个定义,请编写“Nf”。“b”代表“向后”,而“f”代表“向前”
循环的代码可能如下所示:
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;
}
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 */
}
while(i<100000000){
__asm____________;挥发性_______
:“eax”);
/*EAX是一个拦截器,因为中止的事务将设置状态*/
/*1f是下面第二个asm块中的本地标签1:*/
/*“+rm”(i)约束是一个错误的依赖项,以确保
此asm块将始终出现在i++语句之前*/
i++;
__asm\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
“jmp 2f\n”/*跳转到asm块的末尾,未中止*/
“1:\n\t”/*这是xbegin指向的中止标签*/
“包括%0\n”/*递增中止计数器*/
“2://*asm块底部的标签*/
:“+rm”(中止计数器)
:“rm”(i));/*为了确保
此asm块将始终出现在i++语句之后*/
}
如果您的编译器支持它(GCC 4.8.x+),请使用GCC的。这有助于减少代码中可能出现错误的向量。编译器很可能在继续之后优化掉所有内容,因为它无法访问(而且它也不分析汇编代码,所以它不知道您在做什么).这只是一个猜测,但我认为编译器会将continue之后的行视为死代码(永远不会执行),因此在编译时将其删除。C编译器不知道“xbegin ABORT”是什么意思。使用asm\uuuuuuuuuuuuu volatile\uuuuuuuuuuu
修复它吗?你检查了gcc-S rtm\u simple.C的输出了吗?你可能会欺骗它:继续;可访问:_uasm__;(“中止”)++中止计数器;}。。。如果(abort_计数器<0)转到可到达代码>这不能保证是安全的,因为编译器不知道可以从\uuu asm\uuu(“xbegin ABORT”)代码>。它可能有点安全,因为abort\u计数器
是易变的,但它可能在寄存器中有其他东西,它希望在asm
语句之后存储这些东西。因此,它可能适用于这个玩具示例,但很容易与真实的周围代码冲突。不使用asm goto
跳转的asm
语句通常是不安全的。
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 */
}