Skip to content

Commit 9660fba

Browse files
nikomatsakismark-i-m
authored andcommitted
Explain the unpack! macro
1 parent 02ce4af commit 9660fba

File tree

1 file changed

+42
-14
lines changed

1 file changed

+42
-14
lines changed

src/mir/construction.md

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,48 @@ writes the result into the `RETURN_PLACE`.
4040

4141
## `unpack!` all the things
4242

43-
One important thing of note is the `unpack!` macro, which accompanies all recursive
44-
calls. The macro ensures that you get the result of the recursive call while updating
45-
the basic block that you are now in. As an example: lowering `a + b` will need to do
46-
three somewhat independent things:
47-
48-
* give you an `Rvalue` referring to the result of the operation
49-
* insert an assertion ensuring that the operation does not overflow
50-
* tell you in which basic block you should write further operations into, because
51-
the basic block has changed due to the inserted assertion (assertions terminate
52-
blocks and jump either to a panic block or a newly created block, the latter being
53-
the one you get back).
54-
55-
The `unpack!` macro will call the recursive function you pass it, return the `Rvalue`
56-
and update the basic block by mutating the basic block variable you pass to it.
43+
Functions that generate MIR tend to fall into one of two patterns.
44+
First, if the function generates only statements, then it will take a
45+
basic block as argument onto which those statements should be appended.
46+
It can then return a result as normal:
47+
48+
```rust
49+
fn generate_some_mir(&mut self, block: BasicBlock) -> ResultType {
50+
...
51+
}
52+
```
53+
54+
But there are other functions that may generate new basic blocks as well.
55+
For example, lowering an expression like `if foo { 22 } else { 44 }`
56+
requires generating a small "diamond-shaped graph".
57+
In this case, the functions take a basic block where their code starts
58+
and return a (potentially) new basic block where the code generation ends.
59+
The `BlockAnd` type is used to represent this:
60+
61+
```rust
62+
fn generate_more_mir(&mut self, block: BasicBlock) -> BlockAnd<ResultType> {
63+
...
64+
}
65+
```
66+
67+
When you invoke these functions, it is common to have a local variable `block` that is effectively a "cursor". It represents the point at which we are adding new MIR. When you invoke `generate_more_mir`, you want to update this cursor. You can do this manually, but it's tedious:
68+
69+
```rust
70+
let mut block;
71+
let v = match self.generate_more_mir(..) {
72+
BlockAnd { block: new_block, value: v } => {
73+
block = new_block;
74+
v
75+
}
76+
};
77+
```
78+
79+
For this reason, we offer a macro that lets you write
80+
`let v = unpack!(block = self.generate_more_mir(...))`.
81+
It simply extracts the new block and overwrites the
82+
variable `block` that you named in the `unpack!`.
83+
MIR functions that generate statements always take as argument a
84+
basic block onto which the statements should be appended.
5785

5886
## Lowering expressions into the desired MIR
5987

0 commit comments

Comments
 (0)