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