Skip to content

Commit 65fca0c

Browse files
committed
feat: create new inlineable cfg benchmark
1 parent c585cff commit 65fca0c

File tree

2 files changed

+149
-46
lines changed

2 files changed

+149
-46
lines changed

libs/@local/hashql/mir/benches/transform.rs

Lines changed: 137 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use hashql_core::{
1616
use hashql_diagnostics::DiagnosticIssues;
1717
use hashql_mir::{
1818
body::Body,
19-
builder::BodyBuilder,
19+
builder::{BodyBuilder, body},
2020
context::MirContext,
2121
def::DefId,
2222
intern::Interner,
@@ -38,7 +38,10 @@ use hashql_mir::{
3838
/// bb1: y = x == 2; goto bb2
3939
/// bb2: z = y == 3; return z
4040
/// ```
41-
fn create_linear_cfg<'heap>(env: &Environment<'heap>, interner: &Interner<'heap>) -> Body<'heap> {
41+
fn create_linear_cfg<'heap>(
42+
env: &Environment<'heap>,
43+
interner: &Interner<'heap>,
44+
) -> [Body<'heap>; 1] {
4245
let mut builder = BodyBuilder::new(interner);
4346
let int_ty = TypeBuilder::synthetic(env).integer();
4447

@@ -71,7 +74,8 @@ fn create_linear_cfg<'heap>(env: &Environment<'heap>, interner: &Interner<'heap>
7174

7275
let mut body = builder.finish(0, TypeBuilder::synthetic(env).integer());
7376
body.id = DefId::new(0);
74-
body
77+
78+
[body]
7579
}
7680

7781
/// Creates a branching CFG body with a diamond pattern for benchmarking.
@@ -83,7 +87,10 @@ fn create_linear_cfg<'heap>(env: &Environment<'heap>, interner: &Interner<'heap>
8387
/// bb2: b = 20; goto bb3
8488
/// bb3(p): result = p; return result
8589
/// ```
86-
fn create_diamond_cfg<'heap>(env: &Environment<'heap>, interner: &Interner<'heap>) -> Body<'heap> {
90+
fn create_diamond_cfg<'heap>(
91+
env: &Environment<'heap>,
92+
interner: &Interner<'heap>,
93+
) -> [Body<'heap>; 1] {
8794
let mut builder = BodyBuilder::new(interner);
8895
let int_ty = TypeBuilder::synthetic(env).integer();
8996

@@ -122,7 +129,7 @@ fn create_diamond_cfg<'heap>(env: &Environment<'heap>, interner: &Interner<'heap
122129

123130
let mut body = builder.finish(1, TypeBuilder::synthetic(env).integer());
124131
body.id = DefId::new(0);
125-
body
132+
[body]
126133
}
127134

128135
/// Creates a body with dead code for dead store elimination benchmarking.
@@ -134,7 +141,7 @@ fn create_diamond_cfg<'heap>(env: &Environment<'heap>, interner: &Interner<'heap
134141
fn create_dead_store_cfg<'heap>(
135142
env: &Environment<'heap>,
136143
interner: &Interner<'heap>,
137-
) -> Body<'heap> {
144+
) -> [Body<'heap>; 1] {
138145
let mut builder = BodyBuilder::new(interner);
139146
let int_ty = TypeBuilder::synthetic(env).integer();
140147

@@ -160,7 +167,7 @@ fn create_dead_store_cfg<'heap>(
160167

161168
let mut body = builder.finish(0, TypeBuilder::synthetic(env).integer());
162169
body.id = DefId::new(0);
163-
body
170+
[body]
164171
}
165172

166173
/// Creates a body with patterns that `InstSimplify` can optimize.
@@ -181,7 +188,7 @@ fn create_dead_store_cfg<'heap>(
181188
fn create_inst_simplify_cfg<'heap>(
182189
env: &Environment<'heap>,
183190
interner: &Interner<'heap>,
184-
) -> Body<'heap> {
191+
) -> [Body<'heap>; 1] {
185192
let mut builder = BodyBuilder::new(interner);
186193
let int_ty = TypeBuilder::synthetic(env).integer();
187194
let bool_ty = TypeBuilder::synthetic(env).boolean();
@@ -218,10 +225,10 @@ fn create_inst_simplify_cfg<'heap>(
218225

219226
let mut body = builder.finish(0, TypeBuilder::synthetic(env).boolean());
220227
body.id = DefId::new(0);
221-
body
228+
[body]
222229
}
223230

