CIRCT  19.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 convIndexType(OpBuilder &builder, Type type) {
140  if (type.isIndex())
141  return builder.getI32Type();
142  return type;
143 }
144 
146  calyx::GroupOp groupOp,
147  calyx::ComponentOp componentOp,
148  calyx::RegisterOp &reg,
149  Value inputValue) {
150  mlir::IRRewriter::InsertionGuard guard(builder);
151  auto loc = inputValue.getLoc();
152  builder.setInsertionPointToEnd(groupOp.getBodyBlock());
153  builder.create<calyx::AssignOp>(loc, reg.getIn(), inputValue);
154  builder.create<calyx::AssignOp>(
155  loc, reg.getWriteEn(), createConstant(loc, builder, componentOp, 1, 1));
156  builder.create<calyx::GroupDoneOp>(loc, reg.getDone());
157 }
158 
159 //===----------------------------------------------------------------------===//
160 // MemoryInterface
161 //===----------------------------------------------------------------------===//
162 
163 MemoryInterface::MemoryInterface() = default;
164 MemoryInterface::MemoryInterface(const MemoryPortsImpl &ports) : impl(ports) {
165  if (ports.writeEn.has_value() && ports.readOrContentEn.has_value()) {
166  assert(ports.isContentEn.value());
167  }
168 }
169 MemoryInterface::MemoryInterface(calyx::MemoryOp memOp) : impl(memOp) {}
170 MemoryInterface::MemoryInterface(calyx::SeqMemoryOp memOp) : impl(memOp) {}
171 
173  auto readData = readDataOpt();
174  assert(readData.has_value() && "Memory does not have readData");
175  return readData.value();
176 }
177 
179  auto readEn = readEnOpt();
180  assert(readEn.has_value() && "Memory does not have readEn");
181  return readEn.value();
182 }
183 
185  auto contentEn = contentEnOpt();
186  assert(contentEn.has_value() && "Memory does not have readEn");
187  return contentEn.value();
188 }
189 
191  auto writeData = writeDataOpt();
192  assert(writeData.has_value() && "Memory does not have writeData");
193  return writeData.value();
194 }
195 
197  auto writeEn = writeEnOpt();
198  assert(writeEn.has_value() && "Memory does not have writeEn");
199  return writeEn.value();
200 }
201 
203  auto done = doneOpt();
204  assert(done.has_value() && "Memory does not have done");
205  return done.value();
206 }
207 
208 std::optional<Value> MemoryInterface::readDataOpt() {
209  if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
210  return memOp->readData();
211  }
212 
213  if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
214  return memOp->readData();
215  }
216  return std::get<MemoryPortsImpl>(impl).readData;
217 }
218 
219 std::optional<Value> MemoryInterface::readEnOpt() {
220  if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
221  return std::nullopt;
222  }
223 
224  if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
225  return std::nullopt;
226  }
227 
228  if (std::get<MemoryPortsImpl>(impl).readOrContentEn.has_value()) {
229  assert(std::get<MemoryPortsImpl>(impl).isContentEn.has_value());
230  assert(!std::get<MemoryPortsImpl>(impl).isContentEn.value());
231  }
232  return std::get<MemoryPortsImpl>(impl).readOrContentEn;
233 }
234 
235 std::optional<Value> MemoryInterface::contentEnOpt() {
236  if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
237  return std::nullopt;
238  }
239 
240  if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
241  return memOp->contentEn();
242  }
243 
244  if (std::get<MemoryPortsImpl>(impl).readOrContentEn.has_value()) {
245  assert(std::get<MemoryPortsImpl>(impl).writeEn.has_value());
246  assert(std::get<MemoryPortsImpl>(impl).isContentEn.has_value());
247  assert(std::get<MemoryPortsImpl>(impl).isContentEn.value());
248  }
249  return std::get<MemoryPortsImpl>(impl).readOrContentEn;
250 }
251 
252 std::optional<Value> MemoryInterface::writeDataOpt() {
253  if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
254  return memOp->writeData();
255  }
256 
257  if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
258  return memOp->writeData();
259  }
260  return std::get<MemoryPortsImpl>(impl).writeData;
261 }
262 
263 std::optional<Value> MemoryInterface::writeEnOpt() {
264  if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
265  return memOp->writeEn();
266  }
267 
268  if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
269  return memOp->writeEn();
270  }
271  return std::get<MemoryPortsImpl>(impl).writeEn;
272 }
273 
274 std::optional<Value> MemoryInterface::doneOpt() {
275  if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
276  return memOp->done();
277  }
278 
279  if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
280  return memOp->done();
281  }
282  return std::get<MemoryPortsImpl>(impl).done;
283 }
284 
286  if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
287  return memOp->addrPorts();
288  }
289 
290  if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
291  return memOp->addrPorts();
292  }
293  return std::get<MemoryPortsImpl>(impl).addrPorts;
294 }
295 
296 //===----------------------------------------------------------------------===//
297 // BasicLoopInterface
298 //===----------------------------------------------------------------------===//
299 
301 
302 //===----------------------------------------------------------------------===//
303 // ComponentLoweringStateInterface
304 //===----------------------------------------------------------------------===//
305 
307  calyx::ComponentOp component)
308  : component(component) {}
309 
311 
313  return component;
314 }
315 
317  calyx::RegisterOp reg,
318  unsigned idx) {
319  assert(blockArgRegs[block].count(idx) == 0);
320  assert(idx < block->getArguments().size());
321  blockArgRegs[block][idx] = reg;
322 }
323 
324 const DenseMap<unsigned, calyx::RegisterOp> &
326  return blockArgRegs[block];
327 }
328 
330  calyx::GroupOp grp) {
331  blockArgGroups[from][to].push_back(grp);
332 }
333 
334 ArrayRef<calyx::GroupOp>
336  return blockArgGroups[from][to];
337 }
338 
339 std::string ComponentLoweringStateInterface::getUniqueName(StringRef prefix) {
340  std::string prefixStr = prefix.str();
341  unsigned idx = prefixIdMap[prefixStr];
342  ++prefixIdMap[prefixStr];
343  return (prefix + "_" + std::to_string(idx)).str();
344 }
345 
347  auto it = opNames.find(op);
348  assert(it != opNames.end() && "A unique name should have been set for op");
349  return it->second;
350 }
351 
353  StringRef prefix) {
354  assert(opNames.find(op) == opNames.end() &&
355  "A unique name was already set for op");
356  opNames[op] = getUniqueName(prefix);
357 }
358 
360  Value v, calyx::GroupInterface group) {
361  valueGroupAssigns[v] = group;
362 }
363 
365  unsigned idx) {
366  assert(returnRegs.count(idx) == 0 &&
367  "A register was already registered for this index");
368  returnRegs[idx] = reg;
369 }
370 
371 calyx::RegisterOp ComponentLoweringStateInterface::getReturnReg(unsigned idx) {
372  assert(returnRegs.count(idx) && "No register registered for index!");
373  return returnRegs[idx];
374 }
375 
377  Value memref, const calyx::MemoryInterface &memoryInterface) {
378  assert(isa<MemRefType>(memref.getType()));
379  assert(memories.find(memref) == memories.end() &&
380  "Memory already registered for memref");
381  memories[memref] = memoryInterface;
382 }
383 
386  assert(isa<MemRefType>(memref.getType()));
387  auto it = memories.find(memref);
388  assert(it != memories.end() && "No memory registered for memref");
389  return it->second;
390 }
391 
392 std::optional<calyx::MemoryInterface>
394  for (auto &memIf : memories) {
395  auto &mem = memIf.getSecond();
396  if (mem.writeEn() == v || mem.writeData() == v ||
397  llvm::any_of(mem.addrPorts(), [=](Value port) { return port == v; }))
398  return {mem};
399  }
400  return {};
401 }
402 
404  const DenseMap<unsigned, unsigned> &mapping) {
405  funcOpResultMapping = mapping;
406 }
407 
409  unsigned funcReturnIdx) {
410  auto it = funcOpResultMapping.find(funcReturnIdx);
411  assert(it != funcOpResultMapping.end() &&
412  "No component return port index recorded for the requested function "
413  "return index");
414  return it->second;
415 }
416 
417 InstanceOp ComponentLoweringStateInterface::getInstance(StringRef calleeName) {
418  return instanceMap[calleeName];
419 }
420 
422  InstanceOp instanceOp) {
423  instanceMap[calleeName] = instanceOp;
424 }
425 
426 //===----------------------------------------------------------------------===//
427 // CalyxLoweringState
428 //===----------------------------------------------------------------------===//
429 
431  StringRef topLevelFunction)
432  : topLevelFunction(topLevelFunction), module(module) {}
433 
435  assert(module.getOperation() != nullptr);
436  return module;
437 }
438 
440  return topLevelFunction;
441 }
442 
443 std::string CalyxLoweringState::blockName(Block *b) {
444  std::string blockName = irName(*b);
445  blockName.erase(std::remove(blockName.begin(), blockName.end(), '^'),
446  blockName.end());
447  return blockName;
448 }
449 
450 //===----------------------------------------------------------------------===//
451 // ModuleOpConversion
452 //===----------------------------------------------------------------------===//
453 
454 /// Helper to update the top-level ModuleOp to set the entrypoing function.
455 LogicalResult applyModuleOpConversion(mlir::ModuleOp moduleOp,
456  StringRef topLevelFunction) {
457 
458  if (moduleOp->hasAttr("calyx.entrypoint"))
459  return failure();
460 
461  moduleOp->setAttr("calyx.entrypoint",
462  StringAttr::get(moduleOp.getContext(), topLevelFunction));
463  return success();
464 }
465 
466 //===----------------------------------------------------------------------===//
467 // Partial lowering patterns
468 //===----------------------------------------------------------------------===//
469 
471  MLIRContext *context, LogicalResult &resRef,
472  PatternApplicationState &patternState,
473  DenseMap<mlir::func::FuncOp, calyx::ComponentOp> &map,
475  : PartialLoweringPattern(context, resRef, patternState),
476  functionMapping(map), calyxLoweringState(state) {}
477 
478 LogicalResult
480  PatternRewriter &rewriter) const {
481  // Initialize the component op references if a calyx::ComponentOp has been
482  // created for the matched funcOp.
483  if (auto it = functionMapping.find(funcOp); it != functionMapping.end()) {
484  componentOp = it->second;
487  componentOp);
488  }
489 
490  return partiallyLowerFuncToComp(funcOp, rewriter);
491 }
492 
493 calyx::ComponentOp FuncOpPartialLoweringPattern::getComponent() const {
495  "Component operation should be set during pattern construction");
496  return componentOp;
497 }
498 
500  return calyxLoweringState;
501 }
502 
503 //===----------------------------------------------------------------------===//
504 // ConvertIndexTypes
505 //===----------------------------------------------------------------------===//
506 
507 LogicalResult
509  PatternRewriter &rewriter) const {
510  funcOp.walk([&](Block *block) {
511  for (Value arg : block->getArguments())
512  arg.setType(calyx::convIndexType(rewriter, arg.getType()));
513  });
514 
515  funcOp.walk([&](Operation *op) {
516  for (Value result : op->getResults()) {
517  Type resType = result.getType();
518  if (!resType.isIndex())
519  continue;
520 
521  result.setType(calyx::convIndexType(rewriter, resType));
522  auto constant = dyn_cast<mlir::arith::ConstantOp>(op);
523  if (!constant)
524  continue;
525 
526  APInt value;
527  calyx::matchConstantOp(constant, value);
528  rewriter.setInsertionPoint(constant);
529  rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
530  constant, rewriter.getI32IntegerAttr(value.getSExtValue()));
531  }
532  });
533  return success();
534 }
535 
536 //===----------------------------------------------------------------------===//
537 // NonTerminatingGroupDonePattern
538 //===----------------------------------------------------------------------===//
539 
540 LogicalResult
541 NonTerminatingGroupDonePattern::matchAndRewrite(calyx::GroupDoneOp groupDoneOp,
542  PatternRewriter &) const {
543  Block *block = groupDoneOp->getBlock();
544  if (&block->back() == groupDoneOp)
545  return failure();
546 
547  groupDoneOp->moveBefore(groupDoneOp->getBlock(),
548  groupDoneOp->getBlock()->end());
549  return success();
550 }
551 
552 //===----------------------------------------------------------------------===//
553 // MultipleGroupDonePattern
554 //===----------------------------------------------------------------------===//
555 
556 LogicalResult
558  PatternRewriter &rewriter) const {
559  auto groupDoneOps = SmallVector<calyx::GroupDoneOp>(
560  groupOp.getBodyBlock()->getOps<calyx::GroupDoneOp>());
561 
562  if (groupDoneOps.size() <= 1)
563  return failure();
564 
565  /// 'and' all of the calyx::GroupDoneOp's.
566  rewriter.setInsertionPointToEnd(groupDoneOps[0]->getBlock());
567  SmallVector<Value> doneOpSrcs;
568  llvm::transform(groupDoneOps, std::back_inserter(doneOpSrcs),
569  [](calyx::GroupDoneOp op) { return op.getSrc(); });
570  Value allDone = rewriter.create<comb::AndOp>(groupDoneOps.front().getLoc(),
571  doneOpSrcs, false);
572 
573  /// Create a group done op with the complex expression as a guard.
574  rewriter.create<calyx::GroupDoneOp>(
575  groupOp.getLoc(),
576  rewriter.create<hw::ConstantOp>(groupOp.getLoc(), APInt(1, 1)), allDone);
577  for (auto groupDoneOp : groupDoneOps)
578  rewriter.eraseOp(groupDoneOp);
579 
580  return success();
581 }
582 
583 //===----------------------------------------------------------------------===//
584 // EliminateUnusedCombGroups
585 //===----------------------------------------------------------------------===//
586 
587 LogicalResult
588 EliminateUnusedCombGroups::matchAndRewrite(calyx::CombGroupOp combGroupOp,
589  PatternRewriter &rewriter) const {
590  auto control =
591  combGroupOp->getParentOfType<calyx::ComponentOp>().getControlOp();
592  if (!SymbolTable::symbolKnownUseEmpty(combGroupOp.getSymNameAttr(), control))
593  return failure();
594 
595  rewriter.eraseOp(combGroupOp);
596  return success();
597 }
598 
599 //===----------------------------------------------------------------------===//
600 // InlineCombGroups
601 //===----------------------------------------------------------------------===//
602 
603 InlineCombGroups::InlineCombGroups(MLIRContext *context, LogicalResult &resRef,
604  PatternApplicationState &patternState,
606  : PartialLoweringPattern(context, resRef, patternState), cls(cls) {}
607 
608 LogicalResult
609 InlineCombGroups::partiallyLower(calyx::GroupInterface originGroup,
610  PatternRewriter &rewriter) const {
611  auto component = originGroup->getParentOfType<calyx::ComponentOp>();
612  ComponentLoweringStateInterface *state = cls.getState(component);
613 
614  // Filter groups which are not part of the control schedule.
615  if (SymbolTable::symbolKnownUseEmpty(originGroup.symName(),
616  component.getControlOp()))
617  return success();
618 
619  // Maintain a set of the groups which we've inlined so far. The group
620  // itself is implicitly inlined.
621  llvm::SmallSetVector<Operation *, 8> inlinedGroups;
622  inlinedGroups.insert(originGroup);
623 
624  // Starting from the matched originGroup, we traverse use-def chains of
625  // combinational logic, and inline assignments from the defining
626  // combinational groups.
627  recurseInlineCombGroups(rewriter, *state, inlinedGroups, originGroup,
628  originGroup,
629  /*doInline=*/false);
630  return success();
631 }
632 
634  PatternRewriter &rewriter, ComponentLoweringStateInterface &state,
635  llvm::SmallSetVector<Operation *, 8> &inlinedGroups,
636  calyx::GroupInterface originGroup, calyx::GroupInterface recGroup,
637  bool doInline) const {
638  inlinedGroups.insert(recGroup);
639  for (auto assignOp : recGroup.getBody()->getOps<calyx::AssignOp>()) {
640  if (doInline) {
641  /// Inline the assignment into the originGroup.
642  auto *clonedAssignOp = rewriter.clone(*assignOp.getOperation());
643  clonedAssignOp->moveBefore(originGroup.getBody(),
644  originGroup.getBody()->end());
645  }
646  Value src = assignOp.getSrc();
647 
648  // Things which stop recursive inlining (or in other words, what
649  // breaks combinational paths).
650  // - Component inputs
651  // - Register and memory reads
652  // - Constant ops (constant ops are not evaluated by any group)
653  // - Multiplication pipelines are sequential.
654  // - 'While' return values (these are registers, however, 'while'
655  // return values have at the current point of conversion not yet
656  // been rewritten to their register outputs, see comment in
657  // LateSSAReplacement)
658  if (isa<BlockArgument>(src) ||
659  isa<calyx::RegisterOp, calyx::MemoryOp, calyx::SeqMemoryOp,
660  hw::ConstantOp, mlir::arith::ConstantOp, calyx::MultPipeLibOp,
661  calyx::DivUPipeLibOp, calyx::DivSPipeLibOp, calyx::RemSPipeLibOp,
662  calyx::RemUPipeLibOp, mlir::scf::WhileOp, calyx::InstanceOp>(
663  src.getDefiningOp()))
664  continue;
665 
666  auto srcCombGroup = dyn_cast<calyx::CombGroupOp>(
667  state.getEvaluatingGroup(src).getOperation());
668  if (!srcCombGroup)
669  continue;
670  if (inlinedGroups.count(srcCombGroup))
671  continue;
672 
673  recurseInlineCombGroups(rewriter, state, inlinedGroups, originGroup,
674  srcCombGroup, /*doInline=*/true);
675  }
676 }
677 
678 //===----------------------------------------------------------------------===//
679 // RewriteMemoryAccesses
680 //===----------------------------------------------------------------------===//
681 
682 LogicalResult
683 RewriteMemoryAccesses::partiallyLower(calyx::AssignOp assignOp,
684  PatternRewriter &rewriter) const {
685  auto *state = cls.getState(assignOp->getParentOfType<calyx::ComponentOp>());
686 
687  Value dest = assignOp.getDest();
688  if (!state->isInputPortOfMemory(dest))
689  return success();
690 
691  Value src = assignOp.getSrc();
692  unsigned srcBits = src.getType().getIntOrFloatBitWidth();
693  unsigned dstBits = dest.getType().getIntOrFloatBitWidth();
694  if (srcBits == dstBits)
695  return success();
696 
697  SmallVector<Type> types = {
698  rewriter.getIntegerType(srcBits),
699  rewriter.getIntegerType(dstBits),
700  };
701  mlir::Location loc = assignOp.getLoc();
702  Operation *newOp;
703  if (srcBits > dstBits)
704  newOp =
705  state->getNewLibraryOpInstance<calyx::SliceLibOp>(rewriter, loc, types);
706  else
707  newOp =
708  state->getNewLibraryOpInstance<calyx::PadLibOp>(rewriter, loc, types);
709 
710  rewriter.setInsertionPoint(assignOp->getBlock(),
711  assignOp->getBlock()->begin());
712  rewriter.create<calyx::AssignOp>(assignOp->getLoc(), newOp->getResult(0),
713  src);
714  assignOp.setOperand(1, newOp->getResult(1));
715 
716  return success();
717 }
718 
719 //===----------------------------------------------------------------------===//
720 // BuildBasicBlockRegs
721 //===----------------------------------------------------------------------===//
722 
723 LogicalResult
725  PatternRewriter &rewriter) const {
726  funcOp.walk([&](Block *block) {
727  /// Do not register component input values.
728  if (block == &block->getParent()->front())
729  return;
730 
731  for (auto arg : enumerate(block->getArguments())) {
732  Type argType = arg.value().getType();
733  assert(isa<IntegerType>(argType) && "unsupported block argument type");
734  unsigned width = argType.getIntOrFloatBitWidth();
735  std::string index = std::to_string(arg.index());
736  std::string name = loweringState().blockName(block) + "_arg" + index;
737  auto reg = createRegister(arg.value().getLoc(), rewriter, getComponent(),
738  width, name);
739  getState().addBlockArgReg(block, reg, arg.index());
740  arg.value().replaceAllUsesWith(reg.getOut());
741  }
742  });
743  return success();
744 }
745 
746 //===----------------------------------------------------------------------===//
747 // BuildReturnRegs
748 //===----------------------------------------------------------------------===//
749 
750 LogicalResult
752  PatternRewriter &rewriter) const {
753 
754  for (auto argType : enumerate(funcOp.getResultTypes())) {
755  auto convArgType = calyx::convIndexType(rewriter, argType.value());
756  assert(isa<IntegerType>(convArgType) && "unsupported return type");
757  unsigned width = convArgType.getIntOrFloatBitWidth();
758  std::string name = "ret_arg" + std::to_string(argType.index());
759  auto reg =
760  createRegister(funcOp.getLoc(), rewriter, getComponent(), width, name);
761  getState().addReturnReg(reg, argType.index());
762 
763  rewriter.setInsertionPointToStart(
764  getComponent().getWiresOp().getBodyBlock());
765  rewriter.create<calyx::AssignOp>(
766  funcOp->getLoc(),
768  getComponent(), getState().getFuncOpResultMapping(argType.index())),
769  reg.getOut());
770  }
771  return success();
772 }
773 
774 //===----------------------------------------------------------------------===//
775 // BuildCallInstance
776 //===----------------------------------------------------------------------===//
777 
778 LogicalResult
780  PatternRewriter &rewriter) const {
781  funcOp.walk([&](mlir::func::CallOp callOp) {
782  ComponentOp componentOp = getCallComponent(callOp);
783  SmallVector<Type, 8> resultTypes;
784  for (auto type : componentOp.getArgumentTypes())
785  resultTypes.push_back(type);
786  for (auto type : componentOp.getResultTypes())
787  resultTypes.push_back(type);
788  std::string instanceName = getInstanceName(callOp);
789 
790  // Determines if an instance needs to be created. If the same function was
791  // called by CallOp before, it doesn't need to be created, if not, the
792  // instance is created.
793  if (!getState().getInstance(instanceName)) {
794  InstanceOp instanceOp =
795  createInstance(callOp.getLoc(), rewriter, getComponent(), resultTypes,
796  instanceName, componentOp.getName());
797  getState().addInstance(instanceName, instanceOp);
798  hw::ConstantOp constantOp =
799  createConstant(callOp.getLoc(), rewriter, getComponent(), 1, 1);
800  OpBuilder::InsertionGuard g(rewriter);
801  rewriter.setInsertionPointToStart(
802  getComponent().getWiresOp().getBodyBlock());
803 
804  // Creates the group that initializes the instance.
805  calyx::GroupOp groupOp = rewriter.create<calyx::GroupOp>(
806  callOp.getLoc(), "init_" + instanceName);
807  rewriter.setInsertionPointToStart(groupOp.getBodyBlock());
808  auto portInfos = instanceOp.getReferencedComponent().getPortInfo();
809  auto results = instanceOp.getResults();
810  for (const auto &[portInfo, result] : llvm::zip(portInfos, results)) {
811  if (portInfo.hasAttribute(goPort) || portInfo.hasAttribute(resetPort))
812  rewriter.create<calyx::AssignOp>(callOp.getLoc(), result, constantOp);
813  else if (portInfo.hasAttribute(donePort))
814  rewriter.create<calyx::GroupDoneOp>(callOp.getLoc(), result);
815  }
816  }
817  WalkResult::advance();
818  });
819  return success();
820 }
821 
822 ComponentOp
823 BuildCallInstance::getCallComponent(mlir::func::CallOp callOp) const {
824  std::string callee = "func_" + callOp.getCallee().str();
825  for (auto [funcOp, componentOp] : functionMapping) {
826  if (funcOp.getSymName() == callee)
827  return componentOp;
828  }
829  return nullptr;
830 }
831 
832 } // namespace calyx
833 } // namespace circt
assert(baseType &&"element must be base type")
int32_t width
Definition: FIRRTL.cpp:36
@ Input
Definition: HW.h:35
@ Output
Definition: HW.h:35
Builder builder
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:54
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...
bool isControlLeafNode(Operation *op)
Type convIndexType(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.
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.
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:20
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:85