Implement float IR operations: 25 words compiled to native WASM f64
Convert 25 float words from host functions to IR primitives: - Stack: FDROP FDUP FSWAP FOVER FNIP FTUCK - Arithmetic: F+ F- F* F/ FNEGATE FABS FSQRT FMIN FMAX FLOOR FROUND - Comparisons: F0= F0< F= F< - Memory: F@ F! - Conversions: S>F F>S 24 new IrOp variants compiled to native WASM f64 instructions. EmitCtx struct threads f64 scratch locals through all emit functions. Float constant folding: 1.5E0 2.5E0 F+ folds to PushF64(4.0). Float peephole: PushF64+FDrop, FDup+FDrop, FSwap+FSwap eliminated. Float literals now compile as PushF64 IR ops instead of anonymous host calls. ~420 lines of Rust closure code removed from outer.rs. All 14 optimizations now implemented. 430 tests passing.
This commit is contained in:
@@ -194,6 +194,26 @@ fn peephole_one_pass(ops: Vec<IrOp>) -> Vec<IrOp> {
|
||||
out.pop();
|
||||
continue;
|
||||
}
|
||||
// PushF64, FDrop => remove both
|
||||
(IrOp::PushF64(_), IrOp::FDrop) => {
|
||||
out.pop();
|
||||
continue;
|
||||
}
|
||||
// FDup, FDrop => remove both
|
||||
(IrOp::FDup, IrOp::FDrop) => {
|
||||
out.pop();
|
||||
continue;
|
||||
}
|
||||
// FSwap, FSwap => remove both
|
||||
(IrOp::FSwap, IrOp::FSwap) => {
|
||||
out.pop();
|
||||
continue;
|
||||
}
|
||||
// FNegate, FNegate => remove both
|
||||
(IrOp::FNegate, IrOp::FNegate) => {
|
||||
out.pop();
|
||||
continue;
|
||||
}
|
||||
// Over, Over => TwoDup
|
||||
(IrOp::Over, IrOp::Over) => {
|
||||
out.pop();
|
||||
@@ -236,6 +256,17 @@ fn constant_fold(ops: Vec<IrOp>) -> Vec<IrOp> {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try float binary fold: last two outputs are PushF64
|
||||
if out.len() >= 2
|
||||
&& let Some(result) =
|
||||
try_float_binary_fold(&out[out.len() - 2], &out[out.len() - 1], &op)
|
||||
{
|
||||
out.pop();
|
||||
out.pop();
|
||||
out.push(IrOp::PushF64(result));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try unary fold: last output is PushI32, current op is foldable
|
||||
if !out.is_empty()
|
||||
&& let Some(result) = try_unary_fold(&out[out.len() - 1], &op)
|
||||
@@ -245,6 +276,15 @@ fn constant_fold(ops: Vec<IrOp>) -> Vec<IrOp> {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try float unary fold: last output is PushF64
|
||||
if !out.is_empty()
|
||||
&& let Some(result) = try_float_unary_fold(&out[out.len() - 1], &op)
|
||||
{
|
||||
out.pop();
|
||||
out.push(IrOp::PushF64(result));
|
||||
continue;
|
||||
}
|
||||
|
||||
out.push(op);
|
||||
}
|
||||
out
|
||||
@@ -317,6 +357,53 @@ fn try_unary_fold(n_op: &IrOp, op: &IrOp) -> Option<i32> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to fold a binary float operation on two constants.
|
||||
fn try_float_binary_fold(a_op: &IrOp, b_op: &IrOp, op: &IrOp) -> Option<f64> {
|
||||
let (a, b) = match (a_op, b_op) {
|
||||
(IrOp::PushF64(a), IrOp::PushF64(b)) => (*a, *b),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
match op {
|
||||
IrOp::FAdd => Some(a + b),
|
||||
IrOp::FSub => Some(a - b),
|
||||
IrOp::FMul => Some(a * b),
|
||||
IrOp::FDiv => {
|
||||
if b != 0.0 {
|
||||
Some(a / b)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
IrOp::FMin => Some(a.min(b)),
|
||||
IrOp::FMax => Some(a.max(b)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to fold a unary float operation on a constant.
|
||||
fn try_float_unary_fold(n_op: &IrOp, op: &IrOp) -> Option<f64> {
|
||||
let n = match n_op {
|
||||
IrOp::PushF64(n) => *n,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
match op {
|
||||
IrOp::FNegate => Some(-n),
|
||||
IrOp::FAbs => Some(n.abs()),
|
||||
IrOp::FSqrt => {
|
||||
if n >= 0.0 {
|
||||
Some(n.sqrt())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
IrOp::FFloor => Some(n.floor()),
|
||||
IrOp::FRound => Some(n.round_ties_even()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Pass 3: Strength reduction
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -779,6 +866,52 @@ mod tests {
|
||||
));
|
||||
}
|
||||
|
||||
// Float peephole tests
|
||||
#[test]
|
||||
fn float_push_fdrop_removed() {
|
||||
assert_eq!(opt(vec![IrOp::PushF64(1.0), IrOp::FDrop]), vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float_fdup_fdrop_removed() {
|
||||
assert_eq!(opt(vec![IrOp::FDup, IrOp::FDrop]), vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float_fswap_fswap_removed() {
|
||||
assert_eq!(opt(vec![IrOp::FSwap, IrOp::FSwap]), vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float_fnegate_fnegate_removed() {
|
||||
assert_eq!(opt(vec![IrOp::FNegate, IrOp::FNegate]), vec![]);
|
||||
}
|
||||
|
||||
// Float constant folding tests
|
||||
#[test]
|
||||
fn float_constant_fold_add() {
|
||||
assert_eq!(
|
||||
opt(vec![IrOp::PushF64(1.5), IrOp::PushF64(2.5), IrOp::FAdd]),
|
||||
vec![IrOp::PushF64(4.0)]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float_constant_fold_negate() {
|
||||
assert_eq!(
|
||||
opt(vec![IrOp::PushF64(3.0), IrOp::FNegate]),
|
||||
vec![IrOp::PushF64(-3.0)]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float_constant_fold_sqrt() {
|
||||
assert_eq!(
|
||||
opt(vec![IrOp::PushF64(9.0), IrOp::FSqrt]),
|
||||
vec![IrOp::PushF64(3.0)]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_inline_large() {
|
||||
let mut bodies = HashMap::new();
|
||||
|
||||
Reference in New Issue
Block a user