, Patch #1 of 10, Add instruction format enumeration
diff mbox series

Message ID 20190814213609.GA16578@ibm-toto.the-meissners.org
State New
Headers show
Series
  • , Patch #1 of 10, Add instruction format enumeration
Related show

Commit Message

Michael Meissner Aug. 14, 2019, 9:36 p.m. UTC
This patch implements the insn_form enumeration that identifies which types of
instruction format is used for memory instruction.  While the PowerPC has
additional formats, the instruction formats that we need to use are:

	INSN_FORM_D	-- Traditional D-form instructions (16 bits offset);
	INSN_FORM_DS	-- Traditional DS-form instructions (14 bits offset);
	INSN_FORM_DQ	-- Traditional DQ-form instructions (12 bits offset).

In the previous patches, these were called offset_format instead of insn_form.
I changed the way the insn_form values are computed to use the mask bits in the
reg_addr data structure, instead of having a function that set this up in a
confusing manner.

Previous patches did not have the support for external addresses, and these are
added in this new patch.

This patch includes the notation of a default instruction format.  This is used
in the absence of the actual register used.  The default instruction format is
based on the anticipated usage.

For example, in 64-bit mode, a DImode integer's default instruction format is
INSN_FORM_DS because the LD and STD instructions use the DS instruction
encoding.  But if you were loading the DImode into a traditional FPR register,
the LFD and STFD instructins are D format.

Similarly, for SFmode and DFmode, the traditional FPR memory instructions uses
D format instruction, but the traditional Altivec memory instructions use the
DS format.  In this case the traditional instruction format is D format.

The new prefixed memory and pc-relative lookup functions now take the default
insn_form as an argument.  This is important if the default format is DS format
or DQ format, and the offset has the bottom 2 or 4 bits non-zero.  In this
case, we can do the memory operation using a prefixed load or store instead of
requiring the offset to be loaded into a GPR.

The pc-relative match function (pcrel_addr_p) now optionally returns the base
address and offset to allow print_operand_address and other functions that
would otherwise need to decode the instruction to have the values available
directly.

There is a new function (reg_to_insn_form) that takes a register and an address
and returns the instruction format for that particular memory address.  This is
due to the fact that when offset addressing was added to the PowerPC
traditional Altivec registers, the instruction format used was DS format
instead of D format for the scalar values.  If the register is a pseudo
register, the function returns the default instruction format.  This function
will primarily be used in the next patch to identify whether an insn uses a
prefixed instruction or not.

2019-08-14   Michael Meissner  <meissner@linux.ibm.com>

	* config/rs6000/predicates.md (pcrel_address): Rewrite to use
	pcrel_addr_p.
	(pcrel_external_address): Rewrite to use pcrel_addr_p.
	(prefixed_mem_operand): Rewrite to use prefixed_local_addr_p.
	(pcrel_external_mem_operand): Rewrite to use pcrel_addr_p.
	* config/rs6000/rs6000-protos.h (reg_to_insn_form): New
	declaration.
	(pcrel_info_type): New declaration.
	(PCREL_NULL): New macro.
	(pcrel_addr_p): New declaration.
	(rs6000_prefixed_address_mode_p): Delete.
	* config/rs6000/rs6000.c (struct rs6000_reg_addr): Add fields for
	instruction format and prefixed memory support.
	(rs6000_debug_insn_form): New debug function.
	(rs6000_debug_print_mode): Print instruction formats.
	(setup_insn_form): New function.
	(rs6000_init_hard_regno_mode_ok): Call setup_insn_form.
	(print_operand_address): Call pcrel_addr_p instead of
	pcrel_address.  Add support for external pc-relative labels.
	(mode_supports_prefixed_address_p): Delete.
	(rs6000_prefixed_address_mode_p): Delete, replace with
	prefixed_local_addr_p.
	(prefixed_local_addr_p): Replace rs6000_prefixed_address_mode_p.
	Add argument to specify the instruction format.
	(pcrel_addr_p): New function.
	(reg_to_insn_form): New function.
	* config/rs6000/rs6000.md (enum insn_form): New enumeration.

Patch
diff mbox series

Index: gcc/config/rs6000/predicates.md
===================================================================
--- gcc/config/rs6000/predicates.md	(revision 274173)
+++ gcc/config/rs6000/predicates.md	(working copy)
@@ -1626,32 +1626,11 @@ 
   return GET_CODE (op) == UNSPEC && XINT (op, 1) == UNSPEC_TOCREL;
 })
 
