diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -2732,9 +2732,17 @@ QualType QTy(Ty, 0); auto SizeExpr = SizeExprCache.find(QTy); if (SizeExpr != SizeExprCache.end()) - Subscript = DBuilder.getOrCreateSubrange(0, SizeExpr->getSecond()); - else - Subscript = DBuilder.getOrCreateSubrange(0, Count ? Count : -1); + Subscript = DBuilder.getOrCreateSubrange( + SizeExpr->getSecond() /*count*/, nullptr /*lowerBound*/, + nullptr /*upperBound*/, nullptr /*stride*/); + else { + auto *CountNode = + llvm::ConstantAsMetadata::get(llvm::ConstantInt::getSigned( + llvm::Type::getInt64Ty(CGM.getLLVMContext()), Count ? Count : -1)); + Subscript = DBuilder.getOrCreateSubrange( + CountNode /*count*/, nullptr /*lowerBound*/, nullptr /*upperBound*/, + nullptr /*stride*/); + } llvm::DINodeArray SubscriptArray = DBuilder.getOrCreateArray(Subscript); uint64_t Size = CGM.getContext().getTypeSize(Ty); @@ -2754,8 +2762,18 @@ // Create ranges for both dimensions. llvm::SmallVector Subscripts; - Subscripts.push_back(DBuilder.getOrCreateSubrange(0, Ty->getNumColumns())); - Subscripts.push_back(DBuilder.getOrCreateSubrange(0, Ty->getNumRows())); + auto *ColumnCountNode = + llvm::ConstantAsMetadata::get(llvm::ConstantInt::getSigned( + llvm::Type::getInt64Ty(CGM.getLLVMContext()), Ty->getNumColumns())); + auto *RowCountNode = + llvm::ConstantAsMetadata::get(llvm::ConstantInt::getSigned( + llvm::Type::getInt64Ty(CGM.getLLVMContext()), Ty->getNumRows())); + Subscripts.push_back(DBuilder.getOrCreateSubrange( + ColumnCountNode /*count*/, nullptr /*lowerBound*/, nullptr /*upperBound*/, + nullptr /*stride*/)); + Subscripts.push_back(DBuilder.getOrCreateSubrange( + RowCountNode /*count*/, nullptr /*lowerBound*/, nullptr /*upperBound*/, + nullptr /*stride*/)); llvm::DINodeArray SubscriptArray = DBuilder.getOrCreateArray(Subscripts); return DBuilder.createArrayType(Size, Align, ElementTy, SubscriptArray); } @@ -2810,10 +2828,17 @@ auto SizeNode = SizeExprCache.find(EltTy); if (SizeNode != SizeExprCache.end()) - Subscripts.push_back( - DBuilder.getOrCreateSubrange(0, SizeNode->getSecond())); - else - Subscripts.push_back(DBuilder.getOrCreateSubrange(0, Count)); + Subscripts.push_back(DBuilder.getOrCreateSubrange( + SizeNode->getSecond() /*count*/, nullptr /*lowerBound*/, + nullptr /*upperBound*/, nullptr /*stride*/)); + else { + auto *CountNode = + llvm::ConstantAsMetadata::get(llvm::ConstantInt::getSigned( + llvm::Type::getInt64Ty(CGM.getLLVMContext()), Count)); + Subscripts.push_back(DBuilder.getOrCreateSubrange( + CountNode /*count*/, nullptr /*lowerBound*/, nullptr /*upperBound*/, + nullptr /*stride*/)); + } EltTy = Ty->getElementType(); } diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h --- a/llvm/include/llvm/IR/DIBuilder.h +++ b/llvm/include/llvm/IR/DIBuilder.h @@ -573,6 +573,8 @@ /// implicitly uniques the values returned. DISubrange *getOrCreateSubrange(int64_t Lo, int64_t Count); DISubrange *getOrCreateSubrange(int64_t Lo, Metadata *CountNode); + DISubrange *getOrCreateSubrange(Metadata *Count, Metadata *LowerBound, + Metadata *UpperBound, Metadata *Stride); /// Create a new descriptor for the specified variable. /// \param Context Variable scope. diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h --- a/llvm/include/llvm/IR/DebugInfoMetadata.h +++ b/llvm/include/llvm/IR/DebugInfoMetadata.h @@ -287,12 +287,8 @@ friend class LLVMContextImpl; friend class MDNode; - int64_t LowerBound; - - DISubrange(LLVMContext &C, StorageType Storage, Metadata *Node, - int64_t LowerBound, ArrayRef Ops) - : DINode(C, DISubrangeKind, Storage, dwarf::DW_TAG_subrange_type, Ops), - LowerBound(LowerBound) {} + DISubrange(LLVMContext &C, StorageType Storage, ArrayRef Ops) + : DINode(C, DISubrangeKind, Storage, dwarf::DW_TAG_subrange_type, Ops) {} ~DISubrange() = default; @@ -304,8 +300,14 @@ int64_t LowerBound, StorageType Storage, bool ShouldCreate = true); + static DISubrange *getImpl(LLVMContext &Context, Metadata *CountNode, + Metadata *LowerBound, Metadata *UpperBound, + Metadata *Stride, StorageType Storage, + bool ShouldCreate = true); + TempDISubrange cloneImpl() const { - return getTemporary(getContext(), getRawCountNode(), getLowerBound()); + return getTemporary(getContext(), getRawCountNode(), getRawLowerBound(), + getRawUpperBound(), getRawStride()); } public: @@ -315,25 +317,33 @@ DEFINE_MDNODE_GET(DISubrange, (Metadata *CountNode, int64_t LowerBound = 0), (CountNode, LowerBound)) - TempDISubrange clone() const { return cloneImpl(); } + DEFINE_MDNODE_GET(DISubrange, + (Metadata * CountNode, Metadata *LowerBound, + Metadata *UpperBound, Metadata *Stride), + (CountNode, LowerBound, UpperBound, Stride)) - int64_t getLowerBound() const { return LowerBound; } + TempDISubrange clone() const { return cloneImpl(); } Metadata *getRawCountNode() const { return getOperand(0).get(); } + Metadata *getRawLowerBound() const { return getOperand(1).get(); } + + Metadata *getRawUpperBound() const { return getOperand(2).get(); } + + Metadata *getRawStride() const { return getOperand(3).get(); } + typedef PointerUnion CountType; + typedef PointerUnion BoundType; - CountType getCount() const { - if (auto *MD = dyn_cast(getRawCountNode())) - return CountType(cast(MD->getValue())); + CountType getCount() const; - if (auto *DV = dyn_cast(getRawCountNode())) - return CountType(DV); + BoundType getLowerBound() const; - return CountType(); - } + BoundType getUpperBound() const; + + BoundType getStride() const; static bool classof(const Metadata *MD) { return MD->getMetadataID() == DISubrangeKind; diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -4500,21 +4500,41 @@ /// ParseDISubrange: /// ::= !DISubrange(count: 30, lowerBound: 2) /// ::= !DISubrange(count: !node, lowerBound: 2) +/// ::= !DISubrange(lowerBound: !node1, upperBound: !node2, stride: !node3) bool LLParser::ParseDISubrange(MDNode *&Result, bool IsDistinct) { #define VISIT_MD_FIELDS(OPTIONAL, REQUIRED) \ - REQUIRED(count, MDSignedOrMDField, (-1, -1, INT64_MAX, false)); \ - OPTIONAL(lowerBound, MDSignedField, ); + OPTIONAL(count, MDSignedOrMDField, (-1, -1, INT64_MAX, false)); \ + OPTIONAL(lowerBound, MDSignedOrMDField, ); \ + OPTIONAL(upperBound, MDSignedOrMDField, ); \ + OPTIONAL(stride, MDSignedOrMDField, ); PARSE_MD_FIELDS(); #undef VISIT_MD_FIELDS + Metadata *Count = nullptr; + Metadata *LowerBound = nullptr; + Metadata *UpperBound = nullptr; + Metadata *Stride = nullptr; if (count.isMDSignedField()) - Result = GET_OR_DISTINCT( - DISubrange, (Context, count.getMDSignedValue(), lowerBound.Val)); + Count = ConstantAsMetadata::get(ConstantInt::getSigned( + Type::getInt64Ty(Context), count.getMDSignedValue())); else if (count.isMDField()) - Result = GET_OR_DISTINCT( - DISubrange, (Context, count.getMDFieldValue(), lowerBound.Val)); - else - return true; + Count = count.getMDFieldValue(); + + auto convToMetadata = [&](MDSignedOrMDField Bound) -> Metadata * { + if (Bound.isMDSignedField()) + return ConstantAsMetadata::get(ConstantInt::getSigned( + Type::getInt64Ty(Context), Bound.getMDSignedValue())); + if (Bound.isMDField()) + return Bound.getMDFieldValue(); + return nullptr; + }; + + LowerBound = convToMetadata(lowerBound); + UpperBound = convToMetadata(upperBound); + Stride = convToMetadata(stride); + + Result = GET_OR_DISTINCT(DISubrange, + (Context, Count, LowerBound, UpperBound, Stride)); return false; } diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp --- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp +++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp @@ -1258,14 +1258,24 @@ // Operand 'count' is interpreted as: // - Signed integer (version 0) // - Metadata node (version 1) + // Operand 'lowerBound' is interpreted as: + // - Signed integer (version 0 and 1) + // - Metadata node (version 2) + // Operands 'upperBound' and 'stride' are interpreted as: + // - Metadata node (version 2) switch (Record[0] >> 1) { case 0: Val = GET_OR_DISTINCT(DISubrange, - (Context, Record[1], unrotateSign(Record.back()))); + (Context, Record[1], unrotateSign(Record[2]))); break; case 1: Val = GET_OR_DISTINCT(DISubrange, (Context, getMDOrNull(Record[1]), - unrotateSign(Record.back()))); + unrotateSign(Record[2]))); + break; + case 2: + Val = GET_OR_DISTINCT( + DISubrange, (Context, getMDOrNull(Record[1]), getMDOrNull(Record[2]), + getMDOrNull(Record[3]), getMDOrNull(Record[4]))); break; default: return error("Invalid record: Unsupported version of DISubrange"); diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -1527,10 +1527,12 @@ void ModuleBitcodeWriter::writeDISubrange(const DISubrange *N, SmallVectorImpl &Record, unsigned Abbrev) { - const uint64_t Version = 1 << 1; + const uint64_t Version = 2 << 1; Record.push_back((uint64_t)N->isDistinct() | Version); Record.push_back(VE.getMetadataOrNullID(N->getRawCountNode())); - Record.push_back(rotateSign(N->getLowerBound())); + Record.push_back(VE.getMetadataOrNullID(N->getRawLowerBound())); + Record.push_back(VE.getMetadataOrNullID(N->getRawUpperBound())); + Record.push_back(VE.getMetadataOrNullID(N->getRawStride())); Stream.EmitRecord(bitc::METADATA_SUBRANGE, Record, Abbrev); Record.clear(); diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp --- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -1592,7 +1592,7 @@ assert(Element->getTag() == dwarf::DW_TAG_subrange_type); const DISubrange *Subrange = cast(Element); - assert(Subrange->getLowerBound() == 0 && + assert(!Subrange->getRawLowerBound() && "codeview doesn't support subranges with lower bounds"); int64_t Count = -1; if (auto *CI = Subrange->getCount().dyn_cast()) diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -768,9 +768,18 @@ Result.push_back(DLVar); for (auto *El : Array->getElements()) { if (auto *Subrange = dyn_cast(El)) { - auto Count = Subrange->getCount(); - if (auto *Dependency = Count.dyn_cast()) - Result.push_back(Dependency); + if (auto Count = Subrange->getCount()) + if (auto *Dependency = Count.dyn_cast()) + Result.push_back(Dependency); + if (auto LB = Subrange->getLowerBound()) + if (auto *Dependency = LB.dyn_cast()) + Result.push_back(Dependency); + if (auto UB = Subrange->getUpperBound()) + if (auto *Dependency = UB.dyn_cast()) + Result.push_back(Dependency); + if (auto ST = Subrange->getStride()) + if (auto *Dependency = ST.dyn_cast()) + Result.push_back(Dependency); } } return Result; diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -1349,20 +1349,40 @@ // C/C++. The Count value is the number of elements. Values are 64 bit. If // Count == -1 then the array is unbounded and we do not emit // DW_AT_lower_bound and DW_AT_count attributes. - int64_t LowerBound = SR->getLowerBound(); int64_t DefaultLowerBound = getDefaultLowerBound(); int64_t Count = -1; if (auto *CI = SR->getCount().dyn_cast()) Count = CI->getSExtValue(); - if (DefaultLowerBound == -1 || LowerBound != DefaultLowerBound) - addUInt(DW_Subrange, dwarf::DW_AT_lower_bound, None, LowerBound); + auto addBoundTypeEntry = [&](dwarf::Attribute Attr, + DISubrange::BoundType Bound) -> void { + if (auto *BV = Bound.dyn_cast()) { + if (auto *VarDIE = getDIE(BV)) + addDIEEntry(DW_Subrange, Attr, *VarDIE); + } else if (auto *BE = Bound.dyn_cast()) { + DIELoc *Loc = new (DIEValueAllocator) DIELoc; + DIEDwarfExpression DwarfExpr(*Asm, getCU(), *Loc); + DwarfExpr.setMemoryLocationKind(); + DwarfExpr.addExpression(BE); + addBlock(DW_Subrange, Attr, DwarfExpr.finalize()); + } else if (auto *BI = Bound.dyn_cast()) { + if (Attr != dwarf::DW_AT_lower_bound || DefaultLowerBound == -1 || + BI->getSExtValue() != DefaultLowerBound) + addSInt(DW_Subrange, Attr, dwarf::DW_FORM_sdata, BI->getSExtValue()); + } + }; + + addBoundTypeEntry(dwarf::DW_AT_lower_bound, SR->getLowerBound()); if (auto *CV = SR->getCount().dyn_cast()) { if (auto *CountVarDIE = getDIE(CV)) addDIEEntry(DW_Subrange, dwarf::DW_AT_count, *CountVarDIE); } else if (Count != -1) addUInt(DW_Subrange, dwarf::DW_AT_count, None, Count); + + addBoundTypeEntry(dwarf::DW_AT_upper_bound, SR->getUpperBound()); + + addBoundTypeEntry(dwarf::DW_AT_byte_stride, SR->getStride()); } DIE *DwarfUnit::getIndexTyDie() { diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -1858,9 +1858,34 @@ if (auto *CE = N->getCount().dyn_cast()) Printer.printInt("count", CE->getSExtValue(), /* ShouldSkipZero */ false); else - Printer.printMetadata("count", N->getCount().dyn_cast(), - /*ShouldSkipNull */ false); - Printer.printInt("lowerBound", N->getLowerBound()); + Printer.printMetadata("count", N->getCount().dyn_cast(), + /*ShouldSkipNull */ true); + + // A lowerBound of constant 0 should not be skipped, since it is different + // from an unspecified lower bound (= nullptr). + auto *LBound = N->getRawLowerBound(); + if (auto *LE = dyn_cast_or_null(LBound)) { + auto *LV = cast(LE->getValue()); + Printer.printInt("lowerBound", LV->getSExtValue(), + /* ShouldSkipZero */ false); + } else + Printer.printMetadata("lowerBound", LBound, /*ShouldSkipNull */ true); + + auto *UBound = N->getRawUpperBound(); + if (auto *UE = dyn_cast_or_null(UBound)) { + auto *UV = cast(UE->getValue()); + Printer.printInt("upperBound", UV->getSExtValue(), + /* ShouldSkipZero */ false); + } else + Printer.printMetadata("upperBound", UBound, /*ShouldSkipNull */ true); + + auto *Stride = N->getRawStride(); + if (auto *SE = dyn_cast_or_null(Stride)) { + auto *SV = cast(SE->getValue()); + Printer.printInt("stride", SV->getSExtValue(), /* ShouldSkipZero */ false); + } else + Printer.printMetadata("stride", Stride, /*ShouldSkipNull */ true); + Out << ")"; } diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp --- a/llvm/lib/IR/DIBuilder.cpp +++ b/llvm/lib/IR/DIBuilder.cpp @@ -625,11 +625,22 @@ } DISubrange *DIBuilder::getOrCreateSubrange(int64_t Lo, int64_t Count) { - return DISubrange::get(VMContext, Count, Lo); + auto *LB = ConstantAsMetadata::get( + ConstantInt::getSigned(Type::getInt64Ty(VMContext), Lo)); + auto *CountNode = ConstantAsMetadata::get( + ConstantInt::getSigned(Type::getInt64Ty(VMContext), Count)); + return DISubrange::get(VMContext, CountNode, LB, nullptr, nullptr); } DISubrange *DIBuilder::getOrCreateSubrange(int64_t Lo, Metadata *CountNode) { - return DISubrange::get(VMContext, CountNode, Lo); + auto *LB = ConstantAsMetadata::get( + ConstantInt::getSigned(Type::getInt64Ty(VMContext), Lo)); + return DISubrange::get(VMContext, CountNode, LB, nullptr, nullptr); +} + +DISubrange *DIBuilder::getOrCreateSubrange(Metadata *CountNode, Metadata *LB, + Metadata *UB, Metadata *Stride) { + return DISubrange::get(VMContext, CountNode, LB, UB, Stride); } static void checkGlobalVariableScope(DIScope *Context) { diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp --- a/llvm/lib/IR/DebugInfoMetadata.cpp +++ b/llvm/lib/IR/DebugInfoMetadata.cpp @@ -336,15 +336,103 @@ StorageType Storage, bool ShouldCreate) { auto *CountNode = ConstantAsMetadata::get( ConstantInt::getSigned(Type::getInt64Ty(Context), Count)); - return getImpl(Context, CountNode, Lo, Storage, ShouldCreate); + auto *LB = ConstantAsMetadata::get( + ConstantInt::getSigned(Type::getInt64Ty(Context), Lo)); + return getImpl(Context, CountNode, LB, nullptr, nullptr, Storage, + ShouldCreate); } DISubrange *DISubrange::getImpl(LLVMContext &Context, Metadata *CountNode, int64_t Lo, StorageType Storage, bool ShouldCreate) { - DEFINE_GETIMPL_LOOKUP(DISubrange, (CountNode, Lo)); - Metadata *Ops[] = { CountNode }; - DEFINE_GETIMPL_STORE(DISubrange, (CountNode, Lo), Ops); + auto *LB = ConstantAsMetadata::get( + ConstantInt::getSigned(Type::getInt64Ty(Context), Lo)); + return getImpl(Context, CountNode, LB, nullptr, nullptr, Storage, + ShouldCreate); +} + +DISubrange *DISubrange::getImpl(LLVMContext &Context, Metadata *CountNode, + Metadata *LB, Metadata *UB, Metadata *Stride, + StorageType Storage, bool ShouldCreate) { + DEFINE_GETIMPL_LOOKUP(DISubrange, (CountNode, LB, UB, Stride)); + Metadata *Ops[] = {CountNode, LB, UB, Stride}; + DEFINE_GETIMPL_STORE_NO_CONSTRUCTOR_ARGS(DISubrange, Ops); +} + +DISubrange::CountType DISubrange::getCount() const { + if (!getRawCountNode()) + return CountType(); + + if (auto *MD = dyn_cast(getRawCountNode())) + return CountType(cast(MD->getValue())); + + if (auto *DV = dyn_cast(getRawCountNode())) + return CountType(DV); + + return CountType(); +} + +DISubrange::BoundType DISubrange::getLowerBound() const { + Metadata *LB = getRawLowerBound(); + if (!LB) + return BoundType(); + + assert((isa(LB) || isa(LB) || + isa(LB)) && + "LowerBound must be signed constant or DIVariable or DIExpression"); + + if (auto *MD = dyn_cast(LB)) + return BoundType(cast(MD->getValue())); + + if (auto *MD = dyn_cast(LB)) + return BoundType(MD); + + if (auto *MD = dyn_cast(LB)) + return BoundType(MD); + + return BoundType(); +} + +DISubrange::BoundType DISubrange::getUpperBound() const { + Metadata *UB = getRawUpperBound(); + if (!UB) + return BoundType(); + + assert((isa(UB) || isa(UB) || + isa(UB)) && + "UpperBound must be signed constant or DIVariable or DIExpression"); + + if (auto *MD = dyn_cast(UB)) + return BoundType(cast(MD->getValue())); + + if (auto *MD = dyn_cast(UB)) + return BoundType(MD); + + if (auto *MD = dyn_cast(UB)) + return BoundType(MD); + + return BoundType(); +} + +DISubrange::BoundType DISubrange::getStride() const { + Metadata *ST = getRawStride(); + if (!ST) + return BoundType(); + + assert((isa(ST) || isa(ST) || + isa(ST)) && + "Stride must be signed constant or DIVariable or DIExpression"); + + if (auto *MD = dyn_cast(ST)) + return BoundType(cast(MD->getValue())); + + if (auto *MD = dyn_cast(ST)) + return BoundType(MD); + + if (auto *MD = dyn_cast(ST)) + return BoundType(MD); + + return BoundType(); } DIEnumerator *DIEnumerator::getImpl(LLVMContext &Context, APInt Value, diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h --- a/llvm/lib/IR/LLVMContextImpl.h +++ b/llvm/lib/IR/LLVMContextImpl.h @@ -323,32 +323,46 @@ template <> struct MDNodeKeyImpl { Metadata *CountNode; - int64_t LowerBound; - - MDNodeKeyImpl(Metadata *CountNode, int64_t LowerBound) - : CountNode(CountNode), LowerBound(LowerBound) {} + Metadata *LowerBound; + Metadata *UpperBound; + Metadata *Stride; + + MDNodeKeyImpl(Metadata *CountNode, Metadata *LowerBound, Metadata *UpperBound, + Metadata *Stride) + : CountNode(CountNode), LowerBound(LowerBound), UpperBound(UpperBound), + Stride(Stride) {} MDNodeKeyImpl(const DISubrange *N) - : CountNode(N->getRawCountNode()), - LowerBound(N->getLowerBound()) {} + : CountNode(N->getRawCountNode()), LowerBound(N->getRawLowerBound()), + UpperBound(N->getRawUpperBound()), Stride(N->getRawStride()) {} bool isKeyOf(const DISubrange *RHS) const { - if (LowerBound != RHS->getLowerBound()) - return false; - - if (auto *RHSCount = RHS->getCount().dyn_cast()) - if (auto *MD = dyn_cast(CountNode)) - if (RHSCount->getSExtValue() == - cast(MD->getValue())->getSExtValue()) + auto BoundsEqual = [=](Metadata *Node1, Metadata *Node2) -> bool { + if (Node1 == Node2) + return true; + + ConstantAsMetadata *MD1 = dyn_cast_or_null(Node1); + ConstantAsMetadata *MD2 = dyn_cast_or_null(Node2); + if (MD1 && MD2) { + ConstantInt *CV1 = cast(MD1->getValue()); + ConstantInt *CV2 = cast(MD2->getValue()); + if (CV1->getSExtValue() == CV2->getSExtValue()) return true; + } + return false; + }; - return CountNode == RHS->getRawCountNode(); + return BoundsEqual(CountNode, RHS->getRawCountNode()) && + BoundsEqual(LowerBound, RHS->getRawLowerBound()) && + BoundsEqual(UpperBound, RHS->getRawUpperBound()) && + BoundsEqual(Stride, RHS->getRawStride()); } unsigned getHashValue() const { - if (auto *MD = dyn_cast(CountNode)) - return hash_combine(cast(MD->getValue())->getSExtValue(), - LowerBound); - return hash_combine(CountNode, LowerBound); + if (CountNode) + if (auto *MD = dyn_cast(CountNode)) + return hash_combine(cast(MD->getValue())->getSExtValue(), + LowerBound, UpperBound, Stride); + return hash_combine(CountNode, LowerBound, UpperBound, Stride); } }; diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -893,12 +893,30 @@ void Verifier::visitDISubrange(const DISubrange &N) { AssertDI(N.getTag() == dwarf::DW_TAG_subrange_type, "invalid tag", &N); + AssertDI(N.getRawCountNode() || N.getRawUpperBound(), + "Subrange must contain count or upperBound", &N); + AssertDI(!N.getRawCountNode() || !N.getRawUpperBound(), + "Subrange can have any one of count or upperBound", &N); + AssertDI(!N.getRawCountNode() || N.getCount(), + "Count must either be a signed constant or a DIVariable", &N); auto Count = N.getCount(); - AssertDI(Count, "Count must either be a signed constant or a DIVariable", - &N); - AssertDI(!Count.is() || - Count.get()->getSExtValue() >= -1, + AssertDI(!Count || !Count.is() || + Count.get()->getSExtValue() >= -1, "invalid subrange count", &N); + auto *LBound = N.getRawLowerBound(); + AssertDI(!LBound || isa(LBound) || + isa(LBound) || isa(LBound), + "LowerBound must be signed constant or DIVariable or DIExpression", + &N); + auto *UBound = N.getRawUpperBound(); + AssertDI(!UBound || isa(UBound) || + isa(UBound) || isa(UBound), + "UpperBound must be signed constant or DIVariable or DIExpression", + &N); + auto *Stride = N.getRawStride(); + AssertDI(!Stride || isa(Stride) || + isa(Stride) || isa(Stride), + "Stride must be signed constant or DIVariable or DIExpression", &N); } void Verifier::visitDIEnumerator(const DIEnumerator &N) { diff --git a/llvm/test/Assembler/debug-info.ll b/llvm/test/Assembler/debug-info.ll --- a/llvm/test/Assembler/debug-info.ll +++ b/llvm/test/Assembler/debug-info.ll @@ -4,10 +4,10 @@ ; CHECK: !named = !{!0, !0, !1, !2, !3, !4, !5, !6, !7, !8, !8, !9, !10, !11, !12, !13, !14, !15, !16, !17, !18, !19, !20, !21, !22, !23, !24, !25, !26, !27, !27, !28, !29, !30, !31, !32, !33, !34, !35, !36, !37, !38, !39} !named = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10, !11, !12, !13, !14, !15, !16, !17, !18, !19, !20, !21, !22, !23, !24, !25, !26, !27, !28, !29, !30, !31, !32, !33, !34, !35, !36, !37, !38, !39, !40, !41, !42} -; CHECK: !0 = !DISubrange(count: 3) +; CHECK: !0 = !DISubrange(count: 3, lowerBound: 0) ; CHECK-NEXT: !1 = !DISubrange(count: 3, lowerBound: 4) ; CHECK-NEXT: !2 = !DISubrange(count: 3, lowerBound: -5) -!0 = !DISubrange(count: 3) +!0 = !DISubrange(count: 3, lowerBound: 0) !1 = !DISubrange(count: 3, lowerBound: 0) !2 = !DISubrange(count: 3, lowerBound: 4) diff --git a/llvm/test/Assembler/disubrange-empty-array.ll b/llvm/test/Assembler/disubrange-empty-array.ll --- a/llvm/test/Assembler/disubrange-empty-array.ll +++ b/llvm/test/Assembler/disubrange-empty-array.ll @@ -4,10 +4,10 @@ ; CHECK: !named = !{!0, !0, !1, !2} !named = !{!0, !1, !2, !3} -; CHECK: !0 = !DISubrange(count: -1) +; CHECK: !0 = !DISubrange(count: -1, lowerBound: 0) ; CHECK-NEXT: !1 = !DISubrange(count: -1, lowerBound: 4) ; CHECK-NEXT: !2 = !DISubrange(count: -1, lowerBound: -5) -!0 = !DISubrange(count: -1) +!0 = !DISubrange(count: -1, lowerBound: 0) !1 = !DISubrange(count: -1, lowerBound: 0) !2 = !DISubrange(count: -1, lowerBound: 4) diff --git a/llvm/test/Assembler/invalid-disubrange-count-missing.ll b/llvm/test/Assembler/invalid-disubrange-count-missing.ll --- a/llvm/test/Assembler/invalid-disubrange-count-missing.ll +++ b/llvm/test/Assembler/invalid-disubrange-count-missing.ll @@ -1,4 +1,5 @@ ; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s -; CHECK: [[@LINE+1]]:32: error: missing required field 'count' +!named = !{!0} +; CHECK: Subrange must contain count or upperBound !0 = !DISubrange(lowerBound: -3) diff --git a/llvm/test/Bindings/llvm-c/debug_info.ll b/llvm/test/Bindings/llvm-c/debug_info.ll --- a/llvm/test/Bindings/llvm-c/debug_info.ll +++ b/llvm/test/Bindings/llvm-c/debug_info.ll @@ -60,7 +60,7 @@ ; CHECK-NEXT: !33 = !{!6, !6, !34} ; CHECK-NEXT: !34 = !DICompositeType(tag: DW_TAG_array_type, baseType: !6, size: 640, flags: DIFlagVector, elements: !35) ; CHECK-NEXT: !35 = !{!36} -; CHECK-NEXT: !36 = !DISubrange(count: 10) +; CHECK-NEXT: !36 = !DISubrange(count: 10, lowerBound: 0) ; CHECK-NEXT: !37 = !{!38, !39, !40, !41} ; CHECK-NEXT: !38 = !DILocalVariable(name: "a", arg: 1, scope: !31, file: !1, line: 42, type: !6) ; CHECK-NEXT: !39 = !DILocalVariable(name: "b", arg: 2, scope: !31, file: !1, line: 42, type: !6) diff --git a/llvm/test/Bitcode/fortranSubrange.ll b/llvm/test/Bitcode/fortranSubrange.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Bitcode/fortranSubrange.ll @@ -0,0 +1,44 @@ +;; This test checks DISubrange bounds + +; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s + +;; Test whether bounds are generated correctly. +; CHECK: !{{[0-9]+}} = !DISubrange(lowerBound: 3, upperBound: ![[NODE:[0-9]+]], stride: !DIExpression(DW_OP_constu, 4)) +; CHECK: ![[NODE]] = distinct !DILocalVariable + + +; ModuleID = 'fortsubrange.ll' +source_filename = "fortsubrange.ll" + +define void @MAIN_() !dbg !5 { +L.entry: + %.Z0640_333 = alloca i32*, align 8 + %"arr$sd1_349" = alloca [16 x i64], align 8 + call void @llvm.dbg.declare(metadata i32** %.Z0640_333, metadata !8, metadata !DIExpression(DW_OP_deref)), !dbg !15 + call void @llvm.dbg.declare(metadata [16 x i64]* %"arr$sd1_349", metadata !13, metadata !DIExpression(DW_OP_plus_uconst, 120)), !dbg !15 + ret void, !dbg !16 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) + +!llvm.module.flags = !{!0, !1} +!llvm.dbg.cu = !{!2} + +!0 = !{i32 2, !"Dwarf Version", i32 4} +!1 = !{i32 2, !"Debug Info Version", i32 3} +!2 = distinct !DICompileUnit(language: DW_LANG_Fortran90, file: !3, producer: " F90 Flang - 1.5 2017-05-01", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !4, globals: !4, imports: !4) +!3 = !DIFile(filename: "fortsubrange.f90", directory: "/dir") +!4 = !{} +!5 = distinct !DISubprogram(name: "main", scope: !2, file: !3, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagMainSubprogram, unit: !2) +!6 = !DISubroutineType(cc: DW_CC_program, types: !7) +!7 = !{null} +!8 = !DILocalVariable(name: "arr", scope: !5, file: !3, type: !9) +!9 = !DICompositeType(tag: DW_TAG_array_type, baseType: !10, size: 32, align: 32, elements: !11) +!10 = !DIBasicType(name: "integer", size: 32, align: 32, encoding: DW_ATE_signed) +!11 = !{!12} +!12 = !DISubrange(lowerBound: 3, upperBound: !13, stride: !DIExpression(DW_OP_constu, 4)) +!13 = distinct !DILocalVariable(scope: !5, file: !3, type: !14, flags: DIFlagArtificial) +!14 = !DIBasicType(name: "integer*8", size: 64, align: 64, encoding: DW_ATE_signed) +!15 = !DILocation(line: 0, scope: !5) +!16 = !DILocation(line: 6, column: 1, scope: !5) diff --git a/llvm/test/Bitcode/fortranSubrangeBackward.ll b/llvm/test/Bitcode/fortranSubrangeBackward.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Bitcode/fortranSubrangeBackward.ll @@ -0,0 +1,50 @@ +;; This test checks Backward compatibility of DISubrange bounds +; REQUIRES: x86_64-linux + +; RUN: llvm-dis -o - %s.bc | FileCheck %s + +;; Test whether bounds are generated correctly. +; CHECK: !DISubrange(count: 15, lowerBound: 3) +; CHECK: !DISubrange(count: ![[NODE:[0-9]+]], lowerBound: 3) +; CHECK: ![[NODE]] = distinct !DILocalVariable + + +; ModuleID = 'fortsubrange.ll' +source_filename = "fortsubrange.ll" + +define void @MAIN_() !dbg !10 { +L.entry: + %.Z0640_333 = alloca i32*, align 8 + %"arr$sd1_349" = alloca [16 x i64], align 8 + call void @llvm.dbg.declare(metadata i32** %.Z0640_333, metadata !13, metadata !DIExpression(DW_OP_deref)), !dbg !19 + call void @llvm.dbg.declare(metadata [16 x i64]* %"arr$sd1_349", metadata !17, metadata !DIExpression(DW_OP_plus_uconst, 120)), !dbg !19 + ret void, !dbg !20 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) + +!llvm.module.flags = !{!0, !1} +!llvm.dbg.cu = !{!2} + +!0 = !{i32 2, !"Dwarf Version", i32 4} +!1 = !{i32 2, !"Debug Info Version", i32 3} +!2 = distinct !DICompileUnit(language: DW_LANG_Fortran90, file: !3, producer: " F90 Flang - 1.5 2017-05-01", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !5, globals: !4, imports: !4) +!3 = !DIFile(filename: "fortsubrange.f90", directory: "/dir") +!4 = !{} +!5 = !{!6} +!6 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 32, align: 32, elements: !8) +!7 = !DIBasicType(name: "integer", size: 32, align: 32, encoding: DW_ATE_signed) +!8 = !{!9} +!9 = !DISubrange(count: 15, lowerBound: 3) +!10 = distinct !DISubprogram(name: "main", scope: !2, file: !3, line: 1, type: !11, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagMainSubprogram, unit: !2) +!11 = !DISubroutineType(cc: DW_CC_program, types: !12) +!12 = !{null} +!13 = !DILocalVariable(name: "arr", scope: !10, file: !3, type: !14) +!14 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 32, align: 32, elements: !15) +!15 = !{!16} +!16 = !DISubrange(count: !17, lowerBound: 3) +!17 = distinct !DILocalVariable(scope: !10, file: !3, type: !18, flags: DIFlagArtificial) +!18 = !DIBasicType(name: "integer*8", size: 64, align: 64, encoding: DW_ATE_signed) +!19 = !DILocation(line: 0, scope: !10) +!20 = !DILocation(line: 6, column: 1, scope: !10) diff --git a/llvm/test/Bitcode/fortranSubrangeBackward.ll.bc b/llvm/test/Bitcode/fortranSubrangeBackward.ll.bc new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ {[[BASE2:0x[0-9a-f]*]]} -; CHECK-NEXT: DW_AT_lower_bound [DW_FORM_data8] (0xfffffffffffffffd) +; CHECK-NEXT: DW_AT_lower_bound [DW_FORM_sdata] (-3) ; CHECK-NEXT: DW_AT_count [DW_FORM_data1] (0x2a) ; CHECK: [[BASE]]: DW_TAG_base_type diff --git a/llvm/test/DebugInfo/cDefaultLower.ll b/llvm/test/DebugInfo/cDefaultLower.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/cDefaultLower.ll @@ -0,0 +1,35 @@ +;; This test checks whether c default lowerBound is removed. +; REQUIRES: x86_64-linux + +; RUN: %llc_dwarf %s -filetype=obj -o - | llvm-dwarfdump - | FileCheck %s + +;; c default DW_AT_lower_bound(0) is not dumped. +; CHECK-LABEL: DW_TAG_subrange_type +; CHECK-NEXT: DW_AT_type +; CHECK-NEXT: DW_AT_upper_bound (4) + +;; c non-default lowerBound=1 is dumped. +; CHECK-LABEL: DW_TAG_subrange_type +; CHECK-NEXT: DW_AT_type +; CHECK-NEXT: DW_AT_lower_bound (1) +; CHECK-NEXT: DW_AT_upper_bound (5) + +; ModuleID = 'cDefaultLower.c' +source_filename = "cDefaultLower.c" + +!llvm.module.flags = !{!0, !1} +!llvm.dbg.cu = !{!2} + +!0 = !{i32 2, !"Dwarf Version", i32 4} +!1 = !{i32 2, !"Debug Info Version", i32 3} +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 11.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !5, globals: !4, imports: !4) +!3 = !DIFile(filename: "cDefaultLower.c", directory: "dir") +!4 = !{} +!5 = !{!6, !10} +!6 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 160, align: 32, elements: !8) +!7 = !DIBasicType(name: "integer", size: 32, align: 32, encoding: DW_ATE_signed) +!8 = !{!9} +!9 = !DISubrange(lowerBound: 0, upperBound: 4) +!10 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 160, align: 32, elements: !11) +!11 = !{!12} +!12 = !DISubrange(lowerBound: 1, upperBound: 5) diff --git a/llvm/test/DebugInfo/fortranDefaultLower.ll b/llvm/test/DebugInfo/fortranDefaultLower.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/fortranDefaultLower.ll @@ -0,0 +1,35 @@ +;; This test checks whether fortran default lowerBound is removed. +; REQUIRES: x86_64-linux + +; RUN: %llc_dwarf %s -filetype=obj -o - | llvm-dwarfdump - | FileCheck %s + +;; fortran default DW_AT_lower_bound(1) is not dumped. +; CHECK-LABEL: DW_TAG_subrange_type +; CHECK-NEXT: DW_AT_type +; CHECK-NEXT: DW_AT_upper_bound (5) + +;; fortran non-default lowerBound=2 is dumped. +; CHECK-LABEL: DW_TAG_subrange_type +; CHECK-NEXT: DW_AT_type +; CHECK-NEXT: DW_AT_lower_bound (2) +; CHECK-NEXT: DW_AT_upper_bound (6) + +; ModuleID = 'fortranDefaultLower.ll' +source_filename = "fortranDefaultLower.f90" + +!llvm.module.flags = !{!0, !1} +!llvm.dbg.cu = !{!2} + +!0 = !{i32 2, !"Dwarf Version", i32 4} +!1 = !{i32 2, !"Debug Info Version", i32 3} +!2 = distinct !DICompileUnit(language: DW_LANG_Fortran90, file: !3, producer: " F90 Flang - 1.5 2017-05-01", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !5, globals: !4, imports: !4) +!3 = !DIFile(filename: "fortranDefaultLower.f90", directory: "dir") +!4 = !{} +!5 = !{!6, !10} +!6 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 160, align: 32, elements: !8) +!7 = !DIBasicType(name: "integer", size: 32, align: 32, encoding: DW_ATE_signed) +!8 = !{!9} +!9 = !DISubrange(lowerBound: 1, upperBound: 5) +!10 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 160, align: 32, elements: !11) +!11 = !{!12} +!12 = !DISubrange(lowerBound: 2, upperBound: 6) diff --git a/llvm/test/DebugInfo/fortranSubrangeExpr.ll b/llvm/test/DebugInfo/fortranSubrangeExpr.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/fortranSubrangeExpr.ll @@ -0,0 +1,44 @@ +;; This test checks DISubrange bounds for DIExpression +; REQUIRES: x86_64-linux + +; RUN: %llc_dwarf %s -filetype=obj -o - | llvm-dwarfdump - | FileCheck %s + +;; Test whether bounds are generated correctly. +; CHECK-LABEL: DW_TAG_array_type +; CHECK: DW_TAG_subrange_type +; DW_AT_lower_bound (DW_OP_push_object_address, DW_OP_plus_uconst 0x50, DW_OP_deref) +; CHECK: DW_AT_lower_bound (DW_OP_push_object_address, DW_OP_plus_uconst 0x50, DW_OP_deref) +; CHECK-NEXT: DW_AT_upper_bound (DW_OP_push_object_address, DW_OP_plus_uconst 0x78, DW_OP_deref) +; CHECK-NEXT: DW_AT_byte_stride (DW_OP_push_object_address, DW_OP_plus_uconst 0x70, DW_OP_deref, DW_OP_plus_uconst 0x4, DW_OP_mul) + +; ModuleID = 'fortsubrange.modified.strategy3check-in.ll' +source_filename = "fortsubrange.ll" + +define void @MAIN_() !dbg !5 { +L.entry: + %"arr$sd1_349" = alloca [16 x i64], align 8 + call void @llvm.dbg.declare(metadata [16 x i64]* %"arr$sd1_349", metadata !8, metadata !DIExpression()), !dbg !13 + ret void, !dbg !14 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) + +!llvm.module.flags = !{!0, !1} +!llvm.dbg.cu = !{!2} + +!0 = !{i32 2, !"Dwarf Version", i32 4} +!1 = !{i32 2, !"Debug Info Version", i32 3} +!2 = distinct !DICompileUnit(language: DW_LANG_Fortran90, file: !3, producer: " F90 Flang - 1.5 2017-05-01", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !4, globals: !4, imports: !4) +!3 = !DIFile(filename: "fortsubrange.f90", directory: "/dir") +!4 = !{} +!5 = distinct !DISubprogram(name: "main", scope: !2, file: !3, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagMainSubprogram, unit: !2) +!6 = !DISubroutineType(cc: DW_CC_program, types: !7) +!7 = !{null} +!8 = !DILocalVariable(name: "arr", scope: !5, file: !3, type: !9) +!9 = !DICompositeType(tag: DW_TAG_array_type, baseType: !10, size: 32, align: 32, elements: !11) +!10 = !DIBasicType(name: "integer", size: 32, align: 32, encoding: DW_ATE_signed) +!11 = !{!12} +!12 = !DISubrange(lowerBound: !DIExpression(DW_OP_push_object_address, DW_OP_plus_uconst, 80, DW_OP_deref), upperBound: !DIExpression(DW_OP_push_object_address, DW_OP_plus_uconst, 120, DW_OP_deref), stride: !DIExpression(DW_OP_push_object_address, DW_OP_plus_uconst, 112, DW_OP_deref, DW_OP_plus_uconst, 4, DW_OP_mul)) +!13 = !DILocation(line: 0, scope: !5) +!14 = !DILocation(line: 6, column: 1, scope: !5) diff --git a/llvm/test/DebugInfo/fortranSubrangeInt.ll b/llvm/test/DebugInfo/fortranSubrangeInt.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/fortranSubrangeInt.ll @@ -0,0 +1,43 @@ +;; This test checks DISubrange bounds for constants +; REQUIRES: x86_64-linux + +; RUN: %llc_dwarf %s -filetype=obj -o - | llvm-dwarfdump - | FileCheck %s + +;; Test whether bounds are generated correctly. +; CHECK-LABEL: DW_TAG_array_type +; CHECK: DW_TAG_subrange_type +; CHECK: DW_AT_lower_bound (-10) +; CHECK-NEXT: DW_AT_upper_bound (10) +; CHECK-NEXT: DW_AT_byte_stride (4) + +; ModuleID = 'fortsubrange.ll' +source_filename = "fortsubrange.ll" + +define void @MAIN_() !dbg !5 { +L.entry: + %"arr$sd1_349" = alloca [16 x i64], align 8 + call void @llvm.dbg.declare(metadata [16 x i64]* %"arr$sd1_349", metadata !8, metadata !DIExpression()), !dbg !13 + ret void, !dbg !14 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) + +!llvm.module.flags = !{!0, !1} +!llvm.dbg.cu = !{!2} + +!0 = !{i32 2, !"Dwarf Version", i32 4} +!1 = !{i32 2, !"Debug Info Version", i32 3} +!2 = distinct !DICompileUnit(language: DW_LANG_Fortran90, file: !3, producer: " F90 Flang - 1.5 2017-05-01", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !4, globals: !4, imports: !4) +!3 = !DIFile(filename: "fortsubrange.f90", directory: "/dir") +!4 = !{} +!5 = distinct !DISubprogram(name: "main", scope: !2, file: !3, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagMainSubprogram, unit: !2) +!6 = !DISubroutineType(cc: DW_CC_program, types: !7) +!7 = !{null} +!8 = !DILocalVariable(name: "arr", scope: !5, file: !3, type: !9) +!9 = !DICompositeType(tag: DW_TAG_array_type, baseType: !10, size: 32, align: 32, elements: !11) +!10 = !DIBasicType(name: "integer", size: 32, align: 32, encoding: DW_ATE_signed) +!11 = !{!12} +!12 = !DISubrange(lowerBound: -10, upperBound: 10, stride: 4) +!13 = !DILocation(line: 0, scope: !5) +!14 = !DILocation(line: 6, column: 1, scope: !5) diff --git a/llvm/test/DebugInfo/fortranSubrangeVar.ll b/llvm/test/DebugInfo/fortranSubrangeVar.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/fortranSubrangeVar.ll @@ -0,0 +1,62 @@ +;; This test checks DISubrange bounds for DIVariable + +; RUN: %llc_dwarf %s -filetype=obj -o - | llvm-dwarfdump - | FileCheck %s + +;; Test whether bounds are generated correctly. +; CHECK: [[DIE1:0x.+]]: DW_TAG_variable +; CHECK: DW_AT_location +; CHECK-SAME: DW_OP_plus_uconst 0x70, DW_OP_deref, DW_OP_lit4, DW_OP_mul +; CHECK: [[DIE2:0x.+]]: DW_TAG_variable +; CHECK: DW_AT_location +; CHECK-SAME: DW_OP_plus_uconst 0x78 +; CHECK: [[DIE3:0x.+]]: DW_TAG_variable +; CHECK: DW_AT_location +; CHECK-SAME: DW_OP_plus_uconst 0x50 +; CHECK: DW_TAG_subrange_type +; CHECK: DW_AT_lower_bound ([[DIE3]]) +; CHEK-NEXT: DW_AT_upper_bound ([[DIE2]]) +; CHECK-NEXT DW_AT_byte_stride ([[DIE1]]) + + +; ModuleID = 'fortsubrange.ll' +source_filename = "fortsubrange.ll" + +define void @MAIN_() !dbg !5 { +L.entry: + %.Z0640_333 = alloca i32*, align 8 + %"arr$sd1_349" = alloca [16 x i64], align 8 + call void @llvm.dbg.declare(metadata i32** %.Z0640_333, metadata !8, metadata !DIExpression(DW_OP_deref)), !dbg !17 + call void @llvm.dbg.declare(metadata [16 x i64]* %"arr$sd1_349", metadata !13, metadata !DIExpression(DW_OP_plus_uconst, 80)), !dbg !17 + call void @llvm.dbg.value(metadata [16 x i64]* %"arr$sd1_349", metadata !16, metadata !DIExpression(DW_OP_plus_uconst, 112, DW_OP_deref, DW_OP_constu, 4, DW_OP_mul)), !dbg !17 + call void @llvm.dbg.declare(metadata [16 x i64]* %"arr$sd1_349", metadata !15, metadata !DIExpression(DW_OP_plus_uconst, 120)), !dbg !17 + ret void, !dbg !18 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.module.flags = !{!0, !1} +!llvm.dbg.cu = !{!2} + +!0 = !{i32 2, !"Dwarf Version", i32 4} +!1 = !{i32 2, !"Debug Info Version", i32 3} +!2 = distinct !DICompileUnit(language: DW_LANG_Fortran90, file: !3, producer: " F90 Flang - 1.5 2017-05-01", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !4, globals: !4, imports: !4) +!3 = !DIFile(filename: "fortsubrange.f90", directory: "/dir") +!4 = !{} +!5 = distinct !DISubprogram(name: "main", scope: !2, file: !3, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagMainSubprogram, unit: !2) +!6 = !DISubroutineType(cc: DW_CC_program, types: !7) +!7 = !{null} +!8 = !DILocalVariable(name: "arr", scope: !5, file: !3, type: !9) +!9 = !DICompositeType(tag: DW_TAG_array_type, baseType: !10, size: 32, align: 32, elements: !11) +!10 = !DIBasicType(name: "integer", size: 32, align: 32, encoding: DW_ATE_signed) +!11 = !{!12} +!12 = !DISubrange(lowerBound: !13, upperBound: !15, stride: !16) +!13 = distinct !DILocalVariable(scope: !5, file: !3, type: !14, flags: DIFlagArtificial) +!14 = !DIBasicType(name: "integer*8", size: 64, align: 64, encoding: DW_ATE_signed) +!15 = distinct !DILocalVariable(scope: !5, file: !3, type: !14, flags: DIFlagArtificial) +!16 = distinct !DILocalVariable(scope: !5, file: !3, type: !14, flags: DIFlagArtificial) +!17 = !DILocation(line: 0, scope: !5) +!18 = !DILocation(line: 6, column: 1, scope: !5) diff --git a/llvm/test/Verifier/disubrange-count-upperBound.ll b/llvm/test/Verifier/disubrange-count-upperBound.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/disubrange-count-upperBound.ll @@ -0,0 +1,5 @@ +; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s + +!named = !{!0} +; CHECK: Subrange can have any one of count or upperBound +!0 = !DISubrange(count: 20, lowerBound: 1, upperBound: 10) diff --git a/llvm/test/Verifier/disubrange-missing-upperBound.ll b/llvm/test/Verifier/disubrange-missing-upperBound.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/disubrange-missing-upperBound.ll @@ -0,0 +1,5 @@ +; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s + +!named = !{!0} +; CHECK: Subrange must contain count or upperBound +!0 = !DISubrange(lowerBound: 1, stride: 4) diff --git a/llvm/test/Verifier/invalid-disubrange-lowerBound.ll b/llvm/test/Verifier/invalid-disubrange-lowerBound.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/invalid-disubrange-lowerBound.ll @@ -0,0 +1,6 @@ +; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s + +!named = !{!0, !1} +; CHECK: LowerBound must be signed constant or DIVariable or DIExpression +!0 = !DISubrange(lowerBound: !1, upperBound: 1) +!1 = !DIBasicType(name: "integer*8", size: 64, align: 64, encoding: DW_ATE_signed) diff --git a/llvm/test/Verifier/invalid-disubrange-stride.ll b/llvm/test/Verifier/invalid-disubrange-stride.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/invalid-disubrange-stride.ll @@ -0,0 +1,6 @@ +; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s + +!named = !{!0, !1} +; CHECK: Stride must be signed constant or DIVariable or DIExpression +!0 = !DISubrange(upperBound: 1, stride: !1) +!1 = !DIBasicType(name: "integer*8", size: 64, align: 64, encoding: DW_ATE_signed) diff --git a/llvm/test/Verifier/invalid-disubrange-upperBound.ll b/llvm/test/Verifier/invalid-disubrange-upperBound.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/invalid-disubrange-upperBound.ll @@ -0,0 +1,6 @@ +; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s + +!named = !{!0, !1} +; CHECK: UpperBound must be signed constant or DIVariable or DIExpression +!0 = !DISubrange(lowerBound: 1, upperBound: !1) +!1 = !DIBasicType(name: "integer*8", size: 64, align: 64, encoding: DW_ATE_signed) diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp --- a/llvm/unittests/IR/MetadataTest.cpp +++ b/llvm/unittests/IR/MetadataTest.cpp @@ -1139,11 +1139,12 @@ TEST_F(DISubrangeTest, get) { auto *N = DISubrange::get(Context, 5, 7); auto Count = N->getCount(); + auto Lower = N->getLowerBound(); EXPECT_EQ(dwarf::DW_TAG_subrange_type, N->getTag()); ASSERT_TRUE(Count); ASSERT_TRUE(Count.is()); EXPECT_EQ(5, Count.get()->getSExtValue()); - EXPECT_EQ(7, N->getLowerBound()); + EXPECT_EQ(7, Lower.get()->getSExtValue()); EXPECT_EQ(N, DISubrange::get(Context, 5, 7)); EXPECT_EQ(DISubrange::get(Context, 5, 0), DISubrange::get(Context, 5)); @@ -1154,11 +1155,12 @@ TEST_F(DISubrangeTest, getEmptyArray) { auto *N = DISubrange::get(Context, -1, 0); auto Count = N->getCount(); + auto Lower = N->getLowerBound(); EXPECT_EQ(dwarf::DW_TAG_subrange_type, N->getTag()); ASSERT_TRUE(Count); ASSERT_TRUE(Count.is()); EXPECT_EQ(-1, Count.get()->getSExtValue()); - EXPECT_EQ(0, N->getLowerBound()); + EXPECT_EQ(0, Lower.get()->getSExtValue()); EXPECT_EQ(N, DISubrange::get(Context, -1, 0)); } @@ -1172,15 +1174,146 @@ auto *N = DISubrange::get(Context, VlaExpr, 0); auto Count = N->getCount(); + auto Lower = N->getLowerBound(); ASSERT_TRUE(Count); ASSERT_TRUE(Count.is()); EXPECT_EQ(VlaExpr, Count.get()); ASSERT_TRUE(isa(N->getRawCountNode())); - EXPECT_EQ(0, N->getLowerBound()); + EXPECT_EQ(0, Lower.get()->getSExtValue()); EXPECT_EQ("vla_expr", Count.get()->getName()); EXPECT_EQ(N, DISubrange::get(Context, VlaExpr, 0)); } +TEST_F(DISubrangeTest, fortranAllocatableInt) { + DILocalScope *Scope = getSubprogram(); + DIFile *File = getFile(); + DIType *Type = getDerivedType(); + DINode::DIFlags Flags = static_cast(7); + auto *LI = ConstantAsMetadata::get( + ConstantInt::getSigned(Type::getInt64Ty(Context), -10)); + auto *UI = ConstantAsMetadata::get( + ConstantInt::getSigned(Type::getInt64Ty(Context), 10)); + auto *SI = ConstantAsMetadata::get( + ConstantInt::getSigned(Type::getInt64Ty(Context), 4)); + auto *UIother = ConstantAsMetadata::get( + ConstantInt::getSigned(Type::getInt64Ty(Context), 20)); + auto *UVother = DILocalVariable::get(Context, Scope, "ubother", File, 8, Type, + 2, Flags, 8); + auto *UEother = DIExpression::get(Context, {5, 6}); + auto *LIZero = ConstantAsMetadata::get( + ConstantInt::getSigned(Type::getInt64Ty(Context), 0)); + auto *UIZero = ConstantAsMetadata::get( + ConstantInt::getSigned(Type::getInt64Ty(Context), 0)); + + auto *N = DISubrange::get(Context, nullptr, LI, UI, SI); + + auto Lower = N->getLowerBound(); + ASSERT_TRUE(Lower); + ASSERT_TRUE(Lower.is()); + EXPECT_EQ(cast(LI->getValue()), Lower.get()); + + auto Upper = N->getUpperBound(); + ASSERT_TRUE(Upper); + ASSERT_TRUE(Upper.is()); + EXPECT_EQ(cast(UI->getValue()), Upper.get()); + + auto Stride = N->getStride(); + ASSERT_TRUE(Stride); + ASSERT_TRUE(Stride.is()); + EXPECT_EQ(cast(SI->getValue()), Stride.get()); + + EXPECT_EQ(N, DISubrange::get(Context, nullptr, LI, UI, SI)); + + EXPECT_NE(N, DISubrange::get(Context, nullptr, LI, UIother, SI)); + EXPECT_NE(N, DISubrange::get(Context, nullptr, LI, UEother, SI)); + EXPECT_NE(N, DISubrange::get(Context, nullptr, LI, UVother, SI)); + + auto *NZeroLower = DISubrange::get(Context, nullptr, LIZero, UI, SI); + EXPECT_NE(NZeroLower, DISubrange::get(Context, nullptr, nullptr, UI, SI)); + + auto *NZeroUpper = DISubrange::get(Context, nullptr, LI, UIZero, SI); + EXPECT_NE(NZeroUpper, DISubrange::get(Context, nullptr, LI, nullptr, SI)); +} + +TEST_F(DISubrangeTest, fortranAllocatableVar) { + DILocalScope *Scope = getSubprogram(); + DIFile *File = getFile(); + DIType *Type = getDerivedType(); + DINode::DIFlags Flags = static_cast(7); + auto *LV = + DILocalVariable::get(Context, Scope, "lb", File, 8, Type, 2, Flags, 8); + auto *UV = + DILocalVariable::get(Context, Scope, "ub", File, 8, Type, 2, Flags, 8); + auto *SV = + DILocalVariable::get(Context, Scope, "st", File, 8, Type, 2, Flags, 8); + auto *SVother = DILocalVariable::get(Context, Scope, "stother", File, 8, Type, + 2, Flags, 8); + auto *SIother = ConstantAsMetadata::get( + ConstantInt::getSigned(Type::getInt64Ty(Context), 20)); + auto *SEother = DIExpression::get(Context, {5, 6}); + + auto *N = DISubrange::get(Context, nullptr, LV, UV, SV); + + auto Lower = N->getLowerBound(); + ASSERT_TRUE(Lower); + ASSERT_TRUE(Lower.is()); + EXPECT_EQ(LV, Lower.get()); + + auto Upper = N->getUpperBound(); + ASSERT_TRUE(Upper); + ASSERT_TRUE(Upper.is()); + EXPECT_EQ(UV, Upper.get()); + + auto Stride = N->getStride(); + ASSERT_TRUE(Stride); + ASSERT_TRUE(Stride.is()); + EXPECT_EQ(SV, Stride.get()); + + EXPECT_EQ(N, DISubrange::get(Context, nullptr, LV, UV, SV)); + + EXPECT_NE(N, DISubrange::get(Context, nullptr, LV, UV, SVother)); + EXPECT_NE(N, DISubrange::get(Context, nullptr, LV, UV, SEother)); + EXPECT_NE(N, DISubrange::get(Context, nullptr, LV, UV, SIother)); +} + +TEST_F(DISubrangeTest, fortranAllocatableExpr) { + DILocalScope *Scope = getSubprogram(); + DIFile *File = getFile(); + DIType *Type = getDerivedType(); + DINode::DIFlags Flags = static_cast(7); + auto *LE = DIExpression::get(Context, {1, 2}); + auto *UE = DIExpression::get(Context, {2, 3}); + auto *SE = DIExpression::get(Context, {3, 4}); + auto *LEother = DIExpression::get(Context, {5, 6}); + auto *LIother = ConstantAsMetadata::get( + ConstantInt::getSigned(Type::getInt64Ty(Context), 20)); + auto *LVother = DILocalVariable::get(Context, Scope, "lbother", File, 8, Type, + 2, Flags, 8); + + auto *N = DISubrange::get(Context, nullptr, LE, UE, SE); + + auto Lower = N->getLowerBound(); + ASSERT_TRUE(Lower); + ASSERT_TRUE(Lower.is()); + EXPECT_EQ(LE, Lower.get()); + + auto Upper = N->getUpperBound(); + ASSERT_TRUE(Upper); + ASSERT_TRUE(Upper.is()); + EXPECT_EQ(UE, Upper.get()); + + auto Stride = N->getStride(); + ASSERT_TRUE(Stride); + ASSERT_TRUE(Stride.is()); + EXPECT_EQ(SE, Stride.get()); + + EXPECT_EQ(N, DISubrange::get(Context, nullptr, LE, UE, SE)); + + EXPECT_NE(N, DISubrange::get(Context, nullptr, LEother, UE, SE)); + EXPECT_NE(N, DISubrange::get(Context, nullptr, LIother, UE, SE)); + EXPECT_NE(N, DISubrange::get(Context, nullptr, LVother, UE, SE)); +} + typedef MetadataTest DIEnumeratorTest; TEST_F(DIEnumeratorTest, get) {