搜索
bottom↓
回复: 17

GCC同一语句编译后转换为汇编语句并且出现错误,求救!

[复制链接]

出0入0汤圆

发表于 2009-7-10 02:56:33 | 显示全部楼层 |阅读模式
我写的是一段由flag控制流水灯左流右流的语句,同样的语句只是位移符号不同,编译结果总是不同
if(flag == LEFT){
            //_delay_ms(500);
            if (PORTB == 0x7f) PORTB = 0b11111110;
            else               PORTB = ~(~PORTB << 1);
        }else{
            //_delay_ms(500);
            if (PORTB == 0xfe) PORTB = 0b01111111;
            else               PORTB = ~(~PORTB >> 1);
        }

其中关键的两句
else               PORTB = ~(~PORTB << 1);
else               PORTB = ~(~PORTB >> 1);
只是一个位移符号差异,但是汇编出来的语句完全不同
如下
第一句是这样的
28:                   else               PORTB = ~(~PORTB << 1);
+0000005D:   8180        LDD       R24,Z+0        Load indirect with displacement
+0000005E:   E090        LDI       R25,0x00       Load immediate
+0000005F:   9580        COM       R24            One's complement
+00000060:   9590        COM       R25            One's complement
+00000061:   0F88        LSL       R24            Logical Shift Left
+00000062:   1F99        ROL       R25            Rotate Left Through Carry
+00000063:   9580        COM       R24            One's complement
+00000064:   8380        STD       Z+0,R24        Store indirect with displacement
+00000065:   CFEE        RJMP      PC-0x0011      Relative jump
而第二句却变成这样,明显它忽略了我的取反的运算,即使我把优化等级改到O0也没用
32:                   else               PORTB = ~(~PORTB >> 1);
+0000006B:   8180        LDD       R24,Z+0        Load indirect with displacement
+0000006C:   9586        LSR       R24            Logical shift right
+0000006D:   8380        STD       Z+0,R24        Store indirect with displacement
+0000006E:   CFE5        RJMP      PC-0x001A      Relative jump

第一次使用GCC,请指教



我有考虑到优先级问题,加了括号结果还是一样,我用0s级别再优化一次,得到的结果是这样的
28:                   else               PORTB = ~((~PORTB) << 1);
+00000057:   B388        IN        R24,0x18       In from I/O location
+00000058:   E090        LDI       R25,0x00       Load immediate
+00000059:   9580        COM       R24            One's complement
+0000005A:   9590        COM       R25            One's complement
+0000005B:   0F88        LSL       R24            Logical Shift Left
+0000005C:   1F99        ROL       R25            Rotate Left Through Carry
+0000005D:   9580        COM       R24            One's complement
+0000005E:   C007        RJMP      PC+0x0008      Relative jump

32:                   else               PORTB = ~((~PORTB) >> 1);
+00000064:   B388        IN        R24,0x18       In from I/O location
+00000065:   9586        LSR       R24            Logical shift right
+00000066:   BB88        OUT       0x18,R24       Out to I/O location
+00000067:   CFE6        RJMP      PC-0x0019      Relative jump

很明显,第一句汇编后的结果是正确的,真正做到取反-移位-再取反
而第二句加括号后,汇编结果总是直接将数值移位-输出
两句唯一的区别仅仅在于 左移位和右移位
按我一般的想法就是两者汇编出来的结果应该仅仅是在LSL,LSR的区别,其它应该一样才对啊,真是奇怪
 


我在草稿上演算了一遍又一遍,算法肯定是没问题的
个人估计,导致 《 》两种不同的结果可能是这样的原因,<<其实相当于原来的数值翻一倍速,而>>等于数值除以二,编译器认为,在除法进行之前取反,进行除法后再取反,结果是等同于直接进行除法,所以两个~被优化掉了,不过实在是很怪异,怎么也搞不行

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2009-7-10 03:02:51 | 显示全部楼层
取端口电平用PINB

出0入0汤圆

 楼主| 发表于 2009-7-10 03:12:24 | 显示全部楼层
改成用PINB,结果还是一样,还是给优化掉,按位取反的操作给彻底优化掉,如果把这一句拆成三句就能按我的要求做

出0入0汤圆

发表于 2009-7-10 06:28:17 | 显示全部楼层
原因不清楚,不过为啥写成这样啊?PORTB=(PORTB>>1)|0x80;不好吗?

出0入0汤圆

发表于 2009-7-10 06:37:11 | 显示全部楼层
仔细看了一下,光看这一段看不出来的,你还要看lsr指令之前的c标志。不过gcc应该没问题的。
你看一下lsr的时候c标志是什么再说了

出0入0汤圆

发表于 2009-7-10 06:56:28 | 显示全部楼层
PORTB = ~(~PORTB >> 1);

PORTB =  (PORTB >> 1);
结果是一样的。


GCC把上面的式子优化成下面的式子,没有任何问题。
只能说明GCC的优化好。

出0入0汤圆

发表于 2009-7-10 07:11:11 | 显示全部楼层
楼上说的不对吧,~是按位取反,>>是右移,如果PORTB=0xf0
PORTB = ~(~PORTB >> 1); //这一句的结果是0xf8
PORTB =  (PORTB >> 1);  //这一句的结果是0x78
怎么可能是一样的

