namespace mlir {
class AffineBound;
class AffineValueMap;
+class AffineTerminatorOp;
class FlatAffineConstraints;
class OpBuilder;
let parser = [{ return ::parse$cppClass(parser, result); }];
}
-def AffineForOp : Affine_Op<"for"> {
+def AffineForOp : Affine_Op<"for",
+ [NativeOpTrait<"SingleBlockImplicitTerminator<AffineTerminatorOp>::Impl">]>
+ {
let summary = "for operation";
let description = [{
The "affine.for" operation represents an affine loop nest, defining an SSA
let hasCanonicalizer = 1;
}
-def AffineIfOp : Affine_Op<"if"> {
+def AffineIfOp : Affine_Op<"if",
+ [NativeOpTrait<"SingleBlockImplicitTerminator<AffineTerminatorOp>::Impl">]>
+ {
let summary = "if-then-else operation";
let description = [{
The "if" operation represents an if-then-else construct for conditionally
namespace mlir {
namespace loop {
+class TerminatorOp;
+
class LoopOpsDialect : public Dialect {
public:
LoopOpsDialect(MLIRContext *context);
let parser = [{ return ::parse$cppClass(parser, result); }];
}
-def ForOp : Loop_Op<"for"> {
+def ForOp : Loop_Op<"for",
+ [NativeOpTrait<"SingleBlockImplicitTerminator<TerminatorOp>::Impl">]> {
let summary = "for operation";
let description = [{
The "loop.for" operation represents a loop nest taking 3 SSA value as
}];
}
-def IfOp : Loop_Op<"if"> {
+def IfOp : Loop_Op<"if",
+ [NativeOpTrait<"SingleBlockImplicitTerminator<TerminatorOp>::Impl">]> {
let summary = "if-then-else operation";
let description = [{
The "loop.if" operation represents an if-then-else construct for
explicit operator bool() const { return failed(*this); }
};
+// These functions are out-of-line utilities, which avoids them being template
+// instantiated/duplicated.
+namespace impl {
+/// Insert an operation, generated by `buildTerminatorOp`, at the end of the
+/// region's only block if it does not have a terminator already. If the region
+/// is empty, insert a new block first. `buildTerminatorOp` should return the
+/// terminator operation to insert.
+void ensureRegionTerminator(
+ Region ®ion, Location loc,
+ llvm::function_ref<Operation *()> buildTerminatorOp);
+/// Templated version that fills the generates the provided operation type.
+template <typename OpTy>
+void ensureRegionTerminator(Region ®ion, Builder &builder, Location loc) {
+ ensureRegionTerminator(region, loc, [&] {
+ OperationState state(loc, OpTy::getOperationName());
+ OpTy::build(&builder, &state);
+ return Operation::create(state);
+ });
+}
+} // namespace impl
+
/// This is the concrete base class that holds the operation pointer and has
/// non-generic methods that only depend on State (to avoid having them
/// instantiated on template types that don't affect them.
}
};
+/// This class provides APIs and verifiers for ops with regions having a single
+/// block that must terminate with `TerminatorOpType`.
+template <typename TerminatorOpType> struct SingleBlockImplicitTerminator {
+ template <typename ConcreteType>
+ class Impl : public TraitBase<ConcreteType, Impl> {
+ public:
+ static LogicalResult verifyTrait(Operation *op) {
+ for (unsigned i = 0, e = op->getNumRegions(); i < e; ++i) {
+ Region ®ion = op->getRegion(i);
+
+ // Empty regions are fine.
+ if (region.empty())
+ continue;
+
+ // Non-empty regions must contain a single basic block.
+ if (std::next(region.begin()) != region.end())
+ return op->emitOpError("expects region #")
+ << i << " to have 0 or 1 blocks";
+
+ // The block must terminate with TerminatorOpType. If the block is
+ // empty, silently fail, the general block well-formedness verifier
+ // should complain instead.
+ Block &block = region.front();
+ if (block.empty())
+ return failure();
+ if (isa<TerminatorOpType>(block.back()))
+ continue;
+
+ return op->emitOpError("expects regions to end with '" +
+ TerminatorOpType::getOperationName() + "'")
+ .attachNote()
+ << "in custom textual format, the absence of terminator implies "
+ "'"
+ << TerminatorOpType::getOperationName() << '\'';
+ }
+
+ return success();
+ }
+
+ /// Ensure that the given region has the terminator required by this trait.
+ static void ensureTerminator(Region ®ion, Builder &builder,
+ Location loc) {
+ ::mlir::impl::template ensureRegionTerminator<TerminatorOpType>(
+ region, builder, loc);
+ }
+ };
+};
+
} // end namespace OpTrait
//===----------------------------------------------------------------------===//
void printCastOp(Operation *op, OpAsmPrinter *p);
Value *foldCastOp(Operation *op);
} // namespace impl
-
-// These functions are out-of-line utilities, which avoids them being template
-// instantiated/duplicated.
-namespace impl {
-/// Insert an operation, generated by `buildTerminatorOp`, at the end of the
-/// region's only block if it does not have a terminator already. If the region
-/// is empty, insert a new block first. `buildTerminatorOp` should return the
-/// terminator operation to insert.
-void ensureRegionTerminator(
- Region ®ion, Location loc,
- llvm::function_ref<Operation *()> buildTerminatorOp);
-/// Templated version that fills the generates the provided operation type.
-template <typename OpTy>
-void ensureRegionTerminator(Region ®ion, Builder &builder, Location loc) {
- ensureRegionTerminator(region, loc, [&] {
- OperationState state(loc, OpTy::getOperationName());
- OpTy::build(&builder, &state);
- return Operation::create(state);
- });
-}
-} // namespace impl
} // end namespace mlir
#endif
// AffineForOp
//===----------------------------------------------------------------------===//
-// Check that if a "block" has a terminator, it is an `AffineTerminatorOp`.
-static LogicalResult checkHasAffineTerminator(OpState &op, Block &block) {
- if (block.empty() || isa<AffineTerminatorOp>(block.back()))
- return success();
-
- return op.emitOpError("expects regions to end with '" +
- AffineTerminatorOp::getOperationName() + "'")
- .attachNote()
- << "in custom textual format, the absence of terminator implies '"
- << AffineTerminatorOp::getOperationName() << "'";
-}
-
-// Insert `affine.terminator` at the end of the region's only block if it does
-// not have a terminator already. If the region is empty, insert a new block
-// first.
-static void ensureAffineTerminator(Region ®ion, Builder &builder,
- Location loc) {
- impl::ensureRegionTerminator<AffineTerminatorOp>(region, builder, loc);
-}
-
void AffineForOp::build(Builder *builder, OperationState *result,
ArrayRef<Value *> lbOperands, AffineMap lbMap,
ArrayRef<Value *> ubOperands, AffineMap ubMap,
Block *body = new Block();
body->addArgument(IndexType::get(builder->getContext()));
bodyRegion->push_back(body);
- ensureAffineTerminator(*bodyRegion, *builder, result->location);
+ ensureTerminator(*bodyRegion, *builder, result->location);
// Set the operands list as resizable so that we can freely modify the bounds.
result->setOperandListToResizable();
}
static LogicalResult verify(AffineForOp op) {
- auto &bodyRegion = op.region();
-
- // The body region must contain a single basic block.
- if (bodyRegion.empty() || std::next(bodyRegion.begin()) != bodyRegion.end())
- return op.emitOpError("expected body region to have a single block");
-
// Check that the body defines as single block argument for the induction
// variable.
auto *body = op.getBody();
"expected body to have a single index argument for the "
"induction variable");
- if (failed(checkHasAffineTerminator(op, *body)))
- return failure();
-
// Verify that there are enough operands for the bounds.
AffineMap lowerBoundMap = op.getLowerBoundMap(),
upperBoundMap = op.getUpperBoundMap();
if (parser->parseRegion(*body, inductionVariable, builder.getIndexType()))
return failure();
- ensureAffineTerminator(*body, builder, result->location);
+ AffineForOp::ensureTerminator(*body, builder, result->location);
// Parse the optional attribute list.
if (parser->parseOptionalAttributeDict(result->attributes))
// Verify that the entry of each child region does not have arguments.
for (auto ®ion : op.getOperation()->getRegions()) {
- if (region.empty())
- continue;
-
- // TODO(riverriddle) We currently do not allow multiple blocks in child
- // regions.
- if (std::next(region.begin()) != region.end())
- return op.emitOpError(
- "expects only one block per 'then' or 'else' regions");
- if (failed(checkHasAffineTerminator(op, region.front())))
- return failure();
-
for (auto &b : region)
if (b.getNumArguments() != 0)
return op.emitOpError(
// Parse the 'then' region.
if (parser->parseRegion(*thenRegion, {}, {}))
return failure();
- ensureAffineTerminator(*thenRegion, parser->getBuilder(), result->location);
+ AffineIfOp::ensureTerminator(*thenRegion, parser->getBuilder(),
+ result->location);
// If we find an 'else' keyword then parse the 'else' region.
if (!parser->parseOptionalKeyword("else")) {
if (parser->parseRegion(*elseRegion, {}, {}))
return failure();
- ensureAffineTerminator(*elseRegion, parser->getBuilder(), result->location);
+ AffineIfOp::ensureTerminator(*elseRegion, parser->getBuilder(),
+ result->location);
}
// Parse the optional attribute list.
// ForOp
//===----------------------------------------------------------------------===//
-// Check that if a "block" is not empty, it has a `TerminatorOp` terminator.
-static LogicalResult checkHasTerminator(OpState &op, Block &block) {
- if (block.empty() || isa<TerminatorOp>(block.back()))
- return success();
-
- return op.emitOpError("expects regions to end with '" +
- TerminatorOp::getOperationName() + "'")
- .attachNote()
- << "in custom textual format, the absence of terminator implies '"
- << TerminatorOp::getOperationName() << "'";
-}
-
-void mlir::loop::ensureLoopTerminator(Region ®ion, Builder &builder,
- Location loc) {
- impl::ensureRegionTerminator<TerminatorOp>(region, builder, loc);
-}
-
void ForOp::build(Builder *builder, OperationState *result, Value *lb,
Value *ub, Value *step) {
result->addOperands({lb, ub, step});
Region *bodyRegion = result->addRegion();
- ensureLoopTerminator(*bodyRegion, *builder, result->location);
+ ForOp::ensureTerminator(*bodyRegion, *builder, result->location);
bodyRegion->front().addArgument(builder->getIndexType());
}
!body->getArgument(0)->getType().isIndex())
return op.emitOpError("expected body to have a single index argument for "
"the induction variable");
- if (failed(checkHasTerminator(op, *body)))
- return failure();
return success();
}
if (parser->parseRegion(*body, inductionVariable, indexType))
return failure();
- ensureLoopTerminator(*body, builder, result->location);
+ ForOp::ensureTerminator(*body, builder, result->location);
// Parse the optional attribute list.
if (parser->parseOptionalAttributeDict(result->attributes))
result->addOperands(cond);
Region *thenRegion = result->addRegion();
Region *elseRegion = result->addRegion();
- ensureLoopTerminator(*thenRegion, *builder, result->location);
+ IfOp::ensureTerminator(*thenRegion, *builder, result->location);
if (withElseRegion)
- ensureLoopTerminator(*elseRegion, *builder, result->location);
+ IfOp::ensureTerminator(*elseRegion, *builder, result->location);
}
static LogicalResult verify(IfOp op) {
if (region.empty())
continue;
- // TODO(riverriddle) We currently do not allow multiple blocks in child
- // regions.
- if (std::next(region.begin()) != region.end())
- return op.emitOpError("expected one block per 'then' or 'else' regions");
- if (failed(checkHasTerminator(op, region.front())))
- return failure();
-
for (auto &b : region)
if (b.getNumArguments() != 0)
return op.emitOpError(
// Parse the 'then' region.
if (parser->parseRegion(*thenRegion, {}, {}))
return failure();
- ensureLoopTerminator(*thenRegion, parser->getBuilder(), result->location);
+ IfOp::ensureTerminator(*thenRegion, parser->getBuilder(), result->location);
// If we find an 'else' keyword then parse the 'else' region.
if (!parser->parseOptionalKeyword("else")) {
if (parser->parseRegion(*elseRegion, {}, {}))
return failure();
- ensureLoopTerminator(*elseRegion, parser->getBuilder(), result->location);
+ IfOp::ensureTerminator(*elseRegion, parser->getBuilder(), result->location);
}
// Parse the optional attribute list.
func @std_for_step_nonnegative(%arg0: index) {
// expected-error@+2 {{constant step operand must be nonnegative}}
%c0 = constant 0 : index
- "loop.for"(%arg0, %arg0, %c0) ({^bb0:}) : (index, index, index) -> ()
+ "loop.for"(%arg0, %arg0, %c0) ({
+ ^bb0(%arg1: index):
+ "loop.terminator"() : () -> ()
+ }) : (index, index, index) -> ()
return
}
// -----
func @std_for_single_block(%arg0: index) {
- // expected-error@+1 {{region #0 ('region') failed to verify constraint: region with 1 blocks}}
+ // expected-error@+1 {{expects region #0 to have 0 or 1 blocks}}
"loop.for"(%arg0, %arg0, %arg0) (
{
^bb1:
// -----
func @std_if_not_one_block_per_region(%arg0: i1) {
- // expected-error@+1 {{region #0 ('thenRegion') failed to verify constraint: region with 1 blocks}}
+ // expected-error@+1 {{expects region #0 to have 0 or 1 blocks}}
"loop.if"(%arg0) ({
^bb0:
"loop.terminator"() : () -> ()