Skip to content

[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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions llvm/lib/Transforms/Coroutines/CoroEarly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
224 changes: 1 addition & 223 deletions llvm/lib/Transforms/Coroutines/CoroInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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);
Expand Down
Loading
Loading