掌桥专利:专业的专利平台
掌桥专利
首页

使用编译器生成的仿真优化元数据改进仿真和跟踪性能

文献发布时间:2023-06-19 12:02:28


使用编译器生成的仿真优化元数据改进仿真和跟踪性能

背景技术

在开发软件应用时,开发人员通常花费大量时间来“调试”应用代码,以查找代码中的运行时错误(例如,不希望的行为和软件崩溃)。在这样做时,开发人员可以采取几种方法来重现和定位源代码故障(bug),诸如基于不同输入观察程序的行为、插入调试代码(例如,打印变量值、追踪执行的分支等等),临时去除代码部分等等。追踪引起给定不希望的行为或软件崩溃的代码可能会占用应用开发时间的很大一部分。

为了协助开发人员进行代码调试过程,已经开发了许多类型的调试软件应用(“调试器”)。这些工具向开发人员提供对计算机代码的执行进行跟踪、可视化和更改的能力。例如,调试器可以将代码指令的执行可视化,可以在代码执行期间的不同时间处呈现存储器和寄存器值,可以使得开发人员能够更改代码执行路径,和/或可以使得开发人员能够设置断点以暂停应用执行并且呈现在断点触发时的程序状态。

新兴形式的调试应用实现了“时间行程”、“反向”或“历史性”调试,其中通过将软件和/或硬件跟踪到一个或多个跟踪文件中来记录/跟踪一个或多个程序线程的执行。使用一些跟踪技术,这些(多个)跟踪文件包含每个被跟踪线程的执行的“比特精确”跟踪,其然后可以被用来在以后重放每个被跟踪线程的执行以进行向前和向后分析。使用比特精确的跟踪,可以将每个被跟踪线程的在先执行重现到其单个机器代码指令的粒度。

一些跟踪记录技术部分地基于在处理器执行每个被跟踪线程的机器代码指令期间记录的处理器数据涌入(例如,高速缓存未命中、未高速缓存的读取等等)来记录比特精确的跟踪。这些被记录的处理器数据涌入使得时间行程调试器能够稍后重现在被跟踪线程的重现期间由这些机器代码指令所读取的任何存储器值。虽然可以经由硬件(例如,通过修改物理处理器以协助将数据涌入记录到处理器)来实现比特精确的跟踪,但是在许多情形中,使用二进制仿真来记录比特精确的跟踪可能是有利的,该二进制仿真对软件仿真器处的对象代码的执行进行仿真。但是,二进制仿真增加了比特精确的跟踪的开销,这可能会限制在生产环境中在其中实际地记录比特精确的跟踪的情形。

发明内容

本文所描述的至少一些实施例提高了二进制仿真的效率。这些实施例利用编译器生成的仿真优化元数据,该仿真优化元数据捕获以源代码表达的高级程序行为,以便在仿真从该源代码中生成的机器代码指令的执行的同时执行仿真器优化。使用这些高级程序行为的知识,这些仿真器优化可以减少仿真器为了仿真这些机器代码指令的执行而需要执行的工作量——同时获得与在处理器处已经实际执行机器代码指令的等效结果。例如,仿真器优化可能会减少仿真一个或多个机器代码指令的执行所需要的步骤数(即,与直接在处理器处执行(多个)那些指令相比),和/或可能会完全省略一个或多个机器代码指令。但是,这些仿真器优化保留了根据源代码及其语言存储器模型而有意义的那些机器代码指令的执行的外部可见的副作用(例如,诸如针对其仿真(多个)那些指令的线程外部可见的存储器写入)。这样,本文的实施例可以使用源代码中编译器可用的信息——但是其通常可能不会在对应的机器代码中被明确捕获——以进行仿真器优化,该仿真器优化执行源代码中指定的行为,同时避免以处理器在执行机器代码时将获得的相同保真度来仿真机器代码的一些低效性。

实施例还可以在执行已经利用编译器生成的仿真优化元数据增强的机器代码的二进制仿真时,记录比特精确的跟踪。这些比特精确的跟踪等效于直接在处理器处基于执行该相同机器代码而已经被记录的比特精确的跟踪。在实施例中,跟踪器还可以利用仿真优化元数据来进一步减小这些等效的比特精确的跟踪的大小。

一些实施例涉及使用元数据的方法、系统和计算机程序产品,该元数据保留以源代码表达的行为,来有效地仿真从源代码编译的机器代码的执行。基于访问与作为线程的一部分执行的可执行机器代码相关联的编译器元数据,这些实施例标识未被机器代码所暗示的、源代码的一个或多个行为,机器代码从该源代码被编译。基于从其中机器代码被编译的源代码的经标识的一个或多个行为,这些实施例可以标识在线程的执行的仿真期间可以应用的一个或多个仿真器优化,该一个或多个仿真器优化减少了仿真机器代码中的一个或多个机器代码指令的执行所需要的步骤数,同时保留了线程外部可见的一个或多个副作用。一个或多个仿真器优化包括以下至少一项:(i)减少仿真一个或多个机器代码指令的执行所需要的仿真器操作数,或(ii)从仿真中省略一个或多个机器代码指令。在作为仿真线程的执行的一部分而仿真机器代码的执行的同时,一个或多个仿真器优化被应用以至少:(i)在保留一个或多个副作用的同时,减少仿真一个或多个机器代码指令的执行所需要的仿真器操作数,或(ii)在保留一个或多个副作用的同时,从仿真中省略一个或多个机器代码指令。

至少基于仿真机器代码的执行,一些实施例还可以记录可用于重现仿真机器代码的执行的跟踪。此跟踪等效于在没有应用一个或多个仿真器优化的情况下记录的跟踪。

提供本发明内容是为了以简化的形式介绍一些概念,这些概念将在下面的具体实施方式中被进一步描述。本发明内容既不旨在标识所要求保护的主题的关键特征或必要特征,也不旨在用于帮助确定所要求保护的主题的范围。

附图说明

为了描述可以获得本发明的上述和其他优点和特征的方式,将通过参考在附图中图示出的本发明的特定实施例来呈现上面简要描述的本发明的更具体的描述。应当理解,这些附图仅描绘了本发明的典型实施例,并且因此不应被认为是对其范围的限制,本发明将通过附图的使用,利用附加特征和细节来进行描述和解释,其中:

图1图示出了示例计算机架构,其支持使用保留了以源代码表达的行为的元数据,来有效地仿真从源代码编译的机器代码的执行,其还可以支持将该仿真的执行记录到一个或多个比特精确的跟踪中;

图2图示出了在代码编译期间生成和保留仿真优化元数据的示例;

图3图示出了仿真器的示例,该仿真器可以被用来以在二进制仿真期间利用仿真优化元数据的方式执行二进制仿真;和

图4图示出了用于使用元数据的示例方法的流程图,该元数据保留了以源代码表达的行为,以有效地仿真从源代码编译的机器代码的执行。

具体实施方式

本文所描述的至少一些实施例改进了二进制仿真的效率。这些实施例利用编译器生成的仿真优化元数据以便在仿真从该源代码中生成的机器代码指令的执行的同时执行仿真器优化,该仿真优化元数据捕获以源代码表达的高级程序行为。使用这些高级程序行为的知识,这些仿真器优化可以减少仿真器为了仿真这些机器代码指令的执行而需要执行的工作量——同时获得与在处理器处实际执行机器代码指令的等效结果。例如,仿真器优化可能会减少仿真一个或多个机器代码指令的执行所需要的步骤数(即,与直接在处理器处执行(多个)那些指令相比),和/或可能会完全省略一个或多个机器代码指令。但是,这些仿真器优化保留了根据源代码及其语言存储器模型而有意义的那些机器代码指令的执行的外部可见的副作用(例如,诸如线程外部可见的存储器写入,(多个)那些指令针对该线程而被仿真)。以这样,本文的实施例可以使用源代码中编译器可用的信息——但是其通常可能不会是在对应的机器代码中被明确捕获的信息——以进行仿真器优化,该仿真器优化执行源代码中指定的行为,同时避免以处理器在执行机器代码时将获得的相同保真度来仿真机器代码的一些低效性。

在本说明书和所附权利要求中,在仿真机器代码的同时获得在处理器处执行机器代码的“等效结果”是指:以展现出行为的方式对机器代码进行仿真,该行为根据原始源代码将是合法的(即,基于源代码的编程语言的存储器模型),即使被仿真的机器代码实际上并未捕获到那些行为。因此,如果仿真器仿真了编译器可能已经生成的行为,则使用本文中的优化的仿真器可以在仿真机器代码时产生“等效结果”,即使在被模拟的实际机器代码中未捕获到那些行为。例如,在给定不同的编译器实现选择、不同的编译器优化设置、不同的编译时间限制(例如,寄存器的可用性)等等的情况下,利用仿真优化元数据,仿真器可以以编译器可能具有的方式将对相同存储器位置的读取和/或写入合并、对存储器访问进行重新排序等等。

如所提及的,实施例利用编译器生成的仿真优化元数据,该元数据捕获以源代码表达的、但是可能不容易从所得的机器代码中标识高级程序行为。此仿真优化元数据可以捕获在编译时编译器可用的任何类型的信息,诸如变量依赖性、存储器排序约束(例如,存储器屏障(barrier)/栅栏、获取/释放语义等)、存储器位置的易失性、代码分段的属性(例如,函数是否仅依赖于其参数、函数是否依赖于别名(alias)等等)、指针何时已经被另一实体“采用”等等。虽然可以从源代码中标识此信息,但是在所得的机器代码中,会丢失或混淆此信息中的许多。因此,仿真优化元数据保留了此信息以供仿真器稍后使用。

例如,许多现代处理器可以在给定的处理单元(核心)处执行单个的机器代码指令,其执行顺序不同于软件开发人员编写它们的顺序,或者更典型地,不同于由编译器从源代码生成它们的顺序。这种“乱序”执行使得处理器能够更充分地利用其内部处理单元资源(例如,执行单元),这些资源常常是高度并行化的。例如,如果给定代码流中的两个(或更多)机器代码指令彼此不依赖,则单个处理单元可能能够并行地执行这些指令,而不是在串行地执行下一指令之前先等待一个指令完成。为了在保留正确性的同时能够对存储器操作进行重新排序,现代处理器和编程语言采用了“存储器模型”,该存储器模型限定了存储器效果如何在多处理器/多线程环境中全局可见。特别地,存储器模型限定了多个线程如何可以通过共享存储器来进行交互,包括它们如何可以访问共享数据。因此,存储器模型限定了在执行多个线程时哪些类型的乱序的跨线程存储器观察是合法的。

乱序执行可以被应用于多种类型的机器代码指令,包括执行存储器操作的指令(即,从存储器层级读取或写入到存储器层级的操作,通常包括一个或多个高速缓存和系统存储器)。由于乱序执行和/或存储器层级设计,在一个处理单元处执行的存储器访问操作可以被另一处理单元(或另一处理器)感知为以与机器代码指令的原始流中规定的顺序不同的顺序发生。因此,许多编程语言和/或编译器使得软件开发人员能够通过对源代码中特定位置的注释来指定存储器排序约束(例如,存储器屏障、获取/释放语义等等)、存储器易失性等等。这样的注释阻止了许多编译器优化,诸如省略对应的存储器访问,相对于任何其他存储器访问对存储器访问进行重新排序,假定不变性并且将存储器访问提升到循环之外,等等。在C/C++语言/编译器的特定示例中,例如,此类注释可以包括对类型、变量定义和/或存储器访问添加关键字“易失性”。一些编译器还提供自定义的固有函数,诸如MICROSOFT的VISUAL C++编译器中的“_ReadWriteBarrier”,当其被包括在源代码中时,防止编译器围绕函数调用对存储器访问进行重新排序,而不考虑其易失性。

