符号表
存储的类型、可见性、和生命周期
符号的属性和符号表的entry
一个符号有哪些属性:
name:
class: storage class
volatile: 异步访问。符号不能进reg,每次都必须从mem读取值。
size:字节数
bitsize:bit数
boundary:字节对齐值
bitbdry:bit对齐值
type:对于预定义类型,用枚举量表示。对于构造类型,用type referent表示。
basetype:构造类型中单个元素的type。
machtype:机器类型。对于简单的预定义类型,例如char,机器类型就是byte。
对于构造的类型,就是其basetype的机器类型。
nelts:元素的个数
register:boolean,表示符号在reg中。
reg:寄存器名称。
basereg:基址寄存器,用来计算符号的地址。
disp:偏移量。 basereg + disp就是符号地址。
type属性的表示:
预定义类型,就用枚举值表示。
构造类型,使用tuple表示。
例如:
t1=<array, 2, [<0,5>, <1,10>], integer> t1表示一个两维数组,元素类型为整数。
t2=<record, 3, [<t2a, integer>, <t2b, <pointer, t2>>, <t2c, <array, 1, [<1, 3>], char>>]>
t2表示一个记录,有3个element。
局部符号表操作
将符号表的entry保存在一个数组中,然后使用hash表来访问。hash key构成一个key array。
这样可以快速的查询,删除、插入。
全局符号表
...
存储绑定和符号寄存器
1. 存储绑定:将变量名变换为地址,同时,将赋值语句变为load/operations/store,将函数调用变为指令序列。
2. 全局变量访问:MIPS架构,使用gp + offset来表示全局变量的地址。
所以 load r2, [gp+8],一条指令就可以访问全局变量。
—— 注意,这是静态链接下访问全局变量。
动态链接访问共享库中的全局变量,需要通过GOT。gp保存的是GOT的起始地址。
load r3, [gp + a_off] ; 访问GOT中a变量的entry,获得a的地址
load r2, [r3] ; 获得a的值
需要两条指令。
3. 栈变量访问:使用offset + fp/sp。
4. 将全局变量和栈变量放入寄存器中。有两种做法:
(1) 将所有标量放入(数量没有限制的)符号寄存器,然后使用全局寄存器分配算法来决定变量是放入实际的寄存器还是放在内存中。【参见16.3节】
(2) 将所有对象先放入home内存位置,然后尝试将它们放入实际寄存器,如果成功,就删除对home内存位置的操作。
MIR: a是全局变量,b和c[]是栈变量。
a <- a * 2
b <- a + c[1]
LIR: 符号寄存器形式
s0 <- s0 * 2 ; a放入s0
s1 <- [fp - 56] ; c[1]的地址是fp-56
s2 <- s0 + s1 ; b放入s2
LIR: home内存形式
r1 <- [gp + 8] ; a的地址为gp+8
r2 <- r1 * 2
[gp + 8] <- r2
r3 <- [gp + 8]
r4 <- [fp - 56]
r5 <- r3 + r4
[fp - 20] <- r5 ; b的地址为fp-20
在栈上分配局部变量,为了高效利用空间,可以按变量大小排序,然后对齐。具体细节略。
产生load和store
描述了一种local的register allocation。等到16章会详细论文global register allocation和local register allocation。