编译原理
文章平均质量分 75
青衫客36
这个作者很懒,什么都没留下…
展开
-
LLVM- cc1 & llc & lld
是 LLVM 工具链的核心部分,特别是在 Clang 编译器框架中。尽管开发者在日常编程工作中可能不会直接与它互动,但了解它是如何工作的对于深入理解整个编译流程是很有帮助的。定义: 是 Clang 的实际编译器前端。当我们使用 命令行工具编译 C/C++ 代码时,它实际上在内部启动 来执行大部分工作。主要职责:为什么存在:如何使用: 通常,开发者不需要直接调用 ,因为 工具会自动为我们做这件事。然而,了解其存在并知道如何使用它可以帮助我们更好地调试和理解编译过程。例如,使用 命令可以显示 如何调原创 2023-10-30 20:13:13 · 414 阅读 · 0 评论 -
Linux- DWARF调试文件格式
DWARF是一个用于在可执行程序和其源代码之间进行关联的调试文件格式。当开发者使用调试编译选项(例如,使用gcc时的-g标志)编译程序时,编译器会生成这种格式的调试信息。这些信息在后续的调试过程中非常有用,例如,使用gdb(GNU调试器)。以下是DWARFDWARF起初是为了满足UNIX系统上的高效、紧凑和跨平台的调试需求而设计的。自那时起,它已经经历了多个版本,每个版本都增加了新的特性。:从DWARF 1到DWARF 5,每个版本都引入了新的特性和改进,以支持新的编程语言特性、编译器优化等。DWARF。原创 2023-10-12 23:33:51 · 1295 阅读 · 0 评论 -
GCC -fno-builtin & 内建函数
是 GCC 编译器的一个选项。让我们来深入了解它。当使用 GCC 编译代码时,编译器知道很多标准的库函数(例如strcpymemcpyprintf等)。当优化代码时,如果 GCC 看到这些函数的使用,并且可以确定它们的语义,它可能会替换它们为更有效率的代码版本,或者直接内联它们的功能,而不是进行函数调用。这是因为 GCC 内部有这些函数的“内建”版本(built-in)。使用选项可以告诉编译器不要为任何函数做这样的替换,即使编译器知道这些函数的语义。原创 2023-09-30 19:07:24 · 928 阅读 · 0 评论 -
初识 flex & bison
flex和bison经常结合使用,分别用于词法分析和语法分析。flex):flex用于生成词法分析器或者说是扫描器(scanner)。它将输入的文本分解为称为"tokens"的序列。每个 token 都有一个特定的意义,例如一个数字、一个变量名或一个操作符。bison):bison用于生成语法分析器或称为解析器(parser)。它的任务是根据由flex产生的 tokens 来确保输入遵循某种给定的语法,并从中生成一个通常的抽象语法树 (AST) 或其他中间表示。原创 2023-09-13 23:33:32 · 1331 阅读 · 0 评论 -
gcc中的cc1 & collect2
命令编译一个C程序时,我们可能认为这是一个简单的操作,但实际上,编译过程包含了多个步骤和子工具的调用。是其中两个最主要的组件,负责核心的编译和链接前端工作。如果想深入了解这些工具是如何工作的,可以考虑在。通常作为一个前端,管理这些步骤并调用其他工具来完成特定的工作。的完整编译和链接过程中,其实还涉及到其他一些子工具和步骤,如预处理(选项,这将显示编译过程中的详细信息,包括所有子工具的调用。原创 2023-09-13 14:03:24 · 719 阅读 · 0 评论 -
argp包处理命令行参数
argp是一个在 GNU C Library (glibc) 中的库,用于解析命令行参数。它为 UNIX 风格的命令行参数提供了一种简洁和统一的方法。以下是一个简单的使用argp解析命令行参数的例子:先确保您的系统中有argp。它通常随glibc一起分发,所以大多数 Linux 系统上应该都有。下面我们来看一个实例。原创 2023-09-12 16:34:44 · 723 阅读 · 0 评论 -
Code Block & Basic Block
Code Block & Basic Block原创 2023-08-23 20:42:41 · 164 阅读 · 0 评论 -
The Backus-Naur Form (BNF) & The Extended Backus-Naur Form (EBNF)
【代码】Extended Backus-Naur Form。原创 2023-08-23 12:18:00 · 119 阅读 · 0 评论 -
浅谈反汇编器
反汇编器是用来将机器码(通常是二进制格式)转换回汇编语言的工具。该过程是汇编过程的逆过程。为了理解反汇编器的工作原理,首先需要理解编译和汇编。:反汇编器首先读取目标机器码二进制文件。每个指令的长度可能会因指令集架构(ISA)的不同而不同。例如,在某些架构中,所有指令的长度都是固定的,而在其他架构中,指令的长度可能会有所不同。:反汇编器使用指令集架构(ISA)的规范来解码机器码。每个机器指令都对应于特定的汇编指令。解码的过程通常涉及查找与机器码相对应的汇编指令。原创 2023-08-07 10:48:43 · 254 阅读 · 0 评论 -
Escape Analysis
【代码】Escape Analysis。原创 2023-07-31 14:21:02 · 89 阅读 · 0 评论 -
Compiler Lab2- 自制极简编译器
笔者实现的这个超级迷你版编译器(词法分析、语法分析、生成中间代码(cpp))仅支持四则运算,功能真的是非常非常简单,不过其中的流程(词法分析 -> 语法分析 -> 中间代码生成)还是有一定的学习价值的,可以把这些阶段都给串起来,看看不同的处理阶段是如何衔接的,而不是仅仅停留在教科书中各个分裂的模块里。原创 2023-05-03 12:49:30 · 1951 阅读 · 0 评论 -
Compiler Lab1- 自制词法分析器
由于编译原理的Lab1为自制词法分析器,所以笔者用C++实现了一个极简的C语言词法分析器,用于分析C语言源代码。它可以处理关键字、标识符、整数、实数、浮点数的科学计数法表示、运算符、分隔符、字符串字面量、字符字面量、注释和预处理指令。用户输入输入文件名和输出文件名,然后检查这些文件是否可以正确打开。然后,我们从输入文件中读取内容,对其进行词法分析,并将结果写入输出文件中。最后,我们通知用户词法分析已完成,并提示用户查看输出文件以获取结果。原创 2023-05-01 17:15:24 · 977 阅读 · 1 评论 -
Compiler- 尾调用
这种方式是尾递归,特点是,这种调用在整个函数的最后一步,它调用完之后整个函数就结束了,由于这是函数最后一步,做完之后函数调用堆栈框架也可以拆除了。所以,这种优化真是很了不起的。这两个代码在功能上是一致的,只不过第二个代码把a原创 2023-04-25 15:35:12 · 406 阅读 · 0 评论 -
Compiler- 循环展开
ps:上述PHP源码中的switch也比较有意思,它的作用是:如果nKeyLength不是8的倍数,就需要额外的处理最后这几个数字,源码中还特意在case后面提示了/* fallthrough... */,我们知道case后面一般都会跟上break,不然会把后面的代码一行一行的执行一遍,而PHP源码在此处故意不写break,这样的好处是,如果nKeyLength=7,那么从case 7开始执行7行,如果nKeyLength=5,那么从case 5开始执行5行,这种写法还是比较高效的hh~原创 2023-04-25 11:13:17 · 655 阅读 · 0 评论 -
Compiler- 自增运算
后面在进行加法操作的时候,左操作数已经准备好了(在%eax中),然后右操作数再从内存进入寄存器,此时的值就是5。这里要特别强调的一点,++ i 不是自己一遍做好就取出来存到寄存器,而是会等到所有的对 i 的操作都结束,在必须使用它来进行操作的时候才从内存中取值,所以它的值是 i 的最新值,而不一定是 ++ i之后的值。将 i 变量的值从内存(-0x8(%rbp))放入寄存器 %eax,然后在寄存器中增加1,将值放入%edx,然后再将%edx中的值放入内存(4)。那,下面的代码呢(手动滑稽hh~)?原创 2023-04-24 21:48:08 · 446 阅读 · 0 评论 -
Compiler- volatile关键字
因此,上述代码中的local有volatile修饰的时候,一旦被修改,立刻会被反映出来,但如果没有volatile,编译器会认为,这个local变量是const修饰的(是个常量),那么我就没必要去读内存了,直接从寄存器中把这个值读出来就好了,虽然我们此时利用指针ptr修改了内存中的值,但是print的时候,仍然是读取的寄存器中的10。(2)对于持这种观点的读者(这不就是通过指针把指向地址的内容给修改了嘛),我们再对代码进行修改(待我把volatile去掉,再运行一下)嘿嘿,这个结果是不是比较出乎意料呢?原创 2023-04-24 20:17:29 · 414 阅读 · 0 评论 -
代码优化- 中间表示上的优化
中间表示上的代码优化依赖于具体所使用的中间表示:控制流图(CFG)、控制依赖图(CDG)、静态单赋值形式(SSA)、后续传递风格(CPS)等共同的特点是需要进行程序分析,优化是全局进行的,而不是局部,通用的模式是:程序分析->程序重写在这部分中,我们将基于控制流图进行讨论,但类似的技术可以用于其他类型的中间表示。原创 2023-04-22 17:48:13 · 529 阅读 · 0 评论 -
代码优化- 前端优化
基本思想:在编译期间计算表达式的值(编译时静态计算)例如:a = 3 + 5 ==> a = 8,if (true && false) ... ==> if (false)好处是:语法树的节点数量减少了,意味着编译器要维护的中间表示所要耗费的计算资源(比如内存)减少了;将来生成机器码的话,指令也减少了。常量折叠把进行更深层次的优化的机会暴露出来了,例如上面的if (false),就可以进行死代码消除优化。可以整型、布尔型、浮点型等数据类型上进行(依赖于我们编译的元语言的情况)原创 2023-04-22 17:06:13 · 851 阅读 · 1 评论 -
代码优化- 基本概念
代码优化是对被优化的程序进行的一种语义保持的变换语义保持:程序的可观察行为不能改变变换的目的是让程序能够比变换前更小、更快、Cache行为更好、更节能等等。原创 2023-04-22 15:18:20 · 645 阅读 · 0 评论 -
中间表示- 活性分析
(1)在代码生成的讨论中,我们曾假设目标机器有无限多个(虚拟)寄存器可用,这简化了代码生成的算法,但对物理机器是个坏消息,因为机器只有有限多个寄存器,必须把无限多个虚拟寄存器分配到有限个寄存器中。(2)这是寄存器分配优化的任务,需要进行活性分析。考虑这段三地址码:有三个变量a,b,c,假设目标机器上只有一个物理寄存器 r,那么是否可能把这三个变量同时放到寄存器 r 中?答案是肯定的,因为这三个变量可以分阶段的交替占用这个寄存器r。计算在给定的程序点,哪些变量是“活跃”的(对应下图花括号中的变量),活跃原创 2023-04-17 19:00:51 · 470 阅读 · 0 评论 -
中间表示- 到达定义分析
给每条语句定义两个集合in[S]和out[S],in集合表示在进入语句S之前,有哪些定义是可以见到的(有多少语句是可达的,可到达这儿的),out集合表示经过S之后有哪些语句是可以出去,继续到达下一条的。kill集合,例如kill[4: y = 6] = defs[y] - {4} = {1, 5},可以理解为,这些集合都被当前的定义点杀死,只能看到此处(定义点)的4,其他的y所在的序号都被屏蔽了。注意,在此处到达5的定义是{4,3,2}没有1,意味着y在1处的定义是不可能到达5的,因为它总是会被4覆盖掉。原创 2023-04-09 11:11:51 · 582 阅读 · 0 评论 -
中间表示- 数据流分析
(1)数据流分析通过对程序代码进行静态分析,得到关于程序数据相关的保守信息必须保证程序分析的结果是安全的(2)根据优化的目标不同,需要进行的数据流分析也不同到达定义分析,活性分析。原创 2023-04-08 16:20:52 · 304 阅读 · 0 评论 -
中间表示- 控制流图
是更精细的三地址码数据结构定义(以B为例)Jump_t j;原创 2023-04-08 15:25:24 · 787 阅读 · 0 评论 -
中间表示- 三地址码
我们重点关注语句的代码生成,其余的可参考。要写如下几个递归函数来生成三地址码。如何定义三地址码数据结构?以 C--生成三地址码 为例。原创 2023-04-07 21:24:00 · 1121 阅读 · 0 评论 -
中间表示- 引言
树和有向无环图(DAG)三地址码(3-address code)控制流图(CFG)静态单复制形式(SSA)连续传递风格(CPS)还有很多其他的中间表示。原创 2023-04-07 20:07:45 · 454 阅读 · 0 评论 -
代码生成- 寄存器计算机
寄存器计算机是目前最流行的机器体系结构之一。原创 2023-04-05 21:43:55 · 449 阅读 · 0 评论 -
代码生成- 栈式计算机
注:我们可以看到这样的7条指令分成了四类, 上述的指令其实就是Java字节码的一个子集,Java字节码是用一个字节来表示字节码中的操作符,所以一个字节的话,Java字节码的指令原则上有256条,但是现在还没完全用完。伪指令并不会真正的被ALU那样的执行单元来执行,而是Stack机器在装载一个程序时,就会读取伪指令,并且给相关变量分配内存空间。3、在非栈式计算机上进行模拟,例如,可以在X86上模拟栈式计算机的行为,用X86的调用栈模拟栈。-> div 算术运算。原创 2023-04-05 20:26:49 · 345 阅读 · 0 评论 -
代码生成- 引言
目标机器可以是真实物理机器(X86,ARM,MIPS等),也可以是虚拟机(JVM等)原创 2023-04-04 20:15:32 · 422 阅读 · 0 评论 -
语义分析- 其他问题
子类对象y赋给父类对象x,如果只看类型的话,y的类型是B,x的类型是A,也是不相等的,所以在面向对象语言中,为了这种类型检查可以通过,需要维护类型间的继承关系,在Java中,继承是一棵树状的关系,任何子类对象都可以赋给父类对象。的策略,那么这样的赋值就是非法的,因为y的类型名字是B,x的类型名字是A,类型名字是不一样的,所以这样的赋值是不能做的;的语言,两侧同为结构体类型,我们需要看每个域,域的个数是不是一样,每个域是不是可以分别赋值,可能需要递归的来比较各个域。对采用结构相等的语言,需要递归比较各个域。原创 2023-04-04 19:40:36 · 525 阅读 · 0 评论 -
语义分析- 符号表
比如,hash表是一个数组,当在某一个位置发生冲突时,若采用拉链法解决冲突的话,需要保证后面插入的x要插入在表头上面,想一下为什么在这儿用头插法,而不是尾插法?在一个程序中,有可能许多地方用到同一个变量名,但是这些变量名的作用是不一样的,他们可以同时存在。当变量使用时,需要根据变量类别的不同,到不同的符号表中去查找这个变量。每个名字空间用一个表来处理,以C语言为例,有不同的名字空间:变量、标签、标号......上面说的,用hash表或栈的方式来组织符号表,是指具体的每一种符号表该怎么来做。原创 2023-04-04 11:58:41 · 798 阅读 · 0 评论 -
语义分析- C-- 语言
E -> n| true| false| E + E| E && E对这个语言,语义分析的任务是:对给定的一个表达式e,写一个函数返回表达式e的类型;若类型不合法,则报错。针对C -- 语言构造的类型检查伪代码算法如下:(ps:其中的数据结构定义可参考。原创 2023-04-04 10:59:04 · 962 阅读 · 0 评论 -
抽象语法树的定义(C语言版)
给定抽象语法:E -> n| E + E| E * E构造出相应的抽象语法树。原创 2023-04-02 11:36:26 · 880 阅读 · 0 评论 -
语法分析-LL(1)文法
以下的代码体现了之前介绍的LL分析的思想。首先,构造预测分析表,描述了当非终结符遇到特定输入时应该使用哪个产生式;其次,在使用产生式进行处理的,将产生式的右部替换左部即可。原创 2023-03-21 11:01:41 · 258 阅读 · 0 评论