Skip to content

Commit f7a8e99

Browse files
committed
[flang] Represent use statement in fir.
This patch adds infrastructure to emit Fortran USE statement information in FIR, which will be used by subsequent patches to generate DWARF debug information. The information about use statement is collected during semantic analysis and stored in PreservedUseStmt objects. During lowering, fir.use_stmt operations are emitted for each PreservedUseStmt object. The fir.use_stmt operation captures the module name, ONLY list symbols, and any renames specified in the USE statement.
1 parent 6e5f277 commit f7a8e99

File tree

7 files changed

+342
-2
lines changed

7 files changed

+342
-2
lines changed

flang/include/flang/Optimizer/Dialect/FIRAttr.td

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,4 +239,21 @@ def fir_FortranInlineAttr
239239
: EnumAttr<FIROpsDialect, fir_FortranInlineEnum, "inline_attrs"> {
240240
let assemblyFormat = "`<` $value `>`";
241241
}
242+
243+
// USE statement rename mapping: local_name => use_name
244+
def fir_UseRenameAttr : fir_Attr<"UseRename"> {
245+
let mnemonic = "use_rename";
246+
let summary = "Represents a rename in a Fortran USE statement";
247+
let description = [{
248+
This attribute stores the mapping for a renamed symbol in a USE statement.
249+
For example, in "USE mod, local_var => module_var", this stores the
250+
local name and a symbol reference to the module variable.
251+
}];
252+
253+
let parameters = (ins "mlir::StringAttr":$local_name,
254+
"mlir::FlatSymbolRefAttr":$symbol);
255+
256+
let assemblyFormat = "`<` $local_name `,` $symbol `>`";
257+
}
258+
242259
#endif // FIR_DIALECT_FIR_ATTRS

flang/include/flang/Optimizer/Dialect/FIROps.td

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3054,6 +3054,58 @@ def fir_GlobalLenOp : fir_Op<"global_len", []> {
30543054
}];
30553055
}
30563056

3057+
def fir_UseStmtOp
3058+
: fir_Op<"use_stmt", [MemoryEffects<[MemWrite<DebuggingResource>]>]> {
3059+
let summary = "Represents a Fortran USE statement";
3060+
let description = [{
3061+
This operation records a Fortran USE statement with its associated only/rename
3062+
information. It has no runtime effect but preserves semantic information for
3063+
debug information generation.
3064+
3065+
The operation captures:
3066+
- The module being used (via module_name string)
3067+
- Symbol references to symbols imported via the ONLY clause (if present)
3068+
- Symbol renames (local_name and symbol reference)
3069+
3070+
Examples:
3071+
```
3072+
// USE mod1
3073+
fir.use_stmt "mod1"
3074+
3075+
// USE mod1, ONLY: var2
3076+
fir.use_stmt "mod1" only_symbols [@_QMmod1Evar2]
3077+
3078+
// USE mod2, var4 => var3
3079+
fir.use_stmt "mod2" renames [#fir.use_rename<"var4", @_QMmod2Evar3>]
3080+
3081+
// USE mod2, ONLY: var1, renamed => original
3082+
fir.use_stmt "mod2" only_symbols [@_QMmod2Evar1]
3083+
renames [#fir.use_rename<"renamed", @_QMmod2Eoriginal>]
3084+
```
3085+
}];
3086+
3087+
let arguments = (ins StrAttr:$module_name,
3088+
OptionalAttr<ArrayAttr>:$only_symbols, OptionalAttr<ArrayAttr>:$renames);
3089+
3090+
let assemblyFormat = [{
3091+
$module_name
3092+
(`only_symbols` `[` $only_symbols^ `]`)?
3093+
(`renames` `[` $renames^ `]`)?
3094+
attr-dict
3095+
}];
3096+
3097+
let extraClassDeclaration = [{
3098+
/// Returns true if this is a USE with ONLY clause
3099+
bool hasOnlyClause() { return getOnlySymbols().has_value(); }
3100+
3101+
/// Returns true if this has any renames
3102+
bool hasRenames() { return getRenames().has_value(); }
3103+
3104+
/// Returns true if this imports the entire module (no ONLY clause)
3105+
bool importsAll() { return !hasOnlyClause(); }
3106+
}];
3107+
}
3108+
30573109
def ImplicitFirTerminator : SingleBlockImplicitTerminator<"FirEndOp">;
30583110

