#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 {
class IRPrinterInstrumentation : public PassInstrumentation {
public:
IRPrinterInstrumentation(std::unique_ptr<PassManager::IRPrinterConfig> config)
: config(std::move(config)) {}
private:
void runBeforePass(Pass *pass, Operation *op) override;
void runAfterPass(Pass *pass, Operation *op) override;
void runAfterPassFailed(Pass *pass, Operation *op) override;
std::unique_ptr<PassManager::IRPrinterConfig> config;
DenseMap<Pass *, OperationFingerPrint> beforePassFingerPrints;
};
}
static void printIR(Operation *op, bool printModuleScope, raw_ostream &out,
OpPrintingFlags flags) {
if (!printModuleScope)
return op->print(out << " //----- //\n",
op->getBlock() ? flags.useLocalScope() : flags);
out << " ('" << op->getName() << "' operation";
if (auto symbolName =
op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()))
out << ": @" << symbolName.getValue();
out << ") //----- //\n";
auto *topLevelOp = op;
while (auto *parentOp = topLevelOp->getParentOp())
topLevelOp = parentOp;
topLevelOp->print(out, flags);
}
void IRPrinterInstrumentation::runBeforePass(Pass *pass, Operation *op) {
if (isa<OpToOpPassAdaptor>(pass))
return;
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;
if (config->shouldPrintAfterOnlyOnFailure())
return;
if (config->shouldPrintAfterOnlyOnChange()) {
auto fingerPrintIt = beforePassFingerPrints.find(pass);
assert(fingerPrintIt != beforePassFingerPrints.end() &&
"expected valid fingerprint");
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";
});
}
PassManager::IRPrinterConfig::IRPrinterConfig(bool printModuleScope,
bool printAfterOnlyOnChange,
bool printAfterOnlyOnFailure,
OpPrintingFlags opPrintingFlags)
: printModuleScope(printModuleScope),
printAfterOnlyOnChange(printAfterOnlyOnChange),
printAfterOnlyOnFailure(printAfterOnlyOnFailure),
opPrintingFlags(opPrintingFlags) {}
PassManager::IRPrinterConfig::~IRPrinterConfig() = default;
void PassManager::IRPrinterConfig::printBeforeIfEnabled(
Pass *pass, Operation *operation, PrintCallbackFn printCallback) {
}
void PassManager::IRPrinterConfig::printAfterIfEnabled(
Pass *pass, Operation *operation, PrintCallbackFn printCallback) {
}
namespace {
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);
}
std::function<bool(Pass *, Operation *)> shouldPrintBeforePass;
std::function<bool(Pass *, Operation *)> shouldPrintAfterPass;
raw_ostream &out;
};
}
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)));
}
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));
}