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