bpf: gas: consolidate handling of immediate overflows

This commit changes the BPF GAS port in order to handle immediate
overflows the same way than the clang BPF assembler:

- For an immediate field of N bits, any written number (positive or
  negative) whose two's complement encoding fit in N its is accepted.
  This means that -2 is the same than 0xffffffe.  It is up to the
  instructions to decide how to interpret the encoded value.

- Immediate fields in jump instructions are no longer relaxed.
  Relaxing to jump instructions with wider range is only performed
  when expressions are involved.

- The manual is updated to document this, and testsuite adapted
  accordingly.

Tested in x86_64-linux-gnu host, bpf-unknown-none target.

gas/ChangeLog:

2023-08-17  Jose E. Marchesi  <jose.marchesi@oracle.com>

	* config/tc-bpf.c (check_immediate_overflow): New function.
	(encode_insn): Use check_immediate_overflow.
	(md_assemble): Do not relax instructions with
	constant disp16 fields.
	* doc/c-bpf.texi (BPF Instructions): Add note about how numerical
	literal values are interpreted for instruction immediate operands.
	* testsuite/gas/bpf/disp16-overflow.s: Adapt accordingly.
	* testsuite/gas/bpf/jump-relax-jump.s: Likewise.
	* testsuite/gas/bpf/jump-relax-jump.d: Likewise.
	* testsuite/gas/bpf/jump-relax-jump-be.d: Likewise.
	* testsuite/gas/bpf/jump-relax-ja.s: Likewise.
	* testsuite/gas/bpf/jump-relax-ja.d: Likewise.
	* testsuite/gas/bpf/jump-relax-ja-be.d: Likewise.
	* testsuite/gas/bpf/disp16-overflow-relax.l: Likewise.
	* testsuite/gas/bpf/imm32-overflow.s: Likewise.
	* testsuite/gas/bpf/disp32-overflow.s: Likewise.
	* testsuite/gas/bpf/disp16-overflow.l: Likewise.
	* testsuite/gas/bpf/disp32-overflow.l: Likewise.
	* testsuite/gas/bpf/imm32-overflow.l: Likewise.
	* testsuite/gas/bpf/offset16-overflow.l: Likewise.
diff --git a/gas/ChangeLog b/gas/ChangeLog
index 26d7dc1..3339c4c 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,3 +1,26 @@
+2023-08-17  Jose E. Marchesi  <jose.marchesi@oracle.com>
+
+	* config/tc-bpf.c (check_immediate_overflow): New function.
+	(encode_insn): Use check_immediate_overflow.
+	(md_assemble): Do not relax instructions with
+	constant disp16 fields.
+	* doc/c-bpf.texi (BPF Instructions): Add note about how numerical
+	literal values are interpreted for instruction immediate operands.
+	* testsuite/gas/bpf/disp16-overflow.s: Adapt accordingly.
+	* testsuite/gas/bpf/jump-relax-jump.s: Likewise.
+	* testsuite/gas/bpf/jump-relax-jump.d: Likewise.
+	* testsuite/gas/bpf/jump-relax-jump-be.d: Likewise.
+	* testsuite/gas/bpf/jump-relax-ja.s: Likewise.
+	* testsuite/gas/bpf/jump-relax-ja.d: Likewise.
+	* testsuite/gas/bpf/jump-relax-ja-be.d: Likewise.
+	* testsuite/gas/bpf/disp16-overflow-relax.l: Likewise.
+	* testsuite/gas/bpf/imm32-overflow.s: Likewise.
+	* testsuite/gas/bpf/disp32-overflow.s: Likewise.
+	* testsuite/gas/bpf/disp16-overflow.l: Likewise.
+	* testsuite/gas/bpf/disp32-overflow.l: Likewise.
+	* testsuite/gas/bpf/imm32-overflow.l: Likewise.
+	* testsuite/gas/bpf/offset16-overflow.l: Likewise.
+
 2023-07-30  Jose E. Marchesi  <jose.marchesi@oracle.com>
 
 	* config/tc-bpf.h (elf_tc_final_processing): Define.
