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
17#include "mlir/IR/Attributes.h"
18#include "mlir/IR/Builders.h"
19#include "mlir/IR/BuiltinAttributes.h"
20#include "mlir/IR/BuiltinOps.h"
21#include "mlir/IR/BuiltinTypes.h"
22#include "mlir/IR/Diagnostics.h"
23#include "mlir/IR/Location.h"
24#include "mlir/IR/MLIRContext.h"
25#include "mlir/IR/Value.h"
26#include "mlir/Support/FileUtilities.h"
27#include "mlir/Support/LogicalResult.h"
28#include "mlir/Tools/mlir-translate/Translation.h"
29#include "llvm/ADT/STLExtras.h"
30#include "llvm/ADT/StringRef.h"
31#include "llvm/Support/LogicalResult.h"
32#include "llvm/Support/SourceMgr.h"
33
34#define DEBUG_TYPE "import-liberty"
35
36using namespace mlir;
37using namespace circt;
38using namespace circt::hw;
39using namespace circt::comb;
40
41namespace {
42
43/// Liberty token types for lexical analysis
44enum class LibertyTokenKind {
45 // Literals
46 Identifier,
47 String,
48 Number,
49
50 // Punctuation
51 LBrace, // {
52 RBrace, // }
53 LParen, // (
54 RParen, // )
55 Colon, // :
56 Semi, // ;
57 Comma, // ,
58
59 // Special
60 EndOfFile,
61 Error
62};
63
64StringRef stringifyTokenKind(LibertyTokenKind kind) {
65 switch (kind) {
66 case LibertyTokenKind::Identifier:
67 return "identifier";
68 case LibertyTokenKind::String:
69 return "string";
70 case LibertyTokenKind::Number:
71 return "number";
72 case LibertyTokenKind::LBrace:
73 return "'{'";
74 case LibertyTokenKind::RBrace:
75 return "'}'";
76 case LibertyTokenKind::LParen:
77 return "'('";
78 case LibertyTokenKind::RParen:
79 return "')'";
80 case LibertyTokenKind::Colon:
81 return "':'";
82 case LibertyTokenKind::Semi:
83 return "';'";
84 case LibertyTokenKind::Comma:
85 return "','";
86
87 case LibertyTokenKind::EndOfFile:
88 return "end of file";
89 case LibertyTokenKind::Error:
90 return "error";
91 }
92 return "unknown";
93}
94
95struct LibertyToken {
96 LibertyTokenKind kind;
97 StringRef spelling;
98 SMLoc location;
99
100 LibertyToken(LibertyTokenKind kind, StringRef spelling, SMLoc location)
101 : kind(kind), spelling(spelling), location(location) {}
102
103 bool is(LibertyTokenKind k) const { return kind == k; }
104};
105
106class LibertyLexer {
107public:
108 LibertyLexer(const llvm::SourceMgr &sourceMgr, MLIRContext *context)
109 : sourceMgr(sourceMgr), context(context),
110 curBuffer(
111 sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID())->getBuffer()),
112 curPtr(curBuffer.begin()) {}
113
114 LibertyToken nextToken();
115 LibertyToken peekToken();
116
117 SMLoc getCurrentLoc() const { return SMLoc::getFromPointer(curPtr); }
118
119 Location translateLocation(llvm::SMLoc loc) const {
120 unsigned mainFileID = sourceMgr.getMainFileID();
121 auto lineAndColumn = sourceMgr.getLineAndColumn(loc, mainFileID);
122 return FileLineColLoc::get(
123 StringAttr::get(
124 context,
125 sourceMgr.getMemoryBuffer(mainFileID)->getBufferIdentifier()),
126 lineAndColumn.first, lineAndColumn.second);
127 }
128
129 bool isAtEnd() const { return curPtr >= curBuffer.end(); }
130
131private:
132 const llvm::SourceMgr &sourceMgr;
133 MLIRContext *context;
134 StringRef curBuffer;
135 const char *curPtr;
136
137 void skipWhitespaceAndComments();
138 LibertyToken lexIdentifier();
139 LibertyToken lexString();
140 LibertyToken lexNumber();
141 LibertyToken makeToken(LibertyTokenKind kind, const char *start);
142};
143
144/// Helper class to parse boolean expressions from Liberty function attributes.
145///
146/// This parser implements a recursive-descent parser for Liberty boolean
147/// expressions with the following operator precedence (highest to lowest):
148/// 1. NOT (!, ') - Unary negation (prefix or postfix)
149/// 2. AND (*, &) - Conjunction
150/// 3. XOR (^) - Exclusive OR
151/// 4. OR (+, |) - Disjunction
152///
153/// Parentheses can be used to override precedence. The grammar is:
154/// OrExpr -> XorExpr { ('+'|'|') XorExpr }
155/// XorExpr -> AndExpr { '^' AndExpr }
156/// AndExpr -> UnaryExpr { ('*'|'&') UnaryExpr }
157/// UnaryExpr -> '!' UnaryExpr | '(' OrExpr ')' ['\''] | ID ['\']
158class ExpressionParser {
159public:
160 ExpressionParser(LibertyLexer &lexer, OpBuilder &builder, StringRef expr,
161 const llvm::StringMap<Value> &values, SMLoc baseLoc);
162
163 ParseResult parse(Value &result);
164
165private:
166 enum class TokenKind {
167 ID,
168 AND,
169 OR,
170 XOR,
171 PREFIX_NOT, // !
172 POSTFIX_NOT, // '
173 LPAREN,
174 RPAREN,
175 END
176 };
177
178 struct Token {
179 TokenKind kind;
180 StringRef spelling;
181 SMLoc loc;
182 };
183
184 LibertyLexer &lexer;
185 OpBuilder &builder;
186 const llvm::StringMap<Value> &values;
187 SMLoc baseLoc;
188 const char *exprStart;
189 SmallVector<Token> tokens;
190 size_t pos = 0;
191 Value trueVal = nullptr;
192
193 SMLoc getLoc(const char *ptr);
194 void tokenize(StringRef expr);
195 Token peek() const;
196 Token consume();
197 Value createNot(Value val, SMLoc loc);
198
199 ParseResult parseOrExpr(Value &result);
200 ParseResult parseXorExpr(Value &result);
201 ParseResult parseAndExpr(Value &result);
202 ParseResult parseUnaryExpr(Value &result);
203
204 InFlightDiagnostic emitError(llvm::SMLoc loc, const Twine &message) const;
205 InFlightDiagnostic emitWarning(llvm::SMLoc loc, const Twine &message) const;
206};
207
208//===----------------------------------------------------------------------===//
209// ExpressionParser Implementation
210//===----------------------------------------------------------------------===//
211
212ExpressionParser::ExpressionParser(LibertyLexer &lexer, OpBuilder &builder,
213 StringRef expr,
214 const llvm::StringMap<Value> &values,
215 SMLoc baseLoc)
216 : lexer(lexer), builder(builder), values(values), baseLoc(baseLoc),
217 exprStart(expr.begin()) {
218 tokenize(expr);
219}
220
221ParseResult ExpressionParser::parse(Value &result) {
222 return parseOrExpr(result);
223}
224
225SMLoc ExpressionParser::getLoc(const char *ptr) {
226 size_t offset = ptr - exprStart;
227 return SMLoc::getFromPointer(baseLoc.getPointer() + 1 + offset);
228}
229
230void ExpressionParser::tokenize(StringRef expr) {
231 const char *ptr = expr.begin();
232 const char *end = expr.end();
233
234 while (ptr < end) {
235 // Skip whitespace
236 if (isspace(*ptr)) {
237 ++ptr;
238 continue;
239 }
240
241 SMLoc loc = getLoc(ptr);
242
243 // Identifier
244 if (isalnum(*ptr) || *ptr == '_') {
245 const char *start = ptr;
246 while (ptr < end && (isalnum(*ptr) || *ptr == '_'))
247 ++ptr;
248 tokens.push_back({TokenKind::ID, StringRef(start, ptr - start), loc});
249 continue;
250 }
251
252 // Operators and punctuation
253 switch (*ptr) {
254 case '*':
255 case '&':
256 tokens.push_back({TokenKind::AND, StringRef(ptr, 1), loc});
257 break;
258 case '+':
259 case '|':
260 tokens.push_back({TokenKind::OR, StringRef(ptr, 1), loc});
261 break;
262 case '^':
263 tokens.push_back({TokenKind::XOR, StringRef(ptr, 1), loc});
264 break;
265 case '!':
266 tokens.push_back({TokenKind::PREFIX_NOT, StringRef(ptr, 1), loc});
267 break;
268 case '\'':
269 tokens.push_back({TokenKind::POSTFIX_NOT, StringRef(ptr, 1), loc});
270 break;
271 case '(':
272 tokens.push_back({TokenKind::LPAREN, StringRef(ptr, 1), loc});
273 break;
274 case ')':
275 tokens.push_back({TokenKind::RPAREN, StringRef(ptr, 1), loc});
276 break;
277 }
278 ++ptr;
279 }
280
281 tokens.push_back({TokenKind::END, "", getLoc(ptr)});
282}
283
284ExpressionParser::Token ExpressionParser::peek() const { return tokens[pos]; }
285
286ExpressionParser::Token ExpressionParser::consume() { return tokens[pos++]; }
287
288Value ExpressionParser::createNot(Value val, SMLoc loc) {
289 if (!trueVal)
290 trueVal = hw::ConstantOp::create(builder, lexer.translateLocation(loc),
291 builder.getI1Type(), 1);
292 return comb::XorOp::create(builder, lexer.translateLocation(loc), val,
293 trueVal);
294}
295
296/// Parse OR expressions with lowest precedence.
297/// OrExpr -> XorExpr { ('+'|'|') XorExpr }
298/// This implements left-associative parsing: A + B + C becomes (A + B) + C
299ParseResult ExpressionParser::parseOrExpr(Value &result) {
300 Value lhs;
301 if (parseXorExpr(lhs))
302 return failure();
303 while (peek().kind == TokenKind::OR) {
304 auto loc = consume().loc;
305 Value rhs;
306 if (parseXorExpr(rhs))
307 return failure();
308 lhs = comb::OrOp::create(builder, lexer.translateLocation(loc), lhs, rhs);
309 }
310 result = lhs;
311 return success();
312}
313
314/// Parse XOR expressions with medium precedence.
315/// XorExpr -> AndExpr { '^' AndExpr }
316/// This implements left-associative parsing: A ^ B ^ C becomes (A ^ B) ^ C
317ParseResult ExpressionParser::parseXorExpr(Value &result) {
318 Value lhs;
319 if (parseAndExpr(lhs))
320 return failure();
321 while (peek().kind == TokenKind::XOR) {
322 auto loc = consume().loc;
323 Value rhs;
324 if (parseAndExpr(rhs))
325 return failure();
326 lhs = comb::XorOp::create(builder, lexer.translateLocation(loc), lhs, rhs);
327 }
328 result = lhs;
329 return success();
330}
331
332/// Parse AND expressions with highest precedence.
333/// AndExpr -> UnaryExpr { ('*'|'&'|implicit) UnaryExpr }
334/// This implements left-associative parsing: A * B * C becomes (A * B) * C
335/// Space between expressions is treated as implicit AND.
336ParseResult ExpressionParser::parseAndExpr(Value &result) {
337 Value lhs;
338 if (parseUnaryExpr(lhs))
339 return failure();
340 while (peek().kind == TokenKind::AND ||
341 (peek().kind == TokenKind::ID || peek().kind == TokenKind::LPAREN ||
342 peek().kind == TokenKind::PREFIX_NOT)) {
343 auto loc = peek().loc;
344 if (peek().kind == TokenKind::AND)
345 consume();
346 Value rhs;
347 if (parseUnaryExpr(rhs))
348 return failure();
349 lhs = comb::AndOp::create(builder, lexer.translateLocation(loc), lhs, rhs);
350 }
351 result = lhs;
352 return success();
353}
354
355/// Parse unary expressions and primary expressions.
356/// UnaryExpr -> '!' UnaryExpr | '(' OrExpr ')' ['\''] | ID ['\']
357/// Handles prefix NOT (!), parenthesized expressions, and identifiers.
358/// Postfix NOT (') is only allowed after expressions and identifiers.
359ParseResult ExpressionParser::parseUnaryExpr(Value &result) {
360 // Prefix NOT (!)
361 if (peek().kind == TokenKind::PREFIX_NOT) {
362 auto loc = consume().loc;
363 Value val;
364 if (parseUnaryExpr(val))
365 return failure();
366 result = createNot(val, loc);
367 return success();
368 }
369
370 // Parenthesized expression
371 if (peek().kind == TokenKind::LPAREN) {
372 consume();
373 Value val;
374 if (parseOrExpr(val))
375 return failure();
376 if (peek().kind != TokenKind::RPAREN)
377 return failure();
378 consume();
379 // Postfix NOT (')
380 if (peek().kind == TokenKind::POSTFIX_NOT) {
381 auto loc = consume().loc;
382 val = createNot(val, loc);
383 }
384 result = val;
385 return success();
386 }
387
388 // Identifier
389 if (peek().kind == TokenKind::ID) {
390 auto tok = consume();
391 StringRef name = tok.spelling;
392 auto it = values.find(name);
393 if (it == values.end())
394 return emitError(tok.loc, "variable not found");
395 Value val = it->second;
396 // Postfix NOT (')
397 if (peek().kind == TokenKind::POSTFIX_NOT) {
398 auto loc = consume().loc;
399 val = createNot(val, loc);
400 }
401 result = val;
402 return success();
403 }
404
405 return emitError(peek().loc, "expected expression");
406}
407
408InFlightDiagnostic ExpressionParser::emitError(llvm::SMLoc loc,
409 const Twine &message) const {
410 return mlir::emitError(lexer.translateLocation(loc), message);
411}
412
413InFlightDiagnostic ExpressionParser::emitWarning(llvm::SMLoc loc,
414 const Twine &message) const {
415 return mlir::emitWarning(lexer.translateLocation(loc), message);
416}
417
418// Parsed result of a Liberty group
419struct LibertyGroup {
420 StringRef name;
421 SMLoc loc;
422 SmallVector<Attribute> args;
423 struct AttrEntry {
424 StringRef name;
425 Attribute value;
426 SMLoc loc;
427 AttrEntry(StringRef name, Attribute value, SMLoc loc)
428 : name(name), value(value), loc(loc) {}
429 };
430 SmallVector<AttrEntry> attrs;
431 SmallVector<std::unique_ptr<LibertyGroup>> subGroups;
432
433 // Helper to find an attribute by name
434 std::pair<Attribute, SMLoc> getAttribute(StringRef name) const {
435 for (const auto &attr : attrs)
436 if (attr.name == name)
437 return {attr.value, attr.loc};
438 return {};
439 }
440
441 void eraseSubGroup(llvm::function_ref<bool(const LibertyGroup &)> pred) {
442 subGroups.erase(
443 std::remove_if(subGroups.begin(), subGroups.end(),
444 [pred](const auto &group) { return pred(*group); }),
445 subGroups.end());
446 }
447
448 LogicalResult checkArgs(
449 size_t n,
450 llvm::function_ref<LogicalResult(size_t idx, Attribute)> pred) const {
451 if (args.size() != n)
452 return failure();
453 for (size_t i = 0; i < n; ++i)
454 if (failed(pred(i, args[i])))
455 return failure();
456 return success();
457 }
458};
459
460class LibertyParser {
461public:
462 LibertyParser(const llvm::SourceMgr &sourceMgr, MLIRContext *context,
463 ModuleOp module)
464 : lexer(sourceMgr, context), module(module),
465 builder(module.getBodyRegion()) {}
466
467 ParseResult parse();
468
469private:
470 LibertyLexer lexer;
471 ModuleOp module;
472 OpBuilder builder;
473
474 // Specific group parsers
475 ParseResult parseLibrary();
476 ParseResult parseGroupBody(LibertyGroup &group);
477 ParseResult parseStatement(LibertyGroup &parent);
478
479 // Lowering methods
480 ParseResult lowerCell(const LibertyGroup &group, StringAttr &cellNameAttr);
481
482 //===--------------------------------------------------------------------===//
483 // Parser for Subgroup of "cell" group.
484 //===--------------------------------------------------------------------===//
485
486 Attribute convertGroupToAttr(const LibertyGroup &group);
487
488 // Attribute parsing
489 ParseResult parseAttribute(Attribute &result);
490 static ParseResult parseAttribute(Attribute &result, OpBuilder &builder,
491 StringRef attr);
492
493 // Expression parsing
494 ParseResult parseExpression(Value &result, StringRef expr,
495 const llvm::StringMap<Value> &values, SMLoc loc);
496
497 InFlightDiagnostic emitError(llvm::SMLoc loc, const Twine &message) const {
498 return mlir::emitError(lexer.translateLocation(loc), message);
499 }
500
501 InFlightDiagnostic emitWarning(llvm::SMLoc loc, const Twine &message) const {
502 return mlir::emitWarning(lexer.translateLocation(loc), message);
503 }
504
505 ParseResult consume(LibertyTokenKind kind, const Twine &msg) {
506 if (lexer.nextToken().is(kind))
507 return success();
508 return emitError(lexer.getCurrentLoc(), msg);
509 }
510
511 ParseResult consumeUntil(LibertyTokenKind kind) {
512 while (!lexer.peekToken().is(kind) &&
513 lexer.peekToken().kind != LibertyTokenKind::EndOfFile)
514 lexer.nextToken();
515 if (lexer.peekToken().is(kind))
516 return success();
517 return emitError(lexer.getCurrentLoc(),
518 " expected " + stringifyTokenKind(kind));
519 }
520
521 ParseResult consume(LibertyTokenKind kind) {
522 if (lexer.nextToken().is(kind))
523 return success();
524 return emitError(lexer.getCurrentLoc(),
525 " expected " + stringifyTokenKind(kind));
526 }
527
528 ParseResult expect(LibertyTokenKind kind) {
529 if (!lexer.peekToken().is(kind))
530 return emitError(lexer.getCurrentLoc(),
531 " expected " + stringifyTokenKind(kind));
532
533 return success();
534 }
535
536 StringRef getTokenSpelling(const LibertyToken &token) {
537 StringRef str = token.spelling;
538 if (token.kind == LibertyTokenKind::String)
539 str = str.drop_front().drop_back();
540 return str;
541 }
542};
543
544//===----------------------------------------------------------------------===//
545// LibertyLexer Implementation
546//===----------------------------------------------------------------------===//
547
548void LibertyLexer::skipWhitespaceAndComments() {
549 while (curPtr < curBuffer.end()) {
550 if (isspace(*curPtr)) {
551 ++curPtr;
552 continue;
553 }
554
555 // Comments
556 if (*curPtr == '/' && curPtr + 1 < curBuffer.end()) {
557 if (*(curPtr + 1) == '*') { // /* ... */
558 curPtr += 2;
559 while (curPtr + 1 < curBuffer.end() &&
560 (*curPtr != '*' || *(curPtr + 1) != '/'))
561 ++curPtr;
562 if (curPtr + 1 < curBuffer.end())
563 curPtr += 2;
564 continue;
565 }
566 if (*(curPtr + 1) == '/') { // // ...
567 while (curPtr < curBuffer.end() && *curPtr != '\n')
568 ++curPtr;
569 continue;
570 }
571 }
572
573 // Backslash newline continuation
574 if (*curPtr == '\\' && curPtr + 1 < curBuffer.end() &&
575 *(curPtr + 1) == '\n') {
576 curPtr += 2;
577 continue;
578 }
579
580 break;
581 }
582}
583
584LibertyToken LibertyLexer::lexIdentifier() {
585 const char *start = curPtr;
586 while (curPtr < curBuffer.end() &&
587 (isalnum(*curPtr) || *curPtr == '_' || *curPtr == '.'))
588 ++curPtr;
589 return makeToken(LibertyTokenKind::Identifier, start);
590}
591
592LibertyToken LibertyLexer::lexString() {
593 const char *start = curPtr;
594 ++curPtr; // skip opening quote
595 while (curPtr < curBuffer.end() && *curPtr != '"') {
596 if (*curPtr == '\\' && curPtr + 1 < curBuffer.end())
597 ++curPtr; // skip escaped char
598 ++curPtr;
599 }
600 if (curPtr < curBuffer.end())
601 ++curPtr; // skip closing quote
602 return makeToken(LibertyTokenKind::String, start);
603}
604
605LibertyToken LibertyLexer::lexNumber() {
606 const char *start = curPtr;
607 bool seenDot = false;
608 while (curPtr < curBuffer.end()) {
609 if (isdigit(*curPtr)) {
610 ++curPtr;
611 } else if (*curPtr == '.') {
612 if (seenDot) {
613 mlir::emitError(translateLocation(SMLoc::getFromPointer(curPtr)),
614 "multiple decimal points in number");
615 return makeToken(LibertyTokenKind::Error, start);
616 }
617 seenDot = true;
618 ++curPtr;
619 } else {
620 break;
621 }
622 }
623 return makeToken(LibertyTokenKind::Number, start);
624}
625
626LibertyToken LibertyLexer::makeToken(LibertyTokenKind kind, const char *start) {
627 return LibertyToken(kind, StringRef(start, curPtr - start),
628 SMLoc::getFromPointer(start));
629}
630
631LibertyToken LibertyLexer::nextToken() {
632 skipWhitespaceAndComments();
633
634 if (curPtr >= curBuffer.end())
635 return makeToken(LibertyTokenKind::EndOfFile, curPtr);
636
637 const char *start = curPtr;
638 char c = *curPtr;
639
640 if (isalpha(c) || c == '_')
641 return lexIdentifier();
642
643 if (isdigit(c) ||
644 (c == '.' && curPtr + 1 < curBuffer.end() && isdigit(*(curPtr + 1))))
645 return lexNumber();
646
647 if (c == '"')
648 return lexString();
649
650 ++curPtr;
651 switch (c) {
652 case '{':
653 return makeToken(LibertyTokenKind::LBrace, start);
654 case '}':
655 return makeToken(LibertyTokenKind::RBrace, start);
656 case '(':
657 return makeToken(LibertyTokenKind::LParen, start);
658 case ')':
659 return makeToken(LibertyTokenKind::RParen, start);
660 case ':':
661 return makeToken(LibertyTokenKind::Colon, start);
662 case ';':
663 return makeToken(LibertyTokenKind::Semi, start);
664 case ',':
665 return makeToken(LibertyTokenKind::Comma, start);
666
667 default:
668 return makeToken(LibertyTokenKind::Error, start);
669 }
670}
671
672LibertyToken LibertyLexer::peekToken() {
673 const char *savedPtr = curPtr;
674 LibertyToken token = nextToken();
675 curPtr = savedPtr;
676 return token;
677}
678
679//===----------------------------------------------------------------------===//
680// LibertyParser Implementation
681//===----------------------------------------------------------------------===//
682
683ParseResult LibertyParser::parse() {
684 while (lexer.peekToken().kind != LibertyTokenKind::EndOfFile) {
685 auto token = lexer.peekToken();
686 // Skip any stray tokens that aren't valid group starts
687 if (token.kind != LibertyTokenKind::Identifier ||
688 token.spelling != "library") {
689 return emitError(token.location, "expected `library` keyword");
690 }
691 lexer.nextToken();
692 if (parseLibrary())
693 return failure();
694 }
695 return success();
696}
697
698ParseResult LibertyParser::parseLibrary() {
699 LibertyGroup libertyLib;
700 if (parseGroupBody(libertyLib))
701 return failure();
702
703 DenseSet<StringAttr> seenCells;
704 for (auto &stmt : libertyLib.subGroups) {
705 // TODO: Support more group types
706 if (stmt->name == "cell") {
707 StringAttr cellNameAttr;
708 if (lowerCell(*stmt, cellNameAttr))
709 return failure();
710 if (!seenCells.insert(cellNameAttr).second)
711 return emitError(stmt->loc, "redefinition of cell '" +
712 cellNameAttr.getValue() + "'");
713 continue;
714 }
715 }
716
717 libertyLib.eraseSubGroup(
718 [](const LibertyGroup &group) { return group.name == "cell"; });
719 auto attr = convertGroupToAttr(libertyLib);
720 module->setAttr("synth.liberty.library", attr);
721 return success();
722}
723
724// Parse a template like: lu_table_template (delay_template_6x6) { variable_1:
725// total_output_net_capacitance; variable_2: input_net_transition; }
726Attribute LibertyParser::convertGroupToAttr(const LibertyGroup &group) {
727 SmallVector<NamedAttribute> attrs;
728 if (!group.args.empty())
729 attrs.push_back(
730 builder.getNamedAttr("args", builder.getArrayAttr(group.args)));
731
732 for (const auto &attr : group.attrs)
733 attrs.push_back(builder.getNamedAttr(attr.name, attr.value));
734
735 llvm::StringMap<SmallVector<Attribute>> subGroups;
736 for (const auto &sub : group.subGroups)
737 subGroups[sub->name].push_back(convertGroupToAttr(*sub));
738
739 for (auto &it : subGroups)
740 attrs.push_back(
741 builder.getNamedAttr(it.getKey(), builder.getArrayAttr(it.getValue())));
742
743 return builder.getDictionaryAttr(attrs);
744}
745
746ParseResult LibertyParser::lowerCell(const LibertyGroup &group,
747 StringAttr &cellNameAttr) {
748 if (group.args.empty())
749 return emitError(group.loc, "cell missing name");
750
751 cellNameAttr = dyn_cast<StringAttr>(group.args[0]);
752 if (!cellNameAttr)
753 return emitError(group.loc, "cell name must be a string");
754
755 SmallVector<hw::PortInfo> ports;
756 SmallVector<const LibertyGroup *> pinGroups;
757 llvm::DenseSet<StringAttr> seenPins;
758
759 // First pass: gather ports
760 for (const auto &sub : group.subGroups) {
761 if (sub->name != "pin")
762 continue;
763
764 pinGroups.push_back(sub.get());
765 if (sub->args.empty())
766 return emitError(sub->loc, "pin missing name");
767
768 StringAttr pinName = dyn_cast<StringAttr>(sub->args[0]);
769 if (!seenPins.insert(pinName).second)
770 return emitError(sub->loc,
771 "redefinition of pin '" + pinName.getValue() + "'");
772
773 std::optional<hw::ModulePort::Direction> dir;
774 SmallVector<NamedAttribute> pinAttrs;
775
776 for (const auto &attr : sub->attrs) {
777 if (attr.name == "direction") {
778 if (auto val = dyn_cast<StringAttr>(attr.value)) {
779 if (val.getValue() == "input")
780 dir = hw::ModulePort::Direction::Input;
781 else if (val.getValue() == "output")
782 dir = hw::ModulePort::Direction::Output;
783 else if (val.getValue() == "inout")
784 dir = hw::ModulePort::Direction::InOut;
785 else
786 return emitError(sub->loc,
787 "pin direction must be input, output, or inout");
788 } else {
789 return emitError(sub->loc,
790 "pin direction must be a string attribute");
791 }
792 continue;
793 }
794 pinAttrs.push_back(builder.getNamedAttr(attr.name, attr.value));
795 }
796
797 if (!dir)
798 return emitError(sub->loc, "pin direction must be specified");
799
800 llvm::StringMap<SmallVector<Attribute>> subGroups;
801 for (const auto &child : sub->subGroups) {
802 // TODO: Properly handle timing subgroups etc.
803 subGroups[child->name].push_back(convertGroupToAttr(*child));
804 }
805
806 for (auto &it : subGroups)
807 pinAttrs.push_back(builder.getNamedAttr(
808 it.getKey(), builder.getArrayAttr(it.getValue())));
809
810 auto libertyAttrs = builder.getDictionaryAttr(pinAttrs);
811 auto attrs = builder.getDictionaryAttr(
812 builder.getNamedAttr("synth.liberty.pin", libertyAttrs));
813
814 hw::PortInfo port;
815 port.name = pinName;
816 port.type = builder.getI1Type();
817 port.dir = *dir;
818 port.attrs = attrs;
819 ports.push_back(port);
820 }
821
822 // Fix up argNum for inputs
823 int inputIdx = 0;
824 for (auto &p : ports) {
825 if (p.dir == hw::ModulePort::Direction::Input)
826 p.argNum = inputIdx++;
827 else
828 p.argNum = 0;
829 }
830
831 auto loc = lexer.translateLocation(group.loc);
832 auto moduleOp = hw::HWModuleOp::create(builder, loc, cellNameAttr, ports);
833
834 OpBuilder::InsertionGuard guard(builder);
835 builder.setInsertionPointToStart(moduleOp.getBodyBlock());
836
837 llvm::StringMap<Value> portValues;
838 for (const auto &port : ports)
839 if (port.dir == hw::ModulePort::Direction::Input)
840 portValues[port.name.getValue()] =
841 moduleOp.getBodyBlock()->getArgument(port.argNum);
842
843 SmallVector<Value> outputs;
844 for (const auto &port : ports) {
845 if (port.dir != hw::ModulePort::Direction::Output)
846 continue;
847
848 auto *it = llvm::find_if(pinGroups, [&](const LibertyGroup *g) {
849 assert(g->name == "pin" && "expected pin group");
850 // First arg is the pin name
851 return cast<StringAttr>(g->args[0]) == port.name;
852 });
853
854 if (!it)
855 continue;
856 const LibertyGroup *pg = *it;
857 auto attrPair = pg->getAttribute("function");
858 if (!attrPair.first)
859 return emitError(pg->loc, "expected function attribute");
860 Value val;
861 if (parseExpression(val, cast<StringAttr>(attrPair.first).getValue(),
862 portValues, attrPair.second))
863 return failure();
864 outputs.push_back(val);
865 }
866
867 auto *block = moduleOp.getBodyBlock();
868 block->getTerminator()->setOperands(outputs);
869 return success();
870}
871
872ParseResult LibertyParser::parseGroupBody(LibertyGroup &group) {
873 // Parse args: ( arg1, arg2 )
874 if (lexer.peekToken().kind == LibertyTokenKind::LParen) {
875 lexer.nextToken(); // (
876 while (lexer.peekToken().kind != LibertyTokenKind::RParen) {
877 Attribute arg;
878 if (parseAttribute(arg))
879 return failure();
880 group.args.push_back(arg);
881 if (lexer.peekToken().kind == LibertyTokenKind::Comma)
882 lexer.nextToken();
883 }
884 if (consume(LibertyTokenKind::RParen, "expected ')'"))
885 return failure();
886 }
887
888 // Parse body: { ... }
889 if (lexer.peekToken().kind == LibertyTokenKind::LBrace) {
890 lexer.nextToken(); // {
891 while (lexer.peekToken().kind != LibertyTokenKind::RBrace &&
892 lexer.peekToken().kind != LibertyTokenKind::EndOfFile) {
893 if (parseStatement(group))
894 return failure();
895 }
896 if (consume(LibertyTokenKind::RBrace, "expected '}'"))
897 return failure();
898 } else {
899 // Optional semicolon if no body
900 if (lexer.peekToken().kind == LibertyTokenKind::Semi)
901 lexer.nextToken();
902 }
903 return success();
904}
905
906// Parse group, attribute, or define statements
907ParseResult LibertyParser::parseStatement(LibertyGroup &parent) {
908 auto nameTok = lexer.nextToken();
909 if (nameTok.kind != LibertyTokenKind::Identifier)
910 return emitError(nameTok.location, "expected identifier");
911 StringRef name = nameTok.spelling;
912
913 // Attribute statement.
914 if (lexer.peekToken().kind == LibertyTokenKind::Colon) {
915 lexer.nextToken(); // :
916 Attribute val;
917 auto loc = lexer.peekToken().location;
918 if (parseAttribute(val))
919 return failure();
920 parent.attrs.emplace_back(name, val, loc);
921 return consume(LibertyTokenKind::Semi, "expected ';'");
922 }
923
924 // Group statement.
925 if (lexer.peekToken().kind == LibertyTokenKind::LParen) {
926 auto subGroup = std::make_unique<LibertyGroup>();
927 subGroup->name = name;
928 subGroup->loc = nameTok.location;
929 if (parseGroupBody(*subGroup))
930 return failure();
931 parent.subGroups.push_back(std::move(subGroup));
932 return success();
933 }
934
935 // TODO: Support define.
936
937 return emitError(nameTok.location, "expected ':' or '('");
938}
939
940ParseResult LibertyParser::parseAttribute(Attribute &result, OpBuilder &builder,
941 StringRef attr) {
942 double val;
943 if (!attr.getAsDouble(val)) {
944 result = builder.getF64FloatAttr(val);
945 } else {
946 // Keep as string if not a valid number
947 result = builder.getStringAttr(attr);
948 }
949 return success();
950}
951// Parse an attribute value, which can be:
952// - A string: "value"
953// - A number: 1.23
954// - An identifier: value
955ParseResult LibertyParser::parseAttribute(Attribute &result) {
956 auto token = lexer.peekToken();
957 // Number token
958 if (token.is(LibertyTokenKind::Number)) {
959 lexer.nextToken();
960 StringRef numStr = token.spelling;
961 double val;
962 if (!numStr.getAsDouble(val)) {
963 result = builder.getF64FloatAttr(val);
964 return success();
965 }
966 return emitError(token.location, "expected number value");
967 }
968
969 // Identifier token
970 if (token.is(LibertyTokenKind::Identifier)) {
971 lexer.nextToken();
972 result = builder.getStringAttr(token.spelling);
973 return success();
974 }
975
976 if (token.is(LibertyTokenKind::String)) {
977 lexer.nextToken();
978 StringRef str = getTokenSpelling(token);
979 result = builder.getStringAttr(str);
980 return success();
981 }
982
983 // TODO: Add array for timing attributes
984 return emitError(token.location, "expected attribute value");
985}
986
987// Parse boolean expressions from Liberty function attributes
988// Supports: *, +, ^, !, (), and identifiers
989ParseResult LibertyParser::parseExpression(Value &result, StringRef expr,
990 const llvm::StringMap<Value> &values,
991 SMLoc loc) {
992 ExpressionParser parser(lexer, builder, expr, values, loc);
993 return parser.parse(result);
994}
995
996} // namespace
997
998namespace circt::liberty {
1000 TranslateToMLIRRegistration reg(
1001 "import-liberty", "Import Liberty file",
1002 [](llvm::SourceMgr &sourceMgr, MLIRContext *context) {
1003 ModuleOp module = ModuleOp::create(UnknownLoc::get(context));
1004 LibertyParser parser(sourceMgr, context, module);
1005 // Load required dialects
1006 context->loadDialect<hw::HWDialect>();
1007 context->loadDialect<comb::CombDialect>();
1008 if (failed(parser.parse()))
1009 return OwningOpRef<ModuleOp>();
1010 return OwningOpRef<ModuleOp>(module);
1011 },
1012 [](DialectRegistry &registry) {
1013 registry.insert<HWDialect>();
1014 registry.insert<comb::CombDialect>();
1015 });
1016}
1017} // 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:216
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.