avr-gcc在设置寄存器时生成汇编

时间:2014-05-09 16:07:21

标签: c assembly avr avr-gcc

我正在查看从以下C代码生成的asm。

uint8_t anode = lednum / 4;
PORTB = (1 << anode);

我使用O2优化获得以下内容:

00000040 <setout>:
  40:   86 95           lsr r24
  42:   86 95           lsr r24
  44:   21 e0           ldi r18, 0x01   ; 1
  46:   30 e0           ldi r19, 0x00   ; 0
  48:   08 2e           mov r0, r24
  4a:   01 c0           rjmp    .+2     ; 0x4e
  4c:   22 0f           add r18, r18
  4e:   0a 94           dec r0
  50:   ea f7           brpl    .-6     ; 0x4c
  52:   28 bb           out 0x18, r18   ; 24
  54:   08 95           ret

我得到lsr只是换了两个(除以4)但为什么以下所有工作都在两个寄存器中完成? 是因为1被解释为int,16位或其他东西。 我想象只需要一个寄存器,例如r18。

[EDIT1] 添加完整代码

[EDIT2] 根据{{​​3}} r18和r19可以在任何函数内使用(不包括ISR)而不进行恢复。两者都不在函数之外使用。

[EDIT3] 完全最小的例子。

#include <avr/io.h>
#include <stdint.h>

void
setout(uint8_t lednum)
{
        uint8_t anode = lednum / 4;
        PORTB = (1 << anode);
}

void
main(void)
{
        while(1)
        {
            for (int i = 0; i < 10; ++i)
                setout(i);
        }
}

给我:

bin/scanner.elf:     file format elf32-avr


Disassembly of section .text:

00000000 <__vectors>:
   0:   0e c0           rjmp    .+28        ; 0x1e <__ctors_end>
   2:   15 c0           rjmp    .+42        ; 0x2e <__bad_interrupt>
   4:   14 c0           rjmp    .+40        ; 0x2e <__bad_interrupt>
   6:   13 c0           rjmp    .+38        ; 0x2e <__bad_interrupt>
   8:   12 c0           rjmp    .+36        ; 0x2e <__bad_interrupt>
   a:   11 c0           rjmp    .+34        ; 0x2e <__bad_interrupt>
   c:   10 c0           rjmp    .+32        ; 0x2e <__bad_interrupt>
   e:   0f c0           rjmp    .+30        ; 0x2e <__bad_interrupt>
  10:   0e c0           rjmp    .+28        ; 0x2e <__bad_interrupt>
  12:   0d c0           rjmp    .+26        ; 0x2e <__bad_interrupt>
  14:   0c c0           rjmp    .+24        ; 0x2e <__bad_interrupt>
  16:   0b c0           rjmp    .+22        ; 0x2e <__bad_interrupt>
  18:   0a c0           rjmp    .+20        ; 0x2e <__bad_interrupt>
  1a:   09 c0           rjmp    .+18        ; 0x2e <__bad_interrupt>
  1c:   08 c0           rjmp    .+16        ; 0x2e <__bad_interrupt>

0000001e <__ctors_end>:
  1e:   11 24           eor r1, r1
  20:   1f be           out 0x3f, r1    ; 63
  22:   cf e5           ldi r28, 0x5F   ; 95
  24:   d1 e0           ldi r29, 0x01   ; 1
  26:   de bf           out 0x3e, r29   ; 62
  28:   cd bf           out 0x3d, r28   ; 61
  2a:   0d d0           rcall   .+26        ; 0x46 <main>
  2c:   1e c0           rjmp    .+60        ; 0x6a <_exit>

0000002e <__bad_interrupt>:
  2e:   e8 cf           rjmp    .-48        ; 0x0 <__vectors>

