[binutils-gdb] gas: also maintain signed-ness for O_big expressions

Jan Beulich jbeulich@sourceware.org
Fri Jun 13 06:40:31 GMT 2025


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=023b7811d644a8e3d60b802731a8987232ebb1a0

commit 023b7811d644a8e3d60b802731a8987232ebb1a0
Author: Jan Beulich <jbeulich@suse.com>
Date:   Fri Jun 13 08:39:44 2025 +0200

    gas: also maintain signed-ness for O_big expressions
    
    Interestingly emit_leb128_expr() already assumes X_unsigned is properly
    set for O_big. Adjust its conversion-to-bignum to respect the incoming
    flag, and have convert_to_bignum() correctly set it on output.
    
    It further can't be quite right that convert_to_bignum() depends on
    anything other than the incoming expression. Therefore adjust
    emit_expr_with_reloc() to be in line with the other invocation.
    
    This also requires an adjustment for SH, which really should have been
    part of 762acf217c40 ("gas: maintain O_constant signedness in more
    cases").

Diff:
---
 gas/config/tc-sh.c           |  1 +
 gas/expr.c                   |  4 ++++
 gas/expr.h                   | 10 ++++++----
 gas/read.c                   | 11 +++++++----
 gas/testsuite/gas/all/octa.d |  8 ++++++++
 gas/testsuite/gas/all/octa.s | 10 ++++++++++
 6 files changed, 36 insertions(+), 8 deletions(-)

diff --git a/gas/config/tc-sh.c b/gas/config/tc-sh.c
index 9620eef9e93..c37306f2f58 100644
--- a/gas/config/tc-sh.c
+++ b/gas/config/tc-sh.c
@@ -544,6 +544,7 @@ sh_optimize_expr (expressionS *l, operatorT op, expressionS *r)
       add_to_result (l, symval_diff, symval_diff < 0);
       l->X_op = O_constant;
       l->X_add_symbol = 0;
+      l->X_unsigned = 0;
       return 1;
     }
   return 0;
diff --git a/gas/expr.c b/gas/expr.c
index bf58fc3a768..78a247cde09 100644
--- a/gas/expr.c
+++ b/gas/expr.c
@@ -632,6 +632,7 @@ integer_constant (int radix, expressionS *expressionP)
       /* Not a small number.  */
       expressionP->X_op = O_big;
       expressionP->X_add_number = number;	/* Number of littlenums.  */
+      expressionP->X_unsigned = 1;
       input_line_pointer--;	/* -> char following number.  */
     }
 }
@@ -707,6 +708,7 @@ mri_char_constant (expressionS *expressionP)
     {
       expressionP->X_op = O_big;
       expressionP->X_add_number = i;
+      expressionP->X_unsigned = 1;
     }
   else
     {
@@ -1164,6 +1166,8 @@ operand (expressionS *expressionP, enum expr_mode mode)
 		      if (generic_bignum[i])
 			break;
 		    }
