From: Richard Earnshaw <[email protected]>

The minipool code is pretty-much stand-alone, with very few (if any) direct
calls back into the main arm backend (beyond standard dependencies).  Move
it into a separate compilation unit and put it in its own namespace.

gcc/ChangeLog:

        * config/arm/minipool.cc: New file.
        * config/arm/minipool.h: New file.
        * config/arm/t-arm: Add rules to build minipool.o.
        * config/arm/arm.cc (get_jump_table_size): Move to minipool.cc.
        (arm_print_value): Rename to print_value and move to minipool.cc.
        (arm_barrier_cost): Rename to barrier_cost and move to minipool.cc.
        (arm_add_gc_roots): Call minipools::add_gc_roots.
        (create_fix_barrier): Move to minipool.cc.
        (push_minipool_barrier): Likewise.
        (push_minipool_fix): Likewise.
        (minipool_obstack): Likewise.
        (minipool_startobj): Likewise.
        (struct minipool_node): Likewise.
        (struct minipool_fixup): Likewise.
        (MINIPOOL_FIX_SIZE): Likewise.
        (class minipool): Likewise.
        (JUMP_TABLES_IN_TEXT_SECTION): Likewise.
        (get_label_padding): Likewise.
        (minipool::move_fix_forward_ref): Likewise.
        (minipool::add_forward_ref): Likewise.
        (minipool::move_fix_backward_ref): Likewise.
        (minipool::add_backward_ref): Likewise.
        (minipool::assign_offsets): Likewise.
        (minipool::dump): Likewise.
        (note_invalid_constants): Likewise.
        (place_minipools): Likewise.
        (arm_reorg): Call minipools::place_minipools.
---
 gcc/config/arm/arm.cc      | 1206 +---------------------------------
 gcc/config/arm/minipool.cc | 1242 ++++++++++++++++++++++++++++++++++++
 gcc/config/arm/minipool.h  |   23 +
 gcc/config/arm/t-arm       |   27 +-
 4 files changed, 1288 insertions(+), 1210 deletions(-)
 create mode 100644 gcc/config/arm/minipool.cc
 create mode 100644 gcc/config/arm/minipool.h

diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc
index 6e6f5d0b34db0..940c689b21e0e 100644
--- a/gcc/config/arm/arm.cc
+++ b/gcc/config/arm/arm.cc
@@ -77,14 +77,11 @@
 #include "aarch-common-protos.h"
 #include "machmode.h"
 #include "arm-builtins.h"
+#include "minipool.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
 
-/* Forward definitions of types.  */
-typedef struct minipool_node    Mnode;
-typedef struct minipool_fixup   Mfix;
-
 void (*arm_lang_output_object_attributes_hook)(void);
 
 struct four_ints
