22#include "mlir/IR/Builders.h"
23#include "mlir/IR/PatternMatch.h"
24#include "mlir/Support/FileUtilities.h"
25#include "mlir/Support/LLVM.h"
26#include "mlir/Support/Timing.h"
27#include "mlir/Transforms/InliningUtils.h"
28#include "mlir/Transforms/RegionUtils.h"
29#include "llvm/ADT/MapVector.h"
30#include "llvm/ADT/StringRef.h"
31#include "llvm/Support/FileSystem.h"
32#include "llvm/Support/LogicalResult.h"
33#include "llvm/Support/Path.h"
34#include "llvm/Support/Program.h"
35#include "llvm/Support/SourceMgr.h"
36#include "llvm/Support/ToolOutputFile.h"
37#include "llvm/Support/raw_ostream.h"
42#define DEBUG_TYPE "aig-runner"
46#define GEN_PASS_DEF_AIGERRUNNER
47#define GEN_PASS_DEF_ABCRUNNER
48#include "circt/Dialect/AIG/AIGPasses.h.inc"
66 llvm::MapVector<std::pair<Operation *, size_t>, SmallVector<int>> operandMap;
69 llvm::MapVector<Value, SmallVector<int>> valueMap;
71 llvm::SetVector<Operation *> willBeErased;
74 size_t outputIndex)
override;
75 bool valueCallback(Value value,
size_t bitPos,
size_t inputIndex)
override;
85bool Converter::operandCallback(OpOperand &op,
size_t bitPos,
88 auto operandKey = std::make_pair(op.getOwner(), op.getOperandNumber());
89 assert(op.get().getType().isInteger() &&
"operand is not an integer");
92 auto *mapIterator = operandMap.find(operandKey);
93 if (mapIterator == operandMap.end()) {
95 auto bitWidth = hw::getBitWidth(op.get().getType());
97 operandMap.insert({operandKey, SmallVector<int>(bitWidth, -1)}).first;
101 mapIterator->second[bitPos] = outputIndex;
107bool Converter::valueCallback(Value value,
size_t bitPos,
size_t inputIndex) {
108 assert(value.getType().isInteger() &&
"value is not an integer");
111 auto *mapIterator = valueMap.find(value);
112 if (mapIterator == valueMap.end()) {
114 auto bitWidth = hw::getBitWidth(value.getType());
116 valueMap.insert({value, SmallVector<int>(bitWidth, -1)}).first;
119 LLVM_DEBUG(llvm::dbgs() <<
"Mapping value: " << value <<
" bitPos: " << bitPos
120 <<
" inputIndex: " << inputIndex <<
"\n");
123 mapIterator->second[bitPos] = inputIndex;
131 SetVector<Operation *> operationsToErase;
135 mlir::IRRewriter rewriter(module);
136 while (!willBeErased.empty()) {
137 auto *operationToReplace = willBeErased.pop_back_val();
138 rewriter.setInsertionPoint(operationToReplace);
141 auto conversionCast =
142 rewriter.replaceOpWithNewOp<mlir::UnrealizedConversionCastOp>(
143 operationToReplace, operationToReplace->getResultTypes(),
145 (void)conversionCast;
149 conversionCast->setAttr(
"aig.runner.must_be_dead", rewriter.getUnitAttr());
154 (void)mlir::runRegionDCE(rewriter, module->getRegions());
158 module.walk([&](mlir::UnrealizedConversionCastOp castOp) {
159 assert(!castOp->hasAttr("aig.runner.must_be_dead") &&
160 "Operation marked for deletion was not eliminated by DCE");
168void Converter::integrateOptimizedModule(
hw::HWModuleOp originalModule,
170 mlir::IRRewriter builder(originalModule->getContext());
171 builder.setInsertionPointToStart(originalModule.getBodyBlock());
173 auto *optimizedTerminator = optimizedModule.getBodyBlock()->getTerminator();
174 auto *originalTerminator = originalModule.getBodyBlock()->getTerminator();
177 for (
const auto &[operandKey, bitOutputIndices] : operandMap) {
178 auto [operationOwner, operandIndex] = operandKey;
179 SmallVector<Value> bitsToConcat;
180 builder.setInsertionPoint(operationOwner);
181 bitsToConcat.reserve(bitOutputIndices.size());
184 for (
auto outputIndex :
llvm::reverse(bitOutputIndices)) {
185 assert(outputIndex != -1 &&
"Unmapped output index found");
186 bitsToConcat.push_back(optimizedTerminator->getOperand(outputIndex));
190 if (bitsToConcat.size() == 1) {
191 operationOwner->setOperand(operandIndex, bitsToConcat.front());
194 operationOwner->getLoc(), bitsToConcat);
195 operationOwner->setOperand(operandIndex, concatenatedValue);
200 SmallVector<Value> moduleArguments(
201 optimizedModule.getBodyBlock()->getNumArguments());
202 for (
const auto &[originalValue, bitInputIndices] : valueMap) {
203 builder.setInsertionPointAfterValue(originalValue);
206 for (
auto [bitPosition, argumentIndex] :
llvm::enumerate(bitInputIndices)) {
209 originalValue.getLoc(), originalValue, bitPosition, 1);
210 moduleArguments[argumentIndex] = extractedBit;
215 auto arguments = optimizedModule.getBodyBlock()->getArguments();
216 if (arguments.size() > 0 && isa<seq::ClockType>(arguments.back().getType())) {
217 assert(clock &&
"Clock signal not found");
218 moduleArguments.back() = clock;
222 assert(llvm::all_of(moduleArguments, [](Value v) {
return v; }) &&
223 "Some module arguments were not properly mapped");
226 builder.inlineBlockBefore(optimizedModule.getBodyBlock(),
227 originalModule.getBodyBlock(),
228 originalTerminator->getIterator(), moduleArguments);
229 optimizedTerminator->erase();
235void Converter::notifyEmitted(Operation *op) { willBeErased.insert(op); }
239void Converter::notifyClock(Value value) { clock = value; }
248 AIGERRunner(llvm::StringRef solverPath, SmallVector<std::string> solverArgs,
249 bool continueOnFailure)
250 : solverPath(solverPath), solverArgs(std::move(solverArgs)),
251 continueOnFailure(continueOnFailure) {}
257 LogicalResult runSolver(
hw::HWModuleOp module, StringRef inputPath,
258 StringRef outputPath);
259 LogicalResult exportToAIGER(Converter &converter,
hw::HWModuleOp module,
260 StringRef outputPath);
261 LogicalResult importFromAIGER(Converter &converter, StringRef inputPath,
263 llvm::StringRef solverPath;
264 SmallVector<std::string> solverArgs;
265 bool continueOnFailure;
273 SmallString<128> tempDir;
275 llvm::sys::fs::createUniqueDirectory(
"aiger-runner", tempDir))
276 return emitError(module.getLoc(),
"failed to create temporary directory: ")
279 SmallString<128> inputPath(tempDir);
280 llvm::sys::path::append(inputPath,
"input.aig");
281 SmallString<128> outputPath(tempDir);
282 llvm::sys::path::append(outputPath,
"output.aig");
286 auto reportWarningOrError = [&](
const Twine &message) -> LogicalResult {
287 (continueOnFailure ? mlir::emitWarning(module.getLoc())
289 << message <<
" on module " << module.getModuleNameAttr();
290 return success(continueOnFailure);
294 if (failed(exportToAIGER(converter, cast<hw::HWModuleOp>(module), inputPath)))
295 return reportWarningOrError(
"failed to export module to AIGER format");
298 if (failed(runSolver(module, inputPath, outputPath)))
299 return reportWarningOrError(
"failed to run external solver");
303 importFromAIGER(converter, outputPath, cast<hw::HWModuleOp>(module))))
304 return reportWarningOrError(
"failed to import results from AIGER format");
307 if (llvm::sys::fs::remove(inputPath))
308 return emitError(module.getLoc(),
"failed to remove input file: ")
310 if (llvm::sys::fs::remove(outputPath))
311 return emitError(module.getLoc(),
"failed to remove output file: ")
313 if (llvm::sys::fs::remove(tempDir))
314 return emitError(module.getLoc(),
"failed to remove temporary directory: ")
317 return llvm::success();
322LogicalResult AIGERRunner::runSolver(
hw::HWModuleOp module, StringRef inputPath,
323 StringRef outputPath) {
325 SmallVector<StringRef> commandArgs;
326 std::vector<std::string> processedSolverArgs;
329 auto replaceAll = [](std::string str, StringRef from, StringRef to) {
331 while ((pos = str.find(from.str(), pos)) != std::string::npos) {
332 str.replace(pos, from.size(), to.str());
339 for (
const auto &solverArg : solverArgs) {
340 std::string processedArg =
341 replaceAll(replaceAll(solverArg,
"<inputFile>", inputPath),
342 "<outputFile>", outputPath);
343 processedSolverArgs.push_back(std::move(processedArg));
347 std::string executionError;
348 auto solverProgram = llvm::sys::findProgramByName(solverPath);
349 if (
auto e = solverProgram.getError())
350 return emitError(module.getLoc(),
"failed to find solver program: ")
351 << solverPath.str() <<
": " << e.message();
354 commandArgs.push_back(*solverProgram);
355 for (
auto &processedArg : processedSolverArgs)
356 commandArgs.push_back(processedArg);
359 int executionResult = llvm::sys::ExecuteAndWait(
360 solverProgram.get(), commandArgs,
362 0, 0, &executionError);
365 if (executionResult != 0)
366 return emitError(module.getLoc(),
"solver execution failed for module ")
367 <<
module.getModuleNameAttr() << " with error: " << executionError;
373LogicalResult AIGERRunner::exportToAIGER(Converter &converter,
375 StringRef outputPath) {
377 auto outputFile = mlir::openOutputFile(outputPath);
379 return emitError(module.getLoc(),
"failed to open AIGER output file: ")
382 LLVM_DEBUG(llvm::dbgs() <<
"Exporting module " << module.getModuleNameAttr()
383 <<
" to AIGER format\n");
391 &exportOptions, &converter);
400LogicalResult AIGERRunner::importFromAIGER(Converter &converter,
404 llvm::SourceMgr sourceMgr;
405 auto inputFile = mlir::openInputFile(inputPath);
407 return emitError(originalModule.getLoc(),
408 "failed to open AIGER input file: ")
412 sourceMgr.AddNewSourceBuffer(std::move(inputFile), llvm::SMLoc());
415 mlir::TimingScope timingScope;
416 mlir::Block temporaryBlock;
417 mlir::OpBuilder builder(originalModule->getContext());
418 builder.setInsertionPointToStart(&temporaryBlock);
419 auto temporaryModule =
420 mlir::ModuleOp::create(builder, builder.getUnknownLoc());
424 timingScope, temporaryModule)))
425 return emitError(originalModule.getLoc(),
426 "failed to import optimized AIGER file");
429 auto optimizedModule =
430 cast<hw::HWModuleOp>(temporaryModule.getBody()->front());
433 converter.integrateOptimizedModule(originalModule, optimizedModule);
436 converter.cleanup(originalModule);
438 return llvm::success();
446class AIGERRunnerPass :
public impl::AIGERRunnerBase<AIGERRunnerPass> {
448 using AIGERRunnerBase<AIGERRunnerPass>::AIGERRunnerBase;
449 void runOnOperation()
override;
453void AIGERRunnerPass::runOnOperation() {
454 auto module = getOperation();
457 SmallVector<std::string> solverArgsRef;
458 for (
const auto &arg : solverArgs)
459 solverArgsRef.push_back(arg);
461 AIGERRunner runner(solverPath, std::move(solverArgsRef), continueOnFailure);
462 if (failed(runner.run(module)))
471class ABCRunnerPass :
public impl::ABCRunnerBase<ABCRunnerPass> {
473 using ABCRunnerBase<ABCRunnerPass>::ABCRunnerBase;
474 void runOnOperation()
override;
480void ABCRunnerPass::runOnOperation() {
481 auto module = getOperation();
483 SmallVector<std::string> abcArguments;
486 auto addABCCommand = [&](
const std::string &command) {
487 abcArguments.push_back(
"-q");
488 abcArguments.push_back(command);
492 addABCCommand(
"read <inputFile>");
495 for (
const auto &optimizationCmd : abcCommands)
496 addABCCommand(optimizationCmd);
499 addABCCommand(
"write <outputFile>");
502 AIGERRunner abcRunner(abcPath, std::move(abcArguments), continueOnFailure);
503 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).