LLVM 23.0.0git
SPIRVPrepareFunctions.cpp
Go to the documentation of this file.
1//===-- SPIRVPrepareFunctions.cpp - modify function signatures --*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This pass modifies function signatures containing aggregate arguments
10// and/or return value before IRTranslator. Information about the original
11// signatures is stored in metadata. It is used during call lowering to
12// restore correct SPIR-V types of function arguments and return values.
13// This pass also substitutes some llvm intrinsic calls with calls to newly
14// generated functions (as the Khronos LLVM/SPIR-V Translator does).
15//
16// NOTE: this pass is a module-level one due to the necessity to modify
17// GVs/functions.
18//
19//===----------------------------------------------------------------------===//
20
22#include "SPIRV.h"
23#include "SPIRVSubtarget.h"
24#include "SPIRVTargetMachine.h"
25#include "SPIRVUtils.h"
30#include "llvm/IR/IRBuilder.h"
34#include "llvm/IR/Intrinsics.h"
35#include "llvm/IR/IntrinsicsSPIRV.h"
38#include <regex>
39
40using namespace llvm;
41
42namespace {
43
44class SPIRVPrepareFunctionsImpl {
45 const SPIRVTargetMachine &TM;
46 bool substituteIntrinsicCalls(Function *F);
47 Function *removeAggregateTypesFromSignature(Function *F);
48 bool removeAggregateTypesFromCalls(Function *F);
49
50public:
51 SPIRVPrepareFunctionsImpl(const SPIRVTargetMachine &TM) : TM(TM) {}
52 bool runOnModule(Module &M);
53};
54
55class SPIRVPrepareFunctionsLegacy : public ModulePass {
56 const SPIRVTargetMachine &TM;
57
58public:
59 static char ID;
60 SPIRVPrepareFunctionsLegacy(const SPIRVTargetMachine &TM)
61 : ModulePass(ID), TM(TM) {}
62
63 bool runOnModule(Module &M) override {
64 return SPIRVPrepareFunctionsImpl(TM).runOnModule(M);
65 }
66
67 StringRef getPassName() const override { return "SPIRV prepare functions"; }
68};
69
70static cl::list<std::string> SPVAllowUnknownIntrinsics(
71 "spv-allow-unknown-intrinsics", cl::CommaSeparated,
72 cl::desc("Emit unknown intrinsics as calls to external functions. A "
73 "comma-separated input list of intrinsic prefixes must be "
74 "provided, and only intrinsics carrying a listed prefix get "
75 "emitted as described."),
76 cl::value_desc("intrinsic_prefix_0,intrinsic_prefix_1"), cl::ValueOptional);
77} // namespace
78
79char SPIRVPrepareFunctionsLegacy::ID = 0;
80
81INITIALIZE_PASS(SPIRVPrepareFunctionsLegacy, "spirv-prepare-functions",
82 "SPIRV prepare functions", false, false)
83
84static std::string lowerLLVMIntrinsicName(IntrinsicInst *II) {
85 Function *IntrinsicFunc = II->getCalledFunction();
86 assert(IntrinsicFunc && "Missing function");
87 std::string FuncName = IntrinsicFunc->getName().str();
88 llvm::replace(FuncName, '.', '_');
89 FuncName = "spirv." + FuncName;
90 return FuncName;
91}
92
94 ArrayRef<Type *> ArgTypes,
95 StringRef Name) {
96 FunctionType *FT = FunctionType::get(RetTy, ArgTypes, false);
97 Function *F = M->getFunction(Name);
98 if (F && F->getFunctionType() == FT)
99 return F;
101 if (F)
102 NewF->setDSOLocal(F->isDSOLocal());
104 return NewF;
105}
106
108 const TargetTransformInfo &TTI) {
109 // For @llvm.memset.* intrinsic cases with constant value and length arguments
110 // are emulated via "storing" a constant array to the destination. For other
111 // cases we wrap the intrinsic in @spirv.llvm_memset_* function and expand the
112 // intrinsic to a loop via expandMemSetAsLoop().
113 if (auto *MSI = dyn_cast<MemSetInst>(Intrinsic))
114 if (isa<Constant>(MSI->getValue()) && isa<ConstantInt>(MSI->getLength()))
115 return false; // It is handled later using OpCopyMemorySized.
116
117 Module *M = Intrinsic->getModule();
118 std::string FuncName = lowerLLVMIntrinsicName(Intrinsic);
119 if (Intrinsic->isVolatile())
120 FuncName += ".volatile";
121 // Redirect @llvm.intrinsic.* call to @spirv.llvm_intrinsic_*
122 Function *F = M->getFunction(FuncName);
123 if (F) {
124 Intrinsic->setCalledFunction(F);
125 return true;
126 }
127 // TODO copy arguments attributes: nocapture writeonly.
128 FunctionCallee FC =
129 M->getOrInsertFunction(FuncName, Intrinsic->getFunctionType());
130 auto IntrinsicID = Intrinsic->getIntrinsicID();
131 Intrinsic->setCalledFunction(FC);
132
133 F = dyn_cast<Function>(FC.getCallee());
134 assert(F && "Callee must be a function");
135
136 switch (IntrinsicID) {
137 case Intrinsic::memset: {
138 auto *MSI = static_cast<MemSetInst *>(Intrinsic);
139 Argument *Dest = F->getArg(0);
140 Argument *Val = F->getArg(1);
141 Argument *Len = F->getArg(2);
142 Argument *IsVolatile = F->getArg(3);
143 Dest->setName("dest");
144 Val->setName("val");
145 Len->setName("len");
146 IsVolatile->setName("isvolatile");
147 BasicBlock *EntryBB = BasicBlock::Create(M->getContext(), "entry", F);
148 IRBuilder<> IRB(EntryBB);
149 auto *MemSet = IRB.CreateMemSet(Dest, Val, Len, MSI->getDestAlign(),
150 MSI->isVolatile());
151 IRB.CreateRetVoid();
153 MemSet->eraseFromParent();
154 break;
155 }
156 case Intrinsic::bswap: {
157 BasicBlock *EntryBB = BasicBlock::Create(M->getContext(), "entry", F);
158 IRBuilder<> IRB(EntryBB);
159 auto *BSwap = IRB.CreateIntrinsic(Intrinsic::bswap, Intrinsic->getType(),
160 F->getArg(0));
161 IRB.CreateRet(BSwap);
162 IntrinsicLowering IL(M->getDataLayout());
163 IL.LowerIntrinsicCall(BSwap);
164 break;
165 }
166 default:
167 break;
168 }
169 return true;
170}
171
172static std::string getAnnotation(Value *AnnoVal, Value *OptAnnoVal) {
173 if (auto *Ref = dyn_cast_or_null<GetElementPtrInst>(AnnoVal))
174 AnnoVal = Ref->getOperand(0);
175 if (auto *Ref = dyn_cast_or_null<BitCastInst>(OptAnnoVal))
176 OptAnnoVal = Ref->getOperand(0);
177
178 std::string Anno;
179 if (auto *C = dyn_cast_or_null<Constant>(AnnoVal)) {
180 StringRef Str;
181 if (getConstantStringInfo(C, Str))
182 Anno = Str;
183 }
184 // handle optional annotation parameter in a way that Khronos Translator do
185 // (collect integers wrapped in a struct)
186 if (auto *C = dyn_cast_or_null<Constant>(OptAnnoVal);
187 C && C->getNumOperands()) {
188 Value *MaybeStruct = C->getOperand(0);
189 if (auto *Struct = dyn_cast<ConstantStruct>(MaybeStruct)) {
190 for (unsigned I = 0, E = Struct->getNumOperands(); I != E; ++I) {
191 if (auto *CInt = dyn_cast<ConstantInt>(Struct->getOperand(I)))
192 Anno += (I == 0 ? ": " : ", ") +
193 std::to_string(CInt->getType()->getIntegerBitWidth() == 1
194 ? CInt->getZExtValue()
195 : CInt->getSExtValue());
196 }
197 } else if (auto *Struct = dyn_cast<ConstantAggregateZero>(MaybeStruct)) {
198 // { i32 i32 ... } zeroinitializer
199 for (unsigned I = 0, E = Struct->getType()->getStructNumElements();
200 I != E; ++I)
201 Anno += I == 0 ? ": 0" : ", 0";
202 }
203 }
204 return Anno;
205}
206
208 const std::string &Anno,
209 LLVMContext &Ctx,
210 Type *Int32Ty) {
211 // Try to parse the annotation string according to the following rules:
212 // annotation := ({kind} | {kind:value,value,...})+
213 // kind := number
214 // value := number | string
215 static const std::regex R(
216 "\\{(\\d+)(?:[:,](\\d+|\"[^\"]*\")(?:,(\\d+|\"[^\"]*\"))*)?\\}");
218 int Pos = 0;
219 for (std::sregex_iterator
220 It = std::sregex_iterator(Anno.begin(), Anno.end(), R),
221 ItEnd = std::sregex_iterator();
222 It != ItEnd; ++It) {
223 if (It->position() != Pos)
225 Pos = It->position() + It->length();
226 std::smatch Match = *It;
228 for (std::size_t i = 1; i < Match.size(); ++i) {
229 std::ssub_match SMatch = Match[i];
230 std::string Item = SMatch.str();
231 if (Item.length() == 0)
232 break;
233 if (Item[0] == '"') {
234 Item = Item.substr(1, Item.length() - 2);
235 // Acceptable format of the string snippet is:
236 static const std::regex RStr("^(\\d+)(?:,(\\d+))*$");
237 if (std::smatch MatchStr; std::regex_match(Item, MatchStr, RStr)) {
238 for (std::size_t SubIdx = 1; SubIdx < MatchStr.size(); ++SubIdx)
239 if (std::string SubStr = MatchStr[SubIdx].str(); SubStr.length())
241 ConstantInt::get(Int32Ty, std::stoi(SubStr))));
242 } else {
243 MDsItem.push_back(MDString::get(Ctx, Item));
244 }
245 } else if (int32_t Num; llvm::to_integer(StringRef(Item), Num, 10)) {
246 MDsItem.push_back(
247 ConstantAsMetadata::get(ConstantInt::get(Int32Ty, Num)));
248 } else {
249 MDsItem.push_back(MDString::get(Ctx, Item));
250 }
251 }
252 if (MDsItem.size() == 0)
254 MDs.push_back(MDNode::get(Ctx, MDsItem));
255 }
256 return Pos == static_cast<int>(Anno.length()) ? std::move(MDs)
258}
259
261 LLVMContext &Ctx = II->getContext();
263
264 // Retrieve an annotation string from arguments.
265 Value *PtrArg = nullptr;
266 if (auto *BI = dyn_cast<BitCastInst>(II->getArgOperand(0)))
267 PtrArg = BI->getOperand(0);
268 else
269 PtrArg = II->getOperand(0);
270 std::string Anno =
271 getAnnotation(II->getArgOperand(1),
272 4 < II->arg_size() ? II->getArgOperand(4) : nullptr);
273
274 // Parse the annotation.
276
277 // If the annotation string is not parsed successfully we don't know the
278 // format used and output it as a general UserSemantic decoration.
279 // Otherwise MDs is a Metadata tuple (a decoration list) in the format
280 // expected by `spirv.Decorations`.
281 if (MDs.size() == 0) {
282 auto UserSemantic = ConstantAsMetadata::get(ConstantInt::get(
283 Int32Ty, static_cast<uint32_t>(SPIRV::Decoration::UserSemantic)));
284 MDs.push_back(MDNode::get(Ctx, {UserSemantic, MDString::get(Ctx, Anno)}));
285 }
286
287 // Build the internal intrinsic function.
288 IRBuilder<> IRB(II->getParent());
289 IRB.SetInsertPoint(II);
290 IRB.CreateIntrinsic(
291 Intrinsic::spv_assign_decoration, {PtrArg->getType()},
292 {PtrArg, MetadataAsValue::get(Ctx, MDNode::get(Ctx, MDs))});
293 II->replaceAllUsesWith(II->getOperand(0));
294}
295
296static void lowerFunnelShifts(IntrinsicInst *FSHIntrinsic) {
297 // Get a separate function - otherwise, we'd have to rework the CFG of the
298 // current one. Then simply replace the intrinsic uses with a call to the new
299 // function.
300 // Generate LLVM IR for i* @spirv.llvm_fsh?_i* (i* %a, i* %b, i* %c)
301 Module *M = FSHIntrinsic->getModule();
302 FunctionType *FSHFuncTy = FSHIntrinsic->getFunctionType();
303 Type *FSHRetTy = FSHFuncTy->getReturnType();
304 const std::string FuncName = lowerLLVMIntrinsicName(FSHIntrinsic);
305 Function *FSHFunc =
306 getOrCreateFunction(M, FSHRetTy, FSHFuncTy->params(), FuncName);
307
308 if (!FSHFunc->empty()) {
309 FSHIntrinsic->setCalledFunction(FSHFunc);
310 return;
311 }
312 BasicBlock *RotateBB = BasicBlock::Create(M->getContext(), "rotate", FSHFunc);
313 IRBuilder<> IRB(RotateBB);
314 Type *Ty = FSHFunc->getReturnType();
315 // Build the actual funnel shift rotate logic.
316 // In the comments, "int" is used interchangeably with "vector of int
317 // elements".
319 Type *IntTy = VectorTy ? VectorTy->getElementType() : Ty;
320 unsigned BitWidth = IntTy->getIntegerBitWidth();
321 ConstantInt *BitWidthConstant = IRB.getInt({BitWidth, BitWidth});
322 Value *BitWidthForInsts =
323 VectorTy
324 ? IRB.CreateVectorSplat(VectorTy->getNumElements(), BitWidthConstant)
325 : BitWidthConstant;
326 Value *RotateModVal =
327 IRB.CreateURem(/*Rotate*/ FSHFunc->getArg(2), BitWidthForInsts);
328 Value *FirstShift = nullptr, *SecShift = nullptr;
329 if (FSHIntrinsic->getIntrinsicID() == Intrinsic::fshr) {
330 // Shift the less significant number right, the "rotate" number of bits
331 // will be 0-filled on the left as a result of this regular shift.
332 FirstShift = IRB.CreateLShr(FSHFunc->getArg(1), RotateModVal);
333 } else {
334 // Shift the more significant number left, the "rotate" number of bits
335 // will be 0-filled on the right as a result of this regular shift.
336 FirstShift = IRB.CreateShl(FSHFunc->getArg(0), RotateModVal);
337 }
338 // We want the "rotate" number of the more significant int's LSBs (MSBs) to
339 // occupy the leftmost (rightmost) "0 space" left by the previous operation.
340 // Therefore, subtract the "rotate" number from the integer bitsize...
341 Value *SubRotateVal = IRB.CreateSub(BitWidthForInsts, RotateModVal);
342 if (FSHIntrinsic->getIntrinsicID() == Intrinsic::fshr) {
343 // ...and left-shift the more significant int by this number, zero-filling
344 // the LSBs.
345 SecShift = IRB.CreateShl(FSHFunc->getArg(0), SubRotateVal);
346 } else {
347 // ...and right-shift the less significant int by this number, zero-filling
348 // the MSBs.
349 SecShift = IRB.CreateLShr(FSHFunc->getArg(1), SubRotateVal);
350 }
351 // A simple binary addition of the shifted ints yields the final result.
352 IRB.CreateRet(IRB.CreateOr(FirstShift, SecShift));
353
354 FSHIntrinsic->setCalledFunction(FSHFunc);
355}
356
358 ConstrainedFPCmpIntrinsic *ConstrainedCmpIntrinsic,
359 SmallVector<Instruction *> &EraseFromParent) {
360 if (!ConstrainedCmpIntrinsic)
361 return;
362 // Extract the floating-point values being compared
363 Value *LHS = ConstrainedCmpIntrinsic->getArgOperand(0);
364 Value *RHS = ConstrainedCmpIntrinsic->getArgOperand(1);
365 FCmpInst::Predicate Pred = ConstrainedCmpIntrinsic->getPredicate();
366 IRBuilder<> Builder(ConstrainedCmpIntrinsic);
367 Value *FCmp = Builder.CreateFCmp(Pred, LHS, RHS);
368 ConstrainedCmpIntrinsic->replaceAllUsesWith(FCmp);
369 EraseFromParent.push_back(dyn_cast<Instruction>(ConstrainedCmpIntrinsic));
370}
371
373 // If we cannot use the SPV_KHR_expect_assume extension, then we need to
374 // ignore the intrinsic and move on. It should be removed later on by LLVM.
375 // Otherwise we should lower the intrinsic to the corresponding SPIR-V
376 // instruction.
377 // For @llvm.assume we have OpAssumeTrueKHR.
378 // For @llvm.expect we have OpExpectKHR.
379 //
380 // We need to lower this into a builtin and then the builtin into a SPIR-V
381 // instruction.
382 if (II->getIntrinsicID() == Intrinsic::assume) {
384 II->getModule(), Intrinsic::SPVIntrinsics::spv_assume);
385 II->setCalledFunction(F);
386 } else if (II->getIntrinsicID() == Intrinsic::expect) {
388 II->getModule(), Intrinsic::SPVIntrinsics::spv_expect,
389 {II->getOperand(0)->getType()});
390 II->setCalledFunction(F);
391 } else {
392 llvm_unreachable("Unknown intrinsic");
393 }
394}
395
397 auto *LifetimeArg0 = II->getArgOperand(0);
398
399 // If the lifetime argument is a poison value, the intrinsic has no effect.
400 if (isa<PoisonValue>(LifetimeArg0)) {
401 II->eraseFromParent();
402 return true;
403 }
404
405 IRBuilder<> Builder(II);
406 auto *Alloca = cast<AllocaInst>(LifetimeArg0);
407 std::optional<TypeSize> Size =
408 Alloca->getAllocationSize(Alloca->getDataLayout());
409 Value *SizeVal = Builder.getInt64(Size ? *Size : -1);
410 Builder.CreateIntrinsic(NewID, Alloca->getType(), {SizeVal, LifetimeArg0});
411 II->eraseFromParent();
412 return true;
413}
414
415static void
417 SmallVector<Instruction *> &EraseFromParent) {
418 auto *FPI = cast<ConstrainedFPIntrinsic>(II);
419 Value *A = FPI->getArgOperand(0);
420 Value *Mul = FPI->getArgOperand(1);
421 Value *Add = FPI->getArgOperand(2);
422 IRBuilder<> Builder(II->getParent());
423 Builder.SetInsertPoint(II);
424 std::optional<RoundingMode> Rounding = FPI->getRoundingMode();
425 Value *Product = Builder.CreateFMul(A, Mul, II->getName() + ".mul");
426 Value *Result = Builder.CreateConstrainedFPBinOp(
427 Intrinsic::experimental_constrained_fadd, Product, Add, {},
428 II->getName() + ".add", nullptr, Rounding);
429 II->replaceAllUsesWith(Result);
430 EraseFromParent.push_back(II);
431}
432
433// Substitutes calls to LLVM intrinsics with either calls to SPIR-V intrinsics
434// or calls to proper generated functions. Returns True if F was modified.
435bool SPIRVPrepareFunctionsImpl::substituteIntrinsicCalls(Function *F) {
436 bool Changed = false;
437 const SPIRVSubtarget &STI = TM.getSubtarget<SPIRVSubtarget>(*F);
438 SmallVector<Instruction *> EraseFromParent;
439 const TargetTransformInfo &TTI = TM.getTargetTransformInfo(*F);
440 for (BasicBlock &BB : *F) {
441 for (Instruction &I : make_early_inc_range(BB)) {
442 auto Call = dyn_cast<CallInst>(&I);
443 if (!Call)
444 continue;
446 if (!CF || !CF->isIntrinsic())
447 continue;
448 auto *II = cast<IntrinsicInst>(Call);
449 if (Intrinsic::isTargetIntrinsic(II->getIntrinsicID()) &&
450 II->getCalledOperand()->getName().starts_with("llvm.spv"))
451 continue;
452 switch (II->getIntrinsicID()) {
453 case Intrinsic::memset:
454 case Intrinsic::bswap:
456 break;
457 case Intrinsic::fshl:
458 case Intrinsic::fshr:
460 Changed = true;
461 break;
462 case Intrinsic::assume:
463 case Intrinsic::expect:
464 if (STI.canUseExtension(SPIRV::Extension::SPV_KHR_expect_assume))
466 Changed = true;
467 break;
468 case Intrinsic::lifetime_start:
469 if (!STI.isShader()) {
471 II, Intrinsic::SPVIntrinsics::spv_lifetime_start);
472 } else {
473 II->eraseFromParent();
474 Changed = true;
475 }
476 break;
477 case Intrinsic::lifetime_end:
478 if (!STI.isShader()) {
480 II, Intrinsic::SPVIntrinsics::spv_lifetime_end);
481 } else {
482 II->eraseFromParent();
483 Changed = true;
484 }
485 break;
486 case Intrinsic::ptr_annotation:
488 Changed = true;
489 break;
490 case Intrinsic::experimental_constrained_fmuladd:
491 lowerConstrainedFmuladd(II, EraseFromParent);
492 Changed = true;
493 break;
494 case Intrinsic::experimental_constrained_fcmp:
495 case Intrinsic::experimental_constrained_fcmps:
497 EraseFromParent);
498 Changed = true;
499 break;
500 default:
501 if (TM.getTargetTriple().getVendor() == Triple::AMD ||
502 any_of(SPVAllowUnknownIntrinsics, [II](auto &&Prefix) {
503 if (Prefix.empty())
504 return false;
505 return II->getCalledFunction()->getName().starts_with(Prefix);
506 }))
508 break;
509 }
510 }
511 }
512 for (auto *I : EraseFromParent)
513 I->eraseFromParent();
514 return Changed;
515}
516
517static void
519 SmallVector<std::pair<int, Type *>> ChangedTys,
520 StringRef Name, StringRef AsmConstraints = "") {
521
522 LLVMContext &Ctx = NMD->getParent()->getContext();
523 Type *I32Ty = IntegerType::getInt32Ty(Ctx);
524
526 MDArgs.push_back(MDString::get(Ctx, Name));
527 transform(ChangedTys, std::back_inserter(MDArgs), [=, &Ctx](auto &&CTy) {
528 return MDNode::get(
529 Ctx, {ConstantAsMetadata::get(ConstantInt::get(I32Ty, CTy.first, true)),
531 });
532 if (!AsmConstraints.empty())
533 MDArgs.push_back(MDNode::get(Ctx, MDString::get(Ctx, AsmConstraints)));
534 NMD->addOperand(MDNode::get(Ctx, MDArgs));
535}
536
537// Returns F if aggregate argument/return types are not present or cloned F
538// function with the types replaced by i32 types. The change in types is
539// noted in 'spv.cloned_funcs' metadata for later restoration.
540Function *
541SPIRVPrepareFunctionsImpl::removeAggregateTypesFromSignature(Function *F) {
542 bool IsRetAggr = F->getReturnType()->isAggregateType();
543 // Allow intrinsics with aggregate return type to reach GlobalISel
544 if (F->isIntrinsic() && IsRetAggr)
545 return F;
546
547 IRBuilder<> B(F->getContext());
548
549 bool HasAggrArg = llvm::any_of(F->args(), [](Argument &Arg) {
550 return Arg.getType()->isAggregateType();
551 });
552 bool DoClone = IsRetAggr || HasAggrArg;
553 if (!DoClone)
554 return F;
555 SmallVector<std::pair<int, Type *>, 4> ChangedTypes;
556 Type *RetType = IsRetAggr ? B.getInt32Ty() : F->getReturnType();
557 if (IsRetAggr)
558 ChangedTypes.push_back(std::pair<int, Type *>(-1, F->getReturnType()));
559 SmallVector<Type *, 4> ArgTypes;
560 for (const auto &Arg : F->args()) {
561 if (Arg.getType()->isAggregateType()) {
562 ArgTypes.push_back(B.getInt32Ty());
563 ChangedTypes.push_back(
564 std::pair<int, Type *>(Arg.getArgNo(), Arg.getType()));
565 } else
566 ArgTypes.push_back(Arg.getType());
567 }
568 FunctionType *NewFTy =
569 FunctionType::get(RetType, ArgTypes, F->getFunctionType()->isVarArg());
570 Function *NewF =
571 Function::Create(NewFTy, F->getLinkage(), F->getAddressSpace(),
572 F->getName(), F->getParent());
573
575 auto NewFArgIt = NewF->arg_begin();
576 for (auto &Arg : F->args()) {
577 StringRef ArgName = Arg.getName();
578 NewFArgIt->setName(ArgName);
579 VMap[&Arg] = &(*NewFArgIt++);
580 }
582
583 CloneFunctionInto(NewF, F, VMap, CloneFunctionChangeType::LocalChangesOnly,
584 Returns);
585 NewF->takeName(F);
586
588 NewF->getParent()->getOrInsertNamedMetadata("spv.cloned_funcs"),
589 std::move(ChangedTypes), NewF->getName());
590
591 for (auto *U : make_early_inc_range(F->users())) {
592 if (CallInst *CI;
593 (CI = dyn_cast<CallInst>(U)) && CI->getCalledFunction() == F)
594 CI->mutateFunctionType(NewF->getFunctionType());
595 if (auto *C = dyn_cast<Constant>(U))
596 C->handleOperandChange(F, NewF);
597 else
598 U->replaceUsesOfWith(F, NewF);
599 }
600
601 // register the mutation
602 if (RetType != F->getReturnType())
603 TM.getSubtarget<SPIRVSubtarget>(*F).getSPIRVGlobalRegistry()->addMutated(
604 NewF, F->getReturnType());
605 return NewF;
606}
607
608static std::string fixMultiOutputConstraintString(StringRef Constraints) {
609 // We should only have one =r return for the made up ASM type.
611 SplitString(Constraints, Tmp, ",");
612 std::string SafeConstraints("=r,");
613 for (unsigned I = 0u; I != Tmp.size() - 1; ++I) {
614 if (Tmp[I].starts_with('=') && (Tmp[I][1] == '&' || isalnum(Tmp[I][1])))
615 continue;
616 SafeConstraints.append(Tmp[I]).append({','});
617 }
618 SafeConstraints.append(Tmp.back());
619
620 return SafeConstraints;
621}
622
623// Mutates indirect and inline ASM callsites iff aggregate argument/return types
624// are present with the types replaced by i32 types. The change in types is
625// noted in 'spv.mutated_callsites' metadata for later restoration. For ASM we
626// also have to mutate the constraint string as IRTranslator tries to handle
627// multiple outputs and expects an aggregate return type in their presence.
628bool SPIRVPrepareFunctionsImpl::removeAggregateTypesFromCalls(Function *F) {
629 if (F->isDeclaration() || F->isIntrinsic())
630 return false;
631
633 for (auto &&I : instructions(F)) {
634 if (auto *CB = dyn_cast<CallBase>(&I)) {
635 if (!CB->getCalledOperand() || CB->getCalledFunction())
636 continue;
637 if (CB->getType()->isAggregateType() ||
638 any_of(CB->args(),
639 [](auto &&Arg) { return Arg->getType()->isAggregateType(); }))
640 Calls.emplace_back(CB, nullptr);
641 }
642 }
643
644 if (Calls.empty())
645 return false;
646
647 IRBuilder<> B(F->getContext());
648
649 for (auto &&[CB, NewFnTy] : Calls) {
651 SmallVector<Type *> NewArgTypes;
652
653 Type *RetTy = CB->getType();
654 if (RetTy->isAggregateType()) {
655 ChangedTypes.emplace_back(-1, RetTy);
656 RetTy = B.getInt32Ty();
657 }
658
659 for (auto &&Arg : CB->args()) {
660 if (Arg->getType()->isAggregateType()) {
661 NewArgTypes.push_back(B.getInt32Ty());
662 ChangedTypes.emplace_back(Arg.getOperandNo(), Arg->getType());
663 } else {
664 NewArgTypes.push_back(Arg->getType());
665 }
666 }
667 NewFnTy = FunctionType::get(RetTy, NewArgTypes,
668 CB->getFunctionType()->isVarArg());
669
670 if (!CB->hasName())
671 CB->setName("spv.mutated_callsite." + F->getName());
672 else
673 CB->setName("spv.named_mutated_callsite." + F->getName() + "." +
674 CB->getName());
675
676 std::string Constraints;
677 if (auto *ASM = dyn_cast<InlineAsm>(CB->getCalledOperand())) {
678 Constraints = ASM->getConstraintString();
679
680 CB->setCalledOperand(InlineAsm::get(
681 NewFnTy, ASM->getAsmString(),
682 fixMultiOutputConstraintString(Constraints), ASM->hasSideEffects(),
683 ASM->isAlignStack(), ASM->getDialect(), ASM->canThrow()));
684 }
685
687 F->getParent()->getOrInsertNamedMetadata("spv.mutated_callsites"),
688 std::move(ChangedTypes), CB->getName(), Constraints);
689 }
690
691 for (auto &&[CB, NewFTy] : Calls) {
692 if (NewFTy->getReturnType() != CB->getType())
693 TM.getSubtarget<SPIRVSubtarget>(*F).getSPIRVGlobalRegistry()->addMutated(
694 CB, CB->getType());
695 CB->mutateFunctionType(NewFTy);
696 }
697
698 return true;
699}
700
701bool SPIRVPrepareFunctionsImpl::runOnModule(Module &M) {
702 // Resolve the SPIR-V environment from module content before any
703 // function-level processing. This must happen before legalization so that
704 // isShader()/isKernel() return correct values.
705 const_cast<SPIRVTargetMachine &>(TM)
706 .getMutableSubtargetImpl()
707 ->resolveEnvFromModule(M);
708
709 bool Changed = false;
710 if (M.functions().empty()) {
711 // If there are no functions, insert a service
712 // function so that the global/constant tracking intrinsics
713 // will be created. Without these intrinsics the generated SPIR-V
714 // will be empty. The service function itself is not emitted.
716 BasicBlock *BB = BasicBlock::Create(M.getContext(), "entry", SF);
717 IRBuilder<> IRB(BB);
718 IRB.CreateRetVoid();
719 Changed = true;
720 }
721
722 for (Function &F : M) {
723 Changed |= substituteIntrinsicCalls(&F);
724 Changed |= sortBlocks(F);
725 Changed |= removeAggregateTypesFromCalls(&F);
726 }
727
728 std::vector<Function *> FuncsWorklist;
729 for (auto &F : M)
730 FuncsWorklist.push_back(&F);
731
732 for (auto *F : FuncsWorklist) {
733 Function *NewF = removeAggregateTypesFromSignature(F);
734
735 if (NewF != F) {
736 F->eraseFromParent();
737 Changed = true;
738 }
739 }
740 return Changed;
741}
742
745 return SPIRVPrepareFunctionsImpl(TM).runOnModule(M)
748}
749
752 return new SPIRVPrepareFunctionsLegacy(TM);
753}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
Expand Atomic instructions
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
Machine Check Debug Module
uint64_t IntrinsicInst * II
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
static void lowerFunnelShifts(IntrinsicInst *FSHIntrinsic)
static std::string getAnnotation(Value *AnnoVal, Value *OptAnnoVal)
static void lowerConstrainedFPCmpIntrinsic(ConstrainedFPCmpIntrinsic *ConstrainedCmpIntrinsic, SmallVector< Instruction * > &EraseFromParent)
static void lowerConstrainedFmuladd(IntrinsicInst *II, SmallVector< Instruction * > &EraseFromParent)
static void lowerPtrAnnotation(IntrinsicInst *II)
static bool lowerIntrinsicToFunction(IntrinsicInst *Intrinsic, const TargetTransformInfo &TTI)
static SmallVector< Metadata * > parseAnnotation(Value *I, const std::string &Anno, LLVMContext &Ctx, Type *Int32Ty)
static std::string fixMultiOutputConstraintString(StringRef Constraints)
static void addFunctionTypeMutation(NamedMDNode *NMD, SmallVector< std::pair< int, Type * > > ChangedTys, StringRef Name, StringRef AsmConstraints="")
static bool toSpvLifetimeIntrinsic(IntrinsicInst *II, Intrinsic::ID NewID)
static void lowerExpectAssume(IntrinsicInst *II)
static Function * getOrCreateFunction(Module *M, Type *RetTy, ArrayRef< Type * > ArgTypes, StringRef Name)
This file contains some functions that are useful when dealing with strings.
DEMANGLE_NAMESPACE_BEGIN bool starts_with(std::string_view self, char C) noexcept
This pass exposes codegen information to IR-level passes.
Value * RHS
Value * LHS
BinaryOperator * Mul
This class represents an incoming formal argument to a Function.
Definition Argument.h:32
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
LLVM Basic Block Representation.
Definition BasicBlock.h:62
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition BasicBlock.h:206
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.
Definition InstrTypes.h:676
static ConstantAsMetadata * get(Constant *C)
Definition Metadata.h:537
This is the shared class of boolean and integer constants.
Definition Constants.h:87
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)
Definition Function.h:168
bool empty() const
Definition Function.h:859
FunctionType * getFunctionType() const
Returns the FunctionType for me.
Definition Function.h:211
arg_iterator arg_begin()
Definition Function.h:868
bool isIntrinsic() const
isIntrinsic - Returns true if the function's name starts with "llvm.".
Definition Function.h:251
Type * getReturnType() const
Returns the type of the ret val.
Definition Function.h:216
void setCallingConv(CallingConv::ID CC)
Definition Function.h:276
Argument * getArg(unsigned i) const
Definition Function.h:886
Module * getParent()
Get the module that this global value is contained inside of...
void setDSOLocal(bool Local)
@ ExternalLinkage
Externally visible function.
Definition GlobalValue.h:53
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)
Definition IRBuilder.h:1553
ReturnInst * CreateRet(Value *V)
Create a 'ret <val>' instruction.
Definition IRBuilder.h:1213
LLVM_ABI CallInst * CreateIntrinsic(Intrinsic::ID ID, ArrayRef< Type * > OverloadTypes, ArrayRef< Value * > Args, FMFSource FMFSource={}, const Twine &Name="")
Create a call to intrinsic ID with Args, mangled using OverloadTypes.
Value * CreateSub(Value *LHS, Value *RHS, const Twine &Name="", bool HasNUW=false, bool HasNSW=false)
Definition IRBuilder.h:1460
Value * CreateShl(Value *LHS, Value *RHS, const Twine &Name="", bool HasNUW=false, bool HasNSW=false)
Definition IRBuilder.h:1532
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.
Definition IRBuilder.h:660
ReturnInst * CreateRetVoid()
Create a 'ret void' instruction.
Definition IRBuilder.h:1208
void SetInsertPoint(BasicBlock *TheBB)
This specifies that created instructions should be appended to the end of the specified block.
Definition IRBuilder.h:207
Value * CreateOr(Value *LHS, Value *RHS, const Twine &Name="", bool IsDisjoint=false)
Definition IRBuilder.h:1613
ConstantInt * getInt(const APInt &AI)
Get a constant integer value.
Definition IRBuilder.h:544
Value * CreateURem(Value *LHS, Value *RHS, const Twine &Name="")
Definition IRBuilder.h:1520
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2847
static LLVM_ABI InlineAsm * get(FunctionType *Ty, StringRef AsmString, StringRef Constraints, bool hasSideEffects, bool isAlignStack=false, AsmDialect asmDialect=AD_ATT, bool canThrow=false)
InlineAsm::get - Return the specified uniqued inline asm string.
Definition InlineAsm.cpp:43
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.
Definition LLVMContext.h:68
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1572
static LLVM_ABI MDString * get(LLVMContext &Context, StringRef Str)
Definition Metadata.cpp:614
This class wraps the llvm.memset and llvm.memset.inline intrinsics.
static LLVM_ABI MetadataAsValue * get(LLVMContext &Context, Metadata *MD)
Definition Metadata.cpp:110
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition Pass.h:255
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:67
LLVMContext & getContext() const
Get the global data context.
Definition Module.h:285
NamedMDNode * getOrInsertNamedMetadata(StringRef Name)
Return the named MDNode in the module with the specified name.
Definition Module.cpp:308
A tuple of MDNodes.
Definition Metadata.h:1760
Module * getParent()
Get the module that holds this named metadata collection.
Definition Metadata.h:1830
LLVM_ABI void addOperand(MDNode *M)
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
Definition Analysis.h:115
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM)
bool canUseExtension(SPIRV::Extension::Extension E) const
TargetTransformInfo getTargetTransformInfo(const Function &F) const override
Get a TargetTransformInfo implementation for the target.
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.
Definition StringRef.h:55
std::string str() const
str - Get the contents as an std::string.
Definition StringRef.h:222
const Triple & getTargetTriple() const
const STC & getSubtarget(const Function &F) const
This method returns a pointer to the specified type of TargetSubtargetInfo.
This pass provides access to the codegen interfaces that are needed for IR-level transformations.
VendorType getVendor() const
Get the parsed vendor type of this triple.
Definition Triple.h:441
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
Definition Type.cpp:313
bool isAggregateType() const
Return true if the type is an aggregate type.
Definition Type.h:321
static LLVM_ABI ValueAsMetadata * get(Value *V)
Definition Metadata.cpp:509
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:255
LLVM_ABI void setName(const Twine &Name)
Change the name of the value.
Definition Value.cpp:393
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:549
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
Definition Value.cpp:318
LLVM_ABI void takeName(Value *V)
Transfer the name from V to this value.
Definition Value.cpp:399
Type * getElementType() const
CallInst * Call
Changed
#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.
Definition CallingConv.h:34
@ BasicBlock
Various leaf nodes.
Definition ISDOpcodes.h:81
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 * > OverloadTys={})
Look up the Function declaration of the intrinsic id in the Module M.
LLVM_ABI bool isTargetIntrinsic(ID IID)
isTargetIntrinsic - Returns true if IID is an intrinsic specific to a certain target.
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
FunctionAddr VTableAddr uintptr_t uintptr_t Int32Ty
Definition InstrProf.h:328
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...
Definition STLExtras.h:633
bool sortBlocks(Function &F)
LLVM_ABI void SplitString(StringRef Source, SmallVectorImpl< StringRef > &OutFragments, StringRef Delimiters=" \t\n\v\f\r")
SplitString - Split up the specified string according to the specified delimiters,...
Function * getOrCreateBackendServiceFunction(Module &M)
auto dyn_cast_or_null(const Y &Val)
Definition Casting.h:753
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.
Definition STLExtras.h:2025
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1745
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
@ Ref
The access may reference the value stored in memory.
Definition ModRef.h:32
TargetTransformInfo TTI
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.
Definition STLExtras.h:1909
@ Add
Sum of integers.
constexpr unsigned BitWidth
ValueMap< const Value *, WeakTrackingVH > ValueToValueMapTy
LLVM_ABI void expandMemSetAsLoop(MemSetInst *MemSet, const TargetTransformInfo *TTI=nullptr)
Expand MemSet as a loop.
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<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
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)
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
Definition MIRParser.h:39
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:870