285 lines
8.2 KiB
Markdown
285 lines
8.2 KiB
Markdown
|
;; ----------------------------------------------------------------------
|
||
|
;; JUMP INSTRUCTIONS
|
||
|
;; ----------------------------------------------------------------------
|
||
|
|
||
|
;; Conditional jump instructions
|
||
|
|
||
|
(define_expand "cbranch<mode>4"
|
||
|
[(set (pc)
|
||
|
(if_then_else (match_operator 0 "ordered_comparison_operator"
|
||
|
[(match_operand:QHSI 1 "h8300_dst_operand")
|
||
|
(match_operand:QHSI 2 "h8300_src_operand")])
|
||
|
(label_ref (match_operand 3 ""))
|
||
|
(pc)))]
|
||
|
"")
|
||
|
|
||
|
(define_insn_and_split "*branch"
|
||
|
[(set (pc)
|
||
|
(if_then_else (match_operator 0 "comparison_operator"
|
||
|
[(match_operand:QHSI 1 "h8300_dst_operand" "rQ")
|
||
|
(match_operand:QHSI 2 "h8300_src_operand" "rQi")])
|
||
|
(label_ref (match_operand 3 "" ""))
|
||
|
(pc)))]
|
||
|
""
|
||
|
"#"
|
||
|
"reload_completed"
|
||
|
[(set (reg:H8cc CC_REG)
|
||
|
(compare:H8cc (match_dup 1) (match_dup 2)))
|
||
|
(set (pc)
|
||
|
(if_then_else (match_op_dup 0
|
||
|
[(reg:H8cc CC_REG) (const_int 0)])
|
||
|
(label_ref (match_dup 3)) (pc)))]
|
||
|
"")
|
||
|
|
||
|
(define_insn "*branch_1"
|
||
|
[(set (pc)
|
||
|
(if_then_else (match_operator 1 "comparison_operator"
|
||
|
[(reg:H8cc CC_REG) (const_int 0)])
|
||
|
(label_ref (match_operand 0 "" ""))
|
||
|
(pc)))]
|
||
|
"reload_completed"
|
||
|
{
|
||
|
if (get_attr_length (insn) == 2)
|
||
|
return "b%j1 %l0";
|
||
|
else if (get_attr_length (insn) == 4)
|
||
|
return "b%j1 %l0:16";
|
||
|
else
|
||
|
return "b%k1 .Lh8BR%=\;jmp @%l0\\n.Lh8BR%=:";
|
||
|
}
|
||
|
[(set_attr "type" "branch")])
|
||
|
|
||
|
|
||
|
(define_insn "*branch_1_false"
|
||
|
[(set (pc)
|
||
|
(if_then_else (match_operator 1 "comparison_operator"
|
||
|
[(reg:H8cc CC_REG) (const_int 0)])
|
||
|
(pc)
|
||
|
(label_ref (match_operand 0 "" ""))))]
|
||
|
"reload_completed"
|
||
|
{
|
||
|
if (get_attr_length (insn) == 2)
|
||
|
return "b%k1 %l0";
|
||
|
else if (get_attr_length (insn) == 4)
|
||
|
return "b%k1 %l0:16";
|
||
|
else
|
||
|
return "b%j1 .Lh8BR%=\;jmp @%l0\\n.Lh8BR%=:";
|
||
|
}
|
||
|
[(set_attr "type" "branch")])
|
||
|
|
||
|
;; The brabc/brabs patterns have been disabled because their length computation
|
||
|
;; is horribly broken. When we call out to a function via a SYMBOL_REF we get
|
||
|
;; bogus default and minimum lengths. The trick used by the PA port seems to
|
||
|
;; fix the minimum, but not the default length. The broken lengths can lead
|
||
|
;; to bogusly using a short jump when a long jump was needed and thus
|
||
|
;; incorrect code.
|
||
|
;;
|
||
|
;; Given the restricted addressing modes for operand 1, we could probably just
|
||
|
;; open-code the necessary length computation in the two affected patterns
|
||
|
;; rather than using a function call. I think that would fix this problem.
|
||
|
(define_insn "*brabc"
|
||
|
[(set (pc)
|
||
|
(if_then_else (eq (zero_extract (match_operand:QI 1 "bit_memory_operand" "WU")
|
||
|
(const_int 1)
|
||
|
(match_operand:QI 2 "immediate_operand" "n"))
|
||
|
(const_int 0))
|
||
|
(label_ref (match_operand 0 "" ""))
|
||
|
(pc)))]
|
||
|
"0 && TARGET_H8300SX"
|
||
|
{
|
||
|
switch (get_attr_length (insn)
|
||
|
- h8300_insn_length_from_table (insn, operands))
|
||
|
{
|
||
|
case 2:
|
||
|
return "bra/bc %2,%R1,%l0";
|
||
|
case 4:
|
||
|
return "bra/bc %2,%R1,%l0:16";
|
||
|
default:
|
||
|
return "bra/bs %2,%R1,.Lh8BR%=\;jmp @%l0\\n.Lh8BR%=:";
|
||
|
}
|
||
|
}
|
||
|
[(set_attr "type" "bitbranch")
|
||
|
(set_attr "length_table" "bitbranch")])
|
||
|
|
||
|
(define_insn "*brabs"
|
||
|
[(set (pc)
|
||
|
(if_then_else (ne (zero_extract (match_operand:QI 1 "bit_memory_operand" "WU")
|
||
|
(const_int 1)
|
||
|
(match_operand:QI 2 "immediate_operand" "n"))
|
||
|
(const_int 0))
|
||
|
(label_ref (match_operand 0 "" ""))
|
||
|
(pc)))]
|
||
|
"0 && TARGET_H8300SX"
|
||
|
{
|
||
|
switch (get_attr_length (insn)
|
||
|
- h8300_insn_length_from_table (insn, operands))
|
||
|
{
|
||
|
case 2:
|
||
|
return "bra/bs %2,%R1,%l0";
|
||
|
case 4:
|
||
|
return "bra/bs %2,%R1,%l0:16";
|
||
|
default:
|
||
|
return "bra/bc %2,%R1,.Lh8BR%=\;jmp @%l0\\n.Lh8BR%=:";
|
||
|
}
|
||
|
}
|
||
|
[(set_attr "type" "bitbranch")
|
||
|
(set_attr "length_table" "bitbranch")])
|
||
|
|
||
|
;; Unconditional and other jump instructions.
|
||
|
|
||
|
(define_insn "jump"
|
||
|
[(set (pc)
|
||
|
(label_ref (match_operand 0 "" "")))]
|
||
|
""
|
||
|
{
|
||
|
if (final_sequence != 0)
|
||
|
{
|
||
|
if (get_attr_length (insn) == 2)
|
||
|
return "bra/s %l0";
|
||
|
else
|
||
|
{
|
||
|
/* The branch isn't short enough to use bra/s. Output the
|
||
|
branch and delay slot in their normal order.
|
||
|
|
||
|
If this is a backward branch, it will now be branching two
|
||
|
bytes further than previously thought. The length-based
|
||
|
test for bra vs. jump is very conservative though, so the
|
||
|
branch will still be within range. */
|
||
|
rtx_sequence *seq;
|
||
|
int seen;
|
||
|
|
||
|
seq = final_sequence;
|
||
|
final_sequence = 0;
|
||
|
final_scan_insn (seq->insn (1), asm_out_file, optimize, 1, & seen);
|
||
|
final_scan_insn (seq->insn (0), asm_out_file, optimize, 1, & seen);
|
||
|
seq->insn (1)->set_deleted ();
|
||
|
return "";
|
||
|
}
|
||
|
}
|
||
|
else if (get_attr_length (insn) == 2)
|
||
|
return "bra %l0";
|
||
|
else if (get_attr_length (insn) == 4)
|
||
|
return "bra %l0:16";
|
||
|
else
|
||
|
return "jmp @%l0";
|
||
|
}
|
||
|
[(set_attr "type" "branch")
|
||
|
(set (attr "delay_slot")
|
||
|
(if_then_else (match_test "TARGET_H8300SX")
|
||
|
(const_string "jump")
|
||
|
(const_string "none")))])
|
||
|
|
||
|
;; This is a define expand, because pointers may be either 16 or 32 bits.
|
||
|
|
||
|
(define_expand "tablejump"
|
||
|
[(parallel [(set (pc) (match_operand 0 "register_operand" ""))
|
||
|
(use (label_ref (match_operand 1 "" "")))])]
|
||
|
""
|
||
|
"")
|
||
|
|
||
|
(define_insn "tablejump<mode>"
|
||
|
[(set (pc) (match_operand:P 0 "register_operand" "r"))
|
||
|
(use (label_ref (match_operand 1 "" "")))]
|
||
|
""
|
||
|
{
|
||
|
if (<MODE>mode == E_HImode)
|
||
|
return "jmp @%0";
|
||
|
if (<MODE>mode == E_SImode)
|
||
|
return "jmp @%S0";
|
||
|
abort ();
|
||
|
}
|
||
|
[(set_attr "length" "2")])
|
||
|
|
||
|
;; This is a define expand, because pointers may be either 16 or 32 bits.
|
||
|
|
||
|
(define_expand "indirect_jump"
|
||
|
[(set (pc) (match_operand 0 "jump_address_operand" ""))]
|
||
|
""
|
||
|
"")
|
||
|
|
||
|
(define_insn "*indirect_jump_<mode>"
|
||
|
[(set (pc) (match_operand:P 0 "jump_address_operand" "Vr"))]
|
||
|
""
|
||
|
{
|
||
|
if (<MODE>mode == E_HImode)
|
||
|
return "jmp @%0";
|
||
|
if (<MODE>mode == E_SImode)
|
||
|
return "jmp @%S0";
|
||
|
abort ();
|
||
|
}
|
||
|
[(set_attr "length" "2")])
|
||
|
|
||
|
;; Call subroutine with no return value.
|
||
|
|
||
|
;; ??? Even though we use HImode here, this works on the H8/300H and H8S.
|
||
|
|
||
|
(define_expand "call"
|
||
|
[(call (match_operand:QI 0 "call_expander_operand" "")
|
||
|
(match_operand 1 "general_operand" ""))]
|
||
|
""
|
||
|
{
|
||
|
if (!register_operand (XEXP (operands[0], 0), Pmode)
|
||
|
&& GET_CODE (XEXP (operands[0], 0)) != SYMBOL_REF)
|
||
|
XEXP (operands[0], 0) = force_reg (Pmode, XEXP (operands[0], 0));
|
||
|
})
|
||
|
|
||
|
(define_insn "call_insn_<mode>"
|
||
|
[(call (mem:QI (match_operand 0 "call_insn_operand" "Cr"))
|
||
|
(match_operand:P 1 "general_operand" "g"))]
|
||
|
""
|
||
|
{
|
||
|
rtx xoperands[1];
|
||
|
xoperands[0] = gen_rtx_MEM (QImode, operands[0]);
|
||
|
gcc_assert (GET_MODE (operands[0]) == Pmode);
|
||
|
if (GET_CODE (XEXP (xoperands[0], 0)) == SYMBOL_REF
|
||
|
&& (SYMBOL_REF_FLAGS (XEXP (xoperands[0], 0)) & SYMBOL_FLAG_FUNCVEC_FUNCTION))
|
||
|
output_asm_insn ("jsr\\t@%0:8", xoperands);
|
||
|
else
|
||
|
output_asm_insn ("jsr\\t%0", xoperands);
|
||
|
return "";
|
||
|
}
|
||
|
[(set_attr "type" "call")
|
||
|
(set (attr "length")
|
||
|
(if_then_else (match_operand:QI 0 "small_call_insn_operand" "")
|
||
|
(const_int 2)
|
||
|
(const_int 4)))])
|
||
|
|
||
|
;; Call subroutine, returning value in operand 0
|
||
|
;; (which must be a hard register).
|
||
|
|
||
|
;; ??? Even though we use HImode here, this works on the H8/300H and H8S.
|
||
|
|
||
|
(define_expand "call_value"
|
||
|
[(set (match_operand 0 "" "")
|
||
|
(call (match_operand:QI 1 "call_expander_operand" "")
|
||
|
(match_operand 2 "general_operand" "")))]
|
||
|
""
|
||
|
{
|
||
|
if (!register_operand (XEXP (operands[1], 0), Pmode)
|
||
|
&& GET_CODE (XEXP (operands[1], 0)) != SYMBOL_REF)
|
||
|
XEXP (operands[1], 0) = force_reg (Pmode, XEXP (operands[1], 0));
|
||
|
})
|
||
|
|
||
|
(define_insn "call_value_insn_<mode>"
|
||
|
[(set (match_operand 0 "" "=r")
|
||
|
(call (mem:QI (match_operand 1 "call_insn_operand" "Cr"))
|
||
|
(match_operand:P 2 "general_operand" "g")))]
|
||
|
""
|
||
|
{
|
||
|
rtx xoperands[2];
|
||
|
gcc_assert (GET_MODE (operands[1]) == Pmode);
|
||
|
xoperands[0] = operands[0];
|
||
|
xoperands[1] = gen_rtx_MEM (QImode, operands[1]);
|
||
|
if (GET_CODE (XEXP (xoperands[1], 0)) == SYMBOL_REF
|
||
|
&& (SYMBOL_REF_FLAGS (XEXP (xoperands[1], 0)) & SYMBOL_FLAG_FUNCVEC_FUNCTION))
|
||
|
output_asm_insn ("jsr\\t@%1:8", xoperands);
|
||
|
else
|
||
|
output_asm_insn ("jsr\\t%1", xoperands);
|
||
|
return "";
|
||
|
}
|
||
|
[(set_attr "type" "call")
|
||
|
(set (attr "length")
|
||
|
(if_then_else (match_operand:QI 0 "small_call_insn_operand" "")
|
||
|
(const_int 2)
|
||
|
(const_int 4)))])
|
||
|
|