Skip to content

Commit

Permalink
[flang][debug] Set scope of internal functions correctly. (llvm#99531)
Browse files Browse the repository at this point in the history
Summary:
The functions internal to subroutine should have the scope set to the
parent function. This allows a user to evaluate local variables of
parent function when control is stopped in the child.

Fixes llvm#96314

Test Plan: 

Reviewers: 

Subscribers: 

Tasks: 

Tags: 


Differential Revision: https://phabricator.intern.facebook.com/D60250527
  • Loading branch information
abidh authored and yuxuanchen1997 committed Jul 25, 2024
1 parent deb8ebf commit 626022b
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 75 deletions.
181 changes: 106 additions & 75 deletions flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ class AddDebugInfoPass : public fir::impl::AddDebugInfoBase<AddDebugInfoPass> {
void handleGlobalOp(fir::GlobalOp glocalOp, mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DIScopeAttr scope,
mlir::SymbolTable *symbolTable);
void handleFuncOp(mlir::func::FuncOp funcOp, mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DICompileUnitAttr cuAttr,
mlir::SymbolTable *symbolTable);
};

static uint32_t getLineFromLoc(mlir::Location loc) {
Expand Down Expand Up @@ -207,11 +210,112 @@ void AddDebugInfoPass::handleGlobalOp(fir::GlobalOp globalOp,
globalOp->setLoc(builder.getFusedLoc({globalOp->getLoc()}, gvAttr));
}

void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp,
mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DICompileUnitAttr cuAttr,
mlir::SymbolTable *symbolTable) {
mlir::Location l = funcOp->getLoc();
// If fused location has already been created then nothing to do
// Otherwise, create a fused location.
if (debugInfoIsAlreadySet(l))
return;

mlir::ModuleOp module = getOperation();
mlir::MLIRContext *context = &getContext();
mlir::OpBuilder builder(context);
llvm::StringRef fileName(fileAttr.getName());
llvm::StringRef filePath(fileAttr.getDirectory());
unsigned int CC = (funcOp.getName() == fir::NameUniquer::doProgramEntry())
? llvm::dwarf::getCallingConvention("DW_CC_program")
: llvm::dwarf::getCallingConvention("DW_CC_normal");

if (auto funcLoc = mlir::dyn_cast<mlir::FileLineColLoc>(l)) {
fileName = llvm::sys::path::filename(funcLoc.getFilename().getValue());
filePath = llvm::sys::path::parent_path(funcLoc.getFilename().getValue());
}

mlir::StringAttr fullName = mlir::StringAttr::get(context, funcOp.getName());
mlir::Attribute attr = funcOp->getAttr(fir::getInternalFuncNameAttrName());
mlir::StringAttr funcName =
(attr) ? mlir::cast<mlir::StringAttr>(attr)
: mlir::StringAttr::get(context, funcOp.getName());

auto result = fir::NameUniquer::deconstruct(funcName);
funcName = mlir::StringAttr::get(context, result.second.name);

llvm::SmallVector<mlir::LLVM::DITypeAttr> types;
fir::DebugTypeGenerator typeGen(module);
for (auto resTy : funcOp.getResultTypes()) {
auto tyAttr = typeGen.convertType(resTy, fileAttr, cuAttr, funcOp.getLoc());
types.push_back(tyAttr);
}
for (auto inTy : funcOp.getArgumentTypes()) {
auto tyAttr = typeGen.convertType(fir::unwrapRefType(inTy), fileAttr,
cuAttr, funcOp.getLoc());
types.push_back(tyAttr);
}

mlir::LLVM::DISubroutineTypeAttr subTypeAttr =
mlir::LLVM::DISubroutineTypeAttr::get(context, CC, types);
mlir::LLVM::DIFileAttr funcFileAttr =
mlir::LLVM::DIFileAttr::get(context, fileName, filePath);

// Only definitions need a distinct identifier and a compilation unit.
mlir::DistinctAttr id;
mlir::LLVM::DIScopeAttr Scope = fileAttr;
mlir::LLVM::DICompileUnitAttr compilationUnit;
mlir::LLVM::DISubprogramFlags subprogramFlags =
mlir::LLVM::DISubprogramFlags{};
if (isOptimized)
subprogramFlags = mlir::LLVM::DISubprogramFlags::Optimized;
if (!funcOp.isExternal()) {
id = mlir::DistinctAttr::create(mlir::UnitAttr::get(context));
compilationUnit = cuAttr;
subprogramFlags =
subprogramFlags | mlir::LLVM::DISubprogramFlags::Definition;
}
unsigned line = getLineFromLoc(l);
if (fir::isInternalProcedure(funcOp)) {
// For contained functions, the scope is the parent subroutine.
mlir::SymbolRefAttr sym = mlir::cast<mlir::SymbolRefAttr>(
funcOp->getAttr(fir::getHostSymbolAttrName()));
if (sym) {
if (auto func =
symbolTable->lookup<mlir::func::FuncOp>(sym.getLeafReference())) {
// Make sure that parent is processed.
handleFuncOp(func, fileAttr, cuAttr, symbolTable);
if (auto fusedLoc =
mlir::dyn_cast_if_present<mlir::FusedLoc>(func.getLoc())) {
if (auto spAttr =
mlir::dyn_cast_if_present<mlir::LLVM::DISubprogramAttr>(
fusedLoc.getMetadata()))
Scope = spAttr;
}
}
}
} else if (!result.second.modules.empty()) {
Scope = getOrCreateModuleAttr(result.second.modules[0], fileAttr, cuAttr,
line - 1, false);
}

auto spAttr = mlir::LLVM::DISubprogramAttr::get(
context, id, compilationUnit, Scope, funcName, fullName, funcFileAttr,
line, line, subprogramFlags, subTypeAttr);
funcOp->setLoc(builder.getFusedLoc({funcOp->getLoc()}, spAttr));

// Don't process variables if user asked for line tables only.
if (debugLevel == mlir::LLVM::DIEmissionKind::LineTablesOnly)
return;

funcOp.walk([&](fir::cg::XDeclareOp declOp) {
handleDeclareOp(declOp, fileAttr, spAttr, typeGen, symbolTable);
});
}

void AddDebugInfoPass::runOnOperation() {
mlir::ModuleOp module = getOperation();
mlir::MLIRContext *context = &getContext();
mlir::SymbolTable symbolTable(module);
mlir::OpBuilder builder(context);
llvm::StringRef fileName;
std::string filePath;
// We need 2 type of file paths here.
Expand Down Expand Up @@ -248,80 +352,7 @@ void AddDebugInfoPass::runOnOperation() {
isOptimized, debugLevel);

module.walk([&](mlir::func::FuncOp funcOp) {
mlir::Location l = funcOp->getLoc();
// If fused location has already been created then nothing to do
// Otherwise, create a fused location.
if (debugInfoIsAlreadySet(l))
return;

unsigned int CC = (funcOp.getName() == fir::NameUniquer::doProgramEntry())
? llvm::dwarf::getCallingConvention("DW_CC_program")
: llvm::dwarf::getCallingConvention("DW_CC_normal");

if (auto funcLoc = mlir::dyn_cast<mlir::FileLineColLoc>(l)) {
fileName = llvm::sys::path::filename(funcLoc.getFilename().getValue());
filePath = llvm::sys::path::parent_path(funcLoc.getFilename().getValue());
}

mlir::StringAttr fullName =
mlir::StringAttr::get(context, funcOp.getName());
mlir::Attribute attr = funcOp->getAttr(fir::getInternalFuncNameAttrName());
mlir::StringAttr funcName =
(attr) ? mlir::cast<mlir::StringAttr>(attr)
: mlir::StringAttr::get(context, funcOp.getName());

auto result = fir::NameUniquer::deconstruct(funcName);
funcName = mlir::StringAttr::get(context, result.second.name);

llvm::SmallVector<mlir::LLVM::DITypeAttr> types;
fir::DebugTypeGenerator typeGen(module);
for (auto resTy : funcOp.getResultTypes()) {
auto tyAttr =
typeGen.convertType(resTy, fileAttr, cuAttr, funcOp.getLoc());
types.push_back(tyAttr);
}
for (auto inTy : funcOp.getArgumentTypes()) {
auto tyAttr = typeGen.convertType(fir::unwrapRefType(inTy), fileAttr,
cuAttr, funcOp.getLoc());
types.push_back(tyAttr);
}

mlir::LLVM::DISubroutineTypeAttr subTypeAttr =
mlir::LLVM::DISubroutineTypeAttr::get(context, CC, types);
mlir::LLVM::DIFileAttr funcFileAttr =
mlir::LLVM::DIFileAttr::get(context, fileName, filePath);

// Only definitions need a distinct identifier and a compilation unit.
mlir::DistinctAttr id;
mlir::LLVM::DIScopeAttr Scope = fileAttr;
mlir::LLVM::DICompileUnitAttr compilationUnit;
mlir::LLVM::DISubprogramFlags subprogramFlags =
mlir::LLVM::DISubprogramFlags{};
if (isOptimized)
subprogramFlags = mlir::LLVM::DISubprogramFlags::Optimized;
if (!funcOp.isExternal()) {
id = mlir::DistinctAttr::create(mlir::UnitAttr::get(context));
compilationUnit = cuAttr;
subprogramFlags =
subprogramFlags | mlir::LLVM::DISubprogramFlags::Definition;
}
unsigned line = getLineFromLoc(l);
if (!result.second.modules.empty())
Scope = getOrCreateModuleAttr(result.second.modules[0], fileAttr, cuAttr,
line - 1, false);

auto spAttr = mlir::LLVM::DISubprogramAttr::get(
context, id, compilationUnit, Scope, funcName, fullName, funcFileAttr,
line, line, subprogramFlags, subTypeAttr);
funcOp->setLoc(builder.getFusedLoc({funcOp->getLoc()}, spAttr));

// Don't process variables if user asked for line tables only.
if (debugLevel == mlir::LLVM::DIEmissionKind::LineTablesOnly)
return;

funcOp.walk([&](fir::cg::XDeclareOp declOp) {
handleDeclareOp(declOp, fileAttr, spAttr, typeGen, &symbolTable);
});
handleFuncOp(funcOp, fileAttr, cuAttr, &symbolTable);
});
// Process any global which was not processed through DeclareOp.
if (debugLevel == mlir::LLVM::DIEmissionKind::Full) {
Expand Down
26 changes: 26 additions & 0 deletions flang/test/Transforms/debug-96314.fir
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// RUN: fir-opt --add-debug-info --mlir-print-debuginfo %s -o - | FileCheck %s

module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
func.func @_QMhelperPmod_sub(%arg0: !fir.ref<i32> {fir.bindc_name = "a"} ) {
return
} loc(#loc1)
func.func private @_QMhelperFmod_subPchild1(%arg0: !fir.ref<i32> {fir.bindc_name = "b"} ) attributes {fir.host_symbol = @_QMhelperPmod_sub, llvm.linkage = #llvm.linkage<internal>} {
return
} loc(#loc2)
func.func @global_sub_(%arg0: !fir.ref<i32> {fir.bindc_name = "n"} ) attributes {fir.internal_name = "_QPglobal_sub"} {
return
} loc(#loc3)
func.func private @_QFglobal_subPchild2(%arg0: !fir.ref<i32> {fir.bindc_name = "c"}) attributes {fir.host_symbol = @global_sub_, llvm.linkage = #llvm.linkage<internal>} {
return
} loc(#loc4)
}

#loc1 = loc("test.f90":5:1)
#loc2 = loc("test.f90":15:1)
#loc3 = loc("test.f90":25:1)
#loc4 = loc("test.f90":35:1)

// CHECK-DAG: #[[SP1:.*]] = #llvm.di_subprogram<{{.*}}name = "mod_sub"{{.*}}>
// CHECK-DAG: #llvm.di_subprogram<{{.*}}scope = #[[SP1]], name = "child1"{{.*}}>
// CHECK-DAG: #[[SP2:.*]] = #llvm.di_subprogram<{{.*}}linkageName = "global_sub_"{{.*}}>
// CHECK-DAG: #llvm.di_subprogram<{{.*}}scope = #[[SP2]], name = "child2"{{.*}}>

0 comments on commit 626022b

Please sign in to comment.