V4, patch #1: Rework prefixed/pc-relative lookup
diff mbox series

Message ID 20190918234918.GA28484@ibm-toto.the-meissners.org
State New
Headers show
Series
  • V4, patch #1: Rework prefixed/pc-relative lookup
Related show

Commit Message

Michael Meissner Sept. 18, 2019, 11:49 p.m. UTC
This patch reworks the prefixed and pc-relative memory matching functions.

As I said in the intro message, I do not re-use the address mask bits in
reg_addr, but instead, I have a separate function that takes an address and
decodes it into the various different flavors (single register address, D-form
16-bit address, X-form indexed address, numeric 34-bit offset, local
pc-relative address, etc.).  The caller then decides whether the address
matches what they are looking for.

I have two enumerations that I added to this series:

    1)	insn_form: This is the address format (D, DS, DQ, X, etc.);

    2)	non_prefixed: This is a limited enum that just describes the format of
	the non-prefixed instruction to decide if an address needs to be
	prefixed or not.

Originally, I was trying to re-use the same insn_form enumeration for both the
output and the input to say what the traditional instruction uses, but I
ultimately separated them to make it clearer.

This is an infrastructure patch.  It needs the second patch to enable basic
pc-relative support.

I have done a bootstrap build with all of the patches applied, and there were
no regressions in the test suite.  After posting these patches, I will start a
job to build each set of patches in turn just to make sure there are no extra
warnings.

Can I commit this patch to the trunk?

2019-09-18  Michael Meissner  <meissner@linux.ibm.com>

	* config/rs6000/predicates.md (pcrel_address): Delete predicate.
	(pcrel_local_address): Replace pcrel_address predicate, use the
	new function address_to_insn_form.
	(pcrel_external_address): Replace with new implementation using
	address_to_insn_form..
	(prefixed_mem_operand): Delete predicate which is now unused.
	(pcrel_external_mem_operand): Delete predicate which is now
	unused.
	* config/rs6000/rs6000-protos.h (enum insn_form): New
	enumeration.
	(enum non_prefixed): New enumeration.
	(address_to_insn_form): New declaration.
	* config/rs6000/rs6000.c (print_operand_address): Check for either
	pc-relative local symbols or pc-relative external symbols.
	(mode_supports_prefixed_address_p): Delete, no longer used.
	(rs6000_prefixed_address_mode_p): Delete, no longer used.
	(address_to_insn_form): New function to decode an address format.

Comments

Segher Boessenkool Sept. 21, 2019, 1:29 a.m. UTC | #1
Hi Mike,

On Wed, Sep 18, 2019 at 07:49:18PM -0400, Michael Meissner wrote:
> This patch reworks the prefixed and pc-relative memory matching functions.

This mostly looks fine, thanks!  A few smaller things:


> 	(pcrel_external_address): Replace with new implementation using
> 	address_to_insn_form..

(Two dots is one too many).


> +(define_predicate "pcrel_local_or_external_address"
> +  (match_code "label_ref,symbol_ref,const")
> +{
> +  enum insn_form iform = address_to_insn_form (op, mode, NON_PREFIXED_DEFAULT);
> +  return (iform == INSN_FORM_PCREL_EXTERNAL || iform == INSN_FORM_PCREL_LOCAL);
> +})

(define_predicate "pcrel_local_or_external_address"
  (ior (match_operand 0 "pcrel_local_address")
       (match_operand 0 "pcrel_external_address")))

(or similar) please.  genpreds will generate effectively the same code
as you had automatically.


