AI系统 编译原理与编译器
主要整理编译原理和AI编译器的相关知识点
生活需要深度
这个作者很懒,什么都没留下…
展开
-
用LLVM写一个芯片编译器(三)——芯片的整体架构部分
本章是我们从LLVM森林看到LLVM树木的第一章。介绍了我们如何在LLVM上定义出我们的架构,方便后续添加枝叶进去。可能有点晦涩,可对照代码来读。下一章讲的内容应该更好懂,下一章我们讲讲如何添加寄存器信息进去。原创 2024-09-22 09:35:23 · 652 阅读 · 0 评论 -
用LLVM写一个芯片编译器(二)——从无到有需要写什么东西
本篇文章实际上是解决LLVM如何用起来这个问题的开始。我觉得开源项目要想玩的快,一定是要先看森林后看树叶的。否则很容易陷入开源代码的汪洋大海中毫无方向。本篇文章我们从两个角度介绍了宏观的看看我们最后完成的目标,属于介绍LLVM的森林。从下一章开始,我们逐个方向介绍如何一步步撺起来我们的编译器。【参考文献】[1]知乎 P2TREE[2]Chen Chung-Shu的Tutorial: Creating an LLVM Backend for the Cpu0 Architecture。原创 2024-09-22 09:34:12 · 979 阅读 · 0 评论 -
用LLVM写一个芯片编译器(一)——一文读懂编译器基本概念
这篇文章,我们介绍了程序编译的最基本概念,编译中的大部分流程都有所涉及。下一章开始我们介绍如何用LLVM快捷的实现上面的流程。LLVM的精髓就在于,你不必对上面每一个步骤内部如何实现的彻底了解细节。你只需要知道有这么个东西就能很快攒出你的编译器。目录更新桔里猫:用LLVM写一个芯片编译器(二)——从无到有需要写什么东西桔里猫:用LLVM写一个芯片编译器(三)——芯片的整体架构部分。原创 2024-09-22 09:33:13 · 1093 阅读 · 0 评论 -
使用Flex、Bison和LLVM编写自己的Toy Compiler
我一直对编译器和编程语言很有兴趣,但仅仅有兴趣是不够的。编译器设计中有许多初次学习时感到晦涩难懂的概念,即使是最聪明的程序员也会被这些东西所困扰。我曾经尝试写过一个小型的玩具语言/编译器,但总是在语义解析(semantic parsing)阶段遇到一些麻烦。这篇文章主要是受我最近的一次尝试启发,这个尝试到目前为止是比较成功的。在过去的几年里我有幸参与了一些项目,这些项目帮助我对编译器的实际工作有了更多经验。原创 2024-09-21 11:51:17 · 1215 阅读 · 0 评论 -
flex&bison系列第三章:写一个简单的计算器Calculator
而“factor”仅仅是个单词而已,没有特殊的意义,我们可以简单地把它理解为乘法和除法里的“因素”。其生成的C代码文件名为“simple-calculator.yy.c”、“simple-calculator.tab.c”、“simple-calculator.tab.h”。我们基于flex和bison,用C++写了一个很简单的计算器程序,并且编译运行成功。在此记录下基于flex与bison写一个简单的计算器程序(Calculator)的过程,以备查阅。其实,这些变量都是在我们的bison脚本中定义的。原创 2024-09-21 11:10:42 · 216 阅读 · 0 评论 -
flex&bison系列第二章:写一个简单的单词统计工具Word Counter
我们基于flex,用C++写了一个简单的单词统计工具Word Counter,并且编译运行成功。为简单起见,我们只统计英文单词和整数,比如“Abc”、“123”。在此记录下基于flex写一个简单的单词统计工具(Word Counter)的过程,以备查阅。flex&bison系列第二章:写一个简单的单词统计工具Word Counter。当我们按Ctrl+D时,程序会输出英文单词和整数的总个数。这两行代码分别统计了英文单词的总个数和整数的总个数。最终程序结束时,输出英文单词的总个数及整数的总个数。原创 2024-09-21 11:09:59 · 144 阅读 · 0 评论 -
flex&bison系列第一章:flex Hello World
在这里,为简单起见,我们可以把“结构化的输入”理解为有一定组织结构的文本,我们用flex对文本进行处理时,应提前了解一下正则表达式和编译原理相关的知识(如词法分析器、自动机等)。,这是匹配成功后的“行动”,意思是如果匹配成功,则打印“Hello World”。注意到,当我们输入单词“World”时,程序会输出“Hello World”;我们基于flex,用C++写了一个Hello World,并且编译运行成功。World,它其实是一个匹配字符串,意思是用“World”去匹配输入的数据。原创 2024-09-21 11:09:12 · 342 阅读 · 0 评论 -
LLVM系列第二十八章:写一个JIT Hello World
JIT(Just In Time)也是一个程序,它在运行时,创建并执行了一些新的代码,而这些新代码并不是属于JIT程序本身的。Legacy JIT (LLVM 1.0 - 3.5),引入了ExecutionEngine、延迟编译(lazy compilation),在当前进程中运行(in-process only),在 LLVM 3.5之后被移除了。本章我们就来写一个简单的Hello World,初步感受一下LLVM的ORC JIT引擎是什么样子的。原创 2024-09-21 11:08:02 · 146 阅读 · 0 评论 -
LLVM系列第二十七章:理解IRBuilder
如果未指定,则默认使用IRBuilderDefaultInserter,这个Hook指定了“每次插入的位置总是当前最新的插入点”,即没有做位置上的偏移或跳跃。由以上可以看出,IRBuilder就是一个工具,它提供了一套统一的API,以便用户用来创建IR指令(Instruction),并插入到代码块中。如果要使用其它未集成的API,可以在创建好指令之后,直接对指令进行操作,比如直接调用LoadInst::setVolatile()等各种Instruction的成员函数。原创 2024-09-21 11:06:42 · 145 阅读 · 0 评论 -
LLVM系列第二十六章:理解LLVMContext
再举个例子,有这样一个多线程的程序,其中一个线程运行图像处理库(Graphics Library),另一个线程运行音频处理库(Audio Library),而这两个库都调用了LLVM。在很久以前,也就是LLVM的老版本中,这些状态数据都是全局数据。我们可以把它理解为一个黑盒,它包含(并管理)了LLVM中基础的、核心的“全局”数据,如类型(Type)、标准化的常量表等。通常来说,LLVM的用户也许不需要知道context是什么,但编程语言的开发者(即编译器的开发者)是需要清楚地知道context是什么的。原创 2024-09-21 11:05:41 · 355 阅读 · 0 评论 -
LLVM系列第二十五章:简单统计一下LLVM源码行数
关于如何下载LLVM源码,请参考第一章 《LLVM系列第一章:编译LLVM源码》。注意这里使用的是LLVM 12的源码。可以看到,llvm-project的C++代码已经接近600万行,大约是中大型规模的软件(库)。我们可以用一个名为"scc"的工具来统计LLVM的源码。可以看到,LLVM的C++代码已经超过了200万行,大约是中小型规模的软件(库)。LLVM系列第二十二章:写一个简单的编译时函数调用统计器(Pass)LLVM系列第二十三章:写一个简单的运行时函数调用统计器(Pass)原创 2024-09-21 11:04:49 · 357 阅读 · 0 评论 -
LLVM系列第二十四章:用Xcode编译调试LLVM源码
注意,在配置之前,须确保选中了opt作为编译目标,即我们编辑的是opt程序的Scheme。生成的Xcode项目文件在目录build-xcode中,我们进入该目录并用Xcode打开项目文件LLVM.xcodeproj即可。我们在第一章中编译LLVM源码时,使用的工具是Ninja,本章用的工具是Xcode。我们利用Xcode编译了LLVM的源代码,并用opt作为例子,简单地调试了一下与LLVM Pass相关的代码。开发环境的配置请参考第一章 《LLVM系列第一章:编译LLVM源码》。原创 2024-09-21 11:02:23 · 355 阅读 · 0 评论 -
LLVM系列第二十二章:写一个简单的编译时函数调用统计器(Pass)
所以,我们要做的是,遍历模块中的每一个函数、函数中的每一个代码块、代码块中的每一条指令。如上所述,CompileTimeFunctionCallCounter将会遍历模块中的每一个函数、函数中的每一个代码块、代码块中的每一条指令,统计每个函数的调用次数并打印出来。我们用LLVM提供的C++ API,写了一个简单的Pass,用来统计每个函数在编译时的调用次数,并且做了测试。CompileTimeFunctionCallCounter,一个简单的Pass模块,用来统计每个函数在编译时的调用次数。原创 2024-09-21 11:01:37 · 245 阅读 · 0 评论 -
LLVM系列第二十一章:写一个简单的Loop Pass
Loop Pass执行的顺序是由内而外的,即内层的循环先执行,外层的循环后执行。如果我们是在第一章的编译LLVM完成之后,再编译此项目,则仅仅需要编译SimpleLoopPass项目即可。注意,我们需要把SimpleLoopPass项目加入到LLVM Transforms父项目中,即指示CMake在编译LLVM源码的同时,也要编译SimpleLoopPass项目。我们用LLVM提供的C++ API,创建了一个简单的Loop Pass,并且编译运行成功。原创 2024-09-21 11:00:35 · 228 阅读 · 0 评论 -
LLVM系列第二十章:写一个简单的Function Pass
Function Pass,顾名思义,是在程序中的每个函数(function)上执行的。具体地说,Function Pass不能添加或删除当前模块的函数或全局变量,也不能分析或修改当前正在被处理的函数之外的其它函数。注意,我们需要把SimpleFunctionPass项目加入到LLVM Transforms父项目中,即指示CMake在编译LLVM源码的同时,也要编译SimpleFunctionPass项目。我们用LLVM提供的C++ API,创建了一个简单的Function Pass,并且编译运行成功。原创 2024-09-21 10:59:25 · 203 阅读 · 0 评论 -
LLVM系列第十九章:写一个简单的Module Pass
如果我们是在第一章的编译LLVM完成之后,再编译此项目,则仅仅需要编译SimpleModulePass项目即可。注意,我们需要把SimpleModulePass项目加入到LLVM Transforms父项目中,即指示CMake在编译LLVM源码的同时,也要编译SimpleModulePass项目。我们用LLVM提供的C++ API,创建了一个简单的Module Pass,并且编译运行成功。在此记录下用LLVM创建一个简单的Module Pass的过程,以备查阅。原创 2024-09-21 10:58:03 · 214 阅读 · 0 评论 -
LLVM系列第十八章:写一个简单的IR处理流程Pass
Pass是LLVM中很重要的部分。LLVM Pass可以处理的对象有模块(Module)、函数(Function)、循环(Loop),甚至函数调用栈(Function Call Graph)等等。注意,我们需要把MyPass项目加入到LLVM Transforms父项目中,即指示CMake在编译LLVM源码的同时,也要编译MyPass项目。我们用LLVM提供的C++ API,创建了一个简单的Pass,并且编译运行成功。在此记录下用LLVM创建一个简单的IR处理流程(Pass)的过程,以备查阅。原创 2024-09-21 10:56:34 · 576 阅读 · 0 评论 -
LLVM系列第十七章:控制流语句for
这里的for循环语句IR,其实跟前面章节的if-else语句有些相似的地方。我们用LLVM提供的C++ API,创建了简单的for循环语句,并打印出了它的IR代码。开发环境的配置请参考第一章 《LLVM系列第一章:编译LLVM源码》。LLVM系列第二十二章:写一个简单的编译时函数调用统计器(Pass)LLVM系列第二十三章:写一个简单的运行时函数调用统计器(Pass)LLVM系列第二十九章:写一个简单的常量加法“消除”工具(Pass)LLVM系列第二十章:写一个简单的Function Pass。原创 2024-07-15 20:57:25 · 800 阅读 · 0 评论 -
LLVM系列第十六章:写一个简单的编译器
我们知道,在编译(链接)的时候,是需要把代码跟runtime一起编译(链接)的。其它与词法分析(Lexical Analysis)、语法分析(Syntax Analysis)、语义分析(Semantic Analysis)以及中间代码的生成(IR Generation)相关的内容,请参看前面的章节。我们基于LLVM提供的API,用C++写了一个很简单的编译器,并用它编译出了我们自己的计算器程序。现在,我们用自己定义的SimpleLang语言写一个计算器程序,然后用我们自己的编译器把它编译出来。原创 2022-04-28 21:32:30 · 366 阅读 · 0 评论 -
LLVM系列第十五章:写一个简单的中间代码生成器IR Generator
Module中可以包含函数定义及实现,而函数中则包含了基本代码块BasicBlock,而BasicBlock则包含了一行行的代码,比如变量赋值、数学运算表达式、函数调用、函数返回等等。IRGenerator.h和IRGenerator.cpp,中间代码(Intermediate Representation,即IR)生成器的定义及实现代码。注意到,与前几章相比,我在这里调用了比较多的LLVM API。Lexer.h和Lexer.cpp,SimpleLang语言的词法分析器(Lexer)的定义及实现代码。原创 2024-09-20 20:38:32 · 221 阅读 · 0 评论 -
LLVM系列第十四章:写一个简单的语义分析器Semantic Analyzer
这个示例中,语义分析其实就是遍历AST的节点,并检查每个节点上的变量申明是否符合SimpleLang语言的规则。与词法分析(Lexical Analysis)及语法分析(Syntax Analysis)相关的文章,请参看《LLVM系列第三章:写一个简单的词法分析器Lexer》和《LLVM系列第四章:写一个简单的语法分析器Parser》。我们参考编译器设计中常用的数据结构定义及算法,基于LLVM提供的API,用C++写了一个很简单的词法分析器,并且编译运行成功。其中,变量a被声明了两次,变量b缺少了申明。原创 2024-09-20 20:35:26 · 221 阅读 · 0 评论 -
LLVM系列第十三章:写一个简单的语法分析器Parser
可以看到,我们的SimpleParser工具把SimpleLang程序代码切分成了一系列的Token,用这些Token构建出了一颗抽象语法树(AST),最后把AST打印了出来。我们参考编译器设计中常用的数据结构定义及算法,基于LLVM提供的API,用C++写了一个很简单的代码语法分析器,并且编译运行成功。src/Parser.h,src/Parser.cpp,包含了语法分析器(Parser)的定义及实现代码。在此记录下,基于LLVM写一个简单的语法分析器(Simple Parser)的过程,以备查阅。原创 2024-09-20 20:30:44 · 189 阅读 · 0 评论 -
LLVM系列第十二章:写一个简单的词法分析器Lexer
源文件Lexer.h和Lexer.cpp,包含了SimpleLang语言的词法分析器(Lexer)的定义及实现代码。代码生成器(Code Generator,简称 CodeGen),从 AST 生成用中间表达语言(Intermediate Representation,简称IR)组成的程序代码。我们参考编译器设计的经典词法分析算法,基于LLVM提供的API,用C++写了一个很简单的词法分析器,并且编译运行成功。在此记录下,基于LLVM写一个简单的词法分析器(Simple Lexer)的过程,以备查阅。原创 2024-09-20 20:29:08 · 189 阅读 · 0 评论 -
LLVM系列第十一章:写一个Hello World
simplelang将会调用simplelangBasic中的函数,并把结果打印出来。我们参考LLVM的项目组织结构,基于LLVM提供的API,用C++写了一个Hello World,并且编译运行成功。原文链接:https://blog.csdn.net/Zhanglin_Wu/article/details/124963173。注意/path/to/llvm-project代表的是llvm-project的源代码路径。开发环境的配置请参考第一章 《LLVM系列第一章:编译LLVM源码》。原创 2024-09-20 20:27:53 · 170 阅读 · 0 评论 -
LLVM系列第十章:控制流语句if-else-phi
第一个参数表示的是phi指令的返回值类型,如在以上示例中为i32。接下来的每一个参数都是一个数组,代表了每一个分支及其对应的返回值。C函数跟上一章用到的是一样的,只不过我们这次在IR代码中用到了phi指令而已。我们用LLVM提供的C++ API,创建了简单的if-else控制流语句,并打印出了其带有phi指令的IR代码。在很多情况下,控制流只是为了给某一个变量赋值,而phi 指令,则可以根据控制流来选择合适的值。不过,这一次我们用一个特殊的指令来生成IR代码,即phi指令。原创 2024-09-20 20:26:19 · 193 阅读 · 0 评论 -
LLVM系列第九章:控制流语句if-else
LLVM IR提供了一个指令,可以让我们在栈上申明变量,即 alloca 指令。注意用alloca 指令申明的变量,其实得到是变量的地址。我们用LLVM提供的C++ API,创建了简单的if-else控制流语句,并打印出了它的IR代码。LLVM系列第二十二章:写一个简单的编译时函数调用统计器(Pass)LLVM系列第二十三章:写一个简单的运行时函数调用统计器(Pass)LLVM系列第二十九章:写一个简单的常量加法“消除”工具(Pass)LLVM系列第二十章:写一个简单的Function Pass。原创 2024-09-20 20:25:10 · 185 阅读 · 0 评论 -
LLVM系列第八章:算术运算语句Arithmetic Statement
在LLVM中,一个简单的算术运算操作需要用到操作符和操作数。所以,要创建一条乘法运算指令,我们首先要得到两个操作数,它们可以来自于函数的参数、数值变量、数值常量等等。为简单起见,我们在这里可以把一个简单的算术运算语句理解为一条指令。我们用LLVM提供的C++ API,创建了一个算术运算语句(Arithmetic Statement),并打印出了它的IR代码。LLVM系列第二十二章:写一个简单的编译时函数调用统计器(Pass)LLVM系列第二十三章:写一个简单的运行时函数调用统计器(Pass)原创 2024-09-20 20:22:18 · 133 阅读 · 0 评论 -
LLVM系列第七章:函数参数Function Arguments
我们用LLVM提供的C++ API,创建了一个带有参数(Arguments)的函数(Function),并打印出了它的IR代码。本章,我们就来创建一个带有参数的函数。开发环境的配置请参考第一章 《LLVM系列第一章:编译LLVM源码》。LLVM系列第二十二章:写一个简单的编译时函数调用统计器(Pass)LLVM系列第二十三章:写一个简单的运行时函数调用统计器(Pass)LLVM系列第二十九章:写一个简单的常量加法“消除”工具(Pass)LLVM系列第二十章:写一个简单的Function Pass。原创 2024-09-20 20:20:52 · 147 阅读 · 0 评论 -
LLVM系列第六章:函数返回值Return
我们用LLVM提供的C++ API,创建了一个带有返回值(Return)的函数,并打印出了它的IR代码。我们知道,一个函数运行结束后可以返回一个结果,这就是函数的返回值。LLVM系列第二十二章:写一个简单的编译时函数调用统计器(Pass)LLVM系列第二十三章:写一个简单的运行时函数调用统计器(Pass)LLVM系列第二十九章:写一个简单的常量加法“消除”工具(Pass)LLVM系列第二十章:写一个简单的Function Pass。LLVM系列第十三章:写一个简单的语法分析器Parser。原创 2024-09-20 20:20:10 · 119 阅读 · 0 评论 -
LLVM系列第五章:全局变量Global Variable
全局变量在链接的时候,到底是指向同一个全局变量,还是多个不同的全局变量,是由链接类型决定的。这里说的“多个不同的全局变量”,意思是其名称相同,但是有多个“分身”( 可以简单地理解为Copy、Instance、实例等等),“分身”之间互不影响。我们用LLVM提供的C++ API,创建了一个全局变量(Global Variable),并打印出了它的IR代码。全局变量(Global Variable)是在一个模块(Module)之内全局可见的变量,也就是说模块内所有的函数都能用它。原创 2024-09-20 20:19:27 · 209 阅读 · 0 评论 -
LLVM系列第四章:逻辑代码块Block
代码块的起点是一个标签,它是整个代码块的标签。LLVM中的BasicBlock类,就是用来创建、使用、处理代码块的。当然,我们还可以使用LLVM提供的IR代码创建工具(类)IRBuilder,它能让我们更方便快捷的创建出代码块。我们用LLVM提供的C++ API,创建了一个最简单的基本逻辑代码块(BasicBlock),并打印出了它的IR代码。LLVM系列第二十二章:写一个简单的编译时函数调用统计器(Pass)在这一章中,我们就用LLVM提供的这些工具来创建一个最简单的代码块。原创 2024-09-20 20:16:43 · 333 阅读 · 0 评论 -
LLVM系列第三章:函数Function
当然,如果要创建一个函数,我们还需要用到其它工具,比如llvm::Function、llvm::FunctionType等。我们知道,函数也是有类型的,而llvm::FunctionType就是用来创建函数类型的。注意到这里用了llvm::verifyFunction, 它的作用是检查我们创建的函数是否正确,确保它是符合编程语言规范的。我们用LLVM提供的C++ API,创建了一个最简单的函数(Function),并打印出了它的IR代码。LLVM系列第二十二章:写一个简单的编译时函数调用统计器(Pass)原创 2024-09-20 20:14:15 · 124 阅读 · 0 评论 -
LLVM系列第二章:模块Module
简单来说,LLVM中的模块(Module),代表了一块代码。它是一个比较完整独立的代码块,是一个最小的编译单元。而LLVM中的模块(Module),我们可以把它初步理解为一个编译单元。在LLVM IR的基本概念(构件)中,模块是一个组合,它包含了其它更小的基本构件。我们用LLVM提供的C++ API,创建了一个最简单的模块(Module),并打印出了它的IR代码。LLVM系列第二十二章:写一个简单的编译时函数调用统计器(Pass)在这一章,我们就用LLVM的API来创建一个最简单的模块。原创 2024-09-20 20:13:27 · 202 阅读 · 0 评论 -
LLVM系列第一章:编译LLVM源码
我们利用Git下载了LLVM的源代码,用CMake和Ninja工具编译并安装了LLVM,最后还简单地试用了一下LLVM工具库中的llc工具。LLVM是一个著名的开源编译器项目、库、工具,在此记录下编译LLVM源码的过程,以备查阅。LLVM系列第二十二章:写一个简单的编译时函数调用统计器(Pass)LLVM系列第二十三章:写一个简单的运行时函数调用统计器(Pass)LLVM系列第二十九章:写一个简单的常量加法“消除”工具(Pass)LLVM系列第二十章:写一个简单的Function Pass。原创 2024-09-20 20:12:41 · 896 阅读 · 0 评论 -
基础很重要!elf和map文件有不同?
在做MCU开发的过程中,我们以Keil MDK为例简单介绍一下,通常我们会看到一些后缀的文件名,比如.bin文件,.hex文件,.axf文件,.map文件,.elf文件,当然还有很多其他的文件,这里我们先简单介绍一下,先说.elf吧。简单介绍了使用arm工具链在构建过程中所产生的几种文件类型,包括,bin文件,hex文件,axf文件,elf格式,map文件等等,由于作者能力水平有限,文章难免存在错误,请不吝赐教。ARM公司虽然使用的是自家的armcc编译器,但是也提供了fromelf工具来实现上面的方式。原创 2024-04-01 10:48:33 · 595 阅读 · 0 评论 -
gcc编译过程
GNU编译器套件(GNU Compiler Collection)包括C、C++、Objective-C、Fortran、Java、Ada和Go语言的前端,也包括了这些语言的库(如libstdc++、libgcj等等)。的初衷是为GNU操作系统专门编写的一款编译器。GNU系统是彻底的自由软件。gcc在执行编译工作的时,分为以下四个过程:1.预处理,生成.i的文件2.将预处理后的文件转换成汇编语言,生成.s文件3.汇编变为目标代码(机器代码),生成.o的文件。...原创 2022-08-13 14:30:44 · 1874 阅读 · 0 评论 -
程序内存分布和堆、栈概念
一个正常可以运行的bin文件,直接进行反汇编以后实际有用的数据行数目*4就是文件ll出来的直接大小数目。一个可执行文件正常包含代码段、只读数据段、读写数据段、未初始化数据段、文件注释五个部分。代码段(code 或text),代码段由各个函数产生,函数的每一个语句都最终编译成二进制代码。只读数据段(rodata),被const修饰的初始值非0的全局变量。读写数据段(data), .data段包含初始值非0的全局变量(不管静态还是非静态static)未初始化数据段(bss),包含初始值为0或未初始的全局变量(不原创 2022-03-06 20:53:17 · 893 阅读 · 0 评论
分享