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