[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