LLVM 介绍

2023-11-14

https://blog.csdn.net/weixin_38244174/article/details/82705181     

最近开始搞LLVM,下面我将从以下五个方面来介绍LLVM。分别是:(1)LLVM是什么?(2)LLVM的组成部分;(3)LLVM+Clang环境搭建;(4)LLVM的运行过程;(5)LLVM Pass的构建运行过程。

(一)LLVM是什么??
        LLVM(low level virtual machine)从本质上来说,是一个开源编译器框架,能够提供程序语言的编译期优化、链接优化、在线编译优化、代码生成。LLVM有两个特点:

     (1)LLVM有一个特定指令格式的IR语言,我们可以通过书写Pass来对其IR进行优化。

     (2)可以作为多种语言的后端,提供与编程语言无关的优化和针对多种CPU的代码生成功能。

(二)LLVM的组成部分:
      LLVM主要由Clang前端、IR优化器(Pass)和LLVM后端构成。其功能分别是:

      clang前端:将平台相关的源码生成与平台无关的IR(llvm Bitcode)。

      IR优化器:主要对IR进行优化。

      llvm后端:将优化后的IR转换为与平台相关的汇编代码或者机器码。

                                           

2.1  Clang前端:

       Clang前端以.c文件为输入,经语法词法分析后解析为抽象语法数,最后通过LLVM内联API变为LLVM IR。其功能为:词法分析器:把输入的程序代码切成token;语法分析器:接收token流解析为AST。

                                   

2.2 IR优化器:

       LLVM IR包含三种格式:一种是在内存中的编译中间语言;一种是硬盘上存储的二进制中间语言(以.bc结尾),最后一种是可读的中间格式(以.ll结尾)。这三种中间格式是完全相等的。LLVM IR是LLVM优化和进行代码生成的关键。根据可读的IR,我们可以知道再最终生成目标代码之前,我们已经生成了什么样的代码。我们通过Pass来对IR进行相应的优化。

2.3 LLVM后端:

       Llvm clang编译器主要是将各平台源代码编译成与平台无关的IR指令集,这将支撑对IR的优化及转换操作,而llvm后端的主要工作是优化IR指令,并将这些与平台无关的IR指令转换成目标设备相关的指令。

                              

        由上图所示,LLVM IR进入后端要经过pass优化,指令选择,指令调度,寄存器分配,代码布局优化以及汇编发行等过程。上述各过程都是pass优化的过程,普通(白色)pass可由用户自定义,内置(灰色)pass由一系列小的pass构成,换句话说我们可以对每一个阶段都可以进行不同程度的优化。同时无须为每个目标平台编写重复的代码。

2.3.1 指令选择

                         

     指令选择将内存中三地址结构的IR指令转换成设备相关的DAG节点,如上图所示,指令选择的主要过程有:

    (1) 构建初始DAG:该过程只是将LLVM IR指令简单的转换成不合法的SelectionDAG,利用SelectDAGISe类调用visit()函数遍历IR创建DAGNode节点

   (2)优化 SelectionDAG:识别目标平台支持的元指令

   (3)类型合法化:消除不支持的类型,利用TargetLowering接口

  (4)优化SelectionDAG:清除类型合法化后的冗余

  (5)操作合法化:将操作进行合法化

  (6)优化 SelectionDAG:消除效率低下的操作数

  (7)转换设备无关的DAG到目标DAG(DAGNode转换为MachineSDNode(目标平台机器指令))

       注:机器指令由.td文件描述。Tablegen就是用于记录这些信息的描述性语言。经过tablegen工具批量生成C++源文件。它的好处就是减少我们描述的工作量。Tablegen主要由Class(类)和Definition(定义)组成。其中Class是用于描述构建其它记录的抽象记录,可以理解成模板。描述目标的共同特点以便批量生成记录。Definition是具体的描述实例,不包含任何未定义的变量。

2.3.2 指令调度

         对DAG进行拓扑排序转换为线性指令集尽可能的利用指令级的并行操作,提高运行效率.

