Skip to content

Commit

Permalink
Add support for ISubBorrow SPIRV instruction (#2168)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bwlodarcz authored and MrSidims committed Mar 5, 2024
1 parent e1a0e75 commit d474283
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 3 deletions.
10 changes: 9 additions & 1 deletion lib/SPIRV/SPIRVReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2340,13 +2340,21 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
return mapValue(BV,
transRelational(static_cast<SPIRVInstruction *>(BV), BB));
case OpIAddCarry: {
IRBuilder Builder(BB);
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 OpISubBorrow: {
IRBuilder<> Builder(BB);
auto *BC = static_cast<SPIRVBinary *>(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(
Expand Down
6 changes: 6 additions & 0 deletions lib/SPIRV/SPIRVWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion lib/SPIRV/libSPIRV/SPIRVEntry.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions lib/SPIRV/libSPIRV/SPIRVInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion lib/SPIRV/libSPIRV/SPIRVOpCode.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
58 changes: 58 additions & 0 deletions test/llvm-intrinsics/usub.with.overflow.ll
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit d474283

Please sign in to comment.