[SPIRV] Preserve sign-sensitive width record across G_TRUNC replacement (#211626)

**Summary**
Follow-up to #203661. When the `G_TRUNC` handler in `SPIRVPreLegalizer`
collapses source and destination to the same widened width, it calls
`MRI.replaceRegWith(DstReg, MaskedReg)` and drops the original
`G_TRUNC`. The `SignSensitiveInfo.OrigWidth` map, however, was still
keyed on `DstReg`, so `widenSignSensitiveOps` no longer found the narrow
original width and skipped the `shl/ashr` sign extension for signed
users of the truncated value.
Rekey `OrigWidth` from `DstReg` to `MaskedReg` before the
`replaceRegWith` so signed ops fed by the truncated value still receive
`G_SEXT_INREG` on the widened type.

**Test plan**
New test case is added to
`llvm/test/CodeGen/SPIRV/legalization/signed-narrow-int.ll`: `trunc i8 →
i4` feeding `icmp slt` — src and dst both widen to `i8`, exercising the
same-width `replaceRegWith` path. Checks that the widened `i8` compare
is preceded by matching `OpShiftLeftLogical / OpShiftRightArithmetic`
pairs on both operands.
This commit is contained in:
Faijul Amin 2026-07-28 11:02:11 -07:00 committed by GitHub
parent a4a9b6864c
commit ee25d3ec23
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 33 additions and 0 deletions

View file

@ -684,6 +684,14 @@ generateAssignInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR,
MIB.buildAnd(MaskedReg, SrcReg, MaskReg);
if (NewSrcWidth == NewDstWidth) {
// Rekey OrigWidth from DstReg to MaskedReg so widenSignSensitiveOps
// still sees the narrow original width after replaceRegWith.
if (auto It = SignSensitiveInfo.OrigWidth.find(DstReg);
It != SignSensitiveInfo.OrigWidth.end()) {
unsigned W = It->second;
SignSensitiveInfo.OrigWidth.erase(It);
SignSensitiveInfo.OrigWidth.try_emplace(MaskedReg, W);
}
MRI.replaceRegWith(DstReg, MaskedReg);
TruncToRemove.push_back(&MI);
} else {

View file

@ -225,6 +225,31 @@ define spir_kernel void @trunc_i24_icmp_slt(i64 %a, ptr addrspace(1) %out) {
ret void
}
; ----------------------------------------------------------------------------
; trunc i8 to i4 feeding icmp slt: source and destination widen to the same
; legal width (i8), so the G_TRUNC handler replaces the destination with the
; masking G_AND. The sign-sensitive width record must survive that replacement
; so the widened i8 compare still gets its shl/ashr sign extension.
; CHECK: OpFunction
; CHECK: %[[#XT8:]] = OpFunctionParameter
; CHECK: %[[#YT8:]] = OpFunctionParameter
; CHECK: OpFunctionParameter
; CHECK-DAG: %[[#ANDA_T8:]] = OpBitwiseAnd %[[#I8]] %[[#XT8]] {{%[0-9]+}}
; CHECK-DAG: %[[#ANDB_T8:]] = OpBitwiseAnd %[[#I8]] %[[#YT8]] {{%[0-9]+}}
; CHECK: %[[#SHLA_T8:]] = OpShiftLeftLogical %[[#I8]] %[[#ANDA_T8]] %[[#K4]]
; CHECK: %[[#SXA_T8:]] = OpShiftRightArithmetic %[[#I8]] %[[#SHLA_T8]] %[[#K4]]
; CHECK: %[[#SHLB_T8:]] = OpShiftLeftLogical %[[#I8]] %[[#ANDB_T8]] %[[#K4]]
; CHECK: %[[#SXB_T8:]] = OpShiftRightArithmetic %[[#I8]] %[[#SHLB_T8]] %[[#K4]]
; CHECK: OpSLessThan {{%[0-9]+}} %[[#SXA_T8]] %[[#SXB_T8]]
define spir_kernel void @trunc_i8_to_i4_icmp_slt(i8 %a, i8 %b, ptr addrspace(1) %out) {
%at = trunc i8 %a to i4
%bt = trunc i8 %b to i4
%c = icmp slt i4 %at, %bt
%r = sext i1 %c to i8
store i8 %r, ptr addrspace(1) %out
ret void
}
; ----------------------------------------------------------------------------
; Negative test: unsigned compare must NOT emit sign-extension shifts.
; CHECK: OpFunction