LLVM Cpu0 新后端10

news2024/12/25 9:05:02

 想好好熟悉一下llvm开发一个新后端都要干什么,于是参考了老师的系列文章:

LLVM 后端实践笔记

代码在这里(还没来得及准备,先用网盘暂存一下):

链接: https://pan.baidu.com/s/1yLAtXs9XwtyEzYSlDCSlqw?pwd=vd6s 提取码: vd6s 

一、第一节 汇编器

独立汇编器可以理解为依赖于 LLVM 后端提供的接口实现的一个独立软件,因为 LLVM 和 gcc 在这个地方的实现逻辑不一样。

在 gcc 中,编译器和汇编器是两个独立的工具,编译器,也就是 cc,只能生成汇编代码,而汇编器 as,才用来将汇编代码翻译为二进制目标代码,gcc 驱动软件 gcc 将这些工具按顺序驱动起来(还包括预处理器、链接器等),最终实现从 C 语言到二进制目标代码的功能。但是,这样的设计有个缺点,每个工具都需要先对输入文件做 parse,然后再输出时写入文件,反复多次的磁盘读写一定程度影响了编译的效率。

而在 LLVM 中,编译器后端本身就可以将中间代码(对应 gcc 中 cc 的中间表示)翻译成二进制目标文件,而不需要发射汇编代码到文件中,再重新 parse 汇编文件。当然它也可以通过配置命令行参数指定将中间代码翻译成汇编代码,方便展示底层程序逻辑。

但我们目前已经实现的这些功能,却无法支持输入汇编代码,输出二进制目标文件,虽然通常情况下已经不再需要手工编写汇编代码,但在特殊情况下,比如引导程序、调试特殊功能、需要优化性能等场合下,还是需要编写汇编代码,所以一个汇编器依然是很重要的。

显然,我们之前的章节已经把和指令相关的汇编表示都在 TableGen 中实现了,这一节中,最核心的就是实现一个汇编器的 parser,并将其注册到 LLVM 后端框架中,并使能汇编功能。并且,汇编器的核心功能在 LLVM 中也已经实现了,原理其实就是一个语法制导的翻译,我们要做的只是重写其中部分和后端架构相关的接口。

我还实现了一个额外的特性。当我们仅使用汇编器时,编译器占用的寄存器 $sw,就可以被释放出来当做普通寄存器用了,所以我们重新定义一下 GPROut 这个寄存器类别,并将 Cpu0.td 拆分成两份,将它拆分为 Cpu0Asm.td 和 Cpu0Other.td,前者会在调用汇编器时被使用到,而后者保持和之前一样的设计。

因为 $sw 寄存器是编译器用来记录状态的,如果只编写汇编代码,我们认为程序员有义务去维护这个寄存器中的值什么时候是有效的,进而程序员就可以在认为这个寄存器中值无效时,把它当做普通寄存器来使用。我们的标量寄存器有很多,多这样一个寄存器的意义并不是很大,这里依然这么做,其实是想展示一下 TableGen 机制的灵活性。

在 Cpu0 的后端代码路径下,新建一个子目录 AsmParser,在这个路径下新建 Cpu0AsmParser.cpp 用来实现绝大多数功能。

1.1 修改前的效果

llvm-mc -triple=cpu0 -filetype=obj test.s -o test.o

提示我们当前的汇编器不支持cpu0架构。llvm-mc是llvm的汇编器,能够将汇编文件.s编成目标文件.o。

1.2 修改

1.2.1 AsmParser/Cpu0AsmParser.cpp

作为一个独立的功能模块,使能它的 DEBUG 信息名称为 cpu0-asm-parser,声明一些新的 class: Cpu0AsmParser 作为核心类,用来处理所有汇编 parser 的工作,我们稍后介绍;Cpu0AssemblerOptions 这个类用来做汇编器参数的管理;Cpu0Operand 类用来解析指令操作数,因为指令操作数可能有各种不同的类型,所以将这部分单独抽出来实现。

