diff --git a/lib/SPIRV/SPIRVReader.cpp b/lib/SPIRV/SPIRVReader.cpp index 8c1f418fd6..3a05846c5b 100644 --- a/lib/SPIRV/SPIRVReader.cpp +++ b/lib/SPIRV/SPIRVReader.cpp @@ -2340,13 +2340,21 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F, return mapValue(BV, transRelational(static_cast(BV), BB)); case OpIAddCarry: { - IRBuilder Builder(BB); + 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 OpISubBorrow: { + IRBuilder<> Builder(BB); + auto *BC = static_cast(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( diff --git a/lib/SPIRV/SPIRVWriter.cpp b/lib/SPIRV/SPIRVWriter.cpp index 5fef4b5949..4df81d846b 100644 --- a/lib/SPIRV/SPIRVWriter.cpp +++ b/lib/SPIRV/SPIRVWriter.cpp @@ -2538,6 +2538,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 @@ -2955,6 +2956,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 diff --git a/lib/SPIRV/libSPIRV/SPIRVEntry.h b/lib/SPIRV/libSPIRV/SPIRVEntry.h index b5bd87b460..e7398c7a7c 100644 --- a/lib/SPIRV/libSPIRV/SPIRVEntry.h +++ b/lib/SPIRV/libSPIRV/SPIRVEntry.h @@ -1005,7 +1005,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) diff --git a/lib/SPIRV/libSPIRV/SPIRVInstruction.h b/lib/SPIRV/libSPIRV/SPIRVInstruction.h index 1ea7c5af1f..0fa6327fa3 100644 --- a/lib/SPIRV/libSPIRV/SPIRVInstruction.h +++ b/lib/SPIRV/libSPIRV/SPIRVInstruction.h @@ -663,6 +663,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) diff --git a/lib/SPIRV/libSPIRV/SPIRVOpCode.h b/lib/SPIRV/libSPIRV/SPIRVOpCode.h index 35e48044b8..288a2c0fde 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 == 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 index 0000000000..74588ee7e3 --- /dev/null +++ b/test/llvm-intrinsics/usub.with.overflow.ll @@ -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)