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 *);
}
/* 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,
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;
+ }
}
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
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<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));
+ }
}
rtx *delayed_reg, rtx lo_reg)
{
rtx pattern, set;
- int nops, ninsns;
+ int nops, ninsns, hazard_set;
if (!INSN_P (insn))
return;
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;
}
(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)"))
;; 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<mode>"
[(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"
"<load>\t%0,%R2(%1)"
[(set_attr "type" "load")
(set_attr "mode" "<MODE>")
+ (set_attr "hazard_set" "0")
(set_attr "length" "4")])
;; Sibling calls. All these patterns use jump instructions.