LLVM基础中间代码概念概述

发布时间 2023-10-30 04:15:47作者: 吴建明wujianming

LLVM基础中间代码概念概述

Identifiers 标识符 @ 全局 % 局部 后接字符串 命名量 @name %name 无符号数字 未命名量 @42 %42 类型系统 void 空类型 <type> *

  1. Identifiers 标识符
  2.  
  3.     @ 全局
  4.     % 局部
  5.  
  6.     后接字符串      命名量      @name %name
  7.         无符号数字  未命名量    @42   %42
  8.  
  9.  

10. 类型系统

  1. 11.  
  2. 12.     void                                空类型
  3. 13.     <type> *                            指针类型
  4. 14.     <returntype> (<parameter list>)     函数类型
  5. 15.     < <# elements> x <elementtype> >    向量类型
  6. 16.     [<# elements> x <elementtype>]      数组类型
  7. 17.     { <type list> }                     普通结构体类型
  8. 18.     <{ <type list> }>                   打包结构体类型
  9. 19.     Metadata                            元数据类型
  10. 20.     label                               标签类型
  11. 21.     token                               词元类
  12. 22.  

23. 类型系统(例子)

  1. 24.  
  2. 25.     void                   空
  3. 26.     i32 *                  指针
  4. 27.     i32 (i32)              函数
  5. 28.     <5 x i32>              向量
  6. 29.     [5 x i32]              数组
  7. 30.     { i32,i32,i32 }      普通结构体
  8. 31.     <{ i32,i32 }>    打包结构体
  9. 32.  

33. 元数据

  1. 34.  
  2. 35.     ; 未命名元数据节点
  3. 36.     ; 用于被命名元数据引用
  4. 37.     !0 = !{!"zero"}
  5. 38.     !1 = !{!"one"}
  6. 39.     !2 = !{!"two"}
  7. 40.  
  8. 41.     ; 命名元数据
  9. 42.     !name = !{!0,!1,!2}
  10. 43.  
  11. 44.     !name --- !0
  12. 45.           |-- !1
  13. 46.           |-- !2
  14. 47.  

48. 模块层次内联汇编

  1. 49.  
  2. 50.     module asm "内联汇编代码"
  3. 51.  

52. Target Triple

  1. 53.  
  2. 54.     target triple = "x86_64-amd64-freebsd"
  3. 55.  

56. First Class Types 第一类型

  1. 57.     Single Value Types 单值类型
  2. 58.         只在寄存器里头有效
  3. 59.  
  4. 60.         Integer Type 整数类型
  5. 61.  
  6. 62.             iN ;N为比特数 (通用描述)
  7. 63.  
  8. 64.             i1 一个比特整数
  9. 65.             i32 32为整数
  10. 66.  
  11. 67.         FloaTing-Point Types 浮点类型
  12. 68.  
  13. 69.             half      - 16位浮点值
  14. 70.             float     - 32位浮点值
  15. 71.             double    - 64位浮点值
  16. 72.             fp128     - 128位浮点值
  17. 73.             x86_fp80  - 80位浮点值
  18. 74.             ppc_fp128 - 128位浮点值
  19. 75.  

76. 模块结构

  1. 77.     程序由模块组成,每个模块都是输入程序的翻译单元。
  2. 78.     Hello,world 模块
  3. 79.  
  4. 80.     ; 定义字符串常量作为全局常量
  5. 81.     @.str = private unnamed_addr constant [13 x i8] c"Hello world\0A\00"
  6. 82.  
  7. 83.     ; 外部声明的 puts 函数
  8. 84.     declare i32 @puts(i8* nocapturE) nounwind
  9. 85.  
  10. 86.     ; main 函数的定义
  11. 87.     define i32 @main() {   ; i32()*
  12. 88.       ; [13 x i8] 转换到 i8...
  13. 89.       %cast210 = getelementptr [13 x i8],[13 x i8]* @.str,i64 0,i64 0
  14. 90.  
  15. 91.       ; 调用 puts 函数将字符串写入 stdout
  16. 92.       call i32 @puts(i8* %cast210)
  17. 93.       ret i32 0
  18. 94.     }
  19. 95.  
  20. 96.     ; 命名元信息
  21. 97.     !0 = !{i32 42,null,!"String"}
  22. 98.     !foo = !{!0}
  23. 99.  
  24. 指令参
  25. =========
  26.  
  27. Terminator instructions
  28.     指示当前块完成后,执行哪个块。
  29.     终结指令典型的产生一个 void 值:他们影响控制流,而不是值。(invoke指令是一个例外)
  30.  
  31.     ret            返回
  32.     br             二元条件分支/无条件转移
  33.     switch         多条件分支
  34.     indirectbr   
  35.     invoke         普通/带异常调用
  36.     resume         抛出异常?
  37.     catchswitch    捕获异常
  38.     catchret  
  39.     cleanupret  
  40.     unreachable    不可到达(无语义)
  41.  
  42. Binary Operations
  43.  
  44.     add    加
  45.     fadd   浮点加
  46.     sub    减
  47.     fsub   浮点减
  48.     mul    乘
  49.     fmul   浮点乘
  50.     udiv   无符号整数除
  51.     sdib   带符号整数除
  52.     fdiv   浮点除
  53.     urem   无符号整数求余
  54.     srem   带符号整数求余
  55.     frem   浮点数求余
  56.  
  57.     运算\类型    无符号整数   带符号整数   浮点数
  58.         +               add             fadd
  59.         -               sub             fsub
  60.         *               mul             fmul
  61.         /       udiv        sdiv        fdiv
  62.         %       urem        srem        frem
  63.  
  64. Bitwise Binary Operations
  65.  
  66.     shl    左移
  67.     lshr   逻辑右移
  68.     ashr   算数右移
  69.     and    与
  70.     or     或
  71.     xor    异或
  72.  
  73. Vector Operations
  74.  
  75.     extractelement 取出元素
  76.     insertelement  插入元素
  77.     shufflevector
  78.  
  79. Aggregate Operations
  80.  
  81.     extractvalue  取出值
  82.     insertvalue   插入值
  83.  
  84. Memory Access and Addressing Operations
  85.  
  86.     alloca           分配内存
  87.     load             从内存加载
  88.     store            储存到内存
  89.     fence
  90.     cmpxchg
  91.     atomicrmw        自动修改内存
  92.     getelementptr    获取 aggregate(集合) 数据结构的子成员地址
  93.  
  94. Coversion Operations
  95.  
  96.     这个类型为转换指令(强制类型转换|铸造casTing)
  97.     都取一个单一运算对象和一个类型。
  98.     对运算对象提供一系列位转换。
  99.  
  100.     trunc .. to            截断转换
  101.     zext .. to             零扩展转换
  102.     sext .. to             符号位扩展转换
  103.     fptrunc .. to          浮点截断转换
  104.     fpext .. to            浮点扩展
  105.     fptoui .. to           浮点转无符号整数
  106.     fptosi .. to           浮点转带符号整数
  107.     uitofp .. to           无符号整数转浮点
  108.     sitofp .. to           带符号整数转浮点
  109.     ptrtoint .. to         指针转整数
  110.     inttoptr .. to         整数转指针
  111.     bitcast .. to          位模式转换(重新解释,不改变任何二进制位)
  112.     addrepacecast .. to    地址空间转换
  113.     
  114. Other Operations
  115.  
  116.     icmp          整数比较
  117.     fcmp          浮点数比较
  118.     phi           φ 节点
  119.     SELEct        条件值选择
  120.     call          简单函数调用
  121.     va_arg        可变参数
  122.     landingpad
  123.     catchpad
  124.     cleanuPPAD
  • Objective-C在变为机器代码之前,会被LLVM编译器转换为中间代码(Intermediate Representation)这种中间代码是跨平台通用性代码;
  • 可以使用以下命令行指令生成中间代码
    clang -emit-llvm -S 文件名

语法简介

  • @ - 全局变量
  • % - 局部变量
  • alloca - 在当前执行的函数的堆栈帧中分配内存,当该函数返回到其调用者时,将自动释放内存
  • i32 - 32位4字节的整数
  • align - 对齐
  • load - 读出,store - 写入
    -icmp - 两个整数值比较,返回布尔值
  • lable - 代码标签
  • br - 选择分支,根据条件来转向label,不根据条件跳转的话类似goto
  • call - 调用函数

getelementptr指令就是get the address of 结构体的子变量,只进行地址加计算,不涉及内存获取。

llvm没有取地址的操作

getelementptr是在对一个地址进行加操作

load与store是llvm中两个对地址与数据进行操作的指令

load是对一个地址进行取数据,store是对一个地址进行存数据。

struct munger_struct {

  int f1;

  int f2;

};

void munge(struct munger_struct *P) {

  P[0].f1 = P[1].f1 + P[2].f2;

}

...

munger_struct Array[3];

...

munge(Array);

对应IR

 

void %munge(%struct.munger_struct* %P) {

entry:

  %tmp = getelementptr %struct.munger_struct* %P, i32 1, i32 0

  %tmp = load i32* %tmp

  %tmp6 = getelementptr %struct.munger_struct* %P, i32 2, i32 1

  %tmp7 = load i32* %tmp6

  %tmp8 = add i32 %tmp7, %tmp

  %tmp9 = getelementptr %struct.munger_struct* %P, i32 0, i32 0

  store i32 %tmp8, i32* %tmp9

  ret void

}

GetElementPtr指令其实是一条指针计算语句,本身并不进行任何数据的访问或修改,进行是计算指针,修改计算后指针的类型。

GetElementPtr至少有两个参数,第一个参数为要进行计算的原始指针,往往是一个结构体指针,或数组首地址指针。

第二个参数及以后的参数,都称为indices,表示要进行计算的参数,如结构体的第几个元素,数组的第几个元素。

P[0].f1

    %tmp9 = getelementptr %struct.munger_struct* %P, i32 0, i32 0

P[1].f1

P[2].f2

  %tmp = getelementptr %struct.munger_struct* %P, i32 1, i32 0

  %tmp6 = getelementptr %struct.munger_struct* %P, i32 2, i32 1

仅有数组计算

如果仅有数组指针计算,那么就简单了许多,数组指针的移动只需要一个参数即可。

但如果是仅有结构体指针,那么还是必须两个参数才行

3d_rendering case:

Triangle_3D triangle_3ds[3192]

 

; <label>:11:                                     ; preds = %4, %822

  %12 = phi i64 [ 0, %4 ], [ %823, %822 ]

  %13 = getelementptr inbounds %struct.Triangle_3D, %struct.Triangle_3D* %0, i64 %12

  %14 = bitcast %struct.Triangle_3D* %13 to i64*

  %15 = load i64, i64* %14, align 1

  %16 = getelementptr inbounds %struct.Triangle_3D, %struct.Triangle_3D* %0, i64 %12, i32 8

  %17 = load i8, i8* %16, align 1