在针对给定的处理器指令集架构(ISA)发出机器代码指令时,编译器可能会严重依赖ISA的存储器模型来确保遵循这些开发人员指定的存储器排序约束。例如,当以强序ISA作为目标来编译源代码时(例如,诸如来自INTEL和ADVANCED MICRO DEVICES(“AMD”)的x86和x86-64系列处理器ISA,其中每个存储器负载都带有隐式获取语义,并且每个存储器库都带有隐式释放语义),对于以下常常是足够的:编译器以与源代码中相同的顺序放置所生成的访问存储器的机器代码指令,并且依靠所生成的机器代码指令的隐式获取和释放语义来确保开发人员指定的存储器排序约束。但是,尽管这些机器代码指令遵循了来自源代码中的开发人员指定的行为,但是这些行为本身并不容易从所生成的机器代码指令中标识出来,和/或实际上在所生成的机器代码指令中未被暗示。这样,当将源代码翻译为机器代码时,可能会丢失与某些源代码行为相关的信息。在实施例中,编译器可以生成仿真优化元数据,其保留了这些行为的记录。

另外,编译器在目标处理器的物理约束(例如,寄存器的可用性和/或处理器指令的可用性,取决于目标处理器ISA和/或给定ISA内的处理器模型/生成)内操作,并且在人为施加的约束(例如,编译器实现选择、编译器优化选择、编译期间的可用计算资源、目标处理器模型/生成等)内操作。这些约束可以影响作为编译器的输出的机器代码。这样,编译器可以从相同源代码生成非常不同的机器代码集,在给定不同的物理和/或人为施加的约束的情况下,这些机器代码集中的每个机器代码集都遵循以源代码表达的行为。

在二进制仿真期间可以执行的优化的一个示例——基于从编译器生成的仿真优化元数据中获得的源代码行为——是根据源代码的语言存储器模型将可合并为单个存储器访问的存储器访问进行组合。仿真器优化的其他示例可以包括:将频繁请求的存储器(例如,与堆栈变量相对应)固定到高速缓存(例如,L1)中,以避免针对那些访问而去往存储器;使用诸如高级矢量扩展(“AVX”)寄存器的可用寄存器来存储来自可合并存储器访问的数据;不将函数调用视为获取和/或释放屏障;合并对相同高速缓存行的存储器访问;;在函数调用之前和/或之后针对用于堆栈数据的系统存储器将所有高速缓存值的验证分组在一起,等等。

实施例还可以在执行已经利用编译器生成的仿真优化元数据增强的机器代码的二进制仿真时记录比特精确的跟踪。这些比特精确的跟踪等效于直接在处理器处基于执行该相同机器代码而已经被记录的比特精确的跟踪。在实施例中,跟踪器还可以利用仿真优化元数据来进一步减小这些等效的比特精确的跟踪的大小。

对于给定的机器代码指令序列以及这些指令的输入和输出,可以存在一种以上的有效方式来将这些指令的执行捕获到比特精确的跟踪中。在本说明书和所附权利要求书中,在如下情况下比特精确的跟踪与另一跟踪“等效”:在重现那些跟踪时它们将产生完全相同的指令序列,并且在那些指令中的每个指令上具有完全相同的输入和输出集。为了说明,假设机器代码读取存储器位置三次,并且对于这三次读取中的每次读取,它都读取相同的值。该机器代码的执行的一个跟踪可能存储针对第一次读取的信息,同时依赖于另外两次读取隐式地读取相同值的事实。该相同机器代码的等效跟踪可能存储针对第一次读取和第三次读取的信息,而第二次读取是根据第一次读取推断出的,同时另一等效跟踪可能存储针对第一次读取和第二次读取的信息而第三次读取被推断,等等。

除了在依赖于编译器生成的仿真优化元数据进行二进制仿真时通过执行比特精确的跟踪所提供的内在好处之外,比特精确的跟踪还可以直接利用此编译器生成的仿真优化元数据以便改进跟踪性能和/或减小跟踪大小。例如,跟踪器可以使用此类元数据来根据源代码的语言存储器模型对可合并到单个存储器中的存储器访问执行较少的检查,从而通过对存储器访问进行重新排序以一起执行一致性检查(即与系统存储器一起)来减少存储器访问的数目,以避免记录仅依赖于其参数的函数的单个指令的执行等等。

如本领域的普通技术人员将认识到的,并且如将在本文中进一步描述的那样,与传统的二进制仿真相比,依赖于编译器生成的仿真优化元数据的二进制仿真和/或跟踪文件生成可以引起多种技术改进。例如,依赖于编译器生成的仿真优化元数据的二进制仿真可以改进二进制仿真的速度和/或可以减少执行二进制仿真所需要的计算资源。将这样的二进制仿真与跟踪文件生成相结合减少了与执行比特精确的跟踪相关联的开销,打开了在其中与执行比特精确的跟踪相关联的开销可以可接受的情形。此外,与传统技术相比,利用仿真优化元数据可以减小所得的跟踪文件的大小,节省存储器、磁盘空间和/或网络带宽。

为了实现上述目的,图1图示了示例计算环境100,其支持使用保留了以源代码表达的行为的元数据来有效地仿真从源代码编译的机器代码的执行,其还可以支持将该仿真的执行记录到一个或多个比特精确的跟踪。如所描绘的,计算环境100可以包括或利用专用或通用计算机系统101,其包括计算机硬件,诸如例如一个或多个处理器102、系统存储器103和持久性存储装置104,它们使用一个或多个通信总线107可通信地耦合。

本发明范围内的实施例包括用于携带或存储计算机可执行指令和/或数据结构的物理和其他计算机可读介质。这种计算机可读介质可以是计算机系统可以访问的任何可用介质。存储计算机可执行指令和/或数据结构的计算机可读介质是计算机存储设备。携带计算机可执行指令和/或数据结构的计算机可读介质是传输介质。因此,通过示例的方式,而非限制,本发明的实施例可以包括至少两种截然不同的计算机可读介质:计算机存储设备和传输介质。

计算机存储设备是存储计算机可执行指令和/或数据结构的物理硬件设备。计算机存储设备包括各种计算机硬件,诸如RAM、ROM、EEPROM、固态驱动(“SSD”)、闪存、相变存储器(“PCM”)、光盘存储装置、磁盘存储装置或其他磁性存储设备,或者以下(多个)任何其他硬件设备:其可以被用于以计算机可执行指令或数据结构的形式存储程序代码,并且其可以由处理器102访问和执行以实现本发明所公开的功能。因此,例如,计算机存储设备可以包括所描绘的系统存储器103和/或所描绘的持久性存储装置104,其可以各自存储计算机可执行指令和/或数据结构。