30593111
def fir_TypeInfoOp : fir_Op<"type_info",

flang/include/flang/Semantics/scope.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,19 @@ struct EquivalenceObject {
5555
};
5656
using EquivalenceSet = std::vector<EquivalenceObject>;
5757

58+
// Preserved USE statement information for debug info generation.
59+
struct PreservedUseStmt {
60+
enum class Kind { UseOnly, UseRenames, UseAll };
61+
62+
std::string moduleName;
63+
Kind kind;
64+
std::vector<std::string> onlyNames; // For Kind::UseOnly
65+
std::vector<std::string> renames; // local_name (resolved via GetUltimate)
66+
67+
PreservedUseStmt(std::string modName, Kind k)
68+
: moduleName(std::move(modName)), kind(k) {}
69+
};
70+
5871
class Scope {
5972
using mapType = std::map<SourceName, MutableSymbolRef>;
6073

@@ -190,6 +203,17 @@ class Scope {
190203
return equivalenceSets_;
191204
}
192205
void add_equivalenceSet(EquivalenceSet &&);
206+
207+
// Access preserved USE statements for debug info generation
208+
std::list<PreservedUseStmt> &preservedUseStmts() {
209+
return preservedUseStmts_;
210+
}
211+
const std::list<PreservedUseStmt> &preservedUseStmts() const {
212+
return preservedUseStmts_;
213+
}
214+
void add_preservedUseStmt(PreservedUseStmt &&stmt) {
215+
preservedUseStmts_.push_back(std::move(stmt));
216+
}
193217
// Cray pointers are saved as map of pointee name -> pointer symbol
194218
const mapType &crayPointers() const { return crayPointers_; }
195219
void add_crayPointer(const SourceName &, Symbol &);
@@ -301,6 +325,7 @@ class Scope {
301325
mapType commonBlocks_;
302326
mapType commonBlockUses_; // USE-assocated COMMON blocks
303327
std::list<EquivalenceSet> equivalenceSets_;
328+
std::list<PreservedUseStmt> preservedUseStmts_;
304329
mapType crayPointers_;
305330
std::map<SourceName, common::Reference<Scope>> submodules_;
306331
std::list<DeclTypeSpec> declTypeSpecs_;

flang/lib/Lower/Bridge.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,95 @@ static mlir::FlatSymbolRefAttr gatherComponentInit(
226226
return mlir::FlatSymbolRefAttr::get(mlirContext, name);
227227
}
228228

229+
/// Emit fir.use_stmt operations for USE statements in the given scope
230+
static void
231+
emitUseStatementsFromScope(Fortran::lower::AbstractConverter &converter,
232+
mlir::OpBuilder &builder, mlir::Location loc,
233+
const Fortran::semantics::Scope &scope) {
234+
mlir::MLIRContext *context = builder.getContext();
235+
236+
for (const auto &preservedStmt : scope.preservedUseStmts()) {
237+
238+
auto getMangledName = [&](const std::string &localName) -> std::string {
239+
Fortran::parser::CharBlock charBlock{localName.data(), localName.size()};
240+
const auto *sym = scope.FindSymbol(charBlock);
241+
if (!sym)
242+
return "";
243+
244+
const auto &ultimateSym = sym->GetUltimate();
245+
246+
// Skip cases which can cause mangleName to fail.
247+
if (ultimateSym.has<Fortran::semantics::DerivedTypeDetails>())
248+
return "";
249+
250+
if (const auto *generic =
251+
ultimateSym.detailsIf<Fortran::semantics::GenericDetails>()) {
252+
if (!generic->specific())
253+
return "";
254+
}
255+
256+
return converter.mangleName(ultimateSym);
257+
};
258+
259+
mlir::StringAttr moduleNameAttr =
260+
mlir::StringAttr::get(context, preservedStmt.moduleName);
261+
262+
llvm::SmallVector<mlir::Attribute> onlySymbolAttrs;
263+
llvm::SmallVector<mlir::Attribute> renameAttrs;
264+
265+
switch (preservedStmt.kind) {
266+
case Fortran::semantics::PreservedUseStmt::Kind::UseOnly:
267+
// USE mod, ONLY: list
268+
for (const auto &name : preservedStmt.onlyNames) {
269+
std::string mangledName = getMangledName(name);
270+
if (!mangledName.empty())
271+
onlySymbolAttrs.push_back(
272+
mlir::FlatSymbolRefAttr::get(context, mangledName));
273+
}
274+
// Handle renames within ONLY clause
275+
for (const auto &local : preservedStmt.renames) {
276+
std::string mangledName = getMangledName(local);
277+
if (!mangledName.empty()) {
278+
auto localAttr = mlir::StringAttr::get(context, local);
279+
auto symbolRef = mlir::FlatSymbolRefAttr::get(context, mangledName);
280+
renameAttrs.push_back(
281+
fir::UseRenameAttr::get(context, localAttr, symbolRef));
282+
}
283+
}
284+
break;
285+
286+
case Fortran::semantics::PreservedUseStmt::Kind::UseRenames:
287+
// USE mod, renames (import all with some renames)
288+
for (const auto &local : preservedStmt.renames) {
289+
std::string mangledName = getMangledName(local);
290+
if (!mangledName.empty()) {
291+
auto localAttr = mlir::StringAttr::get(context, local);
292+
auto symbolRef = mlir::FlatSymbolRefAttr::get(context, mangledName);
293+
renameAttrs.push_back(
294+
fir::UseRenameAttr::get(context, localAttr, symbolRef));
295+
}
296+
}
297+
break;
298+
299+
case Fortran::semantics::PreservedUseStmt::Kind::UseAll:
300+
// USE mod (import all, no renames)
301+
break;
302+
}
303+
304+
// Create optional array attributes
305+
mlir::ArrayAttr onlySymbolsAttr =
306+
onlySymbolAttrs.empty()
307+
? mlir::ArrayAttr()
308+
: mlir::ArrayAttr::get(context, onlySymbolAttrs);
309+
mlir::ArrayAttr renamesAttr =
310+
renameAttrs.empty() ? mlir::ArrayAttr()
311+
: mlir::ArrayAttr::get(context, renameAttrs);
312+
313+
fir::UseStmtOp::create(builder, loc, moduleNameAttr, onlySymbolsAttr,
314+
renamesAttr);
315+
}
316+
}
317+
229318
/// Helper class to generate the runtime type info global data and the
230319
/// fir.type_info operations that contain the dipatch tables (if any).
231320
/// The type info global data is required to describe the derived type to the
@@ -6126,6 +6215,9 @@ class FirConverter : public Fortran::lower::AbstractConverter {
61266215

61276216
mapDummiesAndResults(funit, callee);
61286217

6218+
// Emit USE statement operations for debug info generation
6219+
emitUseStatementsFromScope(*this, *builder, toLocation(), funit.getScope());
6220+
61296221
// Map host associated symbols from parent procedure if any.
61306222
if (funit.parentHasHostAssoc())
61316223
funit.parentHostAssoc().internalProcedureBindings(*this, localSymbols);

flang/lib/Optimizer/CodeGen/CodeGen.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3446,6 +3446,20 @@ struct NoReassocOpConversion : public fir::FIROpConversion<fir::NoReassocOp> {
34463446
}
34473447
};
34483448

3449+
/// Erase `fir.use_stmt` operations during LLVM lowering.
3450+
/// These operations are only used for debug info generation by the
3451+
/// AddDebugInfo pass and have no runtime representation.
3452+
struct UseStmtOpConversion : public fir::FIROpConversion<fir::UseStmtOp> {
3453+
using FIROpConversion::FIROpConversion;
3454+
3455+
llvm::LogicalResult
3456+
matchAndRewrite(fir::UseStmtOp useStmt, OpAdaptor adaptor,
3457+
mlir::ConversionPatternRewriter &rewriter) const override {
3458+
rewriter.eraseOp(useStmt);
3459+
return mlir::success();
3460+
}
3461+
};
3462+
34493463
static void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
34503464
std::optional<mlir::ValueRange> destOps,
34513465
mlir::ConversionPatternRewriter &rewriter,
@@ -4429,8 +4443,9 @@ void fir::populateFIRToLLVMConversionPatterns(
44294443
SliceOpConversion, StoreOpConversion, StringLitOpConversion,
44304444
SubcOpConversion, TypeDescOpConversion, TypeInfoOpConversion,
44314445
UnboxCharOpConversion, UnboxProcOpConversion, UndefOpConversion,
4432-
UnreachableOpConversion, XArrayCoorOpConversion, XEmboxOpConversion,
4433-
XReboxOpConversion, ZeroOpConversion>(converter, options);
4446+
UnreachableOpConversion, UseStmtOpConversion, XArrayCoorOpConversion,
4447+
XEmboxOpConversion, XReboxOpConversion, ZeroOpConversion>(converter,
4448+
options);
44344449

44354450
// Patterns that are populated without a type converter do not trigger
44364451
// target materializations for the operands of the root op.

flang/lib/Semantics/resolve-names.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3638,6 +3638,86 @@ void ModuleVisitor::Post(const parser::UseStmt &x) {
36383638
for (const auto &[name, symbol] : useModuleScope_->commonBlockUses()) {
36393639
currScope().AddCommonBlockUse(name, symbol->attrs(), symbol->GetUltimate());
36403640
}
3641+
3642+
// Preserve USE statement information for debug info generation
3643+
std::string moduleName{x.moduleName.source.ToString()};
3644+
3645+
if (const auto *onlyList{std::get_if<std::list<parser::Only>>(&x.u)}) {
3646+
// USE mod, ONLY: list
3647+
PreservedUseStmt stmt{moduleName, PreservedUseStmt::Kind::UseOnly};
3648+
3649+
for (const auto &only : *onlyList) {
3650+
common::visit(
3651+
common::visitors{
3652+
[&](const parser::Rename &rename) {
3653+
// ONLY with rename: ONLY: local => use
3654+
common::visit(common::visitors{
3655+
[&](const parser::Rename::Names &names) {
3656+
std::string localName{
3657+
std::get<0>(names.t).source.ToString()};
3658+
stmt.renames.push_back(localName);
3659+
},
3660+
[&](const parser::Rename::Operators &) {
3661+
// Operator renames - not commonly needed
3662+
// for debug info
3663+
},
3664+
},
3665+
rename.u);
3666+
},
3667+
[&](const parser::Name &name) {
3668+
// ONLY without rename: ONLY: name
3669+
stmt.onlyNames.push_back(name.source.ToString());
3670+
},
3671+
[&](const common::Indirection<parser::GenericSpec> &genericSpec) {
3672+
// Generic spec can contain a Name (for regular symbols) or
3673+
// operators
3674+
common::visit(common::visitors{
3675+
[&](const parser::Name &name) {
3676+
stmt.onlyNames.push_back(
3677+
name.source.ToString());
3678+
},
3679+
[&](const auto &) {
3680+
// Operators and special forms - not
3681+
// commonly needed for variable debug info
3682+
},
3683+
},
3684+
genericSpec.value().u);
3685+
},
3686+
},
3687+
only.u);
3688+
}
3689+
3690+
currScope().add_preservedUseStmt(std::move(stmt));
3691+
} else if (const auto *renameList{
3692+
std::get_if<std::list<parser::Rename>>(&x.u)}) {
3693+
// USE mod with optional renames (not ONLY)
3694+
if (renameList->empty()) {
3695+
// USE mod (import all, no renames)
3696+
PreservedUseStmt stmt{moduleName, PreservedUseStmt::Kind::UseAll};
3697+
currScope().add_preservedUseStmt(std::move(stmt));
3698+
} else {
3699+
// USE mod, renames (import all with some renames)
3700+
PreservedUseStmt stmt{moduleName, PreservedUseStmt::Kind::UseRenames};
3701+
3702+
for (const auto &rename : *renameList) {
3703+
common::visit(common::visitors{
3704+
[&](const parser::Rename::Names &names) {
3705+
std::string localName{
3706+
std::get<0>(names.t).source.ToString()};
3707+
stmt.renames.push_back(localName);
3708+
},
3709+
[&](const parser::Rename::Operators &) {
3710+
// Operator renames - not commonly needed for debug
3711+
// info
3712+
},
3713+
},
3714+
rename.u);
3715+
}
3716+
3717+
currScope().add_preservedUseStmt(std::move(stmt));
3718+
}
3719+
}
3720+
36413721
useModuleScope_ = nullptr;
36423722
}
36433723

0 commit comments

Comments
 (0)