编译器的核心始于YOUR-GCC-SOURCE-DIR/gcc目录下,文件main.c中的main函数。该函数立即调用函数toplev_main。
4684 int
4685 toplev_main (unsigned int argc, const char **argv) in toplev.c
4686 {
4687 save_argv = argv;
4688
4689 /* Initialization of GCC's environment, and diagnostics. */
4690 general_init (argv[0]);
4691
4692 /* Parse the options and do minimal processing; basically just
4693 enough to default flags appropriately. */
4694 decode_options (argc, argv);
4695
4696 randomize ();
4697
4698 /* Exit early if we can (e.g. -help). */
4699 if (!exit_after_options)
4700 do_compile ();
4701
4702 if (errorcount || sorrycount)
4703 return (FATAL_EXIT_CODE);
4704
4705 return (SUCCESS_EXIT_CODE);
4706 }
这个函数完成了整个的编译过程!它所调用的函数都非常复杂。现在我们就一个一个来学习它们。第一个被调用的函数是general_init。正如它的名字所表示的,它为编译器做一些基本的初始化工作。
4208 static void
4209 general_init (const char *argv0) in toplev.c
4210 {
4211 const char *p;
4212
4213 p = argv0 + strlen (argv0);
4214 while (p != argv0 && !IS_DIR_SEPARATOR (p[-1]))
4215 --p;
4216 progname = p;
4217
4218 xmalloc_set_program_name (progname);
4219
4220 hex_init ();
4221
4222 gcc_init_libintl ();
4223
4224 /* Initialize the diagnostics reporting machinery, so option parsing
4225 can give warnings and errors. */
4226 diagnostic_initialize (global_dc);
4227 /* Set a default printer. Language specific initializations will
4228 override it later. */
4229 pp_format_decoder (global_dc->printer) = &default_tree_printer;
4230
4231 /* Trap fatal signals, e.g. SIGSEGV, and convert them to ICE messages. */
4232 #ifdef SIGSEGV
4233 signal (SIGSEGV, crash_signal);
4234 #endif
4235 #ifdef SIGILL
4236 signal (SIGILL, crash_signal);
4237 #endif
4238 #ifdef SIGBUS
4239 signal (SIGBUS, crash_signal);
4240 #endif
4241 #ifdef SIGABRT
4242 signal (SIGABRT, crash_signal);
4243 #endif
4244 #if defined SIGIOT && (!defined SIGABRT || SIGABRT != SIGIOT)
4245 signal (SIGIOT, crash_signal);
4246 #endif
4247 #ifdef SIGFPE
4248 signal (SIGFPE, crash_signal);
4249 #endif
4250
4251 /* Other host-specific signal setup. */
4252 (*host_hooks.extra_signals)();
4253
4254 /* Initialize the garbage-collector, string pools and tree type hash
4255 table. */
4256 init_ggc ();
4257 init_stringpool ();
4258 init_ttree ();
4259
4260 /* Initialize register usage now so switches may override. */
4261 init_reg_sets ();
4262
4263 /* Register the language-independent parameters. */
4264 add_params (lang_independent_params, LAST_PARAM);
4265
4266 /* This must be done after add_params but before argument processing. */
4267 init_ggc_heuristics();
4268 }
在4218行,在支持sbrk 的平台上,xmalloc_set_program_name在first_break中保存程序当前数据段的边界。在内存分配失败的时候,它和102行的name一起,构成debugging信息。
98 void
99 xmalloc_set_program_name (s) in xmalloc.c
100 const char *s;
101 {
102 name = s;
103 #ifdef HAVE_SBRK
104 /* Win32 ports other than cygwin32 don't have brk() */
105 if (first_break == NULL)
106 first_break = (char *) sbrk (0);
107 #endif /* HAVE_SBRK */
108 }
而在4220行,函数hex_init只定义在使用EBCDIC编码的IBM及其兼容系统中。在这些系统里,hex_init初始化数组_hex_value,后者将EBCDIC的字符‘0’~’f’映射到对应的ASCII 字符。而在其他系统中,_hex_value是到这些ASCII字符的自身映射。
接下来,在4222行,gcc_init_libintl初始化了翻译库(这个取决于终端(terminal)对字符集的支持)。
在4251行,host_hooks提供了一个机制,使得平台有机会运行自己特定的初始化操作。对于Linux平台,extra_signals是一个空函数。
在4256行,init_ggc初始化了编译器的垃圾收集器。GCC的垃圾收集也是一个比较大的题目,我们以后再看它。
在C++中,每个标识符(identifier)都是通过其字面的名字来区分(前端总是使用字面名字在当前绑定域中进行名字查找。修饰名是给汇编器和链接器用的)。在GCC里,为了使名字查找更加高效,标识符都是保存在哈希表中,这个哈希表由4257行的init_stringpool 来初始化。
57 void
58 init_stringpool (void) in stringpool.c
59 {
60 /* Create with 16K (2^14) entries. */
61 ident_hash = ht_create (14);
62 ident_hash->alloc_node = alloc_node;
63 gcc_obstack_init (&string_stack);
64 }
上面61行的ident_hash是一个ht类型的全局对象。而在下面46行,stack是一个栈对象。所有标识符的名字都是从中分配的。
49 struct ht *ident_hash; in stringpool.c
43 struct ht in hashtable.h
44 {
45 /* Identifiers are allocated from here. */
46 struct obstack stack;
47
48 hashnode *entries;
49 /* Call back. */
50 hashnode (*alloc_node) (hash_table *);
51
52 unsigned int nslots; /* Total slots in the entries array. */
53 unsigned int nelements; /* Number of live elements. */
54
55 /* Link to reader, if any. For the benefit of cpplib. */
56 struct cpp_reader *pfile;
57
58 /* Table usage statistics. */
59 unsigned int searches;
60 unsigned int collisions;
61 };
上面48行的hashnode实际上是指向在下面27行的ht_identifier的指针。
37 typedef struct ht hash_table; in hashtable.h
38 typedef struct ht_identifier *hashnode;
26 typedef struct ht_identifier ht_identifier; in hashtable.h
27 struct ht_identifier GTY(())
28 {
29 const unsigned char *str;
30 unsigned int len;
31 unsigned int hash_value;
32 };
同时在27行,GTY(())把这个对象托管给垃圾回收器的标记。ht_create 一开始给哈希表分配一张有16K行的表。
53 hash_table *
54 ht_create (unsigned int order) in hashtable.c
55 {
56 unsigned int nslots = 1 << order;
57 hash_table *table;
58
59 table = xcalloc (1, sizeof (hash_table));
60
61 /* Strings need no alignment. */
62 _obstack_begin (&table->stack, 0, 0,
63 (void *(*) (long)) xmalloc,
64 (void (*) (void *)) free);
65
66 obstack_alignment_mask (&table->stack) = 0;
67
68 table->entries = xcalloc (nslots, sizeof (hashnode));
69 table->nslots = nslots;
70 return table;
71 }
66行的obstack_alignment_mask用于指定在该栈上创建对象的对齐掩模(注意这是栈的要求,类型本身还会有自己的对齐要求,最终的对齐量将是2者中的较大者)。
哈希表中的stack对象具有obstack类型,它有如下定义。它是栈的管理块。
168 struct obstack /* control current object in current chunk */ in obstack.h
169 {
170 long chunk_size; /* preferred size to allocate chunks in */
171 struct _obstack_chunk *chunk; /* address of current struct obstack_chunk */
172 char *object_base; /* address of object we are building */
173 char *next_free; /* where to add next char to current object */
174 char *chunk_limit; /* address of char after current chunk */
175 PTR_INT_TYPE temp; /* Temporary for some macros. */
176 int alignment_mask; /* Mask of alignment for each object. */
177 #if defined __STDC__ && __STDC__
178 /* These prototypes vary based on `use_extra_arg', and we use
179 casts to the prototypeless function type in all assignments,
180 but having prototypes here quiets -Wstrict-prototypes. */
181 struct _obstack_chunk *(*chunkfun) (void *, long);
182 void (*freefun) (void *, struct _obstack_chunk *);
183 void *extra_arg; /* first arg for chunk alloc/dealloc funcs */
184 #else
185 struct _obstack_chunk *(*chunkfun) (); /* User's fcn to allocate a chunk. */
186 void (*freefun) (); /* User's function to free a chunk. */
187 char *extra_arg; /* first arg for chunk alloc/dealloc funcs */
188 #endif
189 unsigned use_extra_arg:1; /* chunk alloc/dealloc funcs take extra arg */
190 unsigned maybe_empty_object:1;/* There is a possibility that the current
191 chunk contains a zero-length object. This
192 prevents freeing the chunk if we allocate
193 a bigger chunk to replace it. */
194 unsigned alloc_failed:1; /* No longer used, as we now call the failed
195 handler on error, but retained for binary
196 compatibility. */
197 };
要使用obstack来管理内存,需要按对齐要求初始化这个对象,并指定用于分配和释放的函数。在这里,传入的size是0(一开始是空的),alignment是0(没有特殊要求),函数xmalloc作为chunkfun,函数free作为freefun。
150 int
151 _obstack_begin (h, size, alignment, chunkfun, freefun) in obstack.c
152 struct obstack *h;
153 int size;
154 int alignment;
155 #if defined (__STDC__) && __STDC__
156 POINTER (*chunkfun) (long);
157 void (*freefun) (void *);
158 #else
159 POINTER (*chunkfun) ();
160 void (*freefun) ();
161 #endif
162 {
163 register struct _obstack_chunk *chunk; /* points to new chunk */
164
165 if (alignment == 0)
166 alignment = (int) DEFAULT_ALIGNMENT;
167 if (size == 0)
168 /* Default size is what GNU malloc can fit in a 4096-byte block. */
169 {
170 /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
171 Use the values for range checking, because if range checking is off,
172 the extra bytes won't be missed terribly, but if range checking is on
173 and we used a larger request, a whole extra 4096 bytes would be
174 allocated.
175
176 These number are irrelevant to the new GNU malloc. I suspect it is
177 less sensitive to the size of the request. */
178 int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
179 + 4 + DEFAULT_ROUNDING - 1)
180 & ~(DEFAULT_ROUNDING - 1));
181 size = 4096 - extra;
182 }
183
184 #if defined (__STDC__) && __STDC__
185 h->chunkfun = (struct _obstack_chunk * (*)(void *, long)) chunkfun;
186 h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
187 #else
188 h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun;
189 h->freefun = freefun;
190 #endif
191 h->chunk_size = size;
192 h->alignment_mask = alignment - 1;
193 h->use_extra_arg = 0;
194
195 chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
196 if (!chunk)
197 (*obstack_alloc_failed_handler) ();
198 h->next_free = h->object_base = chunk->contents;
199 h->chunk_limit = chunk->limit
200 = (char *) chunk + h->chunk_size;
201 chunk->prev = 0;
202 /* The initial chunk now contains no empty object. */
203 h->maybe_empty_object = 0;
204 h->alloc_failed = 0;
205 return 1;
206 }
在下面通过fooalign,DEFAULT_ALIGNMENT实际上是double的对齐量,在Linux x86上是4。而DEFAULT_ROUNDING使用fooround来决定对第一个块的舍入值,在Linux x86上是8。
62 struct fooalign {char x; double d;}; in obstack.h
63 #define DEFAULT_ALIGNMENT /
64 ((PTR_INT_TYPE) ((char *) &((struct fooalign *) 0)->d - (char *) 0))
65 /* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT.
66 But in fact it might be less smart and round addresses to as much as
67 DEFAULT_ROUNDING. So we prepare for it to do that. */
68 union fooround {long x; double d;};
69 #define DEFAULT_ROUNDING (sizeof (union fooround))
我们期望分配的块能刚好占据一个页面(通常是4k)。对于这个块大小,我们要扣除被malloc用于簿记数据的大小。在上面178行的注释提到,malloc里结构体mhead 用了12字节,另有4个额外字节。这两部分都要在DEFAULT_ROUNDING大小的边界上对齐,因此最终结果为24个字节。
被分配的对象具有类型_obstack_chunk,它有如下定义。
161 struct _obstack_chunk /* Lives at front of each chunk. */ in obstack.h
162 {
163 char *limit; /* 1 past end of this chunk */
164 struct _obstack_chunk *prev; /* address of prior chunk or NULL */
165 char contents[4]; /* objects begin here */
166 };
正如它的名字所提示的,obstack只能被用作栈。栈的生长将从contents开始直到到达limit – 块的底部。填满的块通过prev域依次连接。
可以通过以下的宏从参数h(obstack对象)中分配由length指定大小的内存。还有一种是通过obstack_grow系列宏在obstack中分配类如字符串这样的没有对齐要求的对象。
566 #define obstack_alloc(h,length) / in obstack.h
567 (obstack_blank ((h), (length)), obstack_finish ((h)))
560 #define obstack_blank(h,length) / in obstack.h
561 ( (h)->temp = (length), /
562 (((h)->chunk_limit - (h)->next_free < (h)->temp) /
563 ? (_obstack_newchunk ((h), (h)->temp), 0) : 0), /
564 obstack_blank_fast (h, (h)->temp))
当块中剩余的内存足够,将执行以下步骤。
348 #define obstack_blank_fast(h,n) ((h)->next_free += (n)) in obstack.h
上面这个宏使得空对象能被检查出来。下面576行,如果obstack_blank_fast执行后next_free等于object_base,我们在请求空对象。当所要求的内存能被满足,就要调用下面的obstack_finish使内存被真正分配出来。
575 #define obstack_finish(h) / in obstack.h
576 ( ((h)->next_free == (h)->object_base /
577 ? (((h)->maybe_empty_object = 1), 0) /
578 : 0), /
579 (h)->temp = __PTR_TO_INT ((h)->object_base), /
580 (h)->next_free /
581 = __INT_TO_PTR ((__PTR_TO_INT ((h)->next_free)+(h)->alignment_mask) /
582 & ~ ((h)->alignment_mask)), /
583 (((h)->next_free - (char *) (h)->chunk /
584 > (h)->chunk_limit - (char *) (h)->chunk) /
585 ? ((h)->next_free = (h)->chunk_limit) : 0), /
586 (h)->object_base = (h)->next_free, /
587 __INT_TO_PTR ((h)->temp))
因为next_free 需要在alignment_mask的边界上对齐,next_free的结果可能超出块的范围。在这种情况下,会有部分用于对齐要求的字节分配不出来,但不影响分配出的对象的使用。因此调整next_free至块的底部,不考虑这部分字节。注意到587行是obstack_finish的值,它同时也是obstack_alloc的值,指向新分配对象的头。
当当前块剩余空间不能满足分配要求(上面562行),需要分配足够大的新块。注意到下面282行,如果通过obstack_alloc来分配对象,next_free和object_base 一直是相等的(通过obstack_grow的话,则表示已请求,但尚未真正分配的内存。在调用obstack_finish前,可以多次调用obstack_grow)。在287行,我们不能只分配所要求的内存,应该稍微多分一点,尤其对于obstack_grow这样的应用。另外新块的容量不能小于chunk_size(4k – 24)。
274 void
275 _obstack_newchunk (h, length) in obstack.c
276 struct obstack *h;
277 int length;
278 {
279 register struct _obstack_chunk *old_chunk = h->chunk;
280 register struct _obstack_chunk *new_chunk;
281 register long new_size;
282 register long obj_size = h->next_free - h->object_base;
283 register long i;
284 long already;
285
286 /* Compute size for new chunk. */
287 new_size = (obj_size + length) + (obj_size >> 3) + 100;
288 if (new_size < h->chunk_size)
289 new_size = h->chunk_size;
290
291 /* Allocate and initialize the new chunk. */
292 new_chunk = CALL_CHUNKFUN (h, new_size);
293 if (!new_chunk)
294 (*obstack_alloc_failed_handler) ();
295 h->chunk = new_chunk;
296 new_chunk->prev = old_chunk;
297 new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size;
298
299 /* Move the existing object to the new chunk.
300 Word at a time is fast and is safe if the object
301 is sufficiently aligned. */
302 if (h->alignment_mask + 1 >= DEFAULT_ALIGNMENT)
303 {
304 for (i = obj_size / sizeof (COPYING_UNIT) - 1;
305 i >= 0; i--)
306 ((COPYING_UNIT *)new_chunk->contents)[i]
307 = ((COPYING_UNIT *)h->object_base)[i];
308 /* We used to copy the odd few remaining bytes as one extra COPYING_UNIT,
309 but that can cross a page boundary on a machine
310 which does not do strict alignment for COPYING_UNITS. */
311 already = obj_size / sizeof (COPYING_UNIT) * sizeof (COPYING_UNIT);
312 }
313 else
314 already = 0;
315 /* Copy remaining bytes one by one. */
316 for (i = already; i < obj_size; i++)
317 new_chunk->contents[i] = h->object_base[i];
318
319 /* If the object just copied was the only data in OLD_CHUNK,
320 free that chunk and remove it from the chain.
321 But not if that chunk might contain an empty object. */
322 if (h->object_base == old_chunk->contents && ! h->maybe_empty_object)
323 {
324 new_chunk->prev = old_chunk->prev;
325 CALL_FREEFUN (h, old_chunk);
326 }
327
328 h->object_base = new_chunk->contents;
329 h->next_free = h->object_base + obj_size;
330 /* The new chunk certainly contains no empty object yet. */
331 h->maybe_empty_object = 0;
332 }
对于没有被真正分配的内存请求,接下来需要将它们从旧块拷贝到新块。因为contents标记了块的头,object_base标记了未兑现内存请求的开头。在旧块中,如果2者相等,而且块不包含空对象,那么该块只包含了这个已被移去新块的内存请求,它可以被释放。
作为栈,当要释放指定对象时,所有在其上的对象亦被释放。
597 #define obstack_free(h,obj) / in obstack.h
598 ( (h)->temp = (char *) (obj) - (char *) (h)->chunk, /
599 (((h)->temp > 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)/
600 ? (int) ((h)->next_free = (h)->object_base /
601 = (h)->temp + (char *) (h)->chunk) /
602 : (_obstack_free ((h), (h)->temp + (char *) (h)->chunk), 0)))
如果对象不在当前块,它必定在其他块中。我们已经见到块通过prev域连在一起。越接近当前块,块就越新鲜。根据后进先出的栈原则,当我们找到目标块时,那些通过prev链访问到的块也需要释放。
372 void
373 _obstack_free (h, obj) in obstack.c
374 struct obstack *h;
375 POINTER obj;
376 {
377 register struct _obstack_chunk *lp; /* below addr of any objects in this chunk */
378 register struct _obstack_chunk *plp; /* point to previous chunk if any */
379
380 lp = h->chunk;
381 /* We use >= because there cannot be an object at the beginning of a chunk.
382 But there can be an empty object at that address
383 at the end of another chunk. */
384 while (lp != 0 && ((POINTER) lp >= obj || (POINTER) (lp)->limit < obj))
385 {
386 plp = lp->prev;
387 CALL_FREEFUN (h, lp);
388 lp = plp;
389 /* If we switch chunks, we can't tell whether the new current
390 chunk contains an empty object, so assume that it may. */
391 h->maybe_empty_object = 1;
392 }
393 if (lp)
394 {
395 h->object_base = h->next_free = (char *) (obj);
396 h->chunk_limit = lp->limit;
397 h->chunk = lp;
398 }
399 else if (obj != 0)
400 /* obj is not in any of the chunks! */
401 abort ();
402 }
如果有块被释放,目标块会变更为当前块。这时我们不可能知道其上是否有空对象,因此假定其有。在_obstack_newchunk中,这个块就不会因为没有对象而被释放。那么万一有空对象,它也不会因为块已被释放而走入401行。
如init_stringpool的62行所示,标识符哈希表元素的分配函数是alloc_node。
67 static hashnode
68 alloc_node (hash_table *table ATTRIBUTE_UNUSED) in stringpool.c
69 {
70 return GCC_IDENT_TO_HT_IDENT (make_node (IDENTIFIER_NODE));
71 }
因为make_node使用IDENTIFIER_NODE作为参数,它会使用lang_hooks.identifier_size 来确定节点的大小。对于C++,它是定义在langhooks-def.h中的默认值 - sizeof (struct lang_identifier)。lang_identifier也是与语言相关的。对于C++,它有如下定义。
220 struct lang_identifier GTY(()) in cp-tree.h
221 {
222 struct c_common_identifier c_common;
223 cxx_binding *namespace_bindings;
224 cxx_binding *bindings;
225 tree class_value;
226 tree class_template_info;
227 tree label_value;
228 tree implicit_decl;
229 tree error_locus;
230 };
在c_common_identifier的定义中,它的第一个成员是tree_common!
180 struct c_common_identifier GTY(()) in c-common.h
181 {
182 struct tree_common common;
183 struct cpp_hashnode node;
184 };
另外,在lang_identifier 定义中,域namespace_bindings指向定义了该标识符的名字空间,而域bindings指向定义了该标识符的非名字空间的域。后面我们可以看到这个安排对名字查找是必要的。
上面make_node返回新建的lang_identifier对象,GCC_IDENT_TO_HT_IDENT将其转换为tree_identifier,进而返回ht_identifier部分。
753 #define HT_IDENT_TO_GCC_IDENT(NODE) / in tree.h
754 ((tree) ((char *) (NODE) - sizeof (struct tree_common)))
755 #define GCC_IDENT_TO_HT_IDENT(NODE) (&((struct tree_identifier *) (NODE))->id)
在tree_identifier的定义中,它的第一个成员也是tree_common。但和cpp_hashnode比较,它第二个成员是什么呢?
757 struct tree_identifier GTY(()) in tree.h
758 {
759 struct tree_common common;
760 struct ht_identifier id;
761 };
顺理成章,cpp_hashnode的第一个成员是ht_identifier。正如我们已看到的,标识符除了呆在树里,还需要通过哈希表来组织。因此,lang_identifier的第一个成员使得对象能生存在树中,而第二个成员使得对象在哈希表中也过得下去。
478 struct cpp_hashnode GTY(()) in cpplib.h
479 {
480 struct ht_identifier ident;
481 unsigned int is_directive : 1;
482 unsigned int directive_index : 7; /* If is_directive,
483 then index into directive table.
484 Otherwise, a NODE_OPERATOR. */
485 unsigned char rid_code; /* Rid code - for front ends. */
486 ENUM_BITFIELD(node_type) type : 8; /* CPP node type. */
487 unsigned char flags; /* CPP flags. */
488
489 union _cpp_hashnode_value
490 {
491 /* If a macro. */
492 cpp_macro * GTY((skip (""))) macro;
493 /* Answers to an assertion. */
494 struct answer * GTY ((skip (""))) answers;
495 /* Code for a builtin macro. */
496 enum builtin_type GTY ((tag ("1"))) builtin;
497 /* Macro argument index. */
498 unsigned short GTY ((tag ("0"))) arg_index;
499 } GTY ((desc ("0"))) value;
500 };
cpp_hashnode首先由词法分析器创建,彼时它是标记(token)cpp_token的一部分。它主要为预处理器使用。
这个哈希表的查找函数是cpp_lookup。
91 cpp_hashnode *
92 cpp_lookup (cpp_reader *pfile, const unsigned char *str, unsigned int len) in cpphash.c
93 {
94 /* ht_lookup cannot return NULL. */
95 return CPP_HASHNODE (ht_lookup (pfile->hash_table, str, len, HT_ALLOC));
96 }
在95行,ht_loopup返回hashnode的地址,CPP_HASHNODE将它转换为cpp_hashnode*。
470 #define CPP_HASHNODE(HNODE) ((cpp_hashnode *) (HNODE)) in cpplib.h