Skip to content
Merged
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: 6 additions & 0 deletions libs/@local/hashql/mir/src/body/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ impl<'heap> Operand<'heap> {
}
}

impl From<!> for Operand<'_> {
fn from(value: !) -> Self {
value
}
}

impl From<Local> for Operand<'_> {
fn from(local: Local) -> Self {
Operand::Place(Place::local(local))
Expand Down
5 changes: 4 additions & 1 deletion libs/@local/hashql/mir/src/builder/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,10 @@ macro_rules! body {
(@type $types:ident; Int) => {
$types.integer()
};
(@type $types:ident; ($($sub:tt),*)) => {
(@type $types:ident; ()) => {
$types.tuple([] as [hashql_core::r#type::TypeId; 0])
};
(@type $types:ident; ($($sub:tt),+)) => {
$types.tuple([$($crate::builder::body!(@type $types; $sub)),*])
};
(@type $types:ident; ($($name:ident: $sub:tt),*)) => {
Expand Down
5 changes: 5 additions & 0 deletions libs/@local/hashql/mir/src/builder/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,11 @@ macro_rules! rvalue {
rv.closure($def, $env)
}; $payload; $($rest)*)
};
($resume:path; $payload:tt; tuple; $($rest:tt)*) => {
$resume!(@rvalue |rv| {
rv.tuple([] as [!; 0])
}; $payload; $($rest)*)
};
($resume:path; $payload:tt; tuple $($members:tt),+; $($rest:tt)*) => {
$resume!(@rvalue |rv| {
let members = [$($crate::builder::_private::operand!(rv; $members)),*];
Expand Down
7 changes: 3 additions & 4 deletions libs/@local/hashql/mir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,15 @@
// Library Features
allocator_api,
assert_matches,
binary_heap_drain_sorted,
const_type_name,
iter_array_chunks,
iter_collect_into,
iter_intersperse,
maybe_uninit_fill,
step_trait,
string_from_utf8_lossy_owned,
try_trait_v2,
step_trait,
maybe_uninit_fill,
binary_heap_into_iter_sorted,
binary_heap_drain_sorted,
)]
#![expect(clippy::indexing_slicing)]
extern crate alloc;
Expand Down
15 changes: 14 additions & 1 deletion libs/@local/hashql/mir/src/pass/transform/inst_simplify/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ use crate::{
location::Location,
operand::Operand,
place::Place,
rvalue::{BinOp, Binary, RValue, Unary},
rvalue::{Aggregate, AggregateKind, BinOp, Binary, RValue, Unary},
statement::Assign,
},
context::MirContext,
Expand Down Expand Up @@ -503,6 +503,19 @@ impl<'heap, A: Allocator> VisitorMut<'heap> for InstSimplifyVisitor<'_, 'heap, A
Ok(())
}

fn visit_rvalue_aggregate(
&mut self,
_: Location,
aggregate: &mut Aggregate<'heap>,
) -> Self::Result<()> {
// Specialize into a unit if it's an empty tuple
if aggregate.kind == AggregateKind::Tuple && aggregate.operands.is_empty() {
self.trampoline = Some(RValue::Load(Operand::Constant(Constant::Unit)));
}

Ok(())
}

fn visit_statement_assign(
&mut self,
location: Location,
Expand Down
73 changes: 50 additions & 23 deletions libs/@local/hashql/mir/src/pass/transform/inst_simplify/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![expect(clippy::min_ident_chars, reason = "tests")]
use std::{io::Write as _, path::PathBuf};
use std::{assert_matches::assert_matches, io::Write as _, path::PathBuf};

use bstr::ByteVec as _;
use hashql_core::{
Expand All @@ -12,8 +12,20 @@ use insta::{Settings, assert_snapshot};

use super::InstSimplify;
use crate::{
body::Body, builder::body, context::MirContext, def::DefIdSlice, intern::Interner,
pass::TransformPass as _, pretty::TextFormat,
body::{
Body,
basic_block::BasicBlockId,
constant::Constant,
operand::Operand,
rvalue::RValue,
statement::{Assign, StatementKind},
},
builder::body,
context::MirContext,
def::DefIdSlice,
intern::Interner,
pass::{Changed, TransformPass as _},
pretty::TextFormat,
};

#[track_caller]
Expand Down Expand Up @@ -64,10 +76,6 @@ fn assert_inst_simplify_pass<'heap>(
assert_snapshot!(name, value);
}

// =============================================================================
// Constant Folding (Bitwise on integers, Unary - not in source language)
// =============================================================================

/// Tests constant folding for bitwise AND on integers.
#[test]
fn const_fold_bit_and() {
Expand Down Expand Up @@ -180,10 +188,6 @@ fn const_fold_unary_neg() {
);
}

// =============================================================================
// Bitwise Identity on Integers (x | 0 => x - not in source language)
// =============================================================================

/// Tests identity simplification for bitwise OR with zero.
#[test]
fn identity_bit_or_zero() {
Expand Down Expand Up @@ -212,10 +216,6 @@ fn identity_bit_or_zero() {
);
}

// =============================================================================
// Identical Operand Patterns (BitAnd/BitOr on integers - not in source)
// =============================================================================

/// Tests idempotent simplification for bitwise AND with identical operands.
#[test]
fn identical_operand_bit_and() {
Expand Down Expand Up @@ -272,10 +272,6 @@ fn identical_operand_bit_or() {
);
}

// =============================================================================
// Block Parameter Propagation (requires CFG control)
// =============================================================================

/// Tests constant propagation through block params with single predecessor.
#[test]
fn block_param_single_predecessor() {
Expand Down Expand Up @@ -381,10 +377,6 @@ fn block_param_predecessors_disagree() {
);
}

// =============================================================================
// Idempotent to Constant Forwarding (requires bitwise op)
// =============================================================================

/// Tests that idempotent simplification propagates constants through the result.
#[test]
fn idempotent_to_const_forwarding() {
Expand Down Expand Up @@ -414,3 +406,38 @@ fn idempotent_to_const_forwarding() {
},
);
}

/// Tests that an empty tuple aggregate is simplified to a unit constant.
#[test]
fn empty_tuple_to_unit() {
let heap = Heap::new();
let interner = Interner::new(&heap);
let env = Environment::new(&heap);

let mut body = body!(interner, env; fn@0/0 -> () {
decl result: ();

bb0() {
result = tuple;
return result;
}
});

let changed = InstSimplify::new().run(
&mut MirContext {
heap: &heap,
env: &env,
interner: &interner,
diagnostics: DiagnosticIssues::new(),
},
&mut body,
);
assert_eq!(changed, Changed::Yes);
assert_matches!(
body.basic_blocks[BasicBlockId::START].statements[0].kind,
StatementKind::Assign(Assign {
lhs: _,
rhs: RValue::Load(Operand::Constant(Constant::Unit))
})
);
}
20 changes: 6 additions & 14 deletions libs/@local/hashql/mir/tests/ui/pass/inline/closure-inline.stdout

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading