void
crx_expand_epilogue (void)
{
  rtx return_reg;

  /* Nonzero if we need to return and pop only RA. This will generate a
   * different insn. This differentiate is for the peepholes for call as last
   * statement in function. */
  int only_popret_RA = (save_regs[RETURN_ADDRESS_REGNUM]
			&& (sum_regs == UNITS_PER_WORD));

  /* Return register.  */
  return_reg = gen_rtx_REG (Pmode, RETURN_ADDRESS_REGNUM);

  if (frame_pointer_needed)
    /* Restore the stack pointer with the frame pointers value */
    emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);

  if (size_for_adjusting_sp > 0)
    emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
			   GEN_INT (size_for_adjusting_sp)));

  if (crx_interrupt_function_p ())
    emit_jump_insn (gen_interrupt_return ());
  else if (last_reg_to_save == -1)
    /* Nothing to pop */
    /* Don't output jump for interrupt routine, only retx.  */
    emit_jump_insn (gen_indirect_jump_return ());
  else if (only_popret_RA)
    emit_jump_insn (gen_popret_RA_return ());
  else
    emit_jump_insn (gen_pop_and_popret_return (GEN_INT (sum_regs)));
}
static void
mpushpop_str (char *stringbuffer, const char *mnemonic, char *mask)
{
  if (strlen (mask) > 2 || crx_interrupt_function_p ()) /* needs 2-word instr. */
    sprintf (stringbuffer, "\n\t%s\tsp, {%s}", mnemonic, mask);
  else /* single word instruction */
    sprintf (stringbuffer, "\n\t%s\t%s", mnemonic, mask);
}
Exemple #3
0
static void
crx_compute_save_regs (void)
{
  unsigned int regno;

  /* initialize here so in case the function is no-return it will be -1. */
  last_reg_to_save = -1;

  /* No need to save any registers if the function never returns.  */
  if (FUNC_IS_NORETURN_P (current_function_decl))
    return;

  /* Initialize the number of bytes to be saved. */
  sum_regs = 0;

  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
    {
      if (fixed_regs[regno])
	{
	  save_regs[regno] = 0;
	  continue;
	}

      /* If this reg is used and not call-used (except RA), save it. */
      if (crx_interrupt_function_p ())
	{
	  if (!current_function_is_leaf && call_used_regs[regno])
	    /* this is a volatile reg in a non-leaf interrupt routine - save it
	     * for the sake of its sons.  */
	    save_regs[regno] = 1;

	  else if (df_regs_ever_live_p (regno))
	    /* This reg is used - save it.  */
	    save_regs[regno] = 1;
	  else
	    /* This reg is not used, and is not a volatile - don't save. */
      	    save_regs[regno] = 0;
	}
      else
	{
	  /* If this reg is used and not call-used (except RA), save it. */
	  if (df_regs_ever_live_p (regno)
	      && (!call_used_regs[regno] || regno == RETURN_ADDRESS_REGNUM))
	    save_regs[regno] = 1;
	  else
	    save_regs[regno] = 0;
	}
    }

  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
    if (save_regs[regno] == 1)
      {
	last_reg_to_save = regno;
	sum_regs += UNITS_PER_WORD;
      }
}
char *
crx_prepare_push_pop_string (int push_or_pop)
{
  /* j is the number of registers being saved, takes care that there won't be
   * more than 8 in one push/pop instruction */

  /* For the register mask string */
  static char mask_str[50];

  /* i is the index of save_regs[], going from 0 until last_reg_to_save */
  int i = 0;

  int ra_in_bitmask = 0;

  char *return_str;

  /* For reversing on the push instructions if there are more than one. */
  char *temp_str;

  return_str = (char *) xmalloc (120);
  temp_str = (char *) xmalloc (120);

  /* Initialize */
  memset (return_str, 0, 3);

  while (i <= last_reg_to_save)
    {
      /* Prepare mask for one instruction. */
      mask_str[0] = 0;

      if (i <= SP_REGNUM)
	{ /* Add regs unit full or SP register reached */
	  int j = 0;
	  while (j < MAX_COUNT && i <= SP_REGNUM)
	    {
	      if (save_regs[i])
		{
		  /* TODO to use ra_in_bitmask for detecting last pop is not
		   * smart it prevents things like:  popret r5 */
		  if (i == RETURN_ADDRESS_REGNUM) ra_in_bitmask = 1;
		  if (j > 0) strcat (mask_str, ", ");
		  strcat (mask_str, reg_names[i]);
		  ++j;
		}
	      ++i;
	    }
	}
      else
	{
	  /* Handle hi/lo savings */
	  while (i <= last_reg_to_save)
	    {
	      if (save_regs[i])
		{
		  strcat (mask_str, "lo, hi");
		  i = last_reg_to_save + 1;
		  break;
		}
	      ++i;
	    }
	}

      if (strlen (mask_str) == 0) continue;
       	
      if (push_or_pop == 1)
	{
	  if (crx_interrupt_function_p ())
	    mpushpop_str (temp_str, "popx", mask_str);
	  else
	    {
	      if (ra_in_bitmask)
		{
		  mpushpop_str (temp_str, "popret", mask_str);
		  ra_in_bitmask = 0;
		}
	      else mpushpop_str (temp_str, "pop", mask_str);
	    }

	  strcat (return_str, temp_str);
	}
      else
	{
	  /* push - We need to reverse the order of the instructions if there
	   * are more than one. (since the pop will not be reversed in the
	   * epilogue */
      	  if (crx_interrupt_function_p ())
	    mpushpop_str (temp_str, "pushx", mask_str);
	  else
	    mpushpop_str (temp_str, "push", mask_str);
	  strcat (temp_str, return_str);
	  strcpy (strcat (return_str, "\t"), temp_str);
	}

    }

  if (push_or_pop == 1)
    {
      /* pop */
      if (crx_interrupt_function_p ())
	strcat (return_str, "\n\tretx\n");

      else if (!FUNC_IS_NORETURN_P (current_function_decl)
	       && !save_regs[RETURN_ADDRESS_REGNUM])
	strcat (return_str, "\n\tjump\tra\n");
    }

  /* Skip the newline and the tab in the start of return_str. */
  return_str += 2;
  return return_str;
}