Skip to content

[SPIR-V] Add vector type support for non-standard integers in G_TRUNC op - #198213

Merged
aobolensk merged 1 commit into
llvm:mainfrom
aobolensk:llvm-spirv-g_trunc-vector-support
Jun 8, 2026
Merged

[SPIR-V] Add vector type support for non-standard integers in G_TRUNC op#198213
aobolensk merged 1 commit into
llvm:mainfrom
aobolensk:llvm-spirv-g_trunc-vector-support

Conversation

@aobolensk

@aobolensk aobolensk commented May 17, 2026

Copy link
Copy Markdown
Contributor

G_TRUNC is the special case for now that require these changes because it is the only one that depend on the original destination

@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-backend-spir-v

Author: Arseniy Obolenskiy (aobolensk)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/198213.diff

2 Files Affected:

  • (modified) llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp (+29-15)
  • (modified) llvm/test/CodeGen/SPIRV/trunc-nonstd-bitwidth.ll (+77)
diff --git a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
index 75cc7133f1766..14fc8b2fc4001 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
@@ -514,30 +514,44 @@ generateAssignInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR,
         Register DstReg = MI.getOperand(0).getReg();
         Register SrcReg = MI.getOperand(1).getReg();
 
-        // TODO: handle vector types.
-        if (!MRI.getType(DstReg).isScalar()) {
-          assert(!MRI.getType(SrcReg).isScalar());
-          continue;
-        }
-
-        unsigned OriginalDstWidth = MRI.getType(DstReg).getScalarSizeInBits();
-        unsigned OriginalSrcWidth = MRI.getType(SrcReg).getScalarSizeInBits();
+        LLT DstTy = MRI.getType(DstReg);
+        LLT SrcTy = MRI.getType(SrcReg);
+        assert((DstTy.isScalar() || DstTy.isVector()) &&
+               (SrcTy.isScalar() || SrcTy.isVector()) &&
+               "Expected scalar or vector G_TRUNC types");
+        assert(DstTy.isVector() == SrcTy.isVector() &&
+               "Expected matching scalar/vector G_TRUNC types");
+        assert((!DstTy.isVector() ||
+                DstTy.getElementCount() == SrcTy.getElementCount()) &&
+               "Expected equal vector element counts");
+
+        unsigned OriginalDstWidth = DstTy.getScalarSizeInBits();
+        unsigned OriginalSrcWidth = SrcTy.getScalarSizeInBits();
 
         unsigned NewDstWidth = widenBitWidthToNextPow2(OriginalDstWidth);
         unsigned NewSrcWidth = widenBitWidthToNextPow2(OriginalSrcWidth);
+        LLT NewDstTy = DstTy.changeElementSize(NewDstWidth);
+        LLT NewSrcTy = SrcTy.changeElementSize(NewSrcWidth);
 
-        // No Dst width change means no truncation semantics change.
-        if (OriginalDstWidth == NewDstWidth)
+        // No Dst width change means no truncation semantics change, but the
+        // source still needs a legal type.
+        if (OriginalDstWidth == NewDstWidth) {
+          MRI.setType(SrcReg, NewSrcTy);
           continue;
+        }
 
-        MRI.setType(SrcReg, LLT::scalar(NewSrcWidth));
-        MRI.setType(DstReg, LLT::scalar(NewDstWidth));
+        MRI.setType(SrcReg, NewSrcTy);
+        MRI.setType(DstReg, NewDstTy);
 
         MIB.setInsertPt(MBB, MI.getIterator());
         APInt Mask = APInt::getLowBitsSet(NewSrcWidth, OriginalDstWidth);
