2312llvm,使用llvm的示例

原文

介绍

除此外,LLVM是编译器编写者的平台.因为其异常干净和小巧的IR(中间表示),使用LLVM编写编译器比其他系统容易得多.
作为证明,栈机的作者在大约四天内编写了整个编译器(语言定义,词法解析器,解析器,代码生成器等)!了解这一点很重要,因为它显示了使用LLVM时,可多快地获得一门新语言.
此外,这是作者使用LLVM创建的第一门语言.学习曲线包含在这四天中.
这里描述的栈机语言,类似Forth.程序是定义单词简单集合,定义只能操作栈或生成I/O.这很简单.虽然计算上是完整的,但不实用.

然而,它是完整的,它很简单,而且它没有类似C的语法,这使得它对演示目的很有用.它表明LLVM可应用至多种语言.

栈机背后的基本概念非常简单.程序操作整数(或符指针)栈.程序只能操作栈并执行有限的I/O操作.语言为你提供了几个内置单词,按有趣的方式操作栈.
以下是在栈机中编写传统"Hello,World"程序的方法:

: hello_world "Hello, World!" >s DROP CR ;
: MAIN hello_world ;

这有两个"定义"(栈机操作有定义的单词,而不是函数):MAINhello_world.
MAIN定义是标准的;它告诉栈机从哪开始.在此,按简单地调用hello_world单词定义开始MAIN.

hello_world定义告诉栈机,把"Hello,World!"串推送到栈上,并打印出来(>s),并从栈中弹出(DROP),最后打印回车符(CR).
尽管hello_world使用栈,但其净效果为空.写得好的栈机定义,应该这样.

读者练习:你如何把它变成一个单行程序

我从LLVM中学到的经验教训

注意到大多数重要的LLVMIR(中间表示)的C++类都从Value类继承.仅当我开始为栈机构建可执行式时,才完全理解该简单设计.

这确实使你的编程速度更快.考虑为以下C/C++式编译代码:(a|b)*((x+1)/(y+1)).假设在栈上,这些值按a,b,x,y的顺序排列,可在栈机中表示为:1+SWAP1+/ROT2 OR *.你可如下用LLVM编写个函数来计算此式:

Value* expression(BasicBlock*bb, Value* a, Value* b, Value* x, Value* y )
{Instruction* tail = bb->getTerminator();ConstantSInt* one = ConstantSInt::get( Type::IntTy, 1);BinaryOperator* or1 =BinaryOperator::create( Instruction::Or, a, b, "", tail );BinaryOperator* add1 =BinaryOperator::create( Instruction::Add, x, one, "", tail );BinaryOperator* add2 =BinaryOperator::create( Instruction::Add, y, one, "", tail );BinaryOperator* div1 =BinaryOperator::create( Instruction::Div, add1, add2, "", tail);BinaryOperator* mult1 =BinaryOperator::create( Instruction::Mul, or1, div1, "", tail );return mult1;
}

"好吧,但没什么大不了,"你说.但,这是件大事.原因如下.注意,我不必告诉此函数,正在传入的是哪种类型的值.它们可以是指令,常量,全局变量等.

此外,如果为此操作序列指定了错误的值,LLVM编译会立即(编译时)注意到,或LLVM验证器在运行时发现不一致.

这样,不会传递错误类型到生成程序中.确实可帮助编写总是生成正确代码编译器!
第二点是,不必担心分支,寄存器,栈变量,保存部分结果等.创建指令就是使用的值.注意,上面代码中创建的所有内容都是一个常量值和五个符号.
每个指令都是该指令结果值.这样可节省大量时间.

教训是:SSA形式非常强大:创建它的指令间没有区别.完全由LLVMIR执行.利用它来发挥你最大的优势.

终止这些块!

规则:必须用终止指令(分支,返回等)终止编译器中的所有BasicBlock(基本块).

终止指令LLVMIR语义要求.不能按函数出现的顺序,把函数中的块隐式链接在一起.一般,因为递归编写编译器,不会按执行顺序把添加到函数中.

此外,如果不终止块,可编译.但只能在LLVM验证器上失败时才会发现问题.

具体块

尽早创建你的块.编写编译器时,会遇见以下几种情况,这时,明显需要有多个块.如,C/C++中的if-then-else,switch,whilefor语句都需要多个块来在llvm中表达.规则是,尽早创建它们.
尽早终止块.避免忘记终止
getTerminator()来插入指令.许多Instruction构造器都带可选的insert_before参数.

显然,正常模式的插入指令是,在其他指令之后插入一个指令,而不是在之前.
但是,如果坚持终止指令(或在BasicBlock上使用方便的getTerminator()方法),则总是可给指令构造器的insert_before参数使用.

导致指令自动插入到终止指令前的RightPlace位置.好处是,无需知道之前哪些指令,就可传递块并把新指令插入其中.

