[Mlir-commits] [mlir] [mlir] targeted verification for transform "inlining" (PR #192956)

Oleksandr Alex Zinenko llvmlistbot at llvm.org
Wed Apr 22 07:50:15 PDT 2026


================
@@ -227,21 +263,57 @@ transform::detail::mergeSymbolsInto(Operation *target,
       assert(targetSymbolTable.lookup(funcOp.getName()) == collidingFuncOp);
 
       // Do the actual merging.
-      {
-        InFlightDiagnostic diag = mergeInto(funcOp, collidingFuncOp);
-        if (failed(diag))
-          return diag;
-      }
+      if (failed(mergeInto(funcOp, collidingFuncOp)))
+        return failure();
     }
   }
 
-  // Need full verification here because merging/inlining may have broken some
-  // nesting invariants that were not broken in the sources.
-  // TODO: implement and use InlinerDialectInterface to avoid this check.
-  if (failed(mlir::verify(target)))
-    return target->emitError()
-           << "failed to verify target op after merging symbols";
+  // Symbol merging only moves callable ops between symbol tables; it does not
+  // alter the bodies that were already valid in the source modules. The only
+  // invariants that may newly be violated after merging are:
+  //   1. a call now refers to a callee whose body is structurally not legal to
+  //      inline at the call site (caught by the transform dialect's
+  //      `DialectInlinerInterface` implementation), or
+  //   2. the merged call graph contains a recursive cycle, which is forbidden
+  //      for `transform.named_sequence` callables (caught by the shared
+  //      `verifyNoRecursionInCallGraph` helper).
+  // Use the inliner interface methods directly (without running the inlining
+  // pass) to validate (1), and reuse the dialect's call-graph verifier for
+  // (2). The call graph builder requires call/callable ops to be well-formed,
+  // so pre-verify them here without recursing into their bodies.
+  WalkResult preVerify = target->walk([](Operation *nested) {
+    if (!isa<CallableOpInterface, CallOpInterface>(nested))
+      return WalkResult::advance();
+    if (failed(mlir::verify(nested, /*verifyRecursively=*/false)))
+      return WalkResult::interrupt();
+    return WalkResult::advance();
+  });
+  if (preVerify.wasInterrupted())
+    return failure();
+
+  InlinerInterface inliner(target->getContext());
+  WalkResult inlineCheck = target->walk([&](CallOpInterface call) {
+    Operation *callable = nullptr;
+    CallInterfaceCallable callee = call.getCallableForCallee();
+    if (auto symRef = dyn_cast<SymbolRefAttr>(callee))
+      callable = targetSymbolTable.lookup(symRef.getLeafReference());
+    else if (auto value = dyn_cast<Value>(callee))
+      callable = value.getDefiningOp();
+
+    if (!callable)
+      return WalkResult::advance();
+    if (!inliner.isLegalToInline(call, callable, /*wouldBeCloned=*/true)) {
----------------
ftynse wrote:

Fair enough, I misremembered where the cloning happens. It was at the entire module and this code moves, I'll set to `false` instead.

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


More information about the Mlir-commits mailing list