Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions flang/include/flang/Optimizer/Dialect/FIRAttr.td
Original file line number Diff line number Diff line change
Expand Up @@ -239,4 +239,21 @@ def fir_FortranInlineAttr
: EnumAttr<FIROpsDialect, fir_FortranInlineEnum, "inline_attrs"> {
let assemblyFormat = "`<` $value `>`";
}

// USE statement rename mapping: local_name => use_name
def fir_UseRenameAttr : fir_Attr<"UseRename"> {
let mnemonic = "use_rename";
let summary = "Represents a rename in a Fortran USE statement";
let description = [{
This attribute stores the mapping for a renamed symbol in a USE statement.
For example, in "USE mod, local_var => module_var", this stores the
local name and a symbol reference to the module variable.
}];

let parameters = (ins "mlir::StringAttr":$local_name,
"mlir::FlatSymbolRefAttr":$symbol);

let assemblyFormat = "`<` $local_name `,` $symbol `>`";
}

#endif // FIR_DIALECT_FIR_ATTRS
52 changes: 52 additions & 0 deletions flang/include/flang/Optimizer/Dialect/FIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -3054,6 +3054,58 @@ def fir_GlobalLenOp : fir_Op<"global_len", []> {
}];
}

def fir_UseStmtOp
: fir_Op<"use_stmt", [MemoryEffects<[MemWrite<DebuggingResource>]>]> {
let summary = "Represents a Fortran USE statement";
let description = [{
This operation records a Fortran USE statement with its associated only/rename
information. It has no runtime effect but preserves semantic information for
debug information generation.

The operation captures:
- The module being used (via module_name string)
- Symbol references to symbols imported via the ONLY clause (if present)
- Symbol renames (local_name and symbol reference)

Examples:
```
// USE mod1
fir.use_stmt "mod1"

// USE mod1, ONLY: var2
fir.use_stmt "mod1" only_symbols [@_QMmod1Evar2]

// USE mod2, var4 => var3
fir.use_stmt "mod2" renames [#fir.use_rename<"var4", @_QMmod2Evar3>]

// USE mod2, ONLY: var1, renamed => original
fir.use_stmt "mod2" only_symbols [@_QMmod2Evar1]
renames [#fir.use_rename<"renamed", @_QMmod2Eoriginal>]
```
}];

let arguments = (ins StrAttr:$module_name,
OptionalAttr<ArrayAttr>:$only_symbols, OptionalAttr<ArrayAttr>:$renames);

let assemblyFormat = [{
$module_name
(`only_symbols` `[` $only_symbols^ `]`)?
(`renames` `[` $renames^ `]`)?
attr-dict
}];

let extraClassDeclaration = [{
/// Returns true if this is a USE with ONLY clause
bool hasOnlyClause() { return getOnlySymbols().has_value(); }

/// Returns true if this has any renames
bool hasRenames() { return getRenames().has_value(); }

/// Returns true if this imports the entire module (no ONLY clause)
bool importsAll() { return !hasOnlyClause(); }
}];
}

def ImplicitFirTerminator : SingleBlockImplicitTerminator<"FirEndOp">;

def fir_TypeInfoOp : fir_Op<"type_info",
Expand Down
25 changes: 25 additions & 0 deletions flang/include/flang/Semantics/scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ struct EquivalenceObject {
};
using EquivalenceSet = std::vector<EquivalenceObject>;

// Preserved USE statement information for debug info generation.
struct PreservedUseStmt {
enum class Kind { UseOnly, UseRenames, UseAll };

std::string moduleName;
Kind kind;
std::vector<std::string> onlyNames; // For Kind::UseOnly
std::vector<std::string> renames; // local_name (resolved via GetUltimate)

PreservedUseStmt(std::string modName, Kind k)
: moduleName(std::move(modName)), kind(k) {}
};

