/* Convert RTL to assembler code and output it, for GNU compiler.
- Copyright (C) 1987-2017 Free Software Foundation, Inc.
+ Copyright (C) 1987-2022 Free Software Foundation, Inc.
This file is part of GCC.
#include "tree-pass.h"
#include "tree-ssa.h"
#include "cfgloop.h"
-#include "params.h"
#include "stringpool.h"
#include "attribs.h"
#include "asan.h"
#include "rtl-iter.h"
#include "print-rtl.h"
+#include "function-abi.h"
+#include "common/common-target.h"
#ifdef XCOFF_DEBUGGING_INFO
#include "xcoffout.h" /* Needed for external data declarations. */
#include "dbxout.h"
#endif
-/* Most ports that aren't using cc0 don't need to define CC_STATUS_INIT.
+/* Most ports don't need to define CC_STATUS_INIT.
So define a null default for it to save conditionalization later. */
#ifndef CC_STATUS_INIT
#define CC_STATUS_INIT
/* Bitflags used by final_scan_insn. */
#define SEEN_NOTE 1
#define SEEN_EMITTED 2
+#define SEEN_NEXT_VIEW 4
/* Last insn processed by final_scan_insn. */
static rtx_insn *debug_insn;
/* Column number of last NOTE. */
static int last_columnnum;
-/* Last discriminator written to assembly. */
+/* Discriminator written to assembly. */
static int last_discriminator;
-/* Discriminator of current block. */
+/* Discriminator to be written to assembly for current instruction.
+ Note: actual usage depends on loc_discriminator_kind setting. */
static int discriminator;
+static inline int compute_discriminator (location_t loc);
+
+/* Discriminator identifying current basic block among others sharing
+ the same locus. */
+static int bb_discriminator;
+
+/* Basic block discriminator for previous instruction. */
+static int last_bb_discriminator;
/* Highest line number in current block. */
static int high_block_linenum;
static const char *override_filename;
static int override_linenum;
static int override_columnnum;
+static int override_discriminator;
/* Whether to force emission of a line note before the next insn. */
static bool force_source_line = false;
static int insn_counter = 0;
-/* This variable contains machine-dependent flags (defined in tm.h)
- set and examined by output routines
- that describe how to interpret the condition codes properly. */
-
-CC_STATUS cc_status;
-
-/* During output of an insn, this contains a copy of cc_status
- from before the insn. */
-
-CC_STATUS cc_prev_status;
-
/* Number of unmatched NOTE_INSN_BLOCK_BEG notes we have seen. */
static int block_depth;
/* Nonnull if the insn currently being emitted was a COND_EXEC pattern. */
rtx current_insn_predicate;
-/* True if printing into -fdump-final-insns= dump. */
+/* True if printing into -fdump-final-insns= dump. */
bool final_insns_dump_p;
/* True if profile_function should be called, but hasn't been called yet. */
#ifdef LEAF_REGISTERS
static void leaf_renumber_regs (rtx_insn *);
#endif
-#if HAVE_cc0
-static int alter_cond (rtx);
-#endif
static int align_fuzz (rtx, rtx, int, unsigned);
static void collect_fn_hard_reg_usage (void);
-static tree get_call_fndecl (rtx_insn *);
\f
/* Initialize data in final at the beginning of a compilation. */
for each insn we'll call the alignment chain of this insn in the following
comments. */
-struct label_alignment
-{
- short alignment;
- short max_skip;
-};
-
static rtx *uid_align;
static int *uid_shuid;
-static struct label_alignment *label_align;
+static vec<align_flags> label_align;
/* Indicate that branch shortening hasn't yet been done. */
address mod X to one mod Y, which is Y - X. */
#ifndef LABEL_ALIGN
-#define LABEL_ALIGN(LABEL) align_labels_log
+#define LABEL_ALIGN(LABEL) align_labels
#endif
#ifndef LOOP_ALIGN
-#define LOOP_ALIGN(LABEL) align_loops_log
+#define LOOP_ALIGN(LABEL) align_loops
#endif
#ifndef LABEL_ALIGN_AFTER_BARRIER
#endif
#ifndef JUMP_ALIGN
-#define JUMP_ALIGN(LABEL) align_jumps_log
+#define JUMP_ALIGN(LABEL) align_jumps
#endif
-int
-default_label_align_after_barrier_max_skip (rtx_insn *insn ATTRIBUTE_UNUSED)
-{
- return 0;
-}
-
-int
-default_loop_align_max_skip (rtx_insn *insn ATTRIBUTE_UNUSED)
-{
- return align_loops_max_skip;
-}
-
-int
-default_label_align_max_skip (rtx_insn *insn ATTRIBUTE_UNUSED)
-{
- return align_labels_max_skip;
-}
-
-int
-default_jump_align_max_skip (rtx_insn *insn ATTRIBUTE_UNUSED)
-{
- return align_jumps_max_skip;
-}
-
#ifndef ADDR_VEC_ALIGN
static int
final_addr_vec_align (rtx_jump_table_data *addr_vec)
static int min_labelno, max_labelno;
#define LABEL_TO_ALIGNMENT(LABEL) \
- (label_align[CODE_LABEL_NUMBER (LABEL) - min_labelno].alignment)
-
-#define LABEL_TO_MAX_SKIP(LABEL) \
- (label_align[CODE_LABEL_NUMBER (LABEL) - min_labelno].max_skip)
+ (label_align[CODE_LABEL_NUMBER (LABEL) - min_labelno])
/* For the benefit of port specific code do this also as a function. */
-int
+align_flags
label_to_alignment (rtx label)
{
if (CODE_LABEL_NUMBER (label) <= max_labelno)
return LABEL_TO_ALIGNMENT (label);
- return 0;
-}
-
-int
-label_to_max_skip (rtx label)
-{
- if (CODE_LABEL_NUMBER (label) <= max_labelno)
- return LABEL_TO_MAX_SKIP (label);
- return 0;
+ return align_flags ();
}
/* The differences in addresses
align_addr = INSN_ADDRESSES (uid) - insn_lengths[uid];
if (uid_shuid[uid] > end_shuid)
break;
- known_align_log = LABEL_TO_ALIGNMENT (align_label);
- new_align = 1 << known_align_log;
+ align_flags alignment = LABEL_TO_ALIGNMENT (align_label);
+ new_align = 1 << alignment.levels[0].log;
if (new_align < known_align)
continue;
fuzz += (-align_addr ^ growth) & (new_align - known_align);
rtx_insn *seq = NEXT_INSN (PREV_INSN (branch));
seq_uid = INSN_UID (seq);
- if (!JUMP_P (branch))
+ if (!jump_to_label_p (branch))
/* This can happen for example on the PA; the objective is to know the
offset to address something in front of the start of the function.
Thus, we can treat it like a backward branch.
unsigned int
compute_alignments (void)
{
- int log, max_skip, max_log;
basic_block bb;
+ align_flags max_alignment;
- if (label_align)
- {
- free (label_align);
- label_align = 0;
- }
+ label_align.truncate (0);
max_labelno = max_label_num ();
min_labelno = get_first_label_num ();
- label_align = XCNEWVEC (struct label_alignment, max_labelno - min_labelno + 1);
+ label_align.safe_grow_cleared (max_labelno - min_labelno + 1, true);
/* If not optimizing or optimizing for size, don't assign any alignments. */
if (! optimize || optimize_function_for_size_p (cfun))
}
loop_optimizer_init (AVOID_CFG_MODIFICATIONS);
profile_count count_threshold = cfun->cfg->count_max.apply_scale
- (1, PARAM_VALUE (PARAM_ALIGN_THRESHOLD));
+ (1, param_align_threshold);
if (dump_file)
{
bb_loop_depth (bb));
continue;
}
- max_log = LABEL_ALIGN (label);
- max_skip = targetm.asm_out.label_align_max_skip (label);
+ max_alignment = LABEL_ALIGN (label);
profile_count fallthru_count = profile_count::zero ();
profile_count branch_count = profile_count::zero ();
if (!has_fallthru
&& (branch_count > count_threshold
|| (bb->count > bb->prev_bb->count.apply_scale (10, 1)
- && (bb->prev_bb->count
+ && (bb->prev_bb->count
<= ENTRY_BLOCK_PTR_FOR_FN (cfun)
->count.apply_scale (1, 2)))))
{
- log = JUMP_ALIGN (label);
+ align_flags alignment = JUMP_ALIGN (label);
if (dump_file)
fprintf (dump_file, " jump alignment added.\n");
- if (max_log < log)
- {
- max_log = log;
- max_skip = targetm.asm_out.jump_align_max_skip (label);
- }
+ max_alignment = align_flags::max (max_alignment, alignment);
}
/* In case block is frequent and reached mostly by non-fallthru edge,
align it. It is most likely a first block of loop. */
&& branch_count + fallthru_count > count_threshold
&& (branch_count
> fallthru_count.apply_scale
- (PARAM_VALUE (PARAM_ALIGN_LOOP_ITERATIONS), 1)))
+ (param_align_loop_iterations, 1)))
{
- log = LOOP_ALIGN (label);
+ align_flags alignment = LOOP_ALIGN (label);
if (dump_file)
fprintf (dump_file, " internal loop alignment added.\n");
- if (max_log < log)
- {
- max_log = log;
- max_skip = targetm.asm_out.loop_align_max_skip (label);
- }
+ max_alignment = align_flags::max (max_alignment, alignment);
}
- LABEL_TO_ALIGNMENT (label) = max_log;
- LABEL_TO_MAX_SKIP (label) = max_skip;
+ LABEL_TO_ALIGNMENT (label) = max_alignment;
}
loop_optimizer_finalize ();
/* Grow the LABEL_ALIGN array after new labels are created. */
-static void
+static void
grow_label_align (void)
{
int old = max_labelno;
n_labels = max_labelno - min_labelno + 1;
n_old_labels = old - min_labelno + 1;
- label_align = XRESIZEVEC (struct label_alignment, label_align, n_labels);
+ label_align.safe_grow_cleared (n_labels, true);
/* Range of labels grows monotonically in the function. Failing here
means that the initialization of array got lost. */
gcc_assert (n_old_labels <= n_labels);
-
- memset (label_align + n_old_labels, 0,
- (n_labels - n_old_labels) * sizeof (struct label_alignment));
}
/* Update the already computed alignment information. LABEL_PAIRS is a vector
FOR_EACH_VEC_ELT (label_pairs, i, iter)
if (i & 1)
- {
- LABEL_TO_ALIGNMENT (label) = LABEL_TO_ALIGNMENT (iter);
- LABEL_TO_MAX_SKIP (label) = LABEL_TO_MAX_SKIP (iter);
- }
+ LABEL_TO_ALIGNMENT (label) = LABEL_TO_ALIGNMENT (iter);
else
label = iter;
}
rtx_insn *insn;
int max_uid;
int i;
- int max_log;
- int max_skip;
-#define MAX_CODE_ALIGN 16
rtx_insn *seq;
int something_changed = 1;
char *varying_length;
rtx body;
int uid;
- rtx align_tab[MAX_CODE_ALIGN];
+ rtx align_tab[MAX_CODE_ALIGN + 1];
/* Compute maximum UID and allocate label_align / uid_shuid. */
max_uid = get_max_uid ();
/* Initialize label_align and set up uid_shuid to be strictly
monotonically rising with insn order. */
- /* We use max_log here to keep track of the maximum alignment we want to
+ /* We use alignment here to keep track of the maximum alignment we want to
impose on the next CODE_LABEL (or the current one if we are processing
the CODE_LABEL itself). */
- max_log = 0;
- max_skip = 0;
+ align_flags max_alignment;
for (insn = get_insns (), i = 1; insn; insn = NEXT_INSN (insn))
{
- int log;
-
INSN_SHUID (insn) = i++;
if (INSN_P (insn))
continue;
if (rtx_code_label *label = dyn_cast <rtx_code_label *> (insn))
{
/* Merge in alignments computed by compute_alignments. */
- log = LABEL_TO_ALIGNMENT (label);
- if (max_log < log)
- {
- max_log = log;
- max_skip = LABEL_TO_MAX_SKIP (label);
- }
+ align_flags alignment = LABEL_TO_ALIGNMENT (label);
+ max_alignment = align_flags::max (max_alignment, alignment);
rtx_jump_table_data *table = jump_table_for_label (label);
if (!table)
{
- log = LABEL_ALIGN (label);
- if (max_log < log)
- {
- max_log = log;
- max_skip = targetm.asm_out.label_align_max_skip (label);
- }
+ align_flags alignment = LABEL_ALIGN (label);
+ max_alignment = align_flags::max (max_alignment, alignment);
}
/* ADDR_VECs only take room if read-only data goes into the text
section. */
|| readonly_data_section == text_section)
&& table)
{
- log = ADDR_VEC_ALIGN (table);
- if (max_log < log)
- {
- max_log = log;
- max_skip = targetm.asm_out.label_align_max_skip (label);
- }
+ align_flags alignment = align_flags (ADDR_VEC_ALIGN (table));
+ max_alignment = align_flags::max (max_alignment, alignment);
}
- LABEL_TO_ALIGNMENT (label) = max_log;
- LABEL_TO_MAX_SKIP (label) = max_skip;
- max_log = 0;
- max_skip = 0;
+ LABEL_TO_ALIGNMENT (label) = max_alignment;
+ max_alignment = align_flags ();
}
else if (BARRIER_P (insn))
{
label = NEXT_INSN (label))
if (LABEL_P (label))
{
- log = LABEL_ALIGN_AFTER_BARRIER (insn);
- if (max_log < log)
- {
- max_log = log;
- max_skip = targetm.asm_out.label_align_after_barrier_max_skip (label);
- }
+ align_flags alignment
+ = align_flags (LABEL_ALIGN_AFTER_BARRIER (insn));
+ max_alignment = align_flags::max (max_alignment, alignment);
break;
}
}
alignment of n. */
uid_align = XCNEWVEC (rtx, max_uid);
- for (i = MAX_CODE_ALIGN; --i >= 0;)
+ for (i = MAX_CODE_ALIGN + 1; --i >= 0;)
align_tab[i] = NULL_RTX;
seq = get_last_insn ();
for (; seq; seq = PREV_INSN (seq))
{
int uid = INSN_UID (seq);
int log;
- log = (LABEL_P (seq) ? LABEL_TO_ALIGNMENT (seq) : 0);
+ log = (LABEL_P (seq) ? LABEL_TO_ALIGNMENT (seq).levels[0].log : 0);
uid_align[uid] = align_tab[0];
if (log)
{
/* Found an alignment label. */
+ gcc_checking_assert (log < MAX_CODE_ALIGN + 1);
uid_align[uid] = align_tab[log];
for (i = log - 1; i >= 0; i--)
align_tab[i] = seq;
max = shuid;
max_lab = lab;
}
- if (min_align > LABEL_TO_ALIGNMENT (lab))
- min_align = LABEL_TO_ALIGNMENT (lab);
+
+ int label_alignment = LABEL_TO_ALIGNMENT (lab).levels[0].log;
+ if (min_align > label_alignment)
+ min_align = label_alignment;
}
XEXP (pat, 2) = gen_rtx_LABEL_REF (Pmode, min_lab);
XEXP (pat, 3) = gen_rtx_LABEL_REF (Pmode, max_lab);
if (LABEL_P (insn))
{
- int log = LABEL_TO_ALIGNMENT (insn);
+ int log = LABEL_TO_ALIGNMENT (insn).levels[0].log;
if (log)
{
int align = 1 << log;
if (rtx_code_label *label = dyn_cast <rtx_code_label *> (insn))
{
- int log = LABEL_TO_ALIGNMENT (label);
+ int log = LABEL_TO_ALIGNMENT (label).levels[0].log;
#ifdef CASE_VECTOR_SHORTEN_MODE
/* If the mode of a following jump table was changed, we
prev = PREV_INSN (prev))
if (varying_length[INSN_UID (prev)] & 2)
{
- rel_align = LABEL_TO_ALIGNMENT (prev);
+ rel_align = LABEL_TO_ALIGNMENT (prev).levels[0].log;
break;
}
return count;
}
\f
-/* ??? This is probably the wrong place for these. */
-/* Structure recording the mapping from source file and directory
- names at compile time to those to be embedded in debug
- information. */
-struct debug_prefix_map
-{
- const char *old_prefix;
- const char *new_prefix;
- size_t old_len;
- size_t new_len;
- struct debug_prefix_map *next;
-};
-
-/* Linked list of such structures. */
-static debug_prefix_map *debug_prefix_maps;
-
-
-/* Record a debug file prefix mapping. ARG is the argument to
- -fdebug-prefix-map and must be of the form OLD=NEW. */
-
-void
-add_debug_prefix_map (const char *arg)
-{
- debug_prefix_map *map;
- const char *p;
-
- p = strchr (arg, '=');
- if (!p)
- {
- error ("invalid argument %qs to -fdebug-prefix-map", arg);
- return;
- }
- map = XNEW (debug_prefix_map);
- map->old_prefix = xstrndup (arg, p - arg);
- map->old_len = p - arg;
- p++;
- map->new_prefix = xstrdup (p);
- map->new_len = strlen (p);
- map->next = debug_prefix_maps;
- debug_prefix_maps = map;
-}
-
-/* Perform user-specified mapping of debug filename prefixes. Return
- the new name corresponding to FILENAME. */
-
-const char *
-remap_debug_filename (const char *filename)
-{
- debug_prefix_map *map;
- char *s;
- const char *name;
- size_t name_len;
-
- for (map = debug_prefix_maps; map; map = map->next)
- if (filename_ncmp (filename, map->old_prefix, map->old_len) == 0)
- break;
- if (!map)
- return filename;
- name = filename + map->old_len;
- name_len = strlen (name) + 1;
- s = (char *) alloca (name_len + map->new_len);
- memcpy (s, map->new_prefix, map->new_len);
- memcpy (s + map->new_len, name, name_len);
- return ggc_strdup (s);
-}
-\f
/* Return true if DWARF2 debug info can be emitted for DECL. */
static bool
dwarf2_debug_info_emitted_p (tree decl)
{
- if (write_symbols != DWARF2_DEBUG && write_symbols != VMS_AND_DWARF2_DEBUG)
+ /* When DWARF2 debug info is not generated internally. */
+ if (!dwarf_debuginfo_p () && !dwarf_based_debuginfo_p ())
return false;
if (DECL_IGNORED_P (decl))
break;
case NOTE_INSN_BEGIN_STMT:
+ case NOTE_INSN_INLINE_ENTRY:
this_block = LOCATION_BLOCK (NOTE_MARKER_LOCATION (insn));
goto set_cur_block_to_this_block;
return 0;
}
+/* Arrange for us to emit a source location note before any further
+ real insns or section changes, by setting the SEEN_NEXT_VIEW bit in
+ *SEEN, as long as we are keeping track of location views. The bit
+ indicates we have referenced the next view at the current PC, so we
+ have to emit it. This should be called next to the var_location
+ debug hook. */
+
+static inline void
+set_next_view_needed (int *seen)
+{
+ if (debug_variable_location_views)
+ *seen |= SEEN_NEXT_VIEW;
+}
+
+/* Clear the flag in *SEEN indicating we need to emit the next view.
+ This should be called next to the source_line debug hook. */
+
+static inline void
+clear_next_view_needed (int *seen)
+{
+ *seen &= ~SEEN_NEXT_VIEW;
+}
+
+/* Test whether we have a pending request to emit the next view in
+ *SEEN, and emit it if needed, clearing the request bit. */
+
+static inline void
+maybe_output_next_view (int *seen)
+{
+ if ((*seen & SEEN_NEXT_VIEW) != 0)
+ {
+ clear_next_view_needed (seen);
+ (*debug_hooks->source_line) (last_linenum, last_columnnum,
+ last_filename, last_discriminator,
+ false);
+ }
+}
+
+/* We want to emit param bindings (before the first begin_stmt) in the
+ initial view, if we are emitting views. To that end, we may
+ consume initial notes in the function, processing them in
+ final_start_function, before signaling the beginning of the
+ prologue, rather than in final.
+
+ We don't test whether the DECLs are PARM_DECLs: the assumption is
+ that there will be a NOTE_INSN_BEGIN_STMT marker before any
+ non-parameter NOTE_INSN_VAR_LOCATION. It's ok if the marker is not
+ there, we'll just have more variable locations bound in the initial
+ view, which is consistent with their being bound without any code
+ that would give them a value. */
+
+static inline bool
+in_initial_view_p (rtx_insn *insn)
+{
+ return (!DECL_IGNORED_P (current_function_decl)
+ && debug_variable_location_views
+ && insn && GET_CODE (insn) == NOTE
+ && (NOTE_KIND (insn) == NOTE_INSN_VAR_LOCATION
+ || NOTE_KIND (insn) == NOTE_INSN_DELETED));
+}
+
/* Output assembler code for the start of a function,
and initialize some of the variables in this file
for the new function. The label for the function and associated
FIRST is the first insn of the rtl for the function being compiled.
FILE is the file to write assembler code to.
+ SEEN should be initially set to zero, and it may be updated to
+ indicate we have references to the next location view, that would
+ require us to emit it at the current PC.
OPTIMIZE_P is nonzero if we should eliminate redundant
test and compare insns. */
-void
-final_start_function (rtx_insn *first, FILE *file,
- int optimize_p ATTRIBUTE_UNUSED)
+static void
+final_start_function_1 (rtx_insn **firstp, FILE *file, int *seen,
+ int optimize_p ATTRIBUTE_UNUSED)
{
block_depth = 0;
last_linenum = LOCATION_LINE (prologue_location);
last_columnnum = LOCATION_COLUMN (prologue_location);
last_discriminator = discriminator = 0;
+ last_bb_discriminator = bb_discriminator = 0;
+ force_source_line = false;
high_block_linenum = high_function_linenum = last_linenum;
if (flag_sanitize & SANITIZE_ADDRESS)
asan_function_start ();
+ rtx_insn *first = *firstp;
+ if (in_initial_view_p (first))
+ {
+ do
+ {
+ final_scan_insn (first, file, 0, 0, seen);
+ first = NEXT_INSN (first);
+ }
+ while (in_initial_view_p (first));
+ *firstp = first;
+ }
+
if (!DECL_IGNORED_P (current_function_decl))
- debug_hooks->begin_prologue (last_linenum, last_columnnum, last_filename);
+ debug_hooks->begin_prologue (last_linenum, last_columnnum,
+ last_filename);
if (!dwarf2_debug_info_emitted_p (current_function_decl))
dwarf2out_begin_prologue (0, 0, NULL);
+ if (DECL_IGNORED_P (current_function_decl) && last_linenum && last_filename)
+ debug_hooks->set_ignored_loc (last_linenum, last_columnnum, last_filename);
+
#ifdef LEAF_REG_REMAP
if (crtl->uses_only_leaf_regs)
leaf_renumber_regs (first);
TREE_ASM_WRITTEN (DECL_INITIAL (current_function_decl)) = 1;
}
- HOST_WIDE_INT min_frame_size = constant_lower_bound (get_frame_size ());
- if (warn_frame_larger_than
- && min_frame_size > frame_larger_than_size)
+ unsigned HOST_WIDE_INT min_frame_size
+ = constant_lower_bound (get_frame_size ());
+ if (min_frame_size > (unsigned HOST_WIDE_INT) warn_frame_larger_than_size)
{
/* Issue a warning */
warning (OPT_Wframe_larger_than_,
- "the frame size of %wd bytes is larger than %wd bytes",
- min_frame_size, frame_larger_than_size);
+ "the frame size of %wu bytes is larger than %wu bytes",
+ min_frame_size, warn_frame_larger_than_size);
}
/* First output the function prologue: code to set up the stack frame. */
profile_after_prologue (file);
}
+/* This is an exported final_start_function_1, callable without SEEN. */
+
+void
+final_start_function (rtx_insn *first, FILE *file,
+ int optimize_p ATTRIBUTE_UNUSED)
+{
+ int seen = 0;
+ final_start_function_1 (&first, file, &seen, optimize_p);
+ gcc_assert (seen == 0);
+}
+
static void
profile_after_prologue (FILE *file ATTRIBUTE_UNUSED)
{
/* Output assembler code for some insns: all or part of a function.
For description of args, see `final_start_function', above. */
-void
-final (rtx_insn *first, FILE *file, int optimize_p)
+static void
+final_1 (rtx_insn *first, FILE *file, int seen, int optimize_p)
{
rtx_insn *insn, *next;
- int seen = 0;
/* Used for -dA dump. */
basic_block *start_to_bb = NULL;
last_ignored_compare = 0;
- if (HAVE_cc0)
- for (insn = first; insn; insn = NEXT_INSN (insn))
- {
- /* If CC tracking across branches is enabled, record the insn which
- jumps to each branch only reached from one place. */
- if (optimize_p && JUMP_P (insn))
- {
- rtx lab = JUMP_LABEL (insn);
- if (lab && LABEL_P (lab) && LABEL_NUSES (lab) == 1)
- {
- LABEL_REFS (lab) = insn;
- }
- }
- }
-
init_recog ();
CC_STATUS_INIT;
}
else
insn_current_address = INSN_ADDRESSES (INSN_UID (insn));
+ /* final can be seen as an iteration of shorten_branches that
+ does nothing (since a fixed point has already been reached). */
+ insn_last_address = insn_current_address;
}
dump_basic_block_info (file, insn, start_to_bb, end_to_bb,
insn = final_scan_insn (insn, file, optimize_p, 0, &seen);
}
+ maybe_output_next_view (&seen);
+
if (flag_debug_asm)
{
free (start_to_bb);
delete_insn (insn);
}
}
+
+/* This is an exported final_1, callable without SEEN. */
+
+void
+final (rtx_insn *first, FILE *file, int optimize_p)
+{
+ /* Those that use the internal final_start_function_1/final_1 API
+ skip initial debug bind notes in final_start_function_1, and pass
+ the modified FIRST to final_1. But those that use the public
+ final_start_function/final APIs, final_start_function can't move
+ FIRST because it's not passed by reference, so if they were
+ skipped there, skip them again here. */
+ while (in_initial_view_p (first))
+ first = NEXT_INSN (first);
+
+ final_1 (first, file, 0, optimize_p);
+}
\f
const char *
-get_insn_template (int code, rtx insn)
+get_insn_template (int code, rtx_insn *insn)
{
switch (insn_data[code].output_format)
{
return insn_data[code].output.multi[which_alternative];
case INSN_OUTPUT_FORMAT_FUNCTION:
gcc_assert (insn);
- return (*insn_data[code].output.function) (recog_data.operand,
- as_a <rtx_insn *> (insn));
+ return (*insn_data[code].output.function) (recog_data.operand, insn);
default:
gcc_unreachable ();
if (!filename)
return;
- int line_size;
- const char *line = location_get_source_line (filename, linenum, &line_size);
+ char_span line = location_get_source_line (filename, linenum);
if (!line)
return;
fprintf (asm_out_file, "%s %s:%i: ", ASM_COMMENT_START, filename, linenum);
- /* "line" is not 0-terminated, so we must use line_size. */
- fwrite (line, 1, line_size, asm_out_file);
+ /* "line" is not 0-terminated, so we must use its length. */
+ fwrite (line.get_buffer (), 1, line.length (), asm_out_file);
fputc ('\n', asm_out_file);
}
+/* Judge if an absolute jump table is relocatable. */
+
+bool
+jumptable_relocatable (void)
+{
+ bool relocatable = false;
+
+ if (!CASE_VECTOR_PC_RELATIVE
+ && !targetm.asm_out.generate_pic_addr_diff_vec ()
+ && targetm_common.have_named_sections)
+ relocatable = targetm.asm_out.reloc_rw_mask ();
+
+ return relocatable;
+}
+
/* The final scan for one insn, INSN.
Args are same as in `final', except that INSN
is the insn being scanned.
debug information. We force the emission of a line note after
both NOTE_INSN_PROLOGUE_END and NOTE_INSN_FUNCTION_BEG. */
-rtx_insn *
-final_scan_insn (rtx_insn *insn, FILE *file, int optimize_p ATTRIBUTE_UNUSED,
- int nopeepholes ATTRIBUTE_UNUSED, int *seen)
+static rtx_insn *
+final_scan_insn_1 (rtx_insn *insn, FILE *file, int optimize_p ATTRIBUTE_UNUSED,
+ int nopeepholes ATTRIBUTE_UNUSED, int *seen)
{
-#if HAVE_cc0
- rtx set;
-#endif
rtx_insn *next;
rtx_jump_table_data *table;
break;
case NOTE_INSN_SWITCH_TEXT_SECTIONS:
+ maybe_output_next_view (seen);
+
+ output_function_exception_table (0);
+
+ if (targetm.asm_out.unwind_emit)
+ targetm.asm_out.unwind_emit (asm_out_file, insn);
+
in_cold_section_p = !in_cold_section_p;
+ gcc_checking_assert (in_cold_section_p);
if (in_cold_section_p)
cold_function_name
= clone_function_name (current_function_decl, "cold");
}
else if (!DECL_IGNORED_P (current_function_decl))
debug_hooks->switch_text_section ();
+ if (DECL_IGNORED_P (current_function_decl) && last_linenum
+ && last_filename)
+ debug_hooks->set_ignored_loc (last_linenum, last_columnnum,
+ last_filename);
switch_to_section (current_function_section ());
targetm.asm_out.function_switched_text_sections (asm_out_file,
ASM_OUTPUT_LABEL (asm_out_file,
IDENTIFIER_POINTER (cold_function_name));
#endif
+ if (dwarf2out_do_frame ()
+ && cfun->fde->dw_fde_second_begin != NULL)
+ ASM_OUTPUT_LABEL (asm_out_file, cfun->fde->dw_fde_second_begin);
}
break;
if (targetm.asm_out.unwind_emit)
targetm.asm_out.unwind_emit (asm_out_file, insn);
- discriminator = NOTE_BASIC_BLOCK (insn)->discriminator;
-
+ bb_discriminator = NOTE_BASIC_BLOCK (insn)->discriminator;
break;
case NOTE_INSN_EH_REGION_BEG:
break;
case NOTE_INSN_BLOCK_BEG:
- if (debug_info_level == DINFO_LEVEL_NORMAL
- || debug_info_level == DINFO_LEVEL_VERBOSE
- || write_symbols == DWARF2_DEBUG
- || write_symbols == VMS_AND_DWARF2_DEBUG
+ if (debug_info_level >= DINFO_LEVEL_NORMAL
+ || dwarf_debuginfo_p ()
|| write_symbols == VMS_DEBUG)
{
int n = BLOCK_NUMBER (NOTE_BLOCK (insn));
override_filename = LOCATION_FILE (*locus_ptr);
override_linenum = LOCATION_LINE (*locus_ptr);
override_columnnum = LOCATION_COLUMN (*locus_ptr);
+ override_discriminator = compute_discriminator (*locus_ptr);
}
}
break;
case NOTE_INSN_BLOCK_END:
- if (debug_info_level == DINFO_LEVEL_NORMAL
- || debug_info_level == DINFO_LEVEL_VERBOSE
- || write_symbols == DWARF2_DEBUG
- || write_symbols == VMS_AND_DWARF2_DEBUG
+ maybe_output_next_view (seen);
+
+ if (debug_info_level >= DINFO_LEVEL_NORMAL
+ || dwarf_debuginfo_p ()
|| write_symbols == VMS_DEBUG)
{
int n = BLOCK_NUMBER (NOTE_BLOCK (insn));
override_filename = LOCATION_FILE (*locus_ptr);
override_linenum = LOCATION_LINE (*locus_ptr);
override_columnnum = LOCATION_COLUMN (*locus_ptr);
+ override_discriminator = compute_discriminator (*locus_ptr);
}
else
{
override_filename = NULL;
override_linenum = 0;
override_columnnum = 0;
+ override_discriminator = 0;
}
}
break;
break;
case NOTE_INSN_VAR_LOCATION:
- case NOTE_INSN_CALL_ARG_LOCATION:
if (!DECL_IGNORED_P (current_function_decl))
- debug_hooks->var_location (insn);
+ {
+ debug_hooks->var_location (insn);
+ set_next_view_needed (seen);
+ }
break;
case NOTE_INSN_BEGIN_STMT:
if (!DECL_IGNORED_P (current_function_decl)
&& notice_source_line (insn, NULL))
{
+ output_source_line:
(*debug_hooks->source_line) (last_linenum, last_columnnum,
last_filename, last_discriminator,
true);
+ clear_next_view_needed (seen);
+ }
+ break;
+
+ case NOTE_INSN_INLINE_ENTRY:
+ gcc_checking_assert (cfun->debug_nonbind_markers);
+ if (!DECL_IGNORED_P (current_function_decl)
+ && notice_source_line (insn, NULL))
+ {
+ (*debug_hooks->inline_entry) (LOCATION_BLOCK
+ (NOTE_MARKER_LOCATION (insn)));
+ goto output_source_line;
}
break;
some insn, e.g. sh.c output_branchy_insn. */
if (CODE_LABEL_NUMBER (insn) <= max_labelno)
{
- int align = LABEL_TO_ALIGNMENT (insn);
-#ifdef ASM_OUTPUT_MAX_SKIP_ALIGN
- int max_skip = LABEL_TO_MAX_SKIP (insn);
-#endif
-
- if (align && NEXT_INSN (insn))
+ align_flags alignment = LABEL_TO_ALIGNMENT (insn);
+ if (alignment.levels[0].log && NEXT_INSN (insn))
{
#ifdef ASM_OUTPUT_MAX_SKIP_ALIGN
- ASM_OUTPUT_MAX_SKIP_ALIGN (file, align, max_skip);
+ /* Output both primary and secondary alignment. */
+ ASM_OUTPUT_MAX_SKIP_ALIGN (file, alignment.levels[0].log,
+ alignment.levels[0].maxskip);
+ ASM_OUTPUT_MAX_SKIP_ALIGN (file, alignment.levels[1].log,
+ alignment.levels[1].maxskip);
#else
#ifdef ASM_OUTPUT_ALIGN_WITH_NOP
- ASM_OUTPUT_ALIGN_WITH_NOP (file, align);
+ ASM_OUTPUT_ALIGN_WITH_NOP (file, alignment.levels[0].log);
#else
- ASM_OUTPUT_ALIGN (file, align);
+ ASM_OUTPUT_ALIGN (file, alignment.levels[0].log);
#endif
#endif
}
int log_align;
switch_to_section (targetm.asm_out.function_rodata_section
- (current_function_decl));
+ (current_function_decl,
+ jumptable_relocatable ()));
#ifdef ADDR_VEC_ALIGN
log_align = ADDR_VEC_ALIGN (table);
|| GET_CODE (body) == CLOBBER)
break;
-#if HAVE_cc0
- {
- /* If there is a REG_CC_SETTER note on this insn, it means that
- the setting of the condition code was done in the delay slot
- of the insn that branched here. So recover the cc status
- from the insn that set it. */
-
- rtx note = find_reg_note (insn, REG_CC_SETTER, NULL_RTX);
- if (note)
- {
- rtx_insn *other = as_a <rtx_insn *> (XEXP (note, 0));
- NOTICE_UPDATE_CC (PATTERN (other), other);
- cc_prev_status = cc_status;
- }
- }
-#endif
-
/* Detect insns that are really jump-tables
and output them as such. */
if (! JUMP_TABLES_IN_TEXT_SECTION)
switch_to_section (targetm.asm_out.function_rodata_section
- (current_function_decl));
+ (current_function_decl,
+ jumptable_relocatable ()));
else
switch_to_section (current_function_section ());
switch_to_section (current_function_section ());
+ if (debug_variable_location_views
+ && !DECL_IGNORED_P (current_function_decl))
+ debug_hooks->var_location (insn);
+
break;
}
/* Output this line note if it is the first or the last line
(*debug_hooks->source_line) (last_linenum, last_columnnum,
last_filename, last_discriminator,
is_stmt);
+ clear_next_view_needed (seen);
}
+ else
+ maybe_output_next_view (seen);
+
+ gcc_checking_assert (!DEBUG_INSN_P (insn));
if (GET_CODE (body) == PARALLEL
&& GET_CODE (XVECEXP (body, 0, 0)) == ASM_INPUT)
body = PATTERN (insn);
-#if HAVE_cc0
- set = single_set (insn);
-
- /* Check for redundant test and compare instructions
- (when the condition codes are already set up as desired).
- This is done only when optimizing; if not optimizing,
- it should be possible for the user to alter a variable
- with the debugger in between statements
- and the next statement should reexamine the variable
- to compute the condition codes. */
-
- if (optimize_p)
- {
- if (set
- && GET_CODE (SET_DEST (set)) == CC0
- && insn != last_ignored_compare)
- {
- rtx src1, src2;
- if (GET_CODE (SET_SRC (set)) == SUBREG)
- SET_SRC (set) = alter_subreg (&SET_SRC (set), true);
-
- src1 = SET_SRC (set);
- src2 = NULL_RTX;
- if (GET_CODE (SET_SRC (set)) == COMPARE)
- {
- if (GET_CODE (XEXP (SET_SRC (set), 0)) == SUBREG)
- XEXP (SET_SRC (set), 0)
- = alter_subreg (&XEXP (SET_SRC (set), 0), true);
- if (GET_CODE (XEXP (SET_SRC (set), 1)) == SUBREG)
- XEXP (SET_SRC (set), 1)
- = alter_subreg (&XEXP (SET_SRC (set), 1), true);
- if (XEXP (SET_SRC (set), 1)
- == CONST0_RTX (GET_MODE (XEXP (SET_SRC (set), 0))))
- src2 = XEXP (SET_SRC (set), 0);
- }
- if ((cc_status.value1 != 0
- && rtx_equal_p (src1, cc_status.value1))
- || (cc_status.value2 != 0
- && rtx_equal_p (src1, cc_status.value2))
- || (src2 != 0 && cc_status.value1 != 0
- && rtx_equal_p (src2, cc_status.value1))
- || (src2 != 0 && cc_status.value2 != 0
- && rtx_equal_p (src2, cc_status.value2)))
- {
- /* Don't delete insn if it has an addressing side-effect. */
- if (! FIND_REG_INC_NOTE (insn, NULL_RTX)
- /* or if anything in it is volatile. */
- && ! volatile_refs_p (PATTERN (insn)))
- {
- /* We don't really delete the insn; just ignore it. */
- last_ignored_compare = insn;
- break;
- }
- }
- }
- }
-
- /* If this is a conditional branch, maybe modify it
- if the cc's are in a nonstandard state
- so that it accomplishes the same thing that it would
- do straightforwardly if the cc's were set up normally. */
-
- if (cc_status.flags != 0
- && JUMP_P (insn)
- && GET_CODE (body) == SET
- && SET_DEST (body) == pc_rtx
- && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE
- && COMPARISON_P (XEXP (SET_SRC (body), 0))
- && XEXP (XEXP (SET_SRC (body), 0), 0) == cc0_rtx)
- {
- /* This function may alter the contents of its argument
- and clear some of the cc_status.flags bits.
- It may also return 1 meaning condition now always true
- or -1 meaning condition now always false
- or 2 meaning condition nontrivial but altered. */
- int result = alter_cond (XEXP (SET_SRC (body), 0));
- /* If condition now has fixed value, replace the IF_THEN_ELSE
- with its then-operand or its else-operand. */
- if (result == 1)
- SET_SRC (body) = XEXP (SET_SRC (body), 1);
- if (result == -1)
- SET_SRC (body) = XEXP (SET_SRC (body), 2);
-
- /* The jump is now either unconditional or a no-op.
- If it has become a no-op, don't try to output it.
- (It would not be recognized.) */
- if (SET_SRC (body) == pc_rtx)
- {
- delete_insn (insn);
- break;
- }
- else if (ANY_RETURN_P (SET_SRC (body)))
- /* Replace (set (pc) (return)) with (return). */
- PATTERN (insn) = body = SET_SRC (body);
-
- /* Rerecognize the instruction if it has changed. */
- if (result != 0)
- INSN_CODE (insn) = -1;
- }
-
- /* If this is a conditional trap, maybe modify it if the cc's
- are in a nonstandard state so that it accomplishes the same
- thing that it would do straightforwardly if the cc's were
- set up normally. */
- if (cc_status.flags != 0
- && NONJUMP_INSN_P (insn)
- && GET_CODE (body) == TRAP_IF
- && COMPARISON_P (TRAP_CONDITION (body))
- && XEXP (TRAP_CONDITION (body), 0) == cc0_rtx)
- {
- /* This function may alter the contents of its argument
- and clear some of the cc_status.flags bits.
- It may also return 1 meaning condition now always true
- or -1 meaning condition now always false
- or 2 meaning condition nontrivial but altered. */
- int result = alter_cond (TRAP_CONDITION (body));
-
- /* If TRAP_CONDITION has become always false, delete the
- instruction. */
- if (result == -1)
- {
- delete_insn (insn);
- break;
- }
-
- /* If TRAP_CONDITION has become always true, replace
- TRAP_CONDITION with const_true_rtx. */
- if (result == 1)
- TRAP_CONDITION (body) = const_true_rtx;
-
- /* Rerecognize the instruction if it has changed. */
- if (result != 0)
- INSN_CODE (insn) = -1;
- }
-
- /* Make same adjustments to instructions that examine the
- condition codes without jumping and instructions that
- handle conditional moves (if this machine has either one). */
-
- if (cc_status.flags != 0
- && set != 0)
- {
- rtx cond_rtx, then_rtx, else_rtx;
-
- if (!JUMP_P (insn)
- && GET_CODE (SET_SRC (set)) == IF_THEN_ELSE)
- {
- cond_rtx = XEXP (SET_SRC (set), 0);
- then_rtx = XEXP (SET_SRC (set), 1);
- else_rtx = XEXP (SET_SRC (set), 2);
- }
- else
- {
- cond_rtx = SET_SRC (set);
- then_rtx = const_true_rtx;
- else_rtx = const0_rtx;
- }
-
- if (COMPARISON_P (cond_rtx)
- && XEXP (cond_rtx, 0) == cc0_rtx)
- {
- int result;
- result = alter_cond (cond_rtx);
- if (result == 1)
- validate_change (insn, &SET_SRC (set), then_rtx, 0);
- else if (result == -1)
- validate_change (insn, &SET_SRC (set), else_rtx, 0);
- else if (result == 2)
- INSN_CODE (insn) = -1;
- if (SET_DEST (set) == SET_SRC (set))
- delete_insn (insn);
- }
- }
-
-#endif
-
/* Do machine-specific peephole optimizations if desired. */
if (HAVE_peephole && optimize_p && !flag_no_peephole && !nopeepholes)
&& GET_CODE (PATTERN (insn)) == COND_EXEC)
current_insn_predicate = COND_EXEC_TEST (PATTERN (insn));
-#if HAVE_cc0
- cc_prev_status = cc_status;
-
- /* Update `cc_status' for this instruction.
- The instruction's output routine may change it further.
- If the output routine for a jump insn needs to depend
- on the cc status, it should look at cc_prev_status. */
-
- NOTICE_UPDATE_CC (body, insn);
-#endif
-
current_output_insn = debug_insn = insn;
/* Find the proper template for this insn. */
/* Let the debug info back-end know about this call. We do this only
after the instruction has been emitted because labels that may be
created to reference the call instruction must appear after it. */
- if (call_insn != NULL && !DECL_IGNORED_P (current_function_decl))
+ if ((debug_variable_location_views || call_insn != NULL)
+ && !DECL_IGNORED_P (current_function_decl))
debug_hooks->var_location (insn);
current_output_insn = debug_insn = 0;
}
return NEXT_INSN (insn);
}
+
+/* This is a wrapper around final_scan_insn_1 that allows ports to
+ call it recursively without a known value for SEEN. The value is
+ saved at the outermost call, and recovered for recursive calls.
+ Recursive calls MUST pass NULL, or the same pointer if they can
+ otherwise get to it. */
+
+rtx_insn *
+final_scan_insn (rtx_insn *insn, FILE *file, int optimize_p,
+ int nopeepholes, int *seen)
+{
+ static int *enclosing_seen;
+ static int recursion_counter;
+
+ gcc_assert (seen || recursion_counter);
+ gcc_assert (!recursion_counter || !seen || seen == enclosing_seen);
+
+ if (!recursion_counter++)
+ enclosing_seen = seen;
+ else if (!seen)
+ seen = enclosing_seen;
+
+ rtx_insn *ret = final_scan_insn_1 (insn, file, optimize_p, nopeepholes, seen);
+
+ if (!--recursion_counter)
+ enclosing_seen = NULL;
+
+ return ret;
+}
+
\f
+
+/* Map DECLs to instance discriminators. This is allocated and
+ defined in ada/gcc-interfaces/trans.c, when compiling with -gnateS.
+ Mappings from this table are saved and restored for LTO, so
+ link-time compilation will have this map set, at least in
+ partitions containing at least one DECL with an associated instance
+ discriminator. */
+
+decl_to_instance_map_t *decl_to_instance_map;
+
+/* Return the instance number assigned to DECL. */
+
+static inline int
+map_decl_to_instance (const_tree decl)
+{
+ int *inst;
+
+ if (!decl_to_instance_map || !decl || !DECL_P (decl))
+ return 0;
+
+ inst = decl_to_instance_map->get (decl);
+
+ if (!inst)
+ return 0;
+
+ return *inst;
+}
+
+/* Set DISCRIMINATOR to the appropriate value, possibly derived from LOC. */
+
+static inline int
+compute_discriminator (location_t loc)
+{
+ int discriminator;
+
+ if (!decl_to_instance_map)
+ discriminator = bb_discriminator;
+ else
+ {
+ tree block = LOCATION_BLOCK (loc);
+
+ while (block && TREE_CODE (block) == BLOCK
+ && !inlined_function_outer_scope_p (block))
+ block = BLOCK_SUPERCONTEXT (block);
+
+ tree decl;
+
+ if (!block)
+ decl = current_function_decl;
+ else if (DECL_P (block))
+ decl = block;
+ else
+ decl = block_ultimate_origin (block);
+
+ discriminator = map_decl_to_instance (decl);
+ }
+
+ return discriminator;
+}
+
/* Return whether a source line note needs to be emitted before INSN.
Sets IS_STMT to TRUE if the line should be marked as a possible
breakpoint location. */
{
location_t loc = NOTE_MARKER_LOCATION (insn);
expanded_location xloc = expand_location (loc);
- if (xloc.line == 0)
- {
- gcc_checking_assert (LOCATION_LOCUS (loc) == UNKNOWN_LOCATION
- || LOCATION_LOCUS (loc) == BUILTINS_LOCATION);
- return false;
- }
+ if (xloc.line == 0
+ && (LOCATION_LOCUS (loc) == UNKNOWN_LOCATION
+ || LOCATION_LOCUS (loc) == BUILTINS_LOCATION))
+ return false;
+
filename = xloc.file;
linenum = xloc.line;
columnnum = xloc.column;
+ discriminator = compute_discriminator (loc);
force_source_line = true;
}
else if (override_filename)
filename = override_filename;
linenum = override_linenum;
columnnum = override_columnnum;
+ discriminator = override_discriminator;
}
else if (INSN_HAS_LOCATION (insn))
{
filename = xloc.file;
linenum = xloc.line;
columnnum = xloc.column;
+ discriminator = compute_discriminator (INSN_LOCATION (insn));
}
else
{
filename = NULL;
linenum = 0;
columnnum = 0;
+ discriminator = 0;
}
if (filename == NULL)
return *xp;
}
\f
-#if HAVE_cc0
-
-/* Given BODY, the body of a jump instruction, alter the jump condition
- as required by the bits that are set in cc_status.flags.
- Not all of the bits there can be handled at this level in all cases.
-
- The value is normally 0.
- 1 means that the condition has become always true.
- -1 means that the condition has become always false.
- 2 means that COND has been altered. */
-
-static int
-alter_cond (rtx cond)
-{
- int value = 0;
-
- if (cc_status.flags & CC_REVERSED)
- {
- value = 2;
- PUT_CODE (cond, swap_condition (GET_CODE (cond)));
- }
-
- if (cc_status.flags & CC_INVERTED)
- {
- value = 2;
- PUT_CODE (cond, reverse_condition (GET_CODE (cond)));
- }
-
- if (cc_status.flags & CC_NOT_POSITIVE)
- switch (GET_CODE (cond))
- {
- case LE:
- case LEU:
- case GEU:
- /* Jump becomes unconditional. */
- return 1;
-
- case GT:
- case GTU:
- case LTU:
- /* Jump becomes no-op. */
- return -1;
-
- case GE:
- PUT_CODE (cond, EQ);
- value = 2;
- break;
-
- case LT:
- PUT_CODE (cond, NE);
- value = 2;
- break;
-
- default:
- break;
- }
-
- if (cc_status.flags & CC_NOT_NEGATIVE)
- switch (GET_CODE (cond))
- {
- case GE:
- case GEU:
- /* Jump becomes unconditional. */
- return 1;
-
- case LT:
- case LTU:
- /* Jump becomes no-op. */
- return -1;
-
- case LE:
- case LEU:
- PUT_CODE (cond, EQ);
- value = 2;
- break;
-
- case GT:
- case GTU:
- PUT_CODE (cond, NE);
- value = 2;
- break;
-
- default:
- break;
- }
-
- if (cc_status.flags & CC_NO_OVERFLOW)
- switch (GET_CODE (cond))
- {
- case GEU:
- /* Jump becomes unconditional. */
- return 1;
-
- case LEU:
- PUT_CODE (cond, EQ);
- value = 2;
- break;
-
- case GTU:
- PUT_CODE (cond, NE);
- value = 2;
- break;
-
- case LTU:
- /* Jump becomes no-op. */
- return -1;
-
- default:
- break;
- }
-
- if (cc_status.flags & (CC_Z_IN_NOT_N | CC_Z_IN_N))
- switch (GET_CODE (cond))
- {
- default:
- gcc_unreachable ();
-
- case NE:
- PUT_CODE (cond, cc_status.flags & CC_Z_IN_N ? GE : LT);
- value = 2;
- break;
-
- case EQ:
- PUT_CODE (cond, cc_status.flags & CC_Z_IN_N ? LT : GE);
- value = 2;
- break;
- }
-
- if (cc_status.flags & CC_NOT_SIGNED)
- /* The flags are valid if signed condition operators are converted
- to unsigned. */
- switch (GET_CODE (cond))
- {
- case LE:
- PUT_CODE (cond, LEU);
- value = 2;
- break;
-
- case LT:
- PUT_CODE (cond, LTU);
- value = 2;
- break;
-
- case GT:
- PUT_CODE (cond, GTU);
- value = 2;
- break;
-
- case GE:
- PUT_CODE (cond, GEU);
- value = 2;
- break;
-
- default:
- break;
- }
-
- return value;
-}
-#endif
-\f
/* Report inconsistency between the assembler template and the operands.
In an `asm', it's the user's fault; otherwise, the compiler's fault. */
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
if (CALL_P (insn)
- && ! SIBLING_CALL_P (insn))
+ && ! SIBLING_CALL_P (insn)
+ && ! FAKE_CALL_P (insn))
return 0;
if (NONJUMP_INSN_P (insn)
&& GET_CODE (PATTERN (insn)) == SEQUENCE
/* Turn debug markers into notes if the var-tracking pass has not
been invoked. */
if (!flag_var_tracking && MAY_HAVE_DEBUG_MARKER_INSNS)
- variable_tracking_main ();
+ delete_vta_debug_insns (false);
assemble_start_function (current_function_decl, fnname);
- final_start_function (get_insns (), asm_out_file, optimize);
- final (get_insns (), asm_out_file, optimize);
+ rtx_insn *first = get_insns ();
+ int seen = 0;
+ final_start_function_1 (&first, asm_out_file, &seen, optimize);
+ final_1 (first, asm_out_file, seen, optimize);
if (flag_ipa_ra
- && !lookup_attribute ("noipa", DECL_ATTRIBUTES (current_function_decl)))
+ && !lookup_attribute ("noipa", DECL_ATTRIBUTES (current_function_decl))
+ /* Functions with naked attributes are supported only with basic asm
+ statements in the body, thus for supported use cases the information
+ on clobbered registers is not available. */
+ && !lookup_attribute ("naked", DECL_ATTRIBUTES (current_function_decl)))
collect_fn_hard_reg_usage ();
final_end_function ();
/* The IA-64 ".handlerdata" directive must be issued before the ".endp"
directive that closes the procedure descriptor. Similarly, for x64 SEH.
Otherwise it's not strictly necessary, but it doesn't hurt either. */
- output_function_exception_table (fnname);
+ output_function_exception_table (crtl->has_bb_partition ? 1 : 0);
assemble_end_function (current_function_decl, fnname);
SET_NEXT_INSN (insn) = NULL;
SET_PREV_INSN (insn) = NULL;
+ rtx_insn *call_insn = insn;
+ if (NONJUMP_INSN_P (call_insn)
+ && GET_CODE (PATTERN (call_insn)) == SEQUENCE)
+ {
+ rtx_sequence *seq = as_a <rtx_sequence *> (PATTERN (call_insn));
+ call_insn = seq->insn (0);
+ }
+ if (CALL_P (call_insn))
+ {
+ rtx note
+ = find_reg_note (call_insn, REG_CALL_ARG_LOCATION, NULL_RTX);
+ if (note)
+ remove_note (call_insn, note);
+ }
+
if (final_output
- && (!NOTE_P (insn) ||
- (NOTE_KIND (insn) != NOTE_INSN_VAR_LOCATION
- && NOTE_KIND (insn) != NOTE_INSN_BEGIN_STMT
- && NOTE_KIND (insn) != NOTE_INSN_CALL_ARG_LOCATION
- && NOTE_KIND (insn) != NOTE_INSN_BLOCK_BEG
- && NOTE_KIND (insn) != NOTE_INSN_BLOCK_END
- && NOTE_KIND (insn) != NOTE_INSN_DELETED_DEBUG_LABEL)))
+ && (!NOTE_P (insn)
+ || (NOTE_KIND (insn) != NOTE_INSN_VAR_LOCATION
+ && NOTE_KIND (insn) != NOTE_INSN_BEGIN_STMT
+ && NOTE_KIND (insn) != NOTE_INSN_INLINE_ENTRY
+ && NOTE_KIND (insn) != NOTE_INSN_BLOCK_BEG
+ && NOTE_KIND (insn) != NOTE_INSN_BLOCK_END
+ && NOTE_KIND (insn) != NOTE_INSN_DELETED_DEBUG_LABEL)))
print_rtl_single (final_output, insn);
}
/* We can reduce stack alignment on call site only when we are sure that
the function body just produced will be actually used in the final
executable. */
- if (decl_binds_to_current_def_p (current_function_decl))
+ if (flag_ipa_stack_alignment
+ && decl_binds_to_current_def_p (current_function_decl))
{
unsigned int pref = crtl->preferred_stack_boundary;
if (crtl->stack_alignment_needed > crtl->preferred_stack_boundary)
if (!targetm.call_fusage_contains_non_callee_clobbers)
return;
- CLEAR_HARD_REG_SET (function_used_regs);
+ /* Be conservative - mark fixed and global registers as used. */
+ function_used_regs = fixed_reg_set;
+
+#ifdef STACK_REGS
+ /* Handle STACK_REGS conservatively, since the df-framework does not
+ provide accurate information for them. */
+
+ for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++)
+ SET_HARD_REG_BIT (function_used_regs, i);
+#endif
for (insn = get_insns (); insn != NULL_RTX; insn = next_insn (insn))
{
if (CALL_P (insn)
&& !self_recursive_call_p (insn))
- {
- if (!get_call_reg_set_usage (insn, &insn_used_regs,
- call_used_reg_set))
- return;
-
- IOR_HARD_REG_SET (function_used_regs, insn_used_regs);
- }
+ function_used_regs
+ |= insn_callee_abi (insn).full_and_partial_reg_clobbers ();
find_all_hard_reg_sets (insn, &insn_used_regs, false);
- IOR_HARD_REG_SET (function_used_regs, insn_used_regs);
- }
-
- /* Be conservative - mark fixed and global registers as used. */
- IOR_HARD_REG_SET (function_used_regs, fixed_reg_set);
-
-#ifdef STACK_REGS
- /* Handle STACK_REGS conservatively, since the df-framework does not
- provide accurate information for them. */
+ function_used_regs |= insn_used_regs;
- for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++)
- SET_HARD_REG_BIT (function_used_regs, i);
-#endif
+ if (hard_reg_set_subset_p (crtl->abi->full_and_partial_reg_clobbers (),
+ function_used_regs))
+ return;
+ }
- /* The information we have gathered is only interesting if it exposes a
- register from the call_used_regs that is not used in this function. */
- if (hard_reg_set_subset_p (call_used_reg_set, function_used_regs))
- return;
+ /* Mask out fully-saved registers, so that they don't affect equality
+ comparisons between function_abis. */
+ function_used_regs &= crtl->abi->full_and_partial_reg_clobbers ();
node = cgraph_node::rtl_info (current_function_decl);
gcc_assert (node != NULL);
- COPY_HARD_REG_SET (node->function_used_regs, function_used_regs);
- node->function_used_regs_valid = 1;
-}
-
-/* Get the declaration of the function called by INSN. */
-
-static tree
-get_call_fndecl (rtx_insn *insn)
-{
- rtx note, datum;
-
- note = find_reg_note (insn, REG_CALL_DECL, NULL_RTX);
- if (note == NULL_RTX)
- return NULL_TREE;
-
- datum = XEXP (note, 0);
- if (datum != NULL_RTX)
- return SYMBOL_REF_DECL (datum);
-
- return NULL_TREE;
-}
-
-/* Return the cgraph_rtl_info of the function called by INSN. Returns NULL for
- call targets that can be overwritten. */
-
-static struct cgraph_rtl_info *
-get_call_cgraph_rtl_info (rtx_insn *insn)
-{
- tree fndecl;
-
- if (insn == NULL_RTX)
- return NULL;
-
- fndecl = get_call_fndecl (insn);
- if (fndecl == NULL_TREE
- || !decl_binds_to_current_def_p (fndecl))
- return NULL;
-
- return cgraph_node::rtl_info (fndecl);
-}
-
-/* Find hard registers used by function call instruction INSN, and return them
- in REG_SET. Return DEFAULT_SET in REG_SET if not found. */
-
-bool
-get_call_reg_set_usage (rtx_insn *insn, HARD_REG_SET *reg_set,
- HARD_REG_SET default_set)
-{
- if (flag_ipa_ra)
- {
- struct cgraph_rtl_info *node = get_call_cgraph_rtl_info (insn);
- if (node != NULL
- && node->function_used_regs_valid)
- {
- COPY_HARD_REG_SET (*reg_set, node->function_used_regs);
- AND_HARD_REG_SET (*reg_set, default_set);
- return true;
- }
- }
-
- COPY_HARD_REG_SET (*reg_set, default_set);
- return false;
+ node->function_used_regs = function_used_regs;
}