> +/* Different PowerPC instruction formats that are used by GCC.  There are
> +   various other instruction formats used by the PowerPC hardware, but the
> +   these formats are not currently used by GCC.  */
> +
> +enum insn_form {
> +  INSN_FORM_BAD,		/* Bad instruction format.  */
> +  INSN_FORM_BASE_REG,		/* Base register only.  */
> +  INSN_FORM_D,			/* Base register + 16-bit numeric offset.  */
> +  INSN_FORM_DS,			/* Base register + 14-bit offset + 00.  */
> +  INSN_FORM_DQ,			/* Base register + 12-bit offset + 0000.  */

It may be easier to describe DS-form as "D-form, with the offset aligned
to a (single) word" and DQ-form as "D-form, with the offset aligned to a
quad-word".  (Or what you do below; see below).

> +  INSN_FORM_X,			/* Base register + index register.  */
> +  INSN_FORM_UPDATE,		/* Address udpates base register.  */

(typo, "updates").

> +  INSN_FORM_LO_SUM,		/* Special offset instruction.  */

That's a somewhat lame description :-)  It's not really a separate form
insn anyway, hrm.  Can you come up with a better comment?  I have no
suggestions, so yeah maybe just keep it like you have.


> +  INSN_FORM_PREFIXED_NUMERIC,	/* Base register + 34 bit numeric offset.  */
> +  INSN_FORM_PCREL_LOCAL,	/* Pc-relative local symbol.  */
> +  INSN_FORM_PCREL_EXTERNAL	/* Pc-relative external symbol.  */
> +};

Either pc or PC please.  It's an initialism.


> +/* Instruction format for the non-prefixed version of a load or store.  This is
> +   used to determine if a 16-bit offset is valid to be used with a non-prefixed
> +   (traditional) instruction or if the bottom bits of the offset cannot be used
> +   with a DS or DQ instruction format, and GCC has to use a prefixed
> +   instruction for the load or store.  */
> +
> +enum non_prefixed {
> +  NON_PREFIXED_DEFAULT,		/* Use the default.  */
> +  NON_PREFIXED_D,		/* All 16-bits are valid.  */
> +  NON_PREFIXED_DS,		/* Bottom 2 bits must be 0.  */
> +  NON_PREFIXED_DQ,		/* Bottom 4 bits must be 0.  */
> +  NON_PREFIXED_X		/* No offset memory form exists.  */
> +};

Yeah the DS- and DQ-form descriptions here are nicer I think, thanks.

Maybe non_prefixed_form is a clearer name?  But it is longer of course.
You decide.


> +      if (SYMBOL_REF_P (x) && !SYMBOL_REF_LOCAL_P (x))
> +	fputs ("@got", file);
> +
>        fputs ("@pcrel", file);

I'd just use fprintf btw, GCC knows since decades to optimise that to
fputs, and it is easier to read IMO.  Not that fputs is so super bad,
but every little we do not have to think helps (helps us think, think
about more important matters!)


> +/* Given an address (ADDR), a mode (MODE), and what the format of the
> +   non-prefixed address (NON_PREFIXED_INSN) is, return the instruction format
> +   for the address.  */
> +
> +enum insn_form
> +address_to_insn_form (rtx addr,
> +		      machine_mode mode,
> +		      enum non_prefixed non_prefixed_insn)

non_prefixed_form, instead?


