diff --git a/llvm/include/llvm-c/DebugInfo.h b/llvm/include/llvm-c/DebugInfo.h --- a/llvm/include/llvm-c/DebugInfo.h +++ b/llvm/include/llvm-c/DebugInfo.h @@ -160,7 +160,8 @@ LLVMDIMacroMetadataKind, LLVMDIMacroFileMetadataKind, LLVMDICommonBlockMetadataKind, - LLVMDIStringTypeMetadataKind + LLVMDIStringTypeMetadataKind, + LLVMDIGenericSubrangeMetadataKind }; typedef unsigned LLVMMetadataKind; diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -341,7 +341,8 @@ METADATA_STRING_TYPE = 41, // [distinct, name, size, align,...] // Codes 42 and 43 are reserved for support for Fortran array specific debug // info. - METADATA_COMMON_BLOCK = 44 // [distinct, scope, name, variable,...] + METADATA_COMMON_BLOCK = 44, // [distinct, scope, name, variable,...] + METADATA_GENERIC_SUBRANGE = 45 // [distinct, count, lo, up, stride] }; // The constants block (CONSTANTS_BLOCK_ID) describes emission for each 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 @@ -582,6 +582,12 @@ DISubrange *getOrCreateSubrange(Metadata *Count, Metadata *LowerBound, Metadata *UpperBound, Metadata *Stride); + DIGenericSubrange * + getOrCreateGenericSubrange(DIGenericSubrange::BoundType Count, + DIGenericSubrange::BoundType LowerBound, + DIGenericSubrange::BoundType UpperBound, + DIGenericSubrange::BoundType Stride); + /// Create a new descriptor for the specified variable. /// \param Context Variable scope. /// \param Name Name of the variable. 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 @@ -201,6 +201,7 @@ case DIObjCPropertyKind: case DIImportedEntityKind: case DIModuleKind: + case DIGenericSubrangeKind: return true; } } @@ -351,6 +352,52 @@ } }; +class DIGenericSubrange : public DINode { + friend class LLVMContextImpl; + friend class MDNode; + + DIGenericSubrange(LLVMContext &C, StorageType Storage, + ArrayRef Ops) + : DINode(C, DIGenericSubrangeKind, Storage, + dwarf::DW_TAG_generic_subrange, Ops) {} + + ~DIGenericSubrange() = default; + + static DIGenericSubrange *getImpl(LLVMContext &Context, Metadata *CountNode, + Metadata *LowerBound, Metadata *UpperBound, + Metadata *Stride, StorageType Storage, + bool ShouldCreate = true); + + TempDIGenericSubrange cloneImpl() const { + return getTemporary(getContext(), getRawCountNode(), getRawLowerBound(), + getRawUpperBound(), getRawStride()); + } + +public: + DEFINE_MDNODE_GET(DIGenericSubrange, + (Metadata * CountNode, Metadata *LowerBound, + Metadata *UpperBound, Metadata *Stride), + (CountNode, LowerBound, UpperBound, Stride)) + + TempDIGenericSubrange 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(); } + + using BoundType = PointerUnion; + + BoundType getCount() const; + BoundType getLowerBound() const; + BoundType getUpperBound() const; + BoundType getStride() const; + + static bool classof(const Metadata *MD) { + return MD->getMetadataID() == DIGenericSubrangeKind; + } +}; + /// Enumeration value. /// /// TODO: Add a pointer to the context (DW_TAG_enumeration_type) once that no @@ -2524,6 +2571,9 @@ /// Determine whether this represents a standalone constant value. bool isConstant() const; + /// Determine whether this represents a standalone signed constant value. + bool isSignedConstant() const; + using element_iterator = ArrayRef::iterator; element_iterator elements_begin() const { return getElements().begin(); } diff --git a/llvm/include/llvm/IR/Metadata.def b/llvm/include/llvm/IR/Metadata.def --- a/llvm/include/llvm/IR/Metadata.def +++ b/llvm/include/llvm/IR/Metadata.def @@ -115,6 +115,7 @@ HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIMacroFile) HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DICommonBlock) HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIStringType) +HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIGenericSubrange) #undef HANDLE_METADATA #undef HANDLE_METADATA_LEAF 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 @@ -4670,6 +4670,39 @@ return false; } +/// parseDIGenericSubrange: +/// ::= !DIGenericSubrange(lowerBound: !node1, upperBound: !node2, stride: +/// !node3) +bool LLParser::parseDIGenericSubrange(MDNode *&Result, bool IsDistinct) { +#define VISIT_MD_FIELDS(OPTIONAL, REQUIRED) \ + OPTIONAL(count, MDSignedOrMDField, ); \ + OPTIONAL(lowerBound, MDSignedOrMDField, ); \ + OPTIONAL(upperBound, MDSignedOrMDField, ); \ + OPTIONAL(stride, MDSignedOrMDField, ); + PARSE_MD_FIELDS(); +#undef VISIT_MD_FIELDS + + auto ConvToMetadata = [&](MDSignedOrMDField Bound) -> Metadata * { + if (Bound.isMDSignedField()) + return DIExpression::get( + Context, {dwarf::DW_OP_consts, + static_cast(Bound.getMDSignedValue())}); + if (Bound.isMDField()) + return Bound.getMDFieldValue(); + return nullptr; + }; + + Metadata *Count = ConvToMetadata(count); + Metadata *LowerBound = ConvToMetadata(lowerBound); + Metadata *UpperBound = ConvToMetadata(upperBound); + Metadata *Stride = ConvToMetadata(stride); + + Result = GET_OR_DISTINCT(DIGenericSubrange, + (Context, Count, LowerBound, UpperBound, Stride)); + + return false; +} + /// parseDIEnumerator: /// ::= !DIEnumerator(value: 30, isUnsigned: true, name: "SomeKind") bool LLParser::parseDIEnumerator(MDNode *&Result, bool IsDistinct) { 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 @@ -875,6 +875,7 @@ case bitc::METADATA_OBJC_PROPERTY: case bitc::METADATA_IMPORTED_ENTITY: case bitc::METADATA_GLOBAL_VAR_EXPR: + case bitc::METADATA_GENERIC_SUBRANGE: // We don't expect to see any of these, if we see one, give up on // lazy-loading and fallback. MDStringRef.clear(); @@ -1371,6 +1372,18 @@ NextMetadataNo++; break; } + case bitc::METADATA_GENERIC_SUBRANGE: { + Metadata *Val = nullptr; + Val = GET_OR_DISTINCT(DIGenericSubrange, + (Context, getMDOrNull(Record[1]), + getMDOrNull(Record[2]), getMDOrNull(Record[3]), + getMDOrNull(Record[4]))); + + MetadataList.assignValue(Val, NextMetadataNo); + IsDistinct = Record[0] & 1; + NextMetadataNo++; + break; + } case bitc::METADATA_ENUMERATOR: { if (Record.size() < 3) return error("Invalid record"); 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 @@ -300,6 +300,9 @@ SmallVectorImpl &Record, unsigned &Abbrev); void writeDISubrange(const DISubrange *N, SmallVectorImpl &Record, unsigned Abbrev); + void writeDIGenericSubrange(const DIGenericSubrange *N, + SmallVectorImpl &Record, + unsigned Abbrev); void writeDIEnumerator(const DIEnumerator *N, SmallVectorImpl &Record, unsigned Abbrev); void writeDIBasicType(const DIBasicType *N, SmallVectorImpl &Record, @@ -1553,6 +1556,19 @@ Record.clear(); } +void ModuleBitcodeWriter::writeDIGenericSubrange( + const DIGenericSubrange *N, SmallVectorImpl &Record, + unsigned Abbrev) { + Record.push_back((uint64_t)N->isDistinct()); + Record.push_back(VE.getMetadataOrNullID(N->getRawCountNode())); + 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_GENERIC_SUBRANGE, Record, Abbrev); + Record.clear(); +} + static void emitSignedInt64(SmallVectorImpl &Vals, uint64_t V) { if ((int64_t)V >= 0) Vals.push_back(V << 1); 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 @@ -812,6 +812,19 @@ if (auto ST = Subrange->getStride()) if (auto *Dependency = ST.dyn_cast()) Result.push_back(Dependency); + } else if (auto *GenericSubrange = dyn_cast(El)) { + if (auto Count = GenericSubrange->getCount()) + if (auto *Dependency = Count.dyn_cast()) + Result.push_back(Dependency); + if (auto LB = GenericSubrange->getLowerBound()) + if (auto *Dependency = LB.dyn_cast()) + Result.push_back(Dependency); + if (auto UB = GenericSubrange->getUpperBound()) + if (auto *Dependency = UB.dyn_cast()) + Result.push_back(Dependency); + if (auto ST = GenericSubrange->getStride()) + if (auto *Dependency = ST.dyn_cast()) + Result.push_back(Dependency); } } return Result; diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp @@ -541,6 +541,10 @@ assert(!isRegisterLocation()); emitConstu(Op->getArg(0)); break; + case dwarf::DW_OP_consts: + assert(!isRegisterLocation()); + emitSigned(Op->getArg(0)); + break; case dwarf::DW_OP_LLVM_convert: { unsigned BitSize = Op->getArg(0); dwarf::TypeKind Encoding = static_cast(Op->getArg(1)); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h --- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h @@ -301,6 +301,8 @@ void constructTypeDIE(DIE &Buffer, const DIDerivedType *DTy); void constructTypeDIE(DIE &Buffer, const DISubroutineType *CTy); void constructSubrangeDIE(DIE &Buffer, const DISubrange *SR, DIE *IndexTy); + void constructGenericSubrangeDIE(DIE &Buffer, const DIGenericSubrange *SR, + DIE *IndexTy); void constructArrayTypeDIE(DIE &Buffer, const DICompositeType *CTy); void constructEnumTypeDIE(DIE &Buffer, const DICompositeType *CTy); DIE &constructMemberDIE(DIE &Buffer, const DIDerivedType *DT); 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 @@ -1357,7 +1357,7 @@ if (auto *CI = SR->getCount().dyn_cast()) Count = CI->getSExtValue(); - auto addBoundTypeEntry = [&](dwarf::Attribute Attr, + auto AddBoundTypeEntry = [&](dwarf::Attribute Attr, DISubrange::BoundType Bound) -> void { if (auto *BV = Bound.dyn_cast()) { if (auto *VarDIE = getDIE(BV)) @@ -1375,7 +1375,7 @@ } }; - addBoundTypeEntry(dwarf::DW_AT_lower_bound, SR->getLowerBound()); + AddBoundTypeEntry(dwarf::DW_AT_lower_bound, SR->getLowerBound()); if (auto *CV = SR->getCount().dyn_cast()) { if (auto *CountVarDIE = getDIE(CV)) @@ -1383,9 +1383,45 @@ } 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_upper_bound, SR->getUpperBound()); - addBoundTypeEntry(dwarf::DW_AT_byte_stride, SR->getStride()); + AddBoundTypeEntry(dwarf::DW_AT_byte_stride, SR->getStride()); +} + +void DwarfUnit::constructGenericSubrangeDIE(DIE &Buffer, + const DIGenericSubrange *GSR, + DIE *IndexTy) { + DIE &DwGenericSubrange = + createAndAddDIE(dwarf::DW_TAG_generic_subrange, Buffer); + addDIEEntry(DwGenericSubrange, dwarf::DW_AT_type, *IndexTy); + + int64_t DefaultLowerBound = getDefaultLowerBound(); + + auto AddBoundTypeEntry = [&](dwarf::Attribute Attr, + DIGenericSubrange::BoundType Bound) -> void { + if (auto *BV = Bound.dyn_cast()) { + if (auto *VarDIE = getDIE(BV)) + addDIEEntry(DwGenericSubrange, Attr, *VarDIE); + } else if (auto *BE = Bound.dyn_cast()) { + if (BE->isSignedConstant()) { + if (Attr != dwarf::DW_AT_lower_bound || DefaultLowerBound == -1 || + static_cast(BE->getElement(1)) != DefaultLowerBound) + addSInt(DwGenericSubrange, Attr, dwarf::DW_FORM_sdata, + BE->getElement(1)); + } else { + DIELoc *Loc = new (DIEValueAllocator) DIELoc; + DIEDwarfExpression DwarfExpr(*Asm, getCU(), *Loc); + DwarfExpr.setMemoryLocationKind(); + DwarfExpr.addExpression(BE); + addBlock(DwGenericSubrange, Attr, DwarfExpr.finalize()); + } + } + }; + + AddBoundTypeEntry(dwarf::DW_AT_lower_bound, GSR->getLowerBound()); + AddBoundTypeEntry(dwarf::DW_AT_count, GSR->getCount()); + AddBoundTypeEntry(dwarf::DW_AT_upper_bound, GSR->getUpperBound()); + AddBoundTypeEntry(dwarf::DW_AT_byte_stride, GSR->getStride()); } DIE *DwarfUnit::getIndexTyDie() { @@ -1495,9 +1531,13 @@ DINodeArray Elements = CTy->getElements(); for (unsigned i = 0, N = Elements.size(); i < N; ++i) { // FIXME: Should this really be such a loose cast? - if (auto *Element = dyn_cast_or_null(Elements[i])) + if (auto *Element = dyn_cast_or_null(Elements[i])) { if (Element->getTag() == dwarf::DW_TAG_subrange_type) constructSubrangeDIE(Buffer, cast(Element), IdxTy); + else if (Element->getTag() == dwarf::DW_TAG_generic_subrange) + constructGenericSubrangeDIE(Buffer, cast(Element), + IdxTy); + } } } 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 @@ -1910,6 +1910,57 @@ Out << ")"; } +static void writeDIGenericSubrange(raw_ostream &Out, const DIGenericSubrange *N, + TypePrinting *TypePrinter, + SlotTracker *Machine, + const Module *Context) { + Out << "!DIGenericSubrange("; + MDFieldPrinter Printer(Out, TypePrinter, Machine, Context); + + auto IsConstant = [&](Metadata *Bound) -> bool { + if (auto *BE = dyn_cast_or_null(Bound)) { + return BE->isSignedConstant(); + } + return false; + }; + + auto GetConstant = [&](Metadata *Bound) -> int64_t { + assert(IsConstant(Bound) && "Expected constant"); + auto *BE = dyn_cast_or_null(Bound); + return static_cast(BE->getElement(1)); + }; + + auto *Count = N->getRawCountNode(); + if (IsConstant(Count)) + Printer.printInt("count", GetConstant(Count), + /* ShouldSkipZero */ false); + else + Printer.printMetadata("count", Count, /*ShouldSkipNull */ true); + + auto *LBound = N->getRawLowerBound(); + if (IsConstant(LBound)) + Printer.printInt("lowerBound", GetConstant(LBound), + /* ShouldSkipZero */ false); + else + Printer.printMetadata("lowerBound", LBound, /*ShouldSkipNull */ true); + + auto *UBound = N->getRawUpperBound(); + if (IsConstant(UBound)) + Printer.printInt("upperBound", GetConstant(UBound), + /* ShouldSkipZero */ false); + else + Printer.printMetadata("upperBound", UBound, /*ShouldSkipNull */ true); + + auto *Stride = N->getRawStride(); + if (IsConstant(Stride)) + Printer.printInt("stride", GetConstant(Stride), + /* ShouldSkipZero */ false); + else + Printer.printMetadata("stride", Stride, /*ShouldSkipNull */ true); + + Out << ")"; +} + static void writeDIEnumerator(raw_ostream &Out, const DIEnumerator *N, TypePrinting *, SlotTracker *, const Module *) { Out << "!DIEnumerator("; 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 @@ -649,6 +649,18 @@ return DISubrange::get(VMContext, CountNode, LB, UB, Stride); } +DIGenericSubrange *DIBuilder::getOrCreateGenericSubrange( + DIGenericSubrange::BoundType CountNode, DIGenericSubrange::BoundType LB, + DIGenericSubrange::BoundType UB, DIGenericSubrange::BoundType Stride) { + auto ConvToMetadata = [&](DIGenericSubrange::BoundType Bound) -> Metadata * { + return Bound.is() ? (Metadata *)Bound.get() + : (Metadata *)Bound.get(); + }; + return DIGenericSubrange::get(VMContext, ConvToMetadata(CountNode), + ConvToMetadata(LB), ConvToMetadata(UB), + ConvToMetadata(Stride)); +} + static void checkGlobalVariableScope(DIScope *Context) { #ifndef NDEBUG if (auto *CT = 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 @@ -435,6 +435,84 @@ return BoundType(); } +DIGenericSubrange *DIGenericSubrange::getImpl(LLVMContext &Context, + Metadata *CountNode, Metadata *LB, + Metadata *UB, Metadata *Stride, + StorageType Storage, + bool ShouldCreate) { + DEFINE_GETIMPL_LOOKUP(DIGenericSubrange, (CountNode, LB, UB, Stride)); + Metadata *Ops[] = {CountNode, LB, UB, Stride}; + DEFINE_GETIMPL_STORE_NO_CONSTRUCTOR_ARGS(DIGenericSubrange, Ops); +} + +DIGenericSubrange::BoundType DIGenericSubrange::getCount() const { + Metadata *CB = getRawCountNode(); + if (!CB) + return BoundType(); + + assert((isa(CB) || isa(CB)) && + "Count must be signed constant or DIVariable or DIExpression"); + + if (auto *MD = dyn_cast(CB)) + return BoundType(MD); + + if (auto *MD = dyn_cast(CB)) + return BoundType(MD); + + return BoundType(); +} + +DIGenericSubrange::BoundType DIGenericSubrange::getLowerBound() const { + Metadata *LB = getRawLowerBound(); + if (!LB) + return BoundType(); + + assert((isa(LB) || isa(LB)) && + "LowerBound must be signed constant or DIVariable or DIExpression"); + + if (auto *MD = dyn_cast(LB)) + return BoundType(MD); + + if (auto *MD = dyn_cast(LB)) + return BoundType(MD); + + return BoundType(); +} + +DIGenericSubrange::BoundType DIGenericSubrange::getUpperBound() const { + Metadata *UB = getRawUpperBound(); + if (!UB) + return BoundType(); + + assert((isa(UB) || isa(UB)) && + "UpperBound must be signed constant or DIVariable or DIExpression"); + + if (auto *MD = dyn_cast(UB)) + return BoundType(MD); + + if (auto *MD = dyn_cast(UB)) + return BoundType(MD); + + return BoundType(); +} + +DIGenericSubrange::BoundType DIGenericSubrange::getStride() const { + Metadata *ST = getRawStride(); + if (!ST) + return BoundType(); + + assert((isa(ST) || isa(ST)) && + "Stride must be signed constant or DIVariable or DIExpression"); + + 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, const APInt &Value, bool IsUnsigned, MDString *Name, StorageType Storage, bool ShouldCreate) { @@ -1061,6 +1139,7 @@ case dwarf::DW_OP_bregx: case dwarf::DW_OP_push_object_address: case dwarf::DW_OP_over: + case dwarf::DW_OP_consts: break; } } @@ -1334,6 +1413,15 @@ return true; } +bool DIExpression::isSignedConstant() const { + // Recognize DW_OP_consts C + if (getNumElements() != 2) + return false; + if (getElement(0) != dwarf::DW_OP_consts) + return false; + return true; +} + DIExpression::ExtOps DIExpression::getExtOps(unsigned FromSize, unsigned ToSize, bool Signed) { dwarf::TypeKind TK = Signed ? dwarf::DW_ATE_signed : dwarf::DW_ATE_unsigned; 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 @@ -346,6 +346,36 @@ } }; +template <> struct MDNodeKeyImpl { + Metadata *CountNode; + 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 DIGenericSubrange *N) + : CountNode(N->getRawCountNode()), LowerBound(N->getRawLowerBound()), + UpperBound(N->getRawUpperBound()), Stride(N->getRawStride()) {} + + bool isKeyOf(const DIGenericSubrange *RHS) const { + return (CountNode == RHS->getRawCountNode()) && + (LowerBound == RHS->getRawLowerBound()) && + (UpperBound == RHS->getRawUpperBound()) && + (Stride == RHS->getRawStride()); + } + + unsigned getHashValue() const { + auto *MD = dyn_cast_or_null(CountNode); + if (CountNode && MD) + return hash_combine(cast(MD->getValue())->getSExtValue(), + LowerBound, UpperBound, Stride); + return hash_combine(CountNode, LowerBound, UpperBound, Stride); + } +}; + template <> struct MDNodeKeyImpl { APInt Value; MDString *Name; 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 @@ -926,6 +926,30 @@ "Stride must be signed constant or DIVariable or DIExpression", &N); } +void Verifier::visitDIGenericSubrange(const DIGenericSubrange &N) { + AssertDI(N.getTag() == dwarf::DW_TAG_generic_subrange, "invalid tag", &N); + AssertDI(N.getRawCountNode() || N.getRawUpperBound(), + "GenericSubrange must contain count or upperBound", &N); + AssertDI(!N.getRawCountNode() || !N.getRawUpperBound(), + "GenericSubrange can have any one of count or upperBound", &N); + auto *CBound = N.getRawCountNode(); + AssertDI(!CBound || isa(CBound) || isa(CBound), + "Count must be signed constant or DIVariable or DIExpression", &N); + auto *LBound = N.getRawLowerBound(); + AssertDI(LBound, "GenericSubrange must contain lowerBound", &N); + AssertDI(isa(LBound) || isa(LBound), + "LowerBound must be signed constant or DIVariable or DIExpression", + &N); + auto *UBound = N.getRawUpperBound(); + AssertDI(!UBound || isa(UBound) || isa(UBound), + "UpperBound must be signed constant or DIVariable or DIExpression", + &N); + auto *Stride = N.getRawStride(); + AssertDI(Stride, "GenericSubrange must contain stride", &N); + AssertDI(isa(Stride) || isa(Stride), + "Stride must be signed constant or DIVariable or DIExpression", &N); +} + void Verifier::visitDIEnumerator(const DIEnumerator &N) { AssertDI(N.getTag() == dwarf::DW_TAG_enumerator, "invalid tag", &N); } diff --git a/llvm/test/Bitcode/generic_subrange.ll b/llvm/test/Bitcode/generic_subrange.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Bitcode/generic_subrange.ll @@ -0,0 +1,35 @@ +;; This test checks generation of DIGenericSubrange. + +; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s + +;; Test whether DIGenericSubrange is generated. +; CHECK: !DIGenericSubrange(lowerBound: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 80, DW_OP_plus, DW_OP_deref), upperBound: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 120, DW_OP_plus, DW_OP_deref), stride: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 112, DW_OP_plus, DW_OP_deref, DW_OP_constu, 4, DW_OP_mul)) + +; ModuleID = 'generic_subrange.f90' +source_filename = "/dir/generic_subrange.ll" + +!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: "generic_subrange.f90", directory: "/dir") +!4 = !{} +!5 = !{!6} +!6 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 32, align: 32, elements: !8, dataLocation: !10, rank: !DIExpression(DW_OP_push_object_address, DW_OP_plus_uconst, 8, DW_OP_deref)) +!7 = !DIBasicType(name: "real", size: 32, align: 32, encoding: DW_ATE_float) +!8 = !{!9} +!9 = !DIGenericSubrange(lowerBound: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 80, DW_OP_plus, DW_OP_deref), upperBound: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 120, DW_OP_plus, DW_OP_deref), stride: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 112, DW_OP_plus, DW_OP_deref, DW_OP_constu, 4, DW_OP_mul)) +!10 = distinct !DILocalVariable(scope: !11, file: !3, type: !18, flags: DIFlagArtificial) +!11 = distinct !DISubprogram(name: "sub1", scope: !2, file: !3, line: 1, type: !12, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2) +!12 = !DISubroutineType(types: !13) +!13 = !{null, !14, !19} +!14 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 32, align: 32, elements: !15) +!15 = !{!16} +!16 = !DISubrange(lowerBound: 1, upperBound: !17) +!17 = distinct !DILocalVariable(scope: !11, file: !3, type: !18, flags: DIFlagArtificial) +!18 = !DIBasicType(name: "integer*8", size: 64, align: 64, encoding: DW_ATE_signed) +!19 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 1024, align: 64, elements: !20) +!20 = !{!21} +!21 = !DISubrange(lowerBound: 1, upperBound: 16) diff --git a/llvm/test/Bitcode/generic_subrange_const.ll b/llvm/test/Bitcode/generic_subrange_const.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Bitcode/generic_subrange_const.ll @@ -0,0 +1,37 @@ +;; This test checks generation of DIGenericSubrange with constant bounds. +;; constant bounds are interally represented as DIExpression + +; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s + +;; Test whether DIGenericSubrange is generated. + +; CHECK: !DIGenericSubrange(lowerBound: -20, upperBound: 0, stride: 4) + +; ModuleID = 'generic_subrange_const.f90' +source_filename = "/dir/generic_subrange_const.ll" + +!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: "generic_subrange_const.f90", directory: "/dir") +!4 = !{} +!5 = !{!6} +!6 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 32, align: 32, elements: !8, dataLocation: !10, rank: !DIExpression(DW_OP_push_object_address, DW_OP_plus_uconst, 8, DW_OP_deref)) +!7 = !DIBasicType(name: "real", size: 32, align: 32, encoding: DW_ATE_float) +!8 = !{!9} +!9 = !DIGenericSubrange(lowerBound: -20, upperBound: 0, stride: 4) +!10 = distinct !DILocalVariable(scope: !11, file: !3, type: !18, flags: DIFlagArtificial) +!11 = distinct !DISubprogram(name: "sub1", scope: !2, file: !3, line: 1, type: !12, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2) +!12 = !DISubroutineType(types: !13) +!13 = !{null, !14, !19} +!14 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 32, align: 32, elements: !15) +!15 = !{!16} +!16 = !DISubrange(lowerBound: 1, upperBound: !17) +!17 = distinct !DILocalVariable(scope: !11, file: !3, type: !18, flags: DIFlagArtificial) +!18 = !DIBasicType(name: "integer*8", size: 64, align: 64, encoding: DW_ATE_signed) +!19 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 1024, align: 64, elements: !20) +!20 = !{!21} +!21 = !DISubrange(lowerBound: 1, upperBound: 16) diff --git a/llvm/test/Bitcode/generic_subrange_count.ll b/llvm/test/Bitcode/generic_subrange_count.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Bitcode/generic_subrange_count.ll @@ -0,0 +1,35 @@ +;; This test checks generation of DIGenericSubrange. + +; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s + +;; Test whether DIGenericSubrange is generated. +; CHECK: !DIGenericSubrange(count: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 88, DW_OP_plus, DW_OP_deref), lowerBound: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 80, DW_OP_plus, DW_OP_deref), stride: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 112, DW_OP_plus, DW_OP_deref, DW_OP_constu, 4, DW_OP_mul)) + +; ModuleID = 'generic_subrange.f90' +source_filename = "/dir/generic_subrange.ll" + +!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: "generic_subrange.f90", directory: "/dir") +!4 = !{} +!5 = !{!6} +!6 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 32, align: 32, elements: !8, dataLocation: !10, rank: !DIExpression(DW_OP_push_object_address, DW_OP_plus_uconst, 8, DW_OP_deref)) +!7 = !DIBasicType(name: "real", size: 32, align: 32, encoding: DW_ATE_float) +!8 = !{!9} +!9 = !DIGenericSubrange(count: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 88, DW_OP_plus, DW_OP_deref), lowerBound: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 80, DW_OP_plus, DW_OP_deref), stride: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 112, DW_OP_plus, DW_OP_deref, DW_OP_constu, 4, DW_OP_mul)) +!10 = distinct !DILocalVariable(scope: !11, file: !3, type: !18, flags: DIFlagArtificial) +!11 = distinct !DISubprogram(name: "sub1", scope: !2, file: !3, line: 1, type: !12, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2) +!12 = !DISubroutineType(types: !13) +!13 = !{null, !14, !19} +!14 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 32, align: 32, elements: !15) +!15 = !{!16} +!16 = !DISubrange(lowerBound: 1, upperBound: !17) +!17 = distinct !DILocalVariable(scope: !11, file: !3, type: !18, flags: DIFlagArtificial) +!18 = !DIBasicType(name: "integer*8", size: 64, align: 64, encoding: DW_ATE_signed) +!19 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 1024, align: 64, elements: !20) +!20 = !{!21} +!21 = !DISubrange(lowerBound: 1, upperBound: 16) diff --git a/llvm/test/DebugInfo/X86/dwarfdump-generic_subrange.ll b/llvm/test/DebugInfo/X86/dwarfdump-generic_subrange.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dwarfdump-generic_subrange.ll @@ -0,0 +1,65 @@ +;; This test checks whether DW_AT_rank attribute accepts DIExpression. + +; RUN: llc %s -filetype=obj -o - | llvm-dwarfdump - | FileCheck %s + +;; Test whether DW_AT_data_location is generated. +; CHECK-LABEL: DW_TAG_array_type + +; CHECK: DW_AT_rank (DW_OP_push_object_address, DW_OP_plus_uconst 0x8, DW_OP_deref) +; CHECK: DW_TAG_generic_subrange +; CHECK: DW_AT_lower_bound (DW_OP_push_object_address, DW_OP_over, DW_OP_constu 0x30, DW_OP_mul, DW_OP_plus_uconst 0x50, DW_OP_plus, DW_OP_deref) +; CHECK: DW_AT_upper_bound (DW_OP_push_object_address, DW_OP_over, DW_OP_constu 0x30, DW_OP_mul, DW_OP_plus_uconst 0x78, DW_OP_plus, DW_OP_deref) +; CHECK: DW_AT_byte_stride (DW_OP_push_object_address, DW_OP_over, DW_OP_constu 0x30, DW_OP_mul, DW_OP_plus_uconst 0x70, DW_OP_plus, DW_OP_deref, DW_OP_lit4, DW_OP_mul) + +;; Test case is hand written with the help of below testcase +;;------------------------------ +;;subroutine sub(arank) +;; real :: arank(..) +;; print *, RANK(arank) +;;end +;;------------------------------ + +; ModuleID = 'dwarfdump-generic_subrange.ll' +source_filename = "dwarfdump-generic_subrange.ll" + +define void @sub_(i64* noalias %arank, i64* noalias %"arank$sd") !dbg !5 { +L.entry: + call void @llvm.dbg.value(metadata i64* %arank, metadata !17, metadata !DIExpression()), !dbg !18 + call void @llvm.dbg.declare(metadata i64* %"arank$sd", metadata !19, metadata !DIExpression()), !dbg !18 + call void @llvm.dbg.declare(metadata i64* %"arank$sd", metadata !29, metadata !DIExpression()), !dbg !18 + 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: "generic_subrange.f90", directory: "/dir") +!4 = !{} +!5 = distinct !DISubprogram(name: "sub", scope: !2, file: !3, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2) +!6 = !DISubroutineType(types: !7) +!7 = !{null, !8, !14} +!8 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, size: 32, align: 32, elements: !10) +!9 = !DIBasicType(name: "real", size: 32, align: 32, encoding: DW_ATE_float) +!10 = !{!11} +!11 = !DISubrange(lowerBound: 1, upperBound: !12) +!12 = distinct !DILocalVariable(scope: !5, file: !3, type: !13, flags: DIFlagArtificial) +!13 = !DIBasicType(name: "integer*8", size: 64, align: 64, encoding: DW_ATE_signed) +!14 = !DICompositeType(tag: DW_TAG_array_type, baseType: !13, size: 1024, align: 64, elements: !15) +!15 = !{!16} +!16 = !DISubrange(lowerBound: 1, upperBound: 16) +!17 = distinct !DILocalVariable(scope: !5, file: !3, type: !13, flags: DIFlagArtificial) +!18 = !DILocation(line: 0, scope: !5) +!19 = !DILocalVariable(name: "arank", arg: 1, scope: !5, file: !3, line: 1, type: !20) +!20 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, size: 32, align: 32, elements: !21, dataLocation: !17, rank: !DIExpression(DW_OP_push_object_address, DW_OP_plus_uconst, 8, DW_OP_deref)) +!21 = !{!22} +!22 = !DIGenericSubrange(lowerBound: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 80, DW_OP_plus, DW_OP_deref), upperBound: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 120, DW_OP_plus, DW_OP_deref), stride: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 112, DW_OP_plus, DW_OP_deref, DW_OP_constu, 4, DW_OP_mul)) +!29 = !DILocalVariable(arg: 2, scope: !5, file: !3, line: 1, type: !14, flags: DIFlagArtificial) diff --git a/llvm/test/DebugInfo/X86/dwarfdump-generic_subrange_const.ll b/llvm/test/DebugInfo/X86/dwarfdump-generic_subrange_const.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dwarfdump-generic_subrange_const.ll @@ -0,0 +1,66 @@ +;; This test checks whether DW_AT_rank attribute accepts constants. +;; constants are interally stored as DIExpression. + +; RUN: llc %s -filetype=obj -o - | llvm-dwarfdump - | FileCheck %s + +;; Test whether DW_AT_data_location is generated. +; CHECK-LABEL: DW_TAG_array_type + +; CHECK: DW_AT_rank (DW_OP_push_object_address, DW_OP_plus_uconst 0x8, DW_OP_deref) +; CHECK: DW_TAG_generic_subrange +; CHECK: DW_AT_lower_bound (-20) +; CHECK: DW_AT_upper_bound (0) +; CHECK: DW_AT_byte_stride (4) + +;; Test case is hand written with the help of below testcase +;;------------------------------ +;;subroutine sub(arank) +;; real :: arank(..) +;; print *, RANK(arank) +;;end +;;------------------------------ + +; ModuleID = 'dwarfdump-subrange_const.ll' +source_filename = "dwarfdump-subrange_const.ll" + +define void @sub_(i64* noalias %arank, i64* noalias %"arank$sd") !dbg !5 { +L.entry: + call void @llvm.dbg.value(metadata i64* %arank, metadata !17, metadata !DIExpression()), !dbg !18 + call void @llvm.dbg.declare(metadata i64* %"arank$sd", metadata !19, metadata !DIExpression()), !dbg !18 + call void @llvm.dbg.declare(metadata i64* %"arank$sd", metadata !29, metadata !DIExpression()), !dbg !18 + 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: "generic_subrange_const.f90", directory: "/dir") +!4 = !{} +!5 = distinct !DISubprogram(name: "sub", scope: !2, file: !3, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2) +!6 = !DISubroutineType(types: !7) +!7 = !{null, !8, !14} +!8 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, size: 32, align: 32, elements: !10) +!9 = !DIBasicType(name: "real", size: 32, align: 32, encoding: DW_ATE_float) +!10 = !{!11} +!11 = !DISubrange(lowerBound: 1, upperBound: !12) +!12 = distinct !DILocalVariable(scope: !5, file: !3, type: !13, flags: DIFlagArtificial) +!13 = !DIBasicType(name: "integer*8", size: 64, align: 64, encoding: DW_ATE_signed) +!14 = !DICompositeType(tag: DW_TAG_array_type, baseType: !13, size: 1024, align: 64, elements: !15) +!15 = !{!16} +!16 = !DISubrange(lowerBound: 1, upperBound: 16) +!17 = distinct !DILocalVariable(scope: !5, file: !3, type: !13, flags: DIFlagArtificial) +!18 = !DILocation(line: 0, scope: !5) +!19 = !DILocalVariable(name: "arank", arg: 1, scope: !5, file: !3, line: 1, type: !20) +!20 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, size: 32, align: 32, elements: !21, dataLocation: !17, rank: !DIExpression(DW_OP_push_object_address, DW_OP_plus_uconst, 8, DW_OP_deref)) +!21 = !{!22} +!22 = !DIGenericSubrange(lowerBound: -20, upperBound: 0, stride: 4) +!29 = !DILocalVariable(arg: 2, scope: !5, file: !3, line: 1, type: !14, flags: DIFlagArtificial) diff --git a/llvm/test/DebugInfo/X86/dwarfdump-generic_subrange_count.ll b/llvm/test/DebugInfo/X86/dwarfdump-generic_subrange_count.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dwarfdump-generic_subrange_count.ll @@ -0,0 +1,65 @@ +;; This test checks whether DW_AT_rank attribute accepts DIExpression. + +; RUN: llc %s -filetype=obj -o - | llvm-dwarfdump - | FileCheck %s + +;; Test whether DW_AT_data_location is generated. +; CHECK-LABEL: DW_TAG_array_type + +; CHECK: DW_AT_rank (DW_OP_push_object_address, DW_OP_plus_uconst 0x8, DW_OP_deref) +; CHECK: DW_TAG_generic_subrange +; CHECK: DW_AT_lower_bound (DW_OP_push_object_address, DW_OP_over, DW_OP_constu 0x30, DW_OP_mul, DW_OP_plus_uconst 0x50, DW_OP_plus, DW_OP_deref) +; CHECK: DW_AT_count (DW_OP_push_object_address, DW_OP_over, DW_OP_constu 0x30, DW_OP_mul, DW_OP_plus_uconst 0x58, DW_OP_plus, DW_OP_deref) +; CHECK: DW_AT_byte_stride (DW_OP_push_object_address, DW_OP_over, DW_OP_constu 0x30, DW_OP_mul, DW_OP_plus_uconst 0x70, DW_OP_plus, DW_OP_deref, DW_OP_lit4, DW_OP_mul) + +;; Test case is hand written with the help of below testcase +;;------------------------------ +;;subroutine sub(arank) +;; real :: arank(..) +;; print *, RANK(arank) +;;end +;;------------------------------ + +; ModuleID = 'dwarfdump-rank.ll' +source_filename = "dwarfdump-rank.ll" + +define void @sub_(i64* noalias %arank, i64* noalias %"arank$sd") !dbg !5 { +L.entry: + call void @llvm.dbg.value(metadata i64* %arank, metadata !17, metadata !DIExpression()), !dbg !18 + call void @llvm.dbg.declare(metadata i64* %"arank$sd", metadata !19, metadata !DIExpression()), !dbg !18 + call void @llvm.dbg.declare(metadata i64* %"arank$sd", metadata !29, metadata !DIExpression()), !dbg !18 + 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: "generic_subrange.f90", directory: "/dir") +!4 = !{} +!5 = distinct !DISubprogram(name: "sub", scope: !2, file: !3, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2) +!6 = !DISubroutineType(types: !7) +!7 = !{null, !8, !14} +!8 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, size: 32, align: 32, elements: !10) +!9 = !DIBasicType(name: "real", size: 32, align: 32, encoding: DW_ATE_float) +!10 = !{!11} +!11 = !DISubrange(lowerBound: 1, upperBound: !12) +!12 = distinct !DILocalVariable(scope: !5, file: !3, type: !13, flags: DIFlagArtificial) +!13 = !DIBasicType(name: "integer*8", size: 64, align: 64, encoding: DW_ATE_signed) +!14 = !DICompositeType(tag: DW_TAG_array_type, baseType: !13, size: 1024, align: 64, elements: !15) +!15 = !{!16} +!16 = !DISubrange(lowerBound: 1, upperBound: 16) +!17 = distinct !DILocalVariable(scope: !5, file: !3, type: !13, flags: DIFlagArtificial) +!18 = !DILocation(line: 0, scope: !5) +!19 = !DILocalVariable(name: "arank", arg: 1, scope: !5, file: !3, line: 1, type: !20) +!20 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, size: 32, align: 32, elements: !21, dataLocation: !17, rank: !DIExpression(DW_OP_push_object_address, DW_OP_plus_uconst, 8, DW_OP_deref)) +!21 = !{!22} +!22 = !DIGenericSubrange(count: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 88, DW_OP_plus, DW_OP_deref), lowerBound: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 80, DW_OP_plus, DW_OP_deref), stride: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 112, DW_OP_plus, DW_OP_deref, DW_OP_constu, 4, DW_OP_mul)) +!29 = !DILocalVariable(arg: 2, scope: !5, file: !3, line: 1, type: !14, flags: DIFlagArtificial) diff --git a/llvm/test/Verifier/digenericsubrange-count-upperBound.ll b/llvm/test/Verifier/digenericsubrange-count-upperBound.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/digenericsubrange-count-upperBound.ll @@ -0,0 +1,5 @@ +; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s + +!named = !{!0} +; CHECK: GenericSubrange can have any one of count or upperBound +!0 = !DIGenericSubrange(count: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 88, DW_OP_plus, DW_OP_deref), lowerBound: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 80, DW_OP_plus, DW_OP_deref), upperBound: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 120, DW_OP_plus, DW_OP_deref), stride: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 112, DW_OP_plus, DW_OP_deref, DW_OP_constu, 4, DW_OP_mul)) diff --git a/llvm/test/Verifier/digenericsubrange-missing-stride.ll b/llvm/test/Verifier/digenericsubrange-missing-stride.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/digenericsubrange-missing-stride.ll @@ -0,0 +1,5 @@ +; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s + +!named = !{!0} +; CHECK: GenericSubrange must contain stride +!0 = !DIGenericSubrange(lowerBound: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 80, DW_OP_plus, DW_OP_deref), upperBound: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 120, DW_OP_plus, DW_OP_deref)) diff --git a/llvm/test/Verifier/digenericsubrange-missing-upperBound.ll b/llvm/test/Verifier/digenericsubrange-missing-upperBound.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/digenericsubrange-missing-upperBound.ll @@ -0,0 +1,5 @@ +; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s + +!named = !{!0} +; CHECK: GenericSubrange must contain count or upperBound +!0 = !DIGenericSubrange(lowerBound: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 80, DW_OP_plus, DW_OP_deref), stride: !DIExpression(DW_OP_push_object_address, DW_OP_over, DW_OP_constu, 48, DW_OP_mul, DW_OP_plus_uconst, 112, DW_OP_plus, DW_OP_deref, DW_OP_constu, 4, DW_OP_mul)) 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 @@ -10,6 +10,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DIBuilder.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/Function.h" @@ -1314,6 +1315,151 @@ EXPECT_NE(N, DISubrange::get(Context, nullptr, LVother, UE, SE)); } +typedef MetadataTest DIGenericSubrangeTest; + +TEST_F(DIGenericSubrangeTest, fortranAssumedRankInt) { + DILocalScope *Scope = getSubprogram(); + DIFile *File = getFile(); + DIType *Type = getDerivedType(); + DINode::DIFlags Flags = static_cast(7); + auto *LI = DIExpression::get( + Context, {dwarf::DW_OP_consts, static_cast(-10)}); + auto *UI = DIExpression::get(Context, {dwarf::DW_OP_consts, 10}); + auto *SI = DIExpression::get(Context, {dwarf::DW_OP_consts, 4}); + auto *UIother = DIExpression::get(Context, {dwarf::DW_OP_consts, 20}); + auto *UVother = DILocalVariable::get(Context, Scope, "ubother", File, 8, Type, + 2, Flags, 8); + auto *UEother = DIExpression::get(Context, {5, 6}); + auto *LIZero = DIExpression::get(Context, {dwarf::DW_OP_consts, 0}); + auto *UIZero = DIExpression::get(Context, {dwarf::DW_OP_consts, 0}); + + auto *N = DIGenericSubrange::get(Context, nullptr, LI, UI, SI); + + auto Lower = N->getLowerBound(); + ASSERT_TRUE(Lower); + ASSERT_TRUE(Lower.is()); + EXPECT_EQ(dyn_cast_or_null(LI), Lower.get()); + + auto Upper = N->getUpperBound(); + ASSERT_TRUE(Upper); + ASSERT_TRUE(Upper.is()); + EXPECT_EQ(dyn_cast_or_null(UI), Upper.get()); + + auto Stride = N->getStride(); + ASSERT_TRUE(Stride); + ASSERT_TRUE(Stride.is()); + EXPECT_EQ(dyn_cast_or_null(SI), Stride.get()); + + EXPECT_EQ(N, DIGenericSubrange::get(Context, nullptr, LI, UI, SI)); + + EXPECT_NE(N, DIGenericSubrange::get(Context, nullptr, LI, UIother, SI)); + EXPECT_NE(N, DIGenericSubrange::get(Context, nullptr, LI, UEother, SI)); + EXPECT_NE(N, DIGenericSubrange::get(Context, nullptr, LI, UVother, SI)); + + auto *NZeroLower = DIGenericSubrange::get(Context, nullptr, LIZero, UI, SI); + EXPECT_NE(NZeroLower, + DIGenericSubrange::get(Context, nullptr, nullptr, UI, SI)); + + auto *NZeroUpper = DIGenericSubrange::get(Context, nullptr, LI, UIZero, SI); + EXPECT_NE(NZeroUpper, + DIGenericSubrange::get(Context, nullptr, LI, nullptr, SI)); +} + +TEST_F(DIGenericSubrangeTest, fortranAssumedRankVar) { + 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 = DIExpression::get( + Context, {dwarf::DW_OP_consts, static_cast(-1)}); + auto *SEother = DIExpression::get(Context, {5, 6}); + + auto *N = DIGenericSubrange::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, DIGenericSubrange::get(Context, nullptr, LV, UV, SV)); + + EXPECT_NE(N, DIGenericSubrange::get(Context, nullptr, LV, UV, SVother)); + EXPECT_NE(N, DIGenericSubrange::get(Context, nullptr, LV, UV, SEother)); + EXPECT_NE(N, DIGenericSubrange::get(Context, nullptr, LV, UV, SIother)); +} + +TEST_F(DIGenericSubrangeTest, useDIBuilder) { + 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 *UE = DIExpression::get(Context, {2, 3}); + auto *SE = DIExpression::get(Context, {3, 4}); + + auto *LVother = DILocalVariable::get(Context, Scope, "lbother", File, 8, Type, + 2, Flags, 8); + auto *LIother = DIExpression::get( + Context, {dwarf::DW_OP_consts, static_cast(-1)}); + + Module M("M", Context); + DIBuilder DIB(M); + + auto *N = DIB.getOrCreateGenericSubrange( + DIGenericSubrange::BoundType(nullptr), DIGenericSubrange::BoundType(LV), + DIGenericSubrange::BoundType(UE), DIGenericSubrange::BoundType(SE)); + + 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(UE, Upper.get()); + + auto Stride = N->getStride(); + ASSERT_TRUE(Stride); + ASSERT_TRUE(Stride.is()); + EXPECT_EQ(SE, Stride.get()); + + EXPECT_EQ( + N, DIB.getOrCreateGenericSubrange(DIGenericSubrange::BoundType(nullptr), + DIGenericSubrange::BoundType(LV), + DIGenericSubrange::BoundType(UE), + DIGenericSubrange::BoundType(SE))); + + EXPECT_NE( + N, DIB.getOrCreateGenericSubrange(DIGenericSubrange::BoundType(nullptr), + DIGenericSubrange::BoundType(LVother), + DIGenericSubrange::BoundType(UE), + DIGenericSubrange::BoundType(SE))); + EXPECT_NE( + N, DIB.getOrCreateGenericSubrange(DIGenericSubrange::BoundType(nullptr), + DIGenericSubrange::BoundType(LIother), + DIGenericSubrange::BoundType(UE), + DIGenericSubrange::BoundType(SE))); +} typedef MetadataTest DIEnumeratorTest; TEST_F(DIEnumeratorTest, get) {