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++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 */
}