GCC-3.4.6源代码学习笔记(71)

4.        源代码解析

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中。这里看到同一命令行中的源文件的内容都会合并到一起,产生一个共同的前端树。

5.1. 设置系统的宏定义

下面如果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  }

 

关于改变源文件的处理的细节,参考前面更换一节。

5.1.1. C++内建宏

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_bufferbuf链入pfilebuffer域。注意到在447行,bufferfile域是类型_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中的curnext_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_tokenbuffer的内容分解成一个个符号(例如,标识符,字符串,数字等),并封装在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  }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值