gcc 源码分析:从IR-RTL 到汇编输出

在完成了IR-RTL的优化与寄存器分配后

就来到汇编代码的输出:

实现如下:

class pass_final : public rtl_opt_pass
{
public:
  pass_final (gcc::context *ctxt)
    : rtl_opt_pass (pass_data_final, ctxt)
  {}

  /* opt_pass methods: */
  unsigned int execute (function *) final override
  {
    return rest_of_handle_final ();
  }

}; // class pass_final

} // anon namespace

rest_of_handle_final (void)
{
  const char *fnname = get_fnname_from_decl (current_function_decl);

  /* Turn debug markers into notes if the var-tracking pass has not
     been invoked.  */
  if (!flag_var_tracking && MAY_HAVE_DEBUG_MARKER_INSNS)
    delete_vta_debug_insns (false);

  assemble_start_function (current_function_decl, fnname);
  rtx_insn *first = get_insns ();
  int seen = 0;
  final_start_function_1 (&first, asm_out_file, &seen, optimize);
  final_1 (first, asm_out_file, seen, optimize);
  if (flag_ipa_ra
      && !lookup_attribute ("noipa", DECL_ATTRIBUTES (current_function_decl))
      /* Functions with naked attributes are supported only with basic asm
     statements in the body, thus for supported use cases the information
     on clobbered registers is not available.  */
      && !lookup_attribute ("naked", DECL_ATTRIBUTES (current_function_decl)))
    collect_fn_hard_reg_usage ();
  final_end_function ();

  /* The IA-64 ".handlerdata" directive must be issued before the ".endp"
     directive that closes the procedure descriptor.  Similarly, for x64 SEH.
     Otherwise it's not strictly necessary, but it doesn't hurt either.  */
  output_function_exception_table (crtl->has_bb_partition ? 1 : 0);

  assemble_end_function (current_function_decl, fnname);

  /* Free up reg info memory.  */
  free_reg_info ();

  if (! quiet_flag)
    fflush (asm_out_file);

  /* Note that for those inline functions where we don't initially
     know for certain that we will be generating an out-of-line copy,
     the first invocation of this routine (rest_of_compilation) will
     skip over this code by doing a `goto exit_rest_of_compilation;'.
     Later on, wrapup_global_declarations will (indirectly) call
     rest_of_compilation again for those inline functions that need
     to have out-of-line copies generated.  During that call, we
     *will* be routed past here.  */

  timevar_push (TV_SYMOUT);
  if (!DECL_IGNORED_P (current_function_decl))
    debug_hooks->function_decl (current_function_decl);
  timevar_pop (TV_SYMOUT);

  /* Release the blocks that are linked to DECL_INITIAL() to free the memory.  */
  DECL_INITIAL (current_function_decl) = error_mark_node;

  if (DECL_STATIC_CONSTRUCTOR (current_function_decl)
      && targetm.have_ctors_dtors)
    targetm.asm_out.constructor (XEXP (DECL_RTL (current_function_decl), 0),
                 decl_init_priority_lookup
                   (current_function_decl));
  if (DECL_STATIC_DESTRUCTOR (current_function_decl)
      && targetm.have_ctors_dtors)
    targetm.asm_out.destructor (XEXP (DECL_RTL (current_function_decl), 0),
                decl_fini_priority_lookup
                  (current_function_decl));
  return 0;
}

static void
final_1 (rtx_insn *first, FILE *file, int seen, int optimize_p)

{
  rtx_insn *insn, *next;

  /* Used for -dA dump.  */
  basic_block *start_to_bb = NULL;
  basic_block *end_to_bb = NULL;
  int bb_map_size = 0;
  int bb_seqn = 0;

  last_ignored_compare = 0;

  init_recog ();

  CC_STATUS_INIT;

  if (flag_debug_asm)
    {
      basic_block bb;

      bb_map_size = get_max_uid () + 1;
      start_to_bb = XCNEWVEC (basic_block, bb_map_size);
      end_to_bb = XCNEWVEC (basic_block, bb_map_size);

      /* There is no cfg for a thunk.  */
      if (!cfun->is_thunk)
    FOR_EACH_BB_REVERSE_FN (bb, cfun)
      {
        start_to_bb[INSN_UID (BB_HEAD (bb))] = bb;
        end_to_bb[INSN_UID (BB_END (bb))] = bb;
      }
    }

  /* Output the insns.  */
  for (insn = first; insn;)
    {
      if (HAVE_ATTR_length)
    {
      if ((unsigned) INSN_UID (insn) >= INSN_ADDRESSES_SIZE ())
        {
          /* This can be triggered by bugs elsewhere in the compiler if
         new insns are created after init_insn_lengths is called.  */
          gcc_assert (NOTE_P (insn));
          insn_current_address = -1;
        }
      else
        insn_current_address = INSN_ADDRESSES (INSN_UID (insn));
      /* final can be seen as an iteration of shorten_branches that
         does nothing (since a fixed point has already been reached).  */
      insn_last_address = insn_current_address;
    }

      dump_basic_block_info (file, insn, start_to_bb, end_to_bb,
                             bb_map_size, &bb_seqn);
      insn = final_scan_insn (insn, file, optimize_p, 0, &seen);
    }

  maybe_output_next_view (&seen);

  if (flag_debug_asm)
    {
      free (start_to_bb);
      free (end_to_bb);
    }

  /* Remove CFI notes, to avoid compare-debug failures.  */
  for (insn = first; insn; insn = next)
    {
      next = NEXT_INSN (insn);
      if (NOTE_P (insn)
      && (NOTE_KIND (insn) == NOTE_INSN_CFI
          || NOTE_KIND (insn) == NOTE_INSN_CFI_LABEL))
    delete_insn (insn);
    }
}

rtx_insn *
final_scan_insn (rtx_insn *insn, FILE *file, int optimize_p,
         int nopeepholes, int *seen)

{
  static int *enclosing_seen;
  static int recursion_counter;

  gcc_assert (seen || recursion_counter);
  gcc_assert (!recursion_counter || !seen || seen == enclosing_seen);

  if (!recursion_counter++)
    enclosing_seen = seen;
  else if (!seen)
    seen = enclosing_seen;

  rtx_insn *ret = final_scan_insn_1 (insn, file, optimize_p, nopeepholes, seen);

  if (!--recursion_counter)
    enclosing_seen = NULL;

  return ret;
}

static rtx_insn *
final_scan_insn_1 (rtx_insn *insn, FILE *file, int optimize_p ATTRIBUTE_UNUSED,
           int nopeepholes ATTRIBUTE_UNUSED, int *seen)

