[flang-commits] [flang] defde18 - [flang][NFC] Add FortranObjectLoadOpInterface for load provenance (#221088)
via flang-commits
flang-commits at lists.llvm.org
Fri Sep 4 09:22:16 PDT 2026
Author: Slava Zakharin
Date: 2026-09-04T09:22:11-07:00
New Revision: defde181bce666df9ceeb57cf8f8ad4cda6c6675
URL: https://github.com/llvm/llvm-project/commit/defde181bce666df9ceeb57cf8f8ad4cda6c6675
DIFF: https://github.com/llvm/llvm-project/commit/defde181bce666df9ceeb57cf8f8ad4cda6c6675.diff
LOG: [flang][NFC] Add FortranObjectLoadOpInterface for load provenance (#221088)
The definition walk in AliasAnalysis::getSourceImpl already keys the
address half of a descriptor access chain on
FortranObjectViewOpInterface (fir.box_addr, fir.create_box,
fir.convert), but the load step was hardcoded to fir::LoadOp. Any other
operation whose result is a value read from memory therefore terminated
the walk at SourceKind::Unknown, losing the AllocDeref/PointerDeref root
and coarsening alias results for everything reached through the loaded
descriptor.
Add FortranObjectLoadOpInterface as the value-producing dual of
FortranObjectViewOpInterface: a view op forwards an address, a load-like
op yields the value read from one. It declares a single getLoadSource
method reporting the memory reference a result was read from. fir.load
implements it as getMemref(), so this is NFC, and the box-load case in
getSourceImpl now keys on the interface instead of the concrete op.
The interface documents provenance only and makes no purity claim, so an
operation whose result depends on more than the contents of the memory
reference can participate as long as it models those extra dependences
through its memory effects. Consumers must not use the interface to
justify value-based transformations such as CSE or load forwarding.
Assisted-by: Cursor
Added:
Modified:
flang/include/flang/Optimizer/Dialect/FIROps.td
flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td
flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
Removed:
################################################################################
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index 57e23ecc01af8..faefdbd37b76f 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -298,6 +298,7 @@ def fir_FreeMemOp : fir_Op<"freemem", []> {
def fir_LoadOp : fir_OneResultOp<"load", [
FirAliasTagOpInterface,
+ fir_FortranObjectLoadOpInterface,
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
DeclareOpInterfaceMethods<PromotableMemOpInterface>
]> {
@@ -332,6 +333,9 @@ def fir_LoadOp : fir_OneResultOp<"load", [
let extraClassDeclaration = [{
static mlir::ParseResult getElementOf(mlir::Type &ele, mlir::Type ref);
+
+ // FortranObjectLoadOpInterface methods:
+ mlir::Value getLoadSource(mlir::OpResult) { return getMemref(); }
}];
}
diff --git a/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td b/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td
index 4bdb13fb54708..65550b7cc03f2 100644
--- a/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td
+++ b/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td
@@ -334,4 +334,47 @@ def fir_FortranObjectViewOpInterface
];
}
+def fir_FortranObjectLoadOpInterface
+ : OpInterface<"FortranObjectLoadOpInterface"> {
+ let description = [{
+ Interface for operations that produce a result by reading the contents
+ of a memory reference passed as one of their operands.
+ For example:
+ ```
+ %0 = fir.load %x : !fir.ref<!fir.box<!fir.heap<f32>>>
+ ```
+ This is the value-producing counterpart of
+ `FortranObjectViewOpInterface`: a view op forwards an *address*, while
+ an op implementing this interface yields the *value* that was read from
+ an address. FIR alias analysis uses it to continue the walk from a
+ loaded descriptor to the memory it was read from, so that it can find
+ the original Fortran object that the descriptor refers to.
+
+ The interface describes provenance only. It states where the result was
+ read from; it does not state that the operation is a pure load. An
+ implementation may produce a result that depends on more than the
+ contents of the memory reference, in which case it must model those
+ extra dependences through its memory effects. Consumers must therefore
+ not use this interface to justify value-based transformations such as
+ CSE or load forwarding.
+ }];
+ let cppNamespace = "::fir";
+
+ let methods =
+ [InterfaceMethod<
+ /*desc=*/
+ [{ Returns the memory reference operand that the given OpResult
+ was read from. }],
+ /*retTy=*/"::mlir::Value",
+ /*methodName=*/"getLoadSource",
+ /*args=*/(ins "::mlir::OpResult":$resultValue),
+ /*methodBody=*/"",
+ /*defaultImplementation=*/[{
+ assert($_op.getOperation() == resultValue.getOwner() &&
+ "resultValue must be a result of this operation");
+ return $_op.getLoadSource(resultValue);
+ }]>,
+ ];
+}
+
#endif // FORTRANVARIABLEINTERFACE
diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index 4534adb66d748..32429d554b7a1 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -1318,16 +1318,24 @@ AliasAnalysis::getSourceImpl(mlir::Value v, bool getLastInstantiationPoint,
type = SourceKind::Allocate;
breakFromLoop = true;
})
- .Case([&](fir::LoadOp op) {
+ .Case([&](fir::FortranObjectLoadOpInterface op) {
+ // Keyed off the interface rather than fir::LoadOp so that any
+ // operation yielding a value read from memory participates in the
+ // walk. The interface reports provenance only, which is all this
+ // walk needs; it does not promise the operation is a pure load.
+ // Keep this ahead of the FortranObjectViewOpInterface case below,
+ // which would otherwise win for an operation implementing both.
+ mlir::Value loadSource = op.getLoadSource(opResult);
+
// If load is inside target and it points to mapped item,
// continue tracking.
- Operation *loadMemrefOp = op.getMemref().getDefiningOp();
+ Operation *loadMemrefOp = loadSource.getDefiningOp();
bool isDeclareOp =
llvm::isa_and_present<fir::DeclareOp>(loadMemrefOp) ||
llvm::isa_and_present<hlfir::DeclareOp>(loadMemrefOp);
if (isDeclareOp &&
llvm::isa<omp::TargetOp>(loadMemrefOp->getParentOp())) {
- v = op.getMemref();
+ v = loadSource;
defOp = v.getDefiningOp();
return;
}
@@ -1353,7 +1361,7 @@ AliasAnalysis::getSourceImpl(mlir::Value v, bool getLastInstantiationPoint,
// Passing true here would stop the inner walk at the declare
// and force SourceKind::Indirect, which spuriously coarsens
// getCallModRef (e.g. for box_addr of allocatable dummies).
- auto boxSrc = getSource(op.getMemref(),
+ auto boxSrc = getSource(loadSource,
/*getLastInstantiationPoint=*/false,
collectScopedOrigins);
attributes |= boxSrc.attributes;
More information about the flang-commits
mailing list