CIRCT 20.0.0git
Loading...
Searching...
No Matches
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
461//===----------------------------------------------------------------------===//
462// CalyxLoweringState
463//===----------------------------------------------------------------------===//
464
466 StringRef topLevelFunction)
467 : topLevelFunction(topLevelFunction), module(module) {}
468
470 assert(module.getOperation() != nullptr);
471 return module;
472}
473
477
478std::string CalyxLoweringState::blockName(Block *b) {
479 std::string blockName = irName(*b);
480 blockName.erase(std::remove(blockName.begin(), blockName.end(), '^'),
481 blockName.end());
482 return blockName;
483}
484
485//===----------------------------------------------------------------------===//
486// ModuleOpConversion
487//===----------------------------------------------------------------------===//
488
489/// Helper to update the top-level ModuleOp to set the entrypoing function.
490LogicalResult applyModuleOpConversion(mlir::ModuleOp moduleOp,
491 StringRef topLevelFunction) {
492
493 if (moduleOp->hasAttr("calyx.entrypoint"))
494 return failure();
495
496 moduleOp->setAttr("calyx.entrypoint",
497 StringAttr::get(moduleOp.getContext(), topLevelFunction));
498 return success();
499}
500
501//===----------------------------------------------------------------------===//
502// Partial lowering patterns
503//===----------------------------------------------------------------------===//
504
506 MLIRContext *context, LogicalResult &resRef,
507 PatternApplicationState &patternState,
508 DenseMap<mlir::func::FuncOp, calyx::ComponentOp> &map,
510 : PartialLoweringPattern(context, resRef, patternState),
511 functionMapping(map), calyxLoweringState(state) {}
512
513LogicalResult
515 PatternRewriter &rewriter) const {
516 // Initialize the component op references if a calyx::ComponentOp has been
517 // created for the matched funcOp.
518 if (auto it = functionMapping.find(funcOp); it != functionMapping.end()) {
519 componentOp = it->second;
523 }
524
525 return partiallyLowerFuncToComp(funcOp, rewriter);
526}
527
530 "Component operation should be set during pattern construction");
531 return componentOp;
532}
533
537
538//===----------------------------------------------------------------------===//
539// ConvertIndexTypes
540//===----------------------------------------------------------------------===//
541
542LogicalResult
544 PatternRewriter &rewriter) const {
545 funcOp.walk([&](Block *block) {
546 for (Value arg : block->getArguments())
547 arg.setType(calyx::normalizeType(rewriter, arg.getType()));
548 });
549
550 funcOp.walk([&](Operation *op) {
551 for (Value result : op->getResults()) {
552 Type resType = result.getType();
553 if (!resType.isIndex())
554 continue;
555
556 result.setType(calyx::normalizeType(rewriter, resType));
557 auto constant = dyn_cast<mlir::arith::ConstantOp>(op);
558 if (!constant)
559 continue;
560
561 APInt value;
562 calyx::matchConstantOp(constant, value);
563 rewriter.setInsertionPoint(constant);
564 rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
565 constant, rewriter.getI32IntegerAttr(value.getSExtValue()));
566 }
567 });
568 return success();
569}
570
571//===----------------------------------------------------------------------===//
572// NonTerminatingGroupDonePattern
573//===----------------------------------------------------------------------===//
574
575LogicalResult
577 PatternRewriter &) const {
578 Block *block = groupDoneOp->getBlock();
579 if (&block->back() == groupDoneOp)
580 return failure();
581
582 groupDoneOp->moveBefore(groupDoneOp->getBlock(),
583 groupDoneOp->getBlock()->end());
584 return success();
585}
586
587//===----------------------------------------------------------------------===//
588// MultipleGroupDonePattern
589//===----------------------------------------------------------------------===//
590
591LogicalResult
593 PatternRewriter &rewriter) const {
594 auto groupDoneOps = SmallVector<calyx::GroupDoneOp>(
595 groupOp.getBodyBlock()->getOps<calyx::GroupDoneOp>());
596
597 if (groupDoneOps.size() <= 1)
598 return failure();
599
600 /// 'and' all of the calyx::GroupDoneOp's.
601 rewriter.setInsertionPointToEnd(groupDoneOps[0]->getBlock());
602 SmallVector<Value> doneOpSrcs;
603 llvm::transform(groupDoneOps, std::back_inserter(doneOpSrcs),
604 [](calyx::GroupDoneOp op) { return op.getSrc(); });
605 Value allDone = rewriter.create<comb::AndOp>(groupDoneOps.front().getLoc(),
606 doneOpSrcs, false);
607
608 /// Create a group done op with the complex expression as a guard.
609 rewriter.create<calyx::GroupDoneOp>(
610 groupOp.getLoc(),
611 rewriter.create<hw::ConstantOp>(groupOp.getLoc(), APInt(1, 1)), allDone);
612 for (auto groupDoneOp : groupDoneOps)
613 rewriter.eraseOp(groupDoneOp);
614
615 return success();
616}
617
618//===----------------------------------------------------------------------===//
619// EliminateUnusedCombGroups
620//===----------------------------------------------------------------------===//
621
622LogicalResult
623EliminateUnusedCombGroups::matchAndRewrite(calyx::CombGroupOp combGroupOp,
624 PatternRewriter &rewriter) const {
625 auto control =
626 combGroupOp->getParentOfType<calyx::ComponentOp>().getControlOp();
627 if (!SymbolTable::symbolKnownUseEmpty(combGroupOp.getSymNameAttr(), control))
628 return failure();
629
630 rewriter.eraseOp(combGroupOp);
631 return success();
632}
633
634//===----------------------------------------------------------------------===//
635// DeduplicateParallelOperations
636//===----------------------------------------------------------------------===//
637
638LogicalResult
640 PatternRewriter &rewriter) const {
641 return deduplicateParallelOperation<calyx::ParOp>(parOp, rewriter);
642}
643
644LogicalResult
646 PatternRewriter &rewriter) const {
647 return deduplicateParallelOperation<calyx::StaticParOp>(parOp, rewriter);
648}
649
650//===----------------------------------------------------------------------===//
651// InlineCombGroups
652//===----------------------------------------------------------------------===//
653
654InlineCombGroups::InlineCombGroups(MLIRContext *context, LogicalResult &resRef,
655 PatternApplicationState &patternState,
657 : PartialLoweringPattern(context, resRef, patternState), cls(cls) {}
658
659LogicalResult
660InlineCombGroups::partiallyLower(calyx::GroupInterface originGroup,
661 PatternRewriter &rewriter) const {
662 auto component = originGroup->getParentOfType<calyx::ComponentOp>();
663 ComponentLoweringStateInterface *state = cls.getState(component);
664
665 // Filter groups which are not part of the control schedule.
666 if (SymbolTable::symbolKnownUseEmpty(originGroup.symName(),
667 component.getControlOp()))
668 return success();
669
670 // Maintain a set of the groups which we've inlined so far. The group
671 // itself is implicitly inlined.
672 llvm::SmallSetVector<Operation *, 8> inlinedGroups;
673 inlinedGroups.insert(originGroup);
674
675 // Starting from the matched originGroup, we traverse use-def chains of
676 // combinational logic, and inline assignments from the defining
677 // combinational groups.
678 recurseInlineCombGroups(rewriter, *state, inlinedGroups, originGroup,
679 originGroup,
680 /*doInline=*/false);
681 return success();
682}
683
685 PatternRewriter &rewriter, ComponentLoweringStateInterface &state,
686 llvm::SmallSetVector<Operation *, 8> &inlinedGroups,
687 calyx::GroupInterface originGroup, calyx::GroupInterface recGroup,
688 bool doInline) const {
689 inlinedGroups.insert(recGroup);
690 for (auto assignOp : recGroup.getBody()->getOps<calyx::AssignOp>()) {
691 if (doInline) {
692 /// Inline the assignment into the originGroup.
693 auto *clonedAssignOp = rewriter.clone(*assignOp.getOperation());
694 clonedAssignOp->moveBefore(originGroup.getBody(),
695 originGroup.getBody()->end());
696 }
697 Value src = assignOp.getSrc();
698
699 // Things which stop recursive inlining (or in other words, what
700 // breaks combinational paths).
701 // - Component inputs
702 // - Register and memory reads
703 // - Constant ops (constant ops are not evaluated by any group)
704 // - Multiplication pipelines are sequential.
705 // - 'While' return values (these are registers, however, 'while'
706 // return values have at the current point of conversion not yet
707 // been rewritten to their register outputs, see comment in
708 // LateSSAReplacement)
709 if (isa<BlockArgument>(src) ||
710 isa<calyx::RegisterOp, calyx::MemoryOp, calyx::SeqMemoryOp,
711 hw::ConstantOp, mlir::arith::ConstantOp, calyx::MultPipeLibOp,
712 calyx::DivUPipeLibOp, calyx::DivSPipeLibOp, calyx::RemSPipeLibOp,
713 calyx::RemUPipeLibOp, mlir::scf::WhileOp, calyx::InstanceOp,
714 calyx::ConstantOp, calyx::AddFOpIEEE754, calyx::MulFOpIEEE754,
715 calyx::CompareFOpIEEE754>(src.getDefiningOp()))
716 continue;
717
718 auto srcCombGroup = dyn_cast<calyx::CombGroupOp>(
719 state.getEvaluatingGroup(src).getOperation());
720 if (!srcCombGroup)
721 continue;
722 if (inlinedGroups.count(srcCombGroup))
723 continue;
724
725 recurseInlineCombGroups(rewriter, state, inlinedGroups, originGroup,
726 srcCombGroup, /*doInline=*/true);
727 }
728}
729
730//===----------------------------------------------------------------------===//
731// RewriteMemoryAccesses
732//===----------------------------------------------------------------------===//
733
734LogicalResult
736 PatternRewriter &rewriter) const {
737 auto *state = cls.getState(assignOp->getParentOfType<calyx::ComponentOp>());
738
739 Value dest = assignOp.getDest();
740 if (!state->isInputPortOfMemory(dest))
741 return success();
742
743 Value src = assignOp.getSrc();
744 unsigned srcBits = src.getType().getIntOrFloatBitWidth();
745 unsigned dstBits = dest.getType().getIntOrFloatBitWidth();
746 if (srcBits == dstBits)
747 return success();
748
749 SmallVector<Type> types = {
750 rewriter.getIntegerType(srcBits),
751 rewriter.getIntegerType(dstBits),
752 };
753 mlir::Location loc = assignOp.getLoc();
754 Operation *newOp;
755 if (srcBits > dstBits)
756 newOp =
757 state->getNewLibraryOpInstance<calyx::SliceLibOp>(rewriter, loc, types);
758 else
759 newOp =
760 state->getNewLibraryOpInstance<calyx::PadLibOp>(rewriter, loc, types);
761
762 rewriter.setInsertionPoint(assignOp->getBlock(),
763 assignOp->getBlock()->begin());
764 rewriter.create<calyx::AssignOp>(assignOp->getLoc(), newOp->getResult(0),
765 src);
766 assignOp.setOperand(1, newOp->getResult(1));
767
768 return success();
769}
770
771//===----------------------------------------------------------------------===//
772// BuildBasicBlockRegs
773//===----------------------------------------------------------------------===//
774
775LogicalResult
777 PatternRewriter &rewriter) const {
778 funcOp.walk([&](Block *block) {
779 /// Do not register component input values.
780 if (block == &block->getParent()->front())
781 return;
782
783 for (auto arg : enumerate(block->getArguments())) {
784 Type argType = arg.value().getType();
785 assert(isa<IntegerType>(argType) && "unsupported block argument type");
786 unsigned width = argType.getIntOrFloatBitWidth();
787 std::string index = std::to_string(arg.index());
788 std::string name = loweringState().blockName(block) + "_arg" + index;
789 auto reg = createRegister(arg.value().getLoc(), rewriter, getComponent(),
790 width, name);
791 getState().addBlockArgReg(block, reg, arg.index());
792 arg.value().replaceAllUsesWith(reg.getOut());
793 }
794 });
795 return success();
796}
797
798//===----------------------------------------------------------------------===//
799// BuildReturnRegs
800//===----------------------------------------------------------------------===//
801
802LogicalResult
804 PatternRewriter &rewriter) const {
805
806 for (auto argType : enumerate(funcOp.getResultTypes())) {
807 auto convArgType = calyx::normalizeType(rewriter, argType.value());
808 assert((isa<IntegerType>(convArgType) || isa<FloatType>(convArgType)) &&
809 "unsupported return type");
810 std::string name = "ret_arg" + std::to_string(argType.index());
811 auto reg = createRegister(funcOp.getLoc(), rewriter, getComponent(),
812 convArgType.getIntOrFloatBitWidth(), name);
813 getState().addReturnReg(reg, argType.index());
814
815 rewriter.setInsertionPointToStart(
816 getComponent().getWiresOp().getBodyBlock());
817 rewriter.create<calyx::AssignOp>(
818 funcOp->getLoc(),
820 getComponent(), getState().getFuncOpResultMapping(argType.index())),
821 reg.getOut());
822 }
823 return success();
824}
825
826//===----------------------------------------------------------------------===//
827// BuildCallInstance
828//===----------------------------------------------------------------------===//
829
830LogicalResult
832 PatternRewriter &rewriter) const {
833 funcOp.walk([&](mlir::func::CallOp callOp) {
834 ComponentOp componentOp = getCallComponent(callOp);
835 SmallVector<Type, 8> resultTypes;
836 for (auto type : componentOp.getArgumentTypes())
837 resultTypes.push_back(type);
838 for (auto type : componentOp.getResultTypes())
839 resultTypes.push_back(type);
840 std::string instanceName = getInstanceName(callOp);
841
842 // Determines if an instance needs to be created. If the same function was
843 // called by CallOp before, it doesn't need to be created, if not, the
844 // instance is created.
845 if (!getState().getInstance(instanceName)) {
846 InstanceOp instanceOp =
847 createInstance(callOp.getLoc(), rewriter, getComponent(), resultTypes,
848 instanceName, componentOp.getName());
849 getState().addInstance(instanceName, instanceOp);
850 }
851 WalkResult::advance();
852 });
853 return success();
854}
855
856ComponentOp
857BuildCallInstance::getCallComponent(mlir::func::CallOp callOp) const {
858 std::string callee = "func_" + callOp.getCallee().str();
859 for (auto [funcOp, componentOp] : functionMapping) {
860 if (funcOp.getSymName() == callee)
861 return componentOp;
862 }
863 return nullptr;
864}
865
866PredicateInfo getPredicateInfo(CmpFPredicate pred) {
867 using CombLogic = PredicateInfo::CombLogic;
869 PredicateInfo info;
870 switch (pred) {
871 case CmpFPredicate::OEQ: {
872 info.logic = CombLogic::And;
873 info.inputPorts = {{Port::Eq, /*inverted=*/false},
874 {Port::Unordered, /*inverted=*/true}};
875 break;
876 }
877 case CmpFPredicate::OGT: {
879 info.inputPorts = {{Port::Gt, /*inverted=*/false},
880 {Port::Unordered, /*inverted=*/true}};
881 break;
882 }
883 case CmpFPredicate::OGE: {
885 info.inputPorts = {{Port::Lt, /*inverted=*/true},
886 {Port::Unordered, /*inverted=*/true}};
887 break;
888 }
889 case CmpFPredicate::OLT: {
891 info.inputPorts = {{Port::Lt, /*inverted=*/false},
892 {Port::Unordered, /*inverted=*/true}};
893 break;
894 }
895 case CmpFPredicate::OLE: {
897 info.inputPorts = {{Port::Gt, /*inverted=*/true},
898 {Port::Unordered, /*inverted=*/true}};
899 break;
900 }
901 case CmpFPredicate::ONE: {
903 info.inputPorts = {{Port::Eq, /*inverted=*/true},
904 {Port::Unordered, /*inverted=*/true}};
905 break;
906 }
907 case CmpFPredicate::ORD: {
909 info.inputPorts = {{Port::Unordered, /*inverted=*/true}};
910 break;
911 }
912 case CmpFPredicate::UEQ: {
914 info.inputPorts = {{Port::Eq, /*inverted=*/false},
915 {Port::Unordered, /*inverted=*/false}};
916 break;
917 }
918 case CmpFPredicate::UGT: {
920 info.inputPorts = {{Port::Gt, /*inverted=*/false},
921 {Port::Unordered, /*inverted=*/false}};
922 break;
923 }
924 case CmpFPredicate::UGE: {
926 info.inputPorts = {{Port::Lt, /*inverted=*/true},
927 {Port::Unordered, /*inverted=*/false}};
928 break;
929 }
930 case CmpFPredicate::ULT: {
932 info.inputPorts = {{Port::Lt, /*inverted=*/false},
933 {Port::Unordered, /*inverted=*/false}};
934 break;
935 }
936 case CmpFPredicate::ULE: {
938 info.inputPorts = {{Port::Gt, /*inverted=*/true},
939 {Port::Unordered, /*inverted=*/false}};
940 break;
941 }
942 case CmpFPredicate::UNE: {
944 info.inputPorts = {{Port::Eq, /*inverted=*/true},
945 {Port::Unordered, /*inverted=*/false}};
946 break;
947 }
948 case CmpFPredicate::UNO: {
950 info.inputPorts = {{Port::Unordered, /*inverted=*/false}};
951 break;
952 }
953 case CmpFPredicate::AlwaysTrue:
954 case CmpFPredicate::AlwaysFalse:
956 break;
957 }
958
959 return info;
960}
961
962} // namespace calyx
963} // 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.
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.
LogicalResult partiallyLower(calyx::AssignOp assignOp, PatternRewriter &rewriter) const override
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.
SmallVector< InputPorts > inputPorts