2312llvm,01基本介绍

LLVM设计的核心是它的IR.

在把LLVMIR翻译特定汇编语言时,LLVM首先将程序变换(DAG)有向无环图,以更易选指(SelectionDAG)容易,然后变换回三地址指令,来调度指令(MachineFunction).

为了看清驱动编译程序时,调用的后续工具,用-###命令行参数:

$ clang -### hello.c -o hello

部分工具:
1,opt:IR层次优化器.输入必须是LLVM位码(编码的LLVMIR)文件,输出文件也是该类型.
2,llc:通过具体后端把LLVM位码变换为目标机器汇编语言文件或目标文件.可通过参数选择优化级别,开启调试选项,开关目标指定优化.
3,llvm-mc:为多种(如ELF,MachO,PE)目标格式汇编指令生成目标文件.也可反汇编同样目标文件,输出等价的汇编信息和内部LLVM机器指令数据结构.
4,lli:为LLVMIR实现了解释器JIT编译器.
5,llvm-link:连接若干LLVM位码,产生包含所有输入的单个LLVM位码.
6,llvm-as:把人类可读的LLVM汇编转换为LLVM位码.
7,llvm-dis:解码LLVM位码,生成LLVM汇编.

考虑简单的来自多个源文件多个函数组成的C程序.第一个源文件main.c,代码如下:

#include <stdio.h>
int sum(int x, int y);
int main() {int r = sum(3, 4);printf("r = %d\n", r);return 0;
}

第二个源文件是sum.c,代码如下:

int sum(int x, int y) {return x + y;
}

可用下面命令编译它:

$ clang main.c sum.c -o sum

然而,用独立工具也可实现相同结果.首先,用不同参数调用clang,让它为C源文件生成LLVM位码,然后不继续编译,就此停止:

$ clang -emit-llvm -c main.c -o main.bc
$ clang -emit-llvm -c sum.c -o sum.bc

-emit-llvm参数,让clang根据输入参数是-c还是-S,生成LLVM位码LLVM汇编文件.

示例中,-emit-llvm参数和-c,让clang生成LLVM位码格式的目标文件.
-flto-c组合参数同样.如果想生成可读LLVM汇编,用下面这对命令代替:

$ clang -emit-llvm -S -c main.c -o main.ll
$ clang -emit-llvm -S -c sum.c -o sum.ll

注意,不用-emit-llvm-flto参数时,-c参数用目标机器语言生成目标文件,而-S参数生成目标汇编语言文件.行为与GCC一样.

这里.bc.ll分别是LLVM位码汇编文件的扩展名.
为每个LLVM位码生成目标指定的目标文件,用系统链接器链接它们以生成可执行文件:

$ llc -filetype=obj main.bc -o main.o
$ llc -filetype=obj sum.bc -o sum.o
$ clang main.o sum.o -o sum

首先,链接这两个LLVM位码为一个最终的LLVM位码.然后,为该最终的位码生成目标指定目标文件,调用系统链接器生成可执行文件:

$ llvm-link main.bc sum.bc -o sum.linked.bc
$ llc -filetype=obj sum.linked.bc -o sum.linked.o
$ clang sum.linked.o -o sum

-filetype=obj参数指定输出目标文件,而不是输出汇编文件.

流程:C==>BC=>llc为.obj文件,再系统链接
流程:C==>BC=>llvm-link为.单个BC文件,再llc,再系统链接

调用(llc)后端前,链接IR文件,用opt工具链接时优化
llc工具也可生成汇编输出,利用llvm-mc进一步汇编.

LLVM基础库