传输介质可以包括网络和/或数据链路,其可以被用来携带以计算机可执行指令或数据结构形式的程序代码,并且其可以由计算机系统101访问。“网络”被限定为一个或多个数据链路,其使得能够在计算机系统和/或模块和/或其他电子设备之间传送电子数据。当在网络或另一通信连接(或者硬连线、无线、或者硬连线或无线的组合)上向计算机系统传递或提供信息时,计算机系统可以将连接视为传输介质。上述的组合也应当被包括在计算机可读介质的范围内。

另外,在到达各种计算机系统组件时,以计算机可执行指令或数据结构形式的程序代码可以自动地从传输介质被传递到计算机存储设备(反之亦然)。例如,在网络或数据链路上接收的计算机可执行指令或数据结构可以被缓冲在网络接口模块(例如“NIC”)内的RAM中,并且然后最终被传递到系统存储器103和/或在计算机系统101处的更非易失性计算机存储设备(例如,持久性存储装置104)。因此,应该理解,计算机存储设备可以被包括在还(或甚至主要)利用传输介质的计算机系统组件中。

计算机可执行指令包括例如指令和数据,当在一个或多个处理器102处被执行时,该指令和数据使通用计算机系统、专用计算机系统或专用处理设备执行某个函数或一组函数。计算机可执行指令可以是例如二进制、中间格式指令(诸如汇编语言)或者甚至源代码。

本领域技术人员将理解,本发明可以在具有许多类型的计算机系统配置的网络计算环境中被实践,该计算机系统配置包括个人计算机、台式计算机、膝上型计算机、消息处理器、手持式设备、多处理器系统、基于微处理器或可编程的消费电子产品、网络PC、小型计算机、大型计算机、移动电话、PDA、平板计算机、寻呼机、路由器、交换机等。本发明还可以在分布式系统环境中被实践,其中通过网络而被链接(或者通过硬连线数据链路、无线数据链路、或者通过硬连线和无线数据链路的组合)的本地和远程计算机系统两者都执行任务。以这样,在分布式系统环境中,计算机系统可以包括多个组成计算机系统。在分布式系统环境中,程序模块可以位于本地和远程存储器存储设备两者中。

如所图示的,持久性存储装置104可以存储表示应用程序的计算机可执行指令和/或数据结构,应用程序诸如例如是仿真器104a、跟踪器104b和应用104c(例如,其可以是用户模式应用和/或在内核模式下执行的代码)。一般来说,仿真器104a可用于在仿真处理器处仿真应用104c的二进制代码(即,机器代码指令)的执行,同时利用嵌入到该二进制代码中或与该二进制代码相关联的编译器生成的仿真优化元数据104d,以便减少仿真二进制代码的执行所花费的工作量。跟踪器104b如果被包括,则可以记录该仿真的执行的比特精确的跟踪。该跟踪可以被临时存储在系统存储器103中(即,如跟踪103e所示),并且可以潜在地被持久存储到持久性存储装置104中(即,如跟踪104e所示)。

仿真器104a和跟踪器104b可以均是独立的应用,可以被集成到相同应用(诸如调试套件)中,或者可以被集成到另一软件组件中——诸如操作系统内核、管理程序、云结构等。以这样,本领域技术人员还将理解,本发明可以被实践在云计算环境中,以便仿真和/或跟踪应用104c的执行,应用104c的执行可以跨多个计算机系统分布。云计算环境可以是分布式的,尽管这不是必需的。当被分布时,云计算环境可以在组织内国际地分布,和/或具有跨多个组织所拥有的组件。在本说明书和所附权利要求中,“云计算”被限定为用于使得能够对可配置计算资源(例如,网络、服务器、存储、应用和服务)的共享池进行按需网络访问的模型。“云计算”的定义不限于在被适当部署时可以从这种模型中获得的其他若干优势中的任何优势。

云计算模型可以由各种特性组成,诸如按需自助服务、广泛网络访问、资源池化、快速弹性、可测量服务等等。云计算模型也可以采用各种服务模型的形式,诸如例如软件即服务(“SaaS”)、平台即服务(“PaaS”)和基础设施即服务(“IaaS”)。也可以使用诸如私有云、社区云、公共云、混合云等不同的部署模型来部署云计算模型。

诸如云计算环境的一些实施例可以包括一种系统,该系统包括一个或多个主机,一个或多个主机均能够运行一个或多个虚拟机。在操作期间,虚拟机仿真操作的计算系统,支持操作系统,并且也许还支持一个或多个其他应用。在一些实施例中,每个主机包括管理程序,该管理程序使用从虚拟机的角度所抽象的物理资源来仿真针对虚拟机的虚拟资源。管理程序还在虚拟机之间提供适当的隔离。因此,从任何给定虚拟机的角度来看,管理程序提供了虚拟机正在与物理资源进行接口的错觉,即使虚拟机仅与物理资源的外观(例如,虚拟资源)进行接口。物理资源的示例包括处理能力、存储器、磁盘空间、网络带宽、媒体驱动器等。

无论本文中的实施例是在单个计算机系统101处还是跨多个计算机系统操作,图1都详述了可以被用来实现本文中所描述的各种实施例的每个处理器102的组件中的一些组件。如图所示,每个处理器102可以(尤其)包括一个或多个处理单元105(例如,处理器核心)和一个或多个高速缓存106。在将要被执行的代码加载到系统存储器103中之后(例如,如系统存储器103中的仿真器103a、跟踪器103b和应用103c/元数据103d所示),每个处理单元105经由高速缓存106加载并且执行机器代码指令。在并行执行单元105b处执行这些指令期间,指令可以使用内部处理器寄存器105a作为临时存储位置,并且可以经由高速缓存106读取和写入到系统存储器103中的各个位置(例如,使用高速缓存106的“数据”部分)。如果处理单元105需要尚未被存储在高速缓存106中的数据(例如,代码或应用运行时数据),那么处理单元105可以发起“高速缓存未命中”,这导致所需要的数据从系统存储器103被取出并且被存储在高速缓存106中——同时可能将一些其他数据从高速缓存106“逐出”回到系统存储器103。

