[flang-commits] [flang] [flang] Make fir.result Pure operation. (PR #173508)
via flang-commits
flang-commits at lists.llvm.org
Wed Dec 24 11:59:47 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-fir-hlfir
Author: Slava Zakharin (vzakhari)
<details>
<summary>Changes</summary>
This allows speculating recursively speculatable operations
containing `fir.result`. Note that making it Pure does not allow
speculating `fir.result` itself from its containing operation,
since it is a terminator.
---
Patch is 205.21 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/173508.diff
17 Files Affected:
- (modified) flang/include/flang/Optimizer/Dialect/FIROps.td (+3-3)
- (modified) flang/include/flang/Optimizer/Transforms/Passes.td (+11)
- (modified) flang/lib/Optimizer/Analysis/AliasAnalysis.cpp (+34-24)
- (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/Fir/basic-program.fir (+3)
- (modified) flang/test/Lower/array-expression-assumed-size.f90 (+164-159)
- (modified) flang/test/Lower/array-temp.f90 (+262-250)
- (modified) flang/test/Lower/vector-subscript-io.f90 (+5-5)
- (added) flang/test/Transforms/licm.fir (+1406)
``````````diff
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index 7bfb304b973f5..a3b8fb79e1b33 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -2252,9 +2252,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 = [{
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/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
+/// of hoisting invariant operations out of loop-like operations.
+/// It may be run on both HLFIR and FIR representations.
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/Analysis/AliasAnalysis.h"
+#include "flang/Optimizer/Dialect/FortranVariableInterface.h"
+#include "flang/Optimizer/Transforms/Passes.h"
+#include "mlir/Pass/Pass.h"
+#include "mlir/Transforms/LoopInvariantCodeMotionUtils.h"
+#include "llvm/Support/DebugLog.h"
+
+namespace fir {
+#define GEN_PASS_DEF_LOOPINVARIANTCODEMOTION
+#include "flang/Optimizer/Transforms/Passes.h.inc"
+} // namespace fir
+
+#define DEBUG_TYPE "flang-licm"
+
+// Temporary engineering option for triaging LICM.
+static llvm::cl::opt<bool> disableFlangLICM(
+ "disable-flang-licm", llvm::cl::init(false), llvm::cl::Hidden,
+ llvm::cl::desc("Disable Flang's loop invariant code motion"));
+
+namespace {
+
+using namespace mlir;
+
+/// The pass tries to hoist loop invariant operations with only
+/// MemoryEffects::Read effects (MemoryEffects::Write support
+/// may be added later).
+/// The safety of hoisting is proven by:
+/// * Proving that the loop runs at least one iteration.
+/// * Proving that is is always safe to load from this location
+/// (see isSafeToHoistLoad() comments below).
+struct LoopInvariantCodeMotion
+ : fir::impl::LoopInvariantCodeMotionBase<LoopInvariantCodeMotion> {
+ void runOnOperation() override;
+};
+
+} // namespace
+
+/// 'location' is a memory reference used by a memory access.
+/// The type of 'location' defines the data type of the access
+/// (e.g. it is considered to be invalid to access 'i64'
+/// data using '!fir.ref<i32>`).
+/// For the given location, this function returns true iff
+/// the Fortran object being accessed is a scalar that
+/// may not be OPTIONAL.
+///
+/// Note that the '!fir.ref<!fir.box<>>' accesses are considered
+/// to be scalar, even if the underlying data is an array.
+///
+/// Note that an access of '!fir.ref<scalar>' may access
+/// an array object. For example:
+/// real :: x(:)
+/// do i=...
+/// = x(10)
+/// 'x(10)' accesses array 'x', and it may be unsafe to hoist
+/// it without proving that '10' is a valid index for the array.
+/// The fact that 'x' is not OPTIONAL does not allow hoisting
+/// on its own.
+static bool isNonOptionalScalar(Value location) {
+ while (true) {
+ LDBG() << "Checking location:\n" << location;
+ Type dataType = fir::unwrapRefType(location.getType());
+ if (!isa<fir::BaseBoxType>(location.getType()) &&
+ (!dataType ||
+ (!isa<fir::BaseBoxType>(dataType) && !fir::isa_trivial(dataType) &&
+ !fir::isa_derived(dataType)))) {
+ LDBG() << "Failure: data access is not scalar";
+ return false;
+ }
+ Operation *defOp = location.getDefiningOp();
+ if (!defOp) {
+ LDBG() << "Failure: no defining operation";
+ return false;
+ }
+ if (auto varIface = dyn_cast<fir::FortranVariableOpInterface>(defOp)) {
+ bool result = !varIface.isOptional();
+ if (result)
+ LDBG() << "Success: is non optional scalar";
+ else
+ LDBG() << "Failure: is not non optional scalar";
+ return result;
+ }
+ if (auto viewIface = dyn_cast<fir::FortranObjectViewOpInterface>(defOp)) {
+ location = viewIface.getViewSource(cast<OpResult>(location));
+ } else {
+ LDBG() << "Failure: unknown operation:\n" << *defOp;
+ return false;
+ }
+ }
+}
+
+/// Returns true iff it is safe to hoist the given load-like operation 'op',
+/// which access given memory 'locations', out of the operation 'loopLike'.
+/// The current safety conditions are:
+/// * The loop runs at least one iteration, OR
+/// * all the accessed locations are inside scalar non-OPTIONAL
+/// Fortran objects (Fortran descriptors are considered to be scalars).
+static bool isSafeToHoistLoad(Operation *op, ArrayRef<Value> locations,
+ LoopLikeOpInterface loopLike,
+ AliasAnalysis &aliasAnalysis) {
+ for (Value location : locations)
+ if (aliasAnalysis.getModRef(loopLike.getOperation(), location)
+ .isModAndRef()) {
+ LDBG() << "Failure: reads location:\n"
+ << location << "\nwhich is modified inside the loop";
+ return false;
+ }
+
+ // Check that it is safe to read from all the locations before the loop.
+ std::optional<llvm::APInt> tripCount = loopLike.getStaticTripCount();
+ if (tripCount && !tripCount->isZero()) {
+ // Loop executes at least one iteration, so it is safe to hoist.
+ LDBG() << "Success: loop has non-zero iterations";
+ return true;
+ }
+
+ // Check whether the access must always be valid.
+ return llvm::all_of(
+ locations, [&](Value location) { return isNonOptionalScalar(location); });
+ // TODO: consider hoisting under condition of the loop's trip count
+ // being non-zero.
+}
+
+/// Returns true iff the given 'op' is a load-like operation,
+/// and it can be hoisted out of 'loopLike' operation.
+static bool canHoistLoad(Operation *op, LoopLikeOpInterface loopLike,
+ AliasAnalysis &aliasAnalysis) {
+ LDBG() << "Checking operation:\n" << *op;
+ if (auto effectInterface = dyn_cast<MemoryEffectOpInterface>(op)) {
+ SmallVector<MemoryEffects::EffectInstance> effects;
+ effectInterface.getEffects(effects);
+ if (effects.empty()) {
+ LDBG() << "Failure: not a load";
+ return false;
+ }
+ llvm::SetVector<Value> locations;
+ for (const MemoryEffects::EffectInstance &effect : effects) {
+ Value location = effect.getValue();
+ if (!isa<MemoryEffects::Read>(effect.getEffect())) {
+ LDBG() << "Failure: has unsupported effects";
+ return false;
+ } else if (!location) {
+ LDBG() << "Failure: reads from unknown location";
+ return false;
+ }
+ locations.insert(location);
+ }
+ return isSafeToHoistLoad(op, locations.getArrayRef(), loopLike,
+ aliasAnalysis);
+ }
+ LDBG() << "Failure: has unknown effects";
+ return false;
+}
+
+void LoopInvariantCodeMotion::runOnOperation() {
+ if (disableFlangLICM) {
+ LDBG() << "Skipping [HL]FIR LoopInvariantCodeMotion()";
+ return;
+ }
+
+ LDBG() << "Enter [HL]FIR LoopInvariantCodeMotion()";
+
+ auto &aliasAnalysis = getAnalysis<AliasAnalysis>();
+ aliasAnalysis.addAnalysisImplementation(fir::AliasAnalysis{});
+
+ std::function<bool(Operation *, LoopLikeOpInterface loopLike)>
+ shouldMoveOutOfLoop = [&](Operation *op, LoopLikeOpInterface loopLike) {
+ if (isPure(op)) {
+ LDBG() << "Pure operation: " << *op;
+ return true;
+ }
+
+ // Handle RecursivelySpeculatable operations that have
+ // RecursiveMemoryEffects by checking if all their
+ // nested operations can be hoisted.
+ auto iface = dyn_cast<ConditionallySpeculatable>(op);
+ if (iface && iface.getSpeculatability() ==
+ Speculation::RecursivelySpeculatable) {
+ if (op->hasTrait<OpTrait::HasRecursiveMemoryEffects>()) {
+ LDBG() << "Checking recursive operation:\n" << *op;
+ llvm::SmallVector<Operation *> nestedOps;
+ for (Region ®ion : op->getRegions())
+ for (Block &block : region)
+ for (Operation &nestedOp : block)
+ nestedOps.push_back(&nestedOp);
+
+ bool result = llvm::all_of(nestedOps, [&](Operation *nestedOp) {
+ return shouldMoveOutOfLoop(nestedOp, loopLike);
+ });
+ LDBG() << "Recursive operation can" << (result ? "" : "not")
+ << " be hoisted";
+
+ // If nested operations cannot be hoisted, there is nothing
+ // else to check. Also if the operation itself does not have
+ // any memory effects, we can return the result now.
+ // Otherwise, we have to check the operation itself below.
+ if (!result || !isa<MemoryEffectOpInterface>(op))
+ return result;
+ }
+ }
+ return canHoistLoad(op, loopLike, aliasAnalysis);
+ };
+
+ getOperation()->walk([&](LoopLikeOpInterface loopLike) {
+ moveLoopInvariantCode(
+ loopLike.getLoopRegions(),
+ /*isDefinedOutsideRegion=*/
+ [&](Value value, Region *) {
+ return loopLike.isDefinedOutsideOfLoop(value);
+ },
+ /*shouldMoveOutOfRegion=*/
+ [&](Operation *op, Region *) {
+ return shouldMoveOutOfLoop(op, loopLike);
+ },
+ /*moveOutOfRegion=*/
+ [&](Operation *op, Region *) { loopLike.moveOutOfLoop(op); });
+ });
+
+ LDBG() << "Exit [HL]FIR LoopInvariantCodeMotion()";
+}
diff --git a/flang/test/Analysis/AliasAnalysis/gen_mod_ref_test.py b/flang/test/Analysis/AliasAnalysis/gen_mod_ref_test.py
index ce7d9b1700bf7..6c73cab0d5f2d 100755
--- a/flang/test/Analysis/AliasAnalysis/gen_mod_ref_test.py
+++ b/flang/test/Analysis/AliasAnalysis/gen_mod_ref_test.py
@@ -7,11 +7,14 @@
This will insert mod ref test hook:
- to any fir.call to a function which name starts with "test_effect_"
- to any hlfir.declare for variable which name starts with "test_var_"
+ - to any fir.box_addr - they are assigned box_addr_# hooks
"""
import sys
import re
+box_addr_counter = 0
+
for line in sys.stdin:
line = re.sub(
r"(fir.call @_\w*P)(test_effect_\w*)(\(.*) : ",
@@ -23,4 +26,11 @@
r'\1\2", test.ptr ="\2"',
line,
)
+ line, count = re.subn(
+ r"(fir.box_addr.*) :",
+ rf'\1 {{test.ptr ="box_addr_{box_addr_counter}"}} :',
+ line,
+ )
+ if count > 0:
+ box_addr_counter += 1
sys.stdout.write(line)
diff --git a/flang/test/Analysis/AliasAnaly...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/173508
More information about the flang-commits
mailing list