@@ -8,14 +8,9 @@ Ergonomic API for constructing MIR bodies in tests. Use for testing and benchmar
88
99The ` body! ` macro does not support all MIR constructs. If you need a feature that is not supported, ** do not work around it manually** - instead, stop and request that the feature be added to the macro.
1010
11- ## Quick Start
12-
13- Two approaches are available:
11+ For advanced cases not supported by the macro, see [ mir-fluent-builder.md] ( mir-fluent-builder.md ) .
1412
15- 1 . ** ` body! ` macro** (preferred for complex CFG) - Declarative, IR-like syntax
16- 2 . ** Fluent builder API** - Programmatic construction for simple cases
17-
18- ### ` body! ` Macro (Preferred)
13+ ## Quick Start
1914
2015``` rust
2116use hashql_core :: {heap :: Heap , r#type :: environment :: Environment };
@@ -44,27 +39,6 @@ let body = body!(interner, env; fn@0/1 -> Int {
4439});
4540```
4641
47- ### Fluent Builder API
48-
49- ``` rust
50- use hashql_core :: r#type :: {TypeBuilder , environment :: Environment };
51- use hashql_mir :: {builder :: BodyBuilder , op, scaffold};
52-
53- scaffold! (heap , interner , builder );
54- let env = Environment :: new (& heap );
55-
56- let x = builder . local (" x" , TypeBuilder :: synthetic (& env ). integer ());
57- let const_1 = builder . const_int (1 );
58-
59- let bb0 = builder . reserve_block ([]);
60- builder
61- . build_block (bb0 )
62- . assign_place (x , | rv | rv . load (const_1 ))
63- . ret (x );
64-
65- let body = builder . finish (0 , TypeBuilder :: synthetic (& env ). integer ());
66- ```
67-
6842## ` body! ` Macro Syntax
6943
7044``` text
@@ -83,17 +57,21 @@ body!(interner, env; <source> @ <id> / <arity> -> <return_type> {
8357| Component | Description | Example |
8458| --------- | ----------- | ------- |
8559| ` <source> ` | Body source type | ` fn ` (closure) or ` thunk ` |
86- | ` <id> ` | DefId numeric literal | ` 0 ` , ` 1 ` , ` 42 ` |
60+ | ` <id> ` | DefId ( literal or variable) | ` 0 ` , ` 42 ` , ` my_def_id ` |
8761| ` <arity> ` | Number of function arguments | ` 0 ` , ` 1 ` , ` 2 ` |
8862| ` <return_type> ` | Return type | ` Int ` , ` Bool ` , ` (Int, Bool) ` |
8963
64+ The ` <id> ` can be a numeric literal (` 0 ` , ` 1 ` , ` 42 ` ) or a variable identifier (` callee_id ` , ` my_def_id ` ). When using a variable, it must be a ` DefId ` in scope.
65+
9066### Types
9167
9268| Syntax | Description | Example |
9369| ------ | ----------- | ------- |
9470| ` Int ` | Integer type | ` Int ` |
9571| ` Bool ` | Boolean type | ` Bool ` |
72+ | ` Null ` | Null type | ` Null ` |
9673| ` (T1, T2, ...) ` | Tuple types | ` (Int, Bool, Int) ` |
74+ | ` (T,) ` | Single-element tuple | ` (Int,) ` |
9775| ` (a: T1, b: T2) ` | Struct types | ` (a: Int, b: Bool) ` |
9876| ` [fn(T1, T2) -> R] ` | Closure types | ` [fn(Int) -> Int] ` , ` [fn() -> Bool] ` |
9977| ` \|types\| types.custom() ` | Custom type expression | ` \|t\| t.null() ` |
@@ -106,15 +84,15 @@ Declare field projections after `decl` to access struct/tuple fields as places:
10684@proj <name> = <base>.<field>: <type>, ...;
10785```
10886
109- Example :
87+ Supports nested projections :
11088
11189``` rust
112- let body = body! (interner , env ; fn @ 1 / 0 -> Int {
113- decl closure : [ fn ( Int ) -> Int ] , result : Int ;
114- @ proj closure_fn = closure . 0 : [ fn (Int ) -> Int ], closure_env = closure . 1 : Int ;
90+ let body = body! (interner , env ; fn @ 0 / 0 -> Int {
91+ decl tup : (( Int , Int ), Int ) , result : Int ;
92+ @ proj inner = tup . 0 : (Int , Int ), inner_1 = tup . 0 .1: Int ;
11593
11694 bb0 () {
117- result = apply closure_fn , closure_env ;
95+ result = load inner_1 ;
11896 return result ;
11997 }
12098});
@@ -153,113 +131,20 @@ let body = body!(interner, env; fn@1/0 -> Int {
153131
154132| Syntax | Description |
155133| ------ | ----------- |
156- | ` x ` , ` cond ` | Place (local variable) |
134+ | ` x ` , ` cond ` | Place (local variable or projection ) |
157135| ` 42 ` , ` -5 ` | Integer literal (i64) |
158136| ` 3.14 ` | Float literal (f64) |
159137| ` true ` , ` false ` | Boolean literal |
160138| ` () ` | Unit |
161139| ` null ` | Null |
162- | ` fn() @ def_id` | Function pointer |
140+ | ` def_id ` | DefId variable (for function pointers) |
163141
164142### Operators
165143
166144** Binary** (` bin.<op> ` ): ` == ` , ` != ` , ` < ` , ` <= ` , ` > ` , ` >= ` , ` & ` , ` | ` , ` + ` , ` - ` , ` * ` , ` / `
167145
168146** Unary** (` un.<op> ` ): ` ! ` , ` neg `
169147
170- ## Fluent Builder Reference
171-
172- ### ` scaffold! ` Macro
173-
174- Sets up heap, interner, and builder:
175-
176- ``` rust
177- scaffold! (heap , interner , builder );
178- let env = Environment :: new (& heap ); // Also needed for types
179- ```
180-
181- ### ` op! ` Macro
182-
183- Creates operators for fluent builder binary/unary operations:
184-
185- ``` rust
186- // Binary: ==, !=, <, <=, >, >=, &, |, +, -, *, /
187- rv . binary (x , op! [== ], y )
188-
189- // Unary: !, neg
190- rv . unary (op! [! ], cond )
191- rv . unary (op! [neg ], value )
192- ```
193-
194- ### Locals and Types
195-
196- ``` rust
197- let env = Environment :: new (& heap );
198-
199- // Common types
200- let int_ty = TypeBuilder :: synthetic (& env ). integer ();
201- let bool_ty = TypeBuilder :: synthetic (& env ). boolean ();
202- let null_ty = TypeBuilder :: synthetic (& env ). null ();
203-
204- // Declare locals
205- let x = builder . local (" x" , int_ty ); // Returns Place<'heap>
206- ```
207-
208- ### Constants
209-
210- ``` rust
211- let const_42 = builder . const_int (42 );
212- let const_true = builder . const_bool (true );
213- let const_unit = builder . const_unit ();
214- let const_null = builder . const_null ();
215- let const_fn = builder . const_fn (def_id );
216- ```
217-
218- ### Basic Blocks
219-
220- ``` rust
221- // Reserve without parameters
222- let bb0 = builder . reserve_block ([]);
223-
224- // Reserve with block parameters (for SSA phi-like merging)
225- let bb1 = builder . reserve_block ([x . local, y . local]);
226- ```
227-
228- ### Building Blocks
229-
230- ``` rust
231- builder
232- . build_block (bb0 )
233- . assign_place (x , | rv | rv . load (const_1 ))
234- . assign_place (y , | rv | rv . binary (x , op! [== ], x ))
235- . storage_live (local )
236- . storage_dead (local )
237- . nop ()
238- . ret (result ); // Must end with terminator
239- ```
240-
241- ### Terminators
242-
243- ``` rust
244- // Return
245- builder . build_block (bb ). ret (value );
246-
247- // Goto
248- builder . build_block (bb0 ). goto (bb1 , []);
249- builder . build_block (bb0 ). goto (bb1 , [x . into (), y . into ()]);
250-
251- // If-else
252- builder . build_block (bb0 ). if_else (cond , bb_then , [], bb_else , []);
253-
254- // Switch
255- builder . build_block (bb0 ). switch (selector , | switch | {
256- switch . case (0 , bb1 , []). case (1 , bb2 , []). otherwise (bb3 , [])
257- });
258-
259- // Unreachable
260- builder . build_block (bb ). unreachable ();
261- ```
262-
263148## Common Patterns
264149
265150### Diamond CFG (Branch and Merge)
@@ -327,17 +212,65 @@ let body = body!(interner, env; fn@0/0 -> Null {
327212});
328213```
329214
330- ### Function Calls
215+ ### Direct Function Calls
216+
217+ Use a ` DefId ` variable directly:
218+
219+ ``` rust
220+ let callee_id = DefId :: new (1 );
221+
222+ let body = body! (interner , env ; fn @ 0 / 0 -> Int {
223+ decl result : Int ;
224+
225+ bb0 () {
226+ result = apply callee_id ;
227+ return result ;
228+ }
229+ });
230+ ```
231+
232+ ### Indirect Function Calls (via local)
233+
234+ Load a DefId into a local, then apply the local:
235+
236+ ``` rust
237+ let callee_id = DefId :: new (1 );
238+
239+ let body = body! (interner , env ; fn @ 0 / 0 -> Int {
240+ decl func : [fn (Int ) -> Int ], result : Int ;
241+
242+ bb0 () {
243+ func = load callee_id ;
244+ result = apply func , 1 ;
245+ return result ;
246+ }
247+ });
248+ ```
249+
250+ ### Multiple Bodies with DefId Variables
251+
252+ When creating multiple bodies that reference each other:
331253
332254``` rust
333- let body = body! (interner , env ; fn @ 1 / 0 -> Int {
255+ let callee_id = DefId :: new (1 );
256+ let caller_id = DefId :: new (0 );
257+
258+ let caller = body! (interner , env ; fn @ caller_id / 0 -> Int {
334259 decl result : Int ;
335260
336261 bb0 () {
337- result = apply fn () @ callee_def_id ;
262+ result = apply callee_id ;
338263 return result ;
339264 }
340265});
266+
267+ let callee = body! (interner , env ; fn @ callee_id / 0 -> Int {
268+ decl ret : Int ;
269+
270+ bb0 () {
271+ return ret ;
272+ }
273+ });
341274```
342275
343276### Struct Aggregate
@@ -380,6 +313,28 @@ let body1 = body!(interner, env; fn@1/0 -> Int {
380313});
381314```
382315
316+ ### Projections in Terminators
317+
318+ Projected places can be used as operands in terminators:
319+
320+ ``` rust
321+ let body = body! (interner , env ; fn @ 0 / 0 -> Int {
322+ decl tup : (Int , Int ), result : Int ;
323+ @ proj tup_0 = tup . 0 : Int , tup_1 = tup . 1 : Int ;
324+
325+ bb0 () {
326+ tup = tuple 1 , 2 ;
327+ if tup_0 then bb1 (tup_0 ) else bb2 (tup_1 );
328+ },
329+ bb1 (result ) {
330+ return result ;
331+ },
332+ bb2 (result ) {
333+ return result ;
334+ }
335+ });
336+ ```
337+
383338## Test Harness Pattern
384339
385340Standard pattern used across transform pass tests:
@@ -485,28 +440,23 @@ fn test_case() {
485440}
486441```
487442
488- ## RValue Types (Fluent Builder)
489-
490- | Method | Creates | Example |
491- | ------ | ------- | ------- |
492- | ` load(operand) ` | Copy/move | ` rv.load(x) ` |
493- | ` binary(l, op, r) ` | Binary op | ` rv.binary(x, op![+], y) ` |
494- | ` unary(op, val) ` | Unary op | ` rv.unary(op![!], cond) ` |
495- | ` tuple([...]) ` | Tuple | ` rv.tuple([x, y, z]) ` |
496- | ` list([...]) ` | List | ` rv.list([a, b, c]) ` |
497- | ` struct([...]) ` | Struct | ` rv.r#struct([("x", val)]) ` |
498- | ` closure(def, env) ` | Closure | ` rv.closure(def_id, env_place) ` |
499- | ` dict([...]) ` | Dict | ` rv.dict([(k, v)]) ` |
500- | ` apply(fn, args) ` | Call | ` rv.apply(func, [arg1]) ` |
501- | ` call(fn) ` | Call (no args) | ` rv.call(func) ` |
502- | ` input(op, name) ` | Input | ` rv.input(InputOp::Load { required: true }, "x") ` |
503-
504443## Examples in Codebase
505444
506- Real test examples in ` libs/@local/hashql/mir/src/pass/transform/ ` :
445+ Real test examples in ` libs/@local/hashql/mir/src/pass/ ` :
507446
508- - ` administrative_reduction/tests.rs ` - Administrative reduction (uses ` body! ` macro)
447+ ** Transform passes** (` transform/ ` ):
448+
449+ - ` administrative_reduction/tests.rs `
509450- ` dse/tests.rs ` - Dead Store Elimination
510451- ` ssa_repair/tests.rs ` - SSA Repair
511452- ` cfg_simplify/tests.rs ` - CFG Simplification
512453- ` dbe/tests.rs ` - Dead Block Elimination
454+ - ` cp/tests.rs ` - Constant Propagation
455+ - ` dle/tests.rs ` - Dead Local Elimination
456+ - ` inst_simplify/tests.rs ` - Instruction Simplification
457+
458+ ** Analysis passes** (` analysis/ ` ):
459+
460+ - ` callgraph/tests.rs ` - Call graph analysis
461+ - ` data_dependency/tests.rs ` - Data dependency analysis
462+ - ` dataflow/liveness/tests.rs ` - Liveness analysis
0 commit comments