Add initial support for prefixed/PC-relative addressing.
authorMichael Meissner <meissner@linux.ibm.com>
Mon, 30 Sep 2019 13:49:13 +0000 (13:49 +0000)
committerMichael Meissner <meissner@gcc.gnu.org>
Mon, 30 Sep 2019 13:49:13 +0000 (13:49 +0000)
2019-09-30  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.
(prefixed_load_p): New declaration.
(prefixed_store_p): New declaration.
(prefixed_paddi_p): New declaration.
(rs6000_asm_output_opcode): New declaration.
(rs6000_final_prescan_insn): Move declaration and update calling
signature.
(address_is_prefixed): New helper inline function.
* config/rs6000/rs6000.c(print_operand_address): Check for either
PC-relative local symbols or PC-relative external symbols.
(rs6000_emit_move): Support loading PC-relative addresses.
(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.
(reg_to_non_prefixed): New function to identify what the
non-prefixed memory instruction format is for a register.
(prefixed_load_p): New function to identify prefixed loads.
(prefixed_store_p): New function to identify prefixed stores.
(prefixed_paddi_p): New function to identify prefixed load
immediates.
(next_insn_prefixed_p): New static state variable.
(rs6000_final_prescan_insn): New function to determine if an insn
uses a prefixed instruction.
(rs6000_asm_output_opcode): New function to emit 'p' in front of a
prefixed instruction.
* config/rs6000/rs6000.h (FINAL_PRESCAN_INSN): New target hook.
(ASM_OUTPUT_OPCODE): New target hook.
* config/rs6000/rs6000.md (prefixed): New insn attribute for
prefixed instructions.
(prefixed_length): New insn attribute for the size of prefixed
instructions.
(non_prefixed_length): New insn attribute for the size of
non-prefixed instructions.
(pcrel_local_addr): New insn to load up a local PC-relative
address.
(pcrel_extern_addr): New insn to load up an external PC-relative
address.
(mov<mode>_64bit_dm): Split the alternatives for loading 0.0 to a
GPR and loading a 128-bit floating point type to a GPR.

From-SVN: r276300

gcc/ChangeLog
gcc/config/rs6000/predicates.md
gcc/config/rs6000/rs6000-protos.h
gcc/config/rs6000/rs6000.c
gcc/config/rs6000/rs6000.h
gcc/config/rs6000/rs6000.md

index 7583de8..74f08cc 100644 (file)
@@ -1,3 +1,56 @@
+2019-09-30  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.
+       (prefixed_load_p): New declaration.
+       (prefixed_store_p): New declaration.
+       (prefixed_paddi_p): New declaration.
+       (rs6000_asm_output_opcode): New declaration.
+       (rs6000_final_prescan_insn): Move declaration and update calling
+       signature.
+       (address_is_prefixed): New helper inline function.
+       * config/rs6000/rs6000.c(print_operand_address): Check for either
+       PC-relative local symbols or PC-relative external symbols.
+       (rs6000_emit_move): Support loading PC-relative addresses.
+       (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.
+       (reg_to_non_prefixed): New function to identify what the
+       non-prefixed memory instruction format is for a register.
+       (prefixed_load_p): New function to identify prefixed loads.
+       (prefixed_store_p): New function to identify prefixed stores.
+       (prefixed_paddi_p): New function to identify prefixed load
+       immediates.
+       (next_insn_prefixed_p): New static state variable.
+       (rs6000_final_prescan_insn): New function to determine if an insn
+       uses a prefixed instruction.
+       (rs6000_asm_output_opcode): New function to emit 'p' in front of a
+       prefixed instruction.
+       * config/rs6000/rs6000.h (FINAL_PRESCAN_INSN): New target hook.
+       (ASM_OUTPUT_OPCODE): New target hook.
+       * config/rs6000/rs6000.md (prefixed): New insn attribute for
+       prefixed instructions.
+       (prefixed_length): New insn attribute for the size of prefixed
+       instructions.
+       (non_prefixed_length): New insn attribute for the size of
+       non-prefixed instructions.
+       (pcrel_local_addr): New insn to load up a local PC-relative
+       address.
+       (pcrel_extern_addr): New insn to load up an external PC-relative
+       address.
+       (mov<mode>_64bit_dm): Split the alternatives for loading 0.0 to a
+       GPR and loading a 128-bit floating point type to a GPR.
+
 2019-09-30  Richard Biener  <rguenther@suse.de>
 
        * gimple.c (gimple_get_lhs): For PHIs return the result.
index 9368bdd..345d9c3 100644 (file)
   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);
-})
-
+\f
 ;; Match the first insn (addis) in fusing the combination of addis and loads to
 ;; GPR registers on power8.
 (define_predicate "fusion_gpr_addis"
 
   return 0;
 })
