572 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This pass modifies function signatures containing aggregate arguments10// and/or return value before IRTranslator. Information about the original11// signatures is stored in metadata. It is used during call lowering to12// restore correct SPIR-V types of function arguments and return values.13// This pass also substitutes some llvm intrinsic calls with calls to newly14// 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 modify17// GVs/functions.18//19//===----------------------------------------------------------------------===//20 21#include "SPIRV.h"22#include "SPIRVSubtarget.h"23#include "SPIRVTargetMachine.h"24#include "SPIRVUtils.h"25#include "llvm/ADT/StringExtras.h"26#include "llvm/Analysis/ValueTracking.h"27#include "llvm/CodeGen/IntrinsicLowering.h"28#include "llvm/IR/IRBuilder.h"29#include "llvm/IR/IntrinsicInst.h"30#include "llvm/IR/Intrinsics.h"31#include "llvm/IR/IntrinsicsSPIRV.h"32#include "llvm/Transforms/Utils/Cloning.h"33#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"34#include <regex>35 36using namespace llvm;37 38namespace {39 40class SPIRVPrepareFunctions : public ModulePass {41 const SPIRVTargetMachine &TM;42 bool substituteIntrinsicCalls(Function *F);43 Function *removeAggregateTypesFromSignature(Function *F);44 45public:46 static char ID;47 SPIRVPrepareFunctions(const SPIRVTargetMachine &TM)48 : ModulePass(ID), TM(TM) {}49 50 bool runOnModule(Module &M) override;51 52 StringRef getPassName() const override { return "SPIRV prepare functions"; }53 54 void getAnalysisUsage(AnalysisUsage &AU) const override {55 ModulePass::getAnalysisUsage(AU);56 }57};58 59static cl::list<std::string> SPVAllowUnknownIntrinsics(60 "spv-allow-unknown-intrinsics", cl::CommaSeparated,61 cl::desc("Emit unknown intrinsics as calls to external functions. A "62 "comma-separated input list of intrinsic prefixes must be "63 "provided, and only intrinsics carrying a listed prefix get "64 "emitted as described."),65 cl::value_desc("intrinsic_prefix_0,intrinsic_prefix_1"), cl::ValueOptional);66} // namespace67 68char SPIRVPrepareFunctions::ID = 0;69 70INITIALIZE_PASS(SPIRVPrepareFunctions, "prepare-functions",71 "SPIRV prepare functions", false, false)72 73static std::string lowerLLVMIntrinsicName(IntrinsicInst *II) {74 Function *IntrinsicFunc = II->getCalledFunction();75 assert(IntrinsicFunc && "Missing function");76 std::string FuncName = IntrinsicFunc->getName().str();77 llvm::replace(FuncName, '.', '_');78 FuncName = "spirv." + FuncName;79 return FuncName;80}81 82static Function *getOrCreateFunction(Module *M, Type *RetTy,83 ArrayRef<Type *> ArgTypes,84 StringRef Name) {85 FunctionType *FT = FunctionType::get(RetTy, ArgTypes, false);86 Function *F = M->getFunction(Name);87 if (F && F->getFunctionType() == FT)88 return F;89 Function *NewF = Function::Create(FT, GlobalValue::ExternalLinkage, Name, M);90 if (F)91 NewF->setDSOLocal(F->isDSOLocal());92 NewF->setCallingConv(CallingConv::SPIR_FUNC);93 return NewF;94}95 96static bool lowerIntrinsicToFunction(IntrinsicInst *Intrinsic) {97 // For @llvm.memset.* intrinsic cases with constant value and length arguments98 // are emulated via "storing" a constant array to the destination. For other99 // cases we wrap the intrinsic in @spirv.llvm_memset_* function and expand the100 // intrinsic to a loop via expandMemSetAsLoop().101 if (auto *MSI = dyn_cast<MemSetInst>(Intrinsic))102 if (isa<Constant>(MSI->getValue()) && isa<ConstantInt>(MSI->getLength()))103 return false; // It is handled later using OpCopyMemorySized.104 105 Module *M = Intrinsic->getModule();106 std::string FuncName = lowerLLVMIntrinsicName(Intrinsic);107 if (Intrinsic->isVolatile())108 FuncName += ".volatile";109 // Redirect @llvm.intrinsic.* call to @spirv.llvm_intrinsic_*110 Function *F = M->getFunction(FuncName);111 if (F) {112 Intrinsic->setCalledFunction(F);113 return true;114 }115 // TODO copy arguments attributes: nocapture writeonly.116 FunctionCallee FC =117 M->getOrInsertFunction(FuncName, Intrinsic->getFunctionType());118 auto IntrinsicID = Intrinsic->getIntrinsicID();119 Intrinsic->setCalledFunction(FC);120 121 F = dyn_cast<Function>(FC.getCallee());122 assert(F && "Callee must be a function");123 124 switch (IntrinsicID) {125 case Intrinsic::memset: {126 auto *MSI = static_cast<MemSetInst *>(Intrinsic);127 Argument *Dest = F->getArg(0);128 Argument *Val = F->getArg(1);129 Argument *Len = F->getArg(2);130 Argument *IsVolatile = F->getArg(3);131 Dest->setName("dest");132 Val->setName("val");133 Len->setName("len");134 IsVolatile->setName("isvolatile");135 BasicBlock *EntryBB = BasicBlock::Create(M->getContext(), "entry", F);136 IRBuilder<> IRB(EntryBB);137 auto *MemSet = IRB.CreateMemSet(Dest, Val, Len, MSI->getDestAlign(),138 MSI->isVolatile());139 IRB.CreateRetVoid();140 expandMemSetAsLoop(cast<MemSetInst>(MemSet));141 MemSet->eraseFromParent();142 break;143 }144 case Intrinsic::bswap: {145 BasicBlock *EntryBB = BasicBlock::Create(M->getContext(), "entry", F);146 IRBuilder<> IRB(EntryBB);147 auto *BSwap = IRB.CreateIntrinsic(Intrinsic::bswap, Intrinsic->getType(),148 F->getArg(0));149 IRB.CreateRet(BSwap);150 IntrinsicLowering IL(M->getDataLayout());151 IL.LowerIntrinsicCall(BSwap);152 break;153 }154 default:155 break;156 }157 return true;158}159 160static std::string getAnnotation(Value *AnnoVal, Value *OptAnnoVal) {161 if (auto *Ref = dyn_cast_or_null<GetElementPtrInst>(AnnoVal))162 AnnoVal = Ref->getOperand(0);163 if (auto *Ref = dyn_cast_or_null<BitCastInst>(OptAnnoVal))164 OptAnnoVal = Ref->getOperand(0);165 166 std::string Anno;167 if (auto *C = dyn_cast_or_null<Constant>(AnnoVal)) {168 StringRef Str;169 if (getConstantStringInfo(C, Str))170 Anno = Str;171 }172 // handle optional annotation parameter in a way that Khronos Translator do173 // (collect integers wrapped in a struct)174 if (auto *C = dyn_cast_or_null<Constant>(OptAnnoVal);175 C && C->getNumOperands()) {176 Value *MaybeStruct = C->getOperand(0);177 if (auto *Struct = dyn_cast<ConstantStruct>(MaybeStruct)) {178 for (unsigned I = 0, E = Struct->getNumOperands(); I != E; ++I) {179 if (auto *CInt = dyn_cast<ConstantInt>(Struct->getOperand(I)))180 Anno += (I == 0 ? ": " : ", ") +181 std::to_string(CInt->getType()->getIntegerBitWidth() == 1182 ? CInt->getZExtValue()183 : CInt->getSExtValue());184 }185 } else if (auto *Struct = dyn_cast<ConstantAggregateZero>(MaybeStruct)) {186 // { i32 i32 ... } zeroinitializer187 for (unsigned I = 0, E = Struct->getType()->getStructNumElements();188 I != E; ++I)189 Anno += I == 0 ? ": 0" : ", 0";190 }191 }192 return Anno;193}194 195static SmallVector<Metadata *> parseAnnotation(Value *I,196 const std::string &Anno,197 LLVMContext &Ctx,198 Type *Int32Ty) {199 // Try to parse the annotation string according to the following rules:200 // annotation := ({kind} | {kind:value,value,...})+201 // kind := number202 // value := number | string203 static const std::regex R(204 "\\{(\\d+)(?:[:,](\\d+|\"[^\"]*\")(?:,(\\d+|\"[^\"]*\"))*)?\\}");205 SmallVector<Metadata *> MDs;206 int Pos = 0;207 for (std::sregex_iterator208 It = std::sregex_iterator(Anno.begin(), Anno.end(), R),209 ItEnd = std::sregex_iterator();210 It != ItEnd; ++It) {211 if (It->position() != Pos)212 return SmallVector<Metadata *>{};213 Pos = It->position() + It->length();214 std::smatch Match = *It;215 SmallVector<Metadata *> MDsItem;216 for (std::size_t i = 1; i < Match.size(); ++i) {217 std::ssub_match SMatch = Match[i];218 std::string Item = SMatch.str();219 if (Item.length() == 0)220 break;221 if (Item[0] == '"') {222 Item = Item.substr(1, Item.length() - 2);223 // Acceptable format of the string snippet is:224 static const std::regex RStr("^(\\d+)(?:,(\\d+))*$");225 if (std::smatch MatchStr; std::regex_match(Item, MatchStr, RStr)) {226 for (std::size_t SubIdx = 1; SubIdx < MatchStr.size(); ++SubIdx)227 if (std::string SubStr = MatchStr[SubIdx].str(); SubStr.length())228 MDsItem.push_back(ConstantAsMetadata::get(229 ConstantInt::get(Int32Ty, std::stoi(SubStr))));230 } else {231 MDsItem.push_back(MDString::get(Ctx, Item));232 }233 } else if (int32_t Num; llvm::to_integer(StringRef(Item), Num, 10)) {234 MDsItem.push_back(235 ConstantAsMetadata::get(ConstantInt::get(Int32Ty, Num)));236 } else {237 MDsItem.push_back(MDString::get(Ctx, Item));238 }239 }240 if (MDsItem.size() == 0)241 return SmallVector<Metadata *>{};242 MDs.push_back(MDNode::get(Ctx, MDsItem));243 }244 return Pos == static_cast<int>(Anno.length()) ? std::move(MDs)245 : SmallVector<Metadata *>{};246}247 248static void lowerPtrAnnotation(IntrinsicInst *II) {249 LLVMContext &Ctx = II->getContext();250 Type *Int32Ty = Type::getInt32Ty(Ctx);251 252 // Retrieve an annotation string from arguments.253 Value *PtrArg = nullptr;254 if (auto *BI = dyn_cast<BitCastInst>(II->getArgOperand(0)))255 PtrArg = BI->getOperand(0);256 else257 PtrArg = II->getOperand(0);258 std::string Anno =259 getAnnotation(II->getArgOperand(1),260 4 < II->arg_size() ? II->getArgOperand(4) : nullptr);261 262 // Parse the annotation.263 SmallVector<Metadata *> MDs = parseAnnotation(II, Anno, Ctx, Int32Ty);264 265 // If the annotation string is not parsed successfully we don't know the266 // format used and output it as a general UserSemantic decoration.267 // Otherwise MDs is a Metadata tuple (a decoration list) in the format268 // expected by `spirv.Decorations`.269 if (MDs.size() == 0) {270 auto UserSemantic = ConstantAsMetadata::get(ConstantInt::get(271 Int32Ty, static_cast<uint32_t>(SPIRV::Decoration::UserSemantic)));272 MDs.push_back(MDNode::get(Ctx, {UserSemantic, MDString::get(Ctx, Anno)}));273 }274 275 // Build the internal intrinsic function.276 IRBuilder<> IRB(II->getParent());277 IRB.SetInsertPoint(II);278 IRB.CreateIntrinsic(279 Intrinsic::spv_assign_decoration, {PtrArg->getType()},280 {PtrArg, MetadataAsValue::get(Ctx, MDNode::get(Ctx, MDs))});281 II->replaceAllUsesWith(II->getOperand(0));282}283 284static void lowerFunnelShifts(IntrinsicInst *FSHIntrinsic) {285 // Get a separate function - otherwise, we'd have to rework the CFG of the286 // current one. Then simply replace the intrinsic uses with a call to the new287 // function.288 // Generate LLVM IR for i* @spirv.llvm_fsh?_i* (i* %a, i* %b, i* %c)289 Module *M = FSHIntrinsic->getModule();290 FunctionType *FSHFuncTy = FSHIntrinsic->getFunctionType();291 Type *FSHRetTy = FSHFuncTy->getReturnType();292 const std::string FuncName = lowerLLVMIntrinsicName(FSHIntrinsic);293 Function *FSHFunc =294 getOrCreateFunction(M, FSHRetTy, FSHFuncTy->params(), FuncName);295 296 if (!FSHFunc->empty()) {297 FSHIntrinsic->setCalledFunction(FSHFunc);298 return;299 }300 BasicBlock *RotateBB = BasicBlock::Create(M->getContext(), "rotate", FSHFunc);301 IRBuilder<> IRB(RotateBB);302 Type *Ty = FSHFunc->getReturnType();303 // Build the actual funnel shift rotate logic.304 // In the comments, "int" is used interchangeably with "vector of int305 // elements".306 FixedVectorType *VectorTy = dyn_cast<FixedVectorType>(Ty);307 Type *IntTy = VectorTy ? VectorTy->getElementType() : Ty;308 unsigned BitWidth = IntTy->getIntegerBitWidth();309 ConstantInt *BitWidthConstant = IRB.getInt({BitWidth, BitWidth});310 Value *BitWidthForInsts =311 VectorTy312 ? IRB.CreateVectorSplat(VectorTy->getNumElements(), BitWidthConstant)313 : BitWidthConstant;314 Value *RotateModVal =315 IRB.CreateURem(/*Rotate*/ FSHFunc->getArg(2), BitWidthForInsts);316 Value *FirstShift = nullptr, *SecShift = nullptr;317 if (FSHIntrinsic->getIntrinsicID() == Intrinsic::fshr) {318 // Shift the less significant number right, the "rotate" number of bits319 // will be 0-filled on the left as a result of this regular shift.320 FirstShift = IRB.CreateLShr(FSHFunc->getArg(1), RotateModVal);321 } else {322 // Shift the more significant number left, the "rotate" number of bits323 // will be 0-filled on the right as a result of this regular shift.324 FirstShift = IRB.CreateShl(FSHFunc->getArg(0), RotateModVal);325 }326 // We want the "rotate" number of the more significant int's LSBs (MSBs) to327 // occupy the leftmost (rightmost) "0 space" left by the previous operation.328 // Therefore, subtract the "rotate" number from the integer bitsize...329 Value *SubRotateVal = IRB.CreateSub(BitWidthForInsts, RotateModVal);330 if (FSHIntrinsic->getIntrinsicID() == Intrinsic::fshr) {331 // ...and left-shift the more significant int by this number, zero-filling332 // the LSBs.333 SecShift = IRB.CreateShl(FSHFunc->getArg(0), SubRotateVal);334 } else {335 // ...and right-shift the less significant int by this number, zero-filling336 // the MSBs.337 SecShift = IRB.CreateLShr(FSHFunc->getArg(1), SubRotateVal);338 }339 // A simple binary addition of the shifted ints yields the final result.340 IRB.CreateRet(IRB.CreateOr(FirstShift, SecShift));341 342 FSHIntrinsic->setCalledFunction(FSHFunc);343}344 345static void lowerConstrainedFPCmpIntrinsic(346 ConstrainedFPCmpIntrinsic *ConstrainedCmpIntrinsic,347 SmallVector<Instruction *> &EraseFromParent) {348 if (!ConstrainedCmpIntrinsic)349 return;350 // Extract the floating-point values being compared351 Value *LHS = ConstrainedCmpIntrinsic->getArgOperand(0);352 Value *RHS = ConstrainedCmpIntrinsic->getArgOperand(1);353 FCmpInst::Predicate Pred = ConstrainedCmpIntrinsic->getPredicate();354 IRBuilder<> Builder(ConstrainedCmpIntrinsic);355 Value *FCmp = Builder.CreateFCmp(Pred, LHS, RHS);356 ConstrainedCmpIntrinsic->replaceAllUsesWith(FCmp);357 EraseFromParent.push_back(dyn_cast<Instruction>(ConstrainedCmpIntrinsic));358}359 360static void lowerExpectAssume(IntrinsicInst *II) {361 // If we cannot use the SPV_KHR_expect_assume extension, then we need to362 // ignore the intrinsic and move on. It should be removed later on by LLVM.363 // Otherwise we should lower the intrinsic to the corresponding SPIR-V364 // instruction.365 // For @llvm.assume we have OpAssumeTrueKHR.366 // For @llvm.expect we have OpExpectKHR.367 //368 // We need to lower this into a builtin and then the builtin into a SPIR-V369 // instruction.370 if (II->getIntrinsicID() == Intrinsic::assume) {371 Function *F = Intrinsic::getOrInsertDeclaration(372 II->getModule(), Intrinsic::SPVIntrinsics::spv_assume);373 II->setCalledFunction(F);374 } else if (II->getIntrinsicID() == Intrinsic::expect) {375 Function *F = Intrinsic::getOrInsertDeclaration(376 II->getModule(), Intrinsic::SPVIntrinsics::spv_expect,377 {II->getOperand(0)->getType()});378 II->setCalledFunction(F);379 } else {380 llvm_unreachable("Unknown intrinsic");381 }382}383 384static bool toSpvLifetimeIntrinsic(IntrinsicInst *II, Intrinsic::ID NewID) {385 IRBuilder<> Builder(II);386 auto *Alloca = cast<AllocaInst>(II->getArgOperand(0));387 std::optional<TypeSize> Size =388 Alloca->getAllocationSize(Alloca->getDataLayout());389 Value *SizeVal = Builder.getInt64(Size ? *Size : -1);390 Builder.CreateIntrinsic(NewID, Alloca->getType(),391 {SizeVal, II->getArgOperand(0)});392 II->eraseFromParent();393 return true;394}395 396// Substitutes calls to LLVM intrinsics with either calls to SPIR-V intrinsics397// or calls to proper generated functions. Returns True if F was modified.398bool SPIRVPrepareFunctions::substituteIntrinsicCalls(Function *F) {399 bool Changed = false;400 const SPIRVSubtarget &STI = TM.getSubtarget<SPIRVSubtarget>(*F);401 SmallVector<Instruction *> EraseFromParent;402 for (BasicBlock &BB : *F) {403 for (Instruction &I : make_early_inc_range(BB)) {404 auto Call = dyn_cast<CallInst>(&I);405 if (!Call)406 continue;407 Function *CF = Call->getCalledFunction();408 if (!CF || !CF->isIntrinsic())409 continue;410 auto *II = cast<IntrinsicInst>(Call);411 switch (II->getIntrinsicID()) {412 case Intrinsic::memset:413 case Intrinsic::bswap:414 Changed |= lowerIntrinsicToFunction(II);415 break;416 case Intrinsic::fshl:417 case Intrinsic::fshr:418 lowerFunnelShifts(II);419 Changed = true;420 break;421 case Intrinsic::assume:422 case Intrinsic::expect:423 if (STI.canUseExtension(SPIRV::Extension::SPV_KHR_expect_assume))424 lowerExpectAssume(II);425 Changed = true;426 break;427 case Intrinsic::lifetime_start:428 if (!STI.isShader()) {429 Changed |= toSpvLifetimeIntrinsic(430 II, Intrinsic::SPVIntrinsics::spv_lifetime_start);431 } else {432 II->eraseFromParent();433 Changed = true;434 }435 break;436 case Intrinsic::lifetime_end:437 if (!STI.isShader()) {438 Changed |= toSpvLifetimeIntrinsic(439 II, Intrinsic::SPVIntrinsics::spv_lifetime_end);440 } else {441 II->eraseFromParent();442 Changed = true;443 }444 break;445 case Intrinsic::ptr_annotation:446 lowerPtrAnnotation(II);447 Changed = true;448 break;449 case Intrinsic::experimental_constrained_fcmp:450 case Intrinsic::experimental_constrained_fcmps:451 lowerConstrainedFPCmpIntrinsic(dyn_cast<ConstrainedFPCmpIntrinsic>(II),452 EraseFromParent);453 Changed = true;454 break;455 default:456 if (TM.getTargetTriple().getVendor() == Triple::AMD ||457 any_of(SPVAllowUnknownIntrinsics, [II](auto &&Prefix) {458 if (Prefix.empty())459 return false;460 return II->getCalledFunction()->getName().starts_with(Prefix);461 }))462 Changed |= lowerIntrinsicToFunction(II);463 break;464 }465 }466 }467 for (auto *I : EraseFromParent)468 I->eraseFromParent();469 return Changed;470}471 472// Returns F if aggregate argument/return types are not present or cloned F473// function with the types replaced by i32 types. The change in types is474// noted in 'spv.cloned_funcs' metadata for later restoration.475Function *476SPIRVPrepareFunctions::removeAggregateTypesFromSignature(Function *F) {477 bool IsRetAggr = F->getReturnType()->isAggregateType();478 // Allow intrinsics with aggregate return type to reach GlobalISel479 if (F->isIntrinsic() && IsRetAggr)480 return F;481 482 IRBuilder<> B(F->getContext());483 484 bool HasAggrArg = llvm::any_of(F->args(), [](Argument &Arg) {485 return Arg.getType()->isAggregateType();486 });487 bool DoClone = IsRetAggr || HasAggrArg;488 if (!DoClone)489 return F;490 SmallVector<std::pair<int, Type *>, 4> ChangedTypes;491 Type *RetType = IsRetAggr ? B.getInt32Ty() : F->getReturnType();492 if (IsRetAggr)493 ChangedTypes.push_back(std::pair<int, Type *>(-1, F->getReturnType()));494 SmallVector<Type *, 4> ArgTypes;495 for (const auto &Arg : F->args()) {496 if (Arg.getType()->isAggregateType()) {497 ArgTypes.push_back(B.getInt32Ty());498 ChangedTypes.push_back(499 std::pair<int, Type *>(Arg.getArgNo(), Arg.getType()));500 } else501 ArgTypes.push_back(Arg.getType());502 }503 FunctionType *NewFTy =504 FunctionType::get(RetType, ArgTypes, F->getFunctionType()->isVarArg());505 Function *NewF =506 Function::Create(NewFTy, F->getLinkage(), F->getName(), *F->getParent());507 508 ValueToValueMapTy VMap;509 auto NewFArgIt = NewF->arg_begin();510 for (auto &Arg : F->args()) {511 StringRef ArgName = Arg.getName();512 NewFArgIt->setName(ArgName);513 VMap[&Arg] = &(*NewFArgIt++);514 }515 SmallVector<ReturnInst *, 8> Returns;516 517 CloneFunctionInto(NewF, F, VMap, CloneFunctionChangeType::LocalChangesOnly,518 Returns);519 NewF->takeName(F);520 521 NamedMDNode *FuncMD =522 F->getParent()->getOrInsertNamedMetadata("spv.cloned_funcs");523 SmallVector<Metadata *, 2> MDArgs;524 MDArgs.push_back(MDString::get(B.getContext(), NewF->getName()));525 for (auto &ChangedTyP : ChangedTypes)526 MDArgs.push_back(MDNode::get(527 B.getContext(),528 {ConstantAsMetadata::get(B.getInt32(ChangedTyP.first)),529 ValueAsMetadata::get(Constant::getNullValue(ChangedTyP.second))}));530 MDNode *ThisFuncMD = MDNode::get(B.getContext(), MDArgs);531 FuncMD->addOperand(ThisFuncMD);532 533 for (auto *U : make_early_inc_range(F->users())) {534 if (auto *CI = dyn_cast<CallInst>(U))535 CI->mutateFunctionType(NewF->getFunctionType());536 U->replaceUsesOfWith(F, NewF);537 }538 539 // register the mutation540 if (RetType != F->getReturnType())541 TM.getSubtarget<SPIRVSubtarget>(*F).getSPIRVGlobalRegistry()->addMutated(542 NewF, F->getReturnType());543 return NewF;544}545 546bool SPIRVPrepareFunctions::runOnModule(Module &M) {547 bool Changed = false;548 for (Function &F : M) {549 Changed |= substituteIntrinsicCalls(&F);550 Changed |= sortBlocks(F);551 }552 553 std::vector<Function *> FuncsWorklist;554 for (auto &F : M)555 FuncsWorklist.push_back(&F);556 557 for (auto *F : FuncsWorklist) {558 Function *NewF = removeAggregateTypesFromSignature(F);559 560 if (NewF != F) {561 F->eraseFromParent();562 Changed = true;563 }564 }565 return Changed;566}567 568ModulePass *569llvm::createSPIRVPrepareFunctionsPass(const SPIRVTargetMachine &TM) {570 return new SPIRVPrepareFunctions(TM);571}572