//===- SpillUtils.cpp - Utilities for checking for spills ---------------===// // // 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 // //===----------------------------------------------------------------------===// #include "llvm/Transforms/Coroutines/SpillUtils.h" #include "CoroInternal.h" #include "llvm/Analysis/CFG.h" #include "llvm/Analysis/PtrUseVisitor.h" #include "llvm/IR/CFG.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/InstIterator.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" namespace llvm { namespace coro { namespace { VisitedBlocksSet; // Check for structural coroutine intrinsics that should not be spilled into // the coroutine frame. static bool isCoroutineStructureIntrinsic(Instruction &I) { … } /// Does control flow starting at the given block ever reach a suspend /// instruction before reaching a block in VisitedOrFreeBBs? static bool isSuspendReachableFrom(BasicBlock *From, VisitedBlocksSet &VisitedOrFreeBBs) { … } /// Is the given alloca "local", i.e. bounded in lifetime to not cross a /// suspend point? static bool isLocalAlloca(CoroAllocaAllocInst *AI) { … } /// Turn the given coro.alloca.alloc call into a dynamic allocation. /// This happens during the all-instructions iteration, so it must not /// delete the call. static Instruction * lowerNonLocalAlloca(CoroAllocaAllocInst *AI, const coro::Shape &Shape, SmallVectorImpl<Instruction *> &DeadInsts) { … } // We need to make room to insert a spill after initial PHIs, but before // catchswitch instruction. Placing it before violates the requirement that // catchswitch, like all other EHPads must be the first nonPHI in a block. // // Split away catchswitch into a separate block and insert in its place: // // cleanuppad <InsertPt> cleanupret. // // cleanupret instruction will act as an insert point for the spill. static Instruction *splitBeforeCatchSwitch(CatchSwitchInst *CatchSwitch) { … } // We use a pointer use visitor to track how an alloca is being used. // The goal is to be able to answer the following three questions: // 1. Should this alloca be allocated on the frame instead. // 2. Could the content of the alloca be modified prior to CoroBegn, which would // require copying the data from alloca to the frame after CoroBegin. // 3. Is there any alias created for this alloca prior to CoroBegin, but used // after CoroBegin. In that case, we will need to recreate the alias after // CoroBegin based off the frame. To answer question 1, we track two things: // a. List of all BasicBlocks that use this alloca or any of the aliases of // the alloca. In the end, we check if there exists any two basic blocks that // cross suspension points. If so, this alloca must be put on the frame. b. // Whether the alloca or any alias of the alloca is escaped at some point, // either by storing the address somewhere, or the address is used in a // function call that might capture. If it's ever escaped, this alloca must be // put on the frame conservatively. // To answer quetion 2, we track through the variable MayWriteBeforeCoroBegin. // Whenever a potential write happens, either through a store instruction, a // function call or any of the memory intrinsics, we check whether this // instruction is prior to CoroBegin. To answer question 3, we track the offsets // of all aliases created for the alloca prior to CoroBegin but used after // CoroBegin. std::optional is used to be able to represent the case when the // offset is unknown (e.g. when you have a PHINode that takes in different // offset values). We cannot handle unknown offsets and will assert. This is the // potential issue left out. An ideal solution would likely require a // significant redesign. namespace { struct AllocaUseVisitor : PtrUseVisitor<AllocaUseVisitor> { … }; } // namespace static void collectFrameAlloca(AllocaInst *AI, const coro::Shape &Shape, const SuspendCrossingInfo &Checker, SmallVectorImpl<AllocaInfo> &Allocas, const DominatorTree &DT) { … } } // namespace void collectSpillsFromArgs(SpillInfo &Spills, Function &F, const SuspendCrossingInfo &Checker) { … } void collectSpillsAndAllocasFromInsts( SpillInfo &Spills, SmallVector<AllocaInfo, 8> &Allocas, SmallVector<Instruction *, 4> &DeadInstructions, SmallVector<CoroAllocaAllocInst *, 4> &LocalAllocas, Function &F, const SuspendCrossingInfo &Checker, const DominatorTree &DT, const coro::Shape &Shape) { … } void collectSpillsFromDbgInfo(SpillInfo &Spills, Function &F, const SuspendCrossingInfo &Checker) { … } /// Async and Retcon{Once} conventions assume that all spill uses can be sunk /// after the coro.begin intrinsic. void sinkSpillUsesAfterCoroBegin(const DominatorTree &Dom, CoroBeginInst *CoroBegin, coro::SpillInfo &Spills, SmallVectorImpl<coro::AllocaInfo> &Allocas) { … } BasicBlock::iterator getSpillInsertionPt(const coro::Shape &Shape, Value *Def, const DominatorTree &DT) { … } } // End namespace coro. } // End namespace llvm.