163d1dc66SMehdi Amini# Understanding the IR Structure
263d1dc66SMehdi Amini
363d1dc66SMehdi AminiThe MLIR Language Reference describes the
431d1ae79SMarkus Böck[High Level Structure](../LangRef.md/#high-level-structure), this document
563d1dc66SMehdi Aminiillustrates this structure through examples, and introduces at the same time the
663d1dc66SMehdi AminiC++ APIs involved in manipulating it.
763d1dc66SMehdi Amini
831d1ae79SMarkus BöckWe will implement a [pass](../PassManagement.md/#operation-pass) that traverses any
963d1dc66SMehdi AminiMLIR input and prints the entity inside the IR. A pass (or in general almost any
1063d1dc66SMehdi Aminipiece of IR) is always rooted with an operation. Most of the time the top-level
1163d1dc66SMehdi Aminioperation is a `ModuleOp`, the MLIR `PassManager` is actually limited to
1263d1dc66SMehdi Aminioperation on a top-level `ModuleOp`. As such a pass starts with an operation,
1363d1dc66SMehdi Aminiand so will our traversal:
1463d1dc66SMehdi Amini
1563d1dc66SMehdi Amini```
1663d1dc66SMehdi Amini  void runOnOperation() override {
1763d1dc66SMehdi Amini    Operation *op = getOperation();
1863d1dc66SMehdi Amini    resetIndent();
1963d1dc66SMehdi Amini    printOperation(op);
2063d1dc66SMehdi Amini  }
2163d1dc66SMehdi Amini```
2263d1dc66SMehdi Amini
2363d1dc66SMehdi Amini## Traversing the IR Nesting
2463d1dc66SMehdi Amini
2563d1dc66SMehdi AminiThe IR is recursively nested, an `Operation` can have one or multiple nested
2663d1dc66SMehdi Amini`Region`s, each of which is actually a list of `Blocks`, each of which itself
2763d1dc66SMehdi Aminiwraps a list of `Operation`s. Our traversal will follow this structure with
2863d1dc66SMehdi Aminithree methods: `printOperation()`, `printRegion()`, and `printBlock()`.
2963d1dc66SMehdi Amini
3063d1dc66SMehdi AminiThe first method inspects the properties of an operation, before iterating on
3163d1dc66SMehdi Aminithe nested regions and print them individually:
3263d1dc66SMehdi Amini
3363d1dc66SMehdi Amini```c++
3463d1dc66SMehdi Amini  void printOperation(Operation *op) {
3563d1dc66SMehdi Amini    // Print the operation itself and some of its properties
3663d1dc66SMehdi Amini    printIndent() << "visiting op: '" << op->getName() << "' with "
3763d1dc66SMehdi Amini                  << op->getNumOperands() << " operands and "
3863d1dc66SMehdi Amini                  << op->getNumResults() << " results\n";
3963d1dc66SMehdi Amini    // Print the operation attributes
4063d1dc66SMehdi Amini    if (!op->getAttrs().empty()) {
4163d1dc66SMehdi Amini      printIndent() << op->getAttrs().size() << " attributes:\n";
4263d1dc66SMehdi Amini      for (NamedAttribute attr : op->getAttrs())
4363d1dc66SMehdi Amini        printIndent() << " - '" << attr.first << "' : '" << attr.second
4463d1dc66SMehdi Amini                      << "'\n";
4563d1dc66SMehdi Amini    }
4663d1dc66SMehdi Amini
4763d1dc66SMehdi Amini    // Recurse into each of the regions attached to the operation.
4863d1dc66SMehdi Amini    printIndent() << " " << op->getNumRegions() << " nested regions:\n";
4963d1dc66SMehdi Amini    auto indent = pushIndent();
5063d1dc66SMehdi Amini    for (Region &region : op->getRegions())
5163d1dc66SMehdi Amini      printRegion(region);
5263d1dc66SMehdi Amini  }
5363d1dc66SMehdi Amini```
5463d1dc66SMehdi Amini
5563d1dc66SMehdi AminiA `Region` does not hold anything other than a list of `Block`s:
5663d1dc66SMehdi Amini
5763d1dc66SMehdi Amini```c++
5863d1dc66SMehdi Amini  void printRegion(Region &region) {
5963d1dc66SMehdi Amini    // A region does not hold anything by itself other than a list of blocks.
6063d1dc66SMehdi Amini    printIndent() << "Region with " << region.getBlocks().size()
6163d1dc66SMehdi Amini                  << " blocks:\n";
6263d1dc66SMehdi Amini    auto indent = pushIndent();
6363d1dc66SMehdi Amini    for (Block &block : region.getBlocks())
6463d1dc66SMehdi Amini      printBlock(block);
6563d1dc66SMehdi Amini  }
6663d1dc66SMehdi Amini```
6763d1dc66SMehdi Amini
6863d1dc66SMehdi AminiFinally, a `Block` has a list of arguments, and holds a list of `Operation`s:
6963d1dc66SMehdi Amini
7063d1dc66SMehdi Amini```c++
7163d1dc66SMehdi Amini  void printBlock(Block &block) {
7263d1dc66SMehdi Amini    // Print the block intrinsics properties (basically: argument list)
7363d1dc66SMehdi Amini    printIndent()
7463d1dc66SMehdi Amini        << "Block with " << block.getNumArguments() << " arguments, "
7563d1dc66SMehdi Amini        << block.getNumSuccessors()
7663d1dc66SMehdi Amini        << " successors, and "
7763d1dc66SMehdi Amini        // Note, this `.size()` is traversing a linked-list and is O(n).
7863d1dc66SMehdi Amini        << block.getOperations().size() << " operations\n";
7963d1dc66SMehdi Amini
8063d1dc66SMehdi Amini    // A block main role is to hold a list of Operations: let's recurse into
8163d1dc66SMehdi Amini    // printing each operation.
8263d1dc66SMehdi Amini    auto indent = pushIndent();
8363d1dc66SMehdi Amini    for (Operation &op : block.getOperations())
8463d1dc66SMehdi Amini      printOperation(&op);
8563d1dc66SMehdi Amini  }
8663d1dc66SMehdi Amini```
8763d1dc66SMehdi Amini
8863d1dc66SMehdi AminiThe code for the pass is available
8994fac81fSxgupta[here in the repo](https://github.com/llvm/llvm-project/blob/main/mlir/test/lib/IR/TestPrintNesting.cpp)
9063d1dc66SMehdi Aminiand can be exercised with `mlir-opt -test-print-nesting`.
9163d1dc66SMehdi Amini
9263d1dc66SMehdi Amini### Example
9363d1dc66SMehdi Amini
9463d1dc66SMehdi AminiThe Pass introduced in the previous section can be applied on the following IR
9563d1dc66SMehdi Aminiwith `mlir-opt -test-print-nesting -allow-unregistered-dialect
9663d1dc66SMehdi Aminillvm-project/mlir/test/IR/print-ir-nesting.mlir`:
9763d1dc66SMehdi Amini
9863d1dc66SMehdi Amini```mlir
99f8479d9dSRiver Riddle"builtin.module"() ( {
10063d1dc66SMehdi Amini  %0:4 = "dialect.op1"() {"attribute name" = 42 : i32} : () -> (i1, i16, i32, i64)
10163d1dc66SMehdi Amini  "dialect.op2"() ( {
10263d1dc66SMehdi Amini    "dialect.innerop1"(%0#0, %0#1) : (i1, i16) -> ()
10363d1dc66SMehdi Amini  },  {
10463d1dc66SMehdi Amini    "dialect.innerop2"() : () -> ()
10563d1dc66SMehdi Amini    "dialect.innerop3"(%0#0, %0#2, %0#3)[^bb1, ^bb2] : (i1, i32, i64) -> ()
10663d1dc66SMehdi Amini  ^bb1(%1: i32):  // pred: ^bb0
10763d1dc66SMehdi Amini    "dialect.innerop4"() : () -> ()
10863d1dc66SMehdi Amini    "dialect.innerop5"() : () -> ()
10963d1dc66SMehdi Amini  ^bb2(%2: i64):  // pred: ^bb0
11063d1dc66SMehdi Amini    "dialect.innerop6"() : () -> ()
11163d1dc66SMehdi Amini    "dialect.innerop7"() : () -> ()
11263d1dc66SMehdi Amini  }) {"other attribute" = 42 : i64} : () -> ()
11363d1dc66SMehdi Amini}) : () -> ()
11463d1dc66SMehdi Amini```
11563d1dc66SMehdi Amini
11663d1dc66SMehdi AminiAnd will yield the following output:
11763d1dc66SMehdi Amini
11863d1dc66SMehdi Amini```
119f8479d9dSRiver Riddlevisiting op: 'builtin.module' with 0 operands and 0 results
12063d1dc66SMehdi Amini 1 nested regions:
12163d1dc66SMehdi Amini  Region with 1 blocks:
12263d1dc66SMehdi Amini    Block with 0 arguments, 0 successors, and 3 operations
12363d1dc66SMehdi Amini      visiting op: 'dialect.op1' with 0 operands and 4 results
12463d1dc66SMehdi Amini      1 attributes:
12563d1dc66SMehdi Amini       - 'attribute name' : '42 : i32'
12663d1dc66SMehdi Amini       0 nested regions:
12763d1dc66SMehdi Amini      visiting op: 'dialect.op2' with 0 operands and 0 results
12863d1dc66SMehdi Amini       2 nested regions:
12963d1dc66SMehdi Amini        Region with 1 blocks:
13063d1dc66SMehdi Amini          Block with 0 arguments, 0 successors, and 1 operations
13163d1dc66SMehdi Amini            visiting op: 'dialect.innerop1' with 2 operands and 0 results
13263d1dc66SMehdi Amini             0 nested regions:
13363d1dc66SMehdi Amini        Region with 3 blocks:
13463d1dc66SMehdi Amini          Block with 0 arguments, 2 successors, and 2 operations
13563d1dc66SMehdi Amini            visiting op: 'dialect.innerop2' with 0 operands and 0 results
13663d1dc66SMehdi Amini             0 nested regions:
13763d1dc66SMehdi Amini            visiting op: 'dialect.innerop3' with 3 operands and 0 results
13863d1dc66SMehdi Amini             0 nested regions:
13963d1dc66SMehdi Amini          Block with 1 arguments, 0 successors, and 2 operations
14063d1dc66SMehdi Amini            visiting op: 'dialect.innerop4' with 0 operands and 0 results
14163d1dc66SMehdi Amini             0 nested regions:
14263d1dc66SMehdi Amini            visiting op: 'dialect.innerop5' with 0 operands and 0 results
14363d1dc66SMehdi Amini             0 nested regions:
14463d1dc66SMehdi Amini          Block with 1 arguments, 0 successors, and 2 operations
14563d1dc66SMehdi Amini            visiting op: 'dialect.innerop6' with 0 operands and 0 results
14663d1dc66SMehdi Amini             0 nested regions:
14763d1dc66SMehdi Amini            visiting op: 'dialect.innerop7' with 0 operands and 0 results
14863d1dc66SMehdi Amini             0 nested regions:
14963d1dc66SMehdi Amini       0 nested regions:
15063d1dc66SMehdi Amini```
15163d1dc66SMehdi Amini
15263d1dc66SMehdi Amini## Other IR Traversal Methods.
15363d1dc66SMehdi Amini
15463d1dc66SMehdi AminiIn many cases, unwrapping the recursive structure of the IR is cumbersome and
15563d1dc66SMehdi Aminiyou may be interested in using other helpers.
15663d1dc66SMehdi Amini
15763d1dc66SMehdi Amini### Filtered iterator: `getOps<OpTy>()`
15863d1dc66SMehdi Amini
15963d1dc66SMehdi AminiFor example the `Block` class exposes a convenient templated method
16063d1dc66SMehdi Amini`getOps<OpTy>()` that provided a filtered iterator. Here is an example:
16163d1dc66SMehdi Amini
16263d1dc66SMehdi Amini```c++
16363d1dc66SMehdi Amini  auto varOps = entryBlock.getOps<spirv::GlobalVariableOp>();
16463d1dc66SMehdi Amini  for (spirv::GlobalVariableOp gvOp : varOps) {
16563d1dc66SMehdi Amini     // process each GlobalVariable Operation in the block.
16663d1dc66SMehdi Amini     ...
16763d1dc66SMehdi Amini  }
16863d1dc66SMehdi Amini```
16963d1dc66SMehdi Amini
17063d1dc66SMehdi AminiSimilarly, the `Region` class exposes the same `getOps` method that will iterate
17163d1dc66SMehdi Aminion all the blocks in the region.
17263d1dc66SMehdi Amini
17363d1dc66SMehdi Amini### Walkers
17463d1dc66SMehdi Amini
17563d1dc66SMehdi AminiThe `getOps<OpTy>()` is useful to iterate on some Operations immediately listed
17663d1dc66SMehdi Aminiinside a single block (or a single region), however it is frequently interesting
17763d1dc66SMehdi Aminito traverse the IR in a nested fashion. To this end MLIR exposes the `walk()`
17863d1dc66SMehdi Aminihelper on `Operation`, `Block`, and `Region`. This helper takes a single
17963d1dc66SMehdi Aminiargument: a callback method that will be invoked for every operation recursively
18063d1dc66SMehdi Amininested under the provided entity.
18163d1dc66SMehdi Amini
18263d1dc66SMehdi Amini```c++
18363d1dc66SMehdi Amini  // Recursively traverse all the regions and blocks nested inside the function
18463d1dc66SMehdi Amini  // and apply the callback on every single operation in post-order.
18563d1dc66SMehdi Amini  getFunction().walk([&](mlir::Operation *op) {
18663d1dc66SMehdi Amini    // process Operation `op`.
18763d1dc66SMehdi Amini  });
18863d1dc66SMehdi Amini```
18963d1dc66SMehdi Amini
19063d1dc66SMehdi AminiThe provided callback can be specialized to filter on a particular type of
19163d1dc66SMehdi AminiOperation, for example the following will apply the callback only on `LinalgOp`
19263d1dc66SMehdi Aminioperations nested inside the function:
19363d1dc66SMehdi Amini
19463d1dc66SMehdi Amini```c++
195*1fe1f913SIngo Müller  getFunction().walk([](LinalgOp linalgOp) {
19663d1dc66SMehdi Amini    // process LinalgOp `linalgOp`.
19763d1dc66SMehdi Amini  });
19863d1dc66SMehdi Amini```
19963d1dc66SMehdi Amini
20063d1dc66SMehdi AminiFinally, the callback can optionally stop the walk by returning a
20163d1dc66SMehdi Amini`WalkResult::interrupt()` value. For example the following walk will find all
20263d1dc66SMehdi Amini`AllocOp` nested inside the function and interrupt the traversal if one of them
20363d1dc66SMehdi Aminidoes not satisfy a criteria:
20463d1dc66SMehdi Amini
20563d1dc66SMehdi Amini```c++
20663d1dc66SMehdi Amini  WalkResult result = getFunction().walk([&](AllocOp allocOp) {
20763d1dc66SMehdi Amini    if (!isValid(allocOp))
20863d1dc66SMehdi Amini      return WalkResult::interrupt();
20963d1dc66SMehdi Amini    return WalkResult::advance();
21063d1dc66SMehdi Amini  });
21163d1dc66SMehdi Amini  if (result.wasInterrupted())
21263d1dc66SMehdi Amini    // One alloc wasn't matching.
21363d1dc66SMehdi Amini    ...
21463d1dc66SMehdi Amini```
21563d1dc66SMehdi Amini
21663d1dc66SMehdi Amini## Traversing the def-use chains
21763d1dc66SMehdi Amini
21863d1dc66SMehdi AminiAnother relationship in the IR is the one that links a `Value` with its users.
21963d1dc66SMehdi AminiAs defined in the
22031d1ae79SMarkus Böck[language reference](../LangRef.md/#high-level-structure),
22163d1dc66SMehdi Aminieach Value is either a `BlockArgument` or the result of exactly one `Operation`
22263d1dc66SMehdi Amini(an `Operation` can have multiple results, each of them is a separate `Value`).
22363d1dc66SMehdi AminiThe users of a `Value` are `Operation`s, through their arguments: each
22463d1dc66SMehdi Amini`Operation` argument references a single `Value`.
22563d1dc66SMehdi Amini
22663d1dc66SMehdi AminiHere is a code sample that inspects the operands of an `Operation` and prints
22763d1dc66SMehdi Aminisome information about them:
22863d1dc66SMehdi Amini
22963d1dc66SMehdi Amini```c++
23063d1dc66SMehdi Amini  // Print information about the producer of each of the operands.
23163d1dc66SMehdi Amini  for (Value operand : op->getOperands()) {
23263d1dc66SMehdi Amini    if (Operation *producer = operand.getDefiningOp()) {
23363d1dc66SMehdi Amini      llvm::outs() << "  - Operand produced by operation '"
23463d1dc66SMehdi Amini                   << producer->getName() << "'\n";
23563d1dc66SMehdi Amini    } else {
23663d1dc66SMehdi Amini      // If there is no defining op, the Value is necessarily a Block
23763d1dc66SMehdi Amini      // argument.
23863d1dc66SMehdi Amini      auto blockArg = operand.cast<BlockArgument>();
23963d1dc66SMehdi Amini      llvm::outs() << "  - Operand produced by Block argument, number "
24063d1dc66SMehdi Amini                   << blockArg.getArgNumber() << "\n";
24163d1dc66SMehdi Amini    }
24263d1dc66SMehdi Amini  }
24363d1dc66SMehdi Amini```
24463d1dc66SMehdi Amini
24563d1dc66SMehdi AminiSimilarly, the following code sample iterates through the result `Value`s
24663d1dc66SMehdi Aminiproduced by an `Operation` and for each result will iterate the users of these
24763d1dc66SMehdi Aminiresults and print informations about them:
24863d1dc66SMehdi Amini
24963d1dc66SMehdi Amini```c++
25063d1dc66SMehdi Amini  // Print information about the user of each of the result.
25163d1dc66SMehdi Amini  llvm::outs() << "Has " << op->getNumResults() << " results:\n";
25263d1dc66SMehdi Amini  for (auto indexedResult : llvm::enumerate(op->getResults())) {
25363d1dc66SMehdi Amini    Value result = indexedResult.value();
25463d1dc66SMehdi Amini    llvm::outs() << "  - Result " << indexedResult.index();
25563d1dc66SMehdi Amini    if (result.use_empty()) {
25663d1dc66SMehdi Amini      llvm::outs() << " has no uses\n";
25763d1dc66SMehdi Amini      continue;
25863d1dc66SMehdi Amini    }
25963d1dc66SMehdi Amini    if (result.hasOneUse()) {
26063d1dc66SMehdi Amini      llvm::outs() << " has a single use: ";
26163d1dc66SMehdi Amini    } else {
26263d1dc66SMehdi Amini      llvm::outs() << " has "
26363d1dc66SMehdi Amini                   << std::distance(result.getUses().begin(),
26463d1dc66SMehdi Amini                                    result.getUses().end())
26563d1dc66SMehdi Amini                   << " uses:\n";
26663d1dc66SMehdi Amini    }
26763d1dc66SMehdi Amini    for (Operation *userOp : result.getUsers()) {
26863d1dc66SMehdi Amini      llvm::outs() << "    - " << userOp->getName() << "\n";
26963d1dc66SMehdi Amini    }
27063d1dc66SMehdi Amini  }
27163d1dc66SMehdi Amini```
27263d1dc66SMehdi Amini
27363d1dc66SMehdi AminiThe illustrating code for this pass is available
27494fac81fSxgupta[here in the repo](https://github.com/llvm/llvm-project/blob/main/mlir/test/lib/IR/TestPrintDefUse.cpp)
27563d1dc66SMehdi Aminiand can be exercised with `mlir-opt -test-print-defuse`.
27663d1dc66SMehdi Amini
27763d1dc66SMehdi AminiThe chaining of `Value`s and their uses can be viewed as following:
27863d1dc66SMehdi Amini
27963d1dc66SMehdi Amini![Index Map Example](/includes/img/DefUseChains.svg)
28063d1dc66SMehdi Amini
28163d1dc66SMehdi AminiThe uses of a `Value` (`OpOperand` or `BlockOperand`) are also chained in a
28263d1dc66SMehdi Aminidoubly linked-list, which is particularly useful when replacing all uses of a
28363d1dc66SMehdi Amini`Value` with a new one ("RAUW"):
28463d1dc66SMehdi Amini
28563d1dc66SMehdi Amini![Index Map Example](/includes/img/Use-list.svg)
286