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;
551 if (current == stopAt)
553 if (current == parent)
555 current = current->getParentOp()->getBlock();
566 Block *useBlock = use.getOwner()->getBlock();
567 Block *definingBlock = use.get().getParentBlock();
570 "use` must originate from within `stage`");
573 if (useBlock == definingBlock || stage == definingBlock)
578 Block *currBlock = definingBlock;
582LogicalResult ScheduledPipelineOp::verify() {
584 auto &stages = getStages();
585 for (Block &stage : stages) {
586 if (stage.empty() || !isa<ReturnOp, StageOp>(stage.back()))
587 return emitOpError(
"all blocks must be terminated with a "
588 "`pipeline.stage` or `pipeline.return` op.");
595 for (
auto [i, block] :
llvm::enumerate(stages)) {
597 if (block.getNumArguments() != 0) {
599 dyn_cast<IntegerType>(block.getArguments().back().getType());
600 err = !lastArgType || lastArgType.getWidth() != 1;
603 return emitOpError(
"block " + std::to_string(i) +
604 " must have an i1 argument as the last block argument "
605 "(stage valid signal).");
610 llvm::DenseSet<Value> extLikeInputs;
611 for (
auto extInput : getExtInputs())
612 extLikeInputs.insert(extInput);
614 extLikeInputs.insert(getClock());
615 extLikeInputs.insert(getReset());
617 extLikeInputs.insert(getStall());
622 bool materialized = isMaterialized();
623 Block *parentBlock = getOperation()->getBlock();
624 for (
auto &stage : stages) {
625 auto walkRes = stage.walk([&](Operation *op) {
627 if (isa<SourceOp>(op)) {
630 "Pipeline is in register materialized mode - pipeline.src "
631 "operations are not allowed");
632 return WalkResult::interrupt();
638 return WalkResult::advance();
641 for (
auto [index, operand] :
llvm::enumerate(op->getOpOperands())) {
644 if (extLikeInputs.contains(operand.get()))
648 if (
auto *definingOp = operand.get().getDefiningOp()) {
650 if (definingOp->hasTrait<OpTrait::ConstantLike>())
657 auto err = op->emitOpError(
"operand ")
658 << index <<
" is defined in a different stage. ";
660 err <<
"Value should have been passed through block arguments";
662 err <<
"Value should have been passed through a `pipeline.src` "
665 return WalkResult::interrupt();
669 return WalkResult::advance();
672 if (walkRes.wasInterrupted())
676 if (
auto stallability = getStallability()) {
679 return emitOpError(
"cannot specify stallability without a stall signal.");
683 size_t nRegisterStages = stages.size() - 1;
684 if (stallability->size() != nRegisterStages)
685 return emitOpError(
"stallability array must be the same length as the "
686 "number of stages. Pipeline has ")
687 << nRegisterStages <<
" stages but array had "
688 << stallability->size() <<
" elements.";
694StageKind ScheduledPipelineOp::getStageKind(
size_t stageIndex) {
695 assert(stageIndex < getNumStages() &&
"invalid stage index");
698 return StageKind::Continuous;
702 std::optional<ArrayAttr> stallability = getStallability();
705 return StageKind::Stallable;
708 if (stageIndex < stallability->size()) {
709 bool stageIsStallable =
710 cast<BoolAttr>((*stallability)[stageIndex]).getValue();
711 if (!stageIsStallable) {
713 return StageKind::NonStallable;
721 return StageKind::Stallable;
723 for (
size_t i = stageIndex - 1; i > 0; --i) {
724 if (getStageKind(i) == StageKind::NonStallable)
725 return StageKind::Runoff;
727 return StageKind::Stallable;
734LogicalResult ReturnOp::verify() {
735 Operation *parent = getOperation()->getParentOp();
736 size_t nInputs = getInputs().size();
737 auto expectedResults = TypeRange(parent->getResultTypes()).drop_back();
738 size_t expectedNResults = expectedResults.size();
739 if (nInputs != expectedNResults)
740 return emitOpError(
"expected ")
741 << expectedNResults <<
" return values, got " << nInputs <<
".";
743 for (
auto [inType, reqType] :
744 llvm::zip(getInputs().getTypes(), expectedResults)) {
745 if (inType != reqType)
746 return emitOpError(
"expected return value of type ")
747 << reqType <<
", got " << inType <<
".";
762 OpAsmParser::UnresolvedOperand &v, Type &t,
766 if (succeeded(parser.parseOptionalString(&nameref))) {
768 return parser.emitError(parser.getCurrentLocation(),
769 "name cannot be empty");
771 if (failed(parser.parseEqual()))
772 return parser.emitError(parser.getCurrentLocation(),
773 "expected '=' after name");
774 name = parser.getBuilder().getStringAttr(nameref);
776 name = parser.getBuilder().getStringAttr(
"");
780 if (failed(parser.parseOperand(v)) || failed(parser.parseColonType(t)))
789 OpAsmParser &parser, OpAsmParser::UnresolvedOperand &v, Type &t,
790 llvm::SmallVector<OpAsmParser::UnresolvedOperand> &clockGates,
796 if (failed(parser.parseOptionalKeyword(
"gated")))
799 if (failed(parser.parseKeyword(
"by")) ||
801 parser.parseOperandList(clockGates, OpAsmParser::Delimiter::Square)))
812 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 4> ®isters,
813 llvm::SmallVector<mlir::Type, 1> ®isterTypes,
814 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 4> &clockGates,
815 ArrayAttr &clockGatesPerRegister, ArrayAttr ®isterNames) {
817 if (failed(parser.parseOptionalKeyword(
"regs"))) {
818 clockGatesPerRegister = parser.getBuilder().getI64ArrayAttr({});
822 llvm::SmallVector<int64_t> clockGatesPerRegisterList;
823 llvm::SmallVector<Attribute> registerNamesList;
824 bool withNames =
false;
825 if (failed(parser.parseCommaSeparatedList(AsmParser::Delimiter::Paren, [&]() {
826 OpAsmParser::UnresolvedOperand v;
828 llvm::SmallVector<OpAsmParser::UnresolvedOperand> cgs;
830 if (parseSingleStageRegister(parser, v, t, cgs, name))
832 registers.push_back(v);
833 registerTypes.push_back(t);
834 registerNamesList.push_back(name);
835 withNames |= static_cast<bool>(name);
836 llvm::append_range(clockGates, cgs);
837 clockGatesPerRegisterList.push_back(cgs.size());
842 clockGatesPerRegister =
843 parser.getBuilder().getI64ArrayAttr(clockGatesPerRegisterList);
845 registerNames = parser.getBuilder().getArrayAttr(registerNamesList);
851 TypeRange registerTypes, ValueRange clockGates,
852 ArrayAttr clockGatesPerRegister, ArrayAttr names) {
853 if (registers.empty())
857 size_t clockGateStartIdx = 0;
858 llvm::interleaveComma(
860 llvm::zip(registers, registerTypes, clockGatesPerRegister)),
862 size_t idx = it.index();
863 auto &[reg, type, nClockGatesAttr] = it.value();
865 if (
auto nameAttr = dyn_cast<StringAttr>(names[idx]);
866 nameAttr && !nameAttr.strref().
empty())
867 p << nameAttr <<
" = ";
870 p << reg <<
" : " << type;
871 int64_t nClockGates = cast<IntegerAttr>(nClockGatesAttr).getInt();
872 if (nClockGates == 0)
875 llvm::interleaveComma(clockGates.slice(clockGateStartIdx, nClockGates),
878 clockGateStartIdx += nClockGates;
884 TypeRange passthroughTypes, ArrayAttr names) {
886 if (passthroughs.empty())
890 llvm::interleaveComma(
891 llvm::enumerate(llvm::zip(passthroughs, passthroughTypes)), p,
893 size_t idx = it.index();
894 auto &[reg, type] = it.value();
896 if (
auto nameAttr = dyn_cast<StringAttr>(names[idx]);
897 nameAttr && !nameAttr.strref().
empty())
898 p << nameAttr <<
" = ";
900 p << reg <<
" : " << type;
909 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 4> &passthroughs,
910 llvm::SmallVector<mlir::Type, 1> &passthroughTypes,
911 ArrayAttr &passthroughNames) {
912 if (failed(parser.parseOptionalKeyword(
"pass")))
915 llvm::SmallVector<Attribute> passthroughsNameList;
916 bool withNames =
false;
917 if (failed(parser.parseCommaSeparatedList(AsmParser::Delimiter::Paren, [&]() {
918 OpAsmParser::UnresolvedOperand v;
921 if (parseOptNamedTypedAssignment(parser, v, t, name))
923 passthroughs.push_back(v);
924 passthroughTypes.push_back(t);
925 passthroughsNameList.push_back(name);
926 withNames |= static_cast<bool>(name);
932 passthroughNames = parser.getBuilder().getArrayAttr(passthroughsNameList);
937void StageOp::build(OpBuilder &odsBuilder, OperationState &odsState,
938 Block *dest, ValueRange registers,
939 ValueRange passthroughs) {
940 odsState.addSuccessors(dest);
941 odsState.addOperands(registers);
942 odsState.addOperands(passthroughs);
943 odsState.addAttribute(
"operandSegmentSizes",
944 odsBuilder.getDenseI32ArrayAttr(
945 {static_cast<int32_t>(registers.size()),
946 static_cast<int32_t>(passthroughs.size()),
947 static_cast<int32_t>(0)}));
948 llvm::SmallVector<int64_t> clockGatesPerRegister(registers.size(), 0);
949 odsState.addAttribute(
"clockGatesPerRegister",
950 odsBuilder.getI64ArrayAttr(clockGatesPerRegister));
953void StageOp::build(OpBuilder &odsBuilder, OperationState &odsState,
954 Block *dest, ValueRange registers, ValueRange passthroughs,
955 llvm::ArrayRef<llvm::SmallVector<Value>> clockGateList,
956 mlir::ArrayAttr registerNames,
957 mlir::ArrayAttr passthroughNames) {
958 build(odsBuilder, odsState, dest, registers, passthroughs);
960 llvm::SmallVector<Value> clockGates;
961 llvm::SmallVector<int64_t> clockGatesPerRegister(registers.size(), 0);
962 for (
auto gates : clockGateList) {
963 llvm::append_range(clockGates, gates);
964 clockGatesPerRegister.push_back(gates.size());
966 odsState.attributes.set(
"clockGatesPerRegister",
967 odsBuilder.getI64ArrayAttr(clockGatesPerRegister));
968 odsState.addOperands(clockGates);
971 odsState.addAttribute(
"registerNames", registerNames);
973 if (passthroughNames)
974 odsState.addAttribute(
"passthroughNames", passthroughNames);
977ValueRange StageOp::getClockGatesForReg(
unsigned regIdx) {
978 assert(regIdx < getRegisters().size() &&
"register index out of bounds.");
985 unsigned clockGateStartIdx = 0;
986 for (
auto [index, nClockGatesAttr] :
987 llvm::enumerate(getClockGatesPerRegister().getAsRange<IntegerAttr>())) {
988 int64_t nClockGates = nClockGatesAttr.getInt();
989 if (index == regIdx) {
991 return getClockGates().slice(clockGateStartIdx, nClockGates);
995 clockGateStartIdx += nClockGates;
998 llvm_unreachable(
"register index out of bounds.");
1001LogicalResult StageOp::verify() {
1004 llvm::SmallVector<Type> expectedTargetArgTypes;
1005 llvm::append_range(expectedTargetArgTypes, getRegisters().getTypes());
1006 llvm::append_range(expectedTargetArgTypes, getPassthroughs().getTypes());
1007 Block *targetStage = getNextStage();
1009 TypeRange targetStageArgTypes =
1010 TypeRange(targetStage->getArgumentTypes()).drop_back();
1012 if (targetStageArgTypes.size() != expectedTargetArgTypes.size())
1013 return emitOpError(
"expected ") << expectedTargetArgTypes.size()
1014 <<
" arguments in the target stage, got "
1015 << targetStageArgTypes.size() <<
".";
1017 for (
auto [index, it] :
llvm::enumerate(
1018 llvm::zip(expectedTargetArgTypes, targetStageArgTypes))) {
1019 auto [arg, barg] = it;
1021 return emitOpError(
"expected target stage argument ")
1022 << index <<
" to have type " << arg <<
", got " << barg <<
".";
1027 if (getClockGatesPerRegister().size() != getRegisters().size())
1028 return emitOpError(
"expected clockGatesPerRegister to be equally sized to "
1029 "the number of registers.");
1033 if (
auto regNames = getRegisterNames()) {
1034 if (regNames->size() != getRegisters().size())
1035 return emitOpError(
"expected registerNames to be equally sized to "
1036 "the number of registers.");
1041 if (
auto passthroughNames = getPassthroughNames()) {
1042 if (passthroughNames->size() != getPassthroughs().size())
1043 return emitOpError(
"expected passthroughNames to be equally sized to "
1044 "the number of passthroughs.");
1054LogicalResult LatencyOp::verify() {
1055 ScheduledPipelineOp scheduledPipelineParent =
1056 dyn_cast<ScheduledPipelineOp>(getOperation()->getParentOp());
1058 if (!scheduledPipelineParent) {
1067 if (getNumResults() == 0)
1068 return emitOpError(
"expected at least one result type.");
1072 size_t latency = getLatency();
1073 Block *definingStage = getOperation()->getBlock();
1075 llvm::DenseMap<Block *, unsigned> stageMap =
1076 scheduledPipelineParent.getStageMap();
1078 auto stageDistance = [&](
Block *from,
Block *to) {
1079 assert(stageMap.count(from) &&
"stage 'from' not contained in pipeline");
1080 assert(stageMap.count(to) &&
"stage 'to' not contained in pipeline");
1081 int64_t fromStage = stageMap[from];
1082 int64_t toStage = stageMap[to];
1083 return toStage - fromStage;
1086 for (
auto [i, res] :
llvm::enumerate(getResults())) {
1087 for (
auto &use : res.getUses()) {
1088 auto *user = use.getOwner();
1095 unsigned useDistance = stageDistance(definingStage, userStage);
1099 StageOp stageOp = dyn_cast<StageOp>(user);
1100 if (userStage == definingStage && stageOp) {
1101 if (llvm::is_contained(stageOp.getPassthroughs(), res))
1108 if (useDistance < latency) {
1109 auto diag = emitOpError(
"result ")
1110 << i <<
" is used before it is available.";
1111 diag.attachNote(user->getLoc())
1112 <<
"use was operand " << use.getOperandNumber()
1113 <<
". The result is available " << latency - useDistance
1114 <<
" stages later than this use.";
1126LogicalResult LatencyReturnOp::verify() {
1127 LatencyOp parent = cast<LatencyOp>(getOperation()->getParentOp());
1128 size_t nInputs = getInputs().size();
1129 size_t nResults = parent->getNumResults();
1130 if (nInputs != nResults)
1131 return emitOpError(
"expected ")
1132 << nResults <<
" return values, got " << nInputs <<
".";
1134 for (
auto [inType, reqType] :
1135 llvm::zip(getInputs().getTypes(), parent->getResultTypes())) {
1136 if (inType != reqType)
1137 return emitOpError(
"expected return value of type ")
1138 << reqType <<
", got " << inType <<
".";
1144#define GET_OP_CLASSES
1145#include "circt/Dialect/Pipeline/Pipeline.cpp.inc"
1147void PipelineDialect::initialize() {
1150#include "circt/Dialect/Pipeline/Pipeline.cpp.inc"
assert(baseType &&"element must be base type")
static InstancePath empty
static Location getLoc(DefSlot slot)
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)
static bool useDefinedInStage(Block *stopAt, Block *stage, OpOperand &use)
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 bool isNestedBlock(Block *stopAt, Block *parent, Block *current)
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