Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions libs/@local/hashql/mir/benches/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ fn execution_analysis(criterion: &mut Criterion) {
scratch: &mut *scratch,
};

let _result = core::hint::black_box(analysis.run(context, body));
let _result = core::hint::black_box(analysis.run_in(context, body, context.heap));
});
});

Expand All @@ -123,7 +123,7 @@ fn execution_analysis(criterion: &mut Criterion) {
scratch: &mut *scratch,
};

let _result = core::hint::black_box(analysis.run(context, body));
let _result = core::hint::black_box(analysis.run_in(context, body, context.heap));
},
);
});
Expand All @@ -140,7 +140,7 @@ fn execution_analysis(criterion: &mut Criterion) {
scratch: &mut *scratch,
};

let _result = core::hint::black_box(analysis.run(context, body));
let _result = core::hint::black_box(analysis.run_in(context, body, context.heap));
});
});
}
Expand Down
4 changes: 2 additions & 2 deletions libs/@local/hashql/mir/src/builder/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ impl<'env, 'heap> Deref for BodyBuilder<'env, 'heap> {
///
/// Binary (`bin.<op>`): `==`, `!=`, `<`, `<=`, `>`, `>=`, `&`, `|`, `+`, `-`, `*`, `/`.
///
/// Unary (`un.<op>`): `!`, `neg`.
/// Unary (`un.<op>`): `!`, `neg`, `~`.
#[macro_export]
macro_rules! body {
(
Expand Down Expand Up @@ -364,7 +364,7 @@ macro_rules! body {
$types.unknown()
};
(@type $types:ident; $other:expr) => {
$other($types)
$other(&$types)
};

(@source thunk) => {
Expand Down
17 changes: 17 additions & 0 deletions libs/@local/hashql/mir/src/builder/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use core::ops::Deref;
use hashql_core::{
heap::{self, FromIteratorIn as _},
id::IdVec,
symbol::Symbol,
r#type::builder::IntoSymbol,
};
use hashql_hir::node::operation::{InputOp, UnOp};
Expand Down Expand Up @@ -158,6 +159,16 @@ impl<'env, 'heap> RValueBuilder<'env, 'heap> {
})
}

pub fn opaque(self, id: Symbol<'heap>, value: impl Into<Operand<'heap>>) -> RValue<'heap> {
let mut operands = IdVec::with_capacity_in(1, self.interner.heap);
operands.push(value.into());

RValue::Aggregate(Aggregate {
kind: AggregateKind::Opaque(id),
operands,
})
}

/// Creates a function application r-value.
#[must_use]
pub fn apply(
Expand Down Expand Up @@ -235,6 +246,12 @@ macro_rules! rvalue {
rv.tuple(members)
}; $payload; $($rest)*)
};
($resume:path; $payload:tt; opaque $name:tt, $value:tt; $($rest:tt)*) => {
$resume!(@rvalue |rv| {
let inner = $crate::builder::_private::operand!(rv; $value);
rv.opaque($name, inner)
}; $payload; $($rest)*)
};
($resume:path; $payload:tt; list; $($rest:tt)*) => {
$resume!(@rvalue |rv| {
rv.list([] as [!; 0])
Expand Down
1 change: 1 addition & 0 deletions libs/@local/hashql/mir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
iter_collect_into,
likely_unlikely,
maybe_uninit_fill,
option_into_flat_iter,
step_trait,
temporary_niche_types,
try_trait_v2,
Expand Down
25 changes: 25 additions & 0 deletions libs/@local/hashql/mir/src/pass/execution/island/graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,24 @@ impl IslandNode {
self.target
}

#[inline]
pub fn members(&self) -> impl Iterator<Item = BasicBlockId> {
match &self.kind {
IslandKind::Exec(exec_island) => Some(exec_island.members.iter()),
IslandKind::Data => None,
}
.into_flat_iter()
}

#[inline]
#[must_use]
pub fn contains(&self, block: BasicBlockId) -> bool {
match &self.kind {
IslandKind::Exec(exec_island) => exec_island.members.contains(block),
IslandKind::Data => false,
}
}

/// Returns the set of traversal paths this island requires.
#[inline]
#[must_use]
Expand Down Expand Up @@ -254,6 +272,13 @@ impl<A: Allocator> IslandGraph<A> {

RequirementResolver::new(self, start, scratch).resolve(&topo);
}

pub fn find(&self, target: TargetId) -> impl Iterator<Item = (IslandId, &'_ IslandNode)> {
self.inner
.iter_nodes()
.filter(move |node| node.data.target == target)
.map(|node| (IslandId::new(node.id().as_u32()), &node.data))
}
}

impl<A: Allocator> DirectedGraph for IslandGraph<A> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub(crate) fn make_targets(assignments: &[TargetId]) -> BasicBlockVec<TargetId,

pub(crate) fn build_graph(body: &Body<'_>, targets: &[TargetId]) -> IslandGraph<Global> {
let target_vec = make_targets(targets);
let islands = IslandPlacement::new().run(body, VertexType::Entity, &target_vec, Global);
let islands = IslandPlacement::new().run_in(body, VertexType::Entity, &target_vec, Global);
IslandGraph::new_in(body, VertexType::Entity, islands, Global, Global)
}

Expand Down
6 changes: 3 additions & 3 deletions libs/@local/hashql/mir/src/pass/execution/island/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ impl Island {
/// Two blocks belong to the same island when they are connected in the CFG (directly or
/// transitively through same-target successors) and share the same [`TargetId`]. The pass
/// uses a union-find to identify these components in nearly linear time.
pub(crate) struct IslandPlacement<A: Allocator> {
scratch: A,
pub(crate) struct IslandPlacement<S: Allocator> {
scratch: S,
}

impl IslandPlacement<Global> {
Expand All @@ -130,7 +130,7 @@ impl<S: Allocator + Clone> IslandPlacement<S> {
///
/// Returns an [`IslandVec`] where each [`Island`] contains the set of blocks that form
/// a connected same-target component. The output is allocated with `alloc`.
pub(crate) fn run<A>(
pub(crate) fn run_in<A>(
&self,
body: &Body<'_>,
vertex: VertexType,
Expand Down
16 changes: 8 additions & 8 deletions libs/@local/hashql/mir/src/pass/execution/island/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ fn single_block() {
});

let targets = make_targets(&[TargetId::Interpreter]);
let islands = IslandPlacement::new().run(&body, VertexType::Entity, &targets, Global);
let islands = IslandPlacement::new().run_in(&body, VertexType::Entity, &targets, Global);

assert_eq!(islands.len(), 1);
assert_eq!(islands[IslandId::new(0)].target(), TargetId::Interpreter);
Expand Down Expand Up @@ -71,7 +71,7 @@ fn same_target_chain() {
});

let targets = make_targets(&[TargetId::Postgres, TargetId::Postgres]);
let islands = IslandPlacement::new().run(&body, VertexType::Entity, &targets, Global);
let islands = IslandPlacement::new().run_in(&body, VertexType::Entity, &targets, Global);

assert_eq!(islands.len(), 1);
assert_eq!(islands[IslandId::new(0)].target(), TargetId::Postgres);
Expand Down Expand Up @@ -101,7 +101,7 @@ fn different_targets() {
});

let targets = make_targets(&[TargetId::Interpreter, TargetId::Postgres]);
let islands = IslandPlacement::new().run(&body, VertexType::Entity, &targets, Global);
let islands = IslandPlacement::new().run_in(&body, VertexType::Entity, &targets, Global);

assert_eq!(islands.len(), 2);

Expand Down Expand Up @@ -153,7 +153,7 @@ fn diamond_same_target() {
TargetId::Interpreter,
TargetId::Interpreter,
]);
let islands = IslandPlacement::new().run(&body, VertexType::Entity, &targets, Global);
let islands = IslandPlacement::new().run_in(&body, VertexType::Entity, &targets, Global);

assert_eq!(islands.len(), 1);
assert_eq!(islands[IslandId::new(0)].target(), TargetId::Interpreter);
Expand Down Expand Up @@ -198,7 +198,7 @@ fn diamond_mixed_targets() {
TargetId::Embedding,
TargetId::Interpreter,
]);
let islands = IslandPlacement::new().run(&body, VertexType::Entity, &targets, Global);
let islands = IslandPlacement::new().run_in(&body, VertexType::Entity, &targets, Global);

// bb0 alone, bb1 alone, bb2 alone, bb3 alone β€” 4 islands, since no same-target
// edges exist between any pair of connected blocks.
Expand Down Expand Up @@ -250,7 +250,7 @@ fn alternating_targets() {
TargetId::Interpreter,
TargetId::Postgres,
]);
let islands = IslandPlacement::new().run(&body, VertexType::Entity, &targets, Global);
let islands = IslandPlacement::new().run_in(&body, VertexType::Entity, &targets, Global);

assert_eq!(islands.len(), 4);
for island_id in islands.ids() {
Expand Down Expand Up @@ -300,7 +300,7 @@ fn transitive_same_target_chain() {
});

let targets = make_targets(&[TargetId::Postgres, TargetId::Postgres, TargetId::Postgres]);
let islands = IslandPlacement::new().run(&body, VertexType::Entity, &targets, Global);
let islands = IslandPlacement::new().run_in(&body, VertexType::Entity, &targets, Global);

assert_eq!(islands.len(), 1);
assert_eq!(islands[IslandId::new(0)].count(), 3);
Expand Down Expand Up @@ -337,7 +337,7 @@ fn island_joins_traversal_paths() {
});

let targets = make_targets(&[TargetId::Interpreter, TargetId::Interpreter]);
let islands = IslandPlacement::new().run(&body, VertexType::Entity, &targets, Global);
let islands = IslandPlacement::new().run_in(&body, VertexType::Entity, &targets, Global);

assert_eq!(islands.len(), 1);
let island = &islands[IslandId::new(0)];
Expand Down
49 changes: 40 additions & 9 deletions libs/@local/hashql/mir/src/pass/execution/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,24 +49,27 @@ use super::analysis::size_estimation::BodyFootprint;
use crate::{
body::{Body, Source, basic_block::BasicBlockVec, local::Local},
context::MirContext,
def::DefIdSlice,
def::{DefIdSlice, DefIdVec},
pass::analysis::size_estimation::InformationRange,
};

pub struct ExecutionAnalysisResidual<A: Allocator> {
pub assignment: BasicBlockVec<TargetId, A>,
pub islands: IslandGraph<A>,
}

pub struct ExecutionAnalysis<'ctx, 'heap, S: Allocator> {
pub footprints: &'ctx DefIdSlice<BodyFootprint<&'heap Heap>>,
pub scratch: S,
}

impl<'heap, S: BumpAllocator> ExecutionAnalysis<'_, 'heap, S> {
pub fn run(
pub fn run_in<A: Allocator + Clone>(
&self,
context: &mut MirContext<'_, 'heap>,
body: &mut Body<'heap>,
) -> (
BasicBlockVec<TargetId, &'heap Heap>,
IslandVec<Island, &'heap Heap>,
) {
alloc: A,
) -> ExecutionAnalysisResidual<A> {
assert_matches!(body.source, Source::GraphReadFilter(_));

let Some(vertex) = VertexType::from_local(context.env, &body.local_decls[Local::VERTEX])
Expand Down Expand Up @@ -129,14 +132,42 @@ impl<'heap, S: BumpAllocator> ExecutionAnalysis<'_, 'heap, S> {
}
.build_in(body, &self.scratch);

let mut assignment = solver.run(context, body);
let mut assignment = solver.run_in(context, body, alloc.clone());

let fusion = BasicBlockFusion::new_in(&self.scratch);
fusion.fuse(body, &mut assignment);

let islands =
IslandPlacement::new_in(&self.scratch).run(body, vertex, &assignment, context.heap);
IslandPlacement::new_in(&self.scratch).run_in(body, vertex, &assignment, &self.scratch);
let islands = IslandGraph::new_in(body, vertex, islands, &self.scratch, alloc);

ExecutionAnalysisResidual {
assignment,
islands,
}
}

pub fn run_all_in<A: Allocator + Clone>(
&self,
context: &mut MirContext<'_, 'heap>,
bodies: &mut DefIdSlice<Body<'heap>>,
alloc: A,
) -> DefIdVec<Option<ExecutionAnalysisResidual<A>>, A> {
let mut items = DefIdVec::with_capacity_in(bodies.len(), alloc.clone());

for (def, body) in bodies.iter_enumerated_mut() {
match body.source {
Source::Ctor(_)
| Source::Closure(_, _)
| Source::Thunk(_, _)
| Source::Intrinsic(_) => continue,
Source::GraphReadFilter(_) => {}
}

let residual = self.run_in(context, body, alloc.clone());
items.insert(def, residual);
}

(assignment, islands)
items
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//! Block ordering uses the MRV (minimum remaining values) heuristic, with highest constraint degree
//! as tie-breaker. Forward checking narrows domains bidirectionally after each assignment.

use core::{alloc::Allocator, cmp, mem};
use core::{alloc::Allocator, mem};
use std::f32;

use hashql_core::{
Expand Down Expand Up @@ -415,13 +415,13 @@ impl<'ctx, 'parent, 'alloc, A: Allocator, S: BumpAllocator>

// Per-unassigned-block: minimum block cost over remaining domain
for block in unfixed {
let mut min_block = ApproxCost::INF;

for target in &block.possible {
min_block = cmp::min(min_block, self.solver.data.blocks.cost(block.id, target));
}
let min_block = block
.possible
.iter()
.map(|target| self.solver.data.blocks.cost(block.id, target))
.min();

if min_block < ApproxCost::INF {
if let Some(min_block) = min_block {
bound += min_block;
}
}
Expand All @@ -446,7 +446,7 @@ impl<'ctx, 'parent, 'alloc, A: Allocator, S: BumpAllocator>

#[expect(clippy::option_if_let_else, reason = "readability")]
let min_trans = if let Some(succ_possible) = succ_domain {
// Both endpoints involve an unfixed block β€” min over all compatible pairs
// Both endpoints involve an unfixed block - min over all compatible pairs
block
.possible
.iter()
Expand All @@ -457,9 +457,8 @@ impl<'ctx, 'parent, 'alloc, A: Allocator, S: BumpAllocator>
.then_some(cost.as_approx())
})
.min()
.unwrap_or(ApproxCost::INF)
} else {
// Successor is fixed (or external) β€” min over block's domain
// Successor is fixed (or external) - min over block's domain
let succ_target = self
.region
.find_block(succ)
Expand All @@ -471,20 +470,17 @@ impl<'ctx, 'parent, 'alloc, A: Allocator, S: BumpAllocator>
})
.or_else(|| self.solver.targets[succ].map(|elem| elem.target));

if let Some(succ_target) = succ_target {
succ_target.and_then(|succ_target| {
block
.possible
.iter()
.filter_map(|source_target| matrix.get(source_target, succ_target))
.map(Cost::as_approx)
.min()
.unwrap_or(ApproxCost::INF)
} else {
ApproxCost::INF
}
})
};

if min_trans < ApproxCost::INF {
if let Some(min_trans) = min_trans {
bound += min_trans;
}
}
Expand Down Expand Up @@ -640,7 +636,7 @@ impl<'ctx, 'parent, 'alloc, A: Allocator, S: BumpAllocator>
solutions
} else {
self.solver
.alloc
.scratch
.allocate_slice_uninit(RETAIN_SOLUTIONS)
.write_filled(Solution::new())
};
Expand Down
Loading
Loading