Skip to content

add elide transfers pass - #1

Draft
ziereis wants to merge 92 commits into
stream_topology_attrfrom
stream_elide_transfer_ops
Draft

add elide transfers pass#1
ziereis wants to merge 92 commits into
stream_topology_attrfrom
stream_elide_transfer_ops

Conversation

@ziereis

@ziereis ziereis commented May 22, 2025

Copy link
Copy Markdown
Owner

draft implementation for iree-org#20853

@ziereis
ziereis force-pushed the stream_elide_transfer_ops branch from 501f6a7 to b48d0b1 Compare May 26, 2025 14:10
Groverkss and others added 18 commits May 27, 2025 12:45
This PR adds support for dynamic unit trip (0 or 1 trip) scf.for using scf.if.

---------

Signed-off-by: Nirvedh Meshram <nirvedh@gmail.com>
This should almost always be 'auto' but when performing executable
compilation (`--compile-mode=hal-executable`) it can be explicitly set
to `hsaco` to have the output of the compile command be the raw HSACO
ELF image.

Example:
```
iree-compile \
  --compile-mode=hal-executable \
  --iree-hal-target-device=hip --iree-hip-target=gfx942 \
  --iree-rocm-container-type=hsaco \
  tools/test/iree-benchmark-executable.mlir \
  -o=hsaco.elf
```
…ree-org#20897)

Renames `iree_codegen.load_from_memref` to
`iree_codegen.load_from_buffer`, and `iree_codegen.store_to_memref` to
`iree_codegen.store_to_buffer` for consistency with the op definition.

Signed-off-by: Max Dawkins <max.dawkins@gmail.com>
This allows for kernel specialization by way of annotating tilable ops
with the preferred iteration ranges and divisibilities to specialize
for. For example,

```
%35 = linalg.matmul ins(%30, %31 : tensor<?x?xf16>, tensor<?x?xf16>) outs(%34 : tensor<?x?xf32>)
  {iree_codegen.specialization_ranges = #util<int.assumption.multi_array[
    [<umin = 128, umax = 4096, udiv = 128>, <umin = 128, umax = 4096, udiv = 128>, <umin = 64, udiv = 64>],
    [<umin = 4096, udiv = 256>, <umin = 4096, udiv = 256>, <udiv = 64>]
]>} -> tensor<?x?xf32>
```

This matmul will be specialized into up to 3 variants, one default, and
two for each list of ranges. More details can be found in the code
documentation.

To specialize a dispatch, the pass queries the iteration domain of the
tilable op and looks for a producer `util.int.assume` op. The function
is then cloned and the assume is updated to match the requested ranges.

This is just one way to implement kernel specialization, in the future
we can implement many more options (e.g. based on estimated TOPs or
smallest individual dimension) and that's without considering what
device we're targeting. The implementation here supports many common
cases of minimum/maximum sizes and alignments. Learnings here will
translate to future improvements for other strategies.

This pass runs first thing in the configuration pipelines because it
needs to apply to the variant, and should run before passes like
BlockDynamicDims which takes advantage of `util.assume.int` information
that this pass refines.
## Summary
This PR sets the foundation for using `global_load_lds` instruction to
load values from global to LDS memory. The pipeline is as follows:
* Only convert `linalg.copy` emitted in `PromoteGPUMatMulOperands`. When
it sees fit, insert a different attribute
(`#iree_gpu.use_global_load_dma`) to `linalg.copy` to tag it along the
pipeline.
* Tagged `linalg.copy` will not be decomposed/tiled until bufferization.
* after distributed to threads and bufferization, the tagged
`linalg.copy` will then be lowered to a sequence of code responsible for
subgroup-coalesced loading op `iree_gpu.global_load_dma`.
* `iree_gpu.global_load_dma` will be mapped to `amdgpu.gather_to_lds`
op, which will mapped to corresponding rocdl op.
* Disable padding to reduce bank conflict pass because the destination
workgroup memory has to be contiguous.

## Lowering `linalg.copy`
After bufferization and distribute to threads, tagged `linalg.copy`
still exists in the IR:
```
linalg.copy {lowering_config = #iree_gpu.use_global_load_dma}
  ins(%subview_12 : memref<64x128xi8, strided<[256, 1], offset: ?>, #amdgpu.address_space<fat_raw_buffer>>)
  outs(%alloc_4 : memref<64x128xi8, #gpu.address_space<workgroup>>)
```

