LLVM - 学习笔记一

2023-10-26

1. 工具和库

LLVM中的独立工具:

  • opt:在IR级对程序进行优化的工具,输入必须是LLVM的bitcode,生成的输出文件必须具有相同的类型。
  • llc:通过特定后端将LLVM bitcode转换成目标汇编或目标问价的工具。
  • llvm-mc:能够汇编指令并生成像ELF、MachO、PE等对象格式的目标文件,也可以反汇编相同的对象,从而转存这些指令的相应汇编信息和内部LLVM机器指令数据结构。
  • lli:LLVM IP的解释器和JIT编译器。
  • llvm-link:将几个LLVM bitcode链接在一起,产生一个包含你所有输入的LLVM bitcode。
  • llvm-as:将人工可读的LLVM IR文件转换为LLVM bitcode。
  • llvm-dis:将LLVM bitcode解码成LLVM汇编码。

LLVM中的基本库:

  • libLLVMCore:该库包含LLVM IR相关的所有逻辑:IR构造以及IR校验器,以及负责编译器中各种编译流程。
  • libLLVMAnalysis:包含几个IR分析过程,如别名分析、依赖分析和指令简化等。
  • libLLVMCodeGen:该库实现与目标目标无关的代码生成和机器级别的分析和转换。
  • libLLVMTarget:该库通过通用目标抽象来提供对目标机器信息的访问接口。
  • libLLVMX86CodeGen:该库具有特定于X86目标的代码生成信息、转换和分析过程,组成x86后端。
  • libLLVMSupport:该库包含一个通用工具集合。
  • libclang:该库实现了一个C接口,它是LLVM代码的默认实现语言。

2. Clang

1. Clang简介

Clang是LLVM编译器的c-family前端,Clang可能意指三种不同的实体:

  • 前端(在Clang库中实现)
  • 编译器驱动程序(在clang命令和Clang驱动程序库中实现)
  • 实际的编译器(在clang -cc1命令中实现)。clang -cc1中的编译器不仅是由Clang库实现,还广泛地使用其他LLVM库来实现编译器的中端、后端以及集成的汇编器。

例如,对于如下size.c示例:

#include<stdio.h>
typedef struct _PixelPacket{
	char rt, gt, ot;
}PixelPacket;

int main(){
    printf("%lu\n",sizeof(PixelPacket));
    return 0;
}

可使用clang命令来生成它的bitcode文件、汇编文件或是前端信息,如下:
clang -emit-llvm -c size.c -o size.bc
clang -emit-llvm -S -c size.c -o size.ll
clang -mllvm -print-after-all size.c -S -emit-llvm &> log.txt

-emit-llvm标记会告诉clang根据是否存在-c-S来生成LLVM bitcode或是LLVM汇编码等信息。

也可使用clang -cc1工具的一个特殊选项打印Clang的AST,如下(手动调用clang -cc1需要使用-I指定头文件位置):
clang -Xclang -ast-dump (-fsyntax-only) size.c
clang -cc1 -ast-dump (-fsyntax-only) size.c -I xx/xx/stdio.h
t1

  clang -cc1工具不仅实现了编译器的前端,还通过LLVM库实例化了其它LLVM组件,以执行LLVM支持的所有编译功能。即clang -cc1几乎实现了完整的编译器。可使用-###标志来显示由Clang驱动程序调用的程序列表,如下:
clang -### size.c -o size.o
t2

2. Clang前端阶段

  源码转换为LLVM IR bitcode的过程需要经过前端解析,包含:预处理、词法分析、语法分析、语义分析一系列过程。以size.c代码为例,前端的第一个步骤是负责处理源代码的文本输入,它将语言结构拆分成一组单词和记号,并删除注释、空格、制表符等字符。每个记号包含一个SourceLocation类的实例,用于表示程序源代码的位置,可使用clang -cc1输出词法分析中的所有行号和SourceLocation结果,如下:
clang -cc1 -dump-tokens size.c
t3

  C/C++预处理在进行任何语义分析之前使用,负责通过处理以#开头的预处理指令来展开宏、头文件,或者跳过部分代码。预处理器与词法分析器紧密相连,并连续相互作用。

  在词法分析把源码解析成记号之后,编译器会进行语法分析,并将记号组合到一起形成表达式、语句和函数体等。这种分析过程也称为解析,它的输入为一个记号流,输出为AST。AST中最顶层的节点是TranslationUnitDeclare类。它是所有其它AST节点的根,代表整个翻译单元。
  以size.c代码为例,打印其AST视图如下(使用Xmanager打开Xshell进行操作):clang -fsyntax-only -Xclang -ast-view size.c

  语义分析借助于符号表来确保代码是否违反编程语言的类型系统。该表主要存储了标识符与其各自类型之间的映射。一种直观的类型检查方法是在解析之后执行它,具体做法是从符号表中收集有关类型的信息同时遍历AST。但Clang它在生成AST节点的同时执行类型检查。