> +{
> +  rtx op0, op1;

You can declare these at first use.  Declaring things in multiple blocks
(so with shorter scopes) is a bit nicer.

> +  /* If we don't support offset addressing, make sure only indexed addressing
> +     is allowed.  We special case SDmode so that the register allocator does
> +     try to move SDmode through GPR registers, but instead uses the 32-bit
> +     integer read/write instructions for the floating point registers.  */

Does *not* try?

Read/write, do you mean load/store?  lfiwzx and friends?


> +  if (GET_CODE (addr) != PLUS)
> +    return GET_CODE (addr) == LO_SUM ? INSN_FORM_LO_SUM : INSN_FORM_BAD;

  if (GET_CODE (addr) == LO_SUM)
    return INSN_FORM_LO_SUM;

  if (GET_CODE (addr) != PLUS)
    return INSN_FORM_BAD;

(Avoid using the conditional operator if you can use an "if" statement
just as well; easier to read).

> +  op0 = XEXP (addr, 0);
> +  op1 = XEXP (addr, 1);
> +
> +  if (REG_P (op1) || SUBREG_P (op1))
> +    return INSN_FORM_X;

I think you should have checked op0 here as well?

> +  /* If it isn't pc-relative, check for 16-bit D/DS/DQ-form.  */
> +  if (!REG_P (op0) && !SUBREG_P (op0))
> +    return INSN_FORM_BAD;

(Instead of only later here).

Overall this looks like it will work nicely, thanks!


Segher
Michael Meissner Sept. 23, 2019, 5:48 p.m. UTC | #2
On Fri, Sep 20, 2019 at 08:29:04PM -0500, Segher Boessenkool wrote:
> Hi Mike,
> 
> On Wed, Sep 18, 2019 at 07:49:18PM -0400, Michael Meissner wrote:
> > This patch reworks the prefixed and pc-relative memory matching functions.
> 
> This mostly looks fine, thanks!  A few smaller things:
> 
> 
> > 	(pcrel_external_address): Replace with new implementation using
> > 	address_to_insn_form..
> 
> (Two dots is one too many).

Yes.

> > +(define_predicate "pcrel_local_or_external_address"
> > +  (match_code "label_ref,symbol_ref,const")
> > +{
> > +  enum insn_form iform = address_to_insn_form (op, mode, NON_PREFIXED_DEFAULT);
> > +  return (iform == INSN_FORM_PCREL_EXTERNAL || iform == INSN_FORM_PCREL_LOCAL);
> > +})
> 
> (define_predicate "pcrel_local_or_external_address"
>   (ior (match_operand 0 "pcrel_local_address")
>        (match_operand 0 "pcrel_external_address")))
> 
> (or similar) please.  genpreds will generate effectively the same code
> as you had automatically.

Ok.

> > +/* Different PowerPC instruction formats that are used by GCC.  There are
> > +   various other instruction formats used by the PowerPC hardware, but the
> > +   these formats are not currently used by GCC.  */
> > +
> > +enum insn_form {
> > +  INSN_FORM_BAD,		/* Bad instruction format.  */
> > +  INSN_FORM_BASE_REG,		/* Base register only.  */
> > +  INSN_FORM_D,			/* Base register + 16-bit numeric offset.  */
> > +  INSN_FORM_DS,			/* Base register + 14-bit offset + 00.  */
> > +  INSN_FORM_DQ,			/* Base register + 12-bit offset + 0000.  */
> 
> It may be easier to describe DS-form as "D-form, with the offset aligned
> to a (single) word" and DQ-form as "D-form, with the offset aligned to a
> quad-word".  (Or what you do below; see below).

Ok.

> > +  INSN_FORM_X,			/* Base register + index register.  */
> > +  INSN_FORM_UPDATE,		/* Address udpates base register.  */
> 
> (typo, "updates").
> 
> > +  INSN_FORM_LO_SUM,		/* Special offset instruction.  */
> 
> That's a somewhat lame description :-)  It's not really a separate form
> insn anyway, hrm.  Can you come up with a better comment?  I have no
> suggestions, so yeah maybe just keep it like you have.

None of my code uses INSN_FORM_LO_SUM, but I was adding it for completeness,
since it is a valid instruction format (as used within the compiler).

> 
> > +  INSN_FORM_PREFIXED_NUMERIC,	/* Base register + 34 bit numeric offset.  */
> > +  INSN_FORM_PCREL_LOCAL,	/* Pc-relative local symbol.  */
> > +  INSN_FORM_PCREL_EXTERNAL	/* Pc-relative external symbol.  */
> > +};
> 
> Either pc or PC please.  It's an initialism.

Yep.

> 
> > +/* Instruction format for the non-prefixed version of a load or store.  This is
> > +   used to determine if a 16-bit offset is valid to be used with a non-prefixed
> > +   (traditional) instruction or if the bottom bits of the offset cannot be used
> > +   with a DS or DQ instruction format, and GCC has to use a prefixed
> > +   instruction for the load or store.  */
> > +
> > +enum non_prefixed {
> > +  NON_PREFIXED_DEFAULT,		/* Use the default.  */
> > +  NON_PREFIXED_D,		/* All 16-bits are valid.  */
> > +  NON_PREFIXED_DS,		/* Bottom 2 bits must be 0.  */
> > +  NON_PREFIXED_DQ,		/* Bottom 4 bits must be 0.  */
> > +  NON_PREFIXED_X		/* No offset memory form exists.  */
> > +};
> 
> Yeah the DS- and DQ-form descriptions here are nicer I think, thanks.
> 
> Maybe non_prefixed_form is a clearer name?  But it is longer of course.
> You decide.

That is fine.

> 
> > +      if (SYMBOL_REF_P (x) && !SYMBOL_REF_LOCAL_P (x))
> > +	fputs ("@got", file);
> > +
> >        fputs ("@pcrel", file);
> 
> I'd just use fprintf btw, GCC knows since decades to optimise that to
> fputs, and it is easier to read IMO.  Not that fputs is so super bad,
> but every little we do not have to think helps (helps us think, think
> about more important matters!)

Ok.  It is just the many years of doing C code before GCC did the optimization
is ingrained on me.
 
> > +/* Given an address (ADDR), a mode (MODE), and what the format of the
> > +   non-prefixed address (NON_PREFIXED_INSN) is, return the instruction format
> > +   for the address.  */
> > +
> > +enum insn_form
> > +address_to_insn_form (rtx addr,
> > +		      machine_mode mode,
> > +		      enum non_prefixed non_prefixed_insn)
> 
> non_prefixed_form, instead?
> 
> 
> > +{
> > +  rtx op0, op1;
> 
> You can declare these at first use.  Declaring things in multiple blocks
> (so with shorter scopes) is a bit nicer.
> 
> > +  /* If we don't support offset addressing, make sure only indexed addressing
> > +     is allowed.  We special case SDmode so that the register allocator does
> > +     try to move SDmode through GPR registers, but instead uses the 32-bit
> > +     integer read/write instructions for the floating point registers.  */
> 
> Does *not* try?
> 
> Read/write, do you mean load/store?  lfiwzx and friends?
> 
> 
> > +  if (GET_CODE (addr) != PLUS)
> > +    return GET_CODE (addr) == LO_SUM ? INSN_FORM_LO_SUM : INSN_FORM_BAD;
> 
>   if (GET_CODE (addr) == LO_SUM)
>     return INSN_FORM_LO_SUM;
> 
>   if (GET_CODE (addr) != PLUS)
>     return INSN_FORM_BAD;
> 
> (Avoid using the conditional operator if you can use an "if" statement
> just as well; easier to read).
> 
> > +  op0 = XEXP (addr, 0);
> > +  op1 = XEXP (addr, 1);
> > +
> > +  if (REG_P (op1) || SUBREG_P (op1))
> > +    return INSN_FORM_X;
> 
> I think you should have checked op0 here as well?

Yes probably.  Too many years of writing secondary reload patterns where the
first argument can be funny during the reload phase. :-)

> > +  /* If it isn't pc-relative, check for 16-bit D/DS/DQ-form.  */
> > +  if (!REG_P (op0) && !SUBREG_P (op0))
> > +    return INSN_FORM_BAD;
> 
> (Instead of only later here).
> 
> Overall this looks like it will work nicely, thanks!

Did you want the patch updated now, or should I wait for you to comment on the
next set of patches?

Patch
diff mbox series

Index: gcc/config/rs6000/predicates.md
===================================================================
--- gcc/config/rs6000/predicates.md	(revision 275903)
+++ gcc/config/rs6000/predicates.md	(working copy)
@@ -1625,82 +1625,7 @@  (define_predicate "small_toc_ref"
   return GET_CODE (op) == UNSPEC && XINT (op, 1) == UNSPEC_TOCREL;
 })
 
