我正在查看用avr-gcc
编译后编写的某些代码的汇编结果。具体来说,我使用-Os
选项进行了编译。总的来说,输出是我所期望的,但是我无法理解的是正在发出指令push r1
。甚至更奇怪的是,函数末尾的补充指令是pop r0
。因此,r1
的值已保存,但似乎已还原为r0
根据此处的文档:
https://gcc.gnu.org/wiki/avr-gcc#Register_Layout
寄存器r1
总是包含零,但是如果函数将其恢复,则可以使用该寄存器。恢复为ldi r1, 0x0
,则无需进行推翻,从我的理解中可以理解。
这是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 r1
和pop r0
的目的是什么?
答案 0 :(得分:3)
这里有两个技巧。
首先,gcc需要在堆栈上保留一个字节的空间—为uint8_t idx;
堆栈指针需要递减并保存回SPH:SPL
。但是,此两{out
”操作可能会因灾难性的结果而中断。因此,必须用cli
/ sei
对包裹-额外的代码和时间。
按下 any 寄存器会自动产生相同的结果,并使用短代码。
第二个:如您所注意,按照avr-gcc / avr-libc约定,r1
是__zero_reg__
,在任何C代码中都假定始终为零。
因此,push r1
不仅为idx
保留了空间,而且还通过0
对其进行了初始化。
pop r0
恢复堆栈指针。按照约定,r0
是__temp_reg__
,可以被任何C代码破坏的临时寄存器。因此,编译器可以随时销毁int内容。
p.s。该函数不会更改r1
,因此无需还原r1
。