Skip to content

Commit 6d7e3df

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 2a2b81c commit 6d7e3df

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
@@ -2471,13 +2471,21 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
24712471
return mapValue(BV,
24722472
transRelational(static_cast<SPIRVInstruction *>(BV), BB));
24732473
case OpIAddCarry: {
2474-
IRBuilder Builder(BB);
2474+
IRBuilder<> Builder(BB);
24752475
auto *BC = static_cast<SPIRVBinary *>(BV);
24762476
return mapValue(BV, Builder.CreateBinaryIntrinsic(
24772477
Intrinsic::uadd_with_overflow,
24782478
transValue(BC->getOperand(0), F, BB),
24792479
transValue(BC->getOperand(1), F, BB)));
24802480
}
2481+
case OpISubBorrow: {
2482+
IRBuilder<> Builder(BB);
2483+
auto *BC = static_cast<SPIRVBinary *>(BV);
2484+
return mapValue(BV, Builder.CreateBinaryIntrinsic(
2485+
Intrinsic::usub_with_overflow,
2486+
transValue(BC->getOperand(0), F, BB),
2487+
transValue(BC->getOperand(1), F, BB)));
2488+
}
24812489
case OpGetKernelWorkGroupSize:
24822490
case OpGetKernelPreferredWorkGroupSizeMultiple:
24832491
return mapValue(

lib/SPIRV/SPIRVWriter.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3364,6 +3364,7 @@ bool LLVMToSPIRVBase::isKnownIntrinsic(Intrinsic::ID Id) {
33643364
case Intrinsic::trap:
33653365
case Intrinsic::arithmetic_fence:
33663366
case Intrinsic::uadd_with_overflow:
3367+
case Intrinsic::usub_with_overflow:
33673368
return true;
33683369
default:
33693370
// Unknown intrinsics' declarations should always be translated
@@ -3782,6 +3783,11 @@ SPIRVValue *LLVMToSPIRVBase::transIntrinsicInst(IntrinsicInst *II,
37823783
transValue(II->getArgOperand(0), BB),
37833784
transValue(II->getArgOperand(1), BB), BB);
37843785
}
3786+
case Intrinsic::usub_with_overflow: {
3787+
return BM->addBinaryInst(OpISubBorrow, transType(II->getType()),
3788+
transValue(II->getArgOperand(0), BB),
3789+
transValue(II->getArgOperand(1), BB), BB);
3790+
}
37853791
case Intrinsic::memset: {
37863792
// Generally there is no direct mapping of memset to SPIR-V. But it turns
37873793
// 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
@@ -1006,7 +1006,6 @@ _SPIRV_OP(ImageDrefGather)
10061006
_SPIRV_OP(QuantizeToF16)
10071007
_SPIRV_OP(ArrayLength)
10081008
_SPIRV_OP(OuterProduct)
1009-
_SPIRV_OP(ISubBorrow)
10101009
_SPIRV_OP(SMulExtended)
10111010
_SPIRV_OP(UMulExtended)
10121011
_SPIRV_OP(DPdx)

lib/SPIRV/libSPIRV/SPIRVInstruction.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,7 @@ _SPIRV_OP(IAdd)
664664
_SPIRV_OP(IAddCarry)
665665
_SPIRV_OP(FAdd)
666666
_SPIRV_OP(ISub)
667+
_SPIRV_OP(ISubBorrow)
667668
_SPIRV_OP(FSub)
668669
_SPIRV_OP(IMul)
669670
_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)