DIAB编译器生成的PPC汇编代码栈外存LR为何不崩溃?
为什么DIAB编译器生成的PPC汇编能在栈帧外保存LR且正常运行
要理解这个现象,得从PowerPC的栈行为和ABI规范入手,一步步拆解指令逻辑:
1. 先理清栈指针的变化
PPC的栈是向下生长的,r1作为栈指针(SP)始终指向栈顶(当前栈帧的最低地址)。先看序言的核心指令:
stwu r1,-8(r1) // Allocate 8 bytes on stack and save r1 on top
这条指令做了两件事:
- 把当前栈指针
r1的旧值(记为r1_old)存入地址r1_old - 8的位置(这是新栈帧的栈底,用来保存回溯链) - 把栈指针
r1更新为r1_old - 8(记为r1_new,也就是新的栈顶)
此时栈的布局(地址从高到低):
[r1_old + 4] → 即将用来存LR的位置 [r1_old] → 调用者的栈顶 ... [r1_old - 8] → 存着r1_old(当前函数的回溯指针) [r1_new] → 栈顶(r1_old -8)
2. 为什么存LR到12(r1)是安全的
接下来的指令是把LR存入12(r1):
stw r0,12(r1) // save the LR 12 bytes below the stack - how is that ??
代入r1 = r1_new = r1_old -8,计算目标地址:12(r1_new) = (r1_old -8) +12 = r1_old +4
这个地址属于调用者的栈帧区域,而非当前函数刚分配的8字节栈帧。
它能安全使用的核心原因是PowerPC ABI规范的约定:
- 调用者在调用任何函数前,必须保证自己栈帧的
SP +4位置(也就是r1_old +4)是可用的临时空间,被调用者可以自由使用这个位置来保存LR或其他临时数据 - 调用者不会在这个位置存储需要保留的关键数据,因此当前函数写入/读取这个位置不会破坏调用者的栈帧数据
3. 尾声指令的逻辑验证
尾声指令从12(r1)恢复LR,再调整栈指针:
lwz r0,12(r1) // recover the LR register into r0 mtspr 8,r0 // recover the LR addi r1,r1,8 // adjust the stack blr // return to the caller
addi r1,r1,8把栈指针恢复为r1_old,回到调用者的栈顶位置- 整个过程中,调用者栈帧的
r1_old +4只是被临时借用,没有被破坏,因此函数返回后调用者能正常继续执行
编译器的优化意图
DIAB编译器这么做是为了节省栈空间:如果把LR存在当前函数自己的栈帧里,需要至少额外4字节空间,栈帧大小就得改成12字节;而借用调用者栈帧的预留空间,只需要分配8字节栈帧即可,实现了栈空间的优化。
内容的提问来源于stack exchange,提问作者Martin Roth
相关产品推荐
相关产品推荐