+
+		expressionP->X_unsigned = 0;
 	      }
 	    else if (op == O_logical_not)
 	      {
diff --git a/gas/expr.h b/gas/expr.h
index 14787c41343..2be3d69683c 100644
--- a/gas/expr.h
+++ b/gas/expr.h
@@ -131,10 +131,12 @@ typedef struct expressionS
   unsigned char X_op;
 #endif
 
-  /* Non-zero if X_add_number should be regarded as unsigned.  This is
-     only valid for O_constant expressions.  It is only used when an
-     O_constant must be extended into a bignum (i.e., it is not used
-     when performing arithmetic on these values).
+  /* Non-zero if the expression value should be regarded as unsigned.  This is
+     only valid for
+     - O_constant expressions, where it is only used when an O_constant must be
+       extended into a bignum (i.e., it is not used when performing arithmetic
+       on these values),
+     - O_big integer expressions, i.e. when X_add_number is positive.
      FIXME: This field is not set very reliably.  */
   unsigned int X_unsigned : 1;
   /* This is used to implement "word size + 1 bit" arithmetic, so that e.g.
diff --git a/gas/read.c b/gas/read.c
index c9cbd224a7d..1b483d683d5 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -1458,6 +1458,7 @@ convert_to_bignum (expressionS *exp, int sign)
     generic_bignum[i++] = sign ? LITTLENUM_MASK : 0;
   exp->X_op = O_big;
   exp->X_add_number = i;
+  exp->X_unsigned = !sign;
 }
 
 /* For most MRI pseudo-ops, the line actually ends at the first
@@ -4651,8 +4652,8 @@ emit_expr_with_reloc (expressionS *exp,
      pass to md_number_to_chars, handle it as a bignum.  */
   if (op == O_constant && nbytes > sizeof (valueT))
     {
-      extra_digit = exp->X_unsigned ? 0 : -1;
-      convert_to_bignum (exp, !exp->X_unsigned);
+      extra_digit = exp->X_unsigned ? 0 : -exp->X_extrabit;
+      convert_to_bignum (exp, -extra_digit);
       op = O_big;
     }
 
@@ -5371,12 +5372,14 @@ emit_leb128_expr (expressionS *exp, int sign)
     }
   else if (op == O_constant
 	   && sign
-	   && (exp->X_add_number < 0) == !exp->X_extrabit)
+	   && (exp->X_unsigned
+	       ? exp->X_add_number < 0
+	       : (exp->X_add_number < 0) != exp->X_extrabit))
     {
       /* We're outputting a signed leb128 and the sign of X_add_number
 	 doesn't reflect the sign of the original value.  Convert EXP
 	 to a correctly-extended bignum instead.  */
-      convert_to_bignum (exp, exp->X_extrabit);
+      convert_to_bignum (exp, !exp->X_unsigned && exp->X_extrabit);
       op = O_big;
     }
 
diff --git a/gas/testsuite/gas/all/octa.d b/gas/testsuite/gas/all/octa.d
index e8eff2ce828..9c9120605d7 100644
--- a/gas/testsuite/gas/all/octa.d
+++ b/gas/testsuite/gas/all/octa.d
@@ -14,3 +14,11 @@ Contents of section .data:
  [^ ]* (00000000 ffffffff ffffffff ffffffff|ffffffff ffffffff ffffffff 00000000) .*
  [^ ]* (00000080 ffffffff ffffffff ffffffff|ffffffff ffffffff ffffffff 80000000) .*
  [^ ]* (01000000 ffffffff ffffffff ffffffff|ffffffff ffffffff ffffffff 00000001) .*
+ [^ ]* (00000000 000000f0 ffffffff ffffffff|ffffffff ffffffff f0000000 00000000) .*
+ [^ ]* (00000000 00000010 00000000 00000000|00000000 00000000 10000000 00000000) .*
+ [^ ]* (ffffffff ffffffef ffffffff ffffffff|ffffffff ffffffff efffffff ffffffff) .*
+ [^ ]* (01000000 00000010 00000000 00000000|00000000 00000000 10000000 00000001) .*
+ [^ ]* (01000000 000000f0 ffffffff ffffffff|ffffffff ffffffff f0000000 00000001) .*
+ [^ ]* (feffffff ffffff0f 00000000 00000000|00000000 00000000 0fffffff fffffffe) .*
+ [^ ]* (02000000 000000f0 ffffffff ffffffff|ffffffff ffffffff f0000000 00000002) .*
+ [^ ]* (fdffffff ffffff0f 00000000 00000000|00000000 00000000 0fffffff fffffffd) .*
diff --git a/gas/testsuite/gas/all/octa.s b/gas/testsuite/gas/all/octa.s
index faa21fa7ba0..037941f2e6d 100644
--- a/gas/testsuite/gas/all/octa.s
+++ b/gas/testsuite/gas/all/octa.s
@@ -9,3 +9,13 @@
  .octa ~0xffffffff
  .octa 0 - 0x80000000
  .octa 0 - 0xffffffff
+
+ .octa ~0xfffffffffffffff
+ .octa -~0xfffffffffffffff
+ .octa ~-~0xfffffffffffffff
+ .octa -~-~0xfffffffffffffff
+
+ .octa -0xfffffffffffffff
+ .octa ~-0xfffffffffffffff
+ .octa -~-0xfffffffffffffff
+ .octa ~-~-0xfffffffffffffff


More information about the Binutils-cvs mailing list