public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 0/5] PowerPC -mfuture support
@ 2019-05-24  1:31 Alan Modra
  2019-05-24  1:33 ` [PATCH 1/5] PowerPC add initial -mfuture instruction support Alan Modra
                   ` (4 more replies)
  0 siblings, 5 replies; 13+ messages in thread
From: Alan Modra @ 2019-05-24  1:31 UTC (permalink / raw)
  To: binutils

This patch series adds support for pc-relative addressing on PowerPC.
It's part of a much larger patch that I'm not yet allowed to make
public, for a possible future IBM POWER architecture processor.  I'm
instructed to note that this does not imply any committment to include
these instructions in a future Book I tied to any IBM product.

Also, until this all becomes properly released with an updated ABI
document, new relocation numbers and semantics might change in ways
that break object file compatibility.

Alan Modra (3):
  PowerPC relocations for prefix insns
  PowerPC GOT_PCREL34 optimisation
  PowerPC notoc linkage stubs

Peter Bergner (2):
  PowerPC add initial -mfuture instruction support
  PowerPC D-form prefixed loads and stores

 bfd/ChangeLog                        |  56 ++
 bfd/bfd-in2.h                        |  17 +
 bfd/elf64-ppc.c                      | 950 ++++++++++++++++++++++++---
 bfd/libbfd.h                         |  17 +
 bfd/reloc.c                          |  34 +
 binutils/ChangeLog                   |   4 +
 binutils/objdump.c                   |   3 +-
 gas/ChangeLog                        |  50 ++
 gas/config/tc-ppc.c                  | 370 +++++++++--
 gas/config/tc-ppc.h                  |  24 +-
 gas/messages.c                       |  18 +-
 gas/testsuite/gas/ppc/ppc.exp        |   3 +
 gas/testsuite/gas/ppc/prefix-align.d |  30 +
 gas/testsuite/gas/ppc/prefix-align.s |  21 +
 gas/testsuite/gas/ppc/prefix-pcrel.d | 235 +++++++
 gas/testsuite/gas/ppc/prefix-pcrel.s | 121 ++++
 gas/testsuite/gas/ppc/prefix-reloc.d |  35 +
 gas/testsuite/gas/ppc/prefix-reloc.s |  13 +
 include/ChangeLog                    |  20 +
 include/dis-asm.h                    |   2 +
 include/elf/ppc64.h                  |  24 +
 include/opcode/ppc.h                 |  18 +
 ld/ChangeLog                         |  13 +
 ld/testsuite/ld-powerpc/notoc2.d     |  29 +
 ld/testsuite/ld-powerpc/notoc2.s     |  13 +
 ld/testsuite/ld-powerpc/pcrelopt.d   |  89 +++
 ld/testsuite/ld-powerpc/pcrelopt.s   | 119 ++++
 ld/testsuite/ld-powerpc/pcrelopt.sec |   6 +
 ld/testsuite/ld-powerpc/powerpc.exp  |   6 +
 opcodes/ChangeLog                    |  27 +
 opcodes/ppc-dis.c                    |  93 ++-
 opcodes/ppc-opc.c                    | 212 +++++-
 32 files changed, 2498 insertions(+), 174 deletions(-)
 create mode 100644 gas/testsuite/gas/ppc/prefix-align.d
 create mode 100644 gas/testsuite/gas/ppc/prefix-align.s
 create mode 100644 gas/testsuite/gas/ppc/prefix-pcrel.d
 create mode 100644 gas/testsuite/gas/ppc/prefix-pcrel.s
 create mode 100644 gas/testsuite/gas/ppc/prefix-reloc.d
 create mode 100644 gas/testsuite/gas/ppc/prefix-reloc.s
 create mode 100644 ld/testsuite/ld-powerpc/notoc2.d
 create mode 100644 ld/testsuite/ld-powerpc/notoc2.s
 create mode 100644 ld/testsuite/ld-powerpc/pcrelopt.d
 create mode 100644 ld/testsuite/ld-powerpc/pcrelopt.s
 create mode 100644 ld/testsuite/ld-powerpc/pcrelopt.sec

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/5] PowerPC add initial -mfuture instruction support
  2019-05-24  1:31 [PATCH 0/5] PowerPC -mfuture support Alan Modra
@ 2019-05-24  1:33 ` Alan Modra
  2019-06-02  6:33   ` Jan Beulich
  2019-06-20 12:09   ` Jan Beulich
  2019-05-24  1:34 ` [PATCH 2/5] PowerPC D-form prefixed loads and stores Alan Modra
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 13+ messages in thread
From: Alan Modra @ 2019-05-24  1:33 UTC (permalink / raw)
  To: binutils

This patch adds initial 64-bit insn assembler/disassembler support.
The only instruction added is "pnop" along with the automatic aligning
of prefix instruction so they do not cross 64-byte boundaries.

include/
	* dis-asm.h (WIDE_OUTPUT): Define.
	* opcode/ppc.h (prefix_opcodes, prefix_num_opcodes): Declare.
	(PPC_OPCODE_POWERXX, PPC_GET_PREFIX, PPC_GET_SUFFIX),
	(PPC_PREFIX_P, PPC_PREFIX_SEG): Define.
opcodes/
	* ppc-dis.c (ppc_opts): Add "future" entry.
	(PREFIX_OPCD_SEGS): Define.
	(prefix_opcd_indices): New array.
	(disassemble_init_powerpc): Initialize prefix_opcd_indices.
	(lookup_prefix): New function.
	(print_insn_powerpc): Handle 64-bit prefix instructions.
	* ppc-opc.c (PREFIX_OP, PREFIX_FORM, SUFFIX_MASK, PREFIX_MASK),
	(PMRR, POWERXX): Define.
	(prefix_opcodes): New instruction table.
	(prefix_num_opcodes): New constant.
binutils/
	* objdump.c (disassemble_bytes): Set WIDE_OUTPUT in flags.
gas/
	* config/tc-ppc.c (ppc_setup_opcodes): Handle prefix_opcodes.
	(struct insn_label_list): New.
	(insn_labels, free_insn_labels): New variables.
	(ppc_record_label, ppc_clear_labels, ppc_start_line_hook): New funcs.
	(ppc_frob_label, ppc_new_dot_label): Move functions earlier in file
	and call ppc_record_label.
	(md_assemble): Handle 64-bit prefix instructions.  Align labels
	that are on the same line as a prefix instruction.
	* config/tc-ppc.h (tc_frob_label, ppc_frob_label): Move to
	later in the file.
	(md_start_line_hook): Define.
	(ppc_start_line_hook): Declare.
	* testsuite/gas/ppc/prefix-align.d,
	* testsuite/gas/ppc/prefix-align.s: New test.
	* testsuite/gas/ppc/ppc.exp: Run new test.

diff --git a/binutils/objdump.c b/binutils/objdump.c
index 05d503e514..7381e4885d 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -1946,7 +1946,8 @@ disassemble_bytes (struct disassemble_info * inf,
 	      inf->stream = &sfile;
 	      inf->bytes_per_line = 0;
 	      inf->bytes_per_chunk = 0;
-	      inf->flags = disassemble_all ? DISASSEMBLE_DATA : 0;
+	      inf->flags = ((disassemble_all ? DISASSEMBLE_DATA : 0)
+			    | (wide_output ? WIDE_OUTPUT : 0));
 	      if (machine)
 		inf->flags |= USER_SPECIFIED_MACHINE_TYPE;
 
diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c
index d5d51f78f3..4abb5b8a31 100644
--- a/gas/config/tc-ppc.c
+++ b/gas/config/tc-ppc.c
@@ -1673,6 +1673,50 @@ ppc_setup_opcodes (void)
     for (op = powerpc_opcodes; op < op_end; op++)
       hash_insert (ppc_hash, op->name, (void *) op);
 
+  op_end = prefix_opcodes + prefix_num_opcodes;
+  for (op = prefix_opcodes; op < op_end; op++)
+    {
+      if (ENABLE_CHECKING)
+	{
+	  unsigned int new_opcode = PPC_PREFIX_SEG (op[0].opcode);
+
+#ifdef PRINT_OPCODE_TABLE
+	  printf ("%-14s\t#%04u\tmajor op/2: 0x%x\top: 0x%llx\tmask: 0x%llx\tflags: 0x%llx\n",
+		  op->name, (unsigned int) (op - prefix_opcodes),
+		  new_opcode, (unsigned long long) op->opcode,
+		  (unsigned long long) op->mask, (unsigned long long) op->flags);
+#endif
+
+	  /* The major opcodes had better be sorted.  Code in the disassembler
+	     assumes the insns are sorted according to major opcode.  */
+	  if (op != prefix_opcodes
+	      && new_opcode < PPC_PREFIX_SEG (op[-1].opcode))
+	    {
+	      as_bad (_("major opcode is not sorted for %s"), op->name);
+	      bad_insn = TRUE;
+	    }
+	  bad_insn |= insn_validate (op);
+	}
+
+      if ((ppc_cpu & op->flags) != 0
+	  && !(ppc_cpu & op->deprecated))
+	{
+	  const char *retval;
+
+	  retval = hash_insert (ppc_hash, op->name, (void *) op);
+	  if (retval != NULL)
+	    {
+	      as_bad (_("duplicate instruction %s"),
+		      op->name);
+	      bad_insn = TRUE;
+	    }
+	}
+    }
+
+  if ((ppc_cpu & PPC_OPCODE_ANY) != 0)
+    for (op = prefix_opcodes; op < op_end; op++)
+      hash_insert (ppc_hash, op->name, (void *) op);
+
   op_end = vle_opcodes + vle_num_opcodes;
   for (op = vle_opcodes; op < op_end; op++)
     {
@@ -2740,6 +2784,90 @@ ppc_apuinfo_section_add (unsigned int apu, unsigned int version)
 #undef APUID
 #endif
 \f
+/* Various frobbings of labels and their addresses.  */
+
+/* Symbols labelling the current insn.  */
+struct insn_label_list
+{
+  struct insn_label_list *next;
+  symbolS *label;
+};
+
+static struct insn_label_list *insn_labels;
+static struct insn_label_list *free_insn_labels;
+
+static void
+ppc_record_label (symbolS *sym)
+{
+  struct insn_label_list *l;
+
+  if (free_insn_labels == NULL)
+    l = XNEW (struct insn_label_list);
+  else
+    {
+      l = free_insn_labels;
+      free_insn_labels = l->next;
+    }
+
+  l->label = sym;
+  l->next = insn_labels;
+  insn_labels = l;
+}
+
+static void
+ppc_clear_labels (void)
+{
+  while (insn_labels != NULL)
+    {
+      struct insn_label_list *l = insn_labels;
+      insn_labels = l->next;
+      l->next = free_insn_labels;
+      free_insn_labels = l;
+    }
+}
+
+void
+ppc_start_line_hook (void)
+{
+  ppc_clear_labels ();
+}
+
+void
+ppc_new_dot_label (symbolS *sym)
+{
+  ppc_record_label (sym);
+#ifdef OBJ_XCOFF
+  /* Anchor this label to the current csect for relocations.  */
+  symbol_get_tc (sym)->within = ppc_current_csect;
+#endif
+}
+
+void
+ppc_frob_label (symbolS *sym)
+{
+  ppc_record_label (sym);
+
+#ifdef OBJ_XCOFF
+  /* Set the class of a label based on where it is defined.  This handles
+     symbols without suffixes.  Also, move the symbol so that it follows
+     the csect symbol.  */
+  if (ppc_current_csect != (symbolS *) NULL)
+    {
+      if (symbol_get_tc (sym)->symbol_class == -1)
+	symbol_get_tc (sym)->symbol_class = symbol_get_tc (ppc_current_csect)->symbol_class;
+
+      symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+      symbol_append (sym, symbol_get_tc (ppc_current_csect)->within,
+		     &symbol_rootP, &symbol_lastP);
+      symbol_get_tc (ppc_current_csect)->within = sym;
+      symbol_get_tc (sym)->within = ppc_current_csect;
+    }
+#endif
+
+#ifdef OBJ_ELF
+  dwarf2_emit_label (sym);
+#endif
+}
 
 /* We need to keep a list of fixups.  We can't simply generate them as
    we go, because that would require us to first create the frag, and
@@ -3074,6 +3202,7 @@ md_assemble (char *str)
       else
 	ppc_macro (s, macro);
 
+      ppc_clear_labels ();
       return;
     }
 
@@ -3828,14 +3957,50 @@ md_assemble (char *str)
   if ((frag_now_fix () & addr_mask) != 0)
     as_bad (_("instruction address is not a multiple of %d"), addr_mask + 1);
 
-  /* Differentiate between two and four byte insns.  */
+  /* Differentiate between two, four, and eight byte insns.  */
   insn_length = 4;
   if ((ppc_cpu & PPC_OPCODE_VLE) != 0 && PPC_OP_SE_VLE (insn))
     insn_length = 2;
+  else if ((opcode->flags & PPC_OPCODE_POWERXX) != 0
+	   && PPC_PREFIX_P (insn))
+    {
+      struct insn_label_list *l;
+
+      insn_length = 8;
+
+      /* 8-byte prefix instructions are not allowed to cross 64-byte
+	 boundaries.  */
+      frag_align_code (6, 4);
+      record_alignment (now_seg, 6);
+
+      /* Update "dot" in any expressions used by this instruction, and
+	 a label attached to the instruction.  By "attached" we mean
+	 on the same source line as the instruction and without any
+	 intervening semicolons.  */
+      dot_value = frag_now_fix ();
+      dot_frag = frag_now;
+      for (l = insn_labels; l != NULL; l = l->next)
+	{
+	  symbol_set_frag (l->label, dot_frag);
+	  S_SET_VALUE (l->label, dot_value);
+	}
+    }
+
+  ppc_clear_labels ();
 
   f = frag_more (insn_length);
   frag_now->insn_addr = addr_mask;
-  md_number_to_chars (f, insn, insn_length);
+
+  /* The prefix part of an 8-byte instruction always occupies the lower
+     addressed word in a doubleword, regardless of endianness.  */
+  if (!target_big_endian && insn_length == 8)
+    {
+      md_number_to_chars (f, PPC_GET_PREFIX (insn), 4);
+      md_number_to_chars (f + 4, PPC_GET_SUFFIX (insn), 4);
+    }
+  else
+    md_number_to_chars (f, insn, insn_length);
+
   last_insn = insn;
   last_seg = now_seg;
   last_subseg = now_subseg;
@@ -6118,30 +6283,6 @@ ppc_symbol_new_hook (symbolS *sym)
     as_bad (_("unrecognized symbol suffix"));
 }
 
-/* Set the class of a label based on where it is defined.  This
-   handles symbols without suffixes.  Also, move the symbol so that it
-   follows the csect symbol.  */
-
-void
-ppc_frob_label (symbolS *sym)
-{
-  if (ppc_current_csect != (symbolS *) NULL)
-    {
-      if (symbol_get_tc (sym)->symbol_class == -1)
-	symbol_get_tc (sym)->symbol_class = symbol_get_tc (ppc_current_csect)->symbol_class;
-
-      symbol_remove (sym, &symbol_rootP, &symbol_lastP);
-      symbol_append (sym, symbol_get_tc (ppc_current_csect)->within,
-		     &symbol_rootP, &symbol_lastP);
-      symbol_get_tc (ppc_current_csect)->within = sym;
-      symbol_get_tc (sym)->within = ppc_current_csect;
-    }
-
-#ifdef OBJ_ELF
-  dwarf2_emit_label (sym);
-#endif
-}
-
 /* This variable is set by ppc_frob_symbol if any absolute symbols are
    seen.  It tells ppc_adjust_symtab whether it needs to look through
    the symbols.  */
@@ -6673,14 +6814,6 @@ ppc_force_relocation (fixS *fix)
 
   return generic_force_reloc (fix);
 }
-
-void
-ppc_new_dot_label (symbolS *sym)
-{
-  /* Anchor this label to the current csect for relocations.  */
-  symbol_get_tc (sym)->within = ppc_current_csect;
-}
-
 #endif /* OBJ_XCOFF */
 
 #ifdef OBJ_ELF
diff --git a/gas/config/tc-ppc.h b/gas/config/tc-ppc.h
index 84217eabe0..08e381e293 100644
--- a/gas/config/tc-ppc.h
+++ b/gas/config/tc-ppc.h
@@ -171,10 +171,6 @@ extern char *ppc_canonicalize_symbol_name (char *);
 #define tc_symbol_new_hook(sym) ppc_symbol_new_hook (sym)
 extern void ppc_symbol_new_hook (symbolS *);
 
-/* Set the symbol class of a label based on the csect.  */
-#define tc_frob_label(sym) ppc_frob_label (sym)
-extern void ppc_frob_label (symbolS *);
-
 /* TOC relocs requires special handling.  */
 #define tc_fix_adjustable(FIX) ppc_fix_adjustable (FIX)
 extern int ppc_fix_adjustable (struct fix *);
@@ -206,11 +202,11 @@ do {								\
 extern void ppc_xcoff_end (void);
 #define md_end ppc_xcoff_end
 
+#endif /* OBJ_XCOFF */
+
 #define tc_new_dot_label(sym) ppc_new_dot_label (sym)
 extern void ppc_new_dot_label (symbolS *);
 
-#endif /* OBJ_XCOFF */
-
 extern const char       ppc_symbol_chars[];
 #define tc_symbol_chars ppc_symbol_chars
 
@@ -282,6 +278,14 @@ extern int ppc_force_relocation (struct fix *);
 
 #define TC_VALIDATE_FIX_SUB(FIX, SEG) 0
 
+/* Various frobbings of labels and their addresses.  */
+#define md_start_line_hook() ppc_start_line_hook ()
+extern void ppc_start_line_hook (void);
+
+/* Set the symbol class of a label based on the csect.  */
+#define tc_frob_label(sym) ppc_frob_label (sym)
+extern void ppc_frob_label (symbolS *);
+
 /* call md_pcrel_from_section, not md_pcrel_from */
 #define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section(FIX, SEC)
 extern long md_pcrel_from_section (struct fix *, segT);
diff --git a/gas/testsuite/gas/ppc/ppc.exp b/gas/testsuite/gas/ppc/ppc.exp
index 62d67a391c..6be042bf26 100644
--- a/gas/testsuite/gas/ppc/ppc.exp
+++ b/gas/testsuite/gas/ppc/ppc.exp
@@ -114,3 +114,4 @@ run_dump_test "vsx2"
 run_dump_test "vsx3"
 run_dump_test "htm"
 run_dump_test "titan"
+run_dump_test "prefix-align"
diff --git a/gas/testsuite/gas/ppc/prefix-align.d b/gas/testsuite/gas/ppc/prefix-align.d
new file mode 100644
index 0000000000..b2e1b8374d
--- /dev/null
+++ b/gas/testsuite/gas/ppc/prefix-align.d
@@ -0,0 +1,30 @@
+#as: -mfuture
+#objdump: -dr -Mfuture
+#name: POWERXX alignment of labels test
+
+.*
+
+
+Disassembly of section \.text:
+
+0+00 <_start>:
+   0:	(48 00 00 3c|3c 00 00 48) 	b       3c <_start\+0x3c>
+   4:	(48 00 00 3c|3c 00 00 48) 	b       40 <_start\+0x40>
+   8:	(48 00 00 40|40 00 00 48) 	b       48 <_start\+0x48>
+   c:	(7f e0 00 08|08 00 e0 7f) 	trap
+  10:	(7f e0 00 08|08 00 e0 7f) 	trap
+  14:	(7f e0 00 08|08 00 e0 7f) 	trap
+  18:	(7f e0 00 08|08 00 e0 7f) 	trap
+  1c:	(7f e0 00 08|08 00 e0 7f) 	trap
+  20:	(7f e0 00 08|08 00 e0 7f) 	trap
+  24:	(7f e0 00 08|08 00 e0 7f) 	trap
+  28:	(7f e0 00 08|08 00 e0 7f) 	trap
+  2c:	(7f e0 00 08|08 00 e0 7f) 	trap
+  30:	(7f e0 00 08|08 00 e0 7f) 	trap
+  34:	(7f e0 00 08|08 00 e0 7f) 	trap
+  38:	(7f e0 00 08|08 00 e0 7f) 	trap
+  3c:	(60 00 00 00|00 00 00 60) 	nop
+  40:	(07 00 00 00|00 00 00 07) 	pnop
+  44:	(00 00 00 00|00 00 00 00) 
+  48:	(4e 80 00 20|20 00 80 4e) 	blr
+#pass
diff --git a/gas/testsuite/gas/ppc/prefix-align.s b/gas/testsuite/gas/ppc/prefix-align.s
new file mode 100644
index 0000000000..fd0043958b
--- /dev/null
+++ b/gas/testsuite/gas/ppc/prefix-align.s
@@ -0,0 +1,21 @@
+	.text
+_start:
+	b 1f;
+	b 2f;
+	b 3f;
+	trap
+	trap
+	trap
+	trap
+	trap
+	trap
+	trap
+	trap
+	trap
+	trap
+	trap
+	trap
+1:
+2:	pnop
+3:
+	blr
diff --git a/include/dis-asm.h b/include/dis-asm.h
index 4e1263c90e..b4d5025811 100644
--- a/include/dis-asm.h
+++ b/include/dis-asm.h
@@ -116,6 +116,8 @@ typedef struct disassemble_info
   /* Set if the user has specifically set the machine type encoded in the
      mach field of this structure.  */
 #define USER_SPECIFIED_MACHINE_TYPE (1 << 29)
+  /* Set if the user has requested wide output.  */
+#define WIDE_OUTPUT (1 << 28)
 
   /* Use internally by the target specific disassembly code.  */
   void *private_data;
diff --git a/include/opcode/ppc.h b/include/opcode/ppc.h
index 7a0bc6030b..314b9b49ff 100644
--- a/include/opcode/ppc.h
+++ b/include/opcode/ppc.h
@@ -68,6 +68,8 @@ struct powerpc_opcode
    instructions.  */
 extern const struct powerpc_opcode powerpc_opcodes[];
 extern const unsigned int powerpc_num_opcodes;
+extern const struct powerpc_opcode prefix_opcodes[];
+extern const unsigned int prefix_num_opcodes;
 extern const struct powerpc_opcode vle_opcodes[];
 extern const unsigned int vle_num_opcodes;
 extern const struct powerpc_opcode spe2_opcodes[];
@@ -226,6 +228,9 @@ extern const unsigned int spe2_num_opcodes;
 /* Opcode is supported by EFS2.  */
 #define PPC_OPCODE_EFS2	    0x200000000000ull
 
+/* Opcode is only supported by powerxx architecture.  */
+#define PPC_OPCODE_POWERXX  0x400000000000ull
+
 /* A macro to extract the major opcode from an instruction.  */
 #define PPC_OP(i) (((i) >> 26) & 0x3f)
 
@@ -243,6 +248,19 @@ extern const unsigned int spe2_num_opcodes;
 
 /* A macro to convert a SPE2 extended opcode to a SPE2 xopcode segment.  */
 #define SPE2_XOP_TO_SEG(i) ((i) >> 7)
+
+/* A macro to extract the prefix word from an 8-byte PREFIX instruction.  */
+#define PPC_GET_PREFIX(i) (((i) >> 32) & ((1LL << 32) - 1))
+
+/* A macro to extract the suffix word from an 8-byte PREFIX instruction.  */
+#define PPC_GET_SUFFIX(i) ((i) & ((1LL << 32) - 1))
+
+/* A macro to determine whether insn I is an 8-byte prefix instruction.  */
+#define PPC_PREFIX_P(i) (PPC_OP (PPC_GET_PREFIX (i)) == 0x1)
+
+/* A macro used to hash 8-byte PREFIX instructions.  */
+#define PPC_PREFIX_SEG(i) (PPC_OP (i) >> 1)
+
 \f
 /* The operands table is an array of struct powerpc_operand.  */
 
diff --git a/opcodes/ppc-dis.c b/opcodes/ppc-dis.c
index e9e3b3621a..9334be2138 100644
--- a/opcodes/ppc-dis.c
+++ b/opcodes/ppc-dis.c
@@ -185,6 +185,11 @@ struct ppc_mopt ppc_opts[] = {
 		| PPC_OPCODE_POWER7 | PPC_OPCODE_POWER8 | PPC_OPCODE_POWER9
 		| PPC_OPCODE_ALTIVEC | PPC_OPCODE_VSX),
     0 },
+  { "future",  (PPC_OPCODE_PPC | PPC_OPCODE_ISEL | PPC_OPCODE_64
+		| PPC_OPCODE_POWER4 | PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6
+		| PPC_OPCODE_POWER7 | PPC_OPCODE_POWER8 | PPC_OPCODE_POWER9
+		| PPC_OPCODE_POWERXX | PPC_OPCODE_ALTIVEC | PPC_OPCODE_VSX),
+    0 },
   { "ppc",     PPC_OPCODE_PPC,
     0 },
   { "ppc32",   PPC_OPCODE_PPC,
@@ -376,6 +381,8 @@ powerpc_init_dialect (struct disassemble_info *info)
 
 #define PPC_OPCD_SEGS (1 + PPC_OP (-1))
 static unsigned short powerpc_opcd_indices[PPC_OPCD_SEGS + 1];
+#define PREFIX_OPCD_SEGS (1 + PPC_PREFIX_SEG (-1))
+static unsigned short prefix_opcd_indices[PPC_OPCD_SEGS+1];
 #define VLE_OPCD_SEGS (1 + VLE_OP_TO_SEG (VLE_OP (-1, 0xffff)))
 static unsigned short vle_opcd_indices[VLE_OPCD_SEGS + 1];
 #define SPE2_OPCD_SEGS (1 + SPE2_XOP_TO_SEG (SPE2_XOP (-1)))
@@ -400,6 +407,15 @@ disassemble_init_powerpc (struct disassemble_info *info)
 	      break;
 	}
 
