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
