CIRCT 23.0.0git
Loading...
Searching...
No Matches
ImportLiberty.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 Liberty file import functionality.
10//
11//===----------------------------------------------------------------------===//
12
19#include "mlir/IR/Attributes.h"
20#include "mlir/IR/Builders.h"
21#include "mlir/IR/BuiltinAttributes.h"
22#include "mlir/IR/BuiltinOps.h"
23#include "mlir/IR/BuiltinTypes.h"
24#include "mlir/IR/Diagnostics.h"
25#include "mlir/IR/Location.h"
26#include "mlir/IR/MLIRContext.h"
27#include "mlir/IR/Value.h"
28#include "mlir/Support/FileUtilities.h"
29#include "mlir/Support/LogicalResult.h"
30#include "mlir/Tools/mlir-translate/Translation.h"
31#include "llvm/ADT/STLExtras.h"
32#include "llvm/ADT/StringRef.h"
33#include "llvm/Support/LogicalResult.h"
34#include "llvm/Support/SourceMgr.h"
35
36#define DEBUG_TYPE "import-liberty"
37
38using namespace mlir;
39using namespace circt;
40using namespace circt::hw;
41using namespace circt::comb;
42
43namespace {
44
45/// Liberty token types for lexical analysis
46enum class LibertyTokenKind {
47 // Literals
48 Identifier,
49 String,
50 Number,
51
52 // Punctuation
53 LBrace, // {
54 RBrace, // }
55 LParen, // (
56 RParen, // )
57 Colon, // :
58 Semi, // ;
59 Comma, // ,
60
61 // Special
62 EndOfFile,
63 Error
64};
65
66StringRef stringifyTokenKind(LibertyTokenKind kind) {
67 switch (kind) {
68 case LibertyTokenKind::Identifier:
69 return "identifier";
70 case LibertyTokenKind::String:
71 return "string";
72 case LibertyTokenKind::Number:
73 return "number";
74 case LibertyTokenKind::LBrace:
75 return "'{'";
76 case LibertyTokenKind::RBrace:
77 return "'}'";
78 case LibertyTokenKind::LParen:
79 return "'('";
80 case LibertyTokenKind::RParen:
81 return "')'";
82 case LibertyTokenKind::Colon:
83 return "':'";
84 case LibertyTokenKind::Semi:
85 return "';'";
86 case LibertyTokenKind::Comma:
87 return "','";
88
89 case LibertyTokenKind::EndOfFile:
90 return "end of file";
91 case LibertyTokenKind::Error:
92 return "error";
93 }
94 return "unknown";
95}
96
97struct LibertyToken {
98 LibertyTokenKind kind;
99 StringRef spelling;
100 SMLoc location;
101
102 LibertyToken(LibertyTokenKind kind, StringRef spelling, SMLoc location)
103 : kind(kind), spelling(spelling), location(location) {}
104
105 bool is(LibertyTokenKind k) const { return kind == k; }
106};
107
108class LibertyLexer {
109public:
110 LibertyLexer(const llvm::SourceMgr &sourceMgr, MLIRContext *context)
111 : sourceMgr(sourceMgr), context(context),
112 curBuffer(
113 sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID())->getBuffer()),
114 curPtr(curBuffer.begin()) {}
115
116 LibertyToken nextToken();
117 LibertyToken peekToken();
118
119 SMLoc getCurrentLoc() const { return SMLoc::getFromPointer(curPtr); }
120
121 Location translateLocation(llvm::SMLoc loc) const {
122 unsigned mainFileID = sourceMgr.getMainFileID();
123 auto lineAndColumn = sourceMgr.getLineAndColumn(loc, mainFileID);
124 return FileLineColLoc::get(
125 StringAttr::get(
126 context,
127 sourceMgr.getMemoryBuffer(mainFileID)->getBufferIdentifier()),
128 lineAndColumn.first, lineAndColumn.second);
129 }
130
131 bool isAtEnd() const { return curPtr >= curBuffer.end(); }
132
133private:
134 const llvm::SourceMgr &sourceMgr;
135 MLIRContext *context;
136 StringRef curBuffer;
137 const char *curPtr;
138
139 void skipWhitespaceAndComments();
140 LibertyToken lexIdentifier();
141 LibertyToken lexString();
142 LibertyToken lexNumber();
143 LibertyToken makeToken(LibertyTokenKind kind, const char *start);
144};
145
146/// Helper class to parse boolean expressions from Liberty function attributes.
147///
148/// This parser implements a recursive-descent parser for Liberty boolean
149/// expressions with the following operator precedence (highest to lowest):
150/// 1. NOT (!, ') - Unary negation (prefix or postfix)
151/// 2. AND (*, &) - Conjunction
152/// 3. XOR (^) - Exclusive OR
153/// 4. OR (+, |) - Disjunction
154///
155/// Parentheses can be used to override precedence. The grammar is:
156/// OrExpr -> XorExpr { ('+'|'|') XorExpr }
157/// XorExpr -> AndExpr { '^' AndExpr }
158/// AndExpr -> UnaryExpr { ('*'|'&') UnaryExpr }
159/// UnaryExpr -> '!' UnaryExpr | '(' OrExpr ')' ['\''] | ID ['\']
160class ExpressionParser {
161public:
162 ExpressionParser(LibertyLexer &lexer, OpBuilder &builder, StringRef expr,
163 const llvm::StringMap<Value> &values, SMLoc baseLoc);
164
165 ParseResult parse(Value &result);
166
167private:
168 enum class TokenKind {
169 ID,
170 AND,
171 OR,
172 XOR,
173 PREFIX_NOT, // !
174 POSTFIX_NOT, // '
175 LPAREN,
176 RPAREN,
177 END
178 };
179
180 struct Token {
181 TokenKind kind;
182 StringRef spelling;
183 SMLoc loc;
184 };
185
186 LibertyLexer &lexer;
187 OpBuilder &builder;
188 const llvm::StringMap<Value> &values;
189 SMLoc baseLoc;
190 const char *exprStart;
191 SmallVector<Token> tokens;
192 size_t pos = 0;
193 Value trueVal = nullptr;
194
195 SMLoc getLoc(const char *ptr);
196 void tokenize(StringRef expr);
197 Token peek() const;
198 Token consume();
199 Value createNot(Value val, SMLoc loc);
200
201 ParseResult parseOrExpr(Value &result);
202 ParseResult parseXorExpr(Value &result);
203 ParseResult parseAndExpr(Value &result);
204 ParseResult parseUnaryExpr(Value &result);
205
206 InFlightDiagnostic emitError(llvm::SMLoc loc, const Twine &message) const;
207};
208
209//===----------------------------------------------------------------------===//
210// ExpressionParser Implementation
211//===----------------------------------------------------------------------===//
212
213ExpressionParser::ExpressionParser(LibertyLexer &lexer, OpBuilder &builder,
214 StringRef expr,
215 const llvm::StringMap<Value> &values,
216 SMLoc baseLoc)
217 : lexer(lexer), builder(builder), values(values), baseLoc(baseLoc),
218 exprStart(expr.begin()) {
219 tokenize(expr);
220}
221
222ParseResult ExpressionParser::parse(Value &result) {
223 return parseOrExpr(result);
224}
225
226SMLoc ExpressionParser::getLoc(const char *ptr) {
227 size_t offset = ptr - exprStart;
228 return SMLoc::getFromPointer(baseLoc.getPointer() + 1 + offset);
229}
230
231void ExpressionParser::tokenize(StringRef expr) {
232 const char *ptr = expr.begin();
233 const char *end = expr.end();
234
235 while (ptr < end) {
236 // Skip whitespace
237 if (isspace(*ptr)) {
238 ++ptr;
239 continue;
240 }
241
242 SMLoc loc = getLoc(ptr);
243
244 // Identifier
245 if (isalnum(*ptr) || *ptr == '_') {
246 const char *start = ptr;
247 while (ptr < end && (isalnum(*ptr) || *ptr == '_'))
248 ++ptr;
249 tokens.push_back({TokenKind::ID, StringRef(start, ptr - start), loc});
250 continue;
251 }
252
253 // Operators and punctuation
254 switch (*ptr) {
255 case '*':
256 case '&':
257 tokens.push_back({TokenKind::AND, StringRef(ptr, 1), loc});
258 break;
259 case '+':
260 case '|':
261 tokens.push_back({TokenKind::OR, StringRef(ptr, 1), loc});
262 break;
263 case '^':
264 tokens.push_back({TokenKind::XOR, StringRef(ptr, 1), loc});
265 break;
266 case '!':
267 tokens.push_back({TokenKind::PREFIX_NOT, StringRef(ptr, 1), loc});
268 break;
269 case '\'':
270 tokens.push_back({TokenKind::POSTFIX_NOT, StringRef(ptr, 1), loc});
271 break;
272 case '(':
273 tokens.push_back({TokenKind::LPAREN, StringRef(ptr, 1), loc});
274 break;
275 case ')':
276 tokens.push_back({TokenKind::RPAREN, StringRef(ptr, 1), loc});
277 break;
278 }
279 ++ptr;
280 }
281
282 tokens.push_back({TokenKind::END, "", getLoc(ptr)});
283}
284
285ExpressionParser::Token ExpressionParser::peek() const { return tokens[pos]; }
286
287ExpressionParser::Token ExpressionParser::consume() { return tokens[pos++]; }
288
289Value ExpressionParser::createNot(Value val, SMLoc loc) {
290 if (!trueVal)
291 trueVal = hw::ConstantOp::create(builder, lexer.translateLocation(loc),
292 builder.getI1Type(), 1);
293 return comb::XorOp::create(builder, lexer.translateLocation(loc), val,
294 trueVal);
295}
296
297/// Parse OR expressions with lowest precedence.
298/// OrExpr -> XorExpr { ('+'|'|') XorExpr }
299/// This implements left-associative parsing: A + B + C becomes (A + B) + C
300ParseResult ExpressionParser::parseOrExpr(Value &result) {
301 Value lhs;
302 if (parseXorExpr(lhs))
303 return failure();
304 while (peek().kind == TokenKind::OR) {
305 auto loc = consume().loc;
306 Value rhs;
307 if (parseXorExpr(rhs))
308 return failure();
309 lhs = comb::OrOp::create(builder, lexer.translateLocation(loc), lhs, rhs);
310 }
311 result = lhs;
312 return success();
313}
314
315/// Parse XOR expressions with medium precedence.
316/// XorExpr -> AndExpr { '^' AndExpr }
317/// This implements left-associative parsing: A ^ B ^ C becomes (A ^ B) ^ C
318ParseResult ExpressionParser::parseXorExpr(Value &result) {
319 Value lhs;
320 if (parseAndExpr(lhs))
321 return failure();
322 while (peek().kind == TokenKind::XOR) {
323 auto loc = consume().loc;
324 Value rhs;
325 if (parseAndExpr(rhs))
326 return failure();
327 lhs = comb::XorOp::create(builder, lexer.translateLocation(loc), lhs, rhs);
328 }
329 result = lhs;
330 return success();
331}
332
333/// Parse AND expressions with highest precedence.
334/// AndExpr -> UnaryExpr { ('*'|'&'|implicit) UnaryExpr }
335/// This implements left-associative parsing: A * B * C becomes (A * B) * C
336/// Space between expressions is treated as implicit AND.
337ParseResult ExpressionParser::parseAndExpr(Value &result) {
338 Value lhs;
339 if (parseUnaryExpr(lhs))
340 return failure();
341 while (peek().kind == TokenKind::AND ||
342 (peek().kind == TokenKind::ID || peek().kind == TokenKind::LPAREN ||
343 peek().kind == TokenKind::PREFIX_NOT)) {
344 auto loc = peek().loc;
345 if (peek().kind == TokenKind::AND)
346 consume();
347 Value rhs;
348 if (parseUnaryExpr(rhs))
349 return failure();
350 lhs = comb::AndOp::create(builder, lexer.translateLocation(loc), lhs, rhs);
351 }
352 result = lhs;
353 return success();
354}
355
356/// Parse unary expressions and primary expressions.
357/// UnaryExpr -> '!' UnaryExpr | '(' OrExpr ')' ['\''] | ID ['\']
358/// Handles prefix NOT (!), parenthesized expressions, and identifiers.
359/// Postfix NOT (') is only allowed after expressions and identifiers.
360ParseResult ExpressionParser::parseUnaryExpr(Value &result) {
361 // Prefix NOT (!)
362 if (peek().kind == TokenKind::PREFIX_NOT) {
363 auto loc = consume().loc;
364 Value val;
365 if (parseUnaryExpr(val))
366 return failure();
367 result = createNot(val, loc);
368 return success();
369 }
370
371 // Parenthesized expression
372 if (peek().kind == TokenKind::LPAREN) {
373 consume();
374 Value val;
375 if (parseOrExpr(val))
376 return failure();
377 if (peek().kind != TokenKind::RPAREN)
378 return failure();
379 consume();
380 // Postfix NOT (')
381 if (peek().kind == TokenKind::POSTFIX_NOT) {
382 auto loc = consume().loc;
383 val = createNot(val, loc);
384 }
385 result = val;
386 return success();
387 }
388
389 // Identifier
390 if (peek().kind == TokenKind::ID) {
391 auto tok = consume();
392 StringRef name = tok.spelling;
393 auto it = values.find(name);
394 if (it == values.end())
395 return emitError(tok.loc, "variable not found");
396 Value val = it->second;
397 // Postfix NOT (')
398 if (peek().kind == TokenKind::POSTFIX_NOT) {
399 auto loc = consume().loc;
400 val = createNot(val, loc);
401 }
402 result = val;
403 return success();
404 }
405
406 return emitError(peek().loc, "expected expression");
407}
408
409InFlightDiagnostic ExpressionParser::emitError(llvm::SMLoc loc,
410 const Twine &message) const {
411 return mlir::emitError(lexer.translateLocation(loc), message);
412}
413
414// Parsed result of a Liberty group
415struct LibertyGroup {
416 StringRef name;
417 SMLoc loc;
418 SmallVector<Attribute> args;
419 struct AttrEntry {
420 StringRef name;
421 Attribute value;
422 SMLoc loc;
423 AttrEntry(StringRef name, Attribute value, SMLoc loc)
424 : name(name), value(value), loc(loc) {}
425 };
426 SmallVector<AttrEntry> attrs;
427 SmallVector<std::unique_ptr<LibertyGroup>> subGroups;
428
429 // Helper to find an attribute by name
430 std::pair<Attribute, SMLoc> getAttribute(StringRef name) const {
431 for (const auto &attr : attrs)
432 if (attr.name == name)
433 return {attr.value, attr.loc};
434 return {};
435 }
436
437 void eraseSubGroup(llvm::function_ref<bool(const LibertyGroup &)> pred) {
438 subGroups.erase(
439 std::remove_if(subGroups.begin(), subGroups.end(),
440 [pred](const auto &group) { return pred(*group); }),
441 subGroups.end());
442 }
443
444 LogicalResult checkArgs(
445 size_t n,
446 llvm::function_ref<LogicalResult(size_t idx, Attribute)> pred) const {
447 if (args.size() != n)
448 return failure();
449 for (size_t i = 0; i < n; ++i)
450 if (failed(pred(i, args[i])))
451 return failure();
452 return success();
453 }
454};
455
456class LibertyParser {
457public:
458 LibertyParser(const llvm::SourceMgr &sourceMgr, MLIRContext *context,
459 ModuleOp module)
460 : lexer(sourceMgr, context), module(module),
461 builder(module.getBodyRegion()) {}
462
463 ParseResult parse();
464
465private:
466 LibertyLexer lexer;
467 ModuleOp module;
468 OpBuilder builder;
469
470 // Specific group parsers
471 ParseResult parseLibrary();
472 ParseResult parseGroupBody(LibertyGroup &group);
473 ParseResult parseStatement(LibertyGroup &parent);
474
475 // Lowering methods
476 ParseResult
477 lowerCell(const LibertyGroup &group, StringAttr &cellNameAttr,
478 const llvm::StringMap<const LibertyGroup *> &tableTemplates);
479
480 ParseResult
481 parseNLDMTable(const LibertyGroup &group,
482 const llvm::StringMap<const LibertyGroup *> &tableTemplates,
483 synth::NLDMTableAttr &table);
484
485 LogicalResult
486 buildTimingArcs(const LibertyGroup &pinGroup,
487 const llvm::StringMap<const LibertyGroup *> &tableTemplates,
488 SmallVector<Attribute> &timingArcs);
489
490 LogicalResult
491 collectPorts(const LibertyGroup &cellGroup,
492 const llvm::StringMap<const LibertyGroup *> &tableTemplates,
493 SmallVector<hw::PortInfo> &ports,
494 SmallVector<const LibertyGroup *> &pinGroups,
495 SmallVector<Attribute> &timingArcs,
496 size_t &numOutputPinMissingFunction);
497
498 LogicalResult buildCellLogic(hw::HWModuleOp moduleOp,
499 ArrayRef<hw::PortInfo> ports,
500 ArrayRef<const LibertyGroup *> pinGroups);
501
502 LogicalResult parseLibertyFloatList(StringRef value, SMLoc loc,
503 SmallVectorImpl<double> &result) const;
504
505 //===--------------------------------------------------------------------===//
506 // Parser for Subgroup of "cell" group.
507 //===--------------------------------------------------------------------===//
508
509 Attribute convertGroupToAttr(const LibertyGroup &group);
510
511 // Attribute parsing
512 ParseResult parseAttribute(Attribute &result);
513
514 // Expression parsing
515 ParseResult parseExpression(Value &result, StringRef expr,
516 const llvm::StringMap<Value> &values, SMLoc loc);
517
518 InFlightDiagnostic emitError(llvm::SMLoc loc, const Twine &message) const {
519 return mlir::emitError(lexer.translateLocation(loc), message);
520 }
521
522 InFlightDiagnostic emitWarning(llvm::SMLoc loc, const Twine &message) const {
523 return mlir::emitWarning(lexer.translateLocation(loc), message);
524 }
525
526 ParseResult consume(LibertyTokenKind kind, const Twine &msg) {
527 if (lexer.nextToken().is(kind))
528 return success();
529 return emitError(lexer.getCurrentLoc(), msg);
530 }
531
532 ParseResult consumeUntil(LibertyTokenKind kind) {
533 while (!lexer.peekToken().is(kind) &&
534 lexer.peekToken().kind != LibertyTokenKind::EndOfFile)
535 lexer.nextToken();
536 if (lexer.peekToken().is(kind))
537 return success();
538 return emitError(lexer.getCurrentLoc(),
539 " expected " + stringifyTokenKind(kind));
540 }
541
542 ParseResult consume(LibertyTokenKind kind) {
543 if (lexer.nextToken().is(kind))
544 return success();
545 return emitError(lexer.getCurrentLoc(),
546 " expected " + stringifyTokenKind(kind));
547 }
548
549 ParseResult expect(LibertyTokenKind kind) {
550 if (!lexer.peekToken().is(kind))
551 return emitError(lexer.getCurrentLoc(),
552 " expected " + stringifyTokenKind(kind));
553
554 return success();
555 }
556
557 StringRef getTokenSpelling(const LibertyToken &token) {
558 StringRef str = token.spelling;
559 if (token.kind == LibertyTokenKind::String)
560 str = str.drop_front().drop_back();
561 return str;
562 }
563};
564
565//===----------------------------------------------------------------------===//
566// LibertyLexer Implementation
567//===----------------------------------------------------------------------===//
568
569void LibertyLexer::skipWhitespaceAndComments() {
570 while (curPtr < curBuffer.end()) {
571 if (isspace(*curPtr)) {
572 ++curPtr;
573 continue;
574 }
575
576 // Comments
577 if (*curPtr == '/' && curPtr + 1 < curBuffer.end()) {
578 if (*(curPtr + 1) == '*') { // /* ... */
579 curPtr += 2;
580 while (curPtr + 1 < curBuffer.end() &&
581 (*curPtr != '*' || *(curPtr + 1) != '/'))
582 ++curPtr;
583 if (curPtr + 1 < curBuffer.end())
584 curPtr += 2;
585 continue;
586 }
587 if (*(curPtr + 1) == '/') { // // ...
588 while (curPtr < curBuffer.end() && *curPtr != '\n')
589 ++curPtr;
590 continue;
591 }
592 }
593
594 // Backslash newline continuation
595 if (*curPtr == '\\' && curPtr + 1 < curBuffer.end() &&
596 *(curPtr + 1) == '\n') {
597 curPtr += 2;
598 continue;
599 }
600
601 break;
602 }
603}
604
605LibertyToken LibertyLexer::lexIdentifier() {
606 const char *start = curPtr;
607 while (curPtr < curBuffer.end() &&
608 (isalnum(*curPtr) || *curPtr == '_' || *curPtr == '.'))
609 ++curPtr;
610 return makeToken(LibertyTokenKind::Identifier, start);
611}
612
613LibertyToken LibertyLexer::lexString() {
614 const char *start = curPtr;
615 ++curPtr; // skip opening quote
616 while (curPtr < curBuffer.end() && *curPtr != '"') {
617 if (*curPtr == '\\' && curPtr + 1 < curBuffer.end())
618 ++curPtr; // skip escaped char
619 ++curPtr;
620 }
621 if (curPtr < curBuffer.end())
622 ++curPtr; // skip closing quote
623 return makeToken(LibertyTokenKind::String, start);
624}
625
626LibertyToken LibertyLexer::lexNumber() {
627 const char *start = curPtr;
628 bool seenDot = false;
629 while (curPtr < curBuffer.end()) {
630 if (isdigit(*curPtr)) {
631 ++curPtr;
632 } else if (*curPtr == '.') {
633 if (seenDot) {
634 mlir::emitError(translateLocation(SMLoc::getFromPointer(curPtr)),
635 "multiple decimal points in number");
636 return makeToken(LibertyTokenKind::Error, start);
637 }
638 seenDot = true;
639 ++curPtr;
640 } else {
641 break;
642 }
643 }
644 return makeToken(LibertyTokenKind::Number, start);
645}
646
647LibertyToken LibertyLexer::makeToken(LibertyTokenKind kind, const char *start) {
648 return LibertyToken(kind, StringRef(start, curPtr - start),
649 SMLoc::getFromPointer(start));
650}
651
652LibertyToken LibertyLexer::nextToken() {
653 skipWhitespaceAndComments();
654
655 if (curPtr >= curBuffer.end())
656 return makeToken(LibertyTokenKind::EndOfFile, curPtr);
657
658 const char *start = curPtr;
659 char c = *curPtr;
660
661 if (isalpha(c) || c == '_')
662 return lexIdentifier();
663
664 if (isdigit(c) ||
665 (c == '.' && curPtr + 1 < curBuffer.end() && isdigit(*(curPtr + 1))))
666 return lexNumber();
667
668 if (c == '"')
669 return lexString();
670
671 ++curPtr;
672 switch (c) {
673 case '{':
674 return makeToken(LibertyTokenKind::LBrace, start);
675 case '}':
676 return makeToken(LibertyTokenKind::RBrace, start);
677 case '(':
678 return makeToken(LibertyTokenKind::LParen, start);
679 case ')':
680 return makeToken(LibertyTokenKind::RParen, start);
681 case ':':
682 return makeToken(LibertyTokenKind::Colon, start);
683 case ';':
684 return makeToken(LibertyTokenKind::Semi, start);
685 case ',':
686 return makeToken(LibertyTokenKind::Comma, start);
687
688 default:
689 return makeToken(LibertyTokenKind::Error, start);
690 }
691}
692
693LibertyToken LibertyLexer::peekToken() {
694 const char *savedPtr = curPtr;
695 LibertyToken token = nextToken();
696 curPtr = savedPtr;
697 return token;
698}
699
700//===----------------------------------------------------------------------===//
701// LibertyParser Implementation
702//===----------------------------------------------------------------------===//
703
704ParseResult LibertyParser::parse() {
705 while (lexer.peekToken().kind != LibertyTokenKind::EndOfFile) {
706 auto token = lexer.peekToken();
707 // Skip any stray tokens that aren't valid group starts
708 if (token.kind != LibertyTokenKind::Identifier ||
709 token.spelling != "library") {
710 return emitError(token.location, "expected `library` keyword");
711 }
712 lexer.nextToken();
713 if (parseLibrary())
714 return failure();
715 }
716 return success();
717}
718
719ParseResult LibertyParser::parseLibrary() {
720 LibertyGroup libertyLib;
721 if (parseGroupBody(libertyLib))
722 return failure();
723
724 // First Pass: collect lu_table_template groups by name so they can be
725 // referenced during NLDM table parsing in lowerCell. Liberty tables
726 // reference templates by name for their index axes e.g.
727 // cell_rise(delay_template_7x7) where the template defines index_1/index_2.
728
729 llvm::StringMap<const LibertyGroup *> tableTemplates;
730 for (const auto &stmt : libertyLib.subGroups)
731 if (stmt->name == "lu_table_template")
732 if (!stmt->args.empty())
733 if (auto name = dyn_cast<StringAttr>(stmt->args[0]))
734 tableTemplates[name.getValue()] = stmt.get();
735
736 // Second Pass: lower cells, passing the template map through for index
737 // lookup.
738 DenseSet<StringAttr> seenCells;
739 for (auto &stmt : libertyLib.subGroups) {
740 if (stmt->name != "cell")
741 continue;
742 StringAttr cellNameAttr;
743 if (lowerCell(*stmt, cellNameAttr, tableTemplates))
744 return failure();
745 if (!seenCells.insert(cellNameAttr).second)
746 return emitError(stmt->loc, "redefinition of cell '" +
747 cellNameAttr.getValue() + "'");
748 }
749
750 libertyLib.eraseSubGroup(
751 [](const LibertyGroup &group) { return group.name == "cell"; });
752 auto attr = convertGroupToAttr(libertyLib);
753 module->setAttr("synth.liberty.library", attr);
754 return success();
755}
756
757// Parse a template like: lu_table_template (delay_template_6x6) { variable_1:
758// total_output_net_capacitance; variable_2: input_net_transition; }
759Attribute LibertyParser::convertGroupToAttr(const LibertyGroup &group) {
760 SmallVector<NamedAttribute> attrs;
761 if (!group.args.empty())
762 attrs.push_back(
763 builder.getNamedAttr("args", builder.getArrayAttr(group.args)));
764
765 for (const auto &attr : group.attrs)
766 attrs.push_back(builder.getNamedAttr(attr.name, attr.value));
767
768 llvm::StringMap<SmallVector<Attribute>> subGroups;
769 for (const auto &sub : group.subGroups)
770 subGroups[sub->name].push_back(convertGroupToAttr(*sub));
771
772 for (auto &it : subGroups)
773 attrs.push_back(
774 builder.getNamedAttr(it.getKey(), builder.getArrayAttr(it.getValue())));
775
776 return builder.getDictionaryAttr(attrs);
777}
778
779LogicalResult
780LibertyParser::parseLibertyFloatList(StringRef value, SMLoc loc,
781 SmallVectorImpl<double> &result) const {
782 SmallVector<StringRef> parts;
783 value.split(parts, ',');
784 for (auto part : parts) {
785 part = part.trim();
786 if (part.empty())
787 continue;
788 double val;
789 if (part.getAsDouble(val))
790 return emitError(loc, "expected floating point value in NLDM table");
791 result.push_back(val);
792 }
793 return success();
794}
795
796ParseResult LibertyParser::parseNLDMTable(
797 const LibertyGroup &group,
798 const llvm::StringMap<const LibertyGroup *> &tableTemplates,
799 synth::NLDMTableAttr &table) {
800 SmallVector<double> index1, index2, values;
801
802 auto parseList = [&](const LibertyGroup &source, StringRef name,
803 SmallVectorImpl<double> &result) -> ParseResult {
804 if (auto [attr, loc] = source.getAttribute(name); attr) {
805 auto stringAttr = dyn_cast<StringAttr>(attr);
806 if (!stringAttr)
807 return emitError(loc, "expected string value for NLDM table entry");
808 if (failed(parseLibertyFloatList(stringAttr.getValue(), loc, result)))
809 return failure();
810 return success();
811 }
812
813 for (const auto &subGroup : source.subGroups) {
814 if (subGroup->name != name)
815 continue;
816 if (subGroup->args.empty())
817 return emitError(subGroup->loc, "NLDM table entry missing value");
818 for (auto arg : subGroup->args) {
819 auto stringAttr = dyn_cast<StringAttr>(arg);
820 if (!stringAttr)
821 return emitError(subGroup->loc,
822 "expected string value for NLDM table entry");
823 if (failed(parseLibertyFloatList(stringAttr.getValue(), subGroup->loc,
824 result)))
825 return failure();
826 }
827 return success();
828 }
829
830 return success();
831 };
832
833 // Table-local indices override template indices when present.
834 if (parseList(group, "index_1", index1) ||
835 parseList(group, "index_2", index2) || parseList(group, "values", values))
836 return failure();
837
838 if (!group.args.empty()) {
839 auto templateName = dyn_cast<StringAttr>(group.args[0]);
840 if (!templateName)
841 return emitError(group.loc, "NLDM table template name must be a string");
842
843 auto it = tableTemplates.find(templateName.getValue());
844 if (it == tableTemplates.end())
845 return emitError(group.loc, "unknown NLDM table template '")
846 << templateName.getValue() << "'";
847
848 if (index1.empty() && parseList(*it->second, "index_1", index1))
849 return failure();
850 if (index2.empty() && parseList(*it->second, "index_2", index2))
851 return failure();
852 }
853
854 if (index1.empty())
855 return emitError(group.loc, "NLDM table missing index_1");
856 if (index2.empty())
857 return emitError(group.loc, "NLDM table missing index_2");
858 if (values.empty())
859 return emitError(group.loc, "NLDM table missing values");
860
861 if (values.size() != index1.size() * index2.size()) {
862 return emitError(group.loc, "NLDM table has ")
863 << values.size() << " values, expected "
864 << index1.size() * index2.size() << " for " << index1.size() << " x "
865 << index2.size() << " indices";
866 }
867
868 table = synth::NLDMTableAttr::get(
869 builder.getContext(),
870 DenseF64ArrayAttr::get(builder.getContext(), index1),
871 DenseF64ArrayAttr::get(builder.getContext(), index2),
872 DenseF64ArrayAttr::get(builder.getContext(), values));
873 return success();
874}
875
876static bool isNLDMTableGroup(StringRef name) {
877 return name == "cell_rise" || name == "cell_fall" ||
878 name == "rise_transition" || name == "fall_transition";
879}
880
881LogicalResult LibertyParser::buildTimingArcs(
882 const LibertyGroup &pinGroup,
883 const llvm::StringMap<const LibertyGroup *> &tableTemplates,
884 SmallVector<Attribute> &timingArcs) {
885 auto pinName = cast<StringAttr>(pinGroup.args[0]);
886
887 for (const auto &child : pinGroup.subGroups) {
888 if (child->name != "timing")
889 continue;
890
891 auto relatedPin =
892 dyn_cast_or_null<StringAttr>(child->getAttribute("related_pin").first);
893 auto timingSense =
894 dyn_cast_or_null<StringAttr>(child->getAttribute("timing_sense").first);
895 if (!relatedPin || !timingSense)
896 return emitError(child->loc,
897 "timing related_pin and timing_sense must be strings");
898
899 synth::NLDMTableAttr cellRise, cellFall, riseTransition, fallTransition;
900 for (const auto &table : child->subGroups) {
901 if (!isNLDMTableGroup(table->name))
902 continue;
903
904 synth::NLDMTableAttr parsedTable;
905 if (parseNLDMTable(*table, tableTemplates, parsedTable))
906 return failure();
907
908 if (table->name == "cell_rise")
909 cellRise = parsedTable;
910 else if (table->name == "cell_fall")
911 cellFall = parsedTable;
912 else if (table->name == "rise_transition")
913 riseTransition = parsedTable;
914 else if (table->name == "fall_transition")
915 fallTransition = parsedTable;
916 }
917
918 if (!cellRise || !cellFall || !riseTransition || !fallTransition)
919 return emitError(child->loc,
920 "NLDM timing arc requires cell_rise, cell_fall, "
921 "rise_transition, and fall_transition tables");
922
923 timingArcs.push_back(synth::NLDMTimingArcAttr::get(
924 builder.getContext(), pinName, relatedPin, timingSense, cellRise,
925 cellFall, riseTransition, fallTransition));
926 }
927 return success();
928}
929
930LogicalResult LibertyParser::collectPorts(
931 const LibertyGroup &cellGroup,
932 const llvm::StringMap<const LibertyGroup *> &tableTemplates,
933 SmallVector<hw::PortInfo> &ports,
934 SmallVector<const LibertyGroup *> &pinGroups,
935 SmallVector<Attribute> &timingArcs, size_t &numOutputPinMissingFunction) {
936
937 llvm::DenseSet<StringAttr> seenPins;
938
939 // Gather ports and count output pins without "function" attribute.
940 for (const auto &sub : cellGroup.subGroups) {
941 if (sub->name != "pin")
942 continue;
943
944 pinGroups.push_back(sub.get());
945 if (sub->args.empty())
946 return emitError(sub->loc, "pin missing name");
947
948 StringAttr pinName = dyn_cast<StringAttr>(sub->args[0]);
949 if (!seenPins.insert(pinName).second)
950 return emitError(sub->loc,
951 "redefinition of pin '" + pinName.getValue() + "'");
952
953 std::optional<hw::ModulePort::Direction> dir;
954 SmallVector<NamedAttribute> pinAttrs;
955 bool functionExist = false;
956
957 // Track if this pin has a "function" attribute (only relevant for outputs).
958 for (const auto &attr : sub->attrs) {
959 if (attr.name == "direction") {
960 auto val = dyn_cast<StringAttr>(attr.value);
961 if (!val)
962 return emitError(sub->loc, "pin direction must be a string");
963 if (val.getValue() == "input")
964 dir = hw::ModulePort::Direction::Input;
965 else if (val.getValue() == "output")
966 dir = hw::ModulePort::Direction::Output;
967 else if (val.getValue() == "inout")
968 dir = hw::ModulePort::Direction::InOut;
969 else
970 return emitError(sub->loc,
971 "pin direction must be input, output, or inout");
972 continue;
973 }
974 if (attr.name == "function")
975 functionExist = true;
976 pinAttrs.push_back(builder.getNamedAttr(attr.name, attr.value));
977 }
978
979 if (!dir)
980 return emitError(sub->loc, "pin direction must be specified");
981
982 if (dir == hw::ModulePort::Direction::Output && !functionExist)
983 ++numOutputPinMissingFunction;
984
985 // Build timing arcs for this pin's timing subgroups.
986 if (failed(buildTimingArcs(*sub, tableTemplates, timingArcs)))
987 return failure();
988
989 // Collect non-timing subgroups as generic attrs
990 llvm::StringMap<SmallVector<Attribute>> subGroupAttrs;
991 for (const auto &child : sub->subGroups)
992 if (child->name != "timing")
993 subGroupAttrs[child->name].push_back(convertGroupToAttr(*child));
994
995 for (auto &it : subGroupAttrs)
996 pinAttrs.push_back(builder.getNamedAttr(
997 it.getKey(), builder.getArrayAttr(it.getValue())));
998
999 auto libertyAttrs = builder.getDictionaryAttr(pinAttrs);
1000 auto attrs = builder.getDictionaryAttr(
1001 builder.getNamedAttr("synth.liberty.pin", libertyAttrs));
1002
1003 hw::PortInfo port;
1004 port.name = pinName;
1005 port.type = builder.getI1Type();
1006 port.dir = *dir;
1007 port.attrs = attrs;
1008 ports.push_back(port);
1009 }
1010 return success();
1011}
1012
1013LogicalResult
1014LibertyParser::buildCellLogic(hw::HWModuleOp moduleOp,
1015 ArrayRef<hw::PortInfo> ports,
1016 ArrayRef<const LibertyGroup *> pinGroups) {
1017
1018 // All outputs have "function": emit logic into hw.module body.
1019 OpBuilder::InsertionGuard guard(builder);
1020 builder.setInsertionPointToStart(moduleOp.getBodyBlock());
1021
1022 llvm::StringMap<Value> portValues;
1023 for (const auto &port : ports)
1024 if (port.dir == hw::ModulePort::Direction::Input)
1025 portValues[port.name.getValue()] =
1026 moduleOp.getBodyBlock()->getArgument(port.argNum);
1027
1028 SmallVector<Value> outputs;
1029 for (const auto &port : ports) {
1030 if (port.dir != hw::ModulePort::Direction::Output)
1031 continue;
1032
1033 auto *it = llvm::find_if(pinGroups, [&](const LibertyGroup *g) {
1034 assert(g->name == "pin" && "expected pin group");
1035 // First arg is the pin name
1036 return cast<StringAttr>(g->args[0]) == port.name;
1037 });
1038
1039 if (!it)
1040 continue;
1041
1042 const LibertyGroup *pg = *it;
1043 auto attrPair = pg->getAttribute("function");
1044 assert(attrPair.first && "output pin missing function attribute");
1045
1046 Value val;
1047 if (parseExpression(val, cast<StringAttr>(attrPair.first).getValue(),
1048 portValues, attrPair.second))
1049 return failure();
1050 outputs.push_back(val);
1051 }
1052
1053 moduleOp.getBodyBlock()->getTerminator()->setOperands(outputs);
1054 return success();
1055}
1056
1057ParseResult LibertyParser::lowerCell(
1058 const LibertyGroup &group, StringAttr &cellNameAttr,
1059 const llvm::StringMap<const LibertyGroup *> &tableTemplates) {
1060
1061 if (group.args.empty())
1062 return emitError(group.loc, "cell missing name");
1063 cellNameAttr = dyn_cast<StringAttr>(group.args[0]);
1064 if (!cellNameAttr)
1065 return emitError(group.loc, "cell name must be a string");
1066
1067 SmallVector<hw::PortInfo> ports;
1068 SmallVector<const LibertyGroup *> pinGroups;
1069 SmallVector<Attribute> timingArcs;
1070 size_t numOutputPinMissingFunction = 0;
1071
1072 if (failed(collectPorts(group, tableTemplates, ports, pinGroups, timingArcs,
1073 numOutputPinMissingFunction)))
1074 return failure();
1075
1076 // Fix up argNum for inputs
1077 int inputIdx = 0;
1078 for (auto &p : ports)
1079 if (p.dir == hw::ModulePort::Direction::Input)
1080 p.argNum = inputIdx++;
1081 else
1082 p.argNum = 0;
1083
1084 auto loc = lexer.translateLocation(group.loc);
1085 auto numOutput = ports.size() - inputIdx;
1086
1087 // Decide whether to create hw.module (with logic) or hw.module.extern (black
1088 // box) based on whether output pins have "function" attributes.
1089 // All outputs must either have "function" or all must lack it.
1090
1091 // Mixed case: some outputs have "function", others don't - error.
1092 if (numOutputPinMissingFunction != 0 &&
1093 numOutputPinMissingFunction != numOutput)
1094 return emitError(group.loc, "cell '")
1095 << cellNameAttr.getValue()
1096 << "' has mixed output pins with and without 'function' attribute";
1097
1098 // All outputs lack "function": emit as hw.module.extern (black box).
1099 if (numOutputPinMissingFunction == numOutput) {
1100 auto externOp =
1101 hw::HWModuleExternOp::create(builder, loc, cellNameAttr, ports);
1102 if (!timingArcs.empty())
1103 externOp->setAttr("synth.timing.nldm", builder.getArrayAttr(timingArcs));
1104 return success();
1105 }
1106
1107 // All outputs have "function": emit as hw.module with logic.
1108 auto moduleOp = hw::HWModuleOp::create(builder, loc, cellNameAttr, ports);
1109
1110 if (failed(buildCellLogic(moduleOp, ports, pinGroups)))
1111 return failure();
1112
1113 if (!timingArcs.empty())
1114 moduleOp->setAttr("synth.timing.nldm", builder.getArrayAttr(timingArcs));
1115
1116 return success();
1117}
1118
1119ParseResult LibertyParser::parseGroupBody(LibertyGroup &group) {
1120 // Parse args: ( arg1, arg2 )
1121 if (lexer.peekToken().kind == LibertyTokenKind::LParen) {
1122 lexer.nextToken(); // (
1123 while (lexer.peekToken().kind != LibertyTokenKind::RParen) {
1124 Attribute arg;
1125 if (parseAttribute(arg))
1126 return failure();
1127 group.args.push_back(arg);
1128 if (lexer.peekToken().kind == LibertyTokenKind::Comma)
1129 lexer.nextToken();
1130 }
1131 if (consume(LibertyTokenKind::RParen, "expected ')'"))
1132 return failure();
1133 }
1134
1135 // Parse body: { ... }
1136 if (lexer.peekToken().kind == LibertyTokenKind::LBrace) {
1137 lexer.nextToken(); // {
1138 while (lexer.peekToken().kind != LibertyTokenKind::RBrace &&
1139 lexer.peekToken().kind != LibertyTokenKind::EndOfFile) {
1140 if (parseStatement(group))
1141 return failure();
1142 }
1143 if (consume(LibertyTokenKind::RBrace, "expected '}'"))
1144 return failure();
1145 } else {
1146 // Optional semicolon if no body
1147 if (lexer.peekToken().kind == LibertyTokenKind::Semi)
1148 lexer.nextToken();
1149 }
1150 return success();
1151}
1152
1153// Parse group, attribute, or define statements
1154ParseResult LibertyParser::parseStatement(LibertyGroup &parent) {
1155 auto nameTok = lexer.nextToken();
1156 if (nameTok.kind != LibertyTokenKind::Identifier)
1157 return emitError(nameTok.location, "expected identifier");
1158 StringRef name = nameTok.spelling;
1159
1160 // Attribute statement.
1161 if (lexer.peekToken().kind == LibertyTokenKind::Colon) {
1162 lexer.nextToken(); // :
1163 Attribute val;
1164 auto loc = lexer.peekToken().location;
1165 if (parseAttribute(val))
1166 return failure();
1167 parent.attrs.emplace_back(name, val, loc);
1168 return consume(LibertyTokenKind::Semi, "expected ';'");
1169 }
1170
1171 // Group statement.
1172 if (lexer.peekToken().kind == LibertyTokenKind::LParen) {
1173 auto subGroup = std::make_unique<LibertyGroup>();
1174 subGroup->name = name;
1175 subGroup->loc = nameTok.location;
1176 if (parseGroupBody(*subGroup))
1177 return failure();
1178 parent.subGroups.push_back(std::move(subGroup));
1179 return success();
1180 }
1181
1182 // TODO: Support define.
1183
1184 return emitError(nameTok.location, "expected ':' or '('");
1185}
1186
1187// Parse an attribute value, which can be:
1188// - A string: "value"
1189// - A number: 1.23
1190// - An identifier: value
1191ParseResult LibertyParser::parseAttribute(Attribute &result) {
1192 auto token = lexer.peekToken();
1193 // Number token
1194 if (token.is(LibertyTokenKind::Number)) {
1195 lexer.nextToken();
1196 StringRef numStr = token.spelling;
1197 double val;
1198 if (!numStr.getAsDouble(val)) {
1199 result = builder.getF64FloatAttr(val);
1200 return success();
1201 }
1202 return emitError(token.location, "expected number value");
1203 }
1204
1205 // Identifier token
1206 if (token.is(LibertyTokenKind::Identifier)) {
1207 lexer.nextToken();
1208 result = builder.getStringAttr(token.spelling);
1209 return success();
1210 }
1211
1212 if (token.is(LibertyTokenKind::String)) {
1213 lexer.nextToken();
1214 result = builder.getStringAttr(getTokenSpelling(token));
1215 return success();
1216 }
1217
1218 return emitError(token.location, "expected attribute value");
1219}
1220
1221// Parse boolean expressions from Liberty function attributes
1222// Supports: *, +, ^, !, (), and identifiers
1223ParseResult LibertyParser::parseExpression(Value &result, StringRef expr,
1224 const llvm::StringMap<Value> &values,
1225 SMLoc loc) {
1226 ExpressionParser parser(lexer, builder, expr, values, loc);
1227 return parser.parse(result);
1228}
1229
1230} // namespace
1231
1232namespace circt::liberty {
1234 TranslateToMLIRRegistration reg(
1235 "import-liberty", "Import Liberty file",
1236 [](llvm::SourceMgr &sourceMgr, MLIRContext *context) {
1237 ModuleOp module = ModuleOp::create(UnknownLoc::get(context));
1238 LibertyParser parser(sourceMgr, context, module);
1239 // Load required dialects
1240 context->loadDialect<hw::HWDialect>();
1241 context->loadDialect<comb::CombDialect>();
1242 context->loadDialect<synth::SynthDialect>();
1243
1244 if (failed(parser.parse()))
1245 return OwningOpRef<ModuleOp>();
1246 return OwningOpRef<ModuleOp>(module);
1247 },
1248 [](DialectRegistry &registry) {
1249 registry.insert<HWDialect>();
1250 registry.insert<comb::CombDialect>();
1251 registry.insert<synth::SynthDialect>();
1252 });
1253}
1254} // namespace circt::liberty
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
#define isalpha(x)
Definition FIRLexer.cpp:27
#define isdigit(x)
Definition FIRLexer.cpp:26
static Location getLoc(DefSlot slot)
Definition Mem2Reg.cpp:218
create(data_type, value)
Definition hw.py:433
void registerImportLibertyTranslation()
Register the Liberty importer in the translation registry.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
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.
DictionaryAttr attrs
The optional symbol for this port.