出0入0汤圆

发表于 2009-7-10 08:29:49 | 显示全部楼层
PORTB = ~(~PORTB >> 1); //这一句的结果是0xf8
PORTB =  (PORTB >> 1);  //这一句的结果是0x78
---------------------------------------------
楼上你搞错了,PORTB = ~(~PORTB >> 1);的结果仍然是0x78。


假定int是16位,

0xf0>>1          结果是:(int)0x0078


~0xf0            结果是:(int)0xff0f
(~0xf0)>>1       结果是:(int)0xff87
~((~0xf0)>>1)    结果是:(int)0x0078

(0xf0>>1)的值与~((~0xf0)>>1)的值完全一致。

出0入0汤圆

发表于 2009-7-10 10:04:07 | 显示全部楼层
哦,汇编用多了,忘记了整形提升这回事了。
还是改成PORTB=(PORTB>>1)|0x80;就不会有问题了。

出0入0汤圆

 楼主| 发表于 2009-7-10 14:10:31 | 显示全部楼层
楼上你搞错了,PORTB = ~(~PORTB >> 1);的结果仍然是0x78。


假定int是16位,

0xf0>>1          结果是:(int)0x0078


~0xf0            结果是:(int)0xff0f
(~0xf0)>>1       结果是:(int)0xff87
~((~0xf0)>>1)    结果是:(int)0x0078

(0xf0>>1)的值与~((~0xf0)>>1)的值完全一致。
--------------------------------------------------------
你好,我对这流程有点疑问,
~0xf0            结果是:(int)0xff0f 这一步没问题
(~0xf0)>>1       结果是:(int)0xff87 这一步问题就来了,为什么从int)0xff0f 右移一位,最高位不是填充0而变成0x7f87,如果是填充0的话,那么再取反就变成了(int)0x8078了,这是第一点疑问
第二点疑问是,这里假设int为16位,但我是对PORTB这个寄存器直接进行操作,那么这个PORTB是多少位呢?


还有,回3楼,我考虑过C位的问题,也对C位进行操作过,结果没变,因为右移是把最右边的位弹到C里,而不是把C弹到最左边

出0入0汤圆

发表于 2009-7-10 15:06:22 | 显示全部楼层
portb 是*(unsigned char *)类型的,你可以看头文件里的定义

出0入0汤圆

 楼主| 发表于 2009-7-10 15:25:31 | 显示全部楼层
我之前是用51的,51里头unsigned char是8位的,不知道在AVRGCC里是什么情况,还请指教

出0入0汤圆

发表于 2009-7-10 16:06:25 | 显示全部楼层
avr 的char也是8位的,但是标准c在做运算的时候,小于int类型的整数,例如char short 或者union等都会先转换成int,如果int可以表示的了的话,表示不了就转换成unsigned int然后再进行运算。这个叫整型提升。简单地说~PORTB c会把它看成~((int)PORTB)大概是这个意思吧,具体标准你可以自己去查。

出0入0汤圆

 楼主| 发表于 2009-7-11 00:36:24 | 显示全部楼层
问题搞定了
我试着根据你的资料归纳下
有两点与这程序相关的:
1.移位运算的两个运算数必须是整形表达式,在运算数的两边完成整形提升,表达式整体的类型与提升后的左运算数相同。
3.(2)如果左操作数是带符号的负数,则实现者可以选择补0或者把左操作数最左边的位移入(常见的是填充符号位)。因此,左操作数为负的带符号数值,而右操作数为非0时,>>的应用是不可移植的。
汇编过程应该是这样的
1.假原先PORTB中的内容是0x78,将其提升为整型,0x0078
2.取反变成0xff87
3.右移一位,变成0xffc3或是0xefc3(根据“则实现者可以选择补0或者把左操作数最左边的位移入(常见的是填充符号位)”)
4.再取反,即是变成0x003c或0x803c,而再写入PORTB中,就是0x3c

得出这样的结果,和将0x78直接右移一位,变成0x3c是一样的,所以GCC编译将其优化掉了。
这样就解释得通,但我这样的解释是建立在这样一个假设上,即语句  PORTB = ~((~PORTB)>>1); 中(~PORTB)>>1这一部分,是先考虑>>两边的数据类型的
而当我将这一语句拆成三句写,再进行验证时,发现程序烧进芯片后运行正常,没有出现合在一起那种情况

1.PORTB = ~PORTB;先对PORTB取反,假设原来是0x78,取反变成0x87
2.PORTB = PORTB >> 1;第二步本是移位运算,但PORTB在GCC中定义是unsigned char,所以必须先提升为int,此时变为0x0087
提升后进行移位,变成0x0043
3.PORTB = ~PORTB;再取反变成0xffbc,写入PORTB的是0xbc
得出的结果是我想要的,所以这个假设应该行得通

这是我在另一张帖子的回帖
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3446737&bbs_id=1003

出0入0汤圆

发表于 2010-5-12 22:37:37 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-5-12 22:56:36 | 显示全部楼层
MARK 整型提升

出0入0汤圆

发表于 2012-2-22 10:44:10 | 显示全部楼层
mark~~~

出0入0汤圆

发表于 2012-2-23 11:04:54 | 显示全部楼层
mark
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-6-19 01:30

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表