Index: llvm/lib/Transforms/Coroutines/CoroFrame.cpp =================================================================== --- llvm/lib/Transforms/Coroutines/CoroFrame.cpp +++ llvm/lib/Transforms/Coroutines/CoroFrame.cpp @@ -48,6 +48,12 @@ frame for allocas whose liferanges are not overlapped, for testing purposes"), llvm::cl::init(false)); +static cl::opt EnhanceDebugability( + "enhance-debug-with-coroutine", cl::NotHidden, + cl::desc("Try to salvage as many debug infomation as possible." + "This option may enlarge the size of coroutine frame."), + cl::init(false)); + enum { SmallVectorThreshold = 32 }; // Provides two way mapping between the blocks and numbers. @@ -334,6 +340,28 @@ 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((LayoutIndexUpdateStarted || FieldAlignMap.count(V) == 0)); + FieldAlignMap[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((LayoutIndexUpdateStarted || FieldOffsetMap.count(V) == 0)); + FieldOffsetMap[V] = Offset; + } + // Remap the index of every field in the frame, using the final layout index. void updateLayoutIndex(FrameTypeBuilder &B); @@ -345,6 +373,8 @@ // with their original insertion field index. After the frame is built, their // indexes will be updated into the final layout index. DenseMap FieldIndexMap; + DenseMap FieldAlignMap; + DenseMap FieldOffsetMap; }; } // namespace @@ -490,12 +520,20 @@ 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) @@ -710,6 +748,282 @@ IsFinished = true; } +static void cacheDIVar(FrameDataInfo &FrameData, + DenseMap &DIVarCache) { + for (auto *V : FrameData.getAllDefs()) { + if (DIVarCache.find(V) != DIVarCache.end()) + continue; + + for (auto *DDI : FindDbgDeclareUses(V)) { + if (DDI->getExpression()->getNumElements() != 0) + continue; + + DIVarCache.insert(std::make_pair(V, DDI->getVariable())); + break; + } + } +} + +/// Create name for Type. It uses MDString to store new created string to +/// avoid memory leak. +static StringRef solveTypeName(Type *Ty) { + if (Ty->isIntegerTy()) { + std::string TypeName = + "int" + std::to_string(cast(Ty)->getBitWidth()); + auto *MDName = MDString::get(Ty->getContext(), TypeName.c_str()); + return MDName->getString(); + } + + if (Ty->isFloatingPointTy()) { + if (Ty->isFloatTy()) + return "float"; + else if (Ty->isDoubleTy()) + return "double"; + else + return "floating_type"; + } + + if (Ty->isPointerTy()) { + auto *PtrTy = cast(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.c_str()); + return MDName->getString(); + } + + if (Ty->isStructTy()) { + if (!cast(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.c_str()); + return MDName->getString(); + } + + return "UnknownType"; +} + +static DIType *solveDIType(DIBuilder &Builder, Type *Ty, DataLayout &Layout, + DIScope *Scope, + DenseMap &DITypeCache) { + if (DITypeCache.find(Ty) != DITypeCache.end()) + return DITypeCache[Ty]; + + StringRef Name = solveTypeName(Ty); + + DIType *RetType = nullptr; + + if (Ty->isIntegerTy()) { + auto BitWidth = cast(Ty)->getBitWidth(); + RetType = Builder.createBasicType(Name, BitWidth, dwarf::DW_ATE_signed); + } else if (Ty->isFloatingPointTy()) { + RetType = Builder.createBasicType(Name, Layout.getTypeSizeInBits(Ty), + dwarf::DW_ATE_float); + } 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); + } else if (Ty->isStructTy()) { + auto *DIStruct = Builder.createStructType( + Scope, Name, Scope->getFile(), 1, Layout.getTypeSizeInBits(Ty), + Layout.getPrefTypeAlignment(Ty), llvm::DINode::FlagZero, nullptr, + llvm::DINodeArray()); + + auto *StructTy = cast(Ty); + SmallVector Elements; + for (unsigned i = 0; i < StructTy->getNumElements(); i++) { + DIType *DITy = solveDIType(Builder, StructTy->getElementType(i), Layout, + Scope, DITypeCache); + assert(DITy); + Elements.push_back(Builder.createMemberType( + Scope, DITy->getName(), Scope->getFile(), 1, DITy->getSizeInBits(), + DITy->getAlignInBits(), + Layout.getStructLayout(StructTy)->getElementOffsetInBits(i), + llvm::DINode::FlagZero, DITy)); + } + + Builder.replaceArrays(DIStruct, Builder.getOrCreateArray(Elements)); + + RetType = DIStruct; + } else { + LLVM_DEBUG(dbgs() << "Un resolved Ty: " << *Ty << "\n";); + RetType = Builder.createBasicType( + Name.str() + "_" + std::to_string(Layout.getTypeSizeInBits(Ty)), + Layout.getTypeSizeInBits(Ty), dwarf::DW_ATE_address); + } + + DITypeCache[Ty] = RetType; + return RetType; +} + +/// 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.decalre 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) { + DIBuilder DBuilder(*F.getParent(), /*AllowUnresolved*/ false); + + AllocaInst *PromiseAlloca = Shape.getPromiseAlloca(); + TinyPtrVector 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::FlagZero, nullptr, + llvm::DINodeArray()); + StructType *FrameTy = Shape.FrameTy; + SmallVector Elements; + DataLayout Layout = F.getParent()->getDataLayout(); + + DenseMap DIVarCache; + cacheDIVar(FrameData, DIVarCache); + + unsigned ResumeIndex = coro::Shape::SwitchFieldIndex::Resume, + DestroyIndex = coro::Shape::SwitchFieldIndex::Destroy, + IndexIndex = Shape.SwitchLowering.IndexField; + + DenseMap NameCache; + NameCache.insert(std::make_pair(ResumeIndex, "__resume_fn")); + NameCache.insert(std::make_pair(DestroyIndex, "__destroy_fn")); + NameCache.insert(std::make_pair(IndexIndex, "__coro_index")); + + Type *ResumeFnTy = FrameTy->getElementType(ResumeIndex), + *DestroyFnTy = FrameTy->getElementType(DestroyIndex), + *IndexTy = FrameTy->getElementType(IndexIndex); + + DenseMap TyCache; + TyCache.insert(std::make_pair( + ResumeIndex, DBuilder.createBasicType( + "__resume_fn", Layout.getTypeSizeInBits(ResumeFnTy), + dwarf::DW_ATE_address))); + TyCache.insert(std::make_pair( + DestroyIndex, DBuilder.createBasicType( + "__destroy_fn", Layout.getTypeSizeInBits(DestroyFnTy), + dwarf::DW_ATE_address))); + TyCache.insert(std::make_pair( + 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(std::make_pair(Index, DIVarCache[V]->getName())); + TyCache.insert(std::make_pair(Index, DIVarCache[V]->getType())); + } + + // Cache from index to (Align, Offset Pair) + DenseMap> OffsetCache; + // The Align and Offset of Resume function and Destroy function are fixed. + OffsetCache[ResumeIndex] = std::make_pair(8, 0); + OffsetCache[DestroyIndex] = std::make_pair(8, 8); + OffsetCache[IndexIndex] = std::make_pair(Shape.SwitchLowering.IndexAlign, + Shape.SwitchLowering.IndexOffset); + for (auto *V : FrameData.getAllDefs()) { + auto Index = FrameData.getFieldIndex(V); + + OffsetCache[Index] = + std::make_pair(FrameData.getAlign(V), FrameData.getOffset(V)); + } + + DenseMap 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, 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::FlagZero, DITy)); + } + + DBuilder.replaceArrays(FrameDITy, DBuilder.getOrCreateArray(Elements)); + + auto *FrameDIVar = + DBuilder.createAutoVariable(PromiseDIScope, "__coro_frame", DFile, + LineNum, FrameDITy, true, DINode::FlagZero); + 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 (auto *SubProgram = dyn_cast(PromiseDIScope)) { + auto RetainedNodes = SubProgram->getRetainedNodes(); + SmallVector 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; @@ -785,15 +1099,18 @@ 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: @@ -1094,6 +1411,15 @@ 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(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 @@ -1122,9 +1448,7 @@ LLVMContext &C = CB->getContext(); IRBuilder<> Builder(CB->getNextNode()); StructType *FrameTy = Shape.FrameTy; - PointerType *FramePtrTy = FrameTy->getPointerTo(); - auto *FramePtr = - cast(Builder.CreateBitCast(CB, FramePtrTy, "FramePtr")); + Instruction *FramePtr = Shape.FramePtr; DominatorTree DT(*CB->getFunction()); SmallDenseMap DbgPtrAllocaCache; @@ -2189,28 +2513,31 @@ // is available throughout the function when producing unoptimized // code. Extending the lifetime this way is correct because the // variable has been declared by a dbg.declare intrinsic. - if (auto Arg = dyn_cast_or_null(Storage)) { - auto &Cached = DbgPtrAllocaCache[Storage]; - if (!Cached) { - Cached = Builder.CreateAlloca(Storage->getType(), 0, nullptr, - Arg->getName() + ".debug"); - Builder.CreateStore(Storage, Cached); + if (!EnhanceDebugability) + if (auto Arg = dyn_cast_or_null(Storage)) { + auto &Cached = DbgPtrAllocaCache[Storage]; + if (!Cached) { + Cached = Builder.CreateAlloca(Storage->getType(), 0, nullptr, + Arg->getName() + ".debug"); + Builder.CreateStore(Storage, Cached); + } + Storage = Cached; + // FIXME: LLVM lacks nuanced semantics to differentiate between + // memory and direct locations at the IR level. The backend will + // turn a dbg.declare(alloca, ..., DIExpression()) into a memory + // location. Thus, if there are deref and offset operations in the + // expression, we need to add a DW_OP_deref at the *start* of the + // expression to first load the contents of the alloca before + // adjusting it with the expression. + if (Expr && Expr->isComplex()) + Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore); } - Storage = Cached; - // FIXME: LLVM lacks nuanced semantics to differentiate between - // memory and direct locations at the IR level. The backend will - // turn a dbg.declare(alloca, ..., DIExpression()) into a memory - // location. Thus, if there are deref and offset operations in the - // expression, we need to add a DW_OP_deref at the *start* of the - // expression to first load the contents of the alloca before - // adjusting it with the expression. - if (Expr && Expr->isComplex()) - Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore); - } DDI->replaceVariableLocationOp(OriginalStorage, Storage); DDI->setExpression(Expr); if (auto *InsertPt = dyn_cast_or_null(Storage)) DDI->moveAfter(InsertPt); + else if (dyn_cast_or_null(Storage)) + DDI->moveAfter(F->getEntryBlock().getFirstNonPHI()); } void coro::buildCoroutineFrame(Function &F, Shape &Shape) { @@ -2352,7 +2679,10 @@ Shape.ABI == coro::ABI::Async) sinkSpillUsesAfterCoroBegin(F, FrameData, Shape.CoroBegin); Shape.FrameTy = buildFrameType(F, Shape, FrameData); - Shape.FramePtr = insertSpills(FrameData, Shape); + createFramePtr(Shape); + if (EnhanceDebugability) + buildFrameDebugInfo(F, Shape, FrameData); + insertSpills(FrameData, Shape); lowerLocalAllocas(LocalAllocas, DeadInstructions); for (auto I : DeadInstructions) Index: llvm/lib/Transforms/Coroutines/CoroInternal.h =================================================================== --- llvm/lib/Transforms/Coroutines/CoroInternal.h +++ llvm/lib/Transforms/Coroutines/CoroInternal.h @@ -132,6 +132,8 @@ AllocaInst *PromiseAlloca; BasicBlock *ResumeEntryBlock; unsigned IndexField; + unsigned IndexAlign; + unsigned IndexOffset; bool HasFinalSuspend; }; Index: llvm/test/Transforms/Coroutines/coro-debug-coro-frame.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Coroutines/coro-debug-coro-frame.ll @@ -0,0 +1,187 @@ +; RUN: opt < %s -enhance-debug-with-coroutine -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: ![[CORO_FRAME]] = !DILocalVariable(name: "__coro_frame", scope: !{{[0-9]+}}, file: ![[FILE]], line: {{[0-9]+}}, type: ![[FRAME_TYPE:[0-9]+]]) +; 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]+]], ![[CORO_INDEX:[0-9]+]] +; CHECK: ![[RESUME_FN]] = !DIDerivedType(tag: DW_TAG_member, name: "__resume_fn" +; CHECK: ![[DESTROY_FN]] = !DIDerivedType(tag: DW_TAG_member, name: "__destroy_fn" +; 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: ![[CORO_INDEX]] = !DIDerivedType(tag: DW_TAG_member, name: "__coro_index" +; CHECK: ![[CORO_FRAME_IN_RESUME]] = !DILocalVariable(name: "__coro_frame",{{.*}}type: ![[FRAME_TYPE]] +%promise_type = type { i32, i32, double } + +define void @f() !dbg !8 { +entry: + %__promise = alloca %promise_type, align 8 + %0 = bitcast %promise_type* %__promise to i8* + %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 @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: 0, scope: !7) + + + + + +