Note that this `linalg.copy` is kept in the thread's code. The op itself
is then converted into a `for loop`, in which subgroup of threads loads
coalesced chunk of values. For example, assume there are N subgroups
loading from `tensor<a x b x c>`:
* then `i`-th subgruop will load a sub tensor of size `[a/N, b, c]`, so
each slice is consecutive.
	* At this moment, assume row-major, and only tile the outermost dim.
* The reason right now we are only dealing with `linalg.copy` emitted by
`GPUPromoteMatmulOperands` is that we know the destination is allocated
contiguously.
	* TODO: expand to any memref slices.
* given `gpu.subgroup_id` and `gpu.lane_id`, each thread calculates the
consecutive data chunk the subgroup the thread belongs to is responsible
to load:
* the chunk indices is the delinearized indices of the input tensor,
from:
* `affine.delinearize_index[gpu.subgroup_id * (num_elems_of(tensor) /
num_subgroups)]`, to
* `affine.delinearize_index[(gpu.subgroup_id + 1) *
(num_elems_of(tensor) / num_subgroups) - 1]`
* Assume each subgroup will load `n` values from linearized index `[N_f,
N_b]`, then thread with lane id `i` will try to load: `iter = 0 to n :
N_f + subgroup_size * iter + (i - 1)` .
Then it will be converted to something like the following (in the
example, assume `workgroup size = 256`, `subgroup_size = 64`, loading
`64x128xi8`):
```miler
scf.for %indvar = %c0 to %c32 step %c1 {
  ;; thread-specific gathering address from global address
  %17 = affine.apply affine_map<()[s0, s1, s2] -> (s0 + s1 * 2048 + s2 * 64)>()[%lane_id, %subgroup_id, %indvar]
  %18:2 = affine.delinearize_index %17 into (128, 64) : index, index
  ;; this iteration's base storing index
  %19 = affine.apply affine_map<()[s0, s1] -> (s0 * 2048 + s1 * 64)>()[%subgroup_id, %indvar]
  %20:2 = affine.delinearize_index %19 into (128, 64) : index, index 
  iree_gpu.global_load_dma %subview_13[%18#0, %18#1] -> %alloc_5[%20#0, %20#1] : memref<128x64xi8, strided<[256, 1], offset: ?>, #amdgpu.address_space<fat_raw_buffer>> -> memref<128x64xi8, #gpu.address_space<workgroup>>
}
;; if there are residual elements (subgroup_copy_region_size % subgroup_size != 0), copy residual elements here 
gpu.barrier
```

## Dependent PRs:
* design doc: https://hackmd.io/N0RitxPzT9GPhM0jEPtOCg?view
* upstream changes required: 
  * llvm/llvm-project#133498
  * llvm/llvm-project#136405
  * llvm/llvm-project#137671
  * llvm/llvm-project#137425
  * iree-org#20800 (review)

---------

Signed-off-by: Alan Li <me@alanli.org>
…#20911)

It closes iree-org#20907.
It closes iree-org#20093.

Newly supported APIs:
- PJRT_Client_AddressableMemories 
- PJRT_Device_AddressableMemories 
- PJRT_Device_DefaultMemory
- PJRT_Buffer_Memory
- PJRT_Memory_Id
- PJRT_Memory_Kind
- PJRT_Memory_ToString
- PJRT_Memory_DebugString
- PJRT_Memory_AddressableByDevices

Currently the memory is one-to-one mapped from its device, just for JAX
to successfully initialize the PJRT plugin.

