LLVM 的中间代码(IR) 基本语法

LLVM 的中间代码(IR) 基本语法

以下是 LLVM IR 的基本语法和每个语法的实例代码:

1.数据类型

LLVM IR 支持多种数据类型,包括整型、浮点型、指针型和向量型等。以下是 LLVM IR 中定义不同类型的语法和示例代码:

  • 整型:i1、i8、i16、i32、i64 等,表示不同位数的整数。
; 定义一个 32 位整型变量
%var = alloca i32
  • 浮点型:float、double 等,表示不同位数的浮点数。
; 定义一个 64 位浮点型变量
%var = alloca double
  • 指针型:用于表示内存地址,例如 void*、int* 等。
; 定义一个指向整型的指针变量
%ptr = alloca i32*
  • 向量型:用于表示一组相同类型的值,例如 <4 x i32> 表示包含 4 个 32 位整数的向量。
; 定义一个包含 4 个 32 位整数的向量变量
%vec = alloca <4 x i32>

2.常量

LLVM IR 支持多种常量,包括整型常量、浮点型常量、指针常量和向量常量等。以下是 LLVM IR 中定义不同类型常量的语法和示例代码:

  • 整型常量:用于表示整数值。
; 定义一个整型常量
%i32 = i32 42
  • 浮点型常量:用于表示浮点数值。
; 定义一个浮点型常量
%f64 = double 3.14
  • 指针常量:用于表示内存地址。
; 定义一个指向 i32 类型的指针常量
%i32ptr = i32* inttoptr (i64 0 to i32*)
  • 向量常量:用于表示一组相同类型的值。
; 定义一个包含 4 个 32 位整数的向量常量
%vec = <4 x i32> <i32 1, i32 2, i32 3, i32 4>

3.函数

LLVM IR 中的函数包含函数名称、函数参数、函数返回类型和函数体等。函数体由多个基本块组成。以下是 LLVM IR 中定义函数的语法和示例代码:

; 定义一个名为 'foo' 的函数,参数为 i32 类型,返回值类型为 i32
define i32 @foo(i32 %arg) {
entry:
  ; 函数体由基本块组成,entry 是函数入口基本块
  %tmp = add i32 %arg, 1
  ret i32 %tmp
}

4.指令

LLVM IR 中的指令用于执行具体的操作,例如算术运算、逻辑运算、比较、分支等。以下是 LLVM IR 中定义不同类型指令的语法和示例代码:

  • 算术运算指令:用于执行加、减、乘、除等算术运算。
; 定义一个加法指令,将 i32 类型的变量 %a 和 %b 相加
%sum = add i32 %a, %b
  • 逻辑运算指令:用于执行与、或、非等逻辑运算。
; 定义一个逻辑与指令,将 i1 类型的变量 %a 和 %b 做逻辑与操作
%result = and i1 %a, %b
  • 比较指令:用于执行比较操作,例如等于、不等于、大于等。
; 定义一个比较指令,比较 i32 类型的变量 %a 和 %b 是否相等
%eq = icmp eq i32 %a, %b
  • 分支指令:用于实现条件分支。
; 定义一个分支指令,如果 %condition 为真,则跳转到 label1,否则跳转到 label2
br i1 %condition, label %label1, label %label2
  • 加载/存储指令:用于实现内存读写操作。
; 定义一个存储指令,将 i32 类型的变量 %value 存储到指针变量 %ptr 指向的内存地址中
store i32 %value, i32* %ptr

; 定义一个加载指令,将指针变量 %ptr 指向的内存地址中的值读取出来,并存储到 i32 类型的变量 %value 中
%value = load i32, i32* %ptr

5. 全局变量

LLVM IR 中的全局变量是在函数外定义的变量,可以在整个程序中访问。以下是 LLVM IR 中定义全局变量的语法和示例代码:

; 定义一个名为 'global_var' 的全局变量,类型为 i32,初始值为 0
@global_var = global i32 0

; 访问全局变量
%value = load i32, i32* @global_var

6.注释

LLVM IR 中的注释以分号(;)开头,可以在代码中添加注释来解释代码的含义。以下是 LLVM IR 中添加注释的示例代码:

; 这是一条注释,用于解释下面的代码含义
%sum = add i32 %a, %b ; 这也是一条注释

7.函数定义和调用

在 LLVM IR 中定义函数和调用函数都非常简单。以下是定义函数和调用函数的语法和示例代码:

; 定义一个名为 'add' 的函数,返回类型为 i32,参数为 i32 类型的变量 %a 和 %b
define i32 @add(i32 %a, i32 %b) {
  ; 函数体
  %sum = add i32 %a, %b
  ret i32 %sum
}

