This is patch #17 that adds support for automatically detecting prefixed
instructions and changing the length to 12 bytes, and emitting a 'p' before the
instruction.  At the moment, only loading up pc-relative addresses will have
the prefixed RTL set, since I have enabled the prefixed instruction support for
other modes (that will be in the next patch).

It is somewhat different than the previous scheme in terms of the details (but
similar in spirit) that we talked about in private mail.

With this scheme, there are 3 attributes:

The main RTL attribute is "prefixed".  It looks at the "type" RTL attribute, to
see if it is a load of some sort, a store of some sort, or something that may
use PADDI to load up a constant, address, or add a large constant.  Because
there are functions that set the type, but don't have standard operands
(mov<mode>_update1, etc.) the three functions that validate whether the
instruction is prefixed, only look at the INSN and not at the OPERANDS.

The "length" RTL attribute looks the "prefixed" attribute and sets the length
from either the "prefixed_length" or "nonprefixed_length" attributes.  These
attributes default to 12 bytes and 4 bytes respectively.  The idea is this can
simplify the attributes for insn patterns that can generate multiple
instructions, so that they don't have to have if_then_else's to set the types.

I have this patch queued up to do bootstrap and make check.  Assuming there are
no regressions, can I check this into the FSF trunk?

2019-07-25  Michael Meissner  <meiss...@linux.ibm.com>

        * config/rs6000/rs6000-prefixed.c: New file.
        * config/rs6000/rs6000-protos.h (prefixed_addr_reg_p): Add
        declaration.
        (prefixed_load_p): Add declaration.
        (prefixed_store_p): Add declaration.
        (prefixed_paddi_p): Add declaration.
        (rs6000_asm_output_opcode): Add declaration.
        (rs6000_final_prescan_insn): Update calling signature.
        * config/rs6000/rs6000.c (prefixed_addr_reg_p): New function.
        * config/rs6000/rs6000.h (FINAL_PRESCAN_INSN): New target hook.
        (ASM_OUTPUT_OPCODE): New target hook.
        * config/rs6000/rs6000.md (prefixed attribute): New RTL attribute.
        (prefixed_length attribute): New RTL attribute.
        (non_prefixed_length attribute): New RTL attribute.
        (length attribute): Set length based on prefixed, prefixed_length,
        and non_prefixed_length attributes.
        (pcrel_addr): Update to use prefixed RTL attribute.
        (pcrel_ext_addr): Update to use prefixed RTL attribute.
        * config/rs6000/t-rs6000: Add rs6000-prefixed.o build rules.
        * config.gcc (powerpc*-*-*): Add rs6000-prefixed.c.
        (rs6000*-*-*): Add rs6000-prefixed.c.

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,190 @@
+/* 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.  */
+
+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);
+
+  /* Allow sign/zero/float extend as part of the load.  Note, LWA is a DS-form
+     instruction, while LWZ is D-form.  */
+  bool sign_p = false;
+  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);
+
+  /* If we are doing a sign extend operation on SImode (i.e. LWA), override the
+     mode to be DImode so the DS instruction format is used.  */
+  if (mode == SImode && sign_p)
+    {
+      unsigned r = reg_or_subregno (reg);
+      if (r >= FIRST_PSEUDO_REGISTER
+         || IN_RANGE (r, FIRST_GPR_REGNO, LAST_GPR_REGNO))
+       mode = DImode;
+    }
+
+  rtx addr = XEXP (mem, 0);
+  return prefixed_addr_reg_p (addr, reg, mode);
+}
+
+/* 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;
+
+  rtx addr = XEXP (mem, 0);
+  return prefixed_addr_reg_p (addr, reg, GET_MODE (mem));
+}
+
+/* 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;
+
+  const unsigned addr_flags = (ADDR_VALIDATE_PCREL_LOCAL
+                              | ADDR_VALIDATE_PCREL_EXT);
+
+  return addr_validate_p (src, INSN_FORM_PREFIXED, addr_flags);
+}
Index: gcc/config/rs6000/rs6000.c
===================================================================
--- gcc/config/rs6000/rs6000.c  (revision 273798)
+++ gcc/config/rs6000/rs6000.c  (working copy)
@@ -13861,6 +13861,58 @@
 
   return addr_validate_p (addr, reg_addr[mode].default_insn_form, addr_flags);
 }
+
+/* Return whether a memory address (ADDR) is a prefixed instruction when it is
+   loading or storing a given hard register (REG) with a machine mode (MODE).
+   Usually the mode is the mode of the memory operation, but in the case of
+   LWA, it is DImode instead of SImode to force using the DS instruction
+   format.
+
+   The physical register can determine whether a particular operation can use
+   the tradtional encodeing or it needs a prefixed encoding.  For example, LFD
+   is uses the D instruction encoding, while LXSD uses the DS instruction
+   encoding.  */
+
+bool
+prefixed_addr_reg_p (rtx addr, rtx reg, machine_mode mode)
+{
+  enum INSN_FORM insn_form;
+
+  if (!TARGET_PREFIXED_ADDR || !mode_supports_prefixed_address_p (mode))
+    return false;
+
+  unsigned int r = reg_or_subregno (reg);
+
+  /* If we have a pseudo, use the default instruction format.  */
+  if (r >= FIRST_PSEUDO_REGISTER)
+    insn_form = reg_addr[mode].default_insn_form;
+
+  else
+    {
+      addr_mask_type mask = 0;
+
+      /* If we have a hard register, use the address mask from the reload
+        register class to create the instruction format.  */
+      if (IN_RANGE (r, FIRST_GPR_REGNO, LAST_GPR_REGNO))
+       mask = reg_addr[mode].addr_mask[RELOAD_REG_GPR];
+
+      else if (IN_RANGE (r, FIRST_FPR_REGNO, LAST_FPR_REGNO))
+       mask = reg_addr[mode].addr_mask[RELOAD_REG_FPR];
+
+      else if (IN_RANGE (r, FIRST_ALTIVEC_REGNO, LAST_ALTIVEC_REGNO))
+       mask = reg_addr[mode].addr_mask[RELOAD_REG_VMX];
+
+      else
+       gcc_unreachable ();
+
+      insn_form = addr_mask_to_insn_form (mask);
+    }
+
+  const unsigned addr_flags = (ADDR_VALIDATE_REG_34BIT
+                              | ADDR_VALIDATE_PCREL_LOCAL);
+
+  return addr_validate_p (addr, insn_form, addr_flags);
+}
 
 #if defined (HAVE_GAS_HIDDEN) && !TARGET_MACHO
 /* Emit an assembler directive to set symbol visibility for DECL to
Index: gcc/config/rs6000/rs6000.h
===================================================================
--- gcc/config/rs6000/rs6000.h  (revision 273786)
+++ gcc/config/rs6000/rs6000.h  (working copy)
@@ -2557,3 +2557,24 @@
   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 273786)
+++ gcc/config/rs6000/rs6000.md (working copy)
@@ -267,9 +267,48 @@
 ;; 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.
 (define_attr "cpu"
@@ -9885,14 +9924,13 @@
   operands[6] = gen_rtx_PARALLEL (VOIDmode, p);
 })
 
-;; Load up a pc-relative address.  The length is 12 because the assembler may
-;; need to add a NOP to align the PADDI so it doesn't cross a 32-btye boundary.
+;; 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"
-  "pla %0,%a1"
-  [(set_attr "length" "12")])
+  "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
@@ -9902,8 +9940,8 @@
   [(set (match_operand:DI 0 "gpc_reg_operand" "=b*r")
        (match_operand:DI 1 "pcrel_external_address"))]
   "TARGET_PCREL"
-  "pld %0,%a1"
-  [(set_attr "length" "12")])
+  "ld %0,%a1"
+  [(set_attr "prefixed" "yes")])
 
 ;; TOC register handling.
 
Index: gcc/config/rs6000/t-rs6000
===================================================================
--- gcc/config/rs6000/t-rs6000  (revision 273786)
+++ gcc/config/rs6000/t-rs6000  (working copy)
@@ -47,6 +47,10 @@
        $(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 273786)
+++ gcc/config.gcc      (working copy)
@@ -500,6 +500,7 @@
 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 @@
        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 @@
 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

-- 
Michael Meissner, IBM
IBM, M/S 2506R, 550 King Street, Littleton, MA 01460-6245, USA
email: meiss...@linux.ibm.com, phone: +1 (978) 899-4797

Reply via email to