3. LLVM中间表示

LLVM IR有三种等价的表达形式:内存表示(Instruction类等)、被压缩的磁盘表示(bitcode文件)、人工可读的磁盘表示(LLVM汇编码文件)

1. 操作IR格式的基本工具示例
  1. 打印所有IR信息:llc -O0 -print-after-all size.ll
    t4

  2. 查看某种pass之后的IR信息:clang -mllvm -print-after=pass名称 size.c -S -emit-llvm

  3. 打印CFG(使用Xmanager打开Xshell进行操作):opt -view-cfg size.bc

  4. 使用llvm-dis查看bitcode汇编文本:llvm-dis < size.bc | less

  5. LLC编译bitcode为汇编码:llc size.bc -o size.s

  6. .bc和.ll相互转换:llvm-as size.ll -o size.bcllvm-dis size.bc -o size.ll

  7. llvm提取工具llvm-extract提取IR函数、全局变量、删除IR模块中的全局变量:llvm-extract -func=main size.bc -o size-main.bc

2. 编译时,链接时优化

  优化标志一般包含:-O0-O1-O2-O3-O4-Os-Oz-Ofast。其中-Os类似于-O2,但它优化后减少了代码大小,-Oz类似于-Os,但它进一步优化使得代码大小更小,-O4是使用-flto-O3-Ofast是在-O3上开启了更多的激进优化有一些需要严格语法检查的优化。Clang支持-O4优化,但opt工具不支持,opt工具的处理对象是bitcode文件。
  使用opt来优化bitcode文件,示例如下:
opt -O3 size.bc -o size-O3.bc
opt -std-compile-opts size.bc -o size-std.bc

  opt工具也支持单独的代码优化流程,其中mem2reg将allocs指令提升为LLVM局部变量。使用mem2reg并计算模块中每种指令的数量,示例如下:
opt size.bc -mem2reg -instcount -o size-tmp.bc -stats
t5
上述使用了-stats标志使LLVM打印出每个流程的统计信息,也可使用-time-passes标志来统计每次优化需要的执行时间,示例如下:
opt size.bc -time-passes -domtree -instcount -o size-tmp.bc
t6

4. 后端

  LLVM后端由一组代码生成分析器和变换流程组成。阶段大致分为:指令选择、指令调度(前寄存器分配调度)、寄存器分配、后寄存器分配调度、代码输出。

1. 后端代码结构

  后端实现分散在LLVM源代码树的不同目录中。代码生成的主要库在lib目录及其子文件夹CodeGenMCTableGenTarget中:

  • CodeGen目录包含所有通用代码生成算法的实现文件和头文件:指令选择、指令调度、寄存器分配以及它们所需的辅助分析函数。
  • MC目录包含汇编器、反汇编器和具体的对象文件(如:ELF、COFF、MachO)等底层功能的实现。
  • TableGen目录包含TableGen工具的完整实现,该工具用于根据.td文件中的高层目标描述来生成C++代码。
  • 每个目标平台都在Target文件夹下的不同子文件夹中实现。
2. TableGen

  TableGen语言用于生成记录的定义和类组成。定义语句def用于实例化来自关键字class和multiclass的记录。代码生成器广泛使用TableGen记录来表示特定于目标的信息。

  1. Target属性:《Target》.td文件定义后端所支持的ISA功能和处理器系列。例如,lib\Target\X86\X86.td文件中定义的部分向量化扩展:
def FeatureAVX     : SubtargetFeature<"avx", "X86SSELevel", "AVX",
                                      "Enable AVX instructions",
                                      [FeatureSSE42]>;
def FeatureAVX2    : SubtargetFeature<"avx2", "X86SSELevel", "AVX2",
                                      "Enable AVX2 instructions",
                                      [FeatureAVX]>;
def FeatureFMA     : SubtargetFeature<"fma", "HasFMA", "true",
                                      "Enable three-operand fused multiple-add",
                                      [FeatureAVX]>;