224-
/// Creates a larger CFG with multiple branches and join points for more realistic benchmarking.
231+
/// Creates a complex CFG with nested loops, back edges, unreachable blocks, and deep nesting.
225232
///
226233
/// Structure:
227234
/// ```text
@@ -234,7 +241,10 @@ fn create_inst_simplify_cfg<'heap>(
234241
/// bb6(p2): f = p2 == 20; goto bb7
235242
/// bb7(p3): result = p3; return result
236243
/// ```
237-
fn create_complex_cfg<'heap>(env: &Environment<'heap>, interner: &Interner<'heap>) -> Body<'heap> {
244+
fn create_complex_cfg<'heap>(
245+
env: &Environment<'heap>,
246+
interner: &Interner<'heap>,
247+
) -> [Body<'heap>; 1] {
238248
let mut builder = BodyBuilder::new(interner);
239249
let int_ty = TypeBuilder::synthetic(env).integer();
240250

@@ -309,17 +319,90 @@ fn create_complex_cfg<'heap>(env: &Environment<'heap>, interner: &Interner<'heap
309319
.assign_place(result, |rv| rv.load(p3))
310320
.ret(result);
311321

312-
builder.finish(1, TypeBuilder::synthetic(env).integer())
322+
let mut body = builder.finish(1, TypeBuilder::synthetic(env).integer());
323+
body.id = DefId::new(0);
324+
[body]
325+
}
326+
327+
/// Creates multiple bodies for inlining benchmarks: a caller with call sites and a callee.
328+
///
329+
/// The caller has multiple call sites in different control flow paths.
330+
///
331+
/// Structure:
332+
/// ```text
333+
/// callee (DefId 0):
334+
/// bb0(arg): cond = arg < 5; if cond then bb1() else bb2()
335+
/// bb1: r1 = arg == 0; return r1
336+
/// bb2: r2 = arg > 10; return r2
337+
///
338+
/// caller (DefId 1):
339+
/// bb0: switch mode [0 => bb1, 1 => bb2, _ => bb3]
340+
/// bb1: x1 = apply callee, 3; goto bb4(x1)
341+
/// bb2: x2 = apply callee, 7; goto bb4(x2)
342+
/// bb3: x3 = apply callee, 0; goto bb4(x3)
343+
/// bb4(p): return p
344+
/// ```
345+
fn create_inlinable_cfg<'heap>(
346+
env: &Environment<'heap>,
347+
interner: &Interner<'heap>,
348+
) -> [Body<'heap>; 2] {
349+
let callee_id = DefId::new(0);
350+
let caller_id = DefId::new(1);
351+
352+
// Callee: non-trivial function with control flow
353+
// Takes one Int arg, returns Bool based on comparisons
354+
let callee = body!(interner, env; fn@callee_id/1 -> Bool {
355+
decl arg0: Int, cond0: Bool, r1: Bool, r2: Bool;
356+
357+
bb0() {
358+
cond0 = bin.< arg0 5;
359+
if cond0 then bb1() else bb2();
360+
},
361+
bb1() {
362+
r1 = bin.== arg0 0;
363+
return r1;
364+
},
365+
bb2() {
366+
r2 = bin.> arg0 10;
367+
return r2;
368+
}
369+
});
370+
371+
// Caller: calls callee from multiple paths
372+
let caller = body!(interner, env; fn@caller_id/1 -> Bool {
373+
decl mode0: Int, x1: Bool, x2: Bool, x3: Bool, p4: Bool;
374+
375+
bb0() {
376+
switch mode0 [0 => bb1(), 1 => bb2(), _ => bb3()];
377+
},
378+
bb1() {
379+
x1 = apply callee_id, 3;
380+
goto bb4(x1);
381+
},
382+
bb2() {
383+
x2 = apply callee_id, 7;
384+
goto bb4(x2);
385+
},
386+
bb3() {
387+
x3 = apply callee_id, 0;
388+
goto bb4(x3);
389+
},
390+
bb4(p4) {
391+
return p4;
392+
}
393+
});
394+
395+
[callee, caller]
313396
}
314397

