, Patch #2 of 10, Add RTL prefixed attribute
diff mbox series

Message ID 20190814215913.GB16578@ibm-toto.the-meissners.org
State New
Headers show
Series
  • , Patch #2 of 10, Add RTL prefixed attribute
Related show

Commit Message

Michael Meissner Aug. 14, 2019, 9:59 p.m. UTC
This patch adds the RTL attribute "prefixed" that says this particular
instruction is a prefixed instruction.

The target hooks FINAL_SCAN_INSN and ASM_OUTPUT_OPCODE are defined.  If the
insn is prefixed, ASM_OUTPUT_OPCODE will emit a leading 'p' before the
instruction is emitted.  For example, a load word and zero extend instruction
would have the output template:

	lwz%U1%X1 %0,%1

If the insn is prefixed, ASM_OUTPUT_OPCODE will emit the leading 'p' and the
assembler would see something like:

	plwz 3,foo@pcrel

The RTL length attribute looks a the RTL prefixed attribute to set the default
length to either 4 or 12.

In order to simplify setting the length for complex insns that aren't yet
split, I have added two new RTL attributes ("non_prefixed_length" and
"prefixed_length") that the length attribute uses.  Normally these values would
be 4 and 12 bytes, unless this is overwritten by the insn attributes.

In previous versions of the patch, I had a maybe_prefixed attribute that was
used to say this instruction might be prefixed, and to check whether the
instruction was prefixed externally.  Now, I use the type RTL attribute, and I
only look if the type is one of the load types, one of the store types, or one
of the integer and add types.

Due to some of the existing load and store insns not using the traditional
operands[0] and operands[1], the functions that test whether an insn is
prefixed only use the insn and not the operands directly.

Most of the new  code is in a new file (rs6000-prefixed.c).

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

	* config/rs6000/rs6000-prefixed.c: New file.
	* config/rs6000/rs6000-protos.h (rs6000_final_prescan_insn):
	Update calling signature.
	(prefixed_load_p): New function.
	(prefixed_store_p): New function.
	(prefixed_paddi_p): New function.
	* config/rs6000/rs6000.c (rs6000_emit_move): Add support for
	loading up pc-relatve addresses.
	* config/rs6000/rs6000.h (FINAL_SCAN_INSN): New target hook.
	(ASM_OUTPUT_OPCODE): New target hook.
	* config/rs6000/rs6000.md (prefixed attribute): New attribute.
	(prefixed_length attribute): New attribute.
	(non_prefixed_length attribute): New attribute.
	(length attribute): Calculate length in terms of the prefixed,
	prefixed_length, and non_prefixed_length attributes.
	(pcrel_addr): New insn for pc-relative support.
	(pcrel_ext_addr): New insn for pc-relative support.
	* config/rs6000/t-rs6000 (rs6000-prefixed.o): Add build rule.
	* config.gcc (powerpc*-*-*): Add rs6000-prefixed.c.
	(rs6000*-*-*): Add rs6000-prefixed.c.

Comments

Segher Boessenkool Aug. 19, 2019, 5:47 p.m. UTC | #1
Hi Mike,

Some comments on this patch:

On Wed, Aug 14, 2019 at 05:59:13PM -0400, Michael Meissner wrote:
> Due to some of the existing load and store insns not using the traditional
> operands[0] and operands[1], the functions that test whether an insn is
> prefixed only use the insn and not the operands directly.

Both the "update" and the "indexed" attributes have no problem with
this: the insns that have the problem set the attribute value directly.
This is mainly all the various update insns, but there are a bunch more,
and they all need different settings for their attributes.

> 	* config/rs6000/rs6000.c (rs6000_emit_move): Add support for
> 	loading up pc-relatve addresses.

(typo btw)

> +void
> +rs6000_final_prescan_insn (rtx_insn *insn)
> +{
> +  next_insn_prefixed_p = (get_attr_prefixed (insn) != PREFIXED_NO);
> +  return;
> +}

Don't say "return;" at the end of a function please.

