, PowerPC, Patch #9, Refine calculation of whether an address offset is d-form, ds-form, or dq-form
diff mbox series

Message ID 20190711194402.GA24265@ibm-toto.the-meissners.org
State New
Headers show
Series
  • , PowerPC, Patch #9, Refine calculation of whether an address offset is d-form, ds-form, or dq-form
Related show

Commit Message

Michael Meissner July 11, 2019, 7:44 p.m. UTC
As I mentioned in patch #8, I needed to refine the calculation of what type of
address offset it used on the PowerPC for particular loads and stores.

The motivation is that the new prefixed instructions that future processors
may support have improved instruction encodings, and the prefixed form of the
instruction do not need the bottom 2 bits clear for traditional DS-form
instructions or the bottom 4 bits clear for traditional DQ-form instructions.

This means if you have a load where the offset it not aligned:

	LD rx,3(base)

currently the compiler will have to generate code like:

	LI tmp,3
	LDX rx,tmp,base

With prefixed instructions, the compiler can instead generate:

	PLD rx,3(base)

The original implementation just using the mode does not work in a lot of
cases, because you don't know the context of the use.

For example, a LWZ instruction uses a d-form load (all 16 bits are available),
while a LWA instruction uses a ds-form load (bottom 2 bits must be 0).

Another example is loading up a scalar floating value.  If we are loading to a
traditional FPR, the LFD instruction is a d-form instruction, while if we are
loading to a traditional Altivec register, the LXSD instruction is a ds-form
instruction.

I have built the compiler with these patches installed on a little endian
power8 system.  There were no regressions in the test suite.  Can I install
this patch into the FSF trunk?

2019-07-11  Michael Meissner  <meissner@linux.ibm.com>

	* config/rs6000/rs6000-protos.h (enum rs6000_offset_format): New
	enumeration to describe the addressing offset format.
	(reg_to_offset_format): New declaration.
	(rs6000_prefixed_address_format): New declaration.
	* config/rs6000/rs6000.c (struct rs6000_reg_addr): Add default
	address offset format field.
	(mode_supports_prefixed_address_p): Move function higher.
	(initialize_offset_formats): New function to determine the default
	address format for each mode.
	(rs6000_init_hard_regno_mode_ok): Initialize the address formats
	for each mode.
	(quad_address_p): Add support for prefixed instructions.
	(mem_operand_gpr): Add support for prefixed instructions.
	(mem_operand_ds_form): Add support for prefixed instructions.
	(rs6000_prefixed_address_format): New function to validate an
	address given an address format.
	(rs6000_prefixed_address_mode): Rewrite to used saved default
	address mode format.
	(reg_to_offset_format): New function to determine what address
	format to use for a particular register.

Comments

Segher Boessenkool July 12, 2019, 4:49 p.m. UTC | #1
On Thu, Jul 11, 2019 at 03:44:02PM -0400, Michael Meissner wrote:
> --- gcc/config/rs6000/rs6000-protos.h	(revision 273371)
> +++ gcc/config/rs6000/rs6000-protos.h	(working copy)
> @@ -154,6 +154,18 @@ 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);
> +
> +/* Enumeration to describe the offsettable address formats of PowerPC memory
> +   instructions.  */
> +enum rs6000_offset_format {
> +  OFFSET_FORMAT_NONE,		/* No offset format is available.  */
> +  OFFSET_FORMAT_D,		/* All 16-bits are available.  */
> +  OFFSET_FORMAT_DS,		/* Bottom 2 bits must be 0.  */
> +  OFFSET_FORMAT_DQ		/* Bottom 4 bits must be 0.  */
> +};

It's called "instruction form", let's not call it "address format".
"Form" is shorter and more correct, as well.

> +/* 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;
> +}

s/Helper function to return/Return/

It's not obvious from this comment that "mode" is the mode of the thing
that is loaded or stored (and not the address of the datum, or something).
The VOIDmode comment wants to say that you use VOIDmode with this function
when you aren't actually loading or storing anything, just taking an
address?

The last line shouldn't be here: predictions will never be true ;-)

What qualifies as a "prefixed load/store" for this?

> +/* Initiailize the table of offset address formats.  Unfortunately we do not

(Typo)

> +  /* Set offset format for some types based on the switches.  */

Many of those are not clear why you allow or do not allow certain forms,
or what each is meant to be used for.  For example:

> +  enum rs6000_offset_format format_gpr_64bit
> +    = (TARGET_POWERPC64) ? OFFSET_FORMAT_DS : OFFSET_FORMAT_D;

Why does non-powerpc64 allow D?  It cannot even do 64-bit GPR accesses
*at all*!

> +  enum rs6000_offset_format format_gpr_128bit
> +    = (TARGET_QUAD_MEMORY) ? OFFSET_FORMAT_DQ : format_gpr_64bit;

128-bit GPRs do not exist, either.

> +  enum rs6000_offset_format format_fpr_64bit
> +    = (TARGET_SOFT_FLOAT) ? OFFSET_FORMAT_D : format_gpr_64bit;

Why does soft float allow more?  Is that even tested?

> +  enum rs6000_offset_format format_vector
> +    = (TARGET_P9_VECTOR) ? OFFSET_FORMAT_DQ : OFFSET_FORMAT_NONE;

Erm, what?  This could use a comment.  Many insns are DS btw, not DQ.

> +  enum rs6000_offset_format format_tf
> +    = (TARGET_IEEEQUAD) ? format_vector : format_fpr_64bit;

TF is either IF or KF.

> +  /* Small integers.  */
> +  reg_addr[E_QImode].offset_format = OFFSET_FORMAT_D;
> +  reg_addr[E_HImode].offset_format = OFFSET_FORMAT_D;
> +  reg_addr[E_CQImode].offset_format = OFFSET_FORMAT_D;
> +  reg_addr[E_CHImode].offset_format = OFFSET_FORMAT_D;

Do we handle CQI and CHI anywhere else?

> +  /* While DImode can be loaded into a FPR register with LFD (d-form), the
> +     normal use is to use a GPR (ds-form in 64-bit).  */
> +  reg_addr[E_DImode].offset_format = format_gpr_64bit;
> +  reg_addr[E_CDImode].offset_format = format_gpr_64bit;

I'm not sure that comment helps anything -- I don't know what it is trying
to say?

> +  /* Condition registers (uses LWZ/STW).  */
> +  reg_addr[E_CCmode].offset_format = OFFSET_FORMAT_D;
> +  reg_addr[E_CCUNSmode].offset_format = OFFSET_FORMAT_D;
> +  reg_addr[E_CCFPmode].offset_format = OFFSET_FORMAT_D;
> +  reg_addr[E_CCEQmode].offset_format = OFFSET_FORMAT_D;

Okay, why do we make the "allowed address form" function support modes
that we have no load/store insns for?!

