david_ming 发表于 2010-5-3 18:36:29

MDK的bug还是我的bug----volatile被优化

程序没有开中断,其实MDK也不会识别你有没有开中断(其实我是在开中断的时候试出来的)下面的语句while里面翻译错了~,声明我用的是D版


typedef unsigned long ulong;

volatile ulong *Pt;

int main(void)
{
        volatile ulong i;
        Pt = &i;
        while(1)
        {
                *(volatile ulong *)0x50004000 = i;
        }               
}

void SysTick_Handler(void)
{
        ++*Pt;
}

上面的volatile变量翻译的不对,汇编如下:
               main:
0x00000168 B508      PUSH   {r3,lr}
0x0000016A 4906      LDR      r1,; @0x00000184
0x0000016C 4668      MOV      r0,sp
0x0000016E 6008      STR      r0,
0x00000170 4905      LDR      r1,; @0x00000188
0x00000172 9800      LDR      r0,
0x00000174 6008      STR      r0,
0x00000176 E7FD      B      0x00000174                这里一直这样循环,也没有加载过程,郁闷ing,优化级别调到最低没事~
               SysTick_Handler:
0x00000178 4802      LDR      r0,; @0x00000184
0x0000017A 6800      LDR      r0,
0x0000017C 6801      LDR      r1,
0x0000017E 1C49      ADDS   r1,r1,#1
0x00000180 6001      STR      r1,
0x00000182 4770      BX       lr

大家看看,是不是用D版都会这样?还是我的表达有问题~希望是我哪里写错了。我用的是MDK V4.03

WithSword 发表于 2010-5-3 19:52:26

看不出有什么问题啊,i是局部变量,本来就会放在寄存器中而不需要加载。

david_ming 发表于 2010-5-3 19:57:00

volatile应该不分的呀,即使是局部变量我既然声明成了volatile那么他就要去源地址去取东东呀

linnjing 发表于 2010-5-3 19:57:05

把 i 定义成全局变量试试?!

还有,LZ用的编译程序版本多少?

david_ming 发表于 2010-5-3 20:27:28

回复【3楼】linnjing
把 i 定义成全局变量试试?!
还有,lz用的编译程序版本多少?
-----------------------------------------------------------------------

全局的就变味道啦(全局可以),关键是局部的volatile就不管用了吗,郁闷ing

我的版本是
http://cache.amobbs.com/bbs_upload782111/files_28/ourdev_551291.jpg
(原文件名:未命名.jpg)

minux 发表于 2010-5-3 20:32:16

en 编译器的错误。

GCC是这样的:
main:
      sub   sp, sp, #8
      ldr   r3, .L5
      add   r0, sp, #4
      mov   r2, #1342177280
      str   r0,
      add   r2, r2, #16384
.L2:
      ldr   r1,
      str   r1,
      b       .L2
.L6:
      .align2
.L5:
      .word   Pt

david_ming 发表于 2010-5-3 21:07:03

回复【5楼】minux 啊啊?
en 编译器的错误。
gcc是这样的:
main:
      sub   sp, sp, #8
      ldr   r3, .l5
      add   r0, sp, #4
      mov   r2, #1342177280
      str   r0,
      add   r2, r2, #16384
.l2:
      ldr   r1,
      str   r1,
      b       .l2
.l6:
      .align2
.l5:
      .word   pt

-----------------------------------------------------------------------
这样倒是合理
.L2:
      ldr   r1,
      str   r1,
      b       .L2

dianzidog 发表于 2010-5-3 21:17:35

这个要看编译器的说明,里面一定有讲的,volatile的作用范围

tiancaigao7 发表于 2010-5-3 21:36:39

试试最新版,估计应该会修正这个问题

linnjing 发表于 2010-5-3 21:49:29

那再这样试试,

*(volatile ulong *)0x50004000 = *Pt

其实感觉这样做比较正常

typedef unsigned long ulong;

volatile ulong i;

int main(void)
{
while(1)
{
*(volatile ulong *)0x50004000 = i;
}   
}

void SysTick_Handler(void)
{
++i;
}

你这样做算什么?检测KEIL MDK?!你这就是变着法的折腾嘛!!
不如这样,把这个问题问问Keil,搞不好算你发现了一个BUG,keil回头送你一套正版的也说不定呢

dr2001 发表于 2010-5-3 22:17:29

回复【楼主位】david_ming
-----------------------------------------------------------------------

LZ这么写代码不是太好的习惯。有点类似于考察编译器的推断行为。
LZ的代码总的来说,都是对i的读操作,无论是i的地址还是i的数据。对这种情况,编译器可以有不同的选择:A,对volatile的变量不优化,每次操作必须造成内存访问;B,跟踪对i的各种引用关系和操作,决定何种程度的优化;C,单纯看对volatile变量自身的引用关系,决定何种优化。

