From fd2bd5c34b2ee6e8e8560342f6a6bda63f525924 Mon Sep 17 00:00:00 2001 From: tnowicki Date: Sat, 24 Aug 2024 02:13:21 -0400 Subject: [PATCH] [Coroutines] Move Shape to its own header * Plugin libraries to create custom ABIs will need access to CoroShape. * As a step in enabling plugin libraries, move Shape into its own header that will eventually be moved into include/llvm/Transforms/Coroutines --- llvm/lib/Transforms/Coroutines/CoroEarly.cpp | 1 + llvm/lib/Transforms/Coroutines/CoroInternal.h | 224 +--------------- llvm/lib/Transforms/Coroutines/CoroShape.h | 249 ++++++++++++++++++ llvm/lib/Transforms/Coroutines/Coroutines.cpp | 1 + 4 files changed, 252 insertions(+), 223 deletions(-) create mode 100644 llvm/lib/Transforms/Coroutines/CoroShape.h diff --git a/llvm/lib/Transforms/Coroutines/CoroEarly.cpp b/llvm/lib/Transforms/Coroutines/CoroEarly.cpp index 13b6680264c87c..5f8efd1a8f32ea 100644 --- a/llvm/lib/Transforms/Coroutines/CoroEarly.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroEarly.cpp @@ -8,6 +8,7 @@ #include "llvm/Transforms/Coroutines/CoroEarly.h" #include "CoroInternal.h" +#include "CoroShape.h" #include "llvm/IR/DIBuilder.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" diff --git a/llvm/lib/Transforms/Coroutines/CoroInternal.h b/llvm/lib/Transforms/Coroutines/CoroInternal.h index 891798f53b2d00..fcbd31878bdea7 100644 --- a/llvm/lib/Transforms/Coroutines/CoroInternal.h +++ b/llvm/lib/Transforms/Coroutines/CoroInternal.h @@ -12,6 +12,7 @@ #define LLVM_LIB_TRANSFORMS_COROUTINES_COROINTERNAL_H #include "CoroInstr.h" +#include "CoroShape.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/IR/IRBuilder.h" @@ -58,229 +59,6 @@ struct LowererBase { CallInst *makeSubFnCall(Value *Arg, int Index, Instruction *InsertPt); }; -enum class ABI { - /// The "resume-switch" lowering, where there are separate resume and - /// destroy functions that are shared between all suspend points. The - /// coroutine frame implicitly stores the resume and destroy functions, - /// the current index, and any promise value. - Switch, - - /// The "returned-continuation" lowering, where each suspend point creates a - /// single continuation function that is used for both resuming and - /// destroying. Does not support promises. - Retcon, - - /// The "unique returned-continuation" lowering, where each suspend point - /// creates a single continuation function that is used for both resuming - /// and destroying. Does not support promises. The function is known to - /// suspend at most once during its execution, and the return value of - /// the continuation is void. - RetconOnce, - - /// The "async continuation" lowering, where each suspend point creates a - /// single continuation function. The continuation function is available as an - /// intrinsic. - Async, -}; - -// Holds structural Coroutine Intrinsics for a particular function and other -// values used during CoroSplit pass. -struct LLVM_LIBRARY_VISIBILITY Shape { - CoroBeginInst *CoroBegin; - SmallVector CoroEnds; - SmallVector CoroSizes; - SmallVector CoroAligns; - SmallVector CoroSuspends; - SmallVector SwiftErrorOps; - SmallVector CoroAwaitSuspends; - SmallVector SymmetricTransfers; - - // Field indexes for special fields in the switch lowering. - struct SwitchFieldIndex { - enum { - Resume, - Destroy - - // The promise field is always at a fixed offset from the start of - // frame given its type, but the index isn't a constant for all - // possible frames. - - // The switch-index field isn't at a fixed offset or index, either; - // we just work it in where it fits best. - }; - }; - - coro::ABI ABI; - - StructType *FrameTy; - Align FrameAlign; - uint64_t FrameSize; - Value *FramePtr; - BasicBlock *AllocaSpillBlock; - - /// This would only be true if optimization are enabled. - bool OptimizeFrame; - - struct SwitchLoweringStorage { - SwitchInst *ResumeSwitch; - AllocaInst *PromiseAlloca; - BasicBlock *ResumeEntryBlock; - unsigned IndexField; - unsigned IndexAlign; - unsigned IndexOffset; - bool HasFinalSuspend; - bool HasUnwindCoroEnd; - }; - - struct RetconLoweringStorage { - Function *ResumePrototype; - Function *Alloc; - Function *Dealloc; - BasicBlock *ReturnBlock; - bool IsFrameInlineInStorage; - }; - - struct AsyncLoweringStorage { - Value *Context; - CallingConv::ID AsyncCC; - unsigned ContextArgNo; - uint64_t ContextHeaderSize; - uint64_t ContextAlignment; - uint64_t FrameOffset; // Start of the frame. - uint64_t ContextSize; // Includes frame size. - GlobalVariable *AsyncFuncPointer; - - Align getContextAlignment() const { return Align(ContextAlignment); } - }; - - union { - SwitchLoweringStorage SwitchLowering; - RetconLoweringStorage RetconLowering; - AsyncLoweringStorage AsyncLowering; - }; - - CoroIdInst *getSwitchCoroId() const { - assert(ABI == coro::ABI::Switch); - return cast(CoroBegin->getId()); - } - - AnyCoroIdRetconInst *getRetconCoroId() const { - assert(ABI == coro::ABI::Retcon || - ABI == coro::ABI::RetconOnce); - return cast(CoroBegin->getId()); - } - - CoroIdAsyncInst *getAsyncCoroId() const { - assert(ABI == coro::ABI::Async); - return cast(CoroBegin->getId()); - } - - unsigned getSwitchIndexField() const { - assert(ABI == coro::ABI::Switch); - assert(FrameTy && "frame type not assigned"); - return SwitchLowering.IndexField; - } - IntegerType *getIndexType() const { - assert(ABI == coro::ABI::Switch); - assert(FrameTy && "frame type not assigned"); - return cast(FrameTy->getElementType(getSwitchIndexField())); - } - ConstantInt *getIndex(uint64_t Value) const { - return ConstantInt::get(getIndexType(), Value); - } - - PointerType *getSwitchResumePointerType() const { - assert(ABI == coro::ABI::Switch); - assert(FrameTy && "frame type not assigned"); - return cast(FrameTy->getElementType(SwitchFieldIndex::Resume)); - } - - FunctionType *getResumeFunctionType() const { - switch (ABI) { - case coro::ABI::Switch: - return FunctionType::get(Type::getVoidTy(FrameTy->getContext()), - PointerType::getUnqual(FrameTy->getContext()), - /*IsVarArg=*/false); - case coro::ABI::Retcon: - case coro::ABI::RetconOnce: - return RetconLowering.ResumePrototype->getFunctionType(); - case coro::ABI::Async: - // Not used. The function type depends on the active suspend. - return nullptr; - } - - llvm_unreachable("Unknown coro::ABI enum"); - } - - ArrayRef getRetconResultTypes() const { - assert(ABI == coro::ABI::Retcon || - ABI == coro::ABI::RetconOnce); - auto FTy = CoroBegin->getFunction()->getFunctionType(); - - // The safety of all this is checked by checkWFRetconPrototype. - if (auto STy = dyn_cast(FTy->getReturnType())) { - return STy->elements().slice(1); - } else { - return ArrayRef(); - } - } - - ArrayRef getRetconResumeTypes() const { - assert(ABI == coro::ABI::Retcon || - ABI == coro::ABI::RetconOnce); - - // The safety of all this is checked by checkWFRetconPrototype. - auto FTy = RetconLowering.ResumePrototype->getFunctionType(); - return FTy->params().slice(1); - } - - CallingConv::ID getResumeFunctionCC() const { - switch (ABI) { - case coro::ABI::Switch: - return CallingConv::Fast; - - case coro::ABI::Retcon: - case coro::ABI::RetconOnce: - return RetconLowering.ResumePrototype->getCallingConv(); - case coro::ABI::Async: - return AsyncLowering.AsyncCC; - } - llvm_unreachable("Unknown coro::ABI enum"); - } - - AllocaInst *getPromiseAlloca() const { - if (ABI == coro::ABI::Switch) - return SwitchLowering.PromiseAlloca; - return nullptr; - } - - BasicBlock::iterator getInsertPtAfterFramePtr() const { - if (auto *I = dyn_cast(FramePtr)) { - BasicBlock::iterator It = std::next(I->getIterator()); - It.setHeadBit(true); // Copy pre-RemoveDIs behaviour. - return It; - } - return cast(FramePtr)->getParent()->getEntryBlock().begin(); - } - - /// Allocate memory according to the rules of the active lowering. - /// - /// \param CG - if non-null, will be updated for the new call - Value *emitAlloc(IRBuilder<> &Builder, Value *Size, CallGraph *CG) const; - - /// Deallocate memory according to the rules of the active lowering. - /// - /// \param CG - if non-null, will be updated for the new call - void emitDealloc(IRBuilder<> &Builder, Value *Ptr, CallGraph *CG) const; - - Shape() = default; - explicit Shape(Function &F, bool OptimizeFrame = false) - : OptimizeFrame(OptimizeFrame) { - buildFrom(F); - } - void buildFrom(Function &F); -}; - bool defaultMaterializable(Instruction &V); void normalizeCoroutine(Function &F, coro::Shape &Shape, TargetTransformInfo &TTI); diff --git a/llvm/lib/Transforms/Coroutines/CoroShape.h b/llvm/lib/Transforms/Coroutines/CoroShape.h new file mode 100644 index 00000000000000..f5798b63bf7325 --- /dev/null +++ b/llvm/lib/Transforms/Coroutines/CoroShape.h @@ -0,0 +1,249 @@ +//===- CoroShape.h - Coroutine info for lowering --------------*- C++ -*---===// +// +// 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 declares the shape info struct that is required by many coroutine +// utility methods. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_COROUTINES_COROSHAPE_H +#define LLVM_TRANSFORMS_COROUTINES_COROSHAPE_H + +#include "CoroInstr.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/PassManager.h" + +namespace llvm { + +class CallGraph; + +namespace coro { + +enum class ABI { + /// The "resume-switch" lowering, where there are separate resume and + /// destroy functions that are shared between all suspend points. The + /// coroutine frame implicitly stores the resume and destroy functions, + /// the current index, and any promise value. + Switch, + + /// The "returned-continuation" lowering, where each suspend point creates a + /// single continuation function that is used for both resuming and + /// destroying. Does not support promises. + Retcon, + + /// The "unique returned-continuation" lowering, where each suspend point + /// creates a single continuation function that is used for both resuming + /// and destroying. Does not support promises. The function is known to + /// suspend at most once during its execution, and the return value of + /// the continuation is void. + RetconOnce, + + /// The "async continuation" lowering, where each suspend point creates a + /// single continuation function. The continuation function is available as an + /// intrinsic. + Async, +}; + +// Holds structural Coroutine Intrinsics for a particular function and other +// values used during CoroSplit pass. +struct LLVM_LIBRARY_VISIBILITY Shape { + CoroBeginInst *CoroBegin; + SmallVector CoroEnds; + SmallVector CoroSizes; + SmallVector CoroAligns; + SmallVector CoroSuspends; + SmallVector SwiftErrorOps; + SmallVector CoroAwaitSuspends; + SmallVector SymmetricTransfers; + + // Field indexes for special fields in the switch lowering. + struct SwitchFieldIndex { + enum { + Resume, + Destroy + + // The promise field is always at a fixed offset from the start of + // frame given its type, but the index isn't a constant for all + // possible frames. + + // The switch-index field isn't at a fixed offset or index, either; + // we just work it in where it fits best. + }; + }; + + coro::ABI ABI; + + StructType *FrameTy; + Align FrameAlign; + uint64_t FrameSize; + Value *FramePtr; + BasicBlock *AllocaSpillBlock; + + /// This would only be true if optimization are enabled. + bool OptimizeFrame; + + struct SwitchLoweringStorage { + SwitchInst *ResumeSwitch; + AllocaInst *PromiseAlloca; + BasicBlock *ResumeEntryBlock; + unsigned IndexField; + unsigned IndexAlign; + unsigned IndexOffset; + bool HasFinalSuspend; + bool HasUnwindCoroEnd; + }; + + struct RetconLoweringStorage { + Function *ResumePrototype; + Function *Alloc; + Function *Dealloc; + BasicBlock *ReturnBlock; + bool IsFrameInlineInStorage; + }; + + struct AsyncLoweringStorage { + Value *Context; + CallingConv::ID AsyncCC; + unsigned ContextArgNo; + uint64_t ContextHeaderSize; + uint64_t ContextAlignment; + uint64_t FrameOffset; // Start of the frame. + uint64_t ContextSize; // Includes frame size. + GlobalVariable *AsyncFuncPointer; + + Align getContextAlignment() const { return Align(ContextAlignment); } + }; + + union { + SwitchLoweringStorage SwitchLowering; + RetconLoweringStorage RetconLowering; + AsyncLoweringStorage AsyncLowering; + }; + + CoroIdInst *getSwitchCoroId() const { + assert(ABI == coro::ABI::Switch); + return cast(CoroBegin->getId()); + } + + AnyCoroIdRetconInst *getRetconCoroId() const { + assert(ABI == coro::ABI::Retcon || ABI == coro::ABI::RetconOnce); + return cast(CoroBegin->getId()); + } + + CoroIdAsyncInst *getAsyncCoroId() const { + assert(ABI == coro::ABI::Async); + return cast(CoroBegin->getId()); + } + + unsigned getSwitchIndexField() const { + assert(ABI == coro::ABI::Switch); + assert(FrameTy && "frame type not assigned"); + return SwitchLowering.IndexField; + } + IntegerType *getIndexType() const { + assert(ABI == coro::ABI::Switch); + assert(FrameTy && "frame type not assigned"); + return cast(FrameTy->getElementType(getSwitchIndexField())); + } + ConstantInt *getIndex(uint64_t Value) const { + return ConstantInt::get(getIndexType(), Value); + } + + PointerType *getSwitchResumePointerType() const { + assert(ABI == coro::ABI::Switch); + assert(FrameTy && "frame type not assigned"); + return cast(FrameTy->getElementType(SwitchFieldIndex::Resume)); + } + + FunctionType *getResumeFunctionType() const { + switch (ABI) { + case coro::ABI::Switch: + return FunctionType::get(Type::getVoidTy(FrameTy->getContext()), + PointerType::getUnqual(FrameTy->getContext()), + /*IsVarArg=*/false); + case coro::ABI::Retcon: + case coro::ABI::RetconOnce: + return RetconLowering.ResumePrototype->getFunctionType(); + case coro::ABI::Async: + // Not used. The function type depends on the active suspend. + return nullptr; + } + + llvm_unreachable("Unknown coro::ABI enum"); + } + + ArrayRef getRetconResultTypes() const { + assert(ABI == coro::ABI::Retcon || ABI == coro::ABI::RetconOnce); + auto FTy = CoroBegin->getFunction()->getFunctionType(); + + // The safety of all this is checked by checkWFRetconPrototype. + if (auto STy = dyn_cast(FTy->getReturnType())) { + return STy->elements().slice(1); + } else { + return ArrayRef(); + } + } + + ArrayRef getRetconResumeTypes() const { + assert(ABI == coro::ABI::Retcon || ABI == coro::ABI::RetconOnce); + + // The safety of all this is checked by checkWFRetconPrototype. + auto FTy = RetconLowering.ResumePrototype->getFunctionType(); + return FTy->params().slice(1); + } + + CallingConv::ID getResumeFunctionCC() const { + switch (ABI) { + case coro::ABI::Switch: + return CallingConv::Fast; + + case coro::ABI::Retcon: + case coro::ABI::RetconOnce: + return RetconLowering.ResumePrototype->getCallingConv(); + case coro::ABI::Async: + return AsyncLowering.AsyncCC; + } + llvm_unreachable("Unknown coro::ABI enum"); + } + + AllocaInst *getPromiseAlloca() const { + if (ABI == coro::ABI::Switch) + return SwitchLowering.PromiseAlloca; + return nullptr; + } + + BasicBlock::iterator getInsertPtAfterFramePtr() const { + if (auto *I = dyn_cast(FramePtr)) { + BasicBlock::iterator It = std::next(I->getIterator()); + It.setHeadBit(true); // Copy pre-RemoveDIs behaviour. + return It; + } + return cast(FramePtr)->getParent()->getEntryBlock().begin(); + } + + /// Allocate memory according to the rules of the active lowering. + /// + /// \param CG - if non-null, will be updated for the new call + Value *emitAlloc(IRBuilder<> &Builder, Value *Size, CallGraph *CG) const; + + /// Deallocate memory according to the rules of the active lowering. + /// + /// \param CG - if non-null, will be updated for the new call + void emitDealloc(IRBuilder<> &Builder, Value *Ptr, CallGraph *CG) const; + + Shape() = default; + explicit Shape(Function &F, bool OptimizeFrame = false) + : OptimizeFrame(OptimizeFrame) { + buildFrom(F); + } + void buildFrom(Function &F); +}; + +} // end namespace coro + +} // end namespace llvm + +#endif // LLVM_TRANSFORMS_COROUTINES_COROSHAPE_H diff --git a/llvm/lib/Transforms/Coroutines/Coroutines.cpp b/llvm/lib/Transforms/Coroutines/Coroutines.cpp index cdc442bc819c37..5cc13a584aef32 100644 --- a/llvm/lib/Transforms/Coroutines/Coroutines.cpp +++ b/llvm/lib/Transforms/Coroutines/Coroutines.cpp @@ -12,6 +12,7 @@ #include "CoroInstr.h" #include "CoroInternal.h" +#include "CoroShape.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Analysis/CallGraph.h"