Giter VIP home page Giter VIP logo

llvm-ir-tutorial's Introduction

LLVM IR入门指南

本仓库是我写的LLVM IR入门指南。

推荐前往https://Evian-Zhang.github.io/llvm-ir-tutorial阅读以获得最佳排版及语法高亮体验。PDF版本下载请点击前述网页的右上角。本教程中涉及的大部分代码也都在本GitHub仓库Evian-Zhang/llvm-ir-tutorial中。

LLVM是当下最通用的编译器后端之一,无论是想自己动手制作一个编译器,还是为主流的编程语言增加功能,又或者是做软件的静态分析,都离不开LLVM。LLVM IR是LLVM架构中一个重要的组成成分,编译器前端将抽象语法树转变为LLVM IR,而编译器后端则根据LLVM IR进行优化,生成可执行程序。但是,目前对LLVM IR的中文介绍少之又少。因此,我就写了这样的一系列文章,介绍了LLVM的架构,并且从LLVM IR的层面,让大家系统地了解LLVM。

最近(2023年6月),这个GitHub仓库的star数即将达到一千。因此,我打算基于现有的脉络,大范围更新现有的文章,推出LLVM IR入门指南2.0版(如需查看1.0版,请移步v1分支)。我这次更新的原因主要有以下几点:

  • LLVM版本更新

    LLVM是一个不断演进的,由社区积极维护的项目。本系列文章写作于2020年,会有一些落后的知识点,也会有一些新的技术没有涵盖到。因此,本次更新将针对最新的LLVM 16进行写作。

  • 操作系统变化

    在1.0版中,我是以macOS为操作系统来介绍的。但是,随着Apple Silicon Mac占据主流地位,使用Intel芯片的Mac越来越少。由于文章中会大量地使用LLVM IR编译后的AMD64汇编指令作为解释说明,所以本次更新将从macOS转变为Linux,从而也可以使更多的读者能够在自己的机器中对文章中的代码进行验证。

  • 知识水平提升

    在过去的这三年里,我也学习了很多新的知识。并且,我也写出了不少关于底层二进制相关的系列文章,如:

    在这些经验的积累下,我可以更具体、更准确地对文章的内容进行修补。

本人水平有限,写此系列文章仅希望与大家分享学习经验,文章中必有缺漏、错误之处,望方家不吝斧正,与大家共同学习,共同进步,谢谢大家!

前置知识

本系列文章所需的前置知识包括

  • 掌握Linux基本命令(如使用命令行等)
  • 掌握C语言编程知识
  • 掌握AMD64指令集汇编知识

环境

本系列文章使用的环境包括

  • CPU

    Intel i9-12900K

  • 操作系统

    Ubuntu 22.04,内核为Linux 5.19.0

  • 编译器

    C语言采用Clang 16编译器。LLVM采用LLVM 16版本。

本地生成可阅读版本

本系列文章可以在GitHub Pages https://Evian-Zhang.github.io/llvm-ir-tutorial上阅读并生成PDF。如果想在本地离线阅读,可以按照如下步骤生成可阅读版本。

1. 克隆本仓库

git clone https://github.com/Evian-Zhang/llvm-ir-tutorial && cd llvm-ir-tutorial

2. 准备语法高亮文件

由于MDBook自带的highlightjs并不支持本书的所有语法高亮,所以需要按照其官方文档中Custom theme的步骤准备自定义的语法高亮文件:

git clone https://github.com/highlightjs/highlight.js && cd highlight.js
npm install
node tools/build.js \
   bash \
   c \
   llvm \
   x86asm \
   plaintext \
   shell \
   rust
cd ..
mkdir -p theme
cp highlight.js/build/highlight.min.js theme/highlight.js

3. 使用MDBook生成最终文档

使用MDBook,其可通过cargo install mdbook进行安装。

mdbook build

生成的文档将位于book/目录下。

同时,也可以使用

mdbook serve

在本地启动一个服务器,从而可以更方便地在浏览器中阅读。

License

本仓库遵循CC-BY-4.0版权协议
作为copyleft的支持者之一,我由衷地欢迎大家积极热情地参与到开源社区中。Happy coding!

llvm-ir-tutorial's People

Contributors

1ambd4 avatar evian-zhang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

llvm-ir-tutorial's Issues

