struct mips_arg_info *));
static bool mips_get_unaligned_mem PARAMS ((rtx *, unsigned int,
int, rtx *, rtx *));
+static unsigned int mips_global_pointer PARAMS ((void));
+static bool mips_save_reg_p PARAMS ((unsigned int));
static rtx mips_add_large_offset_to_sp PARAMS ((HOST_WIDE_INT));
static void mips_set_frame_expr PARAMS ((rtx));
static rtx mips_frame_set PARAMS ((rtx, int));
static void mips_emit_frame_related_store PARAMS ((rtx, rtx,
HOST_WIDE_INT));
static void save_restore_insns PARAMS ((int, rtx, long));
+static void mips_gp_insn PARAMS ((rtx, rtx));
static void mips16_fp_args PARAMS ((FILE *, int, int));
static void build_mips16_function_stub PARAMS ((FILE *));
static void mips16_optimize_gp PARAMS ((rtx));
long total_size; /* # bytes that the entire frame takes up */
long var_size; /* # bytes that variables take up */
long args_size; /* # bytes that outgoing arguments take up */
- long extra_size; /* # bytes of extra gunk */
int gp_reg_size; /* # bytes needed to store gp regs */
int fp_reg_size; /* # bytes needed to store fp regs */
long mask; /* mask of saved gp registers */
/* Length of instructions in function; mips16 only. */
long insns_len;
+
+ /* The register to use as the global pointer within this function. */
+ unsigned int global_pointer;
};
/* Information about a single argument. */
{
x = XEXP (x, 0);
- if (GET_CODE (x) == REG && REGNO (x) == GP_REG_FIRST + 28)
+ if (x == pic_offset_table_rtx)
return CONSTANT_GP;
while (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
case RELOC_CALL16:
case RELOC_CALL_HI:
case RELOC_CALL_LO:
+ case RELOC_LOADGP_HI:
+ case RELOC_LOADGP_LO:
/* These relocations should be applied to bare symbols only. */
return (info->offset == 0 ? CONSTANT_RELOC : CONSTANT_NONE);
}
loc = hard_frame_pointer_rtx;
else
loc = stack_pointer_rtx;
- loc = plus_constant (loc, cfun->machine->frame.args_size);
+ loc = plus_constant (loc, current_function_outgoing_args_size);
operands[1] = gen_rtx_MEM (ptr_mode, loc);
return mips_output_move (operands[0], operands[1]);
'.' Print the name of the register with a hard-wired zero (zero or $0).
'^' Print the name of the pic call-through register (t9 or $25).
'$' Print the name of the stack pointer register (sp or $29).
- '+' Print the name of the gp register (gp or $28).
+ '+' Print the name of the gp register (usually gp or $28).
'~' Output a branch alignment to LABEL_ALIGN(NULL). */
void
{
register enum rtx_code code;
struct mips_constant_info c;
+ const char *reloc;
if (PRINT_OPERAND_PUNCT_VALID_P (letter))
{
break;
case '+':
- fputs (reg_names[GP_REG_FIRST + 28], file);
+ fputs (reg_names[PIC_OFFSET_TABLE_REGNUM], file);
break;
case '&':
break;
case CONSTANT_GP:
- fputs (reg_names[GP_REG_FIRST + 28], file);
+ fputs (reg_names[PIC_OFFSET_TABLE_REGNUM], file);
break;
case CONSTANT_RELOC:
- fputs (mips_reloc_string (XINT (c.symbol, 1)), file);
+ reloc = mips_reloc_string (XINT (c.symbol, 1));
+ fputs (reloc, file);
output_addr_const (file, plus_constant (XVECEXP (c.symbol, 0, 0),
c.offset));
- fputc (')', file);
- break;
+ while (*reloc != 0)
+ if (*reloc++ == '(')
+ fputc (')', file);
}
}
\f
case RELOC_CALL16: return "%call16(";
case RELOC_CALL_HI: return "%call_hi(";
case RELOC_CALL_LO: return "%call_lo(";
+ case RELOC_LOADGP_HI: return "%hi(%neg(%gp_rel(";
+ case RELOC_LOADGP_LO: return "%lo(%neg(%gp_rel(";
}
abort ();
}
}
}
\f
+/* Return the register that should be used as the global pointer
+ within this function. Return 0 if the function doesn't need
+ a global pointer. */
+
+static unsigned int
+mips_global_pointer ()
+{
+ unsigned int regno;
+
+ /* $gp is always available in non-abicalls code. */
+ if (!TARGET_ABICALLS)
+ return GLOBAL_POINTER_REGNUM;
+
+ /* We must always provide $gp when it is used implicitly. */
+ if (!TARGET_EXPLICIT_RELOCS)
+ return GLOBAL_POINTER_REGNUM;
+
+ /* FUNCTION_PROFILER includes a jal macro, so we need to give it
+ a valid gp. */
+ if (current_function_profile)
+ return GLOBAL_POINTER_REGNUM;
+
+ /* If the gp is never referenced, there's no need to initialize it.
+ Note that reload can sometimes introduce constant pool references
+ into a function that otherwise didn't need them. For example,
+ suppose we have an instruction like:
+
+ (set (reg:DF R1) (float:DF (reg:SI R2)))
+
+ If R2 turns out to be constant such as 1, the instruction may have a
+ REG_EQUAL note saying that R1 == 1.0. Reload then has the option of
+ using this constant if R2 doesn't get allocated to a register.
+
+ In cases like these, reload will have added the constant to the pool
+ but no instruction will yet refer to it. */
+ if (!regs_ever_live[GLOBAL_POINTER_REGNUM]
+ && !current_function_uses_const_pool)
+ return 0;
+
+ /* We need a global pointer, but perhaps we can use a call-clobbered
+ register instead of $gp. */
+ if (TARGET_NEWABI && current_function_is_leaf)
+ for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+ if (!regs_ever_live[regno]
+ && call_used_regs[regno]
+ && !fixed_regs[regno])
+ return regno;
+
+ return GLOBAL_POINTER_REGNUM;
+}
+
+
+/* Return true if the current function must save REGNO. */
+
+static bool
+mips_save_reg_p (regno)
+ unsigned int regno;
+{
+ /* We only need to save $gp for NewABI PIC. */
+ if (regno == GLOBAL_POINTER_REGNUM)
+ return (TARGET_ABICALLS && TARGET_NEWABI
+ && cfun->machine->global_pointer == regno);
+
+ /* Check call-saved registers. */
+ if (regs_ever_live[regno] && !call_used_regs[regno])
+ return true;
+
+ /* We need to save the old frame pointer before setting up a new one. */
+ if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed)
+ return true;
+
+ /* We need to save the incoming return address if it is ever clobbered
+ within the function. */
+ if (regno == GP_REG_FIRST + 31 && regs_ever_live[regno])
+ return true;
+
+ if (TARGET_MIPS16)
+ {
+ tree return_type;
+
+ return_type = DECL_RESULT (current_function_decl);
+
+ /* $18 is a special case in mips16 code. It may be used to call
+ a function which returns a floating point value, but it is
+ marked in call_used_regs. */
+ if (regno == GP_REG_FIRST + 18 && regs_ever_live[regno])
+ return true;
+
+ /* $31 is also a special case. When not using -mentry, it will be
+ used to copy a return value into the floating point registers if
+ the return value is floating point. */
+ if (regno == GP_REG_FIRST + 31
+ && mips16_hard_float
+ && !mips_entry
+ && !aggregate_value_p (return_type)
+ && GET_MODE_CLASS (DECL_MODE (return_type)) == MODE_FLOAT
+ && GET_MODE_SIZE (DECL_MODE (return_type)) <= UNITS_PER_FPVALUE)
+ return true;
+
+ /* The entry and exit pseudo instructions can not save $17
+ without also saving $16. */
+ if (mips_entry
+ && regno == GP_REG_FIRST + 16
+ && mips_save_reg_p (GP_REG_FIRST + 17))
+ return true;
+ }
+
+ return false;
+}
+
+
/* Return the bytes needed to compute the frame pointer from the current
stack pointer.
HOST_WIDE_INT total_size; /* # bytes that the entire frame takes up */
HOST_WIDE_INT var_size; /* # bytes that variables take up */
HOST_WIDE_INT args_size; /* # bytes that outgoing arguments take up */
- HOST_WIDE_INT extra_size; /* # extra bytes */
HOST_WIDE_INT gp_reg_rounded; /* # bytes needed to store gp after rounding */
HOST_WIDE_INT gp_reg_size; /* # bytes needed to store gp regs */
HOST_WIDE_INT fp_reg_size; /* # bytes needed to store fp regs */
long mask; /* mask of saved gp registers */
long fmask; /* mask of saved fp registers */
- tree return_type;
+
+ cfun->machine->global_pointer = mips_global_pointer ();
gp_reg_size = 0;
fp_reg_size = 0;
mask = 0;
fmask = 0;
- extra_size = MIPS_STACK_ALIGN (((TARGET_ABICALLS) ? UNITS_PER_WORD : 0));
var_size = MIPS_STACK_ALIGN (size);
- args_size = MIPS_STACK_ALIGN (current_function_outgoing_args_size);
+ args_size = MIPS_STACK_ALIGN (STARTING_FRAME_OFFSET);
+
+ /* The space set aside by STARTING_FRAME_OFFSET isn't needed in leaf
+ functions. If the function has local variables, we're committed
+ to allocating it anyway. Otherwise reclaim it here. */
+ if (var_size == 0 && current_function_is_leaf)
+ args_size = 0;
/* The MIPS 3.0 linker does not like functions that dynamically
allocate the stack and have 0 for STACK_DYNAMIC_OFFSET, since it
if (args_size == 0 && current_function_calls_alloca)
args_size = 4 * UNITS_PER_WORD;
- total_size = var_size + args_size + extra_size;
- return_type = DECL_RESULT (current_function_decl);
+ total_size = var_size + args_size;
/* Calculate space needed for gp registers. */
for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
- {
- /* $18 is a special case on the mips16. It may be used to call
- a function which returns a floating point value, but it is
- marked in call_used_regs. $31 is also a special case. When
- not using -mentry, it will be used to copy a return value
- into the floating point registers if the return value is
- floating point. */
- if (MUST_SAVE_REGISTER (regno)
- || (TARGET_MIPS16
- && regno == GP_REG_FIRST + 18
- && regs_ever_live[regno])
- || (TARGET_MIPS16
- && regno == GP_REG_FIRST + 31
- && mips16_hard_float
- && ! mips_entry
- && ! aggregate_value_p (return_type)
- && GET_MODE_CLASS (DECL_MODE (return_type)) == MODE_FLOAT
- && GET_MODE_SIZE (DECL_MODE (return_type)) <= UNITS_PER_FPVALUE))
- {
- gp_reg_size += GET_MODE_SIZE (gpr_mode);
- mask |= 1L << (regno - GP_REG_FIRST);
-
- /* The entry and exit pseudo instructions can not save $17
- without also saving $16. */
- if (mips_entry
- && regno == GP_REG_FIRST + 17
- && ! MUST_SAVE_REGISTER (GP_REG_FIRST + 16))
- {
- gp_reg_size += UNITS_PER_WORD;
- mask |= 1L << 16;
- }
- }
- }
+ if (mips_save_reg_p (regno))
+ {
+ gp_reg_size += GET_MODE_SIZE (gpr_mode);
+ mask |= 1L << (regno - GP_REG_FIRST);
+ }
/* We need to restore these for the handler. */
if (current_function_calls_eh_return)
regno >= FP_REG_FIRST;
regno -= FP_INC)
{
- if (regs_ever_live[regno] && !call_used_regs[regno])
+ if (mips_save_reg_p (regno))
{
fp_reg_size += FP_INC * UNITS_PER_FPREG;
fmask |= ((1 << FP_INC) - 1) << (regno - FP_REG_FIRST);
gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size);
total_size += gp_reg_rounded + MIPS_STACK_ALIGN (fp_reg_size);
- /* The gp reg is caller saved in the 32 bit ABI, so there is no need
- for leaf routines (total_size == extra_size) to save the gp reg.
- The gp reg is callee saved in the 64 bit ABI, so all routines must
- save the gp reg. This is not a leaf routine if -p, because of the
- call to mcount. */
- if (total_size == extra_size
- && (mips_abi == ABI_32 || mips_abi == ABI_O64 || mips_abi == ABI_EABI)
- && ! current_function_profile)
- total_size = extra_size = 0;
- else if (TARGET_ABICALLS)
- {
- /* Add the context-pointer to the saved registers. */
- gp_reg_size += UNITS_PER_WORD;
- mask |= 1L << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST);
- total_size -= gp_reg_rounded;
- gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size);
- total_size += gp_reg_rounded;
- }
-
/* Add in space reserved on the stack by the callee for storing arguments
passed in registers. */
if (mips_abi != ABI_32 && mips_abi != ABI_O64)
cfun->machine->frame.total_size = total_size;
cfun->machine->frame.var_size = var_size;
cfun->machine->frame.args_size = args_size;
- cfun->machine->frame.extra_size = extra_size;
cfun->machine->frame.gp_reg_size = gp_reg_size;
cfun->machine->frame.fp_reg_size = fp_reg_size;
cfun->machine->frame.mask = mask;
/* When using mips_entry, the registers are always saved at the
top of the stack. */
if (! mips_entry)
- offset = (args_size + extra_size + var_size
- + gp_reg_size - GET_MODE_SIZE (gpr_mode));
+ offset = args_size + var_size + gp_reg_size - GET_MODE_SIZE (gpr_mode);
else
offset = total_size - GET_MODE_SIZE (gpr_mode);
if (fmask)
{
- unsigned long offset = (args_size + extra_size + var_size
+ unsigned long offset = (args_size + var_size
+ gp_reg_rounded + fp_reg_size
- FP_INC * UNITS_PER_FPREG);
cfun->machine->frame.fp_sp_offset = offset;
}
\f
/* Implement INITIAL_ELIMINATION_OFFSET. FROM is either the frame
- pointer, argument pointer, or return address pointer. TO is either
- the stack pointer or hard frame pointer. */
+ pointer or argument pointer. TO is either the stack pointer or
+ hard frame pointer. */
int
mips_initial_elimination_offset (from, to)
{
long mask = cfun->machine->frame.mask;
long fmask = cfun->machine->frame.fmask;
- long real_mask = mask;
int regno;
rtx base_reg_rtx;
HOST_WIDE_INT base_offset;
&& ! BITSET_P (mask, HARD_FRAME_POINTER_REGNUM - GP_REG_FIRST))
abort ();
- /* Do not restore GP under certain conditions. */
- if (! store_p
- && TARGET_ABICALLS
- && (mips_abi == ABI_32 || mips_abi == ABI_O64))
- mask &= ~(1L << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST));
-
if (mask == 0 && fmask == 0)
return;
emit_move_insn (gen_rtx (REG, gpr_mode, regno),
reg_rtx);
}
+ gp_offset -= GET_MODE_SIZE (gpr_mode);
}
- /* If the restore is being supressed, still take into account
- the offset at which it is stored. */
- if (BITSET_P (real_mask, regno - GP_REG_FIRST))
- gp_offset -= GET_MODE_SIZE (gpr_mode);
}
}
else
{
/* .frame FRAMEREG, FRAMESIZE, RETREG */
fprintf (file,
- "\t.frame\t%s,%ld,%s\t\t# vars= %ld, regs= %d/%d, args= %d, extra= %ld\n",
+ "\t.frame\t%s,%ld,%s\t\t# vars= %ld, regs= %d/%d, args= %d, gp= %ld\n",
(reg_names[(frame_pointer_needed)
? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM]),
((frame_pointer_needed && TARGET_MIPS16)
cfun->machine->frame.num_gp,
cfun->machine->frame.num_fp,
current_function_outgoing_args_size,
- cfun->machine->frame.extra_size);
+ cfun->machine->frame.args_size
+ - current_function_outgoing_args_size);
/* .mask MASK, GPOFFSET; .fmask FPOFFSET */
fprintf (file, "\t.mask\t0x%08lx,%ld\n\t.fmask\t0x%08lx,%ld\n",
fprintf (file, "\n");
}
- if (TARGET_ABICALLS && (mips_abi == ABI_32 || mips_abi == ABI_O64))
- {
- const char *const sp_str = reg_names[STACK_POINTER_REGNUM];
+ /* Handle the initialization of $gp for SVR4 PIC. */
+ if (TARGET_ABICALLS && !TARGET_NEWABI && cfun->machine->global_pointer > 0)
+ fprintf (file, "\t.set\tnoreorder\n\t.cpload\t%s\n\t.set\treorder\n",
+ reg_names[PIC_FUNCTION_ADDR_REGNUM]);
+}
+\f
+/* Emit an instruction to move SRC into DEST. When generating
+ explicit reloc code, mark the instruction as potentially dead. */
- fprintf (file, "\t.set\tnoreorder\n\t.cpload\t%s\n\t.set\treorder\n",
- reg_names[PIC_FUNCTION_ADDR_REGNUM]);
- if (tsize > 0)
- {
- fprintf (file, "\t%s\t%s,%s,%ld\n",
- (ptr_mode == DImode ? "dsubu" : "subu"),
- sp_str, sp_str, (long) tsize);
- fprintf (file, "\t.cprestore %ld\n", cfun->machine->frame.args_size);
- }
+static void
+mips_gp_insn (dest, src)
+ rtx dest, src;
+{
+ rtx insn;
- if (dwarf2out_do_frame ())
- dwarf2out_def_cfa ("", STACK_POINTER_REGNUM, tsize);
+ insn = emit_insn (gen_rtx_SET (VOIDmode, dest, src));
+ if (TARGET_EXPLICIT_RELOCS)
+ {
+ /* compute_frame_size assumes that any function which uses the
+ constant pool will need a gp. However, all constant
+ pool references could be eliminated, in which case
+ it is OK for flow to delete the gp load as well. */
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
+ REG_NOTES (insn));
}
}
-\f
+
/* Expand the prologue into a bunch of separate insns. */
void
int store_args_on_stack = (mips_abi == ABI_32 || mips_abi == ABI_O64)
&& (! mips_entry || mips_can_use_return_insn ());
+ if (cfun->machine->global_pointer > 0)
+ REGNO (pic_offset_table_rtx) = cfun->machine->global_pointer;
+
/* If struct value address is treated as the first argument, make it so. */
if (aggregate_value_p (DECL_RESULT (fndecl))
&& ! current_function_returns_pcc_struct
int offset = (regno - GP_ARG_FIRST) * UNITS_PER_WORD;
rtx ptr = stack_pointer_rtx;
- /* If we are doing svr4-abi, sp has already been decremented by tsize. */
- if (TARGET_ABICALLS)
- offset += tsize;
-
for (; regno <= GP_ARG_LAST; regno++)
{
if (offset != 0)
/* If we are doing svr4-abi, sp move is done by
function_prologue. In mips16 mode with a large frame, we
save the registers before adjusting the stack. */
- if ((!TARGET_ABICALLS || (mips_abi != ABI_32 && mips_abi != ABI_O64))
- && (!TARGET_MIPS16 || tsize <= 32767))
+ if (!TARGET_MIPS16 || tsize <= 32767)
{
rtx adjustment_rtx;
else if (reg_18_save != NULL_RTX)
emit_insn (reg_18_save);
- if ((!TARGET_ABICALLS || (mips_abi != ABI_32 && mips_abi != ABI_O64))
- && TARGET_MIPS16
- && tsize > 32767)
+ if (TARGET_ABICALLS && !TARGET_NEWABI && !current_function_is_leaf)
+ emit_insn (gen_cprestore
+ (GEN_INT (current_function_outgoing_args_size)));
+
+ if (TARGET_MIPS16 && tsize > 32767)
{
rtx reg_rtx;
instructions when using the frame pointer by pointing the
frame pointer ahead of the argument space allocated on
the stack. */
- if ((! TARGET_ABICALLS || (mips_abi != ABI_32 && mips_abi != ABI_O64))
- && TARGET_MIPS16
- && tsize > 32767)
+ if (TARGET_MIPS16 && tsize > 32767)
{
/* In this case, we have already copied the stack
pointer into the frame pointer, above. We need only
if (insn)
RTX_FRAME_RELATED_P (insn) = 1;
}
+ }
+
+ if (TARGET_ABICALLS && TARGET_NEWABI && cfun->machine->global_pointer > 0)
+ {
+ rtx temp, fnsymbol, fnaddr;
- if (TARGET_ABICALLS && (mips_abi != ABI_32 && mips_abi != ABI_O64))
- emit_insn (gen_loadgp (XEXP (DECL_RTL (current_function_decl), 0),
- gen_rtx_REG (DImode, 25)));
+ temp = gen_rtx_REG (Pmode, MIPS_TEMP1_REGNUM);
+ fnsymbol = XEXP (DECL_RTL (current_function_decl), 0);
+ fnaddr = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+
+ mips_gp_insn (temp, mips_lui_reloc (fnsymbol, RELOC_LOADGP_HI));
+ mips_gp_insn (temp, gen_rtx_PLUS (Pmode, temp, fnaddr));
+ mips_gp_insn (pic_offset_table_rtx,
+ gen_rtx_PLUS (Pmode, temp,
+ mips_reloc (fnsymbol, RELOC_LOADGP_LO)));
+
+ if (!TARGET_EXPLICIT_RELOCS)
+ emit_insn (gen_loadgp_blockage ());
}
/* If we are profiling, make sure no instructions are scheduled before
for (string = mips16_strings; string != 0; string = XEXP (string, 1))
SYMBOL_REF_FLAG (XEXP (string, 0)) = 0;
free_EXPR_LIST_list (&mips16_strings);
+
+ /* Reinstate the normal $gp. */
+ REGNO (pic_offset_table_rtx) = GLOBAL_POINTER_REGNUM;
}
\f
/* Expand the epilogue into a bunch of separate insns. SIBCALL_P is true
/* We want to initialize this to a value which gcc will believe
is constant. */
- const_gp = gen_rtx (CONST, Pmode,
- gen_rtx (REG, Pmode, GP_REG_FIRST + 28));
-
+ const_gp = gen_rtx_CONST (Pmode, pic_offset_table_rtx);
start_sequence ();
emit_move_insn (cfun->machine->mips16_gp_pseudo_rtx,
const_gp);
if (gpcopy == NULL_RTX
&& GET_CODE (SET_SRC (set)) == CONST
- && GET_CODE (XEXP (SET_SRC (set), 0)) == REG
- && REGNO (XEXP (SET_SRC (set), 0)) == GP_REG_FIRST + 28
+ && XEXP (SET_SRC (set), 0) == pic_offset_table_rtx
&& GET_CODE (SET_DEST (set)) == REG)
gpcopy = SET_DEST (set);
else if (slot == NULL_RTX
&& (GET_CODE (SET_DEST (set)) != REG
|| REGNO (SET_DEST (set)) != REGNO (gpcopy)
|| ((GET_CODE (SET_SRC (set)) != CONST
- || GET_CODE (XEXP (SET_SRC (set), 0)) != REG
- || (REGNO (XEXP (SET_SRC (set), 0))
- != GP_REG_FIRST + 28))
+ || XEXP (SET_SRC (set), 0) != pic_offset_table_rtx)
&& ! rtx_equal_p (SET_SRC (set), slot))))
break;
else if (slot != NULL_RTX
if (GET_CODE (SET_DEST (set1)) == REG
&& GET_CODE (SET_SRC (set1)) == CONST
- && GET_CODE (XEXP (SET_SRC (set1), 0)) == REG
- && REGNO (XEXP (SET_SRC (set1), 0)) == GP_REG_FIRST + 28
+ && XEXP (SET_SRC (set1), 0) == pic_offset_table_rtx
&& rtx_equal_p (SET_DEST (set1), SET_DEST (set2))
&& GET_CODE (SET_SRC (set2)) == PLUS
&& rtx_equal_p (SET_DEST (set1), XEXP (SET_SRC (set2), 0))
&& rtx_equal_p (SET_SRC (set), slot))
{
enum machine_mode mode;
+ rtx src;
mode = GET_MODE (SET_DEST (set));
- emit_insn_after (gen_rtx (SET, VOIDmode, SET_DEST (set),
- gen_rtx (CONST, mode,
- gen_rtx (REG, mode,
- GP_REG_FIRST + 28))),
- insn);
+ src = gen_rtx_CONST (mode, pic_offset_table_rtx);
+ emit_insn_after (gen_rtx_SET (VOIDmode, SET_DEST (set), src), insn);
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
(UNSPEC_STORE_DF_HIGH 2)
(UNSPEC_GET_FNADDR 4)
(UNSPEC_BLOCKAGE 6)
- (UNSPEC_LOADGP 7)
- (UNSPEC_SETJMP 8)
- (UNSPEC_LONGJMP 9)
+ (UNSPEC_CPRESTORE 8)
(UNSPEC_EH_RECEIVER 10)
(UNSPEC_EH_RETURN 11)
(UNSPEC_CONSTTABLE_QI 12)
(RELOC_GOT_DISP 104)
(RELOC_CALL16 105)
(RELOC_CALL_HI 106)
- (RELOC_CALL_LO 107)])
+ (RELOC_CALL_LO 107)
+ (RELOC_LOADGP_HI 108)
+ (RELOC_LOADGP_LO 109)])
\f
;; ....................
(set_attr "mode" "SF")
(set_attr "length" "4")])
-;; Instructions to load the global pointer register.
-;; This is volatile to make sure that the scheduler won't move any symbol_ref
-;; uses in front of it. All symbol_refs implicitly use the gp reg.
+;; The use of gp is hidden when not using explicit relocations.
+;; This blockage instruction prevents the gp load from being
+;; scheduled after an implicit use of gp. It also prevents
+;; the load from being deleted as dead.
+(define_insn "loadgp_blockage"
+ [(unspec_volatile [(reg:DI 28)] UNSPEC_BLOCKAGE)]
+ ""
+ ""
+ [(set_attr "type" "unknown")
+ (set_attr "mode" "none")
+ (set_attr "length" "0")])
-(define_insn "loadgp"
- [(set (reg:DI 28)
- (unspec_volatile:DI [(match_operand 0 "immediate_operand" "")
- (match_operand:DI 1 "register_operand" "")]
- UNSPEC_LOADGP))
- (clobber (reg:DI 1))]
+;; Emit a .cprestore directive, which expands to a single store instruction.
+;; Note that we continue to use .cprestore for explicit reloc code so that
+;; jals inside inlines asms will work correctly.
+(define_insn "cprestore"
+ [(unspec_volatile [(match_operand 0 "const_int_operand" "")]
+ UNSPEC_CPRESTORE)]
""
- "%[lui\\t$1,%%hi(%%neg(%%gp_rel(%0)))\\n\\taddiu\\t$1,$1,%%lo(%%neg(%%gp_rel(%0)))\\n\\tdaddu\\t$gp,$1,%1%]"
- [(set_attr "type" "move")
- (set_attr "mode" "DI")
- (set_attr "length" "12")])
+ ".cprestore\t%0"
+ [(set_attr "type" "store")
+ (set_attr "length" "4")])
\f
;; Block moves, see mips.c for more details.
;; Argument 0 is the destination
(set_attr "mode" "none")
(set_attr "length" "24")])
-;; For o32/n32/n64, we save the gp in the jmp_buf as well. While it is
-;; possible to either pull it off the stack (in the o32 case) or recalculate
-;; it given t9 and our target label, it takes 3 or 4 insns to do so, and
-;; this is easy.
+;; For TARGET_ABICALLS, we save the gp in the jmp_buf as well.
+;; While it is possible to either pull it off the stack (in the
+;; o32 case) or recalculate it given t9 and our target label,
+;; it takes 3 or 4 insns to do so.
(define_expand "builtin_setjmp_setup"
- [(unspec [(match_operand 0 "register_operand" "r")] UNSPEC_SETJMP)]
+ [(use (match_operand 0 "register_operand" ""))]
"TARGET_ABICALLS"
- "
-{
- if (Pmode == DImode)
- emit_insn (gen_builtin_setjmp_setup_64 (operands[0]));
- else
- emit_insn (gen_builtin_setjmp_setup_32 (operands[0]));
- DONE;
-}")
-
-(define_expand "builtin_setjmp_setup_32"
- [(set (mem:SI (plus:SI (match_operand:SI 0 "register_operand" "r")
- (const_int 12)))
- (reg:SI 28))]
- "TARGET_ABICALLS && ! (Pmode == DImode)"
- "")
+ {
+ rtx addr;
-(define_expand "builtin_setjmp_setup_64"
- [(set (mem:DI (plus:DI (match_operand:DI 0 "register_operand" "r")
- (const_int 24)))
- (reg:DI 28))]
- "TARGET_ABICALLS && Pmode == DImode"
- "")
+ addr = plus_constant (operands[0], GET_MODE_SIZE (Pmode) * 3);
+ emit_move_insn (gen_rtx_MEM (Pmode, addr), pic_offset_table_rtx);
+ DONE;
+ })
-;; For o32/n32/n64, we need to arrange for longjmp to put the
-;; target address in t9 so that we can use it for loading $gp.
+;; Restore the gp that we saved above. Despite the comment, it seems that
+;; older code did recalculate the gp from $25. Continue to jump through
+;; $25 for compatibility (we lose nothing by doing so).
(define_expand "builtin_longjmp"
- [(unspec_volatile [(match_operand 0 "register_operand" "r")] UNSPEC_LONGJMP)]
+ [(use (match_operand 0 "register_operand" "r"))]
"TARGET_ABICALLS"
"
{
/* The elements of the buffer are, in order: */
- int W = (Pmode == DImode ? 8 : 4);
+ int W = GET_MODE_SIZE (Pmode);
rtx fp = gen_rtx_MEM (Pmode, operands[0]);
rtx lab = gen_rtx_MEM (Pmode, plus_constant (operands[0], 1*W));
rtx stack = gen_rtx_MEM (Pmode, plus_constant (operands[0], 2*W));
rtx gpv = gen_rtx_MEM (Pmode, plus_constant (operands[0], 3*W));
- rtx pv = gen_rtx_REG (Pmode, 25);
- rtx gp = gen_rtx_REG (Pmode, 28);
-
- /* This bit is the same as expand_builtin_longjmp. */
+ rtx pv = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+ /* Use gen_raw_REG to avoid being given pic_offset_table_rtx.
+ The target is bound to be using $28 as the global pointer
+ but the current function might not be. */
+ rtx gp = gen_raw_REG (Pmode, GLOBAL_POINTER_REGNUM);
+
+ /* This bit is similar to expand_builtin_longjmp except that it
+ restores $gp as well. */
emit_move_insn (hard_frame_pointer_rtx, fp);
emit_move_insn (pv, lab);
emit_stack_restore (SAVE_NONLOCAL, stack, NULL_RTX);