Commit dbc90b65 by Richard Sandiford Committed by Richard Sandiford

re PR target/34981 (Lazily-bound function called twice)

gcc/
	PR target/34981
	* config/mips/mips-protos.h (mips_expand_call): Return an rtx.
	* config/mips/mips.h (FIRST_PSEUDO_REGISTER): Rename FAKE_CALL_REGNO
	to GOT_VERSION_REGNUM.
	(CALL_REALLY_USED_REGISTERS): Set the GOT_VERSION_REGNUM entry to 0.
	(EPILOGUE_USES): Include GOT_VERSION_REGNUM if TARGET_USE_GOT.
	* config/mips/mips.c (mips_emit_call_insn): New function.
	(mips_call_tls_get_addr): Call mips_expand_call directly.
	(mips16_copy_fpr_return_value): Use mips_emit_call_insn rather than
	emit_call_insn.
	(mips16_build_call_stub): Likewise.  Return the call insn or null.
	(mips_expand_call): Update the call to mips16_build_call_stub
	accordingly and a remove redundant condition.  Assert that MIPS16
	stubs do not use lazy binding.  Use mips_emit_call_insn and return
	the call insn.
	(mips_extra_live_on_entry): Include GOT_VERSION_REGNUM if
	TARGET_USE_GOT.
	(mips_hard_regno_mode_ok_p): Allow SImode for GOT_VERSION_REGNUM.
	(mips_avoid_hazard): Remove hazard_set handling.
	* config/mips/mips.md (UNSPEC_EH_RECEIVER): Rename to...
	(UNSPEC_RESTORE_GP): ...this.
	(UNSPEC_SET_GOT_VERSION, UNSPEC_UPDATE_GOT_VERSION): New constants.
	(FAKE_CALL_REGNO): Rename to...
	(GOT_VERSION_REGNUM): ...this.
	(type): Add "ghost" value.  Add an associated insn reservation.
	(hazard_set): Remove.
	(exception_receiver): Rename to...
	(restore_gp): ...this and update the unspec identifier accordingly.
	(exception_receiver, nonlocal_got_receiver): New expanders.
	(load_call<mode>): Use GOT_VERSION_REGNUM.  Don't set
	FAKE_CALL_REGNO.  Remove hazard_set attribute.
	(set_got_version, update_got_version): New patterns.

gcc/testsuite/
	PR target/34981
	* gcc.target/mips/lazy-binding-1.c: New test.
	* gcc.target/mips/mips.exp (setup_mips_tests): Set
	mips_forced_no_abicalls and mips_forced_no_shared.
	(dg-mips-options): Avoid using -mabicalls with an implicit -mabi=eabi.
	Avoid using small data with -mabicalls.  Don't make -G0 force
	-mn-abicalls.  Skip -mabicalls and -mshared tests if the multilib
	forces the opposite option.