ignore the first `i64 0` in the example of `my_structs[2].y[3]`

When I read this part, I found the example at the end is confusing.

In this block:

%MyStruct = type {
	i32,
	[5 x i32]
}
%my_structs = alloca [4 x %MyStruct]

%1 = getelementptr [4 x %MyStruct], [4 x %MyStruct]* %my_structs, i64 2, i32 1, i64 3

The last line maybe wrong, it ignores the first i64 0. Here is what I compiled

%struct.MyStruct = type { i32, [5 x i32] }

@my_structs = dso_local global [4 x %struct.MyStruct] zeroinitializer, align 16

; Function Attrs: mustprogress noinline norecurse nounwind optnone uwtable
define dso_local noundef i32 @main() #0 {
  %1 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  %2 = load i32, i32* getelementptr inbounds ([4 x %struct.MyStruct], [4 x %struct.MyStruct]* @my_structs, i64 0, i64 2, i32 1, i64 3), align 4
  ret i32 %2
}

That's a small problem, so I think maybe you ignored that. Fixing it may be helpful for people who first read this.

GEP指令的例子似乎有问题

第四章,对于getelementptr的讲解,
%MyStruct = type { i32, i32 } %my_structs = alloca [4 x %MyStruct] %1 = getelementptr [4 x %MyStruct], [4 x %MyStruct]* %my_structs, i64 2, i32 1 ; %1 is pointer to my_structs[2].y %2 = load i32, i32* %1 ; %2 is value of my_structs[2].y

这里,似乎少了一个索引0? 应该是%1 = getelementptr [4 x %MyStruct], [4 x %MyStruct]* %my_structs, i64 0, i64 2,
i32 1

好像有个小错误

%stack_variable = alloca i32
%1 = add i32 1, 2
store i32 %1, i32* %stack_variable
%2 = add i32 3, 4
store i32 %1, i32* %stack_variable
为什么最后一行是%1,我觉得好像是2%。
很感谢作者的教程,学到很多

关于第3章第2节的一些疑问

参考文档:mpx-linux64-abi.pdf p16-p17

This subsection discusses usage of each register. Registers %rbp, %rbx and
%r12 through %r15 “belong” to the calling function and the called function is
required to preserve their values. In other words, a called function must preserve
these registers’ values for its caller. Remaining registers “belong” to the called
function.5 If a calling function wants to preserve such a register value across a
function call, it must save the value in its local stack frame.

我的理解是:

  • callee saved寄存器,是rbp, rbx, r12-r15,由被调用者保证这些寄存器的值不发生变化。从调用者角度看,是没有被保留的寄存器,或不需要保留的寄存器
  • caller saved寄存器,是除callee saved寄存器以外的寄存器,由调用者保证这些寄存器的值不发生变化。从调用者角度看,是被保留的寄存器,或需要保留的寄存器(remaining registers)。

register_test.ll汇编结果来看:

  • 优先使用的是rax寄存器,即被保留的寄存器,而不是没有被保留的寄存器

many_registers_test.ll的汇编结果来看:

  • 被保留的寄存器用完,是将callee saved寄存器的值压栈,并开始使用callee saved寄存器

原文的表述是: 如果我们把所有没有被保留的寄存器都用光了,那么LLVM IR会帮我们把这些被保留的寄存器放在栈上,然后继续使用这些被保留寄存器。
原文的表述是:当所需寄存器数量较少时,直接使用callee-saved register,即不需要保留的寄存器
原文的表述是:当callee-saved register不够时,将caller-saved register原本的值压栈,然后使用caller-saved register
原文的表述是:也就是把那些需要保留的寄存器压栈。

opt --O3 优化级别

LLVM后端优化IR 使用 opt
这里的优化级别参数为 --O3, 不是 -O3
image

clang 的 是 -O3

opt test.ll -S --O3 不会生效

LLVM IR入门指南(1)中clang -S -emit-llvm test.c 生成的.ll文件中带有attributes #0 = { optnone ......},这会导致opt跳过该函数,即opt test.ll -S --O3输出结果与test.ll相比较不会有任何改动。

已知的解决办法是直接删去该属性,想请教一下是否可以在使用clang生成IR文件时通过某个特定命令行参数直接禁止掉这个属性?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.