Loading [MathJax]/extensions/tex2jax.js
CIRCT 21.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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
25using namespace llvm;
26using namespace mlir;
27using namespace mlir::arith;
28
29namespace circt {
30namespace calyx {
31
32template <typename OpTy>
33static LogicalResult deduplicateParallelOperation(OpTy parOp,
34 PatternRewriter &rewriter) {
35 auto *body = parOp.getBodyBlock();
36 if (body->getOperations().size() < 2)
37 return failure();
38
39 LogicalResult result = LogicalResult::failure();
40 SetVector<StringRef> members;
41 for (auto &op : make_early_inc_range(*body)) {
42 auto enableOp = dyn_cast<EnableOp>(&op);
43 if (enableOp == nullptr)
44 continue;
45 bool inserted = members.insert(enableOp.getGroupName());
46 if (!inserted) {
47 rewriter.eraseOp(enableOp);
48 result = LogicalResult::success();
49 }
50 }
51 return result;
52}
53
54void appendPortsForExternalMemref(PatternRewriter &rewriter, StringRef memName,
55 Value memref, unsigned memoryID,
56 SmallVectorImpl<calyx::PortInfo> &inPorts,
57 SmallVectorImpl<calyx::PortInfo> &outPorts) {
58 MemRefType memrefType = cast<MemRefType>(memref.getType());
59
60 // Ports constituting a memory interface are added a set of attributes under
61 // a "mem : {...}" dictionary. These attributes allows for deducing which
62 // top-level I/O signals constitutes a unique memory interface.
63 auto getMemoryInterfaceAttr = [&](StringRef tag,
64 std::optional<unsigned> addrIdx = {}) {
65 auto attrs = SmallVector<NamedAttribute>{
66 // "id" denotes a unique memory interface.
67 rewriter.getNamedAttr("id", rewriter.getI32IntegerAttr(memoryID)),
68 // "tag" denotes the function of this signal.
69 rewriter.getNamedAttr("tag", rewriter.getStringAttr(tag))};
70 if (addrIdx.has_value())
71 // "addr_idx" denotes the address index of this signal, for
72 // multi-dimensional memory interfaces.
73 attrs.push_back(rewriter.getNamedAttr(
74 "addr_idx", rewriter.getI32IntegerAttr(*addrIdx)));
75
76 return rewriter.getNamedAttr("mem", rewriter.getDictionaryAttr(attrs));
77 };
78
79 // Read data
80 inPorts.push_back(calyx::PortInfo{
81 rewriter.getStringAttr(memName + "_read_data"),
82 memrefType.getElementType(), calyx::Direction::Input,
83 DictionaryAttr::get(rewriter.getContext(),
84 {getMemoryInterfaceAttr("read_data")})});
85
86 // Done
87 inPorts.push_back(
88 calyx::PortInfo{rewriter.getStringAttr(memName + "_done"),
89 rewriter.getI1Type(), calyx::Direction::Input,
90 DictionaryAttr::get(rewriter.getContext(),
91 {getMemoryInterfaceAttr("done")})});
92
93 // Write data
94 outPorts.push_back(calyx::PortInfo{
95 rewriter.getStringAttr(memName + "_write_data"),
96 memrefType.getElementType(), calyx::Direction::Output,
97 DictionaryAttr::get(rewriter.getContext(),
98 {getMemoryInterfaceAttr("write_data")})});
99
100 // Memory address outputs
101 for (auto dim : enumerate(memrefType.getShape())) {
102 outPorts.push_back(calyx::PortInfo{
103 rewriter.getStringAttr(memName + "_addr" + std::to_string(dim.index())),
104 rewriter.getIntegerType(calyx::handleZeroWidth(dim.value())),
106 DictionaryAttr::get(rewriter.getContext(),
107 {getMemoryInterfaceAttr("addr", dim.index())})});
108 }
109
110 // Write enable
111 outPorts.push_back(calyx::PortInfo{
112 rewriter.getStringAttr(memName + "_write_en"), rewriter.getI1Type(),
114 DictionaryAttr::get(rewriter.getContext(),
115 {getMemoryInterfaceAttr("write_en")})});
116}
117
118WalkResult
119getCiderSourceLocationMetadata(calyx::ComponentOp component,
120 SmallVectorImpl<Attribute> &sourceLocations) {
121 Builder builder(component->getContext());
122 return component.getControlOp().walk([&](Operation *op) {
124 return WalkResult::advance();
125
126 std::string sourceLocation;
127 llvm::raw_string_ostream os(sourceLocation);
128 op->getLoc()->print(os);
129 int64_t position = sourceLocations.size();
130 sourceLocations.push_back(
131 StringAttr::get(op->getContext(), sourceLocation));
132
133 op->setAttr("pos", builder.getI64IntegerAttr(position));
134 return WalkResult::advance();
135 });
136}
137
138bool matchConstantOp(Operation *op, APInt &value) {
139 return mlir::detail::constant_int_value_binder(&value).match(op);
140}
141
142bool singleLoadFromMemory(Value memoryReference) {
143 return llvm::count_if(memoryReference.getUses(), [](OpOperand &user) {
144 return isa<mlir::memref::LoadOp>(user.getOwner());
145 }) <= 1;
146}
147
148bool noStoresToMemory(Value memoryReference) {
149 return llvm::none_of(memoryReference.getUses(), [](OpOperand &user) {
150 return isa<mlir::memref::StoreOp>(user.getOwner());
151 });
152}
153
154Value getComponentOutput(calyx::ComponentOp compOp, unsigned outPortIdx) {
155 size_t index = compOp.getInputPortInfo().size() + outPortIdx;
156 assert(index < compOp.getNumArguments() &&
157 "Exceeded number of arguments in the Component");
158 return compOp.getArgument(index);
159}
160
161Type normalizeType(OpBuilder &builder, Type type) {
162 if (type.isIndex())
163 return builder.getI32Type();
164 if (type.isIntOrFloat())
165 return toBitVector(type);
166 return type;
167}
168
169void buildAssignmentsForRegisterWrite(OpBuilder &builder,
170 calyx::GroupOp groupOp,
171 calyx::ComponentOp componentOp,
172 calyx::RegisterOp &reg,
173 Value inputValue) {
174 mlir::IRRewriter::InsertionGuard guard(builder);
175 auto loc = inputValue.getLoc();
176 builder.setInsertionPointToEnd(groupOp.getBodyBlock());
177 builder.create<calyx::AssignOp>(loc, reg.getIn(), inputValue);
178 builder.create<calyx::AssignOp>(
179 loc, reg.getWriteEn(), createConstant(loc, builder, componentOp, 1, 1));
180 builder.create<calyx::GroupDoneOp>(loc, reg.getDone());
181}
182
183//===----------------------------------------------------------------------===//
184// MemoryInterface
185//===----------------------------------------------------------------------===//
186
189 if (ports.writeEn.has_value() && ports.readOrContentEn.has_value()) {
190 assert(ports.isContentEn.value());
191 }
192}
193MemoryInterface::MemoryInterface(calyx::MemoryOp memOp) : impl(memOp) {}
194MemoryInterface::MemoryInterface(calyx::SeqMemoryOp memOp) : impl(memOp) {}
195
197 auto readData = readDataOpt();
198 assert(readData.has_value() && "Memory does not have readData");
199 return readData.value();
200}
201
203 auto readEn = readEnOpt();
204 assert(readEn.has_value() && "Memory does not have readEn");
205 return readEn.value();
206}
207
209 auto contentEn = contentEnOpt();
210 assert(contentEn.has_value() && "Memory does not have readEn");
211 return contentEn.value();
212}
213
215 auto writeData = writeDataOpt();
216 assert(writeData.has_value() && "Memory does not have writeData");
217 return writeData.value();
218}
219
221 auto writeEn = writeEnOpt();
222 assert(writeEn.has_value() && "Memory does not have writeEn");
223 return writeEn.value();
224}
225
227 auto done = doneOpt();
228 assert(done.has_value() && "Memory does not have done");
229 return done.value();
230}
231
233 if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
234 return memOp->getName().str();
235 }
236
237 if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
238 return memOp->getName().str();
239 }
240 return std::get<MemoryPortsImpl>(impl).memName;
241}
242
243std::optional<Value> MemoryInterface::readDataOpt() {
244 if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
245 return memOp->readData();
246 }
247
248 if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
249 return memOp->readData();
250 }
251 return std::get<MemoryPortsImpl>(impl).readData;
252}
253
254std::optional<Value> MemoryInterface::readEnOpt() {
255 if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
256 return std::nullopt;
257 }
258
259 if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
260 return std::nullopt;
261 }
262
263 if (std::get<MemoryPortsImpl>(impl).readOrContentEn.has_value()) {
264 assert(std::get<MemoryPortsImpl>(impl).isContentEn.has_value());
265 assert(!std::get<MemoryPortsImpl>(impl).isContentEn.value());
266 }
267 return std::get<MemoryPortsImpl>(impl).readOrContentEn;
268}
269
270std::optional<Value> MemoryInterface::contentEnOpt() {
271 if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
272 return std::nullopt;
273 }
274
275 if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
276 return memOp->contentEn();
277 }
278
279 if (std::get<MemoryPortsImpl>(impl).readOrContentEn.has_value()) {
280 assert(std::get<MemoryPortsImpl>(impl).writeEn.has_value());
281 assert(std::get<MemoryPortsImpl>(impl).isContentEn.has_value());
282 assert(std::get<MemoryPortsImpl>(impl).isContentEn.value());
283 }
284 return std::get<MemoryPortsImpl>(impl).readOrContentEn;
285}
286
287std::optional<Value> MemoryInterface::writeDataOpt() {
288 if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
289 return memOp->writeData();
290 }
291
292 if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
293 return memOp->writeData();
294 }
295 return std::get<MemoryPortsImpl>(impl).writeData;
296}
297
298std::optional<Value> MemoryInterface::writeEnOpt() {
299 if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
300 return memOp->writeEn();
301 }
302
303 if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
304 return memOp->writeEn();
305 }
306 return std::get<MemoryPortsImpl>(impl).writeEn;
307}
308
309std::optional<Value> MemoryInterface::doneOpt() {
310 if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
311 return memOp->done();
312 }
313
314 if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
315 return memOp->done();
316 }
317 return std::get<MemoryPortsImpl>(impl).done;
318}
319
321 if (auto *memOp = std::get_if<calyx::MemoryOp>(&impl); memOp) {
322 return memOp->addrPorts();
323 }
324
325 if (auto *memOp = std::get_if<calyx::SeqMemoryOp>(&impl); memOp) {
326 return memOp->addrPorts();
327 }
328 return std::get<MemoryPortsImpl>(impl).addrPorts;
329}
330
331//===----------------------------------------------------------------------===//
332// BasicLoopInterface
333//===----------------------------------------------------------------------===//
334
336
337//===----------------------------------------------------------------------===//
338// ComponentLoweringStateInterface
339//===----------------------------------------------------------------------===//
340
342 calyx::ComponentOp component)
343 : component(component), extMemData(llvm::json::Object{}) {}
344
346
348 return component;
349}
350
352 calyx::RegisterOp reg,
353 unsigned idx) {
354 assert(blockArgRegs[block].count(idx) == 0);
355 assert(idx < block->getArguments().size());
356 blockArgRegs[block][idx] = reg;
357}
358
359const DenseMap<unsigned, calyx::RegisterOp> &
363
365 calyx::GroupOp grp) {
366 blockArgGroups[from][to].push_back(grp);
367}
368
369ArrayRef<calyx::GroupOp>
371 return blockArgGroups[from][to];
372}
373
375 std::string prefixStr = prefix.str();
376 unsigned idx = prefixIdMap[prefixStr];
377 ++prefixIdMap[prefixStr];
378 return (prefix + "_" + std::to_string(idx)).str();
379}
380
382 auto it = opNames.find(op);
383 assert(it != opNames.end() && "A unique name should have been set for op");
384 return it->second;
385}
386
388 StringRef prefix) {
389 assert(opNames.find(op) == opNames.end() &&
390 "A unique name was already set for op");
391 opNames[op] = getUniqueName(prefix);
392}
393
395 Value v, calyx::GroupInterface group) {
396 valueGroupAssigns[v] = group;
397}
398
400 unsigned idx) {
401 assert(returnRegs.count(idx) == 0 &&
402 "A register was already registered for this index");
403 returnRegs[idx] = reg;
404}
405
406calyx::RegisterOp ComponentLoweringStateInterface::getReturnReg(unsigned idx) {
407 assert(returnRegs.count(idx) && "No register registered for index!");
408 return returnRegs[idx];
409}
410
412 Value memref, const calyx::MemoryInterface &memoryInterface) {
413 assert(isa<MemRefType>(memref.getType()));
414 assert(memories.find(memref) == memories.end() &&
415 "Memory already registered for memref");
416 memories[memref] = memoryInterface;
417}
418
421 assert(isa<MemRefType>(memref.getType()));
422 auto it = memories.find(memref);
423 assert(it != memories.end() && "No memory registered for memref");
424 return it->second;
425}
426
427std::optional<calyx::MemoryInterface>
429 for (auto &memIf : memories) {
430 auto &mem = memIf.getSecond();
431 if (mem.writeEn() == v || mem.writeData() == v ||
432 llvm::any_of(mem.addrPorts(), [=](Value port) { return port == v; }))
433 return {mem};
434 }
435 return {};
436}
437
439 const DenseMap<unsigned, unsigned> &mapping) {
440 funcOpResultMapping = mapping;
441}
442
444 unsigned funcReturnIdx) {
445 auto it = funcOpResultMapping.find(funcReturnIdx);
446 assert(it != funcOpResultMapping.end() &&
447 "No component return port index recorded for the requested function "
448 "return index");
449 return it->second;
450}
451
452InstanceOp ComponentLoweringStateInterface::getInstance(StringRef calleeName) {
453 return instanceMap[calleeName];
454}
455
457 InstanceOp instanceOp) {
458 instanceMap[calleeName] = instanceOp;
459}
460
462 return seqGuardCmpLibOps.contains(op);
463}
464
468
469//===----------------------------------------------------------------------===//
470// CalyxLoweringState
471//===----------------------------------------------------------------------===//
472
474 StringRef topLevelFunction)
475 : topLevelFunction(topLevelFunction), module(module) {}
476
478 assert(module.getOperation() != nullptr);
479 return module;
480}
481
485
486std::string CalyxLoweringState::blockName(Block *b) {
487 std::string blockName = irName(*b);
488 blockName.erase(std::remove(blockName.begin(), blockName.end(), '^'),
489 blockName.end());
490 return blockName;
491}
492
493//===----------------------------------------------------------------------===//
494// ModuleOpConversion
495//===----------------------------------------------------------------------===//
496
497/// Helper to update the top-level ModuleOp to set the entrypoing function.
498LogicalResult applyModuleOpConversion(mlir::ModuleOp moduleOp,
499 StringRef topLevelFunction) {
500
501 if (moduleOp->hasAttr("calyx.entrypoint"))
502 return failure();
503
504 moduleOp->setAttr("calyx.entrypoint",
505 StringAttr::get(moduleOp.getContext(), topLevelFunction));
506 return success();
507}
508
509//===----------------------------------------------------------------------===//
510// Partial lowering patterns
511//===----------------------------------------------------------------------===//
512
514 MLIRContext *context, LogicalResult &resRef,
515 PatternApplicationState &patternState,
516 DenseMap<mlir::func::FuncOp, calyx::ComponentOp> &map,
518 : PartialLoweringPattern(context, resRef, patternState),
519 functionMapping(map), calyxLoweringState(state) {}
520
521LogicalResult
523 PatternRewriter &rewriter) const {
524 // Initialize the component op references if a calyx::ComponentOp has been
525 // created for the matched funcOp.
526 if (auto it = functionMapping.find(funcOp); it != functionMapping.end()) {
527 componentOp = it->second;
531 }
532
533 return partiallyLowerFuncToComp(funcOp, rewriter);
534}
535
538 "Component operation should be set during pattern construction");
539 return componentOp;
540}
541
545
546//===----------------------------------------------------------------------===//
547// ConvertIndexTypes
548//===----------------------------------------------------------------------===//
549
550LogicalResult
552 PatternRewriter &rewriter) const {
553 funcOp.walk([&](Block *block) {
554 for (Value arg : block->getArguments())
555 arg.setType(calyx::normalizeType(rewriter, arg.getType()));
556 });
557
558 funcOp.walk([&](Operation *op) {
559 for (Value result : op->getResults()) {
560 Type resType = result.getType();
561 if (!resType.isIndex())
562 continue;
563
564 result.setType(calyx::normalizeType(rewriter, resType));
565 auto constant = dyn_cast<mlir::arith::ConstantOp>(op);
566 if (!constant)
567 continue;
568
569 APInt value;
570 calyx::matchConstantOp(constant, value);
571 rewriter.setInsertionPoint(constant);
572 rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
573 constant, rewriter.getI32IntegerAttr(value.getSExtValue()));
574 }
575 });
576 return success();
577}
578
579//===----------------------------------------------------------------------===//
580// NonTerminatingGroupDonePattern
581//===----------------------------------------------------------------------===//
582
583LogicalResult
585 PatternRewriter &) const {
586 Block *block = groupDoneOp->getBlock();
587 if (&block->back() == groupDoneOp)
588 return failure();
589
590 groupDoneOp->moveBefore(groupDoneOp->getBlock(),
591 groupDoneOp->getBlock()->end());
592 return success();
593}
594
595//===----------------------------------------------------------------------===//
596// MultipleGroupDonePattern
597//===----------------------------------------------------------------------===//
598
599LogicalResult
601 PatternRewriter &rewriter) const {
602 auto groupDoneOps = SmallVector<calyx::GroupDoneOp>(
603 groupOp.getBodyBlock()->getOps<calyx::GroupDoneOp>());
604
605 if (groupDoneOps.size() <= 1)
606 return failure();
607
608 /// 'and' all of the calyx::GroupDoneOp's.
609 rewriter.setInsertionPointToEnd(groupDoneOps[0]->getBlock());
610 SmallVector<Value> doneOpSrcs;
611 llvm::transform(groupDoneOps, std::back_inserter(doneOpSrcs),
612 [](calyx::GroupDoneOp op) { return op.getSrc(); });
613 Value allDone = rewriter.create<comb::AndOp>(groupDoneOps.front().getLoc(),
614 doneOpSrcs, false);
615
616 /// Create a group done op with the complex expression as a guard.
617 rewriter.create<calyx::GroupDoneOp>(
618 groupOp.getLoc(),
619 rewriter.create<hw::ConstantOp>(groupOp.getLoc(), APInt(1, 1)), allDone);
620 for (auto groupDoneOp : groupDoneOps)
621 rewriter.eraseOp(groupDoneOp);
622
623 return success();
624}
625
626//===----------------------------------------------------------------------===//
627// EliminateUnusedCombGroups
628//===----------------------------------------------------------------------===//
629
630LogicalResult
631EliminateUnusedCombGroups::matchAndRewrite(calyx::CombGroupOp combGroupOp,
632 PatternRewriter &rewriter) const {
633 auto control =
634 combGroupOp->getParentOfType<calyx::ComponentOp>().getControlOp();
635 if (!SymbolTable::symbolKnownUseEmpty(combGroupOp.getSymNameAttr(), control))
636 return failure();
637
638 rewriter.eraseOp(combGroupOp);
639 return success();
640}
641
642//===----------------------------------------------------------------------===//
643// DeduplicateParallelOperations
644//===----------------------------------------------------------------------===//
645
646LogicalResult
648 PatternRewriter &rewriter) const {
649 return deduplicateParallelOperation<calyx::ParOp>(parOp, rewriter);
650}
651
652LogicalResult
654 PatternRewriter &rewriter) const {
655 return deduplicateParallelOperation<calyx::StaticParOp>(parOp, rewriter);
656}
657
658//===----------------------------------------------------------------------===//
659// InlineCombGroups
660//===----------------------------------------------------------------------===//
661
662InlineCombGroups::InlineCombGroups(MLIRContext *context, LogicalResult &resRef,
663 PatternApplicationState &patternState,
665 : PartialLoweringPattern(context, resRef, patternState), cls(cls) {}
666
667LogicalResult
668InlineCombGroups::partiallyLower(calyx::GroupInterface originGroup,
669 PatternRewriter &rewriter) const {
670 auto component = originGroup->getParentOfType<calyx::ComponentOp>();
671 ComponentLoweringStateInterface *state = cls.getState(component);
673 // Filter groups which are not part of the control schedule.
674 if (SymbolTable::symbolKnownUseEmpty(originGroup.symName(),
675 component.getControlOp()))
676 return success();
677
678 // Maintain a set of the groups which we've inlined so far. The group
679 // itself is implicitly inlined.
680 llvm::SmallSetVector<Operation *, 8> inlinedGroups;
681 inlinedGroups.insert(originGroup);
682
683 // Starting from the matched originGroup, we traverse use-def chains of
684 // combinational logic, and inline assignments from the defining
685 // combinational groups.
686 recurseInlineCombGroups(rewriter, *state, inlinedGroups, originGroup,
687 originGroup,
688 /*doInline=*/false);
689 return success();
690}
691
693 PatternRewriter &rewriter, ComponentLoweringStateInterface &state,
694 llvm::SmallSetVector<Operation *, 8> &inlinedGroups,
695 calyx::GroupInterface originGroup, calyx::GroupInterface recGroup,
696 bool doInline) const {
697 inlinedGroups.insert(recGroup);
698 for (auto assignOp : recGroup.getBody()->getOps<calyx::AssignOp>()) {
699 if (doInline) {
700 /// Inline the assignment into the originGroup.
701 auto *clonedAssignOp = rewriter.clone(*assignOp.getOperation());
702 clonedAssignOp->moveBefore(originGroup.getBody(),
703 originGroup.getBody()->end());
704 }
705 Value src = assignOp.getSrc();
706
707 // Things which stop recursive inlining (or in other words, what
708 // breaks combinational paths).
709 // - Component inputs
710 // - Register and memory reads
711 // - Constant ops (constant ops are not evaluated by any group)
712 // - Multiplication pipelines are sequential.
713 // - 'While' return values (these are registers, however, 'while'
714 // return values have at the current point of conversion not yet
715 // been rewritten to their register outputs, see comment in
716 // LateSSAReplacement)
717 if (isa<BlockArgument>(src) ||
718 isa<calyx::RegisterOp, calyx::MemoryOp, calyx::SeqMemoryOp,
719 hw::ConstantOp, mlir::arith::ConstantOp, calyx::MultPipeLibOp,
720 calyx::DivUPipeLibOp, calyx::DivSPipeLibOp, calyx::RemSPipeLibOp,
721 calyx::RemUPipeLibOp, mlir::scf::WhileOp, calyx::InstanceOp,
722 calyx::ConstantOp, calyx::AddFOpIEEE754, calyx::MulFOpIEEE754,
723 calyx::CompareFOpIEEE754, calyx::FpToIntOpIEEE754,
724 calyx::IntToFpOpIEEE754, calyx::DivSqrtOpIEEE754>(
725 src.getDefiningOp()))
726 continue;
727
728 if (state.isSeqGuardCmpLibOp(src.getDefiningOp())) {
729 continue;
730 }
731
732 auto srcCombGroup = dyn_cast<calyx::CombGroupOp>(
733 state.getEvaluatingGroup(src).getOperation());
734 if (!srcCombGroup)
735 continue;
736 if (inlinedGroups.count(srcCombGroup))
737 continue;
738
739 recurseInlineCombGroups(rewriter, state, inlinedGroups, originGroup,
740 srcCombGroup, /*doInline=*/true);
741 }
742}
743
744//===----------------------------------------------------------------------===//
745// RewriteMemoryAccesses
746//===----------------------------------------------------------------------===//
747
748LogicalResult
750 PatternRewriter &rewriter) const {
751 auto *state = cls.getState(assignOp->getParentOfType<calyx::ComponentOp>());
752
753 Value dest = assignOp.getDest();
754 if (!state->isInputPortOfMemory(dest))
755 return success();
756
757 Value src = assignOp.getSrc();
758 unsigned srcBits = src.getType().getIntOrFloatBitWidth();
759 unsigned dstBits = dest.getType().getIntOrFloatBitWidth();
760 if (srcBits == dstBits)
761 return success();
762
763 SmallVector<Type> types = {
764 rewriter.getIntegerType(srcBits),
765 rewriter.getIntegerType(dstBits),
766 };
767 mlir::Location loc = assignOp.getLoc();
768 Operation *newOp;
769 if (srcBits > dstBits)
770 newOp =
771 state->getNewLibraryOpInstance<calyx::SliceLibOp>(rewriter, loc, types);
772 else
773 newOp =
774 state->getNewLibraryOpInstance<calyx::PadLibOp>(rewriter, loc, types);
775
776 rewriter.setInsertionPoint(assignOp->getBlock(),
777 assignOp->getBlock()->begin());
778 rewriter.create<calyx::AssignOp>(assignOp->getLoc(), newOp->getResult(0),
779 src);
780 assignOp.setOperand(1, newOp->getResult(1));
781
782 return success();
783}
784
785//===----------------------------------------------------------------------===//
786// BuildBasicBlockRegs
787//===----------------------------------------------------------------------===//
788
789LogicalResult
791 PatternRewriter &rewriter) const {
792 funcOp.walk([&](Block *block) {
793 /// Do not register component input values.
794 if (block == &block->getParent()->front())
795 return;
796
797 for (auto arg : enumerate(block->getArguments())) {
798 Type argType = arg.value().getType();
799 assert(isa<IntegerType>(argType) && "unsupported block argument type");
800 unsigned width = argType.getIntOrFloatBitWidth();
801 std::string index = std::to_string(arg.index());
802 std::string name = loweringState().blockName(block) + "_arg" + index;
803 auto reg = createRegister(arg.value().getLoc(), rewriter, getComponent(),
804 width, name);
805 getState().addBlockArgReg(block, reg, arg.index());
806 arg.value().replaceAllUsesWith(reg.getOut());
807 }
808 });
809 return success();
810}
811
812//===----------------------------------------------------------------------===//
813// BuildReturnRegs
814//===----------------------------------------------------------------------===//
815
816LogicalResult
818 PatternRewriter &rewriter) const {
819
820 for (auto argType : enumerate(funcOp.getResultTypes())) {
821 auto convArgType = calyx::normalizeType(rewriter, argType.value());
822 assert((isa<IntegerType>(convArgType) || isa<FloatType>(convArgType)) &&
823 "unsupported return type");
824 std::string name = "ret_arg" + std::to_string(argType.index());
825 auto reg = createRegister(funcOp.getLoc(), rewriter, getComponent(),
826 convArgType.getIntOrFloatBitWidth(), name);
827 getState().addReturnReg(reg, argType.index());
828
829 rewriter.setInsertionPointToStart(
830 getComponent().getWiresOp().getBodyBlock());
831 rewriter.create<calyx::AssignOp>(
832 funcOp->getLoc(),
834 getComponent(), getState().getFuncOpResultMapping(argType.index())),
835 reg.getOut());
836 }
837 return success();
838}
839
840//===----------------------------------------------------------------------===//
841// BuildCallInstance
842//===----------------------------------------------------------------------===//
843
844LogicalResult
846 PatternRewriter &rewriter) const {
847 funcOp.walk([&](mlir::func::CallOp callOp) {
848 ComponentOp componentOp = getCallComponent(callOp);
849 SmallVector<Type, 8> resultTypes;
850 for (auto type : componentOp.getArgumentTypes())
851 resultTypes.push_back(type);
852 for (auto type : componentOp.getResultTypes())
853 resultTypes.push_back(type);
854 std::string instanceName = getInstanceName(callOp);
855
856 // Determines if an instance needs to be created. If the same function was
857 // called by CallOp before, it doesn't need to be created, if not, the
858 // instance is created.
859 if (!getState().getInstance(instanceName)) {
860 InstanceOp instanceOp =
861 createInstance(callOp.getLoc(), rewriter, getComponent(), resultTypes,
862 instanceName, componentOp.getName());
863 getState().addInstance(instanceName, instanceOp);
864 }
865 WalkResult::advance();
866 });
867 return success();
868}
869
870ComponentOp
871BuildCallInstance::getCallComponent(mlir::func::CallOp callOp) const {
872 std::string callee = "func_" + callOp.getCallee().str();
873 for (auto [funcOp, componentOp] : functionMapping) {
874 if (funcOp.getSymName() == callee)
875 return componentOp;
876 }
877 return nullptr;
878}
879
880PredicateInfo getPredicateInfo(CmpFPredicate pred) {
881 using CombLogic = PredicateInfo::CombLogic;
883 PredicateInfo info;
884 switch (pred) {
885 case CmpFPredicate::OEQ: {
886 info.logic = CombLogic::And;
887 info.inputPorts = {{Port::Eq, /*inverted=*/false},
888 {Port::Unordered, /*inverted=*/true}};
889 break;
890 }
891 case CmpFPredicate::OGT: {
893 info.inputPorts = {{Port::Gt, /*inverted=*/false},
894 {Port::Unordered, /*inverted=*/true}};
895 break;
896 }
897 case CmpFPredicate::OGE: {
899 info.inputPorts = {{Port::Lt, /*inverted=*/true},
900 {Port::Unordered, /*inverted=*/true}};
901 break;
902 }
903 case CmpFPredicate::OLT: {
905 info.inputPorts = {{Port::Lt, /*inverted=*/false},
906 {Port::Unordered, /*inverted=*/true}};
907 break;
908 }
909 case CmpFPredicate::OLE: {
911 info.inputPorts = {{Port::Gt, /*inverted=*/true},
912 {Port::Unordered, /*inverted=*/true}};
913 break;
914 }
915 case CmpFPredicate::ONE: {
917 info.inputPorts = {{Port::Eq, /*inverted=*/true},
918 {Port::Unordered, /*inverted=*/true}};
919 break;
920 }
921 case CmpFPredicate::ORD: {
923 info.inputPorts = {{Port::Unordered, /*inverted=*/true}};
924 break;
925 }
926 case CmpFPredicate::UEQ: {
927 info.logic = PredicateInfo::CombLogic::Or;
928 info.inputPorts = {{Port::Eq, /*inverted=*/false},
929 {Port::Unordered, /*inverted=*/false}};
930 break;
931 }
932 case CmpFPredicate::UGT: {
933 info.logic = PredicateInfo::CombLogic::Or;
934 info.inputPorts = {{Port::Gt, /*inverted=*/false},
935 {Port::Unordered, /*inverted=*/false}};
936 break;
937 }
938 case CmpFPredicate::UGE: {
939 info.logic = PredicateInfo::CombLogic::Or;
940 info.inputPorts = {{Port::Lt, /*inverted=*/true},
941 {Port::Unordered, /*inverted=*/false}};
942 break;
943 }
944 case CmpFPredicate::ULT: {
945 info.logic = PredicateInfo::CombLogic::Or;
946 info.inputPorts = {{Port::Lt, /*inverted=*/false},
947 {Port::Unordered, /*inverted=*/false}};
948 break;
949 }
950 case CmpFPredicate::ULE: {
951 info.logic = PredicateInfo::CombLogic::Or;
952 info.inputPorts = {{Port::Gt, /*inverted=*/true},
953 {Port::Unordered, /*inverted=*/false}};
954 break;
955 }
956 case CmpFPredicate::UNE: {
957 info.logic = PredicateInfo::CombLogic::Or;
958 info.inputPorts = {{Port::Eq, /*inverted=*/true},
959 {Port::Unordered, /*inverted=*/false}};
960 break;
961 }
962 case CmpFPredicate::UNO: {
964 info.inputPorts = {{Port::Unordered, /*inverted=*/false}};
965 break;
966 }
967 case CmpFPredicate::AlwaysTrue:
968 case CmpFPredicate::AlwaysFalse:
970 break;
971 }
972
973 return info;
974}
975
976bool parentIsSeqCell(const Value value) {
977 if (Operation *defOp = value.getDefiningOp()) {
978 auto cellOp = dyn_cast_or_null<calyx::CellInterface>(defOp);
979 return cellOp && !cellOp.isCombinational();
980 }
981 return false;
982}
983
984} // namespace calyx
985} // namespace circt
assert(baseType &&"element must be base type")
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)
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.
T * getState(calyx::ComponentOp op)
Returns the component lowering state associated with op.
void addSeqGuardCmpLibOp(Operation *op)
Add op if it's a compare operator that requires a register to hold the value of its sequential guard ...
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.
bool isSeqGuardCmpLibOp(Operation *op)
Returns if op is a compare operator that requires a register to hold the value of its sequential guar...
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.
DenseSet< Operation * > seqGuardCmpLibOps
A set of compare operators that require registers to hold their sequential guard computation.
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.
LogicalResult partiallyLower(calyx::AssignOp assignOp, PatternRewriter &rewriter) const override
bool parentIsSeqCell(Value value)
void appendPortsForExternalMemref(PatternRewriter &rewriter, StringRef memName, Value memref, unsigned memoryID, SmallVectorImpl< calyx::PortInfo > &inPorts, SmallVectorImpl< calyx::PortInfo > &outPorts)
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 LogicalResult deduplicateParallelOperation(OpTy parOp, PatternRewriter &rewriter)
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,...
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.
LogicalResult matchAndRewrite(calyx::ParOp parOp, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(calyx::StaticParOp parOp, PatternRewriter &rewriter) const override
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.