; 调用函数 'add',将参数 %x 和 %y 传递给函数,并将返回值存储到变量 %result 中
%x = 10
%y = 20
%result = call i32 @add(i32 %x, i32 %y)

8.强制类型转换

在 LLVM IR 中,可以使用强制类型转换来将一个类型转换为另一个类型。以下是强制类型转换的语法和示例代码:

; 定义一个 i32 类型的变量 %a
%a = i32 10

; 将变量 %a 转换为 i64 类型,并存储到变量 %b 中
%b = sext i32 %a to i64

9.组合类型

在 LLVM IR 中,可以使用组合类型来表示复杂的数据结构。以下是 LLVM IR 中定义组合类型的语法和示例代码:

; 定义一个结构体类型,包含两个字段:i32 类型的变量 %a 和 i64 类型的变量 %b
%my_struct = type { i32, i64 }

; 定义一个名为 'my_var' 的全局变量,类型为结构体类型 %my_struct,初始值为 { 10, 20 }
@my_var = global %my_struct { i32 10, i64 20 }

10.获取地址

在 LLVM IR 中,可以使用 & 操作符获取变量的地址。以下是获取变量地址的语法和示例代码:

; 定义一个名为 'my_var' 的全局变量,类型为 i32,初始值为 10
@my_var = global i32 10

; 获取变量 'my_var' 的地址,并存储到变量 %ptr 中
%ptr = getelementptr i32, i32* @my_var, i32 0, i32 0

11.定义别名

在 LLVM IR 中,可以使用别名来引用其他变量或函数。以下是定义别名的语法和示例代码:

; 定义一个名为 'my_var' 的全局变量,类型为 i32,初始值为 10
@my_var = global i32 10

; 定义一个名为 'my_alias' 的别名,引用全局变量 'my_var'
@my_alias = alias i32* @my_var

12.分支和循环

在 LLVM IR 中,可以使用分支和循环指令来实现控制流。以下是分支和循环指令的语法和示例代码:

; 定义一个名为 'my_func' 的函数,返回类型为 void,没有参数
define void @my_func() {
  ; 定义一个标签 'entry'
  entry:
    ; 定义一个 i32 类型的变量 %i,初始值为 0
    %i = alloca i32
    store i32 0, i32* %i

  ; 定义一个标签 'loop'
  loop:
    ; 加载变量 %i 的值,并将其加 1
    %i_val = load i32, i32* %i
    %next_i = add i32 %i_val, 1

    ; 将计算后的值存储到变量 %i 中
    store i32 %next_i, i32* %i

    ; 如果 %i 的值小于 10,则跳转到标签 'loop'
    %cond = icmp slt i32 %next_i, 10
    br i1 %cond, label %loop, label %exit

  ; 定义一个标签 'exit'
  exit:
    ret void
}

13.内存操作

在 LLVM IR 中,可以使用 load 和 store 指令来进行内存读写操作。以下是 load 和 store 指令的语法和示例代码:

; 定义一个 i32 类型的全局变量 %my_var,初始值为 10
@my_var = global i32 10

; 将全局变量 %my_var 的值加载到变量 %val 中
%val = load i32, i32* @my_var

; 将变量 %new_val 的值存储到全局变量 %my_var 中
%new_val = add i32 %val, 5
store i32 %new_val, i32* @my_var

14.数组和指针

在 LLVM IR 中,可以使用数组和指针来处理数据。以下是数组和指针的语法和示例代码:

; 定义一个名为 'my_func' 的函数,返回类型为 i32,有一个指向 i32 类型的数组的指针参数 %arr
define i32 @my_func(i32* %arr) {
  ; 获取数组第 0 个元素的地址,并将其存储到变量 %arr_ptr 中
  %arr_ptr = getelementptr i32, i32* %arr, i32 0

  ; 将数组第 0 个元素的值加载到变量 %val 中
  %val = load i32, i32* %arr_ptr

  ; 返回变量 %val 的值
  ret i32 %val
}

; 定义一个名为 'my_arr' 的全局数组,包含三个 i32 类型的元素
@my_arr = global [3 x i32] [i32 10, i32 20, i32 30]

; 调用函数 'my_func',将数组 'my_arr' 的首地址传递给函数,并将返回值存储到变量
%result = call i32 @my_func(i32* getelementptr inbounds ([3 x i32], [3 x i32]* @my_arr, i32 0, i32 0))

