/* Generate RTL code for the function epilogue. */
void
-h8300_expand_epilogue (void)
+h8300_expand_epilogue (bool sibcall_p)
{
int regno;
int saved_regs;
/* See if this pop would be the last insn before the return.
If so, use rte/l or rts/l instead of pop or ldm.l. */
if (TARGET_H8300SX
+ && !sibcall_p
&& !frame_pointer_needed
&& frame_size == 0
&& (saved_regs & ((1 << (regno - n_regs + 1)) - 1)) == 0)
/* Pop frame pointer if we had one. */
if (frame_pointer_needed)
{
- if (TARGET_H8300SX)
+ if (TARGET_H8300SX && !sibcall_p)
returned_p = true;
h8300_push_pop (HARD_FRAME_POINTER_REGNUM, 1, true, returned_p);
}
- if (!returned_p)
+ if (!returned_p && !sibcall_p)
emit_jump_insn (ret_rtx);
}
{
return ((bytes + PARM_BOUNDARY / 8 - 1) & (-PARM_BOUNDARY / 8));
}
+
+static bool
+h8300_ok_for_sibcall_p (tree fndecl, tree)
+{
+ /* If either the caller or target are special, then assume sibling
+ calls are not OK. */
+ if (!fndecl
+ || h8300_os_task_function_p (fndecl)
+ || h8300_monitor_function_p (fndecl)
+ || h8300_interrupt_function_p (fndecl)
+ || h8300_saveall_function_p (fndecl)
+ || h8300_os_task_function_p (current_function_decl)
+ || h8300_monitor_function_p (current_function_decl)
+ || h8300_interrupt_function_p (current_function_decl)
+ || h8300_saveall_function_p (current_function_decl))
+ return false;
+
+ return 1;
+}
\f
/* Initialize the GCC target structure. */
#undef TARGET_ATTRIBUTE_TABLE
#undef TARGET_FLAGS_REGNUM
#define TARGET_FLAGS_REGNUM 12
+#undef TARGET_FUNCTION_OK_FOR_SIBCALL
+#define TARGET_FUNCTION_OK_FOR_SIBCALL h8300_ok_for_sibcall_p
+
struct gcc_target targetm = TARGET_INITIALIZER;
(define_insn "call_insn_<mode>"
[(call (mem:QI (match_operand 0 "call_insn_operand" "Cr"))
(match_operand:P 1 "general_operand" "g"))]
- ""
+ "!SIBLING_CALL_P (insn)"
{
rtx xoperands[1];
xoperands[0] = gen_rtx_MEM (QImode, operands[0]);
[(set (match_operand 0 "" "=r")
(call (mem:QI (match_operand 1 "call_insn_operand" "Cr"))
(match_operand:P 2 "general_operand" "g")))]
- ""
+ "!SIBLING_CALL_P (insn)"
{
rtx xoperands[2];
gcc_assert (GET_MODE (operands[1]) == Pmode);
(const_int 2)
(const_int 4)))])
+(define_expand "sibcall"
+ [(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 "sibcall_insn_<mode>"
+ [(call (mem:QI (match_operand 0 "call_insn_operand" "Cr"))
+ (match_operand:P 1 "general_operand" "g"))]
+ "SIBLING_CALL_P (insn)"
+{
+ 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 ("jmp\\t@%0:8", xoperands);
+ else
+ output_asm_insn ("jmp\\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 "sibcall_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 "sibcall_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")))]
+ "SIBLING_CALL_P (insn)"
+{
+ 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 ("jmp\\t@%1:8", xoperands);
+ else
+ output_asm_insn ("jmp\\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)))])
+