rtx reaching_reg; /* Register to use when re-writing. */
};
+/* Array of implicit set patterns indexed by basic block index. */
+static rtx *implicit_sets;
+
/* Head of the list of load/store memory refs. */
static struct ls_expr * pre_ldst_mems = NULL;
static void dump_hash_table PARAMS ((FILE *, const char *,
struct hash_table *));
static struct expr *lookup_expr PARAMS ((rtx, struct hash_table *));
-static struct expr *lookup_set PARAMS ((unsigned int, rtx, struct hash_table *));
+static struct expr *lookup_set PARAMS ((unsigned int, struct hash_table *));
static struct expr *next_set PARAMS ((unsigned int, struct expr *));
static void reset_opr_set_tables PARAMS ((void));
static int oprs_not_set_p PARAMS ((rtx, rtx));
static void canon_list_insert PARAMS ((rtx, rtx, void *));
static int cprop_insn PARAMS ((rtx, int));
static int cprop PARAMS ((int));
+static rtx fis_get_condition PARAMS ((rtx));
+static void find_implicit_sets PARAMS ((void));
static int one_cprop_pass PARAMS ((int, int, int));
static bool constprop_register PARAMS ((rtx, rtx, rtx, int));
static struct expr *find_bypass_set PARAMS ((int, int));
static void clear_modify_mem_tables PARAMS ((void));
static void free_modify_mem_tables PARAMS ((void));
static rtx gcse_emit_move_after PARAMS ((rtx, rtx, rtx));
+static void local_cprop_find_used_regs PARAMS ((rtx *, void *));
static bool do_local_cprop PARAMS ((rtx, rtx, int, rtx*));
static bool adjust_libcall_notes PARAMS ((rtx, rtx, rtx, rtx*));
static void local_cprop_pass PARAMS ((int));
case CONST_DOUBLE:
case CONST_VECTOR:
case CALL:
+ case CONSTANT_P_RTX:
return 0;
default:
&& REGNO (src) >= FIRST_PSEUDO_REGISTER
&& can_copy_p [GET_MODE (dest)]
&& REGNO (src) != regno)
- || CONSTANT_P (src))
+ || (CONSTANT_P (src)
+ && GET_CODE (src) != CONSTANT_P_RTX))
/* A copy is not available if its src or dest is subsequently
modified. Here we want to search from INSN+1 on, but
oprs_available_p searches from INSN on. */
Currently src must be a pseudo-reg or a const_int.
- F is the first insn.
TABLE is the table computed. */
static void
note_stores (PATTERN (insn), record_last_set_info, insn);
}
+ /* Insert implicit sets in the hash table. */
+ if (table->set_p
+ && implicit_sets[current_bb->index] != NULL_RTX)
+ hash_scan_set (implicit_sets[current_bb->index],
+ current_bb->head, table);
+
/* The next pass builds the hash table. */
for (insn = current_bb->head, in_libcall_block = 0;
return expr;
}
-/* Lookup REGNO in the set TABLE. If PAT is non-NULL look for the entry that
- matches it, otherwise return the first entry for REGNO. The result is a
- pointer to the table entry, or NULL if not found. */
+/* Lookup REGNO in the set TABLE. The result is a pointer to the
+ table entry, or NULL if not found. */
static struct expr *
-lookup_set (regno, pat, table)
+lookup_set (regno, table)
unsigned int regno;
- rtx pat;
struct hash_table *table;
{
unsigned int hash = hash_set (regno, table->size);
expr = table->table[hash];
- if (pat)
- {
- while (expr && ! expr_equiv_p (expr->expr, pat))
- expr = expr->next_same_hash;
- }
- else
- {
- while (expr && REGNO (SET_DEST (expr->expr)) != regno)
- expr = expr->next_same_hash;
- }
+ while (expr && REGNO (SET_DEST (expr->expr)) != regno)
+ expr = expr->next_same_hash;
return expr;
}
while (1)
{
rtx src;
- struct expr *set = lookup_set (regno, NULL_RTX, &set_hash_table);
+ struct expr *set = lookup_set (regno, &set_hash_table);
/* Find a set that is available at the start of the block
which contains INSN. */
conditional branch instructions first. */
if (alter_jumps
&& (sset = single_set (insn)) != NULL
+ && NEXT_INSN (insn)
&& any_condjump_p (NEXT_INSN (insn)) && onlyjump_p (NEXT_INSN (insn)))
{
rtx dest = SET_DEST (sset);
return changed;
}
+/* Like find_used_regs, but avoid recording uses that appear in
+ input-output contexts such as zero_extract or pre_dec. This
+ restricts the cases we consider to those for which local cprop
+ can legitimately make replacements. */
+
+static void
+local_cprop_find_used_regs (xptr, data)
+ rtx *xptr;
+ void *data;
+{
+ rtx x = *xptr;
+
+ if (x == 0)
+ return;
+
+ switch (GET_CODE (x))
+ {
+ case ZERO_EXTRACT:
+ case SIGN_EXTRACT:
+ case STRICT_LOW_PART:
+ return;
+
+ case PRE_DEC:
+ case PRE_INC:
+ case POST_DEC:
+ case POST_INC:
+ case PRE_MODIFY:
+ case POST_MODIFY:
+ /* Can only legitimately appear this early in the context of
+ stack pushes for function arguments, but handle all of the
+ codes nonetheless. */
+ return;
+
+ case SUBREG:
+ /* Setting a subreg of a register larger than word_mode leaves
+ the non-written words unchanged. */
+ if (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) > BITS_PER_WORD)
+ return;
+ break;
+
+ default:
+ break;
+ }
+
+ find_used_regs (xptr, data);
+}
+
/* LIBCALL_SP is a zero-terminated array of insns at the end of a libcall;
their REG_EQUAL notes need updating. */
if (l->in_libcall)
continue;
- if (CONSTANT_P (this_rtx))
+ if (CONSTANT_P (this_rtx)
+ && GET_CODE (this_rtx) != CONSTANT_P_RTX)
newcnst = this_rtx;
if (REG_P (this_rtx) && REGNO (this_rtx) >= FIRST_PSEUDO_REGISTER
/* Don't copy propagate if it has attached REG_EQUIV note.
rtx insn;
struct reg_use *reg_used;
rtx libcall_stack[MAX_NESTED_LIBCALLS + 1], *libcall_sp;
+ bool changed = false;
cselib_init ();
libcall_sp = &libcall_stack[MAX_NESTED_LIBCALLS];
do
{
reg_use_count = 0;
- note_uses (&PATTERN (insn), find_used_regs, NULL);
+ note_uses (&PATTERN (insn), local_cprop_find_used_regs, NULL);
if (note)
- find_used_regs (&XEXP (note, 0), NULL);
+ local_cprop_find_used_regs (&XEXP (note, 0), NULL);
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))
- break;
+ {
+ changed = true;
+ break;
+ }
}
while (reg_use_count);
}
cselib_process_insn (insn);
}
cselib_finish ();
+ /* Global analysis may get into infinite loops for unreachable blocks. */
+ if (changed && alter_jumps)
+ {
+ delete_unreachable_blocks ();
+ free_reg_set_mem ();
+ alloc_reg_set_mem (max_reg_num ());
+ compute_sets (get_insns ());
+ }
}
/* Forward propagate copies. This includes copies and constants. Return
return changed;
}
+/* Similar to get_condition, only the resulting condition must be
+ valid at JUMP, instead of at EARLIEST.
+
+ This differs from noce_get_condition in ifcvt.c in that we prefer not to
+ settle for the condition variable in the jump instruction being integral.
+ We prefer to be able to record the value of a user variable, rather than
+ the value of a temporary used in a condition. This could be solved by
+ recording the value of *every* register scaned by canonicalize_condition,
+ but this would require some code reorganization. */
+
+static rtx
+fis_get_condition (jump)
+ rtx jump;
+{
+ rtx cond, set, tmp, insn, earliest;
+ bool reverse;
+
+ if (! any_condjump_p (jump))
+ return NULL_RTX;
+
+ set = pc_set (jump);
+ cond = XEXP (SET_SRC (set), 0);
+
+ /* If this branches to JUMP_LABEL when the condition is false,
+ reverse the condition. */
+ reverse = (GET_CODE (XEXP (SET_SRC (set), 2)) == LABEL_REF
+ && XEXP (XEXP (SET_SRC (set), 2), 0) == JUMP_LABEL (jump));
+
+ /* Use canonicalize_condition to do the dirty work of manipulating
+ MODE_CC values and COMPARE rtx codes. */
+ tmp = canonicalize_condition (jump, cond, reverse, &earliest, NULL_RTX);
+ if (!tmp)
+ return NULL_RTX;
+
+ /* Verify that the given condition is valid at JUMP by virtue of not
+ having been modified since EARLIEST. */
+ for (insn = earliest; insn != jump; insn = NEXT_INSN (insn))
+ if (INSN_P (insn) && modified_in_p (tmp, insn))
+ break;
+ if (insn == jump)
+ return tmp;
+
+ /* The condition was modified. See if we can get a partial result
+ that doesn't follow all the reversals. Perhaps combine can fold
+ them together later. */
+ tmp = XEXP (tmp, 0);
+ if (!REG_P (tmp) || GET_MODE_CLASS (GET_MODE (tmp)) != MODE_INT)
+ return NULL_RTX;
+ tmp = canonicalize_condition (jump, cond, reverse, &earliest, tmp);
+ if (!tmp)
+ return NULL_RTX;
+
+ /* For sanity's sake, re-validate the new result. */
+ for (insn = earliest; insn != jump; insn = NEXT_INSN (insn))
+ if (INSN_P (insn) && modified_in_p (tmp, insn))
+ return NULL_RTX;
+
+ return tmp;
+}
+
+/* Find the implicit sets of a function. An "implicit set" is a constraint
+ on the value of a variable, implied by a conditional jump. For example,
+ following "if (x == 2)", the then branch may be optimized as though the
+ conditional performed an "explicit set", in this example, "x = 2". This
+ function records the set patterns that are implicit at the start of each
+ basic block. */
+
+static void
+find_implicit_sets ()
+{
+ basic_block bb, dest;
+ unsigned int count;
+ rtx cond, new;
+
+ count = 0;
+ FOR_EACH_BB (bb)
+ /* Check for more than one sucessor. */
+ if (bb->succ && bb->succ->succ_next)
+ {
+ cond = fis_get_condition (bb->end);
+
+ if (cond
+ && (GET_CODE (cond) == EQ || GET_CODE (cond) == NE)
+ && GET_CODE (XEXP (cond, 0)) == REG
+ && REGNO (XEXP (cond, 0)) >= FIRST_PSEUDO_REGISTER
+ && CONSTANT_P (XEXP (cond, 1)))
+ {
+ dest = GET_CODE (cond) == EQ ? BRANCH_EDGE (bb)->dest
+ : FALLTHRU_EDGE (bb)->dest;
+
+ if (dest && ! dest->pred->pred_next
+ && dest != EXIT_BLOCK_PTR)
+ {
+ new = gen_rtx_SET (VOIDmode, XEXP (cond, 0),
+ XEXP (cond, 1));
+ implicit_sets[dest->index] = new;
+ if (gcse_file)
+ {
+ fprintf(gcse_file, "Implicit set of reg %d in ",
+ REGNO (XEXP (cond, 0)));
+ fprintf(gcse_file, "basic block %d\n", dest->index);
+ }
+ count++;
+ }
+ }
+ }
+
+ if (gcse_file)
+ fprintf (gcse_file, "Found %d implicit sets\n", count);
+}
+
/* Perform one copy/constant propagation pass.
PASS is the pass count. If CPROP_JUMPS is true, perform constant
propagation into conditional jumps. If BYPASS_JUMPS is true,
local_cprop_pass (cprop_jumps);
+ /* Determine implicit sets. */
+ implicit_sets = (rtx *) xcalloc (last_basic_block, sizeof (rtx));
+ find_implicit_sets ();
+
alloc_hash_table (max_cuid, &set_hash_table, 1);
compute_hash_table (&set_hash_table);
+
+ /* Free implicit_sets before peak usage. */
+ free (implicit_sets);
+ implicit_sets = NULL;
+
if (gcse_file)
dump_hash_table (gcse_file, "SET", &set_hash_table);
if (set_hash_table.n_elems > 0)
fprintf (gcse_file, "%d const props, %d copy props\n\n",
const_prop_count, copy_prop_count);
}
+ /* Global analysis may get into infinite loops for unreachable blocks. */
+ if (changed && cprop_jumps)
+ delete_unreachable_blocks ();
return changed;
}
\f
/* Bypass conditional jumps. */
+/* The value of last_basic_block at the beginning of the jump_bypass
+ pass. The use of redirect_edge_and_branch_force may introduce new
+ basic blocks, but the data flow analysis is only valid for basic
+ block indices less than bypass_last_basic_block. */
+
+static int bypass_last_basic_block;
+
/* Find a set of REGNO to a constant that is available at the end of basic
block BB. Returns NULL if no such set is found. Based heavily upon
find_avail_set. */
for (;;)
{
rtx src;
- struct expr *set = lookup_set (regno, NULL_RTX, &set_hash_table);
+ struct expr *set = lookup_set (regno, &set_hash_table);
while (set)
{
rtx insn, note;
edge e, enext;
int i, change;
+ int may_be_loop_header;
insn = (setcc != NULL) ? setcc : jump;
if (note)
find_used_regs (&XEXP (note, 0), NULL);
+ may_be_loop_header = false;
+ for (e = bb->pred; e; e = e->pred_next)
+ if (e->flags & EDGE_DFS_BACK)
+ {
+ may_be_loop_header = true;
+ break;
+ }
+
change = 0;
for (e = bb->pred; e; e = enext)
{
enext = e->pred_next;
+ if (e->flags & EDGE_COMPLEX)
+ continue;
+
+ /* We can't redirect edges from new basic blocks. */
+ if (e->src->index >= bypass_last_basic_block)
+ continue;
+
+ /* The irreducible loops created by redirecting of edges entering the
+ loop from outside would decrease effectivity of some of the following
+ optimalizations, so prevent this. */
+ if (may_be_loop_header
+ && !(e->flags & EDGE_DFS_BACK))
+ continue;
+
for (i = 0; i < reg_use_count; i++)
{
struct reg_use *reg_used = ®_use_table[i];
if (new == pc_rtx)
dest = FALLTHRU_EDGE (bb)->dest;
else if (GET_CODE (new) == LABEL_REF)
- dest = BRANCH_EDGE (bb)->dest;
+ dest = BLOCK_FOR_INSN (XEXP (new, 0));
else
dest = NULL;
- /* Once basic block indices are stable, we should be able
- to use redirect_edge_and_branch_force instead. */
old_dest = e->dest;
- if (dest != NULL && dest != old_dest
- && redirect_edge_and_branch (e, dest))
- {
+ if (dest != NULL
+ && dest != old_dest
+ && dest != EXIT_BLOCK_PTR)
+ {
+ redirect_edge_and_branch_force (e, dest);
+
/* Copy the register setter to the redirected edge.
Don't copy CC0 setters, as CC0 is dead after jump. */
if (setcc)
/* Find basic blocks with more than one predecessor that only contain a
single conditional jump. If the result of the comparison is known at
compile-time from any incoming edge, redirect that edge to the
- appropriate target. Returns nonzero if a change was made. */
+ appropriate target. Returns nonzero if a change was made.
+
+ This function is now mis-named, because we also handle indirect jumps. */
static int
bypass_conditional_jumps ()
if (ENTRY_BLOCK_PTR->next_bb == EXIT_BLOCK_PTR)
return 0;
+ bypass_last_basic_block = last_basic_block;
+ mark_dfs_back_edges ();
+
changed = 0;
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR->next_bb->next_bb,
EXIT_BLOCK_PTR, next_bb)
}
else if (GET_CODE (insn) == JUMP_INSN)
{
- if (any_condjump_p (insn) && onlyjump_p (insn))
+ if ((any_condjump_p (insn) || computed_jump_p (insn))
+ && onlyjump_p (insn))
changed |= bypass_block (bb, setcc, insn);
break;
}