跳转至

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代码

test_str_arg("hello");

//
fn test_str_arg(s: &str) -> usize {
    let temp = s.len();
    return temp;
}

对应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代码

test_mut_v();

// 
fn test_mut_v(){
    let mut v = 1;
    v = v + 1;
    v = v + 2;
}

对应LLVM代码

调用
call void @_ZN4main10test_mut_v17h72e65efed3283795E()
定义
; 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代码

调用
call void @_ZN4main12test_if_else17h9c4880375b32a152E(i32 3, i32 4)
定义
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代码

调用
call void @_ZN4main10test_match17hbfd2e8a76a59262dE(i32 5)
定义
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代码

调用
call void @_ZN4main8test_for17h140ba4adcffac9f1E()
定义
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代码

调用
call void @_ZN4main10test_while17h8465a089519c404cE()
定义
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代码

test_loop();

//
fn test_loop() {
    let mut c = 0;
    loop {
        c += 1;
        if c > 10 {
            break;
        }
    }
}

对应LLVM代码

调用
call void @_ZN4main9test_loop17he7fe14dc14940699E()
定义
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代码

test_v(true);

// 
fn test_v(b:bool){
    let a:i32;

    if b {
        a = 1;
    } else {
        a = 2;
    }
    let c = a;
}

对应LLVM代码

调用
call void @_ZN4main6test_v17h054b8dfdd429bdf4E(i1 zeroext true)
定义
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代码

test_call(1, 2);

fn foo(a:i32, b:i32) { }
fn test_call(mut a:i32, b:i32) {
    a += 1;
    foo(a, b);
}

对应LLVM代码

调用
call void @_ZN4main9test_call17he1e0e114b674aec4E(i32 1, i32 2)
定义
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文件

opt -dot-cfg  main.ll

安装 dot工具

brew install gobject-introspection
brew install libuv
brew install graphviz

使用dot工具生成控制流(CFG)图

进入dot文件的目录, 运行以下命令, 即可生成 png 格式的图片

dot ._ZN4main12test_if_else17h9c4880375b32a152E.dot -T png -o test_if_else.png