54 Commits

Author SHA1 Message Date
Mehdi Amini
23eec12169
[MLIR] Fix outdated restriction comment in RemoveDeadValuesPass (#189041)
The RemoveDeadValuesPass previously emitted an error and skipped
optimization when the IR contained non-function symbol ops, non-call
symbol user ops, or branch ops. This restriction was later removed, but
the comments in RemoveDeadValues.cpp and Passes.td still described the
pass as operating "iff the IR doesn't have any non-function symbol ops,
non-call symbol user ops and branch ops."

Remove the stale restriction text from both the .cpp file comment and
the Passes.td description. Also add a test that verifies dead function
arguments are correctly removed inside a module that defines a symbol
(has a sym_name attribute), which was the original failure case reported
in issue #98700.

Fixes #98700

Assisted-by: Claude Code
2026-03-27 16:22:47 +00:00
Mehdi Amini
dd6f3534af [MLIR] Apply clang-tidy fixes for llvm-else-after-return in RemoveDeadValues.cpp (NFC) 2026-03-23 05:54:13 -07:00
Mehdi Amini
785490e9db
[MLIR] Remove let constructor = from mlir/include/mlir/Transforms/Passes.td (#183950)
This makes the constructor auto-generated.
2026-03-01 13:51:23 +01:00
Mehdi Amini
bcd8819aee
[mlir][transforms] Fix crash in remove-dead-values when function has non-call users (#183655)
`processFuncOp` asserts that all symbol uses of a function are
`CallOpInterface` operations. This is violated when a function is
referenced by a non-call operation such as `spirv.EntryPoint`, which
uses the function symbol for metadata purposes without calling it.

Fix this by replacing the assertion with an early return: if any user of
the function symbol is not a `CallOpInterface`, skip the function
entirely. This is safe because the pass cannot determine the semantics
of arbitrary non-call references, so it should leave such functions
alone.

Fixes #180416
2026-02-27 15:44:08 +00:00
Prathamesh Tagore
5460a202ea
[mlir][remove-dead-values] Replace appropriate operation results with poison (#181013)
Before erasing the operation, replace all result values with live-uses
by
ub.poison values. This is important to maintain IR validity. For
example,
if we have an op with one of its results used by another op, erasing the
op without replacing its corresponding result would leave us with a
dangling operand in the user op. By replacing the result with a
ub.poison
value, we ensure that the user op still has a valid operand, even though
it's a poison value which will be cleaned up later if it can be cleaned
up. This keeps the IR valid for further simplification and
canonicalization while fixing a related crash in the canonicalizer.

Fixes https://github.com/llvm/llvm-project/issues/179944
2026-02-16 20:46:36 +01:00
Matthias Springer
4a880833b7
[mlir][NFC] remove-dead-values: Get canonicalization patterns from ops (#176712)
Collect canonicalization patterns from the region branch ops (instead of
populating all canonicalization patterns).

Addresses a
[comment](https://github.com/llvm/llvm-project/pull/173505#discussion_r2675222999)
on a merged PR.
2026-01-19 10:30:19 +01:00
Matthias Springer
82c1f9435d
[mlir][Transforms] remove-dead-values: Rely on canonicalizer for region simplification (#173505)
This commit simplifies the `remove-dead-values` pass and fixes a bug in
the handling of `RegionBranchOpInterface` ops. The pass used to produce
invalid IR ("null value found") for the newly added test case.

`remove-dead-values` is a pass for additional IR simplification that
cannot be performed by the canonicalizer pass. Based on a liveness
analysis, it erases dead values / IR. (The liveness analysis is a
dataflow analysis that has more information about the IR than a
canonicalization pattern, which can see only "local" information.)

Region-based ops are difficult. The liveness analysis may determine that
an SSA value is dead. However, that does not mean that the value can
actually be removed. Doing so may violate an region data flow (as
modeled by the `RegionBranchOpInterface`). As an example, consider the
case where a region branch terminator may dispatch to one of two region
successor with the same forwarded values. A successor input (block
argument) can be erased only if it is dead on both successors.

Before this commit, there used to be complex logic to determine when it
is safe to erase an SSA value. That logic was broken. The new
implementation does not remove any block arguments or op results of
region-based ops. Instead, operands of region-based ops and region
branch terminators are replaced with `ub.poison` if all of their
successor values are dead. This simplifies the IR good enough for the
canonicalizer to perform the remaining region simplification (i.e.,
dropping block arguments etc.).

RFC:
https://discourse.llvm.org/t/rfc-delegate-simplification-of-region-based-ops-from-remove-dead-values-to-canonicalizer/89194
2026-01-07 14:51:40 +01:00
Matthias Springer
7de3fb53ce
[mlir][Transforms][NFC] remove-dead-values: Erase ops at the end (#174208)
`remove-dead-values` performs various cleanups:
1. Erasing block arguments
2. Erasing successor operands
3. Erasing operations
4. Erasing function arguments / results
5. Erasing operands
6. Erasing results

This commit moves Step 3 (erasing operations) to the end. While that
does not fix any bugs by itself, it is potentially safer. If an
operation is erased, we must be careful that the operation is not
accessed in the following steps. That can no longer happen if IR is
erased only in the final step and not before.

This commit is prefetching a change from #173505 (to keep that PR
shorter). With #173505, it will become necessary to erase IR in the
final step.
2026-01-02 14:48:26 +01:00
Matthias Springer
cf9b3bbb09
[mlir][IR][NFC] Add RewriterBase::eraseOpResults convenience helper (#174152)
There are various places in the code base where op results are removed.
E.g., some canonicalization patterns remove op results. This commit adds
a new helper function to `RewriterBase` to reduce code duplication and
simplify patterns. The existing implementation from
`RemoveDeadValues.cpp` is moved into the rewriter API.

There is now a uniform API for removing operands and values:
* `Block::eraseArguments(BitVector)`
* `Operation::eraseOperands(BitVector)`
* NEW: `RewriterBase::eraseOpResults(Operation *, BitVector)`

This commit is preparation of adding new canonicalizations for
region-based ops, which will add yet another place where op results must
be erased.
2026-01-02 11:56:32 +00:00
Matthias Springer
8e86107b1f
[mlir][IR][NFC] Add Block::computeBlockNumber convenience helper (#173475)
Add a helper function to compute the number of a block. Recommended only
for debugging purposes and to print error messages.
2025-12-30 13:43:51 +00:00
Matthias Springer
60606b22a7
[mlir][Interfaces] Add RegionBranchOpInterface::getSuccessorOperands helper (#173971)
Add a helper for querying the successor operands for a region branch
`src -> dst`. Both `src` and `dst` may be the region branch op itself or
a terminator.

This helper allows users to query successor operands for the region
branch op and the terminators in a uniform way. This is similar to
`getSuccessorRegions(RegionBranchPoint)`, which works both for region
branch ops and terminators.
2025-12-30 12:28:02 +01:00
Matthias Springer
ca73d19030
[mlir][Transforms][NFC] remove-dead-values: Simplify dropped value handling (#173540)
`RDVFinalCleanupList::values` is used only for function op handling. The
functionality for dropping function arg uses can be incorporated into
Step 5 (function op handling). There is no need for a separate step.
2025-12-30 09:22:58 +01:00
Matthias Springer
7c2bbfae3e
[mlir][Transforms][NFC] remove-dead-values: Split OperationToCleanup (#173542)
The `callee` field does not make sense for op results. Split
`OperationToCleanup` into `OperandsToCleanup` and `ResultsToCleanup`.
2025-12-26 12:44:23 +01:00
Matthias Springer
352bf3d78b
[mlir][Transforms][NFC] remove-dead-values: Use proper rewriter API (#173539)
Use the rewriter API to inline regions.
2025-12-26 12:28:39 +01:00
Matthias Springer
643c5c0fb6
[mlir][Transforms][NFC] Improve debug output of -remove-dead-values (#173468)
Print the index of the block arguments, op results etc. that are being
removed.
2025-12-24 10:31:45 +00:00
Matthias Springer
e6110cb339
[mlir][Transforms] Fix crash in -remove-dead-values on private functions (#169269)
This commit fixes two crashes in the `-remove-dead-values` pass related
to private functions.

Private functions are considered entirely "dead" by the liveness
analysis, which drives the `-remove-dead-values` pass.

The `-remove-dead-values` pass removes dead block arguments from private
functions. Private functions are entirely dead, so all of their block
arguments are removed. However, the pass did not correctly update all
users of these dropped block arguments.

1. A side-effecting operation must be removed if one of its operands is
dead. Otherwise, the operation would end up with a NULL operand. Note:
The liveness analysis would not have marked an SSA value as "dead" if it
had a reachable side-effecting users. (Therefore, it is safe to erase
such side-effecting operations.)
2. A branch operation must be removed if one of its non-forwarded
operands is dead. (E.g., the condition value of a `cf.cond_br`.)
Whenever a terminator is removed, a `ub.unrechable` operation is
inserted. This fixes #158760.
2025-12-03 08:35:05 +01:00
lonely eagle
765208b313
[mlir] Make remove-dead-values remove block and successorOperands before delete ops (#166766)
Reland https://github.com/llvm/llvm-project/pull/165725, fix the Failed
test by removing successor operands before delete operations. Following
the deletion of cond.branch, its successor operands will subsequently be
removed.
2025-11-20 13:55:09 +08:00
lonely eagle
55fb1caf8a
Revert "[mlir] Make remove-dead-values pass remove blocks arguments first" (#166746)
Reverts llvm/llvm-project#165725. See
https://lab.llvm.org/buildbot/#/builders/169/builds/16768,
2025-11-06 11:03:19 +00:00
lonely eagle
a928c61961
[mlir] Make remove-dead-values pass remove blocks arguments first (#165725)
Fix https://github.com/llvm/llvm-project/issues/163051. Some Ops which
have multiple blocks, before deleting the ops, first remove the dead
parameters within its blocks.
2025-11-06 16:36:14 +08:00
Mehdi Amini
41f65666f6
[MLIR] Revamp RegionBranchOpInterface (#165429)
This is still somehow a WIP, we have some issues with this interface
that are not trivial to solve. This patch tries to make the concepts of
RegionBranchPoint and RegionSuccessor more robust and aligned with their
definition:
- A `RegionBranchPoint` is either the parent (`RegionBranchOpInterface`)
op or a `RegionBranchTerminatorOpInterface` operation in a nested
region.
- A `RegionSuccessor` is either one of the nested region or the parent
`RegionBranchOpInterface`

Some new methods with reasonnable default implementation are added to
help resolving the flow of values across the RegionBranchOpInterface.

It is still not trivial in the current state to walk the def-use chain
backward with this interface. For example when you have the 3rd block
argument in the entry block of a for-loop, finding the matching operands
requires to know about the hidden loop iterator block argument and where
the iterargs start. The API is designed around forward-tracking of the
chain unfortunately.

Try to reland #161575 ; I suspect a buildbot incremental build issue.
2025-10-28 09:53:56 -07:00
Mehdi Amini
e3c547179f
Revert " [MLIR] Revamp RegionBranchOpInterface " (#165356)
Reverts llvm/llvm-project#161575

Broke Windows on ARM buildbot build, needs investigations.
2025-10-28 01:06:14 -07:00
Mehdi Amini
ab1fd21b54
[MLIR] Revamp RegionBranchOpInterface (#161575)
This is still somehow a WIP, we have some issues with this interface
that are not trivial to solve. This patch tries to make the concepts of
RegionBranchPoint and RegionSuccessor more robust and aligned with their
definition:
- A `RegionBranchPoint` is either the parent (`RegionBranchOpInterface`)
op or a `RegionBranchTerminatorOpInterface` operation in a nested
region.
- A `RegionSuccessor` is either one of the nested region or the parent
`RegionBranchOpInterface`

Some new methods with reasonnable default implementation are added to
help resolving the flow of values across the RegionBranchOpInterface.

It is still not trivial in the current state to walk the def-use chain
backward with this interface. For example when you have the 3rd block
argument in the entry block of a for-loop, finding the matching operands
requires to know about the hidden loop iterator block argument and where
the iterargs start. The API is designed around forward-tracking of the
chain unfortunately.
2025-10-28 07:47:26 +00:00
Mehdi Amini
7ab7bc7274
[MLIR] Fix LivenessAnalysis/RemoveDeadValues handling of dead function arguments (#160755)
In #153973 I added the correctly handling of block arguments,
unfortunately this was gated on operation that also have results. This
wasn't intentional and this excluded operations like function from being
correctly processed.
2025-09-26 13:47:46 +00:00
Francisco Geiman Thiesen
3e746bd8fb
Allowing RDV to call getArgOperandsMutable() (#160415)
## Problem

`RemoveDeadValues` can legally drop dead function arguments on private
`func.func` callees. But call-sites to such functions aren't fixed if
the call operation keeps its call arguments in a **segmented operand
group** (i.ie, uses `AttrSizedOperandSegments`), unless the call op
implements `getArgOperandsMutable` and the RDV pass actually uses it.

## Fix
When RDV decides to drop callee function args, it should, for each
call-site that implements `CallOpInterface`, **shrink the call's
argument segment** via `getArgOperandsMutable()` using the same dead-arg
indices. This keeps both the flat operand list and the
`operand_segment_sizes` attribute in sync (that's what
`MutableOperandRange` does when bound to the segment).

## Note
This change is a no-op for:
* call ops without segment operands (they still get their flat operands
erased via the generic path)
* call ops whose calle args weren't dropped (public, external,
non-`func-func`, unresolved symbol, etc)
* `llvm.call`/`llvm.invoke` (RDV doesn't drop `llvm.func` args

---------

Co-authored-by: Mehdi Amini <joker.eph@gmail.com>
2025-09-26 15:30:46 +02:00
xin liu
917f078adb
[mlir][Transforms] Allow RemoveDeadValues to process a function whose the last block is not the exit. (#156123)
'processFuncOp' queries the number of returned values of a function
using the terminator of the last block's getNumOperands(). It presumes
the last block is the exit. It is not always the case. 
This patch fixes the bug by querying from FunctionInterfaceOp directly.
2025-08-31 10:00:32 +00:00
Mehdi Amini
dfaebe7f48
[MLIR] Fix Liveness analysis handling of unreachable code (#153973)
This patch is forcing all values to be initialized by the
LivenessAnalysis, even in dead blocks. The dataflow framework will skip
visiting values when its already knows that a block is dynamically
unreachable, so this requires specific handling.
Downstream code could consider that the absence of liveness is the same
a "dead".
However as the code is mutated, new value can be introduced, and a
transformation like "RemoveDeadValue" must conservatively consider that
the absence of liveness information meant that we weren't sure if a
value was dead (it could be a newly introduced value.

Fixes #153906
2025-08-18 20:50:36 +00:00
Mehdi Amini
70e0f31747 [MLIR] Remove unused DBGS macro (NFC) 2025-07-26 06:35:40 -07:00
Mehdi Amini
f26db3f3ec
[MLIR] Add a OpWithFlags class that acts as a "stream modifier" to customize Operation streaming (#150636) 2025-07-25 20:58:14 +02:00
Jacques Pienaar
07967d4af8
[mlir] Switch to new LDBG macro (#150616)
Change local variants to use new central one.
2025-07-25 18:22:46 +02:00
ronigoldman22
9f724d0427
Fix Bug in RemoveDeadValues Pass (#148437)
This patch fixes a bug in the RemoveDeadValues pass where unused
function arguments were not removed from the function signature in an
edge case where the function returns void.
A corresponding test was added to the MLIR LIT test suite to cover this
case.
2025-07-25 02:24:04 +02:00
Kazu Hirata
b5cd49eff0
[mlir] Remove unused includes (NFC) (#146278)
These are identified by misc-include-cleaner.  I've filtered out those
that break builds.  Also, I'm staying away from llvm-config.h,
config.h, and Compiler.h, which likely cause platform- or
compiler-specific build failures.
2025-06-29 12:13:12 -07:00
Menooker
ee5dcdc275
[mlir] fix assertion failure in remove-dead-values (#144849)
Simple IR patterns will trigger assertion error:

```
  func.func @test_zero_operands(%I: memref<10xindex>, %I2: memref<10xf32>) {
    %v0 = arith.constant 0 : index
    %result = memref.alloca_scope -> index {
      %c = arith.addi %v0, %v0 : index
      memref.store %c, %I[%v0] : memref<10xindex>
      memref.alloca_scope.return %c: index
    }
    func.return
  }
```

with error: `mlir/include/mlir/IR/Operation.h:988:
mlir::detail::OperandStorage& mlir::Operation::getOperandStorage():
Assertion `hasOperandStorage && "expected operation to have operand
storage"' failed.`

This PR will fix this issue.

---------

Co-authored-by: Andrzej Warzyński <andrzej.warzynski@gmail.com>
Co-authored-by: Mehdi Amini <joker.eph@gmail.com>
2025-06-22 23:03:36 +09:00
Mehdi Amini
075cb691a5
[MLIR] Add logging/tracing to DataFlow analysis and RemoveDeadValues (NFC) (#144695)
Debugging issues with this pass is quite difficult at the moment, this
should help.
2025-06-22 11:40:01 +02:00
Kazu Hirata
c2835e70fd
[mlir] Remove unused local variables (NFC) (#140423) 2025-05-17 19:43:15 -07:00
Oleksandr "Alex" Zinenko
7318074168
[mlir] allow function type cloning to fail (#137130)
`FunctionOpInterface` assumed the fact that the function type (attribute
of the operation) can be cloned with arbirary lists of function
arguments and results to support argument and result list mutation. This
is not always correct, in particular, LLVM dialect functions require
exactly one result making it impossible to erase the result.

Allow function type cloning to fail and propagate this failure through
various APIs that use it. The common assumption is that existing IR has
not been modified.

Fixes #131142.

Reland a8c7ecdcbc3e89b493b495c6831cc93671c3b844 / #136300.
2025-04-30 09:26:42 +02:00
Kazu Hirata
7da385d797 Revert "[mlir] allow function type cloning to fail (#136300)"
This reverts commit 20a104a7d6423784dab04371a5ca728cc27a15a9.

Buildbot failure:
https://lab.llvm.org/buildbot/#/builders/157/builds/25688

I've reproduced the build failure.
2025-04-18 09:52:28 -07:00
Oleksandr "Alex" Zinenko
20a104a7d6
[mlir] allow function type cloning to fail (#136300)
`FunctionOpInterface` assumed the fact that the function type (attribute
of the operation) can be cloned with arbirary lists of function
arguments and results to support argument and result list mutation. This
is not always correct, in particular, LLVM dialect functions require
exactly one result making it impossible to erase the result.

Allow function type cloning to fail and propagate this failure through
various APIs that use it. The common assumption is that existing IR has
not been modified.

Fixes #131142.
2025-04-18 16:05:54 +02:00
AdityaK
f2849fe05f
Fix RemoveDeadValues: Bail out early when there are no terminators (#133316)
Fixes: #131765
2025-03-27 15:04:31 -07:00
Longsheng Mou
be354cf381
[mlir][transforms] Process RegionBranchOp with empty region (#123895)
This PR adds process for RegionBranchOp with empty region, such as
'else' region of `scf.if`. Fixes #123246.
2025-02-11 14:43:15 +08:00
Renat Idrisov
aa3c31a86f
[MLIR] Prevent invalid IR from being passed outside of RemoveDeadValues (#121079)
This is a follow-up for https://github.com/llvm/llvm-project/pull/119110
and a fix for https://github.com/llvm/llvm-project/issues/118450

RemoveDeadValues used to delete Values and analyzing the IR at the same
time, because of that, `isMemoryEffectFree` got invalid IR with
half-deleted linalg.generic operation. This PR separates analysis and
cleanup to prevent such situation.

Thank you!

---------

Co-authored-by: Renat Idrisov <parsifal-47@users.noreply.github.com>
Co-authored-by: Andrzej Warzyński <andrzej.warzynski@gmail.com>
2025-01-20 14:48:32 +00:00
Renat Idrisov
0629e9e352
[MLIR] Removing dead values for branches (#117501)
Fixing RemoveDeadValues to properly remove arguments from
BranchOpInterface operations.
This is a follow-up for:
https://github.com/llvm/llvm-project/pull/117405
cc: @joker-eph @codemzs

---------

Co-authored-by: Renat Idrisov <parsifal-47@users.noreply.github.com>
2024-12-05 14:05:48 +08:00
Longsheng Mou
e08e5e2c42
[mlir][transforms] Use isExternal instead of isDeclaration for FunctionOpInterface (#116573)
This PR fixes a bug in `RemoveDeadValues` where the
`FunctionOpInterface` does not have the `isDeclaration` method. As a
result, we should use the `isExternal` method instead. Fixes #116347.
2024-12-04 11:14:37 +08:00
M. Zeeshan Siddiqui
5f9db0876a
Allow SymbolUserOpInterface operators to be used in RemoveDeadValues Pass (#117405)
This change removes the restriction on `SymbolUserOpInterface` operators
so they can be used with operators that implement `SymbolOpInterface`,
example:

`memref.global` implements `SymbolOpInterface` so it can be used with
`memref.get_global` which implements `SymbolUserOpInterface`

```
// Define a global constant array
memref.global "private" constant @global_array : memref<10xi32> = dense<[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]> : tensor<10xi32>

// Access this global constant within a function
func @use_global() {
  %0 = memref.get_global @global_array : memref<10xi32>
}
```

Reference: https://github.com/llvm/llvm-project/pull/116519 and
https://discourse.llvm.org/t/question-on-criteria-for-acceptable-ir-in-removedeadvaluespass/83131

---------

Co-authored-by: Zeeshan Siddiqui <mzs@ntdev.microsoft.com>
2024-11-23 19:37:29 +01:00
Renat Idrisov
24ced771cc
[MLIR] RemoveDeadValues: Allowing IRs with global constants to get dead values removed (#116519)
This change is related to discussion:

https://discourse.llvm.org/t/question-on-criteria-for-acceptable-ir-in-removedeadvaluespass/83131

I do not know the original reason to disallow the optimization on
modules with global private constant. Please let me know what am I
missing, I will be happy to make it better. Thank you!

CC: @Wheest

---------

Co-authored-by: Renat Idrisov <parsifal-47@users.noreply.github.com>
2024-11-22 14:41:31 -08:00
Longsheng Mou
70865c448c
[mlir][transforms] Add signalPassFailure in RemoveDeadValues (#112199)
This PR adds `signalPassFailure` in RemoveDeadValues to ensure that a
pipeline would stop here.
Fixes #111757.
2024-10-18 09:55:41 +08:00
Perry Gibson
7ad566d575
[mlir] Fix remove-dead-values pass throws error when module has a name (#109990)
Fixes #107870.

We can allow the enclosing Module operation to have a symbol.
The check was likely originally not considering this case and intended
to catch symbols inside the region, not accounting that the walk would
visit the enclosing operation.
2024-10-03 10:51:55 +02:00
Longsheng Mou
1208699618
[mlir][transforms] Skip RemoveDeadValues for function declaration (#108221)
This patch skips `RemoveDeadValues` if funcOp is declaration, which
fixes a crash.
Fixes #107546.
2024-09-14 21:24:51 +08:00
Dmitriy Smirnov
65fd05517f
[MLIR] Added check for IsTerminator trait (#79317)
This PR adds a check for IsTerminator trait to prevent deletion of ops
like gpu.terminator as a "simple op" by RemoveDeadValues pass.
2024-01-26 14:14:54 +01:00
Martin Erhart
34a35a8b24 [mlir] Move FunctionInterfaces to Interfaces directory and inherit from CallableOpInterface
Functions are always callable operations and thus every operation
implementing the `FunctionOpInterface` also implements the
`CallableOpInterface`. The only exception was the FuncOp in the toy
example. To make implementation of the `FunctionOpInterface` easier,
this commit lets `FunctionOpInterface` inherit from
`CallableOpInterface` and merges some of their methods. More precisely,
the `CallableOpInterface` has methods to get the argument and result
attributes and a method to get the result types of the callable region.
These methods are always implemented the same way as their analogues in
`FunctionOpInterface` and thus this commit moves all the argument and
result attribute handling methods to the callable interface as well as
the methods to get the argument and result types. The
`FuntionOpInterface` then does not have to declare them as well, but
just inherits them from the `CallableOpInterface`.
Adding the inheritance relation also required to move the
`FunctionOpInterface` from the IR directory to the Interfaces directory
since IR should not depend on Interfaces.

Reviewed By: jpienaar, springerm

Differential Revision: https://reviews.llvm.org/D157988
2023-08-31 11:28:23 +00:00
Markus Böck
4dd744ac9c Reland "[mlir] Use a type for representing branch points in RegionBranchOpInterface"
This reverts commit b26bb30b467b996c9786e3bd426c07684d84d406.
2023-08-30 09:31:54 +02:00