Skip to content

Commit 4a63ff4

Browse files
authored
Revert "[mlir] Enable LICM for ops with only read side effects in scf.for" (llvm#126840)
Reverts llvm#120302
1 parent cc7e836 commit 4a63ff4

File tree

9 files changed

+17
-293
lines changed

9 files changed

+17
-293
lines changed

mlir/include/mlir/Dialect/SCF/IR/SCFOps.td

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def SCF_Dialect : Dialect {
4040
and then lowered to some final target like LLVM or SPIR-V.
4141
}];
4242

43-
let dependentDialects = ["arith::ArithDialect", "ub::UBDialect"];
43+
let dependentDialects = ["arith::ArithDialect"];
4444
}
4545

4646
// Base class for SCF dialect ops.
@@ -138,9 +138,7 @@ def ForOp : SCF_Op<"for",
138138
["getInitsMutable", "getLoopResults", "getRegionIterArgs",
139139
"getLoopInductionVars", "getLoopLowerBounds", "getLoopSteps",
140140
"getLoopUpperBounds", "getYieldedValuesMutable",
141-
"moveOutOfLoopWithGuard",
142141
"promoteIfSingleIteration", "replaceWithAdditionalYields",
143-
"wrapInTripCountCheck", "unwrapTripCountCheck",
144142
"yieldTiledValuesAndReplace"]>,
145143
AllTypesMatch<["lowerBound", "upperBound", "step"]>,
146144
ConditionallySpeculatable,

mlir/include/mlir/Interfaces/LoopLikeInterface.td

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,6 @@ def LoopLikeOpInterface : OpInterface<"LoopLikeOpInterface"> {
7979
/*methodBody=*/"",
8080
/*defaultImplementation=*/"op->moveBefore($_op);"
8181
>,
82-
InterfaceMethod<[{
83-
Moves the given loop-invariant operation out of the loop with a
84-
trip-count guard.
85-
}],
86-
/*retTy=*/"void",
87-
/*methodName=*/"moveOutOfLoopWithGuard",
88-
/*args=*/(ins "::mlir::Operation *":$op),
89-
/*methodBody=*/"",
90-
/*defaultImplementation=*/[{
91-
return;
92-
}]
93-
>,
9482
InterfaceMethod<[{
9583
Promotes the loop body to its containing block if the loop is known to
9684
have a single iteration. Returns "success" if the promotion was

mlir/include/mlir/Interfaces/SideEffectInterfaces.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -433,10 +433,6 @@ bool wouldOpBeTriviallyDead(Operation *op);
433433
/// conditions are satisfied.
434434
bool isMemoryEffectFree(Operation *op);
435435

436-
/// Returns true if the given operation is free of memory effects or has only
437-
/// read effect.
438-
bool isMemoryEffectFreeOrOnlyRead(Operation *op);
439-
440436
/// Returns the side effects of an operation. If the operation has
441437
/// RecursiveMemoryEffects, include all side effects of child operations.
442438
///

mlir/include/mlir/Transforms/LoopInvariantCodeMotionUtils.h

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,16 @@ class Value;
4747
/// }
4848
/// }
4949
/// ```
50-
/// Users must supply four callbacks.
50+
///
51+
/// Users must supply three callbacks.
5152
///
5253
/// - `isDefinedOutsideRegion` returns true if the given value is invariant with
5354
/// respect to the given region. A common implementation might be:
5455
/// `value.getParentRegion()->isProperAncestor(region)`.
5556
/// - `shouldMoveOutOfRegion` returns true if the provided operation can be
56-
/// moved of the given region, e.g. if it is side-effect free or has only read
57-
/// side effects.
58-
/// - `moveOutOfRegionWithoutGuard` moves the operation out of the given region
59-
/// without a guard. A common implementation might be:
60-
/// `op->moveBefore(region->getParentOp())`.
61-
/// - `moveOutOfRegionWithGuard` moves the operation out of the given region
62-
/// with a guard.
57+
/// moved of the given region, e.g. if it is side-effect free.
58+
/// - `moveOutOfRegion` moves the operation out of the given region. A common
59+
/// implementation might be: `op->moveBefore(region->getParentOp())`.
6360
///
6461
/// An operation is moved if all of its operands satisfy
6562
/// `isDefinedOutsideRegion` and it satisfies `shouldMoveOutOfRegion`.
@@ -69,8 +66,7 @@ size_t moveLoopInvariantCode(
6966
ArrayRef<Region *> regions,
7067
function_ref<bool(Value, Region *)> isDefinedOutsideRegion,
7168
function_ref<bool(Operation *, Region *)> shouldMoveOutOfRegion,
72-
function_ref<void(Operation *)> moveOutOfRegionWithoutGuard,
73-
function_ref<void(Operation *)> moveOutOfRegionWithGuard);
69+
function_ref<void(Operation *, Region *)> moveOutOfRegion);
7470

7571
/// Move side-effect free loop invariant code out of a loop-like op using
7672
/// methods provided by the interface.

mlir/lib/Dialect/SCF/IR/SCF.cpp

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
#include "mlir/Dialect/MemRef/IR/MemRef.h"
1616
#include "mlir/Dialect/SCF/IR/DeviceMappingInterface.h"
1717
#include "mlir/Dialect/Tensor/IR/Tensor.h"
18-
#include "mlir/Dialect/UB/IR/UBOps.h"
1918
#include "mlir/IR/BuiltinAttributes.h"
2019
#include "mlir/IR/IRMapping.h"
2120
#include "mlir/IR/Matchers.h"
@@ -396,40 +395,6 @@ std::optional<SmallVector<OpFoldResult>> ForOp::getLoopUpperBounds() {
396395

397396
std::optional<ResultRange> ForOp::getLoopResults() { return getResults(); }
398397

399-
/// Moves the op out of the loop with a guard that checks if the loop has at
400-
/// least one iteration.
401-
void ForOp::moveOutOfLoopWithGuard(Operation *op) {
402-
IRRewriter rewriter(this->getContext());
403-
OpBuilder::InsertionGuard insertGuard(rewriter);
404-
rewriter.setInsertionPoint(this->getOperation());
405-
Location loc = this->getLoc();
406-
arith::CmpIOp cmpIOp = rewriter.create<arith::CmpIOp>(
407-
loc, arith::CmpIPredicate::ult, this->getLowerBound(),
408-
this->getUpperBound());
409-
// Create the trip-count check.
410-
scf::YieldOp thenYield;
411-
scf::IfOp ifOp = rewriter.create<scf::IfOp>(
412-
loc, cmpIOp,
413-
[&](OpBuilder &builder, Location loc) {
414-
thenYield = builder.create<scf::YieldOp>(loc, op->getResults());
415-
},
416-
[&](OpBuilder &builder, Location loc) {
417-
SmallVector<Value> poisonResults;
418-
poisonResults.reserve(op->getResults().size());
419-
for (Type type : op->getResults().getTypes()) {
420-
ub::PoisonOp poisonOp =
421-
rewriter.create<ub::PoisonOp>(loc, type, nullptr);
422-
poisonResults.push_back(poisonOp);
423-
}
424-
builder.create<scf::YieldOp>(loc, poisonResults);
425-
});
426-
for (auto [opResult, ifOpResult] :
427-
llvm::zip_equal(op->getResults(), ifOp->getResults()))
428-
rewriter.replaceAllUsesExcept(opResult, ifOpResult, thenYield);
429-
// Move the op into the then block.
430-
rewriter.moveOpBefore(op, thenYield);
431-
}
432-
433398
/// Promotes the loop body of a forOp to its containing block if the forOp
434399
/// it can be determined that the loop has a single iteration.
435400
LogicalResult ForOp::promoteIfSingleIteration(RewriterBase &rewriter) {
@@ -3429,8 +3394,9 @@ ParseResult scf::WhileOp::parse(OpAsmParser &parser, OperationState &result) {
34293394

34303395
if (functionType.getNumInputs() != operands.size()) {
34313396
return parser.emitError(typeLoc)
3432-
<< "expected as many input types as operands " << "(expected "
3433-
<< operands.size() << " got " << functionType.getNumInputs() << ")";
3397+
<< "expected as many input types as operands "
3398+
<< "(expected " << operands.size() << " got "
3399+
<< functionType.getNumInputs() << ")";
34343400
}
34353401

34363402
// Resolve input operands.

mlir/lib/Interfaces/SideEffectInterfaces.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
#include "mlir/Interfaces/SideEffectInterfaces.h"
1010

1111
#include "mlir/IR/SymbolTable.h"
12-
#include "llvm/ADT/STLExtras.h"
1312
#include "llvm/ADT/SmallPtrSet.h"
1413
#include <utility>
1514

@@ -371,17 +370,6 @@ mlir::getEffectsRecursively(Operation *rootOp) {
371370
return effects;
372371
}
373372

374-
bool mlir::isMemoryEffectFreeOrOnlyRead(Operation *op) {
375-
std::optional<SmallVector<MemoryEffects::EffectInstance>> effects =
376-
getEffectsRecursively(op);
377-
if (!effects)
378-
return false;
379-
return llvm::all_of(*effects,
380-
[&](const MemoryEffects::EffectInstance &effect) {
381-
return isa<MemoryEffects::Read>(effect.getEffect());
382-
});
383-
}
384-
385373
bool mlir::isSpeculatable(Operation *op) {
386374
auto conditionallySpeculatable = dyn_cast<ConditionallySpeculatable>(op);
387375
if (!conditionallySpeculatable)

mlir/lib/Transforms/Utils/LoopInvariantCodeMotionUtils.cpp

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,13 @@ size_t mlir::moveLoopInvariantCode(
6060
ArrayRef<Region *> regions,
6161
function_ref<bool(Value, Region *)> isDefinedOutsideRegion,
6262
function_ref<bool(Operation *, Region *)> shouldMoveOutOfRegion,
63-
function_ref<void(Operation *)> moveOutOfRegionWithoutGuard,
64-
function_ref<void(Operation *)> moveOutOfRegionWithGuard) {
63+
function_ref<void(Operation *, Region *)> moveOutOfRegion) {
6564
size_t numMoved = 0;
6665

6766
for (Region *region : regions) {
6867
LLVM_DEBUG(llvm::dbgs() << "Original loop:\n"
6968
<< *region->getParentOp() << "\n");
7069

71-
bool anyOpHoistedWithGuard = false;
72-
bool loopSideEffectFreeOrHasOnlyReadSideEffect =
73-
isMemoryEffectFreeOrOnlyRead(region->getParentOp());
74-
7570
std::queue<Operation *> worklist;
7671
// Add top-level operations in the loop body to the worklist.
7772
for (Operation &op : region->getOps())
@@ -89,26 +84,12 @@ size_t mlir::moveLoopInvariantCode(
8984
continue;
9085

9186
LLVM_DEBUG(llvm::dbgs() << "Checking op: " << *op << "\n");
92-
9387
if (!shouldMoveOutOfRegion(op, region) ||
9488
!canBeHoisted(op, definedOutside))
9589
continue;
96-
// Can only hoist pure ops (side-effect free) when there is an op with
97-
// write and/or unknown side effects in the loop.
98-
if (!loopSideEffectFreeOrHasOnlyReadSideEffect && !isMemoryEffectFree(op))
99-
continue;
10090

101-
bool moveWithoutGuard = !anyOpHoistedWithGuard && isMemoryEffectFree(op);
102-
if (moveWithoutGuard) {
103-
LLVM_DEBUG(llvm::dbgs() << "Moving loop-invariant op: " << *op
104-
<< " without guard\n");
105-
moveOutOfRegionWithoutGuard(op);
106-
} else {
107-
LLVM_DEBUG(llvm::dbgs()
108-
<< "Moving loop-invariant op: " << *op << " with guard\n");
109-
moveOutOfRegionWithGuard(op);
110-
anyOpHoistedWithGuard = true;
111-
}
91+
LLVM_DEBUG(llvm::dbgs() << "Moving loop-invariant op: " << *op << "\n");
92+
moveOutOfRegion(op, region);
11293
++numMoved;
11394

11495
// Since the op has been moved, we need to check its users within the
@@ -125,14 +106,13 @@ size_t mlir::moveLoopInvariantCode(
125106
size_t mlir::moveLoopInvariantCode(LoopLikeOpInterface loopLike) {
126107
return moveLoopInvariantCode(
127108
loopLike.getLoopRegions(),
128-
[&](Value value, Region *region) {
129-
return !region->isAncestor(value.getParentRegion());
109+
[&](Value value, Region *) {
110+
return loopLike.isDefinedOutsideOfLoop(value);
130111
},
131112
[&](Operation *op, Region *) {
132-
return isSpeculatable(op) && isMemoryEffectFreeOrOnlyRead(op);
113+
return isMemoryEffectFree(op) && isSpeculatable(op);
133114
},
134-
[&](Operation *op) { loopLike.moveOutOfLoop(op); },
135-
[&](Operation *op) { loopLike.moveOutOfLoopWithGuard(op); });
115+
[&](Operation *op, Region *) { loopLike.moveOutOfLoop(op); });
136116
}
137117

138118
namespace {

mlir/test/Transforms/loop-invariant-code-motion.mlir

Lines changed: 0 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -714,150 +714,6 @@ func.func @test_recursively_speculatable_op_failure(%lb: index, %ub: index, %ste
714714
return
715715
}
716716

717-
// CHECK-LABEL: test_speculatable_op_with_read_side_effect_success
718-
func.func @test_speculatable_op_with_read_side_effect_success(%lb: index, %ub: index, %step: index) -> i32 {
719-
// CHECK: test.always_speculatable_op
720-
// CHECK-NEXT: %[[CMP:.*]] = arith.cmpi ult, %[[LB:.*]], %[[UB:.*]] : index
721-
// CHECK-NEXT: scf.if %[[CMP]]
722-
// CHECK-NEXT: test.speculatable_op_with_memread
723-
// CHECK: else
724-
// CHECK-NEXT: ub.poison : i32
725-
// CHECK: scf.for %[[_:.*]] = %[[LB]] to %[[UB]]
726-
// CHECK-NOT: test.always_speculatable_op
727-
// CHECK-NOT: test.speculatable_op_with_memread
728-
%cst_0 = arith.constant 0 : i32
729-
%cst_42 = arith.constant dense<42> : tensor<64xi32>
730-
%ind_42 = arith.constant 42 : index
731-
%sum_result = scf.for %i = %lb to %ub step %step iter_args(%acc = %cst_0) -> i32 {
732-
%always_speculate = "test.always_speculatable_op"() : () -> i32
733-
%only_read = "test.speculatable_op_with_memread"(%cst_42, %ind_42) : (tensor<64xi32>, index) -> i32
734-
%i_cast = arith.index_cast %i: index to i32
735-
%add = arith.addi %acc, %i_cast : i32
736-
%sum = arith.addi %add, %only_read : i32
737-
scf.yield %sum : i32
738-
}
739-
return %sum_result : i32
740-
}
741-
742-
// CHECK-LABEL: test_speculatable_op_with_read_side_effect_multiple_result_success
743-
func.func @test_speculatable_op_with_read_side_effect_multiple_result_success(%lb: index, %ub: index, %step: index) -> i32 {
744-
// CHECK: test.always_speculatable_op
745-
// CHECK-NEXT: %[[CMP:.*]] = arith.cmpi ult, %[[LB:.*]], %[[UB:.*]] : index
746-
// CHECK-NEXT: scf.if %[[CMP]]
747-
// CHECK-NEXT: test.speculatable_op_with_memread
748-
// CHECK: else
749-
// CHECK-NEXT: ub.poison : i32
750-
// CHECK-NEXT: ub.poison : f32
751-
// CHECK: scf.for %[[_:.*]] = %[[LB]] to %[[UB]]
752-
// CHECK-NOT: test.always_speculatable_op
753-
// CHECK-NOT: test.speculatable_op_with_memread
754-
%cst_0 = arith.constant 0 : i32
755-
%cst_42 = arith.constant dense<42> : tensor<64xi32>
756-
%ind_42 = arith.constant 42 : index
757-
%sum_result = scf.for %i = %lb to %ub step %step iter_args(%acc = %cst_0) -> i32 {
758-
%always_speculate = "test.always_speculatable_op"() : () -> i32
759-
%only_read:2 = "test.speculatable_op_with_memread"(%cst_42, %ind_42) : (tensor<64xi32>, index) -> (i32, f32)
760-
%i_cast = arith.index_cast %i: index to i32
761-
%add = arith.addi %acc, %i_cast : i32
762-
%sum = arith.addi %add, %only_read#0 : i32
763-
scf.yield %sum : i32
764-
}
765-
return %sum_result : i32
766-
}
767-
768-
// CHECK-LABEL: test_speculatable_op_with_read_side_effect_success_with_dependents
769-
func.func @test_speculatable_op_with_read_side_effect_success_with_dependents(%lb: index, %ub: index, %step: index) -> i32 {
770-
// CHECK: %[[ALWAYS:.*]] = "test.always_speculatable_op"
771-
// CHECK-NEXT: %[[CMP0:.*]] = arith.cmpi ult, %[[LB:.*]], %[[UB:.*]] : index
772-
// CHECK-NEXT: %[[IF0:.*]] = scf.if %[[CMP0]]
773-
// CHECK-NEXT: test.speculatable_op_with_memread
774-
// CHECK: else
775-
// CHECK-NEXT: ub.poison : i32
776-
// CHECK: %[[CMP1:.*]] = arith.cmpi ult, %[[LB]], %[[UB]] : index
777-
// CHECK-NEXT: %[[IF1:.*]] = scf.if %[[CMP1]]
778-
// CHECK-NEXT: arith.addi %[[ALWAYS]], %[[IF0]]
779-
// CHECK: else
780-
// CHECK-NEXT: ub.poison : i32
781-
// CHECK: %[[CMP2:.*]] = arith.cmpi ult, %[[LB]], %[[UB]] : index
782-
// CHECK-NEXT: %[[IF2:.*]] = scf.if %[[CMP2]]
783-
// CHECK-NEXT: test.speculatable_op_with_memread
784-
// CHECK: else
785-
// CHECK-NEXT: ub.poison : i32
786-
// CHECK: %[[CMP3:.*]] = arith.cmpi ult, %[[LB]], %[[UB]] : index
787-
// CHECK-NEXT: %{{.*}} = scf.if %[[CMP3]]
788-
// CHECK-NEXT: arith.addi %[[IF1]], %[[IF2]]
789-
// CHECK: else
790-
// CHECK-NEXT: ub.poison : i32
791-
// CHECK: scf.for %{{.*}} = %[[LB]] to %[[UB]]
792-
// CHECK-NOT: test.always_speculatable_op
793-
// CHECK-NOT: test.speculatable_op_with_memread
794-
%cst_0 = arith.constant 0 : i32
795-
%cst_42 = arith.constant dense<42> : tensor<64xi32>
796-
%ind_42 = arith.constant 42 : index
797-
%sum_result = scf.for %i = %lb to %ub step %step iter_args(%acc = %cst_0) -> i32 {
798-
%always_speculate = "test.always_speculatable_op"() : () -> i32
799-
%only_read_0 = "test.speculatable_op_with_memread"(%cst_42, %ind_42) : (tensor<64xi32>, index) -> i32
800-
%add_0 = arith.addi %always_speculate, %only_read_0 : i32
801-
%only_read_1 = "test.speculatable_op_with_memread"(%cst_42, %ind_42) : (tensor<64xi32>, index) -> i32
802-
%add_1 = arith.addi %add_0, %only_read_1 : i32
803-
%i_cast = arith.index_cast %i: index to i32
804-
%sum = arith.addi %add_1, %i_cast : i32
805-
scf.yield %sum : i32
806-
}
807-
return %sum_result : i32
808-
}
809-
810-
// CHECK-LABEL: test_speculatable_op_with_read_side_effect_failure_due_to_write
811-
func.func @test_speculatable_op_with_read_side_effect_failure_due_to_write(%lb: index, %ub: index, %step: index) -> i32 {
812-
// CHECK: test.always_speculatable_op
813-
// CHECK-NEXT: scf.for
814-
// CHECK-NOT: test.always_speculatable_op
815-
// CHECK: test.speculatable_op_with_memread
816-
// CHECK: test.speculatable_op_with_memwrite
817-
%cst_0 = arith.constant 0 : i32
818-
%cst_42 = arith.constant dense<42> : tensor<64xi32>
819-
%ind_42 = arith.constant 42 : index
820-
%sum_result = scf.for %i = %lb to %ub step %step iter_args(%acc = %cst_0) -> i32 {
821-
%always_speculate = "test.always_speculatable_op"() : () -> i32
822-
%only_read = "test.speculatable_op_with_memread"(%cst_42, %ind_42) : (tensor<64xi32>, index) -> i32
823-
%i_cast = arith.index_cast %i: index to i32
824-
%add = arith.addi %acc, %i_cast : i32
825-
%sum = arith.addi %add, %only_read : i32
826-
%write = "test.speculatable_op_with_memwrite"(%cst_42) : (tensor<64xi32>) -> i32
827-
scf.yield %sum : i32
828-
}
829-
return %sum_result : i32
830-
}
831-
832-
// CHECK-LABEL: test_speculatable_op_with_read_side_effect_failure_due_to_nested_write
833-
func.func @test_speculatable_op_with_read_side_effect_failure_due_to_nested_write(%lb: index, %ub: index, %step: index) -> i32 {
834-
// CHECK: test.always_speculatable_op
835-
// CHECK-NEXT: scf.for
836-
// CHECK-NOT: test.always_speculatable_op
837-
// CHECK: test.speculatable_op_with_memread
838-
// CHECK: scf.for
839-
// CHECK: scf.if
840-
// CHECK: test.speculatable_op_with_memwrite
841-
%cst_0 = arith.constant 0 : i32
842-
%cst_42 = arith.constant dense<42> : tensor<64xi32>
843-
%ind_42 = arith.constant 42 : index
844-
%sum_result = scf.for %i = %lb to %ub step %step iter_args(%acc = %cst_0) -> i32 {
845-
%always_speculate = "test.always_speculatable_op"() : () -> i32
846-
%only_read = "test.speculatable_op_with_memread"(%cst_42, %ind_42) : (tensor<64xi32>, index) -> i32
847-
%i_cast = arith.index_cast %i: index to i32
848-
%add = arith.addi %acc, %i_cast : i32
849-
%sum = arith.addi %add, %only_read : i32
850-
scf.for %j = %lb to %ub step %step {
851-
%eq42 = arith.cmpi eq, %j, %ind_42 : index
852-
scf.if %eq42 {
853-
%always_write = "test.speculatable_op_with_memwrite"(%cst_42) : (tensor<64xi32>) -> i32
854-
}
855-
}
856-
scf.yield %sum : i32
857-
}
858-
return %sum_result : i32
859-
}
860-
861717
// -----
862718

863719
func.func @speculate_tensor_dim_unknown_rank_unknown_dim(

0 commit comments

Comments
 (0)