And*_*and 6 c++ avr arduino compiler-optimization avr-gcc
尝试寻址时uint64,AVR gcc?¹?中的各个字节。给了我一个奇怪的序言/结尾,而使用编写的同一函数uint32_t给了我一个ret(示例函数为NOP)。
为什么gcc这样做?我该如何删除?
您可以在此处在Compiler Explorer中查看代码。
???来自Arduino 1.8.9发行版的gcc 5.4.0,parameters = -O3 -std=c++11。
源代码:
#include <stdint.h>
uint32_t f_u32(uint32_t x) {
union y {
uint8_t p[4];
uint32_t w;
};
return y{ .p = {
y{ .w = x }.p[0],
y{ .w = x }.p[1],
y{ .w = x }.p[2],
y{ .w = x }.p[3]
} }.w;
}
uint64_t f_u64(uint64_t x) {
union y {
uint8_t p[8];
uint64_t w;
};
return y{ .p = {
y{ .w = x }.p[0],
y{ .w = x }.p[1],
y{ .w = x }.p[2],
y{ .w = x }.p[3],
y{ .w = x }.p[4],
y{ .w = x }.p[5],
y{ .w = x }.p[6],
y{ .w = x }.p[7]
} }.w;
}
Run Code Online (Sandbox Code Playgroud)
为该uint32_t版本生成的程序集:
f_u32(unsigned long):
ret
Run Code Online (Sandbox Code Playgroud)
为该uint64_t版本生成的程序集:
f_u64(unsigned long long):
push r28
push r29
in r28,__SP_L__
in r29,__SP_H__
subi r28,72
sbc r29,__zero_reg__
in __tmp_reg__,__SREG__
cli
out __SP_H__,r29
out __SREG__,__tmp_reg__
out __SP_L__,r28
subi r28,-72
sbci r29,-1
in __tmp_reg__,__SREG__
cli
out __SP_H__,r29
out __SREG__,__tmp_reg__
out __SP_L__,r28
pop r29
pop r28
ret
Run Code Online (Sandbox Code Playgroud)
我不确定这是否是一个好答案,但这是我能提供的最好的答案。该f_u64()函数的汇编程序在堆栈上分配72个字节,然后再次对其进行分配(因为这涉及到寄存器r28和r29,它们先保存后在末尾)。
如果您尝试在没有优化的情况下进行编译(我也跳过了该c++11标志,我认为它没有任何区别),那么您会看到该f_u64()函数从在堆栈上分配80个字节开始(类似于您在优化后看到的开始语句)代码,只有80个字节,而不是72个字节):
in r28,__SP_L__
in r29,__SP_H__
subi r28,80
sbc r29,__zero_reg__
in __tmp_reg__,__SREG__
cli
out __SP_H__,r29
out __SREG__,__tmp_reg__
out __SP_L__,r28
Run Code Online (Sandbox Code Playgroud)
这80个字节实际上全部被使用。首先x存储参数的值(8个字节),然后完成涉及其余72个字节的大量移动数据。
之后,类似于优化代码中的结束语句,将80个字节释放到堆栈中:
subi r28,-80
sbci r29,-1
in __tmp_reg__,__SREG__
cli
out __SP_H__,r29
out __SREG__,__tmp_reg__
out __SP_L__,r28
Run Code Online (Sandbox Code Playgroud)
我的猜测是,优化器得出的结论是可以节省用于存储参数的8个字节。因此,它仅需要72个字节。然后得出结论,可以节省所有数据移动。但是,它无法弄清楚这意味着可以保留堆栈上的72个字节。
因此,我最好的选择是,这是优化器中的一个限制或错误(无论您喜欢使用哪种名称)。在这种情况下,唯一的“解决方案”是尝试重新整理实际代码以找到解决方法,或者将其作为编译器上的错误提出。