Skip to content

Commit b36dd71

Browse files
Add IAddCarry and ISubBorrow builtins (#2392) (#2450)
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 e6652c0 commit b36dd71

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
@@ -2231,20 +2231,12 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
22312231
return mapValue(BV,
22322232
transRelational(static_cast<SPIRVInstruction *>(BV), BB));
22332233
case OpIAddCarry: {
2234-
IRBuilder<> Builder(BB);
22352234
auto *BC = static_cast<SPIRVBinary *>(BV);
2236-
return mapValue(BV, Builder.CreateBinaryIntrinsic(
2237-
Intrinsic::uadd_with_overflow,
2238-
transValue(BC->getOperand(0), F, BB),
2239-
transValue(BC->getOperand(1), F, BB)));
2235+
return mapValue(BV, transBuiltinFromInst("__spirv_IAddCarry", BC, BB));
22402236
}
22412237
case OpISubBorrow: {
2242-
IRBuilder<> Builder(BB);
22432238
auto *BC = static_cast<SPIRVBinary *>(BV);
2244-
return mapValue(BV, Builder.CreateBinaryIntrinsic(
2245-
Intrinsic::usub_with_overflow,
2246-
transValue(BC->getOperand(0), F, BB),
2247-
transValue(BC->getOperand(1), F, BB)));
2239+
return mapValue(BV, transBuiltinFromInst("__spirv_ISubBorrow", BC, BB));
22482240
}
22492241
case OpGetKernelWorkGroupSize:
22502242
case OpGetKernelPreferredWorkGroupSizeMultiple:

lib/SPIRV/SPIRVUtil.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1893,22 +1893,37 @@ bool postProcessBuiltinReturningStruct(Function *F) {
18931893
SmallVector<Instruction *, 32> InstToRemove;
18941894
for (auto *U : F->users()) {
18951895
if (auto *CI = dyn_cast<CallInst>(U)) {
1896-
auto *ST = cast<StoreInst>(*(CI->user_begin()));
1896+
IRBuilder<> Builder(CI->getParent());
1897+
Builder.SetInsertPoint(CI);
1898+
SmallVector<User *, 5> Users(CI->users());
1899+
Value *A = nullptr;
1900+
for (auto *U : Users) {
1901+
if (auto *SI = dyn_cast<StoreInst>(U)) {
1902+
A = SI->getPointerOperand();
1903+
InstToRemove.push_back(SI);
1904+
break;
1905+
}
1906+
}
1907+
if (!A) {
1908+
A = Builder.CreateAlloca(F->getReturnType());
1909+
}
18971910
std::vector<Type *> ArgTys;
18981911
getFunctionTypeParameterTypes(F->getFunctionType(), ArgTys);
1899-
ArgTys.insert(ArgTys.begin(),
1900-
PointerType::get(F->getReturnType(), SPIRAS_Private));
1912+
ArgTys.insert(ArgTys.begin(), A->getType());
19011913
auto *NewF =
19021914
getOrCreateFunction(M, Type::getVoidTy(*Context), ArgTys, Name);
19031915
NewF->addParamAttr(0, Attribute::get(*Context,
19041916
Attribute::AttrKind::StructRet,
19051917
F->getReturnType()));
19061918
NewF->setCallingConv(F->getCallingConv());
19071919
auto Args = getArguments(CI);
1908-
Args.insert(Args.begin(), ST->getPointerOperand());
1909-
auto *NewCI = CallInst::Create(NewF, Args, CI->getName(), CI);
1920+
Args.insert(Args.begin(), A);
1921+
CallInst *NewCI = Builder.CreateCall(NewF, Args, CI->getName());
19101922
NewCI->setCallingConv(CI->getCallingConv());
1911-
InstToRemove.push_back(ST);
1923+
SmallVector<User *, 5> CIUsers(CI->users());
1924+
for (auto *CIUser : CIUsers) {
1925+
CIUser->replaceUsesOfWith(CI, A);
1926+
}
19121927
InstToRemove.push_back(CI);
19131928
}
19141929
}

lib/SPIRV/SPIRVWriter.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4198,6 +4198,24 @@ LLVMToSPIRV::transBuiltinToInstWithoutDecoration(Op OC, CallInst *CI,
41984198
return BM->addArbFloatPointIntelInst(OC, transType(ResTy), InA, InB,
41994199
Literals, BB);
42004200
}
4201+
case OpIAddCarry: {
4202+
Function *F = CI->getCalledFunction();
4203+
auto *RetTy = F->arg_begin()->getType()->getPointerElementType();
4204+
StructType *St = cast<StructType>(RetTy);
4205+
SPIRVValue *V = BM->addBinaryInst(OpIAddCarry, transType(St),
4206+
transValue(CI->getArgOperand(1), BB),
4207+
transValue(CI->getArgOperand(2), BB), BB);
4208+
return BM->addStoreInst(transValue(CI->getArgOperand(0), BB), V, {}, BB);
4209+
}
4210+
case OpISubBorrow: {
4211+
Function *F = CI->getCalledFunction();
4212+
auto *RetTy = F->arg_begin()->getType()->getPointerElementType();
4213+
StructType *St = cast<StructType>(RetTy);
4214+
SPIRVValue *V = BM->addBinaryInst(OpISubBorrow, transType(St),
4215+
transValue(CI->getArgOperand(1), BB),
4216+
transValue(CI->getArgOperand(2), BB), BB);
4217+
return BM->addStoreInst(transValue(CI->getArgOperand(0), BB), V, {}, BB);
4218+
}
42014219
default: {
42024220
if (isCvtOpCode(OC) && OC != OpGenericCastToPtrExplicit) {
42034221
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([[i8struct]]) %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([[i16struct]]) %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([[i32struct]]) %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([[i64struct]]) %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([[vecstruct]]) %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([[struct_anon]]) %1, i32 %a, i32 %b)
156+
; CHECK-LLVM: ret void
157+
158+
declare void @_Z17__spirv_IAddCarryIiiE4anonIT_T0_ES1_S2_(%struct.anon addrspace(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)