深入理解C++与LLVM/Clang编译器

C++是一种功能强大的编程语言,得到了广泛的应用和支持,包括来自Microsoft和Apple等大型企业的支持。本文系列将探讨C++的一般特性、不同功能以及其内部机制。将使用llvm/Clang编译器工具链来深入研究这门语言并探索其特性。最好的参考书籍是《Inside the C++ Object Model》,将跟随这本书,从Clang的角度来理解C++。

注意:这不是一个C++初学者教程,也不打算成为初学者教程。可以在网上找到其他书籍或教程进行学习。

LLVM汇编语言简介

在本介绍中,将看到LLVM汇编语言是什么样子的。可以从llvm.org网站的下载链接中安装Clang安装程序。对于LLVM汇编的完整描述,请参考LLVM文档。这里将给出一个简要的介绍,将在需要的时候发现汇编。LLVM汇编是一种类型化的汇编语言。与常规汇编不同,变量代表类型。它被设计为更接近高级语言,并提供更好的优化和安全特性。需要记住的一些事情:

  • LLVM汇编中的注释以分号(;)开始,并一直持续到行尾。
  • 全局标识符以@字符开始。所有函数名和全局变量必须以@开头。
  • LLVM中的局部标识符以百分号(%)开始。标识符的典型正则表达式是[%@][a-zA-Z$._][a-zA-Z$._0-9]*。

LLVM有一个强大的类型系统,这也是其最重要的特性之一。LLVM定义了一个整数类型为iN,其中N是整数将占用的位数。可以指定1到223-1之间的任何位宽。

可以声明一个向量或数组类型为[元素数量 X 每个元素的大小]。对于字符串“Hello World!”,这使得类型为[13 x i8],假设每个字符是1字节,并考虑了1个额外的字节用于NULL字符。

声明一个全局字符串常量为hello-world字符串如下:

@hello = constant [13 x i8] c"Hello World!\00"

使用constant关键字声明一个常量,后跟类型和值。类型已经在前面讨论过,所以让看看值:首先使用c,然后是双引号中的整个字符串,包括\0,并以0结尾。不幸的是,LLVM文档没有提供为什么字符串需要用c前缀声明,并包括一个NULL字符和0在结尾的解释。如果对探索更多的LLVM特性感兴趣,请参阅资源中的语法文件链接。

LLVM函数声明和定义

LLVM允许声明和定义函数。不会详细介绍LLVM函数的所有特性列表,将专注于最基础的部分。从define关键字开始,后跟返回类型,然后是函数名。一个返回32位整数的简单main定义如下:

define i32 @main() { ; some LLVM assembly code that returns i32 }

函数声明,像定义一样,有很多内容。这里有一个最简单的puts方法声明,它是LLVM等同于printf的:

declare i32 puts(i8*)

以declare关键字开始声明,后跟返回类型,函数名,以及函数的可选参数列表。声明必须在全局范围内。

每个函数都以一个返回语句结束。有两种形式的返回语句:ret 或ret void。对于简单main例程,ret i32 0就足够了。

使用call 来调用函数。注意,每个函数参数必须用其类型前置。一个返回6位整数并接受36位整数的函数测试具有以下语法:

call i6 @test( i36 %arg1 )

编译示例

现在知道了足够的知识来开始编译一些示例并进行尝试。在Windows上创建一个名为test.cpp的文件,内容如下:

class A { int _i; }; int main() { A t; return 1; }

现在使用clang.exe和以下命令生成“test.ll”文件,该文件包含llvm汇编。

clang.exe -S -emit-llvm test.cpp

生成的代码如下:

%class.A = type { i32 } ; Function Attrs: nounwind define i32 @main() #0 { entry: %retval = alloca i32, align 4 %t = alloca %class.A, align 4 store i32 0, i32* %retval ret i32 1 }

如所知,所有局部标识符都以%开头,并且可以包含一个“.”。因此,标识符名称是“class.A”。它应该只包含一个整数。注释以“;”开始。main函数以关键字“define”开始。

