LLVM IR 基本概念

LLVM IR 基本概念#

参考:LLVM IR 基本概念

编译器常见的作用是将源高级语言的代码编译到某种中间表示(Intermediate Representation,一般称为 IR),然后再将 IR 翻译为目标体系结构(具体硬件比如 MIPS 或 X86)的汇编语言或者硬件指令。 LLVM IR 提供了一种抽象层,使程序员可以更灵活地控制程序的编译和优化过程,同时保留了与硬件无关的特性。通过使用 LLVM IR,开发人员可以更好地理解程序的行为,提高代码的可移植性和性能优化的可能性。

LLVM IR 示例与语法#

编写简单的 C 语言程序,并将其编译为 LLVM IR。

temp_dir = ".temp"
!mkdir -p $temp_dir
%%file $temp_dir/test.c
#include <stdio.h>

void test(int a, int b)
{
    int c = a + b;
}

int main(void)
{
    int a = 10;
    int b = 20;
    test(a, b);
    return 0;
}
Writing .temp/test.c

使用 Clang 编译器将 C 语言源文件 test.c 编译成 LLVM 格式的中间代码。具体参数的含义如下:

  • clang:Clang 编译器

  • -S:生成汇编代码而非目标文件

  • -emit-llvm:生成 LLVM IR 中间代码

  • test.c:要编译的 C 语言源文件

!clang -S -emit-llvm $temp_dir/test.c -o $temp_dir/test.ll

在 LLVM IR 中,所生成的 .ll 文件的基本语法为:

  • 指令以分号 ; 开头表示注释

  • 全局表示以 @ 开头,局部变量以 % 开头

  • 使用 define 关键字定义函数,在本例中定义了两个函数:@test@main

  • alloca 指令用于在堆栈上分配内存,类似于 C 语言中的变量声明

  • store 指令用于将值存储到指定地址

  • load 指令用于加载指定地址的值

  • add 指令用于对两个操作数进行加法运算

  • i32 32 位 4 个字节的意思

  • align 字节对齐

  • ret 指令用于从函数返回

Hide code cell content

!cat $temp_dir/test.ll
; ModuleID = '.temp/test.c'
source_filename = ".temp/test.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

; Function Attrs: noinline nounwind optnone uwtable
define dso_local void @test(i32 noundef %0, i32 noundef %1) #0 {
  %3 = alloca i32, align 4
  %4 = alloca i32, align 4
  %5 = alloca i32, align 4
  store i32 %0, ptr %3, align 4
  store i32 %1, ptr %4, align 4
  %6 = load i32, ptr %3, align 4
  %7 = load i32, ptr %4, align 4
  %8 = add nsw i32 %6, %7
  store i32 %8, ptr %5, align 4
  ret void
}

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @main() #0 {
  %1 = alloca i32, align 4
  %2 = alloca i32, align 4
  %3 = alloca i32, align 4
  store i32 0, ptr %1, align 4
  store i32 10, ptr %2, align 4
  store i32 20, ptr %3, align 4
  %4 = load i32, ptr %2, align 4
  %5 = load i32, ptr %3, align 4
  call void @test(i32 noundef %4, i32 noundef %5)
  ret i32 0
}

attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }

!llvm.module.flags = !{!0, !1, !2, !3, !4}
!llvm.ident = !{!5}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 8, !"PIC Level", i32 2}
!2 = !{i32 7, !"PIE Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 2}
!4 = !{i32 7, !"frame-pointer", i32 2}
!5 = !{!"Ubuntu clang version 20.0.0 (++20240928031251+29d0a8470426-1~exp1~20240928151427.1956)"}