#include "llvm/Support/MathExtras.h"
#include "llvm/Support/OptimizedStructLayout.h"
#include "llvm/Support/circular_raw_ostream.h"
+#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Transforms/Utils/PromoteMemToReg.h"
FieldIndexMap[V] = Index;
}
+ uint64_t getAlign(Value *V) const {
+ auto Iter = FieldAlignMap.find(V);
+ assert(Iter != FieldAlignMap.end());
+ return Iter->second;
+ }
+
+ void setAlign(Value *V, uint64_t Align) {
+ assert(FieldAlignMap.count(V) == 0);
+ FieldAlignMap.insert({V, Align});
+ }
+
+ uint64_t getOffset(Value *V) const {
+ auto Iter = FieldOffsetMap.find(V);
+ assert(Iter != FieldOffsetMap.end());
+ return Iter->second;
+ }
+
+ void setOffset(Value *V, uint64_t Offset) {
+ assert(FieldOffsetMap.count(V) == 0);
+ FieldOffsetMap.insert({V, Offset});
+ }
+
// Remap the index of every field in the frame, using the final layout index.
void updateLayoutIndex(FrameTypeBuilder &B);
// with their original insertion field index. After the frame is built, their
// indexes will be updated into the final layout index.
DenseMap<Value *, uint32_t> FieldIndexMap;
+ // Map from values to their alignment on the frame. They would be set after
+ // the frame is built.
+ DenseMap<Value *, uint64_t> FieldAlignMap;
+ // Map from values to their offset on the frame. They would be set after
+ // the frame is built.
+ DenseMap<Value *, uint64_t> FieldOffsetMap;
};
} // namespace
assert(IsFinished && "not yet finished!");
return Fields[Id].LayoutFieldIndex;
}
+
+ Field getLayoutField(FieldIDType Id) const {
+ assert(IsFinished && "not yet finished!");
+ return Fields[Id];
+ }
};
} // namespace
void FrameDataInfo::updateLayoutIndex(FrameTypeBuilder &B) {
auto Updater = [&](Value *I) {
- setFieldIndex(I, B.getLayoutFieldIndex(getFieldIndex(I)));
+ auto Field = B.getLayoutField(getFieldIndex(I));
+ setFieldIndex(I, Field.LayoutFieldIndex);
+ setAlign(I, Field.Alignment.value());
+ setOffset(I, Field.Offset);
};
LayoutIndexUpdateStarted = true;
for (auto &S : Spills)
IsFinished = true;
}
+static void cacheDIVar(FrameDataInfo &FrameData,
+ DenseMap<Value *, DILocalVariable *> &DIVarCache) {
+ for (auto *V : FrameData.getAllDefs()) {
+ if (DIVarCache.find(V) != DIVarCache.end())
+ continue;
+
+ auto DDIs = FindDbgDeclareUses(V);
+ auto *I = llvm::find_if(DDIs, [](DbgDeclareInst *DDI) {
+ return DDI->getExpression()->getNumElements() == 0;
+ });
+ if (I != DDIs.end())
+ DIVarCache.insert({V, (*I)->getVariable()});
+ }
+}
+
+/// Create name for Type. It uses MDString to store new created string to
+/// avoid memory leak.
+static StringRef solveTypeName(Type *Ty) {
+ if (Ty->isIntegerTy()) {
+ // The longest name in common may be '__int_128', which has 9 bits.
+ SmallString<16> Buffer;
+ raw_svector_ostream OS(Buffer);
+ OS << "__int_" << cast<IntegerType>(Ty)->getBitWidth();
+ auto *MDName = MDString::get(Ty->getContext(), OS.str());
+ return MDName->getString();
+ }
+
+ if (Ty->isFloatingPointTy()) {
+ if (Ty->isFloatTy())
+ return "__float_";
+ if (Ty->isDoubleTy())
+ return "__double_";
+ return "__floating_type_";
+ }
+
+ if (Ty->isPointerTy()) {
+ auto *PtrTy = cast<PointerType>(Ty);
+ Type *PointeeTy = PtrTy->getElementType();
+ auto Name = solveTypeName(PointeeTy);
+ if (Name == "UnknownType")
+ return "PointerType";
+ SmallString<16> Buffer;
+ Twine(Name + "_Ptr").toStringRef(Buffer);
+ auto *MDName = MDString::get(Ty->getContext(), Buffer.str());
+ return MDName->getString();
+ }
+
+ if (Ty->isStructTy()) {
+ if (!cast<StructType>(Ty)->hasName())
+ return "__LiteralStructType_";
+
+ auto Name = Ty->getStructName();
+
+ SmallString<16> Buffer(Name);
+ for_each(Buffer, [](auto &Iter) {
+ if (Iter == '.' || Iter == ':')
+ Iter = '_';
+ });
+ auto *MDName = MDString::get(Ty->getContext(), Buffer.str());
+ return MDName->getString();
+ }
+
+ return "UnknownType";
+}
+
+static DIType *solveDIType(DIBuilder &Builder, Type *Ty, DataLayout &Layout,
+ DIScope *Scope, unsigned LineNum,
+ DenseMap<Type *, DIType *> &DITypeCache) {
+ if (DIType *DT = DITypeCache.lookup(Ty))
+ return DT;
+
+ StringRef Name = solveTypeName(Ty);
+
+ DIType *RetType = nullptr;
+
+ if (Ty->isIntegerTy()) {
+ auto BitWidth = cast<IntegerType>(Ty)->getBitWidth();
+ RetType = Builder.createBasicType(Name, BitWidth, dwarf::DW_ATE_signed,
+ llvm::DINode::FlagArtificial);
+ } else if (Ty->isFloatingPointTy()) {
+ RetType = Builder.createBasicType(Name, Layout.getTypeSizeInBits(Ty),
+ dwarf::DW_ATE_float,
+ llvm::DINode::FlagArtificial);
+ } else if (Ty->isPointerTy()) {
+ // Construct BasicType instead of PointerType to avoid infinite
+ // search problem.
+ // For example, we would be in trouble if we traverse recursively:
+ //
+ // struct Node {
+ // Node* ptr;
+ // };
+ RetType = Builder.createBasicType(Name, Layout.getTypeSizeInBits(Ty),
+ dwarf::DW_ATE_address,
+ llvm::DINode::FlagArtificial);
+ } else if (Ty->isStructTy()) {
+ auto *DIStruct = Builder.createStructType(
+ Scope, Name, Scope->getFile(), LineNum, Layout.getTypeSizeInBits(Ty),
+ Layout.getPrefTypeAlignment(Ty), llvm::DINode::FlagArtificial, nullptr,
+ llvm::DINodeArray());
+
+ auto *StructTy = cast<StructType>(Ty);
+ SmallVector<Metadata *, 16> Elements;
+ for (unsigned I = 0; I < StructTy->getNumElements(); I++) {
+ DIType *DITy = solveDIType(Builder, StructTy->getElementType(I), Layout,
+ Scope, LineNum, DITypeCache);
+ assert(DITy);
+ Elements.push_back(Builder.createMemberType(
+ Scope, DITy->getName(), Scope->getFile(), LineNum,
+ DITy->getSizeInBits(), DITy->getAlignInBits(),
+ Layout.getStructLayout(StructTy)->getElementOffsetInBits(I),
+ llvm::DINode::FlagArtificial, DITy));
+ }
+
+ Builder.replaceArrays(DIStruct, Builder.getOrCreateArray(Elements));
+
+ RetType = DIStruct;
+ } else {
+ LLVM_DEBUG(dbgs() << "Unresolved Type: " << *Ty << "\n";);
+ SmallString<32> Buffer;
+ raw_svector_ostream OS(Buffer);
+ OS << Name.str() << "_" << Layout.getTypeSizeInBits(Ty);
+ RetType = Builder.createBasicType(OS.str(), Layout.getTypeSizeInBits(Ty),
+ dwarf::DW_ATE_address,
+ llvm::DINode::FlagArtificial);
+ }
+
+ DITypeCache.insert({Ty, RetType});
+ return RetType;
+}
+
+/// Build artificial debug info for C++ coroutine frames to allow users to
+/// inspect the contents of the frame directly
+///
+/// Create Debug information for coroutine frame with debug name "__coro_frame".
+/// The debug information for the fields of coroutine frame is constructed from
+/// the following way:
+/// 1. For all the value in the Frame, we search the use of dbg.declare to find
+/// the corresponding debug variables for the value. If we can find the
+/// debug variable, we can get full and accurate debug information.
+/// 2. If we can't get debug information in step 1 and 2, we could only try to
+/// build the DIType by Type. We did this in solveDIType. We only handle
+/// integer, float, double, integer type and struct type for now.
+static void buildFrameDebugInfo(Function &F, coro::Shape &Shape,
+ FrameDataInfo &FrameData) {
+ DISubprogram *DIS = F.getSubprogram();
+ // If there is no DISubprogram for F, it implies the Function are not compiled
+ // with debug info. So we also don't need to generate debug info for the frame
+ // neither.
+ if (!DIS || !DIS->getUnit() ||
+ !dwarf::isCPlusPlus(
+ (dwarf::SourceLanguage)DIS->getUnit()->getSourceLanguage()))
+ return;
+
+ assert(Shape.ABI == coro::ABI::Switch &&
+ "We could only build debug infomation for C++ coroutine now.\n");
+
+ DIBuilder DBuilder(*F.getParent(), /*AllowUnresolved*/ false);
+
+ AllocaInst *PromiseAlloca = Shape.getPromiseAlloca();
+ assert(PromiseAlloca &&
+ "Coroutine with switch ABI should own Promise alloca");
+
+ TinyPtrVector<DbgDeclareInst *> DIs = FindDbgDeclareUses(PromiseAlloca);
+ if (DIs.empty())
+ return;
+
+ DbgDeclareInst *PromiseDDI = DIs.front();
+ DILocalVariable *PromiseDIVariable = PromiseDDI->getVariable();
+ DILocalScope *PromiseDIScope = PromiseDIVariable->getScope();
+ DIFile *DFile = PromiseDIScope->getFile();
+ DILocation *DILoc = PromiseDDI->getDebugLoc().get();
+ unsigned LineNum = PromiseDIVariable->getLine();
+
+ DICompositeType *FrameDITy = DBuilder.createStructType(
+ PromiseDIScope, "__coro_frame_ty", DFile, LineNum, Shape.FrameSize * 8,
+ Shape.FrameAlign.value() * 8, llvm::DINode::FlagArtificial, nullptr,
+ llvm::DINodeArray());
+ StructType *FrameTy = Shape.FrameTy;
+ SmallVector<Metadata *, 16> Elements;
+ DataLayout Layout = F.getParent()->getDataLayout();
+
+ DenseMap<Value *, DILocalVariable *> DIVarCache;
+ cacheDIVar(FrameData, DIVarCache);
+
+ unsigned ResumeIndex = coro::Shape::SwitchFieldIndex::Resume;
+ unsigned DestroyIndex = coro::Shape::SwitchFieldIndex::Destroy;
+ unsigned IndexIndex = Shape.SwitchLowering.IndexField;
+
+ DenseMap<unsigned, StringRef> NameCache;
+ NameCache.insert({ResumeIndex, "__resume_fn"});
+ NameCache.insert({DestroyIndex, "__destroy_fn"});
+ NameCache.insert({IndexIndex, "__coro_index"});
+
+ Type *ResumeFnTy = FrameTy->getElementType(ResumeIndex),
+ *DestroyFnTy = FrameTy->getElementType(DestroyIndex),
+ *IndexTy = FrameTy->getElementType(IndexIndex);
+
+ DenseMap<unsigned, DIType *> TyCache;
+ TyCache.insert({ResumeIndex,
+ DBuilder.createBasicType("__resume_fn",
+ Layout.getTypeSizeInBits(ResumeFnTy),
+ dwarf::DW_ATE_address)});
+ TyCache.insert(
+ {DestroyIndex, DBuilder.createBasicType(
+ "__destroy_fn", Layout.getTypeSizeInBits(DestroyFnTy),
+ dwarf::DW_ATE_address)});
+
+ /// FIXME: If we fill the field `SizeInBits` with the actual size of
+ /// __coro_index in bits, then __coro_index wouldn't show in the debugger.
+ TyCache.insert({IndexIndex, DBuilder.createBasicType(
+ "__coro_index",
+ (Layout.getTypeSizeInBits(IndexTy) < 8)
+ ? 8
+ : Layout.getTypeSizeInBits(IndexTy),
+ dwarf::DW_ATE_unsigned_char)});
+
+ for (auto *V : FrameData.getAllDefs()) {
+ if (DIVarCache.find(V) == DIVarCache.end())
+ continue;
+
+ auto Index = FrameData.getFieldIndex(V);
+
+ NameCache.insert({Index, DIVarCache[V]->getName()});
+ TyCache.insert({Index, DIVarCache[V]->getType()});
+ }
+
+ // Cache from index to (Align, Offset Pair)
+ DenseMap<unsigned, std::pair<unsigned, unsigned>> OffsetCache;
+ // The Align and Offset of Resume function and Destroy function are fixed.
+ OffsetCache.insert({ResumeIndex, {8, 0}});
+ OffsetCache.insert({DestroyIndex, {8, 8}});
+ OffsetCache.insert(
+ {IndexIndex,
+ {Shape.SwitchLowering.IndexAlign, Shape.SwitchLowering.IndexOffset}});
+
+ for (auto *V : FrameData.getAllDefs()) {
+ auto Index = FrameData.getFieldIndex(V);
+
+ OffsetCache.insert(
+ {Index, {FrameData.getAlign(V), FrameData.getOffset(V)}});
+ }
+
+ DenseMap<Type *, DIType *> DITypeCache;
+ // This counter is used to avoid same type names. e.g., there would be
+ // many i32 and i64 types in one coroutine. And we would use i32_0 and
+ // i32_1 to avoid the same type. Since it makes no sense the name of the
+ // fields confilicts with each other.
+ unsigned UnknownTypeNum = 0;
+ for (unsigned Index = 0; Index < FrameTy->getNumElements(); Index++) {
+ if (OffsetCache.find(Index) == OffsetCache.end())
+ continue;
+
+ std::string Name;
+ uint64_t SizeInBits;
+ uint32_t AlignInBits;
+ uint64_t OffsetInBits;
+ DIType *DITy = nullptr;
+
+ Type *Ty = FrameTy->getElementType(Index);
+ assert(Ty->isSized() && "We can't handle type which is not sized.\n");
+ SizeInBits = Layout.getTypeSizeInBits(Ty).getFixedSize();
+ AlignInBits = OffsetCache[Index].first * 8;
+ OffsetInBits = OffsetCache[Index].second * 8;
+
+ if (NameCache.find(Index) != NameCache.end()) {
+ Name = NameCache[Index].str();
+ DITy = TyCache[Index];
+ } else {
+ DITy = solveDIType(DBuilder, Ty, Layout, PromiseDIScope, LineNum,
+ DITypeCache);
+ assert(DITy && "SolveDIType shouldn't return nullptr.\n");
+ Name = DITy->getName().str();
+ Name += "_" + std::to_string(UnknownTypeNum);
+ UnknownTypeNum++;
+ }
+
+ Elements.push_back(DBuilder.createMemberType(
+ PromiseDIScope, Name, DFile, LineNum, SizeInBits, AlignInBits,
+ OffsetInBits, llvm::DINode::FlagArtificial, DITy));
+ }
+
+ DBuilder.replaceArrays(FrameDITy, DBuilder.getOrCreateArray(Elements));
+
+ auto *FrameDIVar = DBuilder.createAutoVariable(PromiseDIScope, "__coro_frame",
+ DFile, LineNum, FrameDITy,
+ true, DINode::FlagArtificial);
+ assert(FrameDIVar->isValidLocationForIntrinsic(PromiseDDI->getDebugLoc()));
+
+ // Subprogram would have ContainedNodes field which records the debug
+ // variables it contained. So we need to add __coro_frame to the
+ // ContainedNodes of it.
+ //
+ // If we don't add __coro_frame to the RetainedNodes, user may get
+ // `no symbol __coro_frame in context` rather than `__coro_frame`
+ // is optimized out, which is more precise.
+ if (auto *SubProgram = dyn_cast<DISubprogram>(PromiseDIScope)) {
+ auto RetainedNodes = SubProgram->getRetainedNodes();
+ SmallVector<Metadata *, 32> RetainedNodesVec(RetainedNodes.begin(),
+ RetainedNodes.end());
+ RetainedNodesVec.push_back(FrameDIVar);
+ SubProgram->replaceOperandWith(
+ 7, (MDTuple::get(F.getContext(), RetainedNodesVec)));
+ }
+
+ DBuilder.insertDeclare(Shape.FramePtr, FrameDIVar,
+ DBuilder.createExpression(), DILoc,
+ Shape.FramePtr->getNextNode());
+}
+
// Build a struct that will keep state for an active coroutine.
// struct f.frame {
// ResumeFnTy ResumeFnAddr;
Shape.FrameSize = B.getStructSize();
switch (Shape.ABI) {
- case coro::ABI::Switch:
+ case coro::ABI::Switch: {
// In the switch ABI, remember the switch-index field.
- Shape.SwitchLowering.IndexField =
- B.getLayoutFieldIndex(*SwitchIndexFieldId);
+ auto IndexField = B.getLayoutField(*SwitchIndexFieldId);
+ Shape.SwitchLowering.IndexField = IndexField.LayoutFieldIndex;
+ Shape.SwitchLowering.IndexAlign = IndexField.Alignment.value();
+ Shape.SwitchLowering.IndexOffset = IndexField.Offset;
// Also round the frame size up to a multiple of its alignment, as is
// generally expected in C/C++.
Shape.FrameSize = alignTo(Shape.FrameSize, Shape.FrameAlign);
break;
+ }
// In the retcon ABI, remember whether the frame is inline in the storage.
case coro::ABI::Retcon:
return CleanupRet;
}
+static void createFramePtr(coro::Shape &Shape) {
+ auto *CB = Shape.CoroBegin;
+ IRBuilder<> Builder(CB->getNextNode());
+ StructType *FrameTy = Shape.FrameTy;
+ PointerType *FramePtrTy = FrameTy->getPointerTo();
+ Shape.FramePtr =
+ cast<Instruction>(Builder.CreateBitCast(CB, FramePtrTy, "FramePtr"));
+}
+
// Replace all alloca and SSA values that are accessed across suspend points
// with GetElementPointer from coroutine frame + loads and stores. Create an
// AllocaSpillBB that will become the new entry block for the resume parts of
coro::Shape &Shape) {
auto *CB = Shape.CoroBegin;
LLVMContext &C = CB->getContext();
- IRBuilder<> Builder(CB->getNextNode());
+ IRBuilder<> Builder(C);
StructType *FrameTy = Shape.FrameTy;
- PointerType *FramePtrTy = FrameTy->getPointerTo();
- auto *FramePtr =
- cast<Instruction>(Builder.CreateBitCast(CB, FramePtrTy, "FramePtr"));
+ Instruction *FramePtr = Shape.FramePtr;
DominatorTree DT(*CB->getFunction());
SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> DbgPtrAllocaCache;
Shape.ABI == coro::ABI::Async)
sinkSpillUsesAfterCoroBegin(F, FrameData, Shape.CoroBegin);
Shape.FrameTy = buildFrameType(F, Shape, FrameData);
- Shape.FramePtr = insertSpills(FrameData, Shape);
+ createFramePtr(Shape);
+ // For now, this works for C++ programs only.
+ buildFrameDebugInfo(F, Shape, FrameData);
+ insertSpills(FrameData, Shape);
lowerLocalAllocas(LocalAllocas, DeadInstructions);
for (auto I : DeadInstructions)
--- /dev/null
+; RUN: opt < %s -coro-early -coro-split -coro-split -S | FileCheck %s
+
+; Checks whether the dbg.declare for `__coro_frame` are created.
+
+; CHECK-LABEL: define void @f(
+; CHECK: coro.init:
+; CHECK: %[[begin:.*]] = call noalias nonnull i8* @llvm.coro.begin(
+; CHECK: %[[FramePtr:.*]] = bitcast i8* %[[begin]] to
+; CHECK: call void @llvm.dbg.declare(metadata %f.Frame* %[[FramePtr]], metadata ![[CORO_FRAME:[0-9]+]], metadata !DIExpression())
+;
+; CHECK: define internal fastcc void @f.resume(
+; CHECK: entry.resume:
+; CHECK: call void @llvm.dbg.declare(metadata %f.Frame** %[[FramePtr_RESUME:.*]], metadata ![[CORO_FRAME_IN_RESUME:[0-9]+]], metadata !DIExpression()
+;
+; CHECK: ![[FILE:[0-9]+]] = !DIFile(filename: "coro-debug.cpp"
+; CHECK: ![[RAMP:[0-9]+]] = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov",
+; CHECK: ![[RAMP_SCOPE:[0-9]+]] = distinct !DILexicalBlock(scope: ![[RAMP]], file: ![[FILE]], line: 23
+; CHECK: ![[CORO_FRAME]] = !DILocalVariable(name: "__coro_frame", scope: ![[RAMP_SCOPE]], file: ![[FILE]], line: [[PROMISE_VAR_LINE:[0-9]+]], type: ![[FRAME_TYPE:[0-9]+]], flags: DIFlagArtificial)
+; CHECK: ![[FRAME_TYPE]] = !DICompositeType(tag: DW_TAG_structure_type, name: "__coro_frame_ty", {{.*}}elements: ![[ELEMENTS:[0-9]+]]
+; CHECK: ![[ELEMENTS]] = !{![[RESUME_FN:[0-9]+]], ![[DESTROY_FN:[0-9]+]], ![[PROMISE:[0-9]+]], ![[INT64_0:[0-9]+]], ![[DOUBLE_1:[0-9]+]], ![[INT32_2:[0-9]+]], ![[INT32_3:[0-9]+]], ![[STRUCT_4:[0-9]+]], ![[CORO_INDEX:[0-9]+]]
+; CHECK: ![[RESUME_FN]] = !DIDerivedType(tag: DW_TAG_member, name: "__resume_fn"{{.*}}, flags: DIFlagArtificial
+; CHECK: ![[DESTROY_FN]] = !DIDerivedType(tag: DW_TAG_member, name: "__destroy_fn"{{.*}}, flags: DIFlagArtificial
+; CHECK: ![[PROMISE]] = !DIDerivedType(tag: DW_TAG_member, name: "__promise",{{.*}}baseType: ![[PROMISE_BASE:[0-9]+]]
+; CHECK: ![[PROMISE_BASE]] = !DIDerivedType(tag: DW_TAG_typedef, name: "promise_type"
+; CHECK: ![[INT64_0]] = !DIDerivedType(tag: DW_TAG_member, name: "__int_64_0", scope: ![[RAMP_SCOPE]], file: ![[FILE]], line: [[PROMISE_VAR_LINE]], baseType: ![[I64_BASE:[0-9]+]],{{.*}}, flags: DIFlagArtificial
+; CHECK: ![[I64_BASE]] = !DIBasicType(name: "__int_64", size: 64, encoding: DW_ATE_signed, flags: DIFlagArtificial)
+; CHECK: ![[DOUBLE_1]] = !DIDerivedType(tag: DW_TAG_member, name: "__double__1", scope: ![[RAMP_SCOPE]], file: ![[FILE]], line: [[PROMISE_VAR_LINE]], baseType: ![[DOUBLE_BASE:[0-9]+]]{{.*}}, flags: DIFlagArtificial
+; CHECK: ![[DOUBLE_BASE]] = !DIBasicType(name: "__double_", size: 64, encoding: DW_ATE_float, flags: DIFlagArtificial)
+; CHECK: ![[INT32_2]] = !DIDerivedType(tag: DW_TAG_member, name: "__int_32_2", scope: ![[RAMP_SCOPE]], file: ![[FILE]], line: [[PROMISE_VAR_LINE]], baseType: ![[I32_BASE:[0-9]+]]{{.*}}, flags: DIFlagArtificial
+; CHECK: ![[I32_BASE]] = !DIBasicType(name: "__int_32", size: 32, encoding: DW_ATE_signed, flags: DIFlagArtificial)
+; CHECK: ![[INT32_3]] = !DIDerivedType(tag: DW_TAG_member, name: "__int_32_3", scope: ![[RAMP_SCOPE]], file: ![[FILE]], line: [[PROMISE_VAR_LINE]], baseType: ![[I32_BASE]]
+; CHECK: ![[STRUCT_4]] = !DIDerivedType(tag: DW_TAG_member, name: "struct_big_structure_4", scope: ![[RAMP_SCOPE]], file: ![[FILE]], line: [[PROMISE_VAR_LINE]], baseType: ![[STRUCT_BASE:[0-9]+]]
+; CHECK: ![[STRUCT_BASE]] = !DICompositeType(tag: DW_TAG_structure_type, name: "struct_big_structure"{{.*}}elements: ![[STRUCT_ELEMENTS:[0-9]+]]
+; CHECK: ![[STRUCT_ELEMENTS]] = !{![[MEM_TYPE:[0-9]+]]}
+; CHECK: ![[MEM_TYPE]] = !DIDerivedType(tag: DW_TAG_member, name: "UnknownType_4000"
+; CHECK: ![[CORO_INDEX]] = !DIDerivedType(tag: DW_TAG_member, name: "__coro_index"
+; CHECK: ![[PROMISE_VAR:[0-9]+]] = !DILocalVariable(name: "__promise", scope: ![[RAMP_SCOPE]], file: ![[FILE]], line: [[PROMISE_VAR_LINE]]
+; CHECK: ![[BAR_FUNC:[0-9]+]] = distinct !DISubprogram(name: "bar", linkageName: "_Z3barv",
+; CHECK: ![[BAR_SCOPE:[0-9]+]] = distinct !DILexicalBlock(scope: ![[BAR_FUNC]], file: !1
+; CHECK: ![[FRAME_TYPE_IN_BAR:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "__coro_frame_ty", scope: ![[BAR_SCOPE]], file: ![[FILE]], line: [[BAR_LINE:[0-9]+]]{{.*}}elements: ![[ELEMENTS_IN_BAR:[0-9]+]]
+; CHECK: ![[ELEMENTS_IN_BAR]] = !{![[RESUME_FN_IN_BAR:[0-9]+]], ![[DESTROY_FN_IN_BAR:[0-9]+]], ![[PROMISE_IN_BAR:[0-9]+]], ![[INT64_0_IN_BAR:[0-9]+]], ![[DOUBLE_1_IN_BAR:[0-9]+]], ![[INT32_2_IN_BAR:[0-9]+]], ![[STRUCT_3_IN_BAR:[0-9]+]], ![[CORO_INDEX_IN_BAR:[0-9]+]]
+; CHECK: ![[PROMISE_IN_BAR]] = !DIDerivedType(tag: DW_TAG_member, name: "__promise",{{.*}}baseType: ![[PROMISE_BASE]]
+; CHECK: ![[INT64_0_IN_BAR]] = !DIDerivedType(tag: DW_TAG_member, name: "__int_64_0", scope: ![[BAR_SCOPE]], file: ![[FILE]], line: [[BAR_LINE]], baseType: ![[I64_BASE]]
+; CHECK: ![[DOUBLE_1_IN_BAR]] = !DIDerivedType(tag: DW_TAG_member, name: "__double__1", scope: ![[BAR_SCOPE]], file: ![[FILE]], line: [[BAR_LINE]], baseType: ![[DOUBLE_BASE]]
+; CHECK: ![[INT32_2_IN_BAR]] = !DIDerivedType(tag: DW_TAG_member, name: "__int_32_2", scope: ![[BAR_SCOPE]], file: ![[FILE]], line: [[BAR_LINE]], baseType: ![[I32_BASE]]
+; CHECK: ![[STRUCT_3_IN_BAR]] = !DIDerivedType(tag: DW_TAG_member, name: "struct_big_structure_3", scope: ![[BAR_SCOPE]], file: ![[FILE]], line: [[BAR_LINE]], baseType: ![[STRUCT_BASE_IN_BAR:[0-9]+]]
+; CHECK: ![[STRUCT_BASE_IN_BAR]] = !DICompositeType(tag: DW_TAG_structure_type, name: "struct_big_structure", scope: ![[BAR_SCOPE]], file: ![[FILE]], line: [[BAR_LINE]],{{.*}}
+; CHECK: ![[CORO_FRAME_IN_RESUME]] = !DILocalVariable(name: "__coro_frame",{{.*}}type: ![[FRAME_TYPE]]
+
+
+%promise_type = type { i32, i32, double }
+%struct.big_structure = type { [500 x i8] }
+declare void @produce(%struct.big_structure*)
+declare void @consume(%struct.big_structure*)
+declare void @pi32(i32*)
+declare void @pi64(i64*)
+declare void @pdouble(double*)
+
+define void @f(i32 %a, i32 %b, i64 %c, double %d) !dbg !8 {
+entry:
+ %__promise = alloca %promise_type, align 8
+ %0 = bitcast %promise_type* %__promise to i8*
+ %a.alloc = alloca i32, align 4
+ %b.alloc = alloca i32, align 4
+ %c.alloc = alloca i64, align 4
+ %d.alloc = alloca double, align 4
+ store i32 %a, i32* %a.alloc
+ store i32 %b, i32* %b.alloc
+ store i64 %c, i64* %c.alloc
+ store double %d, double* %d.alloc
+ %struct.data = alloca %struct.big_structure, align 1
+ call void @produce(%struct.big_structure* %struct.data)
+ %id = call token @llvm.coro.id(i32 16, i8* %0, i8* null, i8* null)
+ %alloc = call i1 @llvm.coro.alloc(token %id)
+ br i1 %alloc, label %coro.alloc, label %coro.init
+
+coro.alloc: ; preds = %entry
+ %size = call i64 @llvm.coro.size.i64()
+ %memory = call i8* @new(i64 %size)
+ br label %coro.init
+
+coro.init: ; preds = %coro.alloc, %entry
+ %phi.entry.alloc = phi i8* [ null, %entry ], [ %memory, %coro.alloc ]
+ %begin = call i8* @llvm.coro.begin(token %id, i8* %phi.entry.alloc)
+ call void @llvm.dbg.declare(metadata %promise_type* %__promise, metadata !6, metadata !DIExpression()), !dbg !18
+ %ready = call i1 @await_ready()
+ br i1 %ready, label %init.ready, label %init.suspend
+
+init.suspend: ; preds = %coro.init
+ %save = call token @llvm.coro.save(i8* null)
+ call void @await_suspend()
+ %suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
+ switch i8 %suspend, label %coro.ret [
+ i8 0, label %init.ready
+ i8 1, label %init.cleanup
+ ]
+
+init.cleanup: ; preds = %init.suspend
+ br label %cleanup
+
+init.ready: ; preds = %init.suspend, %coro.init
+ call void @await_resume()
+ %ready.again = call zeroext i1 @await_ready()
+ br i1 %ready.again, label %await.ready, label %await.suspend
+
+await.suspend: ; preds = %init.ready
+ %save.again = call token @llvm.coro.save(i8* null)
+ %from.address = call i8* @from_address(i8* %begin)
+ call void @await_suspend()
+ %suspend.again = call i8 @llvm.coro.suspend(token %save.again, i1 false)
+ switch i8 %suspend.again, label %coro.ret [
+ i8 0, label %await.ready
+ i8 1, label %await.cleanup
+ ]
+
+await.cleanup: ; preds = %await.suspend
+ br label %cleanup
+
+await.ready: ; preds = %await.suspend, %init.ready
+ call void @await_resume()
+ %i.i = getelementptr inbounds %promise_type, %promise_type* %__promise, i64 0, i32 0
+ store i32 1, i32* %i.i, align 8
+ %j.i = getelementptr inbounds %promise_type, %promise_type* %__promise, i64 0, i32 1
+ store i32 2, i32* %j.i, align 4
+ %k.i = getelementptr inbounds %promise_type, %promise_type* %__promise, i64 0, i32 2
+ store double 3.000000e+00, double* %k.i, align 8
+ call void @consume(%struct.big_structure* %struct.data)
+ call void @pi32(i32* %a.alloc)
+ call void @pi32(i32* %b.alloc)
+ call void @pi64(i64* %c.alloc)
+ call void @pdouble(double* %d.alloc)
+ call void @return_void()
+ br label %coro.final
+
+coro.final: ; preds = %await.ready
+ call void @final_suspend()
+ %coro.final.await_ready = call i1 @await_ready()
+ br i1 %coro.final.await_ready, label %final.ready, label %final.suspend
+
+final.suspend: ; preds = %coro.final
+ %final.suspend.coro.save = call token @llvm.coro.save(i8* null)
+ %final.suspend.from_address = call i8* @from_address(i8* %begin)
+ call void @await_suspend()
+ %final.suspend.coro.suspend = call i8 @llvm.coro.suspend(token %final.suspend.coro.save, i1 true)
+ switch i8 %final.suspend.coro.suspend, label %coro.ret [
+ i8 0, label %final.ready
+ i8 1, label %final.cleanup
+ ]
+
+final.cleanup: ; preds = %final.suspend
+ br label %cleanup
+
+final.ready: ; preds = %final.suspend, %coro.final
+ call void @await_resume()
+ br label %cleanup
+
+cleanup: ; preds = %final.ready, %final.cleanup, %await.cleanup, %init.cleanup
+ %cleanup.dest.slot.0 = phi i32 [ 0, %final.ready ], [ 2, %final.cleanup ], [ 2, %await.cleanup ], [ 2, %init.cleanup ]
+ %free.memory = call i8* @llvm.coro.free(token %id, i8* %begin)
+ %free = icmp ne i8* %free.memory, null
+ br i1 %free, label %coro.free, label %after.coro.free
+
+coro.free: ; preds = %cleanup
+ call void @delete(i8* %free.memory)
+ br label %after.coro.free
+
+after.coro.free: ; preds = %coro.free, %cleanup
+ switch i32 %cleanup.dest.slot.0, label %unreachable [
+ i32 0, label %cleanup.cont
+ i32 2, label %coro.ret
+ ]
+
+cleanup.cont: ; preds = %after.coro.free
+ br label %coro.ret
+
+coro.ret: ; preds = %cleanup.cont, %after.coro.free, %final.suspend, %await.suspend, %init.suspend
+ %end = call i1 @llvm.coro.end(i8* null, i1 false)
+ ret void
+
+unreachable: ; preds = %after.coro.free
+ unreachable
+
+}
+
+define void @bar(i32 %a, i64 %c, double %d) !dbg !19 {
+entry:
+ %__promise = alloca %promise_type, align 8
+ %0 = bitcast %promise_type* %__promise to i8*
+ %a.alloc = alloca i32, align 4
+ %c.alloc = alloca i64, align 4
+ %d.alloc = alloca double, align 4
+ store i32 %a, i32* %a.alloc
+ store i64 %c, i64* %c.alloc
+ store double %d, double* %d.alloc
+ %struct.data = alloca %struct.big_structure, align 1
+ call void @produce(%struct.big_structure* %struct.data)
+ %id = call token @llvm.coro.id(i32 16, i8* %0, i8* null, i8* null)
+ %alloc = call i1 @llvm.coro.alloc(token %id)
+ br i1 %alloc, label %coro.alloc, label %coro.init
+
+coro.alloc: ; preds = %entry
+ %size = call i64 @llvm.coro.size.i64()
+ %memory = call i8* @new(i64 %size)
+ br label %coro.init
+
+coro.init: ; preds = %coro.alloc, %entry
+ %phi.entry.alloc = phi i8* [ null, %entry ], [ %memory, %coro.alloc ]
+ %begin = call i8* @llvm.coro.begin(token %id, i8* %phi.entry.alloc)
+ call void @llvm.dbg.declare(metadata %promise_type* %__promise, metadata !21, metadata !DIExpression()), !dbg !22
+ %ready = call i1 @await_ready()
+ br i1 %ready, label %init.ready, label %init.suspend
+
+init.suspend: ; preds = %coro.init
+ %save = call token @llvm.coro.save(i8* null)
+ call void @await_suspend()
+ %suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
+ switch i8 %suspend, label %coro.ret [
+ i8 0, label %init.ready
+ i8 1, label %init.cleanup
+ ]
+
+init.cleanup: ; preds = %init.suspend
+ br label %cleanup
+
+init.ready: ; preds = %init.suspend, %coro.init
+ call void @await_resume()
+ %ready.again = call zeroext i1 @await_ready()
+ br i1 %ready.again, label %await.ready, label %await.suspend
+
+await.suspend: ; preds = %init.ready
+ %save.again = call token @llvm.coro.save(i8* null)
+ %from.address = call i8* @from_address(i8* %begin)
+ call void @await_suspend()
+ %suspend.again = call i8 @llvm.coro.suspend(token %save.again, i1 false)
+ switch i8 %suspend.again, label %coro.ret [
+ i8 0, label %await.ready
+ i8 1, label %await.cleanup
+ ]
+
+await.cleanup: ; preds = %await.suspend
+ br label %cleanup
+
+await.ready: ; preds = %await.suspend, %init.ready
+ call void @await_resume()
+ %i.i = getelementptr inbounds %promise_type, %promise_type* %__promise, i64 0, i32 0
+ store i32 1, i32* %i.i, align 8
+ %j.i = getelementptr inbounds %promise_type, %promise_type* %__promise, i64 0, i32 1
+ store i32 2, i32* %j.i, align 4
+ %k.i = getelementptr inbounds %promise_type, %promise_type* %__promise, i64 0, i32 2
+ store double 3.000000e+00, double* %k.i, align 8
+ call void @consume(%struct.big_structure* %struct.data)
+ call void @pi32(i32* %a.alloc)
+ call void @pi64(i64* %c.alloc)
+ call void @pdouble(double* %d.alloc)
+ call void @return_void()
+ br label %coro.final
+
+coro.final: ; preds = %await.ready
+ call void @final_suspend()
+ %coro.final.await_ready = call i1 @await_ready()
+ br i1 %coro.final.await_ready, label %final.ready, label %final.suspend
+
+final.suspend: ; preds = %coro.final
+ %final.suspend.coro.save = call token @llvm.coro.save(i8* null)
+ %final.suspend.from_address = call i8* @from_address(i8* %begin)
+ call void @await_suspend()
+ %final.suspend.coro.suspend = call i8 @llvm.coro.suspend(token %final.suspend.coro.save, i1 true)
+ switch i8 %final.suspend.coro.suspend, label %coro.ret [
+ i8 0, label %final.ready
+ i8 1, label %final.cleanup
+ ]
+
+final.cleanup: ; preds = %final.suspend
+ br label %cleanup
+
+final.ready: ; preds = %final.suspend, %coro.final
+ call void @await_resume()
+ br label %cleanup
+
+cleanup: ; preds = %final.ready, %final.cleanup, %await.cleanup, %init.cleanup
+ %cleanup.dest.slot.0 = phi i32 [ 0, %final.ready ], [ 2, %final.cleanup ], [ 2, %await.cleanup ], [ 2, %init.cleanup ]
+ %free.memory = call i8* @llvm.coro.free(token %id, i8* %begin)
+ %free = icmp ne i8* %free.memory, null
+ br i1 %free, label %coro.free, label %after.coro.free
+
+coro.free: ; preds = %cleanup
+ call void @delete(i8* %free.memory)
+ br label %after.coro.free
+
+after.coro.free: ; preds = %coro.free, %cleanup
+ switch i32 %cleanup.dest.slot.0, label %unreachable [
+ i32 0, label %cleanup.cont
+ i32 2, label %coro.ret
+ ]
+
+cleanup.cont: ; preds = %after.coro.free
+ br label %coro.ret
+
+coro.ret: ; preds = %cleanup.cont, %after.coro.free, %final.suspend, %await.suspend, %init.suspend
+ %end = call i1 @llvm.coro.end(i8* null, i1 false)
+ ret void
+
+unreachable: ; preds = %after.coro.free
+ unreachable
+
+}
+
+declare void @llvm.dbg.declare(metadata, metadata, metadata)
+declare token @llvm.coro.id(i32, i8* readnone, i8* nocapture readonly, i8*)
+declare i1 @llvm.coro.alloc(token)
+declare i64 @llvm.coro.size.i64()
+declare token @llvm.coro.save(i8*)
+declare i8* @llvm.coro.begin(token, i8* writeonly)
+declare i8 @llvm.coro.suspend(token, i1)
+declare i8* @llvm.coro.free(token, i8* nocapture readonly)
+declare i1 @llvm.coro.end(i8*, i1)
+
+declare i8* @new(i64)
+declare void @delete(i8*)
+declare i1 @await_ready()
+declare void @await_suspend()
+declare void @await_resume()
+declare void @print(i32)
+declare i8* @from_address(i8*)
+declare void @return_void()
+declare void @final_suspend()
+
+!llvm.dbg.cu = !{!0}
+!llvm.linker.options = !{}
+!llvm.module.flags = !{!3, !4}
+!llvm.ident = !{!5}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !2, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "coro-debug.cpp", directory: ".")
+!2 = !{}
+!3 = !{i32 7, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{!"clang version 11.0.0"}
+!6 = !DILocalVariable(name: "__promise", scope: !7, file: !1, line: 24, type: !10)
+!7 = distinct !DILexicalBlock(scope: !8, file: !1, line: 23, column: 12)
+!8 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !8, file: !1, line: 23, type: !9, scopeLine: 23, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+!9 = !DISubroutineType(types: !2)
+!10 = !DIDerivedType(tag: DW_TAG_typedef, name: "promise_type", scope: !8, file: !1, line: 15, baseType: !11)
+!11 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "promise_type", scope: !8, file: !1, line: 10, size: 128, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !12, identifier: "_ZTSN4coro12promise_typeE")
+!12 = !{!13, !14, !15}
+!13 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !8, file: !1, line: 10, baseType: !16, size: 32)
+!14 = !DIDerivedType(tag: DW_TAG_member, name: "j", scope: !8, file: !1, line: 10, baseType: !16, size: 32, offset: 32)
+!15 = !DIDerivedType(tag: DW_TAG_member, name: "k", scope: !8, file: !1, line: 10, baseType: !17, size: 64, offset: 64)
+!16 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!17 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float)
+!18 = !DILocation(line: 8, scope: !7)
+!19 = distinct !DISubprogram(name: "bar", linkageName: "_Z3barv", scope: !19, file: !1, line: 54, type: !9, scopeLine: 54, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+!20 = distinct !DILexicalBlock(scope: !19, file: !1, line: 23, column: 12)
+!21 = !DILocalVariable(name: "__promise", scope: !20, file: !1, line: 55, type: !10)
+!22 = !DILocation(line: 10, scope: !20)
+
+
+
+
+
+