Allow creating standalone Regions
Currently, regions can only be constructed by passing in a `Function` or an
`Instruction` pointer referencing the parent object, unlike `Function`s or
`Instruction`s themselves that can be created without a parent. It leads to a
rather complex flow in operation construction where one has to create the
operation first before being able to work with its regions. It may be
necessary to work with the regions before the operation is created. In
particular, in `build` and `parse` functions that are executed _before_ the
operation is created in cases where boilerplate region manipulation is required
(for example, inserting the hypothetical default terminator in affine regions).
Allow creating standalone regions. Such regions are meant to own a list of
blocks and transfer them to other regions on demand.
Each instruction stores a fixed number of regions as trailing objects and has
ownership of them. This decreases the size of the Instruction object for the
common case of instructions without regions. Keep this behavior intact. To
allow some flexibility in construction, make OperationState store an owning
vector of regions. When the Builder creates an Instruction from
OperationState, the bodies of the regions are transferred into the
instruction-owned regions to minimize copying. Thus, it becomes possible to
fill standalone regions with blocks and move them to an operation when it is
constructed, or move blocks from a region to an operation region, e.g., for
inlining.
PiperOrigin-RevId: 240368183
2019-03-27 00:55:06 +08:00
|
|
|
//===- OperationSupport.cpp -----------------------------------------------===//
|
|
|
|
//
|
|
|
|
// Copyright 2019 The MLIR Authors.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
// =============================================================================
|
|
|
|
//
|
|
|
|
// This file contains out-of-line implementations of the support types that
|
2019-03-27 05:45:38 +08:00
|
|
|
// Operation and related classes build on top of.
|
Allow creating standalone Regions
Currently, regions can only be constructed by passing in a `Function` or an
`Instruction` pointer referencing the parent object, unlike `Function`s or
`Instruction`s themselves that can be created without a parent. It leads to a
rather complex flow in operation construction where one has to create the
operation first before being able to work with its regions. It may be
necessary to work with the regions before the operation is created. In
particular, in `build` and `parse` functions that are executed _before_ the
operation is created in cases where boilerplate region manipulation is required
(for example, inserting the hypothetical default terminator in affine regions).
Allow creating standalone regions. Such regions are meant to own a list of
blocks and transfer them to other regions on demand.
Each instruction stores a fixed number of regions as trailing objects and has
ownership of them. This decreases the size of the Instruction object for the
common case of instructions without regions. Keep this behavior intact. To
allow some flexibility in construction, make OperationState store an owning
vector of regions. When the Builder creates an Instruction from
OperationState, the bodies of the regions are transferred into the
instruction-owned regions to minimize copying. Thus, it becomes possible to
fill standalone regions with blocks and move them to an operation when it is
constructed, or move blocks from a region to an operation region, e.g., for
inlining.
PiperOrigin-RevId: 240368183
2019-03-27 00:55:06 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "mlir/IR/OperationSupport.h"
|
|
|
|
#include "mlir/IR/Block.h"
|
2019-03-27 05:45:38 +08:00
|
|
|
#include "mlir/IR/Operation.h"
|
|
|
|
using namespace mlir;
|
Allow creating standalone Regions
Currently, regions can only be constructed by passing in a `Function` or an
`Instruction` pointer referencing the parent object, unlike `Function`s or
`Instruction`s themselves that can be created without a parent. It leads to a
rather complex flow in operation construction where one has to create the
operation first before being able to work with its regions. It may be
necessary to work with the regions before the operation is created. In
particular, in `build` and `parse` functions that are executed _before_ the
operation is created in cases where boilerplate region manipulation is required
(for example, inserting the hypothetical default terminator in affine regions).
Allow creating standalone regions. Such regions are meant to own a list of
blocks and transfer them to other regions on demand.
Each instruction stores a fixed number of regions as trailing objects and has
ownership of them. This decreases the size of the Instruction object for the
common case of instructions without regions. Keep this behavior intact. To
allow some flexibility in construction, make OperationState store an owning
vector of regions. When the Builder creates an Instruction from
OperationState, the bodies of the regions are transferred into the
instruction-owned regions to minimize copying. Thus, it becomes possible to
fill standalone regions with blocks and move them to an operation when it is
constructed, or move blocks from a region to an operation region, e.g., for
inlining.
PiperOrigin-RevId: 240368183
2019-03-27 00:55:06 +08:00
|
|
|
|
2019-03-27 05:45:38 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// OperationState
|
|
|
|
//===----------------------------------------------------------------------===//
|
Allow creating standalone Regions
Currently, regions can only be constructed by passing in a `Function` or an
`Instruction` pointer referencing the parent object, unlike `Function`s or
`Instruction`s themselves that can be created without a parent. It leads to a
rather complex flow in operation construction where one has to create the
operation first before being able to work with its regions. It may be
necessary to work with the regions before the operation is created. In
particular, in `build` and `parse` functions that are executed _before_ the
operation is created in cases where boilerplate region manipulation is required
(for example, inserting the hypothetical default terminator in affine regions).
Allow creating standalone regions. Such regions are meant to own a list of
blocks and transfer them to other regions on demand.
Each instruction stores a fixed number of regions as trailing objects and has
ownership of them. This decreases the size of the Instruction object for the
common case of instructions without regions. Keep this behavior intact. To
allow some flexibility in construction, make OperationState store an owning
vector of regions. When the Builder creates an Instruction from
OperationState, the bodies of the regions are transferred into the
instruction-owned regions to minimize copying. Thus, it becomes possible to
fill standalone regions with blocks and move them to an operation when it is
constructed, or move blocks from a region to an operation region, e.g., for
inlining.
PiperOrigin-RevId: 240368183
2019-03-27 00:55:06 +08:00
|
|
|
|
|
|
|
OperationState::OperationState(MLIRContext *context, Location location,
|
|
|
|
StringRef name)
|
|
|
|
: context(context), location(location), name(name, context) {}
|
|
|
|
|
|
|
|
OperationState::OperationState(MLIRContext *context, Location location,
|
|
|
|
OperationName name)
|
|
|
|
: context(context), location(location), name(name) {}
|
|
|
|
|
|
|
|
OperationState::OperationState(MLIRContext *context, Location location,
|
|
|
|
StringRef name, ArrayRef<Value *> operands,
|
|
|
|
ArrayRef<Type> types,
|
|
|
|
ArrayRef<NamedAttribute> attributes,
|
|
|
|
ArrayRef<Block *> successors,
|
|
|
|
MutableArrayRef<std::unique_ptr<Region>> regions,
|
|
|
|
bool resizableOperandList)
|
|
|
|
: context(context), location(location), name(name, context),
|
|
|
|
operands(operands.begin(), operands.end()),
|
|
|
|
types(types.begin(), types.end()),
|
|
|
|
attributes(attributes.begin(), attributes.end()),
|
|
|
|
successors(successors.begin(), successors.end()) {
|
|
|
|
for (std::unique_ptr<Region> &r : regions) {
|
|
|
|
this->regions.push_back(std::move(r));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Region *OperationState::addRegion() {
|
|
|
|
regions.emplace_back(new Region);
|
|
|
|
return regions.back().get();
|
|
|
|
}
|
|
|
|
|
|
|
|
void OperationState::addRegion(std::unique_ptr<Region> &®ion) {
|
|
|
|
regions.push_back(std::move(region));
|
|
|
|
}
|
|
|
|
|
2019-03-27 05:45:38 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// OperandStorage
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
/// Replace the operands contained in the storage with the ones provided in
|
|
|
|
/// 'operands'.
|
|
|
|
void detail::OperandStorage::setOperands(Operation *owner,
|
|
|
|
ArrayRef<Value *> operands) {
|
|
|
|
// If the number of operands is less than or equal to the current amount, we
|
|
|
|
// can just update in place.
|
|
|
|
if (operands.size() <= numOperands) {
|
|
|
|
auto instOperands = getInstOperands();
|
|
|
|
|
|
|
|
// If the number of new operands is less than the current count, then remove
|
|
|
|
// any extra operands.
|
|
|
|
for (unsigned i = operands.size(); i != numOperands; ++i)
|
|
|
|
instOperands[i].~InstOperand();
|
|
|
|
|
|
|
|
// Set the operands in place.
|
|
|
|
numOperands = operands.size();
|
|
|
|
for (unsigned i = 0; i != numOperands; ++i)
|
|
|
|
instOperands[i].set(operands[i]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, we need to be resizable.
|
|
|
|
assert(resizable && "Only resizable operations may add operands");
|
|
|
|
|
|
|
|
// Grow the capacity if necessary.
|
|
|
|
auto &resizeUtil = getResizableStorage();
|
|
|
|
if (resizeUtil.capacity < operands.size())
|
|
|
|
grow(resizeUtil, operands.size());
|
|
|
|
|
|
|
|
// Set the operands.
|
|
|
|
InstOperand *opBegin = getRawOperands();
|
|
|
|
for (unsigned i = 0; i != numOperands; ++i)
|
|
|
|
opBegin[i].set(operands[i]);
|
|
|
|
for (unsigned e = operands.size(); numOperands != e; ++numOperands)
|
|
|
|
new (&opBegin[numOperands]) InstOperand(owner, operands[numOperands]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Erase an operand held by the storage.
|
|
|
|
void detail::OperandStorage::eraseOperand(unsigned index) {
|
|
|
|
assert(index < size());
|
|
|
|
auto Operands = getInstOperands();
|
|
|
|
--numOperands;
|
|
|
|
|
|
|
|
// Shift all operands down by 1 if the operand to remove is not at the end.
|
|
|
|
if (index != numOperands)
|
|
|
|
std::rotate(&Operands[index], &Operands[index + 1], &Operands[numOperands]);
|
|
|
|
Operands[numOperands].~InstOperand();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Grow the internal operand storage.
|
|
|
|
void detail::OperandStorage::grow(ResizableStorage &resizeUtil,
|
|
|
|
size_t minSize) {
|
|
|
|
// Allocate a new storage array.
|
|
|
|
resizeUtil.capacity =
|
|
|
|
std::max(size_t(llvm::NextPowerOf2(resizeUtil.capacity + 2)), minSize);
|
|
|
|
InstOperand *newStorage = static_cast<InstOperand *>(
|
|
|
|
llvm::safe_malloc(resizeUtil.capacity * sizeof(InstOperand)));
|
|
|
|
|
|
|
|
// Move the current operands to the new storage.
|
|
|
|
auto operands = getInstOperands();
|
|
|
|
std::uninitialized_copy(std::make_move_iterator(operands.begin()),
|
|
|
|
std::make_move_iterator(operands.end()), newStorage);
|
|
|
|
|
|
|
|
// Destroy the original operands and update the resizable storage pointer.
|
|
|
|
for (auto &operand : operands)
|
|
|
|
operand.~InstOperand();
|
|
|
|
resizeUtil.setDynamicStorage(newStorage);
|
|
|
|
}
|