没有合适的资源?快使用搜索试试~ 我知道了~
编译器作为验证和评估工具:使用IntelR Itanium处理器为例,探讨编译工具中的验证和评估技术,以发现和消除软件错误,提高...
编译器作为验证和评估工具Gerolf F. 霍赫纳英特尔®英特尔®微处理器实验室,加利福尼亚邮箱:gerolf.f.ho juehner@intel.comDaniel M. Lavery英特尔®英特尔®微处理器实验室,加利福尼亚mailto:daniel.m.lavery@intel.comDavid C. Sehr英特尔®英特尔®微处理器实验室,加利福尼亚mailto:david.c.sehr@intel.com摘要就像处理器以不同的频率无故障地执行一样,编译器应该以一种新的优化水平来生成正确的代码。由于其新功能(如寄存器堆栈引擎以及控制和数据推测),IntelRItaniumPRONEsssorfamily本文描述了编译工具中可以采用的验证和评估技术,这些技术本文以Itanium为例,解释了为什么寄存器堆栈引擎(RSE)、大型寄存器文件或控制和数据推测可能会在编写或编译不佳的软件中暴露错误。然后,它演示了验证和评估技术,以发现或暴露这些错误。评估团队可以使用它们来发现,消除和评估软件错误。编译器团队可以使用它们来使编译器更加稳定和健壮。性能分析团队可以使用它们来发现应用程序中的性能机会。我们展示了我们的验证和评估技术的代码示例,并提供运行时数据,以表明我们的一些方法的成本2003年由ElsevierScienceB. V. 操作访问和C CB Y-NC-N D链接。G. HOFLEHNER,D. LAVERY,D. 塞赫尔21介绍对于架构的新实现,通常必须重新编译应用程序以获得最佳性能。独立软件供应商(ISV)更有可能重新编译应用程序,如果它是成本有效的。 如果系统、编译器和源代码库是可靠的,并且重新编译一致地提供可测量的性能增益,那么它是成本效益的。 本文描述了在通常的软件应用程序测试周期中评估源代码和验证编译器的有效技术。对于编译器,我们还提出了更积极的自验证方法。我们的方法可以减少软件开发周期中的调试开销,从而提高重编译的成本、效率和实用性安腾电池具有寄存器存储器引擎等功能的架构,控制和数据推测为移植的软件应用和系统工具(如编译器)提供了新的挑战。任何移植的遗留软件都可能包含可以在新架构上暴露的错误本文介绍了编译器验证和评估技术如何面对新的体系结构功能和移植软件可以提供的挑战。我们给出了潜在问题的概述,并描述了掌握它们的编译器技术自我验证技术是最有效的,如果他们可以使用。否则,编译器可以提供用户选项,并为移植,评估,编译器和性能分析团队提供验证和本文中讨论的一些技术可以在其他源代码评估工具(例如Unix系统上的lint)或二进制或汇编重写工具中使用。在本文中,评测是测试源代码是否正确的过程。验证是编译器(或一般工具)正确的过程。1.1贡献我们的论文的贡献是:• 我们提出了一个动态的方法来检测参数不匹配的遗留代码,通过利用寄存器堆栈(实例的多分配算法)• 我们描述了一种编译器算法,它可以在编译时发现变量的丢失和冗余的volatile声明• 我们描述了一种编译器自验证技术,以防止生成的代码发生NaT(=NotA Thing)消费错误[10]。• 我们将展示如何对恢复代码进行压力测试• 我们评估了多分配和恢复代码压力测试技术的成本查找丢失和冗余volatile声明的算法适用于任何架构。自我验证方案可以外推到一个新的软件开发策略。其余的方法是具体的。G. HOFLEHNER,D. LAVERY,D. 塞赫尔3Itanium体系结构,但可以在汇编和二进制重写工具以及编译器中使用。1.2概述本文的其余部分结构如下:第2节简要概述了寄存器堆栈引擎(RSE)和对Itanium的推测第3节描述了两个常见的软件故障,以及编译器如何检测它们。第4节演示了编译器可以使用自检或自动化技术来捕获由未初始化的寄存器或自动变量触发的NaT(=Not A Thing)消费错误[9]。第5节给出了推测和恢复代码的示例,并展示了编译器如何帮助评估推测代码和生成的恢复代码的正确性。第6节提出结论。1.3相关工作本文适合软件工程和评估领域[8][12]。我们认为我们的方法是独特的,因为它将自测试策略集成到编译器中,为评估和移植团队提供编译器用户界面,并提供特定的评估和验证算法,包括对其成本的测量。2寄存器栈引擎(RSE)和投机Itanium架构有128个架构整数寄存器r 0-r127。上面的96个寄存器r32-r127是堆叠的。每个过程可以有自己的可变大小寄存器堆栈框架,最多96个寄存器。过程中的堆栈寄存器称为架构寄存器。硬件将它们映射到依赖于微架构的物理寄存器数量例如,过程中的第一个传入参数寄存器被称为r32。但这可以是从r32到微架构中实现的堆栈寄存器数量的任何物理寄存器请注意,这篇文章每使用相同的命名为架构和物理寄存器。使用alloc指令[9],代码生成器显式指定其寄存器堆栈帧(图1):传入参数的数量(i),传出参数的数量(o),本地(在过程中)需要的寄存器(l)以及软件流水线(swp)循环中使用的旋转寄存器(r)的数量。每个过程的堆栈寄存器总数为i+l+o =96.调用者和被调用者的参数寄存器重叠(图1)。寄存器堆栈帧类似于本地存储器堆栈帧,但由寄存器堆栈引擎(RSE)管理,这是一个处理器状态机[9]。安腾电池 架构支持控制和数据处理。这使编译器能够移动加载和依赖于它们的指令,通过分支和潜在的别名存储所施加的障碍。为G. HOFLEHNER,D. LAVERY,D. 塞赫尔4(一)(二)(三)(四)(五):ld8.a添加……V4=[V1]V5=V4,[V10]=V11V4,记录rec:ld8 V4=[V1]加上V5=V4,V6第二条alloctarget_reg>=ar.pfs,i,l,o,rfoo:alloc2,4,2,0bar:alloc2,1,0,0foo()的寄存器堆栈bar()的寄存器堆栈物理寄存器文件Fig. 1. alloc指令和寄存器堆栈为了控制推测,Itanium提供推测加载(ld.s)和验证推测检查(chk.s)指令(打破分支屏障)。失败的ld.s(例如,由于页面错误)导致在推测加载的目标寄存器中设置延迟异常令牌(NaT)。对于整数寄存器,NaT(= Nota Thing)被编码为寄存器的额外位。chk.s指令被插入到原始的非推测加载指令的位置。如果为chk.s的操作数寄存器设置了NaT位,则执行分支到恢复代码,恢复代码重新执行推测加载的非推测实例和所有依赖指令,然后在chk.s之后分支回到束[10][4]。对于数据推测,Itanium为数据推测提供了一个高级加载(ld. a)和两个高级加载检查(chk. a,ld. c)指令这使代码生成器能够跨潜在的别名存储调度加载(打破存储屏障)。在执行时,高级加载在高级加载地址表(ALAT)中记录有关其物理目标寄存器,内存地址和数据大小的信息 如果后续存储重叠,则硬件使对应ALAT条目无效以指示冲突。与控制推测一样,恢复代码重新执行非推测代码,并分支到高级加载检查之后的点,并恢复常规程序执行(示例1)。示例1:使用恢复代码的数据推测3源代码评估技术我们描述了遗留代码中可能导致Itanium端口运行时失败的两个问题:形式和实际参数不匹配以及缺少volatile声明。编译器可以在计算模式下检测生成的代码,以导致运行时失败并捕获参数public intfindDuplicate(int a,int b){bits(c);R32R33R34R35R36R37R38R39R32R33R34R32R33R34R35R36R37R38R39R40R41..G. HOFLEHNER,D. LAVERY,D. 塞赫尔5源码:foo(){int a;…}public intfindDuplicate(int a,int b){*a=b;}pseudo assembly 1.proc bar:(1)int n =r33;;(2) 添加r33 =(3) alloc伪汇编2. proc bar:(1)st[r32]=r33(2)alloc r34= 2,1,伪汇编3.(1)alloc r34= 2,1,.(2)st8[r32]=r33不匹配. 在3.3节中,我们提出了一个在编译时检测volatile声明3.1形式参数和实际参数不匹配当调用者中的实际参数与被调用者中的形式参数不匹配,并且实际参数比形式参数少时在示例2中,函数foo()调用bar(int *a,int b),只有一个参数bar(a)。由于bar()有两个参数,它可以假设foo()在其寄存器堆栈帧中分配了参数寄存器。示例2:参数不匹配和3个汇编代码场景现在,如果bar在为b分配寄存器堆栈,有两个可能的运行时场景。在示例2的伪组件1中,结果将是指令处的寄存器堆栈错误。(2) 当bar()尝试写入r33时,因为调用者foo()没有在寄存器堆栈上分配参数寄存器在示例2的伪程序集2和伪程序集3中,如果r33的NaT位被设置,则结果可能是NaT消耗这可能会再次发生,因为调用者foo()没有分配和定义相应的参数寄存器。编译器可以通过阻止跨分配指令调度定义参数寄存器的指令来避免在伪程序集1中生成代码。但是,在其他情况下,编译器无能为力。在伪程序集2或3的情况下,有三种替代方案:(i) 如果bar()从RSE接收的寄存器r33的NaT位被设置,则结果将是NaT消耗故障(ii) 或者会有一个内存损坏错误(当一个未定义的值存储到内存中时),该错误在程序中的不同点和时间出现(iii) 否则将出现永远不会出现的内存损坏错误显然,对于例2中的情况,最终的补救办法是声明原型并提供警告自由端口。另一种方法是让编译器检测应用程序,以便在评估时检测到软件错误。这使程序员有机会在被调用方中添加在G. HOFLEHNER,D. LAVERY,D. 塞赫尔6inta;int c;bar(a);foobar(a);}int a,int b,int c{*a=b;}foo的assembly:1:proc foo;2:alloc r32= 0,1,2,0 3:4:alloc r10= 0,1,1,05:mov r33=6:br.call bar 7:alloc r10= 0,1,2,0 8:9:mov r33 =10:mov r34 =br.call杆件装配1:proc bar;2:movr20=r333:movr33=r204:alloc r34= 2,1,0,0在求值模式下,编译器插入一个额外的alloc指令,该指令在调用之前分配确切数量的输出寄存器(实际参数)在被调用方,它保存最后一个传入的(正式的)参数,并立即将其恢复到allocatin bar()如果调用方和被调用方之间存在参数不匹配,则这将导致还原时出现寄存器堆栈错误(示例3中bar的程序集中的指令3:)。被调用方的额外alloc是必要的,因为在通常的单alloc编译模型中,函数条目中的alloc指令将分配任何调用所需的最大数量的传出寄存器这在例3中的foo程序集中显示:因为foobar()有两个参数,foo将分配两个传出参数寄存器。在调用bar()之前,foo收缩寄存器堆栈以匹配参数的实际数量(示例中为1)。在调用bar之后,foo恢复原始的寄存器堆栈(示例3)。示例3:使用多个分配这种评估技术不仅是源代码可用时的一种选择。在汇编重写工具或二进制重写工具中然而,在这些情况下,我们的评估策略将需要更多的时间。例如,有必要使用数据流分析来确定每个函数调用时的参数的确切数量。3.2运行时检测参数不匹配的成本为了表明这种参数不匹配评估方法的成本,我们运行SPECCINT 2000是一款适用于Itaniu市场的产品 2system.第12集C/C++benchmarks是使用内置编译器编译的[4]第四届中国国际纺织品展览会关于《圣经》的Micros WindowsServer2003,带SPEC基本选项,以实现最佳效果性能编译器开关控制求值代码的生成图2显示了与没有额外代码的二进制文件相比,具有评估代码的二进制文件的每个基准的执行时间增加的百分比所有 时 序 均 针 对 完 整 的 参 考 输 入集 。 只 有 四 个 基 准 测 试 , 197.parser ,252.eon,254.gap和256.bzip2,使用评估代码运行速度慢了1%以上没有一个基准的下降超过2%。放缓的原因有三个:G. HOFLEHNER,D. LAVERY,D. 塞赫尔7图二. 计算方法(i) 执行了额外的alloc和move指令(ii) 在alloc和move指令以及alloc指令和访问输出寄存器的指令之间存在额外的(iii) 由于额外的alloc和额外的move指令以及由于额外的依赖性,SPEC CINT 2000基准测试套件没有参数不匹配,至少在ref输入集执行的路径上没有3.3检测调用setjmp的函数中缺少的volatile声明在Itanium体系结构上,编译器可以在寄存器中分配更多的数据对象。 因此,在源代码中暴露缺少volatile声明的可能性高于其他架构。缺少volatile声明通常会耗费大量的调试时间,任何早期检测到它们的方法都应该是有帮助的。根据ANSI C标准,程序必须将自动变量声明为volatile,如果在从setjmp()调用到任何其他可能导致longjmp()调用的路径上定义的变量的longjmp()之后有使用原则上,编译器可以通过使用经典的到达定义和活动数据流分析算法的变体来检测丢失的volatile声明[1]。示例4展示了挥发性检测算法必须处理的一些情况它显示了控制流程图的快照a)- d在a,b)中,程序不需要变量x的volatile声明:在a)中,定义在setjmp()调用之前,在b)中,从setjmp()调用到定义的路径上没有使用然而,在c)和d)情况下,变量x必须声明为volatile:在这两种情况下,程序都打算在从longjmp()调用返回后使用路径上x的这个例子假设foo()可以触发对longjmp()函数的调用每次函数调用的额外分配开销2.50%2.00%1.50%百分之一0.50%0.00%基准执行时间G. HOFLEHNER,D. LAVERY,D. 塞赫尔8X=foo()foo()foo()foo()(a)b)c)d)示例4:在setjmp()存在的情况下的defs和用法我们提出了算法1,用于检测丢失的volatile声明。它采用了一个变种的到达定义分析和一个经典的活性分析的循环控制流图。算法1的第一步是具有三个扩展的可达性定义分析:(i) 每次调用都会杀死到达调用的任何变量定义(ii) setjmp调用是一个定义(iii) 忽略输出参数的定义第二步中的活性分析给出了一个在setjmp()调用中处于活性的自动变量列表第三步将可到达的调用语句列表与每个setjmp()调用关联。第四步是线性传递每个setjmp()调用:它检查setjmp()调用的活动列表中的每个变量是否有匹配的定义。在这种情况下,变量可能需要声明为volatile,并收集在一个“may volatile”列表中该算法的最后一步是查找必须volatile列表的成员的符号表,并在源代码中可能缺少volatile声明时发出报告消息算法1是一个上下文不敏感的算法,用于查找可能必须在源代码中声明为volatile我们注意到没有证据,这是最好的希望:在一般情况下,检测变量被声明为volatile是不可判定的。这样做的理由是,程序可能会在从setjmp()调用到其对应的longjmp()调用(之一)的路径上停止算法1还可以报告声明为volatile的变量,但实际上在这种情况下,算法会报告volatile声明实际上是冗余的。如果在函数的热路径上使用了不必要地声明为volatile的变量,使用这种诊断将有利于程序性能X=setjmp()!= 0=xX=setjmp()!= 0=xsetjmp()!= 0=xsetjmp()!= 0X==xX=G. HOFLEHNER,D. LAVERY,D. 塞赫尔9步骤1:输入:基本块的循环控制流图(cfg)方法:对所有定义进行定义分析-setjmp()也是一个定义-忽略输出参数-每次调用都会杀死到达调用的任何变量定义(setjmp()defs不会被杀死!)输出:每个调用stmt都有一组定义DEF调用到达调用步骤2:输入:cfg,如步骤1方法:对所有自动变量进行活性分析输出:对于每个setjmp()调用,调用时的LIVEsetjmp变量列表步骤3:输入:来自步骤1的DEF调用方法:foreach调用doforeach %d in DEFcalldo如果(d是setjmp()调用),则List_ADD(callsetjmp,call);Fiodod输出:对于每个setjmp(),setjmp()调用所到达的调用stmt CALLsetjmp的列表步骤4:输入:来自步骤1、2和3的DEF调用、LIVEsetjmp和CALLsetjmp方法:List MAY_VOLATILE=[];foreachsetjmp()调用do在LIVEsetjmpdo中foreach如果(V在调用集合jmp中的任何调用的DEF调用中),则LIST_ADD(MAY_VOLATILE,V);Fiodod输出:必须声明为volatile的自动变量的列表MAY_VOLATILE步骤 5:从步骤4输入方法:在MAY_VOLATILEdoforeach如果(V不被声明为volatile),则报告Fiod输出:编译单元中每个函数可能需要声明为volatile的变量列表算法1:检测缺少的volatile声明我们注意到,算法1可以在许多方面进行扩展,以给出更准确的结果。例如,它可以是上下文敏感的(参见例5 be-low). 当编译器可以查看整个应用程序(整个程序分析)时,过程内算法可以扩展为过程间算法:使用调用图分析[14],它可以确定其调用图中的传递闭包包含对longjmp()函数的调用(触发器调用)的所有函数。然后,算法可以只考虑从setjmp()调用到触发器调用的路径上的定义。让我们假设在例5中foo()是一个触发器调用,但bar()不是。使用此信息,应用于示例5的算法1将报告变量x必须声明为volatile,而变量y的volatile声明是不必要的。这种过程间算法也可以用于检测无效的setjmp()调用:当从setjmp()到触发器调用没有路径时,则setjmp()是无效的,可以删除。然而,在缺乏整个程序分析的情况下,算法保守地必须将任何函数调用视为触发器调用,除非用户向编译器提供关于在应用程序中不是触发调用的函数G. HOFLEHNER,D. LAVERY,D. 塞赫尔10bar()foo()y=X==x=ysetjmp()示例5:使用来自调用图分析的触发器调用信息我们注意到,类似于算法1的算法也可以用于消除C++程序中的冗余捕获区域3.4本节摘要由于它具有很大的区域性,而且它的区域性很强,这使得ItaniumR建筑技术,True更有可能暴露出实现不佳的软件应用程序中的错误这方面的两个例子是参数不匹配和缺少volatile声明。 在这两种情况下,Itanium编译器可以帮助在编译时或运行时检测这些问题。虽然部分中的参数不匹配问题通过提供原型可以很容易地避免使用3.1节中的方法,许多应用程序都有很长的源代码历史,并且3.1节中的运行时评估方法可能是静态分析方法的廉价替代品第3.3节介绍了一个通用的过程内算法,用于在编译时检测丢失的volatile声明该算法可以扩展为过程间算法,并且可以用于查找冗余的volatile声明,甚至死setjmp()调用。这两种方法都可以通过编译器用户选项提供给移植和评估团队,例如ecc4防止NaT消耗故障在本节中,我们描述编译器可以用来防止生成代码中的NaT消耗错误的验证算法它在编译器中特别第一部分给出了后传递算法来检测可能的NaT消耗错误,这可能是通过优化编译器引入的。第二部分给出了一个通用的压力测试技术,在软件测试周期的运行时强制NaT消耗故障。我们注意到,RSE不会清除寄存器堆栈帧上寄存器的NaT位。当生成的代码在定义堆栈寄存器之前存储堆栈寄存器时,运行时可能会发生NaT消耗故障[9]。G. HOFLEHNER,D. LAVERY,D. 塞赫尔11while(outerloop)dowhile(innerloop)do…od…if(first_iteration)thenx =其他int maximum(x);fiod伪汇编:1:外环:2:添加r10=96,sp3:st8[r10]=r604:内循环:五:6:br内循环7:添加r10=96,sp8:ld8r60=[r10]九点10:first_iteration:11:mov r60=12:13:else:14:mov r80=r6015:br.call4.1一种防止NaT消耗故障对于控制推测数据及其用途,可以设置NaT位如果寄存器分配器溢出这些数据,它必须使用st8.spill/ld 8. fill指令,这将消耗ar.unat应用寄存器中的一个NaT位。对于非推测数据,寄存器分配器可以使用不消耗NaT位的常规st8/ld 8指令进行溢出和填充。但是,如果将非推测数据分配到定义之前使用的寄存器中,则仍可能发生NaT原因是通用寄存器有一个与之相关的NaT位。如果未定义该寄存器,则NaT位不会被清除。具体而言,RSE不保证寄存器堆栈帧上的寄存器的NaT位被示例6显示了如何在编译器中引入NaT消耗故障。我们假设Itanium编译器中的寄存器分配器实现了一个基于图的着色方案,如([6])中所述在例6中,变量x在循环底部的第一次迭代中初始化当它溢出到顶部内部循环之外时,程序将在执行底部变量x的初始化之前执行溢出代码。当x没有被推测时,寄存器分配器可以使用常规的st 8/ld 8指令(示例6中的指令3和8)进行溢出。但是,如果变量x被分配给设置了NaT位的堆栈寄存器,则在程序执行时会导致NaT消耗故障。示例6:具有高记录压力的外环和内环显然,寄存器分配器可以保守地始终使用st8.spill/ld 8. fill指令,从而消耗额外的NaT位。或者它可以使用我们在算法2中提出的NaT消耗预防算法。如果它检测到一个可能触发NaT消耗故障的寄存器,算法2将初始化代码插入函数入口块。这将清除NaT位,并防止运行时出现潜在的NaT消耗故障。在任何情况下,NaT消耗故障预防算法都可以在编译器中用作安全网。它可以在编译器测试期间警告编译器开发人员该问题,并且如果它检测到潜在的NaT消耗故障,它可以插入初始化代码以保证G. HOFLEHNER,D. LAVERY,D. 塞赫尔12步骤1:输入:基本块的循环控制流图(cfg)方法:对所有堆栈寄存器执行可用性分析输出:每个基本块的寄存器AVAILat_entry步骤2:输入:如步骤1中的基本块的循环控制流图(cfg)方法:对所有堆叠寄存器执行活性分析输出:每个基本块的堆栈寄存器LIVEat_entry步骤3:输入:cfg,其中包含步骤1和步骤2中的AVAILat_entry和LIVEat_entry列表List NAT_CAND =[];foreach基本块bbdo对于bb.LIVEat_entrydo中的每个如果(!在bb.AVAILat_entry中注册R),然后List_ADD(NAT_CAND,V);Fiodod输出:列出堆栈寄存器的NAT_CAND(可能设置NaT位)步骤4:从步骤3输入方法:foreach函数入口块ebdo对于NAT_CAND中的每个R,在eb中初始化R; //insert mov R=0 REPORTodod输出:验证生成的代码和编译时报告算法2在寄存器分配之后运行,并对所有寄存器采用经典的可用性和活性分析它使用此信息查找在步骤3中定义之前使用的寄存器(传入参数寄存器除外)。如果在控制流程图中存在寄存器在入口处有效但在入口处不可用的任何基本块,则在该算法在控制流图上的一次通过中将这些寄存器添加到列表(算法2中的NATCAND最后,它在每个函数条目处插入该列表中寄存器的初始化代码,并在编译器报告中报告问题。算法2:NaT消耗故障预防4.2测试未初始化寄存器上一节讨论了一个特定的实例,当编译器可能引入一个未初始化的寄存器时,它可能会在运行时导致NaT消耗故障,并演示了编译器如何安全地捕获和修复这种情况。然而,未初始化的寄存器也可能来自未初始化的变量或积极的编译器优化中的错误,如部分冗余消除[13][7][14]和全局代码调度[5][3][4]。在任何情况下,未初始化的寄存器可以由A1-出租m 2检测和报告但算法的结果可能是保守的,因为它报告未初始化的寄存器,尽管它们不是。一个例子是在软件流水线循环中旋转寄存器[15][4],这些寄存器在前一次旋转中定义。在这种情况下,算法2可以将这样的寄存器报告为未初始化,尽管它不是。为了找到实际的未初始化的寄存器,算法2可以被算法3补充或替换,算法3在函数条目中构造编译代码,使得在算法3中使用的每个寄存器都可以被替换G. HOFLEHNER,D. LAVERY,D. 塞赫尔13步骤1:输入:基本块的循环控制流图(cfg)方法:-线性扫描以查找函数中使用的所有寄存器。- 从列表中删除参数寄存器和条目块中定义的寄存器输出:配置文件步骤2:输入步骤1方法:foreach函数入口块ebdoforeachR inUSEDdo为eb中的R设置NaT位;■对于Windows/ Unix内核:ld8.s R=[r 0]■eg.对于Unix应用程序:mov =-1;;ld8.s R=[]用于Unix应用程序odod输出:在运行时评估函数的NaT位初始设置。对于NT应用程序和Linux内核,插装代码是从地址0推测的加载对于Linux应用程序,来自内核地址空间的推测加载保证目标寄存器的NaT位被设置。这种插装方法通过在运行时触发NaT消耗故障来帮助查找未初始化的寄存器。这仍然可能需要对根本原因进行一些调试,但在软件开发周期的早期发现错误更便宜,也值得算法3:入口块中所用寄存器的NaT位插装4.3章节摘要和展望我们提出了验证算法,可以在编译或运行时检测可能在运行时导致NaT消耗故障的潜在未初始化寄存器。由于我们的编译时算法的结果可能是保守的,它可以被补充或替代的仪器方法,强制设置NaT位的每个寄存器中使用的功能。这两种方法都可以在汇编或二进制重写工具中使用。本节中的所有方法都可以扩展为也适用于浮点寄存器。5恢复代码的压力测试在本节中,我们将快速回顾控制和数据推测,并给出恢复代码的示例。我们将在5.1节讨论编译器可能引入的错误类型,并在5.2节中提出测试和验证策略。5.2. 第5.3节给出了验证成本的运行时数据5.1控制和数据推测安腾电池 proccessorfamilyprocvidesaspeculativeload(ld.s)anddavali-日期推测检查(chk.s)指令用于控制推测。用于数据G. HOFLEHNER,D. LAVERY,D. 塞赫尔14(a)原始代码:(b)带有控制和数据推测以及恢复代码在数据推测方面,Itanium体系结构为数据推测提供了一个高级加载(ld.a)和两个高级加载检查(chk. a、ld. c)指令这使代码生成器能够跨可能存在别名的存储区调度加载。在这两种情况下,当推测不成功时,检查指令跳转到恢复代码,恢复代码重新执行非推测加载及其相关指令[9][16]。示例7显示了控制推测和数据推测的恢复从验证的角度来看,可能会出现两个问题:a)编译器生成错误的恢复代码或b)编译器根本不生成(加载)检查指令。这两个错误都可能导致难以调试的不确定性程序行为。(1)ld8 V2=[V1]ld8 V2=[V1](二)ld8.sa V7=[V6]//控制+数据(3)st8 [V3]=V4st8 [V3]=V4(四)ld8.s V8=[V7]//控制(5)加上V5=V4,V3加上V5=V4,V3(六)加V9=V9,V8(7)第二条第二条(8)ld8 V7=[V6]chk.sa V7,rec1(9)ld8 V8=[V7]回复: 检查V8,记录2(10)加V9=V9,V8:..(11)建议1:ld8 V7=[V6](十二)ld8.s V8=[V7](十三)加V9=V9,V8(十四)第二次反射(十五)建议2:ld8 V8=[V7](十六)加V9=V9,V8(十七)第二条示例7:带恢复代码的控制和数据推测5.2用于控制和数据推测的测试和验证策略对恢复代码进行压力测试的关键是强制总是执行到恢复代码的分支,这种强制分支方法利用了寄存器r0的高级检查在高级加载地址表(ALAT)[9]中总是未命中的事实。因此,对r0的高级检查总是分支到其恢复代码。对于控制推测,编译器(或重写工具)可以将控制检查转换成高级控制检查指令。然后,chk. a可以转换为检查r0,这将强制执行到恢复代码的分支。唯一需要注意的是,chk.a只能捆绑在M音节中[14]。因此,要将推测检查转换为I音节,编译器必须将bundle与chk.s拆分为新的bundle,并在本地重新绑定代码(图3)。加载检查指令可以被转换为常规加载指令。然而,也可能存在编译器错误地根本不生成检查指令的情况。在这种情况下,编译器可以检测代码,以在运行时强制执行NaT消耗错误。一种方法是将高级加载转换为来自金丝雀地址的推测加载。金丝雀地址保证推测性加载将G. HOFLEHNER,D. LAVERY,D. 塞赫尔15力分支法转换:chk.s chk.ar0chk.achk.ar0 ldx.c ldx发行人:I单位中的chk.s➔ 在M单位中重新捆绑,或拆分捆绑并将chk.a r0移动到M音节中金丝雀地址法ra:register包含canary地址转换:ldx.a =..ldx.sa =[ra] ldx.s=.. ldx .s=[ra]发行人:不适用于后加薪➔ 将ldx.a =[ry]拆分为:ldx.a =[ra]添加ry=off,ry图3.第三章。恢复代码的压力未命中,将设置推测加载的目标寄存器的NaT位。在Windows或Unix内核中,地址0可以用作金丝雀地址。对于Unix应用程序,内核地址将完成相同的工作。在这种情况下,编译器可以保留一个特殊的地址寄存器,并在函数入口处将金丝雀地址加载到其中。然后,每个推测加载都可以被检测,因此它从金丝雀地址加载并且将失败。但是,该方法的一个警告是,它不适用于推测性的后增量加载。在这种情况下,编译器可以将推测性后增量加载分成来自金丝雀地址的常规加载和用于常规地址的地址增量。金丝雀地址方法捕获无效的恢复代码和丢失的检查指令,用于控制和数据推测。然而,特别是对于Unix应用程序,它需要更多的实现比力分支方法。5.3验证测试控制和数据推测的成本为了度量金丝雀地址方法的运行时开销,我们编译了SPECCINT 2000是一款适用于Itaniu市场的产品 2系统运行Micro sOFTWERWindowsServer2003. 我们使用最佳的基本选项-mance和参考输入集。当反馈配置信息可用时,编译器会更积极地进行推测。图4显示了与常规二进制文件相比,强制推测失败的二进制文件的每个基准执行时间的减慢。减速因子为1意味着可执行程序在正常执行模式下的运行速度是压力测试模式下的2倍因此,减速因子为2意味着可执行文件在正常执行模式下比在压力测试模式下运行快3倍。正如我们的数据所示,对于大多数基准测试,对恢复代码进行压力测试的二进制文件的执行时间增加了50%到100%。值得注意的例外是176.gcc和300.twolf。当每个推测都被迫失败时,两者的运行速度都要慢3倍左右G. HOFLEHNER,D. LAVERY,D. 塞赫尔16见图4。 压力测试恢复代码6结论我们设计并实现了各种编译器验证和评估方法。有些方法是普遍适用的,但有些是特定于Itanium架构的。在第3节中,我们讨论了在运行时发现参数不匹配和在编译时发现volatile声明缺失的源代码在第4节中,我们演示了编译器自验证技术,它可以保护生成的代码不受NaT消耗错误的影响. 在第5节中,我们介绍了用于压力测试恢复代码的强制分支和金丝雀地址本文提出的技术可以作为用户选项在编译器中。用户驱动评估方法的时间开销通常是测试系统的一次运行。第3.1节中的参数不匹配评估算法的运行时成本在编译应用程序时,可以在编译时检测缺少的volatile声明这不需要额外的测试周期。在生产编译器中,自验证方法所花费的编译时间必须很低。这可以通过使用众所周知的和经过良好测试的算法并保持验证空间较小来实现这也解决了关于“如何验证验证算法?”的问题对于SPEC CINT 2000基准测试套件中的12个C/C++基准测试,压力测试恢复代码的平均总执行时间在未来的工作中,我们将产品化本文中描述的原型实现,并收集更多关于其可用性和成功率的数据我们还认为,自验证可以内置到通常的设计/实现/测试周期中,可以帮助提高软件的鲁棒性和可扩展性。这可以通过双重编程方法来完成,其中验证在软件开发周期中设计 我们认为,这将恢复代码压力测试2.521.510.50基准执行时间G. HOFLEHNER,D. LAVERY,D. 塞赫尔17使软件系统更健壮,因为算法不变量将被动态和一致地检查。判断这一设想的可行性,还需要更多的数据和经验此外,评估和验证算法使得能够导出评估和验证度量。例如,深入了解测试覆盖率会很有趣,比如压力测试方法实际覆盖了应用程序中恢复代码的百分比。最后,我们正在研究优化验证和评估算法。例如,我们计划采用负载安全技术[3]来减少推测代码引起的开销。7确认感谢匿名软件供应商使用我们的编译器,并使我们了解本文中讨论的方法引用[1] A. Aho,J. Ullman,[2] ANSI标准编程语言C,委员会草案ANSI,1999年1月。[3] D. Bernstein,M. Rodeh,M.陈文辉,[4] J. Bharadwaj , W. Y. 陈 威 Chuang , G. Ho Zehner , K. Menezes , K.Muthukumar,J. Pierce,“The Intel IA-64 Intel Code Generator”,IEEEMICRO,pp. 44-52,2000年9月/10月。[5] 李文,卷38第5期,1994年9月[6] G. Chaitin,建筑工程,卷。号171982年6月[7] F.周,S。昌河 肯尼迪,S-M。 柳河,巴西-地 罗培培,杜立军,“一种基于SSA形式的部分冗余消除算法”,ACM SIGPLAN '97 程序设计语言设计与实现会议,1997年6月。[8] D. Gries,[9] 在电话里,ItaniumPYRAMID软件开发人员3http://developer.intel。com/des ign/itan ium/dow nloa ds/2453 17.h tm,2002年10月。[10] 在电信业务中,“在电信业务中,第二部分是业务资源ftp://download.intel.com/design/Itanium2/manuals/25111001.pdf网站,2002年6月G. HOFLEHNER,D. LAVERY,D. 塞赫尔18[11] R. Krishnaiyer , D. Kulkarni , D. Lavery , W. Li , C. Lim , J.Ng 和 D.Sehr,[12] S. McConnel,[13] E.莫雷尔角Renvoise,[14] S. Muchnick , Advanced Quancher Design and Implementation , MorganKaufman出版,1997年。[15] B.R.Rau,M. Lee,P.P.Tirumalai,M.S. Schlansker,[16] R. Zahir,J.Ross,D.莫里斯,D。Hess,“IA-64体系结构设计中的操作系统 和 编 译 器 考 虑 因 素 ” , ASPLOS-IX
下载后可阅读完整内容,剩余1页未读,立即下载
cpongm
- 粉丝: 5
- 资源: 2万+
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- 基于HTML实现的手机商城首页金盛集团网页(含HTML源代码).zip
- angular_csv_uploader
- QQrobot:机器人小白
- michael-cote-profile-2020:个人投资组合网站
- android界面设计大全,安卓应用界面设计,Java
- barter_town:一个用于交易商品的Node.js应用
- ashdownlandscapes:一个简单的商业网站给一些朋友
- 山东大学单片机原理与应用实验工程文件 3.3 静态LED显示实验
- orquestra-utils:Orquestra BPM JavaScript实用程序库
- RotoGrinders - FanDuel Tools-crx插件
- amavisd-milter-开源
- CG-Filters-2015.1:为了更深入地了解图像滤镜,提出了图形计算类项目
- Psky企业网站系统 v1.3.zip
- passwords:在 Go 中创建专有密码哈希的函数
- Visualizing-Algorithms:主要是在React中内置的Dijkstras算法可视化工具
- Melon.css:一些带有新鲜色彩CSS套件
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功