+
+\f
+;; Return true if the operand is a PC-relative address of 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 a PC-relative 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"
+  (ior (match_operand 0 "pcrel_local_address")
+       (match_operand 0 "pcrel_external_address")))
index 06e40d9..c51b768 100644 (file)
@@ -154,7 +154,66 @@ 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);
+
+/* Different PowerPC instruction formats that are used by GCC.  There are
+   various other instruction formats used by the PowerPC hardware, but 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,                 /* Reg + 16-bit numeric offset.  */
+  INSN_FORM_DS,                        /* Reg + offset, bottom 2 bits must be 0.  */
+  INSN_FORM_DQ,                        /* Reg + offset, bottom 4 bits must be 0.  */
+  INSN_FORM_X,                 /* Base register + index register.  */
+  INSN_FORM_UPDATE,            /* Address updates base register.  */
+  INSN_FORM_LO_SUM,            /* Reg + offset using symbol.  */
+  INSN_FORM_PREFIXED_NUMERIC,  /* Reg + 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_form {
+  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_form);
+extern bool prefixed_load_p (rtx_insn *);
+extern bool prefixed_store_p (rtx_insn *);
+extern bool prefixed_paddi_p (rtx_insn *);
+extern void rs6000_asm_output_opcode (FILE *);
+extern void rs6000_final_prescan_insn (rtx_insn *, rtx [], int);
+
+/* Return true if the address can be used for a prefixed load, store, or add
+   immediate instructions that cannot be used with a non-prefixed instruction.
+   For example, using a numeric offset that is not valid for the non-prefixed
+   instruction or a PC-relative reference to a local symbol would return true,
+   but an address with an offset of 64 would not return true.
+
+   References to external PC-relative symbols aren't allowed, because GCC has
+   to load the address into a register and then issue a separate load or
+   store.  */
+
+static inline bool
+address_is_prefixed (rtx addr,
+                    machine_mode mode,
+                    enum non_prefixed_form non_prefixed)
+{
+  enum insn_form iform = address_to_insn_form (addr, mode, non_prefixed);
+  return (iform == INSN_FORM_PREFIXED_NUMERIC
+         || iform == INSN_FORM_PCREL_LOCAL);
+}
 #endif /* RTX_CODE */
 
 #ifdef TREE_CODE
@@ -234,8 +293,6 @@ extern void rs6000_d_target_versions (void);
 const char * rs6000_xcoff_strip_dollar (const char *);
 #endif
 
-void rs6000_final_prescan_insn (rtx_insn *, rtx *operand, int num_operands);
-
 extern unsigned char rs6000_class_max_nregs[][LIM_REG_CLASSES];
 extern unsigned char rs6000_hard_regno_nregs[][FIRST_PSEUDO_REGISTER];
 
index 1eb1317..d6e1fea 100644 (file)
@@ -9640,6 +9640,14 @@ rs6000_emit_move (rtx dest, rtx source, machine_mode mode)
          return;
        }
 
+      /* Use the default pattern for loading up PC-relative addresses.  */
+      if (TARGET_PCREL && mode == Pmode
+         && pcrel_local_or_external_address (operands[1], Pmode))
+       {
+         emit_insn (gen_rtx_SET (operands[0], operands[1]));
+         return;
+       }
+
       if (DEFAULT_ABI == ABI_V4
          && mode == Pmode && mode == SImode
          && flag_pic == 1 && got_operand (operands[1], mode))