通常,高速缓存106包括多个“高速缓存行”,其中每个高速缓存行存储来自后备存储库的一大块存储器,诸如系统存储器103。例如,图1使用表106a象征性地图示出了高速缓存106,其中,表106a中的每行(即,高速缓存行)至少存储地址和值。地址可以指代系统存储器103中的位置(例如,存储单元)。地址可以是物理地址(例如,系统存储器103中的实际物理位置),或者地址可以是虚拟地址(例如,被映射到物理地址的地址,以提供抽象)。例如,虚拟地址可以被用来支持在处理器102处执行的不同过程之间的存储器隔离。

在实施例中,至少部分地基于利用处理器高速缓存(例如,诸如高速缓存106的硬件高速缓存、被仿真的高速缓存等等)来记录由主题应用的机器代码指令所读取的数据,从而记录跟踪103e/104e。这些实施例被建立在以下观察之上:处理器(例如,诸如处理器102或被仿真的处理器)形成半封闭或准封闭系统。例如,一旦主题应用(例如,应用104c)的针对给定线程的数据的部分(即,代码数据和运行时应用数据)被加载到处理器的高速缓存(例如,高速缓存106)中,则处理器本身可以将该线程执行(没有任何外部输入)为时间突发的半封闭或准封闭系统。特别地,处理单元(例如,处理单元105中的一个处理单元105)可以在使用存储在处理器高速缓存的数据部分中的运行时数据的同时并且在使用处理器的内部寄存器的同时,执行来自处理器高速缓存的代码部分中的线程的机器代码指令中的一个或多个指令。当处理单元需要一些信息涌入时(例如,因为机器代码指令正在访问(或将访问)尚未在处理器高速缓存中的、或者存储在不可高速缓存的存储器中的代码或运行时数据,因为需要附加的(多个)机器代码指令等等),处理单元可以触发高速缓存未命中,以导致该信息从系统存储器中被带入到处理器的高速缓存中,或者执行未高速缓存的读取。然后,处理单元可以使用新信息继续执行一个或多个附加的机器代码指令,直到再次需要新信息为止(例如,由于对尚未在高速缓存中的数据的读取,由于来自不可高速缓存的存储器的读取等等)。因此,可以部分地基于足以重现由线程的机器代码指令在其执行期间所读取的任何数据的记录数据(例如,与线程的高速缓存未命中和未高速缓存的读取相关),来记录给定线程的执行的比特精确表示。然后此跟踪数据可以被用作线程的机器代码指令的输入,以便重现线程的原始执行。

如上面所提及的,仿真器104a可以在仿真处理器处仿真应用104c的二进制代码的执行,同时利用嵌入在该二进制代码中或与该二进制代码相关联的编译器生成的仿真优化元数据104d。该元数据104d由编译器(例如,静态编译器、即时编译器等)在将源代码编译为机器代码指令的过程中生成,并且捕获在机器代码指令中可能丢失或者至少可能混淆的源代码的行为。例如,如所提及的,这些行为可以是在编译时编译器可用的任何类型的信息,诸如变量依赖性、存储器排序约束(例如,存储器屏障、获取/释放语义等等)、存储器位置的易失性、代码分段的属性(例如,函数是否仅依赖于其参数、函数是否依赖于别名等等)、指针何时已经被另一实体“采用”等等。

图2图示出了在代码编译期间生成和保留仿真优化元数据104d的示例200。在示例200中,该元数据特定于存储器排序约束,但是本领域普通技术人员将认识到,类似的或附加的技术可以被用来实际上保留任何类型的源代码行为。尽管该元数据104d可以被存储在与输出二进制相关联的任何文件或数据结构中,但是示例200呈现了在其中元数据104d被存储在输出二进制本身内的实施例。图2仅示出了如何生成和保留仿真优化元数据的一个示例200,并且将认识到,本文中的实施例的特定实现可以以多种其他方式生成和保留仿真优化元数据。

一般来说,图2描绘了作为编译器工具链的输入的一个或多个输入源代码文件201(被称为源代码201)、由编译器工具链从源代码201生成的一个或多个中间目标代码文件202(被称为目标代码202)、以及由编译器工具链从目标代码202生成的输出可执行映像203。图2象征性地表示了源代码201内的两个函数204a和204b。在这些函数204a、204b内,图2象征性地表示了源代码中的一些明确的开发人员指定的行为(尽管实施例也可以标识更多内在的源代码行为)。这些明确的开发人员指定的行为包括:注释205a(例如,注释关键字,诸如C/C++中的“易失性”),其在图2中被象征性地表示为解释点,以及固有(intrinsic)205b(例如,固有函数调用,诸如VISUAL C++中的“_ReadWriteBarrier”),其在图2中被象征性地表示为短虚线的源代码。注释205a和固有205b两者都表示开发人员指定的预期存储器排序约束。例如,注释205a可以在一个或多个对应的源代码语句上限定排序约束,而固有205b可以在该固有之后的一个或多个源代码语句上限定排序约束。

如箭头211a和211b所示,编译器工具链从源代码201生成目标代码202(例如,针对每个源代码文件一个目标代码文件)。箭头211c示出了编译器工具链可以从函数204a的源代码生成机器代码指令的块204a’,而箭头211d示出了编译器工具链也可以从函数204b的源代码生成机器代码指令的块204b’。在实施例中,这些机器代码指令被象征性地表示在目标代码202内,诸如通过使用距每个块的开头(即,对应于每个函数的开头)的偏移量。块204a’内的长虚线表示从与注释205a相对应的(多个)源代码语句生成的特定机器代码指令。另一方面,块204b’内的短虚线表示特定指令,该特定指令是从固有205b生成的无操作/伪指令。

