[Mlir-commits] [mlir] [MLIR] Introduce support for early exits (PR #166688)

Matthias Springer llvmlistbot at llvm.org
Fri Jun 12 03:18:10 PDT 2026


================
@@ -112,6 +117,70 @@ SmallVector<Value> replaceAndCastForOpIterArg(RewriterBase &rewriter,
                                               OpOperand &operand,
                                               Value replacement,
                                               const ValueTypeCastFnTy &castFn);
+namespace op_impl {
+
+//===----------------------------------------------------------------------===//
+// ControlFlowImplicitTerminatorOperation
+//===----------------------------------------------------------------------===//
+
+/// This class provides an interface compatible with
+/// SingleBlockImplicitTerminator, but allows multiple types of potential
+/// terminators aside from just one. If a terminator isn't present, this will
+/// generate a `ImplicitOpT` operation.
+template <typename ImplicitOpT, typename... OtherTerminatorOpTs>
+struct ControlFlowImplicitTerminatorOpType {
+  /// Implementation of `classof` that supports all of the potential terminator
+  /// operations.
+  static bool classof(Operation *op) {
+    return isa<ImplicitOpT, OtherTerminatorOpTs...>(op);
+  }
+
+  //===--------------------------------------------------------------------===//
+  // Implicit Terminator Methods
+
+  /// The following methods are all used when interacting with the "implicit"
+  /// terminator.
+
+  template <typename... Args>
+  static void build(Args &&...args) {
+    ImplicitOpT::build(std::forward<Args>(args)...);
+  }
+  static constexpr StringLiteral getOperationName() {
+    return ImplicitOpT::getOperationName();
+  }
+};
+/// An implicit terminator type for `if` operations. Empty regions still receive
+/// an `scf.yield`, but explicitly terminated regions may use any terminator
+/// implementing RegionExitTerminatorOpInterface so dialect-defined exits can
+/// propagate through `scf.if`.
+struct IfOpImplicitTerminatorType {
+  static bool classof(Operation *op) {
+    return isa<RegionExitTerminatorOpInterface>(op);
+  }
+
+  template <typename... Args>
+  static void build(Args &&...args) {
+    YieldOp::build(std::forward<Args>(args)...);
+  }
+  static constexpr StringLiteral getOperationName() {
+    return YieldOp::getOperationName();
+  }
+};
+struct LoopOpImplicitTerminatorType
+    : public ControlFlowImplicitTerminatorOpType<ContinueOp, BreakOp> {
+  /// Build the implicit `scf.continue` terminator. The control token consumed
+  /// by the terminator is the loop body's entry block argument #0; the
+  /// insertion block is guaranteed to be that body when the implicit
+  /// terminator is materialized. This keeps `scf.continue`'s public builders
+  /// free of any hidden block-argument assumption.
+  static void build(OpBuilder &builder, OperationState &state) {
+    Block *block = builder.getInsertionBlock();
+    assert(block && block->getNumArguments() > 0 &&
+           "expected insertion block with a loop control token");
----------------
matthias-springer wrote:

Should this `assert` also check the type of the first block argument?

https://github.com/llvm/llvm-project/pull/166688


More information about the Mlir-commits mailing list