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