23#include "mlir/IR/Builders.h"
24#include "mlir/IR/PatternMatch.h"
25#include "mlir/Support/FileUtilities.h"
26#include "mlir/Support/LLVM.h"
27#include "mlir/Support/Timing.h"
28#include "mlir/Transforms/InliningUtils.h"
29#include "mlir/Transforms/RegionUtils.h"
30#include "llvm/ADT/MapVector.h"
31#include "llvm/ADT/StringRef.h"
32#include "llvm/Support/FileSystem.h"
33#include "llvm/Support/LogicalResult.h"
34#include "llvm/Support/Path.h"
35#include "llvm/Support/Program.h"
36#include "llvm/Support/SourceMgr.h"
37#include "llvm/Support/ToolOutputFile.h"
38#include "llvm/Support/raw_ostream.h"
40#define DEBUG_TYPE "aig-runner"
44#define GEN_PASS_DEF_AIGERRUNNER
45#define GEN_PASS_DEF_ABCRUNNER
46#include "circt/Dialect/Synth/Transforms/SynthPasses.h.inc"
72 llvm::SetVector<Operation *> willBeErased;
75 size_t outputIndex)
override;
76 bool valueCallback(Value value,
size_t bitPos,
size_t inputIndex)
override;
86bool Converter::operandCallback(OpOperand &op,
size_t bitPos,
89 auto operandKey = std::make_pair(op.getOwner(), op.getOperandNumber());
90 assert(op.get().getType().isInteger() &&
"operand is not an integer");
93 auto *mapIterator = operandMap.find(operandKey);
94 if (mapIterator == operandMap.end()) {
96 auto bitWidth = hw::getBitWidth(op.get().getType());
98 operandMap.insert({operandKey, SmallVector<int>(bitWidth, -1)}).first;
102 mapIterator->second[bitPos] = outputIndex;
108bool Converter::valueCallback(Value value,
size_t bitPos,
size_t inputIndex) {
109 assert(value.getType().isInteger() &&
"value is not an integer");
112 auto *mapIterator = valueMap.find(value);
113 if (mapIterator == valueMap.end()) {
115 auto bitWidth = hw::getBitWidth(value.getType());
117 valueMap.insert({value, SmallVector<int>(bitWidth, -1)}).first;
120 LLVM_DEBUG(llvm::dbgs() <<
"Mapping value: " << value <<
" bitPos: " << bitPos
121 <<
" inputIndex: " << inputIndex <<
"\n");
124 mapIterator->second[bitPos] = inputIndex;
132 SetVector<Operation *> operationsToErase;
136 mlir::IRRewriter rewriter(module);
137 while (!willBeErased.empty()) {
138 auto *operationToReplace = willBeErased.pop_back_val();
139 rewriter.setInsertionPoint(operationToReplace);
142 auto conversionCast =
143 rewriter.replaceOpWithNewOp<mlir::UnrealizedConversionCastOp>(
144 operationToReplace, operationToReplace->getResultTypes(),
146 (void)conversionCast;
150 conversionCast->setAttr(
"aig.runner.must_be_dead", rewriter.getUnitAttr());
155 (void)mlir::runRegionDCE(rewriter, module->getRegions());
159 module.walk([&](mlir::UnrealizedConversionCastOp castOp) {
160 assert(!castOp->hasAttr("aig.runner.must_be_dead") &&
161 "Operation marked for deletion was not eliminated by DCE");
169void Converter::integrateOptimizedModule(
hw::HWModuleOp originalModule,
171 mlir::IRRewriter builder(originalModule->getContext());
172 builder.setInsertionPointToStart(originalModule.getBodyBlock());
174 auto *optimizedTerminator = optimizedModule.getBodyBlock()->getTerminator();
175 auto *originalTerminator = originalModule.getBodyBlock()->getTerminator();
178 for (
const auto &[operandKey, bitOutputIndices] : operandMap) {
179 auto [operationOwner, operandIndex] = operandKey;
180 SmallVector<Value> bitsToConcat;
181 builder.setInsertionPoint(operationOwner);
182 bitsToConcat.reserve(bitOutputIndices.size());
185 for (
auto outputIndex :
llvm::reverse(bitOutputIndices)) {
186 assert(outputIndex != -1 &&
"Unmapped output index found");
187 bitsToConcat.push_back(optimizedTerminator->getOperand(outputIndex));
191 if (bitsToConcat.size() == 1) {
192 operationOwner->setOperand(operandIndex, bitsToConcat.front());
195 operationOwner->getLoc(), bitsToConcat);
196 operationOwner->setOperand(operandIndex, concatenatedValue);
201 SmallVector<Value> moduleArguments(
202 optimizedModule.getBodyBlock()->getNumArguments());
203 for (
const auto &[originalValue, bitInputIndices] : valueMap) {
204 builder.setInsertionPointAfterValue(originalValue);
207 for (
auto [bitPosition, argumentIndex] :
llvm::enumerate(bitInputIndices)) {
210 originalValue.getLoc(), originalValue, bitPosition, 1);
211 moduleArguments[argumentIndex] = extractedBit;
216 auto arguments = optimizedModule.getBodyBlock()->getArguments();
217 if (arguments.size() > 0 && isa<seq::ClockType>(arguments.back().getType())) {
218 assert(clock &&
"Clock signal not found");
219 moduleArguments.back() = clock;
223 assert(llvm::all_of(moduleArguments, [](Value v) {
return v; }) &&
224 "Some module arguments were not properly mapped");
227 builder.inlineBlockBefore(optimizedModule.getBodyBlock(),
228 originalModule.getBodyBlock(),
229 originalTerminator->getIterator(), moduleArguments);
230 optimizedTerminator->erase();
236void Converter::notifyEmitted(Operation *op) { willBeErased.insert(op); }
240void Converter::notifyClock(Value value) { clock = value; }
249 AIGERRunner(llvm::StringRef solverPath, SmallVector<std::string> solverArgs,
250 bool continueOnFailure)
251 : solverPath(solverPath), solverArgs(std::move(solverArgs)),
252 continueOnFailure(continueOnFailure) {}
258 LogicalResult runSolver(
hw::HWModuleOp module, StringRef inputPath,
259 StringRef outputPath);
260 LogicalResult exportToAIGER(Converter &converter,
hw::HWModuleOp module,
261 StringRef outputPath);
262 LogicalResult importFromAIGER(Converter &converter, StringRef inputPath,
264 llvm::StringRef solverPath;
265 SmallVector<std::string> solverArgs;
266 bool continueOnFailure;
274 SmallString<128> tempDir;
276 llvm::sys::fs::createUniqueDirectory(
"aiger-runner", tempDir))
277 return emitError(module.getLoc(),
"failed to create temporary directory: ")
280 SmallString<128> inputPath(tempDir);
281 llvm::sys::path::append(inputPath,
"input.aig");
282 SmallString<128> outputPath(tempDir);
283 llvm::sys::path::append(outputPath,
"output.aig");
287 auto reportWarningOrError = [&](
const Twine &message) -> LogicalResult {
288 (continueOnFailure ? mlir::emitWarning(module.getLoc())
290 << message <<
" on module " << module.getModuleNameAttr();
291 return success(continueOnFailure);
295 if (failed(exportToAIGER(converter, cast<hw::HWModuleOp>(module), inputPath)))
296 return reportWarningOrError(
"failed to export module to AIGER format");
299 if (failed(runSolver(module, inputPath, outputPath)))
300 return reportWarningOrError(
"failed to run external solver");
304 importFromAIGER(converter, outputPath, cast<hw::HWModuleOp>(module))))
305 return reportWarningOrError(
"failed to import results from AIGER format");
308 if (llvm::sys::fs::remove(inputPath))
309 return emitError(module.getLoc(),
"failed to remove input file: ")
311 if (llvm::sys::fs::remove(outputPath))
312 return emitError(module.getLoc(),
"failed to remove output file: ")
314 if (llvm::sys::fs::remove(tempDir))
315 return emitError(module.getLoc(),
"failed to remove temporary directory: ")
318 return llvm::success();
323LogicalResult AIGERRunner::runSolver(
hw::HWModuleOp module, StringRef inputPath,
324 StringRef outputPath) {
326 SmallVector<StringRef> commandArgs;
327 std::vector<std::string> processedSolverArgs;
330 auto replaceAll = [](std::string str, StringRef from, StringRef to) {
332 while ((pos = str.find(from.str(), pos)) != std::string::npos) {
333 str.replace(pos, from.size(), to.str());
340 for (
const auto &solverArg : solverArgs) {
341 std::string processedArg =
342 replaceAll(replaceAll(solverArg,
"<inputFile>", inputPath),
343 "<outputFile>", outputPath);
344 processedSolverArgs.push_back(std::move(processedArg));
348 std::string executionError;
349 auto solverProgram = llvm::sys::findProgramByName(solverPath);
350 if (
auto e = solverProgram.getError())
351 return emitError(module.getLoc(),
"failed to find solver program: ")
352 << solverPath.str() <<
": " << e.message();
355 commandArgs.push_back(*solverProgram);
356 for (
auto &processedArg : processedSolverArgs)
357 commandArgs.push_back(processedArg);
360 int executionResult = llvm::sys::ExecuteAndWait(
361 solverProgram.get(), commandArgs,
363 0, 0, &executionError);
366 if (executionResult != 0)
367 return emitError(module.getLoc(),
"solver execution failed for module ")
368 <<
module.getModuleNameAttr() << " with error: " << executionError;
374LogicalResult AIGERRunner::exportToAIGER(Converter &converter,
376 StringRef outputPath) {
378 auto outputFile = mlir::openOutputFile(outputPath);
380 return emitError(module.getLoc(),
"failed to open AIGER output file: ")
383 LLVM_DEBUG(llvm::dbgs() <<
"Exporting module " << module.getModuleNameAttr()
384 <<
" to AIGER format\n");
392 &exportOptions, &converter);
401LogicalResult AIGERRunner::importFromAIGER(Converter &converter,
405 llvm::SourceMgr sourceMgr;
406 auto inputFile = mlir::openInputFile(inputPath);
408 return emitError(originalModule.getLoc(),
409 "failed to open AIGER input file: ")
413 sourceMgr.AddNewSourceBuffer(std::move(inputFile), llvm::SMLoc());
416 mlir::TimingScope timingScope;
417 mlir::Block temporaryBlock;
418 mlir::OpBuilder builder(originalModule->getContext());
419 builder.setInsertionPointToStart(&temporaryBlock);
420 auto temporaryModule =
421 mlir::ModuleOp::create(builder, builder.getUnknownLoc());
425 timingScope, temporaryModule)))
426 return emitError(originalModule.getLoc(),
427 "failed to import optimized AIGER file");
430 auto optimizedModule =
431 cast<hw::HWModuleOp>(temporaryModule.getBody()->front());
434 converter.integrateOptimizedModule(originalModule, optimizedModule);
437 converter.cleanup(originalModule);
439 return llvm::success();
447class AIGERRunnerPass :
public impl::AIGERRunnerBase<AIGERRunnerPass> {
449 using AIGERRunnerBase<AIGERRunnerPass>::AIGERRunnerBase;
450 void runOnOperation()
override;
454void AIGERRunnerPass::runOnOperation() {
455 auto module = getOperation();
458 SmallVector<std::string> solverArgsRef;
459 for (
const auto &arg : solverArgs)
460 solverArgsRef.push_back(arg);
462 AIGERRunner runner(solverPath, std::move(solverArgsRef), continueOnFailure);
463 if (failed(runner.run(module)))
472class ABCRunnerPass :
public impl::ABCRunnerBase<ABCRunnerPass> {
474 using ABCRunnerBase<ABCRunnerPass>::ABCRunnerBase;
475 void runOnOperation()
override;
481void ABCRunnerPass::runOnOperation() {
482 auto module = getOperation();
484 SmallVector<std::string> abcArguments;
487 auto addABCCommand = [&](
const std::string &command) {
488 abcArguments.push_back(
"-q");
489 abcArguments.push_back(command);
493 addABCCommand(
"read <inputFile>");
496 for (
const auto &optimizationCmd : abcCommands)
497 addABCCommand(optimizationCmd);
500 addABCCommand(
"write <outputFile>");
503 AIGERRunner abcRunner(abcPath, std::move(abcArguments), continueOnFailure);
504 if (failed(abcRunner.run(module)))
assert(baseType &&"element must be base type")
static Location getLoc(DefSlot slot)
mlir::LogicalResult importAIGER(llvm::SourceMgr &sourceMgr, mlir::MLIRContext *context, mlir::TimingScope &ts, mlir::ModuleOp module, const ImportAIGEROptions *options=nullptr)
Parse an AIGER file and populate the given MLIR module with corresponding AIG dialect operations.
mlir::LogicalResult exportAIGER(hw::HWModuleOp module, llvm::raw_ostream &os, const ExportAIGEROptions *options=nullptr, ExportAIGERHandler *handler=nullptr)
Export an MLIR module containing AIG dialect operations to AIGER format.
void error(Twine message)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
Handler for AIGER export.
virtual bool valueCallback(Value result, size_t bitPos, size_t inputIndex)
virtual void notifyClock(Value value)
virtual bool operandCallback(mlir::OpOperand &operand, size_t bitPos, size_t outputIndex)
virtual void notifyEmitted(Operation *op)
Options for AIGER export.
bool includeSymbolTable
Whether to include symbol table in the output.
bool binaryFormat
Whether to export in binary format (aig) or ASCII format (aag).