Skip to content

Commit f2a1b67

Browse files
jcranmer-intelbader
authored andcommitted
Add a helper for type-aware mutateCallInst replacement. (intel#1611)
Opaque pointers requires us to keep more careful track of the pointee types of the builtin methods. While these are discoverable via demangling, the existing interface of mutateCallInst* does not provide easy ways to maintain these types during remapping of OpenCL builtins to SPIR-V builtins. This patch adds a BuiltinCallMutator that keeps track not only of the pointer element types discovered by demangling, but also of the LLVM attributes the arguments originally had, even as arguments are inserted or deleted for OpenCL<->SPIR-V builtin conversions. Additionally, the interface is intended to be more forgiving of the varieties of lambdas needed to do the conversions: for example, there is no need for different entry points to indicate a type-aware mapping, or even to indicate a function whose return type changes. It is intended that all of the existing calls to mutateCallInst be replaced with this new interface, so as to be able to eliminate the getPointerElementType call in the existing implementation of that function. However, the actual migration of the calls will be done in future patches to keep the maximum size of patches relatively small.
1 parent c294993 commit f2a1b67

10 files changed

+524
-19
lines changed

llvm-spirv/lib/SPIRV/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ set(SRC_LIST
99
OCLTypeToSPIRV.cpp
1010
OCLUtil.cpp
1111
VectorComputeUtil.cpp
12+
SPIRVBuiltinHelper.cpp
1213
SPIRVLowerBitCastToNonStandardType.cpp
1314
SPIRVLowerBool.cpp
1415
SPIRVLowerConstExpr.cpp

llvm-spirv/lib/SPIRV/OCLToSPIRV.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ void OCLToSPIRVBase::transVecLoadStoreName(std::string &DemangledName,
160160
char OCLToSPIRVLegacy::ID = 0;
161161

162162
bool OCLToSPIRVBase::runOCLToSPIRV(Module &Module) {
163-
M = &Module;
163+
initialize(Module);
164164
Ctx = &M->getContext();
165165
auto Src = getSPIRVSource(&Module);
166166
// This is a pre-processing pass, which transform LLVM IR module to a more
@@ -1581,7 +1581,7 @@ void OCLToSPIRVBase::visitCallKernelQuery(CallInst *CI,
15811581
auto *BlockF = cast<Function>(getUnderlyingObject(BlockFVal));
15821582

15831583
AttributeList Attrs = CI->getCalledFunction()->getAttributes();
1584-
mutateCallInst(
1584+
::mutateCallInst(
15851585
M, CI,
15861586
[=](CallInst *CI, std::vector<Value *> &Args) {
15871587
Value *Param = *Args.rbegin();

llvm-spirv/lib/SPIRV/OCLToSPIRV.h

+4-3
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#define SPIRV_OCLTOSPIRV_H
4242

4343
#include "OCLUtil.h"
44+
#include "SPIRVBuiltinHelper.h"
4445

4546
#include "llvm/IR/InstVisitor.h"
4647
#include "llvm/IR/PassManager.h"
@@ -50,10 +51,11 @@ namespace SPIRV {
5051

5152
class OCLTypeToSPIRVBase;
5253

53-
class OCLToSPIRVBase : public InstVisitor<OCLToSPIRVBase> {
54+
class OCLToSPIRVBase : public InstVisitor<OCLToSPIRVBase>, BuiltinCallHelper {
5455
public:
5556
OCLToSPIRVBase()
56-
: M(nullptr), Ctx(nullptr), CLVer(0), OCLTypeToSPIRVPtr(nullptr) {}
57+
: BuiltinCallHelper(ManglingRules::SPIRV), Ctx(nullptr), CLVer(0),
58+
OCLTypeToSPIRVPtr(nullptr) {}
5759
virtual ~OCLToSPIRVBase() {}
5860
bool runOCLToSPIRV(Module &M);
5961

@@ -264,7 +266,6 @@ class OCLToSPIRVBase : public InstVisitor<OCLToSPIRVBase> {
264266
OCLTypeToSPIRVBase *getOCLTypeToSPIRV() { return OCLTypeToSPIRVPtr; }
265267

266268
private:
267-
Module *M;
268269
LLVMContext *Ctx;
269270
unsigned CLVer; /// OpenCL version as major*10+minor
270271
std::set<Value *> ValuesToDelete;

llvm-spirv/lib/SPIRV/OCLUtil.cpp

+7-8
Original file line numberDiff line numberDiff line change
@@ -979,9 +979,7 @@ static FunctionType *getBlockInvokeTy(Function *F, unsigned BlockIdx) {
979979
class OCLBuiltinFuncMangleInfo : public SPIRV::BuiltinFuncMangleInfo {
980980
public:
981981
OCLBuiltinFuncMangleInfo(Function *F) : F(F) {}
982-
OCLBuiltinFuncMangleInfo(ArrayRef<Type *> ArgTypes)
983-
: ArgTypes(ArgTypes.vec()) {}
984-
Type *getArgTy(unsigned I) { return F->getFunctionType()->getParamType(I); }
982+
OCLBuiltinFuncMangleInfo() = default;
985983
void init(StringRef UniqName) override {
986984
// Make a local copy as we will modify the string in init function
987985
std::string TempStorage = UniqName.str();
@@ -1305,10 +1303,7 @@ class OCLBuiltinFuncMangleInfo : public SPIRV::BuiltinFuncMangleInfo {
13051303
}
13061304
// Auxiliarry information, it is expected that it is relevant at the moment
13071305
// the init method is called.
1308-
Function *F; // SPIRV decorated function
1309-
// TODO: ArgTypes argument should get removed once all SPV-IR related issues
1310-
// are resolved
1311-
std::vector<Type *> ArgTypes; // Arguments of OCL builtin
1306+
Function *F; // SPIRV decorated function
13121307
};
13131308

13141309
CallInst *mutateCallInstOCL(
@@ -1330,6 +1325,10 @@ Instruction *mutateCallInstOCL(
13301325
TakeFuncName);
13311326
}
13321327

1328+
std::unique_ptr<SPIRV::BuiltinFuncMangleInfo> makeMangler(Function &F) {
1329+
return std::make_unique<OCLBuiltinFuncMangleInfo>(&F);
1330+
}
1331+
13331332
static StringRef getStructName(Type *Ty) {
13341333
if (auto *STy = dyn_cast<StructType>(Ty))
13351334
return STy->isLiteral() ? "" : Ty->getStructName();
@@ -1603,6 +1602,6 @@ Value *SPIRV::transSPIRVMemorySemanticsIntoOCLMemFenceFlags(
16031602
void llvm::mangleOpenClBuiltin(const std::string &UniqName,
16041603
ArrayRef<Type *> ArgTypes,
16051604
std::string &MangledName) {
1606-
OCLUtil::OCLBuiltinFuncMangleInfo BtnInfo(ArgTypes);
1605+
OCLUtil::OCLBuiltinFuncMangleInfo BtnInfo;
16071606
MangledName = SPIRV::mangleBuiltin(UniqName, ArgTypes, &BtnInfo);
16081607
}

llvm-spirv/lib/SPIRV/OCLUtil.h

+6
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ using namespace SPIRV;
5454
using namespace llvm;
5555
using namespace spv;
5656

57+
namespace SPIRV {
58+
class BuiltinCallMutator;
59+
} // namespace SPIRV
60+
5761
namespace OCLUtil {
5862

5963
///////////////////////////////////////////////////////////////////////////////
@@ -520,6 +524,8 @@ std::string getIntelSubgroupBlockDataPostfix(unsigned ElementBitSize,
520524

521525
void insertImageNameAccessQualifier(SPIRVAccessQualifierKind Acc,
522526
std::string &Name);
527+
528+
std::unique_ptr<SPIRV::BuiltinFuncMangleInfo> makeMangler(Function &F);
523529
} // namespace OCLUtil
524530

525531
using namespace OCLUtil;
+213
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
//===- SPIRVBuiltinHelper.cpp - Helpers for managing calls to builtins ----===//
2+
//
3+
// The LLVM/SPIR-V Translator
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
// Copyright (c) 2022 The Khronos Group Inc.
9+
//
10+
// Permission is hereby granted, free of charge, to any person obtaining a
11+
// copy of this software and associated documentation files (the "Software"),
12+
// to deal with the Software without restriction, including without limitation
13+
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
14+
// and/or sell copies of the Software, and to permit persons to whom the
15+
// Software is furnished to do so, subject to the following conditions:
16+
//
17+
// Redistributions of source code must retain the above copyright notice,
18+
// this list of conditions and the following disclaimers.
19+
// Redistributions in binary form must reproduce the above copyright notice,
20+
// this list of conditions and the following disclaimers in the documentation
21+
// and/or other materials provided with the distribution.
22+
// Neither the names of The Khronos Group, nor the names of its
23+
// contributors may be used to endorse or promote products derived from this
24+
// Software without specific prior written permission.
25+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28+
// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
31+
// THE SOFTWARE.
32+
//
33+
//===----------------------------------------------------------------------===//
34+
//
35+
// This file implements helper functions for adding calls to OpenCL or SPIR-V
36+
// builtin functions, or for rewriting calls to one into calls to the other.
37+
//
38+
//===----------------------------------------------------------------------===//
39+
40+
#include "SPIRVBuiltinHelper.h"
41+
42+
#include "OCLUtil.h"
43+
#include "SPIRVInternal.h"
44+
45+
using namespace llvm;
46+
using namespace SPIRV;
47+
48+
static std::unique_ptr<BuiltinFuncMangleInfo> makeMangler(CallBase *CB,
49+
ManglingRules Rules) {
50+
switch (Rules) {
51+
case ManglingRules::None:
52+
return nullptr;
53+
case ManglingRules::SPIRV:
54+
return std::make_unique<BuiltinFuncMangleInfo>();
55+
case ManglingRules::OpenCL:
56+
return OCLUtil::makeMangler(*CB->getCalledFunction());
57+
}
58+
llvm_unreachable("Unknown mangling rules to make a name mangler");
59+
}
60+
61+
BuiltinCallMutator::BuiltinCallMutator(
62+
CallInst *CI, std::string FuncName, ManglingRules Rules,
63+
std::function<std::string(StringRef)> NameMapFn)
64+
: CI(CI), FuncName(FuncName),
65+
Attrs(CI->getCalledFunction()->getAttributes()), ReturnTy(CI->getType()),
66+
Args(CI->args()), Rules(Rules), Builder(CI) {
67+
getParameterTypes(CI->getCalledFunction(), PointerTypes,
68+
std::move(NameMapFn));
69+
PointerTypes.resize(Args.size(), nullptr);
70+
}
71+
72+
BuiltinCallMutator::BuiltinCallMutator(BuiltinCallMutator &&Other)
73+
: CI(Other.CI), FuncName(std::move(Other.FuncName)),
74+
MutateRet(std::move(Other.MutateRet)), Attrs(Other.Attrs),
75+
ReturnTy(Other.ReturnTy), Args(std::move(Other.Args)),
76+
PointerTypes(std::move(Other.PointerTypes)),
77+
Rules(std::move(Other.Rules)), Builder(CI) {
78+
// Clear the other's CI instance so that it knows not to construct the actual
79+
// call.
80+
Other.CI = nullptr;
81+
}
82+
83+
Value *BuiltinCallMutator::doConversion() {
84+
assert(CI && "Need to have a call instruction to do the conversion");
85+
auto Mangler = makeMangler(CI, Rules);
86+
for (unsigned I = 0; I < Args.size(); I++) {
87+
Mangler->getTypeMangleInfo(I).PointerTy = PointerTypes[I];
88+
}
89+
assert(Attrs.getNumAttrSets() <= Args.size() + 2 && "Too many attributes?");
90+
CallInst *NewCall =
91+
Builder.Insert(addCallInst(CI->getModule(), FuncName, ReturnTy, Args,
92+
&Attrs, nullptr, Mangler.get()));
93+
Value *Result = MutateRet ? MutateRet(Builder, NewCall) : NewCall;
94+
Result->takeName(CI);
95+
if (!CI->getType()->isVoidTy())
96+
CI->replaceAllUsesWith(Result);
97+
CI->dropAllReferences();
98+
CI->eraseFromParent();
99+
CI = nullptr;
100+
return Result;
101+
}
102+
103+
BuiltinCallMutator &BuiltinCallMutator::setArgs(ArrayRef<Value *> NewArgs) {
104+
// Retain only the function attributes, not any parameter attributes.
105+
Attrs = AttributeList::get(CI->getContext(), Attrs.getFnAttrs(),
106+
Attrs.getRetAttrs(), {});
107+
Args.clear();
108+
PointerTypes.clear();
109+
for (Value *Arg : NewArgs) {
110+
assert(!Arg->getType()->isPointerTy() &&
111+
"Cannot use this signature with pointer types");
112+
Args.push_back(Arg);
113+
PointerTypes.emplace_back();
114+
}
115+
return *this;
116+
}
117+
118+
// This is a helper method to handle splicing of the attribute lists, as
119+
// llvm::AttributeList doesn't have any helper methods for this sort of design.
120+
// (It's designed to be manually built-up, not adjusted to add/remove
121+
// arguments on the fly).
122+
static void moveAttributes(LLVMContext &Ctx, AttributeList &Attrs,
123+
unsigned Start, unsigned Len, unsigned Dest) {
124+
SmallVector<std::pair<unsigned, AttributeSet>, 6> NewAttrs;
125+
for (unsigned Index : Attrs.indexes()) {
126+
AttributeSet AttrSet = Attrs.getAttributes(Index);
127+
if (!AttrSet.hasAttributes())
128+
continue;
129+
130+
// If the attribute is a parameter index, check to see how its index should
131+
// be adjusted.
132+
if (Index > AttributeList::FirstArgIndex) {
133+
unsigned ParamIndex = Index - AttributeList::FirstArgIndex;
134+
if (ParamIndex >= Start && ParamIndex < Start + Len)
135+
// A parameter in this range needs to have its index adjusted to its
136+
// destination location.
137+
Index += Dest - Start;
138+
else if (ParamIndex >= Dest && ParamIndex < Dest + Len)
139+
// This parameter will be overwritten by one of the moved parameters, so
140+
// omit it entirely.
141+
continue;
142+
}
143+
144+
// The array is usually going to be sorted, but because of the above
145+
// adjustment, we might end up out of order. This logic ensures that the
146+
// array always remains in sorted order.
147+
std::pair<unsigned, AttributeSet> ToInsert(Index, AttrSet);
148+
NewAttrs.insert(llvm::lower_bound(NewAttrs, ToInsert, llvm::less_first()),
149+
ToInsert);
150+
}
151+
Attrs = AttributeList::get(Ctx, NewAttrs);
152+
}
153+
154+
// Convert a ValueTypePair to a TypedPointerType for storing in the PointerTypes
155+
// array.
156+
static TypedPointerType *toTPT(BuiltinCallMutator::ValueTypePair Pair) {
157+
if (!Pair.second)
158+
return nullptr;
159+
unsigned AS = 0;
160+
if (auto *TPT = dyn_cast<TypedPointerType>(Pair.first->getType()))
161+
AS = TPT->getAddressSpace();
162+
else if (isa<PointerType>(Pair.first->getType()))
163+
AS = Pair.first->getType()->getPointerAddressSpace();
164+
return TypedPointerType::get(Pair.second, AS);
165+
}
166+
167+
BuiltinCallMutator &BuiltinCallMutator::insertArg(unsigned Index,
168+
ValueTypePair Arg) {
169+
Args.insert(Args.begin() + Index, Arg.first);
170+
PointerTypes.insert(PointerTypes.begin() + Index, toTPT(Arg));
171+
moveAttributes(CI->getContext(), Attrs, Index, Args.size() - Index,
172+
Index + 1);
173+
return *this;
174+
}
175+
176+
BuiltinCallMutator &BuiltinCallMutator::replaceArg(unsigned Index,
177+
ValueTypePair Arg) {
178+
Args[Index] = Arg.first;
179+
PointerTypes[Index] = toTPT(Arg);
180+
Attrs = Attrs.removeParamAttributes(CI->getContext(), Index);
181+
return *this;
182+
}
183+
184+
BuiltinCallMutator &BuiltinCallMutator::removeArg(unsigned Index) {
185+
// If the argument being dropped is the last one, there is nothing to move, so
186+
// just remove the attributes.
187+
if (Index == Args.size() - 1)
188+
Attrs = Attrs.removeParamAttributes(CI->getContext(), Index);
189+
else
190+
moveAttributes(CI->getContext(), Attrs, Index + 1, Args.size() - Index - 1,
191+
Index);
192+
Args.erase(Args.begin() + Index);
193+
PointerTypes.erase(PointerTypes.begin() + Index);
194+
return *this;
195+
}
196+
197+
BuiltinCallMutator &
198+
BuiltinCallMutator::changeReturnType(Type *NewReturnTy,
199+
MutateRetFuncTy MutateFunc) {
200+
ReturnTy = NewReturnTy;
201+
MutateRet = std::move(MutateFunc);
202+
return *this;
203+
}
204+
205+
BuiltinCallMutator BuiltinCallHelper::mutateCallInst(CallInst *CI,
206+
spv::Op Opcode) {
207+
return mutateCallInst(CI, getSPIRVFuncName(Opcode));
208+
}
209+
210+
BuiltinCallMutator BuiltinCallHelper::mutateCallInst(CallInst *CI,
211+
std::string FuncName) {
212+
return BuiltinCallMutator(CI, std::move(FuncName), Rules, NameMapFn);
213+
}

0 commit comments

Comments
 (0)