该文件也可以包括所有其它.td文件,并且作为记录特定目标信息的主要文件。

  1. 寄存器:寄存器和寄存器类被定义在《Target》RegisterInfo.td文件中,寄存器类被用于将指令的操作数与特定的寄存器组关联起来。例如,X86RegisterInfo.td中定义了16位寄存器,其常用方式如下:
// 16-bit registers
let SubRegIndices = [sub_8bit, sub_8bit_hi], CoveredBySubRegs = 1 in {
def AX : X86Reg<"ax", 0, [AL,AH]>;
def DX : X86Reg<"dx", 2, [DL,DH]>;
def CX : X86Reg<"cx", 1, [CL,CH]>;
def BX : X86Reg<"bx", 3, [BL,BH]>;
}
let SubRegIndices = [sub_8bit, sub_8bit_hi_phony], CoveredBySubRegs = 1 in {
def SI : X86Reg<"si", 6, [SIL,SIH]>;
def DI : X86Reg<"di", 7, [DIL,DIH]>;
def BP : X86Reg<"bp", 5, [BPL,BPH]>;
def SP : X86Reg<"sp", 4, [SPL,SPH]>;
}
def IP : X86Reg<"ip", 0>;
  1. 指令:指令格式在《Target》InstrFormats.td中定义,而指令在《Target》InstrInfo.td中定义。指令格式包含了二进制形式下指令中不同的编码字段,而每个指令记录都对应一条指令。每个指令或指令格式都必须是定义在include/llvm/Target/Target.td中的Instruction TableGen类的直接或间接子类。
3. 指令选择

  指令选择是将LLVM IR转换为代表目标指令的SelectionDAG节点(SDNode)的过程。第一步是从LLVM IR指令构建DAG,从而创建一个其节点执行IR操作的SelectionDAG对象。然后对这些节点执行降级、DAG组合器和合法化阶段,使其能够更容易与目标指令相匹配。指令选择流程是后端代码中执行时间最长的流程之一。

  1. SelectionDAG类:SelectionDAG类使用DAG来表示每个基本块的计算,每个SDNode对应一个指令或操作数。SelectionDAG对象有一个表示基本块入口的特殊EntryToken节点,该节点对应一个类型为Other的值,以允许节点链使用它作为起点。SelectionDAG对象同时维护对DAG图的根节点引用,该根节点位于最后一条指令之后,根节点和最后一条指令的关系也被编码为一条Other类型的值链。

  2. 降级:TargetLowering类为一个抽象接口,为实现该类的抽象接口,每个编译目标都需要声明一个《Target》TargetLowering的子类,每个目标也需要重载那些将高层次的目标降低到更低层次、更接近目标机器的函数。如图显示指令选择之前的所有处理步骤。
    t7

  3. DAG合并以及合法化:从SelectionDAGBuilder产生的输出SelectionDAG还需要经过如上图所示的一系列转换才能进行指令选择。DAG合并在每个合法化阶段后运行,以最小化SelectionDAG的冗余。DAG合法化程序与矢量合法化程序具有相同的作用,但它还负责处理对不支持类型的所有剩余操作。

  4. DAG到DAG指令选择:其目的是通过使用模式匹配将目标无关节点转换成目标相关节点。指令选择算法是一个局部算法,每次在SelectionDAG(基本块)实例上执行。


References:
  • 《LLVM编译器实战教程》
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

LLVM - 学习笔记一 的相关文章