这样的编译器设计非常干净.流程:

BasicBlock* bb = new BasicBlock();
bb->getInstList().push_back( new Branch( ... ) );
new Instruction(..., bb->getTerminator() );

为此,考虑典型的if-then-else语句(见栈机Compiler::handle_if()方法).可如下使用LLVM在单个函数中设置它:

using namespace llvm;
BasicBlock*
MyCompiler::handle_if( BasicBlock* bb, SetCondInst* condition )
{//创建块以包含`if/then/else`结构中的代码BasicBlock* then = new BasicBlock();BasicBlock* else = new BasicBlock();BasicBlock* exit = new BasicBlock();//插入`"if"`的分支指令bb->getInstList().push_back( new BranchInst( then, else, condition ) );//设置终止指令then->getInstList().push_back( new BranchInst( exit ) );else->getInstList().push_back( new BranchInst( exit ) );//填充`则部分`.为了简洁,删去了细节this->fill_in( then );//填充其他部分`..为了简洁,删去了细节this->fill_in( else );//向调用者返回一个,可用`if/then/else`构造后面的`代码`填充的块.return exit;
}

上面,调用"fill_in"方法,会添加"then""else"部分的指令.他们几乎只使用第三部分(在终止块前插入新指令).

此外,如果遇见另一个if/then/else语句,甚至可递归回handle_if,且会工作.
注意,这一切是多么的干净利落.特别是BasicBlock指令列表中的push_back方法.这些是Instruction类型的列表,它们也恰好是.
为了创建"if"分支,只需实例化要分支的块分支条件为参数的BranchInst.分支标签行为一样!
此新BranchInst终止按参数提供的BasicBlock.为让调用者在调用handle_if继续插入,创建了一个返回给调用者的"退出"块.

注意,"exit"块用作"then""else"块的终止块.保证了无论"handle_if""fill_in"干什么,最终都会到达"退出(exit)"块.

狡猾的GetElementPtrInst

1,GetElementPtrInst最后索引内容返回一个值.
2,LLVM中的所有全局变量都是指针.
3,还必须用GetElementPtrInst指令解引用指针.

即,当在(假设它是个结构或数组)全局变量查找元素时,必须首先尊重指针!因此:

std::vector index_vector;
index_vector.push_back( ConstantSInt::get( Type::LongTy, 0 );
// ...压其他索引...
GetElementPtrInst* gep = new GetElementPtrInst( ptr, index_vector );

如,假设有个类型为[24 x int]全局变量.变量自身表示该数组指针.为了引用数组,需要两个索引,而不仅是一个.

第一个(0)索引解引用指针.第二个索引数组下标.

常量很容易!

1,常量是可以是指令符号的.
2,可用ConstantInt,ConstantSIntConstantUInt类的静态"get"方法,创建经常需要的整数常量.好处是,可快速取得任意类型的整数.
3,Constant类上有个允许取类型null常量的特殊方法.初化大型数组或结构等时非常方便.

栈机语言略掉

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

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

相关文章

力扣刷题-二叉树-找树左下角的值

513 找树左下角的值 给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1: 示例 2: 思路 层序遍历 直接层序遍历,因为题目说了是最底层,最左边的值&a…

紫光FPGA DDR3 IP使用和注意事项(axi4协议)

紫光DDR3 IP使用 对于紫光ddr3 IP核的使用需要注意事情。 阅读ddr ip手册: 1、注意:对于写地址通道,axi_awvalid要一直拉高,axi_awready才会拉高。使用的芯片型号时PG2L100H-6FBG676,不同的型号IP核接口和axi的握手协…

IDEA2020关于Cannot resolve symbol ‘servlet‘报错

刚开始也配置了tomcat,但是依然报错,后来查找资料解决了 在项目下面创建一个libs文件夹,然后将tomcat / lib文件夹中的servlet-api.jar复制了过来,然后再添加到library。 具体操作步骤:

Code automatic processing

自动化处理没啥用的代码,测试下,还不错的感觉

Elasticsearch的使用总结

Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎。它能很方便的使大量数据具有搜索、分析和探索的能力。 put/post请求:http://localhost:9200/索引库名称 {"settings":{"index":{"number_of_shards":1, # 分片数量…

Axure的交互样式和情形

Axure的交互样式和情形 交互样式 Axure是一个流行的原型设计工具,它允许您创建交互式原型,模拟应用程序或网站的功能和用户界面。在Axure中,您可以设置各种交互样式来使原型更加生动和真实。 链接触发器:通过给一个元素添加链接…

风速预测(三)EMD-LSTM-Attention模型

目录 1 风速数据EMD分解与可视化 1.1 导入数据 1.2 EMD分解 2 数据集制作与预处理 2.1 先划分数据集,按照8:2划分训练集和测试集 2.2 设置滑动窗口大小为7,制作数据集 3 基于Pytorch的EMD-LSTM-Attention模型预测 3.1 数据加载&#…

记错vue3+ts require 报错

在main.js 中使用require 报错 ‘require’ is not defined 事先声明 ,可能是版本不一样,所以解决办法不一样,报错的原因是Pack.json 文件中type值不同,解决办法有两种。 目前有什么弊端,因为时间比较紧,还…

现代C++的多线程开发

前言 早期的C进行多线程编程,往往需要根据不同的系统编写不同的代码,但是在C11之后,std中已经提供了多线程的支持,所以对于不同操作系统只需要编写一次代码即可。 本文记录一次多线程开发过程中,使用的C新特性&#…

uniapp怎么获取微信步数

微信步数获取的背景 微信步数是指用户在微信运动中记录的步数数据。微信提供了开放能力,允许第三方应用获取用户授权后的微信步数数据,以便进行进一步的数据分析和展示。使用时报错:fail api scope is not declared in the privacy agreemen…

【华为机试】2023年真题B卷(python)-洞穴探险

一、题目 题目描述: 某探险队负责对地下洞穴进行探险。探险队成员在进行探险任务时,随身携带的记录器会不定期地记录自身的坐标,但在记录的间隙中也会记录其他数据。探索工作结束后,探险队需要获取到某成员在探险过程中相对于探险…

libevent服务GET/POST的简单使用

目录 1、前言2、测试demo2.1、目录结构2.2、 测试源码2.2.1、http_server.cpp2.2.2、 http_server.h 2.3、 编译2.4、 运行结果2.4.1、测试POST2.4.2 、测试GET请求 1、前言 项目开发中经常需要使用到私有协议和Qt,Android等GUI前端通信,比较常用的使用POST和GET方式…

计算机操作系统-第十八天

目录 进程调度时机 补充知识 进程调度的方式 非剥夺调度方式 剥夺调度方式 进程的切换与过程 本节思维导图 进程调度时机 进程调度(低级调度),即按照某种算法从就绪队列中选择一个进程为其分配处理机。 共有两种需要进行进程调度与…

基于junit4搭建自定义的接口自动化测试框架

随着业务的逐步稳定,对于接口的改动也会逐渐变少。更多的是对业务逻辑的优化,功能实现的完善。对于测试来说,重复繁琐的功能测试不仅效率低下,而且耗费一定的人力资源。笔者支持的信息流业务下的一个图文管理平台就是一个功能较为…

Lambda表达式的简单理解

1. 初识lambda表达式 Lambda表达式是Java SE 8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。 lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。 Lambda 表达式(Lambda exp…

网络基础试题选择题——附答案

选择题 OSI模型中,表示层的作用是? A) 数据链路B) 数据传输C) 数据格式转换D) 物理连接 IP地址的IPv4版本中,一般由几个十进制数构成? A) 2B) 4C) 6D) 8 在HTTP协议中,常用的请求方法是? A) GETB) POSTC) P…

微信scroll-view小程序实现上拉加载下拉刷新

在使用微信小程序时避免不了查询列表实现分页功能&#xff0c;在这里分享下使用croll-view实现上拉加载下拉刷新功能,如有不足请指出 创建commonPagination组件 wxml文件 <scroll-view class"scroll" style"{{style}}" scroll-top"{{scrollTop}}…

广州华锐互动:VR煤矿特殊工种作业实训帮助提高矿工的操作技能和安全意识

VR煤矿特殊工种作业实训系统为煤矿企业培训提供了全方位的支持&#xff0c;帮助提高矿工的操作技能和安全意识&#xff0c;促进煤矿企业的安全生产。 首先&#xff0c;VR煤矿特殊工种作业实训系统可以提供逼真的虚拟操作环境&#xff0c;使矿工能够身临其境地感受各种工种的作业…

计算机网络 网络层上 | IP数据报,IP地址,ICMP,ARP等

文章目录 1 网络层的两个层面2 网络协议IP2.1 虚拟互联网络2.2 IP地址2.2.1 固定分类编址方式2.2.2 无分类编制CIDR2.2.3 MAC地址和IP地址区别 2.3 地址解析协议ARP2.3.1 解析过程 2.4 IP数据报格式 3 IP层转发分组流程4 国际控制报文协议ICMP4.1 ICMP格式结构4.2 分类4.2.1 差…

学习MS Dynamics AX 2012编程开发 2. X++语言

X是用于构建Dynamics AX功能的编程语言。X是一种与C类似的面向对象编程语言。 完成本章后&#xff0c;您将能够理解X语言&#xff1b;您将知道可用的数据类型是什么&#xff0c;如何创建各种循环&#xff0c;如何比较和操作变量&#xff0c;在哪里可以找到预定义的函数&#x…