在LCC里,最重要的一个特征是可以输出不同的目标代码。比如同一个C程序,可以生成MIPS,X86等汇编代码,只需要选择不同的目标参数。这里只分析生成X86的代码,所以命令行的参数如下:
rcc.exe -target=x86/nasm hello.i hello.asm
参数-target=x86/nasm是让C编译器选择生成X86的NASM汇编代码。
参数hello.i是前面已经介绍的中间文件,它是经过预处理的。
参数hello.asm是生成的目标文件。
C编译的入口函数是main,它在文件main.c里,定义如下:
//
//C编译器入口。
//蔡军生2007/05/13 深圳
//
int main(int argc, char *argv[])
在main函数开始里,就是处理参数的代码,如下:
#001int main(int argc, char *argv[])
#002{
#003int i, j;
#004
#005for (i = argc - 1; i > 0; i--)
#006{
#007 if (strncmp(argv[i], "-target=", 8) == 0)
#008 {
#009 break;
#010 }
#011}
#012
#013if (i > 0)
#014{
#015 char *s = strchr(argv[i], '//');
#016 if (s != NULL)
#017 {
#018 *s = '/';
#019 }
#020
#021 for (j = 0; bindings[j].name && bindings[j].ir; j++)
#022 {
#023 if (strcmp(&argv[i][8], bindings[j].name) == 0)
#024 {
#025 IR = bindings[j].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", argv[0]);
#039 if (i > 0)
#040 {
#041 fprint(stderr, " `%s'", &argv[i][8]);
#042 }
#043
#044 fprint(stderr, "; must specify one of/n");
#045
#046 for (i = 0; bindings[i].name; i++)
#047 {
#048 fprint(stderr, "/t-target=%s/n", bindings[i].name);
#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全局变量里。
到这里,就分析完成选择不同的目标代码生成接口了。
本文介绍LCC编译器如何通过命令行参数选择不同的目标代码生成器,如X86 NASM汇编代码,并详细解析了相关源代码。
1083

被折叠的 条评论
为什么被折叠?



