搜索
bottom↓
回复: 7

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

[复制链接]

出0入0汤圆

发表于 2009-7-9 22:31:31 | 显示全部楼层 |阅读模式
我写的是一段由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,请指教

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

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

出0入0汤圆

发表于 2009-7-9 23:19:58 | 显示全部楼层
1. 注意一下运算符号的优先级别,专门查了一下,位移运算 >> << 优先于取反  ~,当然可以直接优化为左右移。
   建议不知道优先级的运算,都用括号,建议除了加减乘除法,都用括号。
   PORTB = ~((~PORTB) >> 1);

   个人喜欢这样写:
    PORTB <<= 1;
    PORTB  |= 1;

2. LDD这种指令用得比较少,对于IO寄存器,本来可以直接用IN OUT 指令的。COM指令取补码(取反加1)
   一个数据两次取补码等于自身,在第一句中,结果还是相当于左移一bit,只是没有优化而已。

出0入0汤圆

 楼主| 发表于 2009-7-10 00:29:04 | 显示全部楼层
我有考虑到优先级问题,加了括号结果还是一样,我用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的区别,其它应该一样才对啊,真是奇怪

出0入0汤圆

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

出0入0汤圆

发表于 2009-7-10 13:06:07 | 显示全部楼层
我 1L 犯错误了。

    原因应该是这样的:<< >> 运算应该是带符号的,而不是逻辑左右移,睡觉,晚上再验证。

出0入0汤圆

发表于 2009-7-10 21:05:54 | 显示全部楼层
编写了个程序,验证了一下。

int main( void )
{
    INT8U   i=0;
   
    volatile INT8U   x=0xFE;
        volatile INT8S   y=0xFE;
   
    while( 1 )
    {
        if( i & 0x01 ) {
            PORTB = ~((~PORTB)>>1);
            x = ~((~x)>>1);
            y = ~((~y)>>1);
        }      
        else {
            PORTB = ~((~PORTB)<<1);
            x = ~((~x)<<1);
            y = ~((~y)<<1);
        }
        i++;
    }
}



反汇编后:

29:                   PORTB = ~((~PORTB)>>1);
+00000064:   B185        IN      R24,0x05         In from I/O location
+00000065:   9586        LSR     R24              Logical shift right
+00000066:   B985        OUT     0x05,R24         Out to I/O location
30:                   x = ~((~x)>>1);
+00000067:   8189        LDD     R24,Y+1          Load indirect with displacement
+00000068:   9586        LSR     R24              Logical shift right
+00000069:   8389        STD     Y+1,R24          Store indirect with displacement
31:                   y = ~((~y)>>1);
+0000006A:   818A        LDD     R24,Y+2          Load indirect with displacement
+0000006B:   2799        CLR     R25              Clear Register
+0000006C:   FD87        SBRC    R24,7            Skip if bit in register cleared
+0000006D:   9590        COM     R25              One's complement
+0000006E:   9580        COM     R24              One's complement
+0000006F:   9590        COM     R25              One's complement
+00000070:   9595        ASR     R25              Arithmetic shift right
+00000071:   9587        ROR     R24              Rotate right through carry
+00000072:   C018        RJMP    PC+0x0019        Relative jump
34:                   PORTB = ~((~PORTB)<<1);
+00000073:   B185        IN      R24,0x05         In from I/O location
+00000074:   E090        LDI     R25,0x00         Load immediate
+00000075:   9580        COM     R24              One's complement
+00000076:   9590        COM     R25              One's complement
+00000077:   0F88        LSL     R24              Logical Shift Left
+00000078:   1F99        ROL     R25              Rotate Left Through Carry
+00000079:   9580        COM     R24              One's complement
+0000007A:   B985        OUT     0x05,R24         Out to I/O location
35:                   x = ~((~x)<<1);
+0000007B:   8189        LDD     R24,Y+1          Load indirect with displacement
+0000007C:   E090        LDI     R25,0x00         Load immediate
+0000007D:   9580        COM     R24              One's complement
+0000007E:   9590        COM     R25              One's complement
+0000007F:   0F88        LSL     R24              Logical Shift Left
+00000080:   1F99        ROL     R25              Rotate Left Through Carry
+00000081:   9580        COM     R24              One's complement
+00000082:   8389        STD     Y+1,R24          Store indirect with displacement
36:                   y = ~((~y)<<1);
+00000083:   818A        LDD     R24,Y+2          Load indirect with displacement
+00000084:   2799        CLR     R25              Clear Register
+00000085:   FD87        SBRC    R24,7            Skip if bit in register cleared
+00000086:   9590        COM     R25              One's complement
+00000087:   9580        COM     R24              One's complement
+00000088:   9590        COM     R25              One's complement
+00000089:   0F88        LSL     R24              Logical Shift Left
+0000008A:   1F99        ROL     R25              Rotate Left Through Carry
+0000008B:   9580        COM     R24              One's complement
+0000008C:   838A        STD     Y+2,R24          Store indirect with displacement
+0000008D:   5F2F        SUBI    R18,0xFF         Subtract immediate
+0000008E:   4F3F        SBCI    R19,0xFF         Subtract immediate with carry
+0000008F:   CFD2        RJMP    PC-0x002D        Relative jump
+00000090:   CFFF        RJMP    PC-0x0000        Relative jump
+00000091:   95FF9590    CALL    0x003F9590       Call subroutine
+00000093:   0F88        LSL     R24              Logical Shift Left
+00000094:   1F99        ROL     R25              Rotate Left Through Carry
+00000095:   9580        COM     R24              One's complement
+00000096:   838A        STD     Y+2,R24          Store indirect with displacement
+00000097:   5F2F        SUBI    R18,0xFF         Subtract immediate
+00000098:   4F3F        SBCI    R19,0xFF         Subtract immediate with carry
+00000099:   CFC9        RJMP    PC-0x0036        Relative jump
+0000009A:   CFFF        RJMP    PC-0x0000        Relative jump



查了一下资料。

关于c中的移位运算

1.移位运算的两个运算数必须是整形表达式,在运算数的两边完成整形提升,表达式整体的类型与提升后的左运算数相同。

2.左移位 exp1<<exp2的表达式使得exp1的位被按expr2指明的数目进行左移,并在低端移入0

3.右移位运算符>>与左移位运算符是不对称的。对左操作数进行移位取决于左操作数的类型:

(1)如果左操作数是无符号(或带符号的非负数),则左边移入0

(2)如果左操作数是带符号的负数,则实现者可以选择补0或者把左操作数最左边的位移入(常见的是填充符号位)。因此,左操作数为负的带符号数值,而右操作数为非0时,>>的应用是不可移植的。

4. 如果右操作数是负数,则移位运算(左移和右移)的结果是未定义的。

5.如果右操作数的值大于或等于左操作数的宽度(位数),则结果也是未定义的。如果右操作数为0,则结果等于左操作数。


-------------------------------
    所以,还是改改程序吧,这个平时没注意,又学会一点。

出0入0汤圆

 楼主| 发表于 2009-7-11 00:23:09 | 显示全部楼层
我试着根据你的资料归纳下
有两点与这程序相关的:
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
得出的结果是我想要的,所以这个假设应该行得通

出0入0汤圆

 楼主| 发表于 2009-7-11 00:38:49 | 显示全部楼层
感谢楼上的兄台,看来得自己多查查资料才是,avrfreaks上面有人就问同样的问题,看到另一种解决方法是
PORTB = ~(((unsigned char)(~PORTB))>>1),把左操作数的类型再强制转换过来就行
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2024-5-16 02:03

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

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