Re-send to include updated patch files to gcc-patches list.

---------- Forwarded message ---------
From: Migraine Man <[email protected]>
Date: Thu, Jul 9, 2026 at 5:26 PM
Subject: [PATCH 0/3] pdp11: fix three shift-expander wrong-code bugs
To: <[email protected]>
Cc: Paul Koning <[email protected]>


Paul,

This series fixes three independent wrong-code bugs in the pdp11
shift expanders in config/pdp11/pdp11.md, found while bringing up
Fuzix on a PDP-11/20-compatible FPGA system built with gcc 13.3.0
as the cross-compiler.  All three are still present in master.

1/3 - PR target/125058: the QImode branch of lshr<mode>3 does not
negate the shift count, so a logical right shift is emitted as a
LEFT shift.

2/3 - PR target/126190: the HImode/SImode variable-count branch of
lshr<mode>3 expands as a one-bit logical shift followed by an
arithmetic shift of (count - 1), which is invalid for a runtime
count of 0: it computes x & ~1.  In the field this corrupted every
odd single-indirect block of every Fuzix file read (bn >> sh with
sh = 0).

3/3 - PR target/126191: the QImode branch of ashr<mode>3
zero-extends the operand before the arithmetic shift, losing the
sign for counts 4..7 (smaller counts take the asrb path).  The fix
(sign-extend) also makes the emitted code two words shorter.

Each patch adds a scan-assembler test under gcc.target/pdp11.
The combined fixes have been running in a self-hosted gcc 13.3.0
cross toolchain building the Fuzix kernel, libc, and userland;
codegen verified case by case on the PRs.

Best regards,
James McGuire
From 5f567c62f2798c334e25a0f0e51bd5c0fb0d0c90 Mon Sep 17 00:00:00 2001
From: James McGuire <[email protected]>
Date: Thu, 9 Jul 2026 16:40:47 -0400
Subject: [PATCH 1/3] pdp11: negate the count in QImode logical right shift
 [PR125058]

The QImode branch of the lshr<mode>3 expander zero-extends the
operand to HImode and emits aslhi_op, but passes the shift count
through un-negated, so the "right" shift is emitted as a LEFT
shift by the count.  Negate the count as the HImode/SImode paths
do.

gcc/ChangeLog:

	PR target/125058
	* config/pdp11/pdp11.md (lshr<mode>3): Negate the shift count
	in the QImode branch.

gcc/testsuite/ChangeLog:

	PR target/125058
	* gcc.target/pdp11/lshrqi-var.c: New test.

Signed-off-by: James McGuire <[email protected]>
---
 gcc/config/pdp11/pdp11.md                   |  1 +
 gcc/testsuite/gcc.target/pdp11/lshrqi-var.c | 14 ++++++++++++++
 2 files changed, 15 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/pdp11/lshrqi-var.c

diff --git a/gcc/config/pdp11/pdp11.md b/gcc/config/pdp11/pdp11.md
index 9adbe2a..a2cbd35 100644
--- a/gcc/config/pdp11/pdp11.md
+++ b/gcc/config/pdp11/pdp11.md
@@ -1778,6 +1778,7 @@
       if (<QHSint:e_mname> == E_QImode)
         {
           r = copy_to_mode_reg (HImode, gen_rtx_ZERO_EXTEND (HImode, operands[1]));
+          operands[2] = negate_rtx (HImode, operands[2]);
           emit_insn (gen_aslhi_op (r, r, operands[2]));
           emit_insn (gen_movqi (operands[0], gen_rtx_SUBREG (QImode, r, 0)));
         }
diff --git a/gcc/testsuite/gcc.target/pdp11/lshrqi-var.c b/gcc/testsuite/gcc.target/pdp11/lshrqi-var.c
new file mode 100644
index 0000000..0904648
--- /dev/null
+++ b/gcc/testsuite/gcc.target/pdp11/lshrqi-var.c
@@ -0,0 +1,14 @@
+/* PR target/125058: QImode logical right shift by a variable or
+   non-small constant count shifted LEFT (count was not negated).  */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+unsigned char
+uq4 (unsigned char x)
+{
+  return x >> 4;
+}
+
+/* The shift count must be negated: right shift, not left.  */
+/* { dg-final { scan-assembler "ash\t\\\$-04" } } */
+/* { dg-final { scan-assembler-not "ash\t\\\$04" } } */
-- 
2.43.0

