0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

编译器把代码转化为机器码的过程

xCb1_yikoulinux 来源:一口Linux 作者:一口Linux 2022-08-03 13:56 次阅读

编译器,是把高级语言转化为机器语言的工具软件。

高级语言的代码也是个文本字符串,所以编译器的前端与sed、gawk、grep是差不多的,都是广义上的字符串匹配

编译器把代码转化为机器码过程如下:

1,词法分析

这是编译器里最简单的模块

词法分析,就是通过查看下一个字符来确定怎么把代码字符串分割成一个个的语法词汇

起始符、终止符,是词法分析的主要概念。

intday =24 * 3600;

这行代码的第1个词是int,起始符是i,终止符是空格

在词法分析时它是一个标志符,也就是字母、下划线、数字组成的一个字符串,必须以字母或下划线开头。

在分析出int这个标志符之后,它后面的空格就没用了,直接丢弃它。

第2个词day也是一个标志符,起始符是d,终止符也是空格。

习惯把代码写得密集的人可能这么写:int day=24*3600;

这时day的终止符是=,它同时还是下一个词的起始符,在把day加入词汇序列之后需要从=开始接着分析。

第3个词是=,第4个词是24,第5个词是*,第6个词是3600,第7个词是分号;

在词法分析时要把数字字符串24和3600转化为整数24和3600,这两个在程序里是不同的。

10进制、16进制、8进制、2进制、浮点数的支持,都是词法分析时的任务。

另外,转义字符串也要在这里支持。

'' 在源代码里是字符串文本,包含着4个字符' 0 ',要转义成单个字符0。

' ' ' ' ' '的处理和''一样。

词法分析还是很好写的。

2,语法分析

这是编译器前端最难写的模块,它需要把源代码转化成一棵描述整个程序结构的多叉树

这个多叉树叫做抽象语法树(英文缩写AST)。

类型、变量、运算符、函数定义、函数调用、if语句、for/while循环,都是这个这棵树的一部分。

抽象语法树的层次结构,与源代码的结构是一样的。

如果是这样的源代码的话:

int sum = 0;

for (int i = 0; i < 8; i++) {

if (i % 2 == 0)

sum += i;

}

那么语法树是这样的:

f24d7d2a-12df-11ed-ba43-dac502259ad0.jpg

语法树

初始化语句sum = 0与后续的for循环是顺序执行的,它们属于同一个顺序块,在语法树上有同一个父节点

for循环有4个子节点初始化表达式i = 0,条件i < 8,循环体if语句、更新表达式i++。

其中循环体又是个if语句,具有2个子节点:条件表达式i % 2 == 0,主体sum += i。

while循环的结构与for类似,只要去掉初始化表达式和更新表达式就行,只有2个节点。

把词法分析之后的词汇序列转化成抽象语法树时,常用的方法是有限自动机

也可以把代码直接写成递归函数调用,但是后续改起语法来就比较麻烦。

我一开始就是把scf的parse模块写成了递归函数调用,后来为了可以编辑语法,又自己做了个简单的有限自动机。

3,语义分析

把语法树遍历一遍,检查一下类型是否匹配,就是语义分析。

如果要支持面向对象的话,就可以在这里进行函数重载运算符重载

常量表达式也要在这里计算出来,int day = 24 *3600要转化成day = 86400。

f26b1600-12df-11ed-ba43-dac502259ad0.jpg

常量表达式的计算

对语法树进行遍历时,不同的语法节点使用的处理函数不同的,这就是语义

符号=要当作赋值,符号+要当作加法,其他类似。

C语言里常见的函数调用,语法树是这样的:

int printf(const char* fmt, ...);

int main()

{

printf("hello world ");

}

函数调用也是一个运算符,具有一个单独的语法节点,它的子节点都是它的参数

其中函数名也是一个参数,要转化为对应的函数体的节点的指针

通过这个指针才可以找到函数的代码,进行内联优化(inline)

f28dd780-12df-11ed-ba43-dac502259ad0.jpg

函数调用和定义

如果要是调用的外部函数,只有声明没有实现,那就没法内联了。

4,中间代码生成(三地址码),从这里开始就是编译器的后端了。

这一步也是对语法树进行遍历,把对应的表达式、函数、if语句、for循环都变成类似汇编的三地址码。

上面那段for循环,这时会被变成如下的三地址码序列:

assign sum, 0

assign i, 0

start: // for循环的开头

cmp i, 8

jge end //条件不成立,则结束循环

assignt, i % 2 // t是编译器生成的临时变量

cmp t, 0

jne next

add sum, sum, i // 这行才是三地址码

next: // 下一轮for循环

inc i // 循环变量+1

jmp start //跳转回开头,继续循环

end:// for循环结束

到了这里,那个复杂的树型结构已经变成线形结构了,可以按顺序写到一个文本文件里,这就是汇编代码

到这里,编译器就可以生成类似gcc -S的汇编代码了。

5,中间代码优化

这是编译器后端的主要部分,属于机器无关优化,这部分的优化是不依赖于CPU平台的。

scf框架的这部分包含以下功能:

1)内联函数,

2)有向无环图DAG的生成,

3)带二级指针参数的函数调用分析,

4)指针别名分析,也就是分析指针指向的变量,

5)活跃变量分析,

6)变量的加载保存分析,

7)需要自动内存管理的变量分析,

8)代码流程图的深度优先排序

9)自动内存管理代码的添加,

10)基本块内的优化,

11)循环分析,

会把一些变量尽量在循环的入口加载,在循环出口保存,减少循环内的内存读写。

