首页> 中国专利> 一种基于代数系统的跨文件过程间优化方法

一种基于代数系统的跨文件过程间优化方法

摘要

本发明公开了一种基于代数系统的跨文件过程间优化方法,该方法包括以下步骤:针对目标机特征,选择出涉及栈操作、逻辑运算类指令,构建代数系统,并为这些指令与代数系统建立映射关系;从程序入口处开始遍历程序调用图PCG,判断存在边相连的节点是否分属不同的源文件,如果是,继续下一步操作,否则继续遍历PCG;从函数调用指令开始逆向沿着当前函数内的控制流图CFG开始遍历数据依赖图DDG,生成指令压栈操作的代数表达式,并进行表达式归约;分析后继节点函数的出栈操作,从中读出常量值,并依次传递,优化并计算,最终删除冗余指令片段。本发明有效地合并、释放了函数栈框架中可优化部分。除此之外,本发明在跨文件过程间优化、常量传播以及常量计算中也取得了较佳效果。

著录项

  • 公开/公告号CN103559069A

    专利类型发明专利

  • 公开/公告日2014-02-05

    原文格式PDF

  • 申请/专利权人 中国科学院声学研究所;

    申请/专利号CN201310579365.X

  • 发明设计人 朱浩;王东辉;洪缨;

    申请日2013-11-18

  • 分类号G06F9/45;

  • 代理机构北京亿腾知识产权代理事务所;

  • 代理人陈霁

  • 地址 100190 北京市海淀区北四环西路21号

  • 入库时间 2024-02-19 22:14:31

法律信息

  • 法律状态公告日

    法律状态信息

    法律状态

  • 2016-08-17

    授权

    授权

  • 2014-03-12

    实质审查的生效 IPC(主分类):G06F9/45 申请日:20131118

    实质审查的生效

  • 2014-02-05

    公开

    公开

说明书

技术领域

本发明涉及编译器技术,尤其涉及一种基于代数系统的跨文件过程间 优化方法。

背景技术

函数栈框架在传统编译框架中是一种通用协议,并交由用户在二进制接 口(Application Binary Interface,ABI)进行定义。函数栈框架在处理函数调 用时,它根据目标机的硬件资源约束,尽可能的将临时变量保存在临时寄存 器内,而溢出部分则在函数体前被执行前压入栈内,并通过存储访问指令进 行栈操作,待函数体被执行完后再逐一释放函数栈内的数据。

在这种模式下,由于目标机器支持的最大寄存器数有限,而局部临时变 量由于生命周期而分布于多个基本块内,频繁的入栈、出栈以及局部临时寄 存器复用将直接导致函数栈操作存在冗余。

现有技术主要是基于编译框架下的中间语言进行优化,它主要面临以下 几个主要问题:1、函数栈框架协议的制定与目标机器直接相关,修改时涉 及的代码量十分庞大,这将导致工作量急增;2、制定一种适合各种应用场 景的函数栈框架十分复杂,它牵涉到寄存器分配等多个阶段,而且中间语言 只是一种抽象语言,它并不能直接、有效的描述栈操作,因此,从中间语言 入手进行优化的设计复杂度很高。

发明内容

本发明的目的是为了解决上述现有技术存在的不足之处,提出一种基于 代数系统的跨文件过程间优化方法,它通过定义代数系统来对函数栈操作进 行针对性的优化,并取得了更好的效果。

为实现上述目的,本发明提供了一种基于代数系统的跨文件过程间优化 方法,该方法包括以下步骤:

针对目标机特征,选择出涉及栈操作、逻辑运算类等指令,构建代数系 统,并为这些指令与代数系统建立映射关系;

从程序入口处开始遍历函数调用图(Procedure Call Graph,PCG),判 断存在边相连的节点是否分属不同的源文件,如果是,继续下一步操作,否 则继续遍历PCG;

从函数调用指令开始逆向沿着当前函数内的控制流图(Control Flow  Graph,CFG)开始遍历数据依赖图(Data Dependence Graph,DDG),生 成指令压栈操作的代数表达式,并进行代数表达式归约;

分析后继节点函数的出栈操作从中读出常量值,并依次传递,优化并计 算,最终删除冗余指令片段。

本发明定义一套粗糙的代数系统,通过栈操作生命周期分析将栈相关指 令转义为代数表达式,并最终形成λ表达式,通过迭代归约λ表达式,有效 地合并、释放了函数栈框架中可优化部分。除此之外,本发明在跨文件过程 间优化、常量传播以及常量计算中也取得了较佳效果。

附图说明

图1为栈操作生命周期分析流程示意图;

图2为函数栈框架优化流程示意图;

图3为图2所示函数栈框架优化实例;

图4为本发明实施例提供的一种基于代数系统的跨文件过程间优化方法 流程图。

具体实施方式