; 输出变量 %result 的值
; 注意:此处的 'i32' 表示要输出的值为 32 位整数
;       如果要输出其他类型的值,需要相应地修改 'i32' 为其他类型
;       例如:float 表示单精度浮点数,double 表示双精度浮点数,等等
;       如果要输出字符串,则需要使用 'string' 类型
;       例如:%fmt_str = constant [14 x i8] c"result: %d\n\00"
;             call i32 (i8*, ...) @printf(i8* %fmt_str, i32 %result)
;       其中,'...' 表示可变参数,'i8*' 表示字符串的地址,%fmt_str 表示字符串变量的名称
%fmt_str = constant [9 x i8] c"%d\n\00"
call i32 (i8*, ...) @printf(i8* %fmt_str, i32 %result)

15.结构体

在 LLVM IR 中,可以使用结构体来组织数据。以下是结构体的语法和示例代码:

; 定义一个名为 'Person' 的结构体,包含两个字段:'name' 和 'age'
%Person = type { i8*, i32 }

; 定义一个名为 'my_func' 的函数,返回类型为 i32,有一个指向 'Person' 结构体的指针参数 %person_ptr
define i32 @my_func(%Person* %person_ptr) {
  ; 获取结构体第 1 个字段 'age' 的地址,并将其存储到变量 %age_ptr 中
  %age_ptr = getelementptr %Person, %Person* %person_ptr, i32 0, i32 1

  ; 将结构体第 1 个字段 'age' 的值加载到变量 %age 中
  %age = load i32, i32* %age_ptr

  ; 返回变量 %age 的值
  ret i32 %age
}

; 定义一个名为 'my_person' 的全局结构体变量,包含两个字段:'name' 和 'age'
@my_person = global %Person { i8* getelementptr inbounds ([6 x i8], [6 x i8]* @name_str, i32 0, i32 0), i32 20 }

; 调用函数 'my_func',将结构体 'my_person' 的首地址传递给函数,并将返回值存储到变量 %result 中
%result = call i32 @my_func(%Person* @my_person)

; 输出变量 %result 的值
%fmt_str = constant [9 x i8] c"%d\n\00"
call i32 (i8*, ...) @printf(i8* %fmt_str, i32 %result)

; 定义一个名为 'name_str' 的全局字符串,用于初始化结构体 'my_person' 中的 'name' 字段
@name_str = private unnamed_addr constant [6 x i8] c"John

16.全局变量

在 LLVM IR 中,可以使用全局变量来在函数之间共享数据。以下是全局变量的语法和示例代码:

; 定义一个名为 'my_global_var' 的全局变量,类型为 i32,初始值为 10
@my_global_var = global i32 10

; 定义一个名为 'my_func' 的函数,返回类型为 i32,无参数
define i32 @my_func() {
  ; 加载全局变量 'my_global_var' 的值到变量 %var 中
  %var = load i32, i32* @my_global_var

  ; 将变量 %var 的值乘以 2
  %var_times_2 = mul i32 %var, 2

  ; 将变量 %var_times_2 的值存储到全局变量 'my_global_var' 中
  store i32 %var_times_2, i32* @my_global_var

  ; 返回变量 %var_times_2 的值
  ret i32 %var_times_2
}

; 调用函数 'my_func',将返回值存储到变量 %result 中
%result = call i32 @my_func()

; 输出变量 %result 的值
%fmt_str = constant [9 x i8] c"%d\n\00"
call i32 (i8*, ...) @printf(i8* %fmt_str, i32 %result)

17.运算符

在 LLVM IR 中,支持各种算术运算符、位运算符、比较运算符和逻辑运算符。以下是运算符的语法和示例代码:

; 算术运算符
%a = add i32 1, 2           ; %a = 1 + 2 = 3
%b = sub i32 5, 3           ; %b = 5 - 3 = 2
%c = mul i32 2, 3           ; %c = 2 * 3 = 6
%d = sdiv i32 10, 3         ; %d = 10 / 3 = 3(整数除法)
%e = fadd double 1.0, 2.0   ; %e = 1.0 + 2.0 = 3.0(双精度浮点数加法)
%f = fsub float 5.0, 3.0    ; %f = 5.0 - 3.0 = 2.0(单精度浮点数减法)
%g = fmul double 2.0, 3.0   ; %g = 2.0 * 3.0 = 6.0(双精度浮点数乘法)
%h = fdiv float 10.0, 3.0   ; %h = 10.0 / 3.0 ≈ 3.3333(单精度浮点数除法)

