+/* Returns true if A is a cheaper cost pair than B. */
+
+static bool
+cheaper_cost_pair (struct cost_pair *a, struct cost_pair *b)
+{
+ if (!a)
+ return false;
+
+ if (!b)
+ return true;
+
+ if (a->cost < b->cost)
+ return true;
+
+ if (a->cost > b->cost)
+ return false;
+
+ /* In case the costs are the same, prefer the cheaper candidate. */
+ if (a->cand->cost < b->cand->cost)
+ return true;
+
+ return false;
+}
+
+/* Computes the cost field of IVS structure. */
+
+static void
+iv_ca_recount_cost (struct ivopts_data *data, struct iv_ca *ivs)
+{
+ unsigned cost = 0;
+
+ cost += ivs->cand_use_cost;
+ cost += ivs->cand_cost;
+ cost += ivopts_global_cost_for_size (data, ivs->n_regs);
+
+ ivs->cost = cost;
+}
+
+/* Remove invariants in set INVS to set IVS. */
+
+static void
+iv_ca_set_remove_invariants (struct iv_ca *ivs, bitmap invs)
+{
+ bitmap_iterator bi;
+ unsigned iid;
+
+ if (!invs)
+ return;
+
+ EXECUTE_IF_SET_IN_BITMAP (invs, 0, iid, bi)
+ {
+ ivs->n_invariant_uses[iid]--;
+ if (ivs->n_invariant_uses[iid] == 0)
+ ivs->n_regs--;
+ }
+}
+
+/* Set USE not to be expressed by any candidate in IVS. */
+
+static void
+iv_ca_set_no_cp (struct ivopts_data *data, struct iv_ca *ivs,
+ struct iv_use *use)
+{
+ unsigned uid = use->id, cid;
+ struct cost_pair *cp;
+
+ cp = ivs->cand_for_use[uid];
+ if (!cp)
+ return;
+ cid = cp->cand->id;
+
+ ivs->bad_uses++;
+ ivs->cand_for_use[uid] = NULL;
+ ivs->n_cand_uses[cid]--;
+
+ if (ivs->n_cand_uses[cid] == 0)
+ {
+ bitmap_clear_bit (ivs->cands, cid);
+ /* Do not count the pseudocandidates. */
+ if (cp->cand->iv)
+ ivs->n_regs--;
+ ivs->n_cands--;
+ ivs->cand_cost -= cp->cand->cost;
+
+ iv_ca_set_remove_invariants (ivs, cp->cand->depends_on);
+ }
+
+ ivs->cand_use_cost -= cp->cost;
+
+ iv_ca_set_remove_invariants (ivs, cp->depends_on);
+ iv_ca_recount_cost (data, ivs);
+}
+
+/* Add invariants in set INVS to set IVS. */
+
+static void
+iv_ca_set_add_invariants (struct iv_ca *ivs, bitmap invs)
+{
+ bitmap_iterator bi;
+ unsigned iid;
+
+ if (!invs)
+ return;
+
+ EXECUTE_IF_SET_IN_BITMAP (invs, 0, iid, bi)
+ {
+ ivs->n_invariant_uses[iid]++;
+ if (ivs->n_invariant_uses[iid] == 1)
+ ivs->n_regs++;
+ }
+}
+
+/* Set cost pair for USE in set IVS to CP. */
+
+static void
+iv_ca_set_cp (struct ivopts_data *data, struct iv_ca *ivs,
+ struct iv_use *use, struct cost_pair *cp)
+{
+ unsigned uid = use->id, cid;
+
+ if (ivs->cand_for_use[uid] == cp)
+ return;
+
+ if (ivs->cand_for_use[uid])
+ iv_ca_set_no_cp (data, ivs, use);
+
+ if (cp)
+ {
+ cid = cp->cand->id;
+
+ ivs->bad_uses--;
+ ivs->cand_for_use[uid] = cp;
+ ivs->n_cand_uses[cid]++;
+ if (ivs->n_cand_uses[cid] == 1)
+ {
+ bitmap_set_bit (ivs->cands, cid);
+ /* Do not count the pseudocandidates. */
+ if (cp->cand->iv)
+ ivs->n_regs++;
+ ivs->n_cands++;
+ ivs->cand_cost += cp->cand->cost;
+
+ iv_ca_set_add_invariants (ivs, cp->cand->depends_on);
+ }
+
+ ivs->cand_use_cost += cp->cost;
+ iv_ca_set_add_invariants (ivs, cp->depends_on);
+ iv_ca_recount_cost (data, ivs);
+ }
+}
+
+/* Extend set IVS by expressing USE by some of the candidates in it
+ if possible. */
+
+static void
+iv_ca_add_use (struct ivopts_data *data, struct iv_ca *ivs,
+ struct iv_use *use)
+{
+ struct cost_pair *best_cp = NULL, *cp;
+ bitmap_iterator bi;
+ unsigned i;
+
+ gcc_assert (ivs->upto >= use->id);
+
+ if (ivs->upto == use->id)
+ {
+ ivs->upto++;
+ ivs->bad_uses++;
+ }
+
+ EXECUTE_IF_SET_IN_BITMAP (ivs->cands, 0, i, bi)
+ {
+ cp = get_use_iv_cost (data, use, iv_cand (data, i));
+
+ if (cheaper_cost_pair (cp, best_cp))
+ best_cp = cp;
+ }
+
+ iv_ca_set_cp (data, ivs, use, best_cp);
+}
+
+/* Get cost for assignment IVS. */
+
+static unsigned
+iv_ca_cost (struct iv_ca *ivs)
+{
+ return (ivs->bad_uses ? INFTY : ivs->cost);
+}
+
+/* Returns true if all dependences of CP are among invariants in IVS. */
+
+static bool
+iv_ca_has_deps (struct iv_ca *ivs, struct cost_pair *cp)
+{
+ unsigned i;
+ bitmap_iterator bi;
+
+ if (!cp->depends_on)
+ return true;
+
+ EXECUTE_IF_SET_IN_BITMAP (cp->depends_on, 0, i, bi)
+ {
+ if (ivs->n_invariant_uses[i] == 0)
+ return false;
+ }
+
+ return true;
+}
+
+/* Creates change of expressing USE by NEW_CP instead of OLD_CP and chains
+ it before NEXT_CHANGE. */
+
+static struct iv_ca_delta *
+iv_ca_delta_add (struct iv_use *use, struct cost_pair *old_cp,
+ struct cost_pair *new_cp, struct iv_ca_delta *next_change)
+{
+ struct iv_ca_delta *change = XNEW (struct iv_ca_delta);
+
+ change->use = use;
+ change->old_cp = old_cp;
+ change->new_cp = new_cp;
+ change->next_change = next_change;
+
+ return change;
+}
+
+/* Joins two lists of changes L1 and L2. Destructive -- old lists
+ are rewritten. */
+
+static struct iv_ca_delta *
+iv_ca_delta_join (struct iv_ca_delta *l1, struct iv_ca_delta *l2)
+{
+ struct iv_ca_delta *last;
+
+ if (!l2)
+ return l1;
+
+ if (!l1)
+ return l2;
+
+ for (last = l1; last->next_change; last = last->next_change)
+ continue;
+ last->next_change = l2;
+
+ return l1;
+}
+
+/* Returns candidate by that USE is expressed in IVS. */
+
+static struct cost_pair *
+iv_ca_cand_for_use (struct iv_ca *ivs, struct iv_use *use)
+{
+ return ivs->cand_for_use[use->id];
+}
+
+/* Reverse the list of changes DELTA, forming the inverse to it. */
+
+static struct iv_ca_delta *
+iv_ca_delta_reverse (struct iv_ca_delta *delta)
+{
+ struct iv_ca_delta *act, *next, *prev = NULL;
+ struct cost_pair *tmp;
+
+ for (act = delta; act; act = next)
+ {
+ next = act->next_change;
+ act->next_change = prev;
+ prev = act;
+
+ tmp = act->old_cp;
+ act->old_cp = act->new_cp;
+ act->new_cp = tmp;
+ }
+
+ return prev;
+}
+
+/* Commit changes in DELTA to IVS. If FORWARD is false, the changes are
+ reverted instead. */
+
+static void
+iv_ca_delta_commit (struct ivopts_data *data, struct iv_ca *ivs,
+ struct iv_ca_delta *delta, bool forward)
+{
+ struct cost_pair *from, *to;
+ struct iv_ca_delta *act;
+
+ if (!forward)
+ delta = iv_ca_delta_reverse (delta);
+
+ for (act = delta; act; act = act->next_change)
+ {
+ from = act->old_cp;
+ to = act->new_cp;
+ gcc_assert (iv_ca_cand_for_use (ivs, act->use) == from);
+ iv_ca_set_cp (data, ivs, act->use, to);
+ }
+
+ if (!forward)
+ iv_ca_delta_reverse (delta);
+}
+
+/* Returns true if CAND is used in IVS. */
+
+static bool
+iv_ca_cand_used_p (struct iv_ca *ivs, struct iv_cand *cand)
+{
+ return ivs->n_cand_uses[cand->id] > 0;
+}
+
+/* Returns number of induction variable candidates in the set IVS. */
+
+static unsigned
+iv_ca_n_cands (struct iv_ca *ivs)
+{
+ return ivs->n_cands;
+}
+
+/* Free the list of changes DELTA. */
+
+static void
+iv_ca_delta_free (struct iv_ca_delta **delta)
+{
+ struct iv_ca_delta *act, *next;
+
+ for (act = *delta; act; act = next)
+ {
+ next = act->next_change;
+ free (act);
+ }
+
+ *delta = NULL;
+}
+
+/* Allocates new iv candidates assignment. */
+
+static struct iv_ca *
+iv_ca_new (struct ivopts_data *data)
+{
+ struct iv_ca *nw = XNEW (struct iv_ca);
+
+ nw->upto = 0;
+ nw->bad_uses = 0;
+ nw->cand_for_use = XCNEWVEC (struct cost_pair *, n_iv_uses (data));
+ nw->n_cand_uses = XCNEWVEC (unsigned, n_iv_cands (data));
+ nw->cands = BITMAP_ALLOC (NULL);
+ nw->n_cands = 0;
+ nw->n_regs = 0;
+ nw->cand_use_cost = 0;
+ nw->cand_cost = 0;
+ nw->n_invariant_uses = XCNEWVEC (unsigned, data->max_inv_id + 1);
+ nw->cost = 0;
+
+ return nw;
+}
+
+/* Free memory occupied by the set IVS. */
+
+static void
+iv_ca_free (struct iv_ca **ivs)
+{
+ free ((*ivs)->cand_for_use);
+ free ((*ivs)->n_cand_uses);
+ BITMAP_FREE ((*ivs)->cands);
+ free ((*ivs)->n_invariant_uses);
+ free (*ivs);
+ *ivs = NULL;
+}
+
+/* Dumps IVS to FILE. */
+
+static void
+iv_ca_dump (struct ivopts_data *data, FILE *file, struct iv_ca *ivs)
+{
+ const char *pref = " invariants ";
+ unsigned i;
+
+ fprintf (file, " cost %d\n", iv_ca_cost (ivs));
+ bitmap_print (file, ivs->cands, " candidates ","\n");
+
+ for (i = 1; i <= data->max_inv_id; i++)
+ if (ivs->n_invariant_uses[i])
+ {
+ fprintf (file, "%s%d", pref, i);
+ pref = ", ";
+ }
+ fprintf (file, "\n");
+}
+
+/* Try changing candidate in IVS to CAND for each use. Return cost of the
+ new set, and store differences in DELTA. Number of induction variables
+ in the new set is stored to N_IVS. */
+
+static unsigned
+iv_ca_extend (struct ivopts_data *data, struct iv_ca *ivs,
+ struct iv_cand *cand, struct iv_ca_delta **delta,
+ unsigned *n_ivs)
+{
+ unsigned i, cost;
+ struct iv_use *use;
+ struct cost_pair *old_cp, *new_cp;
+
+ *delta = NULL;
+ for (i = 0; i < ivs->upto; i++)
+ {
+ use = iv_use (data, i);
+ old_cp = iv_ca_cand_for_use (ivs, use);
+
+ if (old_cp
+ && old_cp->cand == cand)
+ continue;
+
+ new_cp = get_use_iv_cost (data, use, cand);
+ if (!new_cp)
+ continue;
+
+ if (!iv_ca_has_deps (ivs, new_cp))
+ continue;
+
+ if (!cheaper_cost_pair (new_cp, old_cp))
+ continue;
+
+ *delta = iv_ca_delta_add (use, old_cp, new_cp, *delta);
+ }
+
+ iv_ca_delta_commit (data, ivs, *delta, true);
+ cost = iv_ca_cost (ivs);
+ if (n_ivs)
+ *n_ivs = iv_ca_n_cands (ivs);
+ iv_ca_delta_commit (data, ivs, *delta, false);
+
+ return cost;
+}
+
+/* Try narrowing set IVS by removing CAND. Return the cost of
+ the new set and store the differences in DELTA. */