-        auto MaskReg = MIB.buildConstant(LLT::scalar(NewSrcWidth), Mask);
-        Register MaskedReg =
-            MRI.createGenericVirtualRegister(LLT::scalar(NewSrcWidth));
+        MachineInstrBuilder MaskReg =
+            DstTy.isVector()
+                ? MIB.buildBuildVectorConstant(
+                      NewSrcTy,
+                      SmallVector<APInt, 4>(DstTy.getNumElements(), Mask))
+                : MIB.buildConstant(NewSrcTy, Mask);
+        Register MaskedReg = MRI.createGenericVirtualRegister(NewSrcTy);
         MIB.buildAnd(MaskedReg, SrcReg, MaskReg);
 
         if (NewSrcWidth == NewDstWidth) {
diff --git a/llvm/test/CodeGen/SPIRV/trunc-nonstd-bitwidth.ll b/llvm/test/CodeGen/SPIRV/trunc-nonstd-bitwidth.ll
index 22af0c3b81ee0..e3b2f48ceead3 100644
--- a/llvm/test/CodeGen/SPIRV/trunc-nonstd-bitwidth.ll
+++ b/llvm/test/CodeGen/SPIRV/trunc-nonstd-bitwidth.ll
@@ -13,8 +13,26 @@
 ; CHECK-EXT-DAG: %[[#Int40:]] = OpTypeInt 40 0
 ; CHECK-EXT-DAG: %[[#Int50:]] = OpTypeInt 50 0
 ; CHECK-EXT-DAG: %[[#Int24:]] = OpTypeInt 24 0
+; CHECK-EXT-DAG: %[[#ExtInt32:]] = OpTypeInt 32 0
 ; CHECK-NOEXT-DAG: %[[#Int64:]] = OpTypeInt 64 0
 ; CHECK-NOEXT-DAG: %[[#Int32:]] = OpTypeInt 32 0
+; CHECK-EXT-DAG: %[[#Vec2Int40:]] = OpTypeVector %[[#Int40]] 2
+; CHECK-EXT-DAG: %[[#Vec2Int50:]] = OpTypeVector %[[#Int50]] 2
+; CHECK-EXT-DAG: %[[#Vec2ExtInt32:]] = OpTypeVector %[[#ExtInt32]] 2
+; CHECK-EXT-DAG: %[[#Vec3Int50:]] = OpTypeVector %[[#Int50]] 3
+; CHECK-EXT-DAG: %[[#Vec3Int24:]] = OpTypeVector %[[#Int24]] 3
+; CHECK-EXT-DAG: %[[#Vec4Int24:]] = OpTypeVector %[[#Int24]] 4
+; CHECK-NOEXT-DAG: %[[#Vec2Int64:]] = OpTypeVector %[[#Int64]] 2
+; CHECK-NOEXT-DAG: %[[#Vec2Int32:]] = OpTypeVector %[[#Int32]] 2
+; CHECK-NOEXT-DAG: %[[#Vec3Int64:]] = OpTypeVector %[[#Int64]] 3
+; CHECK-NOEXT-DAG: %[[#Vec4Int64:]] = OpTypeVector %[[#Int64]] 4
+; CHECK-NOEXT-DAG: %[[#Vec3Int32:]] = OpTypeVector %[[#Int32]] 3
+; CHECK-NOEXT-DAG: %[[#Vec4Int32:]] = OpTypeVector %[[#Int32]] 4
+; CHECK-NOEXT-DAG: %[[#Mask40:]] = OpConstant %[[#Int64]] 1099511627775
+; CHECK-NOEXT-DAG: %[[#Mask24:]] = OpConstant %[[#Int64]] 16777215
+; CHECK-NOEXT-DAG: %[[#Mask40Vec2:]] = OpConstantComposite %[[#Vec2Int64]] %[[#Mask40]] %[[#Mask40]]
+; CHECK-NOEXT-DAG: %[[#Mask24Vec3:]] = OpConstantComposite %[[#Vec3Int64]] %[[#Mask24]] %[[#Mask24]] %[[#Mask24]]
+; CHECK-NOEXT-DAG: %[[#Mask24Vec4:]] = OpConstantComposite %[[#Vec4Int64]] %[[#Mask24]] %[[#Mask24]] %[[#Mask24]] %[[#Mask24]]
 
 
 ; Test i64 -> i40: both widen to i64
@@ -60,3 +78,62 @@ define spir_kernel void @trunc_i64_to_i24(ptr addrspace(1) %arg, i64 %val) {
   store i24 %tr, ptr addrspace(1) %arg
   ret void
 }
+
+; Test <2 x i64> -> <2 x i40>: both widen to <2 x i64>
+; CHECK: OpFunction
+; CHECK: %[[#T4Arg:]] = OpFunctionParameter
+; CHECK: %[[#T4Val:]] = OpFunctionParameter
+; CHECK-EXT: %[[#T4Tr:]] = OpUConvert %[[#Vec2Int40]] %[[#T4Val]]
+; CHECK-EXT: OpStore %[[#T4Arg]] %[[#T4Tr]]
+; CHECK-NOEXT: %[[#T4And:]] = OpBitwiseAnd %[[#Vec2Int64]] %[[#T4Val]] %[[#Mask40Vec2]]
+; CHECK-NOEXT: OpStore %[[#T4Arg]] %[[#T4And]]
+define spir_kernel void @trunc_v2i64_to_v2i40(ptr addrspace(1) %arg, <2 x i64> %val) {
+  %tr = trunc <2 x i64> %val to <2 x i40>
+  store <2 x i40> %tr, ptr addrspace(1) %arg
+  ret void
+}
+
+; Test <3 x i50> -> <3 x i24>: src widens to <3 x i64>, dst widens to <3 x i32>
+; CHECK: OpFunction
+; CHECK: %[[#T5Arg:]] = OpFunctionParameter
+; CHECK: %[[#T5Val:]] = OpFunctionParameter
+; CHECK-EXT: %[[#T5Tr:]] = OpUConvert %[[#Vec3Int24]] %[[#T5Val]]
+; CHECK-EXT: OpStore %[[#T5Arg]] %[[#T5Tr]]
+; CHECK-NOEXT: %[[#T5And:]] = OpBitwiseAnd %[[#Vec3Int64]] %[[#T5Val]] %[[#Mask24Vec3]]
+; CHECK-NOEXT: %[[#T5Conv:]] = OpUConvert %[[#Vec3Int32]] %[[#T5And]]
+; CHECK-NOEXT: OpStore %[[#T5Arg]] %[[#T5Conv]]
+define spir_kernel void @trunc_v3i50_to_v3i24(ptr addrspace(1) %arg, <3 x i50> %val) {
+  %tr = trunc <3 x i50> %val to <3 x i24>
+  store <3 x i24> %tr, ptr addrspace(1) %arg
+  ret void
+}
+
+; Test <4 x i64> -> <4 x i24>: src stays <4 x i64>, dst widens to <4 x i32>
+; CHECK: OpFunction
+; CHECK: %[[#T6Arg:]] = OpFunctionParameter
+; CHECK: %[[#T6Val:]] = OpFunctionParameter
+; CHECK-EXT: %[[#T6Tr:]] = OpUConvert %[[#Vec4Int24]] %[[#T6Val]]
+; CHECK-EXT: OpStore %[[#T6Arg]] %[[#T6Tr]]
+; CHECK-NOEXT: %[[#T6And:]] = OpBitwiseAnd %[[#Vec4Int64]] %[[#T6Val]] %[[#Mask24Vec4]]
+; CHECK-NOEXT: %[[#T6Conv:]] = OpUConvert %[[#Vec4Int32]] %[[#T6And]]
+; CHECK-NOEXT: OpStore %[[#T6Arg]] %[[#T6Conv]]
+define spir_kernel void @trunc_v4i64_to_v4i24(ptr addrspace(1) %arg, <4 x i64> %val) {
+  %tr = trunc <4 x i64> %val to <4 x i24>
+  store <4 x i24> %tr, ptr addrspace(1) %arg
+  ret void
+}
+
+; Test <2 x i50> -> <2 x i32>: dst width is already legal, no mask needed
+; CHECK: OpFunction
+; CHECK: %[[#T7Arg:]] = OpFunctionParameter
+; CHECK: %[[#T7Val:]] = OpFunctionParameter
+; CHECK-EXT: %[[#T7Tr:]] = OpUConvert %[[#Vec2ExtInt32]] %[[#T7Val]]
+; CHECK-EXT: OpStore %[[#T7Arg]] %[[#T7Tr]]
+; CHECK-NOEXT-NOT: OpBitwiseAnd
+; CHECK-NOEXT: %[[#T7Conv:]] = OpUConvert %[[#Vec2Int32]] %[[#T7Val]]
+; CHECK-NOEXT: OpStore %[[#T7Arg]] %[[#T7Conv]]
+define spir_kernel void @trunc_v2i50_to_v2i32(ptr addrspace(1) %arg, <2 x i50> %val) {
+  %tr = trunc <2 x i50> %val to <2 x i32>
+  store <2 x i32> %tr, ptr addrspace(1) %arg
+  ret void
+}

@aobolensk
aobolensk requested a review from MrSidims June 3, 2026 14:26

@MrSidims MrSidims left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks!
Q: This fixes G_TRUNC specifically, but widenScalarType still early-returns on vectors, so other vector ops on non-standard int element types remain un-widened. Is G_TRUNC the only op that currently reaches here with such types, or is this the first of several follow-ups? Worth a note in the description.

@aobolensk

Copy link
Copy Markdown
Contributor Author

Thanks! Q: This fixes G_TRUNC specifically, but widenScalarType still early-returns on vectors, so other vector ops on non-standard int element types remain un-widened. Is G_TRUNC the only op that currently reaches here with such types, or is this the first of several follow-ups? Worth a note in the description.

So far, G_TRUNC is special because it is the only one that depend on the original destination. G_ZEXT/G_SEXT/G_ANYEXT are much simpler as of now, not really sure if anything like that should be added there as well, but in that case it should be changes for the scalar case first

@jmmartinez jmmartinez left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@aobolensk
aobolensk merged commit 917117c into llvm:main Jun 8, 2026
13 checks passed
carlobertolli pushed a commit to carlobertolli/llvm-project that referenced this pull request Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants