Skip to content

[U3-B] comparisons and arithmetic shift on the carrier dispatch - #28

Merged
EdanStasiuk merged 7 commits into
angelo-yap:mainfrom
EdanStasiuk:U3-B-comparisons-and-arithmetic-shift
Jul 21, 2026
Merged

[U3-B] comparisons and arithmetic shift on the carrier dispatch#28
EdanStasiuk merged 7 commits into
angelo-yap:mainfrom
EdanStasiuk:U3-B-comparisons-and-arithmetic-shift

Conversation

@EdanStasiuk

@EdanStasiuk EdanStasiuk commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Closes: #7

Summary

Extends the Nybbler pass to support arithmetic right shifts (AShr) and comparisons (ICmp: eq, ne, ult, slt) for narrow-field (i1/i2/i4) vectors.

Adds three new handler families to the carrier dispatch (getHandler), each registered per the existing CarrierHandler pattern:

  • icmp eq/ne -- lowerEqNe: xor + within-field OR-reduce/broadcast (reduceAndBroadcastField), degenerating correctly at i1.
  • icmp ult/slt -- lowerUlt/lowerSlt: SWAR unsigned compare via borrow-containment subtraction (ult.ahi/ult.blo/ult.raw), with slt implemented as a sign-bit flip ahead of the same unsigned compare. i1 ult/slt are trivial and special-cased to single-bit logic (~a & b, a & ~b) before dispatch.
  • ashr -- lowerAshr: same barrel-shift skeleton as shl/lshr (lowerShift), but vacated high bits are filled with each field's sign bit instead of zero. i1 is special-cased to identity.

Two real bugs were found and fixed during implementation:

  • lowerUlt's borrow formula (Raw ^ b ^ ~a) was algebraically incapable of computing unsigned less-than for any input, since a^~b == ~a^b collapses it to one XOR expression regardless of operand order. Replaced with the correct borrow-out formula, (~a & b) | (~(a^b) & ~Raw), verified by exhaustive brute-force check over all i2/i4 field values. lowerSlt was fixed as a side effect, since it reuses lowerUlt via the sign-flip trick.
  • diff/ashr.ll failed intermittently in CI but not locally. Root cause was in the test harness, not the pass: for shift kernels, the b operand doubles as a per-field shift amount, and the generated trials included out-of-range amounts (>= N), which are poison per the LLVM LangRef. The harness's "reference" runs the unlowered op through lli, so for a poison input its result depends on the host LLVM's legalization -- which differed between local (Homebrew llvm@22.1.8, macOS/arm64) and CI (apt llvm-22, Linux/x86_64) -- while Nybbler's deterministic carrier lowering stayed consistent across both. Fixed by clamping each field of the shift-amount operand into [0, N-1] in diff_runner.py before it's emitted, so no trial exercises a poison shift amount.

Tests

  • Un-XFAILed all 15 shape tests for eq/ne/ult/slt/ashr (i1/i2/i4). Several had stale CHECK lines left over from before these ops were implemented (e.g. expecting a literal icmp eq/icmp ult/ashr on the carrier, when the actual lowering never emits those opcodes directly -- it's built from xor/and/or/sub/lshr/shl); updated each to check for the real emitted instruction sequence instead.
  • diff/eq.ll, diff/ne.ll, diff/ult.ll, diff/slt.ll, diff/ashr.ll all pass, confirming correctness against the scalar reference across structured edge cases and randomized trials.
  • Full suite: 55/55 passing on CI

Extends the Nybbler pass to support arithmetic right shifts (AShr) and comparisons (ICmp: eq, ne, ult, slt) for narrow-field (i1/i2/i4) vectors. Need to iron out some kinks, got a couple failed tests.
@EdanStasiuk
EdanStasiuk marked this pull request as draft July 19, 2026 08:39
lowerUlt computed the per-field borrow bit as `Raw ^ b ^ ~a`, but this
is algebraically identical to `Raw ^ a ^ ~b` (since a^~b == ~a^b for
any bits), so no XOR-only combination of {Raw, a, b, ~a, ~b} can
distinguish a <u b in general -- the formula was mathematically unable
to be correct.

Replace it with the borrow-out formula derived from the containment
trick (a's top bit forced 1, b's top bit forced 0):

  a <u b  ==  (~a & b) | (~(a^b) & ~Raw)   [top bit of each field]

Verified by exhaustive brute-force check over all i2/i4 field values.

lowerSlt reuses lowerUlt via the sign-flip trick, so it is fixed as a
side effect -- no changes needed there.

Fixes diff/ult.ll and diff/slt.ll.
@EdanStasiuk
EdanStasiuk marked this pull request as ready for review July 20, 2026 08:08
diff/ashr.ll failed intermittently in CI but not locally, e.g.:

  MISMATCH ashr_i4 a=aaaa...a b=aaaa...a ref=eeee...e cand=ffff...f

Each i4 field of `b` is 0b1010 = 10, which is >= N (4) -- an
out-of-range shift amount, which is poison per the LLVM LangRef. The
harness's reference runs the *unlowered* op through lli, so for a
poison input its result depends on the host LLVM's legalization,
which differs between local (Homebrew llvm@22.1.8, macOS/arm64) and
CI (apt llvm-22, Linux/x86_64). The candidate (Nybbler's carrier
lowering) is deterministic and always saturates to sign, so only
`ref` diverged -- a harness gap, not a lowering bug.

Add mask_shift_amounts(), clamping each N-bit field of a shift
kernel's `b` operand into [0, N-1], applied in build_module() for
shl_/lshr_/ashr_ kernels. `a` is untouched so data values are still
fully exercised.

No changes to Nybbler.cpp: lowerAshr's saturating behavior for
out-of-range amounts is intentional and doesn't need to match
poison output, since poison has no single correct value.
@EdanStasiuk
EdanStasiuk requested a review from angelo-yap July 20, 2026 08:57

@angelo-yap angelo-yap left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, minor issues

Comment thread lib/Nybbler.cpp Outdated
Comment thread lib/Nybbler.cpp Outdated
@EdanStasiuk EdanStasiuk changed the title [U3-B] support AShr and ICmp in narrow-field vector lowering [U3-B] comparisons and arithmetic shift on the carrier dispatch Jul 21, 2026
@EdanStasiuk
EdanStasiuk merged commit d3d154f into angelo-yap:main Jul 21, 2026
1 check passed
@EdanStasiuk
EdanStasiuk deleted the U3-B-comparisons-and-arithmetic-shift branch July 21, 2026 00:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[U3-B] Comparisons and arithmetic shift

2 participants