没有常量传播的优化,哪天有空我把它添上

6,寄存器分配

使用图的着色算法,之前的文章写过。

7,指令选择

直接写在代码里的,没做龙书里提到的那个树的覆盖。

8,机器码生成

根据intel x64的手册编写机器码就行。

9,目标文件生成

也就是gcc -c 得到那个.o文件。

Linux上的elf文件是什么样的就怎么写,可以参考linux的man手册里对elf的讲解。

10,可执行文件的生成

这是连接器的功能,它把多个.o .a .so文件连接成一个可执行文件。

这一步的代码在scf/elf目录,有兴趣的可以看看。

连接之后的文件就可以在shell命令行里运行了。

审核编辑:汤梓红


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 机器码
    +关注

    关注

    0

    文章

    12

    浏览量

    8253
  • 代码
    +关注

    关注

    30

    文章

    4555

    浏览量

    66740
  • 编译器
    +关注

    关注

    1

    文章

    1576

    浏览量

    48606

原文标题:编译器的代码架构

文章出处:【微信号:yikoulinux,微信公众号:一口Linux】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    机器码提取, 芯片破译

    本帖最后由 北风凛冽 于 2012-6-28 16:12 编辑 小弟现在的项目是要从集成电路的芯片里面提取程序的二进制代码出来,哪位大神能提供MSP430系列和RENESAS系列芯片内部机器码提取的方法?跪求!!!! {:23:}
    发表于 06-28 15:55

    求助机器码问题

    一段机器码在两种不同的单片机实现的功能一样吗?
    发表于 07-29 23:18

    机器语言如何改变单片机程序感性认知

    高级语言被编译器变成最后的机器语言,机器码被下载软件下载进单片机的flash,相关处理寄存根据系统时钟一拍一拍工作,读取flash里的代码
    发表于 04-02 01:03

    怎么一个数字常量(字符串也行)转化为4位BCD,,或者说转化为4位二进制

    怎么一个数字常量(字符串也行)转化为4位BCD,,或者说转化为4位二进制例如:5转为0101,而我只能转为101,也就是说要把二进制长度定位4
    发表于 09-06 22:00

    简单分析MDK的编译过程

    使用的编译器是 armcc 和 armasm,它们根据每个 c/c++和汇编源文件  编译成对应的以“.o”;为后缀名的对象文件(Object Code,也称目标文件),其内容主要是从源文件编译得到的
    发表于 09-01 17:34

    HDC技术分论坛:ArkCompiler(方舟编译器)原理解析

    对应体系架构的优化机器码,从而提升运行效率和启动性能。图2 ArkCompiler运行原理下面,本文将从前端编译器,运行时展开介绍。二、前端编译器前端编译器是高级语言通往语言运行时的桥
    发表于 11-22 17:04

    从源代码到CPU执行过程

    1.从源代码到CPU执行过程.c等高级语言经过编译器编译后转换为.s汇编源代码经过汇编
    发表于 12-20 07:55

    如何将高级C语言编译机器码

    C编译机器码要通过预处理,编译,汇编,链接四个步骤。这四个步骤由谁做的呢?答案是编译器编译器做的工作类似我们IC行业里面的综合。在IC设
    发表于 06-01 16:53

    arm7和arm9采用的指令系统的机器码应该是不同的吧

    菜鸟的问题:arm7和arm9采用的指令系统的机器码应该是不同的吧?那么编译器是怎么确定所采用的架构的?或者说我需要根据所采用的ARM芯片的不同而采用不同版本的编译器吗?
    发表于 10-27 16:17

    RealView编译工具4.0版编译器用户指南

    ARM编译器armcc是一个优化的C和C++编译器,它将标准C和标准C++源代码编译成用于基于ARM架构的处理
    发表于 08-12 06:05

    C编译器的设计文档与源代码

    C-编译器的设计文档与源代码:本压缩包包含了C-编译器的设计文档与源代码,供学习参考。  整体框架. 3 词法分析. 3᠙
    发表于 02-09 11:13 45次下载

    编译器是如何工作的_编译器的工作过程详解

    随着计算机的发展,编译器已经发挥着十分重要的作用。本文主要介绍了编译器的种类、编译器的工作原理以及编译器工作的具体操作过程及步骤详解。
    发表于 12-19 12:54 1.5w次阅读

    MPLAB® XC8 C编译器的架构特性

    本视频介绍了MPLAB® XC8 C编译器的架构特性。该编译器编译过程不同于传统的编译器,采用了一种称为"OCG(全知
    的头像 发表于 05-23 12:47 5435次阅读
    MPLAB® XC8 C<b class='flag-5'>编译器</b>的架构特性

    如何对单片机的机器码进行反编译代码免费下载

    应一个做硬件的同事的要求,他利用其他软件可以得到十六进制的机器码,希望做一个简单的软件,可以将机器码编译成汇编指令。本来网上应该有很多这方面的软件。但他说这个很特别,找不到,于是给他做了一个小软件现在将
    发表于 07-17 17:38 11次下载
    如何对单片机的<b class='flag-5'>机器码</b>进行反<b class='flag-5'>编译</b><b class='flag-5'>代码</b>免费下载

    交叉编译器安装教程

    交叉编译器中“交叉”的意思就是在一个架构上编译另外一个架构的代码,相当于两种架构“交叉”起来了。Ubuntu 自带的 gcc 编译器是针对 X86 架构的,而我们现在要
    的头像 发表于 09-29 09:12 2643次阅读