> +  /* 128-bit integers.  */
> +  if (TARGET_POWERPC64)
> +    {
> +      reg_addr[E_TImode].offset_format = format_vector;

TI is not normally / always / often in vectors.

So what is this really doing?  Preferred form for accesses in a certain
mode?

> +  /* Initial the mapping of mode to offset formats.  */
> +  initialize_offset_formats ();

That comment is redundant.  (Also, spelling).

> +  /* Is this a valid prefixed address?  If the bottom four bits of the offset
> +     are non-zero, we could use a prefixed instruction (which does not have the
> +     DQ-form constraint that the traditional instruction had) instead of
> +     forcing the unaligned offset to a GPR.  */
> +  if (mode_supports_prefixed_address_p (mode)
> +      && rs6000_prefixed_address_format (addr, OFFSET_FORMAT_DQ))
> +    return true;

The comment says something different than the code does?

> +      switch (format)
> +	{
> +	default:
> +	  gcc_unreachable ();

First not but the end at default go should.

> +/* Return true if ADDR is a valid prefixed memory address that uses mode
> +   MODE.  */
> +
> +bool
> +rs6000_prefixed_address_mode (rtx addr, machine_mode mode)

Needs _p or *_is_* or whatever.  The name should not suggest it returns
a mode, since it doesn't.


Please change format to form, and change all names and comments so it is
clear what things do, and that it makes *sense*.  It might well work, but
I cannot make heads or tails of it.  Maybe if I would spend another day
on it?


Segher
Michael Meissner July 16, 2019, 5:29 p.m. UTC | #2
On Fri, Jul 12, 2019 at 11:49:51AM -0500, Segher Boessenkool wrote:
> Many of those are not clear why you allow or do not allow certain forms,
> or what each is meant to be used for.  For example:
> 
> > +  enum rs6000_offset_format format_gpr_64bit
> > +    = (TARGET_POWERPC64) ? OFFSET_FORMAT_DS : OFFSET_FORMAT_D;
> 
> Why does non-powerpc64 allow D?  It cannot even do 64-bit GPR accesses
> *at all*!
> 

A single instruction cannot do 64-bit GPR loads, BUT the tests are needed for
register allocation before the instruction is split.  So in 32-bit mode, load
of DImode is split into 2 loads of SImode, which is D-format.

> > +  enum rs6000_offset_format format_gpr_128bit
> > +    = (TARGET_QUAD_MEMORY) ? OFFSET_FORMAT_DQ : format_gpr_64bit;
> 
> 128-bit GPRs do not exist, either.

Yes they do during register allocation, and are split later before final.

> > +  enum rs6000_offset_format format_fpr_64bit
> > +    = (TARGET_SOFT_FLOAT) ? OFFSET_FORMAT_D : format_gpr_64bit;
> 
> Why does soft float allow more?  Is that even tested?

Because on a 32-bit system, soft float DFmode goes into 2 GPRs that are D-format.
On a 64-bit system, soft float DFmode goes into 1 GPR that is DS-format.

> > +  enum rs6000_offset_format format_vector
> > +    = (TARGET_P9_VECTOR) ? OFFSET_FORMAT_DQ : OFFSET_FORMAT_NONE;
> 
> Erm, what?  This could use a comment.  Many insns are DS btw, not DQ.
> 
> > +  enum rs6000_offset_format format_tf
> > +    = (TARGET_IEEEQUAD) ? format_vector : format_fpr_64bit;
> 
> TF is either IF or KF.

Yes, and when TF is KFmode, it is a vector (DQ-format), but when it is IFmode,
it is a pair of DFmode values (i.e. typically D-format, but it can be DS-format
when loading into the traditional Altivec registers.

This is in part why I think the mode format is broken.  But, until register
allocation, we have to guess and that is what this function is trying to do.
It is used by the legitimate address functions and the constraints.

But then I've felt that the current legitimate address stuff has been broken
ever since I started working on GCC 1.37 because you don't have the context of
whether it is a load or store, and don't have the register loaded to or stored
after register allocation.  But it is what it is.

> 
> > +  /* Small integers.  */
> > +  reg_addr[E_QImode].offset_format = OFFSET_FORMAT_D;
> > +  reg_addr[E_HImode].offset_format = OFFSET_FORMAT_D;
> > +  reg_addr[E_CQImode].offset_format = OFFSET_FORMAT_D;
> > +  reg_addr[E_CHImode].offset_format = OFFSET_FORMAT_D;
> 
> Do we handle CQI and CHI anywhere else?

It depends.  It may use this format early in the RTL generation until the
complex types are split.

> 
> > +  /* While DImode can be loaded into a FPR register with LFD (d-form), the
> > +     normal use is to use a GPR (ds-form in 64-bit).  */
> > +  reg_addr[E_DImode].offset_format = format_gpr_64bit;
> > +  reg_addr[E_CDImode].offset_format = format_gpr_64bit;
> 
> I'm not sure that comment helps anything -- I don't know what it is trying
> to say?

Normally when you use a DImode, it goes into a GPR (i.e. DS-format in 64-bit).
However, there are times when you want to use a DImode in a FPR, when you can
use D-format.  As an example, the test dimode_off.c hand crafts loads and
stores with odd offsets.  We had to de-tune movdi so that the register
allocator would not load the value into a FPR and do a direct move (or do a
direct move and store from the FPR).

> > +  /* Condition registers (uses LWZ/STW).  */
> > +  reg_addr[E_CCmode].offset_format = OFFSET_FORMAT_D;
> > +  reg_addr[E_CCUNSmode].offset_format = OFFSET_FORMAT_D;
> > +  reg_addr[E_CCFPmode].offset_format = OFFSET_FORMAT_D;
> > +  reg_addr[E_CCEQmode].offset_format = OFFSET_FORMAT_D;
> 
> Okay, why do we make the "allowed address form" function support modes
> that we have no load/store insns for?!

But we do have load and store functions for the CC* modes.  See movcc_internal1.

> > +  /* 128-bit integers.  */
> > +  if (TARGET_POWERPC64)
> > +    {
> > +      reg_addr[E_TImode].offset_format = format_vector;
> 
> TI is not normally / always / often in vectors.

Not now, but it will be.

> So what is this really doing?  Preferred form for accesses in a certain
> mode?

Basically the whole thing is a guess of what the valid offset format for a mode
is.  This is needed for the legitimate address functions
(i.e. rs6000_legitimate_address_p, rs6000_legitimate_offset_address_p) to
validate whether we allow odd addresses or not.

> > +  /* Initial the mapping of mode to offset formats.  */
> > +  initialize_offset_formats ();
> 
> That comment is redundant.  (Also, spelling).
> 
> > +  /* Is this a valid prefixed address?  If the bottom four bits of the offset
> > +     are non-zero, we could use a prefixed instruction (which does not have the
> > +     DQ-form constraint that the traditional instruction had) instead of
> > +     forcing the unaligned offset to a GPR.  */
> > +  if (mode_supports_prefixed_address_p (mode)
> > +      && rs6000_prefixed_address_format (addr, OFFSET_FORMAT_DQ))
> > +    return true;
> 
> The comment says something different than the code does?
> 
> > +      switch (format)
> > +	{
> > +	default:
> > +	  gcc_unreachable ();
> 
> First not but the end at default go should.
> 
> > +/* Return true if ADDR is a valid prefixed memory address that uses mode
> > +   MODE.  */
> > +
> > +bool
> > +rs6000_prefixed_address_mode (rtx addr, machine_mode mode)
> 
> Needs _p or *_is_* or whatever.  The name should not suggest it returns
> a mode, since it doesn't.
> 
> 
> Please change format to form, and change all names and comments so it is
> clear what things do, and that it makes *sense*.  It might well work, but
> I cannot make heads or tails of it.  Maybe if I would spend another day
> on it?
> 
> 
> Segher
>
Segher Boessenkool July 16, 2019, 9:49 p.m. UTC | #3
%s/format/form/g

On Tue, Jul 16, 2019 at 01:29:50PM -0400, Michael Meissner wrote:
> On Fri, Jul 12, 2019 at 11:49:51AM -0500, Segher Boessenkool wrote:
> > Many of those are not clear why you allow or do not allow certain forms,
> > or what each is meant to be used for.  For example:
> > 
> > > +  enum rs6000_offset_format format_gpr_64bit
> > > +    = (TARGET_POWERPC64) ? OFFSET_FORMAT_DS : OFFSET_FORMAT_D;
> > 
> > Why does non-powerpc64 allow D?  It cannot even do 64-bit GPR accesses
> > *at all*!
> 
> A single instruction cannot do 64-bit GPR loads, BUT the tests are needed for
> register allocation before the instruction is split.  So in 32-bit mode, load
> of DImode is split into 2 loads of SImode, which is D-format.

Then the variable is misnamed?

> > > +  enum rs6000_offset_format format_gpr_128bit
> > > +    = (TARGET_QUAD_MEMORY) ? OFFSET_FORMAT_DQ : format_gpr_64bit;
> > 
> > 128-bit GPRs do not exist, either.
> 
> Yes they do during register allocation, and are split later before final.

You mean that you have a TImode datum in two GPRs.  There are no 128-bit
GPRs.

The *two* GPRs should be part of the name, that would improve it?

> This is in part why I think the mode format is broken.

The alternative is picking at expand time what register set to use where.
That is worse.  We want to use vectors often, but certainly not always.

> But then I've felt that the current legitimate address stuff has been broken
> ever since I started working on GCC 1.37 because you don't have the context of
> whether it is a load or store, and don't have the register loaded to or stored
> after register allocation.  But it is what it is.

That is one of reload's big jobs.  If you have a better design...  :-)

> > > +  /* Small integers.  */
> > > +  reg_addr[E_QImode].offset_format = OFFSET_FORMAT_D;
> > > +  reg_addr[E_HImode].offset_format = OFFSET_FORMAT_D;
> > > +  reg_addr[E_CQImode].offset_format = OFFSET_FORMAT_D;
> > > +  reg_addr[E_CHImode].offset_format = OFFSET_FORMAT_D;
> > 
> > Do we handle CQI and CHI anywhere else?
> 
> It depends.  It may use this format early in the RTL generation until the
> complex types are split.

Is that used *during* expand?  Blergh.

> > > +  /* While DImode can be loaded into a FPR register with LFD (d-form), the
> > > +     normal use is to use a GPR (ds-form in 64-bit).  */
> > > +  reg_addr[E_DImode].offset_format = format_gpr_64bit;
> > > +  reg_addr[E_CDImode].offset_format = format_gpr_64bit;
> > 
> > I'm not sure that comment helps anything -- I don't know what it is trying
> > to say?
> 
> Normally when you use a DImode, it goes into a GPR (i.e. DS-format in 64-bit).
> However, there are times when you want to use a DImode in a FPR, when you can
> use D-format.  As an example, the test dimode_off.c hand crafts loads and
> stores with odd offsets.  We had to de-tune movdi so that the register
> allocator would not load the value into a FPR and do a direct move (or do a
> direct move and store from the FPR).

That doesn't explain the comment to me, sorry :-/

> > > +  /* Condition registers (uses LWZ/STW).  */
> > > +  reg_addr[E_CCmode].offset_format = OFFSET_FORMAT_D;
> > > +  reg_addr[E_CCUNSmode].offset_format = OFFSET_FORMAT_D;
> > > +  reg_addr[E_CCFPmode].offset_format = OFFSET_FORMAT_D;
> > > +  reg_addr[E_CCEQmode].offset_format = OFFSET_FORMAT_D;
> > 
> > Okay, why do we make the "allowed address form" function support modes
> > that we have no load/store insns for?!
> 
> But we do have load and store functions for the CC* modes.  See movcc_internal1.

I meant machine instructions.

movcc_internal has nothing to load/store anything in a CC reg from/to
memory, either.

Do we really have to care what might happen *after* we put something in
a GPR?

> > > +  /* 128-bit integers.  */
> > > +  if (TARGET_POWERPC64)
> > > +    {
> > > +      reg_addr[E_TImode].offset_format = format_vector;
> > 
> > TI is not normally / always / often in vectors.
> 
> Not now, but it will be.

1) Erm.
2) No, you cannot break p8 and p9 performance like this.

> Basically the whole thing is a guess of what the valid offset format for a mode
> is.  This is needed for the legitimate address functions
> (i.e. rs6000_legitimate_address_p, rs6000_legitimate_offset_address_p) to
> validate whether we allow odd addresses or not.

This needs to be made much more explicit, then.  "preferred_form" or
something.

Then suddenly it will probably start to make sense :-)


Segher

Patch
diff mbox series

Index: gcc/config/rs6000/rs6000-protos.h
===================================================================
--- gcc/config/rs6000/rs6000-protos.h	(revision 273371)
+++ gcc/config/rs6000/rs6000-protos.h	(working copy)
@@ -154,6 +154,18 @@  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);
+
+/* Enumeration to describe the offsettable address formats of PowerPC memory
+   instructions.  */
+enum rs6000_offset_format {
+  OFFSET_FORMAT_NONE,		/* No offset format is available.  */
+  OFFSET_FORMAT_D,		/* All 16-bits are available.  */
+  OFFSET_FORMAT_DS,		/* Bottom 2 bits must be 0.  */
+  OFFSET_FORMAT_DQ		/* Bottom 4 bits must be 0.  */
+};
+
+extern enum rs6000_offset_format reg_to_offset_format (rtx, machine_mode, bool);
+extern bool rs6000_prefixed_address_format (rtx, enum rs6000_offset_format);
 extern bool rs6000_prefixed_address_mode (rtx, machine_mode);
 #endif /* RTX_CODE */
 
Index: gcc/config/rs6000/rs6000.c
===================================================================
--- gcc/config/rs6000/rs6000.c	(revision 273371)
+++ gcc/config/rs6000/rs6000.c	(working copy)
@@ -459,6 +459,7 @@  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 rs6000_offset_format offset_format;	/* Format of offset insn.  */
   addr_mask_type addr_mask[(int)N_RELOAD_REG]; /* Valid address masks.  */
   bool scalar_in_vmx_p;			/* Scalar value can go in VMX.  */
 };
@@ -498,6 +499,17 @@  mode_supports_dq_form (machine_mode mode
 	  != 0);
 }
 
