Use, user, value

could someone help to understand the relationship between them?

Similiar answer could be found for LLVM

however, the answer is LLVM-specific (instruction, function…)

is there understanding inside the MLIR?

In MLIR a value (mlir::Value in C++`) is any kind of SSA value that is either produced by an operation, or a block argument of a block:

^bb0(%a : i32):
   %res = arith.constant 5

Both %a and %res are values here. The %a is a mlir::BlockArgument, %res is a OpResult. Both of these convert to mlir::Value (you can think of them as subclasses somewhat).

Every value has a list of uses attached to it. Whereever you use a Value, the use gets recorded in this use-list. In terms of C++, the class representing the use is OpOperand. An operation has an instance of OpOperand for every operand it has, which contains the Value the operand is set to.

The user is then simply the operation owning the OpOperand which is a Operation* at the very least (or can be cast to a more specific C++ Op class).

So to summarize:

  • mlir::Value is a Value that has a list of uses
  • mlir::OpOperand represents a use and holds the mlir::Value
  • mlir::Operation* (and more concrete Op classes) is the user and contains a list of OpOperands for its operands.
1 Like

There is a tutorial for this here: Understanding the IR Structure - MLIR

I included a diagram a the end.

thanks, i’m aware of this figure.
but i still have question about this figure after checking source code:

struct IROperand {
   IRObjectWithUseList *value;
   IROperand nextUse;   // next operand in the use use-chain
   IROperand **back;  // points to the previous link in the use-chain

in your figure , there are 3 OpOperands
the leftmost is the first use of Value, so the nextUse in the rightmost OpOperand is nullptr

i have two questions regarding this figure:

  1. what is back (the comment inside the source code confuses me :frowning: ) and what is it used for?
    back = &value->firstUse; // does it store the address of “address of OpOperand” ?
  2. what will happen if new operand is inserted ?

really appriciate for your help.

thanks for your detail answer.
i have one more question if you don’t mind
what is “first use” in the use-chain?
when looking into source code of MLIR, it seems that use-list is updated by inserting in the tail
so the last one inserted is “first use”…

sorry for my silly question, but this really confuses me :frowning:

I’m not following: why do you think it is null? It links to the next OpOperand (the one in the middle)

If you look at the picture at the bottom with the 3 OpOperands, it includes the nextUse and back pointer: they link the 3 operands together. It’s a doubly-linked-list…

A new OpOperand is created using this value, it is added to the link list of OpOperand.

The first element of the link-list, again follow the pointer on the picture. Are we talking about the same picture? All the pointers you’re asking are drawn here I believe:


yes, we are talking about the same picture.

maybe, i don’t understand how the doubly-linked-list works.
struct IROperandBase {
/// The next operand in the use-chain.
IROperandBase *nextUse = nullptr;

/// This points to the previous link in the use-chain.
** IROperandBase back = nullptr; // ← it is pointer to pointer

Introduction to Doubly Linked List – Data Structure and Algorithm Tutorials - GeeksforGeeks is common
format of doubly-linked-list which i’m familiar with

let’s see the source code ( in terms of inserting :
/// Insert this operand into the given use list.
void insertInto(UseListT *useList) {
back = &useList->firstUse;
nextUse = useList->firstUse;
if (nextUse)
nextUse->back = &nextUse;
useList->firstUse = this;

the last inserted operand is marked as firstUse (useList->firstUse = this;)
back pointer doesn’t point to the previous use which is not same with figure
*back = &address_of_current_operand
please correct me if my understanding is wrong.