1 | //===- Pass.cpp - Pass infrastructure implementation ----------------------===// |
---|---|
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file implements common pass infrastructure. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "mlir/Pass/Pass.h" |
14 | #include "PassDetail.h" |
15 | #include "mlir/IR/Diagnostics.h" |
16 | #include "mlir/IR/Dialect.h" |
17 | #include "mlir/IR/OpDefinition.h" |
18 | #include "mlir/IR/Threading.h" |
19 | #include "mlir/IR/Verifier.h" |
20 | #include "mlir/Support/FileUtilities.h" |
21 | #include "mlir/Support/IndentedOstream.h" |
22 | #include "llvm/ADT/Hashing.h" |
23 | #include "llvm/ADT/STLExtras.h" |
24 | #include "llvm/ADT/ScopeExit.h" |
25 | #include "llvm/Support/CommandLine.h" |
26 | #include "llvm/Support/CrashRecoveryContext.h" |
27 | #include "llvm/Support/Mutex.h" |
28 | #include "llvm/Support/Signals.h" |
29 | #include "llvm/Support/Threading.h" |
30 | #include "llvm/Support/ToolOutputFile.h" |
31 | #include <optional> |
32 | |
33 | using namespace mlir; |
34 | using namespace mlir::detail; |
35 | |
36 | //===----------------------------------------------------------------------===// |
37 | // PassExecutionAction |
38 | //===----------------------------------------------------------------------===// |
39 | |
40 | PassExecutionAction::PassExecutionAction(ArrayRef<IRUnit> irUnits, |
41 | const Pass &pass) |
42 | : Base(irUnits), pass(pass) {} |
43 | |
44 | void PassExecutionAction::print(raw_ostream &os) const { |
45 | os << llvm::formatv(Fmt: "`{0}` running `{1}` on Operation `{2}`", Vals: tag, |
46 | Vals: pass.getName(), Vals: getOp()->getName()); |
47 | } |
48 | |
49 | Operation *PassExecutionAction::getOp() const { |
50 | ArrayRef<IRUnit> irUnits = getContextIRUnits(); |
51 | return irUnits.empty() ? nullptr |
52 | : llvm::dyn_cast_if_present<Operation *>(Val: irUnits[0]); |
53 | } |
54 | |
55 | MLIR_DEFINE_EXPLICIT_TYPE_ID(::mlir::PassExecutionAction) |
56 | |
57 | //===----------------------------------------------------------------------===// |
58 | // Pass |
59 | //===----------------------------------------------------------------------===// |
60 | |
61 | /// Out of line virtual method to ensure vtables and metadata are emitted to a |
62 | /// single .o file. |
63 | void Pass::anchor() {} |
64 | |
65 | /// Attempt to initialize the options of this pass from the given string. |
66 | LogicalResult Pass::initializeOptions( |
67 | StringRef options, |
68 | function_ref<LogicalResult(const Twine &)> errorHandler) { |
69 | std::string errStr; |
70 | llvm::raw_string_ostream os(errStr); |
71 | if (failed(Result: passOptions.parseFromString(options, errorStream&: os))) { |
72 | return errorHandler(errStr); |
73 | } |
74 | return success(); |
75 | } |
76 | |
77 | /// Copy the option values from 'other', which is another instance of this |
78 | /// pass. |
79 | void Pass::copyOptionValuesFrom(const Pass *other) { |
80 | passOptions.copyOptionValuesFrom(other: other->passOptions); |
81 | } |
82 | |
83 | /// Prints out the pass in the textual representation of pipelines. If this is |
84 | /// an adaptor pass, print its pass managers. When `pretty` is true, the |
85 | /// printed pipeline is formatted for readability. |
86 | void Pass::printAsTextualPipeline(raw_ostream &os, bool pretty) { |
87 | // Special case for adaptors to print its pass managers. |
88 | if (auto *adaptor = dyn_cast<OpToOpPassAdaptor>(Val: this)) { |
89 | llvm::interleave( |
90 | c: adaptor->getPassManagers(), |
91 | each_fn: [&](OpPassManager &pm) { pm.printAsTextualPipeline(os, pretty); }, |
92 | between_fn: [&] { |
93 | os << ","; |
94 | if (pretty) |
95 | os << "\n"; |
96 | }); |
97 | return; |
98 | } |
99 | // Otherwise, print the pass argument followed by its options. If the pass |
100 | // doesn't have an argument, print the name of the pass to give some indicator |
101 | // of what pass was run. |
102 | StringRef argument = getArgument(); |
103 | if (!argument.empty()) |
104 | os << argument; |
105 | else |
106 | os << "unknown<"<< getName() << ">"; |
107 | passOptions.print(os); |
108 | } |
109 | |
110 | //===----------------------------------------------------------------------===// |
111 | // OpPassManagerImpl |
112 | //===----------------------------------------------------------------------===// |
113 | |
114 | namespace mlir { |
115 | namespace detail { |
116 | struct OpPassManagerImpl { |
117 | OpPassManagerImpl(OperationName opName, OpPassManager::Nesting nesting) |
118 | : name(opName.getStringRef().str()), opName(opName), |
119 | initializationGeneration(0), nesting(nesting) {} |
120 | OpPassManagerImpl(StringRef name, OpPassManager::Nesting nesting) |
121 | : name(name == OpPassManager::getAnyOpAnchorName() ? "": name.str()), |
122 | initializationGeneration(0), nesting(nesting) {} |
123 | OpPassManagerImpl(OpPassManager::Nesting nesting) |
124 | : initializationGeneration(0), nesting(nesting) {} |
125 | OpPassManagerImpl(const OpPassManagerImpl &rhs) |
126 | : name(rhs.name), opName(rhs.opName), |
127 | initializationGeneration(rhs.initializationGeneration), |
128 | nesting(rhs.nesting) { |
129 | for (const std::unique_ptr<Pass> &pass : rhs.passes) { |
130 | std::unique_ptr<Pass> newPass = pass->clone(); |
131 | newPass->threadingSibling = pass.get(); |
132 | passes.push_back(x: std::move(newPass)); |
133 | } |
134 | } |
135 | |
136 | /// Merge the passes of this pass manager into the one provided. |
137 | void mergeInto(OpPassManagerImpl &rhs); |
138 | |
139 | /// Nest a new operation pass manager for the given operation kind under this |
140 | /// pass manager. |
141 | OpPassManager &nest(OperationName nestedName) { |
142 | return nest(nested: OpPassManager(nestedName, nesting)); |
143 | } |
144 | OpPassManager &nest(StringRef nestedName) { |
145 | return nest(nested: OpPassManager(nestedName, nesting)); |
146 | } |
147 | OpPassManager &nestAny() { return nest(nested: OpPassManager(nesting)); } |
148 | |
149 | /// Nest the given pass manager under this pass manager. |
150 | OpPassManager &nest(OpPassManager &&nested); |
151 | |
152 | /// Add the given pass to this pass manager. If this pass has a concrete |
153 | /// operation type, it must be the same type as this pass manager. |
154 | void addPass(std::unique_ptr<Pass> pass); |
155 | |
156 | /// Clear the list of passes in this pass manager, other options are |
157 | /// preserved. |
158 | void clear(); |
159 | |
160 | /// Finalize the pass list in preparation for execution. This includes |
161 | /// coalescing adjacent pass managers when possible, verifying scheduled |
162 | /// passes, etc. |
163 | LogicalResult finalizePassList(MLIRContext *ctx); |
164 | |
165 | /// Return the operation name of this pass manager. |
166 | std::optional<OperationName> getOpName(MLIRContext &context) { |
167 | if (!name.empty() && !opName) |
168 | opName = OperationName(name, &context); |
169 | return opName; |
170 | } |
171 | std::optional<StringRef> getOpName() const { |
172 | return name.empty() ? std::optional<StringRef>() |
173 | : std::optional<StringRef>(name); |
174 | } |
175 | |
176 | /// Return the name used to anchor this pass manager. This is either the name |
177 | /// of an operation, or the result of `getAnyOpAnchorName()` in the case of an |
178 | /// op-agnostic pass manager. |
179 | StringRef getOpAnchorName() const { |
180 | return getOpName().value_or(u: OpPassManager::getAnyOpAnchorName()); |
181 | } |
182 | |
183 | /// Indicate if the current pass manager can be scheduled on the given |
184 | /// operation type. |
185 | bool canScheduleOn(MLIRContext &context, OperationName opName); |
186 | |
187 | /// The name of the operation that passes of this pass manager operate on. |
188 | std::string name; |
189 | |
190 | /// The cached OperationName (internalized in the context) for the name of the |
191 | /// operation that passes of this pass manager operate on. |
192 | std::optional<OperationName> opName; |
193 | |
194 | /// The set of passes to run as part of this pass manager. |
195 | std::vector<std::unique_ptr<Pass>> passes; |
196 | |
197 | /// The current initialization generation of this pass manager. This is used |
198 | /// to indicate when a pass manager should be reinitialized. |
199 | unsigned initializationGeneration; |
200 | |
201 | /// Control the implicit nesting of passes that mismatch the name set for this |
202 | /// OpPassManager. |
203 | OpPassManager::Nesting nesting; |
204 | }; |
205 | } // namespace detail |
206 | } // namespace mlir |
207 | |
208 | void OpPassManagerImpl::mergeInto(OpPassManagerImpl &rhs) { |
209 | assert(name == rhs.name && "merging unrelated pass managers"); |
210 | for (auto &pass : passes) |
211 | rhs.passes.push_back(x: std::move(pass)); |
212 | passes.clear(); |
213 | } |
214 | |
215 | OpPassManager &OpPassManagerImpl::nest(OpPassManager &&nested) { |
216 | auto *adaptor = new OpToOpPassAdaptor(std::move(nested)); |
217 | addPass(pass: std::unique_ptr<Pass>(adaptor)); |
218 | return adaptor->getPassManagers().front(); |
219 | } |
220 | |
221 | void OpPassManagerImpl::addPass(std::unique_ptr<Pass> pass) { |
222 | // If this pass runs on a different operation than this pass manager, then |
223 | // implicitly nest a pass manager for this operation if enabled. |
224 | std::optional<StringRef> pmOpName = getOpName(); |
225 | std::optional<StringRef> passOpName = pass->getOpName(); |
226 | if (pmOpName && passOpName && *pmOpName != *passOpName) { |
227 | if (nesting == OpPassManager::Nesting::Implicit) |
228 | return nest(nestedName: *passOpName).addPass(pass: std::move(pass)); |
229 | llvm::report_fatal_error(reason: llvm::Twine("Can't add pass '") + pass->getName() + |
230 | "' restricted to '"+ *passOpName + |
231 | "' on a PassManager intended to run on '"+ |
232 | getOpAnchorName() + "', did you intend to nest?"); |
233 | } |
234 | |
235 | passes.emplace_back(args: std::move(pass)); |
236 | } |
237 | |
238 | void OpPassManagerImpl::clear() { passes.clear(); } |
239 | |
240 | LogicalResult OpPassManagerImpl::finalizePassList(MLIRContext *ctx) { |
241 | auto finalizeAdaptor = [ctx](OpToOpPassAdaptor *adaptor) { |
242 | for (auto &pm : adaptor->getPassManagers()) |
243 | if (failed(Result: pm.getImpl().finalizePassList(ctx))) |
244 | return failure(); |
245 | return success(); |
246 | }; |
247 | |
248 | // Walk the pass list and merge adjacent adaptors. |
249 | OpToOpPassAdaptor *lastAdaptor = nullptr; |
250 | for (auto &pass : passes) { |
251 | // Check to see if this pass is an adaptor. |
252 | if (auto *currentAdaptor = dyn_cast<OpToOpPassAdaptor>(Val: pass.get())) { |
253 | // If it is the first adaptor in a possible chain, remember it and |
254 | // continue. |
255 | if (!lastAdaptor) { |
256 | lastAdaptor = currentAdaptor; |
257 | continue; |
258 | } |
259 | |
260 | // Otherwise, try to merge into the existing adaptor and delete the |
261 | // current one. If merging fails, just remember this as the last adaptor. |
262 | if (succeeded(Result: currentAdaptor->tryMergeInto(ctx, rhs&: *lastAdaptor))) |
263 | pass.reset(); |
264 | else |
265 | lastAdaptor = currentAdaptor; |
266 | } else if (lastAdaptor) { |
267 | // If this pass isn't an adaptor, finalize it and forget the last adaptor. |
268 | if (failed(Result: finalizeAdaptor(lastAdaptor))) |
269 | return failure(); |
270 | lastAdaptor = nullptr; |
271 | } |
272 | } |
273 | |
274 | // If there was an adaptor at the end of the manager, finalize it as well. |
275 | if (lastAdaptor && failed(Result: finalizeAdaptor(lastAdaptor))) |
276 | return failure(); |
277 | |
278 | // Now that the adaptors have been merged, erase any empty slots corresponding |
279 | // to the merged adaptors that were nulled-out in the loop above. |
280 | llvm::erase_if(C&: passes, P: std::logical_not<std::unique_ptr<Pass>>()); |
281 | |
282 | // If this is a op-agnostic pass manager, there is nothing left to do. |
283 | std::optional<OperationName> rawOpName = getOpName(context&: *ctx); |
284 | if (!rawOpName) |
285 | return success(); |
286 | |
287 | // Otherwise, verify that all of the passes are valid for the current |
288 | // operation anchor. |
289 | std::optional<RegisteredOperationName> opName = |
290 | rawOpName->getRegisteredInfo(); |
291 | for (std::unique_ptr<Pass> &pass : passes) { |
292 | if (opName && !pass->canScheduleOn(opName: *opName)) { |
293 | return emitError(UnknownLoc::get(ctx)) |
294 | << "unable to schedule pass '"<< pass->getName() |
295 | << "' on a PassManager intended to run on '"<< getOpAnchorName() |
296 | << "'!"; |
297 | } |
298 | } |
299 | return success(); |
300 | } |
301 | |
302 | bool OpPassManagerImpl::canScheduleOn(MLIRContext &context, |
303 | OperationName opName) { |
304 | // If this pass manager is op-specific, we simply check if the provided |
305 | // operation name is the same as this one. |
306 | std::optional<OperationName> pmOpName = getOpName(context); |
307 | if (pmOpName) |
308 | return pmOpName == opName; |
309 | |
310 | // Otherwise, this is an op-agnostic pass manager. Check that the operation |
311 | // can be scheduled on all passes within the manager. |
312 | std::optional<RegisteredOperationName> registeredInfo = |
313 | opName.getRegisteredInfo(); |
314 | if (!registeredInfo || |
315 | !registeredInfo->hasTrait<OpTrait::IsIsolatedFromAbove>()) |
316 | return false; |
317 | return llvm::all_of(Range&: passes, P: [&](const std::unique_ptr<Pass> &pass) { |
318 | return pass->canScheduleOn(opName: *registeredInfo); |
319 | }); |
320 | } |
321 | |
322 | //===----------------------------------------------------------------------===// |
323 | // OpPassManager |
324 | //===----------------------------------------------------------------------===// |
325 | |
326 | OpPassManager::OpPassManager(Nesting nesting) |
327 | : impl(new OpPassManagerImpl(nesting)) {} |
328 | OpPassManager::OpPassManager(StringRef name, Nesting nesting) |
329 | : impl(new OpPassManagerImpl(name, nesting)) {} |
330 | OpPassManager::OpPassManager(OperationName name, Nesting nesting) |
331 | : impl(new OpPassManagerImpl(name, nesting)) {} |
332 | OpPassManager::OpPassManager(OpPassManager &&rhs) { *this = std::move(rhs); } |
333 | OpPassManager::OpPassManager(const OpPassManager &rhs) { *this = rhs; } |
334 | OpPassManager &OpPassManager::operator=(const OpPassManager &rhs) { |
335 | impl = std::make_unique<OpPassManagerImpl>(args&: *rhs.impl); |
336 | return *this; |
337 | } |
338 | OpPassManager &OpPassManager::operator=(OpPassManager &&rhs) { |
339 | impl = std::move(rhs.impl); |
340 | return *this; |
341 | } |
342 | |
343 | OpPassManager::~OpPassManager() = default; |
344 | |
345 | OpPassManager::pass_iterator OpPassManager::begin() { |
346 | return MutableArrayRef<std::unique_ptr<Pass>>{impl->passes}.begin(); |
347 | } |
348 | OpPassManager::pass_iterator OpPassManager::end() { |
349 | return MutableArrayRef<std::unique_ptr<Pass>>{impl->passes}.end(); |
350 | } |
351 | |
352 | OpPassManager::const_pass_iterator OpPassManager::begin() const { |
353 | return ArrayRef<std::unique_ptr<Pass>>{impl->passes}.begin(); |
354 | } |
355 | OpPassManager::const_pass_iterator OpPassManager::end() const { |
356 | return ArrayRef<std::unique_ptr<Pass>>{impl->passes}.end(); |
357 | } |
358 | |
359 | /// Nest a new operation pass manager for the given operation kind under this |
360 | /// pass manager. |
361 | OpPassManager &OpPassManager::nest(OperationName nestedName) { |
362 | return impl->nest(nestedName); |
363 | } |
364 | OpPassManager &OpPassManager::nest(StringRef nestedName) { |
365 | return impl->nest(nestedName); |
366 | } |
367 | OpPassManager &OpPassManager::nestAny() { return impl->nestAny(); } |
368 | |
369 | /// Add the given pass to this pass manager. If this pass has a concrete |
370 | /// operation type, it must be the same type as this pass manager. |
371 | void OpPassManager::addPass(std::unique_ptr<Pass> pass) { |
372 | impl->addPass(pass: std::move(pass)); |
373 | } |
374 | |
375 | void OpPassManager::clear() { impl->clear(); } |
376 | |
377 | /// Returns the number of passes held by this manager. |
378 | size_t OpPassManager::size() const { return impl->passes.size(); } |
379 | |
380 | /// Returns the internal implementation instance. |
381 | OpPassManagerImpl &OpPassManager::getImpl() { return *impl; } |
382 | |
383 | /// Return the operation name that this pass manager operates on. |
384 | std::optional<StringRef> OpPassManager::getOpName() const { |
385 | return impl->getOpName(); |
386 | } |
387 | |
388 | /// Return the operation name that this pass manager operates on. |
389 | std::optional<OperationName> |
390 | OpPassManager::getOpName(MLIRContext &context) const { |
391 | return impl->getOpName(context); |
392 | } |
393 | |
394 | StringRef OpPassManager::getOpAnchorName() const { |
395 | return impl->getOpAnchorName(); |
396 | } |
397 | |
398 | /// Prints out the passes of the pass manager as the textual representation |
399 | /// of pipelines. When `pretty` is true, the printed pipeline is formatted for |
400 | /// readability. |
401 | void printAsTextualPipeline( |
402 | raw_indented_ostream &os, StringRef anchorName, |
403 | const llvm::iterator_range<OpPassManager::pass_iterator> &passes, |
404 | bool pretty = false) { |
405 | os << anchorName << "("; |
406 | if (pretty) { |
407 | os << "\n"; |
408 | os.indent(); |
409 | } |
410 | llvm::interleave( |
411 | c: passes, |
412 | each_fn: [&](mlir::Pass &pass) { pass.printAsTextualPipeline(os, pretty); }, |
413 | between_fn: [&]() { |
414 | os << ","; |
415 | if (pretty) |
416 | os << "\n"; |
417 | }); |
418 | if (pretty) { |
419 | os << "\n"; |
420 | os.unindent(); |
421 | } |
422 | os << ")"; |
423 | } |
424 | void printAsTextualPipeline( |
425 | raw_ostream &os, StringRef anchorName, |
426 | const llvm::iterator_range<OpPassManager::pass_iterator> &passes, |
427 | bool pretty) { |
428 | raw_indented_ostream indentedOS(os); |
429 | printAsTextualPipeline(os&: indentedOS, anchorName, passes, pretty); |
430 | } |
431 | void OpPassManager::printAsTextualPipeline(raw_ostream &os, bool pretty) const { |
432 | StringRef anchorName = getOpAnchorName(); |
433 | raw_indented_ostream indentedOS(os); |
434 | ::printAsTextualPipeline( |
435 | os&: indentedOS, anchorName, |
436 | passes: {MutableArrayRef<std::unique_ptr<Pass>>{impl->passes}.begin(), |
437 | MutableArrayRef<std::unique_ptr<Pass>>{impl->passes}.end()}, |
438 | pretty); |
439 | } |
440 | |
441 | void OpPassManager::dump() { |
442 | llvm::errs() << "Pass Manager with "<< impl->passes.size() << " passes:\n"; |
443 | printAsTextualPipeline(os&: llvm::errs(), /*pretty=*/true); |
444 | llvm::errs() << "\n"; |
445 | } |
446 | |
447 | static void registerDialectsForPipeline(const OpPassManager &pm, |
448 | DialectRegistry &dialects) { |
449 | for (const Pass &pass : pm.getPasses()) |
450 | pass.getDependentDialects(registry&: dialects); |
451 | } |
452 | |
453 | void OpPassManager::getDependentDialects(DialectRegistry &dialects) const { |
454 | registerDialectsForPipeline(pm: *this, dialects); |
455 | } |
456 | |
457 | void OpPassManager::setNesting(Nesting nesting) { impl->nesting = nesting; } |
458 | |
459 | OpPassManager::Nesting OpPassManager::getNesting() { return impl->nesting; } |
460 | |
461 | LogicalResult OpPassManager::initialize(MLIRContext *context, |
462 | unsigned newInitGeneration) { |
463 | if (impl->initializationGeneration == newInitGeneration) |
464 | return success(); |
465 | impl->initializationGeneration = newInitGeneration; |
466 | for (Pass &pass : getPasses()) { |
467 | // If this pass isn't an adaptor, directly initialize it. |
468 | auto *adaptor = dyn_cast<OpToOpPassAdaptor>(Val: &pass); |
469 | if (!adaptor) { |
470 | if (failed(Result: pass.initialize(context))) |
471 | return failure(); |
472 | continue; |
473 | } |
474 | |
475 | // Otherwise, initialize each of the adaptors pass managers. |
476 | for (OpPassManager &adaptorPM : adaptor->getPassManagers()) |
477 | if (failed(Result: adaptorPM.initialize(context, newInitGeneration))) |
478 | return failure(); |
479 | } |
480 | return success(); |
481 | } |
482 | |
483 | llvm::hash_code OpPassManager::hash() { |
484 | llvm::hash_code hashCode{}; |
485 | for (Pass &pass : getPasses()) { |
486 | // If this pass isn't an adaptor, directly hash it. |
487 | auto *adaptor = dyn_cast<OpToOpPassAdaptor>(Val: &pass); |
488 | if (!adaptor) { |
489 | hashCode = llvm::hash_combine(args: hashCode, args: &pass); |
490 | continue; |
491 | } |
492 | // Otherwise, hash recursively each of the adaptors pass managers. |
493 | for (OpPassManager &adaptorPM : adaptor->getPassManagers()) |
494 | llvm::hash_combine(args: hashCode, args: adaptorPM.hash()); |
495 | } |
496 | return hashCode; |
497 | } |
498 | |
499 | //===----------------------------------------------------------------------===// |
500 | // OpToOpPassAdaptor |
501 | //===----------------------------------------------------------------------===// |
502 | |
503 | LogicalResult OpToOpPassAdaptor::run(Pass *pass, Operation *op, |
504 | AnalysisManager am, bool verifyPasses, |
505 | unsigned parentInitGeneration) { |
506 | std::optional<RegisteredOperationName> opInfo = op->getRegisteredInfo(); |
507 | if (!opInfo) |
508 | return op->emitOpError() |
509 | << "trying to schedule a pass on an unregistered operation"; |
510 | if (!opInfo->hasTrait<OpTrait::IsIsolatedFromAbove>()) |
511 | return op->emitOpError() << "trying to schedule a pass on an operation not " |
512 | "marked as 'IsolatedFromAbove'"; |
513 | if (!pass->canScheduleOn(opName: *op->getName().getRegisteredInfo())) |
514 | return op->emitOpError() |
515 | << "trying to schedule a pass on an unsupported operation"; |
516 | |
517 | // Initialize the pass state with a callback for the pass to dynamically |
518 | // execute a pipeline on the currently visited operation. |
519 | PassInstrumentor *pi = am.getPassInstrumentor(); |
520 | PassInstrumentation::PipelineParentInfo parentInfo = {.parentThreadID: llvm::get_threadid(), |
521 | .parentPass: pass}; |
522 | auto dynamicPipelineCallback = [&](OpPassManager &pipeline, |
523 | Operation *root) -> LogicalResult { |
524 | if (!op->isAncestor(other: root)) |
525 | return root->emitOpError() |
526 | << "Trying to schedule a dynamic pipeline on an " |
527 | "operation that isn't " |
528 | "nested under the current operation the pass is processing"; |
529 | assert( |
530 | pipeline.getImpl().canScheduleOn(*op->getContext(), root->getName())); |
531 | |
532 | // Before running, finalize the passes held by the pipeline. |
533 | if (failed(Result: pipeline.getImpl().finalizePassList(ctx: root->getContext()))) |
534 | return failure(); |
535 | |
536 | // Initialize the user provided pipeline and execute the pipeline. |
537 | if (failed(Result: pipeline.initialize(context: root->getContext(), newInitGeneration: parentInitGeneration))) |
538 | return failure(); |
539 | AnalysisManager nestedAm = root == op ? am : am.nest(op: root); |
540 | return OpToOpPassAdaptor::runPipeline(pm&: pipeline, op: root, am: nestedAm, |
541 | verifyPasses, parentInitGeneration, |
542 | instrumentor: pi, parentInfo: &parentInfo); |
543 | }; |
544 | pass->passState.emplace(args&: op, args&: am, args&: dynamicPipelineCallback); |
545 | |
546 | // Instrument before the pass has run. |
547 | if (pi) |
548 | pi->runBeforePass(pass, op); |
549 | |
550 | bool passFailed = false; |
551 | op->getContext()->executeAction<PassExecutionAction>( |
552 | actionFn: [&]() { |
553 | // Invoke the virtual runOnOperation method. |
554 | if (auto *adaptor = dyn_cast<OpToOpPassAdaptor>(Val: pass)) |
555 | adaptor->runOnOperation(verifyPasses); |
556 | else |
557 | pass->runOnOperation(); |
558 | passFailed = pass->passState->irAndPassFailed.getInt(); |
559 | }, |
560 | irUnits: {op}, args&: *pass); |
561 | |
562 | // Invalidate any non preserved analyses. |
563 | am.invalidate(pa: pass->passState->preservedAnalyses); |
564 | |
565 | // When verifyPasses is specified, we run the verifier (unless the pass |
566 | // failed). |
567 | if (!passFailed && verifyPasses) { |
568 | bool runVerifierNow = true; |
569 | |
570 | // If the pass is an adaptor pass, we don't run the verifier recursively |
571 | // because the nested operations should have already been verified after |
572 | // nested passes had run. |
573 | bool runVerifierRecursively = !isa<OpToOpPassAdaptor>(Val: pass); |
574 | |
575 | // Reduce compile time by avoiding running the verifier if the pass didn't |
576 | // change the IR since the last time the verifier was run: |
577 | // |
578 | // 1) If the pass said that it preserved all analyses then it can't have |
579 | // permuted the IR. |
580 | // |
581 | // We run these checks in EXPENSIVE_CHECKS mode out of caution. |
582 | #ifndef EXPENSIVE_CHECKS |
583 | runVerifierNow = !pass->passState->preservedAnalyses.isAll(); |
584 | #endif |
585 | if (runVerifierNow) |
586 | passFailed = failed(Result: verify(op, verifyRecursively: runVerifierRecursively)); |
587 | } |
588 | |
589 | // Instrument after the pass has run. |
590 | if (pi) { |
591 | if (passFailed) |
592 | pi->runAfterPassFailed(pass, op); |
593 | else |
594 | pi->runAfterPass(pass, op); |
595 | } |
596 | |
597 | // Return if the pass signaled a failure. |
598 | return failure(IsFailure: passFailed); |
599 | } |
600 | |
601 | /// Run the given operation and analysis manager on a provided op pass manager. |
602 | LogicalResult OpToOpPassAdaptor::runPipeline( |
603 | OpPassManager &pm, Operation *op, AnalysisManager am, bool verifyPasses, |
604 | unsigned parentInitGeneration, PassInstrumentor *instrumentor, |
605 | const PassInstrumentation::PipelineParentInfo *parentInfo) { |
606 | assert((!instrumentor || parentInfo) && |
607 | "expected parent info if instrumentor is provided"); |
608 | auto scopeExit = llvm::make_scope_exit(F: [&] { |
609 | // Clear out any computed operation analyses. These analyses won't be used |
610 | // any more in this pipeline, and this helps reduce the current working set |
611 | // of memory. If preserving these analyses becomes important in the future |
612 | // we can re-evaluate this. |
613 | am.clear(); |
614 | }); |
615 | |
616 | // Run the pipeline over the provided operation. |
617 | if (instrumentor) { |
618 | instrumentor->runBeforePipeline(name: pm.getOpName(context&: *op->getContext()), |
619 | parentInfo: *parentInfo); |
620 | } |
621 | |
622 | for (Pass &pass : pm.getPasses()) |
623 | if (failed(Result: run(pass: &pass, op, am, verifyPasses, parentInitGeneration))) |
624 | return failure(); |
625 | |
626 | if (instrumentor) { |
627 | instrumentor->runAfterPipeline(name: pm.getOpName(context&: *op->getContext()), |
628 | parentInfo: *parentInfo); |
629 | } |
630 | return success(); |
631 | } |
632 | |
633 | /// Find an operation pass manager with the given anchor name, or nullptr if one |
634 | /// does not exist. |
635 | static OpPassManager * |
636 | findPassManagerWithAnchor(MutableArrayRef<OpPassManager> mgrs, StringRef name) { |
637 | auto *it = llvm::find_if( |
638 | Range&: mgrs, P: [&](OpPassManager &mgr) { return mgr.getOpAnchorName() == name; }); |
639 | return it == mgrs.end() ? nullptr : &*it; |
640 | } |
641 | |
642 | /// Find an operation pass manager that can operate on an operation of the given |
643 | /// type, or nullptr if one does not exist. |
644 | static OpPassManager *findPassManagerFor(MutableArrayRef<OpPassManager> mgrs, |
645 | OperationName name, |
646 | MLIRContext &context) { |
647 | auto *it = llvm::find_if(Range&: mgrs, P: [&](OpPassManager &mgr) { |
648 | return mgr.getImpl().canScheduleOn(context, opName: name); |
649 | }); |
650 | return it == mgrs.end() ? nullptr : &*it; |
651 | } |
652 | |
653 | OpToOpPassAdaptor::OpToOpPassAdaptor(OpPassManager &&mgr) { |
654 | mgrs.emplace_back(Args: std::move(mgr)); |
655 | } |
656 | |
657 | void OpToOpPassAdaptor::getDependentDialects(DialectRegistry &dialects) const { |
658 | for (auto &pm : mgrs) |
659 | pm.getDependentDialects(dialects); |
660 | } |
661 | |
662 | LogicalResult OpToOpPassAdaptor::tryMergeInto(MLIRContext *ctx, |
663 | OpToOpPassAdaptor &rhs) { |
664 | // Functor used to check if a pass manager is generic, i.e. op-agnostic. |
665 | auto isGenericPM = [&](OpPassManager &pm) { return !pm.getOpName(); }; |
666 | |
667 | // Functor used to detect if the given generic pass manager will have a |
668 | // potential schedule conflict with the given `otherPMs`. |
669 | auto hasScheduleConflictWith = [&](OpPassManager &genericPM, |
670 | MutableArrayRef<OpPassManager> otherPMs) { |
671 | return llvm::any_of(Range&: otherPMs, P: [&](OpPassManager &pm) { |
672 | // If this is a non-generic pass manager, a conflict will arise if a |
673 | // non-generic pass manager's operation name can be scheduled on the |
674 | // generic passmanager. |
675 | if (std::optional<OperationName> pmOpName = pm.getOpName(context&: *ctx)) |
676 | return genericPM.getImpl().canScheduleOn(context&: *ctx, opName: *pmOpName); |
677 | // Otherwise, this is a generic pass manager. We current can't determine |
678 | // when generic pass managers can be merged, so conservatively assume they |
679 | // conflict. |
680 | return true; |
681 | }); |
682 | }; |
683 | |
684 | // Check that if either adaptor has a generic pass manager, that pm is |
685 | // compatible within any non-generic pass managers. |
686 | // |
687 | // Check the current adaptor. |
688 | auto *lhsGenericPMIt = llvm::find_if(Range&: mgrs, P: isGenericPM); |
689 | if (lhsGenericPMIt != mgrs.end() && |
690 | hasScheduleConflictWith(*lhsGenericPMIt, rhs.mgrs)) |
691 | return failure(); |
692 | // Check the rhs adaptor. |
693 | auto *rhsGenericPMIt = llvm::find_if(Range&: rhs.mgrs, P: isGenericPM); |
694 | if (rhsGenericPMIt != rhs.mgrs.end() && |
695 | hasScheduleConflictWith(*rhsGenericPMIt, mgrs)) |
696 | return failure(); |
697 | |
698 | for (auto &pm : mgrs) { |
699 | // If an existing pass manager exists, then merge the given pass manager |
700 | // into it. |
701 | if (auto *existingPM = |
702 | findPassManagerWithAnchor(mgrs: rhs.mgrs, name: pm.getOpAnchorName())) { |
703 | pm.getImpl().mergeInto(rhs&: existingPM->getImpl()); |
704 | } else { |
705 | // Otherwise, add the given pass manager to the list. |
706 | rhs.mgrs.emplace_back(Args: std::move(pm)); |
707 | } |
708 | } |
709 | mgrs.clear(); |
710 | |
711 | // After coalescing, sort the pass managers within rhs by name. |
712 | auto compareFn = [](const OpPassManager &lhs, const OpPassManager &rhs) { |
713 | // Order op-specific pass managers first and op-agnostic pass managers last. |
714 | if (std::optional<StringRef> lhsName = lhs.getOpName()) { |
715 | if (std::optional<StringRef> rhsName = rhs.getOpName()) |
716 | return *lhsName < *rhsName; |
717 | return true; // lhs(op-specific) < rhs(op-agnostic) |
718 | } |
719 | return false; // lhs(op-agnostic) > rhs(op-specific) |
720 | }; |
721 | llvm::sort(C&: rhs.mgrs, Comp: compareFn); |
722 | return success(); |
723 | } |
724 | |
725 | /// Returns the adaptor pass name. |
726 | std::string OpToOpPassAdaptor::getAdaptorName() { |
727 | std::string name = "Pipeline Collection : ["; |
728 | llvm::raw_string_ostream os(name); |
729 | llvm::interleaveComma(c: getPassManagers(), os, each_fn: [&](OpPassManager &pm) { |
730 | os << '\'' << pm.getOpAnchorName() << '\''; |
731 | }); |
732 | os << ']'; |
733 | return name; |
734 | } |
735 | |
736 | void OpToOpPassAdaptor::runOnOperation() { |
737 | llvm_unreachable( |
738 | "Unexpected call to Pass::runOnOperation() on OpToOpPassAdaptor"); |
739 | } |
740 | |
741 | /// Run the held pipeline over all nested operations. |
742 | void OpToOpPassAdaptor::runOnOperation(bool verifyPasses) { |
743 | if (getContext().isMultithreadingEnabled()) |
744 | runOnOperationAsyncImpl(verifyPasses); |
745 | else |
746 | runOnOperationImpl(verifyPasses); |
747 | } |
748 | |
749 | /// Run this pass adaptor synchronously. |
750 | void OpToOpPassAdaptor::runOnOperationImpl(bool verifyPasses) { |
751 | auto am = getAnalysisManager(); |
752 | PassInstrumentation::PipelineParentInfo parentInfo = {.parentThreadID: llvm::get_threadid(), |
753 | .parentPass: this}; |
754 | auto *instrumentor = am.getPassInstrumentor(); |
755 | for (auto ®ion : getOperation()->getRegions()) { |
756 | for (auto &block : region) { |
757 | for (auto &op : block) { |
758 | auto *mgr = findPassManagerFor(mgrs, name: op.getName(), context&: *op.getContext()); |
759 | if (!mgr) |
760 | continue; |
761 | |
762 | // Run the held pipeline over the current operation. |
763 | unsigned initGeneration = mgr->impl->initializationGeneration; |
764 | if (failed(Result: runPipeline(pm&: *mgr, op: &op, am: am.nest(op: &op), verifyPasses, |
765 | parentInitGeneration: initGeneration, instrumentor, parentInfo: &parentInfo))) |
766 | signalPassFailure(); |
767 | } |
768 | } |
769 | } |
770 | } |
771 | |
772 | /// Utility functor that checks if the two ranges of pass managers have a size |
773 | /// mismatch. |
774 | static bool hasSizeMismatch(ArrayRef<OpPassManager> lhs, |
775 | ArrayRef<OpPassManager> rhs) { |
776 | return lhs.size() != rhs.size() || |
777 | llvm::any_of(Range: llvm::seq<size_t>(Begin: 0, End: lhs.size()), |
778 | P: [&](size_t i) { return lhs[i].size() != rhs[i].size(); }); |
779 | } |
780 | |
781 | /// Run this pass adaptor synchronously. |
782 | void OpToOpPassAdaptor::runOnOperationAsyncImpl(bool verifyPasses) { |
783 | AnalysisManager am = getAnalysisManager(); |
784 | MLIRContext *context = &getContext(); |
785 | |
786 | // Create the async executors if they haven't been created, or if the main |
787 | // pipeline has changed. |
788 | if (asyncExecutors.empty() || hasSizeMismatch(lhs: asyncExecutors.front(), rhs: mgrs)) |
789 | asyncExecutors.assign(NumElts: context->getThreadPool().getMaxConcurrency(), Elt: mgrs); |
790 | |
791 | // This struct represents the information for a single operation to be |
792 | // scheduled on a pass manager. |
793 | struct OpPMInfo { |
794 | OpPMInfo(unsigned passManagerIdx, Operation *op, AnalysisManager am) |
795 | : passManagerIdx(passManagerIdx), op(op), am(am) {} |
796 | |
797 | /// The index of the pass manager to schedule the operation on. |
798 | unsigned passManagerIdx; |
799 | /// The operation to schedule. |
800 | Operation *op; |
801 | /// The analysis manager for the operation. |
802 | AnalysisManager am; |
803 | }; |
804 | |
805 | // Run a prepass over the operation to collect the nested operations to |
806 | // execute over. This ensures that an analysis manager exists for each |
807 | // operation, as well as providing a queue of operations to execute over. |
808 | std::vector<OpPMInfo> opInfos; |
809 | DenseMap<OperationName, std::optional<unsigned>> knownOpPMIdx; |
810 | for (auto ®ion : getOperation()->getRegions()) { |
811 | for (Operation &op : region.getOps()) { |
812 | // Get the pass manager index for this operation type. |
813 | auto pmIdxIt = knownOpPMIdx.try_emplace(Key: op.getName(), Args: std::nullopt); |
814 | if (pmIdxIt.second) { |
815 | if (auto *mgr = findPassManagerFor(mgrs, name: op.getName(), context&: *context)) |
816 | pmIdxIt.first->second = std::distance(first: mgrs.begin(), last: mgr); |
817 | } |
818 | |
819 | // If this operation can be scheduled, add it to the list. |
820 | if (pmIdxIt.first->second) |
821 | opInfos.emplace_back(args&: *pmIdxIt.first->second, args: &op, args: am.nest(op: &op)); |
822 | } |
823 | } |
824 | |
825 | // Get the current thread for this adaptor. |
826 | PassInstrumentation::PipelineParentInfo parentInfo = {.parentThreadID: llvm::get_threadid(), |
827 | .parentPass: this}; |
828 | auto *instrumentor = am.getPassInstrumentor(); |
829 | |
830 | // An atomic failure variable for the async executors. |
831 | std::vector<std::atomic<bool>> activePMs(asyncExecutors.size()); |
832 | std::fill(first: activePMs.begin(), last: activePMs.end(), value: false); |
833 | std::atomic<bool> hasFailure = false; |
834 | parallelForEach(context, range&: opInfos, func: [&](OpPMInfo &opInfo) { |
835 | // Find an executor for this operation. |
836 | auto it = llvm::find_if(Range&: activePMs, P: [](std::atomic<bool> &isActive) { |
837 | bool expectedInactive = false; |
838 | return isActive.compare_exchange_strong(i1&: expectedInactive, i2: true); |
839 | }); |
840 | unsigned pmIndex = it - activePMs.begin(); |
841 | |
842 | // Get the pass manager for this operation and execute it. |
843 | OpPassManager &pm = asyncExecutors[pmIndex][opInfo.passManagerIdx]; |
844 | LogicalResult pipelineResult = runPipeline( |
845 | pm, op: opInfo.op, am: opInfo.am, verifyPasses, |
846 | parentInitGeneration: pm.impl->initializationGeneration, instrumentor, parentInfo: &parentInfo); |
847 | if (failed(Result: pipelineResult)) |
848 | hasFailure.store(i: true); |
849 | |
850 | // Reset the active bit for this pass manager. |
851 | activePMs[pmIndex].store(i: false); |
852 | }); |
853 | |
854 | // Signal a failure if any of the executors failed. |
855 | if (hasFailure) |
856 | signalPassFailure(); |
857 | } |
858 | |
859 | //===----------------------------------------------------------------------===// |
860 | // PassManager |
861 | //===----------------------------------------------------------------------===// |
862 | |
863 | PassManager::PassManager(MLIRContext *ctx, StringRef operationName, |
864 | Nesting nesting) |
865 | : OpPassManager(operationName, nesting), context(ctx), passTiming(false), |
866 | verifyPasses(true) {} |
867 | |
868 | PassManager::PassManager(OperationName operationName, Nesting nesting) |
869 | : OpPassManager(operationName, nesting), |
870 | context(operationName.getContext()), passTiming(false), |
871 | verifyPasses(true) {} |
872 | |
873 | PassManager::~PassManager() = default; |
874 | |
875 | void PassManager::enableVerifier(bool enabled) { verifyPasses = enabled; } |
876 | |
877 | /// Run the passes within this manager on the provided operation. |
878 | LogicalResult PassManager::run(Operation *op) { |
879 | MLIRContext *context = getContext(); |
880 | std::optional<OperationName> anchorOp = getOpName(context&: *context); |
881 | if (anchorOp && anchorOp != op->getName()) |
882 | return emitError(loc: op->getLoc()) |
883 | << "can't run '"<< getOpAnchorName() << "' pass manager on '" |
884 | << op->getName() << "' op"; |
885 | |
886 | // Register all dialects for the current pipeline. |
887 | DialectRegistry dependentDialects; |
888 | getDependentDialects(dialects&: dependentDialects); |
889 | context->appendDialectRegistry(registry: dependentDialects); |
890 | for (StringRef name : dependentDialects.getDialectNames()) |
891 | context->getOrLoadDialect(name); |
892 | |
893 | // Before running, make sure to finalize the pipeline pass list. |
894 | if (failed(Result: getImpl().finalizePassList(ctx: context))) |
895 | return failure(); |
896 | |
897 | // Notify the context that we start running a pipeline for bookkeeping. |
898 | context->enterMultiThreadedExecution(); |
899 | |
900 | // Initialize all of the passes within the pass manager with a new generation. |
901 | llvm::hash_code newInitKey = context->getRegistryHash(); |
902 | llvm::hash_code pipelineKey = hash(); |
903 | if (newInitKey != initializationKey || |
904 | pipelineKey != pipelineInitializationKey) { |
905 | if (failed(Result: initialize(context, newInitGeneration: impl->initializationGeneration + 1))) |
906 | return failure(); |
907 | initializationKey = newInitKey; |
908 | pipelineKey = pipelineInitializationKey; |
909 | } |
910 | |
911 | // Construct a top level analysis manager for the pipeline. |
912 | ModuleAnalysisManager am(op, instrumentor.get()); |
913 | |
914 | // If reproducer generation is enabled, run the pass manager with crash |
915 | // handling enabled. |
916 | LogicalResult result = |
917 | crashReproGenerator ? runWithCrashRecovery(op, am) : runPasses(op, am); |
918 | |
919 | // Notify the context that the run is done. |
920 | context->exitMultiThreadedExecution(); |
921 | |
922 | // Dump all of the pass statistics if necessary. |
923 | if (passStatisticsMode) |
924 | dumpStatistics(); |
925 | return result; |
926 | } |
927 | |
928 | /// Add the provided instrumentation to the pass manager. |
929 | void PassManager::addInstrumentation(std::unique_ptr<PassInstrumentation> pi) { |
930 | if (!instrumentor) |
931 | instrumentor = std::make_unique<PassInstrumentor>(); |
932 | |
933 | instrumentor->addInstrumentation(pi: std::move(pi)); |
934 | } |
935 | |
936 | LogicalResult PassManager::runPasses(Operation *op, AnalysisManager am) { |
937 | return OpToOpPassAdaptor::runPipeline(pm&: *this, op, am, verifyPasses, |
938 | parentInitGeneration: impl->initializationGeneration); |
939 | } |
940 | |
941 | //===----------------------------------------------------------------------===// |
942 | // AnalysisManager |
943 | //===----------------------------------------------------------------------===// |
944 | |
945 | /// Get an analysis manager for the given operation, which must be a proper |
946 | /// descendant of the current operation represented by this analysis manager. |
947 | AnalysisManager AnalysisManager::nest(Operation *op) { |
948 | Operation *currentOp = impl->getOperation(); |
949 | assert(currentOp->isProperAncestor(op) && |
950 | "expected valid descendant operation"); |
951 | |
952 | // Check for the base case where the provided operation is immediately nested. |
953 | if (currentOp == op->getParentOp()) |
954 | return nestImmediate(op); |
955 | |
956 | // Otherwise, we need to collect all ancestors up to the current operation. |
957 | SmallVector<Operation *, 4> opAncestors; |
958 | do { |
959 | opAncestors.push_back(Elt: op); |
960 | op = op->getParentOp(); |
961 | } while (op != currentOp); |
962 | |
963 | AnalysisManager result = *this; |
964 | for (Operation *op : llvm::reverse(C&: opAncestors)) |
965 | result = result.nestImmediate(op); |
966 | return result; |
967 | } |
968 | |
969 | /// Get an analysis manager for the given immediately nested child operation. |
970 | AnalysisManager AnalysisManager::nestImmediate(Operation *op) { |
971 | assert(impl->getOperation() == op->getParentOp() && |
972 | "expected immediate child operation"); |
973 | |
974 | auto [it, inserted] = impl->childAnalyses.try_emplace(Key: op); |
975 | if (inserted) |
976 | it->second = std::make_unique<NestedAnalysisMap>(args&: op, args&: impl); |
977 | return {it->second.get()}; |
978 | } |
979 | |
980 | /// Invalidate any non preserved analyses. |
981 | void detail::NestedAnalysisMap::invalidate( |
982 | const detail::PreservedAnalyses &pa) { |
983 | // If all analyses were preserved, then there is nothing to do here. |
984 | if (pa.isAll()) |
985 | return; |
986 | |
987 | // Invalidate the analyses for the current operation directly. |
988 | analyses.invalidate(pa); |
989 | |
990 | // If no analyses were preserved, then just simply clear out the child |
991 | // analysis results. |
992 | if (pa.isNone()) { |
993 | childAnalyses.clear(); |
994 | return; |
995 | } |
996 | |
997 | // Otherwise, invalidate each child analysis map. |
998 | SmallVector<NestedAnalysisMap *, 8> mapsToInvalidate(1, this); |
999 | while (!mapsToInvalidate.empty()) { |
1000 | auto *map = mapsToInvalidate.pop_back_val(); |
1001 | for (auto &analysisPair : map->childAnalyses) { |
1002 | analysisPair.second->invalidate(pa); |
1003 | if (!analysisPair.second->childAnalyses.empty()) |
1004 | mapsToInvalidate.push_back(Elt: analysisPair.second.get()); |
1005 | } |
1006 | } |
1007 | } |
1008 | |
1009 | //===----------------------------------------------------------------------===// |
1010 | // PassInstrumentation |
1011 | //===----------------------------------------------------------------------===// |
1012 | |
1013 | PassInstrumentation::~PassInstrumentation() = default; |
1014 | |
1015 | void PassInstrumentation::runBeforePipeline( |
1016 | std::optional<OperationName> name, const PipelineParentInfo &parentInfo) {} |
1017 | |
1018 | void PassInstrumentation::runAfterPipeline( |
1019 | std::optional<OperationName> name, const PipelineParentInfo &parentInfo) {} |
1020 | |
1021 | //===----------------------------------------------------------------------===// |
1022 | // PassInstrumentor |
1023 | //===----------------------------------------------------------------------===// |
1024 | |
1025 | namespace mlir { |
1026 | namespace detail { |
1027 | struct PassInstrumentorImpl { |
1028 | /// Mutex to keep instrumentation access thread-safe. |
1029 | llvm::sys::SmartMutex<true> mutex; |
1030 | |
1031 | /// Set of registered instrumentations. |
1032 | std::vector<std::unique_ptr<PassInstrumentation>> instrumentations; |
1033 | }; |
1034 | } // namespace detail |
1035 | } // namespace mlir |
1036 | |
1037 | PassInstrumentor::PassInstrumentor() : impl(new PassInstrumentorImpl()) {} |
1038 | PassInstrumentor::~PassInstrumentor() = default; |
1039 | |
1040 | /// See PassInstrumentation::runBeforePipeline for details. |
1041 | void PassInstrumentor::runBeforePipeline( |
1042 | std::optional<OperationName> name, |
1043 | const PassInstrumentation::PipelineParentInfo &parentInfo) { |
1044 | llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex); |
1045 | for (auto &instr : impl->instrumentations) |
1046 | instr->runBeforePipeline(name, parentInfo); |
1047 | } |
1048 | |
1049 | /// See PassInstrumentation::runAfterPipeline for details. |
1050 | void PassInstrumentor::runAfterPipeline( |
1051 | std::optional<OperationName> name, |
1052 | const PassInstrumentation::PipelineParentInfo &parentInfo) { |
1053 | llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex); |
1054 | for (auto &instr : llvm::reverse(C&: impl->instrumentations)) |
1055 | instr->runAfterPipeline(name, parentInfo); |
1056 | } |
1057 | |
1058 | /// See PassInstrumentation::runBeforePass for details. |
1059 | void PassInstrumentor::runBeforePass(Pass *pass, Operation *op) { |
1060 | llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex); |
1061 | for (auto &instr : impl->instrumentations) |
1062 | instr->runBeforePass(pass, op); |
1063 | } |
1064 | |
1065 | /// See PassInstrumentation::runAfterPass for details. |
1066 | void PassInstrumentor::runAfterPass(Pass *pass, Operation *op) { |
1067 | llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex); |
1068 | for (auto &instr : llvm::reverse(C&: impl->instrumentations)) |
1069 | instr->runAfterPass(pass, op); |
1070 | } |
1071 | |
1072 | /// See PassInstrumentation::runAfterPassFailed for details. |
1073 | void PassInstrumentor::runAfterPassFailed(Pass *pass, Operation *op) { |
1074 | llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex); |
1075 | for (auto &instr : llvm::reverse(C&: impl->instrumentations)) |
1076 | instr->runAfterPassFailed(pass, op); |
1077 | } |
1078 | |
1079 | /// See PassInstrumentation::runBeforeAnalysis for details. |
1080 | void PassInstrumentor::runBeforeAnalysis(StringRef name, TypeID id, |
1081 | Operation *op) { |
1082 | llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex); |
1083 | for (auto &instr : impl->instrumentations) |
1084 | instr->runBeforeAnalysis(name, id, op); |
1085 | } |
1086 | |
1087 | /// See PassInstrumentation::runAfterAnalysis for details. |
1088 | void PassInstrumentor::runAfterAnalysis(StringRef name, TypeID id, |
1089 | Operation *op) { |
1090 | llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex); |
1091 | for (auto &instr : llvm::reverse(C&: impl->instrumentations)) |
1092 | instr->runAfterAnalysis(name, id, op); |
1093 | } |
1094 | |
1095 | /// Add the given instrumentation to the collection. |
1096 | void PassInstrumentor::addInstrumentation( |
1097 | std::unique_ptr<PassInstrumentation> pi) { |
1098 | llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex); |
1099 | impl->instrumentations.emplace_back(args: std::move(pi)); |
1100 | } |
1101 |
Definitions
- PassExecutionAction
- getOp
- anchor
- initializeOptions
- copyOptionValuesFrom
- printAsTextualPipeline
- OpPassManagerImpl
- OpPassManagerImpl
- OpPassManagerImpl
- OpPassManagerImpl
- OpPassManagerImpl
- nest
- nest
- nestAny
- getOpName
- getOpName
- getOpAnchorName
- mergeInto
- nest
- addPass
- clear
- finalizePassList
- canScheduleOn
- OpPassManager
- OpPassManager
- OpPassManager
- OpPassManager
- OpPassManager
- operator=
- operator=
- ~OpPassManager
- begin
- end
- begin
- end
- nest
- nest
- nestAny
- addPass
- clear
- size
- getImpl
- getOpName
- getOpName
- getOpAnchorName
- printAsTextualPipeline
- printAsTextualPipeline
- printAsTextualPipeline
- dump
- registerDialectsForPipeline
- getDependentDialects
- setNesting
- getNesting
- initialize
- hash
- run
- runPipeline
- findPassManagerWithAnchor
- findPassManagerFor
- OpToOpPassAdaptor
- getDependentDialects
- tryMergeInto
- getAdaptorName
- runOnOperation
- runOnOperation
- runOnOperationImpl
- hasSizeMismatch
- runOnOperationAsyncImpl
- PassManager
- PassManager
- ~PassManager
- enableVerifier
- run
- addInstrumentation
- runPasses
- nest
- nestImmediate
- invalidate
- ~PassInstrumentation
- runBeforePipeline
- runAfterPipeline
- PassInstrumentorImpl
- PassInstrumentor
- ~PassInstrumentor
- runBeforePipeline
- runAfterPipeline
- runBeforePass
- runAfterPass
- runAfterPassFailed
- runBeforeAnalysis
- runAfterAnalysis
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more