https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126191
Bug ID: 126191
Summary: [pdp11] QImode arithmetic right shift by constants
4..7 zero-extends the operand, losing the sign
Product: gcc
Version: 13.3.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: target
Assignee: unassigned at gcc dot gnu.org
Reporter: migraineman33 at gmail dot com
Target Milestone: ---
config/pdp11/pdp11.md, ashr<mode>3 define_expand, QImode branch:
r = copy_to_mode_reg (HImode, gen_rtx_ZERO_EXTEND (HImode, operands[1]));
emit_insn (gen_aslhi_op (r, r, operands[2])); /* count already negated */
emit_insn (gen_movqi (operands[0], gen_rtx_SUBREG (QImode, r, 0)));
The operand is ZERO-extended to HImode before the arithmetic right
shift. That parks the QImode sign bit at bit 7 of a value whose bit
15 is 0, so ash shifts in zeros and the sign is lost. It must be
SIGN_EXTEND — likely copied from the lshr expander directly below,
where zero extension is correct.
Note char is signed by default on this target (DEFAULT_SIGNED_CHAR 1),
so plain-char code is affected too.
Trigger window (verified by inspection of generated code, -Os):
- Constant counts 1..3: correct — pdp11_small_shift() routes them to
an asrb chain, a true byte arithmetic shift.
- Constant counts 4..7: WRONG CODE — pdp11_expand_shift() declines
(count not "small", TARGET_40_PLUS), falling into the zero-extend
branch above.
- Variable counts: correct in practice — gcc keeps the C integer
promotion, and the HImode path loads the byte with movb, which
sign-extends on the PDP-11. However, the expander branch handles
variable counts identically, so it is equally wrong for anything
that routes a variable-count QImode ashr to it.
Test case:
signed char q4(signed char x) { return x >> 4; }
/* q4(-16) returns 15 (expected -1) */
signed char q7(signed char x) { return x >> 7; }
/* q7(-1) returns 1 (expected -1) */
Generated code (gcc 13.3.0, -Os, pdp11-aout):
_q4:
clr r0 ; zero-extend...
bisb 02(sp),r0 ; ...instead of sign-extending movb
ash $-04,r0 ; arithmetic shift of a positive HI value
rts pc
The ubiquitous nibble idiom (c >> 4) & 0x0f is unaffected — the mask
discards exactly the bits where the buggy and correct results differ —
which is how this can hide for a long time.
The expansion is unchanged in current master (checked 2026-07-09).
Third member of a series of independent defects in the pdp11 shift
expanders: see PR125058 (QImode lshr count not negated) and
PR126190 (HI/SI variable lshr by runtime count 0).
A one-word patch against master (ZERO_EXTEND -> SIGN_EXTEND, with a
testsuite scan-assembler test) is attached. With sign extension the
HImode arithmetic shift is exact for all counts and both count forms,
and the emitted code is two words shorter:
_q4:
movb 02(sp),r0 ; movb to register sign-extends
ash $-04,r0
rts pc