理论教育 编译器设计之路:实现定值点、引用点分析

编译器设计之路:实现定值点、引用点分析

时间:2023-11-04 理论教育 版权反馈
【摘要】:前面已经介绍了定值点、引用点相关的一些基本概念及数据结构。一般来说,不可到达过程删除必须是基于过程间分析进行讨论的。第37~62行:主要处理过程调用IR对全局变量定值、引用所产生的副作用。第37行主要就是完成这一判定。第65~88行:处理内嵌汇编的定值点、引用点。第67~86行:遍历内嵌汇编的参数信息,根据参数信息,登记定值点、引用点。至此,笔者已经详细剖析了定值点、引用点分析的算法实现。

编译器设计之路:实现定值点、引用点分析

前面已经介绍了定值点、引用点相关的一些基本概念及数据结构。而程序7-2的目标就是获取相关过程的定值集、引用集、基本块定值位向量、基本块引用位向量等信息。

程序7-2 DataFlowAnalysis.cpp

第5行:获取当前过程的基本块信息。

第6~11行:遍历IR序列,判断IR操作数是否为GETADDR,如果为真,则表示Opl所指的变量被取地址。这种情况下,则将该变量的位序号置入RefVar中。

第12~20行:初始化各基本块的定值位向量、引用位向量。

第21~109行:遍历过程的基本块集。

第23、24行:初始化Def,Use位向量。

第25~106行:遍历基本块内IR列表。

第29~34行:获取处理方案。

第35、36行:如果操作符为CALL或GETPROCADDR,则设置过程引用标志。这是一项非常重要的工作。编译器借助于过程引用标志可以轻松地识别不可到达的过程。所谓“不可到达过程”就是指那些永远无法被调用的过程。如果一个过程既没有被调用,也没有被取地址,那么,这个过程必定是不可到达过程。对于编译器而言,通常不需要为不可到达过程生成目标代码。当然,在多文件编译时,由于外部过程的原因,试图确定一个不可到达过程是相当复杂的。一般来说,不可到达过程删除必须是基于过程间分析进行讨论的。详细的原因,笔者先前己作相关说明。

第37~62行:主要处理过程调用IR对全局变量定值、引用所产生的副作用。实际上,程序的含义非常明确,即假设被调用过程将对所有的全局变量定值、引用都产生副作用。这种处理并不精确,却是最安全的策略。当然,读者需要关注几种特殊的情况:

(1)调用外部过程是不影响全局变量的定值、引用的。这里的外部过程并不是指那些来自其他源文件的过程,而是指那些来自二进制库文件的过程。由于这种外部过程不可能给本地全局变量带来任何副作用,因此,这种过程调用不是设计者所关注的。第37行主要就是完成这一判定。

(2)调用过程并不会给临时全局变量带来副作用。由于临时全局变量是由编译器自动生成的,用户程序是无法访问的,因此,过程的调用并不会影响临时全局变量。第48行主要就是完成这一判定。

第54行:调用RecordUse函数,登记引用点信息。稍后将详细分析RecordUse函数。

第59行:调用RecordDef函数,登记定值点信息。稍后将详细分析RecordDef函数。

第65~88行:处理内嵌汇编的定值点、引用点。

第67~86行:遍历内嵌汇编的参数信息,根据参数信息,登记定值点、引用点。当flag为RW时,表示参数所示变量在内嵌汇编中被定值且引用。当flag为W时,表示参数所示变量在内嵌汇编中被定值。当flag为R时,表示参数所示变量在内嵌汇编中被引用。

第89~92行:Op2所示的变量被引用,登记变量引用点信息。

第93~99行:Opl所示的变量被引用,登记变量引用点信息。有一种特殊的情况不能忽视,那就是变参传递。如果IR是变参传递指令,该IR也将是Opl所示变量的定值点。

第96~98行:主要用于判断当前IR是否为变参传递,为真则登记变量定值集。(www.daowen.com)

第100~104行:Rslt所示的变量被定值,登记变量定值点信息。当前IR为传参指令时跃过即可。

第107、108行:将Def、Use位向量复制到基本块的Def、Use属性中,以便后续优化算法使用。

第110~113行:将每个变量的定值集按从小到大的顺序排序,便于后续应用。

第114~118行:将每个变量的引用集按从小到大的顺序排序,便于后续应用。

最后,再来看看RecordDef, RecordUse函数的实现。

程序7-3 DataFlowAnalysis.cpp

第4行:判断Op是否为间接寻址操作数。

第5~8行:如果操作数不是间接寻址,则主要完成如下几项工作:

(1)设置Def位向量。

(2)调用VarDefUse设置变量定值集。实际上,VarDefUse函数是根据引用的参数而定的,并不区分设置的是VarAllUse还是VarDef。

第10~19行:如果操作数是间接寻址,则主要完成如下几项工作:

(1)由于操作数是间接寻址,所以操作数本身只是引用,而间接寻址的目标变量才是可能被定值。因此,调用VarDefUse设置变量引用集。

(2)设置Use位向量。注意,如果在一个基本块内,某一个变量引用之前存在对该变量的定值,那么,这样的引用并不是编译器所关心的。

(3)根据RefVar集合,调用VarDefUse设置变量定值集。RefVar集合中的成员都是被指针引用的变量。根据先前讨论的安全策略,这里假设间接寻址会对RefVar中所有的变量产生副作用。

程序7-4 DataFlowAnalysis.cpp

第4行:调用VarDefUse设置变量引用集。

第5~8行:设置Use位向量。

第9~17行:如果Op是间接寻址操作数,则根据RefVar集合,调用VarDefUse设置变量定值集。RefVar集合中的成员都是被指针引用的变量。根据先前讨论的安全策略,这里假设间接寻址会对RefVar中所有的变量产生副作用。

至此,笔者已经详细剖析了定值点、引用点分析的算法实现。定值点、引用点是后续数据流分析、优化算法的实现基础。囿于篇幅,笔者无法对该算法作更深入的阐述。另外,Neo Pascal编译器也没有对过程间数据流作深入分析,因此无法得到更准确的结果。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