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" 
   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"; }
 
   61constexpr std::array<StringRef, 7> integerAttributes{
 
   62    "external",       
"static",        
"share", 
"bound",
 
   63    "write_together", 
"read_together", 
"pos",
 
   67constexpr std::array<StringRef, 12> booleanAttributes{
 
   68    "clk",      
"reset",  
"go",          
"done",   
"generated",   
"precious",
 
   69    "toplevel", 
"stable", 
"nointerface", 
"inline", 
"state_share", 
"data",
 
   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(
"."));
 
   86static bool isValidCalyxAttribute(StringRef identifier) {
 
   88  return llvm::find(integerAttributes, identifier) != integerAttributes.end() ||
 
   89         llvm::find(booleanAttributes, identifier) != booleanAttributes.end();
 
   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))
 
  104        auto libraryName = getLibraryFor(&op);
 
  105        if (failed(libraryName))
 
  106          return WalkResult::interrupt();
 
  107        usedLibraries.insert(*libraryName);
 
  109      return WalkResult::advance();
 
  111    if (walkRes.wasInterrupted())
 
  113    return usedLibraries;
 
  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";
 
  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";
 
  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 =
 
  138              return {sBinaryOperators};
 
  140        .Case<MemoryOp>([&](
auto op) -> FailureOr<StringRef> {
 
  141          static constexpr std::string_view sMemories = 
"memories/comb";
 
  144        .Case<SeqMemoryOp>([&](
auto op) -> FailureOr<StringRef> {
 
  145          static constexpr std::string_view sMemories = 
"memories/seq";
 
  148        .Case<ConstantOp>([&](
auto op) -> FailureOr<StringRef> {
 
  149          static constexpr std::string_view sFloat = 
"float";
 
  152        .Case<AddFOpIEEE754>([&](
auto op) -> FailureOr<StringRef> {
 
  153          static constexpr std::string_view sFloatingPoint = 
"float/addFN";
 
  154          return {sFloatingPoint};
 
  156        .Case<MulFOpIEEE754>([&](
auto op) -> FailureOr<StringRef> {
 
  157          static constexpr std::string_view sFloatingPoint = 
"float/mulFN";
 
  158          return {sFloatingPoint};
 
  160        .Case<CompareFOpIEEE754>([&](
auto op) -> FailureOr<StringRef> {
 
  161          static constexpr std::string_view sFloatingPoint = 
"float/compareFN";
 
  162          return {sFloatingPoint};
 
  164        .Case<FpToIntOpIEEE754>([&](
auto op) -> FailureOr<StringRef> {
 
  165          static constexpr std::string_view sFloatingPoint = 
"float/fpToInt";
 
  166          return {sFloatingPoint};
 
  168        .Case<IntToFpOpIEEE754>([&](
auto op) -> FailureOr<StringRef> {
 
  169          static constexpr std::string_view sFloatingPoint = 
"float/intToFp";
 
  170          return {sFloatingPoint};
 
  172        .Case<DivSqrtOpIEEE754>([&](
auto op) -> FailureOr<StringRef> {
 
  173          static constexpr std::string_view sFloatingPoint = 
"float/divSqrtFN";
 
  174          return {sFloatingPoint};
 
  176        .Default([&](
auto op) {
 
  177          auto diag = op->emitOpError() << 
"not supported for emission";
 
  192  Emitter(llvm::raw_ostream &os) : os(os) {}
 
  193  LogicalResult finalize();
 
  196  raw_ostream &indent() { 
return os.indent(currentIndent); }
 
  197  void addIndent() { currentIndent += 2; }
 
  198  void reduceIndent() {
 
  199    assert(currentIndent >= 2 && 
"Unintended indentation wrap");
 
  204  void emitModule(ModuleOp op);
 
  207  void emitCiderMetadata(mlir::ModuleOp op) {
 
  208    auto metadata = op->getAttrOfType<ArrayAttr>(
"calyx.metadata");
 
  212    constexpr std::string_view metadataIdentifier = 
"metadata";
 
  213    os << endl() << metadataIdentifier << 
space() << metadataLBrace();
 
  215    for (
auto sourceLoc : 
llvm::enumerate(metadata)) {
 
  217      os << std::to_string(sourceLoc.index()) << colon();
 
  218      os << cast<StringAttr>(sourceLoc.value()).getValue() << endl();
 
  221    os << metadataRBrace();
 
  225  LogicalResult emitImports(ModuleOp op) {
 
  226    auto emitImport = [&](StringRef library) {
 
  229      os << 
"import " << quote() << 
"primitives/" << library << period()
 
  230         << 
"futil" << quote() << semicolonEndL();
 
  233    auto libraryNames = importTracker.getLibraryNames(op);
 
  234    if (failed(libraryNames))
 
  237    for (StringRef library : *libraryNames)
 
  244  void emitComponent(ComponentInterface op);
 
  245  void emitComponentPorts(ComponentInterface op);
 
  252  void emitInstance(InstanceOp op);
 
  255  void emitPrimitive(PrimitiveOp op);
 
  258  void emitWires(WiresOp op);
 
  261  void emitGroup(GroupInterface group);
 
  264  void emitControl(ControlOp control);
 
  267  void emitAssignment(AssignOp op);
 
  270  void emitEnable(EnableOp enable);
 
  273  void emitRegister(RegisterOp reg);
 
  276  void emitUndef(UndefLibOp op);
 
  279  void emitMemory(MemoryOp memory);
 
  282  void emitSeqMemory(SeqMemoryOp memory);
 
  285  void emitInvoke(InvokeOp invoke);
 
  288  void emitConstant(ConstantOp constant);
 
  296  void emitLibraryPrimTypedByAllPorts(Operation *op);
 
  304  void emitLibraryPrimTypedByFirstInputPort(Operation *op);
 
  312  void emitLibraryPrimTypedByFirstOutputPort(
 
  313      Operation *op, std::optional<StringRef> calyxLibName = {});
 
  316  void emitLibraryFloatingPoint(Operation *op);
 
  320  ImportTracker importTracker;
 
  323  InFlightDiagnostic emitError(Operation *op, 
const Twine &message) {
 
  324    encounteredError = 
true;
 
  325    return op->emitError(message);
 
  329  InFlightDiagnostic emitOpError(Operation *op, 
const Twine &message) {
 
  330    encounteredError = 
true;
 
  331    return op->emitOpError(message);
 
  344  std::string getAttribute(Operation *op, NamedAttribute attr, 
bool isPort,
 
  347    std::optional<StringRef> identifierOpt = getCalyxAttrIdentifier(attr);
 
  349    if (!identifierOpt.has_value())
 
  352    StringRef identifier = *identifierOpt;
 
  354    if (!isValidCalyxAttribute(identifier))
 
  358    llvm::raw_string_ostream buffer(output);
 
  359    buffer.reserveExtraSpace(32);
 
  361    bool isBooleanAttribute =
 
  362        llvm::find(booleanAttributes, identifier) != booleanAttributes.end();
 
  364    if (isa<UnitAttr>(attr.getValue())) {
 
  365      assert(isBooleanAttribute &&
 
  366             "Non-boolean attributes must provide an integer value.");
 
  368        buffer << quote() << identifier << quote() << equals() << 
"1";
 
  370        buffer << addressSymbol() << identifier;
 
  372    } 
else if (
auto intAttr = dyn_cast<IntegerAttr>(attr.getValue())) {
 
  373      APInt value = intAttr.getValue();
 
  375        buffer << quote() << identifier << quote() << equals() << value;
 
  377        buffer << addressSymbol() << identifier;
 
  380        if (!isBooleanAttribute || intAttr.getValue() != 1) {
 
  382          SmallVector<char, 4> s;
 
  383          value.toStringUnsigned(s, 10);
 
  384          buffer << LParen() << s << RParen();
 
  393  std::string getAttributes(Operation *op, 
bool atFormat,
 
  394                            DictionaryAttr attributes = 
nullptr) {
 
  395    bool isPort = attributes != 
nullptr;
 
  396    bool atLeastOne = 
false;
 
  399      attributes = op->getAttrDictionary();
 
  401    std::string calyxAttributes;
 
  402    llvm::raw_string_ostream buf(calyxAttributes);
 
  405      buf << LAngleBracket();
 
  407    for (
auto &attr : attributes) {
 
  409      if (
auto out = getAttribute(op, attr, 
isPort, atFormat); !out.empty()) {
 
  412        buf << (atFormat ? 
space() : comma());
 
  417      auto out = buf.str();
 
  420      out.append(atFormat ? 
space() : RAngleBracket());
 
  432  template <
typename Func>
 
  433  void emitCalyxBody(Func emitBody) {
 
  434    os << 
space() << LBraceEndL();
 
  438    indent() << RBraceEndL();
 
  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);
 
  452  template <
typename CombinationalOp>
 
  453  void emitCombinationalValue(CombinationalOp op, StringRef logicalSymbol) {
 
  454    auto inputs = op.getInputs();
 
  456    for (
size_t i = 0, e = inputs.size(); i != e; ++i) {
 
  457      emitValue(inputs[i], 
false);
 
  465  void emitCycleValue(CycleOp op) {
 
  467    if (op.getEnd().has_value()) {
 
  469      os << op.getStart() << 
":" << op.getEnd();
 
  477  void emitValue(Value value, 
bool isIndented) {
 
  478    if (
auto blockArg = dyn_cast<BlockArgument>(value)) {
 
  481      (isIndented ? indent() : os) << portName.getValue();
 
  485    auto *definingOp = value.getDefiningOp();
 
  486    assert(definingOp && 
"Value does not have a defining operation.");
 
  488    TypeSwitch<Operation *>(definingOp)
 
  489        .Case<CellInterface>([&](
auto cell) {
 
  491          (isIndented ? indent() : os)
 
  492              << cell.instanceName() << period() << cell.portName(value);
 
  494        .Case<hw::ConstantOp>([&](
auto op) {
 
  497          APInt value = op.getValue();
 
  498          auto &stream = isIndented ? indent() : os;
 
  499          bool isNegative = value.isNegative();
 
  500          StringRef base = isNegative ? 
"b" : 
"d";
 
  502          stream << std::to_string(value.getBitWidth()) << apostrophe() << base;
 
  506            value.toStringUnsigned(str, 2);
 
  509            value.print(stream, 
false);
 
  512        .Case<comb::AndOp>([&](
auto op) { emitCombinationalValue(op, 
"&"); })
 
  513        .Case<comb::OrOp>([&](
auto op) { emitCombinationalValue(op, 
"|"); })
 
  514        .Case<comb::XorOp>([&](
auto op) {
 
  517          if (!op.isBinaryNot()) {
 
  518            emitOpError(op, 
"Only supporting Binary Not for XOR.");
 
  523          os << exclamationMark();
 
  524          emitValue(op.getInputs()[0], 
false);
 
  526        .Case<CycleOp>([&](
auto op) { emitCycleValue(op); })
 
  528            [&](
auto op) { emitOpError(op, 
"not supported for emission"); });
 
  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()
 
  539      emitValue(op.getGuard(), 
false);
 
  540      os << questionMark();
 
  542    emitValue(op.getSrc(), 
false);
 
  543    os << semicolonEndL();
 
  547  void emitCalyxControl(Block *body) {
 
  548    Operation *parent = body->getParentOp();
 
  550           "This should only be used to emit Calyx Control structures.");
 
  554    if (
auto enable = dyn_cast<EnableOp>(parent)) {
 
  560    auto prependAttributes = [&](Operation *op, StringRef sym) {
 
  561      return (getAttributes(op, 
true) + sym).str();
 
  564    for (
auto &&op : *body) {
 
  566      TypeSwitch<Operation *>(&op)
 
  567          .Case<SeqOp>([&](
auto op) {
 
  568            emitCalyxSection(prependAttributes(op, 
"seq"),
 
  569                             [&]() { emitCalyxControl(op.getBodyBlock()); });
 
  571          .Case<StaticSeqOp>([&](
auto op) {
 
  572            emitCalyxSection(prependAttributes(op, 
"static seq"),
 
  573                             [&]() { emitCalyxControl(op.getBodyBlock()); });
 
  575          .Case<ParOp>([&](
auto op) {
 
  576            emitCalyxSection(prependAttributes(op, 
"par"),
 
  577                             [&]() { emitCalyxControl(op.getBodyBlock()); });
 
  579          .Case<WhileOp>([&](
auto op) {
 
  580            indent() << prependAttributes(op, 
"while ");
 
  581            emitValue(op.getCond(), 
false);
 
  583            if (
auto groupName = op.getGroupName())
 
  584              os << 
" with " << *groupName;
 
  586            emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
 
  588          .Case<IfOp>([&](
auto op) {
 
  589            indent() << prependAttributes(op, 
"if ");
 
  590            emitValue(op.getCond(), 
false);
 
  592            if (
auto groupName = op.getGroupName())
 
  593              os << 
" with " << *groupName;
 
  595            emitCalyxBody([&]() { emitCalyxControl(op.getThenBody()); });
 
  596            if (op.elseBodyExists())
 
  597              emitCalyxSection(
"else",
 
  598                               [&]() { emitCalyxControl(op.getElseBody()); });
 
  600          .Case<StaticIfOp>([&](
auto op) {
 
  601            indent() << prependAttributes(op, 
"static if ");
 
  602            emitValue(op.getCond(), 
false);
 
  604            emitCalyxBody([&]() { emitCalyxControl(op.getThenBody()); });
 
  605            if (op.elseBodyExists())
 
  606              emitCalyxSection(
"else",
 
  607                               [&]() { emitCalyxControl(op.getElseBody()); });
 
  609          .Case<RepeatOp>([&](
auto op) {
 
  610            indent() << prependAttributes(op, 
"repeat ");
 
  613            emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
 
  615          .Case<StaticRepeatOp>([&](
auto op) {
 
  616            indent() << prependAttributes(op, 
"static repeat ");
 
  619            emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
 
  621          .Case<StaticParOp>([&](
auto op) {
 
  622            emitCalyxSection(prependAttributes(op, 
"static par"),
 
  623                             [&]() { emitCalyxControl(op.getBodyBlock()); });
 
  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.");
 
  634  llvm::raw_ostream &os;
 
  637  bool encounteredError = 
false;
 
  641  unsigned currentIndent = 0;
 
  646LogicalResult Emitter::finalize() { 
return failure(encounteredError); }
 
  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);
 
  656      emitOpError(&bodyOp, 
"Unexpected op");
 
  661void Emitter::emitComponent(ComponentInterface op) {
 
  662  std::string combinationalPrefix = op.isComb() ? 
"comb " : 
"";
 
  664  indent() << combinationalPrefix << 
"component " << op.getName()
 
  665           << getAttributes(op, 
false, 
nullptr);
 
  667  emitComponentPorts(op);
 
  668  os << 
space() << LBraceEndL();
 
  674  emitCalyxSection(
"cells", [&]() {
 
  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) {  })
 
  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); })
 
  695              [&](
auto op) { emitLibraryPrimTypedByFirstOutputPort(op); })
 
  696          .Case<MultPipeLibOp>(
 
  697              [&](
auto op) { emitLibraryPrimTypedByFirstOutputPort(op); })
 
  698          .Case<RemUPipeLibOp, DivUPipeLibOp>([&](
auto op) {
 
  699            emitLibraryPrimTypedByFirstOutputPort(
 
  700                op, {
"std_div_pipe"});
 
  702          .Case<RemSPipeLibOp, DivSPipeLibOp>([&](
auto op) {
 
  703            emitLibraryPrimTypedByFirstOutputPort(
 
  704                op, {
"std_sdiv_pipe"});
 
  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");
 
  716  emitControl(control);
 
  722void Emitter::emitComponentPorts(ComponentInterface op) {
 
  723  auto emitPorts = [&](
auto ports) {
 
  725    for (
size_t i = 0, e = ports.size(); i < e; ++i) {
 
  729      unsigned int bitWidth = port.
type.getIntOrFloatBitWidth();
 
  730      os << getAttributes(op, 
true, port.
attributes)
 
  731         << port.
name.getValue() << colon() << bitWidth;
 
  738  emitPorts(op.getInputPortInfo());
 
  740  emitPorts(op.getOutputPortInfo());
 
  745  Attribute filename = op->getAttrDictionary().get(
"filename");
 
  746  indent() << 
"extern " << filename << 
space() << LBraceEndL();
 
  748  indent() << 
"primitive " << op.getName();
 
  750  if (!op.getParameters().empty()) {
 
  752    llvm::interleaveComma(op.getParameters(), os, [&](Attribute param) {
 
  753      auto paramAttr = cast<hw::ParamDeclAttr>(param);
 
  754      os << paramAttr.getName().str();
 
  758  os << getAttributes(op, 
false);
 
  760  emitPrimitivePorts(op);
 
  761  os << semicolonEndL();
 
  768  auto emitPorts = [&](
auto ports, 
bool isInput) {
 
  769    auto e = 
static_cast<size_t>(std::distance(ports.begin(), ports.end()));
 
  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)));
 
  777      os << getAttributes(op, 
true, portAttr)
 
  778         << port.
name.getValue() << colon();
 
  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();
 
  786        unsigned int bitWidth = port.
type.getIntOrFloatBitWidth();
 
  796  emitPorts(ports.getInputs(), 
true);
 
  798  emitPorts(ports.getOutputs(), 
false);
 
  801void Emitter::emitInstance(InstanceOp op) {
 
  802  indent() << getAttributes(op, 
true) << op.instanceName()
 
  803           << 
space() << equals() << 
space() << op.getComponentName()
 
  804           << LParen() << RParen() << semicolonEndL();
 
  807void Emitter::emitPrimitive(PrimitiveOp op) {
 
  808  indent() << getAttributes(op, 
true) << op.instanceName()
 
  809           << 
space() << equals() << 
space() << op.getPrimitiveName()
 
  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();
 
  821        llvm_unreachable(
"Primitive parameter type not supported");
 
  826  os << RParen() << semicolonEndL();
 
  829void Emitter::emitRegister(RegisterOp reg) {
 
  830  size_t bitWidth = 
reg.getIn().getType().getIntOrFloatBitWidth();
 
  831  indent() << getAttributes(reg, 
true) << 
reg.instanceName()
 
  832           << 
space() << equals() << 
space() << 
"std_reg" << LParen()
 
  833           << std::to_string(bitWidth) << RParen() << semicolonEndL();
 
  836void Emitter::emitUndef(UndefLibOp op) {
 
  837  size_t bitwidth = op.getOut().getType().getIntOrFloatBitWidth();
 
  838  indent() << getAttributes(op, 
true) << op.instanceName()
 
  839           << 
space() << equals() << 
space() << 
"undef" << LParen()
 
  840           << std::to_string(bitwidth) << RParen() << semicolonEndL();
 
  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.");
 
  850  indent() << getAttributes(memory, 
true) << memory.instanceName()
 
  851           << 
space() << equals() << 
space() << 
"std_mem_d" 
  852           << std::to_string(dimension) << LParen() << memory.getWidth()
 
  854  for (Attribute size : memory.getSizes()) {
 
  855    APInt memSize = cast<IntegerAttr>(size).getValue();
 
  856    memSize.print(os, 
false);
 
  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, 
false);
 
  868  os << RParen() << semicolonEndL();
 
  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.");
 
  878  bool isRef = !memory->hasAttr(
"external");
 
  882  os << getAttributes(memory, 
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, 
false);
 
  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, 
false);
 
  899  os << RParen() << semicolonEndL();
 
  902void Emitter::emitInvoke(InvokeOp invoke) {
 
  903  StringRef callee = invoke.getCallee();
 
  904  indent() << 
"invoke " << callee;
 
  905  auto refCellsMap = invoke.getRefCellsMap();
 
  906  if (!refCellsMap.empty()) {
 
  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();
 
  913            cast<FlatSymbolRefAttr>(namedAttr.getValue()).getValue();
 
  914        os << refCellName << 
" = " << externalMem;
 
  919  ArrayAttr portNames = invoke.getPortNames();
 
  920  ArrayAttr inputNames = invoke.getInputNames();
 
  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();
 
  937    StringRef inputMapKey = portName.drop_front(2 + callee.size());
 
  938    if (portName.substr(1, callee.size()) == callee) {
 
  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;
 
  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();
 
  956  llvm::interleaveComma(inputsMap, os, [&](
const auto &iter) {
 
  957    os << iter.getKey() << 
" = " << iter.getValue();
 
  962  llvm::interleaveComma(outputsMap, os, [&](
const auto &iter) {
 
  963    os << iter.getKey() << 
" = " << iter.getValue();
 
  965  os << RParen() << semicolonEndL();
 
  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";
 
  980  static constexpr int32_t 
IEEE754 = 0;
 
  981  os << LParen() << std::to_string(
IEEE754) << comma() << floatBits << comma()
 
  982     << std::to_string(doubleValue) << RParen() << semicolonEndL();
 
  990void Emitter::emitLibraryPrimTypedByAllPorts(Operation *op) {
 
  991  auto cell = cast<CellInterface>(op);
 
  992  indent() << getAttributes(op, 
true) << cell.instanceName()
 
  995  llvm::interleaveComma(op->getResults(), os, [&](
auto res) {
 
  996    os << std::to_string(res.getType().getIntOrFloatBitWidth());
 
  998  os << RParen() << semicolonEndL();
 
 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, 
true) << cell.instanceName()
 
 1007           << LParen() << bitWidth << RParen() << semicolonEndL();
 
 1010void Emitter::emitLibraryPrimTypedByFirstOutputPort(
 
 1011    Operation *op, std::optional<StringRef> calyxLibName) {
 
 1012  auto cell = cast<CellInterface>(op);
 
 1014      cell.getOutputPorts()[0].getType().getIntOrFloatBitWidth();
 
 1015  StringRef opName = op->getName().getStringRef();
 
 1016  indent() << getAttributes(op, 
true) << cell.instanceName()
 
 1019           << LParen() << bitWidth << RParen() << semicolonEndL();
 
 1023  if (isa<IntToFpOpIEEE754>(cell.getOperation())) {
 
 1025    return cell.getOutputPorts()[0].getType().getIntOrFloatBitWidth();
 
 1028  auto inputPorts = cell.getInputPorts();
 
 1029  assert(inputPorts.size() >= 2 && 
"There should be at least two input ports");
 
 1033  size_t inputPortIndex = inputPorts.size() - 2;
 
 1034  return cell.getInputPorts()[inputPortIndex].getType().getIntOrFloatBitWidth();
 
 
 1037unsigned getIntWidth(Operation *op, CellInterface &cell, 
bool isIntToFp) {
 
 1038  auto inputPorts = cell.getInputPorts();
 
 1039  assert(inputPorts.size() >= 2);
 
 1041  size_t integerIndex = inputPorts.size() - 2;
 
 1044    return cell.getOutputPorts()[0].getType().getIntOrFloatBitWidth();
 
 1046  return cell.getInputPorts()[integerIndex].getType().getIntOrFloatBitWidth();
 
 
 1049void Emitter::emitLibraryFloatingPoint(Operation *op) {
 
 1050  auto cell = cast<CellInterface>(op);
 
 1053  unsigned expWidth, sigWidth;
 
 1054  switch (fpBitWidth) {
 
 1072    op->emitError(
"The supported bitwidths are 16, 32, 64, and 128");
 
 1077  if (
auto fpOp = dyn_cast<calyx::FloatingPointOpInterface>(op)) {
 
 1078    opName = fpOp.getCalyxLibraryName();
 
 1081  indent() << getAttributes(op, 
true) << cell.instanceName()
 
 1082           << 
space() << equals() << 
space() << opName << LParen();
 
 1084  if (isa<calyx::IntToFpOpIEEE754>(op)) {
 
 1086    os << intWidth << comma();
 
 1089  os << expWidth << comma() << sigWidth << comma() << fpBitWidth;
 
 1091  if (
auto fpToIntOp = dyn_cast<calyx::FpToIntOpIEEE754>(op)) {
 
 1093    os << comma() << intWidth;
 
 1096  os << RParen() << semicolonEndL();
 
 1099void Emitter::emitAssignment(AssignOp op) {
 
 1101  emitValue(op.getDest(), 
true);
 
 1103  if (op.getGuard()) {
 
 1104    emitValue(op.getGuard(), 
false);
 
 1105    os << questionMark();
 
 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()) {
 
 1116      auto intValue = value.bitcastToAPInt();
 
 1117      intValue.toStringUnsigned(str, 2);
 
 1118      os << std::to_string(intValue.getBitWidth()) << apostrophe() << 
"b" << str
 
 1123  emitValue(op.getSrc(), 
false);
 
 1124  os << semicolonEndL();
 
 1127void Emitter::emitWires(WiresOp op) {
 
 1128  emitCalyxSection(
"wires", [&]() {
 
 1130      TypeSwitch<Operation *>(&bodyOp)
 
 1131          .Case<GroupInterface>([&](
auto op) { emitGroup(op); })
 
 1132          .Case<AssignOp>([&](
auto op) { emitAssignment(op); })
 
 1135          .Default([&](
auto op) {
 
 1136            emitOpError(op, 
"not supported for emission inside wires section");
 
 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>(
 
 1151          .Default([&](
auto op) {
 
 1152            emitOpError(op, 
"not supported for emission inside group.");
 
 1157  if (isa<StaticGroupOp>(group)) {
 
 1158    auto staticGroup = cast<StaticGroupOp>(group);
 
 1159    prefix = llvm::formatv(
"static<{0}> group", staticGroup.getLatency());
 
 1161    prefix = isa<CombGroupOp>(group) ? 
"comb group" : 
"group";
 
 1164      (group.symName().getValue() + getAttributes(group, 
false))
 
 1166  emitCalyxSection(prefix, emitGroupBody, groupHeader);
 
 1169void Emitter::emitEnable(EnableOp enable) {
 
 1170  indent() << getAttributes(enable, 
true) << enable.getGroupName()
 
 1174void Emitter::emitControl(ControlOp control) {
 
 1176  if (control == 
nullptr)
 
 1178  emitCalyxSection(
"control",
 
 1179                   [&]() { emitCalyxControl(control.getBodyBlock()); });
 
 1188                                              llvm::raw_ostream &os) {
 
 1189  Emitter emitter(os);
 
 1190  if (failed(emitter.emitImports(module)))
 
 1192  emitter.emitModule(module);
 
 1193  emitter.emitCiderMetadata(module);
 
 1194  return emitter.finalize();
 
 
 1198  static mlir::TranslateFromMLIRRegistration toCalyx(
 
 1199      "export-calyx", 
"export Calyx",
 
 1200      [](ModuleOp module, llvm::raw_ostream &os) {
 
 1203      [](mlir::DialectRegistry ®istry) {
 
 1205            .insert<calyx::CalyxDialect, comb::CombDialect, hw::HWDialect>();
 
 
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.
 
static Block * getBodyBlock(FModuleLike mod)
 
Signals that the following operation is "control-like.".
 
PortInfo getPortInfo(BlockArgument arg)
Returns port information for the block argument provided.
 
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)
 
This holds information about the port for either a Component or Cell.
 
DictionaryAttr attributes
 
This holds a decoded list of input/inout and output ports for a module or instance.