📜  c to llvm - C 编程语言(1)

📅  最后修改于: 2023-12-03 15:13:46.271000             🧑  作者: Mango

从 C 到 LLVM

什么是 LLVM?

LLVM 是一个开源编译器框架,可以被用于构建编译器和其他语言工具。LLVM 的名字代表“低级虚拟机”(Low-Level Virtual Machine)。这个名字可能会对初学者有些吓人,但实际上 LLVM 可以被用于很多不同层次的编译任务,包括生成汇编代码、提高代码优化、生成机器码等等。

LLVM 构建于三个基础组件之上:

  • LLVM 前端:用于处理输入的语言,例如 C、C++、Fortran 等。
  • LLVM 中间表示(IR):一种高度优化、独立于硬件的表示形式。
  • LLVM 后端:用于将 IR 转化为目标平台的机器码。
C 前端

C 语言是 LLVM 支持的主要语言之一,因此 LLVM 提供了一个 C 前端,用于将 C 语言代码转化为 LLVM IR。

以下是一个简单的 C 程序:

#include <stdio.h>

int main() {
    printf("Hello, world!\n");
    return 0;
}

我们可以使用 LLVM 提供的 Clang 编译器将该代码转化为 LLVM IR:

clang -S -emit-llvm hello.c -o hello.ll

该命令将 hello.c 文件编译为 LLVM IR 并保存到 hello.ll 文件中。以下是 hello.ll 文件的内容:

; ModuleID = 'hello.c'
source_filename = "hello.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@.str = private unnamed_addr constant [14 x i8] c"Hello, world!\0A\00", align 1

; Function Attrs: noinline nounwind optnone sspstrong uwtable
define i32 @main() #0 {
  %1 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  %2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i32 0, i32 0))
  ret i32 0
}

declare i32 @printf(i8*, ...) #1

attributes #0 = { noinline nounwind optnone sspstrong uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="true" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }

该文件中的 IR 语句可能会让初学者感到困惑,但可以看到该文件描述了一个 main() 函数,其中包含一条 printf 语句,打印 "Hello, world!"。

LLVM IR

LLVM IR 是 LLVM 的核心概念之一。它是一种与平台无关的中间表示,可以在编译器的多个阶段使用。

以下是一个简单的 LLVM IR 程序:

define i32 @add(i32 %a, i32 %b) {
  %result = add i32 %a, %b
  ret i32 %result
}

该程序定义了一个 add() 函数,接受两个 i32 类型的参数并返回它们的和。

LLVM IR 的优点之一是它的可读性。通过仔细研究 IR 代码,可以了解编译器是如何处理代码的。

C 到 LLVM IR

由于 LLVM IR 是一种与平台无关的中间表示,因此可以使用 LLVM 将 C 代码转化为 IR 代码,然后使用 LLVM 将 IR 代码转化为机器码。

以下是使用 Clang 将 C 文件转换为 LLVM IR 的命令:

clang -S -emit-llvm hello.c -o hello.ll

该命令将 hello.c 代码转化为 IR 代码,并将其保存到 hello.ll 文件中。我们可以通过查看 hello.ll 文件来了解 LLVM 是如何理解 C 代码的。

总结

通过使用 LLVM,可以将 C 代码转化为中间表示(IR),然后使用 LLVM 将中间表示转化为机器码。LLVM 的灵活性使得它可以广泛应用于编译器开发和其他编译器工具的构建。