llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

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

Author: Kaitlin Peng (kmpeng)

<details>
<summary>Changes</summary>

Fixes llvm/offload-test-suite#<!-- -->1037 on the Clang side.

SPIR-V has no multi-component 64-bit image format, so 
`[RW]Buffer&lt;uint/int64_t2&gt;` was being lowered to an `Unknown` image 
format, and the offloader then backed it with the Vulkan `R64G64` format. Since 
`R64G64` is an optional format and most (if not all) devices do not support it 
for texel buffers, we got all 0 outputs. 

This PR changes the lowering to pack `uint/int64_t2` typed buffer elements into 
4-component 32-bit images and bitcast during load/store. This approach mirrors 
DirectX, where `uint/int64_t2` is backed by 4x32-bit channels and reinterpreted 
in the shader. The PR also adds `RWBuffer&lt;uint/int64_t2&gt;` load/store 
tests to `Unsigned/SignedBufferLoadStore.ll`.

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


5 Files Affected:

- (modified) clang/lib/CodeGen/Targets/SPIR.cpp (+8) 
- (modified) clang/test/CodeGenHLSL/resources/RWBuffer-imageformat.hlsl (+2-2) 
- (modified) llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp (+43-14) 
- (modified) llvm/test/CodeGen/SPIRV/hlsl-resources/SignedBufferLoadStore.ll 
(+26) 
- (modified) llvm/test/CodeGen/SPIRV/hlsl-resources/UnsignedBufferLoadStore.ll 
(+26) 


``````````diff
diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp 
b/clang/lib/CodeGen/Targets/SPIR.cpp
index 88e7c7f9482a4..2c92fbde4f3c3 100644
--- a/clang/lib/CodeGen/Targets/SPIR.cpp
+++ b/clang/lib/CodeGen/Targets/SPIR.cpp
@@ -890,6 +890,14 @@ llvm::Type 
*CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource(
          "The element type for a SPIR-V resource must be a scalar integer or "
          "floating point type.");
 
+  // SPIR-V has no 64-bit multi-component image format, so pack a 2-component
+  // 64-bit typed buffer into a 4-component 32-bit image. The backend
+  // reinterprets it with OpBitcast on load and store.
+  if (SampledType->isIntegerTy(64) && NumChannels == 2) {
+    SampledType = llvm::Type::getInt32Ty(Ctx);
+    NumChannels = 4;
+  }
+
   // These parameters correspond to the operands to the OpTypeImage SPIR-V
   // instruction. See
   // https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpTypeImage.
diff --git a/clang/test/CodeGenHLSL/resources/RWBuffer-imageformat.hlsl 
b/clang/test/CodeGenHLSL/resources/RWBuffer-imageformat.hlsl
index aebee894f79d5..6b967bb1e3e3a 100644
--- a/clang/test/CodeGenHLSL/resources/RWBuffer-imageformat.hlsl
+++ b/clang/test/CodeGenHLSL/resources/RWBuffer-imageformat.hlsl
@@ -23,11 +23,11 @@ RWBuffer<uint4> rwb_uint4;
 // 64-bit integers
 // CHECK: %"class.hlsl::RWBuffer.7" = type { target("spirv.SignedImage", i64, 
5, 2, 0, 0, 2, 41) }
 RWBuffer<int64_t> rwb_i64;
-// CHECK: %"class.hlsl::RWBuffer.8" = type { target("spirv.SignedImage", i64, 
5, 2, 0, 0, 2, 0) }
+// CHECK: %"class.hlsl::RWBuffer.8" = type { target("spirv.SignedImage", i32, 
5, 2, 0, 0, 2, 21) }
 RWBuffer<int64_t2> rwb_i64_2;
 // CHECK: %"class.hlsl::RWBuffer.9" = type { target("spirv.Image", i64, 5, 2, 
0, 0, 2, 40) }
 RWBuffer<uint64_t> rwb_u64;