-;; Return true if the operand is a pc-relative address.
-(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 true if the operand is an external symbol whose address can be loaded
-;; into a register using:
-;;	PLD reg,label@pcrel@got
-;;
-;; The linker will either optimize this to either a PADDI if the label is
-;; defined locally in another module or a PLD of the address if the label is
-;; defined in another module.
-
-(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 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 1 if op is a memory operand to an external variable when we
-;; support pc-relative addressing and the PCREL_OPT relocation to
-;; optimize references to it.
-(define_predicate "pcrel_external_mem_operand"
-  (match_code "mem")
-{
-  return pcrel_external_address (XEXP (op, 0), Pmode);
-})
-
+
 ;; Match the first insn (addis) in fusing the combination of addis and loads to
 ;; GPR registers on power8.
 (define_predicate "fusion_gpr_addis"
@@ -1857,3 +1782,31 @@  (define_predicate "fusion_addis_mem_comb
 
   return 0;
 })
+
+
+;; Return true if the operand is a pc-relative address to a local symbol or a
+;; label that can be used directly in a memory operation.
+(define_predicate "pcrel_local_address"
+  (match_code "label_ref,symbol_ref,const")
+{
+  enum insn_form iform = address_to_insn_form (op, mode, NON_PREFIXED_DEFAULT);
+  return iform == INSN_FORM_PCREL_LOCAL;
+})
+
+;; Return true if the operand is an external symbol whose address can be loaded
+;; into a register.
+(define_predicate "pcrel_external_address"
+  (match_code "symbol_ref,const")
+{
+  enum insn_form iform = address_to_insn_form (op, mode, NON_PREFIXED_DEFAULT);
+  return iform == INSN_FORM_PCREL_EXTERNAL;
+})
+
+;; Return true if the address is pc-relative and the symbol is either local or
+;; external.
+(define_predicate "pcrel_local_or_external_address"
+  (match_code "label_ref,symbol_ref,const")
+{
+  enum insn_form iform = address_to_insn_form (op, mode, NON_PREFIXED_DEFAULT);
+  return (iform == INSN_FORM_PCREL_EXTERNAL || iform == INSN_FORM_PCREL_LOCAL);
+})
Index: gcc/config/rs6000/rs6000-protos.h
===================================================================
--- gcc/config/rs6000/rs6000-protos.h	(revision 275903)
+++ gcc/config/rs6000/rs6000-protos.h	(working copy)
@@ -154,7 +154,41 @@  extern align_flags rs6000_loop_align (rt
 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);
+
+/* Different PowerPC instruction formats that are used by GCC.  There are
+   various other instruction formats used by the PowerPC hardware, but the
+   these formats are not currently used by GCC.  */
+
+enum insn_form {
+  INSN_FORM_BAD,		/* Bad instruction format.  */
+  INSN_FORM_BASE_REG,		/* Base register only.  */
+  INSN_FORM_D,			/* Base register + 16-bit numeric offset.  */
+  INSN_FORM_DS,			/* Base register + 14-bit offset + 00.  */
+  INSN_FORM_DQ,			/* Base register + 12-bit offset + 0000.  */
+  INSN_FORM_X,			/* Base register + index register.  */
+  INSN_FORM_UPDATE,		/* Address udpates base register.  */
+  INSN_FORM_LO_SUM,		/* Special offset instruction.  */
+  INSN_FORM_PREFIXED_NUMERIC,	/* Base register + 34 bit numeric offset.  */
+  INSN_FORM_PCREL_LOCAL,	/* Pc-relative local symbol.  */
+  INSN_FORM_PCREL_EXTERNAL	/* Pc-relative external symbol.  */
+};
+
+/* Instruction format for the non-prefixed version of a load or store.  This is
+   used to determine if a 16-bit offset is valid to be used with a non-prefixed
+   (traditional) instruction or if the bottom bits of the offset cannot be used
+   with a DS or DQ instruction format, and GCC has to use a prefixed
+   instruction for the load or store.  */
+
+enum non_prefixed {
+  NON_PREFIXED_DEFAULT,		/* Use the default.  */
+  NON_PREFIXED_D,		/* All 16-bits are valid.  */
+  NON_PREFIXED_DS,		/* Bottom 2 bits must be 0.  */
+  NON_PREFIXED_DQ,		/* Bottom 4 bits must be 0.  */
+  NON_PREFIXED_X		/* No offset memory form exists.  */
+};
+
+extern enum insn_form address_to_insn_form (rtx, machine_mode,
+					    enum non_prefixed);
 #endif /* RTX_CODE */
 
 #ifdef TREE_CODE
Index: gcc/config/rs6000/rs6000.c
===================================================================
--- gcc/config/rs6000/rs6000.c	(revision 275903)
+++ gcc/config/rs6000/rs6000.c	(working copy)
@@ -13079,7 +13079,7 @@  print_operand_address (FILE *file, rtx x
     fprintf (file, "0(%s)", reg_names[ REGNO (x) ]);
 
   /* Is it a pc-relative address?  */
-  else if (pcrel_address (x, Pmode))
+  else if (TARGET_PCREL && pcrel_local_or_external_address (x, VOIDmode))
     {
       HOST_WIDE_INT offset;
 
@@ -13099,6 +13099,9 @@  print_operand_address (FILE *file, rtx x
       if (offset)
 	fprintf (file, "%+" PRId64, offset);
 
+      if (SYMBOL_REF_P (x) && !SYMBOL_REF_LOCAL_P (x))
+	fputs ("@got", file);
+
       fputs ("@pcrel", file);
     }
   else if (SYMBOL_REF_P (x) || GET_CODE (x) == CONST
@@ -13584,71 +13587,6 @@  rs6000_pltseq_template (rtx *operands, i
   return str;
 }
 #endif
-
-/* 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.  */
-
-static bool
-mode_supports_prefixed_address_p (machine_mode mode)
-{
-  return mode == VOIDmode;
-}
-
-/* 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)
-{
-  if (!TARGET_PREFIXED_ADDR || !mode_supports_prefixed_address_p (mode))
-    return false;
-
-  /* Check for PC-relative addresses.  */
-  if (pcrel_address (addr, Pmode))
-    return true;
-
-  /* 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.  */
-  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))
-	return false;
-
-      HOST_WIDE_INT value = INTVAL (op1);
-      if (!SIGNED_34BIT_OFFSET_P (value))
-	return false;
-
-      /* Offset larger than 16-bits?  */
-      if (!SIGNED_16BIT_OFFSET_P (value))
-	return true;
-
-      /* DQ instruction (bottom 4 bits must be 0) for vectors.  */
-      HOST_WIDE_INT mask;
-      if (GET_MODE_SIZE (mode) >= 16)
-	mask = 15;
-
-      /* 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;
-
-      /* QImode/HImode has no restrictions.  */
-      else
-	return true;
-
-      /* Return true if we must use a prefixed instruction.  */
-      return (value & mask) != 0;
-    }
-
-  return false;
-}
 
 #if defined (HAVE_GAS_HIDDEN) && !TARGET_MACHO
 /* Emit an assembler directive to set symbol visibility for DECL to
@@ -24627,6 +24565,158 @@  rs6000_pcrel_p (struct function *fn)
   return rs6000_fndecl_pcrel_p (fn->decl);
 }
 
+
+/* Given an address (ADDR), a mode (MODE), and what the format of the
+   non-prefixed address (NON_PREFIXED_INSN) is, return the instruction format
+   for the address.  */
+
+enum insn_form
+address_to_insn_form (rtx addr,
+		      machine_mode mode,
+		      enum non_prefixed non_prefixed_insn)
+{
+  rtx op0, op1;
+
+  /* Single register is easy.  */
+  if (REG_P (addr) || SUBREG_P (addr))
+    return INSN_FORM_BASE_REG;
+
+  /* If we don't support offset addressing, make sure only indexed addressing
+     is allowed.  We special case SDmode so that the register allocator does
+     try to move SDmode through GPR registers, but instead uses the 32-bit
+     integer read/write instructions for the floating point registers.  */
+  if (non_prefixed_insn == NON_PREFIXED_X || mode == SDmode)
+    {
+      if (GET_CODE (addr) != PLUS)
+	return INSN_FORM_BAD;
+
+      op0 = XEXP (addr, 0);
+      op1 = XEXP (addr, 1);
+      if (!REG_P (op0) && !SUBREG_P (op0))
+	return INSN_FORM_BAD;
+
+      if (!REG_P (op1) && !SUBREG_P (op1))
+	return INSN_FORM_BAD;
+
+      return INSN_FORM_X;
+    }
+
+  /* Deal with update forms.  */
+  if (GET_RTX_CLASS (GET_CODE (addr)) == RTX_AUTOINC)
+    return INSN_FORM_UPDATE;
+
+  /* Handle pc-relative symbols and labels.  Check for both local and external
+     symbols.  Assume labels are always local.  */
+  if (TARGET_PCREL)
+    {
+      if (SYMBOL_REF_P (addr))
+	return (SYMBOL_REF_LOCAL_P (addr)
+		? INSN_FORM_PCREL_LOCAL
+		: INSN_FORM_PCREL_EXTERNAL);
+
+      if (LABEL_REF_P (addr))
+	return INSN_FORM_PCREL_LOCAL;
+    }
+
+  /* Check whether this is an offsettable address.  Deal with LO_SUM addresses
+     used with TOC and 32-bit addressing and with indexed addresses.  */
+  if (GET_CODE (addr) == CONST)
+    addr = XEXP (addr, 0);
+
+  if (GET_CODE (addr) != PLUS)
+    return GET_CODE (addr) == LO_SUM ? INSN_FORM_LO_SUM : INSN_FORM_BAD;
+
+  op0 = XEXP (addr, 0);
+  op1 = XEXP (addr, 1);
+
+  if (REG_P (op1) || SUBREG_P (op1))
+    return INSN_FORM_X;
+
+  if (!CONST_INT_P (op1))
+    return INSN_FORM_BAD;
+
+  HOST_WIDE_INT offset = INTVAL (op1);
+  if (!SIGNED_34BIT_OFFSET_P (offset))
+    return INSN_FORM_BAD;
+
+  /* Check for local and external pc-relative addresses.  Labels are always
+     local.  */
+  if (TARGET_PCREL)
+    {
+      if (SYMBOL_REF_P (op0))
+	return (SYMBOL_REF_LOCAL_P (op0)
+		? INSN_FORM_PCREL_LOCAL
+		: INSN_FORM_PCREL_EXTERNAL);
+
+      if (LABEL_REF_P (op0))
+	return INSN_FORM_PCREL_LOCAL;
+    }
+
+  /* If it isn't pc-relative, check for 16-bit D/DS/DQ-form.  */
+  if (!REG_P (op0) && !SUBREG_P (op0))
+    return INSN_FORM_BAD;
+
+  /* Large offsets must be prefixed.  */
+  if (!SIGNED_16BIT_OFFSET_P (offset))
+    return (TARGET_PREFIXED_ADDR
+	    ? INSN_FORM_PREFIXED_NUMERIC
+	    : INSN_FORM_BAD);
+
+  /* 16-bit offset, see what default instruction format to use.  */
+  if (non_prefixed_insn == NON_PREFIXED_DEFAULT)
+    {
+      unsigned size = GET_MODE_SIZE (mode);
+
+      /* On 64-bit systems, assume 64-bit integers need to use DS form
+	 addresses.  VSX vectors need to use DQ form addresses.  */
+      if (TARGET_POWERPC64 && size >= 8 && GET_MODE_CLASS (mode) == MODE_INT)
+	non_prefixed_insn = NON_PREFIXED_DS;
+
+      else if (TARGET_VSX && size >= 16
+	       && ALTIVEC_OR_VSX_VECTOR_MODE (mode))
+	non_prefixed_insn = NON_PREFIXED_DQ;
+
+      else
+	non_prefixed_insn = NON_PREFIXED_D;
+    }
+
+  /* Classify the D/DS/DQ-form addresses.  */
+  switch (non_prefixed_insn)
+    {
+      /* Instruction format D, all 16 bits are valid.  */
+    case NON_PREFIXED_D:
+      return INSN_FORM_D;
+
+      /* Instruction format DS, bottom 2 bits must be 0.  */
+    case NON_PREFIXED_DS:
+      if ((offset & 3) == 0)
+	return INSN_FORM_DS;
+
+      else if (TARGET_PREFIXED_ADDR)
+	return INSN_FORM_PREFIXED_NUMERIC;
+
+      else
+	return INSN_FORM_BAD;
+
+      /* Instruction format DQ, bottom 4 bits must be 0.  */
+    case NON_PREFIXED_DQ:
+      if ((offset & 15) == 0)
+	return INSN_FORM_DQ;
+
+      else if (TARGET_PREFIXED_ADDR)
+	return INSN_FORM_PREFIXED_NUMERIC;
+
+      else
+	return INSN_FORM_BAD;
+
+    default:
+      break;
+    }
+
+  return INSN_FORM_BAD;
+}
+
+
 #ifdef HAVE_GAS_HIDDEN
 # define USE_HIDDEN_LINKONCE 1
 #else