【问题标题】:avr-gcc generated assembly when setting a register设置寄存器时 avr-gcc 生成的程序集
【发布时间】:2014-06-27 11:13:03
【问题描述】:

我正在查看从以下 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 只是移动了 2(除以 4),但为什么以下所有工作都在两个寄存器中完成? 是不是因为 1 被解释为 int、16 位或其他东西。 我想只需要一个寄存器,例如 r18。

[编辑1] 添加完整代码

[编辑2] 根据http://gcc.gnu.org/wiki/avr-gcc#Register_Layout r18 和 r19 可以在任何函数(不包括 ISR)内部使用而不会被恢复。两者都不在函数之外使用。

[编辑3] 完整的最小示例。

#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
  • 是的,我知道的就这么多。问题是为什么它在 r18 和 r19 中完成,而不仅仅是在 r18 中。
  • @ 就是这样。之后只有ret 指令
  • 这有点令人费解。 r19 在代码的其他地方有什么特殊作用吗?如果你用某种 NOP 替换它,程序还能工作吗?
  • @ChrisStratton 实际上根本没有使用。根据gcc.gnu.org/wiki/avr-gcc#Register_Layout,它可以在任何函数内部进行修改。如果我将调用 setout 作为主循环中的唯一内容,则不会在函数外部使用它。

标签: c assembly avr avr-gcc


【解决方案1】:

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

我同意 Joachim 的观点,我认为 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 位常数的分配保持不变。所以我认为 Joachim 成功了...发布答案以便我们投票...

【讨论】:

  • 我知道我可以做1L来推广做长。有没有类似char的东西?
  • @evading - 我尝试了一个将 1 转换为 uint8_t 的版本,但似乎没有什么不同;促销似乎是“C语言规则”的事情。
  • @ChrisStratton 我和你一样尝试了同样的(uint8_t)(1) 我得到了同样的结果。所以我想知道是否有办法做到1UC,但我从未见过这样做。
猜你喜欢
  • 1970-01-01
  • 2019-01-28
  • 2017-04-29
  • 2012-12-19
  • 1970-01-01
  • 2013-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多