class Scope {
using mapType = std::map<SourceName, MutableSymbolRef>;

Expand Down Expand Up @@ -190,6 +203,17 @@ class Scope {
return equivalenceSets_;
}
void add_equivalenceSet(EquivalenceSet &&);

// Access preserved USE statements for debug info generation
std::list<PreservedUseStmt> &preservedUseStmts() {
return preservedUseStmts_;
}
const std::list<PreservedUseStmt> &preservedUseStmts() const {
return preservedUseStmts_;
}
void add_preservedUseStmt(PreservedUseStmt &&stmt) {
preservedUseStmts_.push_back(std::move(stmt));
}
// Cray pointers are saved as map of pointee name -> pointer symbol
const mapType &crayPointers() const { return crayPointers_; }
void add_crayPointer(const SourceName &, Symbol &);
Expand Down Expand Up @@ -301,6 +325,7 @@ class Scope {
mapType commonBlocks_;
mapType commonBlockUses_; // USE-assocated COMMON blocks
std::list<EquivalenceSet> equivalenceSets_;
std::list<PreservedUseStmt> preservedUseStmts_;
mapType crayPointers_;
std::map<SourceName, common::Reference<Scope>> submodules_;
std::list<DeclTypeSpec> declTypeSpecs_;
Expand Down
92 changes: 92 additions & 0 deletions flang/lib/Lower/Bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,95 @@ static mlir::FlatSymbolRefAttr gatherComponentInit(
return mlir::FlatSymbolRefAttr::get(mlirContext, name);
}

/// Emit fir.use_stmt operations for USE statements in the given scope
static void
emitUseStatementsFromScope(Fortran::lower::AbstractConverter &converter,
mlir::OpBuilder &builder, mlir::Location loc,
const Fortran::semantics::Scope &scope) {
mlir::MLIRContext *context = builder.getContext();

for (const auto &preservedStmt : scope.preservedUseStmts()) {

auto getMangledName = [&](const std::string &localName) -> std::string {
Fortran::parser::CharBlock charBlock{localName.data(), localName.size()};
const auto *sym = scope.FindSymbol(charBlock);
if (!sym)
return "";

const auto &ultimateSym = sym->GetUltimate();

// Skip cases which can cause mangleName to fail.
if (ultimateSym.has<Fortran::semantics::DerivedTypeDetails>())
return "";

if (const auto *generic =
ultimateSym.detailsIf<Fortran::semantics::GenericDetails>()) {
if (!generic->specific())
return "";
}

return converter.mangleName(ultimateSym);
};

mlir::StringAttr moduleNameAttr =
mlir::StringAttr::get(context, preservedStmt.moduleName);

llvm::SmallVector<mlir::Attribute> onlySymbolAttrs;
llvm::SmallVector<mlir::Attribute> renameAttrs;

switch (preservedStmt.kind) {
case Fortran::semantics::PreservedUseStmt::Kind::UseOnly:
// USE mod, ONLY: list
for (const auto &name : preservedStmt.onlyNames) {
std::string mangledName = getMangledName(name);
if (!mangledName.empty())
onlySymbolAttrs.push_back(
mlir::FlatSymbolRefAttr::get(context, mangledName));
}
// Handle renames within ONLY clause
for (const auto &local : preservedStmt.renames) {
std::string mangledName = getMangledName(local);
if (!mangledName.empty()) {
auto localAttr = mlir::StringAttr::get(context, local);
auto symbolRef = mlir::FlatSymbolRefAttr::get(context, mangledName);
renameAttrs.push_back(
fir::UseRenameAttr::get(context, localAttr, symbolRef));
}
}
break;

case Fortran::semantics::PreservedUseStmt::Kind::UseRenames:
// USE mod, renames (import all with some renames)
for (const auto &local : preservedStmt.renames) {
std::string mangledName = getMangledName(local);
if (!mangledName.empty()) {
auto localAttr = mlir::StringAttr::get(context, local);
auto symbolRef = mlir::FlatSymbolRefAttr::get(context, mangledName);
renameAttrs.push_back(
fir::UseRenameAttr::get(context, localAttr, symbolRef));
}
}
break;

case Fortran::semantics::PreservedUseStmt::Kind::UseAll:
// USE mod (import all, no renames)
break;
}

// Create optional array attributes
mlir::ArrayAttr onlySymbolsAttr =
onlySymbolAttrs.empty()
? mlir::ArrayAttr()
: mlir::ArrayAttr::get(context, onlySymbolAttrs);
mlir::ArrayAttr renamesAttr =
renameAttrs.empty() ? mlir::ArrayAttr()
: mlir::ArrayAttr::get(context, renameAttrs);

fir::UseStmtOp::create(builder, loc, moduleNameAttr, onlySymbolsAttr,
renamesAttr);
}
}

/// Helper class to generate the runtime type info global data and the
/// fir.type_info operations that contain the dipatch tables (if any).
/// The type info global data is required to describe the derived type to the
Expand Down Expand Up @@ -6126,6 +6215,9 @@ class FirConverter : public Fortran::lower::AbstractConverter {

mapDummiesAndResults(funit, callee);

// Emit USE statement operations for debug info generation
emitUseStatementsFromScope(*this, *builder, toLocation(), funit.getScope());

// Map host associated symbols from parent procedure if any.
if (funit.parentHasHostAssoc())
funit.parentHostAssoc().internalProcedureBindings(*this, localSymbols);
Expand Down
19 changes: 17 additions & 2 deletions flang/lib/Optimizer/CodeGen/CodeGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3446,6 +3446,20 @@ struct NoReassocOpConversion : public fir::FIROpConversion<fir::NoReassocOp> {
}
};

/// Erase `fir.use_stmt` operations during LLVM lowering.
/// These operations are only used for debug info generation by the
/// AddDebugInfo pass and have no runtime representation.
struct UseStmtOpConversion : public fir::FIROpConversion<fir::UseStmtOp> {
using FIROpConversion::FIROpConversion;

llvm::LogicalResult
matchAndRewrite(fir::UseStmtOp useStmt, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
rewriter.eraseOp(useStmt);
return mlir::success();
}
};

static void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
std::optional<mlir::ValueRange> destOps,
mlir::ConversionPatternRewriter &rewriter,
Expand Down Expand Up @@ -4429,8 +4443,9 @@ void fir::populateFIRToLLVMConversionPatterns(
SliceOpConversion, StoreOpConversion, StringLitOpConversion,
SubcOpConversion, TypeDescOpConversion, TypeInfoOpConversion,
UnboxCharOpConversion, UnboxProcOpConversion, UndefOpConversion,
UnreachableOpConversion, XArrayCoorOpConversion, XEmboxOpConversion,
XReboxOpConversion, ZeroOpConversion>(converter, options);
UnreachableOpConversion, UseStmtOpConversion, XArrayCoorOpConversion,
XEmboxOpConversion, XReboxOpConversion, ZeroOpConversion>(converter,
options);

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