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