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