« 上一篇下一篇 »

代码生成之指令选择

目标指令集本身的特性对指令选择难度有很大的影响。比如,指令集的统一性和完整性是两个很重要的因素。如果目标机没有以统一的方式支持每种数据类型,那么总体规则的每个例外都需要进行特别处理。比如,在某些机器上,浮点数运算使用单独的寄存器完成。
代码生成器必须把IR程序映射成为可以在目标机上运行的代码序列。完成这个映射的复杂性由如下的因素决定:

*IR的层次。

*指令集体系结构本身的特性。

*想要达到的生成代码的质量。

如果IR是高层次的,代码生成器就要使用代码模板把每个IR语句翻译成为机器指令序列。但是,这种逐个语句生成代码的方式通常会产生质量不佳的代码。这些代码需要进一步优化。如果IR中反映了相关计算机的某些低层次细节,那么代码生成器就可以使用这些信息来生成更 加高效的代码序列。

目标机指令集本身的特性对指令选择的难度有很大的影响。比如,指令集的统一性和完整性是两个很重要的因素。如果目标机没有以统一的方式支持每种数据类型,那么总体规则的每个例外都需要进行特别处理。比如,在某些机器上,浮点数运算使用单独的寄存器完成。

指令速度和机器的特有用法是另外一些重要因素。如果我们不考虑目标程序的效率,那么指令选择是很简单的。对于每一种三地址语句,我们可以生成一个代码骨架。此骨架定义了对这个构造生成什么样的目标代码。比如,每一个形如x = y + z的三地址语句(其中x、y和z都是静态分配的)可以被翻译成如下的代码序列:

LD RO,y // R0 = y (把y装载到寄存器RO)

ADD RO, RO, z // RO = RO + z (把 z 加到 RO)

ST x, RO // x = RO (把 RO 保存到 x)

这种策略常常会产生冗余的加载和存储运算。

生成代码的质量通常是由它的运行速度和大小来确定的。在大多数机器上,一个给定的IR程序可以用很多种不同的代码序列来实现。这些不同实现之间在代价上有着显著的差别。因此,对中 间代码的简单翻译虽然能产生正确的目标代码,但是这些代码却$能过于低效而让人不可接受。

比如,如果目标机有一个“加一”指令(INC),那么三地址i吾句a = a +1可以用一个指令INC a来实现。这个指令要比如下的代码序列更加高效:把a加载进一个寄存器,对寄存器加1,然后把结果保存回a。

LD RO,a //RO = a

ADD RO, RO, #1 // RO = RO + 1

ST a, RO// a = RO

要设计出良好的代码序列,我们就必须知道指令的代价。遗憾的是,我们经常难以得到精确的代价信息。对于一个给定的三地址构造,可能还需要有关该构造所在上下文的信息才能决定哪个是最好的机器代码序列。

我们将看到指令选择可以用树模式匹配过程来建模。在这个过程中,我们把IR和机器指令表示为树结构。然后,我们尝试着用一组对应于机器指令的子树覆盖一棵IR树。如果我们把每棵机器指令子树和一个代价值相关联,我们就可以用动态规划的方法来生成最优化的代码序列。

« 上一篇下一篇 »