Cpu0AsmParser 类继承了基类 MCTargetAsmParser,并重写了部分接口,而有关于汇编 parser 的详细逻辑可以参考 AsmParser.cpp 中的实现。两个比较重要的重写函数:ParseInstruction() 和MatchAndEmitInstruction()。汇编器在做 parser 时,要先做 Parse,然后再对符合语法规范的指令做指令匹配,前者的关键函数就是ParseInstruction(),后者的关键函数就是MatchAndEmitInstruction()。

在 ParseInstruction() 中,根据传入的词法记号,解析指令助记符存入 Operands 容器中,然后在后边依次解析每个操作数,也存入 Operands 中。对于不满足语法规范的输入,比如操作数之间缺逗号等这种问题,直接报错并退出。在解析操作数时,调用了 ParseOperand() 接口,这也是一个很重要的接口,专用来解析操作数,我们也重写了这个接口以适应我们的类型,尤其是地址运算符。ParseInstruction() 执行完毕后会返回到 AsmParser.cpp 中的 parseStatement 方法中,并在做一些分析后,再调用到 MatchAndEmitInstruction() 方法。

在 MatchAndEmitInstruction() 函数中,将 Operands 容器对象传入。首先调用 MatchInstructionImpl 函数,这个函数是 TableGen 参考我们的指令 td 文件生成的 Cpu0GenAsmMatcher.inc 文件自动生成的。匹配之后如果成功了,还需要做额外的处理,如果这个是伪指令,需要汇编器展开,这种指令我们设计了几条,在之后会提到,这种指令需要调用 expandInstruction() 函数来展开,后者根据对应指令调用对应的展开函数,如果不是伪指令,就调用 EmitInstruction() 接口来发射编码,这个函数与我们前边章节设计指令输出的接口是同一个,也就是说在汇编 parser 之后的代码,是复用了之前的代码。匹配如果失败了,则做简单处理并返回,这里我们只实现了几种简单的情况,如果你的后端有一些 TableGen 支持不了的指令形式,也可以在这里做额外的处理,不过还是尽量去依赖 TableGen 的匹配表为好。

在 ParseOperand() 函数中,将前边 parse 出来的 Operands 容器对象传入。首先调用 MatchOperandParserImpl() 函数来 parse 操作数,这个函数也是 Cpu0GenAsmMatcher.inc 文件中定义好的。如果这个函数 parse 成功,就返回, 否则继续在下边完成一些自定义的 parse 动作,在一个 switch 分支中,根据词法 token 的类型来分别处理。其中,对于 Token,可能是一个寄存器,调用 tryParseRegisterOperand() 函数来处理,如果没有解析成功,则按照标识符处理;对于标识符、加减运算符和数字等 Token 的情况,统一调用 parseExpression() 来处理;对于百分号 Token,表示可能是一个重定位信息,比如 %hi($r1),则调用 parseRelocOperand() 函数来处理。

其他函数就不一一说明了,其中包括很多在 parse 操作数时,不同的操作数下的特殊处理,还有伪指令的展开动作,重定位操作数的格式解析以及生成重定位表达式,寄存器、立即数的 parse,还有汇编宏指令的解析(比如 .macro, .cpload 这一类)。

在最后,这些代码都实现完毕后,需要调用 RegisterMCAsmParser 接口将汇编 parser 注册到 LLVM 中,这个步骤写入到 LLVMInitializeCpu0AsmParser() 函数中。

1.2.2 Cpu0RegisterInfoGPROutForAsm.td

在这个文件中,我们定义的 GPROut 类别是支持完整的 CPURegs 的。

1.2.3 Cpu0RegisterInfoGPROutForOther.td

在这个文件中,我们定义的 GPROut 类别不包含 $sw 寄存器。

1.2.4 Cpu0Asm.td

由 Cpu0.td 拆分出来的文件,和 Cpu0Other.td 对应。

1.2.5 Cpu0Other.td

由 Cpu0.td 拆分出来的文件,和 Cpu0Asm.td 对应。

1.2.6 Cpu0.td

删掉 Target.td、Cpu0RegisterInfo.td 文件的包含。添加汇编器 parser 在 td 中的定义,并注册到 Cpu0 的属性中。这些都是常规操作。

1.2.7 Cpu0InstrFormats.td

增加针对伪指令的描述性 class,继承自 Cpu0Pseudo 类。

1.2.8 Cpu0InstrInfo.td