Clang的AST(抽象语法树)

Clang还包括一个方便的工具来转储C++AST(抽象语法树),可以分析它。这并不总是适用于任何类型的更广泛用途,但可以从小示例中学习一些基本的东西。给出的示例代码中不会使用任何包含或库,以减少AST或IR的复杂性。

要生成AST,可以给出以下命令:

clang.exe -fcolor-diagnostics -Xclang -ast-dump test.cpp

上述语句将给出以下输出:

JavaScript TranslationUnitDecl 0x270dc0 <> |-TypedefDecl 0x2710b0 <> implicit __builtin_va_list ' char *' |-TypedefDecl 0x2711b0 col:16 FunPtrType ' void (*)(void)' |-CXXRecordDecl 0x2711e0 line:2:7 union U definition | |-CXXRecordDecl 0x2712b0 col:7 implicit union U | |-FieldDecl 0x271320 col:5 _i ' int' | |-FieldDecl 0x271360 col:7 _f ' float' | |-FieldDecl 0x2713a0 col:6 _c ' char' | |-FieldDecl 0x2713e0 col:8 _d ' double' | |-FieldDecl 0x271420 col:7 _p ' void *' | |-FieldDecl 0x271470 col:12 _fp ' FunPtrType': ' void (*)(void)' | |-CXXConstructorDecl 0x271900 col:7 implicit used U ' void (void) __attribute__((thiscall))' inline noexcept-unevaluated 0x271900 | | ` -CompoundStmt 0x271ad8 | ` -CXXConstructorDecl 0x2719e0 col:7 implicit U ' void (const union U &) __attribute__((thiscall))' inline noexcept-unevaluated 0x2719e0 | ` -ParmVarDecl 0x271aa0 col:7 'const union U &' ` -FunctionDecl 0x2714e0 line:11:5 main ' int (void)' ` -CompoundStmt 0x271b50 |-DeclStmt 0x2715e8 | ` -VarDecl 0x271580 col:6 sizeInt ' int' | ` -ImplicitCastExpr 0x2715d8 'int' | ` -UnaryExprOrTypeTraitExpr 0x2715c0 ' unsigned int' sizeof ' int' |-DeclStmt 0x271678 | ` -VarDecl 0x271610 col:6 sizefloat 'int' | ` -ImplicitCastExpr 0x271668 ' int' | ` -UnaryExprOrTypeTraitExpr 0x271650 'unsigned int' sizeof 'float' |-DeclStmt 0x271700 | ` -VarDecl 0x2716a0 col:6 sizeChar ' int' | ` -ImplicitCastExpr 0x2716f0 'int' | ` -UnaryExprOrTypeTraitExpr 0x2716d8 ' unsigned int' sizeof ' char' |-DeclStmt 0x271788 | ` -VarDecl 0x271720 col:6 sizeDouble 'int' | ` -ImplicitCastExpr 0x271778 ' int' | ` -UnaryExprOrTypeTraitExpr 0x271760 'unsigned int' sizeof 'double' |-DeclStmt 0x271818 | ` -VarDecl 0x2717b0 col:6 sizeV ' int' | ` -ImplicitCastExpr 0x271808 'int' | ` -UnaryExprOrTypeTraitExpr 0x2717f0 ' unsigned int' sizeof ' void *' |-DeclStmt 0x2718a0 | ` -VarDecl 0x271840 col:6 sizeFP 'int' | ` -ImplicitCastExpr 0x271890 ' int' | ` -UnaryExprOrTypeTraitExpr 0x271878 'unsigned int' sizeof 'FunPtrType':'void (*)(void)' |-DeclStmt 0x271b10 | ` -VarDecl 0x2718c0 col:4 u ' union U' | ` -CXXConstructExpr 0x271ae8 'union U' 'void (void) __attribute__((thiscall))' ` -ReturnStmt 0x271b40 ` -IntegerLiteral 0x271b20 'int' 1
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485