CIRCT  19.0.0git
IbisReblockPass.cpp
Go to the documentation of this file.
1 //===- IbisReblockPass.cpp ------------------------------------------------===//
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 
17 
19 #include "mlir/Transforms/DialectConversion.h"
20 
21 namespace circt {
22 namespace ibis {
23 #define GEN_PASS_DEF_IBISREBLOCK
24 #include "circt/Dialect/Ibis/IbisPasses.h.inc"
25 } // namespace ibis
26 } // namespace circt
27 
28 using namespace circt;
29 using namespace ibis;
30 
31 namespace {
32 
33 struct ReblockPass : public circt::ibis::impl::IbisReblockBase<ReblockPass> {
34  void runOnOperation() override;
35 
36  // Transforms an `ibis.sblock.inline.begin/end` scope into an `ibis.sblock`.
37  LogicalResult reblock(ArrayRef<Operation *> ops, Operation *blockTerminator);
38 
39  /// Track ops that should be erased.
40  SmallVector<Operation *> opsToErase;
41 };
42 
43 // Returns true if the given op signal that the existing set of sblock
44 // operations should be closed.
45 static bool isSBlockTerminator(Operation *op) {
46  return op->hasTrait<OpTrait::IsTerminator>() ||
47  isa<ibis::InlineStaticBlockEndOp, InlineStaticBlockBeginOp>(op);
48 }
49 
50 } // anonymous namespace
51 
52 void ReblockPass::runOnOperation() {
53  MethodOp parent = getOperation();
54 
55  llvm::SmallVector<Operation *> opsToBlock;
56  for (Block &block : parent.getRegion()) {
57  for (Operation &op : llvm::make_early_inc_range(block)) {
58  if (isSBlockTerminator(&op)) {
59  if (opsToBlock.empty())
60  continue;
61  if (failed(reblock(opsToBlock, &op)))
62  return signalPassFailure();
63  opsToBlock.clear();
64  if (isa<InlineStaticBlockBeginOp>(op))
65  opsToBlock.push_back(&op);
66  } else
67  opsToBlock.push_back(&op);
68  }
69  }
70 
71  llvm::for_each(opsToErase, [](Operation *op) { op->erase(); });
72 }
73 
74 LogicalResult ReblockPass::reblock(ArrayRef<Operation *> ops,
75  Operation *blockTerminator) {
76  // Determine which values we need to return from within the block scope.
77  // This is done by collecting all values defined within the start/end scope,
78  // and recording uses that exist outside of the scope.
79  ibis::InlineStaticBlockBeginOp blockBeginOp;
80  if (isa<InlineStaticBlockEndOp>(blockTerminator)) {
81  blockBeginOp = dyn_cast<InlineStaticBlockBeginOp>(ops.front());
82  assert(blockBeginOp &&
83  "Expected block begin op when a block end block was provided");
84  ops = ops.drop_front();
85  }
86 
87  Operation *beginOp = ops.front();
88  auto startIt = ops.front()->getIterator();
89  Operation *endOp = ops.back();
90  auto terminatorIt = blockTerminator->getIterator();
91  Block *sblockParentBlock = beginOp->getBlock();
92 
93  auto usedOutsideBlock = [&](OpOperand &use) {
94  Operation *owner = use.getOwner();
95  Block *useBlock = owner->getBlock();
96  if (useBlock != sblockParentBlock)
97  return true;
98  bool isBefore = owner->isBeforeInBlock(beginOp);
99  bool isAfter = isBefore ? false : endOp->isBeforeInBlock(owner);
100  return isBefore || isAfter;
101  };
102 
103  llvm::MapVector<Value, llvm::SmallVector<OpOperand *>> returnValueUses;
104  for (auto &op : llvm::make_range(startIt, terminatorIt)) {
105  for (Value result : op.getResults()) {
106  for (OpOperand &use : result.getUses())
107  if (usedOutsideBlock(use))
108  returnValueUses[result].push_back(&use);
109  }
110  }
111 
112  // Gather the set of types that needs to be returned from within the block.
113  llvm::SmallVector<Type> blockRetTypes;
114  llvm::SmallVector<Value> blockRetValues;
115  for (auto &[v, _] : returnValueUses) {
116  blockRetTypes.push_back(v.getType());
117  blockRetValues.push_back(v);
118  }
119 
120  auto b = OpBuilder(beginOp);
121  auto ibisBlock =
122  b.create<StaticBlockOp>(beginOp->getLoc(), blockRetTypes, ValueRange{});
123 
124  // The new `ibis.sblock` should inherit the attributes of the block begin op,
125  // if provided.
126  if (blockBeginOp)
127  ibisBlock->setAttrs(blockBeginOp->getAttrs());
128 
129  // Move operations into the `ibis.sblock` op.
130  BlockReturnOp blockReturn =
131  cast<BlockReturnOp>(ibisBlock.getBodyBlock()->getTerminator());
132 
133  for (auto &op :
134  llvm::make_early_inc_range(llvm::make_range(startIt, terminatorIt)))
135  op.moveBefore(blockReturn);
136 
137  // Append the terminator operands to the block return.
138  blockReturn->setOperands(blockRetValues);
139 
140  // Replace the uses of the returned values outside of the block with the
141  // block return values.
142  for (auto [blockRet, innerDefAndUses] :
143  llvm::zip(ibisBlock.getResults(), returnValueUses)) {
144  auto &uses = std::get<1>(innerDefAndUses);
145  for (OpOperand *use : uses)
146  use->set(blockRet);
147  }
148 
149  // If this was an explicit sblock, erase the markers.
150  if (blockBeginOp) {
151  opsToErase.push_back(blockBeginOp);
152  opsToErase.push_back(blockTerminator);
153  }
154 
155  return success();
156 }
157 
158 std::unique_ptr<Pass> circt::ibis::createReblockPass() {
159  return std::make_unique<ReblockPass>();
160 }
assert(baseType &&"element must be base type")
std::unique_ptr< mlir::Pass > createReblockPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21