@@ -13082,8 +13090,8 @@ print_operand_address (FILE *file, rtx x)
   if (REG_P (x))
     fprintf (file, "0(%s)", reg_names[ REGNO (x) ]);
 
-  /* Is it a pc-relative address?  */
-  else if (pcrel_address (x, Pmode))
+  /* Is it a PC-relative address?  */
+  else if (TARGET_PCREL && pcrel_local_or_external_address (x, VOIDmode))
     {
       HOST_WIDE_INT offset;
 
@@ -13103,7 +13111,10 @@ print_operand_address (FILE *file, rtx x)
       if (offset)
        fprintf (file, "%+" PRId64, offset);
 
-      fputs ("@pcrel", file);
+      if (SYMBOL_REF_P (x) && !SYMBOL_REF_LOCAL_P (x))
+       fprintf (file, "@got");
+
+      fprintf (file, "@pcrel");
     }
   else if (SYMBOL_REF_P (x) || GET_CODE (x) == CONST
           || GET_CODE (x) == LABEL_REF)
@@ -13588,71 +13599,6 @@ rs6000_pltseq_template (rtx *operands, int which)
   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;
-}
 \f
 #if defined (HAVE_GAS_HIDDEN) && !TARGET_MACHO
 /* Emit an assembler directive to set symbol visibility for DECL to
@@ -24617,6 +24563,385 @@ rs6000_pcrel_p (struct function *fn)
   return rs6000_fndecl_pcrel_p (fn->decl);
 }
 
+\f
+/* Given an address (ADDR), a mode (MODE), and what the format of the
+   non-prefixed address (NON_PREFIXED_FORMAT) is, return the instruction format
+   for the address.  */
+
+enum insn_form
+address_to_insn_form (rtx addr,
+                     machine_mode mode,
+                     enum non_prefixed_form non_prefixed_format)
+{
+  /* Single register is easy.  */
+  if (REG_P (addr) || SUBREG_P (addr))
+    return INSN_FORM_BASE_REG;
+
+  /* If the non prefixed instruction format doesn't support offset addressing,
+     make sure only indexed addressing is allowed.
+
+     We special case SDmode so that the register allocator does not try to move
+     SDmode through GPR registers, but instead uses the 32-bit integer load and
+     store instructions for the floating point registers.  */
+  if (non_prefixed_format == NON_PREFIXED_X || (mode == SDmode && TARGET_DFP))
+    {
+      if (GET_CODE (addr) != PLUS)
+       return INSN_FORM_BAD;
+
+      rtx op0 = XEXP (addr, 0);
+      rtx 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) && !SYMBOL_REF_LOCAL_P (addr))
+       return INSN_FORM_PCREL_EXTERNAL;
+
+      if (SYMBOL_REF_P (addr) || LABEL_REF_P (addr))
+       return INSN_FORM_PCREL_LOCAL;
+    }
+
+  if (GET_CODE (addr) == CONST)
+    addr = XEXP (addr, 0);
+
+  /* Recognize LO_SUM addresses used with TOC and 32-bit addressing.  */
+  if (GET_CODE (addr) == LO_SUM)
+    return INSN_FORM_LO_SUM;
+
+  /* Everything below must be an offset address of some form.  */
+  if (GET_CODE (addr) != PLUS)
+    return INSN_FORM_BAD;
+
+  rtx op0 = XEXP (addr, 0);
+  rtx op1 = XEXP (addr, 1);
+
+  /* Check for indexed addresses.  */
+  if (REG_P (op1) || SUBREG_P (op1))
+    {
+      if (REG_P (op0) || SUBREG_P (op0))
+       return INSN_FORM_X;
+
+      return INSN_FORM_BAD;
+    }
+
+  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) && !SYMBOL_REF_LOCAL_P (op0))
+       return INSN_FORM_PCREL_EXTERNAL;
+
+      if (SYMBOL_REF_P (op0) || LABEL_REF_P (op0))
+       return INSN_FORM_PCREL_LOCAL;
+    }
+
+  /* If it isn't PC-relative, the address must use a base register.  */
+  if (!REG_P (op0) && !SUBREG_P (op0))
+    return INSN_FORM_BAD;
+
+  /* Large offsets must be prefixed.  */
+  if (!SIGNED_16BIT_OFFSET_P (offset))
+    {
+      if (TARGET_PREFIXED_ADDR)
+       return INSN_FORM_PREFIXED_NUMERIC;
+
+      return INSN_FORM_BAD;
+    }
+
+  /* We have a 16-bit offset, see what default instruction format to use.  */
+  if (non_prefixed_format == NON_PREFIXED_DEFAULT)
+    {
+      unsigned size = GET_MODE_SIZE (mode);
+
+      /* On 64-bit systems, assume 64-bit integers need to use DS form
+        addresses (for LD/STD).  VSX vectors need to use DQ form addresses
+        (for LXV and STXV).  TImode is problematical in that its normal usage
+        is expected to be GPRs where it wants a DS instruction format, but if
+        it goes into the vector registers, it wants a DQ instruction
+        format.  */
+      if (TARGET_POWERPC64 && size >= 8 && GET_MODE_CLASS (mode) == MODE_INT)
+       non_prefixed_format = NON_PREFIXED_DS;
+
+      else if (TARGET_VSX && size >= 16
+              && (VECTOR_MODE_P (mode) || FLOAT128_VECTOR_P (mode)))
+       non_prefixed_format = NON_PREFIXED_DQ;
+
+      else
+       non_prefixed_format = NON_PREFIXED_D;
+    }
+
+  /* Classify the D/DS/DQ-form addresses.  */
+  switch (non_prefixed_format)
+    {
+      /* 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;
+}
+
+/* Helper function to take a REG and a MODE and turn it into the non-prefixed
+   instruction format (D/DS/DQ) used for offset memory.  */
+
+static enum non_prefixed_form
+reg_to_non_prefixed (rtx reg, machine_mode mode)
+{
+  /* If it isn't a register, use the defaults.  */
+  if (!REG_P (reg) && !SUBREG_P (reg))
+    return NON_PREFIXED_DEFAULT;
+
+  unsigned int r = reg_or_subregno (reg);
+
+  /* If we have a pseudo, use the default instruction format.  */
+  if (!HARD_REGISTER_NUM_P (r))
+    return NON_PREFIXED_DEFAULT;
+
+  unsigned size = GET_MODE_SIZE (mode);
+
+  /* FPR registers use D-mode for scalars, and DQ-mode for vectors, IEEE
+     128-bit floating point, and 128-bit integers.  */
+  if (FP_REGNO_P (r))
+    {
+      if (mode == SFmode || size == 8 || FLOAT128_2REG_P (mode))
+       return NON_PREFIXED_D;
+
+      else if (size < 8)
+       return NON_PREFIXED_X;
+
+      else if (TARGET_VSX && size >= 16
+              && (VECTOR_MODE_P (mode)
+                  || FLOAT128_VECTOR_P (mode)
+                  || mode == TImode || mode == CTImode))
+       return NON_PREFIXED_DQ;
+
+      else
+       return NON_PREFIXED_DEFAULT;
+    }
+
+  /* Altivec registers use DS-mode for scalars, and DQ-mode for vectors, IEEE
+     128-bit floating point, and 128-bit integers.  */
+  else if (ALTIVEC_REGNO_P (r))
+    {
+      if (mode == SFmode || size == 8 || FLOAT128_2REG_P (mode))
+       return NON_PREFIXED_DS;
+
+      else if (size < 8)
+       return NON_PREFIXED_X;
+
+      else if (TARGET_VSX && size >= 16
+              && (VECTOR_MODE_P (mode)
+                  || FLOAT128_VECTOR_P (mode)
+                  || mode == TImode || mode == CTImode))
+       return NON_PREFIXED_DQ;
+
+      else
+       return NON_PREFIXED_DEFAULT;
+    }
+
+  /* GPR registers use DS-mode for 64-bit items on 64-bit systems, and D-mode
+     otherwise.  Assume that any other register, such as LR, CRs, etc. will go
+     through the GPR registers for memory operations.  */
+  else if (TARGET_POWERPC64 && size >= 8)
+    return NON_PREFIXED_DS;
+
+  return NON_PREFIXED_D;
+}
+
+\f
+/* Whether a load instruction is a prefixed instruction.  This is called from
+   the prefixed attribute processing.  */
+
+bool
+prefixed_load_p (rtx_insn *insn)
+{
+  /* Validate the insn to make sure it is a normal load insn.  */
+  extract_insn_cached (insn);
+  if (recog_data.n_operands < 2)
+    return false;
+
+  rtx reg = recog_data.operand[0];
+  rtx mem = recog_data.operand[1];
+
+  if (!REG_P (reg) && !SUBREG_P (reg))
+    return false;
+
+  if (!MEM_P (mem))
+    return false;
+
+  /* Prefixed load instructions do not support update or indexed forms.  */
+  if (get_attr_indexed (insn) == INDEXED_YES
+      || get_attr_update (insn) == UPDATE_YES)
+    return false;
+
+  /* LWA uses the DS format instead of the D format that LWZ uses.  */
+  enum non_prefixed_form non_prefixed;
+  machine_mode reg_mode = GET_MODE (reg);
+  machine_mode mem_mode = GET_MODE (mem);
+
+  if (mem_mode == SImode && reg_mode == DImode
+      && get_attr_sign_extend (insn) == SIGN_EXTEND_YES)
+    non_prefixed = NON_PREFIXED_DS;
+
+  else
+    non_prefixed = reg_to_non_prefixed (reg, mem_mode);
+
+  return address_is_prefixed (XEXP (mem, 0), mem_mode, non_prefixed);
+}
+
+/* Whether a store instruction is a prefixed instruction.  This is called from
+   the prefixed attribute processing.  */
+
+bool
+prefixed_store_p (rtx_insn *insn)
+{
+  /* Validate the insn to make sure it is a normal store insn.  */
+  extract_insn_cached (insn);
+  if (recog_data.n_operands < 2)
+    return false;
+
+  rtx mem = recog_data.operand[0];
+  rtx reg = recog_data.operand[1];
+
+  if (!REG_P (reg) && !SUBREG_P (reg))
+    return false;
+
+  if (!MEM_P (mem))
+    return false;
+
+  /* Prefixed store instructions do not support update or indexed forms.  */
+  if (get_attr_indexed (insn) == INDEXED_YES
+      || get_attr_update (insn) == UPDATE_YES)
+    return false;
+
+  machine_mode mem_mode = GET_MODE (mem);
+  enum non_prefixed_form non_prefixed = reg_to_non_prefixed (reg, mem_mode);
+  return address_is_prefixed (XEXP (mem, 0), mem_mode, non_prefixed);
+}
+
+/* Whether a load immediate or add instruction is a prefixed instruction.  This
+   is called from the prefixed attribute processing.  */
+
+bool
+prefixed_paddi_p (rtx_insn *insn)
+{
+  rtx set = single_set (insn);
+  if (!set)
+    return false;
+
+  rtx dest = SET_DEST (set);
+  rtx src = SET_SRC (set);
+
+  if (!REG_P (dest) && !SUBREG_P (dest))
+    return false;
+
+  /* Is this a load immediate that can't be done with a simple ADDI or
+     ADDIS?  */
+  if (CONST_INT_P (src))
+    return (satisfies_constraint_eI (src)
+           && !satisfies_constraint_I (src)
+           && !satisfies_constraint_L (src));
+
+  /* Is this a PADDI instruction that can't be done with a simple ADDI or
+     ADDIS?  */
+  if (GET_CODE (src) == PLUS)
+    {
+      rtx op1 = XEXP (src, 1);
+
+      return (CONST_INT_P (op1)
+             && satisfies_constraint_eI (op1)
+             && !satisfies_constraint_I (op1)
+             && !satisfies_constraint_L (op1));
+    }
+
+  /* If not, is it a load of a PC-relative address?  */
+  if (!TARGET_PCREL || GET_MODE (dest) != Pmode)
+    return false;
+
+  if (!SYMBOL_REF_P (src) && !LABEL_REF_P (src) && GET_CODE (src) != CONST)
+    return false;
+
+  enum insn_form iform = address_to_insn_form (src, Pmode,
+                                              NON_PREFIXED_DEFAULT);
+
+  return (iform == INSN_FORM_PCREL_EXTERNAL || iform == INSN_FORM_PCREL_LOCAL);
+}
+
+/* Whether the next instruction needs a 'p' prefix issued before the
+   instruction is printed out.  */
+static bool next_insn_prefixed_p;
+
+/* Define FINAL_PRESCAN_INSN if some processing needs to be done before
+   outputting the assembler code.  On the PowerPC, we remember if the current
+   insn is a prefixed insn where we need to emit a 'p' before the insn.
+
+   In addition, if the insn is part of a PC-relative reference to an external
+   label optimization, this is recorded also.  */
+void
+rs6000_final_prescan_insn (rtx_insn *insn, rtx [], int)
+{
+  next_insn_prefixed_p = (get_attr_prefixed (insn) != PREFIXED_NO);
+  return;
+}
+
+/* Define ASM_OUTPUT_OPCODE to do anything special before emitting an opcode.
+   We use it to emit a 'p' for prefixed insns that is set in
+   FINAL_PRESCAN_INSN.  */
+void
+rs6000_asm_output_opcode (FILE *stream)
+{
+  if (next_insn_prefixed_p)
+    fprintf (stream, "p");
+
+  return;
+}
+
+\f
 #ifdef HAVE_GAS_HIDDEN
 # define USE_HIDDEN_LINKONCE 1
 #else
index 8f5c70e..0156448 100644 (file)
@@ -2547,3 +2547,24 @@ typedef struct GTY(()) machine_function
   IN_RANGE ((VALUE),                                                   \
            -(HOST_WIDE_INT_1 << 33),                                   \
            (HOST_WIDE_INT_1 << 33) - 1 - (EXTRA))
+
+/* Define this if some processing needs to be done before outputting the
+   assembler code.  On the PowerPC, we remember if the current insn is a normal
+   prefixed insn where we need to emit a 'p' before the insn.  */
+#define FINAL_PRESCAN_INSN(INSN, OPERANDS, NOPERANDS)                  \
+do                                                                     \
+  {                                                                    \
+    if (TARGET_PREFIXED_ADDR)                                          \
+      rs6000_final_prescan_insn (INSN, OPERANDS, NOPERANDS);           \
+  }                                                                    \
+while (0)
+
+/* Do anything special before emitting an opcode.  We use it to emit a 'p' for
+   prefixed insns that is set in FINAL_PRESCAN_INSN.  */
+#define ASM_OUTPUT_OPCODE(STREAM, OPCODE)                              \
+  do                                                                   \
+    {                                                                  \
+     if (TARGET_PREFIXED_ADDR)                                         \
+       rs6000_asm_output_opcode (STREAM);                              \
+    }                                                                  \
+  while (0)
index 46167e5..4f27f13 100644 (file)
 ;; Is copying of this instruction disallowed?
 (define_attr "cannot_copy" "no,yes" (const_string "no"))
 
-;; Length of the instruction (in bytes).
-(define_attr "length" "" (const_int 4))
+
+;; Whether an insn is a prefixed insn, and an initial 'p' should be printed
+;; before the instruction.  A prefixed instruction has a prefix instruction
+;; word that extends the immediate value of the instructions from 12-16 bits to
+;; 34 bits.  The macro ASM_OUTPUT_OPCODE emits a leading 'p' for prefixed
+;; insns.  The default "length" attribute will also be adjusted by default to
+;; be 12 bytes.
+(define_attr "prefixed" "no,yes"
+  (cond [(ior (match_test "!TARGET_PREFIXED_ADDR")
+             (match_test "!NONJUMP_INSN_P (insn)"))
+        (const_string "no")
+
+        (eq_attr "type" "load,fpload,vecload")
+        (if_then_else (match_test "prefixed_load_p (insn)")
+                      (const_string "yes")
+                      (const_string "no"))
+
+        (eq_attr "type" "store,fpstore,vecstore")
+        (if_then_else (match_test "prefixed_store_p (insn)")
+                      (const_string "yes")
+                      (const_string "no"))
+
+        (eq_attr "type" "integer,add")
+        (if_then_else (match_test "prefixed_paddi_p (insn)")
+                      (const_string "yes")
+                      (const_string "no"))]
+
+       (const_string "no")))
+
+;; Length in bytes of instructions that use prefixed addressing and length in
+;; bytes of instructions that does not use prefixed addressing.  This allows
+;; both lengths to be defined as constants, and the length attribute can pick
+;; the size as appropriate.
+(define_attr "prefixed_length" "" (const_int 12))
+(define_attr "non_prefixed_length" "" (const_int 4))
+
+;; Length of the instruction (in bytes).  Prefixed insns are 8 bytes, but the
+;; assembler might issue need to issue a NOP so that the prefixed instruction
+;; does not cross a cache boundary, which makes them possibly 12 bytes.
+(define_attr "length" ""
+  (if_then_else (eq_attr "prefixed" "yes")
+               (attr "prefixed_length")
+               (attr "non_prefixed_length")))
 
 ;; Processor type -- this attribute must exactly match the processor_type
 ;; enumeration in rs6000-opts.h.
 ;; not swapped like they are for TImode or TFmode.  Subregs therefore are
 ;; problematical.  Don't allow direct move for this case.
 
+;;             FPR load    FPR store   FPR move    FPR zero    GPR load
+;;             GPR zero    GPR store   GPR move    MFVSRD      MTVSRD
+
 (define_insn_and_split "*mov<mode>_64bit_dm"
-  [(set (match_operand:FMOVE128_FPR 0 "nonimmediate_operand" "=m,d,d,d,Y,r,r,r,d")
-       (match_operand:FMOVE128_FPR 1 "input_operand" "d,m,d,<zero_fp>,r,<zero_fp>Y,r,d,r"))]
+  [(set (match_operand:FMOVE128_FPR 0 "nonimmediate_operand"
+               "=m,        d,          d,          d,          Y,
+                r,         r,          r,          r,          d")
+
+       (match_operand:FMOVE128_FPR 1 "input_operand"
+               "d,         m,          d,          <zero_fp>,  r,
+                <zero_fp>, Y,          r,          d,          r"))]
+
   "TARGET_HARD_FLOAT && TARGET_POWERPC64 && FLOAT128_2REG_P (<MODE>mode)
    && (<MODE>mode != TDmode || WORDS_BIG_ENDIAN)
    && (gpc_reg_operand (operands[0], <MODE>mode)
   "&& reload_completed"
   [(pc)]
 { rs6000_split_multireg_move (operands[0], operands[1]); DONE; }
-  [(set_attr "length" "8,8,8,8,12,12,8,8,8")
-   (set_attr "isa" "*,*,*,*,*,*,*,p8v,p8v")])
+  [(set_attr "length" "8")
+   (set_attr "isa" "*,*,*,*,*,*,*,*,p8v,p8v")])
 
 (define_insn_and_split "*movtd_64bit_nodm"
   [(set (match_operand:TD 0 "nonimmediate_operand" "=m,d,d,Y,r,r")
   operands[6] = gen_rtx_PARALLEL (VOIDmode, p);
 })
 \f
+;; Load up a PC-relative address.  Print_operand_address will append a @pcrel
+;; to the symbol or label.
+(define_insn "*pcrel_local_addr"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=r")
+       (match_operand:DI 1 "pcrel_local_address"))]
+  "TARGET_PCREL"
+  "la %0,%a1"
+  [(set_attr "prefixed" "yes")])
+
+;; Load up a PC-relative address to an external symbol.  If the symbol and the
+;; program are both defined in the main program, the linker will optimize this
+;; to a PADDI.  Otherwise, it will create a GOT address that is relocated by
+;; the dynamic linker and loaded up.  Print_operand_address will append a
+;; @got@pcrel to the symbol.
+(define_insn "*pcrel_extern_addr"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=r")
+       (match_operand:DI 1 "pcrel_external_address"))]
+  "TARGET_PCREL"
+  "ld %0,%a1"
+  [(set_attr "prefixed" "yes")
+   (set_attr "type" "load")])
+
 ;; TOC register handling.
 
 ;; Code to initialize the TOC register...