1,libLLVMCore:包含所有LLVMIR相关的逻辑:IR构造(数据布局,指令,基本块,函数)和IR验证.还提供了管理器.
2,libLLVMAnalysis:包含若干IR分析,如别名分析,依赖分析,常量合并,循环信息,内存依赖分析,指令简化等.
3,libLLVMCodeGen:实现目标无关生成代码和机器级(低层版本LLVMIR)的分析和转换.
4,libLLVMTarget:通过抽象通用目标,访问目标机器信息.libLLVMCodeGen实现了通用后端算法,目标相关逻辑留给后面的库,而高层抽象提供交流通道.
5,libLLVMX86CodeGen:包含x86目标相关的生成代码信息,转换和分析,由它们构成了x86后端.注意,每个机器目标都有自己不同的库,如分别实现了ARMMIPS后端的LLVMARMCodeGenLLVMMipsCodeGen.
6,libLLVMSupport:包含实用工具.如错误处理,整数和浮点数处理,命令行解析,调试,文件支持,串操作等,这些是该库实现算法示例,LLVM各个组件都用它们.
7,libclangDriver:包含一套C++类.编译器驱动用它们理解类似GCC命令行参数,以编译任务,为外部工具组织参数,以完成编译.根据目标平台,可用不同编译策略.
8,libclangAnalysis:这是一系列Clang提供的包括构造CFG调用图,可达代码,安全格式化串等的前端架构分析.

libclang

libclang:(对比C++LLVM代码)它实现一套C接口以暴露Clang的前端功能:诊断报告,遍历AST,补全代码,光标和源码间映射.

C接口相当简单,用C设计接口是为了更稳定,让其它语言(如Python),可很容易地使用Clang的功能.
它仅覆盖内部LLVM组件所用的C++接口的子集.

C++实践

基类中实现通用生成代码算法,继承和多态方法抽象不同后端共同任务.这样,每个具体后端专注实现它的特性,编写少量必需函数以覆盖父类通用操作.

libLLVMCodeGen包含常见算法,libLLVMTarget包含具体机器抽象接口.下面的代码片演示了MIPS目标机器描述类是如何按LLVMTargetMachine类的子类声明的.此代码是LLVMMipsCodeGen库的一部分:


