Skip to content
Merged
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
165 changes: 131 additions & 34 deletions crates/synth-synthesis/src/instruction_selector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5214,22 +5214,53 @@ impl InstructionSelector {
}

I32Or => {
let b = pop_operand(
&mut stack,
&mut next_temp,
&mut instructions,
&mut spill,
&live_params,
idx,
)?;
let a = pop_operand(
&mut stack,
&mut next_temp,
&mut instructions,
&mut spill,
&live_params,
idx,
)?;
// Immediate folding (same shape as I32And, #250): const
// operand 0..=0xFF whose `movw` is at the tail → `orr rd,a,#C`,
// drop the materialization. Encoder ORR-imm hardened in #251.
let fold_imm = foldable_bitwise_imm(wasm_ops, idx).filter(|_| {
matches!(
instructions.last().map(|i| (&i.op, i.source_line)),
Some((ArmOp::Movw { .. }, Some(sl))) if sl == idx - 1
)
});
let (a, op2) = if let Some(c) = fold_imm {
let _b = pop_operand(
&mut stack,
&mut next_temp,
&mut instructions,
&mut spill,
&live_params,
idx,
)?;
Self::drop_prev_const_materialization(&mut instructions, idx - 1);
let a = pop_operand(
&mut stack,
&mut next_temp,
&mut instructions,
&mut spill,
&live_params,
idx,
)?;
(a, Operand2::Imm(c))
} else {
let b = pop_operand(
&mut stack,
&mut next_temp,
&mut instructions,
&mut spill,
&live_params,
idx,
)?;
let a = pop_operand(
&mut stack,
&mut next_temp,
&mut instructions,
&mut spill,
&live_params,
idx,
)?;
(a, Operand2::Reg(b))
};
let dst = if idx == wasm_ops.len() - 1 {
Reg::R0
} else {
Expand All @@ -5239,30 +5270,61 @@ impl InstructionSelector {
op: ArmOp::Orr {
rd: dst,
rn: a,
op2: Operand2::Reg(b),
op2,
},
source_line: Some(idx),
});
stack.push(StackVal::i32(dst));
}

I32Xor => {
let b = pop_operand(
&mut stack,
&mut next_temp,
&mut instructions,
&mut spill,
&live_params,
idx,
)?;
let a = pop_operand(
&mut stack,
&mut next_temp,
&mut instructions,
&mut spill,
&live_params,
idx,
)?;
// Immediate folding (same shape as I32And, #250): const
// operand 0..=0xFF whose `movw` is at the tail → `eor rd,a,#C`,
// drop the materialization. Encoder EOR-imm hardened in #251.
let fold_imm = foldable_bitwise_imm(wasm_ops, idx).filter(|_| {
matches!(
instructions.last().map(|i| (&i.op, i.source_line)),
Some((ArmOp::Movw { .. }, Some(sl))) if sl == idx - 1
)
});
let (a, op2) = if let Some(c) = fold_imm {
let _b = pop_operand(
&mut stack,
&mut next_temp,
&mut instructions,
&mut spill,
&live_params,
idx,
)?;
Self::drop_prev_const_materialization(&mut instructions, idx - 1);
let a = pop_operand(
&mut stack,
&mut next_temp,
&mut instructions,
&mut spill,
&live_params,
idx,
)?;
(a, Operand2::Imm(c))
} else {
let b = pop_operand(
&mut stack,
&mut next_temp,
&mut instructions,
&mut spill,
&live_params,
idx,
)?;
let a = pop_operand(
&mut stack,
&mut next_temp,
&mut instructions,
&mut spill,
&live_params,
idx,
)?;
(a, Operand2::Reg(b))
};
let dst = if idx == wasm_ops.len() - 1 {
Reg::R0
} else {
Expand All @@ -5272,7 +5334,7 @@ impl InstructionSelector {
op: ArmOp::Eor {
rd: dst,
rn: a,
op2: Operand2::Reg(b),
op2,
},
source_line: Some(idx),
});
Expand Down Expand Up @@ -14382,6 +14444,41 @@ mod tests {
);
}

/// VCR-RA-001: `i32.or`/`i32.xor` fold a small const operand into the
/// `ORR`/`EOR` immediate (same shape as `i32.and`, #250; encoder hardened in
/// #251). Drops the `movw`; no register operand.
#[test]
fn i32_or_xor_fold_small_const_into_immediate() {
for op in [WasmOp::I32Or, WasmOp::I32Xor] {
let mut selector = fresh_selector();
let ops = vec![
WasmOp::LocalGet(0),
WasmOp::I32Const(0x7e),
op.clone(),
WasmOp::End,
];
let instrs = selector.select_with_stack(&ops, 1).unwrap();
let movw_126 = instrs
.iter()
.filter(|i| matches!(&i.op, ArmOp::Movw { imm16: 126, .. }))
.count();
let folded = instrs.iter().any(|i| {
matches!(
&i.op,
ArmOp::Orr {
op2: Operand2::Imm(126),
..
} | ArmOp::Eor {
op2: Operand2::Imm(126),
..
}
)
});
assert_eq!(movw_126, 0, "{op:?}: const must be folded away");
assert!(folded, "{op:?}: must use the folded immediate");
}
}

/// VCR-RA-001 immediate folding: `i32.const C (0..=0xFF); i32.and` folds the
/// constant into the `AND` immediate and drops the `movw`. Measured delta on
/// the clamp pattern: 8 → 6 instructions (both `movw #126` eliminated). The
Expand Down
Loading