Skip to content

Commit 577ceda

Browse files
Add IAddCarry and ISubBorrow builtins (#2392) (#2442)
The fix adds support for IR builtin calls __spirv_IAddCarry and __spirv_ISubBorrow. It's also first part of fix which removes noncompliance of uadd/sub_with_overflow intrinsics. SPIRVUtil changes are needed to support situations where builtin don't have corresponding store instruction. Co-authored-by: bwlodarcz <bertrand.wlodarczyk@intel.com>
1 parent 2a290ad commit 577ceda

File tree

7 files changed

+366
-29
lines changed

7 files changed

+366
-29
lines changed

lib/SPIRV/SPIRVReader.cpp

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2305,20 +2305,12 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
23052305
return mapValue(BV,
23062306
transRelational(static_cast<SPIRVInstruction *>(BV), BB));
23072307
case OpIAddCarry: {
2308-
IRBuilder<> Builder(BB);
23092308
auto *BC = static_cast<SPIRVBinary *>(BV);
2310-
return mapValue(BV, Builder.CreateBinaryIntrinsic(
2311-
Intrinsic::uadd_with_overflow,
2312-
transValue(BC->getOperand(0), F, BB),
2313-
transValue(BC->getOperand(1), F, BB)));
2309+
return mapValue(BV, transBuiltinFromInst("__spirv_IAddCarry", BC, BB));
23142310
}
23152311
case OpISubBorrow: {
2316-
IRBuilder<> Builder(BB);
23172312
auto *BC = static_cast<SPIRVBinary *>(BV);
2318-
return mapValue(BV, Builder.CreateBinaryIntrinsic(
2319-
Intrinsic::usub_with_overflow,
2320-
transValue(BC->getOperand(0), F, BB),
2321-
transValue(BC->getOperand(1), F, BB)));
2313+
return mapValue(BV, transBuiltinFromInst("__spirv_ISubBorrow", BC, BB));
23222314
}
23232315
case OpGetKernelWorkGroupSize:
23242316
case OpGetKernelPreferredWorkGroupSizeMultiple:

lib/SPIRV/SPIRVUtil.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1853,23 +1853,38 @@ bool postProcessBuiltinReturningStruct(Function *F) {
18531853
SmallVector<Instruction *, 32> InstToRemove;
18541854
for (auto *U : F->users()) {
18551855
if (auto *CI = dyn_cast<CallInst>(U)) {
1856-
auto *ST = cast<StoreInst>(*(CI->user_begin()));
1856+
IRBuilder<> Builder(CI->getParent());
1857+
Builder.SetInsertPoint(CI);
1858+
SmallVector<User *, 5> Users(CI->users());
1859+
Value *A = nullptr;
1860+
for (auto *U : Users) {
1861+
if (auto *SI = dyn_cast<StoreInst>(U)) {
1862+
A = SI->getPointerOperand();
1863+
InstToRemove.push_back(SI);
1864+
break;
1865+
}
1866+
}
1867+
if (!A) {
1868+
A = Builder.CreateAlloca(F->getReturnType());
1869+
}
18571870
std::vector<Type *> ArgTys;
18581871
getFunctionTypeParameterTypes(F->getFunctionType(), ArgTys);
1859-
ArgTys.insert(ArgTys.begin(),
1860-
PointerType::get(F->getReturnType(), SPIRAS_Private));
1872+
ArgTys.insert(ArgTys.begin(), A->getType());
18611873
auto *NewF =
18621874
getOrCreateFunction(M, Type::getVoidTy(*Context), ArgTys, Name);
18631875
auto SRetAttr = Attribute::get(*Context, Attribute::AttrKind::StructRet,
18641876
F->getReturnType());
18651877
NewF->addParamAttr(0, SRetAttr);
18661878
NewF->setCallingConv(F->getCallingConv());
18671879
auto Args = getArguments(CI);
1868-
Args.insert(Args.begin(), ST->getPointerOperand());
1869-
auto *NewCI = CallInst::Create(NewF, Args, CI->getName(), CI);
1880+
Args.insert(Args.begin(), A);
1881+
CallInst *NewCI = Builder.CreateCall(NewF, Args, CI->getName());
18701882
NewCI->addParamAttr(0, SRetAttr);
18711883
NewCI->setCallingConv(CI->getCallingConv());
1872-
InstToRemove.push_back(ST);
1884+
SmallVector<User *, 5> CIUsers(CI->users());
1885+
for (auto *CIUser : CIUsers) {
1886+
CIUser->replaceUsesOfWith(CI, A);
1887+
}
18731888
InstToRemove.push_back(CI);
18741889
}
18751890
}

lib/SPIRV/SPIRVWriter.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4379,6 +4379,24 @@ LLVMToSPIRVBase::transBuiltinToInstWithoutDecoration(Op OC, CallInst *CI,
43794379
return BM->addArbFloatPointIntelInst(OC, transType(ResTy), InA, InB,
43804380
Literals, BB);
43814381
}
4382+
case OpIAddCarry: {
4383+
Function *F = CI->getCalledFunction();
4384+
auto *RetTy = F->arg_begin()->getType()->getPointerElementType();
4385+
StructType *St = cast<StructType>(RetTy);
4386+
SPIRVValue *V = BM->addBinaryInst(OpIAddCarry, transType(St),
4387+
transValue(CI->getArgOperand(1), BB),
4388+
transValue(CI->getArgOperand(2), BB), BB);
4389+
return BM->addStoreInst(transValue(CI->getArgOperand(0), BB), V, {}, BB);
4390+
}
4391+
case OpISubBorrow: {
4392+
Function *F = CI->getCalledFunction();
4393+
auto *RetTy = F->arg_begin()->getType()->getPointerElementType();
4394+
StructType *St = cast<StructType>(RetTy);
4395+
SPIRVValue *V = BM->addBinaryInst(OpISubBorrow, transType(St),
4396+
transValue(CI->getArgOperand(1), BB),
4397+
transValue(CI->getArgOperand(2), BB), BB);
4398+
return BM->addStoreInst(transValue(CI->getArgOperand(0), BB), V, {}, BB);
4399+
}
43824400
default: {
43834401
if (isCvtOpCode(OC) && OC != OpGenericCastToPtrExplicit) {
43844402
return BM->addUnaryInst(OC, transType(CI->getType()),

test/iaddcarry_builtin.ll

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
; REQUIRES: spirv-dis
2+
; RUN: llvm-as %s -o %t.bc
3+
; RUN: llvm-spirv %t.bc -o %t.spv
4+
; RUN: spirv-dis --raw-id %t.spv | FileCheck --check-prefix CHECK-SPIRV %s
5+
; RUN: 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+
%i8struct = type {i8, i8}
12+
%i16struct = type {i16, i16}
13+
%i32struct = type {i32, i32}
14+
%i64struct = type {i64, i64}
15+
%vecstruct = type {<4 x i32>, <4 x i32>}
16+
17+
; CHECK-SPIRV-DAG: [[uchar:%[a-z0-9_]+]] = OpTypeInt 8
18+
; CHECK-SPIRV-DAG: [[ushort:%[a-z0-9_]+]] = OpTypeInt 16
19+
; CHECK-SPIRV-DAG: [[uint:%[a-z0-9_]+]] = OpTypeInt 32
20+
; CHECK-SPIRV-DAG: [[ulong:%[a-z0-9_]+]] = OpTypeInt 64
21+
; CHECK-SPIRV-DAG: [[void:%[a-z0-9_]+]] = OpTypeVoid
22+
; CHECK-SPIRV-DAG: [[i8struct:%[a-z0-9_]+]] = OpTypeStruct [[uchar]] [[uchar]]
23+
; CHECK-SPIRV-DAG: [[_ptr_Function_i8struct:%[a-z0-9_]+]] = OpTypePointer Function [[i8struct]]
24+
; CHECK-SPIRV-DAG: [[i16struct:%[a-z0-9_]+]] = OpTypeStruct [[ushort]] [[ushort]]
25+
; CHECK-SPIRV-DAG: [[_ptr_Function_i16struct:%[a-z0-9_]+]] = OpTypePointer Function [[i16struct]]
26+
; CHECK-SPIRV-DAG: [[i32struct:%[a-z0-9_]+]] = OpTypeStruct [[uint]] [[uint]]
27+
; CHECK-SPIRV-DAG: [[_ptr_Function_i32struct:%[a-z0-9_]+]] = OpTypePointer Function [[i32struct]]
28+
; CHECK-SPIRV-DAG: [[i64struct:%[a-z0-9_]+]] = OpTypeStruct [[ulong]] [[ulong]]
29+
; CHECK-SPIRV-DAG: [[_ptr_Function_i64struct:%[a-z0-9_]+]] = OpTypePointer Function [[i64struct]]
30+
; CHECK-SPIRV-DAG: [[v4uint:%[a-z0-9_]+]] = OpTypeVector [[uint]] 4
31+
; CHECK-SPIRV-DAG: [[vecstruct:%[a-z0-9_]+]] = OpTypeStruct [[v4uint]] [[v4uint]]
32+
; CHECK-SPIRV-DAG: [[_ptr_Function_vecstruct:%[a-z0-9_]+]] = OpTypePointer Function [[vecstruct]]
33+
; CHECK-SPIRV-DAG: [[struct_anon:%[a-z0-9_.]+]] = OpTypeStruct [[uint]] [[uint]]
34+
; CHECK-SPIRV-DAG: [[_ptr_Function_struct_anon:%[a-z0-9_]+]] = OpTypePointer Function [[struct_anon]]
35+
; CHECK-SPIRV-DAG: [[_ptr_Generic_struct_anon:%[a-z0-9_]+]] = OpTypePointer Generic [[struct_anon]]
36+
37+
; CHECK-LLVM-DAG: [[i8struct:%[a-z0-9_.]+]] = type { i8, i8 }
38+
; CHECK-LLVM-DAG: [[i16struct:%[a-z0-9_.]+]] = type { i16, i16 }
39+
; CHECK-LLVM-DAG: [[i32struct:%[a-z0-9_.]+]] = type { i32, i32 }
40+
; CHECK-LLVM-DAG: [[i64struct:%[a-z0-9_.]+]] = type { i64, i64 }
41+
; CHECK-LLVM-DAG: [[vecstruct:%[a-z0-9_.]+]] = type { <4 x i32>, <4 x i32> }
42+
; CHECK-LLVM-DAG: [[struct_anon:%[a-z0-9_.]+]] = type { i32, i32 }
43+
44+
define spir_func void @test_builtin_iaddcarrycc(i8 %a, i8 %b) {
45+
entry:
46+
%0 = alloca %i8struct
47+
call void @_Z17__spirv_IAddCarrycc(%i8struct* %0, i8 %a, i8 %b)
48+
ret void
49+
}
50+
51+
; CHECK-SPIRV: [[a:%[a-z0-9_]+]] = OpFunctionParameter [[uchar]]
52+
; CHECK-SPIRV: [[b:%[a-z0-9_]+]] = OpFunctionParameter [[uchar]]
53+
; CHECK-SPIRV: [[entry:%[a-z0-9_]+]] = OpLabel
54+
; CHECK-SPIRV: [[var_11:%[a-z0-9_]+]] = OpVariable [[_ptr_Function_i8struct]] Function
55+
; CHECK-SPIRV: [[var_12:%[a-z0-9_]+]] = OpIAddCarry [[i8struct]] [[a]] [[b]]
56+
; CHECK-SPIRV: OpStore [[var_11]] [[var_12]]
57+
; CHECK-SPIRV: OpReturn
58+
; CHECK-SPIRV: OpFunctionEnd
59+
60+
; CHECK-LLVM: %0 = alloca [[i8struct]], align 8
61+
; CHECK-LLVM: call spir_func void @_Z17__spirv_IAddCarrycc([[i8struct]]* sret %0, i8 %a, i8 %b)
62+
; CHECK-LLVM: ret void
63+
define spir_func void @test_builtin_iaddcarryss(i16 %a, i16 %b) {
64+
entry:
65+
%0 = alloca %i16struct
66+
call void @_Z17__spirv_IAddCarryss(%i16struct* %0, i16 %a, i16 %b)
67+
ret void
68+
}
69+
; CHECK-SPIRV: [[a_0:%[a-z0-9_]+]] = OpFunctionParameter [[ushort]]
70+
; CHECK-SPIRV: [[b_0:%[a-z0-9_]+]] = OpFunctionParameter [[ushort]]
71+
; CHECK-SPIRV: [[entry_0:%[a-z0-9_]+]] = OpLabel
72+
; CHECK-SPIRV: [[var_21:%[a-z0-9_]+]] = OpVariable [[_ptr_Function_i16struct]] Function
73+
; CHECK-SPIRV: [[var_22:%[a-z0-9_]+]] = OpIAddCarry [[i16struct]] [[a_0]] [[b_0]]
74+
; CHECK-SPIRV: OpStore [[var_21]] [[var_22]]
75+
; CHECK-SPIRV: OpReturn
76+
; CHECK-SPIRV: OpFunctionEnd
77+
78+
; CHECK-LLVM: %0 = alloca [[i16struct]], align 8
79+
; CHECK-LLVM: call spir_func void @_Z17__spirv_IAddCarryss([[i16struct]]* sret %0, i16 %a, i16 %b)
80+
; CHECK-LLVM: ret void
81+
define spir_func void @test_builtin_iaddcarryii(i32 %a, i32 %b) {
82+
entry:
83+
%0 = alloca %i32struct
84+
call void @_Z17__spirv_IAddCarryii(%i32struct* %0, i32 %a, i32 %b)
85+
ret void
86+
}
87+
; CHECK-SPIRV: [[a_1:%[a-z0-9_]+]] = OpFunctionParameter [[uint]]
88+
; CHECK-SPIRV: [[b_1:%[a-z0-9_]+]] = OpFunctionParameter [[uint]]
89+
; CHECK-SPIRV: [[entry_1:%[a-z0-9_]+]] = OpLabel
90+
; CHECK-SPIRV: [[var_31:%[a-z0-9_]+]] = OpVariable [[_ptr_Function_i32struct]] Function
91+
; CHECK-SPIRV: [[var_32:%[a-z0-9_]+]] = OpIAddCarry [[i32struct]] [[a_1]] [[b_1]]
92+
; CHECK-SPIRV: OpStore [[var_31]] [[var_32]]
93+
; CHECK-SPIRV: OpReturn
94+
; CHECK-SPIRV: OpFunctionEnd
95+
96+
; CHECK-LLVM: %0 = alloca [[i32struct]], align 8
97+
; CHECK-LLVM: call spir_func void @_Z17__spirv_IAddCarryii([[i32struct]]* sret %0, i32 %a, i32 %b)
98+
; CHECK-LLVM: ret void
99+
define spir_func void @test_builtin_iaddcarryll(i64 %a, i64 %b) {
100+
entry:
101+
%0 = alloca %i64struct
102+
call void @_Z17__spirv_IAddCarryll(%i64struct* %0, i64 %a, i64 %b)
103+
ret void
104+
}
105+
; CHECK-SPIRV: [[a_2:%[a-z0-9_]+]] = OpFunctionParameter [[ulong]]
106+
; CHECK-SPIRV: [[b_2:%[a-z0-9_]+]] = OpFunctionParameter [[ulong]]
107+
; CHECK-SPIRV: [[entry_2:%[a-z0-9_]+]] = OpLabel
108+
; CHECK-SPIRV: [[var_41:%[a-z0-9_]+]] = OpVariable [[_ptr_Function_i64struct]] Function
109+
; CHECK-SPIRV: [[var_42:%[a-z0-9_]+]] = OpIAddCarry [[i64struct]] [[a_2]] [[b_2]]
110+
; CHECK-SPIRV: OpStore [[var_41]] [[var_42]]
111+
; CHECK-SPIRV: OpReturn
112+
; CHECK-SPIRV: OpFunctionEnd
113+
114+
; CHECK-LLVM: %0 = alloca [[i64struct]]
115+
; CHECK-LLVM: call spir_func void @_Z17__spirv_IAddCarryll([[i64struct]]* sret %0, i64 %a, i64 %b)
116+
; CHECK-LLVM: ret void
117+
define spir_func void @test_builtin_iaddcarryDv4_xS_(<4 x i32> %a, <4 x i32> %b) {
118+
entry:
119+
%0 = alloca %vecstruct
120+
call void @_Z17__spirv_IAddCarryDv4_iS_(%vecstruct* %0, <4 x i32> %a, <4 x i32> %b)
121+
ret void
122+
}
123+
; CHECK-SPIRV: [[a_3:%[a-z0-9_]+]] = OpFunctionParameter [[v4uint]]
124+
; CHECK-SPIRV: [[b_3:%[a-z0-9_]+]] = OpFunctionParameter [[v4uint]]
125+
; CHECK-SPIRV: [[entry_3:%[a-z0-9_]+]] = OpLabel
126+
; CHECK-SPIRV: [[var_51:%[a-z0-9_]+]] = OpVariable [[_ptr_Function_vecstruct]] Function
127+
; CHECK-SPIRV: [[var_52:%[a-z0-9_]+]] = OpIAddCarry [[vecstruct]] [[a_3]] [[b_3]]
128+
; CHECK-SPIRV: OpStore [[var_51]] [[var_52]]
129+
; CHECK-SPIRV: OpReturn
130+
; CHECK-SPIRV: OpFunctionEnd
131+
132+
; CHECK-LLVM: %0 = alloca [[vecstruct]]
133+
; CHECK-LLVM: call spir_func void @_Z17__spirv_IAddCarryDv4_iS_([[vecstruct]]* sret %0, <4 x i32> %a, <4 x i32> %b)
134+
; CHECK-LLVM: ret void
135+
136+
%struct.anon = type { i32, i32 }
137+
138+
define spir_func void @test_builtin_iaddcarry_anon(i32 %a, i32 %b) {
139+
entry:
140+
%0 = alloca %struct.anon
141+
%1 = addrspacecast %struct.anon* %0 to %struct.anon addrspace(4)*
142+
call spir_func void @_Z17__spirv_IAddCarryIiiE4anonIT_T0_ES1_S2_(%struct.anon addrspace(4)* %1, i32 %a, i32 %b)
143+
ret void
144+
}
145+
; CHECK-SPIRV: [[a_4:%[a-z0-9_]+]] = OpFunctionParameter [[uint]]
146+
; CHECK-SPIRV: [[b_4:%[a-z0-9_]+]] = OpFunctionParameter [[uint]]
147+
; CHECK-SPIRV: [[entry_4:%[a-z0-9_]+]] = OpLabel
148+
; CHECK-SPIRV: [[var_59:%[a-z0-9_]+]] = OpVariable [[_ptr_Function_struct_anon]] Function
149+
; CHECK-SPIRV: [[var_61:%[a-z0-9_]+]] = OpPtrCastToGeneric [[_ptr_Generic_struct_anon]] [[var_59]]
150+
; CHECK-SPIRV: [[var_62:%[a-z0-9_]+]] = OpIAddCarry [[struct_anon]] [[a_4]] [[b_4]]
151+
; CHECK-SPIRV: OpStore [[var_61]] [[var_62]]
152+
153+
; CHECK-LLVM: %0 = alloca [[struct_anon]], align 8
154+
; CHECK-LLVM: %1 = addrspacecast [[struct_anon]]* %0 to [[struct_anon]] addrspace(4)*
155+
; CHECK-LLVM: call spir_func void @_Z17__spirv_IAddCarryii.1([[struct_anon]] addrspace(4)* sret %1, i32 %a, i32 %b)
156+
; CHECK-LLVM: ret void
157+
158+
declare void @_Z17__spirv_IAddCarryIiiE4anonIT_T0_ES1_S2_(%struct.anon addrspace(4)* align 4, i32, i32)
159+
declare void @_Z17__spirv_IAddCarrycc(%i8struct*, i8, i8)
160+
declare void @_Z17__spirv_IAddCarryss(%i16struct*, i16, i16)
161+
declare void @_Z17__spirv_IAddCarryii(%i32struct*, i32, i32)
162+
declare void @_Z17__spirv_IAddCarryll(%i64struct*, i64, i64)
163+
declare void @_Z17__spirv_IAddCarryDv4_iS_(%vecstruct*, <4 x i32>, <4 x i32>)

0 commit comments

Comments
 (0)