




已阅读5页,还剩210页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
LCC编译器的源程序分析目录1C编译器的目标32LCC编译器的预处理53选择不同的目标代码接口84处理文件参数115行号同步与类型初始化166词法分析227语法分析的开始348声明分析419声明类型4610声明与符号表5111自定义类型的声明5412指针类型的声明5613结构类型的声明5914结构类型成员的声明6215函数的声明6616参数变量的声明7117函数定义7218全局函数的定义8019复合语句8320局部变量的声明8721基本表达式9222一元运算表达式9623条件表达式10124赋值表达式10225逗号表达式10326基本语句10427函数表达式语句11128if条件语句11729while循环语句11930do while循环语句12031break语句12332continue语句12433switch语句12534case语句12835default语句12936return语句12937goto语句13138赋值表达式树13339赋值表达式的有向无环图13540函数名称的代码生成13941函数代码入口和出口的代码生成14042计算需要使用栈大小14343寄存器分配14644分配一个寄存器14845寄存器溢出14946指令的选择15247指令模式匹配15348最终代码的生成15949寄存器分配的属性结构16050不同目标代码生成的接口结构16151后端使用的节点结构16252代码生成的源程序注释16353代码表的结构19554复合语句的代码块流程19755生成常量树节点的流程19856创建DAG森林的源程序19857符号表的结构注释19958后端接口的结构注释20159DAG树分析例子20360删除内存链表20461内存分配链表20562全局变量的初始化2081 C编译器的目标先从简单的目标来分析这个大规模的C编译器,毕竟它的功能比较复杂,并且源程序的行数也是非常多的。因此,把简单的目标定出来,然后再分析它,这样才会有的放矢。接着再跟着编译运行的主线来分析它的源程序。下面先看一下简单的C例子,如下:#001#include #002#003int main(void)#004#005int nTest1 = 1;#006int nTest2 = 2;#007int nTest3;#008int i;#009#010nTest3 = nTest1 + nTest2;#011printf(nTest3 = %drn,nTest3);#012#013for (i = 0; i 5; i+)#014#015 printf(%drn,nTest3+i);#016#017#018printf(_TIME_ _DATE_rnhello worldn);#019return 0;#020#021上面的程序就是用来说明编译器工作的例子,它在第一行里包含了头文件stdio.h,由于后面调用printf函数输出显示到屏幕里。第二行空行,第三行是main函数,它是C程序的入口函数。在main函数里,定义了几个局部变量,分别第5,6,7,8行的变量。第10行作两个变量nTest1和nTest2的加法,然后赋值给变量nTest3。第11行显示变量nTest3的值,是用10进制输出显示。在第13到16行是5次输出nTest3+i值。在第18行里输出编译这个程序的时间和hello world的字符串。C编译器的任务,就是把上面的源程序变换到汇编代码输出,或者变成其它中间代码输出。在这里LCC编译器是输出汇编代码的,所以就不介绍其它的中间代码输出。那么LCC把上面的源程序变成什么样的汇编输出呢?下面就先把它的目标代码看一下,如下:#001global $main#002section .text#003$main:#004push ebx#005push esi#006push edi#007push ebp#008mov ebp, esp#009sub esp, 16#010mov dword ebp + -12, 1#011mov dword ebp + -16, 2#012mov edi, dword ebp + -12#013mov esi, dword ebp + -16#014lea edi, esi + edi#015mov dword ebp + -8, edi#016mov edi, dword ebp + -8#017push dword edi#018lea edi, $L2#019push dword edi#020call $printf#021add esp, 8#022mov dword ebp + -4, 0#023$L3:#024mov edi, dword ebp + -8#025mov esi, dword ebp + -4#026lea edi, esi + edi#027push dword edi#028lea edi, $L7#029push dword edi#030call $printf#031add esp, 8#032$L4:#033inc dword ebp + -4#034cmp dword ebp + -4, 5#035jl near $L3#036lea edi, $L8#037push dword edi#038call $printf#039add esp, 4#040mov eax, 0#041$L1:#042mov esp, ebp#043pop ebp#044pop edi#045pop esi#046pop ebx#047ret#048extern $printf#049section .data#050times ($-$) & 0 nop#051$L8:#052db 00:30:28 Apr 07 2007, 13, 10, hello world, 10, 0#053times ($-$) & 0 nop#054$L7:#055db %d, 13, 10, 0#056times ($-$) & 0 nop#057$L2:#058db nTest3 = %d, 13, 10, 0#059LCC是可以生成很多目标代码的C编译器,在这里主要介绍生成X86的NASM汇编的代码。上面的汇编代码就是NASM的汇编格式,可以使用NASM编译生成目标文件,然后再用连接程序生成可执行文件。如果不能看懂上面的NASM汇编,就需要去看NASM手册了,这个手册在网上有下载。如果想更深入理解汇编生成机器码的过程,当然也可以深入分析NASM的程序实现。从上面的C和汇编也可以看出,汇编代码比C代码要复杂,行数也比较多,还分了数据段和代码段。所以使用C编译器是可以大大地提高生产效率的,并且更容易理解,这样就容易降低软件的成本,容易开发大规模的软件工程。2 LCC编译器的预处理上面已经介绍了C编译器的目标,其实在实现这个目标之前,是经历了很多阶段处理的,其中第一个阶段的处理,就是预处理。预处理的任务是做什么呢?在LCC里预处理主要是把所有包含的头文件和源程序生成一个中间文件,并且把所有宏展开,替换为真实的值。前面介绍的例子源程序,经过预处理后,就会生成下面的代码,如下:#line 1 hello.c#line 1 include/stdio.htypedef unsigned int size_t;typedef unsigned short wchar_t;typedef wchar_t wint_t;typedef wchar_t wctype_t;typedef char * va_list;#line 39 include/stdio.hstruct _iobuf char *_ptr;int _cnt;char *_base;int _flag;int _file;int _charbuf;int _bufsiz;char *_tmpfname;typedef struct _iobuf FILE;extern FILE *_iob;typedef long fpos_t;extern FILE (*_imp_iob);int _filbuf(FILE *);int flsbuf(int, FILE *);FILE * _fsopen(const char *, const char *, int);void clearerr(FILE *);int fclose(FILE *);int _fcloseall(void);FILE * fdopen(int, const char *);int feof(FILE *);int ferror(FILE *);int fflush(FILE *);int fgetc(FILE *);wchar_t fgetwc(FILE *);wchar_t getwc(FILE *);int _fgetchar(void);int fgetpos(FILE *, fpos_t *);char * fgets(char *, int, FILE *);int fileno(FILE *);int _flushall(void);FILE * fopen(const char *, const char *);int fprintf(FILE *, const char *, .);int xfprintf(FILE *,const char *,.);int fputc(int, FILE *);int _fputchar(int);int fputs(const char *, FILE *);size_t fread(void *, size_t, size_t, FILE *);FILE * freopen(const char *, const char *, FILE *);int fscanf(FILE *, const char *, .);int xfscanf(FILE *,const char *,.);int fsetpos(FILE *, const fpos_t *);int fseek(FILE *, long, int);long ftell(FILE *);size_t fwrite(const void *, size_t, size_t, FILE *);int getc(FILE *);int getchar(void);char * gets(char *);int getw(FILE *);int _pclose(FILE *);FILE * popen(const char *, const char *);int printf(const char *, .);int xprintf(const char *,.);int dprintf(const char *, .);int putc(int, FILE *);int putchar(int);int puts(const char *);int _putw(int, FILE *);int remove(const char *);int rename(const char *,const char *);void rewind(FILE *);int _rmtmp(void);int scanf(const char *, .);int xscanf(const char *,.);void setbuf(FILE *, char *);int setvbuf(FILE *, char *, int, size_t);int _snprintf(char *, size_t, const char *, .);int sprintf(char *, const char *, .);int xsprintf(char *, const char *, .);int snprintf(char *,size_t,const char *,.);int xsnprintf(char *,size_t,const char *,.);int sscanf(const char *, const char *, .);int xsscanf(const char *,const char *,.);char * _tempnam(char *, char *);FILE * tmpfile(void);char * tmpnam(char *);char *tempnam(char *,char *);int ungetc(int, FILE *);int _unlink(const char *);int vfprintf(FILE *, const char *, va_list);int vprintf(const char *, va_list);int _vsnprintf(char *, size_t, const char *, va_list);int _vsnwprintf(wchar_t *, size_t, const wchar_t *, va_list);int vsnprintf(char *,size_t,const char *,va_list);int xvsnprintf(char *,size_t,const char *,va_list);int xvsnprintf(char *,size_t,const char *,va_list);int vsprintf(char *, const char *, va_list);int xvsprintf(char *, const char *, va_list);void perror(const char *);void _wperror(const wchar_t *);#line 2 hello.cint main(void)int nTest1 = 1;int nTest2 = 2;int nTest3;int i;nTest3 = nTest1 + nTest2;printf(nTest3 = %drn,nTest3);for (i = 0; i 0; i-)#006 #007 if (strncmp(argvi, -target=, 8) = 0)#008 #009 break;#010 #011#012#013if (i 0) #014#015 char *s = strchr(argvi, );#016 if (s != NULL)#017 #018 *s = /;#019 #020#021 for (j = 0; & bindingsj.ir; j+)#022 #023 if (strcmp(&argvi8, ) = 0) #024 #025 IR = bindingsj.ir;#026 break;#027 #028 #029#030 if (s != NULL)#031 #032 *s = ;#033 #034#035#036if (!IR) #037#038 fprint(stderr, %s: unknown target, argv0);#039 if (i 0)#040 #041 fprint(stderr, %s, &argvi8);#042 #043#044 fprint(stderr, ; must specify one ofn);#045#046 for (i = 0; ; i+)#047 #048 fprint(stderr, t-target=%sn, );#049 #050#051 exit(EXIT_FAILURE);#052程序的第5行到第11行是找到目标代码的参数,也就是识别-target=参数。如果找到就跳出循环。程序的第13行到第34行是找到相应的生成目标代码的接口。C编译器已经定义好可以生成代码的数组,因此这里只需要简单地比较一下名称,就可以找到相应的目标代码的接口了。接口定义的结构如下:typedef struct binding char *name;Interface *ir; Binding;name保存目标代码的名称,ir保存了接口。而接口定义如下:#001typedef struct interface #002Metrics charmetric;#003Metrics shortmetric;#004Metrics intmetric;#005Metrics longmetric;#006Metrics longlongmetric;#007Metrics floatmetric;#008Metrics doublemetric;#009Metrics longdoublemetric;#010Metrics ptrmetric;#011Metrics structmetric;#012unsigned little_endian:1;#013unsigned mulops_calls:1;#014unsigned wants_callb:1;#015unsigned wants_argb:1;#016unsigned left_to_right:1;#017unsigned wants_dag:1;#018unsigned unsigned_char:1;#019void (*address)(Symbol p, Symbol q, long n);#020void (*blockbeg)(Env *);#021void (*blockend)(Env *);#022void (*defaddress)(Symbol);#023void (*defconst)(int suffix, int size, Value v);#024void (*defstring)(int n, char *s);#025void (*defsymbol)(Symbol);#026void (*emit) (Node);#027void (*export)(Symbol);#028void (*function)(Symbol, Symbol, Symbol, int);#029Node (*gen) (Node);#030void (*global)(Symbol);#031void (*import)(Symbol);#032void (*local)(Symbol);#033void (*progbeg)(int argc, char *argv);#034void (*progend)(void);#035void (*segment)(int);#036void (*space)(int);#037void (*stabblock)(int, int, Symbol*);#038void (*stabend)(Coordinate *, Symbol, Coordinate *, Symbol *, Symbol *);#039void (*stabfend) (Symbol, int);#040void (*stabinit) (char *, int, char *);#041void (*stabline) (Coordinate *);#042 void (*stabsym)(Symbol);#043void (*stabtype) (Symbol);#044Xinterface x;#045 Interface;接口定义了输出汇编目标代码需要的函数和数据成员。以后再一个函数一个函数地介绍。继续分析main函数里,第36行到第52行是找不到生成目标代码接口的出错处理。命令行的参数是-target=x86/nasm,所以找到的接口,就是x86/name的接口,它保存在IR全局变量里。到这里,就分析完成选择不同的目标代码生成接口了。4 处理文件参数上面已经介绍选择不同的目标输出的参数处理,那么接着下来,自然的事情就是处理剩下的两个参数的问题,当然LCC是可以处理更多其它参数的,但这里只介绍两个文件参数的处理。命令行如下:rcc.exe -target=x86/nasm hello.i hello.asm其中hello.i是输入文件,hello.asm是输出文件。那么LCC是怎么样打开输入文件和输出文件呢?输入文件又有什么技巧呢?要仔细地理解源程序,就知道它的输入处理是非常高效的。当选择合适的目标输出后,就调用下面的函数来处理:init(argc, argv);这个函数就是用来处理其它参数的。它的源程序如下:#001void init(int argc, char *argv) #002#003#004 extern void input_init(int, char *); #005 input_init(argc, argv);#006#007#008#009 extern void main_init(int, char *); #010 main_init(argc, argv);#011#012#013#014 extern void type_init(int, char *); #015 type_init(argc, argv);#016#017第1行代码里传入了命令行的参数。第5行代码是处理参数的处理。如果在第5行里调用没有处理main_init,那么在第10行里会再次调用它进行参数处理。第15行调函数type_init进行类型初始化,比如C缺省的数据类型初始化,比如int类型,就初始化为4字节的有符号类型,还有很多其C默认的类型定义。先来分析函数input_init的源程序是做什么工作的,下面就是它的程序:#001void input_init(int argc, char *argv) #002#003static int inited;#004#005if (inited)#006 return;#007#008inited = 1;#009main_init(argc, argv);#010#011limit = cp = &bufferMAXLINE+1;#012bsize = -1;#013lineno = 0;#014file = NULL;#015#016fillbuf();#017if (cp = limit)#018 cp = limit;#019#020nextline();#021第5行处理是否初始化,因为只允许初始化一次。第8行设置初始化变量为1,让这段代码不要运行两次。第9行调用主要参数处理函数。后面再接着介绍。第11行让当前行指针和缓冲区指针指向输入缓冲区的尾部。第12行初始化读取文件块大小为-1,也就是读取文件失败的状态。第13行设置分析的C程序行号为0。第14行设置当前输入文件名称为空。第16行是从输入文件里读取数据到输入缓冲区,同时设置当前处理的指针。第17行判断当前指针是否大于数据缓冲区的指针。第20行读取下一行源程序到缓冲区里。调用函数main_init主要处理参数,并且打开输入的文件和输出的文件。它的程序如下:#001void main_init(int argc, char *argv) #002#003char *infile = NULL, *outfile = NULL;#004int i;#005static int inited;#006#007if (inited)#008 return;#009#010inited = 1;#011for (i = 1; i stabline;#026 stabIR.stabend = IR-stabend;#027 IR-stabline = stabline;#028 IR-stabend = stabend;#029 #030 #031 #032 else if (strcmp(argvi, -x) = 0)#033 xref+;#034 else if (strcmp(argvi, -A) = 0) #035 #036 +Aflag;#037 #038 else if (strcmp(argvi, -P) = 0)#039 Pflag+;#040 else if (strcmp(argvi, -w) = 0)#041 wflag+;#042 else if (strcmp(argvi, -v) = 0)#043 fprint(stderr, %s %sn, argv0, rcsid);#044 else if (strncmp(argvi, -s, 2) = 0)#045 density = strtod(&argvi2, NULL);#046 else if (strncmp(argvi, -errout=, 8) = 0) #047 #048 FILE *f = fopen(argvi+8, w);#049 if (f = NULL) #050 #051 fprint(stderr, %s: cant write errors to %sn, argv0, argvi+8);#052 exit(EXIT_FAILURE);#053 #054#055 fclose(f);#056 f = freopen(argvi+8, w, stderr);#057 assert(f);#058 #059 else if (strncmp(argvi, -e, 2) = 0) #060 #061 int x;#062 if (x = strtol(&argvi2, NULL, 0) 0)#063 errlimit = x;#064 #065 else if (strncmp(argvi, -little_endian=, 15) = 0)#066 IR-little_endian = argvi15 - 0;#067 else if (strncmp(argvi, -mulops_calls=, 18) = 0)#068 IR-mulops_calls = argvi18 - 0;#069 else if (strncmp(argvi, -wants_callb=, 13) = 0)#070 IR-wants_callb = argvi13 - 0;#071 else if (strncmp(argvi, -wants_argb=, 12) = 0)#072 IR-wants_argb = argvi12 - 0;#073 else if (strncmp(argvi, -left_to_right=, 15) = 0)#074 IR-left_to_right = argvi15 - 0;#075 else if (strncmp(argvi, -wants_dag=, 11) = 0)#076 IR-wants_dag = argvi11 - 0;#077 else if (*argvi != - | strcmp(argvi, -) = 0) #078 #079 if (infile = NULL)#080 infile = argvi;#081 else if (outfile = NULL)#082 outfile = argvi;#083 #084#085 if (infile != NULL & strcmp(infile, -) != 0#086 & freopen(infile, r, stdin) = NULL) #087 #088 fprint(stderr, %s: cant read %sn, argv0, infile);#089 exit(EXIT_FAILURE);#090 #091#092 if (outfile != NULL & strcmp(outfile, -) != 0#093 & freopen(outfile, w, stdout) = NULL) #094 #095 fprint(stderr, %s: cant write %sn, argv0, outfile);#096 exit(EXIT_FAILURE);#097 #098第7行到第10行,同样是让这个函数只运行一次的代码。第79行到第82行是读取输入文件和输出文件的名称。第85行到第90行是打开输入的文件,并处理出错的情况。第9
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 诉讼执行方案文案(3篇)
- 厂区排水检查方案(3篇)
- DB23-T2989-2021-玉米线虫矮化病病原长岭发垫刃线虫鉴定技术规程-黑龙江省
- 物业车辆清洗方案(3篇)
- 公司设备使用管理制度
- 具体社区管理方案(3篇)
- 小学疫情防疫管理制度
- 危重病人护理管理制度
- 施工方案工期(3篇)
- 公司文件格式管理制度
- 三级安全教育试题(公司级、部门级、班组级)
- 消化道出血护理查房7
- MOOC 模拟电子电路实验-东南大学 中国大学慕课答案
- 中班绘本《跑跑镇》微课件
- 基于岗位拓展模型和KPI的主基二元考核绩效体系的构建
- 初三英语毕业考试补考试卷
- 公司《质量管理标准化手册》
- 水平井管内砾石充填防砂 ppt课件
- 电子招生网站设计--网络课程设计
- 运动控制系统思考题参考答案阮毅
- 附件:10kV 及以下配网工程设计说明书(范本)
评论
0/150
提交评论