-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[Coroutines] Move Shape to its own header #108242
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Coroutines] Move Shape to its own header #108242
Conversation
@llvm/pr-subscribers-llvm-transforms @llvm/pr-subscribers-coroutines Author: Tyler Nowicki (TylerNowicki) Changes
See RFC for more info: https://discourse.llvm.org/t/rfc-abi-objects-for-coroutines/81057 Full diff: https://github.com/llvm/llvm-project/pull/108242.diff 4 Files Affected:
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 698c21a797420a..df0a53ba79ed19 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"
@@ -57,229 +58,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<AnyCoroEndInst *, 4> CoroEnds;
- SmallVector<CoroSizeInst *, 2> CoroSizes;
- SmallVector<CoroAlignInst *, 2> CoroAligns;
- SmallVector<AnyCoroSuspendInst *, 4> CoroSuspends;
- SmallVector<CallInst*, 2> SwiftErrorOps;
- SmallVector<CoroAwaitSuspendInst *, 4> CoroAwaitSuspends;
- SmallVector<CallInst *, 2> 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<CoroIdInst>(CoroBegin->getId());
- }
-
- AnyCoroIdRetconInst *getRetconCoroId() const {
- assert(ABI == coro::ABI::Retcon ||
- ABI == coro::ABI::RetconOnce);
- return cast<AnyCoroIdRetconInst>(CoroBegin->getId());
- }
-
- CoroIdAsyncInst *getAsyncCoroId() const {
- assert(ABI == coro::ABI::Async);
- return cast<CoroIdAsyncInst>(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<IntegerType>(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<PointerType>(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<Type*> 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<StructType>(FTy->getReturnType())) {
- return STy->elements().slice(1);
- } else {
- return ArrayRef<Type*>();
- }
- }
-
- ArrayRef<Type*> 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<Instruction>(FramePtr)) {
- BasicBlock::iterator It = std::next(I->getIterator());
- It.setHeadBit(true); // Copy pre-RemoveDIs behaviour.
- return It;
- }
- return cast<Argument>(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<AnyCoroEndInst *, 4> CoroEnds;
+ SmallVector<CoroSizeInst *, 2> CoroSizes;
+ SmallVector<CoroAlignInst *, 2> CoroAligns;
+ SmallVector<AnyCoroSuspendInst *, 4> CoroSuspends;
+ SmallVector<CallInst *, 2> SwiftErrorOps;
+ SmallVector<CoroAwaitSuspendInst *, 4> CoroAwaitSuspends;
+ SmallVector<CallInst *, 2> 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<CoroIdInst>(CoroBegin->getId());
+ }
+
+ AnyCoroIdRetconInst *getRetconCoroId() const {
+ assert(ABI == coro::ABI::Retcon || ABI == coro::ABI::RetconOnce);
+ return cast<AnyCoroIdRetconInst>(CoroBegin->getId());
+ }
+
+ CoroIdAsyncInst *getAsyncCoroId() const {
+ assert(ABI == coro::ABI::Async);
+ return cast<CoroIdAsyncInst>(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<IntegerType>(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<PointerType>(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<Type *> 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<StructType>(FTy->getReturnType())) {
+ return STy->elements().slice(1);
+ } else {
+ return ArrayRef<Type *>();
+ }
+ }
+
+ ArrayRef<Type *> 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<Instruction>(FramePtr)) {
+ BasicBlock::iterator It = std::next(I->getIterator());
+ It.setHeadBit(true); // Copy pre-RemoveDIs behaviour.
+ return It;
+ }
+ return cast<Argument>(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 be257339e0ac49..3811a2ef109415 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"
|
45fdd5a
to
fd2bd5c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM by assuming it is trivial code movement.
* 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
fd2bd5c
to
4baaa38
Compare
See RFC for more info: https://discourse.llvm.org/t/rfc-abi-objects-for-coroutines/81057