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