Keil可能用的是C;GCC可能是A;B的消耗太高,估计不会有人用。。。
这个问题,应该说,不应该是编译器的Bug;而是采用了不够良好的描述,在编译器的优化策略下导致的问题。估计LZ用的Keil也不是Debug模式的。

minux 发表于 2010-5-3 22:20:40

【7楼】 dianzidog

volatile的语义是C标准里明确定义的。

s99060 发表于 2010-5-3 22:42:54

咦?局部变量谁来改它?

wuzhujian 发表于 2010-5-3 23:49:34

LZ,你是不是吃饱了?

david_ming 发表于 2010-5-4 11:15:54

用局部变量是因为尽量提高代码的复用性,我写的这个代码是精简化了的,我的两个程序是在不同的文件里边,Systick函数是单独的一个文件,然后include进来,上面的代码只是验证一下KEIL

dr2001 发表于 2010-5-4 13:39:49

尝试给一个明确写操作,避免只读优化:i = (volatile unsigned long *)(&i);在While前边,试试看吧。

尽管不清楚你为什么不在i上加个static。毕竟你的pt是全局的,要可重入也不是这样的啊。

david_ming 发表于 2010-5-4 14:13:15

回复【15楼】dr2001
尝试给一个明确写操作,避免只读优化:i = (volatile unsigned long *)(&i);在while前边,试试看吧。
尽管不清楚你为什么不在i上加个static。毕竟你的pt是全局的,要可重入也不是这样的啊。
-----------------------------------------------------------------------

只要是使 i 分配在全局区域的关键字都可以正确执行,问题锁定在局部变量的volatile是不是比较矛盾

dr2001 发表于 2010-5-4 14:43:05

回复【16楼】david_ming
-----------------------------------------------------------------------
我所说的方式,和把i放在全局区域或赋予全局的生存期,都没有关系。因为LZ位代码中,对volatile的i没有任何写操作,因而编译器完成了只读优化。那么,我们在While的前边,加入一个volatile的写操作,可能会导致编译器放弃对i的只读优化。

局部作用域,局部生存期的动态变量,volatile的,还是未见直接写入的(编译器处理的时候,看到的都是Read操作),要不要优化呢……
另外,引用一个未初始化的自动变量。。。也不是说是个编译器就能弄清楚的行为。

david_ming 发表于 2010-5-4 15:21:27

回楼上,用你的方法报错如下:

main.c(12): error:#513: a value of type "volatile ulong *" cannot be assigned to an entity of type "ulong"

minux 发表于 2010-5-4 15:59:54

【17楼】 dr2001

不啊,加了volatile后,编译器就不应该做那些优化了。
尤其是编译器可以推断出i的地址并非绝对不会有写入,因为已经给全局变量赋予过&i了(同时那个全局指针还是volatile ulong *类型)。
按照编译器的行为方式,只要不能确保不会在别的地方写入的volatile变量,肯定是不能做只读优化的。

总之肯定是编译器对于volatile的处理有误。

david_ming 发表于 2010-5-4 16:03:32

我在问问KEIL的FAE,英文不太好

david_ming 发表于 2010-5-4 17:53:45

看来真像是的MDK的bug,那个FAE说要去向Team反映

s99060 发表于 2010-5-4 22:08:56

i不是static哦

laoshuhunya 发表于 2019-1-31 22:01:11

无意中看到这帖子,这么好的主题成为烂尾楼岂不可惜了{:titter:}
RVMKD编译器是嵌入式IDE里遵守C标准的典范,所以不要轻易怀疑它的语法BUG。
第一,按照C语言规范,类型限定符(volatile 和 const)只有通过左值访问时才会起作用,而楼主程序里的变量“ i ” 始终是作为右值访问的,编译器可以对它执行优化。
第二,volatile 关键字并不是要求编译器对某个对象不要执行优化,它的确切的含义是:对某个对象的优化不能跨越序列点。如何理解这句话?虚拟世界实验室的《重入与线程安全性解惑》对此做了精确的解释。

bolizhicheng204 发表于 2019-1-31 22:39:22

laoshuhunya 发表于 2019-1-31 22:01
无意中看到这帖子,这么好的主题成为烂尾楼岂不可惜了
RVMKD编译器是嵌入式IDE里遵守C标准的典 ...

楼上好厉害,学习了

Gorgon_Meducer 发表于 2019-1-31 23:22:39

Arm Compiler 5.06的volatile优化bug在之前的版本中的确存在过……只不过基本上都不是中国人发现的……默默飘过。
考虑到大家生存不易,代码能运行结果对就行了,我就不强求所有人遇到问题都像邻国从业人员那样打破砂锅问到底了。
页: [1]
查看完整版本: MDK的bug还是我的bug----volatile被优化