[Mlir-commits] [mlir] [mlir][transform] Add HoistLoopInvariantCodeMotionOp (PR #163687)

lonely eagle llvmlistbot at llvm.org
Wed Oct 15 20:02:38 PDT 2025


https://github.com/linuxlonelyeagle created https://github.com/llvm/llvm-project/pull/163687

HoistLoopInvariantSubsetsOp already exists upstream, and there is equally good reason for HoistLoopInvariantCodeMotionOp to exist. HoistLoopInvariantSubsetsOp cannot solve all problems; when an extractOp has a dynamic shape, we need to use HoistLoopInvariantCodeMotionOp to advance the dynamic size first, and then use HoistLoopInvariantSubsetsOp.

>From 6530bf051f8d122d6381f0af58f7080963595d16 Mon Sep 17 00:00:00 2001
From: linuxlonelyeagle <2020382038 at qq.com>
Date: Thu, 16 Oct 2025 02:57:15 +0000
Subject: [PATCH] add HoistLoopInvariantCodeMotionOp

---
 .../LoopExtension/LoopExtensionOps.td         | 56 +++++++++++++++++++
 .../LoopExtension/LoopExtensionOps.cpp        | 18 ++++++
 .../Transform/test-loop-transforms.mlir       | 34 +++++++++++
 3 files changed, 108 insertions(+)

diff --git a/mlir/include/mlir/Dialect/Transform/LoopExtension/LoopExtensionOps.td b/mlir/include/mlir/Dialect/Transform/LoopExtension/LoopExtensionOps.td
index 885b55811e62c..9854c8db2ebf3 100644
--- a/mlir/include/mlir/Dialect/Transform/LoopExtension/LoopExtensionOps.td
+++ b/mlir/include/mlir/Dialect/Transform/LoopExtension/LoopExtensionOps.td
@@ -73,4 +73,60 @@ def HoistLoopInvariantSubsetsOp
   }];
 }
 
+def HoistLoopInvariantCodeMotionOp
+    : TransformDialectOp<"loop.hoist_loop_invariant_code_motion",
+        [TransformOpInterface, TransformEachOpTrait,
+         DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
+         ReportTrackingListenerFailuresOpTrait]> {
+  let summary = "Hoist loop invariant subset ops";
+  let description = [{
+    This transform hoists loop-invariant instructions out of the targeted
+    loop-like op.
+
+    Example:
+    ```
+    %m = memref.alloc() : memref<10xf32>
+    %cf7 = arith.constant 7.0 : f32
+    %cf8 = arith.constant 8.0 : f32
+
+    affine.for %arg0 = 0 to 10 {
+      affine.for %arg1 = 0 to 10 {
+        %v0 = arith.addf %cf7, %cf8 : f32
+      }
+    }
+    ```
+    Is transformed to:
+    ```
+    %alloc = memref.alloc() : memref<10xf32>
+    %cst = arith.constant 7.000000e+00 : f32
+    %cst_0 = arith.constant 8.000000e+00 : f32
+    %0 = arith.addf %cst, %cst_0 : f32
+    affine.for %arg0 = 0 to 10 {
+    }
+    affine.for %arg0 = 0 to 10 {
+    }
+    ```
+
+    loop-invariant instructions are hoisted only if they are pure ops and
+    they are ancestors of the parent regionOp of all operands.
+
+    This transform reads the target handle and modifies the payload. This
+    transform does not invalidate any handles, but loop-like ops are replaced
+    with new loop-like ops when a loop-invariant op is hoisted. The transform
+    rewriter updates all handles accordingly.
+  }];
+
+  let arguments = (ins TransformHandleTypeInterface:$target);
+  let results = (outs);
+  let assemblyFormat = "$target attr-dict `:` type($target)";
+
+  let extraClassDeclaration = [{
+    ::mlir::DiagnosedSilenceableFailure applyToOne(
+      ::mlir::transform::TransformRewriter &rewriter,
+      ::mlir::LoopLikeOpInterface loopLikeOp,
+      ::mlir::transform::ApplyToEachResultList &results,
+      ::mlir::transform::TransformState &state);
+  }];
+}
+
 #endif // MLIR_DIALECT_TRANSFORM_LOOPEXTENSION_LOOPEXTENSIONOPS
