integrate.c

来自「GCC编译器源代码」· C语言 代码 · 共 1,907 行 · 第 1/5 页

C
1,907
字号
   This routine need not copy any insns because we are not going   to immediately compile the insns in the insn chain.  There   are two cases when we would compile the insns for FNDECL:   (1) when FNDECL is expanded inline, and (2) when FNDECL needs to   be output at the end of other compilation, because somebody took   its address.  In the first case, the insns of FNDECL are copied   as it is expanded inline, so FNDECL's saved insns are not   modified.  In the second case, FNDECL is used for the last time,   so modifying the rtl is not a problem.   We don't have to worry about FNDECL being inline expanded by   other functions which are written at the end of compilation   because flag_no_inline is turned on when we begin writing   functions at the end of compilation.  */voidsave_for_inline_nocopy (fndecl)     tree fndecl;{  rtx insn;  rtx head;  rtx first_nonparm_insn;  /* Set up PARMDECL_MAP which maps pseudo-reg number to its PARM_DECL.     Later we set TREE_READONLY to 0 if the parm is modified inside the fn.     Also set up ARG_VECTOR, which holds the unmodified DECL_RTX values     for the parms, prior to elimination of virtual registers.     These values are needed for substituting parms properly.  */  parmdecl_map = (tree *) alloca (max_parm_reg * sizeof (tree));  /* Make and emit a return-label if we have not already done so.  */  if (return_label == 0)    {      return_label = gen_label_rtx ();      emit_label (return_label);    }  head = initialize_for_inline (fndecl, get_first_label_num (),				max_label_num (), max_reg_num (), 0);  /* If there are insns that copy parms from the stack into pseudo registers,     those insns are not copied.  `expand_inline_function' must     emit the correct code to handle such things.  */  insn = get_insns ();  if (GET_CODE (insn) != NOTE)    abort ();  /* Get the insn which signals the end of parameter setup code.  */  first_nonparm_insn = get_first_nonparm_insn ();  /* Now just scan the chain of insns to see what happens to our     PARM_DECLs.  If a PARM_DECL is used but never modified, we     can substitute its rtl directly when expanding inline (and     perform constant folding when its incoming value is constant).     Otherwise, we have to copy its value into a new register and track     the new register's life.  */  for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn))    {      if (insn == first_nonparm_insn)	in_nonparm_insns = 1;      if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')	{	  if (current_function_uses_const_pool)	    {	      /* Replace any constant pool references with the actual constant.		 We will put the constant back if we need to write the		 function out after all.  */	      save_constants (&PATTERN (insn));	      if (REG_NOTES (insn))		save_constants (&REG_NOTES (insn));	    }	  /* Record what interesting things happen to our parameters.  */	  note_stores (PATTERN (insn), note_modified_parmregs);	}    }  /* Also scan all decls, and replace any constant pool references with the     actual constant.  */  save_constants_in_decl_trees (DECL_INITIAL (fndecl));  /* We have now allocated all that needs to be allocated permanently     on the rtx obstack.  Set our high-water mark, so that we     can free the rest of this when the time comes.  */  preserve_data ();  finish_inline (fndecl, head);}/* Given PX, a pointer into an insn, search for references to the constant   pool.  Replace each with a CONST that has the mode of the original   constant, contains the constant, and has RTX_INTEGRATED_P set.   Similarly, constant pool addresses not enclosed in a MEM are replaced   with an ADDRESS and CONST rtx which also gives the constant, its   mode, the mode of the address, and has RTX_INTEGRATED_P set.  */static voidsave_constants (px)     rtx *px;{  rtx x;  int i, j; again:  x = *px;  /* If this is a CONST_DOUBLE, don't try to fix things up in      CONST_DOUBLE_MEM, because this is an infinite recursion.  */  if (GET_CODE (x) == CONST_DOUBLE)    return;  else if (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == SYMBOL_REF	   && CONSTANT_POOL_ADDRESS_P (XEXP (x,0)))    {      enum machine_mode const_mode = get_pool_mode (XEXP (x, 0));      rtx new = gen_rtx (CONST, const_mode, get_pool_constant (XEXP (x, 0)));      RTX_INTEGRATED_P (new) = 1;      /* If the MEM was in a different mode than the constant (perhaps we	 were only looking at the low-order part), surround it with a 	 SUBREG so we can save both modes.  */      if (GET_MODE (x) != const_mode)	{	  new = gen_rtx (SUBREG, GET_MODE (x), new, 0);	  RTX_INTEGRATED_P (new) = 1;	}      *px = new;      save_constants (&XEXP (*px, 0));    }  else if (GET_CODE (x) == SYMBOL_REF	   && CONSTANT_POOL_ADDRESS_P (x))    {      *px = gen_rtx (ADDRESS, GET_MODE (x),		     gen_rtx (CONST, get_pool_mode (x),			      get_pool_constant (x)));      save_constants (&XEXP (*px, 0));      RTX_INTEGRATED_P (*px) = 1;    }  else    {      char *fmt = GET_RTX_FORMAT (GET_CODE (x));      int len = GET_RTX_LENGTH (GET_CODE (x));      for (i = len-1; i >= 0; i--)	{	  switch (fmt[i])	    {	    case 'E':	      for (j = 0; j < XVECLEN (x, i); j++)		save_constants (&XVECEXP (x, i, j));	      break;	    case 'e':	      if (XEXP (x, i) == 0)		continue;	      if (i == 0)		{		  /* Hack tail-recursion here.  */		  px = &XEXP (x, 0);		  goto again;		}	      save_constants (&XEXP (x, i));	      break;	    }	}    }}/* Note whether a parameter is modified or not.  */static voidnote_modified_parmregs (reg, x)     rtx reg;     rtx x;{  if (GET_CODE (reg) == REG && in_nonparm_insns      && REGNO (reg) < max_parm_reg      && REGNO (reg) >= FIRST_PSEUDO_REGISTER      && parmdecl_map[REGNO (reg)] != 0)    TREE_READONLY (parmdecl_map[REGNO (reg)]) = 0;}/* Copy the rtx ORIG recursively, replacing pseudo-regs and labels   according to `reg_map' and `label_map'.  The original rtl insns   will be saved for inlining; this is used to make a copy   which is used to finish compiling the inline function itself.   If we find a "saved" constant pool entry, one which was replaced with   the value of the constant, convert it back to a constant pool entry.   Since the pool wasn't touched, this should simply restore the old   address.   All other kinds of rtx are copied except those that can never be   changed during compilation.  */static rtxcopy_for_inline (orig)     rtx orig;{  register rtx x = orig;  register rtx new;  register int i;  register enum rtx_code code;  register char *format_ptr;  if (x == 0)    return x;  code = GET_CODE (x);  /* These types may be freely shared.  */  switch (code)    {    case QUEUED:    case CONST_INT:    case SYMBOL_REF:    case PC:    case CC0:      return x;    case CONST_DOUBLE:      /* We have to make a new CONST_DOUBLE to ensure that we account for	 it correctly.  Using the old CONST_DOUBLE_MEM data is wrong.  */      if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)	{	  REAL_VALUE_TYPE d;	  REAL_VALUE_FROM_CONST_DOUBLE (d, x);	  return CONST_DOUBLE_FROM_REAL_VALUE (d, GET_MODE (x));	}      else	return immed_double_const (CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x),				   VOIDmode);    case CONST:      /* Get constant pool entry for constant in the pool.  */      if (RTX_INTEGRATED_P (x))	return validize_mem (force_const_mem (GET_MODE (x),					      copy_for_inline (XEXP (x, 0))));      break;    case SUBREG:      /* Get constant pool entry, but access in different mode.  */      if (RTX_INTEGRATED_P (x))	{	  new = force_const_mem (GET_MODE (SUBREG_REG (x)),				 copy_for_inline (XEXP (SUBREG_REG (x), 0)));	  PUT_MODE (new, GET_MODE (x));	  return validize_mem (new);	}      break;    case ADDRESS:      /* If not special for constant pool error.  Else get constant pool	 address.  */      if (! RTX_INTEGRATED_P (x))	abort ();      new = force_const_mem (GET_MODE (XEXP (x, 0)),			     copy_for_inline (XEXP (XEXP (x, 0), 0)));      new = XEXP (new, 0);#ifdef POINTERS_EXTEND_UNSIGNED      if (GET_MODE (new) != GET_MODE (x))	new = convert_memory_address (GET_MODE (x), new);#endif      return new;    case ASM_OPERANDS:      /* If a single asm insn contains multiple output operands	 then it contains multiple ASM_OPERANDS rtx's that share operand 3.	 We must make sure that the copied insn continues to share it.  */      if (orig_asm_operands_vector == XVEC (orig, 3))	{	  x = rtx_alloc (ASM_OPERANDS);	  x->volatil = orig->volatil;	  XSTR (x, 0) = XSTR (orig, 0);	  XSTR (x, 1) = XSTR (orig, 1);	  XINT (x, 2) = XINT (orig, 2);	  XVEC (x, 3) = copy_asm_operands_vector;	  XVEC (x, 4) = copy_asm_constraints_vector;	  XSTR (x, 5) = XSTR (orig, 5);	  XINT (x, 6) = XINT (orig, 6);	  return x;	}      break;    case MEM:      /* A MEM is usually allowed to be shared if its address is constant	 or is a constant plus one of the special registers.	 We do not allow sharing of addresses that are either a special	 register or the sum of a constant and a special register because	 it is possible for unshare_all_rtl to copy the address, into memory	 that won't be saved.  Although the MEM can safely be shared, and	 won't be copied there, the address itself cannot be shared, and may	 need to be copied. 	 There are also two exceptions with constants: The first is if the	 constant is a LABEL_REF or the sum of the LABEL_REF	 and an integer.  This case can happen if we have an inline	 function that supplies a constant operand to the call of another	 inline function that uses it in a switch statement.  In this case,	 we will be replacing the LABEL_REF, so we have to replace this MEM	 as well.	 The second case is if we have a (const (plus (address ..) ...)).	 In that case we need to put back the address of the constant pool	 entry.  */      if (CONSTANT_ADDRESS_P (XEXP (x, 0))	  && GET_CODE (XEXP (x, 0)) != LABEL_REF	  && ! (GET_CODE (XEXP (x, 0)) == CONST		&& (GET_CODE (XEXP (XEXP (x, 0), 0)) == PLUS		    && ((GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0))			== LABEL_REF)			|| (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0))			    == ADDRESS)))))	return x;      break;    case LABEL_REF:      /* If this is a non-local label, just make a new LABEL_REF.	 Otherwise, use the new label as well.  */      x = gen_rtx (LABEL_REF, GET_MODE (orig),		   LABEL_REF_NONLOCAL_P (orig) ? XEXP (orig, 0)		   : label_map[CODE_LABEL_NUMBER (XEXP (orig, 0))]);      LABEL_REF_NONLOCAL_P (x) = LABEL_REF_NONLOCAL_P (orig);      LABEL_OUTSIDE_LOOP_P (x) = LABEL_OUTSIDE_LOOP_P (orig);      return x;    case REG:      if (REGNO (x) > LAST_VIRTUAL_REGISTER)	return reg_map [REGNO (x)];      else	return x;    case SET:      /* If a parm that gets modified lives in a pseudo-reg,	 clear its TREE_READONLY to prevent certain optimizations.  */      {	rtx dest = SET_DEST (x);	while (GET_CODE (dest) == STRICT_LOW_PART	       || GET_CODE (dest) == ZERO_EXTRACT	       || GET_CODE (dest) == SUBREG)	  dest = XEXP (dest, 0);	if (GET_CODE (dest) == REG	    && REGNO (dest) < max_parm_reg	    && REGNO (dest) >= FIRST_PSEUDO_REGISTER	    && parmdecl_map[REGNO (dest)] != 0	    /* The insn to load an arg pseudo from a stack slot	       does not count as modifying it.  */	    && in_nonparm_insns)	  TREE_READONLY (parmdecl_map[REGNO (dest)]) = 0;      }      break;#if 0 /* This is a good idea, but here is the wrong place for it.  */      /* Arrange that CONST_INTs always appear as the second operand	 if they appear, and that `frame_pointer_rtx' or `arg_pointer_rtx'	 always appear as the first.  */    case PLUS:      if (GET_CODE (XEXP (x, 0)) == CONST_INT	  || (XEXP (x, 1) == frame_pointer_rtx	      || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM		  && XEXP (x, 1) == arg_pointer_rtx)))	{	  rtx t = XEXP (x, 0);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?