diff options
Diffstat (limited to 'gcc/calls.c')
-rw-r--r-- | gcc/calls.c | 159 |
1 files changed, 124 insertions, 35 deletions
diff --git a/gcc/calls.c b/gcc/calls.c index 18df59a92c2..244aa4cb73e 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -133,7 +133,8 @@ static int compute_argument_block_size (int, struct args_size *, int); static void initialize_argument_information (int, struct arg_data *, struct args_size *, int, tree, tree, CUMULATIVE_ARGS *, int, - rtx *, int *, int *, int *); + rtx *, int *, int *, int *, + bool); static void compute_argument_addresses (struct arg_data *, rtx, int); static rtx rtx_for_function_call (tree, tree); static void load_register_parameters (struct arg_data *, int, rtx *, int, @@ -148,6 +149,7 @@ static int check_sibcall_argument_overlap (rtx, struct arg_data *, int); static int combine_pending_stack_adjustment_and_call (int, struct args_size *, int); static tree fix_unsafe_tree (tree); +static bool shift_returned_value (tree, rtx *); #ifdef REG_PARM_STACK_SPACE static rtx save_fixed_argument_area (int, rtx, int *, int *); @@ -289,7 +291,7 @@ prepare_call_address (rtx funexp, tree fndecl, rtx *call_fusage, /* Get possible static chain value for nested function in C. */ static_chain_value = lookup_static_chain (fndecl); - /* Make a valid memory address and copy constants thru pseudo-regs, + /* Make a valid memory address and copy constants through pseudo-regs, but not for a constant address if -fno-function-cse. */ if (GET_CODE (funexp) != SYMBOL_REF) /* If we are using registers for parameters, force the @@ -744,6 +746,28 @@ flags_from_decl_or_type (tree exp) return flags; } +/* Detect flags from a CALL_EXPR. */ + +int +call_expr_flags (tree t) +{ + int flags; + tree decl = get_callee_fndecl (t); + + if (decl) + flags = flags_from_decl_or_type (decl); + else + { + t = TREE_TYPE (TREE_OPERAND (t, 0)); + if (t && TREE_CODE (t) == POINTER_TYPE) + flags = flags_from_decl_or_type (TREE_TYPE (t)); + else + flags = 0; + } + + return flags; +} + /* Precompute all register parameters as described by ARGS, storing values into fields within the ARGS array. @@ -1000,7 +1024,10 @@ store_unaligned_arguments_into_pseudos (struct arg_data *args, int num_actuals) and may be modified by this routine. OLD_PENDING_ADJ, MUST_PREALLOCATE and FLAGS are pointers to integer - flags which may may be modified by this routine. */ + flags which may may be modified by this routine. + + CALL_FROM_THUNK_P is true if this call is the jump from a thunk to + the thunked-to function. */ static void initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, @@ -1011,7 +1038,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, CUMULATIVE_ARGS *args_so_far, int reg_parm_stack_space, rtx *old_stack_level, int *old_pending_adj, - int *must_preallocate, int *ecf_flags) + int *must_preallocate, int *ecf_flags, + bool call_from_thunk_p) { /* 1 if scanning parms front to back, -1 if scanning back to front. */ int inc; @@ -1084,7 +1112,7 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, { /* If we're compiling a thunk, pass through invisible references instead of making a copy. */ - if (current_function_is_thunk + if (call_from_thunk_p #ifdef FUNCTION_ARG_CALLEE_COPIES || (FUNCTION_ARG_CALLEE_COPIES (*args_so_far, TYPE_MODE (type), type, argpos < n_named_args) @@ -1578,11 +1606,7 @@ load_register_parameters (struct arg_data *args, int num_actuals, { int i, j; -#ifdef LOAD_ARGS_REVERSED - for (i = num_actuals - 1; i >= 0; i--) -#else for (i = 0; i < num_actuals; i++) -#endif { rtx reg = ((flags & ECF_SIBCALL) ? args[i].tail_call_reg : args[i].reg); @@ -2000,6 +2024,34 @@ fix_unsafe_tree (tree t) return t; } + +/* If function value *VALUE was returned at the most significant end of a + register, shift it towards the least significant end and convert it to + TYPE's mode. Return true and update *VALUE if some action was needed. + + TYPE is the type of the function's return value, which is known not + to have mode BLKmode. */ + +static bool +shift_returned_value (tree type, rtx *value) +{ + if (targetm.calls.return_in_msb (type)) + { + HOST_WIDE_INT shift; + + shift = (GET_MODE_BITSIZE (GET_MODE (*value)) + - BITS_PER_UNIT * int_size_in_bytes (type)); + if (shift > 0) + { + *value = expand_binop (GET_MODE (*value), lshr_optab, *value, + GEN_INT (shift), 0, 1, OPTAB_WIDEN); + *value = convert_to_mode (TYPE_MODE (type), *value, 0); + return true; + } + } + return false; +} + /* Generate all the code for a function call and return an rtx for its value. Store the value in TARGET (specified as an rtx) if convenient. @@ -2101,6 +2153,7 @@ expand_call (tree exp, rtx target, int ignore) #endif int initial_highest_arg_in_use = highest_outgoing_arg_in_use; + rtx temp_target = 0; char *initial_stack_usage_map = stack_usage_map; int old_stack_allocated; @@ -2361,15 +2414,16 @@ expand_call (tree exp, rtx target, int ignore) (If no anonymous args follow, the result of list_length is actually one too large. This is harmless.) - If PRETEND_OUTGOING_VARARGS_NAMED is set and STRICT_ARGUMENT_NAMING is - zero, this machine will be able to place unnamed args that were - passed in registers into the stack. So treat all args as named. - This allows the insns emitting for a specific argument list to be - independent of the function declaration. + If targetm.calls.pretend_outgoing_varargs_named() returns + nonzero, and STRICT_ARGUMENT_NAMING is zero, this machine will be + able to place unnamed args that were passed in registers into the + stack. So treat all args as named. This allows the insns + emitting for a specific argument list to be independent of the + function declaration. - If PRETEND_OUTGOING_VARARGS_NAMED is not set, we do not have any - reliable way to pass unnamed args in registers, so we must force - them into memory. */ + If targetm.calls.pretend_outgoing_varargs_named() returns zero, + we do not have any reliable way to pass unnamed args in + registers, so we must force them into memory. */ if ((targetm.calls.strict_argument_naming (&args_so_far) || ! targetm.calls.pretend_outgoing_varargs_named (&args_so_far)) @@ -2394,7 +2448,8 @@ expand_call (tree exp, rtx target, int ignore) n_named_args, actparms, fndecl, &args_so_far, reg_parm_stack_space, &old_stack_level, &old_pending_adj, - &must_preallocate, &flags); + &must_preallocate, &flags, + CALL_FROM_THUNK_P (exp)); if (args_size.var) { @@ -2433,11 +2488,15 @@ expand_call (tree exp, rtx target, int ignore) finished with regular parsing. Which means that some of the machinery we use to generate tail-calls is no longer in place. This is most often true of sjlj-exceptions, which we couldn't - tail-call to anyway. */ + tail-call to anyway. + If current_nesting_level () == 0, we're being called after + the function body has been expanded. This can happen when + setting up trampolines in expand_function_end. */ if (currently_expanding_call++ != 0 || !flag_optimize_sibling_calls || !rtx_equal_function_value_matters + || current_nesting_level () == 0 || any_pending_cleanups () || args_size.var) try_tail_call = try_tail_recursion = 0; @@ -3097,22 +3156,33 @@ expand_call (tree exp, rtx target, int ignore) mark_reg_pointer (temp, TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp)))); - /* Construct an "equal form" for the value which mentions all the - arguments in order as well as the function name. */ - for (i = 0; i < num_actuals; i++) - note = gen_rtx_EXPR_LIST (VOIDmode, - args[i].initial_value, note); - note = gen_rtx_EXPR_LIST (VOIDmode, funexp, note); - end_sequence (); - - if (flags & ECF_PURE) - note = gen_rtx_EXPR_LIST (VOIDmode, + if (flag_unsafe_math_optimizations + && fndecl + && DECL_BUILT_IN (fndecl) + && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_SQRT + || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_SQRTF + || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_SQRTL)) + note = gen_rtx_fmt_e (SQRT, + GET_MODE (temp), + args[0].initial_value); + else + { + /* Construct an "equal form" for the value which + mentions all the arguments in order as well as + the function name. */ + for (i = 0; i < num_actuals; i++) + note = gen_rtx_EXPR_LIST (VOIDmode, + args[i].initial_value, note); + note = gen_rtx_EXPR_LIST (VOIDmode, funexp, note); + + if (flags & ECF_PURE) + note = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_USE (VOIDmode, gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode))), note); - + } emit_libcall_block (insns, temp, valreg, note); valreg = temp; @@ -3215,7 +3285,11 @@ expand_call (tree exp, rtx target, int ignore) The Irix 6 ABI has examples of this. */ else if (GET_CODE (valreg) == PARALLEL) { - if (target == 0) + /* Second condition is added because "target" is freed at the + the end of "pass0" for -O2 when call is made to + expand_end_target_temps (). Its "in_use" flag has been set + to false, so allocate a new temp. */ + if (target == 0 || (pass == 1 && target == temp_target)) { /* This will only be assigned once, so it can be readonly. */ tree nt = build_qualified_type (TREE_TYPE (exp), @@ -3223,6 +3297,7 @@ expand_call (tree exp, rtx target, int ignore) | TYPE_QUAL_CONST)); target = assign_temp (nt, 0, 1, 1); + temp_target = target; preserve_temp_slots (target); } @@ -3259,7 +3334,12 @@ expand_call (tree exp, rtx target, int ignore) sibcall_failure = 1; } else - target = copy_to_reg (valreg); + { + if (shift_returned_value (TREE_TYPE (exp), &valreg)) + sibcall_failure = 1; + + target = copy_to_reg (valreg); + } if (targetm.calls.promote_function_return(funtype)) { @@ -4559,9 +4639,18 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags, { /* PUSH_ROUNDING has no effect on us, because emit_push_insn for BLKmode is careful to avoid it. */ - excess = (arg->locate.size.constant - - int_size_in_bytes (TREE_TYPE (pval)) - + partial * UNITS_PER_WORD); + if (reg && GET_CODE (reg) == PARALLEL) + { + /* Use the size of the elt to compute excess. */ + rtx elt = XEXP (XVECEXP (reg, 0, 0), 0); + excess = (arg->locate.size.constant + - int_size_in_bytes (TREE_TYPE (pval)) + + partial * GET_MODE_SIZE (GET_MODE (elt))); + } + else + excess = (arg->locate.size.constant + - int_size_in_bytes (TREE_TYPE (pval)) + + partial * UNITS_PER_WORD); size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)), NULL_RTX, TYPE_MODE (sizetype), 0); } |