[PATCH 83/85] Add support for IAddCarry SPIRV instruction (#2167)
authorbwlodarcz <bertrand.wlodarczyk@intel.com>
Thu, 12 Oct 2023 15:13:50 +0000 (17:13 +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.uadd.with.overflow and the IAddCarry intrinsic.
Intrinsic llvm.uadd.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 IAddCarry needs to have both elements of the same type.
In result, current implementation is not compliant and should be considered temporary.

Gbp-Pq: Name 0083-Add-support-for-IAddCarry-SPIRV-instruction-2167.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/uadd.with.overflow.ll [new file with mode: 0644]

index b8424503107fcb38f2531c9a80a562ae0734dd79..74ed304030c2e293af796c8e8e652135ad30dfc1 100644 (file)
@@ -2470,6 +2470,14 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
   case OpSignBitSet:
     return mapValue(BV,
                     transRelational(static_cast<SPIRVInstruction *>(BV), BB));
+  case OpIAddCarry: {
+    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 OpGetKernelWorkGroupSize:
   case OpGetKernelPreferredWorkGroupSizeMultiple:
     return mapValue(
index cc4034de41c66d52699b0f5d58ad26a3e88acb8a..95e5bf82f96b58a82589f0da1f01b462c8802b05 100644 (file)
@@ -3363,6 +3363,7 @@ bool LLVMToSPIRVBase::isKnownIntrinsic(Intrinsic::ID Id) {
   case Intrinsic::dbg_label:
   case Intrinsic::trap:
   case Intrinsic::arithmetic_fence:
+  case Intrinsic::uadd_with_overflow:
     return true;
   default:
     // Unknown intrinsics' declarations should always be translated
@@ -3776,6 +3777,11 @@ SPIRVValue *LLVMToSPIRVBase::transIntrinsicInst(IntrinsicInst *II,
     SPIRVValue *Zero = transValue(Constant::getNullValue(II->getType()), BB);
     return BM->addSelectInst(Cmp, Sub, Zero, BB);
   }
+  case Intrinsic::uadd_with_overflow: {
+    return BM->addBinaryInst(OpIAddCarry, 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 0e0bd3ce16459b6edae5879da2196945981c6ef5..a7b2c794e1ab79de9b153f0035cd647aeaca1512 100644 (file)
@@ -1006,7 +1006,6 @@ _SPIRV_OP(ImageDrefGather)
 _SPIRV_OP(QuantizeToF16)
 _SPIRV_OP(ArrayLength)
 _SPIRV_OP(OuterProduct)
-_SPIRV_OP(IAddCarry)
 _SPIRV_OP(ISubBorrow)
 _SPIRV_OP(SMulExtended)
 _SPIRV_OP(UMulExtended)
index 5d369db3356c13318126d34eb64f6f7e9fc12142..9eaf1db3590c515895a4b9bc0fe7be65b77fd394 100644 (file)
@@ -661,6 +661,7 @@ class SPIRVBinaryInst
 
 #define _SPIRV_OP(x) typedef SPIRVBinaryInst<Op##x> SPIRV##x;
 _SPIRV_OP(IAdd)
+_SPIRV_OP(IAddCarry)
 _SPIRV_OP(FAdd)
 _SPIRV_OP(ISub)
 _SPIRV_OP(FSub)
index da1f4b1a1e514f2382c8a7c198a582a5f6e33f2f..d95ed81e0c0139a9ed34cedaf362cae98ae2ba87 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 == OpDot || OpCode == OpIAddCarry;
 }
 
 inline bool isShiftOpCode(Op OpCode) {
diff --git a/test/llvm-intrinsics/uadd.with.overflow.ll b/test/llvm-intrinsics/uadd.with.overflow.ll
new file mode 100644 (file)
index 0000000..265e1f8
--- /dev/null
@@ -0,0 +1,57 @@
+; 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: IAddCarry [[#S0TYPE]]
+; CHECK-SPIRV: IAddCarry [[#S1TYPE]]
+; CHECK-SPIRV: IAddCarry [[#S2TYPE]]
+; CHECK-SPIRV: IAddCarry [[#S3TYPE]]
+; CHECK-LLVM: call { i16, i1 } @llvm.uadd.with.overflow.i16(i16 %a, i16 %b)
+; CHECK-LLVM: call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %a, i32 %b)
+; CHECK-LLVM: call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %a, i64 %b)
+; CHECK-LLVM: call { <4 x i32>, <4 x i1> } @llvm.uadd.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b)
+
+define spir_func void @test_uadd_with_overflow_i16(i16 %a, i16 %b) {
+entry:
+  %res = call {i16, i1} @llvm.uadd.with.overflow.i16(i16 %a, i16 %b)
+  ret void
+}
+
+define spir_func void @test_uadd_with_overflow_i32(i32 %a, i32 %b) {
+entry:
+  %res = call {i32, i1} @llvm.uadd.with.overflow.i32(i32 %a, i32 %b)
+  ret void
+}
+
+define spir_func void @test_uadd_with_overflow_i64(i64 %a, i64 %b) {
+entry:
+  %res = call {i64, i1} @llvm.uadd.with.overflow.i64(i64 %a, i64 %b)
+  ret void
+}
+
+define spir_func void @test_uadd_with_overflow_v4i32(<4 x i32> %a, <4 x i32> %b) {
+entry:
+ %res = call {<4 x i32>, <4 x i1>} @llvm.uadd.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b) 
+ ret void
+}
+
+declare {i16, i1} @llvm.uadd.with.overflow.i16(i16 %a, i16 %b)
+declare {i32, i1} @llvm.uadd.with.overflow.i32(i32 %a, i32 %b)
+declare {i64, i1} @llvm.uadd.with.overflow.i64(i64 %a, i64 %b)
+declare {<4 x i32>, <4 x i1>} @llvm.uadd.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b)