@@ -142,6 +142,14 @@ bool getEffectsAfter(Operation *op,
142
142
143
143
void BarrierOp::getEffects (
144
144
SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
145
+
146
+ // If this doesn't synchronize any values, it has no effects.
147
+ if (llvm::all_of (getOperands (), [](Value v) {
148
+ IntegerAttr constValue;
149
+ return matchPattern (v, m_Constant (&constValue));
150
+ }))
151
+ return ;
152
+
145
153
Operation *op = getOperation ();
146
154
147
155
if (!getEffectsBefore (op, effects))
@@ -151,6 +159,8 @@ void BarrierOp::getEffects(
151
159
return ;
152
160
}
153
161
162
+ bool isReadNone (Operation *op);
163
+
154
164
class BarrierHoist final : public OpRewritePattern<BarrierOp> {
155
165
public:
156
166
using OpRewritePattern<BarrierOp>::OpRewritePattern;
@@ -162,12 +172,7 @@ class BarrierHoist final : public OpRewritePattern<BarrierOp> {
162
172
bool below = true ;
163
173
for (Operation *it = barrier->getNextNode (); it != nullptr ;
164
174
it = it->getNextNode ()) {
165
- auto memInterface = dyn_cast<MemoryEffectOpInterface>(it);
166
- if (!memInterface) {
167
- below = false ;
168
- break ;
169
- }
170
- if (!memInterface.hasNoEffect ()) {
175
+ if (!isReadNone (it)) {
171
176
below = false ;
172
177
break ;
173
178
}
@@ -181,12 +186,7 @@ class BarrierHoist final : public OpRewritePattern<BarrierOp> {
181
186
bool above = true ;
182
187
for (Operation *it = barrier->getPrevNode (); it != nullptr ;
183
188
it = it->getPrevNode ()) {
184
- auto memInterface = dyn_cast<MemoryEffectOpInterface>(it);
185
- if (!memInterface) {
186
- above = false ;
187
- break ;
188
- }
189
- if (!memInterface.hasNoEffect ()) {
189
+ if (!isReadNone (it)) {
190
190
above = false ;
191
191
break ;
192
192
}
@@ -198,6 +198,30 @@ class BarrierHoist final : public OpRewritePattern<BarrierOp> {
198
198
return success ();
199
199
}
200
200
}
201
+ // Move barrier into after region and after loop, if possible
202
+ if (auto whileOp = dyn_cast<scf::WhileOp>(barrier->getParentOp ())) {
203
+ if (barrier->getParentRegion () == &whileOp.getBefore ()) {
204
+ auto cond = whileOp.getBefore ().front ().getTerminator ();
205
+
206
+ bool above = true ;
207
+ for (Operation *it = cond; it != nullptr ; it = it->getPrevNode ()) {
208
+ if (it == barrier)
209
+ break ;
210
+ if (!isReadNone (it)) {
211
+ above = false ;
212
+ break ;
213
+ }
214
+ }
215
+ if (above) {
216
+ rewriter.setInsertionPointToStart (&whileOp.getAfter ().front ());
217
+ rewriter.create <BarrierOp>(barrier.getLoc (), barrier.getOperands ());
218
+ rewriter.setInsertionPoint (whileOp->getNextNode ());
219
+ rewriter.create <BarrierOp>(barrier.getLoc (), barrier.getOperands ());
220
+ rewriter.eraseOp (barrier);
221
+ return success ();
222
+ }
223
+ }
224
+ }
201
225
return failure ();
202
226
}
203
227
};
@@ -1531,6 +1555,13 @@ struct MoveIntoIfs : public OpRewritePattern<scf::IfOp> {
1531
1555
if (!thenUse && !elseUse)
1532
1556
return failure ();
1533
1557
1558
+ // If this is used in an affine if/for/parallel op, do not move it, as it
1559
+ // may no longer be a legal symbol
1560
+ for (OpOperand &use : prevOp->getUses ()) {
1561
+ if (isa<AffineForOp, AffineIfOp, AffineParallelOp>(use.getOwner ()))
1562
+ return failure ();
1563
+ }
1564
+
1534
1565
rewriter.startRootUpdate (nextIf);
1535
1566
rewriter.startRootUpdate (prevOp);
1536
1567
prevOp->moveBefore (thenUse ? &nextIf.thenBlock ()->front ()
@@ -1853,6 +1884,7 @@ class CmpProp final : public OpRewritePattern<CmpIOp> {
1853
1884
resultTypes.push_back (op.getType ());
1854
1885
1855
1886
auto rhs2 = rewriter.clone (*rhs)->getResult (0 );
1887
+ rewriter.setInsertionPoint (ifOp);
1856
1888
auto nop = rewriter.create <scf::IfOp>(
1857
1889
ifOp.getLoc (), resultTypes, ifOp.getCondition (), /* hasElse*/ true );
1858
1890
nop.getThenRegion ().takeBody (ifOp.getThenRegion ());
@@ -1877,9 +1909,156 @@ class CmpProp final : public OpRewritePattern<CmpIOp> {
1877
1909
}
1878
1910
};
1879
1911
1912
+ // / Given an operation, return whether this op is guaranteed to
1913
+ // / allocate an AutomaticAllocationScopeResource
1914
+ static bool isGuaranteedAutomaticAllocation (Operation *op) {
1915
+ MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op);
1916
+ if (!interface)
1917
+ return false ;
1918
+ for (auto res : op->getResults ()) {
1919
+ if (auto effect =
1920
+ interface.getEffectOnValue <MemoryEffects::Allocate>(res)) {
1921
+ if (isa<SideEffects::AutomaticAllocationScopeResource>(
1922
+ effect->getResource ()))
1923
+ return true ;
1924
+ }
1925
+ }
1926
+ return false ;
1927
+ }
1928
+
1929
+ template <typename T>
1930
+ struct AlwaysAllocaScopeHoister : public OpRewritePattern <T> {
1931
+ using OpRewritePattern<T>::OpRewritePattern;
1932
+
1933
+ LogicalResult matchAndRewrite (T top,
1934
+ PatternRewriter &rewriter) const override {
1935
+
1936
+ Operation *op = top;
1937
+ if (!op->getParentWithTrait <OpTrait::AutomaticAllocationScope>())
1938
+ return failure ();
1939
+
1940
+ Operation *lastParentWithoutScope =
1941
+ op->hasTrait <OpTrait::AutomaticAllocationScope>() ? op
1942
+ : op->getParentOp ();
1943
+
1944
+ if (!lastParentWithoutScope)
1945
+ return failure ();
1946
+
1947
+ while (!lastParentWithoutScope->getParentOp ()
1948
+ ->hasTrait <OpTrait::AutomaticAllocationScope>()) {
1949
+ lastParentWithoutScope = lastParentWithoutScope->getParentOp ();
1950
+ if (!lastParentWithoutScope)
1951
+ return failure ();
1952
+ }
1953
+ assert (lastParentWithoutScope->getParentOp ()
1954
+ ->hasTrait <OpTrait::AutomaticAllocationScope>());
1955
+
1956
+ Region *containingRegion = nullptr ;
1957
+ if (lastParentWithoutScope == op)
1958
+ containingRegion = &op->getRegion (0 );
1959
+ for (auto &r : lastParentWithoutScope->getRegions ()) {
1960
+ if (r.isAncestor (op->getParentRegion ())) {
1961
+ assert (containingRegion == nullptr &&
1962
+ " only one region can contain the op" );
1963
+ containingRegion = &r;
1964
+ }
1965
+ }
1966
+ assert (containingRegion && " op must be contained in a region" );
1967
+
1968
+ SmallVector<Operation *> toHoist;
1969
+ op->walk <WalkOrder::PreOrder>([&](Operation *alloc) {
1970
+ if (alloc != op && alloc->hasTrait <OpTrait::AutomaticAllocationScope>())
1971
+ return WalkResult::skip ();
1972
+
1973
+ if (!isGuaranteedAutomaticAllocation (alloc))
1974
+ return WalkResult::advance ();
1975
+
1976
+ // If any operand is not defined before the location of
1977
+ // lastParentWithoutScope (i.e. where we would hoist to), skip.
1978
+ if (llvm::any_of (alloc->getOperands (), [&](Value v) {
1979
+ return containingRegion->isAncestor (v.getParentRegion ());
1980
+ }))
1981
+ return WalkResult::skip ();
1982
+ toHoist.push_back (alloc);
1983
+ return WalkResult::advance ();
1984
+ });
1985
+
1986
+ if (toHoist.empty ())
1987
+ return failure ();
1988
+ rewriter.setInsertionPoint (lastParentWithoutScope);
1989
+ for (auto *op : toHoist) {
1990
+ auto *cloned = rewriter.clone (*op);
1991
+ rewriter.replaceOp (op, cloned->getResults ());
1992
+ }
1993
+ return success ();
1994
+ }
1995
+ };
1996
+
1997
+ static bool isOpItselfPotentialAutomaticAllocation (Operation *op) {
1998
+ // This op itself doesn't create a stack allocation,
1999
+ // the inner allocation should be handled separately.
2000
+ if (op->hasTrait <OpTrait::HasRecursiveSideEffects>())
2001
+ return false ;
2002
+ MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op);
2003
+ if (!interface)
2004
+ return true ;
2005
+ for (auto res : op->getResults ()) {
2006
+ if (auto effect =
2007
+ interface.getEffectOnValue <MemoryEffects::Allocate>(res)) {
2008
+ if (isa<SideEffects::AutomaticAllocationScopeResource>(
2009
+ effect->getResource ()))
2010
+ return true ;
2011
+ }
2012
+ }
2013
+ return false ;
2014
+ }
2015
+
2016
+ struct AggressiveAllocaScopeInliner
2017
+ : public OpRewritePattern<memref::AllocaScopeOp> {
2018
+ using OpRewritePattern<memref::AllocaScopeOp>::OpRewritePattern;
2019
+
2020
+ LogicalResult matchAndRewrite (memref::AllocaScopeOp op,
2021
+ PatternRewriter &rewriter) const override {
2022
+ bool hasPotentialAlloca =
2023
+ op->walk <WalkOrder::PreOrder>([&](Operation *alloc) {
2024
+ if (alloc == op || isa<LLVM::CallOp>(alloc) ||
2025
+ isa<func::CallOp>(alloc))
2026
+ return WalkResult::advance ();
2027
+ if (isOpItselfPotentialAutomaticAllocation (alloc))
2028
+ return WalkResult::interrupt ();
2029
+ if (alloc->hasTrait <OpTrait::AutomaticAllocationScope>())
2030
+ return WalkResult::skip ();
2031
+ return WalkResult::advance ();
2032
+ }).wasInterrupted ();
2033
+
2034
+ // If this contains no potential allocation, it is always legal to
2035
+ // inline. Otherwise, consider two conditions:
2036
+ if (hasPotentialAlloca) {
2037
+ // If the parent isn't an allocation scope, or we are not the last
2038
+ // non-terminator op in the parent, we will extend the lifetime.
2039
+ if (!op->getParentOp ()->hasTrait <OpTrait::AutomaticAllocationScope>())
2040
+ return failure ();
2041
+ // if (!lastNonTerminatorInRegion(op))
2042
+ // return failure();
2043
+ }
2044
+
2045
+ Block *block = &op.getRegion ().front ();
2046
+ Operation *terminator = block->getTerminator ();
2047
+ ValueRange results = terminator->getOperands ();
2048
+ rewriter.mergeBlockBefore (block, op);
2049
+ rewriter.replaceOp (op, results);
2050
+ rewriter.eraseOp (terminator);
2051
+ return success ();
2052
+ }
2053
+ };
2054
+
1880
2055
void TypeAlignOp::getCanonicalizationPatterns (RewritePatternSet &results,
1881
2056
MLIRContext *context) {
1882
- results.insert <TypeAlignCanonicalize, OrIExcludedMiddle, SelectI1Ext,
1883
- UndefProp<ExtUIOp>, UndefProp<ExtSIOp>, UndefProp<TruncIOp>,
1884
- CmpProp, UndefCmpProp>(context);
2057
+ results.insert <
2058
+ TypeAlignCanonicalize, OrIExcludedMiddle, SelectI1Ext, UndefProp<ExtUIOp>,
2059
+ UndefProp<ExtSIOp>, UndefProp<TruncIOp>, CmpProp, UndefCmpProp,
2060
+ AlwaysAllocaScopeHoister<memref::AllocaScopeOp>,
2061
+ AlwaysAllocaScopeHoister<scf::ForOp>,
2062
+ AlwaysAllocaScopeHoister<AffineForOp>, AggressiveAllocaScopeInliner>(
2063
+ context);
1885
2064
}
0 commit comments