LLVM: lib/Target/SPIRV/SPIRVPrepareFunctions.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
33#include "llvm/IR/IntrinsicsSPIRV.h"
36#include
37
38using namespace llvm;
39
40namespace {
41
42class SPIRVPrepareFunctions : public ModulePass {
43 const SPIRVTargetMachine &TM;
44 bool substituteIntrinsicCalls(Function *F);
45 Function *removeAggregateTypesFromSignature(Function *F);
46 bool removeAggregateTypesFromCalls(Function *F);
47
48public:
49 static char ID;
50 SPIRVPrepareFunctions(const SPIRVTargetMachine &TM)
51 : ModulePass(ID), TM(TM) {}
52
53 bool runOnModule(Module &M) override;
54
55 StringRef getPassName() const override { return "SPIRV prepare functions"; }
56
57 void getAnalysisUsage(AnalysisUsage &AU) const override {
58 ModulePass::getAnalysisUsage(AU);
59 }
60};
61
64 cl::desc("Emit unknown intrinsics as calls to external functions. A "
65 "comma-separated input list of intrinsic prefixes must be "
66 "provided, and only intrinsics carrying a listed prefix get "
67 "emitted as described."),
69}
70
71char SPIRVPrepareFunctions::ID = 0;
72
74 "SPIRV prepare functions", false, false)
75
77 Function *IntrinsicFunc = II->getCalledFunction();
78 assert(IntrinsicFunc && "Missing function");
79 std::string FuncName = IntrinsicFunc->getName().str();
81 FuncName = "spirv." + FuncName;
82 return FuncName;
83}
84
89 Function *F = M->getFunction(Name);
90 if (F && F->getFunctionType() == FT)
91 return F;
93 if (F)
96 return NewF;
97}
98
100
101
102
103
106 return false;
107
109 std::string FuncName = lowerLLVMIntrinsicName(Intrinsic);
111 FuncName += ".volatile";
112
113 Function *F = M->getFunction(FuncName);
114 if (F) {
116 return true;
117 }
118
120 M->getOrInsertFunction(FuncName, Intrinsic->getFunctionType());
121 auto IntrinsicID = Intrinsic->getIntrinsicID();
122 Intrinsic->setCalledFunction(FC);
123
125 assert(F && "Callee must be a function");
126
127 switch (IntrinsicID) {
128 case Intrinsic::memset: {
133 Argument *IsVolatile = F->getArg(3);
136 Len->setName("len");
137 IsVolatile->setName("isvolatile");
140 auto *MemSet = IRB.CreateMemSet(Dest, Val, Len, MSI->getDestAlign(),
141 MSI->isVolatile());
144 MemSet->eraseFromParent();
145 break;
146 }
147 case Intrinsic::bswap: {
151 F->getArg(0));
155 break;
156 }
157 default:
158 break;
159 }
160 return true;
161}
162
165 AnnoVal = Ref->getOperand(0);
167 OptAnnoVal = Ref->getOperand(0);
168
169 std::string Anno;
173 Anno = Str;
174 }
175
176
178 C && C->getNumOperands()) {
179 Value *MaybeStruct = C->getOperand(0);
181 for (unsigned I = 0, E = Struct->getNumOperands(); I != E; ++I) {
183 Anno += (I == 0 ? ": " : ", ") +
184 std::to_string(CInt->getType()->getIntegerBitWidth() == 1
185 ? CInt->getZExtValue()
186 : CInt->getSExtValue());
187 }
189
190 for (unsigned I = 0, E = Struct->getType()->getStructNumElements();
192 Anno += I == 0 ? ": 0" : ", 0";
193 }
194 }
195 return Anno;
196}
197
199 const std::string &Anno,
202
203
204
205
206 static const std::regex R(
207 "\\{(\\d+)(?:[:,](\\d+|\"[^\"]*\")(?:,(\\d+|\"[^\"]*\"))*)?\\}");
209 int Pos = 0;
210 for (std::sregex_iterator
211 It = std::sregex_iterator(Anno.begin(), Anno.end(), R),
212 ItEnd = std::sregex_iterator();
213 It != ItEnd; ++It) {
214 if (It->position() != Pos)
216 Pos = It->position() + It->length();
217 std::smatch Match = *It;
219 for (std::size_t i = 1; i < Match.size(); ++i) {
220 std::ssub_match SMatch = Match[i];
221 std::string Item = SMatch.str();
222 if (Item.length() == 0)
223 break;
224 if (Item[0] == '"') {
225 Item = Item.substr(1, Item.length() - 2);
226
227 static const std::regex RStr("^(\\d+)(?:,(\\d+))*$");
228 if (std::smatch MatchStr; std::regex_match(Item, MatchStr, RStr)) {
229 for (std::size_t SubIdx = 1; SubIdx < MatchStr.size(); ++SubIdx)
230 if (std::string SubStr = MatchStr[SubIdx].str(); SubStr.length())
232 ConstantInt::get(Int32Ty, std::stoi(SubStr))));
233 } else {
235 }
239 } else {
241 }
242 }
243 if (MDsItem.size() == 0)
246 }
247 return Pos == static_cast<int>(Anno.length()) ? std::move(MDs)
249}
250
254
255
256 Value *PtrArg = nullptr;
258 PtrArg = BI->getOperand(0);
259 else
260 PtrArg = II->getOperand(0);
261 std::string Anno =
263 4 < II->arg_size() ? II->getArgOperand(4) : nullptr);
264
265
267
268
269
270
271
272 if (MDs.size() == 0) {
274 Int32Ty, static_cast<uint32_t>(SPIRV::Decoration::UserSemantic)));
276 }
277
278
282 Intrinsic::spv_assign_decoration, {PtrArg->getType()},
284 II->replaceAllUsesWith(II->getOperand(0));
285}
286
288
289
290
291
294 Type *FSHRetTy = FSHFuncTy->getReturnType();
295 const std::string FuncName = lowerLLVMIntrinsicName(FSHIntrinsic);
298
299 if (!FSHFunc->empty()) {
301 return;
302 }
306
307
308
311 unsigned BitWidth = IntTy->getIntegerBitWidth();
313 Value *BitWidthForInsts =
314 VectorTy
316 : BitWidthConstant;
317 Value *RotateModVal =
318 IRB.CreateURem( FSHFunc->getArg(2), BitWidthForInsts);
319 Value *FirstShift = nullptr, *SecShift = nullptr;
320 if (FSHIntrinsic->getIntrinsicID() == Intrinsic::fshr) {
321
322
323 FirstShift = IRB.CreateLShr(FSHFunc->getArg(1), RotateModVal);
324 } else {
325
326
327 FirstShift = IRB.CreateShl(FSHFunc->getArg(0), RotateModVal);
328 }
329
330
331
332 Value *SubRotateVal = IRB.CreateSub(BitWidthForInsts, RotateModVal);
333 if (FSHIntrinsic->getIntrinsicID() == Intrinsic::fshr) {
334
335
336 SecShift = IRB.CreateShl(FSHFunc->getArg(0), SubRotateVal);
337 } else {
338
339
341 }
342
344
346}
347
351 if (!ConstrainedCmpIntrinsic)
352 return;
353
357 IRBuilder<> Builder(ConstrainedCmpIntrinsic);
358 Value *FCmp = Builder.CreateFCmp(Pred, LHS, RHS);
361}
362
364
365
366
367
368
369
370
371
372
373 if (II->getIntrinsicID() == Intrinsic::assume) {
375 II->getModule(), Intrinsic::SPVIntrinsics::spv_assume);
377 } else if (II->getIntrinsicID() == Intrinsic::expect) {
379 II->getModule(), Intrinsic::SPVIntrinsics::spv_expect,
380 {II->getOperand(0)->getType()});
382 } else {
384 }
385}
386
390 std::optional Size =
391 Alloca->getAllocationSize(Alloca->getDataLayout());
392 Value *SizeVal = Builder.getInt64(Size ? *Size : -1);
393 Builder.CreateIntrinsic(NewID, Alloca->getType(),
394 {SizeVal, II->getArgOperand(0)});
395 II->eraseFromParent();
396 return true;
397}
398
399
400
401bool SPIRVPrepareFunctions::substituteIntrinsicCalls(Function *F) {
403 const SPIRVSubtarget &STI = TM.getSubtarget(*F);
405 for (BasicBlock &BB : *F) {
409 continue;
412 continue;
414 switch (II->getIntrinsicID()) {
415 case Intrinsic::memset:
416 case Intrinsic::bswap:
418 break;
419 case Intrinsic::fshl:
420 case Intrinsic::fshr:
423 break;
424 case Intrinsic::assume:
425 case Intrinsic::expect:
426 if (STI.canUseExtension(SPIRV::Extension::SPV_KHR_expect_assume))
429 break;
430 case Intrinsic::lifetime_start:
433 II, Intrinsic::SPVIntrinsics::spv_lifetime_start);
434 } else {
435 II->eraseFromParent();
437 }
438 break;
439 case Intrinsic::lifetime_end:
442 II, Intrinsic::SPVIntrinsics::spv_lifetime_end);
443 } else {
444 II->eraseFromParent();
446 }
447 break;
448 case Intrinsic::ptr_annotation:
451 break;
452 case Intrinsic::experimental_constrained_fcmp:
453 case Intrinsic::experimental_constrained_fcmps:
455 EraseFromParent);
457 break;
458 default:
460 any_of(SPVAllowUnknownIntrinsics, [II](auto &&Prefix) {
462 return false;
463 return II->getCalledFunction()->getName().starts_with(Prefix);
464 }))
466 break;
467 }
468 }
469 }
470 for (auto *I : EraseFromParent)
471 I->eraseFromParent();
473}
474
475static void
477 SmallVector<std::pair<int, Type *>> ChangedTys,
479
482
485 transform(ChangedTys, std::back_inserter(MDArgs), [=, &Ctx](auto &&CTy) {
489 });
491}
492
493
494
496SPIRVPrepareFunctions::removeAggregateTypesFromSignature(Function *F) {
497 bool IsRetAggr = F->getReturnType()->isAggregateType();
498
499 if (F->isIntrinsic() && IsRetAggr)
500 return F;
501
503
504 bool HasAggrArg = llvm::any_of(F->args(), [](Argument &Arg) {
505 return Arg.getType()->isAggregateType();
506 });
507 bool DoClone = IsRetAggr || HasAggrArg;
508 if (!DoClone)
509 return F;
511 Type *RetType = IsRetAggr ? B.getInt32Ty() : F->getReturnType();
512 if (IsRetAggr)
513 ChangedTypes.push_back(std::pair<int, Type *>(-1, F->getReturnType()));
515 for (const auto &Arg : F->args()) {
516 if (Arg.getType()->isAggregateType()) {
519 std::pair<int, Type *>(Arg.getArgNo(), Arg.getType()));
520 } else
521 ArgTypes.push_back(Arg.getType());
522 }
523 FunctionType *NewFTy =
524 FunctionType::get(RetType, ArgTypes, F->getFunctionType()->isVarArg());
527 F->getName(), F->getParent());
528
530 auto NewFArgIt = NewF->arg_begin();
531 for (auto &Arg : F->args()) {
532 StringRef ArgName = Arg.getName();
533 NewFArgIt->setName(ArgName);
534 VMap[&Arg] = &(*NewFArgIt++);
535 }
537
538 CloneFunctionInto(NewF, F, VMap, CloneFunctionChangeType::LocalChangesOnly,
539 Returns);
541
544 std::move(ChangedTypes), NewF->getName());
545
547 if (CallInst *CI;
551 C->handleOperandChange(F, NewF);
552 else
553 U->replaceUsesOfWith(F, NewF);
554 }
555
556
557 if (RetType != F->getReturnType())
558 TM.getSubtarget(*F).getSPIRVGlobalRegistry()->addMutated(
559 NewF, F->getReturnType());
560 return NewF;
561}
562
563
564
565
566bool SPIRVPrepareFunctions::removeAggregateTypesFromCalls(Function *F) {
567 if (F->isDeclaration() || F->isIntrinsic())
568 return false;
569
573 if (!CB->getCalledOperand() || CB->getCalledFunction())
574 continue;
575 if (CB->getType()->isAggregateType() ||
577 [](auto &&Arg) { return Arg->getType()->isAggregateType(); }))
579 }
580 }
581
582 if (Calls.empty())
583 return false;
584
586
587 for (auto &&[CB, NewFnTy] : Calls) {
590
591 Type *RetTy = CB->getType();
595 }
596
597 for (auto &&Arg : CB->args()) {
598 if (Arg->getType()->isAggregateType()) {
599 NewArgTypes.push_back(B.getInt32Ty());
600 ChangedTypes.emplace_back(Arg.getOperandNo(), Arg->getType());
601 } else {
602 NewArgTypes.push_back(Arg->getType());
603 }
604 }
605 NewFnTy = FunctionType::get(RetTy, NewArgTypes,
606 CB->getFunctionType()->isVarArg());
607
608 if (!CB->hasName())
609 CB->setName("spv.mutated_callsite." + F->getName());
610 else
611 CB->setName("spv.named_mutated_callsite." + F->getName() + "." +
612 CB->getName());
613
615 F->getParent()->getOrInsertNamedMetadata("spv.mutated_callsites"),
616 std::move(ChangedTypes), CB->getName());
617 }
618
619 for (auto &&[CB, NewFTy] : Calls) {
620 if (NewFTy->getReturnType() != CB->getType())
621 TM.getSubtarget(*F).getSPIRVGlobalRegistry()->addMutated(
622 CB, CB->getType());
623 CB->mutateFunctionType(NewFTy);
624 }
625
626 return true;
627}
628
629bool SPIRVPrepareFunctions::runOnModule(Module &M) {
631 for (Function &F : M) {
632 Changed |= substituteIntrinsicCalls(&F);
634 Changed |= removeAggregateTypesFromCalls(&F);
635 }
636
637 std::vector<Function *> FuncsWorklist;
638 for (auto &F : M)
639 FuncsWorklist.push_back(&F);
640
641 for (auto *F : FuncsWorklist) {
642 Function *NewF = removeAggregateTypesFromSignature(F);
643
644 if (NewF != F) {
645 F->eraseFromParent();
647 }
648 }
650}
651
652ModulePass *
654 return new SPIRVPrepareFunctions(TM);
655}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
Expand Atomic instructions
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
Machine Check Debug Module
uint64_t IntrinsicInst * II
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
static void lowerFunnelShifts(IntrinsicInst *FSHIntrinsic)
Definition SPIRVPrepareFunctions.cpp:287
static std::string getAnnotation(Value *AnnoVal, Value *OptAnnoVal)
Definition SPIRVPrepareFunctions.cpp:163
static bool lowerIntrinsicToFunction(IntrinsicInst *Intrinsic)
Definition SPIRVPrepareFunctions.cpp:99
static void lowerConstrainedFPCmpIntrinsic(ConstrainedFPCmpIntrinsic *ConstrainedCmpIntrinsic, SmallVector< Instruction * > &EraseFromParent)
Definition SPIRVPrepareFunctions.cpp:348
static void lowerPtrAnnotation(IntrinsicInst *II)
Definition SPIRVPrepareFunctions.cpp:251
static SmallVector< Metadata * > parseAnnotation(Value *I, const std::string &Anno, LLVMContext &Ctx, Type *Int32Ty)
Definition SPIRVPrepareFunctions.cpp:198
static bool toSpvLifetimeIntrinsic(IntrinsicInst *II, Intrinsic::ID NewID)
Definition SPIRVPrepareFunctions.cpp:387
static void lowerExpectAssume(IntrinsicInst *II)
Definition SPIRVPrepareFunctions.cpp:363
static void addFunctionTypeMutation(NamedMDNode *NMD, SmallVector< std::pair< int, Type * > > ChangedTys, StringRef Name)
Definition SPIRVPrepareFunctions.cpp:476
static Function * getOrCreateFunction(Module *M, Type *RetTy, ArrayRef< Type * > ArgTypes, StringRef Name)
Definition SPIRVPrepareFunctions.cpp:85
This class represents an incoming formal argument to a Function.
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
LLVM Basic Block Representation.
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
Value * getArgOperand(unsigned i) const
FunctionType * getFunctionType() const
void setCalledFunction(Function *Fn)
Sets the function called, including updating the function type.
Predicate
This enumeration lists the possible predicates for CmpInst subclasses.
This is the shared class of boolean and integer constants.
static LLVM_ABI Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
Constrained floating point compare intrinsics.
LLVM_ABI FCmpInst::Predicate getPredicate() const
Class to represent fixed width SIMD vectors.
unsigned getNumElements() const
A handy container for a FunctionType+Callee-pointer pair, which can be passed around as a single enti...
static LLVM_ABI FunctionType * get(Type *Result, ArrayRef< Type * > Params, bool isVarArg)
This static method is the primary way of constructing a FunctionType.
static Function * Create(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, const Twine &N="", Module *M=nullptr)
FunctionType * getFunctionType() const
Returns the FunctionType for me.
bool isIntrinsic() const
isIntrinsic - Returns true if the function's name starts with "llvm.".
Type * getReturnType() const
Returns the type of the ret val.
void setCallingConv(CallingConv::ID CC)
Argument * getArg(unsigned i) const
Module * getParent()
Get the module that this global value is contained inside of...
void setDSOLocal(bool Local)
@ ExternalLinkage
Externally visible function.
LLVM_ABI Value * CreateVectorSplat(unsigned NumElts, Value *V, const Twine &Name="")
Return a vector value that contains.
Value * CreateLShr(Value *LHS, Value *RHS, const Twine &Name="", bool isExact=false)
ReturnInst * CreateRet(Value *V)
Create a 'ret ' instruction.
LLVM_ABI CallInst * CreateIntrinsic(Intrinsic::ID ID, ArrayRef< Type * > Types, ArrayRef< Value * > Args, FMFSource FMFSource={}, const Twine &Name="")
Create a call to intrinsic ID with Args, mangled using Types.
Value * CreateSub(Value *LHS, Value *RHS, const Twine &Name="", bool HasNUW=false, bool HasNSW=false)
Value * CreateShl(Value *LHS, Value *RHS, const Twine &Name="", bool HasNUW=false, bool HasNSW=false)
CallInst * CreateMemSet(Value *Ptr, Value *Val, uint64_t Size, MaybeAlign Align, bool isVolatile=false, const AAMDNodes &AAInfo=AAMDNodes())
Create and insert a memset to the specified pointer and the specified value.
ReturnInst * CreateRetVoid()
Create a 'ret void' instruction.
void SetInsertPoint(BasicBlock *TheBB)
This specifies that created instructions should be appended to the end of the specified block.
Value * CreateOr(Value *LHS, Value *RHS, const Twine &Name="", bool IsDisjoint=false)
ConstantInt * getInt(const APInt &AI)
Get a constant integer value.
Value * CreateURem(Value *LHS, Value *RHS, const Twine &Name="")
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
LLVM_ABI const Module * getModule() const
Return the module owning the function this instruction belongs to or nullptr it the function does not...
A wrapper class for inspecting calls to intrinsic functions.
Intrinsic::ID getIntrinsicID() const
Return the intrinsic ID of this intrinsic.
void LowerIntrinsicCall(CallInst *CI)
Replace a call to the specified intrinsic function.
This is an important class for using LLVM in a threaded context.
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
static LLVM_ABI MDString * get(LLVMContext &Context, StringRef Str)
This class wraps the llvm.memset and llvm.memset.inline intrinsics.
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
A Module instance is used to store all the information related to an LLVM module.
LLVMContext & getContext() const
Get the global data context.
NamedMDNode * getOrInsertNamedMetadata(StringRef Name)
Return the named MDNode in the module with the specified name.
Module * getParent()
Get the module that holds this named metadata collection.
LLVM_ABI void addOperand(MDNode *M)
bool canUseExtension(SPIRV::Extension::Extension E) const
reference emplace_back(ArgTypes &&... Args)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
StringRef - Represent a constant reference to a string, i.e.
std::string str() const
str - Get the contents as an std::string.
const Triple & getTargetTriple() const
const STC & getSubtarget(const Function &F) const
This method returns a pointer to the specified type of TargetSubtargetInfo.
VendorType getVendor() const
Get the parsed vendor type of this triple.
The instances of the Type class are immutable: once they are created, they are never changed.
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
bool isAggregateType() const
Return true if the type is an aggregate type.
LLVM Value Representation.
Type * getType() const
All values are typed, get the type of this value.
LLVM_ABI void setName(const Twine &Name)
Change the name of the value.
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
LLVM_ABI void takeName(Value *V)
Transfer the name from V to this value.
Type * getElementType() const
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ SPIR_FUNC
Used for SPIR non-kernel device functions.
@ C
The default llvm calling convention, compatible with C.
This namespace contains an enum with a value for every intrinsic/builtin function known by LLVM.
LLVM_ABI Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > Tys={})
Look up the Function declaration of the intrinsic id in the Module M.
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) dyn_cast(const From &Val)
dyn_cast - Return the argument parameter cast to the specified type.
FunctionAddr VTableAddr uintptr_t uintptr_t Int32Ty
LLVM_ABI bool getConstantStringInfo(const Value *V, StringRef &Str, bool TrimAtNul=true)
This function computes the length of a null-terminated C string pointed to by V.
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
bool sortBlocks(Function &F)
auto dyn_cast_or_null(const Y &Val)
OutputIt transform(R &&Range, OutputIt d_first, UnaryFunction F)
Wrapper function around std::transform to apply a function to a range and store the result elsewhere.
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa - Return true if the parameter to the template is an instance of one of the template type argu...
@ Ref
The access may reference the value stored in memory.
IRBuilder(LLVMContext &, FolderTy, InserterTy, MDNode *, ArrayRef< OperandBundleDef >) -> IRBuilder< FolderTy, InserterTy >
void replace(R &&Range, const T &OldValue, const T &NewValue)
Provide wrappers to std::replace which take ranges instead of having to pass begin/end explicitly.
constexpr unsigned BitWidth
ValueMap< const Value *, WeakTrackingVH > ValueToValueMapTy
LLVM_ABI void CloneFunctionInto(Function *NewFunc, const Function *OldFunc, ValueToValueMapTy &VMap, CloneFunctionChangeType Changes, SmallVectorImpl< ReturnInst * > &Returns, const char *NameSuffix="", ClonedCodeInfo *CodeInfo=nullptr, ValueMapTypeRemapper *TypeMapper=nullptr, ValueMaterializer *Materializer=nullptr)
Clone OldFunc into NewFunc, transforming the old arguments into references to VMap values.
decltype(auto) cast(const From &Val)
cast - Return the argument parameter cast to the specified type.
bool to_integer(StringRef S, N &Num, unsigned Base=0)
Convert the string S to an integer of the specified type using the radix Base. If Base is 0,...
ModulePass * createSPIRVPrepareFunctionsPass(const SPIRVTargetMachine &TM)
Definition SPIRVPrepareFunctions.cpp:653
LLVM_ABI void expandMemSetAsLoop(MemSetInst *MemSet)
Expand MemSet as a loop. MemSet is not deleted.
Implement std::hash so that hash_code can be used in STL containers.