@@ -324,6 +324,11 @@ class GreedyPatternRewriteDriver : public PatternRewriter,
324
324
llvm::SmallDenseSet<Operation *, 4 > strictModeFilteredOps;
325
325
326
326
private:
327
+ #if MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
328
+ // / Return "true" if the given op is guaranteed to be out of scope.
329
+ bool isOutOfScope (Operation *op) const ;
330
+ #endif // MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
331
+
327
332
// / Look over the provided operands for any defining operations that should
328
333
// / be re-added to the worklist. This function should be called when an
329
334
// / operation is modified or removed, as it may trigger further
@@ -375,6 +380,28 @@ GreedyPatternRewriteDriver::GreedyPatternRewriteDriver(
375
380
#endif // MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
376
381
}
377
382
383
+ #if MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
384
+ bool GreedyPatternRewriteDriver::isOutOfScope (Operation *op) const {
385
+ // No op is out of scope if no scope was set.
386
+ if (!config.scope )
387
+ return false ;
388
+ // Check if the given op and the scope region are part of the same IR tree.
389
+ // The parent op into which the given op was inserted may be unlinked, in
390
+ // which case we do not consider the given op to be out of scope. (That parent
391
+ // op will likely be inserted later, together with all its nested ops.)
392
+ Region *r = config.scope ;
393
+ while (r) {
394
+ if (r->findAncestorOpInRegion (*op) || r->getParentOp () == op)
395
+ break ;
396
+ r = r->getParentRegion ();
397
+ }
398
+ if (!r)
399
+ return false ;
400
+ // Op is out of scope if it is not within the scope region.
401
+ return !config.scope ->findAncestorOpInRegion (*op);
402
+ }
403
+ #endif // MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
404
+
378
405
bool GreedyPatternRewriteDriver::processWorklist () {
379
406
#ifndef NDEBUG
380
407
const char *logLineComment =
@@ -579,6 +606,8 @@ void GreedyPatternRewriteDriver::addToWorklist(Operation *op) {
579
606
addSingleOpToWorklist (op);
580
607
return ;
581
608
}
609
+ // TODO: Unlinked ops are currently not added to the worklist if a `scope`
610
+ // is specified.
582
611
if (region == nullptr )
583
612
return ;
584
613
} while ((op = region->getParentOp ()));
@@ -600,6 +629,13 @@ void GreedyPatternRewriteDriver::notifyOperationInserted(Operation *op) {
600
629
logger.startLine () << " ** Insert : '" << op->getName () << " '(" << op
601
630
<< " )\n " ;
602
631
});
632
+
633
+ #if MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
634
+ if (config.scope && isOutOfScope (op))
635
+ llvm::report_fatal_error (
636
+ " greedy pattern rewrite inserted op into region that is out of scope" );
637
+ #endif // MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
638
+
603
639
if (config.listener )
604
640
config.listener ->notifyOperationInserted (op);
605
641
if (config.strictMode == GreedyRewriteStrictness::ExistingAndNewOps)
@@ -608,10 +644,24 @@ void GreedyPatternRewriteDriver::notifyOperationInserted(Operation *op) {
608
644
}
609
645
610
646
void GreedyPatternRewriteDriver::notifyOperationModified (Operation *op) {
647
+ // TODO: This notification should also be triggered when moving an op into
648
+ // this op.
611
649
LLVM_DEBUG ({
612
650
logger.startLine () << " ** Modified: '" << op->getName () << " '(" << op
613
651
<< " )\n " ;
614
652
});
653
+
654
+ #if MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
655
+ if (config.scope ) {
656
+ // Modifying attributes of the op that owns the scope region is allowed
657
+ // when using the applyPatternsAndFoldGreedily(Operation *) entry point.
658
+ if (op != config.scope ->getParentOp () && isOutOfScope (op)) {
659
+ llvm::report_fatal_error (" greedy pattern rewrite modified op within "
660
+ " region that is out of scope" );
661
+ }
662
+ }
663
+ #endif // MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
664
+
615
665
if (config.listener )
616
666
config.listener ->notifyOperationModified (op);
617
667
addToWorklist (op);
@@ -637,16 +687,11 @@ void GreedyPatternRewriteDriver::notifyOperationRemoved(Operation *op) {
637
687
<< " )\n " ;
638
688
});
639
689
640
- #ifndef NDEBUG
641
- // Only ops that are within the configured scope are added to the worklist of
642
- // the greedy pattern rewriter. Moreover, the parent op of the scope region is
643
- // the part of the IR that is taken into account for the "expensive checks".
644
- // A greedy pattern rewrite is not allowed to erase the parent op of the scope
645
- // region, as that would break the worklist handling and the expensive checks.
646
- if (config.scope && config.scope ->getParentOp () == op)
647
- llvm_unreachable (
648
- " scope region must not be erased during greedy pattern rewrite" );
649
- #endif // NDEBUG
690
+ #if MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
691
+ if (config.scope && isOutOfScope (op))
692
+ llvm::report_fatal_error (
693
+ " greedy pattern rewrite removed op from region that is out of scope" );
694
+ #endif // MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
650
695
651
696
if (config.listener )
652
697
config.listener ->notifyOperationRemoved (op);
@@ -800,16 +845,22 @@ LogicalResult
800
845
mlir::applyPatternsAndFoldGreedily (Region ®ion,
801
846
const FrozenRewritePatternSet &patterns,
802
847
GreedyRewriteConfig config, bool *changed) {
803
- // The top-level operation must be known to be isolated from above to
804
- // prevent performing canonicalizations on operations defined at or above
805
- // the region containing 'op'.
806
- assert (region.getParentOp ()->hasTrait <OpTrait::IsIsolatedFromAbove>() &&
807
- " patterns can only be applied to operations IsolatedFromAbove" );
808
-
809
848
// Set scope if not specified.
810
849
if (!config.scope )
811
850
config.scope = ®ion;
812
851
852
+ // Make sure that the specified region on which the greedy rewrite should
853
+ // operate is in scope.
854
+ assert (config.scope ->isAncestor (®ion) && " input region must be in scope" );
855
+
856
+ // The scope of a greedy pattern rewrite must be IsolatedFromAbove. Ops that
857
+ // are out of scope are never added to the worklist and any out-of-scope IR
858
+ // modifications trigger an assertion when expensive expensive checks are
859
+ // enabled (as long as the rewriter API is used correctly).
860
+ assert (
861
+ config.scope ->getParentOp ()->hasTrait <OpTrait::IsIsolatedFromAbove>() &&
862
+ " greedy pattern rewrite scope must be IsolatedFromAbove" );
863
+
813
864
#if MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
814
865
if (failed (verify (config.scope ->getParentOp ())))
815
866
llvm::report_fatal_error (
@@ -886,7 +937,8 @@ LogicalResult MultiOpPatternRewriteDriver::simplify(ArrayRef<Operation *> ops,
886
937
return success (worklist.empty ());
887
938
}
888
939
889
- // / Find the region that is the closest common ancestor of all given ops.
940
+ // / Find the IsolateFromAbove region that is the closest common ancestor of all
941
+ // / given ops.
890
942
// /
891
943
// / Note: This function returns `nullptr` if there is a top-level op among the
892
944
// / given list of ops.
@@ -896,6 +948,7 @@ static Region *findCommonAncestor(ArrayRef<Operation *> ops) {
896
948
if (ops.size () == 1 )
897
949
return ops.front ()->getParentRegion ();
898
950
951
+ // Find the closest region that contains all ops.
899
952
Region *region = ops.front ()->getParentRegion ();
900
953
ops = ops.drop_front ();
901
954
int sz = ops.size ();
@@ -912,6 +965,12 @@ static Region *findCommonAncestor(ArrayRef<Operation *> ops) {
912
965
break ;
913
966
region = region->getParentRegion ();
914
967
}
968
+
969
+ // Find the closest IsolatedFromAbove region.
970
+ while (region &&
971
+ !region->getParentOp ()->hasTrait <OpTrait::IsIsolatedFromAbove>())
972
+ region = region->getParentRegion ();
973
+
915
974
return region;
916
975
}
917
976
@@ -932,8 +991,16 @@ LogicalResult mlir::applyOpPatternsAndFold(
932
991
// there is a top-level op among `ops`.
933
992
config.scope = findCommonAncestor (ops);
934
993
} else {
935
- // If a scope was provided, make sure that all ops are in scope.
994
+ // If a scope was provided, make sure that it is IsolatedFromAbove and that
995
+ // all ops are in scope.
936
996
#ifndef NDEBUG
997
+ // The scope of a greedy pattern rewrite must be IsolatedFromAbove. Ops that
998
+ // are out of scope are never added to the worklist and any out-of-scope IR
999
+ // modifications trigger an assertion when expensive expensive checks are
1000
+ // enabled (as long as the rewriter API is used correctly).
1001
+ assert (
1002
+ config.scope ->getParentOp ()->hasTrait <OpTrait::IsIsolatedFromAbove>() &&
1003
+ " greedy pattern rewrite scope must be IsolatedFromAbove" );
937
1004
bool allOpsInScope = llvm::all_of (ops, [&](Operation *op) {
938
1005
return static_cast <bool >(config.scope ->findAncestorOpInRegion (*op));
939
1006
});
0 commit comments