@@ -42,6 +42,38 @@ LogicalResult AbstractDenseDataFlowAnalysis::visit(ProgramPoint point) {
42
42
return success ();
43
43
}
44
44
45
+ void AbstractDenseDataFlowAnalysis::visitCallOperation (
46
+ CallOpInterface call, AbstractDenseLattice *after) {
47
+
48
+ const auto *predecessors =
49
+ getOrCreateFor<PredecessorState>(call.getOperation (), call);
50
+ // If not all return sites are known, then conservatively assume we can't
51
+ // reason about the data-flow.
52
+ if (!predecessors->allPredecessorsKnown ())
53
+ return setToEntryState (after);
54
+
55
+ for (Operation *predecessor : predecessors->getKnownPredecessors ()) {
56
+ // Get the lattices at callee return:
57
+ //
58
+ // func.func @callee() {
59
+ // ...
60
+ // return // predecessor
61
+ // // latticeAtCalleeReturn
62
+ // }
63
+ // func.func @caller() {
64
+ // ...
65
+ // call @callee
66
+ // // latticeAfterCall
67
+ // ...
68
+ // }
69
+ AbstractDenseLattice *latticeAfterCall = after;
70
+ const AbstractDenseLattice *latticeAtCalleeReturn =
71
+ getLatticeFor (call.getOperation (), predecessor);
72
+ visitCallControlFlowTransfer (call, CallControlFlowAction::ExitCallee,
73
+ *latticeAtCalleeReturn, latticeAfterCall);
74
+ }
75
+ }
76
+
45
77
void AbstractDenseDataFlowAnalysis::processOperation (Operation *op) {
46
78
// If the containing block is not executable, bail out.
47
79
if (!getOrCreateFor<Executable>(op, op->getBlock ())->isLive ())
@@ -50,30 +82,22 @@ void AbstractDenseDataFlowAnalysis::processOperation(Operation *op) {
50
82
// Get the dense lattice to update.
51
83
AbstractDenseLattice *after = getLattice (op);
52
84
85
+ // Get the dense state before the execution of the op.
86
+ const AbstractDenseLattice *before;
87
+ if (Operation *prev = op->getPrevNode ())
88
+ before = getLatticeFor (op, prev);
89
+ else
90
+ before = getLatticeFor (op, op->getBlock ());
91
+
53
92
// If this op implements region control-flow, then control-flow dictates its
54
93
// transfer function.
55
94
if (auto branch = dyn_cast<RegionBranchOpInterface>(op))
56
95
return visitRegionBranchOperation (op, branch, after);
57
96
58
97
// If this is a call operation, then join its lattices across known return
59
98
// sites.
60
- if (auto call = dyn_cast<CallOpInterface>(op)) {
61
- const auto *predecessors = getOrCreateFor<PredecessorState>(op, call);
62
- // If not all return sites are known, then conservatively assume we can't
63
- // reason about the data-flow.
64
- if (!predecessors->allPredecessorsKnown ())
65
- return setToEntryState (after);
66
- for (Operation *predecessor : predecessors->getKnownPredecessors ())
67
- join (after, *getLatticeFor (op, predecessor));
68
- return ;
69
- }
70
-
71
- // Get the dense state before the execution of the op.
72
- const AbstractDenseLattice *before;
73
- if (Operation *prev = op->getPrevNode ())
74
- before = getLatticeFor (op, prev);
75
- else
76
- before = getLatticeFor (op, op->getBlock ());
99
+ if (auto call = dyn_cast<CallOpInterface>(op))
100
+ return visitCallOperation (call, after);
77
101
78
102
// Invoke the operation transfer function.
79
103
visitOperationImpl (op, *before, after);
@@ -100,10 +124,15 @@ void AbstractDenseDataFlowAnalysis::visitBlock(Block *block) {
100
124
return setToEntryState (after);
101
125
for (Operation *callsite : callsites->getKnownPredecessors ()) {
102
126
// Get the dense lattice before the callsite.
127
+ const AbstractDenseLattice *before;
103
128
if (Operation *prev = callsite->getPrevNode ())
104
- join (after, * getLatticeFor (block, prev) );
129
+ before = getLatticeFor (block, prev);
105
130
else
106
- join (after, *getLatticeFor (block, callsite->getBlock ()));
131
+ before = getLatticeFor (block, callsite->getBlock ());
132
+
133
+ visitCallControlFlowTransfer (cast<CallOpInterface>(callsite),
134
+ CallControlFlowAction::EnterCallee,
135
+ *before, after);
107
136
}
108
137
return ;
109
138
}
@@ -152,7 +181,41 @@ void AbstractDenseDataFlowAnalysis::visitRegionBranchOperation(
152
181
} else {
153
182
before = getLatticeFor (point, op);
154
183
}
155
- join (after, *before);
184
+
185
+ // This function is called in two cases:
186
+ // 1. when visiting the block (point = block);
187
+ // 2. when visiting the parent operation (point = parent op).
188
+ // In both cases, we are looking for predecessor operations of the point,
189
+ // 1. predecessor may be the terminator of another block from another
190
+ // region (assuming that the block does belong to another region via an
191
+ // assertion) or the parent (when parent can transfer control to this
192
+ // region);
193
+ // 2. predecessor may be the terminator of a block that exits the
194
+ // region (when region transfers control to the parent) or the operation
195
+ // before the parent.
196
+ // In the latter case, just perform the join as it isn't the control flow
197
+ // affected by the region.
198
+ std::optional<unsigned > regionFrom =
199
+ op == branch ? std::optional<unsigned >()
200
+ : op->getBlock ()->getParent ()->getRegionNumber ();
201
+ if (auto *toBlock = point.dyn_cast <Block *>()) {
202
+ assert (op == branch ||
203
+ toBlock->getParent () != op->getBlock ()->getParent ());
204
+ unsigned regionTo = toBlock->getParent ()->getRegionNumber ();
205
+ visitRegionBranchControlFlowTransfer (branch, regionFrom, regionTo,
206
+ *before, after);
207
+ } else {
208
+ assert (point.get <Operation *>() == branch &&
209
+ " expected to be visiting the branch itself" );
210
+ // Only need to call the arc transfer when the predecessor is the region
211
+ // or the op itself, not the previous op.
212
+ if (op->getParentOp () == branch || op == branch) {
213
+ visitRegionBranchControlFlowTransfer (
214
+ branch, regionFrom, /* regionTo=*/ std::nullopt, *before, after);
215
+ } else {
216
+ join (after, *before);
217
+ }
218
+ }
156
219
}
157
220
}
158
221
@@ -194,6 +257,44 @@ LogicalResult AbstractDenseBackwardDataFlowAnalysis::visit(ProgramPoint point) {
194
257
return success ();
195
258
}
196
259
260
+ void AbstractDenseBackwardDataFlowAnalysis::visitCallOperation (
261
+ CallOpInterface call, AbstractDenseLattice *before) {
262
+ // Find the callee.
263
+ Operation *callee = call.resolveCallable (&symbolTable);
264
+ auto callable = dyn_cast_or_null<CallableOpInterface>(callee);
265
+ if (!callable)
266
+ return setToExitState (before);
267
+
268
+ // No region means the callee is only declared in this module and we shouldn't
269
+ // assume anything about it.
270
+ Region *region = callable.getCallableRegion ();
271
+ if (!region || region->empty ())
272
+ return setToExitState (before);
273
+
274
+ // Call-level control flow specifies the data flow here.
275
+ //
276
+ // func.func @callee() {
277
+ // ^calleeEntryBlock:
278
+ // // latticeAtCalleeEntry
279
+ // ...
280
+ // }
281
+ // func.func @caller() {
282
+ // ...
283
+ // // latticeBeforeCall
284
+ // call @callee
285
+ // ...
286
+ // }
287
+ Block *calleeEntryBlock = ®ion->front ();
288
+ ProgramPoint calleeEntry = calleeEntryBlock->empty ()
289
+ ? ProgramPoint (calleeEntryBlock)
290
+ : &calleeEntryBlock->front ();
291
+ const AbstractDenseLattice &latticeAtCalleeEntry =
292
+ *getLatticeFor (call.getOperation (), calleeEntry);
293
+ AbstractDenseLattice *latticeBeforeCall = before;
294
+ visitCallControlFlowTransfer (call, CallControlFlowAction::EnterCallee,
295
+ latticeAtCalleeEntry, latticeBeforeCall);
296
+ }
297
+
197
298
void AbstractDenseBackwardDataFlowAnalysis::processOperation (Operation *op) {
198
299
// If the containing block is not executable, bail out.
199
300
if (!getOrCreateFor<Executable>(op, op->getBlock ())->isLive ())
@@ -202,46 +303,19 @@ void AbstractDenseBackwardDataFlowAnalysis::processOperation(Operation *op) {
202
303
// Get the dense lattice to update.
203
304
AbstractDenseLattice *before = getLattice (op);
204
305
205
- // If the op implements region control flow, then the interface specifies the
206
- // control function.
207
- // TODO: this is not always true, e.g. linalg.generic, but is implement this
208
- // way for consistency with the dense forward analysis.
209
- if (auto branch = dyn_cast<RegionBranchOpInterface>(op))
210
- return visitRegionBranchOperation (op, branch, std::nullopt, before);
211
-
212
- // If the op is a call-like, do inter-procedural data flow as follows:
213
- //
214
- // - find the callable (resolve via the symbol table),
215
- // - get the entry block of the callable region,
216
- // - take the state before the first operation if present or at block end
217
- // otherwise,
218
- // - meet that state with the state before the call-like op.
219
- if (auto call = dyn_cast<CallOpInterface>(op)) {
220
- Operation *callee = call.resolveCallable (&symbolTable);
221
- if (auto callable = dyn_cast<CallableOpInterface>(callee)) {
222
- Region *region = callable.getCallableRegion ();
223
- if (region && !region->empty ()) {
224
- Block *entryBlock = ®ion->front ();
225
- if (entryBlock->empty ())
226
- meet (before, *getLatticeFor (op, entryBlock));
227
- else
228
- meet (before, *getLatticeFor (op, &entryBlock->front ()));
229
- } else {
230
- setToExitState (before);
231
- }
232
- } else {
233
- setToExitState (before);
234
- }
235
- return ;
236
- }
237
-
238
306
// Get the dense state after execution of this op.
239
307
const AbstractDenseLattice *after;
240
308
if (Operation *next = op->getNextNode ())
241
309
after = getLatticeFor (op, next);
242
310
else
243
311
after = getLatticeFor (op, op->getBlock ());
244
312
313
+ // Special cases where control flow may dictate data flow.
314
+ if (auto branch = dyn_cast<RegionBranchOpInterface>(op))
315
+ return visitRegionBranchOperation (op, branch, std::nullopt, before);
316
+ if (auto call = dyn_cast<CallOpInterface>(op))
317
+ return visitCallOperation (call, before);
318
+
245
319
// Invoke the operation transfer function.
246
320
visitOperationImpl (op, *after, before);
247
321
}
@@ -280,16 +354,20 @@ void AbstractDenseBackwardDataFlowAnalysis::visitBlock(Block *block) {
280
354
return setToExitState (before);
281
355
282
356
for (Operation *callsite : callsites->getKnownPredecessors ()) {
357
+ const AbstractDenseLattice *after;
283
358
if (Operation *next = callsite->getNextNode ())
284
- meet (before, * getLatticeFor (block, next) );
359
+ after = getLatticeFor (block, next);
285
360
else
286
- meet (before, *getLatticeFor (block, callsite->getBlock ()));
361
+ after = getLatticeFor (block, callsite->getBlock ());
362
+ visitCallControlFlowTransfer (cast<CallOpInterface>(callsite),
363
+ CallControlFlowAction::ExitCallee, *after,
364
+ before);
287
365
}
288
366
return ;
289
367
}
290
368
291
369
// If this block is exiting from an operation with region-based control
292
- // flow, follow that flow.
370
+ // flow, propagate the lattice back along the control flow edge .
293
371
if (auto branch = dyn_cast<RegionBranchOpInterface>(block->getParentOp ())) {
294
372
visitRegionBranchOperation (block, branch,
295
373
block->getParent ()->getRegionNumber (), before);
@@ -346,7 +424,11 @@ void AbstractDenseBackwardDataFlowAnalysis::visitRegionBranchOperation(
346
424
else
347
425
after = getLatticeFor (point, &successorBlock->front ());
348
426
}
349
- meet (before, *after);
427
+ std::optional<unsigned > successorNo =
428
+ successor.isParent () ? std::optional<unsigned >()
429
+ : successor.getSuccessor ()->getRegionNumber ();
430
+ visitRegionBranchControlFlowTransfer (branch, regionNo, successorNo, *after,
431
+ before);
350
432
}
351
433
}
352
434
0 commit comments