由cpp重新排序的编译指令

问题描述 投票:0回答:1

[在处理编译指示和宏时,我在cpp中观察到一种奇怪的行为:它将这些指令从它们的实际行移动到宏扩展的开头。

示例:

#define K( arg ) arg

K(
    int a = 0;

    #pragma unroll
    for(int i=0; i < 5; i++) {
        a++;
    }
)

输出:

#pragma unroll
int a = 0; for(int i=0; i < 5; i++) { a++; }

这是什么原因?有什么办法可以使语用保持在适当的位置吗?请不要使用sed或类似的建议,我只想使用预处理器来解决这个问题。

c macros c-preprocessor
1个回答
1
投票

将选定的评论转换为答案,并扩展信息...

[如果有任何事情要做,使用§6.10.9 Pragma operator-_Pragma("unroll")-可能是这样做的方法。您也不能在宏调用的主体中嵌入其他预处理指令,例如#if#endif。但是,快速实验表明gcc -E仍将#pragma放在扩展循环之前。您可能需要通过查看汇编代码来检查编译指示是否生效。优化器可能会将特定循环更改为a = 5;,完全消除i,并且不会展开任何内容。

另外,GCC Loop-Specific Pragmas文档说您需要使用#pragma GCC unroll n(指定展开循环的次数)。

您可能无法依赖任何未由标准移植到任何编译器的#pragma。语法本质上是特定于编译器的。有3个标准的编译指示,都以#pragma STDC开头-它们均与循环展开无关。另请参见§6.10.6 Pragma directive以获取标准的编译指示和“取决于所有编译器”的文档。

使循环中的代码稍微复杂一点,如果使用,您可以展开循环:

  • _Pragma("GCC unroll 5")

如果尝试,则不起作用:

  • #pragma GCC unroll 5

示例代码:

extern double course_deviation(double x, double y);
#define K( arg ) arg

int main(void)
{
K(
    int a = 0;
    _Pragma("GCC unroll 5")
    //#pragma GCC unroll 5
    for (int i = 0; i < 5; i++)
    {
        a += course_deviation(i + 3.0, i - 3.0);
    }
)
    return a;
}

使用#pragma指令而不是_Pragma运算符,代码无法编译。如图所示,GCC 9.3.0在macOS Mojave 10.14.6上生成的汇编代码的相关部分为:

$ gcc -O -S k37.c
$ sed '/LFE0/q' k37.s
        .text
        .section __TEXT,__text_startup,regular,pure_instructions
        .globl _main
_main:
LFB0:
        subq    $24, %rsp
LCFI0:
        movsd   lC0(%rip), %xmm1
        movsd   lC1(%rip), %xmm0
        call    _course_deviation
        movsd   %xmm0, 8(%rsp)
        movsd   lC2(%rip), %xmm1
        movsd   lC3(%rip), %xmm0
        call    _course_deviation
        movapd  %xmm0, %xmm1
        pxor    %xmm2, %xmm2
        addsd   8(%rsp), %xmm2
        cvttsd2sil      %xmm2, %eax
        pxor    %xmm0, %xmm0
        cvtsi2sdl       %eax, %xmm0
        addsd   %xmm1, %xmm0
        movsd   %xmm0, 8(%rsp)
        movsd   lC5(%rip), %xmm1
        movsd   lC6(%rip), %xmm0
        call    _course_deviation
        movapd  %xmm0, %xmm1
        cvttsd2sil      8(%rsp), %eax
        pxor    %xmm0, %xmm0
        cvtsi2sdl       %eax, %xmm0
        addsd   %xmm1, %xmm0
        movsd   %xmm0, 8(%rsp)
        pxor    %xmm1, %xmm1
        movsd   lC7(%rip), %xmm0
        call    _course_deviation
        movapd  %xmm0, %xmm1
        cvttsd2sil      8(%rsp), %eax
        pxor    %xmm0, %xmm0
        cvtsi2sdl       %eax, %xmm0
        addsd   %xmm1, %xmm0
        movsd   %xmm0, 8(%rsp)
        movsd   lC8(%rip), %xmm1
        movsd   lC9(%rip), %xmm0
        call    _course_deviation
        movapd  %xmm0, %xmm1
        cvttsd2sil      8(%rsp), %eax
        pxor    %xmm0, %xmm0
        cvtsi2sdl       %eax, %xmm0
        addsd   %xmm1, %xmm0
        cvttsd2sil      %xmm0, %eax
        addq    $24, %rsp
LCFI1:
        ret
LFE0:

没有-O选项(无优化),汇编代码的相关部分如下所示-没有循环展开。似乎循环展开是一种优化-没有优化,没有循环展开。

        .text
        .globl _main
_main:
LFB0:
        pushq   %rbp
LCFI0:
        movq    %rsp, %rbp
LCFI1:
        subq    $16, %rsp
        movl    $0, -4(%rbp)
        movl    $0, -8(%rbp)
        jmp     L2
L3:
        cvtsi2sdl       -8(%rbp), %xmm0
        movsd   lC0(%rip), %xmm1
        movapd  %xmm0, %xmm2
        subsd   %xmm1, %xmm2
        cvtsi2sdl       -8(%rbp), %xmm1
        movsd   lC0(%rip), %xmm0
        addsd   %xmm1, %xmm0
        movapd  %xmm2, %xmm1
        call    _course_deviation
        cvtsi2sdl       -4(%rbp), %xmm1
        addsd   %xmm1, %xmm0
        cvttsd2sil      %xmm0, %eax
        movl    %eax, -4(%rbp)
        addl    $1, -8(%rbp)
L2:
        cmpl    $4, -8(%rbp)
        setle   %al
        testb   %al, %al
        jne     L3
        movl    -4(%rbp), %eax
        leave
LCFI2:
        ret
LFE0:

[我尝试使用atan2()中的<math.h>代替course_correction()函数,并且优化程序将代码优化为-仅返回7

        .text
        .section __TEXT,__text_startup,regular,pure_instructions
        .globl _main
_main:
LFB19:
        movl    $7, %eax
        ret
LFE19:

使用原始代码(循环体中的a++;,结果返回了5,而不是7

© www.soinside.com 2019 - 2024. All rights reserved.