2.3.3 寄存器分配

         由于IR拥有多个虚拟寄存器,因此需要将输入的任意数目的寄存器重新分配为符合硬件要求的有限个数寄存器。

2.3.4 代码发行

        输出汇编代码或二进制代码

(三)LLVM+Clang环境搭建(LLVM3.3):
      参考了这篇博客:http://www.cnblogs.com/codemood/p/3142848.html

      环境:Ubuntu16.04 32位

     gcc版本号:5.3.1

     Cmake版本号:3.5.1

     3.1新建LLVM文件夹:

mkdir LLVM
     3.2下载以下五个包:llvm-3.3.src、cfe-3.3.src、clang-tools-extra-3.3.src、compiler-rt-3.3.src、libcxx-3.3.src将其解压至LLVM文件夹下。可以在这个链接或网页找资源。下载链接:https://download.csdn.net/download/weixin_38244174/10667601

      3.3然后按下面的步骤组织,这样可以将clang,clang-tools-extra和compiler-rt就可以和llvm一起编译了。

mv cfe-3.3.src clang
mv clang/ llvm-3.3.src/tools/
mv clang-tools-extra-3.3.src extra
mv extra/ llvm-3.3.src/tools/clang/
mv compiler-rt-3.3.src compiler-rt
mv compiler-rt llvm-3.3.src/projects/
     3.4 在LLVM文件夹下新建build目录:

mkdir build
cd build
     3.5 配置并编译,时间可能比较长,20min以上。

../llvm-3.3.src/configure --enable-optimized --enable-targets=host-only
make -j4
sudo make install
     3.6 验证成功与否:

clang -help
        若显示这样则成功:

                            

(四)LLVM+Clang环境搭建(LLVM5.0):
           最近项目上需要一个东西,结果LLVM3.3由于太老了,没有一些包,故而搭建LLVM5.0环境,发现5.0的坑不比3.3的少啊,特此记录更新下。
         1.新建LLVM文件夹。
         2.从LLVM官网上页面上下载clang ,llvm,clang-tools-extra-5.0.0.src,compiler-rt-5.0.0.src,libcxx-5.0.0.src。将其解压至LLVM文件夹下。或者在:https://download.csdn.net/download/weixin_38244174/10971406 下载这几个包。
         3.执行以下步骤:

mv cfe-5.0.0.src clang
mv clang/ llvm-5.0.0.src/tools/
mv clang-tools-extra-5.0.0.src extra
mv extra/ llvm-5.0.0.src/tools/clang/
mv compiler-rt-5.0.0.src compiler-rt
mv compiler-rt llvm-5.0.0.src/projects/
         4.在LLVM下新建build目录,进入build目录下。
         5. 执行

cmake ../llvm cmake -DLLVM_TARGETS_TO_BUILD=X86 -DCMAKE_BUILD_TYPE=Release -DLLVM_USE_LINKER=gold
        注意事项:
        1.../llvm可能会存在问题,直接将../llvm改为LLVM 5.0源码所在位置,直接拖进去。譬如我自己的是  '/home/hzk/LLVM/llvm-5.0.0.src'
        2.不要使用 Debug+Asserts ,如果是 cmake 使用 Release 模式,用Debug会出现很多很多奇怪的错误);
        3.如果出现:No CMAKE_CXX_COMPILER could be found.这种报错,这是因为没有安装gcc/g++。安装g++即可,安装步骤如下:
        (1).gcc:ubuntu下自带gcc编译器。可以通过“gcc -v”命令来查看是否安装。
        (2).g++:安装g++编译器,可以通过命令“sudo apt-get install build-essential”实现。执行完后,就完成了gcc,g++,make的安装。build-essential是一整套工具,gcc,libc等等。通过“g++ -v”可以查看g++是否安装成功。

        6.执行make -j4进行编译;
       注意事项:
      1.可能会出现各种错误,不用管,重新执行make -j4编译,但这种配置低的话可能会多次崩溃;
      2.推荐使用make直接进行编译。这种一次成功,但时间相对来说比较长一些。

       7.最后执行sudo make install进行安装(一定不要忘了)

