| 1 | //===- DILineTableFromLocations.cpp - -------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "mlir/Dialect/LLVMIR/Transforms/Passes.h" |
| 10 | |
| 11 | #include "mlir/Dialect/LLVMIR/LLVMDialect.h" |
| 12 | #include "llvm/BinaryFormat/Dwarf.h" |
| 13 | #include "llvm/Support/Path.h" |
| 14 | |
| 15 | namespace mlir { |
| 16 | namespace LLVM { |
| 17 | #define GEN_PASS_DEF_DISCOPEFORLLVMFUNCOPPASS |
| 18 | #include "mlir/Dialect/LLVMIR/Transforms/Passes.h.inc" |
| 19 | } // namespace LLVM |
| 20 | } // namespace mlir |
| 21 | |
| 22 | using namespace mlir; |
| 23 | |
| 24 | /// Attempt to extract a filename for the given loc. |
| 25 | static FileLineColLoc (Location loc) { |
| 26 | if (auto fileLoc = dyn_cast<FileLineColLoc>(Val&: loc)) |
| 27 | return fileLoc; |
| 28 | if (auto nameLoc = dyn_cast<NameLoc>(Val&: loc)) |
| 29 | return extractFileLoc(loc: nameLoc.getChildLoc()); |
| 30 | if (auto opaqueLoc = dyn_cast<OpaqueLoc>(Val&: loc)) |
| 31 | return extractFileLoc(loc: opaqueLoc.getFallbackLocation()); |
| 32 | if (auto fusedLoc = dyn_cast<FusedLoc>(Val&: loc)) { |
| 33 | for (auto loc : fusedLoc.getLocations()) { |
| 34 | if (auto fileLoc = extractFileLoc(loc)) |
| 35 | return fileLoc; |
| 36 | } |
| 37 | } |
| 38 | if (auto callerLoc = dyn_cast<CallSiteLoc>(Val&: loc)) |
| 39 | return extractFileLoc(loc: callerLoc.getCaller()); |
| 40 | return FileLineColLoc(); |
| 41 | } |
| 42 | |
| 43 | /// Creates a DISubprogramAttr with the provided compile unit and attaches it |
| 44 | /// to the function. Does nothing when the function already has an attached |
| 45 | /// subprogram. |
| 46 | static void addScopeToFunction(LLVM::LLVMFuncOp llvmFunc, |
| 47 | LLVM::DICompileUnitAttr compileUnitAttr) { |
| 48 | |
| 49 | Location loc = llvmFunc.getLoc(); |
| 50 | if (loc->findInstanceOf<FusedLocWith<LLVM::DISubprogramAttr>>()) |
| 51 | return; |
| 52 | |
| 53 | MLIRContext *context = llvmFunc->getContext(); |
| 54 | |
| 55 | // Filename and line associate to the function. |
| 56 | LLVM::DIFileAttr fileAttr; |
| 57 | int64_t line = 1; |
| 58 | if (FileLineColLoc fileLoc = extractFileLoc(loc)) { |
| 59 | line = fileLoc.getLine(); |
| 60 | StringRef inputFilePath = fileLoc.getFilename().getValue(); |
| 61 | fileAttr = |
| 62 | LLVM::DIFileAttr::get(context, name: llvm::sys::path::filename(path: inputFilePath), |
| 63 | directory: llvm::sys::path::parent_path(path: inputFilePath)); |
| 64 | } else { |
| 65 | fileAttr = compileUnitAttr |
| 66 | ? compileUnitAttr.getFile() |
| 67 | : LLVM::DIFileAttr::get(context, name: "<unknown>" , directory: "" ); |
| 68 | } |
| 69 | auto subroutineTypeAttr = |
| 70 | LLVM::DISubroutineTypeAttr::get(context, callingConvention: llvm::dwarf::DW_CC_normal, types: {}); |
| 71 | |
| 72 | // Figure out debug information (`subprogramFlags` and `compileUnitAttr`) to |
| 73 | // attach to the function definition / declaration. External functions are |
| 74 | // declarations only and are defined in a different compile unit, so mark |
| 75 | // them appropriately in `subprogramFlags` and set an empty `compileUnitAttr`. |
| 76 | DistinctAttr id; |
| 77 | auto subprogramFlags = LLVM::DISubprogramFlags::Optimized; |
| 78 | if (!llvmFunc.isExternal()) { |
| 79 | id = DistinctAttr::create(referencedAttr: UnitAttr::get(context)); |
| 80 | subprogramFlags = subprogramFlags | LLVM::DISubprogramFlags::Definition; |
| 81 | } else { |
| 82 | compileUnitAttr = {}; |
| 83 | } |
| 84 | auto funcNameAttr = llvmFunc.getNameAttr(); |
| 85 | auto subprogramAttr = LLVM::DISubprogramAttr::get( |
| 86 | context, id, compileUnit: compileUnitAttr, scope: fileAttr, name: funcNameAttr, linkageName: funcNameAttr, |
| 87 | file: fileAttr, |
| 88 | /*line=*/line, /*scopeLine=*/line, subprogramFlags, type: subroutineTypeAttr, |
| 89 | /*retainedNodes=*/{}, /*annotations=*/{}); |
| 90 | llvmFunc->setLoc(FusedLoc::get(context, locations: {loc}, metadata: subprogramAttr)); |
| 91 | } |
| 92 | |
| 93 | // Get a nested loc for inlined functions. |
| 94 | static Location getNestedLoc(Operation *op, LLVM::DIScopeAttr scopeAttr, |
| 95 | Location calleeLoc) { |
| 96 | auto calleeFileName = extractFileLoc(loc: calleeLoc).getFilename(); |
| 97 | auto *context = op->getContext(); |
| 98 | LLVM::DIFileAttr calleeFileAttr = |
| 99 | LLVM::DIFileAttr::get(context, name: llvm::sys::path::filename(path: calleeFileName), |
| 100 | directory: llvm::sys::path::parent_path(path: calleeFileName)); |
| 101 | auto lexicalBlockFileAttr = LLVM::DILexicalBlockFileAttr::get( |
| 102 | context, scope: scopeAttr, file: calleeFileAttr, /*discriminator=*/0); |
| 103 | Location loc = calleeLoc; |
| 104 | // Recurse if the callee location is again a call site. |
| 105 | if (auto callSiteLoc = dyn_cast<CallSiteLoc>(Val&: calleeLoc)) { |
| 106 | auto nestedLoc = callSiteLoc.getCallee(); |
| 107 | loc = getNestedLoc(op, scopeAttr: lexicalBlockFileAttr, calleeLoc: nestedLoc); |
| 108 | } |
| 109 | return FusedLoc::get(context, locations: {loc}, metadata: lexicalBlockFileAttr); |
| 110 | } |
| 111 | |
| 112 | static void setLexicalBlockFileAttr(Operation *op) { |
| 113 | if (auto callSiteLoc = dyn_cast<CallSiteLoc>(Val: op->getLoc())) { |
| 114 | auto callerLoc = callSiteLoc.getCaller(); |
| 115 | auto calleeLoc = callSiteLoc.getCallee(); |
| 116 | LLVM::DIScopeAttr scopeAttr; |
| 117 | // We assemble the full inline stack so the parent of this loc must be a |
| 118 | // function |
| 119 | auto funcOp = op->getParentOfType<LLVM::LLVMFuncOp>(); |
| 120 | if (auto funcOpLoc = llvm::dyn_cast_if_present<FusedLoc>(Val: funcOp.getLoc())) { |
| 121 | scopeAttr = cast<LLVM::DISubprogramAttr>(Val: funcOpLoc.getMetadata()); |
| 122 | op->setLoc( |
| 123 | CallSiteLoc::get(callee: getNestedLoc(op, scopeAttr, calleeLoc), caller: callerLoc)); |
| 124 | } |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | namespace { |
| 129 | /// Add a debug info scope to LLVMFuncOp that are missing it. |
| 130 | struct DIScopeForLLVMFuncOpPass |
| 131 | : public LLVM::impl::DIScopeForLLVMFuncOpPassBase< |
| 132 | DIScopeForLLVMFuncOpPass> { |
| 133 | using Base::Base; |
| 134 | |
| 135 | void runOnOperation() override { |
| 136 | ModuleOp module = getOperation(); |
| 137 | Location loc = module.getLoc(); |
| 138 | |
| 139 | MLIRContext *context = &getContext(); |
| 140 | if (!context->getLoadedDialect<LLVM::LLVMDialect>()) { |
| 141 | emitError(loc, message: "LLVM dialect is not loaded." ); |
| 142 | return signalPassFailure(); |
| 143 | } |
| 144 | |
| 145 | // Find a DICompileUnitAttr attached to a parent (the module for example), |
| 146 | // otherwise create a default one. |
| 147 | LLVM::DICompileUnitAttr compileUnitAttr; |
| 148 | if (auto fusedCompileUnitAttr = |
| 149 | module->getLoc() |
| 150 | ->findInstanceOf<FusedLocWith<LLVM::DICompileUnitAttr>>()) { |
| 151 | compileUnitAttr = fusedCompileUnitAttr.getMetadata(); |
| 152 | } else { |
| 153 | LLVM::DIFileAttr fileAttr; |
| 154 | if (FileLineColLoc fileLoc = extractFileLoc(loc)) { |
| 155 | StringRef inputFilePath = fileLoc.getFilename().getValue(); |
| 156 | fileAttr = LLVM::DIFileAttr::get( |
| 157 | context, name: llvm::sys::path::filename(path: inputFilePath), |
| 158 | directory: llvm::sys::path::parent_path(path: inputFilePath)); |
| 159 | } else { |
| 160 | fileAttr = LLVM::DIFileAttr::get(context, name: "<unknown>" , directory: "" ); |
| 161 | } |
| 162 | |
| 163 | compileUnitAttr = LLVM::DICompileUnitAttr::get( |
| 164 | id: DistinctAttr::create(referencedAttr: UnitAttr::get(context)), sourceLanguage: llvm::dwarf::DW_LANG_C, |
| 165 | file: fileAttr, producer: StringAttr::get(context, bytes: "MLIR" ), |
| 166 | /*isOptimized=*/true, emissionKind); |
| 167 | } |
| 168 | |
| 169 | module.walk<WalkOrder::PreOrder>(callback: [&](Operation *op) -> void { |
| 170 | if (auto funcOp = dyn_cast<LLVM::LLVMFuncOp>(Val: op)) { |
| 171 | // Create subprograms for each function with the same distinct compile |
| 172 | // unit. |
| 173 | addScopeToFunction(llvmFunc: funcOp, compileUnitAttr); |
| 174 | } else { |
| 175 | setLexicalBlockFileAttr(op); |
| 176 | } |
| 177 | }); |
| 178 | } |
| 179 | }; |
| 180 | |
| 181 | } // end anonymous namespace |
| 182 | |