[llvm] [SPIRV] Fix trunc nonstandard int types (PR #191393)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Apr 13 02:36:05 PDT 2026
https://github.com/idubinov updated https://github.com/llvm/llvm-project/pull/191393
>From 14b7b36538451da38dd9421e27e0b9879a6ac9ca Mon Sep 17 00:00:00 2001
From: idubinov <igor.dubinov at amd.com>
Date: Fri, 10 Apr 2026 06:02:55 -0500
Subject: [PATCH 1/3] fix nonstandart trunc
---
llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp | 65 ++++++++++++++++---
.../CodeGen/SPIRV/trunc-nonstd-bitwidth.ll | 38 ++++++++++-
2 files changed, 93 insertions(+), 10 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
index aead1ce735c49..a6a0feec6c959 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
@@ -397,25 +397,40 @@ static unsigned widenBitWidthToNextPow2(unsigned BitWidth) {
return std::min(std::max(1u << Log2_32_Ceil(BitWidth), 8u), 128u);
}
-static void widenScalarType(Register Reg, MachineRegisterInfo &MRI) {
+static unsigned widenScalarType(Register Reg, MachineRegisterInfo &MRI) {
+ // returns original size or 0 if no change
LLT RegType = MRI.getType(Reg);
if (!RegType.isScalar())
- return;
+ return 0;
unsigned CurrentWidth = RegType.getScalarSizeInBits();
unsigned NewWidth = widenBitWidthToNextPow2(CurrentWidth);
- if (NewWidth != CurrentWidth)
+ if (NewWidth != CurrentWidth){
MRI.setType(Reg, LLT::scalar(NewWidth));
+ return CurrentWidth;
+ }
+ return 0;
}
-static void widenCImmType(MachineOperand &MOP) {
+static unsigned widenCImmType(MachineOperand &MOP) {
+ // returns original size or 0 if no change
const ConstantInt *CImmVal = MOP.getCImm();
unsigned CurrentWidth = CImmVal->getBitWidth();
unsigned NewWidth = widenBitWidthToNextPow2(CurrentWidth);
- if (NewWidth != CurrentWidth) {
- // Replace the immediate value with the widened version
- MOP.setCImm(ConstantInt::get(CImmVal->getType()->getContext(),
- CImmVal->getValue().zextOrTrunc(NewWidth)));
- }
+ if (NewWidth == CurrentWidth)
+ return 0;
+
+ // Replace the immediate value with the widened version
+ MOP.setCImm(ConstantInt::get(CImmVal->getType()->getContext(),
+ CImmVal->getValue().zextOrTrunc(NewWidth)));
+ return CurrentWidth;
+}
+
+static unsigned widenOperand(MachineOperand &MOP, MachineRegisterInfo &MRI) {
+ // returns original size or 0 if no change
+ if (MOP.isReg())
+ return widenScalarType(MOP.getReg(), MRI);
+ else if (MOP.isCImm())
+ return widenCImmType(MOP);
}
static void setInsertPtAfterDef(MachineIRBuilder &MIB, MachineInstr *Def) {
@@ -486,6 +501,38 @@ generateAssignInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR,
ST->canUseExtension(SPIRV::Extension::SPV_KHR_bit_instructions) ||
ST->canUseExtension(SPIRV::Extension::SPV_INTEL_int4);
+ if (!IsExtendedInts) {
+ // Some instructions' behavior relies on register size, e.g. G_TRUNC. Process them before general register widening.
+ for (MachineBasicBlock &MBB : MF) {
+ for (MachineInstr &MI: MBB) {
+ unsigned MIOp = MI.getOpcode();
+ if (MIOp == TargetOpcode::G_TRUNC) {
+ assert(MI.getNumOperands() == 2);
+
+ widenOperand(MI.getOperand(1), MRI); // SRC
+
+ unsigned OriginalDstWidth = widenOperand(MI.getOperand(0), MRI);
+ if (OriginalDstWidth != 0) {
+ // Dst was widened - replace G_TRUNC with G_AND & appropriate mask.
+ Register DstReg = MI.getOperand(0).getReg();
+ Register SrcReg = MI.getOperand(1).getReg();
+ unsigned NewDstWidth = MRI.getType(DstReg).getScalarSizeInBits();
+
+ // Create mask constant with lower OriginalDstWidth bits set.
+ MIB.setInsertPt(MBB, MI.getIterator());
+ APInt Mask = APInt::getLowBitsSet(NewDstWidth, OriginalDstWidth);
+ auto MaskReg = MIB.buildConstant(LLT::scalar(NewDstWidth), Mask);
+
+ // Replace G_TRUNC with G_AND.
+ MI.setDesc(ST->getInstrInfo()->get(TargetOpcode::G_AND));
+ MI.getOperand(1).setReg(SrcReg);
+ MI.addOperand(MachineOperand::CreateReg(MaskReg.getReg(0), false));
+ }
+ }
+ }
+ }
+ }
+
for (MachineBasicBlock *MBB : post_order(&MF)) {
if (MBB->empty())
continue;
diff --git a/llvm/test/CodeGen/SPIRV/trunc-nonstd-bitwidth.ll b/llvm/test/CodeGen/SPIRV/trunc-nonstd-bitwidth.ll
index 07af684fe0b48..bedc3b59089f8 100644
--- a/llvm/test/CodeGen/SPIRV/trunc-nonstd-bitwidth.ll
+++ b/llvm/test/CodeGen/SPIRV/trunc-nonstd-bitwidth.ll
@@ -17,6 +17,8 @@
; CHECK-DAG: %[[#PtrStruct:]] = OpTypePointer CrossWorkgroup %[[#Struct]]
; CHECK-EXT-DAG: %[[#Int40:]] = OpTypeInt 40 0
; CHECK-EXT-DAG: %[[#Int50:]] = OpTypeInt 50 0
+; CHECK-EXT-DAG: %[[#Int24:]] = OpTypeInt 24 0
+; CHECK-EXT-DAG: %[[#Int5:]] = OpTypeInt 5 0
; CHECK-NOEXT-DAG: %[[#Int40:]] = OpTypeInt 64 0
; CHECK-DAG: %[[#PtrInt40:]] = OpTypePointer CrossWorkgroup %[[#Int40]]
@@ -38,7 +40,27 @@
; CHECK-EXT: %[[#Tq:]] = OpUConvert %[[#Int40]] %[[#Q]]
; CHECK-EXT: OpStore %[[#QArg]] %[[#Tq]]
-; CHECK-NOEXT: OpStore %[[#QArg]] %[[#Q]]
+; CHECK-NOEXT: %[[#TqNoext:]] = OpBitwiseAnd %[[#]] %[[#Q]] %[[#]]
+; CHECK-NOEXT: OpStore %[[#QArg]] %[[#TqNoext]]
+
+; Test 3: trunc to small non-standard width (i64 -> i24)
+; CHECK: OpFunction
+; CHECK: %[[#T3Arg:]] = OpFunctionParameter
+; CHECK: %[[#T3Val:]] = OpFunctionParameter
+; CHECK-EXT: %[[#T3Tr:]] = OpUConvert %[[#Int24]] %[[#T3Val]]
+; CHECK-EXT: OpStore %[[#T3Arg]] %[[#T3Tr]]
+; CHECK-NOEXT: %[[#T3Noext:]] = OpBitwiseAnd %[[#]] %[[#T3Val]] %[[#]]
+; CHECK-NOEXT: OpStore %[[#T3Arg]] %[[#T3Noext]]
+
+; Test 4: trunc to i5 (non-power-of-2, < 8 bits)
+; In NOEXT mode, i5 widens to i8, so mask with 0x1F
+; CHECK: OpFunction
+; CHECK: %[[#T4Arg:]] = OpFunctionParameter
+; CHECK: %[[#T4Val:]] = OpFunctionParameter
+; CHECK-EXT: %[[#T4Tr:]] = OpUConvert %[[#Int5]] %[[#T4Val]]
+; CHECK-EXT: OpStore %[[#T4Arg]] %[[#T4Tr]]
+; CHECK-NOEXT: %[[#T4Noext:]] = OpBitwiseAnd %[[#]] %[[#T4Val]] %[[#]]
+; CHECK-NOEXT: OpStore %[[#T4Arg]] %[[#T4Noext]]
%struct = type <{ i32, i8, [3 x i8] }>
@@ -54,3 +76,17 @@ define spir_kernel void @bar(ptr addrspace(1) %qarg, i50 %q) {
store i40 %tq, ptr addrspace(1) %qarg
ret void
}
+
+; trunc to small non-standard width (i64 -> i24)
+define spir_kernel void @trunc_to_i24(ptr addrspace(1) %arg, i64 %val) {
+ %tr = trunc i64 %val to i24
+ store i24 %tr, ptr addrspace(1) %arg
+ ret void
+}
+
+; trunc to i5 (non-power-of-2, < 8 bits)
+define spir_kernel void @trunc_to_i5(ptr addrspace(1) %arg, i16 %val) {
+ %tr = trunc i16 %val to i5
+ store i5 %tr, ptr addrspace(1) %arg
+ ret void
+}
>From d9b3cc2a70accb8ae77f4466aef32ab8b25d47c0 Mon Sep 17 00:00:00 2001
From: idubinov <igor.dubinov at amd.com>
Date: Fri, 10 Apr 2026 06:04:14 -0500
Subject: [PATCH 2/3] code format
---
llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
index a6a0feec6c959..3bba1836b4ff4 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
@@ -404,7 +404,7 @@ static unsigned widenScalarType(Register Reg, MachineRegisterInfo &MRI) {
return 0;
unsigned CurrentWidth = RegType.getScalarSizeInBits();
unsigned NewWidth = widenBitWidthToNextPow2(CurrentWidth);
- if (NewWidth != CurrentWidth){
+ if (NewWidth != CurrentWidth) {
MRI.setType(Reg, LLT::scalar(NewWidth));
return CurrentWidth;
}
@@ -416,7 +416,7 @@ static unsigned widenCImmType(MachineOperand &MOP) {
const ConstantInt *CImmVal = MOP.getCImm();
unsigned CurrentWidth = CImmVal->getBitWidth();
unsigned NewWidth = widenBitWidthToNextPow2(CurrentWidth);
- if (NewWidth == CurrentWidth)
+ if (NewWidth == CurrentWidth)
return 0;
// Replace the immediate value with the widened version
@@ -502,15 +502,16 @@ generateAssignInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR,
ST->canUseExtension(SPIRV::Extension::SPV_INTEL_int4);
if (!IsExtendedInts) {
- // Some instructions' behavior relies on register size, e.g. G_TRUNC. Process them before general register widening.
+ // Some instructions' behavior relies on register size, e.g. G_TRUNC.
+ // Process them before general register widening.
for (MachineBasicBlock &MBB : MF) {
- for (MachineInstr &MI: MBB) {
+ for (MachineInstr &MI : MBB) {
unsigned MIOp = MI.getOpcode();
if (MIOp == TargetOpcode::G_TRUNC) {
assert(MI.getNumOperands() == 2);
-
+
widenOperand(MI.getOperand(1), MRI); // SRC
-
+
unsigned OriginalDstWidth = widenOperand(MI.getOperand(0), MRI);
if (OriginalDstWidth != 0) {
// Dst was widened - replace G_TRUNC with G_AND & appropriate mask.
>From ed8ba8bad7fa514e106de17d9a93042fba1c9b66 Mon Sep 17 00:00:00 2001
From: idubinov <idubinov at amd.com>
Date: Mon, 13 Apr 2026 11:35:55 +0200
Subject: [PATCH 3/3] Apply suggestions from code review
Co-authored-by: Marcos Maronas <mmaronas at amd.com>
---
llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp | 10 +++++-----
llvm/test/CodeGen/SPIRV/trunc-nonstd-bitwidth.ll | 10 +++++-----
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
index 3bba1836b4ff4..118c30090b783 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
@@ -398,7 +398,7 @@ static unsigned widenBitWidthToNextPow2(unsigned BitWidth) {
}
static unsigned widenScalarType(Register Reg, MachineRegisterInfo &MRI) {
- // returns original size or 0 if no change
+ // Returns original size or 0 if no change.
LLT RegType = MRI.getType(Reg);
if (!RegType.isScalar())
return 0;
@@ -412,21 +412,21 @@ static unsigned widenScalarType(Register Reg, MachineRegisterInfo &MRI) {
}
static unsigned widenCImmType(MachineOperand &MOP) {
- // returns original size or 0 if no change
+ // Returns original size or 0 if no change.
const ConstantInt *CImmVal = MOP.getCImm();
unsigned CurrentWidth = CImmVal->getBitWidth();
unsigned NewWidth = widenBitWidthToNextPow2(CurrentWidth);
if (NewWidth == CurrentWidth)
return 0;
- // Replace the immediate value with the widened version
+ // Replace the immediate value with the widened version.
MOP.setCImm(ConstantInt::get(CImmVal->getType()->getContext(),
CImmVal->getValue().zextOrTrunc(NewWidth)));
return CurrentWidth;
}
static unsigned widenOperand(MachineOperand &MOP, MachineRegisterInfo &MRI) {
- // returns original size or 0 if no change
+ // Returns original size or 0 if no change.
if (MOP.isReg())
return widenScalarType(MOP.getReg(), MRI);
else if (MOP.isCImm())
@@ -510,7 +510,7 @@ generateAssignInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR,
if (MIOp == TargetOpcode::G_TRUNC) {
assert(MI.getNumOperands() == 2);
- widenOperand(MI.getOperand(1), MRI); // SRC
+ widenOperand(MI.getOperand(1), MRI); // SRC.
unsigned OriginalDstWidth = widenOperand(MI.getOperand(0), MRI);
if (OriginalDstWidth != 0) {
diff --git a/llvm/test/CodeGen/SPIRV/trunc-nonstd-bitwidth.ll b/llvm/test/CodeGen/SPIRV/trunc-nonstd-bitwidth.ll
index bedc3b59089f8..744db268ed25a 100644
--- a/llvm/test/CodeGen/SPIRV/trunc-nonstd-bitwidth.ll
+++ b/llvm/test/CodeGen/SPIRV/trunc-nonstd-bitwidth.ll
@@ -43,7 +43,7 @@
; CHECK-NOEXT: %[[#TqNoext:]] = OpBitwiseAnd %[[#]] %[[#Q]] %[[#]]
; CHECK-NOEXT: OpStore %[[#QArg]] %[[#TqNoext]]
-; Test 3: trunc to small non-standard width (i64 -> i24)
+; Test 3: trunc to small non-standard width (i64 -> i24).
; CHECK: OpFunction
; CHECK: %[[#T3Arg:]] = OpFunctionParameter
; CHECK: %[[#T3Val:]] = OpFunctionParameter
@@ -52,8 +52,8 @@
; CHECK-NOEXT: %[[#T3Noext:]] = OpBitwiseAnd %[[#]] %[[#T3Val]] %[[#]]
; CHECK-NOEXT: OpStore %[[#T3Arg]] %[[#T3Noext]]
-; Test 4: trunc to i5 (non-power-of-2, < 8 bits)
-; In NOEXT mode, i5 widens to i8, so mask with 0x1F
+; Test 4: trunc to i5 (non-power-of-2, < 8 bits).
+; In NOEXT mode, i5 widens to i8, so mask with 0x1F.
; CHECK: OpFunction
; CHECK: %[[#T4Arg:]] = OpFunctionParameter
; CHECK: %[[#T4Val:]] = OpFunctionParameter
@@ -77,14 +77,14 @@ define spir_kernel void @bar(ptr addrspace(1) %qarg, i50 %q) {
ret void
}
-; trunc to small non-standard width (i64 -> i24)
+; Trunc to small non-standard width (i64 -> i24).
define spir_kernel void @trunc_to_i24(ptr addrspace(1) %arg, i64 %val) {
%tr = trunc i64 %val to i24
store i24 %tr, ptr addrspace(1) %arg
ret void
}
-; trunc to i5 (non-power-of-2, < 8 bits)
+; Trunc to i5 (non-power-of-2, < 8 bits).
define spir_kernel void @trunc_to_i5(ptr addrspace(1) %arg, i16 %val) {
%tr = trunc i16 %val to i5
store i5 %tr, ptr addrspace(1) %arg
More information about the llvm-commits
mailing list