LLVM 学习(一) -初识LLVM

LLVM 学习(一) -初识LLVM

@2022-04-16 10:09:06

@sizaif

img

LLVM安装&环境搭建

  1. 官网下载预编译的二进制文件
  2. 修改环境变量指向 export PATH="$PATH:/usr/local/clang+llvm/bin"
  3. 检查使用
root@0187031113b5:/home/workhome# env | grep PATH
PATH=/usr/local/clang+llvm/bin:/root/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
root@0187031113b5:/home/workhome# clang --version
clang version 14.0.0
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/local/clang+llvm/bin
root@0187031113b5:/home/workhome# llvm-as --version
LLVM (http://llvm.org/):
  LLVM version 14.0.0
  Optimized build.
  Default target: x86_64-unknown-linux-gnu
  Host CPU: skylake
root@0187031113b5:/home/workhome# 

LLVM架构

  • 使用可重用的组件构建的新的编译器体系结构
  • 将现有语言重新设置到JIT或静态编译

image-20220416110553226

其主要组件是前端,优化器和后端。前端解析源代码,检查其错误,并构建特定于语言的抽象语法树(AST)以表示输入代码。AST可选地将其转换为新的优化表示,并且优化器和后端在代码上运行。

优化器负责进行各种各样的转换,以改进代码的运行时间,例如消除冗余计算,并且通常或多或少地独立于语言和目标。

后端(也称为代码生成器)然后将代码映射到目标指令集。除了编写正确的代码外,它还负责生成利用受支持体系结构的特殊特性的好代码。编译器后端的公共部分包括指令选择、寄存器分配和指令调度。

LLVM的设计架构

image-20220416115305489

支持一种新的编程语言只需重新实现一个前端,支持一种新的目标架构只需重新实现一个后端,前端和后端连接枢纽就是LLVM IR。

LLVM的含义

在不同的语义环境下,LLVM具有以下几种不同的含义:

  • LLVM基础架构:即一个完整编译器项目的集合,包括但不限于前端、后端、优化器、汇编器、链接器、libc++标准库、Compiler-RT和JIT引擎
  • 基于LLVM构建的编译器:部分或完全使用LLVM构建的编译器
  • LLVM库:LLVM基础架构可重用代码部分
  • LLVM核心:在LLVM IR上进行的优化和后端算法
  • LLVM IR:LLVM中间表示

LLVM基础架构的组成部分

  • 前端:将程序源代码转换为LLVM IR的编译器步骤,包括词法分析器、语法分析器、语义分析器、LLVM IR生成器。Clang执行了所有与前端相关的步骤,并提供了一个插件接口和一个单独的静态分析工具来进行更深入的分析
  • 中间表示:LLVM IR可以以可读文本代码和二进制代码两种形式呈现。LLVM库中提供了对IR进行构造、组装和拆卸的接口。LLVM优化器也在IR上进行操作,并在IR上进行了大部分优化。
  • 后端:负责汇编码或机器码生成的步骤,将LLVM IR转换为特定机器架构的汇编代码或而二进制代码,包括寄存器分配、循环转换、窥视孔优化器、特定机器架构的优化和转换等步骤

下面这张图展示了使用LLVM基础架构时各个组件之间的关系

img

除此之外,各个组件之间的协作关系也可以以下面这种方式组织

img

两种方式的主要区别是程序源代码内部的链接是由LLVM或系统链接器完成的还是由LLVM IR链接器完成的,前者是默认方式,后者一般在开启链接优化(Link-Time Optimization)时采用

LLVM GCC 4.2 设计

image-20220416104203344

链接LLVM和GCC编译的代码

  • 可以安全地在编译器之间混合和匹配.o文件
  • 可以安全地调用到与其他编译器一起构建的库中

image-20220416104552625

链接时间优化

  • 使用-O4对文件进行优化(例如内联、固定折叠等)
  • 可以跨语言边界进行优化

image-20220416105357043

LLVM中间数据结构

在LLVM中并不只存在LLVM IR一种中间表现形式,LLVM在不同编译阶段采用以下不同的中间数据结构:

  • LLVM IR:
  • 抽象语法树(AST):将源代码转换为LLVM IR时,Clang前端语法分析器和语义分析器的产出数据结构
  • 有向无环图(DAG):将LLVM IR转换为特定机器架构的汇编代码时,LLVM首先将其转换为有向无环图(DAG)的形式,以方便进行指令选择,然后将其转换回三地址码的形式以进行指令调度
  • MCModule类:为了实现汇编器和链接器,LLVM使用MCModule类将程序表示保存在对象文件(可重定向文件的一种,通常文件名以.o结尾)的上下文中

不同编译阶段的中间数据结构有以下两种存在方式:

  • 内存中:需要编译驱动程序的帮助,将一个阶段的输出数据结构作为下一个阶段的输入数据结构
  • 文件中:独立命令之间多数以文件为媒介进行交互,比如汇编器与链接器通过可重定向的.o对象文件进行交互

LLVM IR

(Intermediate Representation,IR)

LLVM的中间表示,本质上一种与源编程语言和目标机器架构无关的通用中间表示

LLVM IR是一种类似于RISC的低级虚拟指令集

LLVM是使用简单类型系统的强类型(例如,i32是一个32位整数,i32** 是指向32位整数的指针)

LLVM IR 不使用一组固定的命名寄存器,它使用一个名为%字符的无限临时集合

LLVM IR代被设计成三种不同的形式:内存编译器IR,磁盘二进制.bc表示(适合于即时编译器的快速加载),可读的汇编语言.ll

c代码

unsigned add1(unsigned a, unsigned b) {
  return a+b;
}

// Perhaps not the most efficient way to add two numbers.
unsigned add2(unsigned a, unsigned b) {
  if (a == 0) return b;
  return add2(a-1, b+1);
}

对应 llvmIR .ll文件

define i32 @add1(i32 %a, i32 %b) {
entry:
  %tmp1 = add i32 %a, %b
  ret i32 %tmp1
}

define i32 @add2(i32 %a, i32 %b) {
entry:
  %tmp1 = icmp eq i32 %a, 0
  br i1 %tmp1, label %done, label %recurse

recurse:
  %tmp2 = sub i32 %a, 1
  %tmp3 = add i32 %b, 1
  %tmp4 = call i32 @add2(i32 %tmp2, i32 %tmp3)
  ret i32 %tmp4

done:
  ret i32 %b
}

LLVM IR结构

img

  • Module(模块)是一份LLVM IR的顶层容器,对应于编译前端的每个翻译单元(TranslationUnit)。每个模块由目标机器信息、全局符号(全局变量和函数)及元信息组成。
  • Function(函数)就是编程语言中的函数,包括函数签名和若干个基本块,函数内的第一个基本块叫做入口基本块。
  • BasicBlock(基本块)是一组顺序执行的指令集合,只有一个入口和一个出口,非头尾指令执行时不会违背顺序跳转到其他指令上去。每个基本块最后一条指令一般是跳转指令(跳转到其它基本块上去),函数内最后一个基本块的最后条指令是函数返回指令。
  • Instruction(指令)是LLVM IR中的最小可执行单位,每一条指令都单占一行

LLVM IR头部是一些Target Information,

; ModuleID = 'add.c'
source_filename = "add.c"
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.14.0"

每一行分别是,

  • ModuleID:编译器用于区分不同模块的ID
  • source_filename:源文件名
  • target datalayout:目标机器架构数据布局
  • target triple:用于描述目标机器信息的一个元组,一般形式是<architecture>-<vendor>-<system>[-extra-info]

需要关注的是target datalayout,它由-分隔的一列规格组成

  • e:内存存储模式为小端模式
  • m:o:目标文件的格式是Mach格式
  • i64:64:64位整数的对齐方式是64位,即8字节对齐
  • f80:128:80位扩展精度浮点数的对齐方式是128位,即16字节对齐
  • n8:16:32:64:整型数据有8位的、16位的、32位的和64位的
  • S128:128位栈自然对齐

详细 https://llvm.org/docs/LangRef.html#data-layout

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值