, V4, patch #2: Add prefixed insn attribute
diff mbox series

Message ID 20190918235625.GB28484@ibm-toto.the-meissners.org
State New
Headers show
Series
  • , V4, patch #2: Add prefixed insn attribute
Related show

Commit Message

Michael Meissner Sept. 18, 2019, 11:56 p.m. UTC
This patch adds the "prefixed" insn attribute that says whether or not the insn
generates a prefixed instruction or not.

The attributes "prefixed_length" and "non_prefixed_length" give then length in
bytes (12 and 4 by default) of the insn if it is prefixed or not.

The "length" attribute is set based on the "prefixed" attribute.

I use the target hooks ASM_OUTPUT_OPCODE and FINAL_PRESCAN_INSN to decide
whether to emit a leading "p" before the insn.

There are functions (prefixed_load_p, prefixed_store_p, and prefixed_paddi_p)
that given an insn type, say whether that particular insn type is prefixed or
not.

In addition, this patch adds the support in rs6000_emit_move to load up
pc-relative addresses, both local addresses defined in the same compilation
unit, and external addresses that might be need to be loaded from a .GOT
address table.

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/rs6000-protos.h (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 (rs6000_emit_move): Support loading
	pc-relative addresses.
	(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.

Patch
diff mbox series

Index: gcc/config/rs6000/rs6000-protos.h
===================================================================
--- gcc/config/rs6000/rs6000-protos.h	(revision 275908)
+++ gcc/config/rs6000/rs6000-protos.h	(working copy)
@@ -189,6 +189,30 @@  enum non_prefixed {
 
 extern enum insn_form address_to_insn_form (rtx, machine_mode,
 					    enum non_prefixed);
+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 is a prefixed instruction that can be directly
+   used in a memory instruction (i.e. using numeric offset or a pc-relative
+   reference to a local symbol).
+
+   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 non_prefixed_insn)
+{
+  enum insn_form iform = address_to_insn_form (addr, mode,
+					       non_prefixed_insn);
+  return (iform == INSN_FORM_PREFIXED_NUMERIC
+	  || iform == INSN_FORM_PCREL_LOCAL);
+}
 #endif /* RTX_CODE */
 
 #ifdef TREE_CODE
@@ -268,8 +292,6 @@  extern void rs6000_d_target_versions (vo
 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: gcc/config/rs6000/rs6000.c
===================================================================
--- gcc/config/rs6000/rs6000.c	(revision 275908)
+++ gcc/config/rs6000/rs6000.c	(working copy)
@@ -9639,6 +9639,22 @@  rs6000_emit_move (rtx dest, rtx source,
 	  return;
 	}
 
+      /* Use the default pattern for loading up pc-relative addresses.  */
+      if (TARGET_PCREL && mode == Pmode
+	  && (SYMBOL_REF_P (operands[1]) || LABEL_REF_P (operands[1])
+	      || GET_CODE (operands[1]) == CONST))
+	{
+	  enum insn_form iform = address_to_insn_form (operands[1], mode,
+						       NON_PREFIXED_DEFAULT);
+
+	  if (iform == INSN_FORM_PCREL_LOCAL
+	      || iform == INSN_FORM_PCREL_EXTERNAL)
+	    {
+	      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))
@@ -24716,6 +24732,203 @@  address_to_insn_form (rtx addr,
   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
+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 (r >= FIRST_PSEUDO_REGISTER)
+    return NON_PREFIXED_DEFAULT;
+
+  unsigned size = GET_MODE_SIZE (mode);
+
+  /* FPR registers use D-mode for scalars, and DQ-mode for vectors.  */
+  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 && ALTIVEC_OR_VSX_VECTOR_MODE (mode))
+	return NON_PREFIXED_DQ;
+
+      else
+	return NON_PREFIXED_DEFAULT;
+    }
+
+  /* Altivec registers use DS-mode for scalars, and DQ-mode for vectors.  */
+  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 && ALTIVEC_OR_VSX_VECTOR_MODE (mode))
+	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;
+}
+
+
+/* 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;
+
+  /* LWA uses the DS format instead of the D format that LWZ uses.  */
+  enum non_prefixed non_prefixed_insn;
+  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_insn = NON_PREFIXED_DS;
+
+  else
+    non_prefixed_insn = reg_to_non_prefixed (reg, mem_mode);
+
+  return address_is_prefixed (XEXP (mem, 0), mem_mode, non_prefixed_insn);
+}
+
+/* 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;
+
+  machine_mode mem_mode = GET_MODE (mem);
+  enum non_prefixed non_prefixed_insn = reg_to_non_prefixed (reg, mem_mode);
+  return address_is_prefixed (XEXP (mem, 0), mem_mode, non_prefixed_insn);
+}
+
+/* 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)
+    fputc ('p', stream);
+
+  return;
+}
+
 
 #ifdef HAVE_GAS_HIDDEN
 # define USE_HIDDEN_LINKONCE 1
Index: gcc/config/rs6000/rs6000.h
===================================================================
--- gcc/config/rs6000/rs6000.h	(revision 275894)
+++ gcc/config/rs6000/rs6000.h	(working copy)
@@ -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: gcc/config/rs6000/rs6000.md
===================================================================
--- gcc/config/rs6000/rs6000.md	(revision 275894)
+++ gcc/config/rs6000/rs6000.md	(working copy)
@@ -256,8 +256,52 @@  (define_attr "var_shift" "no,yes"
 ;; 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 (and (eq_attr "indexed" "no")
+			    (eq_attr "update" "no")
+			    (match_test "prefixed_load_p (insn)"))
+		       (const_string "yes")
+		       (const_string "no"))
+
+	 (eq_attr "type" "store,fpstore,vecstore")
+	 (if_then_else (and (eq_attr "indexed" "no")
+			    (eq_attr "update" "no")
+			    (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.
@@ -9875,6 +9919,28 @@  (define_expand "restore_stack_nonlocal"
   operands[6] = gen_rtx_PARALLEL (VOIDmode, p);
 })
 
+;; 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...