From: bwlodarcz Date: Thu, 12 Oct 2023 15:13:50 +0000 (+0200) Subject: [PATCH 83/85] Add support for IAddCarry SPIRV instruction (#2167) X-Git-Tag: archive/raspbian/15.0.1-1+rpi1^2^2~4 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=9bb780eb92fc5eaa0b6cc82b8a1c10b715a9339d;p=spirv-llvm-translator-15.git [PATCH 83/85] Add support for IAddCarry SPIRV instruction (#2167) 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 --- diff --git a/lib/SPIRV/SPIRVReader.cpp b/lib/SPIRV/SPIRVReader.cpp index b842450..74ed304 100644 --- a/lib/SPIRV/SPIRVReader.cpp +++ b/lib/SPIRV/SPIRVReader.cpp @@ -2470,6 +2470,14 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F, case OpSignBitSet: return mapValue(BV, transRelational(static_cast(BV), BB)); + case OpIAddCarry: { + IRBuilder Builder(BB); + auto *BC = static_cast(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( diff --git a/lib/SPIRV/SPIRVWriter.cpp b/lib/SPIRV/SPIRVWriter.cpp index cc4034d..95e5bf8 100644 --- a/lib/SPIRV/SPIRVWriter.cpp +++ b/lib/SPIRV/SPIRVWriter.cpp @@ -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 diff --git a/lib/SPIRV/libSPIRV/SPIRVEntry.h b/lib/SPIRV/libSPIRV/SPIRVEntry.h index 0e0bd3c..a7b2c79 100644 --- a/lib/SPIRV/libSPIRV/SPIRVEntry.h +++ b/lib/SPIRV/libSPIRV/SPIRVEntry.h @@ -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) diff --git a/lib/SPIRV/libSPIRV/SPIRVInstruction.h b/lib/SPIRV/libSPIRV/SPIRVInstruction.h index 5d369db..9eaf1db 100644 --- a/lib/SPIRV/libSPIRV/SPIRVInstruction.h +++ b/lib/SPIRV/libSPIRV/SPIRVInstruction.h @@ -661,6 +661,7 @@ class SPIRVBinaryInst #define _SPIRV_OP(x) typedef SPIRVBinaryInst SPIRV##x; _SPIRV_OP(IAdd) +_SPIRV_OP(IAddCarry) _SPIRV_OP(FAdd) _SPIRV_OP(ISub) _SPIRV_OP(FSub) diff --git a/lib/SPIRV/libSPIRV/SPIRVOpCode.h b/lib/SPIRV/libSPIRV/SPIRVOpCode.h index da1f4b1..d95ed81 100644 --- a/lib/SPIRV/libSPIRV/SPIRVOpCode.h +++ b/lib/SPIRV/libSPIRV/SPIRVOpCode.h @@ -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 index 0000000..265e1f8 --- /dev/null +++ b/test/llvm-intrinsics/uadd.with.overflow.ll @@ -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)