class MipsTargetMachine : public LLVMTargetMachine {MipsSubtarget Subtarget;const DataLayout DL;
...

另一例,目标无关的(所有后端公共的)分配寄存器器要知道哪些寄存器保留而不能用于分配的.
此信息依赖具体目标,不能通用的父类中确定.这可调用MachineRegisterInfo::getReservedRegs()函数来确定,每个目标必须覆盖它.
下面演示了SPARC目标如何覆盖:

BitVector SparcRegisterInfo::getReservedRegs(...) const {BitVector Reserved(getNumRegs());Reserved.set(SP::G1);Reserved.set(SP::G2);
...

此代码中,通过位向量,SPARC后端说明了哪些寄存器不能用于通用分配寄存器.

调试编译器细节

尽量使用libLLVMSupport实现的断言.
查看ARM后端代码,它修改常量池布局,重新赋值.
ARM程序常用该策略加载大型常量,因为单个大型的距离指令太远,以致指令无法访问它,来解决受限的PC相对寻址机制.如下:

//lib/Target/ARM/ARMConstantIsland趟.cpp
const DataLayout &TD = *MF->getTarget().getDataLayout();
for (unsigned i = 0, e = CPs.size(); i != e; ++i) {unsigned Size = TD.getTypeAllocSize(CPs[i].getType());assert(Size >= 4 && "Too small constant pool entry");unsigned Align = CPs[i].getAlignment();assert(isPowerOf2_32(Align) && "Invalid alignment");//验证所有常量池项是否都是`对齐的倍数`.如果不是,则要`填充`,以便`指令`保持一致.assert((Size % Align) == 0 && "CP Entry not multiple of 4 bytes!");

此代码遍历ARM常量,期望它的每个字段遵守约束.注意如何用assert控制数据语义.

插件式接口

转换分析或优化.通过LLVMAPI可在编译生命期不同阶段轻松注册任意.
管理器用来注册,调度,声明之间的依赖关系.因此,不同编译阶段都可取得PassManager类的实例.

如,目标可自由地在如分配寄存器前后,或输出汇编前生成代码若干位置,应用定制优化.如:

//lib/Target/X86/X86TargetMachine.cpp
bool X86PassConfig::addPreEmitPass() {...if (getOptLevel() != CodeGenOpt::None && getX86Subtarget().hasSSE2()) {addPass(createExecutionDependencyFixPass(&X86::VR128RegClass));...}if (getOptLevel() != CodeGenOpt::None &&getX86Subtarget().padShortFunctions()) {addPass(createX86PadShortFunctions());...}...

注意后端如何根据具体目标信息,决定是否添加某个.添加第一个前,X86目标检查是否支持SSE2多媒体扩展.
对第二个,检查是否要求特殊填充.

编写第一个LLVM项目

创建一个程序,它读入位码文件,打印程序定义的函数名,函数基本块数量.

编写Makefile

//注意制表符.
LLVM_CONFIG =llvm-config
ifndef VERBOSE
QUIET:=@
endif
SRC_DIR =$(PWD)
LDFLAGS+=$(shell $(LLVM_CONFIG) --ldflags)
COMMON_FLAGS=-Wall -Wextra
CXXFLAGS+=$(COMMON_FLAGS) $(shell $(LLVM_CONFIG) --cxxflags)
CPPFLAGS+=$(shell $(LLVM_CONFIG) --cppflags) -I$(SRC_DIR)

第一部分定义了若干编译选项Makefile变量.第一个变量决定llvm-config程序位置.

llvm-config用来打印构建要链接LLVM库外部项目的有用信息.
如,定义C++编译器的一系列选项时,注意请求Make来运行llvm-config-cxxflagsshell命令,让它打印编译LLVM项目的一系列选项.

这样,编译项目源码LLVM源码兼容.最后把变量定义一系列选项传递给编译器预处理器.

HELLO=helloworld
HELLO_OBJECTS=hello.o
default: $(HELLO)
%.o : $(SRC_DIR)/%.cpp@echo Compiling $*.cpp$(QUIET)$(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $<
$(HELLO) : $(HELLO_OBJECTS)@echo Linking $@$(QUIET)$(CXX) -o $@ $(CXXFLAGS) $(LDFLAGS) $^ `$(LLVM_CONFIG) --libs bitreader core support`

这里:

llvm-config –libs bitreader core support

-libs选项要求llvm-config提供链接器选项清单,来链接期望的LLVM库.这里,要求链接libLLVMBitReader,libLLVMCore,libLLVMSupport.

代码

完整给出代码.因为创建在LLVM趟基础设施上,代码相对较短.

#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_os_ostream.h"
#include "llvm/Support/system_error.h"
#include <iostream>
using namespace llvm;
static cl::opt<std::string> FileName(cl::Positional, cl::desc("位码 file"), cl::Required);
int main(int argc, char** argv) {cl::ParseCommandLineOptions(argc, argv, "LLVM hello world\n");LLVMContext context;std::string error;OwningPtr<MemoryBuffer> mb;MemoryBuffer::getFile(FileName, mb);Module *m = ParseBitcodeFile(mb.get(), context, &error);if (m==0) {std::cerr << "读位码错误: " << error << std::end;return -1;}raw_os_ostream O(std::cout);for (Module::const_iterator i = m->getFunctionList().begin(),e = m->getFunctionList().end(); i != e; ++i) {if (!i->isDeclaration()) {O << i->getName() << " has " << i->size() << " basic block(s).\n";}}return 0;
}

程序利用cl(cl代表命令行)名字空间的llvm工具来实现命令行接口.调用ParseCommandLineOptions函数,并声明一个cl::opt<std::string>类型的全局变量,以此说明程序接收带位码文件名类型的单个参数.

然后,实例化一个保存LLVM编译的从属数据LLVMContext对象,来让LLVM线安.MemoryBuffer类对内存块定义了个只读接口.
ParseBitcodeFile函数用它读取输入文件内容,并解析文件中的LLVMIR.错误检查完成后,遍历文件中模块所有函数.

LLVM模块类似翻译单元,它包含编码一切内容的位码文件,作为LLVM的顶端实体,下面是若干函数,然后基本块,最后是指令.

如果函数只是个声明,则忽略它.找到函数定义时,打印名字,及基本块数.

编译后,用-help参数运行,看一看已为你的程序准备好的LLVM命令行功能.然后,找个想变换为LLVMIR的C或C++文件,变换,并再用程序分析.

$ clang -c -emit-llvm mysource.c -o mysource.bc
$ helloworld mysource.bc

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/231528.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里云主导《Serverless 计算安全指南》国际标准正式立项!

日前&#xff0c;在韩国召开的国际电信联盟电信标准分局 ITU-T SG17 全会上&#xff0c;由阿里云主导的《Serverless 计算安全指南》国际标准正式立项成功。 图 1 项目信息 在现今数字化时代&#xff0c;Serverless 计算正逐渐成为云计算的一个新的发展方向&#xff0c;其灵活…

[GO]解决golang.org/x/ 下包下载不下来的问题

因为在项目中要使用到一个golang.org的包&#xff0c;但是因为墙的问题&#xff0c;官方方法已经无法使用&#xff0c;但是在github上存在一个镜像站可以使用&#xff0c;我们只需要将它克隆下来就可以正常使用了 mkdir -p $GOPATH/src/github.com/golang/ git clone https://…

【目标检测】视频输出体积太大?分析视频的编码与码率问题

在做视频目标检测时&#xff0c;发现一个问题&#xff0c;检测输出完的视频时大时小&#xff0c;有时输出体积过大&#xff0c;造成播放器播放时严重卡顿现象。本文就这一情况进行分析&#xff0c;并就该问题提出相关解决方案。 视频基础知识 隔行扫描和逐行扫描 早期电视台在…

【PHP】TP5验证规则自定义

目录 概述 独立验证 静态调用 内置规则 参考文档 概述 ThinkPHP5.0验证使用独立的\think\Validate类或者验证器进行验证。 独立验证 $rule [name > require|max:25,age > number|between:1,120,email > email, ];$msg [name.require > 名称必须,name.max…

恒创:多链路负载均衡是什么意思

多链路负载均衡是一种网络架构技术&#xff0c;它通过将流量分散到多个网络链路上&#xff0c;以提高网络的性能和可靠性。这种技术可以应用于各种场景&#xff0c;如数据中心、云计算、企业网络等。 在多链路负载均衡中&#xff0c;流量被分配到多个网络链路上&#xff0c;以…

【深度学习目标检测】十、基于yolov5的火灾烟雾识别(python,目标检测)

YOLOv5是目标检测领域一种非常优秀的模型&#xff0c;其具有以下几个优势&#xff1a; 1. 高精度&#xff1a;YOLOv5相比于其前身YOLOv4&#xff0c;在目标检测精度上有了显著的提升。YOLOv5使用了一系列的改进&#xff0c;如更深的网络结构、更多的特征层和更高分辨率的输入图…

MySQL! 一个开源的关系型数据库管理系统!

MySQL! 一个开源的关系型数据库管理系统! MySQL是一个开源的关系型数据库管理系统&#xff0c;广泛应用于各种场景&#xff0c;如网站、企业应用等。了解和配置MySQL数据库是每个开发人员的基本技能之一。本文将详细介绍MySQL的基本概念、安装、配置和使用。 一、MySQL基本概…

物联网对接使用蓝牙还是WiFi,应该如何选择?

蓝牙是一种无线技术协议&#xff0c;可促进连接设备之间短距离的数据交换。它依赖于物理邻近性并使用2.400至2.485 GHz之间的UHF&#xff08;超高频&#xff09;无线电波。蓝牙旨在创建个人区域网络&#xff08;PAN&#xff09;并在笔记本电脑、智能手机和外围设备等计算设备之…

Unity SRP 管线【第四讲:URP 阴影】

URP 全文源码解析参照 引入 在UniversalRenderer.cs/ line 505行处 此处已经准备好了所有渲染数据&#xff08;所有数据全部存储在了renderingData中&#xff09; 我们只用renderingData中的数据初设置mainLightShadows bool mainLightShadows m_MainLightShadowCasterPass…

深度学习 计算预测和真实值之间的差异

要计算预测和真实值之间的差异&#xff0c;可以首先将真实值加载到一个 pandas DataFrame 中&#xff0c;然后将预测值和真实值连接在一起&#xff0c;最后计算它们的差异。以下是一个示例代码&#xff1a;python import pandas as pd# 假设真实数据保存在 real_data_df 中&am…

【MAC】M2 安装mysql

一、docker下载地址 下载地址 二、安装docker完成 三、安装mysql 一、拉取镜像 # 拉取镜像 docker pull mysql# 或者 docker pull mysql:latest# 以上两个命令是一致的&#xff0c;默认拉取的就是 latest 版本的# 我们还可以用下面的命令来查看可用版本&#xff1a; docker…

Docker介绍,Docker安装

docker镜像仓库官网 一、Docker的基本概念 1.Docker的三大核心组件 docker 镜像 --------docker images docker 仓库---------docker registeries docker 容器---------docker containers 2.Docker 镜像 Docker镜像是运行docker容器时的只读模板&#xff0c;每一个镜像由一…

【Transformer】Transformer and BERT(1)

文章目录 TransformerBERT 太…完整了&#xff01;同济大佬唐宇迪博士终于把【Transformer】入门到精通全套课程分享出来了&#xff0c;最新前沿方向 学习笔记 Transformer 无法并行&#xff0c;层数比较少 词向量生成之后&#xff0c;不会变&#xff0c;没有结合语境信息的情…

数据加密标准DES硬件实现(Modelsim)

数据加密标准DES硬件实现 本文内容摘要理论依据和设计内容仿真结果整体代码 本文内容摘要 本文设计并验证了DES的密钥扩展通路&#xff0c;分别采用Round Based方法和Pipeline方法两种方式设计并验证DES的明文通路 理论依据和设计内容 首先&#xff0c;要了解分组密码算法及DE…

STM32为基础的模拟I2C通用8bit和16bit读取以及多字节读取

GPIO模拟I2C驱动的通用代码&#xff0c;I2C的寄存器地址有8位和16位的&#xff0c;主要解决了同一个MCU同时处理8位和16位寄存器地址芯片时候的驱动问题。 typedef enum {IIC_8BIT_BASE_ADDR,IIC_16BIT_BASE_ADDR }iic_bits_e; typedef struct {uint8_t DevAddr;uint16_t RegA…

cdr格式怎么打开?cdr文件查看工具CDR Viewer功能介绍

CDRViewer Pro for Mac是一款专业的矢量图形文件查看器&#xff0c;主要用于打开、浏览和查看CorelDRAW&#xff08;CDR&#xff09;文件。以下是该软件的主要功能和特点&#xff1a; CDR文件支持&#xff1a;CDRViewer Pro可以快速加载和显示CorelDRAW&#xff08;CDR&#x…

【面试】Java最新面试题资深开发-微服务篇(1)

问题九&#xff1a;微服务 什么是微服务架构&#xff1f;它与单体架构相比有哪些优势和劣势&#xff1f;解释一下服务发现和服务注册是什么&#xff0c;它们在微服务中的作用是什么&#xff1f;什么是API网关&#xff08;API Gateway&#xff09;&#xff1f;在微服务中它有何…

大模型(LLM)+词槽(slot)构建动态场景多轮对话系统

构建动态场景多轮对话系统 引言 在人工智能和自然语言处理领域&#xff0c;聊天机器人的开发一直是一个热点话题。近年来&#xff0c;随着大型语言模型&#xff08;LLM&#xff09;的进步&#xff0c;构建能够理解和响应各种用户需求的聊天机器人变得更加可行和强大。本文将介…

Acrel-1000DP分布式光伏系统在某重工企业18MW分布式光伏中应用——安科瑞 顾烊宇

摘 要&#xff1a;分布式光伏发电特指在用户场地附近建设&#xff0c;运行方式以用户侧自发自用、余电上网&#xff0c;且在配电系统平衡调节为特征的光伏发电设施&#xff0c;是一种新型的、具有广阔发展前景的发电和能源综合利用方式&#xff0c;它倡导就近发电&#xff0c;就…

DiffUtil + RecyclerView 在 Kotlin中的使用

很惭愧, 做了多年的Android开发还没有使用过DiffUtil这样解放双手的工具。 文章目录 1 DiffUtil 用来解决什么问题?2 DiffUtil 是什么?3 DiffUtil的使用4 参考文章 1 DiffUtil 用来解决什么问题? 先举几个实际开发中的例子帮助我们感受下: 加载内容流时,第一次加载了ABC,…