[mlir] ensureRegionTerminator: take OpBuilder

The SingleBlockImplicitTerminator op trait provides a function
`ensureRegionTerminator` that injects an appropriate terminator into the block
if necessary, which is used during operation constructing and parsing.
Currently, this function directly modifies the IR using low-level APIs on
Operation and Block. If this function is called from a conversion pattern,
these manipulations are not reflected in the ConversionPatternRewriter and thus
cannot be undone or, worse, lead to tricky memory errors and malformed IR.
Change `ensureRegionTerminator` to take an instance of `OpBuilder` instead of
`Builder`, and use it to construct the block and the terminator when required.
Maintain overloads taking an instance of `Builder` and creating a simple
`OpBuilder` to use in parsers, which don't have an `OpBuilder` and cannot
interact with the dialect conversion mechanism. This change was one of the
reasons to make `<OpTy>::build` accept an `OpBuilder`.

Differential Revision: https://reviews.llvm.org/D80138
This commit is contained in:
Alex Zinenko
2020-05-20 16:00:57 +02:00
parent a7d88a9038
commit 3ccf4a5bd1
2 changed files with 41 additions and 18 deletions

View File

@@ -79,17 +79,12 @@ namespace impl {
/// is empty, insert a new block first. `buildTerminatorOp` should return the
/// terminator operation to insert.
void ensureRegionTerminator(
Region &region, Location loc,
function_ref<Operation *(OpBuilder &)> buildTerminatorOp);
/// Templated version that fills the generates the provided operation type.
template <typename OpTy>
void ensureRegionTerminator(Region &region, Builder &builder, Location loc) {
ensureRegionTerminator(region, loc, [&](OpBuilder &b) {
OperationState state(loc, OpTy::getOperationName());
OpTy::build(b, state);
return Operation::create(state);
});
}
Region &region, OpBuilder &builder, Location loc,
function_ref<Operation *(OpBuilder &, Location)> buildTerminatorOp);
void ensureRegionTerminator(
Region &region, Builder &builder, Location loc,
function_ref<Operation *(OpBuilder &, Location)> buildTerminatorOp);
} // namespace impl
/// This is the concrete base class that holds the operation pointer and has
@@ -1077,6 +1072,15 @@ public:
template <typename TerminatorOpType> struct SingleBlockImplicitTerminator {
template <typename ConcreteType>
class Impl : public TraitBase<ConcreteType, Impl> {
private:
/// Builds a terminator operation without relying on OpBuilder APIs to avoid
/// cyclic header inclusion.
static Operation *buildTerminator(OpBuilder &builder, Location loc) {
OperationState state(loc, TerminatorOpType::getOperationName());
TerminatorOpType::build(builder, state);
return Operation::create(state);
}
public:
static LogicalResult verifyTrait(Operation *op) {
for (unsigned i = 0, e = op->getNumRegions(); i < e; ++i) {
@@ -1112,10 +1116,19 @@ template <typename TerminatorOpType> struct SingleBlockImplicitTerminator {
}
/// Ensure that the given region has the terminator required by this trait.
/// If OpBuilder is provided, use it to build the terminator and notify the
/// OpBuilder litsteners accoridngly. If only a Builder is provided, locally
/// construct an OpBuilder with no listeners; this should only be used if no
/// OpBuilder is available at the call site, e.g., in the parser.
static void ensureTerminator(Region &region, Builder &builder,
Location loc) {
::mlir::impl::template ensureRegionTerminator<TerminatorOpType>(
region, builder, loc);
::mlir::impl::ensureRegionTerminator(region, builder, loc,
buildTerminator);
}
static void ensureTerminator(Region &region, OpBuilder &builder,
Location loc) {
::mlir::impl::ensureRegionTerminator(region, builder, loc,
buildTerminator);
}
Block *getBody(unsigned idx = 0) {

View File

@@ -1099,17 +1099,27 @@ Value impl::foldCastOp(Operation *op) {
/// is empty, insert a new block first. `buildTerminatorOp` should return the
/// terminator operation to insert.
void impl::ensureRegionTerminator(
Region &region, Location loc,
function_ref<Operation *(OpBuilder &)> buildTerminatorOp) {
Region &region, OpBuilder &builder, Location loc,
function_ref<Operation *(OpBuilder &, Location)> buildTerminatorOp) {
OpBuilder::InsertionGuard guard(builder);
if (region.empty())
region.push_back(new Block);
builder.createBlock(&region);
Block &block = region.back();
if (!block.empty() && block.back().isKnownTerminator())
return;
OpBuilder builder(loc.getContext());
block.push_back(buildTerminatorOp(builder));
builder.setInsertionPointToEnd(&block);
builder.insert(buildTerminatorOp(builder, loc));
}
/// Create a simple OpBuilder and forward to the OpBuilder version of this
/// function.
void impl::ensureRegionTerminator(
Region &region, Builder &builder, Location loc,
function_ref<Operation *(OpBuilder &, Location)> buildTerminatorOp) {
OpBuilder opBuilder(builder.getContext());
ensureRegionTerminator(region, opBuilder, loc, buildTerminatorOp);
}
//===----------------------------------------------------------------------===//