assembly - 为什么 avr-gcc 将 "push r1"指令添加到函数的开头?

标签 assembly avr avr-gcc

我正在查看使用 avr-gcc 编译后编写的一些代码的生成程序集。具体来说,我使用 -Os 选项进行了编译。总的来说,输出是我所期望的,但我无法理解的是发出的指令 push r1 。更奇怪的是函数末尾的补充指令是 pop r0。所以 r1 的值正在保存,但它似乎已恢复为 r0

根据此处的文档:

https://gcc.gnu.org/wiki/avr-gcc#Register_Layout

寄存器 r1 始终包含零,但函数可以在恢复寄存器时使用该寄存器。恢复它将是 ldi r1, 0x0,根据我的理解,不需要 push,pop。

这是编译版本的 C 代码和反汇编示例。它是用 -Os 编译的。它有点长,但我必须编写一个大函数才能让编译器发出它。

C 代码:

void mqtt_create_connect(mqtt_parser *p, mqtt_connect_config *cfg){
  uint8_t idx = 0;
  p->buffer[idx++] = MQTT_CTRL_CONNECT << 4;
  idx++; // skip remaining length for now
  p->buffer[idx++] = 0x0;
  p->buffer[idx++] = 0x4;
  p->buffer[idx++] = 'M';
  p->buffer[idx++] = 'Q';
  p->buffer[idx++] = 'T';
  p->buffer[idx++] = 'T';
  p->buffer[idx++] = 0x04; // protocol level 3.1.1
  p->buffer[idx++] = 
    MQTT_CONNECT_FLAG_CLEAN_SESSION;
  push_uint16_t(p, cfg->keepAliveInterval, &idx);
  push_charptr(p, cfg->clientIdentifier, &idx);
  p->bufferIdx = idx;

  // fill in remaining length
  p->buffer[1] = p->bufferIdx - 2;
}

反汇编:

0000006a <mqtt_create_connect>:
  6a:   0f 93           push    r16
  6c:   1f 93           push    r17
  6e:   cf 93           push    r28
  70:   df 93           push    r29
  72:   1f 92           push    r1
  74:   cd b7           in      r28, 0x3d       ; 61
  76:   de b7           in      r29, 0x3e       ; 62
  78:   8c 01           movw    r16, r24
  7a:   fb 01           movw    r30, r22
  7c:   80 e1           ldi     r24, 0x10       ; 16
  7e:   d8 01           movw    r26, r16
  80:   11 96           adiw    r26, 0x01       ; 1
  82:   8c 93           st      X, r24
  84:   11 97           sbiw    r26, 0x01       ; 1
  86:   13 96           adiw    r26, 0x03       ; 3
  88:   1c 92           st      X, r1
  8a:   13 97           sbiw    r26, 0x03       ; 3
  8c:   84 e0           ldi     r24, 0x04       ; 4
  8e:   14 96           adiw    r26, 0x04       ; 4
  90:   8c 93           st      X, r24
  92:   14 97           sbiw    r26, 0x04       ; 4
  94:   9d e4           ldi     r25, 0x4D       ; 77
  96:   15 96           adiw    r26, 0x05       ; 5
  98:   9c 93           st      X, r25
  9a:   15 97           sbiw    r26, 0x05       ; 5
  9c:   91 e5           ldi     r25, 0x51       ; 81
  9e:   16 96           adiw    r26, 0x06       ; 6
  a0:   9c 93           st      X, r25
  a2:   16 97           sbiw    r26, 0x06       ; 6
  a4:   94 e5           ldi     r25, 0x54       ; 84
  a6:   17 96           adiw    r26, 0x07       ; 7
  a8:   9c 93           st      X, r25
  aa:   17 97           sbiw    r26, 0x07       ; 7
  ac:   18 96           adiw    r26, 0x08       ; 8
  ae:   9c 93           st      X, r25
  b0:   18 97           sbiw    r26, 0x08       ; 8
  b2:   19 96           adiw    r26, 0x09       ; 9
  b4:   8c 93           st      X, r24
  b6:   19 97           sbiw    r26, 0x09       ; 9
  b8:   82 e0           ldi     r24, 0x02       ; 2
  ba:   1a 96           adiw    r26, 0x0a       ; 10
  bc:   8c 93           st      X, r24
  be:   1a 97           sbiw    r26, 0x0a       ; 10
  c0:   80 81           ld      r24, Z
  c2:   91 81           ldd     r25, Z+1        ; 0x01
  c4:   1b 96           adiw    r26, 0x0b       ; 11
  c6:   9c 93           st      X, r25
  c8:   1b 97           sbiw    r26, 0x0b       ; 11
  ca:   9c e0           ldi     r25, 0x0C       ; 12
  cc:   99 83           std     Y+1, r25        ; 0x01
  ce:   1c 96           adiw    r26, 0x0c       ; 12
  d0:   8c 93           st      X, r24
  d2:   62 81           ldd     r22, Z+2        ; 0x02
  d4:   73 81           ldd     r23, Z+3        ; 0x03
  d6:   ae 01           movw    r20, r28
  d8:   4f 5f           subi    r20, 0xFF       ; 255
  da:   5f 4f           sbci    r21, 0xFF       ; 255
  dc:   c8 01           movw    r24, r16
  de:   0e 94 00 00     call    0       ; 0x0 <push_charptr>
  e2:   89 81           ldd     r24, Y+1        ; 0x01
  e4:   f8 01           movw    r30, r16
  e6:   ef 5b           subi    r30, 0xBF       ; 191
  e8:   ff 4f           sbci    r31, 0xFF       ; 255
  ea:   80 83           st      Z, r24
  ec:   82 50           subi    r24, 0x02       ; 2
  ee:   f8 01           movw    r30, r16
  f0:   82 83           std     Z+2, r24        ; 0x02
  f2:   0f 90           pop     r0
  f4:   df 91           pop     r29
  f6:   cf 91           pop     r28
  f8:   1f 91           pop     r17
  fa:   0f 91           pop     r16
  fc:   08 95           ret

push r1pop r0 的目的是什么?

最佳答案

这里有两个技巧。

首先,gcc 需要在堆栈上保留一个字节的位置——用于 uint8_t idx; 堆栈指针需要递减并保存回 SPH:SPL。但是这种双out 操作可能会因灾难性的结果而中断。所以它必须由 cli/sei 对包裹——额外的代码和时间。 推送任何寄存器以原子方式给出相同的结果并使用短代码。

其次:正如您所注意到的,根据 avr-gcc/avr-libc 约定,r1__zero_reg__,假定在任何 C 代码中始终为零。 所以,push r1不仅为idx预留了空间,还用0对其进行了初始化。

函数尾声中的

pop r0 恢复堆栈指针。按照上述约定,r0__temp_reg__,可以被任何 C 代码破坏的临时寄存器。因此编译器可以随时销毁其内容。

附注该函数不改变r1,因此不需要r1恢复。

关于assembly - 为什么 avr-gcc 将 "push r1"指令添加到函数的开头?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51958795/

相关文章:

c - 如何获取一个16位变量的高8位?

c - 将多个目标文件链接到二进制 AVR GCC

c - AVR GCC - 使用静态库 - undefined reference 错误

c++ - Arduino 库包括 Sketch 目录中的文件

c - AVR C 编程 - 全局数组

c - mac安装AVR开发平台出现错误

基于寄存器值的汇编调用中断

c - 在推送/弹出其他寄存器时从堆栈访问相对于 EBP 的函数参数?

c - 指针内存错误

assembly - 汇编程序 (fasm) - 读取字符