00000030 <setout>:
  30:   86 95           lsr r24
  32:   86 95           lsr r24
  34:   21 e0           ldi r18, 0x01   ; 1
  36:   30 e0           ldi r19, 0x00   ; 0
  38:   08 2e           mov r0, r24
  3a:   01 c0           rjmp    .+2         ; 0x3e <__SP_H__>
  3c:   22 0f           add r18, r18
  3e:   0a 94           dec r0
  40:   ea f7           brpl    .-6         ; 0x3c <setout+0xc>
  42:   28 bb           out 0x18, r18   ; 24
  44:   08 95           ret

00000046 <main>:
  46:   40 e0           ldi r20, 0x00   ; 0
  48:   21 e0           ldi r18, 0x01   ; 1
  4a:   30 e0           ldi r19, 0x00   ; 0
  4c:   84 2f           mov r24, r20
  4e:   86 95           lsr r24
  50:   86 95           lsr r24
  52:   b9 01           movw    r22, r18
  54:   02 c0           rjmp    .+4         ; 0x5a <main+0x14>
  56:   66 0f           add r22, r22
  58:   77 1f           adc r23, r23
  5a:   8a 95           dec r24
  5c:   e2 f7           brpl    .-8         ; 0x56 <main+0x10>
  5e:   68 bb           out 0x18, r22   ; 24
  60:   4f 5f           subi    r20, 0xFF   ; 255
  62:   4a 30           cpi r20, 0x0A   ; 10
  64:   98 f3           brcs    .-26        ; 0x4c <main+0x6>
  66:   40 e0           ldi r20, 0x00   ; 0
  68:   f1 cf           rjmp    .-30        ; 0x4c <main+0x6>

0000006a <_exit>:
  6a:   f8 94           cli

0000006c <__stop_program>:
  6c:   ff cf           rjmp    .-2         ; 0x6c <__stop_program>

它看起来像是内联的,但它仍然使用两个寄存器而不是一个。

1 个答案:

答案 0 :(得分:0)

gcc 4.9.0稍微差一点,烧掉两个寄存器,加上一条指令,与你正在使用的相比。

#define PORTB (*(volatile unsigned char *)(0x18+0x20))
void setout(unsigned char lednum)
{
        unsigned char  anode = lednum / 4;
        PORTB = (1 << anode);
}

avr-gcc -O2 -mmcu=avr2 -c fun.c -o fun.o
avr-objdump -D fun.o

00000000 <setout>:
   0:   28 2f           mov r18, r24
   2:   26 95           lsr r18
   4:   26 95           lsr r18
   6:   81 e0           ldi r24, 0x01   ; 1
   8:   90 e0           ldi r25, 0x00   ; 0
   a:   02 2e           mov r0, r18
   c:   00 c0           rjmp    .+0         ; 0xe <setout+0xe>
   e:   88 0f           add r24, r24
  10:   0a 94           dec r0
  12:   02 f4           brpl    .+0         ; 0x14 <setout+0x14>
  14:   88 bb           out 0x18, r24   ; 24
  16:   08 95           ret

我同意约阿希姆,我认为1正在被提升为更大的东西。有点像人们犯下的错误:

float a;
...
a = a + 1.0;

如果你这样做

#define PORTB (*(volatile unsigned char *)(0x18+0x20))
void setout(unsigned char one, unsigned char lednum)
{
        unsigned char  anode = lednum / 4;
        PORTB = (one << anode);
}

我明白了

00000000 <setout>:
   0:   66 95           lsr r22
   2:   66 95           lsr r22
   4:   06 2e           mov r0, r22
   6:   00 c0           rjmp    .+0         ; 0x8 <setout+0x8>
   8:   88 0f           add r24, r24
   a:   0a 94           dec r0
   c:   02 f4           brpl    .+0         ; 0xe <setout+0xe>
   e:   88 bb           out 0x18, r24   ; 24
  10:   08 95           ret

我认为编译器强制该常量为规则的16位值,但优化器发现它不需要移位16位。 16位常数的分配仍然存在。所以我认为约阿希姆钉了它...发一个答案,所以我们可以投票给它......