]> oss.titaniummirror.com Git - msp430-gcc.git/blobdiff - gcc/rtl-factoring.c
Imported gcc-4.4.3
[msp430-gcc.git] / gcc / rtl-factoring.c
diff --git a/gcc/rtl-factoring.c b/gcc/rtl-factoring.c
new file mode 100644 (file)
index 0000000..4c87969
--- /dev/null
@@ -0,0 +1,1474 @@
+/* RTL factoring (sequence abstraction).
+   Copyright (C) 2004, 2005, 2006, 2007, 2008 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 3, 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 COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "obstack.h"
+#include "basic-block.h"
+#include "resource.h"
+#include "flags.h"
+#include "ggc.h"
+#include "regs.h"
+#include "params.h"
+#include "expr.h"
+#include "tm_p.h"
+#include "tree-pass.h"
+#include "tree-flow.h"
+#include "timevar.h"
+#include "output.h"
+#include "df.h"
+#include "addresses.h"
+
+/* Sequence abstraction:
+
+   It is a size optimization method. The main idea of this technique is to
+   find identical sequences of code, which can be turned into procedures and
+   then replace all occurrences with calls to the newly created subroutine.
+   It is kind of an opposite of function inlining.
+
+   There are four major parts of this file:
+
+   sequence fingerprint
+     In order to avoid the comparison of every insn with every other, hash
+     value will be designed for every insn by COMPUTE_HASH.
+     These hash values are used for grouping the sequence candidates. So
+     we only need to compare every insn with every other in same hash group.
+
+     FILL_HASH_BUCKET creates all hash values and stores into HASH_BUCKETS.
+     The result is used by COLLECT_PATTERN_SEQS.
+
+   code matching
+     In code matching the algorithm compares every two possible sequence
+     candidates which last insns are in the same hash group. If these
+     sequences are identical they will be stored and do further searches for
+     finding more sequences which are identical with the first one.
+
+     COLLECT_PATTERN_SEQS does the code matching and stores the results into
+     PATTERN_SEQS.
+
+   gain computation
+     This part computes the gain of abstraction which could be archived when
+     turning the pattern sequence into a pseudo-function and its matching
+     sequences into pseudo-calls. After it the most effective sequences will
+     be marked for abstraction.
+
+     RECOMPUTE_GAIN does the gain computation. The sequences with the maximum
+     gain is on the top of PATTERN_SEQS.
+
+   abstract code
+     This part turns the pattern sequence into a pseudo-function and its
+     matching sequences into pseudo-calls.
+
+     ABSTRACT_BEST_SEQ does the code merging.
+
+
+   C code example:
+
+   // Original source            // After sequence abstraction
+   {                             {
+                                   void *jump_label;
+     ...                           ...
+                                   jump_label = &&exit_0;
+                                 entry_0:
+     I0;                           I0;
+     I1;                           I1;
+     I2;                           I2;
+     I3;                           I3;
+                                   goto *jump_label;
+                                 exit_0:
+     ...                           ...
+                                   jump_label = &&exit_1;
+                                 goto entry_0;
+     I0;
+     I1;
+     I2;
+     I3;
+                                 exit_1:
+     ...                           ...
+                                   jump_label = &&exit_2;
+                                   goto entry_0;
+     I0;
+     I1;
+     I2;
+     I3;
+                                 exit_2:
+     ...                           ...
+                                   jump_label = &&exit_3;
+                                   goto entry_0;
+     I0;
+     I1;
+     I2;
+     I3;
+                                exit_3:
+     ...                           ...
+   }                             }
+
+
+   TODO:
+   - Use REG_ALLOC_ORDER when choosing link register.
+   - Handle JUMP_INSNs. Also handle volatile function calls (handle them
+     similar to unconditional jumps.)
+   - Test command line option -fpic.
+*/
+
+/* Predicate yielding nonzero iff X is an abstractable insn.  Non-jump insns are
+   abstractable.  */
+#define ABSTRACTABLE_INSN_P(X) (INSN_P (X) && !JUMP_P (X))
+
+/* First parameter of the htab_create function call.  */
+#define HASH_INIT 1023
+
+/* Multiplier for cost of sequence call to avoid abstracting short
+   sequences.  */
+#ifndef SEQ_CALL_COST_MULTIPLIER
+#define SEQ_CALL_COST_MULTIPLIER 2
+#endif
+
+/* Recomputes the cost of MSEQ pattern/matching sequence.  */
+#define RECOMPUTE_COST(SEQ)                                 \
+{                                                           \
+  int l;                                                    \
+  rtx x = SEQ->insn;                                        \
+  SEQ->cost = 0;                                            \
+  for (l = 0; l < SEQ->abstracted_length; l++)              \
+    {                                                       \
+      SEQ->cost += compute_rtx_cost (x);                    \
+      x = prev_insn_in_block (x);                           \
+    }                                                       \
+}
+
+/* A sequence matching a pattern sequence.  */
+typedef struct matching_seq_def
+{
+  /* The last insn in the matching sequence.  */
+  rtx insn;
+
+  /* Index of INSN instruction.  */
+  unsigned long idx;
+
+  /* The number of insns matching in this sequence and the pattern sequence.
+   */
+  int matching_length;
+
+  /* The number of insns selected to abstract from this sequence. Less than
+     or equal to MATCHING_LENGTH.  */
+  int abstracted_length;
+
+  /* The cost of the sequence.  */
+  int cost;
+
+  /* The next sequence in the chain matching the same pattern.  */
+  struct matching_seq_def *next_matching_seq;
+} *matching_seq;
+
+
+/* A pattern instruction sequence.  */
+typedef struct pattern_seq_def
+{
+  /* The last insn in the pattern sequence.  */
+  rtx insn;
+
+  /* Index of INSN instruction.  */
+  unsigned long idx;
+
+  /* The gain of transforming the pattern sequence into a pseudo-function and
+     the matching sequences into pseudo-calls.  */
+  int gain;
+
+  /* The maximum of the ABSTRACTED_LENGTH of the matching sequences.  */
+  int abstracted_length;
+
+  /* The cost of the sequence.  */
+  int cost;
+
+  /* The register used to hold the return address during the pseudo-call.  */
+  rtx link_reg;
+
+  /* The sequences matching this pattern.  */
+  matching_seq matching_seqs;
+
+  /* The next pattern sequence in the chain.  */
+  struct pattern_seq_def *next_pattern_seq;
+} *pattern_seq;
+
+
+/* A block of a pattern sequence.  */
+typedef struct seq_block_def
+{
+  /* The number of insns in the block.  */
+  int length;
+
+  /* The code_label of the block.  */
+  rtx label;
+
+  /* The sequences entering the pattern sequence at LABEL.  */
+  matching_seq matching_seqs;
+
+  /* The next block in the chain. The blocks are sorted by LENGTH in
+     ascending order.  */
+  struct seq_block_def *next_seq_block;
+} *seq_block;
+
+/* Contains same sequence candidates for further searching.  */
+typedef struct hash_bucket_def
+{
+  /* The hash value of the group.  */
+  unsigned int hash;
+
+  /* List of sequence candidates.  */
+  htab_t seq_candidates;
+} *p_hash_bucket;
+typedef const struct hash_bucket_def *const_p_hash_bucket;
+
+/* Contains the last insn of the sequence, and its index value.  */
+typedef struct hash_elem_def
+{
+  /* Unique index; ordered by FILL_HASH_BUCKET.  */
+  unsigned long idx;
+
+  /* The last insn in the sequence.  */
+  rtx insn;
+
+  /* The cached length of the insn.  */
+  int length;
+} *p_hash_elem;
+typedef const struct hash_elem_def *const_p_hash_elem;
+
+/* The list of same sequence candidates.  */
+static htab_t hash_buckets;
+
+/* The pattern sequences collected from the current functions.  */
+static pattern_seq pattern_seqs;
+
+/* The blocks of the current pattern sequence.  */
+static seq_block seq_blocks;
+
+/* Cost of calling sequence.  */
+static int seq_call_cost;
+
+/* Cost of jump.  */
+static int seq_jump_cost;
+
+/* Cost of returning.  */
+static int seq_return_cost;
+
+/* Returns the first insn preceding INSN for which INSN_P is true and belongs to
+   the same basic block. Returns NULL_RTX if no such insn can be found.  */
+
+static rtx
+prev_insn_in_block (rtx insn)
+{
+  basic_block bb = BLOCK_FOR_INSN (insn);
+
+  if (!bb)
+    return NULL_RTX;
+
+  while (insn != BB_HEAD (bb))
+    {
+      insn = PREV_INSN (insn);
+      if (INSN_P (insn))
+        return insn;
+    }
+  return NULL_RTX;
+}
+
+/* Returns the hash value of INSN.  */
+
+static unsigned int
+compute_hash (rtx insn)
+{
+  unsigned int hash = 0;
+  rtx prev;
+
+  hash = INSN_CODE (insn) * 100;
+
+  prev = prev_insn_in_block (insn);
+  if (prev)
+    hash += INSN_CODE (prev);
+
+  return hash;
+}
+
+/* Compute the cost of INSN rtx for abstraction.  */
+
+static int
+compute_rtx_cost (rtx insn)
+{
+  struct hash_bucket_def tmp_bucket;
+  p_hash_bucket bucket;
+  struct hash_elem_def tmp_elem;
+  p_hash_elem elem = NULL;
+  int cost = -1;
+
+  /* Compute hash value for INSN.  */
+  tmp_bucket.hash = compute_hash (insn);
+
+  /* Select the hash group.  */
+  bucket = (p_hash_bucket) htab_find (hash_buckets, &tmp_bucket);
+
+  if (bucket)
+  {
+    tmp_elem.insn = insn;
+
+    /* Select the insn.  */
+    elem = (p_hash_elem) htab_find (bucket->seq_candidates, &tmp_elem);
+
+    /* If INSN is parsed the cost will be the cached length.  */
+    if (elem)
+      cost = elem->length;
+  }
+
+  /* If we can't parse the INSN cost will be the instruction length.  */
+  if (cost == -1)
+  {
+    cost = get_attr_length (insn);
+
+    /* Cache the length.  */
+    if (elem)
+      elem->length = cost;
+  }
+
+  /* If we can't get an accurate estimate for a complex instruction,
+     assume that it has the same cost as a single fast instruction.  */
+  return cost != 0 ? cost : COSTS_N_INSNS (1);
+}
+
+/* Determines the number of common insns in the sequences ending in INSN1 and
+   INSN2. Returns with LEN number of common insns and COST cost of sequence.
+*/
+
+static void
+matching_length (rtx insn1, rtx insn2, int* len, int* cost)
+{
+  rtx x1;
+  rtx x2;
+
+  x1 = insn1;
+  x2 = insn2;
+  *len = 0;
+  *cost = 0;
+  while (x1 && x2 && (x1 != insn2) && (x2 != insn1)
+         && rtx_equal_p (PATTERN (x1), PATTERN (x2)))
+    {
+      (*len)++;
+      (*cost) += compute_rtx_cost (x1);
+      x1 = prev_insn_in_block (x1);
+      x2 = prev_insn_in_block (x2);
+    }
+}
+
+/* Adds E0 as a pattern sequence to PATTERN_SEQS with E1 as a matching
+   sequence.  */
+
+static void
+match_seqs (p_hash_elem e0, p_hash_elem e1)
+{
+  int len;
+  int cost;
+  matching_seq mseq, p_prev, p_next;
+
+  /* Determines the cost of the sequence and return without doing anything
+     if it is too small to produce any gain.  */
+  matching_length (e0->insn, e1->insn, &len, &cost);
+  if (cost <= seq_call_cost)
+    return;
+
+  /* Prepend a new PATTERN_SEQ to PATTERN_SEQS if the last pattern sequence
+     does not end in E0->INSN. This assumes that once the E0->INSN changes
+     the old value will never appear again.  */
+  if (!pattern_seqs || pattern_seqs->insn != e0->insn)
+    {
+      pattern_seq pseq =
+        (pattern_seq) xmalloc (sizeof (struct pattern_seq_def));
+      pseq->insn = e0->insn;
+      pseq->idx = e0->idx;
+      pseq->gain = 0;                 /* Set to zero to force recomputing.  */
+      pseq->abstracted_length = 0;
+      pseq->cost = 0;
+      pseq->link_reg = NULL_RTX;
+      pseq->matching_seqs = NULL;
+      pseq->next_pattern_seq = pattern_seqs;
+      pattern_seqs = pseq;
+    }
+
+  /* Find the position of E1 in the matching sequences list.  */
+  p_prev = NULL;
+  p_next = pattern_seqs->matching_seqs;
+  while (p_next && p_next->idx < e1->idx)
+    {
+      p_prev = p_next;
+      p_next = p_next->next_matching_seq;
+    }
+
+  /* Add a new E1 matching sequence to the pattern sequence. We know that
+     it ends in E0->INSN.  */
+  mseq = (matching_seq) xmalloc (sizeof (struct matching_seq_def));
+  mseq->insn = e1->insn;
+  mseq->idx = e1->idx;
+  mseq->matching_length = len;
+  mseq->abstracted_length = 0;
+  mseq->cost = cost;
+
+  if (p_prev == NULL)
+    pattern_seqs->matching_seqs = mseq;
+  else
+    p_prev->next_matching_seq = mseq;
+  mseq->next_matching_seq = p_next;
+}
+
+/* Collects all pattern sequences and their matching sequences and puts them
+   into PATTERN_SEQS.  */
+
+static void
+collect_pattern_seqs (void)
+{
+  htab_iterator hti0, hti1, hti2;
+  p_hash_bucket hash_bucket;
+  p_hash_elem e0, e1;
+#if defined STACK_REGS || defined HAVE_cc0
+  basic_block bb;
+  bitmap_head dont_collect;
+
+  /* Extra initialization step to ensure that no stack registers (if present)
+     or cc0 code (if present) are live across abnormal edges.
+     Set a flag in DONT_COLLECT for an insn if a stack register is live
+     after the insn or the insn is cc0 setter or user.  */
+  bitmap_initialize (&dont_collect, NULL);
+
+#ifdef STACK_REGS
+  FOR_EACH_BB (bb)
+  {
+    regset_head live;
+    rtx insn;
+    rtx prev;
+
+    /* Initialize liveness propagation.  */
+    INIT_REG_SET (&live);
+    bitmap_copy (&live, DF_LR_OUT (bb));
+    df_simulate_initialize_backwards (bb, &live);
+
+    /* Propagate liveness info and mark insns where a stack reg is live.  */
+    insn = BB_END (bb);
+    for (insn = BB_END (bb); ; insn = prev)
+      {
+       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 (&dont_collect, 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);
+  }
+#endif
+
+#ifdef HAVE_cc0
+  /* Mark CC0 setters and users as ineligible for collection into sequences.
+     This is an over-conservative fix, since it is OK to include
+     a cc0_setter, but only if we also include the corresponding cc0_user,
+     and vice versa.  */
+  FOR_EACH_BB (bb)
+  {
+    rtx insn;
+    rtx next_tail;
+
+    next_tail = NEXT_INSN (BB_END (bb));
+
+    for (insn = BB_HEAD (bb); insn != next_tail; insn = NEXT_INSN (insn))
+      {
+       if (INSN_P (insn) && reg_mentioned_p (cc0_rtx, PATTERN (insn)))
+         bitmap_set_bit (&dont_collect, INSN_UID (insn));
+      }
+  }
+#endif
+
+#endif /* defined STACK_REGS || defined HAVE_cc0 */
+
+  /* Initialize PATTERN_SEQS to empty.  */
+  pattern_seqs = 0;
+
+  /* Try to match every abstractable insn with every other insn in the same
+     HASH_BUCKET.  */
+
+  FOR_EACH_HTAB_ELEMENT (hash_buckets, hash_bucket, p_hash_bucket, hti0)
+    if (htab_elements (hash_bucket->seq_candidates) > 1)
+      FOR_EACH_HTAB_ELEMENT (hash_bucket->seq_candidates, e0, p_hash_elem, hti1)
+        FOR_EACH_HTAB_ELEMENT (hash_bucket->seq_candidates, e1, p_hash_elem,
+                               hti2)
+          if (e0 != e1
+#if defined STACK_REGS || defined HAVE_cc0
+              && !bitmap_bit_p (&dont_collect, INSN_UID (e0->insn))
+              && !bitmap_bit_p (&dont_collect, INSN_UID (e1->insn))
+#endif
+             )
+            match_seqs (e0, e1);
+#if defined STACK_REGS || defined HAVE_cc0
+  /* Free unused data.  */
+  bitmap_clear (&dont_collect);
+#endif
+}
+
+/* Transforms a regset to a HARD_REG_SET. Every hard register in REGS is added
+   to hregs. Additionally, the hard counterpart of every renumbered pseudo
+   register is also added.  */
+
+static void
+renumbered_reg_set_to_hard_reg_set (HARD_REG_SET * hregs, regset regs)
+{
+  int r;
+
+  REG_SET_TO_HARD_REG_SET (*hregs, regs);
+  for (r = FIRST_PSEUDO_REGISTER; r < max_regno; r++)
+    if (REGNO_REG_SET_P (regs, r) && reg_renumber[r] >= 0)
+      SET_HARD_REG_BIT (*hregs, reg_renumber[r]);
+}
+
+/* Clears the bits in REGS for all registers, which are live in the sequence
+   give by its last INSN and its LENGTH.  */
+
+static void
+clear_regs_live_in_seq (HARD_REG_SET * regs, rtx insn, int length)
+{
+  basic_block bb;
+  regset_head live;
+  HARD_REG_SET hlive;
+  rtx x;
+  int i;
+
+  /* Initialize liveness propagation.  */
+  bb = BLOCK_FOR_INSN (insn);
+  INIT_REG_SET (&live);
+  bitmap_copy (&live, DF_LR_OUT (bb));
+  df_simulate_initialize_backwards (bb, &live);
+
+  /* Propagate until INSN if found.  */
+  for (x = BB_END (bb); x != insn; x = PREV_INSN (x))
+    df_simulate_one_insn_backwards (bb, x, &live);
+
+  /* Clear registers live after INSN.  */
+  renumbered_reg_set_to_hard_reg_set (&hlive, &live);
+  AND_COMPL_HARD_REG_SET (*regs, hlive);
+
+  /* Clear registers live in and before the sequence.  */
+  for (i = 0; i < length;)
+    {
+      rtx prev = PREV_INSN (x);
+      df_simulate_one_insn_backwards (bb, x, &live);
+
+      if (INSN_P (x))
+        {
+          renumbered_reg_set_to_hard_reg_set (&hlive, &live);
+          AND_COMPL_HARD_REG_SET (*regs, hlive);
+          i++;
+        }
+
+      x = prev;
+    }
+
+  /* Free unused data.  */
+  CLEAR_REG_SET (&live);
+}
+
+/* Computes the gain of turning PSEQ into a pseudo-function and its matching
+   sequences into pseudo-calls. Also computes and caches the number of insns to
+   abstract from  the matching sequences.  */
+
+static void
+recompute_gain_for_pattern_seq (pattern_seq pseq)
+{
+  matching_seq mseq;
+  rtx x;
+  int i;
+  int hascall;
+  HARD_REG_SET linkregs;
+
+  /* Initialize data.  */
+  SET_HARD_REG_SET (linkregs);
+  pseq->link_reg = NULL_RTX;
+  pseq->abstracted_length = 0;
+
+  pseq->gain = -(seq_call_cost - seq_jump_cost + seq_return_cost);
+
+  /* Determine ABSTRACTED_LENGTH and COST for matching sequences of PSEQ.
+     ABSTRACTED_LENGTH may be less than MATCHING_LENGTH if sequences in the
+     same block overlap. */
+
+  for (mseq = pseq->matching_seqs; mseq; mseq = mseq->next_matching_seq)
+    {
+      /* Determine ABSTRACTED_LENGTH.  */
+      if (mseq->next_matching_seq)
+        mseq->abstracted_length = (int)(mseq->next_matching_seq->idx -
+                                        mseq->idx);
+      else
+        mseq->abstracted_length = mseq->matching_length;
+
+      if (mseq->abstracted_length > mseq->matching_length)
+        mseq->abstracted_length = mseq->matching_length;
+
+      /* Compute the cost of sequence.  */
+      RECOMPUTE_COST (mseq);
+
+      /* If COST is big enough registers live in this matching sequence
+         should not be used as a link register. Also set ABSTRACTED_LENGTH
+         of PSEQ.  */
+      if (mseq->cost > seq_call_cost)
+        {
+          clear_regs_live_in_seq (&linkregs, mseq->insn,
+                                  mseq->abstracted_length);
+          if (mseq->abstracted_length > pseq->abstracted_length)
+            pseq->abstracted_length = mseq->abstracted_length;
+        }
+    }
+
+  /* Modify ABSTRACTED_LENGTH of PSEQ if pattern sequence overlaps with one
+     of the matching sequences.  */
+  for (mseq = pseq->matching_seqs; mseq; mseq = mseq->next_matching_seq)
+    {
+      x = pseq->insn;
+      for (i = 0; (i < pseq->abstracted_length) && (x != mseq->insn); i++)
+        x = prev_insn_in_block (x);
+      pseq->abstracted_length = i;
+    }
+
+  /* Compute the cost of pattern sequence.  */
+  RECOMPUTE_COST (pseq);
+
+  /* No gain if COST is too small.  */
+  if (pseq->cost <= seq_call_cost)
+  {
+    pseq->gain = -1;
+    return;
+  }
+
+  /* Ensure that no matching sequence is longer than the pattern sequence.  */
+  for (mseq = pseq->matching_seqs; mseq; mseq = mseq->next_matching_seq)
+    {
+      if (mseq->abstracted_length > pseq->abstracted_length)
+        {
+          mseq->abstracted_length = pseq->abstracted_length;
+          RECOMPUTE_COST (mseq);
+        }
+      /* Once the length is stabilizing the gain can be calculated.  */
+      if (mseq->cost > seq_call_cost)
+        pseq->gain += mseq->cost - seq_call_cost;
+    }
+
+  /* No need to do further work if there is no gain.  */
+  if (pseq->gain <= 0)
+    return;
+
+  /* Should not use registers live in the pattern sequence as link register.
+   */
+  clear_regs_live_in_seq (&linkregs, pseq->insn, pseq->abstracted_length);
+
+  /* Determine whether pattern sequence contains a call_insn.  */
+  hascall = 0;
+  x = pseq->insn;
+  for (i = 0; i < pseq->abstracted_length; i++)
+    {
+      if (CALL_P (x))
+        {
+          hascall = 1;
+          break;
+        }
+      x = prev_insn_in_block (x);
+    }
+
+  /* Should not use a register as a link register if - it is a fixed
+     register, or - the sequence contains a call insn and the register is a
+     call used register, or - the register needs to be saved if used in a
+     function but was not used before (since saving it can invalidate already
+     computed frame pointer offsets), or - the register cannot be used as a
+     base register.  */
+
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    if (fixed_regs[i]
+#ifdef REGNO_OK_FOR_INDIRECT_JUMP_P
+        || (!REGNO_OK_FOR_INDIRECT_JUMP_P (i, Pmode))
+#else
+        || (!ok_for_base_p_1 (i, Pmode, MEM, SCRATCH))
+        || (!reg_class_subset_p (REGNO_REG_CLASS (i),
+                                base_reg_class (VOIDmode, MEM, SCRATCH)))
+#endif
+        || (hascall && call_used_regs[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.  */
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    if (TEST_HARD_REG_BIT (linkregs, i))
+      {
+        pseq->link_reg = gen_rtx_REG (Pmode, i);
+        break;
+      }
+
+  /* Abstraction is not possible if no link register is available, so set
+     gain to 0.  */
+  if (!pseq->link_reg)
+    pseq->gain = 0;
+}
+
+/* Deallocates memory occupied by PSEQ and its matching seqs.  */
+
+static void
+free_pattern_seq (pattern_seq pseq)
+{
+  while (pseq->matching_seqs)
+    {
+      matching_seq mseq = pseq->matching_seqs;
+      pseq->matching_seqs = mseq->next_matching_seq;
+      free (mseq);
+    }
+  free (pseq);
+}
+
+
+/* Computes the gain for pattern sequences. Pattern sequences producing no gain
+   are deleted. The pattern sequence with the biggest gain is moved to the first
+   place of PATTERN_SEQS.  */
+
+static void
+recompute_gain (void)
+{
+  pattern_seq *pseq;
+  int maxgain;
+
+  maxgain = 0;
+  for (pseq = &pattern_seqs; *pseq;)
+    {
+      if ((*pseq)->gain <= 0)
+        recompute_gain_for_pattern_seq (*pseq);
+
+      if ((*pseq)->gain > 0)
+        {
+          if ((*pseq)->gain > maxgain)
+            {
+              pattern_seq temp = *pseq;
+              (*pseq) = temp->next_pattern_seq;
+              temp->next_pattern_seq = pattern_seqs;
+              pattern_seqs = temp;
+              maxgain = pattern_seqs->gain;
+            }
+          else
+            {
+              pseq = &(*pseq)->next_pattern_seq;
+            }
+        }
+      else
+        {
+          pattern_seq temp = *pseq;
+          *pseq = temp->next_pattern_seq;
+          free_pattern_seq (temp);
+        }
+    }
+}
+
+/* Updated those pattern sequences and matching sequences, which overlap with
+   the sequence given by INSN and LEN. Deletes sequences shrinking below a
+   limit.  */
+
+static void
+erase_from_pattern_seqs (rtx insn, int len)
+{
+  pattern_seq *pseq;
+  matching_seq *mseq;
+  rtx x;
+  int plen, mlen;
+  int pcost, mcost;
+
+  while (len > 0)
+    {
+      for (pseq = &pattern_seqs; *pseq;)
+        {
+          plen = 0;
+          pcost = 0;
+          for (x = (*pseq)->insn; x && (x != insn);
+               x = prev_insn_in_block (x))
+            {
+              plen++;
+              pcost += compute_rtx_cost (x);
+            }
+
+          if (pcost <= seq_call_cost)
+            {
+              pattern_seq temp = *pseq;
+              *pseq = temp->next_pattern_seq;
+              free_pattern_seq (temp);
+            }
+          else
+            {
+              for (mseq = &(*pseq)->matching_seqs; *mseq;)
+                {
+                  mlen = 0;
+                  mcost = 0;
+                  for (x = (*mseq)->insn;
+                       x && (x != insn) && (mlen < plen)
+                       && (mlen < (*mseq)->matching_length);
+                       x = prev_insn_in_block (x))
+                    {
+                      mlen++;
+                      mcost += compute_rtx_cost (x);
+                    }
+
+                  if (mcost <= seq_call_cost)
+                    {
+                      matching_seq temp = *mseq;
+                      *mseq = temp->next_matching_seq;
+                      free (temp);
+                      /* Set to 0 to force gain recomputation.  */
+                      (*pseq)->gain = 0;
+                    }
+                  else
+                    {
+                      if (mlen < (*mseq)->matching_length)
+                        {
+                          (*mseq)->cost = mcost;
+                          (*mseq)->matching_length = mlen;
+                          /* Set to 0 to force gain recomputation.  */
+                          (*pseq)->gain = 0;
+                        }
+                      mseq = &(*mseq)->next_matching_seq;
+                    }
+                }
+
+              pseq = &(*pseq)->next_pattern_seq;
+            }
+        }
+
+      len--;
+      insn = prev_insn_in_block (insn);
+    }
+}
+
+/* Updates those pattern sequences and matching sequences, which overlap with
+   the pattern sequence with the biggest gain and its matching sequences.  */
+
+static void
+update_pattern_seqs (void)
+{
+  pattern_seq bestpseq;
+  matching_seq mseq;
+
+  bestpseq = pattern_seqs;
+  pattern_seqs = bestpseq->next_pattern_seq;
+
+  for (mseq = bestpseq->matching_seqs; mseq; mseq = mseq->next_matching_seq)
+    if (mseq->cost > seq_call_cost)
+      erase_from_pattern_seqs (mseq->insn, mseq->abstracted_length);
+  erase_from_pattern_seqs (bestpseq->insn, bestpseq->abstracted_length);
+
+  bestpseq->next_pattern_seq = pattern_seqs;
+  pattern_seqs = bestpseq;
+}
+
+/* Groups together those matching sequences of the best pattern sequence, which
+   have the same ABSTRACTED_LENGTH and puts these groups in ascending order.
+   SEQ_BLOCKS contains the result.  */
+
+static void
+determine_seq_blocks (void)
+{
+  seq_block sb;
+  matching_seq *mseq;
+  matching_seq m;
+
+  /* Initialize SEQ_BLOCKS to empty.  */
+  seq_blocks = 0;
+
+  /* Process all matching sequences.  */
+  for (mseq = &pattern_seqs->matching_seqs; *mseq;)
+    {
+      /* Deal only with matching sequences being long enough. */
+      if ((*mseq)->cost <= seq_call_cost)
+        {
+          mseq = &(*mseq)->next_matching_seq;
+          continue;
+        }
+
+      /* Ensure that SB contains a seq_block with the appropriate length.
+         Insert a new seq_block if necessary.  */
+      if (!seq_blocks || ((*mseq)->abstracted_length < seq_blocks->length))
+        {
+          sb = (seq_block) xmalloc (sizeof (struct seq_block_def));
+          sb->length = (*mseq)->abstracted_length;
+          sb->label = NULL_RTX;
+          sb->matching_seqs = 0;
+          sb->next_seq_block = seq_blocks;
+          seq_blocks = sb;
+        }
+      else
+        {
+          for (sb = seq_blocks; sb; sb = sb->next_seq_block)
+            {
+              if ((*mseq)->abstracted_length == sb->length)
+                break;
+              if (!sb->next_seq_block
+                  || ((*mseq)->abstracted_length <
+                      sb->next_seq_block->length))
+                {
+                  seq_block temp =
+                    (seq_block) xmalloc (sizeof (struct seq_block_def));
+                  temp->length = (*mseq)->abstracted_length;
+                  temp->label = NULL_RTX;
+                  temp->matching_seqs = 0;
+                  temp->next_seq_block = sb->next_seq_block;
+                  sb->next_seq_block = temp;
+                }
+            }
+        }
+
+      /* Remove the matching sequence from the linked list of the pattern
+         sequence and link it to SB.  */
+      m = *mseq;
+      *mseq = m->next_matching_seq;
+      m->next_matching_seq = sb->matching_seqs;
+      sb->matching_seqs = m;
+    }
+}
+
+/* Builds a symbol_ref for LABEL.  */
+
+static rtx
+gen_symbol_ref_rtx_for_label (const_rtx label)
+{
+  char name[20];
+  rtx sym;
+
+  ASM_GENERATE_INTERNAL_LABEL (name, "L", CODE_LABEL_NUMBER (label));
+  sym = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
+  SYMBOL_REF_FLAGS (sym) = SYMBOL_FLAG_LOCAL;
+  return sym;
+}
+
+/* Splits basic block at the requested insn and rebuilds dataflow.  */
+
+static basic_block
+split_block_and_df_analyze (basic_block bb, rtx insn)
+{
+  basic_block next;
+  next = split_block (bb, insn)->dest;
+  df_analyze ();
+  return next;
+}
+
+/* Ensures that INSN is the last insn in its block and returns the block label
+   of the next block.  */
+
+static rtx
+block_label_after (rtx insn)
+{
+  basic_block bb = BLOCK_FOR_INSN (insn);
+  if ((insn == BB_END (bb)) && (bb->next_bb != EXIT_BLOCK_PTR))
+    return block_label (bb->next_bb);
+  else
+    return block_label (split_block_and_df_analyze (bb, insn));
+}
+
+/* Ensures that the last insns of the best pattern and its matching sequences
+   are the last insns in their block. Additionally, extends the live set at the
+   end of the pattern sequence with the live sets at the end of the matching
+   sequences.  */
+
+static void
+split_blocks_after_seqs (void)
+{
+  seq_block sb;
+  matching_seq mseq;
+
+  block_label_after (pattern_seqs->insn);
+  for (sb = seq_blocks; sb; sb = sb->next_seq_block)
+    {
+      for (mseq = sb->matching_seqs; mseq; mseq = mseq->next_matching_seq)
+        {
+          block_label_after (mseq->insn);
+          IOR_REG_SET (df_get_live_out (BLOCK_FOR_INSN (pattern_seqs->insn)),
+                       df_get_live_out (BLOCK_FOR_INSN (mseq->insn)));
+        }
+    }
+}
+
+/* Splits the best pattern sequence according to SEQ_BLOCKS. Emits pseudo-call
+   and -return insns before and after the sequence.  */
+
+static void
+split_pattern_seq (void)
+{
+  rtx insn;
+  basic_block bb;
+  rtx retlabel, retjmp, saveinsn;
+  int i;
+  seq_block sb;
+
+  insn = pattern_seqs->insn;
+  bb = BLOCK_FOR_INSN (insn);
+
+  /* Get the label after the sequence. This will be the return address. The
+     label will be referenced using a symbol_ref so protect it from
+     deleting.  */
+  retlabel = block_label_after (insn);
+  LABEL_PRESERVE_P (retlabel) = 1;
+
+  /* Emit an indirect jump via the link register after the sequence acting
+     as the return insn.  Also emit a barrier and update the basic block.  */
+  if (!find_reg_note (BB_END (bb), REG_NORETURN, NULL))
+    retjmp = emit_jump_insn_after (gen_indirect_jump (pattern_seqs->link_reg),
+                                   BB_END (bb));
+  emit_barrier_after (BB_END (bb));
+
+  /* Replace all outgoing edges with a new one to the block of RETLABEL.  */
+  while (EDGE_COUNT (bb->succs) != 0)
+    remove_edge (EDGE_SUCC (bb, 0));
+  make_edge (bb, BLOCK_FOR_INSN (retlabel), EDGE_ABNORMAL);
+
+  /* Split the sequence according to SEQ_BLOCKS and cache the label of the
+     resulting basic blocks.  */
+  i = 0;
+  for (sb = seq_blocks; sb; sb = sb->next_seq_block)
+    {
+      for (; i < sb->length; i++)
+        insn = prev_insn_in_block (insn);
+
+      sb->label = block_label (split_block_and_df_analyze (bb, insn));
+    }
+
+  /* Emit an insn saving the return address to the link register before the
+     sequence.  */
+  saveinsn = emit_insn_after (gen_move_insn (pattern_seqs->link_reg,
+                              gen_symbol_ref_rtx_for_label
+                              (retlabel)), BB_END (bb));
+  /* Update liveness info.  */
+  SET_REGNO_REG_SET (df_get_live_out (bb),
+                     REGNO (pattern_seqs->link_reg));
+}
+
+/* Deletes the insns of the matching sequences of the best pattern sequence and
+   replaces them with pseudo-calls to the pattern sequence.  */
+
+static void
+erase_matching_seqs (void)
+{
+  seq_block sb;
+  matching_seq mseq;
+  rtx insn;
+  basic_block bb;
+  rtx retlabel, saveinsn, callinsn;
+  int i;
+
+  for (sb = seq_blocks; sb; sb = sb->next_seq_block)
+    {
+      for (mseq = sb->matching_seqs; mseq; mseq = mseq->next_matching_seq)
+        {
+          insn = mseq->insn;
+          bb = BLOCK_FOR_INSN (insn);
+
+          /* Get the label after the sequence. This will be the return
+             address. The label will be referenced using a symbol_ref so
+             protect it from deleting.  */
+          retlabel = block_label_after (insn);
+          LABEL_PRESERVE_P (retlabel) = 1;
+
+          /* Delete the insns of the sequence.  */
+          for (i = 0; i < sb->length; i++)
+            insn = prev_insn_in_block (insn);
+          delete_basic_block (split_block_and_df_analyze (bb, insn));
+
+          /* Emit an insn saving the return address to the link register
+             before the deleted sequence.  */
+          saveinsn = emit_insn_after (gen_move_insn (pattern_seqs->link_reg,
+                                      gen_symbol_ref_rtx_for_label
+                                      (retlabel)),
+                                      BB_END (bb));
+          BLOCK_FOR_INSN (saveinsn) = bb;
+
+          /* Emit a jump to the appropriate part of the pattern sequence
+             after the save insn. Also update the basic block.  */
+          callinsn = emit_jump_insn_after (gen_jump (sb->label), saveinsn);
+          JUMP_LABEL (callinsn) = sb->label;
+          LABEL_NUSES (sb->label)++;
+          BLOCK_FOR_INSN (callinsn) = bb;
+          BB_END (bb) = callinsn;
+
+          /* Maintain control flow and liveness information.  */
+          SET_REGNO_REG_SET (df_get_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 (df_get_live_out (bb),
+                      df_get_live_in (BLOCK_FOR_INSN (sb->label)));
+
+          make_edge (BLOCK_FOR_INSN (seq_blocks->label),
+                     BLOCK_FOR_INSN (retlabel), EDGE_ABNORMAL);
+        }
+    }
+}
+
+/* Deallocates SEQ_BLOCKS and all the matching sequences.  */
+
+static void
+free_seq_blocks (void)
+{
+  while (seq_blocks)
+    {
+      seq_block sb = seq_blocks;
+      while (sb->matching_seqs)
+        {
+          matching_seq mseq = sb->matching_seqs;
+          sb->matching_seqs = mseq->next_matching_seq;
+          free (mseq);
+        }
+      seq_blocks = sb->next_seq_block;
+      free (sb);
+    }
+}
+
+/* Transforms the best pattern sequence into a pseudo-function and its matching
+   sequences to pseudo-calls. Afterwards the best pattern sequence is removed
+   from PATTERN_SEQS.  */
+
+static void
+abstract_best_seq (void)
+{
+  pattern_seq bestpseq;
+
+  /* Do the abstraction.  */
+  determine_seq_blocks ();
+  split_blocks_after_seqs ();
+  split_pattern_seq ();
+  erase_matching_seqs ();
+  free_seq_blocks ();
+
+  /* Record the usage of the link register.  */
+  df_set_regs_ever_live (REGNO (pattern_seqs->link_reg), true);
+
+  /* Remove the best pattern sequence.  */
+  bestpseq = pattern_seqs;
+  pattern_seqs = bestpseq->next_pattern_seq;
+  free_pattern_seq (bestpseq);
+}
+
+/* Prints info on the pattern sequences to the dump file.  */
+
+static void
+dump_pattern_seqs (void)
+{
+  pattern_seq pseq;
+  matching_seq mseq;
+
+  if (!dump_file)
+    return;
+
+  fprintf (dump_file, ";; Pattern sequences\n");
+  for (pseq = pattern_seqs; pseq; pseq = pseq->next_pattern_seq)
+    {
+      fprintf (dump_file, "Pattern sequence at insn %d matches sequences at",
+               INSN_UID (pseq->insn));
+      for (mseq = pseq->matching_seqs; mseq; mseq = mseq->next_matching_seq)
+        {
+          fprintf (dump_file, " insn %d (length %d)", INSN_UID (mseq->insn),
+                   mseq->matching_length);
+          if (mseq->next_matching_seq)
+            fprintf (dump_file, ",");
+        }
+      fprintf (dump_file, ".\n");
+    }
+  fprintf (dump_file, "\n");
+}
+
+/* Prints info on the best pattern sequence transformed in the ITER-th
+   iteration to the dump file.  */
+
+static void
+dump_best_pattern_seq (int iter)
+{
+  matching_seq mseq;
+
+  if (!dump_file)
+    return;
+
+  fprintf (dump_file, ";; Iteration %d\n", iter);
+  fprintf (dump_file,
+           "Best pattern sequence with %d gain is at insn %d (length %d).\n",
+           pattern_seqs->gain, INSN_UID (pattern_seqs->insn),
+           pattern_seqs->abstracted_length);
+  fprintf (dump_file, "Matching sequences are at");
+  for (mseq = pattern_seqs->matching_seqs; mseq;
+       mseq = mseq->next_matching_seq)
+    {
+      fprintf (dump_file, " insn %d (length %d)", INSN_UID (mseq->insn),
+               mseq->abstracted_length);
+      if (mseq->next_matching_seq)
+        fprintf (dump_file, ",");
+    }
+  fprintf (dump_file, ".\n");
+  fprintf (dump_file, "Using reg %d as link register.\n\n",
+           REGNO (pattern_seqs->link_reg));
+}
+
+/* Htab hash function for hash_bucket_def structure.  */
+
+static unsigned int
+htab_hash_bucket (const void *p)
+{
+  const_p_hash_bucket bucket = (const_p_hash_bucket) p;
+  return bucket->hash;
+}
+
+/* Htab equal function for hash_bucket_def structure.  */
+
+static int
+htab_eq_bucket (const void *p0, const void *p1)
+{
+  return htab_hash_bucket (p0) == htab_hash_bucket (p1);
+}
+
+/* Htab delete function for hash_bucket_def structure.  */
+
+static void
+htab_del_bucket (void *p)
+{
+  p_hash_bucket bucket = (p_hash_bucket) p;
+
+  if (bucket->seq_candidates)
+    htab_delete (bucket->seq_candidates);
+
+  free (bucket);
+}
+
+/* Htab hash function for hash_bucket_def structure.  */
+
+static unsigned int
+htab_hash_elem (const void *p)
+{
+  const_p_hash_elem elem = (const_p_hash_elem) p;
+  return htab_hash_pointer (elem->insn);
+}
+
+/* Htab equal function for hash_bucket_def structure.  */
+
+static int
+htab_eq_elem (const void *p0, const void *p1)
+{
+  return htab_hash_elem (p0) == htab_hash_elem (p1);
+}
+
+/* Htab delete function for hash_bucket_def structure.  */
+
+static void
+htab_del_elem (void *p)
+{
+  p_hash_elem elem = (p_hash_elem) p;
+  free (elem);
+}
+
+/* Creates a hash value for each sequence candidate and saves them
+   in HASH_BUCKET.  */
+
+static void
+fill_hash_bucket (void)
+{
+  basic_block bb;
+  rtx insn;
+  void **slot;
+  p_hash_bucket bucket;
+  struct hash_bucket_def tmp_bucket;
+  p_hash_elem elem;
+  unsigned long insn_idx;
+
+  insn_idx = 0;
+  FOR_EACH_BB (bb)
+    {
+      FOR_BB_INSNS_REVERSE (bb, insn)
+        {
+          if (!ABSTRACTABLE_INSN_P (insn))
+            continue;
+
+          /* Compute hash value for INSN.  */
+          tmp_bucket.hash = compute_hash (insn);
+
+          /* Select the hash group.  */
+          bucket = (p_hash_bucket) htab_find (hash_buckets, &tmp_bucket);
+
+          if (!bucket)
+            {
+              /* Create a new hash group.  */
+              bucket = (p_hash_bucket) xcalloc (1,
+                                        sizeof (struct hash_bucket_def));
+              bucket->hash = tmp_bucket.hash;
+              bucket->seq_candidates = NULL;
+
+              slot = htab_find_slot (hash_buckets, &tmp_bucket, INSERT);
+              *slot = bucket;
+            }
+
+          /* Create new list for storing sequence candidates.  */
+          if (!bucket->seq_candidates)
+              bucket->seq_candidates = htab_create (HASH_INIT,
+                                                    htab_hash_elem,
+                                                    htab_eq_elem,
+                                                    htab_del_elem);
+
+          elem = (p_hash_elem) xcalloc (1, sizeof (struct hash_elem_def));
+          elem->insn = insn;
+          elem->idx = insn_idx;
+          elem->length = get_attr_length (insn);
+
+          /* Insert INSN into BUCKET hash bucket.  */
+          slot = htab_find_slot (bucket->seq_candidates, elem, INSERT);
+          *slot = elem;
+
+          insn_idx++;
+        }
+    }
+}
+
+/* Computes the cost of calling sequence and the cost of return.  */
+
+static void
+compute_init_costs (void)
+{
+  rtx rtx_jump, rtx_store, rtx_return, reg, label;
+  basic_block bb;
+
+  FOR_EACH_BB (bb)
+    if (BB_HEAD (bb))
+      break;
+
+  label = block_label (bb);
+  reg = gen_rtx_REG (Pmode, 0);
+
+  /* Pattern for indirect jump.  */
+  rtx_jump = gen_indirect_jump (reg);
+
+  /* Pattern for storing address.  */
+  rtx_store = gen_rtx_SET (VOIDmode, reg, gen_symbol_ref_rtx_for_label (label));
+
+  /* Pattern for return insn.  */
+  rtx_return = gen_jump (label);
+
+  /* The cost of jump.  */
+  seq_jump_cost = compute_rtx_cost (make_jump_insn_raw (rtx_jump));
+
+  /* The cost of calling sequence.  */
+  seq_call_cost = seq_jump_cost + compute_rtx_cost (make_insn_raw (rtx_store));
+
+  /* The cost of return.  */
+  seq_return_cost = compute_rtx_cost (make_jump_insn_raw (rtx_return));
+
+  /* Simple heuristic for minimal sequence cost.  */
+  seq_call_cost   = (int)(seq_call_cost * (double)SEQ_CALL_COST_MULTIPLIER);
+}
+
+/* Finds equivalent insn sequences in the current function and retains only one
+   instance of them which is turned into a pseudo-function. The additional
+   copies are erased and replaced by pseudo-calls to the retained sequence.  */
+
+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 ,
+                              htab_del_bucket);
+  fill_hash_bucket ();
+
+  /* Compute the common cost of abstraction.  */
+  compute_init_costs ();
+
+  /* Build an initial set of pattern sequences from the current function.  */
+  collect_pattern_seqs ();
+  dump_pattern_seqs ();
+
+  /* Iterate until there are no sequences to abstract.  */
+  for (iter = 1;; iter++)
+    {
+      /* Recompute gain for sequences if necessary and select sequence with
+         biggest gain.  */
+      recompute_gain ();
+      if (!pattern_seqs)
+        break;
+      dump_best_pattern_seq (iter);
+      /* Update the cached info of the other sequences and force gain
+         recomputation where needed.  */
+      update_pattern_seqs ();
+      /* Turn best sequences into pseudo-functions and -calls.  */
+      abstract_best_seq ();
+    }
+
+  /* Cleanup hash tables.  */
+  htab_delete (hash_buckets);
+}
+
+/* The gate function for TREE_OPT_PASS.  */
+
+static bool
+gate_rtl_seqabstr (void)
+{
+  return flag_rtl_seqabstr;
+}
+
+/* The entry point of the sequence abstraction algorithm.  */
+
+static unsigned int
+rest_of_rtl_seqabstr (void)
+{
+  /* Abstract out common insn sequences. */
+  rtl_seqabstr ();
+  return 0;
+}
+
+struct rtl_opt_pass pass_rtl_seqabstr = 
+{
+ {
+  RTL_PASS,
+  "seqabstr",                           /* name */
+  gate_rtl_seqabstr,                    /* gate */
+  rest_of_rtl_seqabstr,                 /* execute */
+  NULL,                                 /* sub */
+  NULL,                                 /* next */
+  0,                                    /* static_pass_number */
+  TV_SEQABSTR,                          /* tv_id */
+  0,                                    /* properties_required */
+  0,                                    /* properties_provided */
+  0,                                    /* properties_destroyed */
+  0,                                    /* todo_flags_start */
+  TODO_df_finish | TODO_verify_rtl_sharing |
+  TODO_dump_func |
+  TODO_ggc_collect                      /* todo_flags_finish */
+ }
+};