| //===- IRPrinting.cpp -----------------------------------------------------===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "PassDetail.h" | 
 | #include "mlir/IR/SymbolTable.h" | 
 | #include "mlir/Pass/PassManager.h" | 
 | #include "llvm/Support/Format.h" | 
 | #include "llvm/Support/FormatVariadic.h" | 
 |  | 
 | using namespace mlir; | 
 | using namespace mlir::detail; | 
 |  | 
 | namespace { | 
 | //===----------------------------------------------------------------------===// | 
 | // IRPrinter | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | class IRPrinterInstrumentation : public PassInstrumentation { | 
 | public: | 
 |   IRPrinterInstrumentation(std::unique_ptr<PassManager::IRPrinterConfig> config) | 
 |       : config(std::move(config)) {} | 
 |  | 
 | private: | 
 |   /// Instrumentation hooks. | 
 |   void runBeforePass(Pass *pass, Operation *op) override; | 
 |   void runAfterPass(Pass *pass, Operation *op) override; | 
 |   void runAfterPassFailed(Pass *pass, Operation *op) override; | 
 |  | 
 |   /// Configuration to use. | 
 |   std::unique_ptr<PassManager::IRPrinterConfig> config; | 
 |  | 
 |   /// The following is a set of fingerprints for operations that are currently | 
 |   /// being operated on in a pass. This field is only used when the | 
 |   /// configuration asked for change detection. | 
 |   DenseMap<Pass *, OperationFingerPrint> beforePassFingerPrints; | 
 | }; | 
 | } // namespace | 
 |  | 
 | static void printIR(Operation *op, bool printModuleScope, raw_ostream &out, | 
 |                     OpPrintingFlags flags) { | 
 |   // Otherwise, check to see if we are not printing at module scope. | 
 |   if (!printModuleScope) | 
 |     return op->print(out << " //----- //\n", | 
 |                      op->getBlock() ? flags.useLocalScope() : flags); | 
 |  | 
 |   // Otherwise, we are printing at module scope. | 
 |   out << " ('" << op->getName() << "' operation"; | 
 |   if (auto symbolName = | 
 |           op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName())) | 
 |     out << ": @" << symbolName.getValue(); | 
 |   out << ") //----- //\n"; | 
 |  | 
 |   // Find the top-level operation. | 
 |   auto *topLevelOp = op; | 
 |   while (auto *parentOp = topLevelOp->getParentOp()) | 
 |     topLevelOp = parentOp; | 
 |   topLevelOp->print(out, flags); | 
 | } | 
 |  | 
 | /// Instrumentation hooks. | 
 | void IRPrinterInstrumentation::runBeforePass(Pass *pass, Operation *op) { | 
 |   if (isa<OpToOpPassAdaptor>(pass)) | 
 |     return; | 
 |   // If the config asked to detect changes, record the current fingerprint. | 
 |   if (config->shouldPrintAfterOnlyOnChange()) | 
 |     beforePassFingerPrints.try_emplace(pass, op); | 
 |  | 
 |   config->printBeforeIfEnabled(pass, op, [&](raw_ostream &out) { | 
 |     out << "// -----// IR Dump Before " << pass->getName() << " (" | 
 |         << pass->getArgument() << ")"; | 
 |     printIR(op, config->shouldPrintAtModuleScope(), out, | 
 |             config->getOpPrintingFlags()); | 
 |     out << "\n\n"; | 
 |   }); | 
 | } | 
 |  | 
 | void IRPrinterInstrumentation::runAfterPass(Pass *pass, Operation *op) { | 
 |   if (isa<OpToOpPassAdaptor>(pass)) | 
 |     return; | 
 |  | 
 |   // Check to see if we are only printing on failure. | 
 |   if (config->shouldPrintAfterOnlyOnFailure()) | 
 |     return; | 
 |  | 
 |   // If the config asked to detect changes, compare the current fingerprint with | 
 |   // the previous. | 
 |   if (config->shouldPrintAfterOnlyOnChange()) { | 
 |     auto fingerPrintIt = beforePassFingerPrints.find(pass); | 
 |     assert(fingerPrintIt != beforePassFingerPrints.end() && | 
 |            "expected valid fingerprint"); | 
 |     // If the fingerprints are the same, we don't print the IR. | 
 |     if (fingerPrintIt->second == OperationFingerPrint(op)) { | 
 |       beforePassFingerPrints.erase(fingerPrintIt); | 
 |       return; | 
 |     } | 
 |     beforePassFingerPrints.erase(fingerPrintIt); | 
 |   } | 
 |  | 
 |   config->printAfterIfEnabled(pass, op, [&](raw_ostream &out) { | 
 |     out << "// -----// IR Dump After " << pass->getName() << " (" | 
 |         << pass->getArgument() << ")"; | 
 |     printIR(op, config->shouldPrintAtModuleScope(), out, | 
 |             config->getOpPrintingFlags()); | 
 |     out << "\n\n"; | 
 |   }); | 
 | } | 
 |  | 
 | void IRPrinterInstrumentation::runAfterPassFailed(Pass *pass, Operation *op) { | 
 |   if (isa<OpToOpPassAdaptor>(pass)) | 
 |     return; | 
 |   if (config->shouldPrintAfterOnlyOnChange()) | 
 |     beforePassFingerPrints.erase(pass); | 
 |  | 
 |   config->printAfterIfEnabled(pass, op, [&](raw_ostream &out) { | 
 |     out << formatv("// -----// IR Dump After {0} Failed ({1})", pass->getName(), | 
 |                    pass->getArgument()); | 
 |     printIR(op, config->shouldPrintAtModuleScope(), out, OpPrintingFlags()); | 
 |     out << "\n\n"; | 
 |   }); | 
 | } | 
 |  | 
 | //===----------------------------------------------------------------------===// | 
 | // IRPrinterConfig | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | /// Initialize the configuration. | 
 | PassManager::IRPrinterConfig::IRPrinterConfig(bool printModuleScope, | 
 |                                               bool printAfterOnlyOnChange, | 
 |                                               bool printAfterOnlyOnFailure, | 
 |                                               OpPrintingFlags opPrintingFlags) | 
 |     : printModuleScope(printModuleScope), | 
 |       printAfterOnlyOnChange(printAfterOnlyOnChange), | 
 |       printAfterOnlyOnFailure(printAfterOnlyOnFailure), | 
 |       opPrintingFlags(opPrintingFlags) {} | 
 | PassManager::IRPrinterConfig::~IRPrinterConfig() = default; | 
 |  | 
 | /// A hook that may be overridden by a derived config that checks if the IR | 
 | /// of 'operation' should be dumped *before* the pass 'pass' has been | 
 | /// executed. If the IR should be dumped, 'printCallback' should be invoked | 
 | /// with the stream to dump into. | 
 | void PassManager::IRPrinterConfig::printBeforeIfEnabled( | 
 |     Pass *pass, Operation *operation, PrintCallbackFn printCallback) { | 
 |   // By default, never print. | 
 | } | 
 |  | 
 | /// A hook that may be overridden by a derived config that checks if the IR | 
 | /// of 'operation' should be dumped *after* the pass 'pass' has been | 
 | /// executed. If the IR should be dumped, 'printCallback' should be invoked | 
 | /// with the stream to dump into. | 
 | void PassManager::IRPrinterConfig::printAfterIfEnabled( | 
 |     Pass *pass, Operation *operation, PrintCallbackFn printCallback) { | 
 |   // By default, never print. | 
 | } | 
 |  | 
 | //===----------------------------------------------------------------------===// | 
 | // PassManager | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | namespace { | 
 | /// Simple wrapper config that allows for the simpler interface defined above. | 
 | struct BasicIRPrinterConfig : public PassManager::IRPrinterConfig { | 
 |   BasicIRPrinterConfig( | 
 |       std::function<bool(Pass *, Operation *)> shouldPrintBeforePass, | 
 |       std::function<bool(Pass *, Operation *)> shouldPrintAfterPass, | 
 |       bool printModuleScope, bool printAfterOnlyOnChange, | 
 |       bool printAfterOnlyOnFailure, OpPrintingFlags opPrintingFlags, | 
 |       raw_ostream &out) | 
 |       : IRPrinterConfig(printModuleScope, printAfterOnlyOnChange, | 
 |                         printAfterOnlyOnFailure, opPrintingFlags), | 
 |         shouldPrintBeforePass(std::move(shouldPrintBeforePass)), | 
 |         shouldPrintAfterPass(std::move(shouldPrintAfterPass)), out(out) { | 
 |     assert((this->shouldPrintBeforePass || this->shouldPrintAfterPass) && | 
 |            "expected at least one valid filter function"); | 
 |   } | 
 |  | 
 |   void printBeforeIfEnabled(Pass *pass, Operation *operation, | 
 |                             PrintCallbackFn printCallback) final { | 
 |     if (shouldPrintBeforePass && shouldPrintBeforePass(pass, operation)) | 
 |       printCallback(out); | 
 |   } | 
 |  | 
 |   void printAfterIfEnabled(Pass *pass, Operation *operation, | 
 |                            PrintCallbackFn printCallback) final { | 
 |     if (shouldPrintAfterPass && shouldPrintAfterPass(pass, operation)) | 
 |       printCallback(out); | 
 |   } | 
 |  | 
 |   /// Filter functions for before and after pass execution. | 
 |   std::function<bool(Pass *, Operation *)> shouldPrintBeforePass; | 
 |   std::function<bool(Pass *, Operation *)> shouldPrintAfterPass; | 
 |  | 
 |   /// The stream to output to. | 
 |   raw_ostream &out; | 
 | }; | 
 | } // namespace | 
 |  | 
 | /// Add an instrumentation to print the IR before and after pass execution, | 
 | /// using the provided configuration. | 
 | void PassManager::enableIRPrinting(std::unique_ptr<IRPrinterConfig> config) { | 
 |   if (config->shouldPrintAtModuleScope() && | 
 |       getContext()->isMultithreadingEnabled()) | 
 |     llvm::report_fatal_error("IR printing can't be setup on a pass-manager " | 
 |                              "without disabling multi-threading first."); | 
 |   addInstrumentation( | 
 |       std::make_unique<IRPrinterInstrumentation>(std::move(config))); | 
 | } | 
 |  | 
 | /// Add an instrumentation to print the IR before and after pass execution. | 
 | void PassManager::enableIRPrinting( | 
 |     std::function<bool(Pass *, Operation *)> shouldPrintBeforePass, | 
 |     std::function<bool(Pass *, Operation *)> shouldPrintAfterPass, | 
 |     bool printModuleScope, bool printAfterOnlyOnChange, | 
 |     bool printAfterOnlyOnFailure, raw_ostream &out, | 
 |     OpPrintingFlags opPrintingFlags) { | 
 |   enableIRPrinting(std::make_unique<BasicIRPrinterConfig>( | 
 |       std::move(shouldPrintBeforePass), std::move(shouldPrintAfterPass), | 
 |       printModuleScope, printAfterOnlyOnChange, printAfterOnlyOnFailure, | 
 |       opPrintingFlags, out)); | 
 | } |