Lehua Ding <lehua.d...@rivai.ai> writes: > This patch adds a live_subreg problem to extend the original live_reg to > track the liveness of subreg. We will only try to trace speudo registers > who's mode size is a multiple of nature size and eventually a small portion > of the inside will appear to use subreg. With live_reg problem, live_subreg > prbolem will have the following output. full_in/out mean the entire pesudo > live in/out, partial_in/out mean the subregs of the pesudo are live in/out, > and range_in/out indicates which part of the pesudo is live. all_in/out is > the union of full_in/out and partial_in/out: > > bitmap_head all_in, full_in; > bitmap_head all_out, full_out; > bitmap_head partial_in; > bitmap_head partial_out; > subregs_live *range_in = NULL; > subregs_live *range_out = NULL;
I haven't fully processed the patch yet, sorry. And I think I might be about to cover things that you dealt with elsewhere. My assumption going into this was that a subreg liveness tracker would work as follows: - First we would work out which registers need to have subreg tracking. This could be done ahead of time by iterating over regno_reg_rtx. The condition in need_track_subreg looks like the correct one. For every other register, subreg liveness degenerates to the existing liveness problems. Such registers can be ignored. - We would assign a unique identifier to each subreg that we want to track, with subregs for the same register being consecutive. - There would be a mapping from pseudo registers to the first subreg that we want to track. The mapping would probably just be a linear array, but perhaps there are times when something more compact is appropriate. - The dataflow problem itself would then be very similar to the existing ones. But rather than computing bitmaps with a single bit per register, we'd be computing bitmaps that have N bits for N-register pseudos (and no bits for single-register pseudos). - There would be helper functions that consumers could use to iterate over a block. E.g. for a backwards walk over a block, a consumer would start with the bitmap of live-out subregs. It would then use these helper functions to keep the values up-to-date as it moves up through the block. That's done for normal liveness via the df_simulate_* helpers. But now that the codebase is C++, it might be more convenient for the subreg code to provide classes for walking a block. That should be relatively compile-time-friendly, although I agree with Vlad of course that DF does have efficiency problems. The nature of the way it works makes it at least O(#blocks * #regs). Did you consider doing it that way? Or does it not provide the information that you need? Thanks, Richard > gcc/ChangeLog: > > * Makefile.in: Add new object file. > * df-problems.cc (struct df_live_subreg_problem_data): > The data of the new live_subreg problem. > (need_track_subreg): New function. > (get_range): Ditto. > (remove_subreg_range): Ditto. > (add_subreg_range): Ditto. > (df_live_subreg_free_bb_info): Ditto. > (df_live_subreg_alloc): Ditto. > (df_live_subreg_reset): Ditto. > (df_live_subreg_bb_local_compute): Ditto. > (df_live_subreg_local_compute): Ditto. > (df_live_subreg_init): Ditto. > (df_live_subreg_check_result): Ditto. > (df_live_subreg_confluence_0): Ditto. > (df_live_subreg_confluence_n): Ditto. > (df_live_subreg_transfer_function): Ditto. > (df_live_subreg_finalize): Ditto. > (df_live_subreg_free): Ditto. > (df_live_subreg_top_dump): Ditto. > (df_live_subreg_bottom_dump): Ditto. > (df_live_subreg_add_problem): Ditto. > * df.h (enum df_problem_id): Add live_subreg id. > (DF_LIVE_SUBREG_INFO): Data accessor. > (DF_LIVE_SUBREG_IN): Ditto. > (DF_LIVE_SUBREG_OUT): Ditto. > (DF_LIVE_SUBREG_FULL_IN): Ditto. > (DF_LIVE_SUBREG_FULL_OUT): Ditto. > (DF_LIVE_SUBREG_PARTIAL_IN): Ditto. > (DF_LIVE_SUBREG_PARTIAL_OUT): Ditto. > (DF_LIVE_SUBREG_RANGE_IN): Ditto. > (DF_LIVE_SUBREG_RANGE_OUT): Ditto. > (class subregs_live): New class. > (class basic_block_subreg_live_info): Ditto. > (class df_live_subreg_bb_info): Ditto. > (df_live_subreg): Ditto. > (df_live_subreg_add_problem): Ditto. > (df_live_subreg_finalize): Ditto. > (class subreg_range): Ditto. > (need_track_subreg): Ditto. > (remove_subreg_range): Ditto. > (add_subreg_range): Ditto. > (df_live_subreg_get_bb_info): Ditto. > * regs.h (get_nblocks): Helper function. > * timevar.def (TV_DF_LIVE_SUBREG): New timevar. > * subreg-live-range.cc: New file. > * subreg-live-range.h: New file. > > --- > gcc/Makefile.in | 1 + > gcc/df-problems.cc | 889 ++++++++++++++++++++++++++++++++++++++- > gcc/df.h | 67 +++ > gcc/regs.h | 7 + > gcc/subreg-live-range.cc | 628 +++++++++++++++++++++++++++ > gcc/subreg-live-range.h | 333 +++++++++++++++ > gcc/timevar.def | 1 + > 7 files changed, 1925 insertions(+), 1 deletion(-) > create mode 100644 gcc/subreg-live-range.cc > create mode 100644 gcc/subreg-live-range.h > > diff --git a/gcc/Makefile.in b/gcc/Makefile.in > index 29cec21c825..e4403b5a30c 100644 > --- a/gcc/Makefile.in > +++ b/gcc/Makefile.in > @@ -1675,6 +1675,7 @@ OBJS = \ > store-motion.o \ > streamer-hooks.o \ > stringpool.o \ > + subreg-live-range.o \ > substring-locations.o \ > target-globals.o \ > targhooks.o \ > diff --git a/gcc/df-problems.cc b/gcc/df-problems.cc > index d2cfaf7f50f..2585c762fd1 100644 > --- a/gcc/df-problems.cc > +++ b/gcc/df-problems.cc > @@ -28,6 +28,7 @@ along with GCC; see the file COPYING3. If not see > #include "target.h" > #include "rtl.h" > #include "df.h" > +#include "subreg-live-range.h" > #include "memmodel.h" > #include "tm_p.h" > #include "insn-config.h" > @@ -1344,8 +1345,894 @@ df_lr_verify_transfer_functions (void) > bitmap_clear (&all_blocks); > } > > +/*---------------------------------------------------------------------------- > + REGISTER AND SUBREG LIVES > + Like DF_RL, but fine-grained tracking of subreg lifecycle. > + > ----------------------------------------------------------------------------*/ > + > +/* Private data used to verify the solution for this problem. */ > +struct df_live_subreg_problem_data > +{ > + /* An obstack for the bitmaps we need for this problem. */ > + bitmap_obstack live_subreg_bitmaps; > + bool has_subreg_live_p; > +}; > + > +/* Helper functions */ > + > +/* Return true if REGNO is a pseudo and MODE is a multil regs size. */ > +bool > +need_track_subreg (int regno, machine_mode reg_mode) > +{ > + poly_int64 total_size = GET_MODE_SIZE (reg_mode); > + poly_int64 natural_size = REGMODE_NATURAL_SIZE (reg_mode); > + return maybe_gt (total_size, natural_size) > + && multiple_p (total_size, natural_size) > + && regno >= FIRST_PSEUDO_REGISTER; > +} > + > +/* Return subreg_range of REF. */ > +static subreg_range > +get_range (df_ref ref) > +{ > + rtx reg = DF_REF_REAL_REG (ref); > + machine_mode reg_mode = GET_MODE (reg); > + > + if (!read_modify_subreg_p (DF_REF_REG (ref))) > + return subreg_range (0, get_nblocks (reg_mode)); > + > + rtx subreg = DF_REF_REG (ref); > + machine_mode subreg_mode = GET_MODE (subreg); > + poly_int64 offset = SUBREG_BYTE (subreg); > + int nblocks = get_nblocks (reg_mode); > + poly_int64 unit_size = REGMODE_NATURAL_SIZE (reg_mode); > + poly_int64 subreg_size = GET_MODE_SIZE (subreg_mode); > + poly_int64 left = offset + subreg_size; > + > + int subreg_start = -1; > + int subreg_nblocks = -1; > + for (int i = 0; i < nblocks; i += 1) > + { > + poly_int64 right = unit_size * (i + 1); > + if (subreg_start < 0 && maybe_lt (offset, right)) > + subreg_start = i; > + if (subreg_nblocks < 0 && maybe_le (left, right)) > + { > + subreg_nblocks = i + 1 - subreg_start; > + break; > + } > + } > + gcc_assert (subreg_start >= 0 && subreg_nblocks > 0); > + > + return subreg_range (subreg_start, subreg_start + subreg_nblocks); > +} > + > +/* Remove REF from BB_INFO use. */ > +void > +remove_subreg_range (basic_block_subreg_live_info *bb_info, unsigned int > regno, > + machine_mode mode, const subreg_range &range) > +{ > + int max = get_nblocks (mode); > + bitmap full = &bb_info->full_use; > + bitmap partial = &bb_info->partial_use; > + subregs_live *range_live = bb_info->range_use; > + > + if (!range.full_p (max)) > + { > + if (bitmap_bit_p (full, regno)) > + { > + bitmap_clear_bit (full, regno); > + gcc_assert (!bitmap_bit_p (partial, regno)); > + gcc_assert (range_live->empty_p (regno)); > + subreg_ranges temp = subreg_ranges (max); > + temp.make_full (); > + temp.remove_range (max, range); > + range_live->add_ranges (regno, temp); > + bitmap_set_bit (partial, regno); > + return; > + } > + else if (bitmap_bit_p (partial, regno)) > + { > + range_live->remove_range (regno, max, range); > + if (range_live->empty_p (regno)) > + bitmap_clear_bit (partial, regno); > + } > + } > + else if (bitmap_bit_p (full, regno)) > + { > + bitmap_clear_bit (full, regno); > + gcc_assert (!bitmap_bit_p (partial, regno)); > + } > + else if (bitmap_bit_p (partial, regno)) > + { > + bitmap_clear_bit (partial, regno); > + range_live->remove_live (regno); > + } > +} > + > +/* Return true if ref is a tracked subreg access. */ > +bool > +remove_subreg_range (basic_block_subreg_live_info *bb_info, df_ref ref) > +{ > + unsigned int regno = DF_REF_REGNO (ref); > + machine_mode mode = GET_MODE (DF_REF_REAL_REG (ref)); > + bool subreg_p = read_modify_subreg_p (DF_REF_REG (ref)); > + > + if (need_track_subreg (regno, mode)) > + { > + remove_subreg_range (bb_info, regno, mode, get_range (ref)); > + return subreg_p; > + } > + else > + { > + bitmap_clear_bit (&bb_info->full_use, regno); > + gcc_assert (!bitmap_bit_p (&bb_info->partial_use, regno)); > + gcc_assert (!bitmap_bit_p (&bb_info->partial_def, regno)); > + return false; > + } > +} > + > +/* add REF to BB_INFO def/use. */ > +void > +add_subreg_range (basic_block_subreg_live_info *bb_info, unsigned int regno, > + machine_mode mode, const subreg_range &range, bool is_def) > +{ > + int max = get_nblocks (mode); > + bitmap full = is_def ? &bb_info->full_def : &bb_info->full_use; > + bitmap partial = is_def ? &bb_info->partial_def : &bb_info->partial_use; > + subregs_live *range_live = is_def ? bb_info->range_def : > bb_info->range_use; > + > + if (!range.full_p (max)) > + { > + if (bitmap_bit_p (full, regno)) > + return; > + range_live->add_range (regno, max, range); > + if (range_live->full_p (regno)) > + { > + bitmap_set_bit (full, regno); > + gcc_assert (bitmap_bit_p (partial, regno)); > + bitmap_clear_bit (partial, regno); > + range_live->remove_live (regno); > + } > + else if (!bitmap_bit_p (partial, regno)) > + bitmap_set_bit (partial, regno); > + } > + else if (!bitmap_bit_p (full, regno)) > + { > + bitmap_set_bit (full, regno); > + if (bitmap_bit_p (partial, regno)) > + { > + bitmap_clear_bit (partial, regno); > + range_live->remove_live (regno); > + } > + } > +} > + > +/* Return true if ref is a tracked subreg access. */ > +bool > +add_subreg_range (basic_block_subreg_live_info *bb_info, df_ref ref, > + bool is_def) > +{ > + unsigned int regno = DF_REF_REGNO (ref); > + machine_mode mode = GET_MODE (DF_REF_REAL_REG (ref)); > + bool subreg_p = read_modify_subreg_p (DF_REF_REG (ref)); > + > + if (need_track_subreg (regno, mode)) > + { > + add_subreg_range (bb_info, regno, mode, get_range (ref), is_def); > + return subreg_p; > + } > + else > + { > + bitmap full = is_def ? &bb_info->full_def : &bb_info->full_use; > + bitmap partial = is_def ? &bb_info->partial_def : > &bb_info->partial_use; > + bitmap_set_bit (full, regno); > + gcc_assert (!bitmap_bit_p (partial, regno)); > + > + if (is_def && DF_REF_FLAGS (ref) & (DF_REF_PARTIAL | > DF_REF_CONDITIONAL)) > + add_subreg_range (bb_info, ref, false); > + return false; > + } > +} > + > +/* Free basic block info. */ > + > +static void > +df_live_subreg_free_bb_info (basic_block bb ATTRIBUTE_UNUSED, void *vbb_info) > +{ > + df_live_subreg_bb_info *bb_info = (df_live_subreg_bb_info *) vbb_info; > + if (bb_info) > + { > + delete bb_info->range_def; > + bb_info->range_def = NULL; > + delete bb_info->range_use; > + bb_info->range_use = NULL; > + delete bb_info->range_in; > + bb_info->range_in = NULL; > + delete bb_info->range_out; > + bb_info->range_out = NULL; > + > + bitmap_clear (&bb_info->full_use); > + bitmap_clear (&bb_info->partial_use); > + bitmap_clear (&bb_info->full_def); > + bitmap_clear (&bb_info->partial_def); > + bitmap_clear (&bb_info->all_in); > + bitmap_clear (&bb_info->full_in); > + bitmap_clear (&bb_info->partial_in); > + bitmap_clear (&bb_info->all_out); > + bitmap_clear (&bb_info->full_out); > + bitmap_clear (&bb_info->partial_out); > + } > +} > + > +/* Allocate or reset bitmaps for DF_LIVE_SUBREG blocks. The solution bits are > + not touched unless the block is new. */ > + > +static void > +df_live_subreg_alloc (bitmap all_blocks ATTRIBUTE_UNUSED) > +{ > + struct df_live_subreg_problem_data *problem_data; > + df_grow_bb_info (df_live_subreg); > + if (df_live_subreg->problem_data) > + problem_data > + = (struct df_live_subreg_problem_data *) df_live_subreg->problem_data; > + else > + { > + problem_data = XNEW (struct df_live_subreg_problem_data); > + df_live_subreg->problem_data = problem_data; > + > + bitmap_obstack_initialize (&problem_data->live_subreg_bitmaps); > + problem_data->has_subreg_live_p = false; > + } > + > + basic_block bb; > + FOR_EACH_BB_FN (bb, cfun) > + bitmap_set_bit (df_live_subreg->out_of_date_transfer_functions, > bb->index); > + > + bitmap_set_bit (df_live_subreg->out_of_date_transfer_functions, > ENTRY_BLOCK); > + bitmap_set_bit (df_live_subreg->out_of_date_transfer_functions, > EXIT_BLOCK); > + > + unsigned int bb_index; > + bitmap_iterator bi; > + EXECUTE_IF_SET_IN_BITMAP (df_live_subreg->out_of_date_transfer_functions, > 0, > + bb_index, bi) > + { > + df_live_subreg_bb_info *bb_info = df_live_subreg_get_bb_info > (bb_index); > + > + /* When bitmaps are already initialized, just clear them. */ > + if (bb_info->full_use.obstack) > + { > + bitmap_clear (&bb_info->full_def); > + bitmap_clear (&bb_info->partial_def); > + bitmap_clear (&bb_info->full_use); > + bitmap_clear (&bb_info->partial_use); > + bitmap_clear (&bb_info->all_in); > + bitmap_clear (&bb_info->full_in); > + bitmap_clear (&bb_info->partial_in); > + bitmap_clear (&bb_info->all_out); > + bitmap_clear (&bb_info->full_out); > + bitmap_clear (&bb_info->partial_out); > + } > + else > + { > + bitmap_initialize (&bb_info->full_def, > + &problem_data->live_subreg_bitmaps); > + bitmap_initialize (&bb_info->partial_def, > + &problem_data->live_subreg_bitmaps); > + bitmap_initialize (&bb_info->full_use, > + &problem_data->live_subreg_bitmaps); > + bitmap_initialize (&bb_info->partial_use, > + &problem_data->live_subreg_bitmaps); > + bitmap_initialize (&bb_info->all_in, > + &problem_data->live_subreg_bitmaps); > + bitmap_initialize (&bb_info->full_in, > + &problem_data->live_subreg_bitmaps); > + bitmap_initialize (&bb_info->partial_in, > + &problem_data->live_subreg_bitmaps); > + bitmap_initialize (&bb_info->all_out, > + &problem_data->live_subreg_bitmaps); > + bitmap_initialize (&bb_info->full_out, > + &problem_data->live_subreg_bitmaps); > + bitmap_initialize (&bb_info->partial_out, > + &problem_data->live_subreg_bitmaps); > + } > + > + if (bb_info->range_def) > + { > + bb_info->range_def->clear (); > + bb_info->range_use->clear (); > + bb_info->range_in->clear (); > + bb_info->range_out->clear (); > + } > + else > + { > + bb_info->range_def = new subregs_live (); > + bb_info->range_use = new subregs_live (); > + bb_info->range_in = new subregs_live (); > + bb_info->range_out = new subregs_live (); > + } > + } > + df_live_subreg->optional_p = true; > +} > + > +/* Reset the global solution for recalculation. */ > + > +static void > +df_live_subreg_reset (bitmap all_blocks) > +{ > + unsigned int bb_index; > + bitmap_iterator bi; > + > + EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) > + { > + df_live_subreg_bb_info *bb_info = df_live_subreg_get_bb_info > (bb_index); > + gcc_assert (bb_info); > + bitmap_clear (&bb_info->all_in); > + bitmap_clear (&bb_info->full_in); > + bitmap_clear (&bb_info->partial_in); > + bitmap_clear (&bb_info->all_out); > + bitmap_clear (&bb_info->full_out); > + bitmap_clear (&bb_info->partial_out); > + bb_info->range_in->clear (); > + bb_info->range_out->clear (); > + } > +} > + > +/* Compute local live register info for basic block BB. */ > + > +static void > +df_live_subreg_bb_local_compute (unsigned int bb_index) > +{ > + basic_block bb = BASIC_BLOCK_FOR_FN (cfun, bb_index); > + df_live_subreg_bb_info *bb_info = df_live_subreg_get_bb_info (bb_index); > + df_live_subreg_problem_data *problem_data > + = (df_live_subreg_problem_data *) df_live_subreg->problem_data; > + rtx_insn *insn; > + df_ref def, use; > + > + /* Process the registers set in an exception handler. */ > + FOR_EACH_ARTIFICIAL_DEF (def, bb_index) > + if ((DF_REF_FLAGS (def) & DF_REF_AT_TOP) == 0) > + { > + problem_data->has_subreg_live_p > + |= add_subreg_range (bb_info, def, true); > + problem_data->has_subreg_live_p |= remove_subreg_range (bb_info, def); > + } > + > + /* Process the hardware registers that are always live. */ > + FOR_EACH_ARTIFICIAL_USE (use, bb_index) > + /* Add use to set of uses in this BB. */ > + if ((DF_REF_FLAGS (use) & DF_REF_AT_TOP) == 0) > + problem_data->has_subreg_live_p |= add_subreg_range (bb_info, use); > + > + FOR_BB_INSNS_REVERSE (bb, insn) > + { > + if (!NONDEBUG_INSN_P (insn)) > + continue; > + > + df_insn_info *insn_info = DF_INSN_INFO_GET (insn); > + FOR_EACH_INSN_INFO_DEF (def, insn_info) > + { > + problem_data->has_subreg_live_p |= remove_subreg_range (bb_info, def); > + problem_data->has_subreg_live_p > + |= add_subreg_range (bb_info, def, true); > + } > + > + FOR_EACH_INSN_INFO_USE (use, insn_info) > + { > + unsigned int regno = DF_REF_REGNO (use); > + machine_mode mode = GET_MODE (DF_REF_REAL_REG (use)); > + /* Ignore the use of SET_DEST which is (subreg (reg) offset). */ > + if (need_track_subreg (regno, mode) > + && DF_REF_FLAGS (use) & (DF_REF_READ_WRITE | DF_REF_SUBREG)) > + continue; > + problem_data->has_subreg_live_p |= add_subreg_range (bb_info, use); > + } > + } > + > + /* Process the registers set in an exception handler or the hard > + frame pointer if this block is the target of a non local > + goto. */ > + FOR_EACH_ARTIFICIAL_DEF (def, bb_index) > + if (DF_REF_FLAGS (def) & DF_REF_AT_TOP) > + { > + problem_data->has_subreg_live_p > + |= add_subreg_range (bb_info, def, true); > + problem_data->has_subreg_live_p |= remove_subreg_range (bb_info, def); > + } > + > +#ifdef EH_USES > + /* Process the uses that are live into an exception handler. */ > + FOR_EACH_ARTIFICIAL_USE (use, bb_index) > + /* Add use to set of uses in this BB. */ > + if (DF_REF_FLAGS (use) & DF_REF_AT_TOP) > + problem_data->has_subreg_live_p |= add_subreg_range (bb_info, use); > +#endif > +} > + > +/* Compute local live register info for each basic block within BLOCKS. */ > + > +static void > +df_live_subreg_local_compute (bitmap all_blocks ATTRIBUTE_UNUSED) > +{ > + unsigned int bb_index, i; > + bitmap_iterator bi; > + > + bitmap_clear (&df->hardware_regs_used); > + > + /* The all-important stack pointer must always be live. */ > + bitmap_set_bit (&df->hardware_regs_used, STACK_POINTER_REGNUM); > + > + /* Global regs are always live, too. */ > + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) > + if (global_regs[i]) > + bitmap_set_bit (&df->hardware_regs_used, i); > + > + /* Before reload, there are a few registers that must be forced > + live everywhere -- which might not already be the case for > + blocks within infinite loops. */ > + if (!reload_completed) > + { > + unsigned int pic_offset_table_regnum = PIC_OFFSET_TABLE_REGNUM; > + /* Any reference to any pseudo before reload is a potential > + reference of the frame pointer. */ > + bitmap_set_bit (&df->hardware_regs_used, FRAME_POINTER_REGNUM); > + > + /* Pseudos with argument area equivalences may require > + reloading via the argument pointer. */ > + if (FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM > + && fixed_regs[ARG_POINTER_REGNUM]) > + bitmap_set_bit (&df->hardware_regs_used, ARG_POINTER_REGNUM); > + > + /* Any constant, or pseudo with constant equivalences, may > + require reloading from memory using the pic register. */ > + if (pic_offset_table_regnum != INVALID_REGNUM > + && fixed_regs[pic_offset_table_regnum]) > + bitmap_set_bit (&df->hardware_regs_used, pic_offset_table_regnum); > + } > + > + EXECUTE_IF_SET_IN_BITMAP (df_live_subreg->out_of_date_transfer_functions, > 0, > + bb_index, bi) > + { > + if (bb_index == EXIT_BLOCK) > + { > + /* The exit block is special for this problem and its bits are > + computed from thin air. */ > + class df_live_subreg_bb_info *bb_info > + = df_live_subreg_get_bb_info (EXIT_BLOCK); > + bitmap_copy (&bb_info->full_use, df->exit_block_uses); > + } > + else > + df_live_subreg_bb_local_compute (bb_index); > + } > + > + bitmap_clear (df_live_subreg->out_of_date_transfer_functions); > +} > + > +/* Initialize the solution vectors. */ > + > +static void > +df_live_subreg_init (bitmap all_blocks) > +{ > + unsigned int bb_index; > + bitmap_iterator bi; > + > + EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) > + { > + df_live_subreg_bb_info *bb_info = df_live_subreg_get_bb_info > (bb_index); > + bitmap_copy (&bb_info->full_in, &bb_info->full_use); > + bitmap_copy (&bb_info->partial_in, &bb_info->partial_use); > + bb_info->range_in->copy_lives (*bb_info->range_use); > + bitmap_clear (&bb_info->full_out); > + bitmap_clear (&bb_info->partial_out); > + bb_info->range_out->clear (); > + } > +} > + > +/* Check the result is golden. */ > +static void > +df_live_subreg_check_result (bitmap full, bitmap partial, > + subregs_live *partial_live) > +{ > + unsigned int regno; > + bitmap_iterator bi; > + gcc_assert (!bitmap_intersect_p (full, partial)); > + EXECUTE_IF_SET_IN_BITMAP (full, 0, regno, bi) > + gcc_assert (partial_live->empty_p (regno)); > + EXECUTE_IF_SET_IN_BITMAP (partial, 0, regno, bi) > + gcc_assert (!partial_live->empty_p (regno)); > +} > + > +/* Confluence function that processes infinite loops. This might be a > + noreturn function that throws. And even if it isn't, getting the > + unwind info right helps debugging. */ > +static void > +df_live_subreg_confluence_0 (basic_block bb) > +{ > + bitmap full_out = &df_live_subreg_get_bb_info (bb->index)->full_out; > + if (bb != EXIT_BLOCK_PTR_FOR_FN (cfun)) > + bitmap_copy (full_out, &df->hardware_regs_used); > +} > + > +/* Confluence function that ignores fake edges. */ > + > +static bool > +df_live_subreg_confluence_n (edge e) > +{ > + df_live_subreg_problem_data *problem_data > + = (df_live_subreg_problem_data *) df_live_subreg->problem_data; > + class df_live_subreg_bb_info *src_bb_info > + = df_live_subreg_get_bb_info (e->src->index); > + class df_live_subreg_bb_info *dest_bb_info > + = df_live_subreg_get_bb_info (e->dest->index); > + > + if (!problem_data->has_subreg_live_p) > + { > + bool changed = false; > + > + /* Call-clobbered registers die across exception and call edges. > + Conservatively treat partially-clobbered registers as surviving > + across the edges; they might or might not, depending on what > + mode they have. */ > + /* ??? Abnormal call edges ignored for the moment, as this gets > + confused by sibling call edges, which crashes reg-stack. */ > + if (e->flags & EDGE_EH) > + { > + bitmap_view<HARD_REG_SET> eh_kills (eh_edge_abi.full_reg_clobbers ()); > + changed > + = bitmap_ior_and_compl_into (&src_bb_info->full_out, > + &dest_bb_info->full_in, eh_kills); > + } > + else > + changed > + = bitmap_ior_into (&src_bb_info->full_out, &dest_bb_info->full_in); > + > + changed > + |= bitmap_ior_into (&src_bb_info->full_out, &df->hardware_regs_used); > + return changed; > + } > + > + /* If there has subreg live need be tracked. Calculation formula: > + temp_full mean: > + 1. partial in out/in, full in other in/out > + 2. partial in out and in, and mrege range is full > + temp_range mean: > + the range of regno which partial live > + src_bb_info->partial_out = (src_bb_info->partial_out | > + dest_bb_info->partial_in) & ~temp_full > + src_bb_info->range_out = copy(temp_range) > + src_bb_info->full_out |= dest_bb_info->full_in | temp_full > + */ > + subregs_live temp_range; > + temp_range.add_lives (*src_bb_info->range_out); > + temp_range.add_lives (*dest_bb_info->range_in); > + > + bitmap_head temp_partial_all; > + bitmap_initialize (&temp_partial_all, &bitmap_default_obstack); > + bitmap_ior (&temp_partial_all, &src_bb_info->partial_out, > + &dest_bb_info->partial_in); > + > + bitmap_head temp_full; > + bitmap_initialize (&temp_full, &bitmap_default_obstack); > + > + /* Collect regno that become full after merge src_bb_info->partial_out > + and dest_bb_info->partial_in. */ > + unsigned int regno; > + bitmap_iterator bi; > + EXECUTE_IF_SET_IN_BITMAP (&temp_partial_all, FIRST_PSEUDO_REGISTER, regno, > bi) > + { > + if (bitmap_bit_p (&src_bb_info->full_out, regno) > + || bitmap_bit_p (&dest_bb_info->full_in, regno)) > + { > + bitmap_set_bit (&temp_full, regno); > + temp_range.remove_live (regno); > + continue; > + } > + else if (!bitmap_bit_p (&src_bb_info->partial_out, regno) > + || !bitmap_bit_p (&dest_bb_info->partial_in, regno)) > + continue; > + > + subreg_ranges temp = src_bb_info->range_out->lives.at (regno); > + temp.add_ranges (dest_bb_info->range_in->lives.at (regno)); > + if (temp.full_p ()) > + { > + bitmap_set_bit (&temp_full, regno); > + temp_range.remove_live (regno); > + } > + } > + > + /* Calculating src_bb_info->partial_out and src_bb_info->range_out. */ > + bool changed = bitmap_and_compl (&src_bb_info->partial_out, > &temp_partial_all, > + &temp_full); > + changed |= src_bb_info->range_out->copy_lives (temp_range); > + > + /* Calculating src_bb_info->full_out. */ > + bitmap_ior_into (&temp_full, &dest_bb_info->full_in); > + > + /* Call-clobbered registers die across exception and call edges. > + Conservatively treat partially-clobbered registers as surviving > + across the edges; they might or might not, depending on what > + mode they have. */ > + /* ??? Abnormal call edges ignored for the moment, as this gets > + confused by sibling call edges, which crashes reg-stack. */ > + if (e->flags & EDGE_EH) > + { > + bitmap_view<HARD_REG_SET> eh_kills (eh_edge_abi.full_reg_clobbers ()); > + changed |= bitmap_ior_and_compl_into (&src_bb_info->full_out, > &temp_full, > + eh_kills); > + } > + else > + changed |= bitmap_ior_into (&src_bb_info->full_out, &temp_full); > + > + changed |= bitmap_ior_into (&src_bb_info->full_out, > &df->hardware_regs_used); > + > + bitmap_clear (&temp_full); > + bitmap_clear (&temp_partial_all); > + > + df_live_subreg_check_result (&src_bb_info->full_out, > + &src_bb_info->partial_out, > + src_bb_info->range_out); > + return changed; > +} > + > +/* Transfer function. */ > + > +static bool > +df_live_subreg_transfer_function (int bb_index) > +{ > + class df_live_subreg_bb_info *bb_info = df_live_subreg_get_bb_info > (bb_index); > + df_live_subreg_problem_data *problem_data > + = (df_live_subreg_problem_data *) df_live_subreg->problem_data; > + if (!problem_data->has_subreg_live_p) > + { > + bitmap in = &bb_info->full_in; > + bitmap out = &bb_info->full_out; > + bitmap use = &bb_info->full_use; > + bitmap def = &bb_info->full_def; > + > + return bitmap_ior_and_compl (in, use, out, def); > + } > + > + /* If there has subreg live need be tracked, follow the bellow calculation > + formula: > + all_def = full_def | partial_def > + temp_partial_out = ((full_out & partail_def) > + | (partail_out & ~all_def) > + | (partial_out remove partail_def not empty)) > + & ~full_use > + temp_partial_be_full = (temp_partial_out & partial_use) merge be full > + full_in = full_use | full_out & ~all_def | temp_partial_be_full > + partail_in = (temp_partial_out | partial_use) & ~temp_partial_be_full > */ > + unsigned int regno; > + bitmap_iterator bi; > + bool changed = false; > + bitmap_head temp_partial_out; > + bitmap_head temp_partial_be_full; > + bitmap_head all_def; > + subregs_live temp_range_out; > + bitmap_initialize (&temp_partial_out, &bitmap_default_obstack); > + bitmap_initialize (&temp_partial_be_full, &bitmap_default_obstack); > + bitmap_initialize (&all_def, &bitmap_default_obstack); > + > + bitmap_ior (&all_def, &bb_info->full_def, &bb_info->partial_def); > + > + /* temp_partial_out = (full_out & partail_def) */ > + bitmap_and (&temp_partial_out, &bb_info->full_out, &bb_info->partial_def); > + EXECUTE_IF_SET_IN_BITMAP (&temp_partial_out, FIRST_PSEUDO_REGISTER, regno, > bi) > + { > + subreg_ranges temp (bb_info->range_def->lives.at (regno).max); > + temp.make_full (); > + temp.remove_ranges (bb_info->range_def->lives.at (regno)); > + temp_range_out.add_ranges (regno, temp); > + } > + > + /* temp_partial_out |= (partail_out & ~all_def) */ > + bitmap_ior_and_compl_into (&temp_partial_out, &bb_info->partial_out, > + &all_def); > + EXECUTE_IF_AND_COMPL_IN_BITMAP (&bb_info->partial_out, &all_def, > + FIRST_PSEUDO_REGISTER, regno, bi) > + { > + temp_range_out.add_ranges (regno, bb_info->range_out->lives.at > (regno)); > + } > + > + /* temp_partial_out |= (partial_out remove partail_def not empty) */ > + EXECUTE_IF_AND_IN_BITMAP (&bb_info->partial_out, &bb_info->partial_def, 0, > + regno, bi) > + { > + subreg_ranges temp = bb_info->range_out->lives.at (regno); > + temp.remove_ranges (bb_info->range_def->lives.at (regno)); > + if (!temp.empty_p ()) > + { > + bitmap_set_bit (&temp_partial_out, regno); > + temp_range_out.add_ranges (regno, temp); > + } > + } > + > + /* temp_partial_out = temp_partial_out & ~full_use */ > + bitmap_and_compl_into (&temp_partial_out, &bb_info->full_use); > + EXECUTE_IF_SET_IN_BITMAP (&bb_info->full_use, 0, regno, bi) > + if (!temp_range_out.empty_p (regno)) > + temp_range_out.remove_live (regno); > + > + /* temp_partial_be_full = (temp_partial_out & partial_use) merge become > full > + */ > + temp_range_out.add_lives (*bb_info->range_use); > + /* Remove all range which in partial_use and in full_out and not in > all_def. > + */ > + EXECUTE_IF_SET_IN_BITMAP (&bb_info->full_out, 0, regno, bi) > + if (!bitmap_bit_p (&all_def, regno) && !temp_range_out.empty_p (regno)) > + temp_range_out.remove_live (regno); > + > + EXECUTE_IF_AND_IN_BITMAP (&temp_partial_out, &bb_info->partial_use, 0, > regno, > + bi) > + { > + subreg_ranges temp = temp_range_out.lives.at (regno); > + temp.add_ranges (bb_info->range_use->lives.at (regno)); > + if (temp.full_p ()) > + { > + bitmap_set_bit (&temp_partial_be_full, regno); > + temp_range_out.remove_live (regno); > + } > + } > + > + /* Calculating full_in. */ > + bitmap_ior_and_compl_into (&temp_partial_be_full, &bb_info->full_out, > + &all_def); > + changed |= bitmap_ior (&bb_info->full_in, &temp_partial_be_full, > + &bb_info->full_use); > + > + /* Calculating partial_in and range_in. */ > + bitmap_ior_into (&temp_partial_out, &bb_info->partial_use); > + changed |= bitmap_and_compl (&bb_info->partial_in, &temp_partial_out, > + &temp_partial_be_full); > + changed |= bb_info->range_in->copy_lives (temp_range_out); > + > + bitmap_clear (&temp_partial_out); > + bitmap_clear (&temp_partial_be_full); > + bitmap_clear (&all_def); > + > + df_live_subreg_check_result (&bb_info->full_in, &bb_info->partial_in, > + bb_info->range_in); > + > + return changed; > +} > + > +/* Run the fast dce as a side effect of building LR. */ > + > +void > +df_live_subreg_finalize (bitmap all_blocks) > +{ > + unsigned int bb_index; > + bitmap_iterator bi; > + EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) > + { > + class df_live_subreg_bb_info *bb_info > + = df_live_subreg_get_bb_info (bb_index); > + gcc_assert (bb_info); > + bitmap_ior (&bb_info->all_in, &bb_info->full_in, &bb_info->partial_in); > + bitmap_ior (&bb_info->all_out, &bb_info->full_out, > &bb_info->partial_out); > + } > +} > + > +/* Free all storage associated with the problem. */ > + > +static void > +df_live_subreg_free (void) > +{ > + df_live_subreg_problem_data *problem_data > + = (df_live_subreg_problem_data *) df_live_subreg->problem_data; > + if (df_live_subreg->block_info) > + { > + df_live_subreg->block_info_size = 0; > + free (df_live_subreg->block_info); > + df_live_subreg->block_info = NULL; > + bitmap_obstack_release (&problem_data->live_subreg_bitmaps); > + free (df_live_subreg->problem_data); > + df_live_subreg->problem_data = NULL; > + } > + > + BITMAP_FREE (df_live_subreg->out_of_date_transfer_functions); > + free (df_live_subreg); > +} > + > +/* Debugging info at top of bb. */ > + > +static void > +df_live_subreg_top_dump (basic_block bb, FILE *file) > +{ > + df_live_subreg_bb_info *bb_info = df_live_subreg_get_bb_info (bb->index); > + if (!bb_info) > + return; > + > + fprintf (file, ";; subreg live all in \t"); > + df_print_regset (file, &bb_info->all_in); > + fprintf (file, ";; subreg live full in \t"); > + df_print_regset (file, &bb_info->full_in); > + fprintf (file, ";; subreg live partial in \t"); > + df_print_regset (file, &bb_info->partial_in); > + fprintf (file, ";; subreg live range in \t"); > + bb_info->range_in->dump (file, ""); > + > + fprintf (file, "\n;; subreg live full use \t"); > + df_print_regset (file, &bb_info->full_use); > + fprintf (file, ";; subreg live partial use \t"); > + df_print_regset (file, &bb_info->partial_use); > + fprintf (file, ";; subreg live range use \t"); > + bb_info->range_use->dump (file, ""); > + > + fprintf (file, "\n;; subreg live full def \t"); > + df_print_regset (file, &bb_info->full_def); > + fprintf (file, ";; subreg live partial def \t"); > + df_print_regset (file, &bb_info->partial_def); > + fprintf (file, ";; subreg live range def \t"); > + bb_info->range_def->dump (file, ""); > +} > + > +/* Debugging info at bottom of bb. */ > + > +static void > +df_live_subreg_bottom_dump (basic_block bb, FILE *file) > +{ > + df_live_subreg_bb_info *bb_info = df_live_subreg_get_bb_info (bb->index); > + if (!bb_info) > + return; > + > + fprintf (file, ";; subreg live all out \t"); > + df_print_regset (file, &bb_info->all_out); > + fprintf (file, ";; subreg live full out \t"); > + df_print_regset (file, &bb_info->full_out); > + fprintf (file, ";; subreg live partial out \t"); > + df_print_regset (file, &bb_info->partial_out); > + fprintf (file, ";; subreg live range out \t"); > + bb_info->range_out->dump (file, ""); > +} > + > +/* All of the information associated with every instance of the problem. */ > + > +static const struct df_problem problem_LIVE_SUBREG = { > + DF_LIVE_SUBREG, /* Problem id. */ > + DF_BACKWARD, /* Direction. */ > + df_live_subreg_alloc, /* Allocate the problem specific > data. */ > + df_live_subreg_reset, /* Reset global information. */ > + df_live_subreg_free_bb_info, /* Free basic block info. */ > + df_live_subreg_local_compute, /* Local compute function. */ > + df_live_subreg_init, /* Init the solution specific data. > */ > + df_worklist_dataflow, /* Worklist solver. */ > + df_live_subreg_confluence_0, /* Confluence operator 0. */ > + df_live_subreg_confluence_n, /* Confluence operator n. */ > + df_live_subreg_transfer_function, /* Transfer function. */ > + df_live_subreg_finalize, /* Finalize function. */ > + df_live_subreg_free, /* Free all of the problem > information. */ > + df_live_subreg_free, /* Remove this problem from the stack of > dataflow > + problems. */ > + NULL, /* Debugging. */ > + df_live_subreg_top_dump, /* Debugging start block. */ > + df_live_subreg_bottom_dump, /* Debugging end block. */ > + NULL, /* Debugging start insn. */ > + NULL, /* Debugging end insn. */ > + NULL, /* Incremental solution verify start. */ > + NULL, /* Incremental solution verify end. */ > + &problem_LR, /* Dependent problem. */ > + sizeof (df_live_subreg_bb_info), /* Size of entry of block_info array. */ > + TV_DF_LIVE_SUBREG, /* Timing variable. */ > + false /* Reset blocks on dropping out of blocks_to_analyze. */ > +}; > + > +/* Create a new DATAFLOW instance and add it to an existing instance > + of DF. The returned structure is what is used to get at the > + solution. */ > + > +void > +df_live_subreg_add_problem (void) > +{ > + df_add_problem (&problem_LIVE_SUBREG); > + > + /* These will be initialized when df_scan_blocks processes each > + block. */ > + df_live_subreg->out_of_date_transfer_functions > + = BITMAP_ALLOC (&df_bitmap_obstack); > +} > > - > > /*---------------------------------------------------------------------------- > LIVE AND MAY-INITIALIZED REGISTERS. > > diff --git a/gcc/df.h b/gcc/df.h > index 402657a7076..50a6cf99863 100644 > --- a/gcc/df.h > +++ b/gcc/df.h > @@ -47,6 +47,7 @@ enum df_problem_id > { > DF_SCAN, > DF_LR, /* Live Registers backward. */ > + DF_LIVE_SUBREG, /* Live Ranges and Live Subreg */ > DF_LIVE, /* Live Registers & Uninitialized Registers */ > DF_RD, /* Reaching Defs. */ > DF_CHAIN, /* Def-Use and/or Use-Def Chains. */ > @@ -619,6 +620,7 @@ public: > #define DF_SCAN_BB_INFO(BB) (df_scan_get_bb_info ((BB)->index)) > #define DF_RD_BB_INFO(BB) (df_rd_get_bb_info ((BB)->index)) > #define DF_LR_BB_INFO(BB) (df_lr_get_bb_info ((BB)->index)) > +#define DF_LIVE_SUBREG_INFO(BB) (df_live_subreg_get_bb_info ((BB)->index)) > #define DF_LIVE_BB_INFO(BB) (df_live_get_bb_info ((BB)->index)) > #define DF_WORD_LR_BB_INFO(BB) (df_word_lr_get_bb_info ((BB)->index)) > #define DF_MD_BB_INFO(BB) (df_md_get_bb_info ((BB)->index)) > @@ -632,6 +634,15 @@ public: > #define DF_MIR_IN(BB) (&DF_MIR_BB_INFO (BB)->in) > #define DF_MIR_OUT(BB) (&DF_MIR_BB_INFO (BB)->out) > > +#define DF_LIVE_SUBREG_IN(BB) (&DF_LIVE_SUBREG_INFO (BB)->all_in) > +#define DF_LIVE_SUBREG_OUT(BB) (&DF_LIVE_SUBREG_INFO (BB)->all_out) > +#define DF_LIVE_SUBREG_FULL_IN(BB) (&DF_LIVE_SUBREG_INFO (BB)->full_in) > +#define DF_LIVE_SUBREG_FULL_OUT(BB) (&DF_LIVE_SUBREG_INFO (BB)->full_out) > +#define DF_LIVE_SUBREG_PARTIAL_IN(BB) (&DF_LIVE_SUBREG_INFO (BB)->partial_in) > +#define DF_LIVE_SUBREG_PARTIAL_OUT(BB) (&DF_LIVE_SUBREG_INFO > (BB)->partial_out) > +#define DF_LIVE_SUBREG_RANGE_IN(BB) (DF_LIVE_SUBREG_INFO (BB)->range_in) > +#define DF_LIVE_SUBREG_RANGE_OUT(BB) (DF_LIVE_SUBREG_INFO (BB)->range_out) > + > /* These macros are used by passes that are not tolerant of > uninitialized variables. This intolerance should eventually > be fixed. */ > @@ -878,6 +889,32 @@ public: > bitmap_head out; /* At the bottom of the block. */ > }; > > +class subregs_live; > + > +class basic_block_subreg_live_info > +{ > +public: > + bitmap_head full_def; > + bitmap_head full_use; > + /* Only for pseudo registers. */ > + bitmap_head partial_def; > + bitmap_head partial_use; > + subregs_live *range_def = NULL; > + subregs_live *range_use = NULL; > +}; > + > +/* Live registers and live ranges including specifial subreg. */ > +class df_live_subreg_bb_info : public basic_block_subreg_live_info > +{ > +public: > + bitmap_head all_in, full_in; > + bitmap_head all_out, full_out; > + /* Only for pseudo registers. */ > + bitmap_head partial_in; > + bitmap_head partial_out; > + subregs_live *range_in = NULL; > + subregs_live *range_out = NULL; > +}; > > /* Uninitialized registers. All bitmaps are referenced by the > register number. Anded results of the forwards and backward live > @@ -946,6 +983,7 @@ extern class df_d *df; > #define df_note (df->problems_by_index[DF_NOTE]) > #define df_md (df->problems_by_index[DF_MD]) > #define df_mir (df->problems_by_index[DF_MIR]) > +#define df_live_subreg (df->problems_by_index[DF_LIVE_SUBREG]) > > /* This symbol turns on checking that each modification of the cfg has > been identified to the appropriate df routines. It is not part of > @@ -1031,6 +1069,25 @@ extern void df_lr_add_problem (void); > extern void df_lr_verify_transfer_functions (void); > extern void df_live_verify_transfer_functions (void); > extern void df_live_add_problem (void); > +extern void > +df_live_subreg_add_problem (void); > +extern void > +df_live_subreg_finalize (bitmap all_blocks); > +class subreg_range; > +extern bool > +need_track_subreg (int regno, machine_mode mode); > +extern void > +remove_subreg_range (basic_block_subreg_live_info *bb_info, unsigned int > regno, > + machine_mode mode, const subreg_range &range); > +extern bool > +remove_subreg_range (basic_block_subreg_live_info *bb_info, df_ref ref); > +extern void > +add_subreg_range (basic_block_subreg_live_info *bb_info, unsigned int regno, > + machine_mode mode, const subreg_range &range, > + bool is_def = false); > +extern bool > +add_subreg_range (basic_block_subreg_live_info *bb_info, df_ref ref, > + bool is_def = false); > extern void df_live_set_all_dirty (void); > extern void df_chain_add_problem (unsigned int); > extern void df_word_lr_add_problem (void); > @@ -1124,6 +1181,16 @@ df_lr_get_bb_info (unsigned int index) > return NULL; > } > > +inline class df_live_subreg_bb_info * > +df_live_subreg_get_bb_info (unsigned int index) > +{ > + if (index < df_live_subreg->block_info_size) > + return &( > + (class df_live_subreg_bb_info *) df_live_subreg->block_info)[index]; > + else > + return NULL; > +} > + > inline class df_md_bb_info * > df_md_get_bb_info (unsigned int index) > { > diff --git a/gcc/regs.h b/gcc/regs.h > index aea093ed795..84c6bdb980c 100644 > --- a/gcc/regs.h > +++ b/gcc/regs.h > @@ -389,4 +389,11 @@ range_in_hard_reg_set_p (const_hard_reg_set set, > unsigned regno, int nregs) > return true; > } > > +/* Return the number of blocks the MODE overlap. One block equal mode's > natural > + size. So, satisfy the following equation: > + (nblocks - 1) * natural_size < GET_MODE_SIZE (mode) > + <= nblocks * natural_size. */ > +#define get_nblocks(mode) > \ > + (exact_div (GET_MODE_SIZE (mode), REGMODE_NATURAL_SIZE (mode)).to_constant > ()) > + > #endif /* GCC_REGS_H */ > diff --git a/gcc/subreg-live-range.cc b/gcc/subreg-live-range.cc > new file mode 100644 > index 00000000000..43a5eafedf1 > --- /dev/null > +++ b/gcc/subreg-live-range.cc > @@ -0,0 +1,628 @@ > +/* SUBREG live range track classes for DF & IRA & LRA. > + Copyright (C) 2023 Free Software Foundation, Inc. > + Contributed by Lehua Ding (lehua.d...@rivai.ai), RiVAI Technologies Ltd. > + > + 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/>. */ > + > +#include "subreg-live-range.h" > +#include "selftest.h" > +#include "print-rtl.h" > + > +/* class subreg_range */ > +void > +subreg_range::dump (FILE *file) const > +{ > + fprintf (file, "[%d, %d)", start, end); > +} > + > +/* class subreg_ranges */ > +bool > +subreg_ranges::add_range (int max, const subreg_range &new_range) > +{ > + subreg_range range = new_range; > + if (full_p ()) > + return false; > + else if (max == 1) > + { > + gcc_assert (range.start == 0 && range.end == 1); > + make_full (); > + return true; > + } > + > + if (this->max == 1) > + change_max (max); > + > + gcc_assert (this->max == max); > + gcc_assert (range.start < range.end); > + > + bool changed = empty_p (); > + auto it = ranges.begin (); > + while (it != ranges.end ()) > + { > + const subreg_range &r = *it; > + gcc_assert (r.start < r.end); > + > + /* The possible positional relationship of R and RANGE. > + 1~5 means R.start's possible position relative to RANGE > + A~G means R.end's possible position relative to RANGE > + caseN means when R.start at N positon, the R.end can be in which > + positions. > + > + RANGE.start RANGE.end > + [ ) > + | | > + R.start 1 2 3 4 5 > + R.end | | > + case1 A B C D E > + case2 | C D E > + case3 | F D E > + case4 | | E > + case5 | | G > + > + */ > + > + /* R.start at 1 position. */ > + if (r.start < range.start) > + { > + /* R.end at A position. That means R and RANGE do not overlap. */ > + if (r.end < range.start) > + it++; > + /* R.end at B/C position. That means RANGE's left part overlap R's > + right part. Expand RANGE.start to R.start and remove R. */ > + else if (r.end < range.end) > + { > + changed = true; > + range.start = r.start; > + it = ranges.erase (it); > + } > + /* R.end at D/E position. That means R already contains RANGE, nothing > + todo. */ > + else > + return false; > + } > + /* R.start at 2 position. */ > + else if (r.start == range.start) > + { > + /* R.end at C/D position. That means RANGE contains R, remove R and > + insert RANGE. */ > + if (r.end < range.end) > + { > + changed = true; > + it = ranges.erase (it); > + } > + /* R.end at E position. That means R already contains RANGE, nothing > + todo. */ > + else > + return false; > + } > + /* R.start at 3 position. */ > + else if (r.start > range.start && r.start < range.end) > + { > + /* R.end at F/D position. That means RANGE contains R, just remove R > + and insert RANGE later. */ > + if (r.end <= range.end) > + { > + changed = true; > + it = ranges.erase (it); > + } > + /* R.end at E position. That means RANGE's right part overlap R's > + left part. Expand RANGE.end to R.end and remove R. */ > + else if (r.end > range.end) > + { > + changed = true; > + range.end = r.end; > + it = ranges.erase (it); > + break; > + } > + } > + /* R.start at 4 position and R.end at E position. That means RANGE and > R > + are adjacent and can be merged. */ > + else if (r.start == range.end) > + { > + changed = true; > + range.end = r.end; > + it = ranges.erase (it); > + } > + /* R.start at 5 position and R.end at G position. That means R and > RANGE > + do not overlap. */ > + else > + break; > + } > + ranges.insert (range); > + return changed; > +} > + > +bool > +subreg_ranges::remove_range (int max, const subreg_range &range) > +{ > + if (empty_p ()) > + return false; > + else if (max == 1) > + { > + gcc_assert (range.start == 0 && range.end == 1); > + make_empty (); > + return true; > + } > + > + if (this->max == 1) > + { > + gcc_assert (full_p ()); > + change_max (max); > + } > + gcc_assert (this->max == max); > + gcc_assert (range.start < range.end); > + > + bool changed = false; > + auto it = ranges.begin (); > + std::set<subreg_range> new_ranges; > + while (it != ranges.end ()) > + { > + auto &r = *it; > + gcc_assert (r.start < r.end); > + > + /* The possible positional relationship of R and RANGE. > + 1~5 means R.start's possible position relative to RANGE > + A~G means R.end's possible position relative to RANGE > + caseN means when R.start at N positon, the R.end can be in which > + positions. > + > + RANGE.start RANGE.end > + [ ) > + | | > + R.start 1 2 3 4 5 > + R.end | | > + case1 A B C D E > + case2 | C D E > + case3 | F D E > + case4 | | E > + case5 | | G > + > + */ > + > + /* R.start at 1 position. */ > + if (r.start < range.start) > + { > + /* R.end at A/B position. That means RANGE and R do not overlap, > + nothing to remove. */ > + if (r.end <= range.start) > + it++; > + /* R.end at C/D position. That means R's rigth part contains RANGE, > + need shrink R.end to RANGE.start. */ > + else if (r.end <= range.end) > + { > + changed = true; > + new_ranges.insert (subreg_range (r.start, range.start)); > + it = ranges.erase (it); > + } > + /* R.end at E position. That means R's center part contains RANGE, > + need split R to two range, one range is [R.start, RANGE.start), > + another range is [RANGE.end, R.end). */ > + else > + { > + changed = true; > + new_ranges.insert (subreg_range (r.start, range.start)); > + new_ranges.insert (subreg_range (range.end, r.end)); > + it = ranges.erase (it); > + break; > + } > + } > + /* R.start at 2 position. */ > + else if (r.start == range.start) > + { > + /* R.end at C/D position. That means RANGE contains R, remove R. */ > + if (r.end <= range.end) > + { > + changed = true; > + it = ranges.erase (it); > + } > + /* R.end at E position. That means R's left part contains RANGE, > + shrink R.start to RANGE.end. */ > + else > + { > + changed = true; > + new_ranges.insert (subreg_range (range.end, r.end)); > + it = ranges.erase (it); > + break; > + } > + } > + /* R.start at 3 position. */ > + else if (r.start > range.start && r.start < range.end) > + { > + /* R.end at F/D position. That means RANGE contains R, remove R. */ > + if (r.end <= range.end) > + { > + changed = true; > + it = ranges.erase (it); > + } > + /* R.end at E position. That means RANGE's right part overlap R's left > + part, shrink R.start to RANGE.end. */ > + else > + { > + changed = true; > + new_ranges.insert (subreg_range (range.end, r.end)); > + it = ranges.erase (it); > + break; > + } > + } > + /* R.start at 4/5 position. That means RANGE and R do not overlap. */ > + else > + break; > + } > + for (auto &r : new_ranges) > + add_range (this->max, r); > + return changed; > +} > + > +bool > +subreg_ranges::add_ranges (const subreg_ranges &sr) > +{ > + gcc_assert (max == sr.max || max == 1 || sr.max == 1); > + > + if (full_p () || sr.empty_p ()) > + return false; > + else if (sr.full_p ()) > + { > + make_full (); > + return true; > + } > + > + bool changed = false; > + for (auto &r : sr.ranges) > + changed |= add_range (sr.max, r); > + return changed; > +} > + > +bool > +subreg_ranges::remove_ranges (const subreg_ranges &sr) > +{ > + if (empty_p () || sr.empty_p ()) > + return false; > + else if (sr.full_p ()) > + { > + make_empty (); > + return true; > + } > + > + gcc_assert (max == sr.max || max == 1 || sr.max == 1); > + > + bool changed = false; > + for (auto &r : sr.ranges) > + changed |= remove_range (sr.max, r); > + return changed; > +} > + > +bool > +subreg_ranges::same_p (const subreg_ranges &sr) const > +{ > + if (max == 1 || sr.max == 1) > + return (empty_p () && sr.empty_p ()) || (full_p () && sr.full_p ()); > + else if (max == sr.max) > + { > + if (ranges.size () != sr.ranges.size ()) > + return false; > + /* Make sure that the elements in each position are the same. */ > + auto it1 = ranges.begin (); > + auto it2 = sr.ranges.begin (); > + while (it1 != ranges.end ()) > + { > + const subreg_range &r1 = *it1; > + const subreg_range &r2 = *it2; > + if (r1.start != r2.start || r1.end != r2.end) > + return false; > + it1++; > + it2++; > + } > + return true; > + } > + else > + gcc_unreachable (); > +} > + > +bool > +subreg_ranges::include_ranges_p (const subreg_ranges &sr) const > +{ > + gcc_assert (max == sr.max || max == 1 || sr.max == 1); > + if (full_p ()) > + return true; > + if (empty_p () && sr.empty_p ()) > + return true; > + if (same_p (sr)) > + return true; > + > + for (const auto &r : sr.ranges) > + if (!include_range_p (sr.max, r)) > + return false; > + return true; > +} > + > +bool > +subreg_ranges::include_range_p (int max, const subreg_range &range) const > +{ > + gcc_assert (this->max == max); > + for (const auto &r : ranges) > + { > + if (r.start <= range.start && r.end >= range.end) > + return true; > + else if (r.start >= range.end) > + return false; > + } > + return false; > +} > + > +void > +subreg_ranges::dump (FILE *file) const > +{ > + if (empty_p ()) > + { > + fprintf (file, "empty"); > + return; > + } > + else if (full_p ()) > + { > + fprintf (file, "full"); > + return; > + } > + > + fprintf (file, "patial(max:%d", max); > + fprintf (file, " {"); > + for (auto &range : ranges) > + { > + fprintf (file, " "); > + range.dump (file); > + } > + fprintf (file, " })"); > +} > + > +/* class subregs_live */ > +bool > +subregs_live::copy_lives (const subregs_live &sl) > +{ > + bool changed = false; > + subregs_live temp; > + for (auto &kv : sl.lives) > + { > + unsigned int regno = kv.first; > + const subreg_ranges &sr = kv.second; > + if (lives.count (regno) == 0 && !sr.empty_p ()) > + { > + changed = true; > + temp.add_ranges (regno, sr); > + } > + else if (lives.count (regno) != 0) > + { > + changed |= !lives.at (regno).same_p (sr); > + temp.add_ranges (regno, sr); > + } > + } > + > + for (auto &kv : lives) > + { > + unsigned int regno = kv.first; > + subreg_ranges &sr = kv.second; > + if (temp.lives.count (regno) == 0 && !sr.empty_p ()) > + changed = true; > + } > + lives = temp.lives; > + return changed; > +} > + > +bool > +subregs_live::add_lives (const subregs_live &sl) > +{ > + bool changed = false; > + for (auto &kv : sl.lives) > + { > + unsigned int regno = kv.first; > + const subreg_ranges &sr = kv.second; > + if (sr.empty_p ()) > + continue; > + > + if (lives.count (regno) == 0) > + { > + changed = true; > + lives.insert ({regno, sr}); > + } > + else > + changed |= lives.at (regno).add_ranges (sr); > + } > + return changed; > +} > + > +bool > +subregs_live::remove_lives (const subregs_live &sl) > +{ > + bool changed = false; > + for (auto &kv : sl.lives) > + { > + unsigned int regno = kv.first; > + const subreg_ranges &sr = kv.second; > + if (sr.empty_p ()) > + continue; > + > + if (lives.count (regno) != 0) > + { > + changed |= lives.at (regno).remove_ranges (sr); > + if (lives.at (regno).empty_p ()) > + lives.erase (regno); > + } > + } > + return changed; > +} > + > +void > +subregs_live::dump (FILE *file, const char *indent) const > +{ > + if (lives.empty ()) > + { > + fprintf (file, "%sempty\n", indent); > + return; > + } > + fprintf (file, "%s", indent); > + for (auto &kv : lives) > + { > + const subreg_ranges &sr = kv.second; > + if (sr.empty_p ()) > + continue; > + fprintf (file, "%d ", kv.first); > + if (!sr.full_p ()) > + { > + sr.dump (file); > + fprintf (file, " "); > + } > + } > + fprintf (file, "\n"); > +} > + > +/* class live_point */ > +void > +live_point::dump (FILE *file) const > +{ > + if (!use_reg.empty_p ()) > + { > + fprintf (file, "use "); > + use_reg.dump (file); > + if (!def_reg.empty_p ()) > + { > + fprintf (file, ", def "); > + def_reg.dump (file); > + } > + } > + else if (!def_reg.empty_p ()) > + { > + fprintf (file, "def "); > + def_reg.dump (file); > + } > + else > + gcc_unreachable (); > +} > + > +/* class live_points */ > +void > +live_points::dump (FILE *file) const > +{ > + fprintf (file, "%u :", id); > + if (points.empty ()) > + { > + fprintf (file, " empty"); > + return; > + } > + for (const auto &kv : points) > + { > + fprintf (file, " "); > + kv.second.dump (file); > + fprintf (file, " at point %u;", kv.first); > + } > +} > + > +/* class reg_live_ranges */ > +void > +subregs_live_points::dump (FILE *file) const > +{ > + if (subreg_points.empty ()) > + { > + fprintf (file, ";; empty\n"); > + return; > + } > + for (const auto &kv : subreg_points) > + { > + fprintf (file, ";; "); > + kv.second.dump (file); > + fprintf (file, "\n"); > + } > +} > + > +/* Define some usefull debug functions. */ > + > +DEBUG_FUNCTION void > +debug (const subreg_range &r) > +{ > + r.dump (stderr); > +} > + > +DEBUG_FUNCTION void > +debug (const subreg_ranges &sr) > +{ > + sr.dump (stderr); > +} > + > +DEBUG_FUNCTION void > +debug (const subregs_live &l) > +{ > + l.dump (stderr, ""); > +} > + > +DEBUG_FUNCTION void > +debug (const subregs_live *l) > +{ > + debug (*l); > +} > + > +DEBUG_FUNCTION void > +debug (const live_point &l) > +{ > + l.dump (stderr); > +} > + > +DEBUG_FUNCTION void > +debug (const live_points &ls) > +{ > + ls.dump (stderr); > +} > + > +DEBUG_FUNCTION void > +debug (const subregs_live_points &sls) > +{ > + sls.dump (stderr); > +} > + > +DEBUG_FUNCTION void > +debug (const subregs_live_points *sls) > +{ > + debug (*sls); > +} > + > +#if CHECKING_P > + > +namespace selftest { > + > +class subreg_range_tests > +{ > +public: > + static void run () > + { > + /* class subreg_range tests. */ > + subreg_range r1 = subreg_range (1, 2); > + subreg_range r2 = subreg_range (2, 3); > + subreg_range r3 = subreg_range (2, 3); > + ASSERT_FALSE (r1.same_p (r2)); > + ASSERT_TRUE (r2.same_p (r3)); > + ASSERT_TRUE (r1 < r2); > + ASSERT_FALSE (r2 < r1); > + > + /* class subreg_ranges tests. */ > + } > +}; > + > +void > +subreg_live_range_tests () > +{ > + subreg_range_tests::run (); > +} > + > +} // namespace selftest > + > +#endif /* CHECKING_P */ > diff --git a/gcc/subreg-live-range.h b/gcc/subreg-live-range.h > new file mode 100644 > index 00000000000..76e442d08e8 > --- /dev/null > +++ b/gcc/subreg-live-range.h > @@ -0,0 +1,333 @@ > +/* SUBREG live range track classes for DF & IRA & LRA. > + Copyright (C) 2023 Free Software Foundation, Inc. > + Contributed by Lehua Ding (lehua.d...@rivai.ai), RiVAI Technologies Ltd. > + > + 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/>. */ > + > +#ifndef GCC_SUBREG_LIVE_RANGE_H > +#define GCC_SUBREG_LIVE_RANGE_H > + > +#include "config.h" > +#include "system.h" > +#include "coretypes.h" > +#include <set> > +#include <map> > + > +/* class subreg_range represent bytes range [start, end) of a reg. */ > +class subreg_range > +{ > +public: > + int start; /* Range start point. */ > + int end; /* Range start point. */ > + > + subreg_range (int start, int end) : start (start), end (end) > + { > + gcc_assert (start < end); > + } > + > + /* For sorting. */ > + bool operator<(const subreg_range &r) const > + { > + if (end <= r.start) > + return true; > + else if (start >= r.end) > + return false; > + else > + /* Cannot sorting for overlap range. */ > + gcc_unreachable (); > + } > + /* Return true if R same with self. */ > + bool same_p (const subreg_range &r) const > + { > + return start == r.start && end == r.end; > + } > + > + /* Return true if range is full for 0-MAX range. */ > + bool full_p (int max) const { return start == 0 && end == max; } > + > + /* Debug methods. */ > + void dump (FILE *file) const; > +}; > + > +/* class subreg_ranges represent multiple disjoint and discontinuous > + subreg_range. */ > +class subreg_ranges > +{ > +public: > + /* The maximum boundary value of range. If for a unknown mode hard > register, > + the max set to 1. */ > + int max; > + std::set<subreg_range> ranges; > + > + subreg_ranges () : max (1) {} > + subreg_ranges (int max) : max (max) { gcc_assert (max >= 1); } > + > + /* Modify ranges. */ > + /* Return true if ranges changed. */ > + bool add_range (int max, const subreg_range &range); > + /* Return true if ranges changed. */ > + bool remove_range (int max, const subreg_range &range); > + /* Add SR, return true if ranges changed. */ > + bool add_ranges (const subreg_ranges &sr); > + /* Clear ranges of SR, return true if ranges changed. */ > + bool remove_ranges (const subreg_ranges &sr); > + /* Make range empty. */ > + void make_empty () { ranges.clear (); } > + /* Make range full. */ > + void make_full () > + { > + make_empty (); > + ranges.insert (subreg_range (0, max)); > + } > + /* Change max to MAX, corresponding adjust ranges. */ > + void change_max (int max) > + { > + gcc_assert (this->max == 1); > + this->max = max; > + if (full_p ()) > + make_full (); > + } > + > + /* Predicators. */ > + bool full_p () const > + { > + if (ranges.size () != 1) > + return false; > + const subreg_range &r = *ranges.begin (); > + return r.start == 0 && r.end == max; > + } > + bool empty_p () const { return ranges.empty (); } > + bool same_p (const subreg_ranges &sr) const; > + bool same_p (int max, const subreg_range &range) const > + { > + subreg_ranges sr = subreg_ranges (max); > + sr.add_range (max, range); > + return same_p (sr); > + } > + bool include_ranges_p (const subreg_ranges &sr) const; > + bool include_range_p (int max, const subreg_range &range) const; > + > + /* Debug methods. */ > + void dump (FILE *file) const; > +}; > + > +/* class subregs_live record the live subreg_ranges of registers. */ > +class subregs_live > +{ > +public: > + /* The key is usually the register's regno. */ > + std::map<unsigned int, subreg_ranges> lives; > + > + /* Add/clear live range. */ > + bool add_range (unsigned int regno, int max, const subreg_range &range) > + { > + if (lives.count (regno) == 0) > + lives.insert ({regno, subreg_ranges (max)}); > + return lives.at (regno).add_range (max, range); > + } > + bool remove_range (unsigned int regno, int max, const subreg_range &range) > + { > + if (lives.count (regno) != 0) > + { > + bool changed = lives.at (regno).remove_range (max, range); > + if (lives.at (regno).empty_p ()) > + remove_live (regno); > + return changed; > + } > + return false; > + } > + /* Add a unexist register live range. */ > + void add_ranges (unsigned int regno, const subreg_ranges &ranges) > + { > + if (lives.count (regno) == 0) > + lives.insert ({regno, ranges}); > + else > + lives.at (regno).add_ranges (ranges); > + } > + bool copy_lives (const subregs_live &sl); > + bool add_lives (const subregs_live &sl); > + bool remove_lives (const subregs_live &sl); > + void remove_live (unsigned int regno) { lives.erase (regno); } > + /* Remove all register live range. */ > + void clear () { lives.clear (); } > + void clear (unsigned min_regno) > + { > + if (lives.lower_bound (min_regno) != lives.end ()) > + lives.erase (lives.lower_bound (min_regno), lives.end ()); > + } > + > + /* Return true if regno's live range is full. */ > + bool full_p (unsigned int regno) const > + { > + return lives.count (regno) != 0 && lives.at (regno).full_p (); > + } > + /* Return true if regno's live range is empty. */ > + bool empty_p (unsigned int regno) const > + { > + return lives.count (regno) == 0 || lives.at (regno).empty_p (); > + } > + /* Return true if SL same with this. */ > + bool same_p (const subregs_live &sl) > + { > + if (lives.size () != sl.lives.size ()) > + return false; > + for (auto &kv : lives) > + { > + unsigned int regno = kv.first; > + if (sl.empty_p (regno)) > + return false; > + const subreg_ranges &sr = kv.second; > + if (!sr.same_p (sl.lives.at (regno))) > + return false; > + } > + return true; > + } > + > + /* Debug methods. */ > + void dump (FILE *file, const char *indent = ";; ") const; > +}; > + > +class live_point > +{ > +public: > + int point; > + /* subreg range be defined in current point. */ > + subreg_ranges def_reg; > + /* subreg range be used in current point. */ > + subreg_ranges use_reg; > + > + live_point (int max, const subreg_range &range, bool is_def) > + : def_reg (max), use_reg (max) > + { > + add_range (max, range, is_def); > + } > + live_point (const subreg_ranges &sr, bool is_def) > + : def_reg (sr.max), use_reg (sr.max) > + { > + add_ranges (sr, is_def); > + } > + live_point (int point, int max) : point (point), def_reg (max), use_reg > (max) > + {} > + > + void add_range (int max, const subreg_range &r, bool is_def) > + { > + if (is_def) > + def_reg.add_range (max, r); > + else > + use_reg.add_range (max, r); > + } > + > + void add_ranges (const subreg_ranges &sr, bool is_def) > + { > + if (is_def) > + def_reg.add_ranges (sr); > + else > + use_reg.add_ranges (sr); > + } > + > + void dump (FILE *file) const; > +}; > + > +class live_points > +{ > +public: > + int id; > + int max; > + std::map<int, live_point> points; > + > + live_points (int id, int max) : id (id), max (max) {} > + > + void add_point (int max, const subreg_range &range, bool is_def, int point) > + { > + gcc_assert (this->max == max || this->max == 1 || max == 1); > + if (points.count (point) == 0) > + points.insert ({point, {max, range, is_def}}); > + else > + points.at (point).add_range (max, range, is_def); > + } > + void dump (FILE *file) const; > +}; > + > +class subregs_live_points > +{ > +public: > + std::map<int, live_points> subreg_points; > + std::map<int, int> last_start_points; > + std::map<int, subreg_ranges> subreg_live_ranges; > + > + void add_point (int id, int max, const subreg_range &range, bool is_def, > + int point) > + { > + if (!is_def && empty_live_p (id)) > + { > + if (last_start_points.count (id) == 0) > + last_start_points.insert ({id, point}); > + else > + last_start_points.at (id) = point; > + } > + > + if (subreg_points.count (id) == 0) > + subreg_points.insert ({id, live_points (id, max)}); > + > + subreg_points.at (id).add_point (max, range, is_def, point); > + > + if (subreg_live_ranges.count (id) == 0) > + subreg_live_ranges.insert ({id, subreg_ranges (max)}); > + > + if (is_def) > + subreg_live_ranges.at (id).remove_range (max, range); > + else > + subreg_live_ranges.at (id).add_range (max, range); > + } > + > + void add_range (int id, int max, const subreg_range &range, bool is_def) > + { > + if (subreg_live_ranges.count (id) == 0) > + subreg_live_ranges.insert ({id, subreg_ranges (max)}); > + > + if (is_def) > + subreg_live_ranges.at (id).remove_range (max, range); > + else > + subreg_live_ranges.at (id).add_range (max, range); > + } > + > + bool full_live_p (int id) > + { > + return subreg_live_ranges.count (id) != 0 > + && subreg_live_ranges.at (id).full_p (); > + } > + > + bool empty_live_p (int id) > + { > + return subreg_live_ranges.count (id) == 0 > + || subreg_live_ranges.at (id).empty_p (); > + } > + > + int get_start_point (int id) > + { > + int start_point = last_start_points.at (id); > + gcc_assert (start_point != -1); > + return start_point; > + } > + > + void clear_live_ranges () { subreg_live_ranges.clear (); } > + > + /* Debug methods. */ > + void dump (FILE *file) const; > +}; > + > +#endif /* GCC_SUBREG_LIVE_RANGE_H */ > diff --git a/gcc/timevar.def b/gcc/timevar.def > index d21b08c030d..7c173d3c7c8 100644 > --- a/gcc/timevar.def > +++ b/gcc/timevar.def > @@ -120,6 +120,7 @@ DEFTIMEVAR (TV_DF_SCAN , "df scan insns") > DEFTIMEVAR (TV_DF_MD , "df multiple defs") > DEFTIMEVAR (TV_DF_RD , "df reaching defs") > DEFTIMEVAR (TV_DF_LR , "df live regs") > +DEFTIMEVAR (TV_DF_LIVE_SUBREG , "df live subregs") > DEFTIMEVAR (TV_DF_LIVE , "df live&initialized regs") > DEFTIMEVAR (TV_DF_MIR , "df must-initialized regs") > DEFTIMEVAR (TV_DF_CHAIN , "df use-def / def-use chains")