avr-gcc :(貌似)简单功能中不需要的序言/结尾

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)

nie*_*sen 7

我不确定这是否是一个好答案,但这是我能提供的最好的答案。该f_u64()函数的汇编程序在堆栈上分配72个字节,然后再次对其进行分配(因为这涉及到寄存器r28r29,它们先保存后在末尾)。

如果您尝试在没有优化的情况下进行编译(我也跳过了该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个字节。

因此,我最好的选择是,这是优化器中的一个限制或错误(无论您喜欢使用哪种名称)。在这种情况下,唯一的“解决方案”是尝试重新整理实际代码以找到解决方法,或者将其作为编译器上的错误提出。