Loading [MathJax]/extensions/tex2jax.js
CIRCT 21.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
CalyxEmitter.cpp
Go to the documentation of this file.
1//===- CalyxEmitter.cpp - Calyx dialect to .futil emitter -----------------===//
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 implements an emitter for the native Calyx language, which uses
10// .futil as an alias.
11//
12//===----------------------------------------------------------------------===//
13
19#include "circt/Support/LLVM.h"
20#include "mlir/IR/BuiltinOps.h"
21#include "mlir/IR/BuiltinTypes.h"
22#include "mlir/Tools/mlir-translate/Translation.h"
23#include "llvm/ADT/SmallSet.h"
24#include "llvm/ADT/TypeSwitch.h"
25#include "llvm/Support/Casting.h"
26#include "llvm/Support/FormatVariadic.h"
27#include <bitset>
28#include <string>
29
30using namespace circt;
31using namespace calyx;
32using namespace mlir;
33
34namespace {
35
36static constexpr std::string_view LSquare() { return "["; }
37static constexpr std::string_view RSquare() { return "]"; }
38static constexpr std::string_view LAngleBracket() { return "<"; }
39static constexpr std::string_view RAngleBracket() { return ">"; }
40static constexpr std::string_view LParen() { return "("; }
41static constexpr std::string_view RParen() { return ")"; }
42static constexpr std::string_view colon() { return ": "; }
43static constexpr std::string_view space() { return " "; }
44static constexpr std::string_view period() { return "."; }
45static constexpr std::string_view questionMark() { return " ? "; }
46static constexpr std::string_view exclamationMark() { return "!"; }
47static constexpr std::string_view equals() { return "="; }
48static constexpr std::string_view comma() { return ", "; }
49static constexpr std::string_view arrow() { return " -> "; }
50static constexpr std::string_view quote() { return "\""; }
51static constexpr std::string_view apostrophe() { return "'"; }
52static constexpr std::string_view LBraceEndL() { return "{\n"; }
53static constexpr std::string_view RBraceEndL() { return "}\n"; }
54static constexpr std::string_view semicolonEndL() { return ";\n"; }
55static constexpr std::string_view addressSymbol() { return "@"; }
56static constexpr std::string_view endl() { return "\n"; }
57static constexpr std::string_view metadataLBrace() { return "#{\n"; }
58static constexpr std::string_view metadataRBrace() { return "}#\n"; }
59
60/// A list of integer attributes supported by the native Calyx compiler.
61constexpr std::array<StringRef, 7> integerAttributes{
62 "external", "static", "share", "bound",
63 "write_together", "read_together", "pos",
64};
65
66/// A list of boolean attributes supported by the native Calyx compiler.
67constexpr std::array<StringRef, 12> booleanAttributes{
68 "clk", "reset", "go", "done", "generated", "precious",
69 "toplevel", "stable", "nointerface", "inline", "state_share", "data",
70};
71
72static std::optional<StringRef> getCalyxAttrIdentifier(NamedAttribute attr) {
73 StringRef identifier = attr.getName().strref();
74 if (identifier.contains(".")) {
75 Dialect *dialect = attr.getNameDialect();
76 if (dialect != nullptr && isa<CalyxDialect>(*dialect)) {
77 return std::get<1>(identifier.split("."));
78 }
79 return std::nullopt;
80 }
81
82 return identifier;
83}
84
85/// Determines whether the given identifier is a valid Calyx attribute.
86static bool isValidCalyxAttribute(StringRef identifier) {
87
88 return llvm::find(integerAttributes, identifier) != integerAttributes.end() ||
89 llvm::find(booleanAttributes, identifier) != booleanAttributes.end();
90}
91
92/// A tracker to determine which libraries should be imported for a given
93/// program.
94struct ImportTracker {
95public:
96 /// Returns the list of library names used for in this program.
97 /// E.g. if `primitives/core.futil` is used, returns { "core" }.
98 FailureOr<llvm::SmallSet<StringRef, 4>> getLibraryNames(ModuleOp module) {
99 auto walkRes = module.walk([&](ComponentOp component) {
100 for (auto &op : *component.getBodyBlock()) {
101 if (!isa<CellInterface>(op) || isa<InstanceOp, PrimitiveOp>(op))
102 // It is not a primitive.
103 continue;
104 auto libraryName = getLibraryFor(&op);
105 if (failed(libraryName))
106 return WalkResult::interrupt();
107 usedLibraries.insert(*libraryName);
108 }
109 return WalkResult::advance();
110 });
111 if (walkRes.wasInterrupted())
112 return failure();
113 return usedLibraries;
114 }
115
116private:
117 /// Returns the library name for a given Operation Type.
118 FailureOr<StringRef> getLibraryFor(Operation *op) {
119 return TypeSwitch<Operation *, FailureOr<StringRef>>(op)
120 .Case<AddLibOp, RegisterOp, UndefLibOp, WireLibOp>(
121 [&](auto op) -> FailureOr<StringRef> {
122 static constexpr std::string_view sCompile = "compile";
123 return {sCompile};
124 })
125 .Case<NotLibOp, AndLibOp, OrLibOp, XorLibOp, SubLibOp, GtLibOp, LtLibOp,
126 EqLibOp, NeqLibOp, GeLibOp, LeLibOp, LshLibOp, RshLibOp,
127 SliceLibOp, PadLibOp, MuxLibOp>(
128 [&](auto op) -> FailureOr<StringRef> {
129 static constexpr std::string_view sCore = "core";
130 return {sCore};
131 })
132 .Case<SgtLibOp, SltLibOp, SeqLibOp, SneqLibOp, SgeLibOp, SleLibOp,
133 SrshLibOp, MultPipeLibOp, RemUPipeLibOp, RemSPipeLibOp,
134 DivUPipeLibOp, DivSPipeLibOp, ExtSILibOp>(
135 [&](auto op) -> FailureOr<StringRef> {
136 static constexpr std::string_view sBinaryOperators =
137 "binary_operators";
138 return {sBinaryOperators};
139 })
140 .Case<MemoryOp>([&](auto op) -> FailureOr<StringRef> {
141 static constexpr std::string_view sMemories = "memories/comb";
142 return {sMemories};
143 })
144 .Case<SeqMemoryOp>([&](auto op) -> FailureOr<StringRef> {
145 static constexpr std::string_view sMemories = "memories/seq";
146 return {sMemories};
147 })
148 .Case<ConstantOp>([&](auto op) -> FailureOr<StringRef> {
149 static constexpr std::string_view sFloat = "float";
150 return {sFloat};
151 })
152 .Case<AddFOpIEEE754>([&](auto op) -> FailureOr<StringRef> {
153 static constexpr std::string_view sFloatingPoint = "float/addFN";
154 return {sFloatingPoint};
155 })
156 .Case<MulFOpIEEE754>([&](auto op) -> FailureOr<StringRef> {
157 static constexpr std::string_view sFloatingPoint = "float/mulFN";
158 return {sFloatingPoint};
159 })
160 .Case<CompareFOpIEEE754>([&](auto op) -> FailureOr<StringRef> {
161 static constexpr std::string_view sFloatingPoint = "float/compareFN";
162 return {sFloatingPoint};
163 })
164 .Case<FpToIntOpIEEE754>([&](auto op) -> FailureOr<StringRef> {
165 static constexpr std::string_view sFloatingPoint = "float/fpToInt";
166 return {sFloatingPoint};
167 })
168 .Case<IntToFpOpIEEE754>([&](auto op) -> FailureOr<StringRef> {
169 static constexpr std::string_view sFloatingPoint = "float/intToFp";
170 return {sFloatingPoint};
171 })
172 .Case<DivSqrtOpIEEE754>([&](auto op) -> FailureOr<StringRef> {
173 static constexpr std::string_view sFloatingPoint = "float/divSqrtFN";
174 return {sFloatingPoint};
175 })
176 .Default([&](auto op) {
177 auto diag = op->emitOpError() << "not supported for emission";
178 return diag;
179 });
180 }
181 /// Maintains a unique list of libraries used throughout the lifetime of the
182 /// tracker.
183 llvm::SmallSet<StringRef, 4> usedLibraries;
184};
185
186//===----------------------------------------------------------------------===//
187// Emitter
188//===----------------------------------------------------------------------===//
189
190/// An emitter for Calyx dialect operations to .futil output.
191struct Emitter {
192 Emitter(llvm::raw_ostream &os) : os(os) {}
193 LogicalResult finalize();
194
195 // Indentation
196 raw_ostream &indent() { return os.indent(currentIndent); }
197 void addIndent() { currentIndent += 2; }
198 void reduceIndent() {
199 assert(currentIndent >= 2 && "Unintended indentation wrap");
200 currentIndent -= 2;
201 }
202
203 // Module emission
204 void emitModule(ModuleOp op);
205
206 // Metadata emission for the Cider debugger.
207 void emitCiderMetadata(mlir::ModuleOp op) {
208 auto metadata = op->getAttrOfType<ArrayAttr>("calyx.metadata");
209 if (!metadata)
210 return;
211
212 constexpr std::string_view metadataIdentifier = "metadata";
213 os << endl() << metadataIdentifier << space() << metadataLBrace();
214
215 for (auto sourceLoc : llvm::enumerate(metadata)) {
216 // <index>: <source-location>\n
217 os << std::to_string(sourceLoc.index()) << colon();
218 os << cast<StringAttr>(sourceLoc.value()).getValue() << endl();
219 }
220
221 os << metadataRBrace();
222 }
223
224 /// Import emission.
225 LogicalResult emitImports(ModuleOp op) {
226 auto emitImport = [&](StringRef library) {
227 // Libraries share a common relative path:
228 // primitives/<library-name>.futil
229 os << "import " << quote() << "primitives/" << library << period()
230 << "futil" << quote() << semicolonEndL();
231 };
232
233 auto libraryNames = importTracker.getLibraryNames(op);
234 if (failed(libraryNames))
235 return failure();
236
237 for (StringRef library : *libraryNames)
238 emitImport(library);
239
240 return success();
241 }
242
243 // Component emission
244 void emitComponent(ComponentInterface op);
245 void emitComponentPorts(ComponentInterface op);
246
247 // HWModuleExtern emission
248 void emitPrimitiveExtern(hw::HWModuleExternOp op);
249 void emitPrimitivePorts(hw::HWModuleExternOp op);
250
251 // Instance emission
252 void emitInstance(InstanceOp op);
253
254 // Primitive emission
255 void emitPrimitive(PrimitiveOp op);
256
257 // Wires emission
258 void emitWires(WiresOp op);
259
260 // Group emission
261 void emitGroup(GroupInterface group);
262
263 // Control emission
264 void emitControl(ControlOp control);
265
266 // Assignment emission
267 void emitAssignment(AssignOp op);
268
269 // Enable emission
270 void emitEnable(EnableOp enable);
271
272 // Register emission
273 void emitRegister(RegisterOp reg);
274
275 // Emit undefined op
276 void emitUndef(UndefLibOp op);
277
278 // Memory emission
279 void emitMemory(MemoryOp memory);
280
281 // Seq Memory emission
282 void emitSeqMemory(SeqMemoryOp memory);
283
284 // Invoke emission
285 void emitInvoke(InvokeOp invoke);
286
287 // Floating point Constant emission
288 void emitConstant(ConstantOp constant);
289
290 // Emits a library primitive with template parameters based on all in- and
291 // output ports.
292 // e.g.:
293 // $f.in0, $f.in1, $f.in2, $f.out : calyx.std_foo "f" : i1, i2, i3, i4
294 // emits:
295 // f = std_foo(1, 2, 3, 4);
296 void emitLibraryPrimTypedByAllPorts(Operation *op);
297
298 // Emits a library primitive with a single template parameter based on the
299 // first input port.
300 // e.g.:
301 // $f.in0, $f.in1, $f.out : calyx.std_foo "f" : i32, i32, i1
302 // emits:
303 // f = std_foo(32);
304 void emitLibraryPrimTypedByFirstInputPort(Operation *op);
305
306 // Emits a library primitive with a single template parameter based on the
307 // first output port.
308 // e.g.:
309 // $f.in0, $f.in1, $f.out : calyx.std_foo "f" : i32, i32, i1
310 // emits:
311 // f = std_foo(1);
312 void emitLibraryPrimTypedByFirstOutputPort(
313 Operation *op, std::optional<StringRef> calyxLibName = {});
314
315 // Emits a library floating point primitives
316 void emitLibraryFloatingPoint(Operation *op);
317
318private:
319 /// Used to track which imports are required for this program.
320 ImportTracker importTracker;
321
322 /// Emit an error and remark that emission failed.
323 InFlightDiagnostic emitError(Operation *op, const Twine &message) {
324 encounteredError = true;
325 return op->emitError(message);
326 }
327
328 /// Emit an error and remark that emission failed.
329 InFlightDiagnostic emitOpError(Operation *op, const Twine &message) {
330 encounteredError = true;
331 return op->emitOpError(message);
332 }
333
334 /// Calyx attributes are emitted in one of the two following formats:
335 /// (1) @<attribute-name>(<attribute-value>), e.g. `@go`, `@bound(5)`.
336 /// (2) <"<attribute-name>"=<attribute-value>>, e.g. `<"static"=1>`.
337 ///
338 /// Since ports are structural in nature and not operations, an
339 /// extra boolean value is added to determine whether this is a port of the
340 /// given operation.
341 ///
342 /// By default, this generates format (1) but can generate format (2) if
343 /// `at_format` is false.
344 std::string getAttribute(Operation *op, NamedAttribute attr, bool isPort,
345 bool atFormat) {
346
347 std::optional<StringRef> identifierOpt = getCalyxAttrIdentifier(attr);
348 // Verify this is a Calyx attribute
349 if (!identifierOpt.has_value())
350 return "";
351
352 StringRef identifier = *identifierOpt;
353 // Verify this attribute is supported for emission.
354 if (!isValidCalyxAttribute(identifier))
355 return "";
356
357 std::string output;
358 llvm::raw_string_ostream buffer(output);
359 buffer.reserveExtraSpace(32);
360
361 bool isBooleanAttribute =
362 llvm::find(booleanAttributes, identifier) != booleanAttributes.end();
363
364 if (isa<UnitAttr>(attr.getValue())) {
365 assert(isBooleanAttribute &&
366 "Non-boolean attributes must provide an integer value.");
367 if (!atFormat) {
368 buffer << quote() << identifier << quote() << equals() << "1";
369 } else {
370 buffer << addressSymbol() << identifier;
371 }
372 } else if (auto intAttr = dyn_cast<IntegerAttr>(attr.getValue())) {
373 APInt value = intAttr.getValue();
374 if (!atFormat) {
375 buffer << quote() << identifier << quote() << equals() << value;
376 } else {
377 buffer << addressSymbol() << identifier;
378 // The only time we may omit the value is when it is a Boolean attribute
379 // with value 1.
380 if (!isBooleanAttribute || intAttr.getValue() != 1) {
381 // Retrieve the unsigned representation of the value.
382 SmallVector<char, 4> s;
383 value.toStringUnsigned(s, /*Radix=*/10);
384 buffer << LParen() << s << RParen();
385 }
386 }
387 }
388 return buffer.str();
389 }
390
391 /// Emits the attributes of a dictionary. If the `attributes` dictionary is
392 /// not nullptr, we assume this is for a port.
393 std::string getAttributes(Operation *op, bool atFormat,
394 DictionaryAttr attributes = nullptr) {
395 bool isPort = attributes != nullptr;
396 bool atLeastOne = false;
397
398 if (!isPort)
399 attributes = op->getAttrDictionary();
400
401 std::string calyxAttributes;
402 llvm::raw_string_ostream buf(calyxAttributes);
403
404 if (!atFormat)
405 buf << LAngleBracket();
406
407 for (auto &attr : attributes) {
408 // If the output
409 if (auto out = getAttribute(op, attr, isPort, atFormat); !out.empty()) {
410 buf << out;
411 atLeastOne = true;
412 buf << (atFormat ? space() : comma());
413 }
414 }
415
416 if (atLeastOne) {
417 auto out = buf.str();
418 // Remove the last character which is an extra space or comma.
419 out.pop_back();
420 out.append(atFormat ? space() : RAngleBracket());
421 return out;
422 }
423
424 return "";
425 }
426
427 /// Helper function for emitting a Calyx section. It emits the body in the
428 /// following format:
429 /// {
430 /// <body>
431 /// }
432 template <typename Func>
433 void emitCalyxBody(Func emitBody) {
434 os << space() << LBraceEndL();
435 addIndent();
436 emitBody();
437 reduceIndent();
438 indent() << RBraceEndL();
439 }
440
441 /// Emits a Calyx section.
442 template <typename Func>
443 void emitCalyxSection(StringRef sectionName, Func emitBody,
444 StringRef symbolName = "") {
445 indent() << sectionName;
446 if (!symbolName.empty())
447 os << space() << symbolName;
448 emitCalyxBody(emitBody);
449 }
450
451 /// Helper function for emitting combinational operations.
452 template <typename CombinationalOp>
453 void emitCombinationalValue(CombinationalOp op, StringRef logicalSymbol) {
454 auto inputs = op.getInputs();
455 os << LParen();
456 for (size_t i = 0, e = inputs.size(); i != e; ++i) {
457 emitValue(inputs[i], /*isIndented=*/false);
458 if (i + 1 == e)
459 continue;
460 os << space() << logicalSymbol << space();
461 }
462 os << RParen();
463 }
464
465 void emitCycleValue(CycleOp op) {
466 os << "%";
467 if (op.getEnd().has_value()) {
468 os << LSquare();
469 os << op.getStart() << ":" << op.getEnd();
470 os << RSquare();
471 } else {
472 os << op.getStart();
473 }
474 }
475
476 /// Emits the value of a guard or assignment.
477 void emitValue(Value value, bool isIndented) {
478 if (auto blockArg = dyn_cast<BlockArgument>(value)) {
479 // Emit component block argument.
480 StringAttr portName = getPortInfo(blockArg).name;
481 (isIndented ? indent() : os) << portName.getValue();
482 return;
483 }
484
485 auto *definingOp = value.getDefiningOp();
486 assert(definingOp && "Value does not have a defining operation.");
487
488 TypeSwitch<Operation *>(definingOp)
489 .Case<CellInterface>([&](auto cell) {
490 // A cell port should be defined as <instance-name>.<port-name>
491 (isIndented ? indent() : os)
492 << cell.instanceName() << period() << cell.portName(value);
493 })
494 .Case<hw::ConstantOp>([&](auto op) {
495 // A constant is defined as <bit-width>'<base><value>, where the base
496 // is `b` (binary), `o` (octal), `h` hexadecimal, or `d` (decimal).
497 APInt value = op.getValue();
498 auto &stream = isIndented ? indent() : os;
499 bool isNegative = value.isNegative();
500 StringRef base = isNegative ? "b" : "d";
501
502 stream << std::to_string(value.getBitWidth()) << apostrophe() << base;
503
504 if (isNegative) {
505 SmallString<8> str;
506 value.toStringUnsigned(str, /*Radix=*/2);
507 stream << str;
508 } else {
509 value.print(stream, /*isSigned=*/false);
510 }
511 })
512 .Case<comb::AndOp>([&](auto op) { emitCombinationalValue(op, "&"); })
513 .Case<comb::OrOp>([&](auto op) { emitCombinationalValue(op, "|"); })
514 .Case<comb::XorOp>([&](auto op) {
515 // The XorOp is a bit different, since the Combinational dialect
516 // uses it to represent binary not.
517 if (!op.isBinaryNot()) {
518 emitOpError(op, "Only supporting Binary Not for XOR.");
519 return;
520 }
521 // The LHS is the value to be negated, and the RHS is a constant with
522 // all ones (guaranteed by isBinaryNot).
523 os << exclamationMark();
524 emitValue(op.getInputs()[0], /*isIndented=*/false);
525 })
526 .Case<CycleOp>([&](auto op) { emitCycleValue(op); })
527 .Default(
528 [&](auto op) { emitOpError(op, "not supported for emission"); });
529 }
530
531 /// Emits a port for a Group.
532 template <typename OpTy>
533 void emitGroupPort(GroupInterface group, OpTy op, StringRef portHole) {
534 assert((isa<GroupGoOp>(op) || isa<GroupDoneOp>(op)) &&
535 "Required to be a group port.");
536 indent() << group.symName().getValue() << LSquare() << portHole << RSquare()
537 << space() << equals() << space();
538 if (op.getGuard()) {
539 emitValue(op.getGuard(), /*isIndented=*/false);
540 os << questionMark();
541 }
542 emitValue(op.getSrc(), /*isIndented=*/false);
543 os << semicolonEndL();
544 }
545
546 /// Recursively emits the Calyx control.
547 void emitCalyxControl(Block *body) {
548 Operation *parent = body->getParentOp();
549 assert((isa<ControlOp>(parent) || parent->hasTrait<ControlLike>()) &&
550 "This should only be used to emit Calyx Control structures.");
551
552 // Check to see if this is a stand-alone EnableOp, i.e.
553 // calyx.control { calyx.enable @G }
554 if (auto enable = dyn_cast<EnableOp>(parent)) {
555 emitEnable(enable);
556 // Early return since an EnableOp has no body.
557 return;
558 }
559 // Attribute dictionary is always prepended for a control operation.
560 auto prependAttributes = [&](Operation *op, StringRef sym) {
561 return (getAttributes(op, /*atFormat=*/true) + sym).str();
562 };
563
564 for (auto &&op : *body) {
565
566 TypeSwitch<Operation *>(&op)
567 .Case<SeqOp>([&](auto op) {
568 emitCalyxSection(prependAttributes(op, "seq"),
569 [&]() { emitCalyxControl(op.getBodyBlock()); });
570 })
571 .Case<StaticSeqOp>([&](auto op) {
572 emitCalyxSection(prependAttributes(op, "static seq"),
573 [&]() { emitCalyxControl(op.getBodyBlock()); });
574 })
575 .Case<ParOp>([&](auto op) {
576 emitCalyxSection(prependAttributes(op, "par"),
577 [&]() { emitCalyxControl(op.getBodyBlock()); });
578 })
579 .Case<WhileOp>([&](auto op) {
580 indent() << prependAttributes(op, "while ");
581 emitValue(op.getCond(), /*isIndented=*/false);
582
583 if (auto groupName = op.getGroupName())
584 os << " with " << *groupName;
585
586 emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
587 })
588 .Case<IfOp>([&](auto op) {
589 indent() << prependAttributes(op, "if ");
590 emitValue(op.getCond(), /*isIndented=*/false);
591
592 if (auto groupName = op.getGroupName())
593 os << " with " << *groupName;
594
595 emitCalyxBody([&]() { emitCalyxControl(op.getThenBody()); });
596 if (op.elseBodyExists())
597 emitCalyxSection("else",
598 [&]() { emitCalyxControl(op.getElseBody()); });
599 })
600 .Case<StaticIfOp>([&](auto op) {
601 indent() << prependAttributes(op, "static if ");
602 emitValue(op.getCond(), /*isIndented=*/false);
603
604 emitCalyxBody([&]() { emitCalyxControl(op.getThenBody()); });
605 if (op.elseBodyExists())
606 emitCalyxSection("else",
607 [&]() { emitCalyxControl(op.getElseBody()); });
608 })
609 .Case<RepeatOp>([&](auto op) {
610 indent() << prependAttributes(op, "repeat ");
611 os << op.getCount();
612
613 emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
614 })
615 .Case<StaticRepeatOp>([&](auto op) {
616 indent() << prependAttributes(op, "static repeat ");
617 os << op.getCount();
618
619 emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
620 })
621 .Case<StaticParOp>([&](auto op) {
622 emitCalyxSection(prependAttributes(op, "static par"),
623 [&]() { emitCalyxControl(op.getBodyBlock()); });
624 })
625 .Case<EnableOp>([&](auto op) { emitEnable(op); })
626 .Case<InvokeOp>([&](auto op) { emitInvoke(op); })
627 .Default([&](auto op) {
628 emitOpError(op, "not supported for emission inside control.");
629 });
630 }
631 }
632
633 /// The stream we are emitting into.
634 llvm::raw_ostream &os;
635
636 /// Whether we have encountered any errors during emission.
637 bool encounteredError = false;
638
639 /// Current level of indentation. See `indent()` and
640 /// `addIndent()`/`reduceIndent()`.
641 unsigned currentIndent = 0;
642};
643
644} // end anonymous namespace
645
646LogicalResult Emitter::finalize() { return failure(encounteredError); }
647
648/// Emit an entire program.
649void Emitter::emitModule(ModuleOp op) {
650 for (auto &bodyOp : *op.getBody()) {
651 if (auto componentOp = dyn_cast<ComponentInterface>(bodyOp))
652 emitComponent(componentOp);
653 else if (auto hwModuleExternOp = dyn_cast<hw::HWModuleExternOp>(bodyOp))
654 emitPrimitiveExtern(hwModuleExternOp);
655 else
656 emitOpError(&bodyOp, "Unexpected op");
657 }
658}
659
660/// Emit a component.
661void Emitter::emitComponent(ComponentInterface op) {
662 std::string combinationalPrefix = op.isComb() ? "comb " : "";
663
664 indent() << combinationalPrefix << "component " << op.getName()
665 << getAttributes(op, /*atFormat=*/false, nullptr);
666 // Emit the ports.
667 emitComponentPorts(op);
668 os << space() << LBraceEndL();
669 addIndent();
670 WiresOp wires;
671 ControlOp control;
672
673 // Emit cells.
674 emitCalyxSection("cells", [&]() {
675 for (auto &&bodyOp : *op.getBodyBlock()) {
676 TypeSwitch<Operation *>(&bodyOp)
677 .Case<UndefLibOp>([&](auto op) { emitUndef(op); })
678 .Case<WiresOp>([&](auto op) { wires = op; })
679 .Case<ControlOp>([&](auto op) { control = op; })
680 .Case<InstanceOp>([&](auto op) { emitInstance(op); })
681 .Case<PrimitiveOp>([&](auto op) { emitPrimitive(op); })
682 .Case<RegisterOp>([&](auto op) { emitRegister(op); })
683 .Case<MemoryOp>([&](auto op) { emitMemory(op); })
684 .Case<SeqMemoryOp>([&](auto op) { emitSeqMemory(op); })
685 .Case<hw::ConstantOp>([&](auto op) { /*Do nothing*/ })
686 .Case<calyx::ConstantOp>([&](auto op) { emitConstant(op); })
687 .Case<SliceLibOp, PadLibOp, ExtSILibOp>(
688 [&](auto op) { emitLibraryPrimTypedByAllPorts(op); })
689 .Case<LtLibOp, GtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, SltLibOp,
690 SgtLibOp, SeqLibOp, SneqLibOp, SgeLibOp, SleLibOp, AddLibOp,
691 SubLibOp, ShruLibOp, RshLibOp, SrshLibOp, LshLibOp, AndLibOp,
692 NotLibOp, OrLibOp, XorLibOp, WireLibOp>(
693 [&](auto op) { emitLibraryPrimTypedByFirstInputPort(op); })
694 .Case<MuxLibOp>(
695 [&](auto op) { emitLibraryPrimTypedByFirstOutputPort(op); })
696 .Case<MultPipeLibOp>(
697 [&](auto op) { emitLibraryPrimTypedByFirstOutputPort(op); })
698 .Case<RemUPipeLibOp, DivUPipeLibOp>([&](auto op) {
699 emitLibraryPrimTypedByFirstOutputPort(
700 op, /*calyxLibName=*/{"std_div_pipe"});
701 })
702 .Case<RemSPipeLibOp, DivSPipeLibOp>([&](auto op) {
703 emitLibraryPrimTypedByFirstOutputPort(
704 op, /*calyxLibName=*/{"std_sdiv_pipe"});
705 })
706 .Case<AddFOpIEEE754, MulFOpIEEE754, CompareFOpIEEE754,
707 FpToIntOpIEEE754, IntToFpOpIEEE754, DivSqrtOpIEEE754>(
708 [&](auto op) { emitLibraryFloatingPoint(op); })
709 .Default([&](auto op) {
710 emitOpError(op, "not supported for emission inside component");
711 });
712 }
713 });
714
715 emitWires(wires);
716 emitControl(control);
717 reduceIndent();
718 os << RBraceEndL();
719}
720
721/// Emit the ports of a component.
722void Emitter::emitComponentPorts(ComponentInterface op) {
723 auto emitPorts = [&](auto ports) {
724 os << LParen();
725 for (size_t i = 0, e = ports.size(); i < e; ++i) {
726 const PortInfo &port = ports[i];
727
728 // We only care about the bit width in the emitted .futil file.
729 unsigned int bitWidth = port.type.getIntOrFloatBitWidth();
730 os << getAttributes(op, /*atFormat=*/true, port.attributes)
731 << port.name.getValue() << colon() << bitWidth;
732
733 if (i + 1 < e)
734 os << comma();
735 }
736 os << RParen();
737 };
738 emitPorts(op.getInputPortInfo());
739 os << arrow();
740 emitPorts(op.getOutputPortInfo());
741}
742
743/// Emit a primitive extern
744void Emitter::emitPrimitiveExtern(hw::HWModuleExternOp op) {
745 Attribute filename = op->getAttrDictionary().get("filename");
746 indent() << "extern " << filename << space() << LBraceEndL();
747 addIndent();
748 indent() << "primitive " << op.getName();
749
750 if (!op.getParameters().empty()) {
751 os << LSquare();
752 llvm::interleaveComma(op.getParameters(), os, [&](Attribute param) {
753 auto paramAttr = cast<hw::ParamDeclAttr>(param);
754 os << paramAttr.getName().str();
755 });
756 os << RSquare();
757 }
758 os << getAttributes(op, /*atFormat=*/false);
759 // Emit the ports.
760 emitPrimitivePorts(op);
761 os << semicolonEndL();
762 reduceIndent();
763 os << RBraceEndL();
764}
765
766/// Emit the ports of a component.
767void Emitter::emitPrimitivePorts(hw::HWModuleExternOp op) {
768 auto emitPorts = [&](auto ports, bool isInput) {
769 auto e = static_cast<size_t>(std::distance(ports.begin(), ports.end()));
770 os << LParen();
771 auto type = op.getHWModuleType();
772 for (auto [i, port] : llvm::enumerate(ports)) {
773 DictionaryAttr portAttr = cast_or_null<DictionaryAttr>(
774 op.getPortAttrs(isInput ? type.getPortIdForInputId(i)
775 : type.getPortIdForOutputId(i)));
776
777 os << getAttributes(op, /*atFormat=*/true, portAttr)
778 << port.name.getValue() << colon();
779 // We only care about the bit width in the emitted .futil file.
780 // Emit parameterized or non-parameterized bit width.
781 if (hw::isParametricType(port.type)) {
782 hw::ParamDeclRefAttr bitWidth = dyn_cast<hw::ParamDeclRefAttr>(
783 cast<hw::IntType>(port.type).getWidth());
784 os << bitWidth.getName().str();
785 } else {
786 unsigned int bitWidth = port.type.getIntOrFloatBitWidth();
787 os << bitWidth;
788 }
789
790 if (i < e - 1)
791 os << comma();
792 }
793 os << RParen();
794 };
795 hw::ModulePortInfo ports(op.getPortList());
796 emitPorts(ports.getInputs(), true);
797 os << arrow();
798 emitPorts(ports.getOutputs(), false);
799}
800
801void Emitter::emitInstance(InstanceOp op) {
802 indent() << getAttributes(op, /*atFormat=*/true) << op.instanceName()
803 << space() << equals() << space() << op.getComponentName()
804 << LParen() << RParen() << semicolonEndL();
805}
806
807void Emitter::emitPrimitive(PrimitiveOp op) {
808 indent() << getAttributes(op, /*atFormat=*/true) << op.instanceName()
809 << space() << equals() << space() << op.getPrimitiveName()
810 << LParen();
811
812 if (op.getParameters().has_value()) {
813 llvm::interleaveComma(*op.getParameters(), os, [&](Attribute param) {
814 auto paramAttr = cast<hw::ParamDeclAttr>(param);
815 auto value = paramAttr.getValue();
816 if (auto intAttr = dyn_cast<IntegerAttr>(value)) {
817 os << intAttr.getInt();
818 } else if (auto fpAttr = dyn_cast<FloatAttr>(value)) {
819 os << fpAttr.getValue().convertToFloat();
820 } else {
821 llvm_unreachable("Primitive parameter type not supported");
822 }
823 });
824 }
825
826 os << RParen() << semicolonEndL();
827}
828
829void Emitter::emitRegister(RegisterOp reg) {
830 size_t bitWidth = reg.getIn().getType().getIntOrFloatBitWidth();
831 indent() << getAttributes(reg, /*atFormat=*/true) << reg.instanceName()
832 << space() << equals() << space() << "std_reg" << LParen()
833 << std::to_string(bitWidth) << RParen() << semicolonEndL();
834}
835
836void Emitter::emitUndef(UndefLibOp op) {
837 size_t bitwidth = op.getOut().getType().getIntOrFloatBitWidth();
838 indent() << getAttributes(op, /*atFormat=*/true) << op.instanceName()
839 << space() << equals() << space() << "undef" << LParen()
840 << std::to_string(bitwidth) << RParen() << semicolonEndL();
841}
842
843void Emitter::emitMemory(MemoryOp memory) {
844 size_t dimension = memory.getSizes().size();
845 if (dimension < 1 || dimension > 4) {
846 emitOpError(memory, "Only memories with dimensionality in range [1, 4] are "
847 "supported by the native Calyx compiler.");
848 return;
849 }
850 indent() << getAttributes(memory, /*atFormat=*/true) << memory.instanceName()
851 << space() << equals() << space() << "std_mem_d"
852 << std::to_string(dimension) << LParen() << memory.getWidth()
853 << comma();
854 for (Attribute size : memory.getSizes()) {
855 APInt memSize = cast<IntegerAttr>(size).getValue();
856 memSize.print(os, /*isSigned=*/false);
857 os << comma();
858 }
859
860 ArrayAttr addrSizes = memory.getAddrSizes();
861 for (size_t i = 0, e = addrSizes.size(); i != e; ++i) {
862 APInt addrSize = cast<IntegerAttr>(addrSizes[i]).getValue();
863 addrSize.print(os, /*isSigned=*/false);
864 if (i + 1 == e)
865 continue;
866 os << comma();
867 }
868 os << RParen() << semicolonEndL();
869}
870
871void Emitter::emitSeqMemory(SeqMemoryOp memory) {
872 size_t dimension = memory.getSizes().size();
873 if (dimension < 1 || dimension > 4) {
874 emitOpError(memory, "Only memories with dimensionality in range [1, 4] are "
875 "supported by the native Calyx compiler.");
876 return;
877 }
878 bool isRef = !memory->hasAttr("external");
879 indent();
880 if (isRef)
881 os << "ref ";
882 os << getAttributes(memory, /*atFormat=*/true) << memory.instanceName()
883 << space() << equals() << space() << "seq_mem_d"
884 << std::to_string(dimension) << LParen() << memory.getWidth() << comma();
885 for (Attribute size : memory.getSizes()) {
886 APInt memSize = cast<IntegerAttr>(size).getValue();
887 memSize.print(os, /*isSigned=*/false);
888 os << comma();
889 }
890
891 ArrayAttr addrSizes = memory.getAddrSizes();
892 for (size_t i = 0, e = addrSizes.size(); i != e; ++i) {
893 APInt addrSize = cast<IntegerAttr>(addrSizes[i]).getValue();
894 addrSize.print(os, /*isSigned=*/false);
895 if (i + 1 == e)
896 continue;
897 os << comma();
898 }
899 os << RParen() << semicolonEndL();
900}
901
902void Emitter::emitInvoke(InvokeOp invoke) {
903 StringRef callee = invoke.getCallee();
904 indent() << "invoke " << callee;
905 auto refCellsMap = invoke.getRefCellsMap();
906 if (!refCellsMap.empty()) {
907 os << "[";
908 llvm::interleaveComma(refCellsMap, os, [&](Attribute attr) {
909 auto dictAttr = cast<DictionaryAttr>(attr);
910 llvm::interleaveComma(dictAttr, os, [&](NamedAttribute namedAttr) {
911 auto refCellName = namedAttr.getName().str();
912 auto externalMem =
913 cast<FlatSymbolRefAttr>(namedAttr.getValue()).getValue();
914 os << refCellName << " = " << externalMem;
915 });
916 });
917 os << "]";
918 }
919 ArrayAttr portNames = invoke.getPortNames();
920 ArrayAttr inputNames = invoke.getInputNames();
921 /// Because the ports of all components of calyx.invoke are inside a (),
922 /// here the input and output ports are divided, inputs and outputs store
923 /// the connections for a subset of input and output ports of the instance.
924 llvm::StringMap<std::string> inputsMap;
925 llvm::StringMap<std::string> outputsMap;
926 for (auto [portNameAttr, inputNameAttr, input] :
927 llvm::zip(portNames, inputNames, invoke.getInputs())) {
928 StringRef portName = cast<StringAttr>(portNameAttr).getValue();
929 StringRef inputName = cast<StringAttr>(inputNameAttr).getValue();
930 /// Classify the connection of ports,here's an example. calyx.invoke
931 /// @r(%r.in = %id.out, %out = %r.out) -> (i32, i32) %r.in = %id.out will be
932 /// stored in inputs, because %.r.in is the input port of the component, and
933 /// %out = %r.out will be stored in outputs, because %r.out is the output
934 /// port of the component, which is a bit different from calyx's native
935 /// compiler. Later on, the classified connection relations are outputted
936 /// uniformly and converted to calyx's native compiler format.
937 StringRef inputMapKey = portName.drop_front(2 + callee.size());
938 if (portName.substr(1, callee.size()) == callee) {
939 // If the input to the port is a number.
940 if (isa_and_nonnull<hw::ConstantOp>(input.getDefiningOp())) {
941 hw::ConstantOp constant = cast<hw::ConstantOp>(input.getDefiningOp());
942 APInt value = constant.getValue();
943 std::string mapValue = std::to_string(value.getBitWidth()) +
944 apostrophe().data() + "d" +
945 std::to_string(value.getZExtValue());
946 inputsMap[inputMapKey] = mapValue;
947 continue;
948 }
949 inputsMap[inputMapKey] = inputName.drop_front(1).str();
950 } else if (inputName.substr(1, callee.size()) == callee)
951 outputsMap[inputName.drop_front(2 + callee.size())] =
952 portName.drop_front(1).str();
953 }
954 /// Emit inputs
955 os << LParen();
956 llvm::interleaveComma(inputsMap, os, [&](const auto &iter) {
957 os << iter.getKey() << " = " << iter.getValue();
958 });
959 os << RParen();
960 /// Emit outputs
961 os << LParen();
962 llvm::interleaveComma(outputsMap, os, [&](const auto &iter) {
963 os << iter.getKey() << " = " << iter.getValue();
964 });
965 os << RParen() << semicolonEndL();
966}
967
968void Emitter::emitConstant(ConstantOp constantOp) {
969 TypedAttr attr = constantOp.getValueAttr();
970 assert(isa<FloatAttr>(attr) && "must be a floating point constant");
971 auto fltAttr = cast<FloatAttr>(attr);
972 APFloat value = fltAttr.getValue();
973 auto type = cast<FloatType>(fltAttr.getType());
974 double doubleValue = value.convertToDouble();
975 auto floatBits = value.getSizeInBits(type.getFloatSemantics());
976 indent() << constantOp.getName().str() << space() << equals() << space()
977 << "std_float_const";
978 // Currently defaults to IEEE-754 representation [1].
979 // [1]: https://github.com/calyxir/calyx/blob/main/primitives/float.futil
980 static constexpr int32_t IEEE754 = 0;
981 os << LParen() << std::to_string(IEEE754) << comma() << floatBits << comma()
982 << std::to_string(doubleValue) << RParen() << semicolonEndL();
983}
984
985/// Calling getName() on a calyx operation will return "calyx.${opname}". This
986/// function returns whatever is left after the first '.' in the string,
987/// removing the 'calyx' prefix.
988static StringRef removeCalyxPrefix(StringRef s) { return s.split(".").second; }
989
990void Emitter::emitLibraryPrimTypedByAllPorts(Operation *op) {
991 auto cell = cast<CellInterface>(op);
992 indent() << getAttributes(op, /*atFormat=*/true) << cell.instanceName()
993 << space() << equals() << space()
994 << removeCalyxPrefix(op->getName().getStringRef()) << LParen();
995 llvm::interleaveComma(op->getResults(), os, [&](auto res) {
996 os << std::to_string(res.getType().getIntOrFloatBitWidth());
997 });
998 os << RParen() << semicolonEndL();
999}
1000
1001void Emitter::emitLibraryPrimTypedByFirstInputPort(Operation *op) {
1002 auto cell = cast<CellInterface>(op);
1003 unsigned bitWidth = cell.getInputPorts()[0].getType().getIntOrFloatBitWidth();
1004 StringRef opName = op->getName().getStringRef();
1005 indent() << getAttributes(op, /*atFormat=*/true) << cell.instanceName()
1006 << space() << equals() << space() << removeCalyxPrefix(opName)
1007 << LParen() << bitWidth << RParen() << semicolonEndL();
1008}
1009
1010void Emitter::emitLibraryPrimTypedByFirstOutputPort(
1011 Operation *op, std::optional<StringRef> calyxLibName) {
1012 auto cell = cast<CellInterface>(op);
1013 unsigned bitWidth =
1014 cell.getOutputPorts()[0].getType().getIntOrFloatBitWidth();
1015 StringRef opName = op->getName().getStringRef();
1016 indent() << getAttributes(op, /*atFormat=*/true) << cell.instanceName()
1017 << space() << equals() << space()
1018 << (calyxLibName ? *calyxLibName : removeCalyxPrefix(opName))
1019 << LParen() << bitWidth << RParen() << semicolonEndL();
1020}
1021
1022unsigned getFPBitWidth(CellInterface &cell) {
1023 if (isa<IntToFpOpIEEE754>(cell.getOperation())) {
1024 // `std_intToFp` has the float point value in the first output port.
1025 return cell.getOutputPorts()[0].getType().getIntOrFloatBitWidth();
1026 }
1027
1028 auto inputPorts = cell.getInputPorts();
1029 assert(inputPorts.size() >= 2 && "There should be at least two input ports");
1030
1031 // magic number for the index of `left/right` input port for
1032 // `AddF`/`MulF`/`CompareF`; `in` input port for `FpToInt`.
1033 size_t inputPortIndex = inputPorts.size() - 2;
1034 return cell.getInputPorts()[inputPortIndex].getType().getIntOrFloatBitWidth();
1035}
1036
1037unsigned getIntWidth(Operation *op, CellInterface &cell, bool isIntToFp) {
1038 auto inputPorts = cell.getInputPorts();
1039 assert(inputPorts.size() >= 2);
1040
1041 size_t integerIndex = inputPorts.size() - 2;
1042 if (!isIntToFp) {
1043 // FpToInt case: output width determines integer width.
1044 return cell.getOutputPorts()[0].getType().getIntOrFloatBitWidth();
1045 }
1046 return cell.getInputPorts()[integerIndex].getType().getIntOrFloatBitWidth();
1047}
1048
1049void Emitter::emitLibraryFloatingPoint(Operation *op) {
1050 auto cell = cast<CellInterface>(op);
1051
1052 unsigned fpBitWidth = getFPBitWidth(cell);
1053 unsigned expWidth, sigWidth;
1054 switch (fpBitWidth) {
1055 case 16:
1056 expWidth = 5;
1057 sigWidth = 11;
1058 break;
1059 case 32:
1060 expWidth = 8;
1061 sigWidth = 24;
1062 break;
1063 case 64:
1064 expWidth = 11;
1065 sigWidth = 53;
1066 break;
1067 case 128:
1068 expWidth = 15;
1069 sigWidth = 113;
1070 break;
1071 default:
1072 op->emitError("The supported bitwidths are 16, 32, 64, and 128");
1073 return;
1074 }
1075
1076 std::string opName;
1077 if (auto fpOp = dyn_cast<calyx::FloatingPointOpInterface>(op)) {
1078 opName = fpOp.getCalyxLibraryName();
1079 }
1080
1081 indent() << getAttributes(op, /*atFormat=*/true) << cell.instanceName()
1082 << space() << equals() << space() << opName << LParen();
1083
1084 if (isa<calyx::IntToFpOpIEEE754>(op)) {
1085 unsigned intWidth = getIntWidth(op, cell, /*isIntToFp=*/true);
1086 os << intWidth << comma();
1087 }
1088
1089 os << expWidth << comma() << sigWidth << comma() << fpBitWidth;
1090
1091 if (auto fpToIntOp = dyn_cast<calyx::FpToIntOpIEEE754>(op)) {
1092 unsigned intWidth = getIntWidth(op, cell, /*isIntToFp=*/false);
1093 os << comma() << intWidth;
1094 }
1095
1096 os << RParen() << semicolonEndL();
1097}
1098
1099void Emitter::emitAssignment(AssignOp op) {
1100
1101 emitValue(op.getDest(), /*isIndented=*/true);
1102 os << space() << equals() << space();
1103 if (op.getGuard()) {
1104 emitValue(op.getGuard(), /*isIndented=*/false);
1105 os << questionMark();
1106 }
1107 if (auto constantOp =
1108 dyn_cast_or_null<calyx::ConstantOp>(op.getSrc().getDefiningOp())) {
1109 TypedAttr attr = constantOp.getValueAttr();
1110 assert(isa<FloatAttr>(attr) && "must be a floating point constant");
1111 auto fltAttr = dyn_cast<FloatAttr>(attr);
1112 assert(attr != nullptr && "must be a floating point constant");
1113 APFloat value = fltAttr.getValue();
1114 if (value.isInfinity() || value.isNaN() || value.isNegative()) {
1115 SmallString<8> str;
1116 auto intValue = value.bitcastToAPInt();
1117 intValue.toStringUnsigned(str, /*Radix=*/2);
1118 os << std::to_string(intValue.getBitWidth()) << apostrophe() << "b" << str
1119 << semicolonEndL();
1120 return;
1121 }
1122 }
1123 emitValue(op.getSrc(), /*isIndented=*/false);
1124 os << semicolonEndL();
1125}
1126
1127void Emitter::emitWires(WiresOp op) {
1128 emitCalyxSection("wires", [&]() {
1129 for (auto &&bodyOp : *op.getBodyBlock()) {
1130 TypeSwitch<Operation *>(&bodyOp)
1131 .Case<GroupInterface>([&](auto op) { emitGroup(op); })
1132 .Case<AssignOp>([&](auto op) { emitAssignment(op); })
1133 .Case<hw::ConstantOp, calyx::ConstantOp, comb::AndOp, comb::OrOp,
1134 comb::XorOp, CycleOp>([&](auto op) { /* Do nothing. */ })
1135 .Default([&](auto op) {
1136 emitOpError(op, "not supported for emission inside wires section");
1137 });
1138 }
1139 });
1140}
1141
1142void Emitter::emitGroup(GroupInterface group) {
1143 auto emitGroupBody = [&]() {
1144 for (auto &&bodyOp : *group.getBody()) {
1145 TypeSwitch<Operation *>(&bodyOp)
1146 .Case<AssignOp>([&](auto op) { emitAssignment(op); })
1147 .Case<GroupDoneOp>([&](auto op) { emitGroupPort(group, op, "done"); })
1148 .Case<GroupGoOp>([&](auto op) { emitGroupPort(group, op, "go"); })
1149 .Case<hw::ConstantOp, comb::AndOp, comb::OrOp, comb::XorOp, CycleOp>(
1150 [&](auto op) { /* Do nothing. */ })
1151 .Default([&](auto op) {
1152 emitOpError(op, "not supported for emission inside group.");
1153 });
1154 }
1155 };
1156 std::string prefix;
1157 if (isa<StaticGroupOp>(group)) {
1158 auto staticGroup = cast<StaticGroupOp>(group);
1159 prefix = llvm::formatv("static<{0}> group", staticGroup.getLatency());
1160 } else {
1161 prefix = isa<CombGroupOp>(group) ? "comb group" : "group";
1162 }
1163 auto groupHeader =
1164 (group.symName().getValue() + getAttributes(group, /*atFormat=*/false))
1165 .str();
1166 emitCalyxSection(prefix, emitGroupBody, groupHeader);
1167}
1168
1169void Emitter::emitEnable(EnableOp enable) {
1170 indent() << getAttributes(enable, /*atFormat=*/true) << enable.getGroupName()
1171 << semicolonEndL();
1172}
1173
1174void Emitter::emitControl(ControlOp control) {
1175 // A valid Calyx program does not necessarily need a control section.
1176 if (control == nullptr)
1177 return;
1178 emitCalyxSection("control",
1179 [&]() { emitCalyxControl(control.getBodyBlock()); });
1180}
1181
1182//===----------------------------------------------------------------------===//
1183// Driver
1184//===----------------------------------------------------------------------===//
1185
1186// Emit the specified Calyx circuit into the given output stream.
1187mlir::LogicalResult circt::calyx::exportCalyx(mlir::ModuleOp module,
1188 llvm::raw_ostream &os) {
1189 Emitter emitter(os);
1190 if (failed(emitter.emitImports(module)))
1191 return failure();
1192 emitter.emitModule(module);
1193 emitter.emitCiderMetadata(module);
1194 return emitter.finalize();
1195}
1196
1198 static mlir::TranslateFromMLIRRegistration toCalyx(
1199 "export-calyx", "export Calyx",
1200 [](ModuleOp module, llvm::raw_ostream &os) {
1201 return exportCalyx(module, os);
1202 },
1203 [](mlir::DialectRegistry &registry) {
1204 registry
1205 .insert<calyx::CalyxDialect, comb::CombDialect, hw::HWDialect>();
1206 });
1207}
assert(baseType &&"element must be base type")
unsigned getIntWidth(Operation *op, CellInterface &cell, bool isIntToFp)
unsigned getFPBitWidth(CellInterface &cell)
static StringRef removeCalyxPrefix(StringRef s)
Calling getName() on a calyx operation will return "calyx.${opname}".
static bool isPort(Value value)
Returns whether this value is either (1) a port on a ComponentOp or (2) a port on a cell interface.
Definition CalyxOps.cpp:135
static Block * getBodyBlock(FModuleLike mod)
Signals that the following operation is "control-like.".
Definition CalyxOps.h:42
PortInfo getPortInfo(BlockArgument arg)
Returns port information for the block argument provided.
Definition CalyxOps.cpp:142
void registerToCalyxTranslation()
mlir::LogicalResult exportCalyx(mlir::ModuleOp module, llvm::raw_ostream &os)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition seq.py:21
This holds information about the port for either a Component or Cell.
Definition CalyxOps.h:89
DictionaryAttr attributes
Definition CalyxOps.h:93
This holds a decoded list of input/inout and output ports for a module or instance.