[mlir][Vector]  Promote buffers accessed via dynamic subviews (#217793)

**Motivation**

An op on dynamically-shaped tensors is padded to static tile sizes
before vectorization: its operands are padded up to a fixed size, and
the real region is extracted back from the result. After bufferization
those padded tensors become fixed-size buffers, and the IR around them
reaches each buffer through dynamic-shaped views — the buffer's shape is
static, but the part that really holds data is not.

The pattern looks like this: a fixed-size staging buffer holding one
tile, a dynamic subview covering the part with real data, a reshape
lifting that tile to the rank of the original tensor it came from, and a
memref.copy that stores the tile back into it or loads a tile out.

```mlir
%buf = memref.alloca() : memref<128x64xf16>      // padded staging buffer
vector.transfer_write %v, %buf[%c0, %c0] {in_bounds = [true, true]}
    : vector<128x64xf16>, memref<128x64xf16>
%sv  = memref.subview %buf[0, 0] [%n, 64]  [1, 1]   // %n rows of the padded buffer, %n may <128 for the edge tile 
    : memref<128x64xf16> to memref<?x64xf16, strided<[64, 1]>>
             ...                                     // ops on the dynamic subview like repadding + elewise/reduction
%ex  = memref.expand_shape %sv [[0, 1, 2], [3]]
    : memref<?x64xf16, strided<[64, 1]>> into memref<1x1x?x64xf16, strided<[?, ?, 64, 1]>>
%dst = memref.subview %out[%i, %j, %k, 0]  [1, 1, %n, 64] [1, 1, 1, 1]   // the tile's window in %out: %k is where it starts, %n how long it is
memref.copy %ex, %dst
```

#211880 taught Mem2Reg to promote a staticall vector SSA value, but only
for whole-buffer,in-bounds, unmasked
vector.transfer_read/transfer_write, plus fully-static memref.subview
sub-slices.

This PR extends the vector Mem2Reg models so dynamic subviews and masked
transfers promote as well. Any partial access is reconstructed with an
arith.select during promotion instead of bailing out. The chain above
then promotes to vector values, and the allocation goes away.

This PR closes a gap in the lowering pipeline for linalg IR on
dynamically-shaped tensors: tile-and-fuse → padding → vectorize →
bufferization → mem2reg. The current Mem2Reg handles only whole-buffer
transfers and static-shaped subviews, so the staging buffers remain in
the final IR. Now they promote to vector values instead: the padding
introduced at the linalg level becomes a vector.create_mask and an
arith.select on a fixed-size vector, and the pipeline finishes in pure
vector IR.

The expand_shape and collapse_shape will be supported in a separate PR. 

**What this PR adds**

**Dynamic-shaped subviews.** A vector's shape has to be static, so a
dynamic subview cannot have a vector of its own shape. It uses the whole
buffer's vector instead, and reads or writes through it are masked to
the runtime size with vector.create_mask and arith.select. The subview
has to start at the buffer's origin, so the live region on each
dimension is just [0, size).

**Masked transfers.** A masked transfer touches only some lanes, so
rather than reject it, promotion rebuilds the result with an
arith.select.

**Restrictions**
- a transfer that does not start at the buffer's origin, or whose
permutation map is not the identity
 - a subview whose offsets are not constants
 - a dynamic subview that does not start at the buffer's origin
 - a rank-reducing or non-unit-stride subview
- subviewing or reshaping a scalable buffer — it still promotes through
whole-buffer transfers, just not through these

**Tests**
- mem2reg.mlir covers whole-buffer, static-subview, masked cases. 
- mem2reg-dynamic-subview.mlir covers dynamic views: plain, nested on a
static subview, and merged across a branch by a block argument.
 
 assisted-by-claude

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 files changed
tree: 303d083858ed1a91113b8a9c1f557dec9280a1bb
  1. .ci/
  2. .github/
  3. bolt/
  4. clang/
  5. clang-tools-extra/
  6. cmake/
  7. compiler-rt/
  8. cross-project-tests/
  9. flang/
  10. flang-rt/
  11. libc/
  12. libclc/
  13. libcxx/
  14. libcxxabi/
  15. libsycl/
  16. libunwind/
  17. lld/
  18. lldb/
  19. llvm/
  20. llvm-libgcc/
  21. mlir/
  22. offload/
  23. openmp/
  24. orc-rt/
  25. polly/
  26. runtimes/
  27. third-party/
  28. utils/
  29. .clang-format
  30. .clang-format-ignore
  31. .clang-tidy
  32. .git-blame-ignore-revs
  33. .gitattributes
  34. .gitignore
  35. .mailmap
  36. CODE_OF_CONDUCT.md
  37. CONTRIBUTING.md
  38. LICENSE.TXT
  39. pyproject.toml
  40. README.md
  41. SECURITY.md
README.md

The LLVM Compiler Infrastructure

OpenSSF Scorecard OpenSSF Best Practices libc++

Welcome to the LLVM project!

This repository contains the source code for LLVM, a toolkit for the construction of highly optimized compilers, optimizers, and run-time environments.

The LLVM project has multiple components. The core of the project is itself called “LLVM”. This contains all of the tools, libraries, and header files needed to process intermediate representations and convert them into object files. Tools include an assembler, disassembler, bitcode analyzer, and bitcode optimizer.

C-like languages use the Clang frontend. This component compiles C, C++, Objective-C, and Objective-C++ code into LLVM bitcode -- and from there into object files, using LLVM.

Other components include: the libc++ C++ standard library, the LLD linker, and more.

Getting the Source Code and Building LLVM

Consult the Getting Started with LLVM page for information on building and running LLVM.

For information on how to contribute to the LLVM project, please take a look at the Contributing to LLVM guide.

Getting in touch

Join the LLVM Discourse forums, Discord chat, LLVM Office Hours or Regular sync-ups.

The LLVM project has adopted a code of conduct for participants to all modes of communication within the project.