``continue``打破标签放置

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.)

mel*_*ene 7

你可能会欺骗它:

        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掉.


Mic*_*tch 7

您在此代码中收到错误的原因:

    __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的事务内在函数.这有助于完全消除内联汇编的使用,并且可以减少代码中可能出错的向量.