GCC源码分析(五)——指令生成

本文深入探讨GCC编译器的指令生成过程,重点解析RTL(Register Transfer Language)及其与INSN的关系。从GIMPLE到RTL的转换称为“expand”,通过expand_gimple_stmt等函数将GIMPLE_CALL转化为INSN。Machine Description文件定义了针对不同CPU平台的指令模板,genemit工具将这些模板转换为代码,用于生成和识别insn,最终通过final_pass将RTL转换为ASM代码。
摘要由CSDN通过智能技术生成

一、前言

  又有好久没写了,的确很忙。前篇介绍了GCC的pass格局,它是GCC中间语言部分的核心架构,也是贯穿整个编译流程的核心。在完成优化处理之后,GCC必须做的最后一步就是生成最后的编译结果,通常情况下就是汇编文件(文本或者二进制并不重要)。

  前面也讲到了,GCC中间语言的核心数据结构是GENERIC、GIMPLE和RTL。其中的RTL就是和指令紧密相关的一种结构,它是指令生成的起点。

二、RTL和INSN

2.1 什么是RTL,什么是INSN

  RTL叫做寄存器转移语言(Register Transfering Language)。说是寄存器,其实也包含内存操作。RTL被设计成一种函数式语言,由表达式和对象构成。其中对象指的是寄存器、内存和值(常数或者表达式的值),表达式就是对对象和子表达式的操作。这些在gcc internal里面都有介绍。

  RTL对象和操作组成RTL表达式,子表达式加上操作组成复合RTL表达式。当一个RTL表达式表示一条中间语言指令时,这个RTL表达式叫做INSN。RTL表达式(RTL Expression)在gcc代码中缩写为RTX,代码中的rtx类型就是指向RTL表达式的指针。所以insn就是rtx,但是rtx不一定是insn。

2.2 INSN的生成

  RTL是由gimple生成的,从gimple到RTL的转换叫做“expand”。在整个优化的pass链中,这一步由pass_expand完成。该pass实现在gcc/cfgexpand.c中。它的execute函数gimple_expand_cfg很长,但是核心工作是对每个basic block进行转换:

  FOR_BB_BETWEEN (bb, init_block->next_bb, EXIT_BLOCK_PTR, next_bb)
    bb = expand_gimple_basic_block (bb);
expand_gimple_basic_block会调用expan_gimple_stmt来展开每一个gimple语句,并将展开后的rtx连接在一起。首先就有一个问题:insn是怎么生成的?

  此外,每个expand_xxx函数只负责一部分工作,有些函数有rtx类型的返回值,有些函数没有返回值。那些有返回值的函数通常也不会有变量来保存它们返回的insn。那么就有另外一个问题:那些展开的insn到哪里去了?

  为了弄清楚这两个问题,首先要找到生成insn的地方。这是一项工程浩大的体力活,不妨从某个点来研究这个问题,比如就从函数调用的语句来入手吧。我们可以从expand_gimple_basic_block开始顺藤摸瓜,来看看一个GIMPLE_CALL是如何翻译成insn的。

  首先,expand_gimple_basic_block里有一个对basic block里的gimple statement的遍历循环,在这个循环里面,首先判断了一些特殊的情况,比如debug之类的,忽略之。直到循环最后一部分才进入正题:

	  if (is_gimple_call (stmt) && gimple_call_tail_p (stmt)) // 尾调用,特殊情况,忽略之
	    {
	      bool can_fallthru;
	      new_bb = expand_gimple_tailcall (bb, stmt, &can_fallthru);
	      if (new_bb)
		{
		  if (can_fallthru)
		    bb = new_bb;
		  else
		    return new_bb;
		}
	    }
	  else
	    {
	      def_operand_p def_p;
	      def_p = SINGLE_SSA_DEF_OPERAND (stmt, SSA_OP_DEF);

	      if (def_p != NULL)
		{
		  /* Ignore this stmt if it is in the list of
		     replaceable expressions.  */
		  if (SA.values
		      && bitmap_bit_p (SA.values,
				       SSA_NAME_VERSION (DEF_FROM_PTR (def_p))))
		    continue;
		}
	      last = expand_gimple_stmt (stmt); // 这是真正干活的地方
	      maybe_dump_rtl_for_gimple_stmt (stmt, last);
	    }

  进入到expand_gimple_stmt里面,这个函数不长,一眼可以看出来,核心是expand_gimple_stmt_1 (stmt);,这个函数分情况展开了stmt。其中GIMPLE_CALL对应的是expand_call_stmt。这个函数也不长,关键在最后。

  if (lhs)
    expand_assignment (lhs, exp, false); // lhs = func(args)
  else
    expand_expr_real_1 (exp, const0_rtx, VOIDmode, EXPAND_NORMAL, NULL); // func(args)

  gimple call语句形如 lhs = func ( args ); 。其中,lhs是可以没有的。所以如果存在lhs的话,就按赋值语句展开。否则的话就按表达式展开。赋值语句的右边也是表达式,因此按赋值语句展开最终也会将“func(args)”部分按表达式展开。

  expand_gimple_expr_1函数很长,因为要处理的表达式类型比较多。其中我们关注的是case CALL_EXPR:分支:

    case CALL_EXPR:
      /* All valid uses of __builtin_va_arg_pack () are removed during
	 inlining.  */
      if (CALL_EXPR_VA_ARG_PACK (exp))
	error ("%Kinvalid use of %<__builtin_va_arg_pack ()%>", exp);
      {
	tree fndecl = get_callee_fndecl (exp), attr;

	if (fndecl
	    && (attr = lookup_attribute ("error",
					 DECL_ATTRIBUTES (fndecl))) != NULL)
	  error ("%Kcall to %qs declared with attribute error: %s",
		 exp, identifier_to_locale (lang_hooks.decl_printable_name (fndecl, 1)),
		 TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
	if (fndecl
	    && (attr = lookup_attribute ("warning",
					 DECL_ATTRIBUTES (fndecl))) != NULL)
	  warning_at (tree_nonartificial_location (exp),
		      0, "%Kcall to %qs declared with attribute warning: %s",
		      exp, identifier_to_locale (lang_hooks.decl_printable_name (fndecl, 1)),
		      TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));

	/* Check for a built-in function.  */
	if (fndecl && DECL_BUILT_IN (fndecl))
	  {
	    gcc_assert (DECL_BUILT_IN_CLASS (f
  • 16
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值