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

时间:2018-02-01 19:24:29

标签: c compiler-construction microcontroller avr ram

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

absoluteAdr = functionDataStartAdr + localShiftOfVariable.

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

2 个答案:

答案 0 :(得分:2)

我们来看看avr-gcc,它可以免费获得,包括its ABI

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

是的,不,这取决于:

静态存储

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

定义的变量
unsigned char func (void)
{
    static unsigned char var;
    return ++var;
}

编译器会生成一个具有适当大小(在这种情况下为1个字节)的符号,如var.123。然后,链接器/定位器将分配地址。

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种类型的实体,根据程序的不同,所有这些实体可能存在或不存在:

  • 被调用方保存的寄存器,由函数序言进行保存(按PUSH),由结语进行恢复(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占用rcall .分配的2个字节(它是为具有16位程序计数器的设备编译的)。然后,编译器使用堆栈指针的值初始化帧指针Y(R29:R28)。这是必需的,因为在AVR上,您无法通过SP访问内存;仅有涉及SP的存储操作是PUSHPOP。然后,该变量的地址Y+1R24中传递。调用该函数后,结尾部分将释放帧并还原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表示必须在堆栈上传递varargs函数的所有参数,包括命名的参数。

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

1 某些AVR实际上允许这样做,但是您永远也不想像在NEVER中那样传递通用寄存器的地址!

答案 1 :(得分:0)

编译器不管理RAM,编译器在编译时计算bss,data,text,rodata等每个数据部分所需的大小,并为每个翻译单元生成可重定位的目标文件

链接器紧随其后并生成一个目标文件,并将可重定位地址分配给根据链接器配置文件LCF映射的绝对地址。

在运行时,该机制取决于体系结构本身。通常,每个函数调用在堆栈中都有一个框架,其中定义了参数,返回地址和局部变量。堆栈随着变量的创建而扩展,并且对于低成本的AVR微控制器,没有关于堆栈增加或堆栈与另一个内存部分(通常是堆)之间的重叠的内存管理保护。即使有操作系统在不使用内存管理单元的情况下管理任务的保护以超过其分配的堆栈,操作系统也能做的就是用非法的内存访问原因声明 RESET