diff --git a/gas/config/tc-bpf.c b/gas/config/tc-bpf.c
index b4566d8..cdde05c 100644
--- a/gas/config/tc-bpf.c
+++ b/gas/config/tc-bpf.c
@@ -286,6 +286,26 @@
   return (value < -lim || value >= lim);
 }
 
+/* Return non-zero if the two's complement encoding of VALUE would
+   overflow an immediate field of width BITS bits.  */
+
+static bool
+immediate_overflow (int64_t value, unsigned bits)
+{
+  if (value < 0)
+    return signed_overflow (value, bits);
+  else
+    {
+      valueT lim;
+
+      if (bits >= sizeof (valueT) * 8)
+        return false;
+
+      lim = (valueT) 1 << bits;
+      return ((valueT) value >= lim);
+    }
+}
+
 
 /* Functions concerning relocs.  */
 
@@ -379,7 +399,7 @@
 #define RELAX_BRANCH_UNCOND(i) (((i) & 1) != 0)
 
 
-/* Compute the length of a branch seuqence, and adjust the stored
+/* Compute the length of a branch sequence, and adjust the stored
    length accordingly.  If FRAG is NULL, the worst-case length is
    returned.  */
 
@@ -854,7 +874,8 @@
    immediates are encoded as zeroes.  */
 
 static void
-encode_insn (struct bpf_insn *insn, char *bytes, int relaxed)
+encode_insn (struct bpf_insn *insn, char *bytes,
+             int relaxed ATTRIBUTE_UNUSED)
 {
   uint8_t src, dst;
 
@@ -911,8 +932,8 @@
     {
       int64_t imm = insn->imm32.X_add_number;
 
-      if (signed_overflow (imm, 32))
-        as_bad (_("signed immediate out of range, shall fit in 32 bits"));
+      if (immediate_overflow (imm, 32))
+        as_bad (_("immediate out of range, shall fit in 32 bits"));
       else
         encode_int32 (insn->imm32.X_add_number, bytes + 4);        
     }
@@ -921,8 +942,8 @@
     {
       int64_t disp = insn->disp32.X_add_number;
 
-      if (signed_overflow (disp, 32))
-        as_bad (_("signed pc-relative offset out of range, shall fit in 32 bits"));
+      if (immediate_overflow (disp, 32))
+        as_bad (_("pc-relative offset out of range, shall fit in 32 bits"));
       else
         encode_int32 (insn->disp32.X_add_number, bytes + 4);
     }
@@ -931,8 +952,8 @@
     {
       int64_t offset = insn->offset16.X_add_number;
 
-      if (signed_overflow (offset, 16))
-        as_bad (_("signed pc-relative offset out of range, shall fit in 16 bits"));
+      if (immediate_overflow (offset, 16))
+        as_bad (_("pc-relative offset out of range, shall fit in 16 bits"));
       else
         encode_int16 (insn->offset16.X_add_number, bytes + 2);
     }
@@ -941,8 +962,8 @@
     {
       int64_t disp = insn->disp16.X_add_number;
 
-      if (!relaxed && signed_overflow (disp, 16))
-        as_bad (_("signed pc-relative offset out of range, shall fit in 16 bits"));
+      if (immediate_overflow (disp, 16))
+        as_bad (_("pc-relative offset out of range, shall fit in 16 bits"));
       else
         encode_int16 (insn->disp16.X_add_number, bytes + 2);
     }
@@ -1517,7 +1538,7 @@
                       break;
                     }
                   insn.has_disp16 = 1;
-                  insn.is_relaxable = 1;
+                  insn.is_relaxable = (insn.disp16.X_op != O_constant);
                   p += 4;
                 }
               else if (strncmp (p, "%d32", 4) == 0)
