PowerPC @l, @h and @ha warnings, plus VLE e_li

This patch started off just adding the warnings in tc-ppc.c about
incorrect usage of @l, @h and @ha in instructions that don't have
16-bit D-form fields.  That unfortunately showed up three warnings in
ld/testsuite/ld-powerpc/vle-multiseg.s on instructions like
	e_li r3, IV_table@l+0x00
which was being assembled to
   8:	70 60 00 00 	e_li    r3,0
			a: R_PPC_ADDR16_LO	IV_table
The ADDR16_LO reloc is of course completely bogus on e_li, which has
a split 20-bit signed integer field in bits 0x1f7fff, the low 11 bit
in 0x7ff, the next 5 bits in 0x1f0000, and the high 4 bits in 0x7800.
Applying an ADDR16_LO reloc to the instruction potentially changes
the e_li instruction to e_add2i., e_add2is, e_cmp16i, e_mull2i,
e_cmpl16i, e_cmph16i, e_cmphl16i, e_or2i, e_and2i., e_or2is, e_lis,
e_and2is, or some invalid encodings.

Now there is a relocation that suits e_li, R_PPC_VLE_ADDR20, which was
added 2017-09-05 but I can't see code in gas to generate the
relocation.  In any case, VLE_ADDR20 probably doesn't have the correct
semantics for @l since ideally you'd want an @l to pair with @h or @ha
to generate a 32-bit constant.  Thus @l should only produce a 16-bit
value, I think.  So we need some more relocations to handle e_li it
seems, or as I do in this patch, modify the behaviour of existing
relocations when applied to e_li instructions.

include/
	* opcode/ppc.h (E_OPCODE_MASK, E_LI_MASK, E_LI_INSN): Define.
bfd/
	* elf32-ppc.c (ppc_elf_howto_raw <R_PPC_VLE_ADDR20>): Correct
	mask and shift value.
	(ppc_elf_vle_split16): Use E_OPCODE_MASK.  Handle e_li
	specially.
gas/
	* config/tc-ppc.c (md_assemble): Adjust relocs for VLE before
	TLS tweaks.  Handle e_li.  Warn on unexpected operand field
	for lo16/hi16/ha16 relocs.
diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index de468a4..8a37271 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,10 @@
+2018-12-06  Alan Modra  <amodra@gmail.com>
+
+	* elf32-ppc.c (ppc_elf_howto_raw <R_PPC_VLE_ADDR20>): Correct
+	mask and shift value.
+	(ppc_elf_vle_split16): Use E_OPCODE_MASK.  Handle e_li
+	specially.
+
 2018-12-05  Sam Tebbs  <sam.tebbs@arm.com>
 
 	* elf-eh-frame.c (_bfd_elf_parse_eh_frame): Add check for 'B'.
diff --git a/bfd/elf32-ppc.c b/bfd/elf32-ppc.c
index c31e26e..6b6043a 100644
--- a/bfd/elf32-ppc.c
+++ b/bfd/elf32-ppc.c
@@ -649,8 +649,8 @@
        ppc_elf_unhandled_reloc),
 
   /* e_li split20 format.  */
-  HOW (R_PPC_VLE_ADDR20, 2, 20, 0x1f07ff, 16, FALSE, dont,
-       bfd_elf_generic_reloc),
+  HOW (R_PPC_VLE_ADDR20, 2, 20, 0x1f7fff, 0, FALSE, dont,
+       ppc_elf_unhandled_reloc),
 
   HOW (R_PPC_IRELATIVE, 2, 32, 0xffffffff, 0, FALSE, dont,
        ppc_elf_unhandled_reloc),
@@ -3886,10 +3886,10 @@
 		     split16_format_type split16_format,
 		     bfd_boolean fixup)
 {
-  unsigned int insn, opcode, top5;
+  unsigned int insn, opcode;
 
   insn = bfd_get_32 (input_bfd, loc);
-  opcode = insn & 0xfc00f800;
+  opcode = insn & E_OPCODE_MASK;
   if (opcode == E_OR2I_INSN
       || opcode == E_AND2I_DOT_INSN
       || opcode == E_OR2IS_INSN
@@ -3926,10 +3926,22 @@
 	       input_bfd, input_section, offset, opcode);
 	}
     }
-  top5 = value & 0xf800;
-  top5 = top5 << (split16_format == split16a_type ? 5 : 10);
-  insn &= (split16_format == split16a_type ? ~0x1f07ff : ~0x3e007ff);
-  insn |= top5;
+  if (split16_format == split16a_type)
+    {
+      insn &= ~((0xf800 << 5) | 0x7ff);
+      insn |= (value & 0xf800) << 5;
+      if ((insn & E_LI_MASK) == E_LI_INSN)
+	{
+	  /* Hack for e_li.  Extend sign.  */
+	  insn &= ~(0xf0000 >> 5);
+	  insn |= (-(value & 0x8000) & 0xf0000) >> 5;
+	}
+    }
+  else
+    {
+      insn &= ~((0xf800 << 10) | 0x7ff);
+      insn |= (value & 0xf800) << 10;
+    }
   insn |= value & 0x7ff;
   bfd_put_32 (input_bfd, insn, loc);
 }
diff --git a/gas/ChangeLog b/gas/ChangeLog
index a4e0767..72ae3bc 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,3 +1,9 @@
+2018-12-06  Alan Modra  <amodra@gmail.com>
+
+	* config/tc-ppc.c (md_assemble): Adjust relocs for VLE before
+	TLS tweaks.  Handle e_li.  Warn on unexpected operand field
+	for lo16/hi16/ha16 relocs.
+
 2018-12-06  Andrew Burgess  <andrew.burgess@embecosm.com>
 
 	* config/tc-riscv.h (FAKE_LABEL_NAME): Define as
diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c
index a8eda5d..cebbd42 100644
--- a/gas/config/tc-ppc.c
+++ b/gas/config/tc-ppc.c
@@ -3433,39 +3433,6 @@
 
 	  if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_NONE)
 	    {
-	      /* Some TLS tweaks.  */
-	      switch (reloc)
-		{
-		default:
-		  break;
-
-		case BFD_RELOC_PPC_TLS:
-		  if (!_bfd_elf_ppc_at_tls_transform (opcode->opcode, 0))
-		    as_bad (_("@tls may not be used with \"%s\" operands"),
-			    opcode->name);
-		  else if (operand->shift != 11)
-		    as_bad (_("@tls may only be used in last operand"));
-		  else
-		    insn = ppc_insert_operand (insn, operand,
-					       ppc_obj64 ? 13 : 2,
-					       ppc_cpu, (char *) NULL, 0);
-		  break;
-
-		  /* We'll only use the 32 (or 64) bit form of these relocations
-		     in constants.  Instructions get the 16 bit form.  */
-		case BFD_RELOC_PPC_DTPREL:
-		  reloc = BFD_RELOC_PPC_DTPREL16;
-		  break;
-		case BFD_RELOC_PPC_TPREL:
-		  reloc = BFD_RELOC_PPC_TPREL16;
-		  break;
-		}
-
-	      /* addpcis.  */
-	      if (opcode->opcode == (19 << 26) + (2 << 1)
-		  && reloc == BFD_RELOC_HI16_S)
-		reloc = BFD_RELOC_PPC_16DX_HA;
-
 	      /* If VLE-mode convert LO/HI/HA relocations.  */
       	      if (opcode->flags & PPC_OPCODE_VLE)
 		{
@@ -3474,6 +3441,7 @@
 		  int use_a_reloc = (tmp_insn == E_OR2I_INSN
 				     || tmp_insn == E_AND2I_DOT_INSN
 				     || tmp_insn == E_OR2IS_INSN
+				     || tmp_insn == E_LI_INSN
 				     || tmp_insn == E_LIS_INSN
 				     || tmp_insn == E_AND2IS_DOT_INSN);
 
@@ -3532,6 +3500,60 @@
 		      break;
 		    }
 		}
+
+	      /* TLS and other tweaks.  */
+	      switch (reloc)
+		{
+		default:
+		  break;
+
+		case BFD_RELOC_PPC_TLS:
+		  if (!_bfd_elf_ppc_at_tls_transform (opcode->opcode, 0))
+		    as_bad (_("@tls may not be used with \"%s\" operands"),
+			    opcode->name);
+		  else if (operand->shift != 11)
+		    as_bad (_("@tls may only be used in last operand"));
+		  else
+		    insn = ppc_insert_operand (insn, operand,
+					       ppc_obj64 ? 13 : 2,
+					       ppc_cpu, (char *) NULL, 0);
+		  break;
+
+		  /* We'll only use the 32 (or 64) bit form of these relocations
+		     in constants.  Instructions get the 16 bit form.  */
+		case BFD_RELOC_PPC_DTPREL:
+		  reloc = BFD_RELOC_PPC_DTPREL16;
+		  break;
+
+		case BFD_RELOC_PPC_TPREL:
+		  reloc = BFD_RELOC_PPC_TPREL16;
+		  break;
+
+		case BFD_RELOC_LO16:
+		  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)
+		    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))
+		    /* addpcis.  */
+		    reloc = BFD_RELOC_PPC_16DX_HA;
+		  else if (operand->bitm != 0xffff
+			   || operand->shift != 0
+			   || (operand->flags & PPC_OPERAND_NEGATIVE) != 0)
+		    as_warn (_("%s unsupported on this instruction"), "@ha");
+		}
 	    }
 #endif /* OBJ_ELF */
 
diff --git a/include/ChangeLog b/include/ChangeLog
index b3fd56b..3b7c66d 100644
--- a/include/ChangeLog
+++ b/include/ChangeLog
@@ -1,3 +1,7 @@
+2018-12-06  Alan Modra  <amodra@gmail.com>
+
+	* opcode/ppc.h (E_OPCODE_MASK, E_LI_MASK, E_LI_INSN): Define.
+
 2018-12-06  Andrew Burgess  <andrew.burgess@embecosm.com>
 
 	* dis-asm.h (riscv_symbol_is_valid): Declare.
diff --git a/include/opcode/ppc.h b/include/opcode/ppc.h
index 2b7f51e..c7262f1 100644
--- a/include/opcode/ppc.h
+++ b/include/opcode/ppc.h
@@ -476,6 +476,8 @@
 }
 
 /* PowerPC VLE insns.  */
+#define E_OPCODE_MASK		0xfc00f800
+
 /* Form I16L, uses 16A relocs.  */
 #define E_OR2I_INSN		0x7000C000
 #define E_AND2I_DOT_INSN	0x7000C800
@@ -492,6 +494,9 @@
 #define E_CMPH16I_INSN		0x7000B000
 #define E_CMPHL16I_INSN		0x7000B800
 
+#define E_LI_INSN		0x70000000
+#define E_LI_MASK		0xfc008000
+
 #ifdef __cplusplus
 }
 #endif