@@ -126,15 +123,7 @@ static const char *output_multi_immediate (rtx *, const 
char *, const char *,
 static const char *shift_op (rtx, HOST_WIDE_INT *);
 static struct machine_function *arm_init_machine_status (void);
 static void thumb_exit (FILE *, int);
-static HOST_WIDE_INT get_jump_table_size (rtx_jump_table_data *);
-static void arm_print_value (FILE *, rtx);
-static int arm_barrier_cost (rtx_insn *);
-static Mfix *create_fix_barrier (Mfix *, HOST_WIDE_INT);
-static void push_minipool_barrier (rtx_insn *, HOST_WIDE_INT);
-static void push_minipool_fix (rtx_insn *, HOST_WIDE_INT, rtx *,
-                              machine_mode, rtx);
 static void arm_reorg (void);
-static void note_invalid_constants (rtx_insn *, HOST_WIDE_INT, int);
 static unsigned long arm_compute_save_reg0_reg12_mask (void);
 static unsigned long arm_compute_save_core_reg_mask (void);
 static unsigned long arm_isr_value (tree);
@@ -840,9 +829,6 @@ static const scoped_attribute_specs *const 
arm_attribute_table[] =
 #undef TARGET_C_BITINT_TYPE_INFO
 #define TARGET_C_BITINT_TYPE_INFO arm_bitint_type_info
 
-/* Obstack for minipool constant handling.  */
-static struct obstack minipool_obstack;
-static char *         minipool_startobj;
 
 /* The maximum number of insns skipped which
    will be conditionalised if possible.  */
@@ -4087,8 +4073,7 @@ arm_fdpic_local_funcdesc_p (rtx fnx)
 static void
 arm_add_gc_roots (void)
 {
-  gcc_obstack_init(&minipool_obstack);
-  minipool_startobj = (char *) obstack_alloc (&minipool_obstack, 0);
+  minipools::add_gc_roots ();
 }
 
 /* A table of known ARM exception types.
@@ -17594,255 +17579,6 @@ valid_operands_ldrd_strd (rtx *operands, bool load)
 }
 
 
-/* Print a symbolic form of X to the debug file, F.  */
-static void
-arm_print_value (FILE *f, rtx x)
-{
-  switch (GET_CODE (x))
-    {
-    case CONST_INT:
-      fprintf (f, HOST_WIDE_INT_PRINT_HEX, INTVAL (x));
-      return;
-
-    case CONST_DOUBLE:
-      {
-       char fpstr[20];
-       real_to_decimal (fpstr, CONST_DOUBLE_REAL_VALUE (x),
-                        sizeof (fpstr), 0, 1);
-       fputs (fpstr, f);
-      }
-      return;
-
-    case CONST_VECTOR:
-      {
-       int i;
-
-       fprintf (f, "<");
-       for (i = 0; i < CONST_VECTOR_NUNITS (x); i++)
-         {
-           fprintf (f, HOST_WIDE_INT_PRINT_HEX, INTVAL (CONST_VECTOR_ELT (x, 
i)));
-           if (i < (CONST_VECTOR_NUNITS (x) - 1))
-             fputc (',', f);
-         }
-       fprintf (f, ">");
-      }
-      return;
-
-    case CONST_STRING:
-      fprintf (f, "\"%s\"", XSTR (x, 0));
-      return;
-
-    case SYMBOL_REF:
-      fprintf (f, "`%s'", XSTR (x, 0));
-      return;
-
-    case LABEL_REF:
-      fprintf (f, "L%d", INSN_UID (XEXP (x, 0)));
-      return;
-
-    case CONST:
-      arm_print_value (f, XEXP (x, 0));
-      return;
-
-    case PLUS:
-      arm_print_value (f, XEXP (x, 0));
-      fprintf (f, "+");
-      arm_print_value (f, XEXP (x, 1));
-      return;
-
-    case PC:
-      fprintf (f, "pc");
-      return;
-
-    default:
-      fprintf (f, "????");
-      return;
-    }
-}
-
-/* Routines for manipulation of the constant pool.  */
-
-/* Arm instructions cannot load a large constant directly into a
-   register; they have to come from a pc relative load.  The constant
-   must therefore be placed in the addressable range of the pc
-   relative load.  Depending on the precise pc relative load
-   instruction the range is somewhere between 256 bytes and 4k.  This
-   means that we often have to dump a constant inside a function, and
-   generate code to branch around it.
-
-   It is important to minimize this, since the branches will slow
-   things down and make the code larger.
-
-   Normally we can hide the table after an existing unconditional
-   branch so that there is no interruption of the flow, but in the
-   worst case the code looks like this:
-
-       ldr     rn, L1
-       ...
-       b       L2
-       align
-       L1:     .long value
-       L2:
-       ...
-
-       ldr     rn, L3
-       ...
-       b       L4
-       align
-       L3:     .long value
-       L4:
-       ...
-
-   We fix this by performing a scan after scheduling, which notices
-   which instructions need to have their operands fetched from the
-   constant table and builds the table.
-
-   The algorithm starts by building a table of all the constants that
-   need fixing up and all the natural barriers in the function (places
-   where a constant table can be dropped without breaking the flow).
-   For each fixup we note how far the pc-relative replacement will be
-   able to reach and the offset of the instruction into the function.
-
-   Having built the table we then group the fixes together to form
-   tables that are as large as possible (subject to addressing
-   constraints) and emit each table of constants after the last
-   barrier that is within range of all the instructions in the group.
-   If a group does not contain a barrier, then we forcibly create one
-   by inserting a jump instruction into the flow.  Once the table has
-   been inserted, the insns are then modified to reference the
-   relevant entry in the pool.
-
-   Possible enhancements to the algorithm (not implemented) are:
-
-   1) For some processors and object formats, there may be benefit in
-   aligning the pools to the start of cache lines; this alignment
-   would need to be taken into account when calculating addressability
-   of a pool.  */
-
-/* These typedefs are located at the start of this file, so that
-   they can be used in the prototypes there.  This comment is to
-   remind readers of that fact so that the following structures
-   can be understood more easily.
-
-     typedef struct minipool_node    Mnode;
-     typedef struct minipool_fixup   Mfix;  */
-
-struct minipool_node
-{
-  /* Doubly linked chain of entries.  */
-  Mnode * next;
-  Mnode * prev;
-  /* The maximum offset into the code that this entry can be placed.  While
-     pushing fixes for forward references, all entries are sorted in order
-     of increasing max_address.  */
-  HOST_WIDE_INT max_address;
-  /* Similarly for an entry inserted for a backwards ref.  */
-  HOST_WIDE_INT min_address;
-  /* The number of fixes referencing this entry.  This can become zero
-     if we "unpush" an entry.  In this case we ignore the entry when we
-     come to emit the code.  */
-  int refcount;
-  /* The offset from the start of the minipool.  */
-  HOST_WIDE_INT offset;
-  /* The value in table.  */
-  rtx value;
-  /* The mode of value.  */
-  machine_mode mode;
-  /* The size of the value.  */
-  int fix_size;
-};
-
-struct minipool_fixup
-{
-  Mfix *            next;
-  rtx_insn *        insn;
-  HOST_WIDE_INT     address;
-  rtx *             loc;
-  machine_mode mode;
-  int               fix_size;
-  rtx               value;
-  Mnode *           minipool;
-  HOST_WIDE_INT     forwards;
-  HOST_WIDE_INT     backwards;
-};
-
-/* Fixes less than a word need padding out to a word boundary.  */
-#define MINIPOOL_FIX_SIZE(mode) \
-  (GET_MODE_SIZE ((mode)) >= 4 ? GET_MODE_SIZE ((mode)) : 4)
-
-class minipool
-{
- public:
-  minipool () { reset (); }
-  void reset()
-    {
-      vector_head = NULL;
-      vector_tail = NULL;
-      vector_label = NULL;
-    }
-  Mnode *head () { return vector_head; }
-  rtx_code_label *label () { return vector_label; }
-  Mnode *add_forward_ref (Mfix *);
-  Mnode *add_backward_ref (Mfix *);
-  void assign_offsets (Mfix *);
-  void dump (rtx_insn *);
- private:
-  Mnode *move_fix_forward_ref (Mnode *, Mnode *, HOST_WIDE_INT);
-  Mnode *move_fix_backward_ref (Mnode *, Mnode *, HOST_WIDE_INT);
-  Mnode *vector_head;
-  Mnode *vector_tail;
-  rtx_code_label *vector_label;
-};
-
-static int minipool_pad;
-
-/* The linked list of all minipool fixes required for this function.  */
-Mfix *minipool_fix_head;
-Mfix *minipool_fix_tail;
-/* The fix entry for the current minipool, once it has been placed.  */
-Mfix *minipool_barrier;
-
-#ifndef JUMP_TABLES_IN_TEXT_SECTION
-#define JUMP_TABLES_IN_TEXT_SECTION 0
-#endif
-
-static HOST_WIDE_INT
-get_jump_table_size (rtx_jump_table_data *insn)
-{
-  /* ADDR_VECs only take room if read-only data does into the text
-     section.  */
-  if (JUMP_TABLES_IN_TEXT_SECTION || readonly_data_section == text_section)
-    {
-      rtx body = PATTERN (insn);
-      int elt = GET_CODE (body) == ADDR_DIFF_VEC ? 1 : 0;
-      HOST_WIDE_INT size;
-      HOST_WIDE_INT modesize;
-
-      modesize = GET_MODE_SIZE (GET_MODE (body));
-      size = modesize * XVECLEN (body, elt);
-      switch (modesize)
-       {
-       case 1:
-         /* Round up size  of TBB table to a halfword boundary.  */
-         size = (size + 1) & ~HOST_WIDE_INT_1;
-         break;
-       case 2:
-         /* No padding necessary for TBH.  */
-         break;
-       case 4:
-         /* Add two bytes for alignment on Thumb.  */
-         if (TARGET_THUMB)
-           size += 2;
-         break;
-       default:
-         gcc_unreachable ();
-       }
-      return size;
-    }
-
-  return 0;
-}
-
 /* Emit insns to load the function address from FUNCDESC (an FDPIC
    function descriptor) into a register and the GOT address into the
    FDPIC register, returning an rtx for the register holding the
@@ -17867,702 +17603,6 @@ arm_load_function_descriptor (rtx funcdesc)
   return fnaddr_reg;
 }
 
-/* Return the maximum amount of padding that will be inserted before
-   label LABEL.  */
-static HOST_WIDE_INT
-get_label_padding (rtx label)
-{
-  HOST_WIDE_INT align, min_insn_size;
-
-  align = 1 << label_to_alignment (label).levels[0].log;
-  min_insn_size = TARGET_THUMB ? 2 : 4;
-  return align > min_insn_size ? align - min_insn_size : 0;
-}
-
-/* Move a minipool fix MP from its current location to before MAX_MP.
-   If MAX_MP is NULL, then MP doesn't need moving, but the addressing
-   constraints may need updating.  */
-Mnode *
-minipool::move_fix_forward_ref (Mnode *mp, Mnode *max_mp,
-                               HOST_WIDE_INT max_address)
-{
-  /* The code below assumes these are different.  */
-  gcc_assert (mp != max_mp);
-
-  if (max_mp == NULL)
-    {
-      if (max_address < mp->max_address)
-       mp->max_address = max_address;
-    }
-  else
-    {
-      if (max_address > max_mp->max_address - mp->fix_size)
-       mp->max_address = max_mp->max_address - mp->fix_size;
-      else
-       mp->max_address = max_address;
-
-      /* Unlink MP from its current position.  Since max_mp is non-null,
-       mp->prev must be non-null.  */
-      mp->prev->next = mp->next;
-      if (mp->next != NULL)
-       mp->next->prev = mp->prev;
-      else
-       vector_tail = mp->prev;
-
-      /* Re-insert it before MAX_MP.  */
-      mp->next = max_mp;
-      mp->prev = max_mp->prev;
-      max_mp->prev = mp;
-
-      if (mp->prev != NULL)
-       mp->prev->next = mp;
-      else
-       vector_head = mp;
-    }
-
-  /* Save the new entry.  */
-  max_mp = mp;
-
-  /* Scan over the preceding entries and adjust their addresses as
-     required.  */
-  while (mp->prev != NULL
-        && mp->prev->max_address > mp->max_address - mp->prev->fix_size)
-    {
-      mp->prev->max_address = mp->max_address - mp->prev->fix_size;
-      mp = mp->prev;
-    }
-
-  return max_mp;
-}
-
-/* Add a constant to the minipool for a forward reference.  Returns the
-   node added or NULL if the constant will not fit in this pool.  */
-Mnode *
-minipool::add_forward_ref (Mfix *fix)
-{
-  /* If set, max_mp is the first pool_entry that has a lower
-     constraint than the one we are trying to add.  */
-  Mnode *       max_mp = NULL;
-  HOST_WIDE_INT max_address = fix->address + fix->forwards - minipool_pad;
-  Mnode *       mp;
-
-  /* If the minipool starts before the end of FIX->INSN then this FIX
-     cannot be placed into the current pool.  Furthermore, adding the
-     new constant pool entry may cause the pool to start FIX_SIZE bytes
-     earlier.  */
-  if (vector_head &&
-      (fix->address + get_attr_length (fix->insn)
-       >= vector_head->max_address - fix->fix_size))
-    return NULL;
-
-  /* Scan the pool to see if a constant with the same value has
-     already been added.  While we are doing this, also note the
-     location where we must insert the constant if it doesn't already
-     exist.  */
-  for (mp = vector_head; mp != NULL; mp = mp->next)
-    {
-      if (GET_CODE (fix->value) == GET_CODE (mp->value)
-         && fix->mode == mp->mode
-         && (!LABEL_P (fix->value)
-             || (CODE_LABEL_NUMBER (fix->value)
-                 == CODE_LABEL_NUMBER (mp->value)))
-         && rtx_equal_p (fix->value, mp->value))
-       {
-         /* More than one fix references this entry.  */
-         mp->refcount++;
-         return move_fix_forward_ref (mp, max_mp, max_address);
-       }
-
-      /* Note the insertion point if necessary.  */
-      if (max_mp == NULL
-         && mp->max_address > max_address)
-       max_mp = mp;
-
-      /* If we are inserting an 8-bytes aligned quantity and
-        we have not already found an insertion point, then
-        make sure that all such 8-byte aligned quantities are
-        placed at the start of the pool.  */
-      if (ARM_DOUBLEWORD_ALIGN
-         && max_mp == NULL
-         && fix->fix_size >= 8
-         && mp->fix_size < 8)
-       {
-         max_mp = mp;
-         max_address = mp->max_address;
-       }
-    }
-
-  /* The value is not currently in the minipool, so we need to create
-     a new entry for it.  If MAX_MP is NULL, the entry will be put on
-     the end of the list since the placement is less constrained than
-     any existing entry.  Otherwise, we insert the new fix before
-     MAX_MP and, if necessary, adjust the constraints on the other
-     entries.  */
-  mp = XNEW (Mnode);
-  mp->fix_size = fix->fix_size;
-  mp->mode = fix->mode;
-  mp->value = fix->value;
-  mp->refcount = 1;
-  /* Not yet required for a backwards ref.  */
-  mp->min_address = -65536;
-
-  if (max_mp == NULL)
-    {
-      mp->max_address = max_address;
-      mp->next = NULL;
-      mp->prev = vector_tail;
-
-      if (mp->prev == NULL)
-       {
-         vector_head = mp;
-         vector_label = gen_label_rtx ();
-       }
-      else
-       mp->prev->next = mp;
-
-      vector_tail = mp;
-    }
-  else
-    {
-      if (max_address > max_mp->max_address - mp->fix_size)
-       mp->max_address = max_mp->max_address - mp->fix_size;
-      else
-       mp->max_address = max_address;
-
-      mp->next = max_mp;
-      mp->prev = max_mp->prev;
-      max_mp->prev = mp;
-      if (mp->prev != NULL)
-       mp->prev->next = mp;
-      else
-       vector_head = mp;
-    }
-
-  /* Save the new entry.  */
-  max_mp = mp;
-
-  /* Scan over the preceding entries and adjust their addresses as
-     required.  */
-  while (mp->prev != NULL
-        && mp->prev->max_address > mp->max_address - mp->prev->fix_size)
-    {
-      mp->prev->max_address = mp->max_address - mp->prev->fix_size;
-      mp = mp->prev;
-    }
-
-  return max_mp;
-}
-
-Mnode *
-minipool::move_fix_backward_ref (Mnode *mp, Mnode *min_mp,
-                                HOST_WIDE_INT  min_address)
-{
-  HOST_WIDE_INT offset;
-
-  /* The code below assumes these are different.  */
-  gcc_assert (mp != min_mp);
-
-  if (min_mp == NULL)
-    {
-      if (min_address > mp->min_address)
-       mp->min_address = min_address;
-    }
-  else
-    {
-      /* We will adjust this below if it is too loose.  */
-      mp->min_address = min_address;
-
-      /* Unlink MP from its current position.  Since min_mp is non-null,
-        mp->next must be non-null.  */
-      mp->next->prev = mp->prev;
-      if (mp->prev != NULL)
-       mp->prev->next = mp->next;
-      else
-       vector_head = mp->next;
-
-      /* Reinsert it after MIN_MP.  */
-      mp->prev = min_mp;
-      mp->next = min_mp->next;
-      min_mp->next = mp;
-      if (mp->next != NULL)
-       mp->next->prev = mp;
-      else
-       vector_tail = mp;
-    }
-
-  min_mp = mp;
-
-  offset = 0;
-  for (mp = vector_head; mp != NULL; mp = mp->next)
-    {
-      mp->offset = offset;
-      if (mp->refcount > 0)
-       offset += mp->fix_size;
-
-      if (mp->next && mp->next->min_address < mp->min_address + mp->fix_size)
-       mp->next->min_address = mp->min_address + mp->fix_size;
-    }
-
-  return min_mp;
-}
-
-/* Add a constant to the minipool for a backward reference.  Returns the
-   node added or NULL if the constant will not fit in this pool.
-
-   Note that the code for insertion for a backwards reference can be
-   somewhat confusing because the calculated offsets for each fix do
-   not take into account the size of the pool (which is still under
-   construction.  */
-Mnode *
-minipool::add_backward_ref (Mfix *fix)
-{
-  /* If set, min_mp is the last pool_entry that has a lower constraint
-     than the one we are trying to add.  */
-  Mnode *min_mp = NULL;
-  /* This can be negative, since it is only a constraint.  */
-  HOST_WIDE_INT  min_address = fix->address - fix->backwards;
-  Mnode *mp;
-
-  /* If we can't reach the current pool from this insn, or if we can't
-     insert this entry at the end of the pool without pushing other
-     fixes out of range, then we don't try.  This ensures that we
-     can't fail later on.  */
-  if (min_address >= minipool_barrier->address
-      || (vector_tail->min_address + fix->fix_size
-         >= minipool_barrier->address))
-    return NULL;
-
-  /* Scan the pool to see if a constant with the same value has
-     already been added.  While we are doing this, also note the
-     location where we must insert the constant if it doesn't already
-     exist.  */
-  for (mp = vector_tail; mp != NULL; mp = mp->prev)
-    {
-      if (GET_CODE (fix->value) == GET_CODE (mp->value)
-         && fix->mode == mp->mode
-         && (!LABEL_P (fix->value)
-             || (CODE_LABEL_NUMBER (fix->value)
-                 == CODE_LABEL_NUMBER (mp->value)))
-         && rtx_equal_p (fix->value, mp->value)
-         /* Check that there is enough slack to move this entry to the
-            end of the table (this is conservative).  */
-         && (mp->max_address
-             > (minipool_barrier->address
-                + vector_tail->offset
-                + vector_tail->fix_size)))
-       {
-         mp->refcount++;
-         return move_fix_backward_ref (mp, min_mp, min_address);
-       }
-
-      if (min_mp != NULL)
-       mp->min_address += fix->fix_size;
-      else
-       {
-         /* Note the insertion point if necessary.  */
-         if (mp->min_address < min_address)
-           {
-             /* For now, we do not allow the insertion of 8-byte alignment
-                requiring nodes anywhere but at the start of the pool.  */
-             if (ARM_DOUBLEWORD_ALIGN
-                 && fix->fix_size >= 8 && mp->fix_size < 8)
-               return NULL;
-             else
-               min_mp = mp;
-           }
-         else if (mp->max_address
-                  < minipool_barrier->address + mp->offset + fix->fix_size)
-           {
-             /* Inserting before this entry would push the fix beyond
-                its maximum address (which can happen if we have
-                re-located a forwards fix); force the new fix to come
-                after it.  */
-             if (ARM_DOUBLEWORD_ALIGN
-                 && fix->fix_size >= 8 && mp->fix_size < 8)
-               return NULL;
-             else
-               {
-                 min_mp = mp;
-                 min_address = mp->min_address + fix->fix_size;
-               }
-           }
-         /* Do not insert a non-8-byte aligned quantity before 8-byte
-            aligned quantities.  */
-         else if (ARM_DOUBLEWORD_ALIGN
-                  && fix->fix_size < 8
-                  && mp->fix_size >= 8)
-           {
-             min_mp = mp;
-             min_address = mp->min_address + fix->fix_size;
-           }
-       }
-    }
-
-  /* We need to create a new entry.  */
-  mp = XNEW (Mnode);
-  mp->fix_size = fix->fix_size;
-  mp->mode = fix->mode;
-  mp->value = fix->value;
-  mp->refcount = 1;
-  mp->max_address = minipool_barrier->address + 65536;
-
-  mp->min_address = min_address;
-
-  if (min_mp == NULL)
-    {
-      mp->prev = NULL;
-      mp->next = vector_head;
-
-      if (mp->next == NULL)
-       {
-         vector_tail = mp;
-         vector_label = gen_label_rtx ();
-       }
-      else
-       mp->next->prev = mp;
-
-      vector_head = mp;
-    }
-  else
-    {
-      mp->next = min_mp->next;
-      mp->prev = min_mp;
-      min_mp->next = mp;
-
-      if (mp->next != NULL)
-       mp->next->prev = mp;
-      else
-       vector_tail = mp;
-    }
-
-  /* Save the new entry.  */
-  min_mp = mp;
-
-  if (mp->prev)
-    mp = mp->prev;
-  else
-    mp->offset = 0;
-
-  /* Scan over the following entries and adjust their offsets.  */
-  while (mp->next != NULL)
-    {
-      if (mp->next->min_address < mp->min_address + mp->fix_size)
-       mp->next->min_address = mp->min_address + mp->fix_size;
-
-      if (mp->refcount)
-       mp->next->offset = mp->offset + mp->fix_size;
-      else
-       mp->next->offset = mp->offset;
-
-      mp = mp->next;
-    }
-
-  return min_mp;
-}
-
-void
-minipool::assign_offsets (Mfix *barrier)
-{
-  HOST_WIDE_INT offset = 0;
-  Mnode *mp;
-
-  minipool_barrier = barrier;
-
-  for (mp = vector_head; mp != NULL; mp = mp->next)
-    {
-      mp->offset = offset;
-
-      if (mp->refcount > 0)
-       offset += mp->fix_size;
-    }
-}
-
-/* Output the literal table */
-void
-minipool::dump (rtx_insn *scan)
-{
-  Mnode * mp;
-  Mnode * nmp;
-  int align64 = 0;
-
-  if (ARM_DOUBLEWORD_ALIGN)
-    for (mp = vector_head; mp != NULL; mp = mp->next)
-      if (mp->refcount > 0 && mp->fix_size >= 8)
-       {
-         align64 = 1;
-         break;
-       }
-
-  if (dump_file)
-    fprintf (dump_file,
-            ";; Emitting minipool after insn %u; address %ld; align %d 
(bytes)\n",
-            INSN_UID (scan), (unsigned long) minipool_barrier->address, 
align64 ? 8 : 4);
-
-  scan = emit_label_after (gen_label_rtx (), scan);
-  scan = emit_insn_after (align64 ? gen_align_8 () : gen_align_4 (), scan);
-  scan = emit_label_after (vector_label, scan);
-
-  for (mp = vector_head; mp != NULL; mp = nmp)
-    {
-      if (mp->refcount > 0)
-       {
-         if (dump_file)
-           {
-             fprintf (dump_file,
-                      ";;  Offset %u, min %ld, max %ld ",
-                      (unsigned) mp->offset, (unsigned long) mp->min_address,
-                      (unsigned long) mp->max_address);
-             arm_print_value (dump_file, mp->value);
-             fputc ('\n', dump_file);
-           }
-
-         rtx val = copy_rtx (mp->value);
-
-         switch (GET_MODE_SIZE (mp->mode))
-           {
-#ifdef HAVE_consttable_1
-           case 1:
-             scan = emit_insn_after (gen_consttable_1 (val), scan);
-             break;
-
-#endif
-#ifdef HAVE_consttable_2
-           case 2:
-             scan = emit_insn_after (gen_consttable_2 (val), scan);
-             break;
-
-#endif
-#ifdef HAVE_consttable_4
-           case 4:
-             scan = emit_insn_after (gen_consttable_4 (val), scan);
-             break;
-
-#endif
-#ifdef HAVE_consttable_8
-           case 8:
-             scan = emit_insn_after (gen_consttable_8 (val), scan);
-             break;
-
-#endif
-#ifdef HAVE_consttable_16
-           case 16:
-              scan = emit_insn_after (gen_consttable_16 (val), scan);
-              break;
-
-#endif
-           default:
-             gcc_unreachable ();
-           }
-       }
-
-      nmp = mp->next;
-      free (mp);
-    }
-
-  reset ();
-  scan = emit_insn_after (gen_consttable_end (), scan);
-  scan = emit_barrier_after (scan);
-}
-
-/* Return the cost of forcibly inserting a barrier after INSN.  */
-static int
-arm_barrier_cost (rtx_insn *insn)
-{
-  /* Basing the location of the pool on the loop depth is preferable,
-     but at the moment, the basic block information seems to be
-     corrupt by this stage of the compilation.  */
-  int base_cost = 50;
-  rtx_insn *next = next_nonnote_insn (insn);
-
-  if (next != NULL && LABEL_P (next))
-    base_cost -= 20;
-
-  switch (GET_CODE (insn))
-    {
-    case CODE_LABEL:
-      /* It will always be better to place the table before the label, rather
-        than after it.  */
-      return 50;
-
-    case INSN:
-    case CALL_INSN:
-      return base_cost;
-
-    case JUMP_INSN:
-      return base_cost - 10;
-
-    default:
-      return base_cost + 10;
-    }
-}
-
-/* Find the best place in the insn stream in the range
-   (FIX->address,MAX_ADDRESS) to forcibly insert a minipool barrier.
-   Create the barrier by inserting a jump and add a new fix entry for
-   it.  */
-static Mfix *
-create_fix_barrier (Mfix *fix, HOST_WIDE_INT max_address)
-{
-  HOST_WIDE_INT count = 0;
-  rtx_barrier *barrier;
-  rtx_insn *from = fix->insn;
-  /* The instruction after which we will insert the jump.  */
-  rtx_insn *selected = NULL;
-  int selected_cost;
-  /* The address at which the jump instruction will be placed.  */
-  HOST_WIDE_INT selected_address;
-  Mfix * new_fix;
-  HOST_WIDE_INT max_count = max_address - fix->address;
-  rtx_code_label *label = gen_label_rtx ();
-
-  selected_cost = arm_barrier_cost (from);
-  selected_address = fix->address;
-
-  while (from && count < max_count)
-    {
-      rtx_jump_table_data *tmp;
-      int new_cost;
-
-      /* This code shouldn't have been called if there was a natural barrier
-        within range.  */
-      gcc_assert (!BARRIER_P (from));
-
-      /* Count the length of this insn.  This must stay in sync with the
-        code that pushes minipool fixes.  */
-      if (LABEL_P (from))
-       count += get_label_padding (from);
-      else
-       count += get_attr_length (from);
-
-      /* If there is a jump table, add its length.  */
-      if (tablejump_p (from, NULL, &tmp))
-       {
-         count += get_jump_table_size (tmp);
-
-         /* Jump tables aren't in a basic block, so base the cost on
-            the dispatch insn.  If we select this location, we will
-            still put the pool after the table.  */
-         new_cost = arm_barrier_cost (from);
-
-         if (count < max_count
-             && (!selected || new_cost <= selected_cost))
-           {
-             selected = tmp;
-             selected_cost = new_cost;
-             selected_address = fix->address + count;
-           }
-
-         /* Continue after the dispatch table.  */
-         from = NEXT_INSN (tmp);
-         continue;
-       }
-
-      new_cost = arm_barrier_cost (from);
-
-      if (count < max_count
-         && (!selected || new_cost <= selected_cost))
-       {
-         selected = from;
-         selected_cost = new_cost;
-         selected_address = fix->address + count;
-       }
-
-      from = NEXT_INSN (from);
-    }
-
-  /* Make sure that we found a place to insert the jump.  */
-  gcc_assert (selected);
-
-  /* Create a new JUMP_INSN that branches around a barrier.  */
-  from = emit_jump_insn_after (gen_jump (label), selected);
-  JUMP_LABEL (from) = label;
-  barrier = emit_barrier_after (from);
-  emit_label_after (label, barrier);
-
-  /* Create a minipool barrier entry for the new barrier.  */
-  new_fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* new_fix));
-  new_fix->insn = barrier;
-  new_fix->address = selected_address;
-  new_fix->next = fix->next;
-  fix->next = new_fix;
-
-  return new_fix;
-}
-
-/* Record that there is a natural barrier in the insn stream at
-   ADDRESS.  */
-static void
-push_minipool_barrier (rtx_insn *insn, HOST_WIDE_INT address)
-{
-  Mfix * fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* fix));
-
-  fix->insn = insn;
-  fix->address = address;
-
-  fix->next = NULL;
-  if (minipool_fix_head != NULL)
-    minipool_fix_tail->next = fix;
-  else
-    minipool_fix_head = fix;
-
-  minipool_fix_tail = fix;
-}
-
-/* Record INSN, which will need fixing up to load a value from the
-   minipool.  ADDRESS is the offset of the insn since the start of the
-   function; LOC is a pointer to the part of the insn which requires
-   fixing; VALUE is the constant that must be loaded, which is of type
-   MODE.  */
-static void
-push_minipool_fix (rtx_insn *insn, HOST_WIDE_INT address, rtx *loc,
-                  machine_mode mode, rtx value)
-{
-  gcc_assert (!arm_disable_literal_pool);
-  Mfix * fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* fix));
-
-  fix->insn = insn;
-  fix->address = address;
-  fix->loc = loc;
-  fix->mode = mode;
-  fix->fix_size = MINIPOOL_FIX_SIZE (mode);
-  fix->value = value;
-  fix->forwards = get_attr_pool_range (insn);
-  fix->backwards = get_attr_neg_pool_range (insn);
-  fix->minipool = NULL;
-
-  /* If an insn doesn't have a range defined for it, then it isn't
-     expecting to be reworked by this code.  Better to stop now than
-     to generate duff assembly code.  */
-  gcc_assert (fix->forwards || fix->backwards);
-
-  /* If an entry requires 8-byte alignment then assume all constant pools
-     require 4 bytes of padding.  Trying to do this later on a per-pool
-     basis is awkward because existing pool entries have to be modified.  */
-  if (ARM_DOUBLEWORD_ALIGN && fix->fix_size >= 8)
-    minipool_pad = 4;
-
-  if (dump_file)
-    {
-      fprintf (dump_file,
-              ";; %smode fixup for i%d; addr %lu, range (%ld,%ld): ",
-              GET_MODE_NAME (mode),
-              INSN_UID (insn), (unsigned long) address,
-              -1 * (long)fix->backwards, (long)fix->forwards);
-      arm_print_value (dump_file, fix->value);
-      fprintf (dump_file, "\n");
-    }
-
-  /* Add it to the chain of fixes.  */
-  fix->next = NULL;
-
-  if (minipool_fix_head != NULL)
-    minipool_fix_tail->next = fix;
-  else
-    minipool_fix_head = fix;
-
-  minipool_fix_tail = fix;
-}
-
 /* Return maximum allowed cost of synthesizing a 64-bit constant VAL inline.
    Returns the number of insns needed, or 99 if we always want to synthesize
    the value.  */
@@ -18671,246 +17711,6 @@ arm_const_double_by_immediates (rtx val)
   return true;
 }
 
-/* Scan INSN and note any of its operands that need fixing.
-   If DO_PUSHES is false we do not actually push any of the fixups
-   needed.  */
-static void
-note_invalid_constants (rtx_insn *insn, HOST_WIDE_INT address, int do_pushes)
-{
-  int opno;
-
-  extract_constrain_insn (insn);
-
-  if (recog_data.n_alternatives == 0)
-    return;
-
-  /* Fill in recog_op_alt with information about the constraints of
-     this insn.  */
-  preprocess_constraints (insn);
-
-  const operand_alternative *op_alt = which_op_alt ();
-  for (opno = 0; opno < recog_data.n_operands; opno++)
-    {
-      /* Things we need to fix can only occur in inputs.  */
-      if (recog_data.operand_type[opno] != OP_IN)
-       continue;
-
-      /* If this alternative is a memory reference, then any mention
-        of constants in this alternative is really to fool reload
-        into allowing us to accept one there.  We need to fix them up
-        now so that we output the right code.  */
-      if (op_alt[opno].memory_ok)
-       {
-         rtx op = recog_data.operand[opno];
-
-         if (CONSTANT_P (op))
-           {
-             if (do_pushes)
-               push_minipool_fix (insn, address, recog_data.operand_loc[opno],
-                                  recog_data.operand_mode[opno], op);
-           }
-         else if (MEM_P (op)
-                  && GET_CODE (XEXP (op, 0)) == SYMBOL_REF
-                  && CONSTANT_POOL_ADDRESS_P (XEXP (op, 0)))
-           {
-             if (do_pushes)
-               {
-                 rtx cop = avoid_constant_pool_reference (op);
-
-                 /* Casting the address of something to a mode narrower
-                    than a word can cause avoid_constant_pool_reference()
-                    to return the pool reference itself.  That's no good to
-                    us here.  Lets just hope that we can use the
-                    constant pool value directly.  */
-                 if (op == cop)
-                   cop = get_pool_constant (XEXP (op, 0));
-
-                 push_minipool_fix (insn, address,
-                                    recog_data.operand_loc[opno],
-                                    recog_data.operand_mode[opno], cop);
-               }
-
-           }
-       }
-    }
-
-  return;
-}
-
-/* GCC normally builds a single literal pool for an entire function,
-   but pc-relative loads on Arm (and especially thumb1) have very
-   limited offsets, which means accessing values through a single
-   pool is expensive if we have to build an address for the pool entry.
-   To mitigate this we build tables of literals into the body of a function
-   if it is too large to put them at the end, then place the tables
-   inside the function body (ideally after unconditional branches).  */
-static void
-place_minipools (void)
-{
-  rtx_insn *insn;
-  HOST_WIDE_INT address = 0;
-  Mfix *fix;
-  minipool mpool;
-
-  /* Ensure all insns that must be split have been split at this point.
-     Otherwise, the pool placement code below may compute incorrect
-     insn lengths.  Note that when optimizing, all insns have already
-     been split at this point.  */
-  if (!optimize)
-    split_all_insns_noflow ();
-
-  minipool_fix_head = minipool_fix_tail = NULL;
-
-  /* The first insn must always be a note, or the code below won't
-     scan it properly.  */
-  insn = get_insns ();
-  gcc_assert (NOTE_P (insn));
-  minipool_pad = 0;
-
-  /* Scan all the insns and record the operands that will need fixing.  */
-  for (insn = next_nonnote_insn (insn); insn; insn = next_nonnote_insn (insn))
-    {
-      if (BARRIER_P (insn))
-       push_minipool_barrier (insn, address);
-      else if (INSN_P (insn))
-       {
-         rtx_jump_table_data *table;
-
-         note_invalid_constants (insn, address, true);
-         address += get_attr_length (insn);
-
-         /* If the insn is a vector jump, add the size of the table
-            and skip the table.  */
-         if (tablejump_p (insn, NULL, &table))
-           {
-             address += get_jump_table_size (table);
-             insn = table;
-           }
-       }
-      else if (LABEL_P (insn))
-       /* Add the worst-case padding due to alignment.  We don't add
-          the _current_ padding because the minipool insertions
-          themselves might change it.  */
-       address += get_label_padding (insn);
-    }
-
-  fix = minipool_fix_head;
-
-  /* Now scan the fixups and perform the required changes.  */
-  while (fix)
-    {
-      Mfix *ftmp;
-      Mfix *fdel;
-      Mfix *last_added_fix;
-      Mfix *last_barrier = NULL;
-      Mfix *this_fix;
-
-      /* Skip any further barriers before the next fix.  */
-      while (fix && BARRIER_P (fix->insn))
-       fix = fix->next;
-
-      /* No more fixes.  */
-      if (fix == NULL)
-       break;
-
-      last_added_fix = NULL;
-
-      for (ftmp = fix; ftmp; ftmp = ftmp->next)
-       {
-         if (BARRIER_P (ftmp->insn))
-           {
-             if (ftmp->address >= mpool.head ()->max_address)
-               break;
-
-             last_barrier = ftmp;
-           }
-         else if ((ftmp->minipool = mpool.add_forward_ref (ftmp)) == NULL)
-           break;
-
-         last_added_fix = ftmp;  /* Keep track of the last fix added.  */
-       }
-
-      /* If we found a barrier, drop back to that; any fixes that we
-        could have reached but come after the barrier will now go in
-        the next mini-pool.  */
-      if (last_barrier != NULL)
-       {
-         /* Reduce the refcount for those fixes that won't go into this
-            pool after all.  */
-         for (fdel = last_barrier->next;
-              fdel && fdel != ftmp;
-              fdel = fdel->next)
-           {
-             fdel->minipool->refcount--;
-             fdel->minipool = NULL;
-           }
-
-         ftmp = last_barrier;
-       }
-      else
-       {
-         /* ftmp is first fix that we can't fit into this pool and
-            there no natural barriers that we could use.  Insert a
-            new barrier in the code somewhere between the previous
-            fix and this one, and arrange to jump around it.  */
-         HOST_WIDE_INT max_address;
-
-         /* The last item on the list of fixes must be a barrier, so
-            we can never run off the end of the list of fixes without
-            last_barrier being set.  */
-         gcc_assert (ftmp);
-
-         max_address = mpool.head()->max_address;
-         /* Check that there isn't another fix that is in range that
-            we couldn't fit into this pool because the pool was
-            already too large: we need to put the pool before such an
-            instruction.  The pool itself may come just after the
-            fix because create_fix_barrier also allows space for a
-            jump instruction.  */
-         if (ftmp->address < max_address)
-           max_address = ftmp->address + 1;
-
-         last_barrier = create_fix_barrier (last_added_fix, max_address);
-       }
-
-      mpool.assign_offsets (last_barrier);
-
-      while (ftmp)
-       {
-         if (!BARRIER_P (ftmp->insn)
-             && ((ftmp->minipool = mpool.add_backward_ref (ftmp)) == NULL))
-           break;
-
-         ftmp = ftmp->next;
-       }
-
-      /* Scan over the fixes we have identified for this pool, fixing them
-        up and adding the constants to the pool itself.  */
-      for (this_fix = fix; this_fix && ftmp != this_fix;
-          this_fix = this_fix->next)
-       if (!BARRIER_P (this_fix->insn))
-         {
-           rtx addr
-             = plus_constant (Pmode,
-                              gen_rtx_LABEL_REF (VOIDmode,
-                                                 mpool.label ()),
-                              this_fix->minipool->offset);
-           *this_fix->loc = gen_rtx_MEM (this_fix->mode, addr);
-         }
-
-      mpool.dump (last_barrier->insn);
-      fix = ftmp;
-    }
-
-  /* From now on we must synthesize any constants that we can't handle
-     directly.  This can happen if the RTL gets split during final
-     instruction generation.  */
-  cfun->machine->after_arm_reorg = 1;
-
-  /* Free the minipool memory.  */
-  obstack_free (&minipool_obstack, minipool_startobj);
-}
-
 /* This function computes the clear mask and PADDING_BITS_TO_CLEAR for structs
    and unions in the context of ARMv8-M Security Extensions.  It is used as a
    helper function for both 'cmse_nonsecure_call' and 'cmse_nonsecure_entry'
@@ -20016,7 +18816,7 @@ arm_reorg (void)
   /* Only create a literal pool when one is wanted.  This should be a no-op if
      we've disabled them, but...  */
   if (!arm_disable_literal_pool)
