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 (v.isa<BlockArgument>())
75 v.cast<BlockArgument>().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(name.template cast<StringAttr>().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 = (*regNames)[regI].dyn_cast<StringAttr>();
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 (*passthroughNames)[passthroughI].dyn_cast<StringAttr>();
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());
467 stage->addArgument(
builder.getIntegerType(1), getLoc());
471 void ScheduledPipelineOp::getAsmBlockArgumentNames(
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;
529 LogicalResult ScheduledPipelineOp::verify() {
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 block.getArguments().back().getType().dyn_cast<IntegerType>();
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 size_t nStages = getNumStages();
620 assert(stageIndex < nStages &&
"invalid stage index");
623 return StageKind::Continuous;
627 std::optional<ArrayAttr> stallability = getStallability();
630 return StageKind::Stallable;
633 if (stageIndex < stallability->size()) {
634 bool stageIsStallable =
635 (*stallability)[stageIndex].cast<BoolAttr>().getValue();
636 if (!stageIsStallable) {
638 return StageKind::NonStallable;
646 return StageKind::Stallable;
648 for (
size_t i = stageIndex - 1; i > 0; --i) {
649 if (getStageKind(i) == StageKind::NonStallable)
650 return StageKind::Runoff;
652 return StageKind::Stallable;
659 LogicalResult ReturnOp::verify() {
660 Operation *parent = getOperation()->getParentOp();
661 size_t nInputs = getInputs().size();
662 auto expectedResults = TypeRange(parent->getResultTypes()).drop_back();
663 size_t expectedNResults = expectedResults.size();
664 if (nInputs != expectedNResults)
665 return emitOpError(
"expected ")
666 << expectedNResults <<
" return values, got " << nInputs <<
".";
668 for (
auto [inType, reqType] :
669 llvm::zip(getInputs().getTypes(), expectedResults)) {
670 if (inType != reqType)
671 return emitOpError(
"expected return value of type ")
672 << reqType <<
", got " << inType <<
".";
687 OpAsmParser::UnresolvedOperand &v, Type &t,
691 if (succeeded(parser.parseOptionalString(&nameref))) {
693 return parser.emitError(parser.getCurrentLocation(),
694 "name cannot be empty");
696 if (failed(parser.parseEqual()))
697 return parser.emitError(parser.getCurrentLocation(),
698 "expected '=' after name");
699 name = parser.getBuilder().getStringAttr(nameref);
701 name = parser.getBuilder().getStringAttr(
"");
705 if (failed(parser.parseOperand(v)) || failed(parser.parseColonType(t)))
714 OpAsmParser &parser, OpAsmParser::UnresolvedOperand &v, Type &t,
715 llvm::SmallVector<OpAsmParser::UnresolvedOperand> &clockGates,
721 if (failed(parser.parseOptionalKeyword(
"gated")))
724 if (failed(parser.parseKeyword(
"by")) ||
726 parser.parseOperandList(clockGates, OpAsmParser::Delimiter::Square)))
737 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 4> ®isters,
738 llvm::SmallVector<mlir::Type, 1> ®isterTypes,
739 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 4> &clockGates,
740 ArrayAttr &clockGatesPerRegister, ArrayAttr ®isterNames) {
742 if (failed(parser.parseOptionalKeyword(
"regs"))) {
743 clockGatesPerRegister = parser.getBuilder().getI64ArrayAttr({});
747 llvm::SmallVector<int64_t> clockGatesPerRegisterList;
748 llvm::SmallVector<Attribute> registerNamesList;
749 bool withNames =
false;
750 if (failed(parser.parseCommaSeparatedList(AsmParser::Delimiter::Paren, [&]() {
751 OpAsmParser::UnresolvedOperand v;
753 llvm::SmallVector<OpAsmParser::UnresolvedOperand> cgs;
755 if (parseSingleStageRegister(parser, v, t, cgs, name))
757 registers.push_back(v);
758 registerTypes.push_back(t);
759 registerNamesList.push_back(name);
760 withNames |= static_cast<bool>(name);
761 llvm::append_range(clockGates, cgs);
762 clockGatesPerRegisterList.push_back(cgs.size());
767 clockGatesPerRegister =
768 parser.getBuilder().getI64ArrayAttr(clockGatesPerRegisterList);
770 registerNames = parser.getBuilder().getArrayAttr(registerNamesList);
776 TypeRange registerTypes, ValueRange clockGates,
777 ArrayAttr clockGatesPerRegister, ArrayAttr names) {
778 if (registers.empty())
782 size_t clockGateStartIdx = 0;
783 llvm::interleaveComma(
785 llvm::zip(registers, registerTypes, clockGatesPerRegister)),
787 size_t idx = it.index();
788 auto &[
reg, type, nClockGatesAttr] = it.value();
790 if (
auto nameAttr = names[idx].dyn_cast<StringAttr>();
791 nameAttr && !nameAttr.strref().
empty())
792 p << nameAttr <<
" = ";
795 p <<
reg <<
" : " << type;
796 int64_t nClockGates =
797 nClockGatesAttr.template cast<IntegerAttr>().getInt();
798 if (nClockGates == 0)
801 llvm::interleaveComma(clockGates.slice(clockGateStartIdx, nClockGates),
804 clockGateStartIdx += nClockGates;
810 TypeRange passthroughTypes, ArrayAttr names) {
812 if (passthroughs.empty())
816 llvm::interleaveComma(
817 llvm::enumerate(llvm::zip(passthroughs, passthroughTypes)), p,
819 size_t idx = it.index();
820 auto &[
reg, type] = it.value();
822 if (
auto nameAttr = names[idx].dyn_cast<StringAttr>();
823 nameAttr && !nameAttr.strref().
empty())
824 p << nameAttr <<
" = ";
826 p <<
reg <<
" : " << type;
835 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 4> &passthroughs,
836 llvm::SmallVector<mlir::Type, 1> &passthroughTypes,
837 ArrayAttr &passthroughNames) {
838 if (failed(parser.parseOptionalKeyword(
"pass")))
841 llvm::SmallVector<Attribute> passthroughsNameList;
842 bool withNames =
false;
843 if (failed(parser.parseCommaSeparatedList(AsmParser::Delimiter::Paren, [&]() {
844 OpAsmParser::UnresolvedOperand v;
847 if (parseOptNamedTypedAssignment(parser, v, t, name))
849 passthroughs.push_back(v);
850 passthroughTypes.push_back(t);
851 passthroughsNameList.push_back(name);
852 withNames |= static_cast<bool>(name);
858 passthroughNames = parser.getBuilder().getArrayAttr(passthroughsNameList);
863 void StageOp::build(OpBuilder &odsBuilder, OperationState &odsState,
864 Block *dest, ValueRange registers,
865 ValueRange passthroughs) {
866 odsState.addSuccessors(dest);
867 odsState.addOperands(registers);
868 odsState.addOperands(passthroughs);
869 odsState.addAttribute(
"operandSegmentSizes",
870 odsBuilder.getDenseI32ArrayAttr(
871 {static_cast<int32_t>(registers.size()),
872 static_cast<int32_t>(passthroughs.size()),
873 static_cast<int32_t>(0)}));
874 llvm::SmallVector<int64_t> clockGatesPerRegister(registers.size(), 0);
875 odsState.addAttribute(
"clockGatesPerRegister",
876 odsBuilder.getI64ArrayAttr(clockGatesPerRegister));
879 void StageOp::build(OpBuilder &odsBuilder, OperationState &odsState,
880 Block *dest, ValueRange registers, ValueRange passthroughs,
881 llvm::ArrayRef<llvm::SmallVector<Value>> clockGateList,
882 mlir::ArrayAttr registerNames,
883 mlir::ArrayAttr passthroughNames) {
884 build(odsBuilder, odsState, dest, registers, passthroughs);
886 llvm::SmallVector<Value> clockGates;
887 llvm::SmallVector<int64_t> clockGatesPerRegister(registers.size(), 0);
888 for (
auto gates : clockGateList) {
889 llvm::append_range(clockGates, gates);
890 clockGatesPerRegister.push_back(gates.size());
892 odsState.attributes.set(
"clockGatesPerRegister",
893 odsBuilder.getI64ArrayAttr(clockGatesPerRegister));
894 odsState.addOperands(clockGates);
897 odsState.addAttribute(
"registerNames", registerNames);
899 if (passthroughNames)
900 odsState.addAttribute(
"passthroughNames", passthroughNames);
903 ValueRange StageOp::getClockGatesForReg(
unsigned regIdx) {
904 assert(regIdx < getRegisters().size() &&
"register index out of bounds.");
911 unsigned clockGateStartIdx = 0;
912 for (
auto [index, nClockGatesAttr] :
913 llvm::enumerate(getClockGatesPerRegister().getAsRange<IntegerAttr>())) {
914 int64_t nClockGates = nClockGatesAttr.getInt();
915 if (index == regIdx) {
917 return getClockGates().slice(clockGateStartIdx, nClockGates);
921 clockGateStartIdx += nClockGates;
924 llvm_unreachable(
"register index out of bounds.");
927 LogicalResult StageOp::verify() {
930 llvm::SmallVector<Type> expectedTargetArgTypes;
931 llvm::append_range(expectedTargetArgTypes, getRegisters().getTypes());
932 llvm::append_range(expectedTargetArgTypes, getPassthroughs().getTypes());
933 Block *targetStage = getNextStage();
935 TypeRange targetStageArgTypes =
936 TypeRange(targetStage->getArgumentTypes()).drop_back();
938 if (targetStageArgTypes.size() != expectedTargetArgTypes.size())
939 return emitOpError(
"expected ") << expectedTargetArgTypes.size()
940 <<
" arguments in the target stage, got "
941 << targetStageArgTypes.size() <<
".";
943 for (
auto [index, it] : llvm::enumerate(
944 llvm::zip(expectedTargetArgTypes, targetStageArgTypes))) {
945 auto [arg, barg] = it;
947 return emitOpError(
"expected target stage argument ")
948 << index <<
" to have type " << arg <<
", got " << barg <<
".";
953 if (getClockGatesPerRegister().size() != getRegisters().size())
954 return emitOpError(
"expected clockGatesPerRegister to be equally sized to "
955 "the number of registers.");
959 if (
auto regNames = getRegisterNames()) {
960 if (regNames->size() != getRegisters().size())
961 return emitOpError(
"expected registerNames to be equally sized to "
962 "the number of registers.");
967 if (
auto passthroughNames = getPassthroughNames()) {
968 if (passthroughNames->size() != getPassthroughs().size())
969 return emitOpError(
"expected passthroughNames to be equally sized to "
970 "the number of passthroughs.");
980 LogicalResult LatencyOp::verify() {
981 ScheduledPipelineOp scheduledPipelineParent =
982 dyn_cast<ScheduledPipelineOp>(getOperation()->getParentOp());
984 if (!scheduledPipelineParent) {
992 size_t latency = getLatency();
993 Block *definingStage = getOperation()->getBlock();
995 llvm::DenseMap<Block *, unsigned> stageMap =
996 scheduledPipelineParent.getStageMap();
998 auto stageDistance = [&](
Block *from,
Block *to) {
999 assert(stageMap.count(from) &&
"stage 'from' not contained in pipeline");
1000 assert(stageMap.count(to) &&
"stage 'to' not contained in pipeline");
1001 int64_t fromStage = stageMap[from];
1002 int64_t toStage = stageMap[to];
1003 return toStage - fromStage;
1006 for (
auto [i, res] : llvm::enumerate(getResults())) {
1007 for (
auto &use : res.getUses()) {
1008 auto *user = use.getOwner();
1015 unsigned useDistance = stageDistance(definingStage, userStage);
1019 StageOp stageOp = dyn_cast<StageOp>(user);
1020 if (userStage == definingStage && stageOp) {
1021 if (llvm::is_contained(stageOp.getPassthroughs(), res))
1028 if (useDistance < latency) {
1029 auto diag = emitOpError(
"result ")
1030 << i <<
" is used before it is available.";
1031 diag.attachNote(user->getLoc())
1032 <<
"use was operand " << use.getOperandNumber()
1033 <<
". The result is available " << latency - useDistance
1034 <<
" stages later than this use.";
1046 LogicalResult LatencyReturnOp::verify() {
1047 LatencyOp parent = cast<LatencyOp>(getOperation()->getParentOp());
1048 size_t nInputs = getInputs().size();
1049 size_t nResults = parent->getNumResults();
1050 if (nInputs != nResults)
1051 return emitOpError(
"expected ")
1052 << nResults <<
" return values, got " << nInputs <<
".";
1054 for (
auto [inType, reqType] :
1055 llvm::zip(getInputs().getTypes(), parent->getResultTypes())) {
1056 if (inType != reqType)
1057 return emitOpError(
"expected return value of type ")
1058 << reqType <<
", got " << inType <<
".";
1064 #define GET_OP_CLASSES
1065 #include "circt/Dialect/Pipeline/Pipeline.cpp.inc"
1067 void PipelineDialect::initialize() {
1070 #include "circt/Dialect/Pipeline/Pipeline.cpp.inc"
assert(baseType &&"element must be base type")
static InstancePath empty
llvm::SmallVector< StringAttr > inputs
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)
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)
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)