随机推荐

  • 第一个CUDA程序-addVector

    本文主要通过对两个浮点数组中的数据进行相加 并将其结果放入第三个数组中 其算法分别在CPU GPU上分别执行 并比较了所需时间 强烈感受到GPU的并行计算能力 这里 每个数组的元素大小为30000000个 一 实现代码 cpp view p
  • 对比学习simSiam(一)--Exploring Simple Siamese Representation Learning总体理解

    1 从名字上把握 sim是我们熟知的相似的那个单词 这个Siam是孪生的意思 这里使用这个来命名应该是为了指出孪生的重要性 这里的核心其实是在提出一个思想 对比学习这种由孪生网络结构构成的无监督学习的关键其实是孪生网络 两个网络有其中一方停
  • PyQt入门(8)-常用控件(下)

    目录 1 QListWidget 2 QTreeWidget 3 QTableWidget 1 QListWidget QListWidget是一个QListView的便捷类 提供一个列表视图 大数据量的情况下QListView确实更加灵活
  • Java 第一个程序 HelloWorld

    目录 1 常用 DOS 命令 2 Path 环境变量的配置 3 HelloWorld 编写和执行 4 HelloWorld 详解 1 常用 DOS 命令 在接触集成开发环境之前 我们需要使用命令行窗口对 Java 程序进行编译和运行 所以需
  • LeetCode每日一题2021.11.21—12.01

    2021 11 21 559 N叉树的最大深度 题目 思路 深度遍历 广度优先遍历 每次出队要把队列所有的元素拿出来 代码 Definition for a Node class Node public int val vector
  • 常用的C语言学习网站

    1 C语言网 C语言网 www dotcpp com 不仅提供C语言 还包括C java 算法与数据结构等课程在内的各种入门教程 视频录像 编程经验 编译器教程及软件下载 题解博客 源码分享等优质资源 提倡边学边练边分享 同时提供对口的IT
  • windows 通过ssh连接到Linux主机

    windows 通过ssh连接到Linux主机 文章目录 windows 通过ssh连接到Linux主机 1 ssh的认识 2 ssh的安全验证 3 连接方法 4 windows 通过ssh连接到Linux主机 1 ssh的认识 SSH 为
  • pixelBook2017原系统ChromeOS改windows

    部分内容引用于CSDN博主 行走的病毒 的原创文章 遵循CC 4 0 BY SA版权协议 转载请附上原文出处链接及本声明 原文链接 https blog csdn net DZTlaila article details 103843172
  • redis

    django redis 使用 结合 django redis 配置 django settings CACHES default BACKEND django redis cache RedisCache LOCATION redis 1
  • 手机连不上 mac 的解决办法

    原文地址 http mobile 51cto com aprogram 386942 htm http www miui com thread 1413676 1 1 html 小米2及其他Android手机无法连接mac解决方案 2013
  • Intellij IDEA运行报Command line is too long解法

    报错内容 Error running ServiceStarter Command line is too long Shorten command line for ServiceStarter or also for Applicati
  • 向女性程序员致敬!

    今天是3月8日 国际妇女节 先祝我娘节日快乐 再祝广大女性们节日快乐 这里特别祝福一下程序媛们 你们冒着脱发 单身 没周末 xxxxxx等各种高风险在互联网事业中与程序猿们同甘共苦 一起撑起了互联网的半边天 而且听闻历史上第一位程序员也是女
  • 【Android开发】用户界面设计-在代码中控制UI界面

    效果图 实现方法 MainActivity package com example test import android app ActionBar LayoutParams import android app Activity imp
  • 三种电源防反接电路(二极管、PMOS)

    最近偶然看到PMOS防反接电路 感觉挺实用的 做个记录 软件 LTspice 二极管串联 以常用的5V 2A为例 常用二极管串联在电路中 在电源反接时 二极管承担所有的电压 有效防止电源反接损坏后级设备 但是 二极管上压降较大 损耗较高 使
  • 了解Golang基本数据类型

    文章目录 前言 一 整数数字 二 浮点数字 请注意 与前面的代码一样 Go 会从使用的值推断其数据类型 三 布尔型 四 字符串 五 常见转义字符 五 默认值 六 类型转换 总结 前言 Go 是一种强类型语言 这意味着你声明的每个变量都绑定到
  • Springboot日志级别

    一 开启Springboot详细日志 在application properties文件中添加以下代码 logging level root debug 二 sql打印在控制台 在application properties文件中添加以下代
  • 预测数值型数据:回归源码分析(1)

    回归模型比较简单 这里先简单介绍下 后面遇到难点再具体分析 回归的一般方法 1 收集数据 采用任意方法收集数据 2 准备数据 回归需要数值型数据 标称型数据将被转成二值型数据 3 分析数据 绘出数据的可视化二维图将有助于对数据做出理解和分析
  • 零基础入门microbit教程

    1 什么是microbit 1 micro bit 百度百科 micro bit 是一款由英国广播电视公司 BBC 推出的专为青少年编程教育设计的微型电脑开发板 2 官网介绍 The Micro bit Educational Founda
  • OC语言——点语法和成员变量的4种作用域及property和synthesize的使用

    点语法 点语法的本质还是方法调用 Person p Person new 点语法的本质还是方法调用 p age 10 p setAge 10 一 点语法注意点 implementation Person setter方法中的死循环 void
  • LLVM - 学习笔记一

    1 工具和库 LLVM中的独立工具 opt 在IR级对程序进行优化的工具 输入必须是LLVM的bitcode 生成的输出文件必须具有相同的类型 llc 通过特定后端将LLVM bitcode转换成目标汇编或目标问价的工具 llvm mc 能