diff --git a/gas/config/tc-sparc.c b/gas/config/tc-sparc.c
index c273bd7..8c23b65 100644
--- a/gas/config/tc-sparc.c
+++ b/gas/config/tc-sparc.c
@@ -844,7 +844,7 @@
 {
   { "hix",		BFD_RELOC_SPARC_HIX22,		F_POP_V9 },
   { "lox",		BFD_RELOC_SPARC_LOX10, 		F_POP_V9 },
-  { "hi",		BFD_RELOC_HI22,			F_POP_PCREL },
+  { "hi"5,		BFD_RELOC_HI22,			F_POP_PCREL },
   { "lo",		BFD_RELOC_LO10,			F_POP_PCREL },
   { "pc22",		BFD_RELOC_SPARC_PC22,		F_POP_PCREL },
   { "pc10",		BFD_RELOC_SPARC_PC10,		F_POP_PCREL },
diff --git a/gas/doc/c-bpf.texi b/gas/doc/c-bpf.texi
index 6b43c77..75372de 100644
--- a/gas/doc/c-bpf.texi
+++ b/gas/doc/c-bpf.texi
@@ -172,6 +172,14 @@
 Signed 64-bit immediate.
 @end table
 
+@noindent
+Note that the assembler allows to express the value for an immediate
+using any numerical literal whose two's complement encoding fits in
+the immediate field.  For example, @code{-2}, @code{0xfffffffe} and
+@code{4294967294} all denote the same encoded 32-bit immediate, whose
+value may be then interpreted by different instructions as either as a
+negative or a positive number.
+
 @subsection Arithmetic instructions
 
 The destination register in these instructions act like an
diff --git a/gas/testsuite/gas/bpf/disp16-overflow-relax.l b/gas/testsuite/gas/bpf/disp16-overflow-relax.l
index ca572cb..935a8ab 100644
--- a/gas/testsuite/gas/bpf/disp16-overflow-relax.l
+++ b/gas/testsuite/gas/bpf/disp16-overflow-relax.l
@@ -1,3 +1,5 @@
 .*: Assembler messages:
-.*:2: Error: signed instruction operand out of range, shall fit in 32 bits
-.*:4: Error: signed instruction operand out of range, shall fit in 32 bits
+.*:1: Error: pc-relative offset out of range, shall fit in 16 bits
+.*:2: Error: pc-relative offset out of range, shall fit in 16 bits
+.*:3: Error: pc-relative offset out of range, shall fit in 16 bits
+.*:4: Error: pc-relative offset out of range, shall fit in 16 bits
diff --git a/gas/testsuite/gas/bpf/disp16-overflow-relax.s b/gas/testsuite/gas/bpf/disp16-overflow-relax.s
index 3953992..05d505c 100644
--- a/gas/testsuite/gas/bpf/disp16-overflow-relax.s
+++ b/gas/testsuite/gas/bpf/disp16-overflow-relax.s
@@ -1,4 +1,4 @@
-        jeq %r1,%r2,2147483647
+        jeq %r1,%r2,2147483647  ; Overflows.
         jlt %r3,%r4,2147483648  ; Overflows.
-        jge %r5,10,-2147483648
+        jge %r5,10,-2147483648  ; Overflows.
         ja -2147483649          ; Overflows.
diff --git a/gas/testsuite/gas/bpf/disp16-overflow.l b/gas/testsuite/gas/bpf/disp16-overflow.l
index 6404b1b..1fd0f94 100644
--- a/gas/testsuite/gas/bpf/disp16-overflow.l
+++ b/gas/testsuite/gas/bpf/disp16-overflow.l
@@ -1,3 +1,3 @@
 .*: Assembler messages:
-.*:2: Error: signed pc-relative offset out of range, shall fit in 16 bits
-.*:4: Error: signed pc-relative offset out of range, shall fit in 16 bits
+.*:2: Error: pc-relative offset out of range, shall fit in 16 bits
+.*:4: Error: pc-relative offset out of range, shall fit in 16 bits
diff --git a/gas/testsuite/gas/bpf/disp16-overflow.s b/gas/testsuite/gas/bpf/disp16-overflow.s
index ab66753..4a8fd9f 100644
--- a/gas/testsuite/gas/bpf/disp16-overflow.s
+++ b/gas/testsuite/gas/bpf/disp16-overflow.s
@@ -1,4 +1,4 @@
         ja 32767
-        jeq %r1,%r2,32768       ; Overflows
+        jeq %r1,%r2,65536       ; Overflows
         jlt %r3,%r4,-32768
         jge %r5,10,-32769       ; Overflows
diff --git a/gas/testsuite/gas/bpf/disp32-overflow.l b/gas/testsuite/gas/bpf/disp32-overflow.l
index 8a6b647..40a20dd 100644
--- a/gas/testsuite/gas/bpf/disp32-overflow.l
+++ b/gas/testsuite/gas/bpf/disp32-overflow.l
@@ -1,3 +1,3 @@
 .*: Assembler messages:
-.*:2: Error: signed pc-relative offset out of range, shall fit in 32 bits
-.*:4: Error: signed pc-relative offset out of range, shall fit in 32 bits
+.*:2: Error: pc-relative offset out of range, shall fit in 32 bits
+.*:4: Error: pc-relative offset out of range, shall fit in 32 bits
diff --git a/gas/testsuite/gas/bpf/disp32-overflow.s b/gas/testsuite/gas/bpf/disp32-overflow.s
index 03a0d97..11128a2 100644
--- a/gas/testsuite/gas/bpf/disp32-overflow.s
+++ b/gas/testsuite/gas/bpf/disp32-overflow.s
@@ -1,4 +1,4 @@
         call -2147483648
         call -2147483649        ; This overflows.
-        call 2147483647
-        call 2147483648         ; This overflows.
+        call 4294967295
+        call 4294967296         ; This overflows.
diff --git a/gas/testsuite/gas/bpf/imm32-overflow.l b/gas/testsuite/gas/bpf/imm32-overflow.l
index f6691c4..4e5ac5a 100644
--- a/gas/testsuite/gas/bpf/imm32-overflow.l
+++ b/gas/testsuite/gas/bpf/imm32-overflow.l
@@ -1,3 +1,3 @@
 .*: Assembler messages:
-.*:2: Error: signed immediate out of range, shall fit in 32 bits
-.*:4: Error: signed immediate out of range, shall fit in 32 bits
+.*:2: Error: immediate out of range, shall fit in 32 bits
+.*:4: Error: immediate out of range, shall fit in 32 bits
diff --git a/gas/testsuite/gas/bpf/imm32-overflow.s b/gas/testsuite/gas/bpf/imm32-overflow.s
index 5cb858c..b2ab43d 100644
--- a/gas/testsuite/gas/bpf/imm32-overflow.s
+++ b/gas/testsuite/gas/bpf/imm32-overflow.s
@@ -1,4 +1,4 @@
         add %r1, 2147483647
-        or %r2, 2147483648         ; This overflows.
-        xor %r3, -2147483648
-        sub %r4, -2147483649       ; This overflows.
+        or %r2, 4294967296         ; This overflows.
+        xor %r3, 4294967295
+        sub %r4, 4294967296        ; This overflows.
diff --git a/gas/testsuite/gas/bpf/jump-relax-ja-be.d b/gas/testsuite/gas/bpf/jump-relax-ja-be.d
index 08b85a9..5f07847 100644
--- a/gas/testsuite/gas/bpf/jump-relax-ja-be.d
+++ b/gas/testsuite/gas/bpf/jump-relax-ja-be.d
@@ -13,7 +13,5 @@
   10:	05 00 ff fd 00 00 00 00 	ja -3
   18:	05 00 00 00 00 00 00 00 	ja 0
 			18: R_BPF_GNU_64_16	undefined
-  20:	06 00 00 00 ff ff 7f ff 	jal -32769
-  28:	06 00 00 00 00 00 80 00 	jal 32768
-  30:	06 00 00 00 00 00 80 01 	jal 32769
-  38:	06 00 00 00 00 00 80 01 	jal 32769
+  20:	06 00 00 00 00 00 80 01 	jal 32769
+  28:	06 00 00 00 00 00 80 01 	jal 32769
diff --git a/gas/testsuite/gas/bpf/jump-relax-ja.d b/gas/testsuite/gas/bpf/jump-relax-ja.d
index 6b50973..ed3aa6b 100644
--- a/gas/testsuite/gas/bpf/jump-relax-ja.d
+++ b/gas/testsuite/gas/bpf/jump-relax-ja.d
@@ -13,7 +13,5 @@
   10:	05 00 fd ff 00 00 00 00 	ja -3
   18:	05 00 00 00 00 00 00 00 	ja 0
 			18: R_BPF_GNU_64_16	undefined
-  20:	06 00 00 00 ff 7f ff ff 	jal -32769
-  28:	06 00 00 00 00 80 00 00 	jal 32768
-  30:	06 00 00 00 01 80 00 00 	jal 32769
-  38:	06 00 00 00 01 80 00 00 	jal 32769
+  20:	06 00 00 00 01 80 00 00 	jal 32769
+  28:	06 00 00 00 01 80 00 00 	jal 32769
diff --git a/gas/testsuite/gas/bpf/jump-relax-ja.s b/gas/testsuite/gas/bpf/jump-relax-ja.s
index 8be3d7a..1faf679 100644
--- a/gas/testsuite/gas/bpf/jump-relax-ja.s
+++ b/gas/testsuite/gas/bpf/jump-relax-ja.s
@@ -9,10 +9,6 @@
         ;; The following instruction has an undefined symbol as a
         ;; target.  It is not to be relaxed.
         ja undefined + 10
-        ;; The following instructions are relaxed to JAL instructions
-        ;; so they can fit their displacements.
-        ja -32769
-        ja 32768
         ;; The following instructions refer to a defined symbol that
         ;; is not on reach.  They shall be relaxed to a JAL.
         ja tail
diff --git a/gas/testsuite/gas/bpf/jump-relax-jump-be.d b/gas/testsuite/gas/bpf/jump-relax-jump-be.d
index 5626d56..0cacdb3 100644
--- a/gas/testsuite/gas/bpf/jump-relax-jump-be.d
+++ b/gas/testsuite/gas/bpf/jump-relax-jump-be.d
@@ -11,15 +11,9 @@
    0:	1d 12 80 00 00 00 00 00 	jeq %r1,%r2,-32768
    8:	ad 12 7f ff 00 00 00 00 	jlt %r1,%r2,32767
   10:	bd 12 ff fd 00 00 00 00 	jle %r1,%r2,-3
-  18:	3d 12 00 01 00 00 00 00 	jge %r1,%r2,1
+  18:	1d 12 00 01 00 00 00 00 	jeq %r1,%r2,1
   20:	05 00 00 01 00 00 00 00 	ja 1
-  28:	06 00 00 00 ff ff 7f ff 	jal -32769
+  28:	06 00 00 00 00 00 80 01 	jal 32769
   30:	2d 12 00 01 00 00 00 00 	jgt %r1,%r2,1
   38:	05 00 00 01 00 00 00 00 	ja 1
-  40:	06 00 00 00 00 00 80 00 	jal 32768
-  48:	1d 12 00 01 00 00 00 00 	jeq %r1,%r2,1
-  50:	05 00 00 01 00 00 00 00 	ja 1
-  58:	06 00 00 00 00 00 80 01 	jal 32769
-  60:	2d 12 00 01 00 00 00 00 	jgt %r1,%r2,1
-  68:	05 00 00 01 00 00 00 00 	ja 1
-  70:	06 00 00 00 00 00 80 01 	jal 32769
+  40:	06 00 00 00 00 00 80 01 	jal 32769
diff --git a/gas/testsuite/gas/bpf/jump-relax-jump.d b/gas/testsuite/gas/bpf/jump-relax-jump.d
index cd46ea7..dd31ba5 100644
--- a/gas/testsuite/gas/bpf/jump-relax-jump.d
+++ b/gas/testsuite/gas/bpf/jump-relax-jump.d
@@ -11,15 +11,9 @@
    0:	1d 21 00 80 00 00 00 00 	jeq %r1,%r2,-32768
    8:	ad 21 ff 7f 00 00 00 00 	jlt %r1,%r2,32767
   10:	bd 21 fd ff 00 00 00 00 	jle %r1,%r2,-3
-  18:	3d 21 01 00 00 00 00 00 	jge %r1,%r2,1
+  18:	1d 21 01 00 00 00 00 00 	jeq %r1,%r2,1
   20:	05 00 01 00 00 00 00 00 	ja 1
-  28:	06 00 00 00 ff 7f ff ff 	jal -32769
+  28:	06 00 00 00 01 80 00 00 	jal 32769
   30:	2d 21 01 00 00 00 00 00 	jgt %r1,%r2,1
   38:	05 00 01 00 00 00 00 00 	ja 1
-  40:	06 00 00 00 00 80 00 00 	jal 32768
-  48:	1d 21 01 00 00 00 00 00 	jeq %r1,%r2,1
-  50:	05 00 01 00 00 00 00 00 	ja 1
-  58:	06 00 00 00 01 80 00 00 	jal 32769
-  60:	2d 21 01 00 00 00 00 00 	jgt %r1,%r2,1
-  68:	05 00 01 00 00 00 00 00 	ja 1
-  70:	06 00 00 00 01 80 00 00 	jal 32769
+  40:	06 00 00 00 01 80 00 00 	jal 32769
diff --git a/gas/testsuite/gas/bpf/jump-relax-jump.s b/gas/testsuite/gas/bpf/jump-relax-jump.s
index dabbab8..3ee7c87 100644
--- a/gas/testsuite/gas/bpf/jump-relax-jump.s
+++ b/gas/testsuite/gas/bpf/jump-relax-jump.s
@@ -5,11 +5,6 @@
         ;; The following instruction refers to a defined symbol that
         ;; is on reach, so it should not be relaxed.
         jle %r1, %r2, 1b
-        ;; The following instructions are relaxed to sequences
-        ;; involving unconditional jumps, so they can fi their
-        ;; displacements.
-        jge %r1, %r2, -32769
-        jgt %r1, %r2, 32768
         ;; The following instructions refer to a defined symbol that
         ;; is not on reach.  They shall be relaxed.
         jeq %r1, %r2, tail
diff --git a/gas/testsuite/gas/bpf/offset16-overflow.l b/gas/testsuite/gas/bpf/offset16-overflow.l
index 6404b1b..1fd0f94 100644
--- a/gas/testsuite/gas/bpf/offset16-overflow.l
+++ b/gas/testsuite/gas/bpf/offset16-overflow.l
@@ -1,3 +1,3 @@
 .*: Assembler messages:
-.*:2: Error: signed pc-relative offset out of range, shall fit in 16 bits
-.*:4: Error: signed pc-relative offset out of range, shall fit in 16 bits
+.*:2: Error: pc-relative offset out of range, shall fit in 16 bits
+.*:4: Error: pc-relative offset out of range, shall fit in 16 bits
diff --git a/gas/testsuite/gas/bpf/offset16-overflow.s b/gas/testsuite/gas/bpf/offset16-overflow.s
index da9f633..ebd8e05 100644
--- a/gas/testsuite/gas/bpf/offset16-overflow.s
+++ b/gas/testsuite/gas/bpf/offset16-overflow.s
@@ -1,4 +1,4 @@
-        ldxh %r2, [%r1 + 32767]
-        ldxw %r2, [%r1 + 32768]  ; This overflows
+        ldxh %r2, [%r1 + 65535]
+        ldxw %r2, [%r1 + 65536]  ; This overflows
         stxw [%r2 - 32768], %r1
         stxdw [%r2 - 32769], %r1  ; This overflows