//===- CoroFrame.cpp - Builds and manipulates coroutine frame -------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // This file contains classes used to discover if for a particular value // its definition precedes and its uses follow a suspend block. This is // referred to as a suspend crossing value. // // Using the information discovered we form a Coroutine Frame structure to // contain those values. All uses of those values are replaced with appropriate // GEP + load from the coroutine frame. At the point of the definition we spill // the value into the coroutine frame. //===----------------------------------------------------------------------===// #include "ABI.h" #include "CoroInternal.h" #include "MaterializationUtils.h" #include "SpillUtils.h" #include "SuspendCrossingInfo.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" #include "llvm/Analysis/StackLifetime.h" #include "llvm/IR/DIBuilder.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" #include "llvm/Support/Debug.h" #include "llvm/Support/OptimizedStructLayout.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/PromoteMemToReg.h" #include <algorithm> #include <optional> usingnamespacellvm; extern cl::opt<bool> UseNewDbgInfoFormat; #define DEBUG_TYPE … namespace { class FrameTypeBuilder; // Mapping from the to-be-spilled value to all the users that need reload. struct FrameDataInfo { … }; } // namespace #ifndef NDEBUG static void dumpSpills(StringRef Title, const coro::SpillInfo &Spills) { dbgs() << "------------- " << Title << " --------------\n"; for (const auto &E : Spills) { E.first->dump(); dbgs() << " user: "; for (auto *I : E.second) I->dump(); } } static void dumpAllocas(const SmallVectorImpl<coro::AllocaInfo> &Allocas) { dbgs() << "------------- Allocas --------------\n"; for (const auto &A : Allocas) { A.Alloca->dump(); } } #endif namespace { FieldIDType; // We cannot rely solely on natural alignment of a type when building a // coroutine frame and if the alignment specified on the Alloca instruction // differs from the natural alignment of the alloca type we will need to insert // padding. class FrameTypeBuilder { … }; } // namespace void FrameDataInfo::updateLayoutIndex(FrameTypeBuilder &B) { … } void FrameTypeBuilder::addFieldForAllocas(const Function &F, FrameDataInfo &FrameData, coro::Shape &Shape) { … } void FrameTypeBuilder::finish(StructType *Ty) { … } static void cacheDIVar(FrameDataInfo &FrameData, DenseMap<Value *, DILocalVariable *> &DIVarCache) { … } /// Create name for Type. It uses MDString to store new created string to /// avoid memory leak. static StringRef solveTypeName(Type *Ty) { … } static DIType *solveDIType(DIBuilder &Builder, Type *Ty, const DataLayout &Layout, DIScope *Scope, unsigned LineNum, DenseMap<Type *, DIType *> &DITypeCache) { … } /// Build artificial debug info for C++ coroutine frames to allow users to /// inspect the contents of the frame directly /// /// Create Debug information for coroutine frame with debug name "__coro_frame". /// The debug information for the fields of coroutine frame is constructed from /// the following way: /// 1. For all the value in the Frame, we search the use of dbg.declare to find /// the corresponding debug variables for the value. If we can find the /// debug variable, we can get full and accurate debug information. /// 2. If we can't get debug information in step 1 and 2, we could only try to /// build the DIType by Type. We did this in solveDIType. We only handle /// integer, float, double, integer type and struct type for now. static void buildFrameDebugInfo(Function &F, coro::Shape &Shape, FrameDataInfo &FrameData) { … } // Build a struct that will keep state for an active coroutine. // struct f.frame { // ResumeFnTy ResumeFnAddr; // ResumeFnTy DestroyFnAddr; // ... promise (if present) ... // int ResumeIndex; // ... spills ... // }; static StructType *buildFrameType(Function &F, coro::Shape &Shape, FrameDataInfo &FrameData) { … } // Replace all alloca and SSA values that are accessed across suspend points // with GetElementPointer from coroutine frame + loads and stores. Create an // AllocaSpillBB that will become the new entry block for the resume parts of // the coroutine: // // %hdl = coro.begin(...) // whatever // // becomes: // // %hdl = coro.begin(...) // br label %AllocaSpillBB // // AllocaSpillBB: // ; geps corresponding to allocas that were moved to coroutine frame // br label PostSpill // // PostSpill: // whatever // // static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) { … } // Moves the values in the PHIs in SuccBB that correspong to PredBB into a new // PHI in InsertedBB. static void movePHIValuesToInsertedBlock(BasicBlock *SuccBB, BasicBlock *InsertedBB, BasicBlock *PredBB, PHINode *UntilPHI = nullptr) { … } // Rewrites the PHI Nodes in a cleanuppad. static void rewritePHIsForCleanupPad(BasicBlock *CleanupPadBB, CleanupPadInst *CleanupPad) { … } static void cleanupSinglePredPHIs(Function &F) { … } static void rewritePHIs(BasicBlock &BB) { … } static void rewritePHIs(Function &F) { … } // Splits the block at a particular instruction unless it is the first // instruction in the block with a single predecessor. static BasicBlock *splitBlockIfNotFirst(Instruction *I, const Twine &Name) { … } // Split above and below a particular instruction so that it // will be all alone by itself in a block. static void splitAround(Instruction *I, const Twine &Name) { … } /// After we split the coroutine, will the given basic block be along /// an obvious exit path for the resumption function? static bool willLeaveFunctionImmediatelyAfter(BasicBlock *BB, unsigned depth = 3) { … } static bool localAllocaNeedsStackSave(CoroAllocaAllocInst *AI) { … } /// Turn each of the given local allocas into a normal (dynamic) alloca /// instruction. static void lowerLocalAllocas(ArrayRef<CoroAllocaAllocInst*> LocalAllocas, SmallVectorImpl<Instruction*> &DeadInsts) { … } /// Get the current swifterror value. static Value *emitGetSwiftErrorValue(IRBuilder<> &Builder, Type *ValueTy, coro::Shape &Shape) { … } /// Set the given value as the current swifterror value. /// /// Returns a slot that can be used as a swifterror slot. static Value *emitSetSwiftErrorValue(IRBuilder<> &Builder, Value *V, coro::Shape &Shape) { … } /// Set the swifterror value from the given alloca before a call, /// then put in back in the alloca afterwards. /// /// Returns an address that will stand in for the swifterror slot /// until splitting. static Value *emitSetAndGetSwiftErrorValueAround(Instruction *Call, AllocaInst *Alloca, coro::Shape &Shape) { … } /// Eliminate a formerly-swifterror alloca by inserting the get/set /// intrinsics and attempting to MemToReg the alloca away. static void eliminateSwiftErrorAlloca(Function &F, AllocaInst *Alloca, coro::Shape &Shape) { … } /// "Eliminate" a swifterror argument by reducing it to the alloca case /// and then loading and storing in the prologue and epilog. /// /// The argument keeps the swifterror flag. static void eliminateSwiftErrorArgument(Function &F, Argument &Arg, coro::Shape &Shape, SmallVectorImpl<AllocaInst*> &AllocasToPromote) { … } /// Eliminate all problematic uses of swifterror arguments and allocas /// from the function. We'll fix them up later when splitting the function. static void eliminateSwiftError(Function &F, coro::Shape &Shape) { … } /// For each local variable that all of its user are only used inside one of /// suspended region, we sink their lifetime.start markers to the place where /// after the suspend block. Doing so minimizes the lifetime of each variable, /// hence minimizing the amount of data we end up putting on the frame. static void sinkLifetimeStartMarkers(Function &F, coro::Shape &Shape, SuspendCrossingInfo &Checker, const DominatorTree &DT) { … } static std::optional<std::pair<Value &, DIExpression &>> salvageDebugInfoImpl(SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap, bool UseEntryValue, Function *F, Value *Storage, DIExpression *Expr, bool SkipOutermostLoad) { … } void coro::salvageDebugInfo( SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap, DbgVariableIntrinsic &DVI, bool UseEntryValue) { … } void coro::salvageDebugInfo( SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap, DbgVariableRecord &DVR, bool UseEntryValue) { … } void coro::normalizeCoroutine(Function &F, coro::Shape &Shape, TargetTransformInfo &TTI) { … } void coro::BaseABI::buildCoroutineFrame() { … }