此问题在此处已有答案:
Once more volatile: necessary to prevent optimization?(3个答案)
16天前关闭。
我使用gcc编译了一个简单的ARM Cortex-M4测试代码,它会优化全局变量的使用,这让我很困惑。gcc优化全局变量使用的规则是什么?
GCC编译器:gcc-臂-无-eabi-8-2019-第3季度-更新/bin/臂-无-eabi-gcc
优化级别:- 奥斯
我的测试代码:
以下代码位于“foo.c”中,并且在任务A中调用了函数foo 1()和foo 2(),在任务B中调用了函数global_cnt_add()。
int g_global_cnt = 0;
void dummy_func(void);
void global_cnt_add(void)
{
g_global_cnt++;
}
int foo1(void)
{
while (g_global_cnt == 0) {
// do nothing
}
return 0;
}
int foo2(void)
{
while (g_global_cnt == 0) {
dummy_func();
}
return 0;
}
The function dummy_func() is implemented in bar.c as following:
void dummy_func(void)
{
// do nothing
}
函数foo 1()的汇编代码如下所示:
int foo1(void)
{
while (g_global_cnt == 0) {
201218: 4b02 ldr r3, [pc, #8] ; (201224 <foo1+0xc>)
20121a: 681b ldr r3, [r3, #0]
20121c: b903 cbnz r3, 201220 <foo1+0x8>
20121e: e7fe b.n 20121e <foo1+0x6>
// do nothing
}
return 0;
}
201220: 2000 movs r0, #0
201222: 4770 bx lr
201224: 00204290 .word 0x00204290
函数foo 2()的汇编代码如下所示:
int foo2(void)
{
201228: b510 push {r4, lr}
while (g_global_cnt == 0) {
20122a: 4c04 ldr r4, [pc, #16] ; (20123c <foo2+0x14>)
20122c: 6823 ldr r3, [r4, #0]
20122e: b10b cbz r3, 201234 <foo2+0xc>
dummy_func();
}
return 0;
}
201230: 2000 movs r0, #0
201232: bd10 pop {r4, pc}
dummy_func();
201234: f1ff fcb8 bl 400ba8 <dummy_func>
201238: e7f8 b.n 20122c <foo2+0x4>
20123a: bf00 nop
20123c: 00204290 .word 0x00204290
在函数foo 1()的汇编代码中,全局变量“g_global_cnt”只被加载一次,while循环永远不会被打断,编译器优化了“g_global_cnt”的使用,我知道我可以添加volatile来避免这种优化。
在函数foo 2()的汇编代码中,在每个while循环中加载并检查全局变量“g_global_cnt”,可以中断while循环。
gcc的优化规则有哪些?
1条答案
按热度按时间5tmbdcev1#
为了理解这种行为,你必须考虑副作用和序列点ref。
对于编译器 ,副作用是指运算符、表达式、语句或函数的结果,即使在运算符、表达式、语句或函数完成求值后,副作用仍然存在。
而 * 序列点定义了计算机程序执行过程中的任何一个点,在这个点上,可以保证先前求值的所有副作用都已执行,并且还没有执行后续求值的任何副作用。*
序列点的主要规则是,除了计算变量值的变化之外,不会出于任何目的在两个点之间多次访问变量
引用C标准:
在抽象机器中,所有表达式都按照语义的规定求值。如果一个实际的实现可以推断出表达式的值没有被使用,并且没有产生所需的副作用(包括任何由调用函数或访问volatile对象引起的副作用),那么它就不需要对表达式的一部分求值。
在您的代码中
在阅读
g_global_cnt
之后,就不会有任何可能影响变量值的副作用了。编译器无法知道它是在函数作用域之外被修改的,因此它认为你只能读取它一次,这是因为函数作用域中没有更多的序列点。告诉编译器每次读取都有副作用的方法是用标识符
volatile
标记变量。对于
int g_global_cnt = 0;
:对于
volatile int g_global_cnt = 0;
: