2015-05-28 69 views

回答

6

Hardware registers是昂贵的(无论是在芯片面积和解决它们所需的指令位的数量方面),因此通常数量很少。 Spilling发生在给定程序点的live variables(或更准确地说,活动范围的数量)超过可用寄存器的数量时。

考虑下面的示例程序在虚拟机器中执行,该虚拟机器有两个硬件寄存器。假设编译器除寄存器分配外不执行优化。

a := 1 ; liveout: {a} 
b := 2 ; liveout: {a,b} 
c := 3 ; liveout: {a,b,c} 
d := a + b + c 

由于abd定义的使用,他们的生活范围跨越的c定义。但是由于机器只有两个寄存器,所以当定义d时,不可能将abc保存在寄存器中。至少其中一个必须溢出。

在最简单的溢出形式中,溢出变量的所有定义都被存储替换为stack插槽,并且所有用途都被替换为负载。一些编译器也可以选择执行寄存器到寄存器的溢出,这意味着该值被存储到另一个类的寄存器并从其中载入。例如,在x86-64上,编译器可能会将一个通用寄存器(如rax)的值溢出到SIMD寄存器(如xmm0)中。这有利于减少内存流量。

作为溢出的替代方法,编译器可以改为执行活动范围分割。这涉及到将活动范围分成较小的部分 - 只在分割点处插入加载和存储 - 以使着色不可干扰的干扰图形成为可能。正如你可以想象的那样,选择哪个变量溢出对结果代码的性能有着重要的影响。任意溢出在紧密环路中使用或定义的变量可能会造成灾难性后果。因此,一个好的编译器可能会采用某种形式的启发式方法来估算溢出每个变量的成本,然后再进行选择。

+0

令人敬畏的解释,所以编译器也使用启发式来估计寄存器的溢出?令人惊讶的是,你能指出一些有关在那里使用的启发式算法的资源。谢谢,并upvoted。 –

+1

我一直无法找到任何可访问的资源。 [Cooper&Torczon](http://www.amazon.com/Engineering-Compiler-Second-Edition-Cooper/dp/012088478X)表明循环嵌套可以用作估计。基本上,我们假设每个循环执行N次(我们可能选择N = 10)。因此,循环中引用的变量的成本是存储和加载所需的内存操作次数的N倍。类似地,在双重嵌套循环中引用的变量将具有N^2次操作次数的成本。 –