From 54bd2aecb5c95d0ac63a3067dea914b7534c9d04 Mon Sep 17 00:00:00 2001
From: James McGuire <[email protected]>
Date: Thu, 9 Jul 2026 16:41:48 -0400
Subject: [PATCH 3/3] pdp11: sign-extend the operand in QImode arithmetic right
 shift [PR126191]

The QImode branch of the ashr<mode>3 expander ZERO-extends the
operand to HImode before the arithmetic shift, parking the QImode
sign bit at bit 7 of a positive HImode value, so the shift pulls in
zeros and the sign is lost: (signed char)-16 >> 4 returns 15 instead
of -1.  Sign-extend instead; the HImode arithmetic shift is then
exact for all counts.  The emitted code is also two words shorter
(movb in place of clr + bisb).

Reached for constant counts 4..7: smaller constants are handled by
the asrb-chain path, and variable counts currently stay in the
promoted HImode form, but the expander must be correct for any
operands it accepts.

gcc/ChangeLog:

	PR target/126191
	* config/pdp11/pdp11.md (ashr<mode>3): Sign-extend rather than
	zero-extend the operand in the QImode branch.

gcc/testsuite/ChangeLog:

	PR target/126191
	* gcc.target/pdp11/ashrqi-sign.c: New test.

Signed-off-by: James McGuire <[email protected]>
---
 gcc/config/pdp11/pdp11.md                    |  2 +-
 gcc/testsuite/gcc.target/pdp11/ashrqi-sign.c | 16 ++++++++++++++++
 2 files changed, 17 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gcc.target/pdp11/ashrqi-sign.c

diff --git a/gcc/config/pdp11/pdp11.md b/gcc/config/pdp11/pdp11.md
index b35b96b..4f3fd3e 100644
--- a/gcc/config/pdp11/pdp11.md
+++ b/gcc/config/pdp11/pdp11.md
@@ -1752,7 +1752,7 @@
       operands[2] = negate_rtx (HImode, operands[2]);
       if (<QHSint:e_mname> == E_QImode)
         {
-          r = copy_to_mode_reg (HImode, gen_rtx_ZERO_EXTEND (HImode, operands[1]));
+          r = copy_to_mode_reg (HImode, gen_rtx_SIGN_EXTEND (HImode, operands[1]));
           emit_insn (gen_aslhi_op (r, r, operands[2]));
           emit_insn (gen_movqi (operands[0], gen_rtx_SUBREG (QImode, r, 0)));
         }
diff --git a/gcc/testsuite/gcc.target/pdp11/ashrqi-sign.c b/gcc/testsuite/gcc.target/pdp11/ashrqi-sign.c
new file mode 100644
index 0000000..7e09207
--- /dev/null
+++ b/gcc/testsuite/gcc.target/pdp11/ashrqi-sign.c
@@ -0,0 +1,16 @@
+/* PR target/126191: QImode arithmetic right shift zero-extended the
+   operand, losing the sign: q4 (-16) returned 15 instead of -1.
+   Triggers for constant counts 4..7 (1..3 use an asrb chain).  */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+signed char
+q4 (signed char x)
+{
+  return x >> 4;
+}
+
+/* The operand must be sign-extended (movb), not zero-extended
+   (clr + bisb).  */
+/* { dg-final { scan-assembler "movb" } } */
+/* { dg-final { scan-assembler-not "bisb" } } */
-- 
2.43.0

From f1fc8ab33c23e32a91a21cb2fc0381ccfaf7338c Mon Sep 17 00:00:00 2001
From: James McGuire <[email protected]>
Date: Thu, 9 Jul 2026 16:41:29 -0400
Subject: [PATCH v2] pdp11: guard variable logical right shift against a zero
 count [PR126190]

The non-constant HImode/SImode branch of lshr<mode>3 expands
x >> n as a one-bit logical shift followed by an arithmetic shift
of (n - 1).  That identity only holds for n >= 1: for a runtime
count of zero it computes x & ~1.  In the field this corrupted
every odd-numbered single-indirect block of every file read on a
Fuzix PDP-11 port (bn >> sh with sh = 0 in bmap()).

Decrement the count first and branch around the shift pair if it
went negative; compare elimination reuses the condition codes the
decrement sets, so the guard costs a single branch and no compare
instruction (suggested by Paul Koning).

gcc/ChangeLog:

	PR target/126190
	* config/pdp11/pdp11.md (lshr<mode>3): Guard the variable
	count expansion against a runtime count of zero.

gcc/testsuite/ChangeLog:

	PR target/126190
	* gcc.target/pdp11/lshr-zero.c: New test.

Signed-off-by: James McGuire <[email protected]>
---
 gcc/config/pdp11/pdp11.md                  | 28 +++++++++++++++++-----
 gcc/testsuite/gcc.target/pdp11/lshr-zero.c | 22 +++++++++++++++++
 2 files changed, 44 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/pdp11/lshr-zero.c

diff --git a/gcc/config/pdp11/pdp11.md b/gcc/config/pdp11/pdp11.md
index a2cbd35..d19c6bd 100644
--- a/gcc/config/pdp11/pdp11.md
+++ b/gcc/config/pdp11/pdp11.md
@@ -1784,17 +1784,33 @@
         }
       else
         {
-          r = gen_reg_rtx (<QHSint:mname>);
-          emit_insn (gen_lshiftrt<mode>_sc (r, operands[1], const1_rtx));
           if (GET_CODE (operands[2]) != CONST_INT)
             {
-              n = gen_reg_rtx (HImode);
-              emit_insn (gen_addhi3 (n, operands [2], GEN_INT (-1)));
-              emit_insn (gen_ashr<mode>3 (operands[0], r, n));
+              /* The one-bit logical shift followed by an arithmetic
+                 shift of (count - 1) is only valid for count >= 1;
+                 a runtime count of 0 must leave the value unchanged.
+                 Decrement first and skip the shift pair if the
+                 result went negative - compare elimination then
+                 reuses the condition codes the decrement set, so no
+                 separate compare is emitted.  */
+              rtx skip = gen_label_rtx ();
+              r = copy_to_mode_reg (<QHSint:mname>, operands[1]);
+              n = copy_to_mode_reg (HImode, operands[2]);
+              emit_insn (gen_addhi3 (n, n, GEN_INT (-1)));
+              emit_cmp_and_jump_insns (n, const0_rtx, LT, NULL_RTX,
+                                       HImode, 0, skip);
+              emit_insn (gen_lshiftrt<mode>_sc (r, r, const1_rtx));
+              emit_insn (gen_ashr<mode>3 (r, r, n));
+              emit_label (skip);
+              emit_move_insn (operands[0], r);
             }
           else
-            emit_insn (gen_asl<QHSint:hmode>_op (operands[0], r,
+            {
+              r = gen_reg_rtx (<QHSint:mname>);
+              emit_insn (gen_lshiftrt<mode>_sc (r, operands[1], const1_rtx));
+              emit_insn (gen_asl<QHSint:hmode>_op (operands[0], r,
 				  GEN_INT (1 - INTVAL (operands[2]))));
+            }
         }
     }
   DONE;
diff --git a/gcc/testsuite/gcc.target/pdp11/lshr-zero.c b/gcc/testsuite/gcc.target/pdp11/lshr-zero.c
new file mode 100644
index 0000000..668744a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/pdp11/lshr-zero.c
@@ -0,0 +1,22 @@
+/* PR target/126190: HImode/SImode logical right shift by a variable
+   count computed (x >> 1) << 1 = x & ~1 for a runtime count of 0.  */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+unsigned int
+hshift (unsigned int x, int n)
+{
+  return x >> n;   /* hshift (19, 0) must return 19, not 18.  */
+}
+
+unsigned long
+sshift (unsigned long x, int n)
+{
+  return x >> n;
+}
+
+/* Both functions guard the shift pair by decrementing the count
+   and branching on the decrement's own condition codes - no
+   separate compare instruction should be emitted.  */
+/* { dg-final { scan-assembler-times "bmi" 2 } } */
+/* { dg-final { scan-assembler-not "cmp" } } */
-- 
2.43.0

Reply via email to