[PATCH 85/85] [Backport to 15] Handle OpVectorShuffle with differing vector sizes...
authorSven van Haastregt <sven.vanhaastregt@arm.com>
Wed, 6 Mar 2024 15:18:41 +0000 (16:18 +0100)
committerAndreas Beckmann <anbe@debian.org>
Thu, 14 Mar 2024 19:01:08 +0000 (20:01 +0100)
The SPIR-V to LLVM conversion would bail out when encountering an
`OpVectorShuffle` whose vector operands differ in size.  SPIR-V
allows differing vector sizes, but LLVM's `shufflevector` does not.

Remove the assert and insert an additional `shufflevector` to align
the vector operands when needed.

(cherry picked from commit 3df5e38250a6d7c50b58fbb0393be81d909487ff)

Gbp-Pq: Name 0085-Backport-to-15-Handle-OpVectorShuffle-with-differing.patch

lib/SPIRV/SPIRVInternal.h
lib/SPIRV/SPIRVReader.cpp
lib/SPIRV/SPIRVUtil.cpp
lib/SPIRV/libSPIRV/SPIRVInstruction.h
test/OpVectorShuffle.spvasm [new file with mode: 0644]

index 9adb620e8f407db1be730256197f725fc5dfbe50..e0dd4b6d86475b4c3c386dcc2d1132cd046c07e4 100644 (file)
@@ -59,6 +59,7 @@ using namespace llvm;
 
 namespace llvm {
 class IntrinsicInst;
+class IRBuilderBase;
 }
 
 namespace SPIRV {
@@ -573,6 +574,10 @@ std::string mapLLVMTypeToOCLType(const Type *Ty, bool Signed,
                                  Type *PointerElementType = nullptr);
 SPIRVDecorate *mapPostfixToDecorate(StringRef Postfix, SPIRVEntry *Target);
 
+/// Return vector V extended with poison elements to match the number of
+/// components of NewType.
+Value *extendVector(Value *V, FixedVectorType *NewType, IRBuilderBase &Builder);
+
 /// Add decorations to a SPIR-V entry.
 /// \param Decs Each string is a postfix without _ at the beginning.
 SPIRVValue *addDecorations(SPIRVValue *Target,
index 0d6dbf284013c8570a1eaca7902a03f1f0a4ba58..939bdd3043afd44b7736a0885bd9694118b386e3 100644 (file)
@@ -2305,10 +2305,37 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
     if (BB) {
       Builder.SetInsertPoint(BB);
     }
-    return mapValue(BV, Builder.CreateShuffleVector(
-                            transValue(VS->getVector1(), F, BB),
-                            transValue(VS->getVector2(), F, BB),
-                            ConstantVector::get(Components), BV->getName()));
+    Value *Vec1 = transValue(VS->getVector1(), F, BB);
+    Value *Vec2 = transValue(VS->getVector2(), F, BB);
+    auto *Vec1Ty = cast<FixedVectorType>(Vec1->getType());
+    auto *Vec2Ty = cast<FixedVectorType>(Vec2->getType());
+    if (Vec1Ty->getNumElements() != Vec2Ty->getNumElements()) {
+      // LLVM's shufflevector requires that the two vector operands have the
+      // same type; SPIR-V's OpVectorShuffle allows the vector operands to
+      // differ in the number of components.  Adjust for that by extending
+      // the smaller vector.
+      if (Vec1Ty->getNumElements() < Vec2Ty->getNumElements()) {
+        Vec1 = extendVector(Vec1, Vec2Ty, Builder);
+        // Extending Vec1 requires offsetting any Vec2 indices in Components by
+        // the number of new elements.
+        unsigned Offset = Vec2Ty->getNumElements() - Vec1Ty->getNumElements();
+        unsigned Vec2Start = Vec1Ty->getNumElements();
+        for (auto &C : Components) {
+          if (auto *CI = dyn_cast<ConstantInt>(C)) {
+            uint64_t V = CI->getZExtValue();
+            if (V >= Vec2Start) {
+              // This is a Vec2 index; add the offset to it.
+              C = ConstantInt::get(Int32Ty, V + Offset);
+            }
+          }
+        }
+      } else {
+        Vec2 = extendVector(Vec2, Vec1Ty, Builder);
+      }
+    }
+    return mapValue(
+        BV, Builder.CreateShuffleVector(
+                Vec1, Vec2, ConstantVector::get(Components), BV->getName()));
   }
 
   case OpBitReverse: {
index 88b3684fd626d9bc8306b3c6193fa27eed23d9bb..49b468127a72104afe626f71c5724ab1a1f53cd2 100644 (file)
@@ -90,6 +90,24 @@ void removeFnAttr(CallInst *Call, Attribute::AttrKind Attr) {
   Call->removeFnAttr(Attr);
 }
 
+Value *extendVector(Value *V, FixedVectorType *NewType,
+                    IRBuilderBase &Builder) {
+  unsigned OldSize = cast<FixedVectorType>(V->getType())->getNumElements();
+  unsigned NewSize = NewType->getNumElements();
+  assert(OldSize < NewSize);
+  std::vector<Constant *> Components;
+  IntegerType *Int32Ty = Builder.getInt32Ty();
+  for (unsigned I = 0; I < NewSize; I++) {
+    if (I < OldSize)
+      Components.push_back(ConstantInt::get(Int32Ty, I));
+    else
+      Components.push_back(PoisonValue::get(Int32Ty));
+  }
+
+  return Builder.CreateShuffleVector(V, PoisonValue::get(V->getType()),
+                                     ConstantVector::get(Components), "vecext");
+}
+
 void saveLLVMModule(Module *M, const std::string &OutputFile) {
   std::error_code EC;
   ToolOutputFile Out(OutputFile.c_str(), EC, sys::fs::OF_None);
index bd18313915f0e9b074d0186cf7db5fdccbc04a84..fff2a5c78fbc1767f76747d87253eeb94d352067 100644 (file)
@@ -2201,15 +2201,11 @@ public:
 protected:
   void validate() const override {
     SPIRVInstruction::validate();
-    SPIRVId Vector1 = Ops[0];
-    SPIRVId Vector2 = Ops[1];
+    [[maybe_unused]] SPIRVId Vector1 = Ops[0];
     assert(OpCode == OpVectorShuffle);
     assert(Type->isTypeVector());
     assert(Type->getVectorComponentType() ==
            getValueType(Vector1)->getVectorComponentType());
-    if (getValue(Vector1)->isForward() || getValue(Vector2)->isForward())
-      return;
-    assert(getValueType(Vector1) == getValueType(Vector2));
     assert(Ops.size() - 2 == Type->getVectorComponentCount());
   }
 };
diff --git a/test/OpVectorShuffle.spvasm b/test/OpVectorShuffle.spvasm
new file mode 100644 (file)
index 0000000..9bcbe51
--- /dev/null
@@ -0,0 +1,36 @@
+; REQUIRES: spirv-as
+; RUN: spirv-as --target-env spv1.0 -o %t.spv %s
+; RUN: spirv-val %t.spv
+; RUN: llvm-spirv -r -o - %t.spv | llvm-dis | FileCheck %s
+               OpCapability Addresses
+               OpCapability Kernel
+               OpMemoryModel Physical32 OpenCL
+               OpEntryPoint Kernel %1 "testVecShuffle"
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+     %uintv2 = OpTypeVector %uint 2
+     %uintv3 = OpTypeVector %uint 3
+     %uintv4 = OpTypeVector %uint 4
+       %func = OpTypeFunction %void %uintv2 %uintv3
+
+          %1 = OpFunction %void None %func
+        %pv2 = OpFunctionParameter %uintv2
+        %pv3 = OpFunctionParameter %uintv3
+      %entry = OpLabel
+
+               ; Same vector lengths
+        %vs1 = OpVectorShuffle %uintv4 %pv3 %pv3 0 1 3 5
+; CHECK: shufflevector <3 x i32> %[[#]], <3 x i32> %[[#]], <4 x i32> <i32 0, i32 1, i32 3, i32 5>
+
+               ; vec1 smaller than vec2
+        %vs2 = OpVectorShuffle %uintv4 %pv2 %pv3 0 1 3 4
+; CHECK: %[[VS2EXT:[0-9a-z]+]] = shufflevector <2 x i32> %0, <2 x i32> poison, <3 x i32> <i32 0, i32 1, i32 undef>
+; CHECK: shufflevector <3 x i32> %[[VS2EXT]], <3 x i32> %[[#]], <4 x i32> <i32 0, i32 1, i32 4, i32 5>
+
+               ; vec1 larger than vec2
+        %vs3 = OpVectorShuffle %uintv4 %pv3 %pv2 0 1 3 4
+; CHECK: %[[VS3EXT:[0-9a-z]+]] = shufflevector <2 x i32> %0, <2 x i32> poison, <3 x i32> <i32 0, i32 1, i32 undef>
+; CHECK: shufflevector <3 x i32> %[[#]], <3 x i32> %[[VS3EXT]], <4 x i32> <i32 0, i32 1, i32 3, i32 4>
+
+               OpReturn
+               OpFunctionEnd