After this patch, the PJRT plugin can support JAX from 0.5.1 up to the
latest version (0.6.1) (see iree-org#20907 for the current status).

ci-exactly: build_packages, test_pjrt

Signed-off-by: PragmaTwice <twice@apache.org>
It was rarely doing anything but deleting unused ops after cloning but
running on the full module (including dispatches). This does not change
the number of iterations required but does make each iteration
significantly faster and makes the code easier to refactor in subsequent
changes.
InlineConstantGlobalInitializer was not using a symbol table and is
redundant with what FoldGlobalsPass does anyway. The global info queried
in resource usage analysis was just used to get the type - instead, the
type can be accessed locally on the load/store op.
Previously the pass would always clone an op if analysis resulted in
multiple producer affinities. This leads to bad behavior where the pass
will keep trying to clone ops in order to fix the situation but do
nothing meaningful before the next try (and eventually hit the limit).

This should help situations where CloneToConsumersPass was hitting its
iteration limit. It does not fix affinity analysis iteration limits -
those are caused by ambiguous IR and will still fail to converge with
these changes.
Mostly NFC besides early-exiting on assigning affinities to
function-like ops, as they can never have affinities. Removes a lot of
debug spam (entire function/initializer contents).
… semantic change. (iree-org#20913)

This drops a revert:
iree-org/llvm-project@435bb50

To make IREE compatible with the new `memref.assuem_assignment`
semantics, the revision moves the op creation from OneShotBufferization
to post bufferization passes. It is mainly for reducing the burdens of
bufferization analysis. Otherwise, it'd easily generate redundant copies
between the original memref and the result of AssumeAlignment op.

Furthermore, there are several missing support when AssumeAlignment op
is present in SSA chain.
- The `FlattenMemRefSubspanPass` needs to be improved to take the op
into account.
- We are missing a pattern that propagates the `memref.dim` ops for
`memref.dim(memref.assume_alignment)` cases. It reduces the deps from
AssumeAlignment op, so we don't need to add more patterns for it.
- The chained memref.assume_alignment ops are not folded away. This can
be done as a canonicalization pattern. The revision adds a workaround
for early-bufferization dispatch, and it will be removed once we
implement the pattern.
- Need to teach SPIRVVectorizeLoadStore pass to take AssumeAlignment ops
into account.
- Need to teach VMVX ResolveBufferDescriptors pass to take
AssumeAlignment ops into account. The new pattern is copied from the
other one, and a refactoring can be done in the future. For now, we
mainly want to drop the revert, and we can easily refactor the code out.
So it is a TODO for now.
- It triggers failures in hoisting vector transfer ops when
AssumeAlignment ops are present. We don't know the root cause yet, and
it is too complicated and hard to fix it within the revision. Because
the implementation is in upstream. See
iree-org#20912 for reproducer.

Closes iree-org#20908

---------

Signed-off-by: hanhanW <hanhan0912@gmail.com>
…ils. (iree-org#20915)

Signed-off-by: hanhanW <hanhan0912@gmail.com>
…0925)

Few tests are updated to use the result of `memref.assume_alignment`
because they care about the ops.

Many `memref.assume_alignment` ops are removed from many lit tests
because they do not need them. They don't use them at their pass scope.

There are two tests not changed because of recent regression. I'll
revisit it if I come back to the issue. For now, leave them as what they
are:
- SPIRV/test/tile_and_promote_cooperative_matrix.mlir
- SPIRV/test/tile_and_vectorize_to_cooperative_ops.mlir

This is a follow-up to
iree-org@cbb7536

---------

Signed-off-by: hanhanW <hanhan0912@gmail.com>
Currently we just build and test the IREE PJRT CPU plugin in pkgci.

Since we don't have Nvidia GPUs for CI runners refer to iree-org#18814 now, we
can at least confirm that the CUDA PJRT plugin can be successfully built
in CI.

This PR tries to enable it.

ci-exactly: build_packages, test_pjrt

Signed-off-by: PragmaTwice <twice@apache.org>
This change extends `ReshapeFusion.cpp` to support propagating reshapes
through `iree_linalg_ext.gather`. The logic is very similar to `scatter`
but with the 0th and 2nd operands flipped. Additionally, the `LinalgExt`
reshape propagation tests were moved out of `DispatchCreation`. A test
pass was created in `LinalgExt` to run the reshape propagation patterns.

---------

Signed-off-by: Ian Wood <ianwood2024@u.northwestern.edu>
…org#20923)

`--iree-stream-affinity-solver-max-iterations=N` will allow an easy way
to isolate analysis failures to the solver iteration count. Now that
CloneToConsumersPass isn't running the solver itself in a loop (much)
it's less scary to up the default, though it's still risky in the
failure cases as it could lead to _very_ long times to failure.

The proper solution is to reduce the iteration count by somehow dealing
with very long tied op chains that pass through multiple affinities.
Dropped reverts, which are fixed by the head:
-
iree-org/llvm-project@0357fd9
-
iree-org/llvm-project@20a9a98

Carried revert:
- (old)
iree-org/llvm-project@13631cf
- (new)
iree-org/llvm-project@5f144b5
- (new)
iree-org/llvm-project@cddcc13
- (new)
llvm/llvm-project@989aadf

Additional cherry-picks, which can be dropped after we bump ahead of it:
-
iree-org/llvm-project@92b9689

The fixes in IREE are for
-
llvm/llvm-project@05494f3
-
llvm/llvm-project@61d5fdf

The second and the third reverts because they break torch-mlir. I have a
fix for IREE side and can fix torch-mlir together in our repo. I prefer
fixing them later because we are already ~1 week behind the LLVM head.

The four revert is because of an assertion in convert-to-llvm lowering
in ARM SME pipeline tests.

Fixes iree-org#20737

---------

Signed-off-by: hanhanW <hanhan0912@gmail.com>
krzysz00 and others added 29 commits June 3, 2025 13:57
There are methods on the MMA operation and the MMAKind attribute that I
found no uses of, and so I'm going to go ahead and delete them.
Currently default tuning specs are registered to a global embedded
directory when the plugin for a backend is registered. Then the
MaterializeTuningSpecs pass would try to interpret the target attribute
as a gpu target attr to get a target arch to use. This is action at a
distance that only works for gpu targets.

This patch replaces the global directory with a simple lookup for
"iree_codegen.default_tuning_spec" in the `hal.executable.target`
configuration. The attribute stored there maintains a reference to a
(typically static) module that can be retrieved by attribute interface,
giving target backends freedom over the mechanism used to deliver the
tuning spec + caching implementations.

Today only the ROCMTarget implements a default tuning spec (for a single
arch at that), so this PR adds a ROCMDialect to the ROCM plugin to
implement the `rocm.builtin.tuning_module` attribute. Any builtin
accessed through this attribute is cached on the dialect (same as the
iree_codegen dialect).

The same move to attribute based reference will be done for ukernels as
a follow up (but with a different interface given that parsing is not
needed).
…-org#20988)

If we successfully created the buffer view, it holds a ref to the
buffer, so we want to release our own. If we failed then we want to
release the imported buffer anyway so we don't leak.

Signed-off-by: Andrew Woloszyn <andrew.woloszyn@gmail.com>
This test has been reported as flaky on certain builds:
https://discord.com/channels/689900678990135345/1062405112292712499/1379561856460652706

and currently has no active maintainer. Disable the test until the
non-determinism is fixed or the code is dropped.
Improve documentation related to sharktuner.

See Issue: nod-ai/amd-shark-ai#1248

---------

Signed-off-by: Muzammiluddin Syed <muzasyed@amd.com>
Signed-off-by: hanhanW <hanhan0912@gmail.com>
…iree-org#20885)

Adds `IREE::Stream::AffinityTopologyAttrInterface` so that analysis and
passes in the stream dialect can query for topology information from
target device layers. `#hal.device.topology<...>` is the first
implementation.

Fixes iree-org#20854.

---------

Signed-off-by: Thomas Ziereis <ziereis@roofline.ai>
Co-authored-by: Ben Vanik <ben.vanik@gmail.com>
iree-org#20468)

When performing cross-lane reductions using subgroup_reduce ops across
contiguous lanes on AMD GPUs, lower to Data Parallel Primitives (DPP)
ops when possible. This reduces latency on applicable devices.
See related iree-org#20007

---------

Signed-off-by: Muzammiluddin Syed <muzasyed@amd.com>
This PR is part of a larger change moving
`LLVMCPUTileRootAndFuseProducerConsumer`'s tiling of parallel dimensions
to use `scf.forall` rather than `scf.for`; allowing us to take advantage
of some canonicalization patterns available on scf.forall. The patterns
available prevent redundant stack allocations seen in iree-org#20792. Tiling
parallel dimensions with `scf.forall` rather than `scf.for` could also
be said to be semantically cleaner.

As part of that change, this PR intrudes shared tiling utilities pulled
out of `TileDispatchUsingForall` that will later be used in
`LLVMCPUTileRootAndFuseProducerConsumer`.

PR 1/4 addressing iree-org#20792
This PR is part of a larger change moving
`LLVMCPUTileRootAndFuseProducerConsumer`'s tiling of parallel dimensions
to use `scf.forall` rather than `scf.for`; allowing us to take advantage
of some canonicalization patterns available on scf.forall. The patterns
available prevent redundant stack allocations seen in iree-org#20792. Tiling
parallel dimensions with `scf.forall` rather than `scf.for` could also
be said to be semantically cleaner.

As part of that change, the pipeline will need to moved the tiled
`scf.forall` back to a `scf.for` as later stages expect `scf.for`. This
PR introduces a pass to do the conversion.

PR 2/4 addressing iree-org#20792

---------

Co-authored-by: Prashant Kumar <pk5561@gmail.com>
Co-authored-by: Han-Chung Wang <hanhan0912@gmail.com>
std::string is not a round-trippable attr parameter. Change to a
stringref parameter.
)

This change combines `BubbleUpExpandShapes` and `BubbleUpExtractSlices`
into a single pass. In cases like `linalg.generic -> extract -> expand
-> extract -> expand` the expand & extract bubbling patterns must be
done together and iteratively to be able to bubble all ops through the
`linalg.generic`.

This fixes missed paged attention fusion with the mask producer.

---------

Signed-off-by: Ian Wood <ianwood2024@u.northwestern.edu>
Integrate to
iree-org/llvm-project@d96447b
Drops aall cherry-picks/reverts.
…rg#21011)

The IGEMM works out of the box for the dilated convolutions by removing
the previous guard condition. The output numerics are correct for
forward convs. The performance can be improved from 475us to 195us with
IGEMM for the following conv kernel.
```
convbfp16 -n 16 -c 144 -H 24 -W 16 -k 144 -y 3 -x 3 -p 2 -q 2 -u 1 -v 1 -l 2 -j 2 -m conv -g 1 -F 1 -t 1 --in_layout NHWC --out_layout NHWC --fil_layout NHWC --iter 100
```

---------

Signed-off-by: yzhang93 <zhyuhang88@gmail.com>
In experiments with shortfin, this prevents the kernel launches from
appearing before they are enqueued.

Signed-off-by: Eric Eaton <erieaton@amd.com>
iree-org#21020) …(iree-org#20805)"

This reverts commit d7bb36b.

Issue Tracker: iree-org#21019

Signed-off-by: Praveen G <praveen.g2@amd.com>
…ree-org#21027)

Fixes iree-org#21024

Signed-off-by: MaheshRavishankar <mahesh.ravishankar@gmail.com>
…ee-org#21025)

This commonly arises in dynamic resource offsets on stream bindings: a
bunch of `umin=0, umax=0` (or whatever offset) values that ugly up the
IR.
Signed-off-by: default <ziereis@roofline.ai>
Signed-off-by: default <ziereis@roofline.ai>
Signed-off-by: default <ziereis@roofline.ai>
Signed-off-by: default <ziereis@roofline.ai>
Signed-off-by: default <ziereis@roofline.ai>
Signed-off-by: default <ziereis@roofline.ai>
Signed-off-by: default <ziereis@roofline.ai>
Signed-off-by: default <ziereis@roofline.ai>
Signed-off-by: default <ziereis@roofline.ai>
Signed-off-by: default <ziereis@roofline.ai>
ziereis pushed a commit that referenced this pull request Aug 27, 2025
This PR adds a pattern to swap the `collapse_shape` with the
`extract_slice` op to enable more loop fusion opportunity. Note this
pattern is adapted from the upstream[
`BubbleUpCollapseShapeThroughExtractSlice`](https://github.com/llvm/llvm-project/blob/ffb453989b0e95d85b6cfa543b65fec23b65649d/mlir/lib/Dialect/Tensor/Transforms/ReshapePatterns.cpp#L322)
by allowing some special cases we've seen through the IGEMM path.

The special case is processed under `CASE #1` session with the
corresponding tests mainly focusing on the changes. Note that some
strict conditions are added to match the special case for convolutions
which maybe not general and robust enough to add to upstream. In
addition, we break the [upstream single
pattern](https://github.com/llvm/llvm-project/blob/ffb453989b0e95d85b6cfa543b65fec23b65649d/mlir/lib/Dialect/Tensor/Transforms/ReshapePatterns.cpp#L767)
into two separate ones to be able to apply them separately in different
passes.

---------

Signed-off-by: yzhang93 <zhyuhang88@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.