
汇编编译器
caimouse
在IT行业有20多年的经验。拥有20多年的C和C++开发经验,5年以上Python开发经验,资深数据库开发、上百G数据库优化经验。曾经任职嵌入式工程师、P2P开发工程师、银行信用卡交易系统工程师、全自动化电池测试部门经理。
展开
-
在Visual C++ 2012(MSVC)编译SDCC编译器
当你想使用一个开源的工具来开发单片机51程序时,可以使用SDCC编译器,它不仅是免费的,还是开放源码的编译器;不仅让你可以开发51的程序,还可以深入了解编译原理的编译器。通过本文的指导,你可以实现自己编译一个51编译器出来,也可以继续深入地了解编译器的原理作为基础。 为了下载SDCC编译器的源码,你得先到官方网站下载相应的代码,下载代码有两种方式:方式一是通过SVN代码库来下载:svn://svn原创 2015-12-03 16:28:42 · 4074 阅读 · 2 评论 -
LCC编译器的源程序分析(24)条件表达式
C语言里条件表达式语句如下:表达式1 ?表达式2 :表达式3条件运算符要求有三个操作对象,称为三元运算符,它是C语言中唯一的一个三目运算符。比如计算两个值的最大值,就可以写成下面这样:int nMax = a > b ? a:b;下面就来看LCC是怎么样处理条件表达式的,它的代码如下: #001 static Tree expr2(void) #002 {#0原创 2007-06-03 18:10:00 · 2603 阅读 · 0 评论 -
LCC编译器的源程序分析(20)复合语句
在C语言里,有一种语句叫做复合语句。它是由{ }把一些语句括起来的,如下面的例子:{ y = x + 1; z = y + 2;}在LCC里处理这样的复合语句的函数是compound,它在上面函数定义函数funcdefn是这样调用的:#150 labels = table(NULL, LABELS);#151 stmtlabs = table(NULL, LAB原创 2007-06-01 00:11:00 · 3496 阅读 · 3 评论 -
LCC编译器的源程序分析(69)全局变量的初始化
前面已经介绍了全局函数和全局变量的声明处理,但全局变量的初始化,还没有详细地分析,现在就来干这件事情。比如编写C的程序,有如下的代码:#001 #002 int g_nTest = 100;#003 #004 int main(void)#005 {#006 int nTest1 = 1;#007 int nTest2 = 2;像第2行代码就是全局变量的声明和初原创 2007-08-31 22:07:00 · 5502 阅读 · 4 评论 -
LCC编译器的源程序分析(22)基本表达式
表达式是C编译器里最重要的一部份,由于表达式的使用是无所不在,任何的计算都需要使用到表达式运算。这次就带你去分析一下LCC编译器处理表达式的代码。比如在例子里:int nTest1 = 1;int nTest2 = 2;赋值语句的右边是一个表达式,这个表达式可以简单的也可以复杂的。像下面语句的右边也是表达式:nTest3 = nTest1 + nTest2;还有很多地方都需原创 2007-06-02 20:57:00 · 3092 阅读 · 0 评论 -
LCC编译器的源程序分析(21)局部变量的声明
局部变量的处理是比较特别,它是复杂语句里面声明,作用域也只限于复合语句里。如下面的例子:{ int a = 2;}上面的a就是局部变量的声明。现在再来仔细地分析compound里处理局部变量的代码。如下:#031 //局部变量声明处理.#032 while ( kind[t] == CHAR || kind[t] == STATIC#033原创 2007-06-02 20:55:00 · 2881 阅读 · 0 评论 -
LCC编译器的源程序分析(18)函数定义
激动人心的时刻就要开始了,从这节开始,就进入处理实际的代码了。由于C语言是函数式的语言,也就是每个程序都是有一个一个的函数组成的,一个C源程序至少包含一个函数(main函数),也可以包含一个main函数和若干个其它函数。因此,函数是C程序的基本单位。仔细地查看一下第一节里的例子代码,它是如下:#001 #include #002 #003 int main(void)#004原创 2007-05-29 23:26:00 · 3616 阅读 · 0 评论 -
LCC编译器的源程序分析(19)全局函数的定义
函数定义funcdefn处理里,已经准备好调用参数和参数返回,接着就是调用全局函数声明来处理。如下面的代码:#132 //声明函数。#133 cfunc = dclglobal(sclass, id, ty, &pt);#134 上面的代码是处理函数全局定义。现在就去就分析dclglobal函数的实现,它主要用来分析全局函数的。它的代码如下:#001 //全局函数声明。原创 2007-05-30 22:26:00 · 3673 阅读 · 1 评论 -
LCC编译器的源程序分析(17)参数变量的声明
函数里的参数变量个数不固定,因此也需要检查这些参数的名称是否相同,还需要检查类型的合法性。现在就来分析上次提到的函数dclparam,它的代码如下:#001 //参数类型声明处理#002 static Symbol dclparam(int sclass, char *id, Type ty, Coordinate *pos) #003 {#004 Symbol p;#原创 2007-05-28 22:13:00 · 3246 阅读 · 2 评论 -
LCC编译器的源程序分析(68)内存分配链表
LCC采用大块内存的方法,那它分配内存也是比较特殊的,它的源程序如下:#001 //大块内存结构。#002 struct block#003 {#004 struct block *next; //后继块指针。#005 char *limit; //尾位置#006 char *avail; //可用的开始位置.#007 };#008 #0原创 2007-08-26 11:35:00 · 4591 阅读 · 1 评论 -
LCC编译器的源程序分析(67)删除内存链表
LCC的内存分配是使用先分配大块内存,然后再从大块内存里分配出来小块内存,这样减少调用malloc的次数,当然也就减少调用函数free释放内存的次数,以便提高LCC的内存管理的性能。有网友对这个看不懂,我现在就把释放内存函数分析如下: #001 //#002 //删除内存链表.#003 //#004 //蔡军生 2007/08/23 QQ:9073204 深圳#005 //原创 2007-08-23 21:50:00 · 4734 阅读 · 3 评论 -
LCC编译器的源程序分析(16)函数的声明
在第一节介绍的例子里,就需要使用到函数声明。比如下面的语句: printf("nTest3 = %d/r/n",nTest3);如果没有预先的声明,编译器是找不到它的类型定义,所以编译不通过。因此就需要了解什么是函数的声明,在LCC里又是怎么样处理函数的声明的。在hello.i文件里,有下面一句的函数声明:int printf(const char *, ...);要分原创 2007-05-27 18:49:00 · 3657 阅读 · 1 评论 -
LCC编译器的源程序分析(13)指针类型的声明
在C语言里,指针是最灵活的数据类型,它具有低级语言的特点,高效快速,不过学会它就不是那么容易了。由于指针是直接面向机器的,也就是它是指向内存的地址,因此使用C来编写嵌入式软件,或者操作系统的软件是比较合适的选择。下面就来看例子里的指针语句,如下:typedef char * va_list;上面这句声明了va_list为char的指针类型的别名,那么在LCC里又是怎么样处理它的呢?先识原创 2007-05-23 22:54:00 · 3603 阅读 · 3 评论 -
LCC编译器的源程序分析(12)自定义类型的声明
语法分析是比较复杂的处理,下面再来分析一个例子,它的代码如下:typedef unsigned short wchar_t;typedef wchar_t wint_t;第一句语句在LCC里的处理,前面已经解释清楚,主要生成wchar_t保存符号表里,并且记录这个ID的类型属性。那么第二句是怎么样通过上面的函数来分析的呢?接下来就去分析C编译器怎么处理它。与第一句语句一样,先识原创 2007-05-22 22:10:00 · 3505 阅读 · 0 评论 -
LCC编译器的源程序分析(15)结构类型成员的声明
上次只介绍到开始分析结构类型的定义开始部分,接着就要去分析它的成员类型定义了。它调用函数来处理结构的成员,如下代码:#001 static void fields(Type ty) #002 {#003 { #004 int n = 0;#005 while (istypename(t, tsym)) #006 {#00原创 2007-05-26 20:05:00 · 3164 阅读 · 2 评论 -
LCC编译器的源程序分析(14)结构类型的声明
以前都是简单类型的识别和语法分析,现在来分析结构的声明,它是比较复杂的一种数据类型,但结构在编写程序中使用是非常多的。由于程序的方程式就是:数据结构+算法=程序现在面向对象的方程式是:数据结构+算法=对象对象+对象=程序由上面的公式,就可以看出程序中的数据结构是非常重要的,无论是面向对象的编程,还是面向过程的编程,有什么样的数据结构,就需要有什么样算法。而在C语言里,使用结构类原创 2007-05-25 21:38:00 · 2875 阅读 · 1 评论 -
LCC编译器的源程序分析(25)赋值表达式
C语言是非常强大的,可以连续赋值的操作。将赋值表达式作为表达式的一种,使赋值操作不仅可以出现在赋值语句中,而且可以以表达式的形式出现在其它语句中,这是C语言灵活性的一种表现。变量 赋值运算符 表达式上面就是赋值表达式的组成,如下面的例子:x = y = 2+8;这样的赋值表达式在LCC里是通过函数expr1来实现的。#001 Tree expr1(int tok) #00原创 2007-06-03 18:11:00 · 3011 阅读 · 0 评论 -
LCC编译器的源程序分析(28)函数表达式语句
前面已经介绍了很多表达式,但还没有介绍函数表达式语句,那么在LCC里是怎么样处理函数调用,也就是函数表达式的呢?现在就来分析函数表达式的代码,函数调用是使用非常多的,因此分析这里的代码,需要非常仔细地查看。从hello.i例子里,就可看到下面的函数表达式语句: printf("nTest3 = %d/r/n",nTest3);它是由ID名称printf、表达式("nTest3 = %d原创 2007-06-06 21:45:00 · 3649 阅读 · 1 评论 -
LCC编译器的源程序分析(26)逗号表达式
逗号表达式是优先级最低的表达式。像下面的例子:1+2,2+3;a = (a,b,c);还有在for语句中的使用:for(int a=3,b=4; a {} 要处理上面这些逗号表达式,在LCC里就需调用表达式处理函数expr,它的代码如下:#001 Tree expr(int tok) #002 {#003 static char stop[] = {原创 2007-06-04 21:18:00 · 2878 阅读 · 0 评论 -
LCC编译器的源程序分析(41)赋值表达式的有向无环图
前面已经介绍怎么样把赋值表达式变换到树的中间表示,接着下来编译器要做的事情就是怎么样把树变换成有向无环图。也许你会问为什么要把树变换成有向无环图,而不是直接生成最终代码呢?其实,学习过数据结构就很清楚有向无环图的应用,编译器里就是利用有向无环图的特性来进行局部代码优化的,最主要的优化就是删除公共表达式。下面就来分析LCC从树到有向无环图的实现代码。上面函数dcllocal里调用转换函数如下:原创 2007-06-19 23:55:00 · 4381 阅读 · 2 评论 -
LCC编译器的源程序分析(40)赋值表达式树
前面分析了表达式的语法,也分析了语句的语法,但它们最终的目的就是生成合适的中间表示,在LCC里是采用树作为中间表示的。现在就来分析语句生成什么样的树表示,下面的语句是来自例子里,如下:int nTest1 = 1;这个语句是声明了一个局部变量nTest1,并且给nTest1赋值为1。LCC编译器要把它变换到分析树的表示,这样才利于后面的分析和使用。它的分析树如下: 左子树=右子树原创 2007-06-18 22:26:00 · 3480 阅读 · 0 评论 -
LCC编译器的源程序分析(39)goto语句
在现代设计的程序里,很少再用到goto语句了。虽然使用goto语句是比较高效,但它使程序也会得非常难懂,非常难维护,比较容易出错,所以很少使用goto语句的。goto语句为无条件跳转语句,它的一般形式为: goto 标号;在LCC里的是用下面的代码来处理:#001 case GOTO: #002 walk(NULL, 0, 0);#003原创 2007-06-17 11:23:00 · 3107 阅读 · 0 评论 -
LCC编译器的源程序分析(38)return语句
在实现中,程序其实就是处理数据,然后输出处理过的结果,在C语句里最直接的方式就是函数的返回值。比如求两个数据的最大值,就可以通过函数返回值来返回最大值。而函数的返回值是通过函数中的return语句获得的。return语句后面是一个表达式,需要调用表达式函数来处理。下面来分析LCC里的代码:#001 case RETURN: #002 {#003原创 2007-06-16 20:07:00 · 3379 阅读 · 0 评论 -
LCC编译器的源程序分析(37)default语句
default语句是使用在switch语句的复合语句里,它是所有其它分支不能处理时的分支处理。在LCC里是如下处理的: #001 case DEFAULT: #002 if (swp == NULL)#003 error("illegal default label/n");#004 else if (swp->def原创 2007-06-15 21:28:00 · 2950 阅读 · 0 评论 -
LCC编译器的源程序分析(35)switch语句
switch语句是多分支选择语句,主要方便多个选择的情况使用,当然也可以使用if语句来实现,但嵌套的if语句过多会使用程序的可读性降低。switch(表达式){ case 常量表达式1: 语句1; case 常量表达式2: 语句2; … case 常量表达式n: 语句n; default: 语句n+1}原创 2007-06-13 19:04:00 · 3386 阅读 · 0 评论 -
LCC编译器的源程序分析(34)continue语句
continue语句的作用是跳过循环体中后面尚未执行的语句,接着进行下一次是否执行循环的判断。比如下面的例子: while(表达式1) { … if(表达式2) continue; … }下面就来仔细地分析LCC里处理continue语句的源程序:#001 case CONTINUE: #002 walk(原创 2007-06-12 21:20:00 · 3533 阅读 · 0 评论 -
LCC编译器的源程序分析(36)case语句
case语句是使用在switch语句之中,它实现了选择一个分支执行。当表达式的值与case后面的常量表达式的值相等时,就执行此case语句后面的语句。LCC处理这个语句的代码如下:#001 case CASE: #002 {#003 int lab = genlabel(1);#004 if (swp原创 2007-06-14 21:35:00 · 3192 阅读 · 0 评论 -
LCC编译器的源程序分析(33)break语句
break语句可以使用流程跳出switch语句的分支选择,当然它还可以用来从循环体内跳出循环体,即提前结束循环,接着执行循环下面的语句。但它不能用于循环语句和switch语句之外的其它语句中。比如下面的例子:for(;;){ if( a++ > 100) break;} 现在就来分析一下面处理break的代码:#001 case BREAK:原创 2007-06-11 21:55:00 · 3533 阅读 · 1 评论 -
LCC编译器的源程序分析(32)for循环语句
C语言中的for语句使用最为灵活,不仅可以用于循环次数已经确定的情况,而且可以用于循环次数不确定而只给出循环结束条件的情况。因此,这个语句的使用频率是最高的,当然它的处理情况比上面两种循环要复杂一些。它的形式如下: for(表达式1;表达式2;表达式3) 语句1它的执行过程是先求解表达式1的值,然后再计算表达式2的值。如果其值为真,就执行语句1,然后再执行表达式3。如原创 2007-06-10 14:12:00 · 3467 阅读 · 1 评论 -
LCC编译器的源程序分析(31)do while循环语句
do—while语句是实现“直到型”循环结构。一般形式如下:do{ 语句1}while(表达式) 上面的语句是这样的执行的,先执行一次语句1,然后判断表达式的值,如果表达式的值为真,也就是非零时,返回重新执行语句1,如此反复,直到表达式的值等于0为止。这个表达式与前面一个while语句的区别,就是表达式值的判断和语句1执行先后的问题。前面while语句先判断后执行语句1,原创 2007-06-09 23:43:00 · 2662 阅读 · 0 评论 -
LCC编译器的源程序分析(30)while循环语句
while语句的语义是这样定义的:while(表达式) 语句1当表达式为非0值时执行while语句中的内嵌语句1。其特点就是先判断表达式的值,然后再执行语句。LCC是通过下面的代码来处理这个语句的:#015 case WHILE: #016 whilestmt(genlabel(3), swp, lev + 1); #017 break;原创 2007-06-08 22:08:00 · 2637 阅读 · 0 评论 -
LCC编译器的源程序分析(29)if条件语句
if语句是用来判定所给定的条件是否满足,根据判定的结果决定执行给出来的两种操作之一。if语句有下面三种形式:1. if(表达式)语句2. if(表达式)语句1 else 语句23. if(表达式1)语句1 else if(表达式2)语句2else if(表达式3)语句3else 语句4那么LCC又是原创 2007-06-07 21:39:00 · 3245 阅读 · 0 评论 -
LCC编译器的源程序分析(27)基本语句
由前面的例子可以知道,C函数里包含数据定义部分和执行部分,执行部分就是由语句组成。计算机最基本的动作就是具备条件判断,这也是计算机具有这么大魔力的原因。而这些条件判断是通过语句来实现的。基本的语句有下面几种:1. 控制语句:if else for whiledo whilecontinuebreakswitchgotoreturn2. 函原创 2007-06-05 21:25:00 · 3413 阅读 · 0 评论 -
LCC编译器的源程序分析(23)一元运算表达式
前面分析了基本表达式,主要由常量和变量ID组成。接着下来,就需要分析优先级比较高的一元运算表达式了,主要由第1级和第2级运算符组成的运算,一般只需要一个操作数就可以运算的表达式。在LCC里处理一元运算表达式的代码如下:#001 static Tree unary(void) #002 {#003 Tree p;#004 #005 switch (t) #006 {原创 2007-06-03 18:09:00 · 3141 阅读 · 0 评论 -
LCC编译器的源程序分析(11)声明与符号表
前一次已经分析了声明的函数,但还有一个声明函数没有分析的,它就是dclr函数,这个函数是大内总管,分别调用前面两个声明函数来处理所有的声明语句,接着又会保存声明的ID和属性到符号表,当然它需要调用处理函数定义的函数,接着在那里把函数生成汇编代码并写到输出文件里。现在就来看代码:#001 static void decl(Symbol (*dcl)(int, char *, Type, Co原创 2007-05-21 22:31:00 · 3922 阅读 · 0 评论 -
LCC编译器的源程序分析(9)声明分析
在语法分析里,最主要的组成部份是声明分析,并且这是C语言编译器最复杂的组成部分。由于任何变量都需要声明,那么怎么样知道这个变量声明是合法的呢?现在带着这个问题去分下面的代码。为了理解代码的工作,先来看前面的例子里的第一行有效代码:typedef unsigned int size_t;在这句语句里,使用类型定义关键字来声明了一个无符号整数的类型size_t,为了识别这句语句的语法原创 2007-05-19 23:26:00 · 3997 阅读 · 0 评论 -
LCC编译器的源程序分析(58)后端使用的节点结构
在LCC编译器后端,主要使用下面的节点结构来标识代码属性。#001 #002 //节点注释的扩展,主要是代码生成使用。#003 //#004 //蔡军生 2007/07/21 QQ: 9073204#005 //#006 typedef struct {#007 unsigned listed:1; //标记这是树的根节点。#008 unsig原创 2007-07-21 21:29:00 · 4205 阅读 · 0 评论 -
LCC编译器的源程序分析(54)指令模式匹配
在LCC编译器里,先把下面的语句翻译成中间表示,int nTest1 = 1;其中间表示的树如下:ASGNI4(ADDRLP4(nTest1), CNSTI4(1))然后根据上述的中间表示进行指令模式匹配。下面的函数_label就是进行这样的工作:#001 static void _label(NODEPTR_TYPE a) {#002 int c;#003 s原创 2007-07-14 19:29:00 · 3649 阅读 · 0 评论 -
LCC编译器的源程序分析(53)指令的选择
像下面的语句:int nTest1 = 1;选择什么样的汇编指令生成的呢?又是怎么样去选择指令的呢?在本例子里,LCC是选择下面的指令生成的:#010 mov dword [ebp + -12], 1 现在就来分析选择指令过程的代码,先分析函数gen的代码:#001 Node gen(Node forest) {#002 int i;#003 struct no原创 2007-07-11 19:57:00 · 3809 阅读 · 0 评论 -
LCC编译器的源程序分析(56)寄存器分配的属性结构
现在详细地分析寄存器分配的属性结构,它的定义如下:#001 typedef struct {#002 Symbol vbl; //保存变量符号,而不是临时变量.#003 short set; //寄存器类型,比如整数,或者浮点数。#004 short number; //寄存器编号。#005 unsigned mask; //寄存器字节大小,比如原创 2007-07-17 22:15:00 · 3931 阅读 · 0 评论