225 lines · cpp
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-exception6//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 15namespace mlir {16namespace LLVM {17#define GEN_PASS_DEF_DISCOPEFORLLVMFUNCOPPASS18#include "mlir/Dialect/LLVMIR/Transforms/Passes.h.inc"19} // namespace LLVM20} // namespace mlir21 22using namespace mlir;23 24/// Attempt to extract a filename for the given loc.25static FileLineColLoc extractFileLoc(Location loc) {26 if (auto fileLoc = dyn_cast<FileLineColLoc>(loc))27 return fileLoc;28 if (auto nameLoc = dyn_cast<NameLoc>(loc))29 return extractFileLoc(nameLoc.getChildLoc());30 if (auto opaqueLoc = dyn_cast<OpaqueLoc>(loc))31 return extractFileLoc(opaqueLoc.getFallbackLocation());32 if (auto fusedLoc = dyn_cast<FusedLoc>(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>(loc))39 return extractFileLoc(callerLoc.getCaller());40 return FileLineColLoc();41}42 43/// Creates a DISubprogramAttr with the provided compile unit and attaches it44/// to the function. Does nothing when the function already has an attached45/// subprogram.46static 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, llvm::sys::path::filename(inputFilePath),63 llvm::sys::path::parent_path(inputFilePath));64 } else {65 fileAttr = compileUnitAttr66 ? compileUnitAttr.getFile()67 : LLVM::DIFileAttr::get(context, "<unknown>", "");68 }69 auto subroutineTypeAttr =70 LLVM::DISubroutineTypeAttr::get(context, llvm::dwarf::DW_CC_normal, {});71 72 // Figure out debug information (`subprogramFlags` and `compileUnitAttr`) to73 // attach to the function definition / declaration. External functions are74 // declarations only and are defined in a different compile unit, so mark75 // 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(UnitAttr::get(context));80 subprogramFlags |= LLVM::DISubprogramFlags::Definition;81 } else {82 compileUnitAttr = {};83 }84 auto funcNameAttr = llvmFunc.getNameAttr();85 auto subprogramAttr = LLVM::DISubprogramAttr::get(86 context, id, compileUnitAttr, fileAttr, funcNameAttr, funcNameAttr,87 fileAttr,88 /*line=*/line, /*scopeLine=*/line, subprogramFlags, subroutineTypeAttr,89 /*retainedNodes=*/{}, /*annotations=*/{});90 llvmFunc->setLoc(FusedLoc::get(context, {loc}, subprogramAttr));91}92 93// Get a nested loc for inlined functions.94static Location getNestedLoc(Operation *op, LLVM::DIScopeAttr scopeAttr,95 Location calleeLoc) {96 auto calleeFileName = extractFileLoc(calleeLoc).getFilename();97 auto *context = op->getContext();98 LLVM::DIFileAttr calleeFileAttr =99 LLVM::DIFileAttr::get(context, llvm::sys::path::filename(calleeFileName),100 llvm::sys::path::parent_path(calleeFileName));101 auto lexicalBlockFileAttr = LLVM::DILexicalBlockFileAttr::get(102 context, scopeAttr, 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>(calleeLoc)) {106 auto nestedLoc = callSiteLoc.getCallee();107 loc = getNestedLoc(op, lexicalBlockFileAttr, nestedLoc);108 }109 return FusedLoc::get(context, {loc}, lexicalBlockFileAttr);110}111 112/// Adds DILexicalBlockFileAttr for operations with CallSiteLoc and operations113/// from different files than their containing function.114static void setLexicalBlockFileAttr(Operation *op) {115 Location opLoc = op->getLoc();116 117 if (auto callSiteLoc = dyn_cast<CallSiteLoc>(opLoc)) {118 auto callerLoc = callSiteLoc.getCaller();119 auto calleeLoc = callSiteLoc.getCallee();120 LLVM::DIScopeAttr scopeAttr;121 // We assemble the full inline stack so the parent of this loc must be a122 // function123 auto funcOp = op->getParentOfType<LLVM::LLVMFuncOp>();124 if (auto funcOpLoc = llvm::dyn_cast_if_present<FusedLoc>(funcOp.getLoc())) {125 scopeAttr = cast<LLVM::DISubprogramAttr>(funcOpLoc.getMetadata());126 op->setLoc(127 CallSiteLoc::get(getNestedLoc(op, scopeAttr, calleeLoc), callerLoc));128 }129 130 return;131 }132 133 auto funcOp = op->getParentOfType<LLVM::LLVMFuncOp>();134 if (!funcOp)135 return;136 137 FileLineColLoc opFileLoc = extractFileLoc(opLoc);138 if (!opFileLoc)139 return;140 141 FileLineColLoc funcFileLoc = extractFileLoc(funcOp.getLoc());142 if (!funcFileLoc)143 return;144 145 StringRef opFile = opFileLoc.getFilename().getValue();146 StringRef funcFile = funcFileLoc.getFilename().getValue();147 148 // Handle cross-file operations: add DILexicalBlockFileAttr when the149 // operation's source file differs from its containing function.150 if (opFile != funcFile) {151 auto funcOpLoc = llvm::dyn_cast_if_present<FusedLoc>(funcOp.getLoc());152 if (!funcOpLoc)153 return;154 auto scopeAttr = dyn_cast<LLVM::DISubprogramAttr>(funcOpLoc.getMetadata());155 if (!scopeAttr)156 return;157 158 auto *context = op->getContext();159 LLVM::DIFileAttr opFileAttr =160 LLVM::DIFileAttr::get(context, llvm::sys::path::filename(opFile),161 llvm::sys::path::parent_path(opFile));162 163 LLVM::DILexicalBlockFileAttr lexicalBlockFileAttr =164 LLVM::DILexicalBlockFileAttr::get(context, scopeAttr, opFileAttr, 0);165 166 Location newLoc = FusedLoc::get(context, {opLoc}, lexicalBlockFileAttr);167 op->setLoc(newLoc);168 }169}170 171namespace {172/// Add a debug info scope to LLVMFuncOp that are missing it.173struct DIScopeForLLVMFuncOpPass174 : public LLVM::impl::DIScopeForLLVMFuncOpPassBase<175 DIScopeForLLVMFuncOpPass> {176 using Base::Base;177 178 void runOnOperation() override {179 ModuleOp module = getOperation();180 Location loc = module.getLoc();181 182 MLIRContext *context = &getContext();183 if (!context->getLoadedDialect<LLVM::LLVMDialect>()) {184 emitError(loc, "LLVM dialect is not loaded.");185 return signalPassFailure();186 }187 188 // Find a DICompileUnitAttr attached to a parent (the module for example),189 // otherwise create a default one.190 LLVM::DICompileUnitAttr compileUnitAttr;191 if (auto fusedCompileUnitAttr =192 module->getLoc()193 ->findInstanceOf<FusedLocWith<LLVM::DICompileUnitAttr>>()) {194 compileUnitAttr = fusedCompileUnitAttr.getMetadata();195 } else {196 LLVM::DIFileAttr fileAttr;197 if (FileLineColLoc fileLoc = extractFileLoc(loc)) {198 StringRef inputFilePath = fileLoc.getFilename().getValue();199 fileAttr = LLVM::DIFileAttr::get(200 context, llvm::sys::path::filename(inputFilePath),201 llvm::sys::path::parent_path(inputFilePath));202 } else {203 fileAttr = LLVM::DIFileAttr::get(context, "<unknown>", "");204 }205 206 compileUnitAttr = LLVM::DICompileUnitAttr::get(207 DistinctAttr::create(UnitAttr::get(context)), llvm::dwarf::DW_LANG_C,208 fileAttr, StringAttr::get(context, "MLIR"),209 /*isOptimized=*/true, emissionKind);210 }211 212 module.walk<WalkOrder::PreOrder>([&](Operation *op) -> void {213 if (auto funcOp = dyn_cast<LLVM::LLVMFuncOp>(op)) {214 // Create subprograms for each function with the same distinct compile215 // unit.216 addScopeToFunction(funcOp, compileUnitAttr);217 } else {218 setLexicalBlockFileAttr(op);219 }220 });221 }222};223 224} // end anonymous namespace225