X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Ffwprop.c;h=2b29aa474c7683b290910f19228c097c22d57fc6;hb=49b6dba67f06de2ec02bf958c556fc34b171408e;hp=1ab3e525138155fcd172e8a7aca66c0453e0a421;hpb=750a330e3c5ea0ee3a0ba6991939d9387d100727;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/fwprop.c b/gcc/fwprop.c index 1ab3e525138..2b29aa474c7 100644 --- a/gcc/fwprop.c +++ b/gcc/fwprop.c @@ -1,12 +1,12 @@ /* 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. 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 +Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY @@ -15,9 +15,8 @@ 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. */ +along with GCC; see the file COPYING3. If not see +. */ #include "config.h" #include "system.h" @@ -104,7 +103,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; @@ -186,71 +184,104 @@ canonicalize_address (rtx x) for a memory access in the given MODE. */ static bool -should_replace_address (rtx old, rtx new, enum machine_mode mode) +should_replace_address (rtx old_rtx, rtx new_rtx, enum machine_mode mode, + bool speed) { int gain; - if (rtx_equal_p (old, new) || !memory_address_p (mode, new)) + if (rtx_equal_p (old_rtx, new_rtx) || !memory_address_p (mode, new_rtx)) return false; /* Copy propagation is always ok. */ - if (REG_P (old) && REG_P (new)) + if (REG_P (old_rtx) && REG_P (new_rtx)) return true; /* Prefer the new address if it is less expensive. */ - gain = address_cost (old, mode) - address_cost (new, mode); + gain = address_cost (old_rtx, mode, speed) - address_cost (new_rtx, mode, speed); /* If the addresses have equivalent cost, prefer the new address if it has the highest `rtx_cost'. That has the potential of eliminating the most insns without additional costs, and it is the same that cse.c used to do. */ if (gain == 0) - gain = rtx_cost (new, SET) - rtx_cost (old, SET); + gain = rtx_cost (new_rtx, SET, speed) - rtx_cost (old_rtx, SET, speed); return (gain > 0); } + +/* Flags for the last parameter of propagate_rtx_1. */ + +enum { + /* If PR_CAN_APPEAR is true, propagate_rtx_1 always returns true; + if it is false, propagate_rtx_1 returns false if, for at least + one occurrence OLD, it failed to collapse the result to a constant. + For example, (mult:M (reg:M A) (minus:M (reg:M B) (reg:M A))) may + collapse to zero if replacing (reg:M B) with (reg:M A). + + PR_CAN_APPEAR is disregarded inside MEMs: in that case, + propagate_rtx_1 just tries to make cheaper and valid memory + addresses. */ + PR_CAN_APPEAR = 1, + + /* If PR_HANDLE_MEM is not set, propagate_rtx_1 won't attempt any replacement + outside memory addresses. This is needed because propagate_rtx_1 does + not do any analysis on memory; thus it is very conservative and in general + it will fail if non-read-only MEMs are found in the source expression. + + PR_HANDLE_MEM is set when the source of the propagation was not + another MEM. Then, it is safe not to treat non-read-only MEMs as + ``opaque'' objects. */ + PR_HANDLE_MEM = 2, + + /* Set when costs should be optimized for speed. */ + PR_OPTIMIZE_FOR_SPEED = 4 +}; + + /* Replace all occurrences of OLD in *PX with NEW and try to simplify the resulting expression. Replace *PX with a new RTL expression if an occurrence of OLD was found. - If CAN_APPEAR is true, we always return true; if it is false, we - can return false if, for at least one occurrence OLD, we failed to - collapse the result to a constant. For example, (mult:M (reg:M A) - (minus:M (reg:M B) (reg:M A))) may collapse to zero if replacing - (reg:M B) with (reg:M A). - - CAN_APPEAR is disregarded inside MEMs: in that case, we always return - true if the simplification is a cheaper and valid memory address. - This is only a wrapper around simplify-rtx.c: do not add any pattern matching code here. (The sole exception is the handling of LO_SUM, but that is because there is no simplify_gen_* function for LO_SUM). */ static bool -propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear) +propagate_rtx_1 (rtx *px, rtx old_rtx, rtx new_rtx, int flags) { rtx x = *px, tem = NULL_RTX, op0, op1, op2; enum rtx_code code = GET_CODE (x); enum machine_mode mode = GET_MODE (x); enum machine_mode op_mode; + bool can_appear = (flags & PR_CAN_APPEAR) != 0; bool valid_ops = true; - /* If X is OLD_RTX, return NEW_RTX. Otherwise, if this is an expression, - try to build a new expression from recursive substitution. */ + if (!(flags & PR_HANDLE_MEM) && MEM_P (x) && !MEM_READONLY_P (x)) + { + /* If unsafe, change MEMs to CLOBBERs or SCRATCHes (to preserve whether + they have side effects or not). */ + *px = (side_effects_p (x) + ? gen_rtx_CLOBBER (GET_MODE (x), const0_rtx) + : gen_rtx_SCRATCH (GET_MODE (x))); + return false; + } - if (x == old) + /* If X is OLD_RTX, return NEW_RTX. But not if replacing only within an + address, and we are *not* inside one. */ + if (x == old_rtx) { - *px = new; + *px = new_rtx; return can_appear; } + /* If this is an expression, try recursive substitution. */ switch (GET_RTX_CLASS (code)) { case RTX_UNARY: op0 = XEXP (x, 0); op_mode = GET_MODE (op0); - valid_ops &= propagate_rtx_1 (&op0, old, new, can_appear); + valid_ops &= propagate_rtx_1 (&op0, old_rtx, new_rtx, flags); if (op0 == XEXP (x, 0)) return true; tem = simplify_gen_unary (code, mode, op0, op_mode); @@ -260,8 +291,8 @@ propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear) case RTX_COMM_ARITH: op0 = XEXP (x, 0); op1 = XEXP (x, 1); - valid_ops &= propagate_rtx_1 (&op0, old, new, can_appear); - valid_ops &= propagate_rtx_1 (&op1, old, new, can_appear); + valid_ops &= propagate_rtx_1 (&op0, old_rtx, new_rtx, flags); + valid_ops &= propagate_rtx_1 (&op1, old_rtx, new_rtx, flags); if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1)) return true; tem = simplify_gen_binary (code, mode, op0, op1); @@ -272,8 +303,8 @@ propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear) op0 = XEXP (x, 0); op1 = XEXP (x, 1); op_mode = GET_MODE (op0) != VOIDmode ? GET_MODE (op0) : GET_MODE (op1); - valid_ops &= propagate_rtx_1 (&op0, old, new, can_appear); - valid_ops &= propagate_rtx_1 (&op1, old, new, can_appear); + valid_ops &= propagate_rtx_1 (&op0, old_rtx, new_rtx, flags); + valid_ops &= propagate_rtx_1 (&op1, old_rtx, new_rtx, flags); if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1)) return true; tem = simplify_gen_relational (code, mode, op_mode, op0, op1); @@ -285,9 +316,9 @@ propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear) op1 = XEXP (x, 1); op2 = XEXP (x, 2); op_mode = GET_MODE (op0); - valid_ops &= propagate_rtx_1 (&op0, old, new, can_appear); - valid_ops &= propagate_rtx_1 (&op1, old, new, can_appear); - valid_ops &= propagate_rtx_1 (&op2, old, new, can_appear); + valid_ops &= propagate_rtx_1 (&op0, old_rtx, new_rtx, flags); + valid_ops &= propagate_rtx_1 (&op1, old_rtx, new_rtx, flags); + valid_ops &= propagate_rtx_1 (&op2, old_rtx, new_rtx, flags); if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1) && op2 == XEXP (x, 2)) return true; if (op_mode == VOIDmode) @@ -300,7 +331,7 @@ propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear) if (code == SUBREG) { op0 = XEXP (x, 0); - valid_ops &= propagate_rtx_1 (&op0, old, new, can_appear); + valid_ops &= propagate_rtx_1 (&op0, old_rtx, new_rtx, flags); if (op0 == XEXP (x, 0)) return true; tem = simplify_gen_subreg (mode, op0, GET_MODE (SUBREG_REG (x)), @@ -309,7 +340,7 @@ propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear) break; case RTX_OBJ: - if (code == MEM && x != new) + if (code == MEM && x != new_rtx) { rtx new_op0; op0 = XEXP (x, 0); @@ -319,19 +350,22 @@ propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear) return true; op0 = new_op0 = targetm.delegitimize_address (op0); - valid_ops &= propagate_rtx_1 (&new_op0, old, new, true); + valid_ops &= propagate_rtx_1 (&new_op0, old_rtx, new_rtx, + flags | PR_CAN_APPEAR); /* Dismiss transformation that we do not want to carry on. */ if (!valid_ops || new_op0 == op0 - || GET_MODE (new_op0) != GET_MODE (op0)) + || !(GET_MODE (new_op0) == GET_MODE (op0) + || GET_MODE (new_op0) == VOIDmode)) return true; canonicalize_address (new_op0); /* Copy propagations are always ok. Otherwise check the costs. */ - if (!(REG_P (old) && REG_P (new)) - && !should_replace_address (op0, new_op0, GET_MODE (x))) + if (!(REG_P (old_rtx) && REG_P (new_rtx)) + && !should_replace_address (op0, new_op0, GET_MODE (x), + flags & PR_OPTIMIZE_FOR_SPEED)) return true; tem = replace_equiv_address_nv (x, new_op0); @@ -345,8 +379,8 @@ propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear) /* The only simplification we do attempts to remove references to op0 or make it constant -- in both cases, op0's invalidity will not make the result invalid. */ - propagate_rtx_1 (&op0, old, new, true); - valid_ops &= propagate_rtx_1 (&op1, old, new, can_appear); + propagate_rtx_1 (&op0, old_rtx, new_rtx, flags | PR_CAN_APPEAR); + valid_ops &= propagate_rtx_1 (&op1, old_rtx, new_rtx, flags); if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1)) return true; @@ -364,9 +398,9 @@ propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear) else if (code == REG) { - if (rtx_equal_p (x, old)) + if (rtx_equal_p (x, old_rtx)) { - *px = new; + *px = new_rtx; return can_appear; } } @@ -388,6 +422,18 @@ propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear) return valid_ops || can_appear || CONSTANT_P (tem); } + +/* for_each_rtx traversal function that returns 1 if BODY points to + a non-constant mem. */ + +static int +varying_mem_p (rtx *body, void *data ATTRIBUTE_UNUSED) +{ + rtx x = *body; + return MEM_P (x) && !MEM_READONLY_P (x); +} + + /* Replace all occurrences of OLD in X with NEW and try to simplify the resulting expression (in mode MODE). Return a new expression if it is a constant, otherwise X. @@ -397,18 +443,27 @@ propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear) Otherwise, we accept simplifications that have a lower or equal cost. */ static rtx -propagate_rtx (rtx x, enum machine_mode mode, rtx old, rtx new) +propagate_rtx (rtx x, enum machine_mode mode, rtx old_rtx, rtx new_rtx, + bool speed) { rtx tem; bool collapsed; + int flags; - if (REG_P (new) && REGNO (new) < FIRST_PSEUDO_REGISTER) + if (REG_P (new_rtx) && REGNO (new_rtx) < FIRST_PSEUDO_REGISTER) return NULL_RTX; - new = copy_rtx (new); + flags = 0; + if (REG_P (new_rtx) || CONSTANT_P (new_rtx)) + flags |= PR_CAN_APPEAR; + if (!for_each_rtx (&new_rtx, varying_mem_p, NULL)) + flags |= PR_HANDLE_MEM; + + if (speed) + flags |= PR_OPTIMIZE_FOR_SPEED; tem = x; - collapsed = propagate_rtx_1 (&tem, old, new, REG_P (new) || CONSTANT_P (new)); + collapsed = propagate_rtx_1 (&tem, old_rtx, copy_rtx (new_rtx), flags); if (tem == x || !collapsed) return NULL_RTX; @@ -431,23 +486,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 -local_ref_killed_between_p (struct df_ref * ref, rtx from, rtx to) +static bool +local_ref_killed_between_p (df_ref ref, rtx from, rtx to) { rtx insn; - struct df_ref *def; for (insn = from; insn != to; insn = NEXT_INSN (insn)) { + 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++) { + df_ref def = *def_rec; if (DF_REF_REGNO (ref) == DF_REF_REGNO (def)) return true; - def = def->next_ref; } } return false; @@ -463,12 +517,12 @@ local_ref_killed_between_p (struct df_ref * ref, rtx from, rtx to) we check if the definition is killed after DEF_INSN or before TARGET_INSN insn, in their respective basic blocks. */ static bool -use_killed_between (struct df_ref *use, rtx def_insn, rtx target_insn) +use_killed_between (df_ref use, rtx def_insn, rtx target_insn) { basic_block def_bb = BLOCK_FOR_INSN (def_insn); basic_block target_bb = BLOCK_FOR_INSN (target_insn); int regno; - struct df_ref * def; + df_ref def; /* In some obscure situations we can have a def reaching a use that is _before_ the def. In other words the def does not @@ -477,15 +531,20 @@ 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. */ + know that this definition reaches use, or we wouldn't be here. + However, this is invalid for hard registers because if they are + live at the beginning of the function it does not mean that we + have an uninitialized access. */ regno = DF_REF_REGNO (use); - def = DF_REG_DEF_GET (df, regno)->reg_chain; - if (def && (def->next_reg == NULL)) + def = DF_REG_DEF_CHAIN (regno); + if (def + && DF_REF_NEXT_REG (def) == NULL + && regno >= FIRST_PSEUDO_REGISTER) return false; /* Check locally if we are in the same basic block. */ @@ -496,18 +555,18 @@ use_killed_between (struct df_ref *use, rtx def_insn, rtx target_insn) if (single_pred_p (target_bb) && single_pred (target_bb) == def_bb) { - struct df_ref *x; + df_ref x; /* 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 (DF_REF_INSN (x)) >= 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 (DF_REF_INSN (x)) < DF_INSN_LUID (target_insn)) return true; return false; @@ -518,23 +577,14 @@ use_killed_between (struct df_ref *use, rtx def_insn, rtx target_insn) } -/* for_each_rtx traversal function that returns 1 if BODY points to - a non-constant mem. */ - -static int -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; + df_ref *use_rec; + struct df_insn_info *insn_info = DF_INSN_INFO_GET (def_insn); rtx def_set = single_set (def_insn); gcc_assert (def_set); @@ -548,22 +598,38 @@ 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_INFO_USES (insn_info); *use_rec; use_rec++) + { + df_ref use = *use_rec; + if (rtx_equal_p (DF_REF_REG (use), def_reg)) + return false; + } + for (use_rec = DF_INSN_INFO_EQ_USES (insn_info); *use_rec; use_rec++) + { + df_ref use = *use_rec; + if (rtx_equal_p (DF_REF_REG (use), 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_INFO_USES (insn_info); *use_rec; use_rec++) + { + df_ref use = *use_rec; + if (use_killed_between (use, def_insn, target_insn)) + return false; + } + for (use_rec = DF_INSN_INFO_EQ_USES (insn_info); *use_rec; use_rec++) + { + 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 - instruction that involves references to non-constant memory. */ - return !for_each_rtx (&SET_SRC (def_set), varying_mem_p, NULL); + return true; } @@ -612,34 +678,49 @@ 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, 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 *orig_use = use, *new_use; + df_ref use = *use_rec; + df_ref orig_use = use, new_use; + int width = -1; + int offset = -1; + enum machine_mode mode = 0; rtx *new_loc = find_occurrence (loc, DF_REF_REG (orig_use)); + use_rec++; if (!new_loc) continue; + if (DF_REF_FLAGS_IS_SET (orig_use, DF_REF_SIGN_EXTRACT | DF_REF_ZERO_EXTRACT)) + { + width = DF_REF_EXTRACT_WIDTH (orig_use); + offset = DF_REF_EXTRACT_OFFSET (orig_use); + mode = DF_REF_EXTRACT_MODE (orig_use); + } + /* 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); + type, DF_REF_FLAGS (orig_use) | new_flags, + width, offset, mode); /* 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); } @@ -650,65 +731,97 @@ update_df (rtx insn, rtx *loc, struct df_ref *orig_uses, enum df_ref_type type, performed. */ static bool -try_fwprop_subst (struct df_ref *use, rtx *loc, rtx new, rtx def_insn, bool set_reg_equal) +try_fwprop_subst (df_ref use, rtx *loc, rtx new_rtx, rtx def_insn, bool set_reg_equal) { rtx insn = DF_REF_INSN (use); enum df_ref_type type = DF_REF_TYPE (use); int flags = DF_REF_FLAGS (use); + rtx set = single_set (insn); + bool speed = optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn)); + int old_cost = rtx_cost (SET_SRC (set), SET, speed); + bool ok; if (dump_file) { fprintf (dump_file, "\nIn insn %d, replacing\n ", INSN_UID (insn)); print_inline_rtx (dump_file, *loc, 2); fprintf (dump_file, "\n with "); - print_inline_rtx (dump_file, new, 2); + print_inline_rtx (dump_file, new_rtx, 2); fprintf (dump_file, "\n"); } - if (validate_change (insn, loc, new, false)) + validate_unshare_change (insn, loc, new_rtx, true); + if (!verify_changes (0)) + { + if (dump_file) + fprintf (dump_file, "Changes to insn %d not recognized\n", + INSN_UID (insn)); + ok = false; + } + + else if (DF_REF_TYPE (use) == DF_REF_REG_USE + && rtx_cost (SET_SRC (set), SET, speed) > old_cost) + { + if (dump_file) + fprintf (dump_file, "Changes to insn %d not profitable\n", + INSN_UID (insn)); + ok = false; + } + + else { - num_changes++; if (dump_file) fprintf (dump_file, "Changed insn %d\n", INSN_UID (insn)); + ok = true; + } - /* Unlink the use that we changed. */ - df_ref_remove (df, use); - if (!CONSTANT_P (new)) - update_df (insn, loc, DF_INSN_USES (df, def_insn), type, flags); + if (ok) + { + confirm_change_group (); + num_changes++; - return true; + df_ref_remove (use); + if (!CONSTANT_P (new_rtx)) + { + struct df_insn_info *insn_info = DF_INSN_INFO_GET (def_insn); + update_df (insn, loc, DF_INSN_INFO_USES (insn_info), type, flags); + update_df (insn, loc, DF_INSN_INFO_EQ_USES (insn_info), type, flags); + } } else { - if (dump_file) - fprintf (dump_file, "Changes to insn %d not recognized\n", - INSN_UID (insn)); + cancel_changes (0); - /* Can also record a simplified value in a REG_EQUAL note, making a - new one if one does not already exist. */ + /* Can also record a simplified value in a REG_EQUAL note, + making a new one if one does not already exist. */ if (set_reg_equal) { if (dump_file) fprintf (dump_file, " Setting REG_EQUAL note\n"); - set_unique_reg_note (insn, REG_EQUAL, copy_rtx (new)); + set_unique_reg_note (insn, REG_EQUAL, copy_rtx (new_rtx)); /* ??? 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); + if (!CONSTANT_P (new_rtx)) + { + struct df_insn_info *insn_info = DF_INSN_INFO_GET (def_insn); + update_df (insn, loc, DF_INSN_INFO_USES (insn_info), + type, DF_REF_IN_NOTE); + update_df (insn, loc, DF_INSN_INFO_EQ_USES (insn_info), + type, DF_REF_IN_NOTE); + } } - - return false; } + + return ok; } /* If USE is a paradoxical subreg, see if it can be replaced by a pseudo. */ static bool -forward_propagate_subreg (struct df_ref *use, rtx def_insn, rtx def_set) +forward_propagate_subreg (df_ref use, rtx def_insn, rtx def_set) { rtx use_reg = DF_REF_REG (use); rtx use_insn, src; @@ -743,11 +856,11 @@ forward_propagate_subreg (struct df_ref *use, rtx def_insn, rtx def_set) result. */ static bool -forward_propagate_and_simplify (struct df_ref *use, rtx def_insn, rtx def_set) +forward_propagate_and_simplify (df_ref use, rtx def_insn, rtx def_set) { rtx use_insn = DF_REF_INSN (use); rtx use_set = single_set (use_insn); - rtx src, reg, new, *loc; + rtx src, reg, new_rtx, *loc; bool set_reg_equal; enum machine_mode mode; @@ -790,10 +903,10 @@ forward_propagate_and_simplify (struct df_ref *use, rtx def_insn, rtx def_set) if (x != src) { 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) - set_unique_reg_note (use_insn, REG_EQUAL, copy_rtx (new)); + rtx old_rtx = note ? XEXP (note, 0) : SET_SRC (use_set); + rtx new_rtx = simplify_replace_rtx (old_rtx, src, x); + if (old_rtx != new_rtx) + set_unique_reg_note (use_insn, REG_EQUAL, copy_rtx (new_rtx)); } return false; } @@ -812,7 +925,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 @@ -825,12 +938,13 @@ forward_propagate_and_simplify (struct df_ref *use, rtx def_insn, rtx def_set) else mode = GET_MODE (*loc); - new = propagate_rtx (*loc, mode, reg, src); - - if (!new) + new_rtx = propagate_rtx (*loc, mode, reg, src, + optimize_bb_for_speed_p (BLOCK_FOR_INSN (use_insn))); + + if (!new_rtx) return false; - return try_fwprop_subst (use, loc, new, def_insn, set_reg_equal); + return try_fwprop_subst (use, loc, new_rtx, def_insn, set_reg_equal); } @@ -838,16 +952,16 @@ forward_propagate_and_simplify (struct df_ref *use, rtx def_insn, rtx def_set) definition, try to forward propagate it into that insn. */ static void -forward_propagate_into (struct df_ref *use) +forward_propagate_into (df_ref use) { struct df_link *defs; - struct df_ref *def; + 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. */ @@ -858,13 +972,11 @@ 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 if - we are going to unroll. */ - if (current_loops - && DF_REF_BB (def)->loop_father != DF_REF_BB (use)->loop_father) + /* Do not propagate loop invariant definitions inside the loop. */ + if (DF_REF_BB (def)->loop_father != DF_REF_BB (use)->loop_father) return; /* Check if the use is still present in the insn! */ @@ -874,10 +986,12 @@ forward_propagate_into (struct df_ref *use) else parent = PATTERN (use_insn); - if (!loc_mentioned_in_p (DF_REF_LOC (use), parent)) + if (!reg_mentioned_p (DF_REF_REG (use), parent)) return; def_insn = DF_REF_INSN (def); + if (multiple_sets (def_insn)) + return; def_set = single_set (def_insn); if (!def_set) return; @@ -899,24 +1013,21 @@ fwprop_init (void) loops and be careful about them. But we have to call flow_loops_find before df_analyze, because flow_loops_find may introduce new jump insns (sadly) if we are not working in cfglayout mode. */ - if (flag_rerun_cse_after_loop && (flag_unroll_loops || flag_peel_loops)) - loop_optimizer_init (0); + loop_optimizer_init (0); /* 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); - - if (flag_rerun_cse_after_loop && (flag_unroll_loops || flag_peel_loops)) - loop_optimizer_finalize (); + loop_optimizer_finalize (); free_dominance_info (CDI_DOMINATORS); cleanup_cfg (0); @@ -926,6 +1037,7 @@ fwprop_done (void) fprintf (dump_file, "\nNumber of successful forward propagations: %d\n\n", num_changes); + df_remove_problem (df_chain); } @@ -951,27 +1063,28 @@ 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); + df_ref use = DF_USES_GET (i); if (use) - if (!current_loops - || DF_REF_TYPE (use) == DF_REF_REG_USE - || DF_REF_BB (use)->loop_father == NULL) + if (DF_REF_TYPE (use) == DF_REF_REG_USE + || DF_REF_BB (use)->loop_father == NULL + /* The outer most loop is not really a loop. */ + || loop_outer (DF_REF_BB (use)->loop_father) == NULL) forward_propagate_into (use); } fwprop_done (); - return 0; } -struct tree_opt_pass pass_rtl_fwprop = +struct rtl_opt_pass pass_rtl_fwprop = { + { + RTL_PASS, "fwprop1", /* name */ - gate_fwprop, /* gate */ - fwprop, /* execute */ + gate_fwprop, /* gate */ + fwprop, /* execute */ NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ @@ -980,17 +1093,11 @@ struct tree_opt_pass pass_rtl_fwprop = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ - TODO_dump_func, /* todo_flags_finish */ - 0 /* letter */ + TODO_df_finish | TODO_verify_rtl_sharing | + TODO_dump_func /* todo_flags_finish */ + } }; -static bool -gate_fwprop_addr (void) -{ - return optimize > 0 && flag_forward_propagate && flag_rerun_cse_after_loop - && (flag_unroll_loops || flag_peel_loops); -} - static unsigned int fwprop_addr (void) { @@ -999,13 +1106,16 @@ 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); + 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) + && DF_REF_BB (use)->loop_father != NULL + /* The outer most loop is not really a loop. */ + && loop_outer (DF_REF_BB (use)->loop_father) != NULL) forward_propagate_into (use); } @@ -1014,11 +1124,13 @@ fwprop_addr (void) return 0; } -struct tree_opt_pass pass_rtl_fwprop_addr = +struct rtl_opt_pass pass_rtl_fwprop_addr = { + { + RTL_PASS, "fwprop2", /* name */ - gate_fwprop_addr, /* gate */ - fwprop_addr, /* execute */ + gate_fwprop, /* gate */ + fwprop_addr, /* execute */ NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ @@ -1027,6 +1139,7 @@ struct tree_opt_pass pass_rtl_fwprop_addr = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ - TODO_dump_func, /* todo_flags_finish */ - 0 /* letter */ + TODO_df_finish | TODO_verify_rtl_sharing | + TODO_dump_func /* todo_flags_finish */ + } };