增加 Operand 操作数 class 中 ParserMatchClass 和 ParserMethod 属性的描述,只有这样,td 中的操作数才会支持汇编 parse。

定义伪指令 LoadImm32Reg, LoadAddr32Reg, LoadAddr32Imm,这几个指令会在 Cpu0AsmParser.cpp 中实现对应的展开函数 expandLoadImm(), expandLoadAddressImm 和 expandLoadAddressReg,这些函数统一放到 expandInstruction() 中管理,后者在 MatchAndEmitInstruction() 函数中被调用。

1.2.9 Cpu0RegisterInfo.td

将 GPROut 的定义移动到 Cpu0RegisterInfoGPROutForAsm.td 和 Cpu0RegisterInfoGPROutForOther.td 中。

1.3 修改后效果

用上述命令能正确生成.o文件,我们还可以使用之前适配的反汇编器进行反汇编查看。

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

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

相关文章

GitLab代码导出 gitlab4j-api 实现

目录 GitLab简介 GitLab 的主要特点包括: GitLab代码导出 gitlab4j-api 添加 gitlab4j-api 依赖 使用 gitlab4j-api 获取特定命名空间下的所有项目 说明 注意事项 GitLab简介 GitLab 是一个开源的代码仓库和协作平台,主要用于版本控制和源代码管理…

无人用过!QRTCN-BiLSTM实现区间预测!区间预测全家桶再更新!

声明:文章是从本人公众号中复制而来,因此,想最新最快了解各类智能优化算法及其改进的朋友,可关注我的公众号:强盛机器学习,不定期会有很多免费代码分享~ 今天对我们之前推出的区间预测全家桶再次进行更新&…

SPSS 27 安装教程(附安装包下载)

SPSS 27 是一款用于统计学分析运算、数据挖掘、预测分析和决策支持任务的软件产品。它最初是为社会科学领域的研究者设计的,但随着其功能和应用的不断扩展,现在已广泛应用于各个领域,如医学、市场调研、教育等。 [安装注意]:安装前…

智能制造 v3.13.11 发布,ERP、在线课堂、表白墙更新

智能制造一体化管理系统 [SpringBoot2 - 快速开发平台],适用于制造业、建筑业、汽车行业、互联网、教育、政府机关等机构的管理。包含文件在线操作、工作日志、多班次考勤、CRM、ERP 进销存、项目管理、EHR、拖拽式生成问卷、日程、笔记、工作计划、行政办公、薪资模…

UE5基础1-下载安装

目录 一.下载 二.安装 三.安装引擎 四.其他 简介: UE5(Unreal Engine 5)是一款功能极其强大的游戏引擎。 它具有以下显著特点: 先进的图形技术:能够呈现出令人惊叹的逼真视觉效果,包括高逼真的光影、材…

Robust Tiny Object Detection in Aerial Images amidst Label Noise

文章目录 AbstractIntroductionRelated WorkMethodsClass-aware Label CorrectionUpdateFilteringTrend-guided Learning StrategyTrend-guided Label ReweightingRecurrent Box RegenerationExperimentpaper Abstract 精确检测遥感图像中的小目标非常困难,因为这类目标视觉信…

【C++题解】1389 - 数据分析

问题:1389 - 数据分析 类型:简单循环 题目描述: 该方法的操作方式为,如果要传递 2 个数字信息给友军,会直接传递给友军一个整数 n(n 是一个 10 位以内的整数),该整数的长度代表要传…

Ps:自动批量处理照片

有很多种方法可以将调色风格一次性应用到多张照片上。 但对于要进行局部修饰的照片,比如人像照片中要去除皮肤上的瑕疵、柔化皮肤上的光影以及均匀肤色等,想要实现成批处理似乎很困难。 随着人工智能技术的不断发展,越来越多的插件具备自动修…

Linux下文件权限管理

任务要求 1. 在跳板机上为开发部门专门创建一个目录,只允许开发部门所有员工使用该目录 2. 其他人员不能进入和查看该目录里的内容 任务分解 1. 在跳板机给开发部门创建目录 2. 对该目录做好权限的管控工作 只允许开发部门的所有人使用,创建、删除…