-;; Return true if the operand is a pc-relative address.
+;; Return true if the operand is a pc-relative address to a local symbol.
 (define_predicate "pcrel_address"
   (match_code "label_ref,symbol_ref,const")
 {
-  if (!rs6000_pcrel_p (cfun))
-    return false;
-
-  if (GET_CODE (op) == CONST)
-    op = XEXP (op, 0);
-
-  /* Validate offset.  */
-  if (GET_CODE (op) == PLUS)
-    {
-      rtx op0 = XEXP (op, 0);
-      rtx op1 = XEXP (op, 1);
-
-      if (!CONST_INT_P (op1) || !SIGNED_34BIT_OFFSET_P (INTVAL (op1)))
-	return false;
-
-      op = op0;
-    }
-
-  if (LABEL_REF_P (op))
-    return true;
-
-  return (SYMBOL_REF_P (op) && SYMBOL_REF_LOCAL_P (op));
+  return pcrel_addr_p (op, true, false, PCREL_NULL);
 })
 
 ;; Return true if the operand is an external symbol whose address can be loaded
@@ -1665,32 +1644,14 @@ 
 (define_predicate "pcrel_external_address"
   (match_code "symbol_ref,const")
 {
-  if (!rs6000_pcrel_p (cfun))
-    return false;
-
-  if (GET_CODE (op) == CONST)
-    op = XEXP (op, 0);
-
-  /* Validate offset.  */
-  if (GET_CODE (op) == PLUS)
-    {
-      rtx op0 = XEXP (op, 0);
-      rtx op1 = XEXP (op, 1);
-
-      if (!CONST_INT_P (op1) || !SIGNED_34BIT_OFFSET_P (INTVAL (op1)))
-	return false;
-
-      op = op0;
-    }
-
-  return (SYMBOL_REF_P (op) && !SYMBOL_REF_LOCAL_P (op));
+  return pcrel_addr_p (op, false, true, PCREL_NULL);
 })
 
 ;; Return 1 if op is a prefixed memory operand.
 (define_predicate "prefixed_mem_operand"
   (match_code "mem")
 {
-  return rs6000_prefixed_address_mode_p (XEXP (op, 0), GET_MODE (op));
+  return prefixed_local_addr_p (XEXP (op, 0), mode, INSN_FORM_UNKNOWN);
 })
 
 ;; Return 1 if op is a memory operand to an external variable when we
@@ -1699,7 +1660,7 @@ 
 (define_predicate "pcrel_external_mem_operand"
   (match_code "mem")
 {
-  return pcrel_external_address (XEXP (op, 0), Pmode);
+  return pcrel_addr_p (XEXP (op, 0), false, true, PCREL_NULL);
 })
 
 ;; Match the first insn (addis) in fusing the combination of addis and loads to
Index: gcc/config/rs6000/rs6000-protos.h
===================================================================
--- gcc/config/rs6000/rs6000-protos.h	(revision 274173)
+++ gcc/config/rs6000/rs6000-protos.h	(working copy)
@@ -47,7 +47,19 @@  extern bool legitimate_indirect_address_p (rtx, in
 extern bool legitimate_indexed_address_p (rtx, int);
 extern bool avoiding_indexed_address_p (machine_mode);
 extern rtx rs6000_force_indexed_or_indirect_mem (rtx x);
+extern enum insn_form reg_to_insn_form (rtx, machine_mode);
+extern bool prefixed_local_addr_p (rtx, machine_mode, enum insn_form);
 
+/* Pc-relative address broken into component parts by pcrel_addr_p.  */
+typedef struct {
+  rtx base_addr;		/* SYMBOL_REF or LABEL_REF.  */
+  HOST_WIDE_INT offset;		/* Offset from the base address.  */
+  bool external_p;		/* Is the symbol external?  */
+} pcrel_info_type;
+
+#define PCREL_NULL ((pcrel_info_type *)0)
+
+extern bool pcrel_addr_p (rtx, bool, bool, pcrel_info_type *);
 extern rtx rs6000_got_register (rtx);
 extern rtx find_addr_reg (rtx);
 extern rtx gen_easy_altivec_constant (rtx);
@@ -154,7 +166,6 @@  extern align_flags rs6000_loop_align (rtx);
 extern void rs6000_split_logical (rtx [], enum rtx_code, bool, bool, bool);
 extern bool rs6000_pcrel_p (struct function *);
 extern bool rs6000_fndecl_pcrel_p (const_tree);
-extern bool rs6000_prefixed_address_mode_p (rtx, machine_mode);
 #endif /* RTX_CODE */
 
 #ifdef TREE_CODE
Index: gcc/config/rs6000/rs6000.c
===================================================================
--- gcc/config/rs6000/rs6000.c	(revision 274173)
+++ gcc/config/rs6000/rs6000.c	(working copy)
@@ -369,8 +369,11 @@  struct rs6000_reg_addr {
   enum insn_code reload_fpr_gpr;	/* INSN to move from FPR to GPR.  */
   enum insn_code reload_gpr_vsx;	/* INSN to move from GPR to VSX.  */
   enum insn_code reload_vsx_gpr;	/* INSN to move from VSX to GPR.  */
+  enum insn_form default_insn_form;	/* Default format for offsets.  */
+  enum insn_form insn_form[(int)N_RELOAD_REG]; /* Register insn format.  */
   addr_mask_type addr_mask[(int)N_RELOAD_REG]; /* Valid address masks.  */
   bool scalar_in_vmx_p;			/* Scalar value can go in VMX.  */
+  bool prefixed_memory_p;		/* We can use prefixed memory.  */
 };
 
 static struct rs6000_reg_addr reg_addr[NUM_MACHINE_MODES];
@@ -2053,6 +2056,28 @@  rs6000_debug_vector_unit (enum rs6000_vector v)
   return ret;
 }
 
+/* Return a character that can be printed out to describe an instruction
+   format.  */
+
+DEBUG_FUNCTION char
+rs6000_debug_insn_form (enum insn_form iform)
+{
+  char ret;
+
+  switch (iform)
+    {
+    case INSN_FORM_UNKNOWN:  ret = '-'; break;
+    case INSN_FORM_D:        ret = 'd'; break;
+    case INSN_FORM_DS:       ret = 's'; break;
+    case INSN_FORM_DQ:       ret = 'q'; break;
+    case INSN_FORM_X:        ret = 'x'; break;
+    case INSN_FORM_PREFIXED: ret = 'p'; break;
+    default:                 ret = '?'; break;
+    }
+
+  return ret;
+}
+
 /* Inner function printing just the address mask for a particular reload
    register class.  */
 DEBUG_FUNCTION char *
@@ -2115,6 +2140,12 @@  rs6000_debug_print_mode (ssize_t m)
     fprintf (stderr, " %s: %s", reload_reg_map[rc].name,
 	     rs6000_debug_addr_mask (reg_addr[m].addr_mask[rc], true));
 
+  fprintf (stderr, "  Format: %c:%c%c%c",
+          rs6000_debug_insn_form (reg_addr[m].default_insn_form),
+          rs6000_debug_insn_form (reg_addr[m].insn_form[RELOAD_REG_GPR]),
+          rs6000_debug_insn_form (reg_addr[m].insn_form[RELOAD_REG_FPR]),
+          rs6000_debug_insn_form (reg_addr[m].insn_form[RELOAD_REG_VMX]));
+
   if ((reg_addr[m].reload_store != CODE_FOR_nothing)
       || (reg_addr[m].reload_load != CODE_FOR_nothing))
     {
@@ -2668,6 +2699,153 @@  rs6000_setup_reg_addr_masks (void)
     }
 }
 
