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
  • [Bug target/126191] New: [pdp1... migraineman33 at gmail dot com via Gcc-bugs

Reply via email to