下面通过附图和实施例,对本发明的技术方案做进一步的详细描述。

线性空间中的代数系统由非空集合S及定义于集合S上的代数符集,关 系集组成,代数系统中用于推理计算的代数表达式则是由常数、变量、有限 个相关代数操作(加法、减法、乘法等)构成。汇编函数内的执行路径可看作 是由多个汇编级基本块组成有序多元组,假设存在一条执行路径L=<x1,x2, x3,..,xn>,其中L[i]=xi{i=1,…,n为执行节点或沿路上的汇编级基本块},针 对L的特征可定义函数:

对于一个确定的输入L[i]而言,Ret(L[i])的值是可确定,那么L的执行 信息可被递归形式的跳转函数Jump(L[i])=(if Ret(L[i])=Ret1&&i≠n-1)then Successor(L[i])else if(i=n-1)then Last(L[i])else Jump(L[i-α])来表示,其中 α(α>0)为路径的回溯距离,Successor(L[i])=L[i+1],Last(L[i])=L[n-1]。

控制流图CFG中存在的各条执行路径以及这些路径之上的节点和回路, 可用Jump(L[i])函数作为它们的计算模型并提供一种适用于程序计算的表达 形式,而挖掘栈框架中可优化部分时,本发明实施例是通过分析栈指令生命 周期并利用栈指令替换L[i]而实现。Church在1936年提出了一种能把函数 当作变量处理的、计算的λ(Lambda)演算系统,它能有效的定义可计算函数 并提供通用的计算表达形式,一般被用于研究递归函数,因此,本发明实施 例将Jump(b[i])转换为如下的λ表达式,其中f=Jump(L[i]),r=Ret(L[i]), s=Successor(L[i]),I=Last(L[i])。

g=λfλi.(s(i),if r(i)is Ret1&&i≠n-1;I[i],if i=n-1;else f(i-α))

表1为代数符与栈相关指令之间的映射关系

代数符 操作 映射指令 表达式规范 # 压栈 stb;sth;stw等 des#src ! 出栈 ldb;ldh;ldw等 des!src

+ 加法 add;addia;addaw等 des=src1+src2 - 减法 sub等 des=src1–src2 @ 分析跳转 j等 @...@ * 乘法 mul等 des=src1*src 数据搬移 movidl;movidl等 des~src

表1定义了本发明实施例建立的代数符与栈相关指令之间的映射关 系,以下为每个代数符对应表达式规范的实例。

压栈指令,des和src分别为被压栈寄存器以及栈地址,以指令pr0sta a12, -8,a13为例,它将a13的值压入栈地址a12–8,表达式记为a13#(a12–8)。

出栈指令,des和src分别为被出栈寄存器以及栈地址,以指令pr0ldw d1, a12,-2为例,它从栈地址a12–8中读出四字节数据存入d1中,对应的表达 式为d1!(a12-8)。

逻辑运算类指令,包括加、减、乘操作,des和src分别为目标和源寄存 器以指令pr0addia,(0-(4))为例,它对应的表达式为a12=a12-4。

分支指令,在函数内主要是检测函数内部循环情况,两个@之间代表的 是循环区间α,它包含了一系列的逻辑运算,本发明实施例在面向环路的函 数栈框架优化中只优化α内的片段,而舍弃优化存在循环的路径,@可认为 是λ表达式中的if r(i)。

数据搬移指令,des和src分别为目标寄存器和需存入的值,以指令 movidh d3,0xfffffffa为例,它对应的表达式为d3=0xfffffffa。

以下针对函数栈操作生命周期进行分析:

在进行函数栈框架相关优化前,本发明实施例对函数内栈相关操作的生 命周期进行了分析。编译器在进行栈框架设计时,除了指令模板中指定使用 的寄存器之外,函数中全的局部临时变量都会按序分配一个虚拟寄存器,当 编译进行到实质的寄存器分配阶段时,再利用图着色算法进行物理寄存器分 配,而虚拟寄存器中序号超过物理寄存器可用部分的将被保存在栈中。栈生 命周期的分析正是基于这些机制实现的。

本发明实施例对栈相关操作进行的生命周期分析流程如图1所示,它首 先根据栈寄存器a12(编译器一般会指定专用的栈寄存器,a12只是一个例 子)遍历函数体内的控制流图CFG以及数据依赖图DDG,然后,获取全部 可能与栈操作相关的指令集合S,最后,通过分析指令间的关系静态分析栈 操作的生命周期从S中删除冗余部分。值得注意的是CFG中一个节点可能 存在多条路径上,本发明实施例在静态分析过程中只要发现一条路径能使当 前栈操作指令成立,它的生命周期就被认为在基本路径Pathi下被认定为有 效。

结合下述汇编代码,本发明实施例进行栈操作生命周期的详细步骤如 下:

TestFunc:

01pr0sta a12,-2,a13.

02pr0sta a12,-1,d1.

03pr0addia a12,(0-(4)).

04pr0addaw a13,a12,a0.

05pr0stw a13,0,d2.

06pr0addaw a3,a13,a0.

07pr0sta a3,-3,a7.

08pr0movda a3,d15.

09pr0stw a3,-3,d15.

顺序遍历目标函数内的全部指令,从中筛选出压栈、出栈以及写栈寄存 器a12的指令,获取栈操作相关指令集合StackInsn{1,2,3},以及栈相关寄存 器集合StackReg{a12};

依次分析指令集合{1,2,3}中各元素所在的DDG,分析数据依赖关系从 中筛选出与它们存在数据依赖关系的指令,可以看到指令4与指令3间存在 数据依赖关系S3δfS4:a12,因此,StackInsn被扩充为{1,2,3,4}。然而,指令4 中用于将地址寄存器a13的值被修改为a12+a0,而临时地址寄存器a0的值 被编译器定义为0,因此a13被添加入StackReg;

继续分析指令4所在DDG并找出与它在a13上存在δf依赖关系的指令, 可以看到指令5是存储访问类指令且以a13作为目标地址,因此指令5为入 栈指令,同理可知指令6,7,8,9同样可能是栈相关指令,StackInsn被扩 充为{1,2,3,4,5,6,7,8,9},而StackReg被扩充为{a12,a13,a3};

步骤3结束后全部可能的栈操作相关的指令均被找出,接下来本发明实 施例开始追溯CFG以及DDG来对StackInsn以及StackReg进行生命周期判 断;

指令8被添加入StackInsn中是因为在a3在之前的操作中并判断为栈地 址,但该指令的操作是将数据寄存器d15的值赋给a3,然而数据寄存器d15 中的值是否与栈地址有关并不确定;

分析指令8所在DDG中与其在d15上存在数据依赖关系的指令,如果 不存在则沿着CFG查找TestFunc内指令8所在节点的前驱节点内是否存在, 如果仍然不存在则沿着PCG查找TestFunc函数的前驱函数内是否存在,如 果可以找到判断d15中是否存储了栈地址;

最终判断指令8中的地址寄存器a3的生命周期已结束,因此指令9同 样也不再与栈操作相关,因此StackInsn为指令1~7。

以下针对函数栈框架优化进行介绍:

本节的研究目标是根据栈操作生命周期的分析结果,挖掘出其中尚未被 充分优化的部分,图2为优化流程图,具体操作将结合图3中的实例进行描 述。

1、代码移动

栈操作相关的指令主要分布在函数的入口和出口汇编级基本块中,其余 部分则根据周期的不同离散的分布于多个汇编级基本块内,这就导致一条栈 操作指令在不同的路径下有可能存在多种λ表达式,这样并不适合优化挖掘, 因此需将它们尽可能的移动到能获得唯一λ表达式的基本块内。

2、λ表达式映射

代码移动完成后,根据表1的映射关系将能将指令映射为唯一的λ表达 式,图3(b)为指令直接映射后的结果,其中小写和大写字母分别代表原始寄 存器和修改后的值。

3、分析函数的CFG以及存在λ表达式的指令所在DDG,逐层迭代并 归约表达式。

如指令3与4存在S3δfS4:a12的数据依赖关系,那么表达式迭代后指令 4的表达式变为A13=a12–4+a0,而a0的值恒为0,因此指令4表达式 最终的归约结果为A13=a12–4。依次类推,获得图3(c)中归约后各指令的 λ表达式。

4、压栈、出栈匹配

编译器中是从压栈开始使用一个临时寄存器,而将出栈视作释放这个临 时寄存器,对临时寄存器进行压栈、出栈匹配是为了观察函数执行时它的行 为。图3(c)中λ表达式1和21分别为a13的压栈和出栈操作,表达式4是用 于跟踪栈指针,但通过分析其余表达式可知均不存在a13变量,因此表达式 4成为冗余可被删除,这就导致表达式1和21之间a13不再存在活动记录, 因此表达式1和21同样可被删除。依次类推,继续对d1与d2的相关操作 进行判断。

5、指令映射

这个过程可认为是表1的逆集,它直接将剩余的最简λ表达式映射为指 令,形成3(d)中优化后的代码片段,可以看到静态代码行数从22条优化至 14条。

以下针对跨文件过程间优化进行介绍:

