[flang-commits] [flang] [flang] Added ConditionallySpeculatable and Pure for some FIR ops. (PR #174013)
via flang-commits
flang-commits at lists.llvm.org
Tue Dec 30 11:51:24 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-fir-hlfir
Author: Slava Zakharin (vzakhari)
<details>
<summary>Changes</summary>
This patch implements `ConditionallySpeculatable` interface for some
FIR operations (`embox`, `rebox`, `box_addr`, `box_dims` and `convert`).
It also adds `Pure` trait for `fir.shape`, `fir.shapeshift`,
`fir.shift and `fir.slice`.
I could have split this into multiple patches, but the changes
are better tested together on real apps, and the amount of affected
code is small.
There are more `NoMemoryEffect` operations for which I am planning
to do the same in future PRs.
---
Patch is 238.18 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/174013.diff
21 Files Affected:
- (modified) flang/include/flang/Optimizer/Dialect/FIROps.td (+27-8)
- (modified) flang/include/flang/Optimizer/Transforms/Passes.td (+11)
- (modified) flang/lib/Optimizer/Analysis/AliasAnalysis.cpp (+34-24)
- (modified) flang/lib/Optimizer/Dialect/FIROps.cpp (+65)
- (modified) flang/lib/Optimizer/Passes/Pipelines.cpp (+4)
- (modified) flang/lib/Optimizer/Transforms/CMakeLists.txt (+28-25)
- (added) flang/lib/Optimizer/Transforms/LoopInvariantCodeMotion.cpp (+233)
- (modified) flang/test/Analysis/AliasAnalysis/gen_mod_ref_test.py (+10)
- (modified) flang/test/Analysis/AliasAnalysis/modref-call-dummies.f90 (+59-2)
- (modified) flang/test/Analysis/AliasAnalysis/modref-call-globals.f90 (+22)
- (added) flang/test/Analysis/AliasAnalysis/modref-call-recursive.fir (+62)
- (modified) flang/test/Driver/bbc-mlir-pass-pipeline.f90 (+3)
- (modified) flang/test/Driver/mlir-pass-pipeline.f90 (+3)
- (modified) flang/test/Driver/tco-test-gen.fir (+1-2)
- (modified) flang/test/Fir/basic-program.fir (+3)
- (modified) flang/test/Lower/array-expression-assumed-size.f90 (+164-159)
- (modified) flang/test/Lower/array-substring.f90 (+1-1)
- (modified) flang/test/Lower/array-temp.f90 (+262-250)
- (modified) flang/test/Lower/host-associated.f90 (+5-5)
- (modified) flang/test/Lower/vector-subscript-io.f90 (+9-9)
- (added) flang/test/Transforms/licm.fir (+1543)
``````````diff
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index 7bfb304b973f5..feeed99beea5d 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -819,6 +819,7 @@ def fir_HasValueOp : fir_Op<"has_value", [Terminator, HasParent<"GlobalOp">]> {
//===----------------------------------------------------------------------===//
def fir_EmboxOp : fir_Op<"embox", [NoMemoryEffect, AttrSizedOperandSegments,
+ ConditionallySpeculatable,
fir_FortranObjectViewOpInterface]> {
let summary = "boxes a given reference and (optional) dimension information";
@@ -886,10 +887,14 @@ def fir_EmboxOp : fir_Op<"embox", [NoMemoryEffect, AttrSizedOperandSegments,
// FortranObjectViewOpInterface methods:
mlir::Value getViewSource(mlir::OpResult) { return getMemref(); }
std::optional<std::int64_t> getViewOffset(mlir::OpResult);
+
+ // Interface method for ConditionallySpeculatable.
+ mlir::Speculation::Speculatability getSpeculatability();
}];
}
def fir_ReboxOp : fir_Op<"rebox", [NoMemoryEffect, AttrSizedOperandSegments,
+ ConditionallySpeculatable,
fir_FortranObjectViewOpInterface]> {
let summary =
"create a box given another box and (optional) dimension information";
@@ -945,6 +950,9 @@ def fir_ReboxOp : fir_Op<"rebox", [NoMemoryEffect, AttrSizedOperandSegments,
// FortranObjectViewOpInterface methods:
mlir::Value getViewSource(mlir::OpResult) { return getBox(); }
std::optional<std::int64_t> getViewOffset(mlir::OpResult);
+
+ // Interface method for ConditionallySpeculatable.
+ mlir::Speculation::Speculatability getSpeculatability();
}];
}
@@ -1096,6 +1104,7 @@ def fir_UnboxProcOp : fir_SimpleOp<"unboxproc", [NoMemoryEffect]> {
def fir_BoxAddrOp
: fir_SimpleOneResultOp<"box_addr", [NoMemoryEffect,
+ ConditionallySpeculatable,
fir_FortranObjectViewOpInterface]> {
let summary = "return a memory reference to the boxed value";
@@ -1124,6 +1133,9 @@ def fir_BoxAddrOp
// FortranObjectViewOpInterface methods:
mlir::Value getViewSource(mlir::OpResult) { return getVal(); }
std::optional<std::int64_t> getViewOffset(mlir::OpResult);
+
+ // Interface method for ConditionallySpeculatable.
+ mlir::Speculation::Speculatability getSpeculatability();
}];
}
@@ -1146,7 +1158,8 @@ def fir_BoxCharLenOp : fir_SimpleOp<"boxchar_len", [NoMemoryEffect]> {
let hasFolder = 1;
}
-def fir_BoxDimsOp : fir_Op<"box_dims", [NoMemoryEffect]> {
+def fir_BoxDimsOp
+ : fir_Op<"box_dims", [NoMemoryEffect, ConditionallySpeculatable]> {
let summary = "return the dynamic dimension information for the boxed value";
let description = [{
@@ -1178,6 +1191,8 @@ def fir_BoxDimsOp : fir_Op<"box_dims", [NoMemoryEffect]> {
mlir::Value getLowerBound() {return getResult(0);};
mlir::Value getExtent() {return getResult(1);};
mlir::Value getByteStride() {return getResult(2);};
+ // Interface method for ConditionallySpeculatable.
+ mlir::Speculation::Speculatability getSpeculatability();
}];
}
@@ -1975,7 +1990,7 @@ def fir_FieldIndexOp : fir_OneResultOp<"field_index", [NoMemoryEffect]> {
}];
}
-def fir_ShapeOp : fir_Op<"shape", [NoMemoryEffect]> {
+def fir_ShapeOp : fir_Op<"shape", [Pure]> {
let summary = "generate an abstract shape vector of type `!fir.shape`";
@@ -2004,7 +2019,7 @@ def fir_ShapeOp : fir_Op<"shape", [NoMemoryEffect]> {
let builders = [OpBuilder<(ins "mlir::ValueRange":$extents)>];
}
-def fir_ShapeShiftOp : fir_Op<"shape_shift", [NoMemoryEffect]> {
+def fir_ShapeShiftOp : fir_Op<"shape_shift", [Pure]> {
let summary = [{
generate an abstract shape and shift vector of type `!fir.shapeshift`
@@ -2054,7 +2069,7 @@ def fir_ShapeShiftOp : fir_Op<"shape_shift", [NoMemoryEffect]> {
}];
}
-def fir_ShiftOp : fir_Op<"shift", [NoMemoryEffect]> {
+def fir_ShiftOp : fir_Op<"shift", [Pure]> {
let summary = "generate an abstract shift vector of type `!fir.shift`";
@@ -2081,7 +2096,7 @@ def fir_ShiftOp : fir_Op<"shift", [NoMemoryEffect]> {
let hasVerifier = 1;
}
-def fir_SliceOp : fir_Op<"slice", [NoMemoryEffect, AttrSizedOperandSegments]> {
+def fir_SliceOp : fir_Op<"slice", [Pure, AttrSizedOperandSegments]> {
let summary = "generate an abstract slice vector of type `!fir.slice`";
@@ -2252,9 +2267,9 @@ def fir_LenParamIndexOp : fir_OneResultOp<"len_param_index", [NoMemoryEffect]> {
// Fortran loops
//===----------------------------------------------------------------------===//
-def fir_ResultOp : fir_Op<"result",
- [NoMemoryEffect, ReturnLike, Terminator,
- ParentOneOf<["IfOp", "DoLoopOp", "IterWhileOp"]>]> {
+def fir_ResultOp
+ : fir_Op<"result", [Pure, ReturnLike, Terminator,
+ ParentOneOf<["IfOp", "DoLoopOp", "IterWhileOp"]>]> {
let summary = "special terminator for use in fir region operations";
let description = [{
@@ -2872,6 +2887,7 @@ def fir_VolatileCastOp : fir_SimpleOneResultOp<"volatile_cast", [Pure]> {
def fir_ConvertOp
: fir_SimpleOneResultOp<"convert", [NoMemoryEffect, ViewLikeOpInterface,
+ ConditionallySpeculatable,
fir_FortranObjectViewOpInterface]> {
let summary = "encapsulates all Fortran entity type conversions";
@@ -2917,6 +2933,9 @@ def fir_ConvertOp
// FortranObjectViewOpInterface methods:
mlir::Value getViewSource(mlir::OpResult) { return getValue(); }
std::optional<std::int64_t> getViewOffset(mlir::OpResult) { return 0; }
+
+ // Interface method for ConditionallySpeculatable.
+ mlir::Speculation::Speculatability getSpeculatability();
}];
let hasCanonicalizer = 1;
}
diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td
index f50202784e2dc..b6b78ec7b21b0 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.td
+++ b/flang/include/flang/Optimizer/Transforms/Passes.td
@@ -596,4 +596,15 @@ def MIFOpConversion : Pass<"mif-convert", "mlir::ModuleOp"> {
let dependentDialects = ["fir::FIROpsDialect", "mlir::LLVM::LLVMDialect"];
}
+def LoopInvariantCodeMotion : Pass<"flang-licm", "::mlir::func::FuncOp"> {
+ let summary = "Hoist invariants from loops";
+ let description = [{
+ Hoist invariants from loops. This is a FIR-specific version of loop
+ invariant code motion, which relies on FIR types, operations (such as
+ fir.declare) and interfaces such as FortranObjectViewOpInterface.
+ The pass only moves existing operations, so there are no dependent
+ dialects.
+ }];
+}
+
#endif // FLANG_OPTIMIZER_TRANSFORMS_PASSES
diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index d2656fa7b8ea8..0dc9dda3aa0f5 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -468,7 +468,8 @@ static ModRefResult getCallModRef(fir::CallOp call, mlir::Value var) {
// TODO: limit to Fortran functions??
// 1. Detect variables that can be accessed indirectly.
fir::AliasAnalysis aliasAnalysis;
- fir::AliasAnalysis::Source varSrc = aliasAnalysis.getSource(var);
+ fir::AliasAnalysis::Source varSrc =
+ aliasAnalysis.getSource(var, /*getLastInstantiationPoint=*/true);
// If the variable is not a user variable, we cannot safely assume that
// Fortran semantics apply (e.g., a bare alloca/allocmem result may very well
// be placed in an allocatable/pointer descriptor and escape).
@@ -498,6 +499,7 @@ static ModRefResult getCallModRef(fir::CallOp call, mlir::Value var) {
// At that stage, it has been ruled out that local (including the saved ones)
// and dummy cannot be indirectly accessed in the call.
if (varSrc.kind != fir::AliasAnalysis::SourceKind::Allocate &&
+ varSrc.kind != fir::AliasAnalysis::SourceKind::Argument &&
!varSrc.isDummyArgument()) {
if (varSrc.kind != fir::AliasAnalysis::SourceKind::Global ||
!isSavedLocal(varSrc))
@@ -523,19 +525,36 @@ static ModRefResult getCallModRef(fir::CallOp call, mlir::Value var) {
/// flow analysis to come 2) Allocate and Free effects are considered
/// modifying
ModRefResult AliasAnalysis::getModRef(Operation *op, Value location) {
- MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op);
- if (!interface) {
- if (auto call = llvm::dyn_cast<fir::CallOp>(op))
- return getCallModRef(call, location);
- return ModRefResult::getModAndRef();
- }
+ if (auto call = llvm::dyn_cast<fir::CallOp>(op))
+ return getCallModRef(call, location);
// Build a ModRefResult by merging the behavior of the effects of this
// operation.
+ ModRefResult result = ModRefResult::getNoModRef();
+ MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op);
+ if (op->hasTrait<mlir::OpTrait::HasRecursiveMemoryEffects>()) {
+ for (mlir::Region ®ion : op->getRegions()) {
+ result = result.merge(getModRef(region, location));
+ if (result.isModAndRef())
+ break;
+ }
+
+ // In MLIR, RecursiveMemoryEffects can be combined with
+ // MemoryEffectOpInterface to describe extra effects on top of the
+ // effects of the nested operations. However, the presence of
+ // RecursiveMemoryEffects and the absence of MemoryEffectOpInterface
+ // implies the operation has no other memory effects than the one of its
+ // nested operations.
+ if (!interface)
+ return result;
+ }
+
+ if (!interface || result.isModAndRef())
+ return ModRefResult::getModAndRef();
+
SmallVector<MemoryEffects::EffectInstance> effects;
interface.getEffects(effects);
- ModRefResult result = ModRefResult::getNoModRef();
for (const MemoryEffects::EffectInstance &effect : effects) {
// Check for an alias between the effect and our memory location.
@@ -563,22 +582,6 @@ ModRefResult AliasAnalysis::getModRef(mlir::Region ®ion,
mlir::Value location) {
ModRefResult result = ModRefResult::getNoModRef();
for (mlir::Operation &op : region.getOps()) {
- if (op.hasTrait<mlir::OpTrait::HasRecursiveMemoryEffects>()) {
- for (mlir::Region &subRegion : op.getRegions()) {
- result = result.merge(getModRef(subRegion, location));
- // Fast return is already mod and ref.
- if (result.isModAndRef())
- return result;
- }
- // In MLIR, RecursiveMemoryEffects can be combined with
- // MemoryEffectOpInterface to describe extra effects on top of the
- // effects of the nested operations. However, the presence of
- // RecursiveMemoryEffects and the absence of MemoryEffectOpInterface
- // implies the operation has no other memory effects than the one of its
- // nested operations.
- if (!mlir::isa<mlir::MemoryEffectOpInterface>(op))
- continue;
- }
result = result.merge(getModRef(&op, location));
if (result.isModAndRef())
return result;
@@ -674,6 +677,13 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
isCapturedInInternalProcedure |=
boxSrc.isCapturedInInternalProcedure;
+ if (getLastInstantiationPoint) {
+ if (!instantiationPoint)
+ instantiationPoint = boxSrc.origin.instantiationPoint;
+ } else {
+ instantiationPoint = boxSrc.origin.instantiationPoint;
+ }
+
global = llvm::dyn_cast<mlir::SymbolRefAttr>(boxSrc.origin.u);
if (global) {
type = SourceKind::Global;
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index c2a3d52fe88d2..713135374ba25 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -174,6 +174,34 @@ static void printAllocatableOp(mlir::OpAsmPrinter &p, OP &op) {
p.printOptionalAttrDict(op->getAttrs(), {"in_type", "operandSegmentSizes"});
}
+/// Returns true if the given box value may be absent.
+/// The given value must have BaseBoxType.
+static bool mayBeAbsentBox(mlir::Value val) {
+ assert(mlir::isa<fir::BaseBoxType>(val.getType()) && "expected box argument");
+ while (val) {
+ mlir::Operation *defOp = val.getDefiningOp();
+ if (!defOp)
+ return true;
+
+ if (auto varIface = mlir::dyn_cast<fir::FortranVariableOpInterface>(defOp))
+ return varIface.isOptional();
+
+ // Check for fir.embox and fir.rebox before checking for
+ // FortranObjectViewOpInterface, which they support.
+ // A box created by fir.embox/rebox cannot be absent.
+ if (mlir::isa<fir::ReboxOp, fir::EmboxOp, fir::LoadOp>(defOp))
+ return false;
+
+ if (auto viewIface =
+ mlir::dyn_cast<fir::FortranObjectViewOpInterface>(defOp)) {
+ val = viewIface.getViewSource(mlir::cast<mlir::OpResult>(val));
+ continue;
+ }
+ break;
+ }
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// AllocaOp
//===----------------------------------------------------------------------===//
@@ -1128,6 +1156,11 @@ std::optional<std::int64_t> fir::BoxAddrOp::getViewOffset(mlir::OpResult) {
return 0;
}
+mlir::Speculation::Speculatability fir::BoxAddrOp::getSpeculatability() {
+ return mayBeAbsentBox(getVal()) ? mlir::Speculation::NotSpeculatable
+ : mlir::Speculation::Speculatable;
+}
+
//===----------------------------------------------------------------------===//
// BoxCharLenOp
//===----------------------------------------------------------------------===//
@@ -1152,6 +1185,11 @@ mlir::Type fir::BoxDimsOp::getTupleType() {
return mlir::TupleType::get(getContext(), triple);
}
+mlir::Speculation::Speculatability fir::BoxDimsOp::getSpeculatability() {
+ return mayBeAbsentBox(getVal()) ? mlir::Speculation::NotSpeculatable
+ : mlir::Speculation::Speculatable;
+}
+
//===----------------------------------------------------------------------===//
// BoxRankOp
//===----------------------------------------------------------------------===//
@@ -1630,6 +1668,22 @@ llvm::LogicalResult fir::ConvertOp::verify() {
<< getValue().getType() << " / " << getType();
}
+mlir::Speculation::Speculatability fir::ConvertOp::getSpeculatability() {
+ // fir.convert is speculatable, in general. The only concern may be
+ // converting from or/and to floating point types, which may trigger
+ // some FP exceptions. Disallow speculating such converts for the time being.
+ // Also disallow speculation for converts to/from non-FIR types, except
+ // for some builtin types.
+ auto canSpeculateType = [](mlir::Type ty) {
+ if (fir::isa_fir_type(ty) || fir::isa_integer(ty))
+ return true;
+ return false;
+ };
+ return (canSpeculateType(getValue().getType()) && canSpeculateType(getType()))
+ ? mlir::Speculation::Speculatable
+ : mlir::Speculation::NotSpeculatable;
+}
+
//===----------------------------------------------------------------------===//
// CoordinateOp
//===----------------------------------------------------------------------===//
@@ -2121,6 +2175,12 @@ std::optional<std::int64_t> fir::EmboxOp::getViewOffset(mlir::OpResult) {
return std::nullopt;
}
+mlir::Speculation::Speculatability fir::EmboxOp::getSpeculatability() {
+ return (getSourceBox() && mayBeAbsentBox(getSourceBox()))
+ ? mlir::Speculation::NotSpeculatable
+ : mlir::Speculation::Speculatable;
+}
+
//===----------------------------------------------------------------------===//
// EmboxCharOp
//===----------------------------------------------------------------------===//
@@ -3417,6 +3477,11 @@ std::optional<std::int64_t> fir::ReboxOp::getViewOffset(mlir::OpResult) {
return std::nullopt;
}
+mlir::Speculation::Speculatability fir::ReboxOp::getSpeculatability() {
+ return mayBeAbsentBox(getBox()) ? mlir::Speculation::NotSpeculatable
+ : mlir::Speculation::Speculatable;
+}
+
//===----------------------------------------------------------------------===//
// ReboxAssumedRankOp
//===----------------------------------------------------------------------===//
diff --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp
index 103e736accca0..038ceb1d4bd75 100644
--- a/flang/lib/Optimizer/Passes/Pipelines.cpp
+++ b/flang/lib/Optimizer/Passes/Pipelines.cpp
@@ -206,6 +206,10 @@ void createDefaultFIROptimizerPassPipeline(mlir::PassManager &pm,
pm.addPass(fir::createSimplifyRegionLite());
pm.addPass(mlir::createCSEPass());
+ // Run LICM after CSE, which may reduce the number of operations to hoist.
+ if (pc.OptLevel.isOptimizingForSpeed())
+ pm.addPass(fir::createLoopInvariantCodeMotion());
+
// Polymorphic types
pm.addPass(fir::createPolymorphicOpConversion());
pm.addPass(fir::createAssumedRankOpConversion());
diff --git a/flang/lib/Optimizer/Transforms/CMakeLists.txt b/flang/lib/Optimizer/Transforms/CMakeLists.txt
index 619f3adc67c85..fad6f34f478ba 100644
--- a/flang/lib/Optimizer/Transforms/CMakeLists.txt
+++ b/flang/lib/Optimizer/Transforms/CMakeLists.txt
@@ -1,43 +1,44 @@
add_flang_library(FIRTransforms
AbstractResult.cpp
AddAliasTags.cpp
- AffinePromotion.cpp
+ AddDebugInfo.cpp
AffineDemotion.cpp
+ AffinePromotion.cpp
+ AlgebraicSimplification.cpp
AnnotateConstant.cpp
+ ArrayValueCopy.cpp
AssumedRankOpConversion.cpp
- CharacterConversion.cpp
- CompilerGeneratedNames.cpp
- ConstantArgumentGlobalisation.cpp
- ControlFlowConverter.cpp
CUDA/CUFAllocationConversion.cpp
CUFAddConstructor.cpp
+ CUFComputeSharedMemoryOffsetsAndSize.cpp
CUFDeviceGlobal.cpp
- CUFOpConversion.cpp
CUFGPUToLLVMConversion.cpp
- CUFComputeSharedMemoryOffsetsAndSize.cpp
- ArrayValueCopy.cpp
+ CUFOpConversion.cpp
+ CharacterConversion.cpp
+ CompilerGeneratedNames.cpp
+ ConstantArgumentGlobalisation.cpp
+ ControlFlowConverter.cpp
+ ConvertComplexPow.cpp
+ DebugTypeGenerator.cpp
ExternalNameConversion.cpp
FIRToSCF.cpp
- MemoryUtils.cpp
- MemoryAllocation.cpp
- StackArrays.cpp
+ FunctionAttr.cpp
+ GenRuntimeCallsForTest.cpp
+ LoopInvariantCodeMotion.cpp
+ LoopVersioning.cpp
+ MIFOpConversion.cpp
MemRefDataFlowOpt.cpp
- SimplifyRegionLite.cpp
- AlgebraicSimplification.cpp
- SimplifyIntrinsics.cpp
- AddDebugInfo.cpp
+ MemoryAllocation.cpp
+ MemoryUtils.cpp
+ OptimizeArrayRepacking.cpp
PolymorphicOpConversion.cpp
- LoopVersioning.cpp
- StackReclaim.cpp
- VScaleAttr.cpp
- FunctionAttr.cpp
- DebugTypeGenerator.cpp
SetRuntimeCallAttributes.cpp
- GenRuntimeCallsForTest.cpp
SimplifyFIROperations.cpp
- OptimizeArrayRepacking.cpp
- ConvertComplexPow.cpp
- MIFOpConversion.cpp
+ SimplifyIntrinsics.cpp
+ SimplifyRegionLite.cpp
+ StackArrays.cpp
+ StackReclaim.cpp
+ VScaleAttr.cpp
DEPENDS
CUFAttrs
@@ -63,12 +64,14 @@ add_flang_library(FIRTransforms
MLIR_LIBS
MLIRAffineUtils
+ MLIRAnalysis
MLIRFuncDialect
MLIRGPUDialect
- MLIRLLVMDialect
MLIRLLVMCommonConversion
+ MLIRLLVMDialect
MLIRMathTransforms
MLIROpenACCDialect
MLIROpenACCToLLVMIRTranslation
MLIROpenMPDialect
+ MLIRTransformUtils
)
diff --git a/flang/lib/Optimizer/Transforms/LoopInvariantCodeMotion.cpp b/flang/lib/Optimizer/Transforms/LoopInvariantCodeMotion.cpp
new file mode 100644
index 0000000000000..c033d5e278c8d
--- /dev/null
+++ b/flang/lib/Optimizer/Transforms/LoopInvariantCodeMotion.cpp
@@ -0,0 +1,233 @@
+//===- LoopInvariantCodeMotion.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
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// FIR-specific Loop Invariant Code Motion pass.
+/// The pass relies on FIR types and interfaces to prove the safety
+//...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/174013
More information about the flang-commits
mailing list