diff options
Diffstat (limited to 'gcc/calls.c')
-rw-r--r-- | gcc/calls.c | 221 |
1 files changed, 180 insertions, 41 deletions
diff --git a/gcc/calls.c b/gcc/calls.c index 6415e08b28d..587969fcf01 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -1102,6 +1102,19 @@ store_unaligned_arguments_into_pseudos (struct arg_data *args, int num_actuals) } } +/* Issue an error if CALL_EXPR was flagged as requiring + tall-call optimization. */ + +static void +maybe_complain_about_tail_call (tree call_expr, const char *reason) +{ + gcc_assert (TREE_CODE (call_expr) == CALL_EXPR); + if (!CALL_EXPR_MUST_TAIL_CALL (call_expr)) + return; + + error_at (EXPR_LOCATION (call_expr), "cannot tail-call: %s", reason); +} + /* Fill in ARGS_SIZE and ARGS array based on the parameters found in CALL_EXPR EXP. @@ -1188,6 +1201,7 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, j--; } } + argpos = 0; FOR_EACH_CALL_EXPR_ARG (arg, iter, exp) { tree argtype = TREE_TYPE (arg); @@ -1206,6 +1220,14 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, chkp_find_bound_slots (argtype, slots); } } + else if (CALL_WITH_BOUNDS_P (exp) + && pass_by_reference (NULL, TYPE_MODE (argtype), argtype, + argpos < n_named_args)) + { + if (slots) + BITMAP_FREE (slots); + ptr_arg = j; + } else if (POINTER_BOUNDS_TYPE_P (argtype)) { /* We expect bounds in instrumented calls only. @@ -1249,6 +1271,7 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, else args[j].tree_value = arg; j--; + argpos++; } if (slots) @@ -1333,7 +1356,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, /* We can't use sibcalls if a callee-copied argument is stored in the current function's frame. */ if (!call_from_thunk_p && DECL_P (base) && !TREE_STATIC (base)) - *may_tailcall = false; + { + *may_tailcall = false; + maybe_complain_about_tail_call (exp, + "a callee-copied argument is" + " stored in the current " + " function's frame"); + } args[i].tree_value = build_fold_addr_expr_loc (loc, args[i].tree_value); @@ -1396,6 +1425,9 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, = build_fold_addr_expr_loc (loc, make_tree (type, copy)); type = TREE_TYPE (args[i].tree_value); *may_tailcall = false; + maybe_complain_about_tail_call (exp, + "argument must be passed" + " by copying"); } } @@ -2334,6 +2366,126 @@ avoid_likely_spilled_reg (rtx x) return x; } +/* Helper function for expand_call. + Return false is EXP is not implementable as a sibling call. */ + +static bool +can_implement_as_sibling_call_p (tree exp, + rtx structure_value_addr, + tree funtype, + int reg_parm_stack_space ATTRIBUTE_UNUSED, + tree fndecl, + int flags, + tree addr, + const args_size &args_size) +{ + if (!targetm.have_sibcall_epilogue ()) + { + maybe_complain_about_tail_call + (exp, + "machine description does not have" + " a sibcall_epilogue instruction pattern"); + return false; + } + + /* Doing sibling call optimization needs some work, since + structure_value_addr can be allocated on the stack. + It does not seem worth the effort since few optimizable + sibling calls will return a structure. */ + if (structure_value_addr != NULL_RTX) + { + maybe_complain_about_tail_call (exp, "callee returns a structure"); + return false; + } + +#ifdef REG_PARM_STACK_SPACE + /* If outgoing reg parm stack space changes, we can not do sibcall. */ + if (OUTGOING_REG_PARM_STACK_SPACE (funtype) + != OUTGOING_REG_PARM_STACK_SPACE (TREE_TYPE (current_function_decl)) + || (reg_parm_stack_space != REG_PARM_STACK_SPACE (current_function_decl))) + { + maybe_complain_about_tail_call (exp, + "inconsistent size of stack space" + " allocated for arguments which are" + " passed in registers"); + return false; + } +#endif + + /* Check whether the target is able to optimize the call + into a sibcall. */ + if (!targetm.function_ok_for_sibcall (fndecl, exp)) + { + maybe_complain_about_tail_call (exp, + "target is not able to optimize the" + " call into a sibling call"); + return false; + } + + /* Functions that do not return exactly once may not be sibcall + optimized. */ + if (flags & ECF_RETURNS_TWICE) + { + maybe_complain_about_tail_call (exp, "callee returns twice"); + return false; + } + if (flags & ECF_NORETURN) + { + maybe_complain_about_tail_call (exp, "callee does not return"); + return false; + } + + if (TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (addr)))) + { + maybe_complain_about_tail_call (exp, "volatile function type"); + return false; + } + + /* If the called function is nested in the current one, it might access + some of the caller's arguments, but could clobber them beforehand if + the argument areas are shared. */ + if (fndecl && decl_function_context (fndecl) == current_function_decl) + { + maybe_complain_about_tail_call (exp, "nested function"); + return false; + } + + /* If this function requires more stack slots than the current + function, we cannot change it into a sibling call. + crtl->args.pretend_args_size is not part of the + stack allocated by our caller. */ + if (args_size.constant > (crtl->args.size - crtl->args.pretend_args_size)) + { + maybe_complain_about_tail_call (exp, + "callee required more stack slots" + " than the caller"); + return false; + } + + /* If the callee pops its own arguments, then it must pop exactly + the same number of arguments as the current function. */ + if (targetm.calls.return_pops_args (fndecl, funtype, args_size.constant) + != targetm.calls.return_pops_args (current_function_decl, + TREE_TYPE (current_function_decl), + crtl->args.size)) + { + maybe_complain_about_tail_call (exp, + "inconsistent number of" + " popped arguments"); + return false; + } + + if (!lang_hooks.decls.ok_for_sibcall (fndecl)) + { + maybe_complain_about_tail_call (exp, "frontend does not support" + " sibling call"); + return false; + } + + /* All checks passed. */ + return true; +} + /* Generate all the code for a CALL_EXPR exp and return an rtx for its value. Store the value in TARGET (specified as an rtx) if convenient. @@ -2362,6 +2514,7 @@ expand_call (tree exp, rtx target, int ignore) /* The type of the function being called. */ tree fntype; bool try_tail_call = CALL_EXPR_TAILCALL (exp); + bool must_tail_call = CALL_EXPR_MUST_TAIL_CALL (exp); int pass; /* Register in which non-BLKmode value will be returned, @@ -2729,45 +2882,19 @@ expand_call (tree exp, rtx target, int ignore) || dbg_cnt (tail_call) == false) try_tail_call = 0; + /* If the user has marked the function as requiring tail-call + optimization, attempt it. */ + if (must_tail_call) + try_tail_call = 1; + /* Rest of purposes for tail call optimizations to fail. */ - if (!try_tail_call - || !targetm.have_sibcall_epilogue () - /* Doing sibling call optimization needs some work, since - structure_value_addr can be allocated on the stack. - It does not seem worth the effort since few optimizable - sibling calls will return a structure. */ - || structure_value_addr != NULL_RTX -#ifdef REG_PARM_STACK_SPACE - /* If outgoing reg parm stack space changes, we can not do sibcall. */ - || (OUTGOING_REG_PARM_STACK_SPACE (funtype) - != OUTGOING_REG_PARM_STACK_SPACE (TREE_TYPE (current_function_decl))) - || (reg_parm_stack_space != REG_PARM_STACK_SPACE (current_function_decl)) -#endif - /* Check whether the target is able to optimize the call - into a sibcall. */ - || !targetm.function_ok_for_sibcall (fndecl, exp) - /* Functions that do not return exactly once may not be sibcall - optimized. */ - || (flags & (ECF_RETURNS_TWICE | ECF_NORETURN)) - || TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (addr))) - /* If the called function is nested in the current one, it might access - some of the caller's arguments, but could clobber them beforehand if - the argument areas are shared. */ - || (fndecl && decl_function_context (fndecl) == current_function_decl) - /* If this function requires more stack slots than the current - function, we cannot change it into a sibling call. - crtl->args.pretend_args_size is not part of the - stack allocated by our caller. */ - || args_size.constant > (crtl->args.size - - crtl->args.pretend_args_size) - /* If the callee pops its own arguments, then it must pop exactly - the same number of arguments as the current function. */ - || (targetm.calls.return_pops_args (fndecl, funtype, args_size.constant) - != targetm.calls.return_pops_args (current_function_decl, - TREE_TYPE (current_function_decl), - crtl->args.size)) - || !lang_hooks.decls.ok_for_sibcall (fndecl)) - try_tail_call = 0; + if (try_tail_call) + try_tail_call = can_implement_as_sibling_call_p (exp, + structure_value_addr, + funtype, + reg_parm_stack_space, + fndecl, + flags, addr, args_size); /* Check if caller and callee disagree in promotion of function return value. */ @@ -2797,7 +2924,13 @@ expand_call (tree exp, rtx target, int ignore) && (caller_unsignedp != callee_unsignedp || GET_MODE_BITSIZE (caller_mode) < GET_MODE_BITSIZE (callee_mode))))) - try_tail_call = 0; + { + try_tail_call = 0; + maybe_complain_about_tail_call (exp, + "caller and callee disagree in" + " promotion of function" + " return value"); + } } /* Ensure current function's preferred stack boundary is at least @@ -3695,7 +3828,13 @@ expand_call (tree exp, rtx target, int ignore) crtl->tail_call_emit = true; } else - emit_insn (normal_call_insns); + { + emit_insn (normal_call_insns); + if (try_tail_call) + /* Ideally we'd emit a message for all of the ways that it could + have failed. */ + maybe_complain_about_tail_call (exp, "tail call production failed"); + } currently_expanding_call--; |