315398
#[expect(unsafe_code)]
316399
#[inline]
317-
fn run_bencher<T>(
400+
fn run_bencher<T, const N: usize>(
318401
bencher: &mut Bencher,
319-
body: for<'heap> fn(&Environment<'heap>, &Interner<'heap>) -> Body<'heap>,
402+
body: for<'heap> fn(&Environment<'heap>, &Interner<'heap>) -> [Body<'heap>; N],
320403
mut func: impl for<'env, 'heap> FnMut(
321404
&mut MirContext<'env, 'heap>,
322-
&mut Body<'heap>,
405+
&mut [Body<'heap>; N],
323406
&mut Scratch,
324407
) -> T,
325408
) {
@@ -402,19 +485,19 @@ fn cfg_simplify(criterion: &mut Criterion) {
402485
let mut group = criterion.benchmark_group("cfg_simplify");
403486

404487
group.bench_function("linear", |bencher| {
405-
run_bencher(bencher, create_linear_cfg, |context, body, scratch| {
488+
run_bencher(bencher, create_linear_cfg, |context, [body], scratch| {
406489
CfgSimplify::new_in(scratch).run(context, body)
407490
});
408491
});
409492

410493
group.bench_function("diamond", |bencher| {
411-
run_bencher(bencher, create_diamond_cfg, |context, body, scratch| {
494+
run_bencher(bencher, create_diamond_cfg, |context, [body], scratch| {
412495
CfgSimplify::new_in(scratch).run(context, body)
413496
});
414497
});
415498

416499
group.bench_function("complex", |bencher| {
417-
run_bencher(bencher, create_complex_cfg, |context, body, scratch| {
500+
run_bencher(bencher, create_complex_cfg, |context, [body], scratch| {
418501
CfgSimplify::new_in(scratch).run(context, body)
419502
});
420503
});
@@ -424,19 +507,19 @@ fn forward_substitution(criterion: &mut Criterion) {
424507
let mut group = criterion.benchmark_group("forward_substitution");
425508

426509
group.bench_function("linear", |bencher| {
427-
run_bencher(bencher, create_linear_cfg, |context, body, scratch| {
510+
run_bencher(bencher, create_linear_cfg, |context, [body], scratch| {
428511
ForwardSubstitution::new_in(scratch).run(context, body)
429512
});
430513
});
431514

432515
group.bench_function("diamond", |bencher| {
433-
run_bencher(bencher, create_diamond_cfg, |context, body, scratch| {
516+
run_bencher(bencher, create_diamond_cfg, |context, [body], scratch| {
434517
ForwardSubstitution::new_in(scratch).run(context, body)
435518
});
436519
});
437520

438521
group.bench_function("complex", |bencher| {
439-
run_bencher(bencher, create_complex_cfg, |context, body, scratch| {
522+
run_bencher(bencher, create_complex_cfg, |context, [body], scratch| {
440523
ForwardSubstitution::new_in(scratch).run(context, body)
441524
});
442525
});
@@ -446,25 +529,27 @@ fn dse(criterion: &mut Criterion) {
446529
let mut group = criterion.benchmark_group("dse");
447530

448531
group.bench_function("dead stores", |bencher| {
449-
run_bencher(bencher, create_dead_store_cfg, |context, body, scratch| {
450-
DeadStoreElimination::new_in(scratch).run(context, body)
451-
});
532+
run_bencher(
533+
bencher,
534+
create_dead_store_cfg,
535+
|context, [body], scratch| DeadStoreElimination::new_in(scratch).run(context, body),
536+
);
452537
});
453538

454539
group.bench_function("linear", |bencher| {
455-
run_bencher(bencher, create_linear_cfg, |context, body, scratch| {
540+
run_bencher(bencher, create_linear_cfg, |context, [body], scratch| {
456541
DeadStoreElimination::new_in(scratch).run(context, body)
457542
});
458543
});
459544

460545
group.bench_function("diamond", |bencher| {
461-
run_bencher(bencher, create_diamond_cfg, |context, body, scratch| {
546+
run_bencher(bencher, create_diamond_cfg, |context, [body], scratch| {
462547
DeadStoreElimination::new_in(scratch).run(context, body)
463548
});
464549
});
465550

466551
group.bench_function("complex", |bencher| {
467-
run_bencher(bencher, create_complex_cfg, |context, body, scratch| {
552+
run_bencher(bencher, create_complex_cfg, |context, [body], scratch| {
468553
DeadStoreElimination::new_in(scratch).run(context, body)
469554
});
470555
});
@@ -477,23 +562,23 @@ fn inst_simplify(criterion: &mut Criterion) {
477562
run_bencher(
478563
bencher,
479564
create_inst_simplify_cfg,
480-
|context, body, scratch| InstSimplify::new_in(scratch).run(context, body),
565+
|context, [body], scratch| InstSimplify::new_in(scratch).run(context, body),
481566
);
482567
});
483568
group.bench_function("linear", |bencher| {
484-
run_bencher(bencher, create_linear_cfg, |context, body, scratch| {
569+
run_bencher(bencher, create_linear_cfg, |context, [body], scratch| {
485570
InstSimplify::new_in(scratch).run(context, body)
486571
});
487572
});
488573

489574
group.bench_function("diamond", |bencher| {
490-
run_bencher(bencher, create_diamond_cfg, |context, body, scratch| {
575+
run_bencher(bencher, create_diamond_cfg, |context, [body], scratch| {
491576
InstSimplify::new_in(scratch).run(context, body)
492577
});
493578
});
494579

495580
group.bench_function("complex", |bencher| {
496-
run_bencher(bencher, create_complex_cfg, |context, body, scratch| {
581+
run_bencher(bencher, create_complex_cfg, |context, [body], scratch| {
497582
InstSimplify::new_in(scratch).run(context, body)
498583
});
499584
});
@@ -503,8 +588,8 @@ fn pipeline(criterion: &mut Criterion) {
503588
let mut group = criterion.benchmark_group("pipeline");
504589

505590
group.bench_function("linear", |bencher| {
506-
run_bencher(bencher, create_linear_cfg, |context, body, scratch| {
507-
let bodies = IdSlice::from_raw_mut(core::slice::from_mut(body));
591+
run_bencher(bencher, create_linear_cfg, |context, bodies, scratch| {
592+
let bodies = IdSlice::from_raw_mut(bodies);
508593
let mut state = GlobalTransformState::new_in(bodies, context.heap);
509594

510595
let mut changed = PreInline::new_in(&mut *scratch).run(context, &mut state, bodies);
@@ -518,8 +603,8 @@ fn pipeline(criterion: &mut Criterion) {
518603
});
519604
});
520605
group.bench_function("diamond", |bencher| {
521-
run_bencher(bencher, create_diamond_cfg, |context, body, scratch| {
522-
let bodies = IdSlice::from_raw_mut(core::slice::from_mut(body));
606+
run_bencher(bencher, create_diamond_cfg, |context, bodies, scratch| {
607+
let bodies = IdSlice::from_raw_mut(bodies);
523608
let mut state = GlobalTransformState::new_in(bodies, context.heap);
524609

525610
let mut changed = PreInline::new_in(&mut *scratch).run(context, &mut state, bodies);
@@ -533,8 +618,23 @@ fn pipeline(criterion: &mut Criterion) {
533618
});
534619
});
535620
group.bench_function("complex", |bencher| {
536-
run_bencher(bencher, create_complex_cfg, |context, body, scratch| {
537-
let bodies = IdSlice::from_raw_mut(core::slice::from_mut(body));
621+
run_bencher(bencher, create_complex_cfg, |context, bodies, scratch| {
622+
let bodies = IdSlice::from_raw_mut(bodies);
623+
let mut state = GlobalTransformState::new_in(bodies, context.heap);
624+
625+
let mut changed = PreInline::new_in(&mut *scratch).run(context, &mut state, bodies);
626+
scratch.reset();
627+
changed |= Inline::new_in(InlineConfig::default(), &mut *scratch)
628+
.run(context, &mut state, bodies);
629+
scratch.reset();
630+
changed |= PostInline::new_in(&mut *scratch).run(context, &mut state, bodies);
631+
scratch.reset();
632+
changed
633+
});
634+
});
635+
group.bench_function("inline", |bencher| {
636+
run_bencher(bencher, create_inlinable_cfg, |context, bodies, scratch| {
637+
let bodies = IdSlice::from_raw_mut(bodies);
538638
let mut state = GlobalTransformState::new_in(bodies, context.heap);
539639

540640
let mut changed = PreInline::new_in(&mut *scratch).run(context, &mut state, bodies);

libs/@local/hashql/mir/src/pass/analysis/callgraph/mod.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -198,15 +198,18 @@ impl<A: Allocator> CallGraph<'_, A> {
198198
pub fn is_leaf(&self, def: DefId) -> bool {
199199
let def = NodeId::new(def.as_usize());
200200

201-
self.inner.outgoing_edges(def).all(|edge| {
202-
let target = self
203-
.inner
204-
.node(edge.target())
205-
.unwrap_or_else(|| unreachable!("target must exist"));
206-
207-
// Leafs are functions, which can only have intrinsic edges
208-
matches!(target.data, Source::Intrinsic(_))
209-
})
201+
self.inner
202+
.outgoing_edges(def)
203+
.filter(|edge| matches!(edge.data, CallKind::Apply(_)))
204+
.all(|edge| {
205+
let target = self
206+
.inner
207+
.node(edge.target())
208+
.unwrap_or_else(|| unreachable!("target must exist"));
209+
210+
// Leafs are functions, which can only have intrinsic edges
211+
matches!(target.data, Source::Intrinsic(_))
212+
})
210213
}
211214

212215
#[inline]

0 commit comments

Comments
 (0)