| /* Convert function calls to rtl insns, for GNU C compiler. |
| Copyright (C) 1989-2021 Free Software Foundation, Inc. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #define INCLUDE_STRING |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "backend.h" |
| #include "target.h" |
| #include "rtl.h" |
| #include "tree.h" |
| #include "gimple.h" |
| #include "predict.h" |
| #include "memmodel.h" |
| #include "tm_p.h" |
| #include "stringpool.h" |
| #include "expmed.h" |
| #include "optabs.h" |
| #include "emit-rtl.h" |
| #include "cgraph.h" |
| #include "diagnostic-core.h" |
| #include "fold-const.h" |
| #include "stor-layout.h" |
| #include "varasm.h" |
| #include "internal-fn.h" |
| #include "dojump.h" |
| #include "explow.h" |
| #include "calls.h" |
| #include "expr.h" |
| #include "output.h" |
| #include "langhooks.h" |
| #include "except.h" |
| #include "dbgcnt.h" |
| #include "rtl-iter.h" |
| #include "tree-vrp.h" |
| #include "tree-ssanames.h" |
| #include "tree-ssa-strlen.h" |
| #include "intl.h" |
| #include "stringpool.h" |
| #include "hash-map.h" |
| #include "hash-traits.h" |
| #include "attribs.h" |
| #include "builtins.h" |
| #include "gimple-fold.h" |
| #include "attr-fnspec.h" |
| #include "value-query.h" |
| |
| #include "tree-pretty-print.h" |
| |
| /* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */ |
| #define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT) |
| |
| /* Data structure and subroutines used within expand_call. */ |
| |
| struct arg_data |
| { |
| /* Tree node for this argument. */ |
| tree tree_value; |
| /* Mode for value; TYPE_MODE unless promoted. */ |
| machine_mode mode; |
| /* Current RTL value for argument, or 0 if it isn't precomputed. */ |
| rtx value; |
| /* Initially-compute RTL value for argument; only for const functions. */ |
| rtx initial_value; |
| /* Register to pass this argument in, 0 if passed on stack, or an |
| PARALLEL if the arg is to be copied into multiple non-contiguous |
| registers. */ |
| rtx reg; |
| /* Register to pass this argument in when generating tail call sequence. |
| This is not the same register as for normal calls on machines with |
| register windows. */ |
| rtx tail_call_reg; |
| /* If REG is a PARALLEL, this is a copy of VALUE pulled into the correct |
| form for emit_group_move. */ |
| rtx parallel_value; |
| /* If REG was promoted from the actual mode of the argument expression, |
| indicates whether the promotion is sign- or zero-extended. */ |
| int unsignedp; |
| /* Number of bytes to put in registers. 0 means put the whole arg |
| in registers. Also 0 if not passed in registers. */ |
| int partial; |
| /* Nonzero if argument must be passed on stack. |
| Note that some arguments may be passed on the stack |
| even though pass_on_stack is zero, just because FUNCTION_ARG says so. |
| pass_on_stack identifies arguments that *cannot* go in registers. */ |
| int pass_on_stack; |
| /* Some fields packaged up for locate_and_pad_parm. */ |
| struct locate_and_pad_arg_data locate; |
| /* Location on the stack at which parameter should be stored. The store |
| has already been done if STACK == VALUE. */ |
| rtx stack; |
| /* Location on the stack of the start of this argument slot. This can |
| differ from STACK if this arg pads downward. This location is known |
| to be aligned to TARGET_FUNCTION_ARG_BOUNDARY. */ |
| rtx stack_slot; |
| /* Place that this stack area has been saved, if needed. */ |
| rtx save_area; |
| /* If an argument's alignment does not permit direct copying into registers, |
| copy in smaller-sized pieces into pseudos. These are stored in a |
| block pointed to by this field. The next field says how many |
| word-sized pseudos we made. */ |
| rtx *aligned_regs; |
| int n_aligned_regs; |
| }; |
| |
| /* A vector of one char per byte of stack space. A byte if nonzero if |
| the corresponding stack location has been used. |
| This vector is used to prevent a function call within an argument from |
| clobbering any stack already set up. */ |
| static char *stack_usage_map; |
| |
| /* Size of STACK_USAGE_MAP. */ |
| static unsigned int highest_outgoing_arg_in_use; |
| |
| /* Assume that any stack location at this byte index is used, |
| without checking the contents of stack_usage_map. */ |
| static unsigned HOST_WIDE_INT stack_usage_watermark = HOST_WIDE_INT_M1U; |
| |
| /* A bitmap of virtual-incoming stack space. Bit is set if the corresponding |
| stack location's tail call argument has been already stored into the stack. |
| This bitmap is used to prevent sibling call optimization if function tries |
| to use parent's incoming argument slots when they have been already |
| overwritten with tail call arguments. */ |
| static sbitmap stored_args_map; |
| |
| /* Assume that any virtual-incoming location at this byte index has been |
| stored, without checking the contents of stored_args_map. */ |
| static unsigned HOST_WIDE_INT stored_args_watermark; |
| |
| /* stack_arg_under_construction is nonzero when an argument may be |
| initialized with a constructor call (including a C function that |
| returns a BLKmode struct) and expand_call must take special action |
| to make sure the object being constructed does not overlap the |
| argument list for the constructor call. */ |
| static int stack_arg_under_construction; |
| |
| static void precompute_register_parameters (int, struct arg_data *, int *); |
| static int store_one_arg (struct arg_data *, rtx, int, int, int); |
| static void store_unaligned_arguments_into_pseudos (struct arg_data *, int); |
| static int finalize_must_preallocate (int, int, struct arg_data *, |
| struct args_size *); |
| static void precompute_arguments (int, struct arg_data *); |
| 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, |
| int, int *); |
| static int special_function_p (const_tree, int); |
| static int check_sibcall_argument_overlap_1 (rtx); |
| static int check_sibcall_argument_overlap (rtx_insn *, struct arg_data *, int); |
| |
| static tree split_complex_types (tree); |
| |
| #ifdef REG_PARM_STACK_SPACE |
| static rtx save_fixed_argument_area (int, rtx, int *, int *); |
| static void restore_fixed_argument_area (rtx, rtx, int, int); |
| #endif |
| |
| /* Return true if bytes [LOWER_BOUND, UPPER_BOUND) of the outgoing |
| stack region might already be in use. */ |
| |
| static bool |
| stack_region_maybe_used_p (poly_uint64 lower_bound, poly_uint64 upper_bound, |
| unsigned int reg_parm_stack_space) |
| { |
| unsigned HOST_WIDE_INT const_lower, const_upper; |
| const_lower = constant_lower_bound (lower_bound); |
| if (!upper_bound.is_constant (&const_upper)) |
| const_upper = HOST_WIDE_INT_M1U; |
| |
| if (const_upper > stack_usage_watermark) |
| return true; |
| |
| /* Don't worry about things in the fixed argument area; |
| it has already been saved. */ |
| const_lower = MAX (const_lower, reg_parm_stack_space); |
| const_upper = MIN (const_upper, highest_outgoing_arg_in_use); |
| for (unsigned HOST_WIDE_INT i = const_lower; i < const_upper; ++i) |
| if (stack_usage_map[i]) |
| return true; |
| return false; |
| } |
| |
| /* Record that bytes [LOWER_BOUND, UPPER_BOUND) of the outgoing |
| stack region are now in use. */ |
| |
| static void |
| mark_stack_region_used (poly_uint64 lower_bound, poly_uint64 upper_bound) |
| { |
| unsigned HOST_WIDE_INT const_lower, const_upper; |
| const_lower = constant_lower_bound (lower_bound); |
| if (upper_bound.is_constant (&const_upper)) |
| for (unsigned HOST_WIDE_INT i = const_lower; i < const_upper; ++i) |
| stack_usage_map[i] = 1; |
| else |
| stack_usage_watermark = MIN (stack_usage_watermark, const_lower); |
| } |
| |
| /* Force FUNEXP into a form suitable for the address of a CALL, |
| and return that as an rtx. Also load the static chain register |
| if FNDECL is a nested function. |
| |
| CALL_FUSAGE points to a variable holding the prospective |
| CALL_INSN_FUNCTION_USAGE information. */ |
| |
| rtx |
| prepare_call_address (tree fndecl_or_type, rtx funexp, rtx static_chain_value, |
| rtx *call_fusage, int reg_parm_seen, int flags) |
| { |
| /* 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 it's an indirect call by descriptor, generate code to perform |
| runtime identification of the pointer and load the descriptor. */ |
| if ((flags & ECF_BY_DESCRIPTOR) && !flag_trampolines) |
| { |
| const int bit_val = targetm.calls.custom_function_descriptors; |
| rtx call_lab = gen_label_rtx (); |
| |
| gcc_assert (fndecl_or_type && TYPE_P (fndecl_or_type)); |
| fndecl_or_type |
| = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, NULL_TREE, |
| fndecl_or_type); |
| DECL_STATIC_CHAIN (fndecl_or_type) = 1; |
| rtx chain = targetm.calls.static_chain (fndecl_or_type, false); |
| |
| if (GET_MODE (funexp) != Pmode) |
| funexp = convert_memory_address (Pmode, funexp); |
| |
| /* Avoid long live ranges around function calls. */ |
| funexp = copy_to_mode_reg (Pmode, funexp); |
| |
| if (REG_P (chain)) |
| emit_insn (gen_rtx_CLOBBER (VOIDmode, chain)); |
| |
| /* Emit the runtime identification pattern. */ |
| rtx mask = gen_rtx_AND (Pmode, funexp, GEN_INT (bit_val)); |
| emit_cmp_and_jump_insns (mask, const0_rtx, EQ, NULL_RTX, Pmode, 1, |
| call_lab); |
| |
| /* Statically predict the branch to very likely taken. */ |
| rtx_insn *insn = get_last_insn (); |
| if (JUMP_P (insn)) |
| predict_insn_def (insn, PRED_BUILTIN_EXPECT, TAKEN); |
| |
| /* Load the descriptor. */ |
| rtx mem = gen_rtx_MEM (ptr_mode, |
| plus_constant (Pmode, funexp, - bit_val)); |
| MEM_NOTRAP_P (mem) = 1; |
| mem = convert_memory_address (Pmode, mem); |
| emit_move_insn (chain, mem); |
| |
| mem = gen_rtx_MEM (ptr_mode, |
| plus_constant (Pmode, funexp, |
| POINTER_SIZE / BITS_PER_UNIT |
| - bit_val)); |
| MEM_NOTRAP_P (mem) = 1; |
| mem = convert_memory_address (Pmode, mem); |
| emit_move_insn (funexp, mem); |
| |
| emit_label (call_lab); |
| |
| if (REG_P (chain)) |
| { |
| use_reg (call_fusage, chain); |
| STATIC_CHAIN_REG_P (chain) = 1; |
| } |
| |
| /* Make sure we're not going to be overwritten below. */ |
| gcc_assert (!static_chain_value); |
| } |
| |
| /* If we are using registers for parameters, force the |
| function address into a register now. */ |
| funexp = ((reg_parm_seen |
| && targetm.small_register_classes_for_mode_p (FUNCTION_MODE)) |
| ? force_not_mem (memory_address (FUNCTION_MODE, funexp)) |
| : memory_address (FUNCTION_MODE, funexp)); |
| } |
| else |
| { |
| /* funexp could be a SYMBOL_REF represents a function pointer which is |
| of ptr_mode. In this case, it should be converted into address mode |
| to be a valid address for memory rtx pattern. See PR 64971. */ |
| if (GET_MODE (funexp) != Pmode) |
| funexp = convert_memory_address (Pmode, funexp); |
| |
| if (!(flags & ECF_SIBCALL)) |
| { |
| if (!NO_FUNCTION_CSE && optimize && ! flag_no_function_cse) |
| funexp = force_reg (Pmode, funexp); |
| } |
| } |
| |
| if (static_chain_value != 0 |
| && (TREE_CODE (fndecl_or_type) != FUNCTION_DECL |
| || DECL_STATIC_CHAIN (fndecl_or_type))) |
| { |
| rtx chain; |
| |
| chain = targetm.calls.static_chain (fndecl_or_type, false); |
| static_chain_value = convert_memory_address (Pmode, static_chain_value); |
| |
| emit_move_insn (chain, static_chain_value); |
| if (REG_P (chain)) |
| { |
| use_reg (call_fusage, chain); |
| STATIC_CHAIN_REG_P (chain) = 1; |
| } |
| } |
| |
| return funexp; |
| } |
| |
| /* Generate instructions to call function FUNEXP, |
| and optionally pop the results. |
| The CALL_INSN is the first insn generated. |
| |
| FNDECL is the declaration node of the function. This is given to the |
| hook TARGET_RETURN_POPS_ARGS to determine whether this function pops |
| its own args. |
| |
| FUNTYPE is the data type of the function. This is given to the hook |
| TARGET_RETURN_POPS_ARGS to determine whether this function pops its |
| own args. We used to allow an identifier for library functions, but |
| that doesn't work when the return type is an aggregate type and the |
| calling convention says that the pointer to this aggregate is to be |
| popped by the callee. |
| |
| STACK_SIZE is the number of bytes of arguments on the stack, |
| ROUNDED_STACK_SIZE is that number rounded up to |
| PREFERRED_STACK_BOUNDARY; zero if the size is variable. This is |
| both to put into the call insn and to generate explicit popping |
| code if necessary. |
| |
| STRUCT_VALUE_SIZE is the number of bytes wanted in a structure value. |
| It is zero if this call doesn't want a structure value. |
| |
| NEXT_ARG_REG is the rtx that results from executing |
| targetm.calls.function_arg (&args_so_far, |
| function_arg_info::end_marker ()); |
| just after all the args have had their registers assigned. |
| This could be whatever you like, but normally it is the first |
| arg-register beyond those used for args in this call, |
| or 0 if all the arg-registers are used in this call. |
| It is passed on to `gen_call' so you can put this info in the call insn. |
| |
| VALREG is a hard register in which a value is returned, |
| or 0 if the call does not return a value. |
| |
| OLD_INHIBIT_DEFER_POP is the value that `inhibit_defer_pop' had before |
| the args to this call were processed. |
| We restore `inhibit_defer_pop' to that value. |
| |
| CALL_FUSAGE is either empty or an EXPR_LIST of USE expressions that |
| denote registers used by the called function. */ |
| |
| static void |
| emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNUSED, |
| tree funtype ATTRIBUTE_UNUSED, |
| poly_int64 stack_size ATTRIBUTE_UNUSED, |
| poly_int64 rounded_stack_size, |
| poly_int64 struct_value_size ATTRIBUTE_UNUSED, |
| rtx next_arg_reg ATTRIBUTE_UNUSED, rtx valreg, |
| int old_inhibit_defer_pop, rtx call_fusage, int ecf_flags, |
| cumulative_args_t args_so_far ATTRIBUTE_UNUSED) |
| { |
| rtx rounded_stack_size_rtx = gen_int_mode (rounded_stack_size, Pmode); |
| rtx call, funmem, pat; |
| int already_popped = 0; |
| poly_int64 n_popped = 0; |
| |
| /* Sibling call patterns never pop arguments (no sibcall(_value)_pop |
| patterns exist). Any popping that the callee does on return will |
| be from our caller's frame rather than ours. */ |
| if (!(ecf_flags & ECF_SIBCALL)) |
| { |
| n_popped += targetm.calls.return_pops_args (fndecl, funtype, stack_size); |
| |
| #ifdef CALL_POPS_ARGS |
| n_popped += CALL_POPS_ARGS (*get_cumulative_args (args_so_far)); |
| #endif |
| } |
| |
| /* Ensure address is valid. SYMBOL_REF is already valid, so no need, |
| and we don't want to load it into a register as an optimization, |
| because prepare_call_address already did it if it should be done. */ |
| if (GET_CODE (funexp) != SYMBOL_REF) |
| funexp = memory_address (FUNCTION_MODE, funexp); |
| |
| funmem = gen_rtx_MEM (FUNCTION_MODE, funexp); |
| if (fndecl && TREE_CODE (fndecl) == FUNCTION_DECL) |
| { |
| tree t = fndecl; |
| |
| /* Although a built-in FUNCTION_DECL and its non-__builtin |
| counterpart compare equal and get a shared mem_attrs, they |
| produce different dump output in compare-debug compilations, |
| if an entry gets garbage collected in one compilation, then |
| adds a different (but equivalent) entry, while the other |
| doesn't run the garbage collector at the same spot and then |
| shares the mem_attr with the equivalent entry. */ |
| if (DECL_BUILT_IN_CLASS (t) == BUILT_IN_NORMAL) |
| { |
| tree t2 = builtin_decl_explicit (DECL_FUNCTION_CODE (t)); |
| if (t2) |
| t = t2; |
| } |
| |
| set_mem_expr (funmem, t); |
| } |
| else if (fntree) |
| set_mem_expr (funmem, build_simple_mem_ref (CALL_EXPR_FN (fntree))); |
| |
| if (ecf_flags & ECF_SIBCALL) |
| { |
| if (valreg) |
| pat = targetm.gen_sibcall_value (valreg, funmem, |
| rounded_stack_size_rtx, |
| next_arg_reg, NULL_RTX); |
| else |
| pat = targetm.gen_sibcall (funmem, rounded_stack_size_rtx, |
| next_arg_reg, |
| gen_int_mode (struct_value_size, Pmode)); |
| } |
| /* If the target has "call" or "call_value" insns, then prefer them |
| if no arguments are actually popped. If the target does not have |
| "call" or "call_value" insns, then we must use the popping versions |
| even if the call has no arguments to pop. */ |
| else if (maybe_ne (n_popped, 0) |
| || !(valreg |
| ? targetm.have_call_value () |
| : targetm.have_call ())) |
| { |
| rtx n_pop = gen_int_mode (n_popped, Pmode); |
| |
| /* If this subroutine pops its own args, record that in the call insn |
| if possible, for the sake of frame pointer elimination. */ |
| |
| if (valreg) |
| pat = targetm.gen_call_value_pop (valreg, funmem, |
| rounded_stack_size_rtx, |
| next_arg_reg, n_pop); |
| else |
| pat = targetm.gen_call_pop (funmem, rounded_stack_size_rtx, |
| next_arg_reg, n_pop); |
| |
| already_popped = 1; |
| } |
| else |
| { |
| if (valreg) |
| pat = targetm.gen_call_value (valreg, funmem, rounded_stack_size_rtx, |
| next_arg_reg, NULL_RTX); |
| else |
| pat = targetm.gen_call (funmem, rounded_stack_size_rtx, next_arg_reg, |
| gen_int_mode (struct_value_size, Pmode)); |
| } |
| emit_insn (pat); |
| |
| /* Find the call we just emitted. */ |
| rtx_call_insn *call_insn = last_call_insn (); |
| |
| /* Some target create a fresh MEM instead of reusing the one provided |
| above. Set its MEM_EXPR. */ |
| call = get_call_rtx_from (call_insn); |
| if (call |
| && MEM_EXPR (XEXP (call, 0)) == NULL_TREE |
| && MEM_EXPR (funmem) != NULL_TREE) |
| set_mem_expr (XEXP (call, 0), MEM_EXPR (funmem)); |
| |
| /* Put the register usage information there. */ |
| add_function_usage_to (call_insn, call_fusage); |
| |
| /* If this is a const call, then set the insn's unchanging bit. */ |
| if (ecf_flags & ECF_CONST) |
| RTL_CONST_CALL_P (call_insn) = 1; |
| |
| /* If this is a pure call, then set the insn's unchanging bit. */ |
| if (ecf_flags & ECF_PURE) |
| RTL_PURE_CALL_P (call_insn) = 1; |
| |
| /* If this is a const call, then set the insn's unchanging bit. */ |
| if (ecf_flags & ECF_LOOPING_CONST_OR_PURE) |
| RTL_LOOPING_CONST_OR_PURE_CALL_P (call_insn) = 1; |
| |
| /* Create a nothrow REG_EH_REGION note, if needed. */ |
| make_reg_eh_region_note (call_insn, ecf_flags, 0); |
| |
| if (ecf_flags & ECF_NORETURN) |
| add_reg_note (call_insn, REG_NORETURN, const0_rtx); |
| |
| if (ecf_flags & ECF_RETURNS_TWICE) |
| { |
| add_reg_note (call_insn, REG_SETJMP, const0_rtx); |
| cfun->calls_setjmp = 1; |
| } |
| |
| SIBLING_CALL_P (call_insn) = ((ecf_flags & ECF_SIBCALL) != 0); |
| |
| /* Restore this now, so that we do defer pops for this call's args |
| if the context of the call as a whole permits. */ |
| inhibit_defer_pop = old_inhibit_defer_pop; |
| |
| if (maybe_ne (n_popped, 0)) |
| { |
| if (!already_popped) |
| CALL_INSN_FUNCTION_USAGE (call_insn) |
| = gen_rtx_EXPR_LIST (VOIDmode, |
| gen_rtx_CLOBBER (VOIDmode, stack_pointer_rtx), |
| CALL_INSN_FUNCTION_USAGE (call_insn)); |
| rounded_stack_size -= n_popped; |
| rounded_stack_size_rtx = gen_int_mode (rounded_stack_size, Pmode); |
| stack_pointer_delta -= n_popped; |
| |
| add_args_size_note (call_insn, stack_pointer_delta); |
| |
| /* If popup is needed, stack realign must use DRAP */ |
| if (SUPPORTS_STACK_ALIGNMENT) |
| crtl->need_drap = true; |
| } |
| /* For noreturn calls when not accumulating outgoing args force |
| REG_ARGS_SIZE note to prevent crossjumping of calls with different |
| args sizes. */ |
| else if (!ACCUMULATE_OUTGOING_ARGS && (ecf_flags & ECF_NORETURN) != 0) |
| add_args_size_note (call_insn, stack_pointer_delta); |
| |
| if (!ACCUMULATE_OUTGOING_ARGS) |
| { |
| /* If returning from the subroutine does not automatically pop the args, |
| we need an instruction to pop them sooner or later. |
| Perhaps do it now; perhaps just record how much space to pop later. |
| |
| If returning from the subroutine does pop the args, indicate that the |
| stack pointer will be changed. */ |
| |
| if (maybe_ne (rounded_stack_size, 0)) |
| { |
| if (ecf_flags & ECF_NORETURN) |
| /* Just pretend we did the pop. */ |
| stack_pointer_delta -= rounded_stack_size; |
| else if (flag_defer_pop && inhibit_defer_pop == 0 |
| && ! (ecf_flags & (ECF_CONST | ECF_PURE))) |
| pending_stack_adjust += rounded_stack_size; |
| else |
| adjust_stack (rounded_stack_size_rtx); |
| } |
| } |
| /* When we accumulate outgoing args, we must avoid any stack manipulations. |
| Restore the stack pointer to its original value now. Usually |
| ACCUMULATE_OUTGOING_ARGS targets don't get here, but there are exceptions. |
| On i386 ACCUMULATE_OUTGOING_ARGS can be enabled on demand, and |
| popping variants of functions exist as well. |
| |
| ??? We may optimize similar to defer_pop above, but it is |
| probably not worthwhile. |
| |
| ??? It will be worthwhile to enable combine_stack_adjustments even for |
| such machines. */ |
| else if (maybe_ne (n_popped, 0)) |
| anti_adjust_stack (gen_int_mode (n_popped, Pmode)); |
| } |
| |
| /* Determine if the function identified by FNDECL is one with |
| special properties we wish to know about. Modify FLAGS accordingly. |
| |
| For example, if the function might return more than one time (setjmp), then |
| set ECF_RETURNS_TWICE. |
| |
| Set ECF_MAY_BE_ALLOCA for any memory allocation function that might allocate |
| space from the stack such as alloca. */ |
| |
| static int |
| special_function_p (const_tree fndecl, int flags) |
| { |
| tree name_decl = DECL_NAME (fndecl); |
| |
| if (maybe_special_function_p (fndecl) |
| && IDENTIFIER_LENGTH (name_decl) <= 11) |
| { |
| const char *name = IDENTIFIER_POINTER (name_decl); |
| const char *tname = name; |
| |
| /* We assume that alloca will always be called by name. It |
| makes no sense to pass it as a pointer-to-function to |
| anything that does not understand its behavior. */ |
| if (IDENTIFIER_LENGTH (name_decl) == 6 |
| && name[0] == 'a' |
| && ! strcmp (name, "alloca")) |
| flags |= ECF_MAY_BE_ALLOCA; |
| |
| /* Disregard prefix _ or __. */ |
| if (name[0] == '_') |
| { |
| if (name[1] == '_') |
| tname += 2; |
| else |
| tname += 1; |
| } |
| |
| /* ECF_RETURNS_TWICE is safe even for -ffreestanding. */ |
| if (! strcmp (tname, "setjmp") |
| || ! strcmp (tname, "sigsetjmp") |
| || ! strcmp (name, "savectx") |
| || ! strcmp (name, "vfork") |
| || ! strcmp (name, "getcontext")) |
| flags |= ECF_RETURNS_TWICE; |
| } |
| |
| if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL |
| && ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (fndecl))) |
| flags |= ECF_MAY_BE_ALLOCA; |
| |
| return flags; |
| } |
| |
| /* Return fnspec for DECL. */ |
| |
| static attr_fnspec |
| decl_fnspec (tree fndecl) |
| { |
| tree attr; |
| tree type = TREE_TYPE (fndecl); |
| if (type) |
| { |
| attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type)); |
| if (attr) |
| { |
| return TREE_VALUE (TREE_VALUE (attr)); |
| } |
| } |
| if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)) |
| return builtin_fnspec (fndecl); |
| return ""; |
| } |
| |
| /* Similar to special_function_p; return a set of ERF_ flags for the |
| function FNDECL. */ |
| static int |
| decl_return_flags (tree fndecl) |
| { |
| attr_fnspec fnspec = decl_fnspec (fndecl); |
| |
| unsigned int arg; |
| if (fnspec.returns_arg (&arg)) |
| return ERF_RETURNS_ARG | arg; |
| |
| if (fnspec.returns_noalias_p ()) |
| return ERF_NOALIAS; |
| return 0; |
| } |
| |
| /* Return nonzero when FNDECL represents a call to setjmp. */ |
| |
| int |
| setjmp_call_p (const_tree fndecl) |
| { |
| if (DECL_IS_RETURNS_TWICE (fndecl)) |
| return ECF_RETURNS_TWICE; |
| return special_function_p (fndecl, 0) & ECF_RETURNS_TWICE; |
| } |
| |
| |
| /* Return true if STMT may be an alloca call. */ |
| |
| bool |
| gimple_maybe_alloca_call_p (const gimple *stmt) |
| { |
| tree fndecl; |
| |
| if (!is_gimple_call (stmt)) |
| return false; |
| |
| fndecl = gimple_call_fndecl (stmt); |
| if (fndecl && (special_function_p (fndecl, 0) & ECF_MAY_BE_ALLOCA)) |
| return true; |
| |
| return false; |
| } |
| |
| /* Return true if STMT is a builtin alloca call. */ |
| |
| bool |
| gimple_alloca_call_p (const gimple *stmt) |
| { |
| tree fndecl; |
| |
| if (!is_gimple_call (stmt)) |
| return false; |
| |
| fndecl = gimple_call_fndecl (stmt); |
| if (fndecl && fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)) |
| switch (DECL_FUNCTION_CODE (fndecl)) |
| { |
| CASE_BUILT_IN_ALLOCA: |
| return gimple_call_num_args (stmt) > 0; |
| default: |
| break; |
| } |
| |
| return false; |
| } |
| |
| /* Return true when exp contains a builtin alloca call. */ |
| |
| bool |
| alloca_call_p (const_tree exp) |
| { |
| tree fndecl; |
| if (TREE_CODE (exp) == CALL_EXPR |
| && (fndecl = get_callee_fndecl (exp)) |
| && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL) |
| switch (DECL_FUNCTION_CODE (fndecl)) |
| { |
| CASE_BUILT_IN_ALLOCA: |
| return true; |
| default: |
| break; |
| } |
| |
| return false; |
| } |
| |
| /* Return TRUE if FNDECL is either a TM builtin or a TM cloned |
| function. Return FALSE otherwise. */ |
| |
| static bool |
| is_tm_builtin (const_tree fndecl) |
| { |
| if (fndecl == NULL) |
| return false; |
| |
| if (decl_is_tm_clone (fndecl)) |
| return true; |
| |
| if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL) |
| { |
| switch (DECL_FUNCTION_CODE (fndecl)) |
| { |
| case BUILT_IN_TM_COMMIT: |
| case BUILT_IN_TM_COMMIT_EH: |
| case BUILT_IN_TM_ABORT: |
| case BUILT_IN_TM_IRREVOCABLE: |
| case BUILT_IN_TM_GETTMCLONE_IRR: |
| case BUILT_IN_TM_MEMCPY: |
| case BUILT_IN_TM_MEMMOVE: |
| case BUILT_IN_TM_MEMSET: |
| CASE_BUILT_IN_TM_STORE (1): |
| CASE_BUILT_IN_TM_STORE (2): |
| CASE_BUILT_IN_TM_STORE (4): |
| CASE_BUILT_IN_TM_STORE (8): |
| CASE_BUILT_IN_TM_STORE (FLOAT): |
| CASE_BUILT_IN_TM_STORE (DOUBLE): |
| CASE_BUILT_IN_TM_STORE (LDOUBLE): |
| CASE_BUILT_IN_TM_STORE (M64): |
| CASE_BUILT_IN_TM_STORE (M128): |
| CASE_BUILT_IN_TM_STORE (M256): |
| CASE_BUILT_IN_TM_LOAD (1): |
| CASE_BUILT_IN_TM_LOAD (2): |
| CASE_BUILT_IN_TM_LOAD (4): |
| CASE_BUILT_IN_TM_LOAD (8): |
| CASE_BUILT_IN_TM_LOAD (FLOAT): |
| CASE_BUILT_IN_TM_LOAD (DOUBLE): |
| CASE_BUILT_IN_TM_LOAD (LDOUBLE): |
| CASE_BUILT_IN_TM_LOAD (M64): |
| CASE_BUILT_IN_TM_LOAD (M128): |
| CASE_BUILT_IN_TM_LOAD (M256): |
| case BUILT_IN_TM_LOG: |
| case BUILT_IN_TM_LOG_1: |
| case BUILT_IN_TM_LOG_2: |
| case BUILT_IN_TM_LOG_4: |
| case BUILT_IN_TM_LOG_8: |
| case BUILT_IN_TM_LOG_FLOAT: |
| case BUILT_IN_TM_LOG_DOUBLE: |
| case BUILT_IN_TM_LOG_LDOUBLE: |
| case BUILT_IN_TM_LOG_M64: |
| case BUILT_IN_TM_LOG_M128: |
| case BUILT_IN_TM_LOG_M256: |
| return true; |
| default: |
| break; |
| } |
| } |
| return false; |
| } |
| |
| /* Detect flags (function attributes) from the function decl or type node. */ |
| |
| int |
| flags_from_decl_or_type (const_tree exp) |
| { |
| int flags = 0; |
| |
| if (DECL_P (exp)) |
| { |
| /* The function exp may have the `malloc' attribute. */ |
| if (DECL_IS_MALLOC (exp)) |
| flags |= ECF_MALLOC; |
| |
| /* The function exp may have the `returns_twice' attribute. */ |
| if (DECL_IS_RETURNS_TWICE (exp)) |
| flags |= ECF_RETURNS_TWICE; |
| |
| /* Process the pure and const attributes. */ |
| if (TREE_READONLY (exp)) |
| flags |= ECF_CONST; |
| if (DECL_PURE_P (exp)) |
| flags |= ECF_PURE; |
| if (DECL_LOOPING_CONST_OR_PURE_P (exp)) |
| flags |= ECF_LOOPING_CONST_OR_PURE; |
| |
| if (DECL_IS_NOVOPS (exp)) |
| flags |= ECF_NOVOPS; |
| if (lookup_attribute ("leaf", DECL_ATTRIBUTES (exp))) |
| flags |= ECF_LEAF; |
| if (lookup_attribute ("cold", DECL_ATTRIBUTES (exp))) |
| flags |= ECF_COLD; |
| |
| if (TREE_NOTHROW (exp)) |
| flags |= ECF_NOTHROW; |
| |
| if (flag_tm) |
| { |
| if (is_tm_builtin (exp)) |
| flags |= ECF_TM_BUILTIN; |
| else if ((flags & (ECF_CONST|ECF_NOVOPS)) != 0 |
| || lookup_attribute ("transaction_pure", |
| TYPE_ATTRIBUTES (TREE_TYPE (exp)))) |
| flags |= ECF_TM_PURE; |
| } |
| |
| flags = special_function_p (exp, flags); |
| } |
| else if (TYPE_P (exp)) |
| { |
| if (TYPE_READONLY (exp)) |
| flags |= ECF_CONST; |
| |
| if (flag_tm |
| && ((flags & ECF_CONST) != 0 |
| || lookup_attribute ("transaction_pure", TYPE_ATTRIBUTES (exp)))) |
| flags |= ECF_TM_PURE; |
| } |
| else |
| gcc_unreachable (); |
| |
| if (TREE_THIS_VOLATILE (exp)) |
| { |
| flags |= ECF_NORETURN; |
| if (flags & (ECF_CONST|ECF_PURE)) |
| flags |= ECF_LOOPING_CONST_OR_PURE; |
| } |
| |
| return flags; |
| } |
| |
| /* Detect flags from a CALL_EXPR. */ |
| |
| int |
| call_expr_flags (const_tree t) |
| { |
| int flags; |
| tree decl = get_callee_fndecl (t); |
| |
| if (decl) |
| flags = flags_from_decl_or_type (decl); |
| else if (CALL_EXPR_FN (t) == NULL_TREE) |
| flags = internal_fn_flags (CALL_EXPR_IFN (t)); |
| else |
| { |
| tree type = TREE_TYPE (CALL_EXPR_FN (t)); |
| if (type && TREE_CODE (type) == POINTER_TYPE) |
| flags = flags_from_decl_or_type (TREE_TYPE (type)); |
| else |
| flags = 0; |
| if (CALL_EXPR_BY_DESCRIPTOR (t)) |
| flags |= ECF_BY_DESCRIPTOR; |
| } |
| |
| return flags; |
| } |
| |
| /* Return true if ARG should be passed by invisible reference. */ |
| |
| bool |
| pass_by_reference (CUMULATIVE_ARGS *ca, function_arg_info arg) |
| { |
| if (tree type = arg.type) |
| { |
| /* If this type contains non-trivial constructors, then it is |
| forbidden for the middle-end to create any new copies. */ |
| if (TREE_ADDRESSABLE (type)) |
| return true; |
| |
| /* GCC post 3.4 passes *all* variable sized types by reference. */ |
| if (!TYPE_SIZE (type) || !poly_int_tree_p (TYPE_SIZE (type))) |
| return true; |
| |
| /* If a record type should be passed the same as its first (and only) |
| member, use the type and mode of that member. */ |
| if (TREE_CODE (type) == RECORD_TYPE && TYPE_TRANSPARENT_AGGR (type)) |
| { |
| arg.type = TREE_TYPE (first_field (type)); |
| arg.mode = TYPE_MODE (arg.type); |
| } |
| } |
| |
| return targetm.calls.pass_by_reference (pack_cumulative_args (ca), arg); |
| } |
| |
| /* Return true if TYPE should be passed by reference when passed to |
| the "..." arguments of a function. */ |
| |
| bool |
| pass_va_arg_by_reference (tree type) |
| { |
| return pass_by_reference (NULL, function_arg_info (type, /*named=*/false)); |
| } |
| |
| /* Decide whether ARG, which occurs in the state described by CA, |
| should be passed by reference. Return true if so and update |
| ARG accordingly. */ |
| |
| bool |
| apply_pass_by_reference_rules (CUMULATIVE_ARGS *ca, function_arg_info &arg) |
| { |
| if (pass_by_reference (ca, arg)) |
| { |
| arg.type = build_pointer_type (arg.type); |
| arg.mode = TYPE_MODE (arg.type); |
| arg.pass_by_reference = true; |
| return true; |
| } |
| return false; |
| } |
| |
| /* Return true if ARG, which is passed by reference, should be callee |
| copied instead of caller copied. */ |
| |
| bool |
| reference_callee_copied (CUMULATIVE_ARGS *ca, const function_arg_info &arg) |
| { |
| if (arg.type && TREE_ADDRESSABLE (arg.type)) |
| return false; |
| return targetm.calls.callee_copies (pack_cumulative_args (ca), arg); |
| } |
| |
| |
| /* Precompute all register parameters as described by ARGS, storing values |
| into fields within the ARGS array. |
| |
| NUM_ACTUALS indicates the total number elements in the ARGS array. |
| |
| Set REG_PARM_SEEN if we encounter a register parameter. */ |
| |
| static void |
| precompute_register_parameters (int num_actuals, struct arg_data *args, |
| int *reg_parm_seen) |
| { |
| int i; |
| |
| *reg_parm_seen = 0; |
| |
| for (i = 0; i < num_actuals; i++) |
| if (args[i].reg != 0 && ! args[i].pass_on_stack) |
| { |
| *reg_parm_seen = 1; |
| |
| if (args[i].value == 0) |
| { |
| push_temp_slots (); |
| args[i].value = expand_normal (args[i].tree_value); |
| preserve_temp_slots (args[i].value); |
| pop_temp_slots (); |
| } |
| |
| /* If we are to promote the function arg to a wider mode, |
| do it now. */ |
| |
| if (args[i].mode != TYPE_MODE (TREE_TYPE (args[i].tree_value))) |
| args[i].value |
| = convert_modes (args[i].mode, |
| TYPE_MODE (TREE_TYPE (args[i].tree_value)), |
| args[i].value, args[i].unsignedp); |
| |
| /* If the value is a non-legitimate constant, force it into a |
| pseudo now. TLS symbols sometimes need a call to resolve. */ |
| if (CONSTANT_P (args[i].value) |
| && (!targetm.legitimate_constant_p (args[i].mode, args[i].value) |
| || targetm.precompute_tls_p (args[i].mode, args[i].value))) |
| args[i].value = force_reg (args[i].mode, args[i].value); |
| |
| /* If we're going to have to load the value by parts, pull the |
| parts into pseudos. The part extraction process can involve |
| non-trivial computation. */ |
| if (GET_CODE (args[i].reg) == PARALLEL) |
| { |
| tree type = TREE_TYPE (args[i].tree_value); |
| args[i].parallel_value |
| = emit_group_load_into_temps (args[i].reg, args[i].value, |
| type, int_size_in_bytes (type)); |
| } |
| |
| /* If the value is expensive, and we are inside an appropriately |
| short loop, put the value into a pseudo and then put the pseudo |
| into the hard reg. |
| |
| For small register classes, also do this if this call uses |
| register parameters. This is to avoid reload conflicts while |
| loading the parameters registers. */ |
| |
| else if ((! (REG_P (args[i].value) |
| || (GET_CODE (args[i].value) == SUBREG |
| && REG_P (SUBREG_REG (args[i].value))))) |
| && args[i].mode != BLKmode |
| && (set_src_cost (args[i].value, args[i].mode, |
| optimize_insn_for_speed_p ()) |
| > COSTS_N_INSNS (1)) |
| && ((*reg_parm_seen |
| && targetm.small_register_classes_for_mode_p (args[i].mode)) |
| || optimize)) |
| args[i].value = copy_to_mode_reg (args[i].mode, args[i].value); |
| } |
| } |
| |
| #ifdef REG_PARM_STACK_SPACE |
| |
| /* The argument list is the property of the called routine and it |
| may clobber it. If the fixed area has been used for previous |
| parameters, we must save and restore it. */ |
| |
| static rtx |
| save_fixed_argument_area (int reg_parm_stack_space, rtx argblock, int *low_to_save, int *high_to_save) |
| { |
| unsigned int low; |
| unsigned int high; |
| |
| /* Compute the boundary of the area that needs to be saved, if any. */ |
| high = reg_parm_stack_space; |
| if (ARGS_GROW_DOWNWARD) |
| high += 1; |
| |
| if (high > highest_outgoing_arg_in_use) |
| high = highest_outgoing_arg_in_use; |
| |
| for (low = 0; low < high; low++) |
| if (stack_usage_map[low] != 0 || low >= stack_usage_watermark) |
| { |
| int num_to_save; |
| machine_mode save_mode; |
| int delta; |
| rtx addr; |
| rtx stack_area; |
| rtx save_area; |
| |
| while (stack_usage_map[--high] == 0) |
| ; |
| |
| *low_to_save = low; |
| *high_to_save = high; |
| |
| num_to_save = high - low + 1; |
| |
| /* If we don't have the required alignment, must do this |
| in BLKmode. */ |
| scalar_int_mode imode; |
| if (int_mode_for_size (num_to_save * BITS_PER_UNIT, 1).exists (&imode) |
| && (low & (MIN (GET_MODE_SIZE (imode), |
| BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1)) == 0) |
| save_mode = imode; |
| else |
| save_mode = BLKmode; |
| |
| if (ARGS_GROW_DOWNWARD) |
| delta = -high; |
| else |
| delta = low; |
| |
| addr = plus_constant (Pmode, argblock, delta); |
| stack_area = gen_rtx_MEM (save_mode, memory_address (save_mode, addr)); |
| |
| set_mem_align (stack_area, PARM_BOUNDARY); |
| if (save_mode == BLKmode) |
| { |
| save_area = assign_stack_temp (BLKmode, num_to_save); |
| emit_block_move (validize_mem (save_area), stack_area, |
| GEN_INT (num_to_save), BLOCK_OP_CALL_PARM); |
| } |
| else |
| { |
| save_area = gen_reg_rtx (save_mode); |
| emit_move_insn (save_area, stack_area); |
| } |
| |
| return save_area; |
| } |
| |
| return NULL_RTX; |
| } |
| |
| static void |
| restore_fixed_argument_area (rtx save_area, rtx argblock, int high_to_save, int low_to_save) |
| { |
| machine_mode save_mode = GET_MODE (save_area); |
| int delta; |
| rtx addr, stack_area; |
| |
| if (ARGS_GROW_DOWNWARD) |
| delta = -high_to_save; |
| else |
| delta = low_to_save; |
| |
| addr = plus_constant (Pmode, argblock, delta); |
| stack_area = gen_rtx_MEM (save_mode, memory_address (save_mode, addr)); |
| set_mem_align (stack_area, PARM_BOUNDARY); |
| |
| if (save_mode != BLKmode) |
| emit_move_insn (stack_area, save_area); |
| else |
| emit_block_move (stack_area, validize_mem (save_area), |
| GEN_INT (high_to_save - low_to_save + 1), |
| BLOCK_OP_CALL_PARM); |
| } |
| #endif /* REG_PARM_STACK_SPACE */ |
| |
| /* If any elements in ARGS refer to parameters that are to be passed in |
| registers, but not in memory, and whose alignment does not permit a |
| direct copy into registers. Copy the values into a group of pseudos |
| which we will later copy into the appropriate hard registers. |
| |
| Pseudos for each unaligned argument will be stored into the array |
| args[argnum].aligned_regs. The caller is responsible for deallocating |
| the aligned_regs array if it is nonzero. */ |
| |
| static void |
| store_unaligned_arguments_into_pseudos (struct arg_data *args, int num_actuals) |
| { |
| int i, j; |
| |
| for (i = 0; i < num_actuals; i++) |
| if (args[i].reg != 0 && ! args[i].pass_on_stack |
| && GET_CODE (args[i].reg) != PARALLEL |
| && args[i].mode == BLKmode |
| && MEM_P (args[i].value) |
| && (MEM_ALIGN (args[i].value) |
| < (unsigned int) MIN (BIGGEST_ALIGNMENT, BITS_PER_WORD))) |
| { |
| int bytes = int_size_in_bytes (TREE_TYPE (args[i].tree_value)); |
| int endian_correction = 0; |
| |
| if (args[i].partial) |
| { |
| gcc_assert (args[i].partial % UNITS_PER_WORD == 0); |
| args[i].n_aligned_regs = args[i].partial / UNITS_PER_WORD; |
| } |
| else |
| { |
| args[i].n_aligned_regs |
| = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD; |
| } |
| |
| args[i].aligned_regs = XNEWVEC (rtx, args[i].n_aligned_regs); |
| |
| /* Structures smaller than a word are normally aligned to the |
| least significant byte. On a BYTES_BIG_ENDIAN machine, |
| this means we must skip the empty high order bytes when |
| calculating the bit offset. */ |
| if (bytes < UNITS_PER_WORD |
| #ifdef BLOCK_REG_PADDING |
| && (BLOCK_REG_PADDING (args[i].mode, |
| TREE_TYPE (args[i].tree_value), 1) |
| == PAD_DOWNWARD) |
| #else |
| && BYTES_BIG_ENDIAN |
| #endif |
| ) |
| endian_correction = BITS_PER_WORD - bytes * BITS_PER_UNIT; |
| |
| for (j = 0; j < args[i].n_aligned_regs; j++) |
| { |
| rtx reg = gen_reg_rtx (word_mode); |
| rtx word = operand_subword_force (args[i].value, j, BLKmode); |
| int bitsize = MIN (bytes * BITS_PER_UNIT, BITS_PER_WORD); |
| |
| args[i].aligned_regs[j] = reg; |
| word = extract_bit_field (word, bitsize, 0, 1, NULL_RTX, |
| word_mode, word_mode, false, NULL); |
| |
| /* There is no need to restrict this code to loading items |
| in TYPE_ALIGN sized hunks. The bitfield instructions can |
| load up entire word sized registers efficiently. |
| |
| ??? This may not be needed anymore. |
| We use to emit a clobber here but that doesn't let later |
| passes optimize the instructions we emit. By storing 0 into |
| the register later passes know the first AND to zero out the |
| bitfield being set in the register is unnecessary. The store |
| of 0 will be deleted as will at least the first AND. */ |
| |
| emit_move_insn (reg, const0_rtx); |
| |
| bytes -= bitsize / BITS_PER_UNIT; |
| store_bit_field (reg, bitsize, endian_correction, 0, 0, |
| word_mode, word, false); |
| } |
| } |
| } |
| |
| /* The limit set by -Walloc-larger-than=. */ |
| static GTY(()) tree alloc_object_size_limit; |
| |
| /* Initialize ALLOC_OBJECT_SIZE_LIMIT based on the -Walloc-size-larger-than= |
| setting if the option is specified, or to the maximum object size if it |
| is not. Return the initialized value. */ |
| |
| static tree |
| alloc_max_size (void) |
| { |
| if (alloc_object_size_limit) |
| return alloc_object_size_limit; |
| |
| HOST_WIDE_INT limit = warn_alloc_size_limit; |
| if (limit == HOST_WIDE_INT_MAX) |
| limit = tree_to_shwi (TYPE_MAX_VALUE (ptrdiff_type_node)); |
| |
| alloc_object_size_limit = build_int_cst (size_type_node, limit); |
| |
| return alloc_object_size_limit; |
| } |
| |
| /* Return true when EXP's range can be determined and set RANGE[] to it |
| after adjusting it if necessary to make EXP a represents a valid size |
| of object, or a valid size argument to an allocation function declared |
| with attribute alloc_size (whose argument may be signed), or to a string |
| manipulation function like memset. |
| When ALLOW_ZERO is set in FLAGS, allow returning a range of [0, 0] for |
| a size in an anti-range [1, N] where N > PTRDIFF_MAX. A zero range is |
| a (nearly) invalid argument to allocation functions like malloc but it |
| is a valid argument to functions like memset. |
| When USE_LARGEST is set in FLAGS set RANGE to the largest valid subrange |
| in a multi-range, otherwise to the smallest valid subrange. */ |
| |
| bool |
| get_size_range (range_query *query, tree exp, gimple *stmt, tree range[2], |
| int flags /* = 0 */) |
| { |
| if (!exp) |
| return false; |
| |
| if (tree_fits_uhwi_p (exp)) |
| { |
| /* EXP is a constant. */ |
| range[0] = range[1] = exp; |
| return true; |
| } |
| |
| tree exptype = TREE_TYPE (exp); |
| bool integral = INTEGRAL_TYPE_P (exptype); |
| |
| wide_int min, max; |
| enum value_range_kind range_type; |
| |
| if (integral) |
| { |
| value_range vr; |
| if (query && query->range_of_expr (vr, exp, stmt)) |
| { |
| if (vr.undefined_p ()) |
| vr.set_varying (TREE_TYPE (exp)); |
| range_type = vr.kind (); |
| min = wi::to_wide (vr.min ()); |
| max = wi::to_wide (vr.max ()); |
| } |
| else |
| range_type = determine_value_range (exp, &min, &max); |
| } |
| else |
| range_type = VR_VARYING; |
| |
| if (range_type == VR_VARYING) |
| { |
| if (integral) |
| { |
| /* Use the full range of the type of the expression when |
| no value range information is available. */ |
| range[0] = TYPE_MIN_VALUE (exptype); |
| range[1] = TYPE_MAX_VALUE (exptype); |
| return true; |
| } |
| |
| range[0] = NULL_TREE; |
| range[1] = NULL_TREE; |
| return false; |
| } |
| |
| unsigned expprec = TYPE_PRECISION (exptype); |
| |
| bool signed_p = !TYPE_UNSIGNED (exptype); |
| |
| if (range_type == VR_ANTI_RANGE) |
| { |
| if (signed_p) |
| { |
| if (wi::les_p (max, 0)) |
| { |
| /* EXP is not in a strictly negative range. That means |
| it must be in some (not necessarily strictly) positive |
| range which includes zero. Since in signed to unsigned |
| conversions negative values end up converted to large |
| positive values, and otherwise they are not valid sizes, |
| the resulting range is in both cases [0, TYPE_MAX]. */ |
| min = wi::zero (expprec); |
| max = wi::to_wide (TYPE_MAX_VALUE (exptype)); |
| } |
| else if (wi::les_p (min - 1, 0)) |
| { |
| /* EXP is not in a negative-positive range. That means EXP |
| is either negative, or greater than max. Since negative |
| sizes are invalid make the range [MAX + 1, TYPE_MAX]. */ |
| min = max + 1; |
| max = wi::to_wide (TYPE_MAX_VALUE (exptype)); |
| } |
| else |
| { |
| max = min - 1; |
| min = wi::zero (expprec); |
| } |
| } |
| else |
| { |
| wide_int maxsize = wi::to_wide (max_object_size ()); |
| min = wide_int::from (min, maxsize.get_precision (), UNSIGNED); |
| max = wide_int::from (max, maxsize.get_precision (), UNSIGNED); |
| if (wi::eq_p (0, min - 1)) |
| { |
| /* EXP is unsigned and not in the range [1, MAX]. That means |
| it's either zero or greater than MAX. Even though 0 would |
| normally be detected by -Walloc-zero, unless ALLOW_ZERO |
| is set, set the range to [MAX, TYPE_MAX] so that when MAX |
| is greater than the limit the whole range is diagnosed. */ |
| wide_int maxsize = wi::to_wide (max_object_size ()); |
| if (flags & SR_ALLOW_ZERO) |
| { |
| if (wi::leu_p (maxsize, max + 1) |
| || !(flags & SR_USE_LARGEST)) |
| min = max = wi::zero (expprec); |
| else |
| { |
| min = max + 1; |
| max = wi::to_wide (TYPE_MAX_VALUE (exptype)); |
| } |
| } |
| else |
| { |
| min = max + 1; |
| max = wi::to_wide (TYPE_MAX_VALUE (exptype)); |
| } |
| } |
| else if ((flags & SR_USE_LARGEST) |
| && wi::ltu_p (max + 1, maxsize)) |
| { |
| /* When USE_LARGEST is set and the larger of the two subranges |
| is a valid size, use it... */ |
| min = max + 1; |
| max = maxsize; |
| } |
| else |
| { |
| /* ...otherwise use the smaller subrange. */ |
| max = min - 1; |
| min = wi::zero (expprec); |
| } |
| } |
| } |
| |
| range[0] = wide_int_to_tree (exptype, min); |
| range[1] = wide_int_to_tree (exptype, max); |
| |
| return true; |
| } |
| |
| bool |
| get_size_range (tree exp, tree range[2], int flags /* = 0 */) |
| { |
| return get_size_range (/*query=*/NULL, exp, /*stmt=*/NULL, range, flags); |
| } |
| |
| /* Diagnose a call EXP to function FN decorated with attribute alloc_size |
| whose argument numbers given by IDX with values given by ARGS exceed |
| the maximum object size or cause an unsigned oveflow (wrapping) when |
| multiplied. FN is null when EXP is a call via a function pointer. |
| When ARGS[0] is null the function does nothing. ARGS[1] may be null |
| for functions like malloc, and non-null for those like calloc that |
| are decorated with a two-argument attribute alloc_size. */ |
| |
| void |
| maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2]) |
| { |
| /* The range each of the (up to) two arguments is known to be in. */ |
| tree argrange[2][2] = { { NULL_TREE, NULL_TREE }, { NULL_TREE, NULL_TREE } }; |
| |
| /* Maximum object size set by -Walloc-size-larger-than= or SIZE_MAX / 2. */ |
| tree maxobjsize = alloc_max_size (); |
| |
| location_t loc = EXPR_LOCATION (exp); |
| |
| tree fntype = fn ? TREE_TYPE (fn) : TREE_TYPE (TREE_TYPE (exp)); |
| bool warned = false; |
| |
| /* Validate each argument individually. */ |
| for (unsigned i = 0; i != 2 && args[i]; ++i) |
| { |
| if (TREE_CODE (args[i]) == INTEGER_CST) |
| { |
| argrange[i][0] = args[i]; |
| argrange[i][1] = args[i]; |
| |
| if (tree_int_cst_lt (args[i], integer_zero_node)) |
| { |
| warned = warning_at (loc, OPT_Walloc_size_larger_than_, |
| "%Kargument %i value %qE is negative", |
| exp, idx[i] + 1, args[i]); |
| } |
| else if (integer_zerop (args[i])) |
| { |
| /* Avoid issuing -Walloc-zero for allocation functions other |
| than __builtin_alloca that are declared with attribute |
| returns_nonnull because there's no portability risk. This |
| avoids warning for such calls to libiberty's xmalloc and |
| friends. |
| Also avoid issuing the warning for calls to function named |
| "alloca". */ |
| if (fn && fndecl_built_in_p (fn, BUILT_IN_ALLOCA) |
| ? IDENTIFIER_LENGTH (DECL_NAME (fn)) != 6 |
| : !lookup_attribute ("returns_nonnull", |
| TYPE_ATTRIBUTES (fntype))) |
| warned = warning_at (loc, OPT_Walloc_zero, |
| "%Kargument %i value is zero", |
| exp, idx[i] + 1); |
| } |
| else if (tree_int_cst_lt (maxobjsize, args[i])) |
| { |
| /* G++ emits calls to ::operator new[](SIZE_MAX) in C++98 |
| mode and with -fno-exceptions as a way to indicate array |
| size overflow. There's no good way to detect C++98 here |
| so avoid diagnosing these calls for all C++ modes. */ |
| if (i == 0 |
| && fn |
| && !args[1] |
| && lang_GNU_CXX () |
| && DECL_IS_OPERATOR_NEW_P (fn) |
| && integer_all_onesp (args[i])) |
| continue; |
| |
| warned = warning_at (loc, OPT_Walloc_size_larger_than_, |
| "%Kargument %i value %qE exceeds " |
| "maximum object size %E", |
| exp, idx[i] + 1, args[i], maxobjsize); |
| } |
| } |
| else if (TREE_CODE (args[i]) == SSA_NAME |
| && get_size_range (args[i], argrange[i])) |
| { |
| /* Verify that the argument's range is not negative (including |
| upper bound of zero). */ |
| if (tree_int_cst_lt (argrange[i][0], integer_zero_node) |
| && tree_int_cst_le (argrange[i][1], integer_zero_node)) |
| { |
| warned = warning_at (loc, OPT_Walloc_size_larger_than_, |
| "%Kargument %i range [%E, %E] is negative", |
| exp, idx[i] + 1, |
| argrange[i][0], argrange[i][1]); |
| } |
| else if (tree_int_cst_lt (maxobjsize, argrange[i][0])) |
| { |
| warned = warning_at (loc, OPT_Walloc_size_larger_than_, |
| "%Kargument %i range [%E, %E] exceeds " |
| "maximum object size %E", |
| exp, idx[i] + 1, |
| argrange[i][0], argrange[i][1], |
| maxobjsize); |
| } |
| } |
| } |
| |
| if (!argrange[0]) |
| return; |
| |
| /* For a two-argument alloc_size, validate the product of the two |
| arguments if both of their values or ranges are known. */ |
| if (!warned && tree_fits_uhwi_p (argrange[0][0]) |
| && argrange[1][0] && tree_fits_uhwi_p (argrange[1][0]) |
| && !integer_onep (argrange[0][0]) |
| && !integer_onep (argrange[1][0])) |
| { |
| /* Check for overflow in the product of a function decorated with |
| attribute alloc_size (X, Y). */ |
| unsigned szprec = TYPE_PRECISION (size_type_node); |
| wide_int x = wi::to_wide (argrange[0][0], szprec); |
| wide_int y = wi::to_wide (argrange[1][0], szprec); |
| |
| wi::overflow_type vflow; |
| wide_int prod = wi::umul (x, y, &vflow); |
| |
| if (vflow) |
| warned = warning_at (loc, OPT_Walloc_size_larger_than_, |
| "%Kproduct %<%E * %E%> of arguments %i and %i " |
| "exceeds %<SIZE_MAX%>", |
| exp, argrange[0][0], argrange[1][0], |
| idx[0] + 1, idx[1] + 1); |
| else if (wi::ltu_p (wi::to_wide (maxobjsize, szprec), prod)) |
| warned = warning_at (loc, OPT_Walloc_size_larger_than_, |
| "%Kproduct %<%E * %E%> of arguments %i and %i " |
| "exceeds maximum object size %E", |
| exp, argrange[0][0], argrange[1][0], |
| idx[0] + 1, idx[1] + 1, |
| maxobjsize); |
| |
| if (warned) |
| { |
| /* Print the full range of each of the two arguments to make |
| it clear when it is, in fact, in a range and not constant. */ |
| if (argrange[0][0] != argrange [0][1]) |
| inform (loc, "argument %i in the range [%E, %E]", |
| idx[0] + 1, argrange[0][0], argrange[0][1]); |
| if (argrange[1][0] != argrange [1][1]) |
| inform (loc, "argument %i in the range [%E, %E]", |
| idx[1] + 1, argrange[1][0], argrange[1][1]); |
| } |
| } |
| |
| if (warned && fn) |
| { |
| location_t fnloc = DECL_SOURCE_LOCATION (fn); |
| |
| if (DECL_IS_UNDECLARED_BUILTIN (fn)) |
| inform (loc, |
| "in a call to built-in allocation function %qD", fn); |
| else |
| inform (fnloc, |
| "in a call to allocation function %qD declared here", fn); |
| } |
| } |
| |
| /* If EXPR refers to a character array or pointer declared attribute |
| nonstring return a decl for that array or pointer and set *REF to |
| the referenced enclosing object or pointer. Otherwise returns |
| null. */ |
| |
| tree |
| get_attr_nonstring_decl (tree expr, tree *ref) |
| { |
| tree decl = expr; |
| tree var = NULL_TREE; |
| if (TREE_CODE (decl) == SSA_NAME) |
| { |
| gimple *def = SSA_NAME_DEF_STMT (decl); |
| |
| if (is_gimple_assign (def)) |
| { |
| tree_code code = gimple_assign_rhs_code (def); |
| if (code == ADDR_EXPR |
| || code == COMPONENT_REF |
| || code == VAR_DECL) |
| decl = gimple_assign_rhs1 (def); |
| } |
| else |
| var = SSA_NAME_VAR (decl); |
| } |
| |
| if (TREE_CODE (decl) == ADDR_EXPR) |
| decl = TREE_OPERAND (decl, 0); |
| |
| /* To simplify calling code, store the referenced DECL regardless of |
| the attribute determined below, but avoid storing the SSA_NAME_VAR |
| obtained above (it's not useful for dataflow purposes). */ |
| if (ref) |
| *ref = decl; |
| |
| /* Use the SSA_NAME_VAR that was determined above to see if it's |
| declared nonstring. Otherwise drill down into the referenced |
| DECL. */ |
| if (var) |
| decl = var; |
| else if (TREE_CODE (decl) == ARRAY_REF) |
| decl = TREE_OPERAND (decl, 0); |
| else if (TREE_CODE (decl) == COMPONENT_REF) |
| decl = TREE_OPERAND (decl, 1); |
| else if (TREE_CODE (decl) == MEM_REF) |
| return get_attr_nonstring_decl (TREE_OPERAND (decl, 0), ref); |
| |
| if (DECL_P (decl) |
| && lookup_attribute ("nonstring", DECL_ATTRIBUTES (decl))) |
| return decl; |
| |
| return NULL_TREE; |
| } |
| |
| /* Warn about passing a non-string array/pointer to a built-in function |
| that expects a nul-terminated string argument. Returns true if |
| a warning has been issued.*/ |
| |
| bool |
| maybe_warn_nonstring_arg (tree fndecl, tree exp) |
| { |
| if (!fndecl || !fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)) |
| return false; |
| |
| if (TREE_NO_WARNING (exp) || !warn_stringop_overread) |
| return false; |
| |
| /* Avoid clearly invalid calls (more checking done below). */ |
| unsigned nargs = call_expr_nargs (exp); |
| if (!nargs) |
| return false; |
| |
| /* The bound argument to a bounded string function like strncpy. */ |
| tree bound = NULL_TREE; |
| |
| /* The longest known or possible string argument to one of the comparison |
| functions. If the length is less than the bound it is used instead. |
| Since the length is only used for warning and not for code generation |
| disable strict mode in the calls to get_range_strlen below. */ |
| tree maxlen = NULL_TREE; |
| |
| /* It's safe to call "bounded" string functions with a non-string |
| argument since the functions provide an explicit bound for this |
| purpose. The exception is strncat where the bound may refer to |
| either the destination or the source. */ |
| int fncode = DECL_FUNCTION_CODE (fndecl); |
| switch (fncode) |
| { |
| case BUILT_IN_STRCMP: |
| case BUILT_IN_STRNCMP: |
| case BUILT_IN_STRNCASECMP: |
| { |
| /* For these, if one argument refers to one or more of a set |
| of string constants or arrays of known size, determine |
| the range of their known or possible lengths and use it |
| conservatively as the bound for the unbounded function, |
| and to adjust the range of the bound of the bounded ones. */ |
| for (unsigned argno = 0; |
| argno < MIN (nargs, 2) |
| && !(maxlen && TREE_CODE (maxlen) == INTEGER_CST); argno++) |
| { |
| tree arg = CALL_EXPR_ARG (exp, argno); |
| if (!get_attr_nonstring_decl (arg)) |
| { |
| c_strlen_data lendata = { }; |
| /* Set MAXBOUND to an arbitrary non-null non-integer |
| node as a request to have it set to the length of |
| the longest string in a PHI. */ |
| lendata.maxbound = arg; |
| get_range_strlen (arg, &lendata, /* eltsize = */ 1); |
| maxlen = lendata.maxbound; |
| } |
| } |
| } |
| /* Fall through. */ |
| |
| case BUILT_IN_STRNCAT: |
| case BUILT_IN_STPNCPY: |
| case BUILT_IN_STRNCPY: |
| if (nargs > 2) |
| bound = CALL_EXPR_ARG (exp, 2); |
| break; |
| |
| case BUILT_IN_STRNDUP: |
| if (nargs > 1) |
| bound = CALL_EXPR_ARG (exp, 1); |
| break; |
| |
| case BUILT_IN_STRNLEN: |
| { |
| tree arg = CALL_EXPR_ARG (exp, 0); |
| if (!get_attr_nonstring_decl (arg)) |
| { |
| c_strlen_data lendata = { }; |
| /* Set MAXBOUND to an arbitrary non-null non-integer |
| node as a request to have it set to the length of |
| the longest string in a PHI. */ |
| lendata.maxbound = arg; |
| get_range_strlen (arg, &lendata, /* eltsize = */ 1); |
| maxlen = lendata.maxbound; |
| } |
| if (nargs > 1) |
| bound = CALL_EXPR_ARG (exp, 1); |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| /* Determine the range of the bound argument (if specified). */ |
| tree bndrng[2] = { NULL_TREE, NULL_TREE }; |
| if (bound) |
| { |
| STRIP_NOPS (bound); |
| get_size_range (bound, bndrng); |
| } |
| |
| location_t loc = EXPR_LOCATION (exp); |
| |
| if (bndrng[0]) |
| { |
| /* Diagnose excessive bound prior to the adjustment below and |
| regardless of attribute nonstring. */ |
| tree maxobjsize = max_object_size (); |
| if (tree_int_cst_lt (maxobjsize, bndrng[0])) |
| { |
| bool warned = false; |
| if (tree_int_cst_equal (bndrng[0], bndrng[1])) |
| warned = warning_at (loc, OPT_Wstringop_overread, |
| "%K%qD specified bound %E " |
| "exceeds maximum object size %E", |
| exp, fndecl, bndrng[0], maxobjsize); |
| else |
| warned = warning_at (loc, OPT_Wstringop_overread, |
| "%K%qD specified bound [%E, %E] " |
| "exceeds maximum object size %E", |
| exp, fndecl, bndrng[0], bndrng[1], |
| maxobjsize); |
| if (warned) |
| TREE_NO_WARNING (exp) = true; |
| |
| return warned; |
| } |
| } |
| |
| if (maxlen && !integer_all_onesp (maxlen)) |
| { |
| /* Add one for the nul. */ |
| maxlen = const_binop (PLUS_EXPR, TREE_TYPE (maxlen), maxlen, |
| size_one_node); |
| |
| if (!bndrng[0]) |
| { |
| /* Conservatively use the upper bound of the lengths for |
| both the lower and the upper bound of the operation. */ |
| bndrng[0] = maxlen; |
| bndrng[1] = maxlen; |
| bound = void_type_node; |
| } |
| else if (maxlen) |
| { |
| /* Replace the bound on the operation with the upper bound |
| of the length of the string if the latter is smaller. */ |
| if (tree_int_cst_lt (maxlen, bndrng[0])) |
| bndrng[0] = maxlen; |
| else if (tree_int_cst_lt (maxlen, bndrng[1])) |
| bndrng[1] = maxlen; |
| } |
| } |
| |
| bool any_arg_warned = false; |
| /* Iterate over the built-in function's formal arguments and check |
| each const char* against the actual argument. If the actual |
| argument is declared attribute non-string issue a warning unless |
| the argument's maximum length is bounded. */ |
| function_args_iterator it; |
| function_args_iter_init (&it, TREE_TYPE (fndecl)); |
| |
| for (unsigned argno = 0; ; ++argno, function_args_iter_next (&it)) |
| { |
| /* Avoid iterating past the declared argument in a call |
| to function declared without a prototype. */ |
| if (argno >= nargs) |
| break; |
| |
| tree argtype = function_args_iter_cond (&it); |
| if (!argtype) |
| break; |
| |
| if (TREE_CODE (argtype) != POINTER_TYPE) |
| continue; |
| |
| argtype = TREE_TYPE (argtype); |
| |
| if (TREE_CODE (argtype) != INTEGER_TYPE |
| || !TYPE_READONLY (argtype)) |
| continue; |
| |
| argtype = TYPE_MAIN_VARIANT (argtype); |
| if (argtype != char_type_node) |
| continue; |
| |
| tree callarg = CALL_EXPR_ARG (exp, argno); |
| if (TREE_CODE (callarg) == ADDR_EXPR) |
| callarg = TREE_OPERAND (callarg, 0); |
| |
| /* See if the destination is declared with attribute "nonstring". */ |
| tree decl = get_attr_nonstring_decl (callarg); |
| if (!decl) |
| continue; |
| |
| /* The maximum number of array elements accessed. */ |
| offset_int wibnd = 0; |
| |
| if (argno && fncode == BUILT_IN_STRNCAT) |
| { |
| /* See if the bound in strncat is derived from the length |
| of the strlen of the destination (as it's expected to be). |
| If so, reset BOUND and FNCODE to trigger a warning. */ |
| tree dstarg = CALL_EXPR_ARG (exp, 0); |
| if (is_strlen_related_p (dstarg, bound)) |
| { |
| /* The bound applies to the destination, not to the source, |
| so reset these to trigger a warning without mentioning |
| the bound. */ |
| bound = NULL; |
| fncode = 0; |
| } |
| else if (bndrng[1]) |
| /* Use the upper bound of the range for strncat. */ |
| wibnd = wi::to_offset (bndrng[1]); |
| } |
| else if (bndrng[0]) |
| /* Use the lower bound of the range for functions other than |
| strncat. */ |
| wibnd = wi::to_offset (bndrng[0]); |
| |
| /* Determine the size of the argument array if it is one. */ |
| offset_int asize = wibnd; |
| bool known_size = false; |
| tree type = TREE_TYPE (decl); |
| |
| /* Determine the array size. For arrays of unknown bound and |
| pointers reset BOUND to trigger the appropriate warning. */ |
| if (TREE_CODE (type) == ARRAY_TYPE) |
| { |
| if (tree arrbnd = TYPE_DOMAIN (type)) |
| { |
| if ((arrbnd = TYPE_MAX_VALUE (arrbnd))) |
| { |
| asize = wi::to_offset (arrbnd) + 1; |
| known_size = true; |
| } |
| } |
| else if (bound == void_type_node) |
| bound = NULL_TREE; |
| } |
| else if (bound == void_type_node) |
| bound = NULL_TREE; |
| |
| /* In a call to strncat with a bound in a range whose lower but |
| not upper bound is less than the array size, reset ASIZE to |
| be the same as the bound and the other variable to trigger |
| the apprpriate warning below. */ |
| if (fncode == BUILT_IN_STRNCAT |
| && bndrng[0] != bndrng[1] |
| && wi::ltu_p (wi::to_offset (bndrng[0]), asize) |
| && (!known_size |
| || wi::ltu_p (asize, wibnd))) |
| { |
| asize = wibnd; |
| bound = NULL_TREE; |
| fncode = 0; |
| } |
| |
| bool warned = false; |
| |
| auto_diagnostic_group d; |
| if (wi::ltu_p (asize, wibnd)) |
| { |
| if (bndrng[0] == bndrng[1]) |
| warned = warning_at (loc, OPT_Wstringop_overread, |
| "%qD argument %i declared attribute " |
| "%<nonstring%> is smaller than the specified " |
| "bound %wu", |
| fndecl, argno + 1, wibnd.to_uhwi ()); |
| else if (wi::ltu_p (asize, wi::to_offset (bndrng[0]))) |
| warned = warning_at (loc, OPT_Wstringop_overread, |
| "%qD argument %i declared attribute " |
| "%<nonstring%> is smaller than " |
| "the specified bound [%E, %E]", |
| fndecl, argno + 1, bndrng[0], bndrng[1]); |
| else |
| warned = warning_at (loc, OPT_Wstringop_overread, |
| "%qD argument %i declared attribute " |
| "%<nonstring%> may be smaller than " |
| "the specified bound [%E, %E]", |
| fndecl, argno + 1, bndrng[0], bndrng[1]); |
| } |
| else if (fncode == BUILT_IN_STRNCAT) |
| ; /* Avoid warning for calls to strncat() when the bound |
| is equal to the size of the non-string argument. */ |
| else if (!bound) |
| warned = warning_at (loc, OPT_Wstringop_overread, |
| "%qD argument %i declared attribute %<nonstring%>", |
| fndecl, argno + 1); |
| |
| if (warned) |
| { |
| inform (DECL_SOURCE_LOCATION (decl), |
| "argument %qD declared here", decl); |
| any_arg_warned = true; |
| } |
| } |
| |
| if (any_arg_warned) |
| TREE_NO_WARNING (exp) = true; |
| |
| return any_arg_warned; |
| } |
| |
| /* Issue an error if CALL_EXPR was flagged as requiring |
| tall-call optimization. */ |
| |
| 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); |
| } |
| |
| /* Returns the type of the argument ARGNO to function with type FNTYPE |
| or null when the typoe cannot be determined or no such argument exists. */ |
| |
| static tree |
| fntype_argno_type (tree fntype, unsigned argno) |
| { |
| if (!prototype_p (fntype)) |
| return NULL_TREE; |
| |
| tree argtype; |
| function_args_iterator it; |
| FOREACH_FUNCTION_ARGS (fntype, argtype, it) |
| if (argno-- == 0) |
| return argtype; |
| |
| return NULL_TREE; |
| } |
| |
| /* Helper to append the "human readable" attribute access specification |
| described by ACCESS to the array ATTRSTR with size STRSIZE. Used in |
| diagnostics. */ |
| |
| static inline void |
| append_attrname (const std::pair<int, attr_access> &access, |
| char *attrstr, size_t strsize) |
| { |
| if (access.second.internal_p) |
| return; |
| |
| tree str = access.second.to_external_string (); |
| gcc_assert (strsize >= (size_t) TREE_STRING_LENGTH (str)); |
| strcpy (attrstr, TREE_STRING_POINTER (str)); |
| } |
| |
| /* Iterate over attribute access read-only, read-write, and write-only |
| arguments and diagnose past-the-end accesses and related problems |
| in the function call EXP. */ |
| |
| static void |
| maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp) |
| { |
| auto_diagnostic_group adg; |
| |
| /* Set if a warning has been issued for any argument (used to decide |
| whether to emit an informational note at the end). */ |
| bool any_warned = false; |
| |
| /* A string describing the attributes that the warnings issued by this |
| function apply to. Used to print one informational note per function |
| call, rather than one per warning. That reduces clutter. */ |
| char attrstr[80]; |
| attrstr[0] = 0; |
| |
| for (rdwr_map::iterator it = rwm->begin (); it != rwm->end (); ++it) |
| { |
| std::pair<int, attr_access> access = *it; |
| |
| /* Get the function call arguments corresponding to the attribute's |
| positional arguments. When both arguments have been specified |
| there will be two entries in *RWM, one for each. They are |
| cross-referenced by their respective argument numbers in |
| ACCESS.PTRARG and ACCESS.SIZARG. */ |
| const int ptridx = access.second.ptrarg; |
| const int sizidx = access.second.sizarg; |
| |
| gcc_assert (ptridx != -1); |
| gcc_assert (access.first == ptridx || access.first == sizidx); |
| |
| /* The pointer is set to null for the entry corresponding to |
| the size argument. Skip it. It's handled when the entry |
| corresponding to the pointer argument comes up. */ |
| if (!access.second.ptr) |
| continue; |
| |
| tree ptrtype = fntype_argno_type (fntype, ptridx); |
| tree argtype = TREE_TYPE (ptrtype); |
| |
| /* The size of the access by the call. */ |
| tree access_size; |
| if (sizidx == -1) |
| { |
| /* If only the pointer attribute operand was specified and |
| not size, set SIZE to the greater of MINSIZE or size of |
| one element of the pointed to type to detect smaller |
| objects (null pointers are diagnosed in this case only |
| if the pointer is also declared with attribute nonnull. */ |
| if (access.second.minsize |
| && access.second.minsize != HOST_WIDE_INT_M1U) |
| access_size = build_int_cstu (sizetype, access.second.minsize); |
| else |
| access_size = size_one_node; |
| } |
| else |
| access_size = rwm->get (sizidx)->size; |
| |
| /* Format the value or range to avoid an explosion of messages. */ |
| char sizstr[80]; |
| tree sizrng[2] = { size_zero_node, build_all_ones_cst (sizetype) }; |
| if (get_size_range (access_size, sizrng, true)) |
| { |
| char *s0 = print_generic_expr_to_str (sizrng[0]); |
| if (tree_int_cst_equal (sizrng[0], sizrng[1])) |
| { |
| gcc_checking_assert (strlen (s0) < sizeof sizstr); |
| strcpy (sizstr, s0); |
| } |
| else |
| { |
| char *s1 = print_generic_expr_to_str (sizrng[1]); |
| gcc_checking_assert (strlen (s0) + strlen (s1) |
| < sizeof sizstr - 4); |
| sprintf (sizstr, "[%s, %s]", s0, s1); |
| free (s1); |
| } |
| free (s0); |
| } |
| else |
| *sizstr = '\0'; |
| |
| /* Set if a warning has been issued for the current argument. */ |
| bool arg_warned = false; |
| location_t loc = EXPR_LOCATION (exp); |
| tree ptr = access.second.ptr; |
| if (*sizstr |
| && tree_int_cst_sgn (sizrng[0]) < 0 |
| && tree_int_cst_sgn (sizrng[1]) < 0) |
| { |
| /* Warn about negative sizes. */ |
| if (access.second.internal_p) |
| { |
| const std::string argtypestr |
| = access.second.array_as_string (ptrtype); |
| |
| arg_warned = warning_at (loc, OPT_Wstringop_overflow_, |
| "%Kbound argument %i value %s is " |
| "negative for a variable length array " |
| "argument %i of type %s", |
| exp, sizidx + 1, sizstr, |
| ptridx + 1, argtypestr.c_str ()); |
| } |
| else |
| arg_warned = warning_at (loc, OPT_Wstringop_overflow_, |
| "%Kargument %i value %s is negative", |
| exp, sizidx + 1, sizstr); |
| |
| if (arg_warned) |
| { |
| append_attrname (access, attrstr, sizeof attrstr); |
| /* Remember a warning has been issued and avoid warning |
| again below for the same attribute. */ |
| any_warned = true; |
| continue; |
| } |
| } |
| |
| if (tree_int_cst_sgn (sizrng[0]) >= 0) |
| { |
| if (COMPLETE_TYPE_P (argtype)) |
| { |
| /* Multiply ACCESS_SIZE by the size of the type the pointer |
| argument points to. If it's incomplete the size is used |
| as is. */ |
| if (tree argsize = TYPE_SIZE_UNIT (argtype)) |
| if (TREE_CODE (argsize) == INTEGER_CST) |
| { |
| const int prec = TYPE_PRECISION (sizetype); |
| wide_int minsize = wi::to_wide (sizrng[0], prec); |
| minsize *= wi::to_wide (argsize, prec); |
| access_size = wide_int_to_tree (sizetype, minsize); |
| } |
| } |
| } |
| else |
| access_size = NULL_TREE; |
| |
| if (integer_zerop (ptr)) |
| { |
| if (sizidx >= 0 && tree_int_cst_sgn (sizrng[0]) > 0) |
| { |
| /* Warn about null pointers with positive sizes. This is |
| different from also declaring the pointer argument with |
| attribute nonnull when the function accepts null pointers |
| only when the corresponding size is zero. */ |
| if (access.second.internal_p) |
| { |
| const std::string argtypestr |
| = access.second.array_as_string (ptrtype); |
| |
| arg_warned = warning_at (loc, OPT_Wnonnull, |
| "%Kargument %i of variable length " |
| "array %s is null but " |
| "the corresponding bound argument " |
| "%i value is %s", |
| exp, sizidx + 1, argtypestr.c_str (), |
| ptridx + 1, sizstr); |
| } |
| else |
| arg_warned = warning_at (loc, OPT_Wnonnull, |
| "%Kargument %i is null but " |
| "the corresponding size argument " |
| "%i value is %s", |
| exp, ptridx + 1, sizidx + 1, |
| sizstr); |
| } |
| else if (access_size && access.second.static_p) |
| { |
| /* Warn about null pointers for [static N] array arguments |
| but do not warn for ordinary (i.e., nonstatic) arrays. */ |
| arg_warned = warning_at (loc, OPT_Wnonnull, |
| "%Kargument %i to %<%T[static %E]%> " |
| "is null where non-null expected", |
| exp, ptridx + 1, argtype, |
| access_size); |
| } |
| |
| if (arg_warned) |
| { |
| append_attrname (access, attrstr, sizeof attrstr); |
| /* Remember a warning has been issued and avoid warning |
| again below for the same attribute. */ |
| any_warned = true; |
| continue; |
| } |
| } |
| |
| access_data data (ptr, access.second.mode, NULL_TREE, false, |
| NULL_TREE, false); |
| access_ref* const pobj = (access.second.mode == access_write_only |
| ? &data.dst : &data.src); |
| tree objsize = compute_objsize (ptr, 1, pobj); |
| |
| /* The size of the destination or source object. */ |
| tree dstsize = NULL_TREE, srcsize = NULL_TREE; |
| if (access.second.mode == access_read_only |
| || access.second.mode == access_none) |
| { |
| /* For a read-only argument there is no destination. For |
| no access, set the source as well and differentiate via |
| the access flag below. */ |
| srcsize = objsize; |
| if (access.second.mode == access_read_only |
| || access.second.mode == access_none) |
| { |
| /* For a read-only attribute there is no destination so |
| clear OBJSIZE. This emits "reading N bytes" kind of |
| diagnostics instead of the "writing N bytes" kind, |
| unless MODE is none. */ |
| objsize = NULL_TREE; |
| } |
| } |
| else |
| dstsize = objsize; |
| |
| /* Clear the no-warning bit in case it was set by check_access |
| in a prior iteration so that accesses via different arguments |
| are diagnosed. */ |
| TREE_NO_WARNING (exp) = false; |
| access_mode mode = data.mode; |
| if (mode == access_deferred) |
| mode = TYPE_READONLY (argtype) ? access_read_only : access_read_write; |
| check_access (exp, access_size, /*maxread=*/ NULL_TREE, srcsize, |
| dstsize, mode, &data); |
| |
| if (TREE_NO_WARNING (exp)) |
| { |
| any_warned = true; |
| |
| if (access.second.internal_p) |
| inform (loc, "referencing argument %u of type %qT", |
| ptridx + 1, ptrtype); |
| else |
| /* If check_access issued a warning above, append the relevant |
| attribute to the string. */ |
| append_attrname (access, attrstr, sizeof attrstr); |
| } |
| } |
| |
| if (*attrstr) |
| { |
| if (fndecl) |
| inform (DECL_SOURCE_LOCATION (fndecl), |
| "in a call to function %qD declared with attribute %qs", |
| fndecl, attrstr); |
| else |
| inform (EXPR_LOCATION (fndecl), |
| "in a call with type %qT and attribute %qs", |
| fntype, attrstr); |
| } |
| else if (any_warned) |
| { |
| if (fndecl) |
| inform (DECL_SOURCE_LOCATION (fndecl), |
| "in a call to function %qD", fndecl); |
| else |
| inform (EXPR_LOCATION (fndecl), |
| "in a call with type %qT", fntype); |
| } |
| |
| /* Set the bit in case if was cleared and not set above. */ |
| TREE_NO_WARNING (exp) = true; |
| } |
| |
| /* Fill in ARGS_SIZE and ARGS array based on the parameters found in |
| CALL_EXPR EXP. |
| |
| NUM_ACTUALS is the total number of parameters. |
| |
| N_NAMED_ARGS is the total number of named arguments. |
| |
| STRUCT_VALUE_ADDR_VALUE is the implicit argument for a struct return |
| value, or null. |
| |
| FNDECL is the tree code for the target of this call (if known) |
| |
| ARGS_SO_FAR holds state needed by the target to know where to place |
| the next argument. |
| |
| REG_PARM_STACK_SPACE is the number of bytes of stack space reserved |
| for arguments which are passed in registers. |
| |
| OLD_STACK_LEVEL is a pointer to an rtx which olds the old stack level |
| and may be modified by this routine. |
| |
| OLD_PENDING_ADJ, MUST_PREALLOCATE and FLAGS are pointers to integer |
| flags which may be modified by this routine. |
| |
| MAY_TAILCALL is cleared if we encounter an invisible pass-by-reference |
| that requires allocation of stack space. |
| |
| 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, |
| struct arg_data *args, |
| struct args_size *args_size, |
| int n_named_args ATTRIBUTE_UNUSED, |
| tree exp, tree struct_value_addr_value, |
| tree fndecl, tree fntype, |
| cumulative_args_t args_so_far, |
| int reg_parm_stack_space, |
| rtx *old_stack_level, |
| poly_int64_pod *old_pending_adj, |
| int *must_preallocate, int *ecf_flags, |
| bool *may_tailcall, bool call_from_thunk_p) |
| { |
| CUMULATIVE_ARGS *args_so_far_pnt = get_cumulative_args (args_so_far); |
| location_t loc = EXPR_LOCATION (exp); |
| |
| /* Count arg position in order args appear. */ |
| int argpos; |
| |
| int i; |
| |
| args_size->constant = 0; |
| args_size->var = 0; |
| |
| bitmap_obstack_initialize (NULL); |
| |
| /* In this loop, we consider args in the order they are written. |
| We fill up ARGS from the back. */ |
| |
| i = num_actuals - 1; |
| { |
| int j = i; |
| call_expr_arg_iterator iter; |
| tree arg; |
| bitmap slots = NULL; |
| |
| if (struct_value_addr_value) |
| { |
| args[j].tree_value = struct_value_addr_value; |
| j--; |
| } |
| argpos = 0; |
| FOR_EACH_CALL_EXPR_ARG (arg, iter, exp) |
| { |
| tree argtype = TREE_TYPE (arg); |
| |
| if (targetm.calls.split_complex_arg |
| && argtype |
| && TREE_CODE (argtype) == COMPLEX_TYPE |
| && targetm.calls.split_complex_arg (argtype)) |
| { |
| tree subtype = TREE_TYPE (argtype); |
| args[j].tree_value = build1 (REALPART_EXPR, subtype, arg); |
| j--; |
| args[j].tree_value = build1 (IMAGPART_EXPR, subtype, arg); |
| } |
| else |
| args[j].tree_value = arg; |
| j--; |
| argpos++; |
| } |
| |
| if (slots) |
| BITMAP_FREE (slots); |
| } |
| |
| bitmap_obstack_release (NULL); |
| |
| tree fntypeattrs = TYPE_ATTRIBUTES (fntype); |
| /* Extract attribute alloc_size from the type of the called expression |
| (which could be a function or a function pointer) and if set, store |
| the indices of the corresponding arguments in ALLOC_IDX, and then |
| the actual argument(s) at those indices in ALLOC_ARGS. */ |
| int alloc_idx[2] = { -1, -1 }; |
| if (tree alloc_size = lookup_attribute ("alloc_size", fntypeattrs)) |
| { |
| tree args = TREE_VALUE (alloc_size); |
| alloc_idx[0] = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; |
| if (TREE_CHAIN (args)) |
| alloc_idx[1] = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; |
| } |
| |
| /* Array for up to the two attribute alloc_size arguments. */ |
| tree alloc_args[] = { NULL_TREE, NULL_TREE }; |
| |
| /* Map of attribute accewss specifications for function arguments. */ |
| rdwr_map rdwr_idx; |
| init_attr_rdwr_indices (&rdwr_idx, fntypeattrs); |
| |
| /* I counts args in order (to be) pushed; ARGPOS counts in order written. */ |
| for (argpos = 0; argpos < num_actuals; i--, argpos++) |
| { |
| tree type = TREE_TYPE (args[i].tree_value); |
| int unsignedp; |
| |
| /* Replace erroneous argument with constant zero. */ |
| if (type == error_mark_node || !COMPLETE_TYPE_P (type)) |
| args[i].tree_value = integer_zero_node, type = integer_type_node; |
| |
| /* If TYPE is a transparent union or record, pass things the way |
| we would pass the first field of the union or record. We have |
| already verified that the modes are the same. */ |
| if (RECORD_OR_UNION_TYPE_P (type) && TYPE_TRANSPARENT_AGGR (type)) |
| type = TREE_TYPE (first_field (type)); |
| |
| /* Decide where to pass this arg. |
| |
| args[i].reg is nonzero if all or part is passed in registers. |
| |
| args[i].partial is nonzero if part but not all is passed in registers, |
| and the exact value says how many bytes are passed in registers. |
| |
| args[i].pass_on_stack is nonzero if the argument must at least be |
| computed on the stack. It may then be loaded back into registers |
| if args[i].reg is nonzero. |
| |
| These decisions are driven by the FUNCTION_... macros and must agree |
| with those made by function.c. */ |
| |
| /* See if this argument should be passed by invisible reference. */ |
| function_arg_info arg (type, argpos < n_named_args); |
| if (pass_by_reference (args_so_far_pnt, arg)) |
| { |
| const bool callee_copies |
| = reference_callee_copied (args_so_far_pnt, arg); |
| tree base; |
| |
| /* If we're compiling a thunk, pass directly the address of an object |
| already in memory, instead of making a copy. Likewise if we want |
| to make the copy in the callee instead of the caller. */ |
| if ((call_from_thunk_p || callee_copies) |
| && (base = get_base_address (args[i].tree_value)) |
| && TREE_CODE (base) != SSA_NAME |
| && (!DECL_P (base) || MEM_P (DECL_RTL (base)))) |
| { |
| /* We may have turned the parameter value into an SSA name. |
| Go back to the original parameter so we can take the |
| address. */ |
| if (TREE_CODE (args[i].tree_value) == SSA_NAME) |
| { |
| gcc_assert (SSA_NAME_IS_DEFAULT_DEF (args[i].tree_value)); |
| args[i].tree_value = SSA_NAME_VAR (args[i].tree_value); |
| gcc_assert (TREE_CODE (args[i].tree_value) == PARM_DECL); |
| } |
| /* Argument setup code may have copied the value to register. We |
| revert that optimization now because the tail call code must |
| use the original location. */ |
| if (TREE_CODE (args[i].tree_value) == PARM_DECL |
| && !MEM_P (DECL_RTL (args[i].tree_value)) |
| && DECL_INCOMING_RTL (args[i].tree_value) |
| && MEM_P (DECL_INCOMING_RTL (args[i].tree_value))) |
| set_decl_rtl (args[i].tree_value, |
| DECL_INCOMING_RTL (args[i].tree_value)); |
| |
| mark_addressable (args[i].tree_value); |
| |
| /* 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; |
| 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); |
| type = TREE_TYPE (args[i].tree_value); |
| |
| if (*ecf_flags & ECF_CONST) |
| *ecf_flags &= ~(ECF_CONST | ECF_LOOPING_CONST_OR_PURE); |
| } |
| else |
| { |
| /* We make a copy of the object and pass the address to the |
| function being called. */ |
| rtx copy; |
| |
| if (!COMPLETE_TYPE_P (type) |
| || TREE_CODE (TYPE_SIZE_UNIT (type)) != INTEGER_CST |
| || (flag_stack_check == GENERIC_STACK_CHECK |
| && compare_tree_int (TYPE_SIZE_UNIT (type), |
| STACK_CHECK_MAX_VAR_SIZE) > 0)) |
| { |
| /* This is a variable-sized object. Make space on the stack |
| for it. */ |
| rtx size_rtx = expr_size (args[i].tree_value); |
| |
| if (*old_stack_level == 0) |
| { |
| emit_stack_save (SAVE_BLOCK, old_stack_level); |
| *old_pending_adj = pending_stack_adjust; |
| pending_stack_adjust = 0; |
| } |
| |
| /* We can pass TRUE as the 4th argument because we just |
| saved the stack pointer and will restore it right after |
| the call. */ |
| copy = allocate_dynamic_stack_space (size_rtx, |
| TYPE_ALIGN (type), |
| TYPE_ALIGN (type), |
| max_int_size_in_bytes |
| (type), |
| true); |
| copy = gen_rtx_MEM (BLKmode, copy); |
| set_mem_attributes (copy, type, 1); |
| } |
| else |
| copy = assign_temp (type, 1, 0); |
| |
| store_expr (args[i].tree_value, copy, 0, false, false); |
| |
| /* Just change the const function to pure and then let |
| the next test clear the pure based on |
| callee_copies. */ |
| if (*ecf_flags & ECF_CONST) |
| { |
| *ecf_flags &= ~ECF_CONST; |
| *ecf_flags |= ECF_PURE; |
| } |
| |
| if (!callee_copies && *ecf_flags & ECF_PURE) |
| *ecf_flags &= ~(ECF_PURE | ECF_LOOPING_CONST_OR_PURE); |
| |
| args[i].tree_value |
| = 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"); |
| } |
| arg.pass_by_reference = true; |
| } |
| |
| unsignedp = TYPE_UNSIGNED (type); |
| arg.type = type; |
| arg.mode |
| = promote_function_mode (type, TYPE_MODE (type), &unsignedp, |
| fndecl ? TREE_TYPE (fndecl) : fntype, 0); |
| |
| args[i].unsignedp = unsignedp; |
| args[i].mode = arg.mode; |
| |
| targetm.calls.warn_parameter_passing_abi (args_so_far, type); |
| |
| args[i].reg = targetm.calls.function_arg (args_so_far, arg); |
| |
| if (args[i].reg && CONST_INT_P (args[i].reg)) |
| args[i].reg = NULL; |
| |
| /* If this is a sibling call and the machine has register windows, the |
| register window has to be unwinded before calling the routine, so |
| arguments have to go into the incoming registers. */ |
| if (targetm.calls.function_incoming_arg != targetm.calls.function_arg) |
| args[i].tail_call_reg |
| = targetm.calls.function_incoming_arg (args_so_far, arg); |
| else |
| args[i].tail_call_reg = args[i].reg; |
| |
| if (args[i].reg) |
| args[i].partial = targetm.calls.arg_partial_bytes (args_so_far, arg); |
| |
| args[i].pass_on_stack = targetm.calls.must_pass_in_stack (arg); |
| |
| /* If FUNCTION_ARG returned a (parallel [(expr_list (nil) ...) ...]), |
| it means that we are to pass this arg in the register(s) designated |
| by the PARALLEL, but also to pass it in the stack. */ |
| if (args[i].reg && GET_CODE (args[i].reg) == PARALLEL |
| && XEXP (XVECEXP (args[i].reg, 0, 0), 0) == 0) |
| args[i].pass_on_stack = 1; |
| |
| /* If this is an addressable type, we must preallocate the stack |
| since we must evaluate the object into its final location. |
| |
| If this is to be passed in both registers and the stack, it is simpler |
| to preallocate. */ |
| if (TREE_ADDRESSABLE (type) |
| || (args[i].pass_on_stack && args[i].reg != 0)) |
| *must_preallocate = 1; |
| |
| /* Compute the stack-size of this argument. */ |
| if (args[i].reg == 0 || args[i].partial != 0 |
| || reg_parm_stack_space > 0 |
| || args[i].pass_on_stack) |
| locate_and_pad_parm (arg.mode, type, |
| #ifdef STACK_PARMS_IN_REG_PARM_AREA |
| 1, |
| #else |
| args[i].reg != 0, |
| #endif |
| reg_parm_stack_space, |
| args[i].pass_on_stack ? 0 : args[i].partial, |
| fndecl, args_size, &args[i].locate); |
| #ifdef BLOCK_REG_PADDING |
| else |
| /* The argument is passed entirely in registers. See at which |
| end it should be padded. */ |
| args[i].locate.where_pad = |
| BLOCK_REG_PADDING (arg.mode, type, |
| int_size_in_bytes (type) <= UNITS_PER_WORD); |
| #endif |
| |
| /* Update ARGS_SIZE, the total stack space for args so far. */ |
| |
| args_size->constant += args[i].locate.size.constant; |
| if (args[i].locate.size.var) |
| ADD_PARM_SIZE (*args_size, args[i].locate.size.var); |
| |
| /* Increment ARGS_SO_FAR, which has info about which arg-registers |
| have been used, etc. */ |
| |
| /* ??? Traditionally we've passed TYPE_MODE here, instead of the |
| promoted_mode used for function_arg above. However, the |
| corresponding handling of incoming arguments in function.c |
| does pass the promoted mode. */ |
| arg.mode = TYPE_MODE (type); |
| targetm.calls.function_arg_advance (args_so_far, arg); |
| |
| /* Store argument values for functions decorated with attribute |
| alloc_size. */ |
| if (argpos == alloc_idx[0]) |
| alloc_args[0] = args[i].tree_value; |
| else if (argpos == alloc_idx[1]) |
| alloc_args[1] = args[i].tree_value; |
| |
| /* Save the actual argument that corresponds to the access attribute |
| operand for later processing. */ |
| if (attr_access *access = rdwr_idx.get (argpos)) |
| { |
| if (POINTER_TYPE_P (type)) |
| { |
| access->ptr = args[i].tree_value; |
| // A nonnull ACCESS->SIZE contains VLA bounds. */ |
| } |
| else |
| { |
| access->size = args[i].tree_value; |
| gcc_assert (access->ptr == NULL_TREE); |
| } |
| } |
| } |
| |
| if (alloc_args[0]) |
| { |
| /* Check the arguments of functions decorated with attribute |
| alloc_size. */ |
| maybe_warn_alloc_args_overflow (fndecl, exp, alloc_args, alloc_idx); |
| } |
| |
| /* Detect passing non-string arguments to functions expecting |
| nul-terminated strings. */ |
| maybe_warn_nonstring_arg (fndecl, exp); |
| |
| /* Check attribute access arguments. */ |
| maybe_warn_rdwr_sizes (&rdwr_idx, fndecl, fntype, exp); |
| |
| /* Check calls to operator new for mismatched forms and attempts |
| to deallocate unallocated objects. */ |
| maybe_emit_free_warning (exp); |
| } |
| |
| /* Update ARGS_SIZE to contain the total size for the argument block. |
| Return the original constant component of the argument block's size. |
| |
| REG_PARM_STACK_SPACE holds the number of bytes of stack space reserved |
| for arguments passed in registers. */ |
| |
| static poly_int64 |
| compute_argument_block_size (int reg_parm_stack_space, |
| struct args_size *args_size, |
| tree fndecl ATTRIBUTE_UNUSED, |
| tree fntype ATTRIBUTE_UNUSED, |
| int preferred_stack_boundary ATTRIBUTE_UNUSED) |
| { |
| poly_int64 unadjusted_args_size = args_size->constant; |
| |
| /* For accumulate outgoing args mode we don't need to align, since the frame |
| will be already aligned. Align to STACK_BOUNDARY in order to prevent |
| backends from generating misaligned frame sizes. */ |
| if (ACCUMULATE_OUTGOING_ARGS && preferred_stack_boundary > STACK_BOUNDARY) |
| preferred_stack_boundary = STACK_BOUNDARY; |
| |
| /* Compute the actual size of the argument block required. The variable |
| and constant sizes must be combined, the size may have to be rounded, |
| and there may be a minimum required size. */ |
| |
| if (args_size->var) |
| { |
| args_size->var = ARGS_SIZE_TREE (*args_size); |
| args_size->constant = 0; |
| |
| preferred_stack_boundary /= BITS_PER_UNIT; |
| if (preferred_stack_boundary > 1) |
| { |
| /* We don't handle this case yet. To handle it correctly we have |
| to add the delta, round and subtract the delta. |
| Currently no machine description requires this support. */ |
| gcc_assert (multiple_p (stack_pointer_delta, |
| preferred_stack_boundary)); |
| args_size->var = round_up (args_size->var, preferred_stack_boundary); |
| } |
| |
| if (reg_parm_stack_space > 0) |
| { |
| args_size->var |
| = size_binop (MAX_EXPR, args_size->var, |
| ssize_int (reg_parm_stack_space)); |
| |
| /* The area corresponding to register parameters is not to count in |
| the size of the block we need. So make the adjustment. */ |
| if (! OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype : TREE_TYPE (fndecl)))) |
| args_size->var |
| = size_binop (MINUS_EXPR, args_size->var, |
| ssize_int (reg_parm_stack_space)); |
| } |
| } |
| else |
| { |
| preferred_stack_boundary /= BITS_PER_UNIT; |
| if (preferred_stack_boundary < 1) |
| preferred_stack_boundary = 1; |
| args_size->constant = (aligned_upper_bound (args_size->constant |
| + stack_pointer_delta, |
| preferred_stack_boundary) |
| - stack_pointer_delta); |
| |
| args_size->constant = upper_bound (args_size->constant, |
| reg_parm_stack_space); |
| |
| if (! OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype : TREE_TYPE (fndecl)))) |
| args_size->constant -= reg_parm_stack_space; |
| } |
| return unadjusted_args_size; |
| } |
| |
| /* Precompute parameters as needed for a function call. |
| |
| FLAGS is mask of ECF_* constants. |
| |
| NUM_ACTUALS is the number of arguments. |
| |
| ARGS is an array containing information for each argument; this |
| routine fills in the INITIAL_VALUE and VALUE fields for each |
| precomputed argument. */ |
| |
| static void |
| precompute_arguments (int num_actuals, struct arg_data *args) |
| { |
| int i; |
| |
| /* If this is a libcall, then precompute all arguments so that we do not |
| get extraneous instructions emitted as part of the libcall sequence. */ |
| |
| /* If we preallocated the stack space, and some arguments must be passed |
| on the stack, then we must precompute any parameter which contains a |
| function call which will store arguments on the stack. |
| Otherwise, evaluating the parameter may clobber previous parameters |
| which have already been stored into the stack. (we have code to avoid |
| such case by saving the outgoing stack arguments, but it results in |
| worse code) */ |
| if (!ACCUMULATE_OUTGOING_ARGS) |
| return; |
| |
| for (i = 0; i < num_actuals; i++) |
| { |
| tree type; |
| machine_mode mode; |
| |
| if (TREE_CODE (args[i].tree_value) != CALL_EXPR) |
| continue; |
| |
| /* If this is an addressable type, we cannot pre-evaluate it. */ |
| type = TREE_TYPE (args[i].tree_value); |
| gcc_assert (!TREE_ADDRESSABLE (type)); |
| |
| args[i].initial_value = args[i].value |
| = expand_normal (args[i].tree_value); |
| |
| mode = TYPE_MODE (type); |
| if (mode != args[i].mode) |
| { |
| int unsignedp = args[i].unsignedp; |
| args[i].value |
| = convert_modes (args[i].mode, mode, |
| args[i].value, args[i].unsignedp); |
| |
| /* CSE will replace this only if it contains args[i].value |
| pseudo, so convert it down to the declared mode using |
| a SUBREG. */ |
| if (REG_P (args[i].value) |
| && GET_MODE_CLASS (args[i].mode) == MODE_INT |
| && promote_mode (type, mode, &unsignedp) != args[i].mode) |
| { |
| args[i].initial_value |
| = gen_lowpart_SUBREG (mode, args[i].value); |
| SUBREG_PROMOTED_VAR_P (args[i].initial_value) = 1; |
| SUBREG_PROMOTED_SET (args[i].initial_value, args[i].unsignedp); |
| } |
| } |
| } |
| } |
| |
| /* Given the current state of MUST_PREALLOCATE and information about |
| arguments to a function call in NUM_ACTUALS, ARGS and ARGS_SIZE, |
| compute and return the final value for MUST_PREALLOCATE. */ |
| |
| static int |
| finalize_must_preallocate (int must_preallocate, int num_actuals, |
| struct arg_data *args, struct args_size *args_size) |
| { |
| /* See if we have or want to preallocate stack space. |
| |
| If we would have to push a partially-in-regs parm |
| before other stack parms, preallocate stack space instead. |
| |
| If the size of some parm is not a multiple of the required stack |
| alignment, we must preallocate. |
| |
| If the total size of arguments that would otherwise create a copy in |
| a temporary (such as a CALL) is more than half the total argument list |
| size, preallocation is faster. |
| |
| Another reason to preallocate is if we have a machine (like the m88k) |
| where stack alignment is required to be maintained between every |
| pair of insns, not just when the call is made. However, we assume here |
| that such machines either do not have push insns (and hence preallocation |
| would occur anyway) or the problem is taken care of with |
| PUSH_ROUNDING. */ |
| |
| if (! must_preallocate) |
| { |
| int partial_seen = 0; |
| poly_int64 copy_to_evaluate_size = 0; |
| int i; |
| |
| for (i = 0; i < num_actuals && ! must_preallocate; i++) |
| { |
| if (args[i].partial > 0 && ! args[i].pass_on_stack) |
| partial_seen = 1; |
| else if (partial_seen && args[i].reg == 0) |
| must_preallocate = 1; |
| |
| if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode |
| && (TREE_CODE (args[i].tree_value) == CALL_EXPR |
| || TREE_CODE (args[i].tree_value) == TARGET_EXPR |
| || TREE_CODE (args[i].tree_value) == COND_EXPR |
| || TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)))) |
| copy_to_evaluate_size |
| += int_size_in_bytes (TREE_TYPE (args[i].tree_value)); |
| } |
| |
| if (maybe_ne (args_size->constant, 0) |
| && maybe_ge (copy_to_evaluate_size * 2, args_size->constant)) |
| must_preallocate = 1; |
| } |
| return must_preallocate; |
| } |
| |
| /* If we preallocated stack space, compute the address of each argument |
| and store it into the ARGS array. |
| |
| We need not ensure it is a valid memory address here; it will be |
| validized when it is used. |
| |
| ARGBLOCK is an rtx for the address of the outgoing arguments. */ |
| |
| static void |
| compute_argument_addresses (struct arg_data *args, rtx argblock, int num_actuals) |
| { |
| if (argblock) |
| { |
| rtx arg_reg = argblock; |
| int i; |
| poly_int64 arg_offset = 0; |
| |
| if (GET_CODE (argblock) == PLUS) |
| { |
| arg_reg = XEXP (argblock, 0); |
| arg_offset = rtx_to_poly_int64 (XEXP (argblock, 1)); |
| } |
| |
| for (i = 0; i < num_actuals; i++) |
| { |
| rtx offset = ARGS_SIZE_RTX (args[i].locate.offset); |
| rtx slot_offset = ARGS_SIZE_RTX (args[i].locate.slot_offset); |
| rtx addr; |
| unsigned int align, boundary; |
| poly_uint64 units_on_stack = 0; |
| machine_mode partial_mode = VOIDmode; |
| |
| /* Skip this parm if it will not be passed on the stack. */ |
| if (! args[i].pass_on_stack |
| && args[i].reg != 0 |
| && args[i].partial == 0) |
| continue; |
| |
| if (TYPE_EMPTY_P (TREE_TYPE (args[i].tree_value))) |
| continue; |
| |
| addr = simplify_gen_binary (PLUS, Pmode, arg_reg, offset); |
| addr = plus_constant (Pmode, addr, arg_offset); |
| |
| if (args[i].partial != 0) |
| { |
| /* Only part of the parameter is being passed on the stack. |
| Generate a simple memory reference of the correct size. */ |
| units_on_stack = args[i].locate.size.constant; |
| poly_uint64 bits_on_stack = units_on_stack * BITS_PER_UNIT; |
| partial_mode = int_mode_for_size (bits_on_stack, 1).else_blk (); |
| args[i].stack = gen_rtx_MEM (partial_mode, addr); |
| set_mem_size (args[i].stack, units_on_stack); |
| } |
| else |
| { |
| args[i].stack = gen_rtx_MEM (args[i].mode, addr); |
| set_mem_attributes (args[i].stack, |
| TREE_TYPE (args[i].tree_value), 1); |
| } |
| align = BITS_PER_UNIT; |
| boundary = args[i].locate.boundary; |
| poly_int64 offset_val; |
| if (args[i].locate.where_pad != PAD_DOWNWARD) |
| align = boundary; |
| else if (poly_int_rtx_p (offset, &offset_val)) |
| { |
| align = least_bit_hwi (boundary); |
| unsigned int offset_align |
| = known_alignment (offset_val) * BITS_PER_UNIT; |
| if (offset_align != 0) |
| align = MIN (align, offset_align); |
| } |
| set_mem_align (args[i].stack, align); |
| |
| addr = simplify_gen_binary (PLUS, Pmode, arg_reg, slot_offset); |
| addr = plus_constant (Pmode, addr, arg_offset); |
| |
| if (args[i].partial != 0) |
| { |
| /* Only part of the parameter is being passed on the stack. |
| Generate a simple memory reference of the correct size. |
| */ |
| args[i].stack_slot = gen_rtx_MEM (partial_mode, addr); |
| set_mem_size (args[i].stack_slot, units_on_stack); |
| } |
| else |
| { |
| args[i].stack_slot = gen_rtx_MEM (args[i].mode, addr); |
| set_mem_attributes (args[i].stack_slot, |
| TREE_TYPE (args[i].tree_value), 1); |
| } |
| set_mem_align (args[i].stack_slot, args[i].locate.boundary); |
| |
| /* Function incoming arguments may overlap with sibling call |
| outgoing arguments and we cannot allow reordering of reads |
| from function arguments with stores to outgoing arguments |
| of sibling calls. */ |
| set_mem_alias_set (args[i].stack, 0); |
| set_mem_alias_set (args[i].stack_slot, 0); |
| } |
| } |
| } |
| |
| /* Given a FNDECL and EXP, return an rtx suitable for use as a target address |
| in a call instruction. |
| |
| FNDECL is the tree node for the target function. For an indirect call |
| FNDECL will be NULL_TREE. |
| |
| ADDR is the operand 0 of CALL_EXPR for this call. */ |
| |
| static rtx |
| rtx_for_function_call (tree fndecl, tree addr) |
| { |
| rtx funexp; |
| |
| /* Get the function to call, in the form of RTL. */ |
| if (fndecl) |
| { |
| if (!TREE_USED (fndecl) && fndecl != current_function_decl) |
| TREE_USED (fndecl) = 1; |
| |
| /* Get a SYMBOL_REF rtx for the function address. */ |
| funexp = XEXP (DECL_RTL (fndecl), 0); |
| } |
| else |
| /* Generate an rtx (probably a pseudo-register) for the address. */ |
| { |
| push_temp_slots (); |
| funexp = expand_normal (addr); |
| pop_temp_slots (); /* FUNEXP can't be BLKmode. */ |
| } |
| return funexp; |
| } |
| |
| /* Return the static chain for this function, if any. */ |
| |
| rtx |
| rtx_for_static_chain (const_tree fndecl_or_type, bool incoming_p) |
| { |
| if (DECL_P (fndecl_or_type) && !DECL_STATIC_CHAIN (fndecl_or_type)) |
| return NULL; |
| |
| return targetm.calls.static_chain (fndecl_or_type, incoming_p); |
| } |
| |
| /* Internal state for internal_arg_pointer_based_exp and its helpers. */ |
| static struct |
| { |
| /* Last insn that has been scanned by internal_arg_pointer_based_exp_scan, |
| or NULL_RTX if none has been scanned yet. */ |
| rtx_insn *scan_start; |
| /* Vector indexed by REGNO - FIRST_PSEUDO_REGISTER, recording if a pseudo is |
| based on crtl->args.internal_arg_pointer. The element is NULL_RTX if the |
| pseudo isn't based on it, a CONST_INT offset if the pseudo is based on it |
| with fixed offset, or PC if this is with variable or unknown offset. */ |
| vec<rtx> cache; |
| } internal_arg_pointer_exp_state; |
| |
| static rtx internal_arg_pointer_based_exp (const_rtx, bool); |
| |
| /* Helper function for internal_arg_pointer_based_exp. Scan insns in |
| the tail call sequence, starting with first insn that hasn't been |
| scanned yet, and note for each pseudo on the LHS whether it is based |
| on crtl->args.internal_arg_pointer or not, and what offset from that |
| that pointer it has. */ |
| |
| static void |
| internal_arg_pointer_based_exp_scan (void) |
| { |
| rtx_insn *insn, *scan_start = internal_arg_pointer_exp_state.scan_start; |
| |
| if (scan_start == NULL_RTX) |
| insn = get_insns (); |
| else |
| insn = NEXT_INSN (scan_start); |
| |
| while (insn) |
| { |
| rtx set = single_set (insn); |
| if (set && REG_P (SET_DEST (set)) && !HARD_REGISTER_P (SET_DEST (set))) |
| { |
| rtx val = NULL_RTX; |
| unsigned int idx = REGNO (SET_DEST (set)) - FIRST_PSEUDO_REGISTER; |
| /* Punt on pseudos set multiple times. */ |
| if (idx < internal_arg_pointer_exp_state.cache.length () |
| && (internal_arg_pointer_exp_state.cache[idx] |
| != NULL_RTX)) |
| val = pc_rtx; |
| else |
| val = internal_arg_pointer_based_exp (SET_SRC (set), false); |
| if (val != NULL_RTX) |
| { |
| if (idx >= internal_arg_pointer_exp_state.cache.length ()) |
| internal_arg_pointer_exp_state.cache |
| .safe_grow_cleared (idx + 1, true); |
| internal_arg_pointer_exp_state.cache[idx] = val; |
| } |
| } |
| if (NEXT_INSN (insn) == NULL_RTX) |
| scan_start = insn; |
| insn = NEXT_INSN (insn); |
| } |
| |
| internal_arg_pointer_exp_state.scan_start = scan_start; |
| } |
| |
| /* Compute whether RTL is based on crtl->args.internal_arg_pointer. Return |
| NULL_RTX if RTL isn't based on it, a CONST_INT offset if RTL is based on |
| it with fixed offset, or PC if this is with variable or unknown offset. |
| TOPLEVEL is true if the function is invoked at the topmost level. */ |
| |
| static rtx |
| internal_arg_pointer_based_exp (const_rtx rtl, bool toplevel) |
| { |
| if (CONSTANT_P (rtl)) |
| return NULL_RTX; |
| |
| if (rtl == crtl->args.internal_arg_pointer) |
| return const0_rtx; |
| |
| if (REG_P (rtl) && HARD_REGISTER_P (rtl)) |
| return NULL_RTX; |
| |
| poly_int64 offset; |
| if (GET_CODE (rtl) == PLUS && poly_int_rtx_p (XEXP (rtl, 1), &offset)) |
| { |
| rtx val = internal_arg_pointer_based_exp (XEXP (rtl, 0), toplevel); |
| if (val == NULL_RTX || val == pc_rtx) |
| return val; |
| return plus_constant (Pmode, val, offset); |
| } |
| |
| /* When called at the topmost level, scan pseudo assignments in between the |
| last scanned instruction in the tail call sequence and the latest insn |
| in that sequence. */ |
| if (toplevel) |
| internal_arg_pointer_based_exp_scan (); |
| |
| if (REG_P (rtl)) |
| { |
| unsigned int idx = REGNO (rtl) - FIRST_PSEUDO_REGISTER; |
| if (idx < internal_arg_pointer_exp_state.cache.length ()) |
| return internal_arg_pointer_exp_state.cache[idx]; |
| |
| return NULL_RTX; |
| } |
| |
| subrtx_iterator::array_type array; |
| FOR_EACH_SUBRTX (iter, array, rtl, NONCONST) |
| { |
| const_rtx x = *iter; |
| if (REG_P (x) && internal_arg_pointer_based_exp (x, false) != NULL_RTX) |
| return pc_rtx; |
| if (MEM_P (x)) |
| iter.skip_subrtxes (); |
| } |
| |
| return NULL_RTX; |
| } |
| |
| /* Return true if SIZE bytes starting from address ADDR might overlap an |
| already-clobbered argument area. This function is used to determine |
| if we should give up a sibcall. */ |
| |
| static bool |
| mem_might_overlap_already_clobbered_arg_p (rtx addr, poly_uint64 size) |
| { |
| poly_int64 i; |
| unsigned HOST_WIDE_INT start, end; |
| rtx val; |
| |
| if (bitmap_empty_p (stored_args_map) |
| && stored_args_watermark == HOST_WIDE_INT_M1U) |
| return false; |
| val = internal_arg_pointer_based_exp (addr, true); |
| if (val == NULL_RTX) |
| return false; |
| else if (!poly_int_rtx_p (val, &i)) |
| return true; |
| |
| if (known_eq (size, 0U)) |
| return false; |
| |
| if (STACK_GROWS_DOWNWARD) |
| i -= crtl->args.pretend_args_size; |
| else |
| i += crtl->args.pretend_args_size; |
| |
| if (ARGS_GROW_DOWNWARD) |
| i = -i - size; |
| |
| /* We can ignore any references to the function's pretend args, |
| which at this point would manifest as negative values of I. */ |
| if (known_le (i, 0) && known_le (size, poly_uint64 (-i))) |
| return false; |
| |
| start = maybe_lt (i, 0) ? 0 : constant_lower_bound (i); |
| if (!(i + size).is_constant (&end)) |
| end = HOST_WIDE_INT_M1U; |
| |
| if (end > stored_args_watermark) |
| return true; |
| |
| end = MIN (end, SBITMAP_SIZE (stored_args_map)); |
| for (unsigned HOST_WIDE_INT k = start; k < end; ++k) |
| if (bitmap_bit_p (stored_args_map, k)) |
| return true; |
| |
| return false; |
| } |
| |
| /* Do the register loads required for any wholly-register parms or any |
| parms which are passed both on the stack and in a register. Their |
| expressions were already evaluated. |
| |
| Mark all register-parms as living through the call, putting these USE |
| insns in the CALL_INSN_FUNCTION_USAGE field. |
| |
| When IS_SIBCALL, perform the check_sibcall_argument_overlap |
| checking, setting *SIBCALL_FAILURE if appropriate. */ |
| |
| static void |
| load_register_parameters (struct arg_data *args, int num_actuals, |
| rtx *call_fusage, int flags, int is_sibcall, |
| int *sibcall_failure) |
| { |
| int i, j; |
| |
| for (i = 0; i < num_actuals; i++) |
| { |
| rtx reg = ((flags & ECF_SIBCALL) |
| ? args[i].tail_call_reg : args[i].reg); |
| if (reg) |
| { |
| int partial = args[i].partial; |
| int nregs; |
| poly_int64 size = 0; |
| HOST_WIDE_INT const_size = 0; |
| rtx_insn *before_arg = get_last_insn (); |
| tree type = TREE_TYPE (args[i].tree_value); |
| if (RECORD_OR_UNION_TYPE_P (type) && TYPE_TRANSPARENT_AGGR (type |