Skip to content

Commit 473c348

Browse files
bwlodarczMrSidims
authored andcommitted
Add support for ISubBorrow SPIRV instruction (KhronosGroup#2168)
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.
1 parent eb23080 commit 473c348

File tree

6 files changed

+75
-3
lines changed

6 files changed

+75
-3
lines changed

lib/SPIRV/SPIRVReader.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2392,13 +2392,21 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
23922392
return mapValue(BV,
23932393
transRelational(static_cast<SPIRVInstruction *>(BV), BB));
23942394
case OpIAddCarry: {
2395-
IRBuilder Builder(BB);
2395+
IRBuilder<> Builder(BB);
23962396
auto *BC = static_cast<SPIRVBinary *>(BV);
23972397
return mapValue(BV, Builder.CreateBinaryIntrinsic(
23982398
Intrinsic::uadd_with_overflow,
23992399
transValue(BC->getOperand(0), F, BB),
24002400
transValue(BC->getOperand(1), F, BB)));
24012401
}
2402+
case OpISubBorrow: {
2403+
IRBuilder<> Builder(BB);
2404+
auto *BC = static_cast<SPIRVBinary *>(BV);
2405+
return mapValue(BV, Builder.CreateBinaryIntrinsic(
2406+
Intrinsic::usub_with_overflow,
2407+
transValue(BC->getOperand(0), F, BB),
2408+
transValue(BC->getOperand(1), F, BB)));
2409+
}
24022410
case OpGetKernelWorkGroupSize:
24032411
case OpGetKernelPreferredWorkGroupSizeMultiple:
24042412
return mapValue(

lib/SPIRV/SPIRVWriter.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3493,6 +3493,7 @@ bool LLVMToSPIRVBase::isKnownIntrinsic(Intrinsic::ID Id) {
34933493
case Intrinsic::trap:
34943494
case Intrinsic::arithmetic_fence:
34953495
case Intrinsic::uadd_with_overflow:
3496+
case Intrinsic::usub_with_overflow:
34963497
return true;
34973498
default:
34983499
// Unknown intrinsics' declarations should always be translated
@@ -3918,6 +3919,11 @@ SPIRVValue *LLVMToSPIRVBase::transIntrinsicInst(IntrinsicInst *II,
39183919
transValue(II->getArgOperand(0), BB),
39193920
transValue(II->getArgOperand(1), BB), BB);
39203921
}
3922+
case Intrinsic::usub_with_overflow: {
3923+
return BM->addBinaryInst(OpISubBorrow, transType(II->getType()),
3924+
transValue(II->getArgOperand(0), BB),
3925+
transValue(II->getArgOperand(1), BB), BB);
3926+
}
39213927
case Intrinsic::memset: {
39223928
// Generally there is no direct mapping of memset to SPIR-V. But it turns
39233929
// out that memset is emitted by Clang for initialization in default

lib/SPIRV/libSPIRV/SPIRVEntry.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1004,7 +1004,6 @@ _SPIRV_OP(ImageDrefGather)
10041004
_SPIRV_OP(QuantizeToF16)
10051005
_SPIRV_OP(ArrayLength)
10061006
_SPIRV_OP(OuterProduct)
1007-
_SPIRV_OP(ISubBorrow)
10081007
_SPIRV_OP(SMulExtended)
10091008
_SPIRV_OP(UMulExtended)
10101009
_SPIRV_OP(DPdx)

lib/SPIRV/libSPIRV/SPIRVInstruction.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,7 @@ _SPIRV_OP(IAdd)
665665
_SPIRV_OP(IAddCarry)
666666
_SPIRV_OP(FAdd)
667667
_SPIRV_OP(ISub)
668+
_SPIRV_OP(ISubBorrow)
668669
_SPIRV_OP(FSub)
669670
_SPIRV_OP(IMul)
670671
_SPIRV_OP(FMul)

lib/SPIRV/libSPIRV/SPIRVOpCode.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ inline bool isAtomicOpCode(Op OpCode) {
7171
}
7272
inline bool isBinaryOpCode(Op OpCode) {
7373
return ((unsigned)OpCode >= OpIAdd && (unsigned)OpCode <= OpFMod) ||
74-
OpCode == OpDot || OpCode == OpIAddCarry;
74+
OpCode == OpDot || OpCode == OpIAddCarry || OpCode == OpISubBorrow;
7575
}
7676

7777
inline bool isShiftOpCode(Op OpCode) {
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
; RUN: llvm-as %s -o %t.bc
2+
; RUN: llvm-spirv %t.bc -spirv-text -o - | FileCheck --check-prefix CHECK-SPIRV %s
3+
; RUN: llvm-spirv %t.bc -o %t.spv
4+
; Current implementation doesn't comply with specification and should be fixed in future.
5+
; TODO: spirv-val %t.spv
6+
; RUN: llvm-spirv -r %t.spv -o %t.rev.bc
7+
; RUN: llvm-dis %t.rev.bc -o - | FileCheck --check-prefix CHECK-LLVM %s
8+
9+
target triple = "spir64-unknown-unknown"
10+
11+
12+
; CHECK-SPIRV: TypeInt [[#I16TYPE:]] 16
13+
; CHECK-SPIRV: TypeInt [[#I32TYPE:]] 32
14+
; CHECK-SPIRV: TypeInt [[#I64TYPE:]] 64
15+
; CHECK-SPIRV: TypeBool [[#BTYPE:]]
16+
; CHECK-SPIRV: TypeStruct [[#S0TYPE:]] [[#I16TYPE]] [[#BTYPE]]
17+
; CHECK-SPIRV: TypeStruct [[#S1TYPE:]] [[#I32TYPE]] [[#BTYPE]]
18+
; CHECK-SPIRV: TypeStruct [[#S2TYPE:]] [[#I64TYPE]] [[#BTYPE]]
19+
; CHECK-SPIRV: TypeVector [[#V4XI32TYPE:]] [[#I32TYPE]] 4
20+
; CHECK-SPIRV: TypeVector [[#V4XBTYPE:]] [[#BTYPE]] 4
21+
; CHECK-SPIRV: TypeStruct [[#S3TYPE:]] [[#V4XI32TYPE]] [[#V4XBTYPE]]
22+
; CHECK-SPIRV: ISubBorrow [[#S0TYPE]]
23+
; CHECK-SPIRV: ISubBorrow [[#S1TYPE]]
24+
; CHECK-SPIRV: ISubBorrow [[#S2TYPE]]
25+
; CHECK-SPIRV: ISubBorrow [[#S3TYPE]]
26+
; CHECK-LLVM: call { i16, i1 } @llvm.usub.with.overflow.i16(i16 %a, i16 %b)
27+
; CHECK-LLVM: call { i32, i1 } @llvm.usub.with.overflow.i32(i32 %a, i32 %b)
28+
; CHECK-LLVM: call { i64, i1 } @llvm.usub.with.overflow.i64(i64 %a, i64 %b)
29+
; CHECK-LLVM: call { <4 x i32>, <4 x i1> } @llvm.usub.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b)
30+
31+
define spir_func void @test_usub_with_overflow_i16(i16 %a, i16 %b) {
32+
entry:
33+
%res = call {i16, i1} @llvm.usub.with.overflow.i16(i16 %a, i16 %b)
34+
ret void
35+
}
36+
37+
define spir_func void @test_usub_with_overflow_i32(i32 %a, i32 %b) {
38+
entry:
39+
%res = call {i32, i1} @llvm.usub.with.overflow.i32(i32 %a, i32 %b)
40+
ret void
41+
}
42+
43+
define spir_func void @test_usub_with_overflow_i64(i64 %a, i64 %b) {
44+
entry:
45+
%res = call {i64, i1} @llvm.usub.with.overflow.i64(i64 %a, i64 %b)
46+
ret void
47+
}
48+
49+
define spir_func void @test_usub_with_overflow_v4i32(<4 x i32> %a, <4 x i32> %b) {
50+
entry:
51+
%res = call {<4 x i32>, <4 x i1>} @llvm.usub.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b)
52+
ret void
53+
}
54+
55+
declare {i16, i1} @llvm.usub.with.overflow.i16(i16 %a, i16 %b)
56+
declare {i32, i1} @llvm.usub.with.overflow.i32(i32 %a, i32 %b)
57+
declare {i64, i1} @llvm.usub.with.overflow.i64(i64 %a, i64 %b)
58+
declare {<4 x i32>, <4 x i1>} @llvm.usub.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b)

0 commit comments

Comments
 (0)