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 @@ -742,9 +742,14 @@ /// definitions as they would appear on a command line. /// \param IncludePath The path to the module map file. /// \param APINotesFile The path to an API notes file for this module. + /// \param File Source file where Fortran module is declared(Not + /// applicable otherwise). + /// \param LineNo Line number at which Fortran module is declared(Not + /// applicable otherwise). DIModule *createModule(DIScope *Scope, StringRef Name, - StringRef ConfigurationMacros, - StringRef IncludePath, StringRef APINotesFile = {}); + StringRef ConfigurationMacros, StringRef IncludePath, + StringRef APINotesFile = {}, DIFile *File = {}, + unsigned LineNo = {}); /// This creates a descriptor for a lexical block with a new file /// attached. This merely extends the existing 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 @@ -2078,50 +2078,56 @@ } }; -/// A (clang) module that has been imported by the compile unit. -/// +/// DIModlule can represent a clang module or a Fortran module. +/// Fortran module representation can be used by a Fortran FE +/// like flang. class DIModule : public DIScope { friend class LLVMContextImpl; friend class MDNode; + unsigned LineNo; - DIModule(LLVMContext &Context, StorageType Storage, ArrayRef Ops) - : DIScope(Context, DIModuleKind, Storage, dwarf::DW_TAG_module, Ops) {} + DIModule(LLVMContext &Context, StorageType Storage, unsigned LineNo, + ArrayRef Ops) + : DIScope(Context, DIModuleKind, Storage, dwarf::DW_TAG_module, Ops), + LineNo(LineNo) {} ~DIModule() = default; static DIModule *getImpl(LLVMContext &Context, DIScope *Scope, StringRef Name, StringRef ConfigurationMacros, StringRef IncludePath, - StringRef APINotesFile, StorageType Storage, + StringRef APINotesFile, DIFile *File, + unsigned LineNo, StorageType Storage, bool ShouldCreate = true) { return getImpl(Context, Scope, getCanonicalMDString(Context, Name), getCanonicalMDString(Context, ConfigurationMacros), getCanonicalMDString(Context, IncludePath), - getCanonicalMDString(Context, APINotesFile), + getCanonicalMDString(Context, APINotesFile), File, LineNo, Storage, ShouldCreate); } static DIModule *getImpl(LLVMContext &Context, Metadata *Scope, MDString *Name, MDString *ConfigurationMacros, MDString *IncludePath, MDString *APINotesFile, - StorageType Storage, bool ShouldCreate = true); + Metadata *File, unsigned LineNo, StorageType Storage, + bool ShouldCreate = true); TempDIModule cloneImpl() const { return getTemporary(getContext(), getScope(), getName(), getConfigurationMacros(), getIncludePath(), - getAPINotesFile()); + getAPINotesFile(), getFile(), getLineNo()); } public: DEFINE_MDNODE_GET(DIModule, (DIScope * Scope, StringRef Name, StringRef ConfigurationMacros, StringRef IncludePath, - StringRef APINotesFile), + StringRef APINotesFile, DIFile *File, unsigned LineNo), (Scope, Name, ConfigurationMacros, IncludePath, - APINotesFile)) + APINotesFile, File, LineNo)) DEFINE_MDNODE_GET(DIModule, (Metadata * Scope, MDString *Name, MDString *ConfigurationMacros, MDString *IncludePath, - MDString *APINotesFile), + MDString *APINotesFile, Metadata *File, unsigned LineNo), (Scope, Name, ConfigurationMacros, IncludePath, - APINotesFile)) + APINotesFile, File, LineNo)) TempDIModule clone() const { return cloneImpl(); } @@ -2130,12 +2136,15 @@ StringRef getConfigurationMacros() const { return getStringOperand(2); } StringRef getIncludePath() const { return getStringOperand(3); } StringRef getAPINotesFile() const { return getStringOperand(4); } + DIFile *getFile() const { return cast_or_null(getRawFile()); } + unsigned getLineNo() const { return LineNo; } Metadata *getRawScope() const { return getOperand(0); } MDString *getRawName() const { return getOperandAs(1); } MDString *getRawConfigurationMacros() const { return getOperandAs(2); } MDString *getRawIncludePath() const { return getOperandAs(3); } MDString *getRawAPINotesFile() const { return getOperandAs(4); } + Metadata *getRawFile() const { return getOperand(5); } static bool classof(const Metadata *MD) { return MD->getMetadataID() == DIModuleKind; 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 @@ -4828,20 +4828,23 @@ /// ParseDIModule: /// ::= !DIModule(scope: !0, name: "SomeModule", configMacros: "-DNDEBUG", -/// includePath: "/usr/include", apinotes: "module.apinotes") +/// includePath: "/usr/include", apinotes: "module.apinotes", +/// file: !1, line: 4) bool LLParser::ParseDIModule(MDNode *&Result, bool IsDistinct) { #define VISIT_MD_FIELDS(OPTIONAL, REQUIRED) \ REQUIRED(scope, MDField, ); \ REQUIRED(name, MDStringField, ); \ OPTIONAL(configMacros, MDStringField, ); \ OPTIONAL(includePath, MDStringField, ); \ - OPTIONAL(apinotes, MDStringField, ); + OPTIONAL(apinotes, MDStringField, ); \ + OPTIONAL(file, MDField, ); \ + OPTIONAL(line, LineField, ); PARSE_MD_FIELDS(); #undef VISIT_MD_FIELDS - Result = - GET_OR_DISTINCT(DIModule, (Context, scope.Val, name.Val, configMacros.Val, - includePath.Val, apinotes.Val)); + Result = GET_OR_DISTINCT(DIModule, + (Context, scope.Val, name.Val, configMacros.Val, + includePath.Val, apinotes.Val, file.Val, line.Val)); 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 @@ -1418,7 +1418,7 @@ } case bitc::METADATA_MODULE: { - if (Record.size() < 5 || Record.size() > 7) + if (Record.size() < 5 || Record.size() > 8) return error("Invalid record"); IsDistinct = Record[0]; @@ -1426,7 +1426,8 @@ GET_OR_DISTINCT(DIModule, (Context, getMDOrNull(Record[1]), getMDString(Record[2]), getMDString(Record[3]), - getMDString(Record[4]), getMDString(Record[5]))), + getMDString(Record[4]), getMDString(Record[5]), + getMDOrNull(Record[6]), Record[7])), NextMetadataNo); NextMetadataNo++; break; 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 @@ -1785,6 +1785,7 @@ Record.push_back(N->isDistinct()); for (auto &I : N->operands()) Record.push_back(VE.getMetadataOrNullID(I)); + Record.push_back(N->getLineNo()); Stream.EmitRecord(bitc::METADATA_MODULE, Record, Abbrev); Record.clear(); 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 @@ -1128,6 +1128,11 @@ addString(MDie, dwarf::DW_AT_LLVM_include_path, M->getIncludePath()); if (!M->getAPINotesFile().empty()) addString(MDie, dwarf::DW_AT_LLVM_apinotes, M->getAPINotesFile()); + if (M->getFile()) + addUInt(MDie, dwarf::DW_AT_decl_file, None, + getOrCreateSourceID(M->getFile())); + if (M->getLineNo()) + addUInt(MDie, dwarf::DW_AT_decl_line, None, M->getLineNo()); return &MDie; } 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 @@ -2092,6 +2092,8 @@ Printer.printString("configMacros", N->getConfigurationMacros()); Printer.printString("includePath", N->getIncludePath()); Printer.printString("apinotes", N->getAPINotesFile()); + Printer.printMetadata("file", N->getRawFile()); + Printer.printInt("line", N->getLineNo()); 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 @@ -831,10 +831,11 @@ DIModule *DIBuilder::createModule(DIScope *Scope, StringRef Name, StringRef ConfigurationMacros, - StringRef IncludePath, - StringRef APINotesFile) { + StringRef IncludePath, StringRef APINotesFile, + DIFile *File, unsigned LineNo) { return DIModule::get(VMContext, getNonCompileUnitScope(Scope), Name, - ConfigurationMacros, IncludePath, APINotesFile); + ConfigurationMacros, IncludePath, APINotesFile, File, + LineNo); } DILexicalBlockFile *DIBuilder::createLexicalBlockFile(DIScope *Scope, 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 @@ -720,13 +720,14 @@ DIModule *DIModule::getImpl(LLVMContext &Context, Metadata *Scope, MDString *Name, MDString *ConfigurationMacros, MDString *IncludePath, MDString *APINotesFile, + Metadata *File, unsigned LineNo, StorageType Storage, bool ShouldCreate) { assert(isCanonical(Name) && "Expected canonical MDString"); - DEFINE_GETIMPL_LOOKUP( - DIModule, (Scope, Name, ConfigurationMacros, IncludePath, APINotesFile)); - Metadata *Ops[] = {Scope, Name, ConfigurationMacros, IncludePath, - APINotesFile}; - DEFINE_GETIMPL_STORE_NO_CONSTRUCTOR_ARGS(DIModule, Ops); + DEFINE_GETIMPL_LOOKUP(DIModule, (Scope, Name, ConfigurationMacros, + IncludePath, APINotesFile, File, LineNo)); + Metadata *Ops[] = {Scope, Name, ConfigurationMacros, + IncludePath, APINotesFile, File}; + DEFINE_GETIMPL_STORE(DIModule, (LineNo), Ops); } DITemplateTypeParameter * 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 @@ -820,26 +820,33 @@ MDString *ConfigurationMacros; MDString *IncludePath; MDString *APINotesFile; + Metadata *File; + unsigned LineNo; MDNodeKeyImpl(Metadata *Scope, MDString *Name, MDString *ConfigurationMacros, - MDString *IncludePath, MDString *APINotesFile) + MDString *IncludePath, MDString *APINotesFile, Metadata *File, + unsigned LineNo) : Scope(Scope), Name(Name), ConfigurationMacros(ConfigurationMacros), - IncludePath(IncludePath), APINotesFile(APINotesFile) {} + IncludePath(IncludePath), APINotesFile(APINotesFile), File(File), + LineNo(LineNo) {} MDNodeKeyImpl(const DIModule *N) : Scope(N->getRawScope()), Name(N->getRawName()), ConfigurationMacros(N->getRawConfigurationMacros()), IncludePath(N->getRawIncludePath()), - APINotesFile(N->getRawAPINotesFile()) {} + APINotesFile(N->getRawAPINotesFile()), File(N->getRawFile()), + LineNo(N->getLineNo()) {} bool isKeyOf(const DIModule *RHS) const { return Scope == RHS->getRawScope() && Name == RHS->getRawName() && ConfigurationMacros == RHS->getRawConfigurationMacros() && IncludePath == RHS->getRawIncludePath() && - APINotesFile == RHS->getRawAPINotesFile(); + APINotesFile == RHS->getRawAPINotesFile() && + File == RHS->getRawFile() && LineNo == RHS->getLineNo(); } unsigned getHashValue() const { - return hash_combine(Scope, Name, ConfigurationMacros, IncludePath); + return hash_combine(Scope, Name, ConfigurationMacros, IncludePath, File, + LineNo); } }; diff --git a/llvm/test/DebugInfo/X86/Fortran-DIModule.ll b/llvm/test/DebugInfo/X86/Fortran-DIModule.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/Fortran-DIModule.ll @@ -0,0 +1,44 @@ +; This test checks attributes of a Fortran module. +; RUN: %llc_dwarf %s -filetype=obj -o - | \ +; RUN: llvm-dwarfdump - | FileCheck %s + +; CHECK: DW_TAG_module +; CHECK-NEXT: DW_AT_name ("dummy") +; CHECK-NEXT: DW_AT_decl_file ("/fortran/module.f90") +; CHECK-NEXT: DW_AT_decl_line (2) + +; Generated from flang compiler, Fortran source to regenerate: +; module dummy +; integer :: foo +; end module dummy + +; ModuleID = '/tmp/module-b198fa.ll' +source_filename = "/tmp/module-b198fa.ll" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct_dummy_0_ = type <{ [4 x i8] }> + +@_dummy_0_ = common global %struct_dummy_0_ zeroinitializer, align 64, !dbg !0 + +; Function Attrs: noinline +define float @dummy_() #0 { +.L.entry: + ret float undef +} + +attributes #0 = { noinline "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" } + +!llvm.module.flags = !{!8, !9} +!llvm.dbg.cu = !{!3} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "foo", scope: !2, file: !4, type: !7, isLocal: false, isDefinition: true) +!2 = !DIModule(scope: !3, name: "dummy", file: !4, line: 2) +!3 = distinct !DICompileUnit(language: DW_LANG_Fortran90, file: !4, producer: " F90 Flang - 1.5 2017-05-01", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5, retainedTypes: !5, globals: !6, imports: !5) +!4 = !DIFile(filename: "module.f90", directory: "/fortran") +!5 = !{} +!6 = !{!0} +!7 = !DIBasicType(name: "integer", size: 32, align: 32, encoding: DW_ATE_signed) +!8 = !{i32 2, !"Dwarf Version", i32 4} +!9 = !{i32 2, !"Debug Info Version", i32 3} 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 @@ -2058,8 +2058,11 @@ StringRef ConfigMacro = "-DNDEBUG"; StringRef Includes = "-I."; StringRef APINotes = "/tmp/m.apinotes"; + DIFile *File = getFile(); + unsigned LineNo = {}; - auto *N = DIModule::get(Context, Scope, Name, ConfigMacro, Includes, APINotes); + auto *N = DIModule::get(Context, Scope, Name, ConfigMacro, Includes, APINotes, + File, LineNo); EXPECT_EQ(dwarf::DW_TAG_module, N->getTag()); EXPECT_EQ(Scope, N->getScope()); @@ -2067,18 +2070,20 @@ EXPECT_EQ(ConfigMacro, N->getConfigurationMacros()); EXPECT_EQ(Includes, N->getIncludePath()); EXPECT_EQ(APINotes, N->getAPINotesFile()); - EXPECT_EQ( - N, DIModule::get(Context, Scope, Name, ConfigMacro, Includes, APINotes)); + EXPECT_EQ(File, N->getFile()); + EXPECT_EQ(LineNo, N->getLineNo()); + EXPECT_EQ(N, DIModule::get(Context, Scope, Name, ConfigMacro, Includes, + APINotes, File, LineNo)); EXPECT_NE(N, DIModule::get(Context, getFile(), Name, ConfigMacro, Includes, - APINotes)); + APINotes, File, LineNo)); EXPECT_NE(N, DIModule::get(Context, Scope, "other", ConfigMacro, Includes, - APINotes)); - EXPECT_NE(N, - DIModule::get(Context, Scope, Name, "other", Includes, APINotes)); - EXPECT_NE( - N, DIModule::get(Context, Scope, Name, ConfigMacro, "other", APINotes)); - EXPECT_NE( - N, DIModule::get(Context, Scope, Name, ConfigMacro, Includes, "other")); + APINotes, File, LineNo)); + EXPECT_NE(N, DIModule::get(Context, Scope, Name, "other", Includes, APINotes, + File, LineNo)); + EXPECT_NE(N, DIModule::get(Context, Scope, Name, ConfigMacro, "other", + APINotes, File, LineNo)); + EXPECT_NE(N, DIModule::get(Context, Scope, Name, ConfigMacro, Includes, + "other", File, LineNo)); TempDIModule Temp = N->clone(); EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));