Best way to DCE ops with side effects

I have IR that looks like this:

    %2 = iree.list.create %c2 : !iree.list<i64>
    iree.list.set %2[%c0], %c1_i64 : !iree.list<i64>, i64
    iree.list.set %2[%c1], %c1_i64 : !iree.list<i64>, i64

There are no other uses of %2.

Does MLIR have a pass that can delete this code?

The ops have:

def IREE_ListCreateOp ... MemoryEffects<[MemAlloc]>
def IREE_ListSetOp ... MemoryEffects<[MemWrite]>

There isn’t such a pass since an op with side effects isn’t strictly dead and a generic pass can’t delete it without knowing something more about the op. Ops like these are currently erased via a canonicalization pattern - eg. for AllocOp. We’d need to add another trait to create such a pass (like ErasableIfNoUses). But that still won’t help with erasing the iree.list.set in your snippet. That will still need a separate pattern.

The effect of memwrite here being removable is due to unread list if I’m following, but not true in general. E.g., I could have a op which creates a resource, the alloc would initialize allocate space in transfer bus [is MemAlloc sufficient for this? One could consider this a visible mutation but doc for MemAlloc says it makes no visible mutation] and the write’s value would be visible across the bus without any use of the create space op (as it’s return is only a resource handle used to compute dependencies).

So seems like pattern rooted on list create and one where one can delete all unless there is some observable use (e.g., a read whose value was used further). To delete one would need a trait like “CanDeleteIfNoUseHasTrait<[MemRead]>” on the list create op, then a general pass could work.

If I follow correctly, you’re trying to point at what is in C/C++ the difference between a volatile and non-volatile memory effect.
We haven’t got into specifying this in MLIR, I believe we consider the memory effects non-volatile ; so we could treat the snippet above in two steps:

  • dead-store elimination: the writes are “dead”. This is tricky as we haven’t invested in specifying any “escape analysis” either. Aliasing questions can also be raised here to show that a store is really dead.
  • unused alloc elimination: after removing the writes, the resource is unused and should be able to be eliminated.

Didn’t think about aliasing & escape here, good point. So currently these two steps could be done but would have to conservatively assume more alias and/or escape.

This sounds about right to me. The DSE case is also related to the liveness questions by @jurahul in Liveness broken with regions? – without that, it seems hard to write a DSE pass that supports region-based control flow with reasonable precision.