From-SVN: r131860
parent 763a27ee
2008-01-26 Richard Sandiford <rsandifo@nildram.co.uk>
PR target/34981
* config/mips/mips-protos.h (mips_expand_call): Return an rtx.
* config/mips/mips.h (FIRST_PSEUDO_REGISTER): Rename FAKE_CALL_REGNO
to GOT_VERSION_REGNUM.
(CALL_REALLY_USED_REGISTERS): Set the GOT_VERSION_REGNUM entry to 0.
(EPILOGUE_USES): Include GOT_VERSION_REGNUM if TARGET_USE_GOT.
* config/mips/mips.c (mips_emit_call_insn): New function.
(mips_call_tls_get_addr): Call mips_expand_call directly.
(mips16_copy_fpr_return_value): Use mips_emit_call_insn rather than
emit_call_insn.
(mips16_build_call_stub): Likewise. Return the call insn or null.
(mips_expand_call): Update the call to mips16_build_call_stub
accordingly and a remove redundant condition. Assert that MIPS16
stubs do not use lazy binding. Use mips_emit_call_insn and return
the call insn.
(mips_extra_live_on_entry): Include GOT_VERSION_REGNUM if
TARGET_USE_GOT.
(mips_hard_regno_mode_ok_p): Allow SImode for GOT_VERSION_REGNUM.
(mips_avoid_hazard): Remove hazard_set handling.
* config/mips/mips.md (UNSPEC_EH_RECEIVER): Rename to...
(UNSPEC_RESTORE_GP): ...this.
(UNSPEC_SET_GOT_VERSION, UNSPEC_UPDATE_GOT_VERSION): New constants.
(FAKE_CALL_REGNO): Rename to...
(GOT_VERSION_REGNUM): ...this.
(type): Add "ghost" value. Add an associated insn reservation.
(hazard_set): Remove.
(exception_receiver): Rename to...
(restore_gp): ...this and update the unspec identifier accordingly.
(exception_receiver, nonlocal_got_receiver): New expanders.
(load_call<mode>): Use GOT_VERSION_REGNUM. Don't set
FAKE_CALL_REGNO. Remove hazard_set attribute.
(set_got_version, update_got_version): New patterns.
2008-01-26 Danny Smith <dannysmith@users.sourceforge.net> 2008-01-26 Danny Smith <dannysmith@users.sourceforge.net>
PR target/34970 PR target/34970
......
...@@ -209,7 +209,7 @@ extern void mips_expand_vcondv2sf (rtx, rtx, rtx, enum rtx_code, rtx, rtx); ...@@ -209,7 +209,7 @@ extern void mips_expand_vcondv2sf (rtx, rtx, rtx, enum rtx_code, rtx, rtx);
extern void mips_expand_conditional_move (rtx *); extern void mips_expand_conditional_move (rtx *);
extern void mips_expand_conditional_trap (enum rtx_code); extern void mips_expand_conditional_trap (enum rtx_code);
#endif #endif
extern void mips_expand_call (rtx, rtx, rtx, rtx, bool); extern rtx mips_expand_call (rtx, rtx, rtx, rtx, bool);
extern void mips_expand_fcc_reload (rtx, rtx, rtx); extern void mips_expand_fcc_reload (rtx, rtx, rtx);
extern void mips_set_return_address (rtx, rtx); extern void mips_set_return_address (rtx, rtx);
extern bool mips_expand_block_move (rtx, rtx, rtx); extern bool mips_expand_block_move (rtx, rtx, rtx);
......
...@@ -2135,6 +2135,31 @@ mips_force_temporary (rtx dest, rtx value) ...@@ -2135,6 +2135,31 @@ mips_force_temporary (rtx dest, rtx value)
return dest; return dest;
} }
} }
/* Emit a call sequence with call pattern PATTERN and return the call
instruction itself (which is not necessarily the last instruction
emitted). LAZY_P is true if the call address is lazily-bound. */
static rtx
mips_emit_call_insn (rtx pattern, bool lazy_p)
{
rtx insn;
insn = emit_call_insn (pattern);
/* Lazy-binding stubs require $gp to be valid on entry. */
if (lazy_p)
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
if (TARGET_USE_GOT)
{
/* See the comment above load_call<mode> for details. */
use_reg (&CALL_INSN_FUNCTION_USAGE (insn),
gen_rtx_REG (Pmode, GOT_VERSION_REGNUM));
emit_insn (gen_update_got_version ());
}
return insn;
}
/* Return a pseudo register that contains the value of $gp throughout /* Return a pseudo register that contains the value of $gp throughout
the current function. Such registers are needed by MIPS16 functions, the current function. Such registers are needed by MIPS16 functions,
...@@ -2309,7 +2334,7 @@ static GTY(()) rtx mips_tls_symbol; ...@@ -2309,7 +2334,7 @@ static GTY(()) rtx mips_tls_symbol;
static rtx static rtx
mips_call_tls_get_addr (rtx sym, enum mips_symbol_type type, rtx v0) mips_call_tls_get_addr (rtx sym, enum mips_symbol_type type, rtx v0)
{ {
rtx insn, loc, tga, a0; rtx insn, loc, a0;
a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST); a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST);
...@@ -2322,8 +2347,7 @@ mips_call_tls_get_addr (rtx sym, enum mips_symbol_type type, rtx v0) ...@@ -2322,8 +2347,7 @@ mips_call_tls_get_addr (rtx sym, enum mips_symbol_type type, rtx v0)
emit_insn (gen_rtx_SET (Pmode, a0, emit_insn (gen_rtx_SET (Pmode, a0,
gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, loc))); gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, loc)));
tga = gen_const_mem (Pmode, mips_tls_symbol); insn = mips_expand_call (v0, mips_tls_symbol, const0_rtx, const0_rtx, false);
insn = emit_call_insn (gen_call_value (v0, tga, const0_rtx, const0_rtx));
CONST_OR_PURE_CALL_P (insn) = 1; CONST_OR_PURE_CALL_P (insn) = 1;
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0); use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0);
insn = get_insns (); insn = get_insns ();
...@@ -5198,7 +5222,7 @@ mips16_copy_fpr_return_value (void) ...@@ -5198,7 +5222,7 @@ mips16_copy_fpr_return_value (void)
fn = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id)); fn = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id));
arg = gen_rtx_REG (return_mode, GP_RETURN); arg = gen_rtx_REG (return_mode, GP_RETURN);
call = gen_call_value_internal (arg, fn, const0_rtx); call = gen_call_value_internal (arg, fn, const0_rtx);
insn = emit_call_insn (call); insn = mips_emit_call_insn (call, false);
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), arg); use_reg (&CALL_INSN_FUNCTION_USAGE (insn), arg);
} }
...@@ -5208,7 +5232,8 @@ mips16_copy_fpr_return_value (void) ...@@ -5208,7 +5232,8 @@ mips16_copy_fpr_return_value (void)
arguments and FP_CODE is the code built by mips_function_arg; arguments and FP_CODE is the code built by mips_function_arg;
see the comment above CUMULATIVE_ARGS for details. see the comment above CUMULATIVE_ARGS for details.
Return true if a stub was needed, and emit the call if so. If a stub was needed, emit the call and return the call insn itself.
Return null otherwise.
A stub is needed for calls to functions that, in normal mode, A stub is needed for calls to functions that, in normal mode,
receive arguments in FPRs or return values in FPRs. The stub receive arguments in FPRs or return values in FPRs. The stub
...@@ -5221,7 +5246,7 @@ mips16_copy_fpr_return_value (void) ...@@ -5221,7 +5246,7 @@ mips16_copy_fpr_return_value (void)
to be to a non-MIPS16 function, the linker automatically redirects to be to a non-MIPS16 function, the linker automatically redirects
the JAL to the stub, otherwise the JAL continues to call FN directly. */ the JAL to the stub, otherwise the JAL continues to call FN directly. */
static bool static rtx
mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code) mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
{ {
const char *fnname; const char *fnname;
...@@ -5232,7 +5257,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code) ...@@ -5232,7 +5257,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
/* We don't need to do anything if we aren't in MIPS16 mode, or if /* We don't need to do anything if we aren't in MIPS16 mode, or if
we were invoked with the -msoft-float option. */ we were invoked with the -msoft-float option. */
if (!TARGET_MIPS16 || TARGET_SOFT_FLOAT_ABI) if (!TARGET_MIPS16 || TARGET_SOFT_FLOAT_ABI)
return false; return NULL_RTX;
/* Figure out whether the value might come back in a floating-point /* Figure out whether the value might come back in a floating-point
register. */ register. */
...@@ -5242,13 +5267,13 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code) ...@@ -5242,13 +5267,13 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
arguments and the value will not be returned in a floating-point arguments and the value will not be returned in a floating-point
register. */ register. */
if (fp_code == 0 && !fp_ret_p) if (fp_code == 0 && !fp_ret_p)
return false; return NULL_RTX;
/* We don't need to do anything if this is a call to a special /* We don't need to do anything if this is a call to a special
MIPS16 support function. */ MIPS16 support function. */
if (GET_CODE (fn) == SYMBOL_REF if (GET_CODE (fn) == SYMBOL_REF
&& strncmp (XSTR (fn, 0), "__mips16_", 9) == 0) && strncmp (XSTR (fn, 0), "__mips16_", 9) == 0)
return false; return NULL_RTX;
/* This code will only work for o32 and o64 abis. The other ABI's /* This code will only work for o32 and o64 abis. The other ABI's
require more sophisticated support. */ require more sophisticated support. */
...@@ -5281,7 +5306,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code) ...@@ -5281,7 +5306,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
insn = gen_call_internal (stub_fn, args_size); insn = gen_call_internal (stub_fn, args_size);
else else
insn = gen_call_value_internal (retval, stub_fn, args_size); insn = gen_call_value_internal (retval, stub_fn, args_size);
insn = emit_call_insn (insn); insn = mips_emit_call_insn (insn, false);
/* Tell GCC that this call does indeed use the value of $2. */ /* Tell GCC that this call does indeed use the value of $2. */
CALL_INSN_FUNCTION_USAGE (insn) = CALL_INSN_FUNCTION_USAGE (insn) =
...@@ -5301,7 +5326,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code) ...@@ -5301,7 +5326,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
gen_rtx_REG (word_mode, 18)), gen_rtx_REG (word_mode, 18)),
CALL_INSN_FUNCTION_USAGE (insn)); CALL_INSN_FUNCTION_USAGE (insn));
return true; return insn;
} }
/* We know the function we are going to call. If we have already /* We know the function we are going to call. If we have already
...@@ -5468,7 +5493,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code) ...@@ -5468,7 +5493,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
insn = gen_call_internal_direct (fn, args_size); insn = gen_call_internal_direct (fn, args_size);
else else
insn = gen_call_value_internal_direct (retval, fn, args_size); insn = gen_call_value_internal_direct (retval, fn, args_size);
insn = emit_call_insn (insn); insn = mips_emit_call_insn (insn, false);
/* If we are calling a stub which handles a floating-point return /* If we are calling a stub which handles a floating-point return
value, we need to arrange to save $18 in the prologue. We do this value, we need to arrange to save $18 in the prologue. We do this
...@@ -5480,7 +5505,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code) ...@@ -5480,7 +5505,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
gen_rtx_USE (VOIDmode, gen_rtx_REG (word_mode, 18)), gen_rtx_USE (VOIDmode, gen_rtx_REG (word_mode, 18)),
CALL_INSN_FUNCTION_USAGE (insn)); CALL_INSN_FUNCTION_USAGE (insn));
return true; return insn;
} }
/* Return true if calls to X can use R_MIPS_CALL* relocations. */ /* Return true if calls to X can use R_MIPS_CALL* relocations. */
...@@ -5531,9 +5556,11 @@ mips_load_call_address (rtx dest, rtx addr, bool sibcall_p) ...@@ -5531,9 +5556,11 @@ mips_load_call_address (rtx dest, rtx addr, bool sibcall_p)
ADDR is the address of the function, ARGS_SIZE is the size of the ADDR is the address of the function, ARGS_SIZE is the size of the
arguments and AUX is the value passed to us by mips_function_arg. arguments and AUX is the value passed to us by mips_function_arg.
SIBCALL_P is true if we are expanding a sibling call, false if we're SIBCALL_P is true if we are expanding a sibling call, false if we're
expanding a normal call. */ expanding a normal call.
void Return the call itself. */
rtx
mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, bool sibcall_p) mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, bool sibcall_p)
{ {
rtx orig_addr, pattern, insn; rtx orig_addr, pattern, insn;
...@@ -5547,13 +5574,12 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, bool sibcall_p) ...@@ -5547,13 +5574,12 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, bool sibcall_p)
lazy_p = mips_load_call_address (addr, orig_addr, sibcall_p); lazy_p = mips_load_call_address (addr, orig_addr, sibcall_p);
} }
if (TARGET_MIPS16 insn = mips16_build_call_stub (result, addr, args_size,
&& TARGET_HARD_FLOAT_ABI aux == 0 ? 0 : (int) GET_MODE (aux));
&& mips16_build_call_stub (result, addr, args_size, if (insn)
aux == 0 ? 0 : (int) GET_MODE (aux)))
{ {
gcc_assert (!sibcall_p); gcc_assert (!sibcall_p && !lazy_p);
return; return insn;
} }
if (result == 0) if (result == 0)
...@@ -5582,17 +5608,7 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, bool sibcall_p) ...@@ -5582,17 +5608,7 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, bool sibcall_p)
: gen_call_value_internal (result, addr, args_size)); : gen_call_value_internal (result, addr, args_size));
} }
insn = emit_call_insn (pattern); return mips_emit_call_insn (pattern, lazy_p);
/* Lazy-binding stubs require $gp to be valid on entry. We also pretend
that they use FAKE_CALL_REGNO; see the load_call<mode> patterns for
details. */
if (lazy_p)
{
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
use_reg (&CALL_INSN_FUNCTION_USAGE (insn),
gen_rtx_REG (Pmode, FAKE_CALL_REGNO));
}
} }
/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL. */ /* Implement TARGET_FUNCTION_OK_FOR_SIBCALL. */
...@@ -7977,14 +7993,21 @@ mips_initial_elimination_offset (int from, int to) ...@@ -7977,14 +7993,21 @@ mips_initial_elimination_offset (int from, int to)
return offset; return offset;
} }
/* Implement TARGET_EXTRA_LIVE_ON_ENTRY. Some code models use the incoming /* Implement TARGET_EXTRA_LIVE_ON_ENTRY. */
value of PIC_FUNCTION_ADDR_REGNUM to set up the global pointer. */
static void static void
mips_extra_live_on_entry (bitmap regs) mips_extra_live_on_entry (bitmap regs)
{ {
if (TARGET_USE_GOT && !TARGET_ABSOLUTE_ABICALLS) if (TARGET_USE_GOT)
bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM); {
/* PIC_FUNCTION_ADDR_REGNUM is live if we need it to set up
the global pointer. */
if (!TARGET_ABSOLUTE_ABICALLS)
bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM);
/* See the comment above load_call<mode> for details. */
bitmap_set_bit (regs, GOT_VERSION_REGNUM);
}
} }
/* Implement RETURN_ADDR_RTX. We do not support moving back to a /* Implement RETURN_ADDR_RTX. We do not support moving back to a
...@@ -8720,6 +8743,9 @@ mips_hard_regno_mode_ok_p (unsigned int regno, enum machine_mode mode) ...@@ -8720,6 +8743,9 @@ mips_hard_regno_mode_ok_p (unsigned int regno, enum machine_mode mode)
if (ALL_COP_REG_P (regno)) if (ALL_COP_REG_P (regno))
return class == MODE_INT && size <= UNITS_PER_WORD; return class == MODE_INT && size <= UNITS_PER_WORD;
if (regno == GOT_VERSION_REGNUM)
return mode == SImode;
return false; return false;
} }
...@@ -11360,7 +11386,7 @@ mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay, ...@@ -11360,7 +11386,7 @@ mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay,
rtx *delayed_reg, rtx lo_reg) rtx *delayed_reg, rtx lo_reg)
{ {
rtx pattern, set; rtx pattern, set;
int nops, ninsns, hazard_set; int nops, ninsns;
pattern = PATTERN (insn); pattern = PATTERN (insn);
...@@ -11406,15 +11432,8 @@ mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay, ...@@ -11406,15 +11432,8 @@ mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay,
break; break;
case HAZARD_DELAY: case HAZARD_DELAY:
hazard_set = (int) get_attr_hazard_set (insn); set = single_set (insn);
if (hazard_set == 0) gcc_assert (set);
set = single_set (insn);
else
{
gcc_assert (GET_CODE (PATTERN (insn)) == PARALLEL);
set = XVECEXP (PATTERN (insn), 0, hazard_set - 1);
}
gcc_assert (set && GET_CODE (set) == SET);
*delayed_reg = SET_DEST (set); *delayed_reg = SET_DEST (set);
break; break;
} }
......
...@@ -1372,7 +1372,7 @@ enum mips_code_readable_setting { ...@@ -1372,7 +1372,7 @@ enum mips_code_readable_setting {
- 3 fake registers: - 3 fake registers:
- ARG_POINTER_REGNUM - ARG_POINTER_REGNUM
- FRAME_POINTER_REGNUM - FRAME_POINTER_REGNUM
- FAKE_CALL_REGNO (see the comment above load_callsi for details) - GOT_VERSION_REGNUM (see the comment above load_call<mode> for details)
- 3 dummy entries that were used at various times in the past. - 3 dummy entries that were used at various times in the past.
- 6 DSP accumulator registers (3 hi-lo pairs) for MIPS DSP ASE - 6 DSP accumulator registers (3 hi-lo pairs) for MIPS DSP ASE
- 6 DSP control registers */ - 6 DSP control registers */
...@@ -1452,7 +1452,7 @@ enum mips_code_readable_setting { ...@@ -1452,7 +1452,7 @@ enum mips_code_readable_setting {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
/* Others. */ \ /* Others. */ \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, \
/* COP0 registers */ \ /* COP0 registers */ \
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
...@@ -2089,8 +2089,12 @@ typedef struct mips_args { ...@@ -2089,8 +2089,12 @@ typedef struct mips_args {
/* Say that the epilogue uses the return address register. Note that /* Say that the epilogue uses the return address register. Note that
in the case of sibcalls, the values "used by the epilogue" are in the case of sibcalls, the values "used by the epilogue" are
considered live at the start of the called function. */ considered live at the start of the called function.
#define EPILOGUE_USES(REGNO) ((REGNO) == 31)
If using a GOT, say that the epilogue also uses GOT_VERSION_REGNUM.
See the comment above load_call<mode> for details. */
#define EPILOGUE_USES(REGNO) \
((REGNO) == 31 || (TARGET_USE_GOT && (REGNO) == GOT_VERSION_REGNUM))
/* Treat LOC as a byte offset from the stack pointer and round it up /* Treat LOC as a byte offset from the stack pointer and round it up
to the next fully-aligned offset. */ to the next fully-aligned offset. */
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
(UNSPEC_GET_FNADDR 3) (UNSPEC_GET_FNADDR 3)
(UNSPEC_BLOCKAGE 4) (UNSPEC_BLOCKAGE 4)
(UNSPEC_CPRESTORE 5) (UNSPEC_CPRESTORE 5)
(UNSPEC_EH_RECEIVER 6) (UNSPEC_RESTORE_GP 6)
(UNSPEC_EH_RETURN 7) (UNSPEC_EH_RETURN 7)
(UNSPEC_CONSTTABLE_INT 8) (UNSPEC_CONSTTABLE_INT 8)
(UNSPEC_CONSTTABLE_FLOAT 9) (UNSPEC_CONSTTABLE_FLOAT 9)
...@@ -58,10 +58,12 @@ ...@@ -58,10 +58,12 @@
(UNSPEC_SYNC_NEW_OP 39) (UNSPEC_SYNC_NEW_OP 39)
(UNSPEC_SYNC_EXCHANGE 40) (UNSPEC_SYNC_EXCHANGE 40)
(UNSPEC_MEMORY_BARRIER 41) (UNSPEC_MEMORY_BARRIER 41)
(UNSPEC_SET_GOT_VERSION 42)
(UNSPEC_UPDATE_GOT_VERSION 43)
(UNSPEC_ADDRESS_FIRST 100) (UNSPEC_ADDRESS_FIRST 100)
(FAKE_CALL_REGNO 79) (GOT_VERSION_REGNUM 79)
;; For MIPS Paired-Singled Floating Point Instructions. ;; For MIPS Paired-Singled Floating Point Instructions.
...@@ -290,8 +292,9 @@ ...@@ -290,8 +292,9 @@
;; frsqrt2 floating point reciprocal square root step2 ;; frsqrt2 floating point reciprocal square root step2
;; multi multiword sequence (or user asm statements) ;; multi multiword sequence (or user asm statements)
;; nop no operation ;; nop no operation
;; ghost an instruction that produces no real code
(define_attr "type" (define_attr "type"
"unknown,branch,jump,call,load,fpload,fpidxload,store,fpstore,fpidxstore,prefetch,prefetchx,condmove,mfc,mtc,mthilo,mfhilo,const,arith,logical,shift,slt,signext,clz,trap,imul,imul3,imadd,idiv,move,fmove,fadd,fmul,fmadd,fdiv,frdiv,frdiv1,frdiv2,fabs,fneg,fcmp,fcvt,fsqrt,frsqrt,frsqrt1,frsqrt2,multi,nop" "unknown,branch,jump,call,load,fpload,fpidxload,store,fpstore,fpidxstore,prefetch,prefetchx,condmove,mfc,mtc,mthilo,mfhilo,const,arith,logical,shift,slt,signext,clz,trap,imul,imul3,imadd,idiv,move,fmove,fadd,fmul,fmadd,fdiv,frdiv,frdiv1,frdiv2,fabs,fneg,fcmp,fcvt,fsqrt,frsqrt,frsqrt1,frsqrt2,multi,nop,ghost"
(cond [(eq_attr "jal" "!unset") (const_string "call") (cond [(eq_attr "jal" "!unset") (const_string "call")
(eq_attr "got" "load") (const_string "load")] (eq_attr "got" "load") (const_string "load")]
(const_string "unknown"))) (const_string "unknown")))
...@@ -438,17 +441,6 @@ ...@@ -438,17 +441,6 @@
(const_string "hilo")] (const_string "hilo")]
(const_string "none"))) (const_string "none")))
;; Indicates which SET in an instruction pattern induces a hazard.
;; Only meaningful when "hazard" is not "none". SINGLE means that
;; the pattern has only one set while the other values are indexes
;; into a PARALLEL vector.
;;
;; Hazardous instructions with multiple sets should generally put the
;; hazardous set first. The only purpose of this attribute is to force
;; each multi-set pattern to explicitly assert that this condition holds.
(define_attr "hazard_set" "single,0"
(const_string "single"))
;; Is it a single instruction? ;; Is it a single instruction?
(define_attr "single_insn" "no,yes" (define_attr "single_insn" "no,yes"
(symbol_ref "get_attr_length (insn) == (TARGET_MIPS16 ? 2 : 4)")) (symbol_ref "get_attr_length (insn) == (TARGET_MIPS16 ? 2 : 4)"))
...@@ -703,6 +695,12 @@ ...@@ -703,6 +695,12 @@
(define_cpu_unit "alu" "alu") (define_cpu_unit "alu" "alu")
(define_cpu_unit "imuldiv" "imuldiv") (define_cpu_unit "imuldiv" "imuldiv")
;; Ghost instructions produce no real code and introduce no hazards.
;; They exist purely to express an effect on dataflow.
(define_insn_reservation "ghost" 0
(eq_attr "type" "ghost")
"nothing")
(include "4k.md") (include "4k.md")
(include "5k.md") (include "5k.md")
(include "20kc.md") (include "20kc.md")
...@@ -5598,9 +5596,33 @@ ...@@ -5598,9 +5596,33 @@
DONE; DONE;
}) })
(define_insn_and_split "exception_receiver" (define_expand "exception_receiver"
[(const_int 0)]
"TARGET_USE_GOT"
{
/* See the comment above load_call<mode> for details. */
emit_insn (gen_set_got_version ());
/* If we have a call-clobbered $gp, restore it from its save slot. */
if (HAVE_restore_gp)
emit_insn (gen_restore_gp ());
DONE;
})
(define_expand "nonlocal_goto_receiver"
[(const_int 0)]
"TARGET_USE_GOT"
{
/* See the comment above load_call<mode> for details. */
emit_insn (gen_set_got_version ());
DONE;
})
;; Restore $gp from its .cprestore stack slot. The instruction remains
;; volatile until all uses of $28 are exposed.
(define_insn_and_split "restore_gp"
[(set (reg:SI 28) [(set (reg:SI 28)
(unspec_volatile:SI [(const_int 0)] UNSPEC_EH_RECEIVER))] (unspec_volatile:SI [(const_int 0)] UNSPEC_RESTORE_GP))]
"TARGET_CALL_CLOBBERED_GP" "TARGET_CALL_CLOBBERED_GP"
"#" "#"
"&& reload_completed" "&& reload_completed"
...@@ -5629,24 +5651,66 @@ ...@@ -5629,24 +5651,66 @@
;; potentially modify the GOT entry. And once a stub has been called, ;; potentially modify the GOT entry. And once a stub has been called,
;; we must not call it again. ;; we must not call it again.
;; ;;
;; We represent this restriction using an imaginary fixed register that ;; We represent this restriction using an imaginary, fixed, call-saved
;; is set by the GOT load and used by the call. By making this register ;; register called GOT_VERSION_REGNUM. The idea is to make the register
;; call-clobbered, and by making the GOT load the only way of setting ;; live throughout the function and to change its value after every
;; the register, we ensure that the load cannot be moved past a call. ;; potential call site. This stops any rtx value that uses the register
;; from being computed before an earlier call. To do this, we:
;;
;; - Ensure that the register is live on entry to the function,
;; so that it is never thought to be used uninitalized.
;;
;; - Ensure that the register is live on exit from the function,
;; so that it is live throughout.
;;
;; - Make each call (lazily-bound or not) use the current value
;; of GOT_VERSION_REGNUM, so that updates of the register are
;; not moved across call boundaries.
;;
;; - Add "ghost" definitions of the register to the beginning of
;; blocks reached by EH and ABNORMAL_CALL edges, because those
;; edges may involve calls that normal paths don't. (E.g. the
;; unwinding code that handles a non-call exception may change
;; lazily-bound GOT entries.) We do this by making the
;; exception_receiver and nonlocal_goto_receiver expanders emit
;; a set_got_version instruction.
;;
;; - After each call (lazily-bound or not), use a "ghost"
;; update_got_version instruction to change the register's value.
;; This instruction mimics the _possible_ effect of the dynamic
;; resolver during the call and it remains live even if the call
;; itself becomes dead.
;;
;; - Leave GOT_VERSION_REGNUM out of all register classes.
;; The register is therefore not a valid register_operand
;; and cannot be moved to or from other registers.
(define_insn "load_call<mode>" (define_insn "load_call<mode>"
[(set (match_operand:P 0 "register_operand" "=d") [(set (match_operand:P 0 "register_operand" "=d")
(unspec:P [(match_operand:P 1 "register_operand" "r") (unspec:P [(match_operand:P 1 "register_operand" "r")
(match_operand:P 2 "immediate_operand" "")] (match_operand:P 2 "immediate_operand" "")
UNSPEC_LOAD_CALL)) (reg:SI GOT_VERSION_REGNUM)] UNSPEC_LOAD_CALL))]
(set (reg:P FAKE_CALL_REGNO)
(unspec:P [(match_dup 2)] UNSPEC_LOAD_CALL))]
"TARGET_USE_GOT" "TARGET_USE_GOT"
"<load>\t%0,%R2(%1)" "<load>\t%0,%R2(%1)"
[(set_attr "type" "load") [(set_attr "type" "load")
(set_attr "mode" "<MODE>") (set_attr "mode" "<MODE>")
(set_attr "hazard_set" "0")
(set_attr "length" "4")]) (set_attr "length" "4")])
(define_insn "set_got_version"
[(set (reg:SI GOT_VERSION_REGNUM)
(unspec_volatile:SI [(const_int 0)] UNSPEC_SET_GOT_VERSION))]
"TARGET_USE_GOT"
""
[(set_attr "length" "0")
(set_attr "type" "ghost")])
(define_insn "update_got_version"
[(set (reg:SI GOT_VERSION_REGNUM)
(unspec:SI [(reg:SI GOT_VERSION_REGNUM)] UNSPEC_UPDATE_GOT_VERSION))]
"TARGET_USE_GOT"
""
[(set_attr "length" "0")
(set_attr "type" "ghost")])
;; Sibling calls. All these patterns use jump instructions. ;; Sibling calls. All these patterns use jump instructions.
;; If TARGET_SIBCALLS, call_insn_operand will only accept constant ;; If TARGET_SIBCALLS, call_insn_operand will only accept constant
......
2008-01-26 Richard Sandiford <rsandifo@nildram.co.uk>
PR target/34981
* gcc.target/mips/lazy-binding-1.c: New test.
* gcc.target/mips/mips.exp (setup_mips_tests): Set
mips_forced_no_abicalls and mips_forced_no_shared.
(dg-mips-options): Avoid using -mabicalls with an implicit -mabi=eabi.
Avoid using small data with -mabicalls. Don't make -G0 force
-mn-abicalls. Skip -mabicalls and -mshared tests if the multilib
forces the opposite option.
2008-01-26 Danny Smith <dannysmith@users.sourceforge.net> 2008-01-26 Danny Smith <dannysmith@users.sourceforge.net>
PR target/34970 PR target/34970
/* { dg-mips-options "-mabicalls -mshared -mexplicit-relocs -O2 -fno-delayed-branch" } */
void bar (void);
void
foo (int n)
{
while (n--)
{
bar ();
bar ();
}
}
/* There should be exactly five uses of $25: one to set up $gp, two to
load the address of bar (), and two to call it. */
/* { dg-final { scan-assembler-times "\tl.\t\\\$25,%call16\\\(bar\\\)" 2 } } */
/* { dg-final { scan-assembler-times "\tjalr\t\\\$25" 2 } } */
/* { dg-final { scan-assembler "(\\\$28,|\t.cpload\t)\\\$25" } } */
/* { dg-final { scan-assembler-times "\\\$25" 5 } } */
...@@ -44,6 +44,10 @@ load_lib gcc-dg.exp ...@@ -44,6 +44,10 @@ load_lib gcc-dg.exp
# $mips_forced_be true if the command line uses -EB or -meb # $mips_forced_be true if the command line uses -EB or -meb
# $mips_forced_le true if the command line uses -EL or -mel # $mips_forced_le true if the command line uses -EL or -mel
# $mips_forced_gp true if the command line forces a particular GP mode # $mips_forced_gp true if the command line forces a particular GP mode
# $mips_forced_no_abicalls
# true if the command line contains -mno-abicalls
# $mips_forced_no_shared
# true if the command line contains -mno-shared
# $mips_forced_no_er true if the command line contains -mno-explicit-relocs # $mips_forced_no_er true if the command line contains -mno-explicit-relocs
proc setup_mips_tests {} { proc setup_mips_tests {} {
global mips_isa global mips_isa
...@@ -61,6 +65,8 @@ proc setup_mips_tests {} { ...@@ -61,6 +65,8 @@ proc setup_mips_tests {} {
global mips_forced_be global mips_forced_be
global mips_forced_le global mips_forced_le
global mips_forced_gp global mips_forced_gp
global mips_forced_no_abicalls
global mips_forced_no_shared
global mips_forced_no_er global mips_forced_no_er
global mips_forced_regs global mips_forced_regs
...@@ -123,6 +129,8 @@ proc setup_mips_tests {} { ...@@ -123,6 +129,8 @@ proc setup_mips_tests {} {
set mips_forced_be [regexp -- {-(EB|meb)[[:>:]]} $compiler_flags] set mips_forced_be [regexp -- {-(EB|meb)[[:>:]]} $compiler_flags]
set mips_forced_le [regexp -- {-(EL|mel)[[:>:]]} $compiler_flags] set mips_forced_le [regexp -- {-(EL|mel)[[:>:]]} $compiler_flags]
set mips_forced_gp [regexp -- {-(G|m(|no-)((extern|local)-sdata|gpopt)|mabicalls|mrtp)} $compiler_flags] set mips_forced_gp [regexp -- {-(G|m(|no-)((extern|local)-sdata|gpopt)|mabicalls|mrtp)} $compiler_flags]
set mips_forced_no_abicalls [regexp -- {-mno-abicalls} $compiler_flags]
set mips_forced_no_shared [regexp -- {-mno-shared} $compiler_flags]
set mips_forced_no_er [regexp -- {-mno-explicit-relocs} $compiler_flags] set mips_forced_no_er [regexp -- {-mno-explicit-relocs} $compiler_flags]
if {$mips_forced_regs && $mips_gp == 32 && $mips_fp == 64} { if {$mips_forced_regs && $mips_gp == 32 && $mips_fp == 64} {
...@@ -178,6 +186,11 @@ proc setup_mips_tests {} { ...@@ -178,6 +186,11 @@ proc setup_mips_tests {} {
# the multilib flags already contain such an option, or specify # the multilib flags already contain such an option, or specify
# something that might be incompatible with them. # something that might be incompatible with them.
# #
# -mabicalls
# -mshared
# Select the form of SVR4 PIC. Skip the test if the multilib flags
# conflict with the required setting.
#
# -mexplicit-relocs # -mexplicit-relocs
# Select explicit relocations. Skip the test if the multilib flags # Select explicit relocations. Skip the test if the multilib flags
# force -mno-explicit-relocs. # force -mno-explicit-relocs.
...@@ -204,6 +217,8 @@ proc dg-mips-options {args} { ...@@ -204,6 +217,8 @@ proc dg-mips-options {args} {
global mips_forced_be global mips_forced_be
global mips_forced_le global mips_forced_le
global mips_forced_gp global mips_forced_gp
global mips_forced_no_abicalls
global mips_forced_no_shared
global mips_forced_no_er global mips_forced_no_er
set flags [lindex $args 1] set flags [lindex $args 1]
...@@ -281,6 +296,21 @@ proc dg-mips-options {args} { ...@@ -281,6 +296,21 @@ proc dg-mips-options {args} {
} }
} }
foreach flag $flags {
if {[string match -mabicalls $flag]} {
# EABI has no SVR4-style PIC mode, so try to force another ABI.
if {$mips_abi == "eabi" && [lsearch $flags "-mabi=*"] < 0} {
if {$mips_new_gp == 32} {
append flags " -mabi=32"
} else {
append flags " -mabi=n32"
}
}
# Turn off small data, if on by default.
append flags " -G0"
}
}
# Handle the other options. # Handle the other options.
foreach flag $flags { foreach flag $flags {
if {[regexp -- {^-mabi=(.*)} $flag dummy abi]} { if {[regexp -- {^-mabi=(.*)} $flag dummy abi]} {
...@@ -313,10 +343,20 @@ proc dg-mips-options {args} { ...@@ -313,10 +343,20 @@ proc dg-mips-options {args} {
set matches 0 set matches 0
} }
} elseif {[regexp -- {^-(G|m(|no-)((extern|local)-sdata|gpopt))} $flag]} { } elseif {[regexp -- {^-(G|m(|no-)((extern|local)-sdata|gpopt))} $flag]} {
append flags " -mno-abicalls" if {$flag != "-G0"} {
append flags " -mno-abicalls"
}
if {$mips_forced_gp} { if {$mips_forced_gp} {
set matches 0 set matches 0
} }
} elseif {[regexp -- {^-mabicalls$} $flag]} {
if {$mips_forced_no_abicalls} {
set matches 0
}
} elseif {[regexp -- {^-mshared$} $flag]} {
if {$mips_forced_no_shared} {
set matches 0
}
} elseif {[regexp -- {^-mexplicit-relocs$} $flag]} { } elseif {[regexp -- {^-mexplicit-relocs$} $flag]} {
if {$mips_forced_no_er} { if {$mips_forced_no_er} {
set matches 0 set matches 0
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment