Skip to content

Commit ccb7f41

Browse files
committed
LICM now checks if parent loop has constant bounds/steps and isn't zero trip
1 parent 8bad9d4 commit ccb7f41

File tree

4 files changed

+119
-16
lines changed

4 files changed

+119
-16
lines changed

mlir/include/mlir/Interfaces/SideEffectInterfaces.h

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
#ifndef MLIR_INTERFACES_SIDEEFFECTINTERFACES_H
1515
#define MLIR_INTERFACES_SIDEEFFECTINTERFACES_H
1616

17-
#include "mlir/IR/OpDefinition.h"
1817
#include "mlir/IR/Dominance.h"
18+
#include "mlir/IR/OpDefinition.h"
19+
#include "mlir/Interfaces/LoopLikeInterface.h"
1920

2021
namespace mlir {
2122
namespace SideEffects {
@@ -423,6 +424,15 @@ bool isOpTriviallyDead(Operation *op);
423424
/// Note: Terminators and symbols are never considered to be trivially dead.
424425
bool wouldOpBeTriviallyDead(Operation *op);
425426

427+
/// Returns TRUE if the loop is dead/zero-trip,
428+
/// FALSE if loop has constant bounds/steps and has at least 1 iteration
429+
/// on every dimension, returns nullopt otherwise
430+
///
431+
/// Can only infer if loop is dead if it has constant loop bounds/steps.
432+
/// Otherwise we assume that it's dead to be conservative.
433+
///
434+
std::optional<bool> isZeroTrip(mlir::LoopLikeOpInterface loop);
435+
426436
/// Returns true if the given operation is allowed to be moved under the
427437
/// memory effects interface.
428438
///
@@ -449,13 +459,15 @@ bool isMemoryEffectFree(Operation *op);
449459
/// Returns true if the given operation has conflict-free write effects
450460
///
451461
/// An operation is conflict free:
452-
/// (1) all of its memory effects are of type Write
453-
/// (2) there are no other ops with Alloc/Free/Write effects on the same
454-
/// resources within the ops parent region
455-
/// (3) all ops in the parent region with Read effects on the same resources
456-
/// are dominated by the operation
462+
/// (1) Parent is a loop with the LoopLikeOpInterface
463+
/// (2) Parent loop is not a zero trip loop and has constant bounds/steps
464+
/// (3) all of the op's memory effects are of type Write
465+
/// (4) there are no other ops with Alloc/Free/Write effects on the same
466+
/// resources within the op's parent loop region
467+
/// (5) all ops in the parent loop region with Read effects on the same
468+
/// resources are dominated by the operation
457469
///
458-
/// If the operation meets all 3 criteria, then it is conflict free
470+
/// If the operation meets all criteria, then it is conflict free
459471
bool isMemoryEffectConflictFree(Operation *op);
460472

461473
/// Returns true if op and/or any operations within its nested regions

mlir/lib/Interfaces/CMakeLists.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,21 @@ add_mlir_interface_library(MemorySlotInterfaces)
8585
add_mlir_interface_library(ParallelCombiningOpInterface)
8686
add_mlir_interface_library(RuntimeVerifiableOpInterface)
8787
add_mlir_interface_library(ShapedOpInterfaces)
88-
add_mlir_interface_library(SideEffectInterfaces)
88+
89+
add_mlir_library(MLIRSideEffectInterfaces
90+
SideEffectInterfaces.cpp
91+
92+
ADDITIONAL_HEADER_DIRS
93+
${MLIR_MAIN_INCLUDE_DIR}/mlir/Interfaces
94+
95+
DEPENDS
96+
MLIRSideEffectInterfacesIncGen
97+
98+
LINK_LIBS PUBLIC
99+
MLIRIR
100+
MLIRDialectUtils
101+
MLIRLoopLikeInterface
102+
)
89103

90104
add_mlir_library(MLIRSubsetOpInterface
91105
SubsetOpInterface.cpp

mlir/lib/Interfaces/SideEffectInterfaces.cpp

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@
88

99
#include "mlir/Interfaces/SideEffectInterfaces.h"
1010

11+
#include "mlir/Dialect/Utils/StaticValueUtils.h"
1112
#include "mlir/IR/Dominance.h"
1213
#include "mlir/IR/SymbolTable.h"
13-
#include "llvm/ADT/SmallPtrSet.h"
14-
#include <unordered_set>
1514
#include <utility>
1615

1716
using namespace mlir;
@@ -23,6 +22,13 @@ using namespace mlir;
2322
/// Include the definitions of the side effect interfaces.
2423
#include "mlir/Interfaces/SideEffectInterfaces.cpp.inc"
2524

25+
// //===----------------------------------------------------------------------===//
26+
// // LoopLike Interfaces
27+
// //===----------------------------------------------------------------------===//
28+
29+
// /// Include the definitions of the loop like interfaces.
30+
// #include "mlir/Interfaces/LoopLikeInterface.cpp.inc"
31+
2632
//===----------------------------------------------------------------------===//
2733
// MemoryEffects
2834
//===----------------------------------------------------------------------===//
@@ -315,6 +321,33 @@ bool mlir::wouldOpBeTriviallyDead(Operation *op) {
315321
return wouldOpBeTriviallyDeadImpl(op);
316322
}
317323

324+
std::optional<bool> mlir::isZeroTrip(mlir::LoopLikeOpInterface loop) {
325+
auto lbs = loop.getLoopLowerBounds();
326+
auto ubs = loop.getLoopUpperBounds();
327+
auto steps = loop.getLoopSteps();
328+
329+
if (!lbs || !ubs || !steps)
330+
return std::nullopt;
331+
332+
if (lbs->size() != ubs->size() || ubs->size() != steps->size())
333+
return std::nullopt;
334+
335+
for (size_t i = 0; i < steps->size(); ++i) {
336+
auto lb = getConstantIntValue((*lbs)[i]);
337+
auto ub = getConstantIntValue((*ubs)[i]);
338+
auto st = getConstantIntValue((*steps)[i]);
339+
340+
if (!lb || !ub || !st)
341+
return std::nullopt; // non-constant -> unknown
342+
343+
if (*st >= 0 && *lb >= *ub)
344+
return true;
345+
if (*st < 0 && *lb <= *ub)
346+
return true;
347+
}
348+
return false;
349+
}
350+
318351
bool mlir::isMemoryEffectMovable(Operation *op) {
319352
if (isMemoryEffectFree(op))
320353
return true;
@@ -358,6 +391,19 @@ bool mlir::isMemoryEffectConflictFree(Operation *op) {
358391
// shouldn't be flagged as movable to be conservative
359392
if (!memInterface) return false;
360393

394+
// check parent loop to make sure it's not dead
395+
Operation *parent = op->getParentOp();
396+
if (!parent)
397+
return false;
398+
399+
auto loopInterface = dyn_cast<LoopLikeOpInterface>(parent);
400+
if (!loopInterface)
401+
return false;
402+
403+
auto isDead = isZeroTrip(loopInterface);
404+
if (!isDead.has_value() || isDead.value())
405+
return false;
406+
361407
// gather all effects on op
362408
llvm::SmallVector<MemoryEffects::EffectInstance> effects;
363409
memInterface.getEffects(effects);
@@ -376,8 +422,6 @@ bool mlir::isMemoryEffectConflictFree(Operation *op) {
376422
resourceCounts.try_emplace(effect.getResource()->getResourceID(), 0);
377423
}
378424

379-
// op itself is good, need to check rest of its parent region
380-
Operation *parent = op->getParentOp();
381425
mlir::DominanceInfo dom(parent);
382426

383427
for (Region &region : parent->getRegions())

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

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1480,8 +1480,8 @@ func.func @move_single_resource_write_dominant() attributes {} {
14801480

14811481
// -----
14821482

1483-
// CHECK-LABEL func.func @move_single_resource_read_dominant_negative
1484-
func.func @move_single_resource_read_dominant_negative() attributes {} {
1483+
// CHECK-LABEL func.func @move_single_resource_read_dominant
1484+
func.func @move_single_resource_read_dominant() attributes {} {
14851485
%c0_i32 = arith.constant 0 : i32
14861486
%c1_i32 = arith.constant 10 : i32
14871487
%c2_i32 = arith.constant 1 : i32
@@ -1530,8 +1530,8 @@ func.func @move_single_resource_basic_conflict() attributes {} {
15301530

15311531
// -----
15321532

1533-
// CHECK-LABEL func.func @move_single_resource_if_region_negative
1534-
func.func @move_single_resource_if_region_negative() attributes {} {
1533+
// CHECK-LABEL func.func @move_single_resource_if_region
1534+
func.func @move_single_resource_if_region() attributes {} {
15351535
%c0_i32 = arith.constant 0 : i32
15361536
%c1_i32 = arith.constant 10 : i32
15371537
%c2_i32 = arith.constant 1 : i32
@@ -1641,3 +1641,36 @@ func.func @move_multi_resource_comprehensive() attributes {} {
16411641
}
16421642
return
16431643
}
1644+
1645+
// -----
1646+
1647+
// CHECK-LABEL func.func @move_single_resource_dead_loops
1648+
func.func @move_single_resource_dead_loops() attributes {} {
1649+
%c0_i32 = arith.constant 0 : i32
1650+
%c1_i32 = arith.constant 10 : i32
1651+
%c2_i32 = arith.constant 1 : i32
1652+
%c3_i32 = arith.constant -1 : i32
1653+
1654+
scf.for %arg0 = %c0_i32 to %c1_i32 step %c3_i32 : i32 {
1655+
// CHECK: "test.test_effects_write_C"() : () -> ()
1656+
"test.test_effects_write_C"() : () -> ()
1657+
1658+
scf.for %arg1 = %c1_i32 to %c0_i32 step %c2_i32 : i32 {
1659+
// CHECK: "test.test_effects_write_B"() : () -> ()
1660+
"test.test_effects_write_B"() : () -> ()
1661+
1662+
scf.for %arg2 = %c0_i32 to %c0_i32 step %c0_i32 : i32 {
1663+
// CHECK: "test.test_effects_write_A"() : () -> ()
1664+
"test.test_effects_write_A"() : () -> ()
1665+
1666+
// CHECK: "test.test_effects_write_EF"() : () -> ()
1667+
scf.for %arg3 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
1668+
"test.test_effects_write_EF"() : () -> ()
1669+
// CHECK: "test.test_effects_read_EF"() : () -> ()
1670+
"test.test_effects_read_EF"() : () -> ()
1671+
}
1672+
}
1673+
}
1674+
}
1675+
return
1676+
}

0 commit comments

Comments
 (0)