(五)LLVM运行过程:
                       

(一)理论基础:

      1.如上图所示,以c文件为例,首先使用-emit-llvm命令告诉clang前端将.C文件生成llvm的IR(LLVM IR主要有三种格式:一种是在内存中的编译中间语言;一种是硬盘上存储的二进制中间语言(以.bc结尾),最后一种是可读的中间格式(以.ll结尾)。这三种中间格式是完全相等的)。

      2.生成可执行文件有两种方式:

      a)使用llc命令将IR文件(.bc文件)生成目标文件(.o文件),通过系统链接器链接多个目标文件,生成可执行文件。(针对单个IR)

       b)使用llvm-link链接多个IR,使用llc命令将IR文件生成目标文件,之后生成可执行文件。(针对多个IR)

(二)具体实践:

参考链接:https://blog.csdn.net/earbao/article/details/53421319

对源码的编译。

1.创建简单的c语言源码文件test.c

#include <stdio.h>
 
int main() {
  printf("hello llvm\n");
  return 0;
}
2.编译可执行文件

clang test.c -o test
3.生成LLVM字节码文件

clang -O3 -emit-llvm test.c -c -o test.bc
4.生成LLVM可视化字节码文件(c文件和OpenCL文件)

clang -O3 -emit-llvm test.c -S -o test.ll
 
 
clang -c -x cl -emit-llvm -S -cl-std=CL2.0 -Xclang -finclude-default-header my_kernel.cl -o my_kernel.ll
5.运行可执行文件

./test
6.运行字节码文件

lli test.bc
7.反汇编字节码文件

llvm-dis < test.bc | less
8.编译字节码为汇编文件

llc test.bc -o test.s
(六)LLVM Pass的构建运行过程:
       参考博客:https://blog.csdn.net/ZCMUCZX/article/details/80856655

       该Pass功能是:opt命令行工具会动态的去加载动态链接库,以运行Pass,之后Pass会遍历每一个函数,输出其函数名,但未对IR做任何改动。

步骤如下:

6.1 创建一个FuncBlockCount.cpp文件,内容如下:

#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
//引入llvm命名空间,可以让其实用LLVM当中的函数
using namespace llvm;
 
//创建一个匿名的命名空间
namespace  {
 
    //声明Pass
    struct FuncBlockCount : public FunctionPass
    {
        //声明Pass的标识符,会被LLVM用作识别Pass
        static char ID;
        //对父类进行初始化
        FuncBlockCount() : FunctionPass(ID){}
 
        //其实就是FunctionPass的一个虚函数,这里对它进行了实现。一个FunctionPass的子类要想做一些实际的工作,就必须对这个虚函数进行实现。
        bool runOnFunction(Function &F) override {
            //errs()是一个LLVM提供的C++输出流,我们可以用它来输出到控制台
            errs() << "Function "<<F.getName()<<'\n';
            //函数返回false说明它没有改动函数F。之后,如果我们真的变换了程序,我们需要返回一个true。
            return false;
        }
    };
}
//初始化Pass ID
char FuncBlockCount::ID = 0;
//需要注册Pass、填写名称和命令行参数
static RegisterPass<FuncBlockCount> X("funcblockcount","Function Block Count",false,false);//,最后两个参数描述了它的行为:如果一个pass不修改CFG,那么第三个参数将被设置为true;如果pass是一个分析传递,例如dominator树(支配树)传递,则true作为第四个参数提供。
6.2 使用以下命令编译so文件

g++ FuncBlockCount.cpp -fPIC -g -Wall -Wextra -std=c++11 `llvm-config --cxxflags ` -shared -o FuncBlockCount.so
6.3 测试文件为(4)中的test.ll文件;

6.4使用命令运行新Pass.

opt -load /home/kyriehe/Desktop/testpass/FuncBlockCount.so -funcblockcount test.ll
6.5若显示下图则说明运行正确。

