从lang_dependent_init返回,编译器终于差不多准备好了,可以开始对我们的源代码进行解析。这里看到,每次从命令行执行源文件的编译,编译器都要进行上面那些复杂的初始化过程。这样一次编译,被称为一个编译单元。从代码中可以看到,C++编译器的初始化要远远复杂于C编译器的初始化;而且在后面,亦可看到,对C++语言的处理也要远比C语言的处理要复杂。对于简单的源程序,C++编译器无疑效率要低些。但对于复杂的源程序,C++强大的运行时库,及中间树形式的运行时环境,还有丰富的语言特性能派上用场,其结果就未必。
do_compile (continue)
4660 compile_file ();
4661
4662 if (flag_unit_at_a_time)
4663 {
4664 rtl_dump_file = cgraph_dump_file;
4665 cgraph_dump_file = NULL;
4666 close_dump_file (DFI_cgraph, NULL, NULL_RTX);
4667 }
4668 }
这里,compile_file要编译整个编译单元,并把汇编代码及各种调试转储输出到文件。
1810 static void
1811 compile_file (void) in toplev.c
1812 {
1813 /* Initialize yet another pass. */
1814
1815 init_final (main_input_filename);
1816 coverage_init (aux_base_name);
1817
1818 timevar_push (TV_PARSE);
1819
1820 /* Call the parser, which parses the entire file (calling
1821 rest_of_compilation for each function). */
1822 (*lang_hooks.parse_file) (set_yydebug);
例程init_final初始化最后遍(final pass)所用的数据。这里如果启动了对输出汇编代码的APP处理(这个变量和宏ASM_APP_ON一起使用,这个宏是一个C的字符串常量,被输出在每个汇编语句或一组连串汇编语句之前。通常这是“#APP”,它是一个对绝大多数汇编器没有影响的注释,但会提醒GNU汇编器必须要检查随后的所有有效汇编构建),app_on 被设置为非0值。而如果我们将要输出insn序列,则变量final_sequence将包含对应的rtx序列。
241 void
242 init_final (const char *filename ATTRIBUTE_UNUSED) in final.c
243 {
244 app_on = 0;
245 final_sequence = 0;
246
247 #ifdef ASSEMBLER_DIALECT
248 dialect_number = ASSEMBLER_DIALECT;
249 #endif
250 }
接着,coverage_init执行对gcov – GCC中用于评估测试代码覆盖率的工具的初始化。这里我们跳过它。对于C++语言,在上面1822行,用于源程序解析的钩子是以下的c_common_parse_file。
1219 void
1220 c_common_parse_file (int set_yydebug ATTRIBUTE_UNUSED) in c-opts.c
1221 {
1222 unsigned file_index;
1223
1224 #if YYDEBUG != 0
1225 yydebug = set_yydebug;
1226 #else
1227 warning ("YYDEBUG not defined");
1228 #endif
1229
1230 file_index = 0;
1231
1232 do
1233 {
1234 if (file_index > 0)
1235 {
1236 /* Reset the state of the parser. */
1237 c_reset_state();
1238
1239 /* Reset cpplib's macros and start a new file. */
1240 cpp_undef_all (parse_in);
1241 main_input_filename = this_input_filename
1242 = cpp_read_main_file (parse_in, in_fnames[file_index]);
1243 if (this_input_filename == NULL)
1244 break;
1245 }
1246 finish_options ();
1247 if (file_index == 0)
1248 pch_init ();
1249 c_parse_file ();
1250
1251 file_index++;
1252 } while (file_index < num_in_fnames);
1253
1254 finish_file ();
1255 }
注意在c_common_post_options中,命令行中的第一个源文件已经被读入,保存在变量main_input_filename中。而变量num_in_fnames则记录了命令行里出现的源文件的数目。这些源文件的名字都保存在数组in_fnames中。这里看到同一命令行中的源文件的内容都会合并到一起,产生一个共同的前端树。
下面如果cpp_opts->preprocessed是非0值,表示我们正在处理经过预处理的源文件。否则,对于每个输入源文件,现在所有的命令行选项都已被记录,但其中一些尚未得到处理,仍被保存在deferred_opts中。同时也要为运行时建立一些宏定义,这些宏定义将影响系统头文件中的定义。
1411 static void
1412 finish_options (void) in c-opts.c
1413 {
1414 if (!cpp_opts->preprocessed)
1415 {
1416 size_t i;
1417
1418 cpp_change_file (parse_in, LC_RENAME, _("<built-in>"));
1419 cpp_init_builtins (parse_in, flag_hosted);
1420 c_cpp_builtins (parse_in);
注意到在1418行,cpp_change_file具有以下的定义。具有值LC_RENAME的参数reason 表示文件名或行数,因为除进入或离开新文件以外的原因,发生变化(例如,#line指示)。这里它表示以下的内容构成一个被包含系统文件“built-in”。
973 void
974 cpp_change_file (cpp_reader *pfile, enum lc_reason reason, in cppfiles.c
975 const char *new_name)
976 {
977 _cpp_do_file_change (pfile, reason, new_name, 1, 0);
978 }
关于改变源文件的处理的细节,参考前面更换文件一节。
C++标准定义了一些预定义的宏,我们可以在自己的程序中使用它们。编译器使用数组builtin_array来描述这些宏。
289 static const struct builtin builtin_array[] = in cppinit.c
290 {
291 B("__TIME__", BT_TIME),
292 B("__DATE__", BT_DATE),
293 B("__FILE__", BT_FILE),
294 B("__BASE_FILE__", BT_BASE_FILE),
295 B("__LINE__", BT_SPECLINE),
296 B("__INCLUDE_LEVEL__", BT_INCLUDE_LEVEL),
297 /* Keep builtins not used for -traditional-cpp at the end, and
298 update init_builtins() if any more are added. */
299 B("_Pragma", BT_PRAGMA),
300 B("__STDC__", BT_STDC),
301 };
上面的最后2个宏是传统模式不支持的。而在下面348行的FOR循环中,函数cpp_lookup将这些宏的名字所对应的标识符插入到ident_hash这个哈希表。因为这些标识符代表的是内建对象,也要相应设置哈希表的节点。同时注意,这里我们没有给这些宏指定具体的内容——我们还不知道。
339 void
340 cpp_init_builtins (cpp_reader *pfile, int hosted) in cppinit.c
341 {
342 const struct builtin *b;
343 size_t n = ARRAY_SIZE (builtin_array);
344
345 if (CPP_OPTION (pfile, traditional))
346 n -= 2;
347
348 for(b = builtin_array; b < builtin_array + n; b++)
349 {
350 cpp_hashnode *hp = cpp_lookup (pfile, b->name, b->len);
351 hp->type = NT_MACRO;
352 hp->flags |= NODE_BUILTIN | NODE_WARN;
353 hp->value.builtin = b->value;
354 }
355
356 if (CPP_OPTION (pfile, cplusplus))
357 _cpp_define_builtin (pfile, "__cplusplus 1");
358 else if (CPP_OPTION (pfile, lang) == CLK_ASM)
359 _cpp_define_builtin (pfile, "__ASSEMBLER__ 1");
360 else if (CPP_OPTION (pfile, lang) == CLK_STDC94)
361 _cpp_define_builtin (pfile, "__STDC_VERSION__ 199409L");
362 else if (CPP_OPTION (pfile, c99))
363 _cpp_define_builtin (pfile, "__STDC_VERSION__ 199901L");
364
365 if (hosted)
366 _cpp_define_builtin (pfile, "__STDC_HOSTED__ 1");
367 else
368 _cpp_define_builtin (pfile, "__STDC_HOSTED__ 0");
369
370 if (CPP_OPTION (pfile, objc))
371 _cpp_define_builtin (pfile, "__OBJC__ 1");
372 }
接下来,在357行的语句,等同于声明:#define __cplusplus 1。对于这个具体的定义,下面的函数负责构建代表宏定义的cpp_macro节点。
1832 void
1833 _cpp_define_builtin (cpp_reader *pfile, const char *str) in cpplib.c
1834 {
1835 size_t len = strlen (str);
1836 char *buf = alloca (len + 1);
1837 memcpy (buf, str, len);
1838 buf[len] = '/n';
1839 run_directive (pfile, T_DEFINE, buf, len);
1840 }
顾名思义,指示要由run_directive执行。而指示的具体细节则保存在数组dtable里。
440 static void
441 run_directive (cpp_reader *pfile, int dir_no, const char *buf, size_t count) in cpplib.c
442 {
443 cpp_push_buffer (pfile, (const uchar *) buf, count,
444 /* from_stage3 */ true);
445 /* Disgusting hack. */
446 if (dir_no == T_PRAGMA)
447 pfile->buffer->file = pfile->buffer->prev->file;
448 start_directive (pfile);
449
450 /* This is a short-term fix to prevent a leading '#' being
451 interpreted as a directive. */
452 _cpp_clean_line (pfile);
453
454 pfile->directive = &dtable[dir_no];
455 if (CPP_OPTION (pfile, traditional))
456 prepare_directive_trad (pfile);
457 pfile->directive->handler (pfile);
458 end_directive (pfile, 1);
459 if (dir_no == T_PRAGMA)
460 pfile->buffer->file = NULL;
461 _cpp_pop_buffer (pfile);
462 }
在443行,cpp_push_buffer把buf链入pfile的buffer域。注意到在447行,buffer的file域是类型_cpp_file,它代表了源文件。由于之前执行了cpp_push_buffer,当前的buffer就是指示本身,但是其file域为0。而某些#pragma指示的作用是施加在源文件的余下部分,因此447行的更新很重要。
223 static void
224 start_directive (cpp_reader *pfile) in cpplib.c
225 {
226 /* Setup in-directive state. */
227 pfile->state.in_directive = 1;
228 pfile->state.save_comments = 0;
229
230 /* Some handlers need the position of the # for diagnostics. */
231 pfile->directive_line = pfile->line;
232 }
然后start_directive设置好pfile中相关的域,从而牵动下面lex_macro_node的行为。而上面的_cpp_clean_line,则把buffer中的cur及next_line域。设置为这个字符串的开头及结尾。在457行,handler实际上调用do_define。
502 static void
503 do_define (cpp_reader *pfile) in cpplib.c
504 {
505 cpp_hashnode *node = lex_macro_node (pfile);
506
507 if (node)
508 {
509 /* If we have been requested to expand comments into macros,
510 then re-enable saving of comments. */
511 pfile->state.save_comments =
512 ! CPP_OPTION (pfile, discard_comments_in_macro_exp);
513
514 if (_cpp_create_definition (pfile, node))
515 if (pfile->cb.define)
516 pfile->cb.define (pfile, pfile->directive_line, node);
517 }
518 }
函数_cpp_lex_token把buffer的内容分解成一个个符号(例如,标识符,字符串,数字等),并封装在cpp_token中返回。每次调用返回一个符号。
466 static cpp_hashnode *
467 lex_macro_node (cpp_reader *pfile) in cpplib.c
468 {
469 const cpp_token *token = _cpp_lex_token (pfile);
470
471 /* The token immediately after #define must be an identifier. That
472 identifier may not be "defined", per C99 6.10.8p4.
473 In C++, it may not be any of the "named operators" either,
474 per C++98 [lex.digraph], [lex.key].
475 Finally, the identifier may not have been poisoned. (In that case
476 the lexer has issued the error message for us.) */
477
478 if (token->type == CPP_NAME)
479 {
480 cpp_hashnode *node = token->val.node;
481
482 if (node == pfile->spec_nodes.n_defined)
483 cpp_error (pfile, CPP_DL_ERROR,
484 "/"defined/" cannot be used as a macro name");
485 else if (! (node->flags & NODE_POISONED))
486 return node;
487 }
488 else if (token->flags & NAMED_OP)
489 cpp_error (pfile, CPP_DL_ERROR,
490 "/"%s/" cannot be used as a macro name as it is an operator in C++",
491 NODE_NAME (token->val.node));
492 else if (token->type == CPP_EOF)
493 cpp_error (pfile, CPP_DL_ERROR, "no macro name given in #%s directive",
494 pfile->directive->name);
495 else
496 cpp_error (pfile, CPP_DL_ERROR, "macro names must be identifiers");
497
498 return NULL;
499 }
记得我们可以“毒死”一个识别符,从而从整个程序中把它移除。对于被“毒死”的标识符,它的flags域设置了NODE_POISONED。而那些flags域设置了NAMED_OP的标识符是C++的具名操作符。lex_macro_node确保识别符是有效的宏名字。
实现宏定义的细节,可以参考创建宏定义 – ISO模式一节。在515行的define句柄用于调试目的,这里我们跳过。
而函数end_directive恢复词法分析器的状态,并在skip_line为非0值时,跳过指示所在行剩余的内容。
235 static void
236 end_directive (cpp_reader *pfile, int skip_line) in cpplib.c
237 {
238 if (CPP_OPTION (pfile, traditional))
239 {
240 /* Revert change of prepare_directive_trad. */
241 pfile->state.prevent_expansion--;
242
243 if (pfile->directive != &dtable[T_DEFINE])
244 _cpp_remove_overlay (pfile);
245 }
246 /* We don't skip for an assembler #. */
247 else if (skip_line)
248 {
249 skip_rest_of_line (pfile);
250 if (!pfile->keep_tokens)
251 {
252 pfile->cur_run = &pfile->base_run;
253 pfile->cur_token = pfile->base_run.base;
254 }
255 }
256
257 /* Restore state. */
258 pfile->state.save_comments = ! CPP_OPTION (pfile, discard_comments);
259 pfile->state.in_directive = 0;
260 pfile->state.in_expression = 0;
261 pfile->state.angled_headers = 0;
262 pfile->directive = 0;
263 }