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