6.6 将C语言测试代码编译为LLVM IR的形式

clang -O0 -S -emit-llvm test.c -o test.ll
————————————————
版权声明:本文为CSDN博主「勤修」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_38244174/article/details/82705181

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

LLVM 介绍 的相关文章

  • SONAR - 使用 Cobertura 测量代码覆盖率

    我正在使用声纳来测量代码质量 我不知道的一件事是使用 Cobertura 测量代码覆盖率的步骤 我按照以下步骤操作http cobertura sourceforge net anttaskreference html http cober
  • .NET Core 中的跨平台文件名处理

    如何处理文件名System IO以跨平台方式运行类以使其在 Windows 和 Linux 上运行 例如 我编写的代码在 Windows 上完美运行 但它不会在 Ubuntu Linux 上创建文件 var tempFilename Dat
  • 并行运行 make 时出错

    考虑以下制作 all a b a echo a exit 1 b echo b start sleep 1 echo b end 当运行它时make j2我收到以下输出 echo a echo b start a exit 1 b star
  • 在 Linux 中禁用历史记录 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 要在 Linux 环境中禁用历史记录 我执行了以下命令 export HISTFILESIZE 0 export HISTSIZE 0 u
  • 嵌入式Linux poll()不断返回

    我有一个特别的问题 当我知道没有什么可读时 民意调查不断返回 因此设置如下 我有 2 个文件描述符 它们构成fd设置民意调查监视 一种用于引脚从高到低的变化 GPIO 另一个用于代理输入 代理输入出现问题 处理的顺序是 启动main函数 然
  • ansible 重新启动 2.1.1.0 失败

    我一直在尝试创建一个非常简单的 Ansible 剧本 它将重新启动服务器并等待它回来 我过去在 Ansible 1 9 上有一个可以运行的 但我最近升级到 2 1 1 0 并且失败了 我正在重新启动的主机名为 idm IP 为 192 16
  • 在 Mac OS X 上构建 Linux 内核

    我正在做一个修改Linux内核的项目 我有一台桌面 Linux 机器 在上面构建内核没有问题 不过 我要去旅行 我想在途中工作 我只有一台 MacBook 当我尝试构建 Linux 内核时 它抱怨说elf h was not found 我
  • Unix 命令列出包含字符串但*不*包含另一个字符串的文件

    如何递归查看包含一个字符串且不包含另一个字符串的文件列表 另外 我的意思是评估文件的文本 而不是文件名 结论 根据评论 我最终使用了 find name html exec grep lR base maps xargs grep L ba
  • Linux 中的无缓冲 I/O

    我正在写入大量的数据 这些数据数周内都不会再次读取 由于我的程序运行 机器上的可用内存量 显示为 空闲 或 顶部 很快下降 我的内存量应用程序使用量不会增加 其他进程使用的内存量也不会增加 这让我相信内存正在被文件系统缓存消耗 因为我不打算
  • 使用 grep 查找包含所有搜索字符串的行

    我有一个文件 其中包含很多与此类似的行 id 2796 some model Profile message type MODEL SAVE fields account 14 address null modification times
  • gdb查找行号的内存地址

    假设我已将 gdb 附加到一个进程 并且在其内存布局中有一个文件和行号 我想要其内存地址 如何获取文件x中第n行的内存地址 这是在 Linux x86 上 gdb info line test c 56 Line 56 of test c
  • Linux中的CONFIG_OF是什么?

    我看到它在很多地方被广泛使用 但不明白在什么场景下我需要使用它 What is 配置 OF OF 的全名是什么 打开固件 这是很久以前发明的 当时苹果公司正在生产基于 PowerPC CPU 的笔记本电脑 而 Sun Microsystem
  • sendfile64 只复制约2GB

    我需要使用 sendfile64 复制大约 16GB 的文件 到目前为止我所取得的成就是 include
  • 在哪里可以找到并安装 pygame 的依赖项?

    我对 Linux 比较陌生 正在尝试安装 python 的 pygame 开发环境 当我运行 setup py 时 它说我需要安装以下依赖项 我找到并安装了其中之一 SDL 然而 其他人则更加难以捉摸 Hunting dependencie
  • Linux 中的动态环境变量?

    Linux 中是否可以通过某种方式拥有动态环境变量 我有一个网络服务器 网站遵循以下布局 site qa production 我想要一个环境变量 例如 APPLICATION ENV 当我在 qa 目录中时设置为 qa 当我在生产目录中时
  • 域套接字“sendto”遇到“errno 111,连接被拒绝”

    我正在使用域套接字从另一个进程获取值 就像 A 从 B 获取值一样 它可以运行几个月 但最近 A 向 B 发送消息时偶尔会失败 出现 errno 111 连接被拒绝 我检查了B域套接字绑定文件 它是存在的 我也在另一台机器上做了一些测试 效
  • 加载数据infile,Windows和Linux的区别

    我有一个需要导入到 MySQL 表的文件 这是我的命令 LOAD DATA LOCAL INFILE C test csv INTO TABLE logs fields terminated by LINES terminated BY n
  • 如何有效截断文件头?

    大家都知道truncate file size 函数 通过截断文件尾部将文件大小更改为给定大小 但是如何做同样的事情 只截断文件的尾部和头部呢 通常 您必须重写整个文件 最简单的方法是跳过前几个字节 将其他所有内容复制到临时文件中 并在完成
  • linux perf:如何解释和查找热点

    我尝试了linux perf https perf wiki kernel org index php Main Page今天很实用 但在解释其结果时遇到了困难 我习惯了 valgrind 的 callgrind 这当然是与基于采样的 pe
  • 添加要在给定命令中运行的 .env 变量

    我有一个 env 文件 其中包含如下变量 HELLO world SOMETHING nothing 前几天我发现了这个很棒的脚本 它将这些变量放入当前会话中 所以当我运行这样的东西时 cat env grep v xargs node t

