diff --git a/content/1/chapter2/4.tex b/content/1/chapter2/4.tex index cf56a8b..f3bfa8e 100644 --- a/content/1/chapter2/4.tex +++ b/content/1/chapter2/4.tex @@ -160,7 +160,7 @@ endmacro() \end{tcolorbox} -包含该模块,可以使用add\underline{~}tinylang\underline{~}subdirectory()、add\underline{~}tinylang\underline{~}library()、add\underline{~}tinylang\underline{~}executable()和add\underline{~}tinylang\underline{~}tool()函数。这些是LLVM(在AddLLVM模块中)提供的函数包装器。tinylang\underline{~}subdirectory()为构建添加了一个新的源目录。此外,还添加了一个新的CMake选项。使用此选项,用户可以控制是否应该编译目录的内容。使用add\underline{~}tinylang\underline{~}library(),可以定义库并安装。Add \underline{~}tinylang\underline{~}executable()定义了可执行文件,Add \underline{~}tinylang\underline{~}tool()定义了同样安装的可执行文件。\par +包含该模块,可以使用add\underline{~}tinylang\underline{~}subdirectory()、add\underline{~}tinylang\underline{~}library()、add\underline{~}tinylang\underline{~}exe\allowbreak cutable()和add\underline{~}tinylang\underline{~}tool()函数。这些是LLVM(在AddLLVM模块中)提供的函数包装器。tinylang\underline{~}subdirectory()为构建添加了一个新的源目录。此外,还添加了一个新的CMake选项。使用此选项,用户可以控制是否应该编译目录的内容。使用add\underline{~}tinylang\underline{~}library(),可以定义库并安装。Add \underline{~}tinylang\underline{~}executable()定义了可执行文件,Add \underline{~}tinylang\underline{~}tool()定义了同样安装的可执行文件。\par lib目录中,即使没有源代码,也需要CMakeLists.txt文件,必须包含这个项目库的源目录。打开文本编辑器,将以下内容保存在文件中:\par diff --git a/content/2/chapter4/8.tex b/content/2/chapter4/8.tex index 3dde522..059d650 100644 --- a/content/2/chapter4/8.tex +++ b/content/2/chapter4/8.tex @@ -219,11 +219,11 @@ } \end{lstlisting} -DeclList是一个名为std::vector的声明列表,而IdentList是一个类型为std::vector>的位置和标识符列表。\par +DeclList是一个名为std::vector的声明列表,而IdentList是一个类型为std::vector<\allowbreak std::pair>的位置和标识符列表。\par parseQualident()方法返回一个声明。本例中,应该是一个类型声明。\par -解析器类知道语义分析器类Sema的一个实例,该实例存储在Actions成员中。对actOnVariableDeclaration()的调用运行语义分析器和AST构造。实现在lib/Sema/Sema.cpp中:\par +解析器类知道语义分析器类Sema的一个实例,该实例存储在Actions成员中。对actOnVariable\allowbreak Declaration()的调用运行语义分析器和AST构造。实现在lib/Sema/Sema.cpp中:\par \begin{lstlisting}[caption={}] void Sema::actOnVariableDeclaration(DeclList &Decls, diff --git a/content/2/chapter6/4.tex b/content/2/chapter6/4.tex index f61ac95..c5c589f 100644 --- a/content/2/chapter6/4.tex +++ b/content/2/chapter6/4.tex @@ -8,10 +8,10 @@ \begin{tcolorbox}[colback=white,colframe=black] TYPE Shape = RECORD \\ \hspace*{3cm}color: INTEGER; \\ -\hspace*{3cm}PROCEDURE (VAR s: Shape) GetColor(): \ +\hspace*{3cm}PROCEDURE (VAR s: Shape) GetColor(): \\ \hspace*{3.5cm}INTEGER; \\ \hspace*{3cm}PROCEDURE (VAR s: Shape) Area(): REAL;\\ -\hspace*{2.5cm}cmEND; +\hspace*{2.5cm}END; \end{tcolorbox} GetColor方法只返回颜色编号:\par diff --git a/content/2/chapter7/2.tex b/content/2/chapter7/2.tex index 419fe9f..54ee8be 100644 --- a/content/2/chapter7/2.tex +++ b/content/2/chapter7/2.tex @@ -337,7 +337,7 @@ {Ptr}); \end{lstlisting} -\item 通过调用puts()函数来处理异常,将消息打印到控制台。为此,首先通过调用CreateGlobalStringPtr()函数生成一个指向该字符串的指针,然后在生成的调用puts()函数中将该指针作为参数传入: +\item 通过调用puts()函数来处理异常,将消息打印到控制台。为此,首先通过调用CreateGlobalString\allowbreak Ptr()函数生成一个指向该字符串的指针,然后在生成的调用puts()函数中将该指针作为参数传入: \begin{lstlisting}[caption={}] Builder.CreateCall(EndCatchFty, EndCatchFn); Builder.CreateRet(Int32Zero); diff --git a/content/2/chapter7/3.tex b/content/2/chapter7/3.tex index cbd8115..7fef623 100644 --- a/content/2/chapter7/3.tex +++ b/content/2/chapter7/3.tex @@ -219,7 +219,7 @@ \end{lstlisting} \end{enumerate} -要启用TBAA元数据的生成,只需要将元数据附加到生成的加载和存储指令。例如,在CGProcedure::writeVariable()中,对全局变量进行存储,使用存储指令:\par +要启用TBAA元数据的生成,只需要将元数据附加到生成的加载和存储指令。例如,在CGProced\allowbreak ure::writeVariable()中,对全局变量进行存储,使用存储指令:\par \begin{lstlisting}[caption={}] Builder.CreateStore(Val, CGM.getGlobal(D)); diff --git a/content/2/chapter7/4.tex b/content/2/chapter7/4.tex index 2931bc9..be7a493 100644 --- a/content/2/chapter7/4.tex +++ b/content/2/chapter7/4.tex @@ -11,9 +11,9 @@ \begin{itemize} \item lvm::DIFile: 使用文件名和包含该文件的目录的绝对路径来描述一个文件,可以使用createFile()方法来创建。一个文件可以包含主编译单元,也可以包含导入的声明。 -\item llvm::DICompileUnit: 这用于描述当前的编译单元,还需要指定源语言、特定于编译器的生成器字符串、是否启用了优化,当然还有编译单元所在的DIFile。您可以通过调用createCompileUnit()来创建。 +\item llvm::DICompileUnit: 这用于描述当前的编译单元,还需要指定源语言、特定于编译器的生成器字符串、是否启用了优化,当然还有编译单元所在的DIFile。您可以通过调用createCompile\allowbreak Unit()来创建。 -\item llvm::DISubprogram: 描述一个函数。其中重要的信息有:作用域(通常是嵌套函数的DICompileUnit或DISubprogram)、函数名、修饰过的函数名和函数类型。可以通过调用createFunction()来创建。 +\item llvm::DISubprogram: 描述一个函数。其中重要的信息有:作用域(通常是嵌套函数的DIComp\allowbreak ileUnit或DISubprogram)、函数名、修饰过的函数名和函数类型。可以通过调用createFunction()来创建。 \item llvm::DILexicalBlock: 描述了如何在语言中对块作用域进行建模的词汇块。可以通过调用createLexicalBlock()来创建。 @@ -24,7 +24,7 @@ \begin{itemize} \item createBasicType()函数返回一个指向llvm::DIBasicType类的指针,创建元数据来描述基本类型,如tinylang中的INTEGER或C++中的int。除了类型的名称之外,所需参数是以位为单位大小和编码的。例如,有符号类型还是无符号类型。 -\item 有几种方法可以构造复合数据类型的元数据,用llvm::DIComposite类表示。可以使用createArrayType()、createStructType()、createUnionType()和createVectorType()函数实例化数组、结构、联合和向量数据类型的元数据。这些函数需要相应的参数—例如,数组类型的基类型和下标,或者结构类型的字段成员列表。 +\item 有几种方法可以构造复合数据类型的元数据,用llvm::DIComposite类表示。可以使用create\allowbreak ArrayType()、createStructType()、createUnionType()和createVectorType()函数实例化数组、结构、联合和向量数据类型的元数据。这些函数需要相应的参数—例如,数组类型的基类型和下标,或者结构类型的字段成员列表。 \item 还有一些方法支持枚举、模板、类等。 \end{itemize} @@ -202,7 +202,7 @@ 调试器允许程序员逐行调试应用程序。为此,调试器需要知道哪个机器指令属于源代码中的哪一行,LLVM允许在每个指令中添加一个源位置。上一节中,创建了llvm::DILocation类型的位置信息。调试位置包含的信息不仅仅是行、列和范围。如果需要,可以指定这一行内联到的范围。还可以指出此调试位置属于隐式代码,即前端生成的但不在源代码中的代码。\par -将它附加到指令之前,必须将调试位置包装在llvm::DebugLoc对象中。为此,只需将从llvm::DILocation类获得的位置信息传递给llvm::DebugLoc构造函数。通过这种包装,LLVM可以跟踪位置信息。虽然源代码中的位置显然没有改变,但在优化期间可以删除为源代码级语句或表达式生成的机器码。封装有助于处理这些可能的更改。\par +将它附加到指令之前,必须将调试位置包装在llvm::DebugLoc对象中。为此,只需将从llvm::DI\allowbreak Location类获得的位置信息传递给llvm::DebugLoc构造函数。通过这种包装,LLVM可以跟踪位置信息。虽然源代码中的位置显然没有改变,但在优化期间可以删除为源代码级语句或表达式生成的机器码。封装有助于处理这些可能的更改。\par 添加行号信息主要可以归结为从AST检索行号信息并将其添加到生成的指令中。指令类有setDebugLoc()方法,将位置信息附加到指令上。\par @@ -211,7 +211,7 @@ \hspace*{\fill} \par %插入空行 \textbf{为tinylang添加调试支持} -我们将调试元数据的生成封装在新的CGDebugInfo类中。把声明放到tinylang/CodeGen/CGDebugInfo.h头文件中,把定义放到tinylang/CodeGen/CGDebugInfo.cpp文件中。\par +我们将调试元数据的生成封装在新的CGDebugInfo类中。把声明放到tinylang/CodeGen/CG\allowbreak DebugInfo.h头文件中,把定义放到tinylang/CodeGen/CGDebugInfo.cpp文件中。\par CGDebugInfo类有五个重要成员。我们需要引用模块CGM的代码生成器,因为我们需要将类型从AST表示转换为LLVM类型。当然,还需要llvm::DIBuilder类的一个实例,称为DBuilder,如上一节所述。还需要一个指向编译单元实例的指针,我们将它存储在名为CU的成员中。\par diff --git a/content/2/chapter8/3.tex b/content/2/chapter8/3.tex index 2a86be7..908b6d7 100644 --- a/content/2/chapter8/3.tex +++ b/content/2/chapter8/3.tex @@ -86,7 +86,7 @@ 目前为止,已经实现了新Pass的功能。稍后将对out-of-tree Pass重用此实现。对于LLVM树中的解决方案,必须更改LLVM中的几个文件来声明新的Pass:\par \begin{enumerate} -\item 首先,需要将CMakeLists.txt添加到源文件夹。这个文件包含一个新的LLVM库名称LLVMCountIR的构建说明。新库需要链接到LLVM Support组件,因为我们使用了调试和统计基础设施,还需要链接到LLVM Core组件,其中包含LLVM IR的定义: +\item 首先,需要将CMakeLists.txt添加到源文件夹。这个文件包含一个新的LLVM库名称LLVM\allowbreak CountIR的构建说明。新库需要链接到LLVM Support组件,因为我们使用了调试和统计基础设施,还需要链接到LLVM Core组件,其中包含LLVM IR的定义: \begin{tcolorbox}[colback=white,colframe=black] add\underline{~}llvm\underline{~}component\underline{~}library(LLVMCountIR \\ \hspace*{0.5cm}CountIR.cpp \\ @@ -215,7 +215,7 @@ RegisterCB}; } \end{lstlisting} -为每个回调函数实现一个单独的函数有助于理解发生了什么。如果插件提供了几个Pass,那么可以扩展RegisterCB回调函数来注册所有Pass。通常,可以找到一种紧凑的方法。下面的llvmGetPassPluginInfo()函数将前面的PipelineParsingCB()、RegisterCB()和llvmGetPassPluginInfo()组合成一个函数,并通过Lambda函数来实现: +为每个回调函数实现一个单独的函数有助于理解发生了什么。如果插件提供了几个Pass,那么可以扩展RegisterCB回调函数来注册所有Pass。通常,可以找到一种紧凑的方法。下面的llvmGetPassPluginInfo()函数将前面的PipelineParsingCB()、RegisterCB()和llvmGetPass\allowbreak PluginInfo()组合成一个函数,并通过Lambda函数来实现: \begin{lstlisting}[caption={}] extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_ WEAK diff --git a/content/2/chapter8/5.tex b/content/2/chapter8/5.tex index a0333ea..ce1076a 100644 --- a/content/2/chapter8/5.tex +++ b/content/2/chapter8/5.tex @@ -11,7 +11,7 @@ 为了实现功能,我们将tinylang编译器中的tools/driver/Driver.cpp文件进行了扩展:\par \begin{enumerate} -\item 我们使用新的类,因此从添加新的包含文件开始。llvm/Passes/PassBuilder.h文件提供了PassBuilder类的定义。llvm/Passes/PassPlugin.h文件是插件支持所必需的。最后,llvm/Analysis/TargetTransformInfo.h文件提供了一个连接IR级转换和特定目标信息的Pass: +\item 我们使用新的类,因此从添加新的包含文件开始。llvm/Passes/PassBuilder.h文件提供了PassBuilder类的定义。llvm/Passes/PassPlugin.h文件是插件支持所必需的。最后,llvm/An\allowbreak alysis/TargetTransformInfo.h文件提供了一个连接IR级转换和特定目标信息的Pass: \begin{lstlisting}[caption={}] #include "llvm/Passes/PassBuilder.h" #include "llvm/Passes/PassPlugin.h" @@ -251,7 +251,7 @@ 还有其他扩展点。要使用扩展点,需要注册一个回调。在构造Pass流水期间,回调将在定义的扩展点运行,并可以向给定的Pass管理器添加一个Pass。\par -要为流水起始扩展点注册回调,可以调用PassBuilder类的registerPipelineStartEPCallback()方法。例如,将CountIRPass Pass添加到流水的开头,需要通过调用createModuleToFunctionPassAdaptor()模板函数来调整Pass作为模块Pass使用,然后将Pass添加到模块Pass管理器中:\par +要为流水起始扩展点注册回调,可以调用PassBuilder类的registerPipelineStartEPCallback()方法。例如,将CountIRPass Pass添加到流水的开头,需要通过调用createModuleToFunctionPass\allowbreak Adaptor()模板函数来调整Pass作为模块Pass使用,然后将Pass添加到模块Pass管理器中:\par \begin{lstlisting}[caption={}] PB.registerPipelineStartEPCallback( @@ -262,9 +262,9 @@ }); \end{lstlisting} -可以在Pass流水创建之前的任何时刻,也就是调用parsepaspipeline()方法之前,在Pass流水设置代码中添加此代码。\par +可以在Pass流水创建之前的任何时刻,也就是调用parsePassPipeline()方法之前,在Pass流水设置代码中添加此代码。\par -对于上一节中所做的工作,一个非常自然的扩展是让用户在命令行上传递一个扩展点的Pass流水描述(opt工具也允许这样做)。先为流水起始扩展点这样做。首先,将以下代码添加到tools/driver/Driver.cpp文件中:\par +对于上一节中所做的工作,一个非常自然的扩展是让用户在命令行上传递一个扩展点的Pass流水描述(opt工具也允许这样做)。先为流水起始扩展点这样做。首先,将以下代码添加到tools/driver/\allowbreak Driver.cpp文件中:\par \begin{enumerate} \item 为用户添加了一个新的命令行来指定管道描述。同样,从opt工具中获取选项名: @@ -298,7 +298,7 @@ LLVM 12支持-print-changed选项,与之前Pass的结果相比,该选项仅在IR代码发生更改时打印。大大减少的输出,使得跟踪IR转换更加容易。 \end{tcolorbox} -PassBuilder类有一个嵌套的OptimizationLevel类来表示六个不同的优化级别。而不是使用"default"流水描述作为parsepaspipeline()方法的参数,我们也可以调用buildPerModuleDefaultPipeline()方法,为优化级别构建相应的优化管道——除了级别O0。优化级别为O0,表示不执行优化。因此,Pass管理器中不会添加任何Pass。如果我们仍然想运行某个Pass,可以手动将它添加到Pass管理器中。这个级别运行的是一个简单的Pass:AlwaysInliner Pass,它将一个标记有always\underline{~}内联属性的函数内联到调用者中。将优化级别的命令行选项值转换为OptimizationLevel类的相应成员,实现如下: +PassBuilder类有一个嵌套的OptimizationLevel类来表示六个不同的优化级别。而不是使用"default"流水描述作为parsepaspipeline()方法的参数,我们也可以调用buildPer\allowbreak ModuleDefaultPipeline()方法,为优化级别构建相应的优化管道——除了级别O0。优化级别为O0,表示不执行优化。因此,Pass管理器中不会添加任何Pass。如果我们仍然想运行某个Pass,可以手动将它添加到Pass管理器中。这个级别运行的是一个简单的Pass:AlwaysInliner Pass,它将一个标记有always\underline{~}内联属性的函数内联到调用者中。将优化级别的命令行选项值转换为OptimizationLevel类的相应成员,实现如下: \begin{lstlisting}[caption={}] PassBuilder::OptimizationLevel Olevel = …; if (OLevel == PassBuilder::OptimizationLevel::O0) diff --git a/content/3/chapter10/3.tex b/content/3/chapter10/3.tex index 214744a..3530c31 100644 --- a/content/3/chapter10/3.tex +++ b/content/3/chapter10/3.tex @@ -128,7 +128,7 @@ return Err; \end{lstlisting} -\item 与lli工具一样,我们也支持来自C库的符号。DefinitionGenerator类公开符号,DynamicLibrarySearchGenerator子类公开在动态库中找到的名称。这个类提供了两个工厂方法:Load()方法可用于加载动态库,而GetForCurrentProcess()方法公开当前进程的符号。这里,我们使用后一个函数。符号名可以有前缀,这取决于平台。我们检索数据布局并将前缀传递给GetForCurrentprocess()函数。然后以正确的方式处理符号名,我们不需要关心它。像往常一样,如果发生错误,则返回函数: +\item 与lli工具一样,我们也支持来自C库的符号。DefinitionGenerator类公开符号,DynamicLibrary\allowbreak SearchGenerator子类公开在动态库中找到的名称。这个类提供了两个工厂方法:Load()方法可用于加载动态库,而GetForCurrentProcess()方法公开当前进程的符号。这里,我们使用后一个函数。符号名可以有前缀,这取决于平台。我们检索数据布局并将前缀传递给GetForCurrentprocess()函数。然后以正确的方式处理符号名,我们不需要关心它。像往常一样,如果发生错误,则返回函数: \begin{lstlisting}[caption={}] const DataLayout &DL = (*JIT)->getDataLayout(); auto DLSG = orc::DynamicLibrarySearchGenerator:: @@ -272,7 +272,7 @@ project ("jit") \end{tcolorbox} -\item 需要加载LLVM包,将LLVM提供的CMake模块目录添加到搜索路径中。然后包含ChooseMSVCCRT模块,确保与LLVM使用的C运行时相同: +\item 需要加载LLVM包,将LLVM提供的CMake模块目录添加到搜索路径中。然后包含Choose\allowbreak MSVCCRT模块,确保与LLVM使用的C运行时相同: \begin{tcolorbox}[colback=white,colframe=black] find\underline{~}package(LLVM REQUIRED CONFIG) \\ list(APPEND CMAKE\underline{~}MODULE\underline{~}PATH \$\{LLVM\underline{~}DIR\}) \\ @@ -320,7 +320,7 @@ 使用ORC的分层方法,可以很容易地构建一个定制的JIT编译器。没有适合所有人的JIT编译器,本章的第一节给出了一些示例。让我们看看如何设置JIT编译器。\par -ORC API将这些层是堆叠在一起的。最低的一层是对象链接层,由llvm::orc::RTDyldObjectLinkingLayer类表示,负责链接内存中的对象并将它们转换为可执行代码。此任务所需的内存由MemoryManager接口的一个实例管理,有一个默认实现,如果需要,也可以使用自定义版本。\par +ORC API将这些层是堆叠在一起的。最低的一层是对象链接层,由llvm::orc::RTDyldObject\allowbreak LinkingLayer类表示,负责链接内存中的对象并将它们转换为可执行代码。此任务所需的内存由MemoryManager接口的一个实例管理,有一个默认实现,如果需要,也可以使用自定义版本。\par 在对象链接层之上是编译层,它负责创建内存中的对象文件。llvm::orc::IRCompileLayer类将一个IR模块作为输入,并将其编译为对象文件。IRCompileLayer类是IRLayer类的子类,IRLayer类是一个通用类,用于接受LLVM IR的层实现。\par @@ -354,7 +354,7 @@ #define JIT_H \end{lstlisting} -\item 需要一堆包含文件。它们中的大多数都提供了与头文件同名的类。Core.h头文件提供了两个基本类,包括ExecutionSession类。ExecutionUtils.h头文件提供了DynamicLibrarySearchGenerator类来搜索库中的符号,我们已经在使用LLJIT实现我们自己的JIT编译器部分使用过。CompileUtils.h头文件提供了ConcurrentIRCompiler类: +\item 需要一堆包含文件。它们中的大多数都提供了与头文件同名的类。Core.h头文件提供了两个基本类,包括ExecutionSession类。ExecutionUtils.h头文件提供了DynamicLibrarySearch\allowbreak Generator类来搜索库中的符号,我们已经在使用LLJIT实现我们自己的JIT编译器部分使用过。CompileUtils.h头文件提供了ConcurrentIRCompiler类: \begin{lstlisting}[caption={}] #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/ExecutionEngine/JITSymbol.h" @@ -377,7 +377,7 @@ class JIT { \end{lstlisting} -\item 私有数据成员为ORC层和一个助手类。ExecutionSession、ObjectLinkingLayer、CompileLayer、OptIRLayer和MainJITDylib实例表示正在运行的JIT程序、各层和符号表。TargetProcessControl实例用于与JIT目标流程进行交互,这可以是相同的进程,也可以是同一台机器上的另一个进程,或者是不同机器上的远程进程(可能具有不同的体系结构)。DataLayout和MangleAndInterner类需要以正确的方式篡改符号名称。符号名是内化的,这意味着所有相等的名称都有相同的地址。为了检查两个符号名是否相等,比较地址就足够了,这是非常快速的操作: +\item 私有数据成员为ORC层和一个助手类。ExecutionSession、ObjectLinkingLayer、CompileLayer、OptIRLayer和MainJITDylib实例表示正在运行的JIT程序、各层和符号表。TargetProcess\allowbreak Control实例用于与JIT目标流程进行交互,这可以是相同的进程,也可以是同一台机器上的另一个进程,或者是不同机器上的远程进程(可能具有不同的体系结构)。DataLayout和MangleAnd\allowbreak Interner类需要以正确的方式篡改符号名称。符号名是内化的,这意味着所有相等的名称都有相同的地址。为了检查两个符号名是否相等,比较地址就足够了,这是非常快速的操作: \begin{lstlisting}[caption={}] std::unique_ptr TPC; @@ -452,7 +452,7 @@ } \end{lstlisting} -\item 为了创建对象链接层,需要提供一个内存管理器。这里我们坚持使用默认的SectionMemoryManager类,但如果需要,也可以提供不同的实现: +\item 为了创建对象链接层,需要提供一个内存管理器。这里我们坚持使用默认的SectionMemory\allowbreak Manager类,但如果需要,也可以提供不同的实现: \begin{lstlisting}[caption={}] static std::unique_ptr< llvm::orc::RTDyldObjectLinkingLayer> @@ -587,7 +587,7 @@ 我们新的JIT编译器类的接口,类似于(用LLJIT实现我们自己的JIT编译器部分中使用的)LLJIT类。为了测试我们的实现,可以从上一节复制LIT.cpp中的类,并进行以下更改:\par \begin{enumerate} -\item 为了能够使用我们的新类,我们包含了JIT.h头文件。这将替换llvm/ExecutionEngine/Orc/LLJIT.h头文件,因为这里不再使用LLJIT类,所以不再需要这个头文件。 +\item 为了能够使用我们的新类,我们包含了JIT.h头文件。这将替换llvm/ExecutionEngine/\allowbreak Orc/LLJIT.h头文件,因为这里不再使用LLJIT类,所以不再需要这个头文件。 \item 在jitmain()函数中,用一个新的JIT::create()方法来替换对orc::LLJITBuilder().create()的调用。 diff --git a/content/3/chapter11/2.tex b/content/3/chapter11/2.tex index 1d879bb..e4083ca 100644 --- a/content/3/chapter11/2.tex +++ b/content/3/chapter11/2.tex @@ -6,7 +6,7 @@ 可以使用地址sanitizer来检测应用程序中的两个内存访问错误。这包括一些常见的错误,比如:在释放动态分配的内存后使用它,或者在已分配内存的边界之外写入动态分配的内存。\par -当启用地址sanitizer时,地址sanitizer将用它自己的版本替换对malloc()和free()函数的调用,并使用检查保护程序检测所有内存访问。当然,这给应用程序增加了很多开销,您将只在应用程序的测试阶段使用地址消毒剂。如果对实现细节感兴趣,可以在llvm/lib/Transforms/Instrumentation/AddressSanitzer.cpp文件中找到Pass源,并在\url{https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm}中找到算法描述。\par +当启用地址sanitizer时,地址sanitizer将用它自己的版本替换对malloc()和free()函数的调用,并使用检查保护程序检测所有内存访问。当然,这给应用程序增加了很多开销,您将只在应用程序的测试阶段使用地址消毒剂。如果对实现细节感兴趣,可以在llvm/lib/Transforms/Instrumentation/\allowbreak AddressSanitzer.cpp文件中找到Pass源,并在\url{https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm}中找到算法描述。\par 让我们运行一个简短的示例来演示地址sanitizer的功能。下面的示例应用程序outfbounds.c分配了12字节的内存,但初始化了14字节:\par @@ -117,7 +117,7 @@ \hspace*{\fill} \par %插入空行 \textbf{用线程sanitizer指出数据竞争} -为了充分利用现代CPU的功能,应用程序现在使用多线程。这是一项强大的技术,但它也引入了新的错误来源。多线程应用程序中一个常见的问题是,对全局数据的访问没有保护,例如:没有使用互斥锁或信号量。这样的问题称为数据竞争。线程sanitizer可以检测基于pthread的应用程序和使用LLVM libc++实现的应用程序中的数据竞争。可以在llvm/lib/Transforms/Instrumentation/ThreadSanitize.cpp文件中找到实现。\par +为了充分利用现代CPU的功能,应用程序现在使用多线程。这是一项强大的技术,但它也引入了新的错误来源。多线程应用程序中一个常见的问题是,对全局数据的访问没有保护,例如:没有使用互斥锁或信号量。这样的问题称为数据竞争。线程sanitizer可以检测基于pthread的应用程序和使用LLVM libc++实现的应用程序中的数据竞争。可以在llvm/lib/Transforms/Instrumentation/\allowbreak ThreadSanitize.cpp文件中找到实现。\par 为了演示线程sanitizer的功能,我们将创建一个简单的生产者/消费者的应用程序。生产者线程增加一个全局变量,而消费者线程减少同一个变量。对全局变量的访问不受保护,因此这显然是一场数据竞争。在thread.c文件中保存以下源代码:\par diff --git a/content/3/chapter11/3.tex b/content/3/chapter11/3.tex index 7958cbb..03ee1fd 100644 --- a/content/3/chapter11/3.tex +++ b/content/3/chapter11/3.tex @@ -27,7 +27,7 @@ 代码中,count()函数计算Data变量所指向的内存中的位数。只检查数据的大小以确定是否有可用的字节。在while循环中,不检查数据长度。\par -与普通C字符串一起使用时,不会出现错误,因为C字符串总是以0字节结束。LLVMFuzzerTestOneInput()函数就是fuzz目标,它是libFuzzer调用的函数。调用我们想要测试的函数并返回0,这是目前唯一允许的值。\par +与普通C字符串一起使用时,不会出现错误,因为C字符串总是以0字节结束。LLVMFuzzer\allowbreak TestOneInput()函数就是fuzz目标,它是libFuzzer调用的函数。调用我们想要测试的函数并返回0,这是目前唯一允许的值。\par 要使用libFuzzer编译文件,需要添加-fsanitize=fuzzer选项。建议还启用地址sanitizer和调试符号的生成:\par diff --git a/content/3/chapter11/4.tex b/content/3/chapter11/4.tex index 87f50ad..9fb0c4c 100644 --- a/content/3/chapter11/4.tex +++ b/content/3/chapter11/4.tex @@ -19,7 +19,7 @@ } \end{lstlisting} -要在编译期间启用XRay检测,需要指定-fxray-instrument选项,不测试小于200条指令的函数。这是一个由开发人员定义的任意阈值,在例子中,函数不会被检测。该阈值可以通过-fxrayinstruction-threshold= 指定,或者可以添加一个function属性来控制函数是否应该检测,例如:添加以下修饰会让函数始终执行检测:\par +要在编译期间启用XRay检测,需要指定-fxray-instrument选项,不测试小于200条指令的函数。这是一个由开发人员定义的任意阈值,在例子中,函数不会被检测。该阈值可以通过-fxrayin\allowbreak struction-threshold= 指定,或者可以添加一个function属性来控制函数是否应该检测,例如:添加以下修饰会让函数始终执行检测:\par \begin{lstlisting}[caption={}] void func1() __attribute__((xray_always_instrument)); diff --git a/content/3/chapter11/5.tex b/content/3/chapter11/5.tex index 8b28fc2..0fdd495 100644 --- a/content/3/chapter11/5.tex +++ b/content/3/chapter11/5.tex @@ -34,7 +34,7 @@ \hspace*{\fill} \par %插入空行 \begin{center} -\includegraphics[width=1\textwidth]{content/3/chapter11/images/3.jpg}\\ +\includegraphics[width=0.6\textwidth]{content/3/chapter11/images/3.jpg}\\ 图11.3 – 摘要页面 \end{center} @@ -45,7 +45,7 @@ \hspace*{\fill} \par %插入空行 \begin{center} -\includegraphics[width=0.8\textwidth]{content/3/chapter11/images/4.jpg}\\ +\includegraphics[width=0.6\textwidth]{content/3/chapter11/images/4.jpg}\\ 图11.4 – 详细报告 \end{center} @@ -274,7 +274,7 @@ \end{enumerate} -这就完成了新检查器的实现。只需要将文件名添加到clang/lib/StaticAnalyzer/Checkers/CmakeLists.txt文件中的源文件名列表中:\par +这就完成了新检查器的实现。只需要将文件名添加到clang/lib/StaticAnalyzer/Checkers/\par CmakeLists.txt文件中的源文件名列表中:\par \begin{tcolorbox}[colback=white,colframe=black] add\underline{~}clang\underline{~}library(clangStaticAnalyzerCheckers \\ diff --git a/content/3/chapter11/6.tex b/content/3/chapter11/6.tex index 20af58a..bda5e3a 100644 --- a/content/3/chapter11/6.tex +++ b/content/3/chapter11/6.tex @@ -170,7 +170,7 @@ endif() \end{tcolorbox} -\item 将这两个文件保存在NamingPlugin目录中。创建一个与NamingPlugin目录同级的build-NamingPlugin目录,并使用以下命令构建插件: +\item 将这两个文件保存在NamingPlugin目录中。创建一个与NamingPlugin目录同级的\par build-NamingPlugin目录,并使用以下命令构建插件: \begin{tcolorbox}[colback=white,colframe=black] \$ mkdir build-naming-plugin \\ \$ cd build-naming-plugin \\ @@ -260,7 +260,7 @@ : Visitor(std::make_unique(CI)) {} \end{lstlisting} -\item 您将删除HandleTopLevelDecl()方法,因为该功能现在在访问者类中,所以需要重写HandleTranslationUnit()方法。每个翻译单元调用一次,会在这里开始AST遍历: +\item 您将删除HandleTopLevelDecl()方法,因为该功能现在在访问者类中,所以需要重写Handle\allowbreak TranslationUnit()方法。每个翻译单元调用一次,会在这里开始AST遍历: \begin{lstlisting}[caption={}] void HandleTranslationUnit(ASTContext &ASTCtx) override { diff --git a/content/3/chapter12/9.tex b/content/3/chapter12/9.tex index 13aa3af..4bcab17 100644 --- a/content/3/chapter12/9.tex +++ b/content/3/chapter12/9.tex @@ -1,6 +1,6 @@ 目标描述中指令的定义允许构造解码器表,解码器表用于将目标文件反汇编成文本汇编程序。解码器表和解码器函数是由llvm-tblgen工具生成的。除了生成的代码外,我们只需要提供注册和初始化M88kDisassembler类的代码,以及一些解码寄存器和操作数的帮助函数。\par -我们将实现放在Disassembler/M88kDisassembler.cpp文件中。M88kDisassembler类的getInstruction()方法执行解码工作。它接受一个字节数组作为输入,并将下一条指令解码到MCInst类的实例中。类声明如下:\par +我们将实现放在Disassembler/M88kDisassembler.cpp文件中。M88kDisassembler类的get\allowbreak Instruction()方法执行解码工作。它接受一个字节数组作为输入,并将下一条指令解码到MCInst类的实例中。类声明如下:\par \begin{lstlisting}[caption={}] using DecodeStatus = MCDisassembler::DecodeStatus; @@ -45,7 +45,7 @@ } \end{lstlisting} -当LLVM核心库初始化时,会调用initializealldisassemers()函数或InitializeNativeTargetDisassembler()函数。\par +当LLVM核心库初始化时,会调用initializealldisassemers()函数或InitializeNativeTarget\allowbreak Disassembler()函数。\par 生成的解码器函数需要助手函数来解码寄存器和操作数,这些元素的编码通常涉及目标描述中没有表示的特殊情况,例如:两个指令之间的距离总是偶数,所以最小的位可以忽略,因为它总是零。\par diff --git a/content/3/chapter9/2.tex b/content/3/chapter9/2.tex index c91c267..141c791 100644 --- a/content/3/chapter9/2.tex +++ b/content/3/chapter9/2.tex @@ -24,7 +24,7 @@ \} \end{tcolorbox} -将此代码保存为sum.ll,使用llc(LLVM静态编译器)编译MIPS架构。该工具将LLVM IR编译为汇编文本或目标文件。可以在命令行中用-mtriple选项覆盖编译的目标平台。使用-debug-pass=Structure选项调用llc工具:\par +将此代码保存为sum.ll,使用llc(LLVM静态编译器)编译MIPS架构。该工具将LLVM IR编译为汇编文本或目标文件。可以在命令行中用-mtriple选项覆盖编译的目标平台。使用-debug-pass=\allowbreak Structure选项调用llc工具:\par \begin{tcolorbox}[colback=white,colframe=black] \$ llc -mtriple=mips-linux-gnu -debug-pass=Structure < sum.ll diff --git a/content/3/chapter9/4.tex b/content/3/chapter9/4.tex index 7ddd93e..be79d18 100644 --- a/content/3/chapter9/4.tex +++ b/content/3/chapter9/4.tex @@ -50,7 +50,7 @@ LLVM中的TableGen库可以解析用TableGen语言编写的文件,并创建记录的内存表示。您可以使用这个库创建自己的生成器。\par -LLVM自带了自己的生成工具LLVM-tblgen和一些.td文件。后端目标描述包括llvm/target/target.td文件。该文件定义诸如Register、Target或Processor等类。llvm-tblgen工具了解这些类,并从定义的记录中生成C++代码。\par +LLVM自带了自己的生成工具LLVM-tblgen和一些.td文件。后端目标描述包括llvm/target/\allowbreak target.td文件。该文件定义诸如Register、Target或Processor等类。llvm-tblgen工具了解这些类,并从定义的记录中生成C++代码。\par 以MIPS后端为例来看看。目标描述在Mips.td中,文件位于llvm/lib/Target/Mips文件夹,该文件包含Target.td文件。它还定义了目标特性,例如:\par diff --git a/content/3/chapter9/5.tex b/content/3/chapter9/5.tex index 94af304..b1d4131 100644 --- a/content/3/chapter9/5.tex +++ b/content/3/chapter9/5.tex @@ -15,7 +15,7 @@ 我们首先添加的CPU特性称为sqsum。这将允许使用\verb|--|mattr=+sqsum选项调用llc,以启用对新指令进行识别。\par -我们将添加的大部分代码都在描述MIPS后端的TableGen文件中。所有文件都位于llvm/lib/Target/Mips文件夹中。顶层文件为Mips.td,查看该文件并找到定义各种特性的部分。这里添加了我们新特性的定义:\par +我们将添加的大部分代码都在描述MIPS后端的TableGen文件中。所有文件都位于llvm/lib/\allowbreak Target/Mips文件夹中。顶层文件为Mips.td,查看该文件并找到定义各种特性的部分。这里添加了我们新特性的定义:\par \begin{tcolorbox}[colback=white,colframe=black] def FeatureSQSum \\