IR traversal via use-chains

Hi everyone,

I have a practical question about traversing the IR via use-chains, as well as better understanding the design of the internal IR structure.

What I’d like to do is walk the uses of a value starting from some OpOperand (that is not necessarily the first or last use of the value).
From my understanding looking at the Understand The IR Structure document and the source code, an OpOperand represents a specific use of a value, and the uses are chained together in a doubly linked-list via previous (‘back’) and next (‘nextUse’) pointers.
What confuses me is two things:

  • why is the order of uses inverse to that in the mlir source? I.e. ‘firstUse’ actually points to the last use, iterating through uses starts at the bottom and works upwards, etc.
  • why is only one direction of links exposed? I.e. the ‘nextUse’ pointer is accessible via ‘getNextOperandUsingThisValue()’ (although inline documentation suggests it only be used internally), but there is no way to access the ‘back’ pointer.

So the problem I have is that I can’t walk downwards (in the mlir input) to the next use starting from some OpOperand of a specific operation.

On a related note, I’m assuming the order of value uses stems from the order of their associated operations, which, as described in the language ref, are in fact ordered within their block, and the blocks within their regions… Is this order maintained throughout transformations, because I noticed when printing a use-list after a transformation that operations were now partially out-of-order with respect to the mlir output. Or is this a bug on my end?

The use-list order is not specified and is the order of insertion.
I suspect that we insert in the front of the list and that’s why the parser will yield a use-list order that is reversed to the source.

I suspect this was enough for every use-cases so far? Since you’re not suppose to rely on the order of the use-list, in general iterating through it is the normal case.

This isn’t supported, you would have to collect the Operation users and sort them I believe.

To provide further reasoning on what Mehdi mentioned: an MLIR Value stores a pointer to its first use (firstUse indirectly via IRObjectWithUseList) and this is used to insert new uses. So the ones inserted last would be at the front of the use list (which is a doubly linked list) and appear first via getUses - but in any case, one shouldn’t expect any specific order on them. When IR gets mutated, new uses could be created anywhere, and so there isn’t yet a reason to prefer a specific position to insert them.

I see, thank you for these explanations! I was also trying to figure it out if some op is the last user of a value, but I see I can’t rely on the ordering of uses that way.