CIRCT  20.0.0git
CalyxLoweringUtils.cpp
Go to the documentation of this file.
1 //===- CalyxLoweringUtils.cpp - Calyx lowering utility methods --*- C++ -*-===//
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 // Various lowering utility methods converting to and from Calyx programs.
10 //
11 //===----------------------------------------------------------------------===//
12 
17 #include "circt/Support/LLVM.h"
18 #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
19 #include "mlir/Dialect/MemRef/IR/MemRef.h"
20 #include "mlir/Dialect/SCF/IR/SCF.h"
21 #include "mlir/IR/Matchers.h"
22 
23 #include <variant>
24 
25 using namespace llvm;
26 using namespace mlir;
27 using namespace mlir::arith;
28 
29 namespace circt {
30 namespace calyx {
31 
32 void appendPortsForExternalMemref(PatternRewriter &rewriter, StringRef memName,
33  Value memref, unsigned memoryID,
34  SmallVectorImpl<calyx::PortInfo> &inPorts,
35  SmallVectorImpl<calyx::PortInfo> &outPorts) {
36  MemRefType memrefType = cast<MemRefType>(memref.getType());
37 
38  // Ports constituting a memory interface are added a set of attributes under
39  // a "mem : {...}" dictionary. These attributes allows for deducing which
40  // top-level I/O signals constitutes a unique memory interface.
41  auto getMemoryInterfaceAttr = [&](StringRef tag,
42  std::optional<unsigned> addrIdx = {}) {
43  auto attrs = SmallVector<NamedAttribute>{
44  // "id" denotes a unique memory interface.
45  rewriter.getNamedAttr("id", rewriter.getI32IntegerAttr(memoryID)),
46  // "tag" denotes the function of this signal.
47  rewriter.getNamedAttr("tag", rewriter.getStringAttr(tag))};
48  if (addrIdx.has_value())
49  // "addr_idx" denotes the address index of this signal, for
50  // multi-dimensional memory interfaces.
51  attrs.push_back(rewriter.getNamedAttr(
52  "addr_idx", rewriter.getI32IntegerAttr(*addrIdx)));
53 
54  return rewriter.getNamedAttr("mem", rewriter.getDictionaryAttr(attrs));
55  };
56 
57  // Read data
58  inPorts.push_back(calyx::PortInfo{
59  rewriter.getStringAttr(memName + "_read_data"),
60  memrefType.getElementType(), calyx::Direction::Input,
61  DictionaryAttr::get(rewriter.getContext(),
62  {getMemoryInterfaceAttr("read_data")})});
63 
64  // Done
65  inPorts.push_back(
66  calyx::PortInfo{rewriter.getStringAttr(memName + "_done"),
67  rewriter.getI1Type(), calyx::Direction::Input,
68  DictionaryAttr::get(rewriter.getContext(),
69  {getMemoryInterfaceAttr("done")})});
70 
71  // Write data
72  outPorts.push_back(calyx::PortInfo{
73  rewriter.getStringAttr(memName + "_write_data"),
74  memrefType.getElementType(), calyx::Direction::Output,
75  DictionaryAttr::get(rewriter.getContext(),
76  {getMemoryInterfaceAttr("write_data")})});
77 
78  // Memory address outputs
79  for (auto dim : enumerate(memrefType.getShape())) {
80  outPorts.push_back(calyx::PortInfo{
81  rewriter.getStringAttr(memName + "_addr" + std::to_string(dim.index())),
82  rewriter.getIntegerType(calyx::handleZeroWidth(dim.value())),
84  DictionaryAttr::get(rewriter.getContext(),
85  {getMemoryInterfaceAttr("addr", dim.index())})});
86  }
87 
88  // Write enable
89  outPorts.push_back(calyx::PortInfo{
90  rewriter.getStringAttr(memName + "_write_en"), rewriter.getI1Type(),
92  DictionaryAttr::get(rewriter.getContext(),
93  {getMemoryInterfaceAttr("write_en")})});
94 }
95 
96 WalkResult
97 getCiderSourceLocationMetadata(calyx::ComponentOp component,
98  SmallVectorImpl<Attribute> &sourceLocations) {
99  Builder builder(component->getContext());
100  return component.getControlOp().walk([&](Operation *op) {
101  if (!calyx::isControlLeafNode(op))
102  return WalkResult::advance();
103 
104  std::string sourceLocation;
105  llvm::raw_string_ostream os(sourceLocation);
106  op->getLoc()->print(os);
107  int64_t position = sourceLocations.size();
108  sourceLocations.push_back(
109  StringAttr::get(op->getContext(), sourceLocation));
110 
111  op->setAttr("pos", builder.getI64IntegerAttr(position));
112  return WalkResult::advance();
113  });
114 }
115 
116 bool matchConstantOp(Operation *op, APInt &value) {
117  return mlir::detail::constant_int_value_binder(&value).match(op);
118 }
119 
120 bool singleLoadFromMemory(Value memoryReference) {
121  return llvm::count_if(memoryReference.getUses(), [](OpOperand &user) {
122  return isa<mlir::memref::LoadOp>(user.getOwner());
123  }) <= 1;
124 }
125 
126 bool noStoresToMemory(Value memoryReference) {
127  return llvm::none_of(memoryReference.getUses(), [](OpOperand &user) {
128  return isa<mlir::memref::StoreOp>(user.getOwner());
129  });
130 }
131 
132 Value getComponentOutput(calyx::ComponentOp compOp, unsigned outPortIdx) {
133  size_t index = compOp.getInputPortInfo().size() + outPortIdx;
134  assert(index < compOp.getNumArguments() &&
135  "Exceeded number of arguments in the Component");
136  return compOp.getArgument(index);
137 }
138 
139 Type normalizeType(OpBuilder &builder, Type type) {
140  if (type.isIndex())
141  return builder.getI32Type();
142  if (type.isIntOrFloat())
143  return toBitVector(type);
144  return type;
145 }
146 
147 void buildAssignmentsForRegisterWrite(OpBuilder &builder,
148  calyx::GroupOp groupOp,
149  calyx::ComponentOp componentOp,
150  calyx::RegisterOp &reg,
151  Value inputValue) {
152  mlir::IRRewriter::InsertionGuard guard(builder);
153  auto loc = inputValue.getLoc();
154  builder.setInsertionPointToEnd(groupOp.getBodyBlock());
155  builder.create<calyx::AssignOp>(loc, reg.getIn(), inputValue);
156  builder.create<calyx::AssignOp>(
157  loc, reg.getWriteEn(), createConstant(loc, builder, componentOp, 1, 1));
158  builder.create<calyx::GroupDoneOp>(loc, reg.getDone());
159 }
160 
161 //===----------------------------------------------------------------------===//
162 // MemoryInterface
163 //===----------------------------------------------------------------------===//
164 
165 MemoryInterface::MemoryInterface() = default;
166 MemoryInterface::MemoryInterface(const MemoryPortsImpl &ports) : impl(ports) {
167  if (ports.writeEn.has_value() && ports.readOrContentEn.has_value()) {
168  assert(ports.isContentEn.value());
169  }
170 }
171 MemoryInterface::MemoryInterface(calyx::MemoryOp memOp) : impl(memOp) {}
172 MemoryInterface::MemoryInterface(calyx::SeqMemoryOp memOp) : impl(memOp) {}
173 
175  auto readData = readDataOpt();
176  assert(readData.has_value() && "Memory does not have readData");
177  return readData.value();
178 }
179 
181  auto readEn = readEnOpt();
182  assert(readEn.has_value() && "Memory does not have readEn");
183  return readEn.value();
184 }
185 
187  auto contentEn = contentEnOpt();
188  assert(contentEn.has_value() && "Memory does not have readEn");
189  return contentEn.value();
190 }
191 
193  auto writeData = writeDataOpt();
194  assert(writeData.has_value() && "Memory does not have writeData");
195  return writeData.value();
196 }
197 
199  auto writeEn = writeEnOpt();
200  assert(writeEn.has_value() && "Memory does not have writeEn");
201  return writeEn.value();
202 }
203 
205  auto done = doneOpt();
206  assert(done.has_value() && "Memory does not have done");
207  return done.value();
208 }
209 
211  if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
212  return memOp->getName().str();
213  }
214 
215  if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
216  return memOp->getName().str();
217  }
218  return std::get<MemoryPortsImpl>(impl).memName;
219 }
220 
221 std::optional<Value> MemoryInterface::readDataOpt() {
222  if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
223  return memOp->readData();
224  }
225 
226  if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
227  return memOp->readData();
228  }
229  return std::get<MemoryPortsImpl>(impl).readData;
230 }
231 
232 std::optional<Value> MemoryInterface::readEnOpt() {
233  if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
234  return std::nullopt;
235  }
236 
237  if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
238  return std::nullopt;
239  }
240 
241  if (std::get<MemoryPortsImpl>(impl).readOrContentEn.has_value()) {
242  assert(std::get<MemoryPortsImpl>(impl).isContentEn.has_value());
243  assert(!std::get<MemoryPortsImpl>(impl).isContentEn.value());
244  }
245  return std::get<MemoryPortsImpl>(impl).readOrContentEn;
246 }
247 
248 std::optional<Value> MemoryInterface::contentEnOpt() {
249  if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
250  return std::nullopt;
251  }
252 
253  if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
254  return memOp->contentEn();
255  }
256 
257  if (std::get<MemoryPortsImpl>(impl).readOrContentEn.has_value()) {
258  assert(std::get<MemoryPortsImpl>(impl).writeEn.has_value());
259  assert(std::get<MemoryPortsImpl>(impl).isContentEn.has_value());
260  assert(std::get<MemoryPortsImpl>(impl).isContentEn.value());
261  }
262  return std::get<MemoryPortsImpl>(impl).readOrContentEn;
263 }
264 
265 std::optional<Value> MemoryInterface::writeDataOpt() {
266  if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
267  return memOp->writeData();
268  }
269 
270  if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
271  return memOp->writeData();
272  }
273  return std::get<MemoryPortsImpl>(impl).writeData;
274 }
275 
276 std::optional<Value> MemoryInterface::writeEnOpt() {
277  if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
278  return memOp->writeEn();
279  }
280 
281  if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
282  return memOp->writeEn();
283  }
284  return std::get<MemoryPortsImpl>(impl).writeEn;
285 }
286 
287 std::optional<Value> MemoryInterface::doneOpt() {
288  if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
289  return memOp->done();
290  }
291 
292  if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
293  return memOp->done();
294  }
295  return std::get<MemoryPortsImpl>(impl).done;
296 }
297 
299  if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
300  return memOp->addrPorts();
301  }
302 
303  if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
304  return memOp->addrPorts();
305  }
306  return std::get<MemoryPortsImpl>(impl).addrPorts;
307 }
308 
309 //===----------------------------------------------------------------------===//
310 // BasicLoopInterface
311 //===----------------------------------------------------------------------===//
312 
314 
315 //===----------------------------------------------------------------------===//
316 // ComponentLoweringStateInterface
317 //===----------------------------------------------------------------------===//
318 
320  calyx::ComponentOp component)
321  : component(component), extMemData(llvm::json::Object{}) {}
322 
324 
326  return component;
327 }
328 
330  calyx::RegisterOp reg,
331  unsigned idx) {
332  assert(blockArgRegs[block].count(idx) == 0);
333  assert(idx < block->getArguments().size());
334  blockArgRegs[block][idx] = reg;
335 }
336 
337 const DenseMap<unsigned, calyx::RegisterOp> &
339  return blockArgRegs[block];
340 }
341 
343  calyx::GroupOp grp) {
344  blockArgGroups[from][to].push_back(grp);
345 }
346 
347 ArrayRef<calyx::GroupOp>
349  return blockArgGroups[from][to];
350 }
351 
352 std::string ComponentLoweringStateInterface::getUniqueName(StringRef prefix) {
353  std::string prefixStr = prefix.str();
354  unsigned idx = prefixIdMap[prefixStr];
355  ++prefixIdMap[prefixStr];
356  return (prefix + "_" + std::to_string(idx)).str();
357 }
358 
360  auto it = opNames.find(op);
361  assert(it != opNames.end() && "A unique name should have been set for op");
362  return it->second;
363 }
364 
366  StringRef prefix) {
367  assert(opNames.find(op) == opNames.end() &&
368  "A unique name was already set for op");
369  opNames[op] = getUniqueName(prefix);
370 }
371 
373  Value v, calyx::GroupInterface group) {
374  valueGroupAssigns[v] = group;
375 }
376 
378  unsigned idx) {
379  assert(returnRegs.count(idx) == 0 &&
380  "A register was already registered for this index");
381  returnRegs[idx] = reg;
382 }
383 
384 calyx::RegisterOp ComponentLoweringStateInterface::getReturnReg(unsigned idx) {
385  assert(returnRegs.count(idx) && "No register registered for index!");
386  return returnRegs[idx];
387 }
388 
390  Value memref, const calyx::MemoryInterface &memoryInterface) {
391  assert(isa<MemRefType>(memref.getType()));
392  assert(memories.find(memref) == memories.end() &&
393  "Memory already registered for memref");
394  memories[memref] = memoryInterface;
395 }
396 
399  assert(isa<MemRefType>(memref.getType()));
400  auto it = memories.find(memref);
401  assert(it != memories.end() && "No memory registered for memref");
402  return it->second;
403 }
404 
405 std::optional<calyx::MemoryInterface>
407  for (auto &memIf : memories) {
408  auto &mem = memIf.getSecond();
409  if (mem.writeEn() == v || mem.writeData() == v ||
410  llvm::any_of(mem.addrPorts(), [=](Value port) { return port == v; }))
411  return {mem};
412  }
413  return {};
414 }
415 
417  const DenseMap<unsigned, unsigned> &mapping) {
418  funcOpResultMapping = mapping;
419 }
420 
422  unsigned funcReturnIdx) {
423  auto it = funcOpResultMapping.find(funcReturnIdx);
424  assert(it != funcOpResultMapping.end() &&
425  "No component return port index recorded for the requested function "
426  "return index");
427  return it->second;
428 }
429 
430 InstanceOp ComponentLoweringStateInterface::getInstance(StringRef calleeName) {
431  return instanceMap[calleeName];
432 }
433 
435  InstanceOp instanceOp) {
436  instanceMap[calleeName] = instanceOp;
437 }
438 
439 //===----------------------------------------------------------------------===//
440 // CalyxLoweringState
441 //===----------------------------------------------------------------------===//
442 
444  StringRef topLevelFunction)
445  : topLevelFunction(topLevelFunction), module(module) {}
446 
448  assert(module.getOperation() != nullptr);
449  return module;
450 }
451 
453  return topLevelFunction;
454 }
455 
456 std::string CalyxLoweringState::blockName(Block *b) {
457  std::string blockName = irName(*b);
458  blockName.erase(std::remove(blockName.begin(), blockName.end(), '^'),
459  blockName.end());
460  return blockName;
461 }
462 
463 //===----------------------------------------------------------------------===//
464 // ModuleOpConversion
465 //===----------------------------------------------------------------------===//
466 
467 /// Helper to update the top-level ModuleOp to set the entrypoing function.
468 LogicalResult applyModuleOpConversion(mlir::ModuleOp moduleOp,
469  StringRef topLevelFunction) {
470 
471  if (moduleOp->hasAttr("calyx.entrypoint"))
472  return failure();
473 
474  moduleOp->setAttr("calyx.entrypoint",
475  StringAttr::get(moduleOp.getContext(), topLevelFunction));
476  return success();
477 }
478 
479 //===----------------------------------------------------------------------===//
480 // Partial lowering patterns
481 //===----------------------------------------------------------------------===//
482 
484  MLIRContext *context, LogicalResult &resRef,
485  PatternApplicationState &patternState,
486  DenseMap<mlir::func::FuncOp, calyx::ComponentOp> &map,
488  : PartialLoweringPattern(context, resRef, patternState),
489  functionMapping(map), calyxLoweringState(state) {}
490 
491 LogicalResult
493  PatternRewriter &rewriter) const {
494  // Initialize the component op references if a calyx::ComponentOp has been
495  // created for the matched funcOp.
496  if (auto it = functionMapping.find(funcOp); it != functionMapping.end()) {
497  componentOp = it->second;
500  componentOp);
501  }
502 
503  return partiallyLowerFuncToComp(funcOp, rewriter);
504 }
505 
506 calyx::ComponentOp FuncOpPartialLoweringPattern::getComponent() const {
508  "Component operation should be set during pattern construction");
509  return componentOp;
510 }
511 
513  return calyxLoweringState;
514 }
515 
516 //===----------------------------------------------------------------------===//
517 // ConvertIndexTypes
518 //===----------------------------------------------------------------------===//
519 
520 LogicalResult
522  PatternRewriter &rewriter) const {
523  funcOp.walk([&](Block *block) {
524  for (Value arg : block->getArguments())
525  arg.setType(calyx::normalizeType(rewriter, arg.getType()));
526  });
527 
528  funcOp.walk([&](Operation *op) {
529  for (Value result : op->getResults()) {
530  Type resType = result.getType();
531  if (!resType.isIndex())
532  continue;
533 
534  result.setType(calyx::normalizeType(rewriter, resType));
535  auto constant = dyn_cast<mlir::arith::ConstantOp>(op);
536  if (!constant)
537  continue;
538 
539  APInt value;
540  calyx::matchConstantOp(constant, value);
541  rewriter.setInsertionPoint(constant);
542  rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
543  constant, rewriter.getI32IntegerAttr(value.getSExtValue()));
544  }
545  });
546  return success();
547 }
548 
549 //===----------------------------------------------------------------------===//
550 // NonTerminatingGroupDonePattern
551 //===----------------------------------------------------------------------===//
552 
553 LogicalResult
554 NonTerminatingGroupDonePattern::matchAndRewrite(calyx::GroupDoneOp groupDoneOp,
555  PatternRewriter &) const {
556  Block *block = groupDoneOp->getBlock();
557  if (&block->back() == groupDoneOp)
558  return failure();
559 
560  groupDoneOp->moveBefore(groupDoneOp->getBlock(),
561  groupDoneOp->getBlock()->end());
562  return success();
563 }
564 
565 //===----------------------------------------------------------------------===//
566 // MultipleGroupDonePattern
567 //===----------------------------------------------------------------------===//
568 
569 LogicalResult
571  PatternRewriter &rewriter) const {
572  auto groupDoneOps = SmallVector<calyx::GroupDoneOp>(
573  groupOp.getBodyBlock()->getOps<calyx::GroupDoneOp>());
574 
575  if (groupDoneOps.size() <= 1)
576  return failure();
577 
578  /// 'and' all of the calyx::GroupDoneOp's.
579  rewriter.setInsertionPointToEnd(groupDoneOps[0]->getBlock());
580  SmallVector<Value> doneOpSrcs;
581  llvm::transform(groupDoneOps, std::back_inserter(doneOpSrcs),
582  [](calyx::GroupDoneOp op) { return op.getSrc(); });
583  Value allDone = rewriter.create<comb::AndOp>(groupDoneOps.front().getLoc(),
584  doneOpSrcs, false);
585 
586  /// Create a group done op with the complex expression as a guard.
587  rewriter.create<calyx::GroupDoneOp>(
588  groupOp.getLoc(),
589  rewriter.create<hw::ConstantOp>(groupOp.getLoc(), APInt(1, 1)), allDone);
590  for (auto groupDoneOp : groupDoneOps)
591  rewriter.eraseOp(groupDoneOp);
592 
593  return success();
594 }
595 
596 //===----------------------------------------------------------------------===//
597 // EliminateUnusedCombGroups
598 //===----------------------------------------------------------------------===//
599 
600 LogicalResult
601 EliminateUnusedCombGroups::matchAndRewrite(calyx::CombGroupOp combGroupOp,
602  PatternRewriter &rewriter) const {
603  auto control =
604  combGroupOp->getParentOfType<calyx::ComponentOp>().getControlOp();
605  if (!SymbolTable::symbolKnownUseEmpty(combGroupOp.getSymNameAttr(), control))
606  return failure();
607 
608  rewriter.eraseOp(combGroupOp);
609  return success();
610 }
611 
612 //===----------------------------------------------------------------------===//
613 // InlineCombGroups
614 //===----------------------------------------------------------------------===//
615 
616 InlineCombGroups::InlineCombGroups(MLIRContext *context, LogicalResult &resRef,
617  PatternApplicationState &patternState,
619  : PartialLoweringPattern(context, resRef, patternState), cls(cls) {}
620 
621 LogicalResult
622 InlineCombGroups::partiallyLower(calyx::GroupInterface originGroup,
623  PatternRewriter &rewriter) const {
624  auto component = originGroup->getParentOfType<calyx::ComponentOp>();
625  ComponentLoweringStateInterface *state = cls.getState(component);
626 
627  // Filter groups which are not part of the control schedule.
628  if (SymbolTable::symbolKnownUseEmpty(originGroup.symName(),
629  component.getControlOp()))
630  return success();
631 
632  // Maintain a set of the groups which we've inlined so far. The group
633  // itself is implicitly inlined.
634  llvm::SmallSetVector<Operation *, 8> inlinedGroups;
635  inlinedGroups.insert(originGroup);
636 
637  // Starting from the matched originGroup, we traverse use-def chains of
638  // combinational logic, and inline assignments from the defining
639  // combinational groups.
640  recurseInlineCombGroups(rewriter, *state, inlinedGroups, originGroup,
641  originGroup,
642  /*doInline=*/false);
643  return success();
644 }
645 
647  PatternRewriter &rewriter, ComponentLoweringStateInterface &state,
648  llvm::SmallSetVector<Operation *, 8> &inlinedGroups,
649  calyx::GroupInterface originGroup, calyx::GroupInterface recGroup,
650  bool doInline) const {
651  inlinedGroups.insert(recGroup);
652  for (auto assignOp : recGroup.getBody()->getOps<calyx::AssignOp>()) {
653  if (doInline) {
654  /// Inline the assignment into the originGroup.
655  auto *clonedAssignOp = rewriter.clone(*assignOp.getOperation());
656  clonedAssignOp->moveBefore(originGroup.getBody(),
657  originGroup.getBody()->end());
658  }
659  Value src = assignOp.getSrc();
660 
661  // Things which stop recursive inlining (or in other words, what
662  // breaks combinational paths).
663  // - Component inputs
664  // - Register and memory reads
665  // - Constant ops (constant ops are not evaluated by any group)
666  // - Multiplication pipelines are sequential.
667  // - 'While' return values (these are registers, however, 'while'
668  // return values have at the current point of conversion not yet
669  // been rewritten to their register outputs, see comment in
670  // LateSSAReplacement)
671  if (isa<BlockArgument>(src) ||
672  isa<calyx::RegisterOp, calyx::MemoryOp, calyx::SeqMemoryOp,
673  hw::ConstantOp, mlir::arith::ConstantOp, calyx::MultPipeLibOp,
674  calyx::DivUPipeLibOp, calyx::DivSPipeLibOp, calyx::RemSPipeLibOp,
675  calyx::RemUPipeLibOp, mlir::scf::WhileOp, calyx::InstanceOp,
676  calyx::ConstantOp, calyx::AddFOpIEEE754, calyx::MulFOpIEEE754,
677  calyx::CompareFOpIEEE754>(src.getDefiningOp()))
678  continue;
679 
680  auto srcCombGroup = dyn_cast<calyx::CombGroupOp>(
681  state.getEvaluatingGroup(src).getOperation());
682  if (!srcCombGroup)
683  continue;
684  if (inlinedGroups.count(srcCombGroup))
685  continue;
686 
687  recurseInlineCombGroups(rewriter, state, inlinedGroups, originGroup,
688  srcCombGroup, /*doInline=*/true);
689  }
690 }
691 
692 //===----------------------------------------------------------------------===//
693 // RewriteMemoryAccesses
694 //===----------------------------------------------------------------------===//
695 
696 LogicalResult
697 RewriteMemoryAccesses::partiallyLower(calyx::AssignOp assignOp,
698  PatternRewriter &rewriter) const {
699  auto *state = cls.getState(assignOp->getParentOfType<calyx::ComponentOp>());
700 
701  Value dest = assignOp.getDest();
702  if (!state->isInputPortOfMemory(dest))
703  return success();
704 
705  Value src = assignOp.getSrc();
706  unsigned srcBits = src.getType().getIntOrFloatBitWidth();
707  unsigned dstBits = dest.getType().getIntOrFloatBitWidth();
708  if (srcBits == dstBits)
709  return success();
710 
711  SmallVector<Type> types = {
712  rewriter.getIntegerType(srcBits),
713  rewriter.getIntegerType(dstBits),
714  };
715  mlir::Location loc = assignOp.getLoc();
716  Operation *newOp;
717  if (srcBits > dstBits)
718  newOp =
719  state->getNewLibraryOpInstance<calyx::SliceLibOp>(rewriter, loc, types);
720  else
721  newOp =
722  state->getNewLibraryOpInstance<calyx::PadLibOp>(rewriter, loc, types);
723 
724  rewriter.setInsertionPoint(assignOp->getBlock(),
725  assignOp->getBlock()->begin());
726  rewriter.create<calyx::AssignOp>(assignOp->getLoc(), newOp->getResult(0),
727  src);
728  assignOp.setOperand(1, newOp->getResult(1));
729 
730  return success();
731 }
732 
733 //===----------------------------------------------------------------------===//
734 // BuildBasicBlockRegs
735 //===----------------------------------------------------------------------===//
736 
737 LogicalResult
739  PatternRewriter &rewriter) const {
740  funcOp.walk([&](Block *block) {
741  /// Do not register component input values.
742  if (block == &block->getParent()->front())
743  return;
744 
745  for (auto arg : enumerate(block->getArguments())) {
746  Type argType = arg.value().getType();
747  assert(isa<IntegerType>(argType) && "unsupported block argument type");
748  unsigned width = argType.getIntOrFloatBitWidth();
749  std::string index = std::to_string(arg.index());
750  std::string name = loweringState().blockName(block) + "_arg" + index;
751  auto reg = createRegister(arg.value().getLoc(), rewriter, getComponent(),
752  width, name);
753  getState().addBlockArgReg(block, reg, arg.index());
754  arg.value().replaceAllUsesWith(reg.getOut());
755  }
756  });
757  return success();
758 }
759 
760 //===----------------------------------------------------------------------===//
761 // BuildReturnRegs
762 //===----------------------------------------------------------------------===//
763 
764 LogicalResult
766  PatternRewriter &rewriter) const {
767 
768  for (auto argType : enumerate(funcOp.getResultTypes())) {
769  auto convArgType = calyx::normalizeType(rewriter, argType.value());
770  assert((isa<IntegerType>(convArgType) || isa<FloatType>(convArgType)) &&
771  "unsupported return type");
772  std::string name = "ret_arg" + std::to_string(argType.index());
773  auto reg = createRegister(funcOp.getLoc(), rewriter, getComponent(),
774  convArgType.getIntOrFloatBitWidth(), name);
775  getState().addReturnReg(reg, argType.index());
776 
777  rewriter.setInsertionPointToStart(
778  getComponent().getWiresOp().getBodyBlock());
779  rewriter.create<calyx::AssignOp>(
780  funcOp->getLoc(),
782  getComponent(), getState().getFuncOpResultMapping(argType.index())),
783  reg.getOut());
784  }
785  return success();
786 }
787 
788 //===----------------------------------------------------------------------===//
789 // BuildCallInstance
790 //===----------------------------------------------------------------------===//
791 
792 LogicalResult
794  PatternRewriter &rewriter) const {
795  funcOp.walk([&](mlir::func::CallOp callOp) {
796  ComponentOp componentOp = getCallComponent(callOp);
797  SmallVector<Type, 8> resultTypes;
798  for (auto type : componentOp.getArgumentTypes())
799  resultTypes.push_back(type);
800  for (auto type : componentOp.getResultTypes())
801  resultTypes.push_back(type);
802  std::string instanceName = getInstanceName(callOp);
803 
804  // Determines if an instance needs to be created. If the same function was
805  // called by CallOp before, it doesn't need to be created, if not, the
806  // instance is created.
807  if (!getState().getInstance(instanceName)) {
808  InstanceOp instanceOp =
809  createInstance(callOp.getLoc(), rewriter, getComponent(), resultTypes,
810  instanceName, componentOp.getName());
811  getState().addInstance(instanceName, instanceOp);
812  hw::ConstantOp constantOp =
813  createConstant(callOp.getLoc(), rewriter, getComponent(), 1, 1);
814  OpBuilder::InsertionGuard g(rewriter);
815  rewriter.setInsertionPointToStart(
816  getComponent().getWiresOp().getBodyBlock());
817 
818  // Creates the group that initializes the instance.
819  calyx::GroupOp groupOp = rewriter.create<calyx::GroupOp>(
820  callOp.getLoc(), "init_" + instanceName);
821  rewriter.setInsertionPointToStart(groupOp.getBodyBlock());
822  auto portInfos = instanceOp.getReferencedComponent().getPortInfo();
823  auto results = instanceOp.getResults();
824  for (const auto &[portInfo, result] : llvm::zip(portInfos, results)) {
825  if (portInfo.hasAttribute(goPort) || portInfo.hasAttribute(resetPort))
826  rewriter.create<calyx::AssignOp>(callOp.getLoc(), result, constantOp);
827  else if (portInfo.hasAttribute(donePort))
828  rewriter.create<calyx::GroupDoneOp>(callOp.getLoc(), result);
829  }
830  }
831  WalkResult::advance();
832  });
833  return success();
834 }
835 
836 ComponentOp
837 BuildCallInstance::getCallComponent(mlir::func::CallOp callOp) const {
838  std::string callee = "func_" + callOp.getCallee().str();
839  for (auto [funcOp, componentOp] : functionMapping) {
840  if (funcOp.getSymName() == callee)
841  return componentOp;
842  }
843  return nullptr;
844 }
845 
846 PredicateInfo getPredicateInfo(CmpFPredicate pred) {
847  using CombLogic = PredicateInfo::CombLogic;
848  using Port = PredicateInfo::InputPorts::Port;
849  PredicateInfo info;
850  switch (pred) {
851  case CmpFPredicate::OEQ: {
852  info.logic = CombLogic::And;
853  info.inputPorts = {{Port::Eq, /*inverted=*/false},
854  {Port::Unordered, /*inverted=*/true}};
855  break;
856  }
857  case CmpFPredicate::OGT: {
859  info.inputPorts = {{Port::Gt, /*inverted=*/false},
860  {Port::Unordered, /*inverted=*/true}};
861  break;
862  }
863  case CmpFPredicate::OGE: {
865  info.inputPorts = {{Port::Lt, /*inverted=*/true},
866  {Port::Unordered, /*inverted=*/true}};
867  break;
868  }
869  case CmpFPredicate::OLT: {
871  info.inputPorts = {{Port::Lt, /*inverted=*/false},
872  {Port::Unordered, /*inverted=*/true}};
873  break;
874  }
875  case CmpFPredicate::OLE: {
877  info.inputPorts = {{Port::Gt, /*inverted=*/true},
878  {Port::Unordered, /*inverted=*/true}};
879  break;
880  }
881  case CmpFPredicate::ONE: {
883  info.inputPorts = {{Port::Eq, /*inverted=*/true},
884  {Port::Unordered, /*inverted=*/true}};
885  break;
886  }
887  case CmpFPredicate::ORD: {
889  info.inputPorts = {{Port::Unordered, /*inverted=*/true}};
890  break;
891  }
892  case CmpFPredicate::UEQ: {
894  info.inputPorts = {{Port::Eq, /*inverted=*/false},
895  {Port::Unordered, /*inverted=*/false}};
896  break;
897  }
898  case CmpFPredicate::UGT: {
900  info.inputPorts = {{Port::Gt, /*inverted=*/false},
901  {Port::Unordered, /*inverted=*/false}};
902  break;
903  }
904  case CmpFPredicate::UGE: {
906  info.inputPorts = {{Port::Lt, /*inverted=*/true},
907  {Port::Unordered, /*inverted=*/false}};
908  break;
909  }
910  case CmpFPredicate::ULT: {
912  info.inputPorts = {{Port::Lt, /*inverted=*/false},
913  {Port::Unordered, /*inverted=*/false}};
914  break;
915  }
916  case CmpFPredicate::ULE: {
918  info.inputPorts = {{Port::Gt, /*inverted=*/true},
919  {Port::Unordered, /*inverted=*/false}};
920  break;
921  }
922  case CmpFPredicate::UNE: {
924  info.inputPorts = {{Port::Eq, /*inverted=*/true},
925  {Port::Unordered, /*inverted=*/false}};
926  break;
927  }
928  case CmpFPredicate::UNO: {
930  info.inputPorts = {{Port::Unordered, /*inverted=*/false}};
931  break;
932  }
933  case CmpFPredicate::AlwaysTrue:
934  case CmpFPredicate::AlwaysFalse:
936  break;
937  }
938 
939  return info;
940 }
941 
942 } // namespace calyx
943 } // namespace circt
assert(baseType &&"element must be base type")
@ Input
Definition: HW.h:35
@ Output
Definition: HW.h:35
static Block * getBodyBlock(FModuleLike mod)
LogicalResult partiallyLowerFuncToComp(mlir::func::FuncOp funcOp, PatternRewriter &rewriter) const override
LogicalResult partiallyLowerFuncToComp(mlir::func::FuncOp funcOp, PatternRewriter &rewriter) const override
ComponentOp getCallComponent(mlir::func::CallOp callOp) const
LogicalResult partiallyLowerFuncToComp(mlir::func::FuncOp funcOp, PatternRewriter &rewriter) const override
An interface for conversion passes that lower Calyx programs.
std::string irName(ValueOrBlock &v)
Returns a meaningful name for a value within the program scope.
mlir::ModuleOp getModule()
Returns the current program.
CalyxLoweringState(mlir::ModuleOp module, StringRef topLevelFunction)
T * getState(calyx::ComponentOp op)
Returns the component lowering state associated with op.
std::string blockName(Block *b)
Returns a meaningful name for a block within the program scope (removes the ^ prefix from block names...
StringRef getTopLevelFunction() const
Returns the name of the top-level function in the source program.
StringRef topLevelFunction
The name of this top-level function.
mlir::ModuleOp module
The program associated with this state.
const DenseMap< unsigned, calyx::RegisterOp > & getBlockArgRegs(Block *block)
Return a mapping of block argument indices to block argument registers.
void setFuncOpResultMapping(const DenseMap< unsigned, unsigned > &mapping)
Assign a mapping between the source funcOp result indices and the corresponding output port indices o...
DenseMap< Value, calyx::MemoryInterface > memories
A mapping from memref's to their corresponding Calyx memory interface.
TGroupOp getEvaluatingGroup(Value v)
Return the group which evaluates the value v.
void addReturnReg(calyx::RegisterOp reg, unsigned idx)
Register reg as being the idx'th return value register.
calyx::MemoryInterface getMemoryInterface(Value memref)
Returns the memory interface registered for the given memref.
llvm::StringMap< calyx::InstanceOp > instanceMap
A mapping between the callee and the instance.
DenseMap< Block *, DenseMap< Block *, SmallVector< calyx::GroupOp > > > blockArgGroups
Block arg groups is a list of groups that should be sequentially executed when passing control from t...
void setUniqueName(Operation *op, StringRef prefix)
Registers a unique name for a given operation using a provided prefix.
calyx::RegisterOp getReturnReg(unsigned idx)
Returns the idx'th return value register.
std::string getUniqueName(StringRef prefix)
Returns a unique name within compOp with the provided prefix.
calyx::ComponentOp component
The component which this lowering state is associated to.
void registerMemoryInterface(Value memref, const calyx::MemoryInterface &memoryInterface)
Registers a memory interface as being associated with a memory identified by 'memref'.
calyx::ComponentOp getComponentOp()
Returns the calyx::ComponentOp associated with this lowering state.
void addBlockArgReg(Block *block, calyx::RegisterOp reg, unsigned idx)
Register reg as being the idx'th argument register for block.
InstanceOp getInstance(StringRef calleeName)
The instance is obtained from the name of the callee.
unsigned getFuncOpResultMapping(unsigned funcReturnIdx)
Get the output port index of this component for which the funcReturnIdx of the original function maps...
DenseMap< unsigned, unsigned > funcOpResultMapping
A mapping between the source funcOp result indices and the corresponding output port indices of this ...
void addInstance(StringRef calleeName, InstanceOp instanceOp)
Put the name of the callee and the instance of the call into map.
DenseMap< Value, calyx::GroupInterface > valueGroupAssigns
A mapping between SSA values and the groups which assign them.
std::map< std::string, unsigned > prefixIdMap
A mapping of string prefixes and the current uniqueness counter for that prefix.
ArrayRef< calyx::GroupOp > getBlockArgGroups(Block *from, Block *to)
Returns a list of groups to be evaluated to perform the block argument register assignments when tran...
ComponentLoweringStateInterface(calyx::ComponentOp component)
void registerEvaluatingGroup(Value v, calyx::GroupInterface group)
Register value v as being evaluated when scheduling group.
std::optional< calyx::MemoryInterface > isInputPortOfMemory(Value v)
If v is an input to any memory registered within this component, returns the memory.
void addBlockArgGroup(Block *from, Block *to, calyx::GroupOp grp)
Register 'grp' as a group which performs block argument register transfer when transitioning from bas...
DenseMap< Block *, DenseMap< unsigned, calyx::RegisterOp > > blockArgRegs
A mapping from blocks to block argument registers.
DenseMap< unsigned, calyx::RegisterOp > returnRegs
A mapping from return value indexes to return value registers.
std::map< Operation *, std::string > opNames
A mapping from Operations and previously assigned unique name of the op.
LogicalResult partiallyLowerFuncToComp(mlir::func::FuncOp funcOp, PatternRewriter &rewriter) const override
calyx::ComponentOp getComponent() const
Returns the component operation associated with the currently executing partial lowering.
ComponentLoweringStateInterface * componentLoweringState
DenseMap< mlir::func::FuncOp, calyx::ComponentOp > & functionMapping
virtual LogicalResult partiallyLowerFuncToComp(mlir::func::FuncOp funcOp, PatternRewriter &rewriter) const =0
CalyxLoweringState & loweringState() const
Return the calyx lowering state for this pattern.
FuncOpPartialLoweringPattern(MLIRContext *context, LogicalResult &resRef, PatternApplicationState &patternState, DenseMap< mlir::func::FuncOp, calyx::ComponentOp > &map, calyx::CalyxLoweringState &state)
LogicalResult partiallyLower(mlir::func::FuncOp funcOp, PatternRewriter &rewriter) const override final
Entry point to initialize the state of this class and conduct the partial lowering.
LogicalResult partiallyLower(calyx::GroupInterface originGroup, PatternRewriter &rewriter) const override
InlineCombGroups(MLIRContext *context, LogicalResult &resRef, PatternApplicationState &patternState, calyx::CalyxLoweringState &pls)
void recurseInlineCombGroups(PatternRewriter &rewriter, ComponentLoweringStateInterface &state, llvm::SmallSetVector< Operation *, 8 > &inlinedGroups, calyx::GroupInterface originGroup, calyx::GroupInterface recGroup, bool doInline) const
calyx::CalyxLoweringState & cls
Base class for partial lowering passes.
calyx::CalyxLoweringState & cls
LogicalResult partiallyLower(calyx::AssignOp assignOp, PatternRewriter &rewriter) const override
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
void appendPortsForExternalMemref(PatternRewriter &rewriter, StringRef memName, Value memref, unsigned memoryID, SmallVectorImpl< calyx::PortInfo > &inPorts, SmallVectorImpl< calyx::PortInfo > &outPorts)
static constexpr std::string_view donePort
Definition: CalyxOps.h:32
void buildAssignmentsForRegisterWrite(OpBuilder &builder, calyx::GroupOp groupOp, calyx::ComponentOp componentOp, calyx::RegisterOp &reg, Value inputValue)
Creates register assignment operations within the provided groupOp.
DenseMap< const mlir::RewritePattern *, SmallPtrSet< Operation *, 16 > > PatternApplicationState
Extra state that is passed to all PartialLoweringPatterns so they can record when they have run on an...
PredicateInfo getPredicateInfo(mlir::arith::CmpFPredicate pred)
bool isControlLeafNode(Operation *op)
Type normalizeType(OpBuilder &builder, Type type)
LogicalResult applyModuleOpConversion(mlir::ModuleOp, StringRef topLevelFunction)
Helper to update the top-level ModuleOp to set the entrypoing function.
static constexpr std::string_view resetPort
Definition: CalyxOps.h:33
WalkResult getCiderSourceLocationMetadata(calyx::ComponentOp component, SmallVectorImpl< Attribute > &sourceLocations)
bool matchConstantOp(Operation *op, APInt &value)
unsigned handleZeroWidth(int64_t dim)
hw::ConstantOp createConstant(Location loc, OpBuilder &builder, ComponentOp component, size_t width, size_t value)
A helper function to create constants in the HW dialect.
calyx::RegisterOp createRegister(Location loc, OpBuilder &builder, ComponentOp component, size_t width, Twine prefix)
Creates a RegisterOp, with input and output port bit widths defined by width.
bool noStoresToMemory(Value memoryReference)
Value getComponentOutput(calyx::ComponentOp compOp, unsigned outPortIdx)
bool singleLoadFromMemory(Value memoryReference)
calyx::InstanceOp createInstance(Location loc, OpBuilder &builder, ComponentOp component, SmallVectorImpl< Type > &resultTypes, StringRef instanceName, StringRef componentName)
A helper function to create calyx.instance operation.
Type toBitVector(T type)
Performs a bit cast from a non-signless integer type value, such as a floating point value,...
static constexpr std::string_view goPort
Definition: CalyxOps.h:31
std::string getInstanceName(mlir::func::CallOp callOp)
A helper function to get the instance name.
evaluator::ObjectValue Object
Definition: Evaluator.h:411
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition: seq.py:21
LogicalResult matchAndRewrite(calyx::CombGroupOp combGroupOp, PatternRewriter &rewriter) const override
std::optional< Value > contentEnOpt()
std::variant< calyx::MemoryOp, calyx::SeqMemoryOp, MemoryPortsImpl > impl
std::optional< Value > writeDataOpt()
std::optional< Value > readEnOpt()
std::optional< Value > readDataOpt()
std::optional< Value > doneOpt()
std::optional< Value > writeEnOpt()
std::optional< Value > readOrContentEn
std::optional< Value > writeEn
std::optional< bool > isContentEn
LogicalResult matchAndRewrite(calyx::GroupOp groupOp, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(calyx::GroupDoneOp groupDoneOp, PatternRewriter &) const override
This holds information about the port for either a Component or Cell.
Definition: CalyxOps.h:89
Predicate information for the floating point comparisons.
SmallVector< InputPorts > inputPorts