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 介绍 的相关文章

  • 如何在线程创建和退出时调用函数?

    include
  • Linux 使用 boost asio 拒绝套接字绑定权限

    我在绑定套接字时遇到问题 并且以用户身份运行程序时权限被拒绝 这行代码会产生错误 acceptor new boost asio ip tcp acceptor io boost asio ip tcp endpoint boost asi
  • 如何在不使用 IDE 的情况下在 Linux 上运行 Java 项目

    我是 Java 新手 基本上 我开发了一个java项目 其中包含Eclipse中的多个Java包 该项目在我安装了 redhat Linux 的桌面上运行正常 然而 我需要在一个更强大的没有安装X11的Linux服务器 redhat ent
  • awk 在循环中使用时不打印任何内容[重复]

    这个问题在这里已经有答案了 我有一堆使用 file 1 a 1 txt 格式的文件 如下所示 A 1 B 2 C 3 D 4 并使用以下命令添加包含每个文件名称的新列 awk print FILENAME NF t 0 file 1 a 1
  • 在 docker 中重定向命令输出

    我想为我的服务器做一些简单的日志记录 它是一个在 Docker 容器中运行的小型 Flask 应用程序 这是 Dockerfile Dockerfile FROM dreen flask MAINTAINER dreen WORKDIR s
  • 在内核代码中查找函数的最佳方法[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我开始浏览内核代码 遇到的一件事是如何跟踪函数调用 结构定义等 有没有一种好的方法可以快速跳转到函数定义并退出 我尝试过 Source N
  • 执行命令而不将其保留在历史记录中[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 在进行软件开发时 经常需要在命令行命令中包含机密信息 典型示例是将项目部署到服务器的凭据设置为环境变量 当我不想将某些命令存储在命令历史记
  • 相当于Linux中的导入库

    在 Windows C 中 当您想要链接 DLL 时 您必须提供导入库 但是在 GNU 构建系统中 当您想要链接 so 文件 相当于 dll 时 您就不需要链接 为什么是这样 是否有等效的 Windows 导入库 注意 我不会谈论在 Win
  • 在 C 中使用单个消息队列是否可以实现双向通信

    我希望服务器向客户端发送一些消息 并让客户端确认它 我被分配了这个任务 我可以在 C linux 中使用单个消息队列来完成它还是我需要创建两个 谢谢 是的 可以使用 sysV 消息队列来做到这一点 从您之前的问题来看 您正在使用该队列 您可
  • python获取上传/下载速度

    我想在我的计算机上监控上传和下载速度 一个名为 conky 的程序已经在 conky conf 中执行了以下操作 Connection quality alignr wireless link qual perc wlan0 downspe
  • Linux 上的静态 Qt5 构建:部署时如何处理字体?

    我使用这些配置选项创建了 Qt 5 2 0 库的静态版本 Ubuntu 12 04 开源 确认许可 force pkg config 发布 静止的 前缀 home juzzlin qt5 无icu opengl桌面 无油嘴滑舌 辅助功能 n
  • 就分页分段内存而言的程序寿命

    我对 x86 Linux 机器中的分段和分页过程有一个令人困惑的概念 如果有人能澄清从开始到结束所涉及的所有步骤 我们将很高兴 x86 使用分页分段内存技术进行内存管理 任何人都可以解释一下从可执行的 elf 格式文件从硬盘加载到主内存到它
  • linux-x64 二进制文件无法在 linuxmusl-x64 平台上使用错误

    我正在安装Sharp用于使用 package json 的 Nodejs 项目的 docker 映像上的映像压缩包 当我创建容器时 我收到有关 Sharp 包的以下错误 app node modules sharp lib libvips
  • 如何在linux中以编程方式获取dir的大小?

    我想通过 C 程序获取 linux 中特定目录的确切大小 我尝试使用 statfs path struct statfs 但它没有给出确切的大小 我也尝试过 stat 但它返回任何目录的大小为 4096 请建议我如何获取 dir 的确切大小
  • C 语言的符号表

    我目前正在开发一种执行模式匹配的静态分析工具 我在用Flex https github com westes flex生成词法分析器 我编写了代码来管理符号表 我不太有经验C 所以我决定将符号表实现为线性链表 include
  • 使用循环在 C 中管道传输两个或多个 shell 命令

    我正在尝试执行ls wc l通过 C 语言程序 而不是使用命令行 这是我当前的工作代码 int main int pfds 2 pipe pfds pid t pid fork if pid 0 The child process clos
  • 使用 shell 脚本将行附加到 /etc/hosts 文件

    我有一个新的 Ubuntu 12 04 VPS 我正在尝试编写一个安装脚本来完成整个 LAMP 安装 我遇到问题的地方是在 etc hosts文件 我当前的主机文件如下所示 127 0 0 1 localhost Venus The fol
  • 从 Xlib 转换为 xcb

    我目前正在将我的一个应用程序从 Xlib 移植到 libxcb 但在查找有关我有时使用的 XInput2 扩展的信息时遇到了一些麻烦 libxcb 中有 XInput2 实现吗 如果是的话 在哪里可以找到文档 目前我在使用此功能时遇到问题
  • 绕过 dev/urandom|random 进行测试

    我想编写一个功能测试用例 用已知的随机数值来测试程序 我已经在单元测试期间用模拟对其进行了测试 但我也希望用于功能测试 当然不是全部 最简单的方法是什么 dev urandom仅覆盖一个进程 有没有办法做类似的事情chroot对于单个文件并
  • 如何在 *nix 中登录时运行脚本?

    我知道我曾经知道如何做到这一点 但是 如何在 unix 中登录时运行脚本 bash 可以 From 维基百科 Bash http en wikipedia org wiki Bash 28Unix shell 29 当 Bash 启动时 它

随机推荐

  • 第一次使用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