-    place_minipools ();
+    minipools::place_minipools ();
 }
 
 /* Routines to output assembly language.  */
diff --git a/gcc/config/arm/minipool.cc b/gcc/config/arm/minipool.cc
new file mode 100644
index 0000000000000..39f50577bca5c
--- /dev/null
+++ b/gcc/config/arm/minipool.cc
@@ -0,0 +1,1242 @@
+/* GCC minipool support for ARM.
+   Copyright (C) 2026 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/>.  */
+
+/* Arm instructions cannot load a large constant directly into a
+   register; they have to come from a pc relative load.  The constant
+   must therefore be placed in the addressable range of the pc
+   relative load.  Depending on the precise pc relative load
+   instruction the range is somewhere between 256 bytes and 4k.  This
+   means that we often have to dump a constant inside a function, and
+   generate code to branch around it.
+
+   It is important to minimize this, since the branches will slow
+   things down and make the code larger.
+
+   Normally we can hide the table after an existing unconditional
+   branch so that there is no interruption of the flow, but in the
+   worst case the code looks like this:
+
+       ldr     rn, L1
+       ...
+       b       L2
+       align
+       L1:     .long value
+       L2:
+       ...
+
+       ldr     rn, L3
+       ...
+       b       L4
+       align
+       L3:     .long value
+       L4:
+       ...
+
+   We fix this by performing a scan after scheduling, which notices
+   which instructions need to have their operands fetched from the
+   constant table and builds the table.
+
+   The algorithm starts by building a table of all the constants that
+   need fixing up and all the natural barriers in the function (places
+   where a constant table can be dropped without breaking the flow).
+   For each fixup we note how far the pc-relative replacement will be
+   able to reach and the offset of the instruction into the function.
+
+   Having built the table we then group the fixes together to form
+   tables that are as large as possible (subject to addressing
+   constraints) and emit each table of constants after the last
+   barrier that is within range of all the instructions in the group.
+   If a group does not contain a barrier, then we forcibly create one
+   by inserting a jump instruction into the flow.  Once the table has
+   been inserted, the insns are then modified to reference the
+   relevant entry in the pool.
+
+   Possible enhancements to the algorithm (not implemented) are:
+
+   1) For some processors and object formats, there may be benefit in
+   aligning the pools to the start of cache lines; this alignment
+   would need to be taken into account when calculating addressability
+   of a pool.  */
+
+#define IN_TARGET_CODE 1
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "target.h"
+#include "memmodel.h"
+#include "rtl.h"
+#include "emit-rtl.h"
+#include "output.h"
+#include "insn-attr.h"
+#include "optabs.h"
+#include "recog.h"
+#include "obstack.h"
+
+#include "minipool.h"
+
+namespace minipools {
+
+  /* Obstack for minipool constant handling.  */
+  static struct obstack minipool_obstack;
+  static char *minipool_startobj;
+
+  /* Forward definitions of types.  */
+  typedef struct minipool_node    Mnode;
+  typedef struct minipool_fixup   Mfix;
+
+  struct minipool_node
+  {
+    /* Doubly linked chain of entries.  */
+    Mnode * next;
+    Mnode * prev;
+    /* The maximum offset into the code that this entry can be placed.  While
+       pushing fixes for forward references, all entries are sorted in order
+       of increasing max_address.  */
+    HOST_WIDE_INT max_address;
+    /* Similarly for an entry inserted for a backwards ref.  */
+    HOST_WIDE_INT min_address;
+    /* The number of fixes referencing this entry.  This can become zero
+       if we "unpush" an entry.  In this case we ignore the entry when we
+       come to emit the code.  */
+    int refcount;
+    /* The offset from the start of the minipool.  */
+    HOST_WIDE_INT offset;
+    /* The value in table.  */
+    rtx value;
+    /* The mode of value.  */
+    machine_mode mode;
+    /* The size of the value.  */
+    int fix_size;
+  };
+
+  struct minipool_fixup
+  {
+    Mfix *            next;
+    rtx_insn *        insn;
+    HOST_WIDE_INT     address;
+    rtx *             loc;
+    machine_mode mode;
+    int               fix_size;
+    rtx               value;
+    Mnode *           minipool;
+    HOST_WIDE_INT     forwards;
+    HOST_WIDE_INT     backwards;
+  };
+
+  class minipool
+  {
+  public:
+    minipool () { reset (); }
+    void reset()
+      {
+       vector_head = NULL;
+       vector_tail = NULL;
+       vector_label = NULL;
+      }
+    Mnode *head () { return vector_head; }
+    rtx_code_label *label () { return vector_label; }
+    Mnode *add_forward_ref (Mfix *);
+    Mnode *add_backward_ref (Mfix *);
+    void assign_offsets (Mfix *);
+    void dump (rtx_insn *);
+  private:
+    Mnode *move_fix_forward_ref (Mnode *, Mnode *, HOST_WIDE_INT);
+    Mnode *move_fix_backward_ref (Mnode *, Mnode *, HOST_WIDE_INT);
+    Mnode *vector_head;
+    Mnode *vector_tail;
+    rtx_code_label *vector_label;
+  };
+
+  static int minipool_pad;
+
+  /* The linked list of all minipool fixes required for this function.  */
+  Mfix *minipool_fix_head;
+  Mfix *minipool_fix_tail;
+  /* The fix entry for the current minipool, once it has been placed.  */
+  Mfix *minipool_barrier;
+
+
+  static Mfix *create_fix_barrier (Mfix *, HOST_WIDE_INT);
+  static void push_minipool_barrier (rtx_insn *, HOST_WIDE_INT);
+  static void push_minipool_fix (rtx_insn *, HOST_WIDE_INT, rtx *,
+                                machine_mode, rtx);
+  static int barrier_cost (rtx_insn *);
+
+  void add_gc_roots (void)
+  {
+    gcc_obstack_init(&minipool_obstack);
+    minipool_startobj = (char *) obstack_alloc (&minipool_obstack, 0);
+  }
+
+  /* Print a symbolic form of X to the debug file, F.  */
+  static void
+  print_value (FILE *f, rtx x)
+  {
+    switch (GET_CODE (x))
+      {
+      case CONST_INT:
+       fprintf (f, HOST_WIDE_INT_PRINT_HEX, INTVAL (x));
+       return;
+
+      case CONST_DOUBLE:
+       {
+         char fpstr[20];
+         real_to_decimal (fpstr, CONST_DOUBLE_REAL_VALUE (x),
+                          sizeof (fpstr), 0, 1);
+         fputs (fpstr, f);
+       }
+       return;
+
+      case CONST_VECTOR:
+       {
+         int i;
+
+         fprintf (f, "<");
+         for (i = 0; i < CONST_VECTOR_NUNITS (x); i++)
+           {
+             fprintf (f, HOST_WIDE_INT_PRINT_HEX,
+                      INTVAL (CONST_VECTOR_ELT (x, i)));
+             if (i < (CONST_VECTOR_NUNITS (x) - 1))
+               fputc (',', f);
+           }
+         fprintf (f, ">");
+       }
+       return;
+
+      case CONST_STRING:
+       fprintf (f, "\"%s\"", XSTR (x, 0));
+       return;
+
+      case SYMBOL_REF:
+       fprintf (f, "`%s'", XSTR (x, 0));
+       return;
+
+      case LABEL_REF:
+       fprintf (f, "L%d", INSN_UID (XEXP (x, 0)));
+       return;
+
+      case CONST:
+       print_value (f, XEXP (x, 0));
+       return;
+
+      case PLUS:
+       print_value (f, XEXP (x, 0));
+       fprintf (f, "+");
+       print_value (f, XEXP (x, 1));
+       return;
+
+      case PC:
+       fprintf (f, "pc");
+       return;
+
+      default:
+       fprintf (f, "????");
+       return;
+      }
+  }
+
+#ifndef JUMP_TABLES_IN_TEXT_SECTION
+#define JUMP_TABLES_IN_TEXT_SECTION 0
+#endif
+
+  static HOST_WIDE_INT
+  get_jump_table_size (rtx_jump_table_data *insn)
+  {
+    /* ADDR_VECs only take room if read-only data does into the text
+       section.  */
+    if (JUMP_TABLES_IN_TEXT_SECTION || readonly_data_section == text_section)
+      {
+       rtx body = PATTERN (insn);
+       int elt = GET_CODE (body) == ADDR_DIFF_VEC ? 1 : 0;
+       HOST_WIDE_INT size;
+       HOST_WIDE_INT modesize;
+
+       modesize = GET_MODE_SIZE (GET_MODE (body));
+       size = modesize * XVECLEN (body, elt);
+       switch (modesize)
+         {
+         case 1:
+           /* Round up size  of TBB table to a halfword boundary.  */
+           size = (size + 1) & ~HOST_WIDE_INT_1;
+           break;
+         case 2:
+           /* No padding necessary for TBH.  */
+           break;
+         case 4:
+           /* Add two bytes for alignment on Thumb.  */
+           if (TARGET_THUMB)
+             size += 2;
+           break;
+         default:
+           gcc_unreachable ();
+         }
+       return size;
+      }
+
+    return 0;
+  }
+
+  /* Return the cost of forcibly inserting a barrier after INSN.  */
+  static int
+  barrier_cost (rtx_insn *insn)
+  {
+    /* Basing the location of the pool on the loop depth is preferable, but at
+       the moment, the basic block information seems to be corrupt by this
+       stage of the compilation.  */
+    int base_cost = 50;
+    rtx_insn *next = next_nonnote_insn (insn);
+
+    if (next != NULL && LABEL_P (next))
+      base_cost -= 20;
+
+    switch (GET_CODE (insn))
+      {
+      case CODE_LABEL:
+       /* It will always be better to place the table before the label, rather
+          than after it.  */
+       return 50;
+
+      case INSN:
+      case CALL_INSN:
+       return base_cost;
+
+      case JUMP_INSN:
+       return base_cost - 10;
+
+      default:
+       return base_cost + 10;
+      }
+  }
+
+  /* Return the maximum amount of padding that will be inserted before label
+     LABEL.  */
+  static HOST_WIDE_INT
+  get_label_padding (rtx label)
+  {
+    HOST_WIDE_INT align, min_insn_size;
+
+    align = 1 << label_to_alignment (label).levels[0].log;
+    min_insn_size = TARGET_THUMB ? 2 : 4;
+    return align > min_insn_size ? align - min_insn_size : 0;
+  }
+
+  /* Fixes less than a word need padding out to a word boundary.  */
+#define MINIPOOL_FIX_SIZE(mode)                                        \
+  (GET_MODE_SIZE ((mode)) >= 4 ? GET_MODE_SIZE ((mode)) : 4)
+
+  /* Move a minipool fix MP from its current location to before MAX_MP.
+     If MAX_MP is NULL, then MP doesn't need moving, but the addressing
+     constraints may need updating.  */
+  Mnode *
+  minipool::move_fix_forward_ref (Mnode *mp, Mnode *max_mp,
+                                 HOST_WIDE_INT max_address)
+  {
+    /* The code below assumes these are different.  */
+    gcc_assert (mp != max_mp);
+
+    if (max_mp == NULL)
+      {
+       if (max_address < mp->max_address)
+         mp->max_address = max_address;
+      }
+    else
+      {
+       if (max_address > max_mp->max_address - mp->fix_size)
+         mp->max_address = max_mp->max_address - mp->fix_size;
+       else
+         mp->max_address = max_address;
+
+       /* Unlink MP from its current position.  Since max_mp is non-null,
+          mp->prev must be non-null.  */
+       mp->prev->next = mp->next;
+       if (mp->next != NULL)
+         mp->next->prev = mp->prev;
+       else
+         vector_tail = mp->prev;
+
+       /* Re-insert it before MAX_MP.  */
+       mp->next = max_mp;
+       mp->prev = max_mp->prev;
+       max_mp->prev = mp;
+
+       if (mp->prev != NULL)
+         mp->prev->next = mp;
+       else
+         vector_head = mp;
+      }
+
+    /* Save the new entry.  */
+    max_mp = mp;
+
+    /* Scan over the preceding entries and adjust their addresses as
+       required.  */
+    while (mp->prev != NULL
+          && mp->prev->max_address > mp->max_address - mp->prev->fix_size)
+      {
+       mp->prev->max_address = mp->max_address - mp->prev->fix_size;
+       mp = mp->prev;
+      }
+
+    return max_mp;
+  }
+
+  /* Add a constant to the minipool for a forward reference.  Returns the
+     node added or NULL if the constant will not fit in this pool.  */
+  Mnode *
+  minipool::add_forward_ref (Mfix *fix)
+  {
+    /* If set, max_mp is the first pool_entry that has a lower
+       constraint than the one we are trying to add.  */
+    Mnode *       max_mp = NULL;
+    HOST_WIDE_INT max_address = fix->address + fix->forwards - minipool_pad;
+    Mnode *       mp;
+
+    /* If the minipool starts before the end of FIX->INSN then this FIX
+       cannot be placed into the current pool.  Furthermore, adding the
+       new constant pool entry may cause the pool to start FIX_SIZE bytes
+       earlier.  */
+    if (vector_head &&
+       (fix->address + get_attr_length (fix->insn)
+        >= vector_head->max_address - fix->fix_size))
+      return NULL;
+
+    /* Scan the pool to see if a constant with the same value has
+       already been added.  While we are doing this, also note the
+       location where we must insert the constant if it doesn't already
+       exist.  */
+    for (mp = vector_head; mp != NULL; mp = mp->next)
+      {
+       if (GET_CODE (fix->value) == GET_CODE (mp->value)
+           && fix->mode == mp->mode
+           && (!LABEL_P (fix->value)
+               || (CODE_LABEL_NUMBER (fix->value)
+                   == CODE_LABEL_NUMBER (mp->value)))
+           && rtx_equal_p (fix->value, mp->value))
+         {
+           /* More than one fix references this entry.  */
+           mp->refcount++;
+           return move_fix_forward_ref (mp, max_mp, max_address);
+         }
+
+       /* Note the insertion point if necessary.  */
+       if (max_mp == NULL
+           && mp->max_address > max_address)
+         max_mp = mp;
+
+       /* If we are inserting an 8-bytes aligned quantity and
+          we have not already found an insertion point, then
+          make sure that all such 8-byte aligned quantities are
+          placed at the start of the pool.  */
+       if (ARM_DOUBLEWORD_ALIGN
+           && max_mp == NULL
+           && fix->fix_size >= 8
+           && mp->fix_size < 8)
+         {
+           max_mp = mp;
+           max_address = mp->max_address;
+         }
+      }
+
+    /* The value is not currently in the minipool, so we need to create
+       a new entry for it.  If MAX_MP is NULL, the entry will be put on
+       the end of the list since the placement is less constrained than
+       any existing entry.  Otherwise, we insert the new fix before
+       MAX_MP and, if necessary, adjust the constraints on the other
+       entries.  */
+    mp = XNEW (Mnode);
+    mp->fix_size = fix->fix_size;
+    mp->mode = fix->mode;
+    mp->value = fix->value;
+    mp->refcount = 1;
+    /* Not yet required for a backwards ref.  */
+    mp->min_address = -65536;
+
+    if (max_mp == NULL)
+      {
+       mp->max_address = max_address;
+       mp->next = NULL;
+       mp->prev = vector_tail;
+
+       if (mp->prev == NULL)
+         {
+           vector_head = mp;
+           vector_label = gen_label_rtx ();
+         }
+       else
+         mp->prev->next = mp;
+
+       vector_tail = mp;
+      }
+    else
+      {
+       if (max_address > max_mp->max_address - mp->fix_size)
+         mp->max_address = max_mp->max_address - mp->fix_size;
+       else
+         mp->max_address = max_address;
+
+       mp->next = max_mp;
+       mp->prev = max_mp->prev;
+       max_mp->prev = mp;
+       if (mp->prev != NULL)
+         mp->prev->next = mp;
+       else
+         vector_head = mp;
+      }
+
+    /* Save the new entry.  */
+    max_mp = mp;
+
+    /* Scan over the preceding entries and adjust their addresses as
+       required.  */
+    while (mp->prev != NULL
+          && mp->prev->max_address > mp->max_address - mp->prev->fix_size)
+      {
+       mp->prev->max_address = mp->max_address - mp->prev->fix_size;
+       mp = mp->prev;
+      }
+
+    return max_mp;
+  }
+
+  Mnode *
+  minipool::move_fix_backward_ref (Mnode *mp, Mnode *min_mp,
+                                  HOST_WIDE_INT  min_address)
+  {
+    HOST_WIDE_INT offset;
+
+    /* The code below assumes these are different.  */
+    gcc_assert (mp != min_mp);
+
+    if (min_mp == NULL)
+      {
+       if (min_address > mp->min_address)
+         mp->min_address = min_address;
+      }
+    else
+      {
+       /* We will adjust this below if it is too loose.  */
+       mp->min_address = min_address;
+
+       /* Unlink MP from its current position.  Since min_mp is non-null,
+          mp->next must be non-null.  */
+       mp->next->prev = mp->prev;
+       if (mp->prev != NULL)
+         mp->prev->next = mp->next;
+       else
+         vector_head = mp->next;
+
+       /* Reinsert it after MIN_MP.  */
+       mp->prev = min_mp;
+       mp->next = min_mp->next;
+       min_mp->next = mp;
+       if (mp->next != NULL)
+         mp->next->prev = mp;
+       else
+         vector_tail = mp;
+      }
+
+    min_mp = mp;
+
+    offset = 0;
+    for (mp = vector_head; mp != NULL; mp = mp->next)
+      {
+       mp->offset = offset;
+       if (mp->refcount > 0)
+         offset += mp->fix_size;
+
+       if (mp->next && mp->next->min_address < mp->min_address + mp->fix_size)
+         mp->next->min_address = mp->min_address + mp->fix_size;
+      }
+
+    return min_mp;
+  }
+
+  /* Add a constant to the minipool for a backward reference.  Returns the
+     node added or NULL if the constant will not fit in this pool.
+
+     Note that the code for insertion for a backwards reference can be
+     somewhat confusing because the calculated offsets for each fix do
+     not take into account the size of the pool (which is still under
+     construction.  */
+  Mnode *
+  minipool::add_backward_ref (Mfix *fix)
+  {
+    /* If set, min_mp is the last pool_entry that has a lower constraint
+       than the one we are trying to add.  */
+    Mnode *min_mp = NULL;
+    /* This can be negative, since it is only a constraint.  */
+    HOST_WIDE_INT  min_address = fix->address - fix->backwards;
+    Mnode *mp;
+
+    /* If we can't reach the current pool from this insn, or if we can't
+       insert this entry at the end of the pool without pushing other
+       fixes out of range, then we don't try.  This ensures that we
+       can't fail later on.  */
+    if (min_address >= minipool_barrier->address
+       || (vector_tail->min_address + fix->fix_size
+           >= minipool_barrier->address))
+      return NULL;
+
+    /* Scan the pool to see if a constant with the same value has
+       already been added.  While we are doing this, also note the
+       location where we must insert the constant if it doesn't already
+       exist.  */
+    for (mp = vector_tail; mp != NULL; mp = mp->prev)
+      {
+       if (GET_CODE (fix->value) == GET_CODE (mp->value)
+           && fix->mode == mp->mode
+           && (!LABEL_P (fix->value)
+               || (CODE_LABEL_NUMBER (fix->value)
+                   == CODE_LABEL_NUMBER (mp->value)))
+           && rtx_equal_p (fix->value, mp->value)
+           /* Check that there is enough slack to move this entry to the
+              end of the table (this is conservative).  */
+           && (mp->max_address
+               > (minipool_barrier->address
+                  + vector_tail->offset
+                  + vector_tail->fix_size)))
+         {
+           mp->refcount++;
+           return move_fix_backward_ref (mp, min_mp, min_address);
+         }
+
+       if (min_mp != NULL)
+         mp->min_address += fix->fix_size;
+       else
+         {
+           /* Note the insertion point if necessary.  */
+           if (mp->min_address < min_address)
+             {
+               /* For now, we do not allow the insertion of 8-byte alignment
+                  requiring nodes anywhere but at the start of the pool.  */
+               if (ARM_DOUBLEWORD_ALIGN
+                   && fix->fix_size >= 8 && mp->fix_size < 8)
+                 return NULL;
+               else
+                 min_mp = mp;
+             }
+           else if (mp->max_address
+                    < minipool_barrier->address + mp->offset + fix->fix_size)
+             {
+               /* Inserting before this entry would push the fix beyond
+                  its maximum address (which can happen if we have
+                  re-located a forwards fix); force the new fix to come
+                  after it.  */
+               if (ARM_DOUBLEWORD_ALIGN
+                   && fix->fix_size >= 8 && mp->fix_size < 8)
+                 return NULL;
+               else
+                 {
+                   min_mp = mp;
+                   min_address = mp->min_address + fix->fix_size;
+                 }
+             }
+           /* Do not insert a non-8-byte aligned quantity before 8-byte
+              aligned quantities.  */
+           else if (ARM_DOUBLEWORD_ALIGN
+                    && fix->fix_size < 8
+                    && mp->fix_size >= 8)
+             {
+               min_mp = mp;
+               min_address = mp->min_address + fix->fix_size;
+             }
+         }
+      }
+
+    /* We need to create a new entry.  */
+    mp = XNEW (Mnode);
+    mp->fix_size = fix->fix_size;
+    mp->mode = fix->mode;
+    mp->value = fix->value;
+    mp->refcount = 1;
+    mp->max_address = minipool_barrier->address + 65536;
+
+    mp->min_address = min_address;
+
+    if (min_mp == NULL)
+      {
+       mp->prev = NULL;
+       mp->next = vector_head;
+
+       if (mp->next == NULL)
+         {
+           vector_tail = mp;
+           vector_label = gen_label_rtx ();
+         }
+       else
+         mp->next->prev = mp;
+
+       vector_head = mp;
+      }
+    else
+      {
+       mp->next = min_mp->next;
+       mp->prev = min_mp;
+       min_mp->next = mp;
+
+       if (mp->next != NULL)
+         mp->next->prev = mp;
+       else
+         vector_tail = mp;
+      }
+
+    /* Save the new entry.  */
+    min_mp = mp;
+
+    if (mp->prev)
+      mp = mp->prev;
+    else
+      mp->offset = 0;
+
+    /* Scan over the following entries and adjust their offsets.  */
+    while (mp->next != NULL)
+      {
+       if (mp->next->min_address < mp->min_address + mp->fix_size)
+         mp->next->min_address = mp->min_address + mp->fix_size;
+
+       if (mp->refcount)
+         mp->next->offset = mp->offset + mp->fix_size;
+       else
+         mp->next->offset = mp->offset;
+
+       mp = mp->next;
+      }
+
+    return min_mp;
+  }
+
+  void
+  minipool::assign_offsets (Mfix *barrier)
+  {
+    HOST_WIDE_INT offset = 0;
+    Mnode *mp;
+
+    minipool_barrier = barrier;
+
+    for (mp = vector_head; mp != NULL; mp = mp->next)
+      {
+       mp->offset = offset;
+
+       if (mp->refcount > 0)
+         offset += mp->fix_size;
+      }
+  }
+
+  /* Output the literal table */
+  void
+  minipool::dump (rtx_insn *scan)
+  {
+    Mnode * mp;
+    Mnode * nmp;
+    int align64 = 0;
+
+    if (ARM_DOUBLEWORD_ALIGN)
+      for (mp = vector_head; mp != NULL; mp = mp->next)
+       if (mp->refcount > 0 && mp->fix_size >= 8)
+         {
+           align64 = 1;
+           break;
+         }
+
+    if (dump_file)
+      fprintf (dump_file,
+              ";; Emitting minipool after insn %u; address %ld; "
+              "align %d (bytes)\n",
+              INSN_UID (scan), (unsigned long) minipool_barrier->address,
+              align64 ? 8 : 4);
+
+    scan = emit_label_after (gen_label_rtx (), scan);
+    scan = emit_insn_after (align64 ? gen_align_8 () : gen_align_4 (), scan);
+    scan = emit_label_after (vector_label, scan);
+
+    for (mp = vector_head; mp != NULL; mp = nmp)
+      {
+       if (mp->refcount > 0)
+         {
+           if (dump_file)
+             {
+               fprintf (dump_file,
+                        ";;  Offset %u, min %ld, max %ld ",
+                        (unsigned) mp->offset,
+                        (unsigned long) mp->min_address,
+                        (unsigned long) mp->max_address);
+               print_value (dump_file, mp->value);
+               fputc ('\n', dump_file);
+             }
+
+           rtx val = copy_rtx (mp->value);
+
+           switch (GET_MODE_SIZE (mp->mode))
+             {
+#ifdef HAVE_consttable_1
+             case 1:
+               scan = emit_insn_after (gen_consttable_1 (val), scan);
+               break;
+
+#endif
+#ifdef HAVE_consttable_2
+             case 2:
+               scan = emit_insn_after (gen_consttable_2 (val), scan);
+               break;
+
+#endif
+#ifdef HAVE_consttable_4
+             case 4:
+               scan = emit_insn_after (gen_consttable_4 (val), scan);
+               break;
+
+#endif
+#ifdef HAVE_consttable_8
+             case 8:
+               scan = emit_insn_after (gen_consttable_8 (val), scan);
+               break;
+
+#endif
+#ifdef HAVE_consttable_16
+             case 16:
+               scan = emit_insn_after (gen_consttable_16 (val), scan);
+               break;
+
+#endif
+             default:
+               gcc_unreachable ();
+             }
+         }
+
+       nmp = mp->next;
+       free (mp);
+      }
+
+    reset ();
+    scan = emit_insn_after (gen_consttable_end (), scan);
+    scan = emit_barrier_after (scan);
+  }
+
+  /* Find the best place in the insn stream in the range
+     (FIX->address,MAX_ADDRESS) to forcibly insert a minipool barrier.
+     Create the barrier by inserting a jump and add a new fix entry for
+     it.  */
+  static Mfix *
+  create_fix_barrier (Mfix *fix, HOST_WIDE_INT max_address)
+  {
+    HOST_WIDE_INT count = 0;
+    rtx_barrier *barrier;
+    rtx_insn *from = fix->insn;
+    /* The instruction after which we will insert the jump.  */
+    rtx_insn *selected = NULL;
+    int selected_cost;
+    /* The address at which the jump instruction will be placed.  */
+    HOST_WIDE_INT selected_address;
+    Mfix * new_fix;
+    HOST_WIDE_INT max_count = max_address - fix->address;
+    rtx_code_label *label = gen_label_rtx ();
+
+    selected_cost = barrier_cost (from);
+    selected_address = fix->address;
+
+    while (from && count < max_count)
+      {
+       rtx_jump_table_data *tmp;
+       int new_cost;
+
+       /* This code shouldn't have been called if there was a natural barrier
+          within range.  */
+       gcc_assert (!BARRIER_P (from));
+
+       /* Count the length of this insn.  This must stay in sync with the
+          code that pushes minipool fixes.  */
+       if (LABEL_P (from))
+         count += get_label_padding (from);
+       else
+         count += get_attr_length (from);
+
+       /* If there is a jump table, add its length.  */
+       if (tablejump_p (from, NULL, &tmp))
+         {
+           count += get_jump_table_size (tmp);
+
+           /* Jump tables aren't in a basic block, so base the cost on
+              the dispatch insn.  If we select this location, we will
+              still put the pool after the table.  */
+           new_cost = barrier_cost (from);
+
+           if (count < max_count
+               && (!selected || new_cost <= selected_cost))
+             {
+               selected = tmp;
+               selected_cost = new_cost;
+               selected_address = fix->address + count;
+             }
+
+           /* Continue after the dispatch table.  */
+           from = NEXT_INSN (tmp);
+           continue;
+         }
+
+       new_cost = barrier_cost (from);
+
+       if (count < max_count
+           && (!selected || new_cost <= selected_cost))
+         {
+           selected = from;
+           selected_cost = new_cost;
+           selected_address = fix->address + count;
+         }
+
+       from = NEXT_INSN (from);
+      }
+
+    /* Make sure that we found a place to insert the jump.  */
+    gcc_assert (selected);
+
+    /* Create a new JUMP_INSN that branches around a barrier.  */
+    from = emit_jump_insn_after (gen_jump (label), selected);
+    JUMP_LABEL (from) = label;
+    barrier = emit_barrier_after (from);
+    emit_label_after (label, barrier);
+
+    /* Create a minipool barrier entry for the new barrier.  */
+    new_fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* new_fix));
+    new_fix->insn = barrier;
+    new_fix->address = selected_address;
+    new_fix->next = fix->next;
+    fix->next = new_fix;
+
+    return new_fix;
+  }
+
+  /* Record that there is a natural barrier in the insn stream at
+     ADDRESS.  */
+  static void
+  push_minipool_barrier (rtx_insn *insn, HOST_WIDE_INT address)
+  {
+    Mfix * fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* fix));
+
+    fix->insn = insn;
+    fix->address = address;
+
+    fix->next = NULL;
+    if (minipool_fix_head != NULL)
+      minipool_fix_tail->next = fix;
+    else
+      minipool_fix_head = fix;
+
+    minipool_fix_tail = fix;
+  }
+
+  /* Record INSN, which will need fixing up to load a value from the
+     minipool.  ADDRESS is the offset of the insn since the start of the
+     function; LOC is a pointer to the part of the insn which requires
+     fixing; VALUE is the constant that must be loaded, which is of type
+     MODE.  */
+  static void
+  push_minipool_fix (rtx_insn *insn, HOST_WIDE_INT address, rtx *loc,
+                    machine_mode mode, rtx value)
+  {
+    gcc_assert (!arm_disable_literal_pool);
+    Mfix * fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* fix));
+
+    fix->insn = insn;
+    fix->address = address;
+    fix->loc = loc;
+    fix->mode = mode;
+    fix->fix_size = MINIPOOL_FIX_SIZE (mode);
+    fix->value = value;
+    fix->forwards = get_attr_pool_range (insn);
+    fix->backwards = get_attr_neg_pool_range (insn);
+    fix->minipool = NULL;
+
+    /* If an insn doesn't have a range defined for it, then it isn't
+       expecting to be reworked by this code.  Better to stop now than
+       to generate duff assembly code.  */
+    gcc_assert (fix->forwards || fix->backwards);
+
+    /* If an entry requires 8-byte alignment then assume all constant pools
+       require 4 bytes of padding.  Trying to do this later on a per-pool
+       basis is awkward because existing pool entries have to be modified.  */
+    if (ARM_DOUBLEWORD_ALIGN && fix->fix_size >= 8)
+      minipool_pad = 4;
+
+    if (dump_file)
+      {
+       fprintf (dump_file,
+                ";; %smode fixup for i%d; addr %lu, range (%ld,%ld): ",
+                GET_MODE_NAME (mode),
+                INSN_UID (insn), (unsigned long) address,
+                -1 * (long)fix->backwards, (long)fix->forwards);
+       print_value (dump_file, fix->value);
+       fprintf (dump_file, "\n");
+      }
+
+    /* Add it to the chain of fixes.  */
+    fix->next = NULL;
+
+    if (minipool_fix_head != NULL)
+      minipool_fix_tail->next = fix;
+    else
+      minipool_fix_head = fix;
+
+    minipool_fix_tail = fix;
+  }
+
+  /* Scan INSN and note any of its operands that need fixing.
+     If DO_PUSHES is false we do not actually push any of the fixups
+     needed.  */
+  static void
+  note_invalid_constants (rtx_insn *insn, HOST_WIDE_INT address, int do_pushes)
+  {
+    int opno;
+
+    extract_constrain_insn (insn);
+
+    if (recog_data.n_alternatives == 0)
+      return;
+
+    /* Fill in recog_op_alt with information about the constraints of
+       this insn.  */
+    preprocess_constraints (insn);
+
+    const operand_alternative *op_alt = which_op_alt ();
+    for (opno = 0; opno < recog_data.n_operands; opno++)
+      {
+       /* Things we need to fix can only occur in inputs.  */
+       if (recog_data.operand_type[opno] != OP_IN)
+         continue;
+
+       /* If this alternative is a memory reference, then any mention
+          of constants in this alternative is really to fool reload
+          into allowing us to accept one there.  We need to fix them up
+          now so that we output the right code.  */
+       if (op_alt[opno].memory_ok)
+         {
+           rtx op = recog_data.operand[opno];
+
+           if (CONSTANT_P (op))
+             {
+               if (do_pushes)
+                 push_minipool_fix (insn, address,
+                                    recog_data.operand_loc[opno],
+                                    recog_data.operand_mode[opno], op);
+             }
+           else if (MEM_P (op)
+                    && GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+                    && CONSTANT_POOL_ADDRESS_P (XEXP (op, 0)))
+             {
+               if (do_pushes)
+                 {
+                   rtx cop = avoid_constant_pool_reference (op);
+
+                   /* Casting the address of something to a mode narrower
+                      than a word can cause avoid_constant_pool_reference()
+                      to return the pool reference itself.  That's no good to
+                      us here.  Lets just hope that we can use the
+                      constant pool value directly.  */
+                   if (op == cop)
+                     cop = get_pool_constant (XEXP (op, 0));
+
+                   push_minipool_fix (insn, address,
+                                      recog_data.operand_loc[opno],
+                                      recog_data.operand_mode[opno], cop);
+                 }
+
+             }
+         }
+      }
+
+    return;
+  }
+
+  /* GCC normally builds a single literal pool for an entire function,
+     but pc-relative loads on Arm (and especially thumb1) have very
+     limited offsets, which means accessing values through a single
+     pool is expensive if we have to build an address for the pool entry.
+     To mitigate this we build tables of literals into the body of a function
+     if it is too large to put them at the end, then place the tables
+     inside the function body (ideally after unconditional branches).  */
+  void
+  place_minipools (void)
+  {
+    rtx_insn *insn;
+    HOST_WIDE_INT address = 0;
+    Mfix *fix;
+    minipool mpool;
+
+    /* Ensure all insns that must be split have been split at this point.
+       Otherwise, the pool placement code below may compute incorrect
+       insn lengths.  Note that when optimizing, all insns have already
+       been split at this point.  */
+    if (!optimize)
+      split_all_insns_noflow ();
+
+    minipool_fix_head = minipool_fix_tail = NULL;
+
+    /* The first insn must always be a note, or the code below won't
+       scan it properly.  */
+    insn = get_insns ();
+    gcc_assert (NOTE_P (insn));
+    minipool_pad = 0;
+
+    /* Scan all the insns and record the operands that will need fixing.  */
+    for (insn = next_nonnote_insn (insn); insn;
+        insn = next_nonnote_insn (insn))
+      {
+       if (BARRIER_P (insn))
+         push_minipool_barrier (insn, address);
+       else if (INSN_P (insn))
+         {
+           rtx_jump_table_data *table;
+
+           note_invalid_constants (insn, address, true);
+           address += get_attr_length (insn);
+
+           /* If the insn is a vector jump, add the size of the table
+              and skip the table.  */
+           if (tablejump_p (insn, NULL, &table))
+             {
+               address += get_jump_table_size (table);
+               insn = table;
+             }
+         }
+       else if (LABEL_P (insn))
+         /* Add the worst-case padding due to alignment.  We don't add
+            the _current_ padding because the minipool insertions
+            themselves might change it.  */
+         address += get_label_padding (insn);
+      }
+
+    fix = minipool_fix_head;
+
+    /* Now scan the fixups and perform the required changes.  */
+    while (fix)
+      {
+       Mfix *ftmp;
+       Mfix *fdel;
+       Mfix *last_added_fix;
+       Mfix *last_barrier = NULL;
+       Mfix *this_fix;
+
+       /* Skip any further barriers before the next fix.  */
+       while (fix && BARRIER_P (fix->insn))
+         fix = fix->next;
+
+       /* No more fixes.  */
+       if (fix == NULL)
+         break;
+
+       last_added_fix = NULL;
+
+       for (ftmp = fix; ftmp; ftmp = ftmp->next)
+         {
+           if (BARRIER_P (ftmp->insn))
+             {
+               if (ftmp->address >= mpool.head ()->max_address)
+                 break;
+
+               last_barrier = ftmp;
+             }
+           else if ((ftmp->minipool = mpool.add_forward_ref (ftmp)) == NULL)
+             break;
+
+           last_added_fix = ftmp;  /* Keep track of the last fix added.  */
+         }
+
+       /* If we found a barrier, drop back to that; any fixes that we
+          could have reached but come after the barrier will now go in
+          the next mini-pool.  */
+       if (last_barrier != NULL)
+         {
+           /* Reduce the refcount for those fixes that won't go into this
+              pool after all.  */
+           for (fdel = last_barrier->next;
+                fdel && fdel != ftmp;
+                fdel = fdel->next)
+             {
+               fdel->minipool->refcount--;
+               fdel->minipool = NULL;
+             }
+
+           ftmp = last_barrier;
+         }
+       else
+         {
+           /* ftmp is first fix that we can't fit into this pool and
+              there no natural barriers that we could use.  Insert a
+              new barrier in the code somewhere between the previous
+              fix and this one, and arrange to jump around it.  */
+           HOST_WIDE_INT max_address;
+
+           /* The last item on the list of fixes must be a barrier, so
+              we can never run off the end of the list of fixes without
+              last_barrier being set.  */
+           gcc_assert (ftmp);
+
+           max_address = mpool.head()->max_address;
+           /* Check that there isn't another fix that is in range that
+              we couldn't fit into this pool because the pool was
+              already too large: we need to put the pool before such an
+              instruction.  The pool itself may come just after the
+              fix because create_fix_barrier also allows space for a
+              jump instruction.  */
+           if (ftmp->address < max_address)
+             max_address = ftmp->address + 1;
+
+           last_barrier = create_fix_barrier (last_added_fix, max_address);
+         }
+
+       mpool.assign_offsets (last_barrier);
+
+       while (ftmp)
+         {
+           if (!BARRIER_P (ftmp->insn)
+               && ((ftmp->minipool = mpool.add_backward_ref (ftmp)) == NULL))
+             break;
+
+           ftmp = ftmp->next;
+         }
+
+       /* Scan over the fixes we have identified for this pool, fixing them
+          up and adding the constants to the pool itself.  */
+       for (this_fix = fix; this_fix && ftmp != this_fix;
+            this_fix = this_fix->next)
+         if (!BARRIER_P (this_fix->insn))
+           {
+             rtx addr
+               = plus_constant (Pmode,
+                                gen_rtx_LABEL_REF (VOIDmode,
+                                                   mpool.label ()),
+                                this_fix->minipool->offset);
+             *this_fix->loc = gen_rtx_MEM (this_fix->mode, addr);
+           }
+
+       mpool.dump (last_barrier->insn);
+       fix = ftmp;
+      }
+
+    /* From now on we must synthesize any constants that we can't handle
+       directly.  This can happen if the RTL gets split during final
+       instruction generation.  */
+    cfun->machine->after_arm_reorg = 1;
+
+    /* Free the minipool memory.  */
+    obstack_free (&minipool_obstack, minipool_startobj);
+  }
+
+} // namespace minipools
diff --git a/gcc/config/arm/minipool.h b/gcc/config/arm/minipool.h
new file mode 100644
index 0000000000000..e10533d76f9e4
--- /dev/null
+++ b/gcc/config/arm/minipool.h
@@ -0,0 +1,23 @@
+/* GCC minipool support for ARM.
+   Copyright (C) 2026 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/>.  */
+
+namespace minipools {
+  void add_gc_roots (void);
+  void place_minipools (void);
+}
diff --git a/gcc/config/arm/t-arm b/gcc/config/arm/t-arm
index 850c7911fb344..33884460d702d 100644
--- a/gcc/config/arm/t-arm
+++ b/gcc/config/arm/t-arm
@@ -119,11 +119,12 @@ s-arm-native: $(srcdir)/config/arm/parsecpu.awk \
        $(STAMP) s-arm-native
 
 aarch-common.o: $(srcdir)/config/arm/aarch-common.cc $(CONFIG_H) $(SYSTEM_H) \
-    coretypes.h $(TM_H) $(TM_P_H) $(RTL_H) $(TREE_H) output.h $(C_COMMON_H)
+    $(CORETYPES_H) $(TM_H) $(TM_P_H) $(RTL_H) $(TREE_H) output.h $(C_COMMON_H)
        $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
                $(srcdir)/config/arm/aarch-common.cc
 
-arm.o: $(srcdir)/config/arm/arm.cc $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) 
\
+arm.o: $(srcdir)/config/arm/arm.cc $(CONFIG_H) $(SYSTEM_H) $(CORETYPES_H) \
+  $(TM_H) \
   $(RTL_H) $(TREE_H) $(HASH_TABLE_H) $(OBSTACK_H) $(REGS_H) hard-reg-set.h \
   insn-config.h conditions.h output.h dumpfile.h \
   $(INSN_ATTR_H) $(FLAGS_H) reload.h $(FUNCTION_H) \
@@ -132,13 +133,14 @@ arm.o: $(srcdir)/config/arm/arm.cc $(CONFIG_H) 
$(SYSTEM_H) coretypes.h $(TM_H) \
   $(TARGET_H) $(TARGET_DEF_H) debug.h langhooks.h $(DF_H) \
   intl.h libfuncs.h $(PARAMS_H) $(OPTS_H) sched-int.h \
   arm-cpu-data.h \
+  $(srcdir)/config/arm/minipool.h\
   $(srcdir)/config/arm/arm-protos.h \
   $(srcdir)/config/arm/arm_neon_builtins.def \
   $(srcdir)/config/arm/arm_vfp_builtins.def \
   $(srcdir)/config/arm/arm_mve_builtins.def
 
 arm-builtins.o: $(srcdir)/config/arm/arm-builtins.cc $(CONFIG_H) \
-  $(SYSTEM_H) coretypes.h $(TM_H) \
+  $(SYSTEM_H) $(CORETYPES_H) $(TM_H) \
   $(RTL_H) $(TREE_H) expr.h $(TM_P_H) $(RECOG_H) langhooks.h \
   $(DIAGNOSTIC_CORE_H) $(OPTABS_H) \
   $(srcdir)/config/arm/arm-protos.h \
@@ -151,7 +153,7 @@ arm-builtins.o: $(srcdir)/config/arm/arm-builtins.cc 
$(CONFIG_H) \
                $(srcdir)/config/arm/arm-builtins.cc
 
 arm-mve-builtins.o: $(srcdir)/config/arm/arm-mve-builtins.cc $(CONFIG_H) \
-  $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) $(RTL_H) $(TM_P_H) \
+  $(SYSTEM_H) $(CORETYPES_H) $(TM_H) $(TREE_H) $(RTL_H) $(TM_P_H) \
   memmodel.h insn-codes.h optabs.h recog.h expr.h basic-block.h \
   function.h fold-const.h gimple.h gimple-fold.h emit-rtl.h langhooks.h \
   stringpool.h attribs.h diagnostic.h \
@@ -167,7 +169,7 @@ arm-mve-builtins.o: 
$(srcdir)/config/arm/arm-mve-builtins.cc $(CONFIG_H) \
 
 arm-mve-builtins-shapes.o: \
   $(srcdir)/config/arm/arm-mve-builtins-shapes.cc \
-  $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
+  $(CONFIG_H) $(SYSTEM_H) $(CORETYPES_H) $(TM_H) $(TREE_H) \
   $(RTL_H) memmodel.h insn-codes.h optabs.h \
   $(srcdir)/config/arm/arm-mve-builtins.h \
   $(srcdir)/config/arm/arm-mve-builtins-shapes.h
@@ -176,7 +178,7 @@ arm-mve-builtins-shapes.o: \
 
 arm-mve-builtins-base.o: \
   $(srcdir)/config/arm/arm-mve-builtins-base.cc \
-  $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) $(RTL_H) \
+  $(CONFIG_H) $(SYSTEM_H) $(CORETYPES_H) $(TM_H) $(TREE_H) $(RTL_H) \
   memmodel.h insn-codes.h $(OPTABS_H) \
   $(BASIC_BLOCK_H) $(FUNCTION_H) $(GIMPLE_H) \
   $(srcdir)/config/arm/arm-mve-builtins.h \
