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"
27#include "circt/Dialect/Pipeline/PipelineDialect.cpp.inc"
29#define DEBUG_TYPE "pipeline-ops"
31llvm::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));
103 outputNames = ArrayAttr::get(parser.getContext(), names);
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::UnresolvedOperand stallOperand, clockOperand, resetOperand,
150 bool withStall =
false;
151 if (succeeded(parser.parseOptionalKeyword(
"stall"))) {
152 if (parser.parseLParen() || parser.parseOperand(stallOperand) ||
153 parser.parseRParen())
163 bool withReset =
false;
164 if (succeeded(parser.parseOptionalKeyword(
"reset"))) {
165 if (parser.parseLParen() || parser.parseOperand(resetOperand) ||
166 parser.parseRParen())
175 OpAsmParser::Argument entryEnable;
176 entryEnable.type = i1;
177 if (parser.parseKeyword(
"entryEn") || parser.parseLParen() ||
178 parser.parseArgument(entryEnable) || parser.parseRParen())
182 if (parser.parseOptionalAttrDict(result.attributes))
186 if (parser.parseArrow())
189 llvm::SmallVector<Type> outputTypes;
190 ArrayAttr outputNames;
193 result.addTypes(outputTypes);
194 result.addAttribute(
"outputNames", outputNames);
197 result.addTypes({i1});
200 if (parser.resolveOperands(inputOperands, inputTypes, parser.getNameLoc(),
205 if (parser.resolveOperand(stallOperand, i1, result.operands))
209 Type clkType = seq::ClockType::get(parser.getContext());
210 if (parser.resolveOperand(clockOperand, clkType, result.operands))
213 if (withReset && parser.resolveOperand(resetOperand, i1, result.operands))
216 if (parser.resolveOperand(goOperand, i1, result.operands))
222 SmallVector<OpAsmParser::Argument> regionArgs;
225 llvm::append_range(regionArgs, inputArguments);
227 regionArgs.push_back(entryEnable);
230 Region *body = result.addRegion();
231 if (parser.parseRegion(*body, regionArgs))
234 result.addAttribute(
"operandSegmentSizes",
235 parser.getBuilder().getDenseI32ArrayAttr(
236 {static_cast<int32_t>(inputTypes.size()),
237 static_cast<int32_t>(withStall ? 1 : 0),
238 static_cast<int32_t>(1),
239 static_cast<int32_t>(withReset ? 1 : 0),
240 static_cast<int32_t>(1)}));
248 p.printOperand(value);
252template <
typename TPipelineOp>
254 if (
auto name = op.getNameAttr()) {
255 p <<
" \"" << name.getValue() <<
"\"";
280 p.printRegionArgument(
281 cast<BlockArgument>(op.getStageEnableSignal(
static_cast<size_t>(0))), {},
286 p.printOptionalAttrDict(op->getAttrs(),
287 {
"name",
"operandSegmentSizes",
288 "outputNames",
"inputNames"});
292 printOutputList(p, op.getDataOutputs().getTypes(), op.getOutputNames());
298 p.printRegion(op.getBody(),
false,
307 TypeRange dataOutputs, ValueRange inputs,
308 ArrayAttr inputNames, ArrayAttr outputNames,
309 Value clock, Value go, Value reset, Value stall,
310 StringAttr name, ArrayAttr stallability) {
311 odsState.addOperands(inputs);
313 odsState.addOperands(stall);
314 odsState.addOperands(clock);
315 odsState.addOperands(reset);
316 odsState.addOperands(go);
318 odsState.addAttribute(
"name", name);
320 odsState.addAttribute(
321 "operandSegmentSizes",
322 odsBuilder.getDenseI32ArrayAttr(
323 {static_cast<int32_t>(inputs.size()),
324 static_cast<int32_t>(stall ? 1 : 0), static_cast<int32_t>(1),
325 static_cast<int32_t>(1), static_cast<int32_t>(1)}));
327 odsState.addAttribute(
"inputNames", inputNames);
328 odsState.addAttribute(
"outputNames", outputNames);
330 auto *region = odsState.addRegion();
331 odsState.addTypes(dataOutputs);
334 Type i1 = odsBuilder.getIntegerType(1);
335 odsState.addTypes({i1});
343 auto &entryBlock = region->emplaceBlock();
344 llvm::SmallVector<Location> entryArgLocs(inputs.size(), odsState.location);
345 entryBlock.addArguments(
347 llvm::SmallVector<Location>(inputs.size(), odsState.location));
349 entryBlock.addArgument(i1, odsState.location);
350 entryBlock.addArgument(i1, odsState.location);
351 entryBlock.addArgument(i1, odsState.location);
354 entryBlock.addArgument(i1, odsState.location);
357 odsState.addAttribute(
"stallability", stallability);
360template <
typename TPipelineOp>
363 for (
auto [res, name] :
364 llvm::zip(op.getDataOutputs(),
365 op.getOutputNames().template getAsValueRange<StringAttr>()))
366 setNameFn(res, name);
367 setNameFn(op.getDone(),
"done");
370template <
typename TPipelineOp>
374 for (
auto [i, block] : llvm::enumerate(op.getRegion())) {
375 if (Block *predBlock = block.getSinglePredecessor()) {
378 auto predStageOp = cast<StageOp>(predBlock->getTerminator());
379 size_t nRegs = predStageOp.getRegisters().size();
380 auto nPassthrough = predStageOp.getPassthroughs().size();
382 auto regNames = predStageOp.getRegisterNames();
383 auto passthroughNames = predStageOp.getPassthroughNames();
386 for (
size_t regI = 0; regI < nRegs; ++regI) {
387 auto arg = block.getArguments()[regI];
390 auto nameAttr = dyn_cast<StringAttr>((*regNames)[regI]);
391 if (nameAttr && !nameAttr.strref().empty()) {
392 setNameFn(arg, nameAttr);
396 setNameFn(arg, llvm::formatv(
"s{0}_reg{1}", i, regI).str());
400 for (
size_t passthroughI = 0; passthroughI < nPassthrough;
402 auto arg = block.getArguments()[nRegs + passthroughI];
404 if (passthroughNames) {
406 dyn_cast<StringAttr>((*passthroughNames)[passthroughI]);
407 if (nameAttr && !nameAttr.strref().empty()) {
408 setNameFn(arg, nameAttr);
412 setNameFn(arg, llvm::formatv(
"s{0}_pass{1}", i, passthroughI).str());
417 for (
auto [inputArg, inputName] :
418 llvm::zip(op.getInnerInputs(),
419 op.getInputNames().template getAsValueRange<StringAttr>()))
420 setNameFn(inputArg, inputName);
424 setNameFn(block.getArguments().back(),
425 llvm::formatv(
"s{0}_enable", i).str());
429void UnscheduledPipelineOp::print(OpAsmPrinter &p) {
433ParseResult UnscheduledPipelineOp::parse(OpAsmParser &parser,
434 OperationState &result) {
442void UnscheduledPipelineOp::getAsmBlockArgumentNames(
447void UnscheduledPipelineOp::build(OpBuilder &odsBuilder,
448 OperationState &odsState,
449 TypeRange dataOutputs, ValueRange inputs,
450 ArrayAttr inputNames, ArrayAttr outputNames,
451 Value clock, Value go, Value reset,
452 Value stall, StringAttr name,
453 ArrayAttr stallability) {
455 outputNames, clock, go, reset, stall, name, stallability);
462void ScheduledPipelineOp::print(OpAsmPrinter &p) {
printPipelineOp(p, *
this); }
464ParseResult ScheduledPipelineOp::parse(OpAsmParser &parser,
465 OperationState &result) {
469void ScheduledPipelineOp::build(OpBuilder &odsBuilder, OperationState &odsState,
470 TypeRange dataOutputs, ValueRange inputs,
471 ArrayAttr inputNames, ArrayAttr outputNames,
472 Value clock, Value go, Value reset, Value stall,
473 StringAttr name, ArrayAttr stallability) {
475 outputNames, clock, go, reset, stall, name, stallability);
478Block *ScheduledPipelineOp::addStage() {
479 OpBuilder builder(getContext());
480 Block *stage = builder.createBlock(&getRegion());
483 stage->addArgument(builder.getIntegerType(1), getLoc());
487void ScheduledPipelineOp::getAsmBlockArgumentNames(
498static FailureOr<llvm::SmallVector<Block *>>
500 llvm::DenseSet<Block *> visited;
501 Block *currentStage = op.getEntryStage();
502 llvm::SmallVector<Block *> orderedStages;
504 if (!visited.insert(currentStage).second)
505 return op.emitOpError(
"pipeline contains a cycle.");
507 orderedStages.push_back(currentStage);
508 if (
auto stageOp = dyn_cast<StageOp>(currentStage->getTerminator()))
509 currentStage = stageOp.getNextStage();
511 currentStage =
nullptr;
512 }
while (currentStage);
514 return {orderedStages};
517llvm::SmallVector<Block *> ScheduledPipelineOp::getOrderedStages() {
523llvm::DenseMap<Block *, unsigned> ScheduledPipelineOp::getStageMap() {
524 llvm::DenseMap<Block *, unsigned> stageMap;
525 auto orderedStages = getOrderedStages();
526 for (
auto [index, stage] :
llvm::enumerate(orderedStages))
527 stageMap[stage] = index;
532Block *ScheduledPipelineOp::getLastStage() {
return getOrderedStages().back(); }
534bool ScheduledPipelineOp::isMaterialized() {
537 return llvm::any_of(getStages(), [
this](Block &block) {
539 if (&block == getEntryStage())
541 return block.getNumArguments() > 1;
545LogicalResult ScheduledPipelineOp::verify() {
547 auto &stages = getStages();
548 for (Block &stage : stages) {
549 if (stage.empty() || !isa<ReturnOp, StageOp>(stage.back()))
550 return emitOpError(
"all blocks must be terminated with a "
551 "`pipeline.stage` or `pipeline.return` op.");
558 for (
auto [i, block] :
llvm::enumerate(stages)) {
560 if (block.getNumArguments() != 0) {
562 dyn_cast<IntegerType>(block.getArguments().back().getType());
563 err = !lastArgType || lastArgType.getWidth() != 1;
566 return emitOpError(
"block " + std::to_string(i) +
567 " must have an i1 argument as the last block argument "
568 "(stage valid signal).");
573 llvm::DenseSet<Value> extLikeInputs;
574 for (
auto extInput : getExtInputs())
575 extLikeInputs.insert(extInput);
577 extLikeInputs.insert(getClock());
578 extLikeInputs.insert(getReset());
580 extLikeInputs.insert(getStall());
585 bool materialized = isMaterialized();
587 for (
auto &stage : stages) {
588 for (
auto &op : stage) {
589 for (
auto [index, operand] :
llvm::enumerate(op.getOperands())) {
591 if (extLikeInputs.contains(operand)) {
596 if (
auto *definingOp = operand.getDefiningOp()) {
598 if (definingOp->hasTrait<OpTrait::ConstantLike>())
600 err = definingOp->getBlock() != &stage;
603 err = !llvm::is_contained(stage.getArguments(), operand);
607 return op.emitOpError(
608 "Pipeline is in register materialized mode - operand ")
610 <<
" is defined in a different stage, which is illegal.";
616 if (
auto stallability = getStallability()) {
619 return emitOpError(
"cannot specify stallability without a stall signal.");
623 size_t nRegisterStages = stages.size() - 1;
624 if (stallability->size() != nRegisterStages)
625 return emitOpError(
"stallability array must be the same length as the "
626 "number of stages. Pipeline has ")
627 << nRegisterStages <<
" stages but array had "
628 << stallability->size() <<
" elements.";
634StageKind ScheduledPipelineOp::getStageKind(
size_t stageIndex) {
635 assert(stageIndex < getNumStages() &&
"invalid stage index");
638 return StageKind::Continuous;
642 std::optional<ArrayAttr> stallability = getStallability();
645 return StageKind::Stallable;
648 if (stageIndex < stallability->size()) {
649 bool stageIsStallable =
650 cast<BoolAttr>((*stallability)[stageIndex]).getValue();
651 if (!stageIsStallable) {
653 return StageKind::NonStallable;
661 return StageKind::Stallable;
663 for (
size_t i = stageIndex - 1; i > 0; --i) {
664 if (getStageKind(i) == StageKind::NonStallable)
665 return StageKind::Runoff;
667 return StageKind::Stallable;
674LogicalResult ReturnOp::verify() {
675 Operation *parent = getOperation()->getParentOp();
676 size_t nInputs = getInputs().size();
677 auto expectedResults = TypeRange(parent->getResultTypes()).drop_back();
678 size_t expectedNResults = expectedResults.size();
679 if (nInputs != expectedNResults)
680 return emitOpError(
"expected ")
681 << expectedNResults <<
" return values, got " << nInputs <<
".";
683 for (
auto [inType, reqType] :
684 llvm::zip(getInputs().getTypes(), expectedResults)) {
685 if (inType != reqType)
686 return emitOpError(
"expected return value of type ")
687 << reqType <<
", got " << inType <<
".";
702 OpAsmParser::UnresolvedOperand &v, Type &t,
706 if (succeeded(parser.parseOptionalString(&nameref))) {
708 return parser.emitError(parser.getCurrentLocation(),
709 "name cannot be empty");
711 if (failed(parser.parseEqual()))
712 return parser.emitError(parser.getCurrentLocation(),
713 "expected '=' after name");
714 name = parser.getBuilder().getStringAttr(nameref);
716 name = parser.getBuilder().getStringAttr(
"");
720 if (failed(parser.parseOperand(v)) || failed(parser.parseColonType(t)))
729 OpAsmParser &parser, OpAsmParser::UnresolvedOperand &v, Type &t,
730 llvm::SmallVector<OpAsmParser::UnresolvedOperand> &clockGates,
736 if (failed(parser.parseOptionalKeyword(
"gated")))
739 if (failed(parser.parseKeyword(
"by")) ||
741 parser.parseOperandList(clockGates, OpAsmParser::Delimiter::Square)))
752 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 4> ®isters,
753 llvm::SmallVector<mlir::Type, 1> ®isterTypes,
754 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 4> &clockGates,
755 ArrayAttr &clockGatesPerRegister, ArrayAttr ®isterNames) {
757 if (failed(parser.parseOptionalKeyword(
"regs"))) {
758 clockGatesPerRegister = parser.getBuilder().getI64ArrayAttr({});
762 llvm::SmallVector<int64_t> clockGatesPerRegisterList;
763 llvm::SmallVector<Attribute> registerNamesList;
764 bool withNames =
false;
765 if (failed(parser.parseCommaSeparatedList(AsmParser::Delimiter::Paren, [&]() {
766 OpAsmParser::UnresolvedOperand v;
768 llvm::SmallVector<OpAsmParser::UnresolvedOperand> cgs;
770 if (parseSingleStageRegister(parser, v, t, cgs, name))
772 registers.push_back(v);
773 registerTypes.push_back(t);
774 registerNamesList.push_back(name);
775 withNames |= static_cast<bool>(name);
776 llvm::append_range(clockGates, cgs);
777 clockGatesPerRegisterList.push_back(cgs.size());
782 clockGatesPerRegister =
783 parser.getBuilder().getI64ArrayAttr(clockGatesPerRegisterList);
785 registerNames = parser.getBuilder().getArrayAttr(registerNamesList);
791 TypeRange registerTypes, ValueRange clockGates,
792 ArrayAttr clockGatesPerRegister, ArrayAttr names) {
793 if (registers.empty())
797 size_t clockGateStartIdx = 0;
798 llvm::interleaveComma(
800 llvm::zip(registers, registerTypes, clockGatesPerRegister)),
802 size_t idx = it.index();
803 auto &[reg, type, nClockGatesAttr] = it.value();
805 if (
auto nameAttr = dyn_cast<StringAttr>(names[idx]);
806 nameAttr && !nameAttr.strref().
empty())
807 p << nameAttr <<
" = ";
810 p << reg <<
" : " << type;
811 int64_t nClockGates = cast<IntegerAttr>(nClockGatesAttr).getInt();
812 if (nClockGates == 0)
815 llvm::interleaveComma(clockGates.slice(clockGateStartIdx, nClockGates),
818 clockGateStartIdx += nClockGates;
824 TypeRange passthroughTypes, ArrayAttr names) {
826 if (passthroughs.empty())
830 llvm::interleaveComma(
831 llvm::enumerate(llvm::zip(passthroughs, passthroughTypes)), p,
833 size_t idx = it.index();
834 auto &[reg, type] = it.value();
836 if (
auto nameAttr = dyn_cast<StringAttr>(names[idx]);
837 nameAttr && !nameAttr.strref().
empty())
838 p << nameAttr <<
" = ";
840 p << reg <<
" : " << type;
849 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 4> &passthroughs,
850 llvm::SmallVector<mlir::Type, 1> &passthroughTypes,
851 ArrayAttr &passthroughNames) {
852 if (failed(parser.parseOptionalKeyword(
"pass")))
855 llvm::SmallVector<Attribute> passthroughsNameList;
856 bool withNames =
false;
857 if (failed(parser.parseCommaSeparatedList(AsmParser::Delimiter::Paren, [&]() {
858 OpAsmParser::UnresolvedOperand v;
861 if (parseOptNamedTypedAssignment(parser, v, t, name))
863 passthroughs.push_back(v);
864 passthroughTypes.push_back(t);
865 passthroughsNameList.push_back(name);
866 withNames |= static_cast<bool>(name);
872 passthroughNames = parser.getBuilder().getArrayAttr(passthroughsNameList);
877void StageOp::build(OpBuilder &odsBuilder, OperationState &odsState,
878 Block *dest, ValueRange registers,
879 ValueRange passthroughs) {
880 odsState.addSuccessors(dest);
881 odsState.addOperands(registers);
882 odsState.addOperands(passthroughs);
883 odsState.addAttribute(
"operandSegmentSizes",
884 odsBuilder.getDenseI32ArrayAttr(
885 {static_cast<int32_t>(registers.size()),
886 static_cast<int32_t>(passthroughs.size()),
887 static_cast<int32_t>(0)}));
888 llvm::SmallVector<int64_t> clockGatesPerRegister(registers.size(), 0);
889 odsState.addAttribute(
"clockGatesPerRegister",
890 odsBuilder.getI64ArrayAttr(clockGatesPerRegister));
893void StageOp::build(OpBuilder &odsBuilder, OperationState &odsState,
894 Block *dest, ValueRange registers, ValueRange passthroughs,
895 llvm::ArrayRef<llvm::SmallVector<Value>> clockGateList,
896 mlir::ArrayAttr registerNames,
897 mlir::ArrayAttr passthroughNames) {
898 build(odsBuilder, odsState, dest, registers, passthroughs);
900 llvm::SmallVector<Value> clockGates;
901 llvm::SmallVector<int64_t> clockGatesPerRegister(registers.size(), 0);
902 for (
auto gates : clockGateList) {
903 llvm::append_range(clockGates, gates);
904 clockGatesPerRegister.push_back(gates.size());
906 odsState.attributes.set(
"clockGatesPerRegister",
907 odsBuilder.getI64ArrayAttr(clockGatesPerRegister));
908 odsState.addOperands(clockGates);
911 odsState.addAttribute(
"registerNames", registerNames);
913 if (passthroughNames)
914 odsState.addAttribute(
"passthroughNames", passthroughNames);
917ValueRange StageOp::getClockGatesForReg(
unsigned regIdx) {
918 assert(regIdx < getRegisters().size() &&
"register index out of bounds.");
925 unsigned clockGateStartIdx = 0;
926 for (
auto [index, nClockGatesAttr] :
927 llvm::enumerate(getClockGatesPerRegister().getAsRange<IntegerAttr>())) {
928 int64_t nClockGates = nClockGatesAttr.getInt();
929 if (index == regIdx) {
931 return getClockGates().slice(clockGateStartIdx, nClockGates);
935 clockGateStartIdx += nClockGates;
938 llvm_unreachable(
"register index out of bounds.");
941LogicalResult StageOp::verify() {
944 llvm::SmallVector<Type> expectedTargetArgTypes;
945 llvm::append_range(expectedTargetArgTypes, getRegisters().getTypes());
946 llvm::append_range(expectedTargetArgTypes, getPassthroughs().getTypes());
947 Block *targetStage = getNextStage();
949 TypeRange targetStageArgTypes =
950 TypeRange(targetStage->getArgumentTypes()).drop_back();
952 if (targetStageArgTypes.size() != expectedTargetArgTypes.size())
953 return emitOpError(
"expected ") << expectedTargetArgTypes.size()
954 <<
" arguments in the target stage, got "
955 << targetStageArgTypes.size() <<
".";
957 for (
auto [index, it] :
llvm::enumerate(
958 llvm::zip(expectedTargetArgTypes, targetStageArgTypes))) {
959 auto [arg, barg] = it;
961 return emitOpError(
"expected target stage argument ")
962 << index <<
" to have type " << arg <<
", got " << barg <<
".";
967 if (getClockGatesPerRegister().size() != getRegisters().size())
968 return emitOpError(
"expected clockGatesPerRegister to be equally sized to "
969 "the number of registers.");
973 if (
auto regNames = getRegisterNames()) {
974 if (regNames->size() != getRegisters().size())
975 return emitOpError(
"expected registerNames to be equally sized to "
976 "the number of registers.");
981 if (
auto passthroughNames = getPassthroughNames()) {
982 if (passthroughNames->size() != getPassthroughs().size())
983 return emitOpError(
"expected passthroughNames to be equally sized to "
984 "the number of passthroughs.");
994LogicalResult LatencyOp::verify() {
995 ScheduledPipelineOp scheduledPipelineParent =
996 dyn_cast<ScheduledPipelineOp>(getOperation()->getParentOp());
998 if (!scheduledPipelineParent) {
1007 if (getNumResults() == 0)
1008 return emitOpError(
"expected at least one result type.");
1012 size_t latency = getLatency();
1013 Block *definingStage = getOperation()->getBlock();
1015 llvm::DenseMap<Block *, unsigned> stageMap =
1016 scheduledPipelineParent.getStageMap();
1018 auto stageDistance = [&](
Block *from,
Block *to) {
1019 assert(stageMap.count(from) &&
"stage 'from' not contained in pipeline");
1020 assert(stageMap.count(to) &&
"stage 'to' not contained in pipeline");
1021 int64_t fromStage = stageMap[from];
1022 int64_t toStage = stageMap[to];
1023 return toStage - fromStage;
1026 for (
auto [i, res] :
llvm::enumerate(getResults())) {
1027 for (
auto &use : res.getUses()) {
1028 auto *user = use.getOwner();
1035 unsigned useDistance = stageDistance(definingStage, userStage);
1039 StageOp stageOp = dyn_cast<StageOp>(user);
1040 if (userStage == definingStage && stageOp) {
1041 if (llvm::is_contained(stageOp.getPassthroughs(), res))
1048 if (useDistance < latency) {
1049 auto diag = emitOpError(
"result ")
1050 << i <<
" is used before it is available.";
1051 diag.attachNote(user->getLoc())
1052 <<
"use was operand " << use.getOperandNumber()
1053 <<
". The result is available " << latency - useDistance
1054 <<
" stages later than this use.";
1066LogicalResult LatencyReturnOp::verify() {
1067 LatencyOp parent = cast<LatencyOp>(getOperation()->getParentOp());
1068 size_t nInputs = getInputs().size();
1069 size_t nResults = parent->getNumResults();
1070 if (nInputs != nResults)
1071 return emitOpError(
"expected ")
1072 << nResults <<
" return values, got " << nInputs <<
".";
1074 for (
auto [inType, reqType] :
1075 llvm::zip(getInputs().getTypes(), parent->getResultTypes())) {
1076 if (inType != reqType)
1077 return emitOpError(
"expected return value of type ")
1078 << reqType <<
", got " << inType <<
".";
1084#define GET_OP_CLASSES
1085#include "circt/Dialect/Pipeline/Pipeline.cpp.inc"
1087void PipelineDialect::initialize() {
1090#include "circt/Dialect/Pipeline/Pipeline.cpp.inc"
assert(baseType &&"element must be base type")
static InstancePath empty
static void buildPipelineLikeOp(OpBuilder &odsBuilder, OperationState &odsState, TypeRange dataOutputs, ValueRange inputs, ArrayAttr inputNames, ArrayAttr outputNames, Value clock, Value go, Value reset, Value stall, StringAttr name, ArrayAttr stallability)
static ParseResult parseSingleStageRegister(OpAsmParser &parser, OpAsmParser::UnresolvedOperand &v, Type &t, llvm::SmallVector< OpAsmParser::UnresolvedOperand > &clockGates, StringAttr &name)
static FailureOr< llvm::SmallVector< Block * > > getOrderedStagesFailable(ScheduledPipelineOp op)
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)
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 getPipelineAsmResultNames(TPipelineOp op, OpAsmSetValueNameFn setNameFn)
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