Coverage Report

Created: 2020-06-26 05:44

/home/arjun/llvm-project/mlir/lib/IR/Region.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- Region.cpp - MLIR Region Class -------------------------------------===//
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 "mlir/IR/Region.h"
10
#include "mlir/IR/BlockAndValueMapping.h"
11
#include "mlir/IR/Operation.h"
12
using namespace mlir;
13
14
0
Region::Region(Operation *container) : container(container) {}
15
16
0
Region::~Region() {
17
0
  // Operations may have cyclic references, which need to be dropped before we
18
0
  // can start deleting them.
19
0
  dropAllReferences();
20
0
}
21
22
/// Return the context this region is inserted in. The region must have a valid
23
/// parent container.
24
0
MLIRContext *Region::getContext() {
25
0
  assert(container && "region is not attached to a container");
26
0
  return container->getContext();
27
0
}
28
29
/// Return a location for this region. This is the location attached to the
30
/// parent container. The region must have a valid parent container.
31
0
Location Region::getLoc() {
32
0
  assert(container && "region is not attached to a container");
33
0
  return container->getLoc();
34
0
}
35
36
0
Region *Region::getParentRegion() {
37
0
  assert(container && "region is not attached to a container");
38
0
  return container->getParentRegion();
39
0
}
40
41
0
Operation *Region::getParentOp() { return container; }
42
43
0
bool Region::isProperAncestor(Region *other) {
44
0
  if (this == other)
45
0
    return false;
46
0
47
0
  while ((other = other->getParentRegion())) {
48
0
    if (this == other)
49
0
      return true;
50
0
  }
51
0
  return false;
52
0
}
53
54
/// Return the number of this region in the parent operation.
55
0
unsigned Region::getRegionNumber() {
56
0
  // Regions are always stored consecutively, so use pointer subtraction to
57
0
  // figure out what number this is.
58
0
  return this - &getParentOp()->getRegions()[0];
59
0
}
60
61
/// Clone the internal blocks from this region into `dest`. Any
62
/// cloned blocks are appended to the back of dest.
63
0
void Region::cloneInto(Region *dest, BlockAndValueMapping &mapper) {
64
0
  assert(dest && "expected valid region to clone into");
65
0
  cloneInto(dest, dest->end(), mapper);
66
0
}
67
68
/// Clone this region into 'dest' before the given position in 'dest'.
69
void Region::cloneInto(Region *dest, Region::iterator destPos,
70
0
                       BlockAndValueMapping &mapper) {
71
0
  assert(dest && "expected valid region to clone into");
72
0
  assert(this != dest && "cannot clone region into itself");
73
0
74
0
  // If the list is empty there is nothing to clone.
75
0
  if (empty())
76
0
    return;
77
0
78
0
  for (Block &block : *this) {
79
0
    Block *newBlock = new Block();
80
0
    mapper.map(&block, newBlock);
81
0
82
0
    // Clone the block arguments. The user might be deleting arguments to the
83
0
    // block by specifying them in the mapper. If so, we don't add the
84
0
    // argument to the cloned block.
85
0
    for (auto arg : block.getArguments())
86
0
      if (!mapper.contains(arg))
87
0
        mapper.map(arg, newBlock->addArgument(arg.getType()));
88
0
89
0
    // Clone and remap the operations within this block.
90
0
    for (auto &op : block)
91
0
      newBlock->push_back(op.clone(mapper));
92
0
93
0
    dest->getBlocks().insert(destPos, newBlock);
94
0
  }
95
0
96
0
  // Now that each of the blocks have been cloned, go through and remap the
97
0
  // operands of each of the operations.
98
0
  auto remapOperands = [&](Operation *op) {
99
0
    for (auto &operand : op->getOpOperands())
100
0
      if (auto mappedOp = mapper.lookupOrNull(operand.get()))
101
0
        operand.set(mappedOp);
102
0
    for (auto &succOp : op->getBlockOperands())
103
0
      if (auto *mappedOp = mapper.lookupOrNull(succOp.get()))
104
0
        succOp.set(mappedOp);
105
0
  };
106
0
107
0
  for (iterator it(mapper.lookup(&front())); it != destPos; ++it)
108
0
    it->walk(remapOperands);
109
0
}
110
111
/// Returns 'block' if 'block' lies in this region, or otherwise finds the
112
/// ancestor of 'block' that lies in this region. Returns nullptr if the latter
113
/// fails.
114
0
Block *Region::findAncestorBlockInRegion(Block &block) {
115
0
  auto currBlock = █
116
0
  while (currBlock->getParent() != this) {
117
0
    Operation *parentOp = currBlock->getParentOp();
118
0
    if (!parentOp || !parentOp->getBlock())
119
0
      return nullptr;
120
0
    currBlock = parentOp->getBlock();
121
0
  }
122
0
  return currBlock;
123
0
}
124
125
0
void Region::dropAllReferences() {
126
0
  for (Block &b : *this)
127
0
    b.dropAllReferences();
128
0
}
129
130
/// Check if there are any values used by operations in `region` defined
131
/// outside its ancestor region `limit`.  That is, given `A{B{C{}}}` with region
132
/// `C` and limit `B`, the values defined in `B` can be used but the values
133
/// defined in `A` cannot.  Emit errors if `noteLoc` is provided; this location
134
/// is used to point to the operation containing the region, the actual error is
135
/// reported at the operation with an offending use.
136
static bool isIsolatedAbove(Region &region, Region &limit,
137
0
                            Optional<Location> noteLoc) {
138
0
  assert(limit.isAncestor(&region) &&
139
0
         "expected isolation limit to be an ancestor of the given region");
140
0
141
0
  // List of regions to analyze.  Each region is processed independently, with
142
0
  // respect to the common `limit` region, so we can look at them in any order.
143
0
  // Therefore, use a simple vector and push/pop back the current region.
144
0
  SmallVector<Region *, 8> pendingRegions;
145
0
  pendingRegions.push_back(&region);
146
0
147
0
  // Traverse all operations in the region.
148
0
  while (!pendingRegions.empty()) {
149
0
    for (Operation &op : pendingRegions.pop_back_val()->getOps()) {
150
0
      for (Value operand : op.getOperands()) {
151
0
        // operand should be non-null here if the IR is well-formed. But
152
0
        // we don't assert here as this function is called from the verifier
153
0
        // and so could be called on invalid IR.
154
0
        if (!operand) {
155
0
          if (noteLoc)
156
0
            op.emitOpError("block's operand not defined").attachNote(noteLoc);
157
0
          return false;
158
0
        }
159
0
160
0
        // Check that any value that is used by an operation is defined in the
161
0
        // same region as either an operation result or a block argument.
162
0
        if (operand.getParentRegion()->isProperAncestor(&limit)) {
163
0
          if (noteLoc) {
164
0
            op.emitOpError("using value defined outside the region")
165
0
                    .attachNote(noteLoc)
166
0
                << "required by region isolation constraints";
167
0
          }
168
0
          return false;
169
0
        }
170
0
      }
171
0
      // Schedule any regions the operations contain for further checking.
172
0
      pendingRegions.reserve(pendingRegions.size() + op.getNumRegions());
173
0
      for (Region &subRegion : op.getRegions())
174
0
        pendingRegions.push_back(&subRegion);
175
0
    }
176
0
  }
177
0
  return true;
178
0
}
179
180
0
bool Region::isIsolatedFromAbove(Optional<Location> noteLoc) {
181
0
  return isIsolatedAbove(*this, *this, noteLoc);
182
0
}
183
184
0
Region *llvm::ilist_traits<::mlir::Block>::getParentRegion() {
185
0
  size_t Offset(
186
0
      size_t(&((Region *)nullptr->*Region::getSublistAccess(nullptr))));
187
0
  iplist<Block> *Anchor(static_cast<iplist<Block> *>(this));
188
0
  return reinterpret_cast<Region *>(reinterpret_cast<char *>(Anchor) - Offset);
189
0
}
190
191
/// This is a trait method invoked when a basic block is added to a region.
192
/// We keep the region pointer up to date.
193
0
void llvm::ilist_traits<::mlir::Block>::addNodeToList(Block *block) {
194
0
  assert(!block->getParent() && "already in a region!");
195
0
  block->parentValidOpOrderPair.setPointer(getParentRegion());
196
0
}
197
198
/// This is a trait method invoked when an operation is removed from a
199
/// region.  We keep the region pointer up to date.
200
0
void llvm::ilist_traits<::mlir::Block>::removeNodeFromList(Block *block) {
201
0
  assert(block->getParent() && "not already in a region!");
202
0
  block->parentValidOpOrderPair.setPointer(nullptr);
203
0
}
204
205
/// This is a trait method invoked when an operation is moved from one block
206
/// to another.  We keep the block pointer up to date.
207
void llvm::ilist_traits<::mlir::Block>::transferNodesFromList(
208
0
    ilist_traits<Block> &otherList, block_iterator first, block_iterator last) {
209
0
  // If we are transferring operations within the same function, the parent
210
0
  // pointer doesn't need to be updated.
211
0
  auto *curParent = getParentRegion();
212
0
  if (curParent == otherList.getParentRegion())
213
0
    return;
214
0
215
0
  // Update the 'parent' member of each Block.
216
0
  for (; first != last; ++first)
217
0
    first->parentValidOpOrderPair.setPointer(curParent);
218
0
}
219
220
//===----------------------------------------------------------------------===//
221
// Region::OpIterator
222
//===----------------------------------------------------------------------===//
223
224
Region::OpIterator::OpIterator(Region *region, bool end)
225
0
    : region(region), block(end ? region->end() : region->begin()) {
226
0
  if (!region->empty())
227
0
    skipOverBlocksWithNoOps();
228
0
}
229
230
0
Region::OpIterator &Region::OpIterator::operator++() {
231
0
  // We increment over operations, if we reach the last use then move to next
232
0
  // block.
233
0
  if (operation != block->end())
234
0
    ++operation;
235
0
  if (operation == block->end()) {
236
0
    ++block;
237
0
    skipOverBlocksWithNoOps();
238
0
  }
239
0
  return *this;
240
0
}
241
242
0
void Region::OpIterator::skipOverBlocksWithNoOps() {
243
0
  while (block != region->end() && block->empty())
244
0
    ++block;
245
0
246
0
  // If we are at the last block, then set the operation to first operation of
247
0
  // next block (sentinel value used for end).
248
0
  if (block == region->end())
249
0
    operation = {};
250
0
  else
251
0
    operation = block->begin();
252
0
}
253
254
//===----------------------------------------------------------------------===//
255
// RegionRange
256
//===----------------------------------------------------------------------===//
257
258
RegionRange::RegionRange(MutableArrayRef<Region> regions)
259
0
    : RegionRange(regions.data(), regions.size()) {}
260
RegionRange::RegionRange(ArrayRef<std::unique_ptr<Region>> regions)
261
0
    : RegionRange(regions.data(), regions.size()) {}
262
263
/// See `llvm::detail::indexed_accessor_range_base` for details.
264
RegionRange::OwnerT RegionRange::offset_base(const OwnerT &owner,
265
0
                                             ptrdiff_t index) {
266
0
  if (auto *operand = owner.dyn_cast<const std::unique_ptr<Region> *>())
267
0
    return operand + index;
268
0
  return &owner.get<Region *>()[index];
269
0
}
270
/// See `llvm::detail::indexed_accessor_range_base` for details.
271
Region *RegionRange::dereference_iterator(const OwnerT &owner,
272
0
                                          ptrdiff_t index) {
273
0
  if (auto *operand = owner.dyn_cast<const std::unique_ptr<Region> *>())
274
0
    return operand[index].get();
275
0
  return &owner.get<Region *>()[index];
276
0
}