diff --git a/gcc/config/h8300/h8300-protos.h b/gcc/config/h8300/h8300-protos.h index 744337d6667..3d344018ff2 100644 --- a/gcc/config/h8300/h8300-protos.h +++ b/gcc/config/h8300/h8300-protos.h @@ -94,7 +94,7 @@ extern int h8300_tiny_data_p (tree); extern int h8300_can_use_return_insn_p (void); extern void h8300_expand_prologue (void); -extern void h8300_expand_epilogue (void); +extern void h8300_expand_epilogue (bool); extern int h8300_current_function_interrupt_function_p (void); extern int h8300_current_function_monitor_function_p (void); extern int h8300_initial_elimination_offset (int, int); diff --git a/gcc/config/h8300/h8300.c b/gcc/config/h8300/h8300.c index 8ccacecba79..5f7251ab78d 100644 --- a/gcc/config/h8300/h8300.c +++ b/gcc/config/h8300/h8300.c @@ -874,7 +874,7 @@ h8300_can_use_return_insn_p (void) /* Generate RTL code for the function epilogue. */ void -h8300_expand_epilogue (void) +h8300_expand_epilogue (bool sibcall_p) { int regno; int saved_regs; @@ -919,6 +919,7 @@ h8300_expand_epilogue (void) /* 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) @@ -931,12 +932,12 @@ h8300_expand_epilogue (void) /* 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); } @@ -5533,6 +5534,25 @@ h8300_push_rounding (poly_int64 bytes) { 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; +} /* Initialize the GCC target structure. */ #undef TARGET_ATTRIBUTE_TABLE @@ -5628,4 +5648,7 @@ h8300_push_rounding (poly_int64 bytes) #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; diff --git a/gcc/config/h8300/jumpcall.md b/gcc/config/h8300/jumpcall.md index 3e59fee58bd..b59639992a3 100644 --- a/gcc/config/h8300/jumpcall.md +++ b/gcc/config/h8300/jumpcall.md @@ -290,7 +290,7 @@ (define_insn "call_insn_" [(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]); @@ -328,7 +328,7 @@ [(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); @@ -347,3 +347,73 @@ (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_" + [(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_" + [(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)))]) + diff --git a/gcc/config/h8300/proepi.md b/gcc/config/h8300/proepi.md index 44d59682496..ab58d02fc7f 100644 --- a/gcc/config/h8300/proepi.md +++ b/gcc/config/h8300/proepi.md @@ -98,7 +98,7 @@ [(return)] "" { - h8300_expand_epilogue (); + h8300_expand_epilogue (false); DONE; }) @@ -121,3 +121,11 @@ gcc_unreachable (); } [(set_attr "length" "20")]) + +(define_expand "sibcall_epilogue" + [(const_int 0)] + "" + { + h8300_expand_epilogue (true); + DONE; + })