[PATCH 84/85] Add support for ISubBorrow SPIRV instruction (#2168)
authorbwlodarcz <bertrand.wlodarczyk@intel.com>
Fri, 13 Oct 2023 10:33:06 +0000 (12:33 +0200)
committerAndreas Beckmann <anbe@debian.org>
Thu, 14 Mar 2024 19:01:08 +0000 (20:01 +0100)
This commit implements bidirectional translation of the llvm.usub.with.overflow and the ISubBorrow intrinsic.
Intrinsic llvm.usub.with.overflow returns struct which second element have a type of i1.
The llvm type i1 is, in llvm-spirv, directly translated to BoolType.
SPIRV specification requires that the composite which returns from ISubBorrow needs to have both elements of the same type.
In result, current implementation is not compliant and should be considered temporary.

Gbp-Pq: Name 0084-Add-support-for-ISubBorrow-SPIRV-instruction-2168.patch

lib/SPIRV/SPIRVReader.cpp
lib/SPIRV/SPIRVWriter.cpp
lib/SPIRV/libSPIRV/SPIRVEntry.h
lib/SPIRV/libSPIRV/SPIRVInstruction.h
lib/SPIRV/libSPIRV/SPIRVOpCode.h
test/llvm-intrinsics/usub.with.overflow.ll [new file with mode: 0644]

index 74ed304030c2e293af796c8e8e652135ad30dfc1..0d6dbf284013c8570a1eaca7902a03f1f0a4ba58 100644 (file)
@@ -2471,13 +2471,21 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
     return mapValue(BV,
                     transRelational(static_cast<SPIRVInstruction *>(BV), BB));
   case OpIAddCarry: {
-    IRBuilder Builder(BB);
+    IRBuilder<> Builder(BB);
     auto *BC = static_cast<SPIRVBinary *>(BV);
     return mapValue(BV, Builder.CreateBinaryIntrinsic(
                             Intrinsic::uadd_with_overflow,
                             transValue(BC->getOperand(0), F, BB),
                             transValue(BC->getOperand(1), F, BB)));
   }
+  case OpISubBorrow: {
+    IRBuilder<> Builder(BB);
+    auto *BC = static_cast<SPIRVBinary *>(BV);
+    return mapValue(BV, Builder.CreateBinaryIntrinsic(
+                            Intrinsic::usub_with_overflow,
+                            transValue(BC->getOperand(0), F, BB),
+                            transValue(BC->getOperand(1), F, BB)));
+  }
   case OpGetKernelWorkGroupSize:
   case OpGetKernelPreferredWorkGroupSizeMultiple:
     return mapValue(
index 95e5bf82f96b58a82589f0da1f01b462c8802b05..ef122036bca9a279b0508add3abffc99d8a777bb 100644 (file)
@@ -3364,6 +3364,7 @@ bool LLVMToSPIRVBase::isKnownIntrinsic(Intrinsic::ID Id) {
   case Intrinsic::trap:
   case Intrinsic::arithmetic_fence:
   case Intrinsic::uadd_with_overflow:
+  case Intrinsic::usub_with_overflow:
     return true;
   default:
     // Unknown intrinsics' declarations should always be translated
@@ -3782,6 +3783,11 @@ SPIRVValue *LLVMToSPIRVBase::transIntrinsicInst(IntrinsicInst *II,
                              transValue(II->getArgOperand(0), BB),
                              transValue(II->getArgOperand(1), BB), BB);
   }
+  case Intrinsic::usub_with_overflow: {
+    return BM->addBinaryInst(OpISubBorrow, transType(II->getType()),
+                             transValue(II->getArgOperand(0), BB),
+                             transValue(II->getArgOperand(1), BB), BB);
+  }
   case Intrinsic::memset: {
     // Generally there is no direct mapping of memset to SPIR-V.  But it turns
     // out that memset is emitted by Clang for initialization in default
index a7b2c794e1ab79de9b153f0035cd647aeaca1512..9e7217aa503133fd4617042624e16e703cabd988 100644 (file)
@@ -1006,7 +1006,6 @@ _SPIRV_OP(ImageDrefGather)
 _SPIRV_OP(QuantizeToF16)
 _SPIRV_OP(ArrayLength)
 _SPIRV_OP(OuterProduct)
-_SPIRV_OP(ISubBorrow)
 _SPIRV_OP(SMulExtended)
 _SPIRV_OP(UMulExtended)
 _SPIRV_OP(DPdx)
index 9eaf1db3590c515895a4b9bc0fe7be65b77fd394..bd18313915f0e9b074d0186cf7db5fdccbc04a84 100644 (file)
@@ -664,6 +664,7 @@ _SPIRV_OP(IAdd)
 _SPIRV_OP(IAddCarry)
 _SPIRV_OP(FAdd)
 _SPIRV_OP(ISub)
+_SPIRV_OP(ISubBorrow)
 _SPIRV_OP(FSub)
 _SPIRV_OP(IMul)
 _SPIRV_OP(FMul)
index d95ed81e0c0139a9ed34cedaf362cae98ae2ba87..173b2496d44bdf5a09b69a1eff8401a32bbbfae1 100644 (file)
@@ -71,7 +71,7 @@ inline bool isAtomicOpCode(Op OpCode) {
 }
 inline bool isBinaryOpCode(Op OpCode) {
   return ((unsigned)OpCode >= OpIAdd && (unsigned)OpCode <= OpFMod) ||
-         OpCode == OpDot || OpCode == OpIAddCarry;
+         OpCode == OpDot || OpCode == OpIAddCarry || OpCode == OpISubBorrow;
 }
 
 inline bool isShiftOpCode(Op OpCode) {
diff --git a/test/llvm-intrinsics/usub.with.overflow.ll b/test/llvm-intrinsics/usub.with.overflow.ll
new file mode 100644 (file)
index 0000000..74588ee
--- /dev/null
@@ -0,0 +1,58 @@
+; RUN: llvm-as %s -o %t.bc
+; RUN: llvm-spirv %t.bc -spirv-text -o - | FileCheck --check-prefix CHECK-SPIRV %s
+; RUN: llvm-spirv %t.bc -o %t.spv
+; Current implementation doesn't comply with specification and should be fixed in future.
+; TODO: spirv-val %t.spv
+; RUN: llvm-spirv -r %t.spv -o %t.rev.bc
+; RUN: llvm-dis %t.rev.bc -o - | FileCheck --check-prefix CHECK-LLVM %s
+
+target triple = "spir64-unknown-unknown"
+
+
+; CHECK-SPIRV: TypeInt [[#I16TYPE:]] 16
+; CHECK-SPIRV: TypeInt [[#I32TYPE:]] 32
+; CHECK-SPIRV: TypeInt [[#I64TYPE:]] 64
+; CHECK-SPIRV: TypeBool [[#BTYPE:]]
+; CHECK-SPIRV: TypeStruct [[#S0TYPE:]] [[#I16TYPE]] [[#BTYPE]]
+; CHECK-SPIRV: TypeStruct [[#S1TYPE:]] [[#I32TYPE]] [[#BTYPE]]
+; CHECK-SPIRV: TypeStruct [[#S2TYPE:]] [[#I64TYPE]] [[#BTYPE]]
+; CHECK-SPIRV: TypeVector [[#V4XI32TYPE:]] [[#I32TYPE]] 4
+; CHECK-SPIRV: TypeVector [[#V4XBTYPE:]] [[#BTYPE]] 4
+; CHECK-SPIRV: TypeStruct [[#S3TYPE:]] [[#V4XI32TYPE]] [[#V4XBTYPE]]
+; CHECK-SPIRV: ISubBorrow [[#S0TYPE]]
+; CHECK-SPIRV: ISubBorrow [[#S1TYPE]]
+; CHECK-SPIRV: ISubBorrow [[#S2TYPE]]
+; CHECK-SPIRV: ISubBorrow [[#S3TYPE]]
+; CHECK-LLVM: call { i16, i1 } @llvm.usub.with.overflow.i16(i16 %a, i16 %b)
+; CHECK-LLVM: call { i32, i1 } @llvm.usub.with.overflow.i32(i32 %a, i32 %b)
+; CHECK-LLVM: call { i64, i1 } @llvm.usub.with.overflow.i64(i64 %a, i64 %b)
+; CHECK-LLVM: call { <4 x i32>, <4 x i1> } @llvm.usub.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b)
+
+define spir_func void @test_usub_with_overflow_i16(i16 %a, i16 %b) {
+entry:
+  %res = call {i16, i1} @llvm.usub.with.overflow.i16(i16 %a, i16 %b)
+  ret void
+}
+
+define spir_func void @test_usub_with_overflow_i32(i32 %a, i32 %b) {
+entry:
+  %res = call {i32, i1} @llvm.usub.with.overflow.i32(i32 %a, i32 %b)
+  ret void
+}
+
+define spir_func void @test_usub_with_overflow_i64(i64 %a, i64 %b) {
+entry:
+  %res = call {i64, i1} @llvm.usub.with.overflow.i64(i64 %a, i64 %b)
+  ret void
+}
+
+define spir_func void @test_usub_with_overflow_v4i32(<4 x i32> %a, <4 x i32> %b) {
+entry:
+ %res = call {<4 x i32>, <4 x i1>} @llvm.usub.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b)
+ ret void
+}
+
+declare {i16, i1} @llvm.usub.with.overflow.i16(i16 %a, i16 %b)
+declare {i32, i1} @llvm.usub.with.overflow.i32(i32 %a, i32 %b)
+declare {i64, i1} @llvm.usub.with.overflow.i64(i64 %a, i64 %b)
+declare {<4 x i32>, <4 x i1>} @llvm.usub.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b)