+/* Set up the instruction format for each mode and register type from the
+   addr_mask.  */
+
+static void
+setup_insn_form (void)
+{
+  for (ssize_t m = 0; m < NUM_MACHINE_MODES; ++m)
+    {
+      machine_mode scalar_mode = (machine_mode) m;
+
+      /* Convert complex and IBM double double/_Decimal128 into their scalar
+	 parts that the registers will be split into for doing load or
+	 store.  */
+      if (COMPLEX_MODE_P (scalar_mode))
+	scalar_mode = GET_MODE_INNER (scalar_mode);
+
+      if (FLOAT128_2REG_P (scalar_mode))
+	scalar_mode = DFmode;
+
+      for (ssize_t rc = FIRST_RELOAD_REG_CLASS; rc <= LAST_RELOAD_REG_CLASS; rc++)
+	{
+	  machine_mode single_reg_mode = scalar_mode;
+	  size_t msize = GET_MODE_SIZE (scalar_mode);
+	  addr_mask_type addr_mask = reg_addr[scalar_mode].addr_mask[rc];
+	  enum insn_form iform = INSN_FORM_UNKNOWN;
+
+	  /* Is the mode permitted in the GPR/FPR/Altivec registers?  */
+	  if ((addr_mask & RELOAD_REG_VALID) != 0)
+	    {
+	      /* The addr_mask does not have the offsettable or indexed bits
+		 set for modes that are split into multiple registers (like
+		 IFmode).  It doesn't need this set, since typically by time it
+		 is used in secondary reload, the modes are split into
+		 component parts.
+
+		 The instruction format however can be used earlier in the
+		 compilation, so we need to setup what kind of instruction can
+		 be generated for the modes that are split.  */
+	      if ((addr_mask & (RELOAD_REG_MULTIPLE
+				| RELOAD_REG_OFFSET
+				| RELOAD_REG_INDEXED)) == RELOAD_REG_MULTIPLE)
+		{
+		  /* Multiple register types in GPRs depend on whether we can
+		     use DImode in a single register or SImode.  */
+		  if (rc == RELOAD_REG_GPR)
+		    {
+		      if (TARGET_POWERPC64)
+			{
+			  gcc_assert ((msize % 8) == 0);
+			  single_reg_mode = DImode;
+			}
+
+		      else
+			{
+			  gcc_assert ((msize % 4) == 0);
+			  single_reg_mode = SImode;
+			}
+		    }
+
+		  /* Multiple VSX vector sized data items will use a single
+		     vector type as an instruction format.  */
+		  else if (TARGET_VSX)
+		    {
+		      gcc_assert ((rc == RELOAD_REG_FPR)
+				  || (rc == RELOAD_REG_VMX));
+
+		      if ((msize % 16) == 0)
+			single_reg_mode = V2DImode;
+		    }
+
+		  /* Multiple Altivec vector sized data items will use a single
+		     vector type as an instruction format.  */
+		  else if (TARGET_ALTIVEC && rc == RELOAD_REG_VMX
+			   && (msize % 16) == 0
+			   && VECTOR_MODE_P (single_reg_mode))
+		    single_reg_mode = V4SImode;
+
+		  /* If we only have the traditional floating point unit, use
+		     DFmode as the base type.  */
+		  else if (!TARGET_VSX && TARGET_HARD_FLOAT
+			   && rc == RELOAD_REG_FPR && (msize % 8) == 0)
+		    single_reg_mode = DFmode;
+
+		  /* Get the information for the register mode used after
+		     splitting.  */
+		  addr_mask = reg_addr[single_reg_mode].addr_mask[rc];
+		  msize = GET_MODE_SIZE (single_reg_mode);
+		}
+
+	      /* Figure out the instruction format of each mode.
+
+		 For offsettable addresses that aren't specifically quad mode,
+		 see if the default form is D or DS.  GPR 64-bit offsettable
+		 addresses are DS format.  Likewise, all Altivec offsettable
+		 adddresses are DS format.  */
+	      if ((addr_mask & RELOAD_REG_OFFSET) != 0)
+		{
+		  if ((addr_mask & RELOAD_REG_QUAD_OFFSET) != 0)
+		    iform = INSN_FORM_DQ;
+
+		  else if (rc == RELOAD_REG_VMX
+			   || (rc == RELOAD_REG_GPR && TARGET_POWERPC64
+			       && (msize >= 8)))
+		    iform = INSN_FORM_DS;
+
+		  else
+		    iform = INSN_FORM_D;
+		}
+
+	      else if ((addr_mask & RELOAD_REG_INDEXED) != 0)
+		iform = INSN_FORM_X;
+	    }
+
+	  reg_addr[m].insn_form[rc] = iform;
+	}
+
+      /* Figure out the default insn format that is used for offsettable memory
+	 instructions.  For scalar floating point use the FPR addressing, for
+	 vectors and IEEE 128-bit use a suitable vector register type, and
+	 otherwise use GPRs.  */
+      ssize_t def_rc;
+      if (TARGET_VSX
+	  && (VECTOR_MODE_P (scalar_mode) || FLOAT128_IEEE_P (scalar_mode)))
+	{
+	  if ((reg_addr[m].addr_mask[RELOAD_REG_FPR] & RELOAD_REG_VALID) != 0)
+	    def_rc = RELOAD_REG_FPR;
+	  else
+	    def_rc = RELOAD_REG_VMX;
+	}
+
+      else if (TARGET_ALTIVEC && !TARGET_VSX && VECTOR_MODE_P (scalar_mode))
+	def_rc = RELOAD_REG_VMX;
+
+      else if (TARGET_HARD_FLOAT && SCALAR_FLOAT_MODE_P (scalar_mode))
+	def_rc = RELOAD_REG_FPR;
+
+      else
+	def_rc = RELOAD_REG_GPR;
+
+      reg_addr[m].default_insn_form = reg_addr[m].insn_form[def_rc];
+
+      /* Don't enable prefixed memory support until all of the infrastructure
+	 changes are in.  */
+      reg_addr[m].prefixed_memory_p = false;
+    }
+}
+
 
 /* Initialize the various global tables that are based on register size.  */
 static void
