rust基础语句LLVM IR以及汇编分析
¶
解读¶
使用 rustc src/main.rs --emit llvm-ir 命令生成的 LLVM IR代码
使用 rustc src/main.rs --emit asm -C "llvm-args=-x86-asm-syntax=intel" 命令生成intel风格ASM代码
main函数¶
rust代码¶
fn test_arg(a1: i32, a2: i32) -> i32 {
let temp = a1 + a2;
return temp;
}
fn test_many_arg(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32, i: i32, j: i32, k: i32, l: i32) {
let a = a + 1;
let b = b + 2;
}
fn test_str_arg(s: &str) -> usize {
let temp = s.len();
return temp;
}
fn test_mut_v(){
let mut v = 1;
v = v + 1;
v = v + 2;
}
fn foo(a:i32, b:i32) { }
fn test_call(mut a:i32, b:i32) {
a += 1;
foo(a, b);
}
fn test_if_else(q1:i32, q2:i32) {
let mut a:i32;
if q1 > q2 {
a = q1
} else if q1 == q2 {
a = q1 - q2
} else {
a = q2
}
}
fn test_match(m:i32) {
let mut foo = 0;
match m {
1 => {
foo = 100;
}
2 => {
foo = 200;
}
_ => {
foo = 999;
}
}
}
fn test_for(){
let v = [1, 2, 3, 4, 5, 6];
let mut c = 0;
for i in v.iter() {
c += i;
}
}
fn test_while(){
let v = [1, 2, 3, 4, 5, 6];
let mut c = 0;
let mut i = 0;
while i < v.len() {
c += v[i];
i += 1;
}
}
fn test_loop() {
let mut c = 0;
loop {
c += 1;
if c > 10 {
break;
}
}
}
fn test_v(b:bool){
let a:i32;
if b {
a = 1;
} else {
a = 2;
}
let c = a;
}
fn main() {
// 函数入参出参
let a1 = 241;
let a2 = 242;
test_arg(232, a2);
// 更多的入参参数
test_many_arg(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
// &str入参
test_str_arg("hello");
// 可变变量
test_mut_v();
// 控制语句 if, else if, else
test_if_else(3, 4);
// 控制语句 match
test_match(5);
// 循环 for
test_for();
// 循环 while
test_while();
// 循环 loop
test_loop();
// 变量初始化
test_v(true);
// 相互调用
test_call(1, 2);
}
对应LLVM代码¶
; internal: 修饰符, 表示这个 函数 是以局部符号的身份出现(函数也有和全局变量完全一致的Linkage Types和Visibility Style, 来控制函数名在符号表中出现的情况)
; void: 无返回
; unnamed_addr: function attribute 函数属性表示知道地址不重要, 可以合并两个相同的函数
; #1: 是一个function attribute的集合
; !dbg !282: (用rustc生成的代码没有这些, 如果用rustplay生成就会有这些调试信息, 很占地方)以感叹号开头, 表示metadata元数据类型, 元数据是为了将额外信息附加 在程序中传递给LLVM后端更好的优化或生成代码, 用于Debug的信息就是通过源数据形式传递的
define internal void @_ZN4main4main17h5ea8adc459579547E() unnamed_addr #1 {
start:
%_3 = call i32 @_ZN4main8test_arg17h95095e7c44781b93E(i32 241, i32 242)
; 跳转到下一个 basic block OUT
br label %bb1
bb1: ; preds = %start, 入口 IN
; 调用 test_many_arg 无返回值, 编译期确定的变量直接传入
call void @_ZN4main13test_many_arg17h07e61bb09fbbde69E(i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10, i32 11)
; 跳转到下一个 basic block OUT
br label %bb2
bb2: ; preds = %bb1, 入口 IN
%_7 = call i64 @_ZN4main12test_str_arg17h49d9770d814903aeE([0 x i8]* nonnull align 1 bitcast (<{ [5 x i8] }>* @alloc42 to [0 x i8]*), i64 5)
; 跳转到下一个 basic block OUT
br label %bb3
bb3: ; preds = %bb2, 入口 IN
; 调用 test_mut_v 无返回值
call void @_ZN4main10test_mut_v17h72e65efed3283795E()
; 跳转到下一个 basic block OUT
br label %bb4
bb4: ; preds = %bb3
; call main::test_if_else
call void @_ZN4main12test_if_else17h9c4880375b32a152E(i32 3, i32 4)
br label %bb5
bb5: ; preds = %bb4
; call main::test_match
call void @_ZN4main10test_match17hbfd2e8a76a59262dE(i32 5)
br label %bb6
bb6: ; preds = %bb5
; call main::test_for
call void @_ZN4main8test_for17h140ba4adcffac9f1E()
br label %bb7
bb7: ; preds = %bb6
; call main::test_while
call void @_ZN4main10test_while17h8465a089519c404cE()
br label %bb8
bb8: ; preds = %bb7
; call main::test_loop
call void @_ZN4main9test_loop17he7fe14dc14940699E()
br label %bb9
bb9: ; preds = %bb8
; call main::test_v
call void @_ZN4main6test_v17h054b8dfdd429bdf4E(i1 zeroext true)
br label %bb10
bb10: ; preds = %bb9
; call main::test_call
call void @_ZN4main9test_call17he1e0e114b674aec4E(i32 1, i32 2)
br label %bb11
bb11: ; preds = %bb10
ret void
}
; 一个function attribute的集合
attributes #1 = { uwtable "frame-pointer"="all" "probe-stack"="__rust_probestack" "target-cpu"="core2" }
; 函数的声明, 这是llvm模块中的函数, 为了保证编译时不出错, 需要先声明该函数
declare { i32, i1 } @llvm.sadd.with.overflow.i32(i32, i32) #4
汇编代码¶
__ZN4main4main17h5ea8adc459579547E:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset rbp, -16
mov rbp, rsp
.cfi_def_cfa_register rbp
sub rsp, 48
mov edi, 232
mov esi, 242
call __ZN4main8test_arg17h95095e7c44781b93E
xor edi, edi
mov esi, 1
mov edx, 2
mov ecx, 3
mov r8d, 4
mov r9d, 5
mov dword ptr [rsp], 6
mov dword ptr [rsp + 8], 7
mov dword ptr [rsp + 16], 8
mov dword ptr [rsp + 24], 9
mov dword ptr [rsp + 32], 10
mov dword ptr [rsp + 40], 11
call __ZN4main13test_many_arg17h07e61bb09fbbde69E
lea rdi, [rip + l___unnamed_12]
mov esi, 5
call __ZN4main12test_str_arg17h49d9770d814903aeE
call __ZN4main10test_mut_v17h72e65efed3283795E
mov edi, 3
mov esi, 4
call __ZN4main12test_if_else17h9c4880375b32a152E
mov edi, 5
call __ZN4main10test_match17hbfd2e8a76a59262dE
call __ZN4main8test_for17h140ba4adcffac9f1E
call __ZN4main10test_while17h8465a089519c404cE
call __ZN4main9test_loop17he7fe14dc14940699E
mov edi, 1
call __ZN4main6test_v17h054b8dfdd429bdf4E
mov edi, 1
mov esi, 2
call __ZN4main9test_call17he1e0e114b674aec4E
add rsp, 48
pop rbp
ret
.cfi_endproc
.globl _main
.p2align 4, 0x90
_main:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset rbp, -16
mov rbp, rsp
.cfi_def_cfa_register rbp
mov rdx, rsi
movsxd rsi, edi
lea rdi, [rip + __ZN4main4main17h5ea8adc459579547E]
call __ZN3std2rt10lang_start17h2f41bd9f89d8e829E
pop rbp
ret
.cfi_endproc
.section __DATA,__const
.p2align 3
test_arg¶
rust代码¶
let a1 = 241;
let a2 = 242;
test_arg(a1, a2);
//
fn test_arg(a1: i32, a2: i32) -> i32 {
let temp = a1 + a2;
return temp;
}
对应LLVM代码¶
调用¶
; 调用之后返回的是 参数1 和参数2 相加的结果
; 这里rust 代码中的a1 和a2 是在编译期就已经知道的字面量, 这里直接放入了调用的参数入口的地方
%_3 = call i32 @_ZN4main8test_arg17h95095e7c44781b93E(i32 241, i32 242)
定义¶
define internal i32 @_ZN4main8test_arg17h95095e7c44781b93E(i32 %a1, i32 %a2) unnamed_addr #1 {
start:
; llvm.sadd.with.overflow.i32这里是定义在外部的一个函数, 返回一个复合类型, {value, 是否溢出}
%0 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a1, i32 %a2)
; 提取 复合类型 第 0个 字段: 相加后的值
%_5.0 = extractvalue { i32, i1 } %0, 0
; 提取 复合类型 第 1个 字段: 溢出位
%_5.1 = extractvalue { i32, i1 } %0, 1
; 对 %_5.1 进行分支预测优化, expect 的第一个参数传入一个bool值, 第二个参数表示 "期望"的值为false, 在编译时会把下一条指令内false分支的汇编指令紧挨着跳转指令(因为false分支得到执行的机会比较大)
%1 = call i1 @llvm.expect.i1(i1 %_5.1, i1 false)
; 如果 溢出位为 true 表示溢出 panic, 没有溢出 那么久return出 %_5.0
br i1 %1, label %panic, label %bb1
bb1: ; preds = %start
ret i32 %_5.0
panic: ; preds = %start
; call core::panicking::panic
call void @_ZN4core9panicking5panic17hf0752d57e0382e6dE([0 x i8]* nonnull align 1 bitcast ([28 x i8]* @str.1 to [0 x i8]*), i64 28, %"core::panic::location::Location"* align 8 dereferenceable(24) bitcast (<{ i8*, [16 x i8] }>* @alloc39 to %"core::panic::location::Location"*)) #8
unreachable
}
汇编代码¶
__ZN4main8test_arg17h95095e7c44781b93E:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset rbp, -16
mov rbp, rsp
.cfi_def_cfa_register rbp
sub rsp, 16
add edi, esi
mov dword ptr [rbp - 4], edi
seto al
test al, 1
jne LBB25_2
mov eax, dword ptr [rbp - 4]
add rsp, 16
pop rbp
ret
LBB25_2:
lea rdi, [rip + _str.0]
lea rdx, [rip + l___unnamed_3]
mov esi, 28
call __ZN4core9panicking5panic17hf0752d57e0382e6dE
.cfi_endproc
.p2align 4, 0x90
test_many_arg¶
rust代码¶
test_many_arg(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
//
fn test_many_arg(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32, i: i32, j: i32, k: i32, l: i32) {
let a = a + 1;
let b = b + 2;
}
对应LLVM代码¶
调用¶
call void @_ZN4main13test_many_arg17h07e61bb09fbbde69E(i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10, i32 11)
定义¶
define internal void @_ZN4main13test_many_arg17h07e61bb09fbbde69E(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h, i32 %i, i32 %j, i32 %k, i32 %l) unnamed_addr #1 {
start:
; 相加得到复合类型
%0 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a, i32 1)
; 判断是否溢出
%_15.0 = extractvalue { i32, i1 } %0, 0
%_15.1 = extractvalue { i32, i1 } %0, 1
%1 = call i1 @llvm.expect.i1(i1 %_15.1, i1 false)
; 没有溢出就跳转到 lable bb1
br i1 %1, label %panic, label %bb1
bb1: ; preds = %start
; 相加得到复合类型
%2 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %b, i32 2)
; 判断是否溢出
%_18.0 = extractvalue { i32, i1 } %2, 0
%_18.1 = extractvalue { i32, i1 } %2, 1
%3 = call i1 @llvm.expect.i1(i1 %_18.1, i1 false)
; 没有发生溢出就跳到 lable bb2 返回 void
br i1 %3, label %panic1, label %bb2
panic: ; preds = %start
; call core::panicking::panic
call void @_ZN4core9panicking5panic17hf0752d57e0382e6dE([0 x i8]* nonnull align 1 bitcast ([28 x i8]* @str.1 to [0 x i8]*), i64 28, %"core::panic::location::Location"* align 8 dereferenceable(24) bitcast (<{ i8*, [16 x i8] }>* @alloc41 to %"core::panic::location::Location"*)) #8
unreachable
bb2: ; preds = %bb1
ret void
panic1: ; preds = %bb1
; call core::panicking::panic
call void @_ZN4core9panicking5panic17hf0752d57e0382e6dE([0 x i8]* nonnull align 1 bitcast ([28 x i8]* @str.1 to [0 x i8]*), i64 28, %"core::panic::location::Location"* align 8 dereferenceable(24) bitcast (<{ i8*, [16 x i8] }>* @alloc43 to %"core::panic::location::Location"*)) #8
unreachable
}
汇编代码¶
__ZN4main13test_many_arg17h07e61bb09fbbde69E:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset rbp, -16
mov rbp, rsp
.cfi_def_cfa_register rbp
sub rsp, 16
mov dword ptr [rbp - 4], esi
inc edi
seto al
test al, 1
jne LBB26_2
mov eax, dword ptr [rbp - 4]
add eax, 2
seto al
test al, 1
jne LBB26_4
jmp LBB26_3
LBB26_2:
lea rdi, [rip + _str.0]
lea rdx, [rip + l___unnamed_4]
mov esi, 28
call __ZN4core9panicking5panic17hf0752d57e0382e6dE
LBB26_3:
add rsp, 16
pop rbp
ret
LBB26_4:
lea rdi, [rip + _str.0]
lea rdx, [rip + l___unnamed_5]
mov esi, 28
call __ZN4core9panicking5panic17hf0752d57e0382e6dE
.cfi_endproc
.p2align 4, 0x90
test_str_arg¶
rust代码¶
对应LLVM代码¶
调用¶
; 调用 test_str_arg 返回值是字符串的长度, LLVM中 usize和isize 默认是 i64 有符号64位整数
; 参数1: 通过bitcast type value type, 把[5 x i8]*的全局常量指针 @alloc56 "转为" [0 x i8]类型的指针, 当做 [0 x i8]类型指针 1字节对齐 的变量传入函数
; 参数2: 是在编译期得知的, 为5
%_7 = call i64 @_ZN4main12test_str_arg17h49d9770d814903aeE([0 x i8]* nonnull align 1 bitcast (<{ [5 x i8] }>* @alloc56 to [0 x i8]*), i64 5)
定义¶
; rust中接收的 &str, 这里变为了接收两个参数: s.0 和 s.1 注意这里 . 并没有实际意义
; s.0: [0 x i8]* 表示这是一个[0xi8]的指针, nonnull align 1 表示非空指针内部1字节对齐
; s.1: 表示指针长度
define internal i64 @_ZN4main12test_str_arg17h49d9770d814903aeE([0 x i8]* nonnull align 1 %s.0, i64 %s.1) unnamed_addr #1 {
start:
; call core::str::<impl str>::len 调用core中的len函数, 返回值给虚拟寄存器
%temp = call i64 @"_ZN4core3str21_$LT$impl$u20$str$GT$3len17hb905782d767736d4E"([0 x i8]* nonnull align 1 %s.0, i64 %s.1)
br label %bb1
bb1: ; preds = %start
ret i64 %temp
}
; 对应 core中的 len函数
define internal i64 @"_ZN4core3str21_$LT$impl$u20$str$GT$3len17hb905782d767736d4E"([0 x i8]* nonnull align 1 %self.0, i64 %self.1) unnamed_addr #2 {
start:
; 分配一个栈上变量 保存复合类型, 8字节对齐
%0 = alloca { [0 x i8]*, i64 }, align 8
; 通过 getelementptr 获得 { [0 x i8]*, i64 }类型 的 { [0 x i8]*, i64 }* 的指针 %0 中的数据
; 第一个i32 0表示[0 x i8]*, 第二个i32 0表示 i8
%1 = getelementptr inbounds { [0 x i8]*, i64 }, { [0 x i8]*, i64 }* %0, i32 0, i32 0
store [0 x i8]* %self.0, [0 x i8]** %1, align 8
%2 = getelementptr inbounds { [0 x i8]*, i64 }, { [0 x i8]*, i64 }* %0, i32 0, i32 1
store i64 %self.1, i64* %2, align 8
%3 = getelementptr inbounds { [0 x i8]*, i64 }, { [0 x i8]*, i64 }* %0, i32 0, i32 0
%4 = load [0 x i8]*, [0 x i8]** %3, align 8, !nonnull !2
%5 = getelementptr inbounds { [0 x i8]*, i64 }, { [0 x i8]*, i64 }* %0, i32 0, i32 1
%6 = load i64, i64* %5, align 8
%7 = insertvalue { [0 x i8]*, i64 } undef, [0 x i8]* %4, 0
%8 = insertvalue { [0 x i8]*, i64 } %7, i64 %6, 1
%_3.0 = extractvalue { [0 x i8]*, i64 } %8, 0
%_3.1 = extractvalue { [0 x i8]*, i64 } %8, 1
br label %bb1
bb1: ; preds = %start
ret i64 %_3.1
}
汇编代码¶
__ZN4main12test_str_arg17h49d9770d814903aeE:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset rbp, -16
mov rbp, rsp
.cfi_def_cfa_register rbp
sub rsp, 16
call __ZN4core3str21_$LT$impl$u20$str$GT$3len17hb905782d767736d4E
mov qword ptr [rbp - 8], rax
mov rax, qword ptr [rbp - 8]
add rsp, 16
pop rbp
ret
.cfi_endproc
.p2align 4, 0x90
test_mut_v¶
rust代码¶
对应LLVM代码¶
调用¶
定义¶
; main::test_mut_v
; Function Attrs: uwtable
define internal void @_ZN4main10test_mut_v17h72e65efed3283795E() unnamed_addr #1 {
start:
; 由于 SSA的限制, 局部变量必须分配到栈上, 这里创建 栈上变量, 其实还是指向 一个栈上内存 指针
%v = alloca i32, align 4
; 使用 store 存储 i32类型的值1 到 栈上内存, 4字节对齐
store i32 1, i32* %v, align 4
// 如上
store i32 2, i32* %v, align 4
store i32 4, i32* %v, align 4
// 无返回值
ret void
}
汇编代码¶
__ZN4main10test_mut_v17h72e65efed3283795E:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset rbp, -16
mov rbp, rsp
.cfi_def_cfa_register rbp
sub rsp, 4
mov dword ptr [rbp - 4], 1
mov dword ptr [rbp - 4], 2
mov dword ptr [rbp - 4], 4
add rsp, 4
pop rbp
ret
.cfi_endproc
.p2align 4, 0x90
test_if_else¶
rust代码¶
test_if_else(3, 4);
//
fn test_if_else(q1:i32, q2:i32) {
let mut a:i32;
if q1 > q2 {
a = q1
} else if q1 == q2 {
a = q1 - q2
} else {
a = q2
}
}
对应LLVM代码¶
调用¶
定义¶
define internal void @_ZN4main12test_if_else17h9c4880375b32a152E(i32 %q1, i32 %q2) unnamed_addr #1 {
start:
; 创建一个栈上变量
%a = alloca i32, align 4
; 有符号类型 比较判断虚拟寄存器 是否 %q1 > %q2
%_4 = icmp sgt i32 %q1, %q2
; true 跳转到 bb1, false 跳转bb2
br i1 %_4, label %bb1, label %bb2
bb2: ; preds = %start
; 有符号类型 比较判断虚拟寄存器是否 %q1 = %q2
%_8 = icmp eq i32 %q1, %q2
; true 跳转到bb3进入else if 分支, false 跳转到 bb5进入else分支
br i1 %_8, label %bb3, label %bb5
bb1: ; preds = %start
; 把虚拟寄存器中 %q1中的值, 保存到栈上变量 %a 中, 4字节对齐, 跳转到 bb6返回
store i32 %q1, i32* %a, align 4
br label %bb6
bb6: ; preds = %bb5, %bb4, %bb1
; 返回
ret void
bb5: ; preds = %bb2
; 把虚拟寄存器中的值 %q2, 保存到栈上变量 %a中, 4字节对齐, 跳转到bb6返回
store i32 %q2, i32* %a, align 4
br label %bb6
bb3: ; preds = %bb2
; 减法计算 获得一个 复合类型, 后续用来判断是否溢出
%0 = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 %q1, i32 %q2)
; 判断是否溢出
%_13.0 = extractvalue { i32, i1 } %0, 0
%_13.1 = extractvalue { i32, i1 } %0, 1
%1 = call i1 @llvm.expect.i1(i1 %_13.1, i1 false)
; 溢出就跳到panic, 没有溢出就跳到 bb4
br i1 %1, label %panic, label %bb4
bb4: ; preds = %bb3
; 虚拟寄存器%_13.0内的值, 保存到栈 %a 中, 4字节对齐
store i32 %_13.0, i32* %a, align 4
; 跳转到 bb6返回
br label %bb6
panic: ; preds = %bb3
; call core::panicking::panic
call void @_ZN4core9panicking5panic17hf0752d57e0382e6dE([0 x i8]* nonnull align 1 bitcast ([33 x i8]* @str.2 to [0 x i8]*), i64 33, %"core::panic::location::Location"* align 8 dereferenceable(24) bitcast (<{ i8*, [16 x i8] }>* @alloc47 to %"core::panic::location::Location"*)) #8
unreachable
}
汇编代码¶
__ZN4main12test_if_else17h9c4880375b32a152E:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset rbp, -16
mov rbp, rsp
.cfi_def_cfa_register rbp
sub rsp, 16
mov dword ptr [rbp - 12], edi
mov dword ptr [rbp - 8], esi
cmp edi, esi
jg LBB31_2
mov eax, dword ptr [rbp - 12]
mov ecx, dword ptr [rbp - 8]
cmp eax, ecx
je LBB31_5
jmp LBB31_4
LBB31_2:
mov eax, dword ptr [rbp - 12]
mov dword ptr [rbp - 4], eax
LBB31_3:
add rsp, 16
pop rbp
ret
LBB31_4:
mov eax, dword ptr [rbp - 8]
mov dword ptr [rbp - 4], eax
jmp LBB31_3
LBB31_5:
mov ecx, dword ptr [rbp - 8]
mov eax, dword ptr [rbp - 12]
sub eax, ecx
mov dword ptr [rbp - 16], eax
seto al
test al, 1
jne LBB31_7
mov eax, dword ptr [rbp - 16]
mov dword ptr [rbp - 4], eax
jmp LBB31_3
LBB31_7:
lea rdi, [rip + _str.1]
lea rdx, [rip + l___unnamed_7]
mov esi, 33
call __ZN4core9panicking5panic17hf0752d57e0382e6dE
.cfi_endproc
.p2align 4, 0x90
test_match¶
rust代码¶
test_match(5);
//
fn test_match(m:i32) {
let mut foo = 0;
match m {
1 => {
foo = 100;
}
2 => {
foo = 200;
}
_ => {
foo = 999;
}
}
}
对应LLVM代码¶
调用¶
定义¶
define internal void @_ZN4main10test_match17hbfd2e8a76a59262dE(i32 %m) unnamed_addr #1 {
start:
; 分配栈上i32类型 变量 %foo 4字节对齐
%foo = alloca i32, align 4
; 保存 i32类型值0, 到 %foo中
store i32 0, i32* %foo, align 4
; 如果 虚拟寄存器%m 都没有匹配, 那么进入 bb1 默认分支, 具体是基于ifelse还是跳表, 需要看LLVM优化
switch i32 %m, label %bb1 [
; 如果是 %m 是1, 进入 bb2 分支
i32 1, label %bb2
; 如上
i32 2, label %bb3
]
bb1: ; preds = %start
store i32 999, i32* %foo, align 4
br label %bb4
bb2: ; preds = %start
store i32 100, i32* %foo, align 4
br label %bb4
bb3: ; preds = %start
store i32 200, i32* %foo, align 4
br label %bb4
bb4: ; preds = %bb2, %bb3, %bb1
ret void
}
汇编代码¶
__ZN4main10test_match17hbfd2e8a76a59262dE:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset rbp, -16
mov rbp, rsp
.cfi_def_cfa_register rbp
push rax
mov dword ptr [rbp - 8], edi
mov dword ptr [rbp - 4], 0
sub edi, 1
je LBB32_2
jmp LBB32_5
LBB32_5:
mov eax, dword ptr [rbp - 8]
sub eax, 2
je LBB32_3
jmp LBB32_1
LBB32_1:
mov dword ptr [rbp - 4], 999
jmp LBB32_4
LBB32_2:
mov dword ptr [rbp - 4], 100
jmp LBB32_4
LBB32_3:
mov dword ptr [rbp - 4], 200
LBB32_4:
add rsp, 8
pop rbp
ret
.cfi_endproc
.p2align 4, 0x90
test_for¶
rust代码¶
test_for();
//
fn test_for(){
let v = [1, 2, 3, 4, 5, 6];
let mut c = 0;
for i in v.iter() {
c += i;
}
}
对应LLVM代码¶
调用¶
定义¶
define internal void @_ZN4main8test_for17h140ba4adcffac9f1E() unnamed_addr #1 {
start:
; 栈上变量 i32类型指针, 8字节对齐
%_9 = alloca i32*, align 8
; 栈上复合类型 保存了两个 i32的指针
%iter = alloca { i32*, i32* }, align 8
; 栈上变量(用来统计相加后的数值)
%c = alloca i32, align 4
; 栈上复合类型 用来保存 array
%v = alloca [6 x i32], align 4
; 转换array首地址 为 i32类型指针, 保存到虚拟寄存器 %0 (TODO: 这里为什么不用下面getelementptr, 而使用bitcast?)
%0 = bitcast [6 x i32]* %v to i32*
; 保存 i32类型数值1, 到 %0
store i32 1, i32* %0, align 4
; 得到 array[1] 的地址保存到虚拟寄存器 %1
%1 = getelementptr inbounds [6 x i32], [6 x i32]* %v, i32 0, i32 1
; 数值2 保存到 %1
store i32 2, i32* %1, align 4
%2 = getelementptr inbounds [6 x i32], [6 x i32]* %v, i32 0, i32 2
store i32 3, i32* %2, align 4
%3 = getelementptr inbounds [6 x i32], [6 x i32]* %v, i32 0, i32 3
store i32 4, i32* %3, align 4
%4 = getelementptr inbounds [6 x i32], [6 x i32]* %v, i32 0, i32 4
store i32 5, i32* %4, align 4
%5 = getelementptr inbounds [6 x i32], [6 x i32]* %v, i32 0, i32 5
store i32 6, i32* %5, align 4
; 统计用的初始化为0
store i32 0, i32* %c, align 4
; 把 [6 x i32]*类型 转为 空数组[0 x i32]* 类型, 保存在虚拟寄存器%_5.0
%_5.0 = bitcast [6 x i32]* %v to [0 x i32]*
; call core::slice::<impl [T]>::iter 获得iter迭代器
%6 = call { i32*, i32* } @"_ZN4core5slice29_$LT$impl$u20$$u5b$T$u5d$$GT$4iter17h2c7738f40d8c3b56E"([0 x i32]* nonnull align 4 %_5.0, i64 6)
%_4.0 = extractvalue { i32*, i32* } %6, 0
%_4.1 = extractvalue { i32*, i32* } %6, 1
br label %bb1
; 不知道在干嘛了
bb1: ; preds = %start
; call <I as core::iter::traits::collect::IntoIterator>::into_iter
%7 = call { i32*, i32* } @"_ZN63_$LT$I$u20$as$u20$core..iter..traits..collect..IntoIterator$GT$9into_iter17hde32256ee7b4b411E"(i32* nonnull %_4.0, i32* %_4.1)
%_3.0 = extractvalue { i32*, i32* } %7, 0
%_3.1 = extractvalue { i32*, i32* } %7, 1
br label %bb2
; 不知道在干嘛了
bb2: ; preds = %bb1
%8 = getelementptr inbounds { i32*, i32* }, { i32*, i32* }* %iter, i32 0, i32 0
store i32* %_3.0, i32** %8, align 8
%9 = getelementptr inbounds { i32*, i32* }, { i32*, i32* }* %iter, i32 0, i32 1
store i32* %_3.1, i32** %9, align 8
br label %bb3
; 不知道在干嘛了
bb3: ; preds = %bb7, %bb2
; call <core::slice::iter::Iter<T> as core::iter::traits::iterator::Iterator>::next
%10 = call align 4 dereferenceable_or_null(4) i32* @"_ZN91_$LT$core..slice..iter..Iter$LT$T$GT$$u20$as$u20$core..iter..traits..iterator..Iterator$GT$4next17haa8dee518e89eb9cE"({ i32*, i32* }* align 8 dereferenceable(16) %iter)
store i32* %10, i32** %_9, align 8
br label %bb4
; 这一步应该在判断 元素是否迭代完毕
bb4: ; preds = %bb3
%11 = bitcast i32** %_9 to {}**
%12 = load {}*, {}** %11, align 8
%13 = icmp eq {}* %12, null
%_12 = select i1 %13, i64 0, i64 1
switch i64 %_12, label %bb6 [
i64 0, label %bb5
i64 1, label %bb7
]
bb6: ; preds = %bb4
unreachable
bb5: ; preds = %bb4
ret void
; 把 %c 传入一个调用中, 然后返回bb3 继续
bb7: ; preds = %bb4
%val = load i32*, i32** %_9, align 8, !nonnull !2
; call <i32 as core::ops::arith::AddAssign<&i32>>::add_assign
call void @"_ZN66_$LT$i32$u20$as$u20$core..ops..arith..AddAssign$LT$$RF$i32$GT$$GT$10add_assign17hf543e36ec52f422eE"(i32* align 4 dereferenceable(4) %c, i32* align 4 dereferenceable(4) %val)
br label %bb3
}
汇编代码¶
__ZN4main8test_for17h140ba4adcffac9f1E:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset rbp, -16
mov rbp, rsp
.cfi_def_cfa_register rbp
sub rsp, 96
mov dword ptr [rbp - 52], 1
mov dword ptr [rbp - 48], 2
mov dword ptr [rbp - 44], 3
mov dword ptr [rbp - 40], 4
mov dword ptr [rbp - 36], 5
mov dword ptr [rbp - 32], 6
mov dword ptr [rbp - 28], 0
lea rdi, [rbp - 52]
mov esi, 6
call __ZN4core5slice29_$LT$impl$u20$$u5b$T$u5d$$GT$4iter17h2c7738f40d8c3b56E
mov qword ptr [rbp - 72], rax
mov qword ptr [rbp - 64], rdx
mov rsi, qword ptr [rbp - 64]
mov rdi, qword ptr [rbp - 72]
call __ZN63_$LT$I$u20$as$u20$core..iter..traits..collect..IntoIterator$GT$9into_iter17hde32256ee7b4b411E
mov qword ptr [rbp - 88], rax
mov qword ptr [rbp - 80], rdx
mov rax, qword ptr [rbp - 80]
mov rcx, qword ptr [rbp - 88]
mov qword ptr [rbp - 24], rcx
mov qword ptr [rbp - 16], rax
LBB33_3:
lea rdi, [rbp - 24]
call __ZN91_$LT$core..slice..iter..Iter$LT$T$GT$$u20$as$u20$core..iter..traits..iterator..Iterator$GT$4next17haa8dee518e89eb9cE
mov qword ptr [rbp - 8], rax
mov rax, qword ptr [rbp - 8]
test rax, rax
setne al
movzx eax, al
je LBB33_6
jmp LBB33_8
LBB33_8:
jmp LBB33_7
ud2
LBB33_6:
add rsp, 96
pop rbp
ret
LBB33_7:
mov rsi, qword ptr [rbp - 8]
lea rdi, [rbp - 28]
call __ZN66_$LT$i32$u20$as$u20$core..ops..arith..AddAssign$LT$$RF$i32$GT$$GT$10add_assign17hf543e36ec52f422eE
jmp LBB33_3
.cfi_endproc
.p2align 4, 0x90
test_while¶
rust代码¶
test_while();
//
fn test_while(){
let v = [1, 2, 3, 4, 5, 6];
let mut c = 0;
let mut i = 0;
while i < v.len() {
c += v[i];
i += 1;
}
}
对应LLVM代码¶
调用¶
定义¶
define internal void @_ZN4main10test_while17h8465a089519c404cE() unnamed_addr #1 {
start:
%i = alloca i64, align 8
%c = alloca i32, align 4
%v = alloca [6 x i32], align 4
%0 = bitcast [6 x i32]* %v to i32*
store i32 1, i32* %0, align 4
%1 = getelementptr inbounds [6 x i32], [6 x i32]* %v, i32 0, i32 1
store i32 2, i32* %1, align 4
%2 = getelementptr inbounds [6 x i32], [6 x i32]* %v, i32 0, i32 2
store i32 3, i32* %2, align 4
%3 = getelementptr inbounds [6 x i32], [6 x i32]* %v, i32 0, i32 3
store i32 4, i32* %3, align 4
%4 = getelementptr inbounds [6 x i32], [6 x i32]* %v, i32 0, i32 4
store i32 5, i32* %4, align 4
%5 = getelementptr inbounds [6 x i32], [6 x i32]* %v, i32 0, i32 5
store i32 6, i32* %5, align 4
store i32 0, i32* %c, align 4
store i64 0, i64* %i, align 8
br label %bb1
bb1: ; preds = %bb5, %start
%_5 = load i64, i64* %i, align 8
%_7.0 = bitcast [6 x i32]* %v to [0 x i32]*
%_4 = icmp ult i64 %_5, 6
br i1 %_4, label %bb2, label %bb6
bb6: ; preds = %bb1
ret void
bb2: ; preds = %bb1
%_10 = load i64, i64* %i, align 8
%_12 = icmp ult i64 %_10, 6
%6 = call i1 @llvm.expect.i1(i1 %_12, i1 true)
br i1 %6, label %bb3, label %panic
bb3: ; preds = %bb2
%7 = getelementptr inbounds [6 x i32], [6 x i32]* %v, i64 0, i64 %_10
%_9 = load i32, i32* %7, align 4
%8 = load i32, i32* %c, align 4
%9 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %8, i32 %_9)
%_13.0 = extractvalue { i32, i1 } %9, 0
%_13.1 = extractvalue { i32, i1 } %9, 1
%10 = call i1 @llvm.expect.i1(i1 %_13.1, i1 false)
br i1 %10, label %panic1, label %bb4
panic: ; preds = %bb2
; call core::panicking::panic_bounds_check
call void @_ZN4core9panicking18panic_bounds_check17h926d17ac6f812765E(i64 %_10, i64 6, %"core::panic::location::Location"* align 8 dereferenceable(24) bitcast (<{ i8*, [16 x i8] }>* @alloc49 to %"core::panic::location::Location"*)) #8
unreachable
bb4: ; preds = %bb3
store i32 %_13.0, i32* %c, align 4
%11 = load i64, i64* %i, align 8
%12 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %11, i64 1)
%_14.0 = extractvalue { i64, i1 } %12, 0
%_14.1 = extractvalue { i64, i1 } %12, 1
%13 = call i1 @llvm.expect.i1(i1 %_14.1, i1 false)
br i1 %13, label %panic2, label %bb5
panic1: ; preds = %bb3
; call core::panicking::panic
call void @_ZN4core9panicking5panic17hf0752d57e0382e6dE([0 x i8]* nonnull align 1 bitcast ([28 x i8]* @str.1 to [0 x i8]*), i64 28, %"core::panic::location::Location"* align 8 dereferenceable(24) bitcast (<{ i8*, [16 x i8] }>* @alloc51 to %"core::panic::location::Location"*)) #8
unreachable
bb5: ; preds = %bb4
store i64 %_14.0, i64* %i, align 8
br label %bb1
panic2: ; preds = %bb4
; call core::panicking::panic
call void @_ZN4core9panicking5panic17hf0752d57e0382e6dE([0 x i8]* nonnull align 1 bitcast ([28 x i8]* @str.1 to [0 x i8]*), i64 28, %"core::panic::location::Location"* align 8 dereferenceable(24) bitcast (<{ i8*, [16 x i8] }>* @alloc53 to %"core::panic::location::Location"*)) #8
unreachable
}
汇编代码¶
__ZN4main10test_while17h8465a089519c404cE:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset rbp, -16
mov rbp, rsp
.cfi_def_cfa_register rbp
sub rsp, 64
mov dword ptr [rbp - 36], 1
mov dword ptr [rbp - 32], 2
mov dword ptr [rbp - 28], 3
mov dword ptr [rbp - 24], 4
mov dword ptr [rbp - 20], 5
mov dword ptr [rbp - 16], 6
mov dword ptr [rbp - 12], 0
mov qword ptr [rbp - 8], 0
LBB34_1:
cmp qword ptr [rbp - 8], 6
jb LBB34_3
add rsp, 64
pop rbp
ret
LBB34_3:
mov rax, qword ptr [rbp - 8]
mov qword ptr [rbp - 48], rax
cmp rax, 6
setb al
test al, 1
jne LBB34_4
jmp LBB34_5
LBB34_4:
mov rax, qword ptr [rbp - 48]
mov eax, dword ptr [rbp + 4*rax - 36]
add eax, dword ptr [rbp - 12]
mov dword ptr [rbp - 52], eax
seto al
test al, 1
jne LBB34_7
jmp LBB34_6
LBB34_5:
mov rdi, qword ptr [rbp - 48]
lea rdx, [rip + l___unnamed_8]
mov esi, 6
call __ZN4core9panicking18panic_bounds_check17h926d17ac6f812765E
LBB34_6:
mov eax, dword ptr [rbp - 52]
mov dword ptr [rbp - 12], eax
mov rax, qword ptr [rbp - 8]
add rax, 1
mov qword ptr [rbp - 64], rax
setb al
test al, 1
jne LBB34_9
jmp LBB34_8
LBB34_7:
lea rdi, [rip + _str.0]
lea rdx, [rip + l___unnamed_9]
mov esi, 28
call __ZN4core9panicking5panic17hf0752d57e0382e6dE
LBB34_8:
mov rax, qword ptr [rbp - 64]
mov qword ptr [rbp - 8], rax
jmp LBB34_1
LBB34_9:
lea rdi, [rip + _str.0]
lea rdx, [rip + l___unnamed_10]
mov esi, 28
call __ZN4core9panicking5panic17hf0752d57e0382e6dE
.cfi_endproc
.p2align 4, 0x90
test_loop¶
rust代码¶
对应LLVM代码¶
调用¶
定义¶
define internal void @_ZN4main9test_loop17he7fe14dc14940699E() unnamed_addr #1 {
start:
%c = alloca i32, align 4
store i32 0, i32* %c, align 4
br label %bb1
bb1: ; preds = %bb2, %start
%0 = load i32, i32* %c, align 4
%1 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %0, i32 1)
%_2.0 = extractvalue { i32, i1 } %1, 0
%_2.1 = extractvalue { i32, i1 } %1, 1
%2 = call i1 @llvm.expect.i1(i1 %_2.1, i1 false)
br i1 %2, label %panic, label %bb2
bb2: ; preds = %bb1
store i32 %_2.0, i32* %c, align 4
%_4 = load i32, i32* %c, align 4
%_3 = icmp sgt i32 %_4, 10
br i1 %_3, label %bb3, label %bb1
panic: ; preds = %bb1
; call core::panicking::panic
call void @_ZN4core9panicking5panic17hf0752d57e0382e6dE([0 x i8]* nonnull align 1 bitcast ([28 x i8]* @str.1 to [0 x i8]*), i64 28, %"core::panic::location::Location"* align 8 dereferenceable(24) bitcast (<{ i8*, [16 x i8] }>* @alloc55 to %"core::panic::location::Location"*)) #8
unreachable
bb3: ; preds = %bb2
ret void
}
汇编代码¶
__ZN4main9test_loop17he7fe14dc14940699E:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset rbp, -16
mov rbp, rsp
.cfi_def_cfa_register rbp
sub rsp, 16
mov dword ptr [rbp - 4], 0
LBB35_1:
mov eax, dword ptr [rbp - 4]
inc eax
mov dword ptr [rbp - 8], eax
seto al
test al, 1
jne LBB35_3
mov eax, dword ptr [rbp - 8]
mov dword ptr [rbp - 4], eax
cmp dword ptr [rbp - 4], 10
jg LBB35_4
jmp LBB35_1
LBB35_3:
lea rdi, [rip + _str.0]
lea rdx, [rip + l___unnamed_11]
mov esi, 28
call __ZN4core9panicking5panic17hf0752d57e0382e6dE
LBB35_4:
add rsp, 16
pop rbp
ret
.cfi_endproc
.p2align 4, 0x90
test_v¶
rust代码¶
对应LLVM代码¶
调用¶
定义¶
define internal void @_ZN4main6test_v17h054b8dfdd429bdf4E(i1 zeroext %b) unnamed_addr #1 {
start:
; 栈上变量
%a = alloca i32, align 4
; 比较参数%b, true跳转到bb1, false跳转到bb2
br i1 %b, label %bb1, label %bb2
bb2: ; preds = %start
store i32 2, i32* %a, align 4
br label %bb3
bb1: ; preds = %start
store i32 1, i32* %a, align 4
br label %bb3
bb3: ; preds = %bb2, %bb1
; 栈上变量赋值给虚拟寄存器 %c
%c = load i32, i32* %a, align 4
ret void
}
汇编代码¶
__ZN4main6test_v17h054b8dfdd429bdf4E:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset rbp, -16
mov rbp, rsp
.cfi_def_cfa_register rbp
sub rsp, 4
mov al, dil
test al, 1
jne LBB36_2
mov dword ptr [rbp - 4], 2
jmp LBB36_3
LBB36_2:
mov dword ptr [rbp - 4], 1
LBB36_3:
add rsp, 4
pop rbp
ret
.cfi_endproc
.p2align 4, 0x90
test_call¶
rust代码¶
对应LLVM代码¶
调用¶
定义¶
define internal void @_ZN4main9test_call17he1e0e114b674aec4E(i32 %0, i32 %b) unnamed_addr #1 {
start:
; 把虚拟寄存器 %0, 赋值给栈上变量 %a
%a = alloca i32, align 4
store i32 %0, i32* %a, align 4
; 从栈上变量 %a 读出值保存到虚拟寄存器 %1
%1 = load i32, i32* %a, align 4
; 调用 相加, 保存复合类型到 %2
%2 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %1, i32 1)
; 判断是否溢出
%_3.0 = extractvalue { i32, i1 } %2, 0
%_3.1 = extractvalue { i32, i1 } %2, 1
%3 = call i1 @llvm.expect.i1(i1 %_3.1, i1 false)
br i1 %3, label %panic, label %bb1
bb1: ; preds = %start
; 把虚拟寄存器%_3.0内的值 保存在栈上
store i32 %_3.0, i32* %a, align 4
; 把栈上的值保存在一个新的虚拟寄存器(TODO, debug模式行为确实很怪异)
%_5 = load i32, i32* %a, align 4
; call main::foo 调用结束后续会跳转到bb2返回
call void @_ZN4main3foo17haca12d7d6aeaa4acE(i32 %_5, i32 %b)
br label %bb2
panic: ; preds = %start
; call core::panicking::panic
call void @_ZN4core9panicking5panic17hf0752d57e0382e6dE([0 x i8]* nonnull align 1 bitcast ([28 x i8]* @str.1 to [0 x i8]*), i64 28, %"core::panic::location::Location"* align 8 dereferenceable(24) bitcast (<{ i8*, [16 x i8] }>* @alloc45 to %"core::panic::location::Location"*)) #8
unreachable
bb2: ; preds = %bb1
ret void
}
define internal void @_ZN4main3foo17haca12d7d6aeaa4acE(i32 %a, i32 %b) unnamed_addr #1 {
start:
ret void
}
汇编代码¶
__ZN4main9test_call17he1e0e114b674aec4E:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset rbp, -16
mov rbp, rsp
.cfi_def_cfa_register rbp
sub rsp, 16
mov dword ptr [rbp - 12], esi
mov dword ptr [rbp - 4], edi
mov eax, dword ptr [rbp - 4]
inc eax
mov dword ptr [rbp - 8], eax
seto al
test al, 1
jne LBB30_2
mov esi, dword ptr [rbp - 12]
mov eax, dword ptr [rbp - 8]
mov dword ptr [rbp - 4], eax
mov edi, dword ptr [rbp - 4]
call __ZN4main3foo17haca12d7d6aeaa4acE
jmp LBB30_3
LBB30_2:
lea rdi, [rip + _str.0]
lea rdx, [rip + l___unnamed_6]
mov esi, 28
call __ZN4core9panicking5panic17hf0752d57e0382e6dE
LBB30_3:
add rsp, 16
pop rbp
ret
.cfi_endproc
.p2align 4, 0x90
__ZN4main3foo17haca12d7d6aeaa4acE:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset rbp, -16
mov rbp, rsp
.cfi_def_cfa_register rbp
pop rbp
ret
.cfi_endproc
.p2align 4, 0x90
基础概念补充¶
Basic block¶
SSA¶
控制流图生成¶
ll文件生成.dot文件¶
安装 dot工具¶
使用dot工具生成控制流(CFG)图¶
进入dot文件的目录, 运行以下命令, 即可生成 png 格式的图片