[llvm] 4c040c0 - [Coroutines] Move Shape to its own header (#108242)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 13 11:11:35 PDT 2024
Author: Tyler Nowicki
Date: 2024-09-13T14:11:30-04:00
New Revision: 4c040c027575f3a30dc94bfab4c975567195bdc7
URL: https://github.com/llvm/llvm-project/commit/4c040c027575f3a30dc94bfab4c975567195bdc7
DIFF: https://github.com/llvm/llvm-project/commit/4c040c027575f3a30dc94bfab4c975567195bdc7.diff
LOG: [Coroutines] Move Shape to its own header (#108242)
* To create custom ABIs plugin libraries need access to CoroShape.
* As a step in enabling plugin libraries, move Shape into its own header
* The header will eventually be moved into include/llvm/Transforms/Coroutines
See RFC for more info:
https://discourse.llvm.org/t/rfc-abi-objects-for-coroutines/81057
Added:
llvm/lib/Transforms/Coroutines/CoroShape.h
Modified:
llvm/lib/Transforms/Coroutines/CoroEarly.cpp
llvm/lib/Transforms/Coroutines/CoroInternal.h
llvm/lib/Transforms/Coroutines/Coroutines.cpp
Removed:
################################################################################
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<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 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"
More information about the llvm-commits
mailing list