+/* 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;
+}
+
 /* Given that there exists at least one variable that is set (produced)
    by OUT_INSN and read (consumed) by IN_INSN, return true iff
    IN_INSN represents one or more memory store operations and none of
@@ -2702,6 +2714,92 @@  rs6000_debug_reg_global (void)
 }
 
 
+/* Initiailize the table of offset address formats.  Unfortunately we do not
+   know the context of the use, so we have to return the address form to model
+   the expected usage.  */
+static void
+initialize_offset_formats (void)
+{
+  /* Set offset format for some types based on the switches.  */
+  enum rs6000_offset_format format_gpr_64bit
+    = (TARGET_POWERPC64) ? OFFSET_FORMAT_DS : OFFSET_FORMAT_D;
+  enum rs6000_offset_format format_gpr_128bit
+    = (TARGET_QUAD_MEMORY) ? OFFSET_FORMAT_DQ : format_gpr_64bit;
+  enum rs6000_offset_format format_fpr_64bit
+    = (TARGET_SOFT_FLOAT) ? OFFSET_FORMAT_D : format_gpr_64bit;
+  enum rs6000_offset_format format_vector
+    = (TARGET_P9_VECTOR) ? OFFSET_FORMAT_DQ : OFFSET_FORMAT_NONE;
+  enum rs6000_offset_format format_tf
+    = (TARGET_IEEEQUAD) ? format_vector : format_fpr_64bit;
+
+  for (ssize_t m = 0; m < NUM_MACHINE_MODES; m++)
+    reg_addr[m].offset_format = OFFSET_FORMAT_NONE;
+
+  /* Small integers.  */
+  reg_addr[E_QImode].offset_format = OFFSET_FORMAT_D;
+  reg_addr[E_HImode].offset_format = OFFSET_FORMAT_D;
+  reg_addr[E_CQImode].offset_format = OFFSET_FORMAT_D;
+  reg_addr[E_CHImode].offset_format = OFFSET_FORMAT_D;
+
+  /* Note, LWA needs ds-form, not d-form, but we set up SImode for the normal
+     load/store.  The Y constraint will prevent the a LWA with odd offset.  */
+  reg_addr[E_SImode].offset_format = OFFSET_FORMAT_D;
+  reg_addr[E_CSImode].offset_format = OFFSET_FORMAT_D;
+
+  /* While DImode can be loaded into a FPR register with LFD (d-form), the
+     normal use is to use a GPR (ds-form in 64-bit).  */
+  reg_addr[E_DImode].offset_format = format_gpr_64bit;
+  reg_addr[E_CDImode].offset_format = format_gpr_64bit;
+
+  /* Condition registers (uses LWZ/STW).  */
+  reg_addr[E_CCmode].offset_format = OFFSET_FORMAT_D;
+  reg_addr[E_CCUNSmode].offset_format = OFFSET_FORMAT_D;
+  reg_addr[E_CCFPmode].offset_format = OFFSET_FORMAT_D;
+  reg_addr[E_CCEQmode].offset_format = OFFSET_FORMAT_D;
+
+  /* 128-bit integers.  */
+  if (TARGET_POWERPC64)
+    {
+      reg_addr[E_TImode].offset_format = format_vector;
+      reg_addr[E_PTImode].offset_format = format_gpr_128bit;
+      reg_addr[E_CTImode].offset_format = format_vector;
+      reg_addr[E_CPTImode].offset_format = format_gpr_128bit;
+    }
+
+  /* Scalar floating point.  While SFmode and DFmode need a ds-form load to be
+     loaded into an Altivec register, use d-form for the FPR registers.  The wY
+     constraint will prevent odd Altivec load/stores.  For SDmode, there isn't
+     an offsettable mode that can be used to load it into a FPR register.  */
+  reg_addr[E_SFmode].offset_format = OFFSET_FORMAT_D;
+  reg_addr[E_SCmode].offset_format = OFFSET_FORMAT_D;
+  reg_addr[E_DFmode].offset_format = format_fpr_64bit;
+  reg_addr[E_DCmode].offset_format = format_fpr_64bit;
+  reg_addr[E_TFmode].offset_format = format_tf;
+  reg_addr[E_TCmode].offset_format = format_tf;
+  reg_addr[E_IFmode].offset_format = format_fpr_64bit;
+  reg_addr[E_ICmode].offset_format = format_fpr_64bit;
+  reg_addr[E_KFmode].offset_format = format_vector;
+  reg_addr[E_KCmode].offset_format = format_vector;
+  reg_addr[E_DDmode].offset_format = format_fpr_64bit;
+  reg_addr[E_TDmode].offset_format = format_fpr_64bit;
+
+  /* Vectors.  */
+  reg_addr[E_V16QImode].offset_format = format_vector;
+  reg_addr[E_V8HImode].offset_format = format_vector;
+  reg_addr[E_V4SImode].offset_format = format_vector;
+  reg_addr[E_V2DImode].offset_format = format_vector;
+  reg_addr[E_V1TImode].offset_format = format_vector;
+  reg_addr[E_V4SFmode].offset_format = format_vector;
+  reg_addr[E_V2DFmode].offset_format = format_vector;
+
+  /* VOIDmode and BLKmode get the most pessimistic format.  */
+  reg_addr[E_VOIDmode].offset_format = OFFSET_FORMAT_DQ;
+  reg_addr[E_BLKmode].offset_format = OFFSET_FORMAT_DQ;
+
+  return;
+}
+
+
 /* Update the addr mask bits in reg_addr to help secondary reload and go if
    legitimate address support to figure out the appropriate addressing to
    use.  */
