diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp --- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp @@ -30,6 +30,7 @@ #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" @@ -334,6 +335,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(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); @@ -345,6 +368,12 @@ // with their original insertion field index. After the frame is built, their // indexes will be updated into the final layout index. DenseMap FieldIndexMap; + // Map from values to their alignment on the frame. They would be set after + // the frame is built. + DenseMap FieldAlignMap; + // Map from values to their offset on the frame. They would be set after + // the frame is built. + DenseMap FieldOffsetMap; }; } // namespace @@ -496,12 +525,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) @@ -716,6 +753,315 @@ IsFinished = true; } +static void cacheDIVar(FrameDataInfo &FrameData, + DenseMap &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(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(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(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 &DITypeCache) { + if (DIType *DT = DITypeCache.lookup(Ty)) + return DT; + + StringRef Name = solveTypeName(Ty); + + DIType *RetType = nullptr; + + if (Ty->isIntegerTy()) { + auto BitWidth = cast(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(Ty); + SmallVector 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 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 Elements; + DataLayout Layout = F.getParent()->getDataLayout(); + + DenseMap DIVarCache; + cacheDIVar(FrameData, DIVarCache); + + unsigned ResumeIndex = coro::Shape::SwitchFieldIndex::Resume; + unsigned DestroyIndex = coro::Shape::SwitchFieldIndex::Destroy; + unsigned IndexIndex = Shape.SwitchLowering.IndexField; + + DenseMap 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 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> 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 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(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; @@ -791,15 +1137,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: @@ -1100,6 +1449,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 @@ -1126,11 +1484,9 @@ 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(Builder.CreateBitCast(CB, FramePtrTy, "FramePtr")); + Instruction *FramePtr = Shape.FramePtr; DominatorTree DT(*CB->getFunction()); SmallDenseMap DbgPtrAllocaCache; @@ -2293,7 +2649,10 @@ 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) diff --git a/llvm/lib/Transforms/Coroutines/CoroInternal.h b/llvm/lib/Transforms/Coroutines/CoroInternal.h --- a/llvm/lib/Transforms/Coroutines/CoroInternal.h +++ b/llvm/lib/Transforms/Coroutines/CoroInternal.h @@ -133,6 +133,8 @@ AllocaInst *PromiseAlloca; BasicBlock *ResumeEntryBlock; unsigned IndexField; + unsigned IndexAlign; + unsigned IndexOffset; bool HasFinalSuspend; }; diff --git a/llvm/test/Transforms/Coroutines/coro-debug-coro-frame.ll b/llvm/test/Transforms/Coroutines/coro-debug-coro-frame.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Coroutines/coro-debug-coro-frame.ll @@ -0,0 +1,361 @@ +; 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) + + + + + + diff --git a/llvm/test/Transforms/Coroutines/coro-inline.ll b/llvm/test/Transforms/Coroutines/coro-inline.ll --- a/llvm/test/Transforms/Coroutines/coro-inline.ll +++ b/llvm/test/Transforms/Coroutines/coro-inline.ll @@ -4,7 +4,7 @@ ; RUN: opt < %s -sample-profile-file=%S/Inputs/sample.text.prof -pgo-kind=pgo-sample-use-pipeline -passes='sample-profile,cgscc(coro-split)' -disable-inlining=true -S | FileCheck %s ; Function Attrs: alwaysinline ssp uwtable -define void @ff() #0 !dbg !12 { +define void @ff() #0 { entry: %id = call token @llvm.coro.id(i32 16, i8* null, i8* null, i8* null) %begin = call i8* @llvm.coro.begin(token %id, i8* null) @@ -12,11 +12,11 @@ } ; Function Attrs: alwaysinline ssp uwtable -define void @foo() #0 !dbg !8 { +define void @foo() #0 { entry: %id1 = call token @llvm.coro.id(i32 16, i8* null, i8* null, i8* null) %begin = call i8* @llvm.coro.begin(token %id1, i8* null) - call void @ff(), !dbg !11 + call void @ff() ret void } ; CHECK-LABEL: define void @foo() @@ -28,18 +28,10 @@ attributes #0 = { alwaysinline ssp uwtable "coroutine.presplit"="1" "use-sample-profile" } -!llvm.dbg.cu = !{!0} -!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.dbg.cu = !{} +!llvm.module.flags = !{!1, !2, !3, !4} -!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) -!1 = !DIFile(filename: "inline_O2.cpp", directory: "") -!2 = !{} -!3 = !{i32 7, !"Dwarf Version", i32 4} -!4 = !{i32 2, !"Debug Info Version", i32 3} -!5 = !{i32 1, !"wchar_size", i32 4} -!6 = !{i32 7, !"PIC Level", i32 2} -!8 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: !1, file: !1, line: 46, type: !9, scopeLine: 46, flags: DIFlagPrototyped, unit: !0, retainedNodes: !2) -!9 = !DISubroutineType(types: !10) -!10 = !{null} -!11 = !DILocation(line: 2, column: 0, scope: !8) -!12 = distinct !DISubprogram(name: "ff", linkageName: "ff", scope: !1, file: !1, line: 46, type: !9, scopeLine: 46, flags: DIFlagPrototyped, unit: !0, retainedNodes: !2) +!1 = !{i32 7, !"Dwarf Version", i32 4} +!2 = !{i32 2, !"Debug Info Version", i32 3} +!3 = !{i32 1, !"wchar_size", i32 4} +!4 = !{i32 7, !"PIC Level", i32 2} \ No newline at end of file