[在处理编译指示和宏时,我在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或类似的建议,我只想使用预处理器来解决这个问题。
将选定的评论转换为答案,并扩展信息...
[如果有任何事情要做,使用§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
。