Day20:LeedCode 654.最大二叉树 617.合并二叉树 700.二叉搜索树中的搜索 98.验证二叉搜索树

654. 最大二叉树 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点,其值为 nums 中的最大值。递归地在最大值 左边 的 子数组前缀上 构建左子树。递归地在最大值 右边 的 子数组后缀上 构建右子树。 返回 nums 构…

机器学习--回归模型和分类模型常用损失函数总结(详细)

文章目录 引言 回归模型常用损失函数均方误差(Mean Squared Error, MSE)均方根误差(Root Mean Squared Error, RMSE)平均绝对误差(Mean Absolute Error, MAE)Huber损失(Huber Loss) …

外部排序快速入门详解:基本原理,败者树,置换-选择排序,最佳归并树

文章目录 外部排序1.最基本的外部排序原理2.外部排序的优化2.1 败者树优化方法2.2 置换-选择排序优化方法2.3 最佳归并树 外部排序 为什么要学习外部排序? 答: 在处理数据的过程中,我们需要把磁盘(外存)中存储的数据拿到内存中处理…

导数和微分

导数和微分 flyfish 本文主要论述其中的区别 导数是描述函数变化率的量,它表示函数在某点的瞬时变化速度和切线斜率。 微分是导数的一个线性近似,表示函数在某点处随着自变量变化的增量。 导数和微分在本质上都是研究函数变化的工具,但导数…

算法学习笔记(7.6)-贪心算法(霍夫曼编码)

目录 1.什么是霍夫曼树 2.霍夫曼树的构造过程 3.霍夫曼编码 3.1具体的作用-频率统计 ##实战题目 1.什么是霍夫曼树 给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也…

【数据结构与算法】使用数组实现栈:原理、步骤与应用

💓 博客主页:倔强的石头的CSDN主页 📝Gitee主页:倔强的石头的gitee主页 ⏩ 文章专栏:《数据结构与算法》 期待您的关注 ​ 目录 一、引言 🎄栈(Stack)是什么? &#x1…

Python私教张大鹏 Vue3整合AntDesignVue之Breadcrumb 面包屑

显示当前页面在系统层级结构中的位置&#xff0c;并能向上返回。 何时使用 当系统拥有超过两级以上的层级结构时&#xff1b; 当需要告知用户『你在哪里』时&#xff1b; 当需要向上导航的功能时。 案例&#xff1a;面包屑导航基本使用 核心代码&#xff1a; <template…

【数据结构(邓俊辉)学习笔记】图07——最短路径

文章目录 0. 概述1. 问题2. 最短路径2.1 最短路径树2.1.1 单调性2.1.2 歧义性2.1. 3 无环性 2.2 Dijkstra 算法2.2.1 贪心迭代2.2.2 实现2.2.3 实例2.2.4 复杂度 0. 概述 学习下最短路径和Dijistra算法 1. 问题 给定带权网络G (V, E)&#xff0c;以及源点&#xff08;source…

【NoSQL数据库】Redis命令、持久化、主从复制

Redis命令、持久化、主从复制 redis配置 Redis命令、持久化、主从复制Redis数据类型redis数据库常用命令redis多数据库常用命令1、多数据库间切换2、多数据库间移动数据3、清除数据库内数据 key命令1、keys 命令2、判断键值是否存在exists3、删除当前数据库的指定key del4、获取…

基于pytorch_lightning测试resnet18不同激活方式在CIFAR10数据集上的精度

基于pytorch_lightning测试resnet18不同激活方式在CIFAR10数据集上的精度 一.曲线1.train_acc2.val_acc3.train_loss4.lr 二.代码 本文介绍了如何基于pytorch_lightning测试resnet18不同激活方式在CIFAR10数据集上的精度 特别说明: 1.NoActive:没有任何激活函数 2.SparseActiva…

【LeetCode 第 401 场周赛】K秒后第 N 个元素的值

文章目录 1. K秒后第 N 个元素的值&#x1f197; 1. K秒后第 N 个元素的值&#x1f197; 题目链接&#x1f517; &#x1f427;解题思路&#xff1a; 前缀和 小规律&#x1f34e; &#x1f34e; 从上图观察可知&#xff0c;规律一目了然&#xff0c;arr[i] arr[i] 对上一…