+      /* 64-bit prefix opcodes */
+      for (seg = 0, idx = 0; seg <= PREFIX_OPCD_SEGS; seg++)
+	{
+	  prefix_opcd_indices[seg] = idx;
+	  for (; idx < prefix_num_opcodes; idx++)
+	    if (seg < PPC_PREFIX_SEG (prefix_opcodes[idx].opcode))
+	      break;
+	}
+
       /* VLE opcodes */
       for (seg = 0, idx = 0; seg <= VLE_OPCD_SEGS; seg++)
 	{
@@ -556,6 +572,57 @@ lookup_powerpc (uint64_t insn, ppc_cpu_t dialect)
   return last;
 }
 
+/* Find a match for INSN in the PREFIX opcode table.  */
+
+static const struct powerpc_opcode *
+lookup_prefix (uint64_t insn, ppc_cpu_t dialect)
+{
+  const struct powerpc_opcode *opcode, *opcode_end, *last;
+  unsigned long seg;
+
+  /* Get the opcode segment of the instruction.  */
+  seg = PPC_PREFIX_SEG (insn);
+
+  /* Find the first match in the opcode table for this major opcode.  */
+  opcode_end = prefix_opcodes + prefix_opcd_indices[seg + 1];
+  last = NULL;
+  for (opcode = prefix_opcodes + prefix_opcd_indices[seg];
+       opcode < opcode_end;
+       ++opcode)
+    {
+      const unsigned char *opindex;
+      const struct powerpc_operand *operand;
+      int invalid;
+
+      if ((insn & opcode->mask) != opcode->opcode
+	  || ((dialect & PPC_OPCODE_ANY) == 0
+	      && ((opcode->flags & dialect) == 0
+		  || (opcode->deprecated & dialect) != 0)))
+	continue;
+
+      /* Check validity of operands.  */
+      invalid = 0;
+      for (opindex = opcode->operands; *opindex != 0; opindex++)
+	{
+	  operand = powerpc_operands + *opindex;
+	  if (operand->extract)
+	    (*operand->extract) (insn, dialect, &invalid);
+	}
+      if (invalid)
+	continue;
+
+      if ((dialect & PPC_OPCODE_RAW) == 0)
+	return opcode;
+
+      /* The raw machine insn is one that is not a specialization.  */
+      if (last == NULL
+	  || (last->mask & ~opcode->mask) != 0)
+	last = opcode;
+    }
+
+  return last;
+}
+
 /* Find a match for INSN in the VLE opcode table.  */
 
 static const struct powerpc_opcode *
