CIRCT  20.0.0git
IbisPrepareScheduling.cpp
Go to the documentation of this file.
1 //===- IbisPrepareScheduling.cpp - Prepares ibis static blocks for scheduling //
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
11 #include "mlir/Pass/Pass.h"
12 
20 
22 #include "circt/Support/SymCache.h"
23 #include "mlir/Transforms/DialectConversion.h"
24 
25 namespace circt {
26 namespace ibis {
27 #define GEN_PASS_DEF_IBISPREPARESCHEDULING
28 #include "circt/Dialect/Ibis/IbisPasses.h.inc"
29 } // namespace ibis
30 } // namespace circt
31 
32 using namespace circt;
33 using namespace ibis;
34 
35 namespace {
36 
37 struct PrepareSchedulingPass
38  : public circt::ibis::impl::IbisPrepareSchedulingBase<
39  PrepareSchedulingPass> {
40  void runOnOperation() override;
41 
42  // Prepares the given sblock for scheduling by moving its body into a
43  // pipeline.unscheduled operation, and returns the pipeline op.
44  pipeline::UnscheduledPipelineOp prepareSBlock(IsolatedStaticBlockOp sblock);
45 
46  // Iterates over the operations in the pipeline, looks up the corresponding
47  // operator in the provided operator library, and attaches the operator type
48  // to the op.
49  LogicalResult attachOperatorTypes(pipeline::UnscheduledPipelineOp pipeline);
50 };
51 } // anonymous namespace
52 
53 pipeline::UnscheduledPipelineOp
54 PrepareSchedulingPass::prepareSBlock(IsolatedStaticBlockOp sblock) {
55  Location loc = sblock.getLoc();
56  Block *bodyBlock = sblock.getBodyBlock();
57  auto b = OpBuilder::atBlockBegin(bodyBlock);
58  auto ph = b.create<ibis::PipelineHeaderOp>(loc);
59 
60  // Create a pipeline.unscheduled operation which returns the same types
61  // as that returned by the sblock.
62  auto sblockRet = cast<BlockReturnOp>(bodyBlock->getTerminator());
63  auto retTypes = sblockRet.getOperandTypes();
64 
65  // Generate in- and output names.
66  SmallVector<Attribute> inNames, outNames;
67  for (size_t i = 0, e = bodyBlock->getNumArguments(); i < e; ++i)
68  inNames.push_back(b.getStringAttr("in" + std::to_string(i)));
69  for (size_t i = 0, e = retTypes.size(); i < e; ++i)
70  outNames.push_back(b.getStringAttr("out" + std::to_string(i)));
71 
72  auto pipeline = b.create<pipeline::UnscheduledPipelineOp>(
73  loc, retTypes, bodyBlock->getArguments(), b.getArrayAttr(inNames),
74  b.getArrayAttr(outNames), ph.getClock(), ph.getReset(), ph.getGo(),
75  ph.getStall());
76  b.setInsertionPointToEnd(pipeline.getEntryStage());
77 
78  // First, we replace all of the operands of the return op with the values
79  // generated by the pipeline. This ensures that argument of the sblock that
80  // is directly returned without being modified by an operation inside the
81  // sblock is still being passed through the pipeline. While doing so, we
82  // sneakily also set the pipeline return values so that it will reflect the
83  // later value replacements.
84  auto pipelineRet = b.create<pipeline::ReturnOp>(loc, sblockRet.getOperands());
85  for (size_t i = 0, e = retTypes.size(); i < e; ++i)
86  sblockRet.setOperand(i, pipeline.getResult(i));
87 
88  // Next, we can replace all of the sblock argument uses within the pipeline
89  // with the pipeline arguments.
90  for (auto [sbArg, plArg] :
91  llvm::zip(bodyBlock->getArguments(),
92  pipeline.getEntryStage()->getArguments())) {
93  sbArg.replaceAllUsesExcept(plArg, pipeline);
94  }
95 
96  // And now we're safe to move the body of the sblock into the pipeline.
97  // Drop the 2 first ops (pipeline, pipeline header) and the back (the return
98  // op). Block::getOperations doesn't play nicely with ArrayRef's so have to
99  // copy it...
100  llvm::SmallVector<Operation *> opsToMove;
101  llvm::transform(bodyBlock->getOperations(), std::back_inserter(opsToMove),
102  [&](Operation &op) { return &op; });
103  for (Operation *op :
104  ArrayRef(opsToMove.begin(), opsToMove.end()).drop_front(2).drop_back())
105  op->moveBefore(pipelineRet);
106 
107  return pipeline;
108 }
109 
110 LogicalResult PrepareSchedulingPass::attachOperatorTypes(
111  pipeline::UnscheduledPipelineOp pipeline) {
112  for (Operation &op : *pipeline.getEntryStage()) {
113  if (op.hasAttr("ssp.operator_type"))
114  continue;
115 
116  // Skip unscheduled pipeline ops.
117  if (isa<pipeline::ReturnOp>(op) || op.hasTrait<OpTrait::ConstantLike>())
118  continue;
119 
120  // The operator lib convention just assumes that the exact
121  // operation name is present in the library.
122  op.setAttr(
123  "ssp.operator_type",
124  FlatSymbolRefAttr::get(op.getContext(), op.getName().getStringRef()));
125  }
126 
127  // Attach the operator lib attribute
128  pipeline->setAttr(
129  "operator_lib",
130  FlatSymbolRefAttr::get(pipeline.getContext(), kIbisOperatorLibName));
131 
132  return success();
133 }
134 
135 void PrepareSchedulingPass::runOnOperation() {
136  pipeline::UnscheduledPipelineOp pipeline = prepareSBlock(getOperation());
137  if (failed(attachOperatorTypes(pipeline)))
138  return signalPassFailure();
139 }
140 
142  return std::make_unique<PrepareSchedulingPass>();
143 }
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
static constexpr const char * kIbisOperatorLibName
Definition: IbisOps.h:36
std::unique_ptr< mlir::Pass > createPrepareSchedulingPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21