> +void
> +rs6000_asm_output_opcode (FILE *stream, const char *)
> +{
> +  if (next_insn_prefixed_p)
> +    {
> +      next_insn_prefixed_p = false;
> +      fprintf (stream, "p");
> +    }

You don't need to clear the flag here; the next call to
rs6000_final_prescan_insn will.

> +#define FINAL_PRESCAN_INSN(INSN, OPERANDS, NOPERANDS)			\
> +do									\
> +  {									\
> +    if (TARGET_PREFIXED_ADDR)						\
> +      rs6000_final_prescan_insn (INSN);					\
> +  }									\
> +while (0)

Either have the function only do what it needs to for prefixed, and call
it something with prefixed in the name, or put the TARGET_PREFIXED_ADDR
test in the function itself.

> +;; Load up a pc-relative address.  ASM_OUTPUT_OPCODE will emit the initial "p".
> +(define_insn "*pcrel_addr"
> +  [(set (match_operand:DI 0 "gpc_reg_operand" "=b*r")
> +	(match_operand:DI 1 "pcrel_address"))]
> +  "TARGET_PCREL"
> +  "la %0,%a1"
> +  [(set_attr "prefixed" "yes")])

(use P for addresses please)

> +;; 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.
> +(define_insn "*pcrel_ext_addr"
> +  [(set (match_operand:DI 0 "gpc_reg_operand" "=b*r")
> +	(match_operand:DI 1 "pcrel_external_address"))]
> +  "TARGET_PCREL"
> +  "ld %0,%a1"
> +  [(set_attr "prefixed" "yes")])

pld does an indirection more than pla does, but this is not clear at all
from the RTL, from the predicate names.  All this is *before* the linker
has done its thing, so pcrel_external_address is really some GOT memory,
so it should have that in its name.


Segher

Patch
diff mbox series

Index: gcc/config/rs6000/rs6000-prefixed.c
===================================================================
--- gcc/config/rs6000/rs6000-prefixed.c	(revision 0)
+++ gcc/config/rs6000/rs6000-prefixed.c	(working copy)
@@ -0,0 +1,188 @@ 
+/* Subroutines used to support prefixed addressing on the PowerPC.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published
+   by the Free Software Foundation; either version 3, or (at your
+   option) any later version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+   License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#define IN_TARGET_CODE 1
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "rtl.h"
+#include "tree.h"
+#include "memmodel.h"
+#include "df.h"
+#include "tm_p.h"
+#include "ira.h"
+#include "print-tree.h"
+#include "varasm.h"
+#include "explow.h"
+#include "expr.h"
+#include "output.h"
+#include "tree-pass.h"
+#include "rtx-vector-builder.h"
+#include "print-rtl.h"
+#include "insn-attr.h"
+#include "insn-config.h"
+#include "recog.h"
+#include "tm-constrs.h"
+
+/* 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.  */
+void
+rs6000_final_prescan_insn (rtx_insn *insn)
+{
+  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.  We also use it for PCREL_OPT to emit the relocation
+   that ties the load of the GOT pointer with the load/store that uses the GOT
+   number.  */
+void
+rs6000_asm_output_opcode (FILE *stream, const char *)
+{
+  if (next_insn_prefixed_p)
+    {
+      next_insn_prefixed_p = false;
+      fprintf (stream, "p");
+    }
+
+  return;
+}
+
+
+/* Whether a load instruction is a prefixed instruction.  This is called from
+   the prefixed attribute processing.  We can't use operands[0] and
+   operands[1], because there are several load insns that don't use the
+   standard destination and source operands (mov<mode>_update1, etc.).  */
+
+bool
+prefixed_load_p (rtx_insn *insn)
+{
+  rtx set = single_set (insn);
+  if (!set)
+    return false;
+
+  rtx reg = SET_DEST (set);
+  rtx mem = SET_SRC (set);
+  bool sign_p = false;
+
+  /* Allow sign/zero/float extend as part of the load.  */
+  if (GET_CODE (mem) == SIGN_EXTEND)
+    {
+      sign_p = true;
+      mem = XEXP (mem, 0);
+    }
+
+  else if (GET_CODE (mem) == ZERO_EXTEND || GET_CODE (mem) == FLOAT_EXTEND)
+    mem = XEXP (mem, 0);
+
+  /* Is this a load?  */
+  if (!MEM_P (mem))
+    return false;
+
+  machine_mode mode = GET_MODE (mem);
+  rtx addr = XEXP (mem, 0);
+
+  /* Special case LWA, which uses the DS instruction format, instead of the D
+     instruction format.  */
+  enum insn_form iform = (sign_p && mode == SImode && GET_CODE (addr) == PLUS
+			  ? INSN_FORM_DS
+			  : reg_to_insn_form (reg, mode));
+
+  return prefixed_local_addr_p (addr, mode, iform);
+}
+
+/* Whether a store instruction is a prefixed instruction.  This is called from
+   the prefixed attribute processing.  */
+
+bool
+prefixed_store_p (rtx_insn *insn)
+{
+  rtx set = single_set (insn);
+  if (!set)
+    return false;
+
+  rtx mem = SET_DEST (set);
+  rtx reg = SET_SRC (set);
+
+  /* Is this a store?  */
+  if (!MEM_P (mem))
+    return false;
+
+  machine_mode mode = GET_MODE (mem);
+  enum insn_form iform = reg_to_insn_form (reg, mode);
+
+  return prefixed_local_addr_p (XEXP (mem, 0), mode, iform);
+}
+
+/* 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)
+    return false;
+
+  if (!SYMBOL_REF_P (src) && !LABEL_REF_P (src) && GET_CODE (src) != CONST)
+    return false;
+
+  /* Look for either pc-relative addresses of local symbols that we can use a
+     PLA to load or external symbols that we can load a GOT address via a
+     pc-relative load.  */
+  return pcrel_addr_p (src, true, true, PCREL_NULL);
+}
Index: gcc/config/rs6000/rs6000-protos.h
===================================================================
--- gcc/config/rs6000/rs6000-protos.h	(revision 274174)
+++ gcc/config/rs6000/rs6000-protos.h	(working copy)
@@ -245,7 +245,14 @@  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);
+/* Declare functions in rs6000-prefixed.c  */
+#ifdef RTX_CODE
+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 *, const char *);
+void rs6000_final_prescan_insn (rtx_insn *);
+#endif
 
 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 274174)
+++ gcc/config/rs6000/rs6000.c	(working copy)
@@ -9815,6 +9815,17 @@  rs6000_emit_move (rtx dest, rtx source, machine_mo
 	  return;
 	}
 
+      /* Handle pc-relative addresses, either external symbols or internal
+	 within the function.  */
+      if (TARGET_PCREL)
+	{
+	  if (pcrel_addr_p (operands[1], true, true, PCREL_NULL))
+	    {
+	      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))
Index: gcc/config/rs6000/rs6000.h
===================================================================
--- gcc/config/rs6000/rs6000.h	(revision 274173)
+++ gcc/config/rs6000/rs6000.h	(working copy)
@@ -2572,3 +2572,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);					\
+  }									\
+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, OPCODE);			\
+    }									\
+  while (0)
Index: gcc/config/rs6000/rs6000.md
===================================================================
--- gcc/config/rs6000/rs6000.md	(revision 274174)
+++ gcc/config/rs6000/rs6000.md	(working copy)
@@ -269,9 +269,48 @@ 
 			  x		; Indexed addressing
 			  prefixed])	; Prefixed instruction
 
-;; 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.
 (define_attr "cpu"
@@ -9888,6 +9927,25 @@ 
   operands[6] = gen_rtx_PARALLEL (VOIDmode, p);
 })
 
+;; Load up a pc-relative address.  ASM_OUTPUT_OPCODE will emit the initial "p".
+(define_insn "*pcrel_addr"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=b*r")
+	(match_operand:DI 1 "pcrel_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.
+(define_insn "*pcrel_ext_addr"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=b*r")
+	(match_operand:DI 1 "pcrel_external_address"))]
+  "TARGET_PCREL"
+  "ld %0,%a1"
+  [(set_attr "prefixed" "yes")])
+
 ;; TOC register handling.
 
 ;; Code to initialize the TOC register...
Index: gcc/config/rs6000/t-rs6000
===================================================================
--- gcc/config/rs6000/t-rs6000	(revision 274173)
+++ gcc/config/rs6000/t-rs6000	(working copy)
@@ -47,6 +47,10 @@  rs6000-call.o: $(srcdir)/config/rs6000/rs6000-call
 	$(COMPILE) $<
 	$(POSTCOMPILE)
 
+rs6000-prefixed.o: $(srcdir)/config/rs6000/rs6000-prefixed.c
+	$(COMPILE) $<
+	$(POSTCOMPILE)
+
 $(srcdir)/config/rs6000/rs6000-tables.opt: $(srcdir)/config/rs6000/genopt.sh \
   $(srcdir)/config/rs6000/rs6000-cpus.def
 	$(SHELL) $(srcdir)/config/rs6000/genopt.sh $(srcdir)/config/rs6000 > \
Index: gcc/config.gcc
===================================================================
--- gcc/config.gcc	(revision 274173)
+++ gcc/config.gcc	(working copy)
@@ -500,6 +500,7 @@  or1k*-*-*)
 powerpc*-*-*)
 	cpu_type=rs6000
 	extra_objs="rs6000-string.o rs6000-p8swap.o rs6000-logue.o rs6000-call.o"
+	extra_objs="${extra_objs} rs6000-prefixed.o"
 	extra_headers="ppc-asm.h altivec.h htmintrin.h htmxlintrin.h"
 	extra_headers="${extra_headers} bmi2intrin.h bmiintrin.h"
 	extra_headers="${extra_headers} xmmintrin.h mm_malloc.h emmintrin.h"
@@ -514,6 +515,7 @@  powerpc*-*-*)
 	esac
 	extra_options="${extra_options} g.opt fused-madd.opt rs6000/rs6000-tables.opt"
 	target_gtfiles="$target_gtfiles \$(srcdir)/config/rs6000/rs6000-logue.c \$(srcdir)/config/rs6000/rs6000-call.c"
+	target_gtfiles="$target_gtfiles \$(srcdir)/config/rs6000/rs6000-prefixed.c"
 	;;
 pru-*-*)
 	cpu_type=pru
@@ -526,7 +528,9 @@  riscv*)
 rs6000*-*-*)
 	extra_options="${extra_options} g.opt fused-madd.opt rs6000/rs6000-tables.opt"
 	extra_objs="rs6000-string.o rs6000-p8swap.o rs6000-logue.o rs6000-call.o"
+	extra_objs="${extra_objs} rs6000-prefixed.o"
 	target_gtfiles="$target_gtfiles \$(srcdir)/config/rs6000/rs6000-logue.c \$(srcdir)/config/rs6000/rs6000-call.c"
+	target_gtfiles="$target_gtfiles \$(srcdir)/config/rs6000/rs6000-prefixed.c"
 	;;
 sparc*-*-*)
 	cpu_type=sparc