Skip to content

Commit cdd5b16

Browse files
committed
[flang][hlfir] Expand array hlfir.assign's.
Expand hlfir.assign with in-memory array RHS and LHS into a loop nest with element-by-element assignments. For small arrays this may result in further loop nest unrolling enabling more value propagation and redundancy elimination. Note the change in flang/test/HLFIR/opt-bufferization.fir: the hlfir.assign inside hlfir.elemental gets expanded by the new pattern. Depends on D159151 Reviewed By: tblah Differential Revision: https://reviews.llvm.org/D159246
1 parent e60dc8e commit cdd5b16

File tree

3 files changed

+406
-1
lines changed

3 files changed

+406
-1
lines changed

flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,102 @@ mlir::LogicalResult BroadcastAssignBufferization::matchAndRewrite(
417417
return mlir::success();
418418
}
419419

420+
/// Expand hlfir.assign of array RHS to array LHS into a loop nest
421+
/// of element-by-element assignments:
422+
/// hlfir.assign %4 to %5 : !fir.ref<!fir.array<3x3xf32>>,
423+
/// !fir.ref<!fir.array<3x3xf32>>
424+
/// into:
425+
/// fir.do_loop %arg1 = %c1 to %c3 step %c1 unordered {
426+
/// fir.do_loop %arg2 = %c1 to %c3 step %c1 unordered {
427+
/// %6 = hlfir.designate %4 (%arg2, %arg1) :
428+
/// (!fir.ref<!fir.array<3x3xf32>>, index, index) -> !fir.ref<f32>
429+
/// %7 = fir.load %6 : !fir.ref<f32>
430+
/// %8 = hlfir.designate %5 (%arg2, %arg1) :
431+
/// (!fir.ref<!fir.array<3x3xf32>>, index, index) -> !fir.ref<f32>
432+
/// hlfir.assign %7 to %8 : f32, !fir.ref<f32>
433+
/// }
434+
/// }
435+
///
436+
/// The transformation is correct only when LHS and RHS do not alias.
437+
/// This transformation does not support runtime checking for
438+
/// non-conforming LHS/RHS arrays' shapes currently.
439+
class VariableAssignBufferization
440+
: public mlir::OpRewritePattern<hlfir::AssignOp> {
441+
private:
442+
public:
443+
using mlir::OpRewritePattern<hlfir::AssignOp>::OpRewritePattern;
444+
445+
mlir::LogicalResult
446+
matchAndRewrite(hlfir::AssignOp assign,
447+
mlir::PatternRewriter &rewriter) const override;
448+
};
449+
450+
mlir::LogicalResult VariableAssignBufferization::matchAndRewrite(
451+
hlfir::AssignOp assign, mlir::PatternRewriter &rewriter) const {
452+
if (assign.isAllocatableAssignment())
453+
return rewriter.notifyMatchFailure(assign, "AssignOp may imply allocation");
454+
455+
hlfir::Entity rhs{assign.getRhs()};
456+
// TODO: ExprType check is here to avoid conflicts with
457+
// ElementalAssignBufferization pattern. We need to combine
458+
// these matchers into a single one that applies to AssignOp.
459+
if (rhs.getType().isa<hlfir::ExprType>())
460+
return rewriter.notifyMatchFailure(assign, "RHS is not in memory");
461+
462+
if (!rhs.isArray())
463+
return rewriter.notifyMatchFailure(assign,
464+
"AssignOp's RHS is not an array");
465+
466+
mlir::Type rhsEleTy = rhs.getFortranElementType();
467+
if (!fir::isa_trivial(rhsEleTy))
468+
return rewriter.notifyMatchFailure(
469+
assign, "AssignOp's RHS data type is not trivial");
470+
471+
hlfir::Entity lhs{assign.getLhs()};
472+
if (!lhs.isArray())
473+
return rewriter.notifyMatchFailure(assign,
474+
"AssignOp's LHS is not an array");
475+
476+
mlir::Type lhsEleTy = lhs.getFortranElementType();
477+
if (!fir::isa_trivial(lhsEleTy))
478+
return rewriter.notifyMatchFailure(
479+
assign, "AssignOp's LHS data type is not trivial");
480+
481+
if (lhsEleTy != rhsEleTy)
482+
return rewriter.notifyMatchFailure(assign,
483+
"RHS/LHS element types mismatch");
484+
485+
fir::AliasAnalysis aliasAnalysis;
486+
mlir::AliasResult aliasRes = aliasAnalysis.alias(lhs, rhs);
487+
if (!aliasRes.isNo()) {
488+
LLVM_DEBUG(llvm::dbgs() << "VariableAssignBufferization:\n"
489+
<< "\tLHS: " << lhs << "\n"
490+
<< "\tRHS: " << rhs << "\n"
491+
<< "\tALIAS: " << aliasRes << "\n");
492+
return rewriter.notifyMatchFailure(assign, "RHS/LHS may alias");
493+
}
494+
495+
mlir::Location loc = assign->getLoc();
496+
fir::FirOpBuilder builder(rewriter, assign.getOperation());
497+
builder.setInsertionPoint(assign);
498+
rhs = hlfir::derefPointersAndAllocatables(loc, builder, rhs);
499+
lhs = hlfir::derefPointersAndAllocatables(loc, builder, lhs);
500+
mlir::Value shape = hlfir::genShape(loc, builder, lhs);
501+
llvm::SmallVector<mlir::Value> extents =
502+
hlfir::getIndexExtents(loc, builder, shape);
503+
hlfir::LoopNest loopNest =
504+
hlfir::genLoopNest(loc, builder, extents, /*isUnordered=*/true);
505+
builder.setInsertionPointToStart(loopNest.innerLoop.getBody());
506+
auto rhsArrayElement =
507+
hlfir::getElementAt(loc, builder, rhs, loopNest.oneBasedIndices);
508+
rhsArrayElement = hlfir::loadTrivialScalar(loc, builder, rhsArrayElement);
509+
auto lhsArrayElement =
510+
hlfir::getElementAt(loc, builder, lhs, loopNest.oneBasedIndices);
511+
builder.create<hlfir::AssignOp>(loc, rhsArrayElement, lhsArrayElement);
512+
rewriter.eraseOp(assign);
513+
return mlir::success();
514+
}
515+
420516
class OptimizedBufferizationPass
421517
: public hlfir::impl::OptimizedBufferizationBase<
422518
OptimizedBufferizationPass> {
@@ -438,6 +534,7 @@ class OptimizedBufferizationPass
438534
// This requires small code reordering in ElementalAssignBufferization.
439535
patterns.insert<ElementalAssignBufferization>(context);
440536
patterns.insert<BroadcastAssignBufferization>(context);
537+
patterns.insert<VariableAssignBufferization>(context);
441538

442539
if (mlir::failed(mlir::applyPatternsAndFoldGreedily(
443540
func, std::move(patterns), config))) {

flang/test/HLFIR/opt-bufferization.fir

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ func.func @write(%arg: !fir.ref<!fir.array<42xi32>>, %arg1: !fir.ref<!fir.array<
251251
// CHECK: %[[VAL_5B:.*]]:2 = hlfir.declare %[[ARG_1]](%[[VAL_4]]) {uniq_name = "array2"} : (!fir.ref<!fir.array<42xi32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<42xi32>>, !fir.ref<!fir.array<42xi32>>)
252252
// CHECK: %[[VAL_6:.*]] = hlfir.elemental %[[VAL_4]] unordered : (!fir.shape<1>) -> !hlfir.expr<42xi32> {
253253
// CHECK: ^bb0(%[[VAL_7:.*]]: index):
254-
// CHECK: hlfir.assign %[[VAL_5B]]#0 to %[[VAL_5]]#0 : !fir.ref<!fir.array<42xi32>>, !fir.ref<!fir.array<42xi32>>
254+
// CHECK: hlfir.assign
255255
// CHECK: %[[VAL_8:.*]] = hlfir.designate %[[VAL_5]]#0 (%[[VAL_7]]) : (!fir.ref<!fir.array<42xi32>>, index) -> !fir.ref<i32>
256256
// CHECK: %[[VAL_9:.*]] = fir.load %[[VAL_8]] : !fir.ref<i32>
257257
// CHECK: %[[VAL_10:.*]] = arith.subi %[[VAL_9]], %[[VAL_1]] : i32

0 commit comments

Comments
 (0)