随机推荐

  • 第一次使用Eclipse:编写简单的Java小程序

    通过前部分的学习 了解了Java的安装和配置 那么从现在开始 要开始自己着手编写Java程序 学习一门编程语言 学会编写的第一个程序一般都是写一个输出 hello World 语句的小程序来表示自己开始学习这门语言 那么这篇教程也不例外 因
  • fsnotify 与 too many open files

    fsnotify fsnotify 是用来监听文件 目录变化的一个 golang 开源库 在 Linux 系统使用中 遇到了too many open files问题 首次尝试 通常 有 2 处配置太小 会触发too many open f
  • 最惊艳的sql

    select from girls where age between 18 and 20 and boyfriend is null order by cup desc
  • 不管人工智能发展如何 开发者都有必要了解 Linux 内核

    Linux内核在计算机世界的地位有目共睹 称它为计算机世界的基石也不为过 而且它还是全球最大的开源项目 几乎最知名的科技公司都参与其中 包括谷歌 Red Hat SUSE Intel Facebook 甲骨文和华为等 当然还包括Linux的
  • 将cmd中输出数据 保存为TXT文本

    原文 http blog sina com cn s blog 6d2d58cd0100x7zw html 在使用Windows XP中的cmd exe工具时 有时候我们想要把我们的输入命令及结果保存起来 但是用复制的方法过于麻烦 有时输出
  • LeetCode 热题 HOT 100:滑动窗口专题

    LeetCode 热题 HOT 100 https leetcode cn problem list 2cktkvj 文章目录 3 无重复字符的最长子串 128 最长连续序列 239 滑动窗口最大值 438 找到字符串中所有字母异位词 3
  • JFLex和JavaCUP简单使用

    由于需要使用到doris中的sql parser功能 所以决定使用其定义好的flex文件和cup文件 生成自己sqlscanner和parser类 步骤如下 1 下载JFlex和JavaCUP程序 路径分别为 https www jflex
  • 机械制造与自动化涉及使用计算机吗,论机械设计制造及自动化中计算机技术

    将计算机技术运用到机械设计制造中 大大提高了机械设计制造智能化水平 在机械设计制造中占据很重要的位置 但我国机械制造设计水平同国外发达国家相比 还存在一定的距离 若是可以加大对计算机技术的研究和探索 对机械制造行业的发展是非常有利的 1机械
  • Flowable入门系列文章29 - Activity解读 05

    1 消息开始事件 描述 甲消息开始事件可用于使用已命名的信息来启动一个过程实例 这有效地允许我们使用消息名称从一组替代开始事件中选择正确的开始事件 在部署具有一个或多个消息启动事件的流程定义时 应考虑以下注意事项 消息开始事件的名称在给定的
  • 机器学习实战:Python基于支持向量机SVM-RFE进行分类预测(三)

    文章目录 1 前言 1 1 支持向量机的介绍 1 2 支持向量机的应用 2 demo数据集演示 2 1 导入函数 2 2 构建数据集拟合 2 3 预测模型及可视化 3 实例演示分类 非SVM 3 1 导入函数和数据 3 2 简单线性分类 3
  • 剑指offer Java实现 第五题

    第五题 请实现一个函数 将一个字符串中的每个空格替换成 20 例如 当字符串为We Are Happy 则经过替换之后的字符串为We 20Are 20Happy 实现代码 public static String replaceSpace
  • MSCOCO数据集格式转化成VOC数据集格式

    MSCOCO数据集格式转化成VOC数据集格式 转载请注明原出处 http blog csdn net ouyangfushu article details 79543575 作者 SyGoing QQ 2446799425 SSD目标检测
  • [springmvc学习]8、JSR 303验证及其国际化

    目录 简介 常见注解 基本使用 BindResult获取异常信息 自定义提示信息 取消属性绑定 总结 简介 JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架 它已经包含在 JavaEE 中 我们可以通过注解的方式来指
  • SFTP报错,sftp couldn‘t stat remote file:No such file or directory

    原因 使用sftp进行文件传输时 需要连接到远程服务器的root用户上去 这就导致了另一个问题 在命令行使用su命令并输入root用户密码可以切换到root用户 但是使用sftp连接root用户 会连接失败 同类型的问题也有使用xshell
  • IDE介绍

    集成开发工具 gt gt gt IDE 编码工具取代了简单的记事本工具 辅助程序员编写源代码的常用高效编写工具 类似word 我们写文档会打开word文档来编写 代码也同样需要借助工具来开发 常见的编辑工具有记事本 sublime text
  • SD HOST——(一)SD简介

    Micro SD有九个引脚 TF卡只要八个 少一个地 CLK CMD 双向口 用于发命令和接收response VDD GND GND D3 D2 D1 D0 D3 D0不一定传输的是数据 读SD内部寄存器状态也可以从D3 30输出 CMD
  • Pytorch并行训练方法-单机多卡

    简单方便的 nn DataParallel DataParallel 可以帮助我们 使用单进程控 将模型和数据加载到多个 GPU 中 控制数据在 GPU 之间的流动 协同不同 GPU 上的模型进行并行训练 细粒度的方法有 scatter g
  • 通过栈实现算术表达式的计算

    最近在看数据结构的栈 其中有一节为栈应用到算术表达式的计算 接下来我讲举例说明如何用栈去计算 如有不对的地方 请各位大神指教 1 定义操作符的优先级 作为栈顶操作符时优先级仅高于 作为栈顶操作符时优先级是最高的 和 优先级一样 但是一个作为
  • redis-cluster集群添加或删除节点以及槽重新分片

    Redis版本 5 0 0 redis 5 0 0版本后可以不用安装ruby环境 集群命令说明 redis cli cluster help Cluster Manager Commands create host1 port1 hostN
  • LLVM 介绍

    https blog csdn net weixin 38244174 article details 82705181 最近开始搞LLVM 下面我将从以下五个方面来介绍LLVM 分别是 1 LLVM是什么 2 LLVM的组成部分 3 LL