Power10 bignum operands

Message ID 20210412022626.GB5425@bubble.grove.modra.org
State New
Headers show
Series
  • Power10 bignum operands
Related show

Commit Message

H.J. Lu via Binutils April 12, 2021, 2:26 a.m.
When built on a 32-bit host without --enable-64-bit-bfd, powerpc-linux
and other 32-bit powerpc targeted binutils fail to assemble some
power10 prefixed instructions with 34-bit fields.  A typical error
seen when running the testsuite is
.../gas/testsuite/gas/ppc/prefix-pcrel.s:10: Error: bignum invalid

In practice this doesn't matter for addresses:  32-bit programs don't
need or use the top 2 bits of a d34 field when calculating addresses.
However it may matter when loading or adding 64-bit constants with
paddi.  A power10 processor in 32-bit mode still has 64-bit wide GPRs.
So this patch enables limited support for O_big PowerPC operands, and
corrects sign extension of 32-bit constants using X_extrabit.

	* config/tc-ppc.c (insn_validate): Use uint64_t for operand values.
	(md_assemble): Likewise.  Handle bignum operands.
	(ppc_elf_suffix): Handle O_big.  Remove unnecessary input_line_pointer
	check.
	* expr.c: Delete unnecessary forward declarations.
	(generic_bignum_to_int32): Return uint32_t.
	(generic_bignum_to_int64): Return uint64_t.  Compile always.
	(operand): Twiddle X_extrabit for unary '~'.  Set X_unsigned and
	clear X_extrabit for unary '!'.
	* expr.h (generic_bignum_to_int32): Declare.
	(generic_bignum_to_int64): Declare.
	* testsuite/gas/ppc/prefix-pcrel.s,
	* testsuite/gas/ppc/prefix-pcrel.d: Add more instructions.


-- 
Alan Modra
Australia Development Lab, IBM

Patch

diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c
index 5511e722108..c719b408b0e 100644
--- a/gas/config/tc-ppc.c
+++ b/gas/config/tc-ppc.c
@@ -1574,7 +1574,7 @@  insn_validate (const struct powerpc_opcode *op)
 	  if (operand->shift == (int) PPC_OPSHIFT_INV)
 	    {
 	      const char *errmsg;
-	      int64_t val;
+	      uint64_t val;
 
 	      errmsg = NULL;
 	      val = -1;
@@ -2197,7 +2197,7 @@  ppc_elf_suffix (char **str_p, expressionS *exp_p)
       {
 	int reloc = ptr->reloc;
 
-	if (!ppc_obj64 && exp_p->X_add_number != 0)
+	if (!ppc_obj64 && (exp_p->X_op == O_big || exp_p->X_add_number != 0))
 	  {
 	    switch (reloc)
 	      {
@@ -2238,14 +2238,12 @@  ppc_elf_suffix (char **str_p, expressionS *exp_p)
 
 	    input_line_pointer = str;
 	    expression (&new_exp);
-	    if (new_exp.X_op == O_constant)
+	    if (new_exp.X_op == O_constant && exp_p->X_op != O_big)
 	      {
 		exp_p->X_add_number += new_exp.X_add_number;
 		str = input_line_pointer;
 	      }
-
-	    if (&input_line_pointer != str_p)
-	      input_line_pointer = orig_line;
+	    input_line_pointer = orig_line;
 	  }
 	*str_p = str;
 
@@ -3397,8 +3395,8 @@  md_assemble (char *str)
 	    }
 	  if (--num_optional_provided < 0)
 	    {
-	      int64_t val = ppc_optional_operand_value (operand, insn, ppc_cpu,
-							num_optional_provided);
+	      uint64_t val = ppc_optional_operand_value (operand, insn, ppc_cpu,
+							 num_optional_provided);
 	      if (operand->insert)
 		{
 		  insn = (*operand->insert) (insn, val, ppc_cpu, &errmsg);
@@ -3459,14 +3457,32 @@  md_assemble (char *str)
 	  insn = ppc_insert_operand (insn, operand, ex.X_add_number,
 				     ppc_cpu, (char *) NULL, 0);
 	}
-      else if (ex.X_op == O_constant)
+      else if (ex.X_op == O_constant
+	       || (ex.X_op == O_big && ex.X_add_number > 0))
 	{
+	  uint64_t val;
+	  if (ex.X_op == O_constant)
+	    {
+	      val = ex.X_add_number;
+	      if (sizeof (ex.X_add_number) < sizeof (val)
+		  && (ex.X_add_number < 0) != ex.X_extrabit)
+		val = val ^ ((addressT) -1 ^ (uint64_t) -1);
+	    }
+	  else
+	    val = generic_bignum_to_int64 ();
 #ifdef OBJ_ELF
 	  /* Allow @HA, @L, @H on constants.  */
-	  bfd_reloc_code_real_type reloc;
 	  char *orig_str = str;
+	  bfd_reloc_code_real_type reloc = ppc_elf_suffix (&str, &ex);
 
-	  if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_NONE)
+	  if (ex.X_op == O_constant)
+	    {
+	      val = ex.X_add_number;
+	      if (sizeof (ex.X_add_number) < sizeof (val)
+		  && (ex.X_add_number < 0) != ex.X_extrabit)
+		val = val ^ ((addressT) -1 ^ (uint64_t) -1);
+	    }
+	  if (reloc != BFD_RELOC_NONE)
 	    switch (reloc)
 	      {
 	      default:
@@ -3474,81 +3490,77 @@  md_assemble (char *str)
 		break;
 
 	      case BFD_RELOC_LO16:
-		ex.X_add_number &= 0xffff;
+		val &= 0xffff;
 		if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
-		  ex.X_add_number = SEX16 (ex.X_add_number);
+		  val = SEX16 (val);
 		break;
 
 	      case BFD_RELOC_HI16:
 		if (REPORT_OVERFLOW_HI && ppc_obj64)
 		  {
 		    /* PowerPC64 @h is tested for overflow.  */
-		    ex.X_add_number = (addressT) ex.X_add_number >> 16;
+		    val = val >> 16;
 		    if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
 		      {
-			addressT sign = (((addressT) -1 >> 16) + 1) >> 1;
-			ex.X_add_number
-			  = ((addressT) ex.X_add_number ^ sign) - sign;
+			uint64_t sign = (((uint64_t) -1 >> 16) + 1) >> 1;
+			val = (val ^ sign) - sign;
 		      }
 		    break;
 		  }
 		/* Fallthru */
 
 	      case BFD_RELOC_PPC64_ADDR16_HIGH:
-		ex.X_add_number = PPC_HI (ex.X_add_number);
+		val = PPC_HI (val);
 		if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
-		  ex.X_add_number = SEX16 (ex.X_add_number);
+		  val = SEX16 (val);
 		break;
 
 	      case BFD_RELOC_HI16_S:
 		if (REPORT_OVERFLOW_HI && ppc_obj64)
 		  {
 		    /* PowerPC64 @ha is tested for overflow.  */
-		    ex.X_add_number
-		      = ((addressT) ex.X_add_number + 0x8000) >> 16;
+		    val = (val + 0x8000) >> 16;
 		    if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
 		      {
-			addressT sign = (((addressT) -1 >> 16) + 1) >> 1;
-			ex.X_add_number
-			  = ((addressT) ex.X_add_number ^ sign) - sign;
+			uint64_t sign = (((uint64_t) -1 >> 16) + 1) >> 1;
+			val = (val ^ sign) - sign;
 		      }
 		    break;
 		  }
 		/* Fallthru */
 
 	      case BFD_RELOC_PPC64_ADDR16_HIGHA:
-		ex.X_add_number = PPC_HA (ex.X_add_number);
+		val = PPC_HA (val);
 		if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
-		  ex.X_add_number = SEX16 (ex.X_add_number);
+		  val = SEX16 (val);
 		break;
 
 	      case BFD_RELOC_PPC64_HIGHER:
-		ex.X_add_number = PPC_HIGHER (ex.X_add_number);
+		val = PPC_HIGHER (val);
 		if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
-		  ex.X_add_number = SEX16 (ex.X_add_number);
+		  val = SEX16 (val);
 		break;
 
 	      case BFD_RELOC_PPC64_HIGHER_S:
-		ex.X_add_number = PPC_HIGHERA (ex.X_add_number);
+		val = PPC_HIGHERA (val);
 		if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
-		  ex.X_add_number = SEX16 (ex.X_add_number);
+		  val = SEX16 (val);
 		break;
 
 	      case BFD_RELOC_PPC64_HIGHEST:
-		ex.X_add_number = PPC_HIGHEST (ex.X_add_number);
+		val = PPC_HIGHEST (val);
 		if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
-		  ex.X_add_number = SEX16 (ex.X_add_number);
+		  val = SEX16 (val);
 		break;
 
 	      case BFD_RELOC_PPC64_HIGHEST_S:
-		ex.X_add_number = PPC_HIGHESTA (ex.X_add_number);
+		val = PPC_HIGHESTA (val);
 		if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
-		  ex.X_add_number = SEX16 (ex.X_add_number);
+		  val = SEX16 (val);
 		break;
 	      }
 #endif /* OBJ_ELF */
-	  insn = ppc_insert_operand (insn, operand, ex.X_add_number,
-				     ppc_cpu, (char *) NULL, 0);
+	  insn = ppc_insert_operand (insn, operand, val, ppc_cpu, NULL, 0);
 	}
       else
 	{
diff --git a/gas/expr.c b/gas/expr.c
index aabd33c84f9..afd065c23be 100644
--- a/gas/expr.c
+++ b/gas/expr.c
@@ -35,16 +35,7 @@ 
 
 bool literal_prefix_dollar_hex = false;
 
-static void floating_constant (expressionS * expressionP);
-static valueT generic_bignum_to_int32 (void);
-#ifdef BFD64
-static valueT generic_bignum_to_int64 (void);
-#endif
-static void integer_constant (int radix, expressionS * expressionP);
-static void mri_char_constant (expressionS *);
 static void clean_up_expression (expressionS * expressionP);
-static segT operand (expressionS *, enum expr_mode);
-static operatorT operatorf (int *);
 
 /* We keep a mapping of expression symbols to file positions, so that
    we can provide better error messages.  */
@@ -218,31 +209,25 @@  floating_constant (expressionS *expressionP)
   expressionP->X_add_number = -1;
 }
 
-static valueT
+uint32_t
 generic_bignum_to_int32 (void)
 {
-  valueT number =
-    ((((valueT) generic_bignum[1] & LITTLENUM_MASK) << LITTLENUM_NUMBER_OF_BITS)
-     | ((valueT) generic_bignum[0] & LITTLENUM_MASK));
-  number &= 0xffffffff;
-  return number;
+  return ((((uint32_t) generic_bignum[1] & LITTLENUM_MASK)
+	   << LITTLENUM_NUMBER_OF_BITS)
+	  | ((uint32_t) generic_bignum[0] & LITTLENUM_MASK));
 }
 
-#ifdef BFD64
-static valueT
+uint64_t
 generic_bignum_to_int64 (void)
 {
-  valueT number =
-    ((((((((valueT) generic_bignum[3] & LITTLENUM_MASK)
-	  << LITTLENUM_NUMBER_OF_BITS)
-	 | ((valueT) generic_bignum[2] & LITTLENUM_MASK))
-	<< LITTLENUM_NUMBER_OF_BITS)
-       | ((valueT) generic_bignum[1] & LITTLENUM_MASK))
-      << LITTLENUM_NUMBER_OF_BITS)
-     | ((valueT) generic_bignum[0] & LITTLENUM_MASK));
-  return number;
+  return ((((((((uint64_t) generic_bignum[3] & LITTLENUM_MASK)
+	       << LITTLENUM_NUMBER_OF_BITS)
+	      | ((uint64_t) generic_bignum[2] & LITTLENUM_MASK))
+	     << LITTLENUM_NUMBER_OF_BITS)
+	    | ((uint64_t) generic_bignum[1] & LITTLENUM_MASK))
+	   << LITTLENUM_NUMBER_OF_BITS)
+	  | ((uint64_t) generic_bignum[0] & LITTLENUM_MASK));
 }
-#endif
 
 static void
 integer_constant (int radix, expressionS *expressionP)
@@ -1031,9 +1016,16 @@  operand (expressionS *expressionP, enum expr_mode mode)
 		  expressionP->X_extrabit ^= 1;
 	      }
 	    else if (c == '~' || c == '"')
-	      expressionP->X_add_number = ~ expressionP->X_add_number;
+	      {
+		expressionP->X_add_number = ~ expressionP->X_add_number;
+		expressionP->X_extrabit ^= 1;
+	      }
 	    else if (c == '!')
-	      expressionP->X_add_number = ! expressionP->X_add_number;
+	      {
+		expressionP->X_add_number = ! expressionP->X_add_number;
+		expressionP->X_unsigned = 1;
+		expressionP->X_extrabit = 0;
+	      }
 	  }
 	else if (expressionP->X_op == O_big
 		 && expressionP->X_add_number <= 0
diff --git a/gas/expr.h b/gas/expr.h
index 5537039edd6..e8707127362 100644
--- a/gas/expr.h
+++ b/gas/expr.h
@@ -185,6 +185,8 @@  extern void current_location (expressionS *);
 
 extern symbolS *expr_build_uconstant (offsetT);
 extern symbolS *expr_build_dot (void);
+extern uint32_t generic_bignum_to_int32 (void);
+extern uint64_t generic_bignum_to_int64 (void);
 
 int resolve_expression (expressionS *);
 
diff --git a/gas/testsuite/gas/ppc/prefix-pcrel.d b/gas/testsuite/gas/ppc/prefix-pcrel.d
index 0d10424ea54..6807d3b2ba6 100644
--- a/gas/testsuite/gas/ppc/prefix-pcrel.d
+++ b/gas/testsuite/gas/ppc/prefix-pcrel.d
@@ -232,4 +232,22 @@  Disassembly of section \.text:
 .*:	(df eb 00 60|60 00 eb df) 
 .*:	(04 10 00 00|00 00 10 04) 	pstxv   vs63,96	# 3d8
 .*:	(df e0 00 60|60 00 e0 df) 
+.*:	(06 00 7f ff|ff 7f 00 06) 	pli     r1,2147483647
+.*:	(38 20 ff ff|ff ff 20 38) 
+.*:	(06 00 80 00|00 80 00 06) 	pli     r2,2147483648
+.*:	(38 40 00 00|00 00 40 38) 
+.*:	(06 00 ff ff|ff ff 00 06) 	pli     r3,4294967295
+.*:	(38 60 ff ff|ff ff 60 38) 
+.*:	(06 00 00 00|00 00 00 06) 	pli     r4,0
+.*:	(38 80 00 00|00 00 80 38) 
+.*:	(06 03 ff ff|ff ff 03 06) 	pli     r5,-1
+.*:	(38 a0 ff ff|ff ff a0 38) 
+.*:	(06 03 80 00|00 80 03 06) 	pli     r6,-2147483647
+.*:	(38 c0 00 01|01 00 c0 38) 
+.*:	(06 03 80 00|00 80 03 06) 	pli     r7,-2147483648
+.*:	(38 e0 00 00|00 00 e0 38) 
+.*:	(06 03 80 00|00 80 03 06) 	pli     r8,-2147483648
+.*:	(39 00 00 00|00 00 00 39) 
+.*:	(06 03 7f ff|ff 7f 03 06) 	pli     r9,-2147483649
+.*:	(39 20 ff ff|ff ff 20 39) 
 #pass
diff --git a/gas/testsuite/gas/ppc/prefix-pcrel.s b/gas/testsuite/gas/ppc/prefix-pcrel.s
index c3831d8a2c3..32f04b109c2 100644
--- a/gas/testsuite/gas/ppc/prefix-pcrel.s
+++ b/gas/testsuite/gas/ppc/prefix-pcrel.s
@@ -119,3 +119,14 @@  prefix:
 	pstxv	13,96(0),1
 	pstxv	63,96(11),0
 	pstxv	63,96(0),1
+
+# test d34 values of interest when bfd_vma is 32-bit
+	pli	1,0x7fffffff
+	pli	2,0x80000000
+	pli	3,0xffffffff
+	pli	4,0
+	pli	5,-1
+	pli	6,-0x7fffffff
+	pli	7,-0x80000000
+	pli	8,~0x7fffffff
+	pli	9,~0x80000000