20#include "mlir/IR/ImplicitLocOpBuilder.h"
21#include "mlir/IR/OperationSupport.h"
22#include "mlir/Pass/Pass.h"
23#include "llvm/ADT/DenseMap.h"
27#define GEN_PASS_DEF_LOWERCHIRRTLPASS
28#include "circt/Dialect/FIRRTL/Passes.h.inc"
33using namespace firrtl;
34using namespace chirrtl;
37struct LowerCHIRRTLPass
38 :
public circt::firrtl::impl::LowerCHIRRTLPassBase<LowerCHIRRTLPass>,
46 void visitCHIRRTL(CombMemOp op);
47 void visitCHIRRTL(SeqMemOp op);
48 void visitCHIRRTL(MemoryPortOp op);
49 void visitCHIRRTL(MemoryDebugPortOp op);
50 void visitCHIRRTL(MemoryPortAccessOp op);
51 void visitExpr(SubaccessOp op);
52 void visitExpr(SubfieldOp op);
53 void visitExpr(SubindexOp op);
54 void visitStmt(ConnectOp op);
55 void visitStmt(MatchingConnectOp op);
56 void visitUnhandledOp(Operation *op);
59 void visitInvalidCHIRRTL(Operation *op) { dispatchVisitor(op); }
60 void visitUnhandledCHIRRTL(Operation *op) { visitUnhandledOp(op); }
64 Value getConst(
unsigned c) {
65 auto &value = constCache[c];
67 auto module = getOperation();
68 auto builder = OpBuilder::atBlockBegin(module.getBodyBlock());
69 auto u1Type = UIntType::get(builder.getContext(), 1);
70 value = builder.create<ConstantOp>(
module.getLoc(), u1Type, APInt(1, c));
85 void emitInvalid(ImplicitLocOpBuilder &builder, Value value);
87 MemDirAttr inferMemoryPortKind(MemoryPortOp memPort);
89 void replaceMem(Operation *op, StringRef name,
bool isSequential, RUWAttr ruw,
90 ArrayAttr annotations);
92 template <
typename OpType,
typename... T>
93 void cloneSubindexOpForMemory(OpType op, Value input, T... operands);
95 void runOnOperation()
override;
98 DenseMap<unsigned, Value> constCache;
99 DenseMap<Type, Value> invalidCache;
102 SmallVector<Operation *> opsToDelete;
107 DenseMap<Operation *, MemDirAttr> subfieldDirs;
112 DenseMap<Value, Value> rdataValues;
124 DenseMap<Value, WDataInfo> wdataValues;
131static void forEachLeaf(ImplicitLocOpBuilder &builder, Value value,
132 llvm::function_ref<
void(Value)> func) {
133 auto type = value.getType();
134 if (
auto bundleType = type_dyn_cast<BundleType>(type)) {
135 for (
size_t i = 0, e = bundleType.getNumElements(); i < e; ++i)
136 forEachLeaf(builder, builder.create<SubfieldOp>(value, i), func);
137 }
else if (
auto vectorType = type_dyn_cast<FVectorType>(type)) {
138 for (
size_t i = 0, e = vectorType.getNumElements(); i != e; ++i)
139 forEachLeaf(builder, builder.create<SubindexOp>(value, i), func);
151 [&](Value leaf) {
emitConnect(builder, leaf, value); });
156void LowerCHIRRTLPass::emitInvalid(ImplicitLocOpBuilder &builder, Value value) {
157 auto type = value.getType();
158 auto &invalid = invalidCache[type];
160 auto builder = OpBuilder::atBlockBegin(getOperation().
getBodyBlock());
161 invalid = builder.create<InvalidValueOp>(getOperation().getLoc(), type);
170 case MemDirAttr::Read:
171 return MemOp::PortKind::Read;
172 case MemDirAttr::Write:
173 return MemOp::PortKind::Write;
174 case MemDirAttr::ReadWrite:
175 return MemOp::PortKind::ReadWrite;
178 "Unhandled MemDirAttr, was the port direction not inferred?");
193MemDirAttr LowerCHIRRTLPass::inferMemoryPortKind(MemoryPortOp memPort) {
200 struct StackElement {
201 StackElement(Value value, Value::use_iterator iterator, MemDirAttr mode)
202 : value(value), iterator(iterator), mode(mode) {}
204 Value::use_iterator iterator;
208 SmallVector<StackElement> stack;
209 stack.emplace_back(memPort.getData(), memPort.getData().use_begin(),
210 memPort.getDirection());
211 MemDirAttr mode = MemDirAttr::Infer;
213 while (!stack.empty()) {
214 auto *iter = &stack.back().iterator;
215 auto end = stack.back().value.use_end();
216 stack.back().mode |= mode;
218 while (*iter != end) {
219 auto &element = stack.back();
220 auto &use = *(*iter);
221 auto *user = use.getOwner();
223 if (isa<SubindexOp, SubfieldOp>(user)) {
225 auto output = user->getResult(0);
226 stack.emplace_back(output, output.use_begin(), MemDirAttr::Infer);
227 mode = MemDirAttr::Infer;
228 iter = &stack.back().iterator;
229 end = output.use_end();
232 if (
auto subaccessOp = dyn_cast<SubaccessOp>(user)) {
236 auto input = subaccessOp.getInput();
237 if (use.get() == input) {
238 auto output = subaccessOp.getResult();
239 stack.emplace_back(output, output.use_begin(), MemDirAttr::Infer);
240 mode = MemDirAttr::Infer;
241 iter = &stack.back().iterator;
242 end = output.use_end();
246 element.mode |= MemDirAttr::Read;
247 }
else if (
auto connectOp = dyn_cast<ConnectOp>(user)) {
248 if (use.get() == connectOp.getDest()) {
249 element.mode |= MemDirAttr::Write;
251 element.mode |= MemDirAttr::Read;
253 }
else if (
auto connectOp = dyn_cast<MatchingConnectOp>(user)) {
254 if (use.get() == connectOp.getDest()) {
255 element.mode |= MemDirAttr::Write;
257 element.mode |= MemDirAttr::Read;
261 element.mode |= MemDirAttr::Read;
264 mode = stack.back().mode;
269 subfieldDirs[stack.back().value.getDefiningOp()] = mode;
276void LowerCHIRRTLPass::replaceMem(Operation *cmem, StringRef name,
277 bool isSequential, RUWAttr ruw,
278 ArrayAttr annotations) {
279 assert(isa<CombMemOp>(cmem) || isa<SeqMemOp>(cmem));
283 opsToDelete.push_back(cmem);
286 auto cmemType = type_cast<CMemoryType>(cmem->getResult(0).getType());
287 auto depth = cmemType.getNumElements();
288 auto type = cmemType.getElementType();
295 MemOp::PortKind portKind;
298 SmallVector<PortInfo, 4> ports;
299 for (
auto *user : cmem->getUsers()) {
300 MemOp::PortKind portKind;
303 if (
auto cmemoryPort = dyn_cast<MemoryPortOp>(user)) {
305 auto portDirection = inferMemoryPortKind(cmemoryPort);
310 if (portDirection == MemDirAttr::Infer)
313 portName = cmemoryPort.getNameAttr();
314 portAnnos = cmemoryPort.getAnnotationsAttr();
315 }
else if (
auto dPort = dyn_cast<MemoryDebugPortOp>(user)) {
316 portKind = MemOp::PortKind::Debug;
317 portName = dPort.getNameAttr();
318 portAnnos = dPort.getAnnotationsAttr();
320 user->emitOpError(
"unhandled user of chirrtl memory");
325 ports.push_back({portName, MemOp::getTypeForPort(depth, type, portKind),
326 portAnnos, portKind, user});
336 llvm::array_pod_sort(ports.begin(), ports.end(),
338 return lhs->name.getValue().compare(
339 rhs->name.getValue());
342 SmallVector<Attribute, 4> resultNames;
343 SmallVector<Type, 4> resultTypes;
344 SmallVector<Attribute, 4> portAnnotations;
345 for (
auto port : ports) {
346 resultNames.push_back(port.name);
347 resultTypes.push_back(port.type);
348 portAnnotations.push_back(port.annotations);
353 auto readLatency = isSequential ? 1 : 0;
354 auto writeLatency = 1;
357 ImplicitLocOpBuilder memBuilder(cmem->getLoc(), cmem);
358 auto symOp = cast<hw::InnerSymbolOpInterface>(cmem);
359 auto memory = memBuilder.create<MemOp>(
360 resultTypes, readLatency, writeLatency, depth, ruw,
361 memBuilder.getArrayAttr(resultNames), name,
362 cmem->getAttrOfType<firrtl::NameKindEnumAttr>(
"nameKind").getValue(),
363 annotations, memBuilder.getArrayAttr(portAnnotations),
364 symOp.getInnerSymAttr(),
365 cmem->getAttrOfType<firrtl::MemoryInitAttr>(
"init"),
366 cmem->getAttrOfType<StringAttr>(
"prefix"));
371 for (
unsigned i = 0, e = memory.getNumResults(); i < e; ++i) {
372 auto memoryPort = memory.getResult(i);
373 auto portKind = ports[i].portKind;
374 if (portKind == MemOp::PortKind::Debug) {
375 rdataValues[ports[i].cmemPort->getResult(0)] = memoryPort;
378 auto cmemoryPort = cast<MemoryPortOp>(ports[i].cmemPort);
379 auto cmemoryPortAccess = cmemoryPort.getAccess();
386 ImplicitLocOpBuilder portBuilder(cmemoryPortAccess.getLoc(),
388 auto address = memBuilder.create<SubfieldOp>(memoryPort,
"addr");
389 emitInvalid(memBuilder, address);
390 auto enable = memBuilder.create<SubfieldOp>(memoryPort,
"en");
392 auto clock = memBuilder.create<SubfieldOp>(memoryPort,
"clk");
393 emitInvalid(memBuilder, clock);
396 emitConnect(portBuilder, address, cmemoryPortAccess.getIndex());
398 auto useEnableInference = isSequential && portKind == MemOp::PortKind::Read;
399 auto *addressOp = cmemoryPortAccess.getIndex().getDefiningOp();
402 useEnableInference &=
403 !addressOp || isa<WireOp, NodeOp, RegOp, RegResetOp>(addressOp);
406 if (!useEnableInference)
409 emitConnect(portBuilder, clock, cmemoryPortAccess.getClock());
411 if (portKind == MemOp::PortKind::Read) {
413 auto data = memBuilder.create<SubfieldOp>(memoryPort,
"data");
414 rdataValues[cmemoryPort.getData()] =
data;
415 }
else if (portKind == MemOp::PortKind::Write) {
417 auto data = memBuilder.create<SubfieldOp>(memoryPort,
"data");
418 emitInvalid(memBuilder,
data);
419 auto mask = memBuilder.create<SubfieldOp>(memoryPort,
"mask");
420 emitInvalid(memBuilder,
mask);
426 wdataValues[cmemoryPort.getData()] = {
data,
mask,
nullptr};
427 }
else if (portKind == MemOp::PortKind::ReadWrite) {
429 auto rdata = memBuilder.create<SubfieldOp>(memoryPort,
"rdata");
430 auto wmode = memBuilder.create<SubfieldOp>(memoryPort,
"wmode");
432 auto wdata = memBuilder.create<SubfieldOp>(memoryPort,
"wdata");
433 emitInvalid(memBuilder,
wdata);
434 auto wmask = memBuilder.create<SubfieldOp>(memoryPort,
"wmask");
435 emitInvalid(memBuilder,
wmask);
442 rdataValues[cmemoryPort.getData()] =
rdata;
450 if (useEnableInference) {
451 auto *indexOp = cmemoryPortAccess.getIndex().getDefiningOp();
452 bool success =
false;
457 }
else if (isa<WireOp, RegResetOp, RegOp>(indexOp)) {
464 make_filter_range(indexOp->getUsers(), [&](Operation *op) {
465 if (auto connectOp = dyn_cast<ConnectOp>(op)) {
466 if (cmemoryPortAccess.getIndex() == connectOp.getDest())
467 return !dyn_cast_or_null<InvalidValueOp>(
468 connectOp.getSrc().getDefiningOp());
469 }
else if (
auto connectOp = dyn_cast<MatchingConnectOp>(op)) {
470 if (cmemoryPortAccess.getIndex() == connectOp.getDest())
471 return !dyn_cast_or_null<InvalidValueOp>(
472 connectOp.getSrc().getDefiningOp());
478 for (
auto *driver : drivers) {
479 ImplicitLocOpBuilder builder(driver->getLoc(), driver);
483 }
else if (isa<NodeOp>(indexOp)) {
486 ImplicitLocOpBuilder builder(indexOp->getLoc(), indexOp);
493 cmemoryPort.emitWarning(
"memory port is never enabled");
498void LowerCHIRRTLPass::visitCHIRRTL(CombMemOp combmem) {
499 replaceMem(combmem, combmem.getName(),
false,
500 RUWAttr::Undefined, combmem.getAnnotations());
503void LowerCHIRRTLPass::visitCHIRRTL(SeqMemOp seqmem) {
504 replaceMem(seqmem, seqmem.getName(),
true, seqmem.getRuw(),
505 seqmem.getAnnotations());
508void LowerCHIRRTLPass::visitCHIRRTL(MemoryPortOp memPort) {
510 opsToDelete.push_back(memPort);
513void LowerCHIRRTLPass::visitCHIRRTL(MemoryDebugPortOp memPort) {
515 opsToDelete.push_back(memPort);
518void LowerCHIRRTLPass::visitCHIRRTL(MemoryPortAccessOp memPortAccess) {
520 opsToDelete.push_back(memPortAccess);
523void LowerCHIRRTLPass::visitStmt(ConnectOp connect) {
526 auto writeIt = wdataValues.find(
connect.getDest());
527 if (writeIt != wdataValues.end()) {
528 auto writeData = writeIt->second;
529 connect.getDestMutable().assign(writeData.data);
531 ImplicitLocOpBuilder builder(
connect.getLoc(), connect);
539 auto readIt = rdataValues.find(
connect.getSrc());
540 if (readIt != rdataValues.end()) {
541 auto newSource = readIt->second;
542 connect.getSrcMutable().assign(newSource);
546void LowerCHIRRTLPass::visitStmt(MatchingConnectOp connect) {
549 auto writeIt = wdataValues.find(
connect.getDest());
550 if (writeIt != wdataValues.end()) {
551 auto writeData = writeIt->second;
552 connect.getDestMutable().assign(writeData.data);
554 ImplicitLocOpBuilder builder(
connect.getLoc(), connect);
562 auto readIt = rdataValues.find(
connect.getSrc());
563 if (readIt != rdataValues.end()) {
564 auto newSource = readIt->second;
565 connect.getSrcMutable().assign(newSource);
576template <
typename OpType,
typename... T>
577void LowerCHIRRTLPass::cloneSubindexOpForMemory(OpType op, Value input,
581 auto it = subfieldDirs.find(op);
582 if (it == subfieldDirs.end()) {
585 auto iter = rdataValues.find(input);
586 if (iter != rdataValues.end()) {
587 opsToDelete.push_back(op);
588 ImplicitLocOpBuilder builder(op->getLoc(), op);
589 rdataValues[op] = builder.create<OpType>(rdataValues[input], operands...);
598 opsToDelete.push_back(op);
600 auto direction = it->second;
601 ImplicitLocOpBuilder builder(op->getLoc(), op);
605 if (direction == MemDirAttr::Read || direction == MemDirAttr::ReadWrite) {
606 rdataValues[op] = builder.create<OpType>(rdataValues[input], operands...);
611 if (direction == MemDirAttr::Write || direction == MemDirAttr::ReadWrite) {
612 auto writeData = wdataValues[input];
613 auto write = builder.create<OpType>(writeData.data, operands...);
614 auto mask = builder.create<OpType>(writeData.mask, operands...);
615 wdataValues[op] = {write,
mask, writeData.mode};
619void LowerCHIRRTLPass::visitExpr(SubaccessOp subaccess) {
622 auto readIt = rdataValues.find(subaccess.getIndex());
623 if (readIt != rdataValues.end()) {
624 subaccess.getIndexMutable().assign(readIt->second);
627 cloneSubindexOpForMemory(subaccess, subaccess.getInput(),
628 subaccess.getIndex());
631void LowerCHIRRTLPass::visitExpr(SubfieldOp subfield) {
632 cloneSubindexOpForMemory<SubfieldOp>(subfield, subfield.getInput(),
633 subfield.getFieldIndex());
636void LowerCHIRRTLPass::visitExpr(SubindexOp subindex) {
637 cloneSubindexOpForMemory<SubindexOp>(subindex, subindex.getInput(),
638 subindex.getIndex());
641void LowerCHIRRTLPass::visitUnhandledOp(Operation *op) {
644 for (
auto &operand : op->getOpOperands()) {
645 auto it = rdataValues.find(operand.get());
646 if (it != rdataValues.end()) {
647 operand.set(it->second);
652void LowerCHIRRTLPass::runOnOperation() {
656 getOperation().getBodyBlock()->walk(
657 [&](Operation *op) { dispatchCHIRRTLVisitor(op); });
661 if (opsToDelete.empty())
662 markAllAnalysesPreserved();
665 while (!opsToDelete.empty())
666 opsToDelete.pop_back_val()->erase();
673 return std::make_unique<LowerCHIRRTLPass>();
assert(baseType &&"element must be base type")
static MemOp::PortKind memDirAttrToPortKind(MemDirAttr direction)
Converts a CHIRRTL memory port direction to a MemoryOp port type.
static void connectLeafsTo(ImplicitLocOpBuilder &builder, Value bundle, Value value)
Drive a value to all leafs of the input aggregate value.
static void forEachLeaf(ImplicitLocOpBuilder &builder, Value value, llvm::function_ref< void(Value)> func)
Performs the callback for each leaf element of a value.
static Block * getBodyBlock(FModuleLike mod)
CHIRRTLVisitor is a visitor for CHIRRTL operations.
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
connect(destination, source)
std::unique_ptr< mlir::Pass > createLowerCHIRRTLPass()
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
This holds the name and type that describes the module's ports.
AnnotationSet annotations