CIRCT 23.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
20#include "mlir/IR/Builders.h"
21#include "mlir/IR/BuiltinOps.h"
22#include "mlir/IR/Diagnostics.h"
23#include "mlir/IR/Location.h"
24#include "mlir/IR/MLIRContext.h"
25#include "mlir/Support/FileUtilities.h"
26#include "mlir/Support/LogicalResult.h"
27#include "mlir/Support/Timing.h"
28#include "mlir/Tools/mlir-translate/Translation.h"
29#include "llvm/ADT/DenseMap.h"
30#include "llvm/ADT/StringExtras.h"
31#include "llvm/ADT/StringRef.h"
32#include "llvm/Support/Debug.h"
33#include "llvm/Support/ErrorHandling.h"
34#include "llvm/Support/LEB128.h"
35#include "llvm/Support/SMLoc.h"
36#include "llvm/Support/SourceMgr.h"
37#include "llvm/Support/raw_ostream.h"
38#include <cctype>
39#include <limits>
40#include <string>
41
42using namespace mlir;
43using namespace circt;
44using namespace circt::hw;
45using namespace circt::synth;
46using namespace circt::seq;
47using namespace circt::aiger;
48using namespace circt::verif;
49
50#define DEBUG_TYPE "import-aiger"
51
52namespace {
53
54/// AIGER token types for lexical analysis
55enum class AIGERTokenKind {
56 // Literals
57 Number,
58 Identifier,
59
60 // Special characters
61 Newline,
62 EndOfFile,
63
64 // Error
65 Error
66};
67
68/// Represents a token in the AIGER file
69struct AIGERToken {
70 AIGERTokenKind kind;
71 StringRef spelling;
72 SMLoc location;
73
74 AIGERToken(AIGERTokenKind kind, StringRef spelling, SMLoc location)
75 : kind(kind), spelling(spelling), location(location) {}
76};
77
78/// Simple lexer for AIGER files
79///
80/// This lexer handles both ASCII (.aag) and binary (.aig) AIGER formats.
81/// It provides basic tokenization for header parsing and symbol tables,
82/// while also supporting byte-level reading for binary format.
83class AIGERLexer {
84public:
85 AIGERLexer(const llvm::SourceMgr &sourceMgr, MLIRContext *context)
86 : sourceMgr(sourceMgr),
87 bufferNameIdentifier(getMainBufferNameIdentifier(sourceMgr, context)),
88 curBuffer(
89 sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID())->getBuffer()),
90 curPtr(curBuffer.begin()) {}
91
92 /// Get the next token
93 AIGERToken nextToken();
94
95 /// Lex the current position as a symbol (used for symbol table parsing)
96 AIGERToken lexAsSymbol();
97
98 /// Peek at the current token without consuming it
99 AIGERToken peekToken();
100
101 /// Check if we're at end of file
102 bool isAtEOF() const { return curPtr >= curBuffer.end(); }
103
104 /// Read a LEB128 encoded unsigned integer
105 ParseResult readLEB128(unsigned &result);
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
196 unsigned numBad = 0;
197 unsigned numConstraints = 0;
198 unsigned numJustice = 0;
199 unsigned numFairness = 0;
200
201 bool isBinaryFormat = false;
202
203 // A mapping from {kind, index} -> symbol where kind is 0 for inputs, 1 for
204 // latches, and 2 for outputs.
205 enum SymbolKind : unsigned {
206 Input,
207 Latch,
208 Output,
209 Bad,
210 Constraint,
211 Justice,
212 Fairness
213 };
214 DenseMap<std::pair<SymbolKind, unsigned>, StringAttr> symbolTable;
215
216 // Parsed data storage
217 SmallVector<unsigned> inputLiterals;
218 SmallVector<std::tuple<unsigned, unsigned, SMLoc>>
219 latchDefs; // current, next, loc
220 SmallVector<std::pair<unsigned, SMLoc>> outputLiterals; // literal, loc
221 SmallVector<std::tuple<unsigned, unsigned, unsigned, SMLoc>>
222 andGateDefs; // lhs, rhs0, rhs1
223 SmallVector<std::pair<unsigned, SMLoc>> badStateLiterals; // literal, loc
224 SmallVector<std::pair<unsigned, SMLoc>> constraintLiterals; // literal, loc
225 SmallVector<std::pair<unsigned, SMLoc>> fairnessLiterals; // literal, loc
226 SmallVector<SmallVector<std::pair<unsigned, SMLoc>>>
227 justiceLiterals; // [property][literal, loc]
228
229 /// Parse the header line (format and counts)
230 ParseResult parseHeader();
231
232 /// Parse inputs section
233 ParseResult parseInputs();
234
235 /// Parse latches section
236 ParseResult parseLatches();
237
238 /// Parse outputs section
239 ParseResult parseOutputs();
240
241 /// Parse AND gates section (dispatches to ASCII or binary)
242 ParseResult parseAndGates();
243
244 /// Parse AND gates in ASCII format
245 ParseResult parseAndGatesASCII();
246
247 /// Parse AND gates in binary format with delta compression
248 ParseResult parseAndGatesBinary();
249
250 /// Parse symbol table (optional)
251 ParseResult parseSymbolTable();
252
253 /// Parse comments (optional)
254 ParseResult parseComments();
255
256 // Parse bad states properties (optional)
257 ParseResult parseBadStates();
258
259 // Parse invariant constraints (optional)
260 ParseResult parseConstraints();
261
262 // Parse justice properties (optional)
263 ParseResult parseJustice();
264
265 // Parse fairness constraints (optional)
266 ParseResult parseFairness();
267
268 /// Convert AIGER literal to MLIR value using backedges
269 ///
270 /// \param literal The AIGER literal (variable * 2 + inversion)
271 /// \param backedges Map from literals to backedge values
272 /// \param loc Location for created operations
273 /// \return The MLIR value corresponding to the literal, or nullptr on error
274 Value getLiteralValue(unsigned literal,
275 DenseMap<unsigned, Backedge> &backedges, Location loc);
276
277 /// Create the top-level HW module from parsed data
278 ParseResult createModule();
279
280 InFlightDiagnostic emitError(llvm::SMLoc loc, const Twine &message) {
281 return mlir::emitError(lexer.translateLocation(loc), message);
282 }
283
284 /// Emit error at current location
285 InFlightDiagnostic emitError(const Twine &message) {
286 return emitError(lexer.getCurrentLoc(), message);
287 }
288
289 InFlightDiagnostic emitWarning(llvm::SMLoc loc, const Twine &message) {
290 return mlir::emitWarning(lexer.translateLocation(loc), message);
291 }
292
293 /// Emit warning at current location
294 InFlightDiagnostic emitWarning(const Twine &message) {
295 return emitWarning(lexer.getCurrentLoc(), message);
296 }
297
298 /// Parse a number token into result
299 ParseResult parseNumber(unsigned &result, SMLoc *loc = nullptr);
300
301 /// Parse a decimal number (optional)
302 ParseResult parseOptionalNumber(unsigned &result, SMLoc *loc = nullptr);
303
304 // Parse a section of AIGER literals, one per line
305 // Used for bad state, invariant constraint, and fairness constraint
306 ParseResult
307 parseLiteralSection(unsigned count,
308 SmallVectorImpl<std::pair<unsigned, SMLoc>> &out,
309 StringRef sectionName);
310
311 /// Parse a binary encoded number (variable-length encoding)
312 ParseResult parseBinaryNumber(unsigned &result);
313
314 /// Expect and consume a newline token
315 ParseResult parseNewLine();
316};
317
318} // anonymous namespace
319
320//===----------------------------------------------------------------------===//
321// AIGERLexer Implementation
322//===----------------------------------------------------------------------===//
323
324void AIGERLexer::skipWhitespace() {
325 while (curPtr < curBuffer.end()) {
326 // NOTE: Don't use llvm::isSpace here because it also skips '\n'.
327 if (*curPtr == ' ' || *curPtr == '\t' || *curPtr == '\r') {
328 ++curPtr;
329 continue;
330 }
331
332 // Treat "//" as whitespace. This is not part of the AIGER format, but we
333 // support it for FileCheck tests.
334 if (*curPtr == '/' &&
335 (curPtr + 1 < curBuffer.end() && *(curPtr + 1) == '/')) {
336 skipUntilNewline();
337 continue;
338 }
339 break;
340 }
341}
342
343void AIGERLexer::skipUntilNewline() {
344 while (curPtr < curBuffer.end() && *curPtr != '\n')
345 ++curPtr;
346 if (curPtr < curBuffer.end() && *curPtr == '\n')
347 ++curPtr;
348}
349
350AIGERToken AIGERLexer::lexNumber() {
351 const char *start = curPtr;
352 while (curPtr < curBuffer.end() && llvm::isDigit(*curPtr))
353 ++curPtr;
354 return makeToken(AIGERTokenKind::Number, start);
355}
356
357AIGERToken AIGERLexer::lexIdentifier() {
358 const char *start = curPtr;
359 while (curPtr < curBuffer.end() && (llvm::isAlnum(*curPtr) || *curPtr == '_'))
360 ++curPtr;
361
362 StringRef spelling(start, curPtr - start);
363 AIGERTokenKind kind = AIGERTokenKind::Identifier;
364
365 return makeToken(kind, start);
366}
367
368AIGERToken AIGERLexer::nextToken() {
369 skipWhitespace();
370
371 auto impl = [this]() {
372 if (curPtr >= curBuffer.end())
373 return makeToken(AIGERTokenKind::EndOfFile, curPtr);
374
375 const char *start = curPtr;
376 char c = *curPtr++;
377
378 switch (c) {
379 case '\n':
380 return makeToken(AIGERTokenKind::Newline, start);
381 case '\r':
382 case ' ':
383 case '\t':
384 llvm_unreachable("Whitespace should have been skipped");
385 return makeToken(AIGERTokenKind::Error, start);
386 default:
387 if (llvm::isDigit(c)) {
388 --curPtr; // Back up to re-lex the number
389 return lexNumber();
390 }
391 if (llvm::isAlpha(c) || c == '_') {
392 --curPtr; // Back up to re-lex the identifier
393 return lexIdentifier();
394 }
395 assert((c != '/' || *curPtr != '/') && "// should have been skipped");
396 return makeToken(AIGERTokenKind::Error, start);
397 }
398 };
399
400 auto token = impl();
401 return token;
402}
403
404AIGERToken AIGERLexer::lexAsSymbol() {
405 skipWhitespace();
406 const char *start = curPtr;
407 while (curPtr < curBuffer.end() &&
408 (llvm::isPrint(*curPtr) && !llvm::isSpace(*curPtr)))
409 ++curPtr;
410 return makeToken(AIGERTokenKind::Identifier, start);
411}
412
413AIGERToken AIGERLexer::peekToken() {
414 const char *savedPtr = curPtr;
415 AIGERToken token = nextToken();
416 curPtr = savedPtr;
417 return token;
418}
419
420ParseResult AIGERLexer::readLEB128(unsigned &result) {
421 unsigned len;
422 uint64_t value =
423 llvm::decodeULEB128(reinterpret_cast<const uint8_t *>(curPtr), &len,
424 reinterpret_cast<const uint8_t *>(curBuffer.end()));
425 if (len == 0 || value > std::numeric_limits<unsigned>::max())
426 return failure();
427 curPtr += len;
428 result = static_cast<unsigned>(value);
429 return success();
430}
431
432//===----------------------------------------------------------------------===//
433// AIGERParser Implementation
434//===----------------------------------------------------------------------===//
435
436ParseResult AIGERParser::parse() {
437 if (parseHeader() || parseInputs() || parseLatches() || parseOutputs() ||
438 parseBadStates() || parseConstraints() || parseJustice() ||
439 parseFairness() || parseAndGates() || parseSymbolTable() ||
440 parseComments())
441 return failure();
442 // Create the final module
443 return createModule();
444}
445
446ParseResult AIGERParser::parseNumber(unsigned &result, SMLoc *loc) {
447 auto token = lexer.nextToken();
448 if (loc)
449 *loc = token.location;
450
451 if (token.kind != AIGERTokenKind::Number)
452 return emitError(token.location, "expected number");
453
454 if (token.spelling.getAsInteger(10, result))
455 return emitError(token.location, "invalid number format");
456
457 return success();
458}
459
460ParseResult AIGERParser::parseOptionalNumber(unsigned &result, SMLoc *loc) {
461 auto token = lexer.peekToken();
462 if (token.kind == AIGERTokenKind::Newline ||
463 token.kind == AIGERTokenKind::EndOfFile)
464 return success();
465
466 if (token.kind != AIGERTokenKind::Number)
467 return emitError(token.location, "expected number");
468 return parseNumber(result, loc);
469}
470
471ParseResult AIGERParser::parseLiteralSection(
472 unsigned count, SmallVectorImpl<std::pair<unsigned, SMLoc>> &out,
473 StringRef sectionName) {
474 LLVM_DEBUG(llvm::dbgs() << "Parsing " << count << " " << sectionName
475 << "s\n");
476 for (unsigned i = 0; i < count; ++i) {
477 unsigned literal;
478 SMLoc loc;
479 if (parseNumber(literal, &loc) || parseNewLine())
480 return emitError(loc, "failed to parse " + sectionName + " literal");
481
482 LLVM_DEBUG(llvm::dbgs()
483 << sectionName << " " << i << ": " << literal << "\n");
484 out.push_back({literal, loc});
485 }
486 return success();
487}
488
489ParseResult AIGERParser::parseBinaryNumber(unsigned &result) {
490 return lexer.readLEB128(result);
491}
492
493ParseResult AIGERParser::parseHeader() {
494 LLVM_DEBUG(llvm::dbgs() << "Parsing AIGER header\n");
495
496 // Parse format identifier (aag or aig)
497 while (lexer.peekToken().kind != AIGERTokenKind::Identifier)
498 lexer.nextToken();
499
500 auto formatToken = lexer.nextToken();
501 if (formatToken.spelling == "aag") {
502 isBinaryFormat = false;
503 LLVM_DEBUG(llvm::dbgs() << "Format: aag (ASCII)\n");
504 } else if (formatToken.spelling == "aig") {
505 isBinaryFormat = true;
506 LLVM_DEBUG(llvm::dbgs() << "Format: aig (binary)\n");
507 } else {
508 return emitError(formatToken.location,
509 "expected 'aag' or 'aig' format identifier");
510 }
511
512 // Parse M I L O A (numbers separated by spaces)
513 SMLoc loc;
514 if (parseNumber(maxVarIndex, &loc))
515 return emitError(loc, "failed to parse M (max variable index)");
516
517 if (parseNumber(numInputs, &loc))
518 return emitError(loc, "failed to parse I (number of inputs)");
519
520 if (parseNumber(numLatches, &loc))
521 return emitError(loc, "failed to parse L (number of latches)");
522
523 if (parseNumber(numOutputs, &loc))
524 return emitError(loc, "failed to parse O (number of outputs)");
525
526 if (parseNumber(numAnds, &loc))
527 return emitError(loc, "failed to parse A (number of AND gates)");
528
529 // Parse an optional header number field (B C J F)
530 // These fields may be ommitted only as a trailing suffix
531
532 if (parseOptionalNumber(numBad, &loc))
533 return emitError(loc, "failed to parse B (number of bad states)");
534
535 if (parseOptionalNumber(numConstraints, &loc))
536 return emitError(loc, "failed to parse C (number of constraints)");
537
538 if (parseOptionalNumber(numJustice, &loc))
539 return emitError(loc, "failed to parse J (number of justice properties)");
540
541 if (parseOptionalNumber(numFairness, &loc))
542 return emitError(loc, "failed to parse F (number of fairness constraints)");
543
544 LLVM_DEBUG(llvm::dbgs() << "Header: M=" << maxVarIndex << " I=" << numInputs
545 << " L=" << numLatches << " O=" << numOutputs
546 << " A=" << numAnds << " B=" << numBad
547 << " C=" << numConstraints << " J=" << numJustice
548 << " F=" << numFairness << "\n");
549
550 // Expect newline after header
551 return parseNewLine();
552}
553
554ParseResult AIGERParser::parseNewLine() {
555 auto token = lexer.nextToken();
556 if (token.kind != AIGERTokenKind::Newline)
557 return emitError(token.location, "expected newline");
558
559 return success();
560}
561
562ParseResult AIGERParser::parseInputs() {
563 LLVM_DEBUG(llvm::dbgs() << "Parsing " << numInputs << " inputs\n");
564 if (isBinaryFormat) {
565 // In binary format, inputs are implicit (literals 2, 4, 6, ...)
566 for (unsigned i = 0; i < numInputs; ++i)
567 inputLiterals.push_back(2 * (i + 1));
568 return success();
569 }
570
571 for (unsigned i = 0; i < numInputs; ++i) {
572 unsigned literal;
573 SMLoc loc;
574 if (parseNumber(literal, &loc) || parseNewLine())
575 return emitError(loc, "failed to parse input literal");
576 inputLiterals.push_back(literal);
577 }
578
579 return success();
580}
581
582ParseResult AIGERParser::parseLatches() {
583 LLVM_DEBUG(llvm::dbgs() << "Parsing " << numLatches << " latches\n");
584 if (isBinaryFormat) {
585 // In binary format, latches are implicit (literals 2, 4, 6, ...)
586 for (unsigned i = 0; i < numLatches; ++i) {
587 unsigned literal;
588 SMLoc loc;
589 if (parseNumber(literal, &loc))
590 return emitError(loc, "failed to parse latch next state literal");
591
592 // Consume optional init value (AIGER 1.9)
593 unsigned init = 0;
594 if (parseOptionalNumber(init))
595 return emitError("failed to parse latch initial value");
596
597 latchDefs.push_back({2 * (i + 1 + numInputs), literal, loc});
598
599 // Expect newline after each latch next state
600 if (parseNewLine())
601 return failure();
602 }
603 return success();
604 }
605
606 // Parse latch definitions: current_state next_state
607 for (unsigned i = 0; i < numLatches; ++i) {
608 unsigned currentState, nextState;
609 SMLoc loc;
610 if (parseNumber(currentState, &loc) || parseNumber(nextState))
611 return emitError(loc, "failed to parse latch definition");
612
613 // Consume optional init value (AIGER 1.9)
614 unsigned init = 0;
615 if (parseOptionalNumber(init))
616 return emitError("failed to parse latch initial value");
617
618 if (parseNewLine()) {
619 return emitError(loc, "failed to parse latch definition");
620 }
621
622 LLVM_DEBUG(llvm::dbgs() << "Latch " << i << ": " << currentState << " -> "
623 << nextState << "\n");
624
625 // Validate current state literal (should be even and positive)
626 if (currentState % 2 != 0 || currentState == 0)
627 return emitError(loc, "invalid latch current state literal");
628
629 latchDefs.push_back({currentState, nextState, loc});
630 }
631
632 return success();
633}
634
635ParseResult AIGERParser::parseOutputs() {
636 LLVM_DEBUG(llvm::dbgs() << "Parsing " << numOutputs << " outputs\n");
637 // NOTE: Parsing is same for binary and ASCII formats
638 // Parse output literals
639 for (unsigned i = 0; i < numOutputs; ++i) {
640 unsigned literal;
641 SMLoc loc;
642 if (parseNumber(literal, &loc) || parseNewLine())
643 return emitError(loc, "failed to parse output literal");
644
645 LLVM_DEBUG(llvm::dbgs() << "Output " << i << ": " << literal << "\n");
646
647 // Output literals can be any valid literal (including inverted)
648 outputLiterals.push_back({literal, loc});
649 }
650
651 return success();
652}
653
654ParseResult AIGERParser::parseAndGates() {
655 LLVM_DEBUG(llvm::dbgs() << "Parsing " << numAnds << " AND gates\n");
656
657 if (isBinaryFormat) {
658 return parseAndGatesBinary();
659 }
660 return parseAndGatesASCII();
661}
662
663ParseResult AIGERParser::parseAndGatesASCII() {
664 // Parse AND gate definitions: lhs rhs0 rhs1
665 for (unsigned i = 0; i < numAnds; ++i) {
666 unsigned lhs, rhs0, rhs1;
667 SMLoc loc;
668 if (parseNumber(lhs, &loc) || parseNumber(rhs0) || parseNumber(rhs1) ||
669 parseNewLine())
670 return emitError(loc, "failed to parse AND gate definition");
671
672 LLVM_DEBUG(llvm::dbgs() << "AND Gate " << i << ": " << lhs << " = " << rhs0
673 << " & " << rhs1 << "\n");
674
675 // Validate LHS (should be even and positive)
676 if (lhs % 2 != 0 || lhs == 0)
677 return emitError(loc, "invalid AND gate LHS literal");
678
679 // Validate literal bounds
680 if (lhs / 2 > maxVarIndex || rhs0 / 2 > maxVarIndex ||
681 rhs1 / 2 > maxVarIndex)
682 return emitError(loc, "AND gate literal exceeds maximum variable index");
683
684 andGateDefs.push_back({lhs, rhs0, rhs1, loc});
685 }
686
687 return success();
688}
689
690ParseResult AIGERParser::parseAndGatesBinary() {
691 // In binary format, AND gates are encoded with delta compression
692 // Each AND gate is encoded as: delta0 delta1
693 // where: rhs0 = lhs - delta0, rhs1 = rhs0 - delta1
694
695 LLVM_DEBUG(llvm::dbgs() << "Starting binary AND gate parsing\n");
696
697 // First AND gate LHS starts after inputs and latches
698 // Variables are numbered: 1, 2, ..., maxVarIndex
699 // Literals are: 2, 4, 6, ..., 2*maxVarIndex
700 // Inputs: 2, 4, ..., 2*numInputs
701 // Latches: 2*(numInputs+1), 2*(numInputs+2), ..., 2*(numInputs+numLatches)
702 // AND gates: 2*(numInputs+numLatches+1), 2*(numInputs+numLatches+2), ...
703 auto currentLHS = 2 * (numInputs + numLatches + 1);
704
705 LLVM_DEBUG(llvm::dbgs() << "First AND gate LHS should be: " << currentLHS
706 << "\n");
707
708 for (unsigned i = 0; i < numAnds; ++i) {
709 unsigned delta0, delta1;
710 SMLoc loc = lexer.getCurrentLoc();
711 if (parseBinaryNumber(delta0) || parseBinaryNumber(delta1))
712 return emitError(loc, "failed to parse binary AND gate deltas");
713
714 auto lhs = static_cast<int64_t>(currentLHS);
715
716 // Check for underflow before subtraction
717 if (delta0 > lhs || delta1 > (lhs - delta0)) {
718 LLVM_DEBUG(llvm::dbgs() << "Delta underflow: lhs=" << lhs << ", delta0="
719 << delta0 << ", delta1=" << delta1 << "\n");
720 return emitError("invalid binary AND gate: delta causes underflow");
721 }
722
723 auto rhs0 = lhs - delta0;
724 auto rhs1 = rhs0 - delta1;
725
726 LLVM_DEBUG(llvm::dbgs() << "Binary AND Gate " << i << ": " << lhs << " = "
727 << rhs0 << " & " << rhs1 << " (deltas: " << delta0
728 << ", " << delta1 << ")\n");
729
730 if (lhs / 2 > maxVarIndex || rhs0 / 2 > maxVarIndex ||
731 rhs1 / 2 > maxVarIndex)
732 return emitError(
733 "binary AND gate literal exceeds maximum variable index");
734
735 assert(lhs > rhs0 && rhs0 >= rhs1 &&
736 "invalid binary AND gate: ordering constraint violated");
737
738 andGateDefs.push_back({static_cast<unsigned>(lhs),
739 static_cast<unsigned>(rhs0),
740 static_cast<unsigned>(rhs1), loc});
741 currentLHS += 2; // Next AND gate LHS
742 }
743
744 return success();
745}
746
747ParseResult AIGERParser::parseBadStates() {
748 // Parse bad state literals
749 return parseLiteralSection(numBad, badStateLiterals, "bad state");
750}
751
752ParseResult AIGERParser::parseConstraints() {
753 // Parse invariant constraint literals
754 return parseLiteralSection(numConstraints, constraintLiterals,
755 "invariant constraint");
756}
757
758ParseResult AIGERParser::parseJustice() {
759 if (numJustice > 0)
760 emitWarning("AIGER justice properties are not yet supported");
761
762 LLVM_DEBUG(llvm::dbgs() << "Parsing " << numJustice
763 << " number of justice properties \n");
764 SmallVector<unsigned> sizes;
765 // Parse justice property sizes
766 for (unsigned i = 0; i < numJustice; ++i) {
767 unsigned size;
768 SMLoc loc;
769 if (parseNumber(size, &loc) || parseNewLine())
770 return emitError(loc, "failed to parse justice property size");
771 sizes.push_back(size);
772 LLVM_DEBUG(llvm::dbgs()
773 << "Justice property " << i << ": " << size << '\n');
774 }
775 justiceLiterals.resize(numJustice);
776 // Parse Justice property literals (grouped by each property size)
777 for (unsigned i = 0; i < numJustice; ++i) {
778 for (unsigned j = 0; j < sizes[i]; ++j) {
779 unsigned literal;
780 SMLoc loc;
781 if (parseNumber(literal, &loc) || parseNewLine())
782 return emitError(loc, "failed to parse justice literal");
783
784 justiceLiterals[i].push_back({literal, loc});
785 LLVM_DEBUG(llvm::dbgs()
786 << "Justice " << i << "[" << j << "]: " << literal << '\n');
787 }
788 }
789 return success();
790}
791
792ParseResult AIGERParser::parseFairness() {
793 if (numFairness > 0)
794 emitWarning("AIGER fairness constraints are not yet supported");
795 // Parse fairness constraint literals
796 return parseLiteralSection(numFairness, fairnessLiterals,
797 "fairness constraint");
798}
799
800ParseResult AIGERParser::parseSymbolTable() {
801
802 // Symbol table is optional and starts with 'i', 'l', or 'o' followed by
803 // position
804 while (!lexer.isAtEOF()) {
805 auto token = lexer.peekToken();
806 if (token.kind != AIGERTokenKind::Identifier)
807 break;
808 (void)lexer.nextToken();
809
810 char symbolType = token.spelling.front();
811
812 // Break on standalone 'c' to avoid consuming the comment section marker
813 // as a constraint symbol (e.g. 'c0', 'c1').
814 if (token.spelling == "c")
815 break;
816
817 if (symbolType != 'i' && symbolType != 'l' && symbolType != 'o' &&
818 symbolType != 'b' && symbolType != 'c' && symbolType != 'j' &&
819 symbolType != 'f')
820 break;
821
822 unsigned literal;
823 if (token.spelling.drop_front().getAsInteger(10, literal))
824 return emitError("failed to parse symbol position");
825
826 SymbolKind kind;
827 switch (symbolType) {
828 case 'i':
829 kind = SymbolKind::Input;
830 break;
831 case 'l':
832 kind = SymbolKind::Latch;
833 break;
834 case 'o':
835 kind = SymbolKind::Output;
836 break;
837 case 'b':
838 kind = SymbolKind::Bad;
839 break;
840 case 'c':
841 kind = SymbolKind::Constraint;
842 break;
843 case 'j':
844 kind = SymbolKind::Justice;
845 break;
846 case 'f':
847 kind = SymbolKind::Fairness;
848 break;
849 }
850
851 auto nextToken = lexer.lexAsSymbol();
852 if (nextToken.kind != AIGERTokenKind::Identifier)
853 return emitError("expected symbol name");
854
855 LLVM_DEBUG(llvm::dbgs()
856 << "Symbol " << literal << ": " << nextToken.spelling << "\n");
857
858 symbolTable[{kind, literal}] = StringAttr::get(context, nextToken.spelling);
859 if (parseNewLine())
860 return failure();
861 }
862
863 return success();
864}
865
866ParseResult AIGERParser::parseComments() {
867 // Comments start with 'c' and continue to end of file
868 auto token = lexer.peekToken();
869 if (token.kind == AIGERTokenKind::Identifier && token.spelling == "c") {
870 // Skip comments for now
871 return success();
872 }
873
874 return success();
875}
876
877Value AIGERParser::getLiteralValue(unsigned literal,
878 DenseMap<unsigned, Backedge> &backedges,
879 Location loc) {
880 LLVM_DEBUG(llvm::dbgs() << "Getting value for literal " << literal << "\n");
881
882 // Handle constants
883 if (literal == 0) {
884 // FALSE constant
886 builder, loc, builder.getI1Type(),
887 builder.getIntegerAttr(builder.getI1Type(), 0));
888 }
889
890 if (literal == 1) {
891 // TRUE constant
893 builder, loc, builder.getI1Type(),
894 builder.getIntegerAttr(builder.getI1Type(), 1));
895 }
896
897 // Extract variable and inversion
898 unsigned variable = literal / 2;
899 bool inverted = literal % 2;
900 unsigned baseLiteral = variable * 2;
901
902 LLVM_DEBUG(llvm::dbgs() << " Variable: " << variable
903 << ", inverted: " << inverted
904 << ", baseLiteral: " << baseLiteral << "\n");
905
906 // Validate literal bounds
907 if (variable > maxVarIndex) {
908 LLVM_DEBUG(llvm::dbgs() << " ERROR: Variable " << variable
909 << " exceeds maxVarIndex " << maxVarIndex << "\n");
910 return nullptr;
911 }
912
913 // Look up the backedge for this literal
914 auto backedgeIt = backedges.find(baseLiteral);
915 if (backedgeIt == backedges.end()) {
916 LLVM_DEBUG(llvm::dbgs() << " ERROR: No backedge found for literal "
917 << baseLiteral << "\n");
918 return nullptr; // Error: undefined literal
919 }
920
921 Value baseValue = backedgeIt->second;
922 if (!baseValue) {
923 LLVM_DEBUG(llvm::dbgs() << " ERROR: Backedge value is null for literal "
924 << baseLiteral << "\n");
925 return nullptr;
926 }
927
928 // Apply inversion if needed
929 if (inverted) {
930 // Create an inverter using synth.aig.and_inv with single input
931 SmallVector<bool> inverts = {true};
932 return aig::AndInverterOp::create(builder, loc, builder.getI1Type(),
933 ValueRange{baseValue}, inverts);
934 }
935
936 return baseValue;
937}
938
939ParseResult AIGERParser::createModule() {
940 // Create the top-level module
941 std::string moduleName = options.topLevelModule;
942 if (moduleName.empty())
943 moduleName = "aiger_top";
944
945 // Set insertion point to the provided module
946 builder.setInsertionPointToStart(module.getBody());
947
948 // Build input/output port info
949 SmallVector<hw::PortInfo> ports;
950
951 // Add input ports
952 for (unsigned i = 0; i < numInputs; ++i) {
953 hw::PortInfo port;
954 auto name = symbolTable.lookup({SymbolKind::Input, i});
955 port.name =
956 name ? name : builder.getStringAttr("input_" + std::to_string(i));
957 port.type = builder.getI1Type();
958 port.dir = hw::ModulePort::Direction::Input;
959 port.argNum = i;
960 ports.push_back(port);
961 }
962
963 // Add output ports
964 for (unsigned i = 0; i < numOutputs; ++i) {
965 hw::PortInfo port;
966 auto name = symbolTable.lookup({SymbolKind::Output, i});
967 port.name =
968 name ? name : builder.getStringAttr("output_" + std::to_string(i));
969 port.type = builder.getI1Type();
970 port.dir = hw::ModulePort::Direction::Output;
971 port.argNum = numInputs + i;
972 ports.push_back(port);
973 }
974
975 // Add clock port if we have latches
976 if (numLatches > 0) {
977 hw::PortInfo clockPort;
978 clockPort.name = builder.getStringAttr("clock");
979 clockPort.type = seq::ClockType::get(builder.getContext());
980 clockPort.dir = hw::ModulePort::Direction::Input;
981 clockPort.argNum = numInputs + numOutputs;
982 ports.push_back(clockPort);
983 }
984
985 // Create the HW module
986 auto hwModule =
987 hw::HWModuleOp::create(builder, builder.getUnknownLoc(),
988 builder.getStringAttr(moduleName), ports);
989
990 // Set insertion point inside the module
991 builder.setInsertionPointToStart(hwModule.getBodyBlock());
992
993 // Get clock value if we have latches
994 Value clockValue;
995 if (numLatches > 0)
996 clockValue = hwModule.getBodyBlock()->getArgument(numInputs);
997
998 // Use BackedgeBuilder to handle all values uniformly
999 BackedgeBuilder bb(builder, builder.getUnknownLoc());
1000 DenseMap<unsigned, Backedge> backedges;
1001
1002 // Create backedges for all literals (inputs, latches, AND gates)
1003 for (unsigned i = 0; i < numInputs; ++i) {
1004 auto literal = inputLiterals[i];
1005 backedges[literal] = bb.get(builder.getI1Type());
1006 }
1007 for (auto [currentState, nextState, _] : latchDefs)
1008 backedges[currentState] = bb.get(builder.getI1Type());
1009
1010 for (auto [lhs, rhs0, rhs1, loc] : andGateDefs)
1011 backedges[lhs] = bb.get(builder.getI1Type());
1012
1013 // Set input values
1014 for (unsigned i = 0; i < numInputs; ++i) {
1015 auto inputValue = hwModule.getBodyBlock()->getArgument(i);
1016 auto literal = inputLiterals[i];
1017 backedges[literal].setValue(inputValue);
1018 }
1019
1020 // Create latches (registers) with backedges for next state
1021 for (auto [i, latchDef] : llvm::enumerate(latchDefs)) {
1022 auto [currentState, nextState, loc] = latchDef;
1023 // Get the backedge for the next state value
1024 auto nextBackedge = bb.get(builder.getI1Type());
1025
1026 // Create the register with the backedge as input
1027 auto regValue = seq::CompRegOp::create(
1028 builder, lexer.translateLocation(loc), (Value)nextBackedge, clockValue);
1029 if (auto name = symbolTable.lookup({SymbolKind::Latch, i}))
1030 regValue.setNameAttr(name);
1031
1032 // Set the backedge for this latch's current state
1033 backedges[currentState].setValue(regValue);
1034 }
1035
1036 // Build AND gates using backedges to handle forward references
1037 for (auto [lhs, rhs0, rhs1, loc] : andGateDefs) {
1038 // Get or create backedges for operands
1039 auto location = lexer.translateLocation(loc);
1040 auto rhs0Value = getLiteralValue(rhs0 & ~1u, backedges, location);
1041 auto rhs1Value = getLiteralValue(rhs1 & ~1u, backedges, location);
1042
1043 if (!rhs0Value || !rhs1Value)
1044 return emitError(loc, "failed to get operand values for AND gate");
1045
1046 // Determine inversion for inputs
1047 SmallVector<bool> inverts = {static_cast<bool>(rhs0 % 2),
1048 static_cast<bool>(rhs1 % 2)};
1049
1050 // Create AND gate with potential inversions
1051 auto andResult =
1052 aig::AndInverterOp::create(builder, location, builder.getI1Type(),
1053 ValueRange{rhs0Value, rhs1Value}, inverts);
1054
1055 // Set the backedge for this AND gate's result
1056 backedges[lhs].setValue(andResult);
1057 }
1058
1059 // Now resolve the latch next state connections.
1060 // We need to update the CompRegOp operations with their actual next state
1061 // values
1062 for (auto [currentState, nextState, sourceLoc] : latchDefs) {
1063 auto loc = lexer.translateLocation(sourceLoc);
1064 auto nextValue = getLiteralValue(nextState, backedges, loc);
1065 if (!nextValue)
1066 return emitError(sourceLoc, "undefined literal in latch next state");
1067
1068 // Find the register operation for this latch and update its input
1069 Value currentValue = backedges[currentState];
1070 if (auto regOp = currentValue.getDefiningOp<seq::CompRegOp>())
1071 regOp.getInputMutable().assign(nextValue);
1072 else
1073 return emitError(sourceLoc, "failed to find register for latch");
1074 }
1075
1076 // Create output values
1077 SmallVector<Value> outputValues;
1078 for (auto [literal, sourceLoc] : outputLiterals) {
1079 auto loc = lexer.translateLocation(sourceLoc);
1080 auto outputValue = getLiteralValue(literal, backedges, loc);
1081 if (!outputValue)
1082 return emitError(sourceLoc, "undefined literal in output");
1083 outputValues.push_back(outputValue);
1084 }
1085
1086 // Create output operation
1087 auto *outputOp = hwModule.getBodyBlock()->getTerminator();
1088 outputOp->setOperands(outputValues);
1089
1090 for (auto [i, badState] : llvm::enumerate(badStateLiterals)) {
1091 auto [literal, sourceLoc] = badState;
1092 auto loc = lexer.translateLocation(sourceLoc);
1093 auto value = getLiteralValue(literal, backedges, loc);
1094 if (!value)
1095 return emitError(sourceLoc, "undefined literal in bad state");
1096 // Negate it (assert literal does not hold)
1097 auto negated = aig::AndInverterOp::create(builder, loc, builder.getI1Type(),
1098 value, true);
1099 auto label = symbolTable.lookup({SymbolKind::Bad, i});
1100 verif::AssertOp::create(builder, loc, negated.getResult(), Value{}, label);
1101 }
1102
1103 for (auto [i, constraint] : llvm::enumerate(constraintLiterals)) {
1104 auto [literal, sourceLoc] = constraint;
1105 auto loc = lexer.translateLocation(sourceLoc);
1106 auto value = getLiteralValue(literal, backedges, loc);
1107 if (!value)
1108 return emitError(sourceLoc, "undefined literal in invariant constraint");
1109 // No negation (assume literal holds)
1110 auto label = symbolTable.lookup({SymbolKind::Constraint, i});
1111 verif::AssumeOp::create(builder, loc, value, Value{}, label);
1112 }
1113
1114 return success();
1115}
1116
1117//===----------------------------------------------------------------------===//
1118// Public API Implementation
1119//===----------------------------------------------------------------------===//
1120
1121LogicalResult circt::aiger::importAIGER(llvm::SourceMgr &sourceMgr,
1122 MLIRContext *context,
1123 mlir::TimingScope &ts, ModuleOp module,
1124 const ImportAIGEROptions *options) {
1125 // Load required dialects
1126 context->loadDialect<hw::HWDialect>();
1127 context->loadDialect<synth::SynthDialect>();
1128 context->loadDialect<seq::SeqDialect>();
1129 context->loadDialect<verif::VerifDialect>();
1130
1131 // Use default options if none provided
1132 ImportAIGEROptions defaultOptions;
1133 if (!options)
1134 options = &defaultOptions;
1135
1136 // Create parser and parse the file
1137 AIGERParser parser(sourceMgr, context, module, *options);
1138 return parser.parse();
1139}
1140
1141//===----------------------------------------------------------------------===//
1142// Translation Registration
1143//===----------------------------------------------------------------------===//
1144
1146 static mlir::TranslateToMLIRRegistration fromAIGER(
1147 "import-aiger", "import AIGER file",
1148 [](llvm::SourceMgr &sourceMgr, MLIRContext *context) {
1149 mlir::TimingScope ts;
1150 OwningOpRef<ModuleOp> module(
1151 ModuleOp::create(UnknownLoc::get(context)));
1152 ImportAIGEROptions options;
1153 if (failed(importAIGER(sourceMgr, context, ts, module.get(), &options)))
1154 module = {};
1155 return module;
1156 });
1157}
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:56
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.