[Mlir-commits] [mlir] [MLIR][XeVM] Fix mask lifetime in HandleVectorExtractPattern (PR #223879)

Sang Ik Lee llvmlistbot at llvm.org
Tue Sep 15 16:52:32 PDT 2026


https://github.com/silee2 updated https://github.com/llvm/llvm-project/pull/223879

>From fc40ab5d394a0a6a89c455333ee85ecdd0d2463f Mon Sep 17 00:00:00 2001
From: "Lee, Sang Ik" <sang.ik.lee at intel.com>
Date: Tue, 15 Sep 2026 23:50:38 +0000
Subject: [PATCH] [MLIR][XeVM] Fix mask lifetime in HandleVectorExtractPattern

When a contiguous-slice shuffle is hoisted through a bitcast that changes
the element count, the slice is rewritten against the bitcast source by
rescaling the mask into a local SmallVector and rebinding the `mask`
ArrayRef to it. That SmallVector was declared inside the
`if (maskScale != 1)` block, so it was destroyed before the two uses of
`mask` that follow the block - a stack-use-after-scope.

It is benign on uninstrumented builds, which is why precommit did not
catch it, but the sanitizer bots trap it on
Conversion/XeVMToLLVM/mxfp_scale_binary_hoist.mlir, the first test whose
chain drives the length-changing bitcast path:

  ERROR: AddressSanitizer: stack-use-after-scope
  READ of size 4
    #23 mlir::detail::DenseArrayAttrImpl<int>::get(MLIRContext*,
                                                  ArrayRef<int>)
    #24 mlir::LLVM::ShuffleVectorOp::build(..., ArrayRef<int>)
    #26 HandleVectorExtractPattern::matchAndRewrite
          XeVMToLLVM.cpp:1866:27
  Address is located in stack of thread T0 at offset 304 in frame
    #0 HandleVectorExtractPattern::matchAndRewrite XeVMToLLVM.cpp:1801
    [288, 352) 'newMask' (line 1856) <== Memory access at offset 304 is
                                         inside this variable
  SUMMARY: AddressSanitizer: stack-use-after-scope

ASan reports the above, MSan reports a read of uninitialized memory and
HWASan a tag mismatch. In each case mlir-opt dies partway through
--split-input-file, so the surface symptom is the CHECK-LABEL of the
second module in that file not being found:

  https://lab.llvm.org/buildbot/#/builders/52/builds/20099
  https://lab.llvm.org/buildbot/#/builders/164/builds/25415
  https://lab.llvm.org/buildbot/#/builders/169/builds/26571
  https://lab.llvm.org/buildbot/#/builders/55/builds/33165

Hoist the declaration so it outlives the uses, and add a lit test for the
length-changing bitcast path, which previously had no direct coverage -
the existing tests only bitcast between equal element counts.
---
 mlir/lib/Conversion/XeVMToLLVM/XeVMToLLVM.cpp  |  6 +++++-
 .../XeVMToLLVM/legalize_large_vector.mlir      | 18 ++++++++++++++++++
 2 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/mlir/lib/Conversion/XeVMToLLVM/XeVMToLLVM.cpp b/mlir/lib/Conversion/XeVMToLLVM/XeVMToLLVM.cpp
index 8e4410729caa8..d9cb0e2eed3e7 100644
--- a/mlir/lib/Conversion/XeVMToLLVM/XeVMToLLVM.cpp
+++ b/mlir/lib/Conversion/XeVMToLLVM/XeVMToLLVM.cpp
@@ -1846,6 +1846,11 @@ class HandleVectorExtractPattern
           return failure();
         }
         auto maskScale = srcResSize / srcInputSize;
+        // Storage for the rescaled mask. `mask` is an ArrayRef that gets
+        // rebound to this buffer below, so it has to outlive the `if` that
+        // fills it - otherwise the uses after the `if` read a destroyed
+        // SmallVector.
+        SmallVector<int32_t> newMask;
         if (maskScale != 1) {
           // The slice has to start at, and cover, whole source elements to be
           // expressible in terms of the bitcast source.
@@ -1853,7 +1858,6 @@ class HandleVectorExtractPattern
             return failure();
           }
           // Create a new mask that maps to the source vector
-          SmallVector<int32_t> newMask;
           int32_t newMaskSize = maskSize / maskScale;
           int32_t maskStart = mask[0] / maskScale;
           for (int32_t i = 0; i < newMaskSize; ++i) {
diff --git a/mlir/test/Conversion/XeVMToLLVM/legalize_large_vector.mlir b/mlir/test/Conversion/XeVMToLLVM/legalize_large_vector.mlir
index 21413e3e53a5f..9aa4892c65aa8 100644
--- a/mlir/test/Conversion/XeVMToLLVM/legalize_large_vector.mlir
+++ b/mlir/test/Conversion/XeVMToLLVM/legalize_large_vector.mlir
@@ -177,3 +177,21 @@ module @test_nary_no_hoist_through_call {
     llvm.return %1 : vector<16xf32>
   }
 }
+
+// -----
+
+// A bitcast that widens the element count (8xi32 -> 16xbf16) feeding a
+// contiguous slice: the slice can only be expressed against the bitcast
+// source by rescaling the mask, which is the one path that rebuilds the mask
+// into a local buffer. Guards the lifetime of that buffer - reading it after
+// scope exit is a stack-use-after-scope that only sanitizer builds observe.
+module @test_bitcast_mask_rescale {
+  // CHECK-LABEL: llvm.func @test_bitcast_mask_rescale
+  // CHECK:         %[[S:.*]] = llvm.shufflevector %arg0, %arg0 [0, 1, 2, 3] : vector<8xi32>
+  // CHECK:         llvm.bitcast %[[S]] : vector<4xi32> to vector<8xbf16>
+  llvm.func @test_bitcast_mask_rescale(%a: vector<8xi32>) -> vector<8xbf16> {
+    %0 = llvm.bitcast %a : vector<8xi32> to vector<16xbf16>
+    %1 = llvm.shufflevector %0, %0 [0, 1, 2, 3, 4, 5, 6, 7] : vector<16xbf16>
+    llvm.return %1 : vector<8xbf16>
+  }
+}



More information about the Mlir-commits mailing list