-// CHECK: %"class.hlsl::RWBuffer.10" = type { target("spirv.Image", i64, 5, 2, 
0, 0, 2, 0) }
+// CHECK: %"class.hlsl::RWBuffer.10" = type { target("spirv.Image", i32, 5, 2, 
0, 0, 2, 30) }
 RWBuffer<uint64_t2> rwb_u64_2;
 
 // Floats
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp 
b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 11128eadef95f..15bb399c2ca09 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -2073,6 +2073,24 @@ bool SPIRVInstructionSelector::selectStore(MachineInstr 
&I) const {
 
     Register IdxReg = IntPtrDef->getOperand(3).getReg();
     if (HandleType->getOpcode() == SPIRV::OpTypeImage) {
+      SPIRVTypeInst SampledType =
+          GR.getSPIRVTypeForVReg(HandleType->getOperand(1).getReg());
+      SPIRVTypeInst StoreValCompType =
+          GR.getScalarOrVectorComponentType(GR.getSPIRVTypeForVReg(StoreVal));
+      if (StoreValCompType && StoreValCompType != SampledType) {
+        // A wide element (e.g. int64_t2) is emulated with a narrower packed
+        // image. This bitcasts the value to match the format.
+        SPIRVTypeInst PackedType = widenTypeToVec4(SampledType, I);
+        Register PackedReg =
+            MRI->createVirtualRegister(GR.getRegClass(PackedType));
+        BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpBitcast))
+            .addDef(PackedReg)
+            .addUse(GR.getSPIRVTypeID(PackedType))
+            .addUse(StoreVal)
+            .constrainAllUses(TII, TRI, RBI);
+        StoreVal = PackedReg;
+      }
+
       auto BMI = BuildMI(*I.getParent(), I, I.getDebugLoc(),
                          TII.get(SPIRV::OpImageWrite))
                      .addUse(NewHandleReg)
@@ -6286,22 +6304,20 @@ bool SPIRVInstructionSelector::generateImageReadOrFetch(
   };
 
   uint64_t ResultSize = GR.getScalarOrVectorComponentCount(ResType);
-  if (ResultSize == 4) {
-    auto BMI =
-        BuildMI(*Pos.getParent(), Pos, Loc,
-                TII.get(IsFetch ? SPIRV::OpImageFetch : SPIRV::OpImageRead))
-            .addDef(ResVReg)
-            .addUse(GR.getSPIRVTypeID(ResType))
-            .addUse(ImageReg)
-            .addUse(IdxReg);
 
-    AddOperands(BMI);
-    BMI.constrainAllUses(TII, TRI, RBI);
-    return true;
-  }
+  // A wide element (e.g. int64_t2) is emulated with a narrower packed image, 
so
+  // its sampled type is different from the result.
+  SPIRVTypeInst SampledType =
+      GR.getSPIRVTypeForVReg(ImageType->getOperand(1).getReg());
+  bool IsPacked = SampledType != GR.getScalarOrVectorComponentType(ResType);
+  SPIRVTypeInst ReadType =
+      widenTypeToVec4(IsPacked ? SampledType : ResType, Pos);
+  bool ReadTypeMatchesResult = ReadType == ResType;
+  // Read directly into the result, or into a temporary to bitcast/extract.
+  Register ReadReg = ReadTypeMatchesResult
+                         ? ResVReg
+                         : 
MRI->createVirtualRegister(GR.getRegClass(ReadType));
 
-  SPIRVTypeInst ReadType = widenTypeToVec4(ResType, Pos);
-  Register ReadReg = MRI->createVirtualRegister(GR.getRegClass(ReadType));
   auto BMI =
       BuildMI(*Pos.getParent(), Pos, Loc,
               TII.get(IsFetch ? SPIRV::OpImageFetch : SPIRV::OpImageRead))
@@ -6312,6 +6328,19 @@ bool SPIRVInstructionSelector::generateImageReadOrFetch(
   AddOperands(BMI);
   BMI.constrainAllUses(TII, TRI, RBI);
 
+  if (ReadTypeMatchesResult)
+    return true;
+
+  if (IsPacked) {
+    // Reinterpret the packed vector as the wide result type.
+    BuildMI(*Pos.getParent(), Pos, Loc, TII.get(SPIRV::OpBitcast))
+        .addDef(ResVReg)
+        .addUse(GR.getSPIRVTypeID(ResType))
+        .addUse(ReadReg)
+        .constrainAllUses(TII, TRI, RBI);
+    return true;
+  }
+
   if (ResultSize == 1) {
     BuildMI(*Pos.getParent(), Pos, Loc, TII.get(SPIRV::OpCompositeExtract))
         .addDef(ResVReg)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/SignedBufferLoadStore.ll 
b/llvm/test/CodeGen/SPIRV/hlsl-resources/SignedBufferLoadStore.ll
index d15f88b5dd799..56ee2fd5ab011 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-resources/SignedBufferLoadStore.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/SignedBufferLoadStore.ll
@@ -2,10 +2,13 @@
 ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-vulkan1.3-library %s -o - 
-filetype=obj | spirv-val --target-env vulkan1.3 %}
 
 @.str.b0 = private unnamed_addr constant [3 x i8] c"B0\00", align 1
[email protected] = private unnamed_addr constant [3 x i8] c"B1\00", align 1
 
 ; CHECK-DAG: [[uint:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[ulong:%[0-9]+]] = OpTypeInt 64 0
 ; CHECK-DAG: [[v2int:%[0-9]+]] = OpTypeVector [[uint]] 2
 ; CHECK-DAG: [[v4int:%[0-9]+]] = OpTypeVector [[uint]] 4
+; CHECK-DAG: [[v2ulong:%[0-9]+]] = OpTypeVector [[ulong]] 2
 ; CHECK-DAG: [[zero:%[0-9]+]] = OpConstant [[uint]] 0
 ; CHECK-DAG: [[one:%[0-9]+]] = OpConstant [[uint]] 1
 ; CHECK-DAG: [[twenty:%[0-9]+]] = OpConstant [[uint]] 20
@@ -13,6 +16,9 @@
 ; CHECK-DAG: [[ImageType:%[0-9]+]] = OpTypeImage [[uint]] Buffer 2 0 0 2 
Unknown 
 ; CHECK-DAG: [[ImagePtr:%[0-9]+]] = OpTypePointer UniformConstant [[ImageType]]
 ; CHECK-DAG: [[Var:%[0-9]+]] = OpVariable [[ImagePtr]] UniformConstant
+; CHECK-DAG: [[PackedImageType:%[0-9]+]] = OpTypeImage [[uint]] Buffer 2 0 0 2 
Rgba32i
+; CHECK-DAG: [[PackedImagePtr:%[0-9]+]] = OpTypePointer UniformConstant 
[[PackedImageType]]
+; CHECK-DAG: [[PackedVar:%[0-9]+]] = OpVariable [[PackedImagePtr]] 
UniformConstant
 
 ; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind 
willreturn memory(readwrite, inaccessiblemem: none)
 ; CHECK: OpFunction
@@ -133,5 +139,25 @@ bb_both:
   ret void
 }
 
+; A 64-bit 2-component element has no representable image format, so it is 
packed into a 4-component 32-bit image.
+; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind 
willreturn memory(readwrite, inaccessiblemem: none)
+; CHECK: OpFunction
+define void @main_int64_vector2() local_unnamed_addr #0 {
+entry:
+; CHECK: [[H:%[0-9]+]] = OpLoad [[PackedImageType]] [[PackedVar]]
+  %s_h.i = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 21) 
@llvm.spv.resource.handlefrombinding.tspirv.SignedImage_i32_5_2_0_0_2_21t(i32 
4, i32 5, i32 1, i32 0, ptr nonnull @.str.b1)
+
+; CHECK: [[R:%[0-9]+]] = OpImageRead [[v4int]] [[H]] [[one]] SignExtend
+; CHECK: [[V:%[0-9]+]] = OpBitcast [[v2ulong]] [[R]]
+  %0 = tail call noundef nonnull align 16 dereferenceable(16) ptr 
@llvm.spv.resource.getpointer.p0.tspirv.SignedImage_i32_5_2_0_0_2_21t(target("spirv.SignedImage",
 i32, 5, 2, 0, 0, 2, 21) %s_h.i, i32 1)
+  %1 = load <2 x i64>, ptr %0, align 16
+
+; CHECK: [[W:%[0-9]+]] = OpBitcast [[v4int]] [[V]]
+; CHECK: OpImageWrite {{%[0-9]+}} [[zero]] [[W]] SignExtend
+  %2 = tail call noundef nonnull align 16 dereferenceable(16) ptr 
@llvm.spv.resource.getpointer.p0.tspirv.SignedImage_i32_5_2_0_0_2_21t(target("spirv.SignedImage",
 i32, 5, 2, 0, 0, 2, 21) %s_h.i, i32 0)
+  store <2 x i64> %1, ptr %2, align 16
+  ret void
+}
+
 attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind 
willreturn memory(readwrite, inaccessiblemem: none) "frame-pointer"="all" 
"hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" 
"stack-protector-buffer-size"="8" }
 attributes #1 = { mustprogress nocallback nofree nosync nounwind willreturn 
memory(none) }
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/UnsignedBufferLoadStore.ll 
b/llvm/test/CodeGen/SPIRV/hlsl-resources/UnsignedBufferLoadStore.ll
index d038c5ce2574d..59602d1be77bf 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-resources/UnsignedBufferLoadStore.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/UnsignedBufferLoadStore.ll
@@ -2,10 +2,13 @@
 ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-vulkan1.3-library %s -o - 