@@ -699,7 +766,31 @@ print_insn_powerpc (bfd_vma memaddr,
 
   /* Get the major opcode of the insn.  */
   opcode = NULL;
-  if ((dialect & PPC_OPCODE_VLE) != 0)
+  if ((dialect & PPC_OPCODE_POWERXX) != 0
+      && PPC_OP (insn) == 0x1)
+    {
+      uint64_t temp_insn, suffix;
+      status = (*info->read_memory_func) (memaddr + 4, buffer, 4, info);
+      if (status == 0)
+	{
+	  if (bigendian)
+	    suffix = bfd_getb32 (buffer);
+	  else
+	    suffix = bfd_getl32 (buffer);
+	  temp_insn = (insn << 32) | suffix;
+	  opcode = lookup_prefix (temp_insn, dialect & ~PPC_OPCODE_ANY);
+	  if (opcode == NULL && (dialect & PPC_OPCODE_ANY) != 0)
+	    opcode = lookup_prefix (temp_insn, dialect);
+	  if (opcode != NULL)
+	    {
+	      insn = temp_insn;
+	      insn_length = 8;
+	      if ((info->flags & WIDE_OUTPUT) != 0)
+		info->bytes_per_line = 8;
+	    }
+	}
+    }
+  if (opcode == NULL && (dialect & PPC_OPCODE_VLE) != 0)
     {
       opcode = lookup_vle (insn);
       if (opcode != NULL && PPC_OP_SE_VLE (opcode->mask))
diff --git a/opcodes/ppc-opc.c b/opcodes/ppc-opc.c
index 394a99750c..7dc2d775d9 100644
--- a/opcodes/ppc-opc.c
+++ b/opcodes/ppc-opc.c
@@ -2721,6 +2721,18 @@ const unsigned int num_powerpc_operands = (sizeof (powerpc_operands)
 #define OP(x) ((((uint64_t)(x)) & 0x3f) << 26)
 #define OP_MASK OP (0x3f)
 
+/* The prefix opcode.  */
+#define PREFIX_OP (1ULL << 58)
+
+/* The 2-bit prefix form.  */
+#define PREFIX_FORM(x) ((x & 3ULL) << 56)
+
+#define SUFFIX_MASK ((1ULL << 32) - 1)
+#define PREFIX_MASK (SUFFIX_MASK << 32)
+
+/* Prefix insn, modified register to register form MRR.  */
+#define PMRR (PREFIX_OP | PREFIX_FORM (3))
+
 /* The main opcode combined with a trap code in the TO field of a D
    form instruction.  Used for extended mnemonics for the trap
    instructions.  */
@@ -3547,6 +3559,7 @@ const unsigned int num_powerpc_operands = (sizeof (powerpc_operands)
 #define POWER7	PPC_OPCODE_POWER7
 #define POWER8	PPC_OPCODE_POWER8
 #define POWER9	PPC_OPCODE_POWER9
+#define POWERXX PPC_OPCODE_POWERXX
 #define CELL	PPC_OPCODE_CELL
 #define PPC64	PPC_OPCODE_64 | PPC_OPCODE_64_BRIDGE
 #define NON32	(PPC_OPCODE_64 | PPC_OPCODE_POWER4	\
@@ -7796,6 +7809,17 @@ const struct powerpc_opcode powerpc_opcodes[] = {
 const unsigned int powerpc_num_opcodes =
   sizeof (powerpc_opcodes) / sizeof (powerpc_opcodes[0]);
 \f
+/* The opcode table for 8-byte prefix instructions.
+
+   The format of this opcode table is the same as the main opcode table.  */
+
+const struct powerpc_opcode prefix_opcodes[] = {
+{"pnop",	  PMRR,		       PREFIX_MASK,	POWERXX, 0,	{0}},
+};
+
+const unsigned int prefix_num_opcodes =
+  sizeof (prefix_opcodes) / sizeof (prefix_opcodes[0]);
+\f
 /* The VLE opcode table.
 
    The format of this opcode table is the same as the main opcode table.  */

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 2/5] PowerPC D-form prefixed loads and stores
  2019-05-24  1:31 [PATCH 0/5] PowerPC -mfuture support Alan Modra
  2019-05-24  1:33 ` [PATCH 1/5] PowerPC add initial -mfuture instruction support Alan Modra
@ 2019-05-24  1:34 ` Alan Modra
  2019-06-20 12:04   ` Jan Beulich
  2019-05-24  1:35 ` [PATCH 3/5] PowerPC relocations for prefix insns Alan Modra
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 13+ messages in thread
From: Alan Modra @ 2019-05-24  1:34 UTC (permalink / raw)
  To: binutils

opcodes/
	* ppc-opc.c (insert_d34, extract_d34, insert_nsi34, extract_nsi34),
	(insert_pcrel, extract_pcrel, extract_pcrel0): New functions.
	(extract_esync, extract_raq, extract_tbr, extract_sxl): Comment.
	(powerpc_operands <D34, SI34, NSI34, PRA0, PRAQ, PCREL, PCREL0,
	XTOP>): Define and add entries.
	(P8LS, PMLS, P_D_MASK, P_DRAPCREL_MASK): Define.
	(prefix_opcodes): Add pli, paddi, pla, psubi, plwz, plbz, pstw,
	pstb, plhz, plha, psth, plfs, plfd, pstfs, pstfd, plq, plxsd,
	plxssp, pld, plwa, pstxsd, pstxssp, pstxv, pstd, and pstq.
gas/
	* config/tc-ppc.c (ppc_insert_operand): Only sign extend fields that
	are 32-bits or smaller.
	* messages.c (as_internal_value_out_of_range): Do not truncate
	variables and use BFD_VMA_FMT to print them.
	* testsuite/gas/ppc/prefix-pcrel.s,
	* testsuite/gas/ppc/prefix-pcrel.d: New test.
	* testsuite/gas/ppc/ppc.exp: Run it.

diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c
index 4abb5b8a31..4026c72293 100644
--- a/gas/config/tc-ppc.c
+++ b/gas/config/tc-ppc.c
@@ -1987,8 +1987,10 @@ ppc_insert_operand (uint64_t insn,
 	 hand but only up to 32 bits.  This shouldn't really be valid,
 	 but, to permit this code to assemble on a 64-bit host, we
 	 sign extend the 32-bit value to 64 bits if so doing makes the
-	 value valid.  */
+	 value valid.  We only do this for operands that are 32-bits or
+	 smaller.  */
       if (val > max
+	  && (operand->bitm & ~0xffffffffULL) == 0
 	  && (val - (1LL << 32)) >= min
 	  && (val - (1LL << 32)) <= max
 	  && ((val - (1LL << 32)) & (right - 1)) == 0)
@@ -1997,6 +1999,7 @@ ppc_insert_operand (uint64_t insn,
       /* Similarly, people write expressions like ~(1<<15), and expect
 	 this to be OK for a 32-bit unsigned value.  */
       else if (val < min
+	       && (operand->bitm & ~0xffffffffULL) == 0
 	       && (val + (1LL << 32)) >= min
 	       && (val + (1LL << 32)) <= max
 	       && ((val + (1LL << 32)) & (right - 1)) == 0)
diff --git a/gas/messages.c b/gas/messages.c
index 95471a6bef..b7d82f595d 100644
--- a/gas/messages.c
+++ b/gas/messages.c
@@ -397,13 +397,12 @@ as_internal_value_out_of_range (const char *prefix,
 	abort ();
 
       /* xgettext:c-format  */
-      err = _("%s out of domain (%d is not a multiple of %d)");
+      err = _("%s out of domain (%" BFD_VMA_FMT "d is not a multiple of %" \
+	      BFD_VMA_FMT "d)");
       if (bad)
-	as_bad_where (file, line, err,
-		      prefix, (int) val, (int) right);
+	as_bad_where (file, line, err, prefix, val, right);
       else
-	as_warn_where (file, line, err,
-		       prefix, (int) val, (int) right);
+	as_warn_where (file, line, err, prefix, val, right);
       return;
     }
 
@@ -415,14 +414,13 @@ as_internal_value_out_of_range (const char *prefix,
       && max > HEX_MIN_THRESHOLD)
     {
       /* xgettext:c-format  */
-      err = _("%s out of range (%d is not between %d and %d)");
+      err = _("%s out of range (%" BFD_VMA_FMT "d is not between %" \
+	      BFD_VMA_FMT "d and %" BFD_VMA_FMT "d)");
 
       if (bad)
-	as_bad_where (file, line, err,
-		      prefix, (int) val, (int) min, (int) max);
+	as_bad_where (file, line, err, prefix, val, min, max);
       else
-	as_warn_where (file, line, err,
-		       prefix, (int) val, (int) min, (int) max);
+	as_warn_where (file, line, err, prefix, val, min, max);
     }
   else
     {
diff --git a/gas/testsuite/gas/ppc/ppc.exp b/gas/testsuite/gas/ppc/ppc.exp
index 6be042bf26..aa199d5e85 100644
--- a/gas/testsuite/gas/ppc/ppc.exp
+++ b/gas/testsuite/gas/ppc/ppc.exp
@@ -115,3 +115,4 @@ run_dump_test "vsx3"
 run_dump_test "htm"
 run_dump_test "titan"
 run_dump_test "prefix-align"
+run_dump_test "prefix-pcrel"
diff --git a/gas/testsuite/gas/ppc/prefix-pcrel.d b/gas/testsuite/gas/ppc/prefix-pcrel.d
new file mode 100644
index 0000000000..a0ca60fa54
--- /dev/null
+++ b/gas/testsuite/gas/ppc/prefix-pcrel.d
@@ -0,0 +1,235 @@
+#as: -mfuture
+#objdump: -dr -Mfuture
+#name: POWERXX pcrel tests
+
+.*
+
+
+Disassembly of section \.text:
+
+0+00 <prefix>:
+.*:	(06 00 00 00|00 00 00 06) 	paddi   r10,r9,0
+.*:	(39 49 00 00|00 00 49 39) 
+.*:	(06 00 00 00|00 00 00 06) 	paddi   r10,r9,0
+.*:	(39 49 00 00|00 00 49 39) 
+.*:	(06 00 00 00|00 00 00 06) 	paddi   r10,r9,0
+.*:	(39 49 00 00|00 00 49 39) 
+.*:	(06 03 ff ff|ff ff 03 06) 	paddi   r11,r9,-32769
+.*:	(39 69 7f ff|ff 7f 69 39) 
+.*:	(06 03 ff ff|ff ff 03 06) 	paddi   r11,r9,-32769
+.*:	(39 69 7f ff|ff 7f 69 39) 
+.*:	(06 03 ff ff|ff ff 03 06) 	paddi   r11,r9,-32769
+.*:	(39 69 7f ff|ff 7f 69 39) 
+.*:	(06 01 ff ff|ff ff 01 06) 	paddi   r12,r9,8589934591
+.*:	(39 89 ff ff|ff ff 89 39) 
+.*:	(06 01 ff ff|ff ff 01 06) 	paddi   r12,r9,8589934591
+.*:	(39 89 ff ff|ff ff 89 39) 
+.*:	(06 01 ff ff|ff ff 01 06) 	paddi   r12,r9,8589934591
+.*:	(39 89 ff ff|ff ff 89 39) 
+.*:	(06 01 ff ff|ff ff 01 06) 	paddi   r12,r9,8589934591
+.*:	(39 89 ff ff|ff ff 89 39) 
+.*:	(06 01 ff ff|ff ff 01 06) 	paddi   r12,r9,8589934591
+.*:	(39 89 ff ff|ff ff 89 39) 
+.*:	(06 02 00 00|00 00 02 06) 	paddi   r13,r9,-8589934592
+.*:	(39 a9 00 00|00 00 a9 39) 
+.*:	(06 02 00 00|00 00 02 06) 	paddi   r13,r9,-8589934592
+.*:	(39 a9 00 00|00 00 a9 39) 
+.*:	(06 02 00 00|00 00 02 06) 	paddi   r13,r9,-8589934592
+.*:	(39 a9 00 00|00 00 a9 39) 
+.*:	(06 02 00 00|00 00 02 06) 	paddi   r13,r9,-8589934592
+.*:	(39 a9 00 00|00 00 a9 39) 
+.*:	(06 02 00 00|00 00 02 06) 	paddi   r13,r9,-8589934592
+.*:	(39 a9 00 00|00 00 a9 39) 
+.*:	(06 10 00 00|00 00 10 06) 	pla     r14,0
+.*:	(39 c0 00 00|00 00 c0 39) 
+.*:	(06 10 00 00|00 00 10 06) 	pla     r14,0
+.*:	(39 c0 00 00|00 00 c0 39) 
+.*:	(06 13 ff ff|ff ff 13 06) 	pla     r15,-32769
+.*:	(39 e0 7f ff|ff 7f e0 39) 
+.*:	(06 13 ff ff|ff ff 13 06) 	pla     r15,-32769
+.*:	(39 e0 7f ff|ff 7f e0 39) 
+.*:	(06 13 ff ff|ff ff 13 06) 	pla     r15,-32769
+.*:	(39 e0 7f ff|ff 7f e0 39) 
+.*:	(06 11 ff ff|ff ff 11 06) 	pla     r16,8589934591
+.*:	(3a 00 ff ff|ff ff 00 3a) 
+.*:	(06 11 ff ff|ff ff 11 06) 	pla     r16,8589934591
+.*:	(3a 00 ff ff|ff ff 00 3a) 
+.*:	(06 11 ff ff|ff ff 11 06) 	pla     r16,8589934591
+.*:	(3a 00 ff ff|ff ff 00 3a) 
+.*:	(06 12 00 00|00 00 12 06) 	pla     r17,-8589934592
+.*:	(3a 20 00 00|00 00 20 3a) 
+.*:	(06 12 00 00|00 00 12 06) 	pla     r17,-8589934592
+.*:	(3a 20 00 00|00 00 20 3a) 
+.*:	(06 12 00 00|00 00 12 06) 	pla     r17,-8589934592
+.*:	(3a 20 00 00|00 00 20 3a) 
+.*:	(06 00 00 00|00 00 00 06) 	pli     r20,13
+.*:	(3a 80 00 0d|0d 00 80 3a) 
+.*:	(06 00 00 00|00 00 00 06) 	pli     r20,13
+.*:	(3a 80 00 0d|0d 00 80 3a) 
+.*:	(06 00 00 00|00 00 00 06) 	pli     r20,13
+.*:	(3a 80 00 0d|0d 00 80 3a) 
+.*:	(06 00 00 00|00 00 00 06) 	pli     r20,13
+.*:	(3a 80 00 0d|0d 00 80 3a) 
+.*:	(06 03 ff ff|ff ff 03 06) 	pli     r21,-32769
+.*:	(3a a0 7f ff|ff 7f a0 3a) 
+.*:	(06 03 ff ff|ff ff 03 06) 	pli     r21,-32769
+.*:	(3a a0 7f ff|ff 7f a0 3a) 
+.*:	(06 03 ff ff|ff ff 03 06) 	pli     r21,-32769
+.*:	(3a a0 7f ff|ff 7f a0 3a) 
+.*:	(06 01 ff ff|ff ff 01 06) 	pli     r22,8589934591
+.*:	(3a c0 ff ff|ff ff c0 3a) 
+.*:	(06 01 ff ff|ff ff 01 06) 	pli     r22,8589934591
+.*:	(3a c0 ff ff|ff ff c0 3a) 
+.*:	(06 01 ff ff|ff ff 01 06) 	pli     r22,8589934591
+.*:	(3a c0 ff ff|ff ff c0 3a) 
+.*:	(06 01 ff ff|ff ff 01 06) 	pli     r22,8589934591
+.*:	(3a c0 ff ff|ff ff c0 3a) 
+.*:	(06 01 ff ff|ff ff 01 06) 	pli     r22,8589934591
+.*:	(3a c0 ff ff|ff ff c0 3a) 
+.*:	(06 01 ff ff|ff ff 01 06) 	pli     r22,8589934591
+.*:	(3a c0 ff ff|ff ff c0 3a) 
+.*:	(06 02 00 00|00 00 02 06) 	pli     r23,-8589934592
+.*:	(3a e0 00 00|00 00 e0 3a) 
+.*:	(06 02 00 00|00 00 02 06) 	pli     r23,-8589934592
+.*:	(3a e0 00 00|00 00 e0 3a) 
+.*:	(06 02 00 00|00 00 02 06) 	pli     r23,-8589934592
+.*:	(3a e0 00 00|00 00 e0 3a) 
+.*:	(06 02 00 00|00 00 02 06) 	pli     r23,-8589934592
+.*:	(3a e0 00 00|00 00 e0 3a) 
+.*:	(06 02 00 00|00 00 02 06) 	pli     r23,-8589934592
+.*:	(3a e0 00 00|00 00 e0 3a) 
+.*:	(06 02 00 00|00 00 02 06) 	pli     r23,-8589934592
+.*:	(3a e0 00 00|00 00 e0 3a) 
+.*:	(06 00 00 00|00 00 00 06) 	plbz    r3,0\(r1\)
+.*:	(88 61 00 00|00 00 61 88) 
+.*:	(06 00 00 00|00 00 00 06) 	plbz    r3,0\(r1\)
+.*:	(88 61 00 00|00 00 61 88) 
+.*:	(06 03 ff ff|ff ff 03 06) 	plbz    r3,-32769\(r1\)
+.*:	(88 61 7f ff|ff 7f 61 88) 
+.*:	(06 03 ff ff|ff ff 03 06) 	plbz    r3,-32769\(r1\)
+.*:	(88 61 7f ff|ff 7f 61 88) 
+.*:	(06 01 ff ff|ff ff 01 06) 	plbz    r3,8589934591\(r1\)
+.*:	(88 61 ff ff|ff ff 61 88) 
+.*:	(06 01 ff ff|ff ff 01 06) 	plbz    r3,8589934591\(r1\)
+.*:	(88 61 ff ff|ff ff 61 88) 
+.*:	(06 02 00 00|00 00 02 06) 	plbz    r3,-8589934592\(r1\)
+.*:	(88 61 00 00|00 00 61 88) 
+.*:	(06 02 00 00|00 00 02 06) 	plbz    r3,-8589934592\(r1\)
+.*:	(88 61 00 00|00 00 61 88) 
+.*:	(06 00 00 00|00 00 00 06) 	plbz    r3,0\(0\)
+.*:	(88 60 00 00|00 00 60 88) 
+.*:	(06 10 00 00|00 00 10 06) 	plbz    r4,0
+.*:	(88 80 00 00|00 00 80 88) 
+.*:	(06 10 00 00|00 00 10 06) 	plbz    r4,0
+.*:	(88 80 00 00|00 00 80 88) 
+.*:	(06 03 ff ff|ff ff 03 06) 	plbz    r3,-32769\(0\)
+.*:	(88 60 7f ff|ff 7f 60 88) 
+.*:	(06 13 ff ff|ff ff 13 06) 	plbz    r4,-32769
+.*:	(88 80 7f ff|ff 7f 80 88) 
+.*:	(06 13 ff ff|ff ff 13 06) 	plbz    r4,-32769
+.*:	(88 80 7f ff|ff 7f 80 88) 
+.*:	(06 01 ff ff|ff ff 01 06) 	plbz    r3,8589934591\(0\)
+.*:	(88 60 ff ff|ff ff 60 88) 
+.*:	(06 11 ff ff|ff ff 11 06) 	plbz    r4,8589934591
+.*:	(88 80 ff ff|ff ff 80 88) 
+.*:	(06 11 ff ff|ff ff 11 06) 	plbz    r4,8589934591
+.*:	(88 80 ff ff|ff ff 80 88) 
+.*:	(06 02 00 00|00 00 02 06) 	plbz    r3,-8589934592\(0\)
+.*:	(88 60 00 00|00 00 60 88) 
+.*:	(06 12 00 00|00 00 12 06) 	plbz    r4,-8589934592
+.*:	(88 80 00 00|00 00 80 88) 
+.*:	(06 12 00 00|00 00 12 06) 	plbz    r4,-8589934592
+.*:	(88 80 00 00|00 00 80 88) 
+.*:	(06 00 00 00|00 00 00 06) 	plhz    r5,4\(r10\)
+.*:	(a0 aa 00 04|04 00 aa a0) 
+.*:	(06 10 00 00|00 00 10 06) 	plhz    r5,4
+.*:	(a0 a0 00 04|04 00 a0 a0) 
+.*:	(06 00 00 00|00 00 00 06) 	plha    r6,8\(r10\)
+.*:	(a8 ca 00 08|08 00 ca a8) 
+.*:	(06 10 00 00|00 00 10 06) 	plha    r6,8
+.*:	(a8 c0 00 08|08 00 c0 a8) 
+.*:	(06 00 00 00|00 00 00 06) 	plwz    r7,12\(r10\)
+.*:	(80 ea 00 0c|0c 00 ea 80) 
+.*:	(06 10 00 00|00 00 10 06) 	plwz    r7,12
+.*:	(80 e0 00 0c|0c 00 e0 80) 
+.*:	(04 00 00 00|00 00 00 04) 	plwa    r8,16\(r10\)
+.*:	(a5 0a 00 10|10 00 0a a5) 
+.*:	(04 10 00 00|00 00 10 04) 	plwa    r8,16
+.*:	(a5 00 00 10|10 00 00 a5) 
+.*:	(04 00 00 00|00 00 00 04) 	pld     r9,20\(r10\)
+.*:	(e5 2a 00 14|14 00 2a e5) 
+.*:	(04 10 00 00|00 00 10 04) 	pld     r9,20
+.*:	(e5 20 00 14|14 00 20 e5) 
+.*:	(06 00 00 00|00 00 00 06) 	plfs    f10,24\(r10\)
+.*:	(c1 4a 00 18|18 00 4a c1) 
+.*:	(06 10 00 00|00 00 10 06) 	plfs    f10,24
+.*:	(c1 40 00 18|18 00 40 c1) 
+.*:	(06 00 00 00|00 00 00 06) 	plfd    f11,28\(r10\)
+.*:	(c9 6a 00 1c|1c 00 6a c9) 
+.*:	(06 10 00 00|00 00 10 06) 	plfd    f11,28
+.*:	(c9 60 00 1c|1c 00 60 c9) 
+.*:	(04 00 00 00|00 00 00 04) 	plxsd   v13,36\(r10\)
+.*:	(a9 aa 00 24|24 00 aa a9) 
+.*:	(04 10 00 00|00 00 10 04) 	plxsd   v13,36
+.*:	(a9 a0 00 24|24 00 a0 a9) 
+.*:	(04 00 00 00|00 00 00 04) 	plxssp  v14,40\(r10\)
+.*:	(ad ca 00 28|28 00 ca ad) 
+.*:	(04 10 00 00|00 00 10 04) 	plxssp  v14,40
+.*:	(ad c0 00 28|28 00 c0 ad) 
+.*:	(04 00 00 00|00 00 00 04) 	plq     r16,48\(r10\)
+.*:	(e2 0a 00 30|30 00 0a e2) 
+.*:	(04 10 00 00|00 00 10 04) 	plq     r16,48
+.*:	(e2 00 00 30|30 00 00 e2) 
+.*:	(04 00 00 00|00 00 00 04) 	plxv    vs17,64\(r10\)
+.*:	(ca 2a 00 40|40 00 2a ca) 
+.*:	(04 10 00 00|00 00 10 04) 	plxv    vs17,64
+.*:	(ca 20 00 40|40 00 20 ca) 
+.*:	(04 00 00 00|00 00 00 04) 	plxv    vs34,64\(r10\)
+.*:	(cc 4a 00 40|40 00 4a cc) 
+.*:	(04 10 00 00|00 00 10 04) 	plxv    vs34,64
+.*:	(cc 40 00 40|40 00 40 cc) 
+.*:	(06 00 00 00|00 00 00 06) 	pstb    r3,52\(r11\)
+.*:	(98 6b 00 34|34 00 6b 98) 
+.*:	(06 10 00 00|00 00 10 06) 	pstb    r3,52
+.*:	(98 60 00 34|34 00 60 98) 
+.*:	(06 00 00 00|00 00 00 06) 	psth    r4,56\(r11\)
+.*:	(b0 8b 00 38|38 00 8b b0) 
+.*:	(06 10 00 00|00 00 10 06) 	psth    r4,56
+.*:	(b0 80 00 38|38 00 80 b0) 
+.*:	(06 00 00 00|00 00 00 06) 	pstw    r5,60\(r11\)
+.*:	(90 ab 00 3c|3c 00 ab 90) 
+.*:	(06 10 00 00|00 00 10 06) 	pstw    r5,60
+.*:	(90 a0 00 3c|3c 00 a0 90) 
+.*:	(06 00 00 00|00 00 00 06) 	pstfs   f6,64\(r11\)
+.*:	(d0 cb 00 40|40 00 cb d0) 
+.*:	(06 10 00 00|00 00 10 06) 	pstfs   f6,64
+.*:	(d0 c0 00 40|40 00 c0 d0) 
+.*:	(06 00 00 00|00 00 00 06) 	pstfd   f7,68\(r11\)
+.*:	(d8 eb 00 44|44 00 eb d8) 
+.*:	(06 10 00 00|00 00 10 06) 	pstfd   f7,68
+.*:	(d8 e0 00 44|44 00 e0 d8) 
+.*:	(04 00 00 00|00 00 00 04) 	pstxsd  v9,76\(r11\)
+.*:	(b9 2b 00 4c|4c 00 2b b9) 
+.*:	(04 10 00 00|00 00 10 04) 	pstxsd  v9,76
+.*:	(b9 20 00 4c|4c 00 20 b9) 
+.*:	(04 00 00 00|00 00 00 04) 	pstxssp v10,80\(r11\)
+.*:	(bd 4b 00 50|50 00 4b bd) 
+.*:	(04 10 00 00|00 00 10 04) 	pstxssp v10,80
+.*:	(bd 40 00 50|50 00 40 bd) 
+.*:	(04 00 00 00|00 00 00 04) 	pstd    r11,84\(r11\)
+.*:	(f5 6b 00 54|54 00 6b f5) 
+.*:	(04 10 00 00|00 00 10 04) 	pstd    r11,84
+.*:	(f5 60 00 54|54 00 60 f5) 
+.*:	(04 00 00 00|00 00 00 04) 	pstq    r12,88\(r11\)
+.*:	(f1 8b 00 58|58 00 8b f1) 
+.*:	(04 10 00 00|00 00 10 04) 	pstq    r12,88
+.*:	(f1 80 00 58|58 00 80 f1) 
+.*:	(04 00 00 00|00 00 00 04) 	pstxv   vs13,96\(r11\)
+.*:	(d9 ab 00 60|60 00 ab d9) 
+.*:	(04 10 00 00|00 00 10 04) 	pstxv   vs13,96
+.*:	(d9 a0 00 60|60 00 a0 d9) 
+.*:	(04 00 00 00|00 00 00 04) 	pstxv   vs63,96\(r11\)
+.*:	(df eb 00 60|60 00 eb df) 
+.*:	(04 10 00 00|00 00 10 04) 	pstxv   vs63,96
+.*:	(df e0 00 60|60 00 e0 df) 
+#pass
diff --git a/gas/testsuite/gas/ppc/prefix-pcrel.s b/gas/testsuite/gas/ppc/prefix-pcrel.s
new file mode 100644
index 0000000000..c3831d8a2c
--- /dev/null
+++ b/gas/testsuite/gas/ppc/prefix-pcrel.s
@@ -0,0 +1,121 @@
+	.text
+prefix:
+	# The following should all disassemble to: paddi rX,rY,disp
+	pla	10,0(9)
+	paddi	10,9,0
+	paddi	10,9,0,0
+	pla	11,~(1<<15)(9)
+	paddi	11,9,~(1<<15)
+	paddi	11,9,~(1<<15),0
+	pla	12,8589934591(9)
+	psubi	12,9,-8589934591
+	psubi	12,9,-8589934591,0
+	paddi	12,9,8589934591
+	paddi	12,9,8589934591,0
+	pla	13,-8589934592(9)
+	psubi	13,9,8589934592
+	psubi	13,9,8589934592,0
+	paddi	13,9,-8589934592
+	paddi	13,9,-8589934592,0
+
+	# The following should all disassemble to: pla rX,disp
+	pla	14,0
+	paddi	14,0,0,1
+	pla	15,~(1<<15)
+	psubi	15,0,-(~(1<<15)),1
+	paddi	15,0,~(1<<15),1
+	pla	16,8589934591
+	psubi	16,0,-8589934591,1
+	paddi	16,0,8589934591,1
+	pla	17,-8589934592
+	psubi	17,0,8589934592,1
+	paddi	17,0,-8589934592,1
+
+	# The following should all disassemble to: pli rX,immed
+	pli	20,13
+	pla	20,13(0)
+	psubi	20,0,-13
+	paddi	20,0,13
+	pli	21,~(1<<15)
+	pla	21,~(1<<15)(0)
+	paddi	21,0,~(1<<15)
+	pli	22,8589934591
+	pla	22,8589934591(0)
+	psubi	22,0,-8589934591
+	psubi	22,0,-8589934591,0
+	paddi	22,0,8589934591
+	paddi	22,0,8589934591,0
+	pli	23,-8589934592
+	pla	23,-8589934592(0)
+	psubi	23,0,8589934592
+	psubi	23,0,8589934592,0
+	paddi	23,0,-8589934592
+	paddi	23,0,-8589934592,0
+
+	# Tests of prefix loads and stores
+	plbz	3,0(1)
+	plbz	3,0(1),0
+	plbz	3,~(1<<15)(1)
+	plbz	3,~(1<<15)(1),0
+	plbz	3,8589934591(1)
+	plbz	3,8589934591(1),0
+	plbz	3,-8589934592(1)
+	plbz	3,-8589934592(1),0
+	plbz	3,0(0)
+	plbz	4,0(0),1
+	plbz	4,0
+	plbz	3,~(1<<15)(0)
+	plbz	4,~(1<<15)(0),1
+	plbz	4,~(1<<15)
+	plbz	3,8589934591(0)
+	plbz	4,8589934591(0),1
+	plbz	4,8589934591
+	plbz	3,-8589934592(0)
+	plbz	4,-8589934592(0),1
+	plbz	4,-8589934592
+	plhz	5,4(10),0
+	plhz	5,4(0),1
+	plha	6,8(10),0
+	plha	6,8(0),1
+	plwz	7,12(10),0
+	plwz	7,12(0),1
+	plwa	8,16(10),0
+	plwa	8,16(0),1
+	pld	9,20(10),0
+	pld	9,20(0),1
+	plfs	10,24(10),0
+	plfs	10,24(0),1
+	plfd	11,28(10),0
+	plfd	11,28(0),1
+	plxsd	13,36(10),0
+	plxsd	13,36(0),1
+	plxssp	14,40(10),0
+	plxssp	14,40(0),1
+	plq	16,48(10),0
+	plq	16,48(0),1
+	plxv	17,64(10),0
+	plxv	17,64(0),1
+	plxv	34,64(10),0
+	plxv	34,64(0),1
+	pstb	3,52(11),0
+	pstb	3,52(0),1
+	psth	4,56(11),0
+	psth	4,56(0),1
+	pstw	5,60(11),0
+	pstw	5,60(0),1
+	pstfs	6,64(11),0
+	pstfs	6,64(0),1
+	pstfd	7,68(11),0
+	pstfd	7,68(0),1
+	pstxsd	9,76(11),0
+	pstxsd	9,76(0),1
+	pstxssp	10,80(11),0
+	pstxssp	10,80(0),1
+	pstd	11,84(11),0
+	pstd	11,84(0),1
+	pstq	12,88(11),0
+	pstq	12,88(0),1
+	pstxv	13,96(11),0
+	pstxv	13,96(0),1
+	pstxv	63,96(11),0
+	pstxv	63,96(0),1
diff --git a/opcodes/ppc-opc.c b/opcodes/ppc-opc.c
index 7dc2d775d9..aa7184230f 100644
--- a/opcodes/ppc-opc.c
+++ b/opcodes/ppc-opc.c
@@ -596,6 +596,106 @@ extract_dxdn (uint64_t insn,
   return -extract_dxd (insn, dialect, invalid);
 }
 
+/* The D field in a 64-bit D form prefix instruction when the field is split
+   into separate D0 and D1 fields.  */
+
+static uint64_t
+insert_d34 (uint64_t insn,
+	    int64_t value,
+	    ppc_cpu_t dialect ATTRIBUTE_UNUSED,
+	    const char **errmsg ATTRIBUTE_UNUSED)
+{
+  return insn | ((value & 0x3ffff0000ULL) << 16) | (value & 0xffff);
+}
+
+static int64_t
+extract_d34 (uint64_t insn,
+	     ppc_cpu_t dialect ATTRIBUTE_UNUSED,
+	     int *invalid ATTRIBUTE_UNUSED)
+{
+  int64_t mask = 1ULL << 33;
+  int64_t value = ((insn >> 16) & 0x3ffff0000ULL) | (insn & 0xffff);
+  value = (value ^ mask) - mask;
+  return value;
+}
+
+/* The NSI34 field in an 8-byte D form prefix instruction.  This is the same
+   as the SI34 field, only negated.  The extraction function always marks it
+   as invalid, since we never want to recognize an instruction which uses
+   a field of this type.  */
+
+static uint64_t
+insert_nsi34 (uint64_t insn,
+	      int64_t value,
+	      ppc_cpu_t dialect,
+	      const char **errmsg)
+{
+  return insert_d34 (insn, -value, dialect, errmsg);
+}
+
+static int64_t
+extract_nsi34 (uint64_t insn,
+	       ppc_cpu_t dialect,
+	       int *invalid)
+{
+  int64_t value = extract_d34 (insn, dialect, invalid);
+  *invalid = 1;
+  return -value;
+}
+
+/* The R field in an 8-byte prefix instruction when there are restrictions
+   between R's value and the RA value (ie, they cannot both be non zero).  */
+
+static uint64_t
+insert_pcrel (uint64_t insn,
+	      int64_t value,
+	      ppc_cpu_t dialect ATTRIBUTE_UNUSED,
+	      const char **errmsg)
+{
+  value &= 0x1;
+  int64_t ra = (insn >> 16) & 0x1f;
+  if (ra != 0 && value != 0)
+    *errmsg = _("invalid R operand");
+
+  return insn | (value << 52);
+}
+
+static int64_t
+extract_pcrel (uint64_t insn,
+	       ppc_cpu_t dialect ATTRIBUTE_UNUSED,
+	       int *invalid)
+{
+  /* If called with *invalid < 0 to return the value for missing
+     operands, *invalid will be the negative count of missing operands
+     including this one.  Return a default value of 1 if the PRA0/PRAQ
+     operand was also omitted (ie. *invalid is -2).  Return a default
+     value of 0 if the PRA0/PRAQ operand was not omitted
+     (ie. *invalid is -1).  */
+  if (*invalid < 0)
+    return ~ *invalid & 1;
+
+  int64_t ra = (insn >> 16) & 0x1f;
+  int64_t pcrel = (insn >> 52) & 0x1;
+  if (ra != 0 && pcrel != 0)
+    *invalid = 1;
+
+  return pcrel;
+}
+
+/* Variant of extract_pcrel that sets invalid for R bit set.  The idea
+   is to disassemble "paddi rt,0,offset,1" as "pla rt,offset".  */
+
+static int64_t
+extract_pcrel0 (uint64_t insn,
+		ppc_cpu_t dialect,
+		int *invalid)
+{
+  int64_t pcrel = extract_pcrel (insn, dialect, invalid);
+  if (pcrel)
+    *invalid = 1;
+  return pcrel;
+}
+
 /* FXM mask in mfcr and mtcrf instructions.  */
 
 static uint64_t
@@ -758,6 +858,7 @@ extract_esync (uint64_t insn,
 	       ppc_cpu_t dialect ATTRIBUTE_UNUSED,
 	       int *invalid)
 {
+  /* Missing optional operands have a value of zero.  */
   if (*invalid < 0)
     return 0;
 
@@ -1013,6 +1114,7 @@ extract_raq (uint64_t insn,
 	     ppc_cpu_t dialect ATTRIBUTE_UNUSED,
 	     int *invalid)
 {
+  /* Missing optional operands have a value of zero.  */
   if (*invalid < 0)
     return 0;
 
@@ -1338,6 +1440,7 @@ extract_tbr (uint64_t insn,
 	     ppc_cpu_t dialect ATTRIBUTE_UNUSED,
 	     int *invalid)
 {
+  /* Missing optional operands have a value of 268.  */
   if (*invalid < 0)
     return 268;
 
@@ -1810,6 +1913,7 @@ extract_sxl (uint64_t insn,
 	     ppc_cpu_t dialect ATTRIBUTE_UNUSED,
 	     int *invalid)
 {
+  /* Missing optional operands have a value of one.  */
   if (*invalid < 0)
     return 1;
   return (insn >> 11) & 0x1;
@@ -2039,9 +2143,26 @@ const struct powerpc_operand powerpc_operands[] =
   { 0xfffc, 0, NULL, NULL,
     PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED | PPC_OPERAND_DS },
 
+  /* The D field in an 8-byte D form prefix instruction.  This is a displacement
+     off a register, and implies that the next operand is a register in
+     parentheses.  */
+#define D34 DS + 1
+  { 0x3ffffffff, PPC_OPSHIFT_INV, insert_d34, extract_d34,
+    PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED },
+
+  /* The SI field in an 8-byte D form prefix instruction.  */
+#define SI34 D34 + 1
+  { 0x3ffffffff, PPC_OPSHIFT_INV, insert_d34, extract_d34, PPC_OPERAND_SIGNED },
+
+  /* The NSI field in an 8-byte D form prefix instruction.  This is the
+     same as the SI34 field, only negated.  */
+#define NSI34 SI34 + 1
+  { 0x3ffffffff, PPC_OPSHIFT_INV, insert_nsi34, extract_nsi34,
+    PPC_OPERAND_NEGATIVE | PPC_OPERAND_SIGNED },
+
   /* The DUIS or BHRBE fields in a XFX form instruction, 10 bits
      unsigned imediate */
-#define DUIS DS + 1
+#define DUIS NSI34 + 1
 #define BHRBE DUIS
   { 0x3ff, 11, NULL, NULL, 0 },
 
@@ -2217,16 +2338,33 @@ const struct powerpc_operand powerpc_operands[] =
 #define RA0 RA + 1
   { 0x1f, 16, NULL, NULL, PPC_OPERAND_GPR_0 },
 
+  /* Similar to above, but optional.  */
+#define PRA0 RA0 + 1
+  { 0x1f, 16, NULL, NULL, PPC_OPERAND_GPR_0 | PPC_OPERAND_OPTIONAL },
+
   /* The RA field in the DQ form lq or an lswx instruction, which have
      special value restrictions.  */
-#define RAQ RA0 + 1
+#define RAQ PRA0 + 1
 #define RAX RAQ
   { 0x1f, 16, insert_raq, extract_raq, PPC_OPERAND_GPR_0 },
 
+  /* Similar to above, but optional.  */
+#define PRAQ RAQ + 1
+  { 0x1f, 16, insert_raq, extract_raq,
+    PPC_OPERAND_GPR_0 | PPC_OPERAND_OPTIONAL },
+
+  /* The R field in an 8-byte D, DS, DQ or X form prefix instruction.  */
+#define PCREL PRAQ + 1
+#define PCREL_MASK (1ULL << 52)
+  { 0x1, 52, insert_pcrel, extract_pcrel, PPC_OPERAND_OPTIONAL },
+
+#define PCREL0 PCREL + 1
+  { 0x1, 52, insert_pcrel, extract_pcrel0, PPC_OPERAND_OPTIONAL },
+
   /* The RA field in a D or X form instruction which is an updating
      load, which means that the RA field may not be zero and may not
      equal the RT field.  */
-#define RAL RAQ + 1
+#define RAL PCREL0 + 1
   { 0x1f, 16, insert_ral, extract_ral, PPC_OPERAND_GPR_0 },
 
   /* The RA field in an lmw instruction, which has special value
@@ -2651,8 +2789,12 @@ const struct powerpc_operand powerpc_operands[] =
 #define XTQ6 XSQ6
   { 0x3f, PPC_OPSHIFT_INV, insert_xtq6, extract_xtq6, PPC_OPERAND_VSR },
 
+  /* The XT field in a plxv instruction.  Runs into the OP field.  */
+#define XTOP XSQ6 + 1
+  { 0x3f, 21, NULL, NULL, PPC_OPERAND_VSR },
+
   /* The XA field in an XX3 form instruction.  This is split.  */
-#define XA6 XTQ6 + 1
+#define XA6 XTOP + 1
   { 0x3f, PPC_OPSHIFT_INV, insert_xa6, extract_xa6, PPC_OPERAND_VSR },
 
   /* The XB field in an XX2 or XX3 form instruction.  This is split.  */
@@ -2730,9 +2872,21 @@ const unsigned int num_powerpc_operands = (sizeof (powerpc_operands)
 #define SUFFIX_MASK ((1ULL << 32) - 1)
 #define PREFIX_MASK (SUFFIX_MASK << 32)
 
+/* Prefix insn, eight byte load/store form 8LS.  */
+#define P8LS (PREFIX_OP | PREFIX_FORM (0))
+
+/* Prefix insn, modified load/store form MLS.  */
+#define PMLS (PREFIX_OP | PREFIX_FORM (2))
+
 /* Prefix insn, modified register to register form MRR.  */
 #define PMRR (PREFIX_OP | PREFIX_FORM (3))
 
+/* An 8-byte D form prefix instruction.  */
+#define P_D_MASK (((-1ULL << 50) & ~PCREL_MASK) | OP_MASK)
+
+/* The same as P_D_MASK, but with the RA and PCREL fields specified.  */
+#define P_DRAPCREL_MASK (P_D_MASK | PCREL_MASK | RA_MASK)
+
 /* The main opcode combined with a trap code in the TO field of a D
    form instruction.  Used for extended mnemonics for the trap
    instructions.  */
@@ -7815,6 +7969,32 @@ const unsigned int powerpc_num_opcodes =
 
 const struct powerpc_opcode prefix_opcodes[] = {
 {"pnop",	  PMRR,		       PREFIX_MASK,	POWERXX, 0,	{0}},
+{"pli",		  PMLS|OP(14),	       P_DRAPCREL_MASK,	POWERXX, 0,	{RT, SI34}},
+{"paddi",	  PMLS|OP(14),	       P_D_MASK,	POWERXX, 0,	{RT, RA0, SI34, PCREL0}},
+{"psubi",	  PMLS|OP(14),	       P_D_MASK,	POWERXX, 0,	{RT, RA0, NSI34, PCREL0}},
+{"pla",		  PMLS|OP(14),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},
+{"plwz",	  PMLS|OP(32),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},
+{"plbz",	  PMLS|OP(34),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},
+{"pstw",	  PMLS|OP(36),	       P_D_MASK,	POWERXX, 0,	{RS, D34, PRA0, PCREL}},
+{"pstb",	  PMLS|OP(38),	       P_D_MASK,	POWERXX, 0,	{RS, D34, PRA0, PCREL}},
+{"plhz",	  PMLS|OP(40),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},
+{"plwa",	  P8LS|OP(41),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},
+{"plxsd",	  P8LS|OP(42),	       P_D_MASK,	POWERXX, 0,	{VD, D34, PRA0, PCREL}},
+{"plha",	  PMLS|OP(42),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},
+{"plxssp",	  P8LS|OP(43),	       P_D_MASK,	POWERXX, 0,	{VD, D34, PRA0, PCREL}},
+{"psth",	  PMLS|OP(44),	       P_D_MASK,	POWERXX, 0,	{RS, D34, PRA0, PCREL}},
+{"pstxsd",	  P8LS|OP(46),	       P_D_MASK,	POWERXX, 0,	{VS, D34, PRA0, PCREL}},
+{"pstxssp",	  P8LS|OP(47),	       P_D_MASK,	POWERXX, 0,	{VS, D34, PRA0, PCREL}},
+{"plfs",	  PMLS|OP(48),	       P_D_MASK,	POWERXX, 0,	{FRT, D34, PRA0, PCREL}},
+{"plxv",	  P8LS|OP(50),	       P_D_MASK&~OP(1),	POWERXX, 0,	{XTOP, D34, PRA0, PCREL}},
+{"plfd",	  PMLS|OP(50),	       P_D_MASK,	POWERXX, 0,	{FRT, D34, PRA0, PCREL}},
+{"pstfs",	  PMLS|OP(52),	       P_D_MASK,	POWERXX, 0,	{FRS, D34, PRA0, PCREL}},
+{"pstxv",	  P8LS|OP(54),	       P_D_MASK&~OP(1),	POWERXX, 0,	{XTOP, D34, PRA0, PCREL}},
+{"pstfd",	  PMLS|OP(54),	       P_D_MASK,	POWERXX, 0,	{FRS, D34, PRA0, PCREL}},
+{"plq",		  P8LS|OP(56),	       P_D_MASK,	POWERXX, 0,	{RTQ, D34, PRAQ, PCREL}},
+{"pld",		  P8LS|OP(57),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},
+{"pstq",	  P8LS|OP(60),	       P_D_MASK,	POWERXX, 0,	{RSQ, D34, PRA0, PCREL}},
+{"pstd",	  P8LS|OP(61),	       P_D_MASK,	POWERXX, 0,	{RS, D34, PRA0, PCREL}},
 };
 
 const unsigned int prefix_num_opcodes =

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 3/5] PowerPC relocations for prefix insns
  2019-05-24  1:31 [PATCH 0/5] PowerPC -mfuture support Alan Modra
  2019-05-24  1:33 ` [PATCH 1/5] PowerPC add initial -mfuture instruction support Alan Modra
  2019-05-24  1:34 ` [PATCH 2/5] PowerPC D-form prefixed loads and stores Alan Modra
@ 2019-05-24  1:35 ` Alan Modra
  2019-05-24  1:36 ` [PATCH 4/5] PowerPC GOT_PCREL34 optimisation Alan Modra
  2019-05-24  1:37 ` [PATCH 5/5] PowerPC notoc linkage stubs Alan Modra
  4 siblings, 0 replies; 13+ messages in thread
From: Alan Modra @ 2019-05-24  1:35 UTC (permalink / raw)
  To: binutils

include/
	* elf/ppc64.h (R_PPC64_PLTSEQ_NOTOC, R_PPC64_PLTCALL_NOTOC),
	(R_PPC64_PCREL_OPT, R_PPC64_D34, R_PPC64_D34_LO, R_PPC64_D34_HI30),
	(R_PPC64_D34_HA30, R_PPC64_PCREL34, R_PPC64_GOT_PCREL34),
	(R_PPC64_PLT_PCREL34, R_PPC64_PLT_PCREL34_NOTOC),
	(R_PPC64_ADDR16_HIGHER34, R_PPC64_ADDR16_HIGHERA34),
	(R_PPC64_ADDR16_HIGHEST34, R_PPC64_ADDR16_HIGHESTA34),
	(R_PPC64_REL16_HIGHER34, R_PPC64_REL16_HIGHERA34),
	(R_PPC64_REL16_HIGHEST34, R_PPC64_REL16_HIGHESTA34),
	(R_PPC64_D28, R_PPC64_PCREL28): Define.
bfd/
	* reloc.c (BFD_RELOC_PPC64_D34, BFD_RELOC_PPC64_D34_LO),
	(BFD_RELOC_PPC64_D34_HI30, BFD_RELOC_PPC64_D34_HA30),
	(BFD_RELOC_PPC64_PCREL34, BFD_RELOC_PPC64_GOT_PCREL34),
	(BFD_RELOC_PPC64_PLT_PCREL34),
	(BFD_RELOC_PPC64_ADDR16_HIGHER34, BFD_RELOC_PPC64_ADDR16_HIGHERA34),
	(BFD_RELOC_PPC64_ADDR16_HIGHEST34, BFD_RELOC_PPC64_ADDR16_HIGHESTA34),
	(BFD_RELOC_PPC64_REL16_HIGHER34, BFD_RELOC_PPC64_REL16_HIGHERA34),
	(BFD_RELOC_PPC64_REL16_HIGHEST34, BFD_RELOC_PPC64_REL16_HIGHESTA34),
	(BFD_RELOC_PPC64_D28, BFD_RELOC_PPC64_PCREL28): New reloc enums.
	* elf64-ppc.c (PNOP): Define.
	(ppc64_elf_howto_raw): Add reloc howtos for new relocations.
	(ppc64_elf_reloc_type_lookup): Translate new bfd reloc numbers.
	(ppc64_elf_ha_reloc): Adjust addend for highera34 and highesta34
	relocs.
	(ppc64_elf_prefix_reloc): New function.
	(struct ppc_link_hash_table): Add notoc_plt.
	(is_branch_reloc): Add R_PPC64_PLTCALL_NOTOC.
	(is_plt_seq_reloc): Add R_PPC64_PLT_PCREL34,
	R_PPC64_PLT_PCREL34_NOTOC, and R_PPC64_PLTSEQ_NOTOC.
	(ppc64_elf_check_relocs): Handle pcrel got and plt relocs.  Set
	has_pltcall for section on seeing R_PPC64_PLTCALL_NOTOC.  Handle
	possible need for dynamic relocs on non-pcrel powerxx relocs.
	(dec_dynrel_count): Handle non-pcrel powerxx relocs.
	(ppc64_elf_inline_plt): Handle R_PPC64_PLTCALL_NOTOC.
	(toc_adjusting_stub_needed): Likewise.
	(ppc64_elf_tls_optimize): Handle R_PPC64_PLTSEQ_NOTOC.
	(ppc64_elf_relocate_section): Handle new powerxx relocs.
	* bfd-in2.h: Regenerate.
	* libbfd.h: Regenerate.
gas/
	* config/tc-ppc.c (ppc_elf_suffix): Support @pcrel, @got@pcrel,
	@plt@pcrel, @higher34, @highera34, @highest34, and @highesta34.
	(fixup_size): Handle new powerxx relocs.
	(md_assemble): Warn for @pcrel on non-prefix insns.
	Accept @l, @h and @ha on prefix insns, and infer reloc without
	any @ suffix.  Translate powerxx relocs to suit DQ and DS field
	instructions.  Include operand tests as well as opcode test to
	translate BFD_RELOC_HI16_S to BFD_RELOC_PPC_16DX_HA.
	(ppc_fix_adjustable): Return false for pcrel GOT and PLT relocs.
	(md_apply_fix): Handle new powerxx relocs.
	* config/tc-ppc.h (TC_FORCE_RELOCATION_SUB_LOCAL): Accept
	BFD_RELOC_PPC64_ADDR16_HIGHER34, BFD_RELOC_PPC64_ADDR16_HIGHERA34,
	BFD_RELOC_PPC64_ADDR16_HIGHEST34, BFD_RELOC_PPC64_ADDR16_HIGHESTA34,
	BFD_RELOC_PPC64_D34, and BFD_RELOC_PPC64_D28.
	* testsuite/gas/ppc/prefix-reloc.d,
	* testsuite/gas/ppc/prefix-reloc.s: New test.
	* testsuite/gas/ppc/ppc.exp: Run it.

diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index 4eb0153dfb..ad47bed372 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -51,6 +51,8 @@ static bfd_reloc_status_type ppc64_elf_toc_ha_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
 static bfd_reloc_status_type ppc64_elf_toc64_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type ppc64_elf_prefix_reloc
+  (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
 static bfd_reloc_status_type ppc64_elf_unhandled_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
 static bfd_vma opd_entry_value
@@ -197,6 +199,7 @@ static bfd_vma opd_entry_value
 #define SLDI_R12_R12_32	0x799c07c6	/* sldi  %r12,%r12,32     */
 #define LDX_R12_R11_R12 0x7d8b602a	/* ldx   %r12,%r11,%r12   */
 #define ADD_R12_R11_R12 0x7d8b6214	/* add   %r12,%r11,%r12   */
+#define PNOP		0x0700000000000000ULL
 
 /* __glink_PLTresolve stub instructions.  We enter with the index in R0.  */
 #define GLINK_PLTRESOLVE_SIZE(htab)			\
@@ -878,6 +881,69 @@ static reloc_howto_type ppc64_elf_howto_raw[] =
   HOW (R_PPC64_ADDR64_LOCAL, 4, 64, 0xffffffffffffffffULL, 0, FALSE, dont,
        bfd_elf_generic_reloc),
 
+  HOW (R_PPC64_PLTSEQ_NOTOC, 2, 32, 0, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_PLTCALL_NOTOC, 2, 32, 0, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_PCREL_OPT, 2, 32, 0, 0, FALSE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_D34, 4, 34, 0x3ffff0000ffffULL, 0, FALSE, signed,
+       ppc64_elf_prefix_reloc),
+
+  HOW (R_PPC64_D34_LO, 4, 34, 0x3ffff0000ffffULL, 0, FALSE, dont,
+       ppc64_elf_prefix_reloc),
+
+  HOW (R_PPC64_D34_HI30, 4, 34, 0x3ffff0000ffffULL, 34, FALSE, dont,
+       ppc64_elf_prefix_reloc),
+
+  HOW (R_PPC64_D34_HA30, 4, 34, 0x3ffff0000ffffULL, 34, FALSE, dont,
+       ppc64_elf_prefix_reloc),
+
+  HOW (R_PPC64_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed,
+       ppc64_elf_prefix_reloc),
+
+  HOW (R_PPC64_GOT_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed,
+       ppc64_elf_unhandled_reloc),
+
+  HOW (R_PPC64_PLT_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed,
+       ppc64_elf_unhandled_reloc),
+
+  HOW (R_PPC64_PLT_PCREL34_NOTOC, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed,
+       ppc64_elf_unhandled_reloc),
+
+  HOW (R_PPC64_ADDR16_HIGHER34, 1, 16, 0xffff, 34, FALSE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_ADDR16_HIGHERA34, 1, 16, 0xffff, 34, FALSE, dont,
+       ppc64_elf_ha_reloc),
+
+  HOW (R_PPC64_ADDR16_HIGHEST34, 1, 16, 0xffff, 50, FALSE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_ADDR16_HIGHESTA34, 1, 16, 0xffff, 50, FALSE, dont,
+       ppc64_elf_ha_reloc),
+
+  HOW (R_PPC64_REL16_HIGHER34, 1, 16, 0xffff, 34, TRUE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_REL16_HIGHERA34, 1, 16, 0xffff, 34, TRUE, dont,
+       ppc64_elf_ha_reloc),
+
+  HOW (R_PPC64_REL16_HIGHEST34, 1, 16, 0xffff, 50, TRUE, dont,
+       bfd_elf_generic_reloc),
+
+  HOW (R_PPC64_REL16_HIGHESTA34, 1, 16, 0xffff, 50, TRUE, dont,
+       ppc64_elf_ha_reloc),
+
+  HOW (R_PPC64_D28, 4, 28, 0xfff0000ffffULL, 0, FALSE, signed,
+       ppc64_elf_prefix_reloc),
+
+  HOW (R_PPC64_PCREL28, 4, 28, 0xfff0000ffffULL, 0, TRUE, signed,
+       ppc64_elf_prefix_reloc),
+
   /* GNU extension to record C++ vtable hierarchy.  */
   HOW (R_PPC64_GNU_VTINHERIT, 0, 0, 0, 0, FALSE, dont,
        NULL),
@@ -1167,6 +1233,40 @@ ppc64_elf_reloc_type_lookup (bfd *abfd,
       break;
     case BFD_RELOC_PPC64_ADDR64_LOCAL:		r = R_PPC64_ADDR64_LOCAL;
       break;
+    case BFD_RELOC_PPC64_D34:			r = R_PPC64_D34;
+      break;
+    case BFD_RELOC_PPC64_D34_LO:		r = R_PPC64_D34_LO;
+      break;
+    case BFD_RELOC_PPC64_D34_HI30:		r = R_PPC64_D34_HI30;
+      break;
+    case BFD_RELOC_PPC64_D34_HA30:		r = R_PPC64_D34_HA30;
+      break;
+    case BFD_RELOC_PPC64_PCREL34:		r = R_PPC64_PCREL34;
+      break;
+    case BFD_RELOC_PPC64_GOT_PCREL34:		r = R_PPC64_GOT_PCREL34;
+      break;
+    case BFD_RELOC_PPC64_PLT_PCREL34:		r = R_PPC64_PLT_PCREL34;
+      break;
+    case BFD_RELOC_PPC64_ADDR16_HIGHER34:	r = R_PPC64_ADDR16_HIGHER34;
+      break;
+    case BFD_RELOC_PPC64_ADDR16_HIGHERA34:	r = R_PPC64_ADDR16_HIGHERA34;
+      break;
+    case BFD_RELOC_PPC64_ADDR16_HIGHEST34:	r = R_PPC64_ADDR16_HIGHEST34;
+      break;
+    case BFD_RELOC_PPC64_ADDR16_HIGHESTA34:	r = R_PPC64_ADDR16_HIGHESTA34;
+      break;
+    case BFD_RELOC_PPC64_REL16_HIGHER34:	r = R_PPC64_REL16_HIGHER34;
+      break;
+    case BFD_RELOC_PPC64_REL16_HIGHERA34:	r = R_PPC64_REL16_HIGHERA34;
+      break;
+    case BFD_RELOC_PPC64_REL16_HIGHEST34:	r = R_PPC64_REL16_HIGHEST34;
+      break;
+    case BFD_RELOC_PPC64_REL16_HIGHESTA34:	r = R_PPC64_REL16_HIGHESTA34;
+      break;
+    case BFD_RELOC_PPC64_D28:			r = R_PPC64_D28;
+      break;
+    case BFD_RELOC_PPC64_PCREL28:		r = R_PPC64_PCREL28;
+      break;
     case BFD_RELOC_VTABLE_INHERIT:		r = R_PPC64_GNU_VTINHERIT;
       break;
     case BFD_RELOC_VTABLE_ENTRY:		r = R_PPC64_GNU_VTENTRY;
@@ -1243,11 +1343,17 @@ ppc64_elf_ha_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
     return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
 				  input_section, output_bfd, error_message);
 
-  /* Adjust the addend for sign extension of the low 16 bits.
-     We won't actually be using the low 16 bits, so trashing them
+  /* Adjust the addend for sign extension of the low 16 (or 34) bits.
+     We won't actually be using the low bits, so trashing them
      doesn't matter.  */
-  reloc_entry->addend += 0x8000;
   r_type = reloc_entry->howto->type;
+  if (r_type == R_PPC64_ADDR16_HIGHERA34
+      || r_type == R_PPC64_ADDR16_HIGHESTA34
+      || r_type == R_PPC64_REL16_HIGHERA34
+      || r_type == R_PPC64_REL16_HIGHESTA34)
+    reloc_entry->addend += 1ULL << 33;
+  else
+    reloc_entry->addend += 1U << 15;
   if (r_type != R_PPC64_REL16DX_HA)
     return bfd_reloc_continue;
 
@@ -1492,6 +1598,48 @@ ppc64_elf_toc64_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
   return bfd_reloc_ok;
 }
 
+static bfd_reloc_status_type
+ppc64_elf_prefix_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+			void *data, asection *input_section,
+			bfd *output_bfd, char **error_message)
+{
+  uint64_t insn;
+  bfd_vma targ;
+
+  if (output_bfd != NULL)
+    return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
+				  input_section, output_bfd, error_message);
+
+  insn = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
+  insn <<= 32;
+  insn |= bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address + 4);
+
+  targ = (symbol->section->output_section->vma
+	  + symbol->section->output_offset
+	  + reloc_entry->addend);
+  if (!bfd_is_com_section (symbol->section))
+    targ += symbol->value;
+  if (reloc_entry->howto->type == R_PPC64_D34_HA30)
+    targ += 1ULL << 33;
+  if (reloc_entry->howto->pc_relative)
+    {
+      bfd_vma from = (reloc_entry->address
+		      + input_section->output_offset
+		      + input_section->output_section->vma);
+      targ -=from;
+    }
+  targ >>= reloc_entry->howto->rightshift;
+  insn &= ~reloc_entry->howto->dst_mask;
+  insn |= ((targ << 16) | (targ & 0xffff)) & reloc_entry->howto->dst_mask;
+  bfd_put_32 (abfd, insn >> 32, (bfd_byte *) data + reloc_entry->address);
+  bfd_put_32 (abfd, insn, (bfd_byte *) data + reloc_entry->address + 4);
+  if (reloc_entry->howto->complain_on_overflow == complain_overflow_signed
+      && (targ + (1ULL << (reloc_entry->howto->bitsize - 1))
+	  >= 1ULL << reloc_entry->howto->bitsize))
+    return bfd_reloc_overflow;
+  return bfd_reloc_ok;
+}
+
 static bfd_reloc_status_type
 ppc64_elf_unhandled_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
 			   void *data, asection *input_section,
@@ -2981,6 +3129,9 @@ struct ppc_link_hash_table
   /* Whether plt calls for ELFv2 localentry:0 funcs have been optimized.  */
   unsigned int has_plt_localentry0:1;
 
+  /* Whether calls are made via the PLT from NOTOC functions.  */
+  unsigned int notoc_plt:1;
+
   /* Incremented every time we size stubs.  */
   unsigned int stub_iteration;
 
@@ -4235,7 +4386,8 @@ is_branch_reloc (enum elf_ppc64_reloc_type r_type)
 	  || r_type == R_PPC64_ADDR14
 	  || r_type == R_PPC64_ADDR14_BRTAKEN
 	  || r_type == R_PPC64_ADDR14_BRNTAKEN
-	  || r_type == R_PPC64_PLTCALL);
+	  || r_type == R_PPC64_PLTCALL
+	  || r_type == R_PPC64_PLTCALL_NOTOC);
 }
 
 /* Relocs on inline plt call sequence insns prior to the call.  */
@@ -4247,7 +4399,10 @@ is_plt_seq_reloc (enum elf_ppc64_reloc_type r_type)
 	  || r_type == R_PPC64_PLT16_HI
 	  || r_type == R_PPC64_PLT16_LO
 	  || r_type == R_PPC64_PLT16_LO_DS
-	  || r_type == R_PPC64_PLTSEQ);
+	  || r_type == R_PPC64_PLT_PCREL34
+	  || r_type == R_PPC64_PLT_PCREL34_NOTOC
+	  || r_type == R_PPC64_PLTSEQ
+	  || r_type == R_PPC64_PLTSEQ_NOTOC);
 }
 
 /* Look through the relocs for a section during the first phase, and
@@ -4302,6 +4457,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       int tls_type;
       struct _ppc64_elf_section_data *ppc64_sec;
       struct plt_entry **ifunc, **plt_list;
+      bfd_vma sym_addend;
 
       r_symndx = ELF64_R_SYM (rel->r_info);
       if (r_symndx < symtab_hdr->sh_info)
@@ -4317,6 +4473,24 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
       tls_type = 0;
       ifunc = NULL;
+      r_type = ELF64_R_TYPE (rel->r_info);
+      switch (r_type)
+	{
+	default:
+	  /* Somewhat foolishly, because the ABIs don't specifically
+	     allow it, ppc64 gas and ld support GOT and PLT relocs
+	     with non-zero addends where the addend results in
+	     sym+addend being stored in the GOT or PLT entry.  This
+	     can't be supported for pcrel relocs because the addend is
+	     used to specify the pcrel offset.  */
+	  sym_addend = rel->r_addend;
+	  break;
+	case R_PPC64_GOT_PCREL34:
+	case R_PPC64_PLT_PCREL34:
+	case R_PPC64_PLT_PCREL34_NOTOC:
+	  sym_addend = 0;
+	  break;
+	}
       if (h != NULL)
 	{
 	  if (h->type == STT_GNU_IFUNC)
@@ -4335,14 +4509,13 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	  if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
 	    {
 	      ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
-					     rel->r_addend,
+					     sym_addend,
 					     NON_GOT | PLT_IFUNC);
 	      if (ifunc == NULL)
 		return FALSE;
 	    }
 	}
 
-      r_type = ELF64_R_TYPE (rel->r_info);
       switch (r_type)
 	{
 	case R_PPC64_TLSGD:
@@ -4353,7 +4526,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	    ((struct ppc_link_hash_entry *) h)->tls_mask |= TLS_TLS | TLS_MARK;
 	  else
 	    if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
-					rel->r_addend,
+					sym_addend,
 					NON_GOT | TLS_TLS | TLS_MARK))
 	      return FALSE;
 	  sec->has_tls_reloc = 1;
@@ -4401,6 +4574,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	case R_PPC64_GOT16:
 	case R_PPC64_GOT16_HI:
 	case R_PPC64_GOT16_LO:
+	case R_PPC64_GOT_PCREL34:
 	dogot:
 	  /* This symbol requires a global offset table entry.  */
 	  sec->has_toc_reloc = 1;
@@ -4426,7 +4600,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
 	      eh = (struct ppc_link_hash_entry *) h;
 	      for (ent = eh->elf.got.glist; ent != NULL; ent = ent->next)
-		if (ent->addend == rel->r_addend
+		if (ent->addend == sym_addend
 		    && ent->owner == abfd
 		    && ent->tls_type == tls_type)
 		  break;
@@ -4437,7 +4611,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 		  if (ent == NULL)
 		    return FALSE;
 		  ent->next = eh->elf.got.glist;
-		  ent->addend = rel->r_addend;
+		  ent->addend = sym_addend;
 		  ent->owner = abfd;
 		  ent->tls_type = tls_type;
 		  ent->is_indirect = FALSE;
@@ -4450,14 +4624,14 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	  else
 	    /* This is a global offset table entry for a local symbol.  */
 	    if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
-					rel->r_addend, tls_type))
+					sym_addend, tls_type))
 	      return FALSE;
 
 	  /* We may also need a plt entry if the symbol turns out to be
 	     an ifunc.  */
 	  if (h != NULL && !bfd_link_pic (info) && abiversion (abfd) != 1)
 	    {
-	      if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend))
+	      if (!update_plt_info (abfd, &h->plt.plist, sym_addend))
 		return FALSE;
 	    }
 	  break;
@@ -4466,6 +4640,8 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	case R_PPC64_PLT16_HI:
 	case R_PPC64_PLT16_LO:
 	case R_PPC64_PLT16_LO_DS:
+	case R_PPC64_PLT_PCREL34:
+	case R_PPC64_PLT_PCREL34_NOTOC:
 	case R_PPC64_PLT32:
 	case R_PPC64_PLT64:
 	  /* This symbol requires a procedure linkage table entry.  */
@@ -4481,9 +4657,9 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	    }
 	  if (plt_list == NULL)
 	    plt_list = update_local_sym_info (abfd, symtab_hdr, r_symndx,
-					      rel->r_addend,
+					      sym_addend,
 					      NON_GOT | PLT_KEEP);
-	  if (!update_plt_info (abfd, plt_list, rel->r_addend))
+	  if (!update_plt_info (abfd, plt_list, sym_addend))
 	    return FALSE;
 	  break;
 
@@ -4521,6 +4697,10 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	case R_PPC64_REL16_HIGHERA:
 	case R_PPC64_REL16_HIGHEST:
 	case R_PPC64_REL16_HIGHESTA:
+	case R_PPC64_REL16_HIGHER34:
+	case R_PPC64_REL16_HIGHERA34:
+	case R_PPC64_REL16_HIGHEST34:
+	case R_PPC64_REL16_HIGHESTA34:
 	case R_PPC64_REL16DX_HA:
 	  break;
 
@@ -4603,6 +4783,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	  goto rel24;
 
 	case R_PPC64_PLTCALL:
+	case R_PPC64_PLTCALL_NOTOC:
 	  ppc64_elf_section_data (sec)->has_pltcall = 1;
 	  /* Fall through.  */
 
@@ -4636,7 +4817,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	  /* We may need a .plt entry if the function this reloc
 	     refers to is in a shared lib.  */
 	  if (plt_list
-	      && !update_plt_info (abfd, plt_list, rel->r_addend))
+	      && !update_plt_info (abfd, plt_list, sym_addend))
 	    return FALSE;
 	  break;
 
@@ -4680,7 +4861,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	    }
 	  else
 	    if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
-					rel->r_addend, tls_type))
+					sym_addend, tls_type))
 	      return FALSE;
 
 	  ppc64_sec = ppc64_elf_section_data (sec);
@@ -4702,7 +4883,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	    }
 	  BFD_ASSERT (rel->r_offset % 8 == 0);
 	  ppc64_sec->u.toc.symndx[rel->r_offset / 8] = r_symndx;
-	  ppc64_sec->u.toc.add[rel->r_offset / 8] = rel->r_addend;
+	  ppc64_sec->u.toc.add[rel->r_offset / 8] = sym_addend;
 
 	  /* Mark the second slot of a GD or LD entry.
 	     -1 to indicate GD and -2 to indicate LD.  */
@@ -4750,12 +4931,21 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	case R_PPC64_ADDR16_HIGHESTA:
 	case R_PPC64_ADDR16_LO:
 	case R_PPC64_ADDR16_LO_DS:
+	case R_PPC64_D34:
+	case R_PPC64_D34_LO:
+	case R_PPC64_D34_HI30:
+	case R_PPC64_D34_HA30:
+	case R_PPC64_ADDR16_HIGHER34:
+	case R_PPC64_ADDR16_HIGHERA34:
+	case R_PPC64_ADDR16_HIGHEST34:
+	case R_PPC64_ADDR16_HIGHESTA34:
+	case R_PPC64_D28:
 	  if (h != NULL && !bfd_link_pic (info) && abiversion (abfd) != 1
 	      && rel->r_addend == 0)
 	    {
 	      /* We may need a .plt entry if this reloc refers to a
 		 function in a shared lib.  */
-	      if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend))
+	      if (!update_plt_info (abfd, &h->plt.plist, 0))
 		return FALSE;
 	      h->pointer_equality_needed = 1;
 	    }
@@ -6562,6 +6752,15 @@ dec_dynrel_count (bfd_vma r_info,
     case R_PPC64_UADDR32:
     case R_PPC64_UADDR64:
     case R_PPC64_TOC:
+    case R_PPC64_D34:
+    case R_PPC64_D34_LO:
+    case R_PPC64_D34_HI30:
+    case R_PPC64_D34_HA30:
+    case R_PPC64_ADDR16_HIGHER34:
+    case R_PPC64_ADDR16_HIGHERA34:
+    case R_PPC64_ADDR16_HIGHEST34:
+    case R_PPC64_ADDR16_HIGHESTA34:
+    case R_PPC64_D28:
       break;
     }
 
@@ -7167,7 +7366,8 @@ ppc64_elf_inline_plt (struct bfd_link_info *info)
 		unsigned char *tls_maskp;
 
 		r_type = ELF64_R_TYPE (rel->r_info);
-		if (r_type != R_PPC64_PLTCALL)
+		if (r_type != R_PPC64_PLTCALL
+		    && r_type != R_PPC64_PLTCALL_NOTOC)
 		  continue;
 
 		r_symndx = ELF64_R_SYM (rel->r_info);
@@ -7195,7 +7395,11 @@ ppc64_elf_inline_plt (struct bfd_link_info *info)
 		    from = (rel->r_offset
 			    + sec->output_offset
 			    + sec->output_section->vma);
-		    if (to - from + limit < 2 * limit)
+		    if (to - from + limit < 2 * limit
+			&& !(r_type == R_PPC64_PLTCALL_NOTOC
+			     && (((h ? h->other : sym->st_other)
+				  & STO_PPC64_LOCAL_MASK)
+				 != 1 << STO_PPC64_LOCAL_BIT)))
 		      *tls_maskp &= ~PLT_KEEP;
 		  }
 	      }
@@ -7574,7 +7778,9 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
 			{
 			  if (pass != 0
 			      && (ELF64_R_TYPE (rel[1].r_info)
-				  != R_PPC64_PLTSEQ))
+				  != R_PPC64_PLTSEQ)
+			      && (ELF64_R_TYPE (rel[1].r_info)
+				  != R_PPC64_PLTSEQ_NOTOC))
 			    {
 			      r_symndx = ELF64_R_SYM (rel[1].r_info);
 			      if (!get_sym_h (&h, NULL, NULL, NULL, &locsyms,
@@ -11631,7 +11837,8 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
 	      && r_type != R_PPC64_REL14
 	      && r_type != R_PPC64_REL14_BRTAKEN
 	      && r_type != R_PPC64_REL14_BRNTAKEN
-	      && r_type != R_PPC64_PLTCALL)
+	      && r_type != R_PPC64_PLTCALL
+	      && r_type != R_PPC64_PLTCALL_NOTOC)
 	    continue;
 
 	  r_symndx = ELF64_R_SYM (rel->r_info);
@@ -14030,10 +14237,14 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	    {
 	      unsigned int insn2;
 	      bfd_vma offset = rel->r_offset;
+	      enum elf_ppc64_reloc_type r_type1 = ELF64_R_TYPE (rel[1].r_info);
 
-	      if (is_plt_seq_reloc (ELF64_R_TYPE (rel[1].r_info)))
+	      if (is_plt_seq_reloc (r_type1))
 		{
 		  bfd_put_32 (output_bfd, NOP, contents + offset);
+		  if (r_type1 == R_PPC64_PLT_PCREL34
+		      || r_type1 == R_PPC64_PLT_PCREL34_NOTOC)
+		    bfd_put_32 (output_bfd, NOP, contents + offset + 4);
 		  rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE);
 		  break;
 		}
@@ -14075,10 +14286,14 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	    {
 	      unsigned int insn2;
 	      bfd_vma offset = rel->r_offset;
+	      enum elf_ppc64_reloc_type r_type1 = ELF64_R_TYPE (rel[1].r_info);
 
-	      if (is_plt_seq_reloc (ELF64_R_TYPE (rel[1].r_info)))
+	      if (is_plt_seq_reloc (r_type1))
 		{
 		  bfd_put_32 (output_bfd, NOP, contents + offset);
+		  if (r_type1 == R_PPC64_PLT_PCREL34
+		      || r_type1 == R_PPC64_PLT_PCREL34_NOTOC)
+		    bfd_put_32 (output_bfd, NOP, contents + offset + 4);
 		  rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE);
 		  break;
 		}
@@ -14279,6 +14494,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	case R_PPC64_REL24:
 	case R_PPC64_REL24_NOTOC:
 	case R_PPC64_PLTCALL:
+	case R_PPC64_PLTCALL_NOTOC:
 	  /* Calls to functions with a different TOC, such as calls to
 	     shared objects, need to alter the TOC pointer.  This is
 	     done using a linkage stub.  A REL24 branching to these
@@ -14292,7 +14508,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	    fdh = ppc_follow_link (h->oh);
 	  stub_entry = ppc_get_stub_entry (input_section, sec, fdh, &orig_rel,
 					   htab);
-	  if (r_type == R_PPC64_PLTCALL
+	  if ((r_type == R_PPC64_PLTCALL
+	       || r_type == R_PPC64_PLTCALL_NOTOC)
 	      && stub_entry != NULL
 	      && stub_entry->stub_type >= ppc_stub_plt_call
 	      && stub_entry->stub_type <= ppc_stub_plt_call_both)
@@ -14522,6 +14739,11 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 			|| stub_entry->stub_type == ppc_stub_plt_call_both)
 		       && r_type == R_PPC64_REL24_NOTOC)
 		relocation += 4;
+
+	      if (r_type == R_PPC64_REL24_NOTOC
+		  && (stub_entry->stub_type == ppc_stub_plt_call_notoc
+		      || stub_entry->stub_type == ppc_stub_plt_call_both))
+		htab->notoc_plt = 1;
 	    }
 
 	  if (insn != 0)
@@ -14665,6 +14887,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	case R_PPC64_GOT16_HA:
 	case R_PPC64_GOT16_DS:
 	case R_PPC64_GOT16_LO_DS:
+	case R_PPC64_GOT_PCREL34:
 	dogot:
 	  {
 	    /* Relocation is to the entry for this symbol in the global
@@ -14674,6 +14897,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	    bfd_vma off;
 	    unsigned long indx = 0;
 	    struct got_entry *ent;
+	    bfd_vma sym_addend = orig_rel.r_addend;
+
+	    if (r_type == R_PPC64_GOT_PCREL34)
+	      sym_addend = 0;
 
 	    if (tls_type == (TLS_TLS | TLS_LD)
 		&& (h == NULL
@@ -14707,7 +14934,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 		  }
 
 		for (; ent != NULL; ent = ent->next)
-		  if (ent->addend == orig_rel.r_addend
+		  if (ent->addend == sym_addend
 		      && ent->owner == input_bfd
 		      && ent->tls_type == tls_type)
 		    break;
@@ -14764,7 +14991,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 		    outrel.r_offset = (got->output_section->vma
 				       + got->output_offset
 				       + off);
-		    outrel.r_addend = addend;
+		    outrel.r_addend = sym_addend;
 		    if (tls_type & (TLS_LD | TLS_GD))
 		      {
 			outrel.r_addend = 0;
@@ -14777,7 +15004,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 			    bfd_elf64_swap_reloca_out (output_bfd,
 						       &outrel, loc);
 			    outrel.r_offset += 8;
-			    outrel.r_addend = addend;
+			    outrel.r_addend = sym_addend;
 			    outrel.r_info
 			      = ELF64_R_INFO (indx, R_PPC64_DTPREL64);
 			  }
@@ -14823,7 +15050,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 		   emitting a reloc.  */
 		else
 		  {
-		    relocation += addend;
+		    relocation += sym_addend;
 		    if (tls_type != 0)
 		      {
 			if (htab->elf.tls_sec == NULL)
@@ -14854,7 +15081,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	      abort ();
 
 	    relocation = got->output_section->vma + got->output_offset + off;
-	    addend = -(TOCstart + htab->sec_info[input_section->id].toc_off);
+	    if (r_type != R_PPC64_GOT_PCREL34)
+	      addend = -(TOCstart + htab->sec_info[input_section->id].toc_off);
 	  }
 	  break;
 
@@ -14862,10 +15090,14 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	case R_PPC64_PLT16_HI:
 	case R_PPC64_PLT16_LO:
 	case R_PPC64_PLT16_LO_DS:
+	case R_PPC64_PLT_PCREL34:
+	case R_PPC64_PLT_PCREL34_NOTOC:
 	case R_PPC64_PLT32:
 	case R_PPC64_PLT64:
 	case R_PPC64_PLTSEQ:
+	case R_PPC64_PLTSEQ_NOTOC:
 	case R_PPC64_PLTCALL:
+	case R_PPC64_PLTCALL_NOTOC:
 	  /* Relocation is to the entry for this symbol in the
 	     procedure linkage table.  */
 	  unresolved_reloc = TRUE;
@@ -14882,10 +15114,15 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	    if (plt_list)
 	      {
 		struct plt_entry *ent;
+		bfd_vma sym_addend = orig_rel.r_addend;
+
+		if (r_type == R_PPC64_PLT_PCREL34
+		    || r_type == R_PPC64_PLT_PCREL34_NOTOC)
+		  sym_addend = 0;
 
 		for (ent = *plt_list; ent != NULL; ent = ent->next)
 		  if (ent->plt.offset != (bfd_vma) -1
-		      && ent->addend == orig_rel.r_addend)
+		      && ent->addend == sym_addend)
 		    {
 		      asection *plt;
 		      bfd_vma got;
@@ -14914,7 +15151,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 				 + htab->sec_info[input_section->id].toc_off);
 			  relocation -= got;
 			}
-		      addend = 0;
+		      if (r_type != R_PPC64_PLT_PCREL34
+			  && r_type != R_PPC64_PLT_PCREL34_NOTOC)
+			addend = 0;
 		      unresolved_reloc = FALSE;
 		      break;
 		    }
@@ -14969,14 +15208,18 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	case R_PPC64_REL16_HIGHERA:
 	case R_PPC64_REL16_HIGHEST:
 	case R_PPC64_REL16_HIGHESTA:
+	case R_PPC64_REL16_HIGHER34:
+	case R_PPC64_REL16_HIGHERA34:
+	case R_PPC64_REL16_HIGHEST34:
+	case R_PPC64_REL16_HIGHESTA34:
 	case R_PPC64_REL16DX_HA:
-	  break;
-
 	case R_PPC64_REL14:
 	case R_PPC64_REL14_BRNTAKEN:
 	case R_PPC64_REL14_BRTAKEN:
 	case R_PPC64_REL24:
 	case R_PPC64_REL24_NOTOC:
+	case R_PPC64_PCREL34:
+	case R_PPC64_PCREL28:
 	  break;
 
 	case R_PPC64_TPREL16:
@@ -15071,12 +15314,21 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	case R_PPC64_ADDR16_HIGHESTA:
 	case R_PPC64_ADDR16_LO:
 	case R_PPC64_ADDR16_LO_DS:
+	case R_PPC64_ADDR16_HIGHER34:
+	case R_PPC64_ADDR16_HIGHERA34:
+	case R_PPC64_ADDR16_HIGHEST34:
+	case R_PPC64_ADDR16_HIGHESTA34:
 	case R_PPC64_ADDR24:
 	case R_PPC64_ADDR32:
 	case R_PPC64_ADDR64:
 	case R_PPC64_UADDR16:
 	case R_PPC64_UADDR32:
 	case R_PPC64_UADDR64:
+	case R_PPC64_D34:
+	case R_PPC64_D34_LO:
+	case R_PPC64_D34_HI30:
+	case R_PPC64_D34_HA30:
+	case R_PPC64_D28:
 	dodyn:
 	  if ((input_section->flags & SEC_ALLOC) == 0)
 	    break;
@@ -15328,6 +15580,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	     insn.  */
 	  break;
 
+	case R_PPC64_PLTCALL_NOTOC:
+	  if (!unresolved_reloc)
+	    htab->notoc_plt = 1;
+	  /* Fall through.  */
 	case R_PPC64_PLTCALL:
 	  if (unresolved_reloc)
 	    {
@@ -15336,12 +15592,14 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	      insn = bfd_get_32 (input_bfd, p);
 	      insn &= 1;
 	      bfd_put_32 (input_bfd, B_DOT | insn, p);
-	      bfd_put_32 (input_bfd, NOP, p + 4);
+	      if (r_type == R_PPC64_PLTCALL)
+		bfd_put_32 (input_bfd, NOP, p + 4);
 	      unresolved_reloc = save_unresolved_reloc;
 	      r_type = R_PPC64_REL24;
 	    }
 	  break;
 
+	case R_PPC64_PLTSEQ_NOTOC:
 	case R_PPC64_PLTSEQ:
 	  if (unresolved_reloc)
 	    {
@@ -15350,6 +15608,21 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	    }
 	  break;
 
+	case R_PPC64_PLT_PCREL34_NOTOC:
+	  if (!unresolved_reloc)
+	    htab->notoc_plt = 1;
+	  /* Fall through.  */
+	case R_PPC64_PLT_PCREL34:
+	  if (unresolved_reloc)
+	    {
+	      bfd_byte *p = contents + rel->r_offset;
+	      bfd_put_32 (input_bfd, PNOP >> 32, p);
+	      bfd_put_32 (input_bfd, PNOP, p + 4);
+	      unresolved_reloc = FALSE;
+	      goto copy_reloc;
+	    }
+	  break;
+
 	case R_PPC64_PLT16_HA:
 	  if (unresolved_reloc)
 	    {
@@ -15488,6 +15761,15 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	  addend += 0x8000;
 	  break;
 
+	case R_PPC64_D34_HA30:
+	case R_PPC64_ADDR16_HIGHERA34:
+	case R_PPC64_ADDR16_HIGHESTA34:
+	case R_PPC64_REL16_HIGHERA34:
+	case R_PPC64_REL16_HIGHESTA34:
+	  if (sec != NULL)
+	    addend += 1ULL << 33;
+	  break;
+
 	case R_PPC64_ADDR16_DS:
 	case R_PPC64_ADDR16_LO_DS:
 	case R_PPC64_GOT16_DS:
@@ -15583,9 +15865,50 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	    }
 	}
 
-      if (r_type == R_PPC64_REL16DX_HA)
+      switch (r_type)
 	{
-	  /* Split field reloc isn't handled by _bfd_final_link_relocate.  */
+	  /* Split field relocs aren't handled by _bfd_final_link_relocate.  */
+	case R_PPC64_D34:
+	case R_PPC64_D34_LO:
+	case R_PPC64_D34_HI30:
+	case R_PPC64_D34_HA30:
+	case R_PPC64_PCREL34:
+	case R_PPC64_GOT_PCREL34:
+	case R_PPC64_PLT_PCREL34:
+	case R_PPC64_PLT_PCREL34_NOTOC:
+	case R_PPC64_D28:
+	case R_PPC64_PCREL28:
+	  if (rel->r_offset + 8 > input_section->size)
+	    r = bfd_reloc_outofrange;
+	  else
+	    {
+	      uint64_t pinsn;
+
+	      relocation += addend;
+	      if (howto->pc_relative)
+		relocation -= (rel->r_offset
+			       + input_section->output_offset
+			       + input_section->output_section->vma);
+	      relocation >>= howto->rightshift;
+
+	      pinsn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+	      pinsn <<= 32;
+	      pinsn |= bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
+
+	      pinsn &= ~howto->dst_mask;
+	      pinsn |= (((relocation << 16) | (relocation & 0xffff))
+			& howto->dst_mask);
+	      bfd_put_32 (input_bfd, pinsn >> 32, contents + rel->r_offset);
+	      bfd_put_32 (input_bfd, pinsn, contents + rel->r_offset + 4);
+	      r = bfd_reloc_ok;
+	      if (howto->complain_on_overflow == complain_overflow_signed
+		  && (relocation + (1ULL << (howto->bitsize - 1))
+		      >= 1ULL << howto->bitsize))
+		r = bfd_reloc_overflow;
+	    }
+	  break;
+
+	case R_PPC64_REL16DX_HA:
 	  if (rel->r_offset + 4 > input_section->size)
 	    r = bfd_reloc_outofrange;
 	  else
@@ -15603,10 +15926,13 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	      if (relocation + 0x8000 > 0xffff)
 		r = bfd_reloc_overflow;
 	    }
+	  break;
+
+	default:
+	  r = _bfd_final_link_relocate (howto, input_bfd, input_section,
+					contents, rel->r_offset,
+					relocation, addend);
 	}
-      else
-	r = _bfd_final_link_relocate (howto, input_bfd, input_section, contents,
-				      rel->r_offset, relocation, addend);
 
       if (r != bfd_reloc_ok)
 	{
@@ -15884,7 +16210,8 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
 	      break;
 
 	    case DT_PPC64_OPT:
-	      if (htab->do_multi_toc && htab->multi_toc_needed)
+	      if ((htab->do_multi_toc && htab->multi_toc_needed)
+		  || htab->notoc_plt)
 		dyn.d_un.d_val |= PPC64_OPT_MULTI_TOC;
 	      if (htab->has_plt_localentry0)
 		dyn.d_un.d_val |= PPC64_OPT_LOCALENTRY;
diff --git a/bfd/reloc.c b/bfd/reloc.c
index 8f0263e354..266e775fa4 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -2878,6 +2878,40 @@ ENUMX
   BFD_RELOC_PPC64_ENTRY
 ENUMX
   BFD_RELOC_PPC64_REL24_NOTOC
+ENUMX
+  BFD_RELOC_PPC64_D34
+ENUMX
+  BFD_RELOC_PPC64_D34_LO
+ENUMX
+  BFD_RELOC_PPC64_D34_HI30
+ENUMX
+  BFD_RELOC_PPC64_D34_HA30
+ENUMX
+  BFD_RELOC_PPC64_PCREL34
+ENUMX
+  BFD_RELOC_PPC64_GOT_PCREL34
+ENUMX
+  BFD_RELOC_PPC64_PLT_PCREL34
+ENUMX
+  BFD_RELOC_PPC64_ADDR16_HIGHER34
+ENUMX
+  BFD_RELOC_PPC64_ADDR16_HIGHERA34
+ENUMX
+  BFD_RELOC_PPC64_ADDR16_HIGHEST34
+ENUMX
+  BFD_RELOC_PPC64_ADDR16_HIGHESTA34
+ENUMX
+  BFD_RELOC_PPC64_REL16_HIGHER34
+ENUMX
+  BFD_RELOC_PPC64_REL16_HIGHERA34
+ENUMX
+  BFD_RELOC_PPC64_REL16_HIGHEST34
+ENUMX
+  BFD_RELOC_PPC64_REL16_HIGHESTA34
+ENUMX
+  BFD_RELOC_PPC64_D28
+ENUMX
+  BFD_RELOC_PPC64_PCREL28
 ENUMDOC
   Power(rs6000) and PowerPC relocations.
 
diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c
index 4026c72293..64ff149b21 100644
--- a/gas/config/tc-ppc.c
+++ b/gas/config/tc-ppc.c
@@ -2151,6 +2151,13 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p)
     MAP64 ("tprel@highest",	BFD_RELOC_PPC64_TPREL16_HIGHEST),
     MAP64 ("tprel@highesta",	BFD_RELOC_PPC64_TPREL16_HIGHESTA),
     MAP64 ("notoc",		BFD_RELOC_PPC64_REL24_NOTOC),
+    MAP64 ("pcrel",		BFD_RELOC_PPC64_PCREL34),
+    MAP64 ("got@pcrel",		BFD_RELOC_PPC64_GOT_PCREL34),
+    MAP64 ("plt@pcrel",		BFD_RELOC_PPC64_PLT_PCREL34),
+    MAP64 ("higher34",		BFD_RELOC_PPC64_ADDR16_HIGHER34),
+    MAP64 ("highera34",		BFD_RELOC_PPC64_ADDR16_HIGHERA34),
+    MAP64 ("highest34",		BFD_RELOC_PPC64_ADDR16_HIGHEST34),
+    MAP64 ("highesta34",	BFD_RELOC_PPC64_ADDR16_HIGHESTA34),
     { (char *) 0, 0, 0, 0,	BFD_RELOC_NONE }
   };
 
@@ -2931,6 +2938,10 @@ fixup_size (bfd_reloc_code_real_type reloc, bfd_boolean *pc_relative)
     case BFD_RELOC_PPC64_ADDR16_DS:
     case BFD_RELOC_PPC64_ADDR16_HIGH:
     case BFD_RELOC_PPC64_ADDR16_HIGHA:
+    case BFD_RELOC_PPC64_ADDR16_HIGHER34:
+    case BFD_RELOC_PPC64_ADDR16_HIGHERA34:
+    case BFD_RELOC_PPC64_ADDR16_HIGHEST34:
+    case BFD_RELOC_PPC64_ADDR16_HIGHESTA34:
     case BFD_RELOC_PPC64_ADDR16_LO_DS:
     case BFD_RELOC_PPC64_DTPREL16_DS:
     case BFD_RELOC_PPC64_DTPREL16_HIGH:
@@ -3018,9 +3029,13 @@ fixup_size (bfd_reloc_code_real_type reloc, bfd_boolean *pc_relative)
     case BFD_RELOC_PPC64_REL16_HIGH:
     case BFD_RELOC_PPC64_REL16_HIGHA:
     case BFD_RELOC_PPC64_REL16_HIGHER:
+    case BFD_RELOC_PPC64_REL16_HIGHER34:
     case BFD_RELOC_PPC64_REL16_HIGHERA:
+    case BFD_RELOC_PPC64_REL16_HIGHERA34:
     case BFD_RELOC_PPC64_REL16_HIGHEST:
+    case BFD_RELOC_PPC64_REL16_HIGHEST34:
     case BFD_RELOC_PPC64_REL16_HIGHESTA:
+    case BFD_RELOC_PPC64_REL16_HIGHESTA34:
 #ifdef OBJ_XCOFF
     case BFD_RELOC_PPC_B16:
 #endif
@@ -3100,12 +3115,21 @@ fixup_size (bfd_reloc_code_real_type reloc, bfd_boolean *pc_relative)
     case BFD_RELOC_64:
     case BFD_RELOC_64_PLTOFF:
     case BFD_RELOC_PPC64_ADDR64_LOCAL:
+    case BFD_RELOC_PPC64_D28:
+    case BFD_RELOC_PPC64_D34:
+    case BFD_RELOC_PPC64_D34_LO:
+    case BFD_RELOC_PPC64_D34_HI30:
+    case BFD_RELOC_PPC64_D34_HA30:
     case BFD_RELOC_PPC64_TOC:
       size = 8;
       break;
 
     case BFD_RELOC_64_PCREL:
     case BFD_RELOC_64_PLT_PCREL:
+    case BFD_RELOC_PPC64_GOT_PCREL34:
+    case BFD_RELOC_PPC64_PCREL28:
+    case BFD_RELOC_PPC64_PCREL34:
+    case BFD_RELOC_PPC64_PLT_PCREL34:
       size = 8;
       pcrel = TRUE;
       break;
@@ -3665,24 +3689,47 @@ md_assemble (char *str)
 		  reloc = BFD_RELOC_PPC_TPREL16;
 		  break;
 
-		case BFD_RELOC_LO16:
-		  if ((operand->bitm | 0xf) != 0xffff
-		      || operand->shift != 0
+		case BFD_RELOC_PPC64_PCREL34:
+		  if (operand->bitm == 0xfffffffULL)
+		    {
+		      reloc = BFD_RELOC_PPC64_PCREL28;
+		      break;
+		    }
+		  /* Fall through.  */
+		case BFD_RELOC_PPC64_GOT_PCREL34:
+		case BFD_RELOC_PPC64_PLT_PCREL34:
+		  if (operand->bitm != 0x3ffffffffULL
 		      || (operand->flags & PPC_OPERAND_NEGATIVE) != 0)
+		    as_warn (_("%s unsupported on this instruction"), "@pcrel");
+		  break;
+
+		case BFD_RELOC_LO16:
+		  if (operand->bitm == 0x3ffffffffULL
+		      && (operand->flags & PPC_OPERAND_NEGATIVE) == 0)
+		    reloc = BFD_RELOC_PPC64_D34_LO;
+		  else if ((operand->bitm | 0xf) != 0xffff
+			   || operand->shift != 0
+			   || (operand->flags & PPC_OPERAND_NEGATIVE) != 0)
 		    as_warn (_("%s unsupported on this instruction"), "@l");
 		  break;
 
 		case BFD_RELOC_HI16:
-		  if (operand->bitm != 0xffff
-		      || operand->shift != 0
-		      || (operand->flags & PPC_OPERAND_NEGATIVE) != 0)
+		  if (operand->bitm == 0x3ffffffffULL
+		      && (operand->flags & PPC_OPERAND_NEGATIVE) == 0)
+		    reloc = BFD_RELOC_PPC64_D34_HI30;
+		  else if (operand->bitm != 0xffff
+			   || operand->shift != 0
+			   || (operand->flags & PPC_OPERAND_NEGATIVE) != 0)
 		    as_warn (_("%s unsupported on this instruction"), "@h");
 		  break;
 
 		case BFD_RELOC_HI16_S:
-		  if (operand->bitm == 0xffff
-		      && operand->shift == (int) PPC_OPSHIFT_INV
-		      && opcode->opcode == (19 << 26) + (2 << 1))
+		  if (operand->bitm == 0x3ffffffffULL
+		      && (operand->flags & PPC_OPERAND_NEGATIVE) == 0)
+		    reloc = BFD_RELOC_PPC64_D34_HA30;
+		  else if (operand->bitm == 0xffff
+			   && operand->shift == (int) PPC_OPSHIFT_INV
+			   && opcode->opcode == (19 << 26) + (2 << 1))
 		    /* addpcis.  */
 		    reloc = BFD_RELOC_PPC_16DX_HA;
 		  else if (operand->bitm != 0xffff
@@ -3738,6 +3785,10 @@ md_assemble (char *str)
 		}
 #endif
 	    }
+	  else if (operand->bitm == 0x3ffffffffULL)
+	    reloc = BFD_RELOC_PPC64_D34;
+	  else if (operand->bitm == 0xfffffffULL)
+	    reloc = BFD_RELOC_PPC64_D28;
 
 	  /* For the absolute forms of branches, convert the PC
 	     relative form back into the absolute.  */
@@ -3787,53 +3838,69 @@ md_assemble (char *str)
 		case BFD_RELOC_16:
 		  reloc = BFD_RELOC_PPC64_ADDR16_DS;
 		  break;
+
 		case BFD_RELOC_LO16:
 		  reloc = BFD_RELOC_PPC64_ADDR16_LO_DS;
 		  break;
+
 		case BFD_RELOC_16_GOTOFF:
 		  reloc = BFD_RELOC_PPC64_GOT16_DS;
 		  break;
+
 		case BFD_RELOC_LO16_GOTOFF:
 		  reloc = BFD_RELOC_PPC64_GOT16_LO_DS;
 		  break;
+
 		case BFD_RELOC_LO16_PLTOFF:
 		  reloc = BFD_RELOC_PPC64_PLT16_LO_DS;
 		  break;
+
 		case BFD_RELOC_16_BASEREL:
 		  reloc = BFD_RELOC_PPC64_SECTOFF_DS;
 		  break;
+
 		case BFD_RELOC_LO16_BASEREL:
 		  reloc = BFD_RELOC_PPC64_SECTOFF_LO_DS;
 		  break;
+
 		case BFD_RELOC_PPC_TOC16:
 		  reloc = BFD_RELOC_PPC64_TOC16_DS;
 		  break;
+
 		case BFD_RELOC_PPC64_TOC16_LO:
 		  reloc = BFD_RELOC_PPC64_TOC16_LO_DS;
 		  break;
+
 		case BFD_RELOC_PPC64_PLTGOT16:
 		  reloc = BFD_RELOC_PPC64_PLTGOT16_DS;
 		  break;
+
 		case BFD_RELOC_PPC64_PLTGOT16_LO:
 		  reloc = BFD_RELOC_PPC64_PLTGOT16_LO_DS;
 		  break;
+
 		case BFD_RELOC_PPC_DTPREL16:
 		  reloc = BFD_RELOC_PPC64_DTPREL16_DS;
 		  break;
+
 		case BFD_RELOC_PPC_DTPREL16_LO:
 		  reloc = BFD_RELOC_PPC64_DTPREL16_LO_DS;
 		  break;
+
 		case BFD_RELOC_PPC_TPREL16:
 		  reloc = BFD_RELOC_PPC64_TPREL16_DS;
 		  break;
+
 		case BFD_RELOC_PPC_TPREL16_LO:
 		  reloc = BFD_RELOC_PPC64_TPREL16_LO_DS;
 		  break;
+
 		case BFD_RELOC_PPC_GOT_DTPREL16:
 		case BFD_RELOC_PPC_GOT_DTPREL16_LO:
 		case BFD_RELOC_PPC_GOT_TPREL16:
 		case BFD_RELOC_PPC_GOT_TPREL16_LO:
 		  break;
+
 		default:
 		  as_bad (_("unsupported relocation for DS offset field"));
 		  break;
@@ -6903,6 +6970,7 @@ ppc_fix_adjustable (fixS *fix)
 	  && fix->fx_r_type != BFD_RELOC_PPC64_GOT16_LO_DS
 	  && fix->fx_r_type != BFD_RELOC_16_GOT_PCREL
 	  && fix->fx_r_type != BFD_RELOC_32_GOTOFF
+	  && fix->fx_r_type != BFD_RELOC_PPC64_GOT_PCREL34
 	  && fix->fx_r_type != BFD_RELOC_24_PLT_PCREL
 	  && fix->fx_r_type != BFD_RELOC_32_PLTOFF
 	  && fix->fx_r_type != BFD_RELOC_32_PLT_PCREL
@@ -6912,6 +6980,7 @@ ppc_fix_adjustable (fixS *fix)
 	  && fix->fx_r_type != BFD_RELOC_64_PLTOFF
 	  && fix->fx_r_type != BFD_RELOC_64_PLT_PCREL
 	  && fix->fx_r_type != BFD_RELOC_PPC64_PLT16_LO_DS
+	  && fix->fx_r_type != BFD_RELOC_PPC64_PLT_PCREL34
 	  && fix->fx_r_type != BFD_RELOC_PPC64_PLTGOT16
 	  && fix->fx_r_type != BFD_RELOC_PPC64_PLTGOT16_LO
 	  && fix->fx_r_type != BFD_RELOC_PPC64_PLTGOT16_HI
@@ -7120,10 +7189,34 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
 	  fixP->fx_r_type = BFD_RELOC_PPC64_REL16_HIGHESTA;
 	  break;
 
+	case BFD_RELOC_PPC64_ADDR16_HIGHER34:
+	  fixP->fx_r_type = BFD_RELOC_PPC64_REL16_HIGHER34;
+	  break;
+
+	case BFD_RELOC_PPC64_ADDR16_HIGHERA34:
+	  fixP->fx_r_type = BFD_RELOC_PPC64_REL16_HIGHERA34;
+	  break;
+
+	case BFD_RELOC_PPC64_ADDR16_HIGHEST34:
+	  fixP->fx_r_type = BFD_RELOC_PPC64_REL16_HIGHEST34;
+	  break;
+
+	case BFD_RELOC_PPC64_ADDR16_HIGHESTA34:
+	  fixP->fx_r_type = BFD_RELOC_PPC64_REL16_HIGHESTA34;
+	  break;
+
 	case BFD_RELOC_PPC_16DX_HA:
 	  fixP->fx_r_type = BFD_RELOC_PPC_REL16DX_HA;
 	  break;
 
+	case BFD_RELOC_PPC64_D34:
+	  fixP->fx_r_type = BFD_RELOC_PPC64_PCREL34;
+	  break;
+
+	case BFD_RELOC_PPC64_D28:
+	  fixP->fx_r_type = BFD_RELOC_PPC64_PCREL28;
+	  break;
+
 	default:
 	  break;
 	}
@@ -7370,6 +7463,8 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
 	case BFD_RELOC_PPC_VLE_SDAREL_HI16D:
 	case BFD_RELOC_PPC_VLE_SDAREL_HA16A:
 	case BFD_RELOC_PPC_VLE_SDAREL_HA16D:
+	case BFD_RELOC_PPC64_GOT_PCREL34:
+	case BFD_RELOC_PPC64_PLT_PCREL34:
 	  gas_assert (fixP->fx_addsy != NULL);
 	  /* Fallthru */
 
@@ -7421,9 +7516,12 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
 #else
 #define APPLY_RELOC 1
 #endif
+      /* We need to call the insert function even when fieldval is
+	 zero if the insert function would translate that zero to a
+	 bit pattern other than all zeros.  */
       if ((fieldval != 0 && APPLY_RELOC) || operand->insert != NULL)
 	{
-	  unsigned long insn;
+	  uint64_t insn;
 	  unsigned char *where;
 
 	  /* Fetch the instruction, insert the fully resolved operand
@@ -7431,34 +7529,56 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
 	  where = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
 	  if (target_big_endian)
 	    {
-	      if (fixP->fx_size == 4)
-		insn = bfd_getb32 (where);
-	      else
+	      if (fixP->fx_size < 4)
 		insn = bfd_getb16 (where);
+	      else
+		{
+		  insn = bfd_getb32 (where);
+		  if (fixP->fx_size > 4)
+		    insn = insn << 32 | bfd_getb32 (where + 4);
+		}
 	    }
 	  else
 	    {
-	      if (fixP->fx_size == 4)
-		insn = bfd_getl32 (where);
-	      else
+	      if (fixP->fx_size < 4)
 		insn = bfd_getl16 (where);
+	      else
+		{
+		  insn = bfd_getl32 (where);
+		  if (fixP->fx_size > 4)
+		    insn = insn << 32 | bfd_getl32 (where + 4);
+		}
 	    }
 	  insn = ppc_insert_operand (insn, operand, fieldval,
 				     fixP->tc_fix_data.ppc_cpu,
 				     fixP->fx_file, fixP->fx_line);
 	  if (target_big_endian)
 	    {
-	      if (fixP->fx_size == 4)
-		bfd_putb32 (insn, where);
-	      else
+	      if (fixP->fx_size < 4)
 		bfd_putb16 (insn, where);
+	      else
+		{
+		  if (fixP->fx_size > 4)
+		    {
+		      bfd_putb32 (insn, where + 4);
+		      insn >>= 32;
+		    }
+		  bfd_putb32 (insn, where);
+		}
 	    }
 	  else
 	    {
-	      if (fixP->fx_size == 4)
-		bfd_putl32 (insn, where);
-	      else
+	      if (fixP->fx_size < 4)
 		bfd_putl16 (insn, where);
+	      else
+		{
+		  if (fixP->fx_size > 4)
+		    {
+		      bfd_putl32 (insn, where + 4);
+		      insn >>= 32;
+		    }
+		  bfd_putl32 (insn, where);
+		}
 	    }
 	}
 
diff --git a/gas/config/tc-ppc.h b/gas/config/tc-ppc.h
index 08e381e293..9de5c08441 100644
--- a/gas/config/tc-ppc.h
+++ b/gas/config/tc-ppc.h
@@ -273,7 +273,13 @@ extern int ppc_force_relocation (struct fix *);
      || (FIX)->fx_r_type == BFD_RELOC_PPC64_HIGHER_S		\
      || (FIX)->fx_r_type == BFD_RELOC_PPC64_HIGHEST		\
      || (FIX)->fx_r_type == BFD_RELOC_PPC64_HIGHEST_S		\
-     || (FIX)->fx_r_type == BFD_RELOC_PPC_16DX_HA))
+     || (FIX)->fx_r_type == BFD_RELOC_PPC64_ADDR16_HIGHER34	\
+     || (FIX)->fx_r_type == BFD_RELOC_PPC64_ADDR16_HIGHERA34	\
+     || (FIX)->fx_r_type == BFD_RELOC_PPC64_ADDR16_HIGHEST34	\
+     || (FIX)->fx_r_type == BFD_RELOC_PPC64_ADDR16_HIGHESTA34	\
+     || (FIX)->fx_r_type == BFD_RELOC_PPC_16DX_HA		\
+     || (FIX)->fx_r_type == BFD_RELOC_PPC64_D34			\
+     || (FIX)->fx_r_type == BFD_RELOC_PPC64_D28))
 #endif
 
 #define TC_VALIDATE_FIX_SUB(FIX, SEG) 0
diff --git a/gas/testsuite/gas/ppc/ppc.exp b/gas/testsuite/gas/ppc/ppc.exp
index aa199d5e85..1660d530f0 100644
--- a/gas/testsuite/gas/ppc/ppc.exp
+++ b/gas/testsuite/gas/ppc/ppc.exp
@@ -116,3 +116,4 @@ run_dump_test "htm"
 run_dump_test "titan"
 run_dump_test "prefix-align"
 run_dump_test "prefix-pcrel"
+run_dump_test "prefix-reloc"
diff --git a/gas/testsuite/gas/ppc/prefix-reloc.d b/gas/testsuite/gas/ppc/prefix-reloc.d
new file mode 100644
index 0000000000..9f554ac388
--- /dev/null
+++ b/gas/testsuite/gas/ppc/prefix-reloc.d
@@ -0,0 +1,35 @@
+#as: -a64 -mfuture
+#objdump: -dr -Mfuture
+#name: Prefix insn relocations
+
+.*
+
+Disassembly of section \.text:
+
+0+ <\.text>:
+   0:	(00 00 00 06|06 00 00 00) 	pli     r9,0
+   4:	(00 00 20 39|39 20 00 00) 
+			0: R_PPC64_D34_HA30	ext
+   8:	(46 17 29 79|79 29 17 46) 	rldicr  r9,r9,34,29
+   c:	(00 00 00 06|06 00 00 00) 	paddi   r9,r9,0
+  10:	(00 00 29 39|39 29 00 00) 
+			c: R_PPC64_D34_LO	ext
+  14:	(00 00 10 04|04 10 00 00) 	pld     r3,0
+  18:	(00 00 60 e4|e4 60 00 00) 
+			14: R_PPC64_PCREL34	ext
+  1c:	(00 00 10 04|04 10 00 00) 	pld     r4,0
+  20:	(00 00 80 e4|e4 80 00 00) 
+			1c: R_PPC64_GOT_PCREL34	ext
+  24:	(00 00 10 04|04 10 00 00) 	pld     r5,0
+  28:	(00 00 a0 e4|e4 a0 00 00) 
+			24: R_PPC64_PLT_PCREL34	ext
+  2c:	(00 00 10 04|04 10 00 00) 	pld     r6,0
+  30:	(00 00 c0 e4|e4 c0 00 00) 
+			2c: R_PPC64_PCREL34	ext
+  34:	(00 00 00 04|04 00 00 00) 	pld     r7,0\(0\)
+  38:	(00 00 e0 e4|e4 e0 00 00) 
+			34: R_PPC64_D34	ext
+  3c:	(00 00 00 60|60 00 00 00) 	nop
+  40:	(00 00 10 04|04 10 00 00) 	pld     r8,0
+  44:	(00 00 00 e5|e5 00 00 00) 
+			40: R_PPC64_PCREL34	ext
diff --git a/gas/testsuite/gas/ppc/prefix-reloc.s b/gas/testsuite/gas/ppc/prefix-reloc.s
new file mode 100644
index 0000000000..a2f23075db
--- /dev/null
+++ b/gas/testsuite/gas/ppc/prefix-reloc.s
@@ -0,0 +1,13 @@
+ .text
+ pli 9,ext@ha
+ sldi 9,9,34
+ paddi 9,9,ext@l
+ pld 3,ext@pcrel
+ pld 4,ext@got@pcrel
+ pld 5,ext@plt@pcrel
+0: pld 6,ext-0b(0),1
+ pld 7,ext(0),0
+# The following insn will need an alignment nop, testing the behaviour
+# of "dot" in the expression.  Don't stupidly edit this file and lose
+# the nop.
+ pld 8,ext-.(0),1
diff --git a/include/elf/ppc64.h b/include/elf/ppc64.h
index 6dd29cdf8a..e90c7fd2ef 100644
--- a/include/elf/ppc64.h
+++ b/include/elf/ppc64.h
@@ -158,6 +158,30 @@ START_RELOC_NUMBERS (elf_ppc64_reloc_type)
   RELOC_NUMBER (R_PPC64_PLTSEQ,		   119)
   RELOC_NUMBER (R_PPC64_PLTCALL,	   120)
 
+/* Powerxx support.  */
+  RELOC_NUMBER (R_PPC64_PLTSEQ_NOTOC,	   121)
+  RELOC_NUMBER (R_PPC64_PLTCALL_NOTOC,	   122)
+  RELOC_NUMBER (R_PPC64_PCREL_OPT,	   123)
+
+  RELOC_NUMBER (R_PPC64_D34,		   128)
+  RELOC_NUMBER (R_PPC64_D34_LO,		   129)
+  RELOC_NUMBER (R_PPC64_D34_HI30,	   130)
+  RELOC_NUMBER (R_PPC64_D34_HA30,	   131)
+  RELOC_NUMBER (R_PPC64_PCREL34,	   132)
+  RELOC_NUMBER (R_PPC64_GOT_PCREL34,	   133)
+  RELOC_NUMBER (R_PPC64_PLT_PCREL34,	   134)
+  RELOC_NUMBER (R_PPC64_PLT_PCREL34_NOTOC, 135)
+  RELOC_NUMBER (R_PPC64_ADDR16_HIGHER34,   136)
+  RELOC_NUMBER (R_PPC64_ADDR16_HIGHERA34,  137)
+  RELOC_NUMBER (R_PPC64_ADDR16_HIGHEST34,  138)
+  RELOC_NUMBER (R_PPC64_ADDR16_HIGHESTA34, 139)
+  RELOC_NUMBER (R_PPC64_REL16_HIGHER34,    140)
+  RELOC_NUMBER (R_PPC64_REL16_HIGHERA34,   141)
+  RELOC_NUMBER (R_PPC64_REL16_HIGHEST34,   142)
+  RELOC_NUMBER (R_PPC64_REL16_HIGHESTA34,  143)
+  RELOC_NUMBER (R_PPC64_D28,		   144)
+  RELOC_NUMBER (R_PPC64_PCREL28,	   145)
+
 #ifndef RELOC_MACROS_GEN_FUNC
 /* Relocation only used internally by gas or ld.  If you need to use
    these reloc numbers, you can change them to some other unused value

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 4/5] PowerPC GOT_PCREL34 optimisation
  2019-05-24  1:31 [PATCH 0/5] PowerPC -mfuture support Alan Modra
                   ` (2 preceding siblings ...)
  2019-05-24  1:35 ` [PATCH 3/5] PowerPC relocations for prefix insns Alan Modra
@ 2019-05-24  1:36 ` Alan Modra
  2019-05-24  1:37 ` [PATCH 5/5] PowerPC notoc linkage stubs Alan Modra
  4 siblings, 0 replies; 13+ messages in thread
From: Alan Modra @ 2019-05-24  1:36 UTC (permalink / raw)
  To: binutils

bfd/
	* elf64-ppc.c (ppc64_elf_check_relocs): Set has_gotrel for
	R_PPC64_GOT_PCREL34.
	(xlate_pcrel_opt): New function.
	(ppc64_elf_edit_toc): Handle R_PPC64_GOT_PCREL34.
	(ppc64_elf_relocate_section): Edit GOT indirect to GOT relative
	for R_PPC64_GOT_PCREL34.  Implement R_PPC64_PCREL_OPT optimisation.
ld/
	* testsuite/ld-powerpc/pcrelopt.s,
	* testsuite/ld-powerpc/pcrelopt.d,
	* testsuite/ld-powerpc/pcrelopt.sec: New test.
	* testsuite/ld-powerpc/powerpc.exp: Run it.

diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index ad47bed372..75189b6967 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -4567,6 +4567,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	case R_PPC64_GOT16_DS:
 	case R_PPC64_GOT16_HA:
 	case R_PPC64_GOT16_LO_DS:
+	case R_PPC64_GOT_PCREL34:
 	  ppc64_elf_tdata (abfd)->has_gotrel = 1;
 	  ppc64_elf_section_data (sec)->has_gotrel = 1;
 	  /* Fall through.  */
@@ -4574,7 +4575,6 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	case R_PPC64_GOT16:
 	case R_PPC64_GOT16_HI:
 	case R_PPC64_GOT16_LO:
-	case R_PPC64_GOT_PCREL34:
 	dogot:
 	  /* This symbol requires a global offset table entry.  */
 	  sec->has_toc_reloc = 1;
@@ -8141,6 +8141,114 @@ ok_lo_toc_insn (unsigned int insn, enum elf_ppc64_reloc_type r_type)
 	      && (insn & 1) == 0));
 }
 
+/* PCREL_OPT in one instance flags to the linker that a pair of insns:
+     pld ra,symbol@got@pcrel
+     load/store rt,0(ra)
+   or
+     paddi ra,symbol@pcrel
+     load/store rt,0(ra)
+   may be translated to
+     pload/pstore rt,symbol@pcrel
+     nop.
+   This function returns true if the optimization is possible, placing
+   the prefix insn in *PINSN1 and a NOP in *PINSN2.
+
+   On entry to this function, the linker has already determined that
+   the pld can be replaced with paddi: *PINSN1 is that paddi insn,
+   while *PINSN2 is the second instruction.  */
+
+static bfd_boolean
+xlate_pcrel_opt (uint64_t *pinsn1, uint64_t *pinsn2)
+{
+  uint32_t insn2 = *pinsn2 >> 32;
+  uint64_t i1new;
+
+  /* Check that regs match.  */
+  if (((insn2 >> 16) & 31) != ((*pinsn1 >> 21) & 31))
+    return FALSE;
+
+  switch ((insn2 >> 26) & 63)
+    {
+    default:
+      return FALSE;
+
+    case 32: /* lwz */
+    case 34: /* lbz */
+    case 36: /* stw */
+    case 38: /* stb */
+    case 40: /* lhz */
+    case 42: /* lha */
+    case 44: /* sth */
+    case 48: /* lfs */
+    case 50: /* lfd */
+    case 52: /* stfs */
+    case 54: /* stfd */
+      /* These are the PMLS cases, where we just need to tack a prefix
+	 on the insn.  Check that the D field is zero.  */
+      if ((insn2 & 0xffff) != 0)
+	return FALSE;
+      i1new = ((1ULL << 58) | (2ULL << 56) | (1ULL << 52)
+	       | (insn2 & ((63ULL << 26) | (31ULL << 21))));
+      break;
+
+    case 58: /* lwa, ld */
+      if ((insn2 & 0xfffd) != 0)
+	return FALSE;
+      i1new = ((1ULL << 58) | (1ULL << 52)
+	       | (insn2 & 2 ? 41ULL << 26 : 57ULL << 26)
+	       | (insn2 & (31ULL << 21)));
+      break;
+
+    case 57: /* lxsd, lxssp */
+      if ((insn2 & 0xfffc) != 0 || (insn2 & 3) < 2)
+	return FALSE;
+      i1new = ((1ULL << 58) | (1ULL << 52)
+	       | ((40ULL | (insn2 & 3)) << 26)
+	       | (insn2 & (31ULL << 21)));
+      break;
+
+    case 61: /* stxsd, stxssp, lxv, stxv  */
+      if ((insn2 & 3) == 0)
+	return FALSE;
+      else if ((insn2 & 3) >= 2)
+	{
+	  if ((insn2 & 0xfffc) != 0)
+	    return FALSE;
+	  i1new = ((1ULL << 58) | (1ULL << 52)
+		   | ((44ULL | (insn2 & 3)) << 26)
+		   | (insn2 & (31ULL << 21)));
+	}
+      else
+	{
+	  if ((insn2 & 0xfff0) != 0)
+	    return FALSE;
+	  i1new = ((1ULL << 58) | (1ULL << 52)
+		   | ((50ULL | (insn2 & 4) | ((insn2 & 8) >> 3)) << 26)
+		   | (insn2 & (31ULL << 21)));
+	}
+      break;
+
+    case 56: /* lq */
+      if ((insn2 & 0xffff) != 0)
+	return FALSE;
+      i1new = ((1ULL << 58) | (1ULL << 52)
+	       | (insn2 & ((63ULL << 26) | (31ULL << 21))));
+      break;
+
+    case 62: /* std, stq */
+      if ((insn2 & 0xfffd) != 0)
+	return FALSE;
+      i1new = ((1ULL << 58) | (1ULL << 52)
+	       | ((insn2 & 2) == 0 ? 61ULL << 26 : 60ULL << 26)
+	       | (insn2 & (31ULL << 21)));
+      break;
+    }
+
+  *pinsn1 = i1new;
+  *pinsn2 = (uint64_t) NOP << 32;
+  return TRUE;
+}
+
 /* Examine all relocs referencing .toc sections in order to remove
    unused .toc entries.  */
 
@@ -8797,8 +8905,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
     }
 
   /* Look for cases where we can change an indirect GOT access to
-     a GOT relative access, possibly reducing the number of GOT
-     entries.  */
+     a GOT relative or PC relative access, possibly reducing the
+     number of GOT entries.  */
   for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
     {
       asection *sec;
@@ -8849,8 +8957,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
 	      asection *sym_sec;
 	      struct elf_link_hash_entry *h;
 	      struct got_entry *ent;
-	      bfd_vma val;
-	      unsigned char buf[4];
+	      bfd_vma sym_addend, val, pc;
+	      unsigned char buf[8];
 	      unsigned int insn;
 
 	      r_type = ELF64_R_TYPE (rel->r_info);
@@ -8862,6 +8970,11 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
 		case R_PPC64_GOT16_DS:
 		case R_PPC64_GOT16_HA:
 		case R_PPC64_GOT16_LO_DS:
+		  sym_addend = rel->r_addend;
+		  break;
+
+		case R_PPC64_GOT_PCREL34:
+		  sym_addend = 0;
 		  break;
 		}
 
@@ -8877,7 +8990,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
 		val = h->root.u.def.value;
 	      else
 		val = sym->st_value;
-	      val += rel->r_addend;
+	      val += sym_addend;
 	      val += sym_sec->output_section->vma + sym_sec->output_offset;
 
 	      switch (r_type)
@@ -8919,6 +9032,22 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
 		  if ((insn & (0x3f << 26 | 0x3)) != 58u << 26 /* ld */)
 		    continue;
 		  break;
+
+		case R_PPC64_GOT_PCREL34:
+		  pc = rel->r_offset;
+		  pc += sec->output_section->vma + sec->output_offset;
+		  if (val - pc + (1ULL << 33) >= 1ULL << 34)
+		    continue;
+		  if (!bfd_get_section_contents (ibfd, sec, buf,
+						 rel->r_offset & ~3, 8))
+		    goto got_error_ret;
+		  insn = bfd_get_32 (ibfd, buf);
+		  if ((insn & (-1u << 18)) != ((1u << 26) | (1u << 20)))
+		    continue;
+		  insn = bfd_get_32 (ibfd, buf + 4);
+		  if ((insn & (0x3f << 26)) != 57u << 26)
+		    continue;
+		  break;
 		}
 
 	      if (h != NULL)
@@ -8929,7 +9058,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
 		  ent = local_got_ents[r_symndx];
 		}
 	      for (; ent != NULL; ent = ent->next)
-		if (ent->addend == rel->r_addend
+		if (ent->addend == sym_addend
 		    && ent->owner == ibfd
 		    && ent->tls_type == 0)
 		  break;
@@ -13772,6 +13901,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
       Elf_Internal_Rela orig_rel;
       reloc_howto_type *howto;
       struct reloc_howto_struct alt_howto;
+      uint64_t pinsn;
+      bfd_vma offset;
 
     again:
       orig_rel = *rel;
@@ -14134,7 +14265,6 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	  if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0)
 	    {
 	      unsigned int insn1, insn2;
-	      bfd_vma offset;
 
 	    tls_ldgd_opt:
 	      offset = (bfd_vma) -1;
@@ -14236,9 +14366,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	      && rel + 1 < relend)
 	    {
 	      unsigned int insn2;
-	      bfd_vma offset = rel->r_offset;
 	      enum elf_ppc64_reloc_type r_type1 = ELF64_R_TYPE (rel[1].r_info);
 
+	      offset = rel->r_offset;
 	      if (is_plt_seq_reloc (r_type1))
 		{
 		  bfd_put_32 (output_bfd, NOP, contents + offset);
@@ -14285,9 +14415,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	      && rel + 1 < relend)
 	    {
 	      unsigned int insn2;
-	      bfd_vma offset = rel->r_offset;
 	      enum elf_ppc64_reloc_type r_type1 = ELF64_R_TYPE (rel[1].r_info);
 
+	      offset = rel->r_offset;
 	      if (is_plt_seq_reloc (r_type1))
 		{
 		  bfd_put_32 (output_bfd, NOP, contents + offset);
@@ -14431,7 +14561,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	      && relocation + 0x80008000 <= 0xffffffff)
 	    {
 	      unsigned int insn1, insn2;
-	      bfd_vma offset = rel->r_offset - d_offset;
+	      offset = rel->r_offset - d_offset;
 	      insn1 = bfd_get_32 (input_bfd, contents + offset);
 	      insn2 = bfd_get_32 (input_bfd, contents + offset + 4);
 	      if ((insn1 & 0xffff0000) == ADDIS_R2_R12
@@ -14823,6 +14953,74 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 		}
 	    }
 	  break;
+
+	case R_PPC64_GOT_PCREL34:
+	  from = (rel->r_offset
+		  + input_section->output_section->vma
+		  + input_section->output_offset);
+	  if (relocation - from + (1ULL << 33) < 1ULL << 34
+	      && SYMBOL_REFERENCES_LOCAL (info, &h->elf))
+	    {
+	      offset = rel->r_offset;
+	      pinsn = bfd_get_32 (input_bfd, contents + offset);
+	      pinsn <<= 32;
+	      pinsn |= bfd_get_32 (input_bfd, contents + offset + 4);
+	      if ((pinsn & ((-1ULL << 50) | (63ULL << 26)))
+		   == ((1ULL << 58) | (1ULL << 52) | (57ULL << 26) /* pld */))
+		{
+		  /* Replace with paddi.  */
+		  pinsn += (2ULL << 56) + (14ULL << 26) - (57ULL << 26);
+		  r_type = R_PPC64_PCREL34;
+		  rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+		  bfd_put_32 (input_bfd, pinsn >> 32, contents + offset);
+		  bfd_put_32 (input_bfd, pinsn, contents + offset + 4);
+		  goto pcrelopt;
+		}
+	    }
+	  break;
+
+	case R_PPC64_PCREL34:
+	  if (SYMBOL_REFERENCES_LOCAL (info, &h->elf))
+	    {
+	      offset = rel->r_offset;
+	      pinsn = bfd_get_32 (input_bfd, contents + offset);
+	      pinsn <<= 32;
+	      pinsn |= bfd_get_32 (input_bfd, contents + offset + 4);
+	      if ((pinsn & ((-1ULL << 50) | (63ULL << 26)))
+		   == ((1ULL << 58) | (2ULL << 56) | (1ULL << 52)
+		       | (14ULL << 26) /* paddi */))
+		{
+		pcrelopt:
+		  if (rel + 1 < relend
+		      && rel[1].r_offset == offset
+		      && rel[1].r_info == ELF64_R_INFO (0, R_PPC64_PCREL_OPT))
+		    {
+		      bfd_vma off2 = rel[1].r_addend;
+		      if (off2 == 0)
+			/* zero means next insn.  */
+			off2 = 8;
+		      off2 += offset;
+		      if (off2 + 4 <= input_section->size)
+			{
+			  uint64_t pinsn2;
+			  pinsn2 = bfd_get_32 (input_bfd, contents + off2);
+			  pinsn2 <<= 32;
+			  if ((pinsn2 & (63ULL << 58)) == 1ULL << 58)
+			    break;
+			  if (xlate_pcrel_opt (&pinsn, &pinsn2))
+			    {
+			      bfd_put_32 (input_bfd, pinsn >> 32,
+					  contents + offset);
+			      bfd_put_32 (input_bfd, pinsn,
+					  contents + offset + 4);
+			      bfd_put_32 (input_bfd, pinsn2 >> 32,
+					  contents + off2);
+			    }
+			}
+		    }
+		}
+	    }
+	  break;
 	}
 
       /* Set `addend'.  */
@@ -14847,6 +15045,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	case R_PPC64_GNU_VTINHERIT:
 	case R_PPC64_GNU_VTENTRY:
 	case R_PPC64_ENTRY:
+	case R_PPC64_PCREL_OPT:
 	  goto copy_reloc;
 
 	  /* GOT16 relocations.  Like an ADDR16 using the symbol's
@@ -15882,8 +16081,6 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	    r = bfd_reloc_outofrange;
 	  else
 	    {
-	      uint64_t pinsn;
-
 	      relocation += addend;
 	      if (howto->pc_relative)
 		relocation -= (rel->r_offset
diff --git a/ld/testsuite/ld-powerpc/pcrelopt.d b/ld/testsuite/ld-powerpc/pcrelopt.d
new file mode 100644
index 0000000000..18fdb95abf
--- /dev/null
+++ b/ld/testsuite/ld-powerpc/pcrelopt.d
@@ -0,0 +1,89 @@
+
+.*:     file format .*
+
+Disassembly of section \.text:
+
+0+10000200 <_start>:
+.*:	(06 10 00 01|01 00 10 06) 	plbz    r3,66320
+.*:	(88 60 03 10|10 03 60 88) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(06 10 00 01|01 00 10 06) 	plhz    r4,66308
+.*:	(a0 80 03 04|04 03 80 a0) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(06 10 00 01|01 00 10 06) 	plha    r3,66288
+.*:	(a8 60 02 f0|f0 02 60 a8) 
+.*:	(40 82 ff f4|f4 ff 82 40) 	bne     .*
+.*:	(06 10 00 01|01 00 10 06) 	plwz    r3,66276
+.*:	(80 60 02 e4|e4 02 60 80) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(04 10 00 01|01 00 10 04) 	plwa    r3,66264
+.*:	(a4 60 02 d8|d8 02 60 a4) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(04 10 00 01|01 00 10 04) 	pld     r3,66252
+.*:	(e4 60 02 cc|cc 02 60 e4) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(04 10 00 01|01 00 10 04) 	plq     r14,66240
+.*:	(e1 c0 02 c0|c0 02 c0 e1) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(06 10 00 01|01 00 10 06) 	plfs    f1,66228
+.*:	(c0 20 02 b4|b4 02 20 c0) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(06 10 00 01|01 00 10 06) 	plfd    f1,66216
+.*:	(c8 20 02 a8|a8 02 20 c8) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(04 10 00 01|01 00 10 04) 	plxsd   v30,66204
+.*:	(ab c0 02 9c|9c 02 c0 ab) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(04 10 00 01|01 00 10 04) 	plxssp  v31,66192
+.*:	(af e0 02 90|90 02 e0 af) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(04 10 00 01|01 00 10 04) 	plxv    vs63,66180
+.*:	(cf e0 02 84|84 02 e0 cf) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(04 10 00 01|01 00 10 04) 	plxv    vs0,66168
+.*:	(c8 00 02 78|78 02 00 c8) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(06 10 00 01|01 00 10 06) 	pstb    r3,66156
+.*:	(98 60 02 6c|6c 02 60 98) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(06 10 00 01|01 00 10 06) 	psth    r3,66144
+.*:	(b0 60 02 60|60 02 60 b0) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(06 10 00 01|01 00 10 06) 	pstw    r3,66128
+.*:	(90 60 02 50|50 02 60 90) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(04 10 00 01|01 00 10 04) 	pstd    r3,66116
+.*:	(f4 60 02 44|44 02 60 f4) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(04 10 00 01|01 00 10 04) 	pstq    r14,66104
+.*:	(f1 c0 02 38|38 02 c0 f1) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(06 10 00 01|01 00 10 06) 	pstfd   f1,66092
+.*:	(d8 20 02 2c|2c 02 20 d8) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(06 10 00 01|01 00 10 06) 	pstfs   f2,66080
+.*:	(d0 40 02 20|20 02 40 d0) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(04 10 00 01|01 00 10 04) 	pstxsd  v30,66064
+.*:	(bb c0 02 10|10 02 c0 bb) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(04 10 00 01|01 00 10 04) 	pstxssp v31,66052
+.*:	(bf e0 02 04|04 02 e0 bf) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(04 10 00 01|01 00 10 04) 	pstxv   vs63,66040
+.*:	(df e0 01 f8|f8 01 e0 df) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(04 10 00 01|01 00 10 04) 	pstxv   vs0,66028
+.*:	(d8 00 01 ec|ec 01 00 d8) 
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(04 10 00 01|01 00 10 04) 	pld     r9,66008
+.*:	(e5 20 01 d8|d8 01 20 e5) 
+.*:	(e8 09 00 00|00 00 09 e8) 	ld      r0,0\(r9\)
+.*:	(60 00 00 00|00 00 00 60) 	nop
+.*:	(06 10 00 01|01 00 10 06) 	pla     r7,66000
+.*:	(38 e0 01 d0|d0 01 e0 38) 
+.*:	(88 c7 00 00|00 00 c7 88) 	lbz     r6,0\(r7\)
diff --git a/ld/testsuite/ld-powerpc/pcrelopt.s b/ld/testsuite/ld-powerpc/pcrelopt.s
new file mode 100644
index 0000000000..78b0f51f73
--- /dev/null
+++ b/ld/testsuite/ld-powerpc/pcrelopt.s
@@ -0,0 +1,119 @@
+	.text
+	.globl _start
+_start:
+# original PCREL_OPT definition, with second insn immediately after first
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0
+	lbz 3,0(9)
+
+# but we now allow an offset to the second insn
+	pld 22,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+	nop
+0:	lhz 4,0(22)
+
+# in fact, it can even be before the "first" insn
+0: 	lha 3,0(9)
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0b-(.-8)
+	bne 0b
+
+# and of course, other local labels work
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,.L1-(.-8)
+.L1:	lwz 3,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	lwa 3,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	ld 3,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	lq 14,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	lfs 1,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	lfd 1,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	lxsd 30,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	lxssp 31,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	lxv 63,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	lxv 0,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	stb 3,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	sth 3,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	stw 3,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	std 3,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	stq 14,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	stfd 1,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	stfs 2,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	stxsd 30,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	stxssp 31,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	stxv 63,0(9)
+
+	pld 9,sym@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	stxv 0,0(9)
+
+# This should not optimize
+	.extern i
+	.type i,@object
+	pld 9,i@got@pcrel
+		.reloc .-8,R_PPC64_PCREL_OPT,0f-(.-8)
+0:	ld 0,0(9)
+
+# and this should edit from GOT indirect to GOT relative
+# ie. change the pld to paddi, leaving the lbz as is.
+	pld 7,sym@got@pcrel
+	lbz 6,0(7)
+
+	.data
+sym:	.space 32
diff --git a/ld/testsuite/ld-powerpc/pcrelopt.sec b/ld/testsuite/ld-powerpc/pcrelopt.sec
new file mode 100644
index 0000000000..1dcd3a7362
--- /dev/null
+++ b/ld/testsuite/ld-powerpc/pcrelopt.sec
@@ -0,0 +1,6 @@
+# check for just one GOT entry
+#...
+.* \.rela\.dyn +RELA +[0-9a-f]+ [0-9a-f]+ 000018 .*
+#...
+.* \.got +PROGBITS +[0-9a-f]+ [0-9a-f]+ 000010 .*
+#pass
diff --git a/ld/testsuite/ld-powerpc/powerpc.exp b/ld/testsuite/ld-powerpc/powerpc.exp
index a2dcfdce89..658f319739 100644
--- a/ld/testsuite/ld-powerpc/powerpc.exp
+++ b/ld/testsuite/ld-powerpc/powerpc.exp
@@ -260,6 +260,10 @@ set ppc64elftests {
     {"notoc ext" "" "" "-a64" {ext.s} {} ""}
     {"notoc" "-melf64ppc --no-plt-localentry -T ext.lnk" "" "-a64" {notoc.s}
 	{{objdump -d notoc.d} {readelf {-wf -W} notoc.wf}} "notoc"}
+    {"pcrelopt" "-melf64ppc --hash-style=gnu" "tmpdir/symtocbase.so"
+	"-a64 -mfuture" {pcrelopt.s}
+	{{objdump {-d -Mfuture} pcrelopt.d}
+	 {readelf {-S --wide} pcrelopt.sec}} "pcrelopt" }
 }
 
 set ppceabitests {

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 5/5] PowerPC notoc linkage stubs
  2019-05-24  1:31 [PATCH 0/5] PowerPC -mfuture support Alan Modra
                   ` (3 preceding siblings ...)
  2019-05-24  1:36 ` [PATCH 4/5] PowerPC GOT_PCREL34 optimisation Alan Modra
@ 2019-05-24  1:37 ` Alan Modra
  4 siblings, 0 replies; 13+ messages in thread
From: Alan Modra @ 2019-05-24  1:37 UTC (permalink / raw)
  To: binutils

Use pcrel addressing instructions in linkage stubs.

bfd/
	* elf64-ppc.c: Comment on powerxx _notoc stub variants.
	(LI_R11_0, LIS_R11, ORI_R11_R11_0, SLDI_R11_R11_34): Define.
	(PADDI_R12_PC, PLD_R12_PC, D34, HA34): Define.
	(struct ppc_link_hash_table): Add powerxx_stubs.
	(ppc64_elf_check_relocs): Set powerxx_stubs.
	(build_powerxx_offset, size_powerxx_offset),
	(num_relocs_for_powerxx_offset),
	(emit_relocs_for_powerxx_offset): New functions.
	(plt_stub_size): Size powerxx stubs.
	(ppc_build_one_stub): Emit powerxx stubs.
	(ppc_size_one_stub): Size powerxx stubs.  Omit .eh_frame for
	powerxx stubs.
ld/
	* testsuite/ld-powerpc/notoc2.d,
	* testsuite/ld-powerpc/notoc2.s: New test.
	* testsuite/ld-powerpc/powerpc.exp: Run it.

diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index 75189b6967..f795e3f90b 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -187,18 +187,24 @@ static bfd_vma opd_entry_value
 #define LD_R2_0R12	0xe84c0000	/* ld	 %r2,0(%r12)	 */
 #define ADD_R2_R2_R12	0x7c426214	/* add	 %r2,%r2,%r12	 */
 
+#define LI_R11_0	0x39600000	/* li    %r11,0		*/
 #define LIS_R2		0x3c400000	/* lis %r2,xxx@ha	  */
+#define LIS_R11		0x3d600000	/* lis %r11,xxx@ha	  */
 #define LIS_R12		0x3d800000	/* lis %r12,xxx@ha	  */
 #define ADDIS_R2_R12	0x3c4c0000	/* addis %r2,%r12,xxx@ha  */
 #define ADDIS_R12_R2	0x3d820000	/* addis %r12,%r2,xxx@ha  */
 #define ADDIS_R12_R11	0x3d8b0000	/* addis %r12,%r11,xxx@ha */
 #define ADDIS_R12_R12	0x3d8c0000	/* addis %r12,%r12,xxx@ha */
 #define ORIS_R12_R12_0	0x658c0000	/* oris  %r12,%r12,xxx@hi */
+#define ORI_R11_R11_0	0x616b0000	/* ori   %r11,%r11,xxx@l  */
 #define ORI_R12_R12_0	0x618c0000	/* ori   %r12,%r12,xxx@l  */
 #define LD_R12_0R12	0xe98c0000	/* ld	 %r12,xxx@l(%r12) */
+#define SLDI_R11_R11_34	0x796b1746	/* sldi  %r11,%r11,34     */
 #define SLDI_R12_R12_32	0x799c07c6	/* sldi  %r12,%r12,32     */
 #define LDX_R12_R11_R12 0x7d8b602a	/* ldx   %r12,%r11,%r12   */
 #define ADD_R12_R11_R12 0x7d8b6214	/* add   %r12,%r11,%r12   */
+#define PADDI_R12_PC	0x0610000039800000ULL
+#define PLD_R12_PC	0x04100000e5800000ULL
 #define PNOP		0x0700000000000000ULL
 
 /* __glink_PLTresolve stub instructions.  We enter with the index in R0.  */
@@ -2841,8 +2847,34 @@ must_be_dyn_reloc (struct bfd_link_info *info,
    .	mtctr	%r12
    .	bctr
 
+   There are also ELFv1 powerxx variants of these stubs.
+   ppc_stub_long_branch_notoc:
+   .	paddi	%r12,dest@pcrel
+   .	b	dest
+   ppc_stub_plt_branch_notoc:
+   .	lis	%r11,(dest-1f)@highesta34
+   .	ori	%r11,%r11,(dest-1f)@highera34
+   .	sldi	%r11,%r11,34
+   . 1: paddi	%r12,dest@pcrel
+   .	add	%r12,%r11,%r12
+   .	mtctr	%r12
+   .	bctr
+   ppc_stub_plt_call_notoc:
+   .	lis	%r11,(xxx-1f)@highesta34
+   .	ori	%r11,%r11,(xxx-1f)@highera34
+   .	sldi	%r11,%r11,34
+   . 1: paddi	%r12,xxx@pcrel
+   .	ldx	%r12,%r11,%r12
+   .	mtctr	%r12
+   .	bctr
+
    In cases where the high instructions would add zero, they are
    omitted and following instructions modified in some cases.
+   For example, a powerxx ppc_stub_plt_call_notoc might simplify down
+   to
+   .	pld	%r12,xxx@pcrel
+   .	mtctr	%r12
+   .	bctr
 
    For a given stub group (a set of sections all using the same toc
    pointer value) there will be just one stub type used for any
@@ -3132,6 +3164,9 @@ struct ppc_link_hash_table
   /* Whether calls are made via the PLT from NOTOC functions.  */
   unsigned int notoc_plt:1;
 
+  /* Whether to use powerxx instructions in linkage stubs.  */
+  unsigned int powerxx_stubs:1;
+
   /* Incremented every time we size stubs.  */
   unsigned int stub_iteration;
 
@@ -4476,6 +4511,13 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       r_type = ELF64_R_TYPE (rel->r_info);
       switch (r_type)
 	{
+	case R_PPC64_D34:
+	case R_PPC64_D34_LO:
+	case R_PPC64_D34_HI30:
+	case R_PPC64_D34_HA30:
+	case R_PPC64_D28:
+	  htab->powerxx_stubs = 1;
+	  /* Fall through.  */
 	default:
 	  /* Somewhat foolishly, because the ABIs don't specifically
 	     allow it, ppc64 gas and ld support GOT and PLT relocs
@@ -4485,9 +4527,13 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	     used to specify the pcrel offset.  */
 	  sym_addend = rel->r_addend;
 	  break;
+
+	case R_PPC64_PCREL34:
 	case R_PPC64_GOT_PCREL34:
 	case R_PPC64_PLT_PCREL34:
 	case R_PPC64_PLT_PCREL34_NOTOC:
+	case R_PPC64_PCREL28:
+	  htab->powerxx_stubs = 1;
 	  sym_addend = 0;
 	  break;
 	}
@@ -9427,6 +9473,9 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
 #define PPC_LO(v) ((v) & 0xffff)
 #define PPC_HI(v) (((v) >> 16) & 0xffff)
 #define PPC_HA(v) PPC_HI ((v) + 0x8000)
+#define D34(v) \
+  ((((v) & 0x3ffff0000ULL) << 16) | (v & 0xffff))
+#define HA34(v) ((v + (1ULL << 33)) >> 34)
 
 /* Called via elf_link_hash_traverse from ppc64_elf_size_dynamic_sections
    to set up space for global entry stubs.  These are put in glink,
@@ -10208,6 +10257,146 @@ emit_relocs_for_offset (struct bfd_link_info *info, Elf_Internal_Rela *r,
   return r;
 }
 
+static bfd_byte *
+build_powerxx_offset (bfd *abfd, bfd_byte *p, bfd_vma off, int odd,
+		      bfd_boolean load)
+{
+  uint64_t insn;
+  if (off - odd + (1ULL << 33) < 1ULL << 34)
+    {
+      off -= odd;
+      if (odd)
+	{
+	  bfd_put_32 (abfd, NOP, p);
+	  p += 4;
+	}
+      if (load)
+	insn = PLD_R12_PC;
+      else
+	insn = PADDI_R12_PC;
+      insn |= D34 (off);
+      bfd_put_32 (abfd, insn >> 32, p);
+      p += 4;
+      bfd_put_32 (abfd, insn, p);
+    }
+  /* The minimum value for paddi is -0x200000000.  The minimum value
+     for li is -0x8000, which when shifted by 34 and added gives a
+     minimum value of -0x2000200000000.  The maximum value is
+     0x1ffffffff+0x7fff<<34 which is 0x2000200000000-1.  */
+  else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+    {
+      off -= 8 - odd;
+      bfd_put_32 (abfd, LI_R11_0 | (HA34 (off) & 0xffff), p);
+      p += 4;
+      if (!odd)
+	{
+	  bfd_put_32 (abfd, SLDI_R11_R11_34, p);
+	  p += 4;
+	}
+      insn = PADDI_R12_PC | D34 (off);
+      bfd_put_32 (abfd, insn >> 32, p);
+      p += 4;
+      bfd_put_32 (abfd, insn, p);
+      p += 4;
+      if (odd)
+	{
+	  bfd_put_32 (abfd, SLDI_R11_R11_34, p);
+	  p += 4;
+	}
+      if (load)
+	bfd_put_32 (abfd, LDX_R12_R11_R12, p);
+      else
+	bfd_put_32 (abfd, ADD_R12_R11_R12, p);
+    }
+  else
+    {
+      off -= odd + 8;
+      bfd_put_32 (abfd, LIS_R11 | ((HA34 (off) >> 16) & 0x3fff), p);
+      p += 4;
+      bfd_put_32 (abfd, ORI_R11_R11_0 | (HA34 (off) & 0xffff), p);
+      p += 4;
+      if (odd)
+	{
+	  bfd_put_32 (abfd, SLDI_R11_R11_34, p);
+	  p += 4;
+	}
+      insn = PADDI_R12_PC | D34 (off);
+      bfd_put_32 (abfd, insn >> 32, p);
+      p += 4;
+      bfd_put_32 (abfd, insn, p);
+      p += 4;
+      if (!odd)
+	{
+	  bfd_put_32 (abfd, SLDI_R11_R11_34, p);
+	  p += 4;
+	}
+      if (load)
+	bfd_put_32 (abfd, LDX_R12_R11_R12, p);
+      else
+	bfd_put_32 (abfd, ADD_R12_R11_R12, p);
+    }
+  p += 4;
+  return p;
+}
+
+static unsigned int
+size_powerxx_offset (bfd_vma off, int odd)
+{
+  if (off - odd + (1ULL << 33) < 1ULL << 34)
+    return odd + 8;
+  else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+    return 20;
+  else
+    return 24;
+}
+
+static unsigned int
+num_relocs_for_powerxx_offset (bfd_vma off, int odd)
+{
+  if (off - odd + (1ULL << 33) < 1ULL << 34)
+    return 1;
+  else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+    return 2;
+  else
+    return 3;
+}
+
+static Elf_Internal_Rela *
+emit_relocs_for_powerxx_offset (struct bfd_link_info *info,
+				Elf_Internal_Rela *r, bfd_vma roff,
+				bfd_vma targ, bfd_vma off, int odd)
+{
+  if (off - odd + (1ULL << 33) < 1ULL << 34)
+    roff += odd;
+  else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+    {
+      int d_offset = bfd_big_endian (info->output_bfd) ? 2 : 0;
+      r->r_offset = roff + d_offset;
+      r->r_addend = targ + 8 - odd - d_offset;
+      r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHERA34);
+      ++r;
+      roff += 8 - odd;
+    }
+  else
+    {
+      int d_offset = bfd_big_endian (info->output_bfd) ? 2 : 0;
+      r->r_offset = roff + d_offset;
+      r->r_addend = targ + 8 + odd - d_offset;
+      r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHESTA34);
+      ++r;
+      roff += 4;
+      r->r_offset = roff + d_offset;
+      r->r_addend = targ + 4 + odd - d_offset;
+      r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHERA34);
+      ++r;
+      roff += 4 + odd;
+    }
+  r->r_offset = roff;
+  r->r_addend = targ;
+  r->r_info = ELF64_R_INFO (0, R_PPC64_PCREL34);
+  return r;
+}
+
 /* Emit .eh_frame opcode to advance pc by DELTA.  */
 
 static bfd_byte *
@@ -10285,7 +10474,17 @@ plt_stub_size (struct ppc_link_hash_table *htab,
 
   if (stub_entry->stub_type >= ppc_stub_plt_call_notoc)
     {
-      size = 8 + size_offset (off - 8);
+      if (htab->powerxx_stubs)
+	{
+	  bfd_vma start = (stub_entry->stub_offset
+			   + stub_entry->group->stub_sec->output_offset
+			   + stub_entry->group->stub_sec->output_section->vma);
+	  if (stub_entry->stub_type > ppc_stub_plt_call_notoc)
+	    start += 4;
+	  size = 8 + size_powerxx_offset (off, start & 4);
+	}
+      else
+	size = 8 + size_offset (off - 8);
       if (stub_entry->stub_type > ppc_stub_plt_call_notoc)
 	size += 4;
       return size;
@@ -10738,6 +10937,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   Elf_Internal_Rela *r;
   asection *plt;
   int num_rel;
+  int odd;
 
   /* Massage our args to the form they really have.  */
   stub_entry = (struct ppc_stub_hash_entry *) gen_entry;
@@ -11016,18 +11216,28 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 	targ = (stub_entry->target_value
 		+ stub_entry->target_section->output_offset
 		+ stub_entry->target_section->output_section->vma);
+      odd = off & 4;
       off = targ - off;
 
       relp = p;
       num_rel = 0;
-      /* The notoc stubs calculate their target (either a PLT entry or
-	 the global entry point of a function) relative to the PC
-	 returned by the "bcl" two instructions past the start of the
-	 sequence emitted by build_offset.  The offset is therefore 8
-	 less than calculated from the start of the sequence.  */
-      off -= 8;
-      p = build_offset (htab->params->stub_bfd, p, off,
-			stub_entry->stub_type >= ppc_stub_plt_call_notoc);
+      if (htab->powerxx_stubs)
+	{
+	  bfd_boolean load = stub_entry->stub_type >= ppc_stub_plt_call_notoc;
+	  p = build_powerxx_offset (htab->params->stub_bfd, p, off, odd, load);
+	}
+      else
+	{
+	  /* The notoc stubs calculate their target (either a PLT entry or
+	     the global entry point of a function) relative to the PC
+	     returned by the "bcl" two instructions past the start of the
+	     sequence emitted by build_offset.  The offset is therefore 8
+	     less than calculated from the start of the sequence.  */
+	  off -= 8;
+	  p = build_offset (htab->params->stub_bfd, p, off,
+			    stub_entry->stub_type >= ppc_stub_plt_call_notoc);
+	}
+
       if (stub_entry->stub_type <= ppc_stub_long_branch_both)
 	{
 	  bfd_vma from;
@@ -11049,13 +11259,21 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 
       if (info->emitrelocations)
 	{
-	  bfd_vma roff;
-	  num_rel += num_relocs_for_offset (off);
+	  bfd_vma roff = relp - stub_entry->group->stub_sec->contents;
+	  if (htab->powerxx_stubs)
+	    num_rel += num_relocs_for_powerxx_offset (off, odd);
+	  else
+	    {
+	      num_rel += num_relocs_for_offset (off);
+	      roff += 16;
+	    }
 	  r = get_relocs (stub_entry->group->stub_sec, num_rel);
 	  if (r == NULL)
 	    return FALSE;
-	  roff = relp + 16 - stub_entry->group->stub_sec->contents;
-	  r = emit_relocs_for_offset (info, r, roff, targ, off);
+	  if (htab->powerxx_stubs)
+	    r = emit_relocs_for_powerxx_offset (info, r, roff, targ, off, odd);
+	  else
+	    r = emit_relocs_for_offset (info, r, roff, targ, off);
 	  if (stub_entry->stub_type == ppc_stub_long_branch_notoc
 	      || stub_entry->stub_type == ppc_stub_long_branch_both)
 	    {
@@ -11070,8 +11288,9 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 	    }
 	}
 
-      if (htab->glink_eh_frame != NULL
-	&& htab->glink_eh_frame->size != 0)
+      if (!htab->powerxx_stubs
+	  && htab->glink_eh_frame != NULL
+	  && htab->glink_eh_frame->size != 0)
 	{
 	  bfd_byte *base, *eh;
 	  unsigned int lr_used, delta;
@@ -11240,7 +11459,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   struct ppc_link_hash_table *htab;
   asection *plt;
   bfd_vma targ, off, r2off;
-  unsigned int size, extra, lr_used, delta;
+  unsigned int size, extra, lr_used, delta, odd;
 
   /* Massage our args to the form they really have.  */
   stub_entry = (struct ppc_stub_hash_entry *) gen_entry;
@@ -11393,16 +11612,24 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       targ = (stub_entry->target_value
 	      + stub_entry->target_section->output_offset
 	      + stub_entry->target_section->output_section->vma);
+      odd = off & 4;
       off = targ - off;
 
       if (info->emitrelocations)
 	{
-	  stub_entry->group->stub_sec->reloc_count
-	    += num_relocs_for_offset (off);
+	  unsigned int num_rel;
+	  if (htab->powerxx_stubs)
+	    num_rel = num_relocs_for_powerxx_offset (off, odd);
+	  else
+	    num_rel = num_relocs_for_offset (off - 8);
+	  stub_entry->group->stub_sec->reloc_count += num_rel;
 	  stub_entry->group->stub_sec->flags |= SEC_RELOC;
 	}
 
-      extra = size_offset (off - 8);
+      if (htab->powerxx_stubs)
+	extra = size_powerxx_offset (off, odd);
+      else
+	extra = size_offset (off - 8);
       /* Include branch insn plus those in the offset sequence.  */
       size += 4 + extra;
       /* The branch insn is at the end, or "extra" bytes along.  So
@@ -11410,17 +11637,20 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 	 calculated.  */
       off -= extra;
 
-      /* After the bcl, lr has been modified so we need to emit
-	 .eh_frame info saying the return address is in r12.  */
-      lr_used = stub_entry->stub_offset + 8;
-      if (stub_entry->stub_type == ppc_stub_long_branch_both)
-	lr_used += 4;
-      /* The eh_frame info will consist of a DW_CFA_advance_loc or
-	 variant, DW_CFA_register, 65, 12, DW_CFA_advance_loc+2,
-	 DW_CFA_restore_extended 65.  */
-      delta = lr_used - stub_entry->group->lr_restore;
-      stub_entry->group->eh_size += eh_advance_size (delta) + 6;
-      stub_entry->group->lr_restore = lr_used + 8;
+      if (!htab->powerxx_stubs)
+	{
+	  /* After the bcl, lr has been modified so we need to emit
+	     .eh_frame info saying the return address is in r12.  */
+	  lr_used = stub_entry->stub_offset + 8;
+	  if (stub_entry->stub_type == ppc_stub_long_branch_both)
+	    lr_used += 4;
+	  /* The eh_frame info will consist of a DW_CFA_advance_loc or
+	     variant, DW_CFA_register, 65, 12, DW_CFA_advance_loc+2,
+	     DW_CFA_restore_extended 65.  */
+	  delta = lr_used - stub_entry->group->lr_restore;
+	  stub_entry->group->eh_size += eh_advance_size (delta) + 6;
+	  stub_entry->group->lr_restore = lr_used + 8;
+	}
 
       /* If the branch can't reach, use a plt_branch.  */
       if (off + (1 << 25) >= (bfd_vma) (1 << 26))
@@ -11455,6 +11685,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 	    plt = htab->pltlocal;
 	}
       targ += plt->output_offset + plt->output_section->vma;
+      odd = off & 4;
       off = targ - off;
 
       if (htab->params->plt_stub_align != 0)
@@ -11468,24 +11699,31 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 
       if (info->emitrelocations)
 	{
-	  stub_entry->group->stub_sec->reloc_count
-	    += num_relocs_for_offset (off - 8);
+	  unsigned int num_rel;
+	  if (htab->powerxx_stubs)
+	    num_rel = num_relocs_for_powerxx_offset (off, odd);
+	  else
+	    num_rel = num_relocs_for_offset (off - 8);
+	  stub_entry->group->stub_sec->reloc_count += num_rel;
 	  stub_entry->group->stub_sec->flags |= SEC_RELOC;
 	}
 
       size = plt_stub_size (htab, stub_entry, off);
 
-      /* After the bcl, lr has been modified so we need to emit
-	 .eh_frame info saying the return address is in r12.  */
-      lr_used = stub_entry->stub_offset + 8;
-      if (stub_entry->stub_type == ppc_stub_plt_call_both)
-	lr_used += 4;
-      /* The eh_frame info will consist of a DW_CFA_advance_loc or
-	 variant, DW_CFA_register, 65, 12, DW_CFA_advance_loc+2,
-	 DW_CFA_restore_extended 65.  */
-      delta = lr_used - stub_entry->group->lr_restore;
-      stub_entry->group->eh_size += eh_advance_size (delta) + 6;
-      stub_entry->group->lr_restore = lr_used + 8;
+      if (!htab->powerxx_stubs)
+	{
+	  /* After the bcl, lr has been modified so we need to emit
+	     .eh_frame info saying the return address is in r12.  */
+	  lr_used = stub_entry->stub_offset + 8;
+	  if (stub_entry->stub_type == ppc_stub_plt_call_both)
+	    lr_used += 4;
+	  /* The eh_frame info will consist of a DW_CFA_advance_loc or
+	     variant, DW_CFA_register, 65, 12, DW_CFA_advance_loc+2,
+	     DW_CFA_restore_extended 65.  */
+	  delta = lr_used - stub_entry->group->lr_restore;
+	  stub_entry->group->eh_size += eh_advance_size (delta) + 6;
+	  stub_entry->group->lr_restore = lr_used + 8;
+	}
       break;
 
     case ppc_stub_plt_call:
diff --git a/ld/testsuite/ld-powerpc/notoc2.d b/ld/testsuite/ld-powerpc/notoc2.d
new file mode 100644
index 0000000000..172835fee4
--- /dev/null
+++ b/ld/testsuite/ld-powerpc/notoc2.d
@@ -0,0 +1,29 @@
+#source: notoc2.s
+#as: -a64 -mfuture
+#ld: -shared -z norelro
+#objdump: -d -Mfuture
+#target: powerpc64*-*-*
+
+.*
+
+Disassembly of section \.text:
+
+.* <.*\.plt_call\.puts>:
+.*:	(04 10 00 01|01 00 10 04) 	pld     r12,66200
+.*:	(e5 80 02 98|98 02 80 e5) 
+.*:	(7d 89 03 a6|a6 03 89 7d) 	mtctr   r12
+.*:	(4e 80 04 20|20 04 80 4e) 	bctr
+#...
+.*:	(04 13 ff ff|ff ff 13 04) 	pld     r12,-1
+.*:	(e5 80 ff ff|ff ff 80 e5) 
+.*:	(04 10 00 00|00 00 10 04) 	pld     r12,0
+.*:	(e5 80 00 00|00 00 80 e5) 
+.*:	(06 13 ff ff|ff ff 13 06) 	pla     r12,-1
+.*:	(39 80 ff ff|ff ff 80 39) 
+.*:	(06 10 00 00|00 00 10 06) 	pla     r12,0
+.*:	(39 80 00 00|00 00 80 39) 
+.*:	(06 10 00 00|00 00 10 06) 	pla     r3,92
+.*:	(38 60 00 5c|5c 00 60 38) 
+.*:	(4b ff ff 99|99 ff ff 4b) 	bl      .* <.*\.plt_call\.puts>
+.*:	(60 00 00 00|00 00 00 60) 	nop
+#pass
diff --git a/ld/testsuite/ld-powerpc/notoc2.s b/ld/testsuite/ld-powerpc/notoc2.s
new file mode 100644
index 0000000000..3a4274c7c0
--- /dev/null
+++ b/ld/testsuite/ld-powerpc/notoc2.s
@@ -0,0 +1,13 @@
+ .text
+ .weak puts
+ pld 12,-1(0),1
+ pld 12,0(0),1
+ paddi 12,0,-1,1
+ paddi 12,0,0,1
+0:
+ paddi 3,0,hello-.,1
+ bl puts@notoc
+ nop
+ b 0b
+ .section .rodata
+hello:	.asciz "Hello!"
diff --git a/ld/testsuite/ld-powerpc/powerpc.exp b/ld/testsuite/ld-powerpc/powerpc.exp
index 658f319739..af4d13e5ce 100644
--- a/ld/testsuite/ld-powerpc/powerpc.exp
+++ b/ld/testsuite/ld-powerpc/powerpc.exp
@@ -260,6 +260,8 @@ set ppc64elftests {
     {"notoc ext" "" "" "-a64" {ext.s} {} ""}
     {"notoc" "-melf64ppc --no-plt-localentry -T ext.lnk" "" "-a64" {notoc.s}
 	{{objdump -d notoc.d} {readelf {-wf -W} notoc.wf}} "notoc"}
+    {"notoc2" "-melf64ppc -shared" "" "-a64 -mfuture" {notoc2.s}
+	{{objdump {-d -Mfuture} notoc2.d}} "notoc2"}
     {"pcrelopt" "-melf64ppc --hash-style=gnu" "tmpdir/symtocbase.so"
 	"-a64 -mfuture" {pcrelopt.s}
 	{{objdump {-d -Mfuture} pcrelopt.d}

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/5] PowerPC add initial -mfuture instruction support
  2019-05-24  1:33 ` [PATCH 1/5] PowerPC add initial -mfuture instruction support Alan Modra
@ 2019-06-02  6:33   ` Jan Beulich
  2019-06-03  2:26     ` Alan Modra
  2019-06-20 12:09   ` Jan Beulich
  1 sibling, 1 reply; 13+ messages in thread
From: Jan Beulich @ 2019-06-02  6:33 UTC (permalink / raw)
  To: amodra; +Cc: binutils

Alan,

>>> Alan Modra <amodra@gmail.com> 05/24/19 3:33 AM >>>
>@@ -376,6 +381,8 @@ powerpc_init_dialect (struct disassemble_info *info)
 >
>#define PPC_OPCD_SEGS (1 + PPC_OP (-1))
>static unsigned short powerpc_opcd_indices[PPC_OPCD_SEGS + 1];
>+#define PREFIX_OPCD_SEGS (1 + PPC_PREFIX_SEG (-1))
>+static unsigned short prefix_opcd_indices[PPC_OPCD_SEGS+1];

Isn't the array dimension a copy-and-paste mistake, i.e. shouldn't it be
PREFIX_OPCD_SEGS + 1 ?

Jan


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/5] PowerPC add initial -mfuture instruction support
  2019-06-02  6:33   ` Jan Beulich
@ 2019-06-03  2:26     ` Alan Modra
  0 siblings, 0 replies; 13+ messages in thread
From: Alan Modra @ 2019-06-03  2:26 UTC (permalink / raw)
  To: Jan Beulich; +Cc: binutils

On Sun, Jun 02, 2019 at 12:33:15AM -0600, Jan Beulich wrote:
> Alan,
> 
> >>> Alan Modra <amodra@gmail.com> 05/24/19 3:33 AM >>>
> >@@ -376,6 +381,8 @@ powerpc_init_dialect (struct disassemble_info *info)
>  >
> >#define PPC_OPCD_SEGS (1 + PPC_OP (-1))
> >static unsigned short powerpc_opcd_indices[PPC_OPCD_SEGS + 1];
> >+#define PREFIX_OPCD_SEGS (1 + PPC_PREFIX_SEG (-1))
> >+static unsigned short prefix_opcd_indices[PPC_OPCD_SEGS+1];
> 
> Isn't the array dimension a copy-and-paste mistake, i.e. shouldn't it be
> PREFIX_OPCD_SEGS + 1 ?

Thanks Jan, yes the array size should be smaller.

	* ppc-dis.c (prefix_opcd_indices): Correct size.

diff --git a/opcodes/ppc-dis.c b/opcodes/ppc-dis.c
index 9334be2138..2f5756b6cc 100644
--- a/opcodes/ppc-dis.c
+++ b/opcodes/ppc-dis.c
@@ -382,7 +382,7 @@ powerpc_init_dialect (struct disassemble_info *info)
 #define PPC_OPCD_SEGS (1 + PPC_OP (-1))
 static unsigned short powerpc_opcd_indices[PPC_OPCD_SEGS + 1];
 #define PREFIX_OPCD_SEGS (1 + PPC_PREFIX_SEG (-1))
-static unsigned short prefix_opcd_indices[PPC_OPCD_SEGS+1];
+static unsigned short prefix_opcd_indices[PREFIX_OPCD_SEGS + 1];
 #define VLE_OPCD_SEGS (1 + VLE_OP_TO_SEG (VLE_OP (-1, 0xffff)))
 static unsigned short vle_opcd_indices[VLE_OPCD_SEGS + 1];
 #define SPE2_OPCD_SEGS (1 + SPE2_XOP_TO_SEG (SPE2_XOP (-1)))

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/5] PowerPC D-form prefixed loads and stores
  2019-05-24  1:34 ` [PATCH 2/5] PowerPC D-form prefixed loads and stores Alan Modra
@ 2019-06-20 12:04   ` Jan Beulich
  2019-06-20 13:17     ` Alan Modra
  0 siblings, 1 reply; 13+ messages in thread
From: Jan Beulich @ 2019-06-20 12:04 UTC (permalink / raw)
  To: amodra; +Cc: binutils

>>> Alan Modra <amodra@gmail.com> 05/24/19 3:34 AM >>>
>@@ -7815,6 +7969,32 @@ const unsigned int powerpc_num_opcodes =
 >
>const struct powerpc_opcode prefix_opcodes[] = {
>{"pnop",	  PMRR,		       PREFIX_MASK,	POWERXX, 0,	{0}},
>+{"pli",		  PMLS|OP(14),	       P_DRAPCREL_MASK,	POWERXX, 0,	{RT, SI34}},
>+{"paddi",	  PMLS|OP(14),	       P_D_MASK,	POWERXX, 0,	{RT, RA0, SI34, PCREL0}},
>+{"psubi",	  PMLS|OP(14),	       P_D_MASK,	POWERXX, 0,	{RT, RA0, NSI34, PCREL0}},
>+{"pla",		  PMLS|OP(14),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},
>+{"plwz",	  PMLS|OP(32),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},
>+{"plbz",	  PMLS|OP(34),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},
>+{"pstw",	  PMLS|OP(36),	       P_D_MASK,	POWERXX, 0,	{RS, D34, PRA0, PCREL}},
>+{"pstb",	  PMLS|OP(38),	       P_D_MASK,	POWERXX, 0,	{RS, D34, PRA0, PCREL}},
>+{"plhz",	  PMLS|OP(40),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},
>+{"plwa",	  P8LS|OP(41),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},

Comparing with the neighboring ones - isn't this supposed to use PMLS?


>+{"plxsd",	  P8LS|OP(42),	       P_D_MASK,	POWERXX, 0,	{VD, D34, PRA0, PCREL}},
>+{"plha",	  PMLS|OP(42),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},

Judging, most importantly, from this one.

Jan


>+{"plxssp",	  P8LS|OP(43),	       P_D_MASK,	POWERXX, 0,	{VD, D34, PRA0, PCREL}},
>+{"psth",	  PMLS|OP(44),	       P_D_MASK,	POWERXX, 0,	{RS, D34, PRA0, PCREL}},
>+{"pstxsd",	  P8LS|OP(46),	       P_D_MASK,	POWERXX, 0,	{VS, D34, PRA0, PCREL}},
>+{"pstxssp",	  P8LS|OP(47),	       P_D_MASK,	POWERXX, 0,	{VS, D34, PRA0, PCREL}},
>+{"plfs",	  PMLS|OP(48),	       P_D_MASK,	POWERXX, 0,	{FRT, D34, PRA0, PCREL}},
>+{"plxv",	  P8LS|OP(50),	       P_D_MASK&~OP(1),	POWERXX, 0,	{XTOP, D34, PRA0, PCREL}},
>+{"plfd",	  PMLS|OP(50),	       P_D_MASK,	POWERXX, 0,	{FRT, D34, PRA0, PCREL}},
>+{"pstfs",	  PMLS|OP(52),	       P_D_MASK,	POWERXX, 0,	{FRS, D34, PRA0, PCREL}},
>+{"pstxv",	  P8LS|OP(54),	       P_D_MASK&~OP(1),	POWERXX, 0,	{XTOP, D34, PRA0, PCREL}},
>+{"pstfd",	  PMLS|OP(54),	       P_D_MASK,	POWERXX, 0,	{FRS, D34, PRA0, PCREL}},
>+{"plq",		  P8LS|OP(56),	       P_D_MASK,	POWERXX, 0,	{RTQ, D34, PRAQ, PCREL}},
>+{"pld",		  P8LS|OP(57),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},
>+{"pstq",	  P8LS|OP(60),	       P_D_MASK,	POWERXX, 0,	{RSQ, D34, PRA0, PCREL}},
>+{"pstd",	  P8LS|OP(61),	       P_D_MASK,	POWERXX, 0,	{RS, D34, PRA0, PCREL}},
>};


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/5] PowerPC add initial -mfuture instruction support
  2019-05-24  1:33 ` [PATCH 1/5] PowerPC add initial -mfuture instruction support Alan Modra
  2019-06-02  6:33   ` Jan Beulich
@ 2019-06-20 12:09   ` Jan Beulich
  2019-06-20 13:31     ` Alan Modra
  1 sibling, 1 reply; 13+ messages in thread
From: Jan Beulich @ 2019-06-20 12:09 UTC (permalink / raw)
  To: amodra; +Cc: binutils

>>> Alan Modra <amodra@gmail.com> 05/24/19 3:33 AM >>>
>--- a/opcodes/ppc-opc.c
>+++ b/opcodes/ppc-opc.c
>@@ -2721,6 +2721,18 @@ const unsigned int num_powerpc_operands = (sizeof (powerpc_operands)
>#define OP(x) ((((uint64_t)(x)) & 0x3f) << 26)
>#define OP_MASK OP (0x3f)
 >
>+/* The prefix opcode.  */
>+#define PREFIX_OP (1ULL << 58)
>+
>+/* The 2-bit prefix form.  */
>+#define PREFIX_FORM(x) ((x & 3ULL) << 56)
>+
>+#define SUFFIX_MASK ((1ULL << 32) - 1)
>+#define PREFIX_MASK (SUFFIX_MASK << 32)

With these, am I right in understanding that ...


>@@ -7796,6 +7809,17 @@ const struct powerpc_opcode powerpc_opcodes[] = {
>const unsigned int powerpc_num_opcodes =
>sizeof (powerpc_opcodes) / sizeof (powerpc_opcodes[0]);
  >
>+/* The opcode table for 8-byte prefix instructions.
>+
>+   The format of this opcode table is the same as the main opcode table.  */
>+
>+const struct powerpc_opcode prefix_opcodes[] = {
>+{"pnop",	  PMRR,		       PREFIX_MASK,	POWERXX, 0,	{0}},

... everything with the 32 prefix bits matching the pattern decodes to PNOP, regardless of
any of the 32 suffix bits? The testcase added covers only the case of the suffix bits all being
zero.


Jan


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/5] PowerPC D-form prefixed loads and stores
  2019-06-20 12:04   ` Jan Beulich
@ 2019-06-20 13:17     ` Alan Modra
  0 siblings, 0 replies; 13+ messages in thread
From: Alan Modra @ 2019-06-20 13:17 UTC (permalink / raw)
  To: Jan Beulich; +Cc: binutils

On Thu, Jun 20, 2019 at 06:04:43AM -0600, Jan Beulich wrote:
> >>> Alan Modra <amodra@gmail.com> 05/24/19 3:34 AM >>>
> >@@ -7815,6 +7969,32 @@ const unsigned int powerpc_num_opcodes =
>  >
> >const struct powerpc_opcode prefix_opcodes[] = {
> >{"pnop",	  PMRR,		       PREFIX_MASK,	POWERXX, 0,	{0}},
> >+{"pli",		  PMLS|OP(14),	       P_DRAPCREL_MASK,	POWERXX, 0,	{RT, SI34}},
> >+{"paddi",	  PMLS|OP(14),	       P_D_MASK,	POWERXX, 0,	{RT, RA0, SI34, PCREL0}},
> >+{"psubi",	  PMLS|OP(14),	       P_D_MASK,	POWERXX, 0,	{RT, RA0, NSI34, PCREL0}},
> >+{"pla",		  PMLS|OP(14),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},
> >+{"plwz",	  PMLS|OP(32),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},
> >+{"plbz",	  PMLS|OP(34),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},
> >+{"pstw",	  PMLS|OP(36),	       P_D_MASK,	POWERXX, 0,	{RS, D34, PRA0, PCREL}},
> >+{"pstb",	  PMLS|OP(38),	       P_D_MASK,	POWERXX, 0,	{RS, D34, PRA0, PCREL}},
> >+{"plhz",	  PMLS|OP(40),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},
> >+{"plwa",	  P8LS|OP(41),	       P_D_MASK,	POWERXX, 0,	{RT, D34, PRA0, PCREL}},
> 
> Comparing with the neighboring ones - isn't this supposed to use PMLS?

No.  The PMLS forms use the same opcode in the suffix word.  plwa
doesn't share the lwa encoding, and doesn't have the lwa restriction
of the offset being a multiple of 4.

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/5] PowerPC add initial -mfuture instruction support
  2019-06-20 12:09   ` Jan Beulich
@ 2019-06-20 13:31     ` Alan Modra
  2019-06-20 13:40       ` Alan Modra
  0 siblings, 1 reply; 13+ messages in thread
From: Alan Modra @ 2019-06-20 13:31 UTC (permalink / raw)
  To: Jan Beulich; +Cc: binutils

On Thu, Jun 20, 2019 at 06:09:07AM -0600, Jan Beulich wrote:
> >>> Alan Modra <amodra@gmail.com> 05/24/19 3:33 AM >>>
> >--- a/opcodes/ppc-opc.c
> >+++ b/opcodes/ppc-opc.c
> >@@ -2721,6 +2721,18 @@ const unsigned int num_powerpc_operands = (sizeof (powerpc_operands)
> >#define OP(x) ((((uint64_t)(x)) & 0x3f) << 26)
> >#define OP_MASK OP (0x3f)
>  >
> >+/* The prefix opcode.  */
> >+#define PREFIX_OP (1ULL << 58)
> >+
> >+/* The 2-bit prefix form.  */
> >+#define PREFIX_FORM(x) ((x & 3ULL) << 56)
> >+
> >+#define SUFFIX_MASK ((1ULL << 32) - 1)
> >+#define PREFIX_MASK (SUFFIX_MASK << 32)
> 
> With these, am I right in understanding that ...
> 
> 
> >@@ -7796,6 +7809,17 @@ const struct powerpc_opcode powerpc_opcodes[] = {
> >const unsigned int powerpc_num_opcodes =
> >sizeof (powerpc_opcodes) / sizeof (powerpc_opcodes[0]);
>   >
> >+/* The opcode table for 8-byte prefix instructions.
> >+
> >+   The format of this opcode table is the same as the main opcode table.  */
> >+
> >+const struct powerpc_opcode prefix_opcodes[] = {
> >+{"pnop",	  PMRR,		       PREFIX_MASK,	POWERXX, 0,	{0}},
> 
> ... everything with the 32 prefix bits matching the pattern decodes to PNOP, regardless of
> any of the 32 suffix bits? The testcase added covers only the case of the suffix bits all being
> zero.

Yes the suffix word is don't care with one restriction, which we
haven't implemented in binutils yet.  It can't be a branch instruction
encoding.

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/5] PowerPC add initial -mfuture instruction support
  2019-06-20 13:31     ` Alan Modra
@ 2019-06-20 13:40       ` Alan Modra
  0 siblings, 0 replies; 13+ messages in thread
From: Alan Modra @ 2019-06-20 13:40 UTC (permalink / raw)
  To: Jan Beulich; +Cc: binutils

On Thu, Jun 20, 2019 at 11:01:03PM +0930, Alan Modra wrote:
> Yes the suffix word is don't care with one restriction, which we
> haven't implemented in binutils yet.  It can't be a branch instruction
> encoding.

Well, that's the thinking now.  As with all of the -mfuture support
everything is subject to change so there isn't much reason to
implement the restriction at this point.

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2019-06-20 13:40 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-05-24  1:31 [PATCH 0/5] PowerPC -mfuture support Alan Modra
2019-05-24  1:33 ` [PATCH 1/5] PowerPC add initial -mfuture instruction support Alan Modra
2019-06-02  6:33   ` Jan Beulich
2019-06-03  2:26     ` Alan Modra
2019-06-20 12:09   ` Jan Beulich
2019-06-20 13:31     ` Alan Modra
2019-06-20 13:40       ` Alan Modra
2019-05-24  1:34 ` [PATCH 2/5] PowerPC D-form prefixed loads and stores Alan Modra
2019-06-20 12:04   ` Jan Beulich
2019-06-20 13:17     ` Alan Modra
2019-05-24  1:35 ` [PATCH 3/5] PowerPC relocations for prefix insns Alan Modra
2019-05-24  1:36 ` [PATCH 4/5] PowerPC GOT_PCREL34 optimisation Alan Modra
2019-05-24  1:37 ` [PATCH 5/5] PowerPC notoc linkage stubs Alan Modra

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).