[flang][debug] Generate DWARF debug info using fir.use_stmt. (#168541)
This patch uses the fir.use_stmt operations to generate correct debug
metadata for use statement when `only` and `=>` are used. The debug flow
is changed a bit where we process the module globals first so that we
have the global variables when we start to process `fir.use_stmt`.
Fixes #160923.
This commit is contained in:
parent
1219cc76d1
commit
dc9c08e6e0
@ -84,6 +84,24 @@ private:
|
||||
mlir::LLVM::DICompileUnitAttr cuAttr,
|
||||
fir::DebugTypeGenerator &typeGen,
|
||||
mlir::SymbolTable *symbolTable);
|
||||
void handleOnlyClause(
|
||||
fir::UseStmtOp useOp, mlir::LLVM::DISubprogramAttr spAttr,
|
||||
mlir::LLVM::DIFileAttr fileAttr, mlir::SymbolTable *symbolTable,
|
||||
llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> &importedModules);
|
||||
void handleRenamesWithoutOnly(
|
||||
fir::UseStmtOp useOp, mlir::LLVM::DISubprogramAttr spAttr,
|
||||
mlir::LLVM::DIModuleAttr modAttr, mlir::LLVM::DIFileAttr fileAttr,
|
||||
mlir::SymbolTable *symbolTable,
|
||||
llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> &importedModules);
|
||||
void handleUseStatements(
|
||||
mlir::func::FuncOp funcOp, mlir::LLVM::DISubprogramAttr spAttr,
|
||||
mlir::LLVM::DIFileAttr fileAttr, mlir::LLVM::DICompileUnitAttr cuAttr,
|
||||
mlir::SymbolTable *symbolTable,
|
||||
llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> &importedEntities);
|
||||
std::optional<mlir::LLVM::DIImportedEntityAttr> createImportedDeclForGlobal(
|
||||
llvm::StringRef symbolName, mlir::LLVM::DISubprogramAttr spAttr,
|
||||
mlir::LLVM::DIFileAttr fileAttr, mlir::StringAttr localNameAttr,
|
||||
mlir::SymbolTable *symbolTable);
|
||||
bool createCommonBlockGlobal(fir::cg::XDeclareOp declOp,
|
||||
const std::string &name,
|
||||
mlir::LLVM::DIFileAttr fileAttr,
|
||||
@ -138,6 +156,34 @@ mlir::StringAttr getTargetFunctionName(mlir::MLIRContext *context,
|
||||
|
||||
} // namespace
|
||||
|
||||
// Check if a global represents a module variable
|
||||
static bool isModuleVariable(fir::GlobalOp globalOp) {
|
||||
std::pair result = fir::NameUniquer::deconstruct(globalOp.getSymName());
|
||||
return result.first == fir::NameUniquer::NameKind::VARIABLE &&
|
||||
result.second.procs.empty() && !result.second.modules.empty();
|
||||
}
|
||||
|
||||
// Look up DIGlobalVariable from a global symbol
|
||||
static std::optional<mlir::LLVM::DIGlobalVariableAttr>
|
||||
lookupDIGlobalVariable(llvm::StringRef symbolName,
|
||||
mlir::SymbolTable *symbolTable) {
|
||||
if (auto globalOp = symbolTable->lookup<fir::GlobalOp>(symbolName)) {
|
||||
if (auto fusedLoc = mlir::dyn_cast<mlir::FusedLoc>(globalOp.getLoc())) {
|
||||
if (auto metadata = fusedLoc.getMetadata()) {
|
||||
if (auto arrayAttr = mlir::dyn_cast<mlir::ArrayAttr>(metadata)) {
|
||||
for (auto elem : arrayAttr) {
|
||||
if (auto gvExpr =
|
||||
mlir::dyn_cast<mlir::LLVM::DIGlobalVariableExpressionAttr>(
|
||||
elem))
|
||||
return gvExpr.getVar();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool AddDebugInfoPass::createCommonBlockGlobal(
|
||||
fir::cg::XDeclareOp declOp, const std::string &name,
|
||||
mlir::LLVM::DIFileAttr fileAttr, mlir::LLVM::DIScopeAttr scopeAttr,
|
||||
@ -526,7 +572,7 @@ void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp,
|
||||
CC = llvm::dwarf::getCallingConvention("DW_CC_normal");
|
||||
mlir::LLVM::DISubroutineTypeAttr spTy =
|
||||
mlir::LLVM::DISubroutineTypeAttr::get(context, CC, types);
|
||||
if (lineTableOnly) {
|
||||
if (lineTableOnly || entities.empty()) {
|
||||
auto spAttr = mlir::LLVM::DISubprogramAttr::get(
|
||||
context, id, compilationUnit, Scope, name, name, funcFileAttr, line,
|
||||
line, flags, spTy, /*retainedNodes=*/{}, /*annotations=*/{});
|
||||
@ -546,9 +592,9 @@ void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp,
|
||||
for (mlir::LLVM::DINodeAttr N : entities) {
|
||||
if (auto entity = mlir::dyn_cast<mlir::LLVM::DIImportedEntityAttr>(N)) {
|
||||
auto importedEntity = mlir::LLVM::DIImportedEntityAttr::get(
|
||||
context, llvm::dwarf::DW_TAG_imported_module, spAttr,
|
||||
entity.getEntity(), fileAttr, /*line=*/1, /*name=*/nullptr,
|
||||
/*elements*/ {});
|
||||
context, entity.getTag(), spAttr, entity.getEntity(),
|
||||
entity.getFile(), entity.getLine(), entity.getName(),
|
||||
entity.getElements());
|
||||
opEntities.push_back(importedEntity);
|
||||
}
|
||||
}
|
||||
@ -573,61 +619,57 @@ void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp,
|
||||
return;
|
||||
}
|
||||
|
||||
mlir::DistinctAttr recId =
|
||||
mlir::DistinctAttr::create(mlir::UnitAttr::get(context));
|
||||
|
||||
// The debug attribute in MLIR are readonly once created. But in case of
|
||||
// imported entities, we have a circular dependency. The
|
||||
// DIImportedEntityAttr requires scope information (DISubprogramAttr in this
|
||||
// case) and DISubprogramAttr requires the list of imported entities. The
|
||||
// MLIR provides a way where a DISubprogramAttr an be created with a certain
|
||||
// recID and be used in places like DIImportedEntityAttr. After that another
|
||||
// DISubprogramAttr can be created with same recID but with list of entities
|
||||
// now available. The MLIR translation code takes care of updating the
|
||||
// references. Note that references will be updated only in the things that
|
||||
// are part of DISubprogramAttr (like DIImportedEntityAttr) so we have to
|
||||
// create the final DISubprogramAttr before we process local variables.
|
||||
// Look at DIRecursiveTypeAttrInterface for more details.
|
||||
|
||||
auto spAttr = mlir::LLVM::DISubprogramAttr::get(
|
||||
context, recId, /*isRecSelf=*/true, id, compilationUnit, Scope, funcName,
|
||||
fullName, funcFileAttr, line, line, subprogramFlags, subTypeAttr,
|
||||
/*retainedNodes=*/{}, /*annotations=*/{});
|
||||
|
||||
// There is no direct information in the IR for any 'use' statement in the
|
||||
// function. We have to extract that information from the DeclareOp. We do
|
||||
// a pass on the DeclareOp and generate ModuleAttr and corresponding
|
||||
// DIImportedEntityAttr for that module.
|
||||
// FIXME: As we are depending on the variables to see which module is being
|
||||
// 'used' in the function, there are certain limitations.
|
||||
// For things like 'use mod1, only: v1', whole module will be brought into the
|
||||
// namespace in the debug info. It is not a problem as such unless there is a
|
||||
// clash of names.
|
||||
// There is no information about module variable renaming
|
||||
llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> importedModules;
|
||||
funcOp.walk([&](fir::cg::XDeclareOp declOp) {
|
||||
if (&funcOp.front() == declOp->getBlock())
|
||||
if (auto global =
|
||||
symbolTable->lookup<fir::GlobalOp>(declOp.getUniqName())) {
|
||||
std::optional<mlir::LLVM::DIModuleAttr> modOpt =
|
||||
getModuleAttrFromGlobalOp(global, fileAttr, cuAttr);
|
||||
if (modOpt) {
|
||||
auto importedEntity = mlir::LLVM::DIImportedEntityAttr::get(
|
||||
context, llvm::dwarf::DW_TAG_imported_module, spAttr, *modOpt,
|
||||
fileAttr, /*line=*/1, /*name=*/nullptr, /*elements*/ {});
|
||||
importedModules.insert(importedEntity);
|
||||
}
|
||||
}
|
||||
// Check if there are any USE statements
|
||||
bool hasUseStmts = false;
|
||||
funcOp.walk([&](fir::UseStmtOp useOp) {
|
||||
hasUseStmts = true;
|
||||
return mlir::WalkResult::interrupt();
|
||||
});
|
||||
llvm::SmallVector<mlir::LLVM::DINodeAttr> entities(importedModules.begin(),
|
||||
importedModules.end());
|
||||
// We have the imported entities now. Generate the final DISubprogramAttr.
|
||||
spAttr = mlir::LLVM::DISubprogramAttr::get(
|
||||
context, recId, /*isRecSelf=*/false, id2, compilationUnit, Scope,
|
||||
funcName, fullName, funcFileAttr, line, line, subprogramFlags,
|
||||
subTypeAttr, entities, /*annotations=*/{});
|
||||
|
||||
mlir::LLVM::DISubprogramAttr spAttr;
|
||||
llvm::SmallVector<mlir::LLVM::DINodeAttr> retainedNodes;
|
||||
|
||||
if (hasUseStmts) {
|
||||
mlir::DistinctAttr recId =
|
||||
mlir::DistinctAttr::create(mlir::UnitAttr::get(context));
|
||||
// The debug attribute in MLIR are readonly once created. But in case of
|
||||
// imported entities, we have a circular dependency. The
|
||||
// DIImportedEntityAttr requires scope information (DISubprogramAttr in this
|
||||
// case) and DISubprogramAttr requires the list of imported entities. The
|
||||
// MLIR provides a way where a DISubprogramAttr an be created with a certain
|
||||
// recID and be used in places like DIImportedEntityAttr. After that another
|
||||
// DISubprogramAttr can be created with same recID but with list of entities
|
||||
// now available. The MLIR translation code takes care of updating the
|
||||
// references. Note that references will be updated only in the things that
|
||||
// are part of DISubprogramAttr (like DIImportedEntityAttr) so we have to
|
||||
// create the final DISubprogramAttr before we process local variables.
|
||||
// Look at DIRecursiveTypeAttrInterface for more details.
|
||||
spAttr = mlir::LLVM::DISubprogramAttr::get(
|
||||
context, recId, /*isRecSelf=*/true, id, compilationUnit, Scope,
|
||||
funcName, fullName, funcFileAttr, line, line, subprogramFlags,
|
||||
subTypeAttr, /*retainedNodes=*/{}, /*annotations=*/{});
|
||||
|
||||
// Process USE statements (module globals are already processed)
|
||||
llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> importedEntities;
|
||||
handleUseStatements(funcOp, spAttr, fileAttr, cuAttr, symbolTable,
|
||||
importedEntities);
|
||||
|
||||
retainedNodes.append(importedEntities.begin(), importedEntities.end());
|
||||
|
||||
// Create final DISubprogramAttr with imported entities and same recId
|
||||
spAttr = mlir::LLVM::DISubprogramAttr::get(
|
||||
context, recId, /*isRecSelf=*/false, id2, compilationUnit, Scope,
|
||||
funcName, fullName, funcFileAttr, line, line, subprogramFlags,
|
||||
subTypeAttr, retainedNodes, /*annotations=*/{});
|
||||
} else
|
||||
// No USE statements - create final DISubprogramAttr directly
|
||||
spAttr = mlir::LLVM::DISubprogramAttr::get(
|
||||
context, id, compilationUnit, Scope, funcName, fullName, funcFileAttr,
|
||||
line, line, subprogramFlags, subTypeAttr, /*retainedNodes=*/{},
|
||||
/*annotations=*/{});
|
||||
|
||||
funcOp->setLoc(builder.getFusedLoc({l}, spAttr));
|
||||
addTargetOpDISP(/*lineTableOnly=*/false, entities);
|
||||
addTargetOpDISP(/*lineTableOnly=*/false, retainedNodes);
|
||||
|
||||
// Find the first dummy_scope definition. This is the one of the current
|
||||
// function. The other ones may come from inlined calls. The variables inside
|
||||
@ -662,6 +704,110 @@ void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp,
|
||||
commonBlockMap.clear();
|
||||
}
|
||||
|
||||
// Helper function to create a DIImportedEntityAttr for an imported declaration.
|
||||
// Looks up the DIGlobalVariable for the given symbol and creates an imported
|
||||
// declaration with the optional local name (for renames).
|
||||
// Returns std::nullopt if the symbol's DIGlobalVariable is not found.
|
||||
std::optional<mlir::LLVM::DIImportedEntityAttr>
|
||||
AddDebugInfoPass::createImportedDeclForGlobal(
|
||||
llvm::StringRef symbolName, mlir::LLVM::DISubprogramAttr spAttr,
|
||||
mlir::LLVM::DIFileAttr fileAttr, mlir::StringAttr localNameAttr,
|
||||
mlir::SymbolTable *symbolTable) {
|
||||
mlir::MLIRContext *context = &getContext();
|
||||
if (auto gvAttr = lookupDIGlobalVariable(symbolName, symbolTable)) {
|
||||
return mlir::LLVM::DIImportedEntityAttr::get(
|
||||
context, llvm::dwarf::DW_TAG_imported_declaration, spAttr, *gvAttr,
|
||||
fileAttr, /*line=*/1, /*name=*/localNameAttr, /*elements*/ {});
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Process USE with ONLY clause
|
||||
void AddDebugInfoPass::handleOnlyClause(
|
||||
fir::UseStmtOp useOp, mlir::LLVM::DISubprogramAttr spAttr,
|
||||
mlir::LLVM::DIFileAttr fileAttr, mlir::SymbolTable *symbolTable,
|
||||
llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> &importedModules) {
|
||||
// Process ONLY symbols (without renames)
|
||||
if (auto onlySymbols = useOp.getOnlySymbols()) {
|
||||
for (mlir::Attribute attr : *onlySymbols) {
|
||||
auto symbolRef = mlir::cast<mlir::FlatSymbolRefAttr>(attr);
|
||||
if (auto importedDecl = createImportedDeclForGlobal(
|
||||
symbolRef.getValue(), spAttr, fileAttr, mlir::StringAttr(),
|
||||
symbolTable))
|
||||
importedModules.insert(*importedDecl);
|
||||
}
|
||||
}
|
||||
|
||||
// Process renames within ONLY clause
|
||||
if (auto renames = useOp.getRenames()) {
|
||||
for (auto attr : *renames) {
|
||||
auto renameAttr = mlir::cast<fir::UseRenameAttr>(attr);
|
||||
if (auto importedDecl = createImportedDeclForGlobal(
|
||||
renameAttr.getSymbol().getValue(), spAttr, fileAttr,
|
||||
renameAttr.getLocalName(), symbolTable))
|
||||
importedModules.insert(*importedDecl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process USE with renames but no ONLY clause
|
||||
void AddDebugInfoPass::handleRenamesWithoutOnly(
|
||||
fir::UseStmtOp useOp, mlir::LLVM::DISubprogramAttr spAttr,
|
||||
mlir::LLVM::DIModuleAttr modAttr, mlir::LLVM::DIFileAttr fileAttr,
|
||||
mlir::SymbolTable *symbolTable,
|
||||
llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> &importedModules) {
|
||||
mlir::MLIRContext *context = &getContext();
|
||||
llvm::SmallVector<mlir::LLVM::DINodeAttr> childDeclarations;
|
||||
|
||||
if (auto renames = useOp.getRenames()) {
|
||||
for (auto attr : *renames) {
|
||||
auto renameAttr = mlir::cast<fir::UseRenameAttr>(attr);
|
||||
if (auto importedDecl = createImportedDeclForGlobal(
|
||||
renameAttr.getSymbol().getValue(), spAttr, fileAttr,
|
||||
renameAttr.getLocalName(), symbolTable))
|
||||
childDeclarations.push_back(*importedDecl);
|
||||
}
|
||||
}
|
||||
|
||||
// Create module import with renamed declarations as children
|
||||
auto moduleImport = mlir::LLVM::DIImportedEntityAttr::get(
|
||||
context, llvm::dwarf::DW_TAG_imported_module, spAttr, modAttr, fileAttr,
|
||||
/*line=*/1, /*name=*/nullptr, childDeclarations);
|
||||
importedModules.insert(moduleImport);
|
||||
}
|
||||
|
||||
// Process all USE statements in a function and collect imported entities
|
||||
void AddDebugInfoPass::handleUseStatements(
|
||||
mlir::func::FuncOp funcOp, mlir::LLVM::DISubprogramAttr spAttr,
|
||||
mlir::LLVM::DIFileAttr fileAttr, mlir::LLVM::DICompileUnitAttr cuAttr,
|
||||
mlir::SymbolTable *symbolTable,
|
||||
llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> &importedEntities) {
|
||||
mlir::MLIRContext *context = &getContext();
|
||||
|
||||
funcOp.walk([&](fir::UseStmtOp useOp) {
|
||||
mlir::LLVM::DIModuleAttr modAttr = getOrCreateModuleAttr(
|
||||
useOp.getModuleName().str(), fileAttr, cuAttr, /*line=*/1,
|
||||
/*decl=*/true);
|
||||
|
||||
llvm::DenseSet<mlir::LLVM::DIImportedEntityAttr> importedModules;
|
||||
|
||||
if (useOp.hasOnlyClause())
|
||||
handleOnlyClause(useOp, spAttr, fileAttr, symbolTable, importedModules);
|
||||
else if (useOp.hasRenames())
|
||||
handleRenamesWithoutOnly(useOp, spAttr, modAttr, fileAttr, symbolTable,
|
||||
importedModules);
|
||||
else {
|
||||
// Simple module import
|
||||
auto importedEntity = mlir::LLVM::DIImportedEntityAttr::get(
|
||||
context, llvm::dwarf::DW_TAG_imported_module, spAttr, modAttr,
|
||||
fileAttr, /*line=*/1, /*name=*/nullptr, /*elements*/ {});
|
||||
importedModules.insert(importedEntity);
|
||||
}
|
||||
|
||||
importedEntities.insert(importedModules.begin(), importedModules.end());
|
||||
});
|
||||
}
|
||||
|
||||
void AddDebugInfoPass::runOnOperation() {
|
||||
mlir::ModuleOp module = getOperation();
|
||||
mlir::MLIRContext *context = &getContext();
|
||||
@ -725,6 +871,26 @@ void AddDebugInfoPass::runOnOperation() {
|
||||
splitDwarfFile.empty() ? mlir::StringAttr()
|
||||
: mlir::StringAttr::get(context, splitDwarfFile));
|
||||
|
||||
// Process module globals early.
|
||||
// Walk through all DeclareOps in functions and process globals that are
|
||||
// module variables. This ensures that when we process USE statements,
|
||||
// the DIGlobalVariable lookups will succeed.
|
||||
if (debugLevel == mlir::LLVM::DIEmissionKind::Full) {
|
||||
module.walk([&](fir::cg::XDeclareOp declOp) {
|
||||
mlir::Operation *defOp = declOp.getMemref().getDefiningOp();
|
||||
if (defOp && llvm::isa<fir::AddrOfOp>(defOp)) {
|
||||
if (auto globalOp =
|
||||
symbolTable.lookup<fir::GlobalOp>(declOp.getUniqName())) {
|
||||
// Only process module variables here, not SAVE variables
|
||||
if (isModuleVariable(globalOp)) {
|
||||
handleGlobalOp(globalOp, fileAttr, cuAttr, typeGen, &symbolTable,
|
||||
declOp);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.walk([&](mlir::func::FuncOp funcOp) {
|
||||
handleFuncOp(funcOp, fileAttr, cuAttr, typeGen, &symbolTable);
|
||||
});
|
||||
|
||||
38
flang/test/Integration/debug-use-stmt.f90
Normal file
38
flang/test/Integration/debug-use-stmt.f90
Normal file
@ -0,0 +1,38 @@
|
||||
! RUN: %flang_fc1 -emit-llvm -debug-info-kind=standalone %s -o - | FileCheck %s
|
||||
|
||||
module testmod
|
||||
integer :: var_a = 10, var_b = 20, var_c = 30
|
||||
end module testmod
|
||||
|
||||
module testmod2
|
||||
real :: var_x = 1.0, var_y = 2.0
|
||||
end module testmod2
|
||||
|
||||
program test_use
|
||||
use testmod, only: var_b, var_d => var_c
|
||||
use testmod2, var_z => var_y
|
||||
implicit none
|
||||
print *, var_b
|
||||
print *, var_d
|
||||
print *, var_z
|
||||
end program
|
||||
|
||||
! CHECK-DAG: [[TESTMOD:![0-9]+]] = !DIModule(scope: !{{.*}}, name: "testmod"
|
||||
! CHECK-DAG: [[TESTMOD2:![0-9]+]] = !DIModule(scope: !{{.*}}, name: "testmod2"
|
||||
|
||||
! CHECK-DAG: [[VAR_B:![0-9]+]] = distinct !DIGlobalVariable(name: "var_b", linkageName: "_QMtestmodEvar_b"
|
||||
! CHECK-DAG: [[VAR_C:![0-9]+]] = distinct !DIGlobalVariable(name: "var_c", linkageName: "_QMtestmodEvar_c"
|
||||
! CHECK-DAG: [[VAR_Y:![0-9]+]] = distinct !DIGlobalVariable(name: "var_y", linkageName: "_QMtestmod2Evar_y"
|
||||
|
||||
! CHECK-DAG: [[SP:![0-9]+]] = distinct !DISubprogram(name: "TEST_USE", linkageName: "_QQmain"{{.*}}retainedNodes:
|
||||
|
||||
! Check testmod imports: var_b directly (no rename), var_d as rename of var_c
|
||||
! CHECK-DAG: !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: [[SP]], entity: [[VAR_B]],{{.*}}file:{{.*}}line:
|
||||
! CHECK-DAG: !DIImportedEntity(tag: DW_TAG_imported_declaration, name: "var_d", scope: [[SP]], entity: [[VAR_C]],{{.*}}file:{{.*}}line:
|
||||
|
||||
! Check testmod2 import: module imported with rename in elements array
|
||||
! The module import should have elements containing the var_z rename
|
||||
! CHECK-DAG: [[MOD2_IMPORT:![0-9]+]] = !DIImportedEntity(tag: DW_TAG_imported_module, scope: [[SP]], entity: [[TESTMOD2]],{{.*}}elements: [[ELEMENTS:![0-9]+]]
|
||||
! CHECK-DAG: [[ELEMENTS]] = !{[[VAR_Z:![0-9]+]]}
|
||||
! CHECK-DAG: [[VAR_Z]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, name: "var_z",{{.*}}entity: [[VAR_Y]],
|
||||
|
||||
@ -11,6 +11,7 @@ module {
|
||||
fir.has_value %c12_i32 : i32
|
||||
} loc(#loc4)
|
||||
func.func @test() attributes {fir.bindc_name = "test"} {
|
||||
fir.use_stmt "foo"
|
||||
%0 = fir.address_of(@_QMfooEv1) : !fir.ref<i32>
|
||||
%1 = fircg.ext_declare %0 {uniq_name = "_QMfooEv1"} : (!fir.ref<i32>) -> !fir.ref<i32> loc(#loc1)
|
||||
%4 = fir.address_of(@_QFtestExyz) : !fir.ref<i32>
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<i64, dense<64> : vector<2xi64>>, #dlti.dl_entry<!llvm.ptr<272>, dense<64> : vector<4xi64>>, #dlti.dl_entry<!llvm.ptr<271>, dense<32> : vector<4xi64>>, #dlti.dl_entry<!llvm.ptr<270>, dense<32> : vector<4xi64>>, #dlti.dl_entry<f128, dense<128> : vector<2xi64>>, #dlti.dl_entry<f80, dense<128> : vector<2xi64>>, #dlti.dl_entry<i128, dense<128> : vector<2xi64>>, #dlti.dl_entry<i8, dense<8> : vector<2xi64>>, #dlti.dl_entry<!llvm.ptr, dense<64> : vector<4xi64>>, #dlti.dl_entry<i1, dense<8> : vector<2xi64>>, #dlti.dl_entry<f16, dense<16> : vector<2xi64>>, #dlti.dl_entry<f64, dense<64> : vector<2xi64>>, #dlti.dl_entry<i32, dense<32> : vector<2xi64>>, #dlti.dl_entry<i16, dense<16> : vector<2xi64>>, #dlti.dl_entry<"dlti.stack_alignment", 128 : i64>, #dlti.dl_entry<"dlti.endianness", "little">>, fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"} {
|
||||
func.func @_QMexamplePmod_sub() {
|
||||
fir.use_stmt "example"
|
||||
%c2 = arith.constant 2 : index
|
||||
%1 = fir.address_of(@_QMexampleEmod_arr) : !fir.ref<!fir.array<2x2xi32>>
|
||||
%2 = fircg.ext_declare %1(%c2, %c2) {uniq_name = "_QMexampleEmod_arr"} : (!fir.ref<!fir.array<2x2xi32>>, index, index) -> !fir.ref<!fir.array<2x2xi32>> loc(#loc4)
|
||||
|
||||
69
flang/test/Transforms/debug-use-stmt.fir
Normal file
69
flang/test/Transforms/debug-use-stmt.fir
Normal file
@ -0,0 +1,69 @@
|
||||
// RUN: fir-opt --add-debug-info --mlir-print-debuginfo %s | FileCheck %s
|
||||
|
||||
module {
|
||||
// Module globals
|
||||
fir.global @_QMtestmodEvar_b : i32 {
|
||||
%c20_i32 = arith.constant 20 : i32
|
||||
fir.has_value %c20_i32 : i32
|
||||
}
|
||||
|
||||
fir.global @_QMtestmodEvar_c : i32 {
|
||||
%c30_i32 = arith.constant 30 : i32
|
||||
fir.has_value %c30_i32 : i32
|
||||
}
|
||||
|
||||
fir.global @_QMtestmod2Evar_y : f32 {
|
||||
%cst = arith.constant 2.000000e+00 : f32
|
||||
fir.has_value %cst : f32
|
||||
}
|
||||
|
||||
func.func @_QQmain() attributes {fir.bindc_name = "TEST_USE"} {
|
||||
// USE testmod, ONLY: var_b, var_d => var_c
|
||||
fir.use_stmt "testmod" only_symbols[[@_QMtestmodEvar_b]] renames[[#fir.use_rename<"var_d", @_QMtestmodEvar_c>]]
|
||||
|
||||
// USE testmod2, var_z => var_y (no ONLY)
|
||||
fir.use_stmt "testmod2" renames[[#fir.use_rename<"var_z", @_QMtestmod2Evar_y>]]
|
||||
|
||||
%0 = fir.address_of(@_QMtestmodEvar_b) : !fir.ref<i32>
|
||||
%1 = fircg.ext_declare %0 {uniq_name = "_QMtestmodEvar_b"} : (!fir.ref<i32>) -> !fir.ref<i32> loc(#loc_b)
|
||||
|
||||
%2 = fir.address_of(@_QMtestmodEvar_c) : !fir.ref<i32>
|
||||
%3 = fircg.ext_declare %2 {uniq_name = "_QMtestmodEvar_c"} : (!fir.ref<i32>) -> !fir.ref<i32> loc(#loc_c)
|
||||
|
||||
%4 = fir.address_of(@_QMtestmod2Evar_y) : !fir.ref<f32>
|
||||
%5 = fircg.ext_declare %4 {uniq_name = "_QMtestmod2Evar_y"} : (!fir.ref<f32>) -> !fir.ref<f32> loc(#loc_y)
|
||||
|
||||
return
|
||||
} loc(#loc_main)
|
||||
}
|
||||
|
||||
#loc_b = loc("test.f90":4:26)
|
||||
#loc_c = loc("test.f90":4:38)
|
||||
#loc_y = loc("test.f90":8:24)
|
||||
#loc_main = loc("test.f90":11:1)
|
||||
|
||||
// CHECK-DAG: #[[MOD_TESTMOD:.+]] = #llvm.di_module<{{.*}}name = "testmod"{{.*}}>
|
||||
// CHECK-DAG: #[[MOD_TESTMOD2:.+]] = #llvm.di_module<{{.*}}name = "testmod2"{{.*}}>
|
||||
|
||||
// CHECK-DAG: #[[GVAR_B:.+]] = #llvm.di_global_variable<scope = #[[MOD_TESTMOD]], name = "var_b", linkageName = "_QMtestmodEvar_b"
|
||||
// CHECK-DAG: #[[GVAR_C:.+]] = #llvm.di_global_variable<scope = #[[MOD_TESTMOD]], name = "var_c", linkageName = "_QMtestmodEvar_c"
|
||||
// CHECK-DAG: #[[GVAR_Y:.+]] = #llvm.di_global_variable<scope = #[[MOD_TESTMOD2]], name = "var_y", linkageName = "_QMtestmod2Evar_y"
|
||||
|
||||
// DISubprogram placeholder (for recursive reference)
|
||||
// CHECK-DAG: #[[SP_REC:.+]] = #llvm.di_subprogram<recId = distinct[[[RECID:[0-9]+]]]<>, isRecSelf = true{{.*}}name = "TEST_USE"
|
||||
|
||||
// 1. Imported declaration without rename (var_b) - has entity but NO name attribute
|
||||
// CHECK-DAG: #llvm.di_imported_entity<tag = DW_TAG_imported_declaration, scope = #[[SP_REC]], entity = #[[GVAR_B]],{{.*}}>
|
||||
|
||||
// 2. Imported declaration with rename (var_d => var_c) - has both entity and name
|
||||
// CHECK-DAG: #llvm.di_imported_entity<tag = DW_TAG_imported_declaration, scope = #[[SP_REC]], entity = #[[GVAR_C]],{{.*}}name = "var_d">
|
||||
|
||||
// 3. Imported declaration with rename (var_z => var_y) - for module import element
|
||||
// CHECK-DAG: #[[IMPORT_Z:.+]] = #llvm.di_imported_entity<tag = DW_TAG_imported_declaration, scope = #[[SP_REC]], entity = #[[GVAR_Y]],{{.*}}name = "var_z">
|
||||
|
||||
// 4. Imported module (testmod2) with renamed element in its elements field
|
||||
// CHECK-DAG: #llvm.di_imported_entity<tag = DW_TAG_imported_module, scope = #[[SP_REC]], entity = #[[MOD_TESTMOD2]]{{.*}}elements = #[[IMPORT_Z]]
|
||||
|
||||
// Verify final DISubprogram has retainedNodes (non-empty)
|
||||
// We don't check the exact order since retainedNodes comes from an unordered collection
|
||||
// CHECK-DAG: #llvm.di_subprogram<recId = distinct[[[RECID]]]<>{{.*}}name = "TEST_USE"{{.*}}retainedNodes = {{.+}}>
|
||||
Loading…
x
Reference in New Issue
Block a user