17#include "mlir/Analysis/TopologicalSortUtils.h"
18#include "mlir/IR/Builders.h"
19#include "mlir/IR/DialectImplementation.h"
20#include "mlir/IR/Matchers.h"
21#include "mlir/IR/PatternMatch.h"
24#include "llvm/ADT/SmallString.h"
31 auto memType = cast<seq::HLMemType>(hlmemHandle.getType());
32 auto shape = memType.getShape();
33 if (shape.size() != addresses.size())
36 for (
auto [dim, addr] : llvm::zip(shape, addresses)) {
37 auto addrType = dyn_cast<IntegerType>(addr.getType());
40 if (addrType.getIntOrFloatBitWidth() != llvm::Log2_64_Ceil(dim))
49 if (result.attributes.getNamed(
"name"))
53 StringRef resultName = parser.getResultName(0).first;
54 if (!resultName.empty() &&
isdigit(resultName[0]))
56 result.addAttribute(
"name", parser.getBuilder().getStringAttr(resultName));
60 if (!op->hasAttr(
"name"))
63 auto name = op->getAttrOfType<StringAttr>(
"name").getValue();
67 SmallString<32> resultNameStr;
68 llvm::raw_svector_ostream tmpStream(resultNameStr);
69 p.printOperand(op->getResult(0), tmpStream);
70 auto actualName = tmpStream.str().drop_front();
71 return actualName == name;
76 std::optional<OpAsmParser::UnresolvedOperand> operand,
84 Value operand, Type type) {
89 OpAsmParser &parser, Type refType,
90 std::optional<OpAsmParser::UnresolvedOperand> operand, Type &type) {
92 type = seq::ImmutableType::get(refType);
97 Type refType, Value operand,
106ParseResult ReadPortOp::parse(OpAsmParser &parser, OperationState &result) {
107 llvm::SMLoc loc = parser.getCurrentLocation();
109 OpAsmParser::UnresolvedOperand memOperand, rdenOperand;
110 bool hasRdEn =
false;
111 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> addressOperands;
112 seq::HLMemType memType;
114 if (parser.parseOperand(memOperand) ||
115 parser.parseOperandList(addressOperands, OpAsmParser::Delimiter::Square))
118 if (succeeded(parser.parseOptionalKeyword(
"rden"))) {
119 if (failed(parser.parseOperand(rdenOperand)))
124 if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
125 parser.parseType(memType))
128 llvm::SmallVector<Type> operandTypes = memType.getAddressTypes();
129 operandTypes.insert(operandTypes.begin(), memType);
131 llvm::SmallVector<OpAsmParser::UnresolvedOperand> allOperands = {memOperand};
132 llvm::copy(addressOperands, std::back_inserter(allOperands));
134 operandTypes.push_back(parser.getBuilder().getI1Type());
135 allOperands.push_back(rdenOperand);
138 if (parser.resolveOperands(allOperands, operandTypes, loc, result.operands))
141 result.addTypes(memType.getElementType());
143 llvm::SmallVector<int32_t, 2> operandSizes;
144 operandSizes.push_back(1);
145 operandSizes.push_back(addressOperands.size());
146 operandSizes.push_back(hasRdEn ? 1 : 0);
147 result.addAttribute(
"operandSegmentSizes",
148 parser.getBuilder().getDenseI32ArrayAttr(operandSizes));
152void ReadPortOp::print(OpAsmPrinter &p) {
153 p <<
" " << getMemory() <<
"[" << getAddresses() <<
"]";
155 p <<
" rden " << getRdEn();
156 p.printOptionalAttrDict((*this)->getAttrs(), {
"operandSegmentSizes"});
157 p <<
" : " << getMemory().getType();
161 auto memName = getMemory().getDefiningOp<seq::HLMemOp>().
getName();
162 setNameFn(getReadData(), (memName +
"_rdata").str());
165void ReadPortOp::build(OpBuilder &builder, OperationState &result, Value memory,
166 ValueRange addresses, Value rdEn,
unsigned latency) {
167 auto memType = cast<seq::HLMemType>(memory.getType());
168 ReadPortOp::build(builder, result, memType.getElementType(), memory,
169 addresses, rdEn, latency);
176ParseResult WritePortOp::parse(OpAsmParser &parser, OperationState &result) {
177 llvm::SMLoc loc = parser.getCurrentLocation();
178 OpAsmParser::UnresolvedOperand memOperand, dataOperand, wrenOperand;
179 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> addressOperands;
180 seq::HLMemType memType;
182 if (parser.parseOperand(memOperand) ||
183 parser.parseOperandList(addressOperands,
184 OpAsmParser::Delimiter::Square) ||
185 parser.parseOperand(dataOperand) || parser.parseKeyword(
"wren") ||
186 parser.parseOperand(wrenOperand) ||
187 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
188 parser.parseType(memType))
191 llvm::SmallVector<Type> operandTypes = memType.getAddressTypes();
192 operandTypes.insert(operandTypes.begin(), memType);
193 operandTypes.push_back(memType.getElementType());
194 operandTypes.push_back(parser.getBuilder().getI1Type());
196 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> allOperands(
198 allOperands.insert(allOperands.begin(), memOperand);
199 allOperands.push_back(dataOperand);
200 allOperands.push_back(wrenOperand);
202 if (parser.resolveOperands(allOperands, operandTypes, loc, result.operands))
208void WritePortOp::print(OpAsmPrinter &p) {
209 p <<
" " << getMemory() <<
"[" << getAddresses() <<
"] " << getInData()
210 <<
" wren " << getWrEn();
211 p.printOptionalAttrDict((*this)->getAttrs());
212 p <<
" : " << getMemory().getType();
220 setNameFn(getHandle(),
getName());
223void HLMemOp::build(OpBuilder &builder, OperationState &result, Value clk,
224 Value rst, StringRef name, llvm::ArrayRef<int64_t> shape,
226 HLMemType t = HLMemType::get(builder.getContext(), shape,
elementType);
227 HLMemOp::build(builder, result, t, clk, rst, name);
236 IntegerAttr &threshold,
237 Type &outputFlagType,
238 StringRef directive) {
240 if (succeeded(parser.parseOptionalKeyword(directive))) {
241 int64_t thresholdValue;
242 if (succeeded(parser.parseInteger(thresholdValue))) {
243 threshold = parser.getBuilder().getI64IntegerAttr(thresholdValue);
244 outputFlagType = parser.getBuilder().getI1Type();
247 return parser.emitError(parser.getNameLoc(),
248 "expected integer value after " + directive +
255 Type &outputFlagType) {
261 Type &outputFlagType) {
267 Type outputFlagType) {
270 <<
" " << threshold.getInt();
274 Type outputFlagType) {
277 <<
" " << threshold.getInt();
281 setNameFn(getOutput(),
"out");
282 setNameFn(getEmpty(),
"empty");
283 setNameFn(getFull(),
"full");
284 if (
auto ae = getAlmostEmpty())
285 setNameFn(ae,
"almostEmpty");
286 if (
auto af = getAlmostFull())
287 setNameFn(af,
"almostFull");
290LogicalResult FIFOOp::verify() {
291 auto aet = getAlmostEmptyThreshold();
292 auto aft = getAlmostFullThreshold();
293 size_t depth = getDepth();
294 if (aft.has_value() && aft.value() > depth)
295 return emitOpError(
"almost full threshold must be <= FIFO depth");
297 if (aet.has_value() && aet.value() > depth)
298 return emitOpError(
"almost empty threshold must be <= FIFO depth");
312 setNameFn(getResult(), *name);
315LogicalResult CompRegOp::verify() {
316 if ((getReset() ==
nullptr) ^ (getResetValue() ==
nullptr))
318 "either reset and resetValue or neither must be specified");
322std::optional<size_t> CompRegOp::getTargetResultIndex() {
return 0; }
324template <
typename TOp>
326 if ((op.getReset() ==
nullptr) ^ (op.getResetValue() ==
nullptr))
327 return op->emitOpError(
328 "either reset and resetValue or neither must be specified");
329 bool hasReset = op.getReset() !=
nullptr;
330 if (hasReset && op.getResetValue().getType() != op.getInput().getType())
331 return op->emitOpError(
"reset value must be the same type as the input");
341 setNameFn(getResult(), *name);
344std::optional<size_t> CompRegClockEnabledOp::getTargetResultIndex() {
348LogicalResult CompRegClockEnabledOp::verify() {
361 setNameFn(getResult(), *name);
364std::optional<size_t> ShiftRegOp::getTargetResultIndex() {
return 0; }
366LogicalResult ShiftRegOp::verify() {
376void FirRegOp::build(OpBuilder &builder, OperationState &result, Value input,
377 Value clk, StringAttr name, hw::InnerSymAttr innerSym,
380 OpBuilder::InsertionGuard guard(builder);
382 result.addOperands(input);
383 result.addOperands(clk);
385 result.addAttribute(getNameAttrName(result.name), name);
388 result.addAttribute(getInnerSymAttrName(result.name), innerSym);
391 result.addAttribute(getPresetAttrName(result.name), preset);
393 result.addTypes(input.getType());
396void FirRegOp::build(OpBuilder &builder, OperationState &result, Value input,
397 Value clk, StringAttr name, Value reset, Value resetValue,
398 hw::InnerSymAttr innerSym,
bool isAsync) {
400 OpBuilder::InsertionGuard guard(builder);
402 result.addOperands(input);
403 result.addOperands(clk);
404 result.addOperands(reset);
405 result.addOperands(resetValue);
407 result.addAttribute(getNameAttrName(result.name), name);
409 result.addAttribute(getIsAsyncAttrName(result.name), builder.getUnitAttr());
412 result.addAttribute(getInnerSymAttrName(result.name), innerSym);
414 result.addTypes(input.getType());
417ParseResult FirRegOp::parse(OpAsmParser &parser, OperationState &result) {
418 auto &builder = parser.getBuilder();
419 llvm::SMLoc loc = parser.getCurrentLocation();
421 using Op = OpAsmParser::UnresolvedOperand;
424 if (parser.parseOperand(next) || parser.parseKeyword(
"clock") ||
425 parser.parseOperand(clk))
428 if (succeeded(parser.parseOptionalKeyword(
"sym"))) {
429 hw::InnerSymAttr innerSym;
430 if (parser.parseCustomAttributeWithFallback(innerSym,
nullptr,
431 "inner_sym", result.attributes))
436 std::optional<std::pair<Op, Op>> resetAndValue;
437 if (succeeded(parser.parseOptionalKeyword(
"reset"))) {
439 if (succeeded(parser.parseOptionalKeyword(
"async")))
441 else if (succeeded(parser.parseOptionalKeyword(
"sync")))
444 return parser.emitError(loc,
"invalid reset, expected 'sync' or 'async'");
446 result.attributes.append(
"isAsync", builder.getUnitAttr());
448 resetAndValue = {{}, {}};
449 if (parser.parseOperand(resetAndValue->first) || parser.parseComma() ||
450 parser.parseOperand(resetAndValue->second))
454 std::optional<APInt> presetValue;
455 llvm::SMLoc presetValueLoc;
456 if (succeeded(parser.parseOptionalKeyword(
"preset"))) {
457 presetValueLoc = parser.getCurrentLocation();
458 OptionalParseResult presetIntResult =
459 parser.parseOptionalInteger(presetValue.emplace());
460 if (!presetIntResult.has_value() || failed(*presetIntResult))
461 return parser.emitError(loc,
"expected integer value");
465 if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
466 parser.parseType(ty))
468 result.addTypes({ty});
472 if (hw::type_isa<seq::ClockType>(ty)) {
477 return parser.emitError(presetValueLoc,
478 "cannot preset register of unknown width");
482 APInt presetResult = presetValue->sextOrTrunc(width);
483 if (presetResult.zextOrTrunc(presetValue->getBitWidth()) != *presetValue)
484 return parser.emitError(loc,
"preset value too large");
486 auto builder = parser.getBuilder();
487 auto presetTy = builder.getIntegerType(width);
488 auto resultAttr = builder.getIntegerAttr(presetTy, presetResult);
489 result.addAttribute(
"preset", resultAttr);
494 if (parser.resolveOperand(next, ty, result.operands))
497 Type clkTy = ClockType::get(result.getContext());
498 if (parser.resolveOperand(clk, clkTy, result.operands))
502 Type i1 = IntegerType::get(result.getContext(), 1);
503 if (parser.resolveOperand(resetAndValue->first, i1, result.operands) ||
504 parser.resolveOperand(resetAndValue->second, ty, result.operands))
511void FirRegOp::print(::mlir::OpAsmPrinter &p) {
512 SmallVector<StringRef> elidedAttrs = {
513 getInnerSymAttrName(), getIsAsyncAttrName(), getPresetAttrName()};
515 p <<
' ' << getNext() <<
" clock " << getClk();
517 if (
auto sym = getInnerSymAttr()) {
523 p <<
" reset " << (getIsAsync() ?
"async" :
"sync") <<
' ';
524 p << getReset() <<
", " << getResetValue();
527 if (
auto preset = getPresetAttr()) {
528 p <<
" preset " << preset.getValue();
532 elidedAttrs.push_back(
"name");
534 p.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
535 p <<
" : " << getNext().getType();
539LogicalResult FirRegOp::verify() {
540 if (getReset() || getResetValue() || getIsAsync()) {
541 if (!getReset() || !getResetValue())
542 return emitOpError(
"must specify reset and reset value");
545 return emitOpError(
"register with no reset cannot be async");
547 if (
auto preset = getPresetAttr()) {
550 if (preset.getType() != getType() && presetWidth != width)
551 return emitOpError(
"preset type width must match register type");
561 setNameFn(getResult(),
getName());
564std::optional<size_t> FirRegOp::getTargetResultIndex() {
return 0; }
566LogicalResult FirRegOp::canonicalize(FirRegOp op, PatternRewriter &rewriter) {
570 if (
auto reset = op.getReset()) {
572 if (constOp.getValue().isZero()) {
573 rewriter.replaceOpWithNewOp<FirRegOp>(
574 op, op.getNext(), op.getClk(), op.getNameAttr(),
575 op.getInnerSymAttr(), op.getPresetAttr());
582 if (op.getInnerSymAttr())
590 if (op.getNext() == op.getResult())
592 if (
auto clk = op.getClk().getDefiningOp<seq::ToClockOp>())
598 bool replaceWithConstZero =
true;
599 if (
auto preset = op.getPresetAttr())
600 if (!preset.getValue().isZero())
601 replaceWithConstZero =
false;
603 if (
isConstant() && !op.getResetValue() && replaceWithConstZero) {
604 if (isa<seq::ClockType>(op.getType())) {
605 rewriter.replaceOpWithNewOp<seq::ConstClockOp>(
606 op, seq::ClockConstAttr::get(rewriter.getContext(), ClockConst::Low));
610 rewriter.replaceOpWithNewOp<
hw::BitcastOp>(op, op.getType(), constant);
621 if (!op.getReset() && !op.getPresetAttr()) {
625 if (isa<IntegerType>(
626 hw::type_cast<hw::ArrayType>(op.getResult().getType())
627 .getElementType())) {
628 SmallVector<Value> nextOperands;
629 bool changed =
false;
630 for (
const auto &[i, value] :
631 llvm::enumerate(arrayCreate.getOperands())) {
632 auto index = arrayCreate.getOperands().size() - i - 1;
636 if (arrayGet.getInput() == op.getResult() &&
637 matchPattern(arrayGet.getIndex(),
638 m_ConstantInt(&elementIndex)) &&
639 elementIndex == index) {
646 nextOperands.push_back(value);
651 arrayCreate.getLoc(), nextOperands);
652 if (arrayCreate->hasOneUse())
655 rewriter.replaceOp(arrayCreate, newNextVal);
658 rewriter.replaceOpWithNewOp<FirRegOp>(op, newNextVal, op.getClk(),
660 op.getInnerSymAttr());
672OpFoldResult FirRegOp::fold(FoldAdaptor adaptor) {
675 if (getInnerSymAttr())
678 auto presetAttr = getPresetAttr();
688 if (
auto reset = getReset())
690 if (constOp.getValue().isOne())
691 return getResetValue();
696 bool isTrivialFeedback = (getNext() == getResult());
697 bool isNeverClocked =
698 adaptor.getClk() !=
nullptr;
699 if (!isTrivialFeedback && !isNeverClocked)
704 if (
auto resetValue = getResetValue()) {
705 if (
auto *op = resetValue.getDefiningOp()) {
706 if (op->hasTrait<OpTrait::ConstantLike>() && !presetAttr)
708 if (
auto constOp = dyn_cast<hw::ConstantOp>(op))
709 if (presetAttr.getValue() == constOp.getValue())
717 auto intType = dyn_cast<IntegerType>(getType());
723 return IntegerAttr::get(intType, 0);
730OpFoldResult ClockGateOp::fold(FoldAdaptor adaptor) {
739 return ClockConstAttr::get(getContext(), ClockConst::Low);
742 if (
auto clockAttr = dyn_cast_or_null<ClockConstAttr>(adaptor.getInput()))
743 if (clockAttr.getValue() == ClockConst::Low)
744 return ClockConstAttr::get(getContext(), ClockConst::Low);
748 auto clockGateInputOp = getInput().getDefiningOp<ClockGateOp>();
749 while (clockGateInputOp) {
750 if (clockGateInputOp.getEnable() == getEnable() &&
751 clockGateInputOp.getTestEnable() == getTestEnable())
753 clockGateInputOp = clockGateInputOp.getInput().getDefiningOp<ClockGateOp>();
759LogicalResult ClockGateOp::canonicalize(ClockGateOp op,
760 PatternRewriter &rewriter) {
762 if (
auto testEnable = op.getTestEnable()) {
764 if (constOp.getValue().isZero()) {
765 rewriter.modifyOpInPlace(op,
766 [&] { op.getTestEnableMutable().clear(); });
775std::optional<size_t> ClockGateOp::getTargetResultIndex() {
783OpFoldResult ClockMuxOp::fold(FoldAdaptor adaptor) {
785 return getTrueClock();
787 return getFalseClock();
795LogicalResult FirMemOp::canonicalize(FirMemOp op, PatternRewriter &rewriter) {
797 if (op.getInnerSymAttr())
801 for (
auto *user : op->getUsers()) {
802 if (isa<FirMemReadOp, FirMemReadWriteOp>(user))
804 assert(isa<FirMemWriteOp>(user) &&
"invalid seq.firmem user");
807 for (
auto *user :
llvm::make_early_inc_range(op->getUsers()))
808 rewriter.eraseOp(user);
810 rewriter.eraseOp(op);
815 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
816 if (!nameAttr.getValue().empty())
817 setNameFn(getResult(), nameAttr.getValue());
820std::optional<size_t> FirMemOp::getTargetResultIndex() {
return 0; }
824 if (
auto mask = op.getMask()) {
825 auto memType = op.getMemory().getType();
826 if (!memType.getMaskWidth())
827 return op.emitOpError(
"has mask operand but memory type '")
828 << memType <<
"' has no mask";
829 auto expected = IntegerType::get(op.getContext(), *memType.getMaskWidth());
830 if (mask.getType() != expected)
831 return op.emitOpError(
"has mask operand of type '")
832 << mask.getType() <<
"', but memory type requires '" << expected
839LogicalResult FirMemReadWriteOp::verify() {
return verifyFirMemMask(*
this); }
844 return value.getDefiningOp<seq::ConstClockOp>();
850 return constOp.getValue().isZero();
857 return constOp.getValue().isAllOnes();
861LogicalResult FirMemReadOp::canonicalize(FirMemReadOp op,
862 PatternRewriter &rewriter) {
865 rewriter.modifyOpInPlace(op, [&] { op.getEnableMutable().erase(0); });
871LogicalResult FirMemWriteOp::canonicalize(FirMemWriteOp op,
872 PatternRewriter &rewriter) {
876 rewriter.eraseOp(op);
879 bool anyChanges =
false;
883 rewriter.modifyOpInPlace(op, [&] { op.getEnableMutable().erase(0); });
889 rewriter.modifyOpInPlace(op, [&] { op.getMaskMutable().erase(0); });
893 return success(anyChanges);
896LogicalResult FirMemReadWriteOp::canonicalize(FirMemReadWriteOp op,
897 PatternRewriter &rewriter) {
902 auto opAttrs = op->getAttrs();
903 auto opAttrNames = op.getAttributeNames();
904 auto newOp = rewriter.replaceOpWithNewOp<FirMemReadOp>(
905 op, op.getMemory(), op.getAddress(), op.getClk(), op.getEnable());
906 for (
auto namedAttr : opAttrs)
907 if (!
llvm::is_contained(opAttrNames, namedAttr.
getName()))
908 newOp->setAttr(namedAttr.
getName(), namedAttr.getValue());
911 bool anyChanges =
false;
915 rewriter.modifyOpInPlace(op, [&] { op.getEnableMutable().erase(0); });
921 rewriter.modifyOpInPlace(op, [&] { op.getMaskMutable().erase(0); });
925 return success(anyChanges);
932OpFoldResult ConstClockOp::fold(FoldAdaptor adaptor) {
933 return ClockConstAttr::get(getContext(), getValue());
940LogicalResult ToClockOp::canonicalize(ToClockOp op, PatternRewriter &rewriter) {
941 if (
auto fromClock = op.getInput().getDefiningOp<FromClockOp>()) {
942 rewriter.replaceOp(op, fromClock.getInput());
948OpFoldResult ToClockOp::fold(FoldAdaptor adaptor) {
949 if (
auto fromClock = getInput().getDefiningOp<FromClockOp>())
950 return fromClock.getInput();
951 if (
auto intAttr = dyn_cast_or_null<IntegerAttr>(adaptor.getInput())) {
953 intAttr.getValue().isZero() ? ClockConst::Low : ClockConst::High;
954 return ClockConstAttr::get(getContext(), value);
959LogicalResult FromClockOp::canonicalize(FromClockOp op,
960 PatternRewriter &rewriter) {
961 if (
auto toClock = op.getInput().getDefiningOp<ToClockOp>()) {
962 rewriter.replaceOp(op, toClock.getInput());
968OpFoldResult FromClockOp::fold(FoldAdaptor adaptor) {
969 if (
auto toClock = getInput().getDefiningOp<ToClockOp>())
970 return toClock.getInput();
971 if (
auto clockAttr = dyn_cast_or_null<ClockConstAttr>(adaptor.getInput())) {
972 auto ty = IntegerType::get(getContext(), 1);
973 return IntegerAttr::get(ty, clockAttr.getValue() == ClockConst::High);
982OpFoldResult ClockInverterOp::fold(FoldAdaptor adaptor) {
983 if (
auto chainedInv = getInput().getDefiningOp<ClockInverterOp>())
984 return chainedInv.getInput();
985 if (
auto clockAttr = dyn_cast_or_null<ClockConstAttr>(adaptor.getInput())) {
986 auto clockIn = clockAttr.getValue() == ClockConst::High;
987 return ClockConstAttr::get(getContext(),
988 clockIn ? ClockConst::Low : ClockConst::High);
998 depth = op->getAttrOfType<IntegerAttr>(
"depth").
getInt();
999 numReadPorts = op->getAttrOfType<IntegerAttr>(
"numReadPorts").getUInt();
1000 numWritePorts = op->getAttrOfType<IntegerAttr>(
"numWritePorts").getUInt();
1002 op->getAttrOfType<IntegerAttr>(
"numReadWritePorts").getUInt();
1003 readLatency = op->getAttrOfType<IntegerAttr>(
"readLatency").getUInt();
1004 writeLatency = op->getAttrOfType<IntegerAttr>(
"writeLatency").getUInt();
1005 dataWidth = op->getAttrOfType<IntegerAttr>(
"width").getUInt();
1006 if (op->hasAttrOfType<IntegerAttr>(
"maskGran"))
1007 maskGran = op->getAttrOfType<IntegerAttr>(
"maskGran").getUInt();
1009 maskGran = dataWidth;
1010 readUnderWrite = op->getAttrOfType<seq::RUWAttr>(
"readUnderWrite").getValue();
1012 op->getAttrOfType<seq::WUWAttr>(
"writeUnderWrite").getValue();
1013 if (
auto clockIDsAttr = op->getAttrOfType<ArrayAttr>(
"writeClockIDs"))
1014 for (
auto clockID : clockIDsAttr)
1015 writeClockIDs.push_back(
1016 cast<IntegerAttr>(clockID).getValue().getZExtValue());
1017 initFilename = op->getAttrOfType<StringAttr>(
"initFilename").getValue();
1018 initIsBinary = op->getAttrOfType<BoolAttr>(
"initIsBinary").getValue();
1019 initIsInline = op->getAttrOfType<BoolAttr>(
"initIsInline").getValue();
1022LogicalResult InitialOp::verify() {
1024 auto *terminator = this->getBody().front().getTerminator();
1025 if (terminator->getOperands().size() != getNumResults())
1026 return emitError() <<
"result type doesn't match with the terminator";
1027 for (
auto [lhs, rhs] :
1028 llvm::zip(terminator->getOperands().getTypes(), getResultTypes())) {
1029 if (cast<seq::ImmutableType>(rhs).getInnerType() != lhs)
1030 return emitError() << cast<seq::ImmutableType>(rhs).getInnerType()
1031 <<
" is expected but got " << lhs;
1034 auto blockArgs = this->getBody().front().getArguments();
1036 if (blockArgs.size() != getNumOperands())
1037 return emitError() <<
"operand type doesn't match with the block arg";
1039 for (
auto [blockArg, operand] :
llvm::zip(blockArgs, getOperands())) {
1040 if (blockArg.getType() !=
1041 cast<ImmutableType>(operand.getType()).getInnerType())
1043 << blockArg.getType() <<
" is expected but got "
1044 << cast<ImmutableType>(operand.getType()).getInnerType();
1048void InitialOp::build(OpBuilder &builder, OperationState &result,
1049 TypeRange resultTypes, std::function<
void()> ctor) {
1050 OpBuilder::InsertionGuard guard(builder);
1052 builder.createBlock(result.addRegion());
1053 SmallVector<Type> types;
1054 for (
auto t : resultTypes)
1055 types.push_back(
seq::ImmutableType::
get(t));
1057 result.addTypes(types);
1063TypedValue<seq::ImmutableType>
1065 mlir::IntegerAttr attr) {
1066 auto initial = builder.create<seq::InitialOp>(loc, attr.getType(), [&]() {
1068 builder.
create<seq::YieldOp>(loc, ArrayRef<Value>{constant});
1070 return cast<TypedValue<seq::ImmutableType>>(initial->getResult(0));
1073mlir::TypedValue<seq::ImmutableType>
1075 assert(op->getNumResults() == 1 &&
1076 op->hasTrait<mlir::OpTrait::ConstantLike>());
1078 builder.create<seq::InitialOp>(op->getLoc(), op->getResultTypes(), [&]() {
1079 auto clonedOp = builder.clone(*op);
1080 builder.create<seq::YieldOp>(op->getLoc(), clonedOp->getResults());
1082 return cast<mlir::TypedValue<seq::ImmutableType>>(initial.getResult(0));
1086 auto resultNum = cast<OpResult>(value).getResultNumber();
1087 auto initialOp = value.getDefiningOp<seq::InitialOp>();
1089 return initialOp.getBodyBlock()->getTerminator()->getOperand(resultNum);
1093 SmallVector<Operation *> initialOps;
1094 for (
auto &op : *block)
1095 if (isa<seq::InitialOp>(op))
1096 initialOps.push_back(&op);
1098 if (!mlir::computeTopologicalSorting(initialOps, {}))
1099 return block->getParentOp()->emitError() <<
"initial ops cannot be "
1100 <<
"topologically sorted";
1103 if (initialOps.size() <= 1)
1104 return initialOps.empty() ? seq::InitialOp()
1105 : cast<seq::InitialOp>(initialOps[0]);
1107 auto initialOp = cast<seq::InitialOp>(initialOps.front());
1108 auto yieldOp = cast<seq::YieldOp>(initialOp.getBodyBlock()->getTerminator());
1110 llvm::MapVector<Value, Value>
1111 resultToYieldOperand;
1113 for (
auto [result, operand] :
1114 llvm::zip(initialOp.getResults(), yieldOp->getOperands()))
1115 resultToYieldOperand.insert({result, operand});
1117 for (
size_t i = 1; i < initialOps.size(); ++i) {
1118 auto currentInitialOp = cast<seq::InitialOp>(initialOps[i]);
1119 auto operands = currentInitialOp->getOperands();
1120 for (
auto [blockArg, operand] :
1122 if (
auto initOp = operand.getDefiningOp<seq::InitialOp>()) {
1123 assert(resultToYieldOperand.count(operand) &&
1124 "it must be visited already");
1125 blockArg.replaceAllUsesWith(resultToYieldOperand.lookup(operand));
1128 initialOp.getBodyBlock()->addArgument(
1129 cast<seq::ImmutableType>(operand.getType()).getInnerType(),
1131 initialOp.getInputsMutable().append(operand);
1135 auto currentYieldOp =
1136 cast<seq::YieldOp>(currentInitialOp.getBodyBlock()->getTerminator());
1138 for (
auto [result, operand] :
llvm::zip(currentInitialOp.getResults(),
1139 currentYieldOp->getOperands()))
1140 resultToYieldOperand.insert({result, operand});
1143 yieldOp.getOperandsMutable().append(currentYieldOp.getOperands());
1144 currentYieldOp->erase();
1148 initialOp.getBodyBlock()->getOperations().splice(
1149 initialOp.end(), currentInitialOp.getBodyBlock()->getOperations());
1153 yieldOp->moveBefore(initialOp.getBodyBlock(),
1154 initialOp.getBodyBlock()->end());
1156 auto builder = OpBuilder::atBlockBegin(block);
1157 SmallVector<Type> types;
1158 for (
auto [result, operand] : resultToYieldOperand)
1159 types.push_back(operand.getType());
1163 auto newInitial = builder.create<seq::InitialOp>(initialOp.getLoc(), types);
1164 newInitial.getInputsMutable().append(initialOp.getInputs());
1166 for (
auto [resultAndOperand, newResult] :
1167 llvm::zip(resultToYieldOperand, newInitial.getResults()))
1168 resultAndOperand.first.replaceAllUsesWith(newResult);
1171 for (
auto oldBlockArg : initialOp.
getBodyBlock()->getArguments()) {
1172 auto blockArg = newInitial.getBodyBlock()->addArgument(
1173 oldBlockArg.getType(), oldBlockArg.getLoc());
1174 oldBlockArg.replaceAllUsesWith(blockArg);
1177 newInitial.getBodyBlock()->getOperations().splice(
1178 newInitial.end(), initialOp.getBodyBlock()->getOperations());
1181 while (!initialOps.empty())
1182 initialOps.pop_back_val()->erase();
1192#define GET_OP_CLASSES
1193#include "circt/Dialect/Seq/Seq.cpp.inc"
assert(baseType &&"element must be base type")
static bool isConstZero(Value value)
static InstancePath empty
static std::optional< APInt > getInt(Value value)
Helper to convert a value to a constant integer if it is one.
static Block * getBodyBlock(FModuleLike mod)
void printFIFOAFThreshold(OpAsmPrinter &p, Operation *op, IntegerAttr threshold, Type outputFlagType)
static bool isConstClock(Value value)
static ParseResult parseFIFOFlagThreshold(OpAsmParser &parser, IntegerAttr &threshold, Type &outputFlagType, StringRef directive)
static void printOptionalTypeMatch(OpAsmPrinter &p, Operation *op, Type refType, Value operand, Type type)
static bool isConstAllOnes(Value value)
static ParseResult parseOptionalImmutableTypeMatch(OpAsmParser &parser, Type refType, std::optional< OpAsmParser::UnresolvedOperand > operand, Type &type)
void printFIFOAEThreshold(OpAsmPrinter &p, Operation *op, IntegerAttr threshold, Type outputFlagType)
LogicalResult verifyResets(TOp op)
static bool canElideName(OpAsmPrinter &p, Operation *op)
ParseResult parseFIFOAEThreshold(OpAsmParser &parser, IntegerAttr &threshold, Type &outputFlagType)
static LogicalResult verifyFirMemMask(Op op)
static void printOptionalImmutableTypeMatch(OpAsmPrinter &p, Operation *op, Type refType, Value operand, Type type)
static ParseResult parseOptionalTypeMatch(OpAsmParser &parser, Type refType, std::optional< OpAsmParser::UnresolvedOperand > operand, Type &type)
static void setNameFromResult(OpAsmParser &parser, OperationState &result)
ParseResult parseFIFOAFThreshold(OpAsmParser &parser, IntegerAttr &threshold, Type &outputFlagType)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
bool isConstant(Operation *op)
Return true if the specified operation has a constant value.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
FailureOr< seq::InitialOp > mergeInitialOps(Block *block)
bool isValidIndexValues(Value hlmemHandle, ValueRange addresses)
mlir::TypedValue< seq::ImmutableType > createConstantInitialValue(OpBuilder builder, Location loc, mlir::IntegerAttr attr)
Value unwrapImmutableValue(mlir::TypedValue< seq::ImmutableType > immutableVal)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
static bool isConstantZero(Attribute operand)
Determine whether a constant operand is a zero value.
static bool isConstantOne(Attribute operand)
Determine whether a constant operand is a one value.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
FirMemory(hw::HWModuleGeneratedOp op)