/* Expands front end tree to back end RTL for GCC.
- Copyright (C) 1987-2022 Free Software Foundation, Inc.
+ Copyright (C) 1987-2023 Free Software Foundation, Inc.
This file is part of GCC.
tree type;
/* The alignment (in bits) of the slot. */
unsigned int align;
- /* Nonzero if this temporary is currently in use. */
- char in_use;
+ /* True if this temporary is currently in use. */
+ bool in_use;
/* Nesting level at which this slot is being used. */
int level;
/* The offset of the slot from the frame_pointer, including extra space
{
cut_slot_from_list (temp, temp_slots_at_level (temp->level));
insert_slot_to_list (temp, &avail_temp_slots);
- temp->in_use = 0;
+ temp->in_use = false;
temp->level = -1;
n_temp_slots_in_use--;
}
if (known_ge (best_p->size - rounded_size, alignment))
{
p = ggc_alloc<temp_slot> ();
- p->in_use = 0;
+ p->in_use = false;
p->size = best_p->size - rounded_size;
p->base_offset = best_p->base_offset + rounded_size;
p->full_size = best_p->full_size - rounded_size;
}
p = selected;
- p->in_use = 1;
+ p->in_use = true;
p->type = type;
p->level = temp_slot_level;
n_temp_slots_in_use++;
return NULL_RTX;
}
-unsigned int
+void
emit_initial_value_sets (void)
{
struct initial_value_struct *ivs = crtl->hard_reg_initial_vals;
rtx_insn *seq;
if (ivs == 0)
- return 0;
+ return;
start_sequence ();
for (i = 0; i < ivs->num_entries; i++)
end_sequence ();
emit_insn_at_entry (seq);
- return 0;
}
/* Return the hardreg-pseudoreg initial values pair entry I and
/* A subroutine of instantiate_virtual_regs_in_insn. Return true if X
matches the predicate for insn CODE operand OPERAND. */
-static int
+static bool
safe_insn_predicate (int code, int operand, rtx x)
{
return code < 0 || insn_operand_matches ((enum insn_code) code, operand, x);
addr = XEXP (x, 0);
if (CONSTANT_P (addr)
|| (REG_P (addr)
- && (REGNO (addr) < FIRST_VIRTUAL_REGISTER
- || REGNO (addr) > LAST_VIRTUAL_REGISTER)))
+ && !VIRTUAL_REGISTER_P (addr)))
return;
instantiate_virtual_regs_in_rtx (&XEXP (x, 0));
/* Pass through the INSNS of function FNDECL and convert virtual register
references to hard register references. */
-static unsigned int
+static void
instantiate_virtual_regs (void)
{
rtx_insn *insn;
/* Indicate that, from now on, assign_stack_local should use
frame_pointer_rtx. */
virtuals_instantiated = 1;
-
- return 0;
}
namespace {
{}
/* opt_pass methods: */
- virtual unsigned int execute (function *)
+ unsigned int execute (function *) final override
{
- return instantiate_virtual_regs ();
+ instantiate_virtual_regs ();
+ return 0;
}
}; // class pass_instantiate_virtual_regs
}
\f
-/* Return 1 if EXP is an aggregate type (or a value with aggregate type).
+/* Return true if EXP is an aggregate type (or a value with aggregate type).
This means a type for which function calls must pass an address to the
function or get an address back from the function.
EXP may be a type node or an expression (whose type is tested). */
-int
+bool
aggregate_value_p (const_tree exp, const_tree fntype)
{
const_tree type = (TYPE_P (exp)) ? exp : TREE_TYPE (exp);
else
/* For internal functions, assume nothing needs to be
returned in memory. */
- return 0;
+ return false;
}
break;
case FUNCTION_DECL:
}
if (VOID_TYPE_P (type))
- return 0;
+ return false;
+
+ if (error_operand_p (fntype))
+ return false;
/* If a record should be passed the same as its first (and only) member
don't pass it as an aggregate. */
reference, do so. */
if ((TREE_CODE (exp) == PARM_DECL || TREE_CODE (exp) == RESULT_DECL)
&& DECL_BY_REFERENCE (exp))
- return 1;
+ return true;
/* Function types that are TREE_ADDRESSABLE force return in memory. */
if (fntype && TREE_ADDRESSABLE (fntype))
- return 1;
+ return true;
/* Types that are TREE_ADDRESSABLE must be constructed in memory,
and thus can't be returned in registers. */
if (TREE_ADDRESSABLE (type))
- return 1;
+ return true;
if (TYPE_EMPTY_P (type))
- return 0;
+ return false;
if (flag_pcc_struct_return && AGGREGATE_TYPE_P (type))
- return 1;
+ return true;
if (targetm.calls.return_in_memory (type, fntype))
- return 1;
+ return true;
/* Make sure we have suitable call-clobbered regs to return
the value in; if not, we must return it in memory. */
/* If we have something other than a REG (e.g. a PARALLEL), then assume
it is OK. */
if (!REG_P (reg))
- return 0;
+ return false;
/* Use the default ABI if the type of the function isn't known.
The scheme for handling interoperability between different ABIs
nregs = hard_regno_nregs (regno, TYPE_MODE (type));
for (i = 0; i < nregs; i++)
if (!fixed_regs[regno + i] && !abi.clobbers_full_reg_p (regno + i))
- return 1;
+ return true;
- return 0;
+ return false;
}
\f
/* Return true if we should assign DECL a pseudo register; false if it
emit_move_insn (tempreg, validize_mem (copy_rtx (data->entry_parm)));
+ /* Some ABIs require scalar floating point modes to be passed
+ in a wider scalar integer mode. We need to explicitly
+ truncate to an integer mode of the correct precision before
+ using a SUBREG to reinterpret as a floating point value. */
+ if (SCALAR_FLOAT_MODE_P (data->nominal_mode)
+ && SCALAR_INT_MODE_P (data->arg.mode)
+ && known_lt (GET_MODE_SIZE (data->nominal_mode),
+ GET_MODE_SIZE (data->arg.mode)))
+ tempreg = convert_wider_int_to_float (data->nominal_mode,
+ data->arg.mode, tempreg);
+
push_to_sequence2 (all->first_conversion_insn, all->last_conversion_insn);
to_conversion = true;
assign_parms_initialize_all (&all);
fnargs = assign_parms_augmented_arg_list (&all);
+ if (TYPE_NO_NAMED_ARGS_STDARG_P (TREE_TYPE (fndecl)))
+ {
+ struct assign_parm_data_one data = {};
+ assign_parms_setup_varargs (&all, &data, false);
+ }
+
FOR_EACH_VEC_ELT (fnargs, i, parm)
{
struct assign_parm_data_one data;
}
}
-/* A subroutine of gimplify_parameters, invoked via walk_tree.
- For all seen types, gimplify their sizes. */
-
-static tree
-gimplify_parm_type (tree *tp, int *walk_subtrees, void *data)
-{
- tree t = *tp;
-
- *walk_subtrees = 0;
- if (TYPE_P (t))
- {
- if (POINTER_TYPE_P (t))
- *walk_subtrees = 1;
- else if (TYPE_SIZE (t) && !TREE_CONSTANT (TYPE_SIZE (t))
- && !TYPE_SIZES_GIMPLIFIED (t))
- {
- gimplify_type_sizes (t, (gimple_seq *) data);
- *walk_subtrees = 1;
- }
- }
-
- return NULL;
-}
-
/* Gimplify the parameter list for current_function_decl. This involves
evaluating SAVE_EXPRs of variable sized parameters and generating code
to implement callee-copies reference parameters. Returns a sequence of
SAVE_EXPRs (amongst others) onto a pending sizes list. This
turned out to be less than manageable in the gimple world.
Now we have to hunt them down ourselves. */
- walk_tree_without_duplicates (&data.arg.type,
- gimplify_parm_type, &stmts);
+ gimplify_type_sizes (TREE_TYPE (parm), &stmts);
if (TREE_CODE (DECL_SIZE_UNIT (parm)) != INTEGER_CST)
{
int n_blocks;
tree *block_vector;
- /* For XCOFF debugging output, we start numbering the blocks
- from 1 within each function, rather than keeping a running
- count. */
-#if defined (XCOFF_DEBUGGING_INFO)
- if (write_symbols == XCOFF_DEBUG)
- next_block_index = 1;
-#endif
-
block_vector = get_block_vector (DECL_INITIAL (fn), &n_blocks);
/* The top-level BLOCK isn't numbered at all. */
instead of just setting it. */
void
-push_struct_function (tree fndecl)
+push_struct_function (tree fndecl, bool abstract_p)
{
/* When in_dummy_function we might be in the middle of a pop_cfun and
current_function_decl and cfun may not match. */
|| (cfun && current_function_decl == cfun->decl));
cfun_stack.safe_push (cfun);
current_function_decl = fndecl;
- allocate_struct_function (fndecl, false);
+ allocate_struct_function (fndecl, abstract_p);
}
/* Reset crtl and other non-struct-function variables to defaults as
/* Warn if this value is an aggregate type,
regardless of which calling convention we are using for it. */
if (AGGREGATE_TYPE_P (TREE_TYPE (DECL_RESULT (subr))))
- warning (OPT_Waggregate_return, "function returns an aggregate");
+ warning_at (DECL_SOURCE_LOCATION (DECL_RESULT (subr)),
+ OPT_Waggregate_return, "function returns an aggregate");
}
/* Expand code to verify the stack_protect_guard. This is invoked at
PARMS_HAVE_CLEANUPS is nonzero if there are cleanups associated with
the function's parameters, which must be run at any return statement. */
+bool currently_expanding_function_start;
void
expand_function_start (tree subr)
{
+ currently_expanding_function_start = true;
+
/* Make sure volatile mem refs aren't considered
valid operands of arithmetic insns. */
init_recog_no_volatile ();
/* If we are doing generic stack checking, the probe should go here. */
if (flag_stack_check == GENERIC_STACK_CHECK)
stack_check_probe_note = emit_note (NOTE_INSN_DELETED);
+
+ currently_expanding_function_start = false;
}
\f
void
return hash->find (const_cast<rtx_insn *> (insn)) != NULL;
}
-int
+bool
prologue_contains (const rtx_insn *insn)
{
return contains (insn, prologue_insn_hash);
}
-int
+bool
epilogue_contains (const rtx_insn *insn)
{
return contains (insn, epilogue_insn_hash);
}
-int
+bool
prologue_epilogue_contains (const rtx_insn *insn)
{
if (contains (insn, prologue_insn_hash))
- return 1;
+ return true;
if (contains (insn, epilogue_insn_hash))
- return 1;
- return 0;
+ return true;
+ return false;
}
void
df_simulate_one_insn_backwards (bb, ret, live_out);
HARD_REG_SET selected_hardregs;
+ HARD_REG_SET all_call_used_regs;
CLEAR_HARD_REG_SET (selected_hardregs);
+ CLEAR_HARD_REG_SET (all_call_used_regs);
for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
{
if (!crtl->abi->clobbers_full_reg_p (regno))
continue;
if (REGNO_REG_SET_P (live_out, regno))
continue;
+#ifdef LEAF_REG_REMAP
+ if (crtl->uses_only_leaf_regs && LEAF_REG_REMAP (regno) < 0)
+ continue;
+#endif
+ /* This is a call used register that is dead at return. */
+ SET_HARD_REG_BIT (all_call_used_regs, regno);
+
if (only_gpr
&& !TEST_HARD_REG_BIT (reg_class_contents[GENERAL_REGS], regno))
continue;
continue;
if (only_arg && !FUNCTION_ARG_REGNO_P (regno))
continue;
-#ifdef LEAF_REG_REMAP
- if (crtl->uses_only_leaf_regs && LEAF_REG_REMAP (regno) < 0)
- continue;
-#endif
/* Now this is a register that we might want to zero. */
SET_HARD_REG_BIT (selected_hardregs, regno);
HARD_REG_SET zeroed_hardregs;
start_sequence ();
zeroed_hardregs = targetm.calls.zero_call_used_regs (selected_hardregs);
+
+ /* For most targets, the returned set of registers is a subset of
+ selected_hardregs, however, for some of the targets (for example MIPS),
+ clearing some registers that are in selected_hardregs requires clearing
+ other call used registers that are not in the selected_hardregs, under
+ such situation, the returned set of registers must be a subset of
+ all call used registers. */
+ gcc_assert (hard_reg_set_subset_p (zeroed_hardregs, all_call_used_regs));
+
rtx_insn *seq = get_insns ();
end_sequence ();
if (seq)
/* Update the data flow information. */
crtl->must_be_zero_on_return |= zeroed_hardregs;
- df_set_bb_dirty (EXIT_BLOCK_PTR_FOR_FN (cfun));
+ df_update_exit_block_uses ();
}
}
}
}
- /* Threading the prologue and epilogue changes the artificial refs
- in the entry and exit blocks. */
- epilogue_completed = 1;
- df_update_entry_exit_and_calls ();
+ /* Threading the prologue and epilogue changes the artificial refs in the
+ entry and exit blocks, and may invalidate DF info for tail calls. */
+ if (optimize
+ || flag_optimize_sibling_calls
+ || flag_ipa_icf_functions
+ || in_lto_p)
+ df_update_entry_exit_and_calls ();
+ else
+ {
+ df_update_entry_block_defs ();
+ df_update_exit_block_uses ();
+ }
}
/* Reposition the prologue-end and epilogue-begin notes after
}
\f
-static unsigned int
+static void
rest_of_handle_check_leaf_regs (void)
{
#ifdef LEAF_REGISTERS
crtl->uses_only_leaf_regs
= optimize > 0 && only_leaf_regs_used () && leaf_function_p ();
#endif
- return 0;
}
/* Insert a TYPE into the used types hash table of CFUN. */
{}
/* opt_pass methods: */
- virtual unsigned int execute (function *)
+ unsigned int execute (function *) final override
{
- return rest_of_handle_check_leaf_regs ();
+ rest_of_handle_check_leaf_regs ();
+ return 0;
}
}; // class pass_leaf_regs
return new pass_leaf_regs (ctxt);
}
-static unsigned int
-rest_of_handle_thread_prologue_and_epilogue (void)
+static void
+rest_of_handle_thread_prologue_and_epilogue (function *fun)
{
/* prepare_shrink_wrap is sensitive to the block structure of the control
flow graph, so clean it up first. */
Fix that up. */
fixup_partitions ();
+ /* After prologue and epilogue generation, the judgement on whether
+ one memory access onto stack frame may trap or not could change,
+ since we get more exact stack information by now. So try to
+ remove any EH edges here, see PR90259. */
+ if (fun->can_throw_non_call_exceptions)
+ purge_all_dead_edges ();
+
/* Shrink-wrapping can result in unreachable edges in the epilogue,
see PR57320. */
cleanup_cfg (optimize ? CLEANUP_EXPENSIVE : 0);
/* The stack usage info is finalized during prologue expansion. */
if (flag_stack_usage_info || flag_callgraph_info)
output_stack_usage ();
-
- return 0;
}
/* Record a final call to CALLEE at LOCATION. */
{}
/* opt_pass methods: */
- virtual unsigned int execute (function *)
+ unsigned int execute (function * fun) final override
{
- return rest_of_handle_thread_prologue_and_epilogue ();
+ rest_of_handle_thread_prologue_and_epilogue (fun);
+ return 0;
}
}; // class pass_thread_prologue_and_epilogue
{}
/* opt_pass methods: */
- virtual unsigned int execute (function *);
+ unsigned int execute (function *) final override;
}; // class pass_zero_call_used_regs
{}
/* opt_pass methods: */
- virtual unsigned int execute (function *);
+ unsigned int execute (function *) final override;
}; // class pass_match_asm_constraints