m156: hasNon16BitAccesses copy-paste bug -- OpIs16Bit check uses TempOtherOp width instead of TempOp
Discovery method: code inspection (during zext/anyext combine audit).
amdgpu/third_party/llvm-project/llvm/lib/Target/AMDGPU/SIISelLowering.cpp:14923-14924:
auto OpIs16Bit =
TempOtherOp.getValueSizeInBits() == 16 || isExtendedFrom16Bits(TempOp);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (BUG)Two lines down (line 14928-14929), the symmetric OtherOpIs16Bit
clause correctly uses TempOtherOp on both sides:
auto OtherOpIs16Bit =
TempOtherOp.getValueSizeInBits() == 16 || isExtendedFrom16Bits(TempOtherOp);The OpIs16Bit check should test TempOp.getValueSizeInBits(), not
TempOtherOp.getValueSizeInBits(). Classic copy-paste defect.
performOrCombine -> matchPERM at SIISelLowering.cpp:15070. Used
to decide whether to lower or-tree patterns mixing (zext i16) and
larger ops into v_perm_b32 (a byte-perm), versus keeping 16-bit
ops.
- Case A (wrong-code direction):
Opis genuinely 32-bit andOtherOphappens to be 16-bit.OpIs16Bitbecomes spuriously true. The combine concludes "both are 16-bit", skipsv_perm, and may leave a 16-bit-shape codegen forOpthat does not model its actual width. With zext semantics in the or-tree, this can drop the upper 16 bits ofOp(lane-mix on i16->i32 zext when paired with an i16 OtherOp under an or, or v2i16->v2i32 patterns). - Case B (lost optimization):
OtherOpis 32-bit andOpis 16-bit/extended.OpIs16Bitis forced false, the function returns true, andv_permis always selected -- losing a valid 16-bit-shape codegen opportunity.
reduced.ll:
define amdgpu_kernel void @t(ptr addrspace(1) %in, ptr addrspace(1) %out) {
%xi = load i32, ptr addrspace(1) %in
%h = trunc i32 %xi to i16
%z = zext i16 %h to i32 ; OtherOp = 16-bit zext
%m = and i32 %xi, 65280
%s = shl i32 %m, 8 ; Op = 32-bit
%p = or i32 %z, %s
store i32 %p, ptr addrspace(1) %out
ret void
}llc -mtriple=amdgcn -mcpu=gfx950 -O2 reduced.ll: the or-tree
reaches matchPERM; the buggy hasNon16BitAccesses returns the
wrong answer for this mixed-width shape.
auto OpIs16Bit =
TempOp.getValueSizeInBits() == 16 || isExtendedFrom16Bits(TempOp);
// ^^^^^^^^ (fix)Same change is needed in the ROCm fork at
amdgpu/third_party/rocm-llvm-project/llvm/lib/Target/AMDGPU/SIISelLowering.cpp:14978-14979.
- Generic random IR usually emits or-trees of uniform width. Per
MEMORY.md(Prefer-random-over-idioms), the random emitter should explicitly mix widths in or-trees (16-bit zext + 32-bit shifted operand) to surface matchPERM defects like this one. - The defect manifests as a subtle change in which byte-perm selector is chosen, not always as a value miscompile, so the O0-vs-O2 oracle may not always catch it on a per-input basis.
| Toolchain | Result |
|---|---|
LLVM HEAD with the local PR patches (build/llvm-fuzzer) |
Copy-paste defect present. |
ROCm 7.1.1 (rocm-llvm-project) |
Same defect at :14978-14979. |