-filetype=obj | spirv-val --target-env vulkan1.3 %}
 
 @.str.b0 = private unnamed_addr constant [3 x i8] c"B0\00", align 1
[email protected] = private unnamed_addr constant [3 x i8] c"B1\00", align 1
 
 ; CHECK-DAG: [[uint:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[ulong:%[0-9]+]] = OpTypeInt 64 0
 ; CHECK-DAG: [[v2int:%[0-9]+]] = OpTypeVector [[uint]] 2
 ; CHECK-DAG: [[v4int:%[0-9]+]] = OpTypeVector [[uint]] 4
+; CHECK-DAG: [[v2ulong:%[0-9]+]] = OpTypeVector [[ulong]] 2
 ; CHECK-DAG: [[zero:%[0-9]+]] = OpConstant [[uint]] 0
 ; CHECK-DAG: [[one:%[0-9]+]] = OpConstant [[uint]] 1
 ; CHECK-DAG: [[twenty:%[0-9]+]] = OpConstant [[uint]] 20
@@ -13,6 +16,9 @@
 ; CHECK-DAG: [[ImageType:%[0-9]+]] = OpTypeImage [[uint]] Buffer 2 0 0 2 
Unknown 
 ; CHECK-DAG: [[ImagePtr:%[0-9]+]] = OpTypePointer UniformConstant [[ImageType]]
 ; CHECK-DAG: [[Var:%[0-9]+]] = OpVariable [[ImagePtr]] UniformConstant
+; CHECK-DAG: [[PackedImageType:%[0-9]+]] = OpTypeImage [[uint]] Buffer 2 0 0 2 
Rgba32ui
+; CHECK-DAG: [[PackedImagePtr:%[0-9]+]] = OpTypePointer UniformConstant 
[[PackedImageType]]
+; CHECK-DAG: [[PackedVar:%[0-9]+]] = OpVariable [[PackedImagePtr]] 
UniformConstant
 
 ; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind 
willreturn memory(readwrite, inaccessiblemem: none)
 ; CHECK: OpFunction
@@ -133,5 +139,25 @@ bb_both:
   ret void
 }
 
+; A 64-bit 2-component element has no representable image format, so it is 
packed into a 4-component 32-bit image.
+; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind 
willreturn memory(readwrite, inaccessiblemem: none)
+; CHECK: OpFunction
+define void @main_uint64_vector2() local_unnamed_addr #0 {
+entry:
+; CHECK: [[H:%[0-9]+]] = OpLoad [[PackedImageType]] [[PackedVar]]
+  %s_h.i = tail call target("spirv.Image", i32, 5, 2, 0, 0, 2, 30) 
@llvm.spv.resource.handlefrombinding.tspirv.Image_i32_5_2_0_0_2_30t(i32 4, i32 
5, i32 1, i32 0, ptr nonnull @.str.b1)
+
+; CHECK: [[R:%[0-9]+]] = OpImageRead [[v4int]] [[H]] [[one]]
+; CHECK: [[V:%[0-9]+]] = OpBitcast [[v2ulong]] [[R]]
+  %0 = tail call noundef nonnull align 16 dereferenceable(16) ptr 
@llvm.spv.resource.getpointer.p0.tspirv.Image_i32_5_2_0_0_2_30t(target("spirv.Image",
 i32, 5, 2, 0, 0, 2, 30) %s_h.i, i32 1)
+  %1 = load <2 x i64>, ptr %0, align 16
+
+; CHECK: [[W:%[0-9]+]] = OpBitcast [[v4int]] [[V]]
+; CHECK: OpImageWrite {{%[0-9]+}} [[zero]] [[W]]
+  %2 = tail call noundef nonnull align 16 dereferenceable(16) ptr 
@llvm.spv.resource.getpointer.p0.tspirv.Image_i32_5_2_0_0_2_30t(target("spirv.Image",
 i32, 5, 2, 0, 0, 2, 30) %s_h.i, i32 0)
+  store <2 x i64> %1, ptr %2, align 16
+  ret void
+}
+
 attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind 
willreturn memory(readwrite, inaccessiblemem: none) "frame-pointer"="all" 
"hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" 
"stack-protector-buffer-size"="8" }
 attributes #1 = { mustprogress nocallback nofree nosync nounwind willreturn 
memory(none) }

``````````

</details>


https://github.com/llvm/llvm-project/pull/208626
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to