15 #include "mlir/Dialect/Arith/IR/Arith.h"
16 #include "mlir/Dialect/Func/IR/FuncOps.h"
17 #include "mlir/IR/Builders.h"
18 #include "mlir/Interfaces/FunctionImplementation.h"
19 #include "llvm/Support/Debug.h"
20 #include "llvm/Support/FormatVariadic.h"
23 using namespace circt;
27 #include "circt/Dialect/Pipeline/PipelineDialect.cpp.inc"
29 #define DEBUG_TYPE "pipeline-ops"
31 llvm::SmallVector<Value>
33 llvm::SetVector<Value> values;
34 region.walk([&](Operation *op) {
35 for (
auto operand : op->getOperands()) {
36 if (region.isAncestor(operand.getParentRegion()))
38 values.insert(operand);
41 return values.takeVector();
49 Operation *directParent = block->getParentOp();
50 if (directParent != pipeline) {
52 directParent->getParentOfType<ScheduledPipelineOp>();
53 assert(indirectParent == pipeline &&
"block is not in the pipeline");
57 while (block && block->getParent() != &pipeline.getRegion()) {
59 block = block->getParent()->getParentOp()->getBlock();
73 if (isa<BlockArgument>(v))
75 cast<BlockArgument>(v).getOwner());
86 llvm::SmallVector<Type> &inputTypes,
87 mlir::ArrayAttr &outputNames) {
89 llvm::SmallVector<Attribute> names;
90 if (parser.parseCommaSeparatedList(
91 OpAsmParser::Delimiter::Paren, [&]() -> ParseResult {
94 if (parser.parseKeyword(&name) || parser.parseColonType(type))
97 inputTypes.push_back(type);
98 names.push_back(StringAttr::get(parser.getContext(), name));
109 llvm::interleaveComma(llvm::zip(types, names), p, [&](
auto it) {
110 auto [type, name] = it;
111 p.printKeywordOrString(cast<StringAttr>(name).str());
118 OpAsmParser::UnresolvedOperand &op) {
119 if (p.parseKeyword(keyword) || p.parseLParen() || p.parseOperand(op) ||
130 mlir::OperationState &result) {
132 if (succeeded(parser.parseOptionalString(&name)))
133 result.addAttribute(
"name", parser.getBuilder().getStringAttr(name));
135 llvm::SmallVector<OpAsmParser::UnresolvedOperand> inputOperands;
136 llvm::SmallVector<OpAsmParser::Argument> inputArguments;
137 llvm::SmallVector<Type> inputTypes;
138 ArrayAttr inputNames;
142 result.addAttribute(
"inputNames", inputNames);
144 Type i1 = parser.getBuilder().getI1Type();
146 OpAsmParser::Argument stallArg;
147 OpAsmParser::UnresolvedOperand stallOperand;
148 bool withStall =
false;
149 if (succeeded(parser.parseOptionalKeyword(
"stall"))) {
150 if (parser.parseLParen() || parser.parseOperand(stallOperand) ||
151 parser.parseRParen())
157 OpAsmParser::UnresolvedOperand clockOperand, resetOperand, goOperand;
164 OpAsmParser::Argument entryEnable;
165 entryEnable.type = i1;
166 if (parser.parseKeyword(
"entryEn") || parser.parseLParen() ||
167 parser.parseArgument(entryEnable) || parser.parseRParen())
171 if (parser.parseOptionalAttrDict(result.attributes))
175 if (parser.parseArrow())
178 llvm::SmallVector<Type> outputTypes;
179 ArrayAttr outputNames;
182 result.addTypes(outputTypes);
183 result.addAttribute(
"outputNames", outputNames);
186 result.addTypes({i1});
189 if (parser.resolveOperands(inputOperands, inputTypes, parser.getNameLoc(),
194 if (parser.resolveOperand(stallOperand, i1, result.operands))
199 if (parser.resolveOperand(clockOperand, clkType, result.operands) ||
200 parser.resolveOperand(resetOperand, i1, result.operands) ||
201 parser.resolveOperand(goOperand, i1, result.operands))
207 SmallVector<OpAsmParser::Argument> regionArgs;
210 llvm::append_range(regionArgs, inputArguments);
212 regionArgs.push_back(entryEnable);
215 Region *body = result.addRegion();
216 if (parser.parseRegion(*body, regionArgs))
220 "operandSegmentSizes",
221 parser.getBuilder().getDenseI32ArrayAttr(
222 {static_cast<int32_t>(inputTypes.size()),
223 static_cast<int32_t>(withStall ? 1 : 0),
224 static_cast<int32_t>(1),
225 static_cast<int32_t>(1), static_cast<int32_t>(1)}));
233 p.printOperand(value);
237 template <
typename TPipelineOp>
239 if (
auto name = op.getNameAttr()) {
240 p <<
" \"" << name.getValue() <<
"\"";
250 p.printOperand(op.getStall());
264 p.printRegionArgument(
265 cast<BlockArgument>(op.getStageEnableSignal(
static_cast<size_t>(0))), {},
270 p.printOptionalAttrDict(op->getAttrs(),
271 {
"name",
"operandSegmentSizes",
272 "outputNames",
"inputNames"});
276 printOutputList(p, op.getDataOutputs().getTypes(), op.getOutputNames());
282 p.printRegion(op.getBody(),
false,
291 TypeRange dataOutputs, ValueRange inputs,
292 ArrayAttr inputNames, ArrayAttr outputNames,
293 Value clock, Value reset, Value go, Value stall,
294 StringAttr name, ArrayAttr stallability) {
295 odsState.addOperands(inputs);
297 odsState.addOperands(stall);
298 odsState.addOperands(clock);
299 odsState.addOperands(reset);
300 odsState.addOperands(go);
302 odsState.addAttribute(
"name", name);
304 odsState.addAttribute(
305 "operandSegmentSizes",
306 odsBuilder.getDenseI32ArrayAttr(
307 {static_cast<int32_t>(inputs.size()),
308 static_cast<int32_t>(stall ? 1 : 0), static_cast<int32_t>(1),
309 static_cast<int32_t>(1), static_cast<int32_t>(1)}));
311 odsState.addAttribute(
"inputNames", inputNames);
312 odsState.addAttribute(
"outputNames", outputNames);
314 auto *region = odsState.addRegion();
315 odsState.addTypes(dataOutputs);
318 Type i1 = odsBuilder.getIntegerType(1);
319 odsState.addTypes({i1});
327 auto &entryBlock = region->emplaceBlock();
328 llvm::SmallVector<Location> entryArgLocs(inputs.size(), odsState.location);
329 entryBlock.addArguments(
331 llvm::SmallVector<Location>(inputs.size(), odsState.location));
333 entryBlock.addArgument(i1, odsState.location);
334 entryBlock.addArgument(i1, odsState.location);
335 entryBlock.addArgument(i1, odsState.location);
338 entryBlock.addArgument(i1, odsState.location);
341 odsState.addAttribute(
"stallability", stallability);
344 template <
typename TPipelineOp>
347 for (
auto [res, name] :
348 llvm::zip(op.getDataOutputs(),
349 op.getOutputNames().template getAsValueRange<StringAttr>()))
350 setNameFn(res, name);
351 setNameFn(op.getDone(),
"done");
354 template <
typename TPipelineOp>
358 for (
auto [i, block] : llvm::enumerate(op.getRegion())) {
359 if (Block *predBlock = block.getSinglePredecessor()) {
362 auto predStageOp = cast<StageOp>(predBlock->getTerminator());
363 size_t nRegs = predStageOp.getRegisters().size();
364 auto nPassthrough = predStageOp.getPassthroughs().size();
366 auto regNames = predStageOp.getRegisterNames();
367 auto passthroughNames = predStageOp.getPassthroughNames();
370 for (
size_t regI = 0; regI < nRegs; ++regI) {
371 auto arg = block.getArguments()[regI];
374 auto nameAttr = dyn_cast<StringAttr>((*regNames)[regI]);
375 if (nameAttr && !nameAttr.strref().empty()) {
376 setNameFn(arg, nameAttr);
380 setNameFn(arg, llvm::formatv(
"s{0}_reg{1}", i, regI).str());
384 for (
size_t passthroughI = 0; passthroughI < nPassthrough;
386 auto arg = block.getArguments()[nRegs + passthroughI];
388 if (passthroughNames) {
390 dyn_cast<StringAttr>((*passthroughNames)[passthroughI]);
391 if (nameAttr && !nameAttr.strref().empty()) {
392 setNameFn(arg, nameAttr);
396 setNameFn(arg, llvm::formatv(
"s{0}_pass{1}", i, passthroughI).str());
401 for (
auto [inputArg, inputName] :
402 llvm::zip(op.getInnerInputs(),
403 op.getInputNames().template getAsValueRange<StringAttr>()))
404 setNameFn(inputArg, inputName);
408 setNameFn(block.getArguments().back(),
409 llvm::formatv(
"s{0}_enable", i).str());
413 void UnscheduledPipelineOp::print(OpAsmPrinter &p) {
417 ParseResult UnscheduledPipelineOp::parse(OpAsmParser &parser,
418 OperationState &result) {
426 void UnscheduledPipelineOp::getAsmBlockArgumentNames(
431 void UnscheduledPipelineOp::build(OpBuilder &odsBuilder,
432 OperationState &odsState,
433 TypeRange dataOutputs, ValueRange inputs,
434 ArrayAttr inputNames, ArrayAttr outputNames,
435 Value clock, Value reset, Value go,
436 Value stall, StringAttr name,
437 ArrayAttr stallability) {
439 outputNames, clock, reset, go, stall, name, stallability);
446 void ScheduledPipelineOp::print(OpAsmPrinter &p) {
printPipelineOp(p, *
this); }
448 ParseResult ScheduledPipelineOp::parse(OpAsmParser &parser,
449 OperationState &result) {
453 void ScheduledPipelineOp::build(OpBuilder &odsBuilder, OperationState &odsState,
454 TypeRange dataOutputs, ValueRange inputs,
455 ArrayAttr inputNames, ArrayAttr outputNames,
456 Value clock, Value reset, Value go, Value stall,
457 StringAttr name, ArrayAttr stallability) {
459 outputNames, clock, reset, go, stall, name, stallability);
462 Block *ScheduledPipelineOp::addStage() {
463 OpBuilder builder(getContext());
464 Block *stage = builder.createBlock(&getRegion());
467 stage->addArgument(builder.getIntegerType(1), getLoc());
471 void ScheduledPipelineOp::getAsmBlockArgumentNames(
482 static FailureOr<llvm::SmallVector<Block *>>
484 llvm::DenseSet<Block *> visited;
485 Block *currentStage = op.getEntryStage();
486 llvm::SmallVector<Block *> orderedStages;
488 if (!visited.insert(currentStage).second)
489 return op.emitOpError(
"pipeline contains a cycle.");
491 orderedStages.push_back(currentStage);
492 if (
auto stageOp = dyn_cast<StageOp>(currentStage->getTerminator()))
493 currentStage = stageOp.getNextStage();
495 currentStage =
nullptr;
496 }
while (currentStage);
498 return {orderedStages};
501 llvm::SmallVector<Block *> ScheduledPipelineOp::getOrderedStages() {
507 llvm::DenseMap<Block *, unsigned> ScheduledPipelineOp::getStageMap() {
508 llvm::DenseMap<Block *, unsigned> stageMap;
509 auto orderedStages = getOrderedStages();
510 for (
auto [index, stage] : llvm::enumerate(orderedStages))
511 stageMap[stage] = index;
516 Block *ScheduledPipelineOp::getLastStage() {
return getOrderedStages().back(); }
518 bool ScheduledPipelineOp::isMaterialized() {
521 return llvm::any_of(getStages(), [
this](Block &block) {
523 if (&block == getEntryStage())
525 return block.getNumArguments() > 1;
531 auto &stages = getStages();
532 for (Block &stage : stages) {
533 if (stage.empty() || !isa<ReturnOp, StageOp>(stage.back()))
534 return emitOpError(
"all blocks must be terminated with a "
535 "`pipeline.stage` or `pipeline.return` op.");
542 for (
auto [i, block] : llvm::enumerate(stages)) {
544 if (block.getNumArguments() != 0) {
546 dyn_cast<IntegerType>(block.getArguments().back().getType());
547 err = !lastArgType || lastArgType.getWidth() != 1;
550 return emitOpError(
"block " + std::to_string(i) +
551 " must have an i1 argument as the last block argument "
552 "(stage valid signal).");
557 llvm::DenseSet<Value> extLikeInputs;
558 for (
auto extInput : getExtInputs())
559 extLikeInputs.insert(extInput);
561 extLikeInputs.insert(getClock());
562 extLikeInputs.insert(getReset());
564 extLikeInputs.insert(getStall());
569 bool materialized = isMaterialized();
571 for (
auto &stage : stages) {
572 for (
auto &op : stage) {
573 for (
auto [index, operand] : llvm::enumerate(op.getOperands())) {
575 if (extLikeInputs.contains(operand)) {
580 if (
auto *definingOp = operand.getDefiningOp()) {
582 if (definingOp->hasTrait<OpTrait::ConstantLike>())
584 err = definingOp->getBlock() != &stage;
587 err = !llvm::is_contained(stage.getArguments(), operand);
591 return op.emitOpError(
592 "Pipeline is in register materialized mode - operand ")
594 <<
" is defined in a different stage, which is illegal.";
600 if (
auto stallability = getStallability()) {
603 return emitOpError(
"cannot specify stallability without a stall signal.");
607 size_t nRegisterStages = stages.size() - 1;
608 if (stallability->size() != nRegisterStages)
609 return emitOpError(
"stallability array must be the same length as the "
610 "number of stages. Pipeline has ")
611 << nRegisterStages <<
" stages but array had "
612 << stallability->size() <<
" elements.";
618 StageKind ScheduledPipelineOp::getStageKind(
size_t stageIndex) {
619 assert(stageIndex < getNumStages() &&
"invalid stage index");
622 return StageKind::Continuous;
626 std::optional<ArrayAttr> stallability = getStallability();
629 return StageKind::Stallable;
632 if (stageIndex < stallability->size()) {
633 bool stageIsStallable =
634 cast<BoolAttr>((*stallability)[stageIndex]).getValue();
635 if (!stageIsStallable) {
637 return StageKind::NonStallable;
645 return StageKind::Stallable;
647 for (
size_t i = stageIndex - 1; i > 0; --i) {
648 if (getStageKind(i) == StageKind::NonStallable)
649 return StageKind::Runoff;
651 return StageKind::Stallable;
659 Operation *parent = getOperation()->getParentOp();
660 size_t nInputs = getInputs().size();
661 auto expectedResults = TypeRange(parent->getResultTypes()).drop_back();
662 size_t expectedNResults = expectedResults.size();
663 if (nInputs != expectedNResults)
664 return emitOpError(
"expected ")
665 << expectedNResults <<
" return values, got " << nInputs <<
".";
667 for (
auto [inType, reqType] :
668 llvm::zip(getInputs().getTypes(), expectedResults)) {
669 if (inType != reqType)
670 return emitOpError(
"expected return value of type ")
671 << reqType <<
", got " << inType <<
".";
686 OpAsmParser::UnresolvedOperand &v, Type &t,
690 if (succeeded(parser.parseOptionalString(&nameref))) {
692 return parser.emitError(parser.getCurrentLocation(),
693 "name cannot be empty");
695 if (failed(parser.parseEqual()))
696 return parser.emitError(parser.getCurrentLocation(),
697 "expected '=' after name");
698 name = parser.getBuilder().getStringAttr(nameref);
700 name = parser.getBuilder().getStringAttr(
"");
704 if (failed(parser.parseOperand(v)) || failed(parser.parseColonType(t)))
713 OpAsmParser &parser, OpAsmParser::UnresolvedOperand &v, Type &t,
714 llvm::SmallVector<OpAsmParser::UnresolvedOperand> &clockGates,
720 if (failed(parser.parseOptionalKeyword(
"gated")))
723 if (failed(parser.parseKeyword(
"by")) ||
725 parser.parseOperandList(clockGates, OpAsmParser::Delimiter::Square)))
736 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 4> ®isters,
737 llvm::SmallVector<mlir::Type, 1> ®isterTypes,
738 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 4> &clockGates,
739 ArrayAttr &clockGatesPerRegister, ArrayAttr ®isterNames) {
741 if (failed(parser.parseOptionalKeyword(
"regs"))) {
742 clockGatesPerRegister = parser.getBuilder().getI64ArrayAttr({});
746 llvm::SmallVector<int64_t> clockGatesPerRegisterList;
747 llvm::SmallVector<Attribute> registerNamesList;
748 bool withNames =
false;
749 if (failed(parser.parseCommaSeparatedList(AsmParser::Delimiter::Paren, [&]() {
750 OpAsmParser::UnresolvedOperand v;
752 llvm::SmallVector<OpAsmParser::UnresolvedOperand> cgs;
754 if (parseSingleStageRegister(parser, v, t, cgs, name))
756 registers.push_back(v);
757 registerTypes.push_back(t);
758 registerNamesList.push_back(name);
759 withNames |= static_cast<bool>(name);
760 llvm::append_range(clockGates, cgs);
761 clockGatesPerRegisterList.push_back(cgs.size());
766 clockGatesPerRegister =
767 parser.getBuilder().getI64ArrayAttr(clockGatesPerRegisterList);
769 registerNames = parser.getBuilder().getArrayAttr(registerNamesList);
775 TypeRange registerTypes, ValueRange clockGates,
776 ArrayAttr clockGatesPerRegister, ArrayAttr names) {
777 if (registers.empty())
781 size_t clockGateStartIdx = 0;
782 llvm::interleaveComma(
784 llvm::zip(registers, registerTypes, clockGatesPerRegister)),
786 size_t idx = it.index();
787 auto &[
reg, type, nClockGatesAttr] = it.value();
789 if (
auto nameAttr = dyn_cast<StringAttr>(names[idx]);
790 nameAttr && !nameAttr.strref().
empty())
791 p << nameAttr <<
" = ";
794 p <<
reg <<
" : " << type;
795 int64_t nClockGates = cast<IntegerAttr>(nClockGatesAttr).getInt();
796 if (nClockGates == 0)
799 llvm::interleaveComma(clockGates.slice(clockGateStartIdx, nClockGates),
802 clockGateStartIdx += nClockGates;
808 TypeRange passthroughTypes, ArrayAttr names) {
810 if (passthroughs.empty())
814 llvm::interleaveComma(
815 llvm::enumerate(llvm::zip(passthroughs, passthroughTypes)), p,
817 size_t idx = it.index();
818 auto &[
reg, type] = it.value();
820 if (
auto nameAttr = dyn_cast<StringAttr>(names[idx]);
821 nameAttr && !nameAttr.strref().
empty())
822 p << nameAttr <<
" = ";
824 p <<
reg <<
" : " << type;
833 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 4> &passthroughs,
834 llvm::SmallVector<mlir::Type, 1> &passthroughTypes,
835 ArrayAttr &passthroughNames) {
836 if (failed(parser.parseOptionalKeyword(
"pass")))
839 llvm::SmallVector<Attribute> passthroughsNameList;
840 bool withNames =
false;
841 if (failed(parser.parseCommaSeparatedList(AsmParser::Delimiter::Paren, [&]() {
842 OpAsmParser::UnresolvedOperand v;
845 if (parseOptNamedTypedAssignment(parser, v, t, name))
847 passthroughs.push_back(v);
848 passthroughTypes.push_back(t);
849 passthroughsNameList.push_back(name);
850 withNames |= static_cast<bool>(name);
856 passthroughNames = parser.getBuilder().getArrayAttr(passthroughsNameList);
861 void StageOp::build(OpBuilder &odsBuilder, OperationState &odsState,
862 Block *dest, ValueRange registers,
863 ValueRange passthroughs) {
864 odsState.addSuccessors(dest);
865 odsState.addOperands(registers);
866 odsState.addOperands(passthroughs);
867 odsState.addAttribute(
"operandSegmentSizes",
868 odsBuilder.getDenseI32ArrayAttr(
869 {static_cast<int32_t>(registers.size()),
870 static_cast<int32_t>(passthroughs.size()),
871 static_cast<int32_t>(0)}));
872 llvm::SmallVector<int64_t> clockGatesPerRegister(registers.size(), 0);
873 odsState.addAttribute(
"clockGatesPerRegister",
874 odsBuilder.getI64ArrayAttr(clockGatesPerRegister));
877 void StageOp::build(OpBuilder &odsBuilder, OperationState &odsState,
878 Block *dest, ValueRange registers, ValueRange passthroughs,
879 llvm::ArrayRef<llvm::SmallVector<Value>> clockGateList,
880 mlir::ArrayAttr registerNames,
881 mlir::ArrayAttr passthroughNames) {
882 build(odsBuilder, odsState, dest, registers, passthroughs);
884 llvm::SmallVector<Value> clockGates;
885 llvm::SmallVector<int64_t> clockGatesPerRegister(registers.size(), 0);
886 for (
auto gates : clockGateList) {
887 llvm::append_range(clockGates, gates);
888 clockGatesPerRegister.push_back(gates.size());
890 odsState.attributes.set(
"clockGatesPerRegister",
891 odsBuilder.getI64ArrayAttr(clockGatesPerRegister));
892 odsState.addOperands(clockGates);
895 odsState.addAttribute(
"registerNames", registerNames);
897 if (passthroughNames)
898 odsState.addAttribute(
"passthroughNames", passthroughNames);
901 ValueRange StageOp::getClockGatesForReg(
unsigned regIdx) {
902 assert(regIdx < getRegisters().size() &&
"register index out of bounds.");
909 unsigned clockGateStartIdx = 0;
910 for (
auto [index, nClockGatesAttr] :
911 llvm::enumerate(getClockGatesPerRegister().getAsRange<IntegerAttr>())) {
912 int64_t nClockGates = nClockGatesAttr.getInt();
913 if (index == regIdx) {
915 return getClockGates().slice(clockGateStartIdx, nClockGates);
919 clockGateStartIdx += nClockGates;
922 llvm_unreachable(
"register index out of bounds.");
928 llvm::SmallVector<Type> expectedTargetArgTypes;
929 llvm::append_range(expectedTargetArgTypes, getRegisters().getTypes());
930 llvm::append_range(expectedTargetArgTypes, getPassthroughs().getTypes());
931 Block *targetStage = getNextStage();
933 TypeRange targetStageArgTypes =
934 TypeRange(targetStage->getArgumentTypes()).drop_back();
936 if (targetStageArgTypes.size() != expectedTargetArgTypes.size())
937 return emitOpError(
"expected ") << expectedTargetArgTypes.size()
938 <<
" arguments in the target stage, got "
939 << targetStageArgTypes.size() <<
".";
941 for (
auto [index, it] : llvm::enumerate(
942 llvm::zip(expectedTargetArgTypes, targetStageArgTypes))) {
943 auto [arg, barg] = it;
945 return emitOpError(
"expected target stage argument ")
946 << index <<
" to have type " << arg <<
", got " << barg <<
".";
951 if (getClockGatesPerRegister().size() != getRegisters().size())
952 return emitOpError(
"expected clockGatesPerRegister to be equally sized to "
953 "the number of registers.");
957 if (
auto regNames = getRegisterNames()) {
958 if (regNames->size() != getRegisters().size())
959 return emitOpError(
"expected registerNames to be equally sized to "
960 "the number of registers.");
965 if (
auto passthroughNames = getPassthroughNames()) {
966 if (passthroughNames->size() != getPassthroughs().size())
967 return emitOpError(
"expected passthroughNames to be equally sized to "
968 "the number of passthroughs.");
979 ScheduledPipelineOp scheduledPipelineParent =
980 dyn_cast<ScheduledPipelineOp>(getOperation()->getParentOp());
982 if (!scheduledPipelineParent) {
991 if (getNumResults() == 0)
992 return emitOpError(
"expected at least one result type.");
996 size_t latency = getLatency();
997 Block *definingStage = getOperation()->getBlock();
999 llvm::DenseMap<Block *, unsigned> stageMap =
1000 scheduledPipelineParent.getStageMap();
1002 auto stageDistance = [&](
Block *from,
Block *to) {
1003 assert(stageMap.count(from) &&
"stage 'from' not contained in pipeline");
1004 assert(stageMap.count(to) &&
"stage 'to' not contained in pipeline");
1005 int64_t fromStage = stageMap[from];
1006 int64_t toStage = stageMap[to];
1007 return toStage - fromStage;
1010 for (
auto [i, res] : llvm::enumerate(getResults())) {
1011 for (
auto &use : res.getUses()) {
1012 auto *user = use.getOwner();
1019 unsigned useDistance = stageDistance(definingStage, userStage);
1023 StageOp stageOp = dyn_cast<StageOp>(user);
1024 if (userStage == definingStage && stageOp) {
1025 if (llvm::is_contained(stageOp.getPassthroughs(), res))
1032 if (useDistance < latency) {
1033 auto diag = emitOpError(
"result ")
1034 << i <<
" is used before it is available.";
1035 diag.attachNote(user->getLoc())
1036 <<
"use was operand " << use.getOperandNumber()
1037 <<
". The result is available " << latency - useDistance
1038 <<
" stages later than this use.";
1051 LatencyOp parent = cast<LatencyOp>(getOperation()->getParentOp());
1052 size_t nInputs = getInputs().size();
1053 size_t nResults = parent->getNumResults();
1054 if (nInputs != nResults)
1055 return emitOpError(
"expected ")
1056 << nResults <<
" return values, got " << nInputs <<
".";
1058 for (
auto [inType, reqType] :
1059 llvm::zip(getInputs().getTypes(), parent->getResultTypes())) {
1060 if (inType != reqType)
1061 return emitOpError(
"expected return value of type ")
1062 << reqType <<
", got " << inType <<
".";
1068 #define GET_OP_CLASSES
1069 #include "circt/Dialect/Pipeline/Pipeline.cpp.inc"
1071 void PipelineDialect::initialize() {
1074 #include "circt/Dialect/Pipeline/Pipeline.cpp.inc"
assert(baseType &&"element must be base type")
static InstancePath empty
static ParseResult parseSingleStageRegister(OpAsmParser &parser, OpAsmParser::UnresolvedOperand &v, Type &t, llvm::SmallVector< OpAsmParser::UnresolvedOperand > &clockGates, StringAttr &name)
static ParseResult parseKeywordAndOperand(OpAsmParser &p, StringRef keyword, OpAsmParser::UnresolvedOperand &op)
static void printOutputList(OpAsmPrinter &p, TypeRange types, ArrayAttr names)
ParseResult parsePassthroughs(OpAsmParser &parser, llvm::SmallVector< OpAsmParser::UnresolvedOperand, 4 > &passthroughs, llvm::SmallVector< mlir::Type, 1 > &passthroughTypes, ArrayAttr &passthroughNames)
static void printPipelineOp(OpAsmPrinter &p, TPipelineOp op)
void printPassthroughs(OpAsmPrinter &p, Operation *op, ValueRange passthroughs, TypeRange passthroughTypes, ArrayAttr names)
static ParseResult parseOptNamedTypedAssignment(OpAsmParser &parser, OpAsmParser::UnresolvedOperand &v, Type &t, StringAttr &name)
static ParseResult parsePipelineOp(mlir::OpAsmParser &parser, mlir::OperationState &result)
static FailureOr< llvm::SmallVector< Block * > > getOrderedStagesFailable(ScheduledPipelineOp op)
void printStageRegisters(OpAsmPrinter &p, Operation *op, ValueRange registers, TypeRange registerTypes, ValueRange clockGates, ArrayAttr clockGatesPerRegister, ArrayAttr names)
static void getPipelineAsmBlockArgumentNames(TPipelineOp op, mlir::Region ®ion, mlir::OpAsmSetValueNameFn setNameFn)
static ParseResult parseOutputList(OpAsmParser &parser, llvm::SmallVector< Type > &inputTypes, mlir::ArrayAttr &outputNames)
static void printKeywordOperand(OpAsmPrinter &p, StringRef keyword, Value value)
ParseResult parseStageRegisters(OpAsmParser &parser, llvm::SmallVector< OpAsmParser::UnresolvedOperand, 4 > ®isters, llvm::SmallVector< mlir::Type, 1 > ®isterTypes, llvm::SmallVector< OpAsmParser::UnresolvedOperand, 4 > &clockGates, ArrayAttr &clockGatesPerRegister, ArrayAttr ®isterNames)
static void buildPipelineLikeOp(OpBuilder &odsBuilder, OperationState &odsState, TypeRange dataOutputs, ValueRange inputs, ArrayAttr inputNames, ArrayAttr outputNames, Value clock, Value reset, Value go, Value stall, StringAttr name, ArrayAttr stallability)
static void getPipelineAsmResultNames(TPipelineOp op, OpAsmSetValueNameFn setNameFn)
static LogicalResult verify(Value clock, bool eventExists, mlir::Location loc)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
ParseResult parseInitializerList(mlir::OpAsmParser &parser, llvm::SmallVector< mlir::OpAsmParser::Argument > &inputArguments, llvm::SmallVector< mlir::OpAsmParser::UnresolvedOperand > &inputOperands, llvm::SmallVector< Type > &inputTypes, ArrayAttr &inputNames)
Parses an initializer.
void printInitializerList(OpAsmPrinter &p, ValueRange ins, ArrayRef< BlockArgument > args)
llvm::SmallVector< Value > getValuesDefinedOutsideRegion(Region ®ion)
Block * getParentStageInPipeline(ScheduledPipelineOp pipeline, Operation *op)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)