目标代码202还包括两个数据结构206a和206b。这些数据结构在图2中被示为表格,但是它们可以采取任何适当的数据结构格式。这些数据结构206a、206b表示由编译器工具链针对机器代码指令的每个块204a’、204b’生成的元数据。如图所示,数据结构206a包括引用块204a’中长虚线的机器代码指令的元数据部分,而数据结构206b包括块204a’中引用短虚线的机器代码指令的元数据部分。

在实施例中,数据结构206a、206b可以被视为包括存储器排序元数据,并且因此在本文中被称为“存储器排序表”。在实施例中,这些存储器排序表包含条目,每个条目通过其偏移量来标识机器代码指令。这些存储器排序表还可以标识特定类型的存储器排序约束,诸如读取屏障、写入屏障或读取/写入屏障。在一些实施例中,甚至可以省略存储器排序约束的类型,其中每个条目都被视为读取/写入屏障。

如箭头211e所示,编译器工具链从目标代码202生成可执行映像203。如所描绘的,可执行映像203可以包括头部203和不同分段210。例如,可执行映像203可以包含PE或ELF头部、分段头部等。可执行映像203也可以包含.text分段、.data分段等。值得注意的是,头部209和分段210不必按所描绘的顺序出现并且可以被散布。在实现中,头部203中的一个或多个头部(例如,分段头部)可以标识可用分段210,并且指定如何将这些分段加载到存储器中以供执行。在实现中,.text分段可以包括来自各种目标代码文件202的机器代码指令,这些指令现在正在由指令存储器地址引用。另一方面,.data分段可以包括程序数据,诸如变量值、常量和由机器代码指令使用的其他数据。

如箭头211f和211g所示,在示例200中,来自目标代码202的数据结构206a、206b被组合并且被插入到可执行映像的分段210中的一个或多个分段中。例如,在图2的示例200中,在可执行映像的.data分段中示出了数据结构207(其聚合了数据结构206a、206b)。实现可以选择将仿真优化元数据包括在可执行映像的各个一个或多个分段中。例如,.data分段的使用(如图2中)可能有益于数据可用性(即,元数据可以加载并且保持驻留在存储器中,甚至是原始目标架构),但是元数据然后甚至会在机器上占用实际上没有利用元数据的存储器。为了减少存储器使用,备选实现可以选择被原始目标架构的二进制加载器丢弃的分段,从而回收被仿真优化元数据所占用的存储器空间。

数据结构207可以被视为存储器排序表,其包含来自目标代码202的存储器排序表的聚合。类似于以上结合目标代码202所讨论的表,数据结构207中的每个条目可以标识特定类型的存储器排序约束,诸如读取屏障、写入屏障或读取/写入屏障。在实现中,数据结构207中的条目通过指令存储器地址来引用对应的机器代码指令。如箭头211h所示,数据结构207可以由头部209中的一个或多个头部209来标识。

可执行映像203可以包括由编译器工具链生成的附加仿真优化元数据。例如,图2还图示出了可执行映像203内的数据结构208。在实施例中,数据结构208包括指令存储器地址范围信息,并且因此可以被视为“范围表”。特别地,数据结构208可以标识由编译器(其还创建仿真优化元数据)生成的机器代码指令覆盖哪个(哪些)范围的存储器地址。如箭头211h所示,数据结构208也可以由头部209中的一个或多个头部209来标识。

当可执行映像203包含源自于除目标代码202以外的源(诸如来自静态链接的共享库)的机器代码指令时,数据结构208可能是有用的。在这些情形中,数据结构208可以被二进制仿真器用来针对每个指令存储器地址确定对应的仿真优化元数据是否可用于该指令存储器地址。如果给定的指令存储器地址被数据结构208中指定的范围所覆盖,那么二进制仿真器可以继续查询数据结构207。否则,如果指令存储器地址未被数据结构208中指定的范围所覆盖,那么二进制仿真器可以确定没有仿真优化元数据可用于该地址,并且因此在适当的情况下悲观地仿真存储器屏障。

值得注意的是,一些实施例可以(或者显式地或者隐式地)指示一个或多个机器代码指令中的一个或多个块缺少对应的仿真优化元数据(例如,诸如(多个)数据结构206a、206b和/或数据结构207)。这些指示可以在编译的任何阶段期间存在,诸如在目标文件中和/或在所得的可执行映像中。例如,可以使用无操作/伪指令、存储在目标代码内和/或在所得的可执行映像中的附加元数据来做出这些指示。例如,这些指示可以有助于避免在代码仿真期间搜索仿真优化元数据,和/或确保仿真器将(多个)指令的(多个)这些块悲观地对待——诸如通过将它们视为存储器屏障。

图3图示出了仿真器301(例如,对应于图1的仿真器104a)的示例300,其可以被用来以在二进制仿真期间利用仿真优化元数据(例如,元数据104d)的方式来执行(例如,应用104c的)二进制仿真。如所描绘的,仿真器301包括各种组件(例如,元数据访问302、行为标识303、优化标识304、处理器仿真305、优化应用306等等),其表示仿真器301可以根据本文所述的各种实施例来实现的各种功能。应当理解,这些组件——包括它们的身份和布置——仅被描绘为帮助描述本文所述的仿真器301的各种实施例,并且这些组件不限于软件和/或硬件可以如何实现本文所述的仿真器301的各种实施例或其特定功能的各种实施例。

元数据访问组件302访问与仿真器300正在仿真的机器代码相关联的编译器生成的元数据。例如,如果应用104c将包括图2的可执行映像203,则元数据访问组件302可以访问包括结合图2所描述的数据结构207、208的元数据104d,或者任何其他适当的编译器生成的元数据。如所提及的,该元数据可以记录包括源代码行为的任何类型的信息,这些信息在编译时是已知的并且在仿真期间已知可能是有用的。

基于由元数据访问组件302访问的元数据,行为标识组件303标识源代码的一个或多个行为,机器代码从该源代码被编译,但是该一个或多个行为在机器代码中可能没有暗示(或者在正在被仿真的机器代码中至少被混淆或不容易标识)。这些行为可以包括,例如变量依赖性、存储器排序约束(例如,存储器屏障、获取/释放语义等等)、存储器易失性、函数级别的元数据(例如,函数是否仅取决于其参数、函数是否依赖于别名)、指针是否已经被另一实体“采用”等等。在一些实施例中,元数据104d甚至可以明确地具体标识可以在仿真器中被省略的事物。例如,元数据104d可以标识不被依赖的处理器的标志。虽然物理处理器在正常操作期间内在地计算其标志,但是软件仿真器会做额外的工作来计算和维护它们。因此,如果仿真器可以避免计算/维护未被消耗的标志,则对仿真器存在潜在大量的节省。

