具体实施方式
通过创建包括异步转移的效果的指示的软件的中间表示克服了现有技术的局限。该中间表示可包括少于所有可能的异步边,由此简化了程序以及基于该程序的任何处理的中间表示,并且确保使用该中间表示处理程序的工具的准确操作。异步转移的效果可以用一种或多种方式来指示。在某些实施例中,异步转移的效果的指示是直接描绘异步转移的边的形式。在其它实施例中,异步转移的效果是由关于程序中所使用的变量所储存的信息来指示的。该信息可被储存在符号表或者中间表示的伪指令中。可使用多种形式来指示同一中间表示中的异步转移的效果。
本发明首先使用包括流程图的中间表示作为一个示例来描述的。所选择的异步转移的效果被表示为流程图中的边。其它异步转移的效果用与中间表示相关联的符号表中的注释来表示。这一表示具有与现有软件工具兼容的优点,但是可使用任何合适的表示。
中间表示可以用传统的方式来表示程序的同步操作。在一个实施例中,程序的所有部分使用现在已知或以后开发的常规技术被建模为流程图。到处理程序的异步流程最初不在流程图中建模。该中间表示然后可被扩充或修改以表示异步转移的效果。
图2图形地示出了图1C中所描绘的同一程序的流程图260。流程图260包括异步边180A、180B、186和190。并非所有可能的异步都被包括在该表示中。异步边186和190被包括在流程图中,因为它们允许传统软件工具使用流程图来操作而不产生不正确的或非预期的结果。图1C的“所有可能边”流程图中所描绘的边182、184、188和192不被包括在内,因为它们对于传统软件使用流程图260如所预期的那样操作而言是不需要的。
边186和190被选中以包括在流程图260中,因为它们跟随这样的框,这些框定义在包含这些框的尝试区域外部使用的变量。在本示例中,变量Y在区域150中使用,异步边在定义Y的每一框之后添加。
相反,在框162、164、168或172之后不包括任何异步边。尽管这些框包括诸如A、B、X和Z等变量的定义,但是这些变量中没有一个是在尝试体140之外使用的。在程序100的执行期间发生的某一组条件下,那些指令可以在区域150中的指令的执行之前执行。在其它条件下,由于异步转移,那些指令可能不被执行。然而,由于如果程序流程由异步事件定向到尝试体140之外则没有指令消耗它们所定义的变量,因此如果流程图不表示那些指令之后的异步转移的可能性,则不引入任何错误。因此,异步边182、184、188不包括在流程图260中。
标识哪些变量在区域之外使用是传统上在软件工具中执行的功能。在区域外部使用的变量有时候被描述为在该区域“外部生存”。用于确定变量是否在区域外部使用的算法被称为“活性”算法。可使用用于确定变量是否在尝试区域外部生存的任何过程,不论是现在已知的还是以后开发的。
此外,变量的活性可以用任何方便的方法来表示。例如,可使用一个或多个位向量对每一尝试区域构造符号表,每一位向量描述了该区域中的变量可能具有或没有的属性。一个这样的位向量可储存变量是否在外部生存的指示。使用位向量可减少处理时间。例如,每一变量可被分配一整数值,该整数值可被散列到位向量中的特定位单元。当对每一变量执行活性计算时,可基于该变量是否在外部生存而设置或清除对应的位。对定义变量的每一指令,对符号表中的位向量的简单访问指示是否应在该定义之后添加异步边。
上述向中间表示添加边的方法在某些情形中未适当地表示所有异步转移的效果。图3示出了其中可能期望异步转移的进一步表示的情形。
图3示出了流程图360。流程图360中所示的程序包括尝试体340、异常处理程序342、最终处理程序344和由区域350表示的在尝试区域之外的其它代码。图3所示的程序与程序100(图1)的不同之处在于在异常处理程序342的执行之后执行返回到尝试体340内的一点。具体地,执行返回到框340B。
在本示例中,框340B包含除异常处理程序342的执行之后的入口点之外没有任何入口点的代码。框340B内的代码也消耗变量X,使用传统的活性,该变量X将不会被认为是在尝试区域340外部生存。因此,基于尝试区域外部的变量的活性简单地添加边不会导致在流程图中包括X的定义之后的边。然而,流程图中不包括边可导致基于中间表示对程序的不正确处理。例如,表示可能隐含没有指令“消耗”指令168中X的定义,从而允许指令168在代码之外“优化”。为避免使用中间表示的不正确操作,如果变量定义在形成仅在异步转移之后执行的尝试区域的一部分的框中消耗,即使它们不在尝试区域外部生存,也在变量的定义之后添加异步边。此处,流程图360包括为此目的的边380。
在处理程序以形成中间表示时,多个实施例可用于在仅在异步转移之后执行的尝试体内的框中消耗的变量定义之后添加边。在一个实施例中,可使用单独的处理步骤以在外部生存的变量定义之后且在处理程序内消耗的变量定义之后添加异步边。
或者,可修改外部生存的定义,以包含在尝试体的框中使用的变量,该尝试体的框仅在异步转移之后执行。使用外部生存的修改的定义允许符号表中所使用的位向量来表示外部生存的变量,以包含必须为其添加异步边的变量的完整列表。如上所述,一旦确定了该位向量,可一次通过该中间表示,在外部生存位向量中指示的每一变量的定义之后添加边。
在某些实施例中,外部生存位向量可以在两次或多次通过中形成。在第一次通过中,可在尝试体上执行传统的外部生存计算。在第二次通过中,在尝试体的框在异步转移之后执行中消耗的变量可被结合到外部生存位向量中。或者,可使用标识外部生存变量的修改的算法,使得指示在尝试区域外部消耗的以及在尝试区域的框仅在异步转移之后执行中消耗的变量的位向量在一次通过中形成。下文描述可用于使用外部生存的修改的定义来构造位向量的程序。
图3也示出了其中仅为在尝试区域外部生存的变量添加异步边可能不产生期望的结果的另一可能情形。在该示例中,Y的值不是外部生存的,但是在异常处理程序342中使用。如上所述形成的中间表示不包括这样的边,它创建流程路径来指示处理程序342内使用Y的指令可以在尝试体340中的Y的定义之后执行。作为结果,在中间表示上操作的工具可以为变量Y建立存储器结构,在运行时,该变量Y不会由异常处理程序342内的代码访问。
例如,变量Y可以被储存在寄存器中,或者储存在运行时动态地确定的栈中的存储器位置处。储存在寄存器中的值可以在执行异常处理程序时改变,这可具有改变Y的值的不合需要的效果。同样,用于访问栈上的信息的栈指针和其它构造可能在程序执行从尝试体区域移至异常处理程序时改变。相反,在尝试体区域外部使用的变量一般以允许它们在程序流程离开尝试体区域340之后被访问的方式来储存。
不包括所有可能的异步边的中间表示可能不指示变量Y的定义以及它在异常处理程序342中的使用之间的流程路径。如果不指示,则在中间表示上操作的传统工具可以用使得尝试体中使用的Y的值从异常处理程序342中的代码不可访问的方式来为变量Y分配存储。
可通过在每一变量Y的定义之后包括边来避免非预期结果,这可向处理该中间表示的任何工具展示由于异步转移,该变量可能在流程离开尝试体之后消耗。更一般地,可在尝试体中相关联的处理程序内使用的每一变量的定义之后向流程图添加异步边。
作为向流程图添加边的替换,当变量在处理程序中使用时异步转移的效果可以由中间表示中的指示来表示,该指示表明变量Y需要以使得它可从异常处理程序342内的代码访问的方式被储存在存储器中。在一个实施例中,该结果是通过使用程序的传统中间表示中可获得的构造来实现的。
作为一个具体示例,可在为尝试区域340构造的符号表中作出注释。符号表可被注释以指示变量Y在处理程序中使用。使用中间表示来为变量分配存储的工具可被构造成识别这一指示并要求变量沿可通往异常处理程序的任何路径被“回归(home)”到栈。被回归到栈的变量将具有与其中创建该变量的栈有关的特定位置。将变量回归到栈允许为访问该变量的异常处理程序342生成代码。
可对在尝试体中定义的且不在外部生存的、但在附加到尝试体的处理程序内使用的每一变量作出类似的注释。该上下文中的术语“使用”意味着变量可以意味着该变量被定义或被消耗。
符号表中的注释可以用任何合适的形式来作出。如果变量表包括用于其它目的来指示变量应当被回归到栈的数据结构,诸如位向量,则可将值写入该数据结构以指示哪些变量要被回归到栈。
如果没有这样的数据结构用于其它目的,则符号表可用位向量或其它数据结构来构造,以指示哪些变量要在处理程序内使用。从中间表示生成机器可执行代码的编译器或其它工具可对每一这样的变量生成将变量回归到栈或为变量分配存储的代码,使得变量可在与使用该变量的尝试区域相关联的处理程序中访问。
图4更详细地示出了在形成程序的中间表示的过程的一个实施例中使用的流程图。图4示出了流程图460,包括尝试区域、异常处理程序142和最终处理程序144。该流程图是依照上述过程来构造的。
该流程图是通过最初形成仅表示同步流程的流程图来创建的。边被选择性地添加以表示异步转移。添加边482以包装尝试体内容。它被包括在内以简化尝试体中少有的、不可达的框的防止。边484包装尝试体末尾。它确保了对于到达尝试体的任何东西的完整流程。在所描述的实施例中,对包括处理程序的每一尝试体,这些边被包括在中间表示内,而无论变量在哪里定义或使用。
边4861和4862表示在外部生存变量的定义之后添加的边。边4881、4882和4883表示在处理程序142的执行之后的可能的返回路径。边4884表示从最终处理程序144的返回。这些流程路径的表示对于大多数软件工具准确地处理中间表示是期望的。
然而,可通过对经由创建“宿框(sink block)”而来往于处理程序的流程建模在中间表示中进行简化。宿框可以是被添加到流程图以表示从尝试区域的异步转移的效果的所有边的目的地。到执行将在处理程序的执行之后到达的每一可能地方的边可以从宿框中发出。
在图4的示例中,区域410包含用于尝试区域140的所有处理程序。区域410可以被表示为宿框。终止于区域140中的任何地方的边可以被描述为终止与宿框。例如,边484、4861和4862可以被指示为终止于宿框。在区域410中的任一点发出的边可以被描绘成从宿框中发出。例如,边4881、4882、4883和4884可以被描绘成从宿框中发出。
不终止于宿框或从宿框中发出的异步边可以由终止于宿框或从宿框中发出的边来表示。例如,边482可以被移除,并用进入宿框的边和终止于宿框且终止于区域150的边来替换。然而,边484在与边482相同的点处始发。边482已经表示了将替换边482的、到宿框的边。同样,边4884已经在宿框和边484的终止点之间运行。不必要在相同的两个点之间包括多条边,因此,在图4的示例中,不需要添加额外的边来表示在使用宿框时沿边482的流程。
在图2、3和4中,流程图的框由符号来表示,而边由具有连接这些符号的箭头的线来表示。这一图形表示是对于流程图的人类概念化的协助。软件工具无需产生流程图的图形表示。流程图中的每一框可被表示为文件中的文本行,这类似于计算机程序中的指令。框可由标签来标识,而边可由指示到标签的分支的文本行来表示。然而,被选择来表示流程图的具体形式对于本发明不是关键的,并且可使用任何合适的表示。
图5示出了被实现为计算机文件中的文本的流程图。文件中的行包含中间指令。中间指令无需具有与从中间表示生成的高级程序或机器语言程序中使用的任何指令有一对一的对应性。较佳地,指令是允许容易地从高级程序中生成中间表示并允许从中问表示生成机器级指令的形式。这一中间指令集是已知的,且可使用任何合适的中间指令集,无论是现在已知的还是以后开发的。
在图5的实施例中,边由转移程序控制的指令来表示。例如,指令512包括短语“GOTO”,它向指令中标识的标签发信号通知控制转移。此处,标签被指示为L42。标签L42在最终处理程序中和然后在区域150(图2)中的指令之前,这些指令在尝试体中的指令执行完成之后执行。
流程图中的框在图5中由顺序指令来表示,这些指令不被转移控制或发信号通知区域的末尾的指令中断。
并非所有的中间指令都被转换成机器级指令。某些指令,称为伪指令,用于指示将如何处理其它指令。例如,指令514标识了尝试区域的开始。指令516标识了尝试体区域的结束。指令518标识了尝试区域的结束。
伪指令也用于表示异步转移的效果。指令520、522和524是包括代码OPONERROR和标签的伪指令。这些伪指令类似于GOTO指令512,但是代码OPONERROR表示了对标签的条件异步转移。指令520和522表示类似于图4所示的边482和484所表示的效果。边524表示类似于图4中的边4883所表示的效果。标签L1、L2和L3被添加到中间表示,以提供对指令520、522和524的目的地。
指令530和532也用于表示异步转移的效果。它们包括OPONERROR代码以及其后的标签。指令530和532表示类似于图4所示的边4861和4862所表示的效果。
如上所述,中间表示可包括符号表或其它数据结构,以提供关于程序的进一步信息,诸如哪些变量在区域外部生存,或者哪些变量应当被回归到栈。然而,也可为此目的使用伪指令。
指令540是伪指令。如上文关于图3所描述的,中间表示指示了当变量不在尝试体外部使用时变量是否在与尝试体相关联的处理程序中使用。指令540包括代码OPSIDEEFFECT以及变量名。指令用于指示包括在指令中的变量在处理程序中使用。如此处所使用的,OPSIDEEFFECT Y形式的伪指令指示该变量在处理程序中消耗。以Y=OPSIDEEFFECT的形式,伪指令指示该变量在处理程序中定义。这一指令可用于例如影响与执行从表示形成的机器代码程序的处理器中的变量相关联的信息的存储。另外,它可用于影响工具在中间表示上的操作方式。例如,某些工具执行“无用信息收集”,这意味着贯穿程序的处理,该工具标识为不再需要的变量的存储所分配的存储器位置。该存储器然后被指示为可用于其它信息存储。同样,某些工具执行“失效代码”移除。这些工具处理中间表示以标识和移除定义随后不消耗的变量的代码。对伪指令的使用排除了在基于中间标识执行常规的无用信息收集或失效代码移除时对所需代码的移除以及对变量所需的存储位置的盖写。
以此方式表示变量的使用也允许在中间表示上执行机器相关的全局优化。例如,某些平台可不同地处理异常。在某些平台上,处理程序可以作为单独的函数来执行。当发生异常时,处理器可能不可预测地盖写处理器中的寄存器的内容。因此,为这一平台生成代码的工具可响应于这些伪指令生成指令,作为导致异常处理程序用尝试体中定义的变量的值来重新加载寄存器的异常处理程序的一部分。伪指令指示必须为异常处理程序编写代码以正确地执行。
在由中间表示510示出的实施例中,异常处理程序是由550处的指令表示的,且最终处理程序是由552处的指令表示的。异常处理程序和最终处理程序被追加到用于其中处理程序为活动的方法的EXIT指令560的末尾。处理程序可以作为拆散的函数来处理。在其它实施例中,处理程序内的特定代码可以在进一步的处理中被简单地忽略。
图6A示出了中间表示610的一个替换实施例。中间表示610表示与中间表示510(图5)相同的程序。中间表示610是用宿框640形成的。类似于宿框410(图4),宿框640是被添加来表示异步转移的边的终止点。宿框640的起始此处由标签L2表示。
指令620是指向标签L2处的宿框的起始的OPONEERROR伪指令。指令620对指令520和522(图5)两者的效果建模。同样,指令630和632是指向标签L2的OPONEERROR伪指令。指令630和632分别对指令530和532(图5)的效果建模。
宿框640包括表示从宿框到从异步转移的每一可能的返回的边的指令。例如,在图5中,从指令520处起始的异步转移在标签L1处返回。在指令522、530或532处起始的异步转移返回到标签L2,从标签L2流程通过标签L3以继续正常处理。因此,宿框640包括表示返回到标签L3的边的指令644。
宿框640也包括发信号通知处理程序中未在与那些处理程序相关联的尝试体外部使用的变量的使用的伪指令。伪指令646发信号通知变量Y在处理程序内使用。在本示例中,变量Y结合返回到标签L3的处理程序来使用。指令646因此在指向标签L3的指令644之前。以此方式,对处理程序内变量的使用沿流程图中对应于在处理程序的执行期间通过处理程序的路径的路径来指示。
图6B是用于图6A所示的函数的流程图的图形表示。该函数此处不使用处理程序来表示。在所描述的实施例中,程序的受保护区域是与保护者区域分离地建模的。保护者区域可以在类似的流程图中建模。
流程图660包括多个框:表示尝试体区域的框的662、664、668、670和672。框674表示宿框640(图6A)。框676包括作为最终处理程序的一部分执行的指令。框678表示尝试体区域外部的指令。
框由边来连接。边682、684、686和688连接尝试体中的框。边690表示从尝试体到尝试体之后的代码。边692和694表示在从处理程序返回时的流程。边681、683和685表示到处理程序的异步流程。
指令被如图所示地划分成各个框,使得每一框具有单个入口点和单个出口点。因此,代码被划分成各个框,使得每一框在分支指令处结束。同样,新的框在可从一个以上位置进入的任何标签处开始。
现在转向图7,示出了程序的处理的流程图700。该过程在过程框710处开始。在过程框710,形成程序的中间表示,单独地对待各处理程序。该表示不对尝试区域和各自的处理程序之间的流程路径建模。取决于对中间表示的使用,处理程序可被完全省略。表示来往于处理程序的流程以及使用处理程序内的变量可以是适当的。或者,处理程序可以被表示为拆散的函数,或可以用任何其它合适的方式来表示。在框710处创建的中间表示无需包括来往于处理程序的所有可能边的表示。
在框710处形成的中间表示可以是任何合适的形式。例如,它可包括图形表示,如关于图3和4所描述的。作为另一替换,中间表示可使用指令和基于文本的指令,以及诸如关于图5和6A示出的伪指令。
中间表示可包括要处理的所有或部分程序。在此处所提供的示例中,具有单个尝试区域的单个程序用于说明的目的。要处理的程序可包括多个尝试区域,所有这些区域可以用与所示的尝试区域相同的方式来处理。
在过程框712,添加宿框。在所示的实施例中,对每一尝试区域添加一个宿框。然而,不必要在宿框和尝试区域之间具有一对一的对应性。例如,某些尝试区域可以不用宿框来表示。其它尝试区域可以用一个以上宿框来表示。
在过程框714,在尝试体区域外部生存的每一变量的定义之后添加边。每一边在指令之后开始,并在用于相关联的处理程序的宿框处结束。在所示的实施例中,在尝试区域“外部生存”的变量使用修改的活性算法来标识,该算法将在仅在从尝试体的异步转移之后执行的尝试区域的一部分中使用的变量分类为外部生存变量。这一算法的一个示例在下文关于图8A和8B示出。
在过程框716,处理处理程序以标识执行可在异步转移之后对其返回的所有点。添加从宿框到这些位置中的每一个的边。
在过程框718,向中间表示添加包装每一尝试体的边。包装尝试体的边的一个示例是指令620(图6A)。
在过程框720,扩充中间表示,以表示不在尝试体外部使用但在处理程序中使用的变量。对中间表示的扩充可以是任何合适的形式,诸如使用OPSIDEFFECT代码作为图6A中所示的指令的伪指令。可在与中间表示相关联的符号表中作为替换或另外地作出注释,或者可以用任何合适的形式来表示信息。
在过程框722,使用所得的中间表示来处理程序。在一个实施例中,中间表示在编译器中使用。编译器使用中间表示用于全局优化和代码生成。它可用于诸如失效代码移除、调度、代码移动、无用信息收集和公共子表达式消除等过程。编译器可生成可被加载到基于处理器的系统上并被执行的机器语言代码。然而,中间表示无需用于创建可执行文件。中间表示可结合已解释的语言来使用,在该语言中,机器语言指令在程序执行时生成。但是,中间表示完全不必要用于生成机器语言代码。中间表示可用于例如协助程序分析、模拟操作或任何其它期望的目的。
图7所描述的处理可以用任何合适的方式来执行。在一个实施例中,中间表示是使用运行在传统计算机上的软件工具来创建的。该软件工具可以用任何合适的编程语言来实现,不论是现在已知还是将来开发的。
图7包括过程框714,其中基于修改的活性定义向中间表示添加边。依照修改的定义,如果变量在异步转移出尝试体区域之外后使用,则变量被认为是生存的。如果变量在异步转移之后执行的程序的另一区域中生存,则变量被认为是外部生存的。如果变量在异步转移之后访问的尝试体的区域中生存,则它们也可被认为是外部生存的。如果那些变量仅在该区域中使用,则它们不会已被包括在尝试体外部生存的变量集中,并且因此需要被添加到该集合。
图8A是用于依照修改的定义标识在尝试体区域外部生存的变量的过程的流程图。在所示的实施例中,处理在正编译的函数上执行。然而,该过程可更一般地应用于为任何目的而处理的任何程序或代码单元。
该过程在过程框810处开始。图8A所示的过程在中间表示被部分地准备之后开始。在所描述的实施例中,图8A的过程在初始中间表示被扩充以反映异步转移的效果之前执行。
以此形式,函数具有流程图与其相关联,且框可以从中间表示中标识。中间表示也可包括示出函数中的区域之间的互连的区域图。在本示例中,每一区域具有三种类型中的一种与其相关联:尝试、处理程序或根。根是区域树的顶部的未保护区域。区域可以被嵌套,使得区域图可以被认为是树,其分支是按照嵌套区域的方式来定义的。
图8A的过程可以用任何合适的方式来实现。在所描述的实施例中,该过程是由形成中间表示的工具所使用的软件驱动程序来执行的。表I给出了可用于实现图8A的过程的伪代码的一个示例。任何合适的编程语言可用于从伪代码中创建程序。例如,可使用C++或C#编程语言。附加的计算机程序清单(形成了本申请的公开内容的一部分)给出了如何实现表I、II和III中表示的伪代码的一个示例。
在过程框810,计算在程序中的每一框中生存的变量。在所示的实施例中,变量的活性是用与流程图分离的处理程序区域来确定的,使得对处理程序区域中的变量的使用不会影响活性计算。然而,可使用传统的活性计算。
在过程框812,标识仅有异步入口点的框。异步入口点是由代码中除了从处理程序区域中的控制转移指令以外不能到达的标签来标识的。
在该过程的后续步骤中,表示哪些变量对每一尝试区域中的每一框为内部生存的值在逐个区域的基础上聚集,以创建对每一尝试体区域为外部生存的所有变量的集合。
每次从过程框814开始时处理一个框,其中选择一个框。表I中的伪代码指示与正在编译的函数相关联的流程图在被标识为list of basic blocks(基本框列表)的数据结构上操作,该数据结构列出了该区域中的所有框。由此,在过程框814,要处理的框可被选为该列表上的下一框,或者以任何其它合适的方式来选择。
在判别过程框816,检查所选择的框是否在尝试体区域内。在该示例中,创建每一尝试体外部生存的变量集,使得能够标识需要被添加到尝试体区域的边。因此,不在尝试区域内的框无需处理。
如果所选择的框不在尝试区域内,则执行前进到判别过程框824。如果如在判别框824所确定的,还有框要处理,则执行返回到过程框814,其中选择另一框。相反,当没有剩下任何框要处理时,执行传递到过程框830以供如下所述的处理。在表I的伪代码中,这些步骤是由指示函数中的每一框已被处理的“foreach(对每一个)”指令来表示的。
如果所选择的框在所处理的尝试体区域内,则该过程从判别框816继续到判别框818。在判别框818,检查在所选择的框之后的框是否也在所处理的尝试体区域内。如由表I的伪代码中的“foreach”指令所指示的,当有一个以上后继框时,考虑每一个。
如果后继框在不同的区域中,则处理继续到框822。在尝试体区域外部生存的变量集可以用任何合适的方式来表示。可用于表示变量集的数据结构的示例是位向量、列表和数据表。在作为示例在表I中给出的程序中,“live out of region(区域外生存)”指可对于每一区域形成的数据结构,“live in(内部生存)”指与每一框相关联的数据结构。计算这两个数据结构的并集。该操作指示当后继框在区域外部时,对该框为内部生存的变量在它之前的区域外部生存。
图8A示出该过程在判别框824处继续。如果还剩下框,则该过程返回到过程框814,其中处理其它框。当处理每一框时,如果它具有在区域外部的后继框,则该后继框中生存的变量被添加到表示在所处理的框的区域外部生存的变量的集合。此处,变量是使用逻辑UNION(并)操作来“添加”的。如果变量已经被列出为在区域外部生存,则将该变量“添加”到列表不影响该列表。
当函数或其它程序中的所有框都已被处理时,该过程从判别框824继续到过程框830。在该过程框中,对具有仅可从处理程序到达的部分的区域作出调整。由过程框830执行的处理由以下的表I的示例中的方法AsyncLiveOutOfRegion(异步区域外生存)来执行,并结合下文的图8B更详细地解释。
表I
AccumulateLiveOutOfRegion(Function func)
{
foreach(func.FlowGraph.list_of basic_blocks中的BasicBlock框)
{
Region currentRegion:=block.region
if(currentRegion.type为TRY)
{
foreach(block.list_of_successors中的BasicBlock后继框)
{
if(successor.region不是currentRegion)
{
rgn.live_out_of_region :=
UNION(rgn.live_out_of_region,successor.live_in)
}
}
}
}
AsyncLiveOutOfRegion(func.RootRegion)
}
由方法AsyncLiveOutOfRegion执行的过程在图8B中示出。表II给出了可用于实现方法AsyncLiveOutOfRegion的伪代码的示例。
图8B的过程在过程框850处开始。图8B的处理是在构造中间表示的至少一个阶段完成之后执行的。在这一状态,所处理的程序的区域已被标识具有区域之间的关系。区域可被嵌入在其它区域中,从而创建了可被认为是树结构的结构,其区域形成了树的节点,且区域之间的关系定义了节点之间的连接。
由图8B表示的过程使用树结构来确定处理区域的顺序,以对区域排序用于处理。在所描述的实施例中,区域树是使用后序(post-order)遍历来处理的,使得首先处理嵌入的区域。
在表II的伪代码示例中,后序遍历是使用递归地调用同一方法的“foreach”循环来实现的。每次调用方法时,所处理的区域对其所有子区域或嵌入的区域调用该方法。该递归构建了对方法的调用的栈,直到栈中包括了要处理的所有区域。然后从栈中以与放置到栈中相反的顺序处理每一调用,从而创建了后序遍历。在表II的示例中,仅当正在处理的区域是尝试区域时才作出调用。
在图8B中,处理前进到过程框852。在过程框852,选择要处理的下一区域。如果该区域没有异步入口,则处理前进到判别框870。对没有异步入口的区域不需要进一步的处理。指示哪些区域包括仅可由异步转移到达的部分的信息可在过程框812处收集(图8A)。
相反,如果区域具有异步入口点,则处理前进到过程框856。在表II的示例中,由对方法AsyncLiveOutOfATryRegion(异步尝试区域外部生存)的调用来执行,该方法在以下表III中更详细地示出。入口点856也通过以下表III中更详细示出的对AsyncLiveOutOfATryRegion的调用来进一步处理。
表II
AsyncLiveOutOfRegion(Region parentRegion)
{
foreach(parentRegion.list_of_child_regions中的Region childRegion)
{
if(parentRegion.type为TRY)
{
AsyncLiveOutOfRegion(childRegion,func)
}
}
if(parentRegion.type为TRY)
{
if(parentRegion.has_asynchronous_entrance)
{
AsyncLiveOutOfATryRegion(parentRegion);
}
}
<!-- SIPO <DP n="16"> -->
<dp n="d16"/>
}
当区域包括至少一个异步入口点时,处理在过程框856处继续,其中选择区域中以异步入口开始的框。过程框856是循环的开始,该循环以判别框864结束,导致处理包含异步入口的每一框。在表III的伪代码示例中,该循环是由“foreach”指令来实现的。
对于包括异步入口点的框,执行过程框858、860和862中的处理。这些过程框对应于以下表III中示出的方法AsyncUpdateLiveOutOfTry(异步更新尝试外部生存)、AsyncUpdateContainingTryLiveness(异步更新包含尝试活性)以及AsyncUpdateConainedTryLiveness(异步更新包含的尝试活性)。
在过程框858,被标识为在框内生存的变量被添加到在包含正在处理的框的区域外部生存的变量的集合。
在过程框860,在步骤858中被标识为在区域外部生存的变量被添加到被标识为其中嵌套了区域的区域(即,区域的“父区域”)外部生存的变量的集合。
在过程框862,被标识为在所选择的框外部生存的变量被添加到在嵌入在包含该框的区域中的任何区域外部生存的外部生存变量的集合。过程框862的功能可由诸如表III中所示的AsyncUpdateContainedTryLiveness等方法来实现。如所示的,该方法包括递归地调用该方法的“foreach”循环,指示该处理是对每一子区域执行的。
在过程框862之后,执行判别框864。如果区域中还有框具有异步入口,则处理返回到过程框856,其中选择并处理具有异步入口的下一框。
当没有剩下具有异步入口的框时,处理在判别框870处继续。如果还剩下尝试区域要处理,则该过程循环返回到过程框852,其中选择并处理下一区域。
表III
AsyncLiveOutOfATryRegion(Region tryRegion)
{
foreach(tryRegion.list_of_asynchronous_entrances中的BasicBlock entranceBlock)
{
AsyncUpdateLiveOutOfTry(tryRegion,entranceBlock)
AsyncUpdateContainingTryLiveness(tryRegion,entranceBlock)
AsyncUpdateContainedTryLiveness(tryRegion,entranceBlock)
}
}
AsyncUpdateLiveOutOfTry(Region tryRegion,BasicBlock entranceBlock)
{
tryRegion.live_out_of_region:=UNION(tryRegion.live_out_of_region,
entranceBlock.live_in)
}
AsyncUpdateContainingTryLiveness(Region tryRegion,BasicBlock entranceBlock)
<!-- SIPO <DP n="17"> -->
<dp n="d17"/>
{
tryRegion.parent.live_out_of_region:=
UNION(tryRegion.parent.live_out_of_region,entranceBlock.live_in)
}
AsyncUpdateContainedTryLiveness(Region tryRegion,BasicBlock entranceBlock)
{
foreach(Region childRegion in tryRegion.list_of_child_regions)
{
if(childRegion.type=TRY)
{
childRegion.live_out_of_region:=
UNION(childRegion.live_out_of_region,
entranceBlock.live_in)
AsyncUpdateContainedTryLiveness(childRegion,entranceBlock)
}
}
}
描述了本发明的至少一个实施例的若干方面之后,可以理解,本领域的技术人员可以容易地想到各种改变、修改和改进。
例如,本发明被示出为结合编译器使用。本发明可用于产生被加载到计算机或其它机器用于执行的可执行机器代码的传统编译器中。然而,本发明可用于处理程序的任何软件工具。例如,本发明可用于驻留在执行程序的处理器上且在需要时将程序的各部分转换成机器代码的即时(JIT)编译器。
这些改变、修改和改进都是本发明的一部分,且旨在落入本发明的精神和范围之内。因此,以上描述和附图仅作为示例。
以上描述的本发明的实施例可以用多种方式中的任一种来实现。例如,各实施例可使用硬件、软件或其组合来实现。当用软件实现时,软件代码可在任何合适的处理器或处理器集合上执行,不论是在单个计算机中提供还是在多个计算机之中分布。应当理解,执行上述功能的任一组件或组件集合一般可被认为是控制上述功能的一个或多个控制器。一个或多个控制器可用多种方法来实现,诸如用专用硬件,或用使用微码或软件编程以执行上述功能的通用硬件(例如,一个或多个处理器)。
同样,此处所列出的各种方法或过程可以被编码为可在采用各种操作系统或平台中的任一个的一个或多个处理器上执行的软件。另外,这类软件可使用多种合适的编程语言中的任一种和/或常规编程或脚本工具来编写,并且也可被编译为可执行机器语言代码。
在这一方面,本发明的一个实施例针对用一个或多个程序编码的计算机可读介质(或多个计算机可读介质)(例如,计算机存储器、一个或多个软盘、紧致盘、光盘、磁带等等),当在一个或多个计算机或其它处理器上执行程序时,执行实现上述本发明的各实施例的方法。这一个或多个计算机可读介质可以是可传输的,使得储存在其上的一个或多个程序可以被加载到一个或多个不同的计算机或其它处理器上,以实现上述本发明的各方面。
术语“程序”在此在一般的意义上用来指可用于对计算机或其它处理器编程以实现上述本发明的各方面的任一类型的计算机代码或指令集。另外,应当理解,依照本实施例的一个方面,当被执行时执行本发明的方法的一个或多个计算机程序无需驻留在单个计算机或处理器上,而是可以用模块化的方式分布在多个不同的计算机或处理器之中,以实现本发明的各方面。
本发明的各方面可被单独使用、组合使用或与上述实施例中未具体描述的各种安排结合使用,因此其应用不限于上文的描述中陈述的或附图中所示的细节和组件的排列。例如,一个实施例中所描述的各方面可以用任何方式与另一实施例中所描述的各方面组合。
权利要求书中对诸如“第一”、“第二”、“第三”等序数词的使用来修改权利要求元素本身不意味着一个权利要求元素对于另一个权利要求元素的任何优先级、优先顺序或次序,也不意味着执行方法的动作的时间顺序,而是仅用作将具有某一名称的一个权利要求元素与具有同一名称的另一权利要求元素(但用于序数词)区分的标签,以区分权利要求元素。
此外,诸如“添加”和“聚集”等术语此处用于描述对象集的创建。在创建这种集合时,创建该集合的成员或向该集合添加成员的顺序对本发明而言不是重要的。集合的成员可以用任何顺序或甚至同时生成。集合的成员可以按所创建的顺序或以任何其它顺序被标识为集合的成员。
同样,此处所使用的措词和术语用于描述的目的,并且不应当被认为是限制。对“包括”、“包含”或“具有”、“含有”、“涉及”及其变体的使用旨在包含之后列出的项及其等效项以及附加项。
计算机程序清单
注意,这是在流程图上执行的算法,它完全不考虑所有的异常处理程序。在JIT64中,选择将它们从父方法中拉出,并将它们追加到末尾的EXIT语句作为拆散的函数。其它方法可忽略处理程序,诸如形成SESE区域。
在计算了框级的内部生存和外部生存(仅在父方法中)之后,需要将其映射到EH区域图。具体地,对每一尝试区域计算区域外部生存。然后使用仅异步地到达的框扩展区域外部生存的定义。该扩展在MSIL_AsyncLiveOutOfRegion()中出现,其中调用站点为粗体。该被调用者的细节在下页上扩展。以下是顶级驱动程序。
void
MSIL_AccumulateLiveOutOfRegion(
PFUNC func,
COMPILER_INSTANCE*ciPtr
){
PARC arc;
PREGION rgn;
PBLOCK blk,blkSucc ;
FOREACH_BLOCK_IN_FUNC(blk,func){
if(REGION_TYPE(FG_REGION(blk))==RGN_TRY){
rgn=FG_REGION(blk);
FOREACH SUCCESSOR_ARC(arc,blk){
blkSucc=FG_SINK(arc);
if(FG_REGION(blk)!=FG_REGION(blkSucc)){
//Accumulatelive_out_of_region
//as the sum of what′s live in
//to every successor block (at this point)
//plus what′s live into the top of the
//try region.
BvOr(REGION_LIVEOR(rgn),FG_LIVEIN(blkSucc));
}
}
}
}
MSIL_EHFinalizeNonLocalFlow().
if(DO_OPT_WITH_ASYNC_TRY_ENTRANCE){
//Handle Visual Basic flow graphs
MSIL_AsyncLiveOutOfRegion(FU_REGION(func),func,ciPtr);
}
}
顶层驱动程序执行区域树的后序遍历。当它从树中的节点上的递归“出栈返回”时,执行落出循环之外。它然后处理作为尝试区域的任何区域(即,不是EH区域图中的处理程序节点)
它使用下页中详细陈述的“工作者”例程MSIL_AsyncLiveOutOfATryRegion。
直观上,EH区域树的简单递归、后序遍历强迫“内部一外部”排序。由此,最深嵌套的尝试区域在包含它们的任何尝试区域之前处理。
void
MSIL_AsyncLiveOutOfRegion(
PREGION rgnParent,
PFUNC func,
COMPILER_INSTANCE*ciPtr
){
PREGION rgnChild;
FOREACH_CHILD_REGION_NOT_DEAD(rgnChild,rgnParent){
if(REGION_TYPE(rgnParent)==RGN_TRY){
MSIL_AsyncLiveOutOfRegion(rgnChild,func,ciPtr);
}
}NEXT_REGION;
if(REGION_TYPE(rgnParent)==RGN_TRY){
if (REGION_ASYNC_TRY_ENTRANCES(rgnparent)){
MSIL_AsyncLiveOutOfATryRegion(rgnParent,func,ciPtr);
}
}
}
void
MSIL_AsyncLiveOutOfATryRegion(
PREGION rgnTry,
PFUNC func,
COMPILER_INSTANCE*ciPtr
){
PTUPLELIST tl;
PBLOCK pblock;
INT flagUpdate=0;
PTUPLE tupHead ,tupTmp,tupLabel;
ASSERTNR(REGION_TYPE(rgnTry)==RGN_TRY);
ASSERTNR(REGION_ASYNC_TRY_ENTRANCES(rgnTry));
FOREACH_TUPLIST_ELEM(tl,REGION_ASYNC_TRY_ENTRANCES(rgnTry)){
ASSERTNR(TU_ISLABEL(TLIST_TUPLE(tl)));
//Then add what′s live in to this (asynchronously reachable)
//subregion of a try body,to what′s live outside the entire
//try.The set {live_out_of_region}is maintained on the
//region node.This cached node data will be used for
//incremental update after we process the entiretry body.
//#1
//Update the live outof region for this try.
pblock=
MSIL_AsyncUpdateLiveOutOfTry(TLIST_TUPLE(tl),rgnTry,ciptr);
//#2
<!-- SIPO <DP n="21"> -->
<dp n="d21"/>
//Now propagate the new information out to any other
//_containING_outer try body.
MSIL_AsyncUpdateContainingTryLiveness(rgnTry,pblock,ciPtr);
//#3
//Now propagate the new information out to any other
//_containED_deeper try body.
MSIL_AsyncUpdateContainedTryLiveness(rgnTry,pblock,ciPtr);
}
}
最后,示出该算法的细节以找出允许扩展区域外部生存的定义的关键框。
//对于尝试体中仅可从处理程序到达的该标签,
//找出任何新的live_out_of_region作用。这将是在从标记异步入口的标签出发
//且不能通过在词汇上包含在尝试区域中的所有边从尝试的顶部到达
//的路径上的任何向上展示的USE。
PBLOCK
MSIL_AsyncUpdateLiveOutOfTry(
PTUPLE tupLabel,
PREGION rgnTry,
COMPILER_INSTANCE*ciPtr
){
PTUPLE tupFirstBIk;
PBLOCK pFirstBIock;
ASSERTNR(TU_REGION(tupLabel)==rgnTry);
tupFirstBlk=TupFindPrev(tupLabeI,TK_BLOCKTUPLE);
ASSERTNR(tupFirstBIk&&TU_ISBLOCK(tupFirstBIk));
pFirstBIock=TU_BLOCK(tupFirstBIk);
BvOr(REGION_LIVEOR(rgnTry),FG_LIVEIN(pFirstBlock));
return pFirstBlock;
}