过程间优化IPO(interprocedural optimization,IPO)是基于模块间的调 用关系实现的一种优化手段,它通常需要构造程序调用图PCG(Procedure  Call Graph,PCG)来辅助描述函数间的调用关系并进行过程间分析。过程间 分析并不是一项简单的工作,它将影响编译速度并加大了编译器的设计复杂 度,正因如此,绝大部份的开源或商用编译器出于对效率的考虑,基本只实 现了文件内的过程间优化。下述实例中定义了三个用C语言编写,相互之间 存在调用关系,但存储于不同文件内的三个函数test0~test2,表2中给出了 它们在GCC3.0.4编译器中的编译结果,但如果将这三个函数合并为同一个 源文件,可以看到GCC3.0.4编译器在利用参数传递优化、函数内联后将这 三个函数的汇编代码压缩为test2函数,从指令片段上来看这三个函数间将 不再存在调用关系且静态指令数获得了一定程度的降低,这一方面说明了 GCC3.0.4并不支持跨文件的过程间优化,另一方面也说明了GCC3.0.4的编 译结果还存在可优化的可能。

针对上述现状,本节研究的过程间优化是围绕跨文件函数间的调用关系 而展开。在此之前,构建了全局函数调用图PCG,它可有效的描述整个项目 工程内全部函数之间的调用关系。本节的研究目标旨在挖掘出函数间常量传 播过程中带来的冗余压栈、入栈操作以及可直接计算的部分。PCG中每个函 数节点的前驱和后继节点并不唯一,对任意一个函数的优化必须保证不能影 响其它前驱节点函数执行时的逻辑语义,因此进行跨文件过程间优化之前, 本发明定义了下述规则来维护程序的执行逻辑不变。

定义:PCG中存在前驱节点i以及它的一个后继节点j,i调用j时的参 数列表Params为{P1,P2,…,Pn}。

规则(一):当i是j的唯一前驱时,如果Params中存在常量Pk,则 将Pk传递入j中利用代数表达式进行计算,删除i中对Params中各参数的 压栈以及j中对的出栈操作并将j的代码移动至i中进行函数调用的部分;

规则(二):当规则一成立时,如果i中调用j的指令insn的推断寄存 器不为pr0,那么需将insn修改为分支跳转指令来维护控制流,否则直接移 动;

规则(三):当i不是j的唯一前驱时,如果Params中存在常量Pk, 则重新一个段函数j-rename,i对j的调用被修改为j-rename,i是它的唯一 前驱,其它操作与规则一中一致但不进行代码移动;

规则(四):调度PCG过程,本发明实施例不处理只由一个节点组成 的自闭环或递归函数,但可处理由多个函数节点组成的环路如 Path:A→B→…→B→A,静态分析中Path还存在表述形式: B→A→…→A→B,因此,需要沿着目标程序的入口函数遍历PCG来确定 Path的入口节点,当Path的入口节点无法确定时,本发明实施例只调度Path 中不导致递归的部分。

基于上述定义的规则,跨文件过程间优化的流程如图4所示。结合表中 的例子可以看到,首先,利用广度优先算法从程序的入口处开始遍历PCG, 判断存在边相连的节点是否分属不同的源文件,如果是,继续下一步操作, 否则继续遍历PCG;然后,从函数调用指令开始逆向沿着当前函数内的CFG 开始遍历DDG,并生成这些压栈操作的代数表达式,如test2函数的指令7 等价于D2#(A12+4);其次,将指令的表达式逐一带入进行归约,那么指 令7等价于11#(a12-12)为常数传播;分析后继节点函数的出栈操作,从 (a12-12)位臵读取出的数据即为11,并依次传递,优化并计算,最终删除test1 函数的指令片段;同理传递入test1继续迭代计算常量部分,以test1中指令 12~15构成的循环片段为例,本发明将生成如下表达式:@if(d2<22),d1= d1+d2,d2=d2+1@,计算后可将循环体等价为:movid d1,671与movid d2, 22这两条指令;再其次,判断这些函数之间的是否存在唯一调用关系来选择 选择代码复制或代码移动;结合上一小节的工作,test1的代码片段最后被优 化为如下指令片段。

test2:

01pr0movid d1,671.

02pr0ret.

本发明实施例通过定义一套粗糙代数系统,通过栈操作生命周期分析将 栈相关指令转义为代数表达式,并最终形成λ表达式,通过迭代归约λ表达 式,该方法有效地合并、释放了函数栈框架中可优化部分。除此之外,本发 明实施例在跨文件过程间优化、常量传播以及常量计算中也取得了较佳效 果。

显而易见,在不偏离本发明的真实精神和范围的前提下,在此描述的 本发明可以有许多变化。因此,所有对于本领域技术人员来说显而易见 的改变,都应包括在本权利要求书所涵盖的范围之内。本发明所要求保 护的范围仅由所述的权利要求书进行限定。

去获取专利,查看全文>

相似文献

  • 专利
  • 中文文献
  • 外文文献
获取专利

客服邮箱:kefu@zhangqiaokeyan.com

京公网安备:11010802029741号 ICP备案号:京ICP备15016152号-6 六维联合信息科技 (北京) 有限公司©版权所有
  • 客服微信

  • 服务号