struct imm_store_chain_info
{
+ /* Doubly-linked list that imposes an order on chain processing.
+ PNXP (prev's next pointer) points to the head of a list, or to
+ the next field in the previous chain in the list.
+ See pass_store_merging::m_stores_head for more rationale. */
+ imm_store_chain_info *next, **pnxp;
tree base_addr;
auto_vec<struct store_immediate_info *> m_store_info;
auto_vec<merged_store_group *> m_merged_store_groups;
- imm_store_chain_info (tree b_a) : base_addr (b_a) {}
+ imm_store_chain_info (imm_store_chain_info *&inspt, tree b_a)
+ : next (inspt), pnxp (&inspt), base_addr (b_a)
+ {
+ inspt = this;
+ if (next)
+ {
+ gcc_checking_assert (pnxp == next->pnxp);
+ next->pnxp = &next;
+ }
+ }
+ ~imm_store_chain_info ()
+ {
+ *pnxp = next;
+ if (next)
+ {
+ gcc_checking_assert (&next == next->pnxp);
+ next->pnxp = pnxp;
+ }
+ }
bool terminate_and_process_chain ();
bool coalesce_immediate_stores ();
bool output_merged_store (merged_store_group *);
private:
hash_map<tree_operand_hash, struct imm_store_chain_info *> m_stores;
+ /* Form a doubly-linked stack of the elements of m_stores, so that
+ we can iterate over them in a predictable way. Using this order
+ avoids extraneous differences in the compiler output just because
+ of tree pointer variations (e.g. different chains end up in
+ different positions of m_stores, so they are handled in different
+ orders, so they allocate or release SSA names in different
+ orders, and when they get reused, subsequent passes end up
+ getting different SSA names, which may ultimately change
+ decisions when going out of SSA). */
+ imm_store_chain_info *m_stores_head;
+
bool terminate_and_process_all_chains ();
bool terminate_all_aliasing_chains (imm_store_chain_info **,
bool, gimple *);
bool
pass_store_merging::terminate_and_process_all_chains ()
{
- hash_map<tree_operand_hash, struct imm_store_chain_info *>::iterator iter
- = m_stores.begin ();
bool ret = false;
- for (; iter != m_stores.end (); ++iter)
- ret |= terminate_and_release_chain ((*iter).second);
+ while (m_stores_head)
+ ret |= terminate_and_release_chain (m_stores_head);
+ gcc_assert (m_stores.elements () == 0);
+ gcc_assert (m_stores_head == NULL);
return ret;
}
}
}
- hash_map<tree_operand_hash, struct imm_store_chain_info *>::iterator iter
- = m_stores.begin ();
-
/* Check for aliasing with all other store chains. */
- for (; iter != m_stores.end (); ++iter)
+ for (imm_store_chain_info *next = m_stores_head, *cur = next; cur; cur = next)
{
+ next = cur->next;
+
/* We already checked all the stores in chain_info and terminated the
chain if necessary. Skip it here. */
- if (chain_info && (*chain_info) == (*iter).second)
+ if (chain_info && (*chain_info) == cur)
continue;
/* We can't use the base object here as that does not reliably exist.
minimum and maximum offset and the maximum size we could improve
things here). */
ao_ref chain_ref;
- ao_ref_init_from_ptr_and_size (&chain_ref, (*iter).first, NULL_TREE);
+ ao_ref_init_from_ptr_and_size (&chain_ref, cur->base_addr, NULL_TREE);
if (ref_maybe_used_by_stmt_p (stmt, &chain_ref)
|| stmt_may_clobber_ref_p_1 (stmt, &chain_ref))
{
- terminate_and_release_chain ((*iter).second);
+ terminate_and_release_chain (cur);
ret = true;
}
}
{
gimple *stmt = gsi_stmt (gsi);
+ if (is_gimple_debug (stmt))
+ continue;
+
if (gimple_has_volatile_ops (stmt))
{
/* Terminate all chains. */
continue;
}
- if (is_gimple_debug (stmt))
- continue;
-
if (gimple_assign_single_p (stmt) && gimple_vdef (stmt)
&& !stmt_can_throw_internal (stmt)
&& lhs_valid_for_store_merging_p (gimple_assign_lhs (stmt)))
terminate_all_aliasing_chains (chain_info, false, stmt);
/* Start a new chain. */
struct imm_store_chain_info *new_chain
- = new imm_store_chain_info (base_addr);
+ = new imm_store_chain_info (m_stores_head, base_addr);
info = new store_immediate_info (bitsize, bitpos,
stmt, 0);
new_chain->m_store_info.safe_push (info);