@@ -3238,6 +3336,9 @@  rs6000_init_hard_regno_mode_ok (bool glo
 	}
     }
 
+  /* Initial the mapping of mode to offset formats.  */
+  initialize_offset_formats ();
+
   /* Precalculate HARD_REGNO_NREGS.  */
   for (r = 0; HARD_REGISTER_NUM_P (r); ++r)
     for (m = 0; m < NUM_MACHINE_MODES; ++m)
@@ -7407,6 +7508,14 @@  quad_address_p (rtx addr, machine_mode m
   if (VECTOR_MODE_P (mode) && !mode_supports_dq_form (mode))
     return false;
 
+  /* Is this a valid prefixed address?  If the bottom four bits of the offset
+     are non-zero, we could use a prefixed instruction (which does not have the
+     DQ-form constraint that the traditional instruction had) instead of
+     forcing the unaligned offset to a GPR.  */
+  if (mode_supports_prefixed_address_p (mode)
+      && rs6000_prefixed_address_format (addr, OFFSET_FORMAT_DQ))
+    return true;
+
   if (GET_CODE (addr) != PLUS)
     return false;
 
@@ -7508,6 +7617,14 @@  mem_operand_gpr (rtx op, machine_mode mo
       && legitimate_indirect_address_p (XEXP (addr, 0), false))
     return true;
 
+  /* Allow prefixed instructions if supported.  If the bottom two bits of the
+     offset are non-zero, we could use a prefixed instruction (which does not
+     have the DS-form constraint that the traditional instruction had) instead
+     of forcing the unaligned offset to a GPR.  */
+  if (mode_supports_prefixed_address_p (mode)
+      && rs6000_prefixed_address_format (XEXP (op, 0), OFFSET_FORMAT_DS))
+    return true;
+
   /* Don't allow non-offsettable addresses.  See PRs 83969 and 84279.  */
   if (!rs6000_offsettable_memref_p (op, mode, false))
     return false;
@@ -7542,6 +7659,14 @@  mem_operand_ds_form (rtx op, machine_mod
   int extra;
   rtx addr = XEXP (op, 0);
 
+  /* Allow prefixed instructions if supported.  If the bottom two bits of the
+     offset are non-zero, we could use a prefixed instruction (which does not
+     have the DS-form constraint that the traditional instruction had) instead
+     of forcing the unaligned offset to a GPR.  */
+  if (mode_supports_prefixed_address_p (mode)
+      && rs6000_prefixed_address_format (XEXP (op, 0), OFFSET_FORMAT_DS))
+    return true;
+
   if (!offsettable_address_p (false, mode, addr))
     return false;
 
@@ -21499,24 +21624,14 @@  rs6000_pltseq_template (rtx *operands, i
 }
 #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.  */
+
+/* Return true if ADDR is a valid prefixed memory address and the traditional
+   form of the instruction uses the address format FORM.  */
 
 bool
-rs6000_prefixed_address_mode (rtx addr, machine_mode mode)
+rs6000_prefixed_address_format (rtx addr, enum rs6000_offset_format format)
 {
-  if (!TARGET_PREFIXED_ADDR || !mode_supports_prefixed_address_p (mode))
+  if (!TARGET_PREFIXED_ADDR)
     return false;
 
   /* Check for PC-relative addresses.  */
@@ -21541,28 +21656,110 @@  rs6000_prefixed_address_mode (rtx addr,
       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;
+      switch (format)
+	{
+	default:
+	  gcc_unreachable ();
+
+	case OFFSET_FORMAT_D:		/* All 16-bits are available.  */
+	  return false;
 
-      /* QImode/HImode has no restrictions.  */
-      else
-	return true;
+	case OFFSET_FORMAT_DS:		/* Bottom 2 bits must be 0.  */
+	  return (value & 3) != 0;
 
-      /* Return true if we must use a prefixed instruction.  */
-      return (value & mask) != 0;
+	case OFFSET_FORMAT_NONE:
+	case OFFSET_FORMAT_DQ:		/* Bottom 4 bits must be 0.  */
+	  return (value & 15) != 0;
+	}
     }
 
   return false;
 }
+
+/* Return true if ADDR is a valid prefixed memory address that uses mode
+   MODE.  */
+
+bool
+rs6000_prefixed_address_mode (rtx addr, machine_mode mode)
+{
+  if (!TARGET_PREFIXED_ADDR || !mode_supports_prefixed_address_p (mode))
+    return false;
+
+  return rs6000_prefixed_address_format (addr, reg_addr[mode].offset_format);
+}
+
+
+/* For a given hard register and whether it is being sign extended, return the
+   address format (d-form, ds-form, dq-form).  */
+
+enum rs6000_offset_format
+reg_to_offset_format (rtx reg, machine_mode mode, bool sign_extend_p)
+{
+  unsigned int msize = GET_MODE_SIZE (mode);
+  bool altivec_reg = false;
+  enum rs6000_reg_type rtype = register_to_reg_type (reg, &altivec_reg);
+  enum rs6000_offset_format format = OFFSET_FORMAT_NONE;
+
+  /* LWA (SImode -> DImode) is ds-form, but normal LWZ is d-form.  */
+  if (mode == SImode && sign_extend_p)
+    return OFFSET_FORMAT_DS;
+
+  switch (rtype)
+    {
+      /* If the register hasn't been allocated yet, use the default format.  */
+    case NO_REG_TYPE:
+    case PSEUDO_REG_TYPE:
+      format = reg_addr[mode].offset_format;
+      break;
+
+      /* LHZ, LWZ, etc. are d-form, LD is ds-format, LQ is dq-form.  */
+    case GPR_REG_TYPE:
+      if (msize < 8)
+	format = OFFSET_FORMAT_D;
+
+      else if (msize == 8)
+	format = TARGET_POWERPC64 ? OFFSET_FORMAT_DS : OFFSET_FORMAT_D;
+
+      else if (TARGET_QUAD_MEMORY)
+	format = OFFSET_FORMAT_DQ;
+
+      else
+	format = TARGET_POWERPC64 ? OFFSET_FORMAT_DS : OFFSET_FORMAT_D;
+
+      break;
+
+      /* LFD, LFS are d-form, vector types are dq-form.  */
+    case FPR_REG_TYPE:
+      format = (msize < 16) ? OFFSET_FORMAT_D : OFFSET_FORMAT_DQ;
+      break;
+
+      /* LXSSD, LXSD, etc. are ds-form, vector types are dq-form.  */
+    case ALTIVEC_REG_TYPE:
+      format = (msize < 16) ? OFFSET_FORMAT_DS : OFFSET_FORMAT_DQ;
+      break;
+
+      /* LFD, LFS are d-form, LXSSD, LXSD are ds-form, and vector types are
+	 dq-form.  */
+    case VSX_REG_TYPE:
+      if (msize >= 16)
+	format = OFFSET_FORMAT_DQ;
+
+      else if (altivec_reg)
+	format = OFFSET_FORMAT_DS;
+
+      else
+	format = OFFSET_FORMAT_D;
+      break;
+
+      /* SPR, CRs don't have offsettable memory.  */
+    default:
+    case SPR_REG_TYPE:
+    case CR_REG_TYPE:
+      gcc_unreachable ();
+    }
+
+  return format;
+}
 
 #if defined (HAVE_GAS_HIDDEN) && !TARGET_MACHO
 /* Emit an assembler directive to set symbol visibility for DECL to