diff options
author | Daniel Berlin <dberlin@gcc.gnu.org> | 2007-06-11 18:02:15 +0000 |
---|---|---|
committer | Daniel Berlin <dberlin@gcc.gnu.org> | 2007-06-11 18:02:15 +0000 |
commit | 6fb5fa3cbc0d78cf9f4ff7cac5e1d8af1e65c5bb (patch) | |
tree | fdb9e9f8a0700a2713dc690fed1a2cf20dae8392 | |
parent | a91d32a4a65d54cccf639a1541417e39cb7b7deb (diff) | |
download | gcc-6fb5fa3cbc0d78cf9f4ff7cac5e1d8af1e65c5bb.zip gcc-6fb5fa3cbc0d78cf9f4ff7cac5e1d8af1e65c5bb.tar.gz gcc-6fb5fa3cbc0d78cf9f4ff7cac5e1d8af1e65c5bb.tar.bz2 |
Merge dataflow branch into mainline
From-SVN: r125624
168 files changed, 24101 insertions, 13108 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index b68b4a3..873677d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -195,6 +195,7 @@ build machinery (*.in) DJ Delorie dj@redhat.com build machinery (*.in) Nathanael Nerode neroden@gcc.gnu.org build machinery (*.in) Alexandre Oliva aoliva@redhat.com dataflow Paolo Bonzini bonzini@gnu.org +dataflow Daniel Berlin dberlin@dberlin.org docs co-maintainer Gerald Pfeifer gerald@pfeifer.com docs co-maintainer Joseph Myers jsm@polyomino.org.uk predict.def Jan Hubicka jh@suse.cz diff --git a/gcc/ChangeLog b/gcc/ChangeLog index e821198..2b2134c 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,7 @@ +2007-06-11 Daniel Berlin <dberlin@dberlin.org> + + * Merge dataflow-branch into mainline (see ChangeLog.dataflow) + 2007-06-11 Uros Bizjak <ubizjak@gmail.com> * config/i386/i386.md ("*movtf_internal): Penalize moves to and diff --git a/gcc/ChangeLog.dataflow b/gcc/ChangeLog.dataflow new file mode 100644 index 0000000..936caa0 --- /dev/null +++ b/gcc/ChangeLog.dataflow @@ -0,0 +1,4481 @@ +2007-06-07 Kenneth Zadeck <zadeck@naturalbridge.com> + + * emit-rtl.c (set_insn_deleted): Changed NOTE_LINE_NUMBER to + NOTE_KIND. + (emit_note_after): Fixed merge glitch. + * combine.c (try_combine): Changed NOTE_LINE_NUMBER to + NOTE_KIND. + * reg-stack.c (convert_regs_1): Fixed merge glitch. + * config/rs6000/rs6000.c (no_global_regs_above): Fixed merge typo. + (rs6000_emit_prologue): Fixed merge glitch. + +2007-06-06 Kenneth Zadeck <zadeck@naturalbridge.com> + + * regrename.c (do_replace): Removed ifdefed out code. + * fwprop.c: Fixed copyright. + * testsuite/Changelog.dataflow: Merged entry into + testsuite/Changelog. Deleted file. + * global.c: Fixed comments. + * recog.c (confirm_change_group): Fixed comments. + (peephole2_optimize): Removed ifdefed out code. + * gcse.c (try_replace_reg): Removed redundant changing and + rescanning of notes. + * rtl.h (SET_REGNO): Replaced brackets. + * stack-ptr-mod.c: Changed copyright years. + +2007-06-06 Seongbae Park <seongbae.park@gmail.com> + + * config/ia64/ia64.c (ia64_frame_info): Restored the comment + for the structure above. Removed left-over dead code from debugging. + (ia64_compute_frame_size): Comment cleanup. + (ia64_expand_prologue): Style fix. Debugging information header. + (ia64_init_expanders): Added a comment. + * config/ia64/ia64.h (ia64_init_expanders): Declaration moved + to ia64-protos.h + * config/ia64/ia64-protos.h (ia64_init_expanders): Declaration + moved from ia64.h + +2007-06-06 Kenneth Zadeck <zadeck@naturalbridge.com> + + * doc/rtl.text: Fixed spelling. + * dse.c (store_info, read_info, insn_info, bb_info, group_info, + deferred_change): Fixed formatting. + (step0): Renamed to dse_step0. + (step1): Renamed to dse_step1. + (step2_init): Renamed to dse_step2_init. + (step2_nospill): Renamed to dse_step2_nospill. + (step2_spill): Renamed to dse_step2_spill. + (step3_scan): Renamed to dse_step3_scan. + (step3_exit_block_scan): Renamed to dse_step3_exit_block_scan. + (step3): Renamed to dse_step3. + (step4_nospill): Renamed to dse_step4_nospill. + (step4_spill): Renamed to dse_step4_spill. + (step4): Renamed to dse_step4. + (step5_nospill): Renamed to dse_step5_nospill. + (step5_spill): Renamed to dse_step5_spill. + (step6): Renamed to dse_step6. + (rest_of_handle_dse): Updated names of functions. + * emit_rtl.c (verify_rtx_sharing, copy_rtx_if_shared_1, + copy_insn_1): Now calls shared_const_p. + * cselib.c (expand_loc, cselib_expand_value_rtx): Fixed comments. + (cselib_expand_value_rtx): Now calls shared_const_p. + * rtl.c (shared_const_p): New function. + (copy_rtx): Now calls shared_const_p. + * rtl.h (shared_const_p): New function. + +2007-06-06 Kenneth Zadeck <zadeck@naturalbridge.com> + + * combine.c (find_single_use_1): Fixed comment and changed 0 to NULL. + (find_single_use): Removed comment. + (delete_noop_move): Removed unnecessary continue and removed + nnoops counter. Function now does not return anything. + +2007-05-31 Kenneth Zadeck <zadeck@naturalbridge.com> + + * bitmap.c (bitmap_elt_copy): Fixed comment. + * cfganal.c (inverted_post_order_compute): Fixed comment and + formatting of test. + * basic_block.h: Removed include rtl.h. + (enum bb_flags): Renumbered. + (FOR_EACH_BB_IN_REGION, FOR_EACH_BB_REVERSE_IN_REGION): Removed. + (FOR_BB_INSNS_SAFE, FOR_BB_INSNS_REVERSE_SAFE): Fixed formatting. + +2007-05-30 Kenneth Zadeck <zadeck@naturalbridge.com> + + * auto-inc-dec.c: Updated copyright date. + (enum form, set_inc_state, dump_inc_insn, move_dead_notes, + insert_move_insn_before, attempt_change, try_merge, find_address, + find_mem): Reformatted. + (reverse_inc, find_address): Fixed spelling. + (attempt_change, try_merge): Add default case. + * basic-block.h: Updated copyright date. + * bitmap.c: Updated copyright date. + * bitmap.h: Updated copyright date. + * cfganal.c: Updated copyright date. + * cfg.c: Updated copyright date. + * cfghooks.h: Updated copyright date. + * cfglayout.c: Updated copyright date. + * cfgloop.c: Updated copyright date. + * cfgloop.h: Updated copyright date. + * cfgrtl.c: Updated copyright date. + * combine.c: Updated copyright date. + * combine-stack-adj.c: Updated copyright date. + * config/arc/arc.c: Updated copyright date. + * config/arm/arm.c: (use_return_insn, + arm_compute_save_reg0_reg12_mask, arm_get_frame_offsets, + arm_save_coproc_regs): Fixed formatting. + * config/bfin/bfin.c: Updated copyright date. + * config/c4x/c4x.c: Updated copyright date. + * config/c4x/c4x.h: Updated copyright date. + * config/cris/cris.c: Updated copyright date. + * config/crx/crx.c: Updated copyright date. + * config/crx/crx.h: Updated copyright date. + * config/darwin.c: Updated copyright date. + * config/frv/frv.c: Updated copyright date. + * config/h8300/h8300.c: Updated copyright date. + * config/h8300/h8300.md: Updated copyright date. + * config/ia64/ia64.h: Updated copyright date. + * config/iq2000/iq2000.c: Updated copyright date. + * config/iq2000/iq2000.h: Updated copyright date. + * config/m32c/m32c.c: Updated copyright date. + * config/m68hc11/m68hc11.c: Updated copyright date. + * config/m68k/m68k.c: Updated copyright date. + * config/mips/mips.c: Updated copyright date. + * config/mips/mips.md: Updated copyright date. + * config/mmix/mmix.c: Updated copyright date. + * config/mn10300/mn10300.c: Updated copyright date. + * config/mt/mt.c: Updated copyright date. + (mt_print_operand_simple_address, mt_print_operand): Fixed formatting. + * config/mt/mt.h: Updated copyright date and fixed formatting. + * config/pa/pa.c: Updated copyright date. + * config/pa/pa.h: Updated copyright date. + * config/pdp11/pdp11.c: Updated copyright date. + * config/pdp11/pdp11.h: Updated copyright date. + * config/rs6000/predicates.md: Updated copyright date. + * config/s390/s390.c: Updated copyright date. + * config/score/score-mdaux.c: Updated copyright date. + * config/sh/sh.c: Updated copyright date. + * config/sh/sh.md: Updated copyright date. + * config/sparc/sparc.c: Updated copyright date. + * config/stormy16/stormy16.c: Updated copyright date. + * config/v850/v850.c: Updated copyright date. + * config/vax/vax.c: Updated copyright date. + * cselib.c: Updated copyright date. + (expand_loc): Fixed formatting. + * cselib.h: Updated copyright date. + * dbgcnt.c: Updated copyright date. + * dbgcnt.def: Updated copyright date. + * dbgcnt.h: Updated copyright date. + * dce.c: Updated copyright date. + (fast_dce): Fixed formatting. + * dce.h: Updated copyright date. + * ddg.c: Updated copyright date. + * ddg.h: Updated copyright date. + * df-core.c: Updated copyright date. + * df.h: Updated copyright date and fixed formatting. + * doc/cfg.texi: Updated copyright date. + * doc/rtl.texi: Updated copyright date. + * dominance.c: Updated copyright date. + * function.h: Updated copyright date. + * fwprop.c: Updated copyright date. + * global.c: Updated copyright date. + * integrate.c: Updated copyright date. + * local-alloc.c: Updated copyright date. + * loop-init.c: Updated copyright date. + * loop-invariant.c: Updated copyright date. + * loop-iv.c: Updated copyright date. + * optabs.h: Updated copyright date. + * output.h: Updated copyright date. + * postreload.c: Updated copyright date. + * postreload-gcse.c: Updated copyright date. + * recog.h: Updated copyright date. + * regmove.c: Updated copyright date. + * reg-notes.def: Updated copyright date. + * regrename.c: Updated copyright date. + * reg-stack.c: Updated copyright date. + * reload.c: Updated copyright date. + * reorg.c: Updated copyright date. + * resource.c: Updated copyright date. + * resource.h: Updated copyright date. + * rtl-factoring.c: Updated copyright date. + * sbitmap.c: Updated copyright date. + * sbitmap.h: Updated copyright date. + * sched-deps.c: Updated copyright date. + * sched-ebb.c: Updated copyright date. + * sched-int.h: Updated copyright date. + * sched-rgn.c: Updated copyright date. + * sched-vis.c: Updated copyright date. + * see.c: Updated copyright date. + (see_handle_relevant_uses): Fixed formatting. + * stack-ptr-mod.c: Updated copyright date. + * struct-equiv.c: Updated copyright date. + * tracer.c: Updated copyright date. + * web.c: Updated copyright date. + +2007-05-30 Kenneth Zadeck <zadeck@naturalbridge.com> + + * auto-inc-dec.c (mem_insn.mem_pat): Renamed mem_loc. + (attempt_change): Fixed comments and renamed new_addr_pat to new_addr. + (try_merge, attempt_change, find_address, find_mem): Renamed + mem_pat to mem_loc. + +2007-05-27 Kenneth Zadeck <zadeck@naturalbridge.com> + + * df-core.c (df_hybrid_search_forward, df_hybrid_search_backward + df_iterative_dataflow): Removed. + * df.h (df_iterative_dataflow): Removed. + +2007-05-27 Kenneth Zadeck <zadeck@naturalbridge.com> + + * dse.c (replace_inc_dec, delete_dead_store_insn, scan_insn, + dse_record_singleton_alias_set, dse_confluence_0, + dse_confluence_n, step4, step5_nospill, step5_spill, + rest_of_handle_dse, pass_rtl_dse1, pass_rtl_dse2): Removed code + to allow dse to run on trunk in front of flow.c + (problem): Removed. + +2007-05-26 Kaz Kojima <kkojima@gcc.gnu.org> + + * bt-load.c: Include recog.h. + (move_btr_def): Use validate_replace_rtx instead of replace_rtx. + +2007-05-25 Steven Bosscher <steven@gcc.gnu.org> + + * emit-rtl.c (try_split): Remove unnecessary setting/resetting + of the USED flags of TRIAL. + +2007-05-25 Kenneth Zadeck <zadeck@naturalbridge.com> + + * dse.c (insn_info.stack_read, group_info.frame_related): New + variable. + (get_group_info): Initialize frame_related. + (scan_insn, scan_reads_nospill): Remove frame_related stores from + consideration for const functions. + (step1, step2_init, step3_exit_block_scan): Use frame_related field. + (step3_scan, step5_nospill): Change parameters to scan_reads_nospill. + +2007-05-25 Ian Lance Taylor <iant@google.com> + + * reload1.c (mark_home_live_1): New static function, broken out of + mark_home_live. + (mark_home_live): Call mark_home_live_1. + (scan_paradoxical_subregs): Call mark_home_live_1. + +2007-05-24 Andrew Pinski <andrew_pinski@playstation.sony.com> + + * config/spu/spu.md (smulsi3_highpart): Unshare the rtl chain. + (umulsi3_highpart): Likewise. + +2007-05-24 Eric Christopher <echristo@gmail.com> + + * config/mips/mips.c (mips_save_reg_p): Use df_regs_ever_live_p. + (mips_output_mi_thunk): Use SET_REGNO. + +2007-05-23 Kaz Kojima <kkojima@gcc.gnu.org> + + * config/sh/sh.c (sh_expand_prologue): Fix typo. + +2007-05-23 Paolo Bonzini <bonzini@gnu.org> + + * emit-rtl.c [!HAVE_blockage]: Provide a gen_blockage routine here. + * rtl.h (gen_blockage): Add prototype. + + * cse.c (last_bb_reg_used_in, reg_used_in_multiple_bb): Remove. + (mark_reg_use_bb): Remove. + (cse_main): Remove the initialization of reg_used_in_multiple_bb + and last_bb_reg_used_in, and the insn walk that calls mark_reg_use_bb. + (cse_ebb_live_in, cse_ebb_live_out): New. + (cse_extended_basic_block): Set them. + (make_regs_eqv): Use them. + +2007-05-23 Kenneth Zadeck <zadeck@naturalbridge.com> + + * df-scan.c (df_get_regular_block_artificial_uses): Set the + HARD_FRAME_POINTER not the FRAME_POINTER after reload. Set both + the HARD_FRAME_POINTER and the FRAME_POINTER before reload. + * dse.c (const_or_frame_p): Removed unnecessary test. + * config/s390/s390.c (s390_emit_prologue): Removed REG_MAYBE_DEAD + notes. + * config/spu/spu.c (frame_emit_add_imm, spu_expand_prologue): Ditto. + * config/i386/i386.c (ix86_expand_prologue): Ditto. + * config/sh/sh.c (sh_expand_prologue): Ditto. + * config/sh/sh.md (define_expand): Ditto. + * config/iq2000/iq2000.c (iq2000_expand_prologue): Ditto. + * config/mn10300/mn10300.c (expand_prologue): Ditto. + * config/ia64/ia64.c (spill_restore_mem, ia64_expand_prologue): Ditto. + * config/m68k/m68k.c (m68k_expand_prologue): Ditto. + * config/rs6000/rs6000.c (rs6000_maybe_dead): Removed. + (rs6000_emit_load_toc_table, rs6000_emit_prologue): Removed calls + to rs6000_maybe_dead. + * config/bfin/bfin.c (expand_interrupt_handler_prologue, + bfin_load_pic_reg): Removed REG_MAYBE_DEAD notes. + * reg-notes.def (MAYBE_DEAD): Removed. + * dce.c (deletable_insn_p): Removed test of RTX_FRAME_RELATED_P. + +2007-05-21 Kenneth Zadeck <zadeck@naturalbridge.com> + + * dbgcnt.def: Fixed comment. + * df-scan.c (df_get_regular_block_artificial_uses): Added frame + pointer after reload if frame_pointer_needed. + * df.h (df_simulate_defs, df_simulate_uses): Made public. + * df-problems.c (df_simulate_defs, df_simulate_uses): Made public. + * dce.c (deletable_insn_p): Only allow frame-related insns to be + deleted if there is a REG_MAYBE_DEAD note. + (dce_process_block): Now uses df_simulate_defs and + df_simulate_uses. + +2007-05-20 Kenneth Zadeck <zadeck@naturalbridge.com> + + * cfg.c (dump_bb_info): Fixed dump formatting problem. + * dse.c (const_or_frame_p): New function. + (canon_address): Fixed dump info and now callse const_or_frame_p + rather than rtx_varies_p. Also do simplification inside cselib. + (record_store): Fixed dump info. + * cselib.c (expand_loc): Fixed dump info. + (cselib_expand_value_rtx): Fixed dump info and now call + simplify_rtx on results of expansion. + +2007-05-17 Kenneth Zadeck <zadeck@naturalbridge.com> + + * ifcvt.c (dead_or_predictable): Replaced insn dfa with new df + routines. + * recog.c (peephole2_optimize): Replaced + df_lr_simulate_artificial_refs_at_end and df_lr_simulate_one_insn + with df_simulate_artificial_refs_at_end and + df_simulate_one_insn_backwards. + * rtl-factoring.c (collect_pattern_seqs, clear_regs_live_in_seq): + Ditto. + * df.h (df_lr_simulate_artificial_refs_at_end, + df_lr_simulate_one_insn): Removed. + (df_simulate_find_defs, df_simulate_artificial_refs_at_top, + df_simulate_one_insn_forwards, df_simulate_artificial_refs_at_end, + df_simulate_one_insn_backwards): Added. + * df-problems.c (df_lr_bb_local_compute): Removed unnecessary + tests. + (df_lr_simulate_artificial_refs_at_end, df_lr_simulate_one_insn): + Removed. + (df_simulate_find_defs, df_simulate_defs, df_simulate_uses, + df_simulate_fixup_sets, df_simulate_artificial_refs_at_top, + df_simulate_one_insn_forwards, df_simulate_artificial_refs_at_end, + df_simulate_one_insn_backwards): Added. + +2007-05-16 Seongbae Park <seongbae.park@gmail.com> + + * ifcvt.c (dead_or_predicable): Update test_live as well as test_set. + (if_convert): New parameter RECOMPUTE_DOMINANCE. + (rest_of_handle_if_conversion, rest_of_handle_if_after_combine, + rest_of_handle_if_after_reload): New parameter to if_convert. + +2007-05-16 Kenneth Zadeck <zadeck@naturalbridge.com> + + * regstat.c (regstat_init_n_sets_and_refs, regstat_compute_ri, + regstat_compute_calls_crossed): Added time variable TV_REG_STATS. + * timevar.def (TV_REG_STATS): Added. + (TV_DF_RI): Deleted. + * df-problems.c (df_print_bb_index): Added identifer for eh blocks. + * dce.c (deletable_insn_p): Added code to not delete + RTX_FRAME_RELATED_P insns. + (dce_process_block): Removed insns_deleted and added code to reset + live set before confluence function. + (rest_of_handle_fast_dce): Added code to turn off df's version of dce if + flag_dce is not set and added DF_NO_INSN_RESCAN to flags that are cleared. + +2007-05-10 Roman Zippel <zippel@linux-m68k.org> + + * config/m68k/m68k.c: include df.h. + (m68k_output_mi_thunk): use SET_REGNO. + +2007-05-09 Kaz Kojima <kkojima@gcc.gnu.org> + + * df-core.c (df_get_bb_dirty): Return false if df_live is null. + +2007-05-09 Kaz Kojima <kkojima@gcc.gnu.org> + + * config/sh/sh.c (r0_life_regions): New variable. + (find_r0_life_regions): New static function. + (sh_md_init_global): Call find_r0_life_regions when + reload_completed isn't set. + (R0_MAX_LIVE_LENGTH): Remove. + (high__pressure): Return 1 if r0_life_regions is over + the given threshold. + +2007-05-08 Kenneth Zadeck <zadeck@naturalbridge.com> + + * regrename.c (regrename_optimize): Renamed df_ri_add_problem to + df_note_add_problem. + * auto-inc-dec.c (rest_of_handle_auto_inc_dec): Ditto. + * sched_ebb.c (schedule_ebbs): Renamed df_ri_add_problem to + df_note_add_problem. Added call to regstat_compute_calls_crossed + and regstat_free_calls_crossed and deleted call to + allocate_reg_life_data. + * regstat.c (regstat_init_n_sets_and_refs, + regstat_free_n_sets_and_refs, regstat_bb_compute_ri, + regstat_compute_ri, regstat_free_ri, + regstat_bb_compute_calls_crossed, regstat_compute_calls_crossed, + regstat_free_calls_crossed): New functions. + * final.c (rest_of_handle_final): Removed call to free_reg_info. + * cfg.c (dump_reg_info): Many changes to accomodate new + implementation of REG_BASIC_BLOCK, REG_N_SETS, REG_N_DEATHS, + REG_N_CALLS_CROSSED. + * toplev.c (finalize): Removed call to free_reg_info. + * regs.h (REG_BASIC_BLOCK, REG_N_SETS, REG_N_REFS, REG_N_DEATHS, + REG_N_CALLS_CROSSED, REG_FREQ, REG_N_THROWING_CALLS_CROSSED, + REG_LIVE_LENGTH, REG_BASIC_BLOCK): Changed implementation. + (allocate_reg_life_data, allocate_reg_info, clear_reg_info_regno): + Removed. + * mode-switching.c (optimize_mode_switching): Removed call to + df_ri_add_problem. + * modulo-sched.c (sms_schedule): Renamed df_ri_add_problem to + df_note_add_problem. Added call to regstat_compute_calls_crossed + and regstat_free_calls_crossed + (rest_of_handle_sms): Deleted call to allocate_reg_info. + * global.c (compute_regsets): Moved all dataflow to local_alloc. + (rest_of_handle_global_alloc): Call regstat_free_n_sets_and_refs + and regstat_free_ri. + * ifcvt.c (dead_or_predicable, if_convert): Removed calls to + allocate_reg_info. + * timevar.def (TV_DF_NOTE): New timevar. + * regmove.c (copy_src_to_dest, regmove_optimize, fixup_match_1): + Changed calls to REG_N_SETS that changed the value to + INC_REG_N_SETS. + (regmove_optimize): Moved calls that compute df and register info + to after early out for flag_non_call_exceptions. Added calls to + regstat_init_n_sets_and_refs, regstat_compute_ri, + regstat_free_n_sets_and_refs and regstat_free_ri. + * local_alloc (local_alloc, equiv_init_movable_p): Added + NUM_FIXED_BLOCKS to tests for REG_BASIC_BLOCK. + (update_equiv_regs): Added code to update bitvectors when certain + local allocations are done. Changed REG_N_SETS to + DF_REG_DEF_COUNT. Added NUM_FIXED_BLOCKS to tests for + REG_BASIC_BLOCK. + (rest_of_handle_local_alloc): Moved computation of UREC from + global to here. + * function.c (regno_clobbered_at_setjmp): Added to test to see if + regno was valid. Moved function size test higher in call + heirarchy. + (setjmp_args_warning): Added tests to early out of check if + function is small or there are no setjmps. + * df.h (DF_RI, df_ri, DF_RI_LIFE, DF_RI_SETJMP, df_ri_add_problem, + df_ri_get_setjmp_crosses): Deleted. + (DF_NOTE, df_note, df_note_add_problem): Added. + * gcse (gcse_main): Removed computation of RI information and + removed calls to allocate_reg_info. + * init-regs.c (initialize_uninitialized_regs): Removed call to + allocate_reg_life_data. + * regclass.c (reg_info_data, reg_info_head, reg_pref_buffer, + allocate_reg_life_data, allocate_reg_info, clear_reg_info_regno): + Deleted. + (scan_one_insn): Changed some calls to REG_N_SETS and REG_N_REFS + to INC_REG_N_SETS and INC_REG_N_REFS. + (regclass): Allocate register information locally. + (free_reg_info): Changed the structures freed. + (reg_scan): Removed call to allocate_reg_info and changed call to + REG_N_SETS to DF_REG_DEF_COUNT. + * combine (try_combine, remove_death, distribute_notes): Removed + computation of REG_N_DEATHS. Changed some references to + REG_N_SETS to INC_REG_N_SETS. + (rest_of_handle_combine): Replaced call to df_ri_add_problem with + call to df_note_add_problem, and added call to + regstat_init_n_sets_and_refs and regstat_free_n_sets_and_refs. + * bb-reorder (rest_of_handle_partition_blocks): Removed call to + allocate_reg_life_data. + * df-problems.c (reg_n_info, df_ri_problem_p, df_ri_problem_data, + df_ri_alloc, df_ri_start_dump, df_ri_get_setjmp_crosses): Removed. + (print_note): Renamed df_print_note. + (df_kill_notes): Added parameters to save notes rather than just + let them die and remake them. + (df_set_note): New function. + (df_set_unused_notes_for_mw, df_set_dead_notes_for_mw, + df_create_unused_note, df_ri_bb_compute, df_ri_compute, + df_ri_free): Removed ri information computations. + (df_ri_bb_compute): Renamed to df_note_bb_compute. + (df_ri_compute): Renamed to df_note_compute. + (df_ri_free): Renamed to df_note_free. + (problem_RI): Renamed to problem_NOTE + (df_ri_add_problem): Renamed to df_note_add_problem. + * (reg-stack.c): Changed call to df_ri_add_problem to + df_note_add_problem. + * combine-stack-adj.c (rest_of_handle_stack_adjustments): Ditto. + * (Makefile.in): Added regstat.o. + * sched-rgn.c (schedule_insns): Changed call to df_ri_add_problem to + df_note_add_problem. Added calls to regstat_compute_calls_crossed + and regstat_free_calls_crossed. + * basic_block.h (REG_BLOCK_UNKNOWN, REG_BLOCK_GLOBAL, + REG_BASIC_BLOCK): Moved to regs.h. + * config/sparc/sparc.c (sparc_check_64): Changed REG_N_SETS to + DF_REG_DEF_COUNT. + * config/sh/sh.c (flow_dependent_p_1): Ditto. + (sh_md_init): Removed useless attempt to measure pressure on R0. + * config/m68k/m68k.c (m68k_output_mi_thunk): Removed call to + allocate_reg_info. + * reload1.c (delete_output_reload): Added NUM_FIXED_BLOCKS to + tests for REG_BASIC_BLOCK. + +2007-05-07 Seongbae Park <seongbae.park@gmail.com> + + * dse.c (add_wild_read): Do not remove read_info_t + that has non-zero alias_set. + (canon_address): Remove unused parameter for_read and bb_info. + Remove the unused code path, and update the dump message. + (record_store): Accomodate canon_address signature change. + (check_mem_read_rtx): Don't bail out early for wild_read. + (check_mem_read_rtx): Accomodate canon_address signature change. + (scan_insn): Move call insn handling after note_uses. + +2007-05-05 Ramana Radhakrishnan <ramana.r@gmail.com> + Serge Belyshev <belyshev@depni.sinp.msu.ru> + Kenneth Zadeck <zadeck@naturalbridge.com> + + * combine.c (recog_for_combine): Keep unused notes from being + generated for scratch registers. + * config/alpha/alpha.c: Add include for df.h. + + +2007-05-01 Kenneth Zadeck <zadeck@naturalbridge.com> + + Do not delete regnotes when recreating them. + * df-problems.c (df_kill_notes): Store notes into new parameters + rather than killing them. + (df_set_note): New function. + (df_set_unused_notes_for_mw, df_set_dead_notes_for_mw, + df_create_unused_note, df_ri_bb_compute): Call df_set_note to + create new notes. + +2007-04-30 Seongbae Park <seongbae.park@gmail.com> + + * df-scan.c (df_reorganize_refs_by_reg_by_insn): + Scan artificial defs and uses as well. + * passes.c (init_optimization_passes): + Move pass_stack_ptr_mod from after pass_inc_dec. + +2007-04-29 Steven Bosscher <steven@gcc.gnu.org> + + Small cleanups without any semantics changes: + * df-scan.c (df_ref_record): Fix small style typo. + * df-core.c (df_hybrid_search_forward): Cleanup redundant loads. + (df_hybrid_search_backward): Likewise. + (df_worklist_dataflow): Check DIR against DF_NONE instead of 0. + + Small speedups: + * haifa-sched.c (move_block_after_check): Mark df solutions dirty + here, instead of... + (move_succs): ...here. + * df-core.c (df_analyze): Only verify POSTORDER_INVERTED if + checking is enabled. + + Fix Java bootstrap on ia64 + * emit-rtl.c (try_split): Unshare RTL sequences produced by splitters. + +2007-04-27 Kenneth Zadeck <zadeck@naturalbridge.com> + + * timevar.def (TV_DF_UR): Removed. + * df-scan.c (df_scan_alloc): Change pool size. + * df-core.c (df_finish_pass, rest_of_handle_df_initialize, + df_get_bb_dirty, df_verify): Merged df_ur and df_live problems + into df_live. + * global.c (compute_regsets, rest_of_handle_global_alloc): Ditto. + * df.h (DF_UR, DF_UR_BB_INFO, DF_UR_IN, DF_UR_OUT, df_ur, + df_ur_get_bb_info): Removed. + (df_ur_bb_info): Merged df_ur and df_live problems + into df_live. + * init-regs.c (initialize_uninitialized_regs): Changed DF_UR_IN to + DF_LIVE_IN. + * df_problems.c (df_ur_problem_data): Renamed to + df_live_problem_data. + (df_ur_set_bb_info): Renamed to df_live_set_bb_info. + (df_ur_free_bb_info): Renamed to df_live_free_bb_info. + (df_ur_alloc): Renamed to df_live_alloc. + (df_ur_reset): Renamed to df_live_reset. + (df_ur_bb_local_compute): Renamed to df_live_bb_local_compute. + (df_ur_local_compute): Renamed to df_live_local_compute. + (df_ur_init): Renamed to df_live_init. + (df_ur_confluence_n): Renamed to df_live_confluence_n. + (df_ur_transfer_function): Renamed to df_live_transfer_function. + (df_ur_local_finalize): Removed. + (df_ur_free): Renamed to df_live_free. + (df_ur_top_dump): Renamed to df_live_top_dump. + (df_ur_bottom_dump): Renamed to df_live_bottom_dump. + (df_ur_verify_solution_start): Renamed to + df_live_verify_solution_start. + (df_ur_verify_solution_end): Renamed to + df_live_verify_solution_end. + (problem_UR): Renamed to problem_LIVE. + (df_ur_add_problem): Renamed to df_live_add_problem. + (df_ur_verify_transfer_functions): Renamed to + df_live_verify_transfer_functions. + (df_live_set_bb_info, df_live_free_bb_info, df_live_alloc, + df_live_free, df_live_top_dump, df_live_bottom_dump, + df_live_add_problem): Deleted. + (df_chain_fully_remove_problem): Changed pool alloc block size. + * dce.c (dce_marked_bitmap_obstack): Removed. + (marked_insn_p, mark_insn, init_dce, end_ud_dce, fini_dce, + fast_dce): Changed marked to be sbitmap rather than bitmap. + * alloc_pool.c (create_alloc_pool, pool_alloc, pool_free): Split + free_list into virgin_free_list and returned_free_list. + * alloc_pool.h (free_list): Split into virgin_free_list and + returned_free_list. + (virgin_elts_remaining): New variable. + +2007-04-26 Kenneth Zadeck <zadeck@naturalbridge.com> + + * df-core.c (df_remove_problem): Allow df_ur and df_live + problems to be removed. + * global.c (compute_regsets): Remove df_ur and df_live when + adding df_urec. + (rest_of_handle_global_alloc): Add back df_ur and df_live when + removing df_urec. + * timevar.def (TV_DF_UD): Renamed to TV_DF_UR. + * function.c (thread_prologue_and_epilogue_insns): Removed + call to df_analyze. + * df-problems.c (problem_UR): Added remove function and renamed TV. + (problem_LIVE): Added remove function. + + +2007-04-26 Seongbae Park <seongbae.park@gmail.com> + + * tree-pass.h: Declaration for new pass. + * passes.c (init_optimization_passes): New pass. + * dce.c (prescan_insns_for_dce): Pass new param "fast" through. + (mark_artificial_uses, mark_reg_dependencies): New functions + - resurrected from the old svn revision. + (end_ud_dce, rest_of_handle_ud_dce, gate_ud_dce): New function. + (pass_ud_rtl_dce): New pass. + (fast_dce): New parameter for prescan_insns_for_dce. + +2007-04-24 Seongbae Park <seongbae.park@gmail.com> + + * final.c (leaf_renumber_regs_insn): Use SET_REGNO(). + * df-scan.c (df_insn_refs_collect): Handle non-local gotos. + * sparc.c: New include of df.h. + * dce.c (prescan_insns_for_dce): Call mark_libcall for all insns + with with REG_LIBCALL_ID. + +2007-04-23 Steven Bosscher <steven@gcc.gnu.org> + Kenneth Zadeck <zadeck@naturalbridge.com> + + * df-scan.c (problem_SCAN): Add time variable. + (df_refs_add_to_chains): Delete the refs vectors before + allocating new ones. + * df-core.c (df_analyze_problem): Push and pop problem specific + time vectors. + * timevar.def (TV_DF_SCAN, TV_DF_RU, TV_DF_RD, TV_DF_LR, TV_DF_UD, + TV_DF_LIVE, TV_DF_UREC, TV_DF_CHAIN, TV_DF_RI): New problem + specific time vars. + * dse.c (positions_needed): Changed to be bitmask from sbitmap. + (store_info.rhs): New field. + (free_store_info, step6): Changed positions_needed to be bitmask + from sbitmap. + (read_info.begin, read_info.end): Changed to be int. + (deferred_change): New struct. + (deferred_change_pool): New variable. + (step0): Initialize deferred_change_pool. + (canon_address): Added parameter to cselib_expand_value_rtx and + improved debugging. + (clear_rhs_from_active_local_stores, replace_read): New function. + (record_store, check_mem_read_rtx): Added code to invalidate + store_info.rhs. Changed positions_needed to be bitmask from + sbitmap. + (step1): Added code to process deferred_change_list. + (rest_of_handle_dse): Added deferred rescanning. + * df.h (df_problem.tv_id): New timevar for each problem. + * cselib.c (expand_loc, cselib_expand_value_rtx): Added code to + limit the amount of recursion allowed when expanding addresses. + * cselib.h (cselib_expand_value_rtx): Added max recursion + parameter. + * rtl.h (rhs_regno): Renamed tree to x. + * df-problems.c (df_problem problem_RU, df_problem problem_RD, + problem_LR, problem_UR, problem_LIVE, problem_UREC, problem_CHAIN, + problem_RI) Added timevar. + (df_live_local_finalize): Only update solution if dirty. + * Makefile.in (dse.o): Added dependencies. + +2007-04-23 Ian Lance Taylor <iant@google.com> + + * lower-subreg.c (resolve_reg_notes): Discard REG_DEAD and + REG_UNUSED notes for split registers. + (decompose_multiword_subregs): Remove life_blocks. + +2007-04-22 Steven Bosscher <steven@gcc.gnu.org> + + * df.h (df_bitmap_obstack): Declare. + + * df-scan.c (df_scan_problem_data): Add new bitmap obstacks for + regsets and insns/notes to be rescanned. + (df_scan_free_internal): Free the new bitmap obstacks. + (df_scan_alloc): Initialize the new bitmap obstacks, and use them. + (df_scan_free): Set df->blocks_to_analyze to NULL after freeing it. + (df_insn_rescan_all): Put temporary bitmaps on df_bitmap_obstack. + (df_process_deferred_rescans): Likewise. + (df_get_call_refs): Likewise. + (df_update_entry_block_defs): Likewise. Put entry_block_defs on + the regsets bitmap obstack. + (df_record_exit_block_uses): Changed like df_record_exit_block_uses. + (df_entry_block_bitmap_verify): Put temporary bitmaps on df_bitmap_obstack. + (df_exit_block_bitmap_verify): Likewise. + (df_scan_verify): Likewise. + + * df-core.c (df_bitmap_obstack): Declare. + (rest_of_handle_df_initialize): Initialize it. + (rest_of_handle_df_finish): Release it. + (df_set_blocks, df_worklist_dataflow, df_analyze, + df_compact_blocks): Use it. + + * df-problems.c (df_set_seen): Use df_bitmap_obstack for seen_in_block + and seen_in_insn. + (df_ri_compute): Likewise for bitmaps allocated for the RI problem. + + For the RU and RD problems, introduce per-problem bitmap obstacks: + (df_r[ud]_problem_data): Add a per-problem bitmap + obstack. + (df_r[ud]_alloc): Initialize and use them per df problem. + (df_r[ud]_confluence_n, df_r[ud]_transfer_function): Use them. + (df_r[ud]_free): Release them. + + * dce.c (dce_marked_bitmap_obstack, dce_blocks_bitmap_obstack, + dce_tmp_bitmap_obstack): New bitmap obstacks. + (init_dce): Initialize them. Use dce_marked_bitmap_obstack for + the bitmap of marked insns. + (end_fast_dce): Renamed to... + (fini_dce): ...this. Release the new bitmap obstacks. + (dce_process_block): Allocate local_live on dce_tmp_bitmap_obstack. + (fast_dce): Allocate basic block bitmaps on dce_blocks_bitmap_obstack. + (rest_of_handle_fast_dce): Call fini_dce, and clear df_in_progress. + (run_fast_df_dce): Likewise. + +2007-04-21 Andrew Pinski <andrew_pinski@playstation.sony.com> + + * config/spu/spu.c (fsmbi_const_p): Replace flow2_completed with + epilogue_completed. + +2007-04-16 Steven Bosscher <steven@gcc.gnu.org> + + * fwprop.c (forward_propagate_into): If multiple sets + return. + +2007-04-16 Kenneth Zadeck <zadeck@naturalbridge.com> + + * df-scan.c (df_ref_create, df_reg_chain_unlink, + df_install_refs): Use df->analyze_subset. + (df_reorganize_refs_by_reg_by_reg, df_reorganize_refs_by_reg_by_insn): + New functions. + (df_reorganize_refs_by_reg): Split into + df_reorganize_refs_by_reg_by_reg, + df_reorganize_refs_by_reg_by_insn. + (df_add_refs_to_table): Do not add hardware_regs if not asked for. + (df_reorganize_refs_by_insn): Move call to count_refs. + (df_maybe_reorganize_def_refs, df_maybe_reorganize_use_refs): + Remove code to modify df->total_size. + * df-core.c (df_set_blocks): Set df->analyze_subset. + (df_finish_pass): Clear df->analyze->subset. + (df_analyze): Use df->analyze_subset. + (df_dump_start): Remove total_size from dump. + * df.h (DF_RI_NO_UPDATE, DF_DEFS_TOTAL_SIZE, DF_USES_TOTAL_SIZE): + Removed. + (df.analyze_subset): New field. * df-problems.c (df_ri_alloc, + df_ri_compute): Removed DF_RI_NO_UPDATE. + + +2007-04-12 Steven Bosscher <steven@gcc.gnu.org> + Kenneth Zadeck <zadeck@naturalbridge.com> + + * cse.c (cse_insn): Do not emit barriers. + * dse.c (store_info.address): Renamed to mem_addr. + (canon_address): Removed address_out parameter. + (record_store): Removed address var and compute mem and + mem_address differently. + (check_mem_read_rtx): Removed address and changed parameters to + canon_true_dependence. + * cselib.c (cselib_expand_value_rtx): Do not translate + FRAME_POINTER_REGNUM and HARD_FRAME_POINTER_REGNUM. + +2007-04-11 Steven Bosscher <steven@gcc.gnu.org> + + * gcse.c (hash_scan_set): Make sure that INSN has only one + SET in its PATTERN before making SRC anticipatable. + * cfglayout.c (fixup_reorder_chain): Fix merge error, don't + call compact_blocks here. + +2007-04-09 Kenneth Zadeck <zadeck@naturalbridge.com> + + * df-core.c (add_problem): Keep the problem sorted in + problems_in_order. + * global.c (compute_regsets): Recompute register information. + +2007-04-08 Kenneth Zadeck <zadeck@naturalbridge.com> + + * optabs.h (maybe_encapsulate_block): Made public. + * optabs.c (maybe_encapsulate_block): Ditto. + (emit_no_conflict_block, emit_libcall_block): Remove + REG_LIBCALL_ID when deleteing REG_RETVAL and REG_LIBCALL + notes. + * reload1.c (reload): Ditto. + * config/rs6000/rs6000.c (rs6000_legitimize_tls_address): + Use maybe_encapsulate_block to add libcall notes. + +2007-04-07 Kenneth Zadeck <zadeck@naturalbridge.com> + + * dse.c (clear_alias_set_lookup): New function. + (store_info.is_set): New field. + (replace_inc_dec, delete_dead_store_insn): Added more ifdefs so + dse could be tested on mainline. + (canon_address): Moved lookup of alias_set info to subroutine and + added better logging. Added code to detect if address contains an + AND and if so, escape. + (record_store, check_mem_read_rtx): White space cleanup. Added + code to skip store_info's for clobbers when comparing with + previous insn stores. We also mark store_info's as to whether + they are set or clobber based. + (scan_insn): Only add insn to active_stores_list if it has exactly + one store. + (step1): Add code to remove a store to the frame that goes dead at + the end of a function if there is a non overlaping block mode read + from the stack. The global algorithm cannot do this because it + takes a more conservative view of block mode reads. + (dse_confluence_0, dse_confluence_n, dse_transfer_function, + step5_spill, pass_rtl_dse2): Add ifdefs so this can be tested on + mainline. + (step5_nospill): Ignore clobbers and only look at the set to + decide if an insn can be deleted. + * init_regs.c: Whitespace. + * cselib.c (cselib_expand_value_rtx): Fixed comment and changed to + use proper macro. + +2007-04-06 Paolo Bonzini <bonzini@gnu.org> + + * bitmap.c (bitmap_set_range): New. + (bitmap_clear_range): Small optimization. + * bitmap.h (bitmap_set_range): New. + * df-problems.c (df_ref_bitmap): Remove. + (struct df_rd_problem_data, df_ru_problem_data): Remove related + data structures. + (df_ru_alloc, df_rd_alloc): Don't allocate them. + (df_ru_free, df_rd_free): Don't free them. + (df_ru_bb_local_compute_process_def, df_ru_local_compute, + df_rd_bb_local_compute_process_def, df_rd_local_compute): + Use bitmap_set_range and bitmap_clear_range instead of df_ref_bitmap. + +2007-04-05 Kenneth Zadeck <zadeck@naturalbridge.com> + + * sbitmap.c (sbitmap_empty_p): New function. + * sbitmap.h (sbitmap_empty_p): New function. + * dbgcnt.def (dse): New counter and sorted the others. + * df-core.c (df_worklist_dataflow): Made init_fun optional. + * cse.c (cse_main): Do dce at start of pass. + * timevar.def (TV_DSE3): Deleted. + * dse.c: New pass. + * dse.h: New file for dse pass. + * gcse.c (gcse_main): Set up the register info. + * alias.c (clear_reg_alias_info): Removed dead function. + * rtl.h (clear_reg_alias_info): Removed dead function. + * cselib.c (cselib_discard_hook): Added cselib val parameter. + (discard_useless_values): Added hook call here. + (remove_useless_values): Removed hook call from here. + (expand_loc, cselib_expand_value_rtx): New function. + (cselib_finish): Clear hook. + * cselib.h (cselib_discard_hook): Added cselib val parameter. + (expand_loc, cselib_expand_value_rtx): New function. + * common.opt (flag_flow_dce, new-dce): Removed flag. + (flag_dce, flag_dse): New flags. + * Makefile.in (dse.o): New pass. + * passes.c (init_iptimization_passes): Removed dse after combine. + Renamed dse3 to dse2. + * dce.c (delete_unmarked_insns): Renamed new_dce to dce. + (end_dce, mark_artificial_uses, mark_reg_dependencies, + invariant_store_base_eq, invariant_store_base_hash, value_store_base_eq, + value_store_base_hash, store_base_del, rs_init, rs_confluence, + rs_transfer_function, init_invariant_store_group, + init_value_store_group, empty_store_group, end_store_group, + init_rs_dflow, end_rs_dflow, init_unmarked_stores, + end_unmarked_stores, init_dse, end_dse, dump_stores, + split_address, add_store_offset, record_store, record_stores, + store_offset_compare, store_base_local, + invariant_store_base_local, value_store_base_local, + value_store_base_useless, remove_useless_values, + store_base_global, finish_max_in_luid, + calculate_reaching_stores, frame_stores_escape_p, + store_base_prune_needed, mark_escaping_stores, + insn_might_read_mem_rtx, insn_might_read_mem_use, + insn_might_read_mem_p, mark_dependent_stores, + prescan_insns_for_dse, rest_of_handle_dse, gate_dse): Deleted functions + as part of new dse pass in separate file. + * reload1.c (alter_reg): Added calls to register spill slots + with dse. + +2007-04-05 Peter Bergner <bergner@vnet.ibm.com> + + * config/rs6000/rs6000.c (rs6000_legitimate_address): Disallow + PRE_MODIFY for TDmode. + +2007-04-04 Kenneth Zadeck <zadeck@naturalbridge.com> + + * cfg.c (dump_reg_info): Added check to lower max if new regs + were added. + * init-regs.c (initialize_uninitialized_regs): Changed code to + insert move right before uninitized insn. + +2007-04-03 Paolo Bonzini <bonzini@gnu.org> + + * df-problems.c (df_chain_create_bb): Plug leak. + + * df-problems.c (df_chain_alloc): Fix typo. + +2007-04-03 Paolo Bonzini <bonzini@gnu.org> + + * dce.c (dce_process_block): Copy into DF_LR_IN. + * df-problems.c (df_lr_free_bb_info): Support bb_info->in + being the same bitmap as bb_info->top. + (df_lr_alloc): The adef and ause fields must be either + both NULL or both non-NULL. Initialize bb_info->top + to bb_info->in. + (df_lr_bb_local_compute): Allocate adef, ause, top together. + (df_lr_transfer_function): Simplify according to above + assumptions. + (df_lr_free): Zero out bb_info->top instead of freeing it + if it is the same as bb_info->in. + +2007-04-01 Kenneth Zadeck <zadeck@naturalbridge.com> + + * config/ia64/ia64.c (emit_predicate_relation_info): Changed + DF_LIVE_IN to df_get_live_in. + +2007-03-29 Paolo Bonzini <bonzini@gnu.org> + + * bitmap.c (bitmap_elt_copy, bitmap_elt_ior): New. + (bitmap_ior, bitmap_ior_into): Use them. + (bitmap_and_compl): Use them, return whether DST changed. + (bitmap_ior_and_compl): Rewrite. + * bitmap.h (bitmap_and_compl): Return a bool. + + * Makefile.in (alias.o): Fix dependencies. + * alias.c (find_base_value, init_alias_analysis): Use + DF_REG_DEF_COUNT. Include df.h. + * cfg.c (dump_reg_info): Don't fail if reg_info not initialized. + * cse.c (cse_main): Assume dataflow initialized. Call reg_scan. + * gcse.c (gcse_main): Call df_analyze, remove call to reg_scan. + * local-alloc.c (rest_of_handle_local_alloc): Call allocate_reg_info + earlier. + * passes.c (init_optimization_passes): Initialize dataflow before CSE. + + * global.c (n_reg_sets): Remove. + (reg_sets): Change to a VEC. + (mark_reg_store): Push onto reg_sets. + (global_conflicts): Assert reg_sets is empty, pop values out of it. + Don't allocate it nor free it here. + (global_alloc): Allocate reg_sets if necessary. + + * see.c (rest_of_handle_see): Don't call reg_scan. + * tracer.c (rest_of_handle_tracer): Likewise. + * cfgcleanup.c (rest_of_handle_jump2): Likewise. + * bb-reorder.c (fix_edges_for_rarely_executed_code): Likewise. + * loop-init.c (rtl_loop_done): Likewise. + * ifcvt.c (rest_of_handle_if_conversion): Likewise. + * mode-switching.c (optimize_mode_switching): Remove useless + allocate_reg_info. + * lower-subreg.c (decompose_register): Don't call clear_reg_info_regno. + (decompose_multiword_subregs): Don't call reg_scan_update. + * web.c (rest_of_handle_web): Delete. + (pass_web): Use web_main as pass routine. + + * regclass.c (max_parallel, max_set_parallel): Remove. + (reg_scan): Don't set them. + (reg_scan_update): Delete. + (reg_scan_mark_refs): Remove last parameter. + + * rtl.h (max_parallel): Remove. + +2007-03-29 Paolo Bonzini <bonzini@gnu.org> + + * df-core.c (pass_df_finish): Restore. + (pass_df_finish_opt, pass_df_finish_no_opt): Remove. + * tree-pass.h (pass_df_finish): Restore. + (pass_df_finish_opt, pass_df_finish_no_opt): Remove. + * passes.c (init_optimizations_passes): Remove pass_df_finish_no_opt. + Rename pass_df_finish_opt to pass_df_finish. + * reg-stack.c: Mostly evert previous commit. + +2007-03-28 Kenneth Zadeck <zadeck@naturalbridge.com> + + * tree-pass.h (pass_df_initialize, pass_df_finish): Deleted. + (pass_df_initialize_opt, pass_df_initialize_no_opt, + pass_stack_regs_run, pass_df_finish_opt, pass_df_finish_no_opt): + Added. + * df-core.c (df_finish_pass): Made ur and live problems condition + on optimization. + (pass_df_initialize): Renamed to pass_df_initialize_opt. + (pass_df_initialize_no_opt): New pass. + (gate_opt, gate_no_opt): New functions. + (pass_df_finish): Renamed to pass_df_finish_opt. + (pass_df_finish_no_opt): New pass. + * mode-switching.c (optimize_mode_switching): Changed references + of DF_LIVE_IN to df_get_live_in. + * global.c (compute_regsets): Only add urec problem when + optimizing. + * function.c (regno_clobbered_at_setjmp): Changed references + of DF_LIVE_OUT to df_get_live_out. + * regclass.c (regclass_init): Only call df_compute_regs_ever_live + if optimizing. + * stack-ptr-mod.c (notice_stack_pointer_modification): Only call + df_update_exit_block_uses if optimizing. + * df-problems.c (df_get_live_in, df_get_live_out): Return df_live + sets if they are there. + * reg-stack.c (rest_of_handle_stack_regs_run): + Added new pass that just runs subpasses. + * passes.c (init_optimization_passes): Added passes to turn on df + at only some parts of the compilation for -O0. Moved + pass_split_before_regstack and pass_stack_regs into a subpass of + the stack regs pass. + +2007-03-28 Paolo Bonzini <bonzini@gnu.org> + + * df.h (df_ru_get_bb_info, df_rd_get_bb_info, df_lr_get_bb_info, + df_ur_get_bb_info, df_live_get_bb_info, df_urec_get_bb_info): + New inlines. + * df-scan.c (df_scan_get_bb_info, df_get_artificial_uses, + df_get_artificial_defs): Delete. + * df-problems.c (df_ru_get_bb_info, df_rd_get_bb_info, + df_lr_get_bb_info, df_ur_get_bb_info, df_live_get_bb_info, + df_urec_get_bb_info): Delete. + +2007-03-20 Seongbae Park <seongbae.park@gmail.com> + + * combine.c (setup_incoming_promotions): Fix a bad merge + - use the argument FIRST instead of recomputing it. + +2007-03-16 Paolo Bonzini <bonzini@gnu.org> + + * tracer.c (tracer): Work around pr19340.c failure by cleaning up + CFG outside cfglayout mode. Proper fix will be brought in by + next mainline merge. + +2007-03-16 Paolo Bonzini <bonzini@gnu.org> + + PR rtl-optimization/31025 + * df.h (df_get_live_out): New prototype. + * df-problems.c (df_get_live_out): New. + (df_ri_bb_compute): Use it. + +2007-03-13 Seongbae Park <seongbae.park@gmail.com> + + * tree-pass.h (pass_subregs_of_mode_finish): New pass declaration. + * regclass.c (record_subregs_of_mode): Made static, and + moved before init_subregs_of_mode to compile. + (cannot_change_mode_set_regs, invalid_mode_change_p): Added assertion. + (finish_subregs_of_mode): New function. + (pass_subregs_of_mode_finish): New pass. + * rtl.h (record_subregs_of_mode): Removed. + * combine.c (gen_lowpart_for_combine): Removed calls to + record_subregs_of_mode. + * passes.c (init_optimization_passes): Moved pass_subregs_of_mode_init + just before local_alloc. New pass pass_subregs_of_mode_finish + after global_alloc. + +2007-03-09 Richard Earnshaw <rearnsha@arm.com> + + * arm.c: Include df.h. + +2007-03-06 Steven Bosscher <steven@gcc.gnu.org> + + * fwprop.c (try_fwprop_subst): Remove redundant df_notes_rescan. + * see.c (rest_of_handle_see): Run a fast dce. Don't run cleanup_cfg. + * tracer.c (tracer): Clean up the cfg when still in cfglayout mode. + * postreload-gcse.c (rest_of_handle_gcse2): Don't run + delete_trivially_dead_insns, this pass cleans up after itself already. + * df-scan.c (df_insn_rescan): Fix typo. + * cse.c (rest_of_handle_cse): Don't use CLEANUP_EXPENSIVE. + (rest_of_handle_cse2): Likewise. Don't call delete_dead_jumptables. + * web.c (rest_of_handle_web): Don't run delete_trivially_dead_insns. + Also don't clean up the CFG, this pass does not transform the CFG. + * loop-init.c (rtl_loop_done): Don't run delete_trivially_dead_insns. + Don't use CLEANUP_EXPENSIVE. + * ifcvt.c (rest_of_handle_if_conversion): Don't use CLEANUP_EXPENSIVE. + * gcse.c (rest_of_handle_jump_bypass): Only remove unreachable basic + blocks before the pass. No need for a reg_scan. Rebuild jump labels + after delete_trivially_dead_insns. Don't use CLEANUP_EXPENSIVE. + (rest_of_handle_gcse): Rebuild jump labels after + delete_trivially_dead_insns. Don't call delete_dead_jumptables. + Don't use CLEANUP_EXPENSIVE. + * cfgcleanup.c (cleanup_cfg): Move delete_dead_jumptables call out of + the loop. + * combine.c (rest_of_handle_combine): Account cleanup_cfg to TV_JUMP. + Don't use CLEANUP_EXPENSIVE. Don't run delete_trivially_dead_insns. + * bb-reorder.c (rest_of_handle_reorder_blocks): Don't use + CLEANUP_EXPENSIVE. Resurrect CLEANUP_CROSSJUMP here. + +2007-03-06 Paolo Bonzini <bonzini@gnu.org> + + * cse.c (validate_canon_reg): Don't do anything if *XLOC is NULL. + Assert it is only called with insn != 0 and it never resets + something non-NULL to NULL. Always use validate_change. + (cse_insn): Always call canon_reg with an INSN except when it + is used on a REG. When processing notes, first canonicalize, + then fold, and call df_notes_rescan. + (cse_process_notes): Rename to cse_process_notes_1, add CHANGED + parameter. + (cse_process_notes_1): Wrapper around cse_process_notes to set + the CHANGED parameter. + (cse_extended_basic_block): Adjust call to cse_process_notes + and use result to call df_notes_rescan. + (cse_main): Set deferred insn rescanning flag, don't rescan on exit. + (pass_cse2): Add TODO_df_finish. + * gcse.c (pass_gcse): Likewise. + +2007-03-04 Roman Zippel <zippel@linux-m68k.org> + + * regclass.c (scan_one_insn): Call df_insn_rescan after breaking + out constant parameter. + +2007-03-04 Roman Zippel <zippel@linux-m68k.org> + + * df-scan.c (df_grow_ref_info): Also add bitmap_addend to size. + +2007-02-25 Ulrich Weigand <uweigand@de.ibm.com> + + Backport from mainline: + * reload.c (find_reloads_address_1): Handle PLUS expressions resulting + from register elimination as PRE_MODIFY / POST_MODIFY increments. + Do not attempt to handle MEM inside auto-inc expressions. + * reload1.c (eliminate_regs_1): Do not attempt to handle elimination + of a register modified by an auto-inc expression. However, do handle + elimination of a register used as PRE_MODIFY / POST_MODIFY increment. + (elimination_effects): Prohibit elimination of a register modified + by an auto-inc expression. Disable register elimination rules whose + target register is modified by an auto-inc expression with variable + increment. + +2007-02-25 Ulrich Weigand <uweigand@de.ibm.com> + + Revert: + 2006-07-01 Daniel Berlin <dberlin@dberlin.org> + David Edelsohn <edelsohn@gnu.org> + Kenneth Zadeck <zadeck@naturalbridge.com> + reload1.c (eliminate_regs_1, elimination_effects): Added cases for + PRE/POST_MODIFY. + +2007-02-22 Seongbae Park <seongbae.park@gmail.com> + + * global.c (reg_becomes_live): Mark registers not live + after CLOBBER. + +2007-02-22 Paolo Bonzini <bonzini@gnu.org> + + * combine.c (set_nonzero_bits_and_sign_copies, reg_nonzero_bits_for_combine, + reg_num_sign_bit_copies_for_combine, get_last_value): Look at DF_LR to + check if a register is uninitialized. + +2007-02-21 Paolo Bonzini <bonzini@gnu.org> + + * combine.c (uid_cuid, max_uid_cuid): Remove. + (INSN_CUID): Replace throughout with DF_INSN_LUID. + (last_call_cuid): Rename to last_call_luid. + (subst_low_cuid): Rename to subst_low_luid. + (last_insn_cost): Rename to max_uid_known. + (uid_log_links): New. + (INSN_COST, LOG_LINKS): New. + (combine_validate_cost): Use INSN_COST instead of accessing + uid_insn_cost. + (create_log_links, clear_log_links): Move before combine_instructions. + (combine_instructions): Don't compute cuids. Allocate uid_log_links. + Call create_log_links and clear_log_links here. Only initialize + data structures for insns within a basic block. Use INSN_COST + instead of accessing uid_insn_cost. Reset last_call_luid and + mem_last_set once for every basic block. Update label_tick on every + basic block, reset label_tick_ebb_start on every label. + (can_combine_p, try_combine, reg_nonzero_bits_for_combine, + reg_num_sign_bit_copies_for_combine, record_value_for_reg, + record_dead_and_set_regs_1, record_dead_and_set_regs, + get_last_value_validate, get_last_value, use_crosses_set_p, + move_deaths, distribute_notes, distribute_links): Rename stuff + as indicated above. In tests for LUIDs, check that we refer to + the same label_tick too. In tests for label ticks, check that + they are > label_tick_ebb_start. + (rest_of_handle_combine): Don't create/clear loglinks here. + + * gengtype.c (adjust_field_rtx_def): Adjust index of JUMP_LABEL. + * caller-save.c (init_caller_save): Adjust creation of INSNs. + * rtl.def (CODE_LABEL): Adjust comment. + (INSN, JUMP_INSN, CALL_INSN, BARRIER): Remove penultimate field. + * emit-rtl.c (unshare_all_rtl_again, verify_rtl_sharing, + unshare_all_rtl_in_chain, make_insn_raw, make_jump_insn_raw, + make_call_insn_raw): Remove references to LOG_LINKS. + * rtl.h (LOG_LINKS): Remove. + (REG_NOTES, CALL_INSN_FUNCTION_USAGE, JUMP_LABEL): Shift index + down by one. + + * regs.h (struct reg_info_def): Remove first_uid and last_uid. + (REGNO_FIRST_UID, REGNO_LAST_UID): Remove. + * cse.c (cse_basic_block_start, cse_basic_block_end, uid_cuid, + max_uid, INSN_CUID): Remove. + (struct cse_basic_block_data): Remove low_cuid and high_cuid. + (reg_used_in_multiple_bb, reg_used_in_bb): New. + (make_regs_eqv): Test reg_used_in_multiple_bb instead of cuids. + (cse_prescan_path): Remove low_cuid and high_cuid. + (mark_reg_use_bb): New. + (cse_main): Replace computation of cuids with initialization of + reg_used_in_multiple_bb. Remove references to deleted variables. + * regmove.c (copy_src_to_dest): Don't update REGNO_FIRST_UID and + REGNO_LAST_UID. + * regclass.c (reg_scan_mark_refs): Remove penultimate argument. + Don't track REGNO_FIRST_UID and REGNO_LAST_UID. + (reg_scan, reg_scan_update): Remove penultimate argument to + reg_scan_mark_refs. + +2007-02-20 Seongbae Park <seongbae.park@gmail.com> + + * sched-ebb.c (schedule_ebbs): Clear DF_LR_RUN_DCE flag + after df_analyze. + +2007-02-20 Kenneth Zadeck <zadeck@naturalbridge.com> + + * fwprop.c (forward_propagate_into): Removed call to + df_recompute_luids. + * see.c (see_free_data_structures): Ditto. + * loop-iv.c (iv_analysis_loop_init): Ditto. + * dce.c (prescan_insns_for_dse): Ditto. + * auto-inc-dec.c (merge_in_block): Moved call to + df_recompute_luids so that it only happens on subsequent passes of + block. + * df-scan.c (df_grow_insn_info): Made public. + * df.h (df_grow_insn_info): Made public. + * df-core.c: Fixed comments. + * df-problems.c (df_ur_bb_local_compute): Reversed order of + processing insns. Removed usage of seen_in_block and + seen_in_insn. Update luid. + (df_ur_local_compute, df_ur_verify_transfer_functions): Added call + to df_grow_insn_info and deleted calls to df_set_seen, df_unset_seen. + +2007-02-16 Paolo Bonzini <bonzini@gnu.org> + + * combine.c (INSN_CUID): Always look up uid_cuid. + (insn_cuid): Delete. + * sched-deps.c (sched_analyze): Don't free LOG_LINKS here. + * sched-int.h (struct dep_list): Don't mention LOG_LINKS in comment. + +2007-02-15 Paolo Bonzini <bonzini@gnu.org> + + * config/sh/sh.c (sh_reorg): Don't look at LOG_LINKS. + +2007-02-15 Paolo Bonzini <bonzini@gnu.org> + + * combine.c (find_single_use): Don't check for reload flags. + +2007-02-15 Paolo Bonzini <bonzini@gnu.org> + + Merge from mainline. + + 2007-02-15 Paolo Bonzini <bonzini@gnu.org> + + * caller-save.c (save_call_clobbered_regs): Do not process sibcalls. + +2007-02-13 Seongbae Park <seongbae.park@gmail.com> + + Merge from mainline. + + 2007-02-13 Seongbae Park <seongbae.park@gmail.com> + + * bitmap.c (bitmap_and, bitmap_and_compl, bitmap_xor): + Ensure dst->current is valid. + +2007-02-13 Kenneth Zadeck <zadeck@naturalbridge.com> + + * lower-subregs.c (resolve_reg_notes): Added code to call + df_notes_rescan when REG_EQUAL notes are changed. + +2007-02-12 Kenneth Zadeck <zadeck@naturalbridge.com> + + * global.c (compute_regsets): Removed first two parameters. + Converted asm_clobbered to regs_asm_clobbered. + (global_alloc): Removed bad merged call to make_accurate_live_analysis. + (global_alloc, rest_of_handle_global_alloc): Removed first two + parameters from compute_regsets. + +2007-02-06 Kenneth Zadeck <zadeck@naturalbridge.com> + + * ifcvt.c (noce_process_if_block): Removed unnecessary insn deletes. + (dead_or_predictable): Removed incorrect bit vector operation. + * lower-subregs (simple_move_operand, resolve_clobber): Change from mainline. + (resolve_clobber): Added df_insn_rescan of clobber. + (decompose_multiword_subregs): Added df setup and removed debugging. + (pass_lower_subreg2): Added df cleanup. + * Makefile.in (lower-subregs.o): Added df.h dependence. + +2007-02-03 Kaz Kojima <kkojima@gcc.gnu.org> + + * config/sh/sh.c (sh_expand_prologue): Remove unneeded brackets. + (sh_expand_epilogue): Add blockage insn when not + frame_pointer_needed. + (sh_output_mi_thunk): Don't use flow analysis here. + * config/sh/sh.md (UNSPEC_EH_RETURN): Remove. + (UNSPECV_EH_RETURN): New macro. + (sibcall_valuei): New. + (sibcall_valuei_pcrel, sibcall_value_pcrel): Likewise. + (sibcall_value_compact, sibcall_value_media): Likewise. + (sibcall_value): Use new sibcall_value* patterns. + (eh_set_ra_si): Use unspec_volatile and UNSPECV_EH_RETURN. + (eh_set_ra_di, eh_set_ra_di+1): Likewise. + +2007-02-03 Kaz Kojima <kkojima@gcc.gnu.org> + + * mode-switching.c (create_pre_exit): Skip CLOBBER of pseudo + register for the result when not optimizing. + (optimize_mode_switching): Move df_ri_add_problem and df_analyze + calls after create_pre_exit call. + +2007-02-03 Kaz Kojima <kkojima@gcc.gnu.org> + + * modulo-sched.c (sms_schedule): Call df_ri_add_problem + with DF_RI_LIFE. + +2007-02-03 Kaz Kojima <kkojima@gcc.gnu.org> + + * df-scan.c (df_get_entry_block_def_set): Use struct_value_rtx + hook instead of TARGET_STRUCT_VALUE_RTX. + +2007-02-03 Kaz Kojima <kkojima@gcc.gnu.org> + + * passes.c (init_optimization_passes): Move df_finish after + delay_slots. + +2007-01-02 Kenneth Zadeck <zadeck@naturalbridge.com> + + * optabs.c (emit_no_conflict_block): Removed redundant code to + number libcalls. + * tree-pass.h (pass_initialize_subregs): Renamed to + pass_initialize_regs. + * passes.c (init_optimization_passes): Ditto. + * builtins.c (expand_builtin_setjmp_receiver): Changed + gen_rtx_ASM_INPUT to gen_blockage. + * function.c (expand_function_end): Ditto. + * stmt.c (gen_non_local_got_receiver): Ditto. + * config/i386.md (blockage): Don't take an operand. + (prologue_use): New. + * config/i386.c (ix86_expand_prologue): Don't build a blockage + insn with an operand. If the PIC reg is used, use a prologue_use + insn to protect it from being removed. + * df-scan.c (df_get_entry_block_def_set): Generate ref for + INCOMING_RETURN_ADDR_RTX after reload_completed. + * df-core.c (df_ref_debug): Fixed formatting. + * init-regs.c: Renamed from subregs_init.c and changed to + initialize all fully uninitialized registers. + * bt-load.c (compute_defs_uses_and_gen): Changed check for + ASM_INPUT to UNSPEC_VOLATILE. + * df-problems.c (df_set_unused_notes_for_mw, + df_set_dead_notes_for_mw, df_ri_bb_compute): Cleaned up reg_notes + debugging info. + (df_set_dead_notes_for_mw): Added do_not_gen to test of all notes + dead. + * Makefile.in (subregs-init.*): Renamed to init-regs.*. + * config/arm/arm.c (arm_expand_prologue, thumb_expand_prologue): + Removed code to generate explicit uses of LR_REGNUM from prologue. + * config/arm/arm.h (EPILOGUE_USES): Made LR_REGNUM unconditionally + added. + * dce.c (deletable_insn_p): Made clobbers always live for use-def + based dce. + (prescan_insns_for_dce, prescan_insns_for_dse): Added fast + parameter to deletable_insn_p. + (run_fast_df_dce): Temporarily turn off DF_DEFER_INSN_RESCAN + during this subphase. + + +2007-02-01 Seongbae Park <seongbae.park@gmail.com> + + * df-scan.c (df_sort_and_compress_refs): + Style fix. + (df_sort_and_compress_mws): + Style fix. Fix missing & in parameters for df_mw_compare call. + +2007-02-01 Seongbae Park <seongbae.park@gmail.com> + + * df-scan.c (df_swap_refs): New function. + (df_sort_and_compress_refs, df_sort_and_compress_mws): + Avoid qsort call overhead if the array has only 2 elements + or if it's already sorted. + +2007-01-17 Eric Christopher <echristo@apple.com> + + * config/mips/mips.c (mips_output_function_epilogue): Use SET_REGNO + instead of REGNO. + (mips_output_mi_thunk): Ditto. + +2007-01-17 Eric Christopher <echristo@apple.com> + + * reorg.c (fill_simple_delay_slots): Fix prototype. + +2007-01-15 Andreas Krebbel <krebbel1@de.ibm.com> + + * regmove.c: (fixup_match_1): Call df_notes_rescan after + changing insn notes. + +2007-01-15 Andreas Krebbel <krebbel1@de.ibm.com> + + * config/s390/s390.c: Include df.h. + (s390_emit_prologue): Call df_insn_rescan when annotating constant + pool references. + +2007-01-15 Andreas Krebbel <krebbel1@de.ibm.com> + + * rtl.h (split_all_insns_noflow): Add prototype. + * recog.c (split_all_insns_noflow): Remove static. + +2007-01-14 Kenneth Zadeck <zadeck@naturalbridge.com> + * fwprop.c (update_df): Only rescan if insn has really changed. + (try_fwprop_subst): Removed call to df_set_bb_dirty. + (fwprop_init): Added new parameter to df_maybe_reorganize_use_refs. + * df-scan.c (df_scan_alloc): Removed calls to df_grow_ref_info. + (df_scan_blocks, df_ref_create, df_reg_chain_unlink, + df_maybe_reorganize_use_refs, df_maybe_reorganize_def_refs, + df_install_ref, df_install_refs, df_refs_add_to_chains): Changed + to support new modes adding refs to the df.ref_info.refs. + (df_reg_chain_unlink, df_install_ref, df_ref_create_structure, + df_hard_reg_used_p): Changed DF_REGS_EVER_LIVE to + DF_HARD_REG_LIVE. + (df_ref_remove, df_ref_create): Added call to dirty the block when + a ref is changed manually. + (df_insn_rescan, df_notes_rescan): Added bb param to + df_refs_add_to_chains. + (df_reorganize_refs): Renamed to df_reorganize_refs_by_reg. + (df_count_refs, df_add_refs_to_table, + df_reorganize_refs_by_insn_bb, df_reorganize_refs_by_insn, + df_hard_reg_used_count): New functions. + (df_drop_organized_tables): Removed. + * df_core.c (df_set_blocks, df_finish_pass): Changed + to support new modes adding refs to the df.ref_info.refs. + * df.h (df_ref_flags.DF_REGS_EVER_LIVE): Renamed to + DF_HARD_REG_LIVE. + (df_ref_order): New Enum. + (df_ref_info.{refs_organized_alone, refs_organized_with_eq_uses, + add_refs_inline}): Replaced with df_ref_info.ref_order. + (DF_REG_EVER_LIVE_P): Removed macro. + * df-problems.c (df_ru_local_compute): Added parm to + df_maybe_reorganize_use_refs. + (df_rd_local_compute): Added parm to + df_maybe_reorganize_def_refs. + +2007-01-12 Kenneth Zadeck <zadeck@naturalbridge.com> + * df-scan.c (df_collection_rec): New structure. + (df_chains): Deleted structure. + (DEBUG_DF_RESCAN): Removed conditional compilation of rescanning. + (df_scan_start_block): Added ifdefed out code that prints the + scanning of insns. + + (df_ref_create, df_get_artificial_defs, df_get_artificial_uses, + df_reg_chain_unlink, df_ref_remove, df_ref_chain_delete_du_chain, + df_ref_chain_delete, df_mw_hardreg_chain_delete, df_insn_rescan, + df_ref_chain_change_bb, df_insn_change_bb, + df_ref_change_reg_with_loc_1, df_mw_hardreg_chain_delete_eq_uses, + df_notes_rescan, df_refs_add_to_chains, df_ref_create_structure, + df_ref_record, df_def_record_1, df_defs_record, df_uses_record, + df_get_conditional_uses, df_insn_refs_collect, df_recompute_luids, + df_bb_refs_collect, df_entry_block_defs_collect, + df_update_entry_block_defs, df_exit_block_uses_collect, + df_record_exit_block_uses, df_update_exit_block_uses, + df_compute_regs_ever_live, df_reg_chain_mark, + df_reg_chain_verify_unmarked, df_insn_refs_verify, df_bb_verify, + df_scan_verify): Changed to use vector rep for refs. + (df_get_call_refs) Ditto plus fixed bug where clobber ref was + generated for same ref as result of call. + (df_reg_chain_create, df_ref_unlink, df_ref_chain_find_ref, + df_ref_chain_find_ref_by_regno, df_ref_chain_append, + df_ref_find_chains, df_ref_add_to_chains, df_insn_refs_record, + df_ref_chain_verify_and_unmark, df_ref_chain_free, df_ref_verify, + df_mw_hardreg_find_hardreg): Deleted function. + (df_ref_compress_rec, df_free_collection_rec, df_ref_compare, + df_sort_and_compress_refs, df_mw_compare, df_sort_and_compress_mws + df_canonize_collection_rec, df_install_ref, df_install_refs, + df_refs_verify, df_mws_verify): New function. + (df_ref_is_equal): Renamed to df_ref_equal_p and changed to use + vector rep for refs. + (df_mw_is_equal): Renamed to df_mw_equal_p and changed to use + vector rep for refs. + * df.h (df_ref_flags.(DF_REF_MW_HARDREG_GROUP, DF_REF_REF_MARKER): + Removed. + (df_ref_flags.(DF_REF_REG_MARKER, DF_REGS_EVER_LIVE)): Renumbered. + (df_mw_hardreg.(loc, start_regno, end_regno, mw_order)): New + fields. + (df_insn_info.(defs, uses, eq_uses, mw_hardregs): Made into arrays + of pointers. + (df_scan_bb_info.(artificial_defs, artificial_uses): Ditto. + (df_ref.ref_order): New field. + (df_ref.next): Removed. + (df.ref_order): New field. + (DF_REF_REF_MARK, DF_REF_REF_UNMARK, DF_REF_IS_REF_MARKED, + DF_REF_NEXT_REF): Removed macro. + (DF_REF_ORDER): New macro. + * df-core.c (df_bb_regno_last_use_find, df_bb_regno_last_use_find, + df_bb_regno_first_def_find, df_bb_regno_last_def_find, + df_insn_regno_def_p, df_find_def, df_find_use, df_refs_chain_dump, + df_mws_dump, df_insn_uid_debug, df_insn_debug): Ditto. + * fwprop.c (update_df): Do not call df_insn_rescan unless insn has + changed. + (local_ref_killed_between_p, all_uses_available_at, + find_occurrence, update_df): Changed to use + vector rep for refs. + * see.c (see_handle_relevant_refs, see_update_relevancy, + see_propagate_extensions_to_uses): Ditto. + * auto-inc-dec.c (find_inc, merge_in_block): Ditto. + * web.c (union_defs, web_main): Ditto. + * global.c (compute_regs_asm_clobbered): Ditto. + * ifcvt.c (dead_or_predicable): Ditto. + * loop-invariant.c (find_defs, check_dependency, + find_invariant_insn): Ditto. + * combine.c (create_log_links): Ditto. + * df-problems.c (df_ru_alloc, df_ru_bb_local_compute_process_def, + df_ru_bb_local_compute_process_use, df_rd_alloc, + df_rd_bb_local_compute_process_def, df_lr_bb_local_compute, + df_lr_simulate_artificial_refs_at_end, df_lr_simulate_one_insn, + df_ur_bb_local_compute, df_urec_bb_local_compute, + df_urec_bb_local_compute, df_chain_remove_problem, + df_chain_remove_problem, df_chain_reset, + df_chain_create_bb_process_use, df_chain_create_bb, + df_chain_top_dump, df_chain_top_dump, df_chain_bottom_dump, + df_set_unused_notes_for_mw, df_set_dead_notes_for_mw, + df_ri_bb_compute): Ditto. + * dce.c (delete_corresponding_reg_eq_notes, mark_artificial_uses, + mark_reg_dependencies, dce_process_block): Ditto. + + + +2007-01-10 Seongbae Park <seongbae.park@gmail.com> + * df-core.c (df_worklist_propagate_backward, + df_worklist_dataflow)): More comments. + (df_iterative_dataflow): Whitespace fixup. + * cfganal.c (inverted_post_order_compute): + More comments and rename a local variable DEST to PRED. + (df_find_deadend): More comments. Use gcc_unreachable(). + +2007-01-09 Seongbae Park <seongbae.park@gmail.com> + * df-core.c (rest_of_handle_df_initialize): Allocate and free new + fields struct dataflow::{postorder_inverted,n_blocks_inverted}. + (df_hybrid_search_forward, df_hybrid_search_backward): Pass visited, + pending, considered as parameters instead of fields of struct df. + (df_worklist_propagate_forward, df_worklist_propagate_backward, + df_worklist_dataflow): New functions. + (df_iterative_dataflow): Remove visited, pending, considered + fields from struct dataflow. + (df_analyze): Allocate and free new fields + df::{postorder_inverted,n_blocks_inverted}. + (df_get_n_blocks, df_get_postorder): Make them return + different values depending on the direction of the dataflow problem. + (df_simple_dataflow): Renamed from df_simple_iterative_dataflow. + Call df_worklist_dataflow instead of df_iterative_dataflow. + * cfganal.c (dfs_find_deadend, inverted_post_order_compute): + New functions. + * df.h (struct dataflow): Remove fields visited, pending, considered. + Add new fields postorder_inverted, n_blocks_inverted. + (df_get_nblocks, df_get_postorder): Prototype change. + (df_simple_dataflow): Renamed from df_simple_iterative_dataflow. + (df_worklist_dataflow): New function prototype. + * df-problems.c: Use df_worklist_dataflow instead of + df_iterative_dataflow for solver. + * basic-block.h (inverted_post_order_compute): New function prototype. + * dce.c (dce_process_block): Pass extra parameter to df_get_n_blocks + and df_get_postorder. + (calculate_reaching_stores): Call df_simple_dataflow, + renamed from df_simple_iterative_dataflow. + + +2007-01-05 Kenneth Zadeck <zadeck@naturalbridge.com> + * see.c (see_update_defs_relevancy): Type fixed. + * df-scan.c (df_reg_chain_unlink, df_ref_verify): Made tolerant of + refs table not being there. + (df_drop_organized_tables): New function. + * df-core.c (df_finish_pass): Drop refs tables after each pass. + * web.c (web_main): Reorganized access to not use ref tables and + go in order of insns. + * df.h (df_drop_organized_tables): New function. + * df-problems.c (df_chain_start_dump): Deleted function. + (df_chain_top_dump, df_chain_bottom_dump): New functions. + +2007-01-03 Kenneth Zadeck <zadeck@naturalbridge.com> + * see.c (see_initialize_data_structures): Does not use + DF_USES_TABLE_SIZE or DF_DEFS_TABLE_SIZE. + (see_handle_relevant_defs, see_handle_relevant_uses): Removed + outer loop. Now a subrouting of see_handle_relevant_refs. + (see_handle_relevant_refs, see_update_relevancy): New functions + that now loops over insns. + (see_update_uses_relevancy, see_update_defs_relevancy): Removed + outer loop. Now a subroutine of see_update_relevancy. + (see_analyze_one_def): Removed unnecessary tests. + (see_propagate_extensions_to_uses): Now iterates over insns and + calls see_handle_relevant_refs and see_update_relevancy. + * df-scan.c (df_reg_chain_unlink, df_insn_delete): Added code to + skip the chain field of refs if the chain problem is not active. + (df_scan_verify): Moved verification of the + out_of_date_transfer_functions fields into the problem verifiers. + * df-core.c (df_set_blocks): More logging. + (df_finish_pass): Moved around when recanning is done. + (rest_of_handle_df_initialize): Moved creation of + handles out_of_date_transfer_functions to the problems. + (df_set_bb_dirty, df_clear_bb_dirty, df_compact_blocks): Now + handles out_of_date_transfer_functions in any problem. + * df.h: Added comments. + * df-problems.c (df_lr_add_problem, df_ur_add_problem, + df_chain_add_problem): Allocates out_of_date_transfer_functions. + (df_lr_verify_transfer_functions, + df_ur_verify_transfer_functions): Now verifies + out_of_date_transfer_functions. + (df_chain_remove_problem): Tears down problem by looping over + insns. + (df_chain_fully_remove_problem, df_chain_free): Frees + out_of_date_transfer_functions. + (df_chain_create_bb): Sets out_of_date_transfer_functions. + + +2007-01-02 Kenneth Zadeck <zadeck@naturalbridge.com> + * df-scan.c (df_reg_chain_create, df_reg_chain_unlink, + df_ref_create_structure, df_hard_reg_used_p): Added code to + process df->hard_regs_live_count. + (df_ref_is_record_live, df_reg_chain_find_ref): Deleted. + (df_refs_add_to_chains): Removed ifdefed code. + (df_compute_regs_ever_live): Fixed "&" vs "&&" problem. + * df-core (rest_of_handle_df_initialize, + rest_of_handle_df_finish): Added code to + process df->hard_regs_live_count. + * global.c (global_alloc): Repositioned use of urec problem. + (build_insn_chain): Changed use of DF_RA_LIVE_TOP to df_get_live_top. + (rest_of_handle_global_alloc): Removed call to df_analyze for no + optimize case. + * local-alloc.c (update_equiv_regs): Added calls to + df_notes_rescan where eq notes are hacked. + (block_alloc): Changed DF_RA_LIVE_TOP to DF_LR_TOP. + (rest_of_handle_local_alloc): Removed addition of urec problem. + * function.c (regno_clobbered_at_setjmp): Changed df_get_live_out + to DF_LIVE_OUT. + * (df_ref_flags.DF_REGS_EVER_LIVE): New flag. + (df.hard_regs_live_count): New bitmap. + (DF_LR_TOP, DF_REG_EVER_LIVE_P): New macro. + (df_get_live_out): Removed. + (df_get_live_top): Added. + * df-problems.c (df_get_live_in): Does not look at DF_LIVE. + (df_get_live_out): Deleted. + (df_get_live_top): Added. + * config/sh/sh.c (calc_live_regs): Changed regs_ever_live to + df_regs_ever_live_p. + * config/mn10300/mn10300.c (fp_regs_to_save): Ditto. + * reload1.c (reload): Corrected the set of bitmaps to modify after + reloading. + + +2007-01-01 Eric Christopher <echristo@apple.com> + + * config/darwin.c: Include df.h. + (machopic_legitimize_pic_address): Use + df_set_regs_ever_live. + +2006-12-28 Kenneth Zadeck <zadeck@naturalbridge.com> + * regs.h: (regs_ever_live, regs_asm_clobbered): Removed. + * final.c (regs_ever_live, regs_asm_clobbered): Removed. + (only_leaf_regs_used, leaf_renumber_regs_insn): Encapsulated + references to regs_ever_live. + * global.c (insn_contains_asm_1, insn_contains_asm, + compute_regs_asm_clobbered): New functions. + (global_alloc): Added call to compute_regs_asm_clobbered and + encapsulated references to regs_ever_live. + (dump_global_regs): Encapsulated references to regs_ever_live. + (rest_of_handle_global_alloc): Changed call to df_scan_alloc. + * regrename.c (regrename_optimize, regrename_optimize): Encapsulated + references to regs_ever_live. + * recog.c (peep2_find_free_register): Ditto. + * rtl-factoring (recompute_gain_for_pattern_seq, + abstract_best_seq): Ditto. + * bt-load.c (move_btr_def, migrate_btr_defs): Ditto. + * reg_stack.c (reg_to_stack): Ditto. + * config/alpha/alpha.c (alpha_ra_ever_killed, alpha_sa_mask, + alpha_sa_size): Ditto. + * config/frv/frv.c (frv_stack_info, frv_function_prologue): Ditto. + * config/s390/390.c (legitimize_pic_address, + legitimize_tls_address, find_unused_clobbered_reg, + s390_regs_ever_clobbered, s390_register_info, + s390_init_frame_layout, s390_update_frame_layout, + s390_emit_prologue): Ditto. + * config/m32c/m32.c (need_to_save): Ditto. + * config/spu/spu.c (spu_split_immediate): Ditto. + * config/sparc/sparc.c (sparc_compute_frame_size, + sparc_output_scratch_registers, save_or_restore_regs, + order_regs_for_local_alloc): Ditto. + * config/m32r/m32r.c (MUST_SAVE_FRAME_POINTER, + MUST_SAVE_RETURN_ADDR, m32r_hard_regno_rename_ok): Ditto. + * config/i386/i386.h (ix86_current_function_calls_tls_descriptor): + Ditto. + * config/i386/i386.c (ix86_select_alt_pic_regnum, ix86_save_reg, + ix86_expand_prologue, legitimize_pic_address, + legitimize_tls_address): Ditto. + * config/sh/sh.c (calc_live_regs, sh_media_register_for_return, + sh_expand_prologue, sh_hard_regno_rename_ok): Ditto. + * config/pdp11/pdp11.c (pdp11_output_function_prologue, + pdp11_output_function_epilogue): Ditto. + * config/pdp11/pdp11.h (may_call_alloca): Ditto. + * config/avr/avr.c (avr_regs_to_save, sequent_regs_live, + avr_peep2_scratch_safe): Ditto. + * config/crx/crx.h (HARD_REGNO_RENAME_OK): Ditto. + * config/crx/crx.c (crx_compute_save_regs): Ditto. + * config/c4x/c4x.c (c4x_isr_reg_used_p, c4x_expand_prologue, + c4x_null_epilogue_p): Ditto. + * config/c4x/c4x.h (reg_class): Ditto. + * config/stormy16/stormy16.c (REG_NEEDS_SAVE): Ditto. + * config/fr30/fr30.c (MUST_SAVE_FRAME_POINTER, + MUST_SAVE_RETURN_POINTER): Ditto. + * config/m68hc11/m68hc11.c (m68hc11_initial_elimination_offset, + m68hc11_total_frame_size, expand_prologue): Ditto. + * config/cris/cris.c (cris_reg_saved_in_regsave_area, + cris_return_addr_rtx): Ditto. + * config/iq2000/iq2000.h (MUST_SAVE_REGISTER): Ditto. + * config/iq2000/iq2000.c (iq2000_can_use_return_insn): Ditto. + * config/mt/mt.c (mt_compute_frame_size): Ditto. + * config/mt/mt.h (save_direction): Ditto. + * config/mn10300/mn10300.c (REG_SAVE_BYTES, can_use_return_insn, + mn10300_get_live_callee_saved_regs, expand_prologue, + initial_offset): Ditto. + * config/ia64/ia64.c (find_gr_spill, ia64_compute_frame_size): + Ditto. + * config/m68k/m68k.c (m68k_save_reg, m68k_hard_regno_rename_ok): + Ditto. + * config/rs6000/rs6000.c (rs6000_got_register, first_reg_to_save, + first_fp_reg_to_save, first_altivec_reg_to_save, + compute_vrsave_mask, rs6000_stack_info, create_TOC_reference, + rs6000_emit_prologue): Ditto. + * config/rs6000/rs6000.h (HARD_REGNO_RENAME_OK): Ditto. + * config/arc/arc.c (MUST_SAVE_REGISTER, MUST_SAVE_RETURN_ADDR): + Ditto. + * config/mcore/mcore.c (calc_live_regs): Ditto. + * config/score/score-mdaux.c (score_save_reg_p): Ditto. + * config/arm/arm.c (use_return_insn, thumb_find_work_register, + arm_compute_save_reg0_reg12_mask, arm_compute_save_reg_mask, + arm_get_vfp_saved_size, arm_output_epilogue, thumb_force_lr_save, + arm_get_frame_offsets, arm_expand_prologue, thumb_far_jump_used_p, + thumb_unexpanded_epilogue, thumb_expand_epilogue): Ditto. + * config/arm/arm.h (HARD_REGNO_RENAME_OK): Ditto. + * config/pa/pa.c (compute_frame_size, pa_output_function_prologue, + hppa_expand_prologue, hppa_expand_epilogue, + hppa_can_use_return_insn_p, output_lbranch): Ditto. + * config/pa/pa.h (HARD_REGNO_RENAME_OK): Ditto. + * config/mips/mips.c (mips_global_pointer, mips_save_reg_p, + mips_can_use_return_insn, build_mips16_call_stub): Ditto. + * config/vax/vax.c (vax_output_function_prologue): Ditto. + * config/v850/v850.c (substitute_ep_register, + compute_register_save_size): Ditto. + * config/h8300/h8300.c (byte_reg, h8300_hard_regno_rename_ok): + Ditto. + * config/mmix/mmix.c (MMIX_CFUN_NEEDS_SAVED_EH_RETURN_ADDRESS, + mmix_initial_elimination_offset, mmix_reorg, + mmix_use_simple_return, mmix_expand_prologue, + mmix_expand_epilogue): Ditto. + * config/bfin/bfin.c (n_dregs_to_save, n_pregs_to_save, + n_regs_saved_by_prologue, expand_interrupt_handler_prologue, + expand_interrupt_handler_epilogue, bfin_hard_regno_rename_ok): + Ditto. + * reload1.c (reload, mark_home_live, spill_hard_reg, + finish_spills): Ditto. + * df-scan.c (regs_ever_live): Added as local var. + (df_scan_free_internal): Delete bitmap. + (df_scan_free_bb_info): Inlined code from removed df_bb_delete. + (df_scan_alloc): Removed blocks_to_rescan param and reference to + out_of_date_transfer_functions and added insns_to_notes_rescan. + (df_scan_free): Removed reference to + out_of_date_transfer_functions. + (df_scan_start_dump, df_scan_start_block): Added ";;" in front of + debugging lines. + (problem_SCAN): Added extra null fields. + (df_scan_blocks): Added code to set entry and exit blocks as + dirty. + (df_insn_delete): Added basic block parameter and support for + insns_to_notes_rescan. + (df_bb_delete): Removed. + (df_insn_rescan, df_insn_rescan_all, df_process_deferred_rescans, + df_notes_rescan): Added support for insns_to_notes_rescan. + (df_insn_rescan, df_insn_rescan_all, df_process_deferred_rescans): + Added extra parameter to df_insn_delete. + (df_process_deferred_rescans): Added support to fixup entry and + exit blocks if regs_ever_live changes. + (df_insn_change_bb): Added debugging. + (df_ref_change_reg_with_loc_1): Added code to dirty the block. + (df_insn_contains_asm_1, df_insn_contains_asm): Function moved to + global.c. + (df_insn_refs_record): Removed code to set insn_contains_asm. + (df_bb_refs_record): Now sets itself dirty. + (df_get_entry_block_def_set, df_get_exit_block_use_set): + Encapsulated references to regs_ever_live. + (df_update_entry_block_defs, df_update_exit_block_uses): Added + finer grained control on dirtying the block. + (df_regs_ever_live_p, df_set_regs_ever_live): New functions. + (df_compute_regs_ever_live): New reset parameter. + (df_verify_blocks): Renamed to df_scan_verify and added more + checking code. + * df-core.c (df_add_problem): Initialized solutions_dirty. + (df_finish_pass): Added calls to verify transfer functions. + (rest_of_handle_df_initialize): Added more initialization for + persistent structures. + * (df_hybrid_search_forward, df_hybrid_search_backward, + df_iterative_dataflow, df_analyze_problem): Removed single_pass parameter. + (df_analyze_problem): Added checking for results of dataflow. + (df_analyze): Added more debugging and removed changing + out_of_date_transfer functions. + (df_get_bb_dirty, df_set_bb_dirty, df_compact_blocks): Supports + each problem having it own incremental + status bits. + (df_clear_bb_dirty, df_verify, df_compute_cfg_image, + df_check_cfg_clean, df_set_clean_cfg): New function. + (df_compact_blocks, df_bb_replace): More debugging. + *df.h (df_alloc_function, df_local_compute_function): Removed + blocks_to_scan parameter. + (df_dataflow_function): Removed single_pass parameter. + (df_verify_solution_start, df_verify_solution_end): New function + types. + (dataflow.out_of_date_transfer_functions, + dataflow.solutions_dirty): Moved from df structure so each problem + could have its own copy. + (df_insn_info.contains_asm, DF_INSN_CONTAINS_ASM): Functionality + moved into global.c. + (df.solutions.dirty, out_of_date_transfer_functions): Moved to + struct dataflow. + (df.redo_entry_and_exit, df.insns_to_notes_rescan): New fields. + (DF_DEBUG_CFG): Flag to control code to check if cfg modifications + are being reported to df. + * df-problems.c (df_print_bb_index, df_lr_top_dump, + df_lr_bottom_dump, df_ur_top_dump, df_ur_bottom_dump): Cleanup + more dump info. + (df_ru_alloc, df_ru_local_compute, df_rd_alloc, + df_rd_local_compute, df_lr_alloc, df_lr_local_compute, + df_ur_alloc, df_ur_local_compute, df_live_alloc, df_urec_alloc, + df_urec_local_compute, df_chain_alloc, df_ri_alloc, + df_ri_compute): Removed blocks_to_rescan parameter. + (problem_RU, problem_RD, problem_LR, problem_UR, problem_LIVE, + problem_UREC, problem_RI): Added two new fields. + (df_lr_problem_data, df_ur_problem_data): New data structure to + support checking of dataflow solutions. + (df_lr_alloc, df_lr_local_compute, df_ur_alloc, + df_ur_local_compute, df_ur_free): Now processes only out of date blocks. + (df_lr_reset, df_lr_verify_solution_start, + df_lr_verify_solution_end, df_lr_verify_transfer_functions, + df_ur_reset, df_ur_local_finalize, df_ur_verify_solution_start, + df_ur_verify_solution_end, df_ur_verify_transfer_functions): New function. + (df_lr_bb_local_compute, df_lr_local_compute): Moved asm scanning to global.c. + (df_lr_local_finalize, df_lr_free, df_live_local_finalize): Added + code to support incremental checking of dataflow solution. + * passes.c (pass_free_cfg): Moved to before machine_reorg. + * cfgrtl.c (rtl_delete_block, rtl_merge_blocks, + cfg_layout_merge_blocks): Changed df_delete_basic_blocks to + df_bb_delete. + (update_bb_for_insn): Changed df_insn_rescan to df_insn_change_bb. + (force_nonfallthru_and_redirect): Added call to + df_mark_solutions_dirty. + * dce.c (dce_process_block): Added more checking code and made the + code closer to the functionality in df_lr. + (fast_dce): Now skips entry and exit blocks. Deleted last parm to + df_analyze_problem and removed changed bitmap. + * fwprop.c (update_df, try_fwprop_subst): Added calls to df_set_bb_dirty. + * cfg.c (connect_src, connect_dest, disconnect_src, + disconnect_dest): Added calls to df_mark_solutions_dirty. + * haifa-sched.c (move_succs): Ditto. + * ifcvt.c (find_cond_trap): Moved calls to df_set_bb_dirty to + before deletion of basic block. + * emit-rtl.c (set_insn_deleted, remove_insn): Added basic block + parm. + * regclass.c (regclass_init): Added reset parm to df_compute_res_ever_live. + + +2006-12-28 Kenneth Zadeck <zadeck@naturalbridge.com> + * local_alloc.c (rest_of_handle_local_alloc): changed + extra_warnings to warn_clobbered. + +2006-12-25 Kenneth Zadeck <zadeck@naturalbridge.com> + * dce.c (delete_corresponding_reg_eq_notes): Added comment. + (delete_unmarked_insns): Added code to delete noop moves + inside of libcalls. Changed to used delete_insn_and_edges. + +2006-12-22 Andrew Pinski <andrew_pinski@playstation.sony.com> + + * config/spu/spu.c (immediate_load_p): Change usage of + flow2_completed over to epilogue_completed. + +2006-12-22 Kenneth Zadeck <zadeck@naturalbridge.com> + + * df-problems.c (df_ru_start_dump, df_ru_top_dump, + df_ru_bottom_dump, df_rd_start_dump, df_rd_top_dump, + df_rd_bottom_dump, df_lr_top_dump, df_lr_bottom_dump, + df_ur_top_dump, df_ur_bottom_dump, df_live_top_dump, + df_urec_top_dump, df_urec_bottom_dump, df_chain_start_dump, + df_ri_start_dump): Added ";; " to the beginning of dataflow + information put in dump files. + * dce.c (marked_libcalls, delete_unmarked_insns): Removed. + (prescan_insns_for_dce, mark_libcall, dce_process_block, + prescan_insns_for_dse): Replaced libcall marking mechanism. + (init_dce, end_dce, end_fast_dce, run_fast_df_dce): Removed + marked_libcalls. + +2006-12-16 Kenneth Zadeck <zadeck@naturalbridge.com> + + * regrename.c (mrege_overlapping_regs): Removed df parameter + and changed calls to df_ routines to support new incremental + scanning. + * sched_ebb (schedule_ebbs): Removed return value and changed + calls to df_ routines to support new incremental scanning. + * fwprop.c (local_ref_killed_between_p, use_killed_between, + all_uses_available_at, update_df, try_fwprop_subst, fwprop, + fwprop_addr): Removed df parameter and changed calls to df_ + routines to support new incremental scanning. + (gate_fwprop, gate_fwprop_addr): Reenabled pass. + * doc/cfg.texi: Updated liveness info documentation. + * doc/rtl.texi: Updated liveness info documentation. + * see.c (see_initialize_data_structures, see_emit_use_extension, + see_commit_changes, see_analyze_merged_def_local_prop, + see_analyze_use_local_prop, see_set_prop_merged_def, + see_set_prop_unmerged_use, see_store_reference_and_extension, + see_handle_relevant_defs, see_handle_relevant_uses, + see_update_uses_relevancy, see_propagate_extensions_to_uses, + pass_see): Removed df parameter and changed calls to df_ routines + to support new incremental scanning. + * postreload.c (reload_cse_simplify_operands): Changed REGNO to + SET_REGNO. + (reload_combine): Removed df parameter and changed calls to df_ + routines to support new incremental scanning. + * tree.h (generate_setjmp_warnings): Removed df parameter. + * reload.c (push_reload, find_dummy_reload): Removed df parameter + and changed calls to df_ routines to support new incremental + scanning. + * tree-pass.h (pass_df_initialize, pass_df_finish): New passes. + * rtlanal.c (remove_note): Call df_notes_rescan if the + REG_EQUAL/EQUIV notes change. + * ddg.c (add_deps_for_def, add_deps_for_use, + add_inter_loop_mem_dep): Removed df parameter and changed calls to + df_ routines to support new incremental scanning. + * ddg.h (struct df) Removed. + * final.c (cleanup_subreg_operands): Added call df_insn_rescan if + insn changes. + (walk_alter_subreg): Added changed parameter to track changes. + (output_address): Added changed parameter to walk_alter_subreg. + * cfg.c (compact_blocks, dump_bb_info): Removed df parameter to df_ + calls. + * auto_inc_dec.c (attempt_changed): Moved call to + df_recompute_luids so that it is only called when moves are added. + (find_inc, merge_in_block, rest_of_handle_auto_inc_dec): Removed + df parameter and changed calls to df_ routines to support new + incremental scanning. + (merge_in_block): Added call to df_recompute_luids. + * reorg.c (delete_from_delay_slot, relax_delay_slots): Added basic + block parm to add_insn_after. + (fill_simple_delay_slots, fill_slots_from_thread, + fill_eager_delay_slots, make_return_insns, dbr_schedule): Removed + df parameter and changed calls to df_ routines to support new + incremental scanning. + * df-scan.c (struct df_reg_chains): Removed. + (df_scan_free_internal, df_scan_free_internal, + df_scan_free_bb_info, df_scan_alloc, df_scan_free, + df_scan_start_dump, df_scan_add_problem, df_grow_reg_info, + df_check_and_grow_ref_info, df_grow_insn_info, df_scan_blocks, + df_ref_create, df_scan_alloc, df_scan_start_block, + df_scan_add_problem, df_grow_reg_info, df_check_and_grow_ref_info, + df_grow_insn_info, df_scan_blocks, df_ref_create, + df_get_artificial_uses, df_reg_chain_create, df_reg_chain_unlink, + df_ref_remove, df_insn_create_insn_record, + df_ref_chain_delete_du_chain, df_ref_chain_delete, df_insn_delete, + df_bb_delete, df_insn_rescan, df_reorganize_refs, + df_insn_change_bb, df_maybe_reorganize_use_refs, + df_maybe_reorganize_def_refs, df_reg_chain_find_ref, + df_ref_find_chains, df_ref_add_to_chains, df_refs_add_to_chains, + df_ref_create_structure, df_ref_record, df_def_record_1, + df_defs_record, df_uses_record, df_get_conditional_uses, + df_get_call_refs, df_get_call_refs, df_insn_refs_collect, + df_insn_refs_record, df_recompute_luids, df_bb_refs_collect, + df_bb_refs_record, df_bb_refs_record, df_mark_reg, + df_get_entry_block_def_set, df_entry_block_defs_collect, + df_record_entry_block_defs, df_update_entry_block_defs, + df_exit_block_uses_collect, df_record_exit_block_uses, + df_update_exit_block_uses, df_compute_regs_ever_live, + df_reg_chain_unmark, df_ref_chain_free, df_ref_verify, + df_ref_verify, df_insn_refs_verify, df_bb_verify, + df_exit_block_bitmap_verify, df_entry_block_bitmap_verify, + df_verify_blocks): Removed df and dflow parameters and changed + calls to df_ routines to support new incremental scanning. + (df_ref_chain_unmark): Renamed to df_ref_chain_verify_and_unmark. + (df_scan_get_bb_info, df_scan_set_bb_info): Made tolerant of + missing basic block info. + (df_insn_rescan_all, df_process_deferred_rescans, + df_ref_chain_find_ref_by_regno, df_ref_change_reg_with_loc_1, + df_ref_change_reg_with_loc, + df_mw_hardreg_chain_delete_eq_uses, df_notes_rescan, + df_update_entry_exit_and_calls, df_hard_reg_used_p, + df_mw_hardreg_find_hardreg): New function. + (df_ref_is_pointer_equal, df_bb_refs_verify): Deleted function. + * haifa_sched.c (move_insn): Removed df parameter and changed calls to + df_ routines to support new incremental scanning. + * df-core.c (df_init): Deleted function. + (df): New static instance of dataflow. + (df_add_problem, df_remove_problem, df_set_flags, df_clear_flags, + df_set_blocks, df_delete_basic_block, df_hybrid_search_forward, + df_hybrid_search_backward, df_iterative_dataflow, + df_analyze_problem, df_analyze, df_get_n_blocks, df_get_postorder, + df_mark_solutions_dirty, df_get_bb_dirty, df_set_bb_dirty, + df_compact_blocks, df_bb_replace, df_bb_regno_last_use_find, + df_bb_regno_first_def_find, df_bb_regno_last_def_find, + df_insn_regno_def_p, df_find_def, df_reg_defined, df_find_use, + df_reg_used, df_dump, df_dump_start, df_dump_top, df_dump_bottom, + df_regs_chain_dump, df_insn_uid_debug, df_insn_debug, + df_insn_debug_regno, df_regno_debug, debug_df_insn, debug_df_reg, + debug_df_defno, debug_df_useno): Removed df parameter and + changed calls to df_ routines to support new incremental scanning. + (df_finish1): Deleted function. + (df_remove_problem, df_finish_pass, rest_of_handle_df_initialize, + rest_of_handle_df_finish): New function. + (pass_df_finish, pass_df_initialize): New passes. + * mode-switching.c (optimize_mode_switching): Removed df parameter and + changed calls to df_ routines to support new incremental scanning. + * modulo-sched.c (sms_schedule): Removed df parameter and + changed calls to df_ routines to support new incremental scanning. + (add_insn_before): Added extra parameter to add_insn_before. + * caller-save.c (init_caller_save): Changed REGNO to SET_REGNO. + * cse.c (cse_main): Disabled incremental df update during this + pass. + * web.c (union_defs, replace_ref, web_main, pass_web): Removed df + parameter and changed calls to df_ routines to support new + incremental scanning. + * loop-init.c (rtl_unroll_and_peel_loops, + pass_rtl_move_loop_invariants): Removed df parameter and changed + calls to df_ routines to support new incremental scanning. + * global.c (global_alloc, global_conflicts, retry_global_alloc, + mark_elimination, build_insn_chain, rest_of_handle_global_alloc): + Removed df parameter and changed calls to df_ routines to support + new incremental scanning. + * ifcvt.c (, find_if_header, find_if_case_1, find_if_case_2, + dead_or_predicable, if_convert, pass_rtl_ifcvt, + pass_if_after_combine, pass_if_after_reload): Removed df parameter + and changed calls to df_ routines to support new incremental + scanning. + * expr.c (init_expr_once): Changed REGNO to SET_REGNO. + * recog.c (peephole2_optimize, pass_split_all_insn): Removed df + parameter and changed calls to df_ routines to support new + incremental scanning. + * regmove.c (mark_flags_life_zones, optimize_reg_copy_2, + regmove_optimize, rest_of_handle_stack_adjustments): Removed df + parameter and changed calls to df_ routines to support new + incremental scanning. + * local-alloc.c (block_alloc, rest_of_handle_local_alloc): Removed df + parameter and changed calls to df_ routines to support new + incremental scanning. + * function.c (regno_clobbered_at_setjmp, setjmp_vars_warning, + setjmp_args_warning, generate_setjmp_warnings, + keep_stack_depressed, thread_prologue_and_epilogue_insns, + epilogue_done, rest_of_handle_thread_prologue_and_epilogue): Removed df + parameter and changed calls to df_ routines to support new + incremental scanning. + * function.h (df): Deleted variable. + * df.h (DF_RU, DF_RD, DF_LR, DF_UR, DF_LIVE): Renumbered to put + permanent problems before optional problems. + (DF_FIRST_OPTIONAL_PROBLEM): New symbol. + (df_ref_flags.DF_REF_REF_MARKER, df_ref_flags.DF_REF_REG_MARKER): New flag. + (df_ref_flags.DF_REF_ARTIFICIAL, df_ref_flags.DF_REF_MARKER): + Deleted flag. + (df_alloc_function, df_reset_function, df_free_bb_function, + df_local_compute_function, df_init_function, df_dataflow_function, + df_confluence_function_0, df_confluence_function_n, + df_transfer_function, df_finalizer_function, df_free_function, + df_remove_problem_function, df_dump_problem_function, + df_dump_bb_problem_function, DF_SCAN_BB_INFO, DF_RU_BB_INFO, + DF_RD_BB_INFO, DF_UR_BB_INFO, DF_UREC_BB_INFO, DF_LIVE_BB_INFO, + DF_LIVE_IN, DF_LIVE_OUT, DF_RA_LIVE_IN, DF_RA_LIVE_OUT, + DF_RA_LIVE_TOP, DF_LR_IN, DF_LR_OUT, DF_UR_IN, DF_UR_OUT, + DF_DEFS_GET, DF_DEFS_SET, DF_DEFS_COUNT, DF_DEFS_BEGIN, + DF_USES_GET, DF_USES_SET, DF_USES_COUNT, DF_USES_BEGIN, + DF_REG_SIZE, DF_REG_DEF_GET, DF_REG_DEF_CHAIN, DF_REG_DEF_COUNT, + DF_REG_USE_GET, DF_REG_USE_CHAIN, DF_REG_USE_COUNT, + DF_REG_EQ_USE_GET, DF_REG_EQ_USE_CHAIN, DF_REG_EQ_USE_COUNT, + DF_REGNO_FIRST_DEF, DF_REGNO_LAST_USE, DF_REGNO_FIRST_DEF, + DF_REGNO_LAST_USE, DF_INSN_SIZE, DF_INSN_GET, DF_INSN_SET, + DF_INSN_CONTAINS_ASM, DF_INSN_LUID, DF_INSN_DEFS, DF_INSN_USES, + DF_INSN_EQ_USES, DF_INSN_UID_GET, DF_INSN_UID_SAFE_GET, + DF_INSN_UID_LUID, DF_INSN_UID_DEFS, DF_INSN_UID_USES, + DF_INSN_UID_EQ_USES, DF_INSN_UID_MWS): Removed df or dflow + parameter and changed calls to df_ routines to support new + incremental scanning. + (DF_DEFS_SIZE, DF_USES_SIZE): Renamed to DF_DEFS_TABLE_SIZE and + DF_USES_TABLE_SIZE. + (DF_DEFS_TOTAL_SIZE, DF_USES_TOTAL_SIZE, df_scan, df_ru, df_rd, + df_lr, df_ur, df_live, df_urec, df_chain, df_ri, + DF_DEFS_TOTAL_SIZE, DF_USES_TOTAL_SIZE): New macros. + (dataflow.df): Removed field. + (df_ref_info.bitmap_size): Split into df_ref_info.table_size and + df_ref_info.total_size. + (dataflow.local_flags, df_insn_info.insn, + df_changeable_flags.DF_DEFER_INSN_RESCAN, df_ref_info.count, + df.insns_to_rescan, df.insns_to_delete): New field. + (df_permanent_flags): Split into df_chain_flags and df_ri_flags. + * gcse (try_replace_reg, adjust_libcall_notes, + update_ld_motion_stores): Removed df + parameter and changed calls to df_ routines to support new + incremental scanning. + (insert_insn_end_basic_block, insert_insn_start_basic_block): + Added bb parameter to emit_insn_before_noloc. + * rtl-factoring.c (match_seqs, collect_pattern_seqs, + collect_pattern_seqs, clear_regs_live_in_seq, + recompute_gain_for_pattern_seq,, recompute_gain, + split_blocks_after_seqs, split_pattern_seq, erase_matching_seqs, + abstract_best_seq, rtl_seqabstr): Removed df parameter and changed + calls to df_ routines to support new incremental scanning. + * expmed.c (init_expmed): Changed REGNO to SET_REGNO. + * bt-load.c (, compute_defs_uses_and_gen, build_btr_def_use_webs, + migrate_btr_defs, branch_target_load_optimize, + pass_branch_target_load_optimize1, + pass_branch_target_load_optimize1): Removed df parameter and changed + calls to df_ routines to support new incremental scanning. + * emit-rtl.c (add_insn_after, add_insn_before, + emit_insn_before_noloc, emit_insn_after_1, + emit_insn_after_noloc): Added basic block parameter and threaded + it to subcalls. + (emit_jump_insn_before_noloc, + emit_call_insn_before_noloc, emit_barrier_before, + emit_label_before, emit_note_before, emit_call_insn_after_noloc, + emit_jump_insn_after_noloc, emit_label_after, emit_note_after, + emit_insn_after_setloc, emit_insn_before_setloc): Add NULL basic + block parameter to certain subcalls. + (set_unique_reg_note): Now calls df_notes_rescan if REG_EQUAL or + REG_EQUIV notes change. + * loop-invariant.c (check_invariant_table_size, + hash_invariant_expr_1, invariant_expr_equal_p, find_defs, + check_dependencies, record_uses, find_invariants_to_move, + move_invariant_reg, fail, free_inv_motion_data, + move_loop_invariants): Removed df parameter and changed + calls to df_ routines to support new incremental scanning. + * subregs_init (initialize_uninitialized_subregs): Removed df + parameter and changed calls to df_ routines to support new + incremental scanning. + * loop-iv.c (iv_current_loop_df): Deleted function. + (check_iv_ref_table_size, clear_iv_info, iv_analysis_loop_init, + latch_dominating_def, iv_get_reaching_def, iv_get_reaching_def, + iv_analyze, iv_analyze_result, biv_p, iv_analysis_done): Removed df + parameter and changed calls to df_ routines to support new + incremental scanning. + * regclass.c (regclass_init): Insert call to recalculate the + effects of changing regs_ever_live. + (init_reg_autoinc): Changed REGNO to SET_REGNO. + * rtl.h (REGNO): Changed so that it cannot appear on lhs. + (SET_REGNO): New macro. + (rhs_regno): New function. + (basic_block): New forward declaration. + * integrate.c (allocate_initial_values): Changed REGNO to + SET_REGNO and removed df parameter and changed calls to df_ + routines to support new incremental scanning. + * combine.c (set_nonzero_bits_and_sign_copies, subst, + reg_nonzero_bits_for_combine, reg_num_sign_bit_copies_for_combine, + get_last_value_validate, get_last_value, reg_dead_at_p, + create_log_links, create_log_links, rest_of_handle_combine, + pass_combine): Removed df parameter and changed calls to df_ + routines to support new incremental scanning. + * stack-ptr-mod.c (notice_stack_pointer_modification): Removed df + parameter and changed calls to df_ routines to support new + incremental scanning. + * resource.c (mark_target_live_regs): Removed df + parameter and changed calls to df_ routines to support new + incremental scanning. + * resource.h (mark_target_live_regs): Removed df + parameter and changed calls to df_ routines to support new + incremental scanning. + * cfgloop.h (iv_current_loop_df): Removed. + * df-problems.c (df_get_live_in, df_get_live_out, + df_ru_get_bb_info, df_ru_set_bb_info, df_ru_free_bb_info, + df_ru_alloc, df_ru_bb_local_compute_process_def, + df_ru_bb_local_compute_process_use, df_ru_bb_local_compute, + df_ru_local_compute, df_ru_init_solution, df_ru_confluence_n, + df_ru_transfer_function, df_ru_free, df_ru_start_dump, + df_ru_top_dump, df_ru_bottom_dump, df_ru_add_problem, + df_rd_get_bb_info, df_rd_set_bb_info, df_rd_free_bb_info, + df_rd_alloc, df_rd_bb_local_compute_process_def, + df_rd_bb_local_compute, df_rd_local_compute, df_rd_init_solution, + df_rd_confluence_n, df_rd_transfer_function, df_rd_free, + df_rd_start_dump, df_rd_top_dump, df_rd_bottom_dump, + df_rd_add_problem, df_lr_get_bb_info, df_lr_set_bb_info, + df_lr_free_bb_info, df_lr_alloc, df_lr_bb_local_compute, + df_lr_local_compute, df_lr_init, df_lr_confluence_0, + df_lr_confluence_n, df_lr_transfer_function, df_lr_local_finalize, + df_lr_free, df_lr_simulate_artificial_refs_at_end, + df_lr_simulate_one_insn, df_lr_top_dump, df_lr_bottom_dump, + df_lr_add_problem, df_ur_get_bb_info, df_ur_set_bb_info, + df_ur_free_bb_info, df_ur_alloc, df_ur_bb_local_compute, + df_ur_local_compute, df_ur_init, df_ur_confluence_n, + df_ur_transfer_function, df_ur_free, df_ur_top_dump, + df_ur_bottom_dump, df_ur_add_problem, df_live_get_bb_info, + df_live_set_bb_info, df_live_free_bb_info, df_live_alloc, + df_live_local_finalize, df_live_free, df_live_top_dump, + df_live_add_problem, df_urec_get_bb_info, df_urec_set_bb_info, + df_urec_free_bb_info, df_urec_alloc, df_urec_bb_local_compute, + df_urec_local_compute, df_urec_init, df_urec_local_finalize, + df_urec_confluence_n, df_urec_transfer_function, df_urec_free, + df_urec_top_dump, df_urec_bottom_dump, df_urec_add_problem, + df_chain_create, df_chain_unlink, df_chain_copy, + df_chain_remove_problem, df_chain_alloc, df_chain_reset, + df_chain_create_bb_process_use, df_chain_create_bb, + df_chain_finalize, df_chain_free, df_chain_start_dump, + df_chain_add_problem, df_ri_alloc, df_kill_notes, + df_set_dead_notes_for_mw, df_set_unused_notes_for_mw, + df_create_unused_note, df_ri_bb_compute, df_ri_compute, + df_ri_free, df_ri_start_dump, df_ri_add_problem, + df_ri_get_setjmp_crosses): Removed df and dflow parameters and + changed calls to df_ routines to support new incremental scanning. + (df_chain_unlink_1, df_chain_fully_remove_problem): New function. + * reg-stack.c (reg_to_stack): Removed df parameter and changed + calls to df_ routines to support new incremental scanning. + * Makefile.in (rtlanal.o, expr.o, expmed.o, cse.o, gcse.o, + regclass.o, caller-save.o, stack-ptr-mod.o, final.o): Added df.h. + (reorg.o): Deleted df.h. + * sched-rgn.c (check_live_1, update_live_1, schedule_insns): + Removed df parameter and changed calls to df_ routines to support + new incremental scanning. + * basic-block.h (forward for basic_block): Moved to rtl.h. + * passes.c (pass_df_initialize, pass_df_finish): New passes. + (execute_todo): Changed TODO_df_finish. + * struct-equiv.c (insns_match_p, struct_equiv_init): Removed df + parameter and changed calls to df_ routines to support new + incremental scanning. + * config/frv/frv.c (frv_function_prologue, frv_int_to_acc): + Changed REGNO to SET_REGNO. + (frv_reorder_packet): Added null basic block parm to + add_insn_before. + * config/i386/i386.c (ix86_eax_live_at_start_p): Removed df + parameter and changed calls to df_ routines to support new + incremental scanning. + (ix86_expand_prologue, ix86_output_function_epilogue): + Changed REGNO to SET_REGNO. + * config/ia64/ia64.c (emit_predicate_relation_info, ia64_reorg): + Removed df parameter and changed calls to df_ routines to support + new incremental scanning. + * config/mips/mips.c (mips_expand_prologue): Changed REGNO to + SET_REGNO. + * cfgrtl.c (commit_one_edge_insertion, rtl_dump_bb, + print_rtl_with_bb, cfg_layout_merge_blocks, + insert_insn_end_bb_new): Added bb parameter to insn insert calls. + * dce.c (prescan_insns_for_dce): Removed fast parameter. + (init_dce, end_dce, mark_artificial_uses, mark_reg_dependencies, + end_fast_dce, dce_process_block, fast_dce, run_fast_df_dce, + rs_init, rs_confluence, rs_transfer_function, dump_stores, + record_store, mark_dependent_stores, prescan_insns_for_dse, + rest_of_handle_dse): Removed df parameter and changed calls to df_ + routines to support new incremental scanning. + (rest_of_handle_dce, gate_dce, run_dce, pass_rtl_dce): Deleted. + (delete_corresponding_reg_equal_notes): Renamed to + delete_corresponding_reg_eq_notes and made to process both kinds + of notes. + (delete_unmarked_insns): Changed call to + delete_corresponding_reg_eq_notes. + * dce.h (run_fast_df_dce): Removed df parameter. + * reload1.c (compute_use_by_pseudos, reload): Removed df parameter + and changed calls to df_ routines to support new incremental + scanning. + (alter_reg): Changed REGNO to SET_REGNO. + + +2006-12-07 David Edelsohn <edelsohn@gnu.org> + + * config/rs6000/predicates.md (lwa_operand): Allow PRE_MODIFY + indexed address. + * config/rs6000/rs6000-protos.h (legitimate_indexed_address_p): + Declare. + * config/rs6000/rs6000.c (legitimate_indexed_address_p): Remove + static. + +2006-12-07 David Edelsohn <edelsohn@gnu.org> + + * config/rs6000/predicates.md (lwa_operand): Exclude PRE_MODIFY + addresses. + +2006-12-01 Seongbae Park <seongbae.park@gmail.com> + + * dbgcnt.def: New counters + * postreload-gcse.c (delete_redundant_insns_1): New + debug counter point. + * postreload.c (reload_cse_move2add, gate_handle_postreload): + New debug counter point. + * auto-inc-dec.c (move_notes): Removed. + (move_dead_notes): New function. + (attempt_change): Call move_dead_notes(). Add missing dependency check. + * haifa-sched.c (schedule_block): New debug counter point. + * cse.c (delete_trivially_dead_insns): New debug counter point. + * gcse.c (pre_delete): New debug counter point. + * Makefile.in: Adding dependency on DBGCNT_H. + * sched-rgn.c (schedule_region): New debug counter point. + * dce.c (delete_corresponding_reg_equal_notes): New function + (delete_unmarked_insns): Call delete_corresponding_reg_equal_notes. + + +2006-11-28 Seongbae Park <seongbae.park@gmail.com> + + * dbgcnt.def: Updated comments. Added new counters. + * auto-inc-dec.c (try_merge): Debug counter. + * global.c (global_conflicts): Use DF_RA_LIVE_TOP() instead of + df_urec_get_live_at_top(). + * dbgcnt.c (count, limit): Use dbgcnt.def. + (dbg_cnt_is_enabled): New function. + (dbg_cnt): Use dbg_cnt_is_enabled(). + * local-alloc.c (block_alloc): Use DF_RA_LIVE_TOP instead of LIVE_IN. + Add a debug counter. + * df.h (DF_RA_LIVE_TOP): New Macro. Remove df_urec_get_live_at_top. + (struct df_rd_bb_info): Added new fields ADEF, AUSE and TOP. + (struct df_urec_bb_info): Added new field TOP. + * loop-iv.c (iv_analyze_def): Added REG_P check. + * df-problems.c (df_lr_free_bb_info): Free new fields AUSE, ADEF and + TOP. + (df_lr_alloc): Allocate new fields TOP, ADEF, AUSE. + (df_lr_bb_local_compute): Compute ADEF and AUSE. + (df_lr_transfer_function): Compute TOP, and then IN based on TOP, ADEF + and AUSE. + (df_lr_free): Free new fields AUSE, ADEF and TOP. + (df_ur_bb_local_compute): Partial/conditional defs don't hide + earlier defs. + (df_urec_alloc): Allocate new field TOP. + (df_urec_local_finalize): Calculate TOP as well as IN. + (df_urec_free): Free new field TOP. + (df_urec_get_live_at_top): Removed. + * Makefile.in (auto-inc-dec.o, local-alloc.o, sched-rgn.o, + sched-ebb.o, recog.o): Added dependency on DBGCNT_H. + * sched-rgn.c (schedule_insns, gate_handle_sched, gate_handle_sched2): + Added debug counter. + * config/ia64/ia64.c (enum ia64_frame_regs): New enum. + (struct ia64_frame_info): Changed register fields to use an array + index by enum. + (emitted_frame_related_regs): New static variable. + (reg_emitted, get_reg, is_emitted): New function. + (ia64_reload_gp): Use new enum. + (find_gr_spill): Added a new param. Use emitted_frame_related_regs. + (ia64_computea_frame_size, ia64_hard_regno_rename_ok, + ia64_epilogue_uses): + Use emitted_frame_related_regs. + (ia64_expand_prologue): Extra deubg output. Use new enum. + (ia64_expand_epilogue, ia64_direct_return, ia64_split_return_addr_rtx, + ia64_output_function_prologue, ia64_output_function_epilogue, + process_set): + Use new enum. + (ia64_init_expanders): New function. + (ia64_reorg): New debug counter. + * config/ia64/ia64.h (INIT_EXPANDERS): New call to + ia64_init_expanders. + + + +2006-11-18 Kenneth Zadeck <zadeck@naturalbridge.com> + + * tree-pass.h (print_current_pass, debug_pass): New functions. + * passes.c (print_current_pass, debug_pass): New functions. + (exec_todo): Changed call to df_verify_blocks. + (current_pass): New variable. + * auto-inc-dec.c (attempt_change): Removed redundant call to + df_insn_rescan and added debugging. + (find_inc): Added debugging and fixed post-inc/dec bug. + (merge_in_block): Fixed post-inc/dec bug. + * dbgcnt.c: Fixed comments. + * dbgcnt.c: Fixed comments. + * loop-init.c (rtl_unroll_and_peel_loops): Added debugging. + * loop-invariant.c (find_defs): Added debugging. + * loop-iv.c (iv_analysis_loop_init): Added debugging. + * df-scan.c (df_ref): Added parameter to control the deletion of + chains. + (df_reg_chain_unlink): Take care not to delete non existent refs. + (df_ref_remove): Changed call to df_reg_chain_unlink. + (df_ref_chain_delete_du_chain, df_ref_chain_delete, + df_mw_hardreg_chain_delete): New function + (df_insn_delete): Now calls df_ref_chain_delete_du_chain, + df_ref_chain_delete, df_mw_hardreg_chain_delete. + (df_bb_delete): Now calls df_ref_chain_delete_du_chain, + df_ref_chain_delete. + (df_insn_rescan): Now supports DF_NO_INSN_RESCAN and added + debugging. + (df_ref_verify): Added code to ignore refs from notes in the table + of refs. + (df_bb_verify, df_exit_block_bitmap_verify, + df_entry_block_bitmap_verify, df_verify_blocks): Made it possible + to abort closer to source of problem. + (df_verify_blocks): Removed dflow parameter. * df-core.c + (df_insn_uid_debug, df_ref_debug): Fixed debugging. * + df-problems.c (df_ru_bb_info, df_rd_bb_info, df_lr_bb_info, + df_ur_bb_info, df_live_bb_info, df_urec_bb_info): Made tolerant of + newly added basic blocks. + (df_chain_alloc): Tolerant of missing refs in structure. + (df_chain_insn_reset, df_chain_bb_reset, df_chain_remove_problem): + Deleted function. + (df_chain_alloc): New function. + (df_chain_reset): Now just calls df_chain_remove_problem. + * df.h: (DF_NO_REGS_EVER_LIVE): New flag. + +2006-11-17 Kenneth Zadeck <zadeck@naturalbridge.com> + + * fwprop.c Removed bad version. + * tree-ssa-pre.c (init_pre): Added parameter to post_order_compute. + +2006-11-15 Seongbae Park <seongbae.park@gmail.com> + + * df-scan.c (df_insn_refs_collect): Code cleanup + for COND_EXEC handling. + (df_need_static_chain_reg): New function. + (df_get_entry_block_def_set): Check and add the static chain register. + * ifcvt.c (if_convert): Don't ignore the return value + of find_if_header. + + +2006-11-14 Seongbae Park <seongbae.park@gmail.com> + + * function.c (thread_prologue_and_epilogue_insns): + Update regs_ever_live during df scanning. + +2006-11-14 Seongbae Park <seongbae.park@gmail.com> + + * dbgcnt.c, dbgcnt.h: Added missing copyright notice. Now uses + dbgcnt.def for the list of counters. + * Makefile.in: Add missing dbgcnt.h header file dependencies. + * dbgcnt.def: New file. + +2006-11-14 Seongbae Park <seongbae.park@gmail.com> + + * tree-tailcall.c (execute_tail_recursion): Added dbg_cnt(). + * df-scan.c (df_mw_hardreg_find_hardreg, df_get_conditional_uses, + df_get_call_refs): + New function. + (df_refs_add_to_chains): Don't add duplicate mw_hardreg. + (df_ins_refs_collect): Refactored to use df_get_conditional_uses + and df_get_call_refs. + (df_insn_refs_verify): Find the matching mw_hardreg. + * dbgcnt.c (dbg_cnt_process_opt): Fix a bug handling multiple + counters. Add a new debug counter tail_call. + * dbgcnt.h (enum debug_counter): Added a new counter tail_call. + * calls.c (expand_call): Check dbg_cnt(tail_call). + * df_problems.c (df_create_unused_note, df_ri_bb_compute): + Handle NULL LOC case. + * dce.c (init_dce): Add a debug dump. + + +2006-11-14 Paolo Bonzini <bonzini@gnu.org> + + Merge from mainline: + + 2006-11-14 Paolo Bonzini <bonzini@gnu.org> + + PR rtl-optimization/29798 + + * fwprop.c (use_killed_between): Check that DEF_INSN dominates + TARGET_INSN before any other check. + (fwprop_init): Always calculate dominators. + (fwprop_done): Always free them. + +2006-11-13 Seongbae Park <seongbae.park@gmail.com> + Kenneth Zadeck <zadeck@naturalbridge.com> + + * tree-pass.h (TODO_df_verify_scan): New flag. + * auto-inc-def.c (find_inc): Added code to check for case where + output of inc is not overwritten by mem to be combined. + * df-scan.c (df_scan_set_bb_info): Now increases the size of the + array if necessary. + (df_scan_blocks): Added flag to avoid recomputing regs_ever_live. + (df_insn_rescan): Fixed missing case for rescanning. + (df_insn_refs_collect): Special case for sibling calls. Changed + to not set up loc field with address of regno_reg_rtx element. + (df_entry_block_defs_collect, df_exit_block_uses_collect, + df_bb_refs_collect): Changed to not set up loc field with address + of regno_reg_rtx element. + (df_bb_refs_record): Removed dflow parameter and added scan_insn + parameter. Changed call to df_bb_refs_record. + (df_compute_regs_ever_live): New function. + (df_reg_chain_unmark): Made to accept NULL blocks parameter. + (df_ref_chain_unmark): New function. + (df_ref_verify): Added abort_if_fail parameter. + (df_insn_refs_verify): Added abort_if_fail and return_refs + parameter and changed function to return bool. Added code to + unmark chains. + (df_bb_refs_verify): Added abort_if_fail parameter and now returns + nothing. + (df_bb_verify): Changed to expect subroutines to abort. + (df_verify_blocks): Now accepts NULL blocks parameter. + * ifcvt.c (if_convert): Added more to dump_files. + * (pass_rtl_ifcvt, pass_if_after_combine, pass_if_after_reload): + added TODO_df_verify_scan. + * opts.c (command_handle_option): New flag OPT_fdbg_cnt_ + * recog.c (confirm_change_group): Fixed updating of df scanning. + * function.c (thread_prologue_and_epilogue_insns): + Pass DF_NO_REGS_EVER_LIVE in df_init, and + call df_compute_regs_ever_live after prologue is generated. + * df.h (enum df_ref_flags): Added DF_REF_ARTIFICIAL. + (enum df_changeable_flags): Added DF_NO_REGS_EVER_LIVE. + (DF_REF_IS_ARTIFICIAL): Added checking for the flag DF_REF_ARTIFICIAL. + * cfgcleanup.c: Fixed typo in comment. + * common.opt: New flag -fdbg-cnt= + * combine.c (try_combine): Added calls to keep df_scanning up to + date. + (pass_combine): Added TODO_df_verify_scan. + * Makefile.in: New gcc object file dbgcnt.o + * passes.c (execute_todo): Added support for TODO_df_verify_scan. + * config/ia64/ia64.c (ia64_split_return_addr_rtx): Finalize + the frame layout early. + (ia64_expand_prologue): Pass DF_NO_REGS_EVER_LIVE to df_init(). + * cfgrtl.c (rtl_delete_block, rtl_merge_blocks, + cfg_layout_merge_blocks): Added debugging. + * dbgcnt.c, dbgcnt.h: New files + * dce.c (delete_unmarked_insns): Use dbg_cnt. + (delete_insn_p): Do not delete unspecs. + +2006-11-10 Seongbae Park <seongbae.park@gmail.com> + + * fwprop.c (forward_propagate_into): Use DF_REF_IS_ARTIFICIAL() + instead of DF_REF_ARTIFICIAL flag. + * df-scan.c (df_ref_record, df_def_record_1): Remove record_live + (df_get_exit_block_use_set, df_get_entry_block_def_set): + Renamed from df_get_{exit_block_uses,entry_block_defs}. + (df_compute_regs_ever_live, df_ref_chain_change_bb, + df_ref_is_record_live, df_reg_chain_unmark, df_ref_chain_free): + New functions. + (df_reg_chains): New structure + (df_ref_add_to_chains): + (df_scan_blocks): Add call to df_compute_regs_ever_live(). + (df_ref_create): Remove record_live, and replace DF_REF_ARTIFICIAL + flag use with DF_REF_IS_ARTIFICIAL () macro. + (df_insn_rescan): Avoid marking blocks dirty if nothing changed. + (df_insn_change_bb): Refactored to use df_ref_change_bb. + (df_ref_is_equal): Now ignores DF_REF_MARKER flag. + (df_ref_chain_find_ref, df_reg_chain_find_reg): Fix the order of + parameters to the evaluation function. + (df_ref_find_chains): Avoid early evaluation of certain fields. + (df_ref_add_to_chains): Remove update to regs_ever_live. + (df_refs_add_to_chains): Use DF_REF_NEXT_REF macro. + (df_ref_record): Remove DF_REF_RECORD_LIVE. + (df_insn_refs_record): Now takes the reference list to be added as + an argument. + (df_bb_refs_record): Handles entry and exit block cases. + (df_bb_refs_collect): Removed DF_REF_ARTIFICIAL. + (df_refs_record): Scan entry and exit blocks first. + (df_ref_verify): Takes reg_chain cache as an argument, + verifies hardreg chain. + (df_exit_block_bitmap_verify): Renamed from df_exit_block_verify + (df_entry-block_bitmap_verify): Renamed from df_entry_block_verify + (df_verify_blocks): Implement regchain cache. Assert immediately + when verification fails in any subfunctions. + * df.h (enum df_ref_flags): Remove DF_REF_ARTIFICIAL and renumber the + enum. + (DF_REF_IS_ARTIFICIAL, DF_REF_MARK, DF_REF_UNMARK, DF_REF_IS_MARKED, + DF_INSN_UID_SAFE_GET): New macros: + + +2006-11-08 Kenneth Zadeck <zadeck@naturalbridge.com> + + * auto-inc-dec.c (attempt_change): Added code to properly maintain + reg_next_inc_use when inc insns are deleted. + (merge_in_block): Added debugging and changed to skip deleted + instructions. + (scan_dflow): Removed variable. + * df-scan.c (df_scan_alloc): Always processes entire function. + (df_scan_free): Removed df->blocks_to_scan. + (df_scan_blocks): Removed blocks_parameter. Now scan entire + function. + (df_insn_rescan, df_insn_change_bb): Renamed df_mark_bb_dirty to + df_set_bb_dirty. + * df.h (blocks_to_scan): Removed variable. + * df-core.c (df_analyze): Add call to df_scan_alloc. Removed + df->blocks_to_scan. + (df_get_bb_dirty): New function. + (df_mark_bb_dirty): Renamed to df_set_bb_dirty. + (df_compact_blocks): Removed df->blocks_to_scan. + (df_bb_replace): Added check to make sure there was no block at + old_index and fixed updating bugs. + * ifcvt.c (cond_exec_process_if_block, + noce_process_if_block, cond_move_process_if_block, + process_if_block, merge_if_block, find_if_header, + find_cond_trap, find_if_case_1, find_if_case_2, if_convert): + Removed modified variable. + (find_if_header, find_cond_trap, find_if_case_1, find_if_case_2, + if_convert): Replaced BB_DIRTY with df_set_bb_dirty and + df_get_bb_dirty. + * recog.c (confirm_change_group): Ditto. + * emit_rtl (remove_insn,, reorder_insns, emit_insn_after_1): Ditto. + * cfgcleanup.c (try_forward_edges, try_crossjump_to_edge, + merge_blocks_move_predecessor_nojumps, try_crossjump_bb): Ditto. + * modulo-sched.c (sms_schedule): Ditto. + * cfgrtl.c (rtl_split_block, rtl_redirect_edge_and_branch, + rtl_redirect_edge_and_branch_force, purge_dead_edges, + cfg_layout_redirect_edge_and_branch): Ditto. + * basic_block.h (BB_DIRTY): Removed. + + +2006-11-07 Kenneth Zadeck <zadeck@naturalbridge.com> + + * cfg.c (compact_blocks): Make df aware when blocks are moved around. + * auto-inc-dec.c (attempt_change): Removed explicit df updating. + * ifcvt.c (cond_exec_process_if_block, + noce_mem_write_may_trap_or_fault_p, noce_process_if_block, + cond_move_process_if_block, process_if_block, find_if_header): + Removed unused df parameter. + (merge_if_block, find_cond_trap, find_if_case_1, find_if_case_2): + Removed explicit df updating. + (if_convert): Rearranged calls to df. + (struct tree_opt_pass pass_rtl_ifcvt, pass_if_after_combine, + pass_if_after_reload): Added TODO_verify_flow. + * recog.c (delete_insn_chain_and_flow): Deleted function. + (peephole2_optimize): Removed unused dataflow problem and variable + and delete explicit df updating calls. + (pass_split_before_sched2): Added TODO_verify_flow. + * emit_rtl (add_insn_after, add_insn_before, remove_insn, + reorder_insns, emit_insn_after_1): Added explicit updating of df. + (set_insn_deleted): New function. + * loop_invariant.c (invariant_table_size, invariant_table): New + variables. + (check_invariant_table_size): New function. + (invariant_for_use, find_defs, check_dependency, + find_invariant_insn, free_inv_motion_data, move_loop_invariants): + Replaced DF_REF_DATA with invariant_table. + * loop-iv.c (clean_slate, iv_ref_table_size, iv_ref_table): New + variables. + (check_iv_ref_table_size): New function. + (clear_iv_info, iv_analysis_loop_init, record_iv, iv_analyze_def, + iv_analysis_done): Replaced DF_REF_DATA with iv_ref_table. + * cfglayout.c (fixup_reorder_chain): Now uses compact_blocks. + * rtl.h (SET_INSN_DELETED): now calls set_insn_deleted. + * Makefile.in: (emit-rtl.o): Now dependent on df.h. + * sched-rgn.c (pass_sched, pass_sched2): Added TODO_verify_flow. + * cfgrtl.c (rtl_delete_block, update_bb_for_insn, + rtl_merge_blocks, try_redirect_by_replacing_jump, + cfg_layout_merge_blocks): Added explicit updating of df. + * dce.c (delete_unmarked_insns): Removed df_delete parameter and + explicit updating of df info. + (rest_of_handle_dce, rest_of_handle_dse): Added call to + df_remove_problem. + (fast_dce, fast_dce, rest_of_handle_fast_dce, run_fast_df_dce): + Removed df_delete parameter. + * df-scan.c (df_scan_free_bb_info): Changed call. + (df_scan_alloc, df_scan_free): Added setting of out_of_date_transfer_functions. + (df_problem problem_SCAN): Added problem removal function. + (df_scan_blocks): Added calls to df_refs_delete and df_bb_delete. + (df_insn_create_insn_record): Added call to df_grow_insn_info. + (df_insn_refs_delete): Renamed to df_insn_delete and removed dflow + parameter. + (df_bb_refs_delete): Renamed to df_bb_delete and removed dflow + parameter. + (df_refs_delete): Deleted. + (df_insn_rescan, df_insn_change_bb): New function. + (df_ref_create_structure): Removed DF_REF_DATA. + * df-core.c (df_add_problem): Changed to use new form of problem + dependency. + (df_remove_problem): New function. + (df_set_blocks): Does a better job of updating the proper blocks. + (df_delete_basic_block): Removed df parameter and checks to see if + block already had infomation. + (df_get_bb_info): Returns NULL if no info was there. + (df_set_bb_info): Checks to make sure problem block information. + (df_mark_solutions_dirty, df_mark_bb_dirty, df_compact_blocks, + df_bb_replace): New functions. + * df.h (df_remove_problem_function): New typedef. + (df_dependent_problem_function): Deleted typedef. + (df_problem): Added remove_problem_fun and dependent_problem and + deleted dependent_problem_fun. + (df_ref.data): Removed. + (df.out_of_date_transfer_functions, df.solutions_dirty): New + variables. + (DF_REF_DATA): Deleted macro. + * df-problems.c (problem_RU, problem_RD, problem_LR, problem_UR, + problem_LIVE, problem_UREC, problem_CHAIN, problem_RI): Added + problem removal function and changed dependent_function. + +2006-11-04 Kenneth Zadeck <zadeck@naturalbridge.com> + + * df-scan.c (df_scan_start_dump): Changed print routine to print + hard register names. + * df-core.c (df_print_regset): New function. + * global.c (global_alloc): Do not recompute register infomation + when recomputing dataflow. + * df.h (DF_RI_NO_UPDATE): New flag. + * df-problems.c (df_lr_top_dump, df_lr_bottom_dump, + df_ur_top_dump, df_ur_bottom_dump, df_live_top_dump, + df_live_bottom_dump, df_urec_top_dump, df_urec_bottom_dump): + Changed to use df_print_regset. + (df_ri_alloc, df_ri_compute): Changed to not update if + DF_RI_NO_UPDATE flag. + * cfgrtl.c (print_rtl_with_bb): Changed to print preds and + successor blocks in dump file. + +2006-11-03 Seongbae Park <seongbae.park@gmail.com> + + * global.c (global_conflicts): Use df_urec_get_live_at_top () + instead of DF_RA_LIVE_IN (). + * function.h: Add a declaration for current_function_assembler_name(). + * function.c (current_function_assembler_name): New function. + (thread_prologue_and_epilogue_insns): Insert rtx_USE when profiling. + * df.h (DF_REF_FLAGS_CLEAR): New macro. + (df_urec_get_live_at_top): New function declaration. + * df-problems.c (df_urec_get_live_at_top): New function. + +2006-10-28 Seongbae Park <seongbae.park@gmail.com> + + * df.h (df_rescan_blocks): Renamed to df_scan_blocks. + * df-core.c (df_analyze): Use df_scan_blocks() instead. + * ifcvt.c (if_convert): Use df_scan_blocks() instead. + * df-scan.c + (df_ref_record, df_def_record_1, df_defs_record, df_uses_record): + They are made side-effect free. + (df_ref_find_chains, df_ref_add_to_chains, df_refs_add_to_chains, + df_ref_is_equal, df_ref_chain_find_ref, df_reg_chain_find_ref, + df_scan_start_block, df_check_and_grow_ref_info, + df_insn_refs_collect, df_bb_refs_collect, + df_get_entry_block_defs, df_get_exit_block_uses, + df_ref_verify, df_refs_verified, df_ref_chain_verify, + df_reg_chain_clear_verified, df_bb_refs_verify, + df_exit_block_verify, df_entry_block_verify, df_verify_blocks): + New functions. + (df_ref_create_structure): Initializes the fields + of df_ref and does not connect it to various chains. + (df_insn_refs_record, df_bb_refs_record, df_record_entry_block_defs, + df_record_exit_block_uses, df_refs_record): + Separate side-effects (adding chains and updating regs_ever_live) + from traversing and finding the refs in the insn/basic block. + (df_ref_create): Separate calls for creating the ref and add it + to the chains. + +2006-10-24 Paolo Bonzini <bonzinI@gnu.rg> + + * ddg.c (add_deps_for_def, add_deps_for_use): Use accessor macros. + * df.h (DF_REF_STRIPPED, DF_SUBREGS): Remove. + * df-core.c: Don't document scanning flags. + * df-scan.c (df_ref_record): Always behave as if DF_SUBREGS was set. + * df-problems.c (df_lr_bb_local_compute): Strip SUBREGs with + DF_REF_REAL_REG. + (df_chain_start_dump): Don't dump DF_REF_STRIPPED. + (df_create_unused_note, df_ri_bb_compute): Don't "inline" + DF_REF_REAL_LOC. + * fwprop.c (fwprop_init): Do not pass DF_SUBREGS. + * loop-invariant.c (record_use): Don't strip SUBREGs. + (record_uses): Do it here with DF_REF_REAL_LOC. + * loop-iv.c (iv_analyze_def): Only allow REGs. Replace previous + way to check for SUBREGs with an assertion. + +2006-10-19 Kenneth Zadeck <zadeck@naturalbridge.com> + + * sched-ebb.c (schedule_ebbs): Changed flags to df_init. + * fwprop.c (use_killed_between): Changed to use proper macros. + (All_uses_available_at, try_fwprop_subst): Added support for + separated reg_equiv/equal df_refs. + (fwprop_init): Changed flags to df_init. + (fwprop, fwprop_addr): Changed call to df_reorganize_refs to + df_maybe_reorganize_use_refs. + * see.c (see_initialize_data_structures): Changed flags to + df_init. + * ddg.c (build_inter_loop_deps): Now skips refs with + reg_equal/equiv notes. + * modulo-sched.c (sms_schedule): Changed flags to df_init. + * web.c (union_defs): Added support for separated reg_equiv/equal + df_refs. + (web_main): Changed flags to df_init and changed call to + df_reorganize_refs to df_maybe_reorganize_(use|def)_refs. + * loop_invariant.c (check_dependency): New function split out from + check_dependencies. + (record_uses): Added support for separated reg_equiv/equal + df_refs. + (move_loop_invariants): Changed flags to df_init. + * loop-iv.c (iv_analysis_loop_init): Changed flags to df_init. + (latch_dominating_def): Changed to use proper macros. + * combine.c (create_log_links): Ditto. + * sched-rgn.c (schedule_insns): Changed flags to df_init. + * dce.c (dce_process_block): Changed to use proper macros. + * df.h (df_insn_info.eq_uses): New field. + (DF_EQUIV_NOTES): Deleted permanent_flag. + (DF_EQ_NOTES): New changeable_flag. + (df_ref_info.regs_size, df_ref_info.regs_inited): Moved to df + structure. + (df.def_regs, df.use_regs, df.eq_use_regs): New fields. + (df_ref_info.begin): Moved from df_reg_info. + (DF_DEFS_COUNT, DF_DEFS_BEGIN, DF_DEFS_COUNT, DF_DEFS_BEGIN, + DF_REG_EQ_USE_GET, DF_REG_EQ_USE_CHAIN, DF_REG_EQ_USE_COUNT): New + macros. + (DF_REG_SIZE, DF_REG_DEF_GET, DF_REG_DEF_CHAIN, DF_REG_DEF_COUNT, + DF_REG_USE_GET, DF_REG_USE_CHAIN, DF_REG_USE_COUNT): Redefined. + (df_reorganize_refs): Split into df_maybe_reorganize_use_refs and + df_maybe_reorganize_def_refs. + (df_ref_info.refs_organized): Split into refs_organized_alone and + refs_organized_with_eq_uses. + * df-problems.c (df_ru_bb_local_compute_process_def, + df_ru_local_compute, df_ru_confluence_n, df_ru_transfer_function, + df_ru_start_dump, df_rd_bb_local_compute_process_def, + df_rd_local_compute, df_rd_confluence_n, df_rd_transfer_function, + df_rd_start_dump, df_chain_alloc, df_chain_insn_reset, + df_chain_create_bb_process_use, df_chain_create_bb, + df_chain_start_dump): Changed to use proper macros. + (df_ru_bb_local_compute, df_chain_insn_reset, df_chain_create_bb): + Added support for separated reg_equiv/equal df_refs. + (df_ru_local_compute, df_rd_local_compute, df_chain_alloc): Split + into df_maybe_reorganize_use_refs and + df_maybe_reorganize_def_refs. + * df-scan.c (df_grow_reg_info, df_rescan_blocks, df_ref_create): + Changed to process all data structures dependent on number of + registers at once. + (df_scan_free_internal, df_scan_alloc): Changed to process new + data structures properly. + (df_rescan_blocks): Split into refs_organized_alone and + refs_organized_with_eq_uses. + (df_reg_chain_unlink): Remove decrement of bitmap_size fields. + (df_reg_chain_unlink, df_insn_refs_delete, + df_ref_create_structure): Changed to use proper macros. + (df_reg_chain_unlink, df_ref_remove, df_insn_refs_delete, + df_reorganize_refs, df_ref_create_structure, df_insn_refs_record): + Added support for separated reg_equiv/equal df_refs. + (df_maybe_reorganize_use_refs, df_maybe_reorganize_def_refs): New + functions. + * df-core.c (df_bb_regno_last_use_find, + df_bb_regno_first_def_find, df_bb_regno_last_def_find, + df_insn_regno_def_p, df_find_def, df_find_use, df_dump_start, + df_regno_debug): Changed to use proper macros. + (df_find_use, df_insn_uid_debug, df_insn_uid_debug, + df_insn_debug_regno, df_insn_debug_regno): Added support for + separated reg_equiv/equal df_refs. + + +2006-10-18 Paolo Bonzini <bonzini@gnu.org> + + * stack-ptr-mod.c (pass_stack_ptr_mod): Don't set pass name. + * final.c (pass_no_new_pseudos): Don't set pass name. + + * fwprop.c (all_uses_available_at): Use DF_REF_REG. + (forward_propagate_into): Discard artificial defs/uses. + (pass_rtl_fwprop_addr): Add TODO_df_finish. + +2006-10-03 Richard Sandiford <richard@codesourcery.com> + + * cselib.h (cselib_discard_hook): Declare. + * cselib.c (cselib_discard_hook): New variable. + (remove_useless_values): Call it before freeing useless values. + * dce.c (base_address): New union. + (store_base_info): Change the type of the base field from "rtx" + to "union base_address". + (local_invariant_stores, local_value_stores): New variables. + (store_base_eq): Split into... + (invariant_store_base_eq, value_store_base_eq): ...these new functions. + (store_base_hash): Split into... + (invariant_store_base_hash, value_store_base_hash): ...these + new functions. + (store_base_del): Fix formatting. + (init_store_group): Split into... + (init_invariant_store_group, init_value_store_group): ...these + new functions. + (init_dse): Use init_invariant_store_group instead of init_store_group. + (get_canonical_address): Delete. + (add_store_offset): Change the type of BASE from "rtx" to "union + base_address *". + (record_store): Remove the GROUP parameter. Don't call + get_canonical_address. Store the base in a "union base_address" and + add stores to either local_invariant_stores or local_value_stores. + (record_stores): Remove the GROUP parameter and adjust the calls + to record_store. + (store_base_local): Add a parameter that indicates whether stores + are invariant or cselib_vals. + (invariant_store_base_local): New function. + (value_store_base_local): Likewise. + (value_store_base_useless): Likewise. + (remove_useless_values): Likewise. + (store_base_prune_needed): Adjust for store_base_info changes. + (prescan_insns_for_dse): Remove the local group variable and use + local_invariant_stores and local_value_stores instead. Adjust the + call to record_stores. Use remove_useless_values as the + cselib_discard_hook while processing a basic block. + +2006-10-03 Maxim Kuvyrkov <mkuvyrkov@ispras.ru> + + * sched-ebb.c (ebb_head, ebb_tail, ebb_head_or_leaf_p): Removed. + (begin_schedule_ready, schedule_ebb, ebb_head_or_leaf_p): Remove + unnecessary argument, update all callers. + (ebb_sched_info): Update initializer. + (df): New static variable to keep dataflow info. + (compute_jump_reg_dependencies): Use it instead of glat. + * haifa-sched.c (glat_start, glat_end, glat_size, init_glat, + init_glat1, free_glat): Removed. + (generate_recovery_code, begin_speculative_block, + add_to_speculative_block, init_before_recovery, + create_recovery_block, create_check_block_twin, + schedule_block, sched_init, add_block): Remove + unnecessary argument, update all callers. + * modulo-sched.c (sms_sched_info): Update initializer. + (sms_schedule): Update call to sched_init (). + * sched-int.h (struct sched_info): Remove unnecessary argument and + update all callers of field 'begin_schedule_ready'. Remove field + 'region_head_or_leaf_p'. + (glat_start, glat_end): Remove prototypes. + (enum SCHED_FLAGS): Remove USE_GLAT, DETACH_LIFE_INFO. Use NEW_BBS + instead. + (schedule_block, sched_init, add_block, schedule_region): Update + prototypes. + * sched-rgn.c (df, not_in_df): New static variables. + (check_live_1, update_live_1, add_block1): Use them instead of glat. + (begin_schedule_read, schedule_region): Remove unnecessary argument, + update all callers. + (region_head_or_leaf_p): Remove. + (region_sched_info): Update initializer. + * config/ia64/ia64.c (set_sched_flags): Use NEW_BBS instead of + DETACH_LIFE_INFO. + +2006-10-01 Kenneth Zadeck <zadeck@naturalbridge.com> + + * df-scan.c (df_ref_record, df_insn_refs_record, + df_bb_refs_record, df_refs_record, df_record_entry_block_defs): + Removed DF_HARD_REGS flag. + * df-core.c (comments): Ditto. + * df.h (permanent_flags.DF_HARD_REFS): Removed. + (changeable_flags.DF_NO_HARD_REGS): Added. + * df-problems.c (df_rd_bb_local_compute_process_def, + df_rd_bb_local_compute, df_chain_create_bb_process_use, + df_chain_create_bb): Added support for DF_NO_HARD_REGS flag. + * dce.c (init_dce): Removed DF_HARD_REFS flag. + * reg_stack.c (reg_to_stack): Ditto. + * sched_rgn.c (sched_insns): Ditto. + * regrename.c (regrename_optimize): Ditto. + * sched_ebb.c (schedule_ebbs): Ditto. + * fwprop.c (fwprop_init): Ditto. + * see.c (see_initialize_data_structures): Ditto. + * auto_inc_dec.c (rest_of_handle_auto_inc_dec): Ditto. + * mode-switching.c (optimize_mode_switching): Ditto. + * modulo-sched.c (sms_schedule): Ditto. + * ifcvt.c (if_convert): Ditto. + * recog.c (peephole2_optimize): Ditto. + * regmove.c (regmove_optimize, rest_of_handle_stack_adjustments): + Ditto. + * local_alloc.c (rest_of_handle_local_alloc): Ditto. + * function.c (thread_prologue_and_epilogue_insns): Ditto. + * rtl_factoring.c (rtl_sequabstr): Ditto. + * bt_load.c (branch_target_load_optimize): Ditto. + * loop_invariant.c (move_loop_invariants): Ditto. + * subregs-init.c (initialize_uninitialized_subregs): Ditto. + * loop-iv.c (iv_analysis_loop_init): Ditto. + * combine.c (rest_of_handle_combine): Ditto. + * web.c (web_main): Added DF_NO_HARD_REGS flag and changed loops + to skip over hard regs. + * reorg.c (dbr_schedule): Removed extra flags from + df_*_add_problems calls. + +2006-09-30 Kenneth Zadeck <zadeck@naturalbridge.com> + + * df-scan.c (problem_SCAN): Removed flags. + (df_scan_add_problem): Ditto. + (df_ref_record, df_insn_refs_record, df_bb_refs_record, + df_refs_record, df_record_entry_block_defs, + df_record_exit_block_uses ): Moved flags to df structure. + * df-core.c (df_init): Added permanent_flags and changeable_flags. + (df_add_problem): Removed flags parameter. + (df_set_flags, df_clear_flags): Changed processing of flags. * + df.h (df_dependent_problem_function, struct df_problem): Removed + flags. + (DF_HARD_REGS, DF_EQUIV_NOTES, DF_SUBREGS, DF_DU_CHAIN, + DF_UD_CHAIN, DF_RI_LIFE, DF_RI_SETJMP): Changed to be fields in + df_permanent_flags. + (DF_LR_RUN_DCE): Changed to be fields in df_changeable_flags. + (df_init, df_add_problem, df_set_flags, df_clear_flags, + df_ru_add_problem, df_rd_add_problem, df_lr_add_problem, + df_ur_add_problem, df_live_add_problem, df_urec_add_problem, + df_chain_add_problem, df_ri_add_problem, df_scan_add_problem): + Changed flag parameters. + * df-problems.c (problem_RU, problem_RD, problem_LR, problem_UR, + problem_LIVE, problem_UREC, problem_CHAIN, problem_RI): Removed + changeable flags field. + (df_ru_add_problem, df_rd_add_problem, df_lr_add_problem, + df_ur_add_problem, df_live_add_problem, df_urec_add_problem, + df_chain_add_problem, df_ri_add_problem): Removed flags parameter. + (df_lr_local_finalize, df_chain_alloc, df_chain_insn_reset, + df_chain_bb_reset, df_chain_create_bb_process_use, + df_chain_start_dump, df_ri_alloc, df_ri_bb_compute, df_ri_compute, + df_ri_free): Changed location of flags. + * dce.c (init_dce): Moved flags from df_*_add_problem to df_init. + (fast_dce): Changed parameters to df_set_flags and df_clear_flags. + * reg_stack.c (reg_to_stack): Moved flags from df_*_add_problem to df_init. + * sched_rgn.c (sched_insns): Ditto. + * regrename.c (regrename_optimize): Ditto. + * sched_ebb.c (schedule_ebbs): Ditto. + * fwprop.c (fwprop_init): Ditto. + * see.c (see_initialize_data_structures): Ditto. + * auto_inc_dec.c (rest_of_handle_auto_inc_dec): Ditto. + * mode-switching.c (optimize_mode_switching): Ditto. + * modulo-sched.c (sms_schedule): Ditto. + * web.c (web_main): Ditto. + * ifcvt.c (if_convert): Ditto. + * recog.c (peephole2_optimize): Ditto. + * regmove.c (regmove_optimize, rest_of_handle_stack_adjustments): + Ditto. + * local_alloc.c (rest_of_handle_local_alloc): Ditto. + * function.c (thread_prologue_and_epilogue_insns): Ditto. + * rtl_factoring.c (rtl_sequabstr): Ditto. + * bt_load.c (branch_target_load_optimize): Ditto. + * loop_invariant.c (move_loop_invariants): Ditto. + * subregs-init.c (initialize_uninitialized_subregs): Ditto. + * loop-iv.c (iv_analysis_loop_init): Ditto. + * combine.c (rest_of_handle_combine): Ditto. + +2006-08-04 Kenneth Zadeck <zadeck@naturalbridge.com> + + * tree-pass.h (pass_rtl_dse): Split into pass_rtl_dse1, + pass_rtl_dse2, pass_rtl_dse3. + * passes.c (init_optimization_passes): Ditto. + * timevar.def (TV_DSE): Split into TV_DSE1, TV_DSE2, and TV_DSE3. + (TV_THREAD_PROLOGUE_AND_EPILOGUE): Made text shorter to improve + readability. + * df.core.c (df_init, df_finish1, df_analyze_problem, df_analyze): + Made postorder and instance variable of df. + (df_finish1): Made tolerant of being passed NULL instance. + (df_get_n_blocks, df_get_postorder): New functions. + * cfganal (post_order_compute): Added delete_unreachable + parameter and code to delete unreachable blocks. + * local_alloc (rest_of_handle_local_alloc): Removed unnecessary + call to delete_unreachable_blocks. + * df.h (postorder, n_blocks): New instance variables. + (df_get_n_blocks, df_get_postorder): New functions. + * sched-rgn.c (extend_rgns): Added extra parameter to + post_order_compute. + * basic-block.h (post_order_compute): Ditto. + * dce.c (fast_dce, init_rs_dflow): Now uses postorder and n_blocks from df. + (pass_rtl_dse): Split into pass_rtl_dse1, + pass_rtl_dse2, pass_rtl_dse3. + * sched-ebb.c (schedule-ebbs): Added return value. + * haifa-sched.c (add_block): Made df parameter unused and fixed + incorrect assert. + +2006-08-01 Kenneth Zadeck <zadeck@naturalbridge.com> + + * sched-ebb.c (sched_ebbs): Now returns the df instance created. + * rtl.h (sched_ebbs): Ditto. + * recog.c (split_all_insns): Made public again so it can be called + from ia64.c + * rtl.h (split_all_insns): Ditto. + * df-problems.c (df_ri_compute): Removed call to non existent + function. + * config/ia64/ia64.c (emit_predicate_relation_info): Added df + parameter and removed rotten ref to global instance. + (ia64_reorg): Added local instance of df and removed calls to non + existent flow.c. + +2006-07-26 Kenneth Zadeck <zadeck@naturalbridge.com> + + * regrename.c (regrename_optimize): Renamed df_clrur to df_live + and removed df_finish call. + (pass_regrename): Added TODO_df_finish flag. + * sched-ebb.c (schedule_ebbs): Renamed df_clrur to df_live + and removed df_finish call. + * fwprop.c (fwprop_done): Removed df_finish call. + (pass_rtl_fwprop): Added TODO_df_finish flag. + * see.c (see_commit_changes): Removed unnecessary set to df. + * postreload.c (rest_of_handle_postreload): Removed df_finish call. + (pass_postreload_cse): Added TODO_df_finish flag. + * tree-pass.h (TODO_df_finish): Added. + * cfg.c (dump_bb_info): Added flow info to dumps + (dump_reg_info): New function. + (dump_flow_info): Moved reg info printing to dump_reg_info. + * auto-inc-dec.c (rest_of_handle_auto_inc_dec): Renamed df_clrur to df_live + and removed df_finish call. + (pass_inc_dec): Added TODO_df_finish flag. + * reorg.c (dbr_schedule): Renamed df_clrur to df_live + and removed df_finish call. + (pass_delay_slots): Added TODO_df_finish flag. + * df-scan.c (df_scan_alloc): Added set to dflow->computed. + (df_scan_dump): Renamed to df_scan_start_dump. + (problem_SCAN): Updated for new dumpers. + * haifa-sched.c (sched_finish): Removed df parameter and deleted + call to df_finish. + * df-core.c (ddf): Renamed to df_current_instance and made public. + (df_init): Ditto and added check to make sure only one instance of + df is alive at any point. + (df_add_problem): Initialized dflow->computed. + (df_analyze_problem): Set dflow->computed. + (df_dump): Now calls new dumpers. + (df_dump_start, df_dump_top, df_dump_bottom): New functions. + (debug_df_insn, debug_df_reg, debug_df_regno, debug_df_defno, + debug_df_useno): Renamed ddf to df_current_instance. + * mode_switching (optimize_mode_switching): Renamed df_clrur to df_live + and removed df_finish call. + (pass_mode_switching): Added TODO_df_finish flag. + * modulo-sched.c (sms_schedule): Removed calls to df_dump and df + parameter from sched_finish. + (pass_sms): Added TODO_df_finish flag. + * web.c (web_main): Removed call to df_dump and df_finish. + (pass_web): Added TODO_df_finish flag. + * loop-init.c (pass_rtl_move_loop_init): Added TODO_df_finish + flag. + * global.c (global_alloc): Removed call to df_dump and debugging + code. + * ifcvt.c (if_convert): Renamed df_clrur to df_live + and removed df_finish call. + (pass_rtl_ifcvt, pass_if_after_combine, pass_if_after_reload): + Added TODO_df_finish flag. + * recog.c (peephole2_optimize): Renamed df_clrur to df_live and + removed df_finish call. + (pass_peephole2): Added TODO_df_finish flag. + * regmove.c (regmove_optimize, rest_of_handle_stack_adjustments): + Renamed df_clrur to df_live and removed df_finish call. + (pass_regmove, pass_stack_adjustments): Added TODO_df_finish flag. + * function.c (epilogue_done): Removed df_finish call. + (pass_thread_prologue): Added TODO_df_finish flag. + * df.h (DF_CLRUR): Renamed to DF_LIVE. + (df_dump_bb_problem_function): New function type. + (df_problem.dump_fun): Deleted. + (df_problem.dump_fun_start, df_problem.dump_fun_top, + df_problem.dump_fun_bottom, problem_data.computed, + df_current_instance): New fields. + (DF_CLRUR_BB_INFO): Renamed to DF_LIVE_BB_INFO. + (df_clrur_bb_info): Renamed to df_live_bb_info. + (df_dump_start, df_dump_top, df_dump_bottom): New functions. + * rtl-factoring (rtl_seqabstr): Renamed df_clrur to df_live + and removed df_finish call. + (pass_rtl_seqabstr): Added TODO_df_finish flag. + * bt-load (branch_target_load_optimize): Renamed df_clrur to df_live + and removed df_finish call. + (pass_branch_target_load_optimize1, + pass_branch_target_load_optimize2): Added TODO_df_finish flag. + * loop-invariant.c (move_loop_invariants): Removed call to + df_finish. + * subregs-init.c (initialize_uninitialized_subregs): Renamed + df_clrur to df_live and changed call to dg_get_live_in to + DF_LIVE_IN. + * rtl.h (dump_reg_info): New function. + * sched-int.h (sched_finish): Removed df parameter. + * combine.c (rest_of_handle_combine): Renamed df_clrur to df_live + and removed df_finish call. + (pass_combine): Added TODO_df_finish flag. + * df-problems.c (df_get_live_in, df_get_live_out): Renamed + DF_CLRUR to DF_LIVE. + (df_ru_dump, df_rd_dump, df_lr_dump, df_ur_dump, df_clrur_dump, + df_urec_dump, df_ri_dump): Deleted function. + (df_ru_start_dump, df_ru_top_dump, df_ru_bottom_dump, + df_rd_start_dump, df_rd_top_dump, df_rd_bottom_dump, + df_lr_start_dump, df_lr_top_dump, df_lr_bottom_dump, + df_ur_start_dump, df_ur_top_dump, df_ur_bottom_dump, + df_live_top_dump, df_live_bottom_dump, df_ri_start_dump, + df_urec_top_dump, df_urec_bottom_dump): New function. + (problem_RU, problem_RD, problem_LR, problem_UR, problem_UREC, + problem_CHAIN, problem_RI): Replaced dump functions. + (df_clrur_get_bb_info): Renamed to df_live_get_bb_info. + (df_clrur_set_bb_info): Renamed to df_live_set_bb_info. + (df_clrur_free_bb_info): Renamed to df_live_free_bb_info. + (df_clrur_alloc): Renamed to df_live_alloc. + (df_clrur_local_finalize): Renamed to df_live_local_finalize. + (df_clrur_free): Renamed to df_clrur_free. + (problem_CLRUR): Renamed to problem_LIVE and dump functions + changed. + (df_clrur_add_problem): Renamed to df_live_add_problem. + (df_chains_dump): Renamed to df_chain_start_dump. + (df_chain_add_problem, df_ri_alloc, df_ri_compute): Removed + problem_data->computed. + * Makefile.in (passes.o): Added DF_H. + * sched-rgn.c (schedule_insns): Renamed df_clrur to df_live + and removed df_finish call. + (pass_sched, pass_sched2): Added TODO_df_finish flag. + * passes.c (execute_todo): Added call to df_finish for + TODO_df_finish flag. + * cfgrtl.c (dump_regset_in, dump_regset_out): Deleted. + (rtl_dump_bb, print_rtl_with_bb): Fixed to find and print df info. + * dce.c (init_dce): Removed call to df_dump. + (end_dce, end_fast_dce): Removed call df_finish and null out local + df instance variable. + (pass_rtl_dce, pass_fast_rtl_dce, pass_rtl_dse): Added + TODO_df_finish flag. + + +2006-07-23 Kenneth Zadeck <zadeck@naturalbridge.com> + + * tree-pass.h (pass_regclass_init, pass_no_new_pseudos, + pass_split_before_sched2): Added. + (pass_life, pass_remove_death_notes): Deleted. + * rtlanal.c: Documentation. + * stack-ptr-mod.c: Ditto. + * output.h: Ditto. + * function.c: Ditto. + * caller-save.c: Ditto. + * sched-deps.c: Ditto. + * jump.c: Ditto. + * alias.c: Ditto. + * calls.c: Ditto. + * cfgloop.c: Ditto. + (establish_preds, flow_loops_find): Removed ref to cfun->max_loop_depth. + * final.c: (rest_of_no_new_pseudos, pass_no_new_pseudos): Added. + * cfg.c (dump_regset, debug_regset): Moved from flow.c. + * regs.h (regs_may_share): Deleted. + (allocate_reg_life_data): Added. + * flow.c: Deleted. + * global.c (reg_may_share): Deleted. + (global_alloc): Removed support for regs_may_share. + * ifcvt.c (rest_or_handel_if_conversion): Removed ref to + clear_reg_deaths. + * timevar.def (TV_FLOW): Removed. + * recog.c (find_single_use, find_single_use_1): Moved to combine + and made static. + (split_all_insns, split_all_insns_noflow): Made static. + (pass_split_before_regclass): Fixed tv_id. + (gate_handle_split_before_sched2, + rest_of_handle_split_before_sched2, pass_split_before_sched2): New. + (pass_split_for_shorten_branches): Renamed and fixed tv_id. + * recog.h (find_single_use): Removed. + * regmove.c (find_use_as_address): Moved from flow and made + static. + * function.h (max_loop_depth): Removed. + * cfgcleanup.c (delete_dead_jumptables): Moved from flow.c. + * subregs-init.c (initialize_uninitialized_subregs): Added call to + allocate_reg_life_data if new regs were added. + * regclass.c (max_regno, reg_set_to_hard_reg_set, + allocate_reg_life_data): Moved from flow.c. + (regclass_init): Now static and returns unsigned int. + (pass_regclass_init, find_subregs_of_mode): New. + (init_subregs_of_mode): Now fills the hash table. + * rtl.h (find_use_as_address, split_all_insns, + split_all_insns_noflow, regclass_init): Deleted. + * combine.c (find_single_use_1, find_single_use): Moved from recog + and made static. + (delete_noop_moves): Moved from flow.c and made static. + (rest_of_handle_combine): Now runs dce as part of df_init. + * df-problems.c (reg_n_info): Moved from flow.c. + (df_ri_problem_data.computed): Added field. + (print_note, df_set_unused_notes_for_mw, df_set_dead_notes_for_mw, + df_create_unused_note, df_ri_bb_compute, df_ri_compute): Changed + note printing to go to dump_file. + (df_ri_alloc, df_ri_compute, df_ri_free, df_ri_dump): Added + support for computed flag. + * Makefile.in: (flow.c flow.o): Removed. + * sched-rgn.c (check_dead_notes1, deaths_in_region): Removed. + (init_regions, add_block1): Removed last of note counting code. + (rest_of_handle_sched2): Moved call to split_all_insns to separate + pass. + * basic_block.h (first_insn_after_basic_block_note, + update_life_extent, PROP_* flags, life_analysis, update_life_info + update_life_info_in_dirty_blocks, count_or_remove_death_notes, + propagate_block, clear_reg_deaths, propagate_block_info, + propagate_one_insn, init_propagate_block_info, + free_propagate_block_info): Removed. + (CLEANUP_*): Renumbered. + sched_vis.c: (print_exp): Added proper printing for PRE and + POST_MODIFY. + * passes.c (init_optimization_passes): Added pass_regclass_init, + pass_no_new_pseudos, and pass_split_before_sched2. Deleted + pass_life. + * struct-equiv.c (struct_equiv_init): Removed dead conditional. + * config/rs6000/rs6000.c (print_operand): More fixes for + PRE_MODIFY. + * config/rs6000/rs6000.md ("*movdf_hardfloat32", + "*movdf_softfloat32"): Ditto. + * cfgrtl (first_insn_after_basic_block_note): Moved from flow.c + and made static. + + +2006-07-16 Zdenek Dvorak <dvorakz@suse.cz> + Kenneth Zadeck <zadeck@naturalbridge.com> + + * regrename.c: Fixed comments. + * see.c (rest_of_handle_see): Removed call to + update_life_info_in_dirty_blocks. + * tree-pass.h: (pass_clear_df, pass_reset_df): Removed. + * passes.c (init_optimization_passes): Ditto. + * cfghooks.c (split_block): Remove call to split_block_end. + (merge_blocks): Removed call to merge_blocks_end. + (duplicate_block): Removed call to duplicate_block_end. + * cfghooks.h (split_block_end, merge_blocks_end, + duplicate_block_end): Removed. + * cfgrtl.c (rtl_split_block_end, rtl_merge_blocks_end): Removed. + (rtl_create_basic_block, force_nonfallthru_and_redirect, + rtl_split_edge, cfg_layout_merge_blocks, cfg_layout_split_edge): + Removed old calls to incremental dataflow. + (dump_regset_in, dump_regset_out): Removed call to dump_regset. + (rtl_dump_bb, print_rtl_with_bb): Rearranged dataflow printing. + * cfg.c (compact_blocks): Removed code to keep dataflow up to + date. + * flow.c (clear_log_links): Deleted + (life_analysis, init_propagate_block_info): Removed PROP_LOG_LINKS. + (update_life_info): Removed call to clear_log_links. + (mark_set_1): Removed code to build log links. + (clear_log_links): Deleted. + * df-scan.c (df_uses_record): Added code to set + DF_REF_PRE_POST_MODIFY. + (df_insn_refs_record): Added code to set DF_REF_CALL_STACK_FRAME. + * df-core.c (df_analyze_simple_change_some_blocks, + df_analyze_simple_change_one_block, df_compact_blocks, + df_bb_replace, reset_df, pass_reset_df, clear_df, pass_clear_df): + Deleted. + * recog.c (peephole2_optimize): Deleted some rotted code. + * df.h (DF_REF_PRE_POST_MODIFY, DF_REF_CALL_STACK_USAGE): New + Flags. + (df_analyze_simple_change_some_blocks, + df_analyze_simple_change_one_block, df_compact_blocks, + df_bb_replace): Deleted. + * bt-load (branch_target_load_optimize): Removed call to + update_life_info. + * cfgcleanup.c (thread_jump, try_forward_edges): Deleted mode + parameter to thread_jump. + (try_optimize_cfg, cleanup_cfg): Removed CLEANUP_UPDATE_LIFE. + * cfglayout.c (cfg_layout_duplicate_bb_end): Deleted. + * combine.c (combine_instructions): Removed call to + update_life_info. + (set_nonzero_bits_and_sign_copies, reg_nonzero_bits_for_combine, + reg_num_sign_bit_copies_for_combine, get_last_value_validate, + get_last_value, reg_dead_at_p): Changed to use local df instance. + (create_log_links, clear_log_links): New function. + (rest_of_handle_combine): Added new version of df, create and + destroy log links locally. + * bb-reorder.c (rest_of_handle_partition_blocks): Removed call to + update_life_info. + * basic-block.h (PROP_LOG_LINKS): Removed and renumbered other + PROP_*. + (CLEANUP_UPDATE_LIFE, CLEANUP_LOG_LINKS): Removed and renumbered + other CLEANUP_*. + * tree-cfg.c (tree_cfg_hooks): Removed fields. + * struct-equiv.c (struct_equiv_init): Removed call to + update_life_in_dirty_blocks. + * dce.c (init_dce, end_dce, end_fast_dce): Removed code to service + rtl_df. + +2006-07-12 Kenneth Zadeck <zadeck@naturalbridge.com> + + * auto-inc-dec.c (parse_add_or_inc): Moved test to merge_in_block. + (find_inc): Now returns success flag if transformation was made. + (find_mem): Ditto. + (merge_in_block): Restructed to loop if any merges were made. + Added max_reg parameter. Added test from parse_add_or_inc. + +2006-07-11 Kenneth Zadeck <zadeck@naturalbridge.com> + + * regrename.c (regrename_optimize): Changed df problem + initialization. + * sched-ebb.c (schedule_ebbs): Ditto. + * reorg.c (dbr_schedule): Ditto. + * mode-switching.c (optimize_mode_switching): Ditto. + * ifcvt.c (if_convert): Ditto. + * reorg.c (peephole2_optimize): Ditto. + * regmove.c (regmove_optimize, rest_of_handle_stack_adjustments): + Ditto. + * sched-rgn.c (schedule_insns): Ditto. + * rtl-factoring.c (rtl_seqabstr): Ditto. + * bt-load.c (branch_target_load_optimize): Ditto. + * subregs-init.c (initialize_uninitialized_subregs): Ditto. + * df-core.c (reset_df): Ditto. + * flow.c (update_life_info, propagate_one_insn): Renamed UPWARD_LIVE + to LR. + (rest_of_handle_life): Changed df problem + initialization. + * function.c (keep_stack_depressed): Renamed UPWARD_LIVE to LR. + * combine.c (set_nonzero_bits_and_sign_copies): Changed DF_LIVE_IN + to DF_UR_IN or DF_LR_IN. + (reg_nonzero_bits_for_combine, + reg_num_sign_bit_copies_for_combine, get_last_value): Changed + DF_LIVE_IN to DF_UR_IN. + * reg-stack.c (reg_to_stack): Changed df problem + initialization. Changed DF_LIVE_IN to DF_LR_IN. str + * struct-equiv.c (struct_equiv_init): Renamed UPWARD_LIVE + to LR. + * dce.c (dce_process_block): Changed DF_UPWARD_LIVE_* to DF_LR_*. + * df.h: (DF_CLRUR) Added symbol and renamed others in block. + (DF_CLRUR_BB_INFO): New macro. + (DF_LIVE_IN, DF_LIVE_OUT): Changed to use output of clrur problem. + (DF_UPWARD_LIVE_IN): Renamed to DF_LR_IN. + (DF_UPWARD_LIVE_OUT): Renamed to DF_LR_OUT. + (df_clrur_bb_info): New structure. + (df_clrur_add_problem, df_clrur_get_bb_info): New functions. + * df-problems: (df_get_live_in, df_get_live_out, + df_lr_simulate_artificial_refs_at_end): Reworked to + to be consistent with new dataflow problems and naming. + (df_ur_local_finalize): Deleted function. + (df_problem problem_UR): Removed ref to df_ur_local_finalize. + (df_clrur_get_bb_info, df_clrur_set_bb_info, + df_clrur_free_bb_info, df_clrur_alloc, df_clrur_free, + df_clrur_dump, df_clrur_add_problem): New functions. + (df_problem problem_CLRUR): New datastructure. + * auto-inc-dec.c (reg_next_inc_use): New Array. + (attempt_change): Added inc_reg parm and boolean result. Move + some tests to try_merge. Added processing for reg_next_inc_use. + (try_merge): Added boolean result and some test moved from + attempt_change and parse_add_or_inc. + (parse_add_or_inc): Move test to try_merge. Improved debugging, + and added code to support reg_next_inc_use. Added more + correctness tests. + (rest_of_handle_auto_inc_dec): Changed df problem initialization + and added code to support reg_next_inc_use. + +2006-07-05 Richard Sandiford <richard@codesourcery.com> + + * passes.c (init_optimization_passes): Move the final RTL DSE pass + after thread_prologue_and_epilogue. + +2006-07-01 Daniel Berlin <dberlin@dberlin.org> + David Edelsohn <edelsohn@gnu.org> + Kenneth Zadeck <zadeck@naturalbridge.com> + + tree-pass.h (pass_inc_dec): New pass variable. + auto-inc-dec.c: New file that contains pass to find auto-inc/dec + instruction combinations. + flow.c: Removed unused includes for execinfo.h and stdio.h. + (init_propagate_block_info): Fixed missing comma typo. + (attempt_auto_inc, try_pre_increment): Added code to abort if any + auto-inc/dec insns are found by this pass. + df-scan.c (df_defs_record): Added flags parameter. + (df_insn_refs_record): Added code to check for conditional def. + (df_recompute_luids): New function. + df-core.c: Fixed comment. + global.c (global_alloc): Moved misplaced debugging code. + (global_conflicts): Added patch to fix the way auto-incs are + accounted for in conflicts graph. + timevar.def (TV_AUTO_INC_DEC): New variable. + recog.c: Fixed comment. + function.c: Fixed comment. + df.h (DF_REF_CONDITIONAL): New flag. + (df_recompute_luids): New function. + gcse.c (extract_mentioned_regs_helper): Added PRE/POST_MODIFY + cases. + common.opt (fauto-inc-dec): New flag. + rtl.h (AUTO_INC_DEC): Made this symbol dependent on + HAVE_(PRE/POST)_MODIFY_(REG/DISP). Made other symbols dependent + on AUTO_INC_DEC rather than components that defined AUTO_INC_DEC. + df-problems.c (df_ru_bb_local_compute_process_def, + df_lr_bb_local_compute, df_lr_simulate_artificial_refs_at_end, + df_lr_simulate_one_insn, df_chain_create_bb, + df_create_unused_note): Added DF_REF_CONDITIONAL to keep + conditional defs from being added to kill sets. + dce.c (dce_process_block): Ditto. + Makefile.in (auto-inc-dec.c): New file. + basic-block.h: Preparation to get rid of PROP_AUTO_INC flag. + passes.c (init_optimization_passes): Added pass_inc_dec. + config/rs6000/rs6000.c (rs6000_legitimate_address): Added code for + PRE_MODIFY. + (rs6000_mode_dependent_address, print_operand): Added case for pre_modify. + config/rs6000/rs6000.h (HAVE_PRE_MODIFY_DISP, + HAVE_PRE_MODIFY_REG): Added flags. + reload1 (eliminate_regs_1, elimination_effects): Added cases for + PRE/POST_MODIFY. + + + +2006-05-27 Steven Bosscher <steven@gcc.gnu.org> + + * reorg.c (dbr_schedule): Fix df_ur_add_problem calls. + +2006-05-24 Kenneth Zadeck <zadeck@naturalbridge.com> + + * df-scan.c (df_scan_free_internal, df_grow_reg_info, + df_reg_chain_unlink, df_ref_remove, + df_insn_create_insn_record, df_insn_refs_delete, + df_ref_create_structure, df_ref_record, df_mark_reg, + df_record_entry_block_defs, df_record_exit_block_uses): + Formatting fixes. + (df_get_artificial_defs): Spelling fixes. + (df_ref_record, df_def_record_1, df_uses_record): + Fix the way that subregs are processed. + (df_defs_record, df_def_record_1, df_insn_refs_record): + Split DF_REF_CLOBBER into two cases. + * df-core.c (df_analyze): Added comments + (df_simple_iterative_dataflow): New function. + (df_dump): Formatting fixes. + * df.h (DF_REF_CLOBBER): Deleted. + (DF_REF_MUST_CLOBBER, DF_REF_MAY_CLOBBER): New enums. + (df_ru_bb_info, df_rd_bb_info, df_lr_bb_info, df_ur_bb_info, + df_urec_bb_info): Added comments. + (df_simple_iterative_dataflow): New function. + * df-problems.c (df_print_bb_index, df_set_dead_notes_for_mw): + Added comments. + (df_ru_alloc, df_ru_local_compute, df_ru_confluence_n, + df_ru_free, df_ru_dump, df_rd_alloc, + df_rd_bb_local_compute_process_def, df_rd_bb_local_compute, + df_rd_local_compute, df_rd_confluence_n, df_rd_free, + df_rd_dump, df_lr_bb_local_compute, df_lr_bb_local_compute, + df_lr_local_compute, df_ur_alloc, df_ur_dump, df_urec_alloc, + df_urec_bb_local_compute, df_urec_local_compute, + df_urec_local_finalize, df_urec_dump, df_chain_create_bb, + df_create_unused_note, df_ri_bb_compute): Formatting fixes. + (df_ru_bb_local_compute_process_def): Do not clear gen set. + (df_ru_bb_local_compute): Reorder processing insn. + (df_ru_transfer_function): Fixed incorrect use of bitmaps. + (df_ru_dump, df_rd_dump, df_chains_dump): Added debugging code. + (df_lr_bb_local_compute, df_lr_simulate_one_insn, + df_ur_bb_local_compute, df_chain_create_bb, + df_create_unused_note, df_ri_bb_compute): Split + DF_REF_CLOBBER into two cases. + * ddg.c (add_deps_for_def): Changed to use ru info. + * modulo-sched.c (sms_schedule): Added debugging. + * dce.c (rs_dflow, df_problem reaching_stores_problem): Deleted. + (init_rs_dflow, calculate_reaching_stores): Move dataflow to new + df function. + +2006-05-23 Kenneth Zadeck <zadeck@naturalbridge.com> + + * fwprop.c (local_ref_killed_between_p): Rearranged definition. + +2006-05-23 Steven Bosscher <stevenb.gcc@gmail.com> + + * rtlanal.c (find_occurrence): Move to fwprop.c. + * rtl.h (find_occurrence): Remove prototype. + * fwprop.c (can_simplify_addr): Fix check for frame based addresses. + (should_replace_address): Update comment before this function. + (local_ref_killed_between_p): Don't choque on NOTEs. + (use_killed_between): Handle the exceptional case that a DEF does + not dominate one of its uses. + (varying_mem_p): Simplify return type. + (all_uses_available_at): Clean up unnecessary single_set calls. + (find_occurrence_callback, find_occurrence): New. + (subst): Rename to try_fwprop_subst. + (forward_propagate_subreg): Update caller. + (forward_propagate_and_simplify): Likewise. + (fwprop_init): Find loops before computing data flow info. + (fwprop_done): Call cleanup_cfg without CLEANUP_PRE_LOOP. Free + loop tree before cleanup_cfg. + +2006-05-14 Kenneth Zadeck <zadeck@naturalbridge.com> + + * regclass.c (init_subregs_of_mode): Created dummy version + when CANNOT_CHANGE_MODE_CLASS is undefined. + +2006-05-13 Steven Bosscher <stevenb.gcc@gmail.com> + + * df-core.c (df_bb_regno_last_use_find): Do not look for dataflow + information attached to non-INSNs such as NOTEs. + (df_bb_regno_first_def_find, df_bb_regno_last_def_find): Likewise. + +2006-05-05 Kenneth Zadeck <zadeck@naturalbridge.com> + + * haifa-sched.c (glat_size): New variable. + (sched-init, extend_bb): Properly initialized glat_start and + glat_end. + (free_glat): Fixed double free of bitmaps. + * df-scan.c (df_scan_get_bb_info, df_scan_set_bb_info): Added + assertions. + * df-problems.c (df_ru_get_bb_info, df_ru_set_bb_info, + df_rd_get_bb_info, df_rd_set_bb_info, df_lr_get_bb_info, + df_lr_set_bb_info, df_ur_get_bb_info, df_ur_set_bb_info, + df_urec_get_bb_info, df_urec_set_bb_info): Ditto. + +2006-04-25 Kenneth Zadeck <zadeck@naturalbridge.com> + + * flow.c (rest_of_handle_life): Removed unnecessary code. + * df-scan.c (df_scan_free_internal, df_scan_free): Fixed + storage leak. + * df-core.c (df_delete_basic_block): Removed dangling pointer. + * mode-switching.c (optimize_move_switching): Moved creation of df + instance to avoid storage leak. + * ifcvt.c (if_convert): Fixed storage leak. + * dce.c (mark_insn, + rest_of_handle_dce): Removed old way of processing libcalls. + (libcall_matches_p, mark_libcall_insns): Removed. + (mark_libcall): New function. + (mark_reg_dependencies, dce_process_block): New code to check for + libcalls. + (fast_dce): Fixed storage leak. + +2006-04-24 Kenneth Zadeck <zadeck@naturalbridge.com> + + * sched-ebb.c (schedule_ebbs): Updated register lifetime info. + * modulo-sched.c (sms_schedule): Ditto. + * sched-reg.c (schedule_insns): Ditto. + * regmove.c (regmove-optimize): Ditto. + * tree.h (setjmp_vars_warning, setjmp_args_warning): Removed. + (generate_setjmp_warning): Added. + * tree-pass.h (pass_subregs_of_mode_init): Added. + * flow.c (life_analysis): Moved parts to other passes. + (regno_clobbered_at_setjmp):Moved setjmp warning to local-alloc. + (rest_of_handle_life): Ditto. + * df-scan.c (df_ref_record): Reformatted comment. + * ifcvt.c (rest_of_handle_if_conversion): Removed last bit of + rtl_df scafolding. + * local-alloc.c (local_alloc, no_equiv, block_alloc): Changed to + use same instance of df as global_alloc, + (rest_of_handle_local_alloc): Now also produces setjmp warnings. + * function.c (regno_clobbered_at_setjmp): Moved from flow. + (setjmp_vars_warning, setjmp_args_warning): Added parms to use + passed in df instance. + (generate_setjmp_warnings): New function. + * df.h (DF_RI_SETJMP): New constant. + (df_ri_get_setjmp_crosses): New function. + * df-problems.c (df_ri_problem_data, setjmp_crosses): New + variables. + (df_ri_alloc, df_ri_bb_compute, df_ri_bb_compute, df_ri_compute): + Changed variables live across setjmp computation. + (df_ri_get_setjmp_crosses): New function. + * regclass.c (init_subregs_of_mode): Changed return. + (gate_subregs_of_mode_init, pass_subregs_of_mode_init): New. + * rtl.h (init_subregs_of_mode): Removed. + * integrate.c (allocate_initial_values): Removed incorrect parm + attribute and changed instance of df used. + * output.h (regno_clobbered_at_setjmp): Removed. + * stack-ptr-mod.c: Fixed comment. + * Makefile.in (regclass.o): Added tree-pass.h. + * basic-block.h (safe_insert_insn_on_edge): Deleted. + * passes.c (init_optimization_passes): Added + pass_subregs_of_mode_init. + * cfgrtl.c (mark_killed_regs, safe_insert_insn_on_edge): Deleted. + * rtl-profile.c: Removed. + +2006-04-21 Kenneth Zadeck <zadeck@naturalbridge.com> + + * tree-pass.h (pass_stack_ptr_mod, pass_initialize_subregs, + pass_reset_df): New. + * flow.c (notice_stack_pointer_modification_1, + notice_stack_pointer_modification, find_regno_partial_param, + find_regno_partial, initialize_uninitialized_subregs): Moved to + separate passes in separate files. + (life_analysis): Removed call to notice_stack_pointer_modification. + (rest_of_handle_life): Removed call to initialize_uninitialized_subargs. + * df-core.c (reset_df, pass_reset_df): New. + * Makefile.in (stack-ptr-mod.o, subregs-init.o): New. + * passes.c (pass_stack_ptr_mod, pass_initialize_subregs, + pass_reset_df): New passes. + * subregs-init.c: New file that contains separate pass for + initialize_uninitialized_subargs. + * stack-pointer-mod.c: New file that contains separate pass for + notice_stack_pointer_modification. + +2006-04-19 Kenneth Zadeck <zadeck@naturalbridge.com> + + * ifcvt.c (rest_of_handle_if_after_combine): Removed flow + scaffolding. + * passes.c (init_optimization_passes): Moved clear_df to after + combine and changed two expensive dce passes into a dse and fast + dce pass. + +2006-04-18 Kenneth Zadeck <zadeck@naturalbridge.com> + + * mode-switching.c (optimize_mode_switching): Created local + instance of df and removed references to flow. + * regmove.c (mark_flags_life_zones, regmove_optimize): Ditto. + * global.c (global_alloc, rest_of_handle_global_alloc): Reused + instance of df created in local alloc. + * local-alloc.c (rest_of_handle_local_alloc): Create instance of + ra_df used by all register allocation. + * bb-reorder.c (fix_crossing_conditional_branches): Removed code + to keep dataflow up to date. + * Makefile.in (bb-reorder.o): Removed ref to DF_H. + * passes.c (init_optimization_passes): Meved clear_df to before + partition_blocks. + +2006-04-17 Kenneth Zadeck <zadeck@naturalbridge.com> + + * tree-pass.h (pass_recompute_reg_usage): Deleted. flow.c + (pass_recompute_reg_usage, recompute_reg_usage): Deleted. * + sched-ebb.c (begin_schedule_ready, schedule_ebb, + begin_schedule_ready, schedule_ebb, schedule_block): Threaded + local instance of df. + (schedule_ebbs): Created local instance of df. + * haifa-sched.c (schedule_block, sched_init, sched_finish, + generate_recovery_code, begin_speculative_block, + add_to_speculative_block, init_before_recovery, + create_recovery_block, create_check_block_twin, init_glat, + init_glat1): Threaded local instance of df. + * modulo-sched.c (sms_schedule): Ditto. + (rest_of_handle_sms): Removed unnecessary update of flow info. + * sched-int.h (df.h): Now includes this. + (schedule_block, sched_init, sched_finish, add_block): Added df + parm. + * sched-rgn.c (begin_schedule_ready, schedule_region, + schedule_insns): Threaded local instance of df. + (schedule_insns): Removed unnecessary update of flow info. + * Makefile.in (SCHED_INT_H): Added DF_H. + (ddg.o, modulo-sched.o, haifa-sched.o, sched-deps.o, sched-rgn.o, + sched-ebb.o): Removed DF_H. + * ddg.c (build_inter_loop_deps): Swapped parms. + (create_ddg): Swapped parms for call. + * passes.c (init_optimization_passes): Removed dce and + recompute_reg_use pass and moved clear_df to before first + schedulers. + +2006-04-16 Kenneth Zadeck <zadeck@naturalbridge.com> + + * df.h (shared_df): Removed. + (ra_df): New. + * core.c (shared_df): Removed. + (ra_df): New. + * postreload.c (reload_combine): Changed to use ra_df. + * reload.c (push_reload, find_dummy_reload): Ditto. + * global.c (global_alloc, global_conflicts, mark_elimination, + build_insn_chain, rest_of_handle_global_alloc): Ditto. + * reload1.c (compute_use_by_pseudos, reload): Ditto. + * local-alloc.c (local_alloc, block_alloc): Created local instance + of df. + * passes.c (init_optimization_passes): Moved clear_df to before + register allocators. + * tree-pass.h (pass_partition_blocks): Removed redundant + copy. + +2006-04-16 Kenneth Zadeck <zadeck@naturalbridge.com> + + * tree-pass.h (pass_reset_df_after_reload): Removed. + * df-core.c (reset_df_after_reload, pass_reset_df_after_reload): + Removed. + * function.c (prologue_epilogue_df): New global. + (rtx keep_stack_depressed, thread_prologue_and_epilogue_insns): + Unthreaded local version of df and replaced with + prologue_epilogue_df. + * rtl.h (prologue_epilogue_df): New. + * passes.c (init_optimization_passes): Removed all uses of flow + after register allocation. + * config/i386/i386.c (ix86_eax_live_at_start_p): Replaced use of + flow with prologue_epilogue_df. + +2006-04-15 Kenneth Zadeck <zadeck@naturalbridge.com> + + * tree-pass.h (pass_flow2): Renamed to + pass_thread_prologue_and_epilogue. + * passes.c (pass_flow2): Ditto. + * final.c (rest_of_clean_state): Removed flow2_completed. + * config/i386/i386.md: Ditto. + * config/sh/sh.md: Ditto. + * config/mips/mips.md: Ditto. + * config/h8300/h8300.md: Ditto. + * flow.c: Ditto. + (rest_of_handle_flow2): Moved to function.c as + rest_of_handle_thread_prologue_and_epilogue. + * timevar.def (TV_FLOW2): Renamed to + TV_THREAD_PROLOGUE_AND_EPILOGUE. + * function.c (keep_stack_depressed): Added df parameter. + (thread_prologue_and_epilogue_insns): Made local function and + removed unused parameter. Added local instance of df. + (rest_of_handle_thread_prologue_and_epilogue): New function + renamed from flow.c. + (pass_thread_prologue_and_epilogue): New pass. + * rtl.h (flow2_completed, thread_prologue_and_epilogue_insns): + Removed. + * df-problems.c (df_ru_get_bb_info, df_rd_get_bb_info, + df_lr_get_bb_info, df_ur_get_bb_info, df_urec_get_bb_info): Added + check. + * Makefile.in (function.o): Added timevar.h. + +2006-04-13 Kenneth Zadeck <zadeck@naturalbridge.com> + + * rtl-factoring (collect_pattern_seqs, clear_regs_live_in_seq, + recompute_gain_for_pattern_seq, clear_regs_live_in_seq, + recompute_gain, split_blocks_after_seqs, split_pattern_seq, + erase_matching_seqs, abstract_best_seq, rtl_seqabstr): Threaded + local instance of df to replace all references to flow. + (gate_rtl_seqabstr): Removed unnecessary calls. + * passes.c (init_optimization_passes): Moved clear_df before + rtl_seqabstr. Removed some expensive calls to dce. + * tree-pass.h (pass_fast_rtl_dce): New. + * regrename.c (gate_handle_regrename): Update call to pass. + * sched-rgn.c (rest_of_handle_sched2): Remove outdated calls. + +2006-04-13 Kenneth Zadeck <zadeck@naturalbridge.com> + + * regmove.c (rest_of_handle_stack_adjustments): Removed all + references to flow.c and replaced with df. + * passes.c (init_optimization_passes): Moved clear_df before + pass_stack_adjustments. + +2006-04-12 Kenneth Zadeck <zadeck@naturalbridge.com> + + * recog.c (delete_insn_chain_and_dflow): New function. + (peephole2_optimize): Replaced all flow references with df. Added + local instance of df. + * passes.c (init_optimization_passes): Moved clear_df before peephole2. + +2006-04-11 Kenneth Zadeck <zadeck@naturalbridge.com> + + * ifcvt.c (cond_exec_process_if_block, merge_if_block, + noce_process_if_block, cond_move_process_if_block, + process_if_block, find_if_header, find_cond_trap, find_if_case_1, + find_if_case_2, dead_or_predicable): Threaded local copy of df + thru the call stack. + (merge_if_block, find_cond_trap, find_if_case_1, find_if_case_2, + dead_or_predicable): Added code to update df in place. + (if_convert): Removed parameter concerned with correct dataflow + info, it now always is. Created instance of df. Removed code to + update old flow info. + (rest_of_handle_if_after_reload): Removed unnecessary calls to + cleanup_cfg. + * passes.c (init_optimization_passes): Moved clear_df to before + last ifconvert. Renamed pass_cprop to pass_cprop_hardreg. + * regrename.c (rest_of_handle_cprop): Renamed pass_cprop to + pass_cprop_hardreg. + * tree-pass.h: Renamed cprop to cprop_hardreg. + +2006-04-11 Kenneth Zadeck <zadeck@naturalbridge.com> + + * sched-ebb.c (schedule_ebbs): Removed code to update dataflow + after scheduling and removed unused parm from + reposition_prologue_and_epilogue_notes. + * sched-rgn.c (schedule_insns): Ditto. + (rest_of_handle_sched): Added temp hack to rebuild flow based dataflow + until flow is permanently removed. + * haifa-sched.c (init_glat): Modified to create glat from df info. + (attach_life_info, attach_life_info1, check_reg_live): Removed. + (free_glat): Now cleans up df. + * modulo-sched.c (compute_jump_reg_dependencies): Removed df parm. + * ifcvt.c (rest_of_handle_if_conversion): Added code to clean up + flow based reg_deaths after first call to ifconvert. + * function.c (reposition_prologue_and_epilogue_notes): Removed + unused parm. + * rtl.h (reposition_prologue_and_epilogue_notes): Ditto. + * sched-int.h (check_reg_live): Removed. + +2006-04-10 Kenneth Zadeck <zadeck@naturalbridge.com> + + * df-core.c (reset_df_after_reload, clear_df): Added return to + make compatible with pass manager. + * dce.c (rest_of_handle_dce, rest_of_handle_fast_dce, + rest_of_handle_dse): Ditto. + +2006-04-09 Kenneth Zadeck <zadeck@naturalbridge.com> + + * regrename.c (merge_overlapping_regs, regrename_optimize): + Threaded private instance of df. + (regrename_optimize): Created private instance of df, removed + all references or flow. + (copyprop_hardreg_forward): Removed all references to flow. + (gate_handle_regrename): Split reg rename and cprop into separate + passes. + (gate_handle_cprop): New function. + (pass_cprop): New pass structure. + * tree-pass.h (pass_cprop): New pass structure. + * passes.c (init_optimization_passes): Added cprop pass and + moved clear_df pass closer to beginning. + +2006-04-08 Kenneth Zadeck <zadeck@naturalbridge.com> + + * tree-pass.h (pass_split_after_reload, pass_branch_target_load_optimize1 + pass_branch_target_load_optimize2): Added. + (pass_branch_target_load_optimize): Deleted. + * flow.c (rest_of_handle_flow2): Split the calls to split_all_insns and + branch_target_load_optimize into their own passes. + * passes.c (init_optimization_passes): Ditto. + (init_optimization_passes): Moved clear_df pass to before + second branch_target_load_optimize pass. + * bt-load (compute_defs_uses_and_gen, build_btr_def_use_webs, + migrate_btr_defs): Threaded private copy of df into these functions. + (branch_target_load_optimize): Made private and add local + instance of df. Removed all references to flow. + (rest_of_handle_branch_target_load_optimize1): New function. + (rest_of_handle_branch_target_load_optimize): Renamed to + rest_of_handle_branch_target_load_optimize2. + rtl.h (branch_target_load_optimize): Removed. + +2006-04-08 Kenneth Zadeck <zadeck@naturalbridge.com> + + * sched-ebb.c (init_ready_list, can_schedule_ready_p, new_ready, + compute_jump_reg_dependencies, schedule_ebb, sched_analyze, + schedule_block): Changed to pass instance of df. + (compute_jump_reg_dependencies): Changed to use local instance + of dataflow. + * ddg.c (build_intra_loop_deps, sched_analyze, + build_intra_loop_deps): Changed to pass instance of df. + * ddg.h: added forward reference to struct df. + * haifa-sched.c (schedule_insns, schedule_insn, + schedule_block): Changed to pass instance of df. + * modulo-sched (compute_jump_reg_dependencies): Ditto. + (sms_schedule): Added call to do dce when stated. + * sched-deps.c (sched_analyze_insn, sched_analyze): + Changed to pass instance of df. + * rtl.h (schedule_insns, schedule_ebbs): Ditto. + * sched-int.h (init_ready_list, can_schedule_ready_p, new_ready, + compute_jump_reg_dependencies, sched_analyze, schedule_block): + Ditto. + * sched-rgn.c (check_live_1, update_live_1, check_live, + update_live, init_ready_list, can_schedule_ready_p, new_ready, + compute_jump_reg_dependencies, compute_block_backward_dependences, + schedule_region, schedule_insns): Ditto. + (schedule_insns): Removed call to update_life_info when finished. + (rest_of_handle_sched, rest_of_handle_sched2): Creates local + instance of df. + * passes.c (init_optimization_passes): moved clear_df pass + earlier. + * Makefile.in (df-core.o): Added except.h and dce.h + (modulo-sched.o): Added DF_H. + * recog.c (split_all_insns): Removed old code that was used to + update dataflow. + * reg-stack.c (rest_of_handle_stack_regs): Removed ifdefed out code. + +2006-04-07 Kenneth Zadeck <zadeck@naturalbridge.com> + + * recog.c (split_all_insns): Removed parameter and code to + update dataflow. + * sched-rgn.c (rest_of_handle_sched2): Removed parm to + split_all_insns. + * flow.c (rest_of_handle_flow2): Ditto. + (rest_of_handle_split_all_insns): Added call to update old + dataflow after this pass. + (rest_of_handle_split_after_reload, + rest_of_handle_split_before_regstack): New function. + (pass_split_after_reload): New structure. + * rtl.h (split_all_insns): Removed param. + * bb-reorder.c (rest_of_handle_reorder_blocks): Removed + references to flow. + * reg-stack.c (subst_stack_regs_pat): Removed unnecessary + assertion. + +2006-04-07 Daniel Berlin <dberlin@dberlin.org> + Kenneth Zadeck <zadeck@naturalbridge.com> + + * regoc.c (dbr_schedule): Added parm to call to df_lr_add_problem. + * ifcvt.c (if_convert): Ditto. + * modulo-sched.c (sms_schedule): Ditto. + * reg-stack.c (reg_to_stack): Ditto. + * global.c (global_alloc, rest_of_handle_global_alloc): Ditto. + (rest_of_handle_global_alloc): Removed call to df_set_state. + * basic_block.h (clear_reg_deaths): New function. + * flow.c: (clear_reg_deaths): New function. + (rest_of_handle_life): Added parm to call to df_lr_add_problem and + df_ur_add_problem. + * df-scan.c (df_scan_free_internal): Added code to clear new + bit vectors. + (df_scan_alloc): Added extra parameter and code to initialize + new bit vectors. + (df_scan_dump): Added code to dump new bit vectors. + (df_problem problem_SCAN): Added extra field. + (df_scan_add_problem): Added flags parameter. + (df_rescan_blocks): Added code to remove deleted blocks from + bitmap and extra parm to call to df_scan_alloc. + (df_insn_create_insn_record): Removed return value. + (df_set_state): Removed function. + (df_ref_record, df_bb_refs_record, df_record_entry_block_defs, + df_record_exit_block_uses): Changed the way flags are processed. + (df_bb_refs_record, df_refs_record, df_record_exit_block_uses): + Restructured scanning to fixed hard regs so bitmaps could be + recorded for later use. + (df_has_eh_preds): Now public. + * df-core.c (df_mvs_dump, df_set_flags, df_clear_flags, + df_delete_basic_block): New function. + (df_init): Changed location of flags. + (df_add_problem): Added flags parameter and the way flags are + processed. + (df_insn_uid_debug, df_ref_debug, debug_df_defno, debug_df_ref, + debug_df_chain): Improved debugging output. + (clear_df): Added call to clear_reg_deaths. + * df.h: Some reordering to remove forward references. + (dataflow.flags): New field. + (df.flag): Deleted field. + (df_alloc_function): Added additional bitmap parameter. + (df_dependent_problem_function): New type. + (df_problem.changeable_flags): New field. + (df_ref_flags.DF_SCAN_INITIAL, + DF_SCAN_GLOBAL, DF_SCAN_POST_ALLOC, df_state): Removed. + (df_refs_chain_dump, df_ref_debug, df_chain_dump): Removed df + parameter. + (df_add_problem, df_ru_add_problem, df_rd_add_problem, + df_lr_add_problem, df_ur_add_problem, df_urec_add_problem, + df_ri_add_problem, df_scan_add_problem): Added flags parameter. + (df_set_state): Removed function. + (df_set_flags, df_clear_flags, df_delete_basic_block, df_has_eh_preds) New functions. + * df-problems.c (df_get_dependent_problem): Deleted function. + (df_ru_alloc, df_rd_alloc, df_lr_alloc, df_ur_alloc, + df_urec_alloc, df_chain_alloc, df_ri_alloc): Added all blocks + parameter. + (df_ru_alloc, df_rd_alloc): Now resets all blocks. + (df_ru_dump, df_rd_dump, df_lr_dump, df_ur_dump, df_urec_dump, + df_chains_dump): Fixed crash if problem was never run. + (df_ru_add_problem, df_rd_add_problem, df_lr_add_problem, + df_ur_add_problem, df_urec_add_problem, df_chain_add_problem, + df_ri_add_problem): Processes flags in uniform manner. + (df_lr_bb_local_compute): Fixed case to account for return value + correctly. + (df_lr_bb_local_compute): Changed processing of exit block. + (df_lr_transfer_function): Added hook to call dce. + (df_lr_free): Added lr problem data. + (df_set_notes_for_mw, df_reg_lifetime): Removed function. + (df_lr_simulate_artificial_refs_at_end, df_lr_simulate_one_insn, + df_kill_notes, df_set_unused_notes_for_mw, df_set_dead_notes_for_mw): + New function. + (df_ri_alloc, df_ri_bb_compute, df_create_unused_note, + df_ri_bb_compute, df_ri_compute, df_ri_free, df_ri_add_problem): + Added functionality to compute register information. + dce.c (deletable_insn_p): Cannot delete PREFETCHes. + (delete_unmarked_insns): Also delete noop moves as well as support for + calling dce from df. + (prescan_insns_for_dce, rest_of_handle_dce, end_fast_dce, + dce_process_block, rest_of_handle_fast_dce, + rest_of_handle_dse): Restructured to handle being directly called from + df. + + +2006-03-14 Kenneth Zadeck <zadeck@naturalbridge.com> + + * tree-pass.h: Added pass to clear other dataflow infomation out. + * final.c (rest_of_handle_final, rest_of_clean_state): Cleared out + references to df. + (rest_of_clean_state) Added regstack_completed. + * reorg.c (fill_simple_delay_slots, fill_slots_from_thread, + fill_eager_delay_slots, make_return_insns): Added df parameter to function. + (make_return_insns, dbr_schedule) Added df parameter to calls. + * flow.c (update_life_info): Added glue code to df calls. + * df-scan.c (df_scan_free_internal, df_insn_refs_delete, df_ref_record): Added + code to properly handle multiword hard registers. + (df_ref_create_structure, df_ref_record): Added code to properly handle subregs. + (df_ref_create_structure): Changed switching structure. + (df_bb_refs_record): Fixed case where duplicate artificial refs + were created. + (df_record_entry_block_defs): Added code to make stack pointer + live in entry block. Refined cases where frame pointer is needed. + * df-core.c (df_mvs_dump, clear_df): New function. + (pass_clear_df): New pass structure. + (df_insn_uid_debug, df_ref_debug, debug_df_defno, debug_df_ref, + debug_df_chain): Improved debugging output. + * df.h: Some reordering to remove forward reference. + (df_ref_flags.DF_REF_MW_HARDREG, DF_REF_PARTIAL): New fields. + (df_ref_flags.DF_REF_DIES_AFTER_THIS_USE): Removed. + (df_mw_hardreg): New struct. + (DF_INSN_UID_MWS): New macro. + (df_refs_chain_dump, df_ref_debug, df_chain_dump): Removed df + parameter. + * rtl.h (regstack_completed): New global var. + * resource.c (mark_target_live_regs): Added passed in instance of + df. + * resource.h (mark_target_live_regs): Ditto. + * df-problems.c (df_chain_dump): Removed df parameter. + (df_ru_bb_local_compute_process_def, + df_rd_bb_local_compute_process_def, df_lr_bb_local_compute, + df_lr_bb_local_compute, df_chain_create_bb): Made subreg aware. + (df_ru_bb_local_compute, df_rd_bb_local_compute, + df_lr_bb_local_compute, df_lr_bb_local_compute, + df_chain_create_bb): Cleanup to use proper macros. + (df_ur_local_finalize, df_ur_local_finalize): Removed unnecessary + code to fixup bitvectors. + (df_ri_alloc): Cleared lifetime. + (df_ignore_stack_reg, df_kill_notes, df_set_notes_for_mw, + df_create_unused_note): New function. + (df_ri_bb_compute, df_ri_compute): Added code to create/update + REG_DEAD and REG_UNUSED notes. + * reg-stack.c (regstack_completed): New variable. + (reg_to_stack): Modified to use it's own instance of df. + (rest_of_handle_stack_regs): Removed most cleanup code. + * Makefile.in (reorg.o, RESOURCE_H): Added dependancy to df.h. + (final.o): Removed dependency to df.h + * passes.c (pass_magic_life): Removed. + (pass_clear_df): Added. + * dce.c (prescan_insns_for_dce, end_fast_dce): Now works if no instance of + df is available. + (dce_process_block): Made subreg aware. + (rest_of_handle_fast_dce): Reset bitvectors when iterating. + (prescan_insns_for_dse): Removed useless code. + +2006-02-06 Daniel Berlin <dberlin@dberlin.org> + + * doc/rtl.texi: Document REG_LIBCALL_ID. + * optabs.c (libcall_id): New variable. + (emit_no_conflict_block): Use it to emit + REG_LIBCALL_ID. + (emit_libcall_block): Ditto. + * combine.c (distribute_notes): Deal with + REG_LIBCALL_ID. + * reg-notes.def (LIBCALL_ID): New note. + * dce.c (marked_libcalls): New variable. + (in_libcall): Removed. + (mark_insn): Mark which libcalls we need to fully mark. + (mark_libcall_insns): New function. + (end_dce): Free marked_libcalls. + (init_dce): Allocate marked_libcalls. + Call mark_libcall_insns. + +2006-01-17 Kenneth Zadeck <zadeck@naturalbridge.com> + * df-core.c (df_iterative_dataflow): Added debugging assert. + (df_insn_uid_debug): New debugging function. + (df_insn_debug): Changed to used df_insn_uid_debug. + (df_analyze_problem): Made public. + * df.h (df_analyze_problem): Ditto. + * cfgcleanup.c (cleanup_cfg): Now calls run_fsst_dce. + * flow.c (update_life_info): Ditto. + * dce.c (init_dce, mark_insn, + mark_nonreg_stores, prescan_insns_for_dce): Added parameter so + that they could be called from both handle_rest_of_dce and + handle_rest_of_fast_dce. + (marked_insn_p): Added code to be tolerant of artifical defs, + which do not have insns. + (mark_nonreg_stores_2): New function. + (mark_artificial_uses, mark_reg_dependencies, store_base_local, + mark_escaping_stores, mark_dependent_stores, + prescan_insns_for_dse): Added parameter to mark_insn. + (rest_of_handle_dce): Added parameter to prescan_insns_for_dce. + (record_stores): Added parameter to mark_non_reg_stores. + (dce_process_block, rest_of_handle_fast_dce, gate_fast_dce, + run_fast_dce): New functions to do dce faster. + (rest_of_handle_dse) Added parameter to init_dce. + * dce.h (run_fast_dce): Added declaration. + +2006-01-17 Kenneth Zadeck <zadeck@naturalbridge.com> + + * df-scan.c (df_hard_reg_init): Moved declaration of i outside macro. + +2006-01-17 Ian Lance Taylor <ian@airs.com> + + * combine.c (combine_instructions): Pass instruction + to note_stores and set_nonzero_bits_and_sign_copies. + (set_nonzero_bits_and_sign_copies): We can't assume + anything about non-zero bits for registers initialized with + themselves if the register is not live on entry to the block. + + +2005-01-17 Kenneth Zadeck <zadeck@naturalbridge.com> + + PR dataflow/25799 + * df-problems.c (df_ru_confluence_n, df_rd_confluence_n): + Corrected confluence operator to remove bits from op2 before oring + with op1 rather than removing bits from op1. + * (df_ru_transfer_function): Corrected test on wrong bitmap which + caused infinite loop. + + +2005-01-17 Kenneth Zadeck <zadeck@naturalbridge.com> + + * df-scan.c (df_scan_free_bb_info): Added basic block parameter to + be able to clean out basic block when not necessary. + (df_scan_free): Fixed to properly delete information if df is + unused before calling df_finish. + (df_scan_alloc, df_rescan_blocks, df_reg_chain_unlink, + df_insn_create_insn_record, df_bb_refs_record): Fixed formatting + or comment typos. + (df_bb_refs_delete): New function. + (df_refs_delete): Moved per block code to df_bb_refs_delete. + * df-core.c (df_set_blocks): Added code to properly clean out + unused blocks if they are not part of the blocks to consider. + (df_compact_blocks): Added basic block parameter to free_bb_fun to + be able to clean out basic block when not necessary + * df.h (df_free_bb_function): Ditto. + (df_bb_refs_delete): New function. + * df-problems.c (df_ru_free_bb_info, df_rd_set_bb_info, + df_lr_set_bb_info, df_ur_free_bb_info, df_urec_free_bb_info):Added + basic block parameter to be able to clean out basic block when not + necessary. + (df_ru_alloc, df_rd_alloc): Fixed dyslexic overflow test. + (df_ru_free, df_rd_free, df_lr_free, df_ur_free, df_urec_free): + Fixed to properly delete information if df is unused before + calling df_finish. + +2006-01-06 Daniel Berlin <dberlin@dberlin.org> + Kenneth Zadeck <zadeck@naturalbridge.com> + + * df-scan.c (df_reg_use_gen, df_reg_def_gen): Removed. + (df_insn_contains_asm_1): New function. + (df_insn_contains_asm): Rewritten to use for_each_rtx. + (df_insn_refs_record): Fixed call to df_insn_contains_asm and + the way calls are processed. + (df_insn_refs_record, df_bb_refs_record): Changed calls to not + need df_reg_use_gen or df_reg_def_gen. + * loop-invariant.c (free_inv_motion_data): Removed duplicated code typo. + +2005-12-30 Kenneth Zadeck <zadeck@naturalbridge.com> + + * df-core.c (df_find_def, df_find_use): Added subreg aware code. + (df_reg_defined, df_reg_used): New function. + * df.h (df_reg_defined, df_reg_used): New function. + * loop-invariant.c (struct invariant, hash_invariant_expr, + eq_invariant_expr, find_or_insert_inv, find_or_insert_inv, + find_identical_invariants, merge_identical_invariants, + find_defs, create_new_invariant, check_dependencies, + find_invariant_insn, find_invariants, get_inv_cost, + best_gain_for_invariant, set_move_mark, move_invariants, + free_inv_motion_data): Functions added from mainline patch. + (invariant_for_use, hash_invariant_expr_1, invariant_expr_equal_p, + check_dependencies, create_new_invariant, find_invariant_insn + move_invariant_reg): Functions modified from mainline patch to be + consistent with latest df. + +2005-12-22 Paolo Bonzini <bonzini@gnu.org> + + * df-scan.c (df_ref_unlink): Fix thinko when REF == CHAIN. + +2005-12-22 Danny Berlin <dberlin@dberlin.org> + Richard Sandiford <richard@codesourcery.com> + Kenneth Zadeck <zadeck@naturalbridge.com> + + * tree-pass.h: Added passes for new dce and dse. + * flow.c (update_life_info, propagate_block): Added hooks to + call new dead code elimination. + * common.opt (flag_flow_dce, flag_new_dce): Ditto. + * passes.c (init_optimization_passes): Ditto. + * cfgcleanup.c (cleanup_cfg): Ditto. + * timevar.def: New time vars for dce and dse. + (propagate_block_delete_insn): Added debugging. + * dce.c: New File containing dead code elimination and dead + store elimination based on df. + +2005-12-19 Kenneth Zadeck <zadeck@naturalbridge.com> + + * flow.c (update_life_info, count_or_remove_death_notes): Fixed + latent bug that could happen if update_life_info was called with a + blocks parameter and the call to cleanup_cfg actually deleted one + of those blocks. + * loop-invariant.c (move_loop_invariants): Fixed df + initialization. + * struct-equiv.c (struct_equiv_init): Made dataflow compatible with + df. + + +2005-12-18 Daniel Berlin <dberlin@dberlin.org> + Kenneth Zadeck <zadeck@naturalbridge.com> + + * fwprop.c (local_ref_killed_between_p): New Function. + (use_killed_between, varying_mem_p, all_uses_available_at, + update_df, subst, forward_propagate_subreg, + forward_propagate_and_simplify, forward_propagate_into, fwprop, + fwprop_addr): Changes to support modifications to df datastructures. + * ddg.c (add_deps_for_def, add_deps_for_use, build_inter_loop_deps): + Ditto. + * flow.c (verify_local_live_at_start, update_life_info, + update_life_info, initialize_uninitialized_subregs, + propagate_one_insn, init_propagate_block_info, + init_propagate_block_info, regno_clobbered_at_setjmp, + rest_of_handle_life): Ditto. + * modulo-sched.c (sms_schedule): Ditto. + * web.c (union_defs, entry_register, web_main): Ditto. + * global.c (global_alloc, global_conflicts, mark_elimination, + build_insn_chain, rest_of_handle_global_alloc): Ditto. + * ifcvt.c (find_if_case_1, if_convert): Ditto. + * loop_invariant.c (find_defs, record_use, find_invariant_insn, + find_invariants_to_move, move_invariant_reg, move_invariant_reg, + free_inv_motion_data, move_loop_invariants): + * bb-reorder.c (rest_of_handle_reorder_blocks): Ditto. + * reg-stack.c (reg_to_stack): Ditto. + * postreload.c (reload_combine): Changed set reference to + reflect different dataflow problem. + * reload.c (push_reload, find_dummy_reload): Ditto. + * postreload.c (rest_of_handle_postreload): Fixed typo in comment. + * tree-pass.h: Added pass_reset_df_after_reload. + * df.c: Removed, rewritten as df-core.c, df-problems.c and df-scan.c. + * df-scan.c: The scanning fuctions, once in df.c, completely + rewritten so that they now fully model the functionality of + register usage at the backend. + * cfgrtl.c (dump_regset_in, dump_regset_out): New dataflow + * print functions. + (rtl_dump_bb, print_rtl_with_bb): Changed to use + dump_regset_in, dump_regset_out). + * sched_deps (sched_analyze_1): Changed to used renamed + df_read_modify_subreg_p (was read_modify_subreg_p). + (df_scan_free_internal, df_scan_get_bb_info, + df_scan_set_bb_info, df_scan_free_bb_info, df_scan_alloc, + df_scan_free, df_scan_dump, df_scan_add_problem, + df_grow_reg_info, df_grow_ref_info, df_grow_insn_info, + df_rescan_blocks, df_ref_create, df_get_artificial_defs, + df_get_artificial_uses, df_reg_chain_create, df_ref_unlink, + df_reg_chain_unlink, df_ref_remove, + df_insn_create_insn_record, df_insn_refs_delete, + df_refs_delete, df_reorganize_refs, df_reg_use_gen, + df_reg_def_gen, df_set_state, df_ref_create_structure, + df_ref_record, df_read_modify_subreg_p, df_def_record_1, + df_defs_record, df_uses_record, df_insn_contains_asm, + df_insn_refs_record, df_bb_refs_record, df_refs_record, + df_mark_reg, df_record_exit_block_uses, df_hard_reg_init): New functions. + * df-core.c: The core dataflow solver and glue routines for + rtl dataflow. + (df_init, df_add_problem, df_set_blocks, df_finish, + df_hybrid_search_forward, df_hybrid_search_backward, + df_iterative_dataflow, df_prune_to_subcfg, + df_analyze_problem, df_analyze, df_get_bb_info, + df_set_bb_info, df_analyze_simple_change_some_blocks, + df_analyze_simple_change_one_block, df_compact_blocks, + df_bb_replace, df_bb_regno_last_use_find, + df_bb_regno_first_def_find, df_bb_regno_last_def_find, + df_insn_regno_def_p, df_find_def, df_find_use, df_dump, + df_refs_chain_dump, df_regs_chain_dump, df_insn_debug, + df_insn_debug_regno, df_regno_debug, df_ref_debug, + debug_df_insn, debug_df_reg, debug_df_regno, debug_df_ref + debug_df_defno, debug_df_useno, reset_df_after_reload): New functions. + * df-problems.c: Seven concrete dataflow problems that use the scanning + in df-scan.c and are solved by the engine in df-core.c. + (df_get_dependent_problem, df_chain_create, + df_chain_unlink, df_chain_copy, df_get_live_in, + df_get_live_out, df_grow_bb_info, df_chain_dump, + df_print_bb_index, df_ref_bitmap, df_set_seen, + df_unset_seen, df_ru_get_bb_info, df_ru_set_bb_info, + df_ru_free_bb_info, df_ru_alloc, + df_ru_bb_local_compute_process_def, + df_ru_bb_local_compute_process_use, df_ru_bb_local_compute, + df_ru_local_compute, df_ru_init_solution, + df_ru_confluence_n, df_ru_transfer_function, df_ru_free, + df_ru_dump, df_ru_add_problem, df_rd_get_bb_info, + df_rd_set_bb_info, df_rd_free_bb_info, df_rd_alloc, + df_rd_bb_local_compute_process_def, df_rd_bb_local_compute, + df_rd_local_compute, df_rd_init_solution, + df_rd_confluence_n, df_rd_transfer_function, df_rd_free, + df_rd_dump, df_rd_add_problem, df_lr_get_bb_info, + df_lr_set_bb_info, df_lr_free_bb_info, df_lr_alloc, + df_lr_bb_local_compute, df_lr_local_compute, df_lr_init, + df_lr_confluence_0, df_lr_confluence_n, + df_lr_transfer_function, df_lr_free, df_lr_dump, + df_lr_add_problem, df_ur_get_bb_info, df_ur_set_bb_info, + df_ur_free_bb_info, df_ur_alloc, df_ur_bb_local_compute, + df_ur_local_compute, df_ur_init, df_ur_local_finalize, + df_ur_confluence_n, df_ur_transfer_function, df_ur_free, + df_ur_dump, df_ur_add_problem, df_urec_get_bb_info, + df_urec_set_bb_info, df_urec_free_bb_info, df_urec_alloc, + df_urec_mark_reg_change, df_urec_check_earlyclobber, + df_urec_mark_reg_use_for_earlyclobber, + df_urec_mark_reg_use_for_earlyclobber_1, + df_urec_bb_local_compute, df_urec_local_compute, + df_urec_init, df_urec_local_finalize, df_urec_confluence_n, + df_urec_transfer_function, df_urec_free, df_urec_dump, + df_urec_add_problem, df_chain_alloc, + df_chain_create_bb_process_use, df_chain_create_bb, + df_chain_finalize, df_chain_free, df_chains_dump, + df_chain_add_problem, df_ri_alloc, df_ri_bb_compute, + df_ri_compute, df_ri_free, df_ri_dump, df_ri_add_problem, + df_reg_lifetime) New functions. + df.h: Complete rewrite to support new df-problems.c, df-scan.c and + df-core.c. + +2005-12-14 Kenneth Zadeck <zadeck@naturalbridge.com> + + * bitmap.c (bitmap_clear_range): Removed extra debugging. + +2005-12-12 Paolo Bonzini <bonzini@gnu.org> + + * rtl.h (find_occurrence): Declare. + * rtlanal.c (find_occurrence): New. + (loc_mentioned_in_p): Accept a NULL value for IN. + (commutative_operand_precedence): Remove useless code looking + for constant RTX_EXTRA rtxen. + * common.opt (-fforward-propagate): New. + * tree-pass.h: Add forward propagation passes. + * passes.c: Ditto. + * timevar.def: Add forward propagation timevar. + * fwprop.c: New file. + * Makefile.in: Add fwprop.o dependencies. + +2005-11-28 Paolo Bonzini <bonzini@gnu.org> + + * config/sh/sh.c (sh_output_mi_thunk): Use df_finish. + +2005-11-17 Paolo Bonzini <bonzini@gnu.org> + + * df.h (struct df_reach): Add size field. + * df.c (df_realloc_reach_bitmaps, df_renumber_refs): New. + (df_reg_use_chain_create, df_reg_def_chain_create): Use them. + Fix GNU coding standards compliancy. + +2005-11-11 Richard Earnshaw <richard.earnshaw@arm.com> + + * df.c (df_chain_create): Move declaration of bb outside define + for ENABLE_CHECKING. + +2005-11-08 Paolo Bonzini <bonzini@gnu.org> + + * df.c (df_local_ref_killed_between_p): Reinstate. + +2005-11-08 Daniel Berlin <dberlin@dberlin.org> + Kenneth Zadeck <zadeck@naturalbridge.com> + + * bitmap.c (bitmap_element_free, bitmap_element_link, bitmap_and, + bitmap_and_compl, bitmap_ior, bitmap_ior_into, bitmap_xor, + bitmap_xor_into): Fixed latent performance issue where current + was being properly kept up to date. + (bitmap_clear_range, bitmap_compl_and_into): New functions to support + dataflow analysis. + * df.c (df_rd_confluence_n, df_alloc_reach_bitmaps, df_free_reach_bitmaps, + df_clear_reach_bitmaps, df_ru_confluence_n, df_lr_confluence_0, + df_lr_confluence_n, df_init, df_dump): Changed the way EH_USES is processed. + (df_bitmaps_alloc, df_bitmaps_free, + df_bb_reg_def_chain_create, df_bb_reg_def_chain_create, + df_reg_use_chain_create, df_rd_transfer_function, + df_alloc_reach_bitmaps, df_ref_bitmap, df_bb_rd_local_compute, + df_rd_local_compute, df_bb_ru_local_compute, df_analyze, + df_analyze_subcfg, df_analyze_simple_change_some_blocks): Fixed + performance problems by grouping all defs and uses + for the same pseudo into contigious ranges. + (df_bb_du_chain_create, df_bb_ud_chain_create): Combined in + new function df_bb_chain_create. + (df_chain_create, df_alloc_reach_bitmaps, df_free_reach_bitmaps, + df_clear_reach_bitmaps, df_ref_bitmap): New function. + (df_rd_init, df_rd_confluence_n, df_rd_transfer_function, + df_rd_set_dflow, df_ru_transfer_function, df_ru_confluence_n, + df_ru_transfer_function, df_ru_set_dflow, df_lr_transfer_function, + df_lr_set_dflow, df_lr_confluence_n, df_ur_transfer_function, + df_ur_set_dflow, df_ur_init, df_ur_transfer_function, df_ur_set_dflow + df_ur_confluence_n, hybrid_search_forward, hybrid_search_backward + iterative_dataflow): Changed interface for dataflow callback + functions to hide where the source of bitmaps. + (df_rd_set_bitmaps, df_ru_set_bitmaps, df_lr_set_bitmaps, + df_ur_set_bitmaps, df_bitmap_ior_edge): Removed as part of + changing dataflow callback interface. + (df_free, df_ref_create, df_ref_record_1, + df_record_exit_block_uses, df_refs_record, df_exit_lr_local_compute, + df_insn_move_before, df_local_ref_killed_between_p, + df_bb_regno_last_def_find, df_chain_dump, df_dump): + Added chains that go to artifical uses in exit block. + (df_exit_bb_reg_use_chain_create, df_exit_bb_chain_create): + New functions to support artifical uses in exit block. + (df_insn_refs_record): Added new DF_REF_CLOBBER ref type to + avoid creation of artifical chains for clobbers at call sites. + +2005-11-03 Paolo Bonzini <bonzini@gnu.org> + Steven Bosscher <stevenb@suse.de> + + * df.c (df_free): Fix call to df_bitmaps_free. + (df_bb_regno_last_def_find): Make it return what it is supposed + to return. + (df_local_ref_killed_between_p): New. + +2005-11-01 Daniel Berlin <dberlin@dberlin.org> + Kenneth Zadeck <zadeck@naturalbridge.com> + + * flow.c (update_life_info): Added debugging. + (init_propagate_block_info): Fixed typo. + * df.c (df_refs_unlink, df_ref_record_1, df_ref_record, + read_modify_subreg_p, df_def_record_1, df_insn_refs_record): + Fixed control of when regs_ever_live is modified. + * combine.c: Documentation rot. + * reload1.c: Used wrong type of comment. + +2005-10-31 Jan Hubicka <jh@suse.cz> + + * reg-stack.c (subst_stack_regs_pat): Expect USEs to be ignored for + liveness. + (change_stack): Initialize partially dead registers. + (convert_regs_1): Handle dead return values. + (reg_to_stack): Switch to partial liveness. + (propagate_stack): Push in partially live stuff. + +2005-10-25 Daniel Berlin <dberlin@dberlin.org> + Kenneth Zadeck <zadeck@naturalbridge.com> + + * cfgrtl.c (print_rtl_with_bb): Fixed problem with debugging + output of bitmaps. + * df.c (mark_reg_change): Fixed problem computing sizes of hard regs. + * reload1.c (reload): Fixed over agressive updating of bitmaps. + * sched-rgn.c (schedule_insns): Cleaned away some useless verification + code. + (rest_of_handle_sched2): Removed debugging. + + +2005-10-24 Kenneth Zadeck <zadeck@naturalbridge.com> + + * sched-rgn.c (check_live_1, update_live_1, rest_of_handle_sched): + Converted to use partially available liveness. + (init_regions, schedule_insns, rest_of_handle_sched): + Removed assertion checking based on death notes. + + +2005-10-23 Daniel Berlin <dberlin@dberlin.org> + Kenneth Zadeck <zadeck@naturalbridge.com> + + * df.c (df_bitmaps_alloc, df_lr_local_compute, df_compute_all_blocks): + Removed df->all_blocks. + (df_lr_local_compute): Added flag to indicate that + blocks was really the whole function. + (df_ref_record): Fixed missing case for memory refs. + (df_insn_refs_record): Fixed regs ever live processing. + (df_bb_refs_record): Removed debugging code. + (df_bb_lr_local_compute, notice_stack_pointer_modification, + df_bb_lr_local_compute): Moved stack pointer modification + detection code back to flow.c. + (df_bb_lr_local_compute, hybrid_search_forward, + hybrid_search_backward): Formatting cleanup. + (df_compute_all_blocks, notice_stack_pointer_modification): + Removed. + (df_analyze): Changed the definition of whole program to be all + reachable blocks rather than all blocks. + (df_analyze_subcfg, df_analyze_simple_change_some_blocks): Added + parameter to df_lr_local_compute. + (df_rtx_reg_replace): Fixed way it decided it was processing + entire function. + * df.h: Removed all_blocks from struct df definition. + * flow.c (notice_stack_pointer_modification_1, + notice_stack_pointer_modification): Added back. + (life_analysis): Added back call to + notice_stack_pointer_modification. + (struct tree_opt_pass pass_life, rest_of_handle_flow2): + Added debugging. + * gcse.c (gcse_main, bypass_jumps): Additional places where we had missed + in renumbering entry and exit blocks. + * global.c (global_alloc): Additional debugging code. + + +2005-10-19 Daniel Berlin <dberlin@dberlin.org> + + * df.c (df_lr_local_compute): Fix thinko regarding pseudos and call + defs. + +2005-10-19 Kenneth Zadeck <zadeck@naturalbridge.com> + + * df.c (df_bitmap_ior_edge, df_lr_confluence_0 + df_lr_confluence_n, df_ur_confluence_n, df_ur_set_bitmaps): New + functions. + (df_exit_lr_local_compute, dataflow_set_a_op_b): Removed + functions. + (df_ur_set_bitmaps, mark_reg_change, df_ur_local_finalize, + mark_reg_use_for_earlyclobber, df_ur_local_compute, + df_bb_reg_live_start, df_bb_reg_live_end, df_bb_reg_live_start_p, + df_bb_reg_live_end_p): Renamed "reaching registers" + problem (rr) to "uninitialized registers" problem. + (df_free): Changed variables freed. + (df_ref_record): Added more sophisticated hard register scan. + (df_insn_refs_record): Changed way invalidated_by_call is + processed. + (df_bb_refs_record, df_exit_lr_local_compute, + df_bb_lr_local_compute): Removed bad way to simulate edge functions. + There are now real confluence functions. + (notice_stack_pointer_modification): Disabled. + (df_bb_lr_local_compute): Disabled call to + notice_stack_pointer_modification. Added code to properly handle hard + regs at call site. + (df_lr_local_compute): Added more hard reg processing. + Changed order exit block is processed. + (df_init): Added some invariant hard regs initialization. + (df_refs_process): Removed artifical special case. + (df_print_bb_index, df_dump): Removed parameter to df_dump. + (hybrid_search_forward, hybrid_search_backward): Added + confluence function calls. + (iterative_dataflow): Added hack to initialize in and out sets + with hard registers. This will be removed later. + * df.h: Removed DF_ARTIFICIAL_DEFS. Renamed "reaching registers". + problem (rr) to "uninitialized registers" problem. Changed confluence + operator to general confluence functions. + * flow.c (verify_wide_reg, verify_local_live_at_start): Removed + parameter to df_dump. + * reg_stack.c (reg_to_stack): Ditto. + * sched_rgn.c (init_regions, schedule_insns): Ditto. + * web.c (web_main): Ditto. + * flow.c: (update_life_info): Removed DF_ARTIFICIAL_DEFS. + * global.c (global_alloc): Ditto. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 4e75701..2c87de7 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -749,7 +749,7 @@ EXPR_H = expr.h insn-config.h $(FUNCTION_H) $(RTL_H) $(FLAGS_H) $(TREE_H) $(MACH OPTABS_H = optabs.h insn-codes.h REGS_H = regs.h varray.h $(MACHMODE_H) $(OBSTACK_H) $(BASIC_BLOCK_H) $(FUNCTION_H) RESOURCE_H = resource.h hard-reg-set.h -SCHED_INT_H = sched-int.h $(INSN_ATTR_H) $(BASIC_BLOCK_H) $(RTL_H) +SCHED_INT_H = sched-int.h $(INSN_ATTR_H) $(BASIC_BLOCK_H) $(RTL_H) $(DF_H) INTEGRATE_H = integrate.h $(VARRAY_H) CFGLAYOUT_H = cfglayout.h $(BASIC_BLOCK_H) CFGLOOP_H = cfgloop.h $(BASIC_BLOCK_H) $(RTL_H) vecprim.h double-int.h @@ -758,6 +758,7 @@ IPA_REFERENCE_H = ipa-reference.h bitmap.h $(TREE_H) IPA_TYPE_ESCAPE_H = ipa-type-escape.h $(TREE_H) CGRAPH_H = cgraph.h $(TREE_H) DF_H = df.h bitmap.h $(BASIC_BLOCK_H) alloc-pool.h +RESOURCE_H = resource.h hard-reg-set.h $(DF_H) DDG_H = ddg.h sbitmap.h $(DF_H) GCC_H = gcc.h version.h GGC_H = ggc.h gtype-desc.h @@ -790,6 +791,7 @@ TREE_DATA_REF_H = tree-data-ref.h $(LAMBDA_H) omega.h VARRAY_H = varray.h $(MACHMODE_H) $(SYSTEM_H) coretypes.h $(TM_H) TREE_INLINE_H = tree-inline.h $(VARRAY_H) pointer-set.h REAL_H = real.h $(MACHMODE_H) +DBGCNT_H = dbgcnt.h dbgcnt.def EBIMAP_H = ebitmap.h sbitmap.h # @@ -953,6 +955,7 @@ OBJS-common = \ $(GGC) \ alias.o \ alloc-pool.o \ + auto-inc-dec.o \ bb-reorder.o \ bitmap.o \ bt-load.o \ @@ -977,6 +980,8 @@ OBJS-common = \ cse.o \ cselib.o \ dbxout.o \ + dbgcnt.o \ + dce.o \ ddg.o \ debug.o \ df-core.o \ @@ -988,6 +993,7 @@ OBJS-common = \ dominance.o \ domwalk.o \ double-int.o \ + dse.o \ dwarf2asm.o \ dwarf2out.o \ ebitmap.o \ @@ -998,7 +1004,6 @@ OBJS-common = \ expmed.o \ expr.o \ final.o \ - flow.o \ fold-const.o \ function.o \ fwprop.o \ @@ -1014,6 +1019,7 @@ OBJS-common = \ haifa-sched.o \ hooks.o \ ifcvt.o \ + init-regs.o \ integrate.o \ intl.o \ jump.o \ @@ -1055,6 +1061,7 @@ OBJS-common = \ regclass.o \ regmove.o \ regrename.o \ + regstat.o \ reload.o \ reload1.o \ reorg.o \ @@ -1073,6 +1080,7 @@ OBJS-common = \ see.o \ simplify-rtx.o \ sreal.o \ + stack-ptr-mod.o \ stmt.o \ stor-layout.o \ stringpool.o \ @@ -2016,7 +2024,8 @@ tree-cfgcleanup.o : tree-cfgcleanup.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \ tree-ssa-propagate.h rtl-factoring.o : rtl-factoring.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) \ coretypes.h $(TM_H) $(BASIC_BLOCK_H) $(GGC_H) $(REGS_H) $(PARAMS_H) $(EXPR_H) \ - addresses.h $(TM_P_H) tree-pass.h $(TREE_FLOW_H) $(TIMEVAR_H) output.h + addresses.h $(TM_P_H) tree-pass.h $(TREE_FLOW_H) $(TIMEVAR_H) output.h \ + $(DF_H) tree-tailcall.o : tree-tailcall.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \ $(RTL_H) $(TREE_H) $(TM_P_H) $(FUNCTION_H) $(TM_H) coretypes.h \ $(TREE_DUMP_H) $(DIAGNOSTIC_H) except.h tree-pass.h $(FLAGS_H) langhooks.h \ @@ -2227,7 +2236,7 @@ diagnostic.o : diagnostic.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ opts.o : opts.c opts.h options.h toplev.h $(CONFIG_H) $(SYSTEM_H) \ coretypes.h $(TREE_H) $(TM_H) langhooks.h $(GGC_H) $(RTL_H) \ output.h $(DIAGNOSTIC_H) $(TM_P_H) $(INSN_ATTR_H) intl.h $(TARGET_H) \ - $(FLAGS_H) $(PARAMS_H) tree-pass.h + $(FLAGS_H) $(PARAMS_H) tree-pass.h $(DBGCNT_H) opts-common.o : opts-common.c opts.h $(CONFIG_H) $(SYSTEM_H) \ coretypes.h intl.h targhooks.o : targhooks.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TREE_H) \ @@ -2257,7 +2266,7 @@ passes.o : passes.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \ langhooks.h insn-flags.h $(CFGLAYOUT_H) $(REAL_H) $(CFGLOOP_H) \ hosthooks.h $(CGRAPH_H) $(COVERAGE_H) tree-pass.h $(TREE_DUMP_H) \ $(GGC_H) $(INTEGRATE_H) $(CPPLIB_H) opts.h $(TREE_FLOW_H) $(TREE_INLINE_H) \ - gt-passes.h $(PREDICT_H) + gt-passes.h $(DF_H) $(PREDICT_H) main.o : main.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) toplev.h @@ -2276,7 +2285,8 @@ print-rtl.o : print-rtl.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(BCONFIG_H) $(REAL_H) rtlanal.o : rtlanal.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) toplev.h \ $(RTL_H) hard-reg-set.h $(TM_P_H) insn-config.h $(RECOG_H) $(REAL_H) \ - $(FLAGS_H) $(REGS_H) output.h $(TARGET_H) $(FUNCTION_H) $(TREE_H) + $(FLAGS_H) $(REGS_H) output.h $(TARGET_H) $(FUNCTION_H) $(TREE_H) \ + $(DF_H) varasm.o : varasm.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \ $(RTL_H) $(FLAGS_H) $(FUNCTION_H) $(EXPR_H) hard-reg-set.h $(REGS_H) \ @@ -2288,7 +2298,7 @@ function.o : function.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(OPTABS_H) libfuncs.h $(REGS_H) hard-reg-set.h insn-config.h $(RECOG_H) \ output.h toplev.h except.h $(HASHTAB_H) $(GGC_H) $(TM_P_H) langhooks.h \ gt-function.h $(TARGET_H) $(BASIC_BLOCK_H) $(INTEGRATE_H) $(PREDICT_H) \ - tree-pass.h vecprim.h + tree-pass.h $(DF_H) timevar.h vecprim.h stmt.o : stmt.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(TREE_H) $(FLAGS_H) $(FUNCTION_H) insn-config.h hard-reg-set.h $(EXPR_H) \ libfuncs.h except.h $(RECOG_H) toplev.h output.h $(GGC_H) $(TM_P_H) \ @@ -2306,7 +2316,7 @@ expr.o : expr.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ typeclass.h hard-reg-set.h toplev.h hard-reg-set.h except.h reload.h \ $(GGC_H) langhooks.h intl.h $(TM_P_H) $(REAL_H) $(TARGET_H) \ tree-iterator.h gt-expr.h $(MACHMODE_H) $(TIMEVAR_H) $(TREE_FLOW_H) \ - tree-pass.h + tree-pass.h $(DF_H) dojump.o : dojump.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \ $(FLAGS_H) $(FUNCTION_H) $(EXPR_H) $(OPTABS_H) $(INSN_ATTR_H) insn-config.h \ langhooks.h $(GGC_H) gt-dojump.h @@ -2319,10 +2329,10 @@ builtins.o : builtins.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ calls.o : calls.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(TREE_H) $(FLAGS_H) $(EXPR_H) $(OPTABS_H) langhooks.h $(TARGET_H) \ libfuncs.h $(REGS_H) toplev.h output.h $(FUNCTION_H) $(TIMEVAR_H) $(TM_P_H) \ - $(CGRAPH_H) except.h sbitmap.h + $(CGRAPH_H) except.h sbitmap.h $(DBGCNT_H) expmed.o : expmed.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \ $(FLAGS_H) insn-config.h $(EXPR_H) $(OPTABS_H) $(RECOG_H) $(REAL_H) \ - toplev.h $(TM_P_H) langhooks.h $(TARGET_H) + toplev.h $(TM_P_H) langhooks.h $(DF_H) $(TARGET_H) explow.o : explow.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \ $(FLAGS_H) hard-reg-set.h insn-config.h $(EXPR_H) $(OPTABS_H) $(RECOG_H) \ toplev.h $(FUNCTION_H) $(GGC_H) $(TM_P_H) langhooks.h gt-explow.h \ @@ -2358,7 +2368,7 @@ emit-rtl.o : emit-rtl.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(TREE_H) $(FLAGS_H) $(FUNCTION_H) $(REGS_H) insn-config.h $(RECOG_H) \ $(GGC_H) $(EXPR_H) hard-reg-set.h bitmap.h toplev.h $(BASIC_BLOCK_H) \ $(HASHTAB_H) $(TM_P_H) debug.h langhooks.h tree-pass.h gt-emit-rtl.h \ - $(REAL_H) + $(REAL_H) $(DF_H) real.o : real.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \ toplev.h $(TM_P_H) $(REAL_H) dfp.o : dfp.c dfp.h $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \ @@ -2367,7 +2377,7 @@ integrate.o : integrate.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_H) $(TREE_H) $(FLAGS_H) debug.h $(INTEGRATE_H) insn-config.h \ $(EXPR_H) $(REAL_H) $(REGS_H) intl.h $(FUNCTION_H) output.h $(RECOG_H) \ except.h toplev.h $(PARAMS_H) $(TM_P_H) $(TARGET_H) langhooks.h \ - gt-integrate.h $(GGC_H) tree-pass.h + gt-integrate.h $(GGC_H) tree-pass.h $(DF_H) jump.o : jump.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(FLAGS_H) hard-reg-set.h $(REGS_H) insn-config.h $(RECOG_H) $(EXPR_H) \ $(REAL_H) except.h $(FUNCTION_H) tree-pass.h $(DIAGNOSTIC_H) \ @@ -2441,7 +2451,15 @@ cselib.o : cselib.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ cse.o : cse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \ hard-reg-set.h $(FLAGS_H) insn-config.h $(RECOG_H) $(EXPR_H) toplev.h \ output.h $(FUNCTION_H) $(BASIC_BLOCK_H) $(GGC_H) $(TM_P_H) $(TIMEVAR_H) \ - except.h $(TARGET_H) $(PARAMS_H) rtlhooks-def.h tree-pass.h $(REAL_H) + except.h $(TARGET_H) $(PARAMS_H) rtlhooks-def.h tree-pass.h $(REAL_H) \ + $(DF_H) $(DBGCNT_H) +dce.o : dce.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ + $(TREE_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) $(DF_H) cselib.h \ + $(DBGCNT_H) dce.h timevar.h tree-pass.h $(DBGCNT_H) +dse.o : dse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ + $(TREE_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) insn-config.h $(RECOG_H) \ + $(EXPR_H) $(DF_H) cselib.h $(DBGCNT_H) timevar.h tree-pass.h \ + alloc-pool.h $(ALIAS_H) dse.h fwprop.o : fwprop.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ toplev.h insn-config.h $(RECOG_H) $(FLAGS_H) $(OBSTACK_H) $(BASIC_BLOCK_H) \ output.h $(DF_H) alloc-pool.h $(TIMEVAR_H) tree-pass.h @@ -2455,9 +2473,9 @@ gcse.o : gcse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(REGS_H) hard-reg-set.h $(FLAGS_H) $(REAL_H) insn-config.h $(GGC_H) \ $(RECOG_H) $(EXPR_H) $(BASIC_BLOCK_H) $(FUNCTION_H) output.h toplev.h \ $(TM_P_H) $(PARAMS_H) except.h gt-gcse.h $(TREE_H) cselib.h $(TIMEVAR_H) \ - intl.h $(OBSTACK_H) tree-pass.h + intl.h $(OBSTACK_H) tree-pass.h $(DF_H) $(DBGCNT_H) resource.o : resource.c $(CONFIG_H) $(RTL_H) hard-reg-set.h $(SYSTEM_H) \ - coretypes.h $(TM_H) $(REGS_H) $(FLAGS_H) output.h $(RESOURCE_H) \ + coretypes.h $(TM_H) $(REGS_H) $(FLAGS_H) output.h $(RESOURCE_H) $(DF_H) \ $(FUNCTION_H) toplev.h $(INSN_ATTR_H) except.h $(PARAMS_H) $(TM_P_H) lcm.o : lcm.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \ hard-reg-set.h $(FLAGS_H) insn-config.h $(INSN_ATTR_H) $(RECOG_H) \ @@ -2465,7 +2483,7 @@ lcm.o : lcm.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \ mode-switching.o : mode-switching.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(TM_H) $(RTL_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) insn-config.h \ $(INSN_ATTR_H) $(RECOG_H) $(BASIC_BLOCK_H) $(TM_P_H) $(FUNCTION_H) \ - output.h tree-pass.h $(TIMEVAR_H) $(REAL_H) + output.h tree-pass.h $(TIMEVAR_H) $(REAL_H) $(DF_H) tree-ssa-dce.o : tree-ssa-dce.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) \ $(RTL_H) $(TM_P_H) $(TREE_FLOW_H) $(DIAGNOSTIC_H) $(TIMEVAR_H) $(TM_H) \ coretypes.h $(TREE_DUMP_H) tree-pass.h $(FLAGS_H) $(BASIC_BLOCK_H) \ @@ -2496,11 +2514,14 @@ df-core.o : df-core.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ df-problems.o : df-problems.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) bitmap.h sbitmap.h $(TM_P_H) \ - $(FLAGS_H) output.h vecprim.h + $(FLAGS_H) output.h except.h dce.h vecprim.h df-scan.o : df-scan.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) bitmap.h sbitmap.h $(TM_P_H) \ $(FLAGS_H) $(TARGET_H) $(TARGET_DEF_H) $(TREE_H) output.h tree-pass.h +regstat.o : regstat.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ + $(TM_P_H) $(FLAGS_H) $(REGS_H) output.h except.h hard-reg-set.h \ + $(BASIC_BLOCK_H) $(TIMEVAR_H) $(DF_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) \ @@ -2522,14 +2543,14 @@ loop-doloop.o : loop-doloop.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_H) $(FLAGS_H) $(EXPR_H) hard-reg-set.h $(BASIC_BLOCK_H) $(TM_P_H) \ toplev.h $(CFGLOOP_H) output.h $(PARAMS_H) $(TARGET_H) alloc-pool.o : alloc-pool.c $(CONFIG_H) $(SYSTEM_H) alloc-pool.h $(HASHTAB_H) -flow.o : flow.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ - $(TREE_H) $(FLAGS_H) insn-config.h $(BASIC_BLOCK_H) $(REGS_H) \ - hard-reg-set.h output.h toplev.h $(RECOG_H) $(FUNCTION_H) except.h \ - $(EXPR_H) $(TM_P_H) $(OBSTACK_H) $(SPLAY_TREE_H) $(TIMEVAR_H) tree-pass.h +auto-inc-dec.o : auto-inc-dec.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ + $(TREE_H) $(RTL_H) $(TM_P_H) hard-reg-set.h $(BASIC_BLOCK_H) insn-config.h \ + $(REGS_H) $(FLAGS_H) output.h $(FUNCTION_H) except.h toplev.h $(RECOG_H) \ + $(EXPR_H) $(TIMEVAR_H) tree-pass.h $(DF_H) $(DBGCNT_H) cfg.o : cfg.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(FLAGS_H) \ $(REGS_H) hard-reg-set.h output.h toplev.h $(FUNCTION_H) except.h $(GGC_H) \ - $(TM_P_H) $(TIMEVAR_H) $(OBSTACK_H) $(TREE_H) alloc-pool.h $(HASHTAB_H) \ - $(CFGLOOP_H) + $(TM_P_H) $(TIMEVAR_H) $(OBSTACK_H) $(TREE_H) alloc-pool.h \ + $(HASHTAB_H) $(DF_H) $(CFGLOOP_H) cfghooks.o: cfghooks.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(TREE_H) $(BASIC_BLOCK_H) $(TREE_FLOW_H) $(TIMEVAR_H) toplev.h $(CFGLOOP_H) cfgexpand.o : cfgexpand.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \ @@ -2541,7 +2562,7 @@ cfgrtl.o : cfgrtl.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(FLAGS_H) insn-config.h $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h \ output.h toplev.h $(FUNCTION_H) except.h $(TM_P_H) insn-config.h $(EXPR_H) \ $(CFGLAYOUT_H) $(CFGLOOP_H) $(OBSTACK_H) $(TARGET_H) $(TREE_H) \ - tree-pass.h + tree-pass.h $(DF_H) cfganal.o : cfganal.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(BASIC_BLOCK_H) hard-reg-set.h insn-config.h $(RECOG_H) $(TM_P_H) \ $(TIMEVAR_H) $(OBSTACK_H) toplev.h @@ -2551,7 +2572,8 @@ cfgbuild.o : cfgbuild.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ cfgcleanup.o : cfgcleanup.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_H) $(TIMEVAR_H) hard-reg-set.h output.h $(FLAGS_H) $(RECOG_H) \ toplev.h insn-config.h cselib.h $(TARGET_H) $(TM_P_H) $(PARAMS_H) \ - $(REGS_H) $(EMIT_RTL_H) $(CFGLAYOUT_H) tree-pass.h $(CFGLOOP_H) $(EXPR_H) + $(REGS_H) $(EMIT_RTL_H) $(CFGLAYOUT_H) tree-pass.h $(CFGLOOP_H) $(EXPR_H) \ + $(DF_H) dce.h cfgloop.o : cfgloop.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) coretypes.h $(TM_H) \ $(BASIC_BLOCK_H) hard-reg-set.h $(CFGLOOP_H) $(FLAGS_H) $(FUNCTION_H) \ $(OBSTACK_H) toplev.h $(TREE_FLOW_H) $(TREE_H) pointer-set.h output.h \ @@ -2564,7 +2586,7 @@ graphds.o : graphds.c graphds.h $(CONFIG_H) $(SYSTEM_H) bitmap.h $(OBSTACK_H) \ struct-equiv.o : struct-equiv.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_H) hard-reg-set.h output.h $(FLAGS_H) $(RECOG_H) \ insn-config.h $(TARGET_H) $(TM_P_H) $(PARAMS_H) \ - $(REGS_H) $(EMIT_RTL_H) + $(REGS_H) $(EMIT_RTL_H) $(DF_H) loop-iv.o : loop-iv.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(BASIC_BLOCK_H) \ hard-reg-set.h $(CFGLOOP_H) $(EXPR_H) coretypes.h $(TM_H) $(OBSTACK_H) \ output.h intl.h $(DF_H) $(HASHTAB_H) @@ -2577,7 +2599,7 @@ cfgloopmanip.o : cfgloopmanip.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) \ coretypes.h $(TM_H) cfghooks.h $(OBSTACK_H) loop-init.o : loop-init.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(GGC_H) \ $(BASIC_BLOCK_H) hard-reg-set.h $(CFGLOOP_H) $(CFGLAYOUT_H) \ - coretypes.h $(TM_H) $(OBSTACK_H) tree-pass.h $(TIMEVAR_H) $(FLAGS_H) + coretypes.h $(TM_H) $(OBSTACK_H) tree-pass.h $(TIMEVAR_H) $(FLAGS_H) $(DF_H) loop-unswitch.o : loop-unswitch.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TM_H) \ $(BASIC_BLOCK_H) hard-reg-set.h $(CFGLOOP_H) $(CFGLAYOUT_H) $(PARAMS_H) \ output.h $(EXPR_H) coretypes.h $(TM_H) $(OBSTACK_H) @@ -2594,53 +2616,53 @@ combine.o : combine.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(FLAGS_H) $(FUNCTION_H) insn-config.h $(INSN_ATTR_H) $(REGS_H) $(EXPR_H) \ rtlhooks-def.h $(BASIC_BLOCK_H) $(RECOG_H) $(REAL_H) hard-reg-set.h \ toplev.h $(TM_P_H) $(TREE_H) $(TARGET_H) output.h $(PARAMS_H) $(OPTABS_H) \ - insn-codes.h $(TIMEVAR_H) tree-pass.h + insn-codes.h $(TIMEVAR_H) tree-pass.h $(DF_H) regclass.o : regclass.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ hard-reg-set.h $(FLAGS_H) $(BASIC_BLOCK_H) $(REGS_H) insn-config.h \ $(RECOG_H) reload.h $(REAL_H) toplev.h $(FUNCTION_H) output.h $(GGC_H) \ $(TM_P_H) $(EXPR_H) $(TIMEVAR_H) gt-regclass.h $(HASHTAB_H) \ - $(TARGET_H) + $(TARGET_H) tree-pass.h $(DF_H) local-alloc.o : local-alloc.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_H) $(FLAGS_H) $(REGS_H) hard-reg-set.h insn-config.h $(RECOG_H) \ output.h $(FUNCTION_H) $(INSN_ATTR_H) toplev.h except.h reload.h $(TM_P_H) \ - $(GGC_H) $(INTEGRATE_H) $(TIMEVAR_H) tree-pass.h + $(GGC_H) $(INTEGRATE_H) $(TIMEVAR_H) tree-pass.h $(DF_H) $(DBGCNT_H) bitmap.o : bitmap.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(FLAGS_H) $(GGC_H) gt-bitmap.h bitmap.h $(OBSTACK_H) global.o : global.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(FLAGS_H) reload.h $(FUNCTION_H) $(RECOG_H) $(REGS_H) hard-reg-set.h \ insn-config.h output.h toplev.h $(TM_P_H) $(MACHMODE_H) tree-pass.h \ - $(TIMEVAR_H) vecprim.h + $(TIMEVAR_H) vecprim.h $(DF_H) varray.o : varray.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(GGC_H) \ $(HASHTAB_H) $(BCONFIG_H) $(VARRAY_H) toplev.h vec.o : vec.c $(CONFIG_H) $(SYSTEM_H) coretypes.h vec.h $(GGC_H) \ toplev.h reload.o : reload.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(FLAGS_H) output.h $(EXPR_H) $(OPTABS_H) reload.h $(RECOG_H) \ - hard-reg-set.h insn-config.h $(REGS_H) $(FUNCTION_H) toplev.h \ - addresses.h $(TM_P_H) $(PARAMS_H) $(TARGET_H) $(REAL_H) + hard-reg-set.h insn-config.h $(REGS_H) $(FUNCTION_H) real.h toplev.h \ + addresses.h $(TM_P_H) $(PARAMS_H) $(TARGET_H) $(REAL_H) $(DF_H) reload1.o : reload1.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(EXPR_H) $(OPTABS_H) reload.h $(REGS_H) hard-reg-set.h insn-config.h \ $(BASIC_BLOCK_H) $(RECOG_H) output.h $(FUNCTION_H) toplev.h $(TM_P_H) \ addresses.h except.h $(TREE_H) $(REAL_H) $(FLAGS_H) $(MACHMODE_H) \ - $(OBSTACK_H) $(TARGET_H) + $(OBSTACK_H) $(DF_H) $(TARGET_H) dse.h rtlhooks.o : rtlhooks.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ rtlhooks-def.h $(EXPR_H) $(RECOG_H) postreload.o : postreload.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_H) $(REAL_H) $(FLAGS_H) $(EXPR_H) $(OPTABS_H) reload.h $(REGS_H) \ hard-reg-set.h insn-config.h $(BASIC_BLOCK_H) $(RECOG_H) output.h \ $(FUNCTION_H) toplev.h cselib.h $(TM_P_H) except.h $(TREE_H) $(MACHMODE_H) \ - $(OBSTACK_H) $(TIMEVAR_H) tree-pass.h + $(OBSTACK_H) $(TIMEVAR_H) tree-pass.h $(DF_H) postreload-gcse.o : postreload-gcse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(TM_H) $(RTL_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) insn-config.h \ $(RECOG_H) $(EXPR_H) $(BASIC_BLOCK_H) $(FUNCTION_H) output.h toplev.h \ $(TM_P_H) except.h $(TREE_H) $(TARGET_H) $(HASHTAB_H) intl.h $(OBSTACK_H) \ - $(PARAMS_H) $(TIMEVAR_H) tree-pass.h $(REAL_H) + $(PARAMS_H) $(TIMEVAR_H) tree-pass.h $(REAL_H) $(DBGCNT_H) caller-save.o : caller-save.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(FLAGS_H) $(REGS_H) hard-reg-set.h insn-config.h $(BASIC_BLOCK_H) $(FUNCTION_H) \ - addresses.h $(RECOG_H) reload.h $(EXPR_H) toplev.h $(TM_P_H) + addresses.h $(RECOG_H) reload.h $(EXPR_H) toplev.h $(TM_P_H) $(DF_H) bt-load.o : bt-load.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) except.h \ $(RTL_H) hard-reg-set.h $(REGS_H) $(TM_P_H) $(FIBHEAP_H) output.h $(EXPR_H) \ - $(TARGET_H) $(FLAGS_H) $(INSN_ATTR_H) $(FUNCTION_H) tree-pass.h toplev.h + $(TARGET_H) $(FLAGS_H) $(INSN_ATTR_H) $(FUNCTION_H) tree-pass.h toplev.h $(DF_H) reorg.o : reorg.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ conditions.h hard-reg-set.h $(BASIC_BLOCK_H) $(REGS_H) insn-config.h \ $(INSN_ATTR_H) except.h $(RECOG_H) $(FUNCTION_H) $(FLAGS_H) output.h \ @@ -2650,9 +2672,15 @@ alias.o : alias.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(FLAGS_H) hard-reg-set.h $(BASIC_BLOCK_H) $(REGS_H) toplev.h output.h \ $(ALIAS_H) $(EMIT_RTL_H) $(GGC_H) $(FUNCTION_H) cselib.h $(TREE_H) $(TM_P_H) \ langhooks.h $(TARGET_H) gt-alias.h $(TIMEVAR_H) $(CGRAPH_H) \ - $(SPLAY_TREE_H) $(VARRAY_H) $(IPA_TYPE_ESCAPE_H) tree-pass.h + $(SPLAY_TREE_H) $(VARRAY_H) $(IPA_TYPE_ESCAPE_H) $(DF_H) tree-pass.h +stack-ptr-mod.o : stack-ptr-mod.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ + $(TM_H) $(TREE_H) $(RTL_H) $(REGS_H) $(EXPR_H) tree-pass.h \ + $(BASIC_BLOCK_H) $(FLAGS_H) output.h $(DF_H) +init-regs.o : init-regs.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ + $(TM_H) $(TREE_H) $(RTL_H) $(REGS_H) $(EXPR_H) tree-pass.h \ + $(BASIC_BLOCK_H) $(FLAGS_H) $(DF_H) regmove.o : regmove.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ - insn-config.h $(TIMEVAR_H) tree-pass.h \ + insn-config.h $(TIMEVAR_H) tree-pass.h $(DF_H)\ $(RECOG_H) output.h $(REGS_H) hard-reg-set.h $(FLAGS_H) $(FUNCTION_H) \ $(EXPR_H) $(BASIC_BLOCK_H) toplev.h $(TM_P_H) except.h reload.h combine-stack-adj.o : combine-stack-adj.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ @@ -2662,30 +2690,30 @@ combine-stack-adj.o : combine-stack-adj.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ ddg.o : ddg.c $(DDG_H) $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TARGET_H) \ toplev.h $(RTL_H) $(TM_P_H) $(REGS_H) $(FUNCTION_H) \ $(FLAGS_H) insn-config.h $(INSN_ATTR_H) except.h $(RECOG_H) \ - $(SCHED_INT_H) $(CFGLAYOUT_H) $(CFGLOOP_H) $(EXPR_H) bitmap.h $(DF_H) \ + $(SCHED_INT_H) $(CFGLAYOUT_H) $(CFGLOOP_H) $(EXPR_H) bitmap.h \ hard-reg-set.h sbitmap.h $(TM_H) modulo-sched.o : modulo-sched.c $(DDG_H) $(CONFIG_H) $(CONFIG_H) $(SYSTEM_H) \ coretypes.h $(TARGET_H) toplev.h $(RTL_H) $(TM_P_H) $(REGS_H) $(FUNCTION_H) \ $(FLAGS_H) insn-config.h $(INSN_ATTR_H) except.h $(RECOG_H) \ $(SCHED_INT_H) $(CFGLAYOUT_H) $(CFGLOOP_H) $(EXPR_H) $(PARAMS_H) \ - cfghooks.h $(DF_H) $(GCOV_IO_H) hard-reg-set.h $(TM_H) $(TIMEVAR_H) \ - tree-pass.h -haifa-sched.o : haifa-sched.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ - $(RTL_H) $(SCHED_INT_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) insn-config.h \ - $(FUNCTION_H) $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h $(TM_P_H) \ - $(TARGET_H) output.h $(PARAMS_H) + cfghooks.h $(GCOV_IO_H) hard-reg-set.h $(TM_H) timevar.h tree-pass.h \ + $(DF_H) +haifa-sched.o : haifa-sched.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ + $(SCHED_INT_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) insn-config.h $(FUNCTION_H) \ + $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h $(TM_P_H) $(TARGET_H) output.h \ + $(PARAMS_H) $(DBGCNT_H) sched-deps.o : sched-deps.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_H) $(SCHED_INT_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) insn-config.h \ $(FUNCTION_H) $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h cselib.h \ - $(PARAMS_H) $(TM_P_H) $(DF_H) + $(PARAMS_H) $(TM_P_H) sched-rgn.o : sched-rgn.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_H) $(SCHED_INT_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) insn-config.h \ $(FUNCTION_H) $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h $(PARAMS_H) \ - $(TM_P_H) $(TARGET_H) $(CFGLAYOUT_H) $(TIMEVAR_H) tree-pass.h + $(TM_P_H) $(TARGET_H) $(CFGLAYOUT_H) $(TIMEVAR_H) tree-pass.h $(DBGCNT_H) sched-ebb.o : sched-ebb.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_H) $(SCHED_INT_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) insn-config.h \ $(FUNCTION_H) $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h $(TM_P_H) \ - $(PARAMS_H) $(CFGLAYOUT_H) $(TARGET_H) output.h + $(PARAMS_H) $(CFGLAYOUT_H) $(TARGET_H) output.h sched-vis.o : sched-vis.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_H) $(SCHED_INT_H) hard-reg-set.h $(BASIC_BLOCK_H) $(OBSTACK_H) \ $(TM_P_H) $(REAL_H) toplev.h tree-pass.h @@ -2694,16 +2722,17 @@ final.o : final.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ insn-config.h $(INSN_ATTR_H) $(FUNCTION_H) output.h hard-reg-set.h \ except.h debug.h xcoffout.h toplev.h reload.h dwarf2out.h tree-pass.h \ $(BASIC_BLOCK_H) $(TM_P_H) $(TARGET_H) $(EXPR_H) $(CFGLAYOUT_H) dbxout.h \ - $(TIMEVAR_H) $(CGRAPH_H) $(COVERAGE_H) $(REAL_H) vecprim.h + $(TIMEVAR_H) $(CGRAPH_H) $(COVERAGE_H) $(REAL_H) $(DF_H) vecprim.h recog.o : recog.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(FUNCTION_H) $(BASIC_BLOCK_H) $(REGS_H) $(RECOG_H) $(EXPR_H) \ $(FLAGS_H) insn-config.h $(INSN_ATTR_H) toplev.h output.h reload.h \ - addresses.h $(TM_P_H) $(TIMEVAR_H) tree-pass.h hard-reg-set.h $(REAL_H) + addresses.h $(TM_P_H) $(TIMEVAR_H) tree-pass.h hard-reg-set.h $(REAL_H) \ + $(DF_H) $(DBGCNT_H) reg-stack.o : reg-stack.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_H) $(TREE_H) $(RECOG_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) \ insn-config.h toplev.h reload.h $(FUNCTION_H) $(TM_P_H) $(GGC_H) \ $(BASIC_BLOCK_H) output.h $(VARRAY_H) $(TIMEVAR_H) tree-pass.h \ - $(TARGET_H) vecprim.h + $(TARGET_H) vecprim.h $(DF_H) sreal.o: sreal.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) sreal.h predict.o: predict.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(TREE_H) $(FLAGS_H) insn-config.h $(BASIC_BLOCK_H) $(REGS_H) \ @@ -2724,17 +2753,19 @@ tracer.o : tracer.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ cfglayout.o : cfglayout.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_H) $(TREE_H) insn-config.h $(BASIC_BLOCK_H) hard-reg-set.h output.h \ $(FUNCTION_H) $(CFGLAYOUT_H) $(CFGLOOP_H) $(TARGET_H) gt-cfglayout.h \ - $(GGC_H) alloc-pool.h $(FLAGS_H) $(OBSTACK_H) tree-pass.h vecprim.h + $(GGC_H) alloc-pool.h $(FLAGS_H) $(OBSTACK_H) tree-pass.h vecprim.h \ + $(DF_H) timevar.o : timevar.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(TIMEVAR_H) $(FLAGS_H) intl.h toplev.h $(RTL_H) timevar.def regrename.o : regrename.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_H) insn-config.h $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h \ output.h $(RECOG_H) $(FUNCTION_H) $(OBSTACK_H) $(FLAGS_H) $(TM_P_H) \ - addresses.h reload.h toplev.h $(TIMEVAR_H) tree-pass.h + addresses.h reload.h toplev.h $(TIMEVAR_H) tree-pass.h $(DF_H) ifcvt.o : ifcvt.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(REGS_H) toplev.h $(FLAGS_H) insn-config.h $(FUNCTION_H) $(RECOG_H) \ $(TARGET_H) $(BASIC_BLOCK_H) $(EXPR_H) output.h except.h $(TM_P_H) \ - $(REAL_H) $(OPTABS_H) $(CFGLOOP_H) hard-reg-set.h $(TIMEVAR_H) tree-pass.h + $(REAL_H) $(OPTABS_H) $(CFGLOOP_H) hard-reg-set.h $(TIMEVAR_H) tree-pass.h \ + $(DF_H) lambda-mat.o : lambda-mat.c $(LAMBDA_H) $(GGC_H) $(SYSTEM_H) $(CONFIG_H) \ $(TM_H) coretypes.h $(TREE_H) lambda-trans.o: lambda-trans.c $(LAMBDA_H) $(GGC_H) $(SYSTEM_H) $(CONFIG_H) \ @@ -2750,10 +2781,11 @@ hooks.o: hooks.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(HOOKS_H) pretty-print.o: $(CONFIG_H) $(SYSTEM_H) coretypes.h intl.h $(PRETTY_PRINT_H) \ $(TREE_H) errors.o : errors.c $(CONFIG_H) $(SYSTEM_H) errors.h $(BCONFIG_H) +dbgcnt.o: dbgcnt.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(DBGCNT_H) lower-subreg.o : lower-subreg.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(MACHMODE_H) $(TM_H) $(RTL_H) $(TM_P_H) $(TIMEVAR_H) $(FLAGS_H) \ insn-config.h $(BASIC_BLOCK_H) $(RECOG_H) $(OBSTACK_H) bitmap.h \ - $(EXPR_H) $(REGS_H) tree-pass.h + $(EXPR_H) $(REGS_H) tree-pass.h $(DF_H) $(out_object_file): $(out_file) $(CONFIG_H) coretypes.h $(TM_H) $(TREE_H) \ $(RTL_H) $(REGS_H) hard-reg-set.h insn-config.h conditions.h \ diff --git a/gcc/alias.c b/gcc/alias.c index d003de92..c03ff03 100644 --- a/gcc/alias.c +++ b/gcc/alias.c @@ -46,6 +46,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "varray.h" #include "tree-pass.h" #include "ipa-type-escape.h" +#include "df.h" /* The aliasing API provided here solves related but different problems: @@ -839,7 +840,7 @@ find_base_value (rtx src) /* If we're inside init_alias_analysis, use new_reg_base_value to reduce the number of relaxation iterations. */ if (new_reg_base_value && new_reg_base_value[regno] - && REG_N_SETS (regno) == 1) + && DF_REG_DEF_COUNT (regno) == 1) return new_reg_base_value[regno]; if (VEC_index (rtx, reg_base_value, regno)) @@ -1087,27 +1088,6 @@ record_set (rtx dest, rtx set, void *data ATTRIBUTE_UNUSED) reg_seen[regno] = 1; } -/* Clear alias info for a register. This is used if an RTL transformation - changes the value of a register. This is used in flow by AUTO_INC_DEC - optimizations. We don't need to clear reg_base_value, since flow only - changes the offset. */ - -void -clear_reg_alias_info (rtx reg) -{ - unsigned int regno = REGNO (reg); - - if (regno >= FIRST_PSEUDO_REGISTER) - { - regno -= FIRST_PSEUDO_REGISTER; - if (regno < reg_known_value_size) - { - reg_known_value[regno] = reg; - reg_known_equiv_p[regno] = false; - } - } -} - /* If a value is known for REGNO, return it. */ rtx @@ -2433,7 +2413,7 @@ init_alias_analysis (void) the optimization level or flag_expensive_optimizations. We could propagate more information in the first pass by making use - of REG_N_SETS to determine immediately that the alias information + of DF_REG_DEF_COUNT to determine immediately that the alias information for a pseudo is "constant". A program with an uninitialized variable can cause an infinite loop @@ -2514,7 +2494,7 @@ init_alias_analysis (void) note = find_reg_equal_equiv_note (insn); if (note && REG_NOTE_KIND (note) == REG_EQUAL - && REG_N_SETS (regno) != 1) + && DF_REG_DEF_COUNT (regno) != 1) note = NULL_RTX; if (note != NULL_RTX @@ -2527,7 +2507,7 @@ init_alias_analysis (void) set_reg_known_equiv_p (regno, REG_NOTE_KIND (note) == REG_EQUIV); } - else if (REG_N_SETS (regno) == 1 + else if (DF_REG_DEF_COUNT (regno) == 1 && GET_CODE (src) == PLUS && REG_P (XEXP (src, 0)) && (t = get_reg_known_value (REGNO (XEXP (src, 0)))) @@ -2537,7 +2517,7 @@ init_alias_analysis (void) set_reg_known_value (regno, t); set_reg_known_equiv_p (regno, 0); } - else if (REG_N_SETS (regno) == 1 + else if (DF_REG_DEF_COUNT (regno) == 1 && ! rtx_varies_p (src, 1)) { set_reg_known_value (regno, src); diff --git a/gcc/alloc-pool.c b/gcc/alloc-pool.c index 0a58f37..52d5cff 100644 --- a/gcc/alloc-pool.c +++ b/gcc/alloc-pool.c @@ -1,5 +1,5 @@ /* Functions to support a pool of allocatable objects. - Copyright (C) 1987, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006 + Copyright (C) 1987, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Daniel Berlin <dan@cgsoftware.com> @@ -161,7 +161,9 @@ create_alloc_pool (const char *name, size_t size, size_t num) header_size = align_eight (sizeof (struct alloc_pool_list_def)); pool->block_size = (size * num) + header_size; - pool->free_list = NULL; + pool->returned_free_list = NULL; + pool->virgin_free_list = NULL; + pool->virgin_elts_remaining = 0; pool->elts_allocated = 0; pool->elts_free = 0; pool->blocks_allocated = 0; @@ -223,7 +225,6 @@ void * pool_alloc (alloc_pool pool) { alloc_pool_list header; - char *block; #ifdef GATHER_STATISTICS struct alloc_pool_descriptor *desc = alloc_pool_descriptor (pool->name); @@ -233,46 +234,57 @@ pool_alloc (alloc_pool pool) gcc_assert (pool); /* If there are no more free elements, make some more!. */ - if (!pool->free_list) + if (!pool->returned_free_list) { - size_t i; - alloc_pool_list block_header; - - /* Make the block. */ - block = XNEWVEC (char, pool->block_size); - block_header = (alloc_pool_list) block; - block += align_eight (sizeof (struct alloc_pool_list_def)); + char *block; + if (!pool->virgin_elts_remaining) + { + alloc_pool_list block_header; + + /* Make the block. */ + block = XNEWVEC (char, pool->block_size); + block_header = (alloc_pool_list) block; + block += align_eight (sizeof (struct alloc_pool_list_def)); #ifdef GATHER_STATISTICS - desc->current += pool->block_size; - if (desc->peak < desc->current) - desc->peak = desc->current; + desc->current += pool->block_size; + if (desc->peak < desc->current) + desc->peak = desc->current; #endif - - /* Throw it on the block list. */ - block_header->next = pool->block_list; - pool->block_list = block_header; - - /* Now put the actual block pieces onto the free list. */ - for (i = 0; i < pool->elts_per_block; i++, block += pool->elt_size) - { + + /* Throw it on the block list. */ + block_header->next = pool->block_list; + pool->block_list = block_header; + + /* Make the block available for allocation. */ + pool->virgin_free_list = block; + pool->virgin_elts_remaining = pool->elts_per_block; + + /* Also update the number of elements we have free/allocated, and + increment the allocated block count. */ + pool->elts_allocated += pool->elts_per_block; + pool->elts_free += pool->elts_per_block; + pool->blocks_allocated += 1; + } + + + /* We now know that we can take the first elt off the virgin list and + put it on the returned list. */ + block = pool->virgin_free_list; + header = (alloc_pool_list) USER_PTR_FROM_ALLOCATION_OBJECT_PTR (block); + header->next = NULL; #ifdef ENABLE_CHECKING - /* Mark the element to be free. */ - ((allocation_object *) block)->id = 0; + /* Mark the element to be free. */ + ((allocation_object *) block)->id = 0; #endif - header = (alloc_pool_list) USER_PTR_FROM_ALLOCATION_OBJECT_PTR (block); - header->next = pool->free_list; - pool->free_list = header; - } - /* Also update the number of elements we have free/allocated, and - increment the allocated block count. */ - pool->elts_allocated += pool->elts_per_block; - pool->elts_free += pool->elts_per_block; - pool->blocks_allocated += 1; + pool->returned_free_list = header; + pool->virgin_free_list += pool->elt_size; + pool->virgin_elts_remaining--; + } /* Pull the first free element from the free list, and return it. */ - header = pool->free_list; - pool->free_list = header->next; + header = pool->returned_free_list; + pool->returned_free_list = header->next; pool->elts_free--; #ifdef ENABLE_CHECKING @@ -305,8 +317,8 @@ pool_free (alloc_pool pool, void *ptr) #endif header = (alloc_pool_list) ptr; - header->next = pool->free_list; - pool->free_list = header; + header->next = pool->returned_free_list; + pool->returned_free_list = header; pool->elts_free++; } /* Output per-alloc_pool statistics. */ diff --git a/gcc/alloc-pool.h b/gcc/alloc-pool.h index 82188f4..faf13e8 100644 --- a/gcc/alloc-pool.h +++ b/gcc/alloc-pool.h @@ -1,5 +1,5 @@ /* Functions to support a pool of allocatable objects - Copyright (C) 1997, 1998, 1999, 2000, 2001, 2003, 2004 + Copyright (C) 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2007 Free Software Foundation, Inc. Contributed by Daniel Berlin <dan@cgsoftware.com> @@ -37,7 +37,18 @@ typedef struct alloc_pool_def ALLOC_POOL_ID_TYPE id; #endif size_t elts_per_block; - alloc_pool_list free_list; + + /* These are the elements that have been allocated at least once and freed. */ + alloc_pool_list returned_free_list; + + /* These are the elements that have not yet been allocated out of + the last block obtained from XNEWVEC. */ + char* virgin_free_list; + + /* The number of elements in the virgin_free_list that can be + allocated before needing another block. */ + size_t virgin_elts_remaining; + size_t elts_allocated; size_t elts_free; size_t blocks_allocated; diff --git a/gcc/auto-inc-dec.c b/gcc/auto-inc-dec.c new file mode 100644 index 0000000..2415662 --- /dev/null +++ b/gcc/auto-inc-dec.c @@ -0,0 +1,1558 @@ +/* Discovery of auto-inc and auto-dec instructions. + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + Contributed by Kenneth Zadeck <zadeck@naturalbridge.com> + +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, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "rtl.h" +#include "tm_p.h" +#include "hard-reg-set.h" +#include "basic-block.h" +#include "insn-config.h" +#include "regs.h" +#include "flags.h" +#include "output.h" +#include "function.h" +#include "except.h" +#include "toplev.h" +#include "recog.h" +#include "expr.h" +#include "timevar.h" +#include "tree-pass.h" +#include "df.h" +#include "dbgcnt.h" + +/* This pass was originally removed from flow.c. However there is + almost nothing that remains of that code. + + There are (4) basic forms that are matched: + + a <- b + c + ... + *a + + becomes + + a <- b + ... + *(a += c) pre + a += c + ... + *a + + becomes + + *(a += c) pre + *a + ... + b <- a + c + + for this case to be true, b must not be assigned or used between + the *a and the assignment to b. B must also be a Pmode reg. + + becomes + + b <- a + ... + *(b += c) post + *a + ... + a <- a + c + + becomes + + *(a += c) post + + There are three types of values of c. + + 1) c is a constant equal to the width of the value being accessed by + the pointer. This is useful for machines that have + HAVE_PRE_INCREMENT, HAVE_POST_INCREMENT, HAVE_PRE_DECREMENT or + HAVE_POST_DECREMENT defined. + + 2) c is a contant not equal to the width of the value being accessed + by the pointer. This is useful for machines that have + HAVE_PRE_MODIFY_DISP, HAVE_POST_MODIFY_DISP defined. + + 3) c is a register. This is useful for machines that have + HAVE_PRE_MODIFY_REG, HAVE_POST_MODIFY_REG + + The is one special case: if a already had an offset equal to it +- + its width and that offset is equal to -c when the increment was + before the ref or +c if the increment was after the ref, then if we + can do the combination but switch the pre/post bit. + + (1) FORM_PRE_ADD + + a <- b + c + ... + *(a - c) + + becomes + + a <- b + ... + *(a += c) post + + (2) FORM_PRE_INC + + a += c + ... + *(a - c) + + becomes + + *(a += c) post + + (3) FORM_POST_ADD + + *(a + c) + ... + b <- a + c + + for this case to be true, b must not be assigned or used between + the *a and the assignment to b. B must also be a Pmode reg. + + becomes + + b <- a + ... + *(b += c) pre + + + (4) FORM_POST_INC + + *(a + c) + ... + a <- a + c + + becomes + + *(a += c) pre +*/ +#ifdef AUTO_INC_DEC + +enum form +{ + FORM_PRE_ADD, + FORM_PRE_INC, + FORM_POST_ADD, + FORM_POST_INC, + FORM_last +}; + +/* The states of the second operands of mem refs and inc insns. If no + second operand of the mem_ref was found, it is assumed to just be + ZERO. SIZE is the size of the mode accessed in the memref. The + ANY is used for constants that are not +-size or 0. REG is used if + the forms are reg1 + reg2. */ + +enum inc_state +{ + INC_ZERO, /* == 0 */ + INC_NEG_SIZE, /* == +size */ + INC_POS_SIZE, /* == -size */ + INC_NEG_ANY, /* == some -constant */ + INC_POS_ANY, /* == some +constant */ + INC_REG, /* == some register */ + INC_last +}; + +/* The eight forms that pre/post inc/dec can take. */ +enum gen_form +{ + NOTHING, + SIMPLE_PRE_INC, /* ++size */ + SIMPLE_POST_INC, /* size++ */ + SIMPLE_PRE_DEC, /* --size */ + SIMPLE_POST_DEC, /* size-- */ + DISP_PRE, /* ++con */ + DISP_POST, /* con++ */ + REG_PRE, /* ++reg */ + REG_POST /* reg++ */ +}; + +/* Tmp mem rtx for use in cost modeling. */ +static rtx mem_tmp; + +static enum inc_state +set_inc_state (HOST_WIDE_INT val, int size) +{ + if (val == 0) + return INC_ZERO; + if (val < 0) + return (val == -size) ? INC_NEG_SIZE : INC_NEG_ANY; + else + return (val == size) ? INC_POS_SIZE : INC_POS_ANY; +} + +/* The DECISION_TABLE that describes what form, if any, the increment + or decrement will take. It is a three dimensional table. The first + index is the type of constant or register found as the second + operand of the inc insn. The second index is the type of constant + or register found as the second operand of the memory reference (if + no second operand exists, 0 is used). The third index is the form + and location (relative to the mem reference) of inc insn. */ + +static bool initialized = false; +static enum gen_form decision_table[INC_last][INC_last][FORM_last]; + +static void +init_decision_table (void) +{ + enum gen_form value; + + if (HAVE_PRE_INCREMENT || HAVE_PRE_MODIFY_DISP) + { + /* Prefer the simple form if both are available. */ + value = (HAVE_PRE_INCREMENT) ? SIMPLE_PRE_INC : DISP_PRE; + + decision_table[INC_POS_SIZE][INC_ZERO][FORM_PRE_ADD] = value; + decision_table[INC_POS_SIZE][INC_ZERO][FORM_PRE_INC] = value; + + decision_table[INC_POS_SIZE][INC_POS_SIZE][FORM_POST_ADD] = value; + decision_table[INC_POS_SIZE][INC_POS_SIZE][FORM_POST_INC] = value; + } + + if (HAVE_POST_INCREMENT || HAVE_POST_MODIFY_DISP) + { + /* Prefer the simple form if both are available. */ + value = (HAVE_POST_INCREMENT) ? SIMPLE_POST_INC : DISP_POST; + + decision_table[INC_POS_SIZE][INC_ZERO][FORM_POST_ADD] = value; + decision_table[INC_POS_SIZE][INC_ZERO][FORM_POST_INC] = value; + + decision_table[INC_POS_SIZE][INC_NEG_SIZE][FORM_PRE_ADD] = value; + decision_table[INC_POS_SIZE][INC_NEG_SIZE][FORM_PRE_INC] = value; + } + + if (HAVE_PRE_DECREMENT || HAVE_PRE_MODIFY_DISP) + { + /* Prefer the simple form if both are available. */ + value = (HAVE_PRE_DECREMENT) ? SIMPLE_PRE_DEC : DISP_PRE; + + decision_table[INC_NEG_SIZE][INC_ZERO][FORM_PRE_ADD] = value; + decision_table[INC_NEG_SIZE][INC_ZERO][FORM_PRE_INC] = value; + + decision_table[INC_NEG_SIZE][INC_NEG_SIZE][FORM_POST_ADD] = value; + decision_table[INC_NEG_SIZE][INC_NEG_SIZE][FORM_POST_INC] = value; + } + + if (HAVE_POST_DECREMENT || HAVE_POST_MODIFY_DISP) + { + /* Prefer the simple form if both are available. */ + value = (HAVE_POST_DECREMENT) ? SIMPLE_POST_DEC : DISP_POST; + + decision_table[INC_NEG_SIZE][INC_ZERO][FORM_POST_ADD] = value; + decision_table[INC_NEG_SIZE][INC_ZERO][FORM_POST_INC] = value; + + decision_table[INC_NEG_SIZE][INC_POS_SIZE][FORM_PRE_ADD] = value; + decision_table[INC_NEG_SIZE][INC_POS_SIZE][FORM_PRE_INC] = value; + } + + if (HAVE_PRE_MODIFY_DISP) + { + decision_table[INC_POS_ANY][INC_ZERO][FORM_PRE_ADD] = DISP_PRE; + decision_table[INC_POS_ANY][INC_ZERO][FORM_PRE_INC] = DISP_PRE; + + decision_table[INC_POS_ANY][INC_POS_ANY][FORM_POST_ADD] = DISP_PRE; + decision_table[INC_POS_ANY][INC_POS_ANY][FORM_POST_INC] = DISP_PRE; + + decision_table[INC_NEG_ANY][INC_ZERO][FORM_PRE_ADD] = DISP_PRE; + decision_table[INC_NEG_ANY][INC_ZERO][FORM_PRE_INC] = DISP_PRE; + + decision_table[INC_NEG_ANY][INC_NEG_ANY][FORM_POST_ADD] = DISP_PRE; + decision_table[INC_NEG_ANY][INC_NEG_ANY][FORM_POST_INC] = DISP_PRE; + } + + if (HAVE_POST_MODIFY_DISP) + { + decision_table[INC_POS_ANY][INC_ZERO][FORM_POST_ADD] = DISP_POST; + decision_table[INC_POS_ANY][INC_ZERO][FORM_POST_INC] = DISP_POST; + + decision_table[INC_POS_ANY][INC_NEG_ANY][FORM_PRE_ADD] = DISP_POST; + decision_table[INC_POS_ANY][INC_NEG_ANY][FORM_PRE_INC] = DISP_POST; + + decision_table[INC_NEG_ANY][INC_ZERO][FORM_POST_ADD] = DISP_POST; + decision_table[INC_NEG_ANY][INC_ZERO][FORM_POST_INC] = DISP_POST; + + decision_table[INC_NEG_ANY][INC_POS_ANY][FORM_PRE_ADD] = DISP_POST; + decision_table[INC_NEG_ANY][INC_POS_ANY][FORM_PRE_INC] = DISP_POST; + } + + /* This is much simpler than the other cases because we do not look + for the reg1-reg2 case. Note that we do not have a INC_POS_REG + and INC_NEG_REG states. Most of the use of such states would be + on a target that had an R1 - R2 update address form. + + There is the remote possibility that you could also catch a = a + + b; *(a - b) as a postdecrement of (a + b). However, it is + unclear if *(a - b) would ever be generated on a machine that did + not have that kind of addressing mode. The IA-64 and RS6000 will + not do this, and I cannot speak for any other. If any + architecture does have an a-b update for, these cases should be + added. */ + if (HAVE_PRE_MODIFY_REG) + { + decision_table[INC_REG][INC_ZERO][FORM_PRE_ADD] = REG_PRE; + decision_table[INC_REG][INC_ZERO][FORM_PRE_INC] = REG_PRE; + + decision_table[INC_REG][INC_REG][FORM_POST_ADD] = REG_PRE; + decision_table[INC_REG][INC_REG][FORM_POST_INC] = REG_PRE; + } + + if (HAVE_POST_MODIFY_REG) + { + decision_table[INC_REG][INC_ZERO][FORM_POST_ADD] = REG_POST; + decision_table[INC_REG][INC_ZERO][FORM_POST_INC] = REG_POST; + } + + initialized = true; +} + +/* Parsed fields of an inc insn of the form "reg_res = reg0+reg1" or + "reg_res = reg0+c". */ + +static struct inc_insn +{ + rtx insn; /* The insn being parsed. */ + rtx pat; /* The pattern of the insn. */ + bool reg1_is_const; /* True if reg1 is const, false if reg1 is a reg. */ + enum form form; + rtx reg_res; + rtx reg0; + rtx reg1; + enum inc_state reg1_state;/* The form of the const if reg1 is a const. */ + HOST_WIDE_INT reg1_val;/* Value if reg1 is const. */ +} inc_insn; + + +/* Dump the parsed inc insn to FILE. */ + +static void +dump_inc_insn (FILE *file) +{ + const char *f = ((inc_insn.form == FORM_PRE_ADD) + || (inc_insn.form == FORM_PRE_INC)) ? "pre" : "post"; + + dump_insn_slim (file, inc_insn.insn); + + switch (inc_insn.form) + { + case FORM_PRE_ADD: + case FORM_POST_ADD: + if (inc_insn.reg1_is_const) + fprintf (file, "found %s add(%d) r[%d]=r[%d]+%d\n", + f, INSN_UID (inc_insn.insn), + REGNO (inc_insn.reg_res), + REGNO (inc_insn.reg0), (int) inc_insn.reg1_val); + else + fprintf (file, "found %s add(%d) r[%d]=r[%d]+r[%d]\n", + f, INSN_UID (inc_insn.insn), + REGNO (inc_insn.reg_res), + REGNO (inc_insn.reg0), REGNO (inc_insn.reg1)); + break; + + case FORM_PRE_INC: + case FORM_POST_INC: + if (inc_insn.reg1_is_const) + fprintf (file, "found %s inc(%d) r[%d]+=%d\n", + f, INSN_UID (inc_insn.insn), + REGNO (inc_insn.reg_res), (int) inc_insn.reg1_val); + else + fprintf (file, "found %s inc(%d) r[%d]+=r[%d]\n", + f, INSN_UID (inc_insn.insn), + REGNO (inc_insn.reg_res), REGNO (inc_insn.reg1)); + break; + + default: + break; + } +} + + +/* Parsed fields of a mem ref of the form "*(reg0+reg1)" or "*(reg0+c)". */ + +static struct mem_insn +{ + rtx insn; /* The insn being parsed. */ + rtx pat; /* The pattern of the insn. */ + rtx *mem_loc; /* The address of the field that holds the mem */ + /* that is to be replaced. */ + bool reg1_is_const; /* True if reg1 is const, false if reg1 is a reg. */ + rtx reg0; + rtx reg1; /* This is either a reg or a const depending on + reg1_is_const. */ + enum inc_state reg1_state;/* The form of the const if reg1 is a const. */ + HOST_WIDE_INT reg1_val;/* Value if reg1 is const. */ +} mem_insn; + + +/* Dump the parsed mem insn to FILE. */ + +static void +dump_mem_insn (FILE *file) +{ + dump_insn_slim (file, mem_insn.insn); + + if (mem_insn.reg1_is_const) + fprintf (file, "found mem(%d) *(r[%d]+%d)\n", + INSN_UID (mem_insn.insn), + REGNO (mem_insn.reg0), (int) mem_insn.reg1_val); + else + fprintf (file, "found mem(%d) *(r[%d]+r[%d])\n", + INSN_UID (mem_insn.insn), + REGNO (mem_insn.reg0), REGNO (mem_insn.reg1)); +} + + +/* The following three arrays contain pointers to instructions. They + are indexed by REGNO. At any point in the basic block where we are + looking these three arrays contain, respectively, the next insn + that uses REGNO, the next inc or add insn that uses REGNO and the + next insn that sets REGNO. + + The arrays are not cleared when we move from block to block so + whenever an insn is retrieved from these arrays, it's block number + must be compared with the current block. +*/ + +static rtx *reg_next_use = NULL; +static rtx *reg_next_inc_use = NULL; +static rtx *reg_next_def = NULL; + + +/* Move dead note that match PATTERN to TO_INSN from FROM_INSN. We do + not really care about moving any other notes from the inc or add + insn. Moving the REG_EQUAL and REG_EQUIV is clearly wrong and it + does not appear that there are any other kinds of relavant notes. */ + +static void +move_dead_notes (rtx to_insn, rtx from_insn, rtx pattern) +{ + rtx note; + rtx next_note; + rtx prev_note = NULL; + + for (note = REG_NOTES (from_insn); note; note = next_note) + { + next_note = XEXP (note, 1); + + if ((REG_NOTE_KIND (note) == REG_DEAD) + && pattern == XEXP (note, 0)) + { + XEXP (note, 1) = REG_NOTES (to_insn); + REG_NOTES (to_insn) = note; + if (prev_note) + XEXP (prev_note, 1) = next_note; + else + REG_NOTES (from_insn) = next_note; + } + else prev_note = note; + } +} + + +/* Create a mov insn DEST_REG <- SRC_REG and insert it before + NEXT_INSN. */ + +static rtx +insert_move_insn_before (rtx next_insn, rtx dest_reg, rtx src_reg) +{ + rtx insns; + + start_sequence (); + emit_move_insn (dest_reg, src_reg); + insns = get_insns (); + end_sequence (); + emit_insn_before (insns, next_insn); + return insns; +} + + +/* Change mem_insn.mem_loc so that uses NEW_ADDR which has an + increment of INC_REG. To have reached this point, the change is a + legitimate one from a dataflow point of view. The only questions + are is this a valid change to the instruction and is this a + profitable change to the instruction. */ + +static bool +attempt_change (rtx new_addr, rtx inc_reg) +{ + /* There are four cases: For the two cases that involve an add + instruction, we are going to have to delete the add and insert a + mov. We are going to assume that the mov is free. This is + fairly early in the backend and there are a lot of opportunities + for removing that move later. In particular, there is the case + where the move may be dead, this is what dead code elimination + passes are for. The two cases where we have an inc insn will be + handled mov free. */ + + basic_block bb = BASIC_BLOCK (BLOCK_NUM (mem_insn.insn)); + rtx mov_insn = NULL; + int regno; + rtx mem = *mem_insn.mem_loc; + enum machine_mode mode = GET_MODE (mem); + rtx new_mem; + int old_cost = 0; + int new_cost = 0; + + PUT_MODE (mem_tmp, mode); + XEXP (mem_tmp, 0) = new_addr; + + old_cost = rtx_cost (mem, 0) + + rtx_cost (PATTERN (inc_insn.insn), 0); + new_cost = rtx_cost (mem_tmp, 0); + + /* The first item of business is to see if this is profitable. */ + if (old_cost < new_cost) + { + if (dump_file) + fprintf (dump_file, "cost failure old=%d new=%d\n", old_cost, new_cost); + return false; + } + + /* Jump thru a lot of hoops to keep the attributes up to date. We + do not want to call one of the change address variants that take + an offset even though we know the offset in many cases. These + assume you are changing where the address is pointing by the + offset. */ + new_mem = replace_equiv_address_nv (mem, new_addr); + if (! validate_change (mem_insn.insn, mem_insn.mem_loc, new_mem, 0)) + { + if (dump_file) + fprintf (dump_file, "validation failure\n"); + return false; + } + + /* From here to the end of the function we are committed to the + change, i.e. nothing fails. Generate any necessary movs, move + any regnotes, and fix up the reg_next_{use,inc_use,def}. */ + switch (inc_insn.form) + { + case FORM_PRE_ADD: + mov_insn = insert_move_insn_before (mem_insn.insn, + inc_insn.reg_res, inc_insn.reg0); + move_dead_notes (mov_insn, inc_insn.insn, inc_insn.reg0); + + regno = REGNO (inc_insn.reg_res); + reg_next_def[regno] = mov_insn; + reg_next_use[regno] = NULL; + regno = REGNO (inc_insn.reg0); + reg_next_use[regno] = mov_insn; + df_recompute_luids (bb); + break; + + case FORM_POST_INC: + regno = REGNO (inc_insn.reg_res); + if (reg_next_use[regno] == reg_next_inc_use[regno]) + reg_next_inc_use[regno] = NULL; + + /* Fallthru. */ + case FORM_PRE_INC: + regno = REGNO (inc_insn.reg_res); + reg_next_def[regno] = mem_insn.insn; + reg_next_use[regno] = NULL; + + break; + + case FORM_POST_ADD: + mov_insn = insert_move_insn_before (mem_insn.insn, + inc_insn.reg_res, inc_insn.reg0); + move_dead_notes (mov_insn, inc_insn.insn, inc_insn.reg0); + + /* Do not move anything to the mov insn because the instruction + pointer for the main iteration has not yet hit that. It is + still pointing to the mem insn. */ + regno = REGNO (inc_insn.reg_res); + reg_next_def[regno] = mem_insn.insn; + reg_next_use[regno] = NULL; + + regno = REGNO (inc_insn.reg0); + reg_next_use[regno] = mem_insn.insn; + if ((reg_next_use[regno] == reg_next_inc_use[regno]) + || (reg_next_inc_use[regno] == inc_insn.insn)) + reg_next_inc_use[regno] = NULL; + df_recompute_luids (bb); + break; + + case FORM_last: + default: + gcc_unreachable (); + } + + if (!inc_insn.reg1_is_const) + { + regno = REGNO (inc_insn.reg1); + reg_next_use[regno] = mem_insn.insn; + if ((reg_next_use[regno] == reg_next_inc_use[regno]) + || (reg_next_inc_use[regno] == inc_insn.insn)) + reg_next_inc_use[regno] = NULL; + } + + delete_insn (inc_insn.insn); + + if (dump_file && mov_insn) + { + fprintf (dump_file, "inserting mov "); + dump_insn_slim (dump_file, mov_insn); + } + + /* Record that this insn has an implicit side effect. */ + REG_NOTES (mem_insn.insn) + = alloc_EXPR_LIST (REG_INC, inc_reg, REG_NOTES (mem_insn.insn)); + + if (dump_file) + { + fprintf (dump_file, "****success "); + dump_insn_slim (dump_file, mem_insn.insn); + } + + return true; +} + + +/* Try to combine the instruction in INC_INSN with the instruction in + MEM_INSN. First the form is determined using the DECISION_TABLE + and and the results of parsing the INC_INSN and the MEM_INSN. + Assuming the form is ok, a prototype new address is built which is + passed to ATTEMPT_CHANGE for final processing. */ + +static bool +try_merge (void) +{ + enum gen_form gen_form; + rtx mem = *mem_insn.mem_loc; + rtx inc_reg = inc_insn.form == FORM_POST_ADD ? + inc_insn.reg_res : mem_insn.reg0; + + /* The width of the mem being accessed. */ + int size = GET_MODE_SIZE (GET_MODE (mem)); + rtx last_insn = NULL; + + switch (inc_insn.form) + { + case FORM_PRE_ADD: + case FORM_PRE_INC: + last_insn = mem_insn.insn; + break; + case FORM_POST_INC: + case FORM_POST_ADD: + last_insn = inc_insn.insn; + break; + case FORM_last: + default: + gcc_unreachable (); + } + + /* Cannot handle auto inc of the stack. */ + if (inc_reg == stack_pointer_rtx) + { + if (dump_file) + fprintf (dump_file, "cannot inc stack %d failure\n", REGNO (inc_reg)); + return false; + } + + /* Look to see if the inc register is dead after the memory + reference. If it is do not do the combination. */ + if (find_regno_note (last_insn, REG_DEAD, REGNO (inc_reg))) + { + if (dump_file) + fprintf (dump_file, "dead failure %d\n", REGNO (inc_reg)); + return false; + } + + mem_insn.reg1_state = (mem_insn.reg1_is_const) + ? set_inc_state (mem_insn.reg1_val, size) : INC_REG; + inc_insn.reg1_state = (inc_insn.reg1_is_const) + ? set_inc_state (inc_insn.reg1_val, size) : INC_REG; + + /* Now get the form that we are generating. */ + gen_form = decision_table + [inc_insn.reg1_state][mem_insn.reg1_state][inc_insn.form]; + + if (dbg_cnt (auto_inc_dec) == false) + return false; + + switch (gen_form) + { + default: + case NOTHING: + return false; + + case SIMPLE_PRE_INC: /* ++size */ + if (dump_file) + fprintf (dump_file, "trying SIMPLE_PRE_INC\n"); + return attempt_change (gen_rtx_PRE_INC (Pmode, inc_reg), inc_reg); + break; + + case SIMPLE_POST_INC: /* size++ */ + if (dump_file) + fprintf (dump_file, "trying SIMPLE_POST_INC\n"); + return attempt_change (gen_rtx_POST_INC (Pmode, inc_reg), inc_reg); + break; + + case SIMPLE_PRE_DEC: /* --size */ + if (dump_file) + fprintf (dump_file, "trying SIMPLE_PRE_DEC\n"); + return attempt_change (gen_rtx_PRE_DEC (Pmode, inc_reg), inc_reg); + break; + + case SIMPLE_POST_DEC: /* size-- */ + if (dump_file) + fprintf (dump_file, "trying SIMPLE_POST_DEC\n"); + return attempt_change (gen_rtx_POST_DEC (Pmode, inc_reg), inc_reg); + break; + + case DISP_PRE: /* ++con */ + if (dump_file) + fprintf (dump_file, "trying DISP_PRE\n"); + return attempt_change (gen_rtx_PRE_MODIFY (Pmode, + inc_reg, + gen_rtx_PLUS (Pmode, + inc_reg, + inc_insn.reg1)), + inc_reg); + break; + + case DISP_POST: /* con++ */ + if (dump_file) + fprintf (dump_file, "trying POST_DISP\n"); + return attempt_change (gen_rtx_POST_MODIFY (Pmode, + inc_reg, + gen_rtx_PLUS (Pmode, + inc_reg, + inc_insn.reg1)), + inc_reg); + break; + + case REG_PRE: /* ++reg */ + if (dump_file) + fprintf (dump_file, "trying PRE_REG\n"); + return attempt_change (gen_rtx_PRE_MODIFY (Pmode, + inc_reg, + gen_rtx_PLUS (Pmode, + inc_reg, + inc_insn.reg1)), + inc_reg); + break; + + case REG_POST: /* reg++ */ + if (dump_file) + fprintf (dump_file, "trying POST_REG\n"); + return attempt_change (gen_rtx_POST_MODIFY (Pmode, + inc_reg, + gen_rtx_PLUS (Pmode, + inc_reg, + inc_insn.reg1)), + inc_reg); + break; + } +} + +/* Return the next insn that uses (if reg_next_use is passed in + NEXT_ARRAY) or defines (if reg_next_def is passed in NEXT_ARRAY) + REGNO in BB. */ + +static rtx +get_next_ref (int regno, basic_block bb, rtx *next_array) +{ + rtx insn = next_array[regno]; + + /* Lazy about cleaning out the next_arrays. */ + if (insn && BASIC_BLOCK (BLOCK_NUM (insn)) != bb) + { + next_array[regno] = NULL; + insn = NULL; + } + + return insn; +} + + +/* Reverse the operands in a mem insn. */ + +static void +reverse_mem (void) +{ + rtx tmp = mem_insn.reg1; + mem_insn.reg1 = mem_insn.reg0; + mem_insn.reg0 = tmp; +} + + +/* Reverse the operands in a inc insn. */ + +static void +reverse_inc (void) +{ + rtx tmp = inc_insn.reg1; + inc_insn.reg1 = inc_insn.reg0; + inc_insn.reg0 = tmp; +} + + +/* Return true if INSN is of a form "a = b op c" where a and b are + regs. op is + if c is a reg and +|- if c is a const. Fill in + INC_INSN with what is found. + + This function is called in two contexts, if BEFORE_MEM is true, + this is called for each insn in the basic block. If BEFORE_MEM is + false, it is called for the instruction in the block that uses the + index register for some memory reference that is currently being + processed. */ + +static bool +parse_add_or_inc (rtx insn, bool before_mem) +{ + rtx pat = single_set (insn); + if (!pat) + return false; + + /* Result must be single reg. */ + if (!REG_P (SET_DEST (pat))) + return false; + + if ((GET_CODE (SET_SRC (pat)) != PLUS) + && (GET_CODE (SET_SRC (pat)) != MINUS)) + return false; + + if (!REG_P (XEXP (SET_SRC (pat), 0))) + return false; + + inc_insn.insn = insn; + inc_insn.pat = pat; + inc_insn.reg_res = SET_DEST (pat); + inc_insn.reg0 = XEXP (SET_SRC (pat), 0); + if (rtx_equal_p (inc_insn.reg_res, inc_insn.reg0)) + inc_insn.form = before_mem ? FORM_PRE_INC : FORM_POST_INC; + else + inc_insn.form = before_mem ? FORM_PRE_ADD : FORM_POST_ADD; + + if (GET_CODE (XEXP (SET_SRC (pat), 1)) == CONST_INT) + { + /* Process a = b + c where c is a const. */ + inc_insn.reg1_is_const = true; + if (GET_CODE (SET_SRC (pat)) == PLUS) + { + inc_insn.reg1 = XEXP (SET_SRC (pat), 1); + inc_insn.reg1_val = INTVAL (inc_insn.reg1); + } + else + { + inc_insn.reg1_val = -INTVAL (XEXP (SET_SRC (pat), 1)); + inc_insn.reg1 = GEN_INT (inc_insn.reg1_val); + } + return true; + } + else if ((HAVE_PRE_MODIFY_REG || HAVE_POST_MODIFY_REG) + && (REG_P (XEXP (SET_SRC (pat), 1))) + && GET_CODE (SET_SRC (pat)) == PLUS) + { + /* Process a = b + c where c is a reg. */ + inc_insn.reg1 = XEXP (SET_SRC (pat), 1); + inc_insn.reg1_is_const = false; + + if (inc_insn.form == FORM_PRE_INC + || inc_insn.form == FORM_POST_INC) + return true; + else if (rtx_equal_p (inc_insn.reg_res, inc_insn.reg1)) + { + /* Reverse the two operands and turn *_ADD into *_INC since + a = c + a. */ + reverse_inc (); + inc_insn.form = before_mem ? FORM_PRE_INC : FORM_POST_INC; + return true; + } + else + return true; + } + + return false; +} + + +/* A recursive function that checks all of the mem uses in + ADDRESS_OF_X to see if any single one of them is compatible with + what has been found in inc_insn. + + -1 is returned for success. 0 is returned if nothing was found and + 1 is returned for failure. */ + +static int +find_address (rtx *address_of_x) +{ + rtx x = *address_of_x; + enum rtx_code code = GET_CODE (x); + const char *const fmt = GET_RTX_FORMAT (code); + int i; + int value = 0; + int tem; + + if (code == MEM && rtx_equal_p (XEXP (x, 0), inc_insn.reg_res)) + { + /* Match with *reg0. */ + mem_insn.mem_loc = address_of_x; + mem_insn.reg0 = inc_insn.reg_res; + mem_insn.reg1_is_const = true; + mem_insn.reg1_val = 0; + mem_insn.reg1 = GEN_INT (0); + return -1; + } + if (code == MEM && GET_CODE (XEXP (x, 0)) == PLUS + && rtx_equal_p (XEXP (XEXP (x, 0), 0), inc_insn.reg_res)) + { + rtx b = XEXP (XEXP (x, 0), 1); + mem_insn.mem_loc = address_of_x; + mem_insn.reg0 = inc_insn.reg_res; + mem_insn.reg1 = b; + mem_insn.reg1_is_const = inc_insn.reg1_is_const; + if (GET_CODE (b) == CONST_INT) + { + /* Match with *(reg0 + reg1) where reg1 is a const. */ + HOST_WIDE_INT val = INTVAL (b); + if (inc_insn.reg1_is_const + && (inc_insn.reg1_val == val || inc_insn.reg1_val == -val)) + { + mem_insn.reg1_val = val; + return -1; + } + } + else if (!inc_insn.reg1_is_const + && rtx_equal_p (inc_insn.reg1, b)) + /* Match with *(reg0 + reg1). */ + return -1; + } + + if (code == SIGN_EXTRACT || code == ZERO_EXTRACT) + { + /* If REG occurs inside a MEM used in a bit-field reference, + that is unacceptable. */ + if (find_address (&XEXP (x, 0))) + return 1; + } + + if (x == inc_insn.reg_res) + return 1; + + /* Time for some deep diving. */ + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + tem = find_address (&XEXP (x, i)); + /* If this is the first use, let it go so the rest of the + insn can be checked. */ + if (value == 0) + value = tem; + else if (tem != 0) + /* More than one match was found. */ + return 1; + } + else if (fmt[i] == 'E') + { + int j; + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + { + tem = find_address (&XVECEXP (x, i, j)); + /* If this is the first use, let it go so the rest of + the insn can be checked. */ + if (value == 0) + value = tem; + else if (tem != 0) + /* More than one match was found. */ + return 1; + } + } + } + return value; +} + +/* Once a suitable mem reference has been found and the MEM_INSN + structure has been filled in, FIND_INC is called to see if there is + a suitable add or inc insn that follows the mem reference and + determine if it is suitable to merge. + + In the case where the MEM_INSN has two registers in the reference, + this function may be called recursively. The first time looking + for an add of the first register, and if that fails, looking for an + add of the second register. The FIRST_TRY parameter is used to + only allow the parameters to be reversed once. */ + +static bool +find_inc (bool first_try) +{ + rtx insn; + basic_block bb = BASIC_BLOCK (BLOCK_NUM (mem_insn.insn)); + rtx other_insn; + struct df_ref **def_rec; + + /* Make sure this reg appears only once in this insn. */ + if (count_occurrences (PATTERN (mem_insn.insn), mem_insn.reg0, 1) != 1) + { + if (dump_file) + fprintf (dump_file, "mem count failure\n"); + return false; + } + + if (dump_file) + dump_mem_insn (dump_file); + + /* Find the next use that is an inc. */ + insn = get_next_ref (REGNO (mem_insn.reg0), + BASIC_BLOCK (BLOCK_NUM (mem_insn.insn)), + reg_next_inc_use); + if (!insn) + return false; + + /* Even though we know the next use is an add or inc because it came + from the reg_next_inc_use, we must still reparse. */ + if (!parse_add_or_inc (insn, false)) + { + /* Next use was not an add. Look for one extra case. It could be + that we have: + + *(a + b) + ...= a; + ...= b + a + + if we reverse the operands in the mem ref we would + find this. Only try it once though. */ + if (first_try && !mem_insn.reg1_is_const) + { + reverse_mem (); + return find_inc (false); + } + else + return false; + } + + /* Need to assure that none of the operands of the inc instruction are + assigned to by the mem insn. */ + for (def_rec = DF_INSN_DEFS (mem_insn.insn); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + unsigned int regno = DF_REF_REGNO (def); + if ((regno == REGNO (inc_insn.reg0)) + || (regno == REGNO (inc_insn.reg_res))) + { + if (dump_file) + fprintf (dump_file, "inc conflicts with store failure.\n"); + return false; + } + if (!inc_insn.reg1_is_const && (regno == REGNO (inc_insn.reg1))) + { + if (dump_file) + fprintf (dump_file, "inc conflicts with store failure.\n"); + return false; + } + } + + if (dump_file) + dump_inc_insn (dump_file); + + if (inc_insn.form == FORM_POST_ADD) + { + /* Make sure that there is no insn that assigns to inc_insn.res + between the mem_insn and the inc_insn. */ + rtx other_insn = get_next_ref (REGNO (inc_insn.reg_res), + BASIC_BLOCK (BLOCK_NUM (mem_insn.insn)), + reg_next_def); + if (other_insn != inc_insn.insn) + { + if (dump_file) + fprintf (dump_file, + "result of add is assigned to between mem and inc insns.\n"); + return false; + } + + other_insn = get_next_ref (REGNO (inc_insn.reg_res), + BASIC_BLOCK (BLOCK_NUM (mem_insn.insn)), + reg_next_use); + if (other_insn + && (other_insn != inc_insn.insn) + && (DF_INSN_LUID (inc_insn.insn) > DF_INSN_LUID (other_insn))) + { + if (dump_file) + fprintf (dump_file, + "result of add is used between mem and inc insns.\n"); + return false; + } + + /* For the post_add to work, the result_reg of the inc must not be + used in the mem insn since this will become the new index + register. */ + if (count_occurrences (PATTERN (mem_insn.insn), inc_insn.reg_res, 1) != 0) + { + if (dump_file) + fprintf (dump_file, "base reg replacement failure.\n"); + return false; + } + } + + if (mem_insn.reg1_is_const) + { + if (mem_insn.reg1_val == 0) + { + if (!inc_insn.reg1_is_const) + { + /* The mem looks like *r0 and the rhs of the add has two + registers. */ + int luid = DF_INSN_LUID (inc_insn.insn); + if (inc_insn.form == FORM_POST_ADD) + { + /* The trick is that we are not going to increment r0, + we are going to increment the result of the add insn. + For this trick to be correct, the result reg of + the inc must be a valid addressing reg. */ + if (GET_MODE (inc_insn.reg_res) != Pmode) + { + if (dump_file) + fprintf (dump_file, "base reg mode failure.\n"); + return false; + } + + /* We also need to make sure that the next use of + inc result is after the inc. */ + other_insn + = get_next_ref (REGNO (inc_insn.reg1), bb, reg_next_use); + if (other_insn && luid > DF_INSN_LUID (other_insn)) + return false; + + if (!rtx_equal_p (mem_insn.reg0, inc_insn.reg0)) + reverse_inc (); + } + + other_insn + = get_next_ref (REGNO (inc_insn.reg1), bb, reg_next_def); + if (other_insn && luid > DF_INSN_LUID (other_insn)) + return false; + } + } + /* Both the inc/add and the mem have a constant. Need to check + that the constants are ok. */ + else if ((mem_insn.reg1_val != inc_insn.reg1_val) + && (mem_insn.reg1_val != -inc_insn.reg1_val)) + return false; + } + else + { + /* The mem insn is of the form *(a + b) where a and b are both + regs. It may be that in order to match the add or inc we + need to treat it as if it was *(b + a). It may also be that + the add is of the form a + c where c does not match b and + then we just abandon this. */ + + int luid = DF_INSN_LUID (inc_insn.insn); + rtx other_insn; + + /* Make sure this reg appears only once in this insn. */ + if (count_occurrences (PATTERN (mem_insn.insn), mem_insn.reg1, 1) != 1) + return false; + + if (inc_insn.form == FORM_POST_ADD) + { + /* For this trick to be correct, the result reg of the inc + must be a valid addressing reg. */ + if (GET_MODE (inc_insn.reg_res) != Pmode) + { + if (dump_file) + fprintf (dump_file, "base reg mode failure.\n"); + return false; + } + + if (rtx_equal_p (mem_insn.reg0, inc_insn.reg0)) + { + if (!rtx_equal_p (mem_insn.reg1, inc_insn.reg1)) + { + /* See comment above on find_inc (false) call. */ + if (first_try) + { + reverse_mem (); + return find_inc (false); + } + else + return false; + } + + /* Need to check that there are no assignemnts to b + before the add insn. */ + other_insn + = get_next_ref (REGNO (inc_insn.reg1), bb, reg_next_def); + if (other_insn && luid > DF_INSN_LUID (other_insn)) + return false; + /* All ok for the next step. */ + } + else + { + /* We know that mem_insn.reg0 must equal inc_insn.reg1 + or else we would not have found the inc insn. */ + reverse_mem (); + if (!rtx_equal_p (mem_insn.reg0, inc_insn.reg0)) + { + /* See comment above on find_inc (false) call. */ + if (first_try) + return find_inc (false); + else + return false; + } + /* To have gotten here know that. + *(b + a) + + ... = (b + a) + + We also know that the lhs of the inc is not b or a. We + need to make sure that there are no assignments to b + between the mem ref and the inc. */ + + other_insn + = get_next_ref (REGNO (inc_insn.reg0), bb, reg_next_def); + if (other_insn && luid > DF_INSN_LUID (other_insn)) + return false; + } + + /* Need to check that the next use of the add result is later than + add insn since this will be the reg incremented. */ + other_insn + = get_next_ref (REGNO (inc_insn.reg_res), bb, reg_next_use); + if (other_insn && luid > DF_INSN_LUID (other_insn)) + return false; + } + else /* FORM_POST_INC. There is less to check here because we + know that operands must line up. */ + { + if (!rtx_equal_p (mem_insn.reg1, inc_insn.reg1)) + /* See comment above on find_inc (false) call. */ + { + if (first_try) + { + reverse_mem (); + return find_inc (false); + } + else + return false; + } + + /* To have gotten here know that. + *(a + b) + + ... = (a + b) + + We also know that the lhs of the inc is not b. We need to make + sure that there are no assignments to b between the mem ref and + the inc. */ + other_insn + = get_next_ref (REGNO (inc_insn.reg1), bb, reg_next_def); + if (other_insn && luid > DF_INSN_LUID (other_insn)) + return false; + } + } + + if (inc_insn.form == FORM_POST_INC) + { + other_insn + = get_next_ref (REGNO (inc_insn.reg0), bb, reg_next_use); + /* When we found inc_insn, we were looking for the + next add or inc, not the next insn that used the + reg. Because we are going to increment the reg + in this form, we need to make sure that there + were no interveining uses of reg. */ + if (inc_insn.insn != other_insn) + return false; + } + + return try_merge (); +} + + +/* A recursive function that walks ADDRESS_OF_X to find all of the mem + uses in pat that could be used as an auto inc or dec. It then + calls FIND_INC for each one. */ + +static bool +find_mem (rtx *address_of_x) +{ + rtx x = *address_of_x; + enum rtx_code code = GET_CODE (x); + const char *const fmt = GET_RTX_FORMAT (code); + int i; + + if (code == MEM && REG_P (XEXP (x, 0))) + { + /* Match with *reg0. */ + mem_insn.mem_loc = address_of_x; + mem_insn.reg0 = XEXP (x, 0); + mem_insn.reg1_is_const = true; + mem_insn.reg1_val = 0; + mem_insn.reg1 = GEN_INT (0); + if (find_inc (true)) + return true; + } + if (code == MEM && GET_CODE (XEXP (x, 0)) == PLUS + && REG_P (XEXP (XEXP (x, 0), 0))) + { + rtx reg1 = XEXP (XEXP (x, 0), 1); + mem_insn.mem_loc = address_of_x; + mem_insn.reg0 = XEXP (XEXP (x, 0), 0); + mem_insn.reg1 = reg1; + if (GET_CODE (reg1) == CONST_INT) + { + mem_insn.reg1_is_const = true; + /* Match with *(reg0 + c) where c is a const. */ + mem_insn.reg1_val = INTVAL (reg1); + if (find_inc (true)) + return true; + } + else if (REG_P (reg1)) + { + /* Match with *(reg0 + reg1). */ + mem_insn.reg1_is_const = false; + if (find_inc (true)) + return true; + } + } + + if (code == SIGN_EXTRACT || code == ZERO_EXTRACT) + { + /* If REG occurs inside a MEM used in a bit-field reference, + that is unacceptable. */ + return false; + } + + /* Time for some deep diving. */ + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + if (find_mem (&XEXP (x, i))) + return true; + } + else if (fmt[i] == 'E') + { + int j; + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + if (find_mem (&XVECEXP (x, i, j))) + return true; + } + } + return false; +} + + +/* Try to combine all incs and decs by constant values with memory + references in BB. */ + +static void +merge_in_block (int max_reg, basic_block bb) +{ + rtx insn; + rtx curr; + int success_in_block = 0; + + if (dump_file) + fprintf (dump_file, "\n\nstarting bb %d\n", bb->index); + + FOR_BB_INSNS_REVERSE_SAFE (bb, insn, curr) + { + unsigned int uid = INSN_UID (insn); + bool insn_is_add_or_inc = true; + + if (!INSN_P (insn)) + continue; + + /* This continue is deliberate. We do not want the uses of the + jump put into reg_next_use because it is not considered safe to + combine a preincrement with a jump. */ + if (JUMP_P (insn)) + continue; + + if (dump_file) + dump_insn_slim (dump_file, insn); + + /* Does this instruction increment or decrement a register? */ + if (parse_add_or_inc (insn, true)) + { + int regno = REGNO (inc_insn.reg_res); + /* Cannot handle case where there are three separate regs + before a mem ref. Too many moves would be needed to be + profitable. */ + if ((inc_insn.form == FORM_PRE_INC) || inc_insn.reg1_is_const) + { + mem_insn.insn = get_next_ref (regno, bb, reg_next_use); + if (mem_insn.insn) + { + bool ok = true; + if (!inc_insn.reg1_is_const) + { + /* We are only here if we are going to try a + HAVE_*_MODIFY_REG type transformation. c is a + reg and we must sure that the path from the + inc_insn to the mem_insn.insn is both def and use + clear of c because the inc insn is going to move + into the mem_insn.insn. */ + int luid = DF_INSN_LUID (mem_insn.insn); + rtx other_insn + = get_next_ref (REGNO (inc_insn.reg1), bb, reg_next_use); + + if (other_insn && luid > DF_INSN_LUID (other_insn)) + ok = false; + + other_insn + = get_next_ref (REGNO (inc_insn.reg1), bb, reg_next_def); + + if (other_insn && luid > DF_INSN_LUID (other_insn)) + ok = false; + } + + if (dump_file) + dump_inc_insn (dump_file); + + if (ok && find_address (&PATTERN (mem_insn.insn)) == -1) + { + if (dump_file) + dump_mem_insn (dump_file); + if (try_merge ()) + { + success_in_block++; + insn_is_add_or_inc = false; + } + } + } + } + } + else + { + insn_is_add_or_inc = false; + mem_insn.insn = insn; + if (find_mem (&PATTERN (insn))) + success_in_block++; + } + + /* If the inc insn was merged with a mem, the inc insn is gone + and there is noting to update. */ + if (DF_INSN_UID_GET(uid)) + { + struct df_ref **def_rec; + struct df_ref **use_rec; + /* Need to update next use. */ + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + reg_next_use[DF_REF_REGNO (def)] = NULL; + reg_next_inc_use[DF_REF_REGNO (def)] = NULL; + reg_next_def[DF_REF_REGNO (def)] = insn; + } + + for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + reg_next_use[DF_REF_REGNO (use)] = insn; + if (insn_is_add_or_inc) + reg_next_inc_use[DF_REF_REGNO (use)] = insn; + else + reg_next_inc_use[DF_REF_REGNO (use)] = NULL; + } + } + else if (dump_file) + fprintf (dump_file, "skipping update of deleted insn %d\n", uid); + } + + /* If we were successful, try again. There may have been several + opportunities that were interleaved. This is rare but + gcc.c-torture/compile/pr17273.c actually exhibits this. */ + if (success_in_block) + { + /* In this case, we must clear these vectors since the trick of + testing if the stale insn in the block will not work. */ + memset (reg_next_use, 0, max_reg * sizeof(rtx)); + memset (reg_next_inc_use, 0, max_reg * sizeof(rtx)); + memset (reg_next_def, 0, max_reg * sizeof(rtx)); + df_recompute_luids (bb); + merge_in_block (max_reg, bb); + } +} + +#endif + +static unsigned int +rest_of_handle_auto_inc_dec (void) +{ +#ifdef AUTO_INC_DEC + basic_block bb; + int max_reg = max_reg_num (); + + if (!initialized) + init_decision_table (); + + mem_tmp = gen_rtx_MEM (Pmode, NULL_RTX); + + df_note_add_problem (); + df_analyze (); + + reg_next_use = XCNEWVEC (rtx, max_reg); + reg_next_inc_use = XCNEWVEC (rtx, max_reg); + reg_next_def = XCNEWVEC (rtx, max_reg); + FOR_EACH_BB (bb) + merge_in_block (max_reg, bb); + + free (reg_next_use); + free (reg_next_inc_use); + free (reg_next_def); + + mem_tmp = NULL; +#endif + return 0; +} + + +/* Discover auto-inc auto-dec instructions. */ + +static bool +gate_auto_inc_dec (void) +{ +#ifdef AUTO_INC_DEC + return (optimize > 0 && flag_auto_inc_dec); +#else + return false; +#endif +} + + +struct tree_opt_pass pass_inc_dec = +{ + "auto-inc-dec", /* name */ + gate_auto_inc_dec, /* gate */ + rest_of_handle_auto_inc_dec, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_AUTO_INC_DEC, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func | + TODO_df_finish, /* todo_flags_finish */ + 0 /* letter */ +}; + diff --git a/gcc/basic-block.h b/gcc/basic-block.h index 8d7dbe3..75bba81 100644 --- a/gcc/basic-block.h +++ b/gcc/basic-block.h @@ -1,6 +1,6 @@ /* Define control and data flow tables, and regsets. - Copyright (C) 1987, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 - Free Software Foundation, Inc. + Copyright (C) 1987, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, + 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -257,12 +257,6 @@ struct rtl_bb_info GTY(()) rtx head_; rtx end_; - /* The registers that are live on entry to this block. */ - bitmap GTY ((skip (""))) global_live_at_start; - - /* The registers that are live on exit from this block. */ - bitmap GTY ((skip (""))) global_live_at_end; - /* In CFGlayout mode points to insn notes/jumptables to be placed just before and after the block. */ rtx header; @@ -299,46 +293,45 @@ DEF_VEC_ALLOC_P(basic_block,heap); enum bb_flags { - - /* Set if insns in BB have are modified. Used for updating liveness info. */ - BB_DIRTY = 1, - /* Only set on blocks that have just been created by create_bb. */ - BB_NEW = 2, + BB_NEW = 1 << 0, /* Set by find_unreachable_blocks. Do not rely on this being set in any pass. */ - BB_REACHABLE = 4, + BB_REACHABLE = 1 << 1, /* Set for blocks in an irreducible loop by loop analysis. */ - BB_IRREDUCIBLE_LOOP = 8, + BB_IRREDUCIBLE_LOOP = 1 << 2, /* Set on blocks that may actually not be single-entry single-exit block. */ - BB_SUPERBLOCK = 16, + BB_SUPERBLOCK = 1 << 3, /* Set on basic blocks that the scheduler should not touch. This is used by SMS to prevent other schedulers from messing with the loop schedule. */ - BB_DISABLE_SCHEDULE = 32, + BB_DISABLE_SCHEDULE = 1 << 4, /* Set on blocks that should be put in a hot section. */ - BB_HOT_PARTITION = 64, + BB_HOT_PARTITION = 1 << 5, /* Set on blocks that should be put in a cold section. */ - BB_COLD_PARTITION = 128, + BB_COLD_PARTITION = 1 << 6, /* Set on block that was duplicated. */ - BB_DUPLICATED = 256, + BB_DUPLICATED = 1 << 7, + + /* Set if the label at the top of this block is the target of a non-local goto. */ + BB_NON_LOCAL_GOTO_TARGET = 1 << 8, /* Set on blocks that are in RTL format. */ - BB_RTL = 1024, + BB_RTL = 1 << 9 , /* Set on blocks that are forwarder blocks. Only used in cfgcleanup.c. */ - BB_FORWARDER_BLOCK = 2048, + BB_FORWARDER_BLOCK = 1 << 10, /* Set on blocks that cannot be threaded through. Only used in cfgcleanup.c. */ - BB_NONTHREADABLE_BLOCK = 4096 + BB_NONTHREADABLE_BLOCK = 1 << 11 }; /* Dummy flag for convenience in the hot/cold partitioning code. */ @@ -435,11 +428,23 @@ struct control_flow_graph GTY(()) (INSN) && (INSN) != NEXT_INSN (BB_END (BB)); \ (INSN) = NEXT_INSN (INSN)) +/* For iterating over insns in basic block when we might remove the + current insn. */ +#define FOR_BB_INSNS_SAFE(BB, INSN, CURR) \ + for ((INSN) = BB_HEAD (BB), (CURR) = (INSN) ? NEXT_INSN ((INSN)): NULL; \ + (INSN) && (INSN) != NEXT_INSN (BB_END (BB)); \ + (INSN) = (CURR), (CURR) = (INSN) ? NEXT_INSN ((INSN)) : NULL) + #define FOR_BB_INSNS_REVERSE(BB, INSN) \ for ((INSN) = BB_END (BB); \ (INSN) && (INSN) != PREV_INSN (BB_HEAD (BB)); \ (INSN) = PREV_INSN (INSN)) +#define FOR_BB_INSNS_REVERSE_SAFE(BB, INSN, CURR) \ + for ((INSN) = BB_END (BB),(CURR) = (INSN) ? PREV_INSN ((INSN)) : NULL; \ + (INSN) && (INSN) != PREV_INSN (BB_HEAD (BB)); \ + (INSN) = (CURR), (CURR) = (INSN) ? PREV_INSN ((INSN)) : NULL) + /* Cycles through _all_ basic blocks, even the fake ones (entry and exit block). */ @@ -451,18 +456,6 @@ struct control_flow_graph GTY(()) extern bitmap_obstack reg_obstack; -/* Indexed by n, gives number of basic block that (REG n) is used in. - If the value is REG_BLOCK_GLOBAL (-2), - it means (REG n) is used in more than one basic block. - REG_BLOCK_UNKNOWN (-1) means it hasn't been seen yet so we don't know. - This information remains valid for the rest of the compilation - of the current function; it is used to control register allocation. */ - -#define REG_BLOCK_UNKNOWN -1 -#define REG_BLOCK_GLOBAL -2 - -#define REG_BASIC_BLOCK(N) \ - (VEC_index (reg_info_p, reg_n_info, N)->basic_block) /* Stuff for recording basic block info. */ @@ -505,7 +498,8 @@ extern edge redirect_edge_succ_nodup (edge, basic_block); extern void redirect_edge_pred (edge, basic_block); extern basic_block create_basic_block_structure (rtx, rtx, rtx, basic_block); extern void clear_bb_flags (void); -extern int post_order_compute (int *, bool); +extern int post_order_compute (int *, bool, bool); +extern int inverted_post_order_compute (int *); extern int pre_and_rev_post_order_compute (int *, int *, bool); extern int dfs_enumerate_from (basic_block, int, bool (*)(basic_block, void *), @@ -515,7 +509,6 @@ extern void dump_bb_info (basic_block, bool, bool, int, const char *, FILE *); extern void dump_edge_info (FILE *, edge, int); extern void brief_dump_cfg (FILE *); extern void clear_edges (void); -extern rtx first_insn_after_basic_block_note (basic_block); extern void scale_bbs_frequencies_int (basic_block *, int, int, int); extern void scale_bbs_frequencies_gcov_type (basic_block *, int, gcov_type, gcov_type); @@ -788,76 +781,26 @@ void verify_edge_list (FILE *, struct edge_list *); int find_edge_index (struct edge_list *, basic_block, basic_block); edge find_edge (basic_block, basic_block); - -enum update_life_extent -{ - UPDATE_LIFE_LOCAL = 0, - UPDATE_LIFE_GLOBAL = 1, - UPDATE_LIFE_GLOBAL_RM_NOTES = 2 -}; - -/* Flags for life_analysis and update_life_info. */ - -#define PROP_DEATH_NOTES 1 /* Create DEAD and UNUSED notes. */ -#define PROP_LOG_LINKS 2 /* Create LOG_LINKS. */ -#define PROP_REG_INFO 4 /* Update regs_ever_live et al. */ -#define PROP_KILL_DEAD_CODE 8 /* Remove dead code. */ -#define PROP_SCAN_DEAD_CODE 16 /* Scan for dead code. */ -#define PROP_ALLOW_CFG_CHANGES 32 /* Allow the CFG to be changed - by dead code removal. */ -#define PROP_AUTOINC 64 /* Create autoinc mem references. */ -#define PROP_SCAN_DEAD_STORES 128 /* Scan for dead code. */ -#define PROP_ASM_SCAN 256 /* Internal flag used within flow.c - to flag analysis of asms. */ -#define PROP_DEAD_INSN 1024 /* Internal flag used within flow.c - to flag analysis of dead insn. */ -#define PROP_POST_REGSTACK 2048 /* We run after reg-stack and need - to preserve REG_DEAD notes for - stack regs. */ -#define PROP_FINAL (PROP_DEATH_NOTES | PROP_LOG_LINKS \ - | PROP_REG_INFO | PROP_KILL_DEAD_CODE \ - | PROP_SCAN_DEAD_CODE | PROP_AUTOINC \ - | PROP_ALLOW_CFG_CHANGES \ - | PROP_SCAN_DEAD_STORES) -#define PROP_POSTRELOAD (PROP_DEATH_NOTES \ - | PROP_KILL_DEAD_CODE \ - | PROP_SCAN_DEAD_CODE \ - | PROP_SCAN_DEAD_STORES) - #define CLEANUP_EXPENSIVE 1 /* Do relatively expensive optimizations except for edge forwarding */ #define CLEANUP_CROSSJUMP 2 /* Do crossjumping. */ #define CLEANUP_POST_REGSTACK 4 /* We run after reg-stack and need to care REG_DEAD notes. */ -#define CLEANUP_UPDATE_LIFE 8 /* Keep life information up to date. */ -#define CLEANUP_THREADING 16 /* Do jump threading. */ -#define CLEANUP_NO_INSN_DEL 32 /* Do not try to delete trivially dead +#define CLEANUP_THREADING 8 /* Do jump threading. */ +#define CLEANUP_NO_INSN_DEL 16 /* Do not try to delete trivially dead insns. */ -#define CLEANUP_CFGLAYOUT 64 /* Do cleanup in cfglayout mode. */ -#define CLEANUP_LOG_LINKS 128 /* Update log links. */ +#define CLEANUP_CFGLAYOUT 32 /* Do cleanup in cfglayout mode. */ /* The following are ORed in on top of the CLEANUP* flags in calls to struct_equiv_block_eq. */ -#define STRUCT_EQUIV_START 256 /* Initializes the search range. */ -#define STRUCT_EQUIV_RERUN 512 /* Rerun to find register use in +#define STRUCT_EQUIV_START 64 /* Initializes the search range. */ +#define STRUCT_EQUIV_RERUN 128 /* Rerun to find register use in found equivalence. */ -#define STRUCT_EQUIV_FINAL 1024 /* Make any changes necessary to get +#define STRUCT_EQUIV_FINAL 256 /* Make any changes necessary to get actual equivalence. */ -#define STRUCT_EQUIV_NEED_FULL_BLOCK 2048 /* struct_equiv_block_eq is required +#define STRUCT_EQUIV_NEED_FULL_BLOCK 512 /* struct_equiv_block_eq is required to match only full blocks */ -#define STRUCT_EQUIV_MATCH_JUMPS 4096 /* Also include the jumps at the end of the block in the comparison. */ - -extern void life_analysis (int); -extern int update_life_info (sbitmap, enum update_life_extent, int); -extern int update_life_info_in_dirty_blocks (enum update_life_extent, int); -extern int count_or_remove_death_notes (sbitmap, int); -extern int propagate_block (basic_block, regset, regset, regset, int); - -struct propagate_block_info; -extern rtx propagate_one_insn (struct propagate_block_info *, rtx); -extern struct propagate_block_info *init_propagate_block_info - (basic_block, regset, regset, regset, int); -extern void free_propagate_block_info (struct propagate_block_info *); +#define STRUCT_EQUIV_MATCH_JUMPS 1024 /* Also include the jumps at the end of the block in the comparison. */ /* In lcm.c */ extern struct edge_list *pre_edge_lcm (int, sbitmap *, sbitmap *, @@ -883,31 +826,19 @@ extern void remove_predictions_associated_with_edge (edge); extern bool edge_probability_reliable_p (edge); extern bool br_prob_note_reliable_p (rtx); -/* In flow.c */ +/* In cfg.c */ +extern void dump_regset (regset, FILE *); +extern void debug_regset (regset); extern void init_flow (void); extern void debug_bb (basic_block); extern basic_block debug_bb_n (int); extern void dump_regset (regset, FILE *); extern void debug_regset (regset); -extern void allocate_reg_life_data (void); extern void expunge_block (basic_block); extern void link_block (basic_block, basic_block); extern void unlink_block (basic_block); extern void compact_blocks (void); extern basic_block alloc_block (void); -extern void find_unreachable_blocks (void); -extern int delete_noop_moves (void); -extern basic_block force_nonfallthru (edge); -extern rtx block_label (basic_block); -extern bool forwarder_block_p (basic_block); -extern bool purge_all_dead_edges (void); -extern bool purge_dead_edges (basic_block); -extern void find_many_sub_basic_blocks (sbitmap); -extern void rtl_make_eh_edge (sbitmap, basic_block, rtx); -extern bool can_fallthru (basic_block, basic_block); -extern bool could_fall_through (basic_block, basic_block); -extern void flow_nodes_print (const char *, const sbitmap, FILE *); -extern void flow_edge_list_print (const char *, const edge *, int, FILE *); extern void alloc_aux_for_block (basic_block, int); extern void alloc_aux_for_blocks (int); extern void clear_aux_for_blocks (void); @@ -916,7 +847,27 @@ extern void alloc_aux_for_edge (edge, int); extern void alloc_aux_for_edges (int); extern void clear_aux_for_edges (void); extern void free_aux_for_edges (void); + +/* In cfganal.c */ +extern void find_unreachable_blocks (void); +extern bool forwarder_block_p (basic_block); +extern bool can_fallthru (basic_block, basic_block); +extern bool could_fall_through (basic_block, basic_block); +extern void flow_nodes_print (const char *, const sbitmap, FILE *); +extern void flow_edge_list_print (const char *, const edge *, int, FILE *); + +/* In cfgrtl.c */ +extern basic_block force_nonfallthru (edge); +extern rtx block_label (basic_block); +extern bool purge_all_dead_edges (void); +extern bool purge_dead_edges (basic_block); + +/* In cfgbuild.c. */ +extern void find_many_sub_basic_blocks (sbitmap); +extern void rtl_make_eh_edge (sbitmap, basic_block, rtx); extern void find_basic_blocks (rtx); + +/* In cfgcleanup.c. */ extern bool cleanup_cfg (int); extern bool delete_unreachable_blocks (void); diff --git a/gcc/bb-reorder.c b/gcc/bb-reorder.c index 27f24fc..79e9dbf 100644 --- a/gcc/bb-reorder.c +++ b/gcc/bb-reorder.c @@ -85,6 +85,7 @@ #include "params.h" #include "toplev.h" #include "tree-pass.h" +#include "df.h" #ifndef HAVE_conditional_execution #define HAVE_conditional_execution 0 @@ -1607,16 +1608,6 @@ fix_crossing_conditional_branches (void) last_bb->aux = new_bb; prev_bb = last_bb; last_bb = new_bb; - - /* Update register liveness information. */ - - new_bb->il.rtl->global_live_at_start = ALLOC_REG_SET (®_obstack); - new_bb->il.rtl->global_live_at_end = ALLOC_REG_SET (®_obstack); - COPY_REG_SET (new_bb->il.rtl->global_live_at_end, - prev_bb->il.rtl->global_live_at_end); - COPY_REG_SET (new_bb->il.rtl->global_live_at_start, - prev_bb->il.rtl->global_live_at_end); - /* Put appropriate instructions in new bb. */ new_label = gen_label_rtx (); @@ -1840,10 +1831,7 @@ fix_edges_for_rarely_executed_code (edge *crossing_edges, well. */ if (!HAS_LONG_UNCOND_BRANCH) - { - fix_crossing_unconditional_branches (); - reg_scan (get_insns (), max_reg_num ()); - } + fix_crossing_unconditional_branches (); add_reg_crossing_jump_notes (); } @@ -2205,13 +2193,11 @@ gate_handle_reorder_blocks (void) static unsigned int rest_of_handle_reorder_blocks (void) { - unsigned int liveness_flags; basic_block bb; /* Last attempt to optimize CFG, as scheduling, peepholing and insn splitting possibly introduced more crossjumping opportunities. */ - liveness_flags = (!HAVE_conditional_execution ? CLEANUP_UPDATE_LIFE : 0); - cfg_layout_initialize (CLEANUP_EXPENSIVE | liveness_flags); + cfg_layout_initialize (CLEANUP_EXPENSIVE); if (flag_sched2_use_traces && flag_schedule_insns_after_reload) { @@ -2224,14 +2210,7 @@ rest_of_handle_reorder_blocks (void) reorder_basic_blocks (); if (flag_reorder_blocks || flag_reorder_blocks_and_partition || (flag_sched2_use_traces && flag_schedule_insns_after_reload)) - cleanup_cfg (CLEANUP_EXPENSIVE | liveness_flags); - - /* On conditional execution targets we can not update the life cheaply, so - we deffer the updating to after both cleanups. This may lose some cases - but should not be terribly bad. */ - if (HAVE_conditional_execution) - update_life_info (NULL, UPDATE_LIFE_GLOBAL_RM_NOTES, - PROP_DEATH_NOTES); + cleanup_cfg (CLEANUP_EXPENSIVE); FOR_EACH_BB (bb) if (bb->next_bb != EXIT_BLOCK_PTR) @@ -2279,9 +2258,6 @@ rest_of_handle_partition_blocks (void) { no_new_pseudos = 0; partition_hot_cold_basic_blocks (); - allocate_reg_life_data (); - update_life_info (NULL, UPDATE_LIFE_GLOBAL_RM_NOTES, - PROP_LOG_LINKS | PROP_REG_INFO | PROP_DEATH_NOTES); no_new_pseudos = 1; return 0; } diff --git a/gcc/bitmap.c b/gcc/bitmap.c index 96889e0..740a883 100644 --- a/gcc/bitmap.c +++ b/gcc/bitmap.c @@ -1,6 +1,6 @@ /* Functions to support general ended bitmaps. - Copyright (C) 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005 - Free Software Foundation, Inc. + Copyright (C) 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, + 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -852,72 +852,151 @@ bitmap_and_into (bitmap a, bitmap b) gcc_assert (!a->current || a->indx == a->current->indx); } + +/* Insert an element equal to SRC_ELT after DST_PREV, overwriting DST_ELT + if non-NULL. CHANGED is true if the destination bitmap had already been + changed; the new value of CHANGED is returned. */ + +static inline bool +bitmap_elt_copy (bitmap dst, bitmap_element *dst_elt, bitmap_element *dst_prev, + bitmap_element *src_elt, bool changed) +{ + if (!changed && dst_elt && dst_elt->indx == src_elt->indx) + { + unsigned ix; + + for (ix = BITMAP_ELEMENT_WORDS; ix--;) + if (src_elt->bits[ix] != dst_elt->bits[ix]) + { + dst_elt->bits[ix] = src_elt->bits[ix]; + changed = true; + } + } + else + { + changed = true; + if (!dst_elt) + dst_elt = bitmap_elt_insert_after (dst, dst_prev, src_elt->indx); + else + dst_elt->indx = src_elt->indx; + memcpy (dst_elt->bits, src_elt->bits, sizeof (dst_elt->bits)); + } + return changed; +} + + + /* DST = A & ~B */ -void +bool bitmap_and_compl (bitmap dst, bitmap a, bitmap b) { bitmap_element *dst_elt = dst->first; bitmap_element *a_elt = a->first; bitmap_element *b_elt = b->first; bitmap_element *dst_prev = NULL; + bitmap_element **dst_prev_pnext = &dst->first; + bool changed = false; gcc_assert (dst != a && dst != b); if (a == b) { + changed = !bitmap_empty_p (dst); bitmap_clear (dst); - return; + return changed; } while (a_elt) { - if (!b_elt || a_elt->indx < b_elt->indx) + while (b_elt && b_elt->indx < a_elt->indx) + b_elt = b_elt->next; + + if (!b_elt || b_elt->indx > a_elt->indx) { - /* Copy a_elt. */ - if (!dst_elt) - dst_elt = bitmap_elt_insert_after (dst, dst_prev, a_elt->indx); - else - dst_elt->indx = a_elt->indx; - memcpy (dst_elt->bits, a_elt->bits, sizeof (dst_elt->bits)); - dst_prev = dst_elt; - dst_elt = dst_elt->next; + changed = bitmap_elt_copy (dst, dst_elt, dst_prev, a_elt, changed); + dst_prev = *dst_prev_pnext; + dst_prev_pnext = &dst_prev->next; + dst_elt = *dst_prev_pnext; a_elt = a_elt->next; } - else if (b_elt->indx < a_elt->indx) - b_elt = b_elt->next; + else { /* Matching elts, generate A & ~B. */ unsigned ix; BITMAP_WORD ior = 0; - if (!dst_elt) - dst_elt = bitmap_elt_insert_after (dst, dst_prev, a_elt->indx); + if (!changed && dst_elt && dst_elt->indx == a_elt->indx) + { + for (ix = BITMAP_ELEMENT_WORDS; ix--;) + { + BITMAP_WORD r = a_elt->bits[ix] & ~b_elt->bits[ix]; + + if (dst_elt->bits[ix] != r) + { + changed = true; + dst_elt->bits[ix] = r; + } + ior |= r; + } + } else - dst_elt->indx = a_elt->indx; - for (ix = BITMAP_ELEMENT_WORDS; ix--;) { - BITMAP_WORD r = a_elt->bits[ix] & ~b_elt->bits[ix]; + bool new_element; + if (!dst_elt || dst_elt->indx > a_elt->indx) + { + dst_elt = bitmap_elt_insert_after (dst, dst_prev, a_elt->indx); + new_element = true; + } + else + { + dst_elt->indx = a_elt->indx; + new_element = false; + } - dst_elt->bits[ix] = r; - ior |= r; + for (ix = BITMAP_ELEMENT_WORDS; ix--;) + { + BITMAP_WORD r = a_elt->bits[ix] & ~b_elt->bits[ix]; + + dst_elt->bits[ix] = r; + ior |= r; + } + + if (ior) + changed = true; + else + { + changed |= !new_element; + bitmap_element_free (dst, dst_elt); + dst_elt = *dst_prev_pnext; + } } + if (ior) { - dst_prev = dst_elt; - dst_elt = dst_elt->next; + dst_prev = *dst_prev_pnext; + dst_prev_pnext = &dst_prev->next; + dst_elt = *dst_prev_pnext; } a_elt = a_elt->next; b_elt = b_elt->next; } } + /* Ensure that dst->current is valid. */ dst->current = dst->first; - bitmap_elt_clear_from (dst, dst_elt); + + if (dst_elt) + { + changed = true; + bitmap_elt_clear_from (dst, dst_elt); + } gcc_assert (!dst->current == !dst->first); if (dst->current) dst->indx = dst->current->indx; + + return changed; } /* A &= ~B. Returns true if A changes */ @@ -974,15 +1053,120 @@ bitmap_and_compl_into (bitmap a, bitmap b) return changed != 0; } +/* Set COUNT bits from START in HEAD. */ +void +bitmap_set_range (bitmap head, unsigned int start, unsigned int count) +{ + unsigned int first_index, end_bit_plus1, last_index; + bitmap_element *elt, *elt_prev; + unsigned int i; + + if (!count) + return; + + first_index = start / BITMAP_ELEMENT_ALL_BITS; + end_bit_plus1 = start + count; + last_index = (end_bit_plus1 - 1) / BITMAP_ELEMENT_ALL_BITS; + elt = bitmap_find_bit (head, start); + + /* If bitmap_find_bit returns zero, the current is the closest block + to the result. Otherwise, just use bitmap_element_allocate to + ensure ELT is set; in the loop below, ELT == NULL means "insert + at the end of the bitmap". */ + if (!elt) + { + elt = bitmap_element_allocate (head); + elt->indx = first_index; + bitmap_element_link (head, elt); + } + + gcc_assert (elt->indx == first_index); + elt_prev = elt->prev; + for (i = first_index; i <= last_index; i++) + { + unsigned elt_start_bit = i * BITMAP_ELEMENT_ALL_BITS; + unsigned elt_end_bit_plus1 = elt_start_bit + BITMAP_ELEMENT_ALL_BITS; + + unsigned int first_word_to_mod; + BITMAP_WORD first_mask; + unsigned int last_word_to_mod; + BITMAP_WORD last_mask; + unsigned int ix; + + if (!elt || elt->indx != i) + elt = bitmap_elt_insert_after (head, elt_prev, i); + + if (elt_start_bit <= start) + { + /* The first bit to turn on is somewhere inside this + elt. */ + first_word_to_mod = (start - elt_start_bit) / BITMAP_WORD_BITS; + + /* This mask should have 1s in all bits >= start position. */ + first_mask = + (((BITMAP_WORD) 1) << ((start % BITMAP_WORD_BITS))) - 1; + first_mask = ~first_mask; + } + else + { + /* The first bit to turn on is below this start of this elt. */ + first_word_to_mod = 0; + first_mask = ~(BITMAP_WORD) 0; + } + + if (elt_end_bit_plus1 <= end_bit_plus1) + { + /* The last bit to turn on is beyond this elt. */ + last_word_to_mod = BITMAP_ELEMENT_WORDS - 1; + last_mask = ~(BITMAP_WORD) 0; + } + else + { + /* The last bit to turn on is inside to this elt. */ + last_word_to_mod = + (end_bit_plus1 - elt_start_bit) / BITMAP_WORD_BITS; + + /* The last mask should have 1s below the end bit. */ + last_mask = + (((BITMAP_WORD) 1) << ((end_bit_plus1 % BITMAP_WORD_BITS))) - 1; + } + + if (first_word_to_mod == last_word_to_mod) + { + BITMAP_WORD mask = first_mask & last_mask; + elt->bits[first_word_to_mod] |= mask; + } + else + { + elt->bits[first_word_to_mod] |= first_mask; + if (BITMAP_ELEMENT_WORDS > 2) + for (ix = first_word_to_mod + 1; ix < last_word_to_mod; ix++) + elt->bits[ix] = ~(BITMAP_WORD) 0; + elt->bits[last_word_to_mod] |= last_mask; + } + + elt_prev = elt; + elt = elt->next; + } + + head->current = elt ? elt : elt_prev; + head->indx = head->current->indx; +} + /* Clear COUNT bits from START in HEAD. */ void bitmap_clear_range (bitmap head, unsigned int start, unsigned int count) { - unsigned int first_index = start / BITMAP_ELEMENT_ALL_BITS; - unsigned int end_bit_plus1 = start + count; - unsigned int end_bit = end_bit_plus1 - 1; - unsigned int last_index = (end_bit) / BITMAP_ELEMENT_ALL_BITS; - bitmap_element *elt = bitmap_find_bit (head, start); + unsigned int first_index, end_bit_plus1, last_index; + bitmap_element *elt; + + if (!count) + return; + + first_index = start / BITMAP_ELEMENT_ALL_BITS; + end_bit_plus1 = start + count; + last_index = (end_bit_plus1 - 1) / BITMAP_ELEMENT_ALL_BITS; + elt = bitmap_find_bit (head, start); /* If bitmap_find_bit returns zero, the current is the closest block to the result. If the current is less than first index, find the @@ -1070,8 +1254,9 @@ bitmap_clear_range (bitmap head, unsigned int start, unsigned int count) else { elt->bits[first_word_to_mod] &= ~first_mask; - for (i = first_word_to_mod + 1; i < last_word_to_mod; i++) - elt->bits[i] = 0; + if (BITMAP_ELEMENT_WORDS > 2) + for (i = first_word_to_mod + 1; i < last_word_to_mod; i++) + elt->bits[i] = 0; elt->bits[last_word_to_mod] &= ~last_mask; } for (i = 0; i < BITMAP_ELEMENT_WORDS; i++) @@ -1163,6 +1348,66 @@ bitmap_compl_and_into (bitmap a, bitmap b) return; } + +/* Insert an element corresponding to A_ELT | B_ELT after DST_PREV, + overwriting DST_ELT if non-NULL. CHANGED is true if the destination bitmap + had already been changed; the new value of CHANGED is returned. */ + +static inline bool +bitmap_elt_ior (bitmap dst, bitmap_element *dst_elt, bitmap_element *dst_prev, + bitmap_element *a_elt, bitmap_element *b_elt, + bool changed) +{ + gcc_assert (a_elt || b_elt); + + if (a_elt && b_elt && a_elt->indx == b_elt->indx) + { + /* Matching elts, generate A | B. */ + unsigned ix; + + if (!changed && dst_elt && dst_elt->indx == a_elt->indx) + { + for (ix = BITMAP_ELEMENT_WORDS; ix--;) + { + BITMAP_WORD r = a_elt->bits[ix] | b_elt->bits[ix]; + if (r != dst_elt->bits[ix]) + { + dst_elt->bits[ix] = r; + changed = true; + } + } + } + else + { + changed = true; + if (!dst_elt) + dst_elt = bitmap_elt_insert_after (dst, dst_prev, a_elt->indx); + else + dst_elt->indx = a_elt->indx; + for (ix = BITMAP_ELEMENT_WORDS; ix--;) + { + BITMAP_WORD r = a_elt->bits[ix] | b_elt->bits[ix]; + dst_elt->bits[ix] = r; + } + } + } + else + { + /* Copy a single element. */ + bitmap_element *src; + + if (!b_elt || (a_elt && a_elt->indx < b_elt->indx)) + src = a_elt; + else + src = b_elt; + + gcc_assert (src); + changed = bitmap_elt_copy (dst, dst_elt, dst_prev, src, changed); + } + return changed; +} + + /* DST = A | B. Return true if DST changes. */ bool @@ -1172,89 +1417,31 @@ bitmap_ior (bitmap dst, bitmap a, bitmap b) bitmap_element *a_elt = a->first; bitmap_element *b_elt = b->first; bitmap_element *dst_prev = NULL; + bitmap_element **dst_prev_pnext = &dst->first; bool changed = false; gcc_assert (dst != a && dst != b); while (a_elt || b_elt) { + changed = bitmap_elt_ior (dst, dst_elt, dst_prev, a_elt, b_elt, changed); + if (a_elt && b_elt && a_elt->indx == b_elt->indx) { - /* Matching elts, generate A | B. */ - unsigned ix; - - if (!changed && dst_elt && dst_elt->indx == a_elt->indx) - { - for (ix = BITMAP_ELEMENT_WORDS; ix--;) - { - BITMAP_WORD r = a_elt->bits[ix] | b_elt->bits[ix]; - - if (r != dst_elt->bits[ix]) - { - dst_elt->bits[ix] = r; - changed = true; - } - } - } - else - { - changed = true; - if (!dst_elt) - dst_elt = bitmap_elt_insert_after (dst, dst_prev, a_elt->indx); - else - dst_elt->indx = a_elt->indx; - for (ix = BITMAP_ELEMENT_WORDS; ix--;) - { - BITMAP_WORD r = a_elt->bits[ix] | b_elt->bits[ix]; - - dst_elt->bits[ix] = r; - } - } a_elt = a_elt->next; b_elt = b_elt->next; - dst_prev = dst_elt; - dst_elt = dst_elt->next; } else { - /* Copy a single element. */ - bitmap_element *src; - - if (!b_elt || (a_elt && a_elt->indx < b_elt->indx)) - { - src = a_elt; - a_elt = a_elt->next; - } - else - { - src = b_elt; - b_elt = b_elt->next; - } - - if (!changed && dst_elt && dst_elt->indx == src->indx) - { - unsigned ix; - - for (ix = BITMAP_ELEMENT_WORDS; ix--;) - if (src->bits[ix] != dst_elt->bits[ix]) - { - dst_elt->bits[ix] = src->bits[ix]; - changed = true; - } - } - else - { - changed = true; - if (!dst_elt) - dst_elt = bitmap_elt_insert_after (dst, dst_prev, src->indx); - else - dst_elt->indx = src->indx; - memcpy (dst_elt->bits, src->bits, sizeof (dst_elt->bits)); - } - - dst_prev = dst_elt; - dst_elt = dst_elt->next; + if (a_elt && (!b_elt || a_elt->indx <= b_elt->indx)) + a_elt = a_elt->next; + else if (b_elt && (!a_elt || b_elt->indx <= a_elt->indx)) + b_elt = b_elt->next; } + + dst_prev = *dst_prev_pnext; + dst_prev_pnext = &dst_prev->next; + dst_elt = *dst_prev_pnext; } if (dst_elt) @@ -1276,6 +1463,7 @@ bitmap_ior_into (bitmap a, bitmap b) bitmap_element *a_elt = a->first; bitmap_element *b_elt = b->first; bitmap_element *a_prev = NULL; + bitmap_element **a_prev_pnext = &a->first; bool changed = false; if (a == b) @@ -1283,48 +1471,23 @@ bitmap_ior_into (bitmap a, bitmap b) while (b_elt) { - if (!a_elt || b_elt->indx < a_elt->indx) + /* If A lags behind B, just advance it. */ + if (!a_elt || a_elt->indx == b_elt->indx) { - /* Copy b_elt. */ - bitmap_element *dst = bitmap_elt_insert_after (a, a_prev, b_elt->indx); - memcpy (dst->bits, b_elt->bits, sizeof (dst->bits)); - a_prev = dst; + changed = bitmap_elt_ior (a, a_elt, a_prev, a_elt, b_elt, changed); b_elt = b_elt->next; - changed = true; - } - else if (a_elt->indx < b_elt->indx) - { - a_prev = a_elt; - a_elt = a_elt->next; } - else + else if (a_elt->indx > b_elt->indx) { - /* Matching elts, generate A |= B. */ - unsigned ix; - - if (changed) - for (ix = BITMAP_ELEMENT_WORDS; ix--;) - { - BITMAP_WORD r = a_elt->bits[ix] | b_elt->bits[ix]; - - a_elt->bits[ix] = r; - } - else - for (ix = BITMAP_ELEMENT_WORDS; ix--;) - { - BITMAP_WORD r = a_elt->bits[ix] | b_elt->bits[ix]; - - if (a_elt->bits[ix] != r) - { - changed = true; - a_elt->bits[ix] = r; - } - } + changed = bitmap_elt_copy (a, NULL, a_prev, b_elt, changed); b_elt = b_elt->next; - a_prev = a_elt; - a_elt = a_elt->next; } + + a_prev = *a_prev_pnext; + a_prev_pnext = &a_prev->next; + a_elt = *a_prev_pnext; } + gcc_assert (!a->current == !a->first); if (a->current) a->indx = a->current->indx; @@ -1548,15 +1711,103 @@ bitmap_intersect_compl_p (bitmap a, bitmap b) /* DST = A | (FROM1 & ~FROM2). Return true if DST changes. */ bool -bitmap_ior_and_compl (bitmap dst, bitmap a, bitmap from1, bitmap from2) +bitmap_ior_and_compl (bitmap dst, bitmap a, bitmap b, bitmap kill) { - bitmap_head tmp; - bool changed; + bool changed = false; - bitmap_initialize (&tmp, &bitmap_default_obstack); - bitmap_and_compl (&tmp, from1, from2); - changed = bitmap_ior (dst, a, &tmp); - bitmap_clear (&tmp); + bitmap_element *dst_elt = dst->first; + bitmap_element *a_elt = a->first; + bitmap_element *b_elt = b->first; + bitmap_element *kill_elt = kill->first; + bitmap_element *dst_prev = NULL; + bitmap_element **dst_prev_pnext = &dst->first; + + gcc_assert (dst != a && dst != b && dst != kill); + + /* Special cases. We don't bother checking for bitmap_equal_p (b, kill). */ + if (b == kill || bitmap_empty_p (b)) + { + changed = !bitmap_equal_p (dst, a); + if (changed) + bitmap_copy (dst, a); + return changed; + } + if (bitmap_empty_p (kill)) + return bitmap_ior (dst, a, b); + if (bitmap_empty_p (a)) + return bitmap_and_compl (dst, b, kill); + + while (a_elt || b_elt) + { + bool new_element = false; + + if (b_elt) + while (kill_elt && kill_elt->indx < b_elt->indx) + kill_elt = kill_elt->next; + + if (b_elt && kill_elt && kill_elt->indx == b_elt->indx + && (!a_elt || a_elt->indx >= b_elt->indx)) + { + bitmap_element tmp_elt; + unsigned ix; + + BITMAP_WORD ior = 0; + tmp_elt.indx = b_elt->indx; + for (ix = BITMAP_ELEMENT_WORDS; ix--;) + { + BITMAP_WORD r = b_elt->bits[ix] & ~kill_elt->bits[ix]; + ior |= r; + tmp_elt.bits[ix] = r; + } + + if (ior) + { + changed = bitmap_elt_ior (dst, dst_elt, dst_prev, + a_elt, &tmp_elt, changed); + new_element = true; + if (a_elt && a_elt->indx == b_elt->indx) + a_elt = a_elt->next; + } + + b_elt = b_elt->next; + kill_elt = kill_elt->next; + } + else + { + changed = bitmap_elt_ior (dst, dst_elt, dst_prev, + a_elt, b_elt, changed); + new_element = true; + + if (a_elt && b_elt && a_elt->indx == b_elt->indx) + { + a_elt = a_elt->next; + b_elt = b_elt->next; + } + else + { + if (a_elt && (!b_elt || a_elt->indx <= b_elt->indx)) + a_elt = a_elt->next; + else if (b_elt && (!a_elt || b_elt->indx <= a_elt->indx)) + b_elt = b_elt->next; + } + } + + if (new_element) + { + dst_prev = *dst_prev_pnext; + dst_prev_pnext = &dst_prev->next; + dst_elt = *dst_prev_pnext; + } + } + + if (dst_elt) + { + changed = true; + bitmap_elt_clear_from (dst, dst_elt); + } + gcc_assert (!dst->current == !dst->first); + if (dst->current) + dst->indx = dst->current->indx; return changed; } @@ -1576,6 +1827,7 @@ bitmap_ior_and_compl_into (bitmap a, bitmap from1, bitmap from2) return changed; } + /* Debugging function to print out the contents of a bitmap. */ diff --git a/gcc/bitmap.h b/gcc/bitmap.h index a81945d..2959603 100644 --- a/gcc/bitmap.h +++ b/gcc/bitmap.h @@ -1,6 +1,6 @@ /* Functions to support general ended bitmaps. - Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 - Free Software Foundation, Inc. + Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -117,11 +117,12 @@ extern unsigned long bitmap_count_bits (bitmap); The operations supported are &, & ~, |, ^. */ extern void bitmap_and (bitmap, bitmap, bitmap); extern void bitmap_and_into (bitmap, bitmap); -extern void bitmap_and_compl (bitmap, bitmap, bitmap); +extern bool bitmap_and_compl (bitmap, bitmap, bitmap); extern bool bitmap_and_compl_into (bitmap, bitmap); #define bitmap_compl_and(DST, A, B) bitmap_and_compl (DST, B, A) extern void bitmap_compl_and_into (bitmap, bitmap); extern void bitmap_clear_range (bitmap, unsigned int, unsigned int); +extern void bitmap_set_range (bitmap, unsigned int, unsigned int); extern bool bitmap_ior (bitmap, bitmap, bitmap); extern bool bitmap_ior_into (bitmap, bitmap); extern void bitmap_xor (bitmap, bitmap, bitmap); diff --git a/gcc/bt-load.c b/gcc/bt-load.c index 4a33940..710c788 100644 --- a/gcc/bt-load.c +++ b/gcc/bt-load.c @@ -1,5 +1,5 @@ /* Perform branch target register load optimizations. - Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 + Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -37,6 +37,8 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "tm_p.h" #include "toplev.h" #include "tree-pass.h" +#include "recog.h" +#include "df.h" /* Target register optimizations - these are performed after reload. */ @@ -476,7 +478,7 @@ compute_defs_uses_and_gen (fibheap_t all_btr_defs, btr_def *def_array, CLEAR_HARD_REG_SET (info.btrs_written_in_block); for (reg = first_btr; reg <= last_btr; reg++) if (TEST_HARD_REG_BIT (all_btrs, reg) - && REGNO_REG_SET_P (bb->il.rtl->global_live_at_start, reg)) + && REGNO_REG_SET_P (DF_LIVE_IN (bb), reg)) SET_HARD_REG_BIT (info.btrs_live_in_block, reg); for (insn = BB_HEAD (bb), last = NEXT_INSN (BB_END (bb)); @@ -508,7 +510,7 @@ compute_defs_uses_and_gen (fibheap_t all_btr_defs, btr_def *def_array, } /* Check for the blockage emitted by expand_nl_goto_receiver. */ else if (current_function_has_nonlocal_label - && GET_CODE (PATTERN (insn)) == ASM_INPUT) + && GET_CODE (PATTERN (insn)) == UNSPEC_VOLATILE) { btr_user user; @@ -577,7 +579,7 @@ compute_defs_uses_and_gen (fibheap_t all_btr_defs, btr_def *def_array, COPY_HARD_REG_SET (btrs_live[i], info.btrs_live_in_block); COPY_HARD_REG_SET (btrs_written[i], info.btrs_written_in_block); - REG_SET_TO_HARD_REG_SET (btrs_live_at_end[i], bb->il.rtl->global_live_at_end); + REG_SET_TO_HARD_REG_SET (btrs_live_at_end[i], DF_LIVE_OUT (bb)); /* If this block ends in a jump insn, add any uses or even clobbers of branch target registers that it might have. */ for (insn = BB_END (bb); insn != BB_HEAD (bb) && ! INSN_P (insn); ) @@ -1203,7 +1205,7 @@ move_btr_def (basic_block new_def_bb, int btr, btr_def def, bitmap live_range, /* Insert target register initialization at head of basic block. */ def->insn = emit_insn_after (new_insn, insp); - regs_ever_live[btr] = 1; + df_set_regs_ever_live (btr, true); if (dump_file) fprintf (dump_file, "New pt is insn %d, inserted after insn %d\n", @@ -1226,7 +1228,7 @@ move_btr_def (basic_block new_def_bb, int btr, btr_def def, bitmap live_range, replacement_rtx = btr_rtx; else replacement_rtx = gen_rtx_REG (GET_MODE (user->use), btr); - replace_rtx (user->insn, user->use, replacement_rtx); + validate_replace_rtx (user->insn, user->use, replacement_rtx); user->use = replacement_rtx; } } @@ -1418,7 +1420,8 @@ migrate_btr_defs (enum reg_class btr_class, int allow_callee_save) CLEAR_HARD_REG_SET (all_btrs); for (first_btr = -1, reg = 0; reg < FIRST_PSEUDO_REGISTER; reg++) if (TEST_HARD_REG_BIT (reg_class_contents[(int) btr_class], reg) - && (allow_callee_save || call_used_regs[reg] || regs_ever_live[reg])) + && (allow_callee_save || call_used_regs[reg] + || df_regs_ever_live_p (reg))) { SET_HARD_REG_BIT (all_btrs, reg); last_btr = reg; @@ -1455,7 +1458,7 @@ migrate_btr_defs (enum reg_class btr_class, int allow_callee_save) fibheap_delete (all_btr_defs); } -void +static void branch_target_load_optimize (bool after_prologue_epilogue_gen) { enum reg_class class = targetm.branch_target_register_class (); @@ -1467,14 +1470,17 @@ branch_target_load_optimize (bool after_prologue_epilogue_gen) else issue_rate = 1; - /* Build the CFG for migrate_btr_defs. */ + if (!after_prologue_epilogue_gen) + { + /* Build the CFG for migrate_btr_defs. */ #if 1 - /* This may or may not be needed, depending on where we - run this phase. */ - cleanup_cfg (optimize ? CLEANUP_EXPENSIVE : 0); + /* This may or may not be needed, depending on where we + run this phase. */ + cleanup_cfg (optimize ? CLEANUP_EXPENSIVE : 0); #endif + } + df_analyze (); - life_analysis (0); /* Dominator info is also needed for migrate_btr_def. */ calculate_dominance_info (CDI_DOMINATORS); @@ -1483,21 +1489,50 @@ branch_target_load_optimize (bool after_prologue_epilogue_gen) (after_prologue_epilogue_gen))); free_dominance_info (CDI_DOMINATORS); - - update_life_info (NULL, UPDATE_LIFE_GLOBAL_RM_NOTES, - PROP_DEATH_NOTES | PROP_REG_INFO); } } static bool -gate_handle_branch_target_load_optimize (void) +gate_handle_branch_target_load_optimize1 (void) +{ + return flag_branch_target_load_optimize; +} + + +static unsigned int +rest_of_handle_branch_target_load_optimize1 (void) +{ + branch_target_load_optimize (epilogue_completed); + return 0; +} + +struct tree_opt_pass pass_branch_target_load_optimize1 = +{ + "btl1", /* name */ + gate_handle_branch_target_load_optimize1, /* gate */ + rest_of_handle_branch_target_load_optimize1, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func | + TODO_ggc_collect, /* todo_flags_finish */ + 'd' /* letter */ +}; + +static bool +gate_handle_branch_target_load_optimize2 (void) { return (optimize > 0 && flag_branch_target_load_optimize2); } static unsigned int -rest_of_handle_branch_target_load_optimize (void) +rest_of_handle_branch_target_load_optimize2 (void) { static int warned = 0; @@ -1518,11 +1553,11 @@ rest_of_handle_branch_target_load_optimize (void) return 0; } -struct tree_opt_pass pass_branch_target_load_optimize = +struct tree_opt_pass pass_branch_target_load_optimize2 = { - "btl", /* name */ - gate_handle_branch_target_load_optimize, /* gate */ - rest_of_handle_branch_target_load_optimize, /* execute */ + "btl2", /* name */ + gate_handle_branch_target_load_optimize2, /* gate */ + rest_of_handle_branch_target_load_optimize2, /* execute */ NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ diff --git a/gcc/builtins.c b/gcc/builtins.c index 80f8978..2f3f1a9 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -761,12 +761,10 @@ expand_builtin_setjmp_receiver (rtx receiver_label ATTRIBUTE_UNUSED) #endif { /* Nothing */ } - /* @@@ This is a kludge. Not all machine descriptions define a blockage - insn, but we must not allow the code we just generated to be reordered - by scheduling. Specifically, the update of the frame pointer must - happen immediately, not later. So emit an ASM_INPUT to act as blockage - insn. */ - emit_insn (gen_rtx_ASM_INPUT (VOIDmode, "")); + /* We must not allow the code we just generated to be reordered by + scheduling. Specifically, the update of the frame pointer must + happen immediately, not later. */ + emit_insn (gen_blockage ()); } /* __builtin_longjmp is passed a pointer to an array of five words (not diff --git a/gcc/caller-save.c b/gcc/caller-save.c index 22b928e..a5cd2e3 100644 --- a/gcc/caller-save.c +++ b/gcc/caller-save.c @@ -36,6 +36,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "toplev.h" #include "tm_p.h" #include "addresses.h" +#include "df.h" #ifndef MAX_MOVE_MAX #define MAX_MOVE_MAX MOVE_MAX @@ -189,8 +190,8 @@ init_caller_save (void) savepat = gen_rtx_SET (VOIDmode, test_mem, test_reg); restpat = gen_rtx_SET (VOIDmode, test_reg, test_mem); - saveinsn = gen_rtx_INSN (VOIDmode, 0, 0, 0, 0, 0, savepat, -1, 0, 0); - restinsn = gen_rtx_INSN (VOIDmode, 0, 0, 0, 0, 0, restpat, -1, 0, 0); + saveinsn = gen_rtx_INSN (VOIDmode, 0, 0, 0, 0, 0, savepat, -1, 0); + restinsn = gen_rtx_INSN (VOIDmode, 0, 0, 0, 0, 0, restpat, -1, 0); for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) for (mode = 0 ; mode < MAX_MACHINE_MODE; mode++) @@ -200,7 +201,7 @@ init_caller_save (void) /* Update the register number and modes of the register and memory operand. */ - REGNO (test_reg) = i; + SET_REGNO (test_reg, i); PUT_MODE (test_reg, mode); PUT_MODE (test_mem, mode); diff --git a/gcc/calls.c b/gcc/calls.c index 32ca3ef..b339c04 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -41,6 +41,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "target.h" #include "cgraph.h" #include "except.h" +#include "dbgcnt.h" /* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */ #define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT) @@ -2222,7 +2223,8 @@ expand_call (tree exp, rtx target, int ignore) if (currently_expanding_call++ != 0 || !flag_optimize_sibling_calls || args_size.var - || lookup_stmt_eh_region (exp) >= 0) + || lookup_stmt_eh_region (exp) >= 0 + || dbg_cnt (tail_call) == false) try_tail_call = 0; /* Rest of purposes for tail call optimizations to fail. */ @@ -2855,9 +2857,10 @@ expand_call (tree exp, rtx target, int ignore) valreg = temp; } - /* For calls to `setjmp', etc., inform flow.c it should complain - if nonvolatile values are live. For functions that cannot return, - inform flow that control does not fall through. */ + /* For calls to `setjmp', etc., inform + function.c:setjmp_warnings that it should complain if + nonvolatile values are live. For functions that cannot + return, inform flow that control does not fall through. */ if ((flags & ECF_NORETURN) || pass == 0) { @@ -3816,9 +3819,10 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value, valreg, old_inhibit_defer_pop + 1, call_fusage, flags, & args_so_far); - /* For calls to `setjmp', etc., inform flow.c it should complain - if nonvolatile values are live. For functions that cannot return, - inform flow that control does not fall through. */ + /* For calls to `setjmp', etc., inform function.c:setjmp_warnings + that it should complain if nonvolatile values are live. For + functions that cannot return, inform flow that control does not + fall through. */ if (flags & ECF_NORETURN) { @@ -1,6 +1,6 @@ /* Control flow graph manipulation code for GNU compiler. Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005 + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -66,6 +66,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "ggc.h" #include "hashtab.h" #include "alloc-pool.h" +#include "df.h" #include "cfgloop.h" /* The obstack on which the flow graph components are allocated. */ @@ -163,24 +164,28 @@ void compact_blocks (void) { int i; - basic_block bb; SET_BASIC_BLOCK (ENTRY_BLOCK, ENTRY_BLOCK_PTR); SET_BASIC_BLOCK (EXIT_BLOCK, EXIT_BLOCK_PTR); - - i = NUM_FIXED_BLOCKS; - FOR_EACH_BB (bb) + + if (df) + df_compact_blocks (); + else { - SET_BASIC_BLOCK (i, bb); - bb->index = i; - i++; - } - - gcc_assert (i == n_basic_blocks); - - for (; i < last_basic_block; i++) - SET_BASIC_BLOCK (i, NULL); + basic_block bb; + + i = NUM_FIXED_BLOCKS; + FOR_EACH_BB (bb) + { + SET_BASIC_BLOCK (i, bb); + bb->index = i; + i++; + } + gcc_assert (i == n_basic_blocks); + for (; i < last_basic_block; i++) + SET_BASIC_BLOCK (i, NULL); + } last_basic_block = n_basic_blocks; } @@ -205,6 +210,7 @@ static inline void connect_src (edge e) { VEC_safe_push (edge, gc, e->src->succs, e); + df_mark_solutions_dirty (); } /* Connect E to E->dest. */ @@ -215,6 +221,7 @@ connect_dest (edge e) basic_block dest = e->dest; VEC_safe_push (edge, gc, dest->preds, e); e->dest_idx = EDGE_COUNT (dest->preds) - 1; + df_mark_solutions_dirty (); } /* Disconnect edge E from E->src. */ @@ -237,6 +244,7 @@ disconnect_src (edge e) ei_next (&ei); } + df_mark_solutions_dirty (); gcc_unreachable (); } @@ -254,6 +262,7 @@ disconnect_dest (edge e) to update dest_idx of the edge that moved into the "hole". */ if (dest_idx < EDGE_COUNT (dest->preds)) EDGE_PRED (dest, dest_idx)->dest_idx = dest_idx; + df_mark_solutions_dirty (); } /* Create an edge connecting SRC and DEST with flags FLAGS. Return newly @@ -275,7 +284,6 @@ unchecked_make_edge (basic_block src, basic_block dst, int flags) connect_dest (e); execute_on_growing_pred (e); - return e; } @@ -409,15 +417,16 @@ redirect_edge_pred (edge e, basic_block new_pred) connect_src (e); } -/* Clear all basic block flags, with the exception of partitioning. */ +/* Clear all basic block flags, with the exception of partitioning and + setjmp_target. */ void clear_bb_flags (void) { basic_block bb; FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb) - bb->flags = (BB_PARTITION (bb) | (bb->flags & BB_DISABLE_SCHEDULE) - | (bb->flags & BB_RTL)); + bb->flags = (BB_PARTITION (bb) + | (bb->flags & (BB_DISABLE_SCHEDULE + BB_RTL + BB_NON_LOCAL_GOTO_TARGET))); } /* Check the consistency of profile information. We can't do that @@ -469,6 +478,41 @@ check_bb_profile (basic_block bb, FILE * file) } } +/* Write information about registers and basic blocks into FILE. + This is part of making a debugging dump. */ + +void +dump_regset (regset r, FILE *outf) +{ + unsigned i; + reg_set_iterator rsi; + + if (r == NULL) + { + fputs (" (nil)", outf); + return; + } + + EXECUTE_IF_SET_IN_REG_SET (r, 0, i, rsi) + { + fprintf (outf, " %d", i); + if (i < FIRST_PSEUDO_REGISTER) + fprintf (outf, " [%s]", + reg_names[i]); + } +} + +/* Print a human-readable representation of R on the standard error + stream. This function is designed to be used from within the + debugger. */ + +void +debug_regset (regset r) +{ + dump_regset (r, stderr); + putc ('\n', stderr); +} + /* Emit basic block information for BB. HEADER is true if the user wants the generic information and the predecessors, FOOTER is true if they want the successors. FLAGS is the dump flags of interest; TDF_DETAILS emit @@ -500,6 +544,14 @@ dump_bb_info (basic_block bb, bool header, bool footer, int flags, fprintf (file, "%sPredecessors: ", prefix); FOR_EACH_EDGE (e, ei, bb->preds) dump_edge_info (file, e, 0); + + if ((flags & TDF_DETAILS) + && (bb->flags & BB_RTL) + && df) + { + fprintf (file, "\n"); + df_dump_top (bb, file); + } } if (footer) @@ -507,81 +559,92 @@ dump_bb_info (basic_block bb, bool header, bool footer, int flags, fprintf (file, "\n%sSuccessors: ", prefix); FOR_EACH_EDGE (e, ei, bb->succs) dump_edge_info (file, e, 1); - } - if ((flags & TDF_DETAILS) - && (bb->flags & BB_RTL)) - { - if (bb->il.rtl->global_live_at_start && header) + if ((flags & TDF_DETAILS) + && (bb->flags & BB_RTL) + && df) { - fprintf (file, "\n%sRegisters live at start:", prefix); - dump_regset (bb->il.rtl->global_live_at_start, file); - } - - if (bb->il.rtl->global_live_at_end && footer) - { - fprintf (file, "\n%sRegisters live at end:", prefix); - dump_regset (bb->il.rtl->global_live_at_end, file); + fprintf (file, "\n"); + df_dump_bottom (bb, file); } } putc ('\n', file); } +/* Dump the register info to FILE. */ + +void +dump_reg_info (FILE *file) +{ + unsigned int i, max = max_reg_num (); + if (reload_completed) + return; + + if (reg_info_p_size < max) + max = reg_info_p_size; + + fprintf (file, "%d registers.\n", max); + for (i = FIRST_PSEUDO_REGISTER; i < max; i++) + { + enum reg_class class, altclass; + + if (regstat_n_sets_and_refs) + fprintf (file, "\nRegister %d used %d times across %d insns", + i, REG_N_REFS (i), REG_LIVE_LENGTH (i)); + else if (df) + fprintf (file, "\nRegister %d used %d times across %d insns", + i, DF_REG_USE_COUNT (i) + DF_REG_DEF_COUNT (i), REG_LIVE_LENGTH (i)); + + if (REG_BASIC_BLOCK (i) >= NUM_FIXED_BLOCKS) + fprintf (file, " in block %d", REG_BASIC_BLOCK (i)); + if (regstat_n_sets_and_refs) + fprintf (file, "; set %d time%s", REG_N_SETS (i), + (REG_N_SETS (i) == 1) ? "" : "s"); + else if (df) + fprintf (file, "; set %d time%s", DF_REG_DEF_COUNT (i), + (DF_REG_DEF_COUNT (i) == 1) ? "" : "s"); + if (regno_reg_rtx[i] != NULL && REG_USERVAR_P (regno_reg_rtx[i])) + fprintf (file, "; user var"); + if (REG_N_DEATHS (i) != 1) + fprintf (file, "; dies in %d places", REG_N_DEATHS (i)); + if (REG_N_CALLS_CROSSED (i) == 1) + fprintf (file, "; crosses 1 call"); + else if (REG_N_CALLS_CROSSED (i)) + fprintf (file, "; crosses %d calls", REG_N_CALLS_CROSSED (i)); + if (regno_reg_rtx[i] != NULL + && PSEUDO_REGNO_BYTES (i) != UNITS_PER_WORD) + fprintf (file, "; %d bytes", PSEUDO_REGNO_BYTES (i)); + + class = reg_preferred_class (i); + altclass = reg_alternate_class (i); + if (class != GENERAL_REGS || altclass != ALL_REGS) + { + if (altclass == ALL_REGS || class == ALL_REGS) + fprintf (file, "; pref %s", reg_class_names[(int) class]); + else if (altclass == NO_REGS) + fprintf (file, "; %s or none", reg_class_names[(int) class]); + else + fprintf (file, "; pref %s, else %s", + reg_class_names[(int) class], + reg_class_names[(int) altclass]); + } + + if (regno_reg_rtx[i] != NULL && REG_POINTER (regno_reg_rtx[i])) + fprintf (file, "; pointer"); + fprintf (file, ".\n"); + } +} + + void dump_flow_info (FILE *file, int flags) { basic_block bb; /* There are no pseudo registers after reload. Don't dump them. */ - if (reg_n_info && !reload_completed - && (flags & TDF_DETAILS) != 0) - { - unsigned int i, max = max_reg_num (); - fprintf (file, "%d registers.\n", max); - for (i = FIRST_PSEUDO_REGISTER; i < max; i++) - if (REG_N_REFS (i)) - { - enum reg_class prefclass, altclass; - - fprintf (file, "\nRegister %d used %d times across %d insns", - i, REG_N_REFS (i), REG_LIVE_LENGTH (i)); - if (REG_BASIC_BLOCK (i) >= 0) - fprintf (file, " in block %d", REG_BASIC_BLOCK (i)); - if (REG_N_SETS (i)) - fprintf (file, "; set %d time%s", REG_N_SETS (i), - (REG_N_SETS (i) == 1) ? "" : "s"); - if (regno_reg_rtx[i] != NULL && REG_USERVAR_P (regno_reg_rtx[i])) - fprintf (file, "; user var"); - if (REG_N_DEATHS (i) != 1) - fprintf (file, "; dies in %d places", REG_N_DEATHS (i)); - if (REG_N_CALLS_CROSSED (i) == 1) - fprintf (file, "; crosses 1 call"); - else if (REG_N_CALLS_CROSSED (i)) - fprintf (file, "; crosses %d calls", REG_N_CALLS_CROSSED (i)); - if (regno_reg_rtx[i] != NULL - && PSEUDO_REGNO_BYTES (i) != UNITS_PER_WORD) - fprintf (file, "; %d bytes", PSEUDO_REGNO_BYTES (i)); - - prefclass = reg_preferred_class (i); - altclass = reg_alternate_class (i); - if (prefclass != GENERAL_REGS || altclass != ALL_REGS) - { - if (altclass == ALL_REGS || prefclass == ALL_REGS) - fprintf (file, "; pref %s", reg_class_names[(int) prefclass]); - else if (altclass == NO_REGS) - fprintf (file, "; %s or none", reg_class_names[(int) prefclass]); - else - fprintf (file, "; pref %s, else %s", - reg_class_names[(int) prefclass], - reg_class_names[(int) altclass]); - } - - if (regno_reg_rtx[i] != NULL && REG_POINTER (regno_reg_rtx[i])) - fprintf (file, "; pointer"); - fprintf (file, ".\n"); - } - } + if (reg_info_p_size && (flags & TDF_DETAILS) != 0) + dump_reg_info (file); fprintf (file, "\n%d basic blocks, %d edges.\n", n_basic_blocks, n_edges); FOR_EACH_BB (bb) diff --git a/gcc/cfganal.c b/gcc/cfganal.c index 467c399..18cef5e 100644 --- a/gcc/cfganal.c +++ b/gcc/cfganal.c @@ -1,6 +1,6 @@ /* Control flow graph analysis code for GNU compiler. Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -645,16 +645,20 @@ connect_infinite_loops_to_exit (void) return; } -/* Compute reverse top sort order. - This is computing a post order numbering of the graph. */ +/* Compute reverse top sort order. This is computing a post order + numbering of the graph. If INCLUDE_ENTRY_EXIT is true, then then + ENTRY_BLOCK and EXIT_BLOCK are included. If DELETE_UNREACHABLE is + true, unreachable blocks are deleted. */ int -post_order_compute (int *post_order, bool include_entry_exit) +post_order_compute (int *post_order, bool include_entry_exit, + bool delete_unreachable) { edge_iterator *stack; int sp; int post_order_num = 0; sbitmap visited; + int count; if (include_entry_exit) post_order[post_order_num++] = EXIT_BLOCK; @@ -699,7 +703,7 @@ post_order_compute (int *post_order, bool include_entry_exit) else { if (ei_one_before_end_p (ei) && src != ENTRY_BLOCK_PTR) - post_order[post_order_num++] = src->index; + post_order[post_order_num++] = src->index; if (!ei_one_before_end_p (ei)) ei_next (&stack[sp - 1]); @@ -709,7 +713,220 @@ post_order_compute (int *post_order, bool include_entry_exit) } if (include_entry_exit) - post_order[post_order_num++] = ENTRY_BLOCK; + { + post_order[post_order_num++] = ENTRY_BLOCK; + count = post_order_num; + } + else + count = post_order_num + 2; + + /* Delete the unreachable blocks if some were found and we are + supposed to do it. */ + if (delete_unreachable && (count != n_basic_blocks)) + { + basic_block b; + basic_block next_bb; + for (b = ENTRY_BLOCK_PTR->next_bb; b != EXIT_BLOCK_PTR; b = next_bb) + { + next_bb = b->next_bb; + + if (!(TEST_BIT (visited, b->index))) + delete_basic_block (b); + } + + tidy_fallthru_edges (); + } + + free (stack); + sbitmap_free (visited); + return post_order_num; +} + + +/* Helper routine for inverted_post_order_compute. + BB has to belong to a region of CFG + unreachable by inverted traversal from the exit. + i.e. there's no control flow path from ENTRY to EXIT + that contains this BB. + This can happen in two cases - if there's an infinite loop + or if there's a block that has no successor + (call to a function with no return). + Some RTL passes deal with this condition by + calling connect_infinite_loops_to_exit () and/or + add_noreturn_fake_exit_edges (). + However, those methods involve modifying the CFG itself + which may not be desirable. + Hence, we deal with the infinite loop/no return cases + by identifying a unique basic block that can reach all blocks + in such a region by inverted traversal. + This function returns a basic block that guarantees + that all blocks in the region are reachable + by starting an inverted traversal from the returned block. */ + +static basic_block +dfs_find_deadend (basic_block bb) +{ + sbitmap visited = sbitmap_alloc (last_basic_block); + sbitmap_zero (visited); + + for (;;) + { + SET_BIT (visited, bb->index); + if (EDGE_COUNT (bb->succs) == 0 + || TEST_BIT (visited, EDGE_SUCC (bb, 0)->dest->index)) + { + sbitmap_free (visited); + return bb; + } + + bb = EDGE_SUCC (bb, 0)->dest; + } + + gcc_unreachable (); +} + + +/* Compute the reverse top sort order of the inverted CFG + i.e. starting from the exit block and following the edges backward + (from successors to predecessors). + This ordering can be used for forward dataflow problems among others. + + This function assumes that all blocks in the CFG are reachable + from the ENTRY (but not necessarily from EXIT). + + If there's an infinite loop, + a simple inverted traversal starting from the blocks + with no successors can't visit all blocks. + To solve this problem, we first do inverted traversal + starting from the blocks with no successor. + And if there's any block left that's not visited by the regular + inverted traversal from EXIT, + those blocks are in such problematic region. + Among those, we find one block that has + any visited predecessor (which is an entry into such a region), + and start looking for a "dead end" from that block + and do another inverted traversal from that block. */ + +int +inverted_post_order_compute (int *post_order) +{ + basic_block bb; + edge_iterator *stack; + int sp; + int post_order_num = 0; + sbitmap visited; + + /* Allocate stack for back-tracking up CFG. */ + stack = XNEWVEC (edge_iterator, n_basic_blocks + 1); + sp = 0; + + /* Allocate bitmap to track nodes that have been visited. */ + visited = sbitmap_alloc (last_basic_block); + + /* None of the nodes in the CFG have been visited yet. */ + sbitmap_zero (visited); + + /* Put all blocks that have no successor into the initial work list. */ + FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb) + if (EDGE_COUNT (bb->succs) == 0) + { + /* Push the initial edge on to the stack. */ + if (EDGE_COUNT (bb->preds) > 0) + { + stack[sp++] = ei_start (bb->preds); + SET_BIT (visited, bb->index); + } + } + + do + { + bool has_unvisited_bb = false; + + /* The inverted traversal loop. */ + while (sp) + { + edge_iterator ei; + basic_block pred; + + /* Look at the edge on the top of the stack. */ + ei = stack[sp - 1]; + bb = ei_edge (ei)->dest; + pred = ei_edge (ei)->src; + + /* Check if the predecessor has been visited yet. */ + if (! TEST_BIT (visited, pred->index)) + { + /* Mark that we have visited the destination. */ + SET_BIT (visited, pred->index); + + if (EDGE_COUNT (pred->preds) > 0) + /* Since the predecessor node has been visited for the first + time, check its predecessors. */ + stack[sp++] = ei_start (pred->preds); + else + post_order[post_order_num++] = pred->index; + } + else + { + if (bb != EXIT_BLOCK_PTR && ei_one_before_end_p (ei)) + post_order[post_order_num++] = bb->index; + + if (!ei_one_before_end_p (ei)) + ei_next (&stack[sp - 1]); + else + sp--; + } + } + + /* Detect any infinite loop and activate the kludge. + Note that this doesn't check EXIT_BLOCK itself + since EXIT_BLOCK is always added after the outer do-while loop. */ + FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR, next_bb) + if (!TEST_BIT (visited, bb->index)) + { + has_unvisited_bb = true; + + if (EDGE_COUNT (bb->preds) > 0) + { + edge_iterator ei; + edge e; + basic_block visited_pred = NULL; + + /* Find an already visited predecessor. */ + FOR_EACH_EDGE (e, ei, bb->preds) + { + if (TEST_BIT (visited, e->src->index)) + visited_pred = e->src; + } + + if (visited_pred) + { + basic_block be = dfs_find_deadend (bb); + gcc_assert (be != NULL); + SET_BIT (visited, be->index); + stack[sp++] = ei_start (be->preds); + break; + } + } + } + + if (has_unvisited_bb && sp == 0) + { + /* No blocks are reachable from EXIT at all. + Find a dead-end from the ENTRY, and restart the iteration. */ + basic_block be = dfs_find_deadend (ENTRY_BLOCK_PTR); + gcc_assert (be != NULL); + SET_BIT (visited, be->index); + stack[sp++] = ei_start (be->preds); + } + + /* The only case the below while fires is + when there's an infinite loop. */ + } + while (sp); + + /* EXIT_BLOCK is always included. */ + post_order[post_order_num++] = EXIT_BLOCK; free (stack); sbitmap_free (visited); @@ -1076,4 +1293,3 @@ compute_dominance_frontiers (bitmap *frontiers) timevar_pop (TV_DOM_FRONTIERS); } - diff --git a/gcc/cfgcleanup.c b/gcc/cfgcleanup.c index dafdcd5..b34a2db 100644 --- a/gcc/cfgcleanup.c +++ b/gcc/cfgcleanup.c @@ -54,6 +54,8 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "tree-pass.h" #include "cfgloop.h" #include "expr.h" +#include "df.h" +#include "dce.h" #define FORWARDER_BLOCK_P(BB) ((BB)->flags & BB_FORWARDER_BLOCK) @@ -70,7 +72,7 @@ static void merge_blocks_move_successor_nojumps (basic_block, basic_block); static bool try_optimize_cfg (int); static bool try_simplify_condjump (basic_block); static bool try_forward_edges (int, basic_block); -static edge thread_jump (int, edge, basic_block); +static edge thread_jump (edge, basic_block); static bool mark_effect (rtx, bitmap); static void notice_new_block (basic_block); static void update_forwarder_flag (basic_block); @@ -260,7 +262,7 @@ mentions_nonequal_regs (rtx *x, void *data) if exist, NULL otherwise. */ static edge -thread_jump (int mode, edge e, basic_block b) +thread_jump (edge e, basic_block b) { rtx set1, set2, cond1, cond2, insn; enum rtx_code code1, code2, reversed_code2; @@ -380,11 +382,6 @@ thread_jump (int mode, edge e, basic_block b) if (for_each_rtx (&cond2, mentions_nonequal_regs, nonequal)) goto failed_exit; - /* In case liveness information is available, we need to prove equivalence - only of the live values. */ - if (mode & CLEANUP_UPDATE_LIFE) - AND_REG_SET (nonequal, b->il.rtl->global_live_at_end); - EXECUTE_IF_SET_IN_REG_SET (nonequal, 0, i, rsi) goto failed_exit; @@ -431,7 +428,7 @@ try_forward_edges (int mode, basic_block b) int counter; bool threaded = false; int nthreaded_edges = 0; - bool may_thread = first_pass | (b->flags & BB_DIRTY); + bool may_thread = first_pass | df_get_bb_dirty (b); /* Skip complex edges because we don't know how to update them. @@ -465,7 +462,7 @@ try_forward_edges (int mode, basic_block b) { basic_block new_target = NULL; bool new_target_threaded = false; - may_thread |= target->flags & BB_DIRTY; + may_thread |= df_get_bb_dirty (target); if (FORWARDER_BLOCK_P (target) && !(single_succ_edge (target)->flags & EDGE_CROSSING) @@ -481,7 +478,7 @@ try_forward_edges (int mode, basic_block b) of probabilities. */ else if ((mode & CLEANUP_THREADING) && may_thread) { - edge t = thread_jump (mode, e, target); + edge t = thread_jump (e, target); if (t) { if (!threaded_edges) @@ -644,7 +641,7 @@ merge_blocks_move_predecessor_nojumps (basic_block a, basic_block b) /* Scramble the insn chain. */ if (BB_END (a) != PREV_INSN (BB_HEAD (b))) reorder_insns_nobb (BB_HEAD (a), BB_END (a), PREV_INSN (BB_HEAD (b))); - a->flags |= BB_DIRTY; + df_set_bb_dirty (a); if (dump_file) fprintf (dump_file, "Moved block %d before %d and merged.\n", @@ -1707,7 +1704,7 @@ try_crossjump_to_edge (int mode, edge e1, edge e2) redirect_to->count += src1->count; redirect_to->frequency += src1->frequency; /* We may have some registers visible through the block. */ - redirect_to->flags |= BB_DIRTY; + df_set_bb_dirty (redirect_to); /* Recompute the frequencies and counts of outgoing edges. */ FOR_EACH_EDGE (s, ei, redirect_to->succs) @@ -1856,8 +1853,8 @@ try_crossjump_bb (int mode, basic_block bb) /* If nothing changed since the last attempt, there is nothing we can do. */ if (!first_pass - && (!(e->src->flags & BB_DIRTY) - && !(fallthru->src->flags & BB_DIRTY))) + && (!(df_get_bb_dirty (e->src)) + && !(df_get_bb_dirty (fallthru->src)))) continue; if (try_crossjump_to_edge (mode, e, fallthru)) @@ -1906,8 +1903,8 @@ try_crossjump_bb (int mode, basic_block bb) /* If nothing changed since the last attempt, there is nothing we can do. */ if (!first_pass - && (!(e->src->flags & BB_DIRTY) - && !(e2->src->flags & BB_DIRTY))) + && (!(df_get_bb_dirty (e->src)) + && !(df_get_bb_dirty (e2->src)))) continue; if (try_crossjump_to_edge (mode, e, e2)) @@ -1937,7 +1934,7 @@ try_optimize_cfg (int mode) if (mode & CLEANUP_CROSSJUMP) add_noreturn_fake_exit_edges (); - if (mode & (CLEANUP_UPDATE_LIFE | CLEANUP_CROSSJUMP | CLEANUP_THREADING)) + if (mode & (CLEANUP_CROSSJUMP | CLEANUP_THREADING)) clear_bb_flags (); FOR_EACH_BB (bb) @@ -2000,7 +1997,7 @@ try_optimize_cfg (int mode) rtx label = BB_HEAD (b); delete_insn_chain (label, label, false); - /* In the case label is undeletable, move it after the + /* If the case label is undeletable, move it after the BASIC_BLOCK note. */ if (NOTE_KIND (BB_HEAD (b)) == NOTE_INSN_DELETED_LABEL) { @@ -2166,6 +2163,47 @@ delete_unreachable_blocks (void) tidy_fallthru_edges (); return changed; } + +/* Delete any jump tables never referenced. We can't delete them at the + time of removing tablejump insn as they are referenced by the preceding + insns computing the destination, so we delay deleting and garbagecollect + them once life information is computed. */ +void +delete_dead_jumptables (void) +{ + basic_block bb; + + /* A dead jump table does not belong to any basic block. Scan insns + between two adjacent basic blocks. */ + FOR_EACH_BB (bb) + { + rtx insn, next; + + for (insn = NEXT_INSN (BB_END (bb)); + insn && !NOTE_INSN_BASIC_BLOCK_P (insn); + insn = next) + { + next = NEXT_INSN (insn); + if (LABEL_P (insn) + && LABEL_NUSES (insn) == LABEL_PRESERVE_P (insn) + && JUMP_P (next) + && (GET_CODE (PATTERN (next)) == ADDR_VEC + || GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC)) + { + rtx label = insn, jump = next; + + if (dump_file) + fprintf (dump_file, "Dead jumptable %i removed\n", + INSN_UID (insn)); + + next = NEXT_INSN (next); + delete_insn (jump); + delete_insn (label); + } + } + } +} + /* Tidy the CFG by deleting unreachable code and whatnot. */ @@ -2186,7 +2224,7 @@ cleanup_cfg (int mode) changed = true; /* We've possibly created trivially dead code. Cleanup it right now to introduce more opportunities for try_optimize_cfg. */ - if (!(mode & (CLEANUP_NO_INSN_DEL | CLEANUP_UPDATE_LIFE)) + if (!(mode & (CLEANUP_NO_INSN_DEL)) && !reload_completed) delete_trivially_dead_insns (get_insns (), max_reg_num ()); } @@ -2196,39 +2234,26 @@ cleanup_cfg (int mode) while (try_optimize_cfg (mode)) { delete_unreachable_blocks (), changed = true; - if (mode & CLEANUP_UPDATE_LIFE) - { - /* Cleaning up CFG introduces more opportunities for dead code - removal that in turn may introduce more opportunities for - cleaning up the CFG. */ - if (!update_life_info_in_dirty_blocks (UPDATE_LIFE_GLOBAL_RM_NOTES, - PROP_DEATH_NOTES - | PROP_SCAN_DEAD_CODE - | PROP_KILL_DEAD_CODE - | ((mode & CLEANUP_LOG_LINKS) - ? PROP_LOG_LINKS : 0))) - break; - } - else if (!(mode & CLEANUP_NO_INSN_DEL) - && (mode & CLEANUP_EXPENSIVE) - && !reload_completed) + if (!(mode & CLEANUP_NO_INSN_DEL) + && (mode & CLEANUP_EXPENSIVE) + && !reload_completed) { if (!delete_trivially_dead_insns (get_insns (), max_reg_num ())) break; } else break; - - /* Don't call delete_dead_jumptables in cfglayout mode, because - that function assumes that jump tables are in the insns stream. - But we also don't _have_ to delete dead jumptables in cfglayout - mode because we shouldn't even be looking at things that are - not in a basic block. Dead jumptables are cleaned up when - going out of cfglayout mode. */ - if (!(mode & CLEANUP_CFGLAYOUT)) - delete_dead_jumptables (); } + /* Don't call delete_dead_jumptables in cfglayout mode, because + that function assumes that jump tables are in the insns stream. + But we also don't _have_ to delete dead jumptables in cfglayout + mode because we shouldn't even be looking at things that are + not in a basic block. Dead jumptables are cleaned up when + going out of cfglayout mode. */ + if (!(mode & CLEANUP_CFGLAYOUT)) + delete_dead_jumptables (); + timevar_pop (TV_CLEANUP_CFG); return changed; @@ -2267,7 +2292,6 @@ static unsigned int rest_of_handle_jump2 (void) { delete_trivially_dead_insns (get_insns (), max_reg_num ()); - reg_scan (get_insns (), max_reg_num ()); if (dump_file) dump_flow_info (dump_file, dump_flags); cleanup_cfg ((optimize ? CLEANUP_EXPENSIVE : 0) diff --git a/gcc/cfghooks.h b/gcc/cfghooks.h index bd7d1b3..46a57fd 100644 --- a/gcc/cfghooks.h +++ b/gcc/cfghooks.h @@ -1,5 +1,5 @@ /* Hooks for cfg representation specific functions. - Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Sebastian Pop <s.pop@laposte.net> This file is part of GCC. @@ -57,7 +57,7 @@ struct cfg_hooks /* Creates a new basic block just after basic block B by splitting everything after specified instruction I. */ basic_block (*split_block) (basic_block b, void * i); - + /* Move block B immediately after block A. */ bool (*move_block_after) (basic_block b, basic_block a); diff --git a/gcc/cfglayout.c b/gcc/cfglayout.c index 3c3654d..be4b087 100644 --- a/gcc/cfglayout.c +++ b/gcc/cfglayout.c @@ -1,5 +1,6 @@ /* Basic block reordering routines for the GNU compiler. - Copyright (C) 2000, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2000, 2001, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. This file is part of GCC. @@ -37,6 +38,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "alloc-pool.h" #include "flags.h" #include "tree-pass.h" +#include "df.h" #include "vecprim.h" /* Holds the interesting trailing notes for the function. */ @@ -883,7 +885,7 @@ fixup_reorder_chain (void) FOR_EACH_EDGE (e, ei, bb->succs) if (e->flags & EDGE_FALLTHRU) break; - + if (e && !can_fallthru (e->src, e->dest)) force_nonfallthru (e); } @@ -1108,35 +1110,34 @@ cfg_layout_duplicate_bb (basic_block bb) new_bb->il.rtl->footer = unlink_insn_chain (insn, get_last_insn ()); } - if (bb->il.rtl->global_live_at_start) - { - new_bb->il.rtl->global_live_at_start = ALLOC_REG_SET (®_obstack); - new_bb->il.rtl->global_live_at_end = ALLOC_REG_SET (®_obstack); - COPY_REG_SET (new_bb->il.rtl->global_live_at_start, - bb->il.rtl->global_live_at_start); - COPY_REG_SET (new_bb->il.rtl->global_live_at_end, - bb->il.rtl->global_live_at_end); - } - return new_bb; } + /* Main entry point to this module - initialize the datastructures for CFG layout changes. It keeps LOOPS up-to-date if not null. - FLAGS is a set of additional flags to pass to cleanup_cfg(). It should - include CLEANUP_UPDATE_LIFE if liveness information must be kept up - to date. */ + FLAGS is a set of additional flags to pass to cleanup_cfg(). */ void cfg_layout_initialize (unsigned int flags) { + rtx x; + basic_block bb; + initialize_original_copy_tables (); cfg_layout_rtl_register_cfg_hooks (); record_effective_endpoints (); + /* Make sure that the targets of non local gotos are marked. */ + for (x = nonlocal_goto_handler_labels; x; x = XEXP (x, 1)) + { + bb = BLOCK_FOR_INSN (XEXP (x, 0)); + bb->flags |= BB_NON_LOCAL_GOTO_TARGET; + } + cleanup_cfg (CLEANUP_CFGLAYOUT | flags); } diff --git a/gcc/cfgloop.c b/gcc/cfgloop.c index 8e1edbd..6363798 100644 --- a/gcc/cfgloop.c +++ b/gcc/cfgloop.c @@ -1,5 +1,6 @@ /* Natural loop discovery code for GNU compiler. - Copyright (C) 2000, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2000, 2001, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. This file is part of GCC. @@ -284,9 +285,6 @@ establish_preds (struct loop *loop, struct loop *father) unsigned depth = loop_depth (father) + 1; unsigned i; - /* Remember the current loop depth if it is the largest seen so far. */ - cfun->max_loop_depth = MAX (cfun->max_loop_depth, (int) depth); - VEC_truncate (loop_p, loop->superloops, 0); VEC_reserve (loop_p, gc, loop->superloops, depth); for (i = 0; VEC_iterate (loop_p, father->superloops, i, ploop); i++) @@ -364,10 +362,6 @@ flow_loops_find (struct loops *loops) memset (loops, 0, sizeof *loops); - /* We are going to recount the maximum loop depth, - so throw away the last count. */ - cfun->max_loop_depth = 0; - /* Taking care of this degenerate case makes the rest of this code simpler. */ if (n_basic_blocks == NUM_FIXED_BLOCKS) diff --git a/gcc/cfgloop.h b/gcc/cfgloop.h index 7b042eb..60b82f0 100644 --- a/gcc/cfgloop.h +++ b/gcc/cfgloop.h @@ -1,6 +1,6 @@ /* Natural loop functions - Copyright (C) 1987, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 - Free Software Foundation, Inc. + Copyright (C) 1987, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, + 2005, 2006, 2007Free Software Foundation, Inc. This file is part of GCC. @@ -386,7 +386,6 @@ extern rtx get_iv_value (struct rtx_iv *, rtx); extern bool biv_p (rtx, rtx); extern void find_simple_exit (struct loop *, struct niter_desc *); extern void iv_analysis_done (void); -extern struct df *iv_current_loop_df (void); extern struct niter_desc *get_simple_loop_desc (struct loop *loop); extern void free_simple_loop_desc (struct loop *loop); diff --git a/gcc/cfgrtl.c b/gcc/cfgrtl.c index 5655eef..d7921e3 100644 --- a/gcc/cfgrtl.c +++ b/gcc/cfgrtl.c @@ -1,6 +1,7 @@ /* Control flow graph manipulation code for GNU compiler. Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. This file is part of GCC. @@ -60,6 +61,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "cfgloop.h" #include "ggc.h" #include "tree-pass.h" +#include "df.h" static int can_delete_note_p (rtx); static int can_delete_label_p (rtx); @@ -307,6 +309,7 @@ create_basic_block_structure (rtx head, rtx end, rtx bb_note, basic_block after) bb->flags = BB_NEW | BB_RTL; link_block (bb, after); SET_BASIC_BLOCK (bb->index, bb); + df_bb_refs_record (bb->index, false); update_bb_for_insn (bb); BB_SET_PARTITION (bb, BB_UNPARTITIONED); @@ -376,13 +379,10 @@ rtl_delete_block (basic_block b) BB_HEAD (b) = NULL; delete_insn_chain (insn, end, true); - if (b->il.rtl->global_live_at_start) - { - FREE_REG_SET (b->il.rtl->global_live_at_start); - FREE_REG_SET (b->il.rtl->global_live_at_end); - b->il.rtl->global_live_at_start = NULL; - b->il.rtl->global_live_at_end = NULL; - } + + if (dump_file) + fprintf (dump_file, "deleting block %d\n", b->index); + df_bb_delete (b->index); } /* Records the basic block struct in BLOCK_FOR_INSN for every insn. */ @@ -466,12 +466,35 @@ update_bb_for_insn (basic_block bb) for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn)) { if (!BARRIER_P (insn)) - set_block_for_insn (insn, bb); + { + set_block_for_insn (insn, bb); + df_insn_change_bb (insn); + } if (insn == BB_END (bb)) break; } } +/* Return the INSN immediately following the NOTE_INSN_BASIC_BLOCK + note associated with the BLOCK. */ + +static rtx +first_insn_after_basic_block_note (basic_block block) +{ + rtx insn; + + /* Get the first instruction in the block. */ + insn = BB_HEAD (block); + + if (insn == NULL_RTX) + return NULL_RTX; + if (LABEL_P (insn)) + insn = NEXT_INSN (insn); + gcc_assert (NOTE_INSN_BASIC_BLOCK_P (insn)); + + return NEXT_INSN (insn); +} + /* Creates a new basic block just after basic block B by splitting everything after specified instruction I. */ @@ -510,32 +533,8 @@ rtl_split_block (basic_block bb, void *insnp) FOR_EACH_EDGE (e, ei, new_bb->succs) e->src = new_bb; - if (bb->il.rtl->global_live_at_start) - { - new_bb->il.rtl->global_live_at_start = ALLOC_REG_SET (®_obstack); - new_bb->il.rtl->global_live_at_end = ALLOC_REG_SET (®_obstack); - COPY_REG_SET (new_bb->il.rtl->global_live_at_end, bb->il.rtl->global_live_at_end); - - /* We now have to calculate which registers are live at the end - of the split basic block and at the start of the new basic - block. Start with those registers that are known to be live - at the end of the original basic block and get - propagate_block to determine which registers are live. */ - COPY_REG_SET (new_bb->il.rtl->global_live_at_start, bb->il.rtl->global_live_at_end); - propagate_block (new_bb, new_bb->il.rtl->global_live_at_start, NULL, NULL, 0); - COPY_REG_SET (bb->il.rtl->global_live_at_end, - new_bb->il.rtl->global_live_at_start); -#ifdef HAVE_conditional_execution - /* In the presence of conditional execution we are not able to update - liveness precisely. */ - if (reload_completed) - { - bb->flags |= BB_DIRTY; - new_bb->flags |= BB_DIRTY; - } -#endif - } - + /* The new block starts off being dirty. */ + df_set_bb_dirty (bb); return new_bb; } @@ -549,6 +548,9 @@ rtl_merge_blocks (basic_block a, basic_block b) rtx del_first = NULL_RTX, del_last = NULL_RTX; int b_empty = 0; + if (dump_file) + fprintf (dump_file, "merging block %d into block %d\n", b->index, a->index); + /* If there was a CODE_LABEL beginning B, delete it. */ if (LABEL_P (b_head)) { @@ -621,17 +623,22 @@ rtl_merge_blocks (basic_block a, basic_block b) rtx x; for (x = a_end; x != b_end; x = NEXT_INSN (x)) - set_block_for_insn (x, a); + { + set_block_for_insn (x, a); + df_insn_change_bb (x); + } set_block_for_insn (b_end, a); + df_insn_change_bb (b_end); a_end = b_end; } + df_bb_delete (b->index); BB_END (a) = a_end; - a->il.rtl->global_live_at_end = b->il.rtl->global_live_at_end; } + /* Return true when block A and B can be merged. */ static bool rtl_can_merge_blocks (basic_block a,basic_block b) @@ -830,7 +837,10 @@ try_redirect_by_replacing_jump (edge e, basic_block target, bool in_cfglayout) for (tmp = NEXT_INSN (BB_END (src)); tmp != barrier; tmp = NEXT_INSN (tmp)) - set_block_for_insn (tmp, src); + { + set_block_for_insn (tmp, src); + df_insn_change_bb (tmp); + } NEXT_INSN (PREV_INSN (new_insn)) = NEXT_INSN (new_insn); PREV_INSN (NEXT_INSN (new_insn)) = PREV_INSN (new_insn); @@ -860,7 +870,6 @@ try_redirect_by_replacing_jump (edge e, basic_block target, bool in_cfglayout) if (e->dest != target) redirect_edge_succ (e, target); - return e; } @@ -944,6 +953,7 @@ redirect_branch_edge (edge e, basic_block target) if (e->dest != target) e = redirect_edge_succ_nodup (e, target); + return e; } @@ -972,7 +982,7 @@ rtl_redirect_edge_and_branch (edge e, basic_block target) if ((ret = try_redirect_by_replacing_jump (e, target, false)) != NULL) { - src->flags |= BB_DIRTY; + df_set_bb_dirty (src); return ret; } @@ -980,7 +990,7 @@ rtl_redirect_edge_and_branch (edge e, basic_block target) if (!ret) return NULL; - src->flags |= BB_DIRTY; + df_set_bb_dirty (src); return ret; } @@ -1088,16 +1098,6 @@ force_nonfallthru_and_redirect (edge e, basic_block target) jump_block->frequency = EDGE_FREQUENCY (e); jump_block->loop_depth = target->loop_depth; - if (target->il.rtl->global_live_at_start) - { - jump_block->il.rtl->global_live_at_start = ALLOC_REG_SET (®_obstack); - jump_block->il.rtl->global_live_at_end = ALLOC_REG_SET (®_obstack); - COPY_REG_SET (jump_block->il.rtl->global_live_at_start, - target->il.rtl->global_live_at_start); - COPY_REG_SET (jump_block->il.rtl->global_live_at_end, - target->il.rtl->global_live_at_start); - } - /* Make sure new block ends up in correct hot/cold section. */ BB_COPY_PARTITION (jump_block, e->src); @@ -1149,6 +1149,7 @@ force_nonfallthru_and_redirect (edge e, basic_block target) if (abnormal_edge_flags) make_edge (src, target, abnormal_edge_flags); + df_mark_solutions_dirty (); return new_bb; } @@ -1175,7 +1176,7 @@ rtl_redirect_edge_and_branch_force (edge e, basic_block target) /* In case the edge redirection failed, try to force it to be non-fallthru and redirect newly created simplejump. */ - e->src->flags |= BB_DIRTY; + df_set_bb_dirty (e->src); return force_nonfallthru_and_redirect (e, target); } @@ -1289,17 +1290,6 @@ rtl_split_edge (edge edge_in) BB_COPY_PARTITION (bb, edge_in->dest); } - /* ??? This info is likely going to be out of date very soon. */ - if (edge_in->dest->il.rtl->global_live_at_start) - { - bb->il.rtl->global_live_at_start = ALLOC_REG_SET (®_obstack); - bb->il.rtl->global_live_at_end = ALLOC_REG_SET (®_obstack); - COPY_REG_SET (bb->il.rtl->global_live_at_start, - edge_in->dest->il.rtl->global_live_at_start); - COPY_REG_SET (bb->il.rtl->global_live_at_end, - edge_in->dest->il.rtl->global_live_at_start); - } - make_single_succ_edge (bb, edge_in->dest, EDGE_FALLTHRU); /* For non-fallthru edges, we must adjust the predecessor's @@ -1433,11 +1423,11 @@ commit_one_edge_insertion (edge e) if (before) { - emit_insn_before_noloc (insns, before); + emit_insn_before_noloc (insns, before, bb); last = prev_nonnote_insn (before); } else - last = emit_insn_after_noloc (insns, after); + last = emit_insn_after_noloc (insns, after, bb); if (returnjump_p (last)) { @@ -1515,6 +1505,7 @@ commit_edge_insertions (void) sbitmap_free (blocks); } + /* Print out RTL-specific basic block information (live information at start and end). */ @@ -1528,18 +1519,23 @@ rtl_dump_bb (basic_block bb, FILE *outf, int indent) s_indent = (char *) alloca ((size_t) indent + 1); memset (s_indent, ' ', (size_t) indent); s_indent[indent] = '\0'; - - fprintf (outf, ";;%s Registers live at start: ", s_indent); - dump_regset (bb->il.rtl->global_live_at_start, outf); - putc ('\n', outf); + + if (df) + { + df_dump_top (bb, outf); + putc ('\n', outf); + } for (insn = BB_HEAD (bb), last = NEXT_INSN (BB_END (bb)); insn != last; insn = NEXT_INSN (insn)) print_rtl_single (outf, insn); - fprintf (outf, ";;%s Registers live at end: ", s_indent); - dump_regset (bb->il.rtl->global_live_at_end, outf); - putc ('\n', outf); + if (df) + { + df_dump_bottom (bb, outf); + putc ('\n', outf); + } + } /* Like print_rtl, but also print out live information for the start of each @@ -1549,7 +1545,6 @@ void print_rtl_with_bb (FILE *outf, rtx rtx_first) { rtx tmp_rtx; - if (rtx_first == 0) fprintf (outf, "(nil)\n"); else @@ -1562,6 +1557,9 @@ print_rtl_with_bb (FILE *outf, rtx rtx_first) basic_block bb; + if (df) + df_dump_start (outf); + FOR_EACH_BB_REVERSE (bb) { rtx x; @@ -1584,15 +1582,21 @@ print_rtl_with_bb (FILE *outf, rtx rtx_first) for (tmp_rtx = rtx_first; NULL != tmp_rtx; tmp_rtx = NEXT_INSN (tmp_rtx)) { int did_output; - edge_iterator ei; - edge e; - if ((bb = start[INSN_UID (tmp_rtx)]) != NULL) { - fprintf (outf, ";; Start of basic block %d, registers live:", - bb->index); - dump_regset (bb->il.rtl->global_live_at_start, outf); - putc ('\n', outf); + edge e; + edge_iterator ei; + + fprintf (outf, ";; Start of basic block ("); + FOR_EACH_EDGE (e, ei, bb->preds) + fprintf (outf, " %d", e->src->index); + fprintf (outf, ") -> %d\n", bb->index); + + if (df) + { + df_dump_top (bb, outf); + putc ('\n', outf); + } FOR_EACH_EDGE (e, ei, bb->preds) { fputs (";; Pred edge ", outf); @@ -1612,9 +1616,19 @@ print_rtl_with_bb (FILE *outf, rtx rtx_first) if ((bb = end[INSN_UID (tmp_rtx)]) != NULL) { - fprintf (outf, ";; End of basic block %d, registers live:", - bb->index); - dump_regset (bb->il.rtl->global_live_at_end, outf); + edge e; + edge_iterator ei; + + fprintf (outf, ";; End of basic block %d -> (", bb->index); + FOR_EACH_EDGE (e, ei, bb->succs) + fprintf (outf, " %d", e->dest->index); + fprintf (outf, ")\n"); + + if (df) + { + df_dump_bottom (bb, outf); + putc ('\n', outf); + } putc ('\n', outf); FOR_EACH_EDGE (e, ei, bb->succs) { @@ -1623,7 +1637,6 @@ print_rtl_with_bb (FILE *outf, rtx rtx_first) fputc ('\n', outf); } } - if (did_output) putc ('\n', outf); } @@ -2163,7 +2176,7 @@ purge_dead_edges (basic_block bb) } remove_edge (e); - bb->flags |= BB_DIRTY; + df_set_bb_dirty (bb); purged = true; } @@ -2231,7 +2244,7 @@ purge_dead_edges (basic_block bb) } /* We do not need this edge. */ - bb->flags |= BB_DIRTY; + df_set_bb_dirty (bb); purged = true; remove_edge (e); } @@ -2303,7 +2316,7 @@ purge_dead_edges (basic_block bb) { if (!(e->flags & (EDGE_FALLTHRU | EDGE_FAKE))) { - bb->flags |= BB_DIRTY; + df_set_bb_dirty (bb); remove_edge (e); purged = true; } @@ -2355,7 +2368,6 @@ cfg_layout_split_block (basic_block bb, void *insnp) return new_bb; } - /* Redirect Edge to DEST. */ static edge cfg_layout_redirect_edge_and_branch (edge e, basic_block dest) @@ -2372,7 +2384,7 @@ cfg_layout_redirect_edge_and_branch (edge e, basic_block dest) if (e->src != ENTRY_BLOCK_PTR && (ret = try_redirect_by_replacing_jump (e, dest, true))) { - src->flags |= BB_DIRTY; + df_set_bb_dirty (src); return ret; } @@ -2383,7 +2395,7 @@ cfg_layout_redirect_edge_and_branch (edge e, basic_block dest) fprintf (dump_file, "Redirecting entry edge from bb %i to %i\n", e->src->index, dest->index); - e->src->flags |= BB_DIRTY; + df_set_bb_dirty (e->src); redirect_edge_succ (e, dest); return e; } @@ -2409,7 +2421,7 @@ cfg_layout_redirect_edge_and_branch (edge e, basic_block dest) redirected = redirect_branch_edge (e, dest); gcc_assert (redirected); e->flags |= EDGE_FALLTHRU; - e->src->flags |= BB_DIRTY; + df_set_bb_dirty (e->src); return e; } /* In case we are redirecting fallthru edge to the branch edge @@ -2435,7 +2447,7 @@ cfg_layout_redirect_edge_and_branch (edge e, basic_block dest) /* We don't want simplejumps in the insn stream during cfglayout. */ gcc_assert (!simplejump_p (BB_END (src))); - src->flags |= BB_DIRTY; + df_set_bb_dirty (src); return ret; } @@ -2574,6 +2586,9 @@ cfg_layout_merge_blocks (basic_block a, basic_block b) gcc_assert (cfg_layout_can_merge_blocks_p (a, b)); #endif + if (dump_file) + fprintf (dump_file, "merging block %d into block %d\n", b->index, a->index); + /* If there was a CODE_LABEL beginning B, delete it. */ if (LABEL_P (BB_HEAD (b))) { @@ -2595,7 +2610,7 @@ cfg_layout_merge_blocks (basic_block a, basic_block b) { rtx first = BB_END (a), last; - last = emit_insn_after_noloc (b->il.rtl->header, BB_END (a)); + last = emit_insn_after_noloc (b->il.rtl->header, BB_END (a), a); delete_insn_chain (NEXT_INSN (first), last, false); b->il.rtl->header = NULL; } @@ -2605,7 +2620,7 @@ cfg_layout_merge_blocks (basic_block a, basic_block b) { rtx first = unlink_insn_chain (BB_HEAD (b), BB_END (b)); - emit_insn_after_noloc (first, BB_END (a)); + emit_insn_after_noloc (first, BB_END (a), a); /* Skip possible DELETED_LABEL insn. */ if (!NOTE_INSN_BASIC_BLOCK_P (first)) first = NEXT_INSN (first); @@ -2621,7 +2636,11 @@ cfg_layout_merge_blocks (basic_block a, basic_block b) for (insn = BB_HEAD (b); insn != NEXT_INSN (BB_END (b)); insn = NEXT_INSN (insn)) - set_block_for_insn (insn, a); + { + set_block_for_insn (insn, a); + df_insn_change_bb (insn); + } + insn = BB_HEAD (b); /* Skip possible DELETED_LABEL insn. */ if (!NOTE_INSN_BASIC_BLOCK_P (insn)) @@ -2632,6 +2651,8 @@ cfg_layout_merge_blocks (basic_block a, basic_block b) delete_insn (insn); } + df_bb_delete (b->index); + /* Possible tablejumps and barriers should appear after the block. */ if (b->il.rtl->footer) { @@ -2648,7 +2669,6 @@ cfg_layout_merge_blocks (basic_block a, basic_block b) } b->il.rtl->footer = NULL; } - a->il.rtl->global_live_at_end = b->il.rtl->global_live_at_end; if (dump_file) fprintf (dump_file, "Merged blocks %d and %d.\n", @@ -2665,18 +2685,6 @@ cfg_layout_split_edge (edge e) ? NEXT_INSN (BB_END (e->src)) : get_insns (), NULL_RTX, e->src); - /* ??? This info is likely going to be out of date very soon, but we must - create it to avoid getting an ICE later. */ - if (e->dest->il.rtl->global_live_at_start) - { - new_bb->il.rtl->global_live_at_start = ALLOC_REG_SET (®_obstack); - new_bb->il.rtl->global_live_at_end = ALLOC_REG_SET (®_obstack); - COPY_REG_SET (new_bb->il.rtl->global_live_at_start, - e->dest->il.rtl->global_live_at_start); - COPY_REG_SET (new_bb->il.rtl->global_live_at_end, - e->dest->il.rtl->global_live_at_start); - } - make_edge (new_bb, e->dest, EDGE_FALLTHRU); redirect_edge_and_branch_force (e, new_bb); @@ -2977,7 +2985,7 @@ insert_insn_end_bb_new (rtx pat, basic_block bb) #endif /* FIXME: What if something in cc0/jump uses value set in new insn? */ - new_insn = emit_insn_before_noloc (pat, insn); + new_insn = emit_insn_before_noloc (pat, insn, bb); } /* Likewise if the last insn is a call, as will happen in the presence @@ -3008,10 +3016,10 @@ insert_insn_end_bb_new (rtx pat, basic_block bb) || NOTE_INSN_BASIC_BLOCK_P (insn)) insn = NEXT_INSN (insn); - new_insn = emit_insn_before_noloc (pat, insn); + new_insn = emit_insn_before_noloc (pat, insn, bb); } else - new_insn = emit_insn_after_noloc (pat, insn); + new_insn = emit_insn_after_noloc (pat, insn, bb); return new_insn; } @@ -3089,7 +3097,7 @@ struct cfg_hooks rtl_cfg_hooks = { /* We do not want to declare these functions in a header file, since they should only be used through the cfghooks interface, and we do not want to move them here since it would require also moving quite a lot of related - code. */ + code. They are in cfglayout.c. */ extern bool cfg_layout_can_duplicate_bb_p (basic_block); extern basic_block cfg_layout_duplicate_bb (basic_block); diff --git a/gcc/combine-stack-adj.c b/gcc/combine-stack-adj.c index fb1480a..3fc26d0 100644 --- a/gcc/combine-stack-adj.c +++ b/gcc/combine-stack-adj.c @@ -1,6 +1,7 @@ /* Combine stack adjustments. - Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, + 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. This file is part of GCC. @@ -55,6 +56,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "function.h" #include "expr.h" #include "basic-block.h" +#include "df.h" #include "except.h" #include "toplev.h" #include "reload.h" @@ -454,9 +456,7 @@ gate_handle_stack_adjustments (void) static unsigned int rest_of_handle_stack_adjustments (void) { - life_analysis (PROP_POSTRELOAD); - cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_UPDATE_LIFE - | (flag_crossjumping ? CLEANUP_CROSSJUMP : 0)); + cleanup_cfg (flag_crossjumping ? CLEANUP_CROSSJUMP : 0); /* This is kind of a heuristic. We need to run combine_stack_adjustments even for machines with possibly nonzero RETURN_POPS_ARGS @@ -465,7 +465,11 @@ rest_of_handle_stack_adjustments (void) #ifndef PUSH_ROUNDING if (!ACCUMULATE_OUTGOING_ARGS) #endif - combine_stack_adjustments (); + { + df_note_add_problem (); + df_analyze (); + combine_stack_adjustments (); + } return 0; } @@ -482,6 +486,7 @@ struct tree_opt_pass pass_stack_adjustments = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func | TODO_ggc_collect, /* todo_flags_finish */ 0 /* letter */ diff --git a/gcc/combine.c b/gcc/combine.c index 268fa76..6077e78 100644 --- a/gcc/combine.c +++ b/gcc/combine.c @@ -1,6 +1,7 @@ /* Optimize by combining instructions for GNU compiler. Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. This file is part of GCC. @@ -49,8 +50,9 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA we install it, delete the earlier insns, and update the data flow information (LOG_LINKS and REG_NOTES) for what we did. - There are a few exceptions where the dataflow information created by - flow.c aren't completely updated: + There are a few exceptions where the dataflow information isn't + completely updated (however this is only a local issue since it is + regenerated before the next pass that uses it): - reg_live_length is not updated - reg_n_refs is not adjusted in the rare case when a register is @@ -102,6 +104,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "params.h" #include "timevar.h" #include "tree-pass.h" +#include "df.h" /* Number of attempts to combine instructions in this function. */ @@ -140,21 +143,6 @@ static rtx i2mod_old_rhs; static rtx i2mod_new_rhs; -/* Vector mapping INSN_UIDs to cuids. - The cuids are like uids but increase monotonically always. - Combine always uses cuids so that it can compare them. - But actually renumbering the uids, which we used to do, - proves to be a bad idea because it makes it hard to compare - the dumps produced by earlier passes with those from later passes. */ - -static int *uid_cuid; -static int max_uid_cuid; - -/* Get the cuid of an insn. */ - -#define INSN_CUID(INSN) \ -(INSN_UID (INSN) > max_uid_cuid ? insn_cuid (INSN) : uid_cuid[INSN_UID (INSN)]) - /* Maximum register number, which is the size of the tables below. */ static unsigned int combine_max_regno; @@ -270,15 +258,15 @@ struct reg_stat { static struct reg_stat *reg_stat; -/* Record the cuid of the last insn that invalidated memory +/* Record the luid of the last insn that invalidated memory (anything that writes memory, and subroutine calls, but not pushes). */ static int mem_last_set; -/* Record the cuid of the last CALL_INSN +/* Record the luid of the last CALL_INSN so we can tell whether a potential combination crosses any calls. */ -static int last_call_cuid; +static int last_call_luid; /* When `subst' is called, this is the insn that is being modified (by combining in a previous insn). The PATTERN of this insn @@ -288,14 +276,14 @@ static int last_call_cuid; static rtx subst_insn; -/* This is the lowest CUID that `subst' is currently dealing with. +/* This is the lowest LUID that `subst' is currently dealing with. get_last_value will not return a value if the register was set at or - after this CUID. If not for this mechanism, we could get confused if + after this LUID. If not for this mechanism, we could get confused if I2 or I1 in try_combine were an insn that used the old value of a register to obtain a new value. In that case, we might erroneously get the new value of the register when we wanted the old one. */ -static int subst_low_cuid; +static int subst_low_luid; /* This contains any hard registers that are used in newpat; reg_dead_at_p must consider all these registers to be always live. */ @@ -311,24 +299,32 @@ static rtx added_links_insn; /* Basic block in which we are performing combines. */ static basic_block this_basic_block; -/* A bitmap indicating which blocks had registers go dead at entry. - After combine, we'll need to re-do global life analysis with - those blocks as starting points. */ -static sbitmap refresh_blocks; +/* Length of the currently allocated uid_insn_cost array. */ + +static int max_uid_known; + /* The following array records the insn_rtx_cost for every insn in the instruction stream. */ static int *uid_insn_cost; -/* Length of the currently allocated uid_insn_cost array. */ +/* The following array records the LOG_LINKS for every insn in the + instruction stream as an INSN_LIST rtx. */ -static int last_insn_cost; +static rtx *uid_log_links; -/* Incremented for each label. */ +#define INSN_COST(INSN) (uid_insn_cost[INSN_UID (INSN)]) +#define LOG_LINKS(INSN) (uid_log_links[INSN_UID (INSN)]) + +/* Incremented for each basic block. */ static int label_tick; +/* Reset to label_tick for each label. */ + +static int label_tick_ebb_start; + /* Mode used to compute significance in reg_stat[].nonzero_bits. It is the largest integer mode that can fit in HOST_BITS_PER_WIDE_INT. */ @@ -384,7 +380,7 @@ static rtx reg_num_sign_bit_copies_for_combine (rtx, enum machine_mode, rtx, static void do_SUBST (rtx *, rtx); static void do_SUBST_INT (int *, int); static void init_reg_last (void); -static void setup_incoming_promotions (void); +static void setup_incoming_promotions (rtx); static void set_nonzero_bits_and_sign_copies (rtx, rtx, void *); static int cant_combine_insn_p (rtx); static int can_combine_p (rtx, rtx, rtx, rtx, rtx *, rtx *); @@ -443,7 +439,6 @@ static int reg_bitfield_target_p (rtx, rtx); static void distribute_notes (rtx, rtx, rtx, rtx, rtx, rtx); static void distribute_links (rtx); static void mark_used_regs_combine (rtx); -static int insn_cuid (rtx); static void record_promoted_value (rtx, rtx); static int unmentioned_reg_p_1 (rtx *, void *); static bool unmentioned_reg_p (rtx, rtx); @@ -473,6 +468,166 @@ static rtx gen_lowpart_or_truncate (enum machine_mode, rtx); static const struct rtl_hooks combine_rtl_hooks = RTL_HOOKS_INITIALIZER; +/* This is used by find_single_use to locate an rtx in LOC that + contains exactly one use of DEST, which is typically either a REG + or CC0. It returns a pointer to the innermost rtx expression + containing DEST. Appearances of DEST that are being used to + totally replace it are not counted. */ + +static rtx * +find_single_use_1 (rtx dest, rtx *loc) +{ + rtx x = *loc; + enum rtx_code code = GET_CODE (x); + rtx *result = NULL; + rtx *this_result; + int i; + const char *fmt; + + switch (code) + { + case CONST_INT: + case CONST: + case LABEL_REF: + case SYMBOL_REF: + case CONST_DOUBLE: + case CONST_VECTOR: + case CLOBBER: + return 0; + + case SET: + /* If the destination is anything other than CC0, PC, a REG or a SUBREG + of a REG that occupies all of the REG, the insn uses DEST if + it is mentioned in the destination or the source. Otherwise, we + need just check the source. */ + if (GET_CODE (SET_DEST (x)) != CC0 + && GET_CODE (SET_DEST (x)) != PC + && !REG_P (SET_DEST (x)) + && ! (GET_CODE (SET_DEST (x)) == SUBREG + && REG_P (SUBREG_REG (SET_DEST (x))) + && (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (x)))) + + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD) + == ((GET_MODE_SIZE (GET_MODE (SET_DEST (x))) + + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)))) + break; + + return find_single_use_1 (dest, &SET_SRC (x)); + + case MEM: + case SUBREG: + return find_single_use_1 (dest, &XEXP (x, 0)); + + default: + break; + } + + /* If it wasn't one of the common cases above, check each expression and + vector of this code. Look for a unique usage of DEST. */ + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + if (dest == XEXP (x, i) + || (REG_P (dest) && REG_P (XEXP (x, i)) + && REGNO (dest) == REGNO (XEXP (x, i)))) + this_result = loc; + else + this_result = find_single_use_1 (dest, &XEXP (x, i)); + + if (result == NULL) + result = this_result; + else if (this_result) + /* Duplicate usage. */ + return NULL; + } + else if (fmt[i] == 'E') + { + int j; + + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + { + if (XVECEXP (x, i, j) == dest + || (REG_P (dest) + && REG_P (XVECEXP (x, i, j)) + && REGNO (XVECEXP (x, i, j)) == REGNO (dest))) + this_result = loc; + else + this_result = find_single_use_1 (dest, &XVECEXP (x, i, j)); + + if (result == NULL) + result = this_result; + else if (this_result) + return NULL; + } + } + } + + return result; +} + + +/* See if DEST, produced in INSN, is used only a single time in the + sequel. If so, return a pointer to the innermost rtx expression in which + it is used. + + If PLOC is nonzero, *PLOC is set to the insn containing the single use. + + If DEST is cc0_rtx, we look only at the next insn. In that case, we don't + care about REG_DEAD notes or LOG_LINKS. + + Otherwise, we find the single use by finding an insn that has a + LOG_LINKS pointing at INSN and has a REG_DEAD note for DEST. If DEST is + only referenced once in that insn, we know that it must be the first + and last insn referencing DEST. */ + +static rtx * +find_single_use (rtx dest, rtx insn, rtx *ploc) +{ + rtx next; + rtx *result; + rtx link; + +#ifdef HAVE_cc0 + if (dest == cc0_rtx) + { + next = NEXT_INSN (insn); + if (next == 0 + || (!NONJUMP_INSN_P (next) && !JUMP_P (next))) + return 0; + + result = find_single_use_1 (dest, &PATTERN (next)); + if (result && ploc) + *ploc = next; + return result; + } +#endif + + if (!REG_P (dest)) + return 0; + + for (next = next_nonnote_insn (insn); + next != 0 && !LABEL_P (next); + next = next_nonnote_insn (next)) + if (INSN_P (next) && dead_or_set_p (next, dest)) + { + for (link = LOG_LINKS (next); link; link = XEXP (link, 1)) + if (XEXP (link, 0) == insn) + break; + + if (link) + { + result = find_single_use_1 (dest, &PATTERN (next)); + if (ploc) + *ploc = next; + return result; + } + } + + return 0; +} + /* Substitute NEWVAL, an rtx expression, into INTO, a place in some insn. The substitution can be undone by undo_all. If INTO is already set to NEWVAL, do not record this change. Because computing NEWVAL might @@ -600,15 +755,12 @@ combine_validate_cost (rtx i1, rtx i2, rtx i3, rtx newpat, rtx newi2pat) int old_cost, new_cost; /* Lookup the original insn_rtx_costs. */ - i2_cost = INSN_UID (i2) <= last_insn_cost - ? uid_insn_cost[INSN_UID (i2)] : 0; - i3_cost = INSN_UID (i3) <= last_insn_cost - ? uid_insn_cost[INSN_UID (i3)] : 0; + i2_cost = INSN_COST (i2); + i3_cost = INSN_COST (i3); if (i1) { - i1_cost = INSN_UID (i1) <= last_insn_cost - ? uid_insn_cost[INSN_UID (i1)] : 0; + i1_cost = INSN_COST (i1); old_cost = (i1_cost > 0 && i2_cost > 0 && i3_cost > 0) ? i1_cost + i2_cost + i3_cost : 0; } @@ -636,8 +788,7 @@ combine_validate_cost (rtx i1, rtx i2, rtx i3, rtx newpat, rtx newi2pat) { int old_other_cost, new_other_cost; - old_other_cost = (INSN_UID (undobuf.other_insn) <= last_insn_cost - ? uid_insn_cost[INSN_UID (undobuf.other_insn)] : 0); + old_other_cost = INSN_COST (undobuf.other_insn); new_other_cost = insn_rtx_cost (PATTERN (undobuf.other_insn)); if (old_other_cost > 0 && new_other_cost > 0) { @@ -685,13 +836,163 @@ combine_validate_cost (rtx i1, rtx i2, rtx i3, rtx newpat, rtx newi2pat) } /* Update the uid_insn_cost array with the replacement costs. */ - uid_insn_cost[INSN_UID (i2)] = new_i2_cost; - uid_insn_cost[INSN_UID (i3)] = new_i3_cost; + INSN_COST (i2) = new_i2_cost; + INSN_COST (i3) = new_i3_cost; if (i1) - uid_insn_cost[INSN_UID (i1)] = 0; + INSN_COST (i1) = 0; return true; } + + +/* Delete any insns that copy a register to itself. */ + +static void +delete_noop_moves (void) +{ + rtx insn, next; + basic_block bb; + + FOR_EACH_BB (bb) + { + for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb)); insn = next) + { + next = NEXT_INSN (insn); + if (INSN_P (insn) && noop_move_p (insn)) + { + rtx note; + + /* If we're about to remove the first insn of a libcall + then move the libcall note to the next real insn and + update the retval note. */ + if ((note = find_reg_note (insn, REG_LIBCALL, NULL_RTX)) + && XEXP (note, 0) != insn) + { + rtx new_libcall_insn = next_real_insn (insn); + rtx retval_note = find_reg_note (XEXP (note, 0), + REG_RETVAL, NULL_RTX); + REG_NOTES (new_libcall_insn) + = gen_rtx_INSN_LIST (REG_LIBCALL, XEXP (note, 0), + REG_NOTES (new_libcall_insn)); + XEXP (retval_note, 0) = new_libcall_insn; + } + + if (dump_file) + fprintf (dump_file, "deleting noop move %d\n", INSN_UID (insn)); + + delete_insn_and_edges (insn); + } + } + } +} + + +/* Fill in log links field for all insns. */ + +static void +create_log_links (void) +{ + basic_block bb; + rtx *next_use, insn; + struct df_ref **def_vec, **use_vec; + + next_use = XCNEWVEC (rtx, max_reg_num ()); + + /* Pass through each block from the end, recording the uses of each + register and establishing log links when def is encountered. + Note that we do not clear next_use array in order to save time, + so we have to test whether the use is in the same basic block as def. + + There are a few cases below when we do not consider the definition or + usage -- these are taken from original flow.c did. Don't ask me why it is + done this way; I don't know and if it works, I don't want to know. */ + + FOR_EACH_BB (bb) + { + FOR_BB_INSNS_REVERSE (bb, insn) + { + if (!INSN_P (insn)) + continue; + + /* Log links are created only once. */ + gcc_assert (!LOG_LINKS (insn)); + + for (def_vec = DF_INSN_DEFS (insn); *def_vec; def_vec++) + { + struct df_ref *def = *def_vec; + int regno = DF_REF_REGNO (def); + rtx use_insn; + + if (!next_use[regno]) + continue; + + /* Do not consider if it is pre/post modification in MEM. */ + if (DF_REF_FLAGS (def) & DF_REF_PRE_POST_MODIFY) + continue; + + /* Do not make the log link for frame pointer. */ + if ((regno == FRAME_POINTER_REGNUM + && (! reload_completed || frame_pointer_needed)) +#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM + || (regno == HARD_FRAME_POINTER_REGNUM + && (! reload_completed || frame_pointer_needed)) +#endif +#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM + || (regno == ARG_POINTER_REGNUM && fixed_regs[regno]) +#endif + ) + continue; + + use_insn = next_use[regno]; + if (BLOCK_FOR_INSN (use_insn) == bb) + { + /* flow.c claimed: + + We don't build a LOG_LINK for hard registers contained + in ASM_OPERANDs. If these registers get replaced, + we might wind up changing the semantics of the insn, + even if reload can make what appear to be valid + assignments later. */ + if (regno >= FIRST_PSEUDO_REGISTER + || asm_noperands (PATTERN (use_insn)) < 0) + LOG_LINKS (use_insn) = + alloc_INSN_LIST (insn, LOG_LINKS (use_insn)); + } + next_use[regno] = NULL_RTX; + } + + for (use_vec = DF_INSN_USES (insn); *use_vec; use_vec++) + { + struct df_ref *use = *use_vec; + int regno = DF_REF_REGNO (use); + + /* Do not consider the usage of the stack pointer + by function call. */ + if (DF_REF_FLAGS (use) & DF_REF_CALL_STACK_USAGE) + continue; + + next_use[regno] = insn; + } + } + } + + free (next_use); +} + +/* Clear LOG_LINKS fields of insns. */ + +static void +clear_log_links (void) +{ + rtx insn; + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (INSN_P (insn)) + free_INSN_LIST_list (&LOG_LINKS (insn)); +} + + + /* Main entry point for combiner. F is the first insn of the function. NREGS is the first unused pseudo-reg number. @@ -705,13 +1006,16 @@ combine_instructions (rtx f, unsigned int nregs) #ifdef HAVE_cc0 rtx prev; #endif - int i; - unsigned int j = 0; rtx links, nextlinks; - sbitmap_iterator sbi; + rtx first; int new_direct_jump_p = 0; + for (first = f; first && !INSN_P (first); ) + first = NEXT_INSN (first); + if (!first) + return 0; + combine_attempts = 0; combine_merges = 0; combine_extras = 0; @@ -725,14 +1029,10 @@ combine_instructions (rtx f, unsigned int nregs) init_recog_no_volatile (); - /* Compute maximum uid value so uid_cuid can be allocated. */ - - for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) - if (INSN_UID (insn) > i) - i = INSN_UID (insn); - - uid_cuid = XNEWVEC (int, i + 1); - max_uid_cuid = i; + /* Allocate array for insn info. */ + max_uid_known = get_max_uid (); + uid_log_links = XCNEWVEC (rtx, max_uid_known + 1); + uid_insn_cost = XCNEWVEC (int, max_uid_known + 1); nonzero_bits_mode = mode_for_size (HOST_BITS_PER_WIDE_INT, MODE_INT, 0); @@ -741,81 +1041,70 @@ combine_instructions (rtx f, unsigned int nregs) nonzero_sign_valid = 0; - /* Compute the mapping from uids to cuids. - Cuids are numbers assigned to insns, like uids, - except that cuids increase monotonically through the code. - - Scan all SETs and see if we can deduce anything about what + /* Scan all SETs and see if we can deduce anything about what bits are known to be zero for some registers and how many copies of the sign bit are known to exist for those registers. Also set any known values so that we can use it while searching for what bits are known to be set. */ - label_tick = 1; + label_tick = label_tick_ebb_start = 1; - setup_incoming_promotions (); + setup_incoming_promotions (first); - refresh_blocks = sbitmap_alloc (last_basic_block); - sbitmap_zero (refresh_blocks); - - /* Allocate array of current insn_rtx_costs. */ - uid_insn_cost = XCNEWVEC (int, max_uid_cuid + 1); - last_insn_cost = max_uid_cuid; - - for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) + create_log_links (); + FOR_EACH_BB (this_basic_block) { - uid_cuid[INSN_UID (insn)] = ++i; - subst_low_cuid = i; - subst_insn = insn; + last_call_luid = 0; + mem_last_set = -1; + label_tick++; + FOR_BB_INSNS (this_basic_block, insn) + if (INSN_P (insn) && BLOCK_FOR_INSN (insn)) + { + subst_low_luid = DF_INSN_LUID (insn); + subst_insn = insn; - if (INSN_P (insn)) - { - note_stores (PATTERN (insn), set_nonzero_bits_and_sign_copies, - NULL); - record_dead_and_set_regs (insn); + note_stores (PATTERN (insn), set_nonzero_bits_and_sign_copies, + insn); + record_dead_and_set_regs (insn); #ifdef AUTO_INC_DEC - for (links = REG_NOTES (insn); links; links = XEXP (links, 1)) - if (REG_NOTE_KIND (links) == REG_INC) - set_nonzero_bits_and_sign_copies (XEXP (links, 0), NULL_RTX, - NULL); + for (links = REG_NOTES (insn); links; links = XEXP (links, 1)) + if (REG_NOTE_KIND (links) == REG_INC) + set_nonzero_bits_and_sign_copies (XEXP (links, 0), NULL_RTX, + insn); #endif - /* Record the current insn_rtx_cost of this instruction. */ - if (NONJUMP_INSN_P (insn)) - uid_insn_cost[INSN_UID (insn)] = insn_rtx_cost (PATTERN (insn)); - if (dump_file) - fprintf(dump_file, "insn_cost %d: %d\n", - INSN_UID (insn), uid_insn_cost[INSN_UID (insn)]); - } - - if (LABEL_P (insn)) - label_tick++; + /* Record the current insn_rtx_cost of this instruction. */ + if (NONJUMP_INSN_P (insn)) + INSN_COST (insn) = insn_rtx_cost (PATTERN (insn)); + if (dump_file) + fprintf(dump_file, "insn_cost %d: %d\n", + INSN_UID (insn), INSN_COST (insn)); + } + else if (LABEL_P (insn)) + label_tick_ebb_start = label_tick; } nonzero_sign_valid = 1; /* Now scan all the insns in forward order. */ - label_tick = 1; - last_call_cuid = 0; - mem_last_set = 0; + label_tick = label_tick_ebb_start = 1; init_reg_last (); - setup_incoming_promotions (); + setup_incoming_promotions (first); FOR_EACH_BB (this_basic_block) { + last_call_luid = 0; + mem_last_set = -1; + label_tick++; for (insn = BB_HEAD (this_basic_block); insn != NEXT_INSN (BB_END (this_basic_block)); insn = next ? next : NEXT_INSN (insn)) { next = 0; - - if (LABEL_P (insn)) - label_tick++; - - else if (INSN_P (insn)) + if (INSN_P (insn)) { /* See if we know about function return values before this insn based upon SUBREG flags. */ @@ -956,24 +1245,20 @@ combine_instructions (rtx f, unsigned int nregs) retry: ; } + else if (LABEL_P (insn)) + label_tick_ebb_start = label_tick; } } - clear_bb_flags (); - EXECUTE_IF_SET_IN_SBITMAP (refresh_blocks, 0, j, sbi) - BASIC_BLOCK (j)->flags |= BB_DIRTY; + clear_log_links (); + clear_bb_flags (); new_direct_jump_p |= purge_all_dead_edges (); delete_noop_moves (); - update_life_info_in_dirty_blocks (UPDATE_LIFE_GLOBAL_RM_NOTES, - PROP_DEATH_NOTES | PROP_SCAN_DEAD_CODE - | PROP_KILL_DEAD_CODE); - /* Clean up. */ - sbitmap_free (refresh_blocks); + free (uid_log_links); free (uid_insn_cost); free (reg_stat); - free (uid_cuid); { struct undo *undo, *next; @@ -1012,16 +1297,13 @@ init_reg_last (void) /* Set up any promoted values for incoming argument registers. */ static void -setup_incoming_promotions (void) +setup_incoming_promotions (rtx first) { - rtx first; tree arg; if (!targetm.calls.promote_function_args (TREE_TYPE (cfun->decl))) return; - first = get_insns (); - for (arg = DECL_ARGUMENTS (current_function_decl); arg; arg = TREE_CHAIN (arg)) { @@ -1059,9 +1341,9 @@ setup_incoming_promotions (void) by any set of X. */ static void -set_nonzero_bits_and_sign_copies (rtx x, rtx set, - void *data ATTRIBUTE_UNUSED) +set_nonzero_bits_and_sign_copies (rtx x, rtx set, void *data) { + rtx insn = (rtx) data; unsigned int num; if (REG_P (x) @@ -1069,7 +1351,7 @@ set_nonzero_bits_and_sign_copies (rtx x, rtx set, /* If this register is undefined at the start of the file, we can't say what its contents were. */ && ! REGNO_REG_SET_P - (ENTRY_BLOCK_PTR->next_bb->il.rtl->global_live_at_start, REGNO (x)) + (DF_LR_IN (ENTRY_BLOCK_PTR->next_bb), REGNO (x)) && GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT) { if (set == 0 || GET_CODE (set) == CLOBBER) @@ -1079,6 +1361,39 @@ set_nonzero_bits_and_sign_copies (rtx x, rtx set, return; } + /* If this register is being initialized using itself, and the + register is uninitialized in this basic block, and there are + no LOG_LINKS which set the register, then part of the + register is uninitialized. In that case we can't assume + anything about the number of nonzero bits. + + ??? We could do better if we checked this in + reg_{nonzero_bits,num_sign_bit_copies}_for_combine. Then we + could avoid making assumptions about the insn which initially + sets the register, while still using the information in other + insns. We would have to be careful to check every insn + involved in the combination. */ + + if (insn + && reg_referenced_p (x, PATTERN (insn)) + && !REGNO_REG_SET_P (DF_LR_IN (BLOCK_FOR_INSN (insn)), + REGNO (x))) + { + rtx link; + + for (link = LOG_LINKS (insn); link; link = XEXP (link, 1)) + { + if (dead_or_set_p (XEXP (link, 0), x)) + break; + } + if (!link) + { + reg_stat[REGNO (x)].nonzero_bits = GET_MODE_MASK (GET_MODE (x)); + reg_stat[REGNO (x)].sign_bit_copies = 1; + return; + } + } + /* If this is a complex assignment, see if we can convert it into a simple assignment. */ set = expand_field_assignment (set); @@ -1307,7 +1622,7 @@ can_combine_p (rtx insn, rtx i3, rtx pred ATTRIBUTE_UNUSED, rtx succ, || (! all_adjacent && (((!MEM_P (src) || ! find_reg_note (insn, REG_EQUIV, src)) - && use_crosses_set_p (src, INSN_CUID (insn))) + && use_crosses_set_p (src, DF_INSN_LUID (insn))) || (GET_CODE (src) == ASM_OPERANDS && MEM_VOLATILE_P (src)) || GET_CODE (src) == UNSPEC_VOLATILE)) /* If there is a REG_NO_CONFLICT note for DEST in I3 or SUCC, we get @@ -1319,7 +1634,7 @@ can_combine_p (rtx insn, rtx i3, rtx pred ATTRIBUTE_UNUSED, rtx succ, and it is a pain to update that information. Exception: if source is a constant, moving it later can't hurt. Accept that special case, because it helps -fforce-addr a lot. */ - || (INSN_CUID (insn) < last_call_cuid && ! CONSTANT_P (src))) + || (DF_INSN_LUID (insn) < last_call_luid && ! CONSTANT_P (src))) return 0; /* DEST must either be a REG or CC0. */ @@ -1869,7 +2184,7 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) /* If I1 and I2 both feed I3, they can be in any order. To simplify the code below, set I1 to be the earlier of the two insns. */ - if (i1 && INSN_CUID (i1) > INSN_CUID (i2)) + if (i1 && DF_INSN_LUID (i1) > DF_INSN_LUID (i2)) temp = i1, i1 = i2, i2 = temp; added_links_insn = 0; @@ -1934,7 +2249,7 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) combine_merges++; subst_insn = i3; - subst_low_cuid = INSN_CUID (i2); + subst_low_luid = DF_INSN_LUID (i2); added_sets_2 = added_sets_1 = 0; i2dest = SET_SRC (PATTERN (i3)); @@ -2075,7 +2390,7 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) combine_merges++; subst_insn = i3; - subst_low_cuid = INSN_CUID (i2); + subst_low_luid = DF_INSN_LUID (i2); added_sets_2 = added_sets_1 = 0; i2dest = SET_DEST (temp); i2dest_killed = dead_or_set_p (i2, i2dest); @@ -2121,14 +2436,13 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) if (i == 1) { /* We make I1 with the same INSN_UID as I2. This gives it - the same INSN_CUID for value tracking. Our fake I1 will + the same DF_INSN_LUID for value tracking. Our fake I1 will never appear in the insn stream so giving it the same INSN_UID as I2 will not cause a problem. */ i1 = gen_rtx_INSN (VOIDmode, INSN_UID (i2), NULL_RTX, i2, BLOCK_FOR_INSN (i2), INSN_LOCATOR (i2), - XVECEXP (PATTERN (i2), 0, 1), -1, NULL_RTX, - NULL_RTX); + XVECEXP (PATTERN (i2), 0, 1), -1, NULL_RTX); SUBST (PATTERN (i2), XVECEXP (PATTERN (i2), 0, 0)); SUBST (XEXP (SET_SRC (PATTERN (i2)), 0), @@ -2342,12 +2656,12 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) simplifications. */ if (i1) { - subst_low_cuid = INSN_CUID (i1); + subst_low_luid = DF_INSN_LUID (i1); i1src = subst (i1src, pc_rtx, pc_rtx, 0, 0); } else { - subst_low_cuid = INSN_CUID (i2); + subst_low_luid = DF_INSN_LUID (i2); i2src = subst (i2src, pc_rtx, pc_rtx, 0, 0); } } @@ -2358,7 +2672,7 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) need to make a unique copy of I2SRC each time we substitute it to avoid self-referential rtl. */ - subst_low_cuid = INSN_CUID (i2); + subst_low_luid = DF_INSN_LUID (i2); newpat = subst (PATTERN (i3), i2dest, i2src, 0, ! i1_feeds_i3 && i1dest_in_i1src); substed_i2 = 1; @@ -2384,7 +2698,7 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) } n_occurrences = 0; - subst_low_cuid = INSN_CUID (i1); + subst_low_luid = DF_INSN_LUID (i1); newpat = subst (newpat, i1dest, i1src, 0, 0); substed_i1 = 1; } @@ -2631,7 +2945,7 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) } else if (m_split && NEXT_INSN (NEXT_INSN (m_split)) == NULL_RTX && (next_real_insn (i2) == i3 - || ! use_crosses_set_p (PATTERN (m_split), INSN_CUID (i2)))) + || ! use_crosses_set_p (PATTERN (m_split), DF_INSN_LUID (i2)))) { rtx i2set, i3set; rtx newi3pat = PATTERN (NEXT_INSN (m_split)); @@ -2675,7 +2989,7 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) if (REG_P (new_i3_dest) && REG_P (new_i2_dest) && REGNO (new_i3_dest) == REGNO (new_i2_dest)) - REG_N_SETS (REGNO (new_i2_dest))++; + INC_REG_N_SETS (REGNO (new_i2_dest), 1); } } @@ -2695,7 +3009,7 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) || can_change_dest_mode (i2dest, added_sets_2, GET_MODE (*split))) && (next_real_insn (i2) == i3 - || ! use_crosses_set_p (*split, INSN_CUID (i2))) + || ! use_crosses_set_p (*split, DF_INSN_LUID (i2))) /* We can't overwrite I2DEST if its value is still used by NEWPAT. */ && ! reg_referenced_p (i2dest, newpat)) @@ -2865,7 +3179,7 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) && rtx_equal_p (SET_SRC (XVECEXP (newpat, 0, 1)), XEXP (SET_SRC (XVECEXP (newpat, 0, 0)), 0)) && ! use_crosses_set_p (SET_SRC (XVECEXP (newpat, 0, 1)), - INSN_CUID (i2)) + DF_INSN_LUID (i2)) && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != ZERO_EXTRACT && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != STRICT_LOW_PART && ! (temp = SET_DEST (XVECEXP (newpat, 0, 1)), @@ -2919,7 +3233,7 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != ZERO_EXTRACT && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != STRICT_LOW_PART && ! use_crosses_set_p (SET_SRC (XVECEXP (newpat, 0, 1)), - INSN_CUID (i2)) + DF_INSN_LUID (i2)) && ! reg_referenced_p (SET_DEST (XVECEXP (newpat, 0, 1)), XVECEXP (newpat, 0, 0)) && ! reg_referenced_p (SET_DEST (XVECEXP (newpat, 0, 0)), @@ -2995,18 +3309,9 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) if (REG_NOTE_KIND (note) == REG_UNUSED && ! reg_set_p (XEXP (note, 0), PATTERN (undobuf.other_insn))) - { - if (REG_P (XEXP (note, 0))) - REG_N_DEATHS (REGNO (XEXP (note, 0)))--; - - remove_note (undobuf.other_insn, note); - } + remove_note (undobuf.other_insn, note); } - for (note = new_other_notes; note; note = XEXP (note, 1)) - if (REG_P (XEXP (note, 0))) - REG_N_DEATHS (REGNO (XEXP (note, 0)))++; - distribute_notes (new_other_notes, undobuf.other_insn, undobuf.other_insn, NULL_RTX, NULL_RTX, NULL_RTX); } @@ -3210,11 +3515,11 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) if (newi2pat) { - move_deaths (newi2pat, NULL_RTX, INSN_CUID (i1), i2, &midnotes); - move_deaths (newpat, newi2pat, INSN_CUID (i1), i3, &midnotes); + move_deaths (newi2pat, NULL_RTX, DF_INSN_LUID (i1), i2, &midnotes); + move_deaths (newpat, newi2pat, DF_INSN_LUID (i1), i3, &midnotes); } else - move_deaths (newpat, NULL_RTX, i1 ? INSN_CUID (i1) : INSN_CUID (i2), + move_deaths (newpat, NULL_RTX, i1 ? DF_INSN_LUID (i1) : DF_INSN_LUID (i2), i3, &midnotes); /* Distribute all the LOG_LINKS and REG_NOTES from I1, I2, and I3. */ @@ -3233,26 +3538,13 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) /* Distribute any notes added to I2 or I3 by recog_for_combine. We know these are REG_UNUSED and want them to go to the desired insn, - so we always pass it as i3. We have not counted the notes in - reg_n_deaths yet, so we need to do so now. */ + so we always pass it as i3. */ if (newi2pat && new_i2_notes) - { - for (temp = new_i2_notes; temp; temp = XEXP (temp, 1)) - if (REG_P (XEXP (temp, 0))) - REG_N_DEATHS (REGNO (XEXP (temp, 0)))++; - - distribute_notes (new_i2_notes, i2, i2, NULL_RTX, NULL_RTX, NULL_RTX); - } - + distribute_notes (new_i2_notes, i2, i2, NULL_RTX, NULL_RTX, NULL_RTX); + if (new_i3_notes) - { - for (temp = new_i3_notes; temp; temp = XEXP (temp, 1)) - if (REG_P (XEXP (temp, 0))) - REG_N_DEATHS (REGNO (XEXP (temp, 0)))++; - - distribute_notes (new_i3_notes, i3, i3, NULL_RTX, NULL_RTX, NULL_RTX); - } + distribute_notes (new_i3_notes, i3, i3, NULL_RTX, NULL_RTX, NULL_RTX); /* If I3DEST was used in I3SRC, it really died in I3. We may need to put a REG_DEAD note for it somewhere. If NEWI2PAT exists and sets @@ -3263,9 +3555,6 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) if (i3dest_killed) { - if (REG_P (i3dest_killed)) - REG_N_DEATHS (REGNO (i3dest_killed))++; - if (newi2pat && reg_set_p (i3dest_killed, newi2pat)) distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i3dest_killed, NULL_RTX), @@ -3279,9 +3568,6 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) if (i2dest_in_i2src) { - if (REG_P (i2dest)) - REG_N_DEATHS (REGNO (i2dest))++; - if (newi2pat && reg_set_p (i2dest, newi2pat)) distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i2dest, NULL_RTX), NULL_RTX, i2, NULL_RTX, NULL_RTX, NULL_RTX); @@ -3293,9 +3579,6 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) if (i1dest_in_i1src) { - if (REG_P (i1dest)) - REG_N_DEATHS (REGNO (i1dest))++; - if (newi2pat && reg_set_p (i1dest, newi2pat)) distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i1dest, NULL_RTX), NULL_RTX, i2, NULL_RTX, NULL_RTX, NULL_RTX); @@ -3336,7 +3619,7 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) && ! i2dest_in_i2src) { regno = REGNO (i2dest); - REG_N_SETS (regno)--; + INC_REG_N_SETS (regno, -1); } } @@ -3354,7 +3637,7 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) regno = REGNO (i1dest); if (! added_sets_1 && ! i1dest_in_i1src) - REG_N_SETS (regno)--; + INC_REG_N_SETS (regno, -1); } /* Update reg_stat[].nonzero_bits et al for any changes that may have @@ -3400,13 +3683,53 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p) && SET_DEST (newpat) == pc_rtx) *new_direct_jump_p = 1; } + + if (undobuf.other_insn != NULL_RTX) + { + if (dump_file) + { + fprintf (dump_file, "modifying other_insn "); + dump_insn_slim (dump_file, undobuf.other_insn); + } + df_insn_rescan (undobuf.other_insn); + } + + if (i1 && !(NOTE_P(i1) && (NOTE_KIND (i1) == NOTE_INSN_DELETED))) + { + if (dump_file) + { + fprintf (dump_file, "modifying insn i1 "); + dump_insn_slim (dump_file, i1); + } + df_insn_rescan (i1); + } + if (i2 && !(NOTE_P(i2) && (NOTE_KIND (i2) == NOTE_INSN_DELETED))) + { + if (dump_file) + { + fprintf (dump_file, "modifying insn i2 "); + dump_insn_slim (dump_file, i2); + } + df_insn_rescan (i2); + } + + if (i3 && !(NOTE_P(i3) && (NOTE_KIND (i3) == NOTE_INSN_DELETED))) + { + if (dump_file) + { + fprintf (dump_file, "modifying insn i3 "); + dump_insn_slim (dump_file, i3); + } + df_insn_rescan (i3); + } + combine_successes++; undo_commit (); if (added_links_insn - && (newi2pat == 0 || INSN_CUID (added_links_insn) < INSN_CUID (i2)) - && INSN_CUID (added_links_insn) < INSN_CUID (i3)) + && (newi2pat == 0 || DF_INSN_LUID (added_links_insn) < DF_INSN_LUID (i2)) + && DF_INSN_LUID (added_links_insn) < DF_INSN_LUID (i3)) return added_links_insn; else return newi2pat ? i2 : i3; @@ -3906,11 +4229,11 @@ subst (rtx x, rtx from, rtx to, int in_dest, int unique_copy) return (unique_copy && n_occurrences > 1 ? copy_rtx (to) : to); } - /* If X and FROM are the same register but different modes, they will - not have been seen as equal above. However, flow.c will make a - LOG_LINKS entry for that case. If we do nothing, we will try to - rerecognize our original insn and, when it succeeds, we will - delete the feeding insn, which is incorrect. + /* If X and FROM are the same register but different modes, they + will not have been seen as equal above. However, the log links code + will make a LOG_LINKS entry for that case. If we do nothing, we + will try to rerecognize our original insn and, when it succeeds, + we will delete the feeding insn, which is incorrect. So force this insn not to match in this (rare) case. */ if (! in_dest && code == REG && REG_P (from) @@ -8277,13 +8600,14 @@ reg_nonzero_bits_for_combine (rtx x, enum machine_mode mode, && (reg_stat[REGNO (x)].last_set_mode == mode || (GET_MODE_CLASS (reg_stat[REGNO (x)].last_set_mode) == MODE_INT && GET_MODE_CLASS (mode) == MODE_INT)) - && (reg_stat[REGNO (x)].last_set_label == label_tick + && ((reg_stat[REGNO (x)].last_set_label >= label_tick_ebb_start + && reg_stat[REGNO (x)].last_set_label < label_tick) + || (reg_stat[REGNO (x)].last_set_label == label_tick + && DF_INSN_LUID (reg_stat[REGNO (x)].last_set) < subst_low_luid) || (REGNO (x) >= FIRST_PSEUDO_REGISTER && REG_N_SETS (REGNO (x)) == 1 - && ! REGNO_REG_SET_P - (ENTRY_BLOCK_PTR->next_bb->il.rtl->global_live_at_start, - REGNO (x)))) - && INSN_CUID (reg_stat[REGNO (x)].last_set) < subst_low_cuid) + && !REGNO_REG_SET_P + (DF_LR_IN (ENTRY_BLOCK_PTR->next_bb), REGNO (x))))) { *nonzero &= reg_stat[REGNO (x)].last_set_nonzero_bits; return NULL; @@ -8345,13 +8669,14 @@ reg_num_sign_bit_copies_for_combine (rtx x, enum machine_mode mode, if (reg_stat[REGNO (x)].last_set_value != 0 && reg_stat[REGNO (x)].last_set_mode == mode - && (reg_stat[REGNO (x)].last_set_label == label_tick + && ((reg_stat[REGNO (x)].last_set_label >= label_tick_ebb_start + && reg_stat[REGNO (x)].last_set_label < label_tick) + || (reg_stat[REGNO (x)].last_set_label == label_tick + && DF_INSN_LUID (reg_stat[REGNO (x)].last_set) < subst_low_luid) || (REGNO (x) >= FIRST_PSEUDO_REGISTER && REG_N_SETS (REGNO (x)) == 1 - && ! REGNO_REG_SET_P - (ENTRY_BLOCK_PTR->next_bb->il.rtl->global_live_at_start, - REGNO (x)))) - && INSN_CUID (reg_stat[REGNO (x)].last_set) < subst_low_cuid) + && !REGNO_REG_SET_P + (DF_LR_IN (ENTRY_BLOCK_PTR->next_bb), REGNO (x))))) { *result = reg_stat[REGNO (x)].last_set_sign_bit_copies; return NULL; @@ -9382,8 +9707,12 @@ recog_for_combine (rtx *pnewpat, rtx insn, rtx *pnotes) if (REG_P (XEXP (XVECEXP (newpat, 0, i), 0)) && ! reg_dead_at_p (XEXP (XVECEXP (newpat, 0, i), 0), insn)) return -1; - notes = gen_rtx_EXPR_LIST (REG_UNUSED, - XEXP (XVECEXP (newpat, 0, i), 0), notes); + if (GET_CODE (XEXP (XVECEXP (newpat, 0, i), 0)) != SCRATCH) + { + gcc_assert (REG_P (XEXP (XVECEXP (newpat, 0, i), 0))); + notes = gen_rtx_EXPR_LIST (REG_UNUSED, + XEXP (XVECEXP (newpat, 0, i), 0), notes); + } } pat = newpat; } @@ -9451,11 +9780,6 @@ gen_lowpart_for_combine (enum machine_mode omode, rtx x) result = gen_lowpart_common (omode, x); -#ifdef CANNOT_CHANGE_MODE_CLASS - if (result != 0 && GET_CODE (result) == SUBREG) - record_subregs_of_mode (result); -#endif - if (result) return result; @@ -10867,7 +11191,7 @@ record_value_for_reg (rtx reg, rtx insn, rtx value) /* Set things up so get_last_value is allowed to see anything set up to our insn. */ - subst_low_cuid = INSN_CUID (insn); + subst_low_luid = DF_INSN_LUID (insn); tem = get_last_value (reg); /* If TEM is simply a binary operation with two CLOBBERs as operands, @@ -10923,7 +11247,8 @@ record_value_for_reg (rtx reg, rtx insn, rtx value) for (i = regno; i < endregno; i++) { reg_stat[i].last_set_label = label_tick; - if (!insn || (value && reg_stat[i].last_set_table_tick == label_tick)) + if (!insn + || (value && reg_stat[i].last_set_table_tick >= label_tick_ebb_start)) reg_stat[i].last_set_invalid = 1; else reg_stat[i].last_set_invalid = 0; @@ -10949,7 +11274,7 @@ record_value_for_reg (rtx reg, rtx insn, rtx value) if (value) { enum machine_mode mode = GET_MODE (reg); - subst_low_cuid = INSN_CUID (insn); + subst_low_luid = DF_INSN_LUID (insn); reg_stat[regno].last_set_mode = mode; if (GET_MODE_CLASS (mode) == MODE_INT && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT) @@ -11000,7 +11325,7 @@ record_dead_and_set_regs_1 (rtx dest, rtx setter, void *data) else if (MEM_P (dest) /* Ignore pushes, they clobber nothing. */ && ! push_operand (dest, GET_MODE (dest))) - mem_last_set = INSN_CUID (record_dead_insn); + mem_last_set = DF_INSN_LUID (record_dead_insn); } /* Update the records of when each REG was most recently set or killed @@ -11010,7 +11335,7 @@ record_dead_and_set_regs_1 (rtx dest, rtx setter, void *data) We update reg_stat[], in particular fields last_set, last_set_value, last_set_mode, last_set_nonzero_bits, last_set_sign_bit_copies, last_death, and also the similar information mem_last_set (which insn - most recently modified memory) and last_call_cuid (which insn was the + most recently modified memory) and last_call_luid (which insn was the most recent subroutine call). */ static void @@ -11039,6 +11364,8 @@ record_dead_and_set_regs (rtx insn) for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i)) { + reg_stat[i].last_set_invalid = 1; + reg_stat[i].last_set = insn; reg_stat[i].last_set_value = 0; reg_stat[i].last_set_mode = 0; reg_stat[i].last_set_nonzero_bits = 0; @@ -11047,10 +11374,10 @@ record_dead_and_set_regs (rtx insn) reg_stat[i].truncated_to_mode = 0; } - last_call_cuid = mem_last_set = INSN_CUID (insn); + last_call_luid = mem_last_set = DF_INSN_LUID (insn); /* We can't combine into a call pattern. Remember, though, that - the return value register is set at this CUID. We could + the return value register is set at this LUID. We could still replace a register with the return value from the wrong subroutine call! */ note_stores (PATTERN (insn), record_dead_and_set_regs_1, NULL_RTX); @@ -11117,7 +11444,8 @@ reg_truncated_to_mode (enum machine_mode mode, rtx x) { enum machine_mode truncated = reg_stat[REGNO (x)].truncated_to_mode; - if (truncated == 0 || reg_stat[REGNO (x)].truncation_label != label_tick) + if (truncated == 0 + || reg_stat[REGNO (x)].truncation_label < label_tick_ebb_start) return false; if (GET_MODE_SIZE (truncated) <= GET_MODE_SIZE (mode)) return true; @@ -11158,7 +11486,7 @@ record_truncated_value (rtx x) return; if (reg_stat[REGNO (x)].truncated_to_mode == 0 - || reg_stat[REGNO (x)].truncation_label < label_tick + || reg_stat[REGNO (x)].truncation_label < label_tick_ebb_start || (GET_MODE_SIZE (truncated_mode) < GET_MODE_SIZE (reg_stat[REGNO (x)].truncated_to_mode))) { @@ -11233,9 +11561,8 @@ get_last_value_validate (rtx *loc, rtx insn, int tick, int replace) live at the beginning of the function, it is always valid. */ || (! (regno >= FIRST_PSEUDO_REGISTER && REG_N_SETS (regno) == 1 - && (! REGNO_REG_SET_P - (ENTRY_BLOCK_PTR->next_bb->il.rtl->global_live_at_start, - regno))) + && !REGNO_REG_SET_P + (DF_LR_IN (ENTRY_BLOCK_PTR->next_bb), regno)) && reg_stat[j].last_set_label > tick)) { if (replace) @@ -11249,7 +11576,7 @@ get_last_value_validate (rtx *loc, rtx insn, int tick, int replace) no stores after it that might have clobbered the value. We don't have alias info, so we assume any store invalidates it. */ else if (MEM_P (x) && !MEM_READONLY_P (x) - && INSN_CUID (insn) <= mem_last_set) + && DF_INSN_LUID (insn) <= mem_last_set) { if (replace) *loc = gen_rtx_CLOBBER (GET_MODE (x), const0_rtx); @@ -11341,17 +11668,17 @@ get_last_value (rtx x) block. */ if (value == 0 - || (reg_stat[regno].last_set_label != label_tick + || (reg_stat[regno].last_set_label < label_tick_ebb_start && (regno < FIRST_PSEUDO_REGISTER || REG_N_SETS (regno) != 1 - || (REGNO_REG_SET_P - (ENTRY_BLOCK_PTR->next_bb->il.rtl->global_live_at_start, - regno))))) + || REGNO_REG_SET_P + (DF_LR_IN (ENTRY_BLOCK_PTR->next_bb), regno)))) return 0; /* If the value was set in a later insn than the ones we are processing, we can't use it even if the register was only set once. */ - if (INSN_CUID (reg_stat[regno].last_set) >= subst_low_cuid) + if (reg_stat[regno].last_set_label == label_tick + && DF_INSN_LUID (reg_stat[regno].last_set) >= subst_low_luid) return 0; /* If the value has all its registers valid, return it. */ @@ -11371,10 +11698,10 @@ get_last_value (rtx x) } /* Return nonzero if expression X refers to a REG or to memory - that is set in an instruction more recent than FROM_CUID. */ + that is set in an instruction more recent than FROM_LUID. */ static int -use_crosses_set_p (rtx x, int from_cuid) +use_crosses_set_p (rtx x, int from_luid) { const char *fmt; int i; @@ -11393,12 +11720,13 @@ use_crosses_set_p (rtx x, int from_cuid) #endif for (; regno < endreg; regno++) if (reg_stat[regno].last_set - && INSN_CUID (reg_stat[regno].last_set) > from_cuid) + && reg_stat[regno].last_set_label == label_tick + && DF_INSN_LUID (reg_stat[regno].last_set) > from_luid) return 1; return 0; } - if (code == MEM && mem_last_set > from_cuid) + if (code == MEM && mem_last_set > from_luid) return 1; fmt = GET_RTX_FORMAT (code); @@ -11409,11 +11737,11 @@ use_crosses_set_p (rtx x, int from_cuid) { int j; for (j = XVECLEN (x, i) - 1; j >= 0; j--) - if (use_crosses_set_p (XVECEXP (x, i, j), from_cuid)) + if (use_crosses_set_p (XVECEXP (x, i, j), from_luid)) return 1; } else if (fmt[i] == 'e' - && use_crosses_set_p (XEXP (x, i), from_cuid)) + && use_crosses_set_p (XEXP (x, i), from_luid)) return 1; } return 0; @@ -11501,14 +11829,13 @@ reg_dead_at_p (rtx reg, rtx insn) } for (i = reg_dead_regno; i < reg_dead_endregno; i++) - if (REGNO_REG_SET_P (block->il.rtl->global_live_at_start, i)) + if (REGNO_REG_SET_P (DF_LIVE_IN (block), i)) return 0; return 1; } -/* Note hard registers in X that are used. This code is similar to - that in flow.c, but much simpler since we don't care about pseudos. */ +/* Note hard registers in X that are used. */ static void mark_used_regs_combine (rtx x) @@ -11616,16 +11943,13 @@ remove_death (unsigned int regno, rtx insn) rtx note = find_regno_note (insn, REG_DEAD, regno); if (note) - { - REG_N_DEATHS (regno)--; - remove_note (insn, note); - } + remove_note (insn, note); return note; } /* For each register (hardware or pseudo) used within expression X, if its - death is in an instruction with cuid between FROM_CUID (inclusive) and + death is in an instruction with luid between FROM_LUID (inclusive) and TO_INSN (exclusive), put a REG_DEAD note for that register in the list headed by PNOTES. @@ -11635,7 +11959,7 @@ remove_death (unsigned int regno, rtx insn) notes will then be distributed as needed. */ static void -move_deaths (rtx x, rtx maybe_kill_insn, int from_cuid, rtx to_insn, +move_deaths (rtx x, rtx maybe_kill_insn, int from_luid, rtx to_insn, rtx *pnotes) { const char *fmt; @@ -11646,28 +11970,15 @@ move_deaths (rtx x, rtx maybe_kill_insn, int from_cuid, rtx to_insn, { unsigned int regno = REGNO (x); rtx where_dead = reg_stat[regno].last_death; - rtx before_dead, after_dead; /* Don't move the register if it gets killed in between from and to. */ if (maybe_kill_insn && reg_set_p (x, maybe_kill_insn) && ! reg_referenced_p (x, maybe_kill_insn)) return; - /* WHERE_DEAD could be a USE insn made by combine, so first we - make sure that we have insns with valid INSN_CUID values. */ - before_dead = where_dead; - while (before_dead && INSN_UID (before_dead) > max_uid_cuid) - before_dead = PREV_INSN (before_dead); - - after_dead = where_dead; - while (after_dead && INSN_UID (after_dead) > max_uid_cuid) - after_dead = NEXT_INSN (after_dead); - - if (before_dead && after_dead - && INSN_CUID (before_dead) >= from_cuid - && (INSN_CUID (after_dead) < INSN_CUID (to_insn) - || (where_dead != after_dead - && INSN_CUID (after_dead) == INSN_CUID (to_insn)))) + if (where_dead + && DF_INSN_LUID (where_dead) >= from_luid + && DF_INSN_LUID (where_dead) < DF_INSN_LUID (to_insn)) { rtx note = remove_death (regno, where_dead); @@ -11720,7 +12031,7 @@ move_deaths (rtx x, rtx maybe_kill_insn, int from_cuid, rtx to_insn, for (i = regno + offset; i < ourend; i++) move_deaths (regno_reg_rtx[i], - maybe_kill_insn, from_cuid, to_insn, &oldnotes); + maybe_kill_insn, from_luid, to_insn, &oldnotes); } if (note != 0 && GET_MODE (XEXP (note, 0)) == GET_MODE (x)) @@ -11730,8 +12041,6 @@ move_deaths (rtx x, rtx maybe_kill_insn, int from_cuid, rtx to_insn, } else *pnotes = gen_rtx_EXPR_LIST (REG_DEAD, x, *pnotes); - - REG_N_DEATHS (regno)++; } return; @@ -11741,7 +12050,7 @@ move_deaths (rtx x, rtx maybe_kill_insn, int from_cuid, rtx to_insn, { rtx dest = SET_DEST (x); - move_deaths (SET_SRC (x), maybe_kill_insn, from_cuid, to_insn, pnotes); + move_deaths (SET_SRC (x), maybe_kill_insn, from_luid, to_insn, pnotes); /* In the case of a ZERO_EXTRACT, a STRICT_LOW_PART, or a SUBREG that accesses one word of a multi-word item, some @@ -11757,7 +12066,7 @@ move_deaths (rtx x, rtx maybe_kill_insn, int from_cuid, rtx to_insn, == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)))) { - move_deaths (dest, maybe_kill_insn, from_cuid, to_insn, pnotes); + move_deaths (dest, maybe_kill_insn, from_luid, to_insn, pnotes); return; } @@ -11771,7 +12080,7 @@ move_deaths (rtx x, rtx maybe_kill_insn, int from_cuid, rtx to_insn, being replaced so the old value is not used in this insn. */ if (MEM_P (dest)) - move_deaths (XEXP (dest, 0), maybe_kill_insn, from_cuid, + move_deaths (XEXP (dest, 0), maybe_kill_insn, from_luid, to_insn, pnotes); return; } @@ -11788,11 +12097,11 @@ move_deaths (rtx x, rtx maybe_kill_insn, int from_cuid, rtx to_insn, { int j; for (j = XVECLEN (x, i) - 1; j >= 0; j--) - move_deaths (XVECEXP (x, i, j), maybe_kill_insn, from_cuid, + move_deaths (XVECEXP (x, i, j), maybe_kill_insn, from_luid, to_insn, pnotes); } else if (fmt[i] == 'e') - move_deaths (XEXP (x, i), maybe_kill_insn, from_cuid, to_insn, pnotes); + move_deaths (XEXP (x, i), maybe_kill_insn, from_luid, to_insn, pnotes); } } @@ -12059,6 +12368,15 @@ distribute_notes (rtx notes, rtx from_insn, rtx i3, rtx i2, rtx elim_i2, to simply delete it. */ break; + case REG_LIBCALL_ID: + /* If the insn previously containing this note still exists, + put it back where it was. Otherwise move it to the previous + insn. */ + if (!NOTE_P (from_insn)) + place = from_insn; + else + place = prev_real_insn (from_insn); + break; case REG_RETVAL: /* If the insn previously containing this note still exists, put it back where it was. Otherwise move it to the previous @@ -12260,10 +12578,9 @@ distribute_notes (rtx notes, rtx from_insn, rtx i3, rtx i2, rtx elim_i2, i2 but does not die in i2, and place is between i2 and i3, then we may need to move a link from place to i2. */ - if (i2 && INSN_UID (place) <= max_uid_cuid - && INSN_CUID (place) > INSN_CUID (i2) + if (i2 && DF_INSN_LUID (place) > DF_INSN_LUID (i2) && from_insn - && INSN_CUID (from_insn) > INSN_CUID (i2) + && DF_INSN_LUID (from_insn) > DF_INSN_LUID (i2) && reg_referenced_p (XEXP (note, 0), PATTERN (i2))) { rtx links = LOG_LINKS (place); @@ -12277,15 +12594,6 @@ distribute_notes (rtx notes, rtx from_insn, rtx i3, rtx i2, rtx elim_i2, break; } - /* We haven't found an insn for the death note and it - is still a REG_DEAD note, but we have hit the beginning - of the block. If the existing life info says the reg - was dead, there's nothing left to do. Otherwise, we'll - need to do a global life update after combine. */ - if (REG_NOTE_KIND (note) == REG_DEAD && place == 0 - && REGNO_REG_SET_P (bb->il.rtl->global_live_at_start, - REGNO (XEXP (note, 0)))) - SET_BIT (refresh_blocks, this_basic_block->index); } /* If the register is set or already dead at PLACE, we needn't do @@ -12298,11 +12606,6 @@ distribute_notes (rtx notes, rtx from_insn, rtx i3, rtx i2, rtx elim_i2, { unsigned int regno = REGNO (XEXP (note, 0)); - /* Similarly, if the instruction on which we want to place - the note is a noop, we'll need do a global live update - after we remove them in delete_noop_moves. */ - if (noop_move_p (place)) - SET_BIT (refresh_blocks, this_basic_block->index); if (dead_or_set_p (place, XEXP (note, 0)) || reg_bitfield_target_p (XEXP (note, 0), PATTERN (place))) @@ -12369,11 +12672,7 @@ distribute_notes (rtx notes, rtx from_insn, rtx i3, rtx i2, rtx elim_i2, if (! INSN_P (tem)) { if (tem == BB_HEAD (bb)) - { - SET_BIT (refresh_blocks, - this_basic_block->index); - break; - } + break; continue; } if (dead_or_set_p (tem, piece) @@ -12406,23 +12705,11 @@ distribute_notes (rtx notes, rtx from_insn, rtx i3, rtx i2, rtx elim_i2, XEXP (note, 1) = REG_NOTES (place); REG_NOTES (place) = note; } - else if ((REG_NOTE_KIND (note) == REG_DEAD - || REG_NOTE_KIND (note) == REG_UNUSED) - && REG_P (XEXP (note, 0))) - REG_N_DEATHS (REGNO (XEXP (note, 0)))--; if (place2) - { - if ((REG_NOTE_KIND (note) == REG_DEAD - || REG_NOTE_KIND (note) == REG_UNUSED) - && REG_P (XEXP (note, 0))) - REG_N_DEATHS (REGNO (XEXP (note, 0)))++; - - REG_NOTES (place2) = gen_rtx_fmt_ee (GET_CODE (note), - REG_NOTE_KIND (note), - XEXP (note, 0), - REG_NOTES (place2)); - } + REG_NOTES (place2) + = gen_rtx_fmt_ee (GET_CODE (note), REG_NOTE_KIND (note), + XEXP (note, 0), REG_NOTES (place2)); } } @@ -12510,7 +12797,7 @@ distribute_links (rtx links) /* Set added_links_insn to the earliest insn we added a link to. */ if (added_links_insn == 0 - || INSN_CUID (added_links_insn) > INSN_CUID (place)) + || DF_INSN_LUID (added_links_insn) > DF_INSN_LUID (place)) added_links_insn = place; } } @@ -12544,20 +12831,6 @@ unmentioned_reg_p (rtx equiv, rtx expr) return for_each_rtx (&equiv, unmentioned_reg_p_1, expr); } -/* Compute INSN_CUID for INSN, which is an insn made by combine. */ - -static int -insn_cuid (rtx insn) -{ - while (insn != 0 && INSN_UID (insn) > max_uid_cuid - && NONJUMP_INSN_P (insn) && GET_CODE (PATTERN (insn)) == USE) - insn = NEXT_INSN (insn); - - gcc_assert (INSN_UID (insn) <= max_uid_cuid); - - return INSN_CUID (insn); -} - void dump_combine_stats (FILE *file) { @@ -12576,7 +12849,6 @@ dump_combine_total_stats (FILE *file) total_attempts, total_merges, total_extras, total_successes); } - static bool gate_handle_combine (void) { @@ -12587,7 +12859,15 @@ gate_handle_combine (void) static unsigned int rest_of_handle_combine (void) { - int rebuild_jump_labels_after_combine + int rebuild_jump_labels_after_combine; + + df_set_flags (DF_LR_RUN_DCE + DF_DEFER_INSN_RESCAN); + df_note_add_problem (); + df_analyze (); + + regstat_init_n_sets_and_refs (); + + rebuild_jump_labels_after_combine = combine_instructions (get_insns (), max_reg_num ()); /* Combining insns may have turned an indirect jump into a @@ -12597,11 +12877,11 @@ rest_of_handle_combine (void) { timevar_push (TV_JUMP); rebuild_jump_labels (get_insns ()); + cleanup_cfg (0); timevar_pop (TV_JUMP); - - delete_dead_jumptables (); - cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_UPDATE_LIFE); } + + regstat_free_n_sets_and_refs (); return 0; } @@ -12619,6 +12899,7 @@ struct tree_opt_pass pass_combine = 0, /* properties_destroyed */ 0, /* todo_flags_start */ TODO_dump_func | + TODO_df_finish | TODO_ggc_collect, /* todo_flags_finish */ 'c' /* letter */ }; diff --git a/gcc/common.opt b/gcc/common.opt index 6299573..726c350 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -313,6 +313,10 @@ fasynchronous-unwind-tables Common Report Var(flag_asynchronous_unwind_tables) Optimization Generate unwind tables that are exact at each instruction boundary +fauto-inc-dec +Common Report Var(flag_auto_inc_dec) Init(1) +Generate auto-inc/dec instructions + ; -fcheck-bounds causes gcc to generate array bounds checks. ; For C, C++ and ObjC: defaults off. ; For Java: defaults to on. @@ -388,6 +392,10 @@ fdata-sections Common Report Var(flag_data_sections) Optimization Place data items into their own section +fdbg-cnt= +Common RejectNegative Joined +-fdbg-cnt=<counter>:<limit> Set the debug counter limit. + ; Nonzero for -fdefer-pop: don't pop args after each function call ; instead save them up to pop many calls' args with one insns. fdefer-pop @@ -656,6 +664,14 @@ fmudflapir Common RejectNegative Report Var(flag_mudflap_ignore_reads) Ignore read operations when inserting mudflap instrumentation +fdce +Common Var(flag_dce) Init(1) +Use the RTL dead code elimination pass + +fdse +Common Var(flag_dse) Init(1) +Use the RTL dead store elimination pass + freschedule-modulo-scheduled-loops Common Report Var(flag_resched_modulo_sched) Optimization Enable/Disable the traditional scheduling in loops that already passed modulo scheduling diff --git a/gcc/config/alpha/alpha.c b/gcc/config/alpha/alpha.c index bc2e804..14d2247 100644 --- a/gcc/config/alpha/alpha.c +++ b/gcc/config/alpha/alpha.c @@ -56,7 +56,7 @@ Boston, MA 02110-1301, USA. */ #include "tree-flow.h" #include "tree-stdarg.h" #include "tm-constrs.h" - +#include "df.h" /* Specify which cpu to schedule for. */ enum processor_type alpha_tune; @@ -4795,7 +4795,7 @@ alpha_ra_ever_killed (void) rtx top; if (!has_hard_reg_initial_val (Pmode, REG_RA)) - return regs_ever_live[REG_RA]; + return (int)df_regs_ever_live_p (REG_RA); push_topmost_sequence (); top = get_insns (); @@ -7091,7 +7091,7 @@ alpha_sa_mask (unsigned long *imaskP, unsigned long *fmaskP) /* One for every register we have to save. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (! fixed_regs[i] && ! call_used_regs[i] - && regs_ever_live[i] && i != REG_RA + && df_regs_ever_live_p (i) && i != REG_RA && (!TARGET_ABI_UNICOSMK || i != HARD_FRAME_POINTER_REGNUM)) { if (i < 32) @@ -7199,7 +7199,7 @@ alpha_sa_size (void) vms_save_fp_regno = -1; if (vms_base_regno == HARD_FRAME_POINTER_REGNUM) for (i = 0; i < 32; i++) - if (! fixed_regs[i] && call_used_regs[i] && ! regs_ever_live[i]) + if (! fixed_regs[i] && call_used_regs[i] && ! df_regs_ever_live_p (i)) vms_save_fp_regno = i; if (vms_save_fp_regno == -1 && alpha_procedure_type == PT_REGISTER) diff --git a/gcc/config/arc/arc.c b/gcc/config/arc/arc.c index 919f03b..4924cea 100644 --- a/gcc/config/arc/arc.c +++ b/gcc/config/arc/arc.c @@ -1,7 +1,6 @@ /* Subroutines used for code generation on the Argonaut ARC cpu. - Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, - 2005 - Free Software Foundation, Inc. + Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003, + 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -1057,9 +1056,9 @@ arc_compute_function_type (tree decl) Don't consider them here. */ #define MUST_SAVE_REGISTER(regno, interrupt_p) \ ((regno) != RETURN_ADDR_REGNUM && (regno) != FRAME_POINTER_REGNUM \ - && (regs_ever_live[regno] && (!call_used_regs[regno] || interrupt_p))) + && (df_regs_ever_live_p (regno) && (!call_used_regs[regno] || interrupt_p))) -#define MUST_SAVE_RETURN_ADDR (regs_ever_live[RETURN_ADDR_REGNUM]) +#define MUST_SAVE_RETURN_ADDR (df_regs_ever_live_p (RETURN_ADDR_REGNUM)) /* Return the bytes needed to compute the frame pointer from the current stack pointer. diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index cd287bf..e316f6a 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -52,6 +52,7 @@ #include "target-def.h" #include "debug.h" #include "langhooks.h" +#include "df.h" /* Forward definitions of types. */ typedef struct minipool_node Mnode; @@ -1641,7 +1642,7 @@ use_return_insn (int iscond, rtx sibling) if (flag_pic && arm_pic_register != INVALID_REGNUM - && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) + && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM)) return 0; } @@ -1654,18 +1655,18 @@ use_return_insn (int iscond, rtx sibling) since this also requires an insn. */ if (TARGET_HARD_FLOAT && TARGET_FPA) for (regno = FIRST_FPA_REGNUM; regno <= LAST_FPA_REGNUM; regno++) - if (regs_ever_live[regno] && !call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && !call_used_regs[regno]) return 0; /* Likewise VFP regs. */ if (TARGET_HARD_FLOAT && TARGET_VFP) for (regno = FIRST_VFP_REGNUM; regno <= LAST_VFP_REGNUM; regno++) - if (regs_ever_live[regno] && !call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && !call_used_regs[regno]) return 0; if (TARGET_REALLY_IWMMXT) for (regno = FIRST_IWMMXT_REGNUM; regno <= LAST_IWMMXT_REGNUM; regno++) - if (regs_ever_live[regno] && ! call_used_regs [regno]) + if (df_regs_ever_live_p (regno) && ! call_used_regs[regno]) return 0; return 1; @@ -3460,7 +3461,7 @@ thumb_find_work_register (unsigned long pushed_regs_mask) register allocation order means that sometimes r3 might be used but earlier argument registers might not, so check them all. */ for (reg = LAST_ARG_REGNUM; reg >= 0; reg --) - if (!regs_ever_live[reg]) + if (!df_regs_ever_live_p (reg)) return reg; /* Before going on to check the call-saved registers we can try a couple @@ -9770,8 +9771,8 @@ arm_compute_save_reg0_reg12_mask (void) max_reg = 12; for (reg = 0; reg <= max_reg; reg++) - if (regs_ever_live[reg] - || (! current_function_is_leaf && call_used_regs [reg])) + if (df_regs_ever_live_p (reg) + || (! current_function_is_leaf && call_used_regs[reg])) save_reg_mask |= (1 << reg); /* Also save the pic base register if necessary. */ @@ -9789,15 +9790,18 @@ arm_compute_save_reg0_reg12_mask (void) /* In the normal case we only need to save those registers which are call saved and which are used by this function. */ for (reg = 0; reg <= last_reg; reg++) - if (regs_ever_live[reg] && ! call_used_regs [reg]) + if (df_regs_ever_live_p (reg) && ! call_used_regs[reg]) save_reg_mask |= (1 << reg); /* Handle the frame pointer as a special case. */ - if (TARGET_THUMB2 && frame_pointer_needed) + if (! TARGET_APCS_FRAME + && ! frame_pointer_needed + && df_regs_ever_live_p (HARD_FRAME_POINTER_REGNUM) + && ! call_used_regs[HARD_FRAME_POINTER_REGNUM]) save_reg_mask |= 1 << HARD_FRAME_POINTER_REGNUM; else if (! TARGET_APCS_FRAME && ! frame_pointer_needed - && regs_ever_live[HARD_FRAME_POINTER_REGNUM] + && df_regs_ever_live_p (HARD_FRAME_POINTER_REGNUM) && ! call_used_regs[HARD_FRAME_POINTER_REGNUM]) save_reg_mask |= 1 << HARD_FRAME_POINTER_REGNUM; @@ -9806,7 +9810,7 @@ arm_compute_save_reg0_reg12_mask (void) if (flag_pic && !TARGET_SINGLE_PIC_BASE && arm_pic_register != INVALID_REGNUM - && (regs_ever_live[PIC_OFFSET_TABLE_REGNUM] + && (df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM) || current_function_uses_pic_offset_table)) save_reg_mask |= 1 << PIC_OFFSET_TABLE_REGNUM; @@ -9872,11 +9876,11 @@ arm_compute_save_reg_mask (void) now and then popping it back into the PC. This incurs extra memory accesses though, so we only do it when optimizing for size, and only if we know that we will not need a fancy return sequence. */ - if (regs_ever_live [LR_REGNUM] - || (save_reg_mask - && optimize_size - && ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL - && !current_function_calls_eh_return)) + if (df_regs_ever_live_p (LR_REGNUM) + || (save_reg_mask + && optimize_size + && ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL + && !current_function_calls_eh_return)) save_reg_mask |= 1 << LR_REGNUM; if (cfun->machine->lr_save_eliminated) @@ -9932,7 +9936,7 @@ thumb1_compute_save_reg_mask (void) mask = 0; for (reg = 0; reg < 12; reg ++) - if (regs_ever_live[reg] && !call_used_regs[reg]) + if (df_regs_ever_live_p (reg) && !call_used_regs[reg]) mask |= 1 << reg; if (flag_pic @@ -9986,8 +9990,8 @@ arm_get_vfp_saved_size (void) regno < LAST_VFP_REGNUM; regno += 2) { - if ((!regs_ever_live[regno] || call_used_regs[regno]) - && (!regs_ever_live[regno + 1] || call_used_regs[regno + 1])) + if ((!df_regs_ever_live_p (regno) || call_used_regs[regno]) + && (!df_regs_ever_live_p (regno + 1) || call_used_regs[regno + 1])) { if (count > 0) { @@ -10389,7 +10393,7 @@ arm_output_epilogue (rtx sibling) if (arm_fpu_arch == FPUTYPE_FPA_EMU2) { for (reg = LAST_FPA_REGNUM; reg >= FIRST_FPA_REGNUM; reg--) - if (regs_ever_live[reg] && !call_used_regs[reg]) + if (df_regs_ever_live_p (reg) && !call_used_regs[reg]) { floats_offset += 12; asm_fprintf (f, "\tldfe\t%r, [%r, #-%d]\n", @@ -10402,7 +10406,7 @@ arm_output_epilogue (rtx sibling) for (reg = LAST_FPA_REGNUM; reg >= FIRST_FPA_REGNUM; reg--) { - if (regs_ever_live[reg] && !call_used_regs[reg]) + if (df_regs_ever_live_p (reg) && !call_used_regs[reg]) { floats_offset += 12; @@ -10448,8 +10452,8 @@ arm_output_epilogue (rtx sibling) start_reg = FIRST_VFP_REGNUM; for (reg = FIRST_VFP_REGNUM; reg < LAST_VFP_REGNUM; reg += 2) { - if ((!regs_ever_live[reg] || call_used_regs[reg]) - && (!regs_ever_live[reg + 1] || call_used_regs[reg + 1])) + if ((!df_regs_ever_live_p (reg) || call_used_regs[reg]) + && (!df_regs_ever_live_p (reg + 1) || call_used_regs[reg + 1])) { if (start_reg != reg) vfp_output_fldmd (f, IP_REGNUM, @@ -10476,7 +10480,7 @@ arm_output_epilogue (rtx sibling) lrm_count += (lrm_count % 2 ? 2 : 1); for (reg = LAST_IWMMXT_REGNUM; reg >= FIRST_IWMMXT_REGNUM; reg--) - if (regs_ever_live[reg] && !call_used_regs[reg]) + if (df_regs_ever_live_p (reg) && !call_used_regs[reg]) { asm_fprintf (f, "\twldrd\t%r, [%r, #-%d]\n", reg, FP_REGNUM, lrm_count * 4); @@ -10557,7 +10561,7 @@ arm_output_epilogue (rtx sibling) if (arm_fpu_arch == FPUTYPE_FPA_EMU2) { for (reg = FIRST_FPA_REGNUM; reg <= LAST_FPA_REGNUM; reg++) - if (regs_ever_live[reg] && !call_used_regs[reg]) + if (df_regs_ever_live_p (reg) && !call_used_regs[reg]) asm_fprintf (f, "\tldfe\t%r, [%r], #12\n", reg, SP_REGNUM); } @@ -10567,7 +10571,7 @@ arm_output_epilogue (rtx sibling) for (reg = FIRST_FPA_REGNUM; reg <= LAST_FPA_REGNUM; reg++) { - if (regs_ever_live[reg] && !call_used_regs[reg]) + if (df_regs_ever_live_p (reg) && !call_used_regs[reg]) { if (reg - start_reg == 3) { @@ -10598,8 +10602,8 @@ arm_output_epilogue (rtx sibling) start_reg = FIRST_VFP_REGNUM; for (reg = FIRST_VFP_REGNUM; reg < LAST_VFP_REGNUM; reg += 2) { - if ((!regs_ever_live[reg] || call_used_regs[reg]) - && (!regs_ever_live[reg + 1] || call_used_regs[reg + 1])) + if ((!df_regs_ever_live_p (reg) || call_used_regs[reg]) + && (!df_regs_ever_live_p (reg + 1) || call_used_regs[reg + 1])) { if (start_reg != reg) vfp_output_fldmd (f, SP_REGNUM, @@ -10615,7 +10619,7 @@ arm_output_epilogue (rtx sibling) } if (TARGET_IWMMXT) for (reg = FIRST_IWMMXT_REGNUM; reg <= LAST_IWMMXT_REGNUM; reg++) - if (regs_ever_live[reg] && !call_used_regs[reg]) + if (df_regs_ever_live_p (reg) && !call_used_regs[reg]) asm_fprintf (f, "\twldrd\t%r, [%r], #8\n", reg, SP_REGNUM); /* If we can, restore the LR into the PC. */ @@ -10957,7 +10961,7 @@ thumb_force_lr_save (void) return !cfun->machine->lr_save_eliminated && (!leaf_function_p () || thumb_far_jump_used_p () - || regs_ever_live [LR_REGNUM]); + || df_regs_ever_live_p (LR_REGNUM)); } @@ -11065,7 +11069,7 @@ arm_get_frame_offsets (void) for (regno = FIRST_IWMMXT_REGNUM; regno <= LAST_IWMMXT_REGNUM; regno++) - if (regs_ever_live [regno] && ! call_used_regs [regno]) + if (df_regs_ever_live_p (regno) && ! call_used_regs[regno]) saved += 8; } @@ -11074,7 +11078,7 @@ arm_get_frame_offsets (void) { /* Space for saved FPA registers. */ for (regno = FIRST_FPA_REGNUM; regno <= LAST_FPA_REGNUM; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && ! call_used_regs[regno]) saved += 12; /* Space for saved VFP registers. */ @@ -11213,7 +11217,7 @@ arm_save_coproc_regs(void) rtx insn; for (reg = LAST_IWMMXT_REGNUM; reg >= FIRST_IWMMXT_REGNUM; reg--) - if (regs_ever_live[reg] && ! call_used_regs [reg]) + if (df_regs_ever_live_p (reg) && ! call_used_regs[reg]) { insn = gen_rtx_PRE_DEC (V2SImode, stack_pointer_rtx); insn = gen_rtx_MEM (V2SImode, insn); @@ -11227,7 +11231,7 @@ arm_save_coproc_regs(void) if (arm_fpu_arch == FPUTYPE_FPA_EMU2) { for (reg = LAST_FPA_REGNUM; reg >= FIRST_FPA_REGNUM; reg--) - if (regs_ever_live[reg] && !call_used_regs[reg]) + if (df_regs_ever_live_p (reg) && !call_used_regs[reg]) { insn = gen_rtx_PRE_DEC (XFmode, stack_pointer_rtx); insn = gen_rtx_MEM (XFmode, insn); @@ -11242,7 +11246,7 @@ arm_save_coproc_regs(void) for (reg = LAST_FPA_REGNUM; reg >= FIRST_FPA_REGNUM; reg--) { - if (regs_ever_live[reg] && !call_used_regs[reg]) + if (df_regs_ever_live_p (reg) && !call_used_regs[reg]) { if (start_reg - reg == 3) { @@ -11277,8 +11281,8 @@ arm_save_coproc_regs(void) for (reg = FIRST_VFP_REGNUM; reg < LAST_VFP_REGNUM; reg += 2) { - if ((!regs_ever_live[reg] || call_used_regs[reg]) - && (!regs_ever_live[reg + 1] || call_used_regs[reg + 1])) + if ((!df_regs_ever_live_p (reg) || call_used_regs[reg]) + && (!df_regs_ever_live_p (reg + 1) || call_used_regs[reg + 1])) { if (start_reg != reg) saved_size += vfp_emit_fstmd (start_reg, @@ -11423,7 +11427,7 @@ arm_expand_prologue (void) doesn't need to be unwound, as it doesn't contain a value inherited from the caller. */ - if (regs_ever_live[3] == 0) + if (df_regs_ever_live_p (3) == false) insn = emit_set_insn (gen_rtx_REG (SImode, 3), ip_rtx); else if (args_to_push == 0) { @@ -11517,7 +11521,7 @@ arm_expand_prologue (void) if (IS_NESTED (func_type)) { /* Recover the static chain register. */ - if (regs_ever_live [3] == 0 + if (!df_regs_ever_live_p (3) || saved_pretend_args) insn = gen_rtx_REG (SImode, 3); else /* if (current_function_pretend_args_size == 0) */ @@ -11586,10 +11590,7 @@ arm_expand_prologue (void) /* If the link register is being kept alive, with the return address in it, then make sure that it does not get reused by the ce2 pass. */ if ((live_regs_mask & (1 << LR_REGNUM)) == 0) - { - emit_insn (gen_prologue_use (gen_rtx_REG (SImode, LR_REGNUM))); - cfun->machine->lr_save_eliminated = 1; - } + cfun->machine->lr_save_eliminated = 1; } /* Print condition code to STREAM. Helper function for arm_print_operand. */ @@ -13922,7 +13923,7 @@ thumb_exit (FILE *f, int reg_containing_return_addr) { /* If we can deduce the registers used from the function's return value. This is more reliable that examining - regs_ever_live[] because that will be set if the register is + df_regs_ever_live_p () because that will be set if the register is ever used in the function, not just if the register is used to hold a return value. */ @@ -14203,7 +14204,7 @@ thumb_far_jump_used_p (void) If we need doubleword stack alignment this could affect the other elimination offsets so we can't risk getting it wrong. */ - if (regs_ever_live [ARG_POINTER_REGNUM]) + if (df_regs_ever_live_p (ARG_POINTER_REGNUM)) cfun->machine->arg_pointer_live = 1; else if (!cfun->machine->arg_pointer_live) return 0; @@ -14267,7 +14268,7 @@ thumb_unexpanded_epilogue (void) high_regs_pushed = bit_count (live_regs_mask & 0x0f00); /* If we can deduce the registers used from the function's return value. - This is more reliable that examining regs_ever_live[] because that + This is more reliable that examining df_regs_ever_live_p () because that will be set if the register is ever used in the function, not just if the register is used to hold a return value. */ size = arm_size_return_regs (); @@ -14638,11 +14639,6 @@ thumb1_expand_prologue (void) cfun->machine->lr_save_eliminated = !thumb_force_lr_save (); if (live_regs_mask & 0xff) cfun->machine->lr_save_eliminated = 0; - - /* If the link register is being kept alive, with the return address in it, - then make sure that it does not get reused by the ce2 pass. */ - if (cfun->machine->lr_save_eliminated) - emit_insn (gen_prologue_use (gen_rtx_REG (SImode, LR_REGNUM))); } @@ -14691,10 +14687,10 @@ thumb1_expand_epilogue (void) /* Emit a clobber for each insn that will be restored in the epilogue, so that flow2 will get register lifetimes correct. */ for (regno = 0; regno < 13; regno++) - if (regs_ever_live[regno] && !call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && !call_used_regs[regno]) emit_insn (gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, regno))); - if (! regs_ever_live[LR_REGNUM]) + if (! df_regs_ever_live_p (LR_REGNUM)) emit_insn (gen_rtx_USE (VOIDmode, gen_rtx_REG (SImode, LR_REGNUM))); } diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h index 62d7485..67fa99f 100644 --- a/gcc/config/arm/arm.h +++ b/gcc/config/arm/arm.h @@ -985,7 +985,7 @@ extern int arm_structure_size_boundary; call-clobbered. */ #define HARD_REGNO_RENAME_OK(SRC, DST) \ (! IS_INTERRUPT (cfun->machine->func_type) || \ - regs_ever_live[DST]) + df_regs_ever_live_p (DST)) /* Register and constant classes. */ @@ -1600,7 +1600,7 @@ typedef struct frame. */ #define EXIT_IGNORE_STACK 1 -#define EPILOGUE_USES(REGNO) (reload_completed && (REGNO) == LR_REGNUM) +#define EPILOGUE_USES(REGNO) ((REGNO) == LR_REGNUM) /* Determine if the epilogue should be output as RTL. You should override this if you define FUNCTION_EXTRA_EPILOGUE. */ diff --git a/gcc/config/avr/avr.c b/gcc/config/avr/avr.c index d5930fa..a393cff 100644 --- a/gcc/config/avr/avr.c +++ b/gcc/config/avr/avr.c @@ -460,7 +460,7 @@ avr_regs_to_save (HARD_REG_SET *set) continue; if ((int_or_sig_p && !leaf_func_p && call_used_regs[reg]) - || (regs_ever_live[reg] + || (df_regs_ever_live_p (reg) && (int_or_sig_p || !call_used_regs[reg]) && !(frame_pointer_needed && (reg == REG_Y || reg == (REG_Y+1))))) @@ -517,7 +517,7 @@ sequent_regs_live (void) { if (!call_used_regs[reg]) { - if (regs_ever_live[reg]) + if (df_regs_ever_live_p (reg)) { ++live_seq; ++cur_seq; @@ -529,7 +529,7 @@ sequent_regs_live (void) if (!frame_pointer_needed) { - if (regs_ever_live[REG_Y]) + if (df_regs_ever_live_p (REG_Y)) { ++live_seq; ++cur_seq; @@ -537,7 +537,7 @@ sequent_regs_live (void) else cur_seq = 0; - if (regs_ever_live[REG_Y+1]) + if (df_regs_ever_live_p (REG_Y+1)) { ++live_seq; ++cur_seq; @@ -5815,7 +5815,7 @@ avr_peep2_scratch_safe (rtx scratch) for (reg = first_reg; reg <= last_reg; reg++) { - if (!regs_ever_live[reg]) + if (!df_regs_ever_live_p (reg)) return 0; } } diff --git a/gcc/config/bfin/bfin.c b/gcc/config/bfin/bfin.c index 60fe51d..26fbc6c 100644 --- a/gcc/config/bfin/bfin.c +++ b/gcc/config/bfin/bfin.c @@ -1,5 +1,5 @@ /* The Blackfin code generation auxiliary output file. - Copyright (C) 2005, 2006 Free Software Foundation, Inc. + Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Analog Devices. This file is part of GCC. @@ -250,7 +250,7 @@ n_dregs_to_save (bool is_inthandler) for (i = REG_R0; i <= REG_R7; i++) { - if (regs_ever_live[i] && (is_inthandler || ! call_used_regs[i])) + if (df_regs_ever_live_p (i) && (is_inthandler || ! call_used_regs[i])) return REG_R7 - i + 1; if (current_function_calls_eh_return) @@ -278,7 +278,7 @@ n_pregs_to_save (bool is_inthandler) unsigned i; for (i = REG_P0; i <= REG_P5; i++) - if ((regs_ever_live[i] && (is_inthandler || ! call_used_regs[i])) + if ((df_regs_ever_live_p (i) && (is_inthandler || ! call_used_regs[i])) || (!TARGET_FDPIC && i == PIC_OFFSET_TABLE_REGNUM && (current_function_uses_pic_offset_table @@ -292,7 +292,7 @@ n_pregs_to_save (bool is_inthandler) static bool must_save_fp_p (void) { - return frame_pointer_needed || regs_ever_live[REG_FP]; + return frame_pointer_needed || df_regs_ever_live_p (REG_FP); } static bool @@ -513,7 +513,7 @@ n_regs_saved_by_prologue (void) for (i = REG_P7 + 1; i < REG_CC; i++) if (all - || regs_ever_live[i] + || df_regs_ever_live_p (i) || (!leaf_function_p () && call_used_regs[i])) n += i == REG_A0 || i == REG_A1 ? 2 : 1; } @@ -815,7 +815,7 @@ expand_interrupt_handler_prologue (rtx spreg, e_funkind fkind) for (i = REG_P7 + 1; i < REG_CC; i++) if (all - || regs_ever_live[i] + || df_regs_ever_live_p (i) || (!leaf_function_p () && call_used_regs[i])) { if (i == REG_A0 || i == REG_A1) @@ -845,23 +845,11 @@ expand_interrupt_handler_prologue (rtx spreg, e_funkind fkind) rtx insn; insn = emit_move_insn (r0reg, gen_rtx_REG (SImode, REG_SEQSTAT)); - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, - NULL_RTX); insn = emit_insn (gen_ashrsi3 (r0reg, r0reg, GEN_INT (26))); - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, - NULL_RTX); insn = emit_insn (gen_ashlsi3 (r0reg, r0reg, GEN_INT (26))); - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, - NULL_RTX); insn = emit_move_insn (r1reg, spreg); - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, - NULL_RTX); insn = emit_move_insn (r2reg, gen_rtx_REG (Pmode, REG_FP)); - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, - NULL_RTX); insn = emit_insn (gen_addsi3 (r2reg, r2reg, GEN_INT (8))); - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, - NULL_RTX); } } @@ -899,7 +887,7 @@ expand_interrupt_handler_epilogue (rtx spreg, e_funkind fkind) for (i = REG_CC - 1; i > REG_P7; i--) if (all - || regs_ever_live[i] + || df_regs_ever_live_p (i) || (!leaf_function_p () && call_used_regs[i])) { if (i == REG_A0 || i == REG_A1) @@ -948,7 +936,6 @@ bfin_load_pic_reg (rtx dest) gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_LIBRARY_OFFSET)); insn = emit_insn (gen_movsi (dest, gen_rtx_MEM (Pmode, addr))); - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, NULL); return dest; } @@ -1068,7 +1055,7 @@ bfin_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED, call-clobbered. */ if (funkind (TREE_TYPE (current_function_decl)) != SUBROUTINE - && !regs_ever_live[new_reg]) + && !df_regs_ever_live_p (new_reg)) return 0; return 1; diff --git a/gcc/config/c4x/c4x.c b/gcc/config/c4x/c4x.c index 0f42acc..2365ebc 100644 --- a/gcc/config/c4x/c4x.c +++ b/gcc/config/c4x/c4x.c @@ -1,6 +1,6 @@ /* Subroutines for assembler code output on the TMS320C[34]x Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2003, - 2004, 2005 + 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz) @@ -750,13 +750,13 @@ c4x_isr_reg_used_p (unsigned int regno) /* Only save/restore regs in leaf function that are used. */ if (c4x_leaf_function) - return regs_ever_live[regno] && fixed_regs[regno] == 0; + return df_regs_ever_live_p (regno) && fixed_regs[regno] == 0; /* Only save/restore regs that are used by the ISR and regs that are likely to be used by functions the ISR calls if they are not fixed. */ return IS_EXT_REGNO (regno) - || ((regs_ever_live[regno] || call_used_regs[regno]) + || ((df_regs_ever_live_p (regno) || call_used_regs[regno]) && fixed_regs[regno] == 0); } @@ -890,9 +890,9 @@ c4x_expand_prologue (void) /* We need to clear the repeat mode flag if the ISR is going to use a RPTB instruction or uses the RC, RS, or RE registers. */ - if (regs_ever_live[RC_REGNO] - || regs_ever_live[RS_REGNO] - || regs_ever_live[RE_REGNO]) + if (df_regs_ever_live_p (RC_REGNO) + || df_regs_ever_live_p (RS_REGNO) + || df_regs_ever_live_p (RE_REGNO)) { insn = emit_insn (gen_andn_st (GEN_INT(~0x100))); RTX_FRAME_RELATED_P (insn) = 1; @@ -983,7 +983,7 @@ c4x_expand_prologue (void) for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) { - if (regs_ever_live[regno] && ! call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && ! call_used_regs[regno]) { if (IS_FLOAT_CALL_SAVED_REGNO (regno)) { @@ -1111,7 +1111,7 @@ c4x_expand_epilogue(void) where required. */ for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno--) { - if (regs_ever_live[regno] && ! call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && ! call_used_regs[regno]) { if (regno == AR3_REGNO && dont_pop_ar3) continue; @@ -1220,7 +1220,7 @@ c4x_null_epilogue_p (void) && ! get_frame_size ()) { for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno--) - if (regs_ever_live[regno] && ! call_used_regs[regno] + if (df_regs_ever_live_p (regno) && ! call_used_regs[regno] && (regno != AR3_REGNO)) return 1; return 0; diff --git a/gcc/config/c4x/c4x.h b/gcc/config/c4x/c4x.h index 6df7ac8..0a5c713 100644 --- a/gcc/config/c4x/c4x.h +++ b/gcc/config/c4x/c4x.h @@ -1,6 +1,6 @@ /* Definitions of target machine for GNU compiler. TMS320C[34]x Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, - 2003, 2004, 2005 Free Software Foundation, Inc. + 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz) and Herman Ten Brugge (Haj.Ten.Brugge@net.HCC.nl). @@ -887,7 +887,7 @@ enum reg_class int regno; \ int offset = 0; \ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) \ - if (regs_ever_live[regno] && ! call_used_regs[regno]) \ + if (df_regs_ever_live_p (regno) && ! call_used_regs[regno]) \ offset += TARGET_PRESERVE_FLOAT \ && IS_FLOAT_CALL_SAVED_REGNO (regno) ? 2 : 1; \ (DEPTH) = -(offset + get_frame_size ()); \ @@ -906,7 +906,7 @@ enum reg_class int regno; \ int offset = 0; \ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) \ - if (regs_ever_live[regno] && ! call_used_regs[regno]) \ + if (df_regs_ever_live_p (regno) && ! call_used_regs[regno]) \ offset += TARGET_PRESERVE_FLOAT \ && IS_FLOAT_CALL_SAVED_REGNO (regno) ? 2 : 1; \ (OFFSET) = -(offset + get_frame_size ()); \ diff --git a/gcc/config/cris/cris.c b/gcc/config/cris/cris.c index 2158295..66b68ef 100644 --- a/gcc/config/cris/cris.c +++ b/gcc/config/cris/cris.c @@ -1,5 +1,5 @@ /* Definitions for GCC. Part of the machine description for CRIS. - Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Axis Communications. Written by Hans-Peter Nilsson. @@ -578,13 +578,13 @@ static int cris_reg_saved_in_regsave_area (unsigned int regno, bool got_really_used) { return - (((regs_ever_live[regno] + (((df_regs_ever_live_p (regno) && !call_used_regs[regno]) || (regno == PIC_OFFSET_TABLE_REGNUM && (got_really_used /* It is saved anyway, if there would be a gap. */ || (flag_pic - && regs_ever_live[regno + 1] + && df_regs_ever_live_p (regno + 1) && !call_used_regs[regno + 1])))) && (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed) && regno != CRIS_SRP_REGNUM) @@ -1122,7 +1122,7 @@ cris_return_addr_rtx (int count, rtx frameaddr ATTRIBUTE_UNUSED) bool cris_return_address_on_stack (void) { - return regs_ever_live[CRIS_SRP_REGNUM] + return df_regs_ever_live_p (CRIS_SRP_REGNUM) || cfun->machine->needs_return_address_on_stack; } diff --git a/gcc/config/crx/crx.c b/gcc/config/crx/crx.c index 7b8efee..f73348e 100644 --- a/gcc/config/crx/crx.c +++ b/gcc/config/crx/crx.c @@ -1,6 +1,6 @@ /* Output routines for GCC for CRX. Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - 2002, 2003, 2004 Free Software Foundation, Inc. + 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -272,7 +272,7 @@ crx_compute_save_regs (void) * for the sake of its sons. */ save_regs[regno] = 1; - else if (regs_ever_live[regno]) + else if (df_regs_ever_live_p (regno)) /* This reg is used - save it. */ save_regs[regno] = 1; else @@ -282,7 +282,7 @@ crx_compute_save_regs (void) else { /* If this reg is used and not call-used (except RA), save it. */ - if (regs_ever_live[regno] + if (df_regs_ever_live_p (regno) && (!call_used_regs[regno] || regno == RETURN_ADDRESS_REGNUM)) save_regs[regno] = 1; else diff --git a/gcc/config/crx/crx.h b/gcc/config/crx/crx.h index d482bb3..b1dacaa1 100644 --- a/gcc/config/crx/crx.h +++ b/gcc/config/crx/crx.h @@ -1,6 +1,6 @@ /* Definitions of target machine for GNU compiler, for CRX. Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, - 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -165,7 +165,7 @@ do { \ /* Interrupt functions can only use registers that have already been saved by * the prologue, even if they would normally be call-clobbered. */ #define HARD_REGNO_RENAME_OK(SRC, DEST) \ - (!crx_interrupt_function_p () || regs_ever_live[DEST]) + (!crx_interrupt_function_p () || df_regs_ever_live_p (DEST)) #define MODES_TIEABLE_P(MODE1, MODE2) 1 diff --git a/gcc/config/darwin.c b/gcc/config/darwin.c index 17a8d59..fb5d6f9 100644 --- a/gcc/config/darwin.c +++ b/gcc/config/darwin.c @@ -1,6 +1,6 @@ /* Functions for generic Darwin as target machine for GNU C compiler. Copyright (C) 1989, 1990, 1991, 1992, 1993, 2000, 2001, 2002, 2003, 2004, - 2005 + 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Apple Computer Inc. @@ -45,6 +45,7 @@ Boston, MA 02110-1301, USA. */ #include "tm_p.h" #include "toplev.h" #include "hashtab.h" +#include "df.h" /* Darwin supports a feature called fix-and-continue, which is used for rapid turn around debugging. When code is compiled with the @@ -777,7 +778,7 @@ machopic_legitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg) #endif if (reload_in_progress) - regs_ever_live[REGNO (pic)] = 1; + df_set_regs_ever_live (REGNO (pic), true); pic_ref = gen_rtx_PLUS (Pmode, pic, gen_pic_offset (XEXP (orig, 0), pic_base)); @@ -848,7 +849,7 @@ machopic_legitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg) pic_offset_table_rtx)); #endif if (reload_in_progress) - regs_ever_live[REGNO (pic)] = 1; + df_set_regs_ever_live (REGNO (pic), true); pic_ref = gen_rtx_PLUS (Pmode, pic, gen_pic_offset (orig, pic_base)); diff --git a/gcc/config/fr30/fr30.c b/gcc/config/fr30/fr30.c index 9d3558d..1d8a004 100644 --- a/gcc/config/fr30/fr30.c +++ b/gcc/config/fr30/fr30.c @@ -137,11 +137,11 @@ static int fr30_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode, #define MUST_SAVE_REGISTER(regno) \ ( (regno) != RETURN_POINTER_REGNUM \ && (regno) != FRAME_POINTER_REGNUM \ - && regs_ever_live [regno] \ + && df_regs_ever_live_p (regno) \ && ! call_used_regs [regno] ) -#define MUST_SAVE_FRAME_POINTER (regs_ever_live [FRAME_POINTER_REGNUM] || frame_pointer_needed) -#define MUST_SAVE_RETURN_POINTER (regs_ever_live [RETURN_POINTER_REGNUM] || current_function_profile) +#define MUST_SAVE_FRAME_POINTER (df_regs_ever_live_p (FRAME_POINTER_REGNUM) || frame_pointer_needed) +#define MUST_SAVE_RETURN_POINTER (df_regs_ever_live_p (RETURN_POINTER_REGNUM) || current_function_profile) #if UNITS_PER_WORD == 4 #define WORD_ALIGN(SIZE) (((SIZE) + 3) & ~3) diff --git a/gcc/config/frv/frv.c b/gcc/config/frv/frv.c index c2b661d..5c2489a 100644 --- a/gcc/config/frv/frv.c +++ b/gcc/config/frv/frv.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005 +/* Copyright (C) 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Red Hat, Inc. @@ -51,6 +51,7 @@ Boston, MA 02110-1301, USA. */ #include "targhooks.h" #include "integrate.h" #include "langhooks.h" +#include "df.h" #ifndef FRV_INLINE #define FRV_INLINE inline @@ -1167,7 +1168,7 @@ frv_stack_info (void) default: for (regno = first; regno <= last; regno++) { - if ((regs_ever_live[regno] && !call_used_regs[regno]) + if ((df_regs_ever_live_p (regno) && !call_used_regs[regno]) || (current_function_calls_eh_return && (regno >= FIRST_EH_REGNUM && regno <= LAST_EH_REGNUM)) || (!TARGET_FDPIC && flag_pic @@ -1185,7 +1186,7 @@ frv_stack_info (void) break; case STACK_REGS_LR: - if (regs_ever_live[LR_REGNO] + if (df_regs_ever_live_p (LR_REGNO) || profile_flag /* This is set for __builtin_return_address, etc. */ || cfun->machine->frame_needed @@ -1498,7 +1499,7 @@ frv_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) rtx insn; /* Just to check that the above comment is true. */ - gcc_assert (!regs_ever_live[GPR_FIRST + 3]); + gcc_assert (!df_regs_ever_live_p (GPR_FIRST + 3)); /* Generate the instruction that saves the link register. */ fprintf (file, "\tmovsg lr,gr3\n"); @@ -1518,7 +1519,7 @@ frv_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) { rtx address = XEXP (XVECEXP (pattern, 0, 1), 0); if (GET_CODE (address) == REG && REGNO (address) == LR_REGNO) - REGNO (address) = GPR_FIRST + 3; + SET_REGNO (address, GPR_FIRST + 3); } } } @@ -5293,15 +5294,15 @@ frv_ifcvt_modify_tests (ce_if_block_t *ce_info, rtx *p_true, rtx *p_false) for (j = CC_FIRST; j <= CC_LAST; j++) if (TEST_HARD_REG_BIT (tmp_reg->regs, j)) { - if (REGNO_REG_SET_P (then_bb->il.rtl->global_live_at_start, j)) + if (REGNO_REG_SET_P (DF_LIVE_IN (rtl_df, then_bb), j)) continue; if (else_bb - && REGNO_REG_SET_P (else_bb->il.rtl->global_live_at_start, j)) + && REGNO_REG_SET_P (DF_LIVE_IN (rtl_df, else_bb), j)) continue; if (join_bb - && REGNO_REG_SET_P (join_bb->il.rtl->global_live_at_start, j)) + && REGNO_REG_SET_P (DF_LIVE_IN (rtl_df, join_bb), j)) continue; SET_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite, j); @@ -5323,7 +5324,7 @@ frv_ifcvt_modify_tests (ce_if_block_t *ce_info, rtx *p_true, rtx *p_false) /* Remove anything live at the beginning of the join block from being available for allocation. */ - EXECUTE_IF_SET_IN_REG_SET (join_bb->il.rtl->global_live_at_start, 0, regno, rsi) + EXECUTE_IF_SET_IN_REG_SET (DF_LIVE_IN (rtl_df, join_bb), 0, regno, rsi) { if (regno < FIRST_PSEUDO_REGISTER) CLEAR_HARD_REG_BIT (tmp_reg->regs, regno); @@ -5367,7 +5368,7 @@ frv_ifcvt_modify_tests (ce_if_block_t *ce_info, rtx *p_true, rtx *p_false) /* Anything live at the beginning of the block is obviously unavailable for allocation. */ - EXECUTE_IF_SET_IN_REG_SET (bb[j]->il.rtl->global_live_at_start, 0, regno, rsi) + EXECUTE_IF_SET_IN_REG_SET (DF_LIVE_IN (rtl_df, bb[j]), 0, regno, rsi) { if (regno < FIRST_PSEUDO_REGISTER) CLEAR_HARD_REG_BIT (tmp_reg->regs, regno); @@ -6021,7 +6022,7 @@ frv_ifcvt_modify_insn (ce_if_block_t *ce_info, severely. */ && ce_info->join_bb && ! (REGNO_REG_SET_P - (ce_info->join_bb->il.rtl->global_live_at_start, + (DF_LIVE_IN (rtl_df, ce_info->join_bb), REGNO (SET_DEST (set)))) /* Similarly, we must not unconditionally set a reg used as scratch in the THEN branch if the same reg @@ -6029,7 +6030,7 @@ frv_ifcvt_modify_insn (ce_if_block_t *ce_info, && (! ce_info->else_bb || BLOCK_FOR_INSN (insn) == ce_info->else_bb || ! (REGNO_REG_SET_P - (ce_info->else_bb->il.rtl->global_live_at_start, + (DF_LIVE_IN (rtl_df, ce_info->else_bb), REGNO (SET_DEST (set)))))) pattern = set; @@ -7641,7 +7642,7 @@ frv_reorder_packet (void) for (from = 0; from < to - 1; from++) { remove_insn (insns[from]); - add_insn_before (insns[from], insns[to - 1]); + add_insn_before (insns[from], insns[to - 1], NULL); SET_PACKING_FLAG (insns[from]); } } @@ -8632,7 +8633,7 @@ frv_int_to_acc (enum insn_code icode, int opnum, rtx opval) reg = gen_rtx_REG (insn_data[icode].operand[opnum].mode, ACC_FIRST + INTVAL (opval)); if (! (*insn_data[icode].operand[opnum].predicate) (reg, VOIDmode)) - REGNO (reg) = ACCG_FIRST + INTVAL (opval); + SET_REGNO (reg, ACCG_FIRST + INTVAL (opval)); if (! (*insn_data[icode].operand[opnum].predicate) (reg, VOIDmode)) { diff --git a/gcc/config/h8300/h8300.c b/gcc/config/h8300/h8300.c index efc32af..ab6d89d 100644 --- a/gcc/config/h8300/h8300.c +++ b/gcc/config/h8300/h8300.c @@ -1,6 +1,6 @@ /* Subroutines for insn-output.c for Renesas H8/300. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, - 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Steve Chamberlain (sac@cygnus.com), Jim Wilson (wilson@cygnus.com), and Doug Evans (dje@cygnus.com). @@ -494,12 +494,12 @@ byte_reg (rtx x, int b) && ! TREE_THIS_VOLATILE (current_function_decl) \ && (h8300_saveall_function_p (current_function_decl) \ /* Save any call saved register that was used. */ \ - || (regs_ever_live[regno] && !call_used_regs[regno]) \ + || (df_regs_ever_live_p (regno) && !call_used_regs[regno]) \ /* Save the frame pointer if it was used. */ \ - || (regno == HARD_FRAME_POINTER_REGNUM && regs_ever_live[regno]) \ + || (regno == HARD_FRAME_POINTER_REGNUM && df_regs_ever_live_p (regno)) \ /* Save any register used in an interrupt handler. */ \ || (h8300_current_function_interrupt_function_p () \ - && regs_ever_live[regno]) \ + && df_regs_ever_live_p (regno)) \ /* Save call clobbered registers in non-leaf interrupt \ handlers. */ \ || (h8300_current_function_interrupt_function_p () \ @@ -5607,7 +5607,7 @@ h8300_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED, call-clobbered. */ if (h8300_current_function_interrupt_function_p () - && !regs_ever_live[new_reg]) + && !df_regs_ever_live_p (new_reg)) return 0; return 1; diff --git a/gcc/config/h8300/h8300.md b/gcc/config/h8300/h8300.md index 8f0083a..91d698f 100644 --- a/gcc/config/h8300/h8300.md +++ b/gcc/config/h8300/h8300.md @@ -1,6 +1,6 @@ ;; GCC machine description for Renesas H8/300 ;; Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, -;; 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. +;; 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. ;; Contributed by Steve Chamberlain (sac@cygnus.com), ;; Jim Wilson (wilson@cygnus.com), and Doug Evans (dje@cygnus.com). @@ -1210,7 +1210,7 @@ [(set (match_operand:HI 0 "stack_pointer_operand" "") (plus:HI (match_dup 0) (match_operand 1 "const_int_gt_2_operand" "")))] - "TARGET_H8300 && flow2_completed" + "TARGET_H8300 && epilogue_completed" [(const_int 0)] "split_adds_subs (HImode, operands); DONE;") @@ -3017,7 +3017,7 @@ [(match_dup 0) (match_operand:QI 1 "register_operand" "")])) (clobber (match_operand:QI 3 "register_operand" ""))] - "flow2_completed + "epilogue_completed && find_regno_note (insn, REG_DEAD, REGNO (operands[1]))" [(set (cc0) (match_dup 1)) @@ -3048,7 +3048,7 @@ [(match_dup 0) (match_operand:QI 1 "register_operand" "")])) (clobber (match_operand:QI 3 "register_operand" ""))] - "flow2_completed + "epilogue_completed && !find_regno_note (insn, REG_DEAD, REGNO (operands[1]))" [(set (match_dup 3) (match_dup 1)) @@ -4183,7 +4183,7 @@ (match_dup 0))) (clobber (match_operand:SI 2 "register_operand" ""))] "(TARGET_H8300H || TARGET_H8300S) - && flow2_completed + && epilogue_completed && find_regno_note (insn, REG_DEAD, REGNO (operands[1])) && REGNO (operands[0]) != REGNO (operands[1])" [(parallel [(set (match_dup 3) @@ -4203,7 +4203,7 @@ (match_dup 0))) (clobber (match_operand:SI 2 "register_operand" ""))] "(TARGET_H8300H || TARGET_H8300S) - && flow2_completed + && epilogue_completed && !(find_regno_note (insn, REG_DEAD, REGNO (operands[1])) && REGNO (operands[0]) != REGNO (operands[1]))" [(set (match_dup 2) @@ -4286,7 +4286,7 @@ (match_dup 0))) (clobber (match_operand:SI 2 "register_operand" ""))] "(TARGET_H8300H || TARGET_H8300S) - && flow2_completed + && epilogue_completed && find_regno_note (insn, REG_DEAD, REGNO (operands[1])) && REGNO (operands[0]) != REGNO (operands[1])" [(parallel [(set (match_dup 3) @@ -4306,7 +4306,7 @@ (match_dup 0))) (clobber (match_operand:SI 2 "register_operand" ""))] "(TARGET_H8300H || TARGET_H8300S) - && flow2_completed + && epilogue_completed && !(find_regno_note (insn, REG_DEAD, REGNO (operands[1])) && REGNO (operands[0]) != REGNO (operands[1]))" [(set (match_dup 2) diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index f8019bd..61a24ee 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -49,6 +49,7 @@ Boston, MA 02110-1301, USA. */ #include "cgraph.h" #include "tree-gimple.h" #include "dwarf2.h" +#include "df.h" #include "tm-constrs.h" #include "params.h" @@ -3014,7 +3015,7 @@ ix86_eax_live_at_start_p (void) to correct at this point. This gives false positives for broken functions that might use uninitialized data that happens to be allocated in eax, but who cares? */ - return REGNO_REG_SET_P (ENTRY_BLOCK_PTR->il.rtl->global_live_at_end, 0); + return REGNO_REG_SET_P (DF_LIVE_OUT (ENTRY_BLOCK_PTR), 0); } /* Return true if TYPE has a variable argument list. */ @@ -5580,7 +5581,7 @@ ix86_select_alt_pic_regnum (void) { int i; for (i = 2; i >= 0; --i) - if (!regs_ever_live[i]) + if (!df_regs_ever_live_p (i)) return i; } @@ -5593,7 +5594,7 @@ ix86_save_reg (unsigned int regno, int maybe_eh_return) { if (pic_offset_table_rtx && regno == REAL_PIC_OFFSET_TABLE_REGNUM - && (regs_ever_live[REAL_PIC_OFFSET_TABLE_REGNUM] + && (df_regs_ever_live_p (REAL_PIC_OFFSET_TABLE_REGNUM) || current_function_profile || current_function_calls_eh_return || current_function_uses_const_pool)) @@ -5620,7 +5621,7 @@ ix86_save_reg (unsigned int regno, int maybe_eh_return) && regno == REGNO (cfun->machine->force_align_arg_pointer)) return 1; - return (regs_ever_live[regno] + return (df_regs_ever_live_p (regno) && !call_used_regs[regno] && !fixed_regs[regno] && (regno != HARD_FRAME_POINTER_REGNUM || !frame_pointer_needed)); @@ -6106,13 +6107,13 @@ ix86_expand_prologue (void) pic_reg_used = false; if (pic_offset_table_rtx - && (regs_ever_live[REAL_PIC_OFFSET_TABLE_REGNUM] + && (df_regs_ever_live_p (REAL_PIC_OFFSET_TABLE_REGNUM) || current_function_profile)) { unsigned int alt_pic_reg_used = ix86_select_alt_pic_regnum (); if (alt_pic_reg_used != INVALID_REGNUM) - REGNO (pic_offset_table_rtx) = alt_pic_reg_used; + SET_REGNO (pic_offset_table_rtx, alt_pic_reg_used); pic_reg_used = true; } @@ -6130,9 +6131,7 @@ ix86_expand_prologue (void) LABEL_PRESERVE_P (label) = 1; gcc_assert (REGNO (pic_offset_table_rtx) != REGNO (tmp_reg)); insn = emit_insn (gen_set_rip_rex64 (pic_offset_table_rtx, label)); - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, NULL); insn = emit_insn (gen_set_got_offset_rex64 (tmp_reg, label)); - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, NULL); insn = emit_insn (gen_adddi3 (pic_offset_table_rtx, pic_offset_table_rtx, tmp_reg)); } @@ -6141,20 +6140,16 @@ ix86_expand_prologue (void) } else insn = emit_insn (gen_set_got (pic_offset_table_rtx)); - - /* Even with accurate pre-reload life analysis, we can wind up - deleting all references to the pic register after reload. - Consider if cross-jumping unifies two sides of a branch - controlled by a comparison vs the only read from a global. - In which case, allow the set_got to be deleted, though we're - too late to do anything about the ebx save in the prologue. */ - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, NULL); } /* Prevent function calls from be scheduled before the call to mcount. In the pic_reg_used case, make sure that the got load isn't deleted. */ if (current_function_profile) - emit_insn (gen_blockage (pic_reg_used ? pic_offset_table_rtx : const0_rtx)); + { + if (pic_reg_used) + emit_insn (gen_prologue_use (pic_offset_table_rtx)); + emit_insn (gen_blockage ()); + } } /* Emit code to restore saved registers using MOV insns. First register @@ -6366,7 +6361,7 @@ ix86_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED, HOST_WIDE_INT size ATTRIBUTE_UNUSED) { if (pic_offset_table_rtx) - REGNO (pic_offset_table_rtx) = REAL_PIC_OFFSET_TABLE_REGNUM; + SET_REGNO (pic_offset_table_rtx, REAL_PIC_OFFSET_TABLE_REGNUM); #if TARGET_MACHO /* Mach-O doesn't support labels at the end of objects, so if it looks like we might want one, insert a NOP. */ @@ -7193,7 +7188,7 @@ legitimize_pic_address (rtx orig, rtx reg) base address (@GOTOFF). */ if (reload_in_progress) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); if (GET_CODE (addr) == CONST) addr = XEXP (addr, 0); if (GET_CODE (addr) == PLUS) @@ -7225,7 +7220,7 @@ legitimize_pic_address (rtx orig, rtx reg) base address (@GOTOFF). */ if (reload_in_progress) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); if (GET_CODE (addr) == CONST) addr = XEXP (addr, 0); if (GET_CODE (addr) == PLUS) @@ -7276,7 +7271,7 @@ legitimize_pic_address (rtx orig, rtx reg) Global Offset Table (@GOT). */ if (reload_in_progress) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT); new_rtx = gen_rtx_CONST (Pmode, new_rtx); if (TARGET_64BIT) @@ -7329,7 +7324,7 @@ legitimize_pic_address (rtx orig, rtx reg) if (!TARGET_64BIT) { if (reload_in_progress) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op0), UNSPEC_GOTOFF); new_rtx = gen_rtx_PLUS (Pmode, new_rtx, op1); @@ -7489,7 +7484,7 @@ legitimize_tls_address (rtx x, enum tls_model model, int for_mov) else if (flag_pic) { if (reload_in_progress) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); pic = pic_offset_table_rtx; type = TARGET_ANY_GNU_TLS ? UNSPEC_GOTNTPOFF : UNSPEC_GOTTPOFF; } diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index 693f8ee..5089883 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -2431,7 +2431,7 @@ struct machine_function GTY(()) verify whether there's any such instruction live by testing that REG_SP is live. */ #define ix86_current_function_calls_tls_descriptor \ - (ix86_tls_descriptor_calls_expanded_in_cfun && regs_ever_live[SP_REG]) + (ix86_tls_descriptor_calls_expanded_in_cfun && df_regs_ever_live_p (SP_REG)) /* Control behavior of x86_file_start. */ #define X86_FILE_START_VERSION_DIRECTIVE false diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 905e1c1..0ecb996 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -194,6 +194,7 @@ (UNSPECV_CMPXCHG_2 11) (UNSPECV_XCHG 12) (UNSPECV_LOCK 13) + (UNSPECV_PROLOGUE_USE 14) ]) ;; Registers by name. @@ -1917,7 +1918,7 @@ [(set (match_operand:DI 0 "push_operand" "") (match_operand:DI 1 "immediate_operand" ""))] "TARGET_64BIT && ((optimize > 0 && flag_peephole2) - ? flow2_completed : reload_completed) + ? epilogue_completed : reload_completed) && !symbolic_operand (operands[1], DImode) && !x86_64_immediate_operand (operands[1], DImode)" [(set (match_dup 0) (match_dup 1)) @@ -2153,7 +2154,7 @@ [(set (match_operand:DI 0 "memory_operand" "") (match_operand:DI 1 "immediate_operand" ""))] "TARGET_64BIT && ((optimize > 0 && flag_peephole2) - ? flow2_completed : reload_completed) + ? epilogue_completed : reload_completed) && !symbolic_operand (operands[1], DImode) && !x86_64_immediate_operand (operands[1], DImode)" [(set (match_dup 2) (match_dup 3)) @@ -10573,7 +10574,7 @@ (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC FLAGS_REG))] "!TARGET_64BIT && ((optimize > 0 && flag_peephole2) - ? flow2_completed : reload_completed)" + ? epilogue_completed : reload_completed)" [(const_int 0)] "ix86_split_ashl (operands, NULL_RTX, DImode); DONE;") @@ -11504,7 +11505,7 @@ (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC FLAGS_REG))] "!TARGET_64BIT && ((optimize > 0 && flag_peephole2) - ? flow2_completed : reload_completed)" + ? epilogue_completed : reload_completed)" [(const_int 0)] "ix86_split_ashr (operands, NULL_RTX, DImode); DONE;") @@ -12182,7 +12183,7 @@ (match_operand:QI 2 "nonmemory_operand" ""))) (clobber (reg:CC FLAGS_REG))] "!TARGET_64BIT && ((optimize > 0 && flag_peephole2) - ? flow2_completed : reload_completed)" + ? epilogue_completed : reload_completed)" [(const_int 0)] "ix86_split_lshr (operands, NULL_RTX, DImode); DONE;") @@ -14347,7 +14348,7 @@ registers we stored in the result block. We avoid problems by claiming that all hard registers are used and clobbered at this point. */ - emit_insn (gen_blockage (const0_rtx)); + emit_insn (gen_blockage ()); DONE; }) @@ -14358,7 +14359,15 @@ ;; all of memory. This blocks insns from being moved across this point. (define_insn "blockage" - [(unspec_volatile [(match_operand 0 "" "")] UNSPECV_BLOCKAGE)] + [(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)] + "" + "" + [(set_attr "length" "0")]) + +;; As USE insns aren't meaningful after reload, this is used instead +;; to prevent deleting instructions setting registers for PIC code +(define_insn "prologue_use" + [(unspec_volatile [(match_operand 0 "" "")] UNSPECV_PROLOGUE_USE)] "" "" [(set_attr "length" "0")]) diff --git a/gcc/config/ia64/ia64-protos.h b/gcc/config/ia64/ia64-protos.h index 555038b..2be4878 100644 --- a/gcc/config/ia64/ia64-protos.h +++ b/gcc/config/ia64/ia64-protos.h @@ -109,3 +109,4 @@ extern void ia64_output_function_profiler (FILE *, int); extern void ia64_profile_hook (int); extern void ia64_optimization_options (int, int); +extern void ia64_init_expanders (void); diff --git a/gcc/config/ia64/ia64.c b/gcc/config/ia64/ia64.c index 5e1328b..24af522 100644 --- a/gcc/config/ia64/ia64.c +++ b/gcc/config/ia64/ia64.c @@ -53,8 +53,10 @@ Boston, MA 02110-1301, USA. */ #include "cfglayout.h" #include "tree-gimple.h" #include "intl.h" +#include "df.h" #include "debug.h" #include "params.h" +#include "dbgcnt.h" #include "tm-constrs.h" /* This is used for communication between ASM_OUTPUT_LABEL and @@ -122,6 +124,18 @@ unsigned int ia64_section_threshold; TRUE if we do insn bundling instead of insn scheduling. */ int bundling_p = 0; +enum ia64_frame_regs +{ + reg_fp, + reg_save_b0, + reg_save_pr, + reg_save_ar_pfs, + reg_save_ar_unat, + reg_save_ar_lc, + reg_save_gp, + number_of_ia64_frame_regs +}; + /* Structure to be filled in by ia64_compute_frame_size with register save masks and offsets for the current function. */ @@ -136,13 +150,7 @@ struct ia64_frame_info unsigned int gr_used_mask; /* mask of registers in use as gr spill registers or long-term scratches. */ int n_spilled; /* number of spilled registers. */ - int reg_fp; /* register for fp. */ - int reg_save_b0; /* save register for b0. */ - int reg_save_pr; /* save register for prs. */ - int reg_save_ar_pfs; /* save register for ar.pfs. */ - int reg_save_ar_unat; /* save register for ar.unat. */ - int reg_save_ar_lc; /* save register for ar.lc. */ - int reg_save_gp; /* save register for gp. */ + int r[number_of_ia64_frame_regs]; /* Frame related registers. */ int n_input_regs; /* number of input registers used. */ int n_local_regs; /* number of local registers used. */ int n_output_regs; /* number of output registers used. */ @@ -154,6 +162,8 @@ struct ia64_frame_info /* Current frame information calculated by ia64_compute_frame_size. */ static struct ia64_frame_info current_frame_info; +/* The actual registers that are emitted. */ +static int emitted_frame_related_regs[number_of_ia64_frame_regs]; static int ia64_first_cycle_multipass_dfa_lookahead (void); static void ia64_dependencies_evaluation_hook (rtx, rtx); @@ -173,7 +183,7 @@ static int ia64_spec_check_p (rtx); static int ia64_spec_check_src_p (rtx); static rtx gen_tls_get_addr (void); static rtx gen_thread_pointer (void); -static int find_gr_spill (int); +static int find_gr_spill (enum ia64_frame_regs, int); static int next_scratch_gr_reg (void); static void mark_reg_gr_used_mask (rtx, void *); static void ia64_compute_frame_size (HOST_WIDE_INT); @@ -1872,13 +1882,42 @@ ia64_expand_call (rtx retval, rtx addr, rtx nextarg ATTRIBUTE_UNUSED, use_reg (&CALL_INSN_FUNCTION_USAGE (insn), b0); } +static void +reg_emitted (enum ia64_frame_regs r) +{ + if (emitted_frame_related_regs[r] == 0) + emitted_frame_related_regs[r] = current_frame_info.r[r]; + else + gcc_assert (emitted_frame_related_regs[r] == current_frame_info.r[r]); +} + +static int +get_reg (enum ia64_frame_regs r) +{ + reg_emitted (r); + return current_frame_info.r[r]; +} + +static bool +is_emitted (int regno) +{ + enum ia64_frame_regs r; + + for (r = reg_fp; r < number_of_ia64_frame_regs; r++) + if (emitted_frame_related_regs[r] == regno) + return true; + return false; +} + void ia64_reload_gp (void) { rtx tmp; - if (current_frame_info.reg_save_gp) - tmp = gen_rtx_REG (DImode, current_frame_info.reg_save_gp); + if (current_frame_info.r[reg_save_gp]) + { + tmp = gen_rtx_REG (DImode, get_reg (reg_save_gp)); + } else { HOST_WIDE_INT offset; @@ -2158,20 +2197,33 @@ ia64_globalize_decl_name (FILE * stream, tree decl) TRY_LOCALS is true if we should attempt to locate a local regnum. */ static int -find_gr_spill (int try_locals) +find_gr_spill (enum ia64_frame_regs r, int try_locals) { int regno; + if (emitted_frame_related_regs[r] != 0) + { + regno = emitted_frame_related_regs[r]; + if (regno >= LOC_REG (0) && regno < LOC_REG (80 - frame_pointer_needed)) + current_frame_info.n_local_regs = regno - LOC_REG (0) + 1; + else if (current_function_is_leaf + && regno >= GR_REG (1) && regno <= GR_REG (31)) + current_frame_info.gr_used_mask |= 1 << regno; + + return regno; + } + /* If this is a leaf function, first try an otherwise unused call-clobbered register. */ if (current_function_is_leaf) { for (regno = GR_REG (1); regno <= GR_REG (31); regno++) - if (! regs_ever_live[regno] + if (! df_regs_ever_live_p (regno) && call_used_regs[regno] && ! fixed_regs[regno] && ! global_regs[regno] - && ((current_frame_info.gr_used_mask >> regno) & 1) == 0) + && ((current_frame_info.gr_used_mask >> regno) & 1) == 0 + && ! is_emitted (regno)) { current_frame_info.gr_used_mask |= 1 << regno; return regno; @@ -2243,6 +2295,7 @@ mark_reg_gr_used_mask (rtx reg, void *data ATTRIBUTE_UNUSED) } } + /* Returns the number of bytes offset between the frame pointer and the stack pointer for the current function. SIZE is the number of bytes of space needed for local variables. */ @@ -2284,7 +2337,7 @@ ia64_compute_frame_size (HOST_WIDE_INT size) since we'll be adjusting that down later. */ regno = LOC_REG (78) + ! frame_pointer_needed; for (; regno >= LOC_REG (0); regno--) - if (regs_ever_live[regno]) + if (df_regs_ever_live_p (regno) && !is_emitted (regno)) break; current_frame_info.n_local_regs = regno - LOC_REG (0) + 1; @@ -2299,13 +2352,13 @@ ia64_compute_frame_size (HOST_WIDE_INT size) else { for (regno = IN_REG (7); regno >= IN_REG (0); regno--) - if (regs_ever_live[regno]) + if (df_regs_ever_live_p (regno)) break; current_frame_info.n_input_regs = regno - IN_REG (0) + 1; } for (regno = OUT_REG (7); regno >= OUT_REG (0); regno--) - if (regs_ever_live[regno]) + if (df_regs_ever_live_p (regno)) break; i = regno - OUT_REG (0) + 1; @@ -2327,7 +2380,7 @@ ia64_compute_frame_size (HOST_WIDE_INT size) which will always wind up on the stack. */ for (regno = FR_REG (2); regno <= FR_REG (127); regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && ! call_used_regs[regno]) { SET_HARD_REG_BIT (mask, regno); spill_size += 16; @@ -2336,7 +2389,7 @@ ia64_compute_frame_size (HOST_WIDE_INT size) } for (regno = GR_REG (1); regno <= GR_REG (31); regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && ! call_used_regs[regno]) { SET_HARD_REG_BIT (mask, regno); spill_size += 8; @@ -2345,7 +2398,7 @@ ia64_compute_frame_size (HOST_WIDE_INT size) } for (regno = BR_REG (1); regno <= BR_REG (7); regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && ! call_used_regs[regno]) { SET_HARD_REG_BIT (mask, regno); spill_size += 8; @@ -2357,15 +2410,15 @@ ia64_compute_frame_size (HOST_WIDE_INT size) if (frame_pointer_needed) { - current_frame_info.reg_fp = find_gr_spill (1); + current_frame_info.r[reg_fp] = find_gr_spill (reg_fp, 1); /* If we did not get a register, then we take LOC79. This is guaranteed to be free, even if regs_ever_live is already set, because this is HARD_FRAME_POINTER_REGNUM. This requires incrementing n_local_regs, as we don't count loc79 above. */ - if (current_frame_info.reg_fp == 0) + if (current_frame_info.r[reg_fp] == 0) { - current_frame_info.reg_fp = LOC_REG (79); - current_frame_info.n_local_regs++; + current_frame_info.r[reg_fp] = LOC_REG (79); + current_frame_info.n_local_regs = LOC_REG (79) - LOC_REG (0) + 1; } } @@ -2376,8 +2429,8 @@ ia64_compute_frame_size (HOST_WIDE_INT size) able to unwind the stack. */ SET_HARD_REG_BIT (mask, BR_REG (0)); - current_frame_info.reg_save_b0 = find_gr_spill (1); - if (current_frame_info.reg_save_b0 == 0) + current_frame_info.r[reg_save_b0] = find_gr_spill (reg_save_b0, 1); + if (current_frame_info.r[reg_save_b0] == 0) { extra_spill_size += 8; n_spilled += 1; @@ -2385,8 +2438,8 @@ ia64_compute_frame_size (HOST_WIDE_INT size) /* Similarly for ar.pfs. */ SET_HARD_REG_BIT (mask, AR_PFS_REGNUM); - current_frame_info.reg_save_ar_pfs = find_gr_spill (1); - if (current_frame_info.reg_save_ar_pfs == 0) + current_frame_info.r[reg_save_ar_pfs] = find_gr_spill (reg_save_ar_pfs, 1); + if (current_frame_info.r[reg_save_ar_pfs] == 0) { extra_spill_size += 8; n_spilled += 1; @@ -2394,9 +2447,9 @@ ia64_compute_frame_size (HOST_WIDE_INT size) /* Similarly for gp. Note that if we're calling setjmp, the stacked registers are clobbered, so we fall back to the stack. */ - current_frame_info.reg_save_gp - = (current_function_calls_setjmp ? 0 : find_gr_spill (1)); - if (current_frame_info.reg_save_gp == 0) + current_frame_info.r[reg_save_gp] + = (current_function_calls_setjmp ? 0 : find_gr_spill (reg_save_gp, 1)); + if (current_frame_info.r[reg_save_gp] == 0) { SET_HARD_REG_BIT (mask, GR_REG (1)); spill_size += 8; @@ -2405,18 +2458,19 @@ ia64_compute_frame_size (HOST_WIDE_INT size) } else { - if (regs_ever_live[BR_REG (0)] && ! call_used_regs[BR_REG (0)]) + if (df_regs_ever_live_p (BR_REG (0)) && ! call_used_regs[BR_REG (0)]) { SET_HARD_REG_BIT (mask, BR_REG (0)); extra_spill_size += 8; n_spilled += 1; } - if (regs_ever_live[AR_PFS_REGNUM]) + if (df_regs_ever_live_p (AR_PFS_REGNUM)) { SET_HARD_REG_BIT (mask, AR_PFS_REGNUM); - current_frame_info.reg_save_ar_pfs = find_gr_spill (1); - if (current_frame_info.reg_save_ar_pfs == 0) + current_frame_info.r[reg_save_ar_pfs] + = find_gr_spill (reg_save_ar_pfs, 1); + if (current_frame_info.r[reg_save_ar_pfs] == 0) { extra_spill_size += 8; n_spilled += 1; @@ -2429,25 +2483,31 @@ ia64_compute_frame_size (HOST_WIDE_INT size) it is absolutely critical that FP get the only hard register that's guaranteed to be free, so we allocated it first. If all three did happen to be allocated hard regs, and are consecutive, rearrange them - into the preferred order now. */ - if (current_frame_info.reg_fp != 0 - && current_frame_info.reg_save_b0 == current_frame_info.reg_fp + 1 - && current_frame_info.reg_save_ar_pfs == current_frame_info.reg_fp + 2) + into the preferred order now. + + If we have already emitted code for any of those registers, + then it's already too late to change. */ + if (current_frame_info.r[reg_fp] != 0 + && current_frame_info.r[reg_save_b0] == current_frame_info.r[reg_fp] + 1 + && current_frame_info.r[reg_save_ar_pfs] == current_frame_info.r[reg_fp] + 2 + && emitted_frame_related_regs[reg_save_b0] == 0 + && emitted_frame_related_regs[reg_save_ar_pfs] == 0 + && emitted_frame_related_regs[reg_fp] == 0) { - current_frame_info.reg_save_b0 = current_frame_info.reg_fp; - current_frame_info.reg_save_ar_pfs = current_frame_info.reg_fp + 1; - current_frame_info.reg_fp = current_frame_info.reg_fp + 2; + current_frame_info.r[reg_save_b0] = current_frame_info.r[reg_fp]; + current_frame_info.r[reg_save_ar_pfs] = current_frame_info.r[reg_fp] + 1; + current_frame_info.r[reg_fp] = current_frame_info.r[reg_fp] + 2; } /* See if we need to store the predicate register block. */ for (regno = PR_REG (0); regno <= PR_REG (63); regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && ! call_used_regs[regno]) break; if (regno <= PR_REG (63)) { SET_HARD_REG_BIT (mask, PR_REG (0)); - current_frame_info.reg_save_pr = find_gr_spill (1); - if (current_frame_info.reg_save_pr == 0) + current_frame_info.r[reg_save_pr] = find_gr_spill (reg_save_pr, 1); + if (current_frame_info.r[reg_save_pr] == 0) { extra_spill_size += 8; n_spilled += 1; @@ -2456,30 +2516,32 @@ ia64_compute_frame_size (HOST_WIDE_INT size) /* ??? Mark them all as used so that register renaming and such are free to use them. */ for (regno = PR_REG (0); regno <= PR_REG (63); regno++) - regs_ever_live[regno] = 1; + df_set_regs_ever_live (regno, true); } /* If we're forced to use st8.spill, we're forced to save and restore ar.unat as well. The check for existing liveness allows inline asm to touch ar.unat. */ if (spilled_gr_p || cfun->machine->n_varargs - || regs_ever_live[AR_UNAT_REGNUM]) + || df_regs_ever_live_p (AR_UNAT_REGNUM)) { - regs_ever_live[AR_UNAT_REGNUM] = 1; + df_set_regs_ever_live (AR_UNAT_REGNUM, true); SET_HARD_REG_BIT (mask, AR_UNAT_REGNUM); - current_frame_info.reg_save_ar_unat = find_gr_spill (spill_size == 0); - if (current_frame_info.reg_save_ar_unat == 0) + current_frame_info.r[reg_save_ar_unat] + = find_gr_spill (reg_save_ar_unat, spill_size == 0); + if (current_frame_info.r[reg_save_ar_unat] == 0) { extra_spill_size += 8; n_spilled += 1; } } - if (regs_ever_live[AR_LC_REGNUM]) + if (df_regs_ever_live_p (AR_LC_REGNUM)) { SET_HARD_REG_BIT (mask, AR_LC_REGNUM); - current_frame_info.reg_save_ar_lc = find_gr_spill (spill_size == 0); - if (current_frame_info.reg_save_ar_lc == 0) + current_frame_info.r[reg_save_ar_lc] + = find_gr_spill (reg_save_ar_lc, spill_size == 0); + if (current_frame_info.r[reg_save_ar_lc] == 0) { extra_spill_size += 8; n_spilled += 1; @@ -2713,16 +2775,6 @@ spill_restore_mem (rtx reg, HOST_WIDE_INT cfa_off) insn = emit_insn (seq); } spill_fill_data.init_after = insn; - - /* If DISP is 0, we may or may not have a further adjustment - afterward. If we do, then the load/store insn may be modified - to be a post-modify. If we don't, then this copy may be - eliminated by copyprop_hardreg_forward, which makes this - insn garbage, which runs afoul of the sanity check in - propagate_one_insn. So mark this insn as legal to delete. */ - if (disp == 0) - REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, - REG_NOTES (insn)); } mem = gen_rtx_MEM (GET_MODE (reg), spill_fill_data.iter_reg[iter]); @@ -2850,6 +2902,22 @@ ia64_expand_prologue (void) ia64_compute_frame_size (get_frame_size ()); last_scratch_gr_reg = 15; + if (dump_file) + { + fprintf (dump_file, "ia64 frame related registers " + "recorded in current_frame_info.r[]:\n"); +#define PRINTREG(a) if (current_frame_info.r[a]) \ + fprintf(dump_file, "%s = %d\n", #a, current_frame_info.r[a]) + PRINTREG(reg_fp); + PRINTREG(reg_save_b0); + PRINTREG(reg_save_pr); + PRINTREG(reg_save_ar_pfs); + PRINTREG(reg_save_ar_unat); + PRINTREG(reg_save_ar_lc); + PRINTREG(reg_save_gp); +#undef PRINTREG + } + /* If there is no epilogue, then we don't need some prologue insns. We need to avoid emitting the dead prologue insns, because flow will complain about them. */ @@ -2892,12 +2960,12 @@ ia64_expand_prologue (void) there is a frame pointer. loc79 gets wasted in this case, as it is renamed to a register that will never be used. See also the try_locals code in find_gr_spill. */ - if (current_frame_info.reg_fp) + if (current_frame_info.r[reg_fp]) { const char *tmp = reg_names[HARD_FRAME_POINTER_REGNUM]; reg_names[HARD_FRAME_POINTER_REGNUM] - = reg_names[current_frame_info.reg_fp]; - reg_names[current_frame_info.reg_fp] = tmp; + = reg_names[current_frame_info.r[reg_fp]]; + reg_names[current_frame_info.r[reg_fp]] = tmp; } /* We don't need an alloc instruction if we've used no outputs or locals. */ @@ -2915,8 +2983,11 @@ ia64_expand_prologue (void) { current_frame_info.need_regstk = 0; - if (current_frame_info.reg_save_ar_pfs) - regno = current_frame_info.reg_save_ar_pfs; + if (current_frame_info.r[reg_save_ar_pfs]) + { + regno = current_frame_info.r[reg_save_ar_pfs]; + reg_emitted (reg_save_ar_pfs); + } else regno = next_scratch_gr_reg (); ar_pfs_save_reg = gen_rtx_REG (DImode, regno); @@ -2926,7 +2997,7 @@ ia64_expand_prologue (void) GEN_INT (current_frame_info.n_local_regs), GEN_INT (current_frame_info.n_output_regs), GEN_INT (current_frame_info.n_rotate_regs))); - RTX_FRAME_RELATED_P (insn) = (current_frame_info.reg_save_ar_pfs != 0); + RTX_FRAME_RELATED_P (insn) = (current_frame_info.r[reg_save_ar_pfs] != 0); } /* Set up frame pointer, stack pointer, and spill iterators. */ @@ -2984,9 +3055,12 @@ ia64_expand_prologue (void) /* Must copy out ar.unat before doing any integer spills. */ if (TEST_HARD_REG_BIT (current_frame_info.mask, AR_UNAT_REGNUM)) { - if (current_frame_info.reg_save_ar_unat) - ar_unat_save_reg - = gen_rtx_REG (DImode, current_frame_info.reg_save_ar_unat); + if (current_frame_info.r[reg_save_ar_unat]) + { + ar_unat_save_reg + = gen_rtx_REG (DImode, current_frame_info.r[reg_save_ar_unat]); + reg_emitted (reg_save_ar_unat); + } else { alt_regno = next_scratch_gr_reg (); @@ -2996,11 +3070,11 @@ ia64_expand_prologue (void) reg = gen_rtx_REG (DImode, AR_UNAT_REGNUM); insn = emit_move_insn (ar_unat_save_reg, reg); - RTX_FRAME_RELATED_P (insn) = (current_frame_info.reg_save_ar_unat != 0); + RTX_FRAME_RELATED_P (insn) = (current_frame_info.r[reg_save_ar_unat] != 0); /* Even if we're not going to generate an epilogue, we still need to save the register so that EH works. */ - if (! epilogue_p && current_frame_info.reg_save_ar_unat) + if (! epilogue_p && current_frame_info.r[reg_save_ar_unat]) emit_insn (gen_prologue_use (ar_unat_save_reg)); } else @@ -3026,9 +3100,10 @@ ia64_expand_prologue (void) if (TEST_HARD_REG_BIT (current_frame_info.mask, PR_REG (0))) { reg = gen_rtx_REG (DImode, PR_REG (0)); - if (current_frame_info.reg_save_pr != 0) + if (current_frame_info.r[reg_save_pr] != 0) { - alt_reg = gen_rtx_REG (DImode, current_frame_info.reg_save_pr); + alt_reg = gen_rtx_REG (DImode, current_frame_info.r[reg_save_pr]); + reg_emitted (reg_save_pr); insn = emit_move_insn (alt_reg, reg); /* ??? Denote pr spill/fill by a DImode move that modifies all @@ -3056,7 +3131,7 @@ ia64_expand_prologue (void) /* Handle AR regs in numerical order. All of them get special handling. */ if (TEST_HARD_REG_BIT (current_frame_info.mask, AR_UNAT_REGNUM) - && current_frame_info.reg_save_ar_unat == 0) + && current_frame_info.r[reg_save_ar_unat] == 0) { reg = gen_rtx_REG (DImode, AR_UNAT_REGNUM); do_spill (gen_movdi_x, ar_unat_save_reg, cfa_off, reg); @@ -3067,7 +3142,7 @@ ia64_expand_prologue (void) only thing we have to do now is copy that register to a stack slot if we'd not allocated a local register for the job. */ if (TEST_HARD_REG_BIT (current_frame_info.mask, AR_PFS_REGNUM) - && current_frame_info.reg_save_ar_pfs == 0) + && current_frame_info.r[reg_save_ar_pfs] == 0) { reg = gen_rtx_REG (DImode, AR_PFS_REGNUM); do_spill (gen_movdi_x, ar_pfs_save_reg, cfa_off, reg); @@ -3077,9 +3152,10 @@ ia64_expand_prologue (void) if (TEST_HARD_REG_BIT (current_frame_info.mask, AR_LC_REGNUM)) { reg = gen_rtx_REG (DImode, AR_LC_REGNUM); - if (current_frame_info.reg_save_ar_lc != 0) + if (current_frame_info.r[reg_save_ar_lc] != 0) { - alt_reg = gen_rtx_REG (DImode, current_frame_info.reg_save_ar_lc); + alt_reg = gen_rtx_REG (DImode, current_frame_info.r[reg_save_ar_lc]); + reg_emitted (reg_save_ar_lc); insn = emit_move_insn (alt_reg, reg); RTX_FRAME_RELATED_P (insn) = 1; @@ -3102,9 +3178,10 @@ ia64_expand_prologue (void) if (TEST_HARD_REG_BIT (current_frame_info.mask, BR_REG (0))) { reg = gen_rtx_REG (DImode, BR_REG (0)); - if (current_frame_info.reg_save_b0 != 0) + if (current_frame_info.r[reg_save_b0] != 0) { - alt_reg = gen_rtx_REG (DImode, current_frame_info.reg_save_b0); + alt_reg = gen_rtx_REG (DImode, current_frame_info.r[reg_save_b0]); + reg_emitted (reg_save_b0); insn = emit_move_insn (alt_reg, reg); RTX_FRAME_RELATED_P (insn) = 1; @@ -3123,17 +3200,12 @@ ia64_expand_prologue (void) } } - if (current_frame_info.reg_save_gp) + if (current_frame_info.r[reg_save_gp]) { + reg_emitted (reg_save_gp); insn = emit_move_insn (gen_rtx_REG (DImode, - current_frame_info.reg_save_gp), + current_frame_info.r[reg_save_gp]), pic_offset_table_rtx); - /* We don't know for sure yet if this is actually needed, since - we've not split the PIC call patterns. If all of the calls - are indirect, and not followed by any uses of the gp, then - this save is dead. Allow it to go away. */ - REG_NOTES (insn) - = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, REG_NOTES (insn)); } /* We should now be at the base of the gr/br/fr spill area. */ @@ -3217,8 +3289,11 @@ ia64_expand_epilogue (int sibcall_p) /* Restore the predicate registers. */ if (TEST_HARD_REG_BIT (current_frame_info.mask, PR_REG (0))) { - if (current_frame_info.reg_save_pr != 0) - alt_reg = gen_rtx_REG (DImode, current_frame_info.reg_save_pr); + if (current_frame_info.r[reg_save_pr] != 0) + { + alt_reg = gen_rtx_REG (DImode, current_frame_info.r[reg_save_pr]); + reg_emitted (reg_save_pr); + } else { alt_regno = next_scratch_gr_reg (); @@ -3236,9 +3311,12 @@ ia64_expand_epilogue (int sibcall_p) after the GRs have been restored. */ if (TEST_HARD_REG_BIT (current_frame_info.mask, AR_UNAT_REGNUM)) { - if (current_frame_info.reg_save_ar_unat != 0) - ar_unat_save_reg - = gen_rtx_REG (DImode, current_frame_info.reg_save_ar_unat); + if (current_frame_info.r[reg_save_ar_unat] != 0) + { + ar_unat_save_reg + = gen_rtx_REG (DImode, current_frame_info.r[reg_save_ar_unat]); + reg_emitted (reg_save_ar_unat); + } else { alt_regno = next_scratch_gr_reg (); @@ -3251,9 +3329,10 @@ ia64_expand_epilogue (int sibcall_p) else ar_unat_save_reg = NULL_RTX; - if (current_frame_info.reg_save_ar_pfs != 0) + if (current_frame_info.r[reg_save_ar_pfs] != 0) { - alt_reg = gen_rtx_REG (DImode, current_frame_info.reg_save_ar_pfs); + reg_emitted (reg_save_ar_pfs); + alt_reg = gen_rtx_REG (DImode, current_frame_info.r[reg_save_ar_pfs]); reg = gen_rtx_REG (DImode, AR_PFS_REGNUM); emit_move_insn (reg, alt_reg); } @@ -3269,8 +3348,11 @@ ia64_expand_epilogue (int sibcall_p) if (TEST_HARD_REG_BIT (current_frame_info.mask, AR_LC_REGNUM)) { - if (current_frame_info.reg_save_ar_lc != 0) - alt_reg = gen_rtx_REG (DImode, current_frame_info.reg_save_ar_lc); + if (current_frame_info.r[reg_save_ar_lc] != 0) + { + alt_reg = gen_rtx_REG (DImode, current_frame_info.r[reg_save_ar_lc]); + reg_emitted (reg_save_ar_lc); + } else { alt_regno = next_scratch_gr_reg (); @@ -3285,8 +3367,11 @@ ia64_expand_epilogue (int sibcall_p) /* Restore the return pointer. */ if (TEST_HARD_REG_BIT (current_frame_info.mask, BR_REG (0))) { - if (current_frame_info.reg_save_b0 != 0) - alt_reg = gen_rtx_REG (DImode, current_frame_info.reg_save_b0); + if (current_frame_info.r[reg_save_b0] != 0) + { + alt_reg = gen_rtx_REG (DImode, current_frame_info.r[reg_save_b0]); + reg_emitted (reg_save_b0); + } else { alt_regno = next_scratch_gr_reg (); @@ -3409,7 +3494,8 @@ ia64_expand_epilogue (int sibcall_p) register, we may have swapped the names of r2 and HARD_FRAME_POINTER_REGNUM, so we have to make sure we're using the string "r2" when emitting the register name for the assembler. */ - if (current_frame_info.reg_fp && current_frame_info.reg_fp == GR_REG (2)) + if (current_frame_info.r[reg_fp] + && current_frame_info.r[reg_fp] == GR_REG (2)) fp = HARD_FRAME_POINTER_REGNUM; /* We must emit an alloc to force the input registers to become output @@ -3442,11 +3528,11 @@ ia64_direct_return (void) return (current_frame_info.total_size == 0 && current_frame_info.n_spilled == 0 - && current_frame_info.reg_save_b0 == 0 - && current_frame_info.reg_save_pr == 0 - && current_frame_info.reg_save_ar_pfs == 0 - && current_frame_info.reg_save_ar_unat == 0 - && current_frame_info.reg_save_ar_lc == 0); + && current_frame_info.r[reg_save_b0] == 0 + && current_frame_info.r[reg_save_pr] == 0 + && current_frame_info.r[reg_save_ar_pfs] == 0 + && current_frame_info.r[reg_save_ar_unat] == 0 + && current_frame_info.r[reg_save_ar_lc] == 0); } return 0; } @@ -3472,8 +3558,11 @@ ia64_split_return_addr_rtx (rtx dest) if (TEST_HARD_REG_BIT (current_frame_info.mask, BR_REG (0))) { - if (current_frame_info.reg_save_b0 != 0) - src = gen_rtx_REG (DImode, current_frame_info.reg_save_b0); + if (current_frame_info.r[reg_save_b0] != 0) + { + src = gen_rtx_REG (DImode, current_frame_info.r[reg_save_b0]); + reg_emitted (reg_save_b0); + } else { HOST_WIDE_INT off; @@ -3520,21 +3609,14 @@ int ia64_hard_regno_rename_ok (int from, int to) { /* Don't clobber any of the registers we reserved for the prologue. */ - if (to == current_frame_info.reg_fp - || to == current_frame_info.reg_save_b0 - || to == current_frame_info.reg_save_pr - || to == current_frame_info.reg_save_ar_pfs - || to == current_frame_info.reg_save_ar_unat - || to == current_frame_info.reg_save_ar_lc) - return 0; + enum ia64_frame_regs r; - if (from == current_frame_info.reg_fp - || from == current_frame_info.reg_save_b0 - || from == current_frame_info.reg_save_pr - || from == current_frame_info.reg_save_ar_pfs - || from == current_frame_info.reg_save_ar_unat - || from == current_frame_info.reg_save_ar_lc) - return 0; + for (r = reg_fp; r <= reg_save_ar_lc; r++) + if (to == current_frame_info.r[r] + || from == current_frame_info.r[r] + || to == emitted_frame_related_regs[r] + || from == emitted_frame_related_regs[r]) + return 0; /* Don't use output registers outside the register frame. */ if (OUT_REGNO_P (to) && to >= OUT_REG (current_frame_info.n_output_regs)) @@ -3592,36 +3674,36 @@ ia64_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) mask = 0; grsave = grsave_prev = 0; - if (current_frame_info.reg_save_b0 != 0) + if (current_frame_info.r[reg_save_b0] != 0) { mask |= 8; - grsave = grsave_prev = current_frame_info.reg_save_b0; + grsave = grsave_prev = current_frame_info.r[reg_save_b0]; } - if (current_frame_info.reg_save_ar_pfs != 0 + if (current_frame_info.r[reg_save_ar_pfs] != 0 && (grsave_prev == 0 - || current_frame_info.reg_save_ar_pfs == grsave_prev + 1)) + || current_frame_info.r[reg_save_ar_pfs] == grsave_prev + 1)) { mask |= 4; if (grsave_prev == 0) - grsave = current_frame_info.reg_save_ar_pfs; - grsave_prev = current_frame_info.reg_save_ar_pfs; + grsave = current_frame_info.r[reg_save_ar_pfs]; + grsave_prev = current_frame_info.r[reg_save_ar_pfs]; } - if (current_frame_info.reg_fp != 0 + if (current_frame_info.r[reg_fp] != 0 && (grsave_prev == 0 - || current_frame_info.reg_fp == grsave_prev + 1)) + || current_frame_info.r[reg_fp] == grsave_prev + 1)) { mask |= 2; if (grsave_prev == 0) grsave = HARD_FRAME_POINTER_REGNUM; - grsave_prev = current_frame_info.reg_fp; + grsave_prev = current_frame_info.r[reg_fp]; } - if (current_frame_info.reg_save_pr != 0 + if (current_frame_info.r[reg_save_pr] != 0 && (grsave_prev == 0 - || current_frame_info.reg_save_pr == grsave_prev + 1)) + || current_frame_info.r[reg_save_pr] == grsave_prev + 1)) { mask |= 1; if (grsave_prev == 0) - grsave = current_frame_info.reg_save_pr; + grsave = current_frame_info.r[reg_save_pr]; } if (mask && TARGET_GNU_AS) @@ -3657,12 +3739,13 @@ ia64_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED, { int i; - if (current_frame_info.reg_fp) + if (current_frame_info.r[reg_fp]) { const char *tmp = reg_names[HARD_FRAME_POINTER_REGNUM]; reg_names[HARD_FRAME_POINTER_REGNUM] - = reg_names[current_frame_info.reg_fp]; - reg_names[current_frame_info.reg_fp] = tmp; + = reg_names[current_frame_info.r[reg_fp]]; + reg_names[current_frame_info.r[reg_fp]] = tmp; + reg_emitted (reg_fp); } if (! TARGET_REG_NAMES) { @@ -3683,11 +3766,11 @@ ia64_dbx_register_number (int regno) /* In ia64_expand_prologue we quite literally renamed the frame pointer from its home at loc79 to something inside the register frame. We must perform the same renumbering here for the debug info. */ - if (current_frame_info.reg_fp) + if (current_frame_info.r[reg_fp]) { if (regno == HARD_FRAME_POINTER_REGNUM) - regno = current_frame_info.reg_fp; - else if (regno == current_frame_info.reg_fp) + regno = current_frame_info.r[reg_fp]; + else if (regno == current_frame_info.r[reg_fp]) regno = HARD_FRAME_POINTER_REGNUM; } @@ -5093,6 +5176,13 @@ ia64_override_options (void) init_machine_status = ia64_init_machine_status; } +/* Initialize the record of emitted frame related registers. */ + +void ia64_init_expanders (void) +{ + memset (&emitted_frame_related_regs, 0, sizeof (emitted_frame_related_regs)); +} + static struct machine_function * ia64_init_machine_status (void) { @@ -6650,11 +6740,12 @@ ia64_set_sched_flags (spec_info_t spec_info) mask |= BE_IN_CONTROL; } - gcc_assert (*flags & USE_GLAT); - if (mask) { - *flags |= USE_DEPS_LIST | DETACH_LIFE_INFO | DO_SPECULATION; + *flags |= USE_DEPS_LIST | DO_SPECULATION; + + if (mask & BE_IN_SPEC) + *flags |= NEW_BBS; spec_info->mask = mask; spec_info->flags = 0; @@ -8338,7 +8429,7 @@ emit_predicate_relation_info (void) /* Skip p0, which may be thought to be live due to (reg:DI p0) grabbing the entire block of predicate registers. */ for (r = PR_REG (2); r < PR_REG (64); r += 2) - if (REGNO_REG_SET_P (bb->il.rtl->global_live_at_start, r)) + if (REGNO_REG_SET_P (df_get_live_in (bb), r)) { rtx p = gen_rtx_REG (BImode, r); rtx n = emit_insn_after (gen_pred_rel_mutex (p), head); @@ -8388,13 +8479,9 @@ ia64_reorg (void) /* If optimizing, we'll have split before scheduling. */ if (optimize == 0) - split_all_insns (0); - - /* ??? update_life_info_in_dirty_blocks fails to terminate during - non-optimizing bootstrap. */ - update_life_info (NULL, UPDATE_LIFE_GLOBAL_RM_NOTES, PROP_DEATH_NOTES); + split_all_insns (); - if (optimize && ia64_flag_schedule_insns2) + if (optimize && ia64_flag_schedule_insns2 && dbg_cnt (ia64_sched2)) { timevar_push (TV_SCHED2); ia64_final_schedule = 1; @@ -8469,6 +8556,8 @@ ia64_reorg (void) _1mlx_ = get_cpu_unit_code ("1b_1mlx."); } schedule_ebbs (); + /* We cannot reuse this one because it has been corrupted by the + evil glat. */ finish_bundle_states (); if (ia64_tune == PROCESSOR_ITANIUM) { @@ -8485,6 +8574,8 @@ ia64_reorg (void) else emit_all_insn_group_barriers (dump_file); + df_analyze (); + /* A call must not be the last instruction in a function, so that the return address is still within the function, so that unwinding works properly. Note that IA-64 differs from dwarf2 on this point. */ @@ -8521,6 +8612,7 @@ ia64_reorg (void) variable_tracking_main (); timevar_pop (TV_VAR_TRACKING); } + df_finish_pass (); } /* Return true if REGNO is used by the epilogue. */ @@ -8567,24 +8659,18 @@ ia64_epilogue_uses (int regno) int ia64_eh_uses (int regno) { + enum ia64_frame_regs r; + if (! reload_completed) return 0; - if (current_frame_info.reg_save_b0 - && regno == current_frame_info.reg_save_b0) - return 1; - if (current_frame_info.reg_save_pr - && regno == current_frame_info.reg_save_pr) - return 1; - if (current_frame_info.reg_save_ar_pfs - && regno == current_frame_info.reg_save_ar_pfs) - return 1; - if (current_frame_info.reg_save_ar_unat - && regno == current_frame_info.reg_save_ar_unat) - return 1; - if (current_frame_info.reg_save_ar_lc - && regno == current_frame_info.reg_save_ar_lc) - return 1; + if (regno == 0) + return 0; + + for (r = reg_save_b0; r <= reg_save_ar_lc; r++) + if (regno == current_frame_info.r[r] + || regno == emitted_frame_related_regs[r]) + return 1; return 0; } @@ -8737,7 +8823,7 @@ process_set (FILE *asm_out_file, rtx pat, rtx insn, bool unwind, bool frame) /* If this is the final destination for ar.pfs, then this must be the alloc in the prologue. */ - if (dest_regno == current_frame_info.reg_save_ar_pfs) + if (dest_regno == current_frame_info.r[reg_save_ar_pfs]) { if (unwind) fprintf (asm_out_file, "\t.save ar.pfs, r%d\n", @@ -8802,28 +8888,28 @@ process_set (FILE *asm_out_file, rtx pat, rtx insn, bool unwind, bool frame) { case BR_REG (0): /* Saving return address pointer. */ - gcc_assert (dest_regno == current_frame_info.reg_save_b0); + gcc_assert (dest_regno == current_frame_info.r[reg_save_b0]); if (unwind) fprintf (asm_out_file, "\t.save rp, r%d\n", ia64_dbx_register_number (dest_regno)); return 1; case PR_REG (0): - gcc_assert (dest_regno == current_frame_info.reg_save_pr); + gcc_assert (dest_regno == current_frame_info.r[reg_save_pr]); if (unwind) fprintf (asm_out_file, "\t.save pr, r%d\n", ia64_dbx_register_number (dest_regno)); return 1; case AR_UNAT_REGNUM: - gcc_assert (dest_regno == current_frame_info.reg_save_ar_unat); + gcc_assert (dest_regno == current_frame_info.r[reg_save_ar_unat]); if (unwind) fprintf (asm_out_file, "\t.save ar.unat, r%d\n", ia64_dbx_register_number (dest_regno)); return 1; case AR_LC_REGNUM: - gcc_assert (dest_regno == current_frame_info.reg_save_ar_lc); + gcc_assert (dest_regno == current_frame_info.r[reg_save_ar_lc]); if (unwind) fprintf (asm_out_file, "\t.save ar.lc, r%d\n", ia64_dbx_register_number (dest_regno)); @@ -8880,31 +8966,31 @@ process_set (FILE *asm_out_file, rtx pat, rtx insn, bool unwind, bool frame) switch (src_regno) { case BR_REG (0): - gcc_assert (!current_frame_info.reg_save_b0); + gcc_assert (!current_frame_info.r[reg_save_b0]); if (unwind) fprintf (asm_out_file, "\t%s rp, %ld\n", saveop, off); return 1; case PR_REG (0): - gcc_assert (!current_frame_info.reg_save_pr); + gcc_assert (!current_frame_info.r[reg_save_pr]); if (unwind) fprintf (asm_out_file, "\t%s pr, %ld\n", saveop, off); return 1; case AR_LC_REGNUM: - gcc_assert (!current_frame_info.reg_save_ar_lc); + gcc_assert (!current_frame_info.r[reg_save_ar_lc]); if (unwind) fprintf (asm_out_file, "\t%s ar.lc, %ld\n", saveop, off); return 1; case AR_PFS_REGNUM: - gcc_assert (!current_frame_info.reg_save_ar_pfs); + gcc_assert (!current_frame_info.r[reg_save_ar_pfs]); if (unwind) fprintf (asm_out_file, "\t%s ar.pfs, %ld\n", saveop, off); return 1; case AR_UNAT_REGNUM: - gcc_assert (!current_frame_info.reg_save_ar_unat); + gcc_assert (!current_frame_info.r[reg_save_ar_unat]); if (unwind) fprintf (asm_out_file, "\t%s ar.unat, %ld\n", saveop, off); return 1; diff --git a/gcc/config/ia64/ia64.h b/gcc/config/ia64/ia64.h index 951fb57..70120ce 100644 --- a/gcc/config/ia64/ia64.h +++ b/gcc/config/ia64/ia64.h @@ -1,5 +1,5 @@ /* Definitions of target machine GNU compiler. IA-64 version. - Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by James E. Wilson <wilson@cygnus.com> and David Mosberger <davidm@hpl.hp.com>. @@ -985,6 +985,7 @@ enum reg_class pointer is not 16-byte aligned like the stack pointer. */ #define INIT_EXPANDERS \ do { \ + ia64_init_expanders (); \ if (cfun && cfun->emit->regno_pointer_align) \ REGNO_POINTER_ALIGN (ARG_POINTER_REGNUM) = 64; \ } while (0) diff --git a/gcc/config/iq2000/iq2000.c b/gcc/config/iq2000/iq2000.c index a86dd40..5270463 100644 --- a/gcc/config/iq2000/iq2000.c +++ b/gcc/config/iq2000/iq2000.c @@ -1,5 +1,5 @@ /* Subroutines used for code generation on Vitesse IQ2000 processors - Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -1964,13 +1964,6 @@ iq2000_expand_prologue (void) PUT_CODE (SET_SRC (pattern), ASHIFTRT); insn = emit_insn (pattern); - - /* Global life information isn't valid at this point, so we - can't check whether these shifts are actually used. Mark - them MAYBE_DEAD so that flow2 will remove them, and not - complain about dead code in the prologue. */ - REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX, - REG_NOTES (insn)); } } @@ -2119,7 +2112,7 @@ iq2000_can_use_return_insn (void) if (! reload_completed) return 0; - if (regs_ever_live[31] || profile_flag) + if (df_regs_ever_live_p (31) || profile_flag) return 0; if (cfun->machine->initialized) diff --git a/gcc/config/iq2000/iq2000.h b/gcc/config/iq2000/iq2000.h index 5c61bef..deb3460 100644 --- a/gcc/config/iq2000/iq2000.h +++ b/gcc/config/iq2000/iq2000.h @@ -1,6 +1,6 @@ /* Definitions of target machine for GNU compiler. Vitesse IQ2000 processors - Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -974,9 +974,9 @@ enum processor_type /* Tell prologue and epilogue if register REGNO should be saved / restored. */ #define MUST_SAVE_REGISTER(regno) \ - ((regs_ever_live[regno] && !call_used_regs[regno]) \ + ((df_regs_ever_live_p (regno) && !call_used_regs[regno]) \ || (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed) \ - || (regno == (GP_REG_FIRST + 31) && regs_ever_live[GP_REG_FIRST + 31])) + || (regno == (GP_REG_FIRST + 31) && df_regs_ever_live_p (GP_REG_FIRST + 31))) /* ALIGN FRAMES on double word boundaries */ #ifndef IQ2000_STACK_ALIGN diff --git a/gcc/config/m32c/m32c.c b/gcc/config/m32c/m32c.c index dd0bce3..f48b252 100644 --- a/gcc/config/m32c/m32c.c +++ b/gcc/config/m32c/m32c.c @@ -1,5 +1,5 @@ /* Target Code for R8C/M16C/M32C - Copyright (C) 2005 + Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Red Hat. @@ -1248,7 +1248,7 @@ need_to_save (int regno) if (cfun->machine->is_interrupt && (!cfun->machine->is_leaf || regno == A0_REGNO)) return 1; - if (regs_ever_live[regno] + if (df_regs_ever_live_p (regno) && (!call_used_regs[regno] || cfun->machine->is_interrupt)) return 1; return 0; diff --git a/gcc/config/m32r/m32r.c b/gcc/config/m32r/m32r.c index 6c9f3e5..fa0a6ad 100644 --- a/gcc/config/m32r/m32r.c +++ b/gcc/config/m32r/m32r.c @@ -1254,10 +1254,10 @@ static struct m32r_frame_info zero_frame_info; Don't consider them here. */ #define MUST_SAVE_REGISTER(regno, interrupt_p) \ ((regno) != RETURN_ADDR_REGNUM && (regno) != FRAME_POINTER_REGNUM \ - && (regs_ever_live[regno] && (!call_really_used_regs[regno] || interrupt_p))) + && (df_regs_ever_live_p (regno) && (!call_really_used_regs[regno] || interrupt_p))) -#define MUST_SAVE_FRAME_POINTER (regs_ever_live[FRAME_POINTER_REGNUM]) -#define MUST_SAVE_RETURN_ADDR (regs_ever_live[RETURN_ADDR_REGNUM] || current_function_profile) +#define MUST_SAVE_FRAME_POINTER (df_regs_ever_live_p (FRAME_POINTER_REGNUM)) +#define MUST_SAVE_RETURN_ADDR (df_regs_ever_live_p (RETURN_ADDR_REGNUM) || current_function_profile) #define SHORT_INSN_SIZE 2 /* Size of small instructions. */ #define LONG_INSN_SIZE 4 /* Size of long instructions. */ @@ -2449,7 +2449,7 @@ m32r_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED, { /* Interrupt routines can't clobber any register that isn't already used. */ if (lookup_attribute ("interrupt", DECL_ATTRIBUTES (current_function_decl)) - && !regs_ever_live[new_reg]) + && !df_regs_ever_live_p (new_reg)) return 0; return 1; diff --git a/gcc/config/m68hc11/m68hc11.c b/gcc/config/m68hc11/m68hc11.c index 655816e..ca75758 100644 --- a/gcc/config/m68hc11/m68hc11.c +++ b/gcc/config/m68hc11/m68hc11.c @@ -1,5 +1,5 @@ /* Subroutines for code generation on Motorola 68HC11 and 68HC12. - Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Stephane Carrez (stcarrez@nerim.fr) @@ -1336,7 +1336,7 @@ m68hc11_initial_elimination_offset (int from, int to) /* Push any 2 byte pseudo hard registers that we need to save. */ for (regno = SOFT_REG_FIRST; regno < SOFT_REG_LAST; regno++) { - if (regs_ever_live[regno] && !call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && !call_used_regs[regno]) { size += 2; } @@ -1550,7 +1550,7 @@ m68hc11_total_frame_size (void) size += HARD_REG_SIZE; for (regno = SOFT_REG_FIRST; regno <= SOFT_REG_LAST; regno++) - if (regs_ever_live[regno] && !call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && !call_used_regs[regno]) size += HARD_REG_SIZE; return size; @@ -1662,7 +1662,7 @@ expand_prologue (void) /* Push any 2 byte pseudo hard registers that we need to save. */ for (regno = SOFT_REG_FIRST; regno <= SOFT_REG_LAST; regno++) { - if (regs_ever_live[regno] && !call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && !call_used_regs[regno]) { emit_move_after_reload (stack_push_word, gen_rtx_REG (HImode, regno), scratch); @@ -1700,7 +1700,7 @@ expand_epilogue (void) /* Pop any 2 byte pseudo hard registers that we saved. */ for (regno = SOFT_REG_LAST; regno >= SOFT_REG_FIRST; regno--) { - if (regs_ever_live[regno] && !call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && !call_used_regs[regno]) { emit_move_after_reload (gen_rtx_REG (HImode, regno), stack_pop_word, scratch); diff --git a/gcc/config/m68k/m68k.c b/gcc/config/m68k/m68k.c index 1ce7042..353b84a 100644 --- a/gcc/config/m68k/m68k.c +++ b/gcc/config/m68k/m68k.c @@ -43,6 +43,7 @@ Boston, MA 02110-1301, USA. */ #include "target-def.h" #include "debug.h" #include "flags.h" +#include "df.h" enum reg_class regno_reg_class[] = { @@ -818,7 +819,7 @@ m68k_save_reg (unsigned int regno, bool interrupt_handler) if they are live or when calling nested functions. */ if (interrupt_handler) { - if (regs_ever_live[regno]) + if (df_regs_ever_live_p (regno)) return true; if (!current_function_is_leaf && call_used_regs[regno]) @@ -826,7 +827,7 @@ m68k_save_reg (unsigned int regno, bool interrupt_handler) } /* Never need to save registers that aren't touched. */ - if (!regs_ever_live[regno]) + if (!df_regs_ever_live_p (regno)) return false; /* Otherwise save everything that isn't call-clobbered. */ @@ -1037,12 +1038,7 @@ m68k_expand_prologue (void) if (flag_pic && !TARGET_SEP_DATA && current_function_uses_pic_offset_table) - { - insn = emit_insn (gen_load_got (pic_offset_table_rtx)); - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, - const0_rtx, - REG_NOTES (insn)); - } + insn = emit_insn (gen_load_got (pic_offset_table_rtx)); } /* Return true if a simple (return) instruction is sufficient for this @@ -4143,7 +4139,6 @@ m68k_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED, /* Pretend to be a post-reload pass while generating rtl. */ no_new_pseudos = 1; reload_completed = 1; - allocate_reg_info (FIRST_PSEUDO_REGISTER, true, true); /* The "this" pointer is stored at 4(%sp). */ this_slot = gen_rtx_MEM (Pmode, plus_constant (stack_pointer_rtx, 4)); @@ -4198,7 +4193,7 @@ m68k_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED, /* Use the static chain register as a temporary (call-clobbered) GOT pointer for this function. We can use the static chain register because it isn't live on entry to the thunk. */ - REGNO (pic_offset_table_rtx) = STATIC_CHAIN_REGNUM; + SET_REGNO (pic_offset_table_rtx, STATIC_CHAIN_REGNUM); emit_insn (gen_load_got (pic_offset_table_rtx)); } legitimize_pic_address (XEXP (mem, 0), Pmode, static_chain_rtx); @@ -4220,7 +4215,7 @@ m68k_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED, /* Restore the original PIC register. */ if (flag_pic) - REGNO (pic_offset_table_rtx) = PIC_REG; + SET_REGNO (pic_offset_table_rtx, PIC_REG); } /* Worker function for TARGET_STRUCT_VALUE_RTX. */ @@ -4244,7 +4239,7 @@ m68k_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED, if ((m68k_get_function_kind (current_function_decl) == m68k_fk_interrupt_handler) - && !regs_ever_live[new_reg]) + && !df_regs_ever_live_p (new_reg)) return 0; return 1; diff --git a/gcc/config/mcore/mcore.c b/gcc/config/mcore/mcore.c index 44035b2..e568699 100644 --- a/gcc/config/mcore/mcore.c +++ b/gcc/config/mcore/mcore.c @@ -267,7 +267,7 @@ calc_live_regs (int * count) for (reg = 0; reg < FIRST_PSEUDO_REGISTER; reg++) { - if (regs_ever_live[reg] && !call_used_regs[reg]) + if (df_regs_ever_live_p (reg) && !call_used_regs[reg]) { (*count)++; live_regs_mask |= (1 << reg); diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c index 0e860b6..d84eb9b 100644 --- a/gcc/config/mips/mips.c +++ b/gcc/config/mips/mips.c @@ -1,6 +1,7 @@ /* Subroutines used for MIPS code generation. Copyright (C) 1989, 1990, 1991, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. Contributed by A. Lichnewsky, lich@inria.inria.fr. Changes by Michael Meissner, meissner@osf.org. 64-bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and @@ -6337,7 +6338,7 @@ mips_global_pointer (void) In cases like these, reload will have added the constant to the pool but no instruction will yet refer to it. */ - if (!regs_ever_live[GLOBAL_POINTER_REGNUM] + if (!df_regs_ever_live_p (GLOBAL_POINTER_REGNUM) && !current_function_uses_const_pool && !mips_function_has_gp_insn ()) return 0; @@ -6346,7 +6347,7 @@ mips_global_pointer (void) register instead of $gp. */ if (TARGET_CALL_SAVED_GP && current_function_is_leaf) for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) - if (!regs_ever_live[regno] + if (!df_regs_ever_live_p (regno) && call_used_regs[regno] && !fixed_regs[regno] && regno != PIC_FUNCTION_ADDR_REGNUM) @@ -6412,15 +6413,15 @@ mips_save_reg_p (unsigned int regno) return TARGET_CALL_SAVED_GP && cfun->machine->global_pointer == regno; /* Check call-saved registers. */ - if (regs_ever_live[regno] && !call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && !call_used_regs[regno]) return true; /* Save both registers in an FPR pair if either one is used. This is needed for the case when MIN_FPRS_PER_FMT == 1, which allows the odd register to be used without the even register. */ if (FP_REG_P (regno) - && MAX_FPRS_PER_FMT == 2 - && regs_ever_live[regno + 1] + && MAX_FPRS_PER_FMT == 2 + && df_regs_ever_live_p (regno + 1) && !call_used_regs[regno + 1]) return true; @@ -6430,7 +6431,7 @@ mips_save_reg_p (unsigned int regno) /* We need to save the incoming return address if it is ever clobbered within the function. */ - if (regno == GP_REG_FIRST + 31 && regs_ever_live[regno]) + if (regno == GP_REG_FIRST + 31 && df_regs_ever_live_p (regno)) return true; if (TARGET_MIPS16) @@ -6438,7 +6439,7 @@ mips_save_reg_p (unsigned int regno) /* $18 is a special case in mips16 code. It may be used to call a function which returns a floating point value, but it is marked in call_used_regs. */ - if (regno == GP_REG_FIRST + 18 && regs_ever_live[regno]) + if (regno == GP_REG_FIRST + 18 && df_regs_ever_live_p (regno)) return true; /* $31 is also a special case. It will be used to copy a return @@ -6990,7 +6991,7 @@ mips_expand_prologue (void) HOST_WIDE_INT size; if (cfun->machine->global_pointer > 0) - REGNO (pic_offset_table_rtx) = cfun->machine->global_pointer; + SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer); size = compute_frame_size (get_frame_size ()); @@ -7099,7 +7100,7 @@ mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED, HOST_WIDE_INT size ATTRIBUTE_UNUSED) { /* Reinstate the normal $gp. */ - REGNO (pic_offset_table_rtx) = GLOBAL_POINTER_REGNUM; + SET_REGNO (pic_offset_table_rtx, GLOBAL_POINTER_REGNUM); mips_output_cplocal (); if (cfun->machine->all_noreorder_p) @@ -7296,7 +7297,7 @@ mips_can_use_return_insn (void) if (! reload_completed) return 0; - if (regs_ever_live[31] || current_function_profile) + if (df_regs_ever_live_p (31) || current_function_profile) return 0; /* In mips16 mode, a function that returns a floating point value @@ -7331,9 +7332,13 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, /* Pick a global pointer. Use a call-clobbered register if TARGET_CALL_SAVED_GP, so that we can use a sibcall. */ if (TARGET_USE_GOT) - cfun->machine->global_pointer - = REGNO (pic_offset_table_rtx) - = TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM; + { + cfun->machine->global_pointer = + TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM; + + SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer); + + } /* Set up the global pointer for n32 or n64 abicalls. If LOADGP_ABSOLUTE then the thunk does not use the gp and there is @@ -8387,7 +8392,7 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code) /* If we are handling a floating point return value, we need to save $18 in the function prologue. Putting a note on the - call will mean that regs_ever_live[$18] will be true if the + call will mean that df_regs_ever_live_p ($18) will be true if the call is not eliminated, and we can check that in the prologue code. */ if (fpret) diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md index de592b9..b89f5f9 100644 --- a/gcc/config/mips/mips.md +++ b/gcc/config/mips/mips.md @@ -1,6 +1,7 @@ ;; Mips.md Machine Description for MIPS based processors ;; Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, -;; 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. +;; 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 +;; Free Software Foundation, Inc. ;; Contributed by A. Lichnewsky, lich@inria.inria.fr ;; Changes by Michael Meissner, meissner@osf.org ;; 64-bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and @@ -3094,7 +3095,7 @@ (high:DI (match_operand:DI 1 "general_symbolic_operand" "")))] "TARGET_EXPLICIT_RELOCS && ABI_HAS_64BIT_SYMBOLS" "#" - "&& flow2_completed" + "&& epilogue_completed" [(set (match_dup 0) (high:DI (match_dup 2))) (set (match_dup 0) (lo_sum:DI (match_dup 0) (match_dup 2))) (set (match_dup 0) (ashift:DI (match_dup 0) (const_int 16))) diff --git a/gcc/config/mmix/mmix.c b/gcc/config/mmix/mmix.c index b57ed7a..8a23c02 100644 --- a/gcc/config/mmix/mmix.c +++ b/gcc/config/mmix/mmix.c @@ -1,5 +1,5 @@ /* Definitions of target machine for GNU compiler, for MMIX. - Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Hans-Peter Nilsson (hp@bitrange.com) @@ -56,7 +56,7 @@ Boston, MA 02110-1301, USA. */ /* We have no means to tell DWARF 2 about the register stack, so we need to store the return address on the stack if an exception can get into this function. FIXME: Narrow condition. Before any whole-function - analysis, regs_ever_live[] isn't initialized. We know it's up-to-date + analysis, df_regs_ever_live_p () isn't initialized. We know it's up-to-date after reload_completed; it may contain incorrect information some time before that. Within a RTL sequence (after a call to start_sequence, such as in RTL expanders), leaf_function_p doesn't see all insns @@ -66,7 +66,7 @@ Boston, MA 02110-1301, USA. */ preferable. */ #define MMIX_CFUN_NEEDS_SAVED_EH_RETURN_ADDRESS \ (flag_exceptions \ - && ((reload_completed && regs_ever_live[MMIX_rJ_REGNUM]) \ + && ((reload_completed && df_regs_ever_live_p (MMIX_rJ_REGNUM)) \ || !leaf_function_p ())) #define IS_MMIX_EH_RETURN_DATA_REG(REGNO) \ @@ -547,7 +547,7 @@ mmix_initial_elimination_offset (int fromreg, int toreg) for (regno = MMIX_FIRST_GLOBAL_REGNUM; regno <= 255; regno++) - if ((regs_ever_live[regno] && ! call_used_regs[regno]) + if ((df_regs_ever_live_p (regno) && ! call_used_regs[regno]) || IS_MMIX_EH_RETURN_DATA_REG (regno)) fp_sp_offset += 8; @@ -765,7 +765,7 @@ mmix_reorg (void) for (regno = MMIX_LAST_STACK_REGISTER_REGNUM; regno >= 0; regno--) - if ((regs_ever_live[regno] && !call_used_regs[regno]) + if ((df_regs_ever_live_p (regno) && !call_used_regs[regno]) || (regno == MMIX_FRAME_POINTER_REGNUM && frame_pointer_needed)) break; @@ -774,7 +774,7 @@ mmix_reorg (void) insns to see whether they're actually used (and indeed do other less trivial register usage analysis and transformations), but it seems wasteful to optimize for unused parameter registers. As of - 2002-04-30, regs_ever_live[n] seems to be set for only-reads too, but + 2002-04-30, df_regs_ever_live_p (n) seems to be set for only-reads too, but that might change. */ if (!TARGET_ABI_GNU && regno < current_function_args_info.regs - 1) { @@ -1836,7 +1836,7 @@ mmix_use_simple_return (void) /* Note that we assume that the frame-pointer-register is one of these registers, in which case we don't count it here. */ if ((((regno != MMIX_FRAME_POINTER_REGNUM || !frame_pointer_needed) - && regs_ever_live[regno] && !call_used_regs[regno])) + && df_regs_ever_live_p (regno) && !call_used_regs[regno])) || IS_MMIX_EH_RETURN_DATA_REG (regno)) return 0; @@ -1872,7 +1872,7 @@ mmix_expand_prologue (void) /* Note that we assume that the frame-pointer-register is one of these registers, in which case we don't count it here. */ if ((((regno != MMIX_FRAME_POINTER_REGNUM || !frame_pointer_needed) - && regs_ever_live[regno] && !call_used_regs[regno])) + && df_regs_ever_live_p (regno) && !call_used_regs[regno])) || IS_MMIX_EH_RETURN_DATA_REG (regno)) stack_space_to_allocate += 8; @@ -2057,7 +2057,7 @@ mmix_expand_prologue (void) regno >= MMIX_FIRST_GLOBAL_REGNUM; regno--) if (((regno != MMIX_FRAME_POINTER_REGNUM || !frame_pointer_needed) - && regs_ever_live[regno] && ! call_used_regs[regno]) + && df_regs_ever_live_p (regno) && ! call_used_regs[regno]) || IS_MMIX_EH_RETURN_DATA_REG (regno)) { rtx insn; @@ -2109,7 +2109,7 @@ mmix_expand_epilogue (void) regno >= MMIX_FIRST_GLOBAL_REGNUM; regno--) if (((regno != MMIX_FRAME_POINTER_REGNUM || !frame_pointer_needed) - && regs_ever_live[regno] && !call_used_regs[regno]) + && df_regs_ever_live_p (regno) && !call_used_regs[regno]) || IS_MMIX_EH_RETURN_DATA_REG (regno)) stack_space_to_deallocate += 8; @@ -2138,7 +2138,7 @@ mmix_expand_epilogue (void) regno <= 255; regno++) if (((regno != MMIX_FRAME_POINTER_REGNUM || !frame_pointer_needed) - && regs_ever_live[regno] && !call_used_regs[regno]) + && df_regs_ever_live_p (regno) && !call_used_regs[regno]) || IS_MMIX_EH_RETURN_DATA_REG (regno)) { if (offset > 255) diff --git a/gcc/config/mn10300/mn10300.c b/gcc/config/mn10300/mn10300.c index d7b7924..8c93f41 100644 --- a/gcc/config/mn10300/mn10300.c +++ b/gcc/config/mn10300/mn10300.c @@ -1,6 +1,6 @@ /* Subroutines for insn-output.c for Matsushita MN10300 series - Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 - Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, + 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Jeff Law (law@cygnus.com). This file is part of GCC. @@ -60,12 +60,12 @@ enum processor_type mn10300_processor = PROCESSOR_DEFAULT; /* The size of the callee register save area. Right now we save everything on entry since it costs us nothing in code size. It does cost us from a speed standpoint, so we want to optimize this sooner or later. */ -#define REG_SAVE_BYTES (4 * regs_ever_live[2] \ - + 4 * regs_ever_live[3] \ - + 4 * regs_ever_live[6] \ - + 4 * regs_ever_live[7] \ - + 16 * (regs_ever_live[14] || regs_ever_live[15] \ - || regs_ever_live[16] || regs_ever_live[17])) +#define REG_SAVE_BYTES (4 * df_regs_ever_live_p (2) \ + + 4 * df_regs_ever_live_p (3) \ + + 4 * df_regs_ever_live_p (6) \ + + 4 * df_regs_ever_live_p (7) \ + + 16 * (df_regs_ever_live_p (14) || df_regs_ever_live_p (15) \ + || df_regs_ever_live_p (16) || df_regs_ever_live_p (17))) static bool mn10300_handle_option (size_t, const char *, int); @@ -537,7 +537,7 @@ fp_regs_to_save (void) return 0; for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i) - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_used_regs[i]) ++n; return n; @@ -590,14 +590,14 @@ can_use_return_insn (void) return (reload_completed && size == 0 - && !regs_ever_live[2] - && !regs_ever_live[3] - && !regs_ever_live[6] - && !regs_ever_live[7] - && !regs_ever_live[14] - && !regs_ever_live[15] - && !regs_ever_live[16] - && !regs_ever_live[17] + && !df_regs_ever_live_p (2) + && !df_regs_ever_live_p (3) + && !df_regs_ever_live_p (6) + && !df_regs_ever_live_p (7) + && !df_regs_ever_live_p (14) + && !df_regs_ever_live_p (15) + && !df_regs_ever_live_p (16) + && !df_regs_ever_live_p (17) && fp_regs_to_save () == 0 && !frame_pointer_needed); } @@ -614,7 +614,7 @@ mn10300_get_live_callee_saved_regs (void) mask = 0; for (i = 0; i <= LAST_EXTENDED_REGNUM; i++) - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_used_regs[i]) mask |= (1 << i); if ((mask & 0x3c000) != 0) mask |= 0x3c000; @@ -907,7 +907,7 @@ expand_prologue (void) /* Now actually save the FP registers. */ for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i) - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_used_regs[i]) { rtx addr; @@ -944,24 +944,8 @@ expand_prologue (void) emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (-size))); - if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) - { - rtx insn = get_last_insn (); - rtx last = emit_insn (gen_GOTaddr2picreg ()); - - /* Mark these insns as possibly dead. Sometimes, flow2 may - delete all uses of the PIC register. In this case, let it - delete the initialization too. */ - do - { - insn = NEXT_INSN (insn); - - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, - const0_rtx, - REG_NOTES (insn)); - } - while (insn != last); - } + if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM)) + emit_insn (gen_GOTaddr2picreg ()); } void @@ -1127,7 +1111,7 @@ expand_epilogue (void) reg = gen_rtx_POST_INC (SImode, reg); for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i) - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_used_regs[i]) { rtx addr; @@ -1189,10 +1173,10 @@ expand_epilogue (void) } /* Adjust the stack and restore callee-saved registers, if any. */ - if (size || regs_ever_live[2] || regs_ever_live[3] - || regs_ever_live[6] || regs_ever_live[7] - || regs_ever_live[14] || regs_ever_live[15] - || regs_ever_live[16] || regs_ever_live[17] + if (size || df_regs_ever_live_p (2) || df_regs_ever_live_p (3) + || df_regs_ever_live_p (6) || df_regs_ever_live_p (7) + || df_regs_ever_live_p (14) || df_regs_ever_live_p (15) + || df_regs_ever_live_p (16) || df_regs_ever_live_p (17) || frame_pointer_needed) emit_jump_insn (gen_return_internal_regs (GEN_INT (size + REG_SAVE_BYTES))); @@ -1401,10 +1385,10 @@ initial_offset (int from, int to) is the size of the callee register save area. */ if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) { - if (regs_ever_live[2] || regs_ever_live[3] - || regs_ever_live[6] || regs_ever_live[7] - || regs_ever_live[14] || regs_ever_live[15] - || regs_ever_live[16] || regs_ever_live[17] + if (df_regs_ever_live_p (2) || df_regs_ever_live_p (3) + || df_regs_ever_live_p (6) || df_regs_ever_live_p (7) + || df_regs_ever_live_p (14) || df_regs_ever_live_p (15) + || df_regs_ever_live_p (16) || df_regs_ever_live_p (17) || fp_regs_to_save () || frame_pointer_needed) return REG_SAVE_BYTES @@ -1418,10 +1402,10 @@ initial_offset (int from, int to) area, and the fixed stack space needed for function calls (if any). */ if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) { - if (regs_ever_live[2] || regs_ever_live[3] - || regs_ever_live[6] || regs_ever_live[7] - || regs_ever_live[14] || regs_ever_live[15] - || regs_ever_live[16] || regs_ever_live[17] + if (df_regs_ever_live_p (2) || df_regs_ever_live_p (3) + || df_regs_ever_live_p (6) || df_regs_ever_live_p (7) + || df_regs_ever_live_p (14) || df_regs_ever_live_p (15) + || df_regs_ever_live_p (16) || df_regs_ever_live_p (17) || fp_regs_to_save () || frame_pointer_needed) return (get_frame_size () + REG_SAVE_BYTES diff --git a/gcc/config/mt/mt.c b/gcc/config/mt/mt.c index df5d8f2..a0e5567 100644 --- a/gcc/config/mt/mt.c +++ b/gcc/config/mt/mt.c @@ -1,5 +1,5 @@ /* Target definitions for the MorphoRISC1 - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Red Hat, Inc. This file is part of GCC. @@ -353,7 +353,7 @@ mt_print_operand_simple_address (FILE * file, rtx addr) switch (GET_CODE (addr)) { case REG: - fprintf (file, "%s, #0", reg_names [REGNO (addr)]); + fprintf (file, "%s, #0", reg_names[REGNO (addr)]); break; case PLUS: @@ -375,11 +375,11 @@ mt_print_operand_simple_address (FILE * file, rtx addr) reg = arg1, offset = arg0; else if (CONSTANT_P (arg0) && CONSTANT_P (arg1)) { - fprintf (file, "%s, #", reg_names [GPR_R0]); + fprintf (file, "%s, #", reg_names[GPR_R0]); output_addr_const (file, addr); break; } - fprintf (file, "%s, #", reg_names [REGNO (reg)]); + fprintf (file, "%s, #", reg_names[REGNO (reg)]); output_addr_const (file, offset); break; } @@ -457,7 +457,7 @@ mt_print_operand (FILE * file, rtx x, int code) switch (GET_CODE (x)) { case REG: - fputs (reg_names [REGNO (x)], file); + fputs (reg_names[REGNO (x)], file); break; case CONST: @@ -884,10 +884,10 @@ mt_compute_frame_size (int size) } } - current_frame_info.save_fp = (regs_ever_live [GPR_FP] + current_frame_info.save_fp = (df_regs_ever_live_p (GPR_FP) || frame_pointer_needed || interrupt_handler); - current_frame_info.save_lr = (regs_ever_live [GPR_LINK] + current_frame_info.save_lr = (df_regs_ever_live_p (GPR_LINK) || profile_flag || interrupt_handler); diff --git a/gcc/config/mt/mt.h b/gcc/config/mt/mt.h index a4afb3c..88abec7 100644 --- a/gcc/config/mt/mt.h +++ b/gcc/config/mt/mt.h @@ -1,5 +1,5 @@ /* Target Definitions for MorphoRISC1 - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Red Hat, Inc. This file is part of GCC. @@ -414,11 +414,11 @@ enum save_direction && (regno) != GPR_FP \ && (regno) != GPR_SP \ && (regno) != GPR_R0 \ - && (( regs_ever_live [regno] && ! call_used_regs [regno] ) \ + && (( df_regs_ever_live_p (regno) && ! call_used_regs[regno] ) \ /* Save ira register in an interrupt handler. */ \ || (interrupt_handler && (regno) == GPR_INTERRUPT_LINK) \ /* Save any register used in an interrupt handler. */ \ - || (interrupt_handler && regs_ever_live [regno]) \ + || (interrupt_handler && df_regs_ever_live_p (regno)) \ /* Save call clobbered registers in non-leaf interrupt \ handlers. */ \ || (interrupt_handler && call_used_regs[regno] \ diff --git a/gcc/config/pa/pa.c b/gcc/config/pa/pa.c index f0a894a..01eb9c3 100644 --- a/gcc/config/pa/pa.c +++ b/gcc/config/pa/pa.c @@ -1,6 +1,6 @@ /* Subroutines for insn-output.c for HPPA. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Tim Moore (moore@cs.utah.edu), based on sparc.c This file is part of GCC. @@ -3491,13 +3491,13 @@ compute_frame_size (HOST_WIDE_INT size, int *fregs_live) /* Account for space used by the callee general register saves. */ for (i = 18, j = frame_pointer_needed ? 4 : 3; i >= j; i--) - if (regs_ever_live[i]) + if (df_regs_ever_live_p (i)) size += UNITS_PER_WORD; /* Account for space used by the callee floating point register saves. */ for (i = FP_SAVED_REG_LAST; i >= FP_SAVED_REG_FIRST; i -= FP_REG_STEP) - if (regs_ever_live[i] - || (!TARGET_64BIT && regs_ever_live[i + 1])) + if (df_regs_ever_live_p (i) + || (!TARGET_64BIT && df_regs_ever_live_p (i + 1))) { freg_saved = 1; @@ -3562,7 +3562,7 @@ pa_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) to output the assembler directives which denote the start of a function. */ fprintf (file, "\t.CALLINFO FRAME=" HOST_WIDE_INT_PRINT_DEC, actual_fsize); - if (regs_ever_live[2]) + if (df_regs_ever_live_p (2)) fputs (",CALLS,SAVE_RP", file); else fputs (",NO_CALLS", file); @@ -3626,7 +3626,7 @@ hppa_expand_prologue (void) /* Save RP first. The calling conventions manual states RP will always be stored into the caller's frame at sp - 20 or sp - 16 depending on which ABI is in use. */ - if (regs_ever_live[2] || current_function_calls_eh_return) + if (df_regs_ever_live_p (2) || current_function_calls_eh_return) store_reg (2, TARGET_64BIT ? -16 : -20, STACK_POINTER_REGNUM); /* Allocate the local frame and set up the frame pointer if needed. */ @@ -3737,7 +3737,7 @@ hppa_expand_prologue (void) } for (i = 18; i >= 4; i--) - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_used_regs[i]) { store_reg (i, offset, FRAME_POINTER_REGNUM); offset += UNITS_PER_WORD; @@ -3777,7 +3777,7 @@ hppa_expand_prologue (void) } for (i = 18; i >= 3; i--) - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_used_regs[i]) { /* If merge_sp_adjust_with_store is nonzero, then we can optimize the first GR save. */ @@ -3840,8 +3840,8 @@ hppa_expand_prologue (void) /* Now actually save the FP registers. */ for (i = FP_SAVED_REG_LAST; i >= FP_SAVED_REG_FIRST; i -= FP_REG_STEP) { - if (regs_ever_live[i] - || (! TARGET_64BIT && regs_ever_live[i + 1])) + if (df_regs_ever_live_p (i) + || (! TARGET_64BIT && df_regs_ever_live_p (i + 1))) { rtx addr, insn, reg; addr = gen_rtx_MEM (DFmode, gen_rtx_POST_INC (DFmode, tmpreg)); @@ -4029,7 +4029,7 @@ hppa_expand_epilogue (void) /* Try to restore RP early to avoid load/use interlocks when RP gets used in the return (bv) instruction. This appears to still be necessary even when we schedule the prologue and epilogue. */ - if (regs_ever_live [2] || current_function_calls_eh_return) + if (df_regs_ever_live_p (2) || current_function_calls_eh_return) { ret_off = TARGET_64BIT ? -16 : -20; if (frame_pointer_needed) @@ -4071,7 +4071,7 @@ hppa_expand_epilogue (void) } for (i = 18; i >= 4; i--) - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_used_regs[i]) { load_reg (i, offset, FRAME_POINTER_REGNUM); offset += UNITS_PER_WORD; @@ -4108,7 +4108,7 @@ hppa_expand_epilogue (void) for (i = 18; i >= 3; i--) { - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_used_regs[i]) { /* Only for the first load. merge_sp_adjust_with_load holds the register load @@ -4138,8 +4138,8 @@ hppa_expand_epilogue (void) /* Actually do the restores now. */ for (i = FP_SAVED_REG_LAST; i >= FP_SAVED_REG_FIRST; i -= FP_REG_STEP) - if (regs_ever_live[i] - || (! TARGET_64BIT && regs_ever_live[i + 1])) + if (df_regs_ever_live_p (i) + || (! TARGET_64BIT && df_regs_ever_live_p (i + 1))) { rtx src = gen_rtx_MEM (DFmode, gen_rtx_POST_INC (DFmode, tmpreg)); rtx dest = gen_rtx_REG (DFmode, i); @@ -4414,7 +4414,7 @@ hppa_can_use_return_insn_p (void) { return (reload_completed && (compute_frame_size (get_frame_size (), 0) ? 0 : 1) - && ! regs_ever_live[2] + && ! df_regs_ever_live_p (2) && ! frame_pointer_needed); } @@ -6256,7 +6256,7 @@ output_lbranch (rtx dest, rtx insn, int xdelay) for other purposes. */ if (TARGET_64BIT) { - if (actual_fsize == 0 && !regs_ever_live[2]) + if (actual_fsize == 0 && !df_regs_ever_live_p (2)) /* Use the return pointer slot in the frame marker. */ output_asm_insn ("std %%r1,-16(%%r30)", xoperands); else @@ -6266,7 +6266,7 @@ output_lbranch (rtx dest, rtx insn, int xdelay) } else { - if (actual_fsize == 0 && !regs_ever_live[2]) + if (actual_fsize == 0 && !df_regs_ever_live_p (2)) /* Use the return pointer slot in the frame marker. */ output_asm_insn ("stw %%r1,-20(%%r30)", xoperands); else @@ -6310,14 +6310,14 @@ output_lbranch (rtx dest, rtx insn, int xdelay) /* Now restore the value of %r1 in the delay slot. */ if (TARGET_64BIT) { - if (actual_fsize == 0 && !regs_ever_live[2]) + if (actual_fsize == 0 && !df_regs_ever_live_p (2)) return "ldd -16(%%r30),%%r1"; else return "ldd -40(%%r30),%%r1"; } else { - if (actual_fsize == 0 && !regs_ever_live[2]) + if (actual_fsize == 0 && !df_regs_ever_live_p (2)) return "ldw -20(%%r30),%%r1"; else return "ldw -12(%%r30),%%r1"; diff --git a/gcc/config/pa/pa.h b/gcc/config/pa/pa.h index d4e31c7..b95dd8a 100644 --- a/gcc/config/pa/pa.h +++ b/gcc/config/pa/pa.h @@ -1,6 +1,6 @@ /* Definitions of target machine for GNU compiler, for the HP Spectrum. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, - 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Michael Tiemann (tiemann@cygnus.com) of Cygnus Support and Tim Moore (moore@defmacro.cs.utah.edu) of the Center for Software Science at the University of Utah. @@ -372,7 +372,7 @@ typedef struct machine_function GTY(()) is already live or already being saved (due to eh). */ #define HARD_REGNO_RENAME_OK(OLD_REG, NEW_REG) \ - ((NEW_REG) != 2 || regs_ever_live[2] || current_function_calls_eh_return) + ((NEW_REG) != 2 || df_regs_ever_live_p (2) || current_function_calls_eh_return) /* C statement to store the difference between the frame pointer and the stack pointer values immediately after the function prologue. diff --git a/gcc/config/pdp11/pdp11.c b/gcc/config/pdp11/pdp11.c index 9232059..d61e45e 100644 --- a/gcc/config/pdp11/pdp11.c +++ b/gcc/config/pdp11/pdp11.c @@ -1,6 +1,6 @@ /* Subroutines for gcc2 for pdp11. - Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2001, 2004, 2005 - Free Software Foundation, Inc. + Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2001, 2004, 2005, + 2006, 2007 Free Software Foundation, Inc. Contributed by Michael K. Gschwind (mike@vlsivie.tuwien.ac.at). This file is part of GCC. @@ -291,7 +291,7 @@ pdp11_output_function_prologue (FILE *stream, HOST_WIDE_INT size) /* save CPU registers */ for (regno = 0; regno < 8; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && ! call_used_regs[regno]) if (! ((regno == FRAME_POINTER_REGNUM) && frame_pointer_needed)) fprintf (stream, "\tmov %s, -(sp)\n", reg_names[regno]); @@ -304,7 +304,7 @@ pdp11_output_function_prologue (FILE *stream, HOST_WIDE_INT size) { /* ac0 - ac3 */ if (LOAD_FPU_REG_P(regno) - && regs_ever_live[regno] + && df_regs_ever_live_p (regno) && ! call_used_regs[regno]) { fprintf (stream, "\tstd %s, -(sp)\n", reg_names[regno]); @@ -314,7 +314,7 @@ pdp11_output_function_prologue (FILE *stream, HOST_WIDE_INT size) /* maybe make ac4, ac5 call used regs?? */ /* ac4 - ac5 */ if (NO_LOAD_FPU_REG_P(regno) - && regs_ever_live[regno] + && df_regs_ever_live_p (regno) && ! call_used_regs[regno]) { gcc_assert (via_ac != -1); @@ -373,10 +373,10 @@ pdp11_output_function_epilogue (FILE *stream, HOST_WIDE_INT size) if (frame_pointer_needed) { /* hope this is safe - m68k does it also .... */ - regs_ever_live[FRAME_POINTER_REGNUM] = 0; + df_regs_ever_live_p (FRAME_POINTER_REGNUM) = 0; for (i =7, j = 0 ; i >= 0 ; i--) - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_used_regs[i]) j++; /* remember # of pushed bytes for CPU regs */ @@ -384,14 +384,14 @@ pdp11_output_function_epilogue (FILE *stream, HOST_WIDE_INT size) /* change fp -> r5 due to the compile error on libgcc2.c */ for (i =7 ; i >= 0 ; i--) - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_used_regs[i]) fprintf(stream, "\tmov %#o(r5), %s\n",(-fsize-2*j--)&0xffff, reg_names[i]); /* get ACs */ via_ac = FIRST_PSEUDO_REGISTER -1; for (i = FIRST_PSEUDO_REGISTER; i > 7; i--) - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_used_regs[i]) { via_ac = i; k += 8; @@ -400,7 +400,7 @@ pdp11_output_function_epilogue (FILE *stream, HOST_WIDE_INT size) for (i = FIRST_PSEUDO_REGISTER; i > 7; i--) { if (LOAD_FPU_REG_P(i) - && regs_ever_live[i] + && df_regs_ever_live_p (i) && ! call_used_regs[i]) { fprintf(stream, "\tldd %#o(r5), %s\n", (-fsize-k)&0xffff, reg_names[i]); @@ -408,7 +408,7 @@ pdp11_output_function_epilogue (FILE *stream, HOST_WIDE_INT size) } if (NO_LOAD_FPU_REG_P(i) - && regs_ever_live[i] + && df_regs_ever_live_p (i) && ! call_used_regs[i]) { gcc_assert (LOAD_FPU_REG_P(via_ac)); @@ -428,18 +428,18 @@ pdp11_output_function_epilogue (FILE *stream, HOST_WIDE_INT size) /* get ACs */ for (i = FIRST_PSEUDO_REGISTER; i > 7; i--) - if (regs_ever_live[i] && call_used_regs[i]) + if (df_regs_ever_live_p (i) && call_used_regs[i]) via_ac = i; for (i = FIRST_PSEUDO_REGISTER; i > 7; i--) { if (LOAD_FPU_REG_P(i) - && regs_ever_live[i] + && df_regs_ever_live_p (i) && ! call_used_regs[i]) fprintf(stream, "\tldd (sp)+, %s\n", reg_names[i]); if (NO_LOAD_FPU_REG_P(i) - && regs_ever_live[i] + && df_regs_ever_live_p (i) && ! call_used_regs[i]) { gcc_assert (LOAD_FPU_REG_P(via_ac)); @@ -450,7 +450,7 @@ pdp11_output_function_epilogue (FILE *stream, HOST_WIDE_INT size) } for (i=7; i >= 0; i--) - if (regs_ever_live[i] && !call_used_regs[i]) + if (df_regs_ever_live_p (i) && !call_used_regs[i]) fprintf(stream, "\tmov (sp)+, %s\n", reg_names[i]); if (fsize) diff --git a/gcc/config/pdp11/pdp11.h b/gcc/config/pdp11/pdp11.h index a018914..4fa2a29 100644 --- a/gcc/config/pdp11/pdp11.h +++ b/gcc/config/pdp11/pdp11.h @@ -1,6 +1,6 @@ /* Definitions of target machine for GNU compiler, for the pdp-11 - Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2004, 2005 - Free Software Foundation, Inc. + Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2004, 2005, + 2006, 2007 Free Software Foundation, Inc. Contributed by Michael K. Gschwind (mike@vlsivie.tuwien.ac.at). This file is part of GCC. @@ -566,10 +566,10 @@ extern int may_call_alloca; int offset, regno; \ offset = get_frame_size(); \ for (regno = 0; regno < 8; regno++) \ - if (regs_ever_live[regno] && ! call_used_regs[regno]) \ + if (df_regs_ever_live_p (regno) && ! call_used_regs[regno]) \ offset += 2; \ for (regno = 8; regno < 14; regno++) \ - if (regs_ever_live[regno] && ! call_used_regs[regno]) \ + if (df_regs_ever_live_p (regno) && ! call_used_regs[regno]) \ offset += 8; \ /* offset -= 2; no fp on stack frame */ \ (DEPTH_VAR) = offset; \ diff --git a/gcc/config/rs6000/predicates.md b/gcc/config/rs6000/predicates.md index 6aefe2d..5f4b233 100644 --- a/gcc/config/rs6000/predicates.md +++ b/gcc/config/rs6000/predicates.md @@ -1,5 +1,5 @@ ;; Predicate definitions for POWER and PowerPC. -;; Copyright (C) 2005, 2006 Free Software Foundation, Inc. +;; Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc. ;; ;; This file is part of GCC. ;; @@ -640,8 +640,8 @@ (match_operand 0 "reg_or_mem_operand"))) ;; Return 1 if the operand is a general register or memory operand without -;; pre_inc or pre_dec, which produces invalid form of PowerPC lwa -;; instruction. +;; pre_inc or pre_dec or pre_modify, which produces invalid form of PowerPC +;; lwa instruction. (define_predicate "lwa_operand" (match_code "reg,subreg,mem") { @@ -654,6 +654,8 @@ || (memory_operand (inner, mode) && GET_CODE (XEXP (inner, 0)) != PRE_INC && GET_CODE (XEXP (inner, 0)) != PRE_DEC + && (GET_CODE (XEXP (inner, 0)) != PRE_MODIFY + || legitimate_indexed_address_p (XEXP (XEXP (inner, 0), 1), 0)) && (GET_CODE (XEXP (inner, 0)) != PLUS || GET_CODE (XEXP (XEXP (inner, 0), 1)) != CONST_INT || INTVAL (XEXP (XEXP (inner, 0), 1)) % 4 == 0)); diff --git a/gcc/config/rs6000/rs6000-protos.h b/gcc/config/rs6000/rs6000-protos.h index 9ef454c..b821b01 100644 --- a/gcc/config/rs6000/rs6000-protos.h +++ b/gcc/config/rs6000/rs6000-protos.h @@ -42,6 +42,7 @@ extern bool invalid_e500_subreg (rtx, enum machine_mode); extern void validate_condition_mode (enum rtx_code, enum machine_mode); extern bool legitimate_constant_pool_address_p (rtx); extern bool legitimate_indirect_address_p (rtx, int); +extern bool legitimate_indexed_address_p (rtx, int); extern rtx rs6000_got_register (rtx); extern rtx find_addr_reg (rtx); diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index 67b7c86..910dae7 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -616,7 +616,6 @@ struct processor_costs power6_cost = { static bool rs6000_function_ok_for_sibcall (tree, tree); static const char *rs6000_invalid_within_doloop (rtx); static rtx rs6000_generate_compare (enum rtx_code); -static void rs6000_maybe_dead (rtx); static void rs6000_emit_stack_tie (void); static void rs6000_frame_related (rtx, rtx, HOST_WIDE_INT, rtx, rtx); static rtx spe_synthesize_frame_save (rtx); @@ -631,7 +630,6 @@ static int toc_hash_eq (const void *, const void *); static int constant_pool_expr_1 (rtx, int *, int *); static bool constant_pool_expr_p (rtx); static bool legitimate_small_data_p (enum machine_mode, rtx); -static bool legitimate_indexed_address_p (rtx, int); static bool legitimate_lo_sum_address_p (enum machine_mode, rtx, int); static struct machine_function * rs6000_init_machine_status (void); static bool rs6000_assemble_integer (rtx, unsigned int, int); @@ -3098,7 +3096,7 @@ rs6000_legitimate_offset_address_p (enum machine_mode mode, rtx x, int strict) return (offset < 0x10000) && (offset + extra < 0x10000); } -static bool +bool legitimate_indexed_address_p (rtx x, int strict) { rtx op0, op1; @@ -3459,10 +3457,7 @@ rs6000_legitimize_tls_address (rtx addr, enum tls_model model) emit_insn (gen_addsi3 (tmp3, tmp1, tmp2)); last = emit_move_insn (got, tmp3); set_unique_reg_note (last, REG_EQUAL, gsym); - REG_NOTES (first) = gen_rtx_INSN_LIST (REG_LIBCALL, last, - REG_NOTES (first)); - REG_NOTES (last) = gen_rtx_INSN_LIST (REG_RETVAL, first, - REG_NOTES (last)); + maybe_encapsulate_block (first, last, gsym); } } } @@ -3832,6 +3827,24 @@ rs6000_legitimate_address (enum machine_mode mode, rtx x, int reg_ok_strict) && (TARGET_POWERPC64 || mode != DImode) && legitimate_indexed_address_p (x, reg_ok_strict)) return 1; + if (GET_CODE (x) == PRE_MODIFY + && mode != TImode + && mode != TFmode + && mode != TDmode + && ((TARGET_HARD_FLOAT && TARGET_FPRS) + || TARGET_POWERPC64 + || ((mode != DFmode || TARGET_E500_DOUBLE) && mode != TFmode)) + && (TARGET_POWERPC64 || mode != DImode) + && !ALTIVEC_VECTOR_MODE (mode) + && !SPE_VECTOR_MODE (mode) + /* Restrict addressing for DI because of our SUBREG hackery. */ + && !(TARGET_E500_DOUBLE && (mode == DFmode || mode == DImode)) + && TARGET_UPDATE + && legitimate_indirect_address_p (XEXP (x, 0), reg_ok_strict) + && (rs6000_legitimate_offset_address_p (mode, XEXP (x, 1), reg_ok_strict) + || legitimate_indexed_address_p (XEXP (x, 1), reg_ok_strict)) + && rtx_equal_p (XEXP (XEXP (x, 1), 0), XEXP (x, 0))) + return 1; if (legitimate_lo_sum_address_p (mode, x, reg_ok_strict)) return 1; return 0; @@ -3864,7 +3877,10 @@ rs6000_mode_dependent_address (rtx addr) case LO_SUM: return true; - /* Auto-increment cases are now treated generically in recog.c. */ + case PRE_INC: + case PRE_DEC: + case PRE_MODIFY: + return TARGET_UPDATE; default: break; @@ -10465,8 +10481,8 @@ rs6000_got_register (rtx value ATTRIBUTE_UNUSED) /* The second flow pass currently (June 1999) can't update regs_ever_live without disturbing other parts of the compiler, so update it here to make the prolog/epilogue code happy. */ - if (no_new_pseudos && ! regs_ever_live[RS6000_PIC_OFFSET_TABLE_REGNUM]) - regs_ever_live[RS6000_PIC_OFFSET_TABLE_REGNUM] = 1; + if (no_new_pseudos && ! df_regs_ever_live_p (RS6000_PIC_OFFSET_TABLE_REGNUM)) + df_set_regs_ever_live (RS6000_PIC_OFFSET_TABLE_REGNUM, true); current_function_uses_pic_offset_table = 1; @@ -10833,6 +10849,9 @@ print_operand (FILE *file, rtx x, int code) || GET_CODE (XEXP (x, 0)) == PRE_DEC) output_address (plus_constant (XEXP (XEXP (x, 0), 0), UNITS_PER_WORD)); + else if (GET_CODE (XEXP (x, 0)) == PRE_MODIFY) + output_address (plus_constant (XEXP (XEXP (x, 0), 0), + UNITS_PER_WORD)); else output_address (XEXP (adjust_address_nv (x, SImode, UNITS_PER_WORD), @@ -11033,7 +11052,8 @@ print_operand (FILE *file, rtx x, int code) /* Print `u' if this has an auto-increment or auto-decrement. */ if (GET_CODE (x) == MEM && (GET_CODE (XEXP (x, 0)) == PRE_INC - || GET_CODE (XEXP (x, 0)) == PRE_DEC)) + || GET_CODE (XEXP (x, 0)) == PRE_DEC + || GET_CODE (XEXP (x, 0)) == PRE_MODIFY)) putc ('u', file); return; @@ -11120,7 +11140,9 @@ print_operand (FILE *file, rtx x, int code) case 'X': if (GET_CODE (x) == MEM - && legitimate_indexed_address_p (XEXP (x, 0), 0)) + && (legitimate_indexed_address_p (XEXP (x, 0), 0) + || (GET_CODE (XEXP (x, 0)) == PRE_MODIFY + && legitimate_indexed_address_p (XEXP (XEXP (x, 0), 1), 0)))) putc ('x', file); return; @@ -11133,6 +11155,8 @@ print_operand (FILE *file, rtx x, int code) if (GET_CODE (XEXP (x, 0)) == PRE_INC || GET_CODE (XEXP (x, 0)) == PRE_DEC) output_address (plus_constant (XEXP (XEXP (x, 0), 0), 8)); + else if (GET_CODE (XEXP (x, 0)) == PRE_MODIFY) + output_address (plus_constant (XEXP (XEXP (x, 0), 0), 8)); else output_address (XEXP (adjust_address_nv (x, SImode, 8), 0)); if (small_data_operand (x, GET_MODE (x))) @@ -11180,6 +11204,8 @@ print_operand (FILE *file, rtx x, int code) if (GET_CODE (XEXP (x, 0)) == PRE_INC || GET_CODE (XEXP (x, 0)) == PRE_DEC) output_address (plus_constant (XEXP (XEXP (x, 0), 0), 12)); + else if (GET_CODE (XEXP (x, 0)) == PRE_MODIFY) + output_address (plus_constant (XEXP (XEXP (x, 0), 0), 12)); else output_address (XEXP (adjust_address_nv (x, SImode, 12), 0)); if (small_data_operand (x, GET_MODE (x))) @@ -11260,6 +11286,8 @@ print_operand (FILE *file, rtx x, int code) else if (GET_CODE (XEXP (x, 0)) == PRE_DEC) fprintf (file, "%d(%s)", - GET_MODE_SIZE (GET_MODE (x)), reg_names[REGNO (XEXP (XEXP (x, 0), 0))]); + else if (GET_CODE (XEXP (x, 0)) == PRE_MODIFY) + output_address (XEXP (XEXP (x, 0), 1)); else output_address (XEXP (x, 0)); } @@ -13120,7 +13148,7 @@ first_reg_to_save (void) /* Find lowest numbered live register. */ for (first_reg = 13; first_reg <= 31; first_reg++) - if (regs_ever_live[first_reg] + if (df_regs_ever_live_p (first_reg) && (! call_used_regs[first_reg] || (first_reg == RS6000_PIC_OFFSET_TABLE_REGNUM && ((DEFAULT_ABI == ABI_V4 && flag_pic != 0) @@ -13147,7 +13175,7 @@ first_fp_reg_to_save (void) /* Find lowest numbered live register. */ for (first_reg = 14 + 32; first_reg <= 63; first_reg++) - if (regs_ever_live[first_reg]) + if (df_regs_ever_live_p (first_reg)) break; return first_reg; @@ -13173,7 +13201,7 @@ first_altivec_reg_to_save (void) /* Find lowest numbered live register. */ for (i = FIRST_ALTIVEC_REGNO + 20; i <= LAST_ALTIVEC_REGNO; ++i) - if (regs_ever_live[i]) + if (df_regs_ever_live_p (i)) break; return i; @@ -13197,7 +13225,7 @@ compute_vrsave_mask (void) /* First, find out if we use _any_ altivec registers. */ for (i = FIRST_ALTIVEC_REGNO; i <= LAST_ALTIVEC_REGNO; ++i) - if (regs_ever_live[i]) + if (df_regs_ever_live_p (i)) mask |= ALTIVEC_REG_BIT (i); if (mask == 0) @@ -13458,13 +13486,13 @@ rs6000_stack_info (void) || rs6000_ra_ever_killed ()) { info_ptr->lr_save_p = 1; - regs_ever_live[LINK_REGISTER_REGNUM] = 1; + df_set_regs_ever_live (LINK_REGISTER_REGNUM, true); } /* Determine if we need to save the condition code registers. */ - if (regs_ever_live[CR2_REGNO] - || regs_ever_live[CR3_REGNO] - || regs_ever_live[CR4_REGNO]) + if (df_regs_ever_live_p (CR2_REGNO) + || df_regs_ever_live_p (CR3_REGNO) + || df_regs_ever_live_p (CR4_REGNO)) { info_ptr->cr_save_p = 1; if (DEFAULT_ABI == ABI_V4) @@ -13988,15 +14016,6 @@ rs6000_ra_ever_killed (void) return 0; } -/* Add a REG_MAYBE_DEAD note to the insn. */ -static void -rs6000_maybe_dead (rtx insn) -{ - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, - const0_rtx, - REG_NOTES (insn)); -} - /* Emit instructions needed to load the TOC register. This is only needed when TARGET_TOC, TARGET_MINIMAL_TOC, and there is a constant pool; or for SVR4 -fpic. */ @@ -14004,7 +14023,7 @@ rs6000_maybe_dead (rtx insn) void rs6000_emit_load_toc_table (int fromprolog) { - rtx dest, insn; + rtx dest; dest = gen_rtx_REG (Pmode, RS6000_PIC_OFFSET_TABLE_REGNUM); if (TARGET_ELF && TARGET_SECURE_PLT && DEFAULT_ABI != ABI_AIX && flag_pic) @@ -14024,29 +14043,16 @@ rs6000_emit_load_toc_table (int fromprolog) tmp1 = gen_reg_rtx (Pmode); tmp2 = gen_reg_rtx (Pmode); } - insn = emit_insn (gen_load_toc_v4_PIC_1 (lab)); - if (fromprolog) - rs6000_maybe_dead (insn); - insn = emit_move_insn (tmp1, + emit_insn (gen_load_toc_v4_PIC_1 (lab)); + emit_move_insn (tmp1, gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM)); - if (fromprolog) - rs6000_maybe_dead (insn); - insn = emit_insn (gen_load_toc_v4_PIC_3b (tmp2, tmp1, got, lab)); - if (fromprolog) - rs6000_maybe_dead (insn); - insn = emit_insn (gen_load_toc_v4_PIC_3c (dest, tmp2, got, lab)); - if (fromprolog) - rs6000_maybe_dead (insn); + emit_insn (gen_load_toc_v4_PIC_3b (tmp2, tmp1, got, lab)); + emit_insn (gen_load_toc_v4_PIC_3c (dest, tmp2, got, lab)); } else if (TARGET_ELF && DEFAULT_ABI == ABI_V4 && flag_pic == 1) { - insn = emit_insn (gen_load_toc_v4_pic_si ()); - if (fromprolog) - rs6000_maybe_dead (insn); - insn = emit_move_insn (dest, - gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM)); - if (fromprolog) - rs6000_maybe_dead (insn); + emit_insn (gen_load_toc_v4_pic_si ()); + emit_move_insn (dest, gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM)); } else if (TARGET_ELF && DEFAULT_ABI != ABI_AIX && flag_pic == 2) { @@ -14065,13 +14071,10 @@ rs6000_emit_load_toc_table (int fromprolog) ASM_GENERATE_INTERNAL_LABEL (buf, "LCL", rs6000_pic_labelno); symL = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf)); - rs6000_maybe_dead (emit_insn (gen_load_toc_v4_PIC_1 (symF))); - rs6000_maybe_dead (emit_move_insn (dest, - gen_rtx_REG (Pmode, - LINK_REGISTER_REGNUM))); - rs6000_maybe_dead (emit_insn (gen_load_toc_v4_PIC_2 (temp0, dest, - symL, - symF))); + emit_insn (gen_load_toc_v4_PIC_1 (symF)); + emit_move_insn (dest, + gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM)); + emit_insn (gen_load_toc_v4_PIC_2 (temp0, dest, symL, symF)); } else { @@ -14083,9 +14086,7 @@ rs6000_emit_load_toc_table (int fromprolog) gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM)); emit_move_insn (temp0, gen_rtx_MEM (Pmode, dest)); } - insn = emit_insn (gen_addsi3 (dest, temp0, dest)); - if (fromprolog) - rs6000_maybe_dead (insn); + emit_insn (gen_addsi3 (dest, temp0, dest)); } else if (TARGET_ELF && !TARGET_AIX && flag_pic == 0 && TARGET_MINIMAL_TOC) { @@ -14095,23 +14096,17 @@ rs6000_emit_load_toc_table (int fromprolog) ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 1); realsym = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf)); - insn = emit_insn (gen_elf_high (dest, realsym)); - if (fromprolog) - rs6000_maybe_dead (insn); - insn = emit_insn (gen_elf_low (dest, dest, realsym)); - if (fromprolog) - rs6000_maybe_dead (insn); + emit_insn (gen_elf_high (dest, realsym)); + emit_insn (gen_elf_low (dest, dest, realsym)); } else { gcc_assert (DEFAULT_ABI == ABI_AIX); if (TARGET_32BIT) - insn = emit_insn (gen_load_toc_aix_si (dest)); + emit_insn (gen_load_toc_aix_si (dest)); else - insn = emit_insn (gen_load_toc_aix_di (dest)); - if (fromprolog) - rs6000_maybe_dead (insn); + emit_insn (gen_load_toc_aix_di (dest)); } } @@ -14198,7 +14193,7 @@ rtx create_TOC_reference (rtx symbol) { if (no_new_pseudos) - regs_ever_live[TOC_REGISTER] = 1; + df_set_regs_ever_live (TOC_REGISTER, true); return gen_rtx_PLUS (Pmode, gen_rtx_REG (Pmode, TOC_REGISTER), gen_rtx_CONST (Pmode, @@ -14641,7 +14636,7 @@ no_global_regs_above (int first_greg) static bool rs6000_reg_live_or_pic_offset_p (int reg) { - return ((regs_ever_live[reg] + return ((df_regs_ever_live_p (reg) && (!call_used_regs[reg] || (reg == RS6000_PIC_OFFSET_TABLE_REGNUM && TARGET_TOC && TARGET_MINIMAL_TOC))) @@ -14881,7 +14876,7 @@ rs6000_emit_prologue (void) { int i; for (i = 0; i < 64 - info->first_fp_reg_save; i++) - if ((regs_ever_live[info->first_fp_reg_save+i] + if ((df_regs_ever_live_p (info->first_fp_reg_save+i) && ! call_used_regs[info->first_fp_reg_save+i])) emit_frame_save (frame_reg_rtx, frame_ptr_rtx, DFmode, info->first_fp_reg_save + i, @@ -14952,7 +14947,7 @@ rs6000_emit_prologue (void) int i; rtx spe_save_area_ptr; int using_static_chain_p = (cfun->static_chain_decl != NULL_TREE - && regs_ever_live[STATIC_CHAIN_REGNUM] + && df_regs_ever_live_p (STATIC_CHAIN_REGNUM) && !call_used_regs[STATIC_CHAIN_REGNUM]); /* Determine whether we can address all of the registers that need @@ -15206,7 +15201,7 @@ rs6000_emit_prologue (void) if ((TARGET_TOC && TARGET_MINIMAL_TOC && get_pool_size () != 0) || (DEFAULT_ABI == ABI_V4 && (flag_pic == 1 || (flag_pic && TARGET_SECURE_PLT)) - && regs_ever_live[RS6000_PIC_OFFSET_TABLE_REGNUM])) + && df_regs_ever_live_p (RS6000_PIC_OFFSET_TABLE_REGNUM))) { /* If emit_load_toc_table will use the link register, we need to save it. We use R12 for this purpose because emit_load_toc_table @@ -15222,13 +15217,11 @@ rs6000_emit_prologue (void) rtx lr = gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM); insn = emit_move_insn (frame_ptr_rtx, lr); - rs6000_maybe_dead (insn); RTX_FRAME_RELATED_P (insn) = 1; rs6000_emit_load_toc_table (TRUE); insn = emit_move_insn (lr, frame_ptr_rtx); - rs6000_maybe_dead (insn); RTX_FRAME_RELATED_P (insn) = 1; } else @@ -15244,17 +15237,16 @@ rs6000_emit_prologue (void) /* Save and restore LR locally around this call (in R0). */ if (!info->lr_save_p) - rs6000_maybe_dead (emit_move_insn (gen_rtx_REG (Pmode, 0), lr)); + emit_move_insn (gen_rtx_REG (Pmode, 0), lr); - rs6000_maybe_dead (emit_insn (gen_load_macho_picbase (src))); + emit_insn (gen_load_macho_picbase (src)); - insn = emit_move_insn (gen_rtx_REG (Pmode, - RS6000_PIC_OFFSET_TABLE_REGNUM), - lr); - rs6000_maybe_dead (insn); + emit_move_insn (gen_rtx_REG (Pmode, + RS6000_PIC_OFFSET_TABLE_REGNUM), + lr); if (!info->lr_save_p) - rs6000_maybe_dead (emit_move_insn (lr, gen_rtx_REG (Pmode, 0))); + emit_move_insn (lr, gen_rtx_REG (Pmode, 0)); } #endif } @@ -15673,7 +15665,7 @@ rs6000_emit_epilogue (int sibcall) /* Restore fpr's if we need to do it without calling a function. */ if (restoring_FPRs_inline) for (i = 0; i < 64 - info->first_fp_reg_save; i++) - if ((regs_ever_live[info->first_fp_reg_save+i] + if ((df_regs_ever_live_p (info->first_fp_reg_save+i) && ! call_used_regs[info->first_fp_reg_save+i])) { rtx addr, mem; @@ -15697,7 +15689,7 @@ rs6000_emit_epilogue (int sibcall) if (using_mtcr_multiple) { for (i = 0; i < 8; i++) - if (regs_ever_live[CR0_REGNO+i] && ! call_used_regs[CR0_REGNO+i]) + if (df_regs_ever_live_p (CR0_REGNO+i) && ! call_used_regs[CR0_REGNO+i]) count++; gcc_assert (count); } @@ -15711,7 +15703,7 @@ rs6000_emit_epilogue (int sibcall) ndx = 0; for (i = 0; i < 8; i++) - if (regs_ever_live[CR0_REGNO+i] && ! call_used_regs[CR0_REGNO+i]) + if (df_regs_ever_live_p (CR0_REGNO+i) && ! call_used_regs[CR0_REGNO+i]) { rtvec r = rtvec_alloc (2); RTVEC_ELT (r, 0) = r12_rtx; @@ -15726,7 +15718,7 @@ rs6000_emit_epilogue (int sibcall) } else for (i = 0; i < 8; i++) - if (regs_ever_live[CR0_REGNO+i] && ! call_used_regs[CR0_REGNO+i]) + if (df_regs_ever_live_p (CR0_REGNO+i) && ! call_used_regs[CR0_REGNO+i]) { emit_insn (gen_movsi_to_cr_one (gen_rtx_REG (CCmode, CR0_REGNO+i), diff --git a/gcc/config/rs6000/rs6000.h b/gcc/config/rs6000/rs6000.h index 509cd6e..5082665 100644 --- a/gcc/config/rs6000/rs6000.h +++ b/gcc/config/rs6000/rs6000.h @@ -888,7 +888,7 @@ extern enum rs6000_nop_insertion rs6000_sched_insert_nops; emitted the vrsave mask. */ #define HARD_REGNO_RENAME_OK(SRC, DST) \ - (! ALTIVEC_REGNO_P (DST) || regs_ever_live[DST]) + (! ALTIVEC_REGNO_P (DST) || df_regs_ever_live_p (DST)) /* A C expression returning the cost of moving data from a register of class CLASS1 to one of CLASS2. */ @@ -1598,6 +1598,8 @@ typedef struct rs6000_args #define HAVE_PRE_DECREMENT 1 #define HAVE_PRE_INCREMENT 1 +#define HAVE_PRE_MODIFY_DISP 1 +#define HAVE_PRE_MODIFY_REG 1 /* Macros to check register numbers against specific register classes. */ diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md index 477f12e..e445705 100644 --- a/gcc/config/rs6000/rs6000.md +++ b/gcc/config/rs6000/rs6000.md @@ -8364,7 +8364,8 @@ || (GET_CODE (operands[1]) == MEM && (GET_CODE (XEXP (operands[1], 0)) == LO_SUM || GET_CODE (XEXP (operands[1], 0)) == PRE_INC - || GET_CODE (XEXP (operands[1], 0)) == PRE_DEC))) + || GET_CODE (XEXP (operands[1], 0)) == PRE_DEC + || GET_CODE (XEXP (operands[1], 0)) == PRE_MODIFY))) { /* If the low-address word is used in the address, we must load it last. Otherwise, load it first. Note that we cannot have @@ -8374,7 +8375,7 @@ operands[1], 0)) return \"{l|lwz} %L0,%L1\;{l|lwz} %0,%1\"; else - return \"{l%U1|lwz%U1} %0,%1\;{l|lwz} %L0,%L1\"; + return \"{l%U1%X1|lwz%U1%X1} %0,%1\;{l|lwz} %L0,%L1\"; } else { @@ -8404,8 +8405,9 @@ || (GET_CODE (operands[0]) == MEM && (GET_CODE (XEXP (operands[0], 0)) == LO_SUM || GET_CODE (XEXP (operands[0], 0)) == PRE_INC - || GET_CODE (XEXP (operands[0], 0)) == PRE_DEC))) - return \"{st%U0|stw%U0} %1,%0\;{st|stw} %L1,%L0\"; + || GET_CODE (XEXP (operands[0], 0)) == PRE_DEC + || GET_CODE (XEXP (operands[0], 0)) == PRE_MODIFY))) + return \"{st%U0%X0|stw%U0%X0} %1,%0\;{st|stw} %L1,%L0\"; else { rtx addreg; @@ -8461,9 +8463,9 @@ operands[1], 0)) return \"{l|lwz} %L0,%L1\;{l|lwz} %0,%1\"; else - return \"{l%U1|lwz%U1} %0,%1\;{l|lwz} %L0,%L1\"; + return \"{l%U1%X1|lwz%U1%X1} %0,%1\;{l|lwz} %L0,%L1\"; case 2: - return \"{st%U0|stw%U0} %1,%0\;{st|stw} %L1,%L0\"; + return \"{st%U0%X0|stw%U0%X0} %1,%0\;{st|stw} %L1,%L0\"; case 3: case 4: case 5: diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c index ced302a..a6524fc 100644 --- a/gcc/config/s390/s390.c +++ b/gcc/config/s390/s390.c @@ -1,6 +1,6 @@ /* Subroutines used for code generation on IBM S/390 and zSeries - Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, + 2007 Free Software Foundation, Inc. Contributed by Hartmut Penner (hpenner@de.ibm.com) and Ulrich Weigand (uweigand@de.ibm.com). @@ -51,6 +51,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "langhooks.h" #include "optabs.h" #include "tree-gimple.h" +#include "df.h" /* Define the specific costs for a given cpu. */ @@ -2890,7 +2891,7 @@ legitimize_pic_address (rtx orig, rtx reg) rtx temp = reg? reg : gen_reg_rtx (Pmode); if (reload_in_progress || reload_completed) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTOFF); addr = gen_rtx_CONST (Pmode, addr); @@ -2916,7 +2917,7 @@ legitimize_pic_address (rtx orig, rtx reg) in both 31- and 64-bit code (@GOT). */ if (reload_in_progress || reload_completed) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT); new = gen_rtx_CONST (Pmode, new); @@ -2948,7 +2949,7 @@ legitimize_pic_address (rtx orig, rtx reg) rtx temp = gen_reg_rtx (Pmode); if (reload_in_progress || reload_completed) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT); addr = gen_rtx_CONST (Pmode, addr); @@ -2996,7 +2997,7 @@ legitimize_pic_address (rtx orig, rtx reg) rtx temp = reg? reg : gen_reg_rtx (Pmode); if (reload_in_progress || reload_completed) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); addr = XVECEXP (addr, 0, 0); addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), @@ -3076,7 +3077,7 @@ legitimize_pic_address (rtx orig, rtx reg) rtx temp = reg? reg : gen_reg_rtx (Pmode); if (reload_in_progress || reload_completed) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op0), UNSPEC_GOTOFF); @@ -3244,7 +3245,7 @@ legitimize_tls_address (rtx addr, rtx reg) in both 31- and 64-bit code. */ if (reload_in_progress || reload_completed) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF); new = gen_rtx_CONST (Pmode, new); @@ -3273,7 +3274,7 @@ legitimize_tls_address (rtx addr, rtx reg) from the literal pool. */ if (reload_in_progress || reload_completed) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF); new = gen_rtx_CONST (Pmode, new); @@ -6325,7 +6326,7 @@ find_unused_clobbered_reg (void) { int i; for (i = 0; i < 6; i++) - if (!regs_ever_live[i]) + if (!df_regs_ever_live_p (i)) return i; return 0; } @@ -6391,7 +6392,7 @@ s390_regs_ever_clobbered (int *regs_ever_clobbered) for (i = 0; EH_RETURN_DATA_REGNO (i) != INVALID_REGNUM ; i++) if (current_function_calls_eh_return || (cfun->machine->has_landing_pad_p - && regs_ever_live [EH_RETURN_DATA_REGNO (i)])) + && df_regs_ever_live_p (EH_RETURN_DATA_REGNO (i)))) regs_ever_clobbered[EH_RETURN_DATA_REGNO (i)] = 1; /* For nonlocal gotos all call-saved registers have to be saved. @@ -6470,7 +6471,7 @@ s390_register_info (int clobbered_regs[]) cfun_frame_layout.high_fprs = 0; if (TARGET_64BIT) for (i = 24; i < 32; i++) - if (regs_ever_live[i] && !global_regs[i]) + if (df_regs_ever_live_p (i) && !global_regs[i]) { cfun_set_fpr_bit (i - 16); cfun_frame_layout.high_fprs++; @@ -6492,7 +6493,7 @@ s390_register_info (int clobbered_regs[]) if (flag_pic) clobbered_regs[PIC_OFFSET_TABLE_REGNUM] - |= regs_ever_live[PIC_OFFSET_TABLE_REGNUM]; + |= df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM); clobbered_regs[BASE_REGNUM] |= (cfun->machine->base_reg @@ -6515,10 +6516,10 @@ s390_register_info (int clobbered_regs[]) || current_function_stdarg); for (i = 6; i < 16; i++) - if (regs_ever_live[i] || clobbered_regs[i]) + if (df_regs_ever_live_p (i) || clobbered_regs[i]) break; for (j = 15; j > i; j--) - if (regs_ever_live[j] || clobbered_regs[j]) + if (df_regs_ever_live_p (j) || clobbered_regs[j]) break; if (i == 16) @@ -6612,7 +6613,7 @@ s390_register_info (int clobbered_regs[]) if (!TARGET_64BIT) for (i = 2; i < 4; i++) - if (regs_ever_live[i + 16] && !global_regs[i + 16]) + if (df_regs_ever_live_p (i + 16) && !global_regs[i + 16]) cfun_set_fpr_bit (i); } @@ -6758,7 +6759,7 @@ s390_init_frame_layout (void) as base register to avoid save/restore overhead. */ if (!base_used) cfun->machine->base_reg = NULL_RTX; - else if (current_function_is_leaf && !regs_ever_live[5]) + else if (current_function_is_leaf && !df_regs_ever_live_p (5)) cfun->machine->base_reg = gen_rtx_REG (Pmode, 5); else cfun->machine->base_reg = gen_rtx_REG (Pmode, BASE_REGNUM); @@ -6781,12 +6782,15 @@ s390_update_frame_layout (void) s390_register_info (clobbered_regs); - regs_ever_live[BASE_REGNUM] = clobbered_regs[BASE_REGNUM]; - regs_ever_live[RETURN_REGNUM] = clobbered_regs[RETURN_REGNUM]; - regs_ever_live[STACK_POINTER_REGNUM] = clobbered_regs[STACK_POINTER_REGNUM]; + df_set_regs_ever_live (BASE_REGNUM, + clobbered_regs[BASE_REGNUM] ? true : false); + df_set_regs_ever_live (RETURN_REGNUM, + clobbered_regs[RETURN_REGNUM] ? true : false); + df_set_regs_ever_live (STACK_POINTER_REGNUM, + clobbered_regs[STACK_POINTER_REGNUM] ? true : false); if (cfun->machine->base_reg) - regs_ever_live[REGNO (cfun->machine->base_reg)] = 1; + df_set_regs_ever_live (REGNO (cfun->machine->base_reg), true); } /* Return true if it is legal to put a value with MODE into REGNO. */ @@ -7169,7 +7173,10 @@ s390_emit_prologue (void) for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) if (INSN_P (insn)) - annotate_constant_pool_refs (&PATTERN (insn)); + { + annotate_constant_pool_refs (&PATTERN (insn)); + df_insn_rescan (insn); + } pop_topmost_sequence (); @@ -7407,17 +7414,12 @@ s390_emit_prologue (void) /* Set up got pointer, if needed. */ - if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) + if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM)) { rtx insns = s390_load_got (); for (insn = insns; insn; insn = NEXT_INSN (insn)) - { - annotate_constant_pool_refs (&PATTERN (insn)); - - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX, - REG_NOTES (insn)); - } + annotate_constant_pool_refs (&PATTERN (insn)); emit_insn (insns); } diff --git a/gcc/config/score/score-mdaux.c b/gcc/config/score/score-mdaux.c index e8d1478..cb77082 100644 --- a/gcc/config/score/score-mdaux.c +++ b/gcc/config/score/score-mdaux.c @@ -1,5 +1,5 @@ /* score-mdaux.c for Sunplus S+CORE processor - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Sunnorth This file is part of GCC. @@ -126,7 +126,7 @@ static int score_save_reg_p (unsigned int regno) { /* Check call-saved registers. */ - if (regs_ever_live[regno] && !call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && !call_used_regs[regno]) return 1; /* We need to save the old frame pointer before setting up a new one. */ @@ -135,7 +135,7 @@ score_save_reg_p (unsigned int regno) /* We need to save the incoming return address if it is ever clobbered within the function. */ - if (regno == RA_REGNUM && regs_ever_live[regno]) + if (regno == RA_REGNUM && df_regs_ever_live_p (regno)) return 1; return 0; diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c index d8825d0..4ca2d5c 100644 --- a/gcc/config/sh/sh.c +++ b/gcc/config/sh/sh.c @@ -1,6 +1,6 @@ /* Output routines for GCC for Renesas / SuperH SH. Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, - 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Steve Chamberlain (sac@cygnus.com). Improved by Jim Wilson (wilson@cygnus.com). @@ -47,6 +47,7 @@ Boston, MA 02110-1301, USA. */ #include "real.h" #include "langhooks.h" #include "basic-block.h" +#include "df.h" #include "cfglayout.h" #include "intl.h" #include "sched-int.h" @@ -88,6 +89,9 @@ static short *regmode_weight[2]; /* Total SFmode and SImode weights of scheduled insns. */ static int curr_regmode_pressure[2]; +/* Number of r0 life regions. */ +static int r0_life_regions; + /* If true, skip cycles for Q -> R movement. */ static int skip_cycles = 0; @@ -195,6 +199,7 @@ static int sh_dfa_new_cycle (FILE *, int, rtx, int, int, int *sort_p); static short find_set_regmode_weight (rtx, enum machine_mode); static short find_insn_regmode_weight (rtx, enum machine_mode); static void find_regmode_weight (basic_block, enum machine_mode); +static int find_r0_life_regions (basic_block); static void sh_md_init_global (FILE *, int, int); static void sh_md_finish_global (FILE *, int); static int rank_for_reorder (const void *, const void *); @@ -4757,51 +4762,25 @@ sh_reorg (void) if (GET_CODE (reg) != REG) continue; - /* This is a function call via REG. If the only uses of REG - between the time that it is set and the time that it dies - are in function calls, then we can associate all the - function calls with the setting of REG. */ - - for (link = LOG_LINKS (insn); link; link = XEXP (link, 1)) - { - rtx linked_insn; - - if (REG_NOTE_KIND (link) != 0) - continue; - linked_insn = XEXP (link, 0); - set = single_set (linked_insn); - if (set - && rtx_equal_p (reg, SET_DEST (set)) - && ! INSN_DELETED_P (linked_insn)) - { - link = linked_insn; - break; - } - } - - if (! link) + /* Try scanning backward to find where the register is set. */ + link = NULL; + for (scan = PREV_INSN (insn); + scan && GET_CODE (scan) != CODE_LABEL; + scan = PREV_INSN (scan)) { - /* ??? Sometimes global register allocation will have - deleted the insn pointed to by LOG_LINKS. Try - scanning backward to find where the register is set. */ - for (scan = PREV_INSN (insn); - scan && GET_CODE (scan) != CODE_LABEL; - scan = PREV_INSN (scan)) - { - if (! INSN_P (scan)) - continue; + if (! INSN_P (scan)) + continue; - if (! reg_mentioned_p (reg, scan)) - continue; + if (! reg_mentioned_p (reg, scan)) + continue; - if (noncall_uses_reg (reg, scan, &set)) - break; + if (noncall_uses_reg (reg, scan, &set)) + break; - if (set) - { - link = scan; - break; - } + if (set) + { + link = scan; + break; } } @@ -4833,7 +4812,7 @@ sh_reorg (void) /* Don't try to trace forward past a CODE_LABEL if we haven't seen INSN yet. Ordinarily, we will only find the setting insn - in LOG_LINKS if it is in the same basic block. However, + if it is in the same basic block. However, cross-jumping can insert code labels in between the load and the call, and can result in situations where a single call insn may have two targets depending on where we came from. */ @@ -4880,11 +4859,8 @@ sh_reorg (void) later insn. */ /* ??? We shouldn't have to use FOUNDINSN here. - However, the LOG_LINKS fields are apparently not - entirely reliable around libcalls; - newlib/libm/math/e_pow.c is a test case. Sometimes - an insn will appear in LOG_LINKS even though it is - not the most recent insn which sets the register. */ + This dates back to when we used LOG_LINKS to find + the most recent insn which sets the register. */ if (foundinsn && (scanset @@ -5849,12 +5825,12 @@ calc_live_regs (HARD_REG_SET *live_regs_mask) CLEAR_HARD_REG_SET (*live_regs_mask); if ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && TARGET_FMOVD && interrupt_handler - && regs_ever_live[FPSCR_REG]) + && df_regs_ever_live_p (FPSCR_REG)) target_flags &= ~MASK_FPU_SINGLE; /* If we can save a lot of saves by switching to double mode, do that. */ else if ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && TARGET_FMOVD && TARGET_FPU_SINGLE) for (count = 0, reg = FIRST_FP_REG; reg <= LAST_FP_REG; reg += 2) - if (regs_ever_live[reg] && regs_ever_live[reg+1] + if (df_regs_ever_live_p (reg) && df_regs_ever_live_p (reg+1) && (! call_really_used_regs[reg] || interrupt_handler) && ++count > 2) @@ -5876,11 +5852,11 @@ calc_live_regs (HARD_REG_SET *live_regs_mask) pr_live = (pr_initial ? (GET_CODE (pr_initial) != REG || REGNO (pr_initial) != (PR_REG)) - : regs_ever_live[PR_REG]); + : df_regs_ever_live_p (PR_REG)); /* For Shcompact, if not optimizing, we end up with a memory reference using the return address pointer for __builtin_return_address even though there is no actual need to put the PR register on the stack. */ - pr_live |= regs_ever_live[RETURN_ADDRESS_POINTER_REGNUM]; + pr_live |= df_regs_ever_live_p (RETURN_ADDRESS_POINTER_REGNUM); } /* Force PR to be live if the prologue has to call the SHmedia argument decoder or register saver. */ @@ -5896,7 +5872,7 @@ calc_live_regs (HARD_REG_SET *live_regs_mask) ? pr_live : interrupt_handler ? (/* Need to save all the regs ever live. */ - (regs_ever_live[reg] + (df_regs_ever_live_p (reg) || (call_really_used_regs[reg] && (! fixed_regs[reg] || reg == MACH_REG || reg == MACL_REG || reg == PIC_OFFSET_TABLE_REGNUM) @@ -5914,7 +5890,7 @@ calc_live_regs (HARD_REG_SET *live_regs_mask) && flag_pic && current_function_args_info.call_cookie && reg == PIC_OFFSET_TABLE_REGNUM) - || (regs_ever_live[reg] + || (df_regs_ever_live_p (reg) && (!call_really_used_regs[reg] || (trapa_handler && reg == FPSCR_REG && TARGET_FPU_ANY))) || (current_function_calls_eh_return @@ -5923,7 +5899,7 @@ calc_live_regs (HARD_REG_SET *live_regs_mask) || reg == EH_RETURN_DATA_REGNO (2) || reg == EH_RETURN_DATA_REGNO (3))) || ((reg == MACL_REG || reg == MACH_REG) - && regs_ever_live[reg] + && df_regs_ever_live_p (reg) && sh_cfun_attr_renesas_p ()) )) { @@ -5935,7 +5911,7 @@ calc_live_regs (HARD_REG_SET *live_regs_mask) { if (FP_REGISTER_P (reg)) { - if (! TARGET_FPU_SINGLE && ! regs_ever_live[reg ^ 1]) + if (! TARGET_FPU_SINGLE && ! df_regs_ever_live_p (reg ^ 1)) { SET_HARD_REG_BIT (*live_regs_mask, (reg ^ 1)); count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg ^ 1)); @@ -6012,10 +5988,10 @@ sh_media_register_for_return (void) if (sh_cfun_interrupt_handler_p ()) return -1; - tr0_used = flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]; + tr0_used = flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM); for (regno = FIRST_TARGET_REG + tr0_used; regno <= LAST_TARGET_REG; regno++) - if (call_really_used_regs[regno] && ! regs_ever_live[regno]) + if (call_really_used_regs[regno] && ! df_regs_ever_live_p (regno)) return regno; return -1; @@ -6174,7 +6150,7 @@ sh_expand_prologue (void) incoming-argument decoder and/or of the return trampoline from the GOT, so make sure the PIC register is preserved and initialized. */ - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); if (TARGET_SHCOMPACT && (current_function_args_info.call_cookie & ~ CALL_COOKIE_RET_TRAMP(1))) @@ -6207,19 +6183,8 @@ sh_expand_prologue (void) int tr = sh_media_register_for_return (); if (tr >= 0) - { - rtx insn = emit_move_insn (gen_rtx_REG (DImode, tr), - gen_rtx_REG (DImode, PR_MEDIA_REG)); - - /* ??? We should suppress saving pr when we don't need it, but this - is tricky because of builtin_return_address. */ - - /* If this function only exits with sibcalls, this copy - will be flagged as dead. */ - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, - const0_rtx, - REG_NOTES (insn)); - } + emit_move_insn (gen_rtx_REG (DImode, tr), + gen_rtx_REG (DImode, PR_MEDIA_REG)); } /* Emit the code for SETUP_VARARGS. */ @@ -6467,24 +6432,8 @@ sh_expand_prologue (void) else push_regs (&live_regs_mask, current_function_interrupt); - if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) - { - rtx insn = get_last_insn (); - rtx last = emit_insn (gen_GOTaddr2picreg ()); - - /* Mark these insns as possibly dead. Sometimes, flow2 may - delete all uses of the PIC register. In this case, let it - delete the initialization too. */ - do - { - insn = NEXT_INSN (insn); - - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, - const0_rtx, - REG_NOTES (insn)); - } - while (insn != last); - } + if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM)) + emit_insn (gen_GOTaddr2picreg ()); if (SHMEDIA_REGS_STACK_ADJUST ()) { @@ -6499,16 +6448,7 @@ sh_expand_prologue (void) } if (target_flags != save_flags && ! current_function_interrupt) - { - rtx insn = emit_insn (gen_toggle_sz ()); - - /* If we're lucky, a mode switch in the function body will - overwrite fpscr, turning this insn dead. Tell flow this - insn is ok to delete. */ - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, - const0_rtx, - REG_NOTES (insn)); - } + emit_insn (gen_toggle_sz ()); target_flags = save_flags; @@ -6729,11 +6669,6 @@ sh_expand_epilogue (bool sibcall_p) } insn = emit_move_insn (reg_rtx, mem_rtx); - if (reg == PR_MEDIA_REG && sh_media_register_for_return () >= 0) - /* This is dead, unless we return with a sibcall. */ - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, - const0_rtx, - REG_NOTES (insn)); } gcc_assert (entry->offset + offset_base == d + d_rounding); @@ -6742,7 +6677,11 @@ sh_expand_epilogue (bool sibcall_p) { save_size = 0; if (TEST_HARD_REG_BIT (live_regs_mask, PR_REG)) - pop (PR_REG); + { + if (!frame_pointer_needed) + emit_insn (gen_blockage ()); + pop (PR_REG); + } for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) { int j = (FIRST_PSEUDO_REGISTER - 1) - i; @@ -8799,7 +8738,7 @@ sh_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED, saved by the prologue, even if they would normally be call-clobbered. */ - if (sh_cfun_interrupt_handler_p () && !regs_ever_live[new_reg]) + if (sh_cfun_interrupt_handler_p () && !df_regs_ever_live_p (new_reg)) return 0; return 1; @@ -9039,7 +8978,7 @@ flow_dependent_p_1 (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data) static int sh_pr_n_sets (void) { - return REG_N_SETS (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG); + return DF_REG_DEF_COUNT (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG); } /* Return where to allocate pseudo for a given hard register initial @@ -9206,6 +9145,56 @@ ready_reorder (rtx *ready, int nready) SCHED_REORDER (ready, nready); } +/* Count life regions of r0 for a block. */ +static int +find_r0_life_regions (basic_block b) +{ + rtx end, insn; + rtx pset; + rtx r0_reg; + int live; + int set; + int death = 0; + + if (REGNO_REG_SET_P (DF_LIVE_IN (b), R0_REG)) + { + set = 1; + live = 1; + } + else + { + set = 0; + live = 0; + } + + insn = BB_HEAD (b); + end = BB_END (b); + r0_reg = gen_rtx_REG (SImode, R0_REG); + while (1) + { + if (INSN_P (insn)) + { + if (find_regno_note (insn, REG_DEAD, R0_REG)) + { + death++; + live = 0; + } + if (!live + && (pset = single_set (insn)) + && reg_overlap_mentioned_p (r0_reg, SET_DEST (pset)) + && !find_regno_note (insn, REG_UNUSED, R0_REG)) + { + set++; + live = 1; + } + } + if (insn == end) + break; + insn = NEXT_INSN (insn); + } + return set - death; +} + /* Calculate regmode weights for all insns of all basic block. */ static void sh_md_init_global (FILE *dump ATTRIBUTE_UNUSED, @@ -9216,11 +9205,14 @@ sh_md_init_global (FILE *dump ATTRIBUTE_UNUSED, regmode_weight[0] = (short *) xcalloc (old_max_uid, sizeof (short)); regmode_weight[1] = (short *) xcalloc (old_max_uid, sizeof (short)); + r0_life_regions = 0; FOR_EACH_BB_REVERSE (b) { find_regmode_weight (b, SImode); find_regmode_weight (b, SFmode); + if (!reload_completed) + r0_life_regions += find_r0_life_regions (b); } CURR_REGMODE_PRESSURE (SImode) = 0; @@ -9281,7 +9273,6 @@ sh_md_init (FILE *dump ATTRIBUTE_UNUSED, /* Pressure on register r0 can lead to spill failures. so avoid sched1 for functions that already have high pressure on r0. */ #define R0_MAX_LIFE_REGIONS 2 -#define R0_MAX_LIVE_LENGTH 12 /* Register Pressure thresholds for SImode and SFmode registers. */ #define SIMODE_MAX_WEIGHT 5 #define SFMODE_MAX_WEIGHT 10 @@ -9292,9 +9283,8 @@ high_pressure (enum machine_mode mode) { /* Pressure on register r0 can lead to spill failures. so avoid sched1 for functions that already have high pressure on r0. */ - if ((REG_N_SETS (0) - REG_N_DEATHS (0)) >= R0_MAX_LIFE_REGIONS - && REG_LIVE_LENGTH (0) >= R0_MAX_LIVE_LENGTH) - return 1; + if (r0_life_regions >= R0_MAX_LIFE_REGIONS) + return 1; if (mode == SFmode) return (CURR_REGMODE_PRESSURE (SFmode) > SFMODE_MAX_WEIGHT); @@ -10275,6 +10265,7 @@ sh_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, insn_locators_alloc (); insns = get_insns (); +#if 0 if (optimize > 0) { /* Initialize the bitmap obstacks. */ @@ -10301,6 +10292,14 @@ sh_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, else if (flag_pic) split_all_insns_noflow (); } +#else + if (optimize > 0) + { + if (! cfun->cfg) + init_flow (); + split_all_insns_noflow (); + } +#endif sh_reorg (); @@ -10312,15 +10311,21 @@ sh_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, final (insns, file, 1); final_end_function (); +#if 0 if (optimize > 0) { - /* Release all memory allocated by flow. */ - free_basic_block_vars (); + /* Release all memory allocated by df. */ + if (rtl_df) + { + df_finish (rtl_df); + rtl_df = NULL; + } /* Release the bitmap obstacks. */ bitmap_obstack_release (®_obstack); bitmap_obstack_release (NULL); } +#endif reload_completed = 0; epilogue_completed = 0; diff --git a/gcc/config/sh/sh.md b/gcc/config/sh/sh.md index 20653b1..e85470e 100644 --- a/gcc/config/sh/sh.md +++ b/gcc/config/sh/sh.md @@ -1,6 +1,6 @@ ;;- Machine description for Renesas / SuperH SH. ;; Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -;; 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +;; 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. ;; Contributed by Steve Chamberlain (sac@cygnus.com). ;; Improved by Jim Wilson (wilson@cygnus.com). @@ -135,7 +135,6 @@ (UNSPEC_FSINA 16) (UNSPEC_NSB 17) (UNSPEC_ALLOCO 18) - (UNSPEC_EH_RETURN 19) (UNSPEC_TLSGD 20) (UNSPEC_TLSLDM 21) (UNSPEC_TLSIE 22) @@ -163,6 +162,7 @@ (UNSPECV_CONST8 6) (UNSPECV_WINDOW_END 10) (UNSPECV_CONST_END 11) + (UNSPECV_EH_RETURN 12) ]) ;; ------------------------------------------------------------------------- @@ -8110,15 +8110,197 @@ label: DONE; }") -(define_expand "sibcall_value" - [(set (match_operand 0 "" "") - (call (match_operand 1 "" "") +(define_insn "sibcall_valuei" + [(set (match_operand 0 "" "=rf") + (call (mem:SI (match_operand:SI 1 "register_operand" "k")) + (match_operand 2 "" ""))) + (use (reg:PSI FPSCR_REG)) + (return)] + "TARGET_SH1" + "jmp @%1%#" + [(set_attr "needs_delay_slot" "yes") + (set (attr "fp_mode") + (if_then_else (eq_attr "fpu_single" "yes") + (const_string "single") (const_string "double"))) + (set_attr "type" "jump_ind")]) + +(define_insn "sibcall_valuei_pcrel" + [(set (match_operand 0 "" "=rf") + (call (mem:SI (match_operand:SI 1 "arith_reg_operand" "k")) + (match_operand 2 "" ""))) + (use (match_operand 3 "" "")) + (use (reg:PSI FPSCR_REG)) + (return)] + "TARGET_SH2" + "braf %1\\n%O3:%#" + [(set_attr "needs_delay_slot" "yes") + (set (attr "fp_mode") + (if_then_else (eq_attr "fpu_single" "yes") + (const_string "single") (const_string "double"))) + (set_attr "type" "jump_ind")]) + +(define_insn_and_split "sibcall_value_pcrel" + [(set (match_operand 0 "" "=rf") + (call (mem:SI (match_operand:SI 1 "symbol_ref_operand" "")) (match_operand 2 "" ""))) - (match_operand 3 "" "")] + (use (reg:PSI FPSCR_REG)) + (clobber (match_scratch:SI 3 "=k")) + (return)] + "TARGET_SH2" + "#" + "reload_completed" + [(const_int 0)] + " +{ + rtx lab = PATTERN (gen_call_site ()); + rtx call_insn; + + emit_insn (gen_sym_label2reg (operands[3], operands[1], lab)); + call_insn = emit_call_insn (gen_sibcall_valuei_pcrel (operands[0], + operands[3], + operands[2], + copy_rtx (lab))); + SIBLING_CALL_P (call_insn) = 1; + DONE; +}" + [(set_attr "needs_delay_slot" "yes") + (set (attr "fp_mode") + (if_then_else (eq_attr "fpu_single" "yes") + (const_string "single") (const_string "double"))) + (set_attr "type" "jump_ind")]) + +(define_insn "sibcall_value_compact" + [(set (match_operand 0 "" "=rf,rf") + (call (mem:SI (match_operand:SI 1 "register_operand" "k,k")) + (match_operand 2 "" ""))) + (return) + (use (match_operand:SI 3 "register_operand" "z,x")) + (use (reg:SI R1_REG)) + (use (reg:PSI FPSCR_REG)) + ;; We want to make sure the `x' above will only match MACH_REG + ;; because sibcall_epilogue may clobber MACL_REG. + (clobber (reg:SI MACL_REG))] + "TARGET_SHCOMPACT" + "@ + jmp @%1%# + jmp @%1\\n sts %3, r0" + [(set_attr "needs_delay_slot" "yes,no") + (set_attr "length" "2,4") + (set (attr "fp_mode") (const_string "single")) + (set_attr "type" "jump_ind")]) + +(define_insn "sibcall_value_media" + [(set (match_operand 0 "" "=rf") + (call (mem:DI (match_operand 1 "target_reg_operand" "k")) + (match_operand 2 "" ""))) + (use (reg:SI PR_MEDIA_REG)) + (return)] + "TARGET_SHMEDIA" + "blink %1, r63" + [(set_attr "type" "jump_media")]) + +(define_expand "sibcall_value" + [(parallel + [(set (match_operand 0 "arith_reg_operand" "") + (call (mem:SI (match_operand 1 "arith_reg_operand" "")) + (match_operand 2 "" ""))) + (match_operand 3 "" "") + (use (reg:PSI FPSCR_REG)) + (return)])] "" " { - emit_call_insn (gen_sibcall (operands[1], operands[2], operands[3])); + if (TARGET_SHMEDIA) + { + operands[1] = shmedia_prepare_call_address (operands[1], 1); + emit_call_insn (gen_sibcall_value_media (operands[0], operands[1], + operands[2])); + DONE; + } + else if (TARGET_SHCOMPACT && operands[3] + && (INTVAL (operands[3]) & ~ CALL_COOKIE_RET_TRAMP (1))) + { + rtx cookie_rtx = operands[3]; + long cookie = INTVAL (cookie_rtx); + rtx func = XEXP (operands[1], 0); + rtx mach, r1; + + if (flag_pic) + { + if (GET_CODE (func) == SYMBOL_REF && ! SYMBOL_REF_LOCAL_P (func)) + { + rtx reg = gen_reg_rtx (Pmode); + + emit_insn (gen_symGOT2reg (reg, func)); + func = reg; + } + else + func = legitimize_pic_address (func, Pmode, 0); + } + + /* FIXME: if we could tell whether all argument registers are + already taken, we could decide whether to force the use of + MACH_REG or to stick to R0_REG. Unfortunately, there's no + simple way to tell. We could use the CALL_COOKIE, but we + can't currently tell a register used for regular argument + passing from one that is unused. If we leave it up to reload + to decide which register to use, it seems to always choose + R0_REG, which leaves no available registers in SIBCALL_REGS + to hold the address of the trampoline. */ + mach = gen_rtx_REG (SImode, MACH_REG); + r1 = gen_rtx_REG (SImode, R1_REG); + + /* Since such a call function may use all call-clobbered + registers, we force a mode switch earlier, so that we don't + run out of registers when adjusting fpscr for the call. */ + emit_insn (gen_force_mode_for_call ()); + + operands[1] + = function_symbol (NULL, \"__GCC_shcompact_call_trampoline\", + SFUNC_GOT); + operands[1] = force_reg (SImode, operands[1]); + + /* We don't need a return trampoline, since the callee will + return directly to the upper caller. */ + if (cookie & CALL_COOKIE_RET_TRAMP (1)) + { + cookie &= ~ CALL_COOKIE_RET_TRAMP (1); + cookie_rtx = GEN_INT (cookie); + } + + emit_move_insn (mach, func); + emit_move_insn (r1, cookie_rtx); + + emit_call_insn (gen_sibcall_value_compact (operands[0], operands[1], + operands[2], mach)); + DONE; + } + else if (TARGET_SHCOMPACT && flag_pic + && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF + && ! SYMBOL_REF_LOCAL_P (XEXP (operands[1], 0))) + { + rtx reg = gen_reg_rtx (Pmode); + + emit_insn (gen_symGOT2reg (reg, XEXP (operands[1], 0))); + XEXP (operands[1], 0) = reg; + } + if (flag_pic && TARGET_SH2 + && GET_CODE (operands[1]) == MEM + && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF + /* The PLT needs the PIC register, but the epilogue would have + to restore it, so we can only use PC-relative PIC calls for + static functions. */ + && SYMBOL_REF_LOCAL_P (XEXP (operands[1], 0))) + { + emit_call_insn (gen_sibcall_value_pcrel (operands[0], + XEXP (operands[1], 0), + operands[2])); + DONE; + } + else + operands[1] = force_reg (SImode, XEXP (operands[1], 0)); + + emit_call_insn (gen_sibcall_valuei (operands[0], operands[1], operands[2])); DONE; }") @@ -8239,19 +8421,14 @@ label: { rtx r0 = gen_rtx_REG (SImode, R0_REG); rtx tmp = gen_rtx_REG (SImode, MACL_REG); - rtx i; /* We can't tell at this point whether the sibcall is a sibcall_compact and, if it is, whether it uses r0 or mach as operand 2, so let the instructions that preserve r0 be optimized away if r0 turns out to be dead. */ - i = emit_insn_before (gen_rtx_SET (SImode, tmp, r0), insn); - REG_NOTES (i) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, - REG_NOTES (i)); - i = emit_move_insn (r0, tmp); - REG_NOTES (i) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, - REG_NOTES (i)); + emit_insn_before (gen_rtx_SET (SImode, tmp, r0), insn); + emit_move_insn (r0, tmp); break; } } @@ -9226,19 +9403,22 @@ mov.l\\t1f,r0\\n\\ ;; until we know where it will be put in the stack frame. (define_insn "eh_set_ra_si" - [(unspec [(match_operand:SI 0 "register_operand" "r")] UNSPEC_EH_RETURN) + [(unspec_volatile [(match_operand:SI 0 "register_operand" "r")] + UNSPECV_EH_RETURN) (clobber (match_scratch:SI 1 "=&r"))] "! TARGET_SHMEDIA64" "#") (define_insn "eh_set_ra_di" - [(unspec [(match_operand:DI 0 "register_operand" "r")] UNSPEC_EH_RETURN) + [(unspec_volatile [(match_operand:DI 0 "register_operand" "r")] + UNSPECV_EH_RETURN) (clobber (match_scratch:DI 1 "=&r"))] "TARGET_SHMEDIA64" "#") (define_split - [(unspec [(match_operand 0 "register_operand" "")] UNSPEC_EH_RETURN) + [(unspec_volatile [(match_operand 0 "register_operand" "")] + UNSPECV_EH_RETURN) (clobber (match_scratch 1 ""))] "reload_completed" [(const_int 0)] @@ -10167,7 +10347,7 @@ mov.l\\t1f,r0\\n\\ [(set (reg:PSI FPSCR_REG) (mem:PSI (match_operand:SI 0 "register_operand" "")))] "(TARGET_SH4 || TARGET_SH2A_DOUBLE) - && (flag_peephole2 ? flow2_completed : reload_completed)" + && (flag_peephole2 ? epilogue_completed : reload_completed)" [(const_int 0)] { rtx fpscr, mem, new_insn; diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c index f94af2a..afe2424 100644 --- a/gcc/config/sparc/sparc.c +++ b/gcc/config/sparc/sparc.c @@ -1,6 +1,6 @@ /* Subroutines for insn-output.c for SPARC. Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Michael Tiemann (tiemann@cygnus.com) 64-bit SPARC-V9 support by Michael Tiemann, Jim Wilson, and Doug Evans, @@ -52,6 +52,7 @@ Boston, MA 02110-1301, USA. */ #include "tree-gimple.h" #include "langhooks.h" #include "params.h" +#include "df.h" /* Processor costs */ static const @@ -3742,20 +3743,20 @@ sparc_compute_frame_size (HOST_WIDE_INT size, int leaf_function_p) if (TARGET_ARCH64) { for (i = 0; i < 8; i++) - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_used_regs[i]) n_regs += 2; } else { for (i = 0; i < 8; i += 2) - if ((regs_ever_live[i] && ! call_used_regs[i]) - || (regs_ever_live[i+1] && ! call_used_regs[i+1])) + if ((df_regs_ever_live_p (i) && ! call_used_regs[i]) + || (df_regs_ever_live_p (i+1) && ! call_used_regs[i+1])) n_regs += 2; } for (i = 32; i < (TARGET_V9 ? 96 : 64); i += 2) - if ((regs_ever_live[i] && ! call_used_regs[i]) - || (regs_ever_live[i+1] && ! call_used_regs[i+1])) + if ((df_regs_ever_live_p (i) && ! call_used_regs[i]) + || (df_regs_ever_live_p (i+1) && ! call_used_regs[i+1])) n_regs += 2; /* Set up values for use in prologue and epilogue. */ @@ -3798,7 +3799,7 @@ sparc_output_scratch_registers (FILE *file ATTRIBUTE_UNUSED) .register being printed for them already. */ for (i = 2; i < 8; i++) { - if (regs_ever_live [i] + if (df_regs_ever_live_p (i) && ! sparc_hard_reg_printed [i]) { sparc_hard_reg_printed [i] = 1; @@ -3829,7 +3830,7 @@ save_or_restore_regs (int low, int high, rtx base, int offset, int action) { for (i = low; i < high; i++) { - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_used_regs[i]) { mem = gen_rtx_MEM (DImode, plus_constant (base, offset)); set_mem_alias_set (mem, sparc_sr_alias_set); @@ -3848,8 +3849,8 @@ save_or_restore_regs (int low, int high, rtx base, int offset, int action) { for (i = low; i < high; i += 2) { - bool reg0 = regs_ever_live[i] && ! call_used_regs[i]; - bool reg1 = regs_ever_live[i+1] && ! call_used_regs[i+1]; + bool reg0 = df_regs_ever_live_p (i) && ! call_used_regs[i]; + bool reg1 = df_regs_ever_live_p (i+1) && ! call_used_regs[i+1]; enum machine_mode mode; int regno; @@ -6509,7 +6510,7 @@ order_regs_for_local_alloc (void) { static int last_order_nonleaf = 1; - if (regs_ever_live[15] != last_order_nonleaf) + if (df_regs_ever_live_p (15) != last_order_nonleaf) { last_order_nonleaf = !last_order_nonleaf; memcpy ((char *) reg_alloc_order, @@ -7673,7 +7674,7 @@ sparc_check_64 (rtx x, rtx insn) y = gen_rtx_REG (SImode, REGNO (x) + WORDS_BIG_ENDIAN); if (flag_expensive_optimizations - && REG_N_SETS (REGNO (y)) == 1) + && DF_REG_DEF_COUNT (REGNO (y)) == 1) set_once = 1; if (insn == 0) diff --git a/gcc/config/spu/spu.c b/gcc/config/spu/spu.c index 5e1626f..fed243f 100644 --- a/gcc/config/spu/spu.c +++ b/gcc/config/spu/spu.c @@ -1387,7 +1387,6 @@ print_operand (FILE * file, rtx x, int code) } extern char call_used_regs[]; -extern char regs_ever_live[]; /* For PIC mode we've reserved PIC_OFFSET_TABLE_REGNUM, which is a caller saved register. For leaf functions it is more efficient to @@ -1517,13 +1516,13 @@ spu_split_immediate (rtx * ops) static int need_to_save_reg (int regno, int saving) { - if (regs_ever_live[regno] && !call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && !call_used_regs[regno]) return 1; if (flag_pic && regno == PIC_OFFSET_TABLE_REGNUM && (!saving || current_function_uses_pic_offset_table) && (!saving - || !current_function_is_leaf || regs_ever_live[LAST_ARG_REGNUM])) + || !current_function_is_leaf || df_regs_ever_live_p (LAST_ARG_REGNUM))) return 1; return 0; } @@ -1571,16 +1570,11 @@ frame_emit_add_imm (rtx dst, rtx src, HOST_WIDE_INT imm, rtx scratch) } else { - insn = emit_insn (gen_movsi (scratch, gen_int_mode (imm, SImode))); - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, - REG_NOTES (insn)); + emit_insn (gen_movsi (scratch, gen_int_mode (imm, SImode))); insn = emit_insn (gen_addsi3 (dst, src, scratch)); if (REGNO (src) == REGNO (scratch)) abort (); } - if (REGNO (dst) == REGNO (scratch)) - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, - REG_NOTES (insn)); return insn; } @@ -1688,11 +1682,7 @@ spu_expand_prologue (void) { rtx pic_reg = get_pic_reg (); insn = emit_insn (gen_load_pic_offset (pic_reg, scratch_reg_0)); - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, - REG_NOTES (insn)); insn = emit_insn (gen_subsi3 (pic_reg, pic_reg, scratch_reg_0)); - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, - REG_NOTES (insn)); } if (total_size > 0) @@ -2424,7 +2414,7 @@ immediate_load_p (rtx op, enum machine_mode mode) { enum immediate_class c = classify_immediate (op, mode); return c == IC_IL1 || c == IC_IL1s - || (!flow2_completed && (c == IC_IL2 || c == IC_IL2s)); + || (!epilogue_completed && (c == IC_IL2 || c == IC_IL2s)); } return 0; } @@ -3833,7 +3823,7 @@ fsmbi_const_p (rtx x) /* We can always choose TImode for CONST_INT because the high bits of an SImode will always be all 1s, i.e., valid for fsmbi. */ enum immediate_class c = classify_immediate (x, TImode); - return c == IC_FSMBI || (!flow2_completed && c == IC_FSMBI2); + return c == IC_FSMBI || (!epilogue_completed && c == IC_FSMBI2); } return 0; } diff --git a/gcc/config/stormy16/stormy16.c b/gcc/config/stormy16/stormy16.c index 5f8be43..8f2dc1e 100644 --- a/gcc/config/stormy16/stormy16.c +++ b/gcc/config/stormy16/stormy16.c @@ -1,6 +1,6 @@ /* Xstormy16 target functions. - Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 - Free Software Foundation, Inc. + Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + 2006, 2007 Free Software Foundation, Inc. Contributed by Red Hat, Inc. This file is part of GCC. @@ -1000,10 +1000,10 @@ struct xstormy16_stack_layout /* Does REGNO need to be saved? */ #define REG_NEEDS_SAVE(REGNUM, IFUN) \ - ((regs_ever_live[REGNUM] && ! call_used_regs[REGNUM]) \ + ((df_regs_ever_live_p (REGNUM) && ! call_used_regs[REGNUM]) \ || (IFUN && ! fixed_regs[REGNUM] && call_used_regs[REGNUM] \ && (REGNO_REG_CLASS (REGNUM) != CARRY_REGS) \ - && (regs_ever_live[REGNUM] || ! current_function_is_leaf))) + && (df_regs_ever_live_p (REGNUM) || ! current_function_is_leaf))) /* Compute the stack layout. */ struct xstormy16_stack_layout diff --git a/gcc/config/v850/v850.c b/gcc/config/v850/v850.c index e741c73..fbb3ac9 100644 --- a/gcc/config/v850/v850.c +++ b/gcc/config/v850/v850.c @@ -1,6 +1,6 @@ /* Subroutines for insn-output.c for NEC V850 series - Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 - Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + 2006, 2007 Free Software Foundation, Inc. Contributed by Jeff Law (law@cygnus.com). This file is part of GCC. @@ -1134,7 +1134,7 @@ substitute_ep_register (rtx first_insn, if (!*p_r1) { - regs_ever_live[1] = 1; + df_set_regs_ever_live_p (1, true); *p_r1 = gen_rtx_REG (Pmode, 1); *p_ep = gen_rtx_REG (Pmode, 30); } @@ -1460,12 +1460,15 @@ compute_register_save_size (long * p_reg_saved) int size = 0; int i; int interrupt_handler = v850_interrupt_function_p (current_function_decl); - int call_p = regs_ever_live [LINK_POINTER_REGNUM]; + int call_p = df_regs_ever_live_p (LINK_POINTER_REGNUM); long reg_saved = 0; /* Count the return pointer if we need to save it. */ if (current_function_profile && !call_p) - regs_ever_live [LINK_POINTER_REGNUM] = call_p = 1; + { + df_set_regs_ever_live (LINK_POINTER_REGNUM, true); + call_p = 1; + } /* Count space for the register saves. */ if (interrupt_handler) @@ -1474,7 +1477,7 @@ compute_register_save_size (long * p_reg_saved) switch (i) { default: - if (regs_ever_live[i] || call_p) + if (df_regs_ever_live_p (i) || call_p) { size += 4; reg_saved |= 1L << i; @@ -1502,7 +1505,7 @@ compute_register_save_size (long * p_reg_saved) { /* Find the first register that needs to be saved. */ for (i = 0; i <= 31; i++) - if (regs_ever_live[i] && ((! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ((! call_used_regs[i]) || i == LINK_POINTER_REGNUM)) break; @@ -1534,7 +1537,7 @@ compute_register_save_size (long * p_reg_saved) reg_saved |= 1L << i; } - if (regs_ever_live [LINK_POINTER_REGNUM]) + if (df_regs_ever_live_p (LINK_POINTER_REGNUM)) { size += 4; reg_saved |= 1L << LINK_POINTER_REGNUM; @@ -1543,7 +1546,7 @@ compute_register_save_size (long * p_reg_saved) else { for (; i <= 31; i++) - if (regs_ever_live[i] && ((! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ((! call_used_regs[i]) || i == LINK_POINTER_REGNUM)) { size += 4; diff --git a/gcc/config/vax/vax.c b/gcc/config/vax/vax.c index 5ce9ad3..dbc2390 100644 --- a/gcc/config/vax/vax.c +++ b/gcc/config/vax/vax.c @@ -1,6 +1,6 @@ /* Subroutines for insn-output.c for VAX. Copyright (C) 1987, 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, - 2004, 2005 + 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -116,7 +116,7 @@ vax_output_function_prologue (FILE * file, HOST_WIDE_INT size) int mask = 0; for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if (regs_ever_live[regno] && !call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && !call_used_regs[regno]) mask |= 1 << regno; fprintf (file, "\t.word 0x%x\n", mask); @@ -127,7 +127,7 @@ vax_output_function_prologue (FILE * file, HOST_WIDE_INT size) int offset = 0; for (regno = FIRST_PSEUDO_REGISTER-1; regno >= 0; --regno) - if (regs_ever_live[regno] && !call_used_regs[regno]) + if (df_regs_ever_live_p (regno) && !call_used_regs[regno]) dwarf2out_reg_save (label, regno, offset -= 4); dwarf2out_reg_save (label, PC_REGNUM, offset -= 4); @@ -45,6 +45,8 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "params.h" #include "rtlhooks-def.h" #include "tree-pass.h" +#include "df.h" +#include "dbgcnt.h" /* The basic idea of common subexpression elimination is to go through the code, keeping a record of expressions that would @@ -347,27 +349,6 @@ static unsigned int cse_reg_info_timestamp; static HARD_REG_SET hard_regs_in_table; -/* CUID of insn that starts the basic block currently being cse-processed. */ - -static int cse_basic_block_start; - -/* CUID of insn that ends the basic block currently being cse-processed. */ - -static int cse_basic_block_end; - -/* Vector mapping INSN_UIDs to cuids. - The cuids are like uids but increase monotonically always. - We use them to see whether a reg is used outside a given basic block. */ - -static int *uid_cuid; - -/* Highest UID in UID_CUID. */ -static int max_uid; - -/* Get the cuid of an insn. */ - -#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)]) - /* Nonzero if cse has altered conditional jump insns in such a way that jump optimization should be redone. */ @@ -538,10 +519,6 @@ static int constant_pool_entries_regcost; struct cse_basic_block_data { - /* Lowest CUID value of insns in block. */ - int low_cuid; - /* Highest CUID value of insns in block. */ - int high_cuid; /* Total number of SETs in block. */ int nsets; /* Size of current branch path, if any. */ @@ -554,6 +531,11 @@ struct cse_basic_block_data } *path; }; + +/* Pointers to the live in/live out bitmaps for the boundaries of the + current EBB. */ +static bitmap cse_ebb_live_in, cse_ebb_live_out; + /* A simple bitmap to track which basic blocks have been visited already as part of an already processed extended basic block. */ static sbitmap cse_visited_basic_blocks; @@ -602,7 +584,7 @@ static void record_jump_cond (enum rtx_code, enum machine_mode, rtx, rtx, static void cse_insn (rtx, rtx); static void cse_prescan_path (struct cse_basic_block_data *); static void invalidate_from_clobbers (rtx); -static rtx cse_process_notes (rtx, rtx); +static rtx cse_process_notes (rtx, rtx, bool *); static void cse_extended_basic_block (struct cse_basic_block_data *); static void count_reg_usage (rtx, int *, rtx, int); static int check_for_label_ref (rtx *, void *); @@ -957,11 +939,10 @@ make_regs_eqv (unsigned int new, unsigned int old) && ((new < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (new)) || (new >= FIRST_PSEUDO_REGISTER && (firstr < FIRST_PSEUDO_REGISTER - || ((uid_cuid[REGNO_LAST_UID (new)] > cse_basic_block_end - || (uid_cuid[REGNO_FIRST_UID (new)] - < cse_basic_block_start)) - && (uid_cuid[REGNO_LAST_UID (new)] - > uid_cuid[REGNO_LAST_UID (firstr)])))))) + || (bitmap_bit_p (cse_ebb_live_out, new) + && !bitmap_bit_p (cse_ebb_live_out, firstr)) + || (bitmap_bit_p (cse_ebb_live_in, new) + && !bitmap_bit_p (cse_ebb_live_in, firstr)))))) { reg_eqv_table[firstr].prev = new; reg_eqv_table[new].next = firstr; @@ -2648,14 +2629,15 @@ cse_rtx_varies_p (rtx x, int from_alias) static void validate_canon_reg (rtx *xloc, rtx insn) { - rtx new = canon_reg (*xloc, insn); + if (*xloc) + { + rtx new = canon_reg (*xloc, insn); - /* If replacing pseudo with hard reg or vice versa, ensure the - insn remains valid. Likewise if the insn has MATCH_DUPs. */ - if (insn != 0 && new != 0) - validate_change (insn, xloc, new, 1); - else - *xloc = new; + /* If replacing pseudo with hard reg or vice versa, ensure the + insn remains valid. Likewise if the insn has MATCH_DUPs. */ + gcc_assert (insn && new); + validate_change (insn, xloc, new, 1); + } } /* Canonicalize an expression: @@ -4151,12 +4133,12 @@ cse_insn (rtx insn, rtx libcall_insn) This does nothing when a register is clobbered because we have already invalidated the reg. */ if (MEM_P (XEXP (y, 0))) - canon_reg (XEXP (y, 0), NULL_RTX); + canon_reg (XEXP (y, 0), insn); } else if (GET_CODE (y) == USE && ! (REG_P (XEXP (y, 0)) && REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER)) - canon_reg (y, NULL_RTX); + canon_reg (y, insn); else if (GET_CODE (y) == CALL) { /* The result of apply_change_group can be ignored; see @@ -4170,14 +4152,14 @@ cse_insn (rtx insn, rtx libcall_insn) else if (GET_CODE (x) == CLOBBER) { if (MEM_P (XEXP (x, 0))) - canon_reg (XEXP (x, 0), NULL_RTX); + canon_reg (XEXP (x, 0), insn); } /* Canonicalize a USE of a pseudo register or memory location. */ else if (GET_CODE (x) == USE && ! (REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER)) - canon_reg (XEXP (x, 0), NULL_RTX); + canon_reg (XEXP (x, 0), insn); else if (GET_CODE (x) == CALL) { /* The result of apply_change_group can be ignored; see canon_reg. */ @@ -4195,8 +4177,12 @@ cse_insn (rtx insn, rtx libcall_insn) && (! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl)) || GET_CODE (SET_DEST (sets[0].rtl)) == STRICT_LOW_PART)) { - src_eqv = fold_rtx (canon_reg (XEXP (tem, 0), NULL_RTX), insn); + /* The result of apply_change_group can be ignored; see canon_reg. */ + canon_reg (XEXP (tem, 0), insn); + apply_change_group (); + src_eqv = fold_rtx (XEXP (tem, 0), insn); XEXP (tem, 0) = src_eqv; + df_notes_rescan (insn); } /* Canonicalize sources and addresses of destinations. @@ -4861,6 +4847,7 @@ cse_insn (rtx insn, rtx libcall_insn) XEXP (note, 0) = simplify_replace_rtx (XEXP (note, 0), sets[i].orig_src, copy_rtx (new)); + df_notes_rescan (libcall_insn); } /* The result of apply_change_group can be ignored; see @@ -4979,6 +4966,7 @@ cse_insn (rtx insn, rtx libcall_insn) /* Record the actual constant value in a REG_EQUAL note, making a new one if one does not already exist. */ set_unique_reg_note (insn, REG_EQUAL, src_const); + df_notes_rescan (insn); } } @@ -5056,11 +5044,6 @@ cse_insn (rtx insn, rtx libcall_insn) else if (dest == pc_rtx && GET_CODE (src) == LABEL_REF && !LABEL_REF_NONLOCAL_P (src)) { - /* Now emit a BARRIER after the unconditional jump. */ - if (NEXT_INSN (insn) == 0 - || !BARRIER_P (NEXT_INSN (insn))) - emit_barrier_after (insn); - /* We reemit the jump in as many cases as possible just in case the form of an unconditional jump is significantly different than a computed jump or conditional jump. @@ -5086,11 +5069,6 @@ cse_insn (rtx insn, rtx libcall_insn) delete_insn_and_edges (insn); insn = new; - - /* Now emit a BARRIER after the unconditional jump. */ - if (NEXT_INSN (insn) == 0 - || !BARRIER_P (NEXT_INSN (insn))) - emit_barrier_after (insn); } else INSN_CODE (insn) = -1; @@ -5716,7 +5694,7 @@ invalidate_from_clobbers (rtx x) Return the replacement for X. */ static rtx -cse_process_notes (rtx x, rtx object) +cse_process_notes_1 (rtx x, rtx object, bool *changed) { enum rtx_code code = GET_CODE (x); const char *fmt = GET_RTX_FORMAT (code); @@ -5737,22 +5715,22 @@ cse_process_notes (rtx x, rtx object) case MEM: validate_change (x, &XEXP (x, 0), - cse_process_notes (XEXP (x, 0), x), 0); + cse_process_notes (XEXP (x, 0), x, changed), 0); return x; case EXPR_LIST: case INSN_LIST: if (REG_NOTE_KIND (x) == REG_EQUAL) - XEXP (x, 0) = cse_process_notes (XEXP (x, 0), NULL_RTX); + XEXP (x, 0) = cse_process_notes (XEXP (x, 0), NULL_RTX, changed); if (XEXP (x, 1)) - XEXP (x, 1) = cse_process_notes (XEXP (x, 1), NULL_RTX); + XEXP (x, 1) = cse_process_notes (XEXP (x, 1), NULL_RTX, changed); return x; case SIGN_EXTEND: case ZERO_EXTEND: case SUBREG: { - rtx new = cse_process_notes (XEXP (x, 0), object); + rtx new = cse_process_notes (XEXP (x, 0), object, changed); /* We don't substitute VOIDmode constants into these rtx, since they would impede folding. */ if (GET_MODE (new) != VOIDmode) @@ -5788,10 +5766,20 @@ cse_process_notes (rtx x, rtx object) for (i = 0; i < GET_RTX_LENGTH (code); i++) if (fmt[i] == 'e') validate_change (object, &XEXP (x, i), - cse_process_notes (XEXP (x, i), object), 0); + cse_process_notes (XEXP (x, i), object, changed), 0); return x; } + +static rtx +cse_process_notes (rtx x, rtx object, bool *changed) +{ + rtx new = cse_process_notes_1 (x, object, changed); + if (new != x) + *changed = true; + return new; +} + /* Find a path in the CFG, starting with FIRST_BB to perform CSE on. @@ -5966,14 +5954,12 @@ have_eh_succ_edges (basic_block bb) /* Scan to the end of the path described by DATA. Return an estimate of - the total number of SETs, and the lowest and highest insn CUID, of all - insns in the path. */ + the total number of SETs of all insns in the path. */ static void cse_prescan_path (struct cse_basic_block_data *data) { int nsets = 0; - int low_cuid = -1, high_cuid = -1; /* FIXME low_cuid not computed correctly */ int path_size = data->path_size; int path_entry; @@ -5996,21 +5982,9 @@ cse_prescan_path (struct cse_basic_block_data *data) nsets += XVECLEN (PATTERN (insn), 0); else nsets += 1; - - /* Ignore insns made by CSE in a previous traversal of this - basic block. They cannot affect the boundaries of the - basic block. - FIXME: When we only visit each basic block at most once, - this can go away. */ - if (INSN_UID (insn) <= max_uid && INSN_CUID (insn) > high_cuid) - high_cuid = INSN_CUID (insn); - if (INSN_UID (insn) <= max_uid && INSN_CUID (insn) < low_cuid) - low_cuid = INSN_CUID (insn); } } - data->low_cuid = low_cuid; - data->high_cuid = high_cuid; data->nsets = nsets; } @@ -6027,6 +6001,8 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data) qty_table = XNEWVEC (struct qty_table_elem, max_qty); new_basic_block (); + cse_ebb_live_in = DF_LIVE_IN (ebb_data->path[0].bb); + cse_ebb_live_out = DF_LIVE_OUT (ebb_data->path[path_size - 1].bb); for (path_entry = 0; path_entry < path_size; path_entry++) { basic_block bb; @@ -6058,8 +6034,13 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data) /* Process notes first so we have all notes in canonical forms when looking for duplicate operations. */ if (REG_NOTES (insn)) - REG_NOTES (insn) = cse_process_notes (REG_NOTES (insn), - NULL_RTX); + { + bool changed = false; + REG_NOTES (insn) = cse_process_notes (REG_NOTES (insn), + NULL_RTX, &changed); + if (changed) + df_notes_rescan (insn); + } /* Track when we are inside in LIBCALL block. Inside such a block we do not want to record destinations. The last @@ -6191,6 +6172,7 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data) free (qty_table); } + /* Perform cse on the instructions of a function. F is the first instruction. @@ -6207,6 +6189,11 @@ cse_main (rtx f ATTRIBUTE_UNUSED, int nregs) int *rc_order = XNEWVEC (int, last_basic_block); int i, n_blocks; + df_set_flags (DF_LR_RUN_DCE); + df_analyze (); + df_set_flags (DF_DEFER_INSN_RESCAN); + + reg_scan (get_insns (), max_reg_num ()); init_cse_reg_info (nregs); ebb_data.path = XNEWVEC (struct branch_path, @@ -6229,19 +6216,6 @@ cse_main (rtx f ATTRIBUTE_UNUSED, int nregs) cse_visited_basic_blocks = sbitmap_alloc (last_basic_block); sbitmap_zero (cse_visited_basic_blocks); - /* Compute the mapping from uids to cuids. - CUIDs are numbers assigned to insns, like uids, except that - that cuids increase monotonically through the code. */ - max_uid = get_max_uid (); - uid_cuid = XCNEWVEC (int, max_uid + 1); - i = 0; - FOR_EACH_BB (bb) - { - rtx insn; - FOR_BB_INSNS (bb, insn) - INSN_CUID (insn) = ++i; - } - /* Loop over basic blocks in reverse completion order (RPO), excluding the ENTRY and EXIT blocks. */ n_blocks = pre_and_rev_post_order_compute (NULL, rc_order, false); @@ -6271,8 +6245,6 @@ cse_main (rtx f ATTRIBUTE_UNUSED, int nregs) needed for this path. For this, we take the number of sets and multiply that by MAX_RECOG_OPERANDS. */ max_qty = ebb_data.nsets * MAX_RECOG_OPERANDS; - cse_basic_block_start = ebb_data.low_cuid; - cse_basic_block_end = ebb_data.high_cuid; /* Dump the path we're about to process. */ if (dump_file) @@ -6284,7 +6256,6 @@ cse_main (rtx f ATTRIBUTE_UNUSED, int nregs) /* Clean up. */ end_alias_analysis (); - free (uid_cuid); free (reg_eqv_table); free (ebb_data.path); sbitmap_free (cse_visited_basic_blocks); @@ -6605,7 +6576,7 @@ delete_trivially_dead_insns (rtx insns, int nreg) /* If this is a dead insn, delete it and show registers in it aren't being used. */ - if (! live_insn) + if (! live_insn && dbg_cnt (delete_trivial_dead)) { count_reg_usage (insn, counts, NULL_RTX, -1); delete_insn_and_edges (insn); @@ -7009,11 +6980,10 @@ static unsigned int rest_of_handle_cse (void) { int tem; + if (dump_file) dump_flow_info (dump_file, dump_flags); - reg_scan (get_insns (), max_reg_num ()); - tem = cse_main (get_insns (), max_reg_num ()); /* If we are not running more CSE passes, then we are no longer @@ -7024,7 +6994,7 @@ rest_of_handle_cse (void) rebuild_jump_labels (get_insns ()); if (tem || optimize > 1) - cleanup_cfg (CLEANUP_EXPENSIVE); + cleanup_cfg (0); return 0; } @@ -7042,6 +7012,7 @@ struct tree_opt_pass pass_cse = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func | TODO_ggc_collect | TODO_verify_flow, /* todo_flags_finish */ @@ -7078,11 +7049,9 @@ rest_of_handle_cse2 (void) { timevar_push (TV_JUMP); rebuild_jump_labels (get_insns ()); - delete_dead_jumptables (); - cleanup_cfg (CLEANUP_EXPENSIVE); + cleanup_cfg (0); timevar_pop (TV_JUMP); } - reg_scan (get_insns (), max_reg_num ()); cse_not_expected = 1; return 0; } @@ -7101,6 +7070,7 @@ struct tree_opt_pass pass_cse2 = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func | TODO_ggc_collect | TODO_verify_flow, /* todo_flags_finish */ diff --git a/gcc/cselib.c b/gcc/cselib.c index b791d9d..6114091 100644 --- a/gcc/cselib.c +++ b/gcc/cselib.c @@ -1,6 +1,6 @@ /* Common subexpression elimination library for GNU compiler. Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -131,6 +131,10 @@ static cselib_val dummy_val; each time memory is invalidated. */ static cselib_val *first_containing_mem = &dummy_val; static alloc_pool elt_loc_list_pool, elt_list_pool, cselib_val_pool, value_pool; + +/* If nonnull, cselib will call this function before freeing useless + VALUEs. A VALUE is deemed useless if its "locs" field is null. */ +void (*cselib_discard_hook) (cselib_val *); /* Allocate a struct elt_list and fill in its two elements with the @@ -331,6 +335,9 @@ discard_useless_values (void **x, void *info ATTRIBUTE_UNUSED) if (v->locs == 0) { + if (cselib_discard_hook) + cselib_discard_hook (v); + CSELIB_VAL_PTR (v->val_rtx) = NULL; htab_clear_slot (cselib_hash_table, x); unchain_one_value (v); @@ -823,6 +830,260 @@ cselib_lookup_mem (rtx x, int create) return mem_elt; } +/* Search thru the possible substitutions in P. We prefer a non reg + substitution because this allows us to expand the tree further. If + we find, just a reg, take the lowest regno. There may be several + non-reg results, we just take the first one because they will all + expand to the same place. */ + +static rtx +expand_loc (struct elt_loc_list *p, bitmap regs_active, int max_depth) +{ + rtx reg_result = NULL; + unsigned int regno = UINT_MAX; + struct elt_loc_list *p_in = p; + + for (; p; p = p -> next) + { + /* Avoid infinite recursion trying to expand a reg into a + the same reg. */ + if ((REG_P (p->loc)) + && (REGNO (p->loc) < regno) + && !bitmap_bit_p (regs_active, REGNO (p->loc))) + { + reg_result = p->loc; + regno = REGNO (p->loc); + } + /* Avoid infinite recursion and do not try to expand the + value. */ + else if (GET_CODE (p->loc) == VALUE + && CSELIB_VAL_PTR (p->loc)->locs == p_in) + continue; + else if (!REG_P (p->loc)) + { + rtx result; + if (dump_file) + { + print_inline_rtx (dump_file, p->loc, 0); + fprintf (dump_file, "\n"); + } + result = cselib_expand_value_rtx (p->loc, regs_active, max_depth - 1); + if (result) + return result; + } + + } + + if (regno != UINT_MAX) + { + rtx result; + if (dump_file) + fprintf (dump_file, "r%d\n", regno); + + result = cselib_expand_value_rtx (reg_result, regs_active, max_depth - 1); + if (result) + return result; + } + + if (dump_file) + { + if (reg_result) + { + print_inline_rtx (dump_file, reg_result, 0); + fprintf (dump_file, "\n"); + } + else + fprintf (dump_file, "NULL\n"); + } + return reg_result; +} + + +/* Forward substitute and expand an expression out to its roots. + This is the opposite of common subexpression. Because local value + numbering is such a weak optimization, the expanded expression is + pretty much unique (not from a pointer equals point of view but + from a tree shape point of view. + + This function returns NULL if the expansion fails. The expansion + will fail if there is no value number for one of the operands or if + one of the operands has been overwritten between the current insn + and the beginning of the basic block. For instance x has no + expansion in: + + r1 <- r1 + 3 + x <- r1 + 8 + + REGS_ACTIVE is a scratch bitmap that should be clear when passing in. + It is clear on return. */ + +rtx +cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth) +{ + rtx copy, scopy; + int i, j; + RTX_CODE code; + const char *format_ptr; + + code = GET_CODE (orig); + + /* For the context of dse, if we end up expand into a huge tree, we + will not have a useful address, so we might as well just give up + quickly. */ + if (max_depth <= 0) + return NULL; + + switch (code) + { + case REG: + { + struct elt_list *l = REG_VALUES (REGNO (orig)); + + if (l && l->elt == NULL) + l = l->next; + for (; l; l = l->next) + if (GET_MODE (l->elt->val_rtx) == GET_MODE (orig)) + { + rtx result; + int regno = REGNO (orig); + + /* The only thing that we are not willing to do (this + is requirement of dse and if others potiential uses + need this function we should add a parm to control + it) is that we will not substitute the + STACK_POINTER_REGNUM, FRAME_POINTER or the + HARD_FRAME_POINTER. + + Thses expansions confuses the code that notices that + stores into the frame go dead at the end of the + function and that the frame is not effected by calls + to subroutines. If you allow the + STACK_POINTER_REGNUM substitution, then dse will + think that parameter pushing also goes dead which is + wrong. If you allow the FRAME_POINTER or the + HARD_FRAME_POINTER then you lose the opportunity to + make the frame assumptions. */ + if (regno == STACK_POINTER_REGNUM + || regno == FRAME_POINTER_REGNUM + || regno == HARD_FRAME_POINTER_REGNUM) + return orig; + + bitmap_set_bit (regs_active, regno); + + if (dump_file) + fprintf (dump_file, "expanding: r%d into: ", regno); + + result = expand_loc (l->elt->locs, regs_active, max_depth); + bitmap_clear_bit (regs_active, regno); + + if (result) + return result; + else + return orig; + } + } + + case CONST_INT: + case CONST_DOUBLE: + case CONST_VECTOR: + case SYMBOL_REF: + case CODE_LABEL: + case PC: + case CC0: + case SCRATCH: + /* SCRATCH must be shared because they represent distinct values. */ + return orig; + case CLOBBER: + if (REG_P (XEXP (orig, 0)) && HARD_REGISTER_NUM_P (REGNO (XEXP (orig, 0)))) + return orig; + break; + + case CONST: + if (shared_const_p (orig)) + return orig; + break; + + + case VALUE: + { + rtx result; + if (dump_file) + fprintf (dump_file, "expanding value %s into: ", GET_MODE_NAME (GET_MODE (orig))); + + result = expand_loc (CSELIB_VAL_PTR (orig)->locs, regs_active, max_depth); + if (result + && GET_CODE (result) == CONST_INT + && GET_MODE (orig) != VOIDmode) + { + result = gen_rtx_CONST (GET_MODE (orig), result); + if (dump_file) + fprintf (dump_file, " wrapping const_int result in const to preserve mode %s\n", + GET_MODE_NAME (GET_MODE (orig))); + } + return result; + } + default: + break; + } + + /* Copy the various flags, fields, and other information. We assume + that all fields need copying, and then clear the fields that should + not be copied. That is the sensible default behavior, and forces + us to explicitly document why we are *not* copying a flag. */ + copy = shallow_copy_rtx (orig); + + format_ptr = GET_RTX_FORMAT (GET_CODE (copy)); + + for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++) + switch (*format_ptr++) + { + case 'e': + if (XEXP (orig, i) != NULL) + { + rtx result = cselib_expand_value_rtx (XEXP (orig, i), regs_active, max_depth - 1); + if (!result) + return NULL; + XEXP (copy, i) = result; + } + break; + + case 'E': + case 'V': + if (XVEC (orig, i) != NULL) + { + XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i)); + for (j = 0; j < XVECLEN (copy, i); j++) + { + rtx result = cselib_expand_value_rtx (XVECEXP (orig, i, j), regs_active, max_depth - 1); + if (!result) + return NULL; + XVECEXP (copy, i, j) = result; + } + } + break; + + case 't': + case 'w': + case 'i': + case 's': + case 'S': + case 'T': + case 'u': + case 'B': + case '0': + /* These are left unchanged. */ + break; + + default: + gcc_unreachable (); + } + + scopy = simplify_rtx (copy); + if (scopy) + return scopy; + return copy; +} + /* Walk rtx X and replace all occurrences of REG and MEM subexpressions with VALUE expressions. This way, it becomes independent of changes to registers and memory. @@ -1505,6 +1766,7 @@ cselib_init (bool record_memory) void cselib_finish (void) { + cselib_discard_hook = NULL; free_alloc_pool (elt_list_pool); free_alloc_pool (elt_loc_list_pool); free_alloc_pool (cselib_val_pool); diff --git a/gcc/cselib.h b/gcc/cselib.h index 47bcb99..427ea1d 100644 --- a/gcc/cselib.h +++ b/gcc/cselib.h @@ -1,6 +1,6 @@ /* Common subexpression elimination for GNU compiler. Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2003, 2004, 2005 Free Software Foundation, Inc. + 1999, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -59,6 +59,8 @@ struct elt_list GTY(()) cselib_val *elt; }; +extern void (*cselib_discard_hook) (cselib_val *); + extern cselib_val *cselib_lookup (rtx, enum machine_mode, int); extern void cselib_init (bool record_memory); extern void cselib_clear_table (void); @@ -67,5 +69,6 @@ extern void cselib_process_insn (rtx); extern enum machine_mode cselib_reg_set_mode (rtx); extern int rtx_equal_for_cselib_p (rtx, rtx); extern int references_value_p (rtx, int); +extern rtx cselib_expand_value_rtx (rtx, bitmap, int); extern rtx cselib_subst_to_values (rtx); extern void cselib_invalidate_rtx (rtx); diff --git a/gcc/dbgcnt.c b/gcc/dbgcnt.c new file mode 100644 index 0000000..df02111 --- /dev/null +++ b/gcc/dbgcnt.c @@ -0,0 +1,107 @@ +/* Debug counter for debugging support + Copyright (C) 2006, 2007 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, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +See dbgcnt.def for usage information. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" + +#include "dbgcnt.h" + +struct string2counter_map { + const char *name; + enum debug_counter counter; +}; + +#define DEBUG_COUNTER(a) { #a , a }, + +static struct string2counter_map map[debug_counter_number_of_counters] = +{ +#include "dbgcnt.def" +}; +#undef DEBUG_COUNTER + +#define DEBUG_COUNTER(a) UINT_MAX, +static unsigned int limit[debug_counter_number_of_counters] = +{ +#include "dbgcnt.def" +}; +#undef DEBUG_COUNTER + +static unsigned int count[debug_counter_number_of_counters]; + +bool +dbg_cnt_is_enabled (enum debug_counter index) +{ + return count[index] <= limit[index]; +} + +bool +dbg_cnt (enum debug_counter index) +{ + count[index]++; + return dbg_cnt_is_enabled (index); +} + + +static void +dbg_cnt_set_limit_by_index (enum debug_counter index, int value) +{ + limit[index] = value; + + fprintf (stderr, "dbg_cnt '%s' set to %d\n", map[index].name, value); +} + +static void +dbg_cnt_set_limit_by_name (const char *name, int len, int value) +{ + int i; + for (i = debug_counter_number_of_counters - 1; i >= 0; i--) + if (!strncmp (map[i].name, name, len)) + break; + + if (i < 0) + return; + + dbg_cnt_set_limit_by_index (i, value); +} + +void +dbg_cnt_process_opt (const char *arg) +{ + char *colon = strchr (arg, ':'); + char *comma; + + if (colon == NULL) + return; + + dbg_cnt_set_limit_by_name (arg, colon - arg, atoi (colon + 1)); + + comma = strchr (colon + 1, ','); + while (comma) + { + colon = strchr (comma + 1, ':'); + if (colon == NULL || !(colon[1] >= '0' && colon[1] <= '9')) + return; + dbg_cnt_set_limit_by_name (comma + 1, colon - (comma + 1), atoi (colon + 1)); + comma = strchr (colon + 1, ','); + } +} diff --git a/gcc/dbgcnt.def b/gcc/dbgcnt.def new file mode 100644 index 0000000..5c0b749 --- /dev/null +++ b/gcc/dbgcnt.def @@ -0,0 +1,84 @@ +/* This file contains the list of the debug counter for GCC. + Copyright (C) 2006, 2007 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, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ + + +/* A debug counter provides you a way to count an event + and return false after the counter has exceeded the threshold + specified by the option. + + What is it used for ? + + This is primarily used to speed up the search for the bad transformation + an optimization pass does. By doing a binary search on N, + you can quickly narrow down to one transformation + which is bad, or which triggers the bad behavior downstream + (usually in the form of the badly generated code). + + How does it work ? + + Everytime dbg_cnt(named-counter) is called, + the counter is incremented for the named-counter. + And the incremented value is compared against the threshold (limit) + specified by the option. + dbg_cnt () returns true if it is at or below threshold, and false if above. + + How to add a new one ? + + To add a new counter, simply add an entry below with some descriptive name, + and add call(s) to dbg_cnt(your-counter-name) in appropriate places. + Usually, you want to control at the finest granularity + any particular transformation can happen. + e.g. for each instruction in a dead code elimination, + or for each copy instruction in register coalescing, + or constant-propagation for each insn, + or a block straightening, etc. + See dce.c for an example. With the dbg_cnt () call in dce.c, + now a developer can use -fdbg-cnt=dce:N + to stop doing the dead code elimination after N times. + + How to use it ? + + By default, all limits are UINT_MAX. + Since debug count is unsigned int, <= UINT_MAX returns true always. + i.e. dbg_cnt() returns true always regardless of the counter value + (although it still counts the event). + Use -fdbg-cnt=counter1:N,counter2:M,... + which sets the limit for counter1 to N, and the limit for counter2 to M, etc. + e.g. setting a limit to zero will make dbg_cnt () return false *always*. +*/ + +/* Debug counter definitions. */ +DEBUG_COUNTER (auto_inc_dec) +DEBUG_COUNTER (cse2_move2add) +DEBUG_COUNTER (dce) +DEBUG_COUNTER (delete_trivial_dead) +DEBUG_COUNTER (dse) +DEBUG_COUNTER (gcse2_delete) +DEBUG_COUNTER (ia64_sched2) +DEBUG_COUNTER (local_alloc_for_sched) +DEBUG_COUNTER (postreload_cse) +DEBUG_COUNTER (pre_insn) +DEBUG_COUNTER (sched2_func) +DEBUG_COUNTER (sched_block) +DEBUG_COUNTER (sched_func) +DEBUG_COUNTER (sched_insn) +DEBUG_COUNTER (sched_region) +DEBUG_COUNTER (split_for_sched2) +DEBUG_COUNTER (tail_call) diff --git a/gcc/dbgcnt.h b/gcc/dbgcnt.h new file mode 100644 index 0000000..38591e9b --- /dev/null +++ b/gcc/dbgcnt.h @@ -0,0 +1,39 @@ +/* Debug counter for debugging support + Copyright (C) 2006, 2007 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, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +See dbgcnt.def for usage information. */ + +#ifndef GCC_DBGCNT_H +#define GCC_DBGCNT_H + +#define DEBUG_COUNTER(a) a, + +enum debug_counter { +#include "dbgcnt.def" + debug_counter_number_of_counters +}; + +#undef DEBUG_COUNTER + +extern bool dbg_cnt_is_enabled (enum debug_counter index); +extern bool dbg_cnt (enum debug_counter index); +extern void dbg_cnt_process_opt (const char *arg); + +#endif /* GCC_DBGCNT_H */ diff --git a/gcc/dce.c b/gcc/dce.c new file mode 100644 index 0000000..902dbd9 --- /dev/null +++ b/gcc/dce.c @@ -0,0 +1,789 @@ +/* RTL dead code elimination. + Copyright (C) 2005, 2006, 2007 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, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "hashtab.h" +#include "tm.h" +#include "rtl.h" +#include "tree.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "flags.h" +#include "df.h" +#include "cselib.h" +#include "dce.h" +#include "timevar.h" +#include "tree-pass.h" +#include "dbgcnt.h" + +DEF_VEC_I(int); +DEF_VEC_ALLOC_I(int,heap); + + +/* ------------------------------------------------------------------------- + Core mark/delete routines + ------------------------------------------------------------------------- */ + +/* The data-flow information needed by this pass. */ +static bool df_in_progress = false; + +/* True if we deleted at least one instruction. */ +static bool something_changed; + +/* Instructions that have been marked but whose dependencies have not + yet been processed. */ +static VEC(rtx,heap) *worklist; + +static bitmap_obstack dce_blocks_bitmap_obstack; +static bitmap_obstack dce_tmp_bitmap_obstack; + +static sbitmap marked = NULL; + +/* Return true if INSN a normal instruction that can be deleted by the + DCE pass. */ + +static bool +deletable_insn_p (rtx insn, bool fast) +{ + rtx x; + + switch (GET_CODE (PATTERN (insn))) + { + case USE: + case PREFETCH: + case TRAP_IF: + /* The UNSPEC case was added here because the ia-64 claims that + USEs do not work after reload and generates UNSPECS rather + than USEs. Since dce is run after reload we need to avoid + deleting these even if they are dead. If it turns out that + USEs really do work after reload, the ia-64 should be + changed, and the UNSPEC case can be removed. */ + case UNSPEC: + return false; + + case CLOBBER: + if (fast) + { + /* A CLOBBER of a dead pseudo register serves no purpose. + That is not necessarily true for hard registers until + after reload. */ + x = XEXP (PATTERN (insn), 0); + return REG_P (x) && (!HARD_REGISTER_P (x) || reload_completed); + } + else + /* Because of the way that use-def chains are built, it is not + possible to tell if the clobber is dead because it can + never be the target of a use-def chain. */ + return false; + + default: + if (!NONJUMP_INSN_P (insn)) + return false; + + if (volatile_insn_p (PATTERN (insn))) + return false; + + if (flag_non_call_exceptions && may_trap_p (PATTERN (insn))) + return false; + + return true; + } +} + + +/* Return true if INSN has not been marked as needed. */ + +static inline int +marked_insn_p (rtx insn) +{ + if (insn) + return TEST_BIT (marked, INSN_UID (insn)); + else + /* Artificial defs are always needed and they do not have an + insn. */ + return true; +} + + +/* If INSN has not yet been marked as needed, mark it now, and add it to + the worklist. */ + +static void +mark_insn (rtx insn, bool fast) +{ + if (!marked_insn_p (insn)) + { + if (!fast) + VEC_safe_push (rtx, heap, worklist, insn); + SET_BIT (marked, INSN_UID (insn)); + if (dump_file) + fprintf (dump_file, " Adding insn %d to worklist\n", INSN_UID (insn)); + } +} + + +/* A note_stores callback used by mark_nonreg_stores. DATA is the + instruction containing DEST. */ + +static void +mark_nonreg_stores_1 (rtx dest, rtx pattern, void *data) +{ + if (GET_CODE (pattern) != CLOBBER && !REG_P (dest)) + mark_insn ((rtx) data, true); +} + + +/* A note_stores callback used by mark_nonreg_stores. DATA is the + instruction containing DEST. */ + +static void +mark_nonreg_stores_2 (rtx dest, rtx pattern, void *data) +{ + if (GET_CODE (pattern) != CLOBBER && !REG_P (dest)) + mark_insn ((rtx) data, false); +} + + +/* Mark INSN if BODY stores to a non-register destination. */ + +static void +mark_nonreg_stores (rtx body, rtx insn, bool fast) +{ + if (fast) + note_stores (body, mark_nonreg_stores_1, insn); + else + note_stores (body, mark_nonreg_stores_2, insn); +} + + +/* Initialize global variables for a new DCE pass. */ + +static void +init_dce (bool fast) +{ + if (!df_in_progress) + { + if (!fast) + df_chain_add_problem (DF_UD_CHAIN); + df_analyze (); + } + + if (dump_file) + df_dump (dump_file); + + bitmap_obstack_initialize (&dce_blocks_bitmap_obstack); + bitmap_obstack_initialize (&dce_tmp_bitmap_obstack); + marked = sbitmap_alloc (get_max_uid () + 1); + sbitmap_zero (marked); +} + + +/* Delete all REG_EQUAL notes of the registers INSN writes, to prevent + bad dangling REG_EQUAL notes. */ + +static void +delete_corresponding_reg_eq_notes (rtx insn) +{ + struct df_ref **def_rec; + for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + unsigned int regno = DF_REF_REGNO (def); + /* This loop is a little tricky. We cannot just go down the + chain because it is being modified by the actions in the + loop. So we just get the head. We plan to drain the list + anyway. */ + while (DF_REG_EQ_USE_CHAIN (regno)) + { + struct df_ref *eq_use = DF_REG_EQ_USE_CHAIN (regno); + rtx noted_insn = DF_REF_INSN (eq_use); + rtx note = find_reg_note (noted_insn, REG_EQUAL, NULL_RTX); + if (!note) + note = find_reg_note (noted_insn, REG_EQUIV, NULL_RTX); + + /* This assert is generally triggered when someone deletes a + REG_EQUAL or REG_EQUIV note by hacking the list manually + rather than calling remove_note. */ + gcc_assert (note); + remove_note (noted_insn, note); + } + } +} + + +/* Delete every instruction that hasn't been marked. Clear the insn + from DCE_DF if DF_DELETE is true. */ + +static void +delete_unmarked_insns (void) +{ + basic_block bb; + rtx insn, next; + + something_changed = false; + FOR_EACH_BB (bb) + FOR_BB_INSNS_SAFE (bb, insn, next) + if (INSN_P (insn)) + { + if (noop_move_p (insn)) + { + /* Note that this code does not handle the case where + the last insn of libcall is deleted. As it turns out + this case is excluded in the call to noop_move_p. */ + rtx note = find_reg_note (insn, REG_LIBCALL, NULL_RTX); + if (note && (XEXP (note, 0) != insn)) + { + rtx new_libcall_insn = next_real_insn (insn); + rtx retval_note = find_reg_note (XEXP (note, 0), + REG_RETVAL, NULL_RTX); + REG_NOTES (new_libcall_insn) + = gen_rtx_INSN_LIST (REG_LIBCALL, XEXP (note, 0), + REG_NOTES (new_libcall_insn)); + XEXP (retval_note, 0) = new_libcall_insn; + } + } + else if (marked_insn_p (insn)) + continue; + + /* WARNING, this debugging can itself cause problems if the + edge of the counter causes part of a libcall to be + deleted but not all of it. */ + if (!dbg_cnt (dce)) + continue; + + if (dump_file) + fprintf (dump_file, "DCE: Deleting insn %d\n", INSN_UID (insn)); + + /* Before we delete the insn, we have to delete + REG_EQUAL of the destination regs of the deleted insn + to prevent dangling REG_EQUAL. */ + delete_corresponding_reg_eq_notes (insn); + + delete_insn_and_edges (insn); + something_changed = true; + } +} + + +/* Mark all insns using DELETE_PARM in the libcall that contains + START_INSN. */ +static void +mark_libcall (rtx start_insn, bool delete_parm) +{ + rtx note = find_reg_note (start_insn, REG_LIBCALL_ID, NULL_RTX); + int id = INTVAL (XEXP (note, 0)); + rtx insn; + + mark_insn (start_insn, delete_parm); + insn = NEXT_INSN (start_insn); + + /* There are tales, long ago and far away, of the mystical nested + libcall. No one alive has actually seen one, but other parts of + the compiler support them so we will here. */ + for (insn = NEXT_INSN (start_insn); insn; insn = NEXT_INSN (insn)) + { + if (INSN_P (insn)) + { + /* Stay in the loop as long as we are in any libcall. */ + if ((note = find_reg_note (insn, REG_LIBCALL_ID, NULL_RTX))) + { + if (id == INTVAL (XEXP (note, 0))) + { + mark_insn (insn, delete_parm); + if (dump_file) + fprintf (dump_file, "matching forward libcall %d[%d]\n", + INSN_UID (insn), id); + } + } + else + break; + } + } + + for (insn = PREV_INSN (start_insn); insn; insn = PREV_INSN (insn)) + { + if (INSN_P (insn)) + { + /* Stay in the loop as long as we are in any libcall. */ + if ((note = find_reg_note (insn, REG_LIBCALL_ID, NULL_RTX))) + { + if (id == INTVAL (XEXP (note, 0))) + { + mark_insn (insn, delete_parm); + if (dump_file) + fprintf (dump_file, "matching backward libcall %d[%d]\n", + INSN_UID (insn), id); + } + } + else + break; + } + } +} + + +/* Go through the instructions and mark those whose necessity is not + dependent on inter-instruction information. Make sure all other + instructions are not marked. */ + +static void +prescan_insns_for_dce (bool fast) +{ + basic_block bb; + rtx insn; + + if (dump_file) + fprintf (dump_file, "Finding needed instructions:\n"); + + FOR_EACH_BB (bb) + FOR_BB_INSNS (bb, insn) + if (INSN_P (insn)) + { + rtx note = find_reg_note (insn, REG_LIBCALL_ID, NULL_RTX); + if (note) + mark_libcall (insn, fast); + else if (deletable_insn_p (insn, fast)) + mark_nonreg_stores (PATTERN (insn), insn, fast); + else + mark_insn (insn, fast); + } + + if (dump_file) + fprintf (dump_file, "Finished finding needed instructions:\n"); +} + + +/* UD-based DSE routines. */ + +/* Mark instructions that define artifically-used registers, such as + the frame pointer and the stack pointer. */ + +static void +mark_artificial_uses (void) +{ + basic_block bb; + struct df_link *defs; + struct df_ref **use_rec; + + FOR_ALL_BB (bb) + { + for (use_rec = df_get_artificial_uses (bb->index); + *use_rec; use_rec++) + for (defs = DF_REF_CHAIN (*use_rec); defs; defs = defs->next) + mark_insn (DF_REF_INSN (defs->ref), false); + } +} + +/* Mark every instruction that defines a register value that INSN uses. */ + +static void +mark_reg_dependencies (rtx insn) +{ + struct df_link *defs; + struct df_ref **use_rec; + + /* If this is part of a libcall, mark the entire libcall. */ + if (find_reg_note (insn, REG_LIBCALL_ID, NULL_RTX)) + mark_libcall (insn, false); + + for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if (dump_file) + { + fprintf (dump_file, "Processing use of "); + print_simple_rtl (dump_file, DF_REF_REG (use)); + fprintf (dump_file, " in insn %d:\n", INSN_UID (insn)); + } + for (defs = DF_REF_CHAIN (use); defs; defs = defs->next) + mark_insn (DF_REF_INSN (defs->ref), false); + } +} + + +static void +end_ud_dce (void) +{ + sbitmap_free (marked); + gcc_assert (VEC_empty (rtx, worklist)); +} + + +/* UD-chain based DCE. */ + +static unsigned int +rest_of_handle_ud_dce (void) +{ + rtx insn; + + df_in_progress = false; + init_dce (false); + + prescan_insns_for_dce (false); + mark_artificial_uses (); + while (VEC_length (rtx, worklist) > 0) + { + insn = VEC_pop (rtx, worklist); + mark_reg_dependencies (insn); + } + /* Before any insns are deleted, we must remove the chains since + they are not bidirectional. */ + df_remove_problem (df_chain); + delete_unmarked_insns (); + + end_ud_dce (); + return 0; +} + + +static bool +gate_ud_dce (void) +{ + return optimize > 1 && flag_dce; +} + +struct tree_opt_pass pass_ud_rtl_dce = +{ + "dce", /* name */ + gate_ud_dce, /* gate */ + rest_of_handle_ud_dce, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_DCE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func | + TODO_df_finish | + TODO_ggc_collect, /* todo_flags_finish */ + 'w' /* letter */ +}; + +/* ------------------------------------------------------------------------- + Fast DCE functions + ------------------------------------------------------------------------- */ + + +/* Free the data allocated by init_dce. */ + +static void +fini_dce (void) +{ + sbitmap_free (marked); + bitmap_obstack_release (&dce_blocks_bitmap_obstack); + bitmap_obstack_release (&dce_tmp_bitmap_obstack); + df_in_progress = false; +} + + +/* Process basic block BB. Return true if the live_in set has + changed. */ + +static bool +dce_process_block (basic_block bb, bool redo_out) +{ + bitmap local_live = BITMAP_ALLOC (&dce_tmp_bitmap_obstack); + rtx insn; + bool block_changed; + struct df_ref **def_rec, **use_rec; + unsigned int bb_index = bb->index; + + if (redo_out) + { + /* Need to redo the live_out set of this block if when one of + the succs of this block has had a change in it live in + set. */ + edge e; + edge_iterator ei; + df_confluence_function_n con_fun_n = df_lr->problem->con_fun_n; + bitmap_clear (DF_LR_OUT (bb)); + FOR_EACH_EDGE (e, ei, bb->succs) + (*con_fun_n) (e); + } + + if (dump_file) + { + fprintf (dump_file, "processing block %d live out = ", bb->index); + df_print_regset (dump_file, DF_LR_OUT (bb)); + } + + bitmap_copy (local_live, DF_LR_OUT (bb)); + + /* Process the artificial defs and uses at the bottom of the block. */ + for (def_rec = df_get_artificial_defs (bb_index); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if (((DF_REF_FLAGS (def) & DF_REF_AT_TOP) == 0) + && (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL)))) + bitmap_clear_bit (local_live, DF_REF_REGNO (def)); + } + + for (use_rec = df_get_artificial_uses (bb_index); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if ((DF_REF_FLAGS (use) & DF_REF_AT_TOP) == 0) + bitmap_set_bit (local_live, DF_REF_REGNO (use)); + } + + FOR_BB_INSNS_REVERSE (bb, insn) + if (INSN_P (insn)) + { + /* If this is a recursive call, the libcall will have already + been marked. */ + if (!marked_insn_p (insn)) + { + bool needed = false; + + /* The insn is needed if there is someone who uses the output. */ + for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++) + if (bitmap_bit_p (local_live, DF_REF_REGNO (*def_rec))) + { + needed = true; + break; + } + + if (needed) + { + rtx note = find_reg_note (insn, REG_LIBCALL_ID, NULL_RTX); + + /* If we need to mark an insn in the middle of a + libcall, we need to back up to mark the entire + libcall. Given that libcalls are rare, rescanning + the block should be a reasonable solution to trying + to figure out how to back up. */ + if (note) + { + if (dump_file) + fprintf (dump_file, "needed libcall %d\n", INSN_UID (insn)); + mark_libcall (insn, true); + BITMAP_FREE (local_live); + return dce_process_block (bb, false); + } + else + mark_insn (insn, true); + } + } + + /* No matter if the instruction is needed or not, we remove + any regno in the defs from the live set. */ + df_simulate_defs (insn, local_live); + + /* On the other hand, we do not allow the dead uses to set + anything in local_live. */ + if (marked_insn_p (insn)) + df_simulate_uses (insn, local_live); + } + + for (def_rec = df_get_artificial_defs (bb_index); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if ((DF_REF_FLAGS (def) & DF_REF_AT_TOP) + && (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL)))) + bitmap_clear_bit (local_live, DF_REF_REGNO (def)); + } +#ifdef EH_USES + /* Process the uses that are live into an exception handler. */ + for (use_rec = df_get_artificial_uses (bb_index); *use_rec; use_rec++) + { + /* Add use to set of uses in this BB. */ + struct df_ref *use = *use_rec; + if (DF_REF_FLAGS (use) & DF_REF_AT_TOP) + bitmap_set_bit (local_live, DF_REF_REGNO (use)); + } +#endif + + block_changed = !bitmap_equal_p (local_live, DF_LR_IN (bb)); + if (block_changed) + bitmap_copy (DF_LR_IN (bb), local_live); + + BITMAP_FREE (local_live); + return block_changed; +} + +static void +fast_dce (void) +{ + int *postorder = df_get_postorder (DF_BACKWARD); + int n_blocks = df_get_n_blocks (DF_BACKWARD); + int i; + /* The set of blocks that have been seen on this iteration. */ + bitmap processed = BITMAP_ALLOC (&dce_blocks_bitmap_obstack); + /* The set of blocks that need to have the out vectors reset because + the in of one of their successors has changed. */ + bitmap redo_out = BITMAP_ALLOC (&dce_blocks_bitmap_obstack); + bitmap all_blocks = BITMAP_ALLOC (&dce_blocks_bitmap_obstack); + bool global_changed = true; + + int loop_count = 0; + + prescan_insns_for_dce (true); + + for (i = 0; i < n_blocks; i++) + bitmap_set_bit (all_blocks, postorder[i]); + + while (global_changed) + { + global_changed = false; + for (i = 0; i < n_blocks; i++) + { + int index = postorder[i]; + basic_block bb = BASIC_BLOCK (index); + bool local_changed; + + if (index < NUM_FIXED_BLOCKS) + { + bitmap_set_bit (processed, index); + continue; + } + + local_changed + = dce_process_block (bb, bitmap_bit_p (redo_out, index)); + bitmap_set_bit (processed, index); + + if (local_changed) + { + edge e; + edge_iterator ei; + FOR_EACH_EDGE (e, ei, bb->preds) + if (bitmap_bit_p (processed, e->src->index)) + /* Be tricky about when we need to iterate the + analysis. We only have redo the analysis if the + bitmaps change at the top of a block that is the + entry to a loop. */ + global_changed = true; + else + bitmap_set_bit (redo_out, e->src->index); + } + } + + if (global_changed) + { + /* Turn off the RUN_DCE flag to prevent recursive calls to + dce. */ + int old_flag = df_clear_flags (DF_LR_RUN_DCE); + + /* So something was deleted that requires a redo. Do it on + the cheap. */ + delete_unmarked_insns (); + sbitmap_zero (marked); + bitmap_clear (processed); + bitmap_clear (redo_out); + + /* We do not need to rescan any instructions. We only need + to redo the dataflow equations for the blocks that had a + change at the top of the block. Then we need to redo the + iteration. */ + df_analyze_problem (df_lr, all_blocks, postorder, n_blocks); + + if (old_flag & DF_LR_RUN_DCE) + df_set_flags (DF_LR_RUN_DCE); + prescan_insns_for_dce (true); + } + loop_count++; + } + + delete_unmarked_insns (); + + BITMAP_FREE (processed); + BITMAP_FREE (redo_out); + BITMAP_FREE (all_blocks); +} + + +/* Callback for running pass_rtl_dce. */ + +static unsigned int +rest_of_handle_fast_dce (void) +{ + init_dce (true); + fast_dce (); + fini_dce (); + df_in_progress = false; + return 0; +} + + +/* This is an internal call that is used by the df live register + problem to run fast dce as a side effect of creating the live + information. The stack is organized so that the lr problem is run, + this pass is run, which updates the live info and the df scanning + info, and then returns to allow the rest of the problems to be run. + + This can be called by elsewhere but it will not update the bit + vectors for any other problems than LR. +*/ + +void +run_fast_df_dce (void) +{ + if (flag_dce) + { + /* If dce is able to delete something, it has to happen + immediately. Otherwise there will be problems handling the + eq_notes. */ + enum df_changeable_flags old_flags + = df_clear_flags (DF_DEFER_INSN_RESCAN + DF_NO_INSN_RESCAN); + + df_in_progress = true; + rest_of_handle_fast_dce (); + df_set_flags (old_flags); + } +} + +static bool +gate_fast_dce (void) +{ + return optimize > 0 && flag_dce; +} + + +/* Run a fast DCE pass and return true if any instructions were + deleted. */ + +bool +run_fast_dce (void) +{ + return gate_fast_dce () && (rest_of_handle_fast_dce (), something_changed); +} + + +struct tree_opt_pass pass_fast_rtl_dce = +{ + "dce", /* name */ + gate_fast_dce, /* gate */ + rest_of_handle_fast_dce, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_DCE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func | + TODO_df_finish | + TODO_ggc_collect, /* todo_flags_finish */ + 'w' /* letter */ +}; + diff --git a/gcc/dce.h b/gcc/dce.h new file mode 100644 index 0000000..2971961 --- /dev/null +++ b/gcc/dce.h @@ -0,0 +1,29 @@ +/* RTL dead code elimination. + Copyright (C) 2005, 2006, 2007 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, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ + +#ifndef GCC_DCE_H +#define GCC_DCE_H + +struct df; + +extern bool run_fast_dce (void); +extern void run_fast_df_dce (void); + +#endif /* GCC_DCE_H */ @@ -1,5 +1,5 @@ /* DDG - Data Dependence Graph implementation. - Copyright (C) 2004, 2005, 2006 + Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Ayal Zaks and Mustafa Hagog <zaks,mustafa@il.ibm.com> @@ -43,7 +43,6 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "sbitmap.h" #include "expr.h" #include "bitmap.h" -#include "df.h" #include "ddg.h" /* A flag indicating that a ddg edge belongs to an SCC or not. */ @@ -230,10 +229,10 @@ create_ddg_dep_no_link (ddg_ptr g, ddg_node_ptr from, ddg_node_ptr to, for all its uses in the next iteration, and an output dependence to the first def of the next iteration. */ static void -add_deps_for_def (ddg_ptr g, struct df *df, struct df_ref *rd) +add_deps_for_def (ddg_ptr g, struct df_ref *rd) { int regno = DF_REF_REGNO (rd); - struct df_ru_bb_info *bb_info = DF_RU_BB_INFO (df, g->bb); + struct df_ru_bb_info *bb_info = DF_RU_BB_INFO (g->bb); struct df_link *r_use; int use_before_def = false; rtx def_insn = DF_REF_INSN (rd); @@ -265,7 +264,7 @@ add_deps_for_def (ddg_ptr g, struct df *df, struct df_ref *rd) there is a use between the two defs. */ if (! use_before_def) { - struct df_ref *def = df_bb_regno_first_def_find (df, g->bb, regno); + struct df_ref *def = df_bb_regno_first_def_find (g->bb, regno); int i; ddg_node_ptr dest_node; @@ -274,7 +273,7 @@ add_deps_for_def (ddg_ptr g, struct df *df, struct df_ref *rd) /* Check if there are uses after RD. */ for (i = src_node->cuid + 1; i < g->num_nodes; i++) - if (df_find_use (df, g->nodes[i].insn, rd->reg)) + if (df_find_use (g->nodes[i].insn, DF_REF_REG (rd))) return; dest_node = get_node_of_insn (g, def->insn); @@ -286,16 +285,16 @@ add_deps_for_def (ddg_ptr g, struct df *df, struct df_ref *rd) (nearest BLOCK_BEGIN) def of the next iteration, unless USE is followed by a def in the block. */ static void -add_deps_for_use (ddg_ptr g, struct df *df, struct df_ref *use) +add_deps_for_use (ddg_ptr g, struct df_ref *use) { int i; int regno = DF_REF_REGNO (use); - struct df_ref *first_def = df_bb_regno_first_def_find (df, g->bb, regno); + struct df_ref *first_def = df_bb_regno_first_def_find (g->bb, regno); ddg_node_ptr use_node; ddg_node_ptr def_node; struct df_rd_bb_info *bb_info; - bb_info = DF_RD_BB_INFO (df, g->bb); + bb_info = DF_RD_BB_INFO (g->bb); if (!first_def) return; @@ -307,7 +306,7 @@ add_deps_for_use (ddg_ptr g, struct df *df, struct df_ref *use) /* Make sure there are no defs after USE. */ for (i = use_node->cuid + 1; i < g->num_nodes; i++) - if (df_find_def (df, g->nodes[i].insn, use->reg)) + if (df_find_def (g->nodes[i].insn, DF_REF_REG (use))) return; /* We must not add ANTI dep when there is an intra-loop TRUE dep in the opposite direction. If the first_def reaches the USE then there is @@ -318,35 +317,35 @@ add_deps_for_use (ddg_ptr g, struct df *df, struct df_ref *use) /* Build inter-loop dependencies, by looking at DF analysis backwards. */ static void -build_inter_loop_deps (ddg_ptr g, struct df *df) +build_inter_loop_deps (ddg_ptr g) { unsigned rd_num, u_num; struct df_rd_bb_info *rd_bb_info; struct df_ru_bb_info *ru_bb_info; bitmap_iterator bi; - rd_bb_info = DF_RD_BB_INFO (df, g->bb); + rd_bb_info = DF_RD_BB_INFO (g->bb); /* Find inter-loop output and true deps by connecting downward exposed defs to the first def of the BB and to upwards exposed uses. */ EXECUTE_IF_SET_IN_BITMAP (rd_bb_info->gen, 0, rd_num, bi) { - struct df_ref *rd = DF_DEFS_GET (df, rd_num); + struct df_ref *rd = DF_DEFS_GET (rd_num); - add_deps_for_def (g, df, rd); + add_deps_for_def (g, rd); } - ru_bb_info = DF_RU_BB_INFO (df, g->bb); + ru_bb_info = DF_RU_BB_INFO (g->bb); /* Find inter-loop anti deps. We are interested in uses of the block that appear below all defs; this implies that these uses are killed. */ EXECUTE_IF_SET_IN_BITMAP (ru_bb_info->kill, 0, u_num, bi) { - struct df_ref *use = DF_USES_GET (df, u_num); - - /* We are interested in uses of this BB. */ - if (BLOCK_FOR_INSN (use->insn) == g->bb) - add_deps_for_use (g, df, use); + struct df_ref *use = DF_USES_GET (u_num); + if (!(DF_REF_FLAGS (use) & DF_REF_IN_NOTE)) + /* We are interested in uses of this BB. */ + if (BLOCK_FOR_INSN (use->insn) == g->bb) + add_deps_for_use (g, use); } } @@ -443,7 +442,7 @@ build_intra_loop_deps (ddg_ptr g) of ddg type that represents it. Initialize the ddg structure fields to the appropriate values. */ ddg_ptr -create_ddg (basic_block bb, struct df *df, int closing_branch_deps) +create_ddg (basic_block bb, int closing_branch_deps) { ddg_ptr g; rtx insn, first_note; @@ -520,7 +519,7 @@ create_ddg (basic_block bb, struct df *df, int closing_branch_deps) /* Build the data dependency graph. */ build_intra_loop_deps (g); - build_inter_loop_deps (g, df); + build_inter_loop_deps (g); return g; } @@ -1,5 +1,5 @@ /* DDG - Data Dependence Graph - interface. - Copyright (C) 2004 + Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Ayal Zaks and Mustafa Hagog <zaks,mustafa@il.ibm.com> @@ -27,7 +27,6 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "sbitmap.h" /* For basic_block. */ #include "basic-block.h" -/* For struct df. */ #include "df.h" typedef struct ddg_node *ddg_node_ptr; @@ -166,7 +165,7 @@ struct ddg_all_sccs }; -ddg_ptr create_ddg (basic_block, struct df *, int closing_branch_deps); +ddg_ptr create_ddg (basic_block, int closing_branch_deps); void free_ddg (ddg_ptr); void print_ddg (FILE *, ddg_ptr); diff --git a/gcc/df-core.c b/gcc/df-core.c index 7f89fcc..56eb039 100644 --- a/gcc/df-core.c +++ b/gcc/df-core.c @@ -1,5 +1,5 @@ /* Allocation for dataflow support routines. - Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Originally contributed by Michael P. Hayes (m.hayes@elec.canterbury.ac.nz, mhayes@redhat.com) @@ -36,51 +36,57 @@ dataflow problems: reaching defs, upward exposed uses, live variables, uninitialized variables, def-use chains, and use-def chains. However, the interface allows other dataflow problems to be defined as well. +Dataflow analysis is available in most of the rtl backend (the parts +between pass_df_initialize and pass_df_finish). It is quite likely +that these boundaries will be expanded in the future. The only +requirement is that there be a correct control flow graph. -USAGE: - -Here is an example of using the dataflow routines. - - struct df *df; +There are three variations of the live variable problem that are +available whenever dataflow is available. The LR problem finds the +areas that can reach a use of a variable, the UR problems finds the +areas tha can be reached from a definition of a variable. The LIVE +problem finds the intersection of these two areas. - df = df_init (init_flags); - - df_add_problem (df, problem, flags); +There are several optional problems. These can be enabled when they +are needed and disabled when they are not needed. - df_set_blocks (df, blocks); +Dataflow problems are generally solved in three layers. The bottom +layer is called scanning where a data structure is built for each rtl +insn that describes the set of defs and uses of that insn. Scanning +is generally kept up to date, i.e. as the insns changes, the scanned +version of that insn changes also. There are various mechanisms for +making this happen and are described in the INCREMENTAL SCANNING +section. - df_rescan_blocks (df, blocks); +In the middle layer, basic blocks are scanned to produce transfer +functions which describe the effects of that block on the a global +dataflow solution. The transfer functions are only rebuilt if the +some instruction within the block has changed. - df_analyze (df); - - df_dump (df, stderr); - - df_finish (df); +The top layer is the dataflow solution itself. The dataflow solution +is computed by using an efficient iterative solver and the trasfer +functions. The dataflow solution must be recomputed whenever the +control changes or if one of the transfer function changes. +USAGE: -DF_INIT simply creates a poor man's object (df) that needs to be -passed to all the dataflow routines. df_finish destroys this object -and frees up any allocated memory. +Here is an example of using the dataflow routines. -There are three flags that can be passed to df_init, each of these -flags controls the scanning of the rtl: + df_[ru,rd,urec,ri,chain]_add_problem (flags); -DF_HARD_REGS means that the scanning is to build information about -both pseudo registers and hardware registers. Without this -information, the problems will be solved only on pseudo registers. -DF_EQUIV_NOTES marks the uses present in EQUIV/EQUAL notes. -DF_SUBREGS return subregs rather than the inner reg. + df_set_blocks (blocks); + df_analyze (); -DF_ADD_PROBLEM adds a problem, defined by an instance to struct -df_problem, to the set of problems solved in this instance of df. All -calls to add a problem for a given instance of df must occur before -the first call to DF_RESCAN_BLOCKS, DF_SET_BLOCKS or DF_ANALYZE. + df_dump (stderr); -For all of the problems defined in df-problems.c, there are -convenience functions named DF_*_ADD_PROBLEM. + df_finish_pass (); +DF_[ru,rd,urec,ri,chain]_ADD_PROBLEM adds a problem, defined by an +instance to struct df_problem, to the set of problems solved in this +instance of df. All calls to add a problem for a given instance of df +must occur before the first call to DF_ANALYZE. Problems can be dependent on other problems. For instance, solving def-use or use-def chains is dependent on solving reaching @@ -95,6 +101,9 @@ that case, df will just be used to do the scanning. DF_SET_BLOCKS is an optional call used to define a region of the function on which the analysis will be performed. The normal case is to analyze the entire function and no call to df_set_blocks is made. +DF_SET_BLOCKS only effects the blocks that are effected when computing +the transfer functions and final solution. The insn level information +is always kept up to date. When a subset is given, the analysis behaves as if the function only contains those blocks and any edges that occur directly between the @@ -102,33 +111,107 @@ blocks in the set. Care should be taken to call df_set_blocks right before the call to analyze in order to eliminate the possibility that optimizations that reorder blocks invalidate the bitvector. - - -DF_RESCAN_BLOCKS is an optional call that causes the scanner to be - (re)run over the set of blocks passed in. If blocks is NULL, the entire -function (or all of the blocks defined in df_set_blocks) is rescanned. -If blocks contains blocks that were not defined in the call to -df_set_blocks, these blocks are added to the set of blocks. - - -DF_ANALYZE causes all of the defined problems to be (re)solved. It -does not cause blocks to be (re)scanned at the rtl level unless no -prior call is made to df_rescan_blocks. When DF_ANALYZE is completes, -the IN and OUT sets for each basic block contain the computer -information. The DF_*_BB_INFO macros can be used to access these -bitvectors. - +DF_ANALYZE causes all of the defined problems to be (re)solved. When +DF_ANALYZE is completes, the IN and OUT sets for each basic block +contain the computer information. The DF_*_BB_INFO macros can be used +to access these bitvectors. All defered rescannings are down before +the transfer functions are recompited. DF_DUMP can then be called to dump the information produce to some -file. - - - -DF_FINISH causes all of the datastructures to be cleaned up and freed. -The df_instance is also freed and its pointer should be NULLed. - - - +file. This calls DF_DUMP_START, to print the information that is not +basic block specific, and then calls DF_DUMP_TOP and DF_DUMP_BOTTOM +for each block to print the basic specific information. These parts +can all be called separately as part of a larger dump function. + + +DF_FINISH_PASS causes df_remove_problem to be called on all of the +optional problems. It also causes any insns whose scanning has been +defered to be rescanned as well as clears all of the changeable flags. +Setting the pass manager TODO_df_finish flag causes this function to +be run. However, the pass manager will call df_finish_pass AFTER the +pass dumping has been done, so if you want to see the results of the +optional problems in the pass dumps, use the TODO flag rather than +calling the function yourself. + +INCREMENTAL SCANNING + +There are four ways of doing the incremental scanning: + +1) Immediate rescanning - Calls to df_insn_rescan, df_notes_rescan, + df_bb_delete, df_insn_change_bb have been added to most of + the low level service functions that maintain the cfg and change + rtl. Calling and of these routines many cause some number of insns + to be rescanned. + + For most modern rtl passes, this is certainly the easiest way to + manage rescanning the insns. This technique also has the advantage + that the scanning information is always correct and can be relied + apon even after changes have been made to the instructions. This + technique is contra indicated in several cases: + + a) If def-use chains OR use-def chains (but not both) are built, + using this is SIMPLY WRONG. The problem is that when a ref is + deleted that is the target of an edge, there is not enough + information to efficiently find the source of the edge and + delete the edge. This leaves a dangling reference that may + cause problems. + + b) If def-use chains AND use-def chains are built, this may + produce unexpected results. The problem is that the incremental + scanning of an insn does not know how to repair the chains that + point into an insn when the insn changes. So the incremental + scanning just deletes the chains that enter and exit the insn + being changed. The dangling reference issue in (a) is not a + problem here, but if the pass is depending on the chains being + maintained after insns have been modified, this technique will + not do the correct thing. + + c) If the pass modifies insns several times, this incremental + updating may be expensive. + + d) If the pass modifies all of the insns, as does register + allocation, it is simply better to rescan the entire function. + + e) If the pass uses either non-standard or ancient techniques to + modify insns, automatic detection of the insns that need to be + rescanned may be impractical. Cse and regrename fall into this + category. + +2) Defered rescanning - Calls to df_insn_rescan, df_notes_rescan, and + df_insn_delete do not immediately change the insn but instead make + a note that the insn needs to be rescanned. The next call to + df_analyze, df_finish_pass, or df_process_deferred_rescans will + cause all of the pending rescans to be processed. + + This is the technique of choice if either 1a, 1b, or 1c are issues + in the pass. In the case of 1a or 1b, a call to df_remove_problem + (df_chain) should be made before the next call to df_analyze or + df_process_deferred_rescans. + + To enable this mode, call df_set_flags (DF_DEFER_INSN_RESCAN). + (This mode can be cleared by calling df_clear_flags + (DF_DEFER_INSN_RESCAN) but this does not cause the defered insns to + be rescanned. + + 3) Total rescanning - In this mode the rescanning is disabled. + However, the df information associated with deleted insn is delete + at the time the insn is deleted. At the end of the pass, a call + must be made to df_insn_rescan_all. This method is used by the + register allocator since it generally changes each insn multiple + times (once for each ref) and does not need to make use of the + updated scanning information. + + It is also currently used by two older passes (cse, and regrename) + which change insns in hard to track ways. It is hoped that this + will be fixed soon since this it is expensive to rescan all of the + insns when only a small number of them have really changed. + +4) Do it yourself - In this mechanism, the pass updates the insns + itself using the low level df primatives. Currently no pass does + this, but it has the advantage that it is quite efficient given + that the pass generally has exact knowledge of what it is changing. + +DATA STRUCTURES Scanning produces a `struct df_ref' data structure (ref) is allocated for every register reference (def or use) and this records the insn @@ -145,7 +228,6 @@ The rest of the backend should be upgraded to using and maintaining the linked information such as def use or use def chains. - PHILOSOPHY: While incremental bitmaps are not worthwhile to maintain, incremental @@ -186,26 +268,39 @@ register. Note that the reg-def and reg-use chains are generally short for pseudos and long for the hard registers. +ACCESSING INSNS: + +1) The df insn information is kept in the insns array. This array is + indexed by insn uid. + +2) Each insn has three sets of refs: They are linked into one of three + lists: the insn's defs list (accessed by the DF_INSN_DEFS or + DF_INSN_UID_DEFS macros), the insn's uses list (accessed by the + DF_INSN_USES or DF_INSN_UID_USES macros) or the insn's eq_uses list + (accessed by the DF_INSN_EQ_USES or DF_INSN_UID_EQ_USES macros). + The latter list are the list of references in REG_EQUAL or + REG_EQUIV notes. These macros produce a ref (or NULL), the rest of + the list can be obtained by traversal of the NEXT_REF field + (accessed by the DF_REF_NEXT_REF macro.) There is no significance + to the ordering of the uses or refs in an instruction. + +3) Each insn has a logical uid field (LUID). When properly set, this + is an integer that numbers each insn in the basic block, in order from + the start of the block. The numbers are only correct after a call to + df_analyse. They will rot after insns are added deleted or moved + around. + ACCESSING REFS: There are 4 ways to obtain access to refs: 1) References are divided into two categories, REAL and ARTIFICIAL. - REAL refs are associated with instructions. They are linked into - either in the insn's defs list (accessed by the DF_INSN_DEFS or - DF_INSN_UID_DEFS macros) or the insn's uses list (accessed by the - DF_INSN_USES or DF_INSN_UID_USES macros). These macros produce a - ref (or NULL), the rest of the list can be obtained by traversal of - the NEXT_REF field (accessed by the DF_REF_NEXT_REF macro.) There - is no significance to the ordering of the uses or refs in an - instruction. + REAL refs are associated with instructions. ARTIFICIAL refs are associated with basic blocks. The heads of - these lists can be accessed by calling get_artificial_defs or - get_artificial_uses for the particular basic block. Artificial - defs and uses are only there if DF_HARD_REGS was specified when the - df instance was created. + these lists can be accessed by calling df_get_artificial_defs or + df_get_artificial_uses for the particular basic block. Artificial defs and uses occur both at the beginning and ends of blocks. @@ -225,34 +320,45 @@ There are 4 ways to obtain access to refs: Artificial defs occur at the end of the entry block. These arise from registers that are live at entry to the function. -2) All of the uses and defs associated with each pseudo or hard - register are linked in a bidirectional chain. These are called - reg-use or reg_def chains. +2) There are three types of refs: defs, uses and eq_uses. (Eq_uses are + uses that appear inside a REG_EQUAL or REG_EQUIV note.) - The first use (or def) for a register can be obtained using the - DF_REG_USE_GET macro (or DF_REG_DEF_GET macro). Subsequent uses - for the same regno can be obtained by following the next_reg field - of the ref. + All of the eq_uses, uses and defs associated with each pseudo or + hard register may be linked in a bidirectional chain. These are + called reg-use or reg_def chains. If the changeable flag + DF_EQ_NOTES is set when the chains are built, the eq_uses will be + treated like uses. If it is not set they are ignored. + + The first use, eq_use or def for a register can be obtained using + the DF_REG_USE_CHAIN, DF_REG_EQ_USE_CHAIN or DF_REG_DEF_CHAIN + macros. Subsequent uses for the same regno can be obtained by + following the next_reg field of the ref. The number of elements in + each of the chains can be found by using the DF_REG_USE_COUNT, + DF_REG_EQ_USE_COUNT or DF_REG_DEF_COUNT macros. In previous versions of this code, these chains were ordered. It has not been practical to continue this practice. 3) If def-use or use-def chains are built, these can be traversed to - get to other refs. + get to other refs. If the flag DF_EQ_NOTES has been set, the chains + include the eq_uses. Otherwise these are ignored when building the + chains. 4) An array of all of the uses (and an array of all of the defs) can + be built. These arrays are indexed by the value in the id structure. These arrays are only lazily kept up to date, and that process can be expensive. To have these arrays built, call - df_reorganize_refs. Note that the values in the id field of a ref - may change across calls to df_analyze or df_reorganize refs. + df_reorganize_defs or df_reorganize_uses. If the flag DF_EQ_NOTES + has been set the array will contain the eq_uses. Otherwise these + are ignored when building the array and assigning the ids. Note + that the values in the id field of a ref may change across calls to + df_analyze or df_reorganize_defs or df_reorganize_uses. If the only use of this array is to find all of the refs, it is better to traverse all of the registers and then traverse all of reg-use or reg-def chains. - - NOTES: Embedded addressing side-effects, such as POST_INC or PRE_INC, generate @@ -296,113 +402,118 @@ are write-only operations. #include "df.h" #include "tree-pass.h" -static struct df *ddf = NULL; -struct df *shared_df = NULL; - static void *df_get_bb_info (struct dataflow *, unsigned int); static void df_set_bb_info (struct dataflow *, unsigned int, void *); -/*---------------------------------------------------------------------------- - Functions to create, destroy and manipulate an instance of df. -----------------------------------------------------------------------------*/ +#ifdef DF_DEBUG_CFG +static void df_set_clean_cfg (void); +#endif +/* An obstack for bitmap not related to specific dataflow problems. + This obstack should e.g. be used for bitmaps with a short life time + such as temporary bitmaps. */ -/* Initialize dataflow analysis and allocate and initialize dataflow - memory. */ +bitmap_obstack df_bitmap_obstack; -struct df * -df_init (int flags) -{ - struct df *df = XCNEW (struct df); - /* This is executed once per compilation to initialize platform - specific data structures. */ - df_hard_reg_init (); - - /* All df instance must define the scanning problem. */ - df_scan_add_problem (df, flags); - ddf = df; - return df; -} +/*---------------------------------------------------------------------------- + Functions to create, destroy and manipulate an instance of df. +----------------------------------------------------------------------------*/ + +struct df *df; -/* Add PROBLEM to the DF instance. */ +/* Add PROBLEM (and any dependent problems) to the DF instance. */ -struct dataflow * -df_add_problem (struct df *df, struct df_problem *problem, int flags) +void +df_add_problem (struct df_problem *problem) { struct dataflow *dflow; + int i; /* First try to add the dependent problem. */ - if (problem->dependent_problem_fun) - (problem->dependent_problem_fun) (df, 0); + if (problem->dependent_problem) + df_add_problem (problem->dependent_problem); /* Check to see if this problem has already been defined. If it has, just return that instance, if not, add it to the end of the vector. */ dflow = df->problems_by_index[problem->id]; if (dflow) - return dflow; + return; /* Make a new one and add it to the end. */ dflow = XCNEW (struct dataflow); - dflow->flags = flags; - dflow->df = df; dflow->problem = problem; - df->problems_in_order[df->num_problems_defined++] = dflow; + dflow->computed = false; + dflow->solutions_dirty = true; df->problems_by_index[dflow->problem->id] = dflow; - return dflow; + /* Keep the defined problems ordered by index. This solves the + problem that RI will use the information from UREC if UREC has + been defined, or from LIVE if LIVE is defined and otherwise LR. + However for this to work, the computation of RI must be pushed + after which ever of those problems is defined, but we do not + require any of those except for LR to have actually been + defined. */ + df->num_problems_defined++; + for (i = df->num_problems_defined - 2; i >= 0; i--) + { + if (problem->id < df->problems_in_order[i]->problem->id) + df->problems_in_order[i+1] = df->problems_in_order[i]; + else + { + df->problems_in_order[i+1] = dflow; + return; + } + } + df->problems_in_order[0] = dflow; } /* Set the MASK flags in the DFLOW problem. The old flags are returned. If a flag is not allowed to be changed this will fail if checking is enabled. */ -int -df_set_flags (struct dataflow *dflow, int mask) +enum df_changeable_flags +df_set_flags (enum df_changeable_flags changeable_flags) { - int old_flags = dflow->flags; - - gcc_assert (!(mask & (~dflow->problem->changeable_flags))); - - dflow->flags |= mask; - + enum df_changeable_flags old_flags = df->changeable_flags; + df->changeable_flags |= changeable_flags; return old_flags; } + /* Clear the MASK flags in the DFLOW problem. The old flags are returned. If a flag is not allowed to be changed this will fail if checking is enabled. */ -int -df_clear_flags (struct dataflow *dflow, int mask) +enum df_changeable_flags +df_clear_flags (enum df_changeable_flags changeable_flags) { - int old_flags = dflow->flags; - - gcc_assert (!(mask & (~dflow->problem->changeable_flags))); - - dflow->flags &= !mask; - + enum df_changeable_flags old_flags = df->changeable_flags; + df->changeable_flags &= ~changeable_flags; return old_flags; } + /* Set the blocks that are to be considered for analysis. If this is not called or is called with null, the entire function in analyzed. */ void -df_set_blocks (struct df *df, bitmap blocks) +df_set_blocks (bitmap blocks) { if (blocks) { + if (dump_file) + bitmap_print (dump_file, blocks, "setting blocks to analyze ", "\n"); if (df->blocks_to_analyze) { int p; - bitmap diff = BITMAP_ALLOC (NULL); + bitmap diff = BITMAP_ALLOC (&df_bitmap_obstack); bitmap_and_compl (diff, df->blocks_to_analyze, blocks); - for (p = df->num_problems_defined - 1; p >= 0 ;p--) + for (p = df->num_problems_defined - 1; p >= DF_FIRST_OPTIONAL_PROBLEM ;p--) { struct dataflow *dflow = df->problems_in_order[p]; if (dflow->problem->reset_fun) - dflow->problem->reset_fun (dflow, df->blocks_to_analyze); + dflow->problem->reset_fun (df->blocks_to_analyze); else if (dflow->problem->free_bb_fun) { bitmap_iterator bi; @@ -413,9 +524,12 @@ df_set_blocks (struct df *df, bitmap blocks) basic_block bb = BASIC_BLOCK (bb_index); if (bb) { - dflow->problem->free_bb_fun - (dflow, bb, df_get_bb_info (dflow, bb_index)); - df_set_bb_info (dflow, bb_index, NULL); + void *bb_info = df_get_bb_info (dflow, bb_index); + if (bb_info) + { + dflow->problem->free_bb_fun (bb, bb_info); + df_set_bb_info (dflow, bb_index, NULL); + } } } } @@ -427,12 +541,11 @@ df_set_blocks (struct df *df, bitmap blocks) { /* If we have not actually run scanning before, do not try to clear anything. */ - struct dataflow *scan_dflow = df->problems_by_index [DF_SCAN]; - if (scan_dflow->problem_data) + if (df_scan->problem_data) { bitmap blocks_to_reset = NULL; int p; - for (p = df->num_problems_defined - 1; p >= 0 ;p--) + for (p = df->num_problems_defined - 1; p >= DF_FIRST_OPTIONAL_PROBLEM ;p--) { struct dataflow *dflow = df->problems_in_order[p]; if (dflow->problem->reset_fun) @@ -440,267 +553,459 @@ df_set_blocks (struct df *df, bitmap blocks) if (!blocks_to_reset) { basic_block bb; - blocks_to_reset = BITMAP_ALLOC (NULL); + blocks_to_reset = + BITMAP_ALLOC (&df_bitmap_obstack); FOR_ALL_BB(bb) { bitmap_set_bit (blocks_to_reset, bb->index); } } - dflow->problem->reset_fun (dflow, blocks_to_reset); + dflow->problem->reset_fun (blocks_to_reset); } } if (blocks_to_reset) BITMAP_FREE (blocks_to_reset); } - df->blocks_to_analyze = BITMAP_ALLOC (NULL); + df->blocks_to_analyze = BITMAP_ALLOC (&df_bitmap_obstack); } bitmap_copy (df->blocks_to_analyze, blocks); + df->analyze_subset = true; } else { + if (dump_file) + fprintf (dump_file, "clearing blocks to analyze\n"); if (df->blocks_to_analyze) { BITMAP_FREE (df->blocks_to_analyze); df->blocks_to_analyze = NULL; } + df->analyze_subset = false; } + + /* Setting the blocks causes the refs to be unorganized since only + the refs in the blocks are seen. */ + df_maybe_reorganize_def_refs (DF_REF_ORDER_NO_TABLE); + df_maybe_reorganize_use_refs (DF_REF_ORDER_NO_TABLE); + df_mark_solutions_dirty (); } -/* Free all of the per basic block dataflow from all of the problems. - This is typically called before a basic block is deleted and the - problem will be reanalyzed. */ +/* Delete a DFLOW problem (and any problems that depend on this + problem). */ void -df_delete_basic_block (struct df *df, int bb_index) +df_remove_problem (struct dataflow *dflow) { - basic_block bb = BASIC_BLOCK (bb_index); + struct df_problem *problem; int i; - - for (i = 0; i < df->num_problems_defined; i++) + int start = 0; + + if (!dflow) + return; + + problem = dflow->problem; + gcc_assert (problem->remove_problem_fun); + + /* Normally only optional problems are removed, but during global, + we remove ur and live and replace it with urec. */ + if (problem->id >= DF_FIRST_OPTIONAL_PROBLEM) + start = DF_FIRST_OPTIONAL_PROBLEM; + + /* Delete any problems that depended on this problem first. */ + for (i = start; i < df->num_problems_defined; i++) + if (df->problems_in_order[i]->problem->dependent_problem == problem) + df_remove_problem (df->problems_in_order[i]); + + /* Now remove this problem. */ + for (i = start; i < df->num_problems_defined; i++) + if (df->problems_in_order[i] == dflow) + { + int j; + for (j = i + 1; j < df->num_problems_defined; j++) + df->problems_in_order[j-1] = df->problems_in_order[j]; + df->problems_in_order[j] = NULL; + df->num_problems_defined--; + break; + } + + (problem->remove_problem_fun) (); + df->problems_by_index[problem->id] = NULL; +} + + +/* Remove all of the problems that are not permanent. Scanning, lr, + ur and live are permanent, the rest are removeable. Also clear all + of the changeable_flags. */ + +void +df_finish_pass (void) +{ + int i; + int removed = 0; + +#ifdef ENABLE_CHECKING + enum df_changeable_flags saved_flags; +#endif + + if (!df) + return; + + df_maybe_reorganize_def_refs (DF_REF_ORDER_NO_TABLE); + df_maybe_reorganize_use_refs (DF_REF_ORDER_NO_TABLE); + +#ifdef ENABLE_CHECKING + saved_flags = df->changeable_flags; +#endif + + for (i = DF_FIRST_OPTIONAL_PROBLEM; i < df->num_problems_defined; i++) { struct dataflow *dflow = df->problems_in_order[i]; - if (dflow->problem->free_bb_fun) - dflow->problem->free_bb_fun - (dflow, bb, df_get_bb_info (dflow, bb_index)); + struct df_problem *problem = dflow->problem; + + gcc_assert (problem->remove_problem_fun); + (problem->remove_problem_fun) (); + df->problems_in_order[i] = NULL; + df->problems_by_index[problem->id] = NULL; + removed++; + } + df->num_problems_defined -= removed; + + /* Clear all of the flags. */ + df->changeable_flags = 0; + df_process_deferred_rescans (); + + /* Set the focus back to the whole function. */ + if (df->blocks_to_analyze) + { + BITMAP_FREE (df->blocks_to_analyze); + df->blocks_to_analyze = NULL; + df_mark_solutions_dirty (); + df->analyze_subset = false; } + +#ifdef ENABLE_CHECKING + /* Verification will fail in DF_NO_INSN_RESCAN. */ + if (!(saved_flags & DF_NO_INSN_RESCAN)) + { + df_lr_verify_transfer_functions (); + if (df_live) + df_live_verify_transfer_functions (); + } + +#ifdef DF_DEBUG_CFG + df_set_clean_cfg (); +#endif +#endif +} + + +/* Set up the dataflow instance for the entire back end. */ + +static unsigned int +rest_of_handle_df_initialize (void) +{ + gcc_assert (!df); + df = XCNEW (struct df); + df->changeable_flags = 0; + + bitmap_obstack_initialize (&df_bitmap_obstack); + + /* Set this to a conservative value. Stack_ptr_mod will compute it + correctly later. */ + current_function_sp_is_unchanging = 0; + + df_scan_add_problem (); + df_scan_alloc (NULL); + + /* These three problems are permanent. */ + df_lr_add_problem (); + if (optimize) + df_live_add_problem (); + + df->postorder = XNEWVEC (int, last_basic_block); + df->postorder_inverted = XNEWVEC (int, last_basic_block); + df->n_blocks = post_order_compute (df->postorder, true, true); + df->n_blocks_inverted = inverted_post_order_compute (df->postorder_inverted); + gcc_assert (df->n_blocks == df->n_blocks_inverted); + + df->hard_regs_live_count = XNEWVEC (unsigned int, FIRST_PSEUDO_REGISTER); + memset (df->hard_regs_live_count, 0, + sizeof (unsigned int) * FIRST_PSEUDO_REGISTER); + + df_hard_reg_init (); + /* After reload, some ports add certain bits to regs_ever_live so + this cannot be reset. */ + df_compute_regs_ever_live (true); + df_scan_blocks (); + df_compute_regs_ever_live (false); + return 0; +} + + +static bool +gate_opt (void) +{ + return optimize > 0; } +struct tree_opt_pass pass_df_initialize_opt = +{ + "dfinit", /* name */ + gate_opt, /* gate */ + rest_of_handle_df_initialize, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ + 'z' /* letter */ +}; + + +static bool +gate_no_opt (void) +{ + return optimize == 0; +} + + +struct tree_opt_pass pass_df_initialize_no_opt = +{ + "dfinit", /* name */ + gate_no_opt, /* gate */ + rest_of_handle_df_initialize, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ + 'z' /* letter */ +}; + + /* Free all the dataflow info and the DF structure. This should be called from the df_finish macro which also NULLs the parm. */ -void -df_finish1 (struct df *df) +static unsigned int +rest_of_handle_df_finish (void) { int i; + gcc_assert (df); + for (i = 0; i < df->num_problems_defined; i++) - df->problems_in_order[i]->problem->free_fun (df->problems_in_order[i]); + { + struct dataflow *dflow = df->problems_in_order[i]; + dflow->problem->free_fun (); + } + if (df->postorder) + free (df->postorder); + if (df->postorder_inverted) + free (df->postorder_inverted); + free (df->hard_regs_live_count); free (df); + df = NULL; + + bitmap_obstack_release (&df_bitmap_obstack); + return 0; } + +struct tree_opt_pass pass_df_finish = +{ + "dfinish", /* name */ + NULL, /* gate */ + rest_of_handle_df_finish, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ + 'z' /* letter */ +}; + + + + /*---------------------------------------------------------------------------- The general data flow analysis engine. ----------------------------------------------------------------------------*/ -/* Hybrid search algorithm from "Implementation Techniques for - Efficient Data-Flow Analysis of Large Programs". */ +/* Helper function for df_worklist_dataflow. + Propagate the dataflow forward. + Given a BB_INDEX, do the dataflow propagation + and set bits on for successors in PENDING + if the out set of the dataflow has changed. */ static void -df_hybrid_search_forward (basic_block bb, - struct dataflow *dataflow, - bool single_pass) +df_worklist_propagate_forward (struct dataflow *dataflow, + unsigned bb_index, + unsigned *bbindex_to_postorder, + bitmap pending, + sbitmap considered) { - int result_changed; - int i = bb->index; edge e; edge_iterator ei; + basic_block bb = BASIC_BLOCK (bb_index); - SET_BIT (dataflow->visited, bb->index); - gcc_assert (TEST_BIT (dataflow->pending, bb->index)); - RESET_BIT (dataflow->pending, i); - - /* Calculate <conf_op> of predecessor_outs. */ + /* Calculate <conf_op> of incoming edges. */ if (EDGE_COUNT (bb->preds) > 0) FOR_EACH_EDGE (e, ei, bb->preds) - { - if (!TEST_BIT (dataflow->considered, e->src->index)) - continue; - - dataflow->problem->con_fun_n (dataflow, e); - } + { + if (TEST_BIT (considered, e->src->index)) + dataflow->problem->con_fun_n (e); + } else if (dataflow->problem->con_fun_0) - dataflow->problem->con_fun_0 (dataflow, bb); - - result_changed = dataflow->problem->trans_fun (dataflow, i); - - if (!result_changed || single_pass) - return; - - FOR_EACH_EDGE (e, ei, bb->succs) - { - if (e->dest->index == i) - continue; - if (!TEST_BIT (dataflow->considered, e->dest->index)) - continue; - SET_BIT (dataflow->pending, e->dest->index); - } - - FOR_EACH_EDGE (e, ei, bb->succs) + dataflow->problem->con_fun_0 (bb); + + if (dataflow->problem->trans_fun (bb_index)) { - if (e->dest->index == i) - continue; - - if (!TEST_BIT (dataflow->considered, e->dest->index)) - continue; - if (!TEST_BIT (dataflow->visited, e->dest->index)) - df_hybrid_search_forward (e->dest, dataflow, single_pass); + /* The out set of this block has changed. + Propagate to the outgoing blocks. */ + FOR_EACH_EDGE (e, ei, bb->succs) + { + unsigned ob_index = e->dest->index; + + if (TEST_BIT (considered, ob_index)) + bitmap_set_bit (pending, bbindex_to_postorder[ob_index]); + } } } + +/* Helper function for df_worklist_dataflow. + Propagate the dataflow backward. */ + static void -df_hybrid_search_backward (basic_block bb, - struct dataflow *dataflow, - bool single_pass) +df_worklist_propagate_backward (struct dataflow *dataflow, + unsigned bb_index, + unsigned *bbindex_to_postorder, + bitmap pending, + sbitmap considered) { - int result_changed; - int i = bb->index; edge e; edge_iterator ei; - - SET_BIT (dataflow->visited, bb->index); - gcc_assert (TEST_BIT (dataflow->pending, bb->index)); - RESET_BIT (dataflow->pending, i); + basic_block bb = BASIC_BLOCK (bb_index); - /* Calculate <conf_op> of predecessor_outs. */ + /* Calculate <conf_op> of incoming edges. */ if (EDGE_COUNT (bb->succs) > 0) - FOR_EACH_EDGE (e, ei, bb->succs) + FOR_EACH_EDGE (e, ei, bb->succs) { - if (!TEST_BIT (dataflow->considered, e->dest->index)) - continue; - - dataflow->problem->con_fun_n (dataflow, e); + if (TEST_BIT (considered, e->dest->index)) + dataflow->problem->con_fun_n (e); } else if (dataflow->problem->con_fun_0) - dataflow->problem->con_fun_0 (dataflow, bb); - - result_changed = dataflow->problem->trans_fun (dataflow, i); - - if (!result_changed || single_pass) - return; - - FOR_EACH_EDGE (e, ei, bb->preds) - { - if (e->src->index == i) - continue; - - if (!TEST_BIT (dataflow->considered, e->src->index)) - continue; + dataflow->problem->con_fun_0 (bb); - SET_BIT (dataflow->pending, e->src->index); - } - - FOR_EACH_EDGE (e, ei, bb->preds) + if (dataflow->problem->trans_fun (bb_index)) { - if (e->src->index == i) - continue; - - if (!TEST_BIT (dataflow->considered, e->src->index)) - continue; - - if (!TEST_BIT (dataflow->visited, e->src->index)) - df_hybrid_search_backward (e->src, dataflow, single_pass); + /* The out set of this block has changed. + Propagate to the outgoing blocks. */ + FOR_EACH_EDGE (e, ei, bb->preds) + { + unsigned ob_index = e->src->index; + + if (TEST_BIT (considered, ob_index)) + bitmap_set_bit (pending, bbindex_to_postorder[ob_index]); + } } } -/* This function will perform iterative bitvector dataflow described - by DATAFLOW, producing the in and out sets. Only the part of the - cfg induced by blocks in DATAFLOW->order is taken into account. - - SINGLE_PASS is true if you just want to make one pass over the - blocks. */ +/* Worklist-based dataflow solver. It uses sbitmap as a worklist, + with "n"-th bit representing the n-th block in the reverse-postorder order. + This is so-called over-eager algorithm where it propagates + changes on demand. This algorithm may visit blocks more than + iterative method if there are deeply nested loops. + Worklist algorithm works better than iterative algorithm + for CFGs with no nested loops. + In practice, the measurement shows worklist algorithm beats + iterative algorithm by some margin overall. + Note that this is slightly different from the traditional textbook worklist solver, + in that the worklist is effectively sorted by the reverse postorder. + For CFGs with no nested loops, this is optimal. */ -void -df_iterative_dataflow (struct dataflow *dataflow, - bitmap blocks_to_consider, bitmap blocks_to_init, - int *blocks_in_postorder, int n_blocks, - bool single_pass) +void +df_worklist_dataflow (struct dataflow *dataflow, + bitmap blocks_to_consider, + int *blocks_in_postorder, + int n_blocks) { - unsigned int idx; - int i; - sbitmap visited = sbitmap_alloc (last_basic_block); - sbitmap pending = sbitmap_alloc (last_basic_block); + bitmap pending = BITMAP_ALLOC (&df_bitmap_obstack); sbitmap considered = sbitmap_alloc (last_basic_block); bitmap_iterator bi; + unsigned int *bbindex_to_postorder; + int i; + unsigned int index; + enum df_flow_dir dir = dataflow->problem->dir; - dataflow->visited = visited; - dataflow->pending = pending; - dataflow->considered = considered; + gcc_assert (dir != DF_NONE); - sbitmap_zero (visited); - sbitmap_zero (pending); - sbitmap_zero (considered); + /* BBINDEX_TO_POSTORDER maps the bb->index to the reverse postorder. */ + bbindex_to_postorder = + (unsigned int *)xmalloc (last_basic_block * sizeof (unsigned int)); - gcc_assert (dataflow->problem->dir); + /* Initialize the array to an out-of-bound value. */ + for (i = 0; i < last_basic_block; i++) + bbindex_to_postorder[i] = last_basic_block; - EXECUTE_IF_SET_IN_BITMAP (blocks_to_consider, 0, idx, bi) + /* Initialize the considered map. */ + sbitmap_zero (considered); + EXECUTE_IF_SET_IN_BITMAP (blocks_to_consider, 0, index, bi) { - SET_BIT (considered, idx); + SET_BIT (considered, index); } + /* Initialize the mapping of block index to postorder. */ for (i = 0; i < n_blocks; i++) { - idx = blocks_in_postorder[i]; - SET_BIT (pending, idx); - }; + bbindex_to_postorder[blocks_in_postorder[i]] = i; + /* Add all blocks to the worklist. */ + bitmap_set_bit (pending, i); + } - dataflow->problem->init_fun (dataflow, blocks_to_init); + if (dataflow->problem->init_fun) + dataflow->problem->init_fun (blocks_to_consider); - while (1) + while (!bitmap_empty_p (pending)) { + unsigned bb_index; - /* For forward problems, you want to pass in reverse postorder - and for backward problems you want postorder. This has been - shown to be as good as you can do by several people, the - first being Mathew Hecht in his phd dissertation. - - The nodes are passed into this function in postorder. */ - - if (dataflow->problem->dir == DF_FORWARD) - { - for (i = n_blocks - 1 ; i >= 0 ; i--) - { - idx = blocks_in_postorder[i]; - - if (TEST_BIT (pending, idx) && !TEST_BIT (visited, idx)) - df_hybrid_search_forward (BASIC_BLOCK (idx), dataflow, single_pass); - } - } - else - { - for (i = 0; i < n_blocks; i++) - { - idx = blocks_in_postorder[i]; - - if (TEST_BIT (pending, idx) && !TEST_BIT (visited, idx)) - df_hybrid_search_backward (BASIC_BLOCK (idx), dataflow, single_pass); - } - } + index = bitmap_first_set_bit (pending); + bitmap_clear_bit (pending, index); - if (sbitmap_first_set_bit (pending) == -1) - break; + bb_index = blocks_in_postorder[index]; - sbitmap_zero (visited); + if (dir == DF_FORWARD) + df_worklist_propagate_forward (dataflow, bb_index, + bbindex_to_postorder, + pending, considered); + else + df_worklist_propagate_backward (dataflow, bb_index, + bbindex_to_postorder, + pending, considered); } - sbitmap_free (pending); - sbitmap_free (visited); + BITMAP_FREE (pending); sbitmap_free (considered); + free (bbindex_to_postorder); } @@ -723,59 +1028,48 @@ df_prune_to_subcfg (int list[], unsigned len, bitmap blocks) /* Execute dataflow analysis on a single dataflow problem. - There are three sets of blocks passed in: - BLOCKS_TO_CONSIDER are the blocks whose solution can either be examined or will be computed. For calls from DF_ANALYZE, this is - the set of blocks that has been passed to DF_SET_BLOCKS. For calls - from DF_ANALYZE_SIMPLE_CHANGE_SOME_BLOCKS, this is the set of - blocks in the fringe (the set of blocks passed in plus the set of - immed preds and succs of those blocks). - - BLOCKS_TO_INIT are the blocks whose solution will be changed by - this iteration. For calls from DF_ANALYZE, this is the set of - blocks that has been passed to DF_SET_BLOCKS. For calls from - DF_ANALYZE_SIMPLE_CHANGE_SOME_BLOCKS, this is the set of blocks - passed in. - - BLOCKS_TO_SCAN are the set of blocks that need to be rescanned. - For calls from DF_ANALYZE, this is the accumulated set of blocks - that has been passed to DF_RESCAN_BLOCKS since the last call to - DF_ANALYZE. For calls from DF_ANALYZE_SIMPLE_CHANGE_SOME_BLOCKS, - this is the set of blocks passed in. - - blocks_to_consider blocks_to_init blocks_to_scan - full redo all all all - partial redo all all sub - small fixup fringe sub sub + the set of blocks that has been passed to DF_SET_BLOCKS. */ void df_analyze_problem (struct dataflow *dflow, bitmap blocks_to_consider, - bitmap blocks_to_init, - bitmap blocks_to_scan, - int *postorder, int n_blocks, bool single_pass) + int *postorder, int n_blocks) { + timevar_push (dflow->problem->tv_id); + +#ifdef ENABLE_CHECKING + if (dflow->problem->verify_start_fun) + dflow->problem->verify_start_fun (); +#endif + /* (Re)Allocate the datastructures necessary to solve the problem. */ if (dflow->problem->alloc_fun) - dflow->problem->alloc_fun (dflow, blocks_to_scan, blocks_to_init); + dflow->problem->alloc_fun (blocks_to_consider); - /* Set up the problem and compute the local information. This - function is passed both the blocks_to_consider and the - blocks_to_scan because the RD and RU problems require the entire - function to be rescanned if they are going to be updated. */ + /* Set up the problem and compute the local information. */ if (dflow->problem->local_compute_fun) - dflow->problem->local_compute_fun (dflow, blocks_to_consider, blocks_to_scan); + dflow->problem->local_compute_fun (blocks_to_consider); /* Solve the equations. */ if (dflow->problem->dataflow_fun) - dflow->problem->dataflow_fun (dflow, blocks_to_consider, blocks_to_init, - postorder, n_blocks, single_pass); + dflow->problem->dataflow_fun (dflow, blocks_to_consider, + postorder, n_blocks); /* Massage the solution. */ if (dflow->problem->finalize_fun) - dflow->problem->finalize_fun (dflow, blocks_to_consider); + dflow->problem->finalize_fun (blocks_to_consider); + +#ifdef ENABLE_CHECKING + if (dflow->problem->verify_end_fun) + dflow->problem->verify_end_fun (); +#endif + + timevar_pop (dflow->problem->tv_id); + + dflow->computed = true; } @@ -783,35 +1077,56 @@ df_analyze_problem (struct dataflow *dflow, BLOCKS, or for the whole CFG if BLOCKS is zero. */ void -df_analyze (struct df *df) +df_analyze (void) { - int *postorder = XNEWVEC (int, last_basic_block); - bitmap current_all_blocks = BITMAP_ALLOC (NULL); - int n_blocks; - int i; + bitmap current_all_blocks = BITMAP_ALLOC (&df_bitmap_obstack); bool everything; - - n_blocks = post_order_compute (postorder, true); - - if (n_blocks != n_basic_blocks) - delete_unreachable_blocks (); - - for (i = 0; i < n_blocks; i++) - bitmap_set_bit (current_all_blocks, postorder[i]); - - /* No one called df_rescan_blocks, so do it. */ - if (!df->blocks_to_scan) - df_rescan_blocks (df, NULL); + int i; + + if (df->postorder) + free (df->postorder); + if (df->postorder_inverted) + free (df->postorder_inverted); + df->postorder = XNEWVEC (int, last_basic_block); + df->postorder_inverted = XNEWVEC (int, last_basic_block); + df->n_blocks = post_order_compute (df->postorder, true, true); + df->n_blocks_inverted = inverted_post_order_compute (df->postorder_inverted); + + /* These should be the same. */ + gcc_assert (df->n_blocks == df->n_blocks_inverted); + + /* We need to do this before the df_verify_all because this is + not kept incrementally up to date. */ + df_compute_regs_ever_live (false); + df_process_deferred_rescans (); + +#ifdef ENABLE_CHECKING + if (dump_file) + fprintf (dump_file, "df_analyze called\n"); + df_verify (); +#endif + + for (i = 0; i < df->n_blocks; i++) + bitmap_set_bit (current_all_blocks, df->postorder[i]); + +#ifdef ENABLE_CHECKING + /* Verify that POSTORDER_INVERTED only contains blocks reachable from + the ENTRY block. */ + for (i = 0; i < df->n_blocks_inverted; i++) + gcc_assert (bitmap_bit_p (current_all_blocks, df->postorder_inverted[i])); +#endif /* Make sure that we have pruned any unreachable blocks from these sets. */ - bitmap_and_into (df->blocks_to_scan, current_all_blocks); - - if (df->blocks_to_analyze) + if (df->analyze_subset) { everything = false; bitmap_and_into (df->blocks_to_analyze, current_all_blocks); - n_blocks = df_prune_to_subcfg (postorder, n_blocks, df->blocks_to_analyze); + df->n_blocks = df_prune_to_subcfg (df->postorder, + df->n_blocks, df->blocks_to_analyze); + df->n_blocks_inverted = df_prune_to_subcfg (df->postorder_inverted, + df->n_blocks_inverted, + df->blocks_to_analyze); BITMAP_FREE (current_all_blocks); } else @@ -823,10 +1138,22 @@ df_analyze (struct df *df) /* Skip over the DF_SCAN problem. */ for (i = 1; i < df->num_problems_defined; i++) - df_analyze_problem (df->problems_in_order[i], - df->blocks_to_analyze, df->blocks_to_analyze, - df->blocks_to_scan, - postorder, n_blocks, false); + { + struct dataflow *dflow = df->problems_in_order[i]; + if (dflow->solutions_dirty) + { + if (dflow->problem->dir == DF_FORWARD) + df_analyze_problem (dflow, + df->blocks_to_analyze, + df->postorder_inverted, + df->n_blocks_inverted); + else + df_analyze_problem (dflow, + df->blocks_to_analyze, + df->postorder, + df->n_blocks); + } + } if (everything) { @@ -834,12 +1161,78 @@ df_analyze (struct df *df) df->blocks_to_analyze = NULL; } - BITMAP_FREE (df->blocks_to_scan); - df->blocks_to_scan = NULL; - free (postorder); +#ifdef DF_DEBUG_CFG + df_set_clean_cfg (); +#endif +} + + +/* Return the number of basic blocks from the last call to df_analyze. */ + +int +df_get_n_blocks (enum df_flow_dir dir) +{ + gcc_assert (dir != DF_NONE); + + if (dir == DF_FORWARD) + { + gcc_assert (df->postorder_inverted); + return df->n_blocks_inverted; + } + + gcc_assert (df->postorder); + return df->n_blocks; +} + + +/* Return a pointer to the array of basic blocks in the reverse postorder. + Depending on the direction of the dataflow problem, + it returns either the usual reverse postorder array + or the reverse postorder of inverted traversal. */ +int * +df_get_postorder (enum df_flow_dir dir) +{ + gcc_assert (dir != DF_NONE); + + if (dir == DF_FORWARD) + { + gcc_assert (df->postorder_inverted); + return df->postorder_inverted; + } + gcc_assert (df->postorder); + return df->postorder; } +static struct df_problem user_problem; +static struct dataflow user_dflow; +/* Interface for calling iterative dataflow with user defined + confluence and transfer functions. All that is necessary is to + supply DIR, a direction, CONF_FUN_0, a confluence function for + blocks with no logical preds (or NULL), CONF_FUN_N, the normal + confluence function, TRANS_FUN, the basic block transfer function, + and BLOCKS, the set of blocks to examine, POSTORDER the blocks in + postorder, and N_BLOCKS, the number of blocks in POSTORDER. */ + +void +df_simple_dataflow (enum df_flow_dir dir, + df_init_function init_fun, + df_confluence_function_0 con_fun_0, + df_confluence_function_n con_fun_n, + df_transfer_function trans_fun, + bitmap blocks, int * postorder, int n_blocks) +{ + memset (&user_problem, 0, sizeof (struct df_problem)); + user_problem.dir = dir; + user_problem.init_fun = init_fun; + user_problem.con_fun_0 = con_fun_0; + user_problem.con_fun_n = con_fun_n; + user_problem.trans_fun = trans_fun; + user_dflow.problem = &user_problem; + df_worklist_dataflow (&user_dflow, blocks, postorder, n_blocks); +} + + /*---------------------------------------------------------------------------- Functions to support limited incremental change. @@ -851,6 +1244,10 @@ df_analyze (struct df *df) static void * df_get_bb_info (struct dataflow *dflow, unsigned int index) { + if (dflow->block_info == NULL) + return NULL; + if (index >= dflow->block_info_size) + return NULL; return (struct df_scan_bb_info *) dflow->block_info[index]; } @@ -861,25 +1258,108 @@ static void df_set_bb_info (struct dataflow *dflow, unsigned int index, void *bb_info) { + gcc_assert (dflow->block_info); dflow->block_info[index] = bb_info; } +/* Mark the solutions as being out of date. */ + +void +df_mark_solutions_dirty (void) +{ + if (df) + { + int p; + for (p = 1; p < df->num_problems_defined; p++) + df->problems_in_order[p]->solutions_dirty = true; + } +} + + +/* Return true if BB needs it's transfer functions recomputed. */ + +bool +df_get_bb_dirty (basic_block bb) +{ + if (df && df_live) + return bitmap_bit_p (df_live->out_of_date_transfer_functions, bb->index); + else + return false; +} + + +/* Mark BB as needing it's transfer functions as being out of + date. */ + +void +df_set_bb_dirty (basic_block bb) +{ + if (df) + { + int p; + for (p = 1; p < df->num_problems_defined; p++) + { + struct dataflow *dflow = df->problems_in_order[p]; + if (dflow->out_of_date_transfer_functions) + bitmap_set_bit (dflow->out_of_date_transfer_functions, bb->index); + } + df_mark_solutions_dirty (); + } +} + + +/* Clear the dirty bits. This is called from places that delete + blocks. */ +static void +df_clear_bb_dirty (basic_block bb) +{ + int p; + for (p = 1; p < df->num_problems_defined; p++) + { + struct dataflow *dflow = df->problems_in_order[p]; + if (dflow->out_of_date_transfer_functions) + bitmap_clear_bit (dflow->out_of_date_transfer_functions, bb->index); + } +} /* Called from the rtl_compact_blocks to reorganize the problems basic block info. */ void -df_compact_blocks (struct df *df) +df_compact_blocks (void) { int i, p; basic_block bb; void **problem_temps; - int size = last_basic_block *sizeof (void *); + int size = last_basic_block * sizeof (void *); + bitmap tmp = BITMAP_ALLOC (&df_bitmap_obstack); problem_temps = xmalloc (size); for (p = 0; p < df->num_problems_defined; p++) { struct dataflow *dflow = df->problems_in_order[p]; + + /* Need to reorganize the out_of_date_transfer_functions for the + dflow problem. */ + if (dflow->out_of_date_transfer_functions) + { + bitmap_copy (tmp, dflow->out_of_date_transfer_functions); + bitmap_clear (dflow->out_of_date_transfer_functions); + if (bitmap_bit_p (tmp, ENTRY_BLOCK)) + bitmap_set_bit (dflow->out_of_date_transfer_functions, ENTRY_BLOCK); + if (bitmap_bit_p (tmp, EXIT_BLOCK)) + bitmap_set_bit (dflow->out_of_date_transfer_functions, EXIT_BLOCK); + + i = NUM_FIXED_BLOCKS; + FOR_EACH_BB (bb) + { + if (bitmap_bit_p (tmp, bb->index)) + bitmap_set_bit (dflow->out_of_date_transfer_functions, i); + i++; + } + } + + /* Now shuffle the block info for the problem. */ if (dflow->problem->free_bb_fun) { df_grow_bb_info (dflow); @@ -887,7 +1367,7 @@ df_compact_blocks (struct df *df) /* Copy the bb info from the problem tmps to the proper place in the block_info vector. Null out the copied - item. */ + item. The entry and exit blocks never move. */ i = NUM_FIXED_BLOCKS; FOR_EACH_BB (bb) { @@ -905,11 +1385,32 @@ df_compact_blocks (struct df *df) basic_block bb = BASIC_BLOCK (i); if (problem_temps[i] && bb) dflow->problem->free_bb_fun - (dflow, bb, problem_temps[i]); + (bb, problem_temps[i]); } } } + /* Shuffle the bits in the basic_block indexed arrays. */ + + if (df->blocks_to_analyze) + { + if (bitmap_bit_p (tmp, ENTRY_BLOCK)) + bitmap_set_bit (df->blocks_to_analyze, ENTRY_BLOCK); + if (bitmap_bit_p (tmp, EXIT_BLOCK)) + bitmap_set_bit (df->blocks_to_analyze, EXIT_BLOCK); + bitmap_copy (tmp, df->blocks_to_analyze); + bitmap_clear (df->blocks_to_analyze); + i = NUM_FIXED_BLOCKS; + FOR_EACH_BB (bb) + { + if (bitmap_bit_p (tmp, bb->index)) + bitmap_set_bit (df->blocks_to_analyze, i); + i++; + } + } + + BITMAP_FREE (tmp); + free (problem_temps); i = NUM_FIXED_BLOCKS; @@ -924,39 +1425,175 @@ df_compact_blocks (struct df *df) for (; i < last_basic_block; i++) SET_BASIC_BLOCK (i, NULL); + +#ifdef DF_DEBUG_CFG + if (!df_lr->solutions_dirty) + df_set_clean_cfg (); +#endif } -/* Shove NEW_BLOCK in at OLD_INDEX. Called from if-cvt to hack a +/* Shove NEW_BLOCK in at OLD_INDEX. Called from ifcvt to hack a block. There is no excuse for people to do this kind of thing. */ void -df_bb_replace (struct df *df, int old_index, basic_block new_block) +df_bb_replace (int old_index, basic_block new_block) { + int new_block_index = new_block->index; int p; + if (dump_file) + fprintf (dump_file, "shoving block %d into %d\n", new_block_index, old_index); + + gcc_assert (df); + gcc_assert (BASIC_BLOCK (old_index) == NULL); + for (p = 0; p < df->num_problems_defined; p++) { struct dataflow *dflow = df->problems_in_order[p]; if (dflow->block_info) { - void *temp; - df_grow_bb_info (dflow); - - /* The old switcheroo. */ - - temp = df_get_bb_info (dflow, old_index); + gcc_assert (df_get_bb_info (dflow, old_index) == NULL); df_set_bb_info (dflow, old_index, - df_get_bb_info (dflow, new_block->index)); - df_set_bb_info (dflow, new_block->index, temp); + df_get_bb_info (dflow, new_block_index)); } } + df_clear_bb_dirty (new_block); SET_BASIC_BLOCK (old_index, new_block); new_block->index = old_index; + df_set_bb_dirty (BASIC_BLOCK (old_index)); + SET_BASIC_BLOCK (new_block_index, NULL); +} + + +/* Free all of the per basic block dataflow from all of the problems. + This is typically called before a basic block is deleted and the + problem will be reanalyzed. */ + +void +df_bb_delete (int bb_index) +{ + basic_block bb = BASIC_BLOCK (bb_index); + int i; + + if (!df) + return; + + for (i = 0; i < df->num_problems_defined; i++) + { + struct dataflow *dflow = df->problems_in_order[i]; + if (dflow->problem->free_bb_fun) + { + void *bb_info = df_get_bb_info (dflow, bb_index); + if (bb_info) + { + dflow->problem->free_bb_fun (bb, bb_info); + df_set_bb_info (dflow, bb_index, NULL); + } + } + } + df_clear_bb_dirty (bb); + df_mark_solutions_dirty (); +} + + +/* Verify that there is a place for everything and everything is in + its place. This is too expensive to run after every pass in the + mainline. However this is an excellent debugging tool if the + dataflow infomation is not being updated properly. You can just + sprinkle calls in until you find the place that is changing an + underlying structure without calling the proper updating + rountine. */ + +void +df_verify (void) +{ + df_scan_verify (); + df_lr_verify_transfer_functions (); + if (df_live) + df_live_verify_transfer_functions (); +} + +#ifdef DF_DEBUG_CFG + +/* Compute an array of ints that describes the cfg. This can be used + to discover places where the cfg is modified by the appropriate + calls have not been made to the keep df informed. The internals of + this are unexciting, the key is that two instances of this can be + compared to see if any changes have been made to the cfg. */ + +static int * +df_compute_cfg_image (void) +{ + basic_block bb; + int size = 2 + (2 * n_basic_blocks); + int i; + int * map; + + FOR_ALL_BB (bb) + { + size += EDGE_COUNT (bb->succs); + } + + map = XNEWVEC (int, size); + map[0] = size; + i = 1; + FOR_ALL_BB (bb) + { + edge_iterator ei; + edge e; + + map[i++] = bb->index; + FOR_EACH_EDGE (e, ei, bb->succs) + map[i++] = e->dest->index; + map[i++] = -1; + } + map[i] = -1; + return map; +} + +static int *saved_cfg = NULL; + + +/* This function compares the saved version of the cfg with the + current cfg and aborts if the two are identical. The function + silently returns if the cfg has been marked as dirty or the two are + the same. */ + +void +df_check_cfg_clean (void) +{ + int *new_map; + + if (!df) + return; + + if (df_lr->solutions_dirty) + return; + + if (saved_cfg == NULL) + return; + + new_map = df_compute_cfg_image (); + gcc_assert (memcmp (saved_cfg, new_map, saved_cfg[0] * sizeof (int)) == 0); + free (new_map); } + +/* This function builds a cfg fingerprint and squirrels it away in + saved_cfg. */ + +static void +df_set_clean_cfg (void) +{ + if (saved_cfg) + free (saved_cfg); + saved_cfg = df_compute_cfg_image (); +} + +#endif /* DF_DEBUG_CFG */ /*---------------------------------------------------------------------------- PUBLIC INTERFACES TO QUERY INFORMATION. ----------------------------------------------------------------------------*/ @@ -965,10 +1602,10 @@ df_bb_replace (struct df *df, int old_index, basic_block new_block) /* Return last use of REGNO within BB. */ struct df_ref * -df_bb_regno_last_use_find (struct df *df, basic_block bb, unsigned int regno) +df_bb_regno_last_use_find (basic_block bb, unsigned int regno) { rtx insn; - struct df_ref *use; + struct df_ref **use_rec; unsigned int uid; FOR_BB_INSNS_REVERSE (bb, insn) @@ -977,9 +1614,20 @@ df_bb_regno_last_use_find (struct df *df, basic_block bb, unsigned int regno) continue; uid = INSN_UID (insn); - for (use = DF_INSN_UID_GET (df, uid)->uses; use; use = use->next_ref) - if (DF_REF_REGNO (use) == regno) - return use; + for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if (DF_REF_REGNO (use) == regno) + return use; + } + + if (df->changeable_flags & DF_EQ_NOTES) + for (use_rec = DF_INSN_UID_EQ_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if (DF_REF_REGNO (use) == regno) + return use; + } } return NULL; } @@ -988,10 +1636,10 @@ df_bb_regno_last_use_find (struct df *df, basic_block bb, unsigned int regno) /* Return first def of REGNO within BB. */ struct df_ref * -df_bb_regno_first_def_find (struct df *df, basic_block bb, unsigned int regno) +df_bb_regno_first_def_find (basic_block bb, unsigned int regno) { rtx insn; - struct df_ref *def; + struct df_ref **def_rec; unsigned int uid; FOR_BB_INSNS (bb, insn) @@ -1000,9 +1648,12 @@ df_bb_regno_first_def_find (struct df *df, basic_block bb, unsigned int regno) continue; uid = INSN_UID (insn); - for (def = DF_INSN_UID_GET (df, uid)->defs; def; def = def->next_ref) - if (DF_REF_REGNO (def) == regno) - return def; + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if (DF_REF_REGNO (def) == regno) + return def; + } } return NULL; } @@ -1011,10 +1662,10 @@ df_bb_regno_first_def_find (struct df *df, basic_block bb, unsigned int regno) /* Return last def of REGNO within BB. */ struct df_ref * -df_bb_regno_last_def_find (struct df *df, basic_block bb, unsigned int regno) +df_bb_regno_last_def_find (basic_block bb, unsigned int regno) { rtx insn; - struct df_ref *def; + struct df_ref **def_rec; unsigned int uid; FOR_BB_INSNS_REVERSE (bb, insn) @@ -1023,9 +1674,12 @@ df_bb_regno_last_def_find (struct df *df, basic_block bb, unsigned int regno) continue; uid = INSN_UID (insn); - for (def = DF_INSN_UID_GET (df, uid)->defs; def; def = def->next_ref) - if (DF_REF_REGNO (def) == regno) - return def; + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if (DF_REF_REGNO (def) == regno) + return def; + } } return NULL; @@ -1034,15 +1688,18 @@ df_bb_regno_last_def_find (struct df *df, basic_block bb, unsigned int regno) /* Return true if INSN defines REGNO. */ bool -df_insn_regno_def_p (struct df *df, rtx insn, unsigned int regno) +df_insn_regno_def_p (rtx insn, unsigned int regno) { unsigned int uid; - struct df_ref *def; + struct df_ref **def_rec; uid = INSN_UID (insn); - for (def = DF_INSN_UID_GET (df, uid)->defs; def; def = def->next_ref) - if (DF_REF_REGNO (def) == regno) - return true; + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if (DF_REF_REGNO (def) == regno) + return true; + } return false; } @@ -1052,19 +1709,22 @@ df_insn_regno_def_p (struct df *df, rtx insn, unsigned int regno) DF is the dataflow object. */ struct df_ref * -df_find_def (struct df *df, rtx insn, rtx reg) +df_find_def (rtx insn, rtx reg) { unsigned int uid; - struct df_ref *def; + struct df_ref **def_rec; if (GET_CODE (reg) == SUBREG) reg = SUBREG_REG (reg); gcc_assert (REG_P (reg)); uid = INSN_UID (insn); - for (def = DF_INSN_UID_GET (df, uid)->defs; def; def = def->next_ref) - if (rtx_equal_p (DF_REF_REAL_REG (def), reg)) - return def; + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if (rtx_equal_p (DF_REF_REAL_REG (def), reg)) + return def; + } return NULL; } @@ -1073,9 +1733,9 @@ df_find_def (struct df *df, rtx insn, rtx reg) /* Return true if REG is defined in INSN, zero otherwise. */ bool -df_reg_defined (struct df *df, rtx insn, rtx reg) +df_reg_defined (rtx insn, rtx reg) { - return df_find_def (df, insn, reg) != NULL; + return df_find_def (insn, reg) != NULL; } @@ -1083,20 +1743,29 @@ df_reg_defined (struct df *df, rtx insn, rtx reg) DF is the dataflow object. */ struct df_ref * -df_find_use (struct df *df, rtx insn, rtx reg) +df_find_use (rtx insn, rtx reg) { unsigned int uid; - struct df_ref *use; + struct df_ref **use_rec; if (GET_CODE (reg) == SUBREG) reg = SUBREG_REG (reg); gcc_assert (REG_P (reg)); uid = INSN_UID (insn); - for (use = DF_INSN_UID_GET (df, uid)->uses; use; use = use->next_ref) - if (rtx_equal_p (DF_REF_REAL_REG (use), reg)) - return use; - + for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if (rtx_equal_p (DF_REF_REAL_REG (use), reg)) + return use; + } + if (df->changeable_flags & DF_EQ_NOTES) + for (use_rec = DF_INSN_UID_EQ_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if (rtx_equal_p (DF_REF_REAL_REG (use), reg)) + return use; + } return NULL; } @@ -1104,9 +1773,9 @@ df_find_use (struct df *df, rtx insn, rtx reg) /* Return true if REG is referenced in INSN, zero otherwise. */ bool -df_reg_used (struct df *df, rtx insn, rtx reg) +df_reg_used (rtx insn, rtx reg) { - return df_find_use (df, insn, reg) != NULL; + return df_find_use (insn, reg) != NULL; } @@ -1114,9 +1783,53 @@ df_reg_used (struct df *df, rtx insn, rtx reg) Debugging and printing functions. ----------------------------------------------------------------------------*/ + +/* Write information about registers and basic blocks into FILE. + This is part of making a debugging dump. */ + +void +df_print_regset (FILE *file, bitmap r) +{ + unsigned int i; + bitmap_iterator bi; + + if (r == NULL) + fputs (" (nil)", file); + else + { + EXECUTE_IF_SET_IN_BITMAP (r, 0, i, bi) + { + fprintf (file, " %d", i); + if (i < FIRST_PSEUDO_REGISTER) + fprintf (file, " [%s]", reg_names[i]); + } + } + fprintf (file, "\n"); +} + + /* Dump dataflow info. */ void -df_dump (struct df *df, FILE *file) +df_dump (FILE *file) +{ + basic_block bb; + df_dump_start (file); + + FOR_ALL_BB (bb) + { + df_print_bb_index (bb, file); + df_dump_top (bb, file); + df_dump_bottom (bb, file); + } + + fprintf (file, "\n"); +} + + +/* Dump the introductory information for each problem defined. */ + +void +df_dump_start (FILE *file) { int i; @@ -1125,29 +1838,83 @@ df_dump (struct df *df, FILE *file) fprintf (file, "\n\n%s\n", current_function_name ()); fprintf (file, "\nDataflow summary:\n"); - fprintf (file, "def_info->bitmap_size = %d, use_info->bitmap_size = %d\n", - df->def_info.bitmap_size, df->use_info.bitmap_size); + if (df->blocks_to_analyze) + fprintf (file, "def_info->table_size = %d, use_info->table_size = %d\n", + DF_DEFS_TABLE_SIZE (), DF_USES_TABLE_SIZE ()); for (i = 0; i < df->num_problems_defined; i++) - df->problems_in_order[i]->problem->dump_fun (df->problems_in_order[i], file); + { + struct dataflow *dflow = df->problems_in_order[i]; + if (dflow->computed) + { + df_dump_problem_function fun = dflow->problem->dump_start_fun; + if (fun) + fun(file); + } + } +} - fprintf (file, "\n"); + +/* Dump the top of the block information for BB. */ + +void +df_dump_top (basic_block bb, FILE *file) +{ + int i; + + if (!df || !file) + return; + + for (i = 0; i < df->num_problems_defined; i++) + { + struct dataflow *dflow = df->problems_in_order[i]; + if (dflow->computed) + { + df_dump_bb_problem_function bbfun = dflow->problem->dump_top_fun; + if (bbfun) + bbfun (bb, file); + } + } +} + + +/* Dump the bottom of the block information for BB. */ + +void +df_dump_bottom (basic_block bb, FILE *file) +{ + int i; + + if (!df || !file) + return; + + for (i = 0; i < df->num_problems_defined; i++) + { + struct dataflow *dflow = df->problems_in_order[i]; + if (dflow->computed) + { + df_dump_bb_problem_function bbfun = dflow->problem->dump_bottom_fun; + if (bbfun) + bbfun (bb, file); + } + } } void -df_refs_chain_dump (struct df_ref *ref, bool follow_chain, FILE *file) +df_refs_chain_dump (struct df_ref **ref_rec, bool follow_chain, FILE *file) { fprintf (file, "{ "); - while (ref) + while (*ref_rec) { - fprintf (file, "%c%d(%d) ", - DF_REF_REG_DEF_P (ref) ? 'd' : 'u', + struct df_ref *ref = *ref_rec; + fprintf (file, "%c%d(%d)", + DF_REF_REG_DEF_P (ref) ? 'd' : (DF_REF_FLAGS (ref) & DF_REF_IN_NOTE) ? 'e' : 'u', DF_REF_ID (ref), DF_REF_REGNO (ref)); if (follow_chain) df_chain_dump (DF_REF_CHAIN (ref), file); - ref = ref->next_ref; + ref_rec++; } fprintf (file, "}"); } @@ -1156,7 +1923,7 @@ df_refs_chain_dump (struct df_ref *ref, bool follow_chain, FILE *file) /* Dump either a ref-def or reg-use chain. */ void -df_regs_chain_dump (struct df *df ATTRIBUTE_UNUSED, struct df_ref *ref, FILE *file) +df_regs_chain_dump (struct df_ref *ref, FILE *file) { fprintf (file, "{ "); while (ref) @@ -1172,99 +1939,84 @@ df_regs_chain_dump (struct df *df ATTRIBUTE_UNUSED, struct df_ref *ref, FILE *f static void -df_mws_dump (struct df_mw_hardreg *mws, FILE *file) +df_mws_dump (struct df_mw_hardreg **mws, FILE *file) { - while (mws) + while (*mws) { - struct df_link *regs = mws->regs; - fprintf (file, "%c%d(", - (mws->type == DF_REF_REG_DEF) ? 'd' : 'u', - DF_REF_REGNO (regs->ref)); - while (regs) - { - fprintf (file, "%d ", DF_REF_REGNO (regs->ref)); - regs = regs->next; - } - - fprintf (file, ") "); - mws = mws->next; + fprintf (file, "mw %c r[%d..%d]\n", + ((*mws)->type == DF_REF_REG_DEF) ? 'd' : 'u', + (*mws)->start_regno, (*mws)->end_regno); + mws++; } } static void -df_insn_uid_debug (struct df *df, unsigned int uid, +df_insn_uid_debug (unsigned int uid, bool follow_chain, FILE *file) { - int bbi; - - if (DF_INSN_UID_DEFS (df, uid)) - bbi = DF_REF_BBNO (DF_INSN_UID_DEFS (df, uid)); - else if (DF_INSN_UID_USES(df, uid)) - bbi = DF_REF_BBNO (DF_INSN_UID_USES (df, uid)); - else - bbi = -1; + fprintf (file, "insn %d luid %d", + uid, DF_INSN_UID_LUID (uid)); - fprintf (file, "insn %d bb %d luid %d", - uid, bbi, DF_INSN_UID_LUID (df, uid)); - - if (DF_INSN_UID_DEFS (df, uid)) + if (DF_INSN_UID_DEFS (uid)) { fprintf (file, " defs "); - df_refs_chain_dump (DF_INSN_UID_DEFS (df, uid), follow_chain, file); + df_refs_chain_dump (DF_INSN_UID_DEFS (uid), follow_chain, file); } - if (DF_INSN_UID_USES (df, uid)) + if (DF_INSN_UID_USES (uid)) { fprintf (file, " uses "); - df_refs_chain_dump (DF_INSN_UID_USES (df, uid), follow_chain, file); + df_refs_chain_dump (DF_INSN_UID_USES (uid), follow_chain, file); + } + + if (DF_INSN_UID_EQ_USES (uid)) + { + fprintf (file, " eq uses "); + df_refs_chain_dump (DF_INSN_UID_EQ_USES (uid), follow_chain, file); } - if (DF_INSN_UID_MWS (df, uid)) + if (DF_INSN_UID_MWS (uid)) { fprintf (file, " mws "); - df_mws_dump (DF_INSN_UID_MWS (df, uid), file); + df_mws_dump (DF_INSN_UID_MWS (uid), file); } fprintf (file, "\n"); } void -df_insn_debug (struct df *df, rtx insn, bool follow_chain, FILE *file) +df_insn_debug (rtx insn, bool follow_chain, FILE *file) { - df_insn_uid_debug (df, INSN_UID (insn), follow_chain, file); + df_insn_uid_debug (INSN_UID (insn), follow_chain, file); } void -df_insn_debug_regno (struct df *df, rtx insn, FILE *file) +df_insn_debug_regno (rtx insn, FILE *file) { - unsigned int uid; - int bbi; - - uid = INSN_UID (insn); - if (DF_INSN_UID_DEFS (df, uid)) - bbi = DF_REF_BBNO (DF_INSN_UID_DEFS (df, uid)); - else if (DF_INSN_UID_USES(df, uid)) - bbi = DF_REF_BBNO (DF_INSN_UID_USES (df, uid)); - else - bbi = -1; + unsigned int uid = INSN_UID(insn); fprintf (file, "insn %d bb %d luid %d defs ", - uid, bbi, DF_INSN_LUID (df, insn)); - df_regs_chain_dump (df, DF_INSN_UID_DEFS (df, uid), file); + uid, BLOCK_FOR_INSN (insn)->index, DF_INSN_LUID (insn)); + df_refs_chain_dump (DF_INSN_UID_DEFS (uid), false, file); fprintf (file, " uses "); - df_regs_chain_dump (df, DF_INSN_UID_USES (df, uid), file); + df_refs_chain_dump (DF_INSN_UID_USES (uid), false, file); + + fprintf (file, " eq_uses "); + df_refs_chain_dump (DF_INSN_UID_EQ_USES (uid), false, file); fprintf (file, "\n"); } void -df_regno_debug (struct df *df, unsigned int regno, FILE *file) +df_regno_debug (unsigned int regno, FILE *file) { fprintf (file, "reg %d defs ", regno); - df_regs_chain_dump (df, DF_REG_DEF_GET (df, regno)->reg_chain, file); + df_regs_chain_dump (DF_REG_DEF_CHAIN (regno), file); fprintf (file, " uses "); - df_regs_chain_dump (df, DF_REG_USE_GET (df, regno)->reg_chain, file); + df_regs_chain_dump (DF_REG_USE_CHAIN (regno), file); + fprintf (file, " eq_uses "); + df_regs_chain_dump (DF_REG_EQ_USE_CHAIN (regno), file); fprintf (file, "\n"); } @@ -1275,11 +2027,16 @@ df_ref_debug (struct df_ref *ref, FILE *file) fprintf (file, "%c%d ", DF_REF_REG_DEF_P (ref) ? 'd' : 'u', DF_REF_ID (ref)); - fprintf (file, "reg %d bb %d insn %d flag %x chain ", + fprintf (file, "reg %d bb %d insn %d flag 0x%x type 0x%x ", DF_REF_REGNO (ref), DF_REF_BBNO (ref), DF_REF_INSN (ref) ? INSN_UID (DF_REF_INSN (ref)) : -1, - DF_REF_FLAGS (ref)); + DF_REF_FLAGS (ref), + DF_REF_TYPE (ref)); + if (DF_REF_LOC (ref)) + fprintf (file, "loc %p(%p) chain ", (void *)DF_REF_LOC (ref), (void *)*DF_REF_LOC (ref)); + else + fprintf (file, "chain "); df_chain_dump (DF_REF_CHAIN (ref), file); fprintf (file, "\n"); } @@ -1289,7 +2046,7 @@ df_ref_debug (struct df_ref *ref, FILE *file) void debug_df_insn (rtx insn) { - df_insn_debug (ddf, insn, true, stderr); + df_insn_debug (insn, true, stderr); debug_rtx (insn); } @@ -1297,14 +2054,14 @@ debug_df_insn (rtx insn) void debug_df_reg (rtx reg) { - df_regno_debug (ddf, REGNO (reg), stderr); + df_regno_debug (REGNO (reg), stderr); } void debug_df_regno (unsigned int regno) { - df_regno_debug (ddf, regno, stderr); + df_regno_debug (regno, stderr); } @@ -1318,14 +2075,14 @@ debug_df_ref (struct df_ref *ref) void debug_df_defno (unsigned int defno) { - df_ref_debug (DF_DEFS_GET (ddf, defno), stderr); + df_ref_debug (DF_DEFS_GET (defno), stderr); } void debug_df_useno (unsigned int defno) { - df_ref_debug (DF_USES_GET (ddf, defno), stderr); + df_ref_debug (DF_USES_GET (defno), stderr); } diff --git a/gcc/df-problems.c b/gcc/df-problems.c index 016ea46..67a8cb0 100644 --- a/gcc/df-problems.c +++ b/gcc/df-problems.c @@ -1,5 +1,5 @@ /* Standard problems for dataflow support routines. - Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Originally contributed by Michael P. Hayes (m.hayes@elec.canterbury.ac.nz, mhayes@redhat.com) @@ -42,8 +42,9 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "bitmap.h" #include "timevar.h" #include "df.h" -#include "vecprim.h" #include "except.h" +#include "dce.h" +#include "vecprim.h" #if 0 #define REG_DEAD_DEBUGGING @@ -53,123 +54,61 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA static bitmap seen_in_block = NULL; static bitmap seen_in_insn = NULL; -static void df_ri_dump (struct dataflow *, FILE *); /*---------------------------------------------------------------------------- Public functions access functions for the dataflow problems. ----------------------------------------------------------------------------*/ +/* Get the live at out set for BB no matter what problem happens to be + defined. This function is used by the register allocators who + choose different dataflow problems depending on the optimization + level. */ -/* Create a du or ud chain from SRC to DST and link it into SRC. */ - -struct df_link * -df_chain_create (struct dataflow *dflow, struct df_ref *src, struct df_ref *dst) -{ - struct df_link *head = DF_REF_CHAIN (src); - struct df_link *link = pool_alloc (dflow->block_pool);; - - DF_REF_CHAIN (src) = link; - link->next = head; - link->ref = dst; - return link; -} - - -/* Delete a du or ud chain for REF. If LINK is NULL, delete all - chains for ref and check to see if the reverse chains can also be - deleted. If LINK is not NULL it must be a link off of ref. In - this case, the other end is not deleted. */ - -void -df_chain_unlink (struct dataflow *dflow, struct df_ref *ref, struct df_link *link) +bitmap +df_get_live_out (basic_block bb) { - struct df_link *chain = DF_REF_CHAIN (ref); - if (link) - { - /* Link was the first element in the chain. */ - if (chain == link) - DF_REF_CHAIN (ref) = link->next; - else - { - /* Link is an internal element in the chain. */ - struct df_link *prev = chain; - while (chain) - { - if (chain == link) - { - prev->next = chain->next; - break; - } - prev = chain; - chain = chain->next; - } - } - pool_free (dflow->block_pool, link); - } - else - { - /* If chain is NULL here, it was because of a recursive call - when the other flavor of chains was not built. Just run thru - the entire chain calling the other side and then deleting the - link. */ - while (chain) - { - struct df_link *next = chain->next; - /* Delete the other side if it exists. */ - df_chain_unlink (dflow, chain->ref, chain); - chain = next; - } - } -} - - -/* Copy the du or ud chain starting at FROM_REF and attach it to - TO_REF. */ + gcc_assert (df_lr); -void -df_chain_copy (struct dataflow *dflow, - struct df_ref *to_ref, - struct df_link *from_ref) -{ - while (from_ref) - { - df_chain_create (dflow, to_ref, from_ref->ref); - from_ref = from_ref->next; - } + if (df_urec) + return DF_RA_LIVE_OUT (bb); + else if (df_live) + return DF_LIVE_OUT (bb); + else + return DF_LR_OUT (bb); } - -/* Get the live in set for BB no matter what problem happens to be - defined. */ +/* Get the live at in set for BB no matter what problem happens to be + defined. This function is used by the register allocators who + choose different dataflow problems depending on the optimization + level. */ bitmap -df_get_live_in (struct df *df, basic_block bb) +df_get_live_in (basic_block bb) { - gcc_assert (df->problems_by_index[DF_LR]); + gcc_assert (df_lr); - if (df->problems_by_index[DF_UREC]) - return DF_RA_LIVE_IN (df, bb); - else if (df->problems_by_index[DF_UR]) - return DF_LIVE_IN (df, bb); + if (df_urec) + return DF_RA_LIVE_IN (bb); + else if (df_live) + return DF_LIVE_IN (bb); else - return DF_UPWARD_LIVE_IN (df, bb); + return DF_LR_IN (bb); } - -/* Get the live out set for BB no matter what problem happens to be - defined. */ +/* Get the live at top set for BB no matter what problem happens to be + defined. This function is used by the register allocators who + choose different dataflow problems depending on the optimization + level. */ bitmap -df_get_live_out (struct df *df, basic_block bb) +df_get_live_top (basic_block bb) { - gcc_assert (df->problems_by_index[DF_LR]); + gcc_assert (df_lr); - if (df->problems_by_index[DF_UREC]) - return DF_RA_LIVE_OUT (df, bb); - else if (df->problems_by_index[DF_UR]) - return DF_LIVE_OUT (df, bb); + if (df_urec) + return DF_RA_LIVE_TOP (bb); else - return DF_UPWARD_LIVE_OUT (df, bb); + return DF_LR_TOP (bb); } @@ -223,42 +162,22 @@ df_print_bb_index (basic_block bb, FILE *file) edge e; edge_iterator ei; - fprintf (file, "( "); + fprintf (file, "\n( "); FOR_EACH_EDGE (e, ei, bb->preds) { basic_block pred = e->src; - fprintf (file, "%d ", pred->index); + fprintf (file, "%d%s ", pred->index, e->flags & EDGE_EH ? "(EH)" : ""); } fprintf (file, ")->[%d]->( ", bb->index); FOR_EACH_EDGE (e, ei, bb->succs) { basic_block succ = e->dest; - fprintf (file, "%d ", succ->index); + fprintf (file, "%d%s ", succ->index, e->flags & EDGE_EH ? "(EH)" : ""); } fprintf (file, ")\n"); } -/* Return a bitmap for REGNO from the cache MAPS. The bitmap is to - contain COUNT bits starting at START. These bitmaps are not to be - changed since there is a cache of them. */ - -static inline bitmap -df_ref_bitmap (bitmap *maps, unsigned int regno, int start, int count) -{ - bitmap ids = maps[regno]; - if (!ids) - { - unsigned int i; - unsigned int end = start + count;; - ids = BITMAP_ALLOC (NULL); - maps[regno] = ids; - for (i = start; i < end; i++) - bitmap_set_bit (ids, i); - } - return ids; -} - /* Make sure that the seen_in_insn and seen_in_block sbitmaps are set up correctly. */ @@ -266,8 +185,8 @@ df_ref_bitmap (bitmap *maps, unsigned int regno, int start, int count) static void df_set_seen (void) { - seen_in_block = BITMAP_ALLOC (NULL); - seen_in_insn = BITMAP_ALLOC (NULL); + seen_in_block = BITMAP_ALLOC (&df_bitmap_obstack); + seen_in_insn = BITMAP_ALLOC (&df_bitmap_obstack); } @@ -300,9 +219,7 @@ df_unset_seen (void) 2) There are two kill sets, one if the number of uses is less or equal to DF_SPARSE_THRESHOLD and another if it is greater. - <= : There is a bitmap for each register, uses_sites[N], that is - built on demand. This bitvector contains a 1 for each use or reg - N. + <= : Data is built directly in the kill set. > : One level of indirection is used to keep from generating long strings of 1 bits in the kill sets. Bitvectors that are indexed @@ -312,45 +229,35 @@ df_unset_seen (void) bits without actually generating a knockout vector. The kill and sparse_kill and the dense_invalidated_by_call and - sparse_invalidated_by call both play this game. */ + sparse_invalidated_by_call both play this game. */ /* Private data used to compute the solution for this problem. These data structures are not accessible outside of this module. */ struct df_ru_problem_data { - - bitmap *use_sites; /* Bitmap of uses for each pseudo. */ - unsigned int use_sites_size; /* Size of use_sites. */ /* The set of defs to regs invalidated by call. */ bitmap sparse_invalidated_by_call; /* The set of defs to regs invalidated by call for ru. */ - bitmap dense_invalidated_by_call; + bitmap dense_invalidated_by_call; + /* An obstack for the bitmaps we need for this problem. */ + bitmap_obstack ru_bitmaps; }; -/* Get basic block info. */ - -struct df_ru_bb_info * -df_ru_get_bb_info (struct dataflow *dflow, unsigned int index) -{ - return (struct df_ru_bb_info *) dflow->block_info[index]; -} - - /* Set basic block info. */ static void -df_ru_set_bb_info (struct dataflow *dflow, unsigned int index, - struct df_ru_bb_info *bb_info) +df_ru_set_bb_info (unsigned int index, struct df_ru_bb_info *bb_info) { - dflow->block_info[index] = bb_info; + gcc_assert (df_ru); + gcc_assert (index < df_ru->block_info_size); + df_ru->block_info[index] = bb_info; } /* Free basic block info. */ static void -df_ru_free_bb_info (struct dataflow *dflow, - basic_block bb ATTRIBUTE_UNUSED, +df_ru_free_bb_info (basic_block bb ATTRIBUTE_UNUSED, void *vbb_info) { struct df_ru_bb_info *bb_info = (struct df_ru_bb_info *) vbb_info; @@ -361,67 +268,44 @@ df_ru_free_bb_info (struct dataflow *dflow, BITMAP_FREE (bb_info->gen); BITMAP_FREE (bb_info->in); BITMAP_FREE (bb_info->out); - pool_free (dflow->block_pool, bb_info); + pool_free (df_ru->block_pool, bb_info); } } -/* Allocate or reset bitmaps for DFLOW blocks. The solution bits are +/* Allocate or reset bitmaps for DF_RU blocks. The solution bits are not touched unless the block is new. */ static void -df_ru_alloc (struct dataflow *dflow, - bitmap blocks_to_rescan ATTRIBUTE_UNUSED, - bitmap all_blocks) +df_ru_alloc (bitmap all_blocks) { unsigned int bb_index; bitmap_iterator bi; - unsigned int reg_size = max_reg_num (); + struct df_ru_problem_data *problem_data; - if (!dflow->block_pool) - dflow->block_pool = create_alloc_pool ("df_ru_block pool", + if (!df_ru->block_pool) + df_ru->block_pool = create_alloc_pool ("df_ru_block pool", sizeof (struct df_ru_bb_info), 50); - if (dflow->problem_data) + if (df_ru->problem_data) { - unsigned int i; - struct df_ru_problem_data *problem_data - = (struct df_ru_problem_data *) dflow->problem_data; - - for (i = 0; i < problem_data->use_sites_size; i++) - { - bitmap bm = problem_data->use_sites[i]; - if (bm) - { - BITMAP_FREE (bm); - problem_data->use_sites[i] = NULL; - } - } - - if (problem_data->use_sites_size < reg_size) - { - problem_data->use_sites - = xrealloc (problem_data->use_sites, reg_size * sizeof (bitmap)); - memset (problem_data->use_sites + problem_data->use_sites_size, 0, - (reg_size - problem_data->use_sites_size) * sizeof (bitmap)); - problem_data->use_sites_size = reg_size; - } - + problem_data = (struct df_ru_problem_data *) df_ru->problem_data; bitmap_clear (problem_data->sparse_invalidated_by_call); bitmap_clear (problem_data->dense_invalidated_by_call); } else { - struct df_ru_problem_data *problem_data = XNEW (struct df_ru_problem_data); - dflow->problem_data = problem_data; - - problem_data->use_sites = XCNEWVEC (bitmap, reg_size); - problem_data->use_sites_size = reg_size; - problem_data->sparse_invalidated_by_call = BITMAP_ALLOC (NULL); - problem_data->dense_invalidated_by_call = BITMAP_ALLOC (NULL); + problem_data = XNEW (struct df_ru_problem_data); + df_ru->problem_data = problem_data; + + bitmap_obstack_initialize (&problem_data->ru_bitmaps); + problem_data->sparse_invalidated_by_call + = BITMAP_ALLOC (&problem_data->ru_bitmaps); + problem_data->dense_invalidated_by_call + = BITMAP_ALLOC (&problem_data->ru_bitmaps); } - df_grow_bb_info (dflow); + df_grow_bb_info (df_ru); /* Because of the clustering of all def sites for the same pseudo, we have to process all of the blocks before doing the @@ -429,7 +313,7 @@ df_ru_alloc (struct dataflow *dflow, EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) { - struct df_ru_bb_info *bb_info = df_ru_get_bb_info (dflow, bb_index); + struct df_ru_bb_info *bb_info = df_ru_get_bb_info (bb_index); if (bb_info) { bitmap_clear (bb_info->kill); @@ -438,13 +322,13 @@ df_ru_alloc (struct dataflow *dflow, } else { - bb_info = (struct df_ru_bb_info *) pool_alloc (dflow->block_pool); - df_ru_set_bb_info (dflow, bb_index, bb_info); - bb_info->kill = BITMAP_ALLOC (NULL); - bb_info->sparse_kill = BITMAP_ALLOC (NULL); - bb_info->gen = BITMAP_ALLOC (NULL); - bb_info->in = BITMAP_ALLOC (NULL); - bb_info->out = BITMAP_ALLOC (NULL); + bb_info = (struct df_ru_bb_info *) pool_alloc (df_ru->block_pool); + df_ru_set_bb_info (bb_index, bb_info); + bb_info->kill = BITMAP_ALLOC (&problem_data->ru_bitmaps); + bb_info->sparse_kill = BITMAP_ALLOC (&problem_data->ru_bitmaps); + bb_info->gen = BITMAP_ALLOC (&problem_data->ru_bitmaps); + bb_info->in = BITMAP_ALLOC (&problem_data->ru_bitmaps); + bb_info->out = BITMAP_ALLOC (&problem_data->ru_bitmaps); } } } @@ -453,22 +337,22 @@ df_ru_alloc (struct dataflow *dflow, /* Process a list of DEFs for df_ru_bb_local_compute. */ static void -df_ru_bb_local_compute_process_def (struct dataflow *dflow, - struct df_ru_bb_info *bb_info, - struct df_ref *def, +df_ru_bb_local_compute_process_def (struct df_ru_bb_info *bb_info, + struct df_ref **def_rec, enum df_ref_flags top_flag) { - struct df *df = dflow->df; - while (def) + while (*def_rec) { + struct df_ref *def = *def_rec; if ((top_flag == (DF_REF_FLAGS (def) & DF_REF_AT_TOP)) /* If the def is to only part of the reg, it is as if it did not happen, since some of the bits may get thru. */ - && (!(DF_REF_FLAGS (def) & DF_REF_PARTIAL))) + && (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL)))) { unsigned int regno = DF_REF_REGNO (def); - unsigned int begin = DF_REG_USE_GET (df, regno)->begin; - unsigned int n_uses = DF_REG_USE_GET (df, regno)->n_refs; + unsigned int begin = DF_USES_BEGIN (regno); + unsigned int n_uses = DF_USES_COUNT (regno); + if (!bitmap_bit_p (seen_in_block, regno)) { /* The first def for regno in the insn, causes the kill @@ -481,19 +365,12 @@ df_ru_bb_local_compute_process_def (struct dataflow *dflow, if (n_uses > DF_SPARSE_THRESHOLD) bitmap_set_bit (bb_info->sparse_kill, regno); else - { - struct df_ru_problem_data * problem_data - = (struct df_ru_problem_data *)dflow->problem_data; - bitmap uses - = df_ref_bitmap (problem_data->use_sites, regno, - begin, n_uses); - bitmap_ior_into (bb_info->kill, uses); - } + bitmap_set_range (bb_info->kill, begin, n_uses); } bitmap_set_bit (seen_in_insn, regno); } } - def = def->next_ref; + def_rec++; } } @@ -502,11 +379,12 @@ df_ru_bb_local_compute_process_def (struct dataflow *dflow, static void df_ru_bb_local_compute_process_use (struct df_ru_bb_info *bb_info, - struct df_ref *use, + struct df_ref **use_rec, enum df_ref_flags top_flag) { - while (use) + while (*use_rec) { + struct df_ref *use = *use_rec; if (top_flag == (DF_REF_FLAGS (use) & DF_REF_AT_TOP)) { /* Add use to set of gens in this BB unless we have seen a @@ -515,18 +393,17 @@ df_ru_bb_local_compute_process_use (struct df_ru_bb_info *bb_info, if (!bitmap_bit_p (seen_in_block, regno)) bitmap_set_bit (bb_info->gen, DF_REF_ID (use)); } - use = use->next_ref; + use_rec++; } } /* Compute local reaching use (upward exposed use) info for basic block BB. USE_INFO->REGS[R] caches the set of uses for register R. */ static void -df_ru_bb_local_compute (struct dataflow *dflow, unsigned int bb_index) +df_ru_bb_local_compute (unsigned int bb_index) { - struct df *df = dflow->df; basic_block bb = BASIC_BLOCK (bb_index); - struct df_ru_bb_info *bb_info = df_ru_get_bb_info (dflow, bb_index); + struct df_ru_bb_info *bb_info = df_ru_get_bb_info (bb_index); rtx insn; /* Set when a def for regno is seen. */ @@ -537,11 +414,11 @@ df_ru_bb_local_compute (struct dataflow *dflow, unsigned int bb_index) /* Variables defined in the prolog that are used by the exception handler. */ df_ru_bb_local_compute_process_use (bb_info, - df_get_artificial_uses (df, bb_index), + df_get_artificial_uses (bb_index), DF_REF_AT_TOP); #endif - df_ru_bb_local_compute_process_def (dflow, bb_info, - df_get_artificial_defs (df, bb_index), + df_ru_bb_local_compute_process_def (bb_info, + df_get_artificial_defs (bb_index), DF_REF_AT_TOP); FOR_BB_INSNS (bb, insn) @@ -551,10 +428,14 @@ df_ru_bb_local_compute (struct dataflow *dflow, unsigned int bb_index) continue; df_ru_bb_local_compute_process_use (bb_info, - DF_INSN_UID_USES (df, uid), 0); + DF_INSN_UID_USES (uid), 0); + + if (df->changeable_flags & DF_EQ_NOTES) + df_ru_bb_local_compute_process_use (bb_info, + DF_INSN_UID_EQ_USES (uid), 0); - df_ru_bb_local_compute_process_def (dflow, bb_info, - DF_INSN_UID_DEFS (df, uid), 0); + df_ru_bb_local_compute_process_def (bb_info, + DF_INSN_UID_DEFS (uid), 0); bitmap_ior_into (seen_in_block, seen_in_insn); bitmap_clear (seen_in_insn); @@ -562,49 +443,45 @@ df_ru_bb_local_compute (struct dataflow *dflow, unsigned int bb_index) /* Process the hardware registers that are always live. */ df_ru_bb_local_compute_process_use (bb_info, - df_get_artificial_uses (df, bb_index), 0); + df_get_artificial_uses (bb_index), 0); - df_ru_bb_local_compute_process_def (dflow, bb_info, - df_get_artificial_defs (df, bb_index), 0); + df_ru_bb_local_compute_process_def (bb_info, + df_get_artificial_defs (bb_index), 0); } /* Compute local reaching use (upward exposed use) info for each basic block within BLOCKS. */ static void -df_ru_local_compute (struct dataflow *dflow, - bitmap all_blocks, - bitmap rescan_blocks ATTRIBUTE_UNUSED) +df_ru_local_compute (bitmap all_blocks) { - struct df *df = dflow->df; unsigned int bb_index; bitmap_iterator bi; unsigned int regno; struct df_ru_problem_data *problem_data - = (struct df_ru_problem_data *) dflow->problem_data; + = (struct df_ru_problem_data *) df_ru->problem_data; bitmap sparse_invalidated = problem_data->sparse_invalidated_by_call; bitmap dense_invalidated = problem_data->dense_invalidated_by_call; df_set_seen (); - df_reorganize_refs (&df->use_info); + + df_maybe_reorganize_use_refs (df->changeable_flags & DF_EQ_NOTES ? + DF_REF_ORDER_BY_REG_WITH_NOTES : DF_REF_ORDER_BY_REG); EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) { - df_ru_bb_local_compute (dflow, bb_index); + df_ru_bb_local_compute (bb_index); } /* Set up the knockout bit vectors to be applied across EH_EDGES. */ EXECUTE_IF_SET_IN_BITMAP (df_invalidated_by_call, 0, regno, bi) { - struct df_reg_info *reg_info = DF_REG_USE_GET (df, regno); - if (reg_info->n_refs > DF_SPARSE_THRESHOLD) + if (DF_USES_COUNT (regno) > DF_SPARSE_THRESHOLD) bitmap_set_bit (sparse_invalidated, regno); else - { - bitmap defs = df_ref_bitmap (problem_data->use_sites, regno, - reg_info->begin, reg_info->n_refs); - bitmap_ior_into (dense_invalidated, defs); - } + bitmap_set_range (dense_invalidated, + DF_USES_BEGIN (regno), + DF_USES_COUNT (regno)); } df_unset_seen (); @@ -614,14 +491,14 @@ df_ru_local_compute (struct dataflow *dflow, /* Initialize the solution bit vectors for problem. */ static void -df_ru_init_solution (struct dataflow *dflow, bitmap all_blocks) +df_ru_init_solution (bitmap all_blocks) { unsigned int bb_index; bitmap_iterator bi; EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) { - struct df_ru_bb_info *bb_info = df_ru_get_bb_info (dflow, bb_index); + struct df_ru_bb_info *bb_info = df_ru_get_bb_info (bb_index); bitmap_copy (bb_info->in, bb_info->gen); bitmap_clear (bb_info->out); } @@ -631,21 +508,20 @@ df_ru_init_solution (struct dataflow *dflow, bitmap all_blocks) /* Out of target gets or of in of source. */ static void -df_ru_confluence_n (struct dataflow *dflow, edge e) +df_ru_confluence_n (edge e) { - bitmap op1 = df_ru_get_bb_info (dflow, e->src->index)->out; - bitmap op2 = df_ru_get_bb_info (dflow, e->dest->index)->in; + bitmap op1 = df_ru_get_bb_info (e->src->index)->out; + bitmap op2 = df_ru_get_bb_info (e->dest->index)->in; if (e->flags & EDGE_EH) { struct df_ru_problem_data *problem_data - = (struct df_ru_problem_data *) dflow->problem_data; + = (struct df_ru_problem_data *) df_ru->problem_data; bitmap sparse_invalidated = problem_data->sparse_invalidated_by_call; bitmap dense_invalidated = problem_data->dense_invalidated_by_call; - struct df *df = dflow->df; bitmap_iterator bi; unsigned int regno; - bitmap tmp = BITMAP_ALLOC (NULL); + bitmap tmp = BITMAP_ALLOC (&df_bitmap_obstack); bitmap_copy (tmp, op2); bitmap_and_compl_into (tmp, dense_invalidated); @@ -653,8 +529,8 @@ df_ru_confluence_n (struct dataflow *dflow, edge e) EXECUTE_IF_SET_IN_BITMAP (sparse_invalidated, 0, regno, bi) { bitmap_clear_range (tmp, - DF_REG_USE_GET (df, regno)->begin, - DF_REG_USE_GET (df, regno)->n_refs); + DF_USES_BEGIN (regno), + DF_USES_COUNT (regno)); } bitmap_ior_into (op1, tmp); BITMAP_FREE (tmp); @@ -667,9 +543,9 @@ df_ru_confluence_n (struct dataflow *dflow, edge e) /* Transfer function. */ static bool -df_ru_transfer_function (struct dataflow *dflow, int bb_index) +df_ru_transfer_function (int bb_index) { - struct df_ru_bb_info *bb_info = df_ru_get_bb_info (dflow, bb_index); + struct df_ru_bb_info *bb_info = df_ru_get_bb_info (bb_index); unsigned int regno; bitmap_iterator bi; bitmap in = bb_info->in; @@ -682,15 +558,21 @@ df_ru_transfer_function (struct dataflow *dflow, int bb_index) return bitmap_ior_and_compl (in, gen, out, kill); else { - struct df *df = dflow->df; + struct df_ru_problem_data *problem_data; + bitmap tmp; bool changed = false; - bitmap tmp = BITMAP_ALLOC (NULL); + + /* Note that TMP is _not_ a temporary bitmap if we end up replacing + IN with TMP. Therefore, allocate TMP in the RU bitmaps obstack. */ + problem_data = (struct df_ru_problem_data *) df_ru->problem_data; + tmp = BITMAP_ALLOC (&problem_data->ru_bitmaps); + bitmap_copy (tmp, out); EXECUTE_IF_SET_IN_BITMAP (sparse_kill, 0, regno, bi) { bitmap_clear_range (tmp, - DF_REG_USE_GET (df, regno)->begin, - DF_REG_USE_GET (df, regno)->n_refs); + DF_USES_BEGIN (regno), + DF_USES_COUNT (regno)); } bitmap_and_compl_into (tmp, kill); bitmap_ior_into (tmp, gen); @@ -710,17 +592,17 @@ df_ru_transfer_function (struct dataflow *dflow, int bb_index) /* Free all storage associated with the problem. */ static void -df_ru_free (struct dataflow *dflow) +df_ru_free (void) { unsigned int i; struct df_ru_problem_data *problem_data - = (struct df_ru_problem_data *) dflow->problem_data; + = (struct df_ru_problem_data *) df_ru->problem_data; if (problem_data) { - for (i = 0; i < dflow->block_info_size; i++) + for (i = 0; i < df_ru->block_info_size; i++) { - struct df_ru_bb_info *bb_info = df_ru_get_bb_info (dflow, i); + struct df_ru_bb_info *bb_info = df_ru_get_bb_info (i); if (bb_info) { BITMAP_FREE (bb_info->kill); @@ -731,75 +613,80 @@ df_ru_free (struct dataflow *dflow) } } - free_alloc_pool (dflow->block_pool); - - for (i = 0; i < problem_data->use_sites_size; i++) - { - bitmap bm = problem_data->use_sites[i]; - if (bm) - BITMAP_FREE (bm); - } - - free (problem_data->use_sites); + free_alloc_pool (df_ru->block_pool); BITMAP_FREE (problem_data->sparse_invalidated_by_call); BITMAP_FREE (problem_data->dense_invalidated_by_call); + bitmap_obstack_release (&problem_data->ru_bitmaps); - dflow->block_info_size = 0; - free (dflow->block_info); - free (dflow->problem_data); + df_ru->block_info_size = 0; + free (df_ru->block_info); + free (df_ru->problem_data); } - free (dflow); + free (df_ru); } /* Debugging info. */ static void -df_ru_dump (struct dataflow *dflow, FILE *file) +df_ru_start_dump (FILE *file) { - basic_block bb; - struct df *df = dflow->df; struct df_ru_problem_data *problem_data - = (struct df_ru_problem_data *) dflow->problem_data; - unsigned int m = max_reg_num (); + = (struct df_ru_problem_data *) df_ru->problem_data; + unsigned int m = DF_REG_SIZE(df); unsigned int regno; - if (!dflow->block_info) + if (!df_ru->block_info) return; - fprintf (file, "Reaching uses:\n"); + fprintf (file, ";; Reaching uses:\n"); - fprintf (file, " sparse invalidated \t"); + fprintf (file, ";; sparse invalidated \t"); dump_bitmap (file, problem_data->sparse_invalidated_by_call); - fprintf (file, " dense invalidated \t"); + fprintf (file, " dense invalidated \t"); dump_bitmap (file, problem_data->dense_invalidated_by_call); for (regno = 0; regno < m; regno++) - if (DF_REG_USE_GET (df, regno)->n_refs) + if (DF_USES_COUNT (regno)) fprintf (file, "%d[%d,%d] ", regno, - DF_REG_USE_GET (df, regno)->begin, - DF_REG_USE_GET (df, regno)->n_refs); + DF_USES_BEGIN (regno), + DF_USES_COUNT (regno)); fprintf (file, "\n"); - - FOR_ALL_BB (bb) - { - struct df_ru_bb_info *bb_info = df_ru_get_bb_info (dflow, bb->index); - df_print_bb_index (bb, file); - - if (!bb_info->in) - continue; - - fprintf (file, " in \t(%d)\n", (int) bitmap_count_bits (bb_info->in)); - dump_bitmap (file, bb_info->in); - fprintf (file, " gen \t(%d)\n", (int) bitmap_count_bits (bb_info->gen)); - dump_bitmap (file, bb_info->gen); - fprintf (file, " kill\t(%d)\n", (int) bitmap_count_bits (bb_info->kill)); - dump_bitmap (file, bb_info->kill); - fprintf (file, " out \t(%d)\n", (int) bitmap_count_bits (bb_info->out)); - dump_bitmap (file, bb_info->out); - } } + +/* Debugging info at top of bb. */ + +static void +df_ru_top_dump (basic_block bb, FILE *file) +{ + struct df_ru_bb_info *bb_info = df_ru_get_bb_info (bb->index); + if (!bb_info || !bb_info->in) + return; + + fprintf (file, ";; ru in \t(%d)\n", (int) bitmap_count_bits (bb_info->in)); + dump_bitmap (file, bb_info->in); + fprintf (file, ";; ru gen \t(%d)\n", (int) bitmap_count_bits (bb_info->gen)); + dump_bitmap (file, bb_info->gen); + fprintf (file, ";; ru kill\t(%d)\n", (int) bitmap_count_bits (bb_info->kill)); + dump_bitmap (file, bb_info->kill); +} + + +/* Debugging info at bottom of bb. */ + +static void +df_ru_bottom_dump (basic_block bb, FILE *file) +{ + struct df_ru_bb_info *bb_info = df_ru_get_bb_info (bb->index); + if (!bb_info || !bb_info->out) + return; + + fprintf (file, ";; ru out \t(%d)\n", (int) bitmap_count_bits (bb_info->out)); + dump_bitmap (file, bb_info->out); +} + + /* All of the information associated with every instance of the problem. */ static struct df_problem problem_RU = @@ -811,15 +698,20 @@ static struct df_problem problem_RU = df_ru_free_bb_info, /* Free basic block info. */ df_ru_local_compute, /* Local compute function. */ df_ru_init_solution, /* Init the solution specific data. */ - df_iterative_dataflow, /* Iterative solver. */ + df_worklist_dataflow, /* Worklist solver. */ NULL, /* Confluence operator 0. */ df_ru_confluence_n, /* Confluence operator n. */ df_ru_transfer_function, /* Transfer function. */ NULL, /* Finalize function. */ df_ru_free, /* Free all of the problem information. */ - df_ru_dump, /* Debugging. */ + df_ru_free, /* Remove this problem from the stack of dataflow problems. */ + df_ru_start_dump, /* Debugging. */ + df_ru_top_dump, /* Debugging start block. */ + df_ru_bottom_dump, /* Debugging end block. */ + NULL, /* Incremental solution verify start. */ + NULL, /* Incremental solution verfiy end. */ NULL, /* Dependent problem. */ - 0 /* Changeable flags. */ + TV_DF_RU /* Timing variable. */ }; @@ -828,10 +720,10 @@ static struct df_problem problem_RU = of DF. The returned structure is what is used to get at the solution. */ -struct dataflow * -df_ru_add_problem (struct df *df, int flags) +void +df_ru_add_problem (void) { - return df_add_problem (df, &problem_RU, flags); + df_add_problem (&problem_RU); } @@ -852,43 +744,30 @@ df_ru_add_problem (struct df *df, int flags) data structures are not accessible outside of this module. */ struct df_rd_problem_data { - /* If the number of defs for regnum N is less than - DF_SPARSE_THRESHOLD, uses_sites[N] contains a mask of the all of - the defs of reg N indexed by the id in the ref structure. If - there are more than DF_SPARSE_THRESHOLD defs for regnum N a - different mechanism is used to mask the def. */ - bitmap *def_sites; /* Bitmap of defs for each pseudo. */ - unsigned int def_sites_size; /* Size of def_sites. */ /* The set of defs to regs invalidated by call. */ bitmap sparse_invalidated_by_call; /* The set of defs to regs invalidate by call for rd. */ - bitmap dense_invalidated_by_call; + bitmap dense_invalidated_by_call; + /* An obstack for the bitmaps we need for this problem. */ + bitmap_obstack rd_bitmaps; }; -/* Get basic block info. */ - -struct df_rd_bb_info * -df_rd_get_bb_info (struct dataflow *dflow, unsigned int index) -{ - return (struct df_rd_bb_info *) dflow->block_info[index]; -} - - /* Set basic block info. */ static void -df_rd_set_bb_info (struct dataflow *dflow, unsigned int index, +df_rd_set_bb_info (unsigned int index, struct df_rd_bb_info *bb_info) { - dflow->block_info[index] = bb_info; + gcc_assert (df_rd); + gcc_assert (index < df_rd->block_info_size); + df_rd->block_info[index] = bb_info; } /* Free basic block info. */ static void -df_rd_free_bb_info (struct dataflow *dflow, - basic_block bb ATTRIBUTE_UNUSED, +df_rd_free_bb_info (basic_block bb ATTRIBUTE_UNUSED, void *vbb_info) { struct df_rd_bb_info *bb_info = (struct df_rd_bb_info *) vbb_info; @@ -899,67 +778,44 @@ df_rd_free_bb_info (struct dataflow *dflow, BITMAP_FREE (bb_info->gen); BITMAP_FREE (bb_info->in); BITMAP_FREE (bb_info->out); - pool_free (dflow->block_pool, bb_info); + pool_free (df_rd->block_pool, bb_info); } } -/* Allocate or reset bitmaps for DFLOW blocks. The solution bits are +/* Allocate or reset bitmaps for DF_RD blocks. The solution bits are not touched unless the block is new. */ static void -df_rd_alloc (struct dataflow *dflow, - bitmap blocks_to_rescan ATTRIBUTE_UNUSED, - bitmap all_blocks) +df_rd_alloc (bitmap all_blocks) { unsigned int bb_index; bitmap_iterator bi; - unsigned int reg_size = max_reg_num (); + struct df_rd_problem_data *problem_data; - if (!dflow->block_pool) - dflow->block_pool = create_alloc_pool ("df_rd_block pool", + if (!df_rd->block_pool) + df_rd->block_pool = create_alloc_pool ("df_rd_block pool", sizeof (struct df_rd_bb_info), 50); - if (dflow->problem_data) + if (df_rd->problem_data) { - unsigned int i; - struct df_rd_problem_data *problem_data - = (struct df_rd_problem_data *) dflow->problem_data; - - for (i = 0; i < problem_data->def_sites_size; i++) - { - bitmap bm = problem_data->def_sites[i]; - if (bm) - { - BITMAP_FREE (bm); - problem_data->def_sites[i] = NULL; - } - } - - if (problem_data->def_sites_size < reg_size) - { - problem_data->def_sites - = xrealloc (problem_data->def_sites, reg_size *sizeof (bitmap)); - memset (problem_data->def_sites + problem_data->def_sites_size, 0, - (reg_size - problem_data->def_sites_size) *sizeof (bitmap)); - problem_data->def_sites_size = reg_size; - } - + problem_data = (struct df_rd_problem_data *) df_rd->problem_data; bitmap_clear (problem_data->sparse_invalidated_by_call); bitmap_clear (problem_data->dense_invalidated_by_call); } else { - struct df_rd_problem_data *problem_data = XNEW (struct df_rd_problem_data); - dflow->problem_data = problem_data; - - problem_data->def_sites = XCNEWVEC (bitmap, reg_size); - problem_data->def_sites_size = reg_size; - problem_data->sparse_invalidated_by_call = BITMAP_ALLOC (NULL); - problem_data->dense_invalidated_by_call = BITMAP_ALLOC (NULL); + problem_data = XNEW (struct df_rd_problem_data); + df_rd->problem_data = problem_data; + + bitmap_obstack_initialize (&problem_data->rd_bitmaps); + problem_data->sparse_invalidated_by_call + = BITMAP_ALLOC (&problem_data->rd_bitmaps); + problem_data->dense_invalidated_by_call + = BITMAP_ALLOC (&problem_data->rd_bitmaps); } - df_grow_bb_info (dflow); + df_grow_bb_info (df_rd); /* Because of the clustering of all use sites for the same pseudo, we have to process all of the blocks before doing the @@ -967,7 +823,7 @@ df_rd_alloc (struct dataflow *dflow, EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) { - struct df_rd_bb_info *bb_info = df_rd_get_bb_info (dflow, bb_index); + struct df_rd_bb_info *bb_info = df_rd_get_bb_info (bb_index); if (bb_info) { bitmap_clear (bb_info->kill); @@ -976,13 +832,13 @@ df_rd_alloc (struct dataflow *dflow, } else { - bb_info = (struct df_rd_bb_info *) pool_alloc (dflow->block_pool); - df_rd_set_bb_info (dflow, bb_index, bb_info); - bb_info->kill = BITMAP_ALLOC (NULL); - bb_info->sparse_kill = BITMAP_ALLOC (NULL); - bb_info->gen = BITMAP_ALLOC (NULL); - bb_info->in = BITMAP_ALLOC (NULL); - bb_info->out = BITMAP_ALLOC (NULL); + bb_info = (struct df_rd_bb_info *) pool_alloc (df_rd->block_pool); + df_rd_set_bb_info (bb_index, bb_info); + bb_info->kill = BITMAP_ALLOC (&problem_data->rd_bitmaps); + bb_info->sparse_kill = BITMAP_ALLOC (&problem_data->rd_bitmaps); + bb_info->gen = BITMAP_ALLOC (&problem_data->rd_bitmaps); + bb_info->in = BITMAP_ALLOC (&problem_data->rd_bitmaps); + bb_info->out = BITMAP_ALLOC (&problem_data->rd_bitmaps); } } } @@ -991,75 +847,76 @@ df_rd_alloc (struct dataflow *dflow, /* Process a list of DEFs for df_rd_bb_local_compute. */ static void -df_rd_bb_local_compute_process_def (struct dataflow *dflow, - struct df_rd_bb_info *bb_info, - struct df_ref *def, +df_rd_bb_local_compute_process_def (struct df_rd_bb_info *bb_info, + struct df_ref **def_rec, enum df_ref_flags top_flag) { - struct df *df = dflow->df; - while (def) + while (*def_rec) { + struct df_ref *def = *def_rec; if (top_flag == (DF_REF_FLAGS (def) & DF_REF_AT_TOP)) { unsigned int regno = DF_REF_REGNO (def); - unsigned int begin = DF_REG_DEF_GET (df, regno)->begin; - unsigned int n_defs = DF_REG_DEF_GET (df, regno)->n_refs; + unsigned int begin = DF_DEFS_BEGIN (regno); + unsigned int n_defs = DF_DEFS_COUNT (regno); - /* Only the last def(s) for a regno in the block has any - effect. */ - if (!bitmap_bit_p (seen_in_block, regno)) + if ((!(df->changeable_flags & DF_NO_HARD_REGS)) + || (regno >= FIRST_PSEUDO_REGISTER)) { - /* The first def for regno in insn gets to knock out the - defs from other instructions. */ - if ((!bitmap_bit_p (seen_in_insn, regno)) - /* If the def is to only part of the reg, it does - not kill the other defs that reach here. */ - && (!((DF_REF_FLAGS (def) & DF_REF_PARTIAL) - || (DF_REF_FLAGS (def) & DF_REF_MAY_CLOBBER)))) + /* Only the last def(s) for a regno in the block has any + effect. */ + if (!bitmap_bit_p (seen_in_block, regno)) { - if (n_defs > DF_SPARSE_THRESHOLD) + /* The first def for regno in insn gets to knock out the + defs from other instructions. */ + if ((!bitmap_bit_p (seen_in_insn, regno)) + /* If the def is to only part of the reg, it does + not kill the other defs that reach here. */ + && (!(DF_REF_FLAGS (def) & + (DF_REF_PARTIAL | DF_REF_CONDITIONAL | DF_REF_MAY_CLOBBER)))) { - bitmap_set_bit (bb_info->sparse_kill, regno); - bitmap_clear_range(bb_info->gen, begin, n_defs); - } - else - { - struct df_rd_problem_data * problem_data - = (struct df_rd_problem_data *)dflow->problem_data; - bitmap defs = df_ref_bitmap (problem_data->def_sites, - regno, begin, n_defs); - bitmap_ior_into (bb_info->kill, defs); - bitmap_and_compl_into (bb_info->gen, defs); + if (n_defs > DF_SPARSE_THRESHOLD) + { + bitmap_set_bit (bb_info->sparse_kill, regno); + bitmap_clear_range(bb_info->gen, begin, n_defs); + } + else + { + bitmap_set_range (bb_info->kill, begin, n_defs); + bitmap_clear_range (bb_info->gen, begin, n_defs); + } } + + bitmap_set_bit (seen_in_insn, regno); + /* All defs for regno in the instruction may be put into + the gen set. */ + if (!(DF_REF_FLAGS (def) + & (DF_REF_MUST_CLOBBER | DF_REF_MAY_CLOBBER))) + bitmap_set_bit (bb_info->gen, DF_REF_ID (def)); } - - bitmap_set_bit (seen_in_insn, regno); - /* All defs for regno in the instruction may be put into - the gen set. */ - if (!(DF_REF_FLAGS (def) - & (DF_REF_MUST_CLOBBER | DF_REF_MAY_CLOBBER))) - bitmap_set_bit (bb_info->gen, DF_REF_ID (def)); } } - def = def->next_ref; + def_rec++; } } /* Compute local reaching def info for basic block BB. */ static void -df_rd_bb_local_compute (struct dataflow *dflow, unsigned int bb_index) +df_rd_bb_local_compute (unsigned int bb_index) { - struct df *df = dflow->df; basic_block bb = BASIC_BLOCK (bb_index); - struct df_rd_bb_info *bb_info = df_rd_get_bb_info (dflow, bb_index); + struct df_rd_bb_info *bb_info = df_rd_get_bb_info (bb_index); rtx insn; bitmap_clear (seen_in_block); bitmap_clear (seen_in_insn); - df_rd_bb_local_compute_process_def (dflow, bb_info, - df_get_artificial_defs (df, bb_index), 0); + /* Artificials are only hard regs. */ + if (!(df->changeable_flags & DF_NO_HARD_REGS)) + df_rd_bb_local_compute_process_def (bb_info, + df_get_artificial_defs (bb_index), + 0); FOR_BB_INSNS_REVERSE (bb, insn) { @@ -1068,8 +925,8 @@ df_rd_bb_local_compute (struct dataflow *dflow, unsigned int bb_index) if (!INSN_P (insn)) continue; - df_rd_bb_local_compute_process_def (dflow, bb_info, - DF_INSN_UID_DEFS (df, uid), 0); + df_rd_bb_local_compute_process_def (bb_info, + DF_INSN_UID_DEFS (uid), 0); /* This complex dance with the two bitmaps is required because instructions can assign twice to the same pseudo. This @@ -1084,50 +941,44 @@ df_rd_bb_local_compute (struct dataflow *dflow, unsigned int bb_index) /* Process the artificial defs at the top of the block last since we are going backwards through the block and these are logically at the start. */ - df_rd_bb_local_compute_process_def (dflow, bb_info, - df_get_artificial_defs (df, bb_index), - DF_REF_AT_TOP); + if (!(df->changeable_flags & DF_NO_HARD_REGS)) + df_rd_bb_local_compute_process_def (bb_info, + df_get_artificial_defs (bb_index), + DF_REF_AT_TOP); } /* Compute local reaching def info for each basic block within BLOCKS. */ static void -df_rd_local_compute (struct dataflow *dflow, - bitmap all_blocks, - bitmap rescan_blocks ATTRIBUTE_UNUSED) +df_rd_local_compute (bitmap all_blocks) { - struct df *df = dflow->df; unsigned int bb_index; bitmap_iterator bi; unsigned int regno; struct df_rd_problem_data *problem_data - = (struct df_rd_problem_data *) dflow->problem_data; + = (struct df_rd_problem_data *) df_rd->problem_data; bitmap sparse_invalidated = problem_data->sparse_invalidated_by_call; bitmap dense_invalidated = problem_data->dense_invalidated_by_call; df_set_seen (); - df_reorganize_refs (&df->def_info); + + df_maybe_reorganize_def_refs (DF_REF_ORDER_BY_REG); EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) { - df_rd_bb_local_compute (dflow, bb_index); + df_rd_bb_local_compute (bb_index); } /* Set up the knockout bit vectors to be applied across EH_EDGES. */ EXECUTE_IF_SET_IN_BITMAP (df_invalidated_by_call, 0, regno, bi) { - struct df_reg_info *reg_info = DF_REG_DEF_GET (df, regno); - if (reg_info->n_refs > DF_SPARSE_THRESHOLD) - { - bitmap_set_bit (sparse_invalidated, regno); - } + if (DF_DEFS_COUNT (regno) > DF_SPARSE_THRESHOLD) + bitmap_set_bit (sparse_invalidated, regno); else - { - bitmap defs = df_ref_bitmap (problem_data->def_sites, regno, - reg_info->begin, reg_info->n_refs); - bitmap_ior_into (dense_invalidated, defs); - } + bitmap_set_range (dense_invalidated, + DF_DEFS_BEGIN (regno), + DF_DEFS_COUNT (regno)); } df_unset_seen (); } @@ -1136,14 +987,14 @@ df_rd_local_compute (struct dataflow *dflow, /* Initialize the solution bit vectors for problem. */ static void -df_rd_init_solution (struct dataflow *dflow, bitmap all_blocks) +df_rd_init_solution (bitmap all_blocks) { unsigned int bb_index; bitmap_iterator bi; EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) { - struct df_rd_bb_info *bb_info = df_rd_get_bb_info (dflow, bb_index); + struct df_rd_bb_info *bb_info = df_rd_get_bb_info (bb_index); bitmap_copy (bb_info->out, bb_info->gen); bitmap_clear (bb_info->in); @@ -1153,21 +1004,20 @@ df_rd_init_solution (struct dataflow *dflow, bitmap all_blocks) /* In of target gets or of out of source. */ static void -df_rd_confluence_n (struct dataflow *dflow, edge e) +df_rd_confluence_n (edge e) { - bitmap op1 = df_rd_get_bb_info (dflow, e->dest->index)->in; - bitmap op2 = df_rd_get_bb_info (dflow, e->src->index)->out; + bitmap op1 = df_rd_get_bb_info (e->dest->index)->in; + bitmap op2 = df_rd_get_bb_info (e->src->index)->out; if (e->flags & EDGE_EH) { struct df_rd_problem_data *problem_data - = (struct df_rd_problem_data *) dflow->problem_data; + = (struct df_rd_problem_data *) df_rd->problem_data; bitmap sparse_invalidated = problem_data->sparse_invalidated_by_call; bitmap dense_invalidated = problem_data->dense_invalidated_by_call; - struct df *df = dflow->df; bitmap_iterator bi; unsigned int regno; - bitmap tmp = BITMAP_ALLOC (NULL); + bitmap tmp = BITMAP_ALLOC (&df_bitmap_obstack); bitmap_copy (tmp, op2); bitmap_and_compl_into (tmp, dense_invalidated); @@ -1175,8 +1025,8 @@ df_rd_confluence_n (struct dataflow *dflow, edge e) EXECUTE_IF_SET_IN_BITMAP (sparse_invalidated, 0, regno, bi) { bitmap_clear_range (tmp, - DF_REG_DEF_GET (df, regno)->begin, - DF_REG_DEF_GET (df, regno)->n_refs); + DF_DEFS_BEGIN (regno), + DF_DEFS_COUNT (regno)); } bitmap_ior_into (op1, tmp); BITMAP_FREE (tmp); @@ -1189,9 +1039,9 @@ df_rd_confluence_n (struct dataflow *dflow, edge e) /* Transfer function. */ static bool -df_rd_transfer_function (struct dataflow *dflow, int bb_index) +df_rd_transfer_function (int bb_index) { - struct df_rd_bb_info *bb_info = df_rd_get_bb_info (dflow, bb_index); + struct df_rd_bb_info *bb_info = df_rd_get_bb_info (bb_index); unsigned int regno; bitmap_iterator bi; bitmap in = bb_info->in; @@ -1204,15 +1054,21 @@ df_rd_transfer_function (struct dataflow *dflow, int bb_index) return bitmap_ior_and_compl (out, gen, in, kill); else { - struct df *df = dflow->df; + struct df_rd_problem_data *problem_data; bool changed = false; - bitmap tmp = BITMAP_ALLOC (NULL); + bitmap tmp; + + /* Note that TMP is _not_ a temporary bitmap if we end up replacing + OUT with TMP. Therefore, allocate TMP in the RD bitmaps obstack. */ + problem_data = (struct df_rd_problem_data *) df_rd->problem_data; + tmp = BITMAP_ALLOC (&problem_data->rd_bitmaps); + bitmap_copy (tmp, in); EXECUTE_IF_SET_IN_BITMAP (sparse_kill, 0, regno, bi) { bitmap_clear_range (tmp, - DF_REG_DEF_GET (df, regno)->begin, - DF_REG_DEF_GET (df, regno)->n_refs); + DF_DEFS_BEGIN (regno), + DF_DEFS_COUNT (regno)); } bitmap_and_compl_into (tmp, kill); bitmap_ior_into (tmp, gen); @@ -1232,17 +1088,17 @@ df_rd_transfer_function (struct dataflow *dflow, int bb_index) /* Free all storage associated with the problem. */ static void -df_rd_free (struct dataflow *dflow) +df_rd_free (void) { unsigned int i; struct df_rd_problem_data *problem_data - = (struct df_rd_problem_data *) dflow->problem_data; + = (struct df_rd_problem_data *) df_rd->problem_data; if (problem_data) { - for (i = 0; i < dflow->block_info_size; i++) + for (i = 0; i < df_rd->block_info_size; i++) { - struct df_rd_bb_info *bb_info = df_rd_get_bb_info (dflow, i); + struct df_rd_bb_info *bb_info = df_rd_get_bb_info (i); if (bb_info) { BITMAP_FREE (bb_info->kill); @@ -1253,43 +1109,33 @@ df_rd_free (struct dataflow *dflow) } } - free_alloc_pool (dflow->block_pool); - - for (i = 0; i < problem_data->def_sites_size; i++) - { - bitmap bm = problem_data->def_sites[i]; - if (bm) - BITMAP_FREE (bm); - } - - free (problem_data->def_sites); + free_alloc_pool (df_rd->block_pool); BITMAP_FREE (problem_data->sparse_invalidated_by_call); BITMAP_FREE (problem_data->dense_invalidated_by_call); + bitmap_obstack_release (&problem_data->rd_bitmaps); - dflow->block_info_size = 0; - free (dflow->block_info); - free (dflow->problem_data); + df_rd->block_info_size = 0; + free (df_rd->block_info); + free (df_rd->problem_data); } - free (dflow); + free (df_rd); } /* Debugging info. */ static void -df_rd_dump (struct dataflow *dflow, FILE *file) +df_rd_start_dump (FILE *file) { - struct df *df = dflow->df; - basic_block bb; struct df_rd_problem_data *problem_data - = (struct df_rd_problem_data *) dflow->problem_data; - unsigned int m = max_reg_num (); + = (struct df_rd_problem_data *) df_rd->problem_data; + unsigned int m = DF_REG_SIZE(df); unsigned int regno; - if (!dflow->block_info) + if (!df_rd->block_info) return; - fprintf (file, "Reaching defs:\n\n"); + fprintf (file, ";; Reaching defs:\n\n"); fprintf (file, " sparse invalidated \t"); dump_bitmap (file, problem_data->sparse_invalidated_by_call); @@ -1297,29 +1143,44 @@ df_rd_dump (struct dataflow *dflow, FILE *file) dump_bitmap (file, problem_data->dense_invalidated_by_call); for (regno = 0; regno < m; regno++) - if (DF_REG_DEF_GET (df, regno)->n_refs) + if (DF_DEFS_COUNT (regno)) fprintf (file, "%d[%d,%d] ", regno, - DF_REG_DEF_GET (df, regno)->begin, - DF_REG_DEF_GET (df, regno)->n_refs); + DF_DEFS_BEGIN (regno), + DF_DEFS_COUNT (regno)); fprintf (file, "\n"); - FOR_ALL_BB (bb) - { - struct df_rd_bb_info *bb_info = df_rd_get_bb_info (dflow, bb->index); - df_print_bb_index (bb, file); - - if (!bb_info->in) - continue; - - fprintf (file, " in \t(%d)\n", (int) bitmap_count_bits (bb_info->in)); - dump_bitmap (file, bb_info->in); - fprintf (file, " gen \t(%d)\n", (int) bitmap_count_bits (bb_info->gen)); - dump_bitmap (file, bb_info->gen); - fprintf (file, " kill\t(%d)\n", (int) bitmap_count_bits (bb_info->kill)); - dump_bitmap (file, bb_info->kill); - fprintf (file, " out \t(%d)\n", (int) bitmap_count_bits (bb_info->out)); - dump_bitmap (file, bb_info->out); - } +} + + +/* Debugging info at top of bb. */ + +static void +df_rd_top_dump (basic_block bb, FILE *file) +{ + struct df_rd_bb_info *bb_info = df_rd_get_bb_info (bb->index); + if (!bb_info || !bb_info->in) + return; + + fprintf (file, ";; rd in \t(%d)\n", (int) bitmap_count_bits (bb_info->in)); + dump_bitmap (file, bb_info->in); + fprintf (file, ";; rd gen \t(%d)\n", (int) bitmap_count_bits (bb_info->gen)); + dump_bitmap (file, bb_info->gen); + fprintf (file, ";; rd kill\t(%d)\n", (int) bitmap_count_bits (bb_info->kill)); + dump_bitmap (file, bb_info->kill); +} + + +/* Debugging info at top of bb. */ + +static void +df_rd_bottom_dump (basic_block bb, FILE *file) +{ + struct df_rd_bb_info *bb_info = df_rd_get_bb_info (bb->index); + if (!bb_info || !bb_info->out) + return; + + fprintf (file, ";; rd out \t(%d)\n", (int) bitmap_count_bits (bb_info->out)); + dump_bitmap (file, bb_info->out); } /* All of the information associated with every instance of the problem. */ @@ -1333,15 +1194,20 @@ static struct df_problem problem_RD = df_rd_free_bb_info, /* Free basic block info. */ df_rd_local_compute, /* Local compute function. */ df_rd_init_solution, /* Init the solution specific data. */ - df_iterative_dataflow, /* Iterative solver. */ + df_worklist_dataflow, /* Worklist solver. */ NULL, /* Confluence operator 0. */ df_rd_confluence_n, /* Confluence operator n. */ df_rd_transfer_function, /* Transfer function. */ NULL, /* Finalize function. */ df_rd_free, /* Free all of the problem information. */ - df_rd_dump, /* Debugging. */ + df_rd_free, /* Remove this problem from the stack of dataflow problems. */ + df_rd_start_dump, /* Debugging. */ + df_rd_top_dump, /* Debugging start block. */ + df_rd_bottom_dump, /* Debugging end block. */ + NULL, /* Incremental solution verify start. */ + NULL, /* Incremental solution verfiy end. */ NULL, /* Dependent problem. */ - 0 /* Changeable flags. */ + TV_DF_RD /* Timing variable. */ }; @@ -1350,10 +1216,10 @@ static struct df_problem problem_RD = of DF. The returned structure is what is used to get at the solution. */ -struct dataflow * -df_rd_add_problem (struct df *df, int flags) +void +df_rd_add_problem (void) { - return df_add_problem (df, &problem_RD, flags); + df_add_problem (&problem_RD); } @@ -1367,30 +1233,30 @@ df_rd_add_problem (struct df *df, int flags) See df.h for details. ----------------------------------------------------------------------------*/ -/* Get basic block info. */ - -struct df_lr_bb_info * -df_lr_get_bb_info (struct dataflow *dflow, unsigned int index) +/* Private data used to verify the solution for this problem. */ +struct df_lr_problem_data { - return (struct df_lr_bb_info *) dflow->block_info[index]; -} + bitmap *in; + bitmap *out; +}; /* Set basic block info. */ static void -df_lr_set_bb_info (struct dataflow *dflow, unsigned int index, +df_lr_set_bb_info (unsigned int index, struct df_lr_bb_info *bb_info) { - dflow->block_info[index] = bb_info; + gcc_assert (df_lr); + gcc_assert (index < df_lr->block_info_size); + df_lr->block_info[index] = bb_info; } /* Free basic block info. */ static void -df_lr_free_bb_info (struct dataflow *dflow, - basic_block bb ATTRIBUTE_UNUSED, +df_lr_free_bb_info (basic_block bb ATTRIBUTE_UNUSED, void *vbb_info) { struct df_lr_bb_info *bb_info = (struct df_lr_bb_info *) vbb_info; @@ -1398,77 +1264,115 @@ df_lr_free_bb_info (struct dataflow *dflow, { BITMAP_FREE (bb_info->use); BITMAP_FREE (bb_info->def); + if (bb_info->in == bb_info->top) + bb_info->top = NULL; + else + { + BITMAP_FREE (bb_info->top); + BITMAP_FREE (bb_info->ause); + BITMAP_FREE (bb_info->adef); + } BITMAP_FREE (bb_info->in); BITMAP_FREE (bb_info->out); - pool_free (dflow->block_pool, bb_info); + pool_free (df_lr->block_pool, bb_info); } } -/* Allocate or reset bitmaps for DFLOW blocks. The solution bits are +/* Allocate or reset bitmaps for DF_LR blocks. The solution bits are not touched unless the block is new. */ static void -df_lr_alloc (struct dataflow *dflow, bitmap blocks_to_rescan, - bitmap all_blocks ATTRIBUTE_UNUSED) +df_lr_alloc (bitmap all_blocks ATTRIBUTE_UNUSED) { unsigned int bb_index; bitmap_iterator bi; - if (!dflow->block_pool) - dflow->block_pool = create_alloc_pool ("df_lr_block pool", + if (!df_lr->block_pool) + df_lr->block_pool = create_alloc_pool ("df_lr_block pool", sizeof (struct df_lr_bb_info), 50); - df_grow_bb_info (dflow); + df_grow_bb_info (df_lr); - EXECUTE_IF_SET_IN_BITMAP (blocks_to_rescan, 0, bb_index, bi) + EXECUTE_IF_SET_IN_BITMAP (df_lr->out_of_date_transfer_functions, 0, bb_index, bi) { - struct df_lr_bb_info *bb_info = df_lr_get_bb_info (dflow, bb_index); + struct df_lr_bb_info *bb_info = df_lr_get_bb_info (bb_index); if (bb_info) { bitmap_clear (bb_info->def); bitmap_clear (bb_info->use); + if (bb_info->adef) + { + bitmap_clear (bb_info->adef); + bitmap_clear (bb_info->ause); + } } else { - bb_info = (struct df_lr_bb_info *) pool_alloc (dflow->block_pool); - df_lr_set_bb_info (dflow, bb_index, bb_info); + bb_info = (struct df_lr_bb_info *) pool_alloc (df_lr->block_pool); + df_lr_set_bb_info (bb_index, bb_info); bb_info->use = BITMAP_ALLOC (NULL); bb_info->def = BITMAP_ALLOC (NULL); bb_info->in = BITMAP_ALLOC (NULL); bb_info->out = BITMAP_ALLOC (NULL); + bb_info->top = bb_info->in; + bb_info->adef = NULL; + bb_info->ause = NULL; } } } +/* Reset the global solution for recalculation. */ + +static void +df_lr_reset (bitmap all_blocks) +{ + unsigned int bb_index; + bitmap_iterator bi; + + EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) + { + struct df_lr_bb_info *bb_info = df_lr_get_bb_info (bb_index); + gcc_assert (bb_info); + bitmap_clear (bb_info->in); + bitmap_clear (bb_info->out); + bitmap_clear (bb_info->top); + } +} + + /* Compute local live register info for basic block BB. */ static void -df_lr_bb_local_compute (struct dataflow *dflow, - struct df *df, unsigned int bb_index) +df_lr_bb_local_compute (unsigned int bb_index) { basic_block bb = BASIC_BLOCK (bb_index); - struct df_lr_bb_info *bb_info = df_lr_get_bb_info (dflow, bb_index); + struct df_lr_bb_info *bb_info = df_lr_get_bb_info (bb_index); rtx insn; - struct df_ref *def; - struct df_ref *use; + struct df_ref **def_rec; + struct df_ref **use_rec; /* Process the registers set in an exception handler. */ - for (def = df_get_artificial_defs (df, bb_index); def; def = def->next_ref) - if (((DF_REF_FLAGS (def) & DF_REF_AT_TOP) == 0) - && (!(DF_REF_FLAGS (def) & DF_REF_PARTIAL))) - { - unsigned int dregno = DF_REF_REGNO (def); - bitmap_set_bit (bb_info->def, dregno); - bitmap_clear_bit (bb_info->use, dregno); - } + for (def_rec = df_get_artificial_defs (bb_index); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if ((DF_REF_FLAGS (def) & DF_REF_AT_TOP) == 0) + { + unsigned int dregno = DF_REF_REGNO (def); + bitmap_set_bit (bb_info->def, dregno); + bitmap_clear_bit (bb_info->use, dregno); + } + } /* Process the hardware registers that are always live. */ - for (use = df_get_artificial_uses (df, bb_index); use; use = use->next_ref) - /* Add use to set of uses in this BB. */ - if ((DF_REF_FLAGS (use) & DF_REF_AT_TOP) == 0) - bitmap_set_bit (bb_info->use, DF_REF_REGNO (use)); + for (use_rec = df_get_artificial_uses (bb_index); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + /* Add use to set of uses in this BB. */ + if ((DF_REF_FLAGS (use) & DF_REF_AT_TOP) == 0) + bitmap_set_bit (bb_info->use, DF_REF_REGNO (use)); + } FOR_BB_INSNS_REVERSE (bb, insn) { @@ -1479,73 +1383,100 @@ df_lr_bb_local_compute (struct dataflow *dflow, if (CALL_P (insn)) { - for (def = DF_INSN_UID_DEFS (df, uid); def; def = def->next_ref) + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) { + struct df_ref *def = *def_rec; unsigned int dregno = DF_REF_REGNO (def); - if (dregno >= FIRST_PSEUDO_REGISTER - || !(SIBLING_CALL_P (insn) - && bitmap_bit_p (df->exit_block_uses, dregno) - && !refers_to_regno_p (dregno, dregno+1, - current_function_return_rtx, - (rtx *)0))) + if (DF_REF_FLAGS (def) & DF_REF_MUST_CLOBBER) { - /* If the def is to only part of the reg, it does - not kill the other defs that reach here. */ - if (!(DF_REF_FLAGS (def) & DF_REF_PARTIAL)) + if (dregno >= FIRST_PSEUDO_REGISTER + || !(SIBLING_CALL_P (insn) + && bitmap_bit_p (df->exit_block_uses, dregno) + && !refers_to_regno_p (dregno, dregno+1, + current_function_return_rtx, + (rtx *)0))) { - bitmap_set_bit (bb_info->def, dregno); - bitmap_clear_bit (bb_info->use, dregno); + /* If the def is to only part of the reg, it does + not kill the other defs that reach here. */ + if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))) + { + bitmap_set_bit (bb_info->def, dregno); + bitmap_clear_bit (bb_info->use, dregno); + } } } + else + /* This is the return value. */ + if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))) + { + bitmap_set_bit (bb_info->def, dregno); + bitmap_clear_bit (bb_info->use, dregno); + } } } else { - for (def = DF_INSN_UID_DEFS (df, uid); def; def = def->next_ref) + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) { - unsigned int dregno = DF_REF_REGNO (def); - - if (DF_INSN_CONTAINS_ASM (df, insn) - && dregno < FIRST_PSEUDO_REGISTER) - { - unsigned int i; - unsigned int end = dregno - + hard_regno_nregs[dregno][GET_MODE (DF_REF_REG (def))] - 1; - for (i = dregno; i <= end; ++i) - regs_asm_clobbered[i] = 1; - } + struct df_ref *def = *def_rec; /* If the def is to only part of the reg, it does not kill the other defs that reach here. */ - if (!(DF_REF_FLAGS (def) & DF_REF_PARTIAL)) + if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))) { + unsigned int dregno = DF_REF_REGNO (def); bitmap_set_bit (bb_info->def, dregno); bitmap_clear_bit (bb_info->use, dregno); } } } - for (use = DF_INSN_UID_USES (df, uid); use; use = use->next_ref) - /* Add use to set of uses in this BB. */ - bitmap_set_bit (bb_info->use, DF_REF_REGNO (use)); + for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + /* Add use to set of uses in this BB. */ + bitmap_set_bit (bb_info->use, DF_REF_REGNO (use)); + } } - /* Process the registers set in an exception handler. */ - for (def = df_get_artificial_defs (df, bb_index); def; def = def->next_ref) - if ((DF_REF_FLAGS (def) & DF_REF_AT_TOP) - && (!(DF_REF_FLAGS (def) & DF_REF_PARTIAL))) - { - unsigned int dregno = DF_REF_REGNO (def); - bitmap_set_bit (bb_info->def, dregno); - bitmap_clear_bit (bb_info->use, dregno); - } + for (def_rec = df_get_artificial_defs (bb_index); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if ((DF_REF_FLAGS (def) & DF_REF_AT_TOP) + && (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL)))) + { + unsigned int dregno = DF_REF_REGNO (def); + if (bb_info->adef == NULL) + { + gcc_assert (bb_info->ause == NULL); + gcc_assert (bb_info->top == bb_info->in); + bb_info->adef = BITMAP_ALLOC (NULL); + bb_info->ause = BITMAP_ALLOC (NULL); + bb_info->top = BITMAP_ALLOC (NULL); + } + bitmap_set_bit (bb_info->adef, dregno); + } + } #ifdef EH_USES /* Process the uses that are live into an exception handler. */ - for (use = df_get_artificial_uses (df, bb_index); use; use = use->next_ref) - /* Add use to set of uses in this BB. */ - if (DF_REF_FLAGS (use) & DF_REF_AT_TOP) - bitmap_set_bit (bb_info->use, DF_REF_REGNO (use)); + for (use_rec = df_get_artificial_uses (bb_index); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + /* Add use to set of uses in this BB. */ + if (DF_REF_FLAGS (use) & DF_REF_AT_TOP) + { + if (bb_info->adef == NULL) + { + gcc_assert (bb_info->ause == NULL); + gcc_assert (bb_info->top == bb_info->in); + bb_info->adef = BITMAP_ALLOC (NULL); + bb_info->ause = BITMAP_ALLOC (NULL); + bb_info->top = BITMAP_ALLOC (NULL); + } + bitmap_set_bit (bb_info->ause, DF_REF_REGNO (use)); + } + } #endif } @@ -1553,19 +1484,11 @@ df_lr_bb_local_compute (struct dataflow *dflow, /* Compute local live register info for each basic block within BLOCKS. */ static void -df_lr_local_compute (struct dataflow *dflow, - bitmap all_blocks, - bitmap rescan_blocks) +df_lr_local_compute (bitmap all_blocks ATTRIBUTE_UNUSED) { - struct df *df = dflow->df; unsigned int bb_index; bitmap_iterator bi; - /* Assume that the stack pointer is unchanging if alloca hasn't - been used. */ - if (bitmap_equal_p (all_blocks, rescan_blocks)) - memset (regs_asm_clobbered, 0, sizeof (regs_asm_clobbered)); - bitmap_clear (df->hardware_regs_used); /* The all-important stack pointer must always be live. */ @@ -1594,34 +1517,34 @@ df_lr_local_compute (struct dataflow *dflow, bitmap_set_bit (df->hardware_regs_used, PIC_OFFSET_TABLE_REGNUM); } - if (bitmap_bit_p (rescan_blocks, EXIT_BLOCK)) - { - /* The exit block is special for this problem and its bits are - computed from thin air. */ - struct df_lr_bb_info *bb_info = df_lr_get_bb_info (dflow, EXIT_BLOCK); - bitmap_copy (bb_info->use, df->exit_block_uses); - } - - EXECUTE_IF_SET_IN_BITMAP (rescan_blocks, 0, bb_index, bi) + EXECUTE_IF_SET_IN_BITMAP (df_lr->out_of_date_transfer_functions, 0, bb_index, bi) { if (bb_index == EXIT_BLOCK) - continue; - df_lr_bb_local_compute (dflow, df, bb_index); + { + /* The exit block is special for this problem and its bits are + computed from thin air. */ + struct df_lr_bb_info *bb_info = df_lr_get_bb_info (EXIT_BLOCK); + bitmap_copy (bb_info->use, df->exit_block_uses); + } + else + df_lr_bb_local_compute (bb_index); } + + bitmap_clear (df_lr->out_of_date_transfer_functions); } /* Initialize the solution vectors. */ static void -df_lr_init (struct dataflow *dflow, bitmap all_blocks) +df_lr_init (bitmap all_blocks) { unsigned int bb_index; bitmap_iterator bi; EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) { - struct df_lr_bb_info *bb_info = df_lr_get_bb_info (dflow, bb_index); + struct df_lr_bb_info *bb_info = df_lr_get_bb_info (bb_index); bitmap_copy (bb_info->in, bb_info->use); bitmap_clear (bb_info->out); } @@ -1632,11 +1555,9 @@ df_lr_init (struct dataflow *dflow, bitmap all_blocks) noreturn function that throws. And even if it isn't, getting the unwind info right helps debugging. */ static void -df_lr_confluence_0 (struct dataflow *dflow, basic_block bb) +df_lr_confluence_0 (basic_block bb) { - struct df *df = dflow->df; - - bitmap op1 = df_lr_get_bb_info (dflow, bb->index)->out; + bitmap op1 = df_lr_get_bb_info (bb->index)->out; if (bb != EXIT_BLOCK_PTR) bitmap_copy (op1, df->hardware_regs_used); } @@ -1645,10 +1566,10 @@ df_lr_confluence_0 (struct dataflow *dflow, basic_block bb) /* Confluence function that ignores fake edges. */ static void -df_lr_confluence_n (struct dataflow *dflow, edge e) +df_lr_confluence_n (edge e) { - bitmap op1 = df_lr_get_bb_info (dflow, e->src->index)->out; - bitmap op2 = df_lr_get_bb_info (dflow, e->dest->index)->in; + bitmap op1 = df_lr_get_bb_info (e->src->index)->out; + bitmap op2 = df_lr_get_bb_info (e->dest->index)->in; /* Call-clobbered registers die across exception and call edges. */ /* ??? Abnormal call edges ignored for the moment, as this gets @@ -1658,85 +1579,221 @@ df_lr_confluence_n (struct dataflow *dflow, edge e) else bitmap_ior_into (op1, op2); - bitmap_ior_into (op1, dflow->df->hardware_regs_used); + bitmap_ior_into (op1, df->hardware_regs_used); } /* Transfer function. */ static bool -df_lr_transfer_function (struct dataflow *dflow, int bb_index) +df_lr_transfer_function (int bb_index) { - struct df_lr_bb_info *bb_info = df_lr_get_bb_info (dflow, bb_index); + struct df_lr_bb_info *bb_info = df_lr_get_bb_info (bb_index); bitmap in = bb_info->in; bitmap out = bb_info->out; bitmap use = bb_info->use; bitmap def = bb_info->def; + bitmap top = bb_info->top; + bitmap ause = bb_info->ause; + bitmap adef = bb_info->adef; + bool changed; - return bitmap_ior_and_compl (in, use, out, def); + changed = bitmap_ior_and_compl (top, use, out, def); + if (in != top) + { + gcc_assert (ause && adef); + changed |= bitmap_ior_and_compl (in, ause, top, adef); + } + + return changed; +} + + +/* Run the fast dce as a side effect of building LR. */ + +static void +df_lr_local_finalize (bitmap all_blocks ATTRIBUTE_UNUSED) +{ + if (df->changeable_flags & DF_LR_RUN_DCE) + { + run_fast_df_dce (); + if (df_lr->problem_data && df_lr->solutions_dirty) + { + /* If we are here, then it is because we are both verifying + the solution and the dce changed the function. In that case + the verification info built will be wrong. So we leave the + dirty flag true so that the verifier will skip the checking + part and just clean up.*/ + df_lr->solutions_dirty = true; + } + else + df_lr->solutions_dirty = false; + } + else + df_lr->solutions_dirty = false; } /* Free all storage associated with the problem. */ static void -df_lr_free (struct dataflow *dflow) +df_lr_free (void) { - if (dflow->block_info) + if (df_lr->block_info) { unsigned int i; - for (i = 0; i < dflow->block_info_size; i++) + for (i = 0; i < df_lr->block_info_size; i++) { - struct df_lr_bb_info *bb_info = df_lr_get_bb_info (dflow, i); + struct df_lr_bb_info *bb_info = df_lr_get_bb_info (i); if (bb_info) { BITMAP_FREE (bb_info->use); BITMAP_FREE (bb_info->def); + if (bb_info->in == bb_info->top) + bb_info->top = NULL; + else + { + BITMAP_FREE (bb_info->top); + BITMAP_FREE (bb_info->ause); + BITMAP_FREE (bb_info->adef); + } BITMAP_FREE (bb_info->in); BITMAP_FREE (bb_info->out); } } - free_alloc_pool (dflow->block_pool); + free_alloc_pool (df_lr->block_pool); - dflow->block_info_size = 0; - free (dflow->block_info); + df_lr->block_info_size = 0; + free (df_lr->block_info); } - free (dflow->problem_data); - free (dflow); + BITMAP_FREE (df_lr->out_of_date_transfer_functions); + free (df_lr); } -/* Debugging info. */ +/* Debugging info at top of bb. */ static void -df_lr_dump (struct dataflow *dflow, FILE *file) +df_lr_top_dump (basic_block bb, FILE *file) { - basic_block bb; + struct df_lr_bb_info *bb_info = df_lr_get_bb_info (bb->index); + struct df_lr_problem_data *problem_data; + if (!bb_info || !bb_info->in) + return; + + fprintf (file, ";; lr in \t"); + df_print_regset (file, bb_info->in); + if (df_lr->problem_data) + { + problem_data = (struct df_lr_problem_data *)df_lr->problem_data; + fprintf (file, ";; old in \t"); + df_print_regset (file, problem_data->in[bb->index]); + } + fprintf (file, ";; lr use \t"); + df_print_regset (file, bb_info->use); + fprintf (file, ";; lr def \t"); + df_print_regset (file, bb_info->def); +} + + +/* Debugging info at bottom of bb. */ + +static void +df_lr_bottom_dump (basic_block bb, FILE *file) +{ + struct df_lr_bb_info *bb_info = df_lr_get_bb_info (bb->index); + struct df_lr_problem_data *problem_data; + if (!bb_info || !bb_info->out) + return; - if (!dflow->block_info) + fprintf (file, ";; lr out \t"); + df_print_regset (file, bb_info->out); + if (df_lr->problem_data) + { + problem_data = (struct df_lr_problem_data *)df_lr->problem_data; + fprintf (file, ";; old out \t"); + df_print_regset (file, problem_data->out[bb->index]); + } +} + + +/* Build the datastructure to verify that the solution to the dataflow + equations is not dirty. */ + +static void +df_lr_verify_solution_start (void) +{ + basic_block bb; + struct df_lr_problem_data *problem_data; + if (df_lr->solutions_dirty) + { + df_lr->problem_data = NULL; + return; + } + + /* Set it true so that the solution is recomputed. */ + df_lr->solutions_dirty = true; + + problem_data = XNEW (struct df_lr_problem_data); + df_lr->problem_data = problem_data; + problem_data->in = XNEWVEC (bitmap, last_basic_block); + problem_data->out = XNEWVEC (bitmap, last_basic_block); + + FOR_ALL_BB (bb) + { + problem_data->in[bb->index] = BITMAP_ALLOC (NULL); + problem_data->out[bb->index] = BITMAP_ALLOC (NULL); + bitmap_copy (problem_data->in[bb->index], DF_LR_IN (bb)); + bitmap_copy (problem_data->out[bb->index], DF_LR_OUT (bb)); + } +} + + +/* Compare the saved datastructure and the new solution to the dataflow + equations. */ + +static void +df_lr_verify_solution_end (void) +{ + struct df_lr_problem_data *problem_data; + basic_block bb; + + if (df_lr->problem_data == NULL) return; - fprintf (file, "Live Registers:\n"); + problem_data = (struct df_lr_problem_data *)df_lr->problem_data; + + if (df_lr->solutions_dirty) + /* Do not check if the solution is still dirty. See the comment + in df_lr_local_finalize for details. */ + df_lr->solutions_dirty = false; + else + FOR_ALL_BB (bb) + { + if ((!bitmap_equal_p (problem_data->in[bb->index], DF_LR_IN (bb))) + || (!bitmap_equal_p (problem_data->out[bb->index], DF_LR_OUT (bb)))) + { + /*df_dump (stderr);*/ + gcc_unreachable (); + } + } + + /* Cannot delete them immediately because you may want to dump them + if the comparison fails. */ FOR_ALL_BB (bb) { - struct df_lr_bb_info *bb_info = df_lr_get_bb_info (dflow, bb->index); - df_print_bb_index (bb, file); - - if (!bb_info->in) - continue; - - fprintf (file, " in \t"); - dump_bitmap (file, bb_info->in); - fprintf (file, " use \t"); - dump_bitmap (file, bb_info->use); - fprintf (file, " def \t"); - dump_bitmap (file, bb_info->def); - fprintf (file, " out \t"); - dump_bitmap (file, bb_info->out); + BITMAP_FREE (problem_data->in[bb->index]); + BITMAP_FREE (problem_data->out[bb->index]); } + + free (problem_data->in); + free (problem_data->out); + free (problem_data); + df_lr->problem_data = NULL; } + /* All of the information associated with every instance of the problem. */ static struct df_problem problem_LR = @@ -1744,19 +1801,24 @@ static struct df_problem problem_LR = DF_LR, /* Problem id. */ DF_BACKWARD, /* Direction. */ df_lr_alloc, /* Allocate the problem specific data. */ - NULL, /* Reset global information. */ + df_lr_reset, /* Reset global information. */ df_lr_free_bb_info, /* Free basic block info. */ df_lr_local_compute, /* Local compute function. */ df_lr_init, /* Init the solution specific data. */ - df_iterative_dataflow, /* Iterative solver. */ + df_worklist_dataflow, /* Worklist solver. */ df_lr_confluence_0, /* Confluence operator 0. */ df_lr_confluence_n, /* Confluence operator n. */ df_lr_transfer_function, /* Transfer function. */ - NULL, /* Finalize function. */ + df_lr_local_finalize, /* Finalize function. */ df_lr_free, /* Free all of the problem information. */ - df_lr_dump, /* Debugging. */ + NULL, /* Remove this problem from the stack of dataflow problems. */ + NULL, /* Debugging. */ + df_lr_top_dump, /* Debugging start block. */ + df_lr_bottom_dump, /* Debugging end block. */ + df_lr_verify_solution_start,/* Incremental solution verify start. */ + df_lr_verify_solution_end, /* Incremental solution verify end. */ NULL, /* Dependent problem. */ - 0 + TV_DF_LR /* Timing variable. */ }; @@ -1764,80 +1826,181 @@ static struct df_problem problem_LR = of DF. The returned structure is what is used to get at the solution. */ -struct dataflow * -df_lr_add_problem (struct df *df, int flags) +void +df_lr_add_problem (void) { - return df_add_problem (df, &problem_LR, flags); + df_add_problem (&problem_LR); + /* These will be initialized when df_scan_blocks processes each + block. */ + df_lr->out_of_date_transfer_functions = BITMAP_ALLOC (NULL); +} + + +/* Verify that all of the lr related info is consistent and + correct. */ + +void +df_lr_verify_transfer_functions (void) +{ + basic_block bb; + bitmap saved_def; + bitmap saved_use; + bitmap saved_adef; + bitmap saved_ause; + bitmap all_blocks; + bool need_as; + + if (!df) + return; + + saved_def = BITMAP_ALLOC (NULL); + saved_use = BITMAP_ALLOC (NULL); + saved_adef = BITMAP_ALLOC (NULL); + saved_ause = BITMAP_ALLOC (NULL); + all_blocks = BITMAP_ALLOC (NULL); + + FOR_ALL_BB (bb) + { + struct df_lr_bb_info *bb_info = df_lr_get_bb_info (bb->index); + bitmap_set_bit (all_blocks, bb->index); + + if (bb_info) + { + /* Make a copy of the transfer functions and then compute + new ones to see if the transfer functions have + changed. */ + if (!bitmap_bit_p (df_lr->out_of_date_transfer_functions, + bb->index)) + { + bitmap_copy (saved_def, bb_info->def); + bitmap_copy (saved_use, bb_info->use); + bitmap_clear (bb_info->def); + bitmap_clear (bb_info->use); + + if (bb_info->adef) + { + need_as = true; + bitmap_copy (saved_adef, bb_info->adef); + bitmap_copy (saved_ause, bb_info->ause); + bitmap_clear (bb_info->adef); + bitmap_clear (bb_info->ause); + } + else + need_as = false; + + df_lr_bb_local_compute (bb->index); + gcc_assert (bitmap_equal_p (saved_def, bb_info->def)); + gcc_assert (bitmap_equal_p (saved_use, bb_info->use)); + + if (need_as) + { + gcc_assert (bb_info->adef); + gcc_assert (bb_info->ause); + gcc_assert (bitmap_equal_p (saved_adef, bb_info->adef)); + gcc_assert (bitmap_equal_p (saved_ause, bb_info->ause)); + } + else + { + gcc_assert (!bb_info->adef); + gcc_assert (!bb_info->ause); + } + } + } + else + { + /* If we do not have basic block info, the block must be in + the list of dirty blocks or else some one has added a + block behind our backs. */ + gcc_assert (bitmap_bit_p (df_lr->out_of_date_transfer_functions, + bb->index)); + } + /* Make sure no one created a block without following + procedures. */ + gcc_assert (df_scan_get_bb_info (bb->index)); + } + + /* Make sure there are no dirty bits in blocks that have been deleted. */ + gcc_assert (!bitmap_intersect_compl_p (df_lr->out_of_date_transfer_functions, + all_blocks)); + + BITMAP_FREE (saved_def); + BITMAP_FREE (saved_use); + BITMAP_FREE (saved_adef); + BITMAP_FREE (saved_ause); + BITMAP_FREE (all_blocks); } /*---------------------------------------------------------------------------- - UNINITIALIZED REGISTERS + COMBINED LIVE REGISTERS AND UNINITIALIZED REGISTERS. - Find the set of uses for registers that are reachable from the entry - block without passing thru a definition. In and out bitvectors are built - for each basic block. The regnum is used to index into these sets. - See df.h for details. -----------------------------------------------------------------------------*/ + First find the set of uses for registers that are reachable from + the entry block without passing thru a definition. In and out + bitvectors are built for each basic block. The regnum is used to + index into these sets. See df.h for details. -/* Get basic block info. */ + Then the in and out sets here are the anded results of the in and + out sets from the lr and ur + problems. +----------------------------------------------------------------------------*/ -struct df_ur_bb_info * -df_ur_get_bb_info (struct dataflow *dflow, unsigned int index) +/* Private data used to verify the solution for this problem. */ +struct df_live_problem_data { - return (struct df_ur_bb_info *) dflow->block_info[index]; -} + bitmap *in; + bitmap *out; +}; /* Set basic block info. */ static void -df_ur_set_bb_info (struct dataflow *dflow, unsigned int index, - struct df_ur_bb_info *bb_info) +df_live_set_bb_info (unsigned int index, + struct df_live_bb_info *bb_info) { - dflow->block_info[index] = bb_info; + gcc_assert (df_live); + gcc_assert (index < df_live->block_info_size); + df_live->block_info[index] = bb_info; } /* Free basic block info. */ static void -df_ur_free_bb_info (struct dataflow *dflow, - basic_block bb ATTRIBUTE_UNUSED, +df_live_free_bb_info (basic_block bb ATTRIBUTE_UNUSED, void *vbb_info) { - struct df_ur_bb_info *bb_info = (struct df_ur_bb_info *) vbb_info; + struct df_live_bb_info *bb_info = (struct df_live_bb_info *) vbb_info; if (bb_info) { BITMAP_FREE (bb_info->gen); BITMAP_FREE (bb_info->kill); BITMAP_FREE (bb_info->in); BITMAP_FREE (bb_info->out); - pool_free (dflow->block_pool, bb_info); + pool_free (df_live->block_pool, bb_info); } } -/* Allocate or reset bitmaps for DFLOW blocks. The solution bits are +/* Allocate or reset bitmaps for DF_LIVE blocks. The solution bits are not touched unless the block is new. */ static void -df_ur_alloc (struct dataflow *dflow, bitmap blocks_to_rescan, - bitmap all_blocks ATTRIBUTE_UNUSED) +df_live_alloc (bitmap all_blocks ATTRIBUTE_UNUSED) { unsigned int bb_index; bitmap_iterator bi; - if (!dflow->block_pool) - dflow->block_pool = create_alloc_pool ("df_ur_block pool", - sizeof (struct df_ur_bb_info), 100); + if (!df_live->block_pool) + df_live->block_pool = create_alloc_pool ("df_live_block pool", + sizeof (struct df_live_bb_info), 100); - df_grow_bb_info (dflow); + df_grow_bb_info (df_live); - EXECUTE_IF_SET_IN_BITMAP (blocks_to_rescan, 0, bb_index, bi) + EXECUTE_IF_SET_IN_BITMAP (df_live->out_of_date_transfer_functions, 0, bb_index, bi) { - struct df_ur_bb_info *bb_info = df_ur_get_bb_info (dflow, bb_index); + struct df_live_bb_info *bb_info = df_live_get_bb_info (bb_index); if (bb_info) { bitmap_clear (bb_info->kill); @@ -1845,8 +2008,8 @@ df_ur_alloc (struct dataflow *dflow, bitmap blocks_to_rescan, } else { - bb_info = (struct df_ur_bb_info *) pool_alloc (dflow->block_pool); - df_ur_set_bb_info (dflow, bb_index, bb_info); + bb_info = (struct df_live_bb_info *) pool_alloc (df_live->block_pool); + df_live_set_bb_info (bb_index, bb_info); bb_info->kill = BITMAP_ALLOC (NULL); bb_info->gen = BITMAP_ALLOC (NULL); bb_info->in = BITMAP_ALLOC (NULL); @@ -1856,160 +2019,132 @@ df_ur_alloc (struct dataflow *dflow, bitmap blocks_to_rescan, } +/* Reset the global solution for recalculation. */ + +static void +df_live_reset (bitmap all_blocks) +{ + unsigned int bb_index; + bitmap_iterator bi; + + EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) + { + struct df_lr_bb_info *bb_info = df_lr_get_bb_info (bb_index); + gcc_assert (bb_info); + bitmap_clear (bb_info->in); + bitmap_clear (bb_info->out); + } +} + + /* Compute local uninitialized register info for basic block BB. */ static void -df_ur_bb_local_compute (struct dataflow *dflow, unsigned int bb_index) +df_live_bb_local_compute (unsigned int bb_index) { - struct df *df = dflow->df; basic_block bb = BASIC_BLOCK (bb_index); - struct df_ur_bb_info *bb_info = df_ur_get_bb_info (dflow, bb_index); + struct df_live_bb_info *bb_info = df_live_get_bb_info (bb_index); rtx insn; - struct df_ref *def; - - bitmap_clear (seen_in_block); - bitmap_clear (seen_in_insn); + struct df_ref **def_rec; + int luid = 0; - for (def = df_get_artificial_defs (df, bb_index); def; def = def->next_ref) - if ((DF_REF_FLAGS (def) & DF_REF_AT_TOP) == 0) - { - unsigned int regno = DF_REF_REGNO (def); - if (!bitmap_bit_p (seen_in_block, regno)) - { - bitmap_set_bit (seen_in_block, regno); - bitmap_set_bit (bb_info->gen, regno); - } - } + for (def_rec = df_get_artificial_defs (bb_index); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if (DF_REF_FLAGS (def) & DF_REF_AT_TOP) + bitmap_set_bit (bb_info->gen, DF_REF_REGNO (def)); + } - FOR_BB_INSNS_REVERSE (bb, insn) + FOR_BB_INSNS (bb, insn) { unsigned int uid = INSN_UID (insn); + struct df_insn_info *insn_info = DF_INSN_UID_GET (uid); + + /* Inserting labels does not always trigger the incremental + rescanning. */ + if (!insn_info) + { + gcc_assert (!INSN_P (insn)); + df_insn_create_insn_record (insn); + } + + DF_INSN_LUID (insn) = luid; if (!INSN_P (insn)) continue; - for (def = DF_INSN_UID_DEFS (df, uid); def; def = def->next_ref) + luid++; + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) { + struct df_ref *def = *def_rec; unsigned int regno = DF_REF_REGNO (def); - /* Only the last def counts. */ - if (!bitmap_bit_p (seen_in_block, regno)) - { - bitmap_set_bit (seen_in_insn, regno); - - if (DF_REF_FLAGS (def) - & (DF_REF_MUST_CLOBBER | DF_REF_MAY_CLOBBER)) - { - /* Only must clobbers for the entire reg destroy the - value. */ - if ((DF_REF_FLAGS (def) & DF_REF_MUST_CLOBBER) - && (!DF_REF_FLAGS (def) & DF_REF_PARTIAL)) - bitmap_set_bit (bb_info->kill, regno); - } - else - bitmap_set_bit (bb_info->gen, regno); - } + + if (DF_REF_FLAGS_IS_SET (def, + DF_REF_PARTIAL | DF_REF_CONDITIONAL)) + /* All partial or conditional def + seen are included in the gen set. */ + bitmap_set_bit (bb_info->gen, regno); + else if (DF_REF_FLAGS_IS_SET (def, DF_REF_MUST_CLOBBER)) + /* Only must clobbers for the entire reg destroy the + value. */ + bitmap_set_bit (bb_info->kill, regno); + else if (! DF_REF_FLAGS_IS_SET (def, DF_REF_MAY_CLOBBER)) + bitmap_set_bit (bb_info->gen, regno); } - bitmap_ior_into (seen_in_block, seen_in_insn); - bitmap_clear (seen_in_insn); } - for (def = df_get_artificial_defs (df, bb_index); def; def = def->next_ref) - if (DF_REF_FLAGS (def) & DF_REF_AT_TOP) - { - unsigned int regno = DF_REF_REGNO (def); - if (!bitmap_bit_p (seen_in_block, regno)) - { - bitmap_set_bit (seen_in_block, regno); - bitmap_set_bit (bb_info->gen, regno); - } - } + for (def_rec = df_get_artificial_defs (bb_index); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if ((DF_REF_FLAGS (def) & DF_REF_AT_TOP) == 0) + bitmap_set_bit (bb_info->gen, DF_REF_REGNO (def)); + } } /* Compute local uninitialized register info. */ static void -df_ur_local_compute (struct dataflow *dflow, - bitmap all_blocks ATTRIBUTE_UNUSED, - bitmap rescan_blocks) +df_live_local_compute (bitmap all_blocks ATTRIBUTE_UNUSED) { unsigned int bb_index; bitmap_iterator bi; - df_set_seen (); + df_grow_insn_info (); - EXECUTE_IF_SET_IN_BITMAP (rescan_blocks, 0, bb_index, bi) + EXECUTE_IF_SET_IN_BITMAP (df_live->out_of_date_transfer_functions, + 0, bb_index, bi) { - df_ur_bb_local_compute (dflow, bb_index); + df_live_bb_local_compute (bb_index); } - df_unset_seen (); + bitmap_clear (df_live->out_of_date_transfer_functions); } /* Initialize the solution vectors. */ static void -df_ur_init (struct dataflow *dflow, bitmap all_blocks) +df_live_init (bitmap all_blocks) { unsigned int bb_index; bitmap_iterator bi; EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) { - struct df_ur_bb_info *bb_info = df_ur_get_bb_info (dflow, bb_index); + struct df_live_bb_info *bb_info = df_live_get_bb_info (bb_index); bitmap_copy (bb_info->out, bb_info->gen); bitmap_clear (bb_info->in); } } - -/* Or in the stack regs, hard regs and early clobber regs into the - ur_in sets of all of the blocks. */ - -static void -df_ur_local_finalize (struct dataflow *dflow, bitmap all_blocks) -{ - struct df *df = dflow->df; - struct dataflow *lr_dflow = df->problems_by_index[DF_LR]; - bitmap tmp = BITMAP_ALLOC (NULL); - bitmap_iterator bi; - unsigned int bb_index; - - EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) - { - struct df_ur_bb_info *bb_info = df_ur_get_bb_info (dflow, bb_index); - struct df_lr_bb_info *bb_lr_info = df_lr_get_bb_info (lr_dflow, bb_index); - - /* No register may reach a location where it is not used. Thus - we trim the rr result to the places where it is used. */ - bitmap_and_into (bb_info->in, bb_lr_info->in); - bitmap_and_into (bb_info->out, bb_lr_info->out); - -#if 1 - /* Hard registers may still stick in the ur_out set, but not - be in the ur_in set, if their only mention was in a call - in this block. This is because a call kills in the lr - problem but does not kill in the ur problem. To clean - this up, we execute the transfer function on the lr_in - set and then use that to knock bits out of ur_out. */ - bitmap_ior_and_compl (tmp, bb_info->gen, bb_lr_info->in, - bb_info->kill); - bitmap_and_into (bb_info->out, tmp); -#endif - } - - BITMAP_FREE (tmp); -} - - /* Confluence function that ignores fake edges. */ static void -df_ur_confluence_n (struct dataflow *dflow, edge e) +df_live_confluence_n (edge e) { - bitmap op1 = df_ur_get_bb_info (dflow, e->dest->index)->in; - bitmap op2 = df_ur_get_bb_info (dflow, e->src->index)->out; + bitmap op1 = df_live_get_bb_info (e->dest->index)->in; + bitmap op2 = df_live_get_bb_info (e->src->index)->out; if (e->flags & EDGE_FAKE) return; @@ -2021,9 +2156,9 @@ df_ur_confluence_n (struct dataflow *dflow, edge e) /* Transfer function. */ static bool -df_ur_transfer_function (struct dataflow *dflow, int bb_index) +df_live_transfer_function (int bb_index) { - struct df_ur_bb_info *bb_info = df_ur_get_bb_info (dflow, bb_index); + struct df_live_bb_info *bb_info = df_live_get_bb_info (bb_index); bitmap in = bb_info->in; bitmap out = bb_info->out; bitmap gen = bb_info->gen; @@ -2033,18 +2168,45 @@ df_ur_transfer_function (struct dataflow *dflow, int bb_index) } +/* And the LR and UR info to produce the LIVE info. */ + +static void +df_live_local_finalize (bitmap all_blocks) +{ + + if (df_live->solutions_dirty) + { + bitmap_iterator bi; + unsigned int bb_index; + + EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) + { + struct df_lr_bb_info *bb_lr_info = df_lr_get_bb_info (bb_index); + struct df_live_bb_info *bb_live_info = df_live_get_bb_info (bb_index); + + /* No register may reach a location where it is not used. Thus + we trim the rr result to the places where it is used. */ + bitmap_and_into (bb_live_info->in, bb_lr_info->in); + bitmap_and_into (bb_live_info->out, bb_lr_info->out); + } + + df_live->solutions_dirty = false; + } +} + + /* Free all storage associated with the problem. */ static void -df_ur_free (struct dataflow *dflow) +df_live_free (void) { - if (dflow->block_info) + if (df_live->block_info) { unsigned int i; - for (i = 0; i < dflow->block_info_size; i++) + for (i = 0; i < df_live->block_info_size; i++) { - struct df_ur_bb_info *bb_info = df_ur_get_bb_info (dflow, i); + struct df_live_bb_info *bb_info = df_live_get_bb_info (i); if (bb_info) { BITMAP_FREE (bb_info->gen); @@ -2054,65 +2216,159 @@ df_ur_free (struct dataflow *dflow) } } - free_alloc_pool (dflow->block_pool); - dflow->block_info_size = 0; - free (dflow->block_info); + free_alloc_pool (df_live->block_pool); + df_live->block_info_size = 0; + free (df_live->block_info); } - free (dflow); + BITMAP_FREE (df_live->out_of_date_transfer_functions); + free (df_live); } -/* Debugging info. */ +/* Debugging info at top of bb. */ static void -df_ur_dump (struct dataflow *dflow, FILE *file) +df_live_top_dump (basic_block bb, FILE *file) +{ + struct df_live_bb_info *bb_info = df_live_get_bb_info (bb->index); + struct df_live_problem_data *problem_data; + + if (!bb_info || !bb_info->in) + return; + + fprintf (file, ";; live in \t"); + df_print_regset (file, bb_info->in); + if (df_live->problem_data) + { + problem_data = (struct df_live_problem_data *)df_live->problem_data; + fprintf (file, ";; old in \t"); + df_print_regset (file, problem_data->in[bb->index]); + } + fprintf (file, ";; live gen \t"); + df_print_regset (file, bb_info->gen); + fprintf (file, ";; live kill\t"); + df_print_regset (file, bb_info->kill); +} + + +/* Debugging info at bottom of bb. */ + +static void +df_live_bottom_dump (basic_block bb, FILE *file) +{ + struct df_live_bb_info *bb_info = df_live_get_bb_info (bb->index); + struct df_live_problem_data *problem_data; + + if (!bb_info || !bb_info->out) + return; + + fprintf (file, ";; live out \t"); + df_print_regset (file, bb_info->out); + if (df_live->problem_data) + { + problem_data = (struct df_live_problem_data *)df_live->problem_data; + fprintf (file, ";; old out \t"); + df_print_regset (file, problem_data->out[bb->index]); + } +} + + +/* Build the datastructure to verify that the solution to the dataflow + equations is not dirty. */ + +static void +df_live_verify_solution_start (void) { basic_block bb; - - if (!dflow->block_info) + struct df_live_problem_data *problem_data; + if (df_live->solutions_dirty) + { + df_live->problem_data = NULL; + return; + } + + /* Set it true so that the solution is recomputed. */ + df_live->solutions_dirty = true; + + problem_data = XNEW (struct df_live_problem_data); + df_live->problem_data = problem_data; + problem_data->in = XNEWVEC (bitmap, last_basic_block); + problem_data->out = XNEWVEC (bitmap, last_basic_block); + + FOR_ALL_BB (bb) + { + problem_data->in[bb->index] = BITMAP_ALLOC (NULL); + problem_data->out[bb->index] = BITMAP_ALLOC (NULL); + bitmap_copy (problem_data->in[bb->index], DF_LIVE_IN (bb)); + bitmap_copy (problem_data->out[bb->index], DF_LIVE_OUT (bb)); + } +} + + +/* Compare the saved datastructure and the new solution to the dataflow + equations. */ + +static void +df_live_verify_solution_end (void) +{ + struct df_live_problem_data *problem_data; + basic_block bb; + + if (df_live->problem_data == NULL) return; - fprintf (file, "Undefined regs:\n"); - + problem_data = (struct df_live_problem_data *)df_live->problem_data; + FOR_ALL_BB (bb) { - struct df_ur_bb_info *bb_info = df_ur_get_bb_info (dflow, bb->index); - df_print_bb_index (bb, file); - - if (!bb_info->in) - continue; - - fprintf (file, " in \t"); - dump_bitmap (file, bb_info->in); - fprintf (file, " gen \t"); - dump_bitmap (file, bb_info->gen); - fprintf (file, " kill\t"); - dump_bitmap (file, bb_info->kill); - fprintf (file, " out \t"); - dump_bitmap (file, bb_info->out); + if ((!bitmap_equal_p (problem_data->in[bb->index], DF_LIVE_IN (bb))) + || (!bitmap_equal_p (problem_data->out[bb->index], DF_LIVE_OUT (bb)))) + { + /*df_dump (stderr);*/ + gcc_unreachable (); + } + } + + /* Cannot delete them immediately because you may want to dump them + if the comparison fails. */ + FOR_ALL_BB (bb) + { + BITMAP_FREE (problem_data->in[bb->index]); + BITMAP_FREE (problem_data->out[bb->index]); } + + free (problem_data->in); + free (problem_data->out); + free (problem_data); + df_live->problem_data = NULL; } + /* All of the information associated with every instance of the problem. */ -static struct df_problem problem_UR = +static struct df_problem problem_LIVE = { - DF_UR, /* Problem id. */ - DF_FORWARD, /* Direction. */ - df_ur_alloc, /* Allocate the problem specific data. */ - NULL, /* Reset global information. */ - df_ur_free_bb_info, /* Free basic block info. */ - df_ur_local_compute, /* Local compute function. */ - df_ur_init, /* Init the solution specific data. */ - df_iterative_dataflow, /* Iterative solver. */ - NULL, /* Confluence operator 0. */ - df_ur_confluence_n, /* Confluence operator n. */ - df_ur_transfer_function, /* Transfer function. */ - df_ur_local_finalize, /* Finalize function. */ - df_ur_free, /* Free all of the problem information. */ - df_ur_dump, /* Debugging. */ - df_lr_add_problem, /* Dependent problem. */ - 0 /* Changeable flags. */ + DF_LIVE, /* Problem id. */ + DF_FORWARD, /* Direction. */ + df_live_alloc, /* Allocate the problem specific data. */ + df_live_reset, /* Reset global information. */ + df_live_free_bb_info, /* Free basic block info. */ + df_live_local_compute, /* Local compute function. */ + df_live_init, /* Init the solution specific data. */ + df_worklist_dataflow, /* Worklist solver. */ + NULL, /* Confluence operator 0. */ + df_live_confluence_n, /* Confluence operator n. */ + df_live_transfer_function, /* Transfer function. */ + df_live_local_finalize, /* Finalize function. */ + df_live_free, /* Free all of the problem information. */ + df_live_free, /* Remove this problem from the stack of dataflow problems. */ + NULL, /* Debugging. */ + df_live_top_dump, /* Debugging start block. */ + df_live_bottom_dump, /* Debugging end block. */ + df_live_verify_solution_start,/* Incremental solution verify start. */ + df_live_verify_solution_end, /* Incremental solution verify end. */ + &problem_LR, /* Dependent problem. */ + TV_DF_LIVE /* Timing variable. */ }; @@ -2120,10 +2376,78 @@ static struct df_problem problem_UR = of DF. The returned structure is what is used to get at the solution. */ -struct dataflow * -df_ur_add_problem (struct df *df, int flags) +void +df_live_add_problem (void) { - return df_add_problem (df, &problem_UR, flags); + df_add_problem (&problem_LIVE); + /* These will be initialized when df_scan_blocks processes each + block. */ + df_live->out_of_date_transfer_functions = BITMAP_ALLOC (NULL); +} + + +/* Verify that all of the lr related info is consistent and + correct. */ + +void +df_live_verify_transfer_functions (void) +{ + basic_block bb; + bitmap saved_gen; + bitmap saved_kill; + bitmap all_blocks; + + if (!df) + return; + + saved_gen = BITMAP_ALLOC (NULL); + saved_kill = BITMAP_ALLOC (NULL); + all_blocks = BITMAP_ALLOC (NULL); + + df_grow_insn_info (); + + FOR_ALL_BB (bb) + { + struct df_live_bb_info *bb_info = df_live_get_bb_info (bb->index); + bitmap_set_bit (all_blocks, bb->index); + + if (bb_info) + { + /* Make a copy of the transfer functions and then compute + new ones to see if the transfer functions have + changed. */ + if (!bitmap_bit_p (df_live->out_of_date_transfer_functions, + bb->index)) + { + bitmap_copy (saved_gen, bb_info->gen); + bitmap_copy (saved_kill, bb_info->kill); + bitmap_clear (bb_info->gen); + bitmap_clear (bb_info->kill); + + df_live_bb_local_compute (bb->index); + gcc_assert (bitmap_equal_p (saved_gen, bb_info->gen)); + gcc_assert (bitmap_equal_p (saved_kill, bb_info->kill)); + } + } + else + { + /* If we do not have basic block info, the block must be in + the list of dirty blocks or else some one has added a + block behind our backs. */ + gcc_assert (bitmap_bit_p (df_live->out_of_date_transfer_functions, + bb->index)); + } + /* Make sure no one created a block without following + procedures. */ + gcc_assert (df_scan_get_bb_info (bb->index)); + } + + /* Make sure there are no dirty bits in blocks that have been deleted. */ + gcc_assert (!bitmap_intersect_compl_p (df_live->out_of_date_transfer_functions, + all_blocks)); + BITMAP_FREE (saved_gen); + BITMAP_FREE (saved_kill); + BITMAP_FREE (all_blocks); } @@ -2154,30 +2478,22 @@ struct df_urec_problem_data }; -/* Get basic block info. */ - -struct df_urec_bb_info * -df_urec_get_bb_info (struct dataflow *dflow, unsigned int index) -{ - return (struct df_urec_bb_info *) dflow->block_info[index]; -} - - /* Set basic block info. */ static void -df_urec_set_bb_info (struct dataflow *dflow, unsigned int index, - struct df_urec_bb_info *bb_info) +df_urec_set_bb_info (unsigned int index, + struct df_urec_bb_info *bb_info) { - dflow->block_info[index] = bb_info; + gcc_assert (df_urec); + gcc_assert (index < df_urec->block_info_size); + df_urec->block_info[index] = bb_info; } /* Free basic block info. */ static void -df_urec_free_bb_info (struct dataflow *dflow, - basic_block bb ATTRIBUTE_UNUSED, +df_urec_free_bb_info (basic_block bb ATTRIBUTE_UNUSED, void *vbb_info) { struct df_urec_bb_info *bb_info = (struct df_urec_bb_info *) vbb_info; @@ -2188,40 +2504,39 @@ df_urec_free_bb_info (struct dataflow *dflow, BITMAP_FREE (bb_info->in); BITMAP_FREE (bb_info->out); BITMAP_FREE (bb_info->earlyclobber); - pool_free (dflow->block_pool, bb_info); + pool_free (df_urec->block_pool, bb_info); } } -/* Allocate or reset bitmaps for DFLOW blocks. The solution bits are +/* Allocate or reset bitmaps for DF_UREC blocks. The solution bits are not touched unless the block is new. */ static void -df_urec_alloc (struct dataflow *dflow, bitmap blocks_to_rescan, - bitmap all_blocks ATTRIBUTE_UNUSED) +df_urec_alloc (bitmap all_blocks) { unsigned int bb_index; bitmap_iterator bi; struct df_urec_problem_data *problem_data - = (struct df_urec_problem_data *) dflow->problem_data; + = (struct df_urec_problem_data *) df_urec->problem_data; - if (!dflow->block_pool) - dflow->block_pool = create_alloc_pool ("df_urec_block pool", + if (!df_urec->block_pool) + df_urec->block_pool = create_alloc_pool ("df_urec_block pool", sizeof (struct df_urec_bb_info), 50); - if (!dflow->problem_data) + if (!df_urec->problem_data) { problem_data = XNEW (struct df_urec_problem_data); - dflow->problem_data = problem_data; + df_urec->problem_data = problem_data; } problem_data->earlyclobbers_found = false; - df_grow_bb_info (dflow); + df_grow_bb_info (df_urec); - EXECUTE_IF_SET_IN_BITMAP (blocks_to_rescan, 0, bb_index, bi) + EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) { - struct df_urec_bb_info *bb_info = df_urec_get_bb_info (dflow, bb_index); + struct df_urec_bb_info *bb_info = df_urec_get_bb_info (bb_index); if (bb_info) { bitmap_clear (bb_info->kill); @@ -2230,12 +2545,13 @@ df_urec_alloc (struct dataflow *dflow, bitmap blocks_to_rescan, } else { - bb_info = (struct df_urec_bb_info *) pool_alloc (dflow->block_pool); - df_urec_set_bb_info (dflow, bb_index, bb_info); + bb_info = (struct df_urec_bb_info *) pool_alloc (df_urec->block_pool); + df_urec_set_bb_info (bb_index, bb_info); bb_info->kill = BITMAP_ALLOC (NULL); bb_info->gen = BITMAP_ALLOC (NULL); bb_info->in = BITMAP_ALLOC (NULL); bb_info->out = BITMAP_ALLOC (NULL); + bb_info->top = BITMAP_ALLOC (NULL); bb_info->earlyclobber = BITMAP_ALLOC (NULL); } } @@ -2433,20 +2749,22 @@ df_urec_mark_reg_use_for_earlyclobber_1 (rtx *x, void *data) /* Compute local uninitialized register info for basic block BB. */ static void -df_urec_bb_local_compute (struct dataflow *dflow, unsigned int bb_index) +df_urec_bb_local_compute (unsigned int bb_index) { - struct df *df = dflow->df; basic_block bb = BASIC_BLOCK (bb_index); - struct df_urec_bb_info *bb_info = df_urec_get_bb_info (dflow, bb_index); + struct df_urec_bb_info *bb_info = df_urec_get_bb_info (bb_index); rtx insn; - struct df_ref *def; + struct df_ref **def_rec; - for (def = df_get_artificial_defs (df, bb_index); def; def = def->next_ref) - if (DF_REF_FLAGS (def) & DF_REF_AT_TOP) - { - unsigned int regno = DF_REF_REGNO (def); - bitmap_set_bit (bb_info->gen, regno); - } + for (def_rec = df_get_artificial_defs (bb_index); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if (DF_REF_FLAGS (def) & DF_REF_AT_TOP) + { + unsigned int regno = DF_REF_REGNO (def); + bitmap_set_bit (bb_info->gen, regno); + } + } FOR_BB_INSNS (bb, insn) { @@ -2456,7 +2774,7 @@ df_urec_bb_local_compute (struct dataflow *dflow, unsigned int bb_index) if (df_urec_check_earlyclobber (insn)) { struct df_urec_problem_data *problem_data - = (struct df_urec_problem_data *) dflow->problem_data; + = (struct df_urec_problem_data *) df_urec->problem_data; problem_data->earlyclobbers_found = true; note_uses (&PATTERN (insn), df_urec_mark_reg_use_for_earlyclobber_1, bb_info); @@ -2464,22 +2782,22 @@ df_urec_bb_local_compute (struct dataflow *dflow, unsigned int bb_index) } } - for (def = df_get_artificial_defs (df, bb_index); def; def = def->next_ref) - if ((DF_REF_FLAGS (def) & DF_REF_AT_TOP) == 0) - { - unsigned int regno = DF_REF_REGNO (def); - bitmap_set_bit (bb_info->gen, regno); - } - + for (def_rec = df_get_artificial_defs (bb_index); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if ((DF_REF_FLAGS (def) & DF_REF_AT_TOP) == 0) + { + unsigned int regno = DF_REF_REGNO (def); + bitmap_set_bit (bb_info->gen, regno); + } + } } /* Compute local uninitialized register info. */ static void -df_urec_local_compute (struct dataflow *dflow, - bitmap all_blocks ATTRIBUTE_UNUSED, - bitmap rescan_blocks) +df_urec_local_compute (bitmap all_blocks) { unsigned int bb_index; bitmap_iterator bi; @@ -2487,7 +2805,7 @@ df_urec_local_compute (struct dataflow *dflow, int i; HARD_REG_SET stack_hard_regs, used; struct df_urec_problem_data *problem_data - = (struct df_urec_problem_data *) dflow->problem_data; + = (struct df_urec_problem_data *) df_urec->problem_data; /* Any register that MAY be allocated to a register stack (like the 387) is treated poorly. Each such register is marked as being @@ -2514,9 +2832,9 @@ df_urec_local_compute (struct dataflow *dflow, N_REG_CLASSES elements. See df_urec_check_earlyclobber. */ earlyclobber_regclass = VEC_alloc (int, heap, N_REG_CLASSES); - EXECUTE_IF_SET_IN_BITMAP (rescan_blocks, 0, bb_index, bi) + EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) { - df_urec_bb_local_compute (dflow, bb_index); + df_urec_bb_local_compute (bb_index); } VEC_free (int, heap, earlyclobber_regclass); @@ -2526,14 +2844,14 @@ df_urec_local_compute (struct dataflow *dflow, /* Initialize the solution vectors. */ static void -df_urec_init (struct dataflow *dflow, bitmap all_blocks) +df_urec_init (bitmap all_blocks) { unsigned int bb_index; bitmap_iterator bi; EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) { - struct df_urec_bb_info *bb_info = df_urec_get_bb_info (dflow, bb_index); + struct df_urec_bb_info *bb_info = df_urec_get_bb_info (bb_index); bitmap_copy (bb_info->out, bb_info->gen); bitmap_clear (bb_info->in); @@ -2542,23 +2860,22 @@ df_urec_init (struct dataflow *dflow, bitmap all_blocks) /* Or in the stack regs, hard regs and early clobber regs into the - ur_in sets of all of the blocks. */ + urec_in sets of all of the blocks. */ + static void -df_urec_local_finalize (struct dataflow *dflow, bitmap all_blocks) +df_urec_local_finalize (bitmap all_blocks) { - struct df *df = dflow->df; - struct dataflow *lr_dflow = df->problems_by_index[DF_LR]; bitmap tmp = BITMAP_ALLOC (NULL); bitmap_iterator bi; unsigned int bb_index; struct df_urec_problem_data *problem_data - = (struct df_urec_problem_data *) dflow->problem_data; + = (struct df_urec_problem_data *) df_urec->problem_data; EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) { - struct df_urec_bb_info *bb_info = df_urec_get_bb_info (dflow, bb_index); - struct df_lr_bb_info *bb_lr_info = df_lr_get_bb_info (lr_dflow, bb_index); + struct df_urec_bb_info *bb_info = df_urec_get_bb_info (bb_index); + struct df_lr_bb_info *bb_lr_info = df_lr_get_bb_info (bb_index); if (bb_index != ENTRY_BLOCK && bb_index != EXIT_BLOCK) { @@ -2580,8 +2897,11 @@ df_urec_local_finalize (struct dataflow *dflow, bitmap all_blocks) we trim the rr result to the places where it is used. */ bitmap_and_into (bb_info->in, bb_lr_info->in); bitmap_and_into (bb_info->out, bb_lr_info->out); - -#if 1 + bitmap_copy (bb_info->top, bb_info->in); + if (bb_lr_info->adef) + bitmap_ior_into (bb_info->top, bb_lr_info->adef); + bitmap_and_into (bb_info->top, bb_lr_info->top); +#if 0 /* Hard registers may still stick in the ur_out set, but not be in the ur_in set, if their only mention was in a call in this block. This is because a call kills in the lr @@ -2604,10 +2924,10 @@ df_urec_local_finalize (struct dataflow *dflow, bitmap all_blocks) /* Confluence function that ignores fake edges. */ static void -df_urec_confluence_n (struct dataflow *dflow, edge e) +df_urec_confluence_n (edge e) { - bitmap op1 = df_urec_get_bb_info (dflow, e->dest->index)->in; - bitmap op2 = df_urec_get_bb_info (dflow, e->src->index)->out; + bitmap op1 = df_urec_get_bb_info (e->dest->index)->in; + bitmap op2 = df_urec_get_bb_info (e->src->index)->out; if (e->flags & EDGE_FAKE) return; @@ -2619,9 +2939,9 @@ df_urec_confluence_n (struct dataflow *dflow, edge e) /* Transfer function. */ static bool -df_urec_transfer_function (struct dataflow *dflow, int bb_index) +df_urec_transfer_function (int bb_index) { - struct df_urec_bb_info *bb_info = df_urec_get_bb_info (dflow, bb_index); + struct df_urec_bb_info *bb_info = df_urec_get_bb_info (bb_index); bitmap in = bb_info->in; bitmap out = bb_info->out; bitmap gen = bb_info->gen; @@ -2634,15 +2954,15 @@ df_urec_transfer_function (struct dataflow *dflow, int bb_index) /* Free all storage associated with the problem. */ static void -df_urec_free (struct dataflow *dflow) +df_urec_free (void) { - if (dflow->block_info) + if (df_urec->block_info) { unsigned int i; - for (i = 0; i < dflow->block_info_size; i++) + for (i = 0; i < df_urec->block_info_size; i++) { - struct df_urec_bb_info *bb_info = df_urec_get_bb_info (dflow, i); + struct df_urec_bb_info *bb_info = df_urec_get_bb_info (i); if (bb_info) { BITMAP_FREE (bb_info->gen); @@ -2650,52 +2970,53 @@ df_urec_free (struct dataflow *dflow) BITMAP_FREE (bb_info->in); BITMAP_FREE (bb_info->out); BITMAP_FREE (bb_info->earlyclobber); + BITMAP_FREE (bb_info->top); } } - free_alloc_pool (dflow->block_pool); + free_alloc_pool (df_urec->block_pool); - dflow->block_info_size = 0; - free (dflow->block_info); - free (dflow->problem_data); + df_urec->block_info_size = 0; + free (df_urec->block_info); + free (df_urec->problem_data); } - free (dflow); + free (df_urec); } -/* Debugging info. */ +/* Debugging info at top of bb. */ static void -df_urec_dump (struct dataflow *dflow, FILE *file) +df_urec_top_dump (basic_block bb, FILE *file) { - basic_block bb; - - if (!dflow->block_info) + struct df_urec_bb_info *bb_info = df_urec_get_bb_info (bb->index); + if (!bb_info || !bb_info->in) return; - - fprintf (file, "Undefined regs:\n"); - - FOR_ALL_BB (bb) - { - struct df_urec_bb_info *bb_info = df_urec_get_bb_info (dflow, bb->index); - df_print_bb_index (bb, file); - - if (!bb_info->in) - continue; - fprintf (file, " in \t"); - dump_bitmap (file, bb_info->in); - fprintf (file, " gen \t"); - dump_bitmap (file, bb_info->gen); - fprintf (file, " kill\t"); - dump_bitmap (file, bb_info->kill); - fprintf (file, " ec\t"); - dump_bitmap (file, bb_info->earlyclobber); - fprintf (file, " out \t"); - dump_bitmap (file, bb_info->out); - } + fprintf (file, ";; urec in \t"); + df_print_regset (file, bb_info->in); + fprintf (file, ";; urec gen \t"); + df_print_regset (file, bb_info->gen); + fprintf (file, ";; urec kill\t"); + df_print_regset (file, bb_info->kill); + fprintf (file, ";; urec ec\t"); + df_print_regset (file, bb_info->earlyclobber); } + +/* Debugging info at bottom of bb. */ + +static void +df_urec_bottom_dump (basic_block bb, FILE *file) +{ + struct df_urec_bb_info *bb_info = df_urec_get_bb_info (bb->index); + if (!bb_info || !bb_info->out) + return; + fprintf (file, ";; urec out \t"); + df_print_regset (file, bb_info->out); +} + + /* All of the information associated with every instance of the problem. */ static struct df_problem problem_UREC = @@ -2707,15 +3028,20 @@ static struct df_problem problem_UREC = df_urec_free_bb_info, /* Free basic block info. */ df_urec_local_compute, /* Local compute function. */ df_urec_init, /* Init the solution specific data. */ - df_iterative_dataflow, /* Iterative solver. */ + df_worklist_dataflow, /* Worklist solver. */ NULL, /* Confluence operator 0. */ df_urec_confluence_n, /* Confluence operator n. */ df_urec_transfer_function, /* Transfer function. */ df_urec_local_finalize, /* Finalize function. */ df_urec_free, /* Free all of the problem information. */ - df_urec_dump, /* Debugging. */ - df_lr_add_problem, /* Dependent problem. */ - 0 /* Changeable flags. */ + df_urec_free, /* Remove this problem from the stack of dataflow problems. */ + NULL, /* Debugging. */ + df_urec_top_dump, /* Debugging start block. */ + df_urec_bottom_dump, /* Debugging end block. */ + NULL, /* Incremental solution verify start. */ + NULL, /* Incremental solution verfiy end. */ + &problem_LR, /* Dependent problem. */ + TV_DF_UREC /* Timing variable. */ }; @@ -2723,10 +3049,10 @@ static struct df_problem problem_UREC = of DF. The returned structure is what is used to get at the solution. */ -struct dataflow * -df_urec_add_problem (struct df *df, int flags) +void +df_urec_add_problem (void) { - return df_add_problem (df, &problem_UREC, flags); + df_add_problem (&problem_UREC); } @@ -2742,212 +3068,223 @@ df_urec_add_problem (struct df *df, int flags) the reaching defs information (the dependent problem). ----------------------------------------------------------------------------*/ -/* Create def-use or use-def chains. */ +#define df_chain_problem_p(FLAG) (((enum df_chain_flags)df_chain->local_flags)&(FLAG)) -static void -df_chain_alloc (struct dataflow *dflow, - bitmap blocks_to_rescan ATTRIBUTE_UNUSED, - bitmap all_blocks ATTRIBUTE_UNUSED) +/* Create a du or ud chain from SRC to DST and link it into SRC. */ +struct df_link * +df_chain_create (struct df_ref *src, struct df_ref *dst) { - struct df *df = dflow->df; - unsigned int i; + struct df_link *head = DF_REF_CHAIN (src); + struct df_link *link = pool_alloc (df_chain->block_pool);; + + DF_REF_CHAIN (src) = link; + link->next = head; + link->ref = dst; + return link; +} - /* Wholesale destruction of the old chains. */ - if (dflow->block_pool) - free_alloc_pool (dflow->block_pool); - dflow->block_pool = create_alloc_pool ("df_chain_chain_block pool", - sizeof (struct df_link), 100); +/* Delete any du or ud chains that start at REF and point to + TARGET. */ +static void +df_chain_unlink_1 (struct df_ref *ref, struct df_ref *target) +{ + struct df_link *chain = DF_REF_CHAIN (ref); + struct df_link *prev = NULL; - if (dflow->flags & DF_DU_CHAIN) + while (chain) { - df_reorganize_refs (&df->def_info); - - /* Clear out the pointers from the refs. */ - for (i = 0; i < DF_DEFS_SIZE (df); i++) + if (chain->ref == target) { - struct df_ref *ref = df->def_info.refs[i]; - DF_REF_CHAIN (ref) = NULL; + if (prev) + prev->next = chain->next; + else + DF_REF_CHAIN (ref) = chain->next; + pool_free (df_chain->block_pool, chain); + return; } + prev = chain; + chain = chain->next; } - - if (dflow->flags & DF_UD_CHAIN) +} + + +/* Delete a du or ud chain that leave or point to REF. */ + +void +df_chain_unlink (struct df_ref *ref) +{ + struct df_link *chain = DF_REF_CHAIN (ref); + while (chain) { - df_reorganize_refs (&df->use_info); - for (i = 0; i < DF_USES_SIZE (df); i++) - { - struct df_ref *ref = df->use_info.refs[i]; - DF_REF_CHAIN (ref) = NULL; - } + struct df_link *next = chain->next; + /* Delete the other side if it exists. */ + df_chain_unlink_1 (chain->ref, ref); + pool_free (df_chain->block_pool, chain); + chain = next; } + DF_REF_CHAIN (ref) = NULL; } -/* Reset all def_use and use_def chains in INSN. */ +/* Copy the du or ud chain starting at FROM_REF and attach it to + TO_REF. */ -static void -df_chain_insn_reset (struct dataflow *dflow, rtx insn) +void +df_chain_copy (struct df_ref *to_ref, + struct df_link *from_ref) { - struct df *df = dflow->df; - unsigned int uid = INSN_UID (insn); - struct df_insn_info *insn_info = NULL; - struct df_ref *ref; + while (from_ref) + { + df_chain_create (to_ref, from_ref->ref); + from_ref = from_ref->next; + } +} - if (uid < df->insns_size) - insn_info = DF_INSN_UID_GET (df, uid); - if (insn_info) - { - if (dflow->flags & DF_DU_CHAIN) - { - ref = insn_info->defs; - while (ref) - { - ref->chain = NULL; - ref = ref->next_ref; - } - } +/* Remove this problem from the stack of dataflow problems. */ + +static void +df_chain_remove_problem (void) +{ + bitmap_iterator bi; + unsigned int bb_index; + + /* Wholesale destruction of the old chains. */ + if (df_chain->block_pool) + free_alloc_pool (df_chain->block_pool); - if (dflow->flags & DF_UD_CHAIN) + EXECUTE_IF_SET_IN_BITMAP (df_chain->out_of_date_transfer_functions, 0, bb_index, bi) + { + rtx insn; + struct df_ref **def_rec; + struct df_ref **use_rec; + basic_block bb = BASIC_BLOCK (bb_index); + + if (df_chain_problem_p (DF_DU_CHAIN)) + for (def_rec = df_get_artificial_defs (bb->index); *def_rec; def_rec++) + DF_REF_CHAIN (*def_rec) = NULL; + if (df_chain_problem_p (DF_UD_CHAIN)) + for (use_rec = df_get_artificial_uses (bb->index); *use_rec; use_rec++) + DF_REF_CHAIN (*use_rec) = NULL; + + FOR_BB_INSNS (bb, insn) { - ref = insn_info->uses; - while (ref) + unsigned int uid = INSN_UID (insn); + + if (INSN_P (insn)) { - ref->chain = NULL; - ref = ref->next_ref; + if (df_chain_problem_p (DF_DU_CHAIN)) + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + DF_REF_CHAIN (*def_rec) = NULL; + if (df_chain_problem_p (DF_UD_CHAIN)) + { + for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) + DF_REF_CHAIN (*use_rec) = NULL; + for (use_rec = DF_INSN_UID_EQ_USES (uid); *use_rec; use_rec++) + DF_REF_CHAIN (*use_rec) = NULL; + } } } } + + bitmap_clear (df_chain->out_of_date_transfer_functions); + df_chain->block_pool = NULL; } -/* Reset all def_use and use_def chains in basic block. */ +/* Remove the chain problem completely. */ -static void -df_chain_bb_reset (struct dataflow *dflow, unsigned int bb_index) +static void +df_chain_fully_remove_problem (void) { - struct df *df = dflow->df; - rtx insn; - basic_block bb = BASIC_BLOCK (bb_index); + df_chain_remove_problem (); + BITMAP_FREE (df_chain->out_of_date_transfer_functions); + free (df_chain); +} - /* Some one deleted the basic block out from under us. */ - if (!bb) - return; - FOR_BB_INSNS (bb, insn) - { - if (INSN_P (insn)) - { - /* Record defs within INSN. */ - df_chain_insn_reset (dflow, insn); - } - } - - /* Get rid of any chains in artificial uses or defs. */ - if (dflow->flags & DF_DU_CHAIN) - { - struct df_ref *def; - def = df_get_artificial_defs (df, bb_index); - while (def) - { - def->chain = NULL; - def = def->next_ref; - } - } +/* Create def-use or use-def chains. */ - if (dflow->flags & DF_UD_CHAIN) - { - struct df_ref *use; - use = df_get_artificial_uses (df, bb_index); - while (use) - { - use->chain = NULL; - use = use->next_ref; - } - } +static void +df_chain_alloc (bitmap all_blocks ATTRIBUTE_UNUSED) +{ + df_chain_remove_problem (); + df_chain->block_pool = create_alloc_pool ("df_chain_block pool", + sizeof (struct df_link), 50); } /* Reset all of the chains when the set of basic blocks changes. */ - static void -df_chain_reset (struct dataflow *dflow, bitmap blocks_to_clear) +df_chain_reset (bitmap blocks_to_clear ATTRIBUTE_UNUSED) { - bitmap_iterator bi; - unsigned int bb_index; - - EXECUTE_IF_SET_IN_BITMAP (blocks_to_clear, 0, bb_index, bi) - { - df_chain_bb_reset (dflow, bb_index); - } - - free_alloc_pool (dflow->block_pool); - dflow->block_pool = NULL; + df_chain_remove_problem (); } /* Create the chains for a list of USEs. */ static void -df_chain_create_bb_process_use (struct dataflow *dflow, - bitmap local_rd, - struct df_ref *use, +df_chain_create_bb_process_use (bitmap local_rd, + struct df_ref **use_rec, enum df_ref_flags top_flag) { - struct df *df = dflow->df; bitmap_iterator bi; unsigned int def_index; - while (use) + while (*use_rec) { - /* Do not want to go through this for an uninitialized var. */ + struct df_ref *use = *use_rec; unsigned int uregno = DF_REF_REGNO (use); - int count = DF_REG_DEF_GET (df, uregno)->n_refs; - if (count) + if ((!(df->changeable_flags & DF_NO_HARD_REGS)) + || (uregno >= FIRST_PSEUDO_REGISTER)) { - if (top_flag == (DF_REF_FLAGS (use) & DF_REF_AT_TOP)) + /* Do not want to go through this for an uninitialized var. */ + int count = DF_DEFS_COUNT (uregno); + if (count) { - unsigned int first_index = DF_REG_DEF_GET (df, uregno)->begin; - unsigned int last_index = first_index + count - 1; - - EXECUTE_IF_SET_IN_BITMAP (local_rd, first_index, def_index, bi) + if (top_flag == (DF_REF_FLAGS (use) & DF_REF_AT_TOP)) { - struct df_ref *def; - if (def_index > last_index) - break; + unsigned int first_index = DF_DEFS_BEGIN (uregno); + unsigned int last_index = first_index + count - 1; - def = DF_DEFS_GET (df, def_index); - if (dflow->flags & DF_DU_CHAIN) - df_chain_create (dflow, def, use); - if (dflow->flags & DF_UD_CHAIN) - df_chain_create (dflow, use, def); + EXECUTE_IF_SET_IN_BITMAP (local_rd, first_index, def_index, bi) + { + struct df_ref *def; + if (def_index > last_index) + break; + + def = DF_DEFS_GET (def_index); + if (df_chain_problem_p (DF_DU_CHAIN)) + df_chain_create (def, use); + if (df_chain_problem_p (DF_UD_CHAIN)) + df_chain_create (use, def); + } } } } - use = use->next_ref; + + use_rec++; } } -/* Reset the storage pool that the def-use or use-def chains have been - allocated in. We do not need to re adjust the pointers in the refs, - these have already been clean out.*/ /* Create chains from reaching defs bitmaps for basic block BB. */ + static void -df_chain_create_bb (struct dataflow *dflow, - struct dataflow *rd_dflow, - unsigned int bb_index) +df_chain_create_bb (unsigned int bb_index) { basic_block bb = BASIC_BLOCK (bb_index); - struct df_rd_bb_info *bb_info = df_rd_get_bb_info (rd_dflow, bb_index); + struct df_rd_bb_info *bb_info = df_rd_get_bb_info (bb_index); rtx insn; bitmap cpy = BITMAP_ALLOC (NULL); - struct df *df = dflow->df; - struct df_ref *def; + struct df_ref **def_rec; bitmap_copy (cpy, bb_info->in); + bitmap_set_bit (df_chain->out_of_date_transfer_functions, bb_index); /* Since we are going forwards, process the artificial uses first then the artificial defs second. */ @@ -2955,26 +3292,32 @@ df_chain_create_bb (struct dataflow *dflow, #ifdef EH_USES /* Create the chains for the artificial uses from the EH_USES at the beginning of the block. */ - df_chain_create_bb_process_use (dflow, cpy, - df_get_artificial_uses (df, bb->index), - DF_REF_AT_TOP); + + /* Artificials are only hard regs. */ + if (!(df->changeable_flags & DF_NO_HARD_REGS)) + df_chain_create_bb_process_use (cpy, + df_get_artificial_uses (bb->index), + DF_REF_AT_TOP); #endif - for (def = df_get_artificial_defs (df, bb_index); def; def = def->next_ref) - if (DF_REF_FLAGS (def) & DF_REF_AT_TOP) - { - unsigned int dregno = DF_REF_REGNO (def); - if (!(DF_REF_FLAGS (def) & DF_REF_PARTIAL)) - bitmap_clear_range (cpy, - DF_REG_DEF_GET (df, dregno)->begin, - DF_REG_DEF_GET (df, dregno)->n_refs); - bitmap_set_bit (cpy, DF_REF_ID (def)); - } + for (def_rec = df_get_artificial_defs (bb_index); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if (DF_REF_FLAGS (def) & DF_REF_AT_TOP) + { + unsigned int dregno = DF_REF_REGNO (def); + if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))) + bitmap_clear_range (cpy, + DF_DEFS_BEGIN (dregno), + DF_DEFS_COUNT (dregno)); + bitmap_set_bit (cpy, DF_REF_ID (def)); + } + } /* Process the regular instructions next. */ FOR_BB_INSNS (bb, insn) { - struct df_ref *def; + struct df_ref **def_rec; unsigned int uid = INSN_UID (insn); if (!INSN_P (insn)) @@ -2983,44 +3326,54 @@ df_chain_create_bb (struct dataflow *dflow, /* Now scan the uses and link them up with the defs that remain in the cpy vector. */ - df_chain_create_bb_process_use (dflow, cpy, - DF_INSN_UID_USES (df, uid), 0); + df_chain_create_bb_process_use (cpy, DF_INSN_UID_USES (uid), 0); + + if (df->changeable_flags & DF_EQ_NOTES) + df_chain_create_bb_process_use (cpy, DF_INSN_UID_EQ_USES (uid), 0); + /* Since we are going forwards, process the defs second. This pass only changes the bits in cpy. */ - for (def = DF_INSN_UID_DEFS (df, uid); def; def = def->next_ref) + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) { + struct df_ref *def = *def_rec; unsigned int dregno = DF_REF_REGNO (def); - if (!(DF_REF_FLAGS (def) & DF_REF_PARTIAL)) - bitmap_clear_range (cpy, - DF_REG_DEF_GET (df, dregno)->begin, - DF_REG_DEF_GET (df, dregno)->n_refs); - if (!(DF_REF_FLAGS (def) - & (DF_REF_MUST_CLOBBER | DF_REF_MAY_CLOBBER))) - bitmap_set_bit (cpy, DF_REF_ID (def)); + if ((!(df->changeable_flags & DF_NO_HARD_REGS)) + || (dregno >= FIRST_PSEUDO_REGISTER)) + { + if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))) + bitmap_clear_range (cpy, + DF_DEFS_BEGIN (dregno), + DF_DEFS_COUNT (dregno)); + if (!(DF_REF_FLAGS (def) + & (DF_REF_MUST_CLOBBER | DF_REF_MAY_CLOBBER))) + bitmap_set_bit (cpy, DF_REF_ID (def)); + } } } /* Create the chains for the artificial uses of the hard registers at the end of the block. */ - df_chain_create_bb_process_use (dflow, cpy, - df_get_artificial_uses (df, bb->index), 0); + if (!(df->changeable_flags & DF_NO_HARD_REGS)) + df_chain_create_bb_process_use (cpy, + df_get_artificial_uses (bb->index), + 0); + + BITMAP_FREE (cpy); } /* Create def-use chains from reaching use bitmaps for basic blocks in BLOCKS. */ static void -df_chain_finalize (struct dataflow *dflow, bitmap all_blocks) +df_chain_finalize (bitmap all_blocks) { unsigned int bb_index; bitmap_iterator bi; - struct df *df = dflow->df; - struct dataflow *rd_dflow = df->problems_by_index [DF_RD]; EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) { - df_chain_create_bb (dflow, rd_dflow, bb_index); + df_chain_create_bb (bb_index); } } @@ -3028,69 +3381,117 @@ df_chain_finalize (struct dataflow *dflow, bitmap all_blocks) /* Free all storage associated with the problem. */ static void -df_chain_free (struct dataflow *dflow) +df_chain_free (void) { - free_alloc_pool (dflow->block_pool); - free (dflow); + free_alloc_pool (df_chain->block_pool); + BITMAP_FREE (df_chain->out_of_date_transfer_functions); + free (df_chain); } /* Debugging info. */ static void -df_chains_dump (struct dataflow *dflow, FILE *file) +df_chain_top_dump (basic_block bb, FILE *file) { - struct df *df = dflow->df; - unsigned int j; - - if (dflow->flags & DF_DU_CHAIN) + if (df_chain_problem_p (DF_DU_CHAIN)) { - fprintf (file, "Def-use chains:\n"); - for (j = 0; j < df->def_info.bitmap_size; j++) + rtx insn; + struct df_ref **def_rec = df_get_artificial_defs (bb->index); + if (*def_rec) { - struct df_ref *def = DF_DEFS_GET (df, j); - if (def) + + fprintf (file, ";; DU chains for artificial defs\n"); + while (*def_rec) { - fprintf (file, "d%d bb %d luid %d insn %d reg %d ", - j, DF_REF_BBNO (def), - DF_REF_INSN (def) ? - DF_INSN_LUID (df, DF_REF_INSN (def)): - -1, - DF_REF_INSN (def) ? DF_REF_INSN_UID (def) : -1, - DF_REF_REGNO (def)); - if (def->flags & DF_REF_READ_WRITE) - fprintf (file, "read/write "); + struct df_ref *def = *def_rec; + fprintf (file, ";; reg %d ", DF_REF_REGNO (def)); df_chain_dump (DF_REF_CHAIN (def), file); fprintf (file, "\n"); + def_rec++; + } + } + + FOR_BB_INSNS (bb, insn) + { + unsigned int uid = INSN_UID (insn); + if (INSN_P (insn)) + { + def_rec = DF_INSN_UID_DEFS (uid); + if (*def_rec) + { + fprintf (file, ";; DU chains for insn luid %d uid %d\n", + DF_INSN_LUID (insn), uid); + + while (*def_rec) + { + struct df_ref *def = *def_rec; + fprintf (file, ";; reg %d ", DF_REF_REGNO (def)); + if (def->flags & DF_REF_READ_WRITE) + fprintf (file, "read/write "); + df_chain_dump (DF_REF_CHAIN (def), file); + fprintf (file, "\n"); + def_rec++; + } + } } } } +} + - if (dflow->flags & DF_UD_CHAIN) +static void +df_chain_bottom_dump (basic_block bb, FILE *file) +{ + if (df_chain_problem_p (DF_UD_CHAIN)) { - fprintf (file, "Use-def chains:\n"); - for (j = 0; j < df->use_info.bitmap_size; j++) + rtx insn; + struct df_ref **use_rec = df_get_artificial_uses (bb->index); + + if (*use_rec) { - struct df_ref *use = DF_USES_GET (df, j); - if (use) + fprintf (file, ";; UD chains for artificial uses\n"); + while (*use_rec) { - fprintf (file, "u%d bb %d luid %d insn %d reg %d ", - j, DF_REF_BBNO (use), - DF_REF_INSN (use) ? - DF_INSN_LUID (df, DF_REF_INSN (use)) - : -1, - DF_REF_INSN (DF_USES_GET (df, j)) ? - DF_REF_INSN_UID (DF_USES_GET (df,j)) - : -1, - DF_REF_REGNO (use)); - if (use->flags & DF_REF_READ_WRITE) - fprintf (file, "read/write "); - if (use->flags & DF_REF_STRIPPED) - fprintf (file, "stripped "); - if (use->flags & DF_REF_IN_NOTE) - fprintf (file, "note "); + struct df_ref *use = *use_rec; + fprintf (file, ";; reg %d ", DF_REF_REGNO (use)); df_chain_dump (DF_REF_CHAIN (use), file); fprintf (file, "\n"); + use_rec++; + } + } + + FOR_BB_INSNS (bb, insn) + { + unsigned int uid = INSN_UID (insn); + if (INSN_P (insn)) + { + struct df_ref **eq_use_rec = DF_INSN_UID_EQ_USES (uid); + use_rec = DF_INSN_UID_USES (uid); + if (*use_rec || *eq_use_rec) + { + fprintf (file, ";; UD chains for insn luid %d uid %d\n", + DF_INSN_LUID (insn), uid); + + while (*use_rec) + { + struct df_ref *use = *use_rec; + fprintf (file, ";; reg %d ", DF_REF_REGNO (use)); + if (use->flags & DF_REF_READ_WRITE) + fprintf (file, "read/write "); + df_chain_dump (DF_REF_CHAIN (use), file); + fprintf (file, "\n"); + use_rec++; + } + while (*eq_use_rec) + { + struct df_ref *use = *eq_use_rec; + fprintf (file, ";; eq_note reg %d ", DF_REF_REGNO (use)); + df_chain_dump (DF_REF_CHAIN (use), file); + fprintf (file, "\n"); + eq_use_rec++; + } + } } } } @@ -3112,9 +3513,14 @@ static struct df_problem problem_CHAIN = NULL, /* Transfer function. */ df_chain_finalize, /* Finalize function. */ df_chain_free, /* Free all of the problem information. */ - df_chains_dump, /* Debugging. */ - df_rd_add_problem, /* Dependent problem. */ - 0 /* Changeable flags. */ + df_chain_fully_remove_problem,/* Remove this problem from the stack of dataflow problems. */ + NULL, /* Debugging. */ + df_chain_top_dump, /* Debugging start block. */ + df_chain_bottom_dump, /* Debugging end block. */ + NULL, /* Incremental solution verify start. */ + NULL, /* Incremental solution verfiy end. */ + &problem_RD, /* Dependent problem. */ + TV_DF_CHAIN /* Timing variable. */ }; @@ -3122,108 +3528,106 @@ static struct df_problem problem_CHAIN = of DF. The returned structure is what is used to get at the solution. */ -struct dataflow * -df_chain_add_problem (struct df *df, int flags) +void +df_chain_add_problem (enum df_chain_flags chain_flags) { - return df_add_problem (df, &problem_CHAIN, flags); + df_add_problem (&problem_CHAIN); + df_chain->local_flags = (unsigned int)chain_flags; + df_chain->out_of_date_transfer_functions = BITMAP_ALLOC (NULL); } +#undef df_chain_problem_p + /*---------------------------------------------------------------------------- - REGISTER INFORMATION - - This pass properly computes REG_DEAD and REG_UNUSED notes. - - If the DF_RI_LIFE flag is set the following vectors containing - information about register usage are properly set: REG_N_REFS, - REG_N_DEATHS, REG_N_SETS, REG_LIVE_LENGTH, REG_N_CALLS_CROSSED, - REG_N_THROWING_CALLS_CROSSED and REG_BASIC_BLOCK. - + This pass computes REG_DEAD and REG_UNUSED notes. ----------------------------------------------------------------------------*/ #ifdef REG_DEAD_DEBUGGING static void -print_note (char *prefix, rtx insn, rtx note) -{ - fprintf (stderr, "%s %d ", prefix, INSN_UID (insn)); - print_rtl (stderr, note); - fprintf (stderr, "\n"); -} -#endif - -/* Allocate the lifetime information. */ - -static void -df_ri_alloc (struct dataflow *dflow, - bitmap blocks_to_rescan ATTRIBUTE_UNUSED, - bitmap all_blocks ATTRIBUTE_UNUSED) +df_print_note (const char *prefix, rtx insn, rtx note) { - int i; - struct df *df = dflow->df; - - if (dflow->flags & DF_RI_LIFE) + if (dump_file) { - max_regno = max_reg_num (); - allocate_reg_info (max_regno, FALSE, FALSE); - - /* Reset all the data we'll collect. */ - for (i = 0; i < max_regno; i++) - { - REG_N_SETS (i) = DF_REG_DEF_COUNT (df, i); - REG_N_REFS (i) = DF_REG_USE_COUNT (df, i) + REG_N_SETS (i); - REG_N_DEATHS (i) = 0; - REG_N_CALLS_CROSSED (i) = 0; - REG_N_THROWING_CALLS_CROSSED (i) = 0; - REG_LIVE_LENGTH (i) = 0; - REG_FREQ (i) = 0; - REG_BASIC_BLOCK (i) = REG_BLOCK_UNKNOWN; - } + fprintf (dump_file, "%s %d ", prefix, INSN_UID (insn)); + print_rtl (dump_file, note); + fprintf (dump_file, "\n"); } } +#endif /* After reg-stack, the x86 floating point stack regs are difficult to analyze because of all of the pushes, pops and rotations. Thus, we just leave the notes alone. */ +#ifdef STACK_REGS static inline bool -df_ignore_stack_reg (int regno ATTRIBUTE_UNUSED) +df_ignore_stack_reg (int regno) { -#ifdef STACK_REGS - return (regstack_completed - && IN_RANGE (regno, FIRST_STACK_REG, LAST_STACK_REG)); + return regstack_completed + && IN_RANGE (regno, FIRST_STACK_REG, LAST_STACK_REG); +} #else +static inline bool +df_ignore_stack_reg (int regno ATTRIBUTE_UNUSED) +{ return false; -#endif } +#endif -/* Remove all of the REG_DEAD or REG_UNUSED notes from INSN. */ +/* Remove all of the REG_DEAD or REG_UNUSED notes from INSN and add + them to OLD_DEAD_NOTES and OLD_UNUSED_NOTES. */ static void -df_kill_notes (rtx insn, int flags) +df_kill_notes (rtx insn, rtx *old_dead_notes, rtx *old_unused_notes) { rtx *pprev = ®_NOTES (insn); rtx link = *pprev; - + rtx dead = NULL; + rtx unused = NULL; + while (link) { switch (REG_NOTE_KIND (link)) { case REG_DEAD: - if (flags & DF_RI_LIFE) - if (df_ignore_stack_reg (REGNO (XEXP (link, 0)))) - REG_N_DEATHS (REGNO (XEXP (link, 0)))++; + /* After reg-stack, we need to ignore any unused notes + for the stack registers. */ + if (df_ignore_stack_reg (REGNO (XEXP (link, 0)))) + { + pprev = &XEXP (link, 1); + link = *pprev; + } + else + { + rtx next = XEXP (link, 1); +#ifdef REG_DEAD_DEBUGGING + df_print_note ("deleting: ", insn, link); +#endif + XEXP (link, 1) = dead; + dead = link; + *pprev = link = next; + } + break; - /* Fallthru */ case REG_UNUSED: - if (!df_ignore_stack_reg (REGNO (XEXP (link, 0)))) + /* After reg-stack, we need to ignore any unused notes + for the stack registers. */ + if (df_ignore_stack_reg (REGNO (XEXP (link, 0)))) + { + pprev = &XEXP (link, 1); + link = *pprev; + } + else { rtx next = XEXP (link, 1); #ifdef REG_DEAD_DEBUGGING - print_note ("deleting: ", insn, link); + df_print_note ("deleting: ", insn, link); #endif - free_EXPR_LIST_node (link); + XEXP (link, 1) = unused; + unused = link; *pprev = link = next; } break; @@ -3234,9 +3638,43 @@ df_kill_notes (rtx insn, int flags) break; } } + + *old_dead_notes = dead; + *old_unused_notes = unused; } +/* Set a NOTE_TYPE note for REG in INSN. Try to pull it from the OLD + list, otherwise create a new one. */ + +static inline rtx +df_set_note (enum reg_note note_type, rtx insn, rtx old, rtx reg) +{ + rtx this = old; + rtx prev = NULL; + + while (this) + if (XEXP (this, 0) == reg) + { + if (prev) + XEXP (prev, 1) = XEXP (this, 1); + else + old = XEXP (this, 1); + XEXP (this, 1) = REG_NOTES (insn); + REG_NOTES (insn) = this; + return old; + } + else + { + prev = this; + this = XEXP (this, 1); + } + + /* Did not find the note. */ + REG_NOTES (insn) = alloc_EXPR_LIST (note_type, reg, REG_NOTES (insn)); + return old; +} + /* Set the REG_UNUSED notes for the multiword hardreg defs in INSN based on the bits in LIVE. Do not generate notes for registers in artificial uses. DO_NOT_GEN is updated so that REG_DEAD notes are @@ -3244,70 +3682,53 @@ df_kill_notes (rtx insn, int flags) instruction. */ -static void -df_set_unused_notes_for_mw (rtx insn, struct df_mw_hardreg *mws, +static rtx +df_set_unused_notes_for_mw (rtx insn, rtx old, struct df_mw_hardreg *mws, bitmap live, bitmap do_not_gen, - bitmap artificial_uses, int flags) + bitmap artificial_uses) { bool all_dead = true; - struct df_link *regs = mws->regs; - unsigned int regno = DF_REF_REGNO (regs->ref); + unsigned int r; #ifdef REG_DEAD_DEBUGGING - fprintf (stderr, "mw unused looking at %d\n", DF_REF_REGNO (regs->ref)); - df_ref_debug (regs->ref, stderr); + if (dump_file) + fprintf (dump_file, "mw_set_unused looking at mws[%d..%d]\n", + mws->start_regno, mws->end_regno); #endif - while (regs) - { - unsigned int regno = DF_REF_REGNO (regs->ref); - if ((bitmap_bit_p (live, regno)) - || bitmap_bit_p (artificial_uses, regno)) - { - all_dead = false; - break; - } - regs = regs->next; - } + for (r=mws->start_regno; r <= mws->end_regno; r++) + if ((bitmap_bit_p (live, r)) + || bitmap_bit_p (artificial_uses, r)) + { + all_dead = false; + break; + } if (all_dead) { - struct df_link *regs = mws->regs; - rtx note = alloc_EXPR_LIST (REG_UNUSED, *DF_REF_LOC (regs->ref), - REG_NOTES (insn)); - REG_NOTES (insn) = note; + unsigned int regno = mws->start_regno; + old = df_set_note (REG_UNUSED, insn, old, *(mws->loc)); + #ifdef REG_DEAD_DEBUGGING - print_note ("adding 1: ", insn, note); + df_print_note ("adding 1: ", insn, REG_NOTES (insn)); #endif bitmap_set_bit (do_not_gen, regno); /* Only do this if the value is totally dead. */ - if (flags & DF_RI_LIFE) - { - REG_N_DEATHS (regno) ++; - REG_LIVE_LENGTH (regno)++; - } } else - { - struct df_link *regs = mws->regs; - while (regs) - { - struct df_ref *ref = regs->ref; - - regno = DF_REF_REGNO (ref); - if ((!bitmap_bit_p (live, regno)) - && (!bitmap_bit_p (artificial_uses, regno))) - { - rtx note = alloc_EXPR_LIST (REG_UNUSED, regno_reg_rtx[regno], - REG_NOTES (insn)); - REG_NOTES (insn) = note; + for (r=mws->start_regno; r <= mws->end_regno; r++) + { + + if ((!bitmap_bit_p (live, r)) + && (!bitmap_bit_p (artificial_uses, r))) + { + old = df_set_note (REG_UNUSED, insn, old, regno_reg_rtx[r]); #ifdef REG_DEAD_DEBUGGING - print_note ("adding 2: ", insn, note); + df_print_note ("adding 2: ", insn, REG_NOTES (insn)); #endif - } - bitmap_set_bit (do_not_gen, regno); - regs = regs->next; - } - } + } + bitmap_set_bit (do_not_gen, r); + } + return old; } @@ -3316,82 +3737,63 @@ df_set_unused_notes_for_mw (rtx insn, struct df_mw_hardreg *mws, from being set if the instruction both reads and writes the register. */ -static void -df_set_dead_notes_for_mw (rtx insn, struct df_mw_hardreg *mws, +static rtx +df_set_dead_notes_for_mw (rtx insn, rtx old, struct df_mw_hardreg *mws, bitmap live, bitmap do_not_gen, - bitmap artificial_uses, int flags) + bitmap artificial_uses) { bool all_dead = true; - struct df_link *regs = mws->regs; - unsigned int regno = DF_REF_REGNO (regs->ref); + unsigned int r; #ifdef REG_DEAD_DEBUGGING - fprintf (stderr, "mw looking at %d\n", DF_REF_REGNO (regs->ref)); - df_ref_debug (regs->ref, stderr); -#endif - while (regs) + if (dump_file) { - unsigned int regno = DF_REF_REGNO (regs->ref); - if ((bitmap_bit_p (live, regno)) - || bitmap_bit_p (artificial_uses, regno)) - { - all_dead = false; - break; - } - regs = regs->next; + fprintf (dump_file, "mw_set_dead looking at mws[%d..%d]\n do_not_gen =", + mws->start_regno, mws->end_regno); + df_print_regset (dump_file, do_not_gen); + fprintf (dump_file, " live ="); + df_print_regset (dump_file, live); + fprintf (dump_file, " artificial uses ="); + df_print_regset (dump_file, artificial_uses); } +#endif + + for (r = mws->start_regno; r <= mws->end_regno; r++) + if ((bitmap_bit_p (live, r)) + || bitmap_bit_p (artificial_uses, r) + || bitmap_bit_p (do_not_gen, r)) + { + all_dead = false; + break; + } if (all_dead) { - if (!bitmap_bit_p (do_not_gen, regno)) + if (!bitmap_bit_p (do_not_gen, mws->start_regno)) { /* Add a dead note for the entire multi word register. */ - struct df_link *regs = mws->regs; - rtx note = alloc_EXPR_LIST (REG_DEAD, *DF_REF_LOC (regs->ref), - REG_NOTES (insn)); - REG_NOTES (insn) = note; + old = df_set_note (REG_DEAD, insn, old, *(mws->loc)); #ifdef REG_DEAD_DEBUGGING - print_note ("adding 1: ", insn, note); + df_print_note ("adding 1: ", insn, REG_NOTES (insn)); #endif - - if (flags & DF_RI_LIFE) - { - struct df_link *regs = mws->regs; - while (regs) - { - struct df_ref *ref = regs->ref; - regno = DF_REF_REGNO (ref); - REG_N_DEATHS (regno)++; - regs = regs->next; - } - } } } else { - struct df_link *regs = mws->regs; - while (regs) + for (r = mws->start_regno; r <= mws->end_regno; r++) { - struct df_ref *ref = regs->ref; - - regno = DF_REF_REGNO (ref); - if ((!bitmap_bit_p (live, regno)) - && (!bitmap_bit_p (artificial_uses, regno)) - && (!bitmap_bit_p (do_not_gen, regno))) + if ((!bitmap_bit_p (live, r)) + && (!bitmap_bit_p (artificial_uses, r)) + && (!bitmap_bit_p (do_not_gen, r))) { - rtx note = alloc_EXPR_LIST (REG_DEAD, regno_reg_rtx[regno], - REG_NOTES (insn)); - REG_NOTES (insn) = note; - if (flags & DF_RI_LIFE) - REG_N_DEATHS (regno)++; + old = df_set_note (REG_DEAD, insn, old, regno_reg_rtx[r]); #ifdef REG_DEAD_DEBUGGING - print_note ("adding 2: ", insn, note); + df_print_note ("adding 2: ", insn, REG_NOTES (insn)); #endif } - - regs = regs->next; } } + return old; } @@ -3399,72 +3801,40 @@ df_set_dead_notes_for_mw (rtx insn, struct df_mw_hardreg *mws, and DO_NOT_GEN. Do not generate notes for registers in artificial uses. */ -static void -df_create_unused_note (basic_block bb, rtx insn, struct df_ref *def, - bitmap live, bitmap do_not_gen, bitmap artificial_uses, - bitmap local_live, bitmap local_processed, - int flags, int luid) +static rtx +df_create_unused_note (rtx insn, rtx old, struct df_ref *def, + bitmap live, bitmap do_not_gen, bitmap artificial_uses) { unsigned int dregno = DF_REF_REGNO (def); #ifdef REG_DEAD_DEBUGGING - fprintf (stderr, " regular looking at def "); - df_ref_debug (def, stderr); -#endif - - if (bitmap_bit_p (live, dregno)) + if (dump_file) { - if (flags & DF_RI_LIFE) - { - /* If we have seen this regno, then it has already been - processed correctly with the per insn increment. If we - have not seen it we need to add the length from here to - the end of the block to the live length. */ - if (bitmap_bit_p (local_processed, dregno)) - { - if (!(DF_REF_FLAGS (def) & DF_REF_PARTIAL)) - bitmap_clear_bit (local_live, dregno); - } - else - { - bitmap_set_bit (local_processed, dregno); - REG_LIVE_LENGTH (dregno) += luid; - } - } + fprintf (dump_file, " regular looking at def "); + df_ref_debug (def, dump_file); } - else if ((!(DF_REF_FLAGS (def) & DF_REF_MW_HARDREG)) - && (!bitmap_bit_p (artificial_uses, dregno)) - && (!df_ignore_stack_reg (dregno))) +#endif + + if (!(bitmap_bit_p (live, dregno) + || (DF_REF_FLAGS (def) & DF_REF_MW_HARDREG) + || bitmap_bit_p (artificial_uses, dregno) + || df_ignore_stack_reg (dregno))) { - rtx reg = GET_CODE (*DF_REF_LOC (def)) == SUBREG ? - SUBREG_REG (*DF_REF_LOC (def)) : *DF_REF_LOC (def); - rtx note = alloc_EXPR_LIST (REG_UNUSED, reg, REG_NOTES (insn)); - REG_NOTES (insn) = note; + rtx reg = (DF_REF_LOC (def)) + ? *DF_REF_REAL_LOC (def): DF_REF_REG (def); + old = df_set_note (REG_UNUSED, insn, old, reg); #ifdef REG_DEAD_DEBUGGING - print_note ("adding 3: ", insn, note); + df_print_note ("adding 3: ", insn, REG_NOTES (insn)); #endif - if (flags & DF_RI_LIFE) - { - REG_N_DEATHS (dregno) ++; - REG_LIVE_LENGTH (dregno)++; - } } - if ((flags & DF_RI_LIFE) && (dregno >= FIRST_PSEUDO_REGISTER)) - { - REG_FREQ (dregno) += REG_FREQ_FROM_BB (bb); - if (REG_BASIC_BLOCK (dregno) == REG_BLOCK_UNKNOWN) - REG_BASIC_BLOCK (dregno) = bb->index; - else if (REG_BASIC_BLOCK (dregno) != bb->index) - REG_BASIC_BLOCK (dregno) = REG_BLOCK_GLOBAL; - } - if (!(DF_REF_FLAGS (def) & (DF_REF_MUST_CLOBBER + DF_REF_MAY_CLOBBER))) bitmap_set_bit (do_not_gen, dregno); - /* Kill this register if it is not a subreg store. */ - if (!(DF_REF_FLAGS (def) & DF_REF_PARTIAL)) + /* Kill this register if it is not a subreg store or conditional store. */ + if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))) bitmap_clear_bit (live, dregno); + return old; } @@ -3473,163 +3843,159 @@ df_create_unused_note (basic_block bb, rtx insn, struct df_ref *def, BB. The three bitvectors are scratch regs used here. */ static void -df_ri_bb_compute (struct dataflow *dflow, unsigned int bb_index, - bitmap live, bitmap do_not_gen, bitmap artificial_uses, - bitmap local_live, bitmap local_processed, bitmap setjumps_crossed) +df_note_bb_compute (unsigned int bb_index, + bitmap live, bitmap do_not_gen, bitmap artificial_uses) { - struct df *df = dflow->df; basic_block bb = BASIC_BLOCK (bb_index); rtx insn; - struct df_ref *def; - struct df_ref *use; - int luid = 0; + struct df_ref **def_rec; + struct df_ref **use_rec; - bitmap_copy (live, df_get_live_out (df, bb)); + bitmap_copy (live, df_get_live_out (bb)); bitmap_clear (artificial_uses); - if (dflow->flags & DF_RI_LIFE) +#ifdef REG_DEAD_DEBUGGING + if (dump_file) { - /* Process the regs live at the end of the block. Mark them as - not local to any one basic block. */ - bitmap_iterator bi; - unsigned int regno; - EXECUTE_IF_SET_IN_BITMAP (live, 0, regno, bi) - REG_BASIC_BLOCK (regno) = REG_BLOCK_GLOBAL; + fprintf (dump_file, "live at bottom "); + df_print_regset (dump_file, live); } +#endif /* Process the artificial defs and uses at the bottom of the block to begin processing. */ - for (def = df_get_artificial_defs (df, bb_index); def; def = def->next_ref) - if ((DF_REF_FLAGS (def) & DF_REF_AT_TOP) == 0) - bitmap_clear_bit (live, DF_REF_REGNO (def)); + for (def_rec = df_get_artificial_defs (bb_index); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if (dump_file) + fprintf (dump_file, "artificial def %d\n", DF_REF_REGNO (def)); - for (use = df_get_artificial_uses (df, bb_index); use; use = use->next_ref) - if ((DF_REF_FLAGS (use) & DF_REF_AT_TOP) == 0) - { - unsigned int regno = DF_REF_REGNO (use); - bitmap_set_bit (live, regno); + if ((DF_REF_FLAGS (def) & DF_REF_AT_TOP) == 0) + bitmap_clear_bit (live, DF_REF_REGNO (def)); + } - /* Notes are not generated for any of the artificial registers - at the bottom of the block. */ - bitmap_set_bit (artificial_uses, regno); - } + for (use_rec = df_get_artificial_uses (bb_index); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if ((DF_REF_FLAGS (use) & DF_REF_AT_TOP) == 0) + { + unsigned int regno = DF_REF_REGNO (use); + bitmap_set_bit (live, regno); + + /* Notes are not generated for any of the artificial registers + at the bottom of the block. */ + bitmap_set_bit (artificial_uses, regno); + } + } +#ifdef REG_DEAD_DEBUGGING + if (dump_file) + { + fprintf (dump_file, "live before artificials out "); + df_print_regset (dump_file, live); + } +#endif + FOR_BB_INSNS_REVERSE (bb, insn) { unsigned int uid = INSN_UID (insn); - unsigned int regno; - bitmap_iterator bi; - struct df_mw_hardreg *mws; - + struct df_mw_hardreg **mws_rec; + rtx old_dead_notes; + rtx old_unused_notes; + if (!INSN_P (insn)) continue; - if (dflow->flags & DF_RI_LIFE) - { - /* Increment the live_length for all of the registers that - are are referenced in this block and live at this - particular point. */ - bitmap_iterator bi; - unsigned int regno; - EXECUTE_IF_SET_IN_BITMAP (local_live, 0, regno, bi) - { - REG_LIVE_LENGTH (regno)++; - } - luid++; - } - bitmap_clear (do_not_gen); - df_kill_notes (insn, dflow->flags); + df_kill_notes (insn, &old_dead_notes, &old_unused_notes); /* Process the defs. */ if (CALL_P (insn)) { - if (dflow->flags & DF_RI_LIFE) +#ifdef REG_DEAD_DEBUGGING + if (dump_file) { - bool can_throw = can_throw_internal (insn); - bool set_jump = (find_reg_note (insn, REG_SETJMP, NULL) != NULL); - EXECUTE_IF_SET_IN_BITMAP (live, 0, regno, bi) - { - REG_N_CALLS_CROSSED (regno)++; - if (can_throw) - REG_N_THROWING_CALLS_CROSSED (regno)++; - - /* We have a problem with any pseudoreg that lives - across the setjmp. ANSI says that if a user - variable does not change in value between the - setjmp and the longjmp, then the longjmp - preserves it. This includes longjmp from a place - where the pseudo appears dead. (In principle, - the value still exists if it is in scope.) If - the pseudo goes in a hard reg, some other value - may occupy that hard reg where this pseudo is - dead, thus clobbering the pseudo. Conclusion: - such a pseudo must not go in a hard reg. */ - if (set_jump && regno >= FIRST_PSEUDO_REGISTER) - bitmap_set_bit (setjumps_crossed, regno); - } + fprintf (dump_file, "processing call %d\n live =", INSN_UID (insn)); + df_print_regset (dump_file, live); } - +#endif /* We only care about real sets for calls. Clobbers only - may clobber and cannot be depended on. */ - for (mws = DF_INSN_UID_MWS (df, uid); mws; mws = mws->next) + may clobbers cannot be depended on. */ + mws_rec = DF_INSN_UID_MWS (uid); + while (*mws_rec) { + struct df_mw_hardreg *mws = *mws_rec; if ((mws->type == DF_REF_REG_DEF) && !df_ignore_stack_reg (REGNO (mws->mw_reg))) - df_set_unused_notes_for_mw (insn, mws, live, do_not_gen, - artificial_uses, dflow->flags); + old_unused_notes + = df_set_unused_notes_for_mw (insn, old_unused_notes, + mws, live, do_not_gen, + artificial_uses); + mws_rec++; } /* All of the defs except the return value are some sort of clobber. This code is for the return. */ - for (def = DF_INSN_UID_DEFS (df, uid); def; def = def->next_ref) - if (!(DF_REF_FLAGS (def) & (DF_REF_MUST_CLOBBER | DF_REF_MAY_CLOBBER))) - df_create_unused_note (bb, insn, def, live, do_not_gen, - artificial_uses, local_live, - local_processed, dflow->flags, luid); - + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if (!(DF_REF_FLAGS (def) & (DF_REF_MUST_CLOBBER | DF_REF_MAY_CLOBBER))) + old_unused_notes + = df_create_unused_note (insn, old_unused_notes, + def, live, do_not_gen, + artificial_uses); + } } else { /* Regular insn. */ - for (mws = DF_INSN_UID_MWS (df, uid); mws; mws = mws->next) + mws_rec = DF_INSN_UID_MWS (uid); + while (*mws_rec) { + struct df_mw_hardreg *mws = *mws_rec; if (mws->type == DF_REF_REG_DEF) - df_set_unused_notes_for_mw (insn, mws, live, do_not_gen, - artificial_uses, dflow->flags); + old_unused_notes + = df_set_unused_notes_for_mw (insn, old_unused_notes, + mws, live, do_not_gen, + artificial_uses); + mws_rec++; } - for (def = DF_INSN_UID_DEFS (df, uid); def; def = def->next_ref) - df_create_unused_note (bb, insn, def, live, do_not_gen, - artificial_uses, local_live, - local_processed, dflow->flags, luid); + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + old_unused_notes + = df_create_unused_note (insn, old_unused_notes, + def, live, do_not_gen, + artificial_uses); + } } /* Process the uses. */ - for (mws = DF_INSN_UID_MWS (df, uid); mws; mws = mws->next) + mws_rec = DF_INSN_UID_MWS (uid); + while (*mws_rec) { + struct df_mw_hardreg *mws = *mws_rec; if ((mws->type != DF_REF_REG_DEF) && !df_ignore_stack_reg (REGNO (mws->mw_reg))) - df_set_dead_notes_for_mw (insn, mws, live, do_not_gen, - artificial_uses, dflow->flags); + old_dead_notes + = df_set_dead_notes_for_mw (insn, old_dead_notes, + mws, live, do_not_gen, + artificial_uses); + mws_rec++; } - for (use = DF_INSN_UID_USES (df, uid); use; use = use->next_ref) + for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) { + struct df_ref *use = *use_rec; unsigned int uregno = DF_REF_REGNO (use); - if ((dflow->flags & DF_RI_LIFE) && (uregno >= FIRST_PSEUDO_REGISTER)) +#ifdef REG_DEAD_DEBUGGING + if (dump_file) { - REG_FREQ (uregno) += REG_FREQ_FROM_BB (bb); - if (REG_BASIC_BLOCK (uregno) == REG_BLOCK_UNKNOWN) - REG_BASIC_BLOCK (uregno) = bb->index; - else if (REG_BASIC_BLOCK (uregno) != bb->index) - REG_BASIC_BLOCK (uregno) = REG_BLOCK_GLOBAL; + fprintf (dump_file, " regular looking at use "); + df_ref_debug (use, dump_file); } - -#ifdef REG_DEAD_DEBUGGING - fprintf (stderr, " regular looking at use "); - df_ref_debug (use, stderr); #endif if (!bitmap_bit_p (live, uregno)) { @@ -3639,157 +4005,99 @@ df_ri_bb_compute (struct dataflow *dflow, unsigned int bb_index, && (!(DF_REF_FLAGS (use) & DF_REF_READ_WRITE)) && (!df_ignore_stack_reg (uregno))) { - rtx reg = GET_CODE (*DF_REF_LOC (use)) == SUBREG ? - SUBREG_REG (*DF_REF_LOC (use)) : *DF_REF_LOC (use); - rtx note = alloc_EXPR_LIST (REG_DEAD, reg, REG_NOTES (insn)); - REG_NOTES (insn) = note; - if (dflow->flags & DF_RI_LIFE) - REG_N_DEATHS (uregno)++; + rtx reg = (DF_REF_LOC (use)) + ? *DF_REF_REAL_LOC (use) : DF_REF_REG (use); + old_dead_notes = df_set_note (REG_DEAD, insn, old_dead_notes, reg); #ifdef REG_DEAD_DEBUGGING - print_note ("adding 4: ", insn, note); + df_print_note ("adding 4: ", insn, REG_NOTES (insn)); #endif } /* This register is now live. */ bitmap_set_bit (live, uregno); - - if (dflow->flags & DF_RI_LIFE) - { - /* If we have seen this regno, then it has already - been processed correctly with the per insn - increment. If we have not seen it we set the bit - so that begins to get processed locally. Note - that we don't even get here if the variable was - live at the end of the block since just a ref - inside the block does not effect the - calculations. */ - REG_LIVE_LENGTH (uregno) ++; - bitmap_set_bit (local_live, uregno); - bitmap_set_bit (local_processed, uregno); - } } } - } - - if (dflow->flags & DF_RI_LIFE) - { - /* Add the length of the block to all of the registers that were - not referenced, but still live in this block. */ - bitmap_iterator bi; - unsigned int regno; - bitmap_and_compl_into (live, local_processed); - EXECUTE_IF_SET_IN_BITMAP (live, 0, regno, bi) + + while (old_unused_notes) + { + rtx next = XEXP (old_unused_notes, 1); + free_EXPR_LIST_node (old_unused_notes); + old_unused_notes = next; + } + while (old_dead_notes) { - REG_LIVE_LENGTH (regno) += luid; + rtx next = XEXP (old_dead_notes, 1); + free_EXPR_LIST_node (old_dead_notes); + old_dead_notes = next; } - bitmap_clear (local_processed); - bitmap_clear (local_live); } } /* Compute register info: lifetime, bb, and number of defs and uses. */ static void -df_ri_compute (struct dataflow *dflow, bitmap all_blocks ATTRIBUTE_UNUSED, - bitmap blocks_to_scan) +df_note_compute (bitmap all_blocks) { unsigned int bb_index; bitmap_iterator bi; - bitmap live = BITMAP_ALLOC (NULL); - bitmap do_not_gen = BITMAP_ALLOC (NULL); - bitmap artificial_uses = BITMAP_ALLOC (NULL); - bitmap local_live = NULL; - bitmap local_processed = NULL; - bitmap setjumps_crossed = NULL; - - if (dflow->flags & DF_RI_LIFE) - { - local_live = BITMAP_ALLOC (NULL); - local_processed = BITMAP_ALLOC (NULL); - setjumps_crossed = BITMAP_ALLOC (NULL); - } - + bitmap live = BITMAP_ALLOC (&df_bitmap_obstack); + bitmap do_not_gen = BITMAP_ALLOC (&df_bitmap_obstack); + bitmap artificial_uses = BITMAP_ALLOC (&df_bitmap_obstack); #ifdef REG_DEAD_DEBUGGING - df_lr_dump (dflow->df->problems_by_index [DF_LR], stderr); - print_rtl_with_bb (stderr, get_insns()); + if (dump_file) + print_rtl_with_bb (dump_file, get_insns()); #endif - EXECUTE_IF_SET_IN_BITMAP (blocks_to_scan, 0, bb_index, bi) + EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi) { - df_ri_bb_compute (dflow, bb_index, live, do_not_gen, artificial_uses, - local_live, local_processed, setjumps_crossed); + df_note_bb_compute (bb_index, live, do_not_gen, artificial_uses); } BITMAP_FREE (live); BITMAP_FREE (do_not_gen); BITMAP_FREE (artificial_uses); - if (dflow->flags & DF_RI_LIFE) - { - bitmap_iterator bi; - unsigned int regno; - /* See the setjump comment in df_ri_bb_compute. */ - EXECUTE_IF_SET_IN_BITMAP (setjumps_crossed, 0, regno, bi) - { - REG_BASIC_BLOCK (regno) = REG_BLOCK_UNKNOWN; - REG_LIVE_LENGTH (regno) = -1; - } - - BITMAP_FREE (local_live); - BITMAP_FREE (local_processed); - BITMAP_FREE (setjumps_crossed); - } } /* Free all storage associated with the problem. */ static void -df_ri_free (struct dataflow *dflow) +df_note_free (void) { - free (dflow->problem_data); - free (dflow); + free (df_note); } -/* Debugging info. */ - -static void -df_ri_dump (struct dataflow *dflow, FILE *file) -{ - print_rtl_with_bb (file, get_insns ()); - - if (dflow->flags & DF_RI_LIFE) - { - fprintf (file, "Register info:\n"); - dump_flow_info (file, -1); - } -} - /* All of the information associated every instance of the problem. */ -static struct df_problem problem_RI = +static struct df_problem problem_NOTE = { - DF_RI, /* Problem id. */ + DF_NOTE, /* Problem id. */ DF_NONE, /* Direction. */ - df_ri_alloc, /* Allocate the problem specific data. */ + NULL, /* Allocate the problem specific data. */ NULL, /* Reset global information. */ NULL, /* Free basic block info. */ - df_ri_compute, /* Local compute function. */ + df_note_compute, /* Local compute function. */ NULL, /* Init the solution specific data. */ NULL, /* Iterative solver. */ NULL, /* Confluence operator 0. */ NULL, /* Confluence operator n. */ NULL, /* Transfer function. */ NULL, /* Finalize function. */ - df_ri_free, /* Free all of the problem information. */ - df_ri_dump, /* Debugging. */ + df_note_free, /* Free all of the problem information. */ + df_note_free, /* Remove this problem from the stack of dataflow problems. */ + NULL, /* Debugging. */ + NULL, /* Debugging start block. */ + NULL, /* Debugging end block. */ + NULL, /* Incremental solution verify start. */ + NULL, /* Incremental solution verfiy end. */ /* Technically this is only dependent on the live registers problem but it will produce information if built one of uninitialized register problems (UR, UREC) is also run. */ - df_lr_add_problem, /* Dependent problem. */ - 0 /* Changeable flags. */ + &problem_LR, /* Dependent problem. */ + TV_DF_NOTE /* Timing variable. */ }; @@ -3797,8 +4105,242 @@ static struct df_problem problem_RI = of DF. The returned structure is what is used to get at the solution. */ -struct dataflow * -df_ri_add_problem (struct df *df, int flags) +void +df_note_add_problem (void) { - return df_add_problem (df, &problem_RI, flags); + df_add_problem (&problem_NOTE); } + + + + +/*---------------------------------------------------------------------------- + Functions for simulating the effects of single insns. + + You can either simulate in the forwards direction, starting from + the top of a block or the backwards direction from the end of the + block. The main difference is that if you go forwards, the uses + are examined first then the defs, and if you go backwards, the defs + are examined first then the uses. + + If you start at the top of the block, use one of DF_LIVE_IN or + DF_LR_IN. If you start at the bottom of the block use one of + DF_LIVE_OUT or DF_LR_OUT. BE SURE TO PASS A COPY OF THESE SETS, + THEY WILL BE DESTROYED. + +----------------------------------------------------------------------------*/ + + +/* Find the set of DEFs for INSN. */ + +void +df_simulate_find_defs (rtx insn, bitmap defs) +{ + struct df_ref **def_rec; + unsigned int uid = INSN_UID (insn); + + if (CALL_P (insn)) + { + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + unsigned int dregno = DF_REF_REGNO (def); + + if (DF_REF_FLAGS (def) & DF_REF_MUST_CLOBBER) + { + if (dregno >= FIRST_PSEUDO_REGISTER + || !(SIBLING_CALL_P (insn) + && bitmap_bit_p (df->exit_block_uses, dregno) + && !refers_to_regno_p (dregno, dregno+1, + current_function_return_rtx, + (rtx *)0))) + { + /* If the def is to only part of the reg, it does + not kill the other defs that reach here. */ + if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))) + bitmap_set_bit (defs, dregno); + } + } + else + /* This is the return value. */ + if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))) + bitmap_set_bit (defs, dregno); + } + } + else + { + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + /* If the def is to only part of the reg, it does + not kill the other defs that reach here. */ + if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))) + bitmap_set_bit (defs, DF_REF_REGNO (def)); + } + } +} + + +/* Simulate the effects of the defs of INSN on LIVE. */ + +void +df_simulate_defs (rtx insn, bitmap live) +{ + struct df_ref **def_rec; + unsigned int uid = INSN_UID (insn); + + if (CALL_P (insn)) + { + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + unsigned int dregno = DF_REF_REGNO (def); + + if (DF_REF_FLAGS (def) & DF_REF_MUST_CLOBBER) + { + if (dregno >= FIRST_PSEUDO_REGISTER + || !(SIBLING_CALL_P (insn) + && bitmap_bit_p (df->exit_block_uses, dregno) + && !refers_to_regno_p (dregno, dregno+1, + current_function_return_rtx, + (rtx *)0))) + { + /* If the def is to only part of the reg, it does + not kill the other defs that reach here. */ + if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))) + bitmap_clear_bit (live, dregno); + } + } + else + /* This is the return value. */ + if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))) + bitmap_clear_bit (live, dregno); + } + } + else + { + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + unsigned int dregno = DF_REF_REGNO (def); + + /* If the def is to only part of the reg, it does + not kill the other defs that reach here. */ + if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))) + bitmap_clear_bit (live, dregno); + } + } +} + + +/* Simulate the effects of the uses of INSN on LIVE. */ + +void +df_simulate_uses (rtx insn, bitmap live) +{ + struct df_ref **use_rec; + unsigned int uid = INSN_UID (insn); + + for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + /* Add use to set of uses in this BB. */ + bitmap_set_bit (live, DF_REF_REGNO (use)); + } +} + + +/* Add back the always live regs in BB to LIVE. */ + +static inline void +df_simulate_fixup_sets (basic_block bb, bitmap live) +{ + /* These regs are considered always live so if they end up dying + because of some def, we need to bring the back again. */ + if (df_has_eh_preds (bb)) + bitmap_ior_into (live, df->eh_block_artificial_uses); + else + bitmap_ior_into (live, df->regular_block_artificial_uses); +} + + +/* Apply the artifical uses and defs at the top of BB in a forwards + direction. */ + +void +df_simulate_artificial_refs_at_top (basic_block bb, bitmap live) +{ + struct df_ref **def_rec; + struct df_ref **use_rec; + int bb_index = bb->index; + + for (use_rec = df_get_artificial_uses (bb_index); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if (DF_REF_FLAGS (use) & DF_REF_AT_TOP) + bitmap_set_bit (live, DF_REF_REGNO (use)); + } + + for (def_rec = df_get_artificial_defs (bb_index); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if (DF_REF_FLAGS (def) & DF_REF_AT_TOP) + bitmap_clear_bit (live, DF_REF_REGNO (def)); + } +} + + +/* Simulate the forwards effects of INSN on the bitmap LIVE. */ + +void +df_simulate_one_insn_forwards (basic_block bb, rtx insn, bitmap live) +{ + if (! INSN_P (insn)) + return; + + df_simulate_uses (insn, live); + df_simulate_defs (insn, live); + df_simulate_fixup_sets (bb, live); +} + + +/* Apply the artifical uses and defs at the end of BB in a backwards + direction. */ + +void +df_simulate_artificial_refs_at_end (basic_block bb, bitmap live) +{ + struct df_ref **def_rec; + struct df_ref **use_rec; + int bb_index = bb->index; + + for (def_rec = df_get_artificial_defs (bb_index); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if ((DF_REF_FLAGS (def) & DF_REF_AT_TOP) == 0) + bitmap_clear_bit (live, DF_REF_REGNO (def)); + } + + for (use_rec = df_get_artificial_uses (bb_index); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if ((DF_REF_FLAGS (use) & DF_REF_AT_TOP) == 0) + bitmap_set_bit (live, DF_REF_REGNO (use)); + } +} + + +/* Simulate the backwards effects of INSN on the bitmap LIVE. */ + +void +df_simulate_one_insn_backwards (basic_block bb, rtx insn, bitmap live) +{ + if (! INSN_P (insn)) + return; + + df_simulate_defs (insn, live); + df_simulate_uses (insn, live); + df_simulate_fixup_sets (bb, live); +} + + diff --git a/gcc/df-scan.c b/gcc/df-scan.c index d1ebfcc..8c7cbf0 100644 --- a/gcc/df-scan.c +++ b/gcc/df-scan.c @@ -1,9 +1,5 @@ -/* FIXME: We need to go back and add the warning messages about code - moved across setjmp. */ - - /* Scanning of rtl for dataflow analysis. - Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Originally contributed by Michael P. Hayes (m.hayes@elec.canterbury.ac.nz, mhayes@redhat.com) @@ -50,6 +46,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "target.h" #include "target-def.h" #include "df.h" +#include "tree-pass.h" #ifndef HAVE_epilogue #define HAVE_epilogue 0 @@ -82,27 +79,75 @@ bitmap df_invalidated_by_call = NULL; /* Initialize ur_in and ur_out as if all hard registers were partially available. */ -static void df_ref_record (struct dataflow *, rtx, rtx *, - basic_block, rtx, enum df_ref_type, - enum df_ref_flags, bool record_live); -static void df_def_record_1 (struct dataflow *, rtx, basic_block, rtx, - enum df_ref_flags, bool record_live); -static void df_defs_record (struct dataflow *, rtx, basic_block, rtx); -static void df_uses_record (struct dataflow *, rtx *, enum df_ref_type, +struct df_collection_rec +{ + struct df_ref ** def_vec; + unsigned int next_def; + struct df_ref ** use_vec; + unsigned int next_use; + struct df_ref ** eq_use_vec; + unsigned int next_eq_use; + struct df_mw_hardreg **mw_vec; + unsigned int next_mw; +}; + +static struct df_ref * df_null_ref_rec[1]; +static struct df_mw_hardreg * df_null_mw_rec[1]; + +static void df_ref_record (struct df_collection_rec *, + rtx, rtx *, + basic_block, rtx, enum df_ref_type, + enum df_ref_flags); +static void df_def_record_1 (struct df_collection_rec *, + rtx, basic_block, rtx, + enum df_ref_flags); +static void df_defs_record (struct df_collection_rec *, + rtx, basic_block, rtx, + enum df_ref_flags); +static void df_uses_record (struct df_collection_rec *, + rtx *, enum df_ref_type, basic_block, rtx, enum df_ref_flags); -static void df_insn_refs_record (struct dataflow *, basic_block, rtx); -static void df_bb_refs_record (struct dataflow *, basic_block); -static void df_refs_record (struct dataflow *, bitmap); -static struct df_ref *df_ref_create_structure (struct dataflow *, rtx, rtx *, +static struct df_ref *df_ref_create_structure (struct df_collection_rec *, rtx, rtx *, basic_block, rtx, enum df_ref_type, enum df_ref_flags); -static void df_record_entry_block_defs (struct dataflow *); -static void df_record_exit_block_uses (struct dataflow *); -static void df_grow_reg_info (struct dataflow *, struct df_ref_info *); + +static void df_insn_refs_collect (struct df_collection_rec*, + basic_block, rtx); +static void df_canonize_collection_rec (struct df_collection_rec *); + +static void df_get_regular_block_artificial_uses (bitmap); +static void df_get_eh_block_artificial_uses (bitmap); + +static void df_record_entry_block_defs (bitmap); +static void df_record_exit_block_uses (bitmap); +static void df_get_exit_block_use_set (bitmap); +static void df_get_entry_block_def_set (bitmap); static void df_grow_ref_info (struct df_ref_info *, unsigned int); -static void df_grow_insn_info (struct df *); +static void df_ref_chain_delete_du_chain (struct df_ref **); +static void df_ref_chain_delete (struct df_ref **); + +static void df_refs_add_to_chains (struct df_collection_rec *, + basic_block, rtx); + +static bool df_insn_refs_verify (struct df_collection_rec *, basic_block, rtx, bool); +static void df_entry_block_defs_collect (struct df_collection_rec *, bitmap); +static void df_exit_block_uses_collect (struct df_collection_rec *, bitmap); +static void df_install_ref (struct df_ref *, struct df_reg_info *, + struct df_ref_info *, bool); + +static int df_ref_compare (const void *, const void *); +static int df_mw_compare (const void *, const void *); +/* Indexed by hardware reg number, is true if that register is ever + used in the current function. + + In df-scan.c, this is set up to record the hard regs used + explicitly. Reload adds in the hard regs used for holding pseudo + regs. Final uses it to generate the code in the function prologue + and epilogue to save and restore registers as needed. */ + +static bool regs_ever_live[FIRST_PSEUDO_REGISTER]; /*---------------------------------------------------------------------------- SCANNING DATAFLOW PROBLEM @@ -121,77 +166,106 @@ struct df_scan_problem_data alloc_pool reg_pool; alloc_pool mw_reg_pool; alloc_pool mw_link_pool; + bitmap_obstack reg_bitmaps; + bitmap_obstack insn_bitmaps; }; typedef struct df_scan_bb_info *df_scan_bb_info_t; static void -df_scan_free_internal (struct dataflow *dflow) +df_scan_free_internal (void) { - struct df *df = dflow->df; struct df_scan_problem_data *problem_data - = (struct df_scan_problem_data *) dflow->problem_data; + = (struct df_scan_problem_data *) df_scan->problem_data; - free (df->def_info.regs); free (df->def_info.refs); + free (df->def_info.begin); + free (df->def_info.count); memset (&df->def_info, 0, (sizeof (struct df_ref_info))); - free (df->use_info.regs); free (df->use_info.refs); + free (df->use_info.begin); + free (df->use_info.count); memset (&df->use_info, 0, (sizeof (struct df_ref_info))); + free (df->def_regs); + df->def_regs = NULL; + free (df->use_regs); + df->use_regs = NULL; + free (df->eq_use_regs); + df->eq_use_regs = NULL; + df->regs_size = 0; + DF_REG_SIZE(df) = 0; + free (df->insns); df->insns = NULL; - df->insns_size = 0; + DF_INSN_SIZE () = 0; - free (dflow->block_info); - dflow->block_info = NULL; - dflow->block_info_size = 0; + free (df_scan->block_info); + df_scan->block_info = NULL; + df_scan->block_info_size = 0; BITMAP_FREE (df->hardware_regs_used); + BITMAP_FREE (df->regular_block_artificial_uses); + BITMAP_FREE (df->eh_block_artificial_uses); BITMAP_FREE (df->entry_block_defs); BITMAP_FREE (df->exit_block_uses); + BITMAP_FREE (df->insns_to_delete); + BITMAP_FREE (df->insns_to_rescan); + BITMAP_FREE (df->insns_to_notes_rescan); - free_alloc_pool (dflow->block_pool); + free_alloc_pool (df_scan->block_pool); free_alloc_pool (problem_data->ref_pool); free_alloc_pool (problem_data->insn_pool); free_alloc_pool (problem_data->reg_pool); free_alloc_pool (problem_data->mw_reg_pool); free_alloc_pool (problem_data->mw_link_pool); -} - - -/* Get basic block info. */ - -struct df_scan_bb_info * -df_scan_get_bb_info (struct dataflow *dflow, unsigned int index) -{ - gcc_assert (index < dflow->block_info_size); - return (struct df_scan_bb_info *) dflow->block_info[index]; + bitmap_obstack_release (&problem_data->reg_bitmaps); + bitmap_obstack_release (&problem_data->insn_bitmaps); + free (df_scan->problem_data); } /* Set basic block info. */ static void -df_scan_set_bb_info (struct dataflow *dflow, unsigned int index, +df_scan_set_bb_info (unsigned int index, struct df_scan_bb_info *bb_info) { - gcc_assert (index < dflow->block_info_size); - dflow->block_info[index] = (void *) bb_info; + gcc_assert (df_scan); + df_grow_bb_info (df_scan); + df_scan->block_info[index] = (void *) bb_info; } /* Free basic block info. */ static void -df_scan_free_bb_info (struct dataflow *dflow, basic_block bb, void *vbb_info) +df_scan_free_bb_info (basic_block bb, void *vbb_info) { struct df_scan_bb_info *bb_info = (struct df_scan_bb_info *) vbb_info; + unsigned int bb_index = bb->index; if (bb_info) { - df_bb_refs_delete (dflow, bb->index); - pool_free (dflow->block_pool, bb_info); + rtx insn; + FOR_BB_INSNS (bb, insn) + { + if (INSN_P (insn)) + /* Record defs within INSN. */ + df_insn_delete (bb, INSN_UID (insn)); + } + + if (bb_index < df_scan->block_info_size) + bb_info = df_scan_get_bb_info (bb_index); + + /* Get rid of any artificial uses or defs. */ + df_ref_chain_delete_du_chain (bb_info->artificial_defs); + df_ref_chain_delete_du_chain (bb_info->artificial_uses); + df_ref_chain_delete (bb_info->artificial_defs); + df_ref_chain_delete (bb_info->artificial_uses); + bb_info->artificial_defs = NULL; + bb_info->artificial_uses = NULL; + pool_free (df_scan->block_pool, bb_info); } } @@ -199,30 +273,27 @@ df_scan_free_bb_info (struct dataflow *dflow, basic_block bb, void *vbb_info) /* Allocate the problem data for the scanning problem. This should be called when the problem is created or when the entire function is to be rescanned. */ - -static void -df_scan_alloc (struct dataflow *dflow, bitmap blocks_to_rescan, - bitmap all_blocks ATTRIBUTE_UNUSED) +void +df_scan_alloc (bitmap all_blocks ATTRIBUTE_UNUSED) { - struct df *df = dflow->df; struct df_scan_problem_data *problem_data; unsigned int insn_num = get_max_uid () + 1; - unsigned int block_size = 50; - unsigned int bb_index; - bitmap_iterator bi; + unsigned int block_size = 400; + basic_block bb; /* Given the number of pools, this is really faster than tearing everything apart. */ - if (dflow->problem_data) - df_scan_free_internal (dflow); + if (df_scan->problem_data) + df_scan_free_internal (); - dflow->block_pool + df_scan->block_pool = create_alloc_pool ("df_scan_block pool", sizeof (struct df_scan_bb_info), block_size); problem_data = XNEW (struct df_scan_problem_data); - dflow->problem_data = problem_data; + df_scan->problem_data = problem_data; + df_scan->computed = true; problem_data->ref_pool = create_alloc_pool ("df_scan_ref pool", @@ -240,77 +311,107 @@ df_scan_alloc (struct dataflow *dflow, bitmap blocks_to_rescan, = create_alloc_pool ("df_scan_mw_link pool", sizeof (struct df_link), block_size); - insn_num += insn_num / 4; - df_grow_reg_info (dflow, &df->def_info); - df_grow_ref_info (&df->def_info, insn_num); + bitmap_obstack_initialize (&problem_data->reg_bitmaps); + bitmap_obstack_initialize (&problem_data->insn_bitmaps); - df_grow_reg_info (dflow, &df->use_info); - df_grow_ref_info (&df->use_info, insn_num *2); + insn_num += insn_num / 4; + df_grow_reg_info (); - df_grow_insn_info (df); - df_grow_bb_info (dflow); + df_grow_insn_info (); + df_grow_bb_info (df_scan); - EXECUTE_IF_SET_IN_BITMAP (blocks_to_rescan, 0, bb_index, bi) + FOR_ALL_BB (bb) { - struct df_scan_bb_info *bb_info = df_scan_get_bb_info (dflow, bb_index); + unsigned int bb_index = bb->index; + struct df_scan_bb_info *bb_info = df_scan_get_bb_info (bb_index); if (!bb_info) { - bb_info = (struct df_scan_bb_info *) pool_alloc (dflow->block_pool); - df_scan_set_bb_info (dflow, bb_index, bb_info); + bb_info = (struct df_scan_bb_info *) pool_alloc (df_scan->block_pool); + df_scan_set_bb_info (bb_index, bb_info); } bb_info->artificial_defs = NULL; bb_info->artificial_uses = NULL; } - df->hardware_regs_used = BITMAP_ALLOC (NULL); - df->entry_block_defs = BITMAP_ALLOC (NULL); - df->exit_block_uses = BITMAP_ALLOC (NULL); + df->hardware_regs_used = BITMAP_ALLOC (&problem_data->reg_bitmaps); + df->regular_block_artificial_uses = BITMAP_ALLOC (&problem_data->reg_bitmaps); + df->eh_block_artificial_uses = BITMAP_ALLOC (&problem_data->reg_bitmaps); + df->entry_block_defs = BITMAP_ALLOC (&problem_data->reg_bitmaps); + df->exit_block_uses = BITMAP_ALLOC (&problem_data->reg_bitmaps); + df->insns_to_delete = BITMAP_ALLOC (&problem_data->insn_bitmaps); + df->insns_to_rescan = BITMAP_ALLOC (&problem_data->insn_bitmaps); + df->insns_to_notes_rescan = BITMAP_ALLOC (&problem_data->insn_bitmaps); } /* Free all of the data associated with the scan problem. */ static void -df_scan_free (struct dataflow *dflow) +df_scan_free (void) { - struct df *df = dflow->df; - - if (dflow->problem_data) - { - df_scan_free_internal (dflow); - free (dflow->problem_data); - } + if (df_scan->problem_data) + df_scan_free_internal (); - if (df->blocks_to_scan) - BITMAP_FREE (df->blocks_to_scan); - if (df->blocks_to_analyze) - BITMAP_FREE (df->blocks_to_analyze); + { + BITMAP_FREE (df->blocks_to_analyze); + df->blocks_to_analyze = NULL; + } - free (dflow); + free (df_scan); } +/* Dump the preamble for DF_SCAN dump. */ static void -df_scan_dump (struct dataflow *dflow ATTRIBUTE_UNUSED, FILE *file ATTRIBUTE_UNUSED) +df_scan_start_dump (FILE *file ATTRIBUTE_UNUSED) { - struct df *df = dflow->df; int i; - fprintf (file, " invalidated by call \t"); - dump_bitmap (file, df_invalidated_by_call); - fprintf (file, " hardware regs used \t"); - dump_bitmap (file, df->hardware_regs_used); - fprintf (file, " entry block uses \t"); - dump_bitmap (file, df->entry_block_defs); - fprintf (file, " exit block uses \t"); - dump_bitmap (file, df->exit_block_uses); - fprintf (file, " regs ever live \t"); + fprintf (file, ";; invalidated by call \t"); + df_print_regset (file, df_invalidated_by_call); + fprintf (file, ";; hardware regs used \t"); + df_print_regset (file, df->hardware_regs_used); + fprintf (file, ";; regular block artificial uses \t"); + df_print_regset (file, df->regular_block_artificial_uses); + fprintf (file, ";; eh block artificial uses \t"); + df_print_regset (file, df->eh_block_artificial_uses); + fprintf (file, ";; entry block defs \t"); + df_print_regset (file, df->entry_block_defs); + fprintf (file, ";; exit block uses \t"); + df_print_regset (file, df->exit_block_uses); + fprintf (file, ";; regs ever live \t"); for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (regs_ever_live[i]) - fprintf (file, "%d ", i); + if (df_regs_ever_live_p (i)) + fprintf (file, " %d[%s]", i, reg_names[i]); + fprintf (file, "\n"); } +/* Dump the bb_info for a given basic block. */ +static void +df_scan_start_block (basic_block bb, FILE *file) +{ + struct df_scan_bb_info *bb_info + = df_scan_get_bb_info (bb->index); + + if (bb_info) + { + fprintf (file, ";; bb %d artificial_defs: ", bb->index); + df_refs_chain_dump (bb_info->artificial_defs, true, file); + fprintf (file, "\n;; bb %d artificial_uses: ", bb->index); + df_refs_chain_dump (bb_info->artificial_uses, true, file); + fprintf (file, "\n"); + } +#if 0 + { + rtx insn; + FOR_BB_INSNS (bb, insn) + if (INSN_P (insn)) + df_insn_debug (insn, false, file); + } +#endif +} + static struct df_problem problem_SCAN = { DF_SCAN, /* Problem id. */ @@ -326,9 +427,14 @@ static struct df_problem problem_SCAN = NULL, /* Transfer function. */ NULL, /* Finalize function. */ df_scan_free, /* Free all of the problem information. */ - df_scan_dump, /* Debugging. */ + NULL, /* Remove this problem from the stack of dataflow problems. */ + df_scan_start_dump, /* Debugging. */ + df_scan_start_block, /* Debugging start block. */ + NULL, /* Debugging end block. */ + NULL, /* Incremental solution verify start. */ + NULL, /* Incremental solution verfiy end. */ NULL, /* Dependent problem. */ - 0 /* Changeable flags. */ + TV_DF_SCAN /* Timing variable. */ }; @@ -336,12 +442,13 @@ static struct df_problem problem_SCAN = of DF. The returned structure is what is used to get at the solution. */ -struct dataflow * -df_scan_add_problem (struct df *df, int flags) +void +df_scan_add_problem (void) { - return df_add_problem (df, &problem_SCAN, flags); + df_add_problem (&problem_SCAN); } + /*---------------------------------------------------------------------------- Storage Allocation Utilities ----------------------------------------------------------------------------*/ @@ -354,31 +461,55 @@ df_scan_add_problem (struct df *df, int flags) Second, assure that all of the slots up to max_reg_num have been filled with reg_info structures. */ -static void -df_grow_reg_info (struct dataflow *dflow, struct df_ref_info *ref_info) +void +df_grow_reg_info (void) { unsigned int max_reg = max_reg_num (); unsigned int new_size = max_reg; struct df_scan_problem_data *problem_data - = (struct df_scan_problem_data *) dflow->problem_data; + = (struct df_scan_problem_data *) df_scan->problem_data; unsigned int i; - if (ref_info->regs_size < new_size) + if (df->regs_size < new_size) { new_size += new_size / 4; - ref_info->regs = xrealloc (ref_info->regs, - new_size *sizeof (struct df_reg_info*)); - ref_info->regs_size = new_size; + df->def_regs = xrealloc (df->def_regs, + new_size *sizeof (struct df_reg_info*)); + df->use_regs = xrealloc (df->use_regs, + new_size *sizeof (struct df_reg_info*)); + df->eq_use_regs = xrealloc (df->eq_use_regs, + new_size *sizeof (struct df_reg_info*)); + df->def_info.begin = xrealloc (df->def_info.begin, + new_size *sizeof (int)); + df->def_info.count = xrealloc (df->def_info.count, + new_size *sizeof (int)); + df->use_info.begin = xrealloc (df->use_info.begin, + new_size *sizeof (int)); + df->use_info.count = xrealloc (df->use_info.count, + new_size *sizeof (int)); + df->regs_size = new_size; } - for (i = ref_info->regs_inited; i < max_reg; i++) + for (i = df->regs_inited; i < max_reg; i++) { - struct df_reg_info *reg_info = pool_alloc (problem_data->reg_pool); + struct df_reg_info *reg_info; + + reg_info = pool_alloc (problem_data->reg_pool); + memset (reg_info, 0, sizeof (struct df_reg_info)); + df->def_regs[i] = reg_info; + reg_info = pool_alloc (problem_data->reg_pool); memset (reg_info, 0, sizeof (struct df_reg_info)); - ref_info->regs[i] = reg_info; + df->use_regs[i] = reg_info; + reg_info = pool_alloc (problem_data->reg_pool); + memset (reg_info, 0, sizeof (struct df_reg_info)); + df->eq_use_regs[i] = reg_info; + df->def_info.begin[i] = 0; + df->def_info.count[i] = 0; + df->use_info.begin[i] = 0; + df->use_info.count[i] = 0; } - ref_info->regs_inited = max_reg; + df->regs_inited = max_reg; } @@ -398,22 +529,40 @@ df_grow_ref_info (struct df_ref_info *ref_info, unsigned int new_size) } +/* Check and grow the ref information if necessary. This routine + guarantees total_size + BITMAP_ADDEND amount of entries in refs + array. It updates ref_info->refs_size only and does not change + ref_info->total_size. */ + +static void +df_check_and_grow_ref_info (struct df_ref_info *ref_info, + unsigned bitmap_addend) +{ + if (ref_info->refs_size < ref_info->total_size + bitmap_addend) + { + int new_size = ref_info->total_size + bitmap_addend; + new_size += ref_info->total_size / 4; + df_grow_ref_info (ref_info, new_size); + } +} + + /* Grow the ref information. If the current size is less than the number of instructions, grow to 25% more than the number of instructions. */ -static void -df_grow_insn_info (struct df *df) +void +df_grow_insn_info (void) { unsigned int new_size = get_max_uid () + 1; - if (df->insns_size < new_size) + if (DF_INSN_SIZE () < new_size) { new_size += new_size / 4; df->insns = xrealloc (df->insns, new_size *sizeof (struct df_insn_info *)); memset (df->insns + df->insns_size, 0, - (new_size - df->insns_size) *sizeof (struct df_insn_info *)); - df->insns_size = new_size; + (new_size - DF_INSN_SIZE ()) *sizeof (struct df_insn_info *)); + DF_INSN_SIZE () = new_size; } } @@ -424,499 +573,1543 @@ df_grow_insn_info (struct df *df) PUBLIC INTERFACES FOR SMALL GRAIN CHANGES TO SCANNING. ----------------------------------------------------------------------------*/ -/* Rescan some BLOCKS or all the blocks defined by the last call to - df_set_blocks if BLOCKS is NULL); */ +/* Rescan all of the block_to_analyze or all of the blocks in the + function if df_set_blocks if blocks_to_analyze is NULL; */ void -df_rescan_blocks (struct df *df, bitmap blocks) +df_scan_blocks (void) { - bitmap local_blocks_to_scan = BITMAP_ALLOC (NULL); - - struct dataflow *dflow = df->problems_by_index[DF_SCAN]; basic_block bb; - df->def_info.refs_organized_size = 0; - df->use_info.refs_organized_size = 0; + df->def_info.ref_order = DF_REF_ORDER_NO_TABLE; + df->use_info.ref_order = DF_REF_ORDER_NO_TABLE; - if (blocks) - { - int i; - unsigned int bb_index; - bitmap_iterator bi; - bool cleared_bits = false; + df_get_regular_block_artificial_uses (df->regular_block_artificial_uses); + df_get_eh_block_artificial_uses (df->eh_block_artificial_uses); - /* Need to assure that there are space in all of the tables. */ - unsigned int insn_num = get_max_uid () + 1; - insn_num += insn_num / 4; + bitmap_ior_into (df->eh_block_artificial_uses, + df->regular_block_artificial_uses); - df_grow_reg_info (dflow, &df->def_info); - df_grow_ref_info (&df->def_info, insn_num); - - df_grow_reg_info (dflow, &df->use_info); - df_grow_ref_info (&df->use_info, insn_num *2); - - df_grow_insn_info (df); - df_grow_bb_info (dflow); + /* ENTRY and EXIT blocks have special defs/uses. */ + df_get_entry_block_def_set (df->entry_block_defs); + df_record_entry_block_defs (df->entry_block_defs); + df_get_exit_block_use_set (df->exit_block_uses); + df_record_exit_block_uses (df->exit_block_uses); + df_set_bb_dirty (BASIC_BLOCK (ENTRY_BLOCK)); + df_set_bb_dirty (BASIC_BLOCK (EXIT_BLOCK)); - bitmap_copy (local_blocks_to_scan, blocks); + /* Regular blocks */ + FOR_EACH_BB (bb) + { + unsigned int bb_index = bb->index; + df_bb_refs_record (bb_index, true); + } +} - EXECUTE_IF_SET_IN_BITMAP (blocks, 0, bb_index, bi) - { - basic_block bb = BASIC_BLOCK (bb_index); - if (!bb) - { - bitmap_clear_bit (local_blocks_to_scan, bb_index); - cleared_bits = true; - } - } - if (cleared_bits) - bitmap_copy (blocks, local_blocks_to_scan); +/* Create a new ref of type DF_REF_TYPE for register REG at address + LOC within INSN of BB. */ - df->def_info.add_refs_inline = true; - df->use_info.add_refs_inline = true; +struct df_ref * +df_ref_create (rtx reg, rtx *loc, rtx insn, + basic_block bb, + enum df_ref_type ref_type, + enum df_ref_flags ref_flags) +{ + struct df_ref *ref; + struct df_reg_info **reg_info; + struct df_ref_info *ref_info; + struct df_ref **ref_rec; + struct df_ref ***ref_rec_ptr; + unsigned int count = 0; + bool add_to_table; + + df_grow_reg_info (); + + /* You cannot hack artificial refs. */ + gcc_assert (insn); + ref = df_ref_create_structure (NULL, reg, loc, bb, insn, + ref_type, ref_flags); - for (i = df->num_problems_defined; i; i--) + if (DF_REF_TYPE (ref) == DF_REF_REG_DEF) + { + reg_info = df->def_regs; + ref_info = &df->def_info; + ref_rec_ptr = &DF_INSN_DEFS (insn); + add_to_table = ref_info->ref_order != DF_REF_ORDER_NO_TABLE; + } + else if (DF_REF_FLAGS (ref) & DF_REF_IN_NOTE) + { + reg_info = df->eq_use_regs; + ref_info = &df->use_info; + ref_rec_ptr = &DF_INSN_EQ_USES (insn); + switch (ref_info->ref_order) { - bitmap blocks_to_reset = NULL; - if (dflow->problem->reset_fun) - { - if (!blocks_to_reset) - { - blocks_to_reset = BITMAP_ALLOC (NULL); - bitmap_copy (blocks_to_reset, local_blocks_to_scan); - if (df->blocks_to_scan) - bitmap_ior_into (blocks_to_reset, df->blocks_to_scan); - } - dflow->problem->reset_fun (dflow, blocks_to_reset); - } - if (blocks_to_reset) - BITMAP_FREE (blocks_to_reset); + case DF_REF_ORDER_UNORDERED_WITH_NOTES: + case DF_REF_ORDER_BY_REG_WITH_NOTES: + case DF_REF_ORDER_BY_INSN_WITH_NOTES: + add_to_table = true; + break; + default: + add_to_table = false; + break; } + } + else + { + reg_info = df->use_regs; + ref_info = &df->use_info; + ref_rec_ptr = &DF_INSN_USES (insn); + add_to_table = ref_info->ref_order != DF_REF_ORDER_NO_TABLE; + } - df_refs_delete (dflow, local_blocks_to_scan); + /* Do not add if ref is not in the right blocks. */ + if (add_to_table && df->analyze_subset) + add_to_table = bitmap_bit_p (df->blocks_to_analyze, bb->index); - /* This may be a mistake, but if an explicit blocks is passed in - and the set of blocks to analyze has been explicitly set, add - the extra blocks to blocks_to_analyze. The alternative is to - put an assert here. We do not want this to just go by - silently or else we may get storage leaks. */ - if (df->blocks_to_analyze) - bitmap_ior_into (df->blocks_to_analyze, blocks); + df_install_ref (ref, reg_info[DF_REF_REGNO (ref)], ref_info, add_to_table); + + if (add_to_table) + switch (ref_info->ref_order) + { + case DF_REF_ORDER_UNORDERED_WITH_NOTES: + case DF_REF_ORDER_BY_REG_WITH_NOTES: + case DF_REF_ORDER_BY_INSN_WITH_NOTES: + ref_info->ref_order = DF_REF_ORDER_UNORDERED_WITH_NOTES; + break; + default: + ref_info->ref_order = DF_REF_ORDER_UNORDERED; + break; + } + + ref_rec = *ref_rec_ptr; + while (*ref_rec) + { + count++; + ref_rec++; + } + + ref_rec = *ref_rec_ptr; + if (count) + { + ref_rec = xrealloc (ref_rec, (count+2) * sizeof (struct df_ref*)); + *ref_rec_ptr = ref_rec; + ref_rec[count] = ref; + ref_rec[count+1] = NULL; + qsort (ref_rec, count + 1, sizeof (struct df_ref *), df_ref_compare); } else { - /* If we are going to do everything, just reallocate everything. - Most stuff is allocated in pools so this is faster than - walking it. */ - if (df->blocks_to_analyze) - bitmap_copy (local_blocks_to_scan, df->blocks_to_analyze); - else - FOR_ALL_BB (bb) - { - bitmap_set_bit (local_blocks_to_scan, bb->index); - } - df_scan_alloc (dflow, local_blocks_to_scan, NULL); - - df->def_info.add_refs_inline = false; - df->use_info.add_refs_inline = false; + struct df_ref **ref_rec = XNEWVEC (struct df_ref*, 2); + ref_rec[0] = ref; + ref_rec[1] = NULL; + *ref_rec_ptr = ref_rec; } - df_refs_record (dflow, local_blocks_to_scan); #if 0 - bitmap_print (stderr, local_blocks_to_scan, "scanning: ", "\n"); + if (dump_file) + { + fprintf (dump_file, "adding ref "); + df_ref_debug (ref, dump_file); + } #endif - - if (!df->blocks_to_scan) - df->blocks_to_scan = BITMAP_ALLOC (NULL); + /* By adding the ref directly, df_insn_rescan my not find any + differences even though the block will have changed. So we need + to mark the block dirty ourselves. */ + df_set_bb_dirty (bb); - bitmap_ior_into (df->blocks_to_scan, local_blocks_to_scan); - BITMAP_FREE (local_blocks_to_scan); + return ref; } -/* Create a new ref of type DF_REF_TYPE for register REG at address - LOC within INSN of BB. */ + +/*---------------------------------------------------------------------------- + UTILITIES TO CREATE AND DESTROY REFS AND CHAINS. +----------------------------------------------------------------------------*/ -struct df_ref * -df_ref_create (struct df *df, rtx reg, rtx *loc, rtx insn, - basic_block bb, - enum df_ref_type ref_type, - enum df_ref_flags ref_flags) + +/* Unlink and delete REF at the reg_use, reg_eq_use or reg_def chain. + Also delete the def-use or use-def chain if it exists. */ + +static void +df_reg_chain_unlink (struct df_ref *ref) { - struct dataflow *dflow = df->problems_by_index[DF_SCAN]; - struct df_scan_bb_info *bb_info; + struct df_ref *next = DF_REF_NEXT_REG (ref); + struct df_ref *prev = DF_REF_PREV_REG (ref); + struct df_scan_problem_data *problem_data + = (struct df_scan_problem_data *) df_scan->problem_data; + int id = DF_REF_ID (ref); + struct df_reg_info *reg_info; + struct df_ref **refs = NULL; + + if (DF_REF_TYPE (ref) == DF_REF_REG_DEF) + { + reg_info = DF_REG_DEF_GET (DF_REF_REGNO (ref)); + refs = df->def_info.refs; + } + else + { + if (DF_REF_FLAGS (ref) & DF_REF_IN_NOTE) + { + reg_info = DF_REG_EQ_USE_GET (DF_REF_REGNO (ref)); + switch (df->use_info.ref_order) + { + case DF_REF_ORDER_UNORDERED_WITH_NOTES: + case DF_REF_ORDER_BY_REG_WITH_NOTES: + case DF_REF_ORDER_BY_INSN_WITH_NOTES: + refs = df->use_info.refs; + break; + default: + break; + } + } + else + { + reg_info = DF_REG_USE_GET (DF_REF_REGNO (ref)); + refs = df->use_info.refs; + } + } + + if (refs) + { + if (df->analyze_subset) + { + if (bitmap_bit_p (df->blocks_to_analyze, DF_REF_BB (ref)->index)) + refs[id] = NULL; + } + else + refs[id] = NULL; + } - df_grow_reg_info (dflow, &df->use_info); - df_grow_reg_info (dflow, &df->def_info); - df_grow_bb_info (dflow); + /* Delete any def-use or use-def chains that start here. It is + possible that there is trash in this field. This happens for + insns that have been deleted when rescanning has been deferred + and the chain problem has also been deleted. The chain tear down + code skips deleted insns. */ + if (df_chain && DF_REF_CHAIN (ref)) + df_chain_unlink (ref); - /* Make sure there is the bb_info for this block. */ - bb_info = df_scan_get_bb_info (dflow, bb->index); - if (!bb_info) + reg_info->n_refs--; + if (DF_REF_FLAGS_IS_SET (ref, DF_HARD_REG_LIVE)) { - bb_info = (struct df_scan_bb_info *) pool_alloc (dflow->block_pool); - df_scan_set_bb_info (dflow, bb->index, bb_info); - bb_info->artificial_defs = NULL; - bb_info->artificial_uses = NULL; + gcc_assert (DF_REF_REGNO (ref) < FIRST_PSEUDO_REGISTER); + df->hard_regs_live_count[DF_REF_REGNO (ref)]--; } - if (ref_type == DF_REF_REG_DEF) - df->def_info.add_refs_inline = true; + /* Unlink from the reg chain. If there is no prev, this is the + first of the list. If not, just join the next and prev. */ + if (prev) + DF_REF_NEXT_REG (prev) = next; else - df->use_info.add_refs_inline = true; - - return df_ref_create_structure (dflow, reg, loc, bb, insn, ref_type, ref_flags); + { + gcc_assert (reg_info->reg_chain == ref); + reg_info->reg_chain = next; + } + if (next) + DF_REF_PREV_REG (next) = prev; + + pool_free (problem_data->ref_pool, ref); } - -/*---------------------------------------------------------------------------- - UTILITIES TO CREATE AND DESTROY REFS AND CHAINS. -----------------------------------------------------------------------------*/ +/* Remove REF from VEC. */ +static void +df_ref_compress_rec (struct df_ref ***vec_ptr, struct df_ref *ref) +{ + struct df_ref **vec = *vec_ptr; -/* Get the artificial uses for a basic block. */ + if (vec[1]) + { + while (*vec && *vec != ref) + vec++; + + while (*vec) + { + *vec = *(vec+1); + vec++; + } + } + else + { + free (vec); + *vec_ptr = df_null_ref_rec; + } +} -struct df_ref * -df_get_artificial_defs (struct df *df, unsigned int bb_index) + +/* Unlink REF from all def-use/use-def chains, etc. */ + +void +df_ref_remove (struct df_ref *ref) { - struct dataflow *dflow = df->problems_by_index[DF_SCAN]; - return df_scan_get_bb_info (dflow, bb_index)->artificial_defs; +#if 0 + if (dump_file) + { + fprintf (dump_file, "removing ref "); + df_ref_debug (ref, dump_file); + } +#endif + + if (DF_REF_REG_DEF_P (ref)) + { + if (DF_REF_IS_ARTIFICIAL (ref)) + { + struct df_scan_bb_info *bb_info + = df_scan_get_bb_info (DF_REF_BB (ref)->index); + df_ref_compress_rec (&bb_info->artificial_defs, ref); + } + else + { + unsigned int uid = DF_REF_INSN_UID (ref); + struct df_insn_info *insn_rec = DF_INSN_UID_GET (uid); + df_ref_compress_rec (&insn_rec->defs, ref); + } + } + else + { + if (DF_REF_IS_ARTIFICIAL (ref)) + { + struct df_scan_bb_info *bb_info + = df_scan_get_bb_info (DF_REF_BB (ref)->index); + df_ref_compress_rec (&bb_info->artificial_uses, ref); + } + else + { + unsigned int uid = DF_REF_INSN_UID (ref); + struct df_insn_info *insn_rec = DF_INSN_UID_GET (uid); + + if (DF_REF_FLAGS (ref) & DF_REF_IN_NOTE) + df_ref_compress_rec (&insn_rec->eq_uses, ref); + else + df_ref_compress_rec (&insn_rec->uses, ref); + } + } + + /* By deleting the ref directly, df_insn_rescan my not find any + differences even though the block will have changed. So we need + to mark the block dirty ourselves. */ + df_set_bb_dirty (DF_REF_BB (ref)); + df_reg_chain_unlink (ref); } -/* Get the artificial uses for a basic block. */ +/* Create the insn record for INSN. If there was one there, zero it + out. */ -struct df_ref * -df_get_artificial_uses (struct df *df, unsigned int bb_index) +struct df_insn_info * +df_insn_create_insn_record (rtx insn) { - struct dataflow *dflow = df->problems_by_index[DF_SCAN]; - return df_scan_get_bb_info (dflow, bb_index)->artificial_uses; + struct df_scan_problem_data *problem_data + = (struct df_scan_problem_data *) df_scan->problem_data; + struct df_insn_info *insn_rec; + + df_grow_insn_info (); + insn_rec = DF_INSN_GET (insn); + if (!insn_rec) + { + insn_rec = pool_alloc (problem_data->insn_pool); + DF_INSN_SET (insn, insn_rec); + } + memset (insn_rec, 0, sizeof (struct df_insn_info)); + insn_rec->insn = insn; + return insn_rec; } -/* Link REF at the front of reg_use or reg_def chain for REGNO. */ +/* Delete all du chain (DF_REF_CHAIN()) of all refs in the ref chain. */ -void -df_reg_chain_create (struct df_reg_info *reg_info, - struct df_ref *ref) +static void +df_ref_chain_delete_du_chain (struct df_ref **ref_rec) { - struct df_ref *head = reg_info->reg_chain; - reg_info->reg_chain = ref; + while (*ref_rec) + { + struct df_ref *ref = *ref_rec; + /* CHAIN is allocated by DF_CHAIN. So make sure to + pass df_scan instance for the problem. */ + if (DF_REF_CHAIN (ref)) + df_chain_unlink (ref); + ref_rec++; + } +} - DF_REF_NEXT_REG (ref) = head; - /* We cannot actually link to the head of the chain. */ - DF_REF_PREV_REG (ref) = NULL; +/* Delete all refs in the ref chain. */ - if (head) - DF_REF_PREV_REG (head) = ref; +static void +df_ref_chain_delete (struct df_ref **ref_rec) +{ + struct df_ref **start = ref_rec; + while (*ref_rec) + { + df_reg_chain_unlink (*ref_rec); + ref_rec++; + } + + /* If the list is empty, it has a special shared element that is not + to be deleted. */ + if (*start) + free (start); } -/* Remove REF from the CHAIN. Return the head of the chain. This - will be CHAIN unless the REF was at the beginning of the chain. */ +/* Delete the hardreg chain. */ -static struct df_ref * -df_ref_unlink (struct df_ref *chain, struct df_ref *ref) +static void +df_mw_hardreg_chain_delete (struct df_mw_hardreg **hardregs) { - struct df_ref *orig_chain = chain; - struct df_ref *prev = NULL; - while (chain) + struct df_scan_problem_data *problem_data; + + if (!hardregs) + return; + + problem_data = (struct df_scan_problem_data *) df_scan->problem_data; + + while (*hardregs) { - if (chain == ref) + pool_free (problem_data->mw_reg_pool, *hardregs); + hardregs++; + } +} + + +/* Delete all of the refs information from INSN. BB must be passed in + except when called from df_process_deferred_rescans to mark the block + as dirty. */ + +void +df_insn_delete (basic_block bb, unsigned int uid) +{ + struct df_insn_info *insn_info = NULL; + if (!df) + return; + + df_grow_bb_info (df_scan); + df_grow_reg_info (); + + /* The block must be marked as dirty now, rather than later as in + df_insn_rescan and df_notes_rescan because it may not be there at + rescanning time and the mark would blow up. */ + if (bb) + df_set_bb_dirty (bb); + + insn_info = DF_INSN_UID_SAFE_GET (uid); + + /* The client has defered rescanning. */ + if (df->changeable_flags & DF_DEFER_INSN_RESCAN) + { + if (insn_info) { - if (prev) - { - prev->next_ref = ref->next_ref; - ref->next_ref = NULL; - return orig_chain; - } - else + bitmap_clear_bit (df->insns_to_rescan, uid); + bitmap_clear_bit (df->insns_to_notes_rescan, uid); + bitmap_set_bit (df->insns_to_delete, uid); + } + if (dump_file) + fprintf (dump_file, "defering deletion of insn with uid = %d.\n", uid); + return; + } + + if (dump_file) + fprintf (dump_file, "deleting insn with uid = %d.\n", uid); + + bitmap_clear_bit (df->insns_to_delete, uid); + bitmap_clear_bit (df->insns_to_rescan, uid); + bitmap_clear_bit (df->insns_to_notes_rescan, uid); + if (insn_info) + { + struct df_scan_problem_data *problem_data + = (struct df_scan_problem_data *) df_scan->problem_data; + + /* In general, notes do not have the insn_info fields + initialized. However, combine deletes insns by changing them + to notes. How clever. So we cannot just check if it is a + valid insn before short circuiting this code, we need to see + if we actually initialized it. */ + if (insn_info->defs) + { + df_mw_hardreg_chain_delete (insn_info->mw_hardregs); + + if (df_chain) { - chain = ref->next_ref; - ref->next_ref = NULL; - return chain; + df_ref_chain_delete_du_chain (insn_info->defs); + df_ref_chain_delete_du_chain (insn_info->uses); + df_ref_chain_delete_du_chain (insn_info->eq_uses); } + + df_ref_chain_delete (insn_info->defs); + df_ref_chain_delete (insn_info->uses); + df_ref_chain_delete (insn_info->eq_uses); } - - prev = chain; - chain = chain->next_ref; + pool_free (problem_data->insn_pool, insn_info); + DF_INSN_UID_SET (uid, NULL); } +} + - /* Someone passed in a ref that was not in the chain. */ - gcc_unreachable (); - return NULL; +/* Free all of the refs and the mw_hardregs in COLLECTION_REC. */ + +static void +df_free_collection_rec (struct df_collection_rec *collection_rec) +{ + struct df_scan_problem_data *problem_data + = (struct df_scan_problem_data *) df_scan->problem_data; + struct df_ref **ref; + struct df_mw_hardreg **mw; + + if (collection_rec->def_vec) + for (ref = collection_rec->def_vec; *ref; ref++) + pool_free (problem_data->ref_pool, *ref); + if (collection_rec->use_vec) + for (ref = collection_rec->use_vec; *ref; ref++) + pool_free (problem_data->ref_pool, *ref); + if (collection_rec->eq_use_vec) + for (ref = collection_rec->eq_use_vec; *ref; ref++) + pool_free (problem_data->ref_pool, *ref); + if (collection_rec->mw_vec) + for (mw = collection_rec->mw_vec; *mw; mw++) + pool_free (problem_data->mw_reg_pool, *mw); } -/* Unlink and delete REF at the reg_use or reg_def chain. Also delete - the def-use or use-def chain if it exists. Returns the next ref in - uses or defs chain. */ +/* Rescan INSN. Return TRUE if the rescanning produced any changes. */ -struct df_ref * -df_reg_chain_unlink (struct dataflow *dflow, struct df_ref *ref) +bool +df_insn_rescan (rtx insn) { - struct df *df = dflow->df; - struct df_ref *next = DF_REF_NEXT_REG (ref); - struct df_ref *prev = DF_REF_PREV_REG (ref); - struct df_scan_problem_data *problem_data - = (struct df_scan_problem_data *) dflow->problem_data; - struct df_reg_info *reg_info; - struct df_ref *next_ref = ref->next_ref; - unsigned int id = DF_REF_ID (ref); + unsigned int uid = INSN_UID (insn); + struct df_insn_info *insn_info = NULL; + basic_block bb = BLOCK_FOR_INSN (insn); + struct df_collection_rec collection_rec; + collection_rec.def_vec = alloca (sizeof (struct df_ref*) * 1000); + collection_rec.use_vec = alloca (sizeof (struct df_ref*) * 1000); + collection_rec.eq_use_vec = alloca (sizeof (struct df_ref*) * 1000); + collection_rec.mw_vec = alloca (sizeof (struct df_mw_hardreg*) * 100); + + if ((!df) || (!INSN_P (insn))) + return false; - if (DF_REF_TYPE (ref) == DF_REF_REG_DEF) + if (!bb) { - reg_info = DF_REG_DEF_GET (df, DF_REF_REGNO (ref)); - df->def_info.bitmap_size--; - if (df->def_info.refs && (id < df->def_info.refs_size)) - DF_DEFS_SET (df, id, NULL); + if (dump_file) + fprintf (dump_file, "no bb for insn with uid = %d.\n", uid); + return false; } - else + + /* The client has disabled rescanning and plans to do it itself. */ + if (df->changeable_flags & DF_NO_INSN_RESCAN) + return false; + + df_grow_bb_info (df_scan); + df_grow_reg_info (); + + insn_info = DF_INSN_UID_SAFE_GET (uid); + + /* The client has defered rescanning. */ + if (df->changeable_flags & DF_DEFER_INSN_RESCAN) { - reg_info = DF_REG_USE_GET (df, DF_REF_REGNO (ref)); - df->use_info.bitmap_size--; - if (df->use_info.refs && (id < df->use_info.refs_size)) - DF_USES_SET (df, id, NULL); + if (!insn_info) + { + insn_info = df_insn_create_insn_record (insn); + insn_info->defs = df_null_ref_rec; + insn_info->uses = df_null_ref_rec; + insn_info->eq_uses = df_null_ref_rec; + insn_info->mw_hardregs = df_null_mw_rec; + } + if (dump_file) + fprintf (dump_file, "defering rescan insn with uid = %d.\n", uid); + + bitmap_clear_bit (df->insns_to_delete, uid); + bitmap_clear_bit (df->insns_to_notes_rescan, uid); + bitmap_set_bit (df->insns_to_rescan, INSN_UID (insn)); + return false; } - - /* Delete any def-use or use-def chains that start here. */ - if (DF_REF_CHAIN (ref)) - df_chain_unlink (df->problems_by_index[DF_CHAIN], ref, NULL); - reg_info->n_refs--; - - /* Unlink from the reg chain. If there is no prev, this is the - first of the list. If not, just join the next and prev. */ - if (prev) + bitmap_clear_bit (df->insns_to_delete, uid); + bitmap_clear_bit (df->insns_to_rescan, uid); + bitmap_clear_bit (df->insns_to_notes_rescan, uid); + if (insn_info) { - DF_REF_NEXT_REG (prev) = next; - if (next) - DF_REF_PREV_REG (next) = prev; + bool the_same = df_insn_refs_verify (&collection_rec, bb, insn, false); + /* If there's no change, return false. */ + if (the_same) + { + df_free_collection_rec (&collection_rec); + if (dump_file) + fprintf (dump_file, "verify found no changes in insn with uid = %d.\n", uid); + return false; + } + if (dump_file) + fprintf (dump_file, "rescanning insn with uid = %d.\n", uid); + + /* There's change - we need to delete the existing info. */ + df_insn_delete (NULL, uid); + df_insn_create_insn_record (insn); } else { - reg_info->reg_chain = next; - if (next) - DF_REF_PREV_REG (next) = NULL; + df_insn_create_insn_record (insn); + df_insn_refs_collect (&collection_rec, bb, insn); + if (dump_file) + fprintf (dump_file, "scanning new insn with uid = %d.\n", uid); } - pool_free (problem_data->ref_pool, ref); - return next_ref; + df_refs_add_to_chains (&collection_rec, bb, insn); + df_set_bb_dirty (bb); + return true; } -/* Unlink REF from all def-use/use-def chains, etc. */ +/* Rescan all of the insns in the function. Note that the artificial + uses and defs are not touched. This function will destroy def-se + or use-def chains. */ void -df_ref_remove (struct df *df, struct df_ref *ref) +df_insn_rescan_all (void) { - struct dataflow *dflow = df->problems_by_index[DF_SCAN]; - if (DF_REF_REG_DEF_P (ref)) + bool no_insn_rescan = false; + bool defer_insn_rescan = false; + basic_block bb; + bitmap_iterator bi; + unsigned int uid; + bitmap tmp = BITMAP_ALLOC (&df_bitmap_obstack); + + if (df->changeable_flags & DF_NO_INSN_RESCAN) { - if (DF_REF_FLAGS (ref) & DF_REF_ARTIFICIAL) - { - struct df_scan_bb_info *bb_info - = df_scan_get_bb_info (dflow, DF_REF_BB (ref)->index); - bb_info->artificial_defs - = df_ref_unlink (bb_info->artificial_defs, ref); - } - else - DF_INSN_UID_DEFS (df, DF_REF_INSN_UID (ref)) - = df_ref_unlink (DF_INSN_UID_DEFS (df, DF_REF_INSN_UID (ref)), ref); + df_clear_flags (DF_NO_INSN_RESCAN); + no_insn_rescan = true; + } + + if (df->changeable_flags & DF_DEFER_INSN_RESCAN) + { + df_clear_flags (DF_DEFER_INSN_RESCAN); + defer_insn_rescan = true; + } - if (df->def_info.add_refs_inline) - DF_DEFS_SET (df, DF_REF_ID (ref), NULL); + bitmap_copy (tmp, df->insns_to_delete); + EXECUTE_IF_SET_IN_BITMAP (tmp, 0, uid, bi) + { + struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid); + if (insn_info) + df_insn_delete (NULL, uid); } - else + + BITMAP_FREE (tmp); + bitmap_clear (df->insns_to_delete); + bitmap_clear (df->insns_to_rescan); + bitmap_clear (df->insns_to_notes_rescan); + + FOR_EACH_BB (bb) { - if (DF_REF_FLAGS (ref) & DF_REF_ARTIFICIAL) + rtx insn; + FOR_BB_INSNS (bb, insn) { - struct df_scan_bb_info *bb_info - = df_scan_get_bb_info (dflow, DF_REF_BB (ref)->index); - bb_info->artificial_uses - = df_ref_unlink (bb_info->artificial_uses, ref); + df_insn_rescan (insn); } - else - DF_INSN_UID_USES (df, DF_REF_INSN_UID (ref)) - = df_ref_unlink (DF_INSN_UID_USES (df, DF_REF_INSN_UID (ref)), ref); - - if (df->use_info.add_refs_inline) - DF_USES_SET (df, DF_REF_ID (ref), NULL); } - df_reg_chain_unlink (dflow, ref); + if (no_insn_rescan) + df_set_flags (DF_NO_INSN_RESCAN); + if (defer_insn_rescan) + df_set_flags (DF_DEFER_INSN_RESCAN); } -/* Create the insn record for INSN. If there was one there, zero it out. */ +/* Process all of the defered rescans or deletions. */ -static struct df_insn_info * -df_insn_create_insn_record (struct dataflow *dflow, rtx insn) +void +df_process_deferred_rescans (void) { - struct df *df = dflow->df; - struct df_scan_problem_data *problem_data - = (struct df_scan_problem_data *) dflow->problem_data; + bool no_insn_rescan = false; + bool defer_insn_rescan = false; + bitmap_iterator bi; + unsigned int uid; + bitmap tmp = BITMAP_ALLOC (&df_bitmap_obstack); + + if (df->changeable_flags & DF_NO_INSN_RESCAN) + { + df_clear_flags (DF_NO_INSN_RESCAN); + no_insn_rescan = true; + } + + if (df->changeable_flags & DF_DEFER_INSN_RESCAN) + { + df_clear_flags (DF_DEFER_INSN_RESCAN); + defer_insn_rescan = true; + } - struct df_insn_info *insn_rec = DF_INSN_GET (df, insn); - if (!insn_rec) + if (dump_file) + fprintf (dump_file, "starting the processing of defered insns\n"); + + bitmap_copy (tmp, df->insns_to_delete); + EXECUTE_IF_SET_IN_BITMAP (tmp, 0, uid, bi) { - insn_rec = pool_alloc (problem_data->insn_pool); - DF_INSN_SET (df, insn, insn_rec); + struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid); + if (insn_info) + df_insn_delete (NULL, uid); } - memset (insn_rec, 0, sizeof (struct df_insn_info)); - return insn_rec; + bitmap_copy (tmp, df->insns_to_rescan); + EXECUTE_IF_SET_IN_BITMAP (tmp, 0, uid, bi) + { + struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid); + if (insn_info) + df_insn_rescan (insn_info->insn); + } + + bitmap_copy (tmp, df->insns_to_notes_rescan); + EXECUTE_IF_SET_IN_BITMAP (tmp, 0, uid, bi) + { + struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid); + if (insn_info) + df_notes_rescan (insn_info->insn); + } + + if (dump_file) + fprintf (dump_file, "ending the processing of defered insns\n"); + + BITMAP_FREE (tmp); + bitmap_clear (df->insns_to_delete); + bitmap_clear (df->insns_to_rescan); + bitmap_clear (df->insns_to_notes_rescan); + + if (no_insn_rescan) + df_set_flags (DF_NO_INSN_RESCAN); + if (defer_insn_rescan) + df_set_flags (DF_DEFER_INSN_RESCAN); + + /* If someone changed regs_ever_live during this pass, fix up the + entry and exit blocks. */ + if (df->redo_entry_and_exit) + { + df_update_entry_exit_and_calls (); + df->redo_entry_and_exit = false; + } } -/* Delete all of the refs information from INSN. */ +/* Count the number of refs. Include the defs if INCLUDE_DEFS. Include + the uses if INCLUDE_USES. Include the eq_uses if + INCLUDE_EQ_USES. */ -void -df_insn_refs_delete (struct dataflow *dflow, rtx insn) +static unsigned int +df_count_refs (bool include_defs, bool include_uses, + bool include_eq_uses) { - struct df *df = dflow->df; - unsigned int uid = INSN_UID (insn); - struct df_insn_info *insn_info = NULL; - struct df_ref *ref; - struct df_scan_problem_data *problem_data - = (struct df_scan_problem_data *) dflow->problem_data; + unsigned int regno; + int size = 0; + unsigned int m = df->regs_inited; + + for (regno = 0; regno < m; regno++) + { + if (include_defs) + size += DF_REG_DEF_COUNT (regno); + if (include_uses) + size += DF_REG_USE_COUNT (regno); + if (include_eq_uses) + size += DF_REG_EQ_USE_COUNT (regno); + } + return size; +} - if (uid < df->insns_size) - insn_info = DF_INSN_UID_GET (df, uid); - if (insn_info) +/* Take build ref table for either the uses or defs from the reg-use + or reg-def chains. This version processes the refs in reg order + which is likely to be best if processing the whole function. */ + +static void +df_reorganize_refs_by_reg_by_reg (struct df_ref_info *ref_info, + bool include_defs, + bool include_uses, + bool include_eq_uses) +{ + unsigned int m = df->regs_inited; + unsigned int regno; + unsigned int offset = 0; + unsigned int start; + + if (df->changeable_flags & DF_NO_HARD_REGS) { - struct df_mw_hardreg *hardregs = insn_info->mw_hardregs; - - while (hardregs) + start = FIRST_PSEUDO_REGISTER; + memset (ref_info->begin, 0, sizeof (int) * FIRST_PSEUDO_REGISTER); + memset (ref_info->count, 0, sizeof (int) * FIRST_PSEUDO_REGISTER); + } + else + start = 0; + + ref_info->total_size + = df_count_refs (include_defs, include_uses, include_eq_uses); + + df_check_and_grow_ref_info (ref_info, 1); + + for (regno = start; regno < m; regno++) + { + int count = 0; + ref_info->begin[regno] = offset; + if (include_defs) { - struct df_mw_hardreg *next_hr = hardregs->next; - struct df_link *link = hardregs->regs; - while (link) + struct df_ref *ref = DF_REG_DEF_CHAIN (regno); + while (ref) { - struct df_link *next_l = link->next; - pool_free (problem_data->mw_link_pool, link); - link = next_l; + ref_info->refs[offset] = ref; + DF_REF_ID (ref) = offset++; + count++; + ref = DF_REF_NEXT_REG (ref); + gcc_assert (offset < ref_info->refs_size); + } + } + if (include_uses) + { + struct df_ref *ref = DF_REG_USE_CHAIN (regno); + while (ref) + { + ref_info->refs[offset] = ref; + DF_REF_ID (ref) = offset++; + count++; + ref = DF_REF_NEXT_REG (ref); + gcc_assert (offset < ref_info->refs_size); + } + } + if (include_eq_uses) + { + struct df_ref *ref = DF_REG_EQ_USE_CHAIN (regno); + while (ref) + { + ref_info->refs[offset] = ref; + DF_REF_ID (ref) = offset++; + count++; + ref = DF_REF_NEXT_REG (ref); + gcc_assert (offset < ref_info->refs_size); } - - pool_free (problem_data->mw_reg_pool, hardregs); - hardregs = next_hr; } + ref_info->count[regno] = count; + } + + /* The bitmap size is not decremented when refs are deleted. So + reset it now that we have squished out all of the empty + slots. */ + ref_info->table_size = offset; +} - ref = insn_info->defs; - while (ref) - ref = df_reg_chain_unlink (dflow, ref); - - ref = insn_info->uses; - while (ref) - ref = df_reg_chain_unlink (dflow, ref); - pool_free (problem_data->insn_pool, insn_info); - DF_INSN_SET (df, insn, NULL); +/* Take build ref table for either the uses or defs from the reg-use + or reg-def chains. This version processes the refs in insn order + which is likely to be best if processing some segment of the + function. */ + +static void +df_reorganize_refs_by_reg_by_insn (struct df_ref_info *ref_info, + bool include_defs, + bool include_uses, + bool include_eq_uses) +{ + bitmap_iterator bi; + unsigned int bb_index; + unsigned int m = df->regs_inited; + unsigned int offset = 0; + unsigned int r; + unsigned int start + = (df->changeable_flags & DF_NO_HARD_REGS) ? FIRST_PSEUDO_REGISTER : 0; + + memset (ref_info->begin, 0, sizeof (int) * df->regs_inited); + memset (ref_info->count, 0, sizeof (int) * df->regs_inited); + + ref_info->total_size = df_count_refs (include_defs, include_uses, include_eq_uses); + df_check_and_grow_ref_info (ref_info, 1); + + EXECUTE_IF_SET_IN_BITMAP (df->blocks_to_analyze, 0, bb_index, bi) + { + basic_block bb = BASIC_BLOCK (bb_index); + rtx insn; + struct df_ref **ref_rec; + + if (include_defs) + for (ref_rec = df_get_artificial_defs (bb_index); *ref_rec; ref_rec++) + { + unsigned int regno = DF_REF_REGNO (*ref_rec); + ref_info->count[regno]++; + } + if (include_uses) + for (ref_rec = df_get_artificial_uses (bb_index); *ref_rec; ref_rec++) + { + unsigned int regno = DF_REF_REGNO (*ref_rec); + ref_info->count[regno]++; + } + + FOR_BB_INSNS (bb, insn) + { + if (INSN_P (insn)) + { + unsigned int uid = INSN_UID (insn); + + if (include_defs) + for (ref_rec = DF_INSN_UID_DEFS (uid); *ref_rec; ref_rec++) + { + unsigned int regno = DF_REF_REGNO (*ref_rec); + ref_info->count[regno]++; + } + if (include_uses) + for (ref_rec = DF_INSN_UID_USES (uid); *ref_rec; ref_rec++) + { + unsigned int regno = DF_REF_REGNO (*ref_rec); + ref_info->count[regno]++; + } + if (include_eq_uses) + for (ref_rec = DF_INSN_UID_EQ_USES (uid); *ref_rec; ref_rec++) + { + unsigned int regno = DF_REF_REGNO (*ref_rec); + ref_info->count[regno]++; + } + } + } + } + + for (r = start; r < m; r++) + { + ref_info->begin[r] = offset; + offset += ref_info->count[r]; + ref_info->count[r] = 0; } + + EXECUTE_IF_SET_IN_BITMAP (df->blocks_to_analyze, 0, bb_index, bi) + { + basic_block bb = BASIC_BLOCK (bb_index); + rtx insn; + struct df_ref **ref_rec; + + if (include_defs) + for (ref_rec = df_get_artificial_defs (bb_index); *ref_rec; ref_rec++) + { + struct df_ref *ref = *ref_rec; + unsigned int regno = DF_REF_REGNO (ref); + if (regno >= start) + { + unsigned int id + = ref_info->begin[regno] + ref_info->count[regno]++; + DF_REF_ID (ref) = id; + ref_info->refs[id] = ref; + } + } + if (include_uses) + for (ref_rec = df_get_artificial_uses (bb_index); *ref_rec; ref_rec++) + { + struct df_ref *ref = *ref_rec; + unsigned int regno = DF_REF_REGNO (ref); + if (regno >= start) + { + unsigned int id + = ref_info->begin[regno] + ref_info->count[regno]++; + DF_REF_ID (ref) = id; + ref_info->refs[id] = ref; + } + } + + FOR_BB_INSNS (bb, insn) + { + if (INSN_P (insn)) + { + unsigned int uid = INSN_UID (insn); + + if (include_defs) + for (ref_rec = DF_INSN_UID_DEFS (uid); *ref_rec; ref_rec++) + { + struct df_ref *ref = *ref_rec; + unsigned int regno = DF_REF_REGNO (ref); + if (regno >= start) + { + unsigned int id + = ref_info->begin[regno] + ref_info->count[regno]++; + DF_REF_ID (ref) = id; + ref_info->refs[id] = ref; + } + } + if (include_uses) + for (ref_rec = DF_INSN_UID_USES (uid); *ref_rec; ref_rec++) + { + struct df_ref *ref = *ref_rec; + unsigned int regno = DF_REF_REGNO (ref); + if (regno >= start) + { + unsigned int id + = ref_info->begin[regno] + ref_info->count[regno]++; + DF_REF_ID (ref) = id; + ref_info->refs[id] = ref; + } + } + if (include_eq_uses) + for (ref_rec = DF_INSN_UID_EQ_USES (uid); *ref_rec; ref_rec++) + { + struct df_ref *ref = *ref_rec; + unsigned int regno = DF_REF_REGNO (ref); + if (regno >= start) + { + unsigned int id + = ref_info->begin[regno] + ref_info->count[regno]++; + DF_REF_ID (ref) = id; + ref_info->refs[id] = ref; + } + } + } + } + } + + /* The bitmap size is not decremented when refs are deleted. So + reset it now that we have squished out all of the empty + slots. */ + + ref_info->table_size = offset; } +/* Take build ref table for either the uses or defs from the reg-use + or reg-def chains. */ -/* Delete all of the refs information from basic_block with BB_INDEX. */ +static void +df_reorganize_refs_by_reg (struct df_ref_info *ref_info, + bool include_defs, + bool include_uses, + bool include_eq_uses) +{ + if (df->analyze_subset) + df_reorganize_refs_by_reg_by_insn (ref_info, include_defs, + include_uses, include_eq_uses); + else + df_reorganize_refs_by_reg_by_reg (ref_info, include_defs, + include_uses, include_eq_uses); +} -void -df_bb_refs_delete (struct dataflow *dflow, int bb_index) + +/* Add the refs in REF_VEC to the table in REF_INFO starting at OFFSET. */ +static unsigned int +df_add_refs_to_table (unsigned int offset, + struct df_ref_info *ref_info, + struct df_ref **ref_vec) { - struct df_ref *def; - struct df_ref *use; + while (*ref_vec) + { + struct df_ref *ref = *ref_vec; + if ((!(df->changeable_flags & DF_NO_HARD_REGS)) + || (DF_REF_REGNO (ref) >= FIRST_PSEUDO_REGISTER)) + { + ref_info->refs[offset] = ref; + DF_REF_ID (*ref_vec) = offset++; + } + ref_vec++; + } + return offset; +} - struct df_scan_bb_info *bb_info - = df_scan_get_bb_info (dflow, bb_index); + +/* Count the number of refs in all of the insns of BB. Include the + defs if INCLUDE_DEFS. Include the uses if INCLUDE_USES. Include the + eq_uses if INCLUDE_EQ_USES. */ + +static unsigned int +df_reorganize_refs_by_insn_bb (basic_block bb, unsigned int offset, + struct df_ref_info *ref_info, + bool include_defs, bool include_uses, + bool include_eq_uses) +{ rtx insn; - basic_block bb = BASIC_BLOCK (bb_index); + + if (include_defs) + offset = df_add_refs_to_table (offset, ref_info, + df_get_artificial_defs (bb->index)); + if (include_uses) + offset = df_add_refs_to_table (offset, ref_info, + df_get_artificial_uses (bb->index)); + FOR_BB_INSNS (bb, insn) + if (INSN_P (insn)) + { + unsigned int uid = INSN_UID (insn); + if (include_defs) + offset = df_add_refs_to_table (offset, ref_info, + DF_INSN_UID_DEFS (uid)); + if (include_uses) + offset = df_add_refs_to_table (offset, ref_info, + DF_INSN_UID_USES (uid)); + if (include_eq_uses) + offset = df_add_refs_to_table (offset, ref_info, + DF_INSN_UID_EQ_USES (uid)); + } + return offset; +} + + +/* Organinze the refs by insn into the table in REF_INFO. If + blocks_to_analyze is defined, use that set, otherwise the entire + program. Include the defs if INCLUDE_DEFS. Include the uses if + INCLUDE_USES. Include the eq_uses if INCLUDE_EQ_USES. */ + +static void +df_reorganize_refs_by_insn (struct df_ref_info *ref_info, + bool include_defs, bool include_uses, + bool include_eq_uses) +{ + basic_block bb; + unsigned int offset = 0; + + ref_info->total_size = df_count_refs (include_defs, include_uses, include_eq_uses); + df_check_and_grow_ref_info (ref_info, 1); + if (df->blocks_to_analyze) { - if (INSN_P (insn)) + bitmap_iterator bi; + unsigned int index; + + EXECUTE_IF_SET_IN_BITMAP (df->blocks_to_analyze, 0, index, bi) { - /* Record defs within INSN. */ - df_insn_refs_delete (dflow, insn); + offset = df_reorganize_refs_by_insn_bb (BASIC_BLOCK (index), offset, ref_info, + include_defs, include_uses, + include_eq_uses); } + + ref_info->table_size = offset; } - - /* Get rid of any artificial uses or defs. */ - if (bb_info) + else { - def = bb_info->artificial_defs; - while (def) - def = df_reg_chain_unlink (dflow, def); - bb_info->artificial_defs = NULL; - use = bb_info->artificial_uses; - while (use) - use = df_reg_chain_unlink (dflow, use); - bb_info->artificial_uses = NULL; + FOR_ALL_BB (bb) + offset = df_reorganize_refs_by_insn_bb (bb, offset, ref_info, + include_defs, include_uses, + include_eq_uses); + ref_info->table_size = offset; } } -/* Delete all of the refs information from BLOCKS. */ +/* If the use refs in DF are not organized, reorganize them. */ void -df_refs_delete (struct dataflow *dflow, bitmap blocks) +df_maybe_reorganize_use_refs (enum df_ref_order order) { - bitmap_iterator bi; - unsigned int bb_index; + if (order == df->use_info.ref_order) + return; - EXECUTE_IF_SET_IN_BITMAP (blocks, 0, bb_index, bi) + switch (order) { - df_bb_refs_delete (dflow, bb_index); + case DF_REF_ORDER_BY_REG: + df_reorganize_refs_by_reg (&df->use_info, false, true, false); + break; + + case DF_REF_ORDER_BY_REG_WITH_NOTES: + df_reorganize_refs_by_reg (&df->use_info, false, true, true); + break; + + case DF_REF_ORDER_BY_INSN: + df_reorganize_refs_by_insn (&df->use_info, false, true, false); + break; + + case DF_REF_ORDER_BY_INSN_WITH_NOTES: + df_reorganize_refs_by_insn (&df->use_info, false, true, true); + break; + + case DF_REF_ORDER_NO_TABLE: + free (df->use_info.refs); + df->use_info.refs = NULL; + df->use_info.refs_size = 0; + break; + + case DF_REF_ORDER_UNORDERED: + case DF_REF_ORDER_UNORDERED_WITH_NOTES: + gcc_unreachable (); + break; } + + df->use_info.ref_order = order; } -/* Take build ref table for either the uses or defs from the reg-use - or reg-def chains. */ +/* If the def refs in DF are not organized, reorganize them. */ void -df_reorganize_refs (struct df_ref_info *ref_info) +df_maybe_reorganize_def_refs (enum df_ref_order order) { - unsigned int m = ref_info->regs_inited; - unsigned int regno; - unsigned int offset = 0; - unsigned int size = 0; + if (order == df->def_info.ref_order) + return; + + switch (order) + { + case DF_REF_ORDER_BY_REG: + df_reorganize_refs_by_reg (&df->def_info, true, false, false); + break; + + case DF_REF_ORDER_BY_INSN: + df_reorganize_refs_by_insn (&df->def_info, true, false, false); + break; + + case DF_REF_ORDER_NO_TABLE: + free (df->def_info.refs); + df->def_info.refs = NULL; + df->def_info.refs_size = 0; + break; + + case DF_REF_ORDER_BY_INSN_WITH_NOTES: + case DF_REF_ORDER_BY_REG_WITH_NOTES: + case DF_REF_ORDER_UNORDERED: + case DF_REF_ORDER_UNORDERED_WITH_NOTES: + gcc_unreachable (); + break; + } + + df->def_info.ref_order = order; +} + + +/* Change the BB of all refs in the ref chain to NEW_BB. + Assumes that all refs in the chain have the same BB. + If changed, return the original bb the chain belonged to + (or . + If no change, return NEW_BB. + If something's wrong, it will return NULL. */ + +static basic_block +df_ref_chain_change_bb (struct df_ref **ref_rec, + basic_block old_bb, + basic_block new_bb) +{ + while (*ref_rec) + { + struct df_ref *ref = *ref_rec; + + if (DF_REF_BB (ref) == new_bb) + return new_bb; + else + { + gcc_assert (old_bb == NULL || DF_REF_BB (ref) == old_bb); + old_bb = DF_REF_BB (ref); + DF_REF_BB (ref) = new_bb; + } + ref_rec++; + } + + return old_bb; +} + + +/* Change all of the basic block references in INSN to use the insn's + current basic block. This function is called from routines that move + instructions from one block to another. */ - if (ref_info->refs_organized_size) +void +df_insn_change_bb (rtx insn) +{ + basic_block new_bb = BLOCK_FOR_INSN (insn); + basic_block old_bb = NULL; + struct df_insn_info *insn_info; + unsigned int uid = INSN_UID (insn); + + if (!df) return; - if (ref_info->refs_size < ref_info->bitmap_size) - { - int new_size = ref_info->bitmap_size + ref_info->bitmap_size / 4; - df_grow_ref_info (ref_info, new_size); + if (dump_file) + fprintf (dump_file, "changing bb of uid %d\n", uid); + + insn_info = DF_INSN_UID_SAFE_GET (uid); + if (insn_info == NULL) + { + if (dump_file) + fprintf (dump_file, " unscanned insn\n"); + df_insn_rescan (insn); + return; } - for (regno = 0; regno < m; regno++) + if (!INSN_P (insn)) + return; + + old_bb = df_ref_chain_change_bb (insn_info->defs, old_bb, new_bb); + if (old_bb == new_bb) + return; + + old_bb = df_ref_chain_change_bb (insn_info->uses, old_bb, new_bb); + if (old_bb == new_bb) + return; + + old_bb = df_ref_chain_change_bb (insn_info->eq_uses, old_bb, new_bb); + if (old_bb == new_bb) + return; + + df_set_bb_dirty (new_bb); + if (old_bb) { - struct df_reg_info *reg_info = ref_info->regs[regno]; - int count = 0; - if (reg_info) + if (dump_file) + fprintf (dump_file, " from %d to %d\n", + old_bb->index, new_bb->index); + df_set_bb_dirty (old_bb); + } + else + if (dump_file) + fprintf (dump_file, " to %d\n", new_bb->index); +} + + +/* Helper function for df_ref_change_reg_with_loc. */ + +static void +df_ref_change_reg_with_loc_1 (struct df_reg_info *old, struct df_reg_info *new, + int new_regno, rtx loc) +{ + struct df_ref *the_ref = old->reg_chain; + + while (the_ref) + { + if (DF_REF_LOC(the_ref) && (*DF_REF_LOC(the_ref) == loc)) { - struct df_ref *ref = reg_info->reg_chain; - reg_info->begin = offset; - while (ref) + struct df_ref *next_ref = the_ref->next_reg; + struct df_ref *prev_ref = the_ref->prev_reg; + struct df_ref **ref_vec, **ref_vec_t; + unsigned int count = 0; + + DF_REF_REGNO (the_ref) = new_regno; + DF_REF_REG (the_ref) = regno_reg_rtx[new_regno]; + + /* Pull the_ref out of the old regno chain. */ + if (prev_ref) + prev_ref->next_reg = next_ref; + else + old->reg_chain = next_ref; + if (next_ref) + next_ref->prev_reg = prev_ref; + old->n_refs--; + + /* Put the ref into the new regno chain. */ + the_ref->prev_reg = NULL; + the_ref->next_reg = new->reg_chain; + if (new->reg_chain) + new->reg_chain->prev_reg = the_ref; + new->reg_chain = the_ref; + new->n_refs++; + df_set_bb_dirty (DF_REF_BB (the_ref)); + + /* Need to resort the record that the ref was in because the + regno is a sorting key. First, find the right record. */ + if (DF_REF_IS_ARTIFICIAL (the_ref)) + { + unsigned int bb_index = DF_REF_BB (the_ref)->index; + if (DF_REF_REG_DEF_P (the_ref)) + ref_vec = df_get_artificial_defs (bb_index); + else + ref_vec = df_get_artificial_uses (bb_index); + } + else + { + struct df_insn_info *insn_info + = DF_INSN_GET (DF_REF_INSN (the_ref)); + if (DF_REF_FLAGS (the_ref) & DF_REF_IN_NOTE) + ref_vec = insn_info->eq_uses; + else + ref_vec = insn_info->uses; + if (dump_file) + fprintf (dump_file, "changing reg in insn %d\n", + INSN_UID (DF_REF_INSN (the_ref))); + } + ref_vec_t = ref_vec; + + /* Find the length. */ + while (*ref_vec_t) { - ref_info->refs[offset] = ref; - DF_REF_ID (ref) = offset++; - ref = DF_REF_NEXT_REG (ref); count++; - size++; + ref_vec_t++; } - reg_info->n_refs = count; + qsort (ref_vec, count, sizeof (struct df_ref *), df_ref_compare); + + the_ref = next_ref; } + else + the_ref = the_ref->next_reg; } +} + + +/* Change the regno of all refs that contained LOC from OLD_REGNO to + NEW_REGNO. Refs that do not match LOC are not changed. This call + is to support the SET_REGNO macro. */ + +void +df_ref_change_reg_with_loc (int old_regno, int new_regno, rtx loc) +{ + if ((!df) || (old_regno == -1) || (old_regno == new_regno)) + return; + + df_grow_reg_info (); + + df_ref_change_reg_with_loc_1 (DF_REG_DEF_GET (old_regno), + DF_REG_DEF_GET (new_regno), new_regno, loc); + df_ref_change_reg_with_loc_1 (DF_REG_USE_GET (old_regno), + DF_REG_USE_GET (new_regno), new_regno, loc); + df_ref_change_reg_with_loc_1 (DF_REG_EQ_USE_GET (old_regno), + DF_REG_EQ_USE_GET (new_regno), new_regno, loc); +} + + +/* Delete the mw_hardregs that point into the eq_notes. */ + +static unsigned int +df_mw_hardreg_chain_delete_eq_uses (struct df_insn_info *insn_info) +{ + struct df_mw_hardreg **mw_vec = insn_info->mw_hardregs; + unsigned int deleted = 0; + unsigned int count = 0; + struct df_scan_problem_data *problem_data + = (struct df_scan_problem_data *) df_scan->problem_data; + + if (!*mw_vec) + return 0; + + while (*mw_vec) + { + if ((*mw_vec)->flags & DF_REF_IN_NOTE) + { + struct df_mw_hardreg **temp_vec = mw_vec; + + pool_free (problem_data->mw_reg_pool, *mw_vec); + temp_vec = mw_vec; + /* Shove the remaining ones down one to fill the gap. While + this looks n**2, it is highly unusual to have any mw regs + in eq_notes and the chances of more than one are almost + non existent. */ + while (*temp_vec) + { + *temp_vec = *(temp_vec + 1); + temp_vec++; + } + deleted++; + } + else + { + mw_vec++; + count++; + } + } + + if (count == 0) + { + free (insn_info->mw_hardregs); + insn_info->mw_hardregs = df_null_mw_rec; + return 0; + } + return deleted; +} + + +/* Rescan only the REG_EQUIV/REG_EQUAL notes part of INSN. */ + +void +df_notes_rescan (rtx insn) +{ + struct df_insn_info *insn_info; + unsigned int uid = INSN_UID (insn); + + if (!df) + return; + + /* The client has disabled rescanning and plans to do it itself. */ + if (df->changeable_flags & DF_NO_INSN_RESCAN) + return; + + df_grow_bb_info (df_scan); + df_grow_reg_info (); + + insn_info = DF_INSN_UID_SAFE_GET (INSN_UID(insn)); + + /* The client has defered rescanning. */ + if (df->changeable_flags & DF_DEFER_INSN_RESCAN) + { + if (!insn_info) + { + insn_info = df_insn_create_insn_record (insn); + insn_info->defs = df_null_ref_rec; + insn_info->uses = df_null_ref_rec; + insn_info->eq_uses = df_null_ref_rec; + insn_info->mw_hardregs = df_null_mw_rec; + } + + bitmap_clear_bit (df->insns_to_delete, uid); + /* If the insn is set to be rescanned, it does not need to also + be notes rescanned. */ + if (!bitmap_bit_p (df->insns_to_rescan, uid)) + bitmap_set_bit (df->insns_to_notes_rescan, INSN_UID (insn)); + return; + } + + bitmap_clear_bit (df->insns_to_delete, uid); + bitmap_clear_bit (df->insns_to_notes_rescan, uid); + + if (insn_info) + { + basic_block bb = BLOCK_FOR_INSN (insn); + rtx note; + struct df_collection_rec collection_rec; + unsigned int num_deleted; + + memset (&collection_rec, 0, sizeof (struct df_collection_rec)); + collection_rec.eq_use_vec = alloca (sizeof (struct df_ref*) * 1000); + collection_rec.mw_vec = alloca (sizeof (struct df_mw_hardreg*) * 1000); + + num_deleted = df_mw_hardreg_chain_delete_eq_uses (insn_info); + df_ref_chain_delete (insn_info->eq_uses); + insn_info->eq_uses = NULL; + + /* Process REG_EQUIV/REG_EQUAL notes */ + for (note = REG_NOTES (insn); note; + note = XEXP (note, 1)) + { + switch (REG_NOTE_KIND (note)) + { + case REG_EQUIV: + case REG_EQUAL: + df_uses_record (&collection_rec, + &XEXP (note, 0), DF_REF_REG_USE, + bb, insn, DF_REF_IN_NOTE); + default: + break; + } + } + + /* Find some place to put any new mw_hardregs. */ + df_canonize_collection_rec (&collection_rec); + if (collection_rec.next_mw) + { + unsigned int count = 0; + struct df_mw_hardreg **mw_rec = insn_info->mw_hardregs; + while (*mw_rec) + { + count++; + mw_rec++; + } + + if (count) + { + /* Append to the end of the existing record after + expanding it if necessary. */ + if (collection_rec.next_mw > num_deleted) + { + insn_info->mw_hardregs = + xrealloc (insn_info->mw_hardregs, + (count + 1 + collection_rec.next_mw) + * sizeof (struct df_ref*)); + } + memcpy (&insn_info->mw_hardregs[count], collection_rec.mw_vec, + (collection_rec.next_mw + 1) * sizeof (struct df_mw_hardreg *)); + qsort (insn_info->mw_hardregs, count + collection_rec.next_mw, + sizeof (struct df_mw_hardreg *), df_mw_compare); + } + else + { + /* No vector there. */ + insn_info->mw_hardregs + = XNEWVEC (struct df_mw_hardreg*, + count + 1 + collection_rec.next_mw); + memcpy (insn_info->mw_hardregs, collection_rec.mw_vec, + (collection_rec.next_mw + 1) * sizeof (struct df_mw_hardreg *)); + } + } + /* Get rid of the mw_rec so that df_refs_add_to_chains will + ignore it. */ + collection_rec.mw_vec = NULL; + collection_rec.next_mw = 0; + df_refs_add_to_chains (&collection_rec, bb, insn); + } + else + df_insn_rescan (insn); - /* The bitmap size is not decremented when refs are deleted. So - reset it now that we have squished out all of the empty - slots. */ - ref_info->bitmap_size = size; - ref_info->refs_organized_size = size; - ref_info->add_refs_inline = true; } @@ -925,21 +2118,457 @@ df_reorganize_refs (struct df_ref_info *ref_info) just a lot of routines that look inside insns. ----------------------------------------------------------------------------*/ -/* Create a ref and add it to the reg-def or reg-use chains. */ + +/* Return true if the contents of two df_ref's are identical. + It ignores DF_REF_MARKER. */ + +static bool +df_ref_equal_p (struct df_ref *ref1, struct df_ref *ref2) +{ + if (!ref2) + return false; + return (ref1 == ref2) || + (DF_REF_REG (ref1) == DF_REF_REG (ref2) + && DF_REF_REGNO (ref1) == DF_REF_REGNO (ref2) + && DF_REF_LOC (ref1) == DF_REF_LOC (ref2) + && DF_REF_INSN (ref1) == DF_REF_INSN (ref2) + && DF_REF_TYPE (ref1) == DF_REF_TYPE (ref2) + && ((DF_REF_FLAGS (ref1) & ~(DF_REF_REG_MARKER + DF_REF_MW_HARDREG)) + == (DF_REF_FLAGS (ref2) & ~(DF_REF_REG_MARKER + DF_REF_MW_HARDREG))) + && DF_REF_BB (ref1) == DF_REF_BB (ref2)); +} + + +/* Compare REF1 and REF2 for sorting. This is only called from places + where all of the refs are of the same type, in the same insn, and + have the same bb. So these fields are not checked. */ + +static int +df_ref_compare (const void *r1, const void *r2) +{ + const struct df_ref *ref1 = *(struct df_ref **)r1; + const struct df_ref *ref2 = *(struct df_ref **)r2; + + if (ref1 == ref2) + return 0; + + if (DF_REF_REGNO (ref1) != DF_REF_REGNO (ref2)) + return (int)DF_REF_REGNO (ref1) - (int)DF_REF_REGNO (ref2); + + if (DF_REF_TYPE (ref1) != DF_REF_TYPE (ref2)) + return (int)DF_REF_TYPE (ref1) - (int)DF_REF_TYPE (ref2); + + if ((DF_REF_REG (ref1) != DF_REF_REG (ref2)) + || (DF_REF_LOC (ref1) != DF_REF_LOC (ref2))) + return (int)DF_REF_ORDER (ref1) - (int)DF_REF_ORDER (ref2); + + if (DF_REF_FLAGS (ref1) != DF_REF_FLAGS (ref2)) + { + /* If two refs are identical except that one of them has is from + a mw and one is not, we need to have the one with the mw + first. */ + if (DF_REF_FLAGS_IS_SET (ref1, DF_REF_MW_HARDREG) == + DF_REF_FLAGS_IS_SET (ref2, DF_REF_MW_HARDREG)) + return DF_REF_FLAGS (ref1) - DF_REF_FLAGS (ref2); + else if (DF_REF_FLAGS_IS_SET (ref1, DF_REF_MW_HARDREG)) + return -1; + else + return 1; + } + return 0; +} + +static void +df_swap_refs (struct df_ref **ref_vec, int i, int j) +{ + struct df_ref *tmp = ref_vec[i]; + ref_vec[i] = ref_vec[j]; + ref_vec[j] = tmp; +} + +/* Sort and compress a set of refs. */ + +static unsigned int +df_sort_and_compress_refs (struct df_ref **ref_vec, unsigned int count) +{ + struct df_scan_problem_data *problem_data + = (struct df_scan_problem_data *) df_scan->problem_data; + unsigned int i; + unsigned int dist = 0; + + ref_vec[count] = NULL; + /* If there are 1 or 0 elements, there is nothing to do. */ + if (count < 2) + return count; + else if (count == 2) + { + if (df_ref_compare (&ref_vec[0], &ref_vec[1]) > 0) + df_swap_refs (ref_vec, 0, 1); + } + else + { + for (i = 0; i < count - 1; i++) + if (df_ref_compare (&ref_vec[i], &ref_vec[i+1]) >= 0) + break; + /* If the array is already strictly ordered, + which is the most common case for large COUNT case + (which happens for CALL INSNs), + no need to sort and filter out duplicate. + Simply return the count. + Make sure DF_GET_ADD_REFS adds refs in the increasing order + of DF_REF_COMPARE. */ + if (i == count - 1) + return count; + qsort (ref_vec, count, sizeof (struct df_ref *), df_ref_compare); + } + + for (i=0; i<count-dist; i++) + { + /* Find the next ref that is not equal to the current ref. */ + while (df_ref_equal_p (ref_vec[i], ref_vec[i + dist + 1])) + { + pool_free (problem_data->ref_pool, ref_vec[i + dist + 1]); + dist++; + } + /* Copy it down to the next position. */ + if (dist) + ref_vec[i+1] = ref_vec[i + dist + 1]; + } + + count -= dist; + ref_vec[count] = NULL; + return count; +} + + +/* Return true if the contents of two df_ref's are identical. + It ignores DF_REF_MARKER. */ + +static bool +df_mw_equal_p (struct df_mw_hardreg *mw1, struct df_mw_hardreg *mw2) +{ + if (!mw2) + return false; + return (mw1 == mw2) || + (mw1->mw_reg == mw2->mw_reg + && mw1->type == mw2->type + && mw1->flags == mw2->flags + && mw1->start_regno == mw2->start_regno + && mw1->end_regno == mw2->end_regno); +} + + +/* Compare MW1 and MW2 for sorting. */ + +static int +df_mw_compare (const void *m1, const void *m2) +{ + const struct df_mw_hardreg *mw1 = *(struct df_mw_hardreg **)m1; + const struct df_mw_hardreg *mw2 = *(struct df_mw_hardreg **)m2; + + if (mw1 == mw2) + return 0; + + if (mw1->type != mw2->type) + return mw1->type - mw2->type; + + if (mw1->flags != mw2->flags) + return mw1->flags - mw2->flags; + + if (mw1->start_regno != mw2->start_regno) + return mw1->start_regno - mw2->start_regno; + + if (mw1->end_regno != mw2->end_regno) + return mw1->end_regno - mw2->end_regno; + + if (mw1->mw_reg != mw2->mw_reg) + return mw1->mw_order - mw2->mw_order; + + return 0; +} + + +/* Sort and compress a set of refs. */ + +static unsigned int +df_sort_and_compress_mws (struct df_mw_hardreg **mw_vec, unsigned int count) +{ + struct df_scan_problem_data *problem_data + = (struct df_scan_problem_data *) df_scan->problem_data; + unsigned int i; + unsigned int dist = 0; + mw_vec[count] = NULL; + + if (count < 2) + return count; + else if (count == 2) + { + if (df_mw_compare (&mw_vec[0], &mw_vec[1]) > 0) + { + struct df_mw_hardreg *tmp = mw_vec[0]; + mw_vec[0] = mw_vec[1]; + mw_vec[1] = tmp; + } + } + else + qsort (mw_vec, count, sizeof (struct df_mw_hardreg *), df_mw_compare); + + for (i=0; i<count-dist; i++) + { + /* Find the next ref that is not equal to the current ref. */ + while (df_mw_equal_p (mw_vec[i], mw_vec[i + dist + 1])) + { + pool_free (problem_data->mw_reg_pool, mw_vec[i + dist + 1]); + dist++; + } + /* Copy it down to the next position. */ + if (dist) + mw_vec[i+1] = mw_vec[i + dist + 1]; + } + + count -= dist; + mw_vec[count] = NULL; + return count; +} + + +/* Sort and remove duplicates from the COLLECTION_REC. */ + +static void +df_canonize_collection_rec (struct df_collection_rec *collection_rec) +{ + if (collection_rec->def_vec) + collection_rec->next_def + = df_sort_and_compress_refs (collection_rec->def_vec, + collection_rec->next_def); + if (collection_rec->use_vec) + collection_rec->next_use + = df_sort_and_compress_refs (collection_rec->use_vec, + collection_rec->next_use); + if (collection_rec->eq_use_vec) + collection_rec->next_eq_use + = df_sort_and_compress_refs (collection_rec->eq_use_vec, + collection_rec->next_eq_use); + if (collection_rec->mw_vec) + collection_rec->next_mw + = df_sort_and_compress_mws (collection_rec->mw_vec, + collection_rec->next_mw); +} + + +/* Add the new df_ref to appropriate reg_info/ref_info chains. */ + +static void +df_install_ref (struct df_ref *this_ref, + struct df_reg_info *reg_info, + struct df_ref_info *ref_info, + bool add_to_table) +{ + unsigned int regno = DF_REF_REGNO (this_ref); + /* Add the ref to the reg_{def,use,eq_use} chain. */ + struct df_ref *head = reg_info->reg_chain; + + reg_info->reg_chain = this_ref; + reg_info->n_refs++; + + if (DF_REF_FLAGS_IS_SET (this_ref, DF_HARD_REG_LIVE)) + { + gcc_assert (regno < FIRST_PSEUDO_REGISTER); + df->hard_regs_live_count[regno]++; + } + + gcc_assert (DF_REF_NEXT_REG (this_ref) == NULL); + gcc_assert (DF_REF_PREV_REG (this_ref) == NULL); + + DF_REF_NEXT_REG (this_ref) = head; + + /* We cannot actually link to the head of the chain. */ + DF_REF_PREV_REG (this_ref) = NULL; + + if (head) + DF_REF_PREV_REG (head) = this_ref; + + if (add_to_table) + { + gcc_assert (ref_info->ref_order != DF_REF_ORDER_NO_TABLE); + df_check_and_grow_ref_info (ref_info, 1); + DF_REF_ID (this_ref) = ref_info->table_size; + /* Add the ref to the big array of defs. */ + ref_info->refs[ref_info->table_size] = this_ref; + ref_info->table_size++; + } + else + DF_REF_ID (this_ref) = -1; + + ref_info->total_size++; +} + + +/* This function takes one of the groups of refs (defs, uses or + eq_uses) and installs the entire group into the insn. It also adds + each of these refs into the appropriate chains. */ + +static struct df_ref ** +df_install_refs (basic_block bb, + struct df_ref **old_vec, unsigned int count, + struct df_reg_info **reg_info, + struct df_ref_info *ref_info, + bool is_notes) +{ + if (count) + { + unsigned int i; + struct df_ref **new_vec = XNEWVEC (struct df_ref*, count + 1); + bool add_to_table; + + switch (ref_info->ref_order) + { + case DF_REF_ORDER_UNORDERED_WITH_NOTES: + case DF_REF_ORDER_BY_REG_WITH_NOTES: + case DF_REF_ORDER_BY_INSN_WITH_NOTES: + ref_info->ref_order = DF_REF_ORDER_UNORDERED_WITH_NOTES; + add_to_table = true; + break; + case DF_REF_ORDER_UNORDERED: + case DF_REF_ORDER_BY_REG: + case DF_REF_ORDER_BY_INSN: + ref_info->ref_order = DF_REF_ORDER_UNORDERED; + add_to_table = !is_notes; + break; + default: + add_to_table = false; + break; + } + + /* Do not add if ref is not in the right blocks. */ + if (add_to_table && df->analyze_subset) + add_to_table = bitmap_bit_p (df->blocks_to_analyze, bb->index); + + for (i = 0; i < count; i++) + { + struct df_ref *this_ref = old_vec[i]; + new_vec[i] = this_ref; + df_install_ref (this_ref, reg_info[DF_REF_REGNO (this_ref)], + ref_info, add_to_table); + } + + new_vec[count] = NULL; + return new_vec; + } + else + return df_null_ref_rec; +} + + +/* This function takes the mws installs the entire group into the + insn. */ + +static struct df_mw_hardreg ** +df_install_mws (struct df_mw_hardreg **old_vec, unsigned int count) +{ + if (count) + { + struct df_mw_hardreg **new_vec + = XNEWVEC (struct df_mw_hardreg*, count + 1); + memcpy (new_vec, old_vec, + sizeof (struct df_mw_hardreg*) * (count + 1)); + return new_vec; + } + else + return df_null_mw_rec; +} + + +/* Add a chain of df_refs to appropriate ref chain/reg_info/ref_info + chains and update other necessary information. */ + +static void +df_refs_add_to_chains (struct df_collection_rec *collection_rec, + basic_block bb, rtx insn) +{ + if (insn) + { + struct df_insn_info *insn_rec = DF_INSN_GET (insn); + /* If there is a vector in the collection rec, add it to the + insn. A null rec is a signal that the caller will handle the + chain specially. */ + if (collection_rec->def_vec) + { + if (insn_rec->defs && *insn_rec->defs) + free (insn_rec->defs); + insn_rec->defs + = df_install_refs (bb, collection_rec->def_vec, + collection_rec->next_def, + df->def_regs, + &df->def_info, false); + } + if (collection_rec->use_vec) + { + if (insn_rec->uses && *insn_rec->uses) + free (insn_rec->uses); + insn_rec->uses + = df_install_refs (bb, collection_rec->use_vec, + collection_rec->next_use, + df->use_regs, + &df->use_info, false); + } + if (collection_rec->eq_use_vec) + { + if (insn_rec->eq_uses && *insn_rec->eq_uses) + free (insn_rec->eq_uses); + insn_rec->eq_uses + = df_install_refs (bb, collection_rec->eq_use_vec, + collection_rec->next_eq_use, + df->eq_use_regs, + &df->use_info, true); + } + if (collection_rec->mw_vec) + { + if (insn_rec->mw_hardregs && *insn_rec->mw_hardregs) + free (insn_rec->mw_hardregs); + insn_rec->mw_hardregs + = df_install_mws (collection_rec->mw_vec, + collection_rec->next_mw); + } + } + else + { + struct df_scan_bb_info *bb_info = df_scan_get_bb_info (bb->index); + + if (bb_info->artificial_defs && *bb_info->artificial_defs) + free (bb_info->artificial_defs); + bb_info->artificial_defs + = df_install_refs (bb, collection_rec->def_vec, + collection_rec->next_def, + df->def_regs, + &df->def_info, false); + if (bb_info->artificial_uses && *bb_info->artificial_uses) + free (bb_info->artificial_uses); + bb_info->artificial_uses + = df_install_refs (bb, collection_rec->use_vec, + collection_rec->next_use, + df->use_regs, + &df->use_info, false); + } +} + + +/* Allocate a ref and initialize its fields. */ static struct df_ref * -df_ref_create_structure (struct dataflow *dflow, rtx reg, rtx *loc, +df_ref_create_structure (struct df_collection_rec *collection_rec, + rtx reg, rtx *loc, basic_block bb, rtx insn, enum df_ref_type ref_type, enum df_ref_flags ref_flags) { struct df_ref *this_ref; - struct df *df = dflow->df; int regno = REGNO (GET_CODE (reg) == SUBREG ? SUBREG_REG (reg) : reg); struct df_scan_problem_data *problem_data - = (struct df_scan_problem_data *) dflow->problem_data; + = (struct df_scan_problem_data *) df_scan->problem_data; this_ref = pool_alloc (problem_data->ref_pool); + DF_REF_ID (this_ref) = -1; DF_REF_REG (this_ref) = reg; DF_REF_REGNO (this_ref) = regno; DF_REF_LOC (this_ref) = loc; @@ -947,100 +2576,41 @@ df_ref_create_structure (struct dataflow *dflow, rtx reg, rtx *loc, DF_REF_CHAIN (this_ref) = NULL; DF_REF_TYPE (this_ref) = ref_type; DF_REF_FLAGS (this_ref) = ref_flags; - DF_REF_DATA (this_ref) = NULL; DF_REF_BB (this_ref) = bb; - - /* Link the ref into the reg_def and reg_use chains and keep a count - of the instances. */ - switch (ref_type) + DF_REF_NEXT_REG (this_ref) = NULL; + DF_REF_PREV_REG (this_ref) = NULL; + DF_REF_ORDER (this_ref) = df->ref_order++; + + /* We need to clear this bit because fwprop, and in the future + possibly other optimizations sometimes create new refs using ond + refs as the model. */ + DF_REF_FLAGS_CLEAR (this_ref, DF_HARD_REG_LIVE); + + /* See if this ref needs to have DF_HARD_REG_LIVE bit set. */ + if ((regno < FIRST_PSEUDO_REGISTER) + && (!DF_REF_IS_ARTIFICIAL (this_ref))) { - case DF_REF_REG_DEF: - { - struct df_reg_info *reg_info = DF_REG_DEF_GET (df, regno); - unsigned int size = df->def_info.refs_organized_size - ? df->def_info.refs_organized_size - : df->def_info.bitmap_size; - - /* Add the ref to the reg_def chain. */ - reg_info->n_refs++; - df_reg_chain_create (reg_info, this_ref); - DF_REF_ID (this_ref) = size; - if (df->def_info.add_refs_inline) - { - if (size >= df->def_info.refs_size) - { - int new_size = size + size / 4; - df_grow_ref_info (&df->def_info, new_size); - } - /* Add the ref to the big array of defs. */ - DF_DEFS_SET (df, size, this_ref); - if (df->def_info.refs_organized_size) - df->def_info.refs_organized_size++; - } - - df->def_info.bitmap_size++; - - if (DF_REF_FLAGS (this_ref) & DF_REF_ARTIFICIAL) - { - struct df_scan_bb_info *bb_info - = df_scan_get_bb_info (dflow, bb->index); - this_ref->next_ref = bb_info->artificial_defs; - bb_info->artificial_defs = this_ref; - } - else - { - this_ref->next_ref = DF_INSN_GET (df, insn)->defs; - DF_INSN_GET (df, insn)->defs = this_ref; - } - } - break; - - case DF_REF_REG_MEM_LOAD: - case DF_REF_REG_MEM_STORE: - case DF_REF_REG_USE: - { - struct df_reg_info *reg_info = DF_REG_USE_GET (df, regno); - unsigned int size = df->use_info.refs_organized_size - ? df->use_info.refs_organized_size - : df->use_info.bitmap_size; - - /* Add the ref to the reg_use chain. */ - reg_info->n_refs++; - df_reg_chain_create (reg_info, this_ref); - DF_REF_ID (this_ref) = size; - if (df->use_info.add_refs_inline) - { - if (size >= df->use_info.refs_size) - { - int new_size = size + size / 4; - df_grow_ref_info (&df->use_info, new_size); - } - /* Add the ref to the big array of defs. */ - DF_USES_SET (df, size, this_ref); - if (df->def_info.refs_organized_size) - df->def_info.refs_organized_size++; - } - - df->use_info.bitmap_size++; - if (DF_REF_FLAGS (this_ref) & DF_REF_ARTIFICIAL) - { - struct df_scan_bb_info *bb_info - = df_scan_get_bb_info (dflow, bb->index); - this_ref->next_ref = bb_info->artificial_uses; - bb_info->artificial_uses = this_ref; - } - else - { - this_ref->next_ref = DF_INSN_GET (df, insn)->uses; - DF_INSN_GET (df, insn)->uses = this_ref; - } - } - break; - - default: - gcc_unreachable (); + if (DF_REF_TYPE (this_ref) == DF_REF_REG_DEF) + { + if (!DF_REF_FLAGS_IS_SET (this_ref, DF_REF_MAY_CLOBBER)) + DF_REF_FLAGS_SET (this_ref, DF_HARD_REG_LIVE); + } + else if (!(TEST_HARD_REG_BIT (elim_reg_set, regno) + && (regno == FRAME_POINTER_REGNUM + || regno == ARG_POINTER_REGNUM))) + DF_REF_FLAGS_SET (this_ref, DF_HARD_REG_LIVE); + } + if (collection_rec) + { + if (DF_REF_TYPE (this_ref) == DF_REF_REG_DEF) + collection_rec->def_vec[collection_rec->next_def++] = this_ref; + else if (DF_REF_FLAGS (this_ref) & DF_REF_IN_NOTE) + collection_rec->eq_use_vec[collection_rec->next_eq_use++] = this_ref; + else + collection_rec->use_vec[collection_rec->next_use++] = this_ref; } + return this_ref; } @@ -1049,45 +2619,26 @@ df_ref_create_structure (struct dataflow *dflow, rtx reg, rtx *loc, at address LOC within INSN of BB. */ static void -df_ref_record (struct dataflow *dflow, rtx reg, rtx *loc, +df_ref_record (struct df_collection_rec *collection_rec, + rtx reg, rtx *loc, basic_block bb, rtx insn, enum df_ref_type ref_type, - enum df_ref_flags ref_flags, - bool record_live) + enum df_ref_flags ref_flags) { - struct df *df = dflow->df; rtx oldreg = reg; unsigned int regno; gcc_assert (REG_P (reg) || GET_CODE (reg) == SUBREG); - /* For the reg allocator we are interested in some SUBREG rtx's, but not - all. Notably only those representing a word extraction from a multi-word - reg. As written in the docu those should have the form - (subreg:SI (reg:M A) N), with size(SImode) > size(Mmode). - XXX Is that true? We could also use the global word_mode variable. */ - if ((dflow->flags & DF_SUBREGS) == 0 - && GET_CODE (reg) == SUBREG - && (GET_MODE_SIZE (GET_MODE (reg)) < GET_MODE_SIZE (word_mode) - || GET_MODE_SIZE (GET_MODE (reg)) - >= GET_MODE_SIZE (GET_MODE (SUBREG_REG (reg))))) - { - loc = &SUBREG_REG (reg); - reg = *loc; - ref_flags |= DF_REF_STRIPPED; - } - regno = REGNO (GET_CODE (reg) == SUBREG ? SUBREG_REG (reg) : reg); if (regno < FIRST_PSEUDO_REGISTER) { - unsigned int i; - unsigned int endregno; struct df_mw_hardreg *hardreg = NULL; struct df_scan_problem_data *problem_data - = (struct df_scan_problem_data *) dflow->problem_data; - - if (!(dflow->flags & DF_HARD_REGS)) - return; + = (struct df_scan_problem_data *) df_scan->problem_data; + unsigned int i; + unsigned int endregno; + struct df_ref *ref; if (GET_CODE (reg) == SUBREG) { @@ -1098,63 +2649,41 @@ df_ref_record (struct dataflow *dflow, rtx reg, rtx *loc, else endregno = END_HARD_REGNO (reg); - /* If this is a multiword hardreg, we create some extra datastructures that - will enable us to easily build REG_DEAD and REG_UNUSED notes. */ + /* If this is a multiword hardreg, we create some extra + datastructures that will enable us to easily build REG_DEAD + and REG_UNUSED notes. */ if ((endregno != regno + 1) && insn) { - struct df_insn_info *insn_info = DF_INSN_GET (df, insn); /* Sets to a subreg of a multiword register are partial. Sets to a non-subreg of a multiword register are not. */ if (GET_CODE (oldreg) == SUBREG) ref_flags |= DF_REF_PARTIAL; ref_flags |= DF_REF_MW_HARDREG; + hardreg = pool_alloc (problem_data->mw_reg_pool); - hardreg->next = insn_info->mw_hardregs; - insn_info->mw_hardregs = hardreg; hardreg->type = ref_type; hardreg->flags = ref_flags; hardreg->mw_reg = reg; - hardreg->regs = NULL; - + hardreg->loc = loc; + hardreg->start_regno = regno; + hardreg->end_regno = endregno - 1; + hardreg->mw_order = df->ref_order++; + collection_rec->mw_vec[collection_rec->next_mw++] = hardreg; } for (i = regno; i < endregno; i++) { - struct df_ref *ref; - - /* Calls are handled at call site because regs_ever_live - doesn't include clobbered regs, only used ones. */ - if (ref_type == DF_REF_REG_DEF && record_live) - regs_ever_live[i] = 1; - else if ((ref_type == DF_REF_REG_USE - || ref_type == DF_REF_REG_MEM_STORE - || ref_type == DF_REF_REG_MEM_LOAD) - && ((ref_flags & DF_REF_ARTIFICIAL) == 0)) - { - /* Set regs_ever_live on uses of non-eliminable frame - pointers and arg pointers. */ - if (!(TEST_HARD_REG_BIT (elim_reg_set, regno) - && (regno == FRAME_POINTER_REGNUM - || regno == ARG_POINTER_REGNUM))) - regs_ever_live[i] = 1; - } - - ref = df_ref_create_structure (dflow, regno_reg_rtx[i], loc, + ref = df_ref_create_structure (collection_rec, regno_reg_rtx[i], loc, bb, insn, ref_type, ref_flags); - if (hardreg) - { - struct df_link *link = pool_alloc (problem_data->mw_link_pool); - link->next = hardreg->regs; - link->ref = ref; - hardreg->regs = link; - } + gcc_assert (ORIGINAL_REGNO (DF_REF_REG (ref)) == i); } } else { - df_ref_create_structure (dflow, reg, loc, - bb, insn, ref_type, ref_flags); + struct df_ref *ref; + ref = df_ref_create_structure (collection_rec, reg, loc, bb, insn, + ref_type, ref_flags); } } @@ -1181,9 +2710,9 @@ df_read_modify_subreg_p (rtx x) df_uses_record. */ static void -df_def_record_1 (struct dataflow *dflow, rtx x, - basic_block bb, rtx insn, - enum df_ref_flags flags, bool record_live) +df_def_record_1 (struct df_collection_rec *collection_rec, + rtx x, basic_block bb, rtx insn, + enum df_ref_flags flags) { rtx *loc; rtx dst; @@ -1207,10 +2736,10 @@ df_def_record_1 (struct dataflow *dflow, rtx x, rtx temp = XVECEXP (dst, 0, i); if (GET_CODE (temp) == EXPR_LIST || GET_CODE (temp) == CLOBBER || GET_CODE (temp) == SET) - df_def_record_1 (dflow, temp, bb, insn, + df_def_record_1 (collection_rec, + temp, bb, insn, GET_CODE (temp) == CLOBBER - ? flags | DF_REF_MUST_CLOBBER : flags, - record_live); + ? flags | DF_REF_MUST_CLOBBER : flags); } return; } @@ -1247,27 +2776,30 @@ df_def_record_1 (struct dataflow *dflow, rtx x, if (REG_P (dst) || (GET_CODE (dst) == SUBREG && REG_P (SUBREG_REG (dst)))) - df_ref_record (dflow, dst, loc, bb, insn, - DF_REF_REG_DEF, flags, record_live); + df_ref_record (collection_rec, + dst, loc, bb, insn, DF_REF_REG_DEF, flags); } /* Process all the registers defined in the pattern rtx, X. */ static void -df_defs_record (struct dataflow *dflow, rtx x, basic_block bb, rtx insn) +df_defs_record (struct df_collection_rec *collection_rec, + rtx x, basic_block bb, rtx insn, enum df_ref_flags flags) { RTX_CODE code = GET_CODE (x); if (code == SET || code == CLOBBER) { /* Mark the single def within the pattern. */ - df_def_record_1 (dflow, x, bb, insn, - code == CLOBBER ? DF_REF_MUST_CLOBBER : 0, true); + enum df_ref_flags clobber_flags = flags; + clobber_flags |= (code == CLOBBER) ? DF_REF_MUST_CLOBBER : 0; + df_def_record_1 (collection_rec, x, bb, insn, clobber_flags); } else if (code == COND_EXEC) { - df_defs_record (dflow, COND_EXEC_CODE (x), bb, insn); + df_defs_record (collection_rec, COND_EXEC_CODE (x), + bb, insn, DF_REF_CONDITIONAL); } else if (code == PARALLEL) { @@ -1275,7 +2807,7 @@ df_defs_record (struct dataflow *dflow, rtx x, basic_block bb, rtx insn) /* Mark the multiple defs within the pattern. */ for (i = XVECLEN (x, 0) - 1; i >= 0; i--) - df_defs_record (dflow, XVECEXP (x, 0, i), bb, insn); + df_defs_record (collection_rec, XVECEXP (x, 0, i), bb, insn, flags); } } @@ -1283,11 +2815,13 @@ df_defs_record (struct dataflow *dflow, rtx x, basic_block bb, rtx insn) /* Process all the registers used in the rtx at address LOC. */ static void -df_uses_record (struct dataflow *dflow, rtx *loc, enum df_ref_type ref_type, +df_uses_record (struct df_collection_rec *collection_rec, + rtx *loc, enum df_ref_type ref_type, basic_block bb, rtx insn, enum df_ref_flags flags) { RTX_CODE code; rtx x; + retry: x = *loc; if (!x) @@ -1311,15 +2845,17 @@ df_uses_record (struct dataflow *dflow, rtx *loc, enum df_ref_type ref_type, /* If we are clobbering a MEM, mark any registers inside the address as being used. */ if (MEM_P (XEXP (x, 0))) - df_uses_record (dflow, &XEXP (XEXP (x, 0), 0), + df_uses_record (collection_rec, + &XEXP (XEXP (x, 0), 0), DF_REF_REG_MEM_STORE, bb, insn, flags); /* If we're clobbering a REG then we have a def so ignore. */ return; case MEM: - df_uses_record (dflow, &XEXP (x, 0), DF_REF_REG_MEM_LOAD, bb, insn, - flags & DF_REF_IN_NOTE); + df_uses_record (collection_rec, + &XEXP (x, 0), DF_REF_REG_MEM_LOAD, + bb, insn, flags & DF_REF_IN_NOTE); return; case SUBREG: @@ -1329,29 +2865,30 @@ df_uses_record (struct dataflow *dflow, rtx *loc, enum df_ref_type ref_type, if (!REG_P (SUBREG_REG (x))) { loc = &SUBREG_REG (x); - df_uses_record (dflow, loc, ref_type, bb, insn, flags); + df_uses_record (collection_rec, loc, ref_type, bb, insn, flags); return; } /* ... Fall through ... */ case REG: - df_ref_record (dflow, x, loc, bb, insn, ref_type, flags, true); + df_ref_record (collection_rec, + x, loc, bb, insn, ref_type, flags); return; case SET: { rtx dst = SET_DEST (x); gcc_assert (!(flags & DF_REF_IN_NOTE)); - df_uses_record (dflow, &SET_SRC (x), DF_REF_REG_USE, bb, insn, flags); + df_uses_record (collection_rec, + &SET_SRC (x), DF_REF_REG_USE, bb, insn, flags); switch (GET_CODE (dst)) { case SUBREG: if (df_read_modify_subreg_p (dst)) { - df_uses_record (dflow, &SUBREG_REG (dst), - DF_REF_REG_USE, bb, - insn, flags | DF_REF_READ_WRITE); + df_uses_record (collection_rec, &SUBREG_REG (dst), + DF_REF_REG_USE, bb, insn, flags | DF_REF_READ_WRITE); break; } /* Fall through. */ @@ -1362,9 +2899,8 @@ df_uses_record (struct dataflow *dflow, rtx *loc, enum df_ref_type ref_type, case CC0: break; case MEM: - df_uses_record (dflow, &XEXP (dst, 0), - DF_REF_REG_MEM_STORE, - bb, insn, flags); + df_uses_record (collection_rec, &XEXP (dst, 0), + DF_REF_REG_MEM_STORE, bb, insn, flags); break; case STRICT_LOW_PART: { @@ -1372,21 +2908,18 @@ df_uses_record (struct dataflow *dflow, rtx *loc, enum df_ref_type ref_type, /* A strict_low_part uses the whole REG and not just the SUBREG. */ dst = XEXP (dst, 0); - df_uses_record (dflow, - (GET_CODE (dst) == SUBREG) - ? &SUBREG_REG (dst) : temp, - DF_REF_REG_USE, bb, - insn, DF_REF_READ_WRITE); + df_uses_record (collection_rec, + (GET_CODE (dst) == SUBREG) ? &SUBREG_REG (dst) : temp, + DF_REF_REG_USE, bb, insn, DF_REF_READ_WRITE); } break; case ZERO_EXTRACT: case SIGN_EXTRACT: - df_uses_record (dflow, &XEXP (dst, 0), - DF_REF_REG_USE, bb, insn, - DF_REF_READ_WRITE); - df_uses_record (dflow, &XEXP (dst, 1), + df_uses_record (collection_rec, &XEXP (dst, 0), + DF_REF_REG_USE, bb, insn, DF_REF_READ_WRITE); + df_uses_record (collection_rec, &XEXP (dst, 1), DF_REF_REG_USE, bb, insn, flags); - df_uses_record (dflow, &XEXP (dst, 2), + df_uses_record (collection_rec, &XEXP (dst, 2), DF_REF_REG_USE, bb, insn, flags); dst = XEXP (dst, 0); break; @@ -1422,9 +2955,8 @@ df_uses_record (struct dataflow *dflow, rtx *loc, enum df_ref_type ref_type, In order to maintain the status quo with regard to liveness and uses, we do what flow.c did and just mark any regs we - can find in ASM_OPERANDS as used. Later on, when liveness - is computed, asm insns are scanned and regs_asm_clobbered - is filled out. + can find in ASM_OPERANDS as used. In global asm insns are + scanned and regs_asm_clobbered is filled out. For all ASM_OPERANDS, we must traverse the vector of input operands. We can not just fall through here since then we @@ -1436,7 +2968,7 @@ df_uses_record (struct dataflow *dflow, rtx *loc, enum df_ref_type ref_type, int j; for (j = 0; j < ASM_OPERANDS_INPUT_LENGTH (x); j++) - df_uses_record (dflow, &ASM_OPERANDS_INPUT (x, j), + df_uses_record (collection_rec, &ASM_OPERANDS_INPUT (x, j), DF_REF_REG_USE, bb, insn, flags); return; } @@ -1450,9 +2982,9 @@ df_uses_record (struct dataflow *dflow, rtx *loc, enum df_ref_type ref_type, case PRE_MODIFY: case POST_MODIFY: /* Catch the def of the register being modified. */ - flags |= DF_REF_READ_WRITE; - df_ref_record (dflow, XEXP (x, 0), &XEXP (x, 0), bb, insn, - DF_REF_REG_DEF, flags, true); + flags |= DF_REF_READ_WRITE | DF_REF_PRE_POST_MODIFY; + df_ref_record (collection_rec, XEXP (x, 0), &XEXP (x, 0), bb, insn, + DF_REF_REG_DEF, flags); /* ... Fall through to handle uses ... */ @@ -1475,138 +3007,183 @@ df_uses_record (struct dataflow *dflow, rtx *loc, enum df_ref_type ref_type, loc = &XEXP (x, 0); goto retry; } - df_uses_record (dflow, &XEXP (x, i), ref_type, bb, insn, flags); + df_uses_record (collection_rec, &XEXP (x, i), ref_type, bb, insn, flags); } else if (fmt[i] == 'E') { int j; for (j = 0; j < XVECLEN (x, i); j++) - df_uses_record (dflow, &XVECEXP (x, i, j), ref_type, - bb, insn, flags); + df_uses_record (collection_rec, + &XVECEXP (x, i, j), ref_type, bb, insn, flags); } } } -} - -/* Return true if *LOC contains an asm. */ -static int -df_insn_contains_asm_1 (rtx *loc, void *data ATTRIBUTE_UNUSED) -{ - if ( !*loc) - return 0; - if (GET_CODE (*loc) == ASM_OPERANDS) - return 1; - return 0; + return; } -/* Return true if INSN contains an ASM. */ +/* For all DF_REF_CONDITIONAL defs, add a corresponding uses. */ -static int -df_insn_contains_asm (rtx insn) +static void +df_get_conditional_uses (struct df_collection_rec *collection_rec) { - return for_each_rtx (&insn, df_insn_contains_asm_1, NULL); + unsigned int i; + for (i = 0; i < collection_rec->next_def; i++) + { + struct df_ref *ref = collection_rec->def_vec[i]; + if (DF_REF_FLAGS_IS_SET (ref, DF_REF_CONDITIONAL)) + { + struct df_ref *use + = df_ref_create_structure (collection_rec, DF_REF_REG (ref), + DF_REF_LOC (ref), DF_REF_BB (ref), + DF_REF_INSN (ref), DF_REF_REG_USE, + DF_REF_FLAGS (ref) & ~DF_REF_CONDITIONAL); + DF_REF_REGNO (use) = DF_REF_REGNO (ref); + } + } } - -/* Record all the refs for DF within INSN of basic block BB. */ +/* Get call's extra defs and uses. */ static void -df_insn_refs_record (struct dataflow *dflow, basic_block bb, rtx insn) +df_get_call_refs (struct df_collection_rec * collection_rec, + basic_block bb, + rtx insn, + enum df_ref_flags flags) { - struct df *df = dflow->df; - int i; + rtx note; + bitmap_iterator bi; + unsigned int ui; + bool is_sibling_call; + unsigned int i; + bitmap defs_generated = BITMAP_ALLOC (&df_bitmap_obstack); - if (INSN_P (insn)) + /* Do not generate clobbers for registers that are the result of the + call. This causes ordering problems in the chain building code + depending on which def is seen first. */ + for (i=0; i<collection_rec->next_def; i++) { - rtx note; + struct df_ref *def = collection_rec->def_vec[i]; + bitmap_set_bit (defs_generated, DF_REF_REGNO (def)); + } - if (df_insn_contains_asm (insn)) - DF_INSN_CONTAINS_ASM (df, insn) = true; - - /* Record register defs. */ - df_defs_record (dflow, PATTERN (insn), bb, insn); + /* Record the registers used to pass arguments, and explicitly + noted as clobbered. */ + for (note = CALL_INSN_FUNCTION_USAGE (insn); note; + note = XEXP (note, 1)) + { + if (GET_CODE (XEXP (note, 0)) == USE) + df_uses_record (collection_rec, &XEXP (XEXP (note, 0), 0), + DF_REF_REG_USE, bb, insn, flags); + else if (GET_CODE (XEXP (note, 0)) == CLOBBER) + { + unsigned int regno = REGNO (XEXP (XEXP (note, 0), 0)); + if (!bitmap_bit_p (defs_generated, regno)) + df_defs_record (collection_rec, XEXP (note, 0), bb, insn, flags); + } + } - if (dflow->flags & DF_EQUIV_NOTES) - for (note = REG_NOTES (insn); note; - note = XEXP (note, 1)) - { - switch (REG_NOTE_KIND (note)) - { - case REG_EQUIV: - case REG_EQUAL: - df_uses_record (dflow, &XEXP (note, 0), DF_REF_REG_USE, - bb, insn, DF_REF_IN_NOTE); - default: - break; - } - } + /* The stack ptr is used (honorarily) by a CALL insn. */ + df_ref_record (collection_rec, regno_reg_rtx[STACK_POINTER_REGNUM], + NULL, bb, insn, DF_REF_REG_USE, DF_REF_CALL_STACK_USAGE | flags); - if (CALL_P (insn)) - { - rtx note; + /* Calls may also reference any of the global registers, + so they are recorded as used. */ + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (global_regs[i]) + df_ref_record (collection_rec, regno_reg_rtx[i], + NULL, bb, insn, DF_REF_REG_USE, flags); - /* Record the registers used to pass arguments, and explicitly - noted as clobbered. */ - for (note = CALL_INSN_FUNCTION_USAGE (insn); note; - note = XEXP (note, 1)) - { - if (GET_CODE (XEXP (note, 0)) == USE) - df_uses_record (dflow, &XEXP (XEXP (note, 0), 0), - DF_REF_REG_USE, - bb, insn, 0); - else if (GET_CODE (XEXP (note, 0)) == CLOBBER) - { - df_defs_record (dflow, XEXP (note, 0), bb, insn); - if (REG_P (XEXP (XEXP (note, 0), 0))) - { - rtx reg = XEXP (XEXP (note, 0), 0); - int regno_last; - int regno_first; - int i; - - regno_last = regno_first = REGNO (reg); - if (regno_first < FIRST_PSEUDO_REGISTER) - regno_last - += hard_regno_nregs[regno_first][GET_MODE (reg)] - 1; - for (i = regno_first; i <= regno_last; i++) - regs_ever_live[i] = 1; - } - } - } + is_sibling_call = SIBLING_CALL_P (insn); + EXECUTE_IF_SET_IN_BITMAP (df_invalidated_by_call, 0, ui, bi) + { + if ((!bitmap_bit_p (defs_generated, ui)) + && (!is_sibling_call + || !bitmap_bit_p (df->exit_block_uses, ui) + || refers_to_regno_p (ui, ui+1, + current_function_return_rtx, NULL))) + + df_ref_record (collection_rec, regno_reg_rtx[ui], + NULL, bb, insn, DF_REF_REG_DEF, DF_REF_MAY_CLOBBER | flags); + } - /* The stack ptr is used (honorarily) by a CALL insn. */ - df_uses_record (dflow, ®no_reg_rtx[STACK_POINTER_REGNUM], - DF_REF_REG_USE, bb, insn, - 0); + BITMAP_FREE (defs_generated); + return; +} - if (dflow->flags & DF_HARD_REGS) - { - bitmap_iterator bi; - unsigned int ui; - /* Calls may also reference any of the global registers, - so they are recorded as used. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (global_regs[i]) - df_uses_record (dflow, ®no_reg_rtx[i], - DF_REF_REG_USE, bb, insn, - 0); - EXECUTE_IF_SET_IN_BITMAP (df_invalidated_by_call, 0, ui, bi) - df_ref_record (dflow, regno_reg_rtx[ui], ®no_reg_rtx[ui], bb, - insn, DF_REF_REG_DEF, DF_REF_MAY_CLOBBER, false); - } - } +/* Collect all refs in the INSN. This function is free of any + side-effect - it will create and return a lists of df_ref's in the + COLLECTION_REC without putting those refs into existing ref chains + and reg chains. */ - /* Record the register uses. */ - df_uses_record (dflow, &PATTERN (insn), - DF_REF_REG_USE, bb, insn, 0); +static void +df_insn_refs_collect (struct df_collection_rec* collection_rec, + basic_block bb, rtx insn) +{ + rtx note; + bool is_cond_exec = (GET_CODE (PATTERN (insn)) == COND_EXEC); + /* Clear out the collection record. */ + collection_rec->next_def = 0; + collection_rec->next_use = 0; + collection_rec->next_eq_use = 0; + collection_rec->next_mw = 0; + + /* Record register defs. */ + df_defs_record (collection_rec, PATTERN (insn), bb, insn, 0); + + /* Process REG_EQUIV/REG_EQUAL notes */ + for (note = REG_NOTES (insn); note; + note = XEXP (note, 1)) + { + switch (REG_NOTE_KIND (note)) + { + case REG_EQUIV: + case REG_EQUAL: + df_uses_record (collection_rec, + &XEXP (note, 0), DF_REF_REG_USE, + bb, insn, DF_REF_IN_NOTE); + break; + case REG_NON_LOCAL_GOTO: + /* The frame ptr is used by a non-local goto. */ + df_ref_record (collection_rec, + regno_reg_rtx[FRAME_POINTER_REGNUM], + NULL, + bb, insn, + DF_REF_REG_USE, 0); +#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM + df_ref_record (collection_rec, + regno_reg_rtx[HARD_FRAME_POINTER_REGNUM], + NULL, + bb, insn, + DF_REF_REG_USE, 0); +#endif + break; + default: + break; + } } + + if (CALL_P (insn)) + df_get_call_refs (collection_rec, bb, insn, + (is_cond_exec) ? DF_REF_CONDITIONAL : 0); + + /* Record the register uses. */ + df_uses_record (collection_rec, + &PATTERN (insn), DF_REF_REG_USE, bb, insn, 0); + + /* DF_REF_CONDITIONAL needs corresponding USES. */ + if (is_cond_exec) + df_get_conditional_uses (collection_rec); + + df_canonize_collection_rec (collection_rec); } -static bool +/* Return true if any pred of BB is an eh. */ + +bool df_has_eh_preds (basic_block bb) { edge e; @@ -1620,45 +3197,72 @@ df_has_eh_preds (basic_block bb) return false; } -/* Record all the refs within the basic block BB. */ -static void -df_bb_refs_record (struct dataflow *dflow, basic_block bb) +/* Recompute the luids for the insns in BB. */ + +void +df_recompute_luids (basic_block bb) { - struct df *df = dflow->df; rtx insn; int luid = 0; - struct df_scan_bb_info *bb_info = df_scan_get_bb_info (dflow, bb->index); - bitmap artificial_uses_at_bottom = NULL; - - if (dflow->flags & DF_HARD_REGS) - artificial_uses_at_bottom = BITMAP_ALLOC (NULL); - /* Need to make sure that there is a record in the basic block info. */ - if (!bb_info) - { - bb_info = (struct df_scan_bb_info *) pool_alloc (dflow->block_pool); - df_scan_set_bb_info (dflow, bb->index, bb_info); - bb_info->artificial_defs = NULL; - bb_info->artificial_uses = NULL; - } + df_grow_insn_info (); /* Scan the block an insn at a time from beginning to end. */ FOR_BB_INSNS (bb, insn) { - df_insn_create_insn_record (dflow, insn); - if (INSN_P (insn)) + struct df_insn_info *insn_info = DF_INSN_GET (insn); + /* Inserting labels does not always trigger the incremental + rescanning. */ + if (!insn_info) { - /* Record defs within INSN. */ - DF_INSN_LUID (df, insn) = luid++; - df_insn_refs_record (dflow, bb, insn); + gcc_assert (!INSN_P (insn)); + df_insn_create_insn_record (insn); } - DF_INSN_LUID (df, insn) = luid; + + DF_INSN_LUID (insn) = luid; + if (INSN_P (insn)) + luid++; + } +} + + +/* Returns true if the function entry needs to + define the static chain register. */ + +static bool +df_need_static_chain_reg (struct function *fun) +{ + tree fun_context = decl_function_context (fun->decl); + return fun_context + && DECL_NO_STATIC_CHAIN (fun_context) == false; +} + + +/* Collect all artificial refs at the block level for BB and add them + to COLLECTION_REC. */ + +static void +df_bb_refs_collect (struct df_collection_rec *collection_rec, basic_block bb) +{ + collection_rec->next_def = 0; + collection_rec->next_use = 0; + collection_rec->next_eq_use = 0; + collection_rec->next_mw = 0; + + if (bb->index == ENTRY_BLOCK) + { + df_entry_block_defs_collect (collection_rec, df->entry_block_defs); + return; + } + else if (bb->index == EXIT_BLOCK) + { + df_exit_block_uses_collect (collection_rec, df->exit_block_uses); + return; } #ifdef EH_RETURN_DATA_REGNO - if ((dflow->flags & DF_HARD_REGS) - && df_has_eh_preds (bb)) + if (df_has_eh_preds (bb)) { unsigned int i; /* Mark the registers that will contain data for the handler. */ @@ -1667,19 +3271,16 @@ df_bb_refs_record (struct dataflow *dflow, basic_block bb) unsigned regno = EH_RETURN_DATA_REGNO (i); if (regno == INVALID_REGNUM) break; - df_ref_record (dflow, regno_reg_rtx[regno], ®no_reg_rtx[regno], - bb, NULL, - DF_REF_REG_DEF, DF_REF_ARTIFICIAL | DF_REF_AT_TOP, - false); + df_ref_record (collection_rec, regno_reg_rtx[regno], NULL, + bb, NULL, DF_REF_REG_DEF, DF_REF_AT_TOP); } } #endif - if ((dflow->flags & DF_HARD_REGS) - && df_has_eh_preds (bb)) - { #ifdef EH_USES + if (df_has_eh_preds (bb)) + { unsigned int i; /* This code is putting in an artificial ref for the use at the TOP of the block that receives the exception. It is too @@ -1694,132 +3295,172 @@ df_bb_refs_record (struct dataflow *dflow, basic_block bb) eh-receiver for all of the edges at once. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (EH_USES (i)) - df_uses_record (dflow, ®no_reg_rtx[i], - DF_REF_REG_USE, bb, NULL, - DF_REF_ARTIFICIAL | DF_REF_AT_TOP); -#endif - - /* The following code (down thru the arg_pointer setting APPEARS - to be necessary because there is nothing that actually - describes what the exception handling code may actually need - to keep alive. */ - if (reload_completed) - { - if (frame_pointer_needed) - { - bitmap_set_bit (artificial_uses_at_bottom, FRAME_POINTER_REGNUM); -#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM - bitmap_set_bit (artificial_uses_at_bottom, HARD_FRAME_POINTER_REGNUM); -#endif - } -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - if (fixed_regs[ARG_POINTER_REGNUM]) - bitmap_set_bit (artificial_uses_at_bottom, ARG_POINTER_REGNUM); -#endif - } + df_ref_record (collection_rec, regno_reg_rtx[i], NULL, + bb, NULL, DF_REF_REG_USE, DF_REF_AT_TOP); } - - if ((dflow->flags & DF_HARD_REGS) - && bb->index >= NUM_FIXED_BLOCKS) - { - /* 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) - { - - /* Any reference to any pseudo before reload is a potential - reference of the frame pointer. */ - bitmap_set_bit (artificial_uses_at_bottom, FRAME_POINTER_REGNUM); - -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - /* Pseudos with argument area equivalences may require - reloading via the argument pointer. */ - if (fixed_regs[ARG_POINTER_REGNUM]) - bitmap_set_bit (artificial_uses_at_bottom, ARG_POINTER_REGNUM); #endif - - /* Any constant, or pseudo with constant equivalences, may - require reloading from memory using the pic register. */ - if ((unsigned) PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM - && fixed_regs[PIC_OFFSET_TABLE_REGNUM]) - bitmap_set_bit (artificial_uses_at_bottom, PIC_OFFSET_TABLE_REGNUM); - } - /* The all-important stack pointer must always be live. */ - bitmap_set_bit (artificial_uses_at_bottom, STACK_POINTER_REGNUM); - } - if (dflow->flags & DF_HARD_REGS) + /* Add the hard_frame_pointer if this block is the target of a + non-local goto. */ + if (bb->flags & BB_NON_LOCAL_GOTO_TARGET) + df_ref_record (collection_rec, hard_frame_pointer_rtx, NULL, + bb, NULL, DF_REF_REG_DEF, DF_REF_AT_TOP); + + /* Add the artificial uses. */ + if (bb->index >= NUM_FIXED_BLOCKS) { bitmap_iterator bi; unsigned int regno; + bitmap au = df_has_eh_preds (bb) + ? df->eh_block_artificial_uses + : df->regular_block_artificial_uses; - EXECUTE_IF_SET_IN_BITMAP (artificial_uses_at_bottom, 0, regno, bi) + EXECUTE_IF_SET_IN_BITMAP (au, 0, regno, bi) { - df_uses_record (dflow, ®no_reg_rtx[regno], - DF_REF_REG_USE, bb, NULL, DF_REF_ARTIFICIAL); + df_ref_record (collection_rec, regno_reg_rtx[regno], NULL, + bb, NULL, DF_REF_REG_USE, 0); } - - BITMAP_FREE (artificial_uses_at_bottom); } + + df_canonize_collection_rec (collection_rec); } -/* Records the implicit definitions at targets of nonlocal gotos in BLOCKS. */ -static void -record_nonlocal_goto_receiver_defs (struct dataflow *dflow, bitmap blocks) +/* Record all the refs within the basic block BB_INDEX and scan the instructions if SCAN_INSNS. */ + +void +df_bb_refs_record (int bb_index, bool scan_insns) { - rtx x; - basic_block bb; + basic_block bb = BASIC_BLOCK (bb_index); + rtx insn; + int luid = 0; + struct df_scan_bb_info *bb_info; + struct df_collection_rec collection_rec; + collection_rec.def_vec = alloca (sizeof (struct df_ref*) * 1000); + collection_rec.use_vec = alloca (sizeof (struct df_ref*) * 1000); + collection_rec.eq_use_vec = alloca (sizeof (struct df_ref*) * 1000); + collection_rec.mw_vec = alloca (sizeof (struct df_mw_hardreg*) * 100); - /* See expand_builtin_setjmp_receiver; hard_frame_pointer_rtx is used in - the nonlocal goto receiver, and needs to be considered defined - implicitly. */ - if (!(dflow->flags & DF_HARD_REGS)) + if (!df) return; - for (x = nonlocal_goto_handler_labels; x; x = XEXP (x, 1)) - { - bb = BLOCK_FOR_INSN (XEXP (x, 0)); - if (!bitmap_bit_p (blocks, bb->index)) - continue; + bb_info = df_scan_get_bb_info (bb_index); - df_ref_record (dflow, hard_frame_pointer_rtx, &hard_frame_pointer_rtx, - bb, NULL, - DF_REF_REG_DEF, DF_REF_ARTIFICIAL | DF_REF_AT_TOP, - false); + /* Need to make sure that there is a record in the basic block info. */ + if (!bb_info) + { + bb_info = (struct df_scan_bb_info *) pool_alloc (df_scan->block_pool); + df_scan_set_bb_info (bb_index, bb_info); + bb_info->artificial_defs = NULL; + bb_info->artificial_uses = NULL; } + + if (scan_insns) + /* Scan the block an insn at a time from beginning to end. */ + FOR_BB_INSNS (bb, insn) + { + struct df_insn_info *insn_info = DF_INSN_GET (insn); + gcc_assert (!insn_info); + + df_insn_create_insn_record (insn); + if (INSN_P (insn)) + { + /* Record refs within INSN. */ + DF_INSN_LUID (insn) = luid++; + df_insn_refs_collect (&collection_rec, bb, insn); + df_refs_add_to_chains (&collection_rec, bb, insn); + } + DF_INSN_LUID (insn) = luid; + } + + /* Other block level artificial refs */ + df_bb_refs_collect (&collection_rec, bb); + df_refs_add_to_chains (&collection_rec, bb, NULL); + + /* Now that the block has been processed, set the block as dirty so + lr and ur will get it processed. */ + df_set_bb_dirty (bb); } -/* Record all the refs in the basic blocks specified by BLOCKS. */ + +/* Get the artificial use set for a regular (i.e. non-exit/non-entry) + block. */ static void -df_refs_record (struct dataflow *dflow, bitmap blocks) +df_get_regular_block_artificial_uses (bitmap regular_block_artificial_uses) { - unsigned int bb_index; - bitmap_iterator bi; + bitmap_clear (regular_block_artificial_uses); - EXECUTE_IF_SET_IN_BITMAP (blocks, 0, bb_index, bi) + if (reload_completed) { - basic_block bb = BASIC_BLOCK (bb_index); - df_bb_refs_record (dflow, bb); + if (frame_pointer_needed) + bitmap_set_bit (regular_block_artificial_uses, HARD_FRAME_POINTER_REGNUM); } + else + /* 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. */ + { + /* Any reference to any pseudo before reload is a potential + reference of the frame pointer. */ + bitmap_set_bit (regular_block_artificial_uses, FRAME_POINTER_REGNUM); + +#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM + bitmap_set_bit (regular_block_artificial_uses, HARD_FRAME_POINTER_REGNUM); +#endif - if (bitmap_bit_p (blocks, EXIT_BLOCK)) - df_record_exit_block_uses (dflow); +#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM + /* Pseudos with argument area equivalences may require + reloading via the argument pointer. */ + if (fixed_regs[ARG_POINTER_REGNUM]) + bitmap_set_bit (regular_block_artificial_uses, ARG_POINTER_REGNUM); +#endif + + /* Any constant, or pseudo with constant equivalences, may + require reloading from memory using the pic register. */ + if ((unsigned) PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM + && fixed_regs[PIC_OFFSET_TABLE_REGNUM]) + bitmap_set_bit (regular_block_artificial_uses, PIC_OFFSET_TABLE_REGNUM); + } + /* The all-important stack pointer must always be live. */ + bitmap_set_bit (regular_block_artificial_uses, STACK_POINTER_REGNUM); +} + + +/* Get the artificial use set for an eh block. */ - if (bitmap_bit_p (blocks, ENTRY_BLOCK)) - df_record_entry_block_defs (dflow); +static void +df_get_eh_block_artificial_uses (bitmap eh_block_artificial_uses) +{ + bitmap_clear (eh_block_artificial_uses); - if (current_function_has_nonlocal_label) - record_nonlocal_goto_receiver_defs (dflow, blocks); + /* The following code (down thru the arg_pointer seting APPEARS + to be necessary because there is nothing that actually + describes what the exception handling code may actually need + to keep alive. */ + if (reload_completed) + { + if (frame_pointer_needed) + { + bitmap_set_bit (eh_block_artificial_uses, FRAME_POINTER_REGNUM); +#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM + bitmap_set_bit (eh_block_artificial_uses, HARD_FRAME_POINTER_REGNUM); +#endif + } +#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM + if (fixed_regs[ARG_POINTER_REGNUM]) + bitmap_set_bit (eh_block_artificial_uses, ARG_POINTER_REGNUM); +#endif + } } + /*---------------------------------------------------------------------------- Specialized hard register scanning functions. ----------------------------------------------------------------------------*/ + /* Mark a register in SET. Hard registers in large modes get all of their component registers set as well. */ @@ -1841,29 +3482,25 @@ df_mark_reg (rtx reg, void *vset) } -/* Record the (conservative) set of hard registers that are defined on - entry to the function. */ + + +/* Set the bit for regs that are considered being defined at the entry. */ static void -df_record_entry_block_defs (struct dataflow *dflow) +df_get_entry_block_def_set (bitmap entry_block_defs) { - unsigned int i; - bitmap_iterator bi; rtx r; - struct df *df = dflow->df; + int i; - bitmap_clear (df->entry_block_defs); - - if (!(dflow->flags & DF_HARD_REGS)) - return; + bitmap_clear (entry_block_defs); for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) { if (FUNCTION_ARG_REGNO_P (i)) #ifdef INCOMING_REGNO - bitmap_set_bit (df->entry_block_defs, INCOMING_REGNO (i)); + bitmap_set_bit (entry_block_defs, INCOMING_REGNO (i)); #else - bitmap_set_bit (df->entry_block_defs, i); + bitmap_set_bit (entry_block_defs, i); #endif } @@ -1874,44 +3511,39 @@ df_record_entry_block_defs (struct dataflow *dflow) /* Defs for the callee saved registers are inserted so that the pushes have some defining location. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if ((call_used_regs[i] == 0) && (regs_ever_live[i])) - bitmap_set_bit (df->entry_block_defs, i); + if ((call_used_regs[i] == 0) && (df_regs_ever_live_p (i))) + bitmap_set_bit (entry_block_defs, i); } else { /* The always important stack pointer. */ - bitmap_set_bit (df->entry_block_defs, STACK_POINTER_REGNUM); + bitmap_set_bit (entry_block_defs, STACK_POINTER_REGNUM); -#ifdef INCOMING_RETURN_ADDR_RTX - if (REG_P (INCOMING_RETURN_ADDR_RTX)) - bitmap_set_bit (df->entry_block_defs, REGNO (INCOMING_RETURN_ADDR_RTX)); -#endif - /* If STATIC_CHAIN_INCOMING_REGNUM == STATIC_CHAIN_REGNUM only STATIC_CHAIN_REGNUM is defined. If they are different, we only care about the STATIC_CHAIN_INCOMING_REGNUM. */ #ifdef STATIC_CHAIN_INCOMING_REGNUM - bitmap_set_bit (df->entry_block_defs, STATIC_CHAIN_INCOMING_REGNUM); + bitmap_set_bit (entry_block_defs, STATIC_CHAIN_INCOMING_REGNUM); #else #ifdef STATIC_CHAIN_REGNUM - bitmap_set_bit (df->entry_block_defs, STATIC_CHAIN_REGNUM); + bitmap_set_bit (entry_block_defs, STATIC_CHAIN_REGNUM); #endif #endif - r = TARGET_STRUCT_VALUE_RTX (current_function_decl, true); + r = targetm.calls.struct_value_rtx (current_function_decl, true); if (r && REG_P (r)) - bitmap_set_bit (df->entry_block_defs, REGNO (r)); + bitmap_set_bit (entry_block_defs, REGNO (r)); } if ((!reload_completed) || frame_pointer_needed) { /* Any reference to any pseudo before reload is a potential reference of the frame pointer. */ - bitmap_set_bit (df->entry_block_defs, FRAME_POINTER_REGNUM); + bitmap_set_bit (entry_block_defs, FRAME_POINTER_REGNUM); #if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM /* If they are different, also mark the hard frame pointer as live. */ if (!LOCAL_REGNO (HARD_FRAME_POINTER_REGNUM)) - bitmap_set_bit (df->entry_block_defs, HARD_FRAME_POINTER_REGNUM); + bitmap_set_bit (entry_block_defs, HARD_FRAME_POINTER_REGNUM); #endif } @@ -1924,7 +3556,7 @@ df_record_entry_block_defs (struct dataflow *dflow) for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (EH_USES (i)) { - bitmap_set_bit (df->entry_block_defs, i); + bitmap_set_bit (entry_block_defs, i); } #endif @@ -1932,7 +3564,7 @@ df_record_entry_block_defs (struct dataflow *dflow) /* Pseudos with argument area equivalences may require reloading via the argument pointer. */ if (fixed_regs[ARG_POINTER_REGNUM]) - bitmap_set_bit (df->entry_block_defs, ARG_POINTER_REGNUM); + bitmap_set_bit (entry_block_defs, ARG_POINTER_REGNUM); #endif #ifdef PIC_OFFSET_TABLE_REGNUM @@ -1940,35 +3572,117 @@ df_record_entry_block_defs (struct dataflow *dflow) require reloading from memory using the pic register. */ if ((unsigned) PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM && fixed_regs[PIC_OFFSET_TABLE_REGNUM]) - bitmap_set_bit (df->entry_block_defs, PIC_OFFSET_TABLE_REGNUM); + bitmap_set_bit (entry_block_defs, PIC_OFFSET_TABLE_REGNUM); #endif } - targetm.live_on_entry (df->entry_block_defs); +#ifdef INCOMING_RETURN_ADDR_RTX + if (REG_P (INCOMING_RETURN_ADDR_RTX)) + bitmap_set_bit (entry_block_defs, REGNO (INCOMING_RETURN_ADDR_RTX)); +#endif + + targetm.live_on_entry (entry_block_defs); - EXECUTE_IF_SET_IN_BITMAP (df->entry_block_defs, 0, i, bi) + /* If the function has an incoming STATIC_CHAIN, + it has to show up in the entry def set. */ + if (df_need_static_chain_reg (cfun)) { - df_ref_record (dflow, regno_reg_rtx[i], ®no_reg_rtx[i], - ENTRY_BLOCK_PTR, NULL, - DF_REF_REG_DEF, DF_REF_ARTIFICIAL , false); +#if !defined (STATIC_CHAIN_INCOMING_REGNUM) \ + || STATIC_CHAIN_REGNUM == STATIC_CHAIN_INCOMING_REGNUM + bitmap_set_bit (entry_block_defs, STATIC_CHAIN_REGNUM); +#else + bitmap_set_bit (entry_block_defs, STATIC_CHAIN_INCOMING_REGNUM); +#endif } } -/* Record the set of hard registers that are used in the exit block. */ +/* Return the (conservative) set of hard registers that are defined on + entry to the function. + It uses df->entry_block_defs to determine which regster + reference to include. */ static void -df_record_exit_block_uses (struct dataflow *dflow) +df_entry_block_defs_collect (struct df_collection_rec *collection_rec, + bitmap entry_block_defs) { unsigned int i; bitmap_iterator bi; - struct df *df = dflow->df; - bitmap_clear (df->exit_block_uses); - - if (!(dflow->flags & DF_HARD_REGS)) - return; + EXECUTE_IF_SET_IN_BITMAP (entry_block_defs, 0, i, bi) + { + df_ref_record (collection_rec, regno_reg_rtx[i], NULL, + ENTRY_BLOCK_PTR, NULL, DF_REF_REG_DEF, 0); + } + + df_canonize_collection_rec (collection_rec); +} + + +/* Record the (conservative) set of hard registers that are defined on + entry to the function. */ + +static void +df_record_entry_block_defs (bitmap entry_block_defs) +{ + struct df_collection_rec collection_rec; + memset (&collection_rec, 0, sizeof (struct df_collection_rec)); + collection_rec.def_vec = alloca (sizeof (struct df_ref*) * FIRST_PSEUDO_REGISTER); + + df_entry_block_defs_collect (&collection_rec, entry_block_defs); + /* Process bb_refs chain */ + df_refs_add_to_chains (&collection_rec, BASIC_BLOCK (ENTRY_BLOCK), NULL); +} + + +/* Update the defs in the entry bolck. */ + +void +df_update_entry_block_defs (void) +{ + bitmap refs = BITMAP_ALLOC (&df_bitmap_obstack); + bool changed = false; + + df_get_entry_block_def_set (refs); + if (df->entry_block_defs) + { + if (!bitmap_equal_p (df->entry_block_defs, refs)) + { + struct df_scan_bb_info *bb_info = df_scan_get_bb_info (ENTRY_BLOCK); + df_ref_chain_delete_du_chain (bb_info->artificial_defs); + df_ref_chain_delete (bb_info->artificial_defs); + bb_info->artificial_defs = NULL; + changed = true; + } + } + else + { + struct df_scan_problem_data *problem_data + = (struct df_scan_problem_data *) df_scan->problem_data; + df->entry_block_defs = BITMAP_ALLOC (&problem_data->reg_bitmaps); + changed = true; + } + + if (changed) + { + df_record_entry_block_defs (refs); + bitmap_copy (df->entry_block_defs, refs); + df_set_bb_dirty (BASIC_BLOCK (ENTRY_BLOCK)); + } + BITMAP_FREE (refs); +} + + +/* Set the bit for regs that are considered being used at the exit. */ + +static void +df_get_exit_block_use_set (bitmap exit_block_uses) +{ + unsigned int i; + + bitmap_clear (exit_block_uses); + /* If exiting needs the right stack value, consider the stack pointer live at the end of the function. */ if ((HAVE_epilogue && epilogue_completed) @@ -1978,7 +3692,7 @@ df_record_exit_block_uses (struct dataflow *dflow) && flag_omit_frame_pointer) || current_function_sp_is_unchanging) { - bitmap_set_bit (df->exit_block_uses, STACK_POINTER_REGNUM); + bitmap_set_bit (exit_block_uses, STACK_POINTER_REGNUM); } /* Mark the frame pointer if needed at the end of the function. @@ -1987,11 +3701,11 @@ df_record_exit_block_uses (struct dataflow *dflow) if ((!reload_completed) || frame_pointer_needed) { - bitmap_set_bit (df->exit_block_uses, FRAME_POINTER_REGNUM); + bitmap_set_bit (exit_block_uses, FRAME_POINTER_REGNUM); #if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM /* If they are different, also mark the hard frame pointer as live. */ if (!LOCAL_REGNO (HARD_FRAME_POINTER_REGNUM)) - bitmap_set_bit (df->exit_block_uses, HARD_FRAME_POINTER_REGNUM); + bitmap_set_bit (exit_block_uses, HARD_FRAME_POINTER_REGNUM); #endif } @@ -2001,7 +3715,7 @@ df_record_exit_block_uses (struct dataflow *dflow) other means, if it is not fixed. */ if ((unsigned) PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM && fixed_regs[PIC_OFFSET_TABLE_REGNUM]) - bitmap_set_bit (df->exit_block_uses, PIC_OFFSET_TABLE_REGNUM); + bitmap_set_bit (exit_block_uses, PIC_OFFSET_TABLE_REGNUM); #endif /* Mark all global registers, and all registers used by the @@ -2009,15 +3723,15 @@ df_record_exit_block_uses (struct dataflow *dflow) may be referenced by our caller. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (global_regs[i] || EPILOGUE_USES (i)) - bitmap_set_bit (df->exit_block_uses, i); + bitmap_set_bit (exit_block_uses, i); if (HAVE_epilogue && epilogue_completed) { /* Mark all call-saved registers that we actually used. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (regs_ever_live[i] && !LOCAL_REGNO (i) + if (df_regs_ever_live_p (i) && !LOCAL_REGNO (i) && !TEST_HARD_REG_BIT (regs_invalidated_by_call, i)) - bitmap_set_bit (df->exit_block_uses, i); + bitmap_set_bit (exit_block_uses, i); } #ifdef EH_RETURN_DATA_REGNO @@ -2028,7 +3742,7 @@ df_record_exit_block_uses (struct dataflow *dflow) unsigned regno = EH_RETURN_DATA_REGNO (i); if (regno == INVALID_REGNUM) break; - bitmap_set_bit (df->exit_block_uses, regno); + bitmap_set_bit (exit_block_uses, regno); } #endif @@ -2038,7 +3752,7 @@ df_record_exit_block_uses (struct dataflow *dflow) { rtx tmp = EH_RETURN_STACKADJ_RTX; if (tmp && REG_P (tmp)) - df_mark_reg (tmp, df->exit_block_uses); + df_mark_reg (tmp, exit_block_uses); } #endif @@ -2048,22 +3762,100 @@ df_record_exit_block_uses (struct dataflow *dflow) { rtx tmp = EH_RETURN_HANDLER_RTX; if (tmp && REG_P (tmp)) - df_mark_reg (tmp, df->exit_block_uses); + df_mark_reg (tmp, exit_block_uses); } #endif - + /* Mark function return value. */ - diddle_return_value (df_mark_reg, (void*) df->exit_block_uses); + diddle_return_value (df_mark_reg, (void*) exit_block_uses); +} + + +/* Return the refs of hard registers that are used in the exit block. + It uses df->exit_block_uses to determine register to include. */ + +static void +df_exit_block_uses_collect (struct df_collection_rec *collection_rec, bitmap exit_block_uses) +{ + unsigned int i; + bitmap_iterator bi; + + EXECUTE_IF_SET_IN_BITMAP (exit_block_uses, 0, i, bi) + df_ref_record (collection_rec, regno_reg_rtx[i], NULL, + EXIT_BLOCK_PTR, NULL, DF_REF_REG_USE, 0); + +#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM + /* It is deliberate that this is not put in the exit block uses but + I do not know why. */ + if (reload_completed + && !bitmap_bit_p (exit_block_uses, ARG_POINTER_REGNUM) + && df_has_eh_preds (EXIT_BLOCK_PTR) + && fixed_regs[ARG_POINTER_REGNUM]) + df_ref_record (collection_rec, regno_reg_rtx[ARG_POINTER_REGNUM], NULL, + EXIT_BLOCK_PTR, NULL, DF_REF_REG_USE, 0); +#endif + + df_canonize_collection_rec (collection_rec); +} + + +/* Record the set of hard registers that are used in the exit block. + It uses df->exit_block_uses to determine which bit to include. */ + +static void +df_record_exit_block_uses (bitmap exit_block_uses) +{ + struct df_collection_rec collection_rec; + memset (&collection_rec, 0, sizeof (struct df_collection_rec)); + collection_rec.use_vec = alloca (sizeof (struct df_ref*) * FIRST_PSEUDO_REGISTER); + + df_exit_block_uses_collect (&collection_rec, exit_block_uses); + + /* Process bb_refs chain */ + df_refs_add_to_chains (&collection_rec, BASIC_BLOCK (EXIT_BLOCK), NULL); +} + - if (dflow->flags & DF_HARD_REGS) - EXECUTE_IF_SET_IN_BITMAP (df->exit_block_uses, 0, i, bi) - df_uses_record (dflow, ®no_reg_rtx[i], - DF_REF_REG_USE, EXIT_BLOCK_PTR, NULL, - DF_REF_ARTIFICIAL); +/* Update the uses in the exit block. */ + +void +df_update_exit_block_uses (void) +{ + bitmap refs = BITMAP_ALLOC (&df_bitmap_obstack); + bool changed = false; + + df_get_exit_block_use_set (refs); + if (df->exit_block_uses) + { + if (!bitmap_equal_p (df->exit_block_uses, refs)) + { + struct df_scan_bb_info *bb_info = df_scan_get_bb_info (EXIT_BLOCK); + df_ref_chain_delete_du_chain (bb_info->artificial_uses); + df_ref_chain_delete (bb_info->artificial_uses); + bb_info->artificial_uses = NULL; + changed = true; + } + } + else + { + struct df_scan_problem_data *problem_data + = (struct df_scan_problem_data *) df_scan->problem_data; + df->exit_block_uses = BITMAP_ALLOC (&problem_data->reg_bitmaps); + changed = true; + } + + if (changed) + { + df_record_exit_block_uses (refs); + bitmap_copy (df->exit_block_uses, refs); + df_set_bb_dirty (BASIC_BLOCK (EXIT_BLOCK)); + } + BITMAP_FREE (refs); } static bool initialized = false; + /* Initialize some platform specific structures. */ void @@ -2073,12 +3865,6 @@ df_hard_reg_init (void) #ifdef ELIMINABLE_REGS static const struct {const int from, to; } eliminables[] = ELIMINABLE_REGS; #endif - /* After reload, some ports add certain bits to regs_ever_live so - this cannot be reset. */ - - if (!reload_completed) - memset (regs_ever_live, 0, sizeof (regs_ever_live)); - if (initialized) return; @@ -2105,3 +3891,456 @@ df_hard_reg_init (void) initialized = true; } + + +/* Recompute the parts of scanning that are based on regs_ever_live + because something changed in that array. */ + +void +df_update_entry_exit_and_calls (void) +{ + basic_block bb; + + df_update_entry_block_defs (); + df_update_exit_block_uses (); + + /* The call insns need to be rescanned because there may be changes + in the set of registers clobbered across the call. */ + FOR_EACH_BB (bb) + { + rtx insn; + FOR_BB_INSNS (bb, insn) + { + if (INSN_P (insn) && CALL_P (insn)) + df_insn_rescan (insn); + } + } +} + + +/* Return true if hard REG is actually used in the some instruction. + There are a fair number of conditions that affect the setting of + this array. See the comment in df.h for df->hard_regs_live_count + for the conditions that this array is set. */ + +bool +df_hard_reg_used_p (unsigned int reg) +{ + gcc_assert (df); + return df->hard_regs_live_count[reg] != 0; +} + + +/* A count of the number of times REG is actually used in the some + instruction. There are a fair number of conditions that affect the + setting of this array. See the comment in df.h for + df->hard_regs_live_count for the conditions that this array is + set. */ + + +unsigned int +df_hard_reg_used_count (unsigned int reg) +{ + gcc_assert (df); + return df->hard_regs_live_count[reg]; +} + + +/* Get the value of regs_ever_live[REGNO]. */ + +bool +df_regs_ever_live_p (unsigned int regno) +{ + return regs_ever_live[regno]; +} + + +/* Set regs_ever_live[REGNO] to VALUE. If this cause regs_ever_live + to change, schedule that change for the next update. */ + +void +df_set_regs_ever_live (unsigned int regno, bool value) +{ + if (regs_ever_live[regno] == value) + return; + + regs_ever_live[regno] = value; + if (df) + df->redo_entry_and_exit = true; +} + + +/* Compute "regs_ever_live" information from the underlying df + information. Set the vector to all false if RESET. */ + +void +df_compute_regs_ever_live (bool reset) +{ + unsigned int i; + bool changed = df->redo_entry_and_exit; + + if (reset) + memset (regs_ever_live, 0, sizeof (regs_ever_live)); + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if ((!regs_ever_live[i]) && df_hard_reg_used_p (i)) + { + regs_ever_live[i] = true; + changed = true; + } + if (changed) + df_update_entry_exit_and_calls (); + df->redo_entry_and_exit = false; +} + + +/*---------------------------------------------------------------------------- + Dataflow ref information verification functions. + + df_reg_chain_mark (refs, regno, is_def, is_eq_use) + df_reg_chain_verify_unmarked (refs) + df_refs_verify (ref*, ref*, bool) + df_mws_verify (mw*, mw*, bool) + df_insn_refs_verify (collection_rec, bb, insn, bool) + df_bb_refs_verify (bb, refs, bool) + df_bb_verify (bb) + df_exit_block_bitmap_verify (bool) + df_entry_block_bitmap_verify (bool) + df_scan_verify () +----------------------------------------------------------------------------*/ + + +/* Mark all refs in the reg chain. Verify that all of the registers +are in the correct chain. */ + +static unsigned int +df_reg_chain_mark (struct df_ref *refs, unsigned int regno, + bool is_def, bool is_eq_use) +{ + unsigned int count = 0; + struct df_ref *ref; + for (ref = refs; ref; ref = DF_REF_NEXT_REG (ref)) + { + gcc_assert (!DF_REF_IS_REG_MARKED (ref)); + + /* If there are no def-use or use-def chains, make sure that all + of the chains are clear. */ + if (!df_chain) + gcc_assert (!DF_REF_CHAIN (ref)); + + /* Check to make sure the ref is in the correct chain. */ + gcc_assert (DF_REF_REGNO (ref) == regno); + if (is_def) + gcc_assert (DF_REF_TYPE(ref) == DF_REF_REG_DEF); + else + gcc_assert (DF_REF_TYPE(ref) != DF_REF_REG_DEF); + + if (is_eq_use) + gcc_assert ((DF_REF_FLAGS (ref) & DF_REF_IN_NOTE)); + else + gcc_assert ((DF_REF_FLAGS (ref) & DF_REF_IN_NOTE) == 0); + + if (ref->next_reg) + gcc_assert (ref->next_reg->prev_reg == ref); + count++; + DF_REF_REG_MARK (ref); + } + return count; +} + + +/* Verify that all of the registers in the chain are unmarked. */ + +static void +df_reg_chain_verify_unmarked (struct df_ref *refs) +{ + struct df_ref *ref; + for (ref = refs; ref; ref = DF_REF_NEXT_REG (ref)) + gcc_assert (!DF_REF_IS_REG_MARKED (ref)); +} + + +/* Verify that NEW_REC and OLD_REC have exactly the same members. */ + +static bool +df_refs_verify (struct df_ref **new_rec, struct df_ref **old_rec, + bool abort_if_fail) +{ + while ((*new_rec) && (*old_rec)) + { + if (!df_ref_equal_p (*new_rec, *old_rec)) + { + if (abort_if_fail) + gcc_assert (0); + else + return false; + } + + /* Abort if fail is called from the function level verifier. If + that is the context, mark this reg as being seem. */ + if (abort_if_fail) + { + gcc_assert (DF_REF_IS_REG_MARKED (*old_rec)); + DF_REF_REG_UNMARK (*old_rec); + } + + new_rec++; + old_rec++; + } + + if (abort_if_fail) + gcc_assert ((*new_rec == NULL) && (*old_rec == NULL)); + else + return ((*new_rec == NULL) && (*old_rec == NULL)); + return false; +} + + +/* Verify that NEW_REC and OLD_REC have exactly the same members. */ + +static bool +df_mws_verify (struct df_mw_hardreg **new_rec, struct df_mw_hardreg **old_rec, + bool abort_if_fail) +{ + while ((*new_rec) && (*old_rec)) + { + if (!df_mw_equal_p (*new_rec, *old_rec)) + { + if (abort_if_fail) + gcc_assert (0); + else + return false; + } + new_rec++; + old_rec++; + } + + if (abort_if_fail) + gcc_assert ((*new_rec == NULL) && (*old_rec == NULL)); + else + return ((*new_rec == NULL) && (*old_rec == NULL)); + return false; +} + + +/* Return true if the existing insn refs information is complete and + correct. Otherwise (i.e. if there's any missing or extra refs), + return the correct df_ref chain in REFS_RETURN. + + If ABORT_IF_FAIL, leave the refs that are verified (already in the + ref chain) as DF_REF_MARKED(). If it's false, then it's a per-insn + verification mode instead of the whole function, so unmark + everything. + + If ABORT_IF_FAIL is set, this function never returns false. */ + +static bool +df_insn_refs_verify (struct df_collection_rec *collection_rec, + basic_block bb, + rtx insn, + bool abort_if_fail) +{ + bool ret1, ret2, ret3, ret4; + unsigned int uid = INSN_UID (insn); + + df_insn_refs_collect (collection_rec, bb, insn); + + if (!DF_INSN_UID_DEFS (uid)) + { + /* The insn_rec was created but it was never filled out. */ + if (abort_if_fail) + gcc_assert (0); + else + return false; + } + + /* Unfortunately we cannot opt out early if one of these is not + right because the marks will not get cleared. */ + ret1 = df_refs_verify (collection_rec->def_vec, DF_INSN_UID_DEFS (uid), + abort_if_fail); + ret2 = df_refs_verify (collection_rec->use_vec, DF_INSN_UID_USES (uid), + abort_if_fail); + ret3 = df_refs_verify (collection_rec->eq_use_vec, DF_INSN_UID_EQ_USES (uid), + abort_if_fail); + ret4 = df_mws_verify (collection_rec->mw_vec, DF_INSN_UID_MWS (uid), + abort_if_fail); + return (ret1 && ret2 && ret3 && ret4); +} + + +/* Return true if all refs in the basic block are correct and complete. + Due to df_ref_chain_verify, it will cause all refs + that are verified to have DF_REF_MARK bit set. */ + +static bool +df_bb_verify (basic_block bb) +{ + rtx insn; + struct df_scan_bb_info *bb_info = df_scan_get_bb_info (bb->index); + struct df_collection_rec collection_rec; + + memset (&collection_rec, 0, sizeof (struct df_collection_rec)); + collection_rec.def_vec = alloca (sizeof (struct df_ref*) * 1000); + collection_rec.use_vec = alloca (sizeof (struct df_ref*) * 1000); + collection_rec.eq_use_vec = alloca (sizeof (struct df_ref*) * 1000); + collection_rec.mw_vec = alloca (sizeof (struct df_mw_hardreg*) * 100); + + gcc_assert (bb_info); + + /* Scan the block an insn at a time from beginning to end. */ + FOR_BB_INSNS_REVERSE (bb, insn) + { + if (!INSN_P (insn)) + continue; + df_insn_refs_verify (&collection_rec, bb, insn, true); + df_free_collection_rec (&collection_rec); + } + + /* Do the artificial defs and uses. */ + df_bb_refs_collect (&collection_rec, bb); + df_refs_verify (collection_rec.def_vec, df_get_artificial_defs (bb->index), true); + df_refs_verify (collection_rec.use_vec, df_get_artificial_uses (bb->index), true); + df_free_collection_rec (&collection_rec); + + return true; +} + + +/* Returns true if the entry block has correct and complete df_ref set. + If not it either aborts if ABORT_IF_FAIL is true or returns false. */ + +static bool +df_entry_block_bitmap_verify (bool abort_if_fail) +{ + bitmap entry_block_defs = BITMAP_ALLOC (&df_bitmap_obstack); + bool is_eq; + + df_get_entry_block_def_set (entry_block_defs); + + is_eq = bitmap_equal_p (entry_block_defs, df->entry_block_defs); + + if (!is_eq && abort_if_fail) + { + print_current_pass (stderr); + fprintf (stderr, "entry_block_defs = "); + df_print_regset (stderr, entry_block_defs); + fprintf (stderr, "df->entry_block_defs = "); + df_print_regset (stderr, df->entry_block_defs); + gcc_assert (0); + } + + BITMAP_FREE (entry_block_defs); + + return is_eq; +} + + +/* Returns true if the exit block has correct and complete df_ref set. + If not it either aborts if ABORT_IF_FAIL is true or returns false. */ + +static bool +df_exit_block_bitmap_verify (bool abort_if_fail) +{ + bitmap exit_block_uses = BITMAP_ALLOC (&df_bitmap_obstack); + bool is_eq; + + df_get_exit_block_use_set (exit_block_uses); + + is_eq = bitmap_equal_p (exit_block_uses, df->exit_block_uses); + + if (!is_eq && abort_if_fail) + { + print_current_pass (stderr); + fprintf (stderr, "exit_block_uses = "); + df_print_regset (stderr, exit_block_uses); + fprintf (stderr, "df->exit_block_uses = "); + df_print_regset (stderr, df->exit_block_uses); + gcc_assert (0); + } + + BITMAP_FREE (exit_block_uses); + + return is_eq; +} + + +/* Return true if df_ref information for all insns in all BLOCKS are + correct and complete. If BLOCKS is null, all blocks are + checked. */ + +void +df_scan_verify (void) +{ + unsigned int i; + basic_block bb; + bitmap regular_block_artificial_uses; + bitmap eh_block_artificial_uses; + + if (!df) + return; + + /* This is a hack, but a necessary one. If you do not do this, + insn_attrtab can never be compiled in a bootstrap. This + verification is just too expensive. */ + if (n_basic_blocks > 250) + return; + + /* Verification is a 4 step process. */ + + /* (1) All of the refs are marked by going thru the reg chains. */ + for (i = 0; i < DF_REG_SIZE (df); i++) + { + gcc_assert (df_reg_chain_mark (DF_REG_DEF_CHAIN (i), i, true, false) + == DF_REG_DEF_COUNT(i)); + gcc_assert (df_reg_chain_mark (DF_REG_USE_CHAIN (i), i, false, false) + == DF_REG_USE_COUNT(i)); + gcc_assert (df_reg_chain_mark (DF_REG_EQ_USE_CHAIN (i), i, false, true) + == DF_REG_EQ_USE_COUNT(i)); + } + + /* (2) There are various bitmaps whose value may change over the + course of the compilation. This step recomputes them to make + sure that they have not slipped out of date. */ + regular_block_artificial_uses = BITMAP_ALLOC (&df_bitmap_obstack); + eh_block_artificial_uses = BITMAP_ALLOC (&df_bitmap_obstack); + + df_get_regular_block_artificial_uses (regular_block_artificial_uses); + df_get_eh_block_artificial_uses (eh_block_artificial_uses); + + bitmap_ior_into (eh_block_artificial_uses, + regular_block_artificial_uses); + + /* Check artificial_uses bitmaps didn't change. */ + gcc_assert (bitmap_equal_p (regular_block_artificial_uses, + df->regular_block_artificial_uses)); + gcc_assert (bitmap_equal_p (eh_block_artificial_uses, + df->eh_block_artificial_uses)); + + BITMAP_FREE (regular_block_artificial_uses); + BITMAP_FREE (eh_block_artificial_uses); + + /* Verify entry block and exit block. These only verify the bitmaps, + the refs are verified in df_bb_verify. */ + df_entry_block_bitmap_verify (true); + df_exit_block_bitmap_verify (true); + + /* (3) All of the insns in all of the blocks are traversed and the + marks are cleared both in the artificial refs attached to the + blocks and the real refs inside the insns. It is a failure to + clear a mark that has not been set as this means that the ref in + the block or insn was not in the reg chain. */ + + FOR_ALL_BB (bb) + df_bb_verify (bb); + + /* (4) See if all reg chains are traversed a second time. This time + a check is made that the marks are clear. A set mark would be a + from a reg that is not in any insn or basic block. */ + + for (i = 0; i < DF_REG_SIZE (df); i++) + { + df_reg_chain_verify_unmarked (DF_REG_DEF_CHAIN (i)); + df_reg_chain_verify_unmarked (DF_REG_USE_CHAIN (i)); + df_reg_chain_verify_unmarked (DF_REG_EQ_USE_CHAIN (i)); + } +} @@ -1,6 +1,6 @@ /* Form lists of pseudo register references for autoinc optimization for GNU compiler. This is part of flow optimization. - Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2006 + Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Originally contributed by Michael P. Hayes (m.hayes@elec.canterbury.ac.nz, mhayes@redhat.com) @@ -36,20 +36,23 @@ struct df; struct df_problem; struct df_link; -/* Data flow problems. All problems must have a unique here. */ +/* Data flow problems. All problems must have a unique id here. */ /* Scanning is not really a dataflow problem, but it is useful to have the basic block functions in the vector so that things get done in - a uniform manner. */ + a uniform manner. The first four problems are always defined. The + last 5 are optional and can be added or deleted at any time. */ #define DF_SCAN 0 -#define DF_RU 1 /* Reaching Uses. */ -#define DF_RD 2 /* Reaching Defs. */ -#define DF_LR 3 /* Live Registers. */ -#define DF_UR 4 /* Uninitialized Registers. */ +#define DF_LR 1 /* Live Registers backward. */ +#define DF_LIVE 2 /* Live Registers & Uninitialized Registers */ + +#define DF_RU 3 /* Reaching Uses. */ +#define DF_RD 4 /* Reaching Defs. */ #define DF_UREC 5 /* Uninitialized Registers with Early Clobber. */ #define DF_CHAIN 6 /* Def-Use and/or Use-Def Chains. */ -#define DF_RI 7 /* Register Info. */ -#define DF_LAST_PROBLEM_PLUS1 (DF_RI + 1) +#define DF_NOTE 7 /* REG_DEF and REG_UNUSED notes. */ +#define DF_LAST_PROBLEM_PLUS1 (DF_NOTE + 1) +#define DF_FIRST_OPTIONAL_PROBLEM DF_RU /* Dataflow direction. */ enum df_flow_dir @@ -73,33 +76,26 @@ enum df_ref_flags /* Read-modify-write refs generate both a use and a def and these are marked with this flag to show that they are not independent. */ - DF_REF_READ_WRITE = 1, - - /* This flag is set, if we stripped the subreg from the reference. - In this case we must make conservative guesses, at what the - outer mode was. */ - DF_REF_STRIPPED = 2, - - /* If this flag is set, this is not a real definition/use, but an - artificial one created to model always live registers, eh uses, etc. */ - DF_REF_ARTIFICIAL = 4, - + DF_REF_READ_WRITE = 1 << 0, /* If this flag is set for an artificial use or def, that ref logically happens at the top of the block. If it is not set for an artificial use or def, that ref logically happens at the bottom of the block. This is never set for regular refs. */ - DF_REF_AT_TOP = 8, + DF_REF_AT_TOP = 1 << 1, - /* This flag is set if the use is inside a REG_EQUAL note. */ - DF_REF_IN_NOTE = 16, + /* This flag is set if the use is inside a REG_EQUAL or REG_EQUIV + note. */ + DF_REF_IN_NOTE = 1 << 2, /* This flag is set if this ref, generally a def, may clobber the referenced register. This is generally only set for hard registers that cross a call site. With better information about calls, some of these could be changed in the future to DF_REF_MUST_CLOBBER. */ - DF_REF_MAY_CLOBBER = 32, + DF_REF_MAY_CLOBBER = 1 << 3, + + /* This flag is set if this ref, generally a def, is a real clobber. This is not currently set for registers live across a @@ -110,21 +106,69 @@ enum df_ref_flags clobber is to a subreg. So in order to tell if the clobber wipes out the entire register, it is necessary to also check the DF_REF_PARTIAL flag. */ - DF_REF_MUST_CLOBBER = 64, + DF_REF_MUST_CLOBBER = 1 << 4, /* This bit is true if this ref is part of a multiword hardreg. */ - DF_REF_MW_HARDREG = 128, + DF_REF_MW_HARDREG = 1 << 5, /* This flag is set if this ref is a partial use or def of the associated register. */ - DF_REF_PARTIAL = 256 + DF_REF_PARTIAL = 1 << 6, + + /* This flag is set if this ref occurs inside of a conditional + execution instruction. */ + DF_REF_CONDITIONAL = 1 << 7, + + + + /* This flag is set if this ref is inside a pre/post modify. */ + DF_REF_PRE_POST_MODIFY = 1 << 8, + + /* This flag is set if this ref is a usage of the stack pointer by + a function call. */ + DF_REF_CALL_STACK_USAGE = 1 << 9, + + /* This flag is used for verification of existing refs. */ + DF_REF_REG_MARKER = 1 << 10, + + /* This bit is true if this ref can make regs_ever_live true for + this regno. */ + DF_HARD_REG_LIVE = 1 << 11 }; +/* The possible ordering of refs within the df_ref_info. */ +enum df_ref_order + { + /* There is not table. */ + DF_REF_ORDER_NO_TABLE, + + /* There is a table of refs but it is not (or no longer) organized + by one of the following methods. */ + DF_REF_ORDER_UNORDERED, + DF_REF_ORDER_UNORDERED_WITH_NOTES, + + /* Organize the table by reg order, all of the refs with regno 0 + followed by all of the refs with regno 1 ... . Within all of + the regs for a particular regno, the refs are unordered. */ + DF_REF_ORDER_BY_REG, + + /* For uses, the refs within eq notes may be added for + DF_REF_ORDER_BY_REG. */ + DF_REF_ORDER_BY_REG_WITH_NOTES, + + /* Organize the refs in insn order. The insns are ordered within a + block, and the blocks are ordered by FOR_ALL_BB. */ + DF_REF_ORDER_BY_INSN, + + /* For uses, the refs within eq notes may be added for + DF_REF_ORDER_BY_INSN. */ + DF_REF_ORDER_BY_INSN_WITH_NOTES + }; /* Function prototypes added to df_problem instance. */ /* Allocate the problem specific data. */ -typedef void (*df_alloc_function) (struct dataflow *, bitmap, bitmap); +typedef void (*df_alloc_function) (bitmap); /* This function is called if the problem has global data that needs to be cleared when ever the set of blocks changes. The bitmap @@ -132,43 +176,52 @@ typedef void (*df_alloc_function) (struct dataflow *, bitmap, bitmap); This call is only made if some of the blocks are going to change. If everything is to be deleted, the wholesale deletion mechanisms apply. */ -typedef void (*df_reset_function) (struct dataflow *, bitmap); +typedef void (*df_reset_function) (bitmap); /* Free the basic block info. Called from the block reordering code to get rid of the blocks that have been squished down. */ -typedef void (*df_free_bb_function) (struct dataflow *, basic_block, void *); +typedef void (*df_free_bb_function) (basic_block, void *); /* Local compute function. */ -typedef void (*df_local_compute_function) (struct dataflow *, bitmap, bitmap); +typedef void (*df_local_compute_function) (bitmap); /* Init the solution specific data. */ -typedef void (*df_init_function) (struct dataflow *, bitmap); +typedef void (*df_init_function) (bitmap); /* Iterative dataflow function. */ -typedef void (*df_dataflow_function) (struct dataflow *, bitmap, bitmap, - int *, int, bool); +typedef void (*df_dataflow_function) (struct dataflow *, bitmap, int *, int); /* Confluence operator for blocks with 0 out (or in) edges. */ -typedef void (*df_confluence_function_0) (struct dataflow *, basic_block); +typedef void (*df_confluence_function_0) (basic_block); /* Confluence operator for blocks with 1 or more out (or in) edges. */ -typedef void (*df_confluence_function_n) (struct dataflow *, edge); +typedef void (*df_confluence_function_n) (edge); /* Transfer function for blocks. */ -typedef bool (*df_transfer_function) (struct dataflow *, int); +typedef bool (*df_transfer_function) (int); /* Function to massage the information after the problem solving. */ -typedef void (*df_finalizer_function) (struct dataflow*, bitmap); +typedef void (*df_finalizer_function) (bitmap); /* Function to free all of the problem specific datastructures. */ -typedef void (*df_free_function) (struct dataflow *); +typedef void (*df_free_function) (void); + +/* Function to remove this problem from the stack of dataflow problems + without effecting the other problems in the stack except for those + that depend on this problem. */ +typedef void (*df_remove_problem_function) (void); + +/* Function to dump basic block independent results to FILE. */ +typedef void (*df_dump_problem_function) (FILE *); -/* Function to dump results to FILE. */ -typedef void (*df_dump_problem_function) (struct dataflow *, FILE *); +/* Function to dump top or bottom of basic block results to FILE. */ +typedef void (*df_dump_bb_problem_function) (basic_block, FILE *); -/* Function to add problem a dataflow problem that must be solved - before this problem can be solved. */ -typedef struct dataflow * (*df_dependent_problem_function) (struct df *, int); +/* Function to dump top or bottom of basic block results to FILE. */ +typedef void (*df_verify_solution_start) (void); + +/* Function to dump top or bottom of basic block results to FILE. */ +typedef void (*df_verify_solution_end) (void); /* The static description of a dataflow problem to solve. See above typedefs for doc for the function fields. */ @@ -189,23 +242,23 @@ struct df_problem { df_transfer_function trans_fun; df_finalizer_function finalize_fun; df_free_function free_fun; - df_dump_problem_function dump_fun; - df_dependent_problem_function dependent_problem_fun; - - /* Flags can be changed after analysis starts. */ - int changeable_flags; + df_remove_problem_function remove_problem_fun; + df_dump_problem_function dump_start_fun; + df_dump_bb_problem_function dump_top_fun; + df_dump_bb_problem_function dump_bottom_fun; + df_verify_solution_start verify_start_fun; + df_verify_solution_end verify_end_fun; + struct df_problem *dependent_problem; + /* The timevar id associated with this pass. */ + unsigned int tv_id; }; /* The specific instance of the problem to solve. */ struct dataflow { - struct df *df; /* Instance of df we are working in. */ struct df_problem *problem; /* The problem to be solved. */ - /* Communication between iterative_dataflow and hybrid_search. */ - sbitmap visited, pending, considered; - /* Array indexed by bb->index, that contains basic block problem and solution specific information. */ void **block_info; @@ -214,25 +267,32 @@ struct dataflow /* The pool to allocate the block_info from. */ alloc_pool block_pool; - /* Problem specific control information. */ - - /* Scanning flags. */ -#define DF_HARD_REGS 1 /* Mark hard registers. */ -#define DF_EQUIV_NOTES 2 /* Mark uses present in EQUIV/EQUAL notes. */ -#define DF_SUBREGS 4 /* Return subregs rather than the inner reg. */ - /* Flags that control the building of chains. */ -#define DF_DU_CHAIN 1 /* Build DU chains. */ -#define DF_UD_CHAIN 2 /* Build UD chains. */ - /* Flag to control the building of register info. */ -#define DF_RI_LIFE 1 /* Build register info. */ - - int flags; + /* The lr and live problems have their transfer functions recomputed + only if necessary. This is possible for them because, the + problems are kept active for the entire backend and their + transfer functions are indexed by the REGNO. These are not + defined for any other problem. */ + bitmap out_of_date_transfer_functions; /* Other problem specific data that is not on a per basic block basis. The structure is generally defined privately for the problem. The exception being the scanning problem where it is fully public. */ - void *problem_data; + void *problem_data; + + /* Local flags for some of the problems. */ + unsigned int local_flags; + + /* True if this problem of this instance has been initialized. This + is used by the dumpers to keep garbage out of the dumps if, for + debugging a dump is produced before the first call to + df_analyze after a new problem is added. */ + bool computed; + + /* True if the something has changed which invalidates the dataflow + solutions. Note that this bit is always true for all problems except + lr and live. */ + bool solutions_dirty; }; @@ -243,36 +303,31 @@ struct dataflow struct df_mw_hardreg { rtx mw_reg; /* The multiword hardreg. */ + rtx *loc; /* The location of the reg. */ enum df_ref_type type; /* Used to see if the ref is read or write. */ enum df_ref_flags flags; /* Various flags. */ - struct df_link *regs; /* The individual regs that make up - this hardreg. */ - struct df_mw_hardreg *next; /* The next mw_hardreg in this insn. */ + unsigned int start_regno; /* First word of the multi word subreg. */ + unsigned int end_regno; /* Last word of the multi word subreg. */ + unsigned int mw_order; /* Same as df_ref.ref_order. */ }; /* One of these structures is allocated for every insn. */ struct df_insn_info { - struct df_ref *defs; /* Head of insn-def chain. */ - struct df_ref *uses; /* Head of insn-use chain. */ - struct df_mw_hardreg *mw_hardregs; - /* ???? The following luid field should be considered private so that - we can change it on the fly to accommodate new insns? */ - int luid; /* Logical UID. */ - bool contains_asm; /* Contains an asm instruction. */ + rtx insn; /* The insn this info comes from. */ + struct df_ref **defs; /* Head of insn-def chain. */ + struct df_ref **uses; /* Head of insn-use chain. */ + /* Head of insn-use chain for uses in REG_EQUAL/EQUIV notes. */ + struct df_ref **eq_uses; + struct df_mw_hardreg **mw_hardregs; + /* The logical uid of the insn in the basic block. This is valid + after any call to df_analyze but may rot after insns are added, + deleted or moved. */ + int luid; }; -/* Two of these structures are allocated for every pseudo reg, one for - the uses and one for the defs. */ -struct df_reg_info -{ - struct df_ref *reg_chain; /* Head of reg-use or def chain. */ - unsigned int begin; /* First def_index for this pseudo. */ - unsigned int n_refs; /* Number of refs or defs for this pseudo. */ -}; - /* Define a register reference structure. One of these is allocated for every register reference (use or def). Note some register references (e.g., post_inc, subreg) generate both a def and a use. */ @@ -287,20 +342,21 @@ struct df_ref rtx insn; rtx *loc; /* The location of the reg. */ struct df_link *chain; /* Head of def-use, use-def. */ - unsigned int id; /* Location in table. */ + /* Location in the ref table. This is only valid after a call to + df_maybe_reorganize_[use,def]_refs which is an expensive operation. */ + int id; + /* The index at which the operand was scanned in the insn. This is + used to totally order the refs in an insn. */ + unsigned int ref_order; + enum df_ref_type type; /* Type of ref. */ enum df_ref_flags flags; /* Various flags. */ - /* For each regno, there are two chains of refs, one for the uses - and one for the defs. These chains go thru the refs themselves - rather than using an external structure. */ + /* For each regno, there are three chains of refs, one for the uses, + the eq_uses and the defs. These chains go thru the refs + themselves rather than using an external structure. */ struct df_ref *next_reg; /* Next ref with same regno and type. */ struct df_ref *prev_reg; /* Prev ref with same regno and type. */ - - /* Each insn has two lists, one for the uses and one for the - defs. This is the next field in either of these chains. */ - struct df_ref *next_ref; - void *data; /* The data assigned to it by user. */ }; /* These links are used for two purposes: @@ -312,26 +368,67 @@ struct df_link struct df_link *next; }; -/* Two of these structures are allocated, one for the uses and one for - the defs. */ + +enum df_chain_flags +{ + /* Flags that control the building of chains. */ + DF_DU_CHAIN = 1, /* Build DU chains. */ + DF_UD_CHAIN = 2 /* Build UD chains. */ +}; + +enum df_changeable_flags +{ + /* Scanning flags. */ + /* Flag to control the running of dce as a side effect of building LR. */ + DF_LR_RUN_DCE = 1, /* Run DCE. */ + DF_NO_HARD_REGS = 2, /* Skip hard registers in RD and CHAIN Building. */ + DF_EQ_NOTES = 4, /* Build chains with uses present in EQUIV/EQUAL notes. */ + DF_NO_REGS_EVER_LIVE = 8, /* Do not compute the regs_ever_live. */ + + /* Cause df_insn_rescan df_notes_rescan and df_insn_delete, to + return immediately. This is used by passes that know how to update + the scanning them selves. */ + DF_NO_INSN_RESCAN = 16, + + /* Cause df_insn_rescan df_notes_rescan and df_insn_delete, to + return after marking the insn for later processing. This allows all + rescans to be batched. */ + DF_DEFER_INSN_RESCAN = 32 +}; + +/* Two of these structures are inline in df, one for the uses and one + for the defs. This structure is only contains the refs within the + boundary of the df_set_blocks if that has been defined. */ struct df_ref_info { - struct df_reg_info **regs; /* Array indexed by pseudo regno. */ - unsigned int regs_size; /* Size of currently allocated regs table. */ - unsigned int regs_inited; /* Number of regs with reg_infos allocated. */ struct df_ref **refs; /* Ref table, indexed by id. */ + unsigned int *begin; /* First ref_index for this pseudo. */ + unsigned int *count; /* Count of refs for this pseudo. */ unsigned int refs_size; /* Size of currently allocated refs table. */ - unsigned int bitmap_size; /* Number of refs seen. */ - - /* >0 if refs table is organized so that every reference for a - pseudo is contiguous. */ - unsigned int refs_organized_size; - /* True if the next refs should be added immediately or false to - defer to later to reorganize the table. */ - bool add_refs_inline; + + /* Table_size is the number of elements in the refs table. This + will also be the width of the bitvectors in the rd and ru + problems. Total_size is the number of refs. These will be the + same if the focus has not been reduced by df_set_blocks. If the + focus has been reduced, table_size will be smaller since it only + contains the refs in the set blocks. */ + unsigned int table_size; + unsigned int total_size; + + enum df_ref_order ref_order; }; - +/* Three of these structures are allocated for every pseudo reg. One + for the uses, one for the eq_uses and one for the defs. */ +struct df_reg_info +{ + /* Head of chain for refs of that type and regno. */ + struct df_ref *reg_chain; + /* Number of refs in the chain. */ + unsigned int n_refs; +}; + + /*---------------------------------------------------------------------------- Problem data for the scanning dataflow problem. Unlike the other dataflow problems, the problem data for scanning is fully exposed and @@ -349,56 +446,109 @@ struct df the problem local data without having to search the first array. */ - struct dataflow *problems_in_order [DF_LAST_PROBLEM_PLUS1]; - struct dataflow *problems_by_index [DF_LAST_PROBLEM_PLUS1]; + struct dataflow *problems_in_order[DF_LAST_PROBLEM_PLUS1]; + struct dataflow *problems_by_index[DF_LAST_PROBLEM_PLUS1]; int num_problems_defined; - /* Set after calls to df_scan_blocks, this contains all of the - blocks that higher level problems must rescan before solving the - dataflow equations. If this is NULL, the blocks_to_analyze is - used. */ - bitmap blocks_to_scan; - - /* If not NULL, the subset of blocks of the program to be considered - for analysis. */ + /* If not NULL, this subset of blocks of the program to be + considered for analysis. At certain times, this will contain all + the blocks in the function so it cannot be used as an indicator + of if we are analyzing a subset. See analyze_subset. */ bitmap blocks_to_analyze; + /* If this is true, then only a subset of the blocks of the program + is considered to compute the solutions of dataflow problems. */ + bool analyze_subset; + + /* True if someone added or deleted something from regs_ever_live so + that the entry and exit blocks need be reprocessed. */ + bool redo_entry_and_exit; + /* The following information is really the problem data for the scanning instance but it is used too often by the other problems to keep getting it from there. */ struct df_ref_info def_info; /* Def info. */ struct df_ref_info use_info; /* Use info. */ + + /* The following three arrays are allocated in parallel. They contain + the sets of refs of each type for each reg. */ + struct df_reg_info **def_regs; /* Def reg info. */ + struct df_reg_info **use_regs; /* Eq_use reg info. */ + struct df_reg_info **eq_use_regs; /* Eq_use info. */ + unsigned int regs_size; /* Size of currently allocated regs table. */ + unsigned int regs_inited; /* Number of regs with reg_infos allocated. */ + + struct df_insn_info **insns; /* Insn table, indexed by insn UID. */ unsigned int insns_size; /* Size of insn table. */ bitmap hardware_regs_used; /* The set of hardware registers used. */ - bitmap entry_block_defs; /* The set of hardware registers live on entry to the function. */ + /* The set of hard regs that are in the artificial uses at the end + of a regular basic block. */ + bitmap regular_block_artificial_uses; + /* The set of hard regs that are in the artificial uses at the end + of a basic block that has an EH pred. */ + bitmap eh_block_artificial_uses; + /* The set of hardware registers live on entry to the function. */ + bitmap entry_block_defs; bitmap exit_block_uses; /* The set of hardware registers used in exit block. */ + + /* Insns to delete, rescan or reprocess the notes at next + df_rescan_all or df_process_deferred_rescans. */ + bitmap insns_to_delete; + bitmap insns_to_rescan; + bitmap insns_to_notes_rescan; + int *postorder; /* The current set of basic blocks + in reverse postorder. */ + int *postorder_inverted; /* The current set of basic blocks + in reverse postorder of inverted CFG. */ + int n_blocks; /* The number of blocks in reverse postorder. */ + int n_blocks_inverted; /* The number of blocks + in reverse postorder of inverted CFG. */ + + /* An array [FIRST_PSEUDO_REGISTER], indexed by regno, of the number + of refs that qualify as being real hard regs uses. Artificial + uses and defs as well as refs in eq notes are ignored. If the + ref is a def, it cannot be a MAY_CLOBBER def. If the ref is a + use, it cannot be the emim_reg_set or be the frame or arg pointer + register. + + IT IS NOT ACCEPTABLE TO MANUALLY CHANGE THIS ARRAY. This array + always reflects the actual number of refs in the insn stream that + satisfy the above criteria. */ + unsigned int *hard_regs_live_count; + + /* This counter provides a way to totally order refs without using + addresses. It is incremented whenever a ref is created. */ + unsigned int ref_order; + + /* Problem specific control infomation. */ + enum df_changeable_flags changeable_flags; }; -#define DF_SCAN_BB_INFO(DF, BB) (df_scan_get_bb_info((DF)->problems_by_index[DF_SCAN],(BB)->index)) -#define DF_RU_BB_INFO(DF, BB) (df_ru_get_bb_info((DF)->problems_by_index[DF_RU],(BB)->index)) -#define DF_RD_BB_INFO(DF, BB) (df_rd_get_bb_info((DF)->problems_by_index[DF_RD],(BB)->index)) -#define DF_LR_BB_INFO(DF, BB) (df_lr_get_bb_info((DF)->problems_by_index[DF_LR],(BB)->index)) -#define DF_UR_BB_INFO(DF, BB) (df_ur_get_bb_info((DF)->problems_by_index[DF_UR],(BB)->index)) -#define DF_UREC_BB_INFO(DF, BB) (df_urec_get_bb_info((DF)->problems_by_index[DF_UREC],(BB)->index)) +#define DF_SCAN_BB_INFO(BB) (df_scan_get_bb_info((BB)->index)) +#define DF_RU_BB_INFO(BB) (df_ru_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_UREC_BB_INFO(BB) (df_urec_get_bb_info((BB)->index)) +#define DF_LIVE_BB_INFO(BB) (df_live_get_bb_info((BB)->index)) /* Most transformations that wish to use live register analysis will - use these macros. The DF_UPWARD_LIVE* macros are only half of the - solution. */ -#define DF_LIVE_IN(DF, BB) (DF_UR_BB_INFO(DF, BB)->in) -#define DF_LIVE_OUT(DF, BB) (DF_UR_BB_INFO(DF, BB)->out) + use these macros. This info is the and of the lr and live sets. */ +#define DF_LIVE_IN(BB) (DF_LIVE_BB_INFO(BB)->in) +#define DF_LIVE_OUT(BB) (DF_LIVE_BB_INFO(BB)->out) /* Live in for register allocation also takes into account several other factors. */ -#define DF_RA_LIVE_IN(DF, BB) (DF_UREC_BB_INFO(DF, BB)->in) -#define DF_RA_LIVE_OUT(DF, BB) (DF_UREC_BB_INFO(DF, BB)->out) +#define DF_RA_LIVE_IN(BB) (DF_UREC_BB_INFO(BB)->in) +#define DF_RA_LIVE_TOP(BB) (DF_UREC_BB_INFO(BB)->top) +#define DF_RA_LIVE_OUT(BB) (DF_UREC_BB_INFO(BB)->out) /* These macros are currently used by only reg-stack since it is not tolerant of uninitialized variables. This intolerance should be fixed because it causes other problems. */ -#define DF_UPWARD_LIVE_IN(DF, BB) (DF_LR_BB_INFO(DF, BB)->in) -#define DF_UPWARD_LIVE_OUT(DF, BB) (DF_LR_BB_INFO(DF, BB)->out) - +#define DF_LR_IN(BB) (DF_LR_BB_INFO(BB)->in) +#define DF_LR_TOP(BB) (DF_LR_BB_INFO(BB)->top) +#define DF_LR_OUT(BB) (DF_LR_BB_INFO(BB)->out) /* Macros to access the elements within the ref structure. */ @@ -406,8 +556,8 @@ struct df #define DF_REF_REAL_REG(REF) (GET_CODE ((REF)->reg) == SUBREG \ ? SUBREG_REG ((REF)->reg) : ((REF)->reg)) #define DF_REF_REGNO(REF) ((REF)->regno) -#define DF_REF_REAL_LOC(REF) (GET_CODE ((REF)->reg) == SUBREG \ - ? &SUBREG_REG ((REF)->reg) : ((REF)->loc)) +#define DF_REF_REAL_LOC(REF) (GET_CODE (*((REF)->loc)) == SUBREG \ + ? &SUBREG_REG (*((REF)->loc)) : ((REF)->loc)) #define DF_REF_REG(REF) ((REF)->reg) #define DF_REF_LOC(REF) ((REF)->loc) #define DF_REF_BB(REF) ((REF)->bb) @@ -418,10 +568,20 @@ struct df #define DF_REF_CHAIN(REF) ((REF)->chain) #define DF_REF_ID(REF) ((REF)->id) #define DF_REF_FLAGS(REF) ((REF)->flags) +#define DF_REF_FLAGS_IS_SET(REF, v) ((DF_REF_FLAGS (REF) & (v)) != 0) +#define DF_REF_FLAGS_SET(REF, v) (DF_REF_FLAGS (REF) |= (v)) +#define DF_REF_FLAGS_CLEAR(REF, v) (DF_REF_FLAGS (REF) &= ~(v)) +#define DF_REF_ORDER(REF) ((REF)->ref_order) +/* If DF_REF_IS_ARTIFICIAL () is true, this is not a real definition/use, + but an artificial one created to model + always live registers, eh uses, etc. + ARTIFICIAL refs has NULL insn. */ +#define DF_REF_IS_ARTIFICIAL(REF) ((REF)->insn == NULL) +#define DF_REF_REG_MARK(REF) (DF_REF_FLAGS_SET ((REF),DF_REF_REG_MARKER)) +#define DF_REF_REG_UNMARK(REF) (DF_REF_FLAGS_CLEAR ((REF),DF_REF_REG_MARKER)) +#define DF_REF_IS_REG_MARKED(REF) (DF_REF_FLAGS_IS_SET ((REF),DF_REF_REG_MARKER)) #define DF_REF_NEXT_REG(REF) ((REF)->next_reg) #define DF_REF_PREV_REG(REF) ((REF)->prev_reg) -#define DF_REF_NEXT_REF(REF) ((REF)->next_ref) -#define DF_REF_DATA(REF) ((REF)->data) /* Macros to determine the reference type. */ @@ -432,46 +592,74 @@ struct df #define DF_REF_REG_MEM_P(REF) (DF_REF_REG_MEM_STORE_P (REF) \ || DF_REF_REG_MEM_LOAD_P (REF)) -/* Macros to get the refs out of def_info or use_info refs table. */ -#define DF_DEFS_SIZE(DF) ((DF)->def_info.refs_organized_size) -#define DF_DEFS_GET(DF,ID) ((DF)->def_info.refs[(ID)]) -#define DF_DEFS_SET(DF,ID,VAL) ((DF)->def_info.refs[(ID)]=(VAL)) -#define DF_USES_SIZE(DF) ((DF)->use_info.refs_organized_size) -#define DF_USES_GET(DF,ID) ((DF)->use_info.refs[(ID)]) -#define DF_USES_SET(DF,ID,VAL) ((DF)->use_info.refs[(ID)]=(VAL)) +/* Macros to get the refs out of def_info or use_info refs table. If + the focus of the dataflow has been set to some subset of blocks + with df_set_blocks, these macros will only find the uses and defs + in that subset of blocks. + + These macros should be used with care. The def macros are only + usable after a call to df_maybe_reorganize_def_refs and the use + macros are only usable after a call to + df_maybe_reorganize_use_refs. HOWEVER, BUILDING AND USING THESE + ARRAYS ARE A CACHE LOCALITY KILLER. */ + +#define DF_DEFS_TABLE_SIZE() (df->def_info.table_size) +#define DF_DEFS_GET(ID) (df->def_info.refs[(ID)]) +#define DF_DEFS_SET(ID,VAL) (df->def_info.refs[(ID)]=(VAL)) +#define DF_DEFS_COUNT(ID) (df->def_info.count[(ID)]) +#define DF_DEFS_BEGIN(ID) (df->def_info.begin[(ID)]) +#define DF_USES_TABLE_SIZE() (df->use_info.table_size) +#define DF_USES_GET(ID) (df->use_info.refs[(ID)]) +#define DF_USES_SET(ID,VAL) (df->use_info.refs[(ID)]=(VAL)) +#define DF_USES_COUNT(ID) (df->use_info.count[(ID)]) +#define DF_USES_BEGIN(ID) (df->use_info.begin[(ID)]) /* Macros to access the register information from scan dataflow record. */ -#define DF_REG_SIZE(DF) ((DF)->def_info.regs_inited) -#define DF_REG_DEF_GET(DF, REG) ((DF)->def_info.regs[(REG)]) -#define DF_REG_DEF_SET(DF, REG, VAL) ((DF)->def_info.regs[(REG)]=(VAL)) -#define DF_REG_DEF_COUNT(DF, REG) ((DF)->def_info.regs[(REG)]->n_refs) -#define DF_REG_USE_GET(DF, REG) ((DF)->use_info.regs[(REG)]) -#define DF_REG_USE_SET(DF, REG, VAL) ((DF)->use_info.regs[(REG)]=(VAL)) -#define DF_REG_USE_COUNT(DF, REG) ((DF)->use_info.regs[(REG)]->n_refs) +#define DF_REG_SIZE(DF) (df->regs_inited) +#define DF_REG_DEF_GET(REG) (df->def_regs[(REG)]) +#define DF_REG_DEF_CHAIN(REG) (df->def_regs[(REG)]->reg_chain) +#define DF_REG_DEF_COUNT(REG) (df->def_regs[(REG)]->n_refs) +#define DF_REG_USE_GET(REG) (df->use_regs[(REG)]) +#define DF_REG_USE_CHAIN(REG) (df->use_regs[(REG)]->reg_chain) +#define DF_REG_USE_COUNT(REG) (df->use_regs[(REG)]->n_refs) +#define DF_REG_EQ_USE_GET(REG) (df->eq_use_regs[(REG)]) +#define DF_REG_EQ_USE_CHAIN(REG) (df->eq_use_regs[(REG)]->reg_chain) +#define DF_REG_EQ_USE_COUNT(REG) (df->eq_use_regs[(REG)]->n_refs) /* Macros to access the elements within the reg_info structure table. */ -#define DF_REGNO_FIRST_DEF(DF, REGNUM) \ -(DF_REG_DEF_GET(DF, REGNUM) ? DF_REG_DEF_GET(DF, REGNUM) : 0) -#define DF_REGNO_LAST_USE(DF, REGNUM) \ -(DF_REG_USE_GET(DF, REGNUM) ? DF_REG_USE_GET(DF, REGNUM) : 0) +#define DF_REGNO_FIRST_DEF(REGNUM) \ +(DF_REG_DEF_GET(REGNUM) ? DF_REG_DEF_GET(REGNUM) : 0) +#define DF_REGNO_LAST_USE(REGNUM) \ +(DF_REG_USE_GET(REGNUM) ? DF_REG_USE_GET(REGNUM) : 0) /* Macros to access the elements within the insn_info structure table. */ -#define DF_INSN_SIZE(DF) ((DF)->insns_size) -#define DF_INSN_GET(DF,INSN) ((DF)->insns[(INSN_UID(INSN))]) -#define DF_INSN_SET(DF,INSN,VAL) ((DF)->insns[(INSN_UID (INSN))]=(VAL)) -#define DF_INSN_CONTAINS_ASM(DF, INSN) (DF_INSN_GET(DF,INSN)->contains_asm) -#define DF_INSN_LUID(DF, INSN) (DF_INSN_GET(DF,INSN)->luid) -#define DF_INSN_DEFS(DF, INSN) (DF_INSN_GET(DF,INSN)->defs) -#define DF_INSN_USES(DF, INSN) (DF_INSN_GET(DF,INSN)->uses) - -#define DF_INSN_UID_GET(DF,UID) ((DF)->insns[(UID)]) -#define DF_INSN_UID_LUID(DF, INSN) (DF_INSN_UID_GET(DF,INSN)->luid) -#define DF_INSN_UID_DEFS(DF, INSN) (DF_INSN_UID_GET(DF,INSN)->defs) -#define DF_INSN_UID_USES(DF, INSN) (DF_INSN_UID_GET(DF,INSN)->uses) -#define DF_INSN_UID_MWS(DF, INSN) (DF_INSN_UID_GET(DF,INSN)->mw_hardregs) +#define DF_INSN_SIZE() ((df)->insns_size) +#define DF_INSN_GET(INSN) (df->insns[(INSN_UID(INSN))]) +#define DF_INSN_SET(INSN,VAL) (df->insns[(INSN_UID (INSN))]=(VAL)) +#define DF_INSN_LUID(INSN) (DF_INSN_GET(INSN)->luid) +#define DF_INSN_DEFS(INSN) (DF_INSN_GET(INSN)->defs) +#define DF_INSN_USES(INSN) (DF_INSN_GET(INSN)->uses) +#define DF_INSN_EQ_USES(INSN) (DF_INSN_GET(INSN)->eq_uses) + +#define DF_INSN_UID_GET(UID) (df->insns[(UID)]) +#define DF_INSN_UID_SET(UID,VAL) (df->insns[(UID)]=(VAL)) +#define DF_INSN_UID_SAFE_GET(UID) (((unsigned)(UID) < DF_INSN_SIZE()) \ + ? DF_INSN_UID_GET (UID) \ + : NULL) +#define DF_INSN_UID_LUID(INSN) (DF_INSN_UID_GET(INSN)->luid) +#define DF_INSN_UID_DEFS(INSN) (DF_INSN_UID_GET(INSN)->defs) +#define DF_INSN_UID_USES(INSN) (DF_INSN_UID_GET(INSN)->uses) +#define DF_INSN_UID_EQ_USES(INSN) (DF_INSN_UID_GET(INSN)->eq_uses) +#define DF_INSN_UID_MWS(INSN) (DF_INSN_UID_GET(INSN)->mw_hardregs) + +/* An obstack for bitmap not related to specific dataflow problems. + This obstack should e.g. be used for bitmaps with a short life time + such as temporary bitmaps. This obstack is declared in df-core.c. */ + +extern bitmap_obstack df_bitmap_obstack; /* This is a bitmap copy of regs_invalidated_by_call so that we can easily add it into bitmaps, etc. */ @@ -484,10 +672,10 @@ struct df_scan_bb_info { /* Defs at the start of a basic block that is the target of an exception edge. */ - struct df_ref *artificial_defs; + struct df_ref **artificial_defs; /* Uses of hard registers that are live at every block. */ - struct df_ref *artificial_uses; + struct df_ref **artificial_uses; }; @@ -531,21 +719,45 @@ struct df_rd_bb_info }; -/* Live registers. All bitmaps are referenced by the register number. */ +/* Live registers. All bitmaps are referenced by the register number. + + df_lr_bb_info:IN is the "in" set of the traditional dataflow sense + which is the confluence of out sets of all predecessor blocks. + The difference between IN and TOP is + due to the artificial defs and uses at the top (DF_REF_TOP) + (e.g. exception handling dispatch block, which can have + a few registers defined by the runtime) - which is NOT included + in the "in" set before this function but is included after. + For the initial live set of forward scanning, TOP should be used + instead of IN - otherwise, artificial defs won't be in IN set + causing the bad transformation. TOP set can not simply be + the union of IN set and artificial defs at the top, + because artificial defs might not be used at all, + in which case those defs are not live at any point + (except as a dangling def) - hence TOP has to be calculated + during the LR problem computation and stored in df_lr_bb_info. */ + struct df_lr_bb_info { /* Local sets to describe the basic blocks. */ - bitmap def; /* The set of registers set in this block. */ + bitmap def; /* The set of registers set in this block + - except artificial defs at the top. */ bitmap use; /* The set of registers used in this block. */ + bitmap adef; /* The artificial defs at top. */ + bitmap ause; /* The artificial uses at top. */ /* The results of the dataflow problem. */ - bitmap in; /* At the top of the block. */ + bitmap in; /* Just before the block itself. */ + bitmap top; /* Just before the first insn in the block. */ bitmap out; /* At the bottom of the block. */ }; -/* Uninitialized registers. All bitmaps are referenced by the register number. */ -struct df_ur_bb_info +/* Uninitialized registers. All bitmaps are referenced by the + register number. Anded results of the forwards and backward live + info. Note that the forwards live information is not available + separately. */ +struct df_live_bb_info { /* Local sets to describe the basic blocks. */ bitmap kill; /* The set of registers unset in this block. Calls, @@ -557,6 +769,7 @@ struct df_ur_bb_info bitmap out; /* At the bottom of the block. */ }; + /* Uninitialized registers. All bitmaps are referenced by the register number. */ struct df_urec_bb_info { @@ -568,41 +781,81 @@ struct df_urec_bb_info bitmap gen; /* The results of the dataflow problem. */ - bitmap in; /* At the top of the block. */ + bitmap in; /* Just before the block. */ + bitmap top; /* Just before the first insn in the block. */ bitmap out; /* At the bottom of the block. */ }; -#define df_finish(df) {df_finish1(df); df=NULL;} +/* This is used for debugging and for the dumpers to find the latest + instance so that the df info can be added to the dumps. This + should not be used by regular code. */ +extern struct df *df; +#define df_scan (df->problems_by_index[DF_SCAN]) +#define df_ru (df->problems_by_index[DF_RU]) +#define df_rd (df->problems_by_index[DF_RD]) +#define df_lr (df->problems_by_index[DF_LR]) +#define df_live (df->problems_by_index[DF_LIVE]) +#define df_urec (df->problems_by_index[DF_UREC]) +#define df_chain (df->problems_by_index[DF_CHAIN]) +#define df_note (df->problems_by_index[DF_NOTE]) + +/* This symbol turns on checking that each modfication of the cfg has + been identified to the appropriate df routines. It is not part of + verification per se because the check that the final solution has + not changed covers this. However, if the solution is not being + properly recomputed because the cfg is being modified, adding in + calls to df_check_cfg_clean can be used to find the source of that + kind of problem. */ +#if 0 +#define DF_DEBUG_CFG +#endif + /* Functions defined in df-core.c. */ -extern struct df *df_init (int); -extern struct dataflow *df_add_problem (struct df *, struct df_problem *, int); -extern int df_set_flags (struct dataflow *, int); -extern int df_clear_flags (struct dataflow *, int); -extern void df_set_blocks (struct df*, bitmap); -extern void df_delete_basic_block (struct df *, int); -extern void df_finish1 (struct df *); -extern void df_analyze_problem (struct dataflow *, bitmap, bitmap, bitmap, int *, int, bool); -extern void df_analyze (struct df *); -extern void df_compact_blocks (struct df *); -extern void df_bb_replace (struct df *, int, basic_block); -extern struct df_ref *df_bb_regno_last_use_find (struct df *, basic_block, unsigned int); -extern struct df_ref *df_bb_regno_first_def_find (struct df *, basic_block, unsigned int); -extern struct df_ref *df_bb_regno_last_def_find (struct df *, basic_block, unsigned int); -extern bool df_insn_regno_def_p (struct df *, rtx, unsigned int); -extern struct df_ref *df_find_def (struct df *, rtx, rtx); -extern bool df_reg_defined (struct df *, rtx, rtx); -extern struct df_ref *df_find_use (struct df *, rtx, rtx); -extern bool df_reg_used (struct df *, rtx, rtx); -extern void df_iterative_dataflow (struct dataflow *, bitmap, bitmap, int *, int, bool); -extern void df_dump (struct df *, FILE *); -extern void df_refs_chain_dump (struct df_ref *, bool, FILE *); -extern void df_regs_chain_dump (struct df *, struct df_ref *, FILE *); -extern void df_insn_debug (struct df *, rtx, bool, FILE *); -extern void df_insn_debug_regno (struct df *, rtx, FILE *); -extern void df_regno_debug (struct df *, unsigned int, FILE *); +extern void df_add_problem (struct df_problem *); +extern enum df_changeable_flags df_set_flags (enum df_changeable_flags); +extern enum df_changeable_flags df_clear_flags (enum df_changeable_flags); +extern void df_set_blocks (bitmap); +extern void df_remove_problem (struct dataflow *); +extern void df_finish_pass (void); +extern void df_analyze_problem (struct dataflow *, bitmap, int *, int); +extern void df_analyze (void); +extern int df_get_n_blocks (enum df_flow_dir); +extern int *df_get_postorder (enum df_flow_dir); +extern void df_simple_dataflow (enum df_flow_dir, df_init_function, + df_confluence_function_0, df_confluence_function_n, + df_transfer_function, bitmap, int *, int); +extern void df_mark_solutions_dirty (void); +extern bool df_get_bb_dirty (basic_block); +extern void df_set_bb_dirty (basic_block); +extern void df_compact_blocks (void); +extern void df_bb_replace (int, basic_block); +extern void df_bb_delete (int); +extern void df_verify (void); +#ifdef DF_DEBUG_CFG +extern void df_check_cfg_clean (void); +#endif +extern struct df_ref *df_bb_regno_last_use_find (basic_block, unsigned int); +extern struct df_ref *df_bb_regno_first_def_find (basic_block, unsigned int); +extern struct df_ref *df_bb_regno_last_def_find (basic_block, unsigned int); +extern bool df_insn_regno_def_p (rtx, unsigned int); +extern struct df_ref *df_find_def (rtx, rtx); +extern bool df_reg_defined (rtx, rtx); +extern struct df_ref *df_find_use (rtx, rtx); +extern bool df_reg_used (rtx, rtx); +extern void df_worklist_dataflow (struct dataflow *,bitmap, int *, int); +extern void df_print_regset (FILE *file, bitmap r); +extern void df_dump (FILE *); +extern void df_dump_start (FILE *); +extern void df_dump_top (basic_block, FILE *); +extern void df_dump_bottom (basic_block, FILE *); +extern void df_refs_chain_dump (struct df_ref **, bool, FILE *); +extern void df_regs_chain_dump (struct df_ref *, FILE *); +extern void df_insn_debug (rtx, bool, FILE *); +extern void df_insn_debug_regno (rtx, FILE *); +extern void df_regno_debug (unsigned int, FILE *); extern void df_ref_debug (struct df_ref *, FILE *); extern void debug_df_insn (rtx); extern void debug_df_regno (unsigned int); @@ -611,51 +864,144 @@ extern void debug_df_defno (unsigned int); extern void debug_df_useno (unsigned int); extern void debug_df_ref (struct df_ref *); extern void debug_df_chain (struct df_link *); -/* An instance of df that can be shared between passes. */ -extern struct df *shared_df; - /* Functions defined in df-problems.c. */ -extern struct df_link *df_chain_create (struct dataflow *, struct df_ref *, struct df_ref *); -extern void df_chain_unlink (struct dataflow *, struct df_ref *, struct df_link *); -extern void df_chain_copy (struct dataflow *, struct df_ref *, struct df_link *); -extern bitmap df_get_live_in (struct df *, basic_block); -extern bitmap df_get_live_out (struct df *, basic_block); +extern struct df_link *df_chain_create (struct df_ref *, struct df_ref *); +extern void df_chain_unlink (struct df_ref *); +extern void df_chain_copy (struct df_ref *, struct df_link *); +extern bitmap df_get_live_in (basic_block); +extern bitmap df_get_live_out (basic_block); +extern bitmap df_get_live_top (basic_block); extern void df_grow_bb_info (struct dataflow *); extern void df_chain_dump (struct df_link *, FILE *); extern void df_print_bb_index (basic_block bb, FILE *file); -extern struct dataflow *df_ru_add_problem (struct df *, int); -extern struct df_ru_bb_info *df_ru_get_bb_info (struct dataflow *, unsigned int); -extern struct dataflow *df_rd_add_problem (struct df *, int); -extern struct df_rd_bb_info *df_rd_get_bb_info (struct dataflow *, unsigned int); -extern struct dataflow *df_lr_add_problem (struct df *, int); -extern struct df_lr_bb_info *df_lr_get_bb_info (struct dataflow *, unsigned int); -extern struct dataflow *df_ur_add_problem (struct df *, int); -extern struct df_ur_bb_info *df_ur_get_bb_info (struct dataflow *, unsigned int); -extern struct dataflow *df_urec_add_problem (struct df *, int); -extern struct df_urec_bb_info *df_urec_get_bb_info (struct dataflow *, unsigned int); -extern struct dataflow *df_chain_add_problem (struct df *, int); -extern struct dataflow *df_ri_add_problem (struct df *, int); - +extern void df_ru_add_problem (void); +extern void df_rd_add_problem (void); +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_urec_add_problem (void); +extern void df_chain_add_problem (enum df_chain_flags); +extern void df_note_add_problem (void); +extern void df_simulate_find_defs (rtx, bitmap); +extern void df_simulate_defs (rtx, bitmap); +extern void df_simulate_uses (rtx, bitmap); +extern void df_simulate_artificial_refs_at_top (basic_block, bitmap); +extern void df_simulate_one_insn_forwards (basic_block, rtx, bitmap); +extern void df_simulate_artificial_refs_at_end (basic_block, bitmap); +extern void df_simulate_one_insn_backwards (basic_block, rtx, bitmap); /* Functions defined in df-scan.c. */ -extern struct df_scan_bb_info *df_scan_get_bb_info (struct dataflow *, unsigned int); -extern struct dataflow *df_scan_add_problem (struct df *, int); -extern void df_rescan_blocks (struct df *, bitmap); -extern struct df_ref *df_ref_create (struct df *, rtx, rtx *, rtx,basic_block,enum df_ref_type, enum df_ref_flags); -extern struct df_ref *df_get_artificial_defs (struct df *, unsigned int); -extern struct df_ref *df_get_artificial_uses (struct df *, unsigned int); -extern void df_reg_chain_create (struct df_reg_info *, struct df_ref *); -extern struct df_ref *df_reg_chain_unlink (struct dataflow *, struct df_ref *); -extern void df_ref_remove (struct df *, struct df_ref *); -extern void df_insn_refs_delete (struct dataflow *, rtx); -extern void df_bb_refs_delete (struct dataflow *, int); -extern void df_refs_delete (struct dataflow *, bitmap); -extern void df_reorganize_refs (struct df_ref_info *); +extern void df_scan_alloc (bitmap); +extern void df_scan_add_problem (void); +extern void df_grow_reg_info (void); +extern void df_grow_insn_info (void); +extern void df_scan_blocks (void); +extern struct df_ref *df_ref_create (rtx, rtx *, rtx,basic_block, + enum df_ref_type, enum df_ref_flags); +extern void df_ref_remove (struct df_ref *); +extern struct df_insn_info * df_insn_create_insn_record (rtx); +extern void df_insn_delete (basic_block, unsigned int); +extern void df_bb_refs_record (int, bool); +extern bool df_insn_rescan (rtx); +extern void df_insn_rescan_all (void); +extern void df_process_deferred_rescans (void); +extern bool df_has_eh_preds (basic_block); +extern void df_recompute_luids (basic_block); +extern void df_insn_change_bb (rtx); +extern void df_maybe_reorganize_use_refs (enum df_ref_order); +extern void df_maybe_reorganize_def_refs (enum df_ref_order); +extern void df_ref_change_reg_with_loc (int, int, rtx); +extern void df_notes_rescan (rtx); extern void df_hard_reg_init (void); +extern void df_update_entry_block_defs (void); +extern void df_update_exit_block_uses (void); +extern void df_update_entry_exit_and_calls (void); +extern bool df_hard_reg_used_p (unsigned int); +extern unsigned int df_hard_reg_used_count (unsigned int); +extern bool df_regs_ever_live_p (unsigned int); +extern void df_set_regs_ever_live (unsigned int, bool); +extern void df_compute_regs_ever_live (bool); extern bool df_read_modify_subreg_p (rtx); +extern void df_scan_verify (void); + + +/* Get basic block info. */ + +static inline struct df_scan_bb_info * +df_scan_get_bb_info (unsigned int index) +{ + if (index < df_scan->block_info_size) + return (struct df_scan_bb_info *) df_scan->block_info[index]; + else + return NULL; +} + +static inline struct df_ru_bb_info * +df_ru_get_bb_info (unsigned int index) +{ + if (index < df_ru->block_info_size) + return (struct df_ru_bb_info *) df_ru->block_info[index]; + else + return NULL; +} + +static inline struct df_rd_bb_info * +df_rd_get_bb_info (unsigned int index) +{ + if (index < df_rd->block_info_size) + return (struct df_rd_bb_info *) df_rd->block_info[index]; + else + return NULL; +} + +static inline struct df_lr_bb_info * +df_lr_get_bb_info (unsigned int index) +{ + if (index < df_lr->block_info_size) + return (struct df_lr_bb_info *) df_lr->block_info[index]; + else + return NULL; +} + +static inline struct df_live_bb_info * +df_live_get_bb_info (unsigned int index) +{ + if (index < df_live->block_info_size) + return (struct df_live_bb_info *) df_live->block_info[index]; + else + return NULL; +} + +static inline struct df_urec_bb_info * +df_urec_get_bb_info (unsigned int index) +{ + if (index < df_urec->block_info_size) + return (struct df_urec_bb_info *) df_urec->block_info[index]; + else + return NULL; +} + + +/* Get the artificial defs for a basic block. */ + +static inline struct df_ref ** +df_get_artificial_defs (unsigned int bb_index) +{ + return df_scan_get_bb_info (bb_index)->artificial_defs; +} + + +/* Get the artificial uses for a basic block. */ + +static inline struct df_ref ** +df_get_artificial_uses (unsigned int bb_index) +{ + return df_scan_get_bb_info (bb_index)->artificial_uses; +} /* web */ @@ -672,9 +1018,8 @@ struct web_entry extern struct web_entry *unionfind_root (struct web_entry *); extern bool unionfind_union (struct web_entry *, struct web_entry *); -extern void union_defs (struct df *, struct df_ref *, +extern void union_defs (struct df_ref *, struct web_entry *, struct web_entry *, bool (*fun) (struct web_entry *, struct web_entry *)); - #endif /* GCC_DF_H */ diff --git a/gcc/doc/cfg.texi b/gcc/doc/cfg.texi index 68b8063..dca8847 100644 --- a/gcc/doc/cfg.texi +++ b/gcc/doc/cfg.texi @@ -1,5 +1,5 @@ @c -*-texinfo-*- -@c Copyright (C) 2001, 2003, 2004, 2005 Free Software Foundation, Inc. +@c Copyright (C) 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. @c This is part of the GCC manual. @c For copying conditions, see the file gcc.texi. @@ -626,41 +626,34 @@ may be used at a later point in the program. This information is used, for instance, during register allocation, as the pseudo registers only need to be assigned to a unique hard register or to a stack slot if they are live. The hard registers and stack slots may -be freely reused for other values when a register is dead. +be freely reused for other values when a register is dead. + +Liveness information is available in the back end starting with +@code{pass_df_initialize} and ending with @code{pass_df_finish}. Three +flavors of live analysis are available: With @code{LR}, it is possible +to determine at any point @code{P} in the function if the register may be +used on some path from @code{P} to the end of the function. With +@code{UR}, it is possible to determine if there is a path from the +beginning of the function to @code{P} that defines the variable. +@code{LIVE} is the intersection of the @code{LR} and @code{UR} and a +variable is live at @code{P} if there is both an assignment that reaches +it from the beginning of the function and a uses that can be reached on +some path from @code{P} to the end of the function. + +In general @code{LIVE} is the most useful of the three. The macros +@code{DF_[LR,UR,LIVE]_[IN,OUT]} can be used to access this information. +The macros take a basic block number and return a bitmap that is indexed +by the register number. This information is only guaranteed to be up to +date after calls are made to @code{df_analyze}. See the file +@code{df-core.c} for details on using the dataflow. + @findex REG_DEAD, REG_UNUSED -The liveness information is stored partly in the RTL instruction -stream and partly in the flow graph. Local information is stored in -the instruction stream: -Each instruction may contain @code{REG_DEAD} notes representing that -the value of a given register is no longer needed, or +The liveness information is stored partly in the RTL instruction stream +and partly in the flow graph. Local information is stored in the +instruction stream: Each instruction may contain @code{REG_DEAD} notes +representing that the value of a given register is no longer needed, or @code{REG_UNUSED} notes representing that the value computed by the instruction is never used. The second is useful for instructions computing multiple values at once. -@findex global_live_at_start, global_live_at_end -Global liveness information is stored in the control flow graph. -Each basic block contains two bitmaps, @code{global_live_at_start} and -@code{global_live_at_end} representing liveness of each register at -the entry and exit of the basic block. The file @code{flow.c} -contains functions to compute liveness of each register at any given -place in the instruction stream using this information. - -@findex BB_DIRTY, clear_bb_flags, update_life_info_in_dirty_blocks -Liveness is expensive to compute and thus it is desirable to keep it -up to date during code modifying passes. This can be easily -accomplished using the @code{flags} field of a basic block. Functions -modifying the instruction stream automatically set the @code{BB_DIRTY} -flag of a modifies basic block, so the pass may simply -use@code{clear_bb_flags} before doing any modifications and then ask -the data flow module to have liveness updated via the -@code{update_life_info_in_dirty_blocks} function. - -This scheme works reliably as long as no control flow graph -transformations are done. The task of updating liveness after control -flow graph changes is more difficult as normal iterative data flow -analysis may produce invalid results or get into an infinite cycle -when the initial solution is not below the desired one. Only simple -transformations, like splitting basic blocks or inserting on edges, -are safe, as functions to implement them already know how to update -liveness information locally. diff --git a/gcc/doc/rtl.texi b/gcc/doc/rtl.texi index 0ba9b95..09e73e1 100644 --- a/gcc/doc/rtl.texi +++ b/gcc/doc/rtl.texi @@ -1,5 +1,5 @@ @c Copyright (C) 1988, 1989, 1992, 1994, 1997, 1998, 1999, 2000, 2001, 2002, -@c 2003, 2004, 2005 +@c 2003, 2004, 2005, 2006, 2007 @c Free Software Foundation, Inc. @c This is part of the GCC manual. @c For copying conditions, see the file gcc.texi. @@ -3250,7 +3250,9 @@ file as some small positive or negative offset from a named pattern. @item LOG_LINKS (@var{i}) A list (chain of @code{insn_list} expressions) giving information about dependencies between instructions within a basic block. Neither a jump -nor a label may come between the related insns. +nor a label may come between the related insns. These are only used by +the schedulers and by combine. This is a deprecated data structure. +Def-use and use-def chains are now prefered. @findex REG_NOTES @item REG_NOTES (@var{i}) @@ -3531,6 +3533,12 @@ of the JUMP@. The format is a bitmask of ATTR_FLAG_* values. This is used on an RTX_FRAME_RELATED_P insn wherein the attached expression is used in place of the actual insn pattern. This is done in cases where the pattern is either complex or misleading. + +@findex REG_LIBCALL_ID +@item REG_LIBCALL_ID +This is used to specify that an insn is part of a libcall. Each libcall +in a function has a unique id, and all the insns that are part of that +libcall will have a REG_LIBCALL_ID note attached with the same ID. @end table For convenience, the machine mode in an @code{insn_list} or diff --git a/gcc/dominance.c b/gcc/dominance.c index 3ad0d20..6fa765c 100644 --- a/gcc/dominance.c +++ b/gcc/dominance.c @@ -1,5 +1,5 @@ /* Calculate (post)dominators in slightly super-linear time. - Copyright (C) 2000, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2000, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Michael Matz (matz@ifh.de). This file is part of GCC. @@ -152,6 +152,7 @@ static unsigned n_bbs_in_dom_tree[2]; static void init_dom_info (struct dom_info *di, enum cdi_direction dir) { + /* We need memory for n_basic_blocks nodes. */ unsigned int num = n_basic_blocks; init_ar (di->dfs_parent, TBB, num, 0); init_ar (di->path_min, TBB, num, i); diff --git a/gcc/dse.c b/gcc/dse.c new file mode 100644 index 0000000..e846f3f --- /dev/null +++ b/gcc/dse.c @@ -0,0 +1,3108 @@ +/* RTL dead store elimination. + Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc. + + Contributed by Richard Sandiford <rsandifor@codesourcery.com> + and Kenneth Zadeck <zadeck@naturalbridge.com> + +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, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ + +#undef BASELINE + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "hashtab.h" +#include "tm.h" +#include "rtl.h" +#include "tree.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "flags.h" +#include "df.h" +#include "cselib.h" +#include "timevar.h" +#include "tree-pass.h" +#include "alloc-pool.h" +#include "alias.h" +#include "insn-config.h" +#include "expr.h" +#include "recog.h" +#include "dse.h" +#include "dbgcnt.h" + +/* This file contains three techniques for performing Dead Store + Elimination (dse). + + * The first technique performs dse locally on any base address. It + is based on the cselib which is a local value numbering technique. + This technique is local to a basic block but deals with a fairly + general addresses. + + * The second technique performs dse globally but is restricted to + base addresses that are either constant or are relative to the + frame_pointer. + + * The third technique, (which is only done after register allocation) + processes the spill spill slots. This differs from the second + technique because it takes advantage of the fact that spilling is + completely free from the effects of aliasing. + + Logically, dse is a backwards dataflow problem. A store can be + deleted if it if cannot be reached in the backward direction by any + use of the value being stored. However, the local technique uses a + forwards scan of the basic block because cselib requires that the + block be processed in that order. + + The pass is logically broken into 7 steps: + + 0) Initialization. + + 1) The local algorithm, as well as scanning the insns for the two + global algorithms. + + 2) Analysis to see if the global algs are necessary. In the case + of stores base on a constant address, there must be at least two + stores to that address, to make it possible to delete some of the + stores. In the case of stores off of the frame or spill related + stores, only one store to an address is necessary because those + stores die at the end of the function. + + 3) Set up the global dataflow equations based on processing the + info parsed in the first step. + + 4) Solve the dataflow equations. + + 5) Delete the insns that the global analysis has indicated are + unnecessary. + + 6) Cleanup. + + This step uses cselib and canon_rtx to build the largest expression + possible for each address. This pass is a forwards pass through + each basic block. From the point of view of the global technique, + the first pass could examine a block in either direction. The + forwards ordering is to accomodate cselib. + + We a simplifying assumption: addresses fall into four broad + categories: + + 1) base has rtx_varies_p == false, offset is constant. + 2) base has rtx_varies_p == false, offset variable. + 3) base has rtx_varies_p == true, offset constant. + 4) base has rtx_varies_p == true, offset variable. + + The local passes are able to process all 4 kinds of addresses. The + global pass only handles (1). + + The global problem is formulated as follows: + + A store, S1, to address A, where A is not relative to the stack + frame, can be eliminated if all paths from S1 to the end of the + of the function contain another store to A before a read to A. + + If the address A is relative to the stack frame, a store S2 to A + can be eliminated if there are no paths from S1 that reach the + end of the function that read A before another store to A. In + this case S2 can be deleted if there are paths to from S2 to the + end of the function that have no reads or writes to A. This + second case allows stores to the stack frame to be deleted that + would otherwise die when the function returns. This cannot be + done if stores_off_frame_dead_at_return is not true. See the doc + for that variable for when this variable is false. + + The global problem is formulated as a backwards set union + dataflow problem where the stores are the gens and reads are the + kills. Set union problems are rare and require some special + handling given our representation of bitmaps. A straightforward + implementation of requires a lot of bitmaps filled with 1s. + These are expensive and cumbersome in our bitmap formulation so + care has been taken to avoid large vectors filled with 1s. See + the comments in bb_info and in the dataflow confluence functions + for details. + + There are two places for further enhancements to this algorithm: + + 1) The original dse which was embedded in a pass called flow also + did local address forwarding. For example in + + A <- r100 + ... <- A + + flow would replace the right hand side of the second insn with a + reference to r100. Most of the infomation is available to add this + to this pass. It has not done it because it is a lot of work in + the case that either r100 is assigned to between the first and + second insn and/or the second insn is a load of part of the value + stored by the first insn. + + insn 5 in gcc.c-torture/compile/990203-1.c simple case. + insn 15 in gcc.c-torture/execute/20001017-2.c simple case. + insn 25 in gcc.c-torture/execute/20001026-1.c simple case. + insn 44 in gcc.c-torture/execute/20010910-1.c simple case. + + 2) The cleaning up of spill code is quite profitable. It currently + depends on reading tea leaves and chicken entrails left by reload. + This pass depends on reload creating a singleton alias set for each + spill slot and telling the next dse pass which of these alias sets + are the singletons. Rather than analyze the addresses of the + spills, dse's spill processing just does analysis of the loads and + stores that use those alias sets. There are three cases where this + falls short: + + a) Reload sometimes creates the slot for one mode of access, and + then inserts loads and/or stores for a smaller mode. In this + case, the current code just punts on the slot. The proper thing + to do is to back out and use one bit vector position for each + byte of the entity associated with the slot. This depends on + KNOWING that reload always generates the accesses for each of the + bytes in some canonical (read that easy to understand several + passes after reload happens) way. + + b) Reload sometimes decides that spill slot it allocated was not + large enough for the mode and goes back and allocates more slots + with the same mode and alias set. The backout in this case is a + little more graceful than (a). In this case the slot is unmarked + as being a spill slot and if final address comes out to be based + off the frame pointer, the global algorithm handles this slot. + + c) For any pass that may prespill, there is currently no + mechanism to tell the dse pass that the slot being used has the + special properties that reload uses. It may be that all that is + requirred is to have those passes make the same calls that reload + does, assuming that the alias sets can be manipulated in the same + way. */ + +/* There are limits to the size of constant offsets we model for the + global problem. There are certainly test cases, that exceed this + limit, however, it is unlikely that there are important programs + that really have constant offsets this size. */ +#define MAX_OFFSET (64 * 1024) + + +static bitmap scratch = NULL; +struct insn_info; + +/* This structure holds information about a candidate store. */ +struct store_info +{ + + /* False means this is a clobber. */ + bool is_set; + + /* The id of the mem group of the base address. If rtx_varies_p is + true, this is -1. Otherwise, it is the index into the group + table. */ + int group_id; + + /* This is the cselib value. */ + cselib_val *cse_base; + + /* This canonized mem. */ + rtx mem; + + /* The result of get_addr on mem. */ + rtx mem_addr; + + /* If this is non-zero, it is the alias set of a spill location. */ + HOST_WIDE_INT alias_set; + + /* The offset of the first and byte before the last byte associated + with the operation. */ + int begin, end; + + /* An bitmask as wide as the number of bytes in the word that + contains a 1 if the byte may be needed. The store is unused if + all of the bits are 0. */ + long positions_needed; + + /* The next store info for this insn. */ + struct store_info *next; + + /* The right hand side of the store. This is used if there is a + subsequent reload of the mems address somewhere later in the + basic block. */ + rtx rhs; +}; + +typedef struct store_info *store_info_t; +static alloc_pool cse_store_info_pool; +static alloc_pool rtx_store_info_pool; + +/* This structure holds information about a load. These are only + built for rtx bases. */ +struct read_info +{ + /* The id of the mem group of the base address. */ + int group_id; + + /* If this is non-zero, it is the alias set of a spill location. */ + HOST_WIDE_INT alias_set; + + /* The offset of the first and byte after the last byte associated + with the operation. If begin == end == 0, the read did not have + a constant offset. */ + int begin, end; + + /* The mem being read. */ + rtx mem; + + /* The next read_info for this insn. */ + struct read_info *next; +}; +typedef struct read_info *read_info_t; +static alloc_pool read_info_pool; + + +/* One of these records is created for each insn. */ + +struct insn_info +{ + /* Set true if the insn contains a store but the insn itself cannot + be deleted. This is set if the insn is a parallel and there is + more than one non dead output or if the insn is in some way + volatile. */ + bool cannot_delete; + + /* This field is only used by the global algorithm. It is set true + if the insn contains any read of mem except for a (1). This is + also set if the insn is a call or has a clobber mem. If the insn + contains a wild read, the use_rec will be null. */ + bool wild_read; + + /* This field is set for const function calls. Const functions + cannot read memory, but they can read the stack because that is + where they may get their parms. So having this set is less + severe than a wild read, it just means that all of the stores to + the stack are killed rather than all stores. */ + bool stack_read; + + /* This is true if any of the sets within the store contains a + cselib base. Such stores can only be deleted by the local + algorithm. */ + bool contains_cselib_groups; + + /* The insn. */ + rtx insn; + + /* The list of mem sets or mem clobbers that are contained in this + insn. If the insn is deletable, it contains only one mem set. + But it could also contain clobbers. Insns that contain more than + one mem set are not deletable, but each of those mems are here in + order to provied info to delete other insns. */ + store_info_t store_rec; + + /* The linked list of mem uses in this insn. Only the reads from + rtx bases are listed here. The reads to cselib bases are + completely processed during the first scan and so are never + created. */ + read_info_t read_rec; + + /* The prev insn in the basic block. */ + struct insn_info * prev_insn; + + /* The linked list of insns that are in consideration for removal in + the forwards pass thru the basic block. This pointer may be + trash as it is not cleared when a wild read occurs. The only + time it is guaranteed to be correct is when the traveral starts + at active_local_stores. */ + struct insn_info * next_local_store; +}; + +typedef struct insn_info *insn_info_t; +static alloc_pool insn_info_pool; + +/* The linked list of stores that are under consideration in this + basic block. */ +static insn_info_t active_local_stores; + +struct bb_info +{ + + /* Pointer to the insn info for the last insn in the block. These + are linked so this is how all of the insns are reached. During + scanning this is the current insn being scanned. */ + insn_info_t last_insn; + + /* The info for the global dataflow problem. */ + + + /* This is set if the transfer function should and in the wild_read + bitmap before applying the kill and gen sets. That vector knocks + out most of the bits in the bitmap and thus speeds up the + operations. */ + bool apply_wild_read; + + /* The set of store positions that exist in this block before a wild read. */ + bitmap gen; + + /* The set of load positions that exist in this block above the + same position of a store. */ + bitmap kill; + + /* The set of stores that reach the top of the block without being + killed by a read. + + Do not represent the in if it is all ones. Note that this is + what the bitvector should logically be initialized to for a set + intersection problem. However, like the kill set, this is too + expensive. So initially, the in set will only be created for the + exit block and any block that contains a wild read. */ + bitmap in; + + /* The set of stores that reach the bottom of the block from it's + successors. + + Do not represent the in if it is all ones. Note that this is + what the bitvector should logically be initialized to for a set + intersection problem. However, like the kill and in set, this is + too expensive. So what is done is that the confluence operator + just initializes the vector from one of the out sets of the + successors of the block. */ + bitmap out; +}; + +typedef struct bb_info *bb_info_t; +static alloc_pool bb_info_pool; + +/* Table to hold all bb_infos. */ +static bb_info_t *bb_table; + +/* There is a group_info for each rtx base that is used to reference + memory. There are also not many of the rtx bases because they are + very limited in scope. */ + +struct group_info +{ + /* The actual base of the address. */ + rtx rtx_base; + + /* The sequential id of the base. This allows us to have a + canonical ordering of these that is not based on addresses. */ + int id; + + /* A mem wrapped around the base pointer for the group in order to + do read dependency. */ + rtx base_mem; + + /* Canonized version of base_mem, most likely the same thing. */ + rtx canon_base_mem; + + /* These two sets of two bitmaps are used to keep track of how many + stores are actually referencing that postion from this base. We + only do this for rtx bases as this will be used to assign + postions in the bitmaps for the global problem. Bit N is set in + store1 on the first store for offset N. Bit N is set in store2 + for the second store to offset N. This is all we need since we + only care about offsets that have two or more stores for them. + + The "_n" suffix is for offsets less than 0 and the "_p" suffix is + for 0 and greater offsets. + + There is one special case here, for stores into the stack frame, + we will or store1 into store2 before deciding which stores look + at globally. This is because stores to the stack frame that have + no other reads before the end of the function can also be + deleted. */ + bitmap store1_n, store1_p, store2_n, store2_p; + + /* The postions in this bitmap have the same assignments as the in, + out, gen and kill bitmaps. This bitmap is all zeros except for + the postions that are occupied by stores for this group. */ + bitmap group_kill; + + /* True if there are any positions that are to be processed + globally. */ + bool process_globally; + + /* True if the base of this group is either the frame_pointer or + hard_frame_pointer. */ + bool frame_related; + + /* The offset_map is used to map the offsets from this base into + postions in the global bitmaps. It is only created after all of + the all of stores have been scanned and we know which ones we + care about. */ + int *offset_map_n, *offset_map_p; + int offset_map_size_n, offset_map_size_p; +}; +typedef struct group_info *group_info_t; +static alloc_pool rtx_group_info_pool; + +/* Tables of group_info structures, hashed by base value. */ +static htab_t rtx_group_table; + +/* Index into the rtx_group_vec. */ +static int rtx_group_next_id; + +DEF_VEC_P(group_info_t); +DEF_VEC_ALLOC_P(group_info_t,heap); + +static VEC(group_info_t,heap) *rtx_group_vec; + + +/* This structure holds the set of changes that are being deferred + when removing read operation. See replace_read. */ +struct deferred_change +{ + + /* The mem that is being replaced. */ + rtx *loc; + + /* The reg it is being replaced with. */ + rtx reg; + + struct deferred_change *next; +}; + +typedef struct deferred_change *deferred_change_t; +static alloc_pool deferred_change_pool; + +static deferred_change_t deferred_change_list = NULL; + +/* This are used to hold the alias sets of spill variables. Since + these are never aliased and there may be a lot of them, it makes + sense to treat them specially. This bitvector is only allocated in + calls from dse_record_singleton_alias_set which currently is only + made during reload1. So when dse is called before reload this + mechanism does nothing. */ + +static bitmap clear_alias_sets = NULL; + +/* The set of clear_alias_sets that have been disqualified because + there are loads or stores using a different mode than the alias set + was registered with. */ +static bitmap disqualified_clear_alias_sets = NULL; + +/* The group that holds all of the clear_alias_sets. */ +static group_info_t clear_alias_group; + +/* The modes of the clear_alias_sets. */ +static htab_t clear_alias_mode_table; + +/* Hash table element to look up the mode for an alias set. */ +struct clear_alias_mode_holder +{ + HOST_WIDE_INT alias_set; + enum machine_mode mode; +}; + +static alloc_pool clear_alias_mode_pool; + +/* This is true except for two cases: + (1) current_function_stdarg -- i.e. we cannot do this + for vararg functions because they play games with the frame. + (2) In ada, it is sometimes not safe to do assume that any stores + based off the stack frame go dead at the exit to a function. */ +static bool stores_off_frame_dead_at_return; + +/* Counter for stats. */ +static int globally_deleted; +static int locally_deleted; +static int spill_deleted; + +static bitmap all_blocks; + +/* The number of bits used in the global bitmaps. */ +static unsigned int current_position; + + +static bool gate_dse (void); + + +/*---------------------------------------------------------------------------- + Zeroth step. + + Initialization. +----------------------------------------------------------------------------*/ + +/* Hashtable callbacks for maintaining the "bases" field of + store_group_info, given that the addresses are function invariants. */ + +static int +clear_alias_mode_eq (const void *p1, const void *p2) +{ + const struct clear_alias_mode_holder * h1 + = (const struct clear_alias_mode_holder *) p1; + const struct clear_alias_mode_holder * h2 + = (const struct clear_alias_mode_holder *) p2; + return h1->alias_set == h2->alias_set; +} + + +static hashval_t +clear_alias_mode_hash (const void *p) +{ + const struct clear_alias_mode_holder *holder + = (const struct clear_alias_mode_holder *) p; + return holder->alias_set; +} + + +/* Find the entry associated with ALIAS_SET. */ + +static struct clear_alias_mode_holder * +clear_alias_set_lookup (HOST_WIDE_INT alias_set) +{ + struct clear_alias_mode_holder tmp_holder; + void **slot; + + tmp_holder.alias_set = alias_set; + slot = htab_find_slot (clear_alias_mode_table, &tmp_holder, NO_INSERT); + gcc_assert (*slot); + + return *slot; +} + + +/* Hashtable callbacks for maintaining the "bases" field of + store_group_info, given that the addresses are function invariants. */ + +static int +invariant_group_base_eq (const void *p1, const void *p2) +{ + const group_info_t gi1 = (const group_info_t) p1; + const group_info_t gi2 = (const group_info_t) p2; + return rtx_equal_p (gi1->rtx_base, gi2->rtx_base); +} + + +static hashval_t +invariant_group_base_hash (const void *p) +{ + const group_info_t gi = (const group_info_t) p; + int do_not_record; + return hash_rtx (gi->rtx_base, Pmode, &do_not_record, NULL, false); +} + + +/* Get the GROUP for BASE. Add a new group if it is not there. */ + +static group_info_t +get_group_info (rtx base) +{ + struct group_info tmp_gi; + group_info_t gi; + void **slot; + + if (base) + { + /* Find the store_base_info structure for BASE, creating a new one + if necessary. */ + tmp_gi.rtx_base = base; + slot = htab_find_slot (rtx_group_table, &tmp_gi, INSERT); + gi = (group_info_t) *slot; + } + else + { + if (!clear_alias_group) + { + clear_alias_group = gi = pool_alloc (rtx_group_info_pool); + memset (gi, 0, sizeof (struct group_info)); + gi->id = rtx_group_next_id++; + gi->store1_n = BITMAP_ALLOC (NULL); + gi->store1_p = BITMAP_ALLOC (NULL); + gi->store2_n = BITMAP_ALLOC (NULL); + gi->store2_p = BITMAP_ALLOC (NULL); + gi->group_kill = BITMAP_ALLOC (NULL); + gi->process_globally = false; + gi->offset_map_size_n = 0; + gi->offset_map_size_p = 0; + gi->offset_map_n = NULL; + gi->offset_map_p = NULL; + VEC_safe_push (group_info_t, heap, rtx_group_vec, gi); + } + return clear_alias_group; + } + + if (gi == NULL) + { + *slot = gi = pool_alloc (rtx_group_info_pool); + gi->rtx_base = base; + gi->id = rtx_group_next_id++; + gi->base_mem = gen_rtx_MEM (QImode, base); + gi->canon_base_mem = canon_rtx (gi->base_mem); + gi->store1_n = BITMAP_ALLOC (NULL); + gi->store1_p = BITMAP_ALLOC (NULL); + gi->store2_n = BITMAP_ALLOC (NULL); + gi->store2_p = BITMAP_ALLOC (NULL); + gi->group_kill = BITMAP_ALLOC (NULL); + gi->process_globally = false; + gi->frame_related = + (base == frame_pointer_rtx) || (base == hard_frame_pointer_rtx); + gi->offset_map_size_n = 0; + gi->offset_map_size_p = 0; + gi->offset_map_n = NULL; + gi->offset_map_p = NULL; + VEC_safe_push (group_info_t, heap, rtx_group_vec, gi); + } + + return gi; +} + + +/* Initialization of data structures. */ + +static void +dse_step0 (void) +{ + locally_deleted = 0; + globally_deleted = 0; + spill_deleted = 0; + + scratch = BITMAP_ALLOC (NULL); + + rtx_store_info_pool + = create_alloc_pool ("rtx_store_info_pool", + sizeof (struct store_info), 100); + read_info_pool + = create_alloc_pool ("read_info_pool", + sizeof (struct read_info), 100); + insn_info_pool + = create_alloc_pool ("insn_info_pool", + sizeof (struct insn_info), 100); + bb_info_pool + = create_alloc_pool ("bb_info_pool", + sizeof (struct bb_info), 100); + rtx_group_info_pool + = create_alloc_pool ("rtx_group_info_pool", + sizeof (struct group_info), 100); + deferred_change_pool + = create_alloc_pool ("deferred_change_pool", + sizeof (struct deferred_change), 10); + + rtx_group_table = htab_create (11, invariant_group_base_hash, + invariant_group_base_eq, NULL); + + bb_table = XCNEWVEC (bb_info_t, last_basic_block); + rtx_group_next_id = 0; + + stores_off_frame_dead_at_return = + (!(TREE_CODE (TREE_TYPE (current_function_decl)) == FUNCTION_TYPE + && (TYPE_RETURNS_STACK_DEPRESSED (TREE_TYPE (current_function_decl))))) + && (!current_function_stdarg); + + init_alias_analysis (); + + if (clear_alias_sets) + clear_alias_group = get_group_info (NULL); + else + clear_alias_group = NULL; +} + + + +/*---------------------------------------------------------------------------- + First step. + + Scan all of the insns. Any random ordering of the blocks is fine. + Each block is scanned in forward order to accomodate cselib which + is used to remove stores with non-constant bases. +----------------------------------------------------------------------------*/ + +/* Delete all of the store_info recs from INSN_INFO. */ + +static void +free_store_info (insn_info_t insn_info) +{ + store_info_t store_info = insn_info->store_rec; + while (store_info) + { + store_info_t next = store_info->next; + if (store_info->cse_base) + pool_free (cse_store_info_pool, store_info); + else + pool_free (rtx_store_info_pool, store_info); + store_info = next; + } + + insn_info->cannot_delete = true; + insn_info->contains_cselib_groups = false; + insn_info->store_rec = NULL; +} + + +struct insn_size { + int size; + rtx insn; +}; + + +/* Add an insn to do the add inside a x if it is a + PRE/POST-INC/DEC/MODIFY. D is an structure containing the insn and + the size of the mode of the MEM that this is inside of. */ + +static int +replace_inc_dec (rtx *r, void *d) +{ + rtx x = *r; + struct insn_size *data = (struct insn_size *)d; + switch (GET_CODE (x)) + { + case PRE_INC: + case POST_INC: + { + rtx r1 = XEXP (x, 0); + rtx c = gen_int_mode (Pmode, data->size); + add_insn_before (data->insn, + gen_rtx_SET (Pmode, r1, + gen_rtx_PLUS (Pmode, r1, c)), + NULL); + return -1; + } + + case PRE_DEC: + case POST_DEC: + { + rtx r1 = XEXP (x, 0); + rtx c = gen_int_mode (Pmode, -data->size); + add_insn_before (data->insn, + gen_rtx_SET (Pmode, r1, + gen_rtx_PLUS (Pmode, r1, c)), + NULL); + return -1; + } + + case PRE_MODIFY: + case POST_MODIFY: + { + /* We can resuse the add because we are about to delete the + insn that contained it. */ + rtx add = XEXP (x, 0); + rtx r1 = XEXP (add, 0); + add_insn_before (data->insn, + gen_rtx_SET (Pmode, r1, add), NULL); + return -1; + } + + default: + return 0; + } +} + + +/* If X is a MEM, check the address to see if it is PRE/POST-INC/DEC/MODIFY + and generate an add to replace that. */ + +static int +replace_inc_dec_mem (rtx *r, void *d) +{ + rtx x = *r; + if (GET_CODE (x) == MEM) + { + struct insn_size data; + + data.size = GET_MODE_SIZE (GET_MODE (x)); + data.insn = (rtx)d; + + for_each_rtx (&XEXP (x, 0), replace_inc_dec, &data); + + return -1; + } + return 0; +} + +/* Before we delete INSN, make sure that the auto inc/dec, if it is + there, is split into a separate insn. */ + +static void +check_for_inc_dec (rtx insn) +{ + rtx note = find_reg_note (insn, REG_INC, NULL_RTX); + if (note) + for_each_rtx (&insn, replace_inc_dec_mem, insn); +} + + +/* Delete the insn and free all of the fields inside INSN_INFO. */ + +static void +delete_dead_store_insn (insn_info_t insn_info) +{ + read_info_t read_info; + + if (!dbg_cnt (dse)) + return; + + check_for_inc_dec (insn_info->insn); + if (dump_file) + { + fprintf (dump_file, "Locally deleting insn %d ", + INSN_UID (insn_info->insn)); + if (insn_info->store_rec->alias_set) + fprintf (dump_file, "alias set %d\n", + (int)insn_info->store_rec->alias_set); + else + fprintf (dump_file, "\n"); + } + + free_store_info (insn_info); + read_info = insn_info->read_rec; + + while (read_info) + { + read_info_t next = read_info->next; + pool_free (read_info_pool, read_info); + read_info = next; + } + insn_info->read_rec = NULL; + + delete_insn (insn_info->insn); + locally_deleted++; + insn_info->insn = NULL; + + insn_info->wild_read = false; +} + + +/* Set the store* bitmaps offset_map_size* fields in GROUP based on + OFFSET and WIDTH. */ + +static void +set_usage_bits (group_info_t group, HOST_WIDE_INT offset, HOST_WIDE_INT width) +{ + HOST_WIDE_INT i; + + if ((offset > -MAX_OFFSET) && (offset < MAX_OFFSET)) + for (i=offset; i<offset+width; i++) + { + bitmap store1; + bitmap store2; + int ai; + if (i < 0) + { + store1 = group->store1_n; + store2 = group->store2_n; + ai = -i; + } + else + { + store1 = group->store1_p; + store2 = group->store2_p; + ai = i; + } + + if (bitmap_bit_p (store1, ai)) + bitmap_set_bit (store2, ai); + else + { + bitmap_set_bit (store1, ai); + if (i < 0) + { + if (group->offset_map_size_n < ai) + group->offset_map_size_n = ai; + } + else + { + if (group->offset_map_size_p < ai) + group->offset_map_size_p = ai; + } + } + } +} + + +/* Set the BB_INFO so that the last insn is marked as a wild read. */ + +static void +add_wild_read (bb_info_t bb_info) +{ + insn_info_t insn_info = bb_info->last_insn; + read_info_t *ptr = &insn_info->read_rec; + + while (*ptr) + { + read_info_t next = (*ptr)->next; + if ( (*ptr)->alias_set == 0 ) + { + pool_free (read_info_pool, *ptr); + *ptr = next; + } + else + ptr = &(*ptr)->next; + } + insn_info->wild_read = true; + active_local_stores = NULL; +} + + +/* Return true if X is a constant or one of the registers that behaves + as a constant over the life of a function. */ + +static bool +const_or_frame_p (rtx x) +{ + switch (GET_CODE (x)) + { + case MEM: + return MEM_READONLY_P (x); + + case CONST: + case CONST_INT: + case CONST_DOUBLE: + case CONST_VECTOR: + case SYMBOL_REF: + case LABEL_REF: + return true; + + case REG: + /* Note that we have to test for the actual rtx used for the frame + and arg pointers and not just the register number in case we have + eliminated the frame and/or arg pointer and are using it + for pseudos. */ + if (x == frame_pointer_rtx || x == hard_frame_pointer_rtx + /* The arg pointer varies if it is not a fixed register. */ + || (x == arg_pointer_rtx && fixed_regs[ARG_POINTER_REGNUM]) + || x == pic_offset_table_rtx) + return true; + return false; + + default: + return false; + } +} + +/* Take all reasonable action to put the address of MEM into the form + that we can do analysis on. + + The gold standard is to get the address into the form: address + + OFFSET where address is something that rtx_varies_p considers a + constant. When we can get the address in this form, we can do + global analysis on it. Note that for constant bases, address is + not actually returned, only the group_id. The address can be + obtained from that. + + If that fails, we try cselib to get a value we can at least use + locally. If that fails we return false. + + The GROUP_ID is set to -1 for cselib bases and the index of the + group for non_varying bases. + + FOR_READ is true if this is a mem read and false if not. */ + +static bool +canon_address (rtx mem, + HOST_WIDE_INT *alias_set_out, + int *group_id, + HOST_WIDE_INT *offset, + cselib_val **base) +{ + rtx mem_address = XEXP (mem, 0); + rtx expanded_address, address; + /* Make sure that cselib is has initialized all of the operands of + the address before asking it to do the subst. */ + + if (clear_alias_sets) + { + /* If this is a spill, do not do any further processing. */ + HOST_WIDE_INT alias_set = MEM_ALIAS_SET (mem); + if (dump_file) + fprintf (dump_file, "found alias set %d\n", (int)alias_set); + if (bitmap_bit_p (clear_alias_sets, alias_set)) + { + struct clear_alias_mode_holder *entry + = clear_alias_set_lookup (alias_set); + + /* If the modes do not match, we cannot process this set. */ + if (entry->mode != GET_MODE (mem)) + { + if (dump_file) + fprintf (dump_file, + "disqualifying alias set %d, (%s) != (%s)\n", + (int)alias_set, GET_MODE_NAME (entry->mode), + GET_MODE_NAME (GET_MODE (mem))); + + bitmap_set_bit (disqualified_clear_alias_sets, alias_set); + return false; + } + + *alias_set_out = alias_set; + *group_id = clear_alias_group->id; + return true; + } + } + + *alias_set_out = 0; + + cselib_lookup (mem_address, Pmode, 1); + + if (dump_file) + { + fprintf (dump_file, " mem: "); + print_inline_rtx (dump_file, mem_address, 0); + fprintf (dump_file, "\n"); + } + + /* Use cselib to replace all of the reg references with the full + expression. This will take care of the case where we have + + r_x = base + offset; + val = *r_x; + + by making it into + + val = *(base + offset); + */ + + expanded_address = cselib_expand_value_rtx (mem_address, scratch, 5); + + /* If this fails, just go with the mem_address. */ + if (!expanded_address) + expanded_address = mem_address; + + /* Split the address into canonical BASE + OFFSET terms. */ + address = canon_rtx (expanded_address); + + *offset = 0; + + if (dump_file) + { + fprintf (dump_file, "\n after cselib_expand address: "); + print_inline_rtx (dump_file, expanded_address, 0); + fprintf (dump_file, "\n"); + + fprintf (dump_file, "\n after canon_rtx address: "); + print_inline_rtx (dump_file, address, 0); + fprintf (dump_file, "\n"); + } + + if (GET_CODE (address) == CONST) + address = XEXP (address, 0); + + if (GET_CODE (address) == PLUS && GET_CODE (XEXP (address, 1)) == CONST_INT) + { + *offset = INTVAL (XEXP (address, 1)); + address = XEXP (address, 0); + } + + if (const_or_frame_p (address)) + { + group_info_t group = get_group_info (address); + + if (dump_file) + fprintf (dump_file, " gid=%d offset=%d \n", group->id, (int)*offset); + *base = NULL; + *group_id = group->id; + } + else + { + *base = cselib_lookup (address, Pmode, true); + *group_id = -1; + + if (*base == NULL) + { + if (dump_file) + fprintf (dump_file, " no cselib val - should be a wild read.\n"); + return false; + } + if (dump_file) + fprintf (dump_file, " varying cselib base=%d offset = %d\n", + (*base)->value, (int)*offset); + } + return true; +} + + +/* Clear the rhs field from the active_local_stores array. */ + +static void +clear_rhs_from_active_local_stores (void) +{ + insn_info_t ptr = active_local_stores; + + while (ptr) + { + store_info_t store_info = ptr->store_rec; + /* Skip the clobbers. */ + while (!store_info->is_set) + store_info = store_info->next; + + store_info->rhs = NULL; + + ptr = ptr->next_local_store; + } +} + + +/* BODY is an instruction pattern that belongs to INSN. Return 1 if + there is a candidate store, after adding it to the appropriate + local store group if so. */ + +static int +record_store (rtx body, bb_info_t bb_info) +{ + rtx mem; + HOST_WIDE_INT offset = 0; + HOST_WIDE_INT width = 0; + HOST_WIDE_INT spill_alias_set; + insn_info_t insn_info = bb_info->last_insn; + store_info_t store_info = NULL; + int group_id; + cselib_val *base = NULL; + insn_info_t ptr, last; + bool store_is_unused; + + if (GET_CODE (body) != SET && GET_CODE (body) != CLOBBER) + return 0; + + /* If this is not used, then this cannot be used to keep the insn + from being deleted. On the other hand, it does provide something + that can be used to prove that another store is dead. */ + store_is_unused + = (find_reg_note (insn_info->insn, REG_UNUSED, body) != NULL); + + /* Check whether that value is a suitable memory location. */ + mem = SET_DEST (body); + if (!MEM_P (mem)) + { + /* If the set or clobber is unused, then it does not effect our + ability to get rid of the entire insn. */ + if (!store_is_unused) + insn_info->cannot_delete = true; + return 0; + } + + /* At this point we know mem is a mem. */ + if (GET_MODE (mem) == BLKmode) + { + if (GET_CODE (XEXP (mem, 0)) == SCRATCH) + { + if (dump_file) + fprintf (dump_file, " adding wild read for (clobber (mem:BLK (scratch))\n"); + add_wild_read (bb_info); + insn_info->cannot_delete = true; + } + else if (!store_is_unused) + { + /* If the set or clobber is unused, then it does not effect our + ability to get rid of the entire insn. */ + insn_info->cannot_delete = true; + clear_rhs_from_active_local_stores (); + } + return 0; + } + + /* We can still process a volatile mem, we just cannot delete it. */ + if (MEM_VOLATILE_P (mem)) + insn_info->cannot_delete = true; + + if (!canon_address (mem, &spill_alias_set, &group_id, &offset, &base)) + { + clear_rhs_from_active_local_stores (); + return 0; + } + + width = GET_MODE_SIZE (GET_MODE (mem)); + + if (spill_alias_set) + { + bitmap store1 = clear_alias_group->store1_p; + bitmap store2 = clear_alias_group->store2_p; + + if (bitmap_bit_p (store1, spill_alias_set)) + bitmap_set_bit (store2, spill_alias_set); + else + bitmap_set_bit (store1, spill_alias_set); + + if (clear_alias_group->offset_map_size_p < spill_alias_set) + clear_alias_group->offset_map_size_p = spill_alias_set; + + store_info = pool_alloc (rtx_store_info_pool); + + if (dump_file) + fprintf (dump_file, " processing spill store %d(%s)\n", + (int)spill_alias_set, GET_MODE_NAME (GET_MODE (mem))); + } + else if (group_id >= 0) + { + /* In the restrictive case where the base is a constant or the + frame pointer we can do global analysis. */ + + group_info_t group + = VEC_index (group_info_t, rtx_group_vec, group_id); + + store_info = pool_alloc (rtx_store_info_pool); + set_usage_bits (group, offset, width); + + if (dump_file) + fprintf (dump_file, " processing const base store gid=%d[%d..%d)\n", + group_id, (int)offset, (int)(offset+width)); + } + else + { + store_info = pool_alloc (cse_store_info_pool); + insn_info->contains_cselib_groups = true; + group_id = -1; + + if (dump_file) + fprintf (dump_file, " processing cselib store [%d..%d)\n", + (int)offset, (int)(offset+width)); + } + + /* Check to see if this stores causes some other stores to be + dead. */ + ptr = active_local_stores; + last = NULL; + + while (ptr) + { + insn_info_t next = ptr->next_local_store; + store_info_t s_info = ptr->store_rec; + bool delete = true; + + /* Skip the clobbers. We delete the active insn if this insn + shaddows the set. To have been put on the active list, it + has exactly on set. */ + while (!s_info->is_set) + s_info = s_info->next; + + if (s_info->alias_set != spill_alias_set) + delete = false; + else if (s_info->alias_set) + { + struct clear_alias_mode_holder *entry + = clear_alias_set_lookup (s_info->alias_set); + /* Generally, spills cannot be processed if and of the + references to the slot have a different mode. But if + we are in the same block and mode is exactly the same + between this store and one before in the same block, + we can still delete it. */ + if ((GET_MODE (mem) == GET_MODE (s_info->mem)) + && (GET_MODE (mem) == entry->mode)) + { + delete = true; + s_info->positions_needed = 0; + } + if (dump_file) + fprintf (dump_file, " trying spill store in insn=%d alias_set=%d\n", + INSN_UID (ptr->insn), (int)s_info->alias_set); + } + else if ((s_info->group_id == group_id) + && (s_info->cse_base == base)) + { + HOST_WIDE_INT i; + if (dump_file) + fprintf (dump_file, " trying store in insn=%d gid=%d[%d..%d)\n", + INSN_UID (ptr->insn), s_info->group_id, + (int)s_info->begin, (int)s_info->end); + for (i = offset; i < offset+width; i++) + if (i >= s_info->begin && i < s_info->end) + s_info->positions_needed &= ~(1L << (i - s_info->begin)); + } + else if (s_info->rhs) + /* Need to see if it is possible for this store to overwrite + the value of store_info. If it is, set the rhs to NULL to + keep it from being used to remove a load. */ + { + if (canon_true_dependence (s_info->mem, + GET_MODE (s_info->mem), + s_info->mem_addr, + mem, rtx_varies_p)) + s_info->rhs = NULL; + } + + /* An insn can be deleted if every position of every one of + its s_infos is zero. */ + if (s_info->positions_needed != 0) + delete = false; + + if (delete) + { + insn_info_t insn_to_delete = ptr; + + if (last) + last->next_local_store = ptr->next_local_store; + else + active_local_stores = ptr->next_local_store; + + delete_dead_store_insn (insn_to_delete); + } + else + last = ptr; + + ptr = next; + } + + gcc_assert ((unsigned) width < sizeof (store_info->positions_needed) * CHAR_BIT); + + /* Finish filling in the store_info. */ + store_info->next = insn_info->store_rec; + insn_info->store_rec = store_info; + store_info->mem = canon_rtx (mem); + store_info->alias_set = spill_alias_set; + store_info->mem_addr = get_addr (XEXP (mem, 0)); + store_info->cse_base = base; + store_info->positions_needed = (1L << width) - 1; + store_info->group_id = group_id; + store_info->begin = offset; + store_info->end = offset + width; + store_info->is_set = GET_CODE (body) == SET; + + if (store_info->is_set + /* No place to keep the value after ra. */ + && !reload_completed + /* The careful reviewer may wish to comment my checking that the + rhs of a store is always a reg. */ + && REG_P (SET_SRC (body)) + /* Sometimes the store and reload is used for truncation and + rounding. */ + && !(FLOAT_MODE_P (GET_MODE (mem)) && (flag_float_store))) + store_info->rhs = SET_SRC (body); + else + store_info->rhs = NULL; + + /* If this is a clobber, we return 0. We will only be able to + delete this insn if there is only one store USED store, but we + can use the clobber to delete other stores earlier. */ + return store_info->is_set ? 1 : 0; +} + + +static void +dump_insn_info (const char * start, insn_info_t insn_info) +{ + fprintf (dump_file, "%s insn=%d %s\n", start, + INSN_UID (insn_info->insn), + insn_info->store_rec ? "has store" : "naked"); +} + + +/* Take a sequence of: + A <- r1 + ... + ... <- A + + and change it into + r2 <- r1 + A <- r1 + ... + ... <- r2 + + The STORE_INFO and STORE_INFO are for the store and the READ_INFO + and READ_INSN are for the read. Return true if the replacement + went ok. */ + +static bool +replace_read (store_info_t store_info, insn_info_t store_insn, + read_info_t read_info, insn_info_t read_insn, rtx *loc) +{ + if (!dbg_cnt (dse)) + return false; + + if (dump_file) + fprintf (dump_file, "generating move to replace load at %d from store at %d\n", + INSN_UID (read_insn->insn), INSN_UID (store_insn->insn)); + if (GET_MODE (store_info->mem) == GET_MODE (read_info->mem)) + { + rtx new_reg = gen_reg_rtx (GET_MODE (store_info->mem)); + if (validate_change (read_insn->insn, loc, new_reg, 0)) + { + rtx insns; + deferred_change_t deferred_change = pool_alloc (deferred_change_pool); + + start_sequence (); + emit_move_insn (new_reg, store_info->rhs); + insns = get_insns (); + end_sequence (); + emit_insn_before (insns, store_insn->insn); + + if (dump_file) + fprintf (dump_file, " -- adding move insn %d: r%d = r%d\n", + INSN_UID (insns), REGNO (new_reg), REGNO (store_info->rhs)); + + /* And now for the cludge part: cselib croaks if you just + return at this point. There are two reasons for this: + + 1) Cselib has an idea of how many pseudos there are and + that does not include the new one we just added. + + 2) Cselib does not know about the move insn we added + above the store_info, and there is no way to tell it + about it, because it has "moved on". + + So we are just going to have to lie. The move insn is + not really an issue, cselib did not see it. But the use + of the new pseudo read_insn is a real problem. The way + that we solve this problem is that we are just going to + put the mem back keep a table of mems to get rid of. At + the end of the basic block we can put it back. */ + + *loc = read_info->mem; + deferred_change->next = deferred_change_list; + deferred_change_list = deferred_change; + deferred_change->loc = loc; + deferred_change->reg = new_reg; + + /* Get rid of the read_info, from the point of view of the + rest of dse, play like this read never happened. */ + read_insn->read_rec = read_info->next; + pool_free (read_info_pool, read_info); + return true; + } + else + { + if (dump_file) + fprintf (dump_file, " -- validation failure\n"); + return false; + } + } + else + { + /* Someone with excellent rtl skills needs to fill this in. You + are guaranteed that the read is of the same size or smaller + than the store, and that the read does not hang off one of + the ends of the store. But the offsets of each must be + checked because the read does not have to line up on either + end of the store so the begin fields need to be examined in + both the store_info and read_info. */ + if (dump_file) + fprintf (dump_file, " -- complex load, currently unsupported.\n"); + return false; + } +} + + +/* A for_each_rtx callback in which DATA is the bb_info. Check to see + if LOC is a mem and if it is look at the address and kill any + appropriate stores that may be active. */ + +static int +check_mem_read_rtx (rtx *loc, void *data) +{ + rtx mem = *loc; + bb_info_t bb_info; + insn_info_t insn_info; + HOST_WIDE_INT offset = 0; + HOST_WIDE_INT width = 0; + HOST_WIDE_INT spill_alias_set = 0; + cselib_val *base = NULL; + int group_id; + read_info_t read_info; + + if (!mem || !MEM_P (mem)) + return 0; + + bb_info = (bb_info_t) data; + insn_info = bb_info->last_insn; + + if ((MEM_ALIAS_SET (mem) == ALIAS_SET_MEMORY_BARRIER) + || (MEM_VOLATILE_P (mem))) + { + if (dump_file) + fprintf (dump_file, " adding wild read, volatile or barrier.\n"); + add_wild_read (bb_info); + insn_info->cannot_delete = true; + return 0; + } + + /* If it is reading readonly mem, then there can be no conflict with + another write. */ + if (MEM_READONLY_P (mem)) + return 0; + + if (!canon_address (mem, &spill_alias_set, &group_id, &offset, &base)) + { + if (dump_file) + fprintf (dump_file, " adding wild read, canon_address failure.\n"); + add_wild_read (bb_info); + return 0; + } + + if (GET_MODE (mem) == BLKmode) + width = -1; + else + width = GET_MODE_SIZE (GET_MODE (mem)); + + read_info = pool_alloc (read_info_pool); + read_info->group_id = group_id; + read_info->mem = mem; + read_info->alias_set = spill_alias_set; + read_info->begin = offset; + read_info->end = offset + width; + read_info->next = insn_info->read_rec; + insn_info->read_rec = read_info; + + /* We ignore the clobbers in store_info. The is mildly agressive, + but there really should not be a clobber followed by a read. */ + + if (spill_alias_set) + { + insn_info_t i_ptr = active_local_stores; + insn_info_t last = NULL; + + if (dump_file) + fprintf (dump_file, " processing spill load %d\n", + (int)spill_alias_set); + + while (i_ptr) + { + store_info_t store_info = i_ptr->store_rec; + + /* Skip the clobbers. */ + while (!store_info->is_set) + store_info = store_info->next; + + if (store_info->alias_set == spill_alias_set) + { + if (dump_file) + dump_insn_info ("removing from active", i_ptr); + + if (last) + last->next_local_store = i_ptr->next_local_store; + else + active_local_stores = i_ptr->next_local_store; + } + else + last = i_ptr; + i_ptr = i_ptr->next_local_store; + } + } + else if (group_id >= 0) + { + /* This is the restricted case where the base is a constant or + the frame pointer and offset is a constant. */ + insn_info_t i_ptr = active_local_stores; + insn_info_t last = NULL; + + if (dump_file) + { + if (width == -1) + fprintf (dump_file, " processing const load gid=%d[BLK]\n", + group_id); + else + fprintf (dump_file, " processing const load gid=%d[%d..%d)\n", + group_id, (int)offset, (int)(offset+width)); + } + + while (i_ptr) + { + bool remove = false; + store_info_t store_info = i_ptr->store_rec; + + /* Skip the clobbers. */ + while (!store_info->is_set) + store_info = store_info->next; + + /* There are three cases here. */ + if (store_info->group_id < 0) + /* We have a cselib store followed by a read from a + const base. */ + remove + = canon_true_dependence (store_info->mem, + GET_MODE (store_info->mem), + store_info->mem_addr, + mem, rtx_varies_p); + + else if (group_id == store_info->group_id) + { + /* This is a block mode load. We may get lucky and + canon_true_dependence may save the day. */ + if (width == -1) + remove + = canon_true_dependence (store_info->mem, + GET_MODE (store_info->mem), + store_info->mem_addr, + mem, rtx_varies_p); + + /* If this read is just reading back something that we just + stored, rewrite the read. */ + else + { + if (store_info->rhs + && (offset >= store_info->begin) + && (offset + width <= store_info->end)) + { + int mask = ((1L << width) - 1) << (offset - store_info->begin); + + if ((store_info->positions_needed & mask) == mask + && replace_read (store_info, i_ptr, + read_info, insn_info, loc)) + return 0; + } + /* The bases are the same, just see if the offsets + overlap. */ + if ((offset < store_info->end) + && (offset + width > store_info->begin)) + remove = true; + } + } + + /* else + The else case that is missing here is that the + bases are constant but different. There is nothing + to do here because there is no overlap. */ + + if (remove) + { + if (dump_file) + dump_insn_info ("removing from active", i_ptr); + + if (last) + last->next_local_store = i_ptr->next_local_store; + else + active_local_stores = i_ptr->next_local_store; + } + else + last = i_ptr; + i_ptr = i_ptr->next_local_store; + } + } + else + { + insn_info_t i_ptr = active_local_stores; + insn_info_t last = NULL; + if (dump_file) + { + fprintf (dump_file, " processing cselib load mem:"); + print_inline_rtx (dump_file, mem, 0); + fprintf (dump_file, "\n"); + } + + while (i_ptr) + { + bool remove = false; + store_info_t store_info = i_ptr->store_rec; + + if (dump_file) + fprintf (dump_file, " processing cselib load against insn %d\n", + INSN_UID (i_ptr->insn)); + + /* Skip the clobbers. */ + while (!store_info->is_set) + store_info = store_info->next; + + /* If this read is just reading back something that we just + stored, rewrite the read. */ + if (store_info->rhs + && store_info->group_id == -1 + && store_info->cse_base == base + && (offset >= store_info->begin) + && (offset + width <= store_info->end)) + { + int mask = ((1L << width) - 1) << (offset - store_info->begin); + + if ((store_info->positions_needed & mask) == mask + && replace_read (store_info, i_ptr, + read_info, insn_info, loc)) + return 0; + } + + if (!store_info->alias_set) + remove = canon_true_dependence (store_info->mem, + GET_MODE (store_info->mem), + store_info->mem_addr, + mem, rtx_varies_p); + + if (remove) + { + if (dump_file) + dump_insn_info ("removing from active", i_ptr); + + if (last) + last->next_local_store = i_ptr->next_local_store; + else + active_local_stores = i_ptr->next_local_store; + } + else + last = i_ptr; + i_ptr = i_ptr->next_local_store; + } + } + return 0; +} + +/* A for_each_rtx callback in which DATA points the INSN_INFO for + as check_mem_read_rtx. Nullify the pointer if i_m_r_m_r returns + true for any part of *LOC. */ + +static void +check_mem_read_use (rtx *loc, void *data) +{ + for_each_rtx (loc, check_mem_read_rtx, data); +} + +/* Apply record_store to all candidate stores in INSN. Mark INSN + if some part of it is not a candidate store and assigns to a + non-register target. */ + +static void +scan_insn (bb_info_t bb_info, rtx insn) +{ + rtx body; + insn_info_t insn_info = pool_alloc (insn_info_pool); + int mems_found = 0; + memset (insn_info, 0, sizeof (struct insn_info)); + + if (dump_file) + fprintf (dump_file, "\n**scanning insn=%d\n", + INSN_UID (insn)); + + insn_info->prev_insn = bb_info->last_insn; + insn_info->insn = insn; + bb_info->last_insn = insn_info; + + + /* Cselib clears the table for this case, so we have to essentually + do the same. */ + if (NONJUMP_INSN_P (insn) + && GET_CODE (PATTERN (insn)) == ASM_OPERANDS + && MEM_VOLATILE_P (PATTERN (insn))) + { + add_wild_read (bb_info); + insn_info->cannot_delete = true; + return; + } + + /* Look at all of the uses in the insn. */ + note_uses (&PATTERN (insn), check_mem_read_use, bb_info); + + if (CALL_P (insn)) + { + insn_info->cannot_delete = true; + /* Const functions cannot do anything bad i.e. read memory, + however, they can read their parameters which may have been + pushed onto the stack. */ + if (CONST_OR_PURE_CALL_P (insn) && !pure_call_p (insn)) + { + insn_info_t i_ptr = active_local_stores; + insn_info_t last = NULL; + + if (dump_file) + fprintf (dump_file, "const call %d\n", INSN_UID (insn)); + + while (i_ptr) + { + store_info_t store_info = i_ptr->store_rec; + + /* Skip the clobbers. */ + while (!store_info->is_set) + store_info = store_info->next; + + /* Remove the frame related stores. */ + if (store_info->group_id >= 0 + && VEC_index (group_info_t, rtx_group_vec, store_info->group_id)->frame_related) + { + if (dump_file) + dump_insn_info ("removing from active", i_ptr); + + if (last) + last->next_local_store = i_ptr->next_local_store; + else + active_local_stores = i_ptr->next_local_store; + } + else + last = i_ptr; + i_ptr = i_ptr->next_local_store; + } + + insn_info->stack_read = true; + + return; + } + + /* Every other call, including pure functions may read memory. */ + add_wild_read (bb_info); + return; + } + + /* Assuming that there are sets in these insns, we cannot delete + them. */ + if ((GET_CODE (PATTERN (insn)) == CLOBBER) + || volatile_insn_p (PATTERN (insn)) + || (flag_non_call_exceptions && may_trap_p (PATTERN (insn))) + || (RTX_FRAME_RELATED_P (insn)) + || find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX)) + insn_info->cannot_delete = true; + + body = PATTERN (insn); + if (GET_CODE (body) == PARALLEL) + { + int i; + for (i = 0; i < XVECLEN (body, 0); i++) + mems_found += record_store (XVECEXP (body, 0, i), bb_info); + } + else + mems_found += record_store (body, bb_info); + + if (dump_file) + fprintf (dump_file, "mems_found = %d, cannot_delete = %s\n", + mems_found, insn_info->cannot_delete ? "true" : "false"); + + /* If we found some sets of mems, and the insn has not been marked + cannot delete, add it into the active_local_stores so that it can + be locally deleted if found dead. Otherwise mark it as cannot + delete. This simplifies the processing later. */ + if (mems_found == 1 && !insn_info->cannot_delete) + { + insn_info->next_local_store = active_local_stores; + active_local_stores = insn_info; + } + else + insn_info->cannot_delete = true; +} + + +/* Remove BASE from the set of active_local_stores. This is a + callback from cselib that is used to get rid of the stores in + active_local_stores. */ + +static void +remove_useless_values (cselib_val *base) +{ + insn_info_t insn_info = active_local_stores; + insn_info_t last = NULL; + + while (insn_info) + { + store_info_t store_info = insn_info->store_rec; + bool delete = false; + + /* If ANY of the store_infos match the cselib group that is + being deleted, then the insn can not be deleted. */ + while (store_info) + { + if ((store_info->group_id == -1) + && (store_info->cse_base == base)) + { + delete = true; + break; + } + store_info = store_info->next; + } + + if (delete) + { + if (last) + last->next_local_store = insn_info->next_local_store; + else + active_local_stores = insn_info->next_local_store; + free_store_info (insn_info); + } + else + last = insn_info; + + insn_info = insn_info->next_local_store; + } +} + + +/* Do all of step 1. */ + +static void +dse_step1 (void) +{ + basic_block bb; + + cselib_init (false); + all_blocks = BITMAP_ALLOC (NULL); + bitmap_set_bit (all_blocks, ENTRY_BLOCK); + bitmap_set_bit (all_blocks, EXIT_BLOCK); + + FOR_ALL_BB (bb) + { + insn_info_t ptr; + bb_info_t bb_info = pool_alloc (bb_info_pool); + + memset (bb_info, 0, sizeof (struct bb_info)); + bitmap_set_bit (all_blocks, bb->index); + + bb_table[bb->index] = bb_info; + cselib_discard_hook = remove_useless_values; + + if (bb->index >= NUM_FIXED_BLOCKS) + { + rtx insn; + + cse_store_info_pool + = create_alloc_pool ("cse_store_info_pool", + sizeof (struct store_info), 100); + active_local_stores = NULL; + cselib_clear_table (); + + /* Scan the insns. */ + FOR_BB_INSNS (bb, insn) + { + if (INSN_P (insn)) + scan_insn (bb_info, insn); + cselib_process_insn (insn); + } + + /* This is something of a hack, because the global algorithm + is supposed to take care of the case where stores go dead + at the end of the function. However, the global + algorithm must take a more conservative view of block + mode reads than the local alg does. So to get the case + where you have a store to the frame followed by a non + overlaping block more read, we look at the active local + stores at the end of the function and delete all of the + frame and spill based ones. */ + if (stores_off_frame_dead_at_return + && (EDGE_COUNT (bb->succs) == 0 + || (single_succ_p (bb) + && single_succ (bb) == EXIT_BLOCK_PTR + && ! current_function_calls_eh_return))) + { + insn_info_t i_ptr = active_local_stores; + while (i_ptr) + { + store_info_t store_info = i_ptr->store_rec; + + /* Skip the clobbers. */ + while (!store_info->is_set) + store_info = store_info->next; + if (store_info->alias_set) + delete_dead_store_insn (i_ptr); + else + if (store_info->group_id >= 0) + { + group_info_t group + = VEC_index (group_info_t, rtx_group_vec, store_info->group_id); + if (group->frame_related) + delete_dead_store_insn (i_ptr); + } + + i_ptr = i_ptr->next_local_store; + } + } + + /* Get rid of the loads that were discovered in + replace_read. Cselib is finished with this block. */ + while (deferred_change_list) + { + deferred_change_t next = deferred_change_list->next; + + /* There is no reason to validate this change. That was + done earlier. */ + *deferred_change_list->loc = deferred_change_list->reg; + pool_free (deferred_change_pool, deferred_change_list); + deferred_change_list = next; + } + + /* Get rid of all of the cselib based store_infos in this + block and mark the containing insns as not being + deletable. */ + ptr = bb_info->last_insn; + while (ptr) + { + if (ptr->contains_cselib_groups) + free_store_info (ptr); + ptr = ptr->prev_insn; + } + + free_alloc_pool (cse_store_info_pool); + } + } + + cselib_finish (); + htab_empty (rtx_group_table); +} + + +/*---------------------------------------------------------------------------- + Second step. + + Assign each byte position in the stores that we are going to + analyze globally to a position in the bitmaps. Returns true if + there are any bit postions assigned. +----------------------------------------------------------------------------*/ + +static void +dse_step2_init (void) +{ + unsigned int i; + group_info_t group; + + for (i = 0; VEC_iterate (group_info_t, rtx_group_vec, i, group); i++) + { + /* For all non stack related bases, we only consider a store to + be deletable if there are two or more stores for that + position. This is because it takes one store to make the + other store redundant. However, for the stores that are + stack related, we consider them if there is only one store + for the position. We do this because the stack related + stores can be deleted if their is no read between them and + the end of the function. + + To make this work in the current framework, we take the stack + related bases add all of the bits from store1 into store2. + This has the effect of making the eligible even if there is + only one store. */ + + if (stores_off_frame_dead_at_return && group->frame_related) + { + bitmap_ior_into (group->store2_n, group->store1_n); + bitmap_ior_into (group->store2_p, group->store1_p); + if (dump_file) + fprintf (dump_file, "group %d is frame related ", i); + } + + group->offset_map_size_n++; + group->offset_map_n = XNEWVEC (int, group->offset_map_size_n); + group->offset_map_size_p++; + group->offset_map_p = XNEWVEC (int, group->offset_map_size_p); + group->process_globally = false; + if (dump_file) + { + fprintf (dump_file, "group %d(%d+%d): ", i, + (int)bitmap_count_bits (group->store2_n), + (int)bitmap_count_bits (group->store2_p)); + bitmap_print (dump_file, group->store2_n, "n ", " "); + bitmap_print (dump_file, group->store2_p, "p ", "\n"); + } + } +} + + +/* Init the offset tables for the normal case. */ + +static bool +dse_step2_nospill (void) +{ + unsigned int i; + group_info_t group; + /* Position 0 is unused because 0 is used in the maps to mean + unused. */ + current_position = 1; + + for (i = 0; VEC_iterate (group_info_t, rtx_group_vec, i, group); i++) + { + bitmap_iterator bi; + unsigned int j; + + if (group == clear_alias_group) + continue; + + memset (group->offset_map_n, 0, sizeof(int) * group->offset_map_size_n); + memset (group->offset_map_p, 0, sizeof(int) * group->offset_map_size_p); + bitmap_clear (group->group_kill); + + EXECUTE_IF_SET_IN_BITMAP (group->store2_n, 0, j, bi) + { + bitmap_set_bit (group->group_kill, current_position); + group->offset_map_n[j] = current_position++; + group->process_globally = true; + } + EXECUTE_IF_SET_IN_BITMAP (group->store2_p, 0, j, bi) + { + bitmap_set_bit (group->group_kill, current_position); + group->offset_map_p[j] = current_position++; + group->process_globally = true; + } + } + return current_position != 1; +} + + +/* Init the offset tables for the spill case. */ + +static bool +dse_step2_spill (void) +{ + unsigned int j; + group_info_t group = clear_alias_group; + bitmap_iterator bi; + + /* Position 0 is unused because 0 is used in the maps to mean + unused. */ + current_position = 1; + + if (dump_file) + { + bitmap_print (dump_file, clear_alias_sets, + "clear alias sets ", "\n"); + bitmap_print (dump_file, disqualified_clear_alias_sets, + "disqualified clear alias sets ", "\n"); + } + + memset (group->offset_map_n, 0, sizeof(int) * group->offset_map_size_n); + memset (group->offset_map_p, 0, sizeof(int) * group->offset_map_size_p); + bitmap_clear (group->group_kill); + + /* Remove the disqualified positions from the store2_p set. */ + bitmap_and_compl_into (group->store2_p, disqualified_clear_alias_sets); + + /* We do not need to process the store2_n set because + alias_sets are always positive. */ + EXECUTE_IF_SET_IN_BITMAP (group->store2_p, 0, j, bi) + { + bitmap_set_bit (group->group_kill, current_position); + group->offset_map_p[j] = current_position++; + group->process_globally = true; + } + + return current_position != 1; +} + + + +/*---------------------------------------------------------------------------- + Third step. + + Build the bit vectors for the transfer functions. +----------------------------------------------------------------------------*/ + + +/* Note that this is NOT a general purpose function. Any mem that has + an alias set registered here expected to be COMPLETELY unaliased: + i.e it's addresses are not and need not be examined. + + It is known that all references to this address will have this + alias set and there are NO other references to this address in the + function. + + Currently the only place that is known to be clean enough to use + this interface is the code that assigns the spill locations. + + All of the mems that have alias_sets registered are subjected to a + very powerful form of dse where function calls, volatile reads and + writes, and reads from random location are not taken into account. + + It is also assumed that these locations go dead when the function + returns. This assumption could be relaxed if there were found to + be places that this assumption was not correct. + + The MODE is passed in and saved. The mode of each load or store to + a mem with ALIAS_SET is checked against MEM. If the size of that + load or store is different from MODE, processing is halted on this + alias set. For the vast majority of aliases sets, all of the loads + and stores will use the same mode. But vectors are treated + differently: the alias set is established for the entire vector, + but reload will insert loads and stores for individual elements and + we do not necessarily have the information to track those separate + elements. So when we see a mode mismatch, we just bail. */ + + +void +dse_record_singleton_alias_set (HOST_WIDE_INT alias_set, + enum machine_mode mode) +{ + struct clear_alias_mode_holder tmp_holder; + struct clear_alias_mode_holder *entry; + void **slot; + + /* If we are not going to run dse, we need to return now or there + will be problems with allocating the bitmaps. */ + if ((!gate_dse()) || !alias_set) + return; + + if (!clear_alias_sets) + { + clear_alias_sets = BITMAP_ALLOC (NULL); + disqualified_clear_alias_sets = BITMAP_ALLOC (NULL); + clear_alias_mode_table = htab_create (11, clear_alias_mode_hash, + clear_alias_mode_eq, NULL); + clear_alias_mode_pool = create_alloc_pool ("clear_alias_mode_pool", + sizeof (struct clear_alias_mode_holder), 100); + } + + bitmap_set_bit (clear_alias_sets, alias_set); + + tmp_holder.alias_set = alias_set; + + slot = htab_find_slot (clear_alias_mode_table, &tmp_holder, INSERT); + gcc_assert (*slot == NULL); + + *slot = entry = pool_alloc (clear_alias_mode_pool); + entry->alias_set = alias_set; + entry->mode = mode; +} + + +/* Remove ALIAS_SET from the sets of stack slots being considered. */ + +void +dse_invalidate_singleton_alias_set (HOST_WIDE_INT alias_set) +{ + if ((!gate_dse()) || !alias_set) + return; + + bitmap_clear_bit (clear_alias_sets, alias_set); +} + + +/* Look up the bitmap index for OFFSET in GROUP_INFO. If it is not + there, return 0. */ + +static int +get_bitmap_index (group_info_t group_info, HOST_WIDE_INT offset) +{ + if (offset < 0) + { + HOST_WIDE_INT offset_p = -offset; + if (offset_p >= group_info->offset_map_size_n) + return 0; + return group_info->offset_map_n[offset_p]; + } + else + { + if (offset >= group_info->offset_map_size_p) + return 0; + return group_info->offset_map_p[offset]; + } +} + + +/* Process the STORE_INFOs into the bitmaps into GEN and KILL. KILL + may be NULL. */ + +static void +scan_stores_nospill (store_info_t store_info, bitmap gen, bitmap kill) +{ + while (store_info) + { + HOST_WIDE_INT i; + group_info_t group_info + = VEC_index (group_info_t, rtx_group_vec, store_info->group_id); + if (group_info->process_globally) + for (i = store_info->begin; i < store_info->end; i++) + { + int index = get_bitmap_index (group_info, i); + if (index != 0) + { + bitmap_set_bit (gen, index); + if (kill) + bitmap_clear_bit (kill, index); + } + } + store_info = store_info->next; + } +} + + +/* Process the STORE_INFOs into the bitmaps into GEN and KILL. KILL + may be NULL. */ + +static void +scan_stores_spill (store_info_t store_info, bitmap gen, bitmap kill) +{ + while (store_info) + { + if (store_info->alias_set) + { + int index = get_bitmap_index (clear_alias_group, + store_info->alias_set); + if (index != 0) + { + bitmap_set_bit (gen, index); + if (kill) + bitmap_clear_bit (kill, index); + } + } + store_info = store_info->next; + } +} + + +/* Process the READ_INFOs into the bitmaps into GEN and KILL. KILL + may be NULL. */ + +static void +scan_reads_nospill (insn_info_t insn_info, bitmap gen, bitmap kill) +{ + read_info_t read_info = insn_info->read_rec; + int i; + group_info_t group; + + /* For const function calls kill the stack related stores. */ + if (insn_info->stack_read) + { + for (i = 0; VEC_iterate (group_info_t, rtx_group_vec, i, group); i++) + if (group->process_globally && group->frame_related) + { + if (kill) + bitmap_ior_into (kill, group->group_kill); + bitmap_and_compl_into (gen, group->group_kill); + } + } + + while (read_info) + { + for (i = 0; VEC_iterate (group_info_t, rtx_group_vec, i, group); i++) + { + if (group->process_globally) + { + if (i == read_info->group_id) + { + if (read_info->begin > read_info->end) + { + /* Begin > end for block mode reads. */ + if (kill) + bitmap_ior_into (kill, group->group_kill); + bitmap_and_compl_into (gen, group->group_kill); + } + else + { + /* The groups are the same, just process the + offsets. */ + HOST_WIDE_INT j; + for (j = read_info->begin; j < read_info->end; j++) + { + int index = get_bitmap_index (group, j); + if (index != 0) + { + if (kill) + bitmap_set_bit (kill, index); + bitmap_clear_bit (gen, index); + } + } + } + } + else + { + /* The groups are different, if the alias sets + conflict, clear the entire group. We only need + to apply this test if the read_info is a cselib + read. Anything with a constant base cannot alias + something else with a different constant + base. */ + if ((read_info->group_id < 0) + && canon_true_dependence (group->base_mem, + QImode, + group->canon_base_mem, + read_info->mem, rtx_varies_p)) + { + if (kill) + bitmap_ior_into (kill, group->group_kill); + bitmap_and_compl_into (gen, group->group_kill); + } + } + } + } + + read_info = read_info->next; + } +} + +/* Process the READ_INFOs into the bitmaps into GEN and KILL. KILL + may be NULL. */ + +static void +scan_reads_spill (read_info_t read_info, bitmap gen, bitmap kill) +{ + while (read_info) + { + if (read_info->alias_set) + { + int index = get_bitmap_index (clear_alias_group, + read_info->alias_set); + if (index != 0) + { + if (kill) + bitmap_set_bit (kill, index); + bitmap_clear_bit (gen, index); + } + } + + read_info = read_info->next; + } +} + + +/* Return the insn in BB_INFO before the first wild read or if there + are no wild reads in the block, return the last insn. */ + +static insn_info_t +find_insn_before_first_wild_read (bb_info_t bb_info) +{ + insn_info_t insn_info = bb_info->last_insn; + insn_info_t last_wild_read = NULL; + + while (insn_info) + { + if (insn_info->wild_read) + { + last_wild_read = insn_info->prev_insn; + /* Block starts with wild read. */ + if (!last_wild_read) + return NULL; + } + + insn_info = insn_info->prev_insn; + } + + if (last_wild_read) + return last_wild_read; + else + return bb_info->last_insn; +} + + +/* Scan the insns in BB_INFO starting at PTR and going to the top of + the block in order to build the gen and kill sets for the block. + We start at ptr which may be the last insn in the block or may be + the first insn with a wild read. In the latter case we are able to + skip the rest of the block because it just does not matter: + anything that happens is hidden by the wild read. */ + +static void +dse_step3_scan (bool for_spills, basic_block bb) +{ + bb_info_t bb_info = bb_table[bb->index]; + insn_info_t insn_info; + + if (for_spills) + /* There are no wild reads in the spill case. */ + insn_info = bb_info->last_insn; + else + insn_info = find_insn_before_first_wild_read (bb_info); + + /* In the spill case or in the no_spill case if there is no wild + read in the block, we will need a kill set. */ + if (insn_info == bb_info->last_insn) + { + if (bb_info->kill) + bitmap_clear (bb_info->kill); + else + bb_info->kill = BITMAP_ALLOC (NULL); + } + else + if (bb_info->kill) + BITMAP_FREE (bb_info->kill); + + while (insn_info) + { + /* There may have been code deleted by the dce pass run before + this phase. */ + if (insn_info->insn && INSN_P (insn_info->insn)) + { + /* Process the read(s) last. */ + if (for_spills) + { + scan_stores_spill (insn_info->store_rec, bb_info->gen, bb_info->kill); + scan_reads_spill (insn_info->read_rec, bb_info->gen, bb_info->kill); + } + else + { + scan_stores_nospill (insn_info->store_rec, bb_info->gen, bb_info->kill); + scan_reads_nospill (insn_info, bb_info->gen, bb_info->kill); + } + } + + insn_info = insn_info->prev_insn; + } +} + + +/* Set the gen set of the exit block, and also any block with no + successors that does not have a wild read. */ + +static void +dse_step3_exit_block_scan (bb_info_t bb_info) +{ + /* The gen set is all 0's for the exit block except for the + frame_pointer_group. */ + + if (stores_off_frame_dead_at_return) + { + unsigned int i; + group_info_t group; + + for (i = 0; VEC_iterate (group_info_t, rtx_group_vec, i, group); i++) + { + if (group->process_globally && group->frame_related) + bitmap_ior_into (bb_info->gen, group->group_kill); + } + } +} + + +/* Find all of the blocks that are not backwards reachable from the + exit block or any block with no successors (BB). These are the + infinite loops or infinite self loops. These blocks will still + have their bits set in UNREACHABLE_BLOCKS. */ + +static void +mark_reachable_blocks (sbitmap unreachable_blocks, basic_block bb) +{ + edge e; + edge_iterator ei; + + if (TEST_BIT (unreachable_blocks, bb->index)) + { + RESET_BIT (unreachable_blocks, bb->index); + FOR_EACH_EDGE (e, ei, bb->preds) + { + mark_reachable_blocks (unreachable_blocks, e->src); + } + } +} + +/* Build the transfer functions for the function. */ + +static void +dse_step3 (bool for_spills) +{ + basic_block bb; + sbitmap unreachable_blocks = sbitmap_alloc (last_basic_block); + sbitmap_iterator sbi; + bitmap all_ones = NULL; + unsigned int i; + + sbitmap_ones (unreachable_blocks); + + FOR_ALL_BB (bb) + { + bb_info_t bb_info = bb_table[bb->index]; + if (bb_info->gen) + bitmap_clear (bb_info->gen); + else + bb_info->gen = BITMAP_ALLOC (NULL); + + if (bb->index == ENTRY_BLOCK) + ; + else if (bb->index == EXIT_BLOCK) + dse_step3_exit_block_scan (bb_info); + else + dse_step3_scan (for_spills, bb); + if (EDGE_COUNT (bb->succs) == 0) + mark_reachable_blocks (unreachable_blocks, bb); + + /* If this is the second time dataflow is run, delete the old + sets. */ + if (bb_info->in) + BITMAP_FREE (bb_info->in); + if (bb_info->out) + BITMAP_FREE (bb_info->out); + } + + /* For any block in an infinite loop, we must initialize the out set + to all ones. This could be expensive, but almost never occurs in + practice. However, it is common in regression tests. */ + EXECUTE_IF_SET_IN_SBITMAP (unreachable_blocks, 0, i, sbi) + { + if (bitmap_bit_p (all_blocks, i)) + { + bb_info_t bb_info = bb_table[i]; + if (!all_ones) + { + unsigned int j; + group_info_t group; + + all_ones = BITMAP_ALLOC (NULL); + for (j = 0; VEC_iterate (group_info_t, rtx_group_vec, j, group); j++) + bitmap_ior_into (all_ones, group->group_kill); + } + if (!bb_info->out) + { + bb_info->out = BITMAP_ALLOC (NULL); + bitmap_copy (bb_info->out, all_ones); + } + } + } + + if (all_ones) + BITMAP_FREE (all_ones); + sbitmap_free (unreachable_blocks); +} + + + +/*---------------------------------------------------------------------------- + Fourth step. + + Solve the bitvector equations. +----------------------------------------------------------------------------*/ + + +/* Confluence function for blocks with no successors. Create an out + set from the gen set of the exit block. This block logically has + the exit block as a successor. */ + + + +static void +dse_confluence_0 (basic_block bb) +{ + bb_info_t bb_info = bb_table[bb->index]; + + if (bb->index == EXIT_BLOCK) + return; + + if (!bb_info->out) + { + bb_info->out = BITMAP_ALLOC (NULL); + bitmap_copy (bb_info->out, bb_table[EXIT_BLOCK]->gen); + } +} + +/* Propagate the information from the in set of the dest of E to the + out set of the src of E. If the various in or out sets are not + there, that means they are all ones. */ + +static void +dse_confluence_n (edge e) +{ + bb_info_t src_info = bb_table[e->src->index]; + bb_info_t dest_info = bb_table[e->dest->index]; + + if (dest_info->in) + { + if (src_info->out) + bitmap_and_into (src_info->out, dest_info->in); + else + { + src_info->out = BITMAP_ALLOC (NULL); + bitmap_copy (src_info->out, dest_info->in); + } + } +} + + +/* Propagate the info from the out to the in set of BB_INDEX's basic + block. There are three cases: + + 1) The block has no kill set. In this case the kill set is all + ones. It does not matter what the out set of the block is, none of + the info can reach the top. The only thing that reaches the top is + the gen set and we just copy the set. + + 2) There is a kill set but no out set and bb has successors. In + this case we just return. Eventually an out set will be created and + it is better to wait than to create a set of ones. + + 3) There is both a kill and out set. We apply the obvious transfer + function. +*/ + +static bool +dse_transfer_function (int bb_index) +{ + bb_info_t bb_info = bb_table[bb_index]; + + if (bb_info->kill) + { + if (bb_info->out) + { + /* Case 3 above. */ + if (bb_info->in) + return bitmap_ior_and_compl (bb_info->in, bb_info->gen, + bb_info->out, bb_info->kill); + else + { + bb_info->in = BITMAP_ALLOC (NULL); + bitmap_ior_and_compl (bb_info->in, bb_info->gen, + bb_info->out, bb_info->kill); + return true; + } + } + else + /* Case 2 above. */ + return false; + } + else + { + /* Case 1 above. If there is already an in set, nothing + happens. */ + if (bb_info->in) + return false; + else + { + bb_info->in = BITMAP_ALLOC (NULL); + bitmap_copy (bb_info->in, bb_info->gen); + return true; + } + } +} + +/* Solve the dataflow equations. */ + +static void +dse_step4 (void) +{ + df_simple_dataflow (DF_BACKWARD, NULL, dse_confluence_0, + dse_confluence_n, dse_transfer_function, + all_blocks, df_get_postorder (DF_BACKWARD), + df_get_n_blocks (DF_BACKWARD)); + if (dump_file) + { + basic_block bb; + + fprintf (dump_file, "\n\n*** Global dataflow info after analysis.\n"); + FOR_ALL_BB (bb) + { + bb_info_t bb_info = bb_table[bb->index]; + + df_print_bb_index (bb, dump_file); + if (bb_info->in) + bitmap_print (dump_file, bb_info->in, " in: ", "\n"); + else + fprintf (dump_file, " in: *MISSING*\n"); + if (bb_info->gen) + bitmap_print (dump_file, bb_info->gen, " gen: ", "\n"); + else + fprintf (dump_file, " gen: *MISSING*\n"); + if (bb_info->kill) + bitmap_print (dump_file, bb_info->kill, " kill: ", "\n"); + else + fprintf (dump_file, " kill: *MISSING*\n"); + if (bb_info->out) + bitmap_print (dump_file, bb_info->out, " out: ", "\n"); + else + fprintf (dump_file, " out: *MISSING*\n\n"); + } + } +} + + + +/*---------------------------------------------------------------------------- + Fifth step. + + Delete the stores that can only be deleted using the global informantion. +----------------------------------------------------------------------------*/ + + +static void +dse_step5_nospill (void) +{ + basic_block bb; + FOR_EACH_BB (bb) + { + bb_info_t bb_info = bb_table[bb->index]; + insn_info_t insn_info = bb_info->last_insn; + bitmap v = bb_info->out; + + while (insn_info) + { + bool deleted = false; + if (dump_file && insn_info->insn) + { + fprintf (dump_file, "starting to process insn %d\n", + INSN_UID (insn_info->insn)); + bitmap_print (dump_file, v, " v: ", "\n"); + } + + /* There may have been code deleted by the dce pass run before + this phase. */ + if (insn_info->insn + && INSN_P (insn_info->insn) + && (!insn_info->cannot_delete) + && (!bitmap_empty_p (v))) + { + store_info_t store_info = insn_info->store_rec; + + /* Try to delete the current insn. */ + deleted = true; + + /* Skip the clobbers. */ + while (!store_info->is_set) + store_info = store_info->next; + + if (store_info->alias_set) + deleted = false; + else + { + HOST_WIDE_INT i; + group_info_t group_info + = VEC_index (group_info_t, rtx_group_vec, store_info->group_id); + + for (i = store_info->begin; i < store_info->end; i++) + { + int index = get_bitmap_index (group_info, i); + + if (dump_file) + fprintf (dump_file, "i = %d, index = %d\n", (int)i, index); + if (index == 0 || !bitmap_bit_p (v, index)) + { + if (dump_file) + fprintf (dump_file, "failing at i = %d\n", (int)i); + deleted = false; + break; + } + } + } + if (deleted) + { + if (dbg_cnt (dse)) + { + check_for_inc_dec (insn_info->insn); + delete_insn (insn_info->insn); + insn_info->insn = NULL; + globally_deleted++; + } + } + } + /* We do want to process the local info if the insn was + deleted. For insntance, if the insn did a wild read, we + no longer need to trash the info. */ + if (insn_info->insn + && INSN_P (insn_info->insn) + && (!deleted)) + { + scan_stores_nospill (insn_info->store_rec, v, NULL); + if (insn_info->wild_read) + { + if (dump_file) + fprintf (dump_file, "wild read\n"); + bitmap_clear (v); + } + else if (insn_info->read_rec) + { + if (dump_file) + fprintf (dump_file, "regular read\n"); + scan_reads_nospill (insn_info, v, NULL); + } + } + + insn_info = insn_info->prev_insn; + } + } +} + + +static void +dse_step5_spill (void) +{ + basic_block bb; + FOR_EACH_BB (bb) + { + bb_info_t bb_info = bb_table[bb->index]; + insn_info_t insn_info = bb_info->last_insn; + bitmap v = bb_info->out; + + while (insn_info) + { + bool deleted = false; + /* There may have been code deleted by the dce pass run before + this phase. */ + if (insn_info->insn + && INSN_P (insn_info->insn) + && (!insn_info->cannot_delete) + && (!bitmap_empty_p (v))) + { + /* Try to delete the current insn. */ + store_info_t store_info = insn_info->store_rec; + deleted = true; + + while (store_info) + { + if (store_info->alias_set) + { + int index = get_bitmap_index (clear_alias_group, + store_info->alias_set); + if (index == 0 || !bitmap_bit_p (v, index)) + { + deleted = false; + break; + } + } + else + deleted = false; + store_info = store_info->next; + } + if (deleted && dbg_cnt (dse)) + { + if (dump_file) + fprintf (dump_file, "Spill deleting insn %d\n", + INSN_UID (insn_info->insn)); + check_for_inc_dec (insn_info->insn); + delete_insn (insn_info->insn); + spill_deleted++; + insn_info->insn = NULL; + } + } + + if (insn_info->insn + && INSN_P (insn_info->insn) + && (!deleted)) + { + scan_stores_spill (insn_info->store_rec, v, NULL); + scan_reads_spill (insn_info->read_rec, v, NULL); + } + + insn_info = insn_info->prev_insn; + } + } +} + + + +/*---------------------------------------------------------------------------- + Sixth step. + + Destroy everything left standing. +----------------------------------------------------------------------------*/ + +static void +dse_step6 (bool global_done) +{ + unsigned int i; + group_info_t group; + basic_block bb; + + if (global_done) + { + for (i = 0; VEC_iterate (group_info_t, rtx_group_vec, i, group); i++) + { + free (group->offset_map_n); + free (group->offset_map_p); + BITMAP_FREE (group->store1_n); + BITMAP_FREE (group->store1_p); + BITMAP_FREE (group->store2_n); + BITMAP_FREE (group->store2_p); + BITMAP_FREE (group->group_kill); + } + + FOR_ALL_BB (bb) + { + bb_info_t bb_info = bb_table[bb->index]; + BITMAP_FREE (bb_info->gen); + if (bb_info->kill) + BITMAP_FREE (bb_info->kill); + if (bb_info->in) + BITMAP_FREE (bb_info->in); + if (bb_info->out) + BITMAP_FREE (bb_info->out); + } + } + else + { + for (i = 0; VEC_iterate (group_info_t, rtx_group_vec, i, group); i++) + { + BITMAP_FREE (group->store1_n); + BITMAP_FREE (group->store1_p); + BITMAP_FREE (group->store2_n); + BITMAP_FREE (group->store2_p); + BITMAP_FREE (group->group_kill); + } + } + + if (clear_alias_sets) + { + BITMAP_FREE (clear_alias_sets); + BITMAP_FREE (disqualified_clear_alias_sets); + free_alloc_pool (clear_alias_mode_pool); + htab_delete (clear_alias_mode_table); + } + + end_alias_analysis (); + free (bb_table); + htab_delete (rtx_group_table); + VEC_free (group_info_t, heap, rtx_group_vec); + BITMAP_FREE (all_blocks); + BITMAP_FREE (scratch); + + free_alloc_pool (rtx_store_info_pool); + free_alloc_pool (read_info_pool); + free_alloc_pool (insn_info_pool); + free_alloc_pool (bb_info_pool); + free_alloc_pool (rtx_group_info_pool); + free_alloc_pool (deferred_change_pool); +} + + + +/* ------------------------------------------------------------------------- + DSE + ------------------------------------------------------------------------- */ + +/* Callback for running pass_rtl_dse. */ + +static unsigned int +rest_of_handle_dse (void) +{ + bool did_global = false; + + df_set_flags (DF_DEFER_INSN_RESCAN); + + dse_step0 (); + dse_step1 (); + dse_step2_init (); + if (dse_step2_nospill ()) + { + df_set_flags (DF_LR_RUN_DCE); + df_analyze (); + did_global = true; + if (dump_file) + fprintf (dump_file, "doing global processing\n"); + dse_step3 (false); + dse_step4 (); + dse_step5_nospill (); + } + + /* For the instance of dse that runs after reload, we make a special + pass to process the spills. These are special in that they are + totally transparent, i.e, there is no aliasing issues that need + to be considered. This means that the wild reads that kill + everything else do not apply here. */ + if (clear_alias_sets && dse_step2_spill ()) + { + if (!did_global) + { + df_set_flags (DF_LR_RUN_DCE); + df_analyze (); + } + did_global = true; + if (dump_file) + fprintf (dump_file, "doing global spill processing\n"); + dse_step3 (true); + dse_step4 (); + dse_step5_spill (); + } + + dse_step6 (did_global); + + if (dump_file) + fprintf (dump_file, "dse: local deletions = %d, global deletions = %d, spill deletions = %d\n", + locally_deleted, globally_deleted, spill_deleted); + return 0; +} + +static bool +gate_dse (void) +{ + return optimize > 0 && flag_dse; +} + +struct tree_opt_pass pass_rtl_dse1 = +{ + "dse1", /* name */ + gate_dse, /* gate */ + rest_of_handle_dse, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_DSE1, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func | + TODO_df_finish | + TODO_ggc_collect, /* todo_flags_finish */ + 'w' /* letter */ +}; + +struct tree_opt_pass pass_rtl_dse2 = +{ + "dse2", /* name */ + gate_dse, /* gate */ + rest_of_handle_dse, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_DSE2, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func | + TODO_df_finish | + TODO_ggc_collect, /* todo_flags_finish */ + 'w' /* letter */ +}; + diff --git a/gcc/dse.h b/gcc/dse.h new file mode 100644 index 0000000..1a845e3 --- /dev/null +++ b/gcc/dse.h @@ -0,0 +1,33 @@ +/* RTL dead store elimination. + Copyright (C) 2007 Free Software Foundation, Inc. + + Contributed by Richard Sandiford <rsandifor@codesourcery.com> + and Kenneth Zadeck <zadeck@naturalbridge.com> + +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, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ + +#ifndef GCC_DSE_H +#define GCC_DSE_H + +struct df; + +extern void dse_record_singleton_alias_set (HOST_WIDE_INT, enum machine_mode); +extern void dse_invalidate_singleton_alias_set (HOST_WIDE_INT); + +#endif /* GCC_DSE_H */ + diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c index f5cea9a..2f9ddbf 100644 --- a/gcc/emit-rtl.c +++ b/gcc/emit-rtl.c @@ -57,6 +57,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "debug.h" #include "langhooks.h" #include "tree-pass.h" +#include "df.h" /* Commonly used modes. */ @@ -358,6 +359,21 @@ get_reg_attrs (tree decl, int offset) return *slot; } + +#if !HAVE_blockage +/* Generate an empty ASM_INPUT, which is used to block attempts to schedule + across this insn. */ + +rtx +gen_blockage (void) +{ + rtx x = gen_rtx_ASM_INPUT (VOIDmode, ""); + MEM_VOLATILE_P (x) = true; + return x; +} +#endif + + /* Generate a new REG rtx. Make sure ORIGINAL_REGNO is set properly, and don't attempt to share with the various global pieces of rtl (such as frame_pointer_rtx). */ @@ -2144,7 +2160,6 @@ unshare_all_rtl_again (rtx insn) { reset_used_flags (PATTERN (p)); reset_used_flags (REG_NOTES (p)); - reset_used_flags (LOG_LINKS (p)); } /* Make sure that virtual stack slots are not shared. */ @@ -2222,11 +2237,7 @@ verify_rtx_sharing (rtx orig, rtx insn) break; case CONST: - /* CONST can be shared if it contains a SYMBOL_REF. If it contains - a LABEL_REF, it isn't sharable. */ - if (GET_CODE (XEXP (x, 0)) == PLUS - && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT) + if (shared_const_p (orig)) return; break; @@ -2307,7 +2318,6 @@ verify_rtl_sharing (void) { reset_used_flags (PATTERN (p)); reset_used_flags (REG_NOTES (p)); - reset_used_flags (LOG_LINKS (p)); if (GET_CODE (PATTERN (p)) == SEQUENCE) { int i; @@ -2319,7 +2329,6 @@ verify_rtl_sharing (void) gcc_assert (INSN_P (q)); reset_used_flags (PATTERN (q)); reset_used_flags (REG_NOTES (q)); - reset_used_flags (LOG_LINKS (q)); } } } @@ -2329,7 +2338,6 @@ verify_rtl_sharing (void) { verify_rtx_sharing (PATTERN (p), p); verify_rtx_sharing (REG_NOTES (p), p); - verify_rtx_sharing (LOG_LINKS (p), p); } } @@ -2344,7 +2352,6 @@ unshare_all_rtl_in_chain (rtx insn) { PATTERN (insn) = copy_rtx_if_shared (PATTERN (insn)); REG_NOTES (insn) = copy_rtx_if_shared (REG_NOTES (insn)); - LOG_LINKS (insn) = copy_rtx_if_shared (LOG_LINKS (insn)); } } @@ -2421,11 +2428,7 @@ repeat: break; case CONST: - /* CONST can be shared if it contains a SYMBOL_REF. If it contains - a LABEL_REF, it isn't sharable. */ - if (GET_CODE (XEXP (x, 0)) == PLUS - && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT) + if (shared_const_p (x)) return; break; @@ -3160,6 +3163,10 @@ try_split (rtx pat, rtx trial, int last) insn_last = NEXT_INSN (insn_last); } + /* We will be adding the new sequence to the function. The splitters + may have introduced invalid RTL sharing, so unshare the sequence now. */ + unshare_all_rtl_in_chain (seq); + /* Mark labels. */ for (insn = insn_last; insn ; insn = PREV_INSN (insn)) { @@ -3307,7 +3314,6 @@ make_insn_raw (rtx pattern) INSN_UID (insn) = cur_insn_uid++; PATTERN (insn) = pattern; INSN_CODE (insn) = -1; - LOG_LINKS (insn) = NULL; REG_NOTES (insn) = NULL; INSN_LOCATOR (insn) = curr_insn_locator (); BLOCK_FOR_INSN (insn) = NULL; @@ -3339,7 +3345,6 @@ make_jump_insn_raw (rtx pattern) PATTERN (insn) = pattern; INSN_CODE (insn) = -1; - LOG_LINKS (insn) = NULL; REG_NOTES (insn) = NULL; JUMP_LABEL (insn) = NULL; INSN_LOCATOR (insn) = curr_insn_locator (); @@ -3360,7 +3365,6 @@ make_call_insn_raw (rtx pattern) PATTERN (insn) = pattern; INSN_CODE (insn) = -1; - LOG_LINKS (insn) = NULL; REG_NOTES (insn) = NULL; CALL_INSN_FUNCTION_USAGE (insn) = NULL; INSN_LOCATOR (insn) = curr_insn_locator (); @@ -3393,10 +3397,9 @@ add_insn (rtx insn) SEQUENCE. */ void -add_insn_after (rtx insn, rtx after) +add_insn_after (rtx insn, rtx after, basic_block bb) { rtx next = NEXT_INSN (after); - basic_block bb; gcc_assert (!optimize || !INSN_DELETED_P (after)); @@ -3431,7 +3434,7 @@ add_insn_after (rtx insn, rtx after) { set_block_for_insn (insn, bb); if (INSN_P (insn)) - bb->flags |= BB_DIRTY; + df_insn_rescan (insn); /* Should not happen as first in the BB is always either NOTE or LABEL. */ if (BB_END (bb) == after @@ -3450,15 +3453,15 @@ add_insn_after (rtx insn, rtx after) } /* Add INSN into the doubly-linked list before insn BEFORE. This and - the previous should be the only functions called to insert an insn once - delay slots have been filled since only they know how to update a - SEQUENCE. */ + the previous should be the only functions called to insert an insn + once delay slots have been filled since only they know how to + update a SEQUENCE. If BB is NULL, an attempt is made to infer the + bb from before. */ void -add_insn_before (rtx insn, rtx before) +add_insn_before (rtx insn, rtx before, basic_block bb) { rtx prev = PREV_INSN (before); - basic_block bb; gcc_assert (!optimize || !INSN_DELETED_P (before)); @@ -3490,13 +3493,16 @@ add_insn_before (rtx insn, rtx before) gcc_assert (stack); } - if (!BARRIER_P (before) - && !BARRIER_P (insn) - && (bb = BLOCK_FOR_INSN (before))) + if (!bb + && !BARRIER_P (before) + && !BARRIER_P (insn)) + bb = BLOCK_FOR_INSN (before); + + if (bb) { set_block_for_insn (insn, bb); if (INSN_P (insn)) - bb->flags |= BB_DIRTY; + df_insn_rescan (insn); /* Should not happen as first in the BB is always either NOTE or LABEL. */ gcc_assert (BB_HEAD (bb) != insn @@ -3510,6 +3516,17 @@ add_insn_before (rtx insn, rtx before) PREV_INSN (XVECEXP (PATTERN (before), 0, 0)) = insn; } + +/* Replace insn with an deleted instruction note. */ + +void set_insn_deleted (rtx insn) +{ + df_insn_delete (BLOCK_FOR_INSN (insn), INSN_UID (insn)); + PUT_CODE (insn, NOTE); + NOTE_KIND (insn) = NOTE_INSN_DELETED; +} + + /* Remove an insn from its doubly-linked list. This function knows how to handle sequences. */ void @@ -3519,6 +3536,9 @@ remove_insn (rtx insn) rtx prev = PREV_INSN (insn); basic_block bb; + /* Later in the code, the block will be marked dirty. */ + df_insn_delete (NULL, INSN_UID (insn)); + if (prev) { NEXT_INSN (prev) = next; @@ -3569,7 +3589,7 @@ remove_insn (rtx insn) && (bb = BLOCK_FOR_INSN (insn))) { if (INSN_P (insn)) - bb->flags |= BB_DIRTY; + df_set_bb_dirty (bb); if (BB_HEAD (bb) == insn) { /* Never ever delete the basic block note without deleting whole @@ -3665,14 +3685,14 @@ reorder_insns (rtx from, rtx to, rtx after) && (bb = BLOCK_FOR_INSN (after))) { rtx x; - bb->flags |= BB_DIRTY; + df_set_bb_dirty (bb); if (!BARRIER_P (from) && (bb2 = BLOCK_FOR_INSN (from))) { if (BB_END (bb2) == to) BB_END (bb2) = prev; - bb2->flags |= BB_DIRTY; + df_set_bb_dirty (bb2); } if (BB_END (bb) == after) @@ -3680,7 +3700,10 @@ reorder_insns (rtx from, rtx to, rtx after) for (x = from; x != NEXT_INSN (to); x = NEXT_INSN (x)) if (!BARRIER_P (x)) - set_block_for_insn (x, bb); + { + set_block_for_insn (x, bb); + df_insn_change_bb (x); + } } } @@ -3713,7 +3736,7 @@ reorder_insns (rtx from, rtx to, rtx after) /* Make X be output before the instruction BEFORE. */ rtx -emit_insn_before_noloc (rtx x, rtx before) +emit_insn_before_noloc (rtx x, rtx before, basic_block bb) { rtx last = before; rtx insn; @@ -3735,7 +3758,7 @@ emit_insn_before_noloc (rtx x, rtx before) while (insn) { rtx next = NEXT_INSN (insn); - add_insn_before (insn, before); + add_insn_before (insn, before, bb); last = insn; insn = next; } @@ -3749,7 +3772,7 @@ emit_insn_before_noloc (rtx x, rtx before) default: last = make_insn_raw (x); - add_insn_before (last, before); + add_insn_before (last, before, bb); break; } @@ -3778,7 +3801,7 @@ emit_jump_insn_before_noloc (rtx x, rtx before) while (insn) { rtx next = NEXT_INSN (insn); - add_insn_before (insn, before); + add_insn_before (insn, before, NULL); last = insn; insn = next; } @@ -3792,7 +3815,7 @@ emit_jump_insn_before_noloc (rtx x, rtx before) default: last = make_jump_insn_raw (x); - add_insn_before (last, before); + add_insn_before (last, before, NULL); break; } @@ -3821,7 +3844,7 @@ emit_call_insn_before_noloc (rtx x, rtx before) while (insn) { rtx next = NEXT_INSN (insn); - add_insn_before (insn, before); + add_insn_before (insn, before, NULL); last = insn; insn = next; } @@ -3835,7 +3858,7 @@ emit_call_insn_before_noloc (rtx x, rtx before) default: last = make_call_insn_raw (x); - add_insn_before (last, before); + add_insn_before (last, before, NULL); break; } @@ -3852,7 +3875,7 @@ emit_barrier_before (rtx before) INSN_UID (insn) = cur_insn_uid++; - add_insn_before (insn, before); + add_insn_before (insn, before, NULL); return insn; } @@ -3866,7 +3889,7 @@ emit_label_before (rtx label, rtx before) if (INSN_UID (label) == 0) { INSN_UID (label) = cur_insn_uid++; - add_insn_before (label, before); + add_insn_before (label, before, NULL); } return label; @@ -3883,31 +3906,35 @@ emit_note_before (enum insn_note subtype, rtx before) BLOCK_FOR_INSN (note) = NULL; memset (&NOTE_DATA (note), 0, sizeof (NOTE_DATA (note))); - add_insn_before (note, before); + add_insn_before (note, before, NULL); return note; } /* Helper for emit_insn_after, handles lists of instructions efficiently. */ -static rtx emit_insn_after_1 (rtx, rtx); - static rtx -emit_insn_after_1 (rtx first, rtx after) +emit_insn_after_1 (rtx first, rtx after, basic_block bb) { rtx last; rtx after_after; - basic_block bb; + if (!bb && !BARRIER_P (after)) + bb = BLOCK_FOR_INSN (after); - if (!BARRIER_P (after) - && (bb = BLOCK_FOR_INSN (after))) + if (bb) { - bb->flags |= BB_DIRTY; + df_set_bb_dirty (bb); for (last = first; NEXT_INSN (last); last = NEXT_INSN (last)) if (!BARRIER_P (last)) - set_block_for_insn (last, bb); + { + set_block_for_insn (last, bb); + df_insn_rescan (last); + } if (!BARRIER_P (last)) - set_block_for_insn (last, bb); + { + set_block_for_insn (last, bb); + df_insn_rescan (last); + } if (BB_END (bb) == after) BB_END (bb) = last; } @@ -3928,10 +3955,11 @@ emit_insn_after_1 (rtx first, rtx after) return last; } -/* Make X be output after the insn AFTER. */ +/* Make X be output after the insn AFTER and set the BB of insn. If + BB is NULL, an attempt is made to infer the BB from AFTER. */ rtx -emit_insn_after_noloc (rtx x, rtx after) +emit_insn_after_noloc (rtx x, rtx after, basic_block bb) { rtx last = after; @@ -3948,7 +3976,7 @@ emit_insn_after_noloc (rtx x, rtx after) case CODE_LABEL: case BARRIER: case NOTE: - last = emit_insn_after_1 (x, after); + last = emit_insn_after_1 (x, after, bb); break; #ifdef ENABLE_RTL_CHECKING @@ -3959,7 +3987,7 @@ emit_insn_after_noloc (rtx x, rtx after) default: last = make_insn_raw (x); - add_insn_after (last, after); + add_insn_after (last, after, bb); break; } @@ -3985,7 +4013,7 @@ emit_jump_insn_after_noloc (rtx x, rtx after) case CODE_LABEL: case BARRIER: case NOTE: - last = emit_insn_after_1 (x, after); + last = emit_insn_after_1 (x, after, NULL); break; #ifdef ENABLE_RTL_CHECKING @@ -3996,7 +4024,7 @@ emit_jump_insn_after_noloc (rtx x, rtx after) default: last = make_jump_insn_raw (x); - add_insn_after (last, after); + add_insn_after (last, after, NULL); break; } @@ -4021,7 +4049,7 @@ emit_call_insn_after_noloc (rtx x, rtx after) case CODE_LABEL: case BARRIER: case NOTE: - last = emit_insn_after_1 (x, after); + last = emit_insn_after_1 (x, after, NULL); break; #ifdef ENABLE_RTL_CHECKING @@ -4032,7 +4060,7 @@ emit_call_insn_after_noloc (rtx x, rtx after) default: last = make_call_insn_raw (x); - add_insn_after (last, after); + add_insn_after (last, after, NULL); break; } @@ -4049,7 +4077,7 @@ emit_barrier_after (rtx after) INSN_UID (insn) = cur_insn_uid++; - add_insn_after (insn, after); + add_insn_after (insn, after, NULL); return insn; } @@ -4064,7 +4092,7 @@ emit_label_after (rtx label, rtx after) if (INSN_UID (label) == 0) { INSN_UID (label) = cur_insn_uid++; - add_insn_after (label, after); + add_insn_after (label, after, NULL); } return label; @@ -4080,7 +4108,7 @@ emit_note_after (enum insn_note subtype, rtx after) NOTE_KIND (note) = subtype; BLOCK_FOR_INSN (note) = NULL; memset (&NOTE_DATA (note), 0, sizeof (NOTE_DATA (note))); - add_insn_after (note, after); + add_insn_after (note, after, NULL); return note; } @@ -4088,7 +4116,7 @@ emit_note_after (enum insn_note subtype, rtx after) rtx emit_insn_after_setloc (rtx pattern, rtx after, int loc) { - rtx last = emit_insn_after_noloc (pattern, after); + rtx last = emit_insn_after_noloc (pattern, after, NULL); if (pattern == NULL_RTX || !loc) return last; @@ -4112,7 +4140,7 @@ emit_insn_after (rtx pattern, rtx after) if (INSN_P (after)) return emit_insn_after_setloc (pattern, after, INSN_LOCATOR (after)); else - return emit_insn_after_noloc (pattern, after); + return emit_insn_after_noloc (pattern, after, NULL); } /* Like emit_jump_insn_after_noloc, but set INSN_LOCATOR according to SCOPE. */ @@ -4182,7 +4210,7 @@ rtx emit_insn_before_setloc (rtx pattern, rtx before, int loc) { rtx first = PREV_INSN (before); - rtx last = emit_insn_before_noloc (pattern, before); + rtx last = emit_insn_before_noloc (pattern, before, NULL); if (pattern == NULL_RTX || !loc) return last; @@ -4209,7 +4237,7 @@ emit_insn_before (rtx pattern, rtx before) if (INSN_P (before)) return emit_insn_before_setloc (pattern, before, INSN_LOCATOR (before)); else - return emit_insn_before_noloc (pattern, before); + return emit_insn_before_noloc (pattern, before, NULL); } /* like emit_insn_before_noloc, but set insn_locator according to scope. */ @@ -4482,6 +4510,7 @@ rtx set_unique_reg_note (rtx insn, enum reg_note kind, rtx datum) { rtx note = find_reg_note (insn, kind, NULL_RTX); + rtx new_note = NULL; switch (kind) { @@ -4501,19 +4530,37 @@ set_unique_reg_note (rtx insn, enum reg_note kind, rtx datum) It serves no useful purpose and breaks eliminate_regs. */ if (GET_CODE (datum) == ASM_OPERANDS) return NULL_RTX; + + if (note) + { + XEXP (note, 0) = datum; + df_notes_rescan (insn); + return note; + } break; default: + if (note) + { + XEXP (note, 0) = datum; + return note; + } break; } - if (note) + new_note = gen_rtx_EXPR_LIST (kind, datum, REG_NOTES (insn)); + REG_NOTES (insn) = new_note; + + switch (kind) { - XEXP (note, 0) = datum; - return note; + case REG_EQUAL: + case REG_EQUIV: + df_notes_rescan (insn); + break; + default: + break; } - REG_NOTES (insn) = gen_rtx_EXPR_LIST (kind, datum, REG_NOTES (insn)); return REG_NOTES (insn); } @@ -4787,11 +4834,7 @@ copy_insn_1 (rtx orig) break; case CONST: - /* CONST can be shared if it contains a SYMBOL_REF. If it contains - a LABEL_REF, it isn't sharable. */ - if (GET_CODE (XEXP (orig, 0)) == PLUS - && GET_CODE (XEXP (XEXP (orig, 0), 0)) == SYMBOL_REF - && GET_CODE (XEXP (XEXP (orig, 0), 1)) == CONST_INT) + if (shared_const_p (orig)) return orig; break; diff --git a/gcc/expmed.c b/gcc/expmed.c index b3709b0..ad00973 100644 --- a/gcc/expmed.c +++ b/gcc/expmed.c @@ -37,6 +37,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "real.h" #include "recog.h" #include "langhooks.h" +#include "df.h" #include "target.h" static void store_fixed_bit_field (rtx, unsigned HOST_WIDE_INT, @@ -150,7 +151,7 @@ init_expmed (void) PUT_CODE (&all.reg, REG); /* Avoid using hard regs in ways which may be unsupported. */ - REGNO (&all.reg) = LAST_VIRTUAL_REGISTER + 1; + SET_REGNO (&all.reg, LAST_VIRTUAL_REGISTER + 1); PUT_CODE (&all.plus, PLUS); XEXP (&all.plus, 0) = &all.reg; @@ -53,6 +53,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "tree-flow.h" #include "target.h" #include "timevar.h" +#include "df.h" /* Decide whether a function's arguments should be processed from first to last or from last to first. @@ -283,7 +284,7 @@ init_expr_once (void) if (! HARD_REGNO_MODE_OK (regno, mode)) continue; - REGNO (reg) = regno; + SET_REGNO (reg, regno); SET_SRC (pat) = mem; SET_DEST (pat) = reg; diff --git a/gcc/final.c b/gcc/final.c index e3a2764..3dc4981 100644 --- a/gcc/final.c +++ b/gcc/final.c @@ -76,6 +76,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "timevar.h" #include "cgraph.h" #include "coverage.h" +#include "df.h" #include "vecprim.h" #ifdef XCOFF_DEBUGGING_INFO @@ -171,23 +172,6 @@ CC_STATUS cc_status; CC_STATUS cc_prev_status; #endif -/* Indexed by hardware reg number, is 1 if that register is ever - used in the current function. - - In life_analysis, or in stupid_life_analysis, this is set - up to record the hard regs used explicitly. Reload adds - in the hard regs used for holding pseudo regs. Final uses - it to generate the code in the function prologue and epilogue - to save and restore registers as needed. */ - -char regs_ever_live[FIRST_PSEUDO_REGISTER]; - -/* Like regs_ever_live, but 1 if a reg is set or clobbered from an asm. - Unlike regs_ever_live, elements of this array corresponding to - eliminable regs like the frame pointer are set if an asm sets them. */ - -char regs_asm_clobbered[FIRST_PSEUDO_REGISTER]; - /* Nonzero means current function must be given a frame pointer. Initialized in function.c to 0. Set only in reload1.c as per the needs of the function. */ @@ -224,7 +208,7 @@ static int asm_insn_count (rtx); static void profile_function (FILE *); static void profile_after_prologue (FILE *); static bool notice_source_line (rtx); -static rtx walk_alter_subreg (rtx *); +static rtx walk_alter_subreg (rtx *, bool *); static void output_asm_name (void); static void output_alternate_entry_point (FILE *, rtx); static tree get_mem_expr_from_op (rtx, int *); @@ -2577,6 +2561,7 @@ void cleanup_subreg_operands (rtx insn) { int i; + bool changed = false; extract_insn_cached (insn); for (i = 0; i < recog_data.n_operands; i++) { @@ -2586,22 +2571,30 @@ cleanup_subreg_operands (rtx insn) matches the else clause. Instead we test the underlying expression directly. */ if (GET_CODE (*recog_data.operand_loc[i]) == SUBREG) - recog_data.operand[i] = alter_subreg (recog_data.operand_loc[i]); + { + recog_data.operand[i] = alter_subreg (recog_data.operand_loc[i]); + changed = true; + } else if (GET_CODE (recog_data.operand[i]) == PLUS || GET_CODE (recog_data.operand[i]) == MULT || MEM_P (recog_data.operand[i])) - recog_data.operand[i] = walk_alter_subreg (recog_data.operand_loc[i]); + recog_data.operand[i] = walk_alter_subreg (recog_data.operand_loc[i], &changed); } for (i = 0; i < recog_data.n_dups; i++) { if (GET_CODE (*recog_data.dup_loc[i]) == SUBREG) - *recog_data.dup_loc[i] = alter_subreg (recog_data.dup_loc[i]); + { + *recog_data.dup_loc[i] = alter_subreg (recog_data.dup_loc[i]); + changed = true; + } else if (GET_CODE (*recog_data.dup_loc[i]) == PLUS || GET_CODE (*recog_data.dup_loc[i]) == MULT || MEM_P (*recog_data.dup_loc[i])) - *recog_data.dup_loc[i] = walk_alter_subreg (recog_data.dup_loc[i]); + *recog_data.dup_loc[i] = walk_alter_subreg (recog_data.dup_loc[i], &changed); } + if (changed) + df_insn_rescan (insn); } /* If X is a SUBREG, replace it with a REG or a MEM, @@ -2655,7 +2648,7 @@ alter_subreg (rtx *xp) /* Do alter_subreg on all the SUBREGs contained in X. */ static rtx -walk_alter_subreg (rtx *xp) +walk_alter_subreg (rtx *xp, bool *changed) { rtx x = *xp; switch (GET_CODE (x)) @@ -2663,16 +2656,17 @@ walk_alter_subreg (rtx *xp) case PLUS: case MULT: case AND: - XEXP (x, 0) = walk_alter_subreg (&XEXP (x, 0)); - XEXP (x, 1) = walk_alter_subreg (&XEXP (x, 1)); + XEXP (x, 0) = walk_alter_subreg (&XEXP (x, 0), changed); + XEXP (x, 1) = walk_alter_subreg (&XEXP (x, 1), changed); break; case MEM: case ZERO_EXTEND: - XEXP (x, 0) = walk_alter_subreg (&XEXP (x, 0)); + XEXP (x, 0) = walk_alter_subreg (&XEXP (x, 0), changed); break; case SUBREG: + *changed = true; return alter_subreg (xp); default: @@ -3242,7 +3236,8 @@ output_operand (rtx x, int code ATTRIBUTE_UNUSED) void output_address (rtx x) { - walk_alter_subreg (&x); + bool changed = false; + walk_alter_subreg (&x, &changed); PRINT_OPERAND_ADDRESS (asm_out_file, x); } @@ -3760,7 +3755,7 @@ only_leaf_regs_used (void) const char *const permitted_reg_in_leaf_functions = LEAF_REGISTERS; for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if ((regs_ever_live[i] || global_regs[i]) + if ((df_regs_ever_live_p (i) || global_regs[i]) && ! permitted_reg_in_leaf_functions[i]) return 0; @@ -3828,9 +3823,9 @@ leaf_renumber_regs_insn (rtx in_rtx) } newreg = LEAF_REG_REMAP (newreg); gcc_assert (newreg >= 0); - regs_ever_live[REGNO (in_rtx)] = 0; - regs_ever_live[newreg] = 1; - REGNO (in_rtx) = newreg; + df_set_regs_ever_live (REGNO (in_rtx), false); + df_set_regs_ever_live (newreg, true); + SET_REGNO (in_rtx, newreg); in_rtx->used = 1; } @@ -3993,12 +3988,12 @@ rest_of_handle_final (void) user_defined_section_attribute = false; + /* Free up reg info memory. */ + free_reg_info (); + if (! quiet_flag) fflush (asm_out_file); - /* Release all memory allocated by flow. */ - free_basic_block_vars (); - /* Write DBX symbols if requested. */ /* Note that for those inline functions where we don't initially @@ -4096,7 +4091,6 @@ rest_of_clean_state (void) reload_completed = 0; epilogue_completed = 0; - flow2_completed = 0; no_new_pseudos = 0; #ifdef STACK_REGS regstack_completed = 0; @@ -4109,10 +4103,8 @@ rest_of_clean_state (void) /* Show no temporary slots allocated. */ init_temp_slots (); - free_basic_block_vars (); free_bb_for_insn (); - if (targetm.binds_local_p (current_function_decl)) { int pref = cfun->preferred_stack_boundary; @@ -4155,3 +4147,27 @@ struct tree_opt_pass pass_clean_state = 0 /* letter */ }; +/* Set no_new_pseudos. */ +static unsigned int +rest_of_no_new_pseudos (void) +{ + no_new_pseudos = 1; + return 0; +} + +struct tree_opt_pass pass_no_new_pseudos = +{ + NULL, /* name */ + NULL, /* gate */ + rest_of_no_new_pseudos, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ + 'p' /* letter */ +}; diff --git a/gcc/flow.c b/gcc/flow.c deleted file mode 100644 index 5b0249b..0000000 --- a/gcc/flow.c +++ /dev/null @@ -1,4786 +0,0 @@ -/* Data flow analysis for GNU compiler. - Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 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, 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301, USA. */ - -/* This file contains the data flow analysis pass of the compiler. It - computes data flow information which tells combine_instructions - which insns to consider combining and controls register allocation. - - Additional data flow information that is too bulky to record is - generated during the analysis, and is used at that time to create - autoincrement and autodecrement addressing. - - The first step is dividing the function into basic blocks. - find_basic_blocks does this. Then life_analysis determines - where each register is live and where it is dead. - - ** find_basic_blocks ** - - find_basic_blocks divides the current function's rtl into basic - blocks and constructs the CFG. The blocks are recorded in the - basic_block_info array; the CFG exists in the edge structures - referenced by the blocks. - - find_basic_blocks also finds any unreachable loops and deletes them. - - ** life_analysis ** - - life_analysis is called immediately after find_basic_blocks. - It uses the basic block information to determine where each - hard or pseudo register is live. - - ** live-register info ** - - The information about where each register is live is in two parts: - the REG_NOTES of insns, and the vector basic_block->global_live_at_start. - - basic_block->global_live_at_start has an element for each basic - block, and the element is a bit-vector with a bit for each hard or - pseudo register. The bit is 1 if the register is live at the - beginning of the basic block. - - Two types of elements can be added to an insn's REG_NOTES. - A REG_DEAD note is added to an insn's REG_NOTES for any register - that meets both of two conditions: The value in the register is not - needed in subsequent insns and the insn does not replace the value in - the register (in the case of multi-word hard registers, the value in - each register must be replaced by the insn to avoid a REG_DEAD note). - - In the vast majority of cases, an object in a REG_DEAD note will be - used somewhere in the insn. The (rare) exception to this is if an - insn uses a multi-word hard register and only some of the registers are - needed in subsequent insns. In that case, REG_DEAD notes will be - provided for those hard registers that are not subsequently needed. - Partial REG_DEAD notes of this type do not occur when an insn sets - only some of the hard registers used in such a multi-word operand; - omitting REG_DEAD notes for objects stored in an insn is optional and - the desire to do so does not justify the complexity of the partial - REG_DEAD notes. - - REG_UNUSED notes are added for each register that is set by the insn - but is unused subsequently (if every register set by the insn is unused - and the insn does not reference memory or have some other side-effect, - the insn is deleted instead). If only part of a multi-word hard - register is used in a subsequent insn, REG_UNUSED notes are made for - the parts that will not be used. - - To determine which registers are live after any insn, one can - start from the beginning of the basic block and scan insns, noting - which registers are set by each insn and which die there. - - ** Other actions of life_analysis ** - - life_analysis sets up the LOG_LINKS fields of insns because the - information needed to do so is readily available. - - life_analysis deletes insns whose only effect is to store a value - that is never used. - - life_analysis notices cases where a reference to a register as - a memory address can be combined with a preceding or following - incrementation or decrementation of the register. The separate - instruction to increment or decrement is deleted and the address - is changed to a POST_INC or similar rtx. - - Each time an incrementing or decrementing address is created, - a REG_INC element is added to the insn's REG_NOTES list. - - life_analysis fills in certain vectors containing information about - register usage: REG_N_REFS, REG_N_DEATHS, REG_N_SETS, REG_LIVE_LENGTH, - REG_N_CALLS_CROSSED, REG_N_THROWING_CALLS_CROSSED and REG_BASIC_BLOCK. - - life_analysis sets current_function_sp_is_unchanging if the function - doesn't modify the stack pointer. */ - -/* TODO: - - Split out from life_analysis: - - local property discovery - - global property computation - - log links creation - - pre/post modify transformation -*/ - -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "tm.h" -#include "tree.h" -#include "rtl.h" -#include "tm_p.h" -#include "hard-reg-set.h" -#include "basic-block.h" -#include "insn-config.h" -#include "regs.h" -#include "flags.h" -#include "output.h" -#include "function.h" -#include "except.h" -#include "toplev.h" -#include "recog.h" -#include "expr.h" -#include "timevar.h" - -#include "obstack.h" -#include "splay-tree.h" -#include "tree-pass.h" -#include "params.h" - -#ifndef HAVE_epilogue -#define HAVE_epilogue 0 -#endif -#ifndef HAVE_prologue -#define HAVE_prologue 0 -#endif -#ifndef HAVE_sibcall_epilogue -#define HAVE_sibcall_epilogue 0 -#endif - -#ifndef EPILOGUE_USES -#define EPILOGUE_USES(REGNO) 0 -#endif -#ifndef EH_USES -#define EH_USES(REGNO) 0 -#endif - -#ifdef HAVE_conditional_execution -#ifndef REVERSE_CONDEXEC_PREDICATES_P -#define REVERSE_CONDEXEC_PREDICATES_P(x, y) \ - (GET_CODE ((x)) == reversed_comparison_code ((y), NULL)) -#endif -#endif - -/* This is the maximum number of times we process any given block if the - latest loop depth count is smaller than this number. Only used for the - failure strategy to avoid infinite loops in calculate_global_regs_live. */ -#define MAX_LIVENESS_ROUNDS 20 - -/* Nonzero if the second flow pass has completed. */ -int flow2_completed; - -/* Maximum register number used in this function, plus one. */ - -int max_regno; - -/* Indexed by n, giving various register information */ - -VEC(reg_info_p,heap) *reg_n_info; - -/* Regset of regs live when calls to `setjmp'-like functions happen. */ -/* ??? Does this exist only for the setjmp-clobbered warning message? */ - -static regset regs_live_at_setjmp; - -/* List made of EXPR_LIST rtx's which gives pairs of pseudo registers - that have to go in the same hard reg. - The first two regs in the list are a pair, and the next two - are another pair, etc. */ -rtx regs_may_share; - -/* Set of registers that may be eliminable. These are handled specially - in updating regs_ever_live. */ - -static HARD_REG_SET elim_reg_set; - -/* Holds information for tracking conditional register life information. */ -struct reg_cond_life_info -{ - /* A boolean expression of conditions under which a register is dead. */ - rtx condition; - /* Conditions under which a register is dead at the basic block end. */ - rtx orig_condition; - - /* A boolean expression of conditions under which a register has been - stored into. */ - rtx stores; - - /* ??? Could store mask of bytes that are dead, so that we could finally - track lifetimes of multi-word registers accessed via subregs. */ -}; - -/* For use in communicating between propagate_block and its subroutines. - Holds all information needed to compute life and def-use information. */ - -struct propagate_block_info -{ - /* The basic block we're considering. */ - basic_block bb; - - /* Bit N is set if register N is conditionally or unconditionally live. */ - regset reg_live; - - /* Bit N is set if register N is set this insn. */ - regset new_set; - - /* Element N is the next insn that uses (hard or pseudo) register N - within the current basic block; or zero, if there is no such insn. */ - rtx *reg_next_use; - - /* Contains a list of all the MEMs we are tracking for dead store - elimination. */ - rtx mem_set_list; - - /* If non-null, record the set of registers set unconditionally in the - basic block. */ - regset local_set; - - /* If non-null, record the set of registers set conditionally in the - basic block. */ - regset cond_local_set; - -#ifdef HAVE_conditional_execution - /* Indexed by register number, holds a reg_cond_life_info for each - register that is not unconditionally live or dead. */ - splay_tree reg_cond_dead; - - /* Bit N is set if register N is in an expression in reg_cond_dead. */ - regset reg_cond_reg; -#endif - - /* The length of mem_set_list. */ - int mem_set_list_len; - - /* Nonzero if the value of CC0 is live. */ - int cc0_live; - - /* Flags controlling the set of information propagate_block collects. */ - int flags; - /* Index of instruction being processed. */ - int insn_num; -}; - -/* Number of dead insns removed. */ -static int ndead; - -/* When PROP_REG_INFO set, array contains pbi->insn_num of instruction - where given register died. When the register is marked alive, we use the - information to compute amount of instructions life range cross. - (remember, we are walking backward). This can be computed as current - pbi->insn_num - reg_deaths[regno]. - At the end of processing each basic block, the remaining live registers - are inspected and live ranges are increased same way so liverange of global - registers are computed correctly. - - The array is maintained clear for dead registers, so it can be safely reused - for next basic block without expensive memset of the whole array after - reseting pbi->insn_num to 0. */ - -static int *reg_deaths; - -/* Forward declarations */ -static int verify_wide_reg_1 (rtx *, void *); -static void verify_wide_reg (int, basic_block); -static void verify_local_live_at_start (regset, basic_block); -static void notice_stack_pointer_modification_1 (rtx, rtx, void *); -static void notice_stack_pointer_modification (void); -static void mark_reg (rtx, void *); -static void mark_regs_live_at_end (regset); -static void calculate_global_regs_live (sbitmap, sbitmap, int); -static void propagate_block_delete_insn (rtx); -static rtx propagate_block_delete_libcall (rtx, rtx); -static int insn_dead_p (struct propagate_block_info *, rtx, int, rtx); -static int libcall_dead_p (struct propagate_block_info *, rtx, rtx); -static void mark_set_regs (struct propagate_block_info *, rtx, rtx); -static void mark_set_1 (struct propagate_block_info *, enum rtx_code, rtx, - rtx, rtx, int); -static int find_regno_partial (rtx *, void *); - -#ifdef HAVE_conditional_execution -static int mark_regno_cond_dead (struct propagate_block_info *, int, rtx); -static void free_reg_cond_life_info (splay_tree_value); -static int flush_reg_cond_reg_1 (splay_tree_node, void *); -static void flush_reg_cond_reg (struct propagate_block_info *, int); -static rtx elim_reg_cond (rtx, unsigned int); -static rtx ior_reg_cond (rtx, rtx, int); -static rtx not_reg_cond (rtx); -static rtx and_reg_cond (rtx, rtx, int); -#endif -#ifdef AUTO_INC_DEC -static void attempt_auto_inc (struct propagate_block_info *, rtx, rtx, rtx, - rtx, rtx); -static void find_auto_inc (struct propagate_block_info *, rtx, rtx); -static int try_pre_increment_1 (struct propagate_block_info *, rtx); -static int try_pre_increment (rtx, rtx, HOST_WIDE_INT); -#endif -static void mark_used_reg (struct propagate_block_info *, rtx, rtx, rtx); -static void mark_used_regs (struct propagate_block_info *, rtx, rtx, rtx); -void debug_flow_info (void); -static void add_to_mem_set_list (struct propagate_block_info *, rtx); -static int invalidate_mems_from_autoinc (rtx *, void *); -static void invalidate_mems_from_set (struct propagate_block_info *, rtx); -static void clear_log_links (sbitmap); -static int count_or_remove_death_notes_bb (basic_block, int); -static void allocate_bb_life_data (void); - -/* Return the INSN immediately following the NOTE_INSN_BASIC_BLOCK - note associated with the BLOCK. */ - -rtx -first_insn_after_basic_block_note (basic_block block) -{ - rtx insn; - - /* Get the first instruction in the block. */ - insn = BB_HEAD (block); - - if (insn == NULL_RTX) - return NULL_RTX; - if (LABEL_P (insn)) - insn = NEXT_INSN (insn); - gcc_assert (NOTE_INSN_BASIC_BLOCK_P (insn)); - - return NEXT_INSN (insn); -} - -/* Perform data flow analysis for the whole control flow graph. - FLAGS is a set of PROP_* flags to be used in accumulating flow info. */ - -void -life_analysis (int flags) -{ -#ifdef ELIMINABLE_REGS - int i; - static const struct {const int from, to; } eliminables[] = ELIMINABLE_REGS; -#endif - - /* Record which registers will be eliminated. We use this in - mark_used_regs. */ - - CLEAR_HARD_REG_SET (elim_reg_set); - -#ifdef ELIMINABLE_REGS - for (i = 0; i < (int) ARRAY_SIZE (eliminables); i++) - SET_HARD_REG_BIT (elim_reg_set, eliminables[i].from); -#else - SET_HARD_REG_BIT (elim_reg_set, FRAME_POINTER_REGNUM); -#endif - - -#ifdef CANNOT_CHANGE_MODE_CLASS - if (flags & PROP_REG_INFO) - init_subregs_of_mode (); -#endif - - if (! optimize) - flags &= ~(PROP_LOG_LINKS | PROP_AUTOINC | PROP_ALLOW_CFG_CHANGES); - - /* The post-reload life analysis have (on a global basis) the same - registers live as was computed by reload itself. elimination - Otherwise offsets and such may be incorrect. - - Reload will make some registers as live even though they do not - appear in the rtl. - - We don't want to create new auto-incs after reload, since they - are unlikely to be useful and can cause problems with shared - stack slots. */ - if (reload_completed) - flags &= ~(PROP_REG_INFO | PROP_AUTOINC); - - /* We want alias analysis information for local dead store elimination. */ - if (optimize && (flags & PROP_SCAN_DEAD_STORES)) - init_alias_analysis (); - - /* Always remove no-op moves. Do this before other processing so - that we don't have to keep re-scanning them. */ - delete_noop_moves (); - - /* Some targets can emit simpler epilogues if they know that sp was - not ever modified during the function. After reload, of course, - we've already emitted the epilogue so there's no sense searching. */ - if (! reload_completed) - notice_stack_pointer_modification (); - - /* Allocate and zero out data structures that will record the - data from lifetime analysis. */ - allocate_reg_life_data (); - allocate_bb_life_data (); - - /* Find the set of registers live on function exit. */ - mark_regs_live_at_end (EXIT_BLOCK_PTR->il.rtl->global_live_at_start); - - /* "Update" life info from zero. It'd be nice to begin the - relaxation with just the exit and noreturn blocks, but that set - is not immediately handy. */ - - if (flags & PROP_REG_INFO) - { - memset (regs_ever_live, 0, sizeof (regs_ever_live)); - memset (regs_asm_clobbered, 0, sizeof (regs_asm_clobbered)); - } - update_life_info (NULL, UPDATE_LIFE_GLOBAL, flags); - if (reg_deaths) - { - free (reg_deaths); - reg_deaths = NULL; - } - - /* Clean up. */ - if (optimize && (flags & PROP_SCAN_DEAD_STORES)) - end_alias_analysis (); - - if (dump_file) - dump_flow_info (dump_file, dump_flags); - - /* Removing dead insns should have made jumptables really dead. */ - delete_dead_jumptables (); -} - -/* A subroutine of verify_wide_reg, called through for_each_rtx. - Search for REGNO. If found, return 2 if it is not wider than - word_mode. */ - -static int -verify_wide_reg_1 (rtx *px, void *pregno) -{ - rtx x = *px; - unsigned int regno = *(int *) pregno; - - if (REG_P (x) && REGNO (x) == regno) - { - if (GET_MODE_BITSIZE (GET_MODE (x)) <= BITS_PER_WORD) - return 2; - return 1; - } - return 0; -} - -/* A subroutine of verify_local_live_at_start. Search through insns - of BB looking for register REGNO. */ - -static void -verify_wide_reg (int regno, basic_block bb) -{ - rtx head = BB_HEAD (bb), end = BB_END (bb); - - while (1) - { - if (INSN_P (head)) - { - int r = for_each_rtx (&PATTERN (head), verify_wide_reg_1, ®no); - if (r == 1) - return; - if (r == 2) - break; - } - if (head == end) - break; - head = NEXT_INSN (head); - } - if (dump_file) - { - fprintf (dump_file, "Register %d died unexpectedly.\n", regno); - dump_bb (bb, dump_file, 0); - } - internal_error ("internal consistency failure"); -} - -/* A subroutine of update_life_info. Verify that there are no untoward - changes in live_at_start during a local update. */ - -static void -verify_local_live_at_start (regset new_live_at_start, basic_block bb) -{ - if (reload_completed) - { - /* After reload, there are no pseudos, nor subregs of multi-word - registers. The regsets should exactly match. */ - if (! REG_SET_EQUAL_P (new_live_at_start, - bb->il.rtl->global_live_at_start)) - { - if (dump_file) - { - fprintf (dump_file, - "live_at_start mismatch in bb %d, aborting\nNew:\n", - bb->index); - debug_bitmap_file (dump_file, new_live_at_start); - fputs ("Old:\n", dump_file); - dump_bb (bb, dump_file, 0); - } - internal_error ("internal consistency failure"); - } - } - else - { - unsigned i; - reg_set_iterator rsi; - - /* Find the set of changed registers. */ - XOR_REG_SET (new_live_at_start, bb->il.rtl->global_live_at_start); - - EXECUTE_IF_SET_IN_REG_SET (new_live_at_start, 0, i, rsi) - { - /* No registers should die. */ - if (REGNO_REG_SET_P (bb->il.rtl->global_live_at_start, i)) - { - if (dump_file) - { - fprintf (dump_file, - "Register %d died unexpectedly.\n", i); - dump_bb (bb, dump_file, 0); - } - internal_error ("internal consistency failure"); - } - /* Verify that the now-live register is wider than word_mode. */ - verify_wide_reg (i, bb); - } - } -} - -/* Updates life information starting with the basic blocks set in BLOCKS. - If BLOCKS is null, consider it to be the universal set. - - If EXTENT is UPDATE_LIFE_LOCAL, such as after splitting or peepholing, - we are only expecting local modifications to basic blocks. If we find - extra registers live at the beginning of a block, then we either killed - useful data, or we have a broken split that wants data not provided. - If we find registers removed from live_at_start, that means we have - a broken peephole that is killing a register it shouldn't. - - ??? This is not true in one situation -- when a pre-reload splitter - generates subregs of a multi-word pseudo, current life analysis will - lose the kill. So we _can_ have a pseudo go live. How irritating. - - It is also not true when a peephole decides that it doesn't need one - or more of the inputs. - - Including PROP_REG_INFO does not properly refresh regs_ever_live - unless the caller resets it to zero. */ - -int -update_life_info (sbitmap blocks, enum update_life_extent extent, - int prop_flags) -{ - regset tmp; - unsigned i = 0; - int stabilized_prop_flags = prop_flags; - basic_block bb; - - tmp = ALLOC_REG_SET (®_obstack); - ndead = 0; - - if ((prop_flags & PROP_REG_INFO) && !reg_deaths) - reg_deaths = XCNEWVEC (int, max_regno); - - timevar_push ((extent == UPDATE_LIFE_LOCAL || blocks) - ? TV_LIFE_UPDATE : TV_LIFE); - - /* Changes to the CFG are only allowed when - doing a global update for the entire CFG. */ - gcc_assert (!(prop_flags & PROP_ALLOW_CFG_CHANGES) - || (extent != UPDATE_LIFE_LOCAL && !blocks)); - - /* For a global update, we go through the relaxation process again. */ - if (extent != UPDATE_LIFE_LOCAL) - { - for ( ; ; ) - { - int changed = 0; - - calculate_global_regs_live (blocks, blocks, - prop_flags & (PROP_SCAN_DEAD_CODE - | PROP_SCAN_DEAD_STORES - | PROP_ALLOW_CFG_CHANGES)); - - if ((prop_flags & (PROP_KILL_DEAD_CODE | PROP_ALLOW_CFG_CHANGES)) - != (PROP_KILL_DEAD_CODE | PROP_ALLOW_CFG_CHANGES)) - break; - - /* Removing dead code may allow the CFG to be simplified which - in turn may allow for further dead code detection / removal. */ - FOR_EACH_BB_REVERSE (bb) - { - COPY_REG_SET (tmp, bb->il.rtl->global_live_at_end); - changed |= propagate_block (bb, tmp, NULL, NULL, - prop_flags & (PROP_SCAN_DEAD_CODE - | PROP_SCAN_DEAD_STORES - | PROP_KILL_DEAD_CODE)); - } - - /* Don't pass PROP_SCAN_DEAD_CODE or PROP_KILL_DEAD_CODE to - subsequent propagate_block calls, since removing or acting as - removing dead code can affect global register liveness, which - is supposed to be finalized for this call after this loop. */ - stabilized_prop_flags - &= ~(PROP_SCAN_DEAD_CODE | PROP_SCAN_DEAD_STORES - | PROP_KILL_DEAD_CODE); - - if (! changed) - break; - - /* We repeat regardless of what cleanup_cfg says. If there were - instructions deleted above, that might have been only a - partial improvement (see PARAM_MAX_FLOW_MEMORY_LOCATIONS usage). - Further improvement may be possible. */ - cleanup_cfg (CLEANUP_EXPENSIVE); - - /* Zap the life information from the last round. If we don't - do this, we can wind up with registers that no longer appear - in the code being marked live at entry. */ - FOR_EACH_BB (bb) - { - CLEAR_REG_SET (bb->il.rtl->global_live_at_start); - CLEAR_REG_SET (bb->il.rtl->global_live_at_end); - } - } - - /* If asked, remove notes from the blocks we'll update. */ - if (extent == UPDATE_LIFE_GLOBAL_RM_NOTES) - count_or_remove_death_notes (blocks, - prop_flags & PROP_POST_REGSTACK ? -1 : 1); - } - else - { - /* FIXME: This can go when the dataflow branch has been merged in. */ - /* For a local update, if we are creating new REG_DEAD notes, then we - must delete the old ones first to avoid conflicts if they are - different. */ - if (prop_flags & PROP_DEATH_NOTES) - count_or_remove_death_notes (blocks, - prop_flags & PROP_POST_REGSTACK ? -1 : 1); - } - - - /* Clear log links in case we are asked to (re)compute them. */ - if (prop_flags & PROP_LOG_LINKS) - clear_log_links (blocks); - - if (blocks) - { - sbitmap_iterator sbi; - - EXECUTE_IF_SET_IN_SBITMAP (blocks, 0, i, sbi) - { - bb = BASIC_BLOCK (i); - if (bb) - { - /* The bitmap may be flawed in that one of the basic - blocks may have been deleted before you get here. */ - COPY_REG_SET (tmp, bb->il.rtl->global_live_at_end); - propagate_block (bb, tmp, NULL, NULL, stabilized_prop_flags); - - if (extent == UPDATE_LIFE_LOCAL) - verify_local_live_at_start (tmp, bb); - } - }; - } - else - { - FOR_EACH_BB_REVERSE (bb) - { - COPY_REG_SET (tmp, bb->il.rtl->global_live_at_end); - - propagate_block (bb, tmp, NULL, NULL, stabilized_prop_flags); - - if (extent == UPDATE_LIFE_LOCAL) - verify_local_live_at_start (tmp, bb); - } - } - - FREE_REG_SET (tmp); - - if (prop_flags & PROP_REG_INFO) - { - reg_set_iterator rsi; - - /* The only pseudos that are live at the beginning of the function - are those that were not set anywhere in the function. local-alloc - doesn't know how to handle these correctly, so mark them as not - local to any one basic block. */ - EXECUTE_IF_SET_IN_REG_SET (ENTRY_BLOCK_PTR->il.rtl->global_live_at_end, - FIRST_PSEUDO_REGISTER, i, rsi) - REG_BASIC_BLOCK (i) = REG_BLOCK_GLOBAL; - - /* We have a problem with any pseudoreg that lives across the setjmp. - ANSI says that if a user variable does not change in value between - the setjmp and the longjmp, then the longjmp preserves it. This - includes longjmp from a place where the pseudo appears dead. - (In principle, the value still exists if it is in scope.) - If the pseudo goes in a hard reg, some other value may occupy - that hard reg where this pseudo is dead, thus clobbering the pseudo. - Conclusion: such a pseudo must not go in a hard reg. */ - EXECUTE_IF_SET_IN_REG_SET (regs_live_at_setjmp, - FIRST_PSEUDO_REGISTER, i, rsi) - { - if (regno_reg_rtx[i] != 0) - { - REG_LIVE_LENGTH (i) = -1; - REG_BASIC_BLOCK (i) = REG_BLOCK_UNKNOWN; - } - } - } - if (reg_deaths) - { - free (reg_deaths); - reg_deaths = NULL; - } - timevar_pop ((extent == UPDATE_LIFE_LOCAL || blocks) - ? TV_LIFE_UPDATE : TV_LIFE); - if (ndead && dump_file) - fprintf (dump_file, "deleted %i dead insns\n", ndead); - return ndead; -} - -/* Update life information in all blocks where BB_DIRTY is set. */ - -int -update_life_info_in_dirty_blocks (enum update_life_extent extent, int prop_flags) -{ - sbitmap update_life_blocks = sbitmap_alloc (last_basic_block); - int n = 0; - basic_block bb; - int retval = 0; - - sbitmap_zero (update_life_blocks); - FOR_EACH_BB (bb) - { - if (bb->flags & BB_DIRTY) - { - SET_BIT (update_life_blocks, bb->index); - n++; - } - } - - if (n) - retval = update_life_info (update_life_blocks, extent, prop_flags); - - sbitmap_free (update_life_blocks); - return retval; -} - -/* Free the variables allocated by find_basic_blocks. */ - -void -free_basic_block_vars (void) -{ - if (basic_block_info) - { - clear_edges (); - basic_block_info = NULL; - } - n_basic_blocks = 0; - last_basic_block = 0; - n_edges = 0; - - label_to_block_map = NULL; - - ENTRY_BLOCK_PTR->aux = NULL; - ENTRY_BLOCK_PTR->il.rtl->global_live_at_end = NULL; - EXIT_BLOCK_PTR->aux = NULL; - EXIT_BLOCK_PTR->il.rtl->global_live_at_start = NULL; -} - -/* Delete any insns that copy a register to itself. */ - -int -delete_noop_moves (void) -{ - rtx insn, next; - basic_block bb; - int nnoops = 0; - - FOR_EACH_BB (bb) - { - for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb)); insn = next) - { - next = NEXT_INSN (insn); - if (INSN_P (insn) && noop_move_p (insn)) - { - rtx note; - - /* If we're about to remove the first insn of a libcall - then move the libcall note to the next real insn and - update the retval note. */ - if ((note = find_reg_note (insn, REG_LIBCALL, NULL_RTX)) - && XEXP (note, 0) != insn) - { - rtx new_libcall_insn = next_real_insn (insn); - rtx retval_note = find_reg_note (XEXP (note, 0), - REG_RETVAL, NULL_RTX); - REG_NOTES (new_libcall_insn) - = gen_rtx_INSN_LIST (REG_LIBCALL, XEXP (note, 0), - REG_NOTES (new_libcall_insn)); - XEXP (retval_note, 0) = new_libcall_insn; - } - - delete_insn_and_edges (insn); - nnoops++; - } - } - } - - if (nnoops && dump_file) - fprintf (dump_file, "deleted %i noop moves\n", nnoops); - - return nnoops; -} - -/* Delete any jump tables never referenced. We can't delete them at the - time of removing tablejump insn as they are referenced by the preceding - insns computing the destination, so we delay deleting and garbagecollect - them once life information is computed. */ -void -delete_dead_jumptables (void) -{ - basic_block bb; - - /* A dead jump table does not belong to any basic block. Scan insns - between two adjacent basic blocks. */ - FOR_EACH_BB (bb) - { - rtx insn, next; - - for (insn = NEXT_INSN (BB_END (bb)); - insn && !NOTE_INSN_BASIC_BLOCK_P (insn); - insn = next) - { - next = NEXT_INSN (insn); - if (LABEL_P (insn) - && LABEL_NUSES (insn) == LABEL_PRESERVE_P (insn) - && JUMP_P (next) - && (GET_CODE (PATTERN (next)) == ADDR_VEC - || GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC)) - { - rtx label = insn, jump = next; - - if (dump_file) - fprintf (dump_file, "Dead jumptable %i removed\n", - INSN_UID (insn)); - - next = NEXT_INSN (next); - delete_insn (jump); - delete_insn (label); - } - } - } -} - -/* Determine if the stack pointer is constant over the life of the function. - Only useful before prologues have been emitted. */ - -static void -notice_stack_pointer_modification_1 (rtx x, rtx pat ATTRIBUTE_UNUSED, - void *data ATTRIBUTE_UNUSED) -{ - if (x == stack_pointer_rtx - /* The stack pointer is only modified indirectly as the result - of a push until later in flow. See the comments in rtl.texi - regarding Embedded Side-Effects on Addresses. */ - || (MEM_P (x) - && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == RTX_AUTOINC - && XEXP (XEXP (x, 0), 0) == stack_pointer_rtx)) - current_function_sp_is_unchanging = 0; -} - -static void -notice_stack_pointer_modification (void) -{ - basic_block bb; - rtx insn; - - /* Assume that the stack pointer is unchanging if alloca hasn't - been used. */ - current_function_sp_is_unchanging = !current_function_calls_alloca; - if (! current_function_sp_is_unchanging) - return; - - FOR_EACH_BB (bb) - FOR_BB_INSNS (bb, insn) - { - if (INSN_P (insn)) - { - /* Check if insn modifies the stack pointer. */ - note_stores (PATTERN (insn), - notice_stack_pointer_modification_1, - NULL); - if (! current_function_sp_is_unchanging) - return; - } - } -} - -/* Mark a register in SET. Hard registers in large modes get all - of their component registers set as well. */ - -static void -mark_reg (rtx reg, void *xset) -{ - regset set = (regset) xset; - int regno = REGNO (reg); - - gcc_assert (GET_MODE (reg) != BLKmode); - - SET_REGNO_REG_SET (set, regno); - if (regno < FIRST_PSEUDO_REGISTER) - { - int n = hard_regno_nregs[regno][GET_MODE (reg)]; - while (--n > 0) - SET_REGNO_REG_SET (set, regno + n); - } -} - -/* Mark those regs which are needed at the end of the function as live - at the end of the last basic block. */ - -static void -mark_regs_live_at_end (regset set) -{ - unsigned int i; - - /* If exiting needs the right stack value, consider the stack pointer - live at the end of the function. */ - if ((HAVE_epilogue && epilogue_completed) - || ! EXIT_IGNORE_STACK - || (! FRAME_POINTER_REQUIRED - && ! current_function_calls_alloca - && flag_omit_frame_pointer) - || current_function_sp_is_unchanging) - { - SET_REGNO_REG_SET (set, STACK_POINTER_REGNUM); - } - - /* Mark the frame pointer if needed at the end of the function. If - we end up eliminating it, it will be removed from the live list - of each basic block by reload. */ - - if (! reload_completed || frame_pointer_needed) - { - SET_REGNO_REG_SET (set, FRAME_POINTER_REGNUM); -#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM - /* If they are different, also mark the hard frame pointer as live. */ - if (! LOCAL_REGNO (HARD_FRAME_POINTER_REGNUM)) - SET_REGNO_REG_SET (set, HARD_FRAME_POINTER_REGNUM); -#endif - } - -#ifndef PIC_OFFSET_TABLE_REG_CALL_CLOBBERED - /* Many architectures have a GP register even without flag_pic. - Assume the pic register is not in use, or will be handled by - other means, if it is not fixed. */ - if ((unsigned) PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM - && fixed_regs[PIC_OFFSET_TABLE_REGNUM]) - SET_REGNO_REG_SET (set, PIC_OFFSET_TABLE_REGNUM); -#endif - - /* Mark all global registers, and all registers used by the epilogue - as being live at the end of the function since they may be - referenced by our caller. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (global_regs[i] || EPILOGUE_USES (i)) - SET_REGNO_REG_SET (set, i); - - if (HAVE_epilogue && epilogue_completed) - { - /* Mark all call-saved registers that we actually used. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (regs_ever_live[i] && ! LOCAL_REGNO (i) - && ! TEST_HARD_REG_BIT (regs_invalidated_by_call, i)) - SET_REGNO_REG_SET (set, i); - } - -#ifdef EH_RETURN_DATA_REGNO - /* Mark the registers that will contain data for the handler. */ - if (reload_completed && current_function_calls_eh_return) - for (i = 0; ; ++i) - { - unsigned regno = EH_RETURN_DATA_REGNO(i); - if (regno == INVALID_REGNUM) - break; - SET_REGNO_REG_SET (set, regno); - } -#endif -#ifdef EH_RETURN_STACKADJ_RTX - if ((! HAVE_epilogue || ! epilogue_completed) - && current_function_calls_eh_return) - { - rtx tmp = EH_RETURN_STACKADJ_RTX; - if (tmp && REG_P (tmp)) - mark_reg (tmp, set); - } -#endif -#ifdef EH_RETURN_HANDLER_RTX - if ((! HAVE_epilogue || ! epilogue_completed) - && current_function_calls_eh_return) - { - rtx tmp = EH_RETURN_HANDLER_RTX; - if (tmp && REG_P (tmp)) - mark_reg (tmp, set); - } -#endif - - /* Mark function return value. */ - diddle_return_value (mark_reg, set); -} - -/* Propagate global life info around the graph of basic blocks. Begin - considering blocks with their corresponding bit set in BLOCKS_IN. - If BLOCKS_IN is null, consider it the universal set. - - BLOCKS_OUT is set for every block that was changed. */ - -static void -calculate_global_regs_live (sbitmap blocks_in, sbitmap blocks_out, int flags) -{ - basic_block *queue, *qhead, *qtail, *qend, bb; - regset tmp, new_live_at_end, invalidated_by_eh_edge; - regset registers_made_dead; - bool failure_strategy_required = false; - int *block_accesses; - - /* The registers that are modified within this in block. */ - regset *local_sets; - - /* The registers that are conditionally modified within this block. - In other words, regs that are set only as part of a COND_EXEC. */ - regset *cond_local_sets; - - unsigned int i; - - /* Some passes used to forget clear aux field of basic block causing - sick behavior here. */ -#ifdef ENABLE_CHECKING - FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb) - gcc_assert (!bb->aux); -#endif - - tmp = ALLOC_REG_SET (®_obstack); - new_live_at_end = ALLOC_REG_SET (®_obstack); - invalidated_by_eh_edge = ALLOC_REG_SET (®_obstack); - registers_made_dead = ALLOC_REG_SET (®_obstack); - - /* Inconveniently, this is only readily available in hard reg set form. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i) - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i)) - SET_REGNO_REG_SET (invalidated_by_eh_edge, i); - - /* The exception handling registers die at eh edges. */ -#ifdef EH_RETURN_DATA_REGNO - for (i = 0; ; ++i) - { - unsigned regno = EH_RETURN_DATA_REGNO (i); - if (regno == INVALID_REGNUM) - break; - SET_REGNO_REG_SET (invalidated_by_eh_edge, regno); - } -#endif - - /* Allocate space for the sets of local properties. */ - local_sets = XCNEWVEC (bitmap, last_basic_block); - cond_local_sets = XCNEWVEC (bitmap, last_basic_block); - - /* Create a worklist. Allocate an extra slot for the `head == tail' - style test for an empty queue doesn't work with a full queue. */ - queue = XNEWVEC (basic_block, n_basic_blocks + 1); - qtail = queue; - qhead = qend = queue + n_basic_blocks; - - /* Queue the blocks set in the initial mask. Do this in reverse block - number order so that we are more likely for the first round to do - useful work. We use AUX non-null to flag that the block is queued. */ - if (blocks_in) - { - FOR_EACH_BB (bb) - if (TEST_BIT (blocks_in, bb->index)) - { - *--qhead = bb; - bb->aux = bb; - } - } - else - { - FOR_EACH_BB (bb) - { - *--qhead = bb; - bb->aux = bb; - } - } - - block_accesses = XCNEWVEC (int, last_basic_block); - - /* We clean aux when we remove the initially-enqueued bbs, but we - don't enqueue ENTRY and EXIT initially, so clean them upfront and - unconditionally. */ - ENTRY_BLOCK_PTR->aux = EXIT_BLOCK_PTR->aux = NULL; - - if (blocks_out) - sbitmap_zero (blocks_out); - - /* We work through the queue until there are no more blocks. What - is live at the end of this block is precisely the union of what - is live at the beginning of all its successors. So, we set its - GLOBAL_LIVE_AT_END field based on the GLOBAL_LIVE_AT_START field - for its successors. Then, we compute GLOBAL_LIVE_AT_START for - this block by walking through the instructions in this block in - reverse order and updating as we go. If that changed - GLOBAL_LIVE_AT_START, we add the predecessors of the block to the - queue; they will now need to recalculate GLOBAL_LIVE_AT_END. - - We are guaranteed to terminate, because GLOBAL_LIVE_AT_START - never shrinks. If a register appears in GLOBAL_LIVE_AT_START, it - must either be live at the end of the block, or used within the - block. In the latter case, it will certainly never disappear - from GLOBAL_LIVE_AT_START. In the former case, the register - could go away only if it disappeared from GLOBAL_LIVE_AT_START - for one of the successor blocks. By induction, that cannot - occur. - - ??? This reasoning doesn't work if we start from non-empty initial - GLOBAL_LIVE_AT_START sets. And there are actually two problems: - 1) Updating may not terminate (endless oscillation). - 2) Even if it does (and it usually does), the resulting information - may be inaccurate. Consider for example the following case: - - a = ...; - while (...) {...} -- 'a' not mentioned at all - ... = a; - - If the use of 'a' is deleted between two calculations of liveness - information and the initial sets are not cleared, the information - about a's liveness will get stuck inside the loop and the set will - appear not to be dead. - - We do not attempt to solve 2) -- the information is conservatively - correct (i.e. we never claim that something live is dead) and the - amount of optimization opportunities missed due to this problem is - not significant. - - 1) is more serious. In order to fix it, we monitor the number of times - each block is processed. Once one of the blocks has been processed more - times than the maximum number of rounds, we use the following strategy: - When a register disappears from one of the sets, we add it to a MAKE_DEAD - set, remove all registers in this set from all GLOBAL_LIVE_AT_* sets and - add the blocks with changed sets into the queue. Thus we are guaranteed - to terminate (the worst case corresponds to all registers in MADE_DEAD, - in which case the original reasoning above is valid), but in general we - only fix up a few offending registers. - - The maximum number of rounds for computing liveness is the largest of - MAX_LIVENESS_ROUNDS and the latest loop depth count for this function. */ - - while (qhead != qtail) - { - int rescan, changed; - basic_block bb; - edge e; - edge_iterator ei; - - bb = *qhead++; - if (qhead == qend) - qhead = queue; - bb->aux = NULL; - - /* Should we start using the failure strategy? */ - if (bb != ENTRY_BLOCK_PTR) - { - int max_liveness_rounds = - MAX (MAX_LIVENESS_ROUNDS, cfun->max_loop_depth); - - block_accesses[bb->index]++; - if (block_accesses[bb->index] > max_liveness_rounds) - failure_strategy_required = true; - } - - /* Begin by propagating live_at_start from the successor blocks. */ - CLEAR_REG_SET (new_live_at_end); - - if (EDGE_COUNT (bb->succs) > 0) - FOR_EACH_EDGE (e, ei, bb->succs) - { - basic_block sb = e->dest; - - /* Call-clobbered registers die across exception and - call edges. */ - /* ??? 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_ior_and_compl_into (new_live_at_end, - sb->il.rtl->global_live_at_start, - invalidated_by_eh_edge); - else - IOR_REG_SET (new_live_at_end, sb->il.rtl->global_live_at_start); - - /* If a target saves one register in another (instead of on - the stack) the save register will need to be live for EH. */ - if (e->flags & EDGE_EH) - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (EH_USES (i)) - SET_REGNO_REG_SET (new_live_at_end, i); - } - else - { - /* This might be a noreturn function that throws. And - even if it isn't, getting the unwind info right helps - debugging. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (EH_USES (i)) - SET_REGNO_REG_SET (new_live_at_end, i); - } - - /* The all-important stack pointer must always be live. */ - SET_REGNO_REG_SET (new_live_at_end, STACK_POINTER_REGNUM); - - /* 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) - { - /* Any reference to any pseudo before reload is a potential - reference of the frame pointer. */ - SET_REGNO_REG_SET (new_live_at_end, FRAME_POINTER_REGNUM); - -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - /* Pseudos with argument area equivalences may require - reloading via the argument pointer. */ - if (fixed_regs[ARG_POINTER_REGNUM]) - SET_REGNO_REG_SET (new_live_at_end, ARG_POINTER_REGNUM); -#endif - - /* Any constant, or pseudo with constant equivalences, may - require reloading from memory using the pic register. */ - if ((unsigned) PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM - && fixed_regs[PIC_OFFSET_TABLE_REGNUM]) - SET_REGNO_REG_SET (new_live_at_end, PIC_OFFSET_TABLE_REGNUM); - } - - if (bb == ENTRY_BLOCK_PTR) - { - COPY_REG_SET (bb->il.rtl->global_live_at_end, new_live_at_end); - continue; - } - - /* On our first pass through this block, we'll go ahead and continue. - Recognize first pass by checking if local_set is NULL for this - basic block. On subsequent passes, we get to skip out early if - live_at_end wouldn't have changed. */ - - if (local_sets[bb->index] == NULL) - { - local_sets[bb->index] = ALLOC_REG_SET (®_obstack); - cond_local_sets[bb->index] = ALLOC_REG_SET (®_obstack); - rescan = 1; - } - else - { - /* If any bits were removed from live_at_end, we'll have to - rescan the block. This wouldn't be necessary if we had - precalculated local_live, however with PROP_SCAN_DEAD_CODE - local_live is really dependent on live_at_end. */ - rescan = bitmap_intersect_compl_p (bb->il.rtl->global_live_at_end, - new_live_at_end); - - if (!rescan) - { - regset cond_local_set; - - /* If any of the registers in the new live_at_end set are - conditionally set in this basic block, we must rescan. - This is because conditional lifetimes at the end of the - block do not just take the live_at_end set into - account, but also the liveness at the start of each - successor block. We can miss changes in those sets if - we only compare the new live_at_end against the - previous one. */ - cond_local_set = cond_local_sets[bb->index]; - rescan = bitmap_intersect_p (new_live_at_end, cond_local_set); - } - - if (!rescan) - { - regset local_set; - - /* Find the set of changed bits. Take this opportunity - to notice that this set is empty and early out. */ - bitmap_xor (tmp, bb->il.rtl->global_live_at_end, new_live_at_end); - if (bitmap_empty_p (tmp)) - continue; - - /* If any of the changed bits overlap with local_sets[bb], - we'll have to rescan the block. */ - local_set = local_sets[bb->index]; - rescan = bitmap_intersect_p (tmp, local_set); - } - } - - /* Let our caller know that BB changed enough to require its - death notes updated. */ - if (blocks_out) - SET_BIT (blocks_out, bb->index); - - if (! rescan) - { - /* Add to live_at_start the set of all registers in - new_live_at_end that aren't in the old live_at_end. */ - - changed = bitmap_ior_and_compl_into (bb->il.rtl->global_live_at_start, - new_live_at_end, - bb->il.rtl->global_live_at_end); - COPY_REG_SET (bb->il.rtl->global_live_at_end, new_live_at_end); - if (! changed) - continue; - } - else - { - COPY_REG_SET (bb->il.rtl->global_live_at_end, new_live_at_end); - - /* Rescan the block insn by insn to turn (a copy of) live_at_end - into live_at_start. */ - propagate_block (bb, new_live_at_end, - local_sets[bb->index], - cond_local_sets[bb->index], - flags); - - /* If live_at start didn't change, no need to go farther. */ - if (REG_SET_EQUAL_P (bb->il.rtl->global_live_at_start, - new_live_at_end)) - continue; - - if (failure_strategy_required) - { - /* Get the list of registers that were removed from the - bb->global_live_at_start set. */ - bitmap_and_compl (tmp, bb->il.rtl->global_live_at_start, - new_live_at_end); - if (!bitmap_empty_p (tmp)) - { - bool pbb_changed; - basic_block pbb; - - /* It should not happen that one of registers we have - removed last time is disappears again before any other - register does. */ - pbb_changed = bitmap_ior_into (registers_made_dead, tmp); - gcc_assert (pbb_changed); - - /* Now remove the registers from all sets. */ - FOR_EACH_BB (pbb) - { - pbb_changed = false; - - pbb_changed - |= bitmap_and_compl_into - (pbb->il.rtl->global_live_at_start, - registers_made_dead); - pbb_changed - |= bitmap_and_compl_into - (pbb->il.rtl->global_live_at_end, - registers_made_dead); - if (!pbb_changed) - continue; - - /* Note the (possible) change. */ - if (blocks_out) - SET_BIT (blocks_out, pbb->index); - - /* Makes sure to really rescan the block. */ - if (local_sets[pbb->index]) - { - FREE_REG_SET (local_sets[pbb->index]); - FREE_REG_SET (cond_local_sets[pbb->index]); - local_sets[pbb->index] = 0; - } - - /* Add it to the queue. */ - if (pbb->aux == NULL) - { - *qtail++ = pbb; - if (qtail == qend) - qtail = queue; - pbb->aux = pbb; - } - } - continue; - } - } /* end of failure_strategy_required */ - - COPY_REG_SET (bb->il.rtl->global_live_at_start, new_live_at_end); - } - - /* Queue all predecessors of BB so that we may re-examine - their live_at_end. */ - FOR_EACH_EDGE (e, ei, bb->preds) - { - basic_block pb = e->src; - - gcc_assert ((e->flags & EDGE_FAKE) == 0); - - if (pb->aux == NULL) - { - *qtail++ = pb; - if (qtail == qend) - qtail = queue; - pb->aux = pb; - } - } - } - - FREE_REG_SET (tmp); - FREE_REG_SET (new_live_at_end); - FREE_REG_SET (invalidated_by_eh_edge); - FREE_REG_SET (registers_made_dead); - - if (blocks_out) - { - sbitmap_iterator sbi; - - EXECUTE_IF_SET_IN_SBITMAP (blocks_out, 0, i, sbi) - { - basic_block bb = BASIC_BLOCK (i); - FREE_REG_SET (local_sets[bb->index]); - FREE_REG_SET (cond_local_sets[bb->index]); - }; - } - else - { - FOR_EACH_BB (bb) - { - FREE_REG_SET (local_sets[bb->index]); - FREE_REG_SET (cond_local_sets[bb->index]); - } - } - - free (block_accesses); - free (queue); - free (cond_local_sets); - free (local_sets); -} - - -/* This structure is used to pass parameters to and from the - the function find_regno_partial(). It is used to pass in the - register number we are looking, as well as to return any rtx - we find. */ - -typedef struct { - unsigned regno_to_find; - rtx retval; -} find_regno_partial_param; - - -/* Find the rtx for the reg numbers specified in 'data' if it is - part of an expression which only uses part of the register. Return - it in the structure passed in. */ -static int -find_regno_partial (rtx *ptr, void *data) -{ - find_regno_partial_param *param = (find_regno_partial_param *)data; - unsigned reg = param->regno_to_find; - param->retval = NULL_RTX; - - if (*ptr == NULL_RTX) - return 0; - - switch (GET_CODE (*ptr)) - { - case ZERO_EXTRACT: - case SIGN_EXTRACT: - case STRICT_LOW_PART: - if (REG_P (XEXP (*ptr, 0)) && REGNO (XEXP (*ptr, 0)) == reg) - { - param->retval = XEXP (*ptr, 0); - return 1; - } - break; - - case SUBREG: - if (REG_P (SUBREG_REG (*ptr)) - && REGNO (SUBREG_REG (*ptr)) == reg) - { - param->retval = SUBREG_REG (*ptr); - return 1; - } - break; - - default: - break; - } - - return 0; -} - -/* Process all immediate successors of the entry block looking for pseudo - registers which are live on entry. Find all of those whose first - instance is a partial register reference of some kind, and initialize - them to 0 after the entry block. This will prevent bit sets within - registers whose value is unknown, and may contain some kind of sticky - bits we don't want. */ - -static int -initialize_uninitialized_subregs (void) -{ - rtx insn; - edge e; - unsigned reg, did_something = 0; - find_regno_partial_param param; - edge_iterator ei; - - FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs) - { - basic_block bb = e->dest; - regset map = bb->il.rtl->global_live_at_start; - reg_set_iterator rsi; - - EXECUTE_IF_SET_IN_REG_SET (map, FIRST_PSEUDO_REGISTER, reg, rsi) - { - int uid = REGNO_FIRST_UID (reg); - rtx i; - - /* Find an insn which mentions the register we are looking for. - Its preferable to have an instance of the register's rtl since - there may be various flags set which we need to duplicate. - If we can't find it, its probably an automatic whose initial - value doesn't matter, or hopefully something we don't care about. */ - for (i = get_insns (); i && INSN_UID (i) != uid; i = NEXT_INSN (i)) - ; - if (i != NULL_RTX) - { - /* Found the insn, now get the REG rtx, if we can. */ - param.regno_to_find = reg; - for_each_rtx (&i, find_regno_partial, ¶m); - if (param.retval != NULL_RTX) - { - start_sequence (); - emit_move_insn (param.retval, - CONST0_RTX (GET_MODE (param.retval))); - insn = get_insns (); - end_sequence (); - insert_insn_on_edge (insn, e); - did_something = 1; - } - } - } - } - - if (did_something) - commit_edge_insertions (); - return did_something; -} - - -/* Subroutines of life analysis. */ - -/* Allocate the permanent data structures that represent the results - of life analysis. */ - -static void -allocate_bb_life_data (void) -{ - basic_block bb; - - FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb) - { - if (bb->il.rtl->global_live_at_start) - { - CLEAR_REG_SET (bb->il.rtl->global_live_at_start); - CLEAR_REG_SET (bb->il.rtl->global_live_at_end); - } - else - { - bb->il.rtl->global_live_at_start = ALLOC_REG_SET (®_obstack); - bb->il.rtl->global_live_at_end = ALLOC_REG_SET (®_obstack); - } - } - - regs_live_at_setjmp = ALLOC_REG_SET (®_obstack); -} - -void -allocate_reg_life_data (void) -{ - int i; - - max_regno = max_reg_num (); - gcc_assert (!reg_deaths); - reg_deaths = XCNEWVEC (int, max_regno); - - /* Recalculate the register space, in case it has grown. Old style - vector oriented regsets would set regset_{size,bytes} here also. */ - allocate_reg_info (max_regno, FALSE, FALSE); - - /* Reset all the data we'll collect in propagate_block and its - subroutines. */ - for (i = 0; i < max_regno; i++) - { - REG_N_SETS (i) = 0; - REG_N_REFS (i) = 0; - REG_N_DEATHS (i) = 0; - REG_N_CALLS_CROSSED (i) = 0; - REG_N_THROWING_CALLS_CROSSED (i) = 0; - REG_LIVE_LENGTH (i) = 0; - REG_FREQ (i) = 0; - REG_BASIC_BLOCK (i) = REG_BLOCK_UNKNOWN; - } -} - -/* Delete dead instructions for propagate_block. */ - -static void -propagate_block_delete_insn (rtx insn) -{ - rtx inote = find_reg_note (insn, REG_LABEL, NULL_RTX); - - /* If the insn referred to a label, and that label was attached to - an ADDR_VEC, it's safe to delete the ADDR_VEC. In fact, it's - pretty much mandatory to delete it, because the ADDR_VEC may be - referencing labels that no longer exist. - - INSN may reference a deleted label, particularly when a jump - table has been optimized into a direct jump. There's no - real good way to fix up the reference to the deleted label - when the label is deleted, so we just allow it here. */ - - if (inote && LABEL_P (inote)) - { - rtx label = XEXP (inote, 0); - rtx next; - - /* The label may be forced if it has been put in the constant - pool. If that is the only use we must discard the table - jump following it, but not the label itself. */ - if (LABEL_NUSES (label) == 1 + LABEL_PRESERVE_P (label) - && (next = next_nonnote_insn (label)) != NULL - && JUMP_P (next) - && (GET_CODE (PATTERN (next)) == ADDR_VEC - || GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC)) - { - rtx pat = PATTERN (next); - int diff_vec_p = GET_CODE (pat) == ADDR_DIFF_VEC; - int len = XVECLEN (pat, diff_vec_p); - int i; - - for (i = 0; i < len; i++) - LABEL_NUSES (XEXP (XVECEXP (pat, diff_vec_p, i), 0))--; - - delete_insn_and_edges (next); - ndead++; - } - } - - delete_insn_and_edges (insn); - ndead++; -} - -/* Delete dead libcalls for propagate_block. Return the insn - before the libcall. */ - -static rtx -propagate_block_delete_libcall (rtx insn, rtx note) -{ - rtx first = XEXP (note, 0); - rtx before = PREV_INSN (first); - - delete_insn_chain_and_edges (first, insn); - ndead++; - return before; -} - -/* Update the life-status of regs for one insn. Return the previous insn. */ - -rtx -propagate_one_insn (struct propagate_block_info *pbi, rtx insn) -{ - rtx prev = PREV_INSN (insn); - int flags = pbi->flags; - int insn_is_dead = 0; - int libcall_is_dead = 0; - rtx note; - unsigned i; - - if (! INSN_P (insn)) - return prev; - - note = find_reg_note (insn, REG_RETVAL, NULL_RTX); - if (flags & PROP_SCAN_DEAD_CODE) - { - insn_is_dead = insn_dead_p (pbi, PATTERN (insn), 0, REG_NOTES (insn)); - libcall_is_dead = (insn_is_dead && note != 0 - && libcall_dead_p (pbi, note, insn)); - } - - /* If an instruction consists of just dead store(s) on final pass, - delete it. */ - if ((flags & PROP_KILL_DEAD_CODE) && insn_is_dead) - { - /* If we're trying to delete a prologue or epilogue instruction - that isn't flagged as possibly being dead, something is wrong. - But if we are keeping the stack pointer depressed, we might well - be deleting insns that are used to compute the amount to update - it by, so they are fine. */ - if (reload_completed - && !(TREE_CODE (TREE_TYPE (current_function_decl)) == FUNCTION_TYPE - && (TYPE_RETURNS_STACK_DEPRESSED - (TREE_TYPE (current_function_decl)))) - && (((HAVE_epilogue || HAVE_prologue) - && prologue_epilogue_contains (insn)) - || (HAVE_sibcall_epilogue - && sibcall_epilogue_contains (insn))) - && find_reg_note (insn, REG_MAYBE_DEAD, NULL_RTX) == 0) - fatal_insn ("Attempt to delete prologue/epilogue insn:", insn); - - /* Record sets. Do this even for dead instructions, since they - would have killed the values if they hadn't been deleted. To - be consistent, we also have to emit a clobber when we delete - an insn that clobbers a live register. */ - pbi->flags |= PROP_DEAD_INSN; - mark_set_regs (pbi, PATTERN (insn), insn); - pbi->flags &= ~PROP_DEAD_INSN; - - /* CC0 is now known to be dead. Either this insn used it, - in which case it doesn't anymore, or clobbered it, - so the next insn can't use it. */ - pbi->cc0_live = 0; - - if (libcall_is_dead) - prev = propagate_block_delete_libcall (insn, note); - else - { - - /* If INSN contains a RETVAL note and is dead, but the libcall - as a whole is not dead, then we want to remove INSN, but - not the whole libcall sequence. - - However, we need to also remove the dangling REG_LIBCALL - note so that we do not have mis-matched LIBCALL/RETVAL - notes. In theory we could find a new location for the - REG_RETVAL note, but it hardly seems worth the effort. - - NOTE at this point will be the RETVAL note if it exists. */ - if (note) - { - rtx libcall_note; - - libcall_note - = find_reg_note (XEXP (note, 0), REG_LIBCALL, NULL_RTX); - remove_note (XEXP (note, 0), libcall_note); - } - - /* Similarly if INSN contains a LIBCALL note, remove the - dangling REG_RETVAL note. */ - note = find_reg_note (insn, REG_LIBCALL, NULL_RTX); - if (note) - { - rtx retval_note; - - retval_note - = find_reg_note (XEXP (note, 0), REG_RETVAL, NULL_RTX); - remove_note (XEXP (note, 0), retval_note); - } - - /* Now delete INSN. */ - propagate_block_delete_insn (insn); - } - - return prev; - } - - /* See if this is an increment or decrement that can be merged into - a following memory address. */ -#ifdef AUTO_INC_DEC - { - rtx x = single_set (insn); - - /* Does this instruction increment or decrement a register? */ - if ((flags & PROP_AUTOINC) - && x != 0 - && REG_P (SET_DEST (x)) - && (GET_CODE (SET_SRC (x)) == PLUS - || GET_CODE (SET_SRC (x)) == MINUS) - && XEXP (SET_SRC (x), 0) == SET_DEST (x) - && GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT - /* Ok, look for a following memory ref we can combine with. - If one is found, change the memory ref to a PRE_INC - or PRE_DEC, cancel this insn, and return 1. - Return 0 if nothing has been done. */ - && try_pre_increment_1 (pbi, insn)) - return prev; - } -#endif /* AUTO_INC_DEC */ - - CLEAR_REG_SET (pbi->new_set); - - /* If this is not the final pass, and this insn is copying the value of - a library call and it's dead, don't scan the insns that perform the - library call, so that the call's arguments are not marked live. */ - if (libcall_is_dead) - { - /* Record the death of the dest reg. */ - mark_set_regs (pbi, PATTERN (insn), insn); - - insn = XEXP (note, 0); - return PREV_INSN (insn); - } - else if (GET_CODE (PATTERN (insn)) == SET - && SET_DEST (PATTERN (insn)) == stack_pointer_rtx - && GET_CODE (SET_SRC (PATTERN (insn))) == PLUS - && XEXP (SET_SRC (PATTERN (insn)), 0) == stack_pointer_rtx - && GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 1)) == CONST_INT) - { - /* We have an insn to pop a constant amount off the stack. - (Such insns use PLUS regardless of the direction of the stack, - and any insn to adjust the stack by a constant is always a pop - or part of a push.) - These insns, if not dead stores, have no effect on life, though - they do have an effect on the memory stores we are tracking. */ - invalidate_mems_from_set (pbi, stack_pointer_rtx); - /* Still, we need to update local_set, lest ifcvt.c:dead_or_predicable - concludes that the stack pointer is not modified. */ - mark_set_regs (pbi, PATTERN (insn), insn); - } - else - { - /* Any regs live at the time of a call instruction must not go - in a register clobbered by calls. Find all regs now live and - record this for them. */ - - if (CALL_P (insn) && (flags & PROP_REG_INFO)) - { - reg_set_iterator rsi; - EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i, rsi) - REG_N_CALLS_CROSSED (i)++; - if (can_throw_internal (insn)) - EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i, rsi) - REG_N_THROWING_CALLS_CROSSED (i)++; - } - - /* Record sets. Do this even for dead instructions, since they - would have killed the values if they hadn't been deleted. */ - mark_set_regs (pbi, PATTERN (insn), insn); - - if (CALL_P (insn)) - { - regset live_at_end; - bool sibcall_p; - rtx note, cond; - int i; - - cond = NULL_RTX; - if (GET_CODE (PATTERN (insn)) == COND_EXEC) - cond = COND_EXEC_TEST (PATTERN (insn)); - - /* Non-constant calls clobber memory, constant calls do not - clobber memory, though they may clobber outgoing arguments - on the stack. */ - if (! CONST_OR_PURE_CALL_P (insn)) - { - free_EXPR_LIST_list (&pbi->mem_set_list); - pbi->mem_set_list_len = 0; - } - else - invalidate_mems_from_set (pbi, stack_pointer_rtx); - - /* There may be extra registers to be clobbered. */ - for (note = CALL_INSN_FUNCTION_USAGE (insn); - note; - note = XEXP (note, 1)) - if (GET_CODE (XEXP (note, 0)) == CLOBBER) - mark_set_1 (pbi, CLOBBER, XEXP (XEXP (note, 0), 0), - cond, insn, pbi->flags); - - /* Calls change all call-used and global registers; sibcalls do not - clobber anything that must be preserved at end-of-function, - except for return values. */ - - sibcall_p = SIBLING_CALL_P (insn); - live_at_end = EXIT_BLOCK_PTR->il.rtl->global_live_at_start; - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i) - && ! (sibcall_p - && REGNO_REG_SET_P (live_at_end, i) - && ! refers_to_regno_p (i, i+1, - current_function_return_rtx, - (rtx *) 0))) - { - enum rtx_code code = global_regs[i] ? SET : CLOBBER; - /* We do not want REG_UNUSED notes for these registers. */ - mark_set_1 (pbi, code, regno_reg_rtx[i], cond, insn, - pbi->flags & ~(PROP_DEATH_NOTES | PROP_REG_INFO)); - } - } - - /* If an insn doesn't use CC0, it becomes dead since we assume - that every insn clobbers it. So show it dead here; - mark_used_regs will set it live if it is referenced. */ - pbi->cc0_live = 0; - - /* Record uses. */ - if (! insn_is_dead) - mark_used_regs (pbi, PATTERN (insn), NULL_RTX, insn); - - /* Sometimes we may have inserted something before INSN (such as a move) - when we make an auto-inc. So ensure we will scan those insns. */ -#ifdef AUTO_INC_DEC - prev = PREV_INSN (insn); -#endif - - if (! insn_is_dead && CALL_P (insn)) - { - int i; - rtx note, cond; - - cond = NULL_RTX; - if (GET_CODE (PATTERN (insn)) == COND_EXEC) - cond = COND_EXEC_TEST (PATTERN (insn)); - - /* Calls use their arguments, and may clobber memory which - address involves some register. */ - for (note = CALL_INSN_FUNCTION_USAGE (insn); - note; - note = XEXP (note, 1)) - /* We find USE or CLOBBER entities in a FUNCTION_USAGE list: both - of which mark_used_regs knows how to handle. */ - mark_used_regs (pbi, XEXP (XEXP (note, 0), 0), cond, insn); - - /* The stack ptr is used (honorarily) by a CALL insn. */ - if ((flags & PROP_REG_INFO) - && !REGNO_REG_SET_P (pbi->reg_live, STACK_POINTER_REGNUM)) - reg_deaths[STACK_POINTER_REGNUM] = pbi->insn_num; - SET_REGNO_REG_SET (pbi->reg_live, STACK_POINTER_REGNUM); - - /* Calls may also reference any of the global registers, - so they are made live. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (global_regs[i]) - mark_used_reg (pbi, regno_reg_rtx[i], cond, insn); - } - } - - pbi->insn_num++; - - return prev; -} - -/* Initialize a propagate_block_info struct for public consumption. - Note that the structure itself is opaque to this file, but that - the user can use the regsets provided here. */ - -struct propagate_block_info * -init_propagate_block_info (basic_block bb, regset live, regset local_set, - regset cond_local_set, int flags) -{ - struct propagate_block_info *pbi = XNEW (struct propagate_block_info); - - pbi->bb = bb; - pbi->reg_live = live; - pbi->mem_set_list = NULL_RTX; - pbi->mem_set_list_len = 0; - pbi->local_set = local_set; - pbi->cond_local_set = cond_local_set; - pbi->cc0_live = 0; - pbi->flags = flags; - pbi->insn_num = 0; - - if (flags & (PROP_LOG_LINKS | PROP_AUTOINC)) - pbi->reg_next_use = XCNEWVEC (rtx, max_reg_num ()); - else - pbi->reg_next_use = NULL; - - pbi->new_set = BITMAP_ALLOC (NULL); - -#ifdef HAVE_conditional_execution - pbi->reg_cond_dead = splay_tree_new (splay_tree_compare_ints, NULL, - free_reg_cond_life_info); - pbi->reg_cond_reg = BITMAP_ALLOC (NULL); - - /* If this block ends in a conditional branch, for each register - live from one side of the branch and not the other, record the - register as conditionally dead. */ - if (JUMP_P (BB_END (bb)) - && any_condjump_p (BB_END (bb))) - { - regset diff = ALLOC_REG_SET (®_obstack); - basic_block bb_true, bb_false; - unsigned i; - - /* Identify the successor blocks. */ - bb_true = EDGE_SUCC (bb, 0)->dest; - if (!single_succ_p (bb)) - { - bb_false = EDGE_SUCC (bb, 1)->dest; - - if (EDGE_SUCC (bb, 0)->flags & EDGE_FALLTHRU) - { - basic_block t = bb_false; - bb_false = bb_true; - bb_true = t; - } - else - gcc_assert (EDGE_SUCC (bb, 1)->flags & EDGE_FALLTHRU); - } - else - { - /* This can happen with a conditional jump to the next insn. */ - gcc_assert (JUMP_LABEL (BB_END (bb)) == BB_HEAD (bb_true)); - - /* Simplest way to do nothing. */ - bb_false = bb_true; - } - - /* Compute which register lead different lives in the successors. */ - bitmap_xor (diff, bb_true->il.rtl->global_live_at_start, - bb_false->il.rtl->global_live_at_start); - - if (!bitmap_empty_p (diff)) - { - /* Extract the condition from the branch. */ - rtx set_src = SET_SRC (pc_set (BB_END (bb))); - rtx cond_true = XEXP (set_src, 0); - rtx reg = XEXP (cond_true, 0); - enum rtx_code inv_cond; - - if (GET_CODE (reg) == SUBREG) - reg = SUBREG_REG (reg); - - /* We can only track conditional lifetimes if the condition is - in the form of a reversible comparison of a register against - zero. If the condition is more complex than that, then it is - safe not to record any information. */ - inv_cond = reversed_comparison_code (cond_true, BB_END (bb)); - if (inv_cond != UNKNOWN - && REG_P (reg) - && XEXP (cond_true, 1) == const0_rtx) - { - rtx cond_false - = gen_rtx_fmt_ee (inv_cond, - GET_MODE (cond_true), XEXP (cond_true, 0), - XEXP (cond_true, 1)); - reg_set_iterator rsi; - - if (GET_CODE (XEXP (set_src, 1)) == PC) - { - rtx t = cond_false; - cond_false = cond_true; - cond_true = t; - } - - SET_REGNO_REG_SET (pbi->reg_cond_reg, REGNO (reg)); - - /* For each such register, mark it conditionally dead. */ - EXECUTE_IF_SET_IN_REG_SET (diff, 0, i, rsi) - { - struct reg_cond_life_info *rcli; - rtx cond; - - rcli = XNEW (struct reg_cond_life_info); - - if (REGNO_REG_SET_P (bb_true->il.rtl->global_live_at_start, - i)) - cond = cond_false; - else - cond = cond_true; - rcli->condition = cond; - rcli->stores = const0_rtx; - rcli->orig_condition = cond; - - splay_tree_insert (pbi->reg_cond_dead, i, - (splay_tree_value) rcli); - } - } - } - - FREE_REG_SET (diff); - } -#endif - - /* If this block has no successors, any stores to the frame that aren't - used later in the block are dead. So make a pass over the block - recording any such that are made and show them dead at the end. We do - a very conservative and simple job here. */ - if (optimize - && ! (TREE_CODE (TREE_TYPE (current_function_decl)) == FUNCTION_TYPE - && (TYPE_RETURNS_STACK_DEPRESSED - (TREE_TYPE (current_function_decl)))) - && (flags & PROP_SCAN_DEAD_STORES) - && (EDGE_COUNT (bb->succs) == 0 - || (single_succ_p (bb) - && single_succ (bb) == EXIT_BLOCK_PTR - && ! current_function_calls_eh_return))) - { - rtx insn, set; - for (insn = BB_END (bb); insn != BB_HEAD (bb); insn = PREV_INSN (insn)) - if (NONJUMP_INSN_P (insn) - && (set = single_set (insn)) - && MEM_P (SET_DEST (set))) - { - rtx mem = SET_DEST (set); - rtx canon_mem = canon_rtx (mem); - - if (XEXP (canon_mem, 0) == frame_pointer_rtx - || (GET_CODE (XEXP (canon_mem, 0)) == PLUS - && XEXP (XEXP (canon_mem, 0), 0) == frame_pointer_rtx - && GET_CODE (XEXP (XEXP (canon_mem, 0), 1)) == CONST_INT)) - add_to_mem_set_list (pbi, canon_mem); - } - } - - return pbi; -} - -/* Release a propagate_block_info struct. */ - -void -free_propagate_block_info (struct propagate_block_info *pbi) -{ - free_EXPR_LIST_list (&pbi->mem_set_list); - - BITMAP_FREE (pbi->new_set); - -#ifdef HAVE_conditional_execution - splay_tree_delete (pbi->reg_cond_dead); - BITMAP_FREE (pbi->reg_cond_reg); -#endif - - if (pbi->flags & PROP_REG_INFO) - { - int num = pbi->insn_num; - unsigned i; - reg_set_iterator rsi; - - EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i, rsi) - { - REG_LIVE_LENGTH (i) += num - reg_deaths[i]; - reg_deaths[i] = 0; - } - } - if (pbi->reg_next_use) - free (pbi->reg_next_use); - - free (pbi); -} - -/* Compute the registers live at the beginning of a basic block BB from - those live at the end. - - When called, REG_LIVE contains those live at the end. On return, it - contains those live at the beginning. - - LOCAL_SET, if non-null, will be set with all registers killed - unconditionally by this basic block. - Likewise, COND_LOCAL_SET, if non-null, will be set with all registers - killed conditionally by this basic block. If there is any unconditional - set of a register, then the corresponding bit will be set in LOCAL_SET - and cleared in COND_LOCAL_SET. - It is valid for LOCAL_SET and COND_LOCAL_SET to be the same set. In this - case, the resulting set will be equal to the union of the two sets that - would otherwise be computed. - - Return nonzero if an INSN is deleted (i.e. by dead code removal). */ - -int -propagate_block (basic_block bb, regset live, regset local_set, - regset cond_local_set, int flags) -{ - struct propagate_block_info *pbi; - rtx insn, prev; - int changed; - - pbi = init_propagate_block_info (bb, live, local_set, cond_local_set, flags); - - if (flags & PROP_REG_INFO) - { - unsigned i; - reg_set_iterator rsi; - - /* Process the regs live at the end of the block. - Mark them as not local to any one basic block. */ - EXECUTE_IF_SET_IN_REG_SET (live, 0, i, rsi) - REG_BASIC_BLOCK (i) = REG_BLOCK_GLOBAL; - } - - /* Scan the block an insn at a time from end to beginning. */ - - changed = 0; - for (insn = BB_END (bb); ; insn = prev) - { - /* If this is a call to `setjmp' et al, warn if any - non-volatile datum is live. */ - if ((flags & PROP_REG_INFO) - && CALL_P (insn) - && find_reg_note (insn, REG_SETJMP, NULL)) - IOR_REG_SET (regs_live_at_setjmp, pbi->reg_live); - - prev = propagate_one_insn (pbi, insn); - if (!prev) - changed |= insn != get_insns (); - else - changed |= NEXT_INSN (prev) != insn; - - if (insn == BB_HEAD (bb)) - break; - } - -#ifdef EH_RETURN_DATA_REGNO - if (bb_has_eh_pred (bb)) - { - unsigned int i; - for (i = 0; ; ++i) - { - unsigned regno = EH_RETURN_DATA_REGNO (i); - if (regno == INVALID_REGNUM) - break; - if (pbi->local_set) - { - CLEAR_REGNO_REG_SET (pbi->cond_local_set, regno); - SET_REGNO_REG_SET (pbi->local_set, regno); - } - if (REGNO_REG_SET_P (pbi->reg_live, regno)) - SET_REGNO_REG_SET (pbi->new_set, regno); - - regs_ever_live[regno] = 1; - } - } -#endif - - free_propagate_block_info (pbi); - - return changed; -} - -/* Return 1 if X (the body of an insn, or part of it) is just dead stores - (SET expressions whose destinations are registers dead after the insn). - NEEDED is the regset that says which regs are alive after the insn. - - Unless CALL_OK is nonzero, an insn is needed if it contains a CALL. - - If X is the entire body of an insn, NOTES contains the reg notes - pertaining to the insn. */ - -static int -insn_dead_p (struct propagate_block_info *pbi, rtx x, int call_ok, - rtx notes ATTRIBUTE_UNUSED) -{ - enum rtx_code code = GET_CODE (x); - - /* Don't eliminate insns that may trap. */ - if (flag_non_call_exceptions && may_trap_p (x)) - return 0; - -#ifdef AUTO_INC_DEC - /* As flow is invoked after combine, we must take existing AUTO_INC - expressions into account. */ - for (; notes; notes = XEXP (notes, 1)) - { - if (REG_NOTE_KIND (notes) == REG_INC) - { - int regno = REGNO (XEXP (notes, 0)); - - /* Don't delete insns to set global regs. */ - if ((regno < FIRST_PSEUDO_REGISTER && global_regs[regno]) - || REGNO_REG_SET_P (pbi->reg_live, regno)) - return 0; - } - } -#endif - - /* If setting something that's a reg or part of one, - see if that register's altered value will be live. */ - - if (code == SET) - { - rtx r = SET_DEST (x); - -#ifdef HAVE_cc0 - if (GET_CODE (r) == CC0) - return ! pbi->cc0_live; -#endif - - /* A SET that is a subroutine call cannot be dead. */ - if (GET_CODE (SET_SRC (x)) == CALL) - { - if (! call_ok) - return 0; - } - - /* Don't eliminate loads from volatile memory or volatile asms. */ - else if (volatile_refs_p (SET_SRC (x))) - return 0; - - if (MEM_P (r)) - { - rtx temp, canon_r; - - if (MEM_VOLATILE_P (r) || GET_MODE (r) == BLKmode) - return 0; - - canon_r = canon_rtx (r); - - /* Walk the set of memory locations we are currently tracking - and see if one is an identical match to this memory location. - If so, this memory write is dead (remember, we're walking - backwards from the end of the block to the start). Since - rtx_equal_p does not check the alias set or flags, we also - must have the potential for them to conflict (anti_dependence). */ - for (temp = pbi->mem_set_list; temp != 0; temp = XEXP (temp, 1)) - if (anti_dependence (r, XEXP (temp, 0))) - { - rtx mem = XEXP (temp, 0); - - if (rtx_equal_p (XEXP (canon_r, 0), XEXP (mem, 0)) - && (GET_MODE_SIZE (GET_MODE (canon_r)) - <= GET_MODE_SIZE (GET_MODE (mem)))) - return 1; - -#ifdef AUTO_INC_DEC - /* Check if memory reference matches an auto increment. Only - post increment/decrement or modify are valid. */ - if (GET_MODE (mem) == GET_MODE (r) - && (GET_CODE (XEXP (mem, 0)) == POST_DEC - || GET_CODE (XEXP (mem, 0)) == POST_INC - || GET_CODE (XEXP (mem, 0)) == POST_MODIFY) - && GET_MODE (XEXP (mem, 0)) == GET_MODE (r) - && rtx_equal_p (XEXP (XEXP (mem, 0), 0), XEXP (r, 0))) - return 1; -#endif - } - } - else - { - while (GET_CODE (r) == SUBREG - || GET_CODE (r) == STRICT_LOW_PART - || GET_CODE (r) == ZERO_EXTRACT) - r = XEXP (r, 0); - - if (REG_P (r)) - { - int regno = REGNO (r); - - /* Obvious. */ - if (REGNO_REG_SET_P (pbi->reg_live, regno)) - return 0; - - /* If this is a hard register, verify that subsequent - words are not needed. */ - if (regno < FIRST_PSEUDO_REGISTER) - { - int n = hard_regno_nregs[regno][GET_MODE (r)]; - - while (--n > 0) - if (REGNO_REG_SET_P (pbi->reg_live, regno+n)) - return 0; - } - - /* Don't delete insns to set global regs. */ - if (regno < FIRST_PSEUDO_REGISTER && global_regs[regno]) - return 0; - - /* Make sure insns to set the stack pointer aren't deleted. */ - if (regno == STACK_POINTER_REGNUM) - return 0; - - /* ??? These bits might be redundant with the force live bits - in calculate_global_regs_live. We would delete from - sequential sets; whether this actually affects real code - for anything but the stack pointer I don't know. */ - /* Make sure insns to set the frame pointer aren't deleted. */ - if (regno == FRAME_POINTER_REGNUM - && (! reload_completed || frame_pointer_needed)) - return 0; -#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM - if (regno == HARD_FRAME_POINTER_REGNUM - && (! reload_completed || frame_pointer_needed)) - return 0; -#endif - -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - /* Make sure insns to set arg pointer are never deleted - (if the arg pointer isn't fixed, there will be a USE - for it, so we can treat it normally). */ - if (regno == ARG_POINTER_REGNUM && fixed_regs[regno]) - return 0; -#endif - - /* Otherwise, the set is dead. */ - return 1; - } - } - } - - /* If performing several activities, insn is dead if each activity - is individually dead. Also, CLOBBERs and USEs can be ignored; a - CLOBBER or USE that's inside a PARALLEL doesn't make the insn - worth keeping. */ - else if (code == PARALLEL) - { - int i = XVECLEN (x, 0); - - for (i--; i >= 0; i--) - if (GET_CODE (XVECEXP (x, 0, i)) != CLOBBER - && GET_CODE (XVECEXP (x, 0, i)) != USE - && ! insn_dead_p (pbi, XVECEXP (x, 0, i), call_ok, NULL_RTX)) - return 0; - - return 1; - } - - /* A CLOBBER of a pseudo-register that is dead serves no purpose. That - is not necessarily true for hard registers until after reload. */ - else if (code == CLOBBER) - { - if (REG_P (XEXP (x, 0)) - && (REGNO (XEXP (x, 0)) >= FIRST_PSEUDO_REGISTER - || reload_completed) - && ! REGNO_REG_SET_P (pbi->reg_live, REGNO (XEXP (x, 0)))) - return 1; - } - - /* ??? A base USE is a historical relic. It ought not be needed anymore. - Instances where it is still used are either (1) temporary and the USE - escaped the pass, (2) cruft and the USE need not be emitted anymore, - or (3) hiding bugs elsewhere that are not properly representing data - flow. */ - - return 0; -} - -/* If INSN is the last insn in a libcall, and assuming INSN is dead, - return 1 if the entire library call is dead. - This is true if INSN copies a register (hard or pseudo) - and if the hard return reg of the call insn is dead. - (The caller should have tested the destination of the SET inside - INSN already for death.) - - If this insn doesn't just copy a register, then we don't - have an ordinary libcall. In that case, cse could not have - managed to substitute the source for the dest later on, - so we can assume the libcall is dead. - - PBI is the block info giving pseudoregs live before this insn. - NOTE is the REG_RETVAL note of the insn. */ - -static int -libcall_dead_p (struct propagate_block_info *pbi, rtx note, rtx insn) -{ - rtx x = single_set (insn); - - if (x) - { - rtx r = SET_SRC (x); - - if (REG_P (r) || GET_CODE (r) == SUBREG) - { - rtx call = XEXP (note, 0); - rtx call_pat; - int i; - - /* Find the call insn. */ - while (call != insn && !CALL_P (call)) - call = NEXT_INSN (call); - - /* If there is none, do nothing special, - since ordinary death handling can understand these insns. */ - if (call == insn) - return 0; - - /* See if the hard reg holding the value is dead. - If this is a PARALLEL, find the call within it. */ - call_pat = PATTERN (call); - if (GET_CODE (call_pat) == PARALLEL) - { - for (i = XVECLEN (call_pat, 0) - 1; i >= 0; i--) - if (GET_CODE (XVECEXP (call_pat, 0, i)) == SET - && GET_CODE (SET_SRC (XVECEXP (call_pat, 0, i))) == CALL) - break; - - /* This may be a library call that is returning a value - via invisible pointer. Do nothing special, since - ordinary death handling can understand these insns. */ - if (i < 0) - return 0; - - call_pat = XVECEXP (call_pat, 0, i); - } - - if (! insn_dead_p (pbi, call_pat, 1, REG_NOTES (call))) - return 0; - - while ((insn = PREV_INSN (insn)) != call) - { - if (! INSN_P (insn)) - continue; - if (! insn_dead_p (pbi, PATTERN (insn), 0, REG_NOTES (insn))) - return 0; - } - return 1; - } - } - return 0; -} - -/* 1 if register REGNO was alive at a place where `setjmp' was called - and was set more than once or is an argument. - Such regs may be clobbered by `longjmp'. */ - -int -regno_clobbered_at_setjmp (int regno) -{ - if (n_basic_blocks == NUM_FIXED_BLOCKS) - return 0; - - return ((REG_N_SETS (regno) > 1 - || REGNO_REG_SET_P (ENTRY_BLOCK_PTR->il.rtl->global_live_at_end, - regno)) - && REGNO_REG_SET_P (regs_live_at_setjmp, regno)); -} - -/* Add MEM to PBI->MEM_SET_LIST. MEM should be canonical. Respect the - maximal list size; look for overlaps in mode and select the largest. */ -static void -add_to_mem_set_list (struct propagate_block_info *pbi, rtx mem) -{ - rtx i; - - /* We don't know how large a BLKmode store is, so we must not - take them into consideration. */ - if (GET_MODE (mem) == BLKmode) - return; - - for (i = pbi->mem_set_list; i ; i = XEXP (i, 1)) - { - rtx e = XEXP (i, 0); - if (rtx_equal_p (XEXP (mem, 0), XEXP (e, 0))) - { - if (GET_MODE_SIZE (GET_MODE (mem)) > GET_MODE_SIZE (GET_MODE (e))) - { -#ifdef AUTO_INC_DEC - /* If we must store a copy of the mem, we can just modify - the mode of the stored copy. */ - if (pbi->flags & PROP_AUTOINC) - PUT_MODE (e, GET_MODE (mem)); - else -#endif - XEXP (i, 0) = mem; - } - return; - } - } - - if (pbi->mem_set_list_len < PARAM_VALUE (PARAM_MAX_FLOW_MEMORY_LOCATIONS)) - { -#ifdef AUTO_INC_DEC - /* Store a copy of mem, otherwise the address may be - scrogged by find_auto_inc. */ - if (pbi->flags & PROP_AUTOINC) - mem = shallow_copy_rtx (mem); -#endif - pbi->mem_set_list = alloc_EXPR_LIST (0, mem, pbi->mem_set_list); - pbi->mem_set_list_len++; - } -} - -/* INSN references memory, possibly using autoincrement addressing modes. - Find any entries on the mem_set_list that need to be invalidated due - to an address change. */ - -static int -invalidate_mems_from_autoinc (rtx *px, void *data) -{ - rtx x = *px; - struct propagate_block_info *pbi = data; - - if (GET_RTX_CLASS (GET_CODE (x)) == RTX_AUTOINC) - { - invalidate_mems_from_set (pbi, XEXP (x, 0)); - return -1; - } - - return 0; -} - -/* EXP is a REG or MEM. Remove any dependent entries from - pbi->mem_set_list. */ - -static void -invalidate_mems_from_set (struct propagate_block_info *pbi, rtx exp) -{ - rtx temp = pbi->mem_set_list; - rtx prev = NULL_RTX; - rtx next; - - while (temp) - { - next = XEXP (temp, 1); - if ((REG_P (exp) && reg_overlap_mentioned_p (exp, XEXP (temp, 0))) - /* When we get an EXP that is a mem here, we want to check if EXP - overlaps the *address* of any of the mems in the list (i.e. not - whether the mems actually overlap; that's done elsewhere). */ - || (MEM_P (exp) - && reg_overlap_mentioned_p (exp, XEXP (XEXP (temp, 0), 0)))) - { - /* Splice this entry out of the list. */ - if (prev) - XEXP (prev, 1) = next; - else - pbi->mem_set_list = next; - free_EXPR_LIST_node (temp); - pbi->mem_set_list_len--; - } - else - prev = temp; - temp = next; - } -} - -/* Process the registers that are set within X. Their bits are set to - 1 in the regset DEAD, because they are dead prior to this insn. - - If INSN is nonzero, it is the insn being processed. - - FLAGS is the set of operations to perform. */ - -static void -mark_set_regs (struct propagate_block_info *pbi, rtx x, rtx insn) -{ - rtx cond = NULL_RTX; - rtx link; - enum rtx_code code; - int flags = pbi->flags; - - if (insn) - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - { - if (REG_NOTE_KIND (link) == REG_INC) - mark_set_1 (pbi, SET, XEXP (link, 0), - (GET_CODE (x) == COND_EXEC - ? COND_EXEC_TEST (x) : NULL_RTX), - insn, flags); - } - retry: - switch (code = GET_CODE (x)) - { - case SET: - if (GET_CODE (XEXP (x, 1)) == ASM_OPERANDS) - flags |= PROP_ASM_SCAN; - /* Fall through */ - case CLOBBER: - mark_set_1 (pbi, code, SET_DEST (x), cond, insn, flags); - return; - - case COND_EXEC: - cond = COND_EXEC_TEST (x); - x = COND_EXEC_CODE (x); - goto retry; - - case PARALLEL: - { - int i; - - /* We must scan forwards. If we have an asm, we need to set - the PROP_ASM_SCAN flag before scanning the clobbers. */ - for (i = 0; i < XVECLEN (x, 0); i++) - { - rtx sub = XVECEXP (x, 0, i); - switch (code = GET_CODE (sub)) - { - case COND_EXEC: - gcc_assert (!cond); - - cond = COND_EXEC_TEST (sub); - sub = COND_EXEC_CODE (sub); - if (GET_CODE (sub) == SET) - goto mark_set; - if (GET_CODE (sub) == CLOBBER) - goto mark_clob; - break; - - case SET: - mark_set: - if (GET_CODE (XEXP (sub, 1)) == ASM_OPERANDS) - flags |= PROP_ASM_SCAN; - /* Fall through */ - case CLOBBER: - mark_clob: - mark_set_1 (pbi, code, SET_DEST (sub), cond, insn, flags); - break; - - case ASM_OPERANDS: - flags |= PROP_ASM_SCAN; - break; - - default: - break; - } - } - break; - } - - default: - break; - } -} - -/* Process a single set, which appears in INSN. REG (which may not - actually be a REG, it may also be a SUBREG, PARALLEL, etc.) is - being set using the CODE (which may be SET, CLOBBER, or COND_EXEC). - If the set is conditional (because it appear in a COND_EXEC), COND - will be the condition. */ - -static void -mark_set_1 (struct propagate_block_info *pbi, enum rtx_code code, rtx reg, rtx cond, rtx insn, int flags) -{ - int regno_first = -1, regno_last = -1; - unsigned long not_dead = 0; - int i; - - /* Modifying just one hardware register of a multi-reg value or just a - byte field of a register does not mean the value from before this insn - is now dead. Of course, if it was dead after it's unused now. */ - - switch (GET_CODE (reg)) - { - case PARALLEL: - /* Some targets place small structures in registers for return values of - functions. We have to detect this case specially here to get correct - flow information. */ - for (i = XVECLEN (reg, 0) - 1; i >= 0; i--) - if (XEXP (XVECEXP (reg, 0, i), 0) != 0) - mark_set_1 (pbi, code, XEXP (XVECEXP (reg, 0, i), 0), cond, insn, - flags); - return; - - case SIGN_EXTRACT: - /* SIGN_EXTRACT cannot be an lvalue. */ - gcc_unreachable (); - - case ZERO_EXTRACT: - case STRICT_LOW_PART: - /* ??? Assumes STRICT_LOW_PART not used on multi-word registers. */ - do - reg = XEXP (reg, 0); - while (GET_CODE (reg) == SUBREG - || GET_CODE (reg) == ZERO_EXTRACT - || GET_CODE (reg) == STRICT_LOW_PART); - if (MEM_P (reg)) - break; - not_dead = (unsigned long) REGNO_REG_SET_P (pbi->reg_live, REGNO (reg)); - /* Fall through. */ - - case REG: - regno_last = regno_first = REGNO (reg); - if (regno_first < FIRST_PSEUDO_REGISTER) - regno_last += hard_regno_nregs[regno_first][GET_MODE (reg)] - 1; - break; - - case SUBREG: - if (REG_P (SUBREG_REG (reg))) - { - enum machine_mode outer_mode = GET_MODE (reg); - enum machine_mode inner_mode = GET_MODE (SUBREG_REG (reg)); - - /* Identify the range of registers affected. This is moderately - tricky for hard registers. See alter_subreg. */ - - regno_last = regno_first = REGNO (SUBREG_REG (reg)); - if (regno_first < FIRST_PSEUDO_REGISTER) - { - regno_first += subreg_regno_offset (regno_first, inner_mode, - SUBREG_BYTE (reg), - outer_mode); - regno_last = regno_first + subreg_nregs (reg) - 1; - - /* Since we've just adjusted the register number ranges, make - sure REG matches. Otherwise some_was_live will be clear - when it shouldn't have been, and we'll create incorrect - REG_UNUSED notes. */ - reg = gen_rtx_REG (outer_mode, regno_first); - } - else - { - /* If the number of words in the subreg is less than the number - of words in the full register, we have a well-defined partial - set. Otherwise the high bits are undefined. - - This is only really applicable to pseudos, since we just took - care of multi-word hard registers. */ - if (((GET_MODE_SIZE (outer_mode) - + UNITS_PER_WORD - 1) / UNITS_PER_WORD) - < ((GET_MODE_SIZE (inner_mode) - + UNITS_PER_WORD - 1) / UNITS_PER_WORD)) - not_dead = (unsigned long) REGNO_REG_SET_P (pbi->reg_live, - regno_first); - - reg = SUBREG_REG (reg); - } - } - else - reg = SUBREG_REG (reg); - break; - - default: - break; - } - - /* If this set is a MEM, then it kills any aliased writes and any - other MEMs which use it. - If this set is a REG, then it kills any MEMs which use the reg. */ - if (optimize && (flags & PROP_SCAN_DEAD_STORES)) - { - if (REG_P (reg) || MEM_P (reg)) - invalidate_mems_from_set (pbi, reg); - - /* If the memory reference had embedded side effects (autoincrement - address modes) then we may need to kill some entries on the - memory set list. */ - if (insn && MEM_P (reg)) - for_each_rtx (&PATTERN (insn), invalidate_mems_from_autoinc, pbi); - - if (MEM_P (reg) && ! side_effects_p (reg) - /* ??? With more effort we could track conditional memory life. */ - && ! cond) - add_to_mem_set_list (pbi, canon_rtx (reg)); - } - - if (REG_P (reg) - && ! (regno_first == FRAME_POINTER_REGNUM - && (! reload_completed || frame_pointer_needed)) -#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM - && ! (regno_first == HARD_FRAME_POINTER_REGNUM - && (! reload_completed || frame_pointer_needed)) -#endif -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - && ! (regno_first == ARG_POINTER_REGNUM && fixed_regs[regno_first]) -#endif - ) - { - int some_was_live = 0, some_was_dead = 0; - - for (i = regno_first; i <= regno_last; ++i) - { - int needed_regno = REGNO_REG_SET_P (pbi->reg_live, i); - if (pbi->local_set) - { - /* Order of the set operation matters here since both - sets may be the same. */ - CLEAR_REGNO_REG_SET (pbi->cond_local_set, i); - if (cond != NULL_RTX - && ! REGNO_REG_SET_P (pbi->local_set, i)) - SET_REGNO_REG_SET (pbi->cond_local_set, i); - else - SET_REGNO_REG_SET (pbi->local_set, i); - } - if (code != CLOBBER || needed_regno) - SET_REGNO_REG_SET (pbi->new_set, i); - - some_was_live |= needed_regno; - some_was_dead |= ! needed_regno; - } - -#ifdef HAVE_conditional_execution - /* Consider conditional death in deciding that the register needs - a death note. */ - if (some_was_live && ! not_dead - /* The stack pointer is never dead. Well, not strictly true, - but it's very difficult to tell from here. Hopefully - combine_stack_adjustments will fix up the most egregious - errors. */ - && regno_first != STACK_POINTER_REGNUM) - { - for (i = regno_first; i <= regno_last; ++i) - if (! mark_regno_cond_dead (pbi, i, cond)) - not_dead |= ((unsigned long) 1) << (i - regno_first); - } -#endif - - /* Additional data to record if this is the final pass. */ - if (flags & (PROP_LOG_LINKS | PROP_REG_INFO - | PROP_DEATH_NOTES | PROP_AUTOINC)) - { - rtx y; - int blocknum = pbi->bb->index; - - y = NULL_RTX; - if (flags & (PROP_LOG_LINKS | PROP_AUTOINC)) - { - y = pbi->reg_next_use[regno_first]; - - /* The next use is no longer next, since a store intervenes. */ - for (i = regno_first; i <= regno_last; ++i) - pbi->reg_next_use[i] = 0; - } - - if (flags & PROP_REG_INFO) - { - for (i = regno_first; i <= regno_last; ++i) - { - /* Count (weighted) references, stores, etc. This counts a - register twice if it is modified, but that is correct. */ - REG_N_SETS (i) += 1; - REG_N_REFS (i) += 1; - REG_FREQ (i) += REG_FREQ_FROM_BB (pbi->bb); - - /* The insns where a reg is live are normally counted - elsewhere, but we want the count to include the insn - where the reg is set, and the normal counting mechanism - would not count it. */ - REG_LIVE_LENGTH (i) += 1; - } - - /* If this is a hard reg, record this function uses the reg. */ - if (regno_first < FIRST_PSEUDO_REGISTER) - { - for (i = regno_first; i <= regno_last; i++) - regs_ever_live[i] = 1; - if (flags & PROP_ASM_SCAN) - for (i = regno_first; i <= regno_last; i++) - regs_asm_clobbered[i] = 1; - } - else - { - /* Keep track of which basic blocks each reg appears in. */ - if (REG_BASIC_BLOCK (regno_first) == REG_BLOCK_UNKNOWN) - REG_BASIC_BLOCK (regno_first) = blocknum; - else if (REG_BASIC_BLOCK (regno_first) != blocknum) - REG_BASIC_BLOCK (regno_first) = REG_BLOCK_GLOBAL; - } - } - - if (! some_was_dead) - { - if (flags & PROP_LOG_LINKS) - { - /* Make a logical link from the next following insn - that uses this register, back to this insn. - The following insns have already been processed. - - We don't build a LOG_LINK for hard registers containing - in ASM_OPERANDs. If these registers get replaced, - we might wind up changing the semantics of the insn, - even if reload can make what appear to be valid - assignments later. - - We don't build a LOG_LINK for global registers to - or from a function call. We don't want to let - combine think that it knows what is going on with - global registers. */ - if (y && (BLOCK_NUM (y) == blocknum) - && (regno_first >= FIRST_PSEUDO_REGISTER - || (asm_noperands (PATTERN (y)) < 0 - && ! ((CALL_P (insn) - || CALL_P (y)) - && global_regs[regno_first])))) - LOG_LINKS (y) = alloc_INSN_LIST (insn, LOG_LINKS (y)); - } - } - else if (not_dead) - ; - else if (! some_was_live) - { - if (flags & PROP_REG_INFO) - REG_N_DEATHS (regno_first) += 1; - - if (flags & PROP_DEATH_NOTES -#ifdef STACK_REGS - && (!(flags & PROP_POST_REGSTACK) - || !IN_RANGE (REGNO (reg), FIRST_STACK_REG, - LAST_STACK_REG)) -#endif - ) - { - /* Note that dead stores have already been deleted - when possible. If we get here, we have found a - dead store that cannot be eliminated (because the - same insn does something useful). Indicate this - by marking the reg being set as dying here. */ - REG_NOTES (insn) - = alloc_EXPR_LIST (REG_UNUSED, reg, REG_NOTES (insn)); - } - } - else - { - if (flags & PROP_DEATH_NOTES -#ifdef STACK_REGS - && (!(flags & PROP_POST_REGSTACK) - || !IN_RANGE (REGNO (reg), FIRST_STACK_REG, - LAST_STACK_REG)) -#endif - ) - { - /* This is a case where we have a multi-word hard register - and some, but not all, of the words of the register are - needed in subsequent insns. Write REG_UNUSED notes - for those parts that were not needed. This case should - be rare. */ - - for (i = regno_first; i <= regno_last; ++i) - if (! REGNO_REG_SET_P (pbi->reg_live, i)) - REG_NOTES (insn) - = alloc_EXPR_LIST (REG_UNUSED, - regno_reg_rtx[i], - REG_NOTES (insn)); - } - } - } - - /* Mark the register as being dead. */ - if (some_was_live - /* The stack pointer is never dead. Well, not strictly true, - but it's very difficult to tell from here. Hopefully - combine_stack_adjustments will fix up the most egregious - errors. */ - && regno_first != STACK_POINTER_REGNUM) - { - for (i = regno_first; i <= regno_last; ++i) - if (!(not_dead & (((unsigned long) 1) << (i - regno_first)))) - { - if ((pbi->flags & PROP_REG_INFO) - && REGNO_REG_SET_P (pbi->reg_live, i)) - { - REG_LIVE_LENGTH (i) += pbi->insn_num - reg_deaths[i]; - reg_deaths[i] = 0; - } - CLEAR_REGNO_REG_SET (pbi->reg_live, i); - } - if (flags & PROP_DEAD_INSN) - emit_insn_after (gen_rtx_CLOBBER (VOIDmode, reg), insn); - } - } - else if (REG_P (reg)) - { - if (flags & (PROP_LOG_LINKS | PROP_AUTOINC)) - pbi->reg_next_use[regno_first] = 0; - - if ((flags & PROP_REG_INFO) != 0 - && (flags & PROP_ASM_SCAN) != 0 - && regno_first < FIRST_PSEUDO_REGISTER) - { - for (i = regno_first; i <= regno_last; i++) - regs_asm_clobbered[i] = 1; - } - } - - /* If this is the last pass and this is a SCRATCH, show it will be dying - here and count it. */ - else if (GET_CODE (reg) == SCRATCH) - { - if (flags & PROP_DEATH_NOTES -#ifdef STACK_REGS - && (!(flags & PROP_POST_REGSTACK) - || !IN_RANGE (REGNO (reg), FIRST_STACK_REG, LAST_STACK_REG)) -#endif - ) - REG_NOTES (insn) - = alloc_EXPR_LIST (REG_UNUSED, reg, REG_NOTES (insn)); - } -} - -#ifdef HAVE_conditional_execution -/* Mark REGNO conditionally dead. - Return true if the register is now unconditionally dead. */ - -static int -mark_regno_cond_dead (struct propagate_block_info *pbi, int regno, rtx cond) -{ - /* If this is a store to a predicate register, the value of the - predicate is changing, we don't know that the predicate as seen - before is the same as that seen after. Flush all dependent - conditions from reg_cond_dead. This will make all such - conditionally live registers unconditionally live. */ - if (REGNO_REG_SET_P (pbi->reg_cond_reg, regno)) - flush_reg_cond_reg (pbi, regno); - - /* If this is an unconditional store, remove any conditional - life that may have existed. */ - if (cond == NULL_RTX) - splay_tree_remove (pbi->reg_cond_dead, regno); - else - { - splay_tree_node node; - struct reg_cond_life_info *rcli; - rtx ncond; - - /* Otherwise this is a conditional set. Record that fact. - It may have been conditionally used, or there may be a - subsequent set with a complementary condition. */ - - node = splay_tree_lookup (pbi->reg_cond_dead, regno); - if (node == NULL) - { - /* The register was unconditionally live previously. - Record the current condition as the condition under - which it is dead. */ - rcli = XNEW (struct reg_cond_life_info); - rcli->condition = cond; - rcli->stores = cond; - rcli->orig_condition = const0_rtx; - splay_tree_insert (pbi->reg_cond_dead, regno, - (splay_tree_value) rcli); - - SET_REGNO_REG_SET (pbi->reg_cond_reg, REGNO (XEXP (cond, 0))); - - /* Not unconditionally dead. */ - return 0; - } - else - { - /* The register was conditionally live previously. - Add the new condition to the old. */ - rcli = (struct reg_cond_life_info *) node->value; - ncond = rcli->condition; - ncond = ior_reg_cond (ncond, cond, 1); - if (rcli->stores == const0_rtx) - rcli->stores = cond; - else if (rcli->stores != const1_rtx) - rcli->stores = ior_reg_cond (rcli->stores, cond, 1); - - /* If the register is now unconditionally dead, remove the entry - in the splay_tree. A register is unconditionally dead if the - dead condition ncond is true. A register is also unconditionally - dead if the sum of all conditional stores is an unconditional - store (stores is true), and the dead condition is identically the - same as the original dead condition initialized at the end of - the block. This is a pointer compare, not an rtx_equal_p - compare. */ - if (ncond == const1_rtx - || (ncond == rcli->orig_condition && rcli->stores == const1_rtx)) - splay_tree_remove (pbi->reg_cond_dead, regno); - else - { - rcli->condition = ncond; - - SET_REGNO_REG_SET (pbi->reg_cond_reg, REGNO (XEXP (cond, 0))); - - /* Not unconditionally dead. */ - return 0; - } - } - } - - return 1; -} - -/* Called from splay_tree_delete for pbi->reg_cond_life. */ - -static void -free_reg_cond_life_info (splay_tree_value value) -{ - struct reg_cond_life_info *rcli = (struct reg_cond_life_info *) value; - free (rcli); -} - -/* Helper function for flush_reg_cond_reg. */ - -static int -flush_reg_cond_reg_1 (splay_tree_node node, void *data) -{ - struct reg_cond_life_info *rcli; - int *xdata = (int *) data; - unsigned int regno = xdata[0]; - - /* Don't need to search if last flushed value was farther on in - the in-order traversal. */ - if (xdata[1] >= (int) node->key) - return 0; - - /* Splice out portions of the expression that refer to regno. */ - rcli = (struct reg_cond_life_info *) node->value; - rcli->condition = elim_reg_cond (rcli->condition, regno); - if (rcli->stores != const0_rtx && rcli->stores != const1_rtx) - rcli->stores = elim_reg_cond (rcli->stores, regno); - - /* If the entire condition is now false, signal the node to be removed. */ - if (rcli->condition == const0_rtx) - { - xdata[1] = node->key; - return -1; - } - else - gcc_assert (rcli->condition != const1_rtx); - - return 0; -} - -/* Flush all (sub) expressions referring to REGNO from REG_COND_LIVE. */ - -static void -flush_reg_cond_reg (struct propagate_block_info *pbi, int regno) -{ - int pair[2]; - - pair[0] = regno; - pair[1] = -1; - while (splay_tree_foreach (pbi->reg_cond_dead, - flush_reg_cond_reg_1, pair) == -1) - splay_tree_remove (pbi->reg_cond_dead, pair[1]); - - CLEAR_REGNO_REG_SET (pbi->reg_cond_reg, regno); -} - -/* Logical arithmetic on predicate conditions. IOR, NOT and AND. - For ior/and, the ADD flag determines whether we want to add the new - condition X to the old one unconditionally. If it is zero, we will - only return a new expression if X allows us to simplify part of - OLD, otherwise we return NULL to the caller. - If ADD is nonzero, we will return a new condition in all cases. The - toplevel caller of one of these functions should always pass 1 for - ADD. */ - -static rtx -ior_reg_cond (rtx old, rtx x, int add) -{ - rtx op0, op1; - - if (COMPARISON_P (old)) - { - if (COMPARISON_P (x) - && REVERSE_CONDEXEC_PREDICATES_P (x, old) - && REGNO (XEXP (x, 0)) == REGNO (XEXP (old, 0))) - return const1_rtx; - if (GET_CODE (x) == GET_CODE (old) - && REGNO (XEXP (x, 0)) == REGNO (XEXP (old, 0))) - return old; - if (! add) - return NULL; - return gen_rtx_IOR (0, old, x); - } - - switch (GET_CODE (old)) - { - case IOR: - op0 = ior_reg_cond (XEXP (old, 0), x, 0); - op1 = ior_reg_cond (XEXP (old, 1), x, 0); - if (op0 != NULL || op1 != NULL) - { - if (op0 == const0_rtx) - return op1 ? op1 : gen_rtx_IOR (0, XEXP (old, 1), x); - if (op1 == const0_rtx) - return op0 ? op0 : gen_rtx_IOR (0, XEXP (old, 0), x); - if (op0 == const1_rtx || op1 == const1_rtx) - return const1_rtx; - if (op0 == NULL) - op0 = gen_rtx_IOR (0, XEXP (old, 0), x); - else if (rtx_equal_p (x, op0)) - /* (x | A) | x ~ (x | A). */ - return old; - if (op1 == NULL) - op1 = gen_rtx_IOR (0, XEXP (old, 1), x); - else if (rtx_equal_p (x, op1)) - /* (A | x) | x ~ (A | x). */ - return old; - return gen_rtx_IOR (0, op0, op1); - } - if (! add) - return NULL; - return gen_rtx_IOR (0, old, x); - - case AND: - op0 = ior_reg_cond (XEXP (old, 0), x, 0); - op1 = ior_reg_cond (XEXP (old, 1), x, 0); - if (op0 != NULL || op1 != NULL) - { - if (op0 == const1_rtx) - return op1 ? op1 : gen_rtx_IOR (0, XEXP (old, 1), x); - if (op1 == const1_rtx) - return op0 ? op0 : gen_rtx_IOR (0, XEXP (old, 0), x); - if (op0 == const0_rtx || op1 == const0_rtx) - return const0_rtx; - if (op0 == NULL) - op0 = gen_rtx_IOR (0, XEXP (old, 0), x); - else if (rtx_equal_p (x, op0)) - /* (x & A) | x ~ x. */ - return op0; - if (op1 == NULL) - op1 = gen_rtx_IOR (0, XEXP (old, 1), x); - else if (rtx_equal_p (x, op1)) - /* (A & x) | x ~ x. */ - return op1; - return gen_rtx_AND (0, op0, op1); - } - if (! add) - return NULL; - return gen_rtx_IOR (0, old, x); - - case NOT: - op0 = and_reg_cond (XEXP (old, 0), not_reg_cond (x), 0); - if (op0 != NULL) - return not_reg_cond (op0); - if (! add) - return NULL; - return gen_rtx_IOR (0, old, x); - - default: - gcc_unreachable (); - } -} - -static rtx -not_reg_cond (rtx x) -{ - if (x == const0_rtx) - return const1_rtx; - else if (x == const1_rtx) - return const0_rtx; - if (GET_CODE (x) == NOT) - return XEXP (x, 0); - if (COMPARISON_P (x) - && REG_P (XEXP (x, 0))) - { - gcc_assert (XEXP (x, 1) == const0_rtx); - - return gen_rtx_fmt_ee (reversed_comparison_code (x, NULL), - VOIDmode, XEXP (x, 0), const0_rtx); - } - return gen_rtx_NOT (0, x); -} - -static rtx -and_reg_cond (rtx old, rtx x, int add) -{ - rtx op0, op1; - - if (COMPARISON_P (old)) - { - if (COMPARISON_P (x) - && GET_CODE (x) == reversed_comparison_code (old, NULL) - && REGNO (XEXP (x, 0)) == REGNO (XEXP (old, 0))) - return const0_rtx; - if (GET_CODE (x) == GET_CODE (old) - && REGNO (XEXP (x, 0)) == REGNO (XEXP (old, 0))) - return old; - if (! add) - return NULL; - return gen_rtx_AND (0, old, x); - } - - switch (GET_CODE (old)) - { - case IOR: - op0 = and_reg_cond (XEXP (old, 0), x, 0); - op1 = and_reg_cond (XEXP (old, 1), x, 0); - if (op0 != NULL || op1 != NULL) - { - if (op0 == const0_rtx) - return op1 ? op1 : gen_rtx_AND (0, XEXP (old, 1), x); - if (op1 == const0_rtx) - return op0 ? op0 : gen_rtx_AND (0, XEXP (old, 0), x); - if (op0 == const1_rtx || op1 == const1_rtx) - return const1_rtx; - if (op0 == NULL) - op0 = gen_rtx_AND (0, XEXP (old, 0), x); - else if (rtx_equal_p (x, op0)) - /* (x | A) & x ~ x. */ - return op0; - if (op1 == NULL) - op1 = gen_rtx_AND (0, XEXP (old, 1), x); - else if (rtx_equal_p (x, op1)) - /* (A | x) & x ~ x. */ - return op1; - return gen_rtx_IOR (0, op0, op1); - } - if (! add) - return NULL; - return gen_rtx_AND (0, old, x); - - case AND: - op0 = and_reg_cond (XEXP (old, 0), x, 0); - op1 = and_reg_cond (XEXP (old, 1), x, 0); - if (op0 != NULL || op1 != NULL) - { - if (op0 == const1_rtx) - return op1 ? op1 : gen_rtx_AND (0, XEXP (old, 1), x); - if (op1 == const1_rtx) - return op0 ? op0 : gen_rtx_AND (0, XEXP (old, 0), x); - if (op0 == const0_rtx || op1 == const0_rtx) - return const0_rtx; - if (op0 == NULL) - op0 = gen_rtx_AND (0, XEXP (old, 0), x); - else if (rtx_equal_p (x, op0)) - /* (x & A) & x ~ (x & A). */ - return old; - if (op1 == NULL) - op1 = gen_rtx_AND (0, XEXP (old, 1), x); - else if (rtx_equal_p (x, op1)) - /* (A & x) & x ~ (A & x). */ - return old; - return gen_rtx_AND (0, op0, op1); - } - if (! add) - return NULL; - return gen_rtx_AND (0, old, x); - - case NOT: - op0 = ior_reg_cond (XEXP (old, 0), not_reg_cond (x), 0); - if (op0 != NULL) - return not_reg_cond (op0); - if (! add) - return NULL; - return gen_rtx_AND (0, old, x); - - default: - gcc_unreachable (); - } -} - -/* Given a condition X, remove references to reg REGNO and return the - new condition. The removal will be done so that all conditions - involving REGNO are considered to evaluate to false. This function - is used when the value of REGNO changes. */ - -static rtx -elim_reg_cond (rtx x, unsigned int regno) -{ - rtx op0, op1; - - if (COMPARISON_P (x)) - { - rtx reg; - - reg = XEXP (x, 0); - if (GET_CODE (reg) == SUBREG) - reg = SUBREG_REG (reg); - gcc_assert (REG_P (reg)); - if (REGNO (reg) == regno) - return const0_rtx; - return x; - } - - switch (GET_CODE (x)) - { - case AND: - op0 = elim_reg_cond (XEXP (x, 0), regno); - op1 = elim_reg_cond (XEXP (x, 1), regno); - if (op0 == const0_rtx || op1 == const0_rtx) - return const0_rtx; - if (op0 == const1_rtx) - return op1; - if (op1 == const1_rtx) - return op0; - if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1)) - return x; - return gen_rtx_AND (0, op0, op1); - - case IOR: - op0 = elim_reg_cond (XEXP (x, 0), regno); - op1 = elim_reg_cond (XEXP (x, 1), regno); - if (op0 == const1_rtx || op1 == const1_rtx) - return const1_rtx; - if (op0 == const0_rtx) - return op1; - if (op1 == const0_rtx) - return op0; - if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1)) - return x; - return gen_rtx_IOR (0, op0, op1); - - case NOT: - op0 = elim_reg_cond (XEXP (x, 0), regno); - if (op0 == const0_rtx) - return const1_rtx; - if (op0 == const1_rtx) - return const0_rtx; - if (op0 != XEXP (x, 0)) - return not_reg_cond (op0); - return x; - - default: - gcc_unreachable (); - } -} -#endif /* HAVE_conditional_execution */ - -#ifdef AUTO_INC_DEC - -/* Try to substitute the auto-inc expression INC as the address inside - MEM which occurs in INSN. Currently, the address of MEM is an expression - involving INCR_REG, and INCR is the next use of INCR_REG; it is an insn - that has a single set whose source is a PLUS of INCR_REG and something - else. */ - -static void -attempt_auto_inc (struct propagate_block_info *pbi, rtx inc, rtx insn, - rtx mem, rtx incr, rtx incr_reg) -{ - int regno = REGNO (incr_reg); - rtx set = single_set (incr); - rtx q = SET_DEST (set); - rtx y = SET_SRC (set); - int opnum = XEXP (y, 0) == incr_reg ? 0 : 1; - int changed; - - /* Make sure this reg appears only once in this insn. */ - if (count_occurrences (PATTERN (insn), incr_reg, 1) != 1) - return; - - if (dead_or_set_p (incr, incr_reg) - /* Mustn't autoinc an eliminable register. */ - && (regno >= FIRST_PSEUDO_REGISTER - || ! TEST_HARD_REG_BIT (elim_reg_set, regno))) - { - /* This is the simple case. Try to make the auto-inc. If - we can't, we are done. Otherwise, we will do any - needed updates below. */ - if (! validate_change (insn, &XEXP (mem, 0), inc, 0)) - return; - } - else if (REG_P (q) - /* PREV_INSN used here to check the semi-open interval - [insn,incr). */ - && ! reg_used_between_p (q, PREV_INSN (insn), incr) - /* We must also check for sets of q as q may be - a call clobbered hard register and there may - be a call between PREV_INSN (insn) and incr. */ - && ! reg_set_between_p (q, PREV_INSN (insn), incr)) - { - /* We have *p followed sometime later by q = p+size. - Both p and q must be live afterward, - and q is not used between INSN and its assignment. - Change it to q = p, ...*q..., q = q+size. - Then fall into the usual case. */ - rtx insns, temp; - - start_sequence (); - emit_move_insn (q, incr_reg); - insns = get_insns (); - end_sequence (); - - /* If we can't make the auto-inc, or can't make the - replacement into Y, exit. There's no point in making - the change below if we can't do the auto-inc and doing - so is not correct in the pre-inc case. */ - - XEXP (inc, 0) = q; - validate_change (insn, &XEXP (mem, 0), inc, 1); - validate_change (incr, &XEXP (y, opnum), q, 1); - if (! apply_change_group ()) - return; - - /* We now know we'll be doing this change, so emit the - new insn(s) and do the updates. */ - emit_insn_before (insns, insn); - - if (BB_HEAD (pbi->bb) == insn) - BB_HEAD (pbi->bb) = insns; - - /* INCR will become a NOTE and INSN won't contain a - use of INCR_REG. If a use of INCR_REG was just placed in - the insn before INSN, make that the next use. - Otherwise, invalidate it. */ - if (NONJUMP_INSN_P (PREV_INSN (insn)) - && GET_CODE (PATTERN (PREV_INSN (insn))) == SET - && SET_SRC (PATTERN (PREV_INSN (insn))) == incr_reg) - pbi->reg_next_use[regno] = PREV_INSN (insn); - else - pbi->reg_next_use[regno] = 0; - - incr_reg = q; - regno = REGNO (q); - - if ((pbi->flags & PROP_REG_INFO) - && !REGNO_REG_SET_P (pbi->reg_live, regno)) - reg_deaths[regno] = pbi->insn_num; - - /* REGNO is now used in INCR which is below INSN, but - it previously wasn't live here. If we don't mark - it as live, we'll put a REG_DEAD note for it - on this insn, which is incorrect. */ - SET_REGNO_REG_SET (pbi->reg_live, regno); - - /* If there are any calls between INSN and INCR, show - that REGNO now crosses them. */ - for (temp = insn; temp != incr; temp = NEXT_INSN (temp)) - if (CALL_P (temp)) - { - REG_N_CALLS_CROSSED (regno)++; - if (can_throw_internal (temp)) - REG_N_THROWING_CALLS_CROSSED (regno)++; - } - - /* Invalidate alias info for Q since we just changed its value. */ - clear_reg_alias_info (q); - } - else - return; - - /* If we haven't returned, it means we were able to make the - auto-inc, so update the status. First, record that this insn - has an implicit side effect. */ - - REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC, incr_reg, REG_NOTES (insn)); - - /* Modify the old increment-insn to simply copy - the already-incremented value of our register. */ - changed = validate_change (incr, &SET_SRC (set), incr_reg, 0); - gcc_assert (changed); - - /* If that makes it a no-op (copying the register into itself) delete - it so it won't appear to be a "use" and a "set" of this - register. */ - if (REGNO (SET_DEST (set)) == REGNO (incr_reg)) - { - /* If the original source was dead, it's dead now. */ - rtx note; - - while ((note = find_reg_note (incr, REG_DEAD, NULL_RTX)) != NULL_RTX) - { - remove_note (incr, note); - if (XEXP (note, 0) != incr_reg) - { - unsigned int regno = REGNO (XEXP (note, 0)); - - if ((pbi->flags & PROP_REG_INFO) - && REGNO_REG_SET_P (pbi->reg_live, regno)) - { - REG_LIVE_LENGTH (regno) += pbi->insn_num - reg_deaths[regno]; - reg_deaths[regno] = 0; - } - CLEAR_REGNO_REG_SET (pbi->reg_live, REGNO (XEXP (note, 0))); - } - } - - SET_INSN_DELETED (incr); - } - - if (regno >= FIRST_PSEUDO_REGISTER) - { - /* Count an extra reference to the reg. When a reg is - incremented, spilling it is worse, so we want to make - that less likely. */ - REG_FREQ (regno) += REG_FREQ_FROM_BB (pbi->bb); - - /* Count the increment as a setting of the register, - even though it isn't a SET in rtl. */ - REG_N_SETS (regno)++; - } -} - -/* X is a MEM found in INSN. See if we can convert it into an auto-increment - reference. */ - -static void -find_auto_inc (struct propagate_block_info *pbi, rtx x, rtx insn) -{ - rtx addr = XEXP (x, 0); - HOST_WIDE_INT offset = 0; - rtx set, y, incr, inc_val; - int regno; - int size = GET_MODE_SIZE (GET_MODE (x)); - - if (JUMP_P (insn)) - return; - - /* Here we detect use of an index register which might be good for - postincrement, postdecrement, preincrement, or predecrement. */ - - if (GET_CODE (addr) == PLUS && GET_CODE (XEXP (addr, 1)) == CONST_INT) - offset = INTVAL (XEXP (addr, 1)), addr = XEXP (addr, 0); - - if (!REG_P (addr)) - return; - - regno = REGNO (addr); - - /* Is the next use an increment that might make auto-increment? */ - incr = pbi->reg_next_use[regno]; - if (incr == 0 || BLOCK_NUM (incr) != BLOCK_NUM (insn)) - return; - set = single_set (incr); - if (set == 0 || GET_CODE (set) != SET) - return; - y = SET_SRC (set); - - if (GET_CODE (y) != PLUS) - return; - - if (REG_P (XEXP (y, 0)) && REGNO (XEXP (y, 0)) == REGNO (addr)) - inc_val = XEXP (y, 1); - else if (REG_P (XEXP (y, 1)) && REGNO (XEXP (y, 1)) == REGNO (addr)) - inc_val = XEXP (y, 0); - else - return; - - if (GET_CODE (inc_val) == CONST_INT) - { - if (HAVE_POST_INCREMENT - && (INTVAL (inc_val) == size && offset == 0)) - attempt_auto_inc (pbi, gen_rtx_POST_INC (Pmode, addr), insn, x, - incr, addr); - else if (HAVE_POST_DECREMENT - && (INTVAL (inc_val) == -size && offset == 0)) - attempt_auto_inc (pbi, gen_rtx_POST_DEC (Pmode, addr), insn, x, - incr, addr); - else if (HAVE_PRE_INCREMENT - && (INTVAL (inc_val) == size && offset == size)) - attempt_auto_inc (pbi, gen_rtx_PRE_INC (Pmode, addr), insn, x, - incr, addr); - else if (HAVE_PRE_DECREMENT - && (INTVAL (inc_val) == -size && offset == -size)) - attempt_auto_inc (pbi, gen_rtx_PRE_DEC (Pmode, addr), insn, x, - incr, addr); - else if (HAVE_POST_MODIFY_DISP && offset == 0) - attempt_auto_inc (pbi, gen_rtx_POST_MODIFY (Pmode, addr, - gen_rtx_PLUS (Pmode, - addr, - inc_val)), - insn, x, incr, addr); - else if (HAVE_PRE_MODIFY_DISP && offset == INTVAL (inc_val)) - attempt_auto_inc (pbi, gen_rtx_PRE_MODIFY (Pmode, addr, - gen_rtx_PLUS (Pmode, - addr, - inc_val)), - insn, x, incr, addr); - } - else if (REG_P (inc_val) - && ! reg_set_between_p (inc_val, PREV_INSN (insn), - NEXT_INSN (incr))) - - { - if (HAVE_POST_MODIFY_REG && offset == 0) - attempt_auto_inc (pbi, gen_rtx_POST_MODIFY (Pmode, addr, - gen_rtx_PLUS (Pmode, - addr, - inc_val)), - insn, x, incr, addr); - } -} - -#endif /* AUTO_INC_DEC */ - -static void -mark_used_reg (struct propagate_block_info *pbi, rtx reg, - rtx cond ATTRIBUTE_UNUSED, rtx insn) -{ - unsigned int regno_first, regno_last, i; - int some_was_live, some_was_dead, some_not_set; - - regno_last = regno_first = REGNO (reg); - if (regno_first < FIRST_PSEUDO_REGISTER) - regno_last += hard_regno_nregs[regno_first][GET_MODE (reg)] - 1; - - /* Find out if any of this register is live after this instruction. */ - some_was_live = some_was_dead = 0; - for (i = regno_first; i <= regno_last; ++i) - { - int needed_regno = REGNO_REG_SET_P (pbi->reg_live, i); - some_was_live |= needed_regno; - some_was_dead |= ! needed_regno; - } - - /* Find out if any of the register was set this insn. */ - some_not_set = 0; - for (i = regno_first; i <= regno_last; ++i) - some_not_set |= ! REGNO_REG_SET_P (pbi->new_set, i); - - if (pbi->flags & (PROP_LOG_LINKS | PROP_AUTOINC)) - { - /* Record where each reg is used, so when the reg is set we know - the next insn that uses it. */ - pbi->reg_next_use[regno_first] = insn; - } - - if (pbi->flags & PROP_REG_INFO) - { - if (regno_first < FIRST_PSEUDO_REGISTER) - { - /* If this is a register we are going to try to eliminate, - don't mark it live here. If we are successful in - eliminating it, it need not be live unless it is used for - pseudos, in which case it will have been set live when it - was allocated to the pseudos. If the register will not - be eliminated, reload will set it live at that point. - - Otherwise, record that this function uses this register. */ - /* ??? The PPC backend tries to "eliminate" on the pic - register to itself. This should be fixed. In the mean - time, hack around it. */ - - if (! (TEST_HARD_REG_BIT (elim_reg_set, regno_first) - && (regno_first == FRAME_POINTER_REGNUM - || regno_first == ARG_POINTER_REGNUM))) - for (i = regno_first; i <= regno_last; ++i) - regs_ever_live[i] = 1; - } - else - { - /* Keep track of which basic block each reg appears in. */ - - int blocknum = pbi->bb->index; - if (REG_BASIC_BLOCK (regno_first) == REG_BLOCK_UNKNOWN) - REG_BASIC_BLOCK (regno_first) = blocknum; - else if (REG_BASIC_BLOCK (regno_first) != blocknum) - REG_BASIC_BLOCK (regno_first) = REG_BLOCK_GLOBAL; - - /* Count (weighted) number of uses of each reg. */ - REG_FREQ (regno_first) += REG_FREQ_FROM_BB (pbi->bb); - REG_N_REFS (regno_first)++; - } - for (i = regno_first; i <= regno_last; ++i) - if (! REGNO_REG_SET_P (pbi->reg_live, i)) - { - gcc_assert (!reg_deaths[i]); - reg_deaths[i] = pbi->insn_num; - } - } - - /* Record and count the insns in which a reg dies. If it is used in - this insn and was dead below the insn then it dies in this insn. - If it was set in this insn, we do not make a REG_DEAD note; - likewise if we already made such a note. */ - if ((pbi->flags & (PROP_DEATH_NOTES | PROP_REG_INFO)) - && some_was_dead - && some_not_set) - { - /* Check for the case where the register dying partially - overlaps the register set by this insn. */ - if (regno_first != regno_last) - for (i = regno_first; i <= regno_last; ++i) - some_was_live |= REGNO_REG_SET_P (pbi->new_set, i); - - /* If none of the words in X is needed, make a REG_DEAD note. - Otherwise, we must make partial REG_DEAD notes. */ - if (! some_was_live) - { - if ((pbi->flags & PROP_DEATH_NOTES) -#ifdef STACK_REGS - && (!(pbi->flags & PROP_POST_REGSTACK) - || !IN_RANGE (REGNO (reg), FIRST_STACK_REG, LAST_STACK_REG)) -#endif - && ! find_regno_note (insn, REG_DEAD, regno_first)) - REG_NOTES (insn) - = alloc_EXPR_LIST (REG_DEAD, reg, REG_NOTES (insn)); - - if (pbi->flags & PROP_REG_INFO) - REG_N_DEATHS (regno_first)++; - } - else - { - /* Don't make a REG_DEAD note for a part of a register - that is set in the insn. */ - for (i = regno_first; i <= regno_last; ++i) - if (! REGNO_REG_SET_P (pbi->reg_live, i) - && ! dead_or_set_regno_p (insn, i)) - REG_NOTES (insn) - = alloc_EXPR_LIST (REG_DEAD, - regno_reg_rtx[i], - REG_NOTES (insn)); - } - } - - /* Mark the register as being live. */ - for (i = regno_first; i <= regno_last; ++i) - { -#ifdef HAVE_conditional_execution - int this_was_live = REGNO_REG_SET_P (pbi->reg_live, i); -#endif - - SET_REGNO_REG_SET (pbi->reg_live, i); - -#ifdef HAVE_conditional_execution - /* If this is a conditional use, record that fact. If it is later - conditionally set, we'll know to kill the register. */ - if (cond != NULL_RTX) - { - splay_tree_node node; - struct reg_cond_life_info *rcli; - rtx ncond; - - if (this_was_live) - { - node = splay_tree_lookup (pbi->reg_cond_dead, i); - if (node == NULL) - { - /* The register was unconditionally live previously. - No need to do anything. */ - } - else - { - /* The register was conditionally live previously. - Subtract the new life cond from the old death cond. */ - rcli = (struct reg_cond_life_info *) node->value; - ncond = rcli->condition; - ncond = and_reg_cond (ncond, not_reg_cond (cond), 1); - - /* If the register is now unconditionally live, - remove the entry in the splay_tree. */ - if (ncond == const0_rtx) - splay_tree_remove (pbi->reg_cond_dead, i); - else - { - rcli->condition = ncond; - SET_REGNO_REG_SET (pbi->reg_cond_reg, - REGNO (XEXP (cond, 0))); - } - } - } - else - { - /* The register was not previously live at all. Record - the condition under which it is still dead. */ - rcli = XNEW (struct reg_cond_life_info); - rcli->condition = not_reg_cond (cond); - rcli->stores = const0_rtx; - rcli->orig_condition = const0_rtx; - splay_tree_insert (pbi->reg_cond_dead, i, - (splay_tree_value) rcli); - - SET_REGNO_REG_SET (pbi->reg_cond_reg, REGNO (XEXP (cond, 0))); - } - } - else if (this_was_live) - { - /* The register may have been conditionally live previously, but - is now unconditionally live. Remove it from the conditionally - dead list, so that a conditional set won't cause us to think - it dead. */ - splay_tree_remove (pbi->reg_cond_dead, i); - } -#endif - } -} - -/* Scan expression X for registers which have to be marked used in PBI. - X is considered to be the SET_DEST rtx of SET. TRUE is returned if - X could be handled by this function. */ - -static bool -mark_used_dest_regs (struct propagate_block_info *pbi, rtx x, rtx cond, rtx insn) -{ - int regno; - bool mark_dest = false; - rtx dest = x; - - /* On some platforms calls return values spread over several - locations. These locations are wrapped in a EXPR_LIST rtx - together with a CONST_INT offset. */ - if (GET_CODE (x) == EXPR_LIST - && GET_CODE (XEXP (x, 1)) == CONST_INT) - x = XEXP (x, 0); - - if (x == NULL_RTX) - return false; - - /* If storing into MEM, don't show it as being used. But do - show the address as being used. */ - if (MEM_P (x)) - { -#ifdef AUTO_INC_DEC - if (pbi->flags & PROP_AUTOINC) - find_auto_inc (pbi, x, insn); -#endif - mark_used_regs (pbi, XEXP (x, 0), cond, insn); - return true; - } - - /* Storing in STRICT_LOW_PART is like storing in a reg - in that this SET might be dead, so ignore it in TESTREG. - but in some other ways it is like using the reg. - - Storing in a SUBREG or a bit field is like storing the entire - register in that if the register's value is not used - then this SET is not needed. */ - while (GET_CODE (x) == STRICT_LOW_PART - || GET_CODE (x) == ZERO_EXTRACT - || GET_CODE (x) == SUBREG) - { -#ifdef CANNOT_CHANGE_MODE_CLASS - if ((pbi->flags & PROP_REG_INFO) && GET_CODE (x) == SUBREG) - record_subregs_of_mode (x); -#endif - - /* Modifying a single register in an alternate mode - does not use any of the old value. But these other - ways of storing in a register do use the old value. */ - if (GET_CODE (x) == SUBREG - && !((REG_BYTES (SUBREG_REG (x)) - + UNITS_PER_WORD - 1) / UNITS_PER_WORD - > (REG_BYTES (x) - + UNITS_PER_WORD - 1) / UNITS_PER_WORD)) - ; - else - mark_dest = true; - - x = XEXP (x, 0); - } - - /* If this is a store into a register or group of registers, - recursively scan the value being stored. */ - if (REG_P (x) - && (regno = REGNO (x), - !(regno == FRAME_POINTER_REGNUM - && (!reload_completed || frame_pointer_needed))) -#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM - && !(regno == HARD_FRAME_POINTER_REGNUM - && (!reload_completed || frame_pointer_needed)) -#endif -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - && !(regno == ARG_POINTER_REGNUM && fixed_regs[regno]) -#endif - ) - { - if (mark_dest) - mark_used_regs (pbi, dest, cond, insn); - return true; - } - return false; -} - -/* Scan expression X and store a 1-bit in NEW_LIVE for each reg it uses. - This is done assuming the registers needed from X are those that - have 1-bits in PBI->REG_LIVE. - - INSN is the containing instruction. If INSN is dead, this function - is not called. */ - -static void -mark_used_regs (struct propagate_block_info *pbi, rtx x, rtx cond, rtx insn) -{ - RTX_CODE code; - int flags = pbi->flags; - - retry: - if (!x) - return; - code = GET_CODE (x); - switch (code) - { - case LABEL_REF: - case SYMBOL_REF: - case CONST_INT: - case CONST: - case CONST_DOUBLE: - case CONST_VECTOR: - case PC: - case ADDR_VEC: - case ADDR_DIFF_VEC: - return; - -#ifdef HAVE_cc0 - case CC0: - pbi->cc0_live = 1; - return; -#endif - - case CLOBBER: - /* If we are clobbering a MEM, mark any registers inside the address - as being used. */ - if (MEM_P (XEXP (x, 0))) - mark_used_regs (pbi, XEXP (XEXP (x, 0), 0), cond, insn); - return; - - case MEM: - /* Don't bother watching stores to mems if this is not the - final pass. We'll not be deleting dead stores this round. */ - if (optimize && (flags & PROP_SCAN_DEAD_STORES)) - { - /* Invalidate the data for the last MEM stored, but only if MEM is - something that can be stored into. */ - if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF - && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0))) - /* Needn't clear the memory set list. */ - ; - else - { - rtx temp = pbi->mem_set_list; - rtx prev = NULL_RTX; - rtx next; - - while (temp) - { - next = XEXP (temp, 1); - if (anti_dependence (XEXP (temp, 0), x)) - { - /* Splice temp out of the list. */ - if (prev) - XEXP (prev, 1) = next; - else - pbi->mem_set_list = next; - free_EXPR_LIST_node (temp); - pbi->mem_set_list_len--; - } - else - prev = temp; - temp = next; - } - } - - /* If the memory reference had embedded side effects (autoincrement - address modes. Then we may need to kill some entries on the - memory set list. */ - if (insn) - for_each_rtx (&PATTERN (insn), invalidate_mems_from_autoinc, pbi); - } - -#ifdef AUTO_INC_DEC - if (flags & PROP_AUTOINC) - find_auto_inc (pbi, x, insn); -#endif - break; - - case SUBREG: -#ifdef CANNOT_CHANGE_MODE_CLASS - if (flags & PROP_REG_INFO) - record_subregs_of_mode (x); -#endif - - /* While we're here, optimize this case. */ - x = SUBREG_REG (x); - if (!REG_P (x)) - goto retry; - /* Fall through. */ - - case REG: - /* See a register other than being set => mark it as needed. */ - mark_used_reg (pbi, x, cond, insn); - return; - - case SET: - { - rtx dest = SET_DEST (x); - int i; - bool ret = false; - - if (GET_CODE (dest) == PARALLEL) - for (i = 0; i < XVECLEN (dest, 0); i++) - ret |= mark_used_dest_regs (pbi, XVECEXP (dest, 0, i), cond, insn); - else - ret = mark_used_dest_regs (pbi, dest, cond, insn); - - if (ret) - { - mark_used_regs (pbi, SET_SRC (x), cond, insn); - return; - } - } - break; - - case ASM_OPERANDS: - case UNSPEC_VOLATILE: - case TRAP_IF: - case ASM_INPUT: - { - /* Traditional and volatile asm instructions must be considered to use - and clobber all hard registers, all pseudo-registers and all of - memory. So must TRAP_IF and UNSPEC_VOLATILE operations. - - Consider for instance a volatile asm that changes the fpu rounding - mode. An insn should not be moved across this even if it only uses - pseudo-regs because it might give an incorrectly rounded result. - - ?!? Unfortunately, marking all hard registers as live causes massive - problems for the register allocator and marking all pseudos as live - creates mountains of uninitialized variable warnings. - - So for now, just clear the memory set list and mark any regs - we can find in ASM_OPERANDS as used. */ - if (code != ASM_OPERANDS || MEM_VOLATILE_P (x)) - { - free_EXPR_LIST_list (&pbi->mem_set_list); - pbi->mem_set_list_len = 0; - } - - /* For all ASM_OPERANDS, we must traverse the vector of input operands. - We can not just fall through here since then we would be confused - by the ASM_INPUT rtx inside ASM_OPERANDS, which do not indicate - traditional asms unlike their normal usage. */ - if (code == ASM_OPERANDS) - { - int j; - - for (j = 0; j < ASM_OPERANDS_INPUT_LENGTH (x); j++) - mark_used_regs (pbi, ASM_OPERANDS_INPUT (x, j), cond, insn); - } - break; - } - - case COND_EXEC: - gcc_assert (!cond); - - mark_used_regs (pbi, COND_EXEC_TEST (x), NULL_RTX, insn); - - cond = COND_EXEC_TEST (x); - x = COND_EXEC_CODE (x); - goto retry; - - default: - break; - } - - /* Recursively scan the operands of this expression. */ - - { - const char * const fmt = GET_RTX_FORMAT (code); - int i; - - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - /* Tail recursive case: save a function call level. */ - if (i == 0) - { - x = XEXP (x, 0); - goto retry; - } - mark_used_regs (pbi, XEXP (x, i), cond, insn); - } - else if (fmt[i] == 'E') - { - int j; - for (j = 0; j < XVECLEN (x, i); j++) - mark_used_regs (pbi, XVECEXP (x, i, j), cond, insn); - } - } - } -} - -#ifdef AUTO_INC_DEC - -static int -try_pre_increment_1 (struct propagate_block_info *pbi, rtx insn) -{ - /* Find the next use of this reg. If in same basic block, - make it do pre-increment or pre-decrement if appropriate. */ - rtx x = single_set (insn); - HOST_WIDE_INT amount = ((GET_CODE (SET_SRC (x)) == PLUS ? 1 : -1) - * INTVAL (XEXP (SET_SRC (x), 1))); - int regno = REGNO (SET_DEST (x)); - rtx y = pbi->reg_next_use[regno]; - if (y != 0 - && SET_DEST (x) != stack_pointer_rtx - && BLOCK_NUM (y) == BLOCK_NUM (insn) - /* Don't do this if the reg dies, or gets set in y; a standard addressing - mode would be better. */ - && ! dead_or_set_p (y, SET_DEST (x)) - && try_pre_increment (y, SET_DEST (x), amount)) - { - /* We have found a suitable auto-increment and already changed - insn Y to do it. So flush this increment instruction. */ - propagate_block_delete_insn (insn); - - /* Count a reference to this reg for the increment insn we are - deleting. When a reg is incremented, spilling it is worse, - so we want to make that less likely. */ - if (regno >= FIRST_PSEUDO_REGISTER) - { - REG_FREQ (regno) += REG_FREQ_FROM_BB (pbi->bb); - REG_N_SETS (regno)++; - } - - /* Flush any remembered memories depending on the value of - the incremented register. */ - invalidate_mems_from_set (pbi, SET_DEST (x)); - - return 1; - } - return 0; -} - -/* Try to change INSN so that it does pre-increment or pre-decrement - addressing on register REG in order to add AMOUNT to REG. - AMOUNT is negative for pre-decrement. - Returns 1 if the change could be made. - This checks all about the validity of the result of modifying INSN. */ - -static int -try_pre_increment (rtx insn, rtx reg, HOST_WIDE_INT amount) -{ - rtx use; - - /* Nonzero if we can try to make a pre-increment or pre-decrement. - For example, addl $4,r1; movl (r1),... can become movl +(r1),... */ - int pre_ok = 0; - /* Nonzero if we can try to make a post-increment or post-decrement. - For example, addl $4,r1; movl -4(r1),... can become movl (r1)+,... - It is possible for both PRE_OK and POST_OK to be nonzero if the machine - supports both pre-inc and post-inc, or both pre-dec and post-dec. */ - int post_ok = 0; - - /* Nonzero if the opportunity actually requires post-inc or post-dec. */ - int do_post = 0; - - /* From the sign of increment, see which possibilities are conceivable - on this target machine. */ - if (HAVE_PRE_INCREMENT && amount > 0) - pre_ok = 1; - if (HAVE_POST_INCREMENT && amount > 0) - post_ok = 1; - - if (HAVE_PRE_DECREMENT && amount < 0) - pre_ok = 1; - if (HAVE_POST_DECREMENT && amount < 0) - post_ok = 1; - - if (! (pre_ok || post_ok)) - return 0; - - /* It is not safe to add a side effect to a jump insn - because if the incremented register is spilled and must be reloaded - there would be no way to store the incremented value back in memory. */ - - if (JUMP_P (insn)) - return 0; - - use = 0; - if (pre_ok) - use = find_use_as_address (PATTERN (insn), reg, 0); - if (post_ok && (use == 0 || use == (rtx) (size_t) 1)) - { - use = find_use_as_address (PATTERN (insn), reg, -amount); - do_post = 1; - } - - if (use == 0 || use == (rtx) (size_t) 1) - return 0; - - if (GET_MODE_SIZE (GET_MODE (use)) != (amount > 0 ? amount : - amount)) - return 0; - - /* See if this combination of instruction and addressing mode exists. */ - if (! validate_change (insn, &XEXP (use, 0), - gen_rtx_fmt_e (amount > 0 - ? (do_post ? POST_INC : PRE_INC) - : (do_post ? POST_DEC : PRE_DEC), - Pmode, reg), 0)) - return 0; - - /* Record that this insn now has an implicit side effect on X. */ - REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC, reg, REG_NOTES (insn)); - return 1; -} - -#endif /* AUTO_INC_DEC */ - -/* Find the place in the rtx X where REG is used as a memory address. - Return the MEM rtx that so uses it. - If PLUSCONST is nonzero, search instead for a memory address equivalent to - (plus REG (const_int PLUSCONST)). - - If such an address does not appear, return 0. - If REG appears more than once, or is used other than in such an address, - return (rtx) 1. */ - -rtx -find_use_as_address (rtx x, rtx reg, HOST_WIDE_INT plusconst) -{ - enum rtx_code code = GET_CODE (x); - const char * const fmt = GET_RTX_FORMAT (code); - int i; - rtx value = 0; - rtx tem; - - if (code == MEM && XEXP (x, 0) == reg && plusconst == 0) - return x; - - if (code == MEM && GET_CODE (XEXP (x, 0)) == PLUS - && XEXP (XEXP (x, 0), 0) == reg - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && INTVAL (XEXP (XEXP (x, 0), 1)) == plusconst) - return x; - - if (code == SIGN_EXTRACT || code == ZERO_EXTRACT) - { - /* If REG occurs inside a MEM used in a bit-field reference, - that is unacceptable. */ - if (find_use_as_address (XEXP (x, 0), reg, 0) != 0) - return (rtx) (size_t) 1; - } - - if (x == reg) - return (rtx) (size_t) 1; - - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - tem = find_use_as_address (XEXP (x, i), reg, plusconst); - if (value == 0) - value = tem; - else if (tem != 0) - return (rtx) (size_t) 1; - } - else if (fmt[i] == 'E') - { - int j; - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - { - tem = find_use_as_address (XVECEXP (x, i, j), reg, plusconst); - if (value == 0) - value = tem; - else if (tem != 0) - return (rtx) (size_t) 1; - } - } - } - - return value; -} - -/* Write information about registers and basic blocks into FILE. - This is part of making a debugging dump. */ - -void -dump_regset (regset r, FILE *outf) -{ - unsigned i; - reg_set_iterator rsi; - - if (r == NULL) - { - fputs (" (nil)", outf); - return; - } - - EXECUTE_IF_SET_IN_REG_SET (r, 0, i, rsi) - { - fprintf (outf, " %d", i); - if (i < FIRST_PSEUDO_REGISTER) - fprintf (outf, " [%s]", - reg_names[i]); - } -} - -/* Print a human-readable representation of R on the standard error - stream. This function is designed to be used from within the - debugger. */ - -void -debug_regset (regset r) -{ - dump_regset (r, stderr); - putc ('\n', stderr); -} - -/* Recompute register set/reference counts immediately prior to register - allocation. - - This avoids problems with set/reference counts changing to/from values - which have special meanings to the register allocators. - - Additionally, the reference counts are the primary component used by the - register allocators to prioritize pseudos for allocation to hard regs. - More accurate reference counts generally lead to better register allocation. - - It might be worthwhile to update REG_LIVE_LENGTH, REG_BASIC_BLOCK and - possibly other information which is used by the register allocators. */ - -static unsigned int -recompute_reg_usage (void) -{ - allocate_reg_life_data (); - /* distribute_notes in combiner fails to convert some of the - REG_UNUSED notes to REG_DEAD notes. This causes CHECK_DEAD_NOTES - in sched1 to die. To solve this update the DEATH_NOTES - here. */ - update_life_info (NULL, UPDATE_LIFE_LOCAL, PROP_REG_INFO | PROP_DEATH_NOTES); - - if (dump_file) - dump_flow_info (dump_file, dump_flags); - return 0; -} - -struct tree_opt_pass pass_recompute_reg_usage = -{ - "life2", /* name */ - NULL, /* gate */ - recompute_reg_usage, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - 0, /* tv_id */ - 0, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - TODO_dump_func, /* todo_flags_finish */ - 'f' /* letter */ -}; - -/* Optionally removes all the REG_DEAD and REG_UNUSED notes from a set of - blocks. If BLOCKS is NULL, assume the universal set. Returns a count - of the number of registers that died. - If KILL is 1, remove old REG_DEAD / REG_UNUSED notes. If it is 0, don't. - if it is -1, remove them unless they pertain to a stack reg. */ - -int -count_or_remove_death_notes (sbitmap blocks, int kill) -{ - int count = 0; - unsigned int i = 0; - basic_block bb; - - /* This used to be a loop over all the blocks with a membership test - inside the loop. That can be amazingly expensive on a large CFG - when only a small number of bits are set in BLOCKs (for example, - the calls from the scheduler typically have very few bits set). - - For extra credit, someone should convert BLOCKS to a bitmap rather - than an sbitmap. */ - if (blocks) - { - sbitmap_iterator sbi; - - EXECUTE_IF_SET_IN_SBITMAP (blocks, 0, i, sbi) - { - basic_block bb = BASIC_BLOCK (i); - /* The bitmap may be flawed in that one of the basic blocks - may have been deleted before you get here. */ - if (bb) - count += count_or_remove_death_notes_bb (bb, kill); - }; - } - else - { - FOR_EACH_BB (bb) - { - count += count_or_remove_death_notes_bb (bb, kill); - } - } - - return count; -} - -/* Optionally removes all the REG_DEAD and REG_UNUSED notes from basic - block BB. Returns a count of the number of registers that died. */ - -static int -count_or_remove_death_notes_bb (basic_block bb, int kill) -{ - int count = 0; - rtx insn; - - for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn)) - { - if (INSN_P (insn)) - { - rtx *pprev = ®_NOTES (insn); - rtx link = *pprev; - - while (link) - { - switch (REG_NOTE_KIND (link)) - { - case REG_DEAD: - if (REG_P (XEXP (link, 0))) - { - rtx reg = XEXP (link, 0); - int n; - - if (REGNO (reg) >= FIRST_PSEUDO_REGISTER) - n = 1; - else - n = hard_regno_nregs[REGNO (reg)][GET_MODE (reg)]; - count += n; - } - - /* Fall through. */ - - case REG_UNUSED: - if (kill > 0 - || (kill -#ifdef STACK_REGS - && (!REG_P (XEXP (link, 0)) - || !IN_RANGE (REGNO (XEXP (link, 0)), - FIRST_STACK_REG, LAST_STACK_REG)) -#endif - )) - { - rtx next = XEXP (link, 1); - free_EXPR_LIST_node (link); - *pprev = link = next; - break; - } - /* Fall through. */ - - default: - pprev = &XEXP (link, 1); - link = *pprev; - break; - } - } - } - - if (insn == BB_END (bb)) - break; - } - - return count; -} - -/* Clear LOG_LINKS fields of insns in a selected blocks or whole chain - if blocks is NULL. */ - -static void -clear_log_links (sbitmap blocks) -{ - rtx insn; - - if (!blocks) - { - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (INSN_P (insn)) - free_INSN_LIST_list (&LOG_LINKS (insn)); - } - else - { - unsigned int i = 0; - sbitmap_iterator sbi; - - EXECUTE_IF_SET_IN_SBITMAP (blocks, 0, i, sbi) - { - basic_block bb = BASIC_BLOCK (i); - - for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb)); - insn = NEXT_INSN (insn)) - if (INSN_P (insn)) - free_INSN_LIST_list (&LOG_LINKS (insn)); - } - } -} - -/* Given a register bitmap, turn on the bits in a HARD_REG_SET that - correspond to the hard registers, if any, set in that map. This - could be done far more efficiently by having all sorts of special-cases - with moving single words, but probably isn't worth the trouble. */ - -void -reg_set_to_hard_reg_set (HARD_REG_SET *to, bitmap from) -{ - unsigned i; - bitmap_iterator bi; - - EXECUTE_IF_SET_IN_BITMAP (from, 0, i, bi) - { - if (i >= FIRST_PSEUDO_REGISTER) - return; - SET_HARD_REG_BIT (*to, i); - } -} - - -static bool -gate_remove_death_notes (void) -{ - return flag_profile_values; -} - -static unsigned int -rest_of_handle_remove_death_notes (void) -{ - count_or_remove_death_notes (NULL, 1); - return 0; -} - -struct tree_opt_pass pass_remove_death_notes = -{ - "ednotes", /* name */ - gate_remove_death_notes, /* gate */ - rest_of_handle_remove_death_notes, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - 0, /* tv_id */ - 0, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - 0, /* todo_flags_finish */ - 0 /* letter */ -}; - -/* Perform life analysis. */ -static unsigned int -rest_of_handle_life (void) -{ - regclass_init (); - - life_analysis (PROP_FINAL); - if (optimize) - cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_UPDATE_LIFE | CLEANUP_LOG_LINKS - | (flag_thread_jumps ? CLEANUP_THREADING : 0)); - - if (warn_clobbered) - { - setjmp_vars_warning (DECL_INITIAL (current_function_decl)); - setjmp_args_warning (); - } - - if (optimize) - { - if (initialize_uninitialized_subregs ()) - { - /* Insns were inserted, and possibly pseudos created, so - things might look a bit different. */ - allocate_reg_life_data (); - update_life_info (NULL, UPDATE_LIFE_GLOBAL_RM_NOTES, - PROP_LOG_LINKS | PROP_REG_INFO | PROP_DEATH_NOTES); - } - } - - no_new_pseudos = 1; - return 0; -} - -struct tree_opt_pass pass_life = -{ - "life1", /* name */ - NULL, /* gate */ - rest_of_handle_life, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - TV_FLOW, /* tv_id */ - 0, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - TODO_verify_flow, /* todo_flags_start */ - TODO_dump_func | - TODO_ggc_collect, /* todo_flags_finish */ - 'f' /* letter */ -}; - -static unsigned int -rest_of_handle_flow2 (void) -{ - /* If optimizing, then go ahead and split insns now. */ -#ifndef STACK_REGS - if (optimize > 0) -#endif - split_all_insns (0); - - if (flag_branch_target_load_optimize) - branch_target_load_optimize (epilogue_completed); - - if (optimize) - cleanup_cfg (CLEANUP_EXPENSIVE); - - /* On some machines, the prologue and epilogue code, or parts thereof, - can be represented as RTL. Doing so lets us schedule insns between - it and the rest of the code and also allows delayed branch - scheduling to operate in the epilogue. */ - thread_prologue_and_epilogue_insns (get_insns ()); - epilogue_completed = 1; - flow2_completed = 1; - return 0; -} - -struct tree_opt_pass pass_flow2 = -{ - "flow2", /* name */ - NULL, /* gate */ - rest_of_handle_flow2, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - TV_FLOW2, /* tv_id */ - 0, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - TODO_verify_flow, /* todo_flags_start */ - TODO_dump_func | - TODO_ggc_collect, /* todo_flags_finish */ - 'w' /* letter */ -}; - diff --git a/gcc/function.c b/gcc/function.c index 7d2063c3..10fe7fe 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -63,6 +63,8 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "tree-gimple.h" #include "tree-pass.h" #include "predict.h" +#include "df.h" +#include "timevar.h" #include "vecprim.h" #ifndef LOCAL_ALIGNMENT @@ -100,7 +102,7 @@ int current_function_is_leaf; /* Nonzero if function being compiled doesn't modify the stack pointer (ignoring the prologue and epilogue). This is only valid after - life_analysis has run. */ + pass_stack_ptr_mod has run. */ int current_function_sp_is_unchanging; /* Nonzero if the function being compiled is a leaf function which only @@ -3485,13 +3487,32 @@ pad_below (struct args_size *offset_ptr, enum machine_mode passed_mode, tree siz } } -/* Walk the tree of blocks describing the binding levels within a function - and warn about variables the might be killed by setjmp or vfork. - This is done after calling flow_analysis and before global_alloc - clobbers the pseudo-regs to hard regs. */ -void -setjmp_vars_warning (tree block) +/* True if register REGNO was alive at a place where `setjmp' was + called and was set more than once or is an argument. Such regs may + be clobbered by `longjmp'. */ + +static bool +regno_clobbered_at_setjmp (bitmap setjmp_crosses, int regno) +{ + /* There appear to be cases where some local vars never reach the + backend but have bogus regnos. */ + if (regno >= max_reg_num ()) + return false; + + return ((REG_N_SETS (regno) > 1 + || REGNO_REG_SET_P (df_get_live_out (ENTRY_BLOCK_PTR), regno)) + && REGNO_REG_SET_P (setjmp_crosses, regno)); +} + +/* Walk the tree of blocks describing the binding levels within a + function and warn about variables the might be killed by setjmp or + vfork. This is done after calling flow_analysis before register + allocation since that will clobber the pseudo-regs to hard + regs. */ + +static void +setjmp_vars_warning (bitmap setjmp_crosses, tree block) { tree decl, sub; @@ -3500,32 +3521,47 @@ setjmp_vars_warning (tree block) if (TREE_CODE (decl) == VAR_DECL && DECL_RTL_SET_P (decl) && REG_P (DECL_RTL (decl)) - && regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl)))) + && regno_clobbered_at_setjmp (setjmp_crosses, REGNO (DECL_RTL (decl)))) warning (OPT_Wclobbered, "variable %q+D might be clobbered by" " %<longjmp%> or %<vfork%>", decl); } for (sub = BLOCK_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub)) - setjmp_vars_warning (sub); + setjmp_vars_warning (setjmp_crosses, sub); } /* Do the appropriate part of setjmp_vars_warning but for arguments instead of local variables. */ -void -setjmp_args_warning (void) +static void +setjmp_args_warning (bitmap setjmp_crosses) { tree decl; for (decl = DECL_ARGUMENTS (current_function_decl); decl; decl = TREE_CHAIN (decl)) if (DECL_RTL (decl) != 0 && REG_P (DECL_RTL (decl)) - && regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl)))) + && regno_clobbered_at_setjmp (setjmp_crosses, REGNO (DECL_RTL (decl)))) warning (OPT_Wclobbered, "argument %q+D might be clobbered by %<longjmp%> or %<vfork%>", decl); } +/* Generate warning messages for variables live across setjmp. */ + +void +generate_setjmp_warnings (void) +{ + bitmap setjmp_crosses = regstat_get_setjmp_crosses (); + + if (n_basic_blocks == NUM_FIXED_BLOCKS + || bitmap_empty_p (setjmp_crosses)) + return; + + setjmp_vars_warning (setjmp_crosses, DECL_INITIAL (current_function_decl)); + setjmp_args_warning (setjmp_crosses); +} + /* Identify BLOCKs referenced by more than one NOTE_INSN_BLOCK_{BEG,END}, and create duplicate blocks. */ @@ -4328,6 +4364,14 @@ expand_function_end (void) if (flag_exceptions) sjlj_emit_function_exit_after (get_last_insn ()); } + else + { + /* We want to ensure that instructions that may trap are not + moved into the epilogue by scheduling, because we don't + always emit unwind information for the epilogue. */ + if (flag_non_call_exceptions) + emit_insn (gen_blockage ()); + } /* If this is an implementation of throw, do what's necessary to communicate between __builtin_eh_return and the epilogue. */ @@ -4782,7 +4826,7 @@ keep_stack_depressed (rtx insns) && !fixed_regs[regno] && TEST_HARD_REG_BIT (regs_invalidated_by_call, regno) && !REGNO_REG_SET_P - (EXIT_BLOCK_PTR->il.rtl->global_live_at_start, regno) + (DF_LR_IN (EXIT_BLOCK_PTR), regno) && !refers_to_regno_p (regno, end_hard_regno (Pmode, regno), info.equiv_reg_src, NULL) @@ -4993,8 +5037,8 @@ emit_equiv_load (struct epi_info *p) this into place with notes indicating where the prologue ends and where the epilogue begins. Update the basic block information when possible. */ -void -thread_prologue_and_epilogue_insns (rtx f ATTRIBUTE_UNUSED) +static void +thread_prologue_and_epilogue_insns (void) { int inserted = 0; edge e; @@ -5016,6 +5060,11 @@ thread_prologue_and_epilogue_insns (rtx f ATTRIBUTE_UNUSED) seq = gen_prologue (); emit_insn (seq); + /* Insert an explicit USE for the frame pointer + if the profiling is on and the frame pointer is required. */ + if (current_function_profile && frame_pointer_needed) + emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx)); + /* Retain a map of the prologue insns. */ record_insns (seq, &prologue); prologue_end = emit_note (NOTE_INSN_PROLOGUE_END); @@ -5254,13 +5303,18 @@ epilogue_done: } } #endif + + /* Threading the prologue and epilogue changes the artificial refs + in the entry and exit blocks. */ + epilogue_completed = 1; + df_update_entry_exit_and_calls (); } /* Reposition the prologue-end and epilogue-begin notes after instruction scheduling and delayed branch scheduling. */ void -reposition_prologue_and_epilogue_notes (rtx f ATTRIBUTE_UNUSED) +reposition_prologue_and_epilogue_notes (void) { #if defined (HAVE_prologue) || defined (HAVE_epilogue) rtx insn, last, note; @@ -5273,7 +5327,7 @@ reposition_prologue_and_epilogue_notes (rtx f ATTRIBUTE_UNUSED) /* Scan from the beginning until we reach the last prologue insn. We apparently can't depend on basic_block_{head,end} after reorg has run. */ - for (insn = f; insn; insn = NEXT_INSN (insn)) + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) { if (NOTE_P (insn)) { @@ -5354,6 +5408,13 @@ current_function_name (void) { return lang_hooks.decl_printable_name (cfun->decl, 2); } + +/* Returns the raw (mangled) name of the current function. */ +const char * +current_function_assembler_name (void) +{ + return IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (cfun->decl)); +} static unsigned int @@ -5411,5 +5472,38 @@ struct tree_opt_pass pass_leaf_regs = 0 /* letter */ }; +static unsigned int +rest_of_handle_thread_prologue_and_epilogue (void) +{ + if (optimize) + cleanup_cfg (CLEANUP_EXPENSIVE); + /* On some machines, the prologue and epilogue code, or parts thereof, + can be represented as RTL. Doing so lets us schedule insns between + it and the rest of the code and also allows delayed branch + scheduling to operate in the epilogue. */ + + thread_prologue_and_epilogue_insns (); + return 0; +} + +struct tree_opt_pass pass_thread_prologue_and_epilogue = +{ + "pro_and_epilogue", /* name */ + NULL, /* gate */ + rest_of_handle_thread_prologue_and_epilogue, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_THREAD_PROLOGUE_AND_EPILOGUE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + TODO_verify_flow, /* todo_flags_start */ + TODO_dump_func | + TODO_df_finish | + TODO_ggc_collect, /* todo_flags_finish */ + 'w' /* letter */ +}; + #include "gt-function.h" diff --git a/gcc/function.h b/gcc/function.h index 3367380..bc3378b 100644 --- a/gcc/function.h +++ b/gcc/function.h @@ -1,6 +1,6 @@ /* Structure for saving state for a nested function. Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. + 1999, 2000, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -21,7 +21,6 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #ifndef GCC_FUNCTION_H #define GCC_FUNCTION_H - #include "tree.h" #include "hashtab.h" @@ -307,13 +306,6 @@ struct function GTY(()) /* Function sequence number for profiling, debugging, etc. */ int funcdef_no; - /* For flow.c. */ - - /* Highest loop depth seen so far in loop analysis. Used in flow.c - for the "failure strategy" when doing liveness analysis starting - with non-empty initial sets. */ - int max_loop_depth; - /* For md files. */ /* tm.h can use this to store whatever it likes. */ @@ -537,6 +529,7 @@ extern int trampolines_created; #define avail_temp_slots (cfun->x_avail_temp_slots) #define temp_slot_level (cfun->x_temp_slot_level) #define nonlocal_goto_handler_labels (cfun->x_nonlocal_goto_handler_labels) +#define rtl_df (cfun->df) #define current_loops (cfun->x_current_loops) #define VALUE_HISTOGRAMS(fun) (fun)->value_histograms @@ -583,6 +576,8 @@ extern rtx get_arg_pointer_save_area (struct function *); /* Returns the name of the current function. */ extern const char *current_function_name (void); +/* Returns the assembler name (raw, mangled) of the current function. */ +extern const char *current_function_assembler_name (void); extern void do_warn_unused_parameter (tree); diff --git a/gcc/fwprop.c b/gcc/fwprop.c index 106424e..a3c8bc1 100644 --- a/gcc/fwprop.c +++ b/gcc/fwprop.c @@ -1,5 +1,5 @@ /* RTL-based forward propagation pass for GNU compiler. - Copyright (C) 2005, 2006 Free Software Foundation, Inc. + Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Paolo Bonzini and Steven Bosscher. This file is part of GCC. @@ -104,7 +104,6 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA where the first two insns are now dead. */ -static struct df *df; static int num_changes; @@ -432,23 +431,22 @@ propagate_rtx (rtx x, enum machine_mode mode, rtx old, rtx new) /* Return true if the register from reference REF is killed between FROM to (but not including) TO. */ -static bool +static bool local_ref_killed_between_p (struct df_ref * ref, rtx from, rtx to) { rtx insn; - struct df_ref *def; for (insn = from; insn != to; insn = NEXT_INSN (insn)) { + struct df_ref **def_rec; if (!INSN_P (insn)) continue; - def = DF_INSN_DEFS (df, insn); - while (def) + for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++) { + struct df_ref *def = *def_rec; if (DF_REF_REGNO (ref) == DF_REF_REGNO (def)) return true; - def = def->next_ref; } } return false; @@ -478,14 +476,14 @@ use_killed_between (struct df_ref *use, rtx def_insn, rtx target_insn) uninitialized in a loop. In such cases, we must assume that DEF is not available. */ if (def_bb == target_bb - ? DF_INSN_LUID (df, def_insn) >= DF_INSN_LUID (df, target_insn) + ? DF_INSN_LUID (def_insn) >= DF_INSN_LUID (target_insn) : !dominated_by_p (CDI_DOMINATORS, target_bb, def_bb)) return true; /* Check if the reg in USE has only one definition. We already know that this definition reaches use, or we wouldn't be here. */ regno = DF_REF_REGNO (use); - def = DF_REG_DEF_GET (df, regno)->reg_chain; + def = DF_REG_DEF_CHAIN (regno); if (def && (def->next_reg == NULL)) return false; @@ -501,14 +499,14 @@ use_killed_between (struct df_ref *use, rtx def_insn, rtx target_insn) /* See if USE is killed between DEF_INSN and the last insn in the basic block containing DEF_INSN. */ - x = df_bb_regno_last_def_find (df, def_bb, regno); - if (x && DF_INSN_LUID (df, x->insn) >= DF_INSN_LUID (df, def_insn)) + x = df_bb_regno_last_def_find (def_bb, regno); + if (x && DF_INSN_LUID (x->insn) >= DF_INSN_LUID (def_insn)) return true; /* See if USE is killed between TARGET_INSN and the first insn in the basic block containing TARGET_INSN. */ - x = df_bb_regno_first_def_find (df, target_bb, regno); - if (x && DF_INSN_LUID (df, x->insn) < DF_INSN_LUID (df, target_insn)) + x = df_bb_regno_first_def_find (target_bb, regno); + if (x && DF_INSN_LUID (x->insn) < DF_INSN_LUID (target_insn)) return true; return false; @@ -528,14 +526,14 @@ varying_mem_p (rtx *body, void *data ATTRIBUTE_UNUSED) rtx x = *body; return MEM_P (x) && !MEM_READONLY_P (x); } - + /* Check if all uses in DEF_INSN can be used in TARGET_INSN. This would require full computation of available expressions; we check only restricted conditions, see use_killed_between. */ static bool all_uses_available_at (rtx def_insn, rtx target_insn) { - struct df_ref * use; + struct df_ref **use_rec; rtx def_set = single_set (def_insn); gcc_assert (def_set); @@ -549,17 +547,35 @@ all_uses_available_at (rtx def_insn, rtx target_insn) /* If the insn uses the reg that it defines, the substitution is invalid. */ - for (use = DF_INSN_USES (df, def_insn); use; use = use->next_ref) - if (rtx_equal_p (use->reg, def_reg)) - return false; + for (use_rec = DF_INSN_USES (def_insn); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if (rtx_equal_p (DF_REF_REG (use), def_reg)) + return false; + } + for (use_rec = DF_INSN_EQ_USES (def_insn); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if (rtx_equal_p (use->reg, def_reg)) + return false; + } } else { /* Look at all the uses of DEF_INSN, and see if they are not killed between DEF_INSN and TARGET_INSN. */ - for (use = DF_INSN_USES (df, def_insn); use; use = use->next_ref) - if (use_killed_between (use, def_insn, target_insn)) - return false; + for (use_rec = DF_INSN_USES (def_insn); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if (use_killed_between (use, def_insn, target_insn)) + return false; + } + for (use_rec = DF_INSN_EQ_USES (def_insn); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if (use_killed_between (use, def_insn, target_insn)) + return false; + } } /* We don't do any analysis of memories or aliasing. Reject any @@ -613,34 +629,38 @@ find_occurrence (rtx *px, rtx find) /* Inside INSN, the expression rooted at *LOC has been changed, moving some - uses from ORIG_USES. Find those that are present, and create new items + uses from USE_VEC. Find those that are present, and create new items in the data flow object of the pass. Mark any new uses as having the given TYPE. */ static void -update_df (rtx insn, rtx *loc, struct df_ref *orig_uses, enum df_ref_type type, +update_df (rtx insn, rtx *loc, struct df_ref **use_rec, enum df_ref_type type, int new_flags) { - struct df_ref *use; + bool changed = false; /* Add a use for the registers that were propagated. */ - for (use = orig_uses; use; use = use->next_ref) + while (*use_rec) { + struct df_ref *use = *use_rec; struct df_ref *orig_use = use, *new_use; rtx *new_loc = find_occurrence (loc, DF_REF_REG (orig_use)); + use_rec++; if (!new_loc) continue; /* Add a new insn use. Use the original type, because it says if the use was within a MEM. */ - new_use = df_ref_create (df, DF_REF_REG (orig_use), new_loc, + new_use = df_ref_create (DF_REF_REG (orig_use), new_loc, insn, BLOCK_FOR_INSN (insn), type, DF_REF_FLAGS (orig_use) | new_flags); /* Set up the use-def chain. */ - df_chain_copy (df->problems_by_index[DF_CHAIN], - new_use, DF_REF_CHAIN (orig_use)); + df_chain_copy (new_use, DF_REF_CHAIN (orig_use)); + changed = true; } + if (changed) + df_insn_rescan (insn); } @@ -672,11 +692,12 @@ try_fwprop_subst (struct df_ref *use, rtx *loc, rtx new, rtx def_insn, bool set_ if (dump_file) fprintf (dump_file, "Changed insn %d\n", INSN_UID (insn)); - /* Unlink the use that we changed. */ - df_ref_remove (df, use); + df_ref_remove (use); if (!CONSTANT_P (new)) - update_df (insn, loc, DF_INSN_USES (df, def_insn), type, flags); - + { + update_df (insn, loc, DF_INSN_USES (def_insn), type, flags); + update_df (insn, loc, DF_INSN_EQ_USES (def_insn), type, flags); + } return true; } else @@ -697,8 +718,12 @@ try_fwprop_subst (struct df_ref *use, rtx *loc, rtx new, rtx def_insn, bool set_ /* ??? Is this still necessary if we add the note through set_unique_reg_note? */ if (!CONSTANT_P (new)) - update_df (insn, loc, DF_INSN_USES (df, def_insn), - type, DF_REF_IN_NOTE); + { + update_df (insn, loc, DF_INSN_USES (def_insn), + type, DF_REF_IN_NOTE); + update_df (insn, loc, DF_INSN_EQ_USES (def_insn), + type, DF_REF_IN_NOTE); + } } return false; @@ -793,7 +818,7 @@ forward_propagate_and_simplify (struct df_ref *use, rtx def_insn, rtx def_set) rtx note = find_reg_note (use_insn, REG_EQUAL, NULL_RTX); rtx old = note ? XEXP (note, 0) : SET_SRC (use_set); rtx new = simplify_replace_rtx (old, src, x); - if (old != new) + if (old != new) set_unique_reg_note (use_insn, REG_EQUAL, copy_rtx (new)); } return false; @@ -813,7 +838,7 @@ forward_propagate_and_simplify (struct df_ref *use, rtx def_insn, rtx def_set) loc = &XEXP (note, 0); else loc = &SET_SRC (use_set); - + /* Do not replace an existing REG_EQUAL note if the insn is not recognized. Either we're already replacing in the note, or we'll separately try plugging the definition in the note and @@ -827,7 +852,7 @@ forward_propagate_and_simplify (struct df_ref *use, rtx def_insn, rtx def_set) mode = GET_MODE (*loc); new = propagate_rtx (*loc, mode, reg, src); - + if (!new) return false; @@ -844,11 +869,11 @@ forward_propagate_into (struct df_ref *use) struct df_link *defs; struct df_ref *def; rtx def_insn, def_set, use_insn; - rtx parent; + rtx parent; if (DF_REF_FLAGS (use) & DF_REF_READ_WRITE) return; - if (DF_REF_FLAGS (use) & DF_REF_ARTIFICIAL) + if (DF_REF_IS_ARTIFICIAL (use)) return; /* Only consider uses that have a single definition. */ @@ -859,7 +884,7 @@ forward_propagate_into (struct df_ref *use) def = defs->ref; if (DF_REF_FLAGS (def) & DF_REF_READ_WRITE) return; - if (DF_REF_FLAGS (def) & DF_REF_ARTIFICIAL) + if (DF_REF_IS_ARTIFICIAL (def)) return; /* Do not propagate loop invariant definitions inside the loop. */ @@ -877,6 +902,8 @@ forward_propagate_into (struct df_ref *use) return; def_insn = DF_REF_INSN (def); + if (multiple_sets (def_insn)) + return; def_set = single_set (def_insn); if (!def_set) return; @@ -902,17 +929,18 @@ fwprop_init (void) /* Now set up the dataflow problem (we only want use-def chains) and put the dataflow solver to work. */ - df = df_init (DF_HARD_REGS | DF_SUBREGS | DF_EQUIV_NOTES); - df_chain_add_problem (df, DF_UD_CHAIN); - df_analyze (df); - df_dump (df, dump_file); + df_set_flags (DF_EQ_NOTES); + df_chain_add_problem (DF_UD_CHAIN); + df_analyze (); + df_maybe_reorganize_use_refs (DF_REF_ORDER_BY_INSN_WITH_NOTES); + df_set_flags (DF_DEFER_INSN_RESCAN); } static void fwprop_done (void) { - df_finish (df); loop_optimizer_finalize (); + free_dominance_info (CDI_DOMINATORS); cleanup_cfg (0); delete_trivially_dead_insns (get_insns (), max_reg_num ()); @@ -946,10 +974,9 @@ fwprop (void) Do not forward propagate addresses into loops until after unrolling. CSE did so because it was able to fix its own mess, but we are not. */ - df_reorganize_refs (&df->use_info); - for (i = 0; i < DF_USES_SIZE (df); i++) + for (i = 0; i < DF_USES_TABLE_SIZE (); i++) { - struct df_ref *use = DF_USES_GET (df, i); + struct df_ref *use = DF_USES_GET (i); if (use) if (DF_REF_TYPE (use) == DF_REF_REG_USE || DF_REF_BB (use)->loop_father == NULL) @@ -957,15 +984,14 @@ fwprop (void) } fwprop_done (); - return 0; } struct tree_opt_pass pass_rtl_fwprop = { "fwprop1", /* name */ - gate_fwprop, /* gate */ - fwprop, /* execute */ + gate_fwprop, /* gate */ + fwprop, /* execute */ NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ @@ -974,6 +1000,7 @@ struct tree_opt_pass pass_rtl_fwprop = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func, /* todo_flags_finish */ 0 /* letter */ }; @@ -986,10 +1013,11 @@ fwprop_addr (void) /* Go through all the uses. update_df will create new ones at the end, and we'll go through them as well. */ - df_reorganize_refs (&df->use_info); - for (i = 0; i < DF_USES_SIZE (df); i++) + df_set_flags (DF_DEFER_INSN_RESCAN); + + for (i = 0; i < DF_USES_TABLE_SIZE (); i++) { - struct df_ref *use = DF_USES_GET (df, i); + struct df_ref *use = DF_USES_GET (i); if (use) if (DF_REF_TYPE (use) != DF_REF_REG_USE && DF_REF_BB (use)->loop_father != NULL) @@ -1004,8 +1032,8 @@ fwprop_addr (void) struct tree_opt_pass pass_rtl_fwprop_addr = { "fwprop2", /* name */ - gate_fwprop, /* gate */ - fwprop_addr, /* execute */ + gate_fwprop, /* gate */ + fwprop_addr, /* execute */ NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ @@ -1014,6 +1042,7 @@ struct tree_opt_pass pass_rtl_fwprop_addr = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func, /* todo_flags_finish */ 0 /* letter */ }; @@ -171,6 +171,8 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "timevar.h" #include "tree-pass.h" #include "hashtab.h" +#include "df.h" +#include "dbgcnt.h" /* Propagate flow information through back edges and thus enable PRE's moving loop invariant calculations out of loops. @@ -584,7 +586,7 @@ static void free_pre_mem (void); static void compute_pre_data (void); static int pre_expr_reaches_here_p (basic_block, struct expr *, basic_block); -static void insert_insn_end_bb (struct expr *, basic_block, int); +static void insert_insn_end_basic_block (struct expr *, basic_block, int); static void pre_insert_copy_insn (struct expr *, rtx); static void pre_insert_copies (void); static int pre_delete (void); @@ -628,7 +630,7 @@ static bool store_killed_in_insn (rtx, rtx, rtx, int); static bool store_killed_after (rtx, rtx, rtx, basic_block, int *, rtx *); static bool store_killed_before (rtx, rtx, rtx, basic_block, int *); static void build_store_vectors (void); -static void insert_insn_start_bb (rtx, basic_block); +static void insert_insn_start_basic_block (rtx, basic_block); static int insert_store (struct ls_expr *, edge); static void remove_reachable_equiv_notes (basic_block, struct ls_expr *); static void replace_store_insn (rtx, rtx, basic_block, struct ls_expr *); @@ -673,6 +675,9 @@ gcse_main (rtx f ATTRIBUTE_UNUSED) successors and predecessors. */ max_gcse_regno = max_reg_num (); + df_note_add_problem (); + df_analyze (); + if (dump_file) dump_flow_info (dump_file, dump_flags); @@ -806,7 +811,6 @@ gcse_main (rtx f ATTRIBUTE_UNUSED) /* We are finished with alias. */ end_alias_analysis (); - allocate_reg_info (max_reg_num (), FALSE, FALSE); if (!optimize_size && flag_gcse_sm) { @@ -1728,8 +1732,11 @@ hash_scan_set (rtx pat, rtx insn, struct hash_table *table) { /* An expression is not anticipatable if its operands are modified before this insn or if this is not the only SET in - this insn. */ - int antic_p = oprs_anticipatable_p (src, insn) && single_set (insn); + this insn. The latter condition does not have to mean that + SRC itself is not anticipatable, but we just will not be + able to handle code motion of insns with multiple sets. */ + int antic_p = oprs_anticipatable_p (src, insn) + && !multiple_sets (insn); /* An expression is not available if its operands are subsequently modified, including this insn. It's also not available if this is a branch, because we can't insert @@ -2670,7 +2677,6 @@ try_replace_reg (rtx from, rtx to, rtx insn) if (note != 0 && REG_NOTE_KIND (note) == REG_EQUAL) set_unique_reg_note (insn, REG_EQUAL, simplify_replace_rtx (XEXP (note, 0), from, to)); - if (!success && set && reg_mentioned_p (from, SET_SRC (set))) { /* If above failed and this is a single set, try to simplify the source of @@ -3095,7 +3101,7 @@ do_local_cprop (rtx x, rtx insn, bool alter_jumps, rtx *libcall_sp) /* If we find a case where we can't fix the retval REG_EQUAL notes match the new register, we either have to abandon this replacement or fix delete_trivially_dead_insns to preserve the setting insn, - or make it delete the REG_EUAQL note, and fix up all passes that + or make it delete the REG_EQUAL note, and fix up all passes that require the REG_EQUAL note there. */ bool adjusted; @@ -3164,6 +3170,7 @@ adjust_libcall_notes (rtx oldreg, rtx newval, rtx insn, rtx *libcall_sp) } } XEXP (note, 0) = simplify_replace_rtx (XEXP (note, 0), oldreg, newval); + df_notes_rescan (end); insn = end; } return true; @@ -3214,12 +3221,14 @@ local_cprop_pass (bool alter_jumps) for (reg_used = ®_use_table[0]; reg_use_count > 0; reg_used++, reg_use_count--) - if (do_local_cprop (reg_used->reg_rtx, insn, alter_jumps, - libcall_sp)) - { - changed = true; - break; - } + { + if (do_local_cprop (reg_used->reg_rtx, insn, alter_jumps, + libcall_sp)) + { + changed = true; + break; + } + } if (INSN_DELETED_P (insn)) break; } @@ -3991,7 +4000,7 @@ process_insert_insn (struct expr *expr) no sense for code hoisting. */ static void -insert_insn_end_bb (struct expr *expr, basic_block bb, int pre) +insert_insn_end_basic_block (struct expr *expr, basic_block bb, int pre) { rtx insn = BB_END (bb); rtx new_insn; @@ -4048,7 +4057,7 @@ insert_insn_end_bb (struct expr *expr, basic_block bb, int pre) } #endif /* FIXME: What if something in cc0/jump uses value set in new insn? */ - new_insn = emit_insn_before_noloc (pat, insn); + new_insn = emit_insn_before_noloc (pat, insn, bb); } /* Likewise if the last insn is a call, as will happen in the presence @@ -4087,10 +4096,10 @@ insert_insn_end_bb (struct expr *expr, basic_block bb, int pre) || NOTE_INSN_BASIC_BLOCK_P (insn)) insn = NEXT_INSN (insn); - new_insn = emit_insn_before_noloc (pat, insn); + new_insn = emit_insn_before_noloc (pat, insn, bb); } else - new_insn = emit_insn_after_noloc (pat, insn); + new_insn = emit_insn_after_noloc (pat, insn, bb); while (1) { @@ -4168,7 +4177,7 @@ pre_edge_insert (struct edge_list *edge_list, struct expr **index_map) now. */ if (eg->flags & EDGE_ABNORMAL) - insert_insn_end_bb (index_map[j], bb, 0); + insert_insn_end_basic_block (index_map[j], bb, 0); else { insn = process_insert_insn (index_map[j]); @@ -4438,7 +4447,8 @@ pre_delete (void) /* We only delete insns that have a single_set. */ if (TEST_BIT (pre_delete_map[bb->index], indx) - && (set = single_set (insn)) != 0) + && (set = single_set (insn)) != 0 + && dbg_cnt (pre_insn)) { /* Create a pseudo-reg to store the result of reaching expressions into. Get the mode for the new pseudo from @@ -4515,7 +4525,6 @@ pre_gcse (void) - we know which insns are redundant when we go to create copies */ changed = pre_delete (); - did_insert = pre_edge_insert (edge_list, index_map); /* In other places with reaching expressions, copy the expression to the @@ -4966,7 +4975,7 @@ hoist_code (void) occr->deleted_p = 1; if (!insn_inserted_p) { - insert_insn_end_bb (index_map[i], bb, 0); + insert_insn_end_basic_block (index_map[i], bb, 0); insn_inserted_p = 1; } } @@ -5437,6 +5446,7 @@ update_ld_motion_stores (struct expr * expr) new = emit_insn_before (copy, insn); record_one_set (REGNO (reg), new); SET_SRC (pat) = reg; + df_insn_rescan (insn); /* un-recognize this pattern since it's probably different now. */ INSN_CODE (insn) = -1; @@ -5553,8 +5563,10 @@ extract_mentioned_regs_helper (rtx x, rtx accum) case PRE_DEC: case PRE_INC: + case PRE_MODIFY: case POST_DEC: case POST_INC: + case POST_MODIFY: /* We do not run this function with arguments having side effects. */ gcc_unreachable (); @@ -6148,7 +6160,7 @@ build_store_vectors (void) the BB_HEAD if needed. */ static void -insert_insn_start_bb (rtx insn, basic_block bb) +insert_insn_start_basic_block (rtx insn, basic_block bb) { /* Insert at start of successor block. */ rtx prev = PREV_INSN (BB_HEAD (bb)); @@ -6164,7 +6176,7 @@ insert_insn_start_bb (rtx insn, basic_block bb) before = NEXT_INSN (before); } - insn = emit_insn_after_noloc (insn, prev); + insn = emit_insn_after_noloc (insn, prev, bb); if (dump_file) { @@ -6221,7 +6233,7 @@ insert_store (struct ls_expr * expr, edge e) int index = EDGE_INDEX (edge_list, tmp->src, tmp->dest); RESET_BIT (pre_insert_map[index], expr->index); } - insert_insn_start_bb (insn, bb); + insert_insn_start_basic_block (insn, bb); return 0; } @@ -6590,7 +6602,6 @@ bypass_jumps (void) /* We are finished with alias. */ end_alias_analysis (); - allocate_reg_info (max_reg_num (), FALSE, FALSE); return changed; } @@ -6645,14 +6656,12 @@ gate_handle_jump_bypass (void) static unsigned int rest_of_handle_jump_bypass (void) { - cleanup_cfg (CLEANUP_EXPENSIVE); - reg_scan (get_insns (), max_reg_num ()); - + delete_unreachable_blocks (); if (bypass_jumps ()) { - rebuild_jump_labels (get_insns ()); - cleanup_cfg (CLEANUP_EXPENSIVE); delete_trivially_dead_insns (get_insns (), max_reg_num ()); + rebuild_jump_labels (get_insns ()); + cleanup_cfg (0); } return 0; } @@ -6688,11 +6697,9 @@ rest_of_handle_gcse (void) { int save_csb, save_cfj; int tem2 = 0, tem; - tem = gcse_main (get_insns ()); - rebuild_jump_labels (get_insns ()); delete_trivially_dead_insns (get_insns (), max_reg_num ()); - + rebuild_jump_labels (get_insns ()); save_csb = flag_cse_skip_blocks; save_cfj = flag_cse_follow_jumps; flag_cse_skip_blocks = flag_cse_follow_jumps = 0; @@ -6702,7 +6709,6 @@ rest_of_handle_gcse (void) if (flag_expensive_optimizations) { timevar_push (TV_CSE); - reg_scan (get_insns (), max_reg_num ()); tem2 = cse_main (get_insns (), max_reg_num ()); purge_all_dead_edges (); delete_trivially_dead_insns (get_insns (), max_reg_num ()); @@ -6716,8 +6722,7 @@ rest_of_handle_gcse (void) { timevar_push (TV_JUMP); rebuild_jump_labels (get_insns ()); - delete_dead_jumptables (); - cleanup_cfg (CLEANUP_EXPENSIVE); + cleanup_cfg (0); timevar_pop (TV_JUMP); } @@ -6739,6 +6744,7 @@ struct tree_opt_pass pass_gcse = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func | TODO_verify_flow | TODO_ggc_collect, /* todo_flags_finish */ 'G' /* letter */ diff --git a/gcc/gengtype.c b/gcc/gengtype.c index ae1f93d..d9d942f 100644 --- a/gcc/gengtype.c +++ b/gcc/gengtype.c @@ -1077,7 +1077,7 @@ adjust_field_rtx_def (type_p t, options_p ARG_UNUSED (opt)) case '0': if (i == MEM && aindex == 1) t = mem_attrs_tp, subname = "rt_mem"; - else if (i == JUMP_INSN && aindex == 9) + else if (i == JUMP_INSN && aindex == 8) t = rtx_tp, subname = "rt_rtx"; else if (i == CODE_LABEL && aindex == 4) t = scalar_tp, subname = "rt_int"; diff --git a/gcc/global.c b/gcc/global.c index 0495b4b..c60c57d 100644 --- a/gcc/global.c +++ b/gcc/global.c @@ -1,6 +1,7 @@ /* Allocate registers for pseudo-registers that span basic blocks. Copyright (C) 1987, 1988, 1991, 1994, 1996, 1997, 1998, - 1999, 2000, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. This file is part of GCC. @@ -38,6 +39,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "toplev.h" #include "tree-pass.h" #include "timevar.h" +#include "df.h" #include "vecprim.h" /* This pass of the compiler performs global register allocation. @@ -151,12 +153,6 @@ static struct allocno *allocno; static int *allocno_order; -/* Indexed by (pseudo) reg number, gives the number of another - lower-numbered pseudo reg which can share a hard reg with this pseudo - *even if the two pseudos would otherwise appear to conflict*. */ - -static int *reg_may_share; - /* Define the number of bits in each element of `conflicts' and what type that element has. We use the largest integer format on the host machine. */ @@ -277,8 +273,63 @@ static struct { int allocno1, allocno2;} /* Record all regs that are set in any one insn. Communication from mark_reg_{store,clobber} and global_conflicts. */ -static rtx *regs_set; -static int n_regs_set; +static VEC(rtx, heap) *regs_set; + + +/* Return true if *LOC contains an asm. */ + +static int +insn_contains_asm_1 (rtx *loc, void *data ATTRIBUTE_UNUSED) +{ + if ( !*loc) + return 0; + if (GET_CODE (*loc) == ASM_OPERANDS) + return 1; + return 0; +} + + +/* Return true if INSN contains an ASM. */ + +static int +insn_contains_asm (rtx insn) +{ + return for_each_rtx (&insn, insn_contains_asm_1, NULL); +} + + +static void +compute_regs_asm_clobbered (char *regs_asm_clobbered) +{ + basic_block bb; + + memset (regs_asm_clobbered, 0, sizeof (char) * FIRST_PSEUDO_REGISTER); + + FOR_EACH_BB (bb) + { + rtx insn; + FOR_BB_INSNS_REVERSE (bb, insn) + { + struct df_ref **def_rec; + if (insn_contains_asm (insn)) + for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + unsigned int dregno = DF_REF_REGNO (def); + if (dregno < FIRST_PSEUDO_REGISTER) + { + unsigned int i; + enum machine_mode mode = GET_MODE (DF_REF_REAL_REG (def)); + unsigned int end = dregno + + hard_regno_nregs[dregno][mode] - 1; + for (i = dregno; i <= end; ++i) + regs_asm_clobbered[i] = 1; + } + } + } + } +} + /* All registers that can be eliminated. */ @@ -301,38 +352,27 @@ static void dump_conflicts (FILE *); static void reg_becomes_live (rtx, rtx, void *); static void reg_dies (int, enum machine_mode, struct insn_chain *); -static void allocate_bb_info (void); -static void free_bb_info (void); -static bool check_earlyclobber (rtx); -static void mark_reg_use_for_earlyclobber_1 (rtx *, void *); -static int mark_reg_use_for_earlyclobber (rtx *, void *); -static void calculate_local_reg_bb_info (void); -static void set_up_bb_rts_numbers (void); -static int rpost_cmp (const void *, const void *); -static void calculate_reg_pav (void); -static void modify_reg_pav (void); -static void make_accurate_live_analysis (void); -/* Look through the list of eliminable registers. Add registers - clobbered by asm statements to LIVE_REGS. Set ELIM_SET to the set of - registers which may be eliminated. Set NO_GLOBAL_SET to the set of - registers which may not be used across blocks. - - ASM_CLOBBERED is the set of registers clobbered by some asm statement. +/* Look through the list of eliminable registers. Set ELIM_SET to the + set of registers which may be eliminated. Set NO_GLOBAL_SET to the + set of registers which may not be used across blocks. - This will normally be called with LIVE_REGS as the global variable - regs_ever_live, ELIM_SET as the file static variable - eliminable_regset, and NO_GLOBAL_SET as the file static variable - NO_GLOBAL_ALLOC_REGS. */ + This will normally be called with ELIM_SET as the file static + variable eliminable_regset, and NO_GLOBAL_SET as the file static + variable NO_GLOBAL_ALLOC_REGS. */ static void -compute_regsets (char asm_clobbered[FIRST_PSEUDO_REGISTER], - char live_regs[FIRST_PSEUDO_REGISTER], - HARD_REG_SET *elim_set, +compute_regsets (HARD_REG_SET *elim_set, HARD_REG_SET *no_global_set) { + +/* Like regs_ever_live, but 1 if a reg is set or clobbered from an asm. + Unlike regs_ever_live, elements of this array corresponding to + eliminable regs like the frame pointer are set if an asm sets them. */ + char *regs_asm_clobbered = alloca (FIRST_PSEUDO_REGISTER * sizeof (char)); + #ifdef ELIMINABLE_REGS static const struct {const int from, to; } eliminables[] = ELIMINABLE_REGS; size_t i; @@ -342,12 +382,18 @@ compute_regsets (char asm_clobbered[FIRST_PSEUDO_REGISTER], || (current_function_calls_alloca && EXIT_IGNORE_STACK) || FRAME_POINTER_REQUIRED); + max_regno = max_reg_num (); + compact_blocks (); + + max_allocno = 0; + /* A machine may have certain hard registers that are safe to use only within a basic block. */ CLEAR_HARD_REG_SET (*no_global_set); CLEAR_HARD_REG_SET (*elim_set); + compute_regs_asm_clobbered (regs_asm_clobbered); /* Build the regset of all eliminable registers and show we can't use those that we already know won't be eliminated. */ #ifdef ELIMINABLE_REGS @@ -357,7 +403,7 @@ compute_regsets (char asm_clobbered[FIRST_PSEUDO_REGISTER], = (! CAN_ELIMINATE (eliminables[i].from, eliminables[i].to) || (eliminables[i].to == STACK_POINTER_REGNUM && need_fp)); - if (!asm_clobbered[eliminables[i].from]) + if (!regs_asm_clobbered[eliminables[i].from]) { SET_HARD_REG_BIT (*elim_set, eliminables[i].from); @@ -368,10 +414,10 @@ compute_regsets (char asm_clobbered[FIRST_PSEUDO_REGISTER], error ("%s cannot be used in asm here", reg_names[eliminables[i].from]); else - live_regs[eliminables[i].from] = 1; + df_set_regs_ever_live (eliminables[i].from, true); } #if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM - if (!asm_clobbered[HARD_FRAME_POINTER_REGNUM]) + if (!regs_asm_clobbered[HARD_FRAME_POINTER_REGNUM]) { SET_HARD_REG_BIT (*elim_set, HARD_FRAME_POINTER_REGNUM); if (need_fp) @@ -381,11 +427,11 @@ compute_regsets (char asm_clobbered[FIRST_PSEUDO_REGISTER], error ("%s cannot be used in asm here", reg_names[HARD_FRAME_POINTER_REGNUM]); else - live_regs[HARD_FRAME_POINTER_REGNUM] = 1; + df_set_regs_ever_live (HARD_FRAME_POINTER_REGNUM, true); #endif #else - if (!asm_clobbered[FRAME_POINTER_REGNUM]) + if (!regs_asm_clobbered[FRAME_POINTER_REGNUM]) { SET_HARD_REG_BIT (*elim_set, FRAME_POINTER_REGNUM); if (need_fp) @@ -394,7 +440,7 @@ compute_regsets (char asm_clobbered[FIRST_PSEUDO_REGISTER], else if (need_fp) error ("%s cannot be used in asm here", reg_names[FRAME_POINTER_REGNUM]); else - live_regs[FRAME_POINTER_REGNUM] = 1; + df_set_regs_ever_live (FRAME_POINTER_REGNUM, true); #endif } @@ -408,12 +454,8 @@ global_alloc (void) { int retval; size_t i; - rtx x; - - make_accurate_live_analysis (); - compute_regsets (regs_asm_clobbered, regs_ever_live, - &eliminable_regset, &no_global_alloc_regs); + compute_regsets (&eliminable_regset, &no_global_alloc_regs); /* Track which registers have already been used. Start with registers explicitly in the rtl, then registers allocated by local register @@ -434,14 +476,14 @@ global_alloc (void) else cheap_regs = call_used_regs; for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (regs_ever_live[i] || cheap_regs[i]) + if (df_regs_ever_live_p (i) || cheap_regs[i]) SET_HARD_REG_BIT (regs_used_so_far, i); } #else /* We consider registers that do not have to be saved over calls as if they were already used since there is no cost in using them. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (regs_ever_live[i] || call_used_regs[i]) + if (df_regs_ever_live_p (i) || call_used_regs[i]) SET_HARD_REG_BIT (regs_used_so_far, i); #endif @@ -457,19 +499,6 @@ global_alloc (void) for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) reg_allocno[i] = -1; - /* Initialize the shared-hard-reg mapping - from the list of pairs that may share. */ - reg_may_share = XCNEWVEC (int, max_regno); - for (x = regs_may_share; x; x = XEXP (XEXP (x, 1), 1)) - { - int r1 = REGNO (XEXP (x, 0)); - int r2 = REGNO (XEXP (XEXP (x, 1), 0)); - if (r1 > r2) - reg_may_share[r1] = r2; - else - reg_may_share[r2] = r1; - } - max_allocno = 0; for (i = FIRST_PSEUDO_REGISTER; i < (size_t) max_regno; i++) /* Note that reg_live_length[i] < 0 indicates a "constant" reg @@ -481,11 +510,7 @@ global_alloc (void) && (! current_function_has_nonlocal_label || REG_N_CALLS_CROSSED (i) == 0)) { - if (reg_renumber[i] < 0 - && reg_may_share[i] && reg_allocno[reg_may_share[i]] >= 0) - reg_allocno[i] = reg_allocno[reg_may_share[i]]; - else - reg_allocno[i] = max_allocno++; + reg_allocno[i] = max_allocno++; gcc_assert (REG_LIVE_LENGTH (i)); } else @@ -531,9 +556,22 @@ global_alloc (void) /* We can't override local-alloc for a reg used not just by local-alloc. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (regs_ever_live[i]) + if (df_regs_ever_live_p (i)) local_reg_n_refs[i] = 0, local_reg_freq[i] = 0; + if (dump_file) + { + for (i = FIRST_PSEUDO_REGISTER; i < (size_t) max_regno; i++) + { + fprintf (dump_file, "%d REG_N_REFS=%d, REG_FREQ=%d, REG_LIVE_LENGTH=%d\n", + (int)i, REG_N_REFS (i), REG_FREQ (i), REG_LIVE_LENGTH (i)); + } + fprintf (dump_file, "regs_ever_live ="); + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (df_regs_ever_live_p (i)) + fprintf (dump_file, " %d", (int)i); + fprintf (dump_file, "\n"); + } allocno_row_words = (max_allocno + INT_BITS - 1) / INT_BITS; /* We used to use alloca here, but the size of what it would try to @@ -548,6 +586,10 @@ global_alloc (void) if (max_allocno > 0) { + /* Make a vector that mark_reg_{store,clobber} will store in. */ + if (!regs_set) + regs_set = VEC_alloc (rtx, heap, 10); + /* Scan all the insns and compute the conflicts among allocnos and between allocnos and hard regs. */ @@ -641,7 +683,6 @@ global_alloc (void) /* Clean up. */ free (reg_allocno); - free (reg_may_share); free (allocno); free (conflicts); free (allocnos_live); @@ -688,9 +729,6 @@ global_conflicts (void) rtx insn; int *block_start_allocnos; - /* Make a vector that mark_reg_{store,clobber} will store in. */ - regs_set = XNEWVEC (rtx, max_parallel * 2); - block_start_allocnos = XNEWVEC (int, max_allocno); FOR_EACH_BB (b) @@ -712,12 +750,11 @@ global_conflicts (void) be explicitly marked in basic_block_live_at_start. */ { - regset old = b->il.rtl->global_live_at_start; int ax = 0; reg_set_iterator rsi; - REG_SET_TO_HARD_REG_SET (hard_regs_live, old); - EXECUTE_IF_SET_IN_REG_SET (old, FIRST_PSEUDO_REGISTER, i, rsi) + REG_SET_TO_HARD_REG_SET (hard_regs_live, DF_RA_LIVE_TOP (b)); + EXECUTE_IF_SET_IN_REG_SET (DF_RA_LIVE_TOP (b), FIRST_PSEUDO_REGISTER, i, rsi) { int a = reg_allocno[i]; if (a >= 0) @@ -816,13 +853,9 @@ global_conflicts (void) RTX_CODE code = GET_CODE (insn); rtx link; - /* Make regs_set an empty set. */ - - n_regs_set = 0; - + gcc_assert (VEC_empty (rtx, regs_set)); if (code == INSN || code == CALL_INSN || code == JUMP_INSN) { - #if 0 int i = 0; for (link = REG_NOTES (insn); @@ -843,6 +876,13 @@ global_conflicts (void) note_stores (PATTERN (insn), mark_reg_clobber, NULL); +#ifdef AUTO_INC_DEC + /* Auto-increment instructions clobber the base + register. */ + for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) + if (REG_NOTE_KIND (link) == REG_INC) + mark_reg_store (XEXP (link, 0), NULL_RTX, NULL); +#endif /* Mark any registers dead after INSN as dead now. */ for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) @@ -856,12 +896,6 @@ global_conflicts (void) note_stores (PATTERN (insn), mark_reg_store, NULL); -#ifdef AUTO_INC_DEC - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_INC) - mark_reg_store (XEXP (link, 0), NULL_RTX, NULL); -#endif - /* If INSN has multiple outputs, then any reg that dies here and is used inside of an output must conflict with the other outputs. @@ -896,10 +930,11 @@ global_conflicts (void) /* Mark any registers set in INSN and then never used. */ - while (n_regs_set-- > 0) + while (!VEC_empty (rtx, regs_set)) { + rtx reg = VEC_pop (rtx, regs_set); rtx note = find_regno_note (insn, REG_UNUSED, - REGNO (regs_set[n_regs_set])); + REGNO (reg)); if (note) mark_reg_death (XEXP (note, 0)); } @@ -913,8 +948,8 @@ global_conflicts (void) /* Clean up. */ free (block_start_allocnos); - free (regs_set); } + /* Expand the preference information by looking for cases where one allocno dies in an insn that sets an allocno. If those two allocnos don't conflict, merge any preferences between those allocnos. */ @@ -1355,11 +1390,6 @@ find_reg (int num, HARD_REG_SET losers, int alt_regs_p, int accept_call_clobbere /* Yes. Record it as the hard register of this pseudo-reg. */ reg_renumber[allocno[num].reg] = best_reg; - /* Also of any pseudo-regs that share with it. */ - if (reg_may_share[allocno[num].reg]) - for (j = FIRST_PSEUDO_REGISTER; j < max_regno; j++) - if (reg_allocno[j] == num) - reg_renumber[j] = best_reg; /* Make a set of the hard regs being allocated. */ CLEAR_HARD_REG_SET (this_reg); @@ -1408,7 +1438,7 @@ retry_global_alloc (int regno, HARD_REG_SET forbidden_regs) show the hard register, and mark that register live. */ if (reg_renumber[regno] >= 0) { - REGNO (regno_reg_rtx[regno]) = reg_renumber[regno]; + SET_REGNO (regno_reg_rtx[regno], reg_renumber[regno]); mark_home_live (regno); } } @@ -1520,7 +1550,7 @@ mark_reg_store (rtx reg, rtx setter, void *data ATTRIBUTE_UNUSED) if (!REG_P (reg)) return; - regs_set[n_regs_set++] = reg; + VEC_safe_push (rtx, heap, regs_set, reg); if (setter && GET_CODE (setter) != CLOBBER) set_preference (reg, SET_SRC (setter)); @@ -1750,7 +1780,7 @@ mark_elimination (int from, int to) FOR_EACH_BB (bb) { - regset r = bb->il.rtl->global_live_at_start; + regset r = DF_RA_LIVE_IN (bb); if (REGNO_REG_SET_P (r, from)) { CLEAR_REGNO_REG_SET (r, from); @@ -1782,15 +1812,22 @@ reg_becomes_live (rtx reg, rtx setter ATTRIBUTE_UNUSED, void *regs_set) int nregs = hard_regno_nregs[regno][GET_MODE (reg)]; while (nregs-- > 0) { - SET_REGNO_REG_SET (live_relevant_regs, regno); - if (! fixed_regs[regno]) + if (GET_CODE (setter) == CLOBBER) + CLEAR_REGNO_REG_SET (live_relevant_regs, regno); + else + SET_REGNO_REG_SET (live_relevant_regs, regno); + + if (!fixed_regs[regno]) SET_REGNO_REG_SET ((regset) regs_set, regno); regno++; } } else if (reg_renumber[regno] >= 0) { - SET_REGNO_REG_SET (live_relevant_regs, regno); + if (GET_CODE (setter) == CLOBBER) + CLEAR_REGNO_REG_SET (live_relevant_regs, regno); + else + SET_REGNO_REG_SET (live_relevant_regs, regno); SET_REGNO_REG_SET ((regset) regs_set, regno); } } @@ -1840,7 +1877,7 @@ build_insn_chain (rtx first) CLEAR_REG_SET (live_relevant_regs); - EXECUTE_IF_SET_IN_BITMAP (b->il.rtl->global_live_at_start, 0, i, bi) + EXECUTE_IF_SET_IN_BITMAP (df_get_live_top (b), 0, i, bi) { if (i < FIRST_PSEUDO_REGISTER ? ! TEST_HARD_REG_BIT (eliminable_regset, i) @@ -1998,488 +2035,11 @@ dump_global_regs (FILE *file) fprintf (file, "\n\n;; Hard regs used: "); for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (regs_ever_live[i]) + if (df_regs_ever_live_p (i)) fprintf (file, " %d", i); fprintf (file, "\n\n"); } - - -/* This page contains code to make live information more accurate. - The accurate register liveness at program point P means: - o there is a path from P to usage of the register and the - register is not redefined or killed on the path. - o register at P is partially available, i.e. there is a path from - a register definition to the point P and the register is not - killed (clobbered) on the path - - The standard GCC live information means only the first condition. - Without the partial availability, there will be more register - conflicts and as a consequence worse register allocation. The - typical example where the information can be different is a - register initialized in the loop at the basic block preceding the - loop in CFG. */ - -/* The following structure contains basic block data flow information - used to calculate partial availability of registers. */ - -struct bb_info -{ - /* The basic block reverse post-order number. */ - int rts_number; - /* Registers used uninitialized in an insn in which there is an - early clobbered register might get the same hard register. */ - bitmap earlyclobber; - /* Registers correspondingly killed (clobbered) and defined but not - killed afterward in the basic block. */ - bitmap killed, avloc; - /* Registers partially available and living (in other words whose - values were calculated and used) correspondingly at the start - and end of the basic block. */ - bitmap live_pavin, live_pavout; -}; - -/* Macros for accessing data flow information of basic blocks. */ - -#define BB_INFO(BB) ((struct bb_info *) (BB)->aux) -#define BB_INFO_BY_INDEX(N) BB_INFO (BASIC_BLOCK(N)) - -static struct bitmap_obstack greg_obstack; -/* The function allocates the info structures of each basic block. It - also initialized LIVE_PAVIN and LIVE_PAVOUT as if all hard - registers were partially available. */ - -static void -allocate_bb_info (void) -{ - int i; - basic_block bb; - struct bb_info *bb_info; - bitmap init; - - alloc_aux_for_blocks (sizeof (struct bb_info)); - init = BITMAP_ALLOC (NULL); - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - bitmap_set_bit (init, i); - bitmap_obstack_initialize (&greg_obstack); - FOR_EACH_BB (bb) - { - bb_info = bb->aux; - bb_info->earlyclobber = BITMAP_ALLOC (&greg_obstack); - bb_info->avloc = BITMAP_ALLOC (&greg_obstack); - bb_info->killed = BITMAP_ALLOC (&greg_obstack); - bb_info->live_pavin = BITMAP_ALLOC (&greg_obstack); - bb_info->live_pavout = BITMAP_ALLOC (&greg_obstack); - bitmap_copy (bb_info->live_pavin, init); - bitmap_copy (bb_info->live_pavout, init); - } - BITMAP_FREE (init); -} - -/* The function frees the allocated info of all basic blocks. */ - -static void -free_bb_info (void) -{ - bitmap_obstack_release (&greg_obstack); - free_aux_for_blocks (); -} - -/* The function modifies local info for register REG being changed in - SETTER. DATA is used to pass the current basic block info. */ - -static void -mark_reg_change (rtx reg, rtx setter, void *data) -{ - int regno; - basic_block bb = data; - struct bb_info *bb_info = BB_INFO (bb); - - if (GET_CODE (reg) == SUBREG) - reg = SUBREG_REG (reg); - - if (!REG_P (reg)) - return; - - regno = REGNO (reg); - bitmap_set_bit (bb_info->killed, regno); - - if (GET_CODE (setter) != CLOBBER) - bitmap_set_bit (bb_info->avloc, regno); - else - bitmap_clear_bit (bb_info->avloc, regno); -} - -/* Classes of registers which could be early clobbered in the current - insn. */ - -static VEC(int,heap) *earlyclobber_regclass; - -/* This function finds and stores register classes that could be early - clobbered in INSN. If any earlyclobber classes are found, the function - returns TRUE, in all other cases it returns FALSE. */ - -static bool -check_earlyclobber (rtx insn) -{ - int opno; - bool found = false; - - extract_insn (insn); - - VEC_truncate (int, earlyclobber_regclass, 0); - for (opno = 0; opno < recog_data.n_operands; opno++) - { - char c; - bool amp_p; - int i; - enum reg_class class; - const char *p = recog_data.constraints[opno]; - - class = NO_REGS; - amp_p = false; - for (;;) - { - c = *p; - switch (c) - { - case '=': case '+': case '?': - case '#': case '!': - case '*': case '%': - case 'm': case '<': case '>': case 'V': case 'o': - case 'E': case 'F': case 'G': case 'H': - case 's': case 'i': case 'n': - case 'I': case 'J': case 'K': case 'L': - case 'M': case 'N': case 'O': case 'P': - case 'X': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - /* These don't say anything we care about. */ - break; - - case '&': - amp_p = true; - break; - case '\0': - case ',': - if (amp_p && class != NO_REGS) - { - int rc; - - found = true; - for (i = 0; - VEC_iterate (int, earlyclobber_regclass, i, rc); - i++) - { - if (rc == (int) class) - goto found_rc; - } - - /* We use VEC_quick_push here because - earlyclobber_regclass holds no more than - N_REG_CLASSES elements. */ - VEC_quick_push (int, earlyclobber_regclass, (int) class); - found_rc: - ; - } - - amp_p = false; - class = NO_REGS; - break; - - case 'r': - class = GENERAL_REGS; - break; - - default: - class = REG_CLASS_FROM_CONSTRAINT (c, p); - break; - } - if (c == '\0') - break; - p += CONSTRAINT_LEN (c, p); - } - } - - return found; -} - -/* The function checks that pseudo-register *X has a class - intersecting with the class of pseudo-register could be early - clobbered in the same insn. - This function is a no-op if earlyclobber_regclass is empty. */ - -static int -mark_reg_use_for_earlyclobber (rtx *x, void *data ATTRIBUTE_UNUSED) -{ - enum reg_class pref_class, alt_class; - int i, regno; - basic_block bb = data; - struct bb_info *bb_info = BB_INFO (bb); - - if (REG_P (*x) && REGNO (*x) >= FIRST_PSEUDO_REGISTER) - { - int rc; - - regno = REGNO (*x); - if (bitmap_bit_p (bb_info->killed, regno) - || bitmap_bit_p (bb_info->avloc, regno)) - return 0; - pref_class = reg_preferred_class (regno); - alt_class = reg_alternate_class (regno); - for (i = 0; VEC_iterate (int, earlyclobber_regclass, i, rc); i++) - { - if (reg_classes_intersect_p (rc, pref_class) - || (rc != NO_REGS - && reg_classes_intersect_p (rc, alt_class))) - { - bitmap_set_bit (bb_info->earlyclobber, regno); - break; - } - } - } - return 0; -} - -/* The function processes all pseudo-registers in *X with the aid of - previous function. */ - -static void -mark_reg_use_for_earlyclobber_1 (rtx *x, void *data) -{ - for_each_rtx (x, mark_reg_use_for_earlyclobber, data); -} - -/* The function calculates local info for each basic block. */ - -static void -calculate_local_reg_bb_info (void) -{ - basic_block bb; - rtx insn, bound; - - /* We know that earlyclobber_regclass holds no more than - N_REG_CLASSES elements. See check_earlyclobber. */ - earlyclobber_regclass = VEC_alloc (int, heap, N_REG_CLASSES); - FOR_EACH_BB (bb) - { - bound = NEXT_INSN (BB_END (bb)); - for (insn = BB_HEAD (bb); insn != bound; insn = NEXT_INSN (insn)) - if (INSN_P (insn)) - { - note_stores (PATTERN (insn), mark_reg_change, bb); - if (check_earlyclobber (insn)) - note_uses (&PATTERN (insn), mark_reg_use_for_earlyclobber_1, bb); - } - } - VEC_free (int, heap, earlyclobber_regclass); -} - -/* The function sets up reverse post-order number of each basic - block. */ - -static void -set_up_bb_rts_numbers (void) -{ - int i; - int *rts_order; - - rts_order = XNEWVEC (int, n_basic_blocks - NUM_FIXED_BLOCKS); - post_order_compute (rts_order, false); - for (i = 0; i < n_basic_blocks - NUM_FIXED_BLOCKS; i++) - BB_INFO_BY_INDEX (rts_order [i])->rts_number = i; - free (rts_order); -} - -/* Compare function for sorting blocks in reverse postorder. */ - -static int -rpost_cmp (const void *bb1, const void *bb2) -{ - basic_block b1 = *(basic_block *) bb1, b2 = *(basic_block *) bb2; - - return BB_INFO (b2)->rts_number - BB_INFO (b1)->rts_number; -} - -/* Temporary bitmap used for live_pavin, live_pavout calculation. */ -static bitmap temp_bitmap; - -/* The function calculates partial register availability according to - the following equations: - - bb.live_pavin - = empty for entry block - | union (live_pavout of predecessors) & global_live_at_start - bb.live_pavout = union (bb.live_pavin - bb.killed, bb.avloc) - & global_live_at_end */ - -static void -calculate_reg_pav (void) -{ - basic_block bb, succ; - edge e; - int i, nel; - VEC(basic_block,heap) *bbs, *new_bbs, *temp; - basic_block *bb_array; - sbitmap wset; - - bbs = VEC_alloc (basic_block, heap, n_basic_blocks); - new_bbs = VEC_alloc (basic_block, heap, n_basic_blocks); - temp_bitmap = BITMAP_ALLOC (NULL); - FOR_EACH_BB (bb) - { - VEC_quick_push (basic_block, bbs, bb); - } - wset = sbitmap_alloc (n_basic_blocks + 1); - while (VEC_length (basic_block, bbs)) - { - bb_array = VEC_address (basic_block, bbs); - nel = VEC_length (basic_block, bbs); - qsort (bb_array, nel, sizeof (basic_block), rpost_cmp); - sbitmap_zero (wset); - for (i = 0; i < nel; i++) - { - edge_iterator ei; - struct bb_info *bb_info; - bitmap bb_live_pavin, bb_live_pavout; - - bb = bb_array [i]; - bb_info = BB_INFO (bb); - bb_live_pavin = bb_info->live_pavin; - bb_live_pavout = bb_info->live_pavout; - FOR_EACH_EDGE (e, ei, bb->preds) - { - basic_block pred = e->src; - - if (pred->index != ENTRY_BLOCK) - bitmap_ior_into (bb_live_pavin, BB_INFO (pred)->live_pavout); - } - bitmap_and_into (bb_live_pavin, bb->il.rtl->global_live_at_start); - bitmap_ior_and_compl (temp_bitmap, bb_info->avloc, - bb_live_pavin, bb_info->killed); - bitmap_and_into (temp_bitmap, bb->il.rtl->global_live_at_end); - if (! bitmap_equal_p (temp_bitmap, bb_live_pavout)) - { - bitmap_copy (bb_live_pavout, temp_bitmap); - FOR_EACH_EDGE (e, ei, bb->succs) - { - succ = e->dest; - if (succ->index != EXIT_BLOCK - && !TEST_BIT (wset, succ->index)) - { - SET_BIT (wset, succ->index); - VEC_quick_push (basic_block, new_bbs, succ); - } - } - } - } - temp = bbs; - bbs = new_bbs; - new_bbs = temp; - VEC_truncate (basic_block, new_bbs, 0); - } - sbitmap_free (wset); - BITMAP_FREE (temp_bitmap); - VEC_free (basic_block, heap, new_bbs); - VEC_free (basic_block, heap, bbs); -} - -/* The function modifies partial availability information for two - special cases to prevent incorrect work of the subsequent passes - with the accurate live information based on the partial - availability. */ - -static void -modify_reg_pav (void) -{ - basic_block bb; - struct bb_info *bb_info; -#ifdef STACK_REGS - int i; - HARD_REG_SET stack_hard_regs, used; - bitmap stack_regs; - - CLEAR_HARD_REG_SET (stack_hard_regs); - for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++) - SET_HARD_REG_BIT(stack_hard_regs, i); - stack_regs = BITMAP_ALLOC (&greg_obstack); - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - { - COPY_HARD_REG_SET (used, reg_class_contents[reg_preferred_class (i)]); - IOR_HARD_REG_SET (used, reg_class_contents[reg_alternate_class (i)]); - AND_HARD_REG_SET (used, stack_hard_regs); - if (!hard_reg_set_empty_p (used)) - bitmap_set_bit (stack_regs, i); - } -#endif - FOR_EACH_BB (bb) - { - bb_info = BB_INFO (bb); - - /* Reload can assign the same hard register to uninitialized - pseudo-register and early clobbered pseudo-register in an - insn if the pseudo-register is used first time in given BB - and not lived at the BB start. To prevent this we don't - change life information for such pseudo-registers. */ - bitmap_ior_into (bb_info->live_pavin, bb_info->earlyclobber); -#ifdef STACK_REGS - /* We can not use the same stack register for uninitialized - pseudo-register and another living pseudo-register because if the - uninitialized pseudo-register dies, subsequent pass reg-stack - will be confused (it will believe that the other register - dies). */ - bitmap_ior_into (bb_info->live_pavin, stack_regs); -#endif - } -#ifdef STACK_REGS - BITMAP_FREE (stack_regs); -#endif -} - -/* The following function makes live information more accurate by - modifying global_live_at_start and global_live_at_end of basic - blocks. - - The standard GCC life analysis permits registers to live - uninitialized, for example: - - R is never used - ..... - Loop: - R is defined - ... - R is used. - - With normal life_analysis, R would be live before "Loop:". - The result is that R causes many interferences that do not - serve any purpose. - - After the function call a register lives at a program point - only if it is initialized on a path from CFG entry to the - program point. */ - -static void -make_accurate_live_analysis (void) -{ - basic_block bb; - struct bb_info *bb_info; - - max_regno = max_reg_num (); - compact_blocks (); - allocate_bb_info (); - calculate_local_reg_bb_info (); - set_up_bb_rts_numbers (); - calculate_reg_pav (); - modify_reg_pav (); - FOR_EACH_BB (bb) - { - bb_info = BB_INFO (bb); - - bitmap_and_into (bb->il.rtl->global_live_at_start, bb_info->live_pavin); - bitmap_and_into (bb->il.rtl->global_live_at_end, bb_info->live_pavout); - } - free_bb_info (); -} /* Run old register allocator. Return TRUE if we must exit rest_of_compilation upon return. */ static unsigned int @@ -2489,14 +2049,13 @@ rest_of_handle_global_alloc (void) /* If optimizing, allocate remaining pseudo-regs. Do the reload pass fixing up any insns that are invalid. */ - if (optimize) failure = global_alloc (); else { - compute_regsets (regs_asm_clobbered, regs_ever_live, - &eliminable_regset, &no_global_alloc_regs); + compute_regsets (&eliminable_regset, &no_global_alloc_regs); build_insn_chain (get_insns ()); + df_set_flags (DF_NO_INSN_RESCAN); failure = reload (get_insns (), 0); } @@ -2507,8 +2066,30 @@ rest_of_handle_global_alloc (void) timevar_pop (TV_DUMP); } + /* FIXME: This appears on the surface to be wrong thing to be doing. + So much of the compiler is designed to check reload_completed to + see if it is running after reload that seems doomed to failure. + We should be returning a value that says that we have found + errors so that nothing but the cleanup passes are run + afterwards. */ gcc_assert (reload_completed || failure); reload_completed = !failure; + + /* The world has changed so much that at this point we might as well + just rescan everything. Not that df_rescan_all_insns is not + going to help here because it does not touch the artificial uses + and defs. */ + df_finish_pass (); + if (optimize) + df_live_add_problem (); + df_scan_alloc (NULL); + df_scan_blocks (); + + if (optimize) + df_analyze (); + + regstat_free_n_sets_and_refs (); + regstat_free_ri (); return 0; } diff --git a/gcc/haifa-sched.c b/gcc/haifa-sched.c index cf02644..92ed9bb 100644 --- a/gcc/haifa-sched.c +++ b/gcc/haifa-sched.c @@ -144,6 +144,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "target.h" #include "output.h" #include "params.h" +#include "dbgcnt.h" #ifdef INSN_SCHEDULING @@ -215,9 +216,6 @@ static bool added_recovery_block_p; /* Counters of different types of speculative instructions. */ static int nr_begin_data, nr_be_in_data, nr_begin_control, nr_be_in_control; -/* Pointers to GLAT data. See init_glat for more information. */ -regset *glat_start, *glat_end; - /* Array used in {unlink, restore}_bb_notes. */ static rtx *bb_header = 0; @@ -573,10 +571,6 @@ static void extend_bb (void); static void fix_jump_move (rtx); static void move_block_after_check (rtx); static void move_succs (VEC(edge,gc) **, basic_block); -static void init_glat (void); -static void init_glat1 (basic_block); -static void attach_life_info1 (basic_block); -static void free_glat (void); static void sched_remove_insn (rtx); static void clear_priorities (rtx, rtx_vec_t *); static void calc_priorities (rtx_vec_t); @@ -1840,6 +1834,7 @@ move_insn (rtx insn) } set_block_for_insn (insn, bb); + df_insn_change_bb (insn); /* Update BB_END, if needed. */ if (BB_END (bb) == last) @@ -2276,6 +2271,23 @@ schedule_block (basic_block *target_bb, int rgn_n_insns1) || !(*current_sched_info->schedule_more_p) ()) break; + if (dbg_cnt (sched_insn) == false) + { + insn = NEXT_INSN (last_scheduled_insn); + while ((*current_sched_info->schedule_more_p) ()) + { + (*current_sched_info->begin_schedule_ready) (insn, + last_scheduled_insn); + if (QUEUE_INDEX (insn) >= 0) + queue_remove (insn); + last_scheduled_insn = insn; + insn = NEXT_INSN (insn); + } + while (ready.n_ready) + ready_remove_first (&ready); + goto bail_out; + } + /* Select and remove the insn from the ready list. */ if (sort_p) { @@ -2359,7 +2371,7 @@ schedule_block (basic_block *target_bb, int rgn_n_insns1) generate_recovery_code (insn); if (control_flow_insn_p (last_scheduled_insn) - /* This is used to to switch basic blocks by request + /* This is used to switch basic blocks by request from scheduler front-end (actually, sched-ebb.c only). This is used to process blocks with single fallthru edge. If succeeding block has jump, it [jump] will try @@ -2433,6 +2445,7 @@ schedule_block (basic_block *target_bb, int rgn_n_insns1) } } +bail_out: /* Debug info. */ if (sched_verbose) { @@ -2680,13 +2693,8 @@ sched_init (void) init_alias_analysis (); old_last_basic_block = 0; - glat_start = 0; - glat_end = 0; extend_bb (); - if (current_sched_info->flags & USE_GLAT) - init_glat (); - /* Compute INSN_REG_WEIGHT for all blocks. We must do this before removing death notes. */ FOR_EACH_BB_REVERSE (b) @@ -2714,7 +2722,6 @@ sched_finish (void) dfa_finish (); free_dependency_caches (); end_alias_analysis (); - free_glat (); if (targetm.sched.md_finish_global) targetm.sched.md_finish_global (sched_dump, sched_verbose); @@ -4064,13 +4071,6 @@ extend_bb (void) old_last_basic_block = last_basic_block; - if (current_sched_info->flags & USE_GLAT) - { - glat_start = xrealloc (glat_start, - last_basic_block * sizeof (*glat_start)); - glat_end = xrealloc (glat_end, last_basic_block * sizeof (*glat_end)); - } - /* The following is done to keep current_sched_info->next_tail non null. */ insn = BB_END (EXIT_BLOCK_PTR->prev_bb); @@ -4093,15 +4093,10 @@ extend_bb (void) void add_block (basic_block bb, basic_block ebb) { - gcc_assert (current_sched_info->flags & DETACH_LIFE_INFO - && bb->il.rtl->global_live_at_start == 0 - && bb->il.rtl->global_live_at_end == 0); + gcc_assert (current_sched_info->flags & NEW_BBS); extend_bb (); - glat_start[bb->index] = 0; - glat_end[bb->index] = 0; - if (current_sched_info->add_block) /* This changes only data structures of the front-end. */ current_sched_info->add_block (bb, ebb); @@ -4164,6 +4159,8 @@ move_block_after_check (rtx jump) move_succs (&(jump_bb->succs), bb); move_succs (&(jump_bb_next->succs), jump_bb); move_succs (&t, jump_bb_next); + + df_mark_solutions_dirty (); if (current_sched_info->fix_recovery_cfg) current_sched_info->fix_recovery_cfg @@ -4189,115 +4186,6 @@ move_succs (VEC(edge,gc) **succsp, basic_block to) *succsp = 0; } -/* Initialize GLAT (global_live_at_{start, end}) structures. - GLAT structures are used to substitute global_live_{start, end} - regsets during scheduling. This is necessary to use such functions as - split_block (), as they assume consistency of register live information. */ -static void -init_glat (void) -{ - basic_block bb; - - FOR_ALL_BB (bb) - init_glat1 (bb); -} - -/* Helper function for init_glat. */ -static void -init_glat1 (basic_block bb) -{ - gcc_assert (bb->il.rtl->global_live_at_start != 0 - && bb->il.rtl->global_live_at_end != 0); - - glat_start[bb->index] = bb->il.rtl->global_live_at_start; - glat_end[bb->index] = bb->il.rtl->global_live_at_end; - - if (current_sched_info->flags & DETACH_LIFE_INFO) - { - bb->il.rtl->global_live_at_start = 0; - bb->il.rtl->global_live_at_end = 0; - } -} - -/* Attach reg_live_info back to basic blocks. - Also save regsets, that should not have been changed during scheduling, - for checking purposes (see check_reg_live). */ -void -attach_life_info (void) -{ - basic_block bb; - - FOR_ALL_BB (bb) - attach_life_info1 (bb); -} - -/* Helper function for attach_life_info. */ -static void -attach_life_info1 (basic_block bb) -{ - gcc_assert (bb->il.rtl->global_live_at_start == 0 - && bb->il.rtl->global_live_at_end == 0); - - if (glat_start[bb->index]) - { - gcc_assert (glat_end[bb->index]); - - bb->il.rtl->global_live_at_start = glat_start[bb->index]; - bb->il.rtl->global_live_at_end = glat_end[bb->index]; - - /* Make them NULL, so they won't be freed in free_glat. */ - glat_start[bb->index] = 0; - glat_end[bb->index] = 0; - -#ifdef ENABLE_CHECKING - if (bb->index < NUM_FIXED_BLOCKS - || current_sched_info->region_head_or_leaf_p (bb, 0)) - { - glat_start[bb->index] = ALLOC_REG_SET (®_obstack); - COPY_REG_SET (glat_start[bb->index], - bb->il.rtl->global_live_at_start); - } - - if (bb->index < NUM_FIXED_BLOCKS - || current_sched_info->region_head_or_leaf_p (bb, 1)) - { - glat_end[bb->index] = ALLOC_REG_SET (®_obstack); - COPY_REG_SET (glat_end[bb->index], bb->il.rtl->global_live_at_end); - } -#endif - } - else - { - gcc_assert (!glat_end[bb->index]); - - bb->il.rtl->global_live_at_start = ALLOC_REG_SET (®_obstack); - bb->il.rtl->global_live_at_end = ALLOC_REG_SET (®_obstack); - } -} - -/* Free GLAT information. */ -static void -free_glat (void) -{ -#ifdef ENABLE_CHECKING - if (current_sched_info->flags & DETACH_LIFE_INFO) - { - basic_block bb; - - FOR_ALL_BB (bb) - { - if (glat_start[bb->index]) - FREE_REG_SET (glat_start[bb->index]); - if (glat_end[bb->index]) - FREE_REG_SET (glat_end[bb->index]); - } - } -#endif - - free (glat_start); - free (glat_end); -} - /* Remove INSN from the instruction stream. INSN should have any dependencies. */ static void @@ -4536,54 +4424,8 @@ check_sched_flags (void) gcc_assert (!(f & DO_SPECULATION)); if (f & DO_SPECULATION) gcc_assert (!flag_sched_stalled_insns - && (f & DETACH_LIFE_INFO) && spec_info && spec_info->mask); - if (f & DETACH_LIFE_INFO) - gcc_assert (f & USE_GLAT); -} - -/* Check global_live_at_{start, end} regsets. - If FATAL_P is TRUE, then abort execution at the first failure. - Otherwise, print diagnostics to STDERR (this mode is for calling - from debugger). */ -void -check_reg_live (bool fatal_p) -{ - basic_block bb; - - FOR_ALL_BB (bb) - { - int i; - - i = bb->index; - - if (glat_start[i]) - { - bool b = bitmap_equal_p (bb->il.rtl->global_live_at_start, - glat_start[i]); - - if (!b) - { - gcc_assert (!fatal_p); - - fprintf (stderr, ";; check_reg_live_at_start (%d) failed.\n", i); - } - } - - if (glat_end[i]) - { - bool b = bitmap_equal_p (bb->il.rtl->global_live_at_end, - glat_end[i]); - - if (!b) - { - gcc_assert (!fatal_p); - - fprintf (stderr, ";; check_reg_live_at_end (%d) failed.\n", i); - } - } - } } #endif /* ENABLE_CHECKING */ diff --git a/gcc/ifcvt.c b/gcc/ifcvt.c index 28f510c..e5d8897 100644 --- a/gcc/ifcvt.c +++ b/gcc/ifcvt.c @@ -43,10 +43,10 @@ #include "target.h" #include "timevar.h" #include "tree-pass.h" +#include "df.h" #include "vec.h" #include "vecprim.h" - #ifndef HAVE_conditional_execution #define HAVE_conditional_execution 0 #endif @@ -70,6 +70,8 @@ #define MAX_CONDITIONAL_EXECUTE (BRANCH_COST + 1) #endif +#define IFCVT_MULTIPLE_DUMPS 1 + #define NULL_BLOCK ((basic_block) NULL) /* # of IF-THEN or IF-THEN-ELSE blocks we looked at */ @@ -79,15 +81,12 @@ static int num_possible_if_blocks; execution. */ static int num_updated_if_blocks; -/* # of changes made which require life information to be updated. */ +/* # of changes made. */ static int num_true_changes; /* Whether conditional execution changes were made. */ static int cond_exec_changed_p; -/* True if life data ok at present. */ -static bool life_data_ok; - /* Forward references. */ static int count_bb_insns (basic_block); static bool cheap_bb_rtx_cost_p (basic_block, int); @@ -2773,6 +2772,7 @@ merge_if_block (struct ce_if_block * ce_info) /* All block merging is done into the lower block numbers. */ combo_bb = test_bb; + df_set_bb_dirty (test_bb); /* Merge any basic blocks to handle && and || subtests. Each of the blocks are on the fallthru path from the predecessor block. */ @@ -2847,7 +2847,8 @@ merge_if_block (struct ce_if_block * ce_info) else if (EDGE_COUNT (join_bb->preds) < 2 && join_bb != EXIT_BLOCK_PTR) { - /* We can merge the JOIN. */ + /* We can merge the JOIN cleanly and update the dataflow try + again on this pass.*/ merge_blocks (combo_bb, join_bb); num_true_changes++; } @@ -2887,6 +2888,11 @@ find_if_header (basic_block test_bb, int pass) then_edge = EDGE_SUCC (test_bb, 0); else_edge = EDGE_SUCC (test_bb, 1); + if (df_get_bb_dirty (then_edge->dest)) + return NULL; + if (df_get_bb_dirty (else_edge->dest)) + return NULL; + /* Neither edge should be abnormal. */ if ((then_edge->flags & EDGE_COMPLEX) || (else_edge->flags & EDGE_COMPLEX)) @@ -2946,6 +2952,8 @@ find_if_header (basic_block test_bb, int pass) success: if (dump_file) fprintf (dump_file, "Conversion succeeded on pass %d.\n", pass); + /* Set this so we continue looking. */ + cond_exec_changed_p = TRUE; return ce_info.test_bb; } @@ -3118,7 +3126,7 @@ cond_exec_find_if_block (struct ce_if_block * ce_info) if (EDGE_COUNT (then_bb->succs) > 0 && (!single_succ_p (then_bb) || (single_succ_edge (then_bb)->flags & EDGE_COMPLEX) - || (flow2_completed && tablejump_p (BB_END (then_bb), NULL, NULL)))) + || (epilogue_completed && tablejump_p (BB_END (then_bb), NULL, NULL)))) return FALSE; /* If the THEN block has no successors, conditional execution can still @@ -3165,7 +3173,7 @@ cond_exec_find_if_block (struct ce_if_block * ce_info) && single_succ (then_bb) == single_succ (else_bb) && single_pred_p (else_bb) && ! (single_succ_edge (else_bb)->flags & EDGE_COMPLEX) - && ! (flow2_completed && tablejump_p (BB_END (else_bb), NULL, NULL))) + && ! (epilogue_completed && tablejump_p (BB_END (else_bb), NULL, NULL))) join_bb = single_succ (else_bb); /* Otherwise it is not an IF-THEN or IF-THEN-ELSE combination. */ @@ -3314,6 +3322,10 @@ find_cond_trap (basic_block test_bb, edge then_edge, edge else_edge) /* Delete the trap block if possible. */ remove_edge (trap_bb == then_bb ? then_edge : else_edge); + df_set_bb_dirty (test_bb); + df_set_bb_dirty (then_bb); + df_set_bb_dirty (else_bb); + if (EDGE_COUNT (trap_bb->preds) == 0) { delete_basic_block (trap_bb); @@ -3452,7 +3464,8 @@ static int find_if_case_1 (basic_block test_bb, edge then_edge, edge else_edge) { basic_block then_bb = then_edge->dest; - basic_block else_bb = else_edge->dest, new_bb; + basic_block else_bb = else_edge->dest; + basic_block new_bb; int then_bb_index; /* If we are partitioning hot/cold basic blocks, we don't want to @@ -3508,11 +3521,6 @@ find_if_case_1 (basic_block test_bb, edge then_edge, edge else_edge) /* Conversion went ok, including moving the insns and fixing up the jump. Adjust the CFG to match. */ - bitmap_ior (test_bb->il.rtl->global_live_at_end, - else_bb->il.rtl->global_live_at_start, - then_bb->il.rtl->global_live_at_end); - - /* We can avoid creating a new basic block if then_bb is immediately followed by else_bb, i.e. deleting then_bb allows test_bb to fall thru to else_bb. */ @@ -3526,7 +3534,10 @@ find_if_case_1 (basic_block test_bb, edge then_edge, edge else_edge) } else new_bb = redirect_edge_and_branch_force (FALLTHRU_EDGE (test_bb), - else_bb); + else_bb); + + df_set_bb_dirty (test_bb); + df_set_bb_dirty (else_bb); then_bb_index = then_bb->index; delete_basic_block (then_bb); @@ -3535,15 +3546,12 @@ find_if_case_1 (basic_block test_bb, edge then_edge, edge else_edge) block we removed. */ if (new_bb) { - new_bb->index = then_bb_index; - SET_BASIC_BLOCK (then_bb_index, new_bb); + df_bb_replace (then_bb_index, new_bb); /* Since the fallthru edge was redirected from test_bb to new_bb, we need to ensure that new_bb is in the same partition as test bb (you can not fall through across section boundaries). */ BB_COPY_PARTITION (new_bb, test_bb); } - /* We've possibly created jump to next insn, cleanup_cfg will solve that - later. */ num_true_changes++; num_updated_if_blocks++; @@ -3626,10 +3634,8 @@ find_if_case_2 (basic_block test_bb, edge then_edge, edge else_edge) /* Conversion went ok, including moving the insns and fixing up the jump. Adjust the CFG to match. */ - bitmap_ior (test_bb->il.rtl->global_live_at_end, - then_bb->il.rtl->global_live_at_start, - else_bb->il.rtl->global_live_at_end); - + df_set_bb_dirty (test_bb); + df_set_bb_dirty (then_bb); delete_basic_block (else_bb); num_true_changes++; @@ -3745,8 +3751,7 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb, that any registers modified are dead at the branch site. */ rtx insn, cond, prev; - regset merge_set, tmp, test_live, test_set; - struct propagate_block_info *pbi; + bitmap merge_set, test_live, test_set; unsigned i, fail = 0; bitmap_iterator bi; @@ -3786,10 +3791,9 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb, TEST_SET = set of registers set between EARLIEST and the end of the block. */ - tmp = ALLOC_REG_SET (®_obstack); - merge_set = ALLOC_REG_SET (®_obstack); - test_live = ALLOC_REG_SET (®_obstack); - test_set = ALLOC_REG_SET (®_obstack); + merge_set = BITMAP_ALLOC (®_obstack); + test_live = BITMAP_ALLOC (®_obstack); + test_set = BITMAP_ALLOC (®_obstack); /* ??? bb->local_set is only valid during calculate_global_regs_live, so we must recompute usage for MERGE_BB. Not so bad, I suppose, @@ -3798,11 +3802,21 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb, expander called from noce_emit_cmove), we must resize the array first. */ if (max_regno < max_reg_num ()) + max_regno = max_reg_num (); + + FOR_BB_INSNS (merge_bb, insn) { - max_regno = max_reg_num (); - allocate_reg_info (max_regno, FALSE, FALSE); + if (INSN_P (insn)) + { + unsigned int uid = INSN_UID (insn); + struct df_ref **def_rec; + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + bitmap_set_bit (merge_set, DF_REF_REGNO (def)); + } + } } - propagate_block (merge_bb, tmp, merge_set, merge_set, 0); /* For small register class machines, don't lengthen lifetimes of hard registers before reload. */ @@ -3816,39 +3830,40 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb, fail = 1; } } - + /* For TEST, we're interested in a range of insns, not a whole block. Moreover, we're interested in the insns live from OTHER_BB. */ - - COPY_REG_SET (test_live, other_bb->il.rtl->global_live_at_start); - pbi = init_propagate_block_info (test_bb, test_live, test_set, test_set, - 0); - + + /* The loop below takes the set of live registers + after JUMP, and calculates the live set before EARLIEST. */ + bitmap_copy (test_live, DF_LIVE_IN (other_bb)); + df_simulate_artificial_refs_at_end (test_bb, test_live); for (insn = jump; ; insn = prev) { - prev = propagate_one_insn (pbi, insn); + if (INSN_P (insn)) + { + df_simulate_find_defs (insn, test_set); + df_simulate_one_insn_backwards (test_bb, insn, test_live); + } + prev = PREV_INSN (insn); if (insn == earliest) break; } - free_propagate_block_info (pbi); - /* We can perform the transformation if MERGE_SET & (TEST_SET | TEST_LIVE) and - TEST_SET & merge_bb->il.rtl->global_live_at_start + TEST_SET & DF_LIVE_IN (merge_bb) are empty. */ if (bitmap_intersect_p (test_set, merge_set) || bitmap_intersect_p (test_live, merge_set) - || bitmap_intersect_p (test_set, - merge_bb->il.rtl->global_live_at_start)) + || bitmap_intersect_p (test_set, DF_LIVE_IN (merge_bb))) fail = 1; - FREE_REG_SET (tmp); - FREE_REG_SET (merge_set); - FREE_REG_SET (test_live); - FREE_REG_SET (test_set); + BITMAP_FREE (merge_set); + BITMAP_FREE (test_live); + BITMAP_FREE (test_set); if (fail) return FALSE; @@ -3938,7 +3953,7 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb, /* Main entry point for all if-conversion. */ static void -if_convert (int x_life_data_ok) +if_convert (bool recompute_dominance) { basic_block bb; int pass; @@ -3946,7 +3961,6 @@ if_convert (int x_life_data_ok) num_possible_if_blocks = 0; num_updated_if_blocks = 0; num_true_changes = 0; - life_data_ok = (x_life_data_ok != 0); /* Some transformations in this pass can create new pseudos, if the pass runs before reload. Make sure we can do so. */ @@ -3958,11 +3972,10 @@ if_convert (int x_life_data_ok) free_dominance_info (CDI_DOMINATORS); /* Compute postdominators if we think we'll use them. */ - if (HAVE_conditional_execution || life_data_ok) + if (HAVE_conditional_execution || recompute_dominance) calculate_dominance_info (CDI_POST_DOMINATORS); - if (life_data_ok) - clear_bb_flags (); + df_set_flags (DF_LR_RUN_DCE); /* Go through each of the basic blocks looking for things to convert. If we have conditional execution, we make multiple passes to allow us to handle @@ -3970,6 +3983,9 @@ if_convert (int x_life_data_ok) pass = 0; do { + df_analyze (); + /* Only need to do dce on the first pass. */ + df_clear_flags (DF_LR_RUN_DCE); cond_exec_changed_p = FALSE; pass++; @@ -3980,9 +3996,10 @@ if_convert (int x_life_data_ok) FOR_EACH_BB (bb) { - basic_block new_bb; - while ((new_bb = find_if_header (bb, pass))) - bb = new_bb; + basic_block new_bb; + while (!df_get_bb_dirty (bb) + && (new_bb = find_if_header (bb, pass)) != NULL) + bb = new_bb; } #ifdef IFCVT_MULTIPLE_DUMPS @@ -4004,19 +4021,9 @@ if_convert (int x_life_data_ok) clear_aux_for_blocks (); - /* Rebuild life info for basic blocks that require it. */ - if (num_true_changes && life_data_ok) - { - /* If we allocated new pseudos, we must resize the array for sched1. */ - if (max_regno < max_reg_num ()) - { - max_regno = max_reg_num (); - allocate_reg_info (max_regno, FALSE, FALSE); - } - update_life_info_in_dirty_blocks (UPDATE_LIFE_GLOBAL_RM_NOTES, - PROP_DEATH_NOTES | PROP_SCAN_DEAD_CODE - | PROP_KILL_DEAD_CODE); - } + /* If we allocated new pseudos, we must resize the array for sched1. */ + if (max_regno < max_reg_num ()) + max_regno = max_reg_num (); /* Write the final stats. */ if (dump_file && num_possible_if_blocks > 0) @@ -4052,14 +4059,10 @@ rest_of_handle_if_conversion (void) if (dump_file) dump_flow_info (dump_file, dump_flags); cleanup_cfg (CLEANUP_EXPENSIVE); - reg_scan (get_insns (), max_reg_num ()); - if_convert (0); + if_convert (false); } - timevar_push (TV_JUMP); - cleanup_cfg (CLEANUP_EXPENSIVE); - reg_scan (get_insns (), max_reg_num ()); - timevar_pop (TV_JUMP); + cleanup_cfg (0); return 0; } @@ -4076,6 +4079,7 @@ struct tree_opt_pass pass_rtl_ifcvt = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func, /* todo_flags_finish */ 'C' /* letter */ }; @@ -4093,7 +4097,7 @@ static unsigned int rest_of_handle_if_after_combine (void) { no_new_pseudos = 0; - if_convert (1); + if_convert (true); no_new_pseudos = 1; return 0; } @@ -4111,6 +4115,7 @@ struct tree_opt_pass pass_if_after_combine = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func | TODO_ggc_collect, /* todo_flags_finish */ 'C' /* letter */ @@ -4126,13 +4131,8 @@ gate_handle_if_after_reload (void) static unsigned int rest_of_handle_if_after_reload (void) { - /* Last attempt to optimize CFG, as scheduling, peepholing and insn - splitting possibly introduced more crossjumping opportunities. */ - cleanup_cfg (CLEANUP_EXPENSIVE - | CLEANUP_UPDATE_LIFE - | (flag_crossjumping ? CLEANUP_CROSSJUMP : 0)); if (flag_if_conversion2) - if_convert (1); + if_convert (true); return 0; } @@ -4150,6 +4150,7 @@ struct tree_opt_pass pass_if_after_reload = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func | TODO_ggc_collect, /* todo_flags_finish */ 'E' /* letter */ diff --git a/gcc/init-regs.c b/gcc/init-regs.c new file mode 100644 index 0000000..adde3fa --- /dev/null +++ b/gcc/init-regs.c @@ -0,0 +1,148 @@ +/* Initialization of uninitialized regs. + Copyright (C) 2007 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, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "rtl.h" +#include "regs.h" +#include "expr.h" +#include "tree-pass.h" +#include "basic-block.h" +#include "flags.h" +#include "df.h" + +/* Check all of the uses of pseudo variables. If any use that is MUST + uninitialized, add a store of 0 immediately before it. For + subregs, this makes combine happy. For full word regs, this makes + other optimizations, like the register allocator and the reg-stack + happy as well as papers over some problems on the arm and other + processors where certain isa constraints cannot be handled by gcc. + These are of the form where two operands to an insn my not be the + same. The ra will only make them the same if they do not + interfere, and this can only happen if one is not initialized. + + There is also the unfortunate consequence that this may mask some + buggy programs where people forget to initialize stack variable. + Any programmer with half a brain would look at the uninitialized + variable warnings. */ + +static void +initialize_uninitialized_regs (void) +{ + basic_block bb; + bitmap already_genned = BITMAP_ALLOC (NULL); + + df_analyze (); + + FOR_EACH_BB (bb) + { + rtx insn; + bitmap lr = DF_LR_IN (bb); + bitmap ur = DF_LIVE_IN (bb); + bitmap_clear (already_genned); + + FOR_BB_INSNS (bb, insn) + { + unsigned int uid = INSN_UID (insn); + struct df_ref **use_rec; + if (!INSN_P (insn)) + continue; + + for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + unsigned int regno = DF_REF_REGNO (use); + + /* Only do this for the pseudos. */ + if (regno < FIRST_PSEUDO_REGISTER) + continue; + + /* Do not generate multiple moves for the same regno. + This is common for sequences of subreg operations. + They would be deleted during combine but there is no + reason to churn the system. */ + if (bitmap_bit_p (already_genned, regno)) + continue; + + /* A use is MUST uninitialized if it reaches the top of + the block from the inside of the block (the lr test) + and no def for it reaches the top of the block from + outside of the block (the ur test). */ + if (bitmap_bit_p (lr, regno) + && (!bitmap_bit_p (ur, regno))) + { + rtx move_insn; + rtx reg = DF_REF_REAL_REG (use); + + bitmap_set_bit (already_genned, regno); + + start_sequence (); + emit_move_insn (reg, CONST0_RTX (GET_MODE (reg))); + move_insn = get_insns (); + end_sequence (); + add_insn_before (move_insn, insn, bb); + if (dump_file) + fprintf (dump_file, + "adding initialization in %s of reg %d at in block %d for insn %d.\n", + current_function_name (), regno, bb->index, uid); + } + } + } + } + + BITMAP_FREE (already_genned); +} + +static bool +gate_initialize_regs (void) +{ + return optimize > 0; +} + +static unsigned int +rest_of_handle_initialize_regs (void) +{ + no_new_pseudos = 0; + initialize_uninitialized_regs (); + no_new_pseudos = 1; + return 0; +} + +struct tree_opt_pass pass_initialize_regs = +{ + "init-regs", /* name */ + gate_initialize_regs, /* gate */ + rest_of_handle_initialize_regs, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func | + TODO_df_finish, /* todo_flags_finish */ + 0 /* letter */ +}; diff --git a/gcc/integrate.c b/gcc/integrate.c index a6eeede..827afb7 100644 --- a/gcc/integrate.c +++ b/gcc/integrate.c @@ -1,6 +1,6 @@ /* Procedure integration for GCC. Copyright (C) 1988, 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, - 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Michael Tiemann (tiemann@cygnus.com) This file is part of GCC. @@ -46,6 +46,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "target.h" #include "langhooks.h" #include "tree-pass.h" +#include "df.h" /* Round to the next highest integer that meets the alignment. */ #define CEIL_ROUND(VALUE,ALIGN) (((VALUE) + (ALIGN) - 1) & ~((ALIGN)- 1)) @@ -318,7 +319,7 @@ struct tree_opt_pass pass_initial_value_sets = /* If the backend knows where to allocate pseudos for hard register initial values, register these allocations now. */ void -allocate_initial_values (rtx *reg_equiv_memory_loc ATTRIBUTE_UNUSED) +allocate_initial_values (rtx *reg_equiv_memory_loc) { if (targetm.allocate_initial_value) { @@ -347,18 +348,14 @@ allocate_initial_values (rtx *reg_equiv_memory_loc ATTRIBUTE_UNUSED) reg_renumber[regno] = new_regno; /* Poke the regno right into regno_reg_rtx so that even fixed regs are accepted. */ - REGNO (ivs->entries[i].pseudo) = new_regno; + SET_REGNO (ivs->entries[i].pseudo, new_regno); /* Update global register liveness information. */ FOR_EACH_BB (bb) { - struct rtl_bb_info *info = bb->il.rtl; - - if (REGNO_REG_SET_P(info->global_live_at_start, regno)) - SET_REGNO_REG_SET (info->global_live_at_start, - new_regno); - if (REGNO_REG_SET_P(info->global_live_at_end, regno)) - SET_REGNO_REG_SET (info->global_live_at_end, - new_regno); + if (REGNO_REG_SET_P(DF_LIVE_IN (bb), regno)) + SET_REGNO_REG_SET (DF_LIVE_IN (bb), new_regno); + if (REGNO_REG_SET_P(DF_LIVE_OUT (bb), regno)) + SET_REGNO_REG_SET (DF_LIVE_OUT (bb), new_regno); } } } diff --git a/gcc/local-alloc.c b/gcc/local-alloc.c index 01d107e..93bdbfa 100644 --- a/gcc/local-alloc.c +++ b/gcc/local-alloc.c @@ -1,7 +1,7 @@ /* Allocate registers within a basic block, for GNU compiler. Copyright (C) 1987, 1988, 1991, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, - Inc. + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. This file is part of GCC. @@ -81,6 +81,9 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "ggc.h" #include "timevar.h" #include "tree-pass.h" +#include "df.h" +#include "dbgcnt.h" + /* Next quantity number available for allocation. */ @@ -388,7 +391,7 @@ local_alloc (void) for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) { - if (REG_BASIC_BLOCK (i) >= 0 && REG_N_DEATHS (i) == 1) + if (REG_BASIC_BLOCK (i) >= NUM_FIXED_BLOCKS && REG_N_DEATHS (i) == 1) reg_qty[i] = -2; else reg_qty[i] = -1; @@ -604,7 +607,7 @@ equiv_init_movable_p (rtx x, int regno) case REG: return (reg_equiv[REGNO (x)].loop_depth >= reg_equiv[regno].loop_depth && reg_equiv[REGNO (x)].replace) - || (REG_BASIC_BLOCK (REGNO (x)) < 0 && ! rtx_varies_p (x, 0)); + || (REG_BASIC_BLOCK (REGNO (x)) < NUM_FIXED_BLOCKS && ! rtx_varies_p (x, 0)); case UNSPEC_VOLATILE: return 0; @@ -796,11 +799,9 @@ update_equiv_regs (void) rtx insn; basic_block bb; int loop_depth; - regset_head cleared_regs; - int clear_regnos = 0; - + bitmap cleared_regs; + reg_equiv = XCNEWVEC (struct equivalence, max_regno); - INIT_REG_SET (&cleared_regs); reg_equiv_init = ggc_alloc_cleared (max_regno * sizeof (rtx)); reg_equiv_init_size = max_regno; @@ -914,7 +915,7 @@ update_equiv_regs (void) if (note && GET_CODE (XEXP (note, 0)) == EXPR_LIST) note = NULL_RTX; - if (REG_N_SETS (regno) != 1 + if (DF_REG_DEF_COUNT (regno) != 1 && (! note || rtx_varies_p (XEXP (note, 0), 0) || (reg_equiv[regno].replacement @@ -930,7 +931,7 @@ update_equiv_regs (void) /* If this register is known to be equal to a constant, record that it is always equivalent to the constant. */ - if (REG_N_SETS (regno) == 1 + if (DF_REG_DEF_COUNT (regno) == 1 && note && ! rtx_varies_p (XEXP (note, 0), 0)) { rtx note_value = XEXP (note, 0); @@ -955,7 +956,7 @@ update_equiv_regs (void) note = find_reg_note (insn, REG_EQUIV, NULL_RTX); - if (note == 0 && REG_BASIC_BLOCK (regno) >= 0 + if (note == 0 && REG_BASIC_BLOCK (regno) >= NUM_FIXED_BLOCKS && MEM_P (SET_SRC (set)) && validate_equiv_mem (insn, dest, SET_SRC (set))) note = set_unique_reg_note (insn, REG_EQUIV, copy_rtx (SET_SRC (set))); @@ -1052,8 +1053,8 @@ update_equiv_regs (void) if (MEM_P (dest) && REG_P (src) && (regno = REGNO (src)) >= FIRST_PSEUDO_REGISTER - && REG_BASIC_BLOCK (regno) >= 0 - && REG_N_SETS (regno) == 1 + && REG_BASIC_BLOCK (regno) >= NUM_FIXED_BLOCKS + && DF_REG_DEF_COUNT (regno) == 1 && reg_equiv[regno].init_insns != 0 && reg_equiv[regno].init_insns != const0_rtx && ! find_reg_note (XEXP (reg_equiv[regno].init_insns, 0), @@ -1071,10 +1072,12 @@ update_equiv_regs (void) the register. */ reg_equiv_init[regno] = gen_rtx_INSN_LIST (VOIDmode, insn, NULL_RTX); + df_notes_rescan (init_insn); } } } + cleared_regs = BITMAP_ALLOC (NULL); /* Now scan all regs killed in an insn to see if any of them are registers only used that once. If so, see if we can replace the reference with the equivalent form. If we can, delete the @@ -1157,18 +1160,15 @@ update_equiv_regs (void) } remove_death (regno, insn); - REG_N_REFS (regno) = 0; + SET_REG_N_REFS (regno, 0); REG_FREQ (regno) = 0; delete_insn (equiv_insn); reg_equiv[regno].init_insns = XEXP (reg_equiv[regno].init_insns, 1); - /* Remember to clear REGNO from all basic block's live - info. */ - SET_REGNO_REG_SET (&cleared_regs, regno); - clear_regnos++; reg_equiv_init[regno] = NULL_RTX; + bitmap_set_bit (cleared_regs, regno); } /* Move the initialization of the register to just before INSN. Update the flow information. */ @@ -1197,51 +1197,34 @@ update_equiv_regs (void) if (insn == BB_HEAD (bb)) BB_HEAD (bb) = PREV_INSN (insn); - /* Remember to clear REGNO from all basic block's live - info. */ - SET_REGNO_REG_SET (&cleared_regs, regno); - clear_regnos++; reg_equiv_init[regno] = gen_rtx_INSN_LIST (VOIDmode, new_insn, NULL_RTX); + bitmap_set_bit (cleared_regs, regno); } } } } } - /* Clear all dead REGNOs from all basic block's live info. */ - if (clear_regnos) - { - unsigned j; - - if (clear_regnos > 8) - { - FOR_EACH_BB (bb) - { - AND_COMPL_REG_SET (bb->il.rtl->global_live_at_start, - &cleared_regs); - AND_COMPL_REG_SET (bb->il.rtl->global_live_at_end, - &cleared_regs); - } - } - else - { - reg_set_iterator rsi; - EXECUTE_IF_SET_IN_REG_SET (&cleared_regs, 0, j, rsi) - { - FOR_EACH_BB (bb) - { - CLEAR_REGNO_REG_SET (bb->il.rtl->global_live_at_start, j); - CLEAR_REGNO_REG_SET (bb->il.rtl->global_live_at_end, j); - } - } - } - } + if (!bitmap_empty_p (cleared_regs)) + FOR_EACH_BB (bb) + { + bitmap_and_compl_into (DF_RA_LIVE_IN (bb), cleared_regs); + if (DF_RA_LIVE_TOP (bb)) + bitmap_and_compl_into (DF_RA_LIVE_TOP (bb), cleared_regs); + bitmap_and_compl_into (DF_RA_LIVE_OUT (bb), cleared_regs); + bitmap_and_compl_into (DF_LR_IN (bb), cleared_regs); + if (DF_LR_TOP (bb)) + bitmap_and_compl_into (DF_LR_TOP (bb), cleared_regs); + bitmap_and_compl_into (DF_LR_OUT (bb), cleared_regs); + } + + BITMAP_FREE (cleared_regs); out: /* Clean up. */ + end_alias_analysis (); - CLEAR_REG_SET (&cleared_regs); free (reg_equiv); } @@ -1314,8 +1297,7 @@ block_alloc (int b) /* Initialize table of hardware registers currently live. */ - REG_SET_TO_HARD_REG_SET (regs_live, - BASIC_BLOCK (b)->il.rtl->global_live_at_start); + REG_SET_TO_HARD_REG_SET (regs_live, DF_LR_TOP (BASIC_BLOCK (b))); /* This loop scans the instructions of the basic block and assigns quantities to registers. @@ -1689,7 +1671,7 @@ block_alloc (int b) This optimization is only appropriate when we will run a scheduling pass after reload and we are not optimizing for code size. */ - if (flag_schedule_insns_after_reload + if (flag_schedule_insns_after_reload && dbg_cnt (local_alloc_for_sched) && !optimize_size && !SMALL_REGISTER_CLASSES) { @@ -1709,7 +1691,7 @@ block_alloc (int b) #ifdef INSN_SCHEDULING /* Similarly, avoid false dependencies. */ - if (flag_schedule_insns_after_reload + if (flag_schedule_insns_after_reload && dbg_cnt (local_alloc_for_sched) && !optimize_size && !SMALL_REGISTER_CLASSES && qty[q].alternate_class != NO_REGS) @@ -2524,15 +2506,38 @@ static unsigned int rest_of_handle_local_alloc (void) { int rebuild_notes; + int max_regno = max_reg_num (); + + df_note_add_problem (); + if (optimize) + { + /* Create a new version of df that has the special version of UR + if we are doing optimization. */ + df_remove_problem (df_live); + df_urec_add_problem (); + } + df_analyze (); + regstat_init_n_sets_and_refs (); + regstat_compute_ri (); + + /* There is just too much going on in the register allocators to + keep things up to date. At the end we have to rescan anyway + because things change when the reload_completed flag is set. + So we just turn off scanning and we will rescan by hand. */ + df_set_flags (DF_NO_INSN_RESCAN); + + + /* If we are not optimizing, then this is the only place before + register allocation where dataflow is done. And that is needed + to generate these warnings. */ + if (warn_clobbered) + generate_setjmp_warnings (); /* Determine if the current function is a leaf before running reload since this can impact optimizations done by the prologue and epilogue thus changing register elimination offsets. */ current_function_is_leaf = leaf_function_p (); - /* Allocate the reg_renumber array. */ - allocate_reg_info (max_regno, FALSE, TRUE); - /* And the reg_equiv_memory_loc array. */ VEC_safe_grow (rtx, gc, reg_equiv_memory_loc_vec, max_regno); memset (VEC_address (rtx, reg_equiv_memory_loc_vec), 0, @@ -2541,7 +2546,7 @@ rest_of_handle_local_alloc (void) allocate_initial_values (reg_equiv_memory_loc); - regclass (get_insns (), max_reg_num ()); + regclass (get_insns (), max_regno); rebuild_notes = local_alloc (); /* Local allocation may have turned an indirect jump into a direct @@ -2553,8 +2558,6 @@ rest_of_handle_local_alloc (void) rebuild_jump_labels (get_insns ()); purge_all_dead_edges (); - delete_unreachable_blocks (); - timevar_pop (TV_JUMP); } diff --git a/gcc/loop-init.c b/gcc/loop-init.c index d78b5ac..303c2da 100644 --- a/gcc/loop-init.c +++ b/gcc/loop-init.c @@ -1,5 +1,5 @@ /* Loop optimizer initialization routines and RTL loop optimization passes. - Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -31,6 +31,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "tree-pass.h" #include "timevar.h" #include "flags.h" +#include "df.h" #include "ggc.h" @@ -198,9 +199,7 @@ rtl_loop_done (void) loop_optimizer_finalize (); free_dominance_info (CDI_DOMINATORS); - cleanup_cfg (CLEANUP_EXPENSIVE); - delete_trivially_dead_insns (get_insns (), max_reg_num ()); - reg_scan (get_insns (), max_reg_num ()); + cleanup_cfg (0); if (dump_file) dump_flow_info (dump_file, dump_flags); @@ -242,7 +241,7 @@ rtl_move_loop_invariants (void) struct tree_opt_pass pass_rtl_move_loop_invariants = { - "loop2_invariant", /* name */ + "loop2_invariant", /* name */ gate_rtl_move_loop_invariants, /* gate */ rtl_move_loop_invariants, /* execute */ NULL, /* sub */ @@ -252,7 +251,8 @@ struct tree_opt_pass pass_rtl_move_loop_invariants = 0, /* properties_required */ 0, /* properties_provided */ 0, /* properties_destroyed */ - 0, /* todo_flags_start */ + 0, /* todo_flags_start */ + TODO_df_finish | /* This is shutting down the instance in loop_invariant.c */ TODO_dump_func, /* todo_flags_finish */ 'L' /* letter */ }; @@ -304,6 +304,8 @@ rtl_unroll_and_peel_loops (void) if (number_of_loops () > 1) { int flags = 0; + if (dump_file) + df_dump (dump_file); if (flag_peel_loops) flags |= UAP_PEEL; diff --git a/gcc/loop-invariant.c b/gcc/loop-invariant.c index 613bfb1..3d4ce61 100644 --- a/gcc/loop-invariant.c +++ b/gcc/loop-invariant.c @@ -1,5 +1,5 @@ /* RTL-level loop invariant motion. - Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -121,6 +121,11 @@ struct invariant unsigned stamp; }; +/* Table of invariants indexed by the df_ref uid field. */ + +static unsigned int invariant_table_size = 0; +static struct invariant ** invariant_table; + /* Entry for hash table of invariant expressions. */ struct invariant_expr_entry @@ -152,9 +157,21 @@ DEF_VEC_ALLOC_P(invariant_p, heap); static VEC(invariant_p,heap) *invariants; -/* The dataflow object. */ +/* Check the size of the invariant table and realloc if necessary. */ -static struct df *df = NULL; +static void +check_invariant_table_size (void) +{ + if (invariant_table_size < DF_DEFS_TABLE_SIZE()) + { + unsigned int new_size = DF_DEFS_TABLE_SIZE () + (DF_DEFS_TABLE_SIZE () / 4); + invariant_table = xrealloc (invariant_table, + sizeof (struct rtx_iv *) * new_size); + memset (&invariant_table[invariant_table_size], 0, + (new_size - invariant_table_size) * sizeof (struct rtx_iv *)); + invariant_table_size = new_size; + } +} /* Test for possibility of invariantness of X. */ @@ -240,13 +257,14 @@ invariant_for_use (struct df_ref *use) if (!defs || defs->next) return NULL; def = defs->ref; - if (!DF_REF_DATA (def)) + check_invariant_table_size (); + if (!invariant_table[DF_REF_ID(def)]) return NULL; def_bb = DF_REF_BB (def); if (!dominated_by_p (CDI_DOMINATORS, bb, def_bb)) return NULL; - return DF_REF_DATA (def); + return invariant_table[DF_REF_ID(def)]; } /* Computes hash value for invariant expression X in INSN. */ @@ -272,7 +290,7 @@ hash_invariant_expr_1 (rtx insn, rtx x) return hash_rtx (x, GET_MODE (x), &do_not_record_p, NULL, false); case REG: - use = df_find_use (df, insn, x); + use = df_find_use (insn, x); if (!use) return hash_rtx (x, GET_MODE (x), &do_not_record_p, NULL, false); inv = invariant_for_use (use); @@ -332,8 +350,8 @@ invariant_expr_equal_p (rtx insn1, rtx e1, rtx insn2, rtx e2) return rtx_equal_p (e1, e2); case REG: - use1 = df_find_use (df, insn1, e1); - use2 = df_find_use (df, insn2, e2); + use1 = df_find_use (insn1, e1); + use2 = df_find_use (insn2, e2); if (use1) inv1 = invariant_for_use (use1); if (use2) @@ -616,8 +634,20 @@ find_defs (struct loop *loop, basic_block *body) for (i = 0; i < loop->num_nodes; i++) bitmap_set_bit (blocks, body[i]->index); - df_set_blocks (df, blocks); - df_analyze (df); + df_remove_problem (df_chain); + df_process_deferred_rescans (); + df_chain_add_problem (DF_UD_CHAIN); + df_set_blocks (blocks); + df_analyze (); + + if (dump_file) + { + fprintf (dump_file, "*****starting processing of loop ******\n"); + print_rtl_with_bb (dump_file, get_insns ()); + fprintf (dump_file, "*****ending processing of loop ******\n"); + } + check_invariant_table_size (); + BITMAP_FREE (blocks); } @@ -673,8 +703,6 @@ record_use (struct def *def, rtx *use, rtx insn) { struct use *u = XNEW (struct use); - if (GET_CODE (*use) == SUBREG) - use = &SUBREG_REG (*use); gcc_assert (REG_P (*use)); u->pos = use; @@ -684,49 +712,68 @@ record_use (struct def *def, rtx *use, rtx insn) def->n_uses++; } -/* Finds the invariants INSN depends on and store them to the DEPENDS_ON - bitmap. Returns true if all dependencies of INSN are known to be +/* Finds the invariants USE depends on and store them to the DEPENDS_ON + bitmap. Returns true if all dependencies of USE are known to be loop invariants, false otherwise. */ static bool -check_dependencies (rtx insn, bitmap depends_on) +check_dependency (basic_block bb, struct df_ref *use, bitmap depends_on) { + struct df_ref *def; + basic_block def_bb; struct df_link *defs; - struct df_ref *use, *def; - basic_block bb = BLOCK_FOR_INSN (insn), def_bb; struct def *def_data; struct invariant *inv; + + if (use->flags & DF_REF_READ_WRITE) + return false; + + defs = DF_REF_CHAIN (use); + if (!defs) + return true; + + if (defs->next) + return false; + + def = defs->ref; + check_invariant_table_size (); + inv = invariant_table[DF_REF_ID(def)]; + if (!inv) + return false; + + def_data = inv->def; + gcc_assert (def_data != NULL); + + def_bb = DF_REF_BB (def); + /* Note that in case bb == def_bb, we know that the definition + dominates insn, because def has invariant_table[DF_REF_ID(def)] + defined and we process the insns in the basic block bb + sequentially. */ + if (!dominated_by_p (CDI_DOMINATORS, bb, def_bb)) + return false; + + bitmap_set_bit (depends_on, def_data->invno); + return true; +} - for (use = DF_INSN_GET (df, insn)->uses; use; use = use->next_ref) - { - if (use->flags & DF_REF_READ_WRITE) - return false; - - defs = DF_REF_CHAIN (use); - if (!defs) - continue; - - if (defs->next) - return false; - - def = defs->ref; - inv = DF_REF_DATA (def); - if (!inv) - return false; - - def_data = inv->def; - gcc_assert (def_data != NULL); - def_bb = DF_REF_BB (def); - /* Note that in case bb == def_bb, we know that the definition dominates - insn, because def has DF_REF_DATA defined and we process the insns - in the basic block bb sequentially. */ - if (!dominated_by_p (CDI_DOMINATORS, bb, def_bb)) - return false; +/* Finds the invariants INSN depends on and store them to the DEPENDS_ON + bitmap. Returns true if all dependencies of INSN are known to be + loop invariants, false otherwise. */ - bitmap_set_bit (depends_on, def_data->invno); - } +static bool +check_dependencies (rtx insn, bitmap depends_on) +{ + struct df_ref **use_rec; + basic_block bb = BLOCK_FOR_INSN (insn); + for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++) + if (!check_dependency (bb, *use_rec, depends_on)) + return false; + for (use_rec = DF_INSN_EQ_USES (insn); *use_rec; use_rec++) + if (!check_dependency (bb, *use_rec, depends_on)) + return false; + return true; } @@ -794,8 +841,9 @@ find_invariant_insn (rtx insn, bool always_reached, bool always_executed) if (simple) { - ref = df_find_def (df, insn, dest); - DF_REF_DATA (ref) = inv; + ref = df_find_def (insn, dest); + check_invariant_table_size (); + invariant_table[DF_REF_ID(ref)] = inv; } } @@ -804,14 +852,22 @@ find_invariant_insn (rtx insn, bool always_reached, bool always_executed) static void record_uses (rtx insn) { - struct df_ref *use; + struct df_ref **use_rec; struct invariant *inv; - for (use = DF_INSN_GET (df, insn)->uses; use; use = use->next_ref) + for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + inv = invariant_for_use (use); + if (inv) + record_use (inv->def, DF_REF_REAL_LOC (use), DF_REF_INSN (use)); + } + for (use_rec = DF_INSN_EQ_USES (insn); *use_rec; use_rec++) { + struct df_ref *use = *use_rec; inv = invariant_for_use (use); if (inv) - record_use (inv->def, DF_REF_LOC (use), DF_REF_INSN (use)); + record_use (inv->def, DF_REF_REAL_LOC (use), DF_REF_INSN (use)); } } @@ -1068,7 +1124,7 @@ find_invariants_to_move (void) { unsigned i, regs_used, regs_needed = 0, new_regs; struct invariant *inv = NULL; - unsigned int n_regs = DF_REG_SIZE (df); + unsigned int n_regs = DF_REG_SIZE (); if (!VEC_length (invariant_p, invariants)) return; @@ -1080,7 +1136,7 @@ find_invariants_to_move (void) for (i = 0; i < n_regs; i++) { - if (!DF_REGNO_FIRST_DEF (df, i) && DF_REGNO_LAST_USE (df, i)) + if (!DF_REGNO_FIRST_DEF (i) && DF_REGNO_LAST_USE (i)) { /* This is a value that is used but not changed inside loop. */ regs_used++; @@ -1127,7 +1183,6 @@ move_invariant_reg (struct loop *loop, unsigned invno) return true; if (!repr->move) return false; - /* If this is a representative of the class of equivalent invariants, really move the invariant. Otherwise just replace its use with the register used for the representative. */ @@ -1160,6 +1215,7 @@ move_invariant_reg (struct loop *loop, unsigned invno) emit_insn_after (gen_move_insn (dest, reg), inv->insn); SET_DEST (set) = reg; + df_insn_rescan (inv->insn); reorder_insns (inv->insn, inv->insn, BB_END (preheader)); /* If there is a REG_EQUAL note on the insn we just moved, and @@ -1204,6 +1260,7 @@ move_invariant_reg (struct loop *loop, unsigned invno) delete_insn (inv->insn); } + inv->reg = reg; /* Replace the uses we know to be dominated. It saves work for copy @@ -1212,7 +1269,10 @@ move_invariant_reg (struct loop *loop, unsigned invno) if (inv->def) { for (use = inv->def->uses; use; use = use->next) - *use->pos = reg; + { + *use->pos = reg; + df_insn_rescan (use->insn); + } } return true; @@ -1224,6 +1284,7 @@ fail: fprintf (dump_file, "Failed to move invariant %d\n", invno); inv->move = false; inv->reg = NULL_RTX; + return false; } @@ -1259,22 +1320,19 @@ free_inv_motion_data (void) struct def *def; struct invariant *inv; - for (i = 0; i < DF_DEFS_SIZE (df); i++) + check_invariant_table_size (); + for (i = 0; i < DF_DEFS_TABLE_SIZE (); i++) { - struct df_ref * ref = DF_DEFS_GET (df, i); - if (!ref) - continue; - - inv = DF_REF_DATA (ref); - if (!inv) - continue; - - def = inv->def; - gcc_assert (def != NULL); - - free_use_list (def->uses); - free (def); - DF_REF_DATA (ref) = NULL; + inv = invariant_table[i]; + if (inv) + { + def = inv->def; + gcc_assert (def != NULL); + + free_use_list (def->uses); + free (def); + invariant_table[i] = NULL; + } } for (i = 0; VEC_iterate (invariant_p, invariants, i, inv); i++) @@ -1318,9 +1376,7 @@ move_loop_invariants (void) struct loop *loop; loop_iterator li; - df = df_init (DF_HARD_REGS | DF_EQUIV_NOTES); - df_chain_add_problem (df, DF_UD_CHAIN); - + df_set_flags (DF_EQ_NOTES + DF_DEFER_INSN_RESCAN); /* Process the loops, innermost first. */ FOR_EACH_LOOP (li, loop, LI_FROM_INNERMOST) { @@ -1332,8 +1388,9 @@ move_loop_invariants (void) free_loop_data (loop); } - df_finish (df); - df = NULL; + free (invariant_table); + invariant_table = NULL; + invariant_table_size = 0; #ifdef ENABLE_CHECKING verify_flow_info (); diff --git a/gcc/loop-iv.c b/gcc/loop-iv.c index 0b6d46d..5016aaf 100644 --- a/gcc/loop-iv.c +++ b/gcc/loop-iv.c @@ -1,5 +1,5 @@ /* Rtl-level induction variable analysis. - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -45,8 +45,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA iv_analyze_expr (insn, rhs, mode, iv): Stores to IV the description of iv corresponding to expression EXPR evaluated at INSN. All registers used bu EXPR must also be used in INSN. - iv_current_loop_df (): Returns the dataflow object for the current loop used - by iv analysis. */ +*/ #include "config.h" #include "system.h" @@ -92,29 +91,25 @@ struct biv_entry struct rtx_iv iv; /* Value of the biv. */ }; +static bool clean_slate = true; + +static unsigned int iv_ref_table_size = 0; + +/* Table of rtx_ivs indexed by the df_ref uid field. */ +static struct rtx_iv ** iv_ref_table; + /* Induction variable stored at the reference. */ -#define DF_REF_IV(REF) ((struct rtx_iv *) DF_REF_DATA (REF)) -#define DF_REF_IV_SET(REF, IV) DF_REF_DATA (REF) = (IV) +#define DF_REF_IV(REF) iv_ref_table[DF_REF_ID(REF)] +#define DF_REF_IV_SET(REF, IV) iv_ref_table[DF_REF_ID(REF)] = (IV) /* The current loop. */ static struct loop *current_loop; -/* Dataflow information for the current loop. */ - -static struct df *df = NULL; - /* Bivs of the current loop. */ static htab_t bivs; -/* Return the dataflow object for the current loop. */ -struct df * -iv_current_loop_df (void) -{ - return df; -} - static bool iv_analyze_op (rtx, rtx, struct rtx_iv *); /* Dumps information about IV to FILE. */ @@ -172,6 +167,21 @@ lowpart_subreg (enum machine_mode outer_mode, rtx expr, subreg_lowpart_offset (outer_mode, inner_mode)); } +static void +check_iv_ref_table_size (void) +{ + if (iv_ref_table_size < DF_DEFS_TABLE_SIZE()) + { + unsigned int new_size = DF_DEFS_TABLE_SIZE () + (DF_DEFS_TABLE_SIZE () / 4); + iv_ref_table = xrealloc (iv_ref_table, + sizeof (struct rtx_iv *) * new_size); + memset (&iv_ref_table[iv_ref_table_size], 0, + (new_size - iv_ref_table_size) * sizeof (struct rtx_iv *)); + iv_ref_table_size = new_size; + } +} + + /* Checks whether REG is a well-behaved register. */ static bool @@ -204,18 +214,18 @@ simple_reg_p (rtx reg) static void clear_iv_info (void) { - unsigned i, n_defs = DF_DEFS_SIZE (df); + unsigned i, n_defs = DF_DEFS_TABLE_SIZE (); struct rtx_iv *iv; - struct df_ref *def; + check_iv_ref_table_size (); for (i = 0; i < n_defs; i++) { - def = DF_DEFS_GET (df, i); - iv = DF_REF_IV (def); - if (!iv) - continue; - free (iv); - DF_REF_IV_SET (def, NULL); + iv = iv_ref_table[i]; + if (iv) + { + free (iv); + iv_ref_table[i] = NULL; + } } htab_empty (bivs); @@ -245,16 +255,15 @@ iv_analysis_loop_init (struct loop *loop) basic_block *body = get_loop_body_in_dom_order (loop), bb; bitmap blocks = BITMAP_ALLOC (NULL); unsigned i; - bool first_time = (df == NULL); current_loop = loop; /* Clear the information from the analysis of the previous loop. */ - if (first_time) + if (clean_slate) { - df = df_init (DF_HARD_REGS | DF_EQUIV_NOTES); - df_chain_add_problem (df, DF_UD_CHAIN); + df_set_flags (DF_EQ_NOTES + DF_DEFER_INSN_RESCAN); bivs = htab_create (10, biv_hash, biv_eq, free); + clean_slate = false; } else clear_iv_info (); @@ -264,8 +273,17 @@ iv_analysis_loop_init (struct loop *loop) bb = body[i]; bitmap_set_bit (blocks, bb->index); } - df_set_blocks (df, blocks); - df_analyze (df); + /* Get rid of the ud chains before processing the rescans. Then add + the problem back. */ + df_remove_problem (df_chain); + df_process_deferred_rescans (); + df_chain_add_problem (DF_UD_CHAIN); + df_set_blocks (blocks); + df_analyze (); + if (dump_file) + df_dump (dump_file); + + check_iv_ref_table_size (); BITMAP_FREE (blocks); free (body); } @@ -280,10 +298,9 @@ latch_dominating_def (rtx reg, struct df_ref **def) { struct df_ref *single_rd = NULL, *adef; unsigned regno = REGNO (reg); - struct df_reg_info *reg_info = DF_REG_DEF_GET (df, regno); - struct df_rd_bb_info *bb_info = DF_RD_BB_INFO (df, current_loop->latch); + struct df_rd_bb_info *bb_info = DF_RD_BB_INFO (current_loop->latch); - for (adef = reg_info->reg_chain; adef; adef = adef->next_reg) + for (adef = DF_REG_DEF_CHAIN (regno); adef; adef = adef->next_reg) { if (!bitmap_bit_p (bb_info->out, DF_REF_ID (adef))) continue; @@ -319,7 +336,7 @@ iv_get_reaching_def (rtx insn, rtx reg, struct df_ref **def) reg = SUBREG_REG (reg); gcc_assert (REG_P (reg)); - use = df_find_use (df, insn, reg); + use = df_find_use (insn, reg); gcc_assert (use != NULL); if (!DF_REF_CHAIN (use)) @@ -335,7 +352,7 @@ iv_get_reaching_def (rtx insn, rtx reg, struct df_ref **def) use_bb = BLOCK_FOR_INSN (insn); if (use_bb == def_bb) - dom_p = (DF_INSN_LUID (df, def_insn) < DF_INSN_LUID (df, insn)); + dom_p = (DF_INSN_LUID (def_insn) < DF_INSN_LUID (insn)); else dom_p = dominated_by_p (CDI_DOMINATORS, use_bb, def_bb); @@ -786,6 +803,7 @@ record_iv (struct df_ref *def, struct rtx_iv *iv) struct rtx_iv *recorded_iv = XNEW (struct rtx_iv); *recorded_iv = *iv; + check_iv_ref_table_size (); DF_REF_IV_SET (def, recorded_iv); } @@ -1031,7 +1049,8 @@ iv_analyze_def (struct df_ref *def, struct rtx_iv *iv) fprintf (dump_file, " in insn "); print_rtl_single (dump_file, insn); } - + + check_iv_ref_table_size (); if (DF_REF_IV (def)) { if (dump_file) @@ -1044,10 +1063,17 @@ iv_analyze_def (struct df_ref *def, struct rtx_iv *iv) iv->base = NULL_RTX; iv->step = NULL_RTX; + if (!REG_P (reg)) + return false; + set = single_set (insn); - if (!set || SET_DEST (set) != reg) + if (!set) + return false; + + if (!REG_P (SET_DEST (set))) return false; + gcc_assert (SET_DEST (set) == reg); rhs = find_reg_equal_equiv_note (insn); if (rhs) rhs = XEXP (rhs, 0); @@ -1146,7 +1172,7 @@ iv_analyze (rtx insn, rtx val, struct rtx_iv *iv) else reg = val; - while (!df_find_use (df, insn, reg)) + while (!df_find_use (insn, reg)) insn = NEXT_INSN (insn); } @@ -1160,7 +1186,7 @@ iv_analyze_result (rtx insn, rtx def, struct rtx_iv *iv) { struct df_ref *adef; - adef = df_find_def (df, insn, def); + adef = df_find_def (insn, def); if (!adef) return false; @@ -1180,7 +1206,7 @@ biv_p (rtx insn, rtx reg) if (!simple_reg_p (reg)) return false; - def = df_find_def (df, insn, reg); + def = df_find_def (insn, reg); gcc_assert (def != NULL); if (!latch_dominating_def (reg, &last_def)) return false; @@ -1232,12 +1258,15 @@ get_iv_value (struct rtx_iv *iv, rtx iteration) void iv_analysis_done (void) { - if (df) + if (!clean_slate) { clear_iv_info (); - df_finish (df); - df = NULL; + clean_slate = true; + df_finish_pass (); htab_delete (bivs); + free (iv_ref_table); + iv_ref_table = NULL; + iv_ref_table_size = 0; bivs = NULL; } } diff --git a/gcc/lower-subreg.c b/gcc/lower-subreg.c index 0b50a88..ea14140 100644 --- a/gcc/lower-subreg.c +++ b/gcc/lower-subreg.c @@ -38,6 +38,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "except.h" #include "regs.h" #include "tree-pass.h" +#include "df.h" #ifdef STACK_GROWS_DOWNWARD # undef STACK_GROWS_DOWNWARD @@ -363,7 +364,6 @@ decompose_register (unsigned int regno) reg = regno_reg_rtx[regno]; regno_reg_rtx[regno] = NULL_RTX; - clear_reg_info_regno (regno); words = GET_MODE_SIZE (GET_MODE (reg)); words = (words + UNITS_PER_WORD - 1) / UNITS_PER_WORD; @@ -624,11 +624,15 @@ resolve_reg_notes (rtx insn) note = find_reg_equal_equiv_note (insn); if (note) { + int old_count = num_validated_changes (); if (for_each_rtx (&XEXP (note, 0), resolve_subreg_use, NULL)) { remove_note (insn, note); remove_retval_note (insn); } + else + if (old_count != num_validated_changes ()) + df_notes_rescan (insn); } pnote = ®_NOTES (insn); @@ -640,6 +644,8 @@ resolve_reg_notes (rtx insn) switch (REG_NOTE_KIND (note)) { case REG_NO_CONFLICT: + case REG_DEAD: + case REG_UNUSED: if (resolve_reg_p (XEXP (note, 0))) delete = true; break; @@ -909,6 +915,7 @@ resolve_clobber (rtx pat, rtx insn) simplify_gen_subreg_concatn (word_mode, reg, orig_mode, 0), 0); + df_insn_rescan (insn); gcc_assert (ret != 0); for (i = words - 1; i > 0; --i) @@ -943,11 +950,14 @@ resolve_use (rtx pat, rtx insn) pseudo-registers. */ static void -decompose_multiword_subregs (bool update_life) +decompose_multiword_subregs (void) { unsigned int max; basic_block bb; + if (df) + df_set_flags (DF_DEFER_INSN_RESCAN); + max = max_reg_num (); /* First see if there are any multi-word pseudo-registers. If there @@ -1061,8 +1071,6 @@ decompose_multiword_subregs (bool update_life) if (!bitmap_empty_p (decomposable_context)) { int hold_no_new_pseudos = no_new_pseudos; - int max_regno = max_reg_num (); - sbitmap life_blocks; sbitmap sub_blocks; unsigned int i; sbitmap_iterator sbi; @@ -1072,8 +1080,6 @@ decompose_multiword_subregs (bool update_life) propagate_pseudo_copies (); no_new_pseudos = 0; - life_blocks = sbitmap_alloc (last_basic_block); - sbitmap_zero (life_blocks); sub_blocks = sbitmap_alloc (last_basic_block); sbitmap_zero (sub_blocks); @@ -1176,21 +1182,11 @@ decompose_multiword_subregs (bool update_life) changed = true; } } - - if (changed) - { - SET_BIT (life_blocks, bb->index); - reg_scan_update (insn, next, max_regno); - } } } no_new_pseudos = hold_no_new_pseudos; - if (update_life) - update_life_info (life_blocks, UPDATE_LIFE_GLOBAL_RM_NOTES, - PROP_DEATH_NOTES); - /* If we had insns to split that caused control flow insns in the middle of a basic block, split those blocks now. Note that we only handle the case where splitting a load has caused multiple possibly trapping @@ -1221,7 +1217,6 @@ decompose_multiword_subregs (bool update_life) } } - sbitmap_free (life_blocks); sbitmap_free (sub_blocks); } @@ -1253,7 +1248,7 @@ gate_handle_lower_subreg (void) static unsigned int rest_of_handle_lower_subreg (void) { - decompose_multiword_subregs (false); + decompose_multiword_subregs (); return 0; } @@ -1262,7 +1257,7 @@ rest_of_handle_lower_subreg (void) static unsigned int rest_of_handle_lower_subreg2 (void) { - decompose_multiword_subregs (true); + decompose_multiword_subregs (); return 0; } @@ -1298,6 +1293,7 @@ struct tree_opt_pass pass_lower_subreg2 = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func | TODO_ggc_collect | TODO_verify_flow, /* todo_flags_finish */ diff --git a/gcc/mode-switching.c b/gcc/mode-switching.c index 448192a..0e4f58c 100644 --- a/gcc/mode-switching.c +++ b/gcc/mode-switching.c @@ -1,5 +1,5 @@ /* CPU mode switching - Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -36,6 +36,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "function.h" #include "tree-pass.h" #include "timevar.h" +#include "df.h" /* We want target macros for the mode switching code to be able to refer to instruction attribute values. */ @@ -217,7 +218,6 @@ create_pre_exit (int n_entities, int *entity_map, const int *num_modes) if (eg->flags & EDGE_FALLTHRU) { basic_block src_bb = eg->src; - regset live_at_end = src_bb->il.rtl->global_live_at_end; rtx last_insn, ret_reg; gcc_assert (!pre_exit); @@ -270,6 +270,25 @@ create_pre_exit (int n_entities, int *entity_map, const int *num_modes) return_copy_pat = PATTERN (return_copy); if (GET_CODE (return_copy_pat) != CLOBBER) break; + else if (!optimize) + { + /* This might be (clobber (reg [<result>])) + when not optimizing. Then check if + the previous insn is the clobber for + the return register. */ + copy_reg = SET_DEST (return_copy_pat); + if (GET_CODE (copy_reg) == REG + && !HARD_REGISTER_NUM_P (REGNO (copy_reg))) + { + if (INSN_P (PREV_INSN (return_copy))) + { + return_copy = PREV_INSN (return_copy); + return_copy_pat = PATTERN (return_copy); + if (GET_CODE (return_copy_pat) != CLOBBER) + break; + } + } + } } copy_reg = SET_DEST (return_copy_pat); if (GET_CODE (copy_reg) == REG) @@ -372,8 +391,6 @@ create_pre_exit (int n_entities, int *entity_map, const int *num_modes) else { pre_exit = split_edge (eg); - COPY_REG_SET (pre_exit->il.rtl->global_live_at_start, live_at_end); - COPY_REG_SET (pre_exit->il.rtl->global_live_at_end, live_at_end); } } @@ -403,8 +420,6 @@ optimize_mode_switching (void) bool emited = false; basic_block post_entry ATTRIBUTE_UNUSED, pre_exit ATTRIBUTE_UNUSED; - clear_bb_flags (); - for (e = N_ENTITIES - 1, n_entities = 0; e >= 0; e--) if (OPTIMIZE_MODE_SWITCHING (e)) { @@ -433,6 +448,8 @@ optimize_mode_switching (void) pre_exit = create_pre_exit (n_entities, entity_map, num_modes); #endif + df_analyze (); + /* Create the bitmap vectors. */ antic = sbitmap_vector_alloc (last_basic_block, n_entities); @@ -456,8 +473,7 @@ optimize_mode_switching (void) int last_mode = no_mode; HARD_REG_SET live_now; - REG_SET_TO_HARD_REG_SET (live_now, - bb->il.rtl->global_live_at_start); + REG_SET_TO_HARD_REG_SET (live_now, df_get_live_in (bb)); /* Pretend the mode is clobbered across abnormal edges. */ { @@ -602,8 +618,7 @@ optimize_mode_switching (void) mode = current_mode[j]; src_bb = eg->src; - REG_SET_TO_HARD_REG_SET (live_at_edge, - src_bb->il.rtl->global_live_at_end); + REG_SET_TO_HARD_REG_SET (live_at_edge, df_get_live_out (src_bb)); start_sequence (); EMIT_MODE_SET (entity_map[j], mode, live_at_edge); @@ -675,7 +690,6 @@ optimize_mode_switching (void) } /* Finished. Free up all the things we've allocated. */ - sbitmap_vector_free (kill); sbitmap_vector_free (antic); sbitmap_vector_free (transp); @@ -691,12 +705,6 @@ optimize_mode_switching (void) return 0; #endif - max_regno = max_reg_num (); - allocate_reg_info (max_regno, FALSE, FALSE); - update_life_info_in_dirty_blocks (UPDATE_LIFE_GLOBAL_RM_NOTES, - (PROP_DEATH_NOTES | PROP_KILL_DEAD_CODE - | PROP_SCAN_DEAD_CODE)); - return 1; } @@ -737,6 +745,7 @@ struct tree_opt_pass pass_mode_switching = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func, /* todo_flags_finish */ 0 /* letter */ }; diff --git a/gcc/modulo-sched.c b/gcc/modulo-sched.c index 3e14ff5..25cd53a 100644 --- a/gcc/modulo-sched.c +++ b/gcc/modulo-sched.c @@ -45,7 +45,6 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "expr.h" #include "params.h" #include "gcov-io.h" -#include "df.h" #include "ddg.h" #include "timevar.h" #include "tree-pass.h" @@ -260,9 +259,6 @@ static struct sched_info sms_sched_info = 0, 0, 0, NULL, NULL, NULL, NULL, NULL, -#ifdef ENABLE_CHECKING - NULL, -#endif 0 }; @@ -502,7 +498,7 @@ generate_reg_moves (partial_schedule_ptr ps) rtx reg_move = gen_move_insn (new_reg, prev_reg); sbitmap_iterator sbi; - add_insn_before (reg_move, last_reg_move); + add_insn_before (reg_move, last_reg_move, NULL); last_reg_move = reg_move; if (!SCHED_FIRST_REG_MOVE (u)) @@ -884,7 +880,6 @@ sms_schedule (void) int maxii; loop_iterator li; partial_schedule_ptr ps; - struct df *df; basic_block bb = NULL; struct loop *loop; basic_block condition_bb = NULL; @@ -913,17 +908,16 @@ sms_schedule (void) /* Initialize the scheduler. */ current_sched_info = &sms_sched_info; - sched_init (); /* Init Data Flow analysis, to be used in interloop dep calculation. */ - df = df_init (DF_HARD_REGS | DF_EQUIV_NOTES | DF_SUBREGS); - df_rd_add_problem (df, 0); - df_ru_add_problem (df, 0); - df_chain_add_problem (df, DF_DU_CHAIN | DF_UD_CHAIN); - df_analyze (df); - - if (dump_file) - df_dump (df, dump_file); + df_set_flags (DF_LR_RUN_DCE); + df_rd_add_problem (); + df_ru_add_problem (); + df_note_add_problem (); + df_chain_add_problem (DF_DU_CHAIN + DF_UD_CHAIN); + df_analyze (); + regstat_compute_calls_crossed (); + sched_init (); /* Allocate memory to hold the DDG array one entry for each loop. We use loop->num as index into this array. */ @@ -1016,7 +1010,7 @@ sms_schedule (void) continue; } - if (! (g = create_ddg (bb, df, 0))) + if (! (g = create_ddg (bb, 0))) { if (dump_file) fprintf (dump_file, "SMS doloop\n"); @@ -1026,10 +1020,6 @@ sms_schedule (void) g_arr[loop->num] = g; } - /* Release Data Flow analysis data structures. */ - df_finish (df); - df = NULL; - /* We don't want to perform SMS on new loops - created by versioning. */ FOR_EACH_LOOP (li, loop, 0) { @@ -1209,7 +1199,7 @@ sms_schedule (void) if (! flag_resched_modulo_sched) g->bb->flags |= BB_DISABLE_SCHEDULE; /* The life-info is not valid any more. */ - g->bb->flags |= BB_DIRTY; + df_set_bb_dirty (g->bb); reg_move_replaces = generate_reg_moves (ps); if (dump_file) @@ -1229,6 +1219,7 @@ sms_schedule (void) free_ddg (g); } + regstat_free_calls_crossed (); free (g_arr); /* Release scheduler data, needed until now because of DFA. */ @@ -2479,18 +2470,11 @@ rest_of_handle_sms (void) /* We want to be able to create new pseudos. */ no_new_pseudos = 0; /* Collect loop information to be used in SMS. */ - cfg_layout_initialize (CLEANUP_UPDATE_LIFE); + cfg_layout_initialize (0); sms_schedule (); /* Update the life information, because we add pseudos. */ max_regno = max_reg_num (); - allocate_reg_info (max_regno, FALSE, FALSE); - update_life_info (NULL, UPDATE_LIFE_GLOBAL_RM_NOTES, - (PROP_DEATH_NOTES - | PROP_REG_INFO - | PROP_KILL_DEAD_CODE - | PROP_SCAN_DEAD_CODE)); - no_new_pseudos = 1; /* Finalize layout changes. */ @@ -2516,6 +2500,7 @@ struct tree_opt_pass pass_sms = 0, /* properties_provided */ 0, /* properties_destroyed */ TODO_dump_func, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func | TODO_ggc_collect, /* todo_flags_finish */ 'm' /* letter */ diff --git a/gcc/optabs.c b/gcc/optabs.c index cfd2934..e86ed62 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -126,6 +126,10 @@ static rtx expand_parity (enum machine_mode, rtx, rtx); static enum rtx_code get_rtx_code (enum tree_code, bool); static rtx vector_compare_rtx (tree, bool, enum insn_code); +/* Current libcall id. It doesn't matter what these are, as long + as they are unique to each libcall that is emitted. */ +static HOST_WIDE_INT libcall_id = 0; + #ifndef HAVE_conditional_trap #define HAVE_conditional_trap 0 #define gen_conditional_trap(a,b) (gcc_unreachable (), NULL_RTX) @@ -3383,7 +3387,7 @@ no_conflict_move_test (rtx dest, rtx set, void *p0) logically equivalent to EQUIV, so it gets manipulated as a unit if it is possible to do so. */ -static void +void maybe_encapsulate_block (rtx first, rtx last, rtx equiv) { if (!flag_non_call_exceptions || !may_trap_p (equiv)) @@ -3407,6 +3411,12 @@ maybe_encapsulate_block (rtx first, rtx last, rtx equiv) REG_NOTES (first)); REG_NOTES (last) = gen_rtx_INSN_LIST (REG_RETVAL, first, REG_NOTES (last)); + next = NEXT_INSN (last); + for (insn = first; insn != next; insn = NEXT_INSN (insn)) + REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_LIBCALL_ID, + GEN_INT (libcall_id), + REG_NOTES (insn)); + libcall_id++; } } } @@ -3467,6 +3477,8 @@ emit_no_conflict_block (rtx insns, rtx target, rtx op0, rtx op1, rtx equiv) remove_note (insn, note); if ((note = find_reg_note (insn, REG_RETVAL, NULL)) != NULL) remove_note (insn, note); + if ((note = find_reg_note (insn, REG_LIBCALL_ID, NULL)) != NULL) + remove_note (insn, note); data.target = target; data.first = insns; @@ -3561,7 +3573,6 @@ emit_no_conflict_block (rtx insns, rtx target, rtx op0, rtx op1, rtx equiv) Except for the first group of insns (the ones setting pseudos), the block is delimited by REG_RETVAL and REG_LIBCALL notes. */ - void emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv) { @@ -3620,6 +3631,8 @@ emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv) remove_note (insn, note); if ((note = find_reg_note (insn, REG_RETVAL, NULL)) != NULL) remove_note (insn, note); + if ((note = find_reg_note (insn, REG_LIBCALL_ID, NULL)) != NULL) + remove_note (insn, note); next = NEXT_INSN (insn); diff --git a/gcc/optabs.h b/gcc/optabs.h index c344b65..c1d57f6 100644 --- a/gcc/optabs.h +++ b/gcc/optabs.h @@ -1,5 +1,5 @@ /* Definitions for code generation pass of GNU compiler. - Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 + Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -644,6 +644,10 @@ extern rtx expand_copysign (rtx, rtx, rtx); an input. */ extern void emit_unop_insn (int, rtx, rtx, enum rtx_code); +/* Excapsulate the block in REG_LIBCALL, and REG_RETVAL reg notes and add + REG_LIBCALL_ID notes to all insns in block. */ +extern void maybe_encapsulate_block (rtx, rtx, rtx); + /* Emit code to perform a series of operations on a multi-word quantity, one word at a time. */ extern rtx emit_no_conflict_block (rtx, rtx, rtx, rtx, rtx); @@ -40,6 +40,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "insn-attr.h" /* For INSN_SCHEDULING. */ #include "target.h" #include "tree-pass.h" +#include "dbgcnt.h" /* Value of the -G xx switch, and whether it was passed or not. */ unsigned HOST_WIDE_INT g_switch_value; @@ -1429,6 +1430,10 @@ common_handle_option (size_t scode, const char *arg, int value, fix_register (arg, 0, 0); break; + case OPT_fdbg_cnt_: + dbg_cnt_process_opt (arg); + break; + case OPT_fdiagnostics_show_location_: if (!strcmp (arg, "once")) diagnostic_prefixing_rule (global_dc) = DIAGNOSTICS_SHOW_PREFIX_ONCE; diff --git a/gcc/output.h b/gcc/output.h index 4cb41d4f..d542c66 100644 --- a/gcc/output.h +++ b/gcc/output.h @@ -1,7 +1,7 @@ /* Declarations for insn-output.c. These functions are defined in recog.c, final.c, and varasm.c. - Copyright (C) 1987, 1991, 1994, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1987, 1991, 1994, 1997, 1998, 1999, 2000, 2001, 2002, + 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -145,9 +145,6 @@ extern void leaf_renumber_regs_insn (rtx); /* Locate the proper template for the given insn-code. */ extern const char *get_insn_template (int, rtx); -/* Functions in flow.c */ -extern int regno_clobbered_at_setjmp (int); - /* Functions in varasm.c. */ /* Declare DECL to be a weak symbol. */ @@ -346,7 +343,7 @@ extern int current_function_is_leaf; /* Nonzero if function being compiled doesn't modify the stack pointer (ignoring the prologue and epilogue). This is only valid after - life_analysis has run. */ + pass_stack_ptr_mod has run. */ extern int current_function_sp_is_unchanging; diff --git a/gcc/passes.c b/gcc/passes.c index f50b7a5..7c2754d 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -83,6 +83,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "tree-flow.h" #include "tree-pass.h" #include "tree-dump.h" +#include "df.h" #include "predict.h" #if defined (DWARF2_UNWIND_INFO) || defined (DWARF2_DEBUGGING_INFO) @@ -102,6 +103,33 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA declarations for e.g. AIX 4.x. */ #endif +/* This is used for debugging. It allows the current pass to printed + from anywhere in compilation. */ +struct tree_opt_pass *current_pass; + +/* Call from anywhere to find out what pass this is. Useful for + printing out debugging information deep inside an service + routine. */ +void +print_current_pass (FILE *file) +{ + if (current_pass) + fprintf (file, "current pass = %s (%d)\n", + current_pass->name, current_pass->static_pass_number); + else + fprintf (file, "no current pass.\n"); +} + + +/* Call from the debugger to get the current pass name. */ +void +debug_pass (void) +{ + print_current_pass (stderr); +} + + + /* Global variables used to communicate with passes. */ int dump_flags; bool in_gimple_form; @@ -418,6 +446,7 @@ next_pass_1 (struct tree_opt_pass **list, struct tree_opt_pass *pass) } + /* Construct the pass tree. The sequencing of passes is driven by the cgraph routines: @@ -668,6 +697,7 @@ init_optimization_passes (void) NEXT_PASS (pass_into_cfg_layout_mode); NEXT_PASS (pass_jump2); NEXT_PASS (pass_lower_subreg); + NEXT_PASS (pass_df_initialize_opt); NEXT_PASS (pass_cse); NEXT_PASS (pass_rtl_fwprop); NEXT_PASS (pass_gcse); @@ -690,39 +720,57 @@ init_optimization_passes (void) NEXT_PASS (pass_web); NEXT_PASS (pass_jump_bypass); NEXT_PASS (pass_cse2); + NEXT_PASS (pass_rtl_dse1); NEXT_PASS (pass_rtl_fwprop_addr); + NEXT_PASS (pass_regclass_init); + NEXT_PASS (pass_inc_dec); + NEXT_PASS (pass_initialize_regs); + NEXT_PASS (pass_no_new_pseudos); NEXT_PASS (pass_outof_cfg_layout_mode); - NEXT_PASS (pass_life); + NEXT_PASS (pass_ud_rtl_dce); NEXT_PASS (pass_combine); NEXT_PASS (pass_if_after_combine); NEXT_PASS (pass_partition_blocks); NEXT_PASS (pass_regmove); NEXT_PASS (pass_split_all_insns); NEXT_PASS (pass_lower_subreg2); + NEXT_PASS (pass_df_initialize_no_opt); + NEXT_PASS (pass_stack_ptr_mod); NEXT_PASS (pass_mode_switching); NEXT_PASS (pass_see); - NEXT_PASS (pass_recompute_reg_usage); NEXT_PASS (pass_sms); NEXT_PASS (pass_sched); + NEXT_PASS (pass_subregs_of_mode_init); NEXT_PASS (pass_local_alloc); NEXT_PASS (pass_global_alloc); + NEXT_PASS (pass_subregs_of_mode_finish); NEXT_PASS (pass_postreload); { struct tree_opt_pass **p = &pass_postreload.sub; NEXT_PASS (pass_postreload_cse); NEXT_PASS (pass_gcse2); - NEXT_PASS (pass_flow2); + NEXT_PASS (pass_split_after_reload); + NEXT_PASS (pass_branch_target_load_optimize1); + NEXT_PASS (pass_thread_prologue_and_epilogue); + NEXT_PASS (pass_rtl_dse2); NEXT_PASS (pass_rtl_seqabstr); NEXT_PASS (pass_stack_adjustments); NEXT_PASS (pass_peephole2); NEXT_PASS (pass_if_after_reload); NEXT_PASS (pass_regrename); + NEXT_PASS (pass_cprop_hardreg); + NEXT_PASS (pass_fast_rtl_dce); NEXT_PASS (pass_reorder_blocks); - NEXT_PASS (pass_branch_target_load_optimize); + NEXT_PASS (pass_branch_target_load_optimize2); NEXT_PASS (pass_leaf_regs); + NEXT_PASS (pass_split_before_sched2); NEXT_PASS (pass_sched2); - NEXT_PASS (pass_split_before_regstack); NEXT_PASS (pass_stack_regs); + { + struct tree_opt_pass **p = &pass_stack_regs.sub; + NEXT_PASS (pass_split_before_regstack); + NEXT_PASS (pass_stack_regs_run); + } NEXT_PASS (pass_compute_alignments); NEXT_PASS (pass_duplicate_computed_gotos); NEXT_PASS (pass_variable_tracking); @@ -730,6 +778,7 @@ init_optimization_passes (void) NEXT_PASS (pass_machine_reorg); NEXT_PASS (pass_cleanup_barriers); NEXT_PASS (pass_delay_slots); + NEXT_PASS (pass_df_finish); NEXT_PASS (pass_split_for_shorten_branches); NEXT_PASS (pass_convert_to_eh_region_ranges); NEXT_PASS (pass_shorten_branches); @@ -964,6 +1013,11 @@ execute_todo (unsigned int flags) { ggc_collect (); } + + /* Now that the dumping has been done, we can get rid of the optional + df problems. */ + if (flags & TODO_df_finish) + df_finish_pass (); } /* Verify invariants that should hold between passes. This is a place @@ -1013,6 +1067,7 @@ execute_one_pass (struct tree_opt_pass *pass) bool initializing_dump; unsigned int todo_after = 0; + current_pass = pass; /* See if we're supposed to run this pass. */ if (pass->gate && !pass->gate ()) return false; @@ -1106,6 +1161,7 @@ execute_one_pass (struct tree_opt_pass *pass) dump_file = NULL; } + current_pass = NULL; /* Reset in_gimple_form to not break non-unit-at-a-time mode. */ in_gimple_form = false; diff --git a/gcc/postreload-gcse.c b/gcc/postreload-gcse.c index 3e3f805..fdf5b04 100644 --- a/gcc/postreload-gcse.c +++ b/gcc/postreload-gcse.c @@ -1,5 +1,5 @@ /* Post reload partially redundant load elimination - Copyright (C) 2004, 2005 + Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -46,6 +46,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "target.h" #include "timevar.h" #include "tree-pass.h" +#include "dbgcnt.h" /* The following code implements gcse after reload, the purpose of this pass is to cleanup redundant loads generated by reload and other @@ -1225,7 +1226,7 @@ delete_redundant_insns_1 (void **slot, void *data ATTRIBUTE_UNUSED) for (occr = expr->avail_occr; occr != NULL; occr = occr->next) { - if (occr->deleted_p) + if (occr->deleted_p && dbg_cnt (gcse2_delete)) { delete_insn (occr->insn); stats.insns_deleted++; @@ -1305,7 +1306,6 @@ rest_of_handle_gcse2 (void) { gcse_after_reload_main (get_insns ()); rebuild_jump_labels (get_insns ()); - delete_trivially_dead_insns (get_insns (), max_reg_num ()); return 0; } diff --git a/gcc/postreload.c b/gcc/postreload.c index 47930ad..3182ac0 100644 --- a/gcc/postreload.c +++ b/gcc/postreload.c @@ -1,6 +1,7 @@ /* Perform simple optimizations to clean up the result of reload. - Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, + 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. This file is part of GCC. @@ -46,6 +47,8 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "tree.h" #include "timevar.h" #include "tree-pass.h" +#include "df.h" +#include "dbgcnt.h" static int reload_cse_noop_set_p (rtx); static void reload_cse_simplify (rtx, rtx); @@ -521,7 +524,7 @@ reload_cse_simplify_operands (rtx insn, rtx testreg) if (! TEST_HARD_REG_BIT (equiv_regs[i], regno)) continue; - REGNO (testreg) = regno; + SET_REGNO (testreg, regno); PUT_MODE (testreg, mode); /* We found a register equal to this operand. Now look for all @@ -741,10 +744,8 @@ reload_combine (void) { HARD_REG_SET live; - REG_SET_TO_HARD_REG_SET (live, - bb->il.rtl->global_live_at_start); - compute_use_by_pseudos (&live, - bb->il.rtl->global_live_at_start); + REG_SET_TO_HARD_REG_SET (live, DF_LIVE_IN (bb)); + compute_use_by_pseudos (&live, DF_LIVE_IN (bb)); COPY_HARD_REG_SET (LABEL_LIVE (insn), live); IOR_HARD_REG_SET (ever_live_at_start, live); } @@ -1219,7 +1220,8 @@ reload_cse_move2add (rtx first) /* Check if we have valid information on the contents of this register in the mode of REG. */ if (reg_set_luid[regno] > move2add_last_label_luid - && MODES_OK_FOR_MOVE2ADD (GET_MODE (reg), reg_mode[regno])) + && MODES_OK_FOR_MOVE2ADD (GET_MODE (reg), reg_mode[regno]) + && dbg_cnt (cse2_move2add)) { /* Try to transform (set (REGX) (CONST_INT A)) ... @@ -1570,12 +1572,16 @@ gate_handle_postreload (void) static unsigned int rest_of_handle_postreload (void) { + if (!dbg_cnt (postreload_cse)) + return 0; + /* Do a very simple CSE pass over just the hard registers. */ reload_cse_regs (get_insns ()); - /* reload_cse_regs can eliminate potentially-trapping MEMs. + /* Reload_cse_regs can eliminate potentially-trapping MEMs. Remove any EH edges associated with them. */ if (flag_non_call_exceptions) purge_all_dead_edges (); + return 0; } @@ -1592,6 +1598,7 @@ struct tree_opt_pass pass_postreload_cse = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func, /* todo_flags_finish */ 'o' /* letter */ }; diff --git a/gcc/recog.c b/gcc/recog.c index 19b6cb2..b12541e 100644 --- a/gcc/recog.c +++ b/gcc/recog.c @@ -43,6 +43,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "reload.h" #include "timevar.h" #include "tree-pass.h" +#include "df.h" #ifndef STACK_PUSH_CODE #ifdef STACK_GROWS_DOWNWARD @@ -61,7 +62,6 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #endif static void validate_replace_rtx_1 (rtx *, rtx, rtx, rtx); -static rtx *find_single_use_1 (rtx, rtx *); static void validate_replace_src_1 (rtx *, void *); static rtx split_insn (rtx); @@ -406,21 +406,21 @@ verify_changes (int num) return (i == num_changes); } -/* A group of changes has previously been issued with validate_change and - verified with verify_changes. Update the BB_DIRTY flags of the affected - blocks, and clear num_changes. */ +/* A group of changes has previously been issued with validate_change + and verified with verify_changes. Call df_insn_rescan for each of + the insn changed and clear num_changes. */ void confirm_change_group (void) { int i; - basic_block bb; for (i = 0; i < num_changes; i++) - if (changes[i].object - && INSN_P (changes[i].object) - && (bb = BLOCK_FOR_INSN (changes[i].object))) - bb->flags |= BB_DIRTY; + { + rtx object = changes[i].object; + if (object && INSN_P (object)) + df_insn_rescan (object); + } num_changes = 0; } @@ -781,168 +781,6 @@ next_insn_tests_no_inequality (rtx insn) } #endif -/* This is used by find_single_use to locate an rtx that contains exactly one - use of DEST, which is typically either a REG or CC0. It returns a - pointer to the innermost rtx expression containing DEST. Appearances of - DEST that are being used to totally replace it are not counted. */ - -static rtx * -find_single_use_1 (rtx dest, rtx *loc) -{ - rtx x = *loc; - enum rtx_code code = GET_CODE (x); - rtx *result = 0; - rtx *this_result; - int i; - const char *fmt; - - switch (code) - { - case CONST_INT: - case CONST: - case LABEL_REF: - case SYMBOL_REF: - case CONST_DOUBLE: - case CONST_VECTOR: - case CLOBBER: - return 0; - - case SET: - /* If the destination is anything other than CC0, PC, a REG or a SUBREG - of a REG that occupies all of the REG, the insn uses DEST if - it is mentioned in the destination or the source. Otherwise, we - need just check the source. */ - if (GET_CODE (SET_DEST (x)) != CC0 - && GET_CODE (SET_DEST (x)) != PC - && !REG_P (SET_DEST (x)) - && ! (GET_CODE (SET_DEST (x)) == SUBREG - && REG_P (SUBREG_REG (SET_DEST (x))) - && (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (x)))) - + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD) - == ((GET_MODE_SIZE (GET_MODE (SET_DEST (x))) - + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)))) - break; - - return find_single_use_1 (dest, &SET_SRC (x)); - - case MEM: - case SUBREG: - return find_single_use_1 (dest, &XEXP (x, 0)); - - default: - break; - } - - /* If it wasn't one of the common cases above, check each expression and - vector of this code. Look for a unique usage of DEST. */ - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - if (dest == XEXP (x, i) - || (REG_P (dest) && REG_P (XEXP (x, i)) - && REGNO (dest) == REGNO (XEXP (x, i)))) - this_result = loc; - else - this_result = find_single_use_1 (dest, &XEXP (x, i)); - - if (result == 0) - result = this_result; - else if (this_result) - /* Duplicate usage. */ - return 0; - } - else if (fmt[i] == 'E') - { - int j; - - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - { - if (XVECEXP (x, i, j) == dest - || (REG_P (dest) - && REG_P (XVECEXP (x, i, j)) - && REGNO (XVECEXP (x, i, j)) == REGNO (dest))) - this_result = loc; - else - this_result = find_single_use_1 (dest, &XVECEXP (x, i, j)); - - if (result == 0) - result = this_result; - else if (this_result) - return 0; - } - } - } - - return result; -} - -/* See if DEST, produced in INSN, is used only a single time in the - sequel. If so, return a pointer to the innermost rtx expression in which - it is used. - - If PLOC is nonzero, *PLOC is set to the insn containing the single use. - - This routine will return usually zero either before flow is called (because - there will be no LOG_LINKS notes) or after reload (because the REG_DEAD - note can't be trusted). - - If DEST is cc0_rtx, we look only at the next insn. In that case, we don't - care about REG_DEAD notes or LOG_LINKS. - - Otherwise, we find the single use by finding an insn that has a - LOG_LINKS pointing at INSN and has a REG_DEAD note for DEST. If DEST is - only referenced once in that insn, we know that it must be the first - and last insn referencing DEST. */ - -rtx * -find_single_use (rtx dest, rtx insn, rtx *ploc) -{ - rtx next; - rtx *result; - rtx link; - -#ifdef HAVE_cc0 - if (dest == cc0_rtx) - { - next = NEXT_INSN (insn); - if (next == 0 - || (!NONJUMP_INSN_P (next) && !JUMP_P (next))) - return 0; - - result = find_single_use_1 (dest, &PATTERN (next)); - if (result && ploc) - *ploc = next; - return result; - } -#endif - - if (reload_completed || reload_in_progress || !REG_P (dest)) - return 0; - - for (next = next_nonnote_insn (insn); - next != 0 && !LABEL_P (next); - next = next_nonnote_insn (next)) - if (INSN_P (next) && dead_or_set_p (next, dest)) - { - for (link = LOG_LINKS (next); link; link = XEXP (link, 1)) - if (XEXP (link, 0) == insn) - break; - - if (link) - { - result = find_single_use_1 (dest, &PATTERN (next)); - if (ploc) - *ploc = next; - return result; - } - } - - return 0; -} - /* Return 1 if OP is a valid general operand for machine mode MODE. This is either a register reference, a memory reference, or a constant. In the case of a memory reference, the address @@ -1691,7 +1529,7 @@ asm_operand_ok (rtx op, const char *constraint) break; case '<': - /* ??? Before flow, auto inc/dec insns are not supposed to exist, + /* ??? Before auto-inc-dec, auto inc/dec insns are not supposed to exist, excepting those that expand_call created. Further, on some machines which do not have generalized auto inc/dec, an inc/dec is not a memory_operand. @@ -2755,7 +2593,7 @@ split_insn (rtx insn) /* Split all insns in the function. If UPD_LIFE, update life info after. */ void -split_all_insns (int upd_life) +split_all_insns (void) { sbitmap blocks; bool changed; @@ -2791,17 +2629,7 @@ split_all_insns (int upd_life) allocation, and there are unlikely to be very many nops then anyways. */ if (reload_completed) - { - /* If the no-op set has a REG_UNUSED note, we need - to update liveness information. */ - if (find_reg_note (insn, REG_UNUSED, NULL_RTX)) - { - SET_BIT (blocks, bb->index); - changed = true; - } - /* ??? Is life info affected by deleting edges? */ delete_insn_and_edges (insn); - } } else { @@ -2823,18 +2651,7 @@ split_all_insns (int upd_life) } if (changed) - { - int old_last_basic_block = last_basic_block; - - find_many_sub_basic_blocks (blocks); - - if (old_last_basic_block != last_basic_block && upd_life) - blocks = sbitmap_resize (blocks, last_basic_block, 1); - } - - if (changed && upd_life) - update_life_info (blocks, UPDATE_LIFE_GLOBAL_RM_NOTES, - PROP_DEATH_NOTES); + find_many_sub_basic_blocks (blocks); #ifdef ENABLE_CHECKING verify_flow_info (); @@ -2893,7 +2710,7 @@ int peep2_current_count; /* A non-insn marker indicating the last insn of the block. The live_before regset for this element is correct, indicating - global_live_at_end for the block. */ + DF_LIVE_OUT for the block. */ #define PEEP2_EOB pc_rtx /* Return the Nth non-note insn after `current', or return NULL_RTX if it @@ -3023,7 +2840,7 @@ peep2_find_free_register (int from, int to, const char *class_str, if (! HARD_REGNO_MODE_OK (regno, mode)) continue; /* And that we don't create an extra save/restore. */ - if (! call_used_regs[regno] && ! regs_ever_live[regno]) + if (! call_used_regs[regno] && ! df_regs_ever_live_p (regno)) continue; /* And we don't clobber traceback for noreturn functions. */ if ((regno == FRAME_POINTER_REGNUM || regno == HARD_FRAME_POINTER_REGNUM) @@ -3063,36 +2880,22 @@ static void peephole2_optimize (void) { rtx insn, prev; - regset live; + bitmap live; int i; basic_block bb; -#ifdef HAVE_conditional_execution - sbitmap blocks; - bool changed; -#endif bool do_cleanup_cfg = false; - bool do_global_life_update = false; bool do_rebuild_jump_labels = false; + df_set_flags (DF_LR_RUN_DCE); + df_analyze (); + /* Initialize the regsets we're going to use. */ for (i = 0; i < MAX_INSNS_PER_PEEP2 + 1; ++i) - peep2_insn_data[i].live_before = ALLOC_REG_SET (®_obstack); - live = ALLOC_REG_SET (®_obstack); - -#ifdef HAVE_conditional_execution - blocks = sbitmap_alloc (last_basic_block); - sbitmap_zero (blocks); - changed = false; -#else - count_or_remove_death_notes (NULL, 1); -#endif + peep2_insn_data[i].live_before = BITMAP_ALLOC (®_obstack); + live = BITMAP_ALLOC (®_obstack); FOR_EACH_BB_REVERSE (bb) { - struct propagate_block_info *pbi; - reg_set_iterator rsi; - unsigned int j; - /* Indicate that all slots except the last holds invalid data. */ for (i = 0; i < MAX_INSNS_PER_PEEP2; ++i) peep2_insn_data[i].insn = NULL_RTX; @@ -3103,14 +2906,9 @@ peephole2_optimize (void) peep2_current = MAX_INSNS_PER_PEEP2; /* Start up propagation. */ - COPY_REG_SET (live, bb->il.rtl->global_live_at_end); - COPY_REG_SET (peep2_insn_data[MAX_INSNS_PER_PEEP2].live_before, live); - -#ifdef HAVE_conditional_execution - pbi = init_propagate_block_info (bb, live, NULL, NULL, 0); -#else - pbi = init_propagate_block_info (bb, live, NULL, NULL, PROP_DEATH_NOTES); -#endif + bitmap_copy (live, DF_LR_OUT (bb)); + df_simulate_artificial_refs_at_end (bb, live); + bitmap_copy (peep2_insn_data[MAX_INSNS_PER_PEEP2].live_before, live); for (insn = BB_END (bb); ; insn = prev) { @@ -3129,7 +2927,7 @@ peephole2_optimize (void) && peep2_insn_data[peep2_current].insn == NULL_RTX) peep2_current_count++; peep2_insn_data[peep2_current].insn = insn; - propagate_one_insn (pbi, insn); + df_simulate_one_insn_backwards (bb, insn, live); COPY_REG_SET (peep2_insn_data[peep2_current].live_before, live); if (RTX_FRAME_RELATED_P (insn)) @@ -3256,10 +3054,6 @@ peephole2_optimize (void) = REG_BR_PROB_BASE - nehe->probability; do_cleanup_cfg |= purge_dead_edges (nfte->dest); -#ifdef HAVE_conditional_execution - SET_BIT (blocks, nfte->dest->index); - changed = true; -#endif bb = nfte->src; eh_edge = nehe; } @@ -3271,14 +3065,6 @@ peephole2_optimize (void) } #ifdef HAVE_conditional_execution - /* With conditional execution, we cannot back up the - live information so easily, since the conditional - death data structures are not so self-contained. - So record that we've made a modification to this - block and update life information at the end. */ - SET_BIT (blocks, bb->index); - changed = true; - for (i = 0; i < MAX_INSNS_PER_PEEP2 + 1; ++i) peep2_insn_data[i].insn = NULL_RTX; peep2_insn_data[peep2_current].insn = PEEP2_EOB; @@ -3288,7 +3074,7 @@ peephole2_optimize (void) newly created sequence. */ if (++i >= MAX_INSNS_PER_PEEP2 + 1) i = 0; - COPY_REG_SET (live, peep2_insn_data[i].live_before); + bitmap_copy (live, peep2_insn_data[i].live_before); /* Update life information for the new sequence. */ x = try; @@ -3302,16 +3088,14 @@ peephole2_optimize (void) && peep2_insn_data[i].insn == NULL_RTX) peep2_current_count++; peep2_insn_data[i].insn = x; - propagate_one_insn (pbi, x); - COPY_REG_SET (peep2_insn_data[i].live_before, live); + df_insn_rescan (x); + df_simulate_one_insn_backwards (bb, x, live); + bitmap_copy (peep2_insn_data[i].live_before, live); } x = PREV_INSN (x); } while (x != prev); - /* ??? Should verify that LIVE now matches what we - had before the new sequence. */ - peep2_current = i; #endif @@ -3329,44 +3113,13 @@ peephole2_optimize (void) if (insn == BB_HEAD (bb)) break; } - - /* Some peepholes can decide the don't need one or more of their - inputs. If this happens, local life update is not enough. */ - EXECUTE_IF_AND_COMPL_IN_BITMAP (bb->il.rtl->global_live_at_start, live, - 0, j, rsi) - { - do_global_life_update = true; - break; - } - - free_propagate_block_info (pbi); } for (i = 0; i < MAX_INSNS_PER_PEEP2 + 1; ++i) - FREE_REG_SET (peep2_insn_data[i].live_before); - FREE_REG_SET (live); - + BITMAP_FREE (peep2_insn_data[i].live_before); + BITMAP_FREE (live); if (do_rebuild_jump_labels) rebuild_jump_labels (get_insns ()); - - /* If we eliminated EH edges, we may be able to merge blocks. Further, - we've changed global life since exception handlers are no longer - reachable. */ - if (do_cleanup_cfg) - { - cleanup_cfg (0); - do_global_life_update = true; - } - if (do_global_life_update) - update_life_info (0, UPDATE_LIFE_GLOBAL_RM_NOTES, PROP_DEATH_NOTES); -#ifdef HAVE_conditional_execution - else - { - count_or_remove_death_notes (blocks, 1); - update_life_info (blocks, UPDATE_LIFE_LOCAL, PROP_DEATH_NOTES); - } - sbitmap_free (blocks); -#endif } #endif /* HAVE_peephole2 */ @@ -3546,6 +3299,7 @@ struct tree_opt_pass pass_peephole2 = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func, /* todo_flags_finish */ 'z' /* letter */ }; @@ -3553,7 +3307,7 @@ struct tree_opt_pass pass_peephole2 = static unsigned int rest_of_handle_split_all_insns (void) { - split_all_insns (1); + split_all_insns (); return 0; } @@ -3574,27 +3328,26 @@ struct tree_opt_pass pass_split_all_insns = 0 /* letter */ }; -/* The placement of the splitting that we do for shorten_branches - depends on whether regstack is used by the target or not. */ -static bool -gate_do_final_split (void) +static unsigned int +rest_of_handle_split_after_reload (void) { -#if defined (HAVE_ATTR_length) && !defined (STACK_REGS) - return 1; -#else + /* If optimizing, then go ahead and split insns now. */ +#ifndef STACK_REGS + if (optimize > 0) +#endif + split_all_insns (); return 0; -#endif } -struct tree_opt_pass pass_split_for_shorten_branches = +struct tree_opt_pass pass_split_after_reload = { - "split3", /* name */ - gate_do_final_split, /* gate */ - split_all_insns_noflow, /* execute */ + "split2", /* name */ + NULL, /* gate */ + rest_of_handle_split_after_reload, /* execute */ NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ - TV_SHORTEN_BRANCH, /* tv_id */ + 0, /* tv_id */ 0, /* properties_required */ 0, /* properties_provided */ 0, /* properties_destroyed */ @@ -3603,7 +3356,6 @@ struct tree_opt_pass pass_split_for_shorten_branches = 0 /* letter */ }; - static bool gate_handle_split_before_regstack (void) { @@ -3622,15 +3374,88 @@ gate_handle_split_before_regstack (void) #endif } +static unsigned int +rest_of_handle_split_before_regstack (void) +{ + split_all_insns (); + return 0; +} + struct tree_opt_pass pass_split_before_regstack = { - "split2", /* name */ + "split3", /* name */ gate_handle_split_before_regstack, /* gate */ - rest_of_handle_split_all_insns, /* execute */ + rest_of_handle_split_before_regstack, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func, /* todo_flags_finish */ + 0 /* letter */ +}; + +static bool +gate_handle_split_before_sched2 (void) +{ +#ifdef INSN_SCHEDULING + return optimize > 0 && flag_schedule_insns_after_reload; +#else + return 0; +#endif +} + +static unsigned int +rest_of_handle_split_before_sched2 (void) +{ +#ifdef INSN_SCHEDULING + split_all_insns (); +#endif + return 0; +} + +struct tree_opt_pass pass_split_before_sched2 = +{ + "split4", /* name */ + gate_handle_split_before_sched2, /* gate */ + rest_of_handle_split_before_sched2, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_verify_flow | + TODO_dump_func, /* todo_flags_finish */ + 0 /* letter */ +}; + +/* The placement of the splitting that we do for shorten_branches + depends on whether regstack is used by the target or not. */ +static bool +gate_do_final_split (void) +{ +#if defined (HAVE_ATTR_length) && !defined (STACK_REGS) + return 1; +#else + return 0; +#endif +} + +struct tree_opt_pass pass_split_for_shorten_branches = +{ + "split5", /* name */ + gate_do_final_split, /* gate */ + split_all_insns_noflow, /* execute */ NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ - TV_SHORTEN_BRANCH, /* tv_id */ + 0, /* tv_id */ 0, /* properties_required */ 0, /* properties_provided */ 0, /* properties_destroyed */ @@ -3638,3 +3463,5 @@ struct tree_opt_pass pass_split_before_regstack = TODO_dump_func, /* todo_flags_finish */ 0 /* letter */ }; + + diff --git a/gcc/recog.h b/gcc/recog.h index b921b80..cf52cd3 100644 --- a/gcc/recog.h +++ b/gcc/recog.h @@ -1,6 +1,6 @@ /* Declarations for interface to insn recognizer and insn-output.c. - Copyright (C) 1987, 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005 - Free Software Foundation, Inc. + Copyright (C) 1987, 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, + 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -94,7 +94,6 @@ extern int num_changes_pending (void); extern int next_insn_tests_no_inequality (rtx); #endif extern int reg_fits_class_p (rtx, enum reg_class, int, enum machine_mode); -extern rtx *find_single_use (rtx, rtx, rtx *); extern int offsettable_memref_p (rtx); extern int offsettable_nonstrict_memref_p (rtx); diff --git a/gcc/reg-notes.def b/gcc/reg-notes.def index 096b2fb..36891b1 100644 --- a/gcc/reg-notes.def +++ b/gcc/reg-notes.def @@ -1,5 +1,5 @@ /* Register note definitions. - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -143,12 +143,6 @@ REG_NOTE (EH_REGION) /* Used by haifa-sched to save NOTE_INSN notes across scheduling. */ REG_NOTE (SAVE_NOTE) -/* Indicates that this insn (which is part of the prologue) computes a - value which might not be used later, and if so it's OK to delete - the insn. Normally, deleting any insn in the prologue is an error. - At present the parameter is unused and set to (const_int 0). */ -REG_NOTE (MAYBE_DEAD) - /* Indicates that a call does not return. */ REG_NOTE (NORETURN) @@ -164,3 +158,7 @@ REG_NOTE (CROSSING_JUMP) /* This kind of note is generated at each to `setjmp', and similar functions that can return twice. */ REG_NOTE (SETJMP) + +/* This kind of note identifies what libcall id an instruction is part of. */ +REG_NOTE (LIBCALL_ID) + diff --git a/gcc/reg-stack.c b/gcc/reg-stack.c index a859a17..851a440 100644 --- a/gcc/reg-stack.c +++ b/gcc/reg-stack.c @@ -1,6 +1,7 @@ /* Register to Stack convert for GNU compiler. - Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, - 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. This file is part of GCC. @@ -174,6 +175,7 @@ #include "timevar.h" #include "tree-pass.h" #include "target.h" +#include "df.h" #include "vecprim.h" #ifdef STACK_REGS @@ -1346,12 +1348,18 @@ subst_stack_regs_pat (rtx insn, stack regstack, rtx pat) if (STACK_REG_P (*src) && find_regno_note (insn, REG_DEAD, REGNO (*src))) { - emit_pop_insn (insn, regstack, *src, EMIT_AFTER); + /* USEs are ignored for liveness information so USEs of dead + register might happen. */ + if (TEST_HARD_REG_BIT (regstack->reg_set, REGNO (*src))) + emit_pop_insn (insn, regstack, *src, EMIT_AFTER); return control_flow_insn_deleted; } - /* ??? Uninitialized USE should not happen. */ - else - gcc_assert (get_hard_regnum (regstack, *src) != -1); + /* Uninitialized USE might happen for functions returning uninitialized + value. We will properly initialize the USE on the edge to EXIT_BLOCK, + so it is safe to ignore the use here. This is consistent with behaviour + of dataflow analyzer that ignores USE too. (This also imply that + forcingly initializing the register to NaN here would lead to ICE later, + since the REG_DEAD notes are not issued.) */ break; case CLOBBER: @@ -2353,6 +2361,7 @@ change_stack (rtx insn, stack old, stack new, enum emit_where where) { int reg; int update_end = 0; + int i; /* Stack adjustments for the first insn in a block update the current_block's stack_in instead of inserting insns directly. @@ -2377,6 +2386,17 @@ change_stack (rtx insn, stack old, stack new, enum emit_where where) insn = NEXT_INSN (insn); } + /* Initialize partially dead variables. */ + for (i = FIRST_STACK_REG; i < LAST_STACK_REG + 1; i++) + if (TEST_HARD_REG_BIT (new->reg_set, i) + && !TEST_HARD_REG_BIT (old->reg_set, i)) + { + old->reg[++old->top] = i; + SET_HARD_REG_BIT (old->reg_set, i); + emit_insn_before (gen_rtx_SET (VOIDmode, + FP_MODE_REG (i, SFmode), not_a_num), insn); + } + /* Pop any registers that are not needed in the new block. */ /* If the destination block's stack already has a specified layout @@ -2662,6 +2682,12 @@ propagate_stack (edge e) for (reg = 0; reg <= src_stack->top; ++reg) if (TEST_HARD_REG_BIT (dest_stack->reg_set, src_stack->reg[reg])) dest_stack->reg[++dest_stack->top] = src_stack->reg[reg]; + + /* Push in any partially dead values. */ + for (reg = FIRST_STACK_REG; reg < LAST_STACK_REG + 1; reg++) + if (TEST_HARD_REG_BIT (dest_stack->reg_set, reg) + && !TEST_HARD_REG_BIT (src_stack->reg_set, reg)) + dest_stack->reg[++dest_stack->top] = reg; } @@ -2954,6 +2980,7 @@ convert_regs_1 (basic_block block) /* Something failed if the stack lives don't match. If we had malformed asms, we zapped the instruction itself, but that didn't produce the same pattern of register kills as before. */ + gcc_assert (hard_reg_set_equal_p (regstack.reg_set, bi->out_reg_set) || any_malformed_asm); bi->stack_out = regstack; @@ -3082,22 +3109,14 @@ reg_to_stack (void) /* See if there is something to do. Flow analysis is quite expensive so we might save some compilation time. */ for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++) - if (regs_ever_live[i]) + if (df_regs_ever_live_p (i)) break; if (i > LAST_STACK_REG) return false; - /* Ok, floating point instructions exist. If not optimizing, - build the CFG and run life analysis. - Also need to rebuild life when superblock scheduling is done - as it don't update liveness yet. */ - if (!optimize - || ((flag_sched2_use_superblocks || flag_sched2_use_traces) - && flag_schedule_insns_after_reload)) - { - count_or_remove_death_notes (NULL, 1); - life_analysis (PROP_DEATH_NOTES); - } + df_note_add_problem (); + df_analyze (); + mark_dfs_back_edges (); /* Set up block info for each basic block. */ @@ -3120,9 +3139,9 @@ reg_to_stack (void) /* Copy live_at_end and live_at_start into temporaries. */ for (reg = FIRST_STACK_REG; reg <= LAST_STACK_REG; reg++) { - if (REGNO_REG_SET_P (bb->il.rtl->global_live_at_end, reg)) + if (REGNO_REG_SET_P (DF_LR_OUT (bb), reg)) SET_HARD_REG_BIT (bi->out_reg_set, reg); - if (REGNO_REG_SET_P (bb->il.rtl->global_live_at_start, reg)) + if (REGNO_REG_SET_P (DF_LR_IN (bb), reg)) SET_HARD_REG_BIT (bi->stack_in.reg_set, reg); } } @@ -3182,42 +3201,39 @@ gate_handle_stack_regs (void) #endif } +struct tree_opt_pass pass_stack_regs = +{ + "stack", /* name */ + gate_handle_stack_regs, /* gate */ + NULL, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_REG_STACK, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ + 'k' /* letter */ +}; + /* Convert register usage from flat register file usage to a stack register file. */ static unsigned int rest_of_handle_stack_regs (void) { #ifdef STACK_REGS - if (reg_to_stack () && optimize) - { - regstack_completed = 1; - if (cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_POST_REGSTACK - | (flag_crossjumping ? CLEANUP_CROSSJUMP : 0)) - && (flag_reorder_blocks || flag_reorder_blocks_and_partition)) - { - basic_block bb; - - cfg_layout_initialize (0); - - reorder_basic_blocks (); - cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_POST_REGSTACK); - - FOR_EACH_BB (bb) - if (bb->next_bb != EXIT_BLOCK_PTR) - bb->aux = bb->next_bb; - cfg_layout_finalize (); - } - } - else - regstack_completed = 1; + reg_to_stack (); + regstack_completed = 1; #endif return 0; } -struct tree_opt_pass pass_stack_regs = +struct tree_opt_pass pass_stack_regs_run = { "stack", /* name */ - gate_handle_stack_regs, /* gate */ + NULL, /* gate */ rest_of_handle_stack_regs, /* execute */ NULL, /* sub */ NULL, /* next */ @@ -3227,6 +3243,7 @@ struct tree_opt_pass pass_stack_regs = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func | TODO_ggc_collect, /* todo_flags_finish */ 'k' /* letter */ diff --git a/gcc/regclass.c b/gcc/regclass.c index 23fd092..0c86dbf 100644 --- a/gcc/regclass.c +++ b/gcc/regclass.c @@ -48,6 +48,12 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "timevar.h" #include "hashtab.h" #include "target.h" +#include "tree-pass.h" +#include "df.h" + +/* Maximum register number used in this function, plus one. */ + +int max_regno; static void init_reg_sets_1 (void); static void init_reg_autoinc (void); @@ -241,20 +247,6 @@ static char *in_inc_dec; static GTY(()) rtx top_of_stack[MAX_MACHINE_MODE]; -/* Linked list of reg_info structures allocated for reg_n_info array. - Grouping all of the allocated structures together in one lump - means only one call to bzero to clear them, rather than n smaller - calls. */ -struct reg_info_data { - struct reg_info_data *next; /* next set of reg_info structures */ - size_t min_index; /* minimum index # */ - size_t max_index; /* maximum index # */ - char used_p; /* nonzero if this has been used previously */ - reg_info data[1]; /* beginning of the reg_info data */ -}; - -static struct reg_info_data *reg_info_head; - /* No more global register variables may be declared; true once regclass has been initialized. */ @@ -263,6 +255,26 @@ static int no_global_reg_vars = 0; /* Specify number of hard registers given machine mode occupy. */ unsigned char hard_regno_nregs[FIRST_PSEUDO_REGISTER][MAX_MACHINE_MODE]; +/* Given a register bitmap, turn on the bits in a HARD_REG_SET that + correspond to the hard registers, if any, set in that map. This + could be done far more efficiently by having all sorts of special-cases + with moving single words, but probably isn't worth the trouble. */ + +void +reg_set_to_hard_reg_set (HARD_REG_SET *to, bitmap from) +{ + unsigned i; + bitmap_iterator bi; + + EXECUTE_IF_SET_IN_BITMAP (from, 0, i, bi) + { + if (i >= FIRST_PSEUDO_REGISTER) + return; + SET_HARD_REG_BIT (*to, i); + } +} + + /* Function called only once to initialize the above data on reg usage. Once this is done, various switches may override. */ @@ -827,10 +839,6 @@ static struct costs init_cost; static struct reg_pref *reg_pref; -/* Allocated buffers for reg_pref. */ - -static struct reg_pref *reg_pref_buffer; - /* Frequency of executions of current insn. */ static int frequency; @@ -848,7 +856,7 @@ static void record_address_regs (enum machine_mode, rtx, int, enum rtx_code, #ifdef FORBIDDEN_INC_DEC_CLASSES static int auto_inc_dec_reg_p (rtx, enum machine_mode); #endif -static void reg_scan_mark_refs (rtx, rtx, int, unsigned int); +static void reg_scan_mark_refs (rtx, rtx); /* Wrapper around REGNO_OK_FOR_INDEX_P, to allow pseudo registers. */ @@ -896,11 +904,14 @@ reg_alternate_class (int regno) /* Initialize some global data for this pass. */ -void +static unsigned int regclass_init (void) { int i; + if (df) + df_compute_regs_ever_live (true); + init_cost.mem_cost = 10000; for (i = 0; i < N_REG_CLASSES; i++) init_cost.cost[i] = 10000; @@ -911,7 +922,27 @@ regclass_init (void) /* No more global register variables may be declared. */ no_global_reg_vars = 1; + return 1; } + +struct tree_opt_pass pass_regclass_init = +{ + "regclass", /* name */ + NULL, /* gate */ + regclass_init, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ + 'k' /* letter */ +}; + + /* Dump register costs. */ static void @@ -1096,7 +1127,7 @@ init_reg_autoinc (void) for (j = 0; j < FIRST_PSEUDO_REGISTER; j++) if (TEST_HARD_REG_BIT (reg_class_contents[i], j)) { - REGNO (r) = j; + SET_REGNO (r, j); for (m = VOIDmode; (int) m < (int) MAX_MACHINE_MODE; m = (enum machine_mode) ((int) m + 1)) @@ -1137,9 +1168,14 @@ regclass (rtx f, int nregs) rtx insn; int i; int pass; + max_regno = max_reg_num (); init_recog (); + reg_renumber = xmalloc (max_regno * sizeof (short)); + reg_pref = XCNEWVEC (struct reg_pref, max_regno); + memset (reg_renumber, -1, max_regno * sizeof (short)); + costs = XNEWVEC (struct costs, nregs); #ifdef FORBIDDEN_INC_DEC_CLASSES @@ -1197,9 +1233,6 @@ regclass (rtx f, int nregs) `prefclass'. Record in `altclass' the largest register class any of whose registers is better than memory. */ - if (pass == 0) - reg_pref = reg_pref_buffer; - if (dump_file) { dump_regclass (dump_file); @@ -2068,243 +2101,56 @@ auto_inc_dec_reg_p (rtx reg, enum machine_mode mode) } #endif -static short *renumber; -static size_t regno_allocated; -static unsigned int reg_n_max; - -/* Allocate enough space to hold NUM_REGS registers for the tables used for - reg_scan and flow_analysis that are indexed by the register number. If - NEW_P is nonzero, initialize all of the registers, otherwise only - initialize the new registers allocated. The same table is kept from - function to function, only reallocating it when we need more room. If - RENUMBER_P is nonzero, allocate the reg_renumber array also. */ - -void -allocate_reg_info (size_t num_regs, int new_p, int renumber_p) -{ - size_t size_info; - size_t size_renumber; - size_t min = (new_p) ? 0 : reg_n_max; - struct reg_info_data *reg_data; - - if (num_regs > regno_allocated) - { - size_t old_allocated = regno_allocated; - - regno_allocated = num_regs + (num_regs / 20); /* Add some slop space. */ - size_renumber = regno_allocated * sizeof (short); - - if (!reg_n_info) - { - reg_n_info = VEC_alloc (reg_info_p, heap, regno_allocated); - VEC_safe_grow_cleared (reg_info_p, heap, reg_n_info, - regno_allocated); - renumber = xmalloc (size_renumber); - reg_pref_buffer = XNEWVEC (struct reg_pref, regno_allocated); - } - else - { - size_t old_length = VEC_length (reg_info_p, reg_n_info); - if (old_length < regno_allocated) - { - VEC_safe_grow_cleared (reg_info_p, heap, reg_n_info, - regno_allocated); - } - else if (regno_allocated < old_length) - { - VEC_truncate (reg_info_p, reg_n_info, regno_allocated); - } - - if (new_p) /* If we're zapping everything, no need to realloc. */ - { - free ((char *) renumber); - free ((char *) reg_pref); - renumber = xmalloc (size_renumber); - reg_pref_buffer = XNEWVEC (struct reg_pref, regno_allocated); - } - - else - { - renumber = xrealloc (renumber, size_renumber); - reg_pref_buffer = (struct reg_pref *) xrealloc (reg_pref_buffer, - regno_allocated - * sizeof (struct reg_pref)); - } - } - - size_info = (regno_allocated - old_allocated) * sizeof (reg_info) - + sizeof (struct reg_info_data) - sizeof (reg_info); - reg_data = xcalloc (size_info, 1); - reg_data->min_index = old_allocated; - reg_data->max_index = regno_allocated - 1; - reg_data->next = reg_info_head; - reg_info_head = reg_data; - } - - reg_n_max = num_regs; - if (min < num_regs) - { - /* Loop through each of the segments allocated for the actual - reg_info pages, and set up the pointers, zero the pages, etc. */ - for (reg_data = reg_info_head; - reg_data && reg_data->max_index >= min; - reg_data = reg_data->next) - { - size_t min_index = reg_data->min_index; - size_t max_index = reg_data->max_index; - size_t max = MIN (max_index, num_regs); - size_t local_min = min - min_index; - size_t i; - - if (reg_data->min_index > num_regs) - continue; - - if (min < min_index) - local_min = 0; - if (!reg_data->used_p) /* page just allocated with calloc */ - reg_data->used_p = 1; /* no need to zero */ - else - memset (®_data->data[local_min], 0, - sizeof (reg_info) * (max - min_index - local_min + 1)); - - for (i = min_index+local_min; i <= max; i++) - { - VEC_replace (reg_info_p, reg_n_info, i, - ®_data->data[i-min_index]); - REG_BASIC_BLOCK (i) = REG_BLOCK_UNKNOWN; - renumber[i] = -1; - reg_pref_buffer[i].prefclass = (char) NO_REGS; - reg_pref_buffer[i].altclass = (char) NO_REGS; - } - } - } - - /* If {pref,alt}class have already been allocated, update the pointers to - the newly realloced ones. */ - if (reg_pref) - reg_pref = reg_pref_buffer; - - if (renumber_p) - reg_renumber = renumber; -} - /* Free up the space allocated by allocate_reg_info. */ void free_reg_info (void) { - if (reg_n_info) + if (reg_pref) { - struct reg_info_data *reg_data; - struct reg_info_data *reg_next; - - VEC_free (reg_info_p, heap, reg_n_info); - for (reg_data = reg_info_head; reg_data; reg_data = reg_next) - { - reg_next = reg_data->next; - free ((char *) reg_data); - } + free (reg_pref); + reg_pref = NULL; + } - free (reg_pref_buffer); - reg_pref_buffer = (struct reg_pref *) 0; - reg_info_head = (struct reg_info_data *) 0; - renumber = (short *) 0; + if (reg_renumber) + { + free (reg_renumber); + reg_renumber = NULL; } - regno_allocated = 0; - reg_n_max = 0; } -/* Clear the information stored for REGNO. */ -void -clear_reg_info_regno (unsigned int regno) -{ - if (regno < regno_allocated) - memset (VEC_index (reg_info_p, reg_n_info, regno), 0, sizeof (reg_info)); -} -/* This is the `regscan' pass of the compiler, run just before cse - and again just before loop. - - It finds the first and last use of each pseudo-register - and records them in the vectors regno_first_uid, regno_last_uid - and counts the number of sets in the vector reg_n_sets. - - REPEAT is nonzero the second time this is called. */ - -/* Maximum number of parallel sets and clobbers in any insn in this fn. - Always at least 3, since the combiner could put that many together - and we want this to remain correct for all the remaining passes. - This corresponds to the maximum number of times note_stores will call - a function for any insn. */ - -int max_parallel; - -/* Used as a temporary to record the largest number of registers in - PARALLEL in a SET_DEST. This is added to max_parallel. */ - -static int max_set_parallel; +/* This is the `regscan' pass of the compiler, run just before cse and + again just before loop. It finds the first and last use of each + pseudo-register. */ void -reg_scan (rtx f, unsigned int nregs) +reg_scan (rtx f, unsigned int nregs ATTRIBUTE_UNUSED) { rtx insn; timevar_push (TV_REG_SCAN); - allocate_reg_info (nregs, TRUE, FALSE); - max_parallel = 3; - max_set_parallel = 0; - for (insn = f; insn; insn = NEXT_INSN (insn)) if (INSN_P (insn)) { - rtx pat = PATTERN (insn); - if (GET_CODE (pat) == PARALLEL - && XVECLEN (pat, 0) > max_parallel) - max_parallel = XVECLEN (pat, 0); - reg_scan_mark_refs (pat, insn, 0, 0); - + reg_scan_mark_refs (PATTERN (insn), insn); if (REG_NOTES (insn)) - reg_scan_mark_refs (REG_NOTES (insn), insn, 1, 0); + reg_scan_mark_refs (REG_NOTES (insn), insn); } - max_parallel += max_set_parallel; - timevar_pop (TV_REG_SCAN); } -/* Update 'regscan' information by looking at the insns - from FIRST to LAST. Some new REGs have been created, - and any REG with number greater than OLD_MAX_REGNO is - such a REG. We only update information for those. */ - -void -reg_scan_update (rtx first, rtx last, unsigned int old_max_regno) -{ - rtx insn; - - allocate_reg_info (max_reg_num (), FALSE, FALSE); - - for (insn = first; insn != last; insn = NEXT_INSN (insn)) - if (INSN_P (insn)) - { - rtx pat = PATTERN (insn); - if (GET_CODE (pat) == PARALLEL - && XVECLEN (pat, 0) > max_parallel) - max_parallel = XVECLEN (pat, 0); - reg_scan_mark_refs (pat, insn, 0, old_max_regno); - - if (REG_NOTES (insn)) - reg_scan_mark_refs (REG_NOTES (insn), insn, 1, old_max_regno); - } -} /* X is the expression to scan. INSN is the insn it appears in. NOTE_FLAG is nonzero if X is from INSN's notes rather than its body. We should only record information for REGs with numbers greater than or equal to MIN_REGNO. */ +extern struct tree_opt_pass *current_pass; + static void -reg_scan_mark_refs (rtx x, rtx insn, int note_flag, unsigned int min_regno) +reg_scan_mark_refs (rtx x, rtx insn) { enum rtx_code code; rtx dest; @@ -2325,50 +2171,24 @@ reg_scan_mark_refs (rtx x, rtx insn, int note_flag, unsigned int min_regno) case LABEL_REF: case ADDR_VEC: case ADDR_DIFF_VEC: - return; - case REG: - { - unsigned int regno = REGNO (x); - - if (regno >= min_regno) - { - if (!note_flag) - REGNO_LAST_UID (regno) = INSN_UID (insn); - if (REGNO_FIRST_UID (regno) == 0) - REGNO_FIRST_UID (regno) = INSN_UID (insn); - /* If we are called by reg_scan_update() (indicated by min_regno - being set), we also need to update the reference count. */ - if (min_regno) - REG_N_REFS (regno)++; - } - } - break; + return; case EXPR_LIST: if (XEXP (x, 0)) - reg_scan_mark_refs (XEXP (x, 0), insn, note_flag, min_regno); + reg_scan_mark_refs (XEXP (x, 0), insn); if (XEXP (x, 1)) - reg_scan_mark_refs (XEXP (x, 1), insn, note_flag, min_regno); + reg_scan_mark_refs (XEXP (x, 1), insn); break; case INSN_LIST: if (XEXP (x, 1)) - reg_scan_mark_refs (XEXP (x, 1), insn, note_flag, min_regno); + reg_scan_mark_refs (XEXP (x, 1), insn); break; case CLOBBER: - { - rtx reg = XEXP (x, 0); - if (REG_P (reg) - && REGNO (reg) >= min_regno) - { - REG_N_SETS (REGNO (reg))++; - REG_N_REFS (REGNO (reg))++; - } - else if (MEM_P (reg)) - reg_scan_mark_refs (XEXP (reg, 0), insn, note_flag, min_regno); - } + if (MEM_P (XEXP (x, 0))) + reg_scan_mark_refs (XEXP (XEXP (x, 0), 0), insn); break; case SET: @@ -2379,18 +2199,6 @@ reg_scan_mark_refs (rtx x, rtx insn, int note_flag, unsigned int min_regno) dest = XEXP (dest, 0)) ; - /* For a PARALLEL, record the number of things (less the usual one for a - SET) that are set. */ - if (GET_CODE (dest) == PARALLEL) - max_set_parallel = MAX (max_set_parallel, XVECLEN (dest, 0) - 1); - - if (REG_P (dest) - && REGNO (dest) >= min_regno) - { - REG_N_SETS (REGNO (dest))++; - REG_N_REFS (REGNO (dest))++; - } - /* If this is setting a pseudo from another pseudo or the sum of a pseudo and a constant integer and the other pseudo is known to be a pointer, set the destination to be a pointer as well. @@ -2405,13 +2213,12 @@ reg_scan_mark_refs (rtx x, rtx insn, int note_flag, unsigned int min_regno) if (REG_P (SET_DEST (x)) && REGNO (SET_DEST (x)) >= FIRST_PSEUDO_REGISTER - && REGNO (SET_DEST (x)) >= min_regno /* If the destination pseudo is set more than once, then other sets might not be to a pointer value (consider access to a union in two threads of control in the presence of global optimizations). So only set REG_POINTER on the destination pseudo if this is the only set of that pseudo. */ - && REG_N_SETS (REGNO (SET_DEST (x))) == 1 + && DF_REG_DEF_COUNT (REGNO (SET_DEST (x))) == 1 && ! REG_USERVAR_P (SET_DEST (x)) && ! REG_POINTER (SET_DEST (x)) && ((REG_P (SET_SRC (x)) @@ -2441,7 +2248,7 @@ reg_scan_mark_refs (rtx x, rtx insn, int note_flag, unsigned int min_regno) /* If this is setting a register from a register or from a simple conversion of a register, propagate REG_EXPR. */ - if (REG_P (dest)) + if (REG_P (dest) && !REG_ATTRS (dest)) { rtx src = SET_SRC (x); @@ -2451,9 +2258,9 @@ reg_scan_mark_refs (rtx x, rtx insn, int note_flag, unsigned int min_regno) || (GET_CODE (src) == SUBREG && subreg_lowpart_p (src))) src = XEXP (src, 0); - if (!REG_ATTRS (dest) && REG_P (src)) + if (REG_P (src)) REG_ATTRS (dest) = REG_ATTRS (src); - if (!REG_ATTRS (dest) && MEM_P (src)) + if (MEM_P (src)) set_reg_attrs_from_mem (dest, src); } @@ -2466,12 +2273,12 @@ reg_scan_mark_refs (rtx x, rtx insn, int note_flag, unsigned int min_regno) for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { if (fmt[i] == 'e') - reg_scan_mark_refs (XEXP (x, i), insn, note_flag, min_regno); + reg_scan_mark_refs (XEXP (x, i), insn); else if (fmt[i] == 'E' && XVEC (x, i) != 0) { int j; for (j = XVECLEN (x, i) - 1; j >= 0; j--) - reg_scan_mark_refs (XVECEXP (x, i, j), insn, note_flag, min_regno); + reg_scan_mark_refs (XVECEXP (x, i, j), insn); } } } @@ -2527,16 +2334,8 @@ som_eq (const void *x, const void *y) return a->block == b->block; } -void -init_subregs_of_mode (void) -{ - if (subregs_of_mode) - htab_empty (subregs_of_mode); - else - subregs_of_mode = htab_create (100, som_hash, som_eq, free); -} -void +static void record_subregs_of_mode (rtx subreg) { struct subregs_of_mode_node dummy, *node; @@ -2567,6 +2366,53 @@ record_subregs_of_mode (rtx subreg) node->modes[mode] |= 1 << (regno & 7); } + +/* Call record_subregs_of_mode for all the subregs in X. */ + +static void +find_subregs_of_mode (rtx x) +{ + enum rtx_code code = GET_CODE (x); + const char * const fmt = GET_RTX_FORMAT (code); + int i; + + if (code == SUBREG) + record_subregs_of_mode (x); + + /* Time for some deep diving. */ + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + find_subregs_of_mode (XEXP (x, i)); + else if (fmt[i] == 'E') + { + int j; + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + find_subregs_of_mode (XVECEXP (x, i, j)); + } + } +} + +static unsigned int +init_subregs_of_mode (void) +{ + basic_block bb; + rtx insn; + + if (subregs_of_mode) + htab_empty (subregs_of_mode); + else + subregs_of_mode = htab_create (100, som_hash, som_eq, free); + + FOR_EACH_BB (bb) + FOR_BB_INSNS (bb, insn) + if (INSN_P (insn)) + find_subregs_of_mode (PATTERN (insn)); + + return 0; +} + + /* Set bits in *USED which correspond to registers which can't change their mode from FROM to any mode in which REGNO was encountered. */ @@ -2579,6 +2425,7 @@ cannot_change_mode_set_regs (HARD_REG_SET *used, enum machine_mode from, unsigned char mask; unsigned int i; + gcc_assert (subregs_of_mode); dummy.block = regno & -8; node = htab_find_with_hash (subregs_of_mode, &dummy, dummy.block); if (node == NULL) @@ -2604,6 +2451,7 @@ invalid_mode_change_p (unsigned int regno, enum reg_class class, enum machine_mode to; unsigned char mask; + gcc_assert (subregs_of_mode); dummy.block = regno & -8; node = htab_find_with_hash (subregs_of_mode, &dummy, dummy.block); if (node == NULL) @@ -2617,6 +2465,72 @@ invalid_mode_change_p (unsigned int regno, enum reg_class class, return false; } + +static unsigned int +finish_subregs_of_mode (void) +{ + htab_delete (subregs_of_mode); + subregs_of_mode = 0; + return 0; +} +#else +static unsigned int +init_subregs_of_mode (void) +{ + return 0; +} +static unsigned int +finish_subregs_of_mode (void) +{ + return 0; +} + #endif /* CANNOT_CHANGE_MODE_CLASS */ +static bool +gate_subregs_of_mode_init (void) +{ +#ifdef CANNOT_CHANGE_MODE_CLASS + return true; +#else + return false; +#endif +} + +struct tree_opt_pass pass_subregs_of_mode_init = +{ + "subregs_of_mode_init", /* name */ + gate_subregs_of_mode_init, /* gate */ + init_subregs_of_mode, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ + 0 /* letter */ +}; + +struct tree_opt_pass pass_subregs_of_mode_finish = +{ + "subregs_of_mode_finish", /* name */ + gate_subregs_of_mode_init, /* gate */ + finish_subregs_of_mode, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ + 0 /* letter */ +}; + + + #include "gt-regclass.h" diff --git a/gcc/regmove.c b/gcc/regmove.c index 05effa6..c54879bc 100644 --- a/gcc/regmove.c +++ b/gcc/regmove.c @@ -1,6 +1,7 @@ /* Move registers around to reduce number of move instructions needed. - Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, + 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. This file is part of GCC. @@ -45,7 +46,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "reload.h" #include "timevar.h" #include "tree-pass.h" - +#include "df.h" static int perhaps_ends_bb_p (rtx); static int optimize_reg_copy_1 (rtx, rtx, rtx); @@ -85,6 +86,72 @@ regclass_compatible_p (int class0, int class1) && ! CLASS_LIKELY_SPILLED_P (class1))); } +/* Find the place in the rtx X where REG is used as a memory address. + Return the MEM rtx that so uses it. + If PLUSCONST is nonzero, search instead for a memory address equivalent to + (plus REG (const_int PLUSCONST)). + + If such an address does not appear, return 0. + If REG appears more than once, or is used other than in such an address, + return (rtx) 1. */ + +static rtx +find_use_as_address (rtx x, rtx reg, HOST_WIDE_INT plusconst) +{ + enum rtx_code code = GET_CODE (x); + const char * const fmt = GET_RTX_FORMAT (code); + int i; + rtx value = 0; + rtx tem; + + if (code == MEM && XEXP (x, 0) == reg && plusconst == 0) + return x; + + if (code == MEM && GET_CODE (XEXP (x, 0)) == PLUS + && XEXP (XEXP (x, 0), 0) == reg + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && INTVAL (XEXP (XEXP (x, 0), 1)) == plusconst) + return x; + + if (code == SIGN_EXTRACT || code == ZERO_EXTRACT) + { + /* If REG occurs inside a MEM used in a bit-field reference, + that is unacceptable. */ + if (find_use_as_address (XEXP (x, 0), reg, 0) != 0) + return (rtx) (size_t) 1; + } + + if (x == reg) + return (rtx) (size_t) 1; + + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + tem = find_use_as_address (XEXP (x, i), reg, plusconst); + if (value == 0) + value = tem; + else if (tem != 0) + return (rtx) (size_t) 1; + } + else if (fmt[i] == 'E') + { + int j; + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + { + tem = find_use_as_address (XVECEXP (x, i, j), reg, plusconst); + if (value == 0) + value = tem; + else if (tem != 0) + return (rtx) (size_t) 1; + } + } + } + + return value; +} + + /* INC_INSN is an instruction that adds INCREMENT to REG. Try to fold INC_INSN as a post/pre in/decrement into INSN. Iff INC_INSN_SET is nonzero, inc_insn has a destination different from src. @@ -258,8 +325,7 @@ mark_flags_life_zones (rtx flags) { int i; for (i = 0; i < flags_nregs; ++i) - live |= REGNO_REG_SET_P (block->il.rtl->global_live_at_start, - flags_regno + i); + live |= REGNO_REG_SET_P (DF_LIVE_IN (block), flags_regno + i); } #endif @@ -612,7 +678,10 @@ optimize_reg_copy_2 (rtx insn, rtx dest, rtx src) if (INSN_P (q)) { if (reg_mentioned_p (dest, PATTERN (q))) - PATTERN (q) = replace_rtx (PATTERN (q), dest, src); + { + PATTERN (q) = replace_rtx (PATTERN (q), dest, src); + df_insn_rescan (q); + } if (CALL_P (q)) { @@ -801,20 +870,11 @@ copy_src_to_dest (rtx insn, rtx src, rtx dest) /* Update the various register tables. */ dest_regno = REGNO (dest); - REG_N_SETS (dest_regno) ++; + INC_REG_N_SETS (dest_regno, 1); REG_LIVE_LENGTH (dest_regno)++; - if (REGNO_FIRST_UID (dest_regno) == insn_uid) - REGNO_FIRST_UID (dest_regno) = move_uid; - src_regno = REGNO (src); if (! find_reg_note (move_insn, REG_DEAD, src)) REG_LIVE_LENGTH (src_regno)++; - - if (REGNO_FIRST_UID (src_regno) == insn_uid) - REGNO_FIRST_UID (src_regno) = move_uid; - - if (REGNO_LAST_UID (src_regno) == insn_uid) - REGNO_LAST_UID (src_regno) = move_uid; } } @@ -1026,6 +1086,12 @@ regmove_optimize (rtx f, int nregs) if (flag_non_call_exceptions) return; + df_note_add_problem (); + df_analyze (); + + regstat_init_n_sets_and_refs (); + regstat_compute_ri (); + /* Find out where a potential flags register is live, and so that we can suppress some optimizations in those zones. */ mark_flags_life_zones (discover_flags_reg ()); @@ -1427,8 +1493,8 @@ regmove_optimize (rtx f, int nregs) dstno = REGNO (dst); srcno = REGNO (src); - REG_N_SETS (dstno)++; - REG_N_SETS (srcno)--; + INC_REG_N_SETS (dstno, 1); + INC_REG_N_SETS (srcno, -1); REG_N_CALLS_CROSSED (dstno) += num_calls; REG_N_CALLS_CROSSED (srcno) -= num_calls; @@ -1457,7 +1523,6 @@ regmove_optimize (rtx f, int nregs) alternative approach of copying the source to the destination. */ if (!success && copy_src != NULL_RTX) copy_src_to_dest (insn, copy_src, copy_dst); - } } @@ -1469,6 +1534,8 @@ regmove_optimize (rtx f, int nregs) free (reg_set_in_bb); reg_set_in_bb = NULL; } + regstat_free_n_sets_and_refs (); + regstat_free_ri (); } /* Returns nonzero if INSN's pattern has matching constraints for any operand. @@ -1823,7 +1890,7 @@ fixup_match_1 (rtx insn, rtx set, rtx src, rtx src_subreg, rtx dst, && try_auto_increment (search_end, post_inc, 0, src, newconst, 1)) post_inc = 0; validate_change (insn, &XEXP (SET_SRC (set), 1), GEN_INT (insn_const), 0); - REG_N_SETS (REGNO (src))++; + INC_REG_N_SETS (REGNO (src), 1); REG_LIVE_LENGTH (REGNO (src))++; } if (overlap) @@ -1844,6 +1911,7 @@ fixup_match_1 (rtx insn, rtx set, rtx src, rtx src_subreg, rtx dst, p = emit_insn_after_setloc (pat, PREV_INSN (p), INSN_LOCATOR (insn)); delete_insn (insn); REG_NOTES (p) = notes; + df_notes_rescan (p); } } /* Sometimes we'd generate src = const; src += n; @@ -1889,7 +1957,7 @@ fixup_match_1 (rtx insn, rtx set, rtx src, rtx src_subreg, rtx dst, && validate_change (insn, &SET_SRC (set), XEXP (note, 0), 0)) { delete_insn (q); - REG_N_SETS (REGNO (src))--; + INC_REG_N_SETS (REGNO (src), -1); REG_N_CALLS_CROSSED (REGNO (src)) -= num_calls2; REG_LIVE_LENGTH (REGNO (src)) -= s_length2; insn_const = 0; @@ -1961,8 +2029,8 @@ fixup_match_1 (rtx insn, rtx set, rtx src, rtx src_subreg, rtx dst, REG_N_CALLS_CROSSED (REGNO (src)) += s_num_calls; } - REG_N_SETS (REGNO (src))++; - REG_N_SETS (REGNO (dst))--; + INC_REG_N_SETS (REGNO (src), 1); + INC_REG_N_SETS (REGNO (dst), -1); REG_N_CALLS_CROSSED (REGNO (dst)) -= num_calls; @@ -2038,7 +2106,6 @@ static unsigned int rest_of_handle_regmove (void) { regmove_optimize (get_insns (), max_reg_num ()); - cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_UPDATE_LIFE); return 0; } @@ -2055,6 +2122,7 @@ struct tree_opt_pass pass_regmove = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func | TODO_ggc_collect, /* todo_flags_finish */ 'N' /* letter */ diff --git a/gcc/regrename.c b/gcc/regrename.c index 6cabe43..7026ef3 100644 --- a/gcc/regrename.c +++ b/gcc/regrename.c @@ -1,5 +1,5 @@ /* Register renaming for the GNU compiler. - Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -39,6 +39,7 @@ #include "obstack.h" #include "timevar.h" #include "tree-pass.h" +#include "df.h" struct du_chain { @@ -93,7 +94,7 @@ static void clear_dead_regs (HARD_REG_SET *, enum machine_mode, rtx); static void merge_overlapping_regs (basic_block, HARD_REG_SET *, struct du_chain *); -/* Called through note_stores from update_life. Find sets of registers, and +/* Called through note_stores. Find sets of registers, and record them in *DATA (which is actually a HARD_REG_SET *). */ static void @@ -138,7 +139,7 @@ merge_overlapping_regs (basic_block b, HARD_REG_SET *pset, rtx insn; HARD_REG_SET live; - REG_SET_TO_HARD_REG_SET (live, b->il.rtl->global_live_at_start); + REG_SET_TO_HARD_REG_SET (live, DF_LIVE_IN (b)); insn = BB_HEAD (b); while (t) { @@ -181,6 +182,11 @@ regrename_optimize (void) basic_block bb; char *first_obj; + df_set_flags (DF_LR_RUN_DCE); + df_note_add_problem (); + df_analyze (); + df_set_flags (DF_NO_INSN_RESCAN); + memset (tick, 0, sizeof tick); gcc_obstack_init (&rename_obstack); @@ -279,7 +285,7 @@ regrename_optimize (void) || fixed_regs[new_reg + i] || global_regs[new_reg + i] /* Can't use regs which aren't saved by the prologue. */ - || (! regs_ever_live[new_reg + i] + || (! df_regs_ever_live_p (new_reg + i) && ! call_used_regs[new_reg + i]) #ifdef LEAF_REGISTERS /* We can't use a non-leaf register if we're in a @@ -330,7 +336,7 @@ regrename_optimize (void) do_replace (this, best_new_reg); tick[best_new_reg] = ++this_tick; - regs_ever_live[best_new_reg] = 1; + df_set_regs_ever_live (best_new_reg, true); if (dump_file) fprintf (dump_file, ", renamed as %s\n", reg_names[best_new_reg]); @@ -340,13 +346,11 @@ regrename_optimize (void) } obstack_free (&rename_obstack, NULL); + df_clear_flags (DF_NO_INSN_RESCAN); + df_insn_rescan_all (); if (dump_file) fputc ('\n', dump_file); - - count_or_remove_death_notes (NULL, 1); - update_life_info (NULL, UPDATE_LIFE_LOCAL, - PROP_DEATH_NOTES); } static void @@ -1794,12 +1798,9 @@ static void copyprop_hardreg_forward (void) { struct value_data *all_vd; - bool need_refresh; basic_block bb; sbitmap visited; - need_refresh = false; - all_vd = XNEWVEC (struct value_data, last_basic_block); visited = sbitmap_alloc (last_basic_block); @@ -1820,27 +1821,10 @@ copyprop_hardreg_forward (void) else init_value_data (all_vd + bb->index); - if (copyprop_hardreg_forward_1 (bb, all_vd + bb->index)) - need_refresh = true; + copyprop_hardreg_forward_1 (bb, all_vd + bb->index); } sbitmap_free (visited); - - if (need_refresh) - { - if (dump_file) - fputs ("\n\n", dump_file); - - /* ??? Irritatingly, delete_noop_moves does not take a set of blocks - to scan, so we have to do a life update with no initial set of - blocks Just In Case. */ - delete_noop_moves (); - update_life_info (NULL, UPDATE_LIFE_GLOBAL_RM_NOTES, - PROP_DEATH_NOTES - | PROP_SCAN_DEAD_CODE - | PROP_KILL_DEAD_CODE); - } - free (all_vd); } @@ -1951,7 +1935,7 @@ validate_value_data (struct value_data *vd) static bool gate_handle_regrename (void) { - return (optimize > 0 && (flag_rename_registers || flag_cprop_registers)); + return (optimize > 0 && (flag_rename_registers)); } @@ -1959,10 +1943,7 @@ gate_handle_regrename (void) static unsigned int rest_of_handle_regrename (void) { - if (flag_rename_registers) - regrename_optimize (); - if (flag_cprop_registers) - copyprop_hardreg_forward (); + regrename_optimize (); return 0; } @@ -1979,6 +1960,39 @@ struct tree_opt_pass pass_regrename = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | + TODO_dump_func, /* todo_flags_finish */ + 'n' /* letter */ +}; + +static bool +gate_handle_cprop (void) +{ + return (optimize > 0 && (flag_cprop_registers)); +} + + +/* Run the regrename and cprop passes. */ +static unsigned int +rest_of_handle_cprop (void) +{ + copyprop_hardreg_forward (); + return 0; +} + +struct tree_opt_pass pass_cprop_hardreg = +{ + "cprop_hardreg", /* name */ + gate_handle_cprop, /* gate */ + rest_of_handle_cprop, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_RENAME_REGISTERS, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ TODO_dump_func, /* todo_flags_finish */ 'n' /* letter */ }; @@ -46,39 +46,88 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA extern int max_regno; -/* Register information indexed by register number */ -typedef struct reg_info_def -{ /* fields set by reg_scan */ - int first_uid; /* UID of first insn to use (REG n) */ - int last_uid; /* UID of last insn to use (REG n) */ +/* REG_N_REFS and REG_N_SETS are initialized by a call to + regstat_init_n_sets_and_refs from the current values of + DF_REG_DEF_COUNT and DF_REG_USE_COUNT. REG_N_REFS and REG_N_SETS + should only be used if a pass need to change these values in some + magical way or or the pass needs to have accurate values for these + and is not using incremental df scanning. + + At the end of a pass that uses REG_N_REFS and REG_N_SETS, a call + should be made to regstat_free_n_sets_and_refs. + + Local alloc seems to play pretty loose with these values. + REG_N_REFS is set to 0 if the register is used in an asm. + Furthermore, local_alloc calls regclass to hack both REG_N_REFS and + REG_N_SETS for three address insns. Other passes seem to have + other special values. */ - /* fields set by reg_scan & flow_analysis */ - int sets; /* # of times (REG n) is set */ - /* fields set by flow_analysis */ + +/* Structure to hold values for REG_N_SETS (i) and REG_N_REFS (i). */ + +struct regstat_n_sets_and_refs_t +{ + int sets; /* # of times (REG n) is set */ int refs; /* # of times (REG n) is used or set */ +}; + +extern struct regstat_n_sets_and_refs_t *regstat_n_sets_and_refs; + +/* Indexed by n, gives number of times (REG n) is used or set. */ +static inline int +REG_N_REFS(int regno) +{ + return regstat_n_sets_and_refs[regno].refs; +} + +/* Indexed by n, gives number of times (REG n) is used or set. */ +#define SET_REG_N_REFS(N,V) (regstat_n_sets_and_refs[N].refs = V) +#define INC_REG_N_REFS(N,V) (regstat_n_sets_and_refs[N].refs += V) + +/* Indexed by n, gives number of times (REG n) is set. */ +static inline int +REG_N_SETS (int regno) +{ + return regstat_n_sets_and_refs[regno].sets; +} + +/* Indexed by n, gives number of times (REG n) is set. */ +#define SET_REG_N_SETS(N,V) (regstat_n_sets_and_refs[N].sets = V) +#define INC_REG_N_SETS(N,V) (regstat_n_sets_and_refs[N].sets += V) + + +/* Functions defined in reg-stat.c. */ +extern void regstat_init_n_sets_and_refs (void); +extern void regstat_free_n_sets_and_refs (void); +extern void regstat_compute_ri (void); +extern void regstat_free_ri (void); +extern bitmap regstat_get_setjmp_crosses (void); +extern void regstat_compute_calls_crossed (void); +extern void regstat_free_calls_crossed (void); + + +/* Register information indexed by register number. This structure is + initialized by calling regstat_compute_ri and is destroyed by + calling regstat_free_ri. */ +struct reg_info_t +{ int freq; /* # estimated frequency (REG n) is used or set */ int deaths; /* # of times (REG n) dies */ int live_length; /* # of instructions (REG n) is live */ int calls_crossed; /* # of calls (REG n) is live across */ int throw_calls_crossed; /* # of calls that may throw (REG n) is live across */ int basic_block; /* # of basic blocks (REG n) is used in */ -} reg_info; - -typedef reg_info *reg_info_p; - -DEF_VEC_P(reg_info_p); -DEF_VEC_ALLOC_P(reg_info_p,heap); +}; -extern VEC(reg_info_p,heap) *reg_n_info; +extern struct reg_info_t *reg_info_p; -/* Indexed by n, gives number of times (REG n) is used or set. */ - -#define REG_N_REFS(N) (VEC_index (reg_info_p, reg_n_info, N)->refs) +/* The number allocated elements of reg_info_p. */ +extern size_t reg_info_p_size; /* Estimate frequency of references to register N. */ -#define REG_FREQ(N) (VEC_index (reg_info_p, reg_n_info, N)->freq) +#define REG_FREQ(N) (reg_info_p[N].freq) /* The weights for each insn varries from 0 to REG_FREQ_BASE. This constant does not need to be high, as in infrequently executed @@ -98,19 +147,13 @@ extern VEC(reg_info_p,heap) *reg_n_info; ? ((bb)->frequency * REG_FREQ_MAX / BB_FREQ_MAX)\ : 1) -/* Indexed by n, gives number of times (REG n) is set. - ??? both regscan and flow allocate space for this. We should settle - on just copy. */ - -#define REG_N_SETS(N) (VEC_index (reg_info_p, reg_n_info, N)->sets) - /* Indexed by N, gives number of insns in which register N dies. Note that if register N is live around loops, it can die in transitions between basic blocks, and that is not counted here. So this is only a reliable indicator of how many regions of life there are for registers that are contained in one basic block. */ -#define REG_N_DEATHS(N) (VEC_index (reg_info_p, reg_n_info, N)->deaths) +#define REG_N_DEATHS(N) (reg_info_p[N].deaths) /* Get the number of consecutive words required to hold pseudo-reg N. */ @@ -129,20 +172,19 @@ extern VEC(reg_info_p,heap) *reg_n_info; /* Indexed by N, gives number of CALL_INSNS across which (REG n) is live. */ -#define REG_N_CALLS_CROSSED(N) \ - (VEC_index (reg_info_p, reg_n_info, N)->calls_crossed) +#define REG_N_CALLS_CROSSED(N) (reg_info_p[N].calls_crossed) /* Indexed by N, gives number of CALL_INSNS that may throw, across which (REG n) is live. */ -#define REG_N_THROWING_CALLS_CROSSED(N) \ - (VEC_index (reg_info_p, reg_n_info, N)->throw_calls_crossed) +#define REG_N_THROWING_CALLS_CROSSED(N) (reg_info_p[N].throw_calls_crossed) -/* Total number of instructions at which (REG n) is live. - The larger this is, the less priority (REG n) gets for - allocation in a hard register (in global-alloc). - This is set in flow.c and remains valid for the rest of the compilation - of the function; it is used to control register allocation. +/* Total number of instructions at which (REG n) is live. The larger + this is, the less priority (REG n) gets for allocation in a hard + register (in global-alloc). This is set in df-problems.c whenever + register info is requested and remains valid for the rest of the + compilation of the function; it is used to control register + allocation. local-alloc.c may alter this number to change the priority. @@ -153,8 +195,19 @@ extern VEC(reg_info_p,heap) *reg_n_info; is not required. global.c makes an allocno for this but does not try to assign a hard register to it. */ -#define REG_LIVE_LENGTH(N) \ - (VEC_index (reg_info_p, reg_n_info, N)->live_length) +#define REG_LIVE_LENGTH(N) (reg_info_p[N].live_length) + +/* Indexed by n, gives number of basic block that (REG n) is used in. + If the value is REG_BLOCK_GLOBAL (-1), + it means (REG n) is used in more than one basic block. + REG_BLOCK_UNKNOWN (0) means it hasn't been seen yet so we don't know. + This information remains valid for the rest of the compilation + of the current function; it is used to control register allocation. */ + +#define REG_BLOCK_UNKNOWN 0 +#define REG_BLOCK_GLOBAL -1 + +#define REG_BASIC_BLOCK(N) (reg_info_p[N].basic_block) /* Vector of substitutions of register numbers, used to map pseudo regs into hardware regs. @@ -165,14 +218,6 @@ extern VEC(reg_info_p,heap) *reg_n_info; extern short *reg_renumber; -/* Vector indexed by hardware reg saying whether that reg is ever used. */ - -extern char regs_ever_live[FIRST_PSEUDO_REGISTER]; - -/* Like regs_ever_live, but saying whether reg is set by asm statements. */ - -extern char regs_asm_clobbered[FIRST_PSEUDO_REGISTER]; - /* Vector indexed by machine mode saying whether there are regs of that mode. */ extern bool have_regs_of_mode [MAX_MACHINE_MODE]; @@ -184,25 +229,6 @@ extern bool have_regs_of_mode [MAX_MACHINE_MODE]; extern enum machine_mode reg_raw_mode[FIRST_PSEUDO_REGISTER]; -/* Vector indexed by regno; gives uid of first insn using that reg. - This is computed by reg_scan for use by cse and loop. - It is sometimes adjusted for subsequent changes during loop, - but not adjusted by cse even if cse invalidates it. */ - -#define REGNO_FIRST_UID(N) (VEC_index (reg_info_p, reg_n_info, N)->first_uid) - -/* Vector indexed by regno; gives uid of last insn using that reg. - This is computed by reg_scan for use by cse and loop. - It is sometimes adjusted for subsequent changes during loop, - but not adjusted by cse even if cse invalidates it. - This is harmless since cse won't scan through a loop end. */ - -#define REGNO_LAST_UID(N) (VEC_index (reg_info_p, reg_n_info, N)->last_uid) - -/* List made of EXPR_LIST rtx's which gives pairs of pseudo registers - that have to go in the same hard reg. */ -extern rtx regs_may_share; - /* Flag set by local-alloc or global-alloc if they decide to allocate something in a call-clobbered register. */ @@ -234,12 +260,6 @@ extern int caller_save_needed; #define HARD_REGNO_CALL_PART_CLOBBERED(REGNO, MODE) 0 #endif -/* Allocate reg_n_info tables */ -extern void allocate_reg_info (size_t, int, int); - -/* Clear the register information for regno. */ -extern void clear_reg_info_regno (unsigned int); - /* Specify number of hard registers given machine mode occupy. */ extern unsigned char hard_regno_nregs[FIRST_PSEUDO_REGISTER][MAX_MACHINE_MODE]; diff --git a/gcc/regstat.c b/gcc/regstat.c new file mode 100644 index 0000000..cfd904f --- /dev/null +++ b/gcc/regstat.c @@ -0,0 +1,511 @@ +/* Scanning of rtl for dataflow analysis. + Copyright (C) 2007 + Free Software Foundation, Inc. + Contributed by Kenneth Zadeck (zadeck@naturalbridge.com). + +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, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. +*/ + + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "rtl.h" +#include "tm_p.h" +#include "flags.h" +#include "regs.h" +#include "output.h" +#include "except.h" +#include "hard-reg-set.h" +#include "basic-block.h" +#include "timevar.h" +#include "df.h" + + +struct regstat_n_sets_and_refs_t *regstat_n_sets_and_refs; +struct regstat_n_sets_and_refs_t *regstat_n_sets_and_refs; + +/*---------------------------------------------------------------------------- + REG_N_SETS and REG_N_REFS. + ----------------------------------------------------------------------------*/ + +/* If a pass need to change these values in some magical way or or the + pass needs to have accurate values for these and is not using + incremental df scanning, then it should use REG_N_SETS and + REG_N_USES. If the pass is doing incremental scanning then it + should be getting the info from DF_REG_DEF_COUNT and + DF_REG_USE_COUNT. */ + +void +regstat_init_n_sets_and_refs (void) +{ + unsigned int i; + unsigned int max_regno = max_reg_num (); + + timevar_push (TV_REG_STATS); + df_grow_reg_info (); + gcc_assert (!regstat_n_sets_and_refs); + + regstat_n_sets_and_refs = xmalloc (max_regno * sizeof (struct regstat_n_sets_and_refs_t)); + + for (i = 0; i < max_regno; i++) + { + SET_REG_N_SETS (i, DF_REG_DEF_COUNT (i)); + SET_REG_N_REFS (i, DF_REG_USE_COUNT (i) + REG_N_SETS (i)); + } + timevar_pop (TV_REG_STATS); + +} + + +/* Free the array that holds the REG_N_SETS and REG_N_REFS. */ + +void +regstat_free_n_sets_and_refs (void) +{ + gcc_assert (regstat_n_sets_and_refs); + free (regstat_n_sets_and_refs); + regstat_n_sets_and_refs = NULL; +} + + +/*---------------------------------------------------------------------------- + REGISTER INFORMATION + + Process REG_N_DEATHS, REG_LIVE_LENGTH, REG_N_CALLS_CROSSED, + REG_N_THROWING_CALLS_CROSSED and REG_BASIC_BLOCK. + + ----------------------------------------------------------------------------*/ + +static bitmap setjmp_crosses; +struct reg_info_t *reg_info_p; + +/* The number allocated elements of reg_info_p. */ +size_t reg_info_p_size; + +/* Compute register info: lifetime, bb, and number of defs and uses + for basic block BB. The three bitvectors are scratch regs used + here. */ + +static void +regstat_bb_compute_ri (unsigned int bb_index, + bitmap live, bitmap do_not_gen, bitmap artificial_uses, + bitmap local_live, bitmap local_processed) +{ + basic_block bb = BASIC_BLOCK (bb_index); + rtx insn; + struct df_ref **def_rec; + struct df_ref **use_rec; + int luid = 0; + bitmap_iterator bi; + unsigned int regno; + + bitmap_copy (live, df_get_live_out (bb)); + bitmap_clear (artificial_uses); + + /* Process the regs live at the end of the block. Mark them as + not local to any one basic block. */ + EXECUTE_IF_SET_IN_BITMAP (live, 0, regno, bi) + REG_BASIC_BLOCK (regno) = REG_BLOCK_GLOBAL; + + /* Process the artificial defs and uses at the bottom of the block + to begin processing. */ + for (def_rec = df_get_artificial_defs (bb_index); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if ((DF_REF_FLAGS (def) & DF_REF_AT_TOP) == 0) + bitmap_clear_bit (live, DF_REF_REGNO (def)); + } + + for (use_rec = df_get_artificial_uses (bb_index); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if ((DF_REF_FLAGS (use) & DF_REF_AT_TOP) == 0) + { + regno = DF_REF_REGNO (use); + bitmap_set_bit (live, regno); + bitmap_set_bit (artificial_uses, regno); + } + } + + FOR_BB_INSNS_REVERSE (bb, insn) + { + unsigned int uid = INSN_UID (insn); + unsigned int regno; + bitmap_iterator bi; + struct df_mw_hardreg **mws_rec; + rtx link; + + if (!INSN_P (insn)) + continue; + + /* Increment the live_length for all of the registers that + are are referenced in this block and live at this + particular point. */ + EXECUTE_IF_SET_IN_BITMAP (local_live, 0, regno, bi) + { + REG_LIVE_LENGTH (regno)++; + } + luid++; + + bitmap_clear (do_not_gen); + + link = REG_NOTES (insn); + while (link) + { + if (REG_NOTE_KIND (link) == REG_DEAD) + REG_N_DEATHS(REGNO (XEXP (link, 0)))++; + link = XEXP (link, 1); + } + + /* Process the defs. */ + if (CALL_P (insn)) + { + bool can_throw = can_throw_internal (insn); + bool set_jump = (find_reg_note (insn, REG_SETJMP, NULL) != NULL); + EXECUTE_IF_SET_IN_BITMAP (live, 0, regno, bi) + { + REG_N_CALLS_CROSSED (regno)++; + if (can_throw) + REG_N_THROWING_CALLS_CROSSED (regno)++; + + /* We have a problem with any pseudoreg that lives + across the setjmp. ANSI says that if a user variable + does not change in value between the setjmp and the + longjmp, then the longjmp preserves it. This + includes longjmp from a place where the pseudo + appears dead. (In principle, the value still exists + if it is in scope.) If the pseudo goes in a hard + reg, some other value may occupy that hard reg where + this pseudo is dead, thus clobbering the pseudo. + Conclusion: such a pseudo must not go in a hard + reg. */ + if (set_jump) + bitmap_set_bit (setjmp_crosses, regno); + } + } + + /* We only care about real sets for calls. Clobbers only + may clobbers cannot be depended on. */ + for (mws_rec = DF_INSN_UID_MWS (uid); *mws_rec; mws_rec++) + { + struct df_mw_hardreg *mws = *mws_rec; + if (mws->type == DF_REF_REG_DEF) + { + bool all_dead = true; + unsigned int r; + + for (r=mws->start_regno; r <= mws->end_regno; r++) + if ((bitmap_bit_p (live, r)) + || bitmap_bit_p (artificial_uses, r)) + { + all_dead = false; + break; + } + + if (all_dead) + { + unsigned int regno = mws->start_regno; + bitmap_set_bit (do_not_gen, regno); + /* Only do this if the value is totally dead. */ + REG_LIVE_LENGTH (regno)++; + } + } + } + + /* All of the defs except the return value are some sort of + clobber. This code is for the return. */ + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if ((!CALL_P (insn)) + || (!(DF_REF_FLAGS (def) & (DF_REF_MUST_CLOBBER | DF_REF_MAY_CLOBBER)))) + { + unsigned int dregno = DF_REF_REGNO (def); + + if (bitmap_bit_p (live, dregno)) + { + /* If we have seen this regno, then it has already been + processed correctly with the per insn increment. If we + have not seen it we need to add the length from here to + the end of the block to the live length. */ + if (bitmap_bit_p (local_processed, dregno)) + { + if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))) + bitmap_clear_bit (local_live, dregno); + } + else + { + bitmap_set_bit (local_processed, dregno); + REG_LIVE_LENGTH (dregno) += luid; + } + } + else if ((!(DF_REF_FLAGS (def) & DF_REF_MW_HARDREG)) + && (!bitmap_bit_p (artificial_uses, dregno))) + { + REG_LIVE_LENGTH (dregno)++; + } + + if (dregno >= FIRST_PSEUDO_REGISTER) + { + REG_FREQ (dregno) += REG_FREQ_FROM_BB (bb); + if (REG_BASIC_BLOCK (dregno) == REG_BLOCK_UNKNOWN) + REG_BASIC_BLOCK (dregno) = bb->index; + else if (REG_BASIC_BLOCK (dregno) != bb->index) + REG_BASIC_BLOCK (dregno) = REG_BLOCK_GLOBAL; + } + + if (!(DF_REF_FLAGS (def) & (DF_REF_MUST_CLOBBER + DF_REF_MAY_CLOBBER))) + bitmap_set_bit (do_not_gen, dregno); + + /* Kill this register if it is not a subreg store or conditional store. */ + if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))) + bitmap_clear_bit (live, dregno); + } + } + + for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + unsigned int uregno = DF_REF_REGNO (use); + + if (uregno >= FIRST_PSEUDO_REGISTER) + { + REG_FREQ (uregno) += REG_FREQ_FROM_BB (bb); + if (REG_BASIC_BLOCK (uregno) == REG_BLOCK_UNKNOWN) + REG_BASIC_BLOCK (uregno) = bb->index; + else if (REG_BASIC_BLOCK (uregno) != bb->index) + REG_BASIC_BLOCK (uregno) = REG_BLOCK_GLOBAL; + } + + if (!bitmap_bit_p (live, uregno)) + { + /* This register is now live. */ + bitmap_set_bit (live, uregno); + + /* If we have seen this regno, then it has already been + processed correctly with the per insn increment. If + we have not seen it we set the bit so that begins to + get processed locally. Note that we don't even get + here if the variable was live at the end of the block + since just a ref inside the block does not effect the + calculations. */ + REG_LIVE_LENGTH (uregno) ++; + bitmap_set_bit (local_live, uregno); + bitmap_set_bit (local_processed, uregno); + } + } + } + + /* Add the length of the block to all of the registers that were not + referenced, but still live in this block. */ + bitmap_and_compl_into (live, local_processed); + EXECUTE_IF_SET_IN_BITMAP (live, 0, regno, bi) + REG_LIVE_LENGTH (regno) += luid; + + bitmap_clear (local_processed); + bitmap_clear (local_live); +} + + +/* Compute register info: lifetime, bb, and number of defs and uses. */ +void +regstat_compute_ri (void) +{ + basic_block bb; + bitmap live = BITMAP_ALLOC (&df_bitmap_obstack); + bitmap do_not_gen = BITMAP_ALLOC (&df_bitmap_obstack); + bitmap artificial_uses = BITMAP_ALLOC (&df_bitmap_obstack); + bitmap local_live = BITMAP_ALLOC (&df_bitmap_obstack); + bitmap local_processed = BITMAP_ALLOC (&df_bitmap_obstack); + unsigned int regno; + bitmap_iterator bi; + + /* Initialize everything. */ + + gcc_assert (!reg_info_p); + + timevar_push (TV_REG_STATS); + setjmp_crosses = BITMAP_ALLOC (&df_bitmap_obstack); + max_regno = max_reg_num (); + reg_info_p_size = max_regno; + reg_info_p = xcalloc (max_regno, sizeof (struct reg_info_t)); + + FOR_EACH_BB (bb) + { + regstat_bb_compute_ri (bb->index, live, do_not_gen, artificial_uses, + local_live, local_processed); + } + + BITMAP_FREE (live); + BITMAP_FREE (do_not_gen); + BITMAP_FREE (artificial_uses); + + /* See the setjmp comment in regstat_ri_bb_compute. */ + EXECUTE_IF_SET_IN_BITMAP (setjmp_crosses, FIRST_PSEUDO_REGISTER, regno, bi) + { + REG_BASIC_BLOCK (regno) = REG_BLOCK_UNKNOWN; + REG_LIVE_LENGTH (regno) = -1; + } + + BITMAP_FREE (local_live); + BITMAP_FREE (local_processed); + timevar_pop (TV_REG_STATS); +} + + +/* Free all storage associated with the problem. */ + +void +regstat_free_ri (void) +{ + gcc_assert (reg_info_p); + reg_info_p_size = 0; + free (reg_info_p); + reg_info_p = NULL; + + BITMAP_FREE (setjmp_crosses); +} + + +/* Return a bitmap containing the set of registers that cross a setjmp. + The client should not change or delete this bitmap. */ + +bitmap +regstat_get_setjmp_crosses (void) +{ + return setjmp_crosses; +} + +/*---------------------------------------------------------------------------- + Process REG_N_CALLS_CROSSED. + + This is used by sched_deps. A good implementation of sched-deps + would really process the blocks directly rather than going thur + lists of insns. If it did this, it could use the exact regs that + cross an individual call rather than using this info that merges + the info for all calls. + + ----------------------------------------------------------------------------*/ + + + +/* Compute callse crossed for BB. Live is a scratch bitvector. */ + +static void +regstat_bb_compute_calls_crossed (unsigned int bb_index, bitmap live) +{ + basic_block bb = BASIC_BLOCK (bb_index); + rtx insn; + struct df_ref **def_rec; + struct df_ref **use_rec; + + bitmap_copy (live, df_get_live_out (bb)); + + /* Process the artificial defs and uses at the bottom of the block + to begin processing. */ + for (def_rec = df_get_artificial_defs (bb_index); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if ((DF_REF_FLAGS (def) & DF_REF_AT_TOP) == 0) + bitmap_clear_bit (live, DF_REF_REGNO (def)); + } + + for (use_rec = df_get_artificial_uses (bb_index); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if ((DF_REF_FLAGS (use) & DF_REF_AT_TOP) == 0) + bitmap_set_bit (live, DF_REF_REGNO (use)); + } + + FOR_BB_INSNS_REVERSE (bb, insn) + { + unsigned int uid = INSN_UID (insn); + unsigned int regno; + + if (!INSN_P (insn)) + continue; + + /* Process the defs. */ + if (CALL_P (insn)) + { + bitmap_iterator bi; + EXECUTE_IF_SET_IN_BITMAP (live, 0, regno, bi) + REG_N_CALLS_CROSSED (regno)++; + } + + /* All of the defs except the return value are some sort of + clobber. This code is for the return. */ + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if ((!CALL_P (insn)) + || (!(DF_REF_FLAGS (def) & (DF_REF_MUST_CLOBBER | DF_REF_MAY_CLOBBER)))) + { + /* Kill this register if it is not a subreg store or conditional store. */ + if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))) + bitmap_clear_bit (live, DF_REF_REGNO (def)); + } + } + + for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + bitmap_set_bit (live, DF_REF_REGNO (use)); + } + } +} + + +/* Compute register info: lifetime, bb, and number of defs and uses. */ +void +regstat_compute_calls_crossed (void) +{ + basic_block bb; + bitmap live = BITMAP_ALLOC (&df_bitmap_obstack); + + /* Initialize everything. */ + gcc_assert (!reg_info_p); + + timevar_push (TV_REG_STATS); + max_regno = max_reg_num (); + reg_info_p_size = max_regno; + reg_info_p = xcalloc (max_regno, sizeof (struct reg_info_t)); + + FOR_EACH_BB (bb) + { + regstat_bb_compute_calls_crossed (bb->index, live); + } + + BITMAP_FREE (live); + timevar_pop (TV_REG_STATS); +} + + +/* Free all storage associated with the problem. */ + +void +regstat_free_calls_crossed (void) +{ + gcc_assert (reg_info_p); + reg_info_p_size = 0; + free (reg_info_p); + reg_info_p = NULL; +} + diff --git a/gcc/reload.c b/gcc/reload.c index 16a7e47..7df7f22 100644 --- a/gcc/reload.c +++ b/gcc/reload.c @@ -1,7 +1,7 @@ /* Search an insn for pseudo regs that must be in hard regs and are not. Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, - Inc. + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. This file is part of GCC. @@ -112,6 +112,7 @@ a register with any other reload. */ #include "toplev.h" #include "params.h" #include "target.h" +#include "df.h" /* True if X is a constant that can be forced into the constant pool. */ #define CONST_POOL_OK_P(X) \ @@ -1521,7 +1522,7 @@ push_reload (rtx in, rtx out, rtx *inloc, rtx *outloc, /* Check that we don't use a hardreg for an uninitialized pseudo. See also find_dummy_reload(). */ && (ORIGINAL_REGNO (XEXP (note, 0)) < FIRST_PSEUDO_REGISTER - || ! bitmap_bit_p (ENTRY_BLOCK_PTR->il.rtl->global_live_at_end, + || ! bitmap_bit_p (DF_RA_LIVE_OUT (ENTRY_BLOCK_PTR), ORIGINAL_REGNO (XEXP (note, 0)))) && ! refers_to_regno_for_reload_p (regno, end_hard_regno (rel_mode, @@ -1847,7 +1848,7 @@ combine_reloads (void) /* Check that we don't use a hardreg for an uninitialized pseudo. See also find_dummy_reload(). */ && (ORIGINAL_REGNO (XEXP (note, 0)) < FIRST_PSEUDO_REGISTER - || ! bitmap_bit_p (ENTRY_BLOCK_PTR->il.rtl->global_live_at_end, + || ! bitmap_bit_p (DF_LR_OUT (ENTRY_BLOCK_PTR), ORIGINAL_REGNO (XEXP (note, 0))))) { rld[output_reload].reg_rtx @@ -2000,7 +2001,7 @@ find_dummy_reload (rtx real_in, rtx real_out, rtx *inloc, rtx *outloc, as they would clobber the other live pseudo using the same. See also PR20973. */ && (ORIGINAL_REGNO (in) < FIRST_PSEUDO_REGISTER - || ! bitmap_bit_p (ENTRY_BLOCK_PTR->il.rtl->global_live_at_end, + || ! bitmap_bit_p (DF_RA_LIVE_OUT (ENTRY_BLOCK_PTR), ORIGINAL_REGNO (in)))) { unsigned int regno = REGNO (in) + in_offset; diff --git a/gcc/reload1.c b/gcc/reload1.c index f880e5a..b19be93 100644 --- a/gcc/reload1.c +++ b/gcc/reload1.c @@ -45,7 +45,9 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "toplev.h" #include "except.h" #include "tree.h" +#include "df.h" #include "target.h" +#include "dse.h" /* This file contains the reload pass of the compiler, which is run after register allocation has been done. It checks that @@ -546,7 +548,7 @@ compute_use_by_pseudos (HARD_REG_SET *to, regset from) if (r < 0) { /* reload_combine uses the information from - BASIC_BLOCK->global_live_at_start, which might still + DF_RA_LIVE_IN (BASIC_BLOCK), which might still contain registers that have not actually been allocated since they have an equivalence. */ gcc_assert (reload_completed); @@ -746,7 +748,7 @@ reload (rtx first, int global) && has_nonexceptional_receiver ())) for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (! call_used_regs[i] && ! fixed_regs[i] && ! LOCAL_REGNO (i)) - regs_ever_live[i] = 1; + df_set_regs_ever_live (i, true); /* Find all the pseudo registers that didn't get hard regs but do have known equivalent constants or memory slots. @@ -1154,9 +1156,11 @@ reload (rtx first, int global) if (! frame_pointer_needed) FOR_EACH_BB (bb) - CLEAR_REGNO_REG_SET (bb->il.rtl->global_live_at_start, - HARD_FRAME_POINTER_REGNUM); - + { + bitmap_clear_bit (df_get_live_in (bb), HARD_FRAME_POINTER_REGNUM); + bitmap_clear_bit (df_get_live_top (bb), HARD_FRAME_POINTER_REGNUM); + } + /* Come here (with failure set nonzero) if we can't get enough spill regs. */ failed: @@ -1274,6 +1278,7 @@ reload (rtx first, int global) || REG_NOTE_KIND (*pnote) == REG_UNUSED || REG_NOTE_KIND (*pnote) == REG_INC || REG_NOTE_KIND (*pnote) == REG_RETVAL + || REG_NOTE_KIND (*pnote) == REG_LIBCALL_ID || REG_NOTE_KIND (*pnote) == REG_LIBCALL) *pnote = XEXP (*pnote, 1); else @@ -1310,7 +1315,7 @@ reload (rtx first, int global) static int verbose_warned = 0; for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (regs_ever_live[i] && ! fixed_regs[i] && call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! fixed_regs[i] && call_used_regs[i]) size += UNITS_PER_WORD; if (size > STACK_CHECK_MAX_FRAME_SIZE) @@ -2042,8 +2047,8 @@ alter_reg (int i, int from_reg) /* Modify the reg-rtx to contain the new hard reg number or else to contain its pseudo reg number. */ - REGNO (regno_reg_rtx[i]) - = reg_renumber[i] >= 0 ? reg_renumber[i] : i; + SET_REGNO (regno_reg_rtx[i], + reg_renumber[i] >= 0 ? reg_renumber[i] : i); /* If we have a pseudo that is needed but has no hard reg or equivalent, allocate a stack slot for it. */ @@ -2072,6 +2077,8 @@ alter_reg (int i, int from_reg) inherent space, and no less total space, then the previous slot. */ if (from_reg == -1) { + HOST_WIDE_INT alias_set = new_alias_set (); + /* No known place to spill from => no slot to reuse. */ x = assign_stack_local (mode, total_size, min_align > inherent_align @@ -2084,7 +2091,8 @@ alter_reg (int i, int from_reg) adjust = inherent_size - total_size; /* Nothing can alias this slot except this pseudo. */ - set_mem_alias_set (x, new_alias_set ()); + set_mem_alias_set (x, alias_set); + dse_record_singleton_alias_set (alias_set, mode); } /* Reuse a stack slot if possible. */ @@ -2094,7 +2102,6 @@ alter_reg (int i, int from_reg) >= inherent_size) && MEM_ALIGN (spill_stack_slot[from_reg]) >= min_align) x = spill_stack_slot[from_reg]; - /* Allocate a bigger slot. */ else { @@ -2121,9 +2128,18 @@ alter_reg (int i, int from_reg) /* All pseudos mapped to this slot can alias each other. */ if (spill_stack_slot[from_reg]) - set_mem_alias_set (x, MEM_ALIAS_SET (spill_stack_slot[from_reg])); + { + HOST_WIDE_INT alias_set + = MEM_ALIAS_SET (spill_stack_slot[from_reg]); + set_mem_alias_set (x, alias_set); + dse_invalidate_singleton_alias_set (alias_set); + } else - set_mem_alias_set (x, new_alias_set ()); + { + HOST_WIDE_INT alias_set = new_alias_set (); + set_mem_alias_set (x, alias_set); + dse_record_singleton_alias_set (alias_set, mode); + } if (BYTES_BIG_ENDIAN) { @@ -2178,11 +2194,11 @@ alter_reg (int i, int from_reg) } } -/* Mark the slots in regs_ever_live for the hard regs - used by pseudo-reg number REGNO. */ +/* Mark the slots in regs_ever_live for the hard regs used by + pseudo-reg number REGNO, accessed in MODE. */ -void -mark_home_live (int regno) +static void +mark_home_live_1 (int regno, enum machine_mode mode) { int i, lim; @@ -2191,7 +2207,17 @@ mark_home_live (int regno) return; lim = end_hard_regno (PSEUDO_REGNO_MODE (regno), i); while (i < lim) - regs_ever_live[i++] = 1; + df_set_regs_ever_live(i++, true); +} + +/* Mark the slots in regs_ever_live for the hard regs + used by pseudo-reg number REGNO. */ + +void +mark_home_live (int regno) +{ + if (reg_renumber[regno] >= 0) + mark_home_live_1 (regno, PSEUDO_REGNO_MODE (regno)); } /* This function handles the tracking of elimination offsets around branches. @@ -3749,7 +3775,7 @@ spill_hard_reg (unsigned int regno, int cant_eliminate) if (cant_eliminate) { SET_HARD_REG_BIT (bad_spill_regs_global, regno); - regs_ever_live[regno] = 1; + df_set_regs_ever_live (regno, true); } /* Spill every pseudo reg that was allocated to this reg @@ -3793,9 +3819,9 @@ finish_spills (int global) { spill_reg_order[i] = n_spills; spill_regs[n_spills++] = i; - if (num_eliminable && ! regs_ever_live[i]) + if (num_eliminable && ! df_regs_ever_live_p (i)) something_changed = 1; - regs_ever_live[i] = 1; + df_set_regs_ever_live (i, true); } else spill_reg_order[i] = -1; @@ -3938,8 +3964,11 @@ scan_paradoxical_subregs (rtx x) if (REG_P (SUBREG_REG (x)) && (GET_MODE_SIZE (GET_MODE (x)) > reg_max_ref_width[REGNO (SUBREG_REG (x))])) - reg_max_ref_width[REGNO (SUBREG_REG (x))] - = GET_MODE_SIZE (GET_MODE (x)); + { + reg_max_ref_width[REGNO (SUBREG_REG (x))] + = GET_MODE_SIZE (GET_MODE (x)); + mark_home_live_1 (REGNO (SUBREG_REG (x)), GET_MODE (x)); + } return; default: @@ -8144,7 +8173,7 @@ delete_output_reload (rtx insn, int j, int last_reload_reg) if (rld[j].out != rld[j].in && REG_N_DEATHS (REGNO (reg)) == 1 && REG_N_SETS (REGNO (reg)) == 1 - && REG_BASIC_BLOCK (REGNO (reg)) >= 0 + && REG_BASIC_BLOCK (REGNO (reg)) >= NUM_FIXED_BLOCKS && find_regno_note (insn, REG_DEAD, REGNO (reg))) { rtx i2; diff --git a/gcc/reorg.c b/gcc/reorg.c index bdf2c31..dcdcfd7 100644 --- a/gcc/reorg.c +++ b/gcc/reorg.c @@ -1,6 +1,7 @@ /* Perform instruction reorganizations for delay slot filling. - Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu). Hacked by Michael Tiemann (tiemann@cygnus.com). @@ -211,7 +212,8 @@ static void update_reg_dead_notes (rtx, rtx); static void fix_reg_dead_note (rtx, rtx); static void update_reg_unused_notes (rtx, rtx); static void fill_simple_delay_slots (int); -static rtx fill_slots_from_thread (rtx, rtx, rtx, rtx, int, int, int, int, +static rtx fill_slots_from_thread (rtx, rtx, rtx, rtx, + int, int, int, int, int *, rtx); static void fill_eager_delay_slots (void); static void relax_delay_slots (rtx); @@ -638,7 +640,7 @@ delete_from_delay_slot (rtx insn) prev = PREV_INSN (seq_insn); trial = XVECEXP (seq, 0, 0); delete_related_insns (seq_insn); - add_insn_after (trial, prev); + add_insn_after (trial, prev, NULL); /* If there was a barrier after the old SEQUENCE, remit it. */ if (had_barrier) @@ -3440,7 +3442,7 @@ relax_delay_slots (rtx first) for (i = 0; i < XVECLEN (pat, 0); i++) { rtx this_insn = XVECEXP (pat, 0, i); - add_insn_after (this_insn, after); + add_insn_after (this_insn, after, NULL); after = this_insn; } delete_scheduled_jump (delay_insn); @@ -3558,7 +3560,7 @@ relax_delay_slots (rtx first) for (i = 0; i < XVECLEN (pat, 0); i++) { rtx this_insn = XVECEXP (pat, 0, i); - add_insn_after (this_insn, after); + add_insn_after (this_insn, after, NULL); after = this_insn; } delete_scheduled_jump (delay_insn); @@ -4011,6 +4013,7 @@ dbr_schedule (rtx first) link = XEXP (link, 1)) INSN_LOCATOR (XEXP (link, 0)) = 0; } + #endif } #endif /* DELAY_SLOTS */ @@ -4020,7 +4023,7 @@ gate_handle_delay_slots (void) { #ifdef DELAY_SLOTS return flag_delayed_branch; -#else +#else return 0; #endif } @@ -4033,7 +4036,7 @@ rest_of_handle_delay_slots (void) dbr_schedule (get_insns ()); #endif return 0; -} +} struct tree_opt_pass pass_delay_slots = { @@ -4085,4 +4088,3 @@ struct tree_opt_pass pass_machine_reorg = TODO_ggc_collect, /* todo_flags_finish */ 'M' /* letter */ }; - diff --git a/gcc/resource.c b/gcc/resource.c index 2e3f3d4..c2b0849 100644 --- a/gcc/resource.c +++ b/gcc/resource.c @@ -1,5 +1,5 @@ /* Definitions for computing resource usage of specific insns. - Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -35,6 +35,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "except.h" #include "insn-attr.h" #include "params.h" +#include "df.h" /* This structure is used to record liveness information at the targets or fallthrough insns of branches. We will most likely need the information @@ -956,7 +957,7 @@ mark_target_live_regs (rtx insns, rtx target, struct resources *res) TARGET. Otherwise, we must assume everything is live. */ if (b != -1) { - regset regs_live = BASIC_BLOCK (b)->il.rtl->global_live_at_start; + regset regs_live = DF_LIVE_IN (BASIC_BLOCK (b)); rtx start_insn, stop_insn; reg_set_iterator rsi; @@ -975,7 +976,8 @@ mark_target_live_regs (rtx insns, rtx target, struct resources *res) /* Get starting and ending insn, handling the case where each might be a SEQUENCE. */ - start_insn = (b == 0 ? insns : BB_HEAD (BASIC_BLOCK (b))); + start_insn = (b == ENTRY_BLOCK_PTR->next_bb->index ? + insns : BB_HEAD (BASIC_BLOCK (b))); stop_insn = target; if (NONJUMP_INSN_P (start_insn) diff --git a/gcc/resource.h b/gcc/resource.h index 589a6eb..3024567 100644 --- a/gcc/resource.h +++ b/gcc/resource.h @@ -1,5 +1,5 @@ /* Definitions for computing resource usage of specific insns. - Copyright (C) 1999, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1999, 2003, 2004, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -22,6 +22,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #define GCC_RESOURCE_H #include "hard-reg-set.h" +#include "df.h" /* Macro to clear all resources. */ #define CLEAR_RESOURCE(RES) \ diff --git a/gcc/rtl-factoring.c b/gcc/rtl-factoring.c index 8113306..8ab5e8b 100644 --- a/gcc/rtl-factoring.c +++ b/gcc/rtl-factoring.c @@ -1,5 +1,5 @@ /* RTL factoring (sequence abstraction). - Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -36,6 +36,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "tree-flow.h" #include "timevar.h" #include "output.h" +#include "df.h" #include "addresses.h" /* Sequence abstraction: @@ -454,36 +455,40 @@ collect_pattern_seqs (void) FOR_EACH_BB (bb) { regset_head live; - struct propagate_block_info *pbi; rtx insn; + rtx prev; /* Initialize liveness propagation. */ INIT_REG_SET (&live); - COPY_REG_SET (&live, bb->il.rtl->global_live_at_end); - pbi = init_propagate_block_info (bb, &live, NULL, NULL, 0); + bitmap_copy (&live, DF_LR_OUT (bb)); + df_simulate_artificial_refs_at_end (bb, &live); /* Propagate liveness info and mark insns where a stack reg is live. */ insn = BB_END (bb); - while (1) + for (insn = BB_END (bb); ; insn = prev) { - int reg; - for (reg = FIRST_STACK_REG; reg <= LAST_STACK_REG; reg++) - { - if (REGNO_REG_SET_P (&live, reg)) - { - bitmap_set_bit (&stack_reg_live, INSN_UID (insn)); - break; - } - } - - if (insn == BB_HEAD (bb)) - break; - insn = propagate_one_insn (pbi, insn); + prev = PREV_INSN (insn); + if (INSN_P (insn)) + { + int reg; + for (reg = FIRST_STACK_REG; reg <= LAST_STACK_REG; reg++) + { + if (REGNO_REG_SET_P (&live, reg)) + { + bitmap_set_bit (&stack_reg_live, INSN_UID (insn)); + break; + } + } + + } + if (insn == BB_HEAD (bb)) + break; + df_simulate_one_insn_backwards (bb, insn, &live); + insn = prev; } /* Free unused data. */ CLEAR_REG_SET (&live); - free_propagate_block_info (pbi); } #endif @@ -535,19 +540,18 @@ clear_regs_live_in_seq (HARD_REG_SET * regs, rtx insn, int length) basic_block bb; regset_head live; HARD_REG_SET hlive; - struct propagate_block_info *pbi; rtx x; int i; /* Initialize liveness propagation. */ bb = BLOCK_FOR_INSN (insn); INIT_REG_SET (&live); - COPY_REG_SET (&live, bb->il.rtl->global_live_at_end); - pbi = init_propagate_block_info (bb, &live, NULL, NULL, 0); + bitmap_copy (&live, DF_LR_OUT (bb)); + df_simulate_artificial_refs_at_end (bb, &live); /* Propagate until INSN if found. */ for (x = BB_END (bb); x != insn;) - x = propagate_one_insn (pbi, x); + df_simulate_one_insn_backwards (bb, insn, &live); /* Clear registers live after INSN. */ renumbered_reg_set_to_hard_reg_set (&hlive, &live); @@ -556,7 +560,8 @@ clear_regs_live_in_seq (HARD_REG_SET * regs, rtx insn, int length) /* Clear registers live in and before the sequence. */ for (i = 0; i < length;) { - rtx prev = propagate_one_insn (pbi, x); + rtx prev = PREV_INSN (x); + df_simulate_one_insn_backwards (bb, insn, &live); if (INSN_P (x)) { @@ -569,7 +574,6 @@ clear_regs_live_in_seq (HARD_REG_SET * regs, rtx insn, int length) } /* Free unused data. */ - free_propagate_block_info (pbi); CLEAR_REG_SET (&live); } @@ -695,7 +699,7 @@ recompute_gain_for_pattern_seq (pattern_seq pseq) base_reg_class (VOIDmode, MEM, SCRATCH))) #endif || (hascall && call_used_regs[i]) - || (!call_used_regs[i] && !regs_ever_live[i])) + || (!call_used_regs[i] && !df_regs_ever_live_p (i))) CLEAR_HARD_REG_BIT (linkregs, i); /* Find an appropriate register to be used as the link register. */ @@ -974,9 +978,8 @@ split_blocks_after_seqs (void) for (mseq = sb->matching_seqs; mseq; mseq = mseq->next_matching_seq) { block_label_after (mseq->insn); - IOR_REG_SET (BLOCK_FOR_INSN (pattern_seqs->insn)-> - il.rtl->global_live_at_end, - BLOCK_FOR_INSN (mseq->insn)->il.rtl->global_live_at_end); + IOR_REG_SET (DF_LIVE_OUT (BLOCK_FOR_INSN (pattern_seqs->insn)), + DF_LIVE_OUT (BLOCK_FOR_INSN (mseq->insn))); } } } @@ -1030,7 +1033,7 @@ split_pattern_seq (void) gen_symbol_ref_rtx_for_label (retlabel)), BB_END (bb)); /* Update liveness info. */ - SET_REGNO_REG_SET (bb->il.rtl->global_live_at_end, + SET_REGNO_REG_SET (DF_LIVE_OUT (bb), REGNO (pattern_seqs->link_reg)); } @@ -1082,12 +1085,12 @@ erase_matching_seqs (void) BB_END (bb) = callinsn; /* Maintain control flow and liveness information. */ - SET_REGNO_REG_SET (bb->il.rtl->global_live_at_end, + SET_REGNO_REG_SET (DF_LIVE_OUT (bb), REGNO (pattern_seqs->link_reg)); emit_barrier_after (BB_END (bb)); make_single_succ_edge (bb, BLOCK_FOR_INSN (sb->label), 0); - IOR_REG_SET (bb->il.rtl->global_live_at_end, - BLOCK_FOR_INSN (sb->label)->il.rtl->global_live_at_start); + IOR_REG_SET (DF_LIVE_OUT (bb), + DF_LIVE_IN (BLOCK_FOR_INSN (sb->label))); make_edge (BLOCK_FOR_INSN (seq_blocks->label), BLOCK_FOR_INSN (retlabel), EDGE_ABNORMAL); @@ -1131,7 +1134,7 @@ abstract_best_seq (void) free_seq_blocks (); /* Record the usage of the link register. */ - regs_ever_live[REGNO (pattern_seqs->link_reg)] = 1; + df_set_regs_ever_live (REGNO (pattern_seqs->link_reg), true); /* Remove the best pattern sequence. */ bestpseq = pattern_seqs; @@ -1359,6 +1362,8 @@ static void rtl_seqabstr (void) { int iter; + df_set_flags (DF_LR_RUN_DCE); + df_analyze (); /* Create a hash list for COLLECT_PATTERN_SEQS. */ hash_buckets = htab_create (HASH_INIT, htab_hash_bucket , htab_eq_bucket , @@ -1390,20 +1395,6 @@ rtl_seqabstr (void) /* Cleanup hash tables. */ htab_delete (hash_buckets); - - if (iter > 1) - { - /* Update notes. */ - count_or_remove_death_notes (NULL, 1); - - life_analysis (PROP_DEATH_NOTES | PROP_SCAN_DEAD_CODE - | PROP_KILL_DEAD_CODE); - - /* Extra cleanup. */ - cleanup_cfg (CLEANUP_EXPENSIVE | - CLEANUP_UPDATE_LIFE | - (flag_crossjumping ? CLEANUP_CROSSJUMP : 0)); - } } /* The gate function for TREE_OPT_PASS. */ @@ -1419,12 +1410,6 @@ gate_rtl_seqabstr (void) static unsigned int rest_of_rtl_seqabstr (void) { - life_analysis (PROP_DEATH_NOTES | PROP_SCAN_DEAD_CODE | PROP_KILL_DEAD_CODE); - - cleanup_cfg (CLEANUP_EXPENSIVE | - CLEANUP_UPDATE_LIFE | - (flag_crossjumping ? CLEANUP_CROSSJUMP : 0)); - /* Abstract out common insn sequences. */ rtl_seqabstr (); return 0; @@ -1442,6 +1427,7 @@ struct tree_opt_pass pass_rtl_seqabstr = { 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func | TODO_ggc_collect, /* todo_flags_finish */ 'Q' /* letter */ @@ -201,6 +201,21 @@ rtx_alloc_stat (RTX_CODE code MEM_STAT_DECL) } +/* Return true if ORIG is a sharable CONST. */ + +bool +shared_const_p (rtx orig) +{ + gcc_assert (GET_CODE (orig) == CONST); + + /* CONST can be shared if it contains a SYMBOL_REF. If it contains + a LABEL_REF, it isn't sharable. */ + return (GET_CODE (XEXP (orig, 0)) == PLUS + && GET_CODE (XEXP (XEXP (orig, 0), 0)) == SYMBOL_REF + && GET_CODE (XEXP (XEXP (orig, 0), 1)) == CONST_INT); +} + + /* Create a new copy of an rtx. Recursively copies the operands of the rtx, except for those few rtx codes that are sharable. */ @@ -234,11 +249,7 @@ copy_rtx (rtx orig) break; case CONST: - /* CONST can be shared if it contains a SYMBOL_REF. If it contains - a LABEL_REF, it isn't sharable. */ - if (GET_CODE (XEXP (orig, 0)) == PLUS - && GET_CODE (XEXP (XEXP (orig, 0), 0)) == SYMBOL_REF - && GET_CODE (XEXP (XEXP (orig, 0), 1)) == CONST_INT) + if (shared_const_p (orig)) return orig; break; diff --git a/gcc/rtl.def b/gcc/rtl.def index 711cf75..5acf475 100644 --- a/gcc/rtl.def +++ b/gcc/rtl.def @@ -113,26 +113,26 @@ DEF_RTL_EXPR(ADDRESS, "address", "e", RTX_MATCH) ---------------------------------------------------------------------- */ /* An instruction that cannot jump. */ -DEF_RTL_EXPR(INSN, "insn", "iuuBieiee", RTX_INSN) +DEF_RTL_EXPR(INSN, "insn", "iuuBieie", RTX_INSN) /* An instruction that can possibly jump. Fields ( rtx->u.fld[] ) have exact same meaning as INSN's. */ -DEF_RTL_EXPR(JUMP_INSN, "jump_insn", "iuuBieiee0", RTX_INSN) +DEF_RTL_EXPR(JUMP_INSN, "jump_insn", "iuuBieie0", RTX_INSN) /* An instruction that can possibly call a subroutine but which will not change which instruction comes next in the current function. - Field ( rtx->u.fld[9] ) is CALL_INSN_FUNCTION_USAGE. + Field ( rtx->u.fld[8] ) is CALL_INSN_FUNCTION_USAGE. All other fields ( rtx->u.fld[] ) have exact same meaning as INSN's. */ -DEF_RTL_EXPR(CALL_INSN, "call_insn", "iuuBieieee", RTX_INSN) +DEF_RTL_EXPR(CALL_INSN, "call_insn", "iuuBieiee", RTX_INSN) /* A marker that indicates that control will not flow through. */ -DEF_RTL_EXPR(BARRIER, "barrier", "iuu000000", RTX_EXTRA) +DEF_RTL_EXPR(BARRIER, "barrier", "iuu00000", RTX_EXTRA) /* Holds a label that is followed by instructions. Operand: 4: is used in jump.c for the use-count of the label. - 5: is used in flow.c to point to the chain of label_ref's to this label. + 5: is used in the sh backend. 6: is a number that is unique in the entire compilation. 7: is the user-given name of the label, if any. */ DEF_RTL_EXPR(CODE_LABEL, "code_label", "iuuB00is", RTX_EXTRA) @@ -741,13 +741,6 @@ extern void rtl_check_failed_flag (const char *, rtx, const char *, -1 means this instruction has not been recognized yet. */ #define INSN_CODE(INSN) XINT (INSN, 6) -/* Set up in flow.c; empty before then. - Holds a chain of INSN_LIST rtx's whose first operands point at - previous insns with direct data-flow connections to this one. - That means that those insns set variables whose next use is in this insn. - They are always in the same basic block as this insn. */ -#define LOG_LINKS(INSN) XEXP(INSN, 7) - #define RTX_FRAME_RELATED_P(RTX) \ (RTL_FLAG_CHECK5("RTX_FRAME_RELATED_P", (RTX), INSN, CALL_INSN, \ JUMP_INSN, BARRIER, SET)->frame_related) @@ -790,7 +783,7 @@ extern void rtl_check_failed_flag (const char *, rtx, const char *, chain pointer and the first operand is the REG being described. The mode field of the EXPR_LIST contains not a real machine mode but a value from enum reg_note. */ -#define REG_NOTES(INSN) XEXP(INSN, 8) +#define REG_NOTES(INSN) XEXP(INSN, 7) enum reg_note { @@ -817,7 +810,7 @@ extern const char * const reg_note_name[]; CLOBBER expressions document the registers explicitly clobbered by this CALL_INSN. Pseudo registers can not be mentioned in this list. */ -#define CALL_INSN_FUNCTION_USAGE(INSN) XEXP(INSN, 9) +#define CALL_INSN_FUNCTION_USAGE(INSN) XEXP(INSN, 8) /* The label-number of a code-label. The assembler label is made from `L' and the label-number printed in decimal. @@ -833,14 +826,7 @@ extern const char * const reg_note_name[]; /* Opaque data. */ #define NOTE_DATA(INSN) RTL_CHECKC1 (INSN, 4, NOTE) #define NOTE_DELETED_LABEL_NAME(INSN) XCSTR (INSN, 4, NOTE) -#ifdef USE_MAPPED_LOCATION -#define SET_INSN_DELETED(INSN) \ - (PUT_CODE (INSN, NOTE), NOTE_KIND (INSN) = NOTE_INSN_DELETED) -#else -#define SET_INSN_DELETED(INSN) \ - (PUT_CODE (INSN, NOTE), \ - NOTE_KIND (INSN) = NOTE_INSN_DELETED) -#endif +#define SET_INSN_DELETED(INSN) set_insn_deleted (INSN); #define NOTE_BLOCK(INSN) XCTREE (INSN, 4, NOTE) #define NOTE_EH_HANDLER(INSN) XCINT (INSN, 4, NOTE) #define NOTE_BASIC_BLOCK(INSN) XCBBDEF (INSN, 4, NOTE) @@ -946,21 +932,32 @@ enum label_kind /* In jump.c, each JUMP_INSN can point to a label that it can jump to, so that if the JUMP_INSN is deleted, the label's LABEL_NUSES can be decremented and possibly the label can be deleted. */ -#define JUMP_LABEL(INSN) XCEXP (INSN, 9, JUMP_INSN) +#define JUMP_LABEL(INSN) XCEXP (INSN, 8, JUMP_INSN) -/* Once basic blocks are found in flow.c, - each CODE_LABEL starts a chain that goes through - all the LABEL_REFs that jump to that label. - The chain eventually winds up at the CODE_LABEL: it is circular. */ +/* Once basic blocks are found, each CODE_LABEL starts a chain that + goes through all the LABEL_REFs that jump to that label. The chain + eventually winds up at the CODE_LABEL: it is circular. */ #define LABEL_REFS(LABEL) XCEXP (LABEL, 5, CODE_LABEL) -/* For a REG rtx, REGNO extracts the register number. ORIGINAL_REGNO holds - the number the register originally had; for a pseudo register turned into - a hard reg this will hold the old pseudo register number. */ - -#define REGNO(RTX) XCUINT (RTX, 0, REG) +/* For a REG rtx, REGNO extracts the register number. REGNO can only + be used on RHS. Use SET_REGNO to change the value. */ +#define REGNO(RTX) (rhs_regno(RTX)) +#define SET_REGNO(RTX,N) (df_ref_change_reg_with_loc (REGNO(RTX), N, RTX), XCUINT (RTX, 0, REG) = N) + +/* ORIGINAL_REGNO holds the number the register originally had; for a + pseudo register turned into a hard reg this will hold the old pseudo + register number. */ #define ORIGINAL_REGNO(RTX) X0UINT (RTX, 1) +/* Force the REGNO macro to only be used on the lhs. */ +static inline unsigned int +rhs_regno (rtx x) +{ + return XCUINT (x, 0, REG); +} + + + /* 1 if RTX is a reg or parallel that is the current function's return value. */ #define REG_FUNCTION_VALUE_P(RTX) \ @@ -1329,10 +1326,20 @@ do { \ offset within that block. */ #define SYMBOL_REF_BLOCK_OFFSET(RTX) (BLOCK_SYMBOL_CHECK (RTX)->offset) +/* Indicate whether the machine has any sort of auto increment addressing. + If not, we can avoid checking for REG_INC notes. */ + +#if (defined (HAVE_PRE_INCREMENT) || defined (HAVE_PRE_DECREMENT) \ + || defined (HAVE_POST_INCREMENT) || defined (HAVE_POST_DECREMENT) \ + || defined (HAVE_PRE_MODIFY_DISP) || defined (HAVE_PRE_MODIFY_DISP) \ + || defined (HAVE_PRE_MODIFY_REG) || defined (HAVE_POST_MODIFY_REG)) +#define AUTO_INC_DEC +#endif + /* Define a macro to look for REG_INC notes, but save time on machines where they never exist. */ -#if (defined (HAVE_PRE_INCREMENT) || defined (HAVE_PRE_DECREMENT) || defined (HAVE_POST_INCREMENT) || defined (HAVE_POST_DECREMENT)) +#ifdef AUTO_INC_DEC #define FIND_REG_INC_NOTE(INSN, REG) \ ((REG) != NULL_RTX && REG_P ((REG)) \ ? find_regno_note ((INSN), REG_INC, REGNO (REG)) \ @@ -1341,13 +1348,6 @@ do { \ #define FIND_REG_INC_NOTE(INSN, REG) 0 #endif -/* Indicate whether the machine has any sort of auto increment addressing. - If not, we can avoid checking for REG_INC notes. */ - -#if (defined (HAVE_PRE_INCREMENT) || defined (HAVE_PRE_DECREMENT) || defined (HAVE_POST_INCREMENT) || defined (HAVE_POST_DECREMENT)) -#define AUTO_INC_DEC -#endif - #ifndef HAVE_PRE_INCREMENT #define HAVE_PRE_INCREMENT 0 #endif @@ -1434,6 +1434,7 @@ extern HOST_WIDE_INT trunc_int_for_mode (HOST_WIDE_INT, enum machine_mode); extern rtx plus_constant (rtx, HOST_WIDE_INT); /* In emit-rtl.c */ +extern rtx gen_blockage (void); extern rtvec gen_rtvec (int, ...); extern rtx copy_insn_1 (rtx); extern rtx copy_insn (rtx); @@ -1449,6 +1450,7 @@ extern rtx rtx_alloc_stat (RTX_CODE MEM_STAT_DECL); #define rtx_alloc(c) rtx_alloc_stat (c MEM_STAT_INFO) extern rtvec rtvec_alloc (int); +extern bool shared_const_p (rtx); extern rtx copy_rtx (rtx); extern void dump_rtx_statistics (void); @@ -1523,7 +1525,7 @@ extern rtx assign_temp (tree, int, int, int); /* In emit-rtl.c */ extern rtx emit_insn_before (rtx, rtx); -extern rtx emit_insn_before_noloc (rtx, rtx); +extern rtx emit_insn_before_noloc (rtx, rtx, struct basic_block_def *); extern rtx emit_insn_before_setloc (rtx, rtx, int); extern rtx emit_jump_insn_before (rtx, rtx); extern rtx emit_jump_insn_before_noloc (rtx, rtx); @@ -1535,7 +1537,7 @@ extern rtx emit_barrier_before (rtx); extern rtx emit_label_before (rtx, rtx); extern rtx emit_note_before (enum insn_note, rtx); extern rtx emit_insn_after (rtx, rtx); -extern rtx emit_insn_after_noloc (rtx, rtx); +extern rtx emit_insn_after_noloc (rtx, rtx, struct basic_block_def *); extern rtx emit_insn_after_setloc (rtx, rtx, int); extern rtx emit_jump_insn_after (rtx, rtx); extern rtx emit_jump_insn_after_noloc (rtx, rtx); @@ -1638,6 +1640,7 @@ extern enum machine_mode choose_hard_reg_mode (unsigned int, unsigned int, /* In emit-rtl.c */ extern rtx set_unique_reg_note (rtx, enum reg_note, rtx); +extern void set_insn_deleted (rtx); /* Functions in rtlanal.c */ @@ -1729,11 +1732,6 @@ extern rtx canonicalize_condition (rtx, rtx, int, rtx *, rtx, int, int); being made. */ extern rtx get_condition (rtx, rtx *, int, int); - -/* flow.c */ - -extern rtx find_use_as_address (rtx, rtx, HOST_WIDE_INT); - /* lists.c */ void free_EXPR_LIST_list (rtx *); @@ -1747,12 +1745,6 @@ rtx remove_list_elem (rtx, rtx *); /* regclass.c */ -/* Maximum number of parallel sets and clobbers in any insn in this fn. - Always at least 3, since the combiner could put that many together - and we want this to remain correct for all the remaining passes. */ - -extern int max_parallel; - /* Free up register info memory. */ extern void free_reg_info (void); @@ -1764,7 +1756,7 @@ extern const char *decode_asm_operands (rtx, rtx *, rtx **, const char **, extern enum reg_class reg_preferred_class (int); extern enum reg_class reg_alternate_class (int); -extern void split_all_insns (int); +extern void split_all_insns (void); extern unsigned int split_all_insns_noflow (void); #define MAX_SAVED_CONST_INT 64 @@ -1953,10 +1945,6 @@ extern rtx gen_rtx_MEM (enum machine_mode, rtx); extern rtx output_constant_def (tree, int); extern rtx lookup_constant_def (tree); -/* Nonzero after the second flow pass has completed. - Set to 1 or 0 by toplev.c */ -extern int flow2_completed; - /* Nonzero after end of reload pass. Set to 1 or 0 by reload1.c. */ @@ -2056,8 +2044,8 @@ extern void set_first_insn (rtx); extern void set_last_insn (rtx); extern void link_cc0_insns (rtx); extern void add_insn (rtx); -extern void add_insn_before (rtx, rtx); -extern void add_insn_after (rtx, rtx); +extern void add_insn_before (rtx, rtx, struct basic_block_def *); +extern void add_insn_after (rtx, rtx, struct basic_block_def *); extern void remove_insn (rtx); extern rtx emit (rtx); extern rtx delete_insn (rtx); @@ -2074,12 +2062,15 @@ extern rtx gen_tmp_stack_mem (enum machine_mode, rtx); extern bool validate_subreg (enum machine_mode, enum machine_mode, rtx, unsigned int); -/* In combine.c */ +/* In combine.c */ extern unsigned int extended_count (rtx, enum machine_mode, int); extern rtx remove_death (unsigned int, rtx); extern void dump_combine_stats (FILE *); extern void dump_combine_total_stats (FILE *); +/* In cfgcleanup.c */ +extern void delete_dead_jumptables (void); + /* In sched-vis.c. */ extern void print_rtl_slim_with_bb (FILE *, rtx, int); extern void dump_insn_slim (FILE *f, rtx x); @@ -2106,12 +2097,8 @@ extern void print_simple_rtl (FILE *, rtx); extern int print_rtl_single (FILE *, rtx); extern void print_inline_rtx (FILE *, rtx, int); -/* In bt-load.c */ -extern void branch_target_load_optimize (bool); - /* In function.c */ -extern void reposition_prologue_and_epilogue_notes (rtx); -extern void thread_prologue_and_epilogue_insns (rtx); +extern void reposition_prologue_and_epilogue_notes (void); extern int prologue_epilogue_contains (rtx); extern int sibcall_epilogue_contains (rtx); extern void mark_temp_addr_taken (rtx); @@ -2126,9 +2113,11 @@ extern void emit_jump (rtx); extern rtx move_by_pieces (rtx, rtx, unsigned HOST_WIDE_INT, unsigned int, int); -/* In flow.c */ -extern void delete_dead_jumptables (void); +/* In cfgrtl.c */ extern void print_rtl_with_bb (FILE *, rtx); + +/* In cfg.c. */ +extern void dump_reg_info (FILE *); extern void dump_flow_info (FILE *, int); /* In expmed.c */ @@ -2157,13 +2146,9 @@ extern void init_reg_modes_once (void); extern void init_regs (void); extern void init_fake_stack_mems (void); extern void init_reg_sets (void); -extern void regclass_init (void); extern void regclass (rtx, int); extern void reg_scan (rtx, unsigned int); -extern void reg_scan_update (rtx, rtx, unsigned int); extern void fix_register (const char *, int, int); -extern void init_subregs_of_mode (void); -extern void record_subregs_of_mode (rtx); #ifdef HARD_CONST extern void cannot_change_mode_set_regs (HARD_REG_SET *, enum machine_mode, unsigned int); @@ -2217,7 +2202,6 @@ extern const char *read_rtx_filename; extern int read_rtx_lineno; /* In alias.c */ -extern void clear_reg_alias_info (rtx); extern rtx canon_rtx (rtx); extern int true_dependence (rtx, enum machine_mode, rtx, int (*)(rtx, int)); extern rtx get_addr (rtx); diff --git a/gcc/rtlanal.c b/gcc/rtlanal.c index b890b72..3a26d8f 100644 --- a/gcc/rtlanal.c +++ b/gcc/rtlanal.c @@ -37,6 +37,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "real.h" #include "regs.h" #include "function.h" +#include "df.h" #include "tree.h" /* Information about a subreg of a hard register. */ @@ -619,6 +620,7 @@ count_occurrences (rtx x, rtx find, int count_dest) } return count; } + /* Nonzero if register REG appears somewhere within IN. Also works if REG is not a register; in this case it checks @@ -1538,7 +1540,7 @@ note_uses (rtx *pbody, void (*fun) (rtx *, void *), void *data) REG may be a hard or pseudo reg. Renumbering is not taken into account, but for this use that makes no difference, since regs don't overlap during their lifetimes. Therefore, this function may be used - at any time after deaths have been computed (in flow.c). + at any time after deaths have been computed. If REG is a hard reg that occupies multiple machine registers, this function will only return 1 if each of those registers will be replaced @@ -1616,8 +1618,7 @@ covers_regno_p (rtx dest, unsigned int test_regno) return covers_regno_no_parallel_p (dest, test_regno); } -/* Utility function for dead_or_set_p to check an individual register. Also - called from flow.c. */ +/* Utility function for dead_or_set_p to check an individual register. */ int dead_or_set_regno_p (rtx insn, unsigned int test_regno) @@ -1872,19 +1873,24 @@ remove_note (rtx insn, rtx note) return; if (REG_NOTES (insn) == note) + REG_NOTES (insn) = XEXP (note, 1); + else + for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) + if (XEXP (link, 1) == note) + { + XEXP (link, 1) = XEXP (note, 1); + break; + } + + switch (REG_NOTE_KIND (note)) { - REG_NOTES (insn) = XEXP (note, 1); - return; + case REG_EQUAL: + case REG_EQUIV: + df_notes_rescan (insn); + break; + default: + break; } - - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - if (XEXP (link, 1) == note) - { - XEXP (link, 1) = XEXP (note, 1); - return; - } - - gcc_unreachable (); } /* Remove REG_EQUAL and/or REG_EQUIV notes if INSN has such notes. */ @@ -2888,11 +2894,7 @@ commutative_operand_precedence (rtx op) if (code == SUBREG && OBJECT_P (SUBREG_REG (op))) return -2; - if (!CONSTANT_P (op)) - return 0; - else - /* As for RTX_CONST_OBJ. */ - return -3; + return 0; case RTX_OBJ: /* Complex expressions should be the first, so decrease priority diff --git a/gcc/sbitmap.c b/gcc/sbitmap.c index a25bff4b..65a4686 100644 --- a/gcc/sbitmap.c +++ b/gcc/sbitmap.c @@ -1,5 +1,6 @@ /* Simple bitmaps. - Copyright (C) 1999, 2000, 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2002, 2003, 2004, 2006, 2007 + Free Software Foundation, Inc. This file is part of GCC. @@ -262,6 +263,19 @@ sbitmap_equal (sbitmap a, sbitmap b) return !memcmp (a->elms, b->elms, sizeof (SBITMAP_ELT_TYPE) * a->size); } +/* Return true if the bitmap is empty. */ + +bool +sbitmap_empty_p (sbitmap bmap) +{ + unsigned int i; + for (i=0; i<bmap->size; i++) + if (bmap->elms[i]) + return false; + + return true; +} + /* Zero all elements in a bitmap. */ void diff --git a/gcc/sbitmap.h b/gcc/sbitmap.h index aacc665..86febb4 100644 --- a/gcc/sbitmap.h +++ b/gcc/sbitmap.h @@ -1,5 +1,6 @@ /* Simple bitmaps. - Copyright (C) 1999, 2000, 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2002, 2003, 2004, 2006, 2007 + Free Software Foundation, Inc. This file is part of GCC. @@ -216,6 +217,7 @@ extern sbitmap sbitmap_resize (sbitmap, unsigned int, int); extern void sbitmap_copy (sbitmap, sbitmap); extern void sbitmap_copy_n (sbitmap, sbitmap, unsigned int); extern int sbitmap_equal (sbitmap, sbitmap); +extern bool sbitmap_empty_p (sbitmap); extern void sbitmap_zero (sbitmap); extern void sbitmap_ones (sbitmap); extern void sbitmap_vector_zero (sbitmap *, unsigned int); diff --git a/gcc/sched-deps.c b/gcc/sched-deps.c index 0fd497c..cf63a91 100644 --- a/gcc/sched-deps.c +++ b/gcc/sched-deps.c @@ -1,7 +1,7 @@ /* Instruction scheduling pass. This file computes dependencies between instructions. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Michael Tiemann (tiemann@cygnus.com) Enhanced by, and currently maintained by, Jim Wilson (wilson@cygnus.com) @@ -42,7 +42,6 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "sched-int.h" #include "params.h" #include "cselib.h" -#include "df.h" #ifdef ENABLE_CHECKING #define CHECK (true) @@ -550,7 +549,7 @@ sched_insns_conditions_mutex_p (rtx insn1, rtx insn2) { rtx cond1, cond2; - /* flow.c doesn't handle conditional lifetimes entirely correctly; + /* df doesn't handle conditional lifetimes entirely correctly; calls mess up the conditional lifetimes. */ if (!CALL_P (insn1) && !CALL_P (insn2)) { @@ -1840,9 +1839,6 @@ sched_analyze (struct deps *deps, rtx head, rtx tail) if (INSN_P (insn)) { - /* Clear out the stale LOG_LINKS from flow. */ - free_INSN_LIST_list (&LOG_LINKS (insn)); - /* These two lists will be freed in schedule_insn (). */ INSN_BACK_DEPS (insn) = create_deps_list (false); INSN_RESOLVED_BACK_DEPS (insn) = create_deps_list (false); diff --git a/gcc/sched-ebb.c b/gcc/sched-ebb.c index 76c65ee..dbece7d 100644 --- a/gcc/sched-ebb.c +++ b/gcc/sched-ebb.c @@ -1,6 +1,7 @@ /* Instruction scheduling pass. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. Contributed by Michael Tiemann (tiemann@cygnus.com) Enhanced by, and currently maintained by, Jim Wilson (wilson@cygnus.com) @@ -42,6 +43,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "sched-int.h" #include "target.h" #include "output.h" + /* The number of insns scheduled so far. */ static int sched_n_insns; @@ -51,8 +53,6 @@ static int n_insns; /* Set of blocks, that already have their dependencies calculated. */ static bitmap_head dont_calc_deps; -/* Set of basic blocks, that are ebb heads of tails respectively. */ -static bitmap_head ebb_head, ebb_tail; /* Last basic block in current ebb. */ static basic_block last_bb; @@ -74,10 +74,6 @@ static void add_block1 (basic_block, basic_block); static basic_block advance_target_bb (basic_block, rtx); static void fix_recovery_cfg (int, int, int); -#ifdef ENABLE_CHECKING -static int ebb_head_or_leaf_p (basic_block, int); -#endif - /* Return nonzero if there are more insns that should be scheduled. */ static int @@ -256,9 +252,9 @@ compute_jump_reg_dependencies (rtx insn, regset cond_set, regset used, it may guard the fallthrough block from using a value that has conditionally overwritten that of the main codepath. So we consider that it restores the value of the main codepath. */ - bitmap_and (set, glat_start [e->dest->index], cond_set); + bitmap_and (set, DF_LIVE_IN (e->dest), cond_set); else - bitmap_ior_into (used, glat_start [e->dest->index]); + bitmap_ior_into (used, DF_LIVE_IN (e->dest)); } /* Used in schedule_insns to initialize current_sched_info for scheduling @@ -284,12 +280,9 @@ static struct sched_info ebb_sched_info = add_block1, advance_target_bb, fix_recovery_cfg, -#ifdef ENABLE_CHECKING - ebb_head_or_leaf_p, -#endif - /* We need to DETACH_LIVE_INFO to be able to create new basic blocks. - See begin_schedule_ready (). */ - SCHED_EBB | USE_GLAT | DETACH_LIFE_INFO + SCHED_EBB + /* We can create new blocks in begin_schedule_ready (). */ + | NEW_BBS }; /* Returns the earliest block in EBB currently being processed where a @@ -541,8 +534,6 @@ schedule_ebbs (void) basic_block bb; int probability_cutoff; rtx tail; - sbitmap large_region_blocks, blocks; - int any_large_regions; if (profile_info && flag_branch_probabilities) probability_cutoff = PARAM_VALUE (TRACER_MIN_BRANCH_PROBABILITY_FEEDBACK); @@ -559,6 +550,11 @@ schedule_ebbs (void) invoked via sched_init. */ current_sched_info = &ebb_sched_info; + df_set_flags (DF_LR_RUN_DCE); + df_note_add_problem (); + df_analyze (); + df_clear_flags (DF_LR_RUN_DCE); + regstat_compute_calls_crossed (); sched_init (); compute_bb_for_insn (); @@ -566,10 +562,6 @@ schedule_ebbs (void) /* Initialize DONT_CALC_DEPS and ebb-{start, end} markers. */ bitmap_initialize (&dont_calc_deps, 0); bitmap_clear (&dont_calc_deps); - bitmap_initialize (&ebb_head, 0); - bitmap_clear (&ebb_head); - bitmap_initialize (&ebb_tail, 0); - bitmap_clear (&ebb_tail); /* Schedule every region in the subroutine. */ FOR_EACH_BB (bb) @@ -608,78 +600,17 @@ schedule_ebbs (void) break; } - bitmap_set_bit (&ebb_head, BLOCK_NUM (head)); bb = schedule_ebb (head, tail); - bitmap_set_bit (&ebb_tail, bb->index); } bitmap_clear (&dont_calc_deps); - gcc_assert (current_sched_info->flags & DETACH_LIFE_INFO); - /* We can create new basic blocks during scheduling, and - attach_life_info () will create regsets for them - (along with attaching existing info back). */ - attach_life_info (); - - /* Updating register live information. */ - allocate_reg_life_data (); - - any_large_regions = 0; - large_region_blocks = sbitmap_alloc (last_basic_block); - sbitmap_zero (large_region_blocks); - FOR_EACH_BB (bb) - SET_BIT (large_region_blocks, bb->index); - - blocks = sbitmap_alloc (last_basic_block); - sbitmap_zero (blocks); - - /* Update life information. For regions consisting of multiple blocks - we've possibly done interblock scheduling that affects global liveness. - For regions consisting of single blocks we need to do only local - liveness. */ - FOR_EACH_BB (bb) - { - int bbi; - - bbi = bb->index; - - if (!bitmap_bit_p (&ebb_head, bbi) - || !bitmap_bit_p (&ebb_tail, bbi) - /* New blocks (e.g. recovery blocks) should be processed - as parts of large regions. */ - || !glat_start[bbi]) - any_large_regions = 1; - else - { - SET_BIT (blocks, bbi); - RESET_BIT (large_region_blocks, bbi); - } - } - - update_life_info (blocks, UPDATE_LIFE_LOCAL, 0); - sbitmap_free (blocks); - - if (any_large_regions) - { - update_life_info (large_region_blocks, UPDATE_LIFE_GLOBAL, 0); - -#ifdef ENABLE_CHECKING - /* !!! We can't check reg_live_info here because of the fact, - that destination registers of COND_EXEC's may be dead - before scheduling (while they should be alive). Don't know why. */ - /*check_reg_live (true);*/ -#endif - } - sbitmap_free (large_region_blocks); - - bitmap_clear (&ebb_head); - bitmap_clear (&ebb_tail); - /* Reposition the prologue and epilogue notes in case we moved the prologue/epilogue insns. */ if (reload_completed) - reposition_prologue_and_epilogue_notes (get_insns ()); + reposition_prologue_and_epilogue_notes (); sched_finish (); + regstat_free_calls_crossed (); } /* INSN has been added to/removed from current ebb. */ @@ -754,17 +685,3 @@ fix_recovery_cfg (int bbi ATTRIBUTE_UNUSED, int jump_bbi, int jump_bb_nexti) if (jump_bb_nexti == last_bb->index) last_bb = BASIC_BLOCK (jump_bbi); } - -#ifdef ENABLE_CHECKING -/* Return non zero, if BB is first or last (depending of LEAF_P) block in - current ebb. For more information please refer to - sched-int.h: struct sched_info: region_head_or_leaf_p. */ -static int -ebb_head_or_leaf_p (basic_block bb, int leaf_p) -{ - if (!leaf_p) - return bitmap_bit_p (&ebb_head, bb->index); - else - return bitmap_bit_p (&ebb_tail, bb->index); -} -#endif /* ENABLE_CHECKING */ diff --git a/gcc/sched-int.h b/gcc/sched-int.h index 8926ac1..c8652c3 100644 --- a/gcc/sched-int.h +++ b/gcc/sched-int.h @@ -1,7 +1,7 @@ /* Instruction scheduling pass. This file contains definitions used internally in the scheduler. - Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -29,6 +29,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "basic-block.h" /* For reg_note. */ #include "rtl.h" +#include "df.h" /* Pointer to data describing the current DFA state. */ extern state_t curr_state; @@ -112,8 +113,7 @@ typedef struct _dep_link *dep_link_t; void debug_dep_links (dep_link_t); -/* A list of dep_links. Lists of this type are now used instead of rtx - LOG_LINKS and alike lists. */ +/* A list of dep_links. */ struct _deps_list { dep_link_t first; @@ -438,16 +438,6 @@ struct sched_info parameter. */ void (*fix_recovery_cfg) (int, int, int); -#ifdef ENABLE_CHECKING - /* If the second parameter is zero, return nonzero, if block is head of the - region. - If the second parameter is nonzero, return nonzero, if block is leaf of - the region. - global_live_at_start should not change in region heads and - global_live_at_end should not change in region leafs due to scheduling. */ - int (*region_head_or_leaf_p) (basic_block, int); -#endif - /* ??? FIXME: should use straight bitfields inside sched_info instead of this flag field. */ unsigned int flags; @@ -561,10 +551,6 @@ struct haifa_insn_data }; extern struct haifa_insn_data *h_i_d; -/* Used only if (current_sched_info->flags & USE_GLAT) != 0. - These regsets store global_live_at_{start, end} information - for each basic block. */ -extern regset *glat_start, *glat_end; /* Accessor macros for h_i_d. There are more in haifa-sched.c and sched-rgn.c. */ @@ -730,13 +716,8 @@ enum SCHED_FLAGS { DO_SPECULATION = USE_DEPS_LIST << 1, SCHED_RGN = DO_SPECULATION << 1, SCHED_EBB = SCHED_RGN << 1, - /* Detach register live information from basic block headers. - This is necessary to invoke functions, that change CFG (e.g. split_edge). - Requires USE_GLAT. */ - DETACH_LIFE_INFO = SCHED_EBB << 1, - /* Save register live information from basic block headers to - glat_{start, end} arrays. */ - USE_GLAT = DETACH_LIFE_INFO << 1 + /* Scheduler can possible create new basic blocks. Used for assertions. */ + NEW_BBS = SCHED_EBB << 1 }; enum SPEC_SCHED_FLAGS { @@ -873,13 +854,8 @@ extern int try_ready (rtx); extern void * xrecalloc (void *, size_t, size_t, size_t); extern void unlink_bb_notes (basic_block, basic_block); extern void add_block (basic_block, basic_block); -extern void attach_life_info (void); extern rtx bb_note (basic_block); -#ifdef ENABLE_CHECKING -extern void check_reg_live (bool); -#endif - /* Functions in sched-rgn.c. */ extern void debug_dependencies (rtx, rtx); diff --git a/gcc/sched-rgn.c b/gcc/sched-rgn.c index 47842c5..aafb812 100644 --- a/gcc/sched-rgn.c +++ b/gcc/sched-rgn.c @@ -1,6 +1,7 @@ /* Instruction scheduling pass. - Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. Contributed by Michael Tiemann (tiemann@cygnus.com) Enhanced by, and currently maintained by, Jim Wilson (wilson@cygnus.com) @@ -67,17 +68,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "target.h" #include "timevar.h" #include "tree-pass.h" - -/* Define when we want to do count REG_DEAD notes before and after scheduling - for sanity checking. We can't do that when conditional execution is used, - as REG_DEAD exist only for unconditional deaths. */ - -#if !defined (HAVE_conditional_execution) && defined (ENABLE_CHECKING) -#define CHECK_DEAD_NOTES 1 -#else -#define CHECK_DEAD_NOTES 0 -#endif - +#include "dbgcnt.h" #ifdef INSN_SCHEDULING /* Some accessor macros for h_i_d members only used within this file. */ @@ -320,8 +311,8 @@ is_cfg_nonregular (void) return 1; /* If we have exception handlers, then we consider the cfg not well - structured. ?!? We should be able to handle this now that flow.c - computes an accurate cfg for EH. */ + structured. ?!? We should be able to handle this now that we + compute an accurate cfg for EH. */ if (current_function_has_exception_handlers ()) return 1; @@ -1040,7 +1031,7 @@ extend_rgns (int *degree, int *idxp, sbitmap header, int *loop_hdr) max_hdr = xmalloc (last_basic_block * sizeof (*max_hdr)); order = xmalloc (last_basic_block * sizeof (*order)); - post_order_compute (order, false); + post_order_compute (order, false, false); for (i = nblocks - 1; i >= 0; i--) { @@ -1500,6 +1491,8 @@ debug_candidates (int trg) /* Functions for speculative scheduling. */ +static bitmap_head not_in_df; + /* Return 0 if x is a set of a register alive in the beginning of one of the split-blocks of src, otherwise return 1. */ @@ -1551,18 +1544,15 @@ check_live_1 (int src, rtx x) for (i = 0; i < candidate_table[src].split_bbs.nr_members; i++) { basic_block b = candidate_table[src].split_bbs.first_member[i]; + int t = bitmap_bit_p (¬_in_df, b->index); /* We can have split blocks, that were recently generated. such blocks are always outside current region. */ - gcc_assert (glat_start[b->index] - || CONTAINING_RGN (b->index) - != CONTAINING_RGN (BB_TO_BLOCK (src))); - if (!glat_start[b->index] - || REGNO_REG_SET_P (glat_start[b->index], - regno + j)) - { - return 0; - } + gcc_assert (!t || (CONTAINING_RGN (b->index) + != CONTAINING_RGN (BB_TO_BLOCK (src)))); + + if (t || REGNO_REG_SET_P (DF_LIVE_IN (b), regno + j)) + return 0; } } } @@ -1572,15 +1562,13 @@ check_live_1 (int src, rtx x) for (i = 0; i < candidate_table[src].split_bbs.nr_members; i++) { basic_block b = candidate_table[src].split_bbs.first_member[i]; + int t = bitmap_bit_p (¬_in_df, b->index); - gcc_assert (glat_start[b->index] - || CONTAINING_RGN (b->index) - != CONTAINING_RGN (BB_TO_BLOCK (src))); - if (!glat_start[b->index] - || REGNO_REG_SET_P (glat_start[b->index], regno)) - { - return 0; - } + gcc_assert (!t || (CONTAINING_RGN (b->index) + != CONTAINING_RGN (BB_TO_BLOCK (src)))); + + if (t || REGNO_REG_SET_P (DF_LIVE_IN (b), regno)) + return 0; } } } @@ -1636,7 +1624,7 @@ update_live_1 (int src, rtx x) { basic_block b = candidate_table[src].update_bbs.first_member[i]; - SET_REGNO_REG_SET (glat_start[b->index], regno + j); + SET_REGNO_REG_SET (DF_LIVE_IN (b), regno + j); } } } @@ -1646,7 +1634,7 @@ update_live_1 (int src, rtx x) { basic_block b = candidate_table[src].update_bbs.first_member[i]; - SET_REGNO_REG_SET (glat_start[b->index], regno); + SET_REGNO_REG_SET (DF_LIVE_IN (b), regno); } } } @@ -1940,10 +1928,6 @@ static void extend_regions (void); static void add_block1 (basic_block, basic_block); static void fix_recovery_cfg (int, int, int); static basic_block advance_target_bb (basic_block, rtx); -static void check_dead_notes1 (int, sbitmap); -#ifdef ENABLE_CHECKING -static int region_head_or_leaf_p (basic_block, int); -#endif static void debug_rgn_dependencies (int); @@ -2211,13 +2195,7 @@ static struct sched_info region_sched_info = add_block1, advance_target_bb, fix_recovery_cfg, -#ifdef ENABLE_CHECKING - region_head_or_leaf_p, -#endif - SCHED_RGN | USE_GLAT -#ifdef ENABLE_CHECKING - | DETACH_LIFE_INFO -#endif + SCHED_RGN }; /* Determine if PAT sets a CLASS_LIKELY_SPILLED_P register. */ @@ -2826,9 +2804,16 @@ schedule_region (int rgn) current_sched_info->queue_must_finish_empty = current_nr_blocks == 1; curr_bb = first_bb; - schedule_block (&curr_bb, rgn_n_insns); - gcc_assert (EBB_FIRST_BB (bb) == first_bb); - sched_rgn_n_insns += sched_n_insns; + if (dbg_cnt (sched_block)) + { + schedule_block (&curr_bb, rgn_n_insns); + gcc_assert (EBB_FIRST_BB (bb) == first_bb); + sched_rgn_n_insns += sched_n_insns; + } + else + { + sched_rgn_n_insns += rgn_n_insns; + } /* Clean up. */ if (current_nr_blocks > 1) @@ -2855,18 +2840,11 @@ schedule_region (int rgn) } } -/* Indexed by region, holds the number of death notes found in that region. - Used for consistency checks. */ -static int *deaths_in_region; - /* Initialize data structures for region scheduling. */ static void init_regions (void) { - sbitmap blocks; - int rgn; - nr_regions = 0; rgn_table = 0; rgn_bb_table = 0; @@ -2894,25 +2872,11 @@ init_regions (void) debug_regions (); /* For now. This will move as more and more of haifa is converted - to using the cfg code in flow.c. */ + to using the cfg code. */ free_dominance_info (CDI_DOMINATORS); } RGN_BLOCKS (nr_regions) = RGN_BLOCKS (nr_regions - 1) + RGN_NR_BLOCKS (nr_regions - 1); - - - if (CHECK_DEAD_NOTES) - { - blocks = sbitmap_alloc (last_basic_block); - deaths_in_region = XNEWVEC (int, nr_regions); - /* Remove all death notes from the subroutine. */ - for (rgn = 0; rgn < nr_regions; rgn++) - check_dead_notes1 (rgn, blocks); - - sbitmap_free (blocks); - } - else - count_or_remove_death_notes (NULL, 1); } /* The one entry point in this file. */ @@ -2920,10 +2884,7 @@ init_regions (void) void schedule_insns (void) { - sbitmap large_region_blocks, blocks; int rgn; - int any_large_regions; - basic_block bb; /* Taking care of this degenerate case makes the rest of this code simpler. */ @@ -2937,8 +2898,16 @@ schedule_insns (void) invoked via sched_init. */ current_sched_info = ®ion_sched_info; + df_set_flags (DF_LR_RUN_DCE); + df_note_add_problem (); + df_analyze (); + regstat_compute_calls_crossed (); + sched_init (); + bitmap_initialize (¬_in_df, 0); + bitmap_clear (¬_in_df); + min_spec_prob = ((PARAM_VALUE (PARAM_MIN_SPEC_PROB) * REG_BR_PROB_BASE) / 100); @@ -2950,92 +2919,14 @@ schedule_insns (void) /* Schedule every region in the subroutine. */ for (rgn = 0; rgn < nr_regions; rgn++) - schedule_region (rgn); + if (dbg_cnt (sched_region)) + schedule_region (rgn); free(ebb_head); - - /* Update life analysis for the subroutine. Do single block regions - first so that we can verify that live_at_start didn't change. Then - do all other blocks. */ - /* ??? There is an outside possibility that update_life_info, or more - to the point propagate_block, could get called with nonzero flags - more than once for one basic block. This would be kinda bad if it - were to happen, since REG_INFO would be accumulated twice for the - block, and we'd have twice the REG_DEAD notes. - - I'm fairly certain that this _shouldn't_ happen, since I don't think - that live_at_start should change at region heads. Not sure what the - best way to test for this kind of thing... */ - - if (current_sched_info->flags & DETACH_LIFE_INFO) - /* this flag can be set either by the target or by ENABLE_CHECKING. */ - attach_life_info (); - - allocate_reg_life_data (); - - any_large_regions = 0; - large_region_blocks = sbitmap_alloc (last_basic_block); - sbitmap_zero (large_region_blocks); - FOR_EACH_BB (bb) - SET_BIT (large_region_blocks, bb->index); - - blocks = sbitmap_alloc (last_basic_block); - sbitmap_zero (blocks); - - /* Update life information. For regions consisting of multiple blocks - we've possibly done interblock scheduling that affects global liveness. - For regions consisting of single blocks we need to do only local - liveness. */ - for (rgn = 0; rgn < nr_regions; rgn++) - if (RGN_NR_BLOCKS (rgn) > 1 - /* Or the only block of this region has been split. */ - || RGN_HAS_REAL_EBB (rgn) - /* New blocks (e.g. recovery blocks) should be processed - as parts of large regions. */ - || !glat_start[rgn_bb_table[RGN_BLOCKS (rgn)]]) - any_large_regions = 1; - else - { - SET_BIT (blocks, rgn_bb_table[RGN_BLOCKS (rgn)]); - RESET_BIT (large_region_blocks, rgn_bb_table[RGN_BLOCKS (rgn)]); - } - - /* Don't update reg info after reload, since that affects - regs_ever_live, which should not change after reload. */ - update_life_info (blocks, UPDATE_LIFE_LOCAL, - (reload_completed ? PROP_DEATH_NOTES - : (PROP_DEATH_NOTES | PROP_REG_INFO))); - if (any_large_regions) - { - update_life_info (large_region_blocks, UPDATE_LIFE_GLOBAL, - (reload_completed ? PROP_DEATH_NOTES - : (PROP_DEATH_NOTES | PROP_REG_INFO))); - -#ifdef ENABLE_CHECKING - check_reg_live (true); -#endif - } - - if (CHECK_DEAD_NOTES) - { - /* Verify the counts of basic block notes in single basic block - regions. */ - for (rgn = 0; rgn < nr_regions; rgn++) - if (RGN_NR_BLOCKS (rgn) == 1) - { - sbitmap_zero (blocks); - SET_BIT (blocks, rgn_bb_table[RGN_BLOCKS (rgn)]); - - gcc_assert (deaths_in_region[rgn] - == count_or_remove_death_notes (blocks, 0)); - } - free (deaths_in_region); - } - /* Reposition the prologue and epilogue notes in case we moved the prologue/epilogue insns. */ if (reload_completed) - reposition_prologue_and_epilogue_notes (get_insns ()); + reposition_prologue_and_epilogue_notes (); if (sched_verbose) { @@ -3056,10 +2947,11 @@ schedule_insns (void) free (block_to_bb); free (containing_rgn); - sched_finish (); + regstat_free_calls_crossed (); + + bitmap_clear (¬_in_df); - sbitmap_free (blocks); - sbitmap_free (large_region_blocks); + sched_finish (); } /* INSN has been added to/removed from current region. */ @@ -3096,6 +2988,8 @@ add_block1 (basic_block bb, basic_block after) { extend_regions (); + bitmap_set_bit (¬_in_df, bb->index); + if (after == 0 || after == EXIT_BLOCK_PTR) { int i; @@ -3113,17 +3007,6 @@ add_block1 (basic_block bb, basic_block after) nr_regions++; RGN_BLOCKS (nr_regions) = i + 1; - - if (CHECK_DEAD_NOTES) - { - sbitmap blocks = sbitmap_alloc (last_basic_block); - deaths_in_region = xrealloc (deaths_in_region, nr_regions * - sizeof (*deaths_in_region)); - - check_dead_notes1 (nr_regions - 1, blocks); - - sbitmap_free (blocks); - } } else { @@ -3176,9 +3059,6 @@ add_block1 (basic_block bb, basic_block after) for (++i; i <= nr_regions; i++) RGN_BLOCKS (i)++; - - /* We don't need to call check_dead_notes1 () because this new block - is just a split of the old. We don't want to count anything twice. */ } } @@ -3228,56 +3108,13 @@ advance_target_bb (basic_block bb, rtx insn) return bb->next_bb; } -/* Count and remove death notes in region RGN, which consists of blocks - with indecies in BLOCKS. */ -static void -check_dead_notes1 (int rgn, sbitmap blocks) -{ - int b; - - sbitmap_zero (blocks); - for (b = RGN_NR_BLOCKS (rgn) - 1; b >= 0; --b) - SET_BIT (blocks, rgn_bb_table[RGN_BLOCKS (rgn) + b]); - - deaths_in_region[rgn] = count_or_remove_death_notes (blocks, 1); -} - -#ifdef ENABLE_CHECKING -/* Return non zero, if BB is head or leaf (depending of LEAF_P) block in - current region. For more information please refer to - sched-int.h: struct sched_info: region_head_or_leaf_p. */ -static int -region_head_or_leaf_p (basic_block bb, int leaf_p) -{ - if (!leaf_p) - return bb->index == rgn_bb_table[RGN_BLOCKS (CONTAINING_RGN (bb->index))]; - else - { - int i; - edge e; - edge_iterator ei; - - i = CONTAINING_RGN (bb->index); - - FOR_EACH_EDGE (e, ei, bb->succs) - if (e->dest != EXIT_BLOCK_PTR - && CONTAINING_RGN (e->dest->index) == i - /* except self-loop. */ - && e->dest != bb) - return 0; - - return 1; - } -} -#endif /* ENABLE_CHECKING */ - #endif static bool gate_handle_sched (void) { #ifdef INSN_SCHEDULING - return flag_schedule_insns; + return flag_schedule_insns && dbg_cnt (sched_func); #else return 0; #endif @@ -3288,9 +3125,6 @@ static unsigned int rest_of_handle_sched (void) { #ifdef INSN_SCHEDULING - /* Do control and data sched analysis, - and write some of the results to dump file. */ - schedule_insns (); #endif return 0; @@ -3300,7 +3134,8 @@ static bool gate_handle_sched2 (void) { #ifdef INSN_SCHEDULING - return optimize > 0 && flag_schedule_insns_after_reload; + return optimize > 0 && flag_schedule_insns_after_reload + && dbg_cnt (sched2_func); #else return 0; #endif @@ -3313,17 +3148,8 @@ rest_of_handle_sched2 (void) #ifdef INSN_SCHEDULING /* Do control and data sched analysis again, and write some more of the results to dump file. */ - - split_all_insns (1); - if (flag_sched2_use_superblocks || flag_sched2_use_traces) - { - schedule_ebbs (); - /* No liveness updating code yet, but it should be easy to do. - reg-stack recomputes the liveness when needed for now. */ - count_or_remove_death_notes (NULL, 1); - cleanup_cfg (CLEANUP_EXPENSIVE); - } + schedule_ebbs (); else schedule_insns (); #endif @@ -3343,7 +3169,9 @@ struct tree_opt_pass pass_sched = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func | + TODO_verify_flow | TODO_ggc_collect, /* todo_flags_finish */ 'S' /* letter */ }; @@ -3361,7 +3189,9 @@ struct tree_opt_pass pass_sched2 = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func | + TODO_verify_flow | TODO_ggc_collect, /* todo_flags_finish */ 'R' /* letter */ }; diff --git a/gcc/sched-vis.c b/gcc/sched-vis.c index 6c97570..7fa0038 100644 --- a/gcc/sched-vis.c +++ b/gcc/sched-vis.c @@ -1,6 +1,6 @@ /* Instruction scheduling pass. - Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Michael Tiemann (tiemann@cygnus.com) Enhanced by, and currently maintained by, Jim Wilson (wilson@cygnus.com) @@ -331,6 +331,18 @@ print_exp (char *buf, rtx x, int verbose) op[0] = XEXP (x, 0); st[1] = "++"; break; + case PRE_MODIFY: + st[0] = "pre "; + op[0] = XEXP (XEXP (x, 1), 0); + st[1] = "+="; + op[1] = XEXP (XEXP (x, 1), 1); + break; + case POST_MODIFY: + st[0] = "post "; + op[0] = XEXP (XEXP (x, 1), 0); + st[1] = "+="; + op[1] = XEXP (XEXP (x, 1), 1); + break; case CALL: st[0] = "call "; op[0] = XEXP (x, 0); @@ -1,5 +1,5 @@ /* Sign extension elimination optimization for GNU compiler. - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Leehod Baruch <leehod@il.ibm.com> This file is part of GCC. @@ -479,6 +479,7 @@ The implementation consists of four data structures: #include "regs.h" #include "timevar.h" #include "tree-pass.h" +#include "dce.h" /* Used to classify defs and uses according to relevancy. */ enum entry_type { @@ -611,9 +612,6 @@ struct see_mentioned_reg_data bool mentioned; }; -/* A data flow object that will be created once and used throughout the - optimization. */ -static struct df *df = NULL; /* An array of web_entries. The i'th definition in the df object is associated with def_entry[i] */ static struct web_entry *def_entry = NULL; @@ -1330,21 +1328,29 @@ see_free_data_structures (void) static void see_initialize_data_structures (void) { + unsigned int max_reg = max_reg_num (); + unsigned int i; + /* Build the df object. */ - df = df_init (DF_HARD_REGS | DF_EQUIV_NOTES | DF_SUBREGS); - df_rd_add_problem (df, 0); - df_chain_add_problem (df, DF_DU_CHAIN | DF_UD_CHAIN); - df_analyze (df); + df_set_flags (DF_EQ_NOTES); + df_chain_add_problem (DF_DU_CHAIN + DF_UD_CHAIN); + df_analyze (); + df_set_flags (DF_DEFER_INSN_RESCAN); if (dump_file) - df_dump (df, dump_file); + df_dump (dump_file); /* Record the last basic block at the beginning of the optimization. */ last_bb = last_basic_block; - /* Record the number of uses at the beginning of the optimization. */ - uses_num = DF_USES_SIZE (df); - /* Record the number of definitions at the beginning of the optimization. */ - defs_num = DF_DEFS_SIZE (df); + + /* Record the number of uses and defs at the beginning of the optimization. */ + uses_num = 0; + defs_num = 0; + for (i = 0; i < max_reg; i++) + { + uses_num += DF_REG_USE_COUNT (i) + DF_REG_EQ_USE_COUNT (i); + defs_num += DF_REG_DEF_COUNT (i); + } /* Allocate web entries array for the union-find data structure. */ def_entry = xcalloc (defs_num, sizeof (struct web_entry)); @@ -1580,7 +1586,7 @@ see_emit_use_extension (void **slot, void *b) print_rtl_single (dump_file, use_se); } - add_insn_before (use_se, curr_ref_s->insn); + add_insn_before (use_se, curr_ref_s->insn, NULL); return 1; } @@ -1801,11 +1807,6 @@ see_commit_changes (void) the first place. */ htab_traverse (see_pre_extension_hash, see_pre_delete_extension, NULL); - /* At this point, we must free the DF object, since the number of basic blocks - may change. */ - df_finish (df); - df = NULL; - /* Insert extensions on edges, according to the LCM result. */ did_insert = see_pre_insert_extensions (index_map); @@ -1875,7 +1876,7 @@ see_analyze_merged_def_local_prop (void **slot, void *b) /* Reset the killed bit. */ RESET_BIT (ae_kill[bb_num], indx); - if (curr_prop->first_se_after_last_def == DF_INSN_LUID (df, ref)) + if (curr_prop->first_se_after_last_def == DF_INSN_LUID (ref)) { /* Set the available bit. */ SET_BIT (comp[bb_num], indx); @@ -1985,7 +1986,7 @@ see_analyze_use_local_prop (void **slot, void *b) indx = extension_expr->bitmap_index; - if (curr_prop->first_se_before_any_def == DF_INSN_LUID (df, ref)) + if (curr_prop->first_se_before_any_def == DF_INSN_LUID (ref)) { /* Set the anticipatable bit. */ SET_BIT (antloc[bb_num], indx); @@ -2025,7 +2026,7 @@ see_analyze_use_local_prop (void **slot, void *b) /* Note: there is no need to reset the killed bit since it must be zero at this point. */ } - else if (curr_prop->first_se_after_last_def == DF_INSN_LUID (df, ref)) + else if (curr_prop->first_se_after_last_def == DF_INSN_LUID (ref)) { /* Set the available bit. */ SET_BIT (comp[bb_num], indx); @@ -2163,7 +2164,7 @@ see_set_prop_merged_def (void **slot, void *b) struct see_register_properties *curr_prop = NULL; struct see_register_properties **slot_prop; struct see_register_properties temp_prop; - int ref_luid = DF_INSN_LUID (df, insn); + int ref_luid = DF_INSN_LUID (insn); curr_bb_hash = see_bb_hash_ar[BLOCK_NUM (curr_ref_s->insn)]; if (!curr_bb_hash) @@ -2234,7 +2235,7 @@ see_set_prop_unmerged_def (void **slot, void *b) struct see_register_properties *curr_prop = NULL; struct see_register_properties **slot_prop; struct see_register_properties temp_prop; - int ref_luid = DF_INSN_LUID (df, insn); + int ref_luid = DF_INSN_LUID (insn); curr_bb_hash = see_bb_hash_ar[BLOCK_NUM (curr_ref_s->insn)]; if (!curr_bb_hash) @@ -2308,7 +2309,7 @@ see_set_prop_unmerged_use (void **slot, void *b) struct see_register_properties **slot_prop; struct see_register_properties temp_prop; bool locally_redundant = false; - int ref_luid = DF_INSN_LUID (df, insn); + int ref_luid = DF_INSN_LUID (insn); curr_bb_hash = see_bb_hash_ar[BLOCK_NUM (curr_ref_s->insn)]; if (!curr_bb_hash) @@ -3057,7 +3058,7 @@ see_store_reference_and_extension (rtx ref_insn, rtx se_insn, in it. */ { stn = splay_tree_lookup (see_bb_splay_ar[curr_bb_num], - DF_INSN_LUID (df, ref_insn)); + DF_INSN_LUID (ref_insn)); if (stn) switch (type) { @@ -3107,7 +3108,7 @@ see_store_reference_and_extension (rtx ref_insn, rtx se_insn, if (!stn) { ref_s = xmalloc (sizeof (struct see_ref_s)); - ref_s->luid = DF_INSN_LUID (df, ref_insn); + ref_s->luid = DF_INSN_LUID (ref_insn); ref_s->insn = ref_insn; ref_s->merged_insn = NULL; @@ -3160,7 +3161,7 @@ see_store_reference_and_extension (rtx ref_insn, rtx se_insn, /* If this is a new reference, insert it into the splay_tree. */ if (!stn) splay_tree_insert (see_bb_splay_ar[curr_bb_num], - DF_INSN_LUID (df, ref_insn), (splay_tree_value) ref_s); + DF_INSN_LUID (ref_insn), (splay_tree_value) ref_s); return true; } @@ -3176,84 +3177,67 @@ see_store_reference_and_extension (rtx ref_insn, rtx se_insn, happened and the optimization should be aborted. */ static int -see_handle_relevant_defs (void) +see_handle_relevant_defs (struct df_ref *ref, rtx insn) { - rtx insn = NULL; - rtx se_insn = NULL; - rtx reg = NULL; - rtx ref_insn = NULL; struct web_entry *root_entry = NULL; - unsigned int i; - int num_relevant_defs = 0; + rtx se_insn = NULL; enum rtx_code extension_code; + rtx reg = DF_REF_REAL_REG (ref); + rtx ref_insn = NULL; + unsigned int i = DF_REF_ID (ref); - for (i = 0; i < defs_num; i++) - { - insn = DF_REF_INSN (DF_DEFS_GET (df, i)); - reg = DF_REF_REAL_REG (DF_DEFS_GET (df, i)); - - if (!insn) - continue; - - if (!INSN_P (insn)) - continue; - - root_entry = unionfind_root (&def_entry[i]); - - if (ENTRY_EI (root_entry)->relevancy != SIGN_EXTENDED_DEF - && ENTRY_EI (root_entry)->relevancy != ZERO_EXTENDED_DEF) - /* The current web is not relevant. Continue to the next def. */ - continue; + root_entry = unionfind_root (&def_entry[DF_REF_ID (ref)]); - if (root_entry->reg) - /* It isn't possible to have two different register for the same - web. */ - gcc_assert (rtx_equal_p (root_entry->reg, reg)); + if (ENTRY_EI (root_entry)->relevancy != SIGN_EXTENDED_DEF + && ENTRY_EI (root_entry)->relevancy != ZERO_EXTENDED_DEF) + /* The current web is not relevant. Continue to the next def. */ + return 0; + + if (root_entry->reg) + /* It isn't possible to have two different register for the same + web. */ + gcc_assert (rtx_equal_p (root_entry->reg, reg)); + else + root_entry->reg = reg; + + /* The current definition is an EXTENDED_DEF or a definition that its + source_mode is narrower then its web's source_mode. + This means that we need to generate the implicit extension explicitly + and store it in the current reference's merged_def_se_hash. */ + if (ENTRY_EI (&def_entry[i])->local_relevancy == EXTENDED_DEF + || (ENTRY_EI (&def_entry[i])->local_source_mode < + ENTRY_EI (root_entry)->source_mode)) + { + + if (ENTRY_EI (root_entry)->relevancy == SIGN_EXTENDED_DEF) + extension_code = SIGN_EXTEND; else - root_entry->reg = reg; - - /* The current definition is an EXTENDED_DEF or a definition that its - source_mode is narrower then its web's source_mode. - This means that we need to generate the implicit extension explicitly - and store it in the current reference's merged_def_se_hash. */ - if (ENTRY_EI (&def_entry[i])->local_relevancy == EXTENDED_DEF - || (ENTRY_EI (&def_entry[i])->local_source_mode < - ENTRY_EI (root_entry)->source_mode)) - { - num_relevant_defs++; - - if (ENTRY_EI (root_entry)->relevancy == SIGN_EXTENDED_DEF) - extension_code = SIGN_EXTEND; - else - extension_code = ZERO_EXTEND; - - se_insn = - see_gen_normalized_extension (reg, extension_code, - ENTRY_EI (root_entry)->source_mode); - - /* This is a dummy extension, mark it as deleted. */ - INSN_DELETED_P (se_insn) = 1; - - if (!see_store_reference_and_extension (insn, se_insn, - IMPLICIT_DEF_EXTENSION)) - /* Something bad happened. Abort the optimization. */ - return -1; - continue; - } - - ref_insn = PREV_INSN (insn); - gcc_assert (BLOCK_NUM (ref_insn) == BLOCK_NUM (insn)); - - num_relevant_defs++; - - if (!see_store_reference_and_extension (ref_insn, insn, - EXPLICIT_DEF_EXTENSION)) + extension_code = ZERO_EXTEND; + + se_insn = + see_gen_normalized_extension (reg, extension_code, + ENTRY_EI (root_entry)->source_mode); + + /* This is a dummy extension, mark it as deleted. */ + INSN_DELETED_P (se_insn) = 1; + + if (!see_store_reference_and_extension (insn, se_insn, + IMPLICIT_DEF_EXTENSION)) /* Something bad happened. Abort the optimization. */ return -1; + return 1; } - return num_relevant_defs; -} + + ref_insn = PREV_INSN (insn); + gcc_assert (BLOCK_NUM (ref_insn) == BLOCK_NUM (insn)); + + if (!see_store_reference_and_extension (ref_insn, insn, + EXPLICIT_DEF_EXTENSION)) + /* Something bad happened. Abort the optimization. */ + return -1; + return 0; +} /* Go over all the uses, for each use in relevant web store its instruction as a reference and generate an extension before it. @@ -3262,120 +3246,122 @@ see_handle_relevant_defs (void) happened and the optimization should be aborted. */ static int -see_handle_relevant_uses (void) +see_handle_relevant_uses (struct df_ref *ref, rtx insn) { - rtx insn = NULL; - rtx reg = NULL; struct web_entry *root_entry = NULL; rtx se_insn = NULL; - unsigned int i; - int num_relevant_uses = 0; enum rtx_code extension_code; + rtx reg = DF_REF_REAL_REG (ref); - for (i = 0; i < uses_num; i++) - { - insn = DF_REF_INSN (DF_USES_GET (df, i)); - reg = DF_REF_REAL_REG (DF_USES_GET (df, i)); - - if (!insn) - continue; - - if (!INSN_P (insn)) - continue; - - root_entry = unionfind_root (&use_entry[i]); - - if (ENTRY_EI (root_entry)->relevancy != SIGN_EXTENDED_DEF - && ENTRY_EI (root_entry)->relevancy != ZERO_EXTENDED_DEF) - /* The current web is not relevant. Continue to the next use. */ - continue; - - if (root_entry->reg) - /* It isn't possible to have two different register for the same - web. */ - gcc_assert (rtx_equal_p (root_entry->reg, reg)); - else - root_entry->reg = reg; - - /* Generate the use extension. */ - if (ENTRY_EI (root_entry)->relevancy == SIGN_EXTENDED_DEF) - extension_code = SIGN_EXTEND; - else - extension_code = ZERO_EXTEND; + root_entry = unionfind_root (&use_entry[DF_REF_ID (ref)]); + + if (ENTRY_EI (root_entry)->relevancy != SIGN_EXTENDED_DEF + && ENTRY_EI (root_entry)->relevancy != ZERO_EXTENDED_DEF) + /* The current web is not relevant. Continue to the next use. */ + return 0; + + if (root_entry->reg) + /* It isn't possible to have two different register for the same + web. */ + gcc_assert (rtx_equal_p (root_entry->reg, reg)); + else + root_entry->reg = reg; + + /* Generate the use extension. */ + if (ENTRY_EI (root_entry)->relevancy == SIGN_EXTENDED_DEF) + extension_code = SIGN_EXTEND; + else + extension_code = ZERO_EXTEND; + + se_insn = + see_gen_normalized_extension (reg, extension_code, + ENTRY_EI (root_entry)->source_mode); + if (!se_insn) + /* This is very bad, abort the transformation. */ + return -1; + + if (!see_store_reference_and_extension (insn, se_insn, + USE_EXTENSION)) + /* Something bad happened. Abort the optimization. */ + return -1; + return 1; +} - se_insn = - see_gen_normalized_extension (reg, extension_code, - ENTRY_EI (root_entry)->source_mode); - if (!se_insn) - /* This is very bad, abort the transformation. */ - return -1; +static int +see_handle_relevant_refs (void) +{ + int num_relevant_refs = 0; + basic_block bb; - num_relevant_uses++; + FOR_ALL_BB (bb) + { + rtx insn; + FOR_BB_INSNS (bb, insn) + { + unsigned int uid = INSN_UID (insn); - if (!see_store_reference_and_extension (insn, se_insn, - USE_EXTENSION)) - /* Something bad happened. Abort the optimization. */ - return -1; + if (INSN_P (insn)) + { + struct df_ref **use_rec; + struct df_ref **def_rec; + + for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + int result = see_handle_relevant_uses (use, insn); + if (result == -1) + return -1; + num_relevant_refs += result; + } + for (use_rec = DF_INSN_UID_EQ_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + int result = see_handle_relevant_uses (use, insn); + if (result == -1) + return -1; + num_relevant_refs += result; + } + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + int result = see_handle_relevant_defs (def, insn); + if (result == -1) + return -1; + num_relevant_refs += result; + } + } + } } - - return num_relevant_uses; + return num_relevant_refs; } -/* Updates the relevancy of all the uses. - The information of the i'th use is stored in use_entry[i]. - Currently all the uses are relevant for the optimization except for uses that - are in LIBCALL or RETVAL instructions. */ +/* Initialized the use_entry field for REF in INSN at INDEX with ET. */ static void -see_update_uses_relevancy (void) +see_update_uses_relevancy (rtx insn, struct df_ref *ref, + enum entry_type et, unsigned int index) { - rtx insn = NULL; - rtx reg = NULL; struct see_entry_extra_info *curr_entry_extra_info; - enum entry_type et; - unsigned int i; - - if (!df || !use_entry) - return; - for (i = 0; i < uses_num; i++) + if (dump_file) { - - insn = DF_REF_INSN (DF_USES_GET (df, i)); - reg = DF_REF_REAL_REG (DF_USES_GET (df, i)); - - et = RELEVANT_USE; - - if (insn) - { - if (!INSN_P (insn)) - et = NOT_RELEVANT; - if (insn && find_reg_note (insn, REG_LIBCALL, NULL_RTX)) - et = NOT_RELEVANT; - if (find_reg_note (insn, REG_RETVAL, NULL_RTX)) - et = NOT_RELEVANT; - } + rtx reg = DF_REF_REAL_REG (ref); + fprintf (dump_file, "u%i insn %i reg %i ", + index, (insn ? INSN_UID (insn) : -1), REGNO (reg)); + if (et == NOT_RELEVANT) + fprintf (dump_file, "NOT RELEVANT \n"); else - et = NOT_RELEVANT; - - if (dump_file) - { - fprintf (dump_file, "u%i insn %i reg %i ", - i, (insn ? INSN_UID (insn) : -1), REGNO (reg)); - if (et == NOT_RELEVANT) - fprintf (dump_file, "NOT RELEVANT \n"); - else - fprintf (dump_file, "RELEVANT USE \n"); - } - - curr_entry_extra_info = xmalloc (sizeof (struct see_entry_extra_info)); - curr_entry_extra_info->relevancy = et; - curr_entry_extra_info->local_relevancy = et; - use_entry[i].extra_info = curr_entry_extra_info; - use_entry[i].reg = NULL; - use_entry[i].pred = NULL; + fprintf (dump_file, "RELEVANT USE \n"); } + + DF_REF_ID (ref) = index; + curr_entry_extra_info = xmalloc (sizeof (struct see_entry_extra_info)); + curr_entry_extra_info->relevancy = et; + curr_entry_extra_info->local_relevancy = et; + use_entry[index].extra_info = curr_entry_extra_info; + use_entry[index].reg = NULL; + use_entry[index].pred = NULL; } @@ -3434,12 +3420,6 @@ see_analyze_one_def (rtx insn, enum machine_mode *source_mode, *source_mode = MAX_MACHINE_MODE; *source_mode_unsigned = MAX_MACHINE_MODE; - if (!insn) - return NOT_RELEVANT; - - if (!INSN_P (insn)) - return NOT_RELEVANT; - extension_code = see_get_extension_data (insn, source_mode); switch (extension_code) { @@ -3546,93 +3526,163 @@ see_analyze_one_def (rtx insn, enum machine_mode *source_mode, } -/* Updates the relevancy and source_mode of all the definitions. - The information of the i'th definition is stored in def_entry[i]. */ +/* Initialized the def_entry field for REF in INSN at INDEX with ET. */ static void -see_update_defs_relevancy (void) +see_update_defs_relevancy (rtx insn, struct df_ref *ref, + enum entry_type et, + enum machine_mode source_mode, + enum machine_mode source_mode_unsigned, + unsigned int index) { - struct see_entry_extra_info *curr_entry_extra_info; - unsigned int i; - rtx insn = NULL; - rtx reg = NULL; - enum entry_type et; - enum machine_mode source_mode; - enum machine_mode source_mode_unsigned; + struct see_entry_extra_info *curr_entry_extra_info + = xmalloc (sizeof (struct see_entry_extra_info)); + curr_entry_extra_info->relevancy = et; + curr_entry_extra_info->local_relevancy = et; - if (!df || !def_entry) - return; + DF_REF_ID (ref) = index; - for (i = 0; i < defs_num; i++) + if (et != EXTENDED_DEF) { - insn = DF_REF_INSN (DF_DEFS_GET (df, i)); - reg = DF_REF_REAL_REG (DF_DEFS_GET (df, i)); - - et = see_analyze_one_def (insn, &source_mode, &source_mode_unsigned); - - curr_entry_extra_info = xmalloc (sizeof (struct see_entry_extra_info)); - curr_entry_extra_info->relevancy = et; - curr_entry_extra_info->local_relevancy = et; - if (et != EXTENDED_DEF) + curr_entry_extra_info->source_mode = source_mode; + curr_entry_extra_info->local_source_mode = source_mode; + } + else + { + curr_entry_extra_info->source_mode_signed = source_mode; + curr_entry_extra_info->source_mode_unsigned = source_mode_unsigned; + } + def_entry[index].extra_info = curr_entry_extra_info; + def_entry[index].reg = NULL; + def_entry[index].pred = NULL; + + if (dump_file) + { + rtx reg = DF_REF_REAL_REG (ref); + if (et == NOT_RELEVANT) { - curr_entry_extra_info->source_mode = source_mode; - curr_entry_extra_info->local_source_mode = source_mode; + fprintf (dump_file, "d%i insn %i reg %i ", + index, (insn ? INSN_UID (insn) : -1), REGNO (reg)); + fprintf (dump_file, "NOT RELEVANT \n"); } else { - curr_entry_extra_info->source_mode_signed = source_mode; - curr_entry_extra_info->source_mode_unsigned = source_mode_unsigned; + fprintf (dump_file, "d%i insn %i reg %i ", + index, INSN_UID (insn), REGNO (reg)); + fprintf (dump_file, "RELEVANT - "); + switch (et) + { + case SIGN_EXTENDED_DEF : + fprintf (dump_file, "SIGN_EXTENDED_DEF, source_mode = %s\n", + GET_MODE_NAME (source_mode)); + break; + case ZERO_EXTENDED_DEF : + fprintf (dump_file, "ZERO_EXTENDED_DEF, source_mode = %s\n", + GET_MODE_NAME (source_mode)); + break; + case EXTENDED_DEF : + fprintf (dump_file, "EXTENDED_DEF, "); + if (source_mode != MAX_MACHINE_MODE + && source_mode_unsigned != MAX_MACHINE_MODE) + { + fprintf (dump_file, "positive const, "); + fprintf (dump_file, "source_mode_signed = %s, ", + GET_MODE_NAME (source_mode)); + fprintf (dump_file, "source_mode_unsigned = %s\n", + GET_MODE_NAME (source_mode_unsigned)); + } + else if (source_mode != MAX_MACHINE_MODE) + fprintf (dump_file, "source_mode_signed = %s\n", + GET_MODE_NAME (source_mode)); + else + fprintf (dump_file, "source_mode_unsigned = %s\n", + GET_MODE_NAME (source_mode_unsigned)); + break; + default : + gcc_unreachable (); + } } - def_entry[i].extra_info = curr_entry_extra_info; - def_entry[i].reg = NULL; - def_entry[i].pred = NULL; + } +} - if (dump_file) + +/* Updates the relevancy of all the uses and all defs. + + The information of the u'th use is stored in use_entry[u] and the + information of the d'th definition is stored in def_entry[d]. + + Currently all the uses are relevant for the optimization except for + uses that are in LIBCALL instructions. */ + +static void +see_update_relevancy (void) +{ + unsigned int d = 0; + unsigned int u = 0; + enum entry_type et; + enum machine_mode source_mode; + enum machine_mode source_mode_unsigned; + basic_block bb; + + if (!def_entry) + return; + + FOR_ALL_BB (bb) + { + struct df_ref **use_rec; + struct df_ref **def_rec; + rtx insn; + FOR_BB_INSNS (bb, insn) { - if (et == NOT_RELEVANT) + unsigned int uid = INSN_UID (insn); + if (INSN_P (insn)) { - fprintf (dump_file, "d%i insn %i reg %i ", - i, (insn ? INSN_UID (insn) : -1), REGNO (reg)); - fprintf (dump_file, "NOT RELEVANT \n"); - } - else - { - fprintf (dump_file, "d%i insn %i reg %i ", - i ,INSN_UID (insn), REGNO (reg)); - fprintf (dump_file, "RELEVANT - "); - switch (et) + + /* If this is an insn in a libcall, do not touch the uses. */ + if (find_reg_note (insn, REG_LIBCALL_ID, NULL_RTX)) + et = NOT_RELEVANT; + else + et = RELEVANT_USE; + + for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) { - case SIGN_EXTENDED_DEF : - fprintf (dump_file, "SIGN_EXTENDED_DEF, source_mode = %s\n", - GET_MODE_NAME (source_mode)); - break; - case ZERO_EXTENDED_DEF : - fprintf (dump_file, "ZERO_EXTENDED_DEF, source_mode = %s\n", - GET_MODE_NAME (source_mode)); - break; - case EXTENDED_DEF : - fprintf (dump_file, "EXTENDED_DEF, "); - if (source_mode != MAX_MACHINE_MODE - && source_mode_unsigned != MAX_MACHINE_MODE) - { - fprintf (dump_file, "positive const, "); - fprintf (dump_file, "source_mode_signed = %s, ", - GET_MODE_NAME (source_mode)); - fprintf (dump_file, "source_mode_unsigned = %s\n", - GET_MODE_NAME (source_mode_unsigned)); - } - else if (source_mode != MAX_MACHINE_MODE) - fprintf (dump_file, "source_mode_signed = %s\n", - GET_MODE_NAME (source_mode)); - else - fprintf (dump_file, "source_mode_unsigned = %s\n", - GET_MODE_NAME (source_mode_unsigned)); - break; - default : - gcc_unreachable (); + struct df_ref *use = *use_rec; + see_update_uses_relevancy (insn, use, et, u); + u++; + } + + for (use_rec = DF_INSN_UID_EQ_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + see_update_uses_relevancy (insn, use, et, u); + u++; + } + + et = see_analyze_one_def (insn, &source_mode, &source_mode_unsigned); + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + see_update_defs_relevancy (insn, def, et, source_mode, + source_mode_unsigned, d); + d++; } } } + + for (use_rec = df_get_artificial_uses (bb->index); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + see_update_uses_relevancy (NULL, use, NOT_RELEVANT, u); + u++; + } + + for (def_rec = df_get_artificial_defs (bb->index); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + see_update_defs_relevancy (NULL, def, NOT_RELEVANT, + MAX_MACHINE_MODE, MAX_MACHINE_MODE, d); + d++; + } } } @@ -3648,41 +3698,56 @@ see_update_defs_relevancy (void) static bool see_propagate_extensions_to_uses (void) { - unsigned int i = 0; - int num_relevant_uses; - int num_relevant_defs; + int num_relevant_refs; + basic_block bb; if (dump_file) fprintf (dump_file, "* Phase 1: Propagate extensions to uses. *\n"); /* Update the relevancy of references using the DF object. */ - see_update_defs_relevancy (); - see_update_uses_relevancy (); + see_update_relevancy (); /* Produce the webs and update the extra_info of the root. In general, a web is relevant if all its definitions and uses are relevant and there is at least one definition that was marked as SIGN_EXTENDED_DEF or ZERO_EXTENDED_DEF. */ - for (i = 0; i < uses_num; i++) - union_defs (df, DF_USES_GET (df, i), def_entry, use_entry, - see_update_leader_extra_info); + FOR_ALL_BB (bb) + { + rtx insn; + struct df_ref **use_rec; - /* Generate use extensions for references and insert these - references to see_bb_splay_ar data structure. */ - num_relevant_uses = see_handle_relevant_uses (); + FOR_BB_INSNS (bb, insn) + { + unsigned int uid = INSN_UID (insn); + if (INSN_P (insn)) + { + for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + union_defs (use, def_entry, use_entry, see_update_leader_extra_info); + } + + for (use_rec = DF_INSN_UID_EQ_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + union_defs (use, def_entry, use_entry, see_update_leader_extra_info); + } + } + } - if (num_relevant_uses < 0) - return false; + for (use_rec = df_get_artificial_uses (bb->index); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + union_defs (use, def_entry, use_entry, see_update_leader_extra_info); + } + } - /* Store the def extensions in their references structures and insert these + /* Generate use extensions for references and insert these references to see_bb_splay_ar data structure. */ - num_relevant_defs = see_handle_relevant_defs (); + num_relevant_refs = see_handle_relevant_refs (); - if (num_relevant_defs < 0) - return false; - - return num_relevant_uses > 0 || num_relevant_defs > 0; + return num_relevant_refs > 0; } @@ -3755,12 +3820,7 @@ rest_of_handle_see (void) see_main (); no_new_pseudos = no_new_pseudos_bcp; - delete_trivially_dead_insns (get_insns (), max_reg_num ()); - update_life_info_in_dirty_blocks (UPDATE_LIFE_GLOBAL_RM_NOTES, - (PROP_DEATH_NOTES)); - cleanup_cfg (CLEANUP_EXPENSIVE); - reg_scan (get_insns (), max_reg_num ()); - + run_fast_dce (); return 0; } @@ -3777,6 +3837,7 @@ struct tree_opt_pass pass_see = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func, /* todo_flags_finish */ 'u' /* letter */ }; diff --git a/gcc/stack-ptr-mod.c b/gcc/stack-ptr-mod.c new file mode 100644 index 0000000..d3f282c --- /dev/null +++ b/gcc/stack-ptr-mod.c @@ -0,0 +1,110 @@ +/* Discover if the stack pointer is modified in a function. + Copyright (C) 2007 + 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, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "rtl.h" +#include "regs.h" +#include "expr.h" +#include "tree-pass.h" +#include "basic-block.h" +#include "flags.h" +#include "output.h" +#include "df.h" + +/* Determine if the stack pointer is constant over the life of the function. + Only useful before prologues have been emitted. */ + +static void +notice_stack_pointer_modification_1 (rtx x, rtx pat ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED) +{ + if (x == stack_pointer_rtx + /* The stack pointer is only modified indirectly as the result + of a push until later. See the comments in rtl.texi + regarding Embedded Side-Effects on Addresses. */ + || (MEM_P (x) + && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == RTX_AUTOINC + && XEXP (XEXP (x, 0), 0) == stack_pointer_rtx)) + current_function_sp_is_unchanging = 0; +} + +static void +notice_stack_pointer_modification (void) +{ + basic_block bb; + rtx insn; + + /* Assume that the stack pointer is unchanging if alloca hasn't + been used. */ + current_function_sp_is_unchanging = !current_function_calls_alloca; + if (current_function_sp_is_unchanging) + FOR_EACH_BB (bb) + FOR_BB_INSNS (bb, insn) + { + if (INSN_P (insn)) + { + /* Check if insn modifies the stack pointer. */ + note_stores (PATTERN (insn), + notice_stack_pointer_modification_1, + NULL); + if (! current_function_sp_is_unchanging) + return; + } + } + + /* The value coming into this pass was 0, and the exit block uses + are based on this. If the value is now 1, we need to redo the + exit block uses. */ + if (df && current_function_sp_is_unchanging) + df_update_exit_block_uses (); +} + + /* Some targets can emit simpler epilogues if they know that sp was + not ever modified during the function. After reload, of course, + we've already emitted the epilogue so there's no sense searching. */ + +static unsigned int +rest_of_handle_stack_ptr_mod (void) +{ + notice_stack_pointer_modification (); + return 0; +} + +struct tree_opt_pass pass_stack_ptr_mod = +{ + NULL, /* name */ + NULL, /* gate */ + rest_of_handle_stack_ptr_mod, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ + 0 /* letter */ +}; @@ -1830,12 +1830,10 @@ expand_nl_goto_receiver (void) emit_insn (gen_nonlocal_goto_receiver ()); #endif - /* @@@ This is a kludge. Not all machine descriptions define a blockage - insn, but we must not allow the code we just generated to be reordered - by scheduling. Specifically, the update of the frame pointer must - happen immediately, not later. So emit an ASM_INPUT to act as blockage - insn. */ - emit_insn (gen_rtx_ASM_INPUT (VOIDmode, "")); + /* We must not allow the code we just generated to be reordered by + scheduling. Specifically, the update of the frame pointer must + happen immediately, not later. */ + emit_insn (gen_blockage ()); } /* Generate RTL for the automatic variable declaration DECL. diff --git a/gcc/struct-equiv.c b/gcc/struct-equiv.c index c37378e..3658e87 100644 --- a/gcc/struct-equiv.c +++ b/gcc/struct-equiv.c @@ -1,6 +1,7 @@ /* Control flow optimization code for GNU compiler. Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. This file is part of GCC. @@ -88,6 +89,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "target.h" #include "emit-rtl.h" #include "reload.h" +#include "df.h" static void merge_memattrs (rtx, rtx); static bool set_dest_equiv_p (rtx x, rtx y, struct equiv_info *info); @@ -983,13 +985,8 @@ insns_match_p (rtx i1, rtx i2, struct equiv_info *info) bool struct_equiv_init (int mode, struct equiv_info *info) { - if ((info->x_block->flags | info->y_block->flags) & BB_DIRTY) - update_life_info_in_dirty_blocks (UPDATE_LIFE_GLOBAL_RM_NOTES, - (PROP_DEATH_NOTES - | ((mode & CLEANUP_POST_REGSTACK) - ? PROP_POST_REGSTACK : 0))); - if (!REG_SET_EQUAL_P (info->x_block->il.rtl->global_live_at_end, - info->y_block->il.rtl->global_live_at_end)) + if (!REG_SET_EQUAL_P (DF_LR_OUT (info->x_block), + DF_LR_OUT (info->y_block))) { #ifdef STACK_REGS unsigned rn; @@ -1002,11 +999,11 @@ struct_equiv_init (int mode, struct equiv_info *info) least makes the regsets comparable. */ for (rn = FIRST_STACK_REG; rn <= LAST_STACK_REG; rn++) { - CLEAR_REGNO_REG_SET (info->x_block->il.rtl->global_live_at_end, rn); - CLEAR_REGNO_REG_SET (info->y_block->il.rtl->global_live_at_end, rn); + CLEAR_REGNO_REG_SET (DF_LR_OUT (info->x_block), rn); + CLEAR_REGNO_REG_SET (DF_LR_OUT (info->y_block), rn); } - if (!REG_SET_EQUAL_P (info->x_block->il.rtl->global_live_at_end, - info->y_block->il.rtl->global_live_at_end)) + if (!REG_SET_EQUAL_P (DF_LR_OUT (info->x_block), + DF_LR_OUT (info->y_block))) #endif return false; } @@ -1028,7 +1025,7 @@ struct_equiv_init (int mode, struct equiv_info *info) info->common_live = ALLOC_REG_SET (®_obstack); info->x_local_live = ALLOC_REG_SET (®_obstack); info->y_local_live = ALLOC_REG_SET (®_obstack); - COPY_REG_SET (info->common_live, info->x_block->il.rtl->global_live_at_end); + COPY_REG_SET (info->common_live, DF_LR_OUT (info->x_block)); struct_equiv_make_checkpoint (&info->best_match, info); return true; } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 6c52a1d..4b014c8 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -2650,6 +2650,11 @@ PR tree-optimization/31041 * gcc.dg/vect/pr31041.c: New test. +2007-03-16 Paolo Bonzini <bonzini@gnu.org> + + PR rtl-optimization/31025 + * gfortran.dg/pr31025.f90: New. + 2007-03-16 Manuel Lopez-Ibanez <manu@gcc.gnu.org> * g++.dg/warn/Wconversion-integer.C: New diff --git a/gcc/testsuite/gfortran.dg/pr31025.f90 b/gcc/testsuite/gfortran.dg/pr31025.f90 new file mode 100644 index 0000000..53fecf8 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/pr31025.f90 @@ -0,0 +1,9 @@ +! { dg-options "-O2" } +real*8 function f(x) +t1 = g(0) +if(x .eq. 0) then + f = 0 +else if(x .eq. 1) then + f = t1 *log( t1 ) +end if +end diff --git a/gcc/timevar.def b/gcc/timevar.def index f9690fe..f89a9df 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -57,6 +57,17 @@ DEFTIMEVAR (TV_DELETE_TRIVIALLY_DEAD , "trivially dead code") DEFTIMEVAR (TV_LIFE , "life analysis") DEFTIMEVAR (TV_LIFE_UPDATE , "life info update") +/* Time spent in dataflow problems. */ +DEFTIMEVAR (TV_DF_SCAN , "df scan insns") +DEFTIMEVAR (TV_DF_RU , "df reaching uses") +DEFTIMEVAR (TV_DF_RD , "df reaching defs") +DEFTIMEVAR (TV_DF_LR , "df live regs") +DEFTIMEVAR (TV_DF_LIVE , "df live&initialized regs") +DEFTIMEVAR (TV_DF_UREC , "df uninitialized regs 2") +DEFTIMEVAR (TV_DF_CHAIN , "df use-def / def-use chains") +DEFTIMEVAR (TV_DF_NOTE , "df reg dead/unused notes") +DEFTIMEVAR (TV_REG_STATS , "register information") + DEFTIMEVAR (TV_ALIAS_ANALYSIS , "alias analysis") DEFTIMEVAR (TV_REG_SCAN , "register scan") DEFTIMEVAR (TV_REBUILD_JUMP , "rebuild jump labels") @@ -134,6 +145,9 @@ DEFTIMEVAR (TV_LOWER_SUBREG , "lower subreg") DEFTIMEVAR (TV_JUMP , "jump") DEFTIMEVAR (TV_FWPROP , "forward prop") DEFTIMEVAR (TV_CSE , "CSE") +DEFTIMEVAR (TV_DCE , "dead code elimination") +DEFTIMEVAR (TV_DSE1 , "dead store elim1") +DEFTIMEVAR (TV_DSE2 , "dead store elim2") DEFTIMEVAR (TV_LOOP , "loop analysis") DEFTIMEVAR (TV_GCSE , "global CSE") DEFTIMEVAR (TV_CPROP1 , "CPROP 1") @@ -144,10 +158,10 @@ DEFTIMEVAR (TV_LSM , "LSM") DEFTIMEVAR (TV_BYPASS , "bypass jumps") DEFTIMEVAR (TV_TRACER , "tracer") DEFTIMEVAR (TV_WEB , "web") +DEFTIMEVAR (TV_AUTO_INC_DEC , "auto inc dec") DEFTIMEVAR (TV_CSE2 , "CSE 2") DEFTIMEVAR (TV_BRANCH_PROB , "branch prediction") DEFTIMEVAR (TV_VPT , "value profile opts") -DEFTIMEVAR (TV_FLOW , "flow analysis") DEFTIMEVAR (TV_COMBINE , "combiner") DEFTIMEVAR (TV_IFCVT , "if-conversion") DEFTIMEVAR (TV_SEE , "see") @@ -160,7 +174,7 @@ DEFTIMEVAR (TV_GLOBAL_ALLOC , "global alloc") DEFTIMEVAR (TV_RELOAD_CSE_REGS , "reload CSE regs") DEFTIMEVAR (TV_SEQABSTR , "sequence abstraction") DEFTIMEVAR (TV_GCSE_AFTER_RELOAD , "load CSE after reload") -DEFTIMEVAR (TV_FLOW2 , "flow 2") +DEFTIMEVAR (TV_THREAD_PROLOGUE_AND_EPILOGUE, "thread pro- & epilogue") DEFTIMEVAR (TV_IFCVT2 , "if-conversion 2") DEFTIMEVAR (TV_PEEPHOLE2 , "peephole 2") DEFTIMEVAR (TV_RENAME_REGISTERS , "rename registers") diff --git a/gcc/toplev.c b/gcc/toplev.c index 8d4a328..8afce8b 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -2117,9 +2117,6 @@ finalize (void) if (mem_report) dump_memory_report (true); - /* Free up memory for the benefit of leak detectors. */ - free_reg_info (); - /* Language-specific end of compilation actions. */ lang_hooks.finish (); } diff --git a/gcc/tracer.c b/gcc/tracer.c index 529f9f9..d938a0f 100644 --- a/gcc/tracer.c +++ b/gcc/tracer.c @@ -1,6 +1,7 @@ /* The tracer pass for the GNU compiler. Contributed by Jan Hubicka, SuSE Labs. - Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. This file is part of GCC. @@ -378,7 +379,7 @@ tracer (void) dump_flow_info (dump_file, dump_flags); /* Merge basic blocks in duplicated traces. */ - cleanup_cfg (CLEANUP_EXPENSIVE); + cleanup_cfg (0); } static bool @@ -394,7 +395,6 @@ rest_of_handle_tracer (void) if (dump_file) dump_flow_info (dump_file, dump_flags); tracer (); - reg_scan (get_insns (), max_reg_num ()); return 0; } diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 39b6bec..1f00e85 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -218,8 +218,13 @@ struct dump_file_info for the passes that are handed to register_dump_files. */ #define TODO_set_props (1 << 15) +/* Call df_finish at the end of the pass. This is done after all of + the dumpers have been allowed to run so that they have access to + the instance before it is destroyed. */ +#define TODO_df_finish (1 << 16) + /* Internally used for the first instance of a pass. */ -#define TODO_mark_first_instance (1 << 16) +#define TODO_mark_first_instance (1 << 17) #define TODO_update_ssa_any \ (TODO_update_ssa \ @@ -346,6 +351,12 @@ extern struct tree_opt_pass pass_rtl_fwprop_addr; extern struct tree_opt_pass pass_jump2; extern struct tree_opt_pass pass_lower_subreg; extern struct tree_opt_pass pass_cse; +extern struct tree_opt_pass pass_fast_rtl_dce; +extern struct tree_opt_pass pass_ud_rtl_dce; +extern struct tree_opt_pass pass_rtl_dce; +extern struct tree_opt_pass pass_rtl_dse1; +extern struct tree_opt_pass pass_rtl_dse2; +extern struct tree_opt_pass pass_rtl_dse3; extern struct tree_opt_pass pass_gcse; extern struct tree_opt_pass pass_jump_bypass; extern struct tree_opt_pass pass_profiling; @@ -365,7 +376,15 @@ extern struct tree_opt_pass pass_rtl_loop_done; extern struct tree_opt_pass pass_web; extern struct tree_opt_pass pass_cse2; -extern struct tree_opt_pass pass_life; +extern struct tree_opt_pass pass_df_initialize_opt; +extern struct tree_opt_pass pass_df_initialize_no_opt; +extern struct tree_opt_pass pass_regclass_init; +extern struct tree_opt_pass pass_subregs_of_mode_init; +extern struct tree_opt_pass pass_subregs_of_mode_finish; +extern struct tree_opt_pass pass_inc_dec; +extern struct tree_opt_pass pass_no_new_pseudos; +extern struct tree_opt_pass pass_stack_ptr_mod; +extern struct tree_opt_pass pass_initialize_regs; extern struct tree_opt_pass pass_combine; extern struct tree_opt_pass pass_if_after_combine; extern struct tree_opt_pass pass_partition_blocks; @@ -374,7 +393,6 @@ extern struct tree_opt_pass pass_split_all_insns; extern struct tree_opt_pass pass_lower_subreg2; extern struct tree_opt_pass pass_mode_switching; extern struct tree_opt_pass pass_see; -extern struct tree_opt_pass pass_recompute_reg_usage; extern struct tree_opt_pass pass_sms; extern struct tree_opt_pass pass_sched; extern struct tree_opt_pass pass_local_alloc; @@ -383,19 +401,24 @@ extern struct tree_opt_pass pass_postreload; extern struct tree_opt_pass pass_clean_state; extern struct tree_opt_pass pass_branch_prob; extern struct tree_opt_pass pass_value_profile_transformations; -extern struct tree_opt_pass pass_remove_death_notes; extern struct tree_opt_pass pass_postreload_cse; extern struct tree_opt_pass pass_gcse2; -extern struct tree_opt_pass pass_flow2; +extern struct tree_opt_pass pass_split_after_reload; +extern struct tree_opt_pass pass_branch_target_load_optimize1; +extern struct tree_opt_pass pass_thread_prologue_and_epilogue; extern struct tree_opt_pass pass_stack_adjustments; extern struct tree_opt_pass pass_peephole2; extern struct tree_opt_pass pass_if_after_reload; extern struct tree_opt_pass pass_regrename; +extern struct tree_opt_pass pass_cprop_hardreg; extern struct tree_opt_pass pass_reorder_blocks; -extern struct tree_opt_pass pass_branch_target_load_optimize; +extern struct tree_opt_pass pass_branch_target_load_optimize2; extern struct tree_opt_pass pass_leaf_regs; +extern struct tree_opt_pass pass_split_before_sched2; extern struct tree_opt_pass pass_sched2; extern struct tree_opt_pass pass_stack_regs; +extern struct tree_opt_pass pass_stack_regs_run; +extern struct tree_opt_pass pass_df_finish; extern struct tree_opt_pass pass_compute_alignments; extern struct tree_opt_pass pass_duplicate_computed_gotos; extern struct tree_opt_pass pass_variable_tracking; @@ -421,6 +444,8 @@ extern struct tree_opt_pass *all_passes, *all_ipa_passes, *all_lowering_passes; extern void execute_pass_list (struct tree_opt_pass *); extern void execute_ipa_pass_list (struct tree_opt_pass *); +extern void print_current_pass (FILE *); +extern void debug_pass (void); /* Set to true if the pass is called the first time during compilation of the current function. Note that using this information in the optimization diff --git a/gcc/tree-ssa-pre.c b/gcc/tree-ssa-pre.c index 72af731..6dfc166 100644 --- a/gcc/tree-ssa-pre.c +++ b/gcc/tree-ssa-pre.c @@ -3758,7 +3758,7 @@ init_pre (bool do_fre) postorder = XNEWVEC (int, n_basic_blocks - NUM_FIXED_BLOCKS); - post_order_compute (postorder, false); + post_order_compute (postorder, false, false); FOR_ALL_BB (bb) bb->aux = xcalloc (1, sizeof (struct bb_bitmap_sets)); diff --git a/gcc/tree-tailcall.c b/gcc/tree-tailcall.c index fa63637..c2e7d33 100644 --- a/gcc/tree-tailcall.c +++ b/gcc/tree-tailcall.c @@ -35,6 +35,7 @@ Boston, MA 02110-1301, USA. */ #include "tree-pass.h" #include "flags.h" #include "langhooks.h" +#include "dbgcnt.h" /* The file implements the tail recursion elimination. It is also used to analyze the tail calls in general, passing the results to the rtl level @@ -1007,7 +1008,7 @@ execute_tail_recursion (void) static bool gate_tail_calls (void) { - return flag_optimize_sibling_calls != 0; + return flag_optimize_sibling_calls != 0 && dbg_cnt (tail_call); } static unsigned int @@ -4612,8 +4612,7 @@ extern unsigned int init_function_for_compilation (void); extern void allocate_struct_function (tree); extern void init_function_start (tree); extern bool use_register_for_decl (tree); -extern void setjmp_vars_warning (tree); -extern void setjmp_args_warning (void); +extern void generate_setjmp_warnings (void); extern void init_temp_slots (void); extern void free_temp_slots (void); extern void pop_temp_slots (void); @@ -1,6 +1,6 @@ /* Web construction code for GNU compiler. Contributed by Jan Hubicka. - Copyright (C) 2001, 2002, 2004, 2006 + Copyright (C) 2001, 2002, 2004, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -102,25 +102,28 @@ unionfind_union (struct web_entry *first, struct web_entry *second) FUN is the function that does the union. */ void -union_defs (struct df *df, struct df_ref *use, struct web_entry *def_entry, +union_defs (struct df_ref *use, struct web_entry *def_entry, struct web_entry *use_entry, bool (*fun) (struct web_entry *, struct web_entry *)) { rtx insn = DF_REF_INSN (use); struct df_link *link = DF_REF_CHAIN (use); - struct df_ref *use_link; - struct df_ref *def_link; + struct df_ref **use_link; + struct df_ref **eq_use_link; + struct df_ref **def_link; rtx set; if (insn) { - use_link = DF_INSN_USES (df, insn); - def_link = DF_INSN_DEFS (df, insn); + use_link = DF_INSN_USES (insn); + eq_use_link = DF_INSN_EQ_USES (insn); + def_link = DF_INSN_DEFS (insn); set = single_set (insn); } else { use_link = NULL; + eq_use_link = NULL; def_link = NULL; set = NULL; } @@ -130,13 +133,24 @@ union_defs (struct df *df, struct df_ref *use, struct web_entry *def_entry, invalid instructions, so union all uses of the same operand for each insn. */ - while (use_link) - { - if (use != use_link - && DF_REF_REAL_REG (use) == DF_REF_REAL_REG (use_link)) - (*fun) (use_entry + DF_REF_ID (use), - use_entry + DF_REF_ID (use_link)); - use_link = use_link->next_ref; + if (use_link) + while (*use_link) + { + if (use != *use_link + && DF_REF_REAL_REG (use) == DF_REF_REAL_REG (*use_link)) + (*fun) (use_entry + DF_REF_ID (use), + use_entry + DF_REF_ID (*use_link)); + use_link++; + } + + if (eq_use_link) + while (*eq_use_link) + { + if (use != *eq_use_link + && DF_REF_REAL_REG (use) == DF_REF_REAL_REG (*eq_use_link)) + (*fun) (use_entry + DF_REF_ID (use), + use_entry + DF_REF_ID (*eq_use_link)); + eq_use_link++; } /* Recognize trivial noop moves and attempt to keep them as noop. @@ -147,13 +161,14 @@ union_defs (struct df *df, struct df_ref *use, struct web_entry *def_entry, && SET_SRC (set) == DF_REF_REG (use) && SET_SRC (set) == SET_DEST (set)) { - while (def_link) - { - if (DF_REF_REAL_REG (use) == DF_REF_REAL_REG (def_link)) - (*fun) (use_entry + DF_REF_ID (use), - def_entry + DF_REF_ID (def_link)); - def_link = def_link->next_ref; - } + if (def_link) + while (*def_link) + { + if (DF_REF_REAL_REG (use) == DF_REF_REAL_REG (*def_link)) + (*fun) (use_entry + DF_REF_ID (use), + def_entry + DF_REF_ID (*def_link)); + def_link++; + } } while (link) { @@ -166,20 +181,21 @@ union_defs (struct df *df, struct df_ref *use, struct web_entry *def_entry, register. Find it and union. */ if (use->flags & DF_REF_READ_WRITE) { - struct df_ref *link; + struct df_ref **link; if (DF_REF_INSN (use)) - link = DF_INSN_DEFS (df, DF_REF_INSN (use)); + link = DF_INSN_DEFS (DF_REF_INSN (use)); else link = NULL; - while (link) - { - if (DF_REF_REAL_REG (link) == DF_REF_REAL_REG (use)) - (*fun) (use_entry + DF_REF_ID (use), - def_entry + DF_REF_ID (link)); - link = link->next_ref; - } + if (link) + while (*link) + { + if (DF_REF_REAL_REG (*link) == DF_REF_REAL_REG (use)) + (*fun) (use_entry + DF_REF_ID (use), + def_entry + DF_REF_ID (*link)); + link++; + } } } @@ -232,84 +248,135 @@ replace_ref (struct df_ref *ref, rtx reg) { rtx oldreg = DF_REF_REAL_REG (ref); rtx *loc = DF_REF_REAL_LOC (ref); + unsigned int uid = INSN_UID (DF_REF_INSN (ref)); if (oldreg == reg) return; if (dump_file) fprintf (dump_file, "Updating insn %i (%i->%i)\n", - INSN_UID (DF_REF_INSN (ref)), REGNO (oldreg), REGNO (reg)); + uid, REGNO (oldreg), REGNO (reg)); *loc = reg; + df_insn_rescan (DF_REF_INSN (ref)); +} + + +static bool +gate_handle_web (void) +{ + return (optimize > 0 && flag_web); } /* Main entry point. */ -static void +static unsigned int web_main (void) { - struct df *df; struct web_entry *def_entry; struct web_entry *use_entry; - unsigned int i; - int max = max_reg_num (); + unsigned int max = max_reg_num (); char *used; + basic_block bb; + unsigned int uses_num = 0; + rtx insn; + + df_set_flags (DF_NO_HARD_REGS + DF_EQ_NOTES); + df_chain_add_problem (DF_UD_CHAIN); + df_analyze (); + df_set_flags (DF_DEFER_INSN_RESCAN); + + /* Assign ids to the uses. */ + FOR_ALL_BB (bb) + FOR_BB_INSNS (bb, insn) + { + unsigned int uid = INSN_UID (insn); + if (INSN_P (insn)) + { + struct df_ref **use_rec; + for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER) + DF_REF_ID (use) = uses_num++; + } + for (use_rec = DF_INSN_UID_EQ_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER) + DF_REF_ID (use) = uses_num++; + } + } + } - df = df_init (DF_EQUIV_NOTES); - df_chain_add_problem (df, DF_UD_CHAIN); - df_analyze (df); - df_reorganize_refs (&df->def_info); - df_reorganize_refs (&df->use_info); - - def_entry = XCNEWVEC (struct web_entry, DF_DEFS_SIZE (df)); - use_entry = XCNEWVEC (struct web_entry, DF_USES_SIZE (df)); + /* Record the number of uses and defs at the beginning of the optimization. */ + def_entry = XCNEWVEC (struct web_entry, DF_DEFS_TABLE_SIZE()); used = XCNEWVEC (char, max); - - if (dump_file) - df_dump (df, dump_file); + use_entry = XCNEWVEC (struct web_entry, uses_num); /* Produce the web. */ - for (i = 0; i < DF_USES_SIZE (df); i++) - union_defs (df, DF_USES_GET (df, i), def_entry, use_entry, unionfind_union); + FOR_ALL_BB (bb) + FOR_BB_INSNS (bb, insn) + { + unsigned int uid = INSN_UID (insn); + if (INSN_P (insn)) + { + struct df_ref **use_rec; + for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER) + union_defs (use, def_entry, use_entry, unionfind_union); + } + for (use_rec = DF_INSN_UID_EQ_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER) + union_defs (use, def_entry, use_entry, unionfind_union); + } + } + } /* Update the instruction stream, allocating new registers for split pseudos in progress. */ - for (i = 0; i < DF_USES_SIZE (df); i++) - replace_ref (DF_USES_GET (df, i), - entry_register (use_entry + i, DF_USES_GET (df, i), used)); - for (i = 0; i < DF_DEFS_SIZE (df); i++) - replace_ref (DF_DEFS_GET (df, i), - entry_register (def_entry + i, DF_DEFS_GET (df, i), used)); - - /* Dataflow information is corrupt here, but it can be easily updated - by creating new entries for new registers and updates or calling - df_insns_modify. */ + FOR_ALL_BB (bb) + FOR_BB_INSNS (bb, insn) + { + unsigned int uid = INSN_UID (insn); + if (INSN_P (insn)) + { + struct df_ref **use_rec; + struct df_ref **def_rec; + for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER) + replace_ref (use, entry_register (use_entry + DF_REF_ID (use), use, used)); + } + for (use_rec = DF_INSN_UID_EQ_USES (uid); *use_rec; use_rec++) + { + struct df_ref *use = *use_rec; + if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER) + replace_ref (use, entry_register (use_entry + DF_REF_ID (use), use, used)); + } + for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) + { + struct df_ref *def = *def_rec; + if (DF_REF_REGNO (def) >= FIRST_PSEUDO_REGISTER) + replace_ref (def, entry_register (def_entry + DF_REF_ID (def), def, used)); + } + } + } + free (def_entry); free (use_entry); free (used); - df_finish (df); - df = NULL; -} - -static bool -gate_handle_web (void) -{ - return (optimize > 0 && flag_web); -} - -static unsigned int -rest_of_handle_web (void) -{ - web_main (); - delete_trivially_dead_insns (get_insns (), max_reg_num ()); - cleanup_cfg (CLEANUP_EXPENSIVE); - reg_scan (get_insns (), max_reg_num ()); return 0; } - + struct tree_opt_pass pass_web = { "web", /* name */ gate_handle_web, /* gate */ - rest_of_handle_web, /* execute */ + web_main, /* execute */ NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ @@ -318,6 +385,7 @@ struct tree_opt_pass pass_web = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ + TODO_df_finish | TODO_dump_func, /* todo_flags_finish */ 'Z' /* letter */ }; |