基于由行为标识组件303所标识的(多个)行为,优化标识组件304标识可以在主题机器代码的仿真过程中应用的一个或多个仿真器优化。一般来说,这些优化减少了作为线程的一部分而仿真机器代码的执行所需要的步骤数,同时获得了直接在处理器处执行机器代码或者无需使用这些优化就仿真机器代码的等效结果。一般来说,这可以包括:保留在线程外部可见的、并且根据原始源代码和源代码的语言存储器模型而有意义的任何副作用,即使这些副作用可能会偏离在未应用优化时将会看到的行为。这些仿真器优化可以或者减少仿真一个或多个机器代码指令的执行所需要的仿真器操作数,或者从仿真中完全省略一个或多个机器代码指令。

例如,假设行为标识组件303从元数据104d中标识两个或更多存储位置(例如,对应于变量或数据结构)相互依赖。利用这一知识,优化标识组件304可能能够标识对一个或多个机器代码指令的仿真进行重新排序的方式(例如,引起较少的存储器访问)。这是可能的,因为编译器常常生成机器代码指令,该机器代码指令以使处理单元105的执行单元105b处于繁忙的方式来对操作进行重新排序,但是对于仿真器而言该顺序可能不是最有效的——这可能不受处理器的物理限制(诸如固定数目的可用处理器寄存器)的约束。利用依赖性的知识,优化标识组件304甚至可能能够标识用于完全消除一个或多个机器代码指令的执行的方式。例如,如果——基于数据依赖性的知识——优化标识组件304可以标识没有任何其他对其依赖的数据,则一个或多个操作可能能够被完全消除。例如,取决于分支/条件的结果,编译器可以在通用代码路径中生成其输出仅在一些代码路径中被使用的指令。编译器之所以可以这样做,是因为在硬件处理器处执行此代码时,当不使用这些指令的输出时的额外运行时执行成本要小于当需要数据时来自已经计算输出值的运行时执行收益。仿真器可以充分改变代码执行的性能特性(例如,包括同时运行指令、进行推测等的能力),以使得在仿真器上运行该代码时,负成本可能超过收益。以这样,仿真器可以从通用代码路径中省略此类指令。

作为另一示例,假设行为标识组件303从元数据104d中标识一个或多个特定的存储器屏障。使用存储器屏障的知识,优化标识组件304可能能够标识可以被完全省略的存储器读取和/或存储器写入。特别地,对相同存储器位置的多个写入可能能够被合并为一个写入,如果它们中的全部都发生在相同的释放屏障之前的话(即,消除了除一次写入以外的所有写入操作)。类似地,对相同存储位置的多个读取可能能够被合并为一个读取,如果它们全部都发生在相同的获取屏障之后的话。

作为另一示例,假设行为标识组件303从元数据104d中标识针对一个或多个函数的函数级别的信息。例如,元数据104d可能已经标识特定函数仅取决于其参数。在这种情况下,优化标识组件304可能能够标识针对该函数内的任何局部变量使用寄存器而非存储器的方式,或者甚至可能能够标识具有可被调用的相同行为的本机函数而不对函数进行仿真。附加地或备选地,元数据104d可能已经标识特定函数不依赖于存储器别名。在这种情况下,仿真器301可能不需要确保可以通过存储器地址或类似的别名来访问数据,并且优化标识组件304因此可以能够标识用于对数据进行重新定位的位置(例如,寄存器、仿真器可用的一些其他暂存器空间等),其可以被仿真器301更有效地访问。附加地或备选地,元数据104d可能已经标识本身不使用存储器屏障的特定函数。通常,仿真器301可能将对函数的调用视为存储器屏障,限制围绕该调用的可用仿真器优化。然而,如果元数据104d指示对于给定函数不是这种情况,则优化标识组件304可能能够标识更有效地在函数内执行读取和写入的方式,诸如通过避免一直去往针对读取/写入的存储器。

作为另一示例,假设行为标识组件303从元数据104d中标识实体是否已经具有指向该实体的被另一实体“采用”的存储器指针。如果是这样,则该实体是可别名的,因此仿真器301可能需要注意不要将实体的数据放置在不支持别名化的位置。否则,如果实体的存储器指针未被别名化(或不能被别名化),那么优化标识组件304可能能够标识将实体的数据存储到是快速的位置中的方法(例如,仿真的处理器中的暂存空间),而不是存储器位置,如果这是有利的话。

其他示例优化可以合并寄存器,将频繁请求的存储器固定到高速缓存中,使用AVX寄存器存储来自可合并存储器访问的数据,合并对相同高速缓存行的存储器访问,在函数调用之前和/或之后,针对用于堆栈数据的系统存储器,将对所有高速缓存值的验证分组在一起。

处理器仿真组件305在软件仿真的处理器处仿真主题机器代码的执行。在此仿真期间,优化应用组件306应用由优化标识组件304所标识的优化。如所讨论的,这些优化可以操作以在保留外部副作用的同时减少仿真主题代码指令的执行所需要的仿真器操作数,或者可从仿真中完全省略机器代码。

鉴于前述内容,图4图示出了使用元数据的示例方法400的流程图,该元数据保留了以源代码表达的行为,以有效地仿真从源代码编译的机器代码的执行。现在将鉴于图1-图3的架构、组件和示例来描述方法400。

如图4中所示,方法400包括访问编译器生成的仿真优化元数据的动作401。在一些实施例中,动作401包括访问与作为线程的一部分执行的可执行机器代码相关联的编译器元数据。例如,基于对应用104d的执行的仿真,元数据访问组件303可以访问元数据104d。元数据可以保留对编译应用104c的编译器可用的任何类型信息,并且这些信息对改进仿真性能可能是有用的。如所讨论的,这可以包括诸如以下的信息:变量依赖性、存储器排序约束、存储器易失性、函数级别的元数据、指针是否已被另一实体“采用”等。

方法400还包括标识一个或多个源代码行为的动作402。在一些实施例中,动作402包括标识未被机器代码暗示的、源代码的一个或多个行为,该机器代码从该源代码被编译。例如,行为标识组件304可以标识保留在元数据104d中的任何行为(例如,诸如变量依赖性、存储器排序约束、存储器易失性、函数级别的元数据、指针是否已被另一实体“采用”等)。如所指出的,这些行为可能无法在机器代码中被明确标识,和/或可能很难根据机器代码确定。例如,编译器可能已经依赖于目标ISA中的、未在可执行代码中明确标识的行为(例如,获取/释放语义),来强制实施源代码行为。另外,机器代码可以暗示在源代码中实际上并不存在的行为(诸如由目标ISA所保证的行为)。

方法400还包括标识(多个)仿真器优化的动作403。在一些实施例中,动作403包括标识可以在对线程的执行的仿真期间应用的一个或多个仿真器优化,该一个或多个仿真器优化减少了对机器代码的一个或多个机器代码指令的执行的仿真所需要的步骤数。可以在保留在线程外部可见的一个或多个机器代码指令的执行的一个或多个副作用的同时完成此操作。这些仿真器优化可以包括以下至少一项:(i)减少对一个或多个机器代码指令的执行的仿真所需要的仿真器操作数,或(ii)从仿真中省略一个或多个机器代码指令。例如,基于在动作402中所标识的行为,优化标识组件305可以标识仿真器优化/快捷方式,其可以被采取来仿真应用104c的代码,同时产生在(多个)处理器102处已经执行应用104c或者尚未进行仿真器优化/快捷方式的等效结果。作为结果,根据原始源代码和源代码的语言存储器模型,执行每个线程的外部可见的副作用是有意义的,即使这些副作用可能会偏离在未采用优化/快捷方式时将会看到的行为。如结合优化标识组件304所讨论的,这些优化/快捷方式可以包括诸如此类的事物:对存储器访问进行重新排序,围绕寄存器屏障合并读取和/或写入,使用寄存器或其他仿真器暂存空间而不是去往存储器,将频繁请求的存储器固定到高速缓存中等。这些优化/快捷方式的结果是使用比其他方式所需的更少的仿真器步骤来仿真应用104c的代码,包括潜在地完全省略应用104c的代码中的一些代码的仿真。

方法400还包括在机器代码仿真期间应用(多个)优化的动作404。在一些实施例中,动作404包括,在作为仿真线程的执行的一部分来仿真机器代码的执行的同时,应用一个或多个仿真器优化以至少:(i)在保留一个或多个副作用的同时,减少仿真一个或多个机器代码指令的执行所需要的仿真器操作数,或者(ii)在保留一个或多个副作用的同时,从仿真中省略一个或多个机器代码指令。例如,在处理器仿真组件305仿真应用104c的代码的同时,优化应用组件306可以应用在动作404中所标识的优化/快捷方式。

鉴于以上讨论,这些优化可以包括,例如:当函数仅依赖于其输入时或者当函数不引发别名时,省略一个或多个存储器访问;当函数不依赖于别名时,将数据从存储器重新定位到寄存器;当函数不使用存储器屏障时,避免针对一个或多个读取或一个或多个写入而去往存储器;在相同的获取屏障之后,将对相同存储器地址的多个存储器读取合并;在相同的释放屏障之前,将对相同存储器地址的多个存储器写入合并;基于变量依赖性,对一个或多个机器代码指令的仿真进行重新排序或者省略至少一个机器代码指令;避免计算标志;如果实体不能被别名化,则避免将实体的数据存储在存储器中等等。

如所提及的,实施例还可以在执行已经利用编译器生成的仿真优化元数据增强的机器代码的二进制仿真时,记录比特精确的跟踪。以这样,方法400还可以包括记录对所仿真的执行的跟踪的动作405。在一些实施例中,动作405包括至少基于仿真对机器代码的执行,记录可用于重现对机器代码的所仿真的执行的跟踪。例如,在仿真器104a使用元数据104d仿真应用104d的执行时,跟踪器104b可以将该所仿真的执行的比特精确的跟踪记录到跟踪103e/104e。因为仿真器104a具有在没有仿真优化/快捷方式的情况下执行应用104c的等效结果,所以此跟踪可以等效于在没有应用仿真器优化的情况下被记录的跟踪。

在实施例中,跟踪器104b还可以直接利用元数据104d来减小跟踪大小。例如,跟踪器104b可以使用这种元数据104d来根据源代码的语言存储器模型对可合并到单个存储器中的存储器访问执行较少的检查,或者通过将它们重新排序来一起执行一致性检查(即,利用系统存储器)以减少存储器访问的数目。在另一示例中,如果元数据104d指示函数仅依赖于其参数,则跟踪器104b可能能够仅记录对函数的输入——并且避免记录该函数内的指令的执行。在函数的代码可用的情况下(例如,在应用二进制中,或者在记录时被注入到跟踪中),可以通过为这些函数输入提供适当的机器代码来完全地重现该函数。

值得注意的是,执行仿真优化/快捷方式可能潜在地混淆在仿真期间竞争条件的发生(例如,诸如通过更改竞争条件发生的可能性)。但是,如果在动作405中记录了对仿真的跟踪,则可以从跟踪中标识这些竞争条件(例如,通过分析跟踪以发现对跨线程发生的对相同存储器位置的两次或更多次非同步访问,并且其中一次或多次存储器访问是写入)。

在不背离本发明的精神或基本特征的情况下,本发明可以以其他特定形式来实现。所描述的实施例在所有方面都应当被认为仅是说明性的而非限制性的。因此,本发明的范围由所附权利要求书而不是前面的描述来指示。在权利要求的等同含义和范围内的所有改变都被包含在其范围之内。

相关技术
  • 使用编译器生成的仿真优化元数据改进仿真和跟踪性能
  • 基于改进生成对抗网络的无人作战仿真地图生成方法
技术分类

06120113142914