diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 125ae4580f74..956f4674d074 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,11 @@ +2007-10-18 Richard Sandiford + + * config/mips/mips.c (mips_expand_call): Use FAKE_CALL_REGNO. + (mips_avoid_hazard): Allow multiple sets for HAZARD_DELAY, + and pick the first. + * config/mips/mips.md (load_call): Don't make the unspec + depend on FAKE_CALL_REGNO. Set FAKE_CALL_REGNO. + 2007-10-18 David Daney * config/mips/linux-unwind.h (mips_fallback_frame_state): Use new diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c index f9d559c5c510..befb1fe452b9 100644 --- a/gcc/config/mips/mips.c +++ b/gcc/config/mips/mips.c @@ -308,7 +308,7 @@ static int m16_check_op (rtx, int, int, int); static bool mips_rtx_costs (rtx, int, int, int *); static int mips_address_cost (rtx); static void mips_emit_compare (enum rtx_code *, rtx *, rtx *, bool); -static void mips_load_call_address (rtx, rtx, int); +static bool mips_load_call_address (rtx, rtx, int); static bool mips_function_ok_for_sibcall (tree, tree); static void mips_block_move_straight (rtx, rtx, HOST_WIDE_INT); static void mips_adjust_block_mem (rtx, HOST_WIDE_INT, rtx *, rtx *); @@ -4135,9 +4135,10 @@ mips_ok_for_lazy_binding_p (rtx x) } /* Load function address ADDR into register DEST. SIBCALL_P is true - if the address is needed for a sibling call. */ + if the address is needed for a sibling call. Return true if we + used an explicit lazy-binding sequence. */ -static void +static bool mips_load_call_address (rtx dest, rtx addr, int sibcall_p) { /* If we're generating PIC, and this call is to a global function, @@ -4157,9 +4158,13 @@ mips_load_call_address (rtx dest, rtx addr, int sibcall_p) emit_insn (gen_load_callsi (dest, high, lo_sum_symbol)); else emit_insn (gen_load_calldi (dest, high, lo_sum_symbol)); + return true; } else - mips_emit_move (dest, addr); + { + mips_emit_move (dest, addr); + return false; + } } @@ -4174,12 +4179,14 @@ void mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p) { rtx orig_addr, pattern, insn; + bool lazy_p; orig_addr = addr; + lazy_p = false; if (!call_insn_operand (addr, VOIDmode)) { addr = gen_reg_rtx (Pmode); - mips_load_call_address (addr, orig_addr, sibcall_p); + lazy_p = mips_load_call_address (addr, orig_addr, sibcall_p); } if (TARGET_MIPS16 @@ -4210,9 +4217,15 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p) insn = emit_call_insn (pattern); - /* Lazy-binding stubs require $gp to be valid on entry. */ - if (mips_ok_for_lazy_binding_p (orig_addr)) - use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx); + /* Lazy-binding stubs require $gp to be valid on entry. We also pretend + that they use FAKE_CALL_REGNO; see the load_call 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)); + } } @@ -10748,7 +10761,7 @@ mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay, rtx *delayed_reg, rtx lo_reg) { rtx pattern, set; - int nops, ninsns; + int nops, ninsns, hazard_set; if (!INSN_P (insn)) return; @@ -10797,8 +10810,15 @@ mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay, break; case HAZARD_DELAY: - set = single_set (insn); - gcc_assert (set != 0); + hazard_set = (int) get_attr_hazard_set (insn); + if (hazard_set == 0) + 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); break; } diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md index f4b90eb6ee05..53726e85261d 100644 --- a/gcc/config/mips/mips.md +++ b/gcc/config/mips/mips.md @@ -438,6 +438,17 @@ (const_string "hilo")] (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? (define_attr "single_insn" "no,yes" (symbol_ref "get_attr_length (insn) == (TARGET_MIPS16 ? 2 : 4)")) @@ -5606,19 +5617,21 @@ ;; we must not call it again. ;; ;; We represent this restriction using an imaginary fixed register that -;; acts like a GOT version number. By making the register call-clobbered, -;; we tell the target-independent code that the address could be changed -;; by any call insn. +;; is set by the GOT load and used by the call. By making this register +;; call-clobbered, and by making the GOT load the only way of setting +;; the register, we ensure that the load cannot be moved past a call. (define_insn "load_call" [(set (match_operand:P 0 "register_operand" "=d") (unspec:P [(match_operand:P 1 "register_operand" "r") - (match_operand:P 2 "immediate_operand" "") - (reg:P FAKE_CALL_REGNO)] - UNSPEC_LOAD_CALL))] + (match_operand:P 2 "immediate_operand" "")] + UNSPEC_LOAD_CALL)) + (set (reg:P FAKE_CALL_REGNO) + (unspec:P [(match_dup 2)] UNSPEC_LOAD_CALL))] "TARGET_USE_GOT" "\t%0,%R2(%1)" [(set_attr "type" "load") (set_attr "mode" "") + (set_attr "hazard_set" "0") (set_attr "length" "4")]) ;; Sibling calls. All these patterns use jump instructions.