diff options
-rw-r--r-- | gcc/ChangeLog | 48 | ||||
-rw-r--r-- | gcc/Makefile.in | 5 | ||||
-rw-r--r-- | gcc/common.opt | 4 | ||||
-rw-r--r-- | gcc/config/ia64/ia64.c | 16 | ||||
-rw-r--r-- | gcc/dbxout.c | 6 | ||||
-rw-r--r-- | gcc/debug.c | 3 | ||||
-rw-r--r-- | gcc/debug.h | 3 | ||||
-rw-r--r-- | gcc/doc/invoke.texi | 13 | ||||
-rw-r--r-- | gcc/doc/passes.texi | 13 | ||||
-rw-r--r-- | gcc/dwarf2out.c | 3 | ||||
-rw-r--r-- | gcc/final.c | 4 | ||||
-rw-r--r-- | gcc/flags.h | 3 | ||||
-rw-r--r-- | gcc/gengtype.c | 1 | ||||
-rw-r--r-- | gcc/opts.c | 4 | ||||
-rw-r--r-- | gcc/print-rtl.c | 8 | ||||
-rw-r--r-- | gcc/rtl.c | 2 | ||||
-rw-r--r-- | gcc/rtl.def | 2 | ||||
-rw-r--r-- | gcc/rtl.h | 13 | ||||
-rw-r--r-- | gcc/sdbout.c | 3 | ||||
-rw-r--r-- | gcc/timevar.def | 1 | ||||
-rw-r--r-- | gcc/toplev.c | 38 | ||||
-rw-r--r-- | gcc/tree.h | 1 | ||||
-rw-r--r-- | gcc/var-tracking.c | 2633 | ||||
-rw-r--r-- | gcc/vmsdbgout.c | 3 |
24 files changed, 2820 insertions, 10 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 45ef069185a..35aab6c99f5 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,51 @@ +2004-02-06 Josef Zlomek <zlomekj@suse.cz> + Daniel Berlin <dberlin@dberlin.org> + + Josef Zlomek <zlomekj@suse.cz> + * Makefile.in (var-tracking.o): New. + * common.opt (fvar-tracking): New. + * flags.h (flag_var_tracking): New. + * gengtype.c (adjust_field_rtx_def): NOTE_INSN_VAR_LOCATION was added. + * opts.c (common_handle_option): Add OPT_fvar_tracking. + * print-rtl.c (print_rtx): NOTE_INSN_VAR_LOCATION was added. + * rtl.c (note_insn_name): Likewise. + * rtl.def (VAR_LOCATION): New. + * rtl.h (NOTE_VAR_LOCATION): New. + (NOTE_VAR_LOCATION_DECL): New. + (NOTE_VAR_LOCATION_LOC): New. + (enum insn_note): NOTE_INSN_VAR_LOCATION was added. + (variable_tracking_main): New exported function. + * timevar.def (TV_VAR_TRACKING): New. + * toplev.c (enum dump_file_index): Added DFI_vartrack. + (dump_file): "vartrack" was added (-dV). + (flag_var_tracking): New. + (f_options): "var-tracking" was added. + (rest_of_handle_variable_tracking): New function. + (rest_of_compilation): Run variable tracking. + (process_options): If user has not specified flag_var_tracking set it + according to optimize, debug_info_level and debug_hooks. + * tree.h (frame_base_decl): New. + * var-tracking.c: New file. + * config/ia64/ia64.c (ia64_flag_var_tracking): New variable. + (ia64_override_options): Set flags to run variable tracking in machine + dependent reorg instead of toplev.c. + (ia64_reorg): Run variable tracking if wanted. + * doc/invoke.texi: Mention variable tracking in -dV, + add and -fvar-tracking. + * doc/passes.texi: Added variable tracking pass. + + Daniel Berlin <dberlin@dberlin.org> + * debug.h (struct gcc_debug_hooks): Added var_location debug hook. + * dbxout.c (dbx_debug_hooks): Likewise. + (xcoff_debug): Likewise. + * debug.c (do_nothing_debug_hooks): Likewise. + * dwarf2out.c (dwarf2_debug_hooks): Likewise. + * dwarfout.c (dwarf_debug_hooks): Likewise. + * sdbout.c (sdb_debug_hooks): Likewise. + * vmsdbgout.c (vmsdbg_debug_hooks): Likewise. + * final.c (final_scan_insn): Call var_location debug hook for each + NOTE_INSN_VAR_LOCATION. + 2004-02-06 Jan Hubicka <jh@suse.cz> * flow.c (update_life_info): Allocate reg_deaths when called from diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 8b94fc4271f..1b4edcf4766 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -852,7 +852,7 @@ OBJS-common = \ insn-extract.o insn-opinit.o insn-output.o insn-peep.o insn-recog.o \ integrate.o intl.o jump.o langhooks.o lcm.o lists.o local-alloc.o \ loop.o optabs.o options.o opts.o params.o postreload.o predict.o \ - print-rtl.o print-tree.o value-prof.o \ + print-rtl.o print-tree.o value-prof.o var-tracking.o \ profile.o ra.o ra-build.o ra-colorize.o ra-debug.o ra-rewrite.o \ real.o recog.o reg-stack.o regclass.o regmove.o regrename.o \ reload.o reload1.o reorg.o resource.o rtl.o rtlanal.o rtl-error.o \ @@ -1669,6 +1669,9 @@ lcm.o : lcm.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \ df.o : df.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ insn-config.h $(RECOG_H) function.h $(REGS_H) alloc-pool.h hard-reg-set.h \ $(BASIC_BLOCK_H) df.h $(FIBHEAP_H) +var-tracking.o : var-tracking.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ + $(RTL_H) $(TREE_H) hard-reg-set.h insn-config.h reload.h flags.h \ + $(BASIC_BLOCK_H) output.h sbitmap.h alloc-pool.h $(FIBHEAP_H) $(HASHTAB_H) conflict.o : conflict.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(OBSTACK_H) \ $(HASHTAB_H) $(RTL_H) hard-reg-set.h $(BASIC_BLOCK_H) profile.o : profile.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ diff --git a/gcc/common.opt b/gcc/common.opt index a384b0400ee..4c08b970587 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -715,6 +715,10 @@ funwind-tables Common Just generate unwind tables for exception handling +fvar-tracking +Common +Perform variable tracking + fverbose-asm Common Add extra commentary to assembler output diff --git a/gcc/config/ia64/ia64.c b/gcc/config/ia64/ia64.c index 7c258d93a70..4764a1a316b 100644 --- a/gcc/config/ia64/ia64.c +++ b/gcc/config/ia64/ia64.c @@ -117,6 +117,10 @@ const char *ia64_tune_string; avoid the normal second scheduling pass. */ static int ia64_flag_schedule_insns2; +/* Determines whether we run variable tracking in machine dependent + reorganization. */ +static int ia64_flag_var_tracking; + /* Variables which are this size or smaller are put in the sdata/sbss sections. */ @@ -4785,6 +4789,11 @@ ia64_override_options (void) ia64_flag_schedule_insns2 = flag_schedule_insns_after_reload; flag_schedule_insns_after_reload = 0; + /* Variable tracking should be run after all optimizations which change order + of insns. It also needs a valid CFG. */ + ia64_flag_var_tracking = flag_var_tracking; + flag_var_tracking = 0; + ia64_section_threshold = g_switch_set ? g_switch_value : IA64_DEFAULT_GVALUE; init_machine_status = ia64_init_machine_status; @@ -7630,6 +7639,13 @@ ia64_reorg (void) fixup_errata (); emit_predicate_relation_info (); + + if (ia64_flag_var_tracking) + { + timevar_push (TV_VAR_TRACKING); + variable_tracking_main (); + timevar_pop (TV_VAR_TRACKING); + } } /* Return true if REGNO is used by the epilogue. */ diff --git a/gcc/dbxout.c b/gcc/dbxout.c index 3561f434f41..e020d03f47a 100644 --- a/gcc/dbxout.c +++ b/gcc/dbxout.c @@ -400,7 +400,8 @@ const struct gcc_debug_hooks dbx_debug_hooks = debug_nothing_tree, /* deferred_inline_function */ debug_nothing_tree, /* outlining_inline_function */ debug_nothing_rtx, /* label */ - dbxout_handle_pch /* handle_pch */ + dbxout_handle_pch, /* handle_pch */ + debug_nothing_rtx /* var_location */ }; #endif /* DBX_DEBUGGING_INFO */ @@ -428,7 +429,8 @@ const struct gcc_debug_hooks xcoff_debug_hooks = debug_nothing_tree, /* deferred_inline_function */ debug_nothing_tree, /* outlining_inline_function */ debug_nothing_rtx, /* label */ - dbxout_handle_pch /* handle_pch */ + dbxout_handle_pch, /* handle_pch */ + debug_nothing_rtx /* var_location */ }; #endif /* XCOFF_DEBUGGING_INFO */ diff --git a/gcc/debug.c b/gcc/debug.c index 3b659e91bef..18d5bc8cae2 100644 --- a/gcc/debug.c +++ b/gcc/debug.c @@ -45,7 +45,8 @@ const struct gcc_debug_hooks do_nothing_debug_hooks = debug_nothing_tree, /* deferred_inline_function */ debug_nothing_tree, /* outlining_inline_function */ debug_nothing_rtx, /* label */ - debug_nothing_int /* handle_pch */ + debug_nothing_int, /* handle_pch */ + debug_nothing_rtx /* var_location */ }; /* This file contains implementations of each debug hook that do diff --git a/gcc/debug.h b/gcc/debug.h index 0ac7f027f94..448b028ea79 100644 --- a/gcc/debug.h +++ b/gcc/debug.h @@ -108,6 +108,9 @@ struct gcc_debug_hooks /* Called after the start and before the end of writing a PCH file. The parameter is 0 if after the start, 1 if before the end. */ void (* handle_pch) (unsigned int); + + /* Called from final_scan_insn for any NOTE_INSN_VAR_LOCATION note. */ + void (* var_location) (rtx); }; extern const struct gcc_debug_hooks *debug_hooks; diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index e4b02b08e01..2a8d919833e 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -251,7 +251,7 @@ in the following sections. -feliminate-dwarf2-dups -feliminate-unused-debug-types @gol -feliminate-unused-debug-symbols -fmem-report -fprofile-arcs @gol -frandom-seed=@var{string} -fsched-verbose=@var{n} @gol --ftest-coverage -ftime-report @gol +-ftest-coverage -ftime-report -fvar-tracking @gol -g -g@var{level} -gcoff -gdwarf-2 @gol -ggdb -gstabs -gstabs+ -gvms -gxcoff -gxcoff+ @gol -p -pg -print-file-name=@var{library} -print-libgcc-file-name @gol @@ -3313,6 +3313,7 @@ Dump callgraph and unit-at-a-time optimization @file{@var{file}.00.unit}. @item V @opindex dV Dump after the value profile transformations, to @file{@var{file}.13.vpt}. +Also dump after variable tracking, to @file{@var{file}.35.vartrack}. @item w @opindex dw Dump after the second flow pass, to @file{@var{file}.27.flow2}. @@ -3467,6 +3468,16 @@ executing the program itself. The second number is ``system time,'' time spent executing operating system routines on behalf of the program. Both numbers are in seconds. +@item -fvar-tracking +@opindex fvar-tracking +Run variable tracking pass. It computes where variables are stored at each +position in code. Better debugging information is then generated +(if the debugging information format supports this information). + +It is enabled by default when compiling with optimization (@option{-Os}, +@option{-O}, @option{-O2}, ...), debugging information (@option{-g}) and +the debug info format supports it. + @item -print-file-name=@var{library} @opindex print-file-name Print the full absolute name of the library file @var{library} that diff --git a/gcc/doc/passes.texi b/gcc/doc/passes.texi index 6bbc61c2822..a71c7c6c444 100644 --- a/gcc/doc/passes.texi +++ b/gcc/doc/passes.texi @@ -507,6 +507,19 @@ The option @option{-dB} causes a debugging dump of the RTL code after this pass. This dump file's name is made by appending @samp{.bbro} to the input file name. +@cindex variable tracking +@item +Variable tracking. This pass computes where the variables are stored at each +position in code and generates notes describing the variable locations +to RTL code. The location lists are then generated according to these +notes to debug information if the debugging information format supports +location lists. + +@opindex dV +The option @option{-dV} causes a debugging dump of the RTL code after +this pass. This dump file's name is made by appending @samp{.vartrack} +to the input file name. + @cindex delayed branch scheduling @cindex scheduling, delayed branch @item diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index 68698730c99..e407a0b4d0c 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -3280,7 +3280,8 @@ const struct gcc_debug_hooks dwarf2_debug_hooks = something tries to reference them. */ dwarf2out_abstract_function, /* outlining_inline_function */ debug_nothing_rtx, /* label */ - debug_nothing_int /* handle_pch */ + debug_nothing_int, /* handle_pch */ + debug_nothing_rtx /* var_location */ }; #endif diff --git a/gcc/final.c b/gcc/final.c index dd5b64ec53f..b12d8b63f0b 100644 --- a/gcc/final.c +++ b/gcc/final.c @@ -1782,6 +1782,10 @@ final_scan_insn (rtx insn, FILE *file, int optimize ATTRIBUTE_UNUSED, ASM_OUTPUT_DEBUG_LABEL (file, "L", CODE_LABEL_NUMBER (insn)); break; + case NOTE_INSN_VAR_LOCATION: + (*debug_hooks->var_location) (insn); + break; + case 0: break; diff --git a/gcc/flags.h b/gcc/flags.h index 370fb0a1936..1ca9d98365d 100644 --- a/gcc/flags.h +++ b/gcc/flags.h @@ -723,6 +723,9 @@ extern int flag_web; used. */ extern int flag_remove_unreachable_functions; +/* Nonzero if we should track variables. */ +extern int flag_var_tracking; + /* A string that's used when a random name is required. NULL means to make it really random. */ diff --git a/gcc/gengtype.c b/gcc/gengtype.c index 3f06ef9b5c7..86286c7cb22 100644 --- a/gcc/gengtype.c +++ b/gcc/gengtype.c @@ -450,6 +450,7 @@ adjust_field_rtx_def (type_p t, options_p opt ATTRIBUTE_UNUSED) break; case NOTE_INSN_EXPECTED_VALUE: + case NOTE_INSN_VAR_LOCATION: note_flds->name = "rtx"; note_flds->type = rtx_tp; break; diff --git a/gcc/opts.c b/gcc/opts.c index f49228b8cc1..d54d1b44552 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -1436,6 +1436,10 @@ common_handle_option (size_t scode, const char *arg, flag_unwind_tables = value; break; + case OPT_fvar_tracking: + flag_var_tracking = value; + break; + case OPT_fverbose_asm: flag_verbose_asm = value; break; diff --git a/gcc/print-rtl.c b/gcc/print-rtl.c index f5e30fe9782..cafdb475c58 100644 --- a/gcc/print-rtl.c +++ b/gcc/print-rtl.c @@ -291,6 +291,14 @@ print_rtx (rtx in_rtx) fprintf (outfile, " [ ERROR ]"); break; + case NOTE_INSN_VAR_LOCATION: + fprintf (outfile, " ("); + print_mem_expr (outfile, NOTE_VAR_LOCATION_DECL (in_rtx)); + fprintf (outfile, " "); + print_rtx (NOTE_VAR_LOCATION_LOC (in_rtx)); + fprintf (outfile, ")"); + break; + default: { const char * const str = X0STR (in_rtx, i); diff --git a/gcc/rtl.c b/gcc/rtl.c index cf72f5b6d18..d0081587198 100644 --- a/gcc/rtl.c +++ b/gcc/rtl.c @@ -122,7 +122,7 @@ const char * const note_insn_name[NOTE_INSN_MAX - NOTE_INSN_BIAS] = "NOTE_INSN_EH_REGION_BEG", "NOTE_INSN_EH_REGION_END", "NOTE_INSN_REPEATED_LINE_NUMBER", "NOTE_INSN_BASIC_BLOCK", "NOTE_INSN_EXPECTED_VALUE", - "NOTE_INSN_PREDICTION" + "NOTE_INSN_PREDICTION", "NOTE_INSN_VAR_LOCATION" }; const char * const reg_note_name[] = diff --git a/gcc/rtl.def b/gcc/rtl.def index 67e79838e05..9dd4af53e4e 100644 --- a/gcc/rtl.def +++ b/gcc/rtl.def @@ -1215,6 +1215,8 @@ DEF_RTL_EXPR(SS_TRUNCATE, "ss_truncate", "e", '1') /* Unsigned saturating truncate. */ DEF_RTL_EXPR(US_TRUNCATE, "us_truncate", "e", '1') +/* Information about the variable and its location. */ +DEF_RTL_EXPR(VAR_LOCATION, "var_location", "te", 'x') /* Local variables: diff --git a/gcc/rtl.h b/gcc/rtl.h index 13fd72ba26f..05ad03d2f27 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -819,6 +819,7 @@ extern const char * const reg_note_name[]; #define NOTE_EXPECTED_VALUE(INSN) XCEXP (INSN, 4, NOTE) #define NOTE_PREDICTION(INSN) XCINT (INSN, 4, NOTE) #define NOTE_PRECONDITIONED(INSN) XCINT (INSN, 4, NOTE) +#define NOTE_VAR_LOCATION(INSN) XCEXP (INSN, 4, NOTE) /* In a NOTE that is a line number, this is the line number. Other kinds of NOTEs are identified by negative numbers here. */ @@ -834,6 +835,12 @@ extern const char * const reg_note_name[]; #define NOTE_PREDICTION_FLAGS(INSN) (XCINT(INSN, 4, NOTE)&0xff) #define NOTE_PREDICT(ALG,FLAGS) ((ALG<<8)+(FLAGS)) +/* Variable declaration and the location of a variable. */ +#define NOTE_VAR_LOCATION_DECL(INSN) (XCTREE (XCEXP (INSN, 4, NOTE), \ + 0, VAR_LOCATION)) +#define NOTE_VAR_LOCATION_LOC(INSN) (XCEXP (XCEXP (INSN, 4, NOTE), \ + 1, VAR_LOCATION)) + /* Codes that appear in the NOTE_LINE_NUMBER field for kinds of notes that are not line numbers. @@ -917,6 +924,9 @@ enum insn_note /* Record a prediction. Uses NOTE_PREDICTION. */ NOTE_INSN_PREDICTION, + /* The location of a variable. */ + NOTE_INSN_VAR_LOCATION, + NOTE_INSN_MAX }; @@ -2343,4 +2353,7 @@ extern bool expensive_function_p (int); /* In tracer.c */ extern void tracer (void); +/* In var-tracking.c */ +extern void variable_tracking_main (void); + #endif /* ! GCC_RTL_H */ diff --git a/gcc/sdbout.c b/gcc/sdbout.c index d33b5c1cc1d..648b4c9ccde 100644 --- a/gcc/sdbout.c +++ b/gcc/sdbout.c @@ -335,7 +335,8 @@ const struct gcc_debug_hooks sdb_debug_hooks = debug_nothing_tree, /* deferred_inline_function */ debug_nothing_tree, /* outlining_inline_function */ sdbout_label, /* label */ - debug_nothing_int /* handle_pch */ + debug_nothing_int, /* handle_pch */ + debug_nothing_rtx /* var_location */ }; /* Return a unique string to name an anonymous type. */ diff --git a/gcc/timevar.def b/gcc/timevar.def index 8ecd7ee1650..cd776fb5f4a 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -95,6 +95,7 @@ DEFTIMEVAR (TV_SHORTEN_BRANCH , "shorten branches") DEFTIMEVAR (TV_REG_STACK , "reg stack") DEFTIMEVAR (TV_FINAL , "final") DEFTIMEVAR (TV_SYMOUT , "symout") +DEFTIMEVAR (TV_VAR_TRACKING , "variable tracking") /* Everything else in rest_of_compilation not included above. */ DEFTIMEVAR (TV_REST_OF_COMPILATION , "rest of compilation") diff --git a/gcc/toplev.c b/gcc/toplev.c index 369ba5c04f4..8330c89bc44 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -158,6 +158,7 @@ static void rest_of_handle_reorder_blocks (tree, rtx); #ifdef STACK_REGS static void rest_of_handle_stack_regs (tree, rtx); #endif +static void rest_of_handle_variable_tracking (tree, rtx); static void rest_of_handle_machine_reorg (tree, rtx); #ifdef DELAY_SLOTS static void rest_of_handle_delay_slots (tree, rtx); @@ -289,6 +290,7 @@ enum dump_file_index DFI_branch_target_load, DFI_sched2, DFI_stack, + DFI_vartrack, DFI_mach, DFI_dbr, DFI_MAX @@ -340,6 +342,7 @@ static struct dump_file_info dump_file[DFI_MAX] = { "btl", 'd', 1, 0, 0 }, /* Yes, duplicate enable switch. */ { "sched2", 'R', 1, 0, 0 }, { "stack", 'k', 1, 0, 0 }, + { "vartrack", 'V', 1, 0, 0 }, /* Yes, duplicate enable switch. */ { "mach", 'M', 1, 0, 0 }, { "dbr", 'd', 0, 0, 0 }, }; @@ -965,6 +968,13 @@ int flag_tracer = 0; int flag_unit_at_a_time = 0; +/* Nonzero if we should track variables. When + flag_var_tracking == AUTODETECT_FLAG_VAR_TRACKING it will be set according + to optimize, debug_info_level and debug_hooks in process_options (). */ + +#define AUTODETECT_FLAG_VAR_TRACKING 2 +int flag_var_tracking = AUTODETECT_FLAG_VAR_TRACKING; + /* Values of the -falign-* flags: how much to align labels in code. 0 means `use default', 1 means `don't align'. For each variable, there is an _log variant which is the power @@ -1145,7 +1155,8 @@ static const lang_independent_options f_options[] = {"mem-report", &mem_report, 1 }, { "trapv", &flag_trapv, 1 }, { "wrapv", &flag_wrapv, 1 }, - { "new-ra", &flag_new_regalloc, 1 } + { "new-ra", &flag_new_regalloc, 1 }, + { "var-tracking", &flag_var_tracking, 1} }; /* Here is a table, controlled by the tm.h file, listing each -m switch @@ -2148,6 +2159,18 @@ rest_of_handle_stack_regs (tree decl, rtx insns) } #endif +/* Track the variables, ie. compute where the variable is stored at each position in function. */ +static void +rest_of_handle_variable_tracking (tree decl, rtx insns) +{ + timevar_push (TV_VAR_TRACKING); + open_dump_file (DFI_vartrack, decl); + + variable_tracking_main (); + + close_dump_file (DFI_vartrack, print_rtl_with_bb, insns); + timevar_pop (TV_VAR_TRACKING); +} /* Machine independent reorg pass. */ static void @@ -3562,6 +3585,9 @@ rest_of_compilation (tree decl) compute_alignments (); + if (flag_var_tracking) + rest_of_handle_variable_tracking (decl, insns); + /* CFG is no longer maintained up-to-date. */ free_bb_for_insn (); @@ -4414,6 +4440,16 @@ process_options (void) error ("target system does not support the \"%s\" debug format", debug_type_names[write_symbols]); + /* Now we know which debug output will be used so we can set + flag_var_tracking if user has not specified it. */ + if (flag_var_tracking == AUTODETECT_FLAG_VAR_TRACKING) + { + /* User has not specified -f(no-)var-tracking so autodetect it. */ + flag_var_tracking + = (optimize >= 1 && debug_info_level >= DINFO_LEVEL_NORMAL + && debug_hooks->var_location != do_nothing_debug_hooks.var_location); + } + /* If auxiliary info generation is desired, open the output file. This goes in the same directory as the source file--unlike all the other output files. */ diff --git a/gcc/tree.h b/gcc/tree.h index 930a1e1da26..f6d3baf5c81 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -2060,6 +2060,7 @@ enum ptrmemfunc_vbit_where_t #define NULL_TREE (tree) NULL +extern tree frame_base_decl; extern tree decl_assembler_name (tree); /* Compute the number of bytes occupied by 'node'. This routine only diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c new file mode 100644 index 00000000000..7a1653d3ca4 --- /dev/null +++ b/gcc/var-tracking.c @@ -0,0 +1,2633 @@ +/* Variable tracking routines for the GNU compiler. + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, 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 COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* This file contains the variable tracking pass. It computes where + variables are located (which registers or where in memory) at each position + in instruction stream and emits notes describing the locations. + Debug information (DWARF2 location lists) is finally generated from + these notes. + With this debug information, it is possible to show variables + even when debugging optimized code. + + How does the variable tracking pass work? + + First, it scans RTL code for uses, stores and clobbers (register/memory + references in instructions), for call insns and for stack adjustments + separately for each basic block and saves them to an array of micro + operations. + The micro operations of one instruction are ordered so that + pre-modifying stack adjustment < use < use with no var < call insn < + < set < clobber < post-modifying stack adjustment + + Then, a forward dataflow analysis is performed to find out how locations + of variables change through code and to propagate the variable locations + along control flow graph. + The IN set for basic block BB is computed as a union of OUT sets of BB's + predecessors, the OUT set for BB is copied from the IN set for BB and + is changed according to micro operations in BB. + + The IN and OUT sets for basic blocks consist of a current stack adjustment + (used for adjusting offset of variables addressed using stack pointer), + the table of structures describing the locations of parts of a variable + and for each physical register a linked list for each physical register. + The linked list is a list of variable parts stored in the register, + i.e. it is a list of triplets (reg, decl, offset) where decl is + REG_EXPR (reg) and offset is REG_OFFSET (reg). The linked list is used for + effective deleting appropriate variable parts when we set or clobber the + register. + + There may be more than one variable part in a register. The linked lists + should be pretty short so it is a good data structure here. + For example in the following code, register allocator may assign same + register to variables A and B, and both of them are stored in the same + register in CODE: + + if (cond) + set A; + else + set B; + CODE; + if (cond) + use A; + else + use B; + + Finally, the NOTE_INSN_VAR_LOCATION notes describing the variable locations + are emitted to appropriate positions in RTL code. Each such a note describes + the location of one variable at the point in instruction stream where the + note is. There is no need to emit a note for each variable before each + instruction, we only emit these notes where the location of variable changes + (this means that we also emit notes for changes between the OUT set of the + previous block and the IN set of the current block). + + The notes consist of two parts: + 1. the declaration (from REG_EXPR or MEM_EXPR) + 2. the location of a variable - it is either a simple register/memory + reference (for simple variables, for example int), + or a parallel of register/memory references (for a large variables + which consist of several parts, for example long long). + +*/ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "rtl.h" +#include "tree.h" +#include "hard-reg-set.h" +#include "basic-block.h" +#include "flags.h" +#include "output.h" +#include "insn-config.h" +#include "reload.h" +#include "sbitmap.h" +#include "alloc-pool.h" +#include "fibheap.h" +#include "hashtab.h" + +/* Type of micro operation. */ +enum micro_operation_type +{ + MO_USE, /* Use location (REG or MEM). */ + MO_USE_NO_VAR,/* Use location which is not associated with a variable + or the variable is not trackable. */ + MO_SET, /* Set location. */ + MO_CLOBBER, /* Clobber location. */ + MO_CALL, /* Call insn. */ + MO_ADJUST /* Adjust stack pointer. */ +}; + +/* Where shall the note be emitted? BEFORE or AFTER the instruction. */ +enum emit_note_where +{ + EMIT_NOTE_BEFORE_INSN, + EMIT_NOTE_AFTER_INSN +}; + +/* Structure holding information about micro operation. */ +typedef struct micro_operation_def +{ + /* Type of micro operation. */ + enum micro_operation_type type; + + union { + /* Location. */ + rtx loc; + + /* Stack adjustment. */ + HOST_WIDE_INT adjust; + } u; + + /* The instruction which the micro operation is in. */ + rtx insn; +} micro_operation; + +/* Structure for passing some other parameters to function + emit_note_insn_var_location. */ +typedef struct emit_note_data_def +{ + /* The instruction which the note will be emitted before/after. */ + rtx insn; + + /* Where the note will be emitted (before/after insn)? */ + enum emit_note_where where; +} emit_note_data; + +/* Description of location of a part of a variable. The content of a physical + register is described by a chain of these structures. + The chains are pretty short (usually 1 or 2 elements) and thus + chain is the best data structure. */ +typedef struct attrs_def +{ + /* Pointer to next member of the list. */ + struct attrs_def *next; + + /* The rtx of register. */ + rtx loc; + + /* The declaration corresponding to LOC. */ + tree decl; + + /* Offset from start of DECL. */ + HOST_WIDE_INT offset; +} *attrs; + +/* Structure holding the IN or OUT set for a basic block. */ +typedef struct dataflow_set_def +{ + /* Adjustment of stack offset. */ + HOST_WIDE_INT stack_adjust; + + /* Attributes for registers (lists of attrs). */ + attrs regs[FIRST_PSEUDO_REGISTER]; + + /* Variable locations. */ + htab_t vars; +} dataflow_set; + +/* The structure (one for each basic block) containing the information + needed for variable tracking. */ +typedef struct variable_tracking_info_def +{ + /* Number of micro operations stored in the MOS array. */ + int n_mos; + + /* The array of micro operations. */ + micro_operation *mos; + + /* The IN and OUT set for dataflow analysis. */ + dataflow_set in; + dataflow_set out; + + /* Has the block been visited in DFS? */ + bool visited; +} *variable_tracking_info; + +/* Structure for chaining the locations. */ +typedef struct location_chain_def +{ + /* Next element in the chain. */ + struct location_chain_def *next; + + /* The location (REG or MEM). */ + rtx loc; +} *location_chain; + +/* Structure describing one part of variable. */ +typedef struct variable_part_def +{ + /* Chain of locations of the part. */ + location_chain loc_chain; + + /* Location which was last emitted to location list. */ + rtx cur_loc; + + /* The offset in the variable. */ + HOST_WIDE_INT offset; +} variable_part; + +/* Maximum number of location parts. */ +#define MAX_VAR_PARTS 16 + +/* Structure describing where the variable is located. */ +typedef struct variable_def +{ + /* The declaration of the variable. */ + tree decl; + + /* Number of variable parts. */ + int n_var_parts; + + /* The variable parts. */ + variable_part var_part[MAX_VAR_PARTS]; +} *variable; + +/* Hash function for DECL for VARIABLE_HTAB. */ +#define VARIABLE_HASH_VAL(decl) ((size_t) (decl)) + +/* Pointer to the BB's information specific to variable tracking pass. */ +#define VTI(BB) ((variable_tracking_info) (BB)->aux) + +/* Alloc pool for struct attrs_def. */ +static alloc_pool attrs_pool; + +/* Alloc pool for struct variable_def. */ +static alloc_pool var_pool; + +/* Alloc pool for struct location_chain_def. */ +static alloc_pool loc_chain_pool; + +/* Changed variables, notes will be emitted for them. */ +static htab_t changed_variables; + +/* Shall notes be emitted? */ +static bool emit_notes; + +/* Fake variable for stack pointer. */ +tree frame_base_decl; + +/* Local function prototypes. */ +static void stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *, + HOST_WIDE_INT *); +static void insn_stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *, + HOST_WIDE_INT *); +static void bb_stack_adjust_offset (basic_block); +static HOST_WIDE_INT prologue_stack_adjust (void); +static bool vt_stack_adjustments (void); +static rtx adjust_stack_reference (rtx, HOST_WIDE_INT); +static hashval_t variable_htab_hash (const void *); +static int variable_htab_eq (const void *, const void *); +static void variable_htab_free (void *); + +static void init_attrs_list_set (attrs *); +static void attrs_list_clear (attrs *); +static attrs attrs_list_member (attrs, tree, HOST_WIDE_INT); +static void attrs_list_insert (attrs *, tree, HOST_WIDE_INT, rtx); +static void attrs_list_copy (attrs *, attrs); +static void attrs_list_union (attrs *, attrs); + +static void vars_clear (htab_t); +static int vars_copy_1 (void **, void *); +static void vars_copy (htab_t, htab_t); +static void var_reg_delete_and_set (dataflow_set *, rtx); +static void var_reg_delete (dataflow_set *, rtx); +static void var_regno_delete (dataflow_set *, int); +static void var_mem_delete_and_set (dataflow_set *, rtx); +static void var_mem_delete (dataflow_set *, rtx); + +static void dataflow_set_init (dataflow_set *, int); +static void dataflow_set_clear (dataflow_set *); +static void dataflow_set_copy (dataflow_set *, dataflow_set *); +static int variable_union_info_cmp_pos (const void *, const void *); +static int variable_union (void **, void *); +static void dataflow_set_union (dataflow_set *, dataflow_set *); +static bool variable_part_different_p (variable_part *, variable_part *); +static bool variable_different_p (variable, variable); +static int dataflow_set_different_1 (void **, void *); +static int dataflow_set_different_2 (void **, void *); +static bool dataflow_set_different (dataflow_set *, dataflow_set *); +static void dataflow_set_destroy (dataflow_set *); + +static bool contains_symbol_ref (rtx); +static bool track_expr_p (tree); +static int count_uses (rtx *, void *); +static void count_uses_1 (rtx *, void *); +static void count_stores (rtx, rtx, void *); +static int add_uses (rtx *, void *); +static void add_uses_1 (rtx *, void *); +static void add_stores (rtx, rtx, void *); +static bool compute_bb_dataflow (basic_block); +static void vt_find_locations (void); + +static void dump_attrs_list (attrs); +static int dump_variable (void **, void *); +static void dump_vars (htab_t); +static void dump_dataflow_set (dataflow_set *); +static void dump_dataflow_sets (void); + +static void variable_was_changed (variable, htab_t); +static void set_frame_base_location (dataflow_set *, rtx); +static void set_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT); +static void delete_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT); +static int emit_note_insn_var_location (void **, void *); +static void emit_notes_for_changes (rtx, enum emit_note_where); +static int emit_notes_for_differences_1 (void **, void *); +static int emit_notes_for_differences_2 (void **, void *); +static void emit_notes_for_differences (rtx, dataflow_set *, dataflow_set *); +static void emit_notes_in_bb (basic_block); +static void vt_emit_notes (void); + +static bool vt_get_decl_and_offset (rtx, tree *, HOST_WIDE_INT *); +static void vt_add_function_parameters (void); +static void vt_initialize (void); +static void vt_finalize (void); + +/* Given a SET, calculate the amount of stack adjustment it contains + PRE- and POST-modifying stack pointer. + This function is similar to stack_adjust_offset. */ + +static void +stack_adjust_offset_pre_post (rtx pattern, HOST_WIDE_INT *pre, + HOST_WIDE_INT *post) +{ + rtx src = SET_SRC (pattern); + rtx dest = SET_DEST (pattern); + enum rtx_code code; + + if (dest == stack_pointer_rtx) + { + /* (set (reg sp) (plus (reg sp) (const_int))) */ + code = GET_CODE (src); + if (! (code == PLUS || code == MINUS) + || XEXP (src, 0) != stack_pointer_rtx + || GET_CODE (XEXP (src, 1)) != CONST_INT) + return; + + if (code == MINUS) + *post += INTVAL (XEXP (src, 1)); + else + *post -= INTVAL (XEXP (src, 1)); + } + else if (GET_CODE (dest) == MEM) + { + /* (set (mem (pre_dec (reg sp))) (foo)) */ + src = XEXP (dest, 0); + code = GET_CODE (src); + + switch (code) + { + case PRE_MODIFY: + case POST_MODIFY: + if (XEXP (src, 0) == stack_pointer_rtx) + { + rtx val = XEXP (XEXP (src, 1), 1); + /* We handle only adjustments by constant amount. */ + if (GET_CODE (XEXP (src, 1)) != PLUS || + GET_CODE (val) != CONST_INT) + abort (); + if (code == PRE_MODIFY) + *pre -= INTVAL (val); + else + *post -= INTVAL (val); + break; + } + return; + + case PRE_DEC: + if (XEXP (src, 0) == stack_pointer_rtx) + { + *pre += GET_MODE_SIZE (GET_MODE (dest)); + break; + } + return; + + case POST_DEC: + if (XEXP (src, 0) == stack_pointer_rtx) + { + *post += GET_MODE_SIZE (GET_MODE (dest)); + break; + } + return; + + case PRE_INC: + if (XEXP (src, 0) == stack_pointer_rtx) + { + *pre -= GET_MODE_SIZE (GET_MODE (dest)); + break; + } + return; + + case POST_INC: + if (XEXP (src, 0) == stack_pointer_rtx) + { + *post -= GET_MODE_SIZE (GET_MODE (dest)); + break; + } + return; + + default: + return; + } + } +} + +/* Given an INSN, calculate the amount of stack adjustment it contains + PRE- and POST-modifying stack pointer. */ + +static void +insn_stack_adjust_offset_pre_post (rtx insn, HOST_WIDE_INT *pre, + HOST_WIDE_INT *post) +{ + *pre = 0; + *post = 0; + + if (GET_CODE (PATTERN (insn)) == SET) + stack_adjust_offset_pre_post (PATTERN (insn), pre, post); + else if (GET_CODE (PATTERN (insn)) == PARALLEL + || GET_CODE (PATTERN (insn)) == SEQUENCE) + { + int i; + + /* There may be stack adjustments inside compound insns. Search + for them. */ + for ( i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) + if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET) + stack_adjust_offset_pre_post (XVECEXP (PATTERN (insn), 0, i), + pre, post); + } +} + +/* Compute stack adjustnment in basic block BB. */ + +static void +bb_stack_adjust_offset (basic_block bb) +{ + HOST_WIDE_INT offset; + int i; + + offset = VTI (bb)->in.stack_adjust; + for (i = 0; i < VTI (bb)->n_mos; i++) + { + if (VTI (bb)->mos[i].type == MO_ADJUST) + offset += VTI (bb)->mos[i].u.adjust; + else if (VTI (bb)->mos[i].type != MO_CALL) + { + if (GET_CODE (VTI (bb)->mos[i].u.loc) == MEM) + { + VTI (bb)->mos[i].u.loc + = adjust_stack_reference (VTI (bb)->mos[i].u.loc, -offset); + } + } + } + VTI (bb)->out.stack_adjust = offset; +} + +/* Compute stack adjustment caused by function prolog. */ + +static HOST_WIDE_INT +prologue_stack_adjust (void) +{ + HOST_WIDE_INT offset = 0; + basic_block bb = ENTRY_BLOCK_PTR->next_bb; + rtx insn; + rtx end; + + if (!BB_END (bb)) + return 0; + + end = NEXT_INSN (BB_END (bb)); + for (insn = BB_HEAD (bb); insn != end; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == NOTE + && NOTE_LINE_NUMBER (insn) == NOTE_INSN_PROLOGUE_END) + break; + + if (INSN_P (insn)) + { + HOST_WIDE_INT tmp; + + insn_stack_adjust_offset_pre_post (insn, &tmp, &tmp); + offset += tmp; + } + } + + return offset; +} + +/* Compute stack adjustments for all blocks by traversing DFS tree. + Return true when the adjustments on all incoming edges are consistent. + Heavily borrowed from flow_depth_first_order_compute. */ + +static bool +vt_stack_adjustments (void) +{ + edge *stack; + int sp; + + /* Initialize enttry block. */ + VTI (ENTRY_BLOCK_PTR)->visited = true; + VTI (ENTRY_BLOCK_PTR)->out.stack_adjust = 0; + + /* Allocate stack for back-tracking up CFG. */ + stack = xmalloc ((n_basic_blocks + 1) * sizeof (edge)); + sp = 0; + + /* Push the first edge on to the stack. */ + stack[sp++] = ENTRY_BLOCK_PTR->succ; + + while (sp) + { + edge e; + basic_block src; + basic_block dest; + + /* Look at the edge on the top of the stack. */ + e = stack[sp - 1]; + src = e->src; + dest = e->dest; + + /* Check if the edge destination has been visited yet. */ + if (!VTI (dest)->visited) + { + VTI (dest)->visited = true; + VTI (dest)->in.stack_adjust = VTI (src)->out.stack_adjust; + bb_stack_adjust_offset (dest); + + if (dest->succ) + /* Since the DEST node has been visited for the first + time, check its successors. */ + stack[sp++] = dest->succ; + } + else + { + /* Check whether the adjustments on the edges are the same. */ + if (VTI (dest)->in.stack_adjust != VTI (src)->out.stack_adjust) + { + free (stack); + return false; + } + + if (e->succ_next) + /* Go to the next edge. */ + stack[sp - 1] = e->succ_next; + else + /* Return to previous level if there are no more edges. */ + sp--; + } + } + + free (stack); + return true; +} + +/* Adjust stack reference MEM by ADJUSTMENT bytes and return the new rtx. */ + +static rtx +adjust_stack_reference (rtx mem, HOST_WIDE_INT adjustment) +{ + rtx adjusted_mem; + rtx tmp; + + adjusted_mem = copy_rtx (mem); + XEXP (adjusted_mem, 0) = replace_rtx (XEXP (adjusted_mem, 0), + stack_pointer_rtx, + gen_rtx_PLUS (Pmode, stack_pointer_rtx, + GEN_INT (adjustment))); + tmp = simplify_rtx (XEXP (adjusted_mem, 0)); + if (tmp) + XEXP (adjusted_mem, 0) = tmp; + + return adjusted_mem; +} + +/* The hash function for variable_htab, computes the hash value + from the declaration of variable X. */ + +static hashval_t +variable_htab_hash (const void *x) +{ + const variable v = (const variable) x; + + return (VARIABLE_HASH_VAL (v->decl)); +} + +/* Compare the declaration of variable X with declaration Y. */ + +static int +variable_htab_eq (const void *x, const void *y) +{ + const variable v = (const variable) x; + const tree decl = (const tree) y; + + return (VARIABLE_HASH_VAL (v->decl) == VARIABLE_HASH_VAL (decl)); +} + +/* Free the element of VARIABLE_HTAB (its type is struct variable_def). */ + +static void +variable_htab_free (void *elem) +{ + int i; + variable var = (variable) elem; + location_chain node, next; + + for (i = 0; i < var->n_var_parts; i++) + { + for (node = var->var_part[i].loc_chain; node; node = next) + { + next = node->next; + pool_free (loc_chain_pool, node); + } + var->var_part[i].loc_chain = NULL; + } + pool_free (var_pool, var); +} + +/* Initialize the set (array) SET of attrs to empty lists. */ + +static void +init_attrs_list_set (attrs *set) +{ + int i; + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + set[i] = NULL; +} + +/* Make the list *LISTP empty. */ + +static void +attrs_list_clear (attrs *listp) +{ + attrs list, next; + + for (list = *listp; list; list = next) + { + next = list->next; + pool_free (attrs_pool, list); + } + *listp = NULL; +} + +/* Return true if the pair of DECL and OFFSET is the member of the LIST. */ + +static attrs +attrs_list_member (attrs list, tree decl, HOST_WIDE_INT offset) +{ + for (; list; list = list->next) + if (list->decl == decl && list->offset == offset) + return list; + return NULL; +} + +/* Insert the triplet DECL, OFFSET, LOC to the list *LISTP. */ + +static void +attrs_list_insert (attrs *listp, tree decl, HOST_WIDE_INT offset, rtx loc) +{ + attrs list; + + list = pool_alloc (attrs_pool); + list->loc = loc; + list->decl = decl; + list->offset = offset; + list->next = *listp; + *listp = list; +} + +/* Copy all nodes from SRC and create a list *DSTP of the copies. */ + +static void +attrs_list_copy (attrs *dstp, attrs src) +{ + attrs n; + + attrs_list_clear (dstp); + for (; src; src = src->next) + { + n = pool_alloc (attrs_pool); + n->loc = src->loc; + n->decl = src->decl; + n->offset = src->offset; + n->next = *dstp; + *dstp = n; + } +} + +/* Add all nodes from SRC which are not in *DSTP to *DSTP. */ + +static void +attrs_list_union (attrs *dstp, attrs src) +{ + for (; src; src = src->next) + { + if (!attrs_list_member (*dstp, src->decl, src->offset)) + attrs_list_insert (dstp, src->decl, src->offset, src->loc); + } +} + +/* Delete all variables from hash table VARS. */ + +static void +vars_clear (htab_t vars) +{ + htab_empty (vars); +} + +/* Copy one variable from *SLOT to hash table DATA. */ + +static int +vars_copy_1 (void **slot, void *data) +{ + htab_t dst = (htab_t) data; + variable src, *dstp, var; + int i; + + src = *(variable *) slot; + dstp = (variable *) htab_find_slot_with_hash (dst, src->decl, + VARIABLE_HASH_VAL (src->decl), + INSERT); + var = pool_alloc (var_pool); + var->decl = src->decl; + var->n_var_parts = src->n_var_parts; + *dstp = (void *) var; + + for (i = 0; i < var->n_var_parts; i++) + { + location_chain last, node; + + var->var_part[i].offset = src->var_part[i].offset; + last = NULL; + for (node = src->var_part[i].loc_chain; node; node = node->next) + { + location_chain new_lc; + + new_lc = pool_alloc (loc_chain_pool); + new_lc->next = NULL; + new_lc->loc = node->loc; + + if (last) + last->next = new_lc; + else + var->var_part[i].loc_chain = new_lc; + last = new_lc; + } + + /* We are at the basic block boundary when copying variable description + so set the CUR_LOC to be the first element of the chain. */ + if (var->var_part[i].loc_chain) + var->var_part[i].cur_loc = var->var_part[i].loc_chain->loc; + else + var->var_part[i].cur_loc = NULL; + } + + /* Continue traversing the hash table. */ + return 1; +} + +/* Copy all variables from hash table SRC to hash table DST. */ + +static void +vars_copy (htab_t dst, htab_t src) +{ + vars_clear (dst); + htab_traverse (src, vars_copy_1, dst); +} + +/* Delete current content of register LOC in dataflow set SET + and set the register to contain REG_EXPR (LOC), REG_OFFSET (LOC). */ + +static void +var_reg_delete_and_set (dataflow_set *set, rtx loc) +{ + attrs *reg = &set->regs[REGNO (loc)]; + tree decl = REG_EXPR (loc); + HOST_WIDE_INT offset = REG_OFFSET (loc); + attrs node, prev, next; + + prev = NULL; + for (node = *reg; node; node = next) + { + next = node->next; + if (node->decl != decl || node->offset != offset) + { + delete_variable_part (set, node->loc, node->decl, node->offset); + + if (prev) + prev->next = next; + else + *reg = next; + pool_free (attrs_pool, node); + } + else + { + node->loc = loc; + prev = node; + } + } + if (*reg == NULL) + attrs_list_insert (reg, decl, offset, loc); + set_variable_part (set, loc, decl, offset); +} + +/* Delete current content of register LOC in dataflow set SET. */ + +static void +var_reg_delete (dataflow_set *set, rtx loc) +{ + attrs *reg = &set->regs[REGNO (loc)]; + attrs node, next; + + for (node = *reg; node; node = next) + { + next = node->next; + delete_variable_part (set, node->loc, node->decl, node->offset); + pool_free (attrs_pool, node); + } + *reg = NULL; +} + +/* Delete content of register with number REGNO in dataflow set SET. */ + +static void +var_regno_delete (dataflow_set *set, int regno) +{ + attrs *reg = &set->regs[regno]; + attrs node, next; + + for (node = *reg; node; node = next) + { + next = node->next; + delete_variable_part (set, node->loc, node->decl, node->offset); + pool_free (attrs_pool, node); + } + *reg = NULL; +} + +/* Delete and set the location part of variable MEM_EXPR (LOC) + in dataflow set SET to LOC. + Adjust the address first if it is stack pointer based. */ + +static void +var_mem_delete_and_set (dataflow_set *set, rtx loc) +{ + tree decl = MEM_EXPR (loc); + HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0; + + set_variable_part (set, loc, decl, offset); +} + +/* Delete the location part LOC from dataflow set SET. + Adjust the address first if it is stack pointer based. */ + +static void +var_mem_delete (dataflow_set *set, rtx loc) +{ + tree decl = MEM_EXPR (loc); + HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0; + + delete_variable_part (set, loc, decl, offset); +} + +/* Initialize dataflow set SET to be empty. + VARS_SIZE is the initial size of hash table VARS. */ + +static void +dataflow_set_init (dataflow_set *set, int vars_size) +{ + init_attrs_list_set (set->regs); + set->vars = htab_create (vars_size, variable_htab_hash, variable_htab_eq, + variable_htab_free); + set->stack_adjust = 0; +} + +/* Delete the contents of dataflow set SET. */ + +static void +dataflow_set_clear (dataflow_set *set) +{ + int i; + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + attrs_list_clear (&set->regs[i]); + + vars_clear (set->vars); +} + +/* Copy the contents of dataflow set SRC to DST. */ + +static void +dataflow_set_copy (dataflow_set *dst, dataflow_set *src) +{ + int i; + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + attrs_list_copy (&dst->regs[i], src->regs[i]); + + vars_copy (dst->vars, src->vars); + dst->stack_adjust = src->stack_adjust; +} + +/* Information for merging lists of locations for a given offset of variable. + */ +struct variable_union_info +{ + /* Node of the location chain. */ + location_chain lc; + + /* The sum of positions in the input chains. */ + int pos; + + /* The position in the chains of SRC and DST dataflow sets. */ + int pos_src; + int pos_dst; +}; + +/* Compare function for qsort, order the structures by POS element. */ + +static int +variable_union_info_cmp_pos (const void *n1, const void *n2) +{ + const struct variable_union_info *i1 = n1; + const struct variable_union_info *i2 = n2; + + if (i1->pos != i2->pos) + return i1->pos - i2->pos; + + return (i1->pos_dst - i2->pos_dst); +} + +/* Compute union of location parts of variable *SLOT and the same variable + from hash table DATA. Compute "sorted" union of the location chains + for common offsets, i.e. the locations of a variable part are sorted by + a priority where the priority is the sum of the positions in the 2 chains + (if a location is only in one list the position in the second list is + defined to be larger than the length of the chains). + When we are updating the location parts the newest location is in the + beginning of the chain, so when we do the described "sorted" union + we keep the newest locations in the beginning. */ + +static int +variable_union (void **slot, void *data) +{ + variable src, dst, *dstp; + dataflow_set *set = (dataflow_set *) data; + int i, j, k; + + src = *(variable *) slot; + dstp = (variable *) htab_find_slot_with_hash (set->vars, src->decl, + VARIABLE_HASH_VAL (src->decl), + INSERT); + if (!*dstp) + { + *dstp = dst = pool_alloc (var_pool); + dst->decl = src->decl; + dst->n_var_parts = 0; + } + else + dst = *dstp; + +#ifdef ENABLE_CHECKING + if (src->n_var_parts == 0) + abort (); +#endif + + /* Count the number of location parts, result is K. */ + for (i = 0, j = 0, k = 0; + i < src->n_var_parts && j < dst->n_var_parts; k++) + { + if (src->var_part[i].offset == dst->var_part[j].offset) + { + i++; + j++; + } + else if (src->var_part[i].offset < dst->var_part[j].offset) + i++; + else + j++; + } + if (i < src->n_var_parts) + k += src->n_var_parts - i; + if (j < dst->n_var_parts) + k += dst->n_var_parts - j; +#ifdef ENABLE_CHECKING + /* We track only variables whose size is <= MAX_VAR_PARTS bytes + thus there are at most MAX_VAR_PARTS different offsets. */ + if (k > MAX_VAR_PARTS) + abort (); +#endif + + i = src->n_var_parts - 1; + j = dst->n_var_parts - 1; + dst->n_var_parts = k; + + for (k--; k >= 0; k--) + { + location_chain node; + + if (i >= 0 && j >= 0 + && src->var_part[i].offset == dst->var_part[j].offset) + { + /* Compute the "sorted" union of the chains, i.e. the locations which + are in both chains go first, they are sorted by the sum of + positions in the chains. */ + int dst_l, src_l; + int ii, jj, n; + struct variable_union_info *vui; + + src_l = 0; + for (node = src->var_part[i].loc_chain; node; node = node->next) + src_l++; + dst_l = 0; + for (node = dst->var_part[j].loc_chain; node; node = node->next) + dst_l++; + vui = xcalloc (src_l + dst_l, sizeof (struct variable_union_info)); + + /* Fill in the locations from DST. */ + for (node = dst->var_part[j].loc_chain, jj = 0; node; + node = node->next, jj++) + { + vui[jj].lc = node; + vui[jj].pos_dst = jj; + + /* Value larger than a sum of 2 valid positions. */ + vui[jj].pos_src = src_l + dst_l; + } + + /* Fill in the locations from SRC. */ + n = dst_l; + for (node = src->var_part[i].loc_chain, ii = 0; node; + node = node->next, ii++) + { + /* Find location from NODE. */ + for (jj = 0; jj < dst_l; jj++) + { + if ((GET_CODE (vui[jj].lc->loc) == REG + && GET_CODE (node->loc) == REG + && REGNO (vui[jj].lc->loc) == REGNO (node->loc)) + || rtx_equal_p (vui[jj].lc->loc, node->loc)) + { + vui[jj].pos_src = ii; + break; + } + } + if (jj >= dst_l) /* The location has not been found. */ + { + location_chain new_node; + + /* Copy the location from SRC. */ + new_node = pool_alloc (loc_chain_pool); + new_node->loc = node->loc; + vui[n].lc = new_node; + vui[n].pos_src = ii; + vui[n].pos_dst = src_l + dst_l; + n++; + } + } + + for (ii = 0; ii < src_l + dst_l; ii++) + vui[ii].pos = vui[ii].pos_src + vui[ii].pos_dst; + + qsort (vui, n, sizeof (struct variable_union_info), + variable_union_info_cmp_pos); + + /* Reconnect the nodes in sorted order. */ + for (ii = 1; ii < n; ii++) + vui[ii - 1].lc->next = vui[ii].lc; + vui[n - 1].lc->next = NULL; + + dst->var_part[k].loc_chain = vui[0].lc; + dst->var_part[k].offset = dst->var_part[j].offset; + + free (vui); + i--; + j--; + } + else if ((i >= 0 && j >= 0 + && src->var_part[i].offset < dst->var_part[j].offset) + || i < 0) + { + dst->var_part[k] = dst->var_part[j]; + j--; + } + else if ((i >= 0 && j >= 0 + && src->var_part[i].offset > dst->var_part[j].offset) + || j < 0) + { + location_chain last = NULL; + + /* Copy the chain from SRC. */ + for (node = src->var_part[i].loc_chain; node; node = node->next) + { + location_chain new_lc; + + new_lc = pool_alloc (loc_chain_pool); + new_lc->next = NULL; + new_lc->loc = node->loc; + + if (last) + last->next = new_lc; + else + dst->var_part[k].loc_chain = new_lc; + last = new_lc; + } + + dst->var_part[k].offset = src->var_part[i].offset; + i--; + } + + /* We are at the basic block boundary when computing union + so set the CUR_LOC to be the first element of the chain. */ + if (dst->var_part[k].loc_chain) + dst->var_part[k].cur_loc = dst->var_part[k].loc_chain->loc; + else + dst->var_part[k].cur_loc = NULL; + } + + /* Continue traversing the hash table. */ + return 1; +} + +/* Compute union of dataflow sets SRC and DST and store it to DST. */ + +static void +dataflow_set_union (dataflow_set *dst, dataflow_set *src) +{ + int i; + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + attrs_list_union (&dst->regs[i], src->regs[i]); + + htab_traverse (src->vars, variable_union, dst); +} + +/* Flag whether two dataflow sets being compared contain different data. */ +static bool +dataflow_set_different_value; + +static bool +variable_part_different_p (variable_part *vp1, variable_part *vp2) +{ + location_chain lc1, lc2; + + for (lc1 = vp1->loc_chain; lc1; lc1 = lc1->next) + { + for (lc2 = vp2->loc_chain; lc2; lc2 = lc2->next) + { + if (GET_CODE (lc1->loc) == REG && GET_CODE (lc2->loc) == REG) + { + if (REGNO (lc1->loc) == REGNO (lc2->loc)) + break; + } + if (rtx_equal_p (lc1->loc, lc2->loc)) + break; + } + if (!lc2) + return true; + } + return false; +} + +/* Return true if variables VAR1 and VAR2 are different (only the first + location in the list of locations is checked for each offset, + i.e. when true is returned a note should be emitted). */ + +static bool +variable_different_p (variable var1, variable var2) +{ + int i; + + if (var1->n_var_parts != var2->n_var_parts) + return true; + + for (i = 0; i < var1->n_var_parts; i++) + { + if (var1->var_part[i].offset != var2->var_part[i].offset) + return true; + if (variable_part_different_p (&var1->var_part[i], &var2->var_part[i])) + return true; + if (variable_part_different_p (&var2->var_part[i], &var1->var_part[i])) + return true; + } + return false; +} + +/* Compare variable *SLOT with the same variable in hash table DATA + and set DATAFLOW_SET_DIFFERENT_VALUE if they are different. */ + +static int +dataflow_set_different_1 (void **slot, void *data) +{ + htab_t htab = (htab_t) data; + variable var1, var2; + + var1 = *(variable *) slot; + var2 = (variable) htab_find_with_hash (htab, var1->decl, + VARIABLE_HASH_VAL (var1->decl)); + if (!var2) + { + dataflow_set_different_value = true; + + /* Stop traversing the hash table. */ + return 0; + } + + if (variable_different_p (var1, var2)) + { + dataflow_set_different_value = true; + + /* Stop traversing the hash table. */ + return 0; + } + + /* Continue traversing the hash table. */ + return 1; +} + +/* Compare variable *SLOT with the same variable in hash table DATA + and set DATAFLOW_SET_DIFFERENT_VALUE if they are different. */ + +static int +dataflow_set_different_2 (void **slot, void *data) +{ + htab_t htab = (htab_t) data; + variable var1, var2; + + var1 = *(variable *) slot; + var2 = (variable) htab_find_with_hash (htab, var1->decl, + VARIABLE_HASH_VAL (var1->decl)); + if (!var2) + { + dataflow_set_different_value = true; + + /* Stop traversing the hash table. */ + return 0; + } + +#ifdef ENABLE_CHECKING + /* If both variables are defined they have been already checked for + equivalence. */ + if (variable_different_p (var1, var2)) + abort (); +#endif + + /* Continue traversing the hash table. */ + return 1; +} + +/* Return true if dataflow sets OLD_SET and NEW_SET differ. */ + +static bool +dataflow_set_different (dataflow_set *old_set, dataflow_set *new_set) +{ + dataflow_set_different_value = false; + + htab_traverse (old_set->vars, dataflow_set_different_1, new_set->vars); + if (!dataflow_set_different_value) + { + /* We have compared the variables which are in both hash tables + so now only check whether there are some variables in NEW_SET->VARS + which are not in OLD_SET->VARS. */ + htab_traverse (new_set->vars, dataflow_set_different_2, old_set->vars); + } + return dataflow_set_different_value; +} + +/* Free the contents of dataflow set SET. */ + +static void +dataflow_set_destroy (dataflow_set *set) +{ + int i; + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + attrs_list_clear (&set->regs[i]); + + htab_delete (set->vars); + set->vars = NULL; +} + +/* Return true if RTL X contains a SYMBOL_REF. */ + +static bool +contains_symbol_ref (rtx x) +{ + const char *fmt; + RTX_CODE code; + int i; + + if (!x) + return false; + + code = GET_CODE (x); + if (code == SYMBOL_REF) + return true; + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + if (contains_symbol_ref (XEXP (x, i))) + return true; + } + else if (fmt[i] == 'E') + { + int j; + for (j = 0; j < XVECLEN (x, i); j++) + if (contains_symbol_ref (XVECEXP (x, i, j))) + return true; + } + } + + return false; +} + +/* Shall EXPR be tracked? */ + +static bool +track_expr_p (tree expr) +{ + rtx decl_rtl; + + /* If EXPR is not a parameter or a variable do not track it. */ + if (TREE_CODE (expr) != VAR_DECL && TREE_CODE (expr) != PARM_DECL) + return 0; + + /* It also must have a name... */ + if (!DECL_NAME (expr)) + return 0; + + /* ... and a RTL assigned to it. */ + decl_rtl = DECL_RTL_IF_SET (expr); + if (!decl_rtl) + return 0; + + /* Do not track global variables until we are able to emit correct location + list for them. */ + if (TREE_STATIC (expr)) + return 0; + + /* When the EXPR is a DECL for alias of some variable (see example) + the TREE_STATIC flag is not used. Disable tracking all DECLs whose + DECL_RTL contains SYMBOL_REF. + + Example: + extern char **_dl_argv_internal __attribute__ ((alias ("_dl_argv"))); + char **_dl_argv; + */ + if (GET_CODE (decl_rtl) == MEM + && contains_symbol_ref (XEXP (decl_rtl, 0))) + return 0; + + /* If RTX is a memory it should not be very large (because it would be + an array or struct). */ + if (GET_CODE (decl_rtl) == MEM) + { + /* Do not track structures and arrays. */ + if (GET_MODE (decl_rtl) == BLKmode) + return 0; + if (MEM_SIZE (decl_rtl) + && INTVAL (MEM_SIZE (decl_rtl)) > MAX_VAR_PARTS) + return 0; + } + + return 1; +} + +/* Count uses (register and memory references) LOC which will be tracked. + INSN is instruction which the LOC is part of. */ + +static int +count_uses (rtx *loc, void *insn) +{ + basic_block bb = BLOCK_FOR_INSN ((rtx) insn); + + if (GET_CODE (*loc) == REG) + { +#ifdef ENABLE_CHECKING + if (REGNO (*loc) >= FIRST_PSEUDO_REGISTER) + abort (); +#endif + VTI (bb)->n_mos++; + } + else if (GET_CODE (*loc) == MEM + && MEM_EXPR (*loc) + && track_expr_p (MEM_EXPR (*loc))) + { + VTI (bb)->n_mos++; + } + + return 0; +} + +/* Helper function for finding all uses of REG/MEM in X in insn INSN. */ + +static void +count_uses_1 (rtx *x, void *insn) +{ + for_each_rtx (x, count_uses, insn); +} + +/* Count stores (register and memory references) LOC which will be tracked. + INSN is instruction which the LOC is part of. */ + +static void +count_stores (rtx loc, rtx expr ATTRIBUTE_UNUSED, void *insn) +{ + count_uses (&loc, insn); +} + +/* Add uses (register and memory references) LOC which will be tracked + to VTI (bb)->mos. INSN is instruction which the LOC is part of. */ + +static int +add_uses (rtx *loc, void *insn) +{ + if (GET_CODE (*loc) == REG) + { + basic_block bb = BLOCK_FOR_INSN ((rtx) insn); + micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++; + + mo->type = ((REG_EXPR (*loc) && track_expr_p (REG_EXPR (*loc))) + ? MO_USE : MO_USE_NO_VAR); + mo->u.loc = *loc; + mo->insn = (rtx) insn; + } + else if (GET_CODE (*loc) == MEM + && MEM_EXPR (*loc) + && track_expr_p (MEM_EXPR (*loc))) + { + basic_block bb = BLOCK_FOR_INSN ((rtx) insn); + micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++; + + mo->type = MO_USE; + mo->u.loc = *loc; + mo->insn = (rtx) insn; + } + + return 0; +} + +/* Helper function for finding all uses of REG/MEM in X in insn INSN. */ + +static void +add_uses_1 (rtx *x, void *insn) +{ + for_each_rtx (x, add_uses, insn); +} + +/* Add stores (register and memory references) LOC which will be tracked + to VTI (bb)->mos. EXPR is the RTL expression containing the store. + INSN is instruction which the LOC is part of. */ + +static void +add_stores (rtx loc, rtx expr, void *insn) +{ + if (GET_CODE (loc) == REG) + { + basic_block bb = BLOCK_FOR_INSN ((rtx) insn); + micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++; + + mo->type = ((GET_CODE (expr) != CLOBBER && REG_EXPR (loc) + && track_expr_p (REG_EXPR (loc))) + ? MO_SET : MO_CLOBBER); + mo->u.loc = loc; + mo->insn = (rtx) insn; + } + else if (GET_CODE (loc) == MEM + && MEM_EXPR (loc) + && track_expr_p (MEM_EXPR (loc))) + { + basic_block bb = BLOCK_FOR_INSN ((rtx) insn); + micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++; + + mo->type = GET_CODE (expr) == CLOBBER ? MO_CLOBBER : MO_SET; + mo->u.loc = loc; + mo->insn = (rtx) insn; + } +} + +/* Compute the changes of variable locations in the basic block BB. */ + +static bool +compute_bb_dataflow (basic_block bb) +{ + int i, n, r; + bool changed; + dataflow_set old_out; + dataflow_set *in = &VTI (bb)->in; + dataflow_set *out = &VTI (bb)->out; + + dataflow_set_init (&old_out, htab_elements (VTI (bb)->out.vars) + 3); + dataflow_set_copy (&old_out, out); + dataflow_set_copy (out, in); + + n = VTI (bb)->n_mos; + for (i = 0; i < n; i++) + { + switch (VTI (bb)->mos[i].type) + { + case MO_CALL: + for (r = 0; r < FIRST_PSEUDO_REGISTER; r++) + if (TEST_HARD_REG_BIT (call_used_reg_set, r)) + var_regno_delete (out, r); + break; + + case MO_USE: + case MO_SET: + { + rtx loc = VTI (bb)->mos[i].u.loc; + + if (GET_CODE (loc) == REG) + var_reg_delete_and_set (out, loc); + else if (GET_CODE (loc) == MEM) + var_mem_delete_and_set (out, loc); + } + break; + + case MO_USE_NO_VAR: + case MO_CLOBBER: + { + rtx loc = VTI (bb)->mos[i].u.loc; + + if (GET_CODE (loc) == REG) + var_reg_delete (out, loc); + else if (GET_CODE (loc) == MEM) + var_mem_delete (out, loc); + } + break; + + case MO_ADJUST: + { + rtx base; + + out->stack_adjust += VTI (bb)->mos[i].u.adjust; + base = gen_rtx_MEM (Pmode, + gen_rtx_PLUS (Pmode, stack_pointer_rtx, + GEN_INT (out->stack_adjust))); + set_frame_base_location (out, base); + } + break; + } + } + + changed = dataflow_set_different (&old_out, out); + dataflow_set_destroy (&old_out); + return changed; +} + +/* Find the locations of variables in the whole function. */ + +static void +vt_find_locations (void) +{ + fibheap_t worklist, pending, fibheap_swap; + sbitmap visited, in_worklist, in_pending, sbitmap_swap; + basic_block bb; + edge e; + int *bb_order; + int *rc_order; + int i; + + /* Compute reverse completion order of depth first search of the CFG + so that the data-flow runs faster. */ + rc_order = (int *) xmalloc (n_basic_blocks * sizeof (int)); + bb_order = (int *) xmalloc (last_basic_block * sizeof (int)); + flow_depth_first_order_compute (NULL, rc_order); + for (i = 0; i < n_basic_blocks; i++) + bb_order[rc_order[i]] = i; + free (rc_order); + + worklist = fibheap_new (); + pending = fibheap_new (); + visited = sbitmap_alloc (last_basic_block); + in_worklist = sbitmap_alloc (last_basic_block); + in_pending = sbitmap_alloc (last_basic_block); + sbitmap_zero (in_worklist); + sbitmap_zero (in_pending); + + FOR_EACH_BB (bb) + { + fibheap_insert (pending, bb_order[bb->index], bb); + SET_BIT (in_pending, bb->index); + } + + while (!fibheap_empty (pending)) + { + fibheap_swap = pending; + pending = worklist; + worklist = fibheap_swap; + sbitmap_swap = in_pending; + in_pending = in_worklist; + in_worklist = sbitmap_swap; + + sbitmap_zero (visited); + + while (!fibheap_empty (worklist)) + { + bb = fibheap_extract_min (worklist); + RESET_BIT (in_worklist, bb->index); + if (!TEST_BIT (visited, bb->index)) + { + bool changed; + + SET_BIT (visited, bb->index); + + /* Calculate the IN set as union of predecessor OUT sets. */ + dataflow_set_clear (&VTI (bb)->in); + for (e = bb->pred; e; e = e->pred_next) + { + dataflow_set_union (&VTI (bb)->in, &VTI (e->src)->out); + } + + changed = compute_bb_dataflow (bb); + if (changed) + { + for (e = bb->succ; e; e = e->succ_next) + { + if (e->dest == EXIT_BLOCK_PTR) + continue; + + if (e->dest == bb) + continue; + + if (TEST_BIT (visited, e->dest->index)) + { + if (!TEST_BIT (in_pending, e->dest->index)) + { + /* Send E->DEST to next round. */ + SET_BIT (in_pending, e->dest->index); + fibheap_insert (pending, + bb_order[e->dest->index], + e->dest); + } + } + else if (!TEST_BIT (in_worklist, e->dest->index)) + { + /* Add E->DEST to current round. */ + SET_BIT (in_worklist, e->dest->index); + fibheap_insert (worklist, bb_order[e->dest->index], + e->dest); + } + } + } + } + } + } + + free (bb_order); + fibheap_delete (worklist); + fibheap_delete (pending); + sbitmap_free (visited); + sbitmap_free (in_worklist); + sbitmap_free (in_pending); +} + +/* Print the content of the LIST to dump file. */ + +static void +dump_attrs_list (attrs list) +{ + for (; list; list = list->next) + { + print_mem_expr (rtl_dump_file, list->decl); + fprintf (rtl_dump_file, "+"); + fprintf (rtl_dump_file, HOST_WIDE_INT_PRINT_DEC, list->offset); + } + fprintf (rtl_dump_file, "\n"); +} + +/* Print the information about variable *SLOT to dump file. */ + +static int +dump_variable (void **slot, void *data ATTRIBUTE_UNUSED) +{ + variable var = *(variable *) slot; + int i; + location_chain node; + + fprintf (rtl_dump_file, " name: %s\n", + IDENTIFIER_POINTER (DECL_NAME (var->decl))); + for (i = 0; i < var->n_var_parts; i++) + { + fprintf (rtl_dump_file, " offset %ld\n", + (long) var->var_part[i].offset); + for (node = var->var_part[i].loc_chain; node; node = node->next) + { + fprintf (rtl_dump_file, " "); + print_rtl_single (rtl_dump_file, node->loc); + } + } + + /* Continue traversing the hash table. */ + return 1; +} + +/* Print the information about variables from hash table VARS to dump file. */ + +static void +dump_vars (htab_t vars) +{ + if (htab_elements (vars) > 0) + { + fprintf (rtl_dump_file, "Variables:\n"); + htab_traverse (vars, dump_variable, NULL); + } +} + +/* Print the dataflow set SET to dump file. */ + +static void +dump_dataflow_set (dataflow_set *set) +{ + int i; + + fprintf (rtl_dump_file, "Stack adjustment: "); + fprintf (rtl_dump_file, HOST_WIDE_INT_PRINT_DEC, set->stack_adjust); + fprintf (rtl_dump_file, "\n"); + for (i = 1; i < FIRST_PSEUDO_REGISTER; i++) + { + if (set->regs[i]) + { + fprintf (rtl_dump_file, "Reg %d:", i); + dump_attrs_list (set->regs[i]); + } + } + dump_vars (set->vars); + fprintf (rtl_dump_file, "\n"); +} + +/* Print the IN and OUT sets for each basic block to dump file. */ + +static void +dump_dataflow_sets (void) +{ + basic_block bb; + + FOR_EACH_BB (bb) + { + fprintf (rtl_dump_file, "\nBasic block %d:\n", bb->index); + fprintf (rtl_dump_file, "IN:\n"); + dump_dataflow_set (&VTI (bb)->in); + fprintf (rtl_dump_file, "OUT:\n"); + dump_dataflow_set (&VTI (bb)->out); + } +} + +/* Add variable VAR to the hash table of changed variables and + if it has no locations delete it from hash table HTAB. */ + +static void +variable_was_changed (variable var, htab_t htab) +{ + hashval_t hash = VARIABLE_HASH_VAL (var->decl); + + if (emit_notes) + { + variable *slot; + + slot = (variable *) htab_find_slot_with_hash (changed_variables, + var->decl, hash, INSERT); + + if (htab && var->n_var_parts == 0) + { + variable empty_var; + void **old; + + empty_var = pool_alloc (var_pool); + empty_var->decl = var->decl; + empty_var->n_var_parts = 0; + *slot = empty_var; + + old = htab_find_slot_with_hash (htab, var->decl, hash, + NO_INSERT); + if (old) + htab_clear_slot (htab, old); + } + else + { + *slot = var; + } + } + else + { +#ifdef ENABLE_CHECKING + if (!htab) + abort (); +#endif + if (var->n_var_parts == 0) + { + void **slot = htab_find_slot_with_hash (htab, var->decl, hash, + NO_INSERT); + if (slot) + htab_clear_slot (htab, slot); + } + } +} + +/* Set the location of frame_base_decl to LOC in dataflow set SET. This + function expects that + frame_base_decl has already one location for offset 0 in the variable table. + */ + +static void +set_frame_base_location (dataflow_set *set, rtx loc) +{ + variable var; + + var = htab_find_with_hash (set->vars, frame_base_decl, + VARIABLE_HASH_VAL (frame_base_decl)); +#ifdef ENABLE_CHECKING + if (!var) + abort (); + if (var->n_var_parts != 1) + abort (); + if (var->var_part[0].offset != 0) + abort (); + if (!var->var_part[0].loc_chain) + abort (); +#endif + + var->var_part[0].loc_chain->loc = loc; + variable_was_changed (var, set->vars); +} + +/* Set the part of variable's location in the dataflow set SET. The variable + part is specified by variable's declaration DECL and offset OFFSET and the + part's location by LOC. */ + +static void +set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset) +{ + int pos, low, high; + location_chain node, prev, next; + variable var; + void **slot; + + slot = htab_find_slot_with_hash (set->vars, decl, + VARIABLE_HASH_VAL (decl), INSERT); + if (!*slot) + { + /* Create new variable information. */ + var = pool_alloc (var_pool); + var->decl = decl; + var->n_var_parts = 1; + var->var_part[0].offset = offset; + var->var_part[0].loc_chain = NULL; + var->var_part[0].cur_loc = NULL; + *slot = var; + pos = 0; + } + else + { + var = (variable) *slot; + + /* Find the location part. */ + low = 0; + high = var->n_var_parts; + while (low != high) + { + pos = (low + high) / 2; + if (var->var_part[pos].offset < offset) + low = pos + 1; + else + high = pos; + } + pos = low; + + if (pos == var->n_var_parts || var->var_part[pos].offset != offset) + { + /* We have not find the location part, new one will be created. */ + +#ifdef ENABLE_CHECKING + /* We track only variables whose size is <= MAX_VAR_PARTS bytes + thus there are at most MAX_VAR_PARTS different offsets. */ + if (var->n_var_parts >= MAX_VAR_PARTS) + abort (); +#endif + + /* We have to move the elements of array starting at index low to the + next position. */ + for (high = var->n_var_parts; high > low; high--) + var->var_part[high] = var->var_part[high - 1]; + + var->n_var_parts++; + var->var_part[pos].offset = offset; + var->var_part[pos].loc_chain = NULL; + var->var_part[pos].cur_loc = NULL; + } + } + + /* Delete the location from list. */ + prev = NULL; + for (node = var->var_part[pos].loc_chain; node; node = next) + { + next = node->next; + if ((GET_CODE (node->loc) == REG && GET_CODE (loc) == REG + && REGNO (node->loc) == REGNO (loc)) + || rtx_equal_p (node->loc, loc)) + { + if (prev) + prev->next = next; + else + var->var_part[pos].loc_chain = next; + pool_free (loc_chain_pool, node); + break; + } + else + prev = node; + } + + /* Add the location to the beginning. */ + node = pool_alloc (loc_chain_pool); + node->loc = loc; + node->next = var->var_part[pos].loc_chain; + var->var_part[pos].loc_chain = node; + + /* If no location was emitted do so. */ + if (var->var_part[pos].cur_loc == NULL) + { + var->var_part[pos].cur_loc = loc; + variable_was_changed (var, set->vars); + } +} + +/* Delete the part of variable's location from dataflow set SET. The variable + part is specified by variable's declaration DECL and offset OFFSET and the + part's location by LOC. */ + +static void +delete_variable_part (dataflow_set *set, rtx loc, tree decl, + HOST_WIDE_INT offset) +{ + int pos, low, high; + void **slot; + + slot = htab_find_slot_with_hash (set->vars, decl, VARIABLE_HASH_VAL (decl), + NO_INSERT); + if (slot) + { + variable var = (variable) *slot; + + /* Find the location part. */ + low = 0; + high = var->n_var_parts; + while (low != high) + { + pos = (low + high) / 2; + if (var->var_part[pos].offset < offset) + low = pos + 1; + else + high = pos; + } + pos = low; + + if (pos < var->n_var_parts && var->var_part[pos].offset == offset) + { + location_chain node, prev, next; + bool changed; + + /* Delete the location part. */ + prev = NULL; + for (node = var->var_part[pos].loc_chain; node; node = next) + { + next = node->next; + if ((GET_CODE (node->loc) == REG && GET_CODE (loc) == REG + && REGNO (node->loc) == REGNO (loc)) + || rtx_equal_p (node->loc, loc)) + { + if (prev) + prev->next = next; + else + var->var_part[pos].loc_chain = next; + pool_free (loc_chain_pool, node); + break; + } + else + prev = node; + } + + /* If we have deleted the location which was last emitted + we have to emit new location so add the variable to set + of changed variables. */ + if (var->var_part[pos].cur_loc + && ((GET_CODE (loc) == REG + && GET_CODE (var->var_part[pos].cur_loc) == REG + && REGNO (loc) == REGNO (var->var_part[pos].cur_loc)) + || rtx_equal_p (loc, var->var_part[pos].cur_loc))) + { + changed = true; + if (var->var_part[pos].loc_chain) + var->var_part[pos].cur_loc = var->var_part[pos].loc_chain->loc; + } + else + changed = false; + + if (var->var_part[pos].loc_chain == NULL) + { + var->n_var_parts--; + while (pos < var->n_var_parts) + { + var->var_part[pos] = var->var_part[pos + 1]; + pos++; + } + } + if (changed) + variable_was_changed (var, set->vars); + } + } +} + +/* Emit the NOTE_INSN_VAR_LOCATION for variable *VARP. DATA contains + additional parameters: WHERE specifies whether the note shall be emitted + before of after instruction INSN. */ + +static int +emit_note_insn_var_location (void **varp, void *data) +{ + variable var = *(variable *) varp; + rtx insn = ((emit_note_data *)data)->insn; + enum emit_note_where where = ((emit_note_data *)data)->where; + rtx note; + int i; + bool complete; + HOST_WIDE_INT last_limit; + tree type_size_unit; + +#ifdef ENABLE_CHECKING + if (!var->decl) + abort (); +#endif + + complete = true; + last_limit = 0; + for (i = 0; i < var->n_var_parts; i++) + { + if (last_limit < var->var_part[i].offset) + { + complete = false; + break; + } + last_limit + = (var->var_part[i].offset + + GET_MODE_SIZE (GET_MODE (var->var_part[i].loc_chain->loc))); + } + type_size_unit = TYPE_SIZE_UNIT (TREE_TYPE (var->decl)); + if ((unsigned HOST_WIDE_INT) last_limit < TREE_INT_CST_LOW (type_size_unit)) + complete = false; + + if (where == EMIT_NOTE_AFTER_INSN) + note = emit_note_after (NOTE_INSN_VAR_LOCATION, insn); + else + note = emit_note_before (NOTE_INSN_VAR_LOCATION, insn); + + if (!complete) + { + NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, var->decl, + NULL_RTX); + } + else if (var->n_var_parts == 1) + { + rtx expr_list + = gen_rtx_EXPR_LIST (VOIDmode, + var->var_part[0].loc_chain->loc, + GEN_INT (var->var_part[0].offset)); + + NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, var->decl, + expr_list); + } + else if (var->n_var_parts) + { + rtx argp[MAX_VAR_PARTS]; + rtx parallel; + + for (i = 0; i < var->n_var_parts; i++) + argp[i] = gen_rtx_EXPR_LIST (VOIDmode, var->var_part[i].loc_chain->loc, + GEN_INT (var->var_part[i].offset)); + parallel = gen_rtx_PARALLEL (VOIDmode, + gen_rtvec_v (var->n_var_parts, argp)); + NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, var->decl, + parallel); + } + + htab_clear_slot (changed_variables, varp); + + /* When there are no location parts the variable has been already + removed from hash table and a new empty variable was created. + Free the empty variable. */ + if (var->n_var_parts == 0) + { + pool_free (var_pool, var); + } + + /* Continue traversing the hash table. */ + return 1; +} + +/* Emit NOTE_INSN_VAR_LOCATION note for each variable from a chain + CHANGED_VARIABLES and delete this chain. WHERE specifies whether the notes + shall be emitted before of after instruction INSN. */ + +static void +emit_notes_for_changes (rtx insn, enum emit_note_where where) +{ + emit_note_data data; + + data.insn = insn; + data.where = where; + htab_traverse (changed_variables, emit_note_insn_var_location, &data); +} + +/* Add variable *SLOT to the chain CHANGED_VARIABLES if it differs from the + same variable in hash table DATA or is not there at all. */ + +static int +emit_notes_for_differences_1 (void **slot, void *data) +{ + htab_t new_vars = (htab_t) data; + variable old_var, new_var; + + old_var = *(variable *) slot; + new_var = (variable) htab_find_with_hash (new_vars, old_var->decl, + VARIABLE_HASH_VAL (old_var->decl)); + + if (!new_var) + { + /* Variable has disappeared. */ + variable empty_var; + + empty_var = pool_alloc (var_pool); + empty_var->decl = old_var->decl; + empty_var->n_var_parts = 0; + variable_was_changed (empty_var, NULL); + } + else if (variable_different_p (old_var, new_var)) + { + variable_was_changed (new_var, NULL); + } + + /* Continue traversing the hash table. */ + return 1; +} + +/* Add variable *SLOT to the chain CHANGED_VARIABLES if it is not in hash + table DATA. */ + +static int +emit_notes_for_differences_2 (void **slot, void *data) +{ + htab_t old_vars = (htab_t) data; + variable old_var, new_var; + + new_var = *(variable *) slot; + old_var = (variable) htab_find_with_hash (old_vars, new_var->decl, + VARIABLE_HASH_VAL (new_var->decl)); + if (!old_var) + { + /* Variable has appeared. */ + variable_was_changed (new_var, NULL); + } + + /* Continue traversing the hash table. */ + return 1; +} + +/* Emit notes before INSN for differences between dataflow sets OLD_SET and + NEW_SET. */ + +static void +emit_notes_for_differences (rtx insn, dataflow_set *old_set, + dataflow_set *new_set) +{ + htab_traverse (old_set->vars, emit_notes_for_differences_1, new_set->vars); + htab_traverse (new_set->vars, emit_notes_for_differences_2, old_set->vars); + emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN); +} + +/* Emit the notes for changes of location parts in the basic block BB. */ + +static void +emit_notes_in_bb (basic_block bb) +{ + int i; + dataflow_set set; + + dataflow_set_init (&set, htab_elements (VTI (bb)->in.vars) + 3); + dataflow_set_copy (&set, &VTI (bb)->in); + + for (i = 0; i < VTI (bb)->n_mos; i++) + { + rtx insn = VTI (bb)->mos[i].insn; + + switch (VTI (bb)->mos[i].type) + { + case MO_CALL: + { + int r; + + for (r = 0; r < FIRST_PSEUDO_REGISTER; r++) + if (TEST_HARD_REG_BIT (call_used_reg_set, r)) + { + var_regno_delete (&set, r); + } + emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN); + } + break; + + case MO_USE: + case MO_SET: + { + rtx loc = VTI (bb)->mos[i].u.loc; + + if (GET_CODE (loc) == REG) + var_reg_delete_and_set (&set, loc); + else + var_mem_delete_and_set (&set, loc); + + if (VTI (bb)->mos[i].type == MO_USE) + emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN); + else + emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN); + } + break; + + case MO_USE_NO_VAR: + case MO_CLOBBER: + { + rtx loc = VTI (bb)->mos[i].u.loc; + + if (GET_CODE (loc) == REG) + var_reg_delete (&set, loc); + else + var_mem_delete (&set, loc); + + if (VTI (bb)->mos[i].type == MO_USE_NO_VAR) + emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN); + else + emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN); + } + break; + + case MO_ADJUST: + { + rtx base; + + set.stack_adjust += VTI (bb)->mos[i].u.adjust; + base = gen_rtx_MEM (Pmode, + gen_rtx_PLUS (Pmode, stack_pointer_rtx, + GEN_INT (set.stack_adjust))); + set_frame_base_location (&set, base); + emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN); + } + break; + } + } + dataflow_set_destroy (&set); +} + +/* Emit notes for the whole function. */ + +static void +vt_emit_notes (void) +{ + basic_block bb; + dataflow_set *last_out; + dataflow_set empty; + +#ifdef ENABLE_CHECKING + if (htab_elements (changed_variables)) + abort (); +#endif + + /* Enable emitting notes by functions (mainly by set_variable_part and + delete_variable_part). */ + emit_notes = true; + + dataflow_set_init (&empty, 7); + last_out = ∅ + + FOR_EACH_BB (bb) + { + /* Emit the notes for changes of variable locations between two + subsequent basic blocks. */ + emit_notes_for_differences (BB_HEAD (bb), last_out, &VTI (bb)->in); + + /* Emit the notes for the changes in the basic block itself. */ + emit_notes_in_bb (bb); + + last_out = &VTI (bb)->out; + } + dataflow_set_destroy (&empty); + emit_notes = false; +} + +/* If there is a declaration and offset associated with register/memory RTL + assign declaration to *DECLP and offset to *OFFSETP, and return true. */ + +static bool +vt_get_decl_and_offset (rtx rtl, tree *declp, HOST_WIDE_INT *offsetp) +{ + if (GET_CODE (rtl) == REG) + { + if (REG_ATTRS (rtl)) + { + *declp = REG_EXPR (rtl); + *offsetp = REG_OFFSET (rtl); + return true; + } + } + else if (GET_CODE (rtl) == MEM) + { + if (MEM_ATTRS (rtl)) + { + *declp = MEM_EXPR (rtl); + *offsetp = MEM_OFFSET (rtl) ? INTVAL (MEM_OFFSET (rtl)) : 0; + return true; + } + } + return false; +} + +/* Insert function parameters to IN and OUT sets of ENTRY_BLOCK. */ + +static void +vt_add_function_parameters (void) +{ + tree parm; + HOST_WIDE_INT stack_adjust = 0; + + if (!frame_pointer_needed) + stack_adjust = prologue_stack_adjust (); + + for (parm = DECL_ARGUMENTS (current_function_decl); + parm; parm = TREE_CHAIN (parm)) + { + rtx decl_rtl = DECL_RTL_IF_SET (parm); + rtx incoming = DECL_INCOMING_RTL (parm); + tree decl; + HOST_WIDE_INT offset; + dataflow_set *in, *out; + + if (TREE_CODE (parm) != PARM_DECL) + continue; + + if (!DECL_NAME (parm)) + continue; + + if (!decl_rtl || !incoming) + continue; + + if (GET_MODE (decl_rtl) == BLKmode || GET_MODE (incoming) == BLKmode) + continue; + + if (!vt_get_decl_and_offset (incoming, &decl, &offset)) + if (!vt_get_decl_and_offset (decl_rtl, &decl, &offset)) + continue; + + if (!decl) + continue; + + if (parm != decl) + abort (); + + incoming = eliminate_regs (incoming, 0, NULL_RTX); + if (!frame_pointer_needed && GET_CODE (incoming) == MEM) + incoming = adjust_stack_reference (incoming, -stack_adjust); + in = &VTI (ENTRY_BLOCK_PTR)->in; + out = &VTI (ENTRY_BLOCK_PTR)->out; + + if (GET_CODE (incoming) == REG) + { + if (REGNO (incoming) >= FIRST_PSEUDO_REGISTER) + abort (); + attrs_list_insert (&in->regs[REGNO (incoming)], + parm, offset, incoming); + attrs_list_insert (&out->regs[REGNO (incoming)], + parm, offset, incoming); + set_variable_part (in, incoming, parm, offset); + set_variable_part (out, incoming, parm, offset); + } + else if (GET_CODE (incoming) == MEM) + { + set_variable_part (in, incoming, parm, offset); + set_variable_part (out, incoming, parm, offset); + } + } +} + +/* Allocate and initialize the data structures for variable tracking + and parse the RTL to get the micro operations. */ + +static void +vt_initialize (void) +{ + basic_block bb; + + alloc_aux_for_blocks (sizeof (struct variable_tracking_info_def)); + + FOR_EACH_BB (bb) + { + rtx insn; + HOST_WIDE_INT pre, post; + + /* Count the number of micro operations. */ + VTI (bb)->n_mos = 0; + for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb)); + insn = NEXT_INSN (insn)) + { + if (INSN_P (insn)) + { + if (!frame_pointer_needed) + { + insn_stack_adjust_offset_pre_post (insn, &pre, &post); + if (pre) + VTI (bb)->n_mos++; + if (post) + VTI (bb)->n_mos++; + } + note_uses (&PATTERN (insn), count_uses_1, insn); + note_stores (PATTERN (insn), count_stores, insn); + if (GET_CODE (insn) == CALL_INSN) + VTI (bb)->n_mos++; + } + } + + /* Add the nicro-operations to the array. */ + VTI (bb)->mos = xmalloc (VTI (bb)->n_mos + * sizeof (struct micro_operation_def)); + VTI (bb)->n_mos = 0; + for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb)); + insn = NEXT_INSN (insn)) + { + if (INSN_P (insn)) + { + int n1, n2; + + if (!frame_pointer_needed) + { + insn_stack_adjust_offset_pre_post (insn, &pre, &post); + if (pre) + { + micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++; + + mo->type = MO_ADJUST; + mo->u.adjust = pre; + mo->insn = insn; + } + } + + n1 = VTI (bb)->n_mos; + note_uses (&PATTERN (insn), add_uses_1, insn); + n2 = VTI (bb)->n_mos - 1; + + /* Order the MO_USEs to be before MO_USE_NO_VARs. */ + while (n1 < n2) + { + while (n1 < n2 && VTI (bb)->mos[n1].type == MO_USE) + n1++; + while (n1 < n2 && VTI (bb)->mos[n2].type == MO_USE_NO_VAR) + n2--; + if (n1 < n2) + { + micro_operation sw; + + sw = VTI (bb)->mos[n1]; + VTI (bb)->mos[n1] = VTI (bb)->mos[n2]; + VTI (bb)->mos[n2] = sw; + } + } + + if (GET_CODE (insn) == CALL_INSN) + { + micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++; + + mo->type = MO_CALL; + mo->insn = insn; + } + + n1 = VTI (bb)->n_mos; + note_stores (PATTERN (insn), add_stores, insn); + n2 = VTI (bb)->n_mos - 1; + + /* Order the MO_SETs to be before MO_CLOBBERs. */ + while (n1 < n2) + { + while (n1 < n2 && VTI (bb)->mos[n1].type == MO_SET) + n1++; + while (n1 < n2 && VTI (bb)->mos[n2].type == MO_CLOBBER) + n2--; + if (n1 < n2) + { + micro_operation sw; + + sw = VTI (bb)->mos[n1]; + VTI (bb)->mos[n1] = VTI (bb)->mos[n2]; + VTI (bb)->mos[n2] = sw; + } + } + + if (!frame_pointer_needed && post) + { + micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++; + + mo->type = MO_ADJUST; + mo->u.adjust = post; + mo->insn = insn; + } + } + } + } + + /* Init the IN and OUT sets. */ + FOR_ALL_BB (bb) + { + VTI (bb)->visited = false; + dataflow_set_init (&VTI (bb)->in, 7); + dataflow_set_init (&VTI (bb)->out, 7); + } + + attrs_pool = create_alloc_pool ("attrs_def pool", + sizeof (struct attrs_def), 1024); + var_pool = create_alloc_pool ("variable_def pool", + sizeof (struct variable_def), 64); + loc_chain_pool = create_alloc_pool ("location_chain_def pool", + sizeof (struct location_chain_def), + 1024); + changed_variables = htab_create (10, variable_htab_hash, variable_htab_eq, + NULL); + vt_add_function_parameters (); + + if (!frame_pointer_needed) + { + rtx base; + + /* Create fake variable for tracking stack pointer changes. */ + frame_base_decl = make_node (VAR_DECL); + DECL_NAME (frame_base_decl) = get_identifier ("___frame_base_decl"); + TREE_TYPE (frame_base_decl) = char_type_node; + DECL_ARTIFICIAL (frame_base_decl) = 1; + + /* Set its initial "location". */ + base = gen_rtx_MEM (Pmode, stack_pointer_rtx); + set_variable_part (&VTI (ENTRY_BLOCK_PTR)->in, base, frame_base_decl, 0); + set_variable_part (&VTI (ENTRY_BLOCK_PTR)->out, base, frame_base_decl, 0); + } + else + { + frame_base_decl = NULL; + } +} + +/* Free the data structures needed for variable tracking. */ + +static void +vt_finalize (void) +{ + basic_block bb; + + FOR_EACH_BB (bb) + { + free (VTI (bb)->mos); + } + + FOR_ALL_BB (bb) + { + dataflow_set_destroy (&VTI (bb)->in); + dataflow_set_destroy (&VTI (bb)->out); + } + free_aux_for_blocks (); + free_alloc_pool (attrs_pool); + free_alloc_pool (var_pool); + free_alloc_pool (loc_chain_pool); + htab_delete (changed_variables); +} + +/* The entry point to variable tracking pass. */ + +void +variable_tracking_main (void) +{ + if (n_basic_blocks > 500 && n_edges / n_basic_blocks >= 20) + return; + + mark_dfs_back_edges (); + vt_initialize (); + if (!frame_pointer_needed) + { + if (!vt_stack_adjustments ()) + { + vt_finalize (); + return; + } + } + + vt_find_locations (); + vt_emit_notes (); + + if (rtl_dump_file) + { + dump_dataflow_sets (); + dump_flow_info (rtl_dump_file); + } + + vt_finalize (); +} diff --git a/gcc/vmsdbgout.c b/gcc/vmsdbgout.c index 2a963d99bc6..5c5634de344 100644 --- a/gcc/vmsdbgout.c +++ b/gcc/vmsdbgout.c @@ -190,7 +190,8 @@ const struct gcc_debug_hooks vmsdbg_debug_hooks debug_nothing_tree, /* deferred_inline_function */ vmsdbgout_abstract_function, debug_nothing_rtx, /* label */ - debug_nothing_int /* handle_pch */ + debug_nothing_int, /* handle_pch */ + debug_nothing_rtx /* var_location */ }; /* Definitions of defaults for assembler-dependent names of various |