llvm-project/flang/lib/Optimizer/Dialect/FIROperationMoveOpInterface.cpp
Slava Zakharin 09ae1bf8b7
[flang] Added OperationMoveOpInterface for controlling LICM. (#175108)
In #173438 I added a FIR specific loop invariant code motion pass.

During the review, Tom pointed out certain limitations about OpenMP
dialect operations that should be taken into consideration during
transformations such as LICM:
https://github.com/llvm/llvm-project/pull/173438#discussion_r2657612148

I also found issues with hoisting operations out of `acc.loop`
operations in certain conditions (see the added test in `licm.fir`).

I am proposing a new operation interface that will allow to control
movement of operations during MLIR transformations. In particular, I
propose two methods (there might be more):
* op.canMoveOutOf(cand) - returns true, if it is allowed to move 'cand'
operation out of 'op'.
* op.canMoveFromDescendant(descendant, cand) - return true, if it is
allowed to move 'cand' out of 'descendant' and into 'op'.

I used the new interface to get rid of explicit OpenMP interfaces checks
in Flang's LICM, and I also used it for `acc.loop` operation (though, I
provided conservative initial implementation).

The new interface is part of FIR dialect, but I think it would better
fit into the core MLIR set of interfaces so that the checks that I make
in Flang's LICM are actually done in
`mlir::moveLoopInvariantCode`. Moreover, other code movement
transformations that may appear in MLIR may also need to use such an
interface.

I would like to get some feedback on whether it is reasonable to move
the interface to core MLIR.
2026-01-16 08:32:38 -08:00

50 lines
1.8 KiB
C++

//===-- FIROperationMoveOpInterface.cpp -----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
//
//===----------------------------------------------------------------------===//
#include "flang/Optimizer/Dialect/FIROperationMoveOpInterface.h"
#include "flang/Optimizer/Dialect/FIROperationMoveOpInterface.cpp.inc"
llvm::LogicalResult
fir::detail::verifyOperationMoveOpInterface(mlir::Operation *op) {
// It does not make sense to use this interface for operations
// without any regions.
if (op->getNumRegions() == 0)
return op->emitOpError("must contain at least one region");
return llvm::success();
}
bool fir::canMoveFromDescendant(mlir::Operation *op,
mlir::Operation *descendant,
mlir::Operation *candidate) {
// Perform some sanity checks.
assert(op->isProperAncestor(descendant) &&
"op must be an ancestor of descendant");
if (candidate)
assert(descendant->isProperAncestor(candidate) &&
"descendant must be an ancestor of candidate");
if (auto iface = mlir::dyn_cast<OperationMoveOpInterface>(op))
return iface.canMoveFromDescendant(descendant, candidate);
return true;
}
bool fir::canMoveOutOf(mlir::Operation *op, mlir::Operation *candidate) {
if (candidate)
assert(op->isProperAncestor(candidate) &&
"op must be an ancestor of candidate");
if (auto iface = mlir::dyn_cast<OperationMoveOpInterface>(op))
return iface.canMoveOutOf(candidate);
return true;
}