@@ -187,7 +189,7 @@ arm-mve-builtins-base.o: \
                $(srcdir)/config/arm/arm-mve-builtins-base.cc
 
 arm-c.o: $(srcdir)/config/arm/arm-c.cc $(CONFIG_H) $(SYSTEM_H) \
-    coretypes.h $(TM_H) $(TREE_H) output.h $(C_COMMON_H)
+    $(CORETYPES_H) $(TM_H) $(TREE_H) output.h $(C_COMMON_H)
        $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
                $(srcdir)/config/arm/arm-c.cc
 
@@ -195,6 +197,17 @@ arm-d.o: $(srcdir)/config/arm/arm-d.cc
        $(COMPILE) $<
        $(POSTCOMPILE)
 
+minipool.o: \
+  $(srcdir)/config/arm/minipool.cc \
+  $(CONFIG_H) $(SYSTEM_H) $(OBSTACK_H) $(CORETYPES_H) $(RTL_H) $(TARGET_H) \
+  output.h insn-attr.h $(OPTABS_H) $(RECOG_H) memmodel.h emit-rtl.h \
+  $(BACKEND_H) \
+  $(srcdir)/config/arm/minipool.h
+       $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
+               $(srcdir)/config/arm/minipool.cc
+
+EXTRA_OBJS += minipool.o
+
 arm-common.o: arm-cpu-cdata.h
 
 driver-arm.o: arm-native.h
-- 
2.54.0

Reply via email to