diff --git a/mlir/lib/Dialect/Transform/LoopExtension/LoopExtensionOps.cpp b/mlir/lib/Dialect/Transform/LoopExtension/LoopExtensionOps.cpp
index 95870e8ef87be..de50e5aab4510 100644
--- a/mlir/lib/Dialect/Transform/LoopExtension/LoopExtensionOps.cpp
+++ b/mlir/lib/Dialect/Transform/LoopExtension/LoopExtensionOps.cpp
@@ -32,3 +32,21 @@ void transform::HoistLoopInvariantSubsetsOp::getEffects(
   transform::onlyReadsHandle(getTargetMutable(), effects);
   transform::modifiesPayload(effects);
 }
+
+//===----------------------------------------------------------------------===//
+// HoistLoopInvariantCodeMotionOp
+//===----------------------------------------------------------------------===//
+
+DiagnosedSilenceableFailure transform::HoistLoopInvariantCodeMotionOp::applyToOne(
+    transform::TransformRewriter &rewriter, LoopLikeOpInterface loopLikeOp,
+    transform::ApplyToEachResultList &results,
+    transform::TransformState &state) {
+  (void)moveLoopInvariantCode(loopLikeOp);
+  return DiagnosedSilenceableFailure::success();
+}
+
+void transform::HoistLoopInvariantCodeMotionOp::getEffects(
+    SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
+  transform::onlyReadsHandle(getTargetMutable(), effects);
+  transform::modifiesPayload(effects);
+}
diff --git a/mlir/test/Dialect/Transform/test-loop-transforms.mlir b/mlir/test/Dialect/Transform/test-loop-transforms.mlir
index 833dd738f79ed..c3d21c406172d 100644
--- a/mlir/test/Dialect/Transform/test-loop-transforms.mlir
+++ b/mlir/test/Dialect/Transform/test-loop-transforms.mlir
@@ -79,3 +79,37 @@ module attributes {transform.with_named_sequence} {
     transform.yield
   }
 }
+
+// -----
+
+func.func @nested_loops_both_having_invariant_code_transform() {
+  %m = memref.alloc() : memref<10xf32>
+  %cf7 = arith.constant 7.0 : f32
+  %cf8 = arith.constant 8.0 : f32
+  affine.for %arg0 = 0 to 10 {
+    %v0 = arith.addf %cf7, %cf8 : f32
+    affine.for %arg1 = 0 to 10 {
+      %v1 = arith.addf %v0, %cf8 : f32
+      affine.store %v0, %m[%arg0] : memref<10xf32>
+    }
+  }
+
+  // CHECK: memref.alloc() : memref<10xf32>
+  // CHECK-NEXT: %[[CST0:.*]] = arith.constant 7.000000e+00 : f32
+  // CHECK-NEXT: %[[CST1:.*]] = arith.constant 8.000000e+00 : f32
+  // CHECK-NEXT: %[[ADD0:.*]] = arith.addf %[[CST0]], %[[CST1]] : f32
+  // CHECK-NEXT: arith.addf %[[ADD0]], %[[CST1]] : f32
+  // CHECK-NEXT: affine.for
+  // CHECK-NEXT: affine.for
+  // CHECK-NEXT: affine.store
+  return
+}
+
+module attributes {transform.with_named_sequence} {
+  transform.named_sequence @__transform_main(
+       %arg0: !transform.any_op {transform.readonly}) {
+    %loop = transform.structured.match interface{LoopLikeInterface} in %arg0 : (!transform.any_op) -> !transform.any_op 
+    transform.loop.hoist_loop_invariant_code_motion %loop : !transform.any_op
+    transform.yield
+  }
+}



More information about the Mlir-commits mailing list