; 位运算符
%i = shl i32 1, 2           ; %i = 1 << 2 = 4(左移 2 位)
%j = shr i32 8, 2           ; %j = 8 >> 2 = 2(右移
; 比较运算符
%k = icmp eq i32 1, 2       ; %k = 1 == 2(相等性比较),结果为 i1 类型
%l = icmp slt i32 3, 4      ; %l = 3 < 4(有符号整数小于比较),结果为 i1 类型
%m = fcmp olt float 1.0, 2.0 ; %m = 1.0 < 2.0(有序单精度浮点数小于比较),结果为 i1 类型

; 逻辑运算符
%n = and i1 1, 0            ; %n = 1 & 0 = 0(逻辑与),结果为 i1 类型
%o = or i1 1, 0             ; %o = 1 | 0 = 1(逻辑或),结果为 i1 类型
%p = xor i1 1, 0            ; %p = 1 ^ 0 = 1(逻辑异或),结果为 i1 类型
%q = icmp eq i1 %n, %o      ; %q = %n == %o(相等性比较),结果为 i1 类型

18.控制流

在 LLVM IR 中,支持各种控制流语句,如条件语句、循环语句和跳转语句。以下是控制流语句的语法和示例代码:

; 条件语句
define i32 @max(i32 %a, i32 %b) {
  %max = phi i32 [ %a, %entry ], [ %b, %if_true ]
  %cmp = icmp sgt i32 %a, %b
  br i1 %cmp, label %if_true, label %if_false

if_true:
  br label %merge

if_false:
  br label %merge

merge:
  %result = phi i32 [ %a, %if_false ], [ %b, %if_true ]
  ret i32 %result
}

; 循环语句
define i32 @sum(i32 %n) {
  %sum = alloca i32
  store i32 0, i32* %sum
  br label %loop

loop:
  %i = phi i32 [ 0, %entry ], [ %next_i, %loop_body ]
  %acc = phi i32 [ 0, %entry ], [ %next_acc, %loop_body ]
  %cmp = icmp slt i32 %i, %n
  br i1 %cmp, label %loop_body, label %exit

loop_body:
  %next_i = add i32 %i, 1
  %next_acc = add i32 %acc, %i
  br label %loop

exit:
  %result = load i32, i32* %sum
  ret i32 %result
}

; 跳转语句
define void @foo() {
  br label %loop

loop:
  %i = phi i32 [ 0, %entry ], [ %next_i, %loop_body ]
  %cmp = icmp eq i32 %i, 10
  br i1 %cmp, label %exit, label %loop_body

19.全局变量和常量

在 LLVM IR 中,可以定义全局变量和常量,以下是全局变量和常量的语法和示例代码:

; 定义全局变量
@global_var = global i32 0

; 定义全局常量
@global_const = constant [12 x i32] [i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10, i32 11, i32 12]

define i32 @access_global_var() {
  %ptr = getelementptr i32, i32* @global_var, i32 0
  %val = load i32, i32* %ptr
  ret i32 %val
}

define i32* @access_global_const() {
  %ptr = getelementptr [12 x i32], [12 x i32]* @global_const, i32 0, i32 0
  ret i32* %ptr
}

20.函数

在 LLVM IR 中,可以定义函数,并为函数指定参数和返回值类型,以下是函数的语法和示例代码:

; 定义函数
define i32 @add(i32 %a, i32 %b) {
  %result = add i32 %a, %b
  ret i32 %result
}

; 调用函数
define i32 @foo() {
  %a = add i32 1, 2
  %b = add i32 %a, 3
  %c = call i32 @add(i32 %a, i32 %b)
  ret i32 %c
}

21.异常处理

在 LLVM IR 中,也支持异常处理,以下是异常处理的语法和示例代码:

; 异常处理
define void @test() personality i32 (...)* @__gxx_personality_v0 {
entry:
  invoke void @foo() to label %exit unwind label %catch

catch:
  %exn = landingpad { i8*, i32 }
          catch i8* bitcast (i8** @typeinfo to i8*)
  %ptr = extractvalue { i8*, i32 } %exn, 0
  %msg = call i8* @__cxa_demangle(i8* %ptr, i8* null, i32* null, i32* null)
  call void @puts(i8* %msg)
  call void @free(i8* %msg)
  resume { i8*, i32 } %exn

exit:
  ret void
}

declare i8* @__cxa_demangle(i8*, i8*, i32*, i32*)
declare void @puts(i8*)
declare void @free(i8*)
declare i8** @typeinfo

以上是 LLVM IR 的基本语法和示例代码,当然还有更多高级的语法和技巧可以使用。

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值