@@ -3593,17 +3593,17 @@ pub mod terms {
35933593 use super::{BlockType, FunctionSignature, Instruction, Module, Value, ValueType};
35943594 use anyhow::{Result, anyhow};
35953595 use loom_isle::{
3596- Imm32, Imm64, ImmF32, ImmF64, block, br, br_if, br_table, call, call_indirect, drop_instr ,
3597- f32_convert_i32_s, f32_convert_i32_u, f32_convert_i64_s, f32_convert_i64_u, f32_demote_f64 ,
3598- f32_load, f32_reinterpret_i32, f32_store, f64_convert_i32_s, f64_convert_i32_u ,
3599- f64_convert_i64_s, f64_convert_i64_u, f64_load, f64_promote_f32, f64_reinterpret_i64 ,
3600- f64_store, fabs32, fabs64, fadd32, fadd64, fceil32, fceil64, fconst32, fconst64 ,
3601- fcopysign32, fcopysign64, fdiv32, fdiv64, feq32, feq64, ffloor32, ffloor64, fge32, fge64 ,
3602- fgt32, fgt64, fle32, fle64, flt32, flt64, fmax32, fmax64, fmin32, fmin64, fmul32, fmul64 ,
3603- fne32, fne64, fnearest32, fnearest64, fneg32, fneg64, fsqrt32, fsqrt64, fsub32, fsub64 ,
3604- ftrunc32, ftrunc64, global_get, global_set, i32_extend8_s, i32_extend16_s, i32_load ,
3605- i32_load8_s, i32_load8_u, i32_load16_s, i32_load16_u, i32_reinterpret_f32, i32_store ,
3606- i32_store8, i32_store16, i32_trunc_f32_s, i32_trunc_f32_u, i32_trunc_f64_s,
3596+ Imm32, Imm64, ImmF32, ImmF64, block, br, br_if, br_table, call, call_indirect, data_drop ,
3597+ drop_instr, f32_convert_i32_s, f32_convert_i32_u, f32_convert_i64_s, f32_convert_i64_u,
3598+ f32_demote_f64, f32_load, f32_reinterpret_i32, f32_store, f64_convert_i32_s,
3599+ f64_convert_i32_u, f64_convert_i64_s, f64_convert_i64_u, f64_load, f64_promote_f32,
3600+ f64_reinterpret_i64, f64_store, fabs32, fabs64, fadd32, fadd64, fceil32, fceil64, fconst32,
3601+ fconst64, fcopysign32, fcopysign64, fdiv32, fdiv64, feq32, feq64, ffloor32, ffloor64,
3602+ fge32, fge64, fgt32, fgt64, fle32, fle64, flt32, flt64, fmax32, fmax64, fmin32, fmin64,
3603+ fmul32, fmul64, fne32, fne64, fnearest32, fnearest64, fneg32, fneg64, fsqrt32, fsqrt64,
3604+ fsub32, fsub64, ftrunc32, ftrunc64, global_get, global_set, i32_extend8_s, i32_extend16_s,
3605+ i32_load, i32_load8_s, i32_load8_u, i32_load16_s, i32_load16_u, i32_reinterpret_f32,
3606+ i32_store, i32_store8, i32_store16, i32_trunc_f32_s, i32_trunc_f32_u, i32_trunc_f64_s,
36073607 i32_trunc_f64_u, i32_trunc_sat_f32_s, i32_trunc_sat_f32_u, i32_trunc_sat_f64_s,
36083608 i32_trunc_sat_f64_u, i32_wrap_i64, i64_extend_i32_s, i64_extend_i32_u, i64_extend8_s,
36093609 i64_extend16_s, i64_extend32_s, i64_load, i64_load8_s, i64_load8_u, i64_load16_s,
@@ -3616,8 +3616,8 @@ pub mod terms {
36163616 iles64, ileu32, ileu64, ilts32, ilts64, iltu32, iltu64, imul32, imul64, ine32, ine64,
36173617 ior32, ior64, ipopcnt32, ipopcnt64, irems32, irems64, iremu32, iremu64, irotl32, irotl64,
36183618 irotr32, irotr64, ishl32, ishl64, ishrs32, ishrs64, ishru32, ishru64, isub32, isub64,
3619- ixor32, ixor64, local_get, local_set, local_tee, loop_construct, memory_grow, memory_size ,
3620- nop, return_val, select_instr, unreachable,
3619+ ixor32, ixor64, local_get, local_set, local_tee, loop_construct, memory_copy, memory_fill ,
3620+ memory_grow, memory_init, memory_size, nop, return_val, select_instr, unreachable,
36213621 };
36223622
36233623 /// Owned context for function signature lookup during ISLE term conversion.
@@ -5196,12 +5196,45 @@ pub mod terms {
51965196 // They are passed through unchanged in the encoding phase
51975197 }
51985198
5199- // Bulk memory instructions - pass through unchanged
5200- Instruction::MemoryFill(_)
5201- | Instruction::MemoryCopy { .. }
5202- | Instruction::MemoryInit { .. }
5203- | Instruction::DataDrop(_) => {
5204- // These don't have ISLE term representations yet
5199+ // Bulk memory instructions - side-effectful, no stack output
5200+ Instruction::MemoryFill(mem) => {
5201+ let len = stack
5202+ .pop()
5203+ .ok_or_else(|| anyhow!("Stack underflow for memory.fill len"))?;
5204+ let val = stack
5205+ .pop()
5206+ .ok_or_else(|| anyhow!("Stack underflow for memory.fill val"))?;
5207+ let dst = stack
5208+ .pop()
5209+ .ok_or_else(|| anyhow!("Stack underflow for memory.fill dst"))?;
5210+ side_effects.push(memory_fill(dst, val, len, *mem));
5211+ }
5212+ Instruction::MemoryCopy { dst_mem, src_mem } => {
5213+ let len = stack
5214+ .pop()
5215+ .ok_or_else(|| anyhow!("Stack underflow for memory.copy len"))?;
5216+ let src = stack
5217+ .pop()
5218+ .ok_or_else(|| anyhow!("Stack underflow for memory.copy src"))?;
5219+ let dst = stack
5220+ .pop()
5221+ .ok_or_else(|| anyhow!("Stack underflow for memory.copy dst"))?;
5222+ side_effects.push(memory_copy(dst, src, len, *dst_mem, *src_mem));
5223+ }
5224+ Instruction::MemoryInit { mem, data_idx } => {
5225+ let len = stack
5226+ .pop()
5227+ .ok_or_else(|| anyhow!("Stack underflow for memory.init len"))?;
5228+ let src = stack
5229+ .pop()
5230+ .ok_or_else(|| anyhow!("Stack underflow for memory.init src"))?;
5231+ let dst = stack
5232+ .pop()
5233+ .ok_or_else(|| anyhow!("Stack underflow for memory.init dst"))?;
5234+ side_effects.push(memory_init(dst, src, len, *mem, *data_idx));
5235+ }
5236+ Instruction::DataDrop(data_idx) => {
5237+ side_effects.push(data_drop(*data_idx));
52055238 }
52065239 }
52075240 }
@@ -5698,6 +5731,46 @@ pub mod terms {
56985731 term_to_instructions_recursive(val, instructions)?;
56995732 instructions.push(Instruction::MemoryGrow(*mem));
57005733 }
5734+ // Bulk memory operations (side-effectful)
5735+ ValueData::MemoryFill { dst, val, len, mem } => {
5736+ term_to_instructions_recursive(dst, instructions)?;
5737+ term_to_instructions_recursive(val, instructions)?;
5738+ term_to_instructions_recursive(len, instructions)?;
5739+ instructions.push(Instruction::MemoryFill(*mem));
5740+ }
5741+ ValueData::MemoryCopy {
5742+ dst,
5743+ src,
5744+ len,
5745+ dst_mem,
5746+ src_mem,
5747+ } => {
5748+ term_to_instructions_recursive(dst, instructions)?;
5749+ term_to_instructions_recursive(src, instructions)?;
5750+ term_to_instructions_recursive(len, instructions)?;
5751+ instructions.push(Instruction::MemoryCopy {
5752+ dst_mem: *dst_mem,
5753+ src_mem: *src_mem,
5754+ });
5755+ }
5756+ ValueData::MemoryInit {
5757+ dst,
5758+ src,
5759+ len,
5760+ mem,
5761+ data_idx,
5762+ } => {
5763+ term_to_instructions_recursive(dst, instructions)?;
5764+ term_to_instructions_recursive(src, instructions)?;
5765+ term_to_instructions_recursive(len, instructions)?;
5766+ instructions.push(Instruction::MemoryInit {
5767+ mem: *mem,
5768+ data_idx: *data_idx,
5769+ });
5770+ }
5771+ ValueData::DataDrop { data_idx } => {
5772+ instructions.push(Instruction::DataDrop(*data_idx));
5773+ }
57015774 // Sign extension operations
57025775 ValueData::I32Extend8S { val } => {
57035776 term_to_instructions_recursive(val, instructions)?;
@@ -6530,12 +6603,9 @@ pub mod optimize {
65306603
65316604 /// Helper: Check if a function contains instructions not supported by ISLE term conversion
65326605 ///
6533- /// The instructions_to_terms function only handles a subset of WASM instructions.
6534- /// For unsupported instructions (floats, conversions, rotations, etc.), it doesn't
6535- /// properly simulate stack effects, which causes stack underflow errors.
6536- ///
6537- /// This function identifies functions that should skip ISLE-based optimization
6538- /// to avoid corrupting the stack simulation.
6606+ /// Currently only `Unknown` instructions are unsupported — all standard WASM
6607+ /// instructions (integer, float, conversion, memory, control flow, call_indirect,
6608+ /// br_table, bulk memory) are fully wired into the ISLE pipeline.
65396609 fn has_unsupported_isle_instructions(func: &Function) -> bool {
65406610 has_unsupported_isle_instructions_in_block(&func.instructions)
65416611 }
@@ -6544,27 +6614,25 @@ pub mod optimize {
65446614 for instr in instructions {
65456615 match instr {
65466616 // Recursively check nested blocks
6547- Instruction::Block { body, .. }
6548- | Instruction::Loop { body, .. } => {
6617+ Instruction::Block { body, .. } | Instruction::Loop { body, .. } => {
65496618 if has_unsupported_isle_instructions_in_block(body) {
65506619 return true;
65516620 }
65526621 }
6553- Instruction::If { then_body, else_body, .. } => {
6622+ Instruction::If {
6623+ then_body,
6624+ else_body,
6625+ ..
6626+ } => {
65546627 if has_unsupported_isle_instructions_in_block(then_body)
65556628 || has_unsupported_isle_instructions_in_block(else_body)
65566629 {
65576630 return true;
65586631 }
65596632 }
65606633
6561- // Bulk memory operations (no ISLE term representation yet)
6562- Instruction::MemoryFill(_)
6563- | Instruction::MemoryCopy { .. }
6564- | Instruction::MemoryInit { .. }
6565- | Instruction::DataDrop(_)
6566- // Unknown instructions
6567- | Instruction::Unknown(_) => {
6634+ // Unknown instructions (opaque — cannot model stack effects)
6635+ Instruction::Unknown(_) => {
65686636 return true;
65696637 }
65706638
@@ -14517,4 +14585,49 @@ mod tests {
1451714585 func.instructions
1451814586 );
1451914587 }
14588+
14589+ #[test]
14590+ fn test_bulk_memory_not_skipped() {
14591+ // Functions with bulk memory ops should NOT be skipped from optimization
14592+ let wat = r#"
14593+ (module
14594+ (memory 1)
14595+ (data $d "hello")
14596+ (func $bulk_test
14597+ i32.const 1
14598+ i32.const 1
14599+ i32.add
14600+ i32.const 0
14601+ i32.const 5
14602+ memory.fill
14603+ )
14604+ )
14605+ "#;
14606+
14607+ let mut module = parse::parse_wat(wat).expect("Failed to parse WAT");
14608+ optimize::optimize_module(&mut module).expect("Failed to optimize");
14609+
14610+ let func = &module.functions[0];
14611+ // i32.const 1 + i32.const 1 should be folded to i32.const 2
14612+ assert!(
14613+ func.instructions.contains(&Instruction::I32Const(2)),
14614+ "Expected constant folding to occur in function with memory.fill: {:?}",
14615+ func.instructions
14616+ );
14617+ // memory.fill should still be present
14618+ assert!(
14619+ func.instructions.contains(&Instruction::MemoryFill(0)),
14620+ "Expected memory.fill to be preserved: {:?}",
14621+ func.instructions
14622+ );
14623+ }
14624+
14625+ #[test]
14626+ fn test_data_drop_round_trip() {
14627+ // data.drop goes through ISLE terms and back unchanged
14628+ let instructions = vec![Instruction::DataDrop(0), Instruction::End];
14629+ let terms = terms::instructions_to_terms(&instructions).expect("Failed to convert");
14630+ let result = terms::terms_to_instructions(&terms).expect("Failed to convert back");
14631+ assert_eq!(result, vec![Instruction::DataDrop(0)]);
14632+ }
1452014633}
0 commit comments