{
  rtx_insn *next;
  rtx_jump_table_data *table;

  insn_counter++;

  /* Ignore deleted insns.  These can occur when we split insns (due to a
     template of "#") while not optimizing.  */
  if (insn->deleted ())
    return NEXT_INSN (insn);

  switch (GET_CODE (insn))
    {
    case NOTE:
      switch (NOTE_KIND (insn))
    {
    case NOTE_INSN_DELETED:
    case NOTE_INSN_UPDATE_SJLJ_CONTEXT:
      break;

    case NOTE_INSN_SWITCH_TEXT_SECTIONS:
      maybe_output_next_view (seen);

      output_function_exception_table (0);

      if (targetm.asm_out.unwind_emit)
        targetm.asm_out.unwind_emit (asm_out_file, insn);

      in_cold_section_p = !in_cold_section_p;

      gcc_checking_assert (in_cold_section_p);
      if (in_cold_section_p)
        cold_function_name
   &n

<think>嗯,用户想了解从源代码到目标文件的过程中GCC的中间表示(IR),需要举一个具体的编译例子。首先,我得确定自己对GCC的编译流程和IR的理解是否正确。GCC的编译过程通常分为预处理、编译、汇编和链接几个阶段。中间表示应该出现在编译阶段,也就是将预处理后的源代码转换为汇编代码之前的部分。 GCC使用的IR是什么?我记得GCC的中间表示是GIMPLE,这是在AST之后生成的,属于一种较低级的表示,方便进行各种优化。可能需要解释GIMPLE是什么,以及它在整个编译过程中的位置。 接下来,我需要想一个具体的例子。比如一个简单的C程序,计算两个数的平方和。这样的例子简单明了,适合演示。然后分步骤展示每个阶段生成的中间表示,特别是GIMPLE的形式。 需要注意用户的系统级指令,比如行内数学表达式用$...$,独立公式用$$...$$,并且用中文回答。同时,回答结构要清晰,逐步引导用户理解。需要确保LaTeX语法正确,比如变量、函数名等用正确格式。 可能用户对编译原理有一定了解,但需要具体的例子来形象化IR的作用。可能需要解释IR的作用,比如优化、跨平台等,以及GCC如何处理IR。同时,要说明IR汇编的转换过程,最后生成目标文件。 需要检查是否有错误的地方,比如GCC的处理阶段是否正确,GIMPLE是否在RTL之前,以及示例代码是否能正确展示IR。可能还需要确认示例中的GIMPLE代码是否准确,或者是否需要简化以适应解释。 最后,确保回答符合用户的要求,结构清晰,步骤明确,并且例子具体。可能需要用代码块展示不同阶段的代码,并解释每个部分的意义,特别是IR的结构和优化过程。</think>好的!我将通过一个具体示例逐步解释GCC的编译过程和中间表示(IR),并说明IR的作用。 --- ### 示例代码 我们以简单C程序为例: ```c // square.c int square(int x) { return x * x; } int main() { int a = 4; int b = square(a); return b; } ``` --- ### 编译过程与IR解析 #### 1. 预处理阶段 命令:`gcc -E square.c -o square.i` - 作用:处理宏、头文件展开等 - 输出:`square.i`(文本文件,仍是C代码) #### 2. 编译阶段(生成IR) 命令:`gcc -fdump-tree-gimple -S square.c` - 关键参数:`-fdump-tree-gimple` 生成GIMPLE格式的IR - **IR作用**:编译器在此阶段进行优化(如常量传播、死代码消除等) 生成文件:`square.c.004t.gimple` ```c square (int x) { int D.2345; D.2345 = x * x; return D.2345; } main () { int a; int b; int D.2346; a = 4; D.2346 = square (a); b = D.2346; D.2346 = b; return D.2346; } ``` #### GIMPLE IR特点: - **三地址码**格式:将复杂表达式拆解为简单操作 - **临时变量**:如`D.2345`用于存储中间结果 - **优化基础**:便于进行数据流分析和优化 #### 3. RTL阶段(更底层的IR) 命令:`gcc -fdump-rtl-all square.c` - 生成文件:`square.c.234r.expand` - 示例片段: ``` (insn 5 4 6 2 (set (reg:SI 85 [ x ]) (reg:SI 0 ax)) "square.c":2 -1 (nil)) ``` - **作用**:与机器架构相关,处理寄存器分配等 #### 4. 生成汇编代码 命令:`gcc -S square.c` - 输出:`square.s`(x86汇编示例) ```asm square: pushq %rbp movq %rsp, %rbp movl %edi, -4(%rbp) movl -4(%rbp), %eax imull %eax, %eax popq %rbp ret ``` #### 5. 生成目标文件 命令:`gcc -c square.c` - 输出:`square.o`(二进制目标文件) --- ### IR的核心作用 1. **平台无关优化**:在GIMPLE层进行与CPU架构无关的优化 2. **多阶段转换**:`C -> GIMPLE -> RTL -> ASM` 的逐步低级化 3. **调试支持**:通过`-fdump`系列参数观察中间状态 --- ### 关键流程图 $$ \begin{aligned} \text{C源代码} & \xrightarrow{\text{预处理}} \text{展开后的C代码} \\ & \xrightarrow{\text{编译}} \text{GIMPLE IR} \xrightarrow{\text{优化}} \text{优化后的IR} \\ & \xrightarrow{\text{RTL生成}} \text{机器相关IR} \xrightarrow{\text{汇编}} \text{目标文件} \end{aligned} $$ 是否需要我补充某个具体阶段的详细分析?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GoldKey

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值