c - AVR C 编译器行为。内存管理

标签 c compiler-construction microcontroller avr ram

AVR C 编译器是否让程序记住函数开始将其数据(变量、数组)存储在索引寄存器之一的数据堆栈中的 SRAM 中的地址,以便通过公式获取局部变量的绝对地址:

absoluteAdr = functionDataStartAdr + localShiftOfVariable.

当由它的长度声明的变量或堆栈指针在所有变量长度的函数结束/开始增加时,它们是否增加数据堆栈点。

最佳答案

让我们看一下 avr-gcc,它可以免费获得,包括 its ABI :

Do AVR C compilers make program memorize the address in SRAM where function started to store its data (variables, arrays) in data stack in one of index registers in order to get absolute address of local variable by formula:

是,不是,这取决于:

静态存储

对于静态存储中的变量,即由定义的变量

unsigned char func (void)
{
    static unsigned char var;
    return ++var;
}

编译器生成一个像 var.123 这样大小合适的符号(本例中为 1 个字节)。然后链接器/定位器将分配地址。

func:
    lds  r24,var.1505
    subi r24,lo8(-(1))
    sts  var.1505,r24
    ret
    .local  var.1505
    .comm   var.1505,1,1

自动

如果可能,自动变量保存在寄存器中,否则编译器会在函数的框架中分配空间。甚至可能是变量被优化掉了,在这种情况下它们在程序中的任何地方都不存在:

int add (void)
{
    int a = 1;
    int b = 2;
    return a + b;
}

add:
    ldi r24,lo8(3)
    ldi r25,0
    ret

有 3 种类型的实体存储在函数的框架中,所有这些实体都可能存在或不存在,具体取决于程序:

  • 由函数序言保存(推送)并由尾声恢复(POP)的被调用方保存的寄存器。当局部变量被分配给被调用者保存的寄存器时,这是需要的。

  • 不能分配给寄存器的局部变量空间。当变量太大而无法保存在寄存器中,自动变量太多,或者变量的地址被获取(并且无法优化获取地址)时,就会发生这种情况。这是因为您不能获取寄存器1 的地址。

    void use_address (int*);
    
    void func (void)
    {
       int a;
       use_address (&a);
    }
    

    这些变量的空间在序言中分配并在尾声中释放。收缩包装未实现:

    func:
        push r28
        push r29
        rcall .
        in r28,__SP_L__
        in r29,__SP_H__
        /* prologue: function */
        /* frame size = 2 */
        /* stack size = 4 */
        movw r24,r28
        adiw r24,1
        rcall use_address
        pop __tmp_reg__
        pop __tmp_reg__
        pop r29
        pop r28
        ret
    

    在这个例子中,a 占用 2 个字节,由 rcall 分配。(它是为具有 16 位程序计数器的设备编译的)。然后编译器用堆栈指针的值初始化帧指针Y (R29:R28)。这是必需的,因为在 AVR 上,您不能通过 SP 访问内存;唯一涉及 SP 的内存操作是 PUSHPOP。然后,Y+1 变量的地址被传递到 R24 中。函数调用后,结语释放帧并恢复 R28 和 R29。

  • 必须在堆栈上传递的参数:

    void xfunc (int, ...);
    
    void call_xfunc (void)
    {
        xfunc (42);
    }
    

    这些参数被压入,被调用者从堆栈中取出它们。这些参数在调用期间被压入/弹出,但也可以通过 -maccumulate-args 进行累积。

    call_func:
        push __zero_reg__
        ldi r24,lo8(42)
        push r24
        rcall xfunc
        pop __tmp_reg__
        pop __tmp_reg__
        ret
    

    在这个例子中,参数必须在堆栈上传递,因为 ABI 规定可变参数函数的所有参数都必须在堆栈上传递,包括命名的参数。

有关框架布局和参数传递的精确描述,请参阅[框架布局和参数传递] ( https://gcc.gnu.org/wiki/avr-gcc#Frame_Layout )。

1 有些 AVR 实际上允许这样做,但您永远(就像从不)想要传递通用寄存器的地址!

关于c - AVR C 编译器行为。内存管理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48570281/

相关文章:

microcontroller - FAT16 目录

c++ - 是否有任何好的工具/框架来分析 C/C++ 应用程序的性能

c - 如何在 C 中使用数组和函数执行简单算术运算

c++ - 在 C++ 中包含目录时指定完整路径

microcontroller - PIC18F1220 尝试刷新几个引脚但无法

c - 过滤掉固定长度数组中先前可变长度数据的剩余部分

c - 从C中的字符串中提取url

c - C中静态函数的正确用法是什么?

python - 调用局部变量和调用对象的实例变量之间有性能差异吗?

C 编程 - 编写可自行编译的文本文件