CIRCT 22.0.0git
Loading...
Searching...
No Matches
ImportAIGER.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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 the AIGER file import functionality.
10//
11//===----------------------------------------------------------------------===//
12
18#include "mlir/IR/Builders.h"
19#include "mlir/IR/BuiltinOps.h"
20#include "mlir/IR/Diagnostics.h"
21#include "mlir/IR/Location.h"
22#include "mlir/IR/MLIRContext.h"
23#include "mlir/Support/FileUtilities.h"
24#include "mlir/Support/LogicalResult.h"
25#include "mlir/Support/Timing.h"
26#include "mlir/Tools/mlir-translate/Translation.h"
27#include "llvm/ADT/DenseMap.h"
28#include "llvm/ADT/StringExtras.h"
29#include "llvm/ADT/StringRef.h"
30#include "llvm/Support/Debug.h"
31#include "llvm/Support/ErrorHandling.h"
32#include "llvm/Support/LEB128.h"
33#include "llvm/Support/SMLoc.h"
34#include "llvm/Support/SourceMgr.h"
35#include "llvm/Support/raw_ostream.h"
36#include <cctype>
37#include <limits>
38#include <string>
39
40using namespace mlir;
41using namespace circt;
42using namespace circt::hw;
43using namespace circt::synth;
44using namespace circt::seq;
45using namespace circt::aiger;
46
47#define DEBUG_TYPE "import-aiger"
48
49namespace {
50
51/// AIGER token types for lexical analysis
52enum class AIGERTokenKind {
53 // Literals
54 Number,
55 Identifier,
56
57 // Special characters
58 Newline,
59 EndOfFile,
60
61 // Error
62 Error
63};
64
65/// Represents a token in the AIGER file
66struct AIGERToken {
67 AIGERTokenKind kind;
68 StringRef spelling;
69 SMLoc location;
70
71 AIGERToken(AIGERTokenKind kind, StringRef spelling, SMLoc location)
72 : kind(kind), spelling(spelling), location(location) {}
73};
74
75/// Simple lexer for AIGER files
76///
77/// This lexer handles both ASCII (.aag) and binary (.aig) AIGER formats.
78/// It provides basic tokenization for header parsing and symbol tables,
79/// while also supporting byte-level reading for binary format.
80class AIGERLexer {
81public:
82 AIGERLexer(const llvm::SourceMgr &sourceMgr, MLIRContext *context)
83 : sourceMgr(sourceMgr),
84 bufferNameIdentifier(getMainBufferNameIdentifier(sourceMgr, context)),
85 curBuffer(
86 sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID())->getBuffer()),
87 curPtr(curBuffer.begin()) {}
88
89 /// Get the next token
90 AIGERToken nextToken();
91
92 /// Lex the current position as a symbol (used for symbol table parsing)
93 AIGERToken lexAsSymbol();
94
95 /// Peek at the current token without consuming it
96 AIGERToken peekToken();
97
98 /// Check if we're at end of file
99 bool isAtEOF() const { return curPtr >= curBuffer.end(); }
100
101 /// Read a LEB128 encoded unsigned integer
102 ParseResult readLEB128(unsigned &result);
103
104 /// Get current location
105 SMLoc getCurrentLoc() const { return SMLoc::getFromPointer(curPtr); }
106
107 /// Encode the specified source location information into a Location object
108 /// for attachment to the IR or error reporting.
109 Location translateLocation(llvm::SMLoc loc) {
110 assert(loc.isValid());
111 unsigned mainFileID = sourceMgr.getMainFileID();
112 auto lineAndColumn = sourceMgr.getLineAndColumn(loc, mainFileID);
113 return FileLineColLoc::get(bufferNameIdentifier, lineAndColumn.first,
114 lineAndColumn.second);
115 }
116
117 /// Emit an error message and return an error token.
118 AIGERToken emitError(const char *loc, const Twine &message) {
119 mlir::emitError(translateLocation(SMLoc::getFromPointer(loc)), message);
120 return AIGERToken(AIGERTokenKind::Error, StringRef(loc, 1),
121 SMLoc::getFromPointer(loc));
122 }
123
124private:
125 const llvm::SourceMgr &sourceMgr;
126 StringAttr bufferNameIdentifier;
127 StringRef curBuffer;
128 const char *curPtr;
129
130 /// Get the main buffer name identifier
131 static StringAttr
132 getMainBufferNameIdentifier(const llvm::SourceMgr &sourceMgr,
133 MLIRContext *context) {
134 auto mainBuffer = sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID());
135 StringRef bufferName = mainBuffer->getBufferIdentifier();
136 if (bufferName.empty())
137 bufferName = "<unknown>";
138 return StringAttr::get(context, bufferName);
139 }
140
141 /// Skip whitespace (except newlines)
142 void skipWhitespace();
143
144 /// Skip to end of line (for comment handling)
145 void skipUntilNewline();
146
147 /// Lex a number
148 AIGERToken lexNumber();
149
150 /// Lex an identifier
151 AIGERToken lexIdentifier();
152
153 /// Create a token
154 AIGERToken makeToken(AIGERTokenKind kind, const char *start) {
155 return AIGERToken(kind, StringRef(start, curPtr - start),
156 SMLoc::getFromPointer(start));
157 }
158};
159
160/// Main AIGER parser class
161///
162/// This parser implements the complete AIGER format specification including:
163/// - ASCII (.aag) and binary (.aig) formats
164/// - Basic AIGER components (inputs, latches, outputs, AND gates)
165/// - Optional sections (bad states, constraints, justice, fairness)
166/// - Symbol tables and comments
167///
168/// The parser creates MLIR modules using the HW, AIG, and Seq dialects.
169class AIGERParser {
170public:
171 AIGERParser(const llvm::SourceMgr &sourceMgr, MLIRContext *context,
172 ModuleOp module, const ImportAIGEROptions &options)
173 : lexer(sourceMgr, context), context(context), module(module),
174 options(options), builder(context) {}
175
176 /// Parse the AIGER file and populate the MLIR module
177 ParseResult parse();
178
179private:
180 AIGERLexer lexer;
181 MLIRContext *context;
182 ModuleOp module;
183 const ImportAIGEROptions &options;
184 OpBuilder builder;
185
186 // AIGER file data
187 unsigned maxVarIndex = 0;
188 unsigned numInputs = 0;
189 unsigned numLatches = 0;
190 unsigned numOutputs = 0;
191 unsigned numAnds = 0;
192 bool isBinaryFormat = false;
193
194 // A mapping from {kind, index} -> symbol where kind is 0 for inputs, 1 for
195 // latches, and 2 for outputs.
196 enum SymbolKind : unsigned { Input, Latch, Output };
197 DenseMap<std::pair<SymbolKind, unsigned>, StringAttr> symbolTable;
198
199 // Parsed data storage
200 SmallVector<unsigned> inputLiterals;
201 SmallVector<std::tuple<unsigned, unsigned, SMLoc>>
202 latchDefs; // current, next, loc
203 SmallVector<std::pair<unsigned, SMLoc>> outputLiterals; // literal, loc
204 SmallVector<std::tuple<unsigned, unsigned, unsigned, SMLoc>>
205 andGateDefs; // lhs, rhs0, rhs1
206
207 /// Parse the header line (format and counts)
208 ParseResult parseHeader();
209
210 /// Parse inputs section
211 ParseResult parseInputs();
212
213 /// Parse latches section
214 ParseResult parseLatches();
215
216 /// Parse outputs section
217 ParseResult parseOutputs();
218
219 /// Parse AND gates section (dispatches to ASCII or binary)
220 ParseResult parseAndGates();
221
222 /// Parse AND gates in ASCII format
223 ParseResult parseAndGatesASCII();
224
225 /// Parse AND gates in binary format with delta compression
226 ParseResult parseAndGatesBinary();
227
228 /// Parse symbol table (optional)
229 ParseResult parseSymbolTable();
230
231 /// Parse comments (optional)
232 ParseResult parseComments();
233
234 /// Convert AIGER literal to MLIR value using backedges
235 ///
236 /// \param literal The AIGER literal (variable * 2 + inversion)
237 /// \param backedges Map from literals to backedge values
238 /// \param loc Location for created operations
239 /// \return The MLIR value corresponding to the literal, or nullptr on error
240 Value getLiteralValue(unsigned literal,
241 DenseMap<unsigned, Backedge> &backedges, Location loc);
242
243 /// Create the top-level HW module from parsed data
244 ParseResult createModule();
245
246 InFlightDiagnostic emitError(llvm::SMLoc loc, const Twine &message) {
247 return mlir::emitError(lexer.translateLocation(loc), message);
248 }
249
250 /// Emit error at current location
251 InFlightDiagnostic emitError(const Twine &message) {
252 return emitError(lexer.getCurrentLoc(), message);
253 }
254
255 /// Parse a number token into result
256 ParseResult parseNumber(unsigned &result, SMLoc *loc = nullptr);
257
258 /// Parse a binary encoded number (variable-length encoding)
259 ParseResult parseBinaryNumber(unsigned &result);
260
261 /// Expect and consume a newline token
262 ParseResult parseNewLine();
263};
264
265} // anonymous namespace
266
267//===----------------------------------------------------------------------===//
268// AIGERLexer Implementation
269//===----------------------------------------------------------------------===//
270
271void AIGERLexer::skipWhitespace() {
272 while (curPtr < curBuffer.end()) {
273 // NOTE: Don't use llvm::isSpace here because it also skips '\n'.
274 if (*curPtr == ' ' || *curPtr == '\t' || *curPtr == '\r') {
275 ++curPtr;
276 continue;
277 }
278
279 // Treat "//" as whitespace. This is not part of the AIGER format, but we
280 // support it for FileCheck tests.
281 if (*curPtr == '/' &&
282 (curPtr + 1 < curBuffer.end() && *(curPtr + 1) == '/')) {
283 skipUntilNewline();
284 continue;
285 }
286 break;
287 }
288}
289
290void AIGERLexer::skipUntilNewline() {
291 while (curPtr < curBuffer.end() && *curPtr != '\n')
292 ++curPtr;
293 if (curPtr < curBuffer.end() && *curPtr == '\n')
294 ++curPtr;
295}
296
297AIGERToken AIGERLexer::lexNumber() {
298 const char *start = curPtr;
299 while (curPtr < curBuffer.end() && llvm::isDigit(*curPtr))
300 ++curPtr;
301 return makeToken(AIGERTokenKind::Number, start);
302}
303
304AIGERToken AIGERLexer::lexIdentifier() {
305 const char *start = curPtr;
306 while (curPtr < curBuffer.end() && (llvm::isAlnum(*curPtr) || *curPtr == '_'))
307 ++curPtr;
308
309 StringRef spelling(start, curPtr - start);
310 AIGERTokenKind kind = AIGERTokenKind::Identifier;
311
312 return makeToken(kind, start);
313}
314
315AIGERToken AIGERLexer::nextToken() {
316 skipWhitespace();
317
318 auto impl = [this]() {
319 if (curPtr >= curBuffer.end())
320 return makeToken(AIGERTokenKind::EndOfFile, curPtr);
321
322 const char *start = curPtr;
323 char c = *curPtr++;
324
325 switch (c) {
326 case '\n':
327 return makeToken(AIGERTokenKind::Newline, start);
328 case '\r':
329 case ' ':
330 case '\t':
331 llvm_unreachable("Whitespace should have been skipped");
332 return makeToken(AIGERTokenKind::Error, start);
333 default:
334 if (llvm::isDigit(c)) {
335 --curPtr; // Back up to re-lex the number
336 return lexNumber();
337 }
338 if (llvm::isAlpha(c) || c == '_') {
339 --curPtr; // Back up to re-lex the identifier
340 return lexIdentifier();
341 }
342 assert((c != '/' || *curPtr != '/') && "// should have been skipped");
343 return makeToken(AIGERTokenKind::Error, start);
344 }
345 };
346
347 auto token = impl();
348 return token;
349}
350
351AIGERToken AIGERLexer::lexAsSymbol() {
352 skipWhitespace();
353 const char *start = curPtr;
354 while (curPtr < curBuffer.end() &&
355 (llvm::isPrint(*curPtr) && !llvm::isSpace(*curPtr)))
356 ++curPtr;
357 return makeToken(AIGERTokenKind::Identifier, start);
358}
359
360AIGERToken AIGERLexer::peekToken() {
361 const char *savedPtr = curPtr;
362 AIGERToken token = nextToken();
363 curPtr = savedPtr;
364 return token;
365}
366
367ParseResult AIGERLexer::readLEB128(unsigned &result) {
368 unsigned len;
369 uint64_t value =
370 llvm::decodeULEB128(reinterpret_cast<const uint8_t *>(curPtr), &len,
371 reinterpret_cast<const uint8_t *>(curBuffer.end()));
372 if (len == 0 || value > std::numeric_limits<unsigned>::max())
373 return failure();
374 curPtr += len;
375 result = static_cast<unsigned>(value);
376 return success();
377}
378
379//===----------------------------------------------------------------------===//
380// AIGERParser Implementation
381//===----------------------------------------------------------------------===//
382
383ParseResult AIGERParser::parse() {
384 if (parseHeader() || parseInputs() || parseLatches() || parseOutputs() ||
385 parseAndGates() || parseSymbolTable() || parseComments())
386 return failure();
387 // Create the final module
388 return createModule();
389}
390
391ParseResult AIGERParser::parseNumber(unsigned &result, SMLoc *loc) {
392 auto token = lexer.nextToken();
393 if (loc)
394 *loc = token.location;
395
396 if (token.kind != AIGERTokenKind::Number)
397 return emitError(token.location, "expected number");
398
399 if (token.spelling.getAsInteger(10, result))
400 return emitError(token.location, "invalid number format");
401
402 return success();
403}
404
405ParseResult AIGERParser::parseBinaryNumber(unsigned &result) {
406 return lexer.readLEB128(result);
407}
408
409ParseResult AIGERParser::parseHeader() {
410 LLVM_DEBUG(llvm::dbgs() << "Parsing AIGER header\n");
411
412 // Parse format identifier (aag or aig)
413 while (lexer.peekToken().kind != AIGERTokenKind::Identifier)
414 lexer.nextToken();
415
416 auto formatToken = lexer.nextToken();
417 if (formatToken.spelling == "aag") {
418 isBinaryFormat = false;
419 LLVM_DEBUG(llvm::dbgs() << "Format: aag (ASCII)\n");
420 } else if (formatToken.spelling == "aig") {
421 isBinaryFormat = true;
422 LLVM_DEBUG(llvm::dbgs() << "Format: aig (binary)\n");
423 } else {
424 return emitError(formatToken.location,
425 "expected 'aag' or 'aig' format identifier");
426 }
427
428 // Parse M I L O A (numbers separated by spaces)
429 SMLoc loc;
430 if (parseNumber(maxVarIndex, &loc))
431 return emitError(loc, "failed to parse M (max variable index)");
432
433 if (parseNumber(numInputs, &loc))
434 return emitError(loc, "failed to parse I (number of inputs)");
435
436 if (parseNumber(numLatches, &loc))
437 return emitError(loc, "failed to parse L (number of latches)");
438
439 if (parseNumber(numOutputs, &loc))
440 return emitError(loc, "failed to parse O (number of outputs)");
441
442 if (parseNumber(numAnds, &loc))
443 return emitError(loc, "failed to parse A (number of AND gates)");
444
445 LLVM_DEBUG(llvm::dbgs() << "Header: M=" << maxVarIndex << " I=" << numInputs
446 << " L=" << numLatches << " O=" << numOutputs
447 << " A=" << numAnds << "\n");
448
449 // Expect newline after header
450 return parseNewLine();
451}
452
453ParseResult AIGERParser::parseNewLine() {
454 auto token = lexer.nextToken();
455 if (token.kind != AIGERTokenKind::Newline)
456 return emitError(token.location, "expected newline");
457
458 return success();
459}
460
461ParseResult AIGERParser::parseInputs() {
462 LLVM_DEBUG(llvm::dbgs() << "Parsing " << numInputs << " inputs\n");
463 if (isBinaryFormat) {
464 // In binary format, inputs are implicit (literals 2, 4, 6, ...)
465 for (unsigned i = 0; i < numInputs; ++i)
466 inputLiterals.push_back(2 * (i + 1));
467 return success();
468 }
469
470 for (unsigned i = 0; i < numInputs; ++i) {
471 unsigned literal;
472 SMLoc loc;
473 if (parseNumber(literal, &loc) || parseNewLine())
474 return emitError(loc, "failed to parse input literal");
475 inputLiterals.push_back(literal);
476 }
477
478 return success();
479}
480
481ParseResult AIGERParser::parseLatches() {
482 LLVM_DEBUG(llvm::dbgs() << "Parsing " << numLatches << " latches\n");
483 if (isBinaryFormat) {
484 // In binary format, latches are implicit (literals 2, 4, 6, ...)
485 for (unsigned i = 0; i < numLatches; ++i) {
486 unsigned literal;
487 SMLoc loc;
488 if (parseNumber(literal, &loc))
489 return emitError(loc, "failed to parse latch next state literal");
490
491 latchDefs.push_back({2 * (i + 1 + numInputs), literal, loc});
492
493 // Expect newline after each latch next state
494 if (parseNewLine())
495 return failure();
496 }
497 return success();
498 }
499
500 // Parse latch definitions: current_state next_state
501 for (unsigned i = 0; i < numLatches; ++i) {
502 unsigned currentState, nextState;
503 SMLoc loc;
504 if (parseNumber(currentState, &loc) || parseNumber(nextState) ||
505 parseNewLine())
506 return emitError(loc, "failed to parse latch definition");
507
508 LLVM_DEBUG(llvm::dbgs() << "Latch " << i << ": " << currentState << " -> "
509 << nextState << "\n");
510
511 // Validate current state literal (should be even and positive)
512 if (currentState % 2 != 0 || currentState == 0)
513 return emitError(loc, "invalid latch current state literal");
514
515 latchDefs.push_back({currentState, nextState, loc});
516 }
517
518 return success();
519}
520
521ParseResult AIGERParser::parseOutputs() {
522 LLVM_DEBUG(llvm::dbgs() << "Parsing " << numOutputs << " outputs\n");
523 // NOTE: Parsing is same for binary and ASCII formats
524 // Parse output literals
525 for (unsigned i = 0; i < numOutputs; ++i) {
526 unsigned literal;
527 SMLoc loc;
528 if (parseNumber(literal, &loc) || parseNewLine())
529 return emitError(loc, "failed to parse output literal");
530
531 LLVM_DEBUG(llvm::dbgs() << "Output " << i << ": " << literal << "\n");
532
533 // Output literals can be any valid literal (including inverted)
534 outputLiterals.push_back({literal, loc});
535 }
536
537 return success();
538}
539
540ParseResult AIGERParser::parseAndGates() {
541 LLVM_DEBUG(llvm::dbgs() << "Parsing " << numAnds << " AND gates\n");
542
543 if (isBinaryFormat) {
544 return parseAndGatesBinary();
545 }
546 return parseAndGatesASCII();
547}
548
549ParseResult AIGERParser::parseAndGatesASCII() {
550 // Parse AND gate definitions: lhs rhs0 rhs1
551 for (unsigned i = 0; i < numAnds; ++i) {
552 unsigned lhs, rhs0, rhs1;
553 SMLoc loc;
554 if (parseNumber(lhs, &loc) || parseNumber(rhs0) || parseNumber(rhs1) ||
555 parseNewLine())
556 return emitError(loc, "failed to parse AND gate definition");
557
558 LLVM_DEBUG(llvm::dbgs() << "AND Gate " << i << ": " << lhs << " = " << rhs0
559 << " & " << rhs1 << "\n");
560
561 // Validate LHS (should be even and positive)
562 if (lhs % 2 != 0 || lhs == 0)
563 return emitError(loc, "invalid AND gate LHS literal");
564
565 // Validate literal bounds
566 if (lhs / 2 > maxVarIndex || rhs0 / 2 > maxVarIndex ||
567 rhs1 / 2 > maxVarIndex)
568 return emitError(loc, "AND gate literal exceeds maximum variable index");
569
570 andGateDefs.push_back({lhs, rhs0, rhs1, loc});
571 }
572
573 return success();
574}
575
576ParseResult AIGERParser::parseAndGatesBinary() {
577 // In binary format, AND gates are encoded with delta compression
578 // Each AND gate is encoded as: delta0 delta1
579 // where: rhs0 = lhs - delta0, rhs1 = rhs0 - delta1
580
581 LLVM_DEBUG(llvm::dbgs() << "Starting binary AND gate parsing\n");
582
583 // First AND gate LHS starts after inputs and latches
584 // Variables are numbered: 1, 2, ..., maxVarIndex
585 // Literals are: 2, 4, 6, ..., 2*maxVarIndex
586 // Inputs: 2, 4, ..., 2*numInputs
587 // Latches: 2*(numInputs+1), 2*(numInputs+2), ..., 2*(numInputs+numLatches)
588 // AND gates: 2*(numInputs+numLatches+1), 2*(numInputs+numLatches+2), ...
589 auto currentLHS = 2 * (numInputs + numLatches + 1);
590
591 LLVM_DEBUG(llvm::dbgs() << "First AND gate LHS should be: " << currentLHS
592 << "\n");
593
594 for (unsigned i = 0; i < numAnds; ++i) {
595 unsigned delta0, delta1;
596 SMLoc loc = lexer.getCurrentLoc();
597 if (parseBinaryNumber(delta0) || parseBinaryNumber(delta1))
598 return emitError(loc, "failed to parse binary AND gate deltas");
599
600 auto lhs = static_cast<int64_t>(currentLHS);
601
602 // Check for underflow before subtraction
603 if (delta0 > lhs || delta1 > (lhs - delta0)) {
604 LLVM_DEBUG(llvm::dbgs() << "Delta underflow: lhs=" << lhs << ", delta0="
605 << delta0 << ", delta1=" << delta1 << "\n");
606 return emitError("invalid binary AND gate: delta causes underflow");
607 }
608
609 auto rhs0 = lhs - delta0;
610 auto rhs1 = rhs0 - delta1;
611
612 LLVM_DEBUG(llvm::dbgs() << "Binary AND Gate " << i << ": " << lhs << " = "
613 << rhs0 << " & " << rhs1 << " (deltas: " << delta0
614 << ", " << delta1 << ")\n");
615
616 if (lhs / 2 > maxVarIndex || rhs0 / 2 > maxVarIndex ||
617 rhs1 / 2 > maxVarIndex)
618 return emitError(
619 "binary AND gate literal exceeds maximum variable index");
620
621 assert(lhs > rhs0 && rhs0 >= rhs1 &&
622 "invalid binary AND gate: ordering constraint violated");
623
624 andGateDefs.push_back({static_cast<unsigned>(lhs),
625 static_cast<unsigned>(rhs0),
626 static_cast<unsigned>(rhs1), loc});
627 currentLHS += 2; // Next AND gate LHS
628 }
629
630 return success();
631}
632
633ParseResult AIGERParser::parseSymbolTable() {
634 // Symbol table is optional and starts with 'i', 'l', or 'o' followed by
635 // position
636 while (!lexer.isAtEOF()) {
637 auto token = lexer.peekToken();
638 if (token.kind != AIGERTokenKind::Identifier)
639 break;
640 (void)lexer.nextToken();
641
642 char symbolType = token.spelling.front();
643 if (symbolType != 'i' && symbolType != 'l' && symbolType != 'o')
644 break;
645
646 unsigned literal;
647 if (token.spelling.drop_front().getAsInteger(10, literal))
648 return emitError("failed to parse symbol position");
649
650 SymbolKind kind;
651 switch (symbolType) {
652 case 'i':
653 kind = SymbolKind::Input;
654 break;
655 case 'l':
656 kind = SymbolKind::Latch;
657 break;
658 case 'o':
659 kind = SymbolKind::Output;
660 break;
661 }
662
663 auto nextToken = lexer.lexAsSymbol();
664 if (nextToken.kind != AIGERTokenKind::Identifier)
665 return emitError("expected symbol name");
666
667 LLVM_DEBUG(llvm::dbgs()
668 << "Symbol " << literal << ": " << nextToken.spelling << "\n");
669
670 symbolTable[{kind, literal}] = StringAttr::get(context, nextToken.spelling);
671 if (parseNewLine())
672 return failure();
673 }
674
675 return success();
676}
677
678ParseResult AIGERParser::parseComments() {
679 // Comments start with 'c' and continue to end of file
680 auto token = lexer.peekToken();
681 if (token.kind == AIGERTokenKind::Identifier && token.spelling == "c") {
682 // Skip comments for now
683 return success();
684 }
685
686 return success();
687}
688
689Value AIGERParser::getLiteralValue(unsigned literal,
690 DenseMap<unsigned, Backedge> &backedges,
691 Location loc) {
692 LLVM_DEBUG(llvm::dbgs() << "Getting value for literal " << literal << "\n");
693
694 // Handle constants
695 if (literal == 0) {
696 // FALSE constant
698 builder, loc, builder.getI1Type(),
699 builder.getIntegerAttr(builder.getI1Type(), 0));
700 }
701
702 if (literal == 1) {
703 // TRUE constant
705 builder, loc, builder.getI1Type(),
706 builder.getIntegerAttr(builder.getI1Type(), 1));
707 }
708
709 // Extract variable and inversion
710 unsigned variable = literal / 2;
711 bool inverted = literal % 2;
712 unsigned baseLiteral = variable * 2;
713
714 LLVM_DEBUG(llvm::dbgs() << " Variable: " << variable
715 << ", inverted: " << inverted
716 << ", baseLiteral: " << baseLiteral << "\n");
717
718 // Validate literal bounds
719 if (variable > maxVarIndex) {
720 LLVM_DEBUG(llvm::dbgs() << " ERROR: Variable " << variable
721 << " exceeds maxVarIndex " << maxVarIndex << "\n");
722 return nullptr;
723 }
724
725 // Look up the backedge for this literal
726 auto backedgeIt = backedges.find(baseLiteral);
727 if (backedgeIt == backedges.end()) {
728 LLVM_DEBUG(llvm::dbgs() << " ERROR: No backedge found for literal "
729 << baseLiteral << "\n");
730 return nullptr; // Error: undefined literal
731 }
732
733 Value baseValue = backedgeIt->second;
734 if (!baseValue) {
735 LLVM_DEBUG(llvm::dbgs() << " ERROR: Backedge value is null for literal "
736 << baseLiteral << "\n");
737 return nullptr;
738 }
739
740 // Apply inversion if needed
741 if (inverted) {
742 // Create an inverter using synth.aig.and_inv with single input
743 SmallVector<bool> inverts = {true};
744 return aig::AndInverterOp::create(builder, loc, builder.getI1Type(),
745 ValueRange{baseValue}, inverts);
746 }
747
748 return baseValue;
749}
750
751ParseResult AIGERParser::createModule() {
752
753 // Create the top-level module
754 std::string moduleName = options.topLevelModule;
755 if (moduleName.empty())
756 moduleName = "aiger_top";
757
758 // Set insertion point to the provided module
759 builder.setInsertionPointToStart(module.getBody());
760
761 // Build input/output port info
762 SmallVector<hw::PortInfo> ports;
763
764 // Add input ports
765 for (unsigned i = 0; i < numInputs; ++i) {
766 hw::PortInfo port;
767 auto name = symbolTable.lookup({SymbolKind::Input, i});
768 port.name =
769 name ? name : builder.getStringAttr("input_" + std::to_string(i));
770 port.type = builder.getI1Type();
771 port.dir = hw::ModulePort::Direction::Input;
772 port.argNum = i;
773 ports.push_back(port);
774 }
775
776 // Add output ports
777 for (unsigned i = 0; i < numOutputs; ++i) {
778 hw::PortInfo port;
779 auto name = symbolTable.lookup({SymbolKind::Output, i});
780 port.name =
781 name ? name : builder.getStringAttr("output_" + std::to_string(i));
782 port.type = builder.getI1Type();
783 port.dir = hw::ModulePort::Direction::Output;
784 port.argNum = numInputs + i;
785 ports.push_back(port);
786 }
787
788 // Add clock port if we have latches
789 if (numLatches > 0) {
790 hw::PortInfo clockPort;
791 clockPort.name = builder.getStringAttr("clock");
792 clockPort.type = seq::ClockType::get(builder.getContext());
793 clockPort.dir = hw::ModulePort::Direction::Input;
794 clockPort.argNum = numInputs + numOutputs;
795 ports.push_back(clockPort);
796 }
797
798 // Create the HW module
799 auto hwModule =
800 hw::HWModuleOp::create(builder, builder.getUnknownLoc(),
801 builder.getStringAttr(moduleName), ports);
802
803 // Set insertion point inside the module
804 builder.setInsertionPointToStart(hwModule.getBodyBlock());
805
806 // Get clock value if we have latches
807 Value clockValue;
808 if (numLatches > 0)
809 clockValue = hwModule.getBodyBlock()->getArgument(numInputs);
810
811 // Use BackedgeBuilder to handle all values uniformly
812 BackedgeBuilder bb(builder, builder.getUnknownLoc());
813 DenseMap<unsigned, Backedge> backedges;
814
815 // Create backedges for all literals (inputs, latches, AND gates)
816 for (unsigned i = 0; i < numInputs; ++i) {
817 auto literal = inputLiterals[i];
818 backedges[literal] = bb.get(builder.getI1Type());
819 }
820 for (auto [currentState, nextState, _] : latchDefs)
821 backedges[currentState] = bb.get(builder.getI1Type());
822
823 for (auto [lhs, rhs0, rhs1, loc] : andGateDefs)
824 backedges[lhs] = bb.get(builder.getI1Type());
825
826 // Set input values
827 for (unsigned i = 0; i < numInputs; ++i) {
828 auto inputValue = hwModule.getBodyBlock()->getArgument(i);
829 auto literal = inputLiterals[i];
830 backedges[literal].setValue(inputValue);
831 }
832
833 // Create latches (registers) with backedges for next state
834 for (auto [i, latchDef] : llvm::enumerate(latchDefs)) {
835 auto [currentState, nextState, loc] = latchDef;
836 // Get the backedge for the next state value
837 auto nextBackedge = bb.get(builder.getI1Type());
838
839 // Create the register with the backedge as input
840 auto regValue = seq::CompRegOp::create(
841 builder, lexer.translateLocation(loc), (Value)nextBackedge, clockValue);
842 if (auto name = symbolTable.lookup({SymbolKind::Latch, i}))
843 regValue.setNameAttr(name);
844
845 // Set the backedge for this latch's current state
846 backedges[currentState].setValue(regValue);
847 }
848
849 // Build AND gates using backedges to handle forward references
850 for (auto [lhs, rhs0, rhs1, loc] : andGateDefs) {
851 // Get or create backedges for operands
852 auto location = lexer.translateLocation(loc);
853 auto rhs0Value = getLiteralValue(rhs0 & ~1u, backedges, location);
854 auto rhs1Value = getLiteralValue(rhs1 & ~1u, backedges, location);
855
856 if (!rhs0Value || !rhs1Value)
857 return emitError(loc, "failed to get operand values for AND gate");
858
859 // Determine inversion for inputs
860 SmallVector<bool> inverts = {static_cast<bool>(rhs0 % 2),
861 static_cast<bool>(rhs1 % 2)};
862
863 // Create AND gate with potential inversions
864 auto andResult =
865 aig::AndInverterOp::create(builder, location, builder.getI1Type(),
866 ValueRange{rhs0Value, rhs1Value}, inverts);
867
868 // Set the backedge for this AND gate's result
869 backedges[lhs].setValue(andResult);
870 }
871
872 // Now resolve the latch next state connections.
873 // We need to update the CompRegOp operations with their actual next state
874 // values
875 for (auto [currentState, nextState, sourceLoc] : latchDefs) {
876 auto loc = lexer.translateLocation(sourceLoc);
877 auto nextValue = getLiteralValue(nextState, backedges, loc);
878 if (!nextValue)
879 return emitError(sourceLoc, "undefined literal in latch next state");
880
881 // Find the register operation for this latch and update its input
882 Value currentValue = backedges[currentState];
883 if (auto regOp = currentValue.getDefiningOp<seq::CompRegOp>())
884 regOp.getInputMutable().assign(nextValue);
885 else
886 return emitError(sourceLoc, "failed to find register for latch");
887 }
888
889 // Create output values
890 SmallVector<Value> outputValues;
891 for (auto [literal, sourceLoc] : outputLiterals) {
892 auto loc = lexer.translateLocation(sourceLoc);
893 auto outputValue = getLiteralValue(literal, backedges, loc);
894 if (!outputValue)
895 return emitError(sourceLoc, "undefined literal in output");
896 outputValues.push_back(outputValue);
897 }
898
899 // Create output operation
900 auto *outputOp = hwModule.getBodyBlock()->getTerminator();
901 outputOp->setOperands(outputValues);
902
903 return success();
904}
905
906//===----------------------------------------------------------------------===//
907// Public API Implementation
908//===----------------------------------------------------------------------===//
909
910LogicalResult circt::aiger::importAIGER(llvm::SourceMgr &sourceMgr,
911 MLIRContext *context,
912 mlir::TimingScope &ts, ModuleOp module,
913 const ImportAIGEROptions *options) {
914 // Load required dialects
915 context->loadDialect<hw::HWDialect>();
916 context->loadDialect<synth::SynthDialect>();
917 context->loadDialect<seq::SeqDialect>();
918
919 // Use default options if none provided
920 ImportAIGEROptions defaultOptions;
921 if (!options)
922 options = &defaultOptions;
923
924 // Create parser and parse the file
925 AIGERParser parser(sourceMgr, context, module, *options);
926 return parser.parse();
927}
928
929//===----------------------------------------------------------------------===//
930// Translation Registration
931//===----------------------------------------------------------------------===//
932
934 static mlir::TranslateToMLIRRegistration fromAIGER(
935 "import-aiger", "import AIGER file",
936 [](llvm::SourceMgr &sourceMgr, MLIRContext *context) {
937 mlir::TimingScope ts;
939 ModuleOp::create(UnknownLoc::get(context)));
940 ImportAIGEROptions options;
941 if (failed(importAIGER(sourceMgr, context, ts, module.get(), &options)))
942 module = {};
943 return module;
944 });
945}
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static StringAttr getMainBufferNameIdentifier(const llvm::SourceMgr &sourceMgr, MLIRContext *context)
Definition FIRLexer.cpp:150
@ Input
Definition HW.h:42
@ Output
Definition HW.h:42
Instantiate one of these and use it to build typed backedges.
create(data_type, value)
Definition hw.py:433
create(cls, result_type, reset=None, reset_value=None, name=None, sym_name=None, **kwargs)
Definition seq.py:157
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.
void registerImportAIGERTranslation()
Register the import-aiger MLIR translation.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:55
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Options for AIGER import.
Definition ImportAIGER.h:29
mlir::Type type
Definition HWTypes.h:31
mlir::StringAttr name
Definition HWTypes.h:30
This holds the name, type, direction of a module's ports.
size_t argNum
This is the argument index or the result index depending on the direction.