@@ -3181,6 +3359,9 @@  rs6000_init_hard_regno_mode_ok (bool global_init_p
      use.  */
   rs6000_setup_reg_addr_masks ();
 
+  /* Update the instruction formats.  */
+  setup_insn_form ();
+
   if (global_init_p || TARGET_DEBUG_TARGET)
     {
       if (TARGET_DEBUG_REG)
@@ -13070,30 +13251,22 @@  print_operand (FILE *file, rtx x, int code)
 void
 print_operand_address (FILE *file, rtx x)
 {
+  pcrel_info_type pcrel_info;
+
   if (REG_P (x))
     fprintf (file, "0(%s)", reg_names[ REGNO (x) ]);
 
   /* Is it a pc-relative address?  */
-  else if (pcrel_address (x, Pmode))
+  else if (pcrel_addr_p (x, true, true, &pcrel_info))
     {
-      HOST_WIDE_INT offset;
+      output_addr_const (file, pcrel_info.base_addr);
 
-      if (GET_CODE (x) == CONST)
-	x = XEXP (x, 0);
+      if (pcrel_info.offset)
+	fprintf (file, "%+" PRId64, pcrel_info.offset);
 
-      if (GET_CODE (x) == PLUS)
-	{
-	  offset = INTVAL (XEXP (x, 1));
-	  x = XEXP (x, 0);
-	}
-      else
-	offset = 0;
+      if (pcrel_info.external_p)
+	fputs ("@got", file);
 
-      output_addr_const (file, x);
-
-      if (offset)
-	fprintf (file, "%+" PRId64, offset);
-
       fputs ("@pcrel", file);
     }
   else if (SYMBOL_REF_P (x) || GET_CODE (x) == CONST
@@ -13579,70 +13752,204 @@  rs6000_pltseq_template (rtx *operands, int which)
   return str;
 }
 #endif
+
+/* Return true if the address ADDR is a prefixed address either with a large
+   offset, an offset that does not fit in the normal instruction form, or a
+   pc-relative address to a local symbol.
 
-/* Helper function to return whether a MODE can do prefixed loads/stores.
-   VOIDmode is used when we are loading the pc-relative address into a base
-   register, but we are not using it as part of a memory operation.  As modes
-   add support for prefixed memory, they will be added here.  */
+   MODE is the mode of the memory.
 
-static bool
-mode_supports_prefixed_address_p (machine_mode mode)
-{
-  return mode == VOIDmode;
-}
+   IFORM is used to determine if the traditional address is either DS format or
+   DQ format and the bottom bits of the offset are non-zero.  */
 
-/* Function to return true if ADDR is a valid prefixed memory address that uses
-   mode MODE.  */
-
 bool
-rs6000_prefixed_address_mode_p (rtx addr, machine_mode mode)
+prefixed_local_addr_p (rtx addr, machine_mode mode, enum insn_form iform)
 {
-  if (!TARGET_PREFIXED_ADDR || !mode_supports_prefixed_address_p (mode))
+  if (!reg_addr[mode].prefixed_memory_p)
     return false;
 
-  /* Check for PC-relative addresses.  */
-  if (pcrel_address (addr, Pmode))
-    return true;
+  if (GET_CODE (addr) == CONST)
+    addr = XEXP (addr, 0);
 
-  /* Check for prefixed memory addresses that have a large numeric offset,
-     or an offset that can't be used for a DS/DQ-form memory operation.  */
+  /* Single register, not prefixed.  */
+  if (REG_P (addr) || SUBREG_P (addr))
+    return false;
+
+  /* Register + offset.  */
+  else if (GET_CODE (addr) == PLUS
+	   && (REG_P (XEXP (addr, 0)) || SUBREG_P (XEXP (addr, 0)))
+	   && CONST_INT_P (XEXP (addr, 1)))
+    {
+      HOST_WIDE_INT offset = INTVAL (XEXP (addr, 1));
+
+      /* Prefixed instructions can only access 34-bits.  Fail if the value
+	 is larger than that.  */
+      if (!SIGNED_34BIT_OFFSET_P (offset))
+	return false;
+
+      /* For small offsets see whether it might be a DS or DQ instruction where
+	 the bottom bits non-zero.  This would require using a prefixed
+	 address.  If the offset is larger than 16 bits, then the instruction
+	 must be prefixed.  */
+      if (SIGNED_16BIT_OFFSET_P (offset))
+	{
+	  /* Use default if we don't know the precise instruction format.  */
+	  if (iform == INSN_FORM_UNKNOWN)
+	    iform = reg_addr[mode].default_insn_form;
+
+	  if (iform == INSN_FORM_DS)
+	    return (offset & 3) != 0;
+
+	  else if (iform == INSN_FORM_DQ)
+	    return (offset & 15) != 0;
+
+	  else if (iform != INSN_FORM_PREFIXED)
+	    return false;
+	}
+
+      return true;
+    }
+
+  else if (!TARGET_PCREL)
+    return false;
+
   if (GET_CODE (addr) == PLUS)
     {
-      rtx op0 = XEXP (addr, 0);
       rtx op1 = XEXP (addr, 1);
 
-      if (!base_reg_operand (op0, Pmode) || !CONST_INT_P (op1))
+      if (!CONST_INT_P (op1) || !SIGNED_34BIT_OFFSET_P (INTVAL (op1)))
 	return false;
 
-      HOST_WIDE_INT value = INTVAL (op1);
-      if (!SIGNED_34BIT_OFFSET_P (value))
+      addr = XEXP (addr, 0);
+    }
+
+  /* Local pc-relative symbols/labels.  */
+  return (LABEL_REF_P (addr)
+	  || (SYMBOL_REF_P (addr) && SYMBOL_REF_LOCAL_P (addr)));
+}
+
+/* Return true if the address ADDR is a prefixed address that is a pc-relative
+   reference either to a local symbol or to an external symbol.  We break apart
+   the address and return the parts.  LOCAL_SYMBOL_P and EXTERNAL_SYMBOL_P says
+   whether local and external pc-relative symbols are allowed.  P_INFO points
+   to a structure that returns the broken out component parts if desired.  */
+
+bool
+pcrel_addr_p (rtx addr,
+	      bool local_symbol_p,
+	      bool external_symbol_p,
+	      pcrel_info_type *p_info)
+{
+  rtx base_addr = NULL_RTX;
+  HOST_WIDE_INT offset = 0;
+  bool was_external_p = false;
+
+  if (p_info)
+    {
+      p_info->base_addr = NULL_RTX;
+      p_info->offset = 0;
+      p_info->external_p = false;
+    }
+
+  if (!TARGET_PCREL)
+    return false;
+
+  if (GET_CODE (addr) == CONST)
+    addr = XEXP (addr, 0);
+
+  /* Pc-relative symbols/labels without offsets.  */
+  if (SYMBOL_REF_P (addr))
+    {
+      base_addr = addr;
+      was_external_p = !SYMBOL_REF_LOCAL_P (addr);
+    }
+
+  else if (LABEL_REF_P (addr))
+    base_addr = addr;
+
+  /* Pc-relative symbols with offsets.  */
+  else if (GET_CODE (addr) == PLUS
+	   && SYMBOL_REF_P (XEXP (addr, 0))
+	   && CONST_INT_P (XEXP (addr, 1)))
+    {
+      base_addr = XEXP (addr, 0);
+      offset = INTVAL (XEXP (addr, 1));
+      was_external_p = !SYMBOL_REF_LOCAL_P (base_addr);
+
+      if (!SIGNED_34BIT_OFFSET_P (offset))
 	return false;
+    }
 
-      /* Offset larger than 16-bits?  */
-      if (!SIGNED_16BIT_OFFSET_P (value))
-	return true;
+  else
+    return false;
 
-      /* DQ instruction (bottom 4 bits must be 0) for vectors.  */
-      HOST_WIDE_INT mask;
-      if (GET_MODE_SIZE (mode) >= 16)
-	mask = 15;
+  if (was_external_p && !external_symbol_p)
+    return false;
 
-      /* DS instruction (bottom 2 bits must be 0).  For 32-bit integers, we
-	 need to use DS instructions if we are sign-extending the value with
-	 LWA.  For 32-bit floating point, we need DS instructions to load and
-	 store values to the traditional Altivec registers.  */
-      else if (GET_MODE_SIZE (mode) >= 4)
-	mask = 3;
+  if (!was_external_p && !local_symbol_p)
+    return false;
 
-      /* QImode/HImode has no restrictions.  */
+  if (p_info)
+    {
+      p_info->base_addr = base_addr;
+      p_info->offset = offset;
+      p_info->external_p = was_external_p;
+    }
+
+  return true;
+}
+
+/* Given a register and a mode, return the instruction format for that
+   register.  If the register is a pseudo register, use the default format.
+   Otherwise if it is hard register, look to see exactly what type of
+   addressing is used.  */
+
+enum insn_form
+reg_to_insn_form (rtx reg, machine_mode mode)
+{
+  enum insn_form iform;
+
+  /* Handle UNSPECs, such as the special UNSPEC_SF_FROM_SI and
+     UNSPEC_SI_FROM_SF UNSPECs, which are used to hide SF/SI interactions.
+     Look at the first argument, and if it is a register, use that.  */
+  if (GET_CODE (reg) == UNSPEC || GET_CODE (reg) == UNSPEC_VOLATILE)
+    {
+      rtx op0 = XVECEXP (reg, 0, 0);
+      if (REG_P (op0) || SUBREG_P (op0))
+	reg = op0;
+    }
+
+  /* If it isn't a register, use the defaults.  */
+  if (!REG_P (reg) && !SUBREG_P (reg))
+    iform = reg_addr[mode].default_insn_form;
+
+  else
+    {
+      unsigned int r = reg_or_subregno (reg);
+
+      /* If we have a pseudo, use the default instruction format.  */
+      if (r >= FIRST_PSEUDO_REGISTER)
+	iform = reg_addr[mode].default_insn_form;
+
+      /* If we have a hard register, use the address format of that hard
+	 register.  */
       else
-	return true;
+	{
+	  if (IN_RANGE (r, FIRST_GPR_REGNO, LAST_GPR_REGNO))
+	    iform = reg_addr[mode].insn_form[RELOAD_REG_GPR];
 
-      /* Return true if we must use a prefixed instruction.  */
-      return (value & mask) != 0;
+	  else if (IN_RANGE (r, FIRST_FPR_REGNO, LAST_FPR_REGNO))
+	    iform = reg_addr[mode].insn_form[RELOAD_REG_FPR];
+
+	  else if (IN_RANGE (r, FIRST_ALTIVEC_REGNO, LAST_ALTIVEC_REGNO))
+	    iform = reg_addr[mode].insn_form[RELOAD_REG_VMX];
+
+	  else
+	    gcc_unreachable ();
+	}
     }
 
-  return false;
+  return iform;
 }
 
 #if defined (HAVE_GAS_HIDDEN) && !TARGET_MACHO
Index: gcc/config/rs6000/rs6000.md
===================================================================
--- gcc/config/rs6000/rs6000.md	(revision 274173)
+++ gcc/config/rs6000/rs6000.md	(working copy)
@@ -252,6 +252,23 @@ 
 ;; Is copying of this instruction disallowed?
 (define_attr "cannot_copy" "no,yes" (const_string "no"))
 
+;; Enumeration of the PowerPC instruction formats.  We only list the
+;; instruction formats that are used by the code, and not every possible
+;; instruction format that the machine supports.
+
+;; The main use for this enumeration is to determine if a particular
+;; offsettable instruction has a valid offset field for a traditional
+;; instruction, or whether a prefixed instruction might be needed to hold the
+;; offset.  For DS/DQ format instructions, if we have an offset that has the
+;; bottom bits non-zero, we can use a prefixed instruction instead of pushing
+;; the offset to an index register.
+(define_enum "insn_form" [unknown	; Unknown format
+			  d		; Offset addressing uses 16 bits
+			  ds		; Offset addressing uses 14 bits
+			  dq		; Offset addressing uses 12 bits
+			  x		; Indexed addressing
+			  prefixed])	; Prefixed instruction
+
 ;; Length of the instruction (in bytes).
 (define_attr "length" "" (const_int 4))