/* Expands front end tree to back end RTL for GCC.
- Copyright (C) 1987-2019 Free Software Foundation, Inc.
+ Copyright (C) 1987-2020 Free Software Foundation, Inc.
This file is part of GCC.
#include "attribs.h"
#include "gimple.h"
#include "options.h"
+#include "function-abi.h"
/* So we can assign to cfun in this file. */
#undef cfun
if (!REG_P (reg))
return 0;
+ /* Use the default ABI if the type of the function isn't known.
+ The scheme for handling interoperability between different ABIs
+ requires us to be able to tell when we're calling a function with
+ a nondefault ABI. */
+ const predefined_function_abi &abi = (fntype
+ ? fntype_abi (fntype)
+ : default_function_abi);
regno = REGNO (reg);
nregs = hard_regno_nregs (regno, TYPE_MODE (type));
for (i = 0; i < nregs; i++)
- if (! call_used_regs[regno + i])
+ if (!fixed_regs[regno + i] && !abi.clobbers_full_reg_p (regno + i))
return 1;
return 0;
struct assign_parm_data_one
{
tree nominal_type;
- tree passed_type;
+ function_arg_info arg;
rtx entry_parm;
rtx stack_parm;
machine_mode nominal_mode;
machine_mode passed_mode;
- machine_mode promoted_mode;
struct locate_and_pad_arg_data locate;
int partial;
- BOOL_BITFIELD named_arg : 1;
- BOOL_BITFIELD passed_pointer : 1;
};
/* A subroutine of assign_parms. Initialize ALL. */
assign_parm_find_data_types (struct assign_parm_data_all *all, tree parm,
struct assign_parm_data_one *data)
{
- tree nominal_type, passed_type;
- machine_mode nominal_mode, passed_mode, promoted_mode;
int unsignedp;
- memset (data, 0, sizeof (*data));
+ *data = assign_parm_data_one ();
/* NAMED_ARG is a misnomer. We really mean 'non-variadic'. */
if (!cfun->stdarg)
- data->named_arg = 1; /* No variadic parms. */
+ data->arg.named = 1; /* No variadic parms. */
else if (DECL_CHAIN (parm))
- data->named_arg = 1; /* Not the last non-variadic parm. */
+ data->arg.named = 1; /* Not the last non-variadic parm. */
else if (targetm.calls.strict_argument_naming (all->args_so_far))
- data->named_arg = 1; /* Only variadic ones are unnamed. */
+ data->arg.named = 1; /* Only variadic ones are unnamed. */
else
- data->named_arg = 0; /* Treat as variadic. */
+ data->arg.named = 0; /* Treat as variadic. */
- nominal_type = TREE_TYPE (parm);
- passed_type = DECL_ARG_TYPE (parm);
+ data->nominal_type = TREE_TYPE (parm);
+ data->arg.type = DECL_ARG_TYPE (parm);
/* Look out for errors propagating this far. Also, if the parameter's
type is void then its value doesn't matter. */
/* This can happen after weird syntax errors
or if an enum type is defined among the parms. */
|| TREE_CODE (parm) != PARM_DECL
- || passed_type == NULL
- || VOID_TYPE_P (nominal_type))
+ || data->arg.type == NULL
+ || VOID_TYPE_P (data->nominal_type))
{
- nominal_type = passed_type = void_type_node;
- nominal_mode = passed_mode = promoted_mode = VOIDmode;
- goto egress;
+ data->nominal_type = data->arg.type = void_type_node;
+ data->nominal_mode = data->passed_mode = data->arg.mode = VOIDmode;
+ return;
}
/* Find mode of arg as it is passed, and mode of arg as it should be
during execution of this function. */
- passed_mode = TYPE_MODE (passed_type);
- nominal_mode = TYPE_MODE (nominal_type);
+ data->passed_mode = data->arg.mode = TYPE_MODE (data->arg.type);
+ data->nominal_mode = TYPE_MODE (data->nominal_type);
/* If the parm is to be passed as a transparent union or record, use the
type of the first field for the tests below. We have already verified
that the modes are the same. */
- if ((TREE_CODE (passed_type) == UNION_TYPE
- || TREE_CODE (passed_type) == RECORD_TYPE)
- && TYPE_TRANSPARENT_AGGR (passed_type))
- passed_type = TREE_TYPE (first_field (passed_type));
+ if (RECORD_OR_UNION_TYPE_P (data->arg.type)
+ && TYPE_TRANSPARENT_AGGR (data->arg.type))
+ data->arg.type = TREE_TYPE (first_field (data->arg.type));
/* See if this arg was passed by invisible reference. */
- if (pass_by_reference (&all->args_so_far_v, passed_mode,
- passed_type, data->named_arg))
+ if (apply_pass_by_reference_rules (&all->args_so_far_v, data->arg))
{
- passed_type = nominal_type = build_pointer_type (passed_type);
- data->passed_pointer = true;
- passed_mode = nominal_mode = TYPE_MODE (nominal_type);
+ data->nominal_type = data->arg.type;
+ data->passed_mode = data->nominal_mode = data->arg.mode;
}
/* Find mode as it is passed by the ABI. */
- unsignedp = TYPE_UNSIGNED (passed_type);
- promoted_mode = promote_function_mode (passed_type, passed_mode, &unsignedp,
- TREE_TYPE (current_function_decl), 0);
-
- egress:
- data->nominal_type = nominal_type;
- data->passed_type = passed_type;
- data->nominal_mode = nominal_mode;
- data->passed_mode = passed_mode;
- data->promoted_mode = promoted_mode;
+ unsignedp = TYPE_UNSIGNED (data->arg.type);
+ data->arg.mode
+ = promote_function_mode (data->arg.type, data->arg.mode, &unsignedp,
+ TREE_TYPE (current_function_decl), 0);
}
/* A subroutine of assign_parms. Invoke setup_incoming_varargs. */
{
int varargs_pretend_bytes = 0;
- targetm.calls.setup_incoming_varargs (all->args_so_far,
- data->promoted_mode,
- data->passed_type,
+ function_arg_info last_named_arg = data->arg;
+ last_named_arg.named = true;
+ targetm.calls.setup_incoming_varargs (all->args_so_far, last_named_arg,
&varargs_pretend_bytes, no_rtl);
/* If the back-end has requested extra stack space, record how much is
rtx entry_parm;
bool in_regs;
- if (data->promoted_mode == VOIDmode)
+ if (data->arg.mode == VOIDmode)
{
data->entry_parm = data->stack_parm = const0_rtx;
return;
}
targetm.calls.warn_parameter_passing_abi (all->args_so_far,
- data->passed_type);
+ data->arg.type);
entry_parm = targetm.calls.function_incoming_arg (all->args_so_far,
- data->promoted_mode,
- data->passed_type,
- data->named_arg);
-
+ data->arg);
if (entry_parm == 0)
- data->promoted_mode = data->passed_mode;
+ data->arg.mode = data->passed_mode;
/* Determine parm's home in the stack, in case it arrives in the stack
or we should pretend it did. Compute the stack position and rtx where
#ifdef STACK_PARMS_IN_REG_PARM_AREA
in_regs = true;
#endif
- if (!in_regs && !data->named_arg)
+ if (!in_regs && !data->arg.named)
{
if (targetm.calls.pretend_outgoing_varargs_named (all->args_so_far))
{
rtx tem;
+ function_arg_info named_arg = data->arg;
+ named_arg.named = true;
tem = targetm.calls.function_incoming_arg (all->args_so_far,
- data->promoted_mode,
- data->passed_type, true);
+ named_arg);
in_regs = tem != NULL;
}
}
/* If this parameter was passed both in registers and in the stack, use
the copy on the stack. */
- if (targetm.calls.must_pass_in_stack (data->promoted_mode,
- data->passed_type))
+ if (targetm.calls.must_pass_in_stack (data->arg))
entry_parm = 0;
if (entry_parm)
{
int partial;
- partial = targetm.calls.arg_partial_bytes (all->args_so_far,
- data->promoted_mode,
- data->passed_type,
- data->named_arg);
+ partial = targetm.calls.arg_partial_bytes (all->args_so_far, data->arg);
data->partial = partial;
/* The caller might already have allocated stack space for the
}
}
- locate_and_pad_parm (data->promoted_mode, data->passed_type, in_regs,
+ locate_and_pad_parm (data->arg.mode, data->arg.type, in_regs,
all->reg_parm_stack_space,
entry_parm ? data->partial : 0, current_function_decl,
&all->stack_args_size, &data->locate);
stack_parm = crtl->args.internal_arg_pointer;
if (offset_rtx != const0_rtx)
stack_parm = gen_rtx_PLUS (Pmode, stack_parm, offset_rtx);
- stack_parm = gen_rtx_MEM (data->promoted_mode, stack_parm);
+ stack_parm = gen_rtx_MEM (data->arg.mode, stack_parm);
- if (!data->passed_pointer)
+ if (!data->arg.pass_by_reference)
{
set_mem_attributes (stack_parm, parm, 1);
/* set_mem_attributes could set MEM_SIZE to the passed mode's size,
while promoted mode's size is needed. */
- if (data->promoted_mode != BLKmode
- && data->promoted_mode != DECL_MODE (parm))
+ if (data->arg.mode != BLKmode
+ && data->arg.mode != DECL_MODE (parm))
{
- set_mem_size (stack_parm, GET_MODE_SIZE (data->promoted_mode));
+ set_mem_size (stack_parm, GET_MODE_SIZE (data->arg.mode));
if (MEM_EXPR (stack_parm) && MEM_OFFSET_KNOWN_P (stack_parm))
{
poly_int64 offset = subreg_lowpart_offset (DECL_MODE (parm),
- data->promoted_mode);
+ data->arg.mode);
if (maybe_ne (offset, 0))
set_mem_offset (stack_parm, MEM_OFFSET (stack_parm) - offset);
}
stack slot boundary, take advantage of that excess alignment.
Don't make any assumptions if STACK_POINTER_OFFSET is in use. */
if (poly_int_rtx_p (offset_rtx, &offset)
- && STACK_POINTER_OFFSET == 0)
+ && known_eq (STACK_POINTER_OFFSET, 0))
{
unsigned int offset_align = known_alignment (offset) * BITS_PER_UNIT;
if (offset_align == 0 || offset_align > STACK_BOUNDARY)
locations. The Irix 6 ABI has examples of this. */
if (GET_CODE (entry_parm) == PARALLEL)
emit_group_store (validize_mem (copy_rtx (stack_parm)), entry_parm,
- data->passed_type,
- int_size_in_bytes (data->passed_type));
+ data->arg.type, int_size_in_bytes (data->arg.type));
else
{
gcc_assert (data->partial % UNITS_PER_WORD == 0);
if (GET_CODE (entry_parm) == PARALLEL && GET_MODE (entry_parm) != BLKmode)
{
rtx parmreg = gen_reg_rtx (GET_MODE (entry_parm));
- emit_group_store (parmreg, entry_parm, data->passed_type,
+ emit_group_store (parmreg, entry_parm, data->arg.type,
GET_MODE_SIZE (GET_MODE (entry_parm)));
entry_parm = parmreg;
}
stack slot, if we need one. */
if (stack_parm
&& ((GET_MODE_ALIGNMENT (data->nominal_mode) > MEM_ALIGN (stack_parm)
- && targetm.slow_unaligned_access (data->nominal_mode,
- MEM_ALIGN (stack_parm)))
+ && ((optab_handler (movmisalign_optab, data->nominal_mode)
+ != CODE_FOR_nothing)
+ || targetm.slow_unaligned_access (data->nominal_mode,
+ MEM_ALIGN (stack_parm))))
|| (data->nominal_type
&& TYPE_ALIGN (data->nominal_type) > MEM_ALIGN (stack_parm)
&& MEM_ALIGN (stack_parm) < PREFERRED_STACK_BOUNDARY)))
pointers in their passed stack slots. */
else if (crtl->stack_protect_guard
&& (flag_stack_protect == 2
- || data->passed_pointer
+ || data->arg.pass_by_reference
|| POINTER_TYPE_P (data->nominal_type)))
stack_parm = NULL;
/* Only assign_parm_setup_block knows how to deal with register arguments
that are padded at the least significant end. */
if (REG_P (data->entry_parm)
- && known_lt (GET_MODE_SIZE (data->promoted_mode), UNITS_PER_WORD)
- && (BLOCK_REG_PADDING (data->passed_mode, data->passed_type, 1)
+ && known_lt (GET_MODE_SIZE (data->arg.mode), UNITS_PER_WORD)
+ && (BLOCK_REG_PADDING (data->passed_mode, data->arg.type, 1)
== (BYTES_BIG_ENDIAN ? PAD_UPWARD : PAD_DOWNWARD)))
return true;
#endif
data->stack_parm = NULL;
}
- size = int_size_in_bytes (data->passed_type);
+ size = int_size_in_bytes (data->arg.type);
size_stored = CEIL_ROUND (size, UNITS_PER_WORD);
if (stack_parm == 0)
{
/* Handle values in multiple non-contiguous locations. */
if (GET_CODE (entry_parm) == PARALLEL && !MEM_P (mem))
- emit_group_store (mem, entry_parm, data->passed_type, size);
+ emit_group_store (mem, entry_parm, data->arg.type, size);
else if (GET_CODE (entry_parm) == PARALLEL)
{
push_to_sequence2 (all->first_conversion_insn,
all->last_conversion_insn);
- emit_group_store (mem, entry_parm, data->passed_type, size);
+ emit_group_store (mem, entry_parm, data->arg.type, size);
all->first_conversion_insn = get_insns ();
all->last_conversion_insn = get_last_insn ();
end_sequence ();
if (mode != BLKmode
#ifdef BLOCK_REG_PADDING
&& (size == UNITS_PER_WORD
- || (BLOCK_REG_PADDING (mode, data->passed_type, 1)
+ || (BLOCK_REG_PADDING (mode, data->arg.type, 1)
!= (BYTES_BIG_ENDIAN ? PAD_UPWARD : PAD_DOWNWARD)))
#endif
)
additional changes to work correctly. */
gcc_checking_assert (BYTES_BIG_ENDIAN
&& (BLOCK_REG_PADDING (mode,
- data->passed_type, 1)
+ data->arg.type, 1)
== PAD_UPWARD));
int by = (UNITS_PER_WORD - size) * BITS_PER_UNIT;
handle all cases (e.g. SIZE == 3). */
else if (size != UNITS_PER_WORD
#ifdef BLOCK_REG_PADDING
- && (BLOCK_REG_PADDING (mode, data->passed_type, 1)
+ && (BLOCK_REG_PADDING (mode, data->arg.type, 1)
== PAD_DOWNWARD)
#else
&& BYTES_BIG_ENDIAN
gcc_checking_assert (size > UNITS_PER_WORD);
#ifdef BLOCK_REG_PADDING
gcc_checking_assert (BLOCK_REG_PADDING (GET_MODE (mem),
- data->passed_type, 0)
+ data->arg.type, 0)
== PAD_UPWARD);
#endif
emit_move_insn (mem, entry_parm);
move_block_from_reg (REGNO (entry_parm), mem,
size_stored / UNITS_PER_WORD);
}
- else if (data->stack_parm == 0)
+ else if (data->stack_parm == 0 && !TYPE_EMPTY_P (data->arg.type))
{
push_to_sequence2 (all->first_conversion_insn, all->last_conversion_insn);
emit_block_move (stack_parm, data->entry_parm, GEN_INT (size),
/* If this was an item that we received a pointer to,
set rtl appropriately. */
- if (data->passed_pointer)
+ if (data->arg.pass_by_reference)
{
- rtl = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (data->passed_type)), parmreg);
+ rtl = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (data->arg.type)), parmreg);
set_mem_attributes (rtl, parm, 1);
}
else
validated_mem = validize_mem (copy_rtx (data->entry_parm));
need_conversion = (data->nominal_mode != data->passed_mode
- || promoted_nominal_mode != data->promoted_mode);
+ || promoted_nominal_mode != data->arg.mode);
moved = false;
if (need_conversion
for (insn = insns; insn && moved; insn = NEXT_INSN (insn))
{
if (INSN_P (insn))
- note_stores (PATTERN (insn), record_hard_reg_sets,
- &hardregs);
+ note_stores (insn, record_hard_reg_sets, &hardregs);
if (!hard_reg_set_empty_p (hardregs))
moved = false;
}
/* If we were passed a pointer but the actual value can safely live
in a register, retrieve it and use it directly. */
- if (data->passed_pointer && TYPE_MODE (TREE_TYPE (parm)) != BLKmode)
+ if (data->arg.pass_by_reference && TYPE_MODE (TREE_TYPE (parm)) != BLKmode)
{
/* We can't use nominal_mode, because it will have been set to
Pmode above. We must use the actual mode of the parm. */
assign_parm_remove_parallels (data);
- if (data->promoted_mode != data->nominal_mode)
+ if (data->arg.mode != data->nominal_mode)
{
/* Conversion is required. */
rtx tempreg = gen_reg_rtx (GET_MODE (data->entry_parm));
if (data->stack_parm == 0)
{
- int align = STACK_SLOT_ALIGNMENT (data->passed_type,
+ int align = STACK_SLOT_ALIGNMENT (data->arg.type,
GET_MODE (data->entry_parm),
- TYPE_ALIGN (data->passed_type));
+ TYPE_ALIGN (data->arg.type));
+ if (align < (int)GET_MODE_ALIGNMENT (GET_MODE (data->entry_parm))
+ && ((optab_handler (movmisalign_optab,
+ GET_MODE (data->entry_parm))
+ != CODE_FOR_nothing)
+ || targetm.slow_unaligned_access (GET_MODE (data->entry_parm),
+ align)))
+ align = GET_MODE_ALIGNMENT (GET_MODE (data->entry_parm));
data->stack_parm
= assign_stack_local (GET_MODE (data->entry_parm),
GET_MODE_SIZE (GET_MODE (data->entry_parm)),
align);
+ align = MEM_ALIGN (data->stack_parm);
set_mem_attributes (data->stack_parm, parm, 1);
+ set_mem_align (data->stack_parm, align);
}
dest = validize_mem (copy_rtx (data->stack_parm));
src = validize_mem (copy_rtx (data->entry_parm));
- if (MEM_P (src))
+ if (TYPE_EMPTY_P (data->arg.type))
+ /* Empty types don't really need to be copied. */;
+ else if (MEM_P (src))
{
/* Use a block move to handle potentially misaligned entry_parm. */
if (!to_conversion)
to_conversion = true;
emit_block_move (dest, src,
- GEN_INT (int_size_in_bytes (data->passed_type)),
+ GEN_INT (int_size_in_bytes (data->arg.type)),
BLOCK_OP_NORMAL);
}
else
if (SUPPORTS_STACK_ALIGNMENT)
{
unsigned int align
- = targetm.calls.function_arg_boundary (data.promoted_mode,
- data.passed_type);
- align = MINIMUM_ALIGNMENT (data.passed_type, data.promoted_mode,
- align);
+ = targetm.calls.function_arg_boundary (data.arg.mode,
+ data.arg.type);
+ align = MINIMUM_ALIGNMENT (data.arg.type, data.arg.mode, align);
if (TYPE_ALIGN (data.nominal_type) > align)
align = MINIMUM_ALIGNMENT (data.nominal_type,
TYPE_MODE (data.nominal_type),
{
assign_parm_find_stack_rtl (parm, &data);
assign_parm_adjust_entry_rtl (&data);
+ /* For arguments that occupy no space in the parameter
+ passing area, have non-zero size and have address taken,
+ force creation of a stack slot so that they have distinct
+ address from other parameters. */
+ if (TYPE_EMPTY_P (data.arg.type)
+ && TREE_ADDRESSABLE (parm)
+ && data.entry_parm == data.stack_parm
+ && MEM_P (data.entry_parm)
+ && int_size_in_bytes (data.arg.type))
+ data.stack_parm = NULL_RTX;
}
/* Record permanently how this parm was passed. */
- if (data.passed_pointer)
+ if (data.arg.pass_by_reference)
{
rtx incoming_rtl
- = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (data.passed_type)),
+ = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (data.arg.type)),
data.entry_parm);
set_decl_incoming_rtl (parm, incoming_rtl, true);
}
if (assign_parm_setup_block_p (&data))
assign_parm_setup_block (&all, parm, &data);
- else if (data.passed_pointer || use_register_for_decl (parm))
+ else if (data.arg.pass_by_reference || use_register_for_decl (parm))
assign_parm_setup_reg (&all, parm, &data);
else
assign_parm_setup_stack (&all, parm, &data);
assign_parms_setup_varargs (&all, &data, false);
/* Update info on where next arg arrives in registers. */
- targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
- data.passed_type, data.named_arg);
+ targetm.calls.function_arg_advance (all.args_so_far, data.arg);
}
if (targetm.calls.split_complex_arg)
continue;
/* Update info on where next arg arrives in registers. */
- targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
- data.passed_type, data.named_arg);
+ targetm.calls.function_arg_advance (all.args_so_far, data.arg);
/* ??? Once upon a time variable_size stuffed parameter list
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.passed_type,
+ walk_tree_without_duplicates (&data.arg.type,
gimplify_parm_type, &stmts);
if (TREE_CODE (DECL_SIZE_UNIT (parm)) != INTEGER_CST)
gimplify_one_sizepos (&DECL_SIZE_UNIT (parm), &stmts);
}
- if (data.passed_pointer)
+ if (data.arg.pass_by_reference)
{
- tree type = TREE_TYPE (data.passed_type);
- if (reference_callee_copied (&all.args_so_far_v, TYPE_MODE (type),
- type, data.named_arg))
+ tree type = TREE_TYPE (data.arg.type);
+ function_arg_info orig_arg (type, data.arg.named);
+ if (reference_callee_copied (&all.args_so_far_v, orig_arg))
{
tree local, t;
if (!is_gimple_reg (local)
&& flag_stack_reuse != SR_NONE)
{
- tree clobber = build_constructor (type, NULL);
+ tree clobber = build_clobber (type);
gimple *clobber_stmt;
- TREE_THIS_VOLATILE (clobber) = 1;
clobber_stmt = gimple_build_assign (local, clobber);
gimple_seq_add_stmt (cleanup, clobber_stmt);
}
return funcdef_no;
}
+/* Allocate and initialize the stack usage info data structure for the
+ current function. */
+static void
+allocate_stack_usage_info (void)
+{
+ gcc_assert (!cfun->su);
+ cfun->su = ggc_cleared_alloc<stack_usage> ();
+ cfun->su->static_stack_size = -1;
+}
+
/* Allocate a function structure for FNDECL and set its contents
to the defaults. Set cfun to the newly-allocated object.
Some of the helper functions invoked during initialization assume
if (!profile_flag && !flag_instrument_function_entry_exit)
DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (fndecl) = 1;
+
+ if (flag_callgraph_info)
+ allocate_stack_usage_info ();
}
/* Don't enable begin stmt markers if var-tracking at assignments is
prepare_function_start (void)
{
gcc_assert (!get_last_insn ());
+
+ if (in_dummy_function)
+ crtl->abi = &default_function_abi;
+ else
+ crtl->abi = &fndecl_abi (cfun->decl).base_abi ();
+
init_temp_slots ();
init_emit ();
init_varasm_status ();
init_expr ();
default_rtl_profile ();
- if (flag_stack_usage_info)
- {
- cfun->su = ggc_cleared_alloc<stack_usage> ();
- cfun->su->static_stack_size = -1;
- }
+ if (flag_stack_usage_info && !flag_callgraph_info)
+ allocate_stack_usage_info ();
cse_not_expected = ! optimize;
cleanup_cfg (optimize ? CLEANUP_EXPENSIVE : 0);
/* The stack usage info is finalized during prologue expansion. */
- if (flag_stack_usage_info)
+ if (flag_stack_usage_info || flag_callgraph_info)
output_stack_usage ();
return 0;
}
+/* Record a final call to CALLEE at LOCATION. */
+
+void
+record_final_call (tree callee, location_t location)
+{
+ struct callinfo_callee datum = { location, callee };
+ vec_safe_push (cfun->su->callees, datum);
+}
+
+/* Record a dynamic allocation made for DECL_OR_EXP. */
+
+void
+record_dynamic_alloc (tree decl_or_exp)
+{
+ struct callinfo_dalloc datum;
+
+ if (DECL_P (decl_or_exp))
+ {
+ datum.location = DECL_SOURCE_LOCATION (decl_or_exp);
+ const char *name = lang_hooks.decl_printable_name (decl_or_exp, 2);
+ const char *dot = strrchr (name, '.');
+ if (dot)
+ name = dot + 1;
+ datum.name = ggc_strdup (name);
+ }
+ else
+ {
+ datum.location = EXPR_LOCATION (decl_or_exp);
+ datum.name = NULL;
+ }
+
+ vec_safe_push (cfun->su->dallocs, datum);
+}
+
namespace {
const pass_data pass_data_thread_prologue_and_epilogue =