diff --git a/src/ir.rs b/src/ir.rs index 41753a4..4164362 100644 --- a/src/ir.rs +++ b/src/ir.rs @@ -13,6 +13,7 @@ pub struct IrProject { pub threads: Vec, pub vars: Rc>, pub targets: Vec, + pub steps: BTreeMap, } impl From for IrProject { @@ -32,7 +33,8 @@ impl From for IrProject { }) .collect(), ); - + + let mut steps: BTreeMap = Default::default(); let mut threads: Vec = vec![]; for (target_index, target) in sb3.targets.iter().enumerate() { for (id, block) in @@ -56,7 +58,7 @@ impl From for IrProject { }), vars: Rc::clone(&vars), }); - let thread = Thread::from_hat(block.clone(), target.blocks.clone(), context); + let thread = Thread::from_hat(block.clone(), target.blocks.clone(), context, &mut steps); threads.push(thread); } } @@ -69,6 +71,7 @@ impl From for IrProject { vars, threads, targets, + steps, } } } @@ -137,11 +140,11 @@ pub enum IrOpcode { event_whenbroadcastreceived, hq_drop(usize), hq_goto { - step: Option>, + step: Option, does_yield: bool, }, hq_goto_if { - step: Option>, + step: Option, does_yield: bool, }, looks_say, @@ -474,7 +477,7 @@ pub struct ThreadContext { #[derive(Debug, Clone, PartialEq)] pub struct Thread { start: ThreadStart, - first_step: Rc, + first_step: String, } trait IrBlockVec { @@ -484,6 +487,7 @@ trait IrBlockVec { blocks: &BTreeMap, context: Rc, last_nexts: Vec, + steps: &mut BTreeMap, ); fn add_block_arr(&mut self, block_arr: &BlockArray); } @@ -544,6 +548,7 @@ impl IrBlockVec for Vec { blocks: &BTreeMap, context: Rc, last_nexts: Vec, + steps: &mut BTreeMap, ) { let block = blocks.get(&block_id).unwrap(); match block { @@ -563,6 +568,7 @@ impl IrBlockVec for Vec { blocks, Rc::clone(&context), last_nexts.clone(), // this probably isn't needed bc inputs donpt have a next? passing an empty vec would save some memory and overhead + steps, ); } BlockArrayOrId::Array(arr) => { @@ -648,7 +654,9 @@ impl IrBlockVec for Vec { if let Some(ref next) = block_info.next { new_nexts.push(next.clone()); } - vec![IrOpcode::hq_goto_if { step: Some(step_from_top_block(substack_id, new_nexts, blocks, Rc::clone(&context))), does_yield: false, }, IrOpcode::hq_goto { step: if block_info.next.is_some() { Some(step_from_top_block(block_info.next.clone().unwrap(), last_nexts, blocks, Rc::clone(&context))) } else { None }, does_yield: false, }] + step_from_top_block(substack_id.clone(), new_nexts, blocks, Rc::clone(&context), steps); + step_from_top_block(block_info.next.clone().unwrap(), last_nexts, blocks, Rc::clone(&context), steps); + vec![IrOpcode::hq_goto_if { step: Some(substack_id), does_yield: false, }, IrOpcode::hq_goto { step: if block_info.next.is_some() { Some(block_info.next.clone().unwrap()) } else { None }, does_yield: false, }] } BlockOpcode::control_if_else => { let substack_id = if let BlockArrayOrId::Id(id) = block_info.inputs.get("SUBSTACK").expect("missing SUBSTACK input for control_if").get_1().unwrap().clone().unwrap() { id } else { panic!("malformed SUBSTACK input") }; @@ -657,7 +665,9 @@ impl IrBlockVec for Vec { if let Some(ref next) = block_info.next { new_nexts.push(next.clone()); } - vec![IrOpcode::hq_goto_if { step: Some(step_from_top_block(substack_id, new_nexts.clone(), blocks, Rc::clone(&context))), does_yield: false, }, IrOpcode::hq_goto { step: Some(step_from_top_block(substack2_id, new_nexts.clone(), blocks, Rc::clone(&context))), does_yield: false, }] + step_from_top_block(substack_id.clone(), new_nexts.clone(), blocks, Rc::clone(&context), steps); + step_from_top_block(substack2_id.clone(), new_nexts.clone(), blocks, Rc::clone(&context), steps); + vec![IrOpcode::hq_goto_if { step: Some(substack_id), does_yield: false, }, IrOpcode::hq_goto { step: Some(substack2_id), does_yield: false, }] } BlockOpcode::control_repeat => vec![IrOpcode::hq_drop(1)], BlockOpcode::control_repeat_until => vec![IrOpcode::hq_drop(1)], @@ -669,21 +679,26 @@ impl IrBlockVec for Vec { } } -pub fn step_from_top_block( +pub fn step_from_top_block<'a>( top_id: String, mut last_nexts: Vec, blocks: &BTreeMap, context: Rc, -) -> Rc { + steps: &'a mut BTreeMap, +) -> &'a Step { + if steps.contains_key(&top_id) { + return steps.get(&top_id).unwrap(); + } let mut ops: Vec = vec![]; let mut next_block = blocks.get(&top_id).unwrap(); - let mut next_id = Some(top_id); + let mut next_id = Some(top_id.clone()); loop { ops.add_block( next_id.clone().unwrap(), blocks, Rc::clone(&context), last_nexts.clone(), + steps, ); if next_block.block_info().unwrap().next.is_none() { next_id = last_nexts.pop(); @@ -736,47 +751,44 @@ pub fn step_from_top_block( .set_expected_output(ty.clone()); } let mut step = Step::new(ops.clone(), Rc::clone(&context)); - step.opcodes_mut() - .push(if let Some(ref id) = next_id { - IrBlock::from(IrOpcode::hq_goto { - step: Some(Rc::clone(&step_from_top_block( - id.clone(), - last_nexts, - blocks, - Rc::clone(&context), - ))), - does_yield: true, - }) - } else { - IrBlock::from(IrOpcode::hq_goto { - step: None, - does_yield: false, - }) - }); - Rc::from(step) + step.opcodes_mut().push(if let Some(ref id) = next_id { + step_from_top_block(id.clone(), last_nexts, blocks, Rc::clone(&context), steps); + IrBlock::from(IrOpcode::hq_goto { + step: Some(id.clone()), + does_yield: true, + }) + } else { + IrBlock::from(IrOpcode::hq_goto { + step: None, + does_yield: false, + }) + }); + steps.insert(top_id.clone(), step); + steps.get(&top_id).unwrap() } impl Thread { - pub fn new(start: ThreadStart, first_step: Rc) -> Thread { + pub fn new(start: ThreadStart, first_step: String) -> Thread { Thread { start, - first_step: Rc::clone(&first_step), + first_step, } } pub fn start(&self) -> &ThreadStart { &self.start } - pub fn first_step(&self) -> Rc { - Rc::clone(&self.first_step) + pub fn first_step(&self) -> &String { + &self.first_step } pub fn from_hat( hat: Block, blocks: BTreeMap, context: Rc, + steps: &mut BTreeMap, ) -> Thread { - let first_step = if let Block::Normal { block_info, .. } = &hat { + let (first_step_id, _first_step) = if let Block::Normal { block_info, .. } = &hat { if let Some(next_id) = &block_info.next { - step_from_top_block(next_id.clone(), vec![], &blocks, Rc::clone(&context)) + (next_id.clone(), step_from_top_block(next_id.clone(), vec![], &blocks, Rc::clone(&context), steps)) } else { unreachable!(); } @@ -791,7 +803,7 @@ impl Thread { } else { unreachable!() }; - Self::new(start_type, first_step) + Self::new(start_type, first_step_id) } } diff --git a/src/targets/wasm.rs b/src/targets/wasm.rs index e8b8b38..61bbb94 100644 --- a/src/targets/wasm.rs +++ b/src/targets/wasm.rs @@ -18,8 +18,9 @@ use wasm_encoder::{ fn instructions( op: &IrBlock, context: Rc, - step_funcs: &mut IndexMap>, Function, BuildHasherDefault>, + step_funcs: &mut IndexMap, Function, BuildHasherDefault>, string_consts: &mut Vec, + steps: &BTreeMap, ) -> Vec> { use Instruction::*; use IrBlockType::*; @@ -192,8 +193,9 @@ fn instructions( step, does_yield: true, } => { - if let Some(next_step) = step { - let next_step_index = next_step.compile_wasm(step_funcs, string_consts); + if let Some(next_step_id) = step { + let next_step = steps.get(next_step_id).unwrap(); + let next_step_index = (next_step_id, next_step).compile_wasm(step_funcs, string_consts, steps); let thread_indices: u64 = byte_offset::THREADS .try_into() .expect("THREAD_INDICES out of bounds (E018)"); @@ -271,8 +273,9 @@ fn instructions( step, does_yield: false, } => { - if let Some(next_step) = step { - let next_step_index = next_step.compile_wasm(step_funcs, string_consts); + if let Some(next_step_id) = step { + let next_step = steps.get(next_step_id).unwrap(); + let next_step_index = (next_step_id, next_step).compile_wasm(step_funcs, string_consts, steps); vec![ LocalGet(step_func_locals::MEM_LOCATION), I32Const( @@ -345,8 +348,9 @@ fn instructions( step, does_yield: true, } => { - if let Some(next_step) = step { - let next_step_index = next_step.compile_wasm(step_funcs, string_consts); + if let Some(next_step_id) = step { + let next_step = steps.get(next_step_id).unwrap(); + let next_step_index = (next_step_id, next_step).compile_wasm(step_funcs, string_consts, steps); let thread_indices: u64 = byte_offset::THREADS .try_into() .expect("THREAD_INDICES out of bounds (E018)"); @@ -430,8 +434,9 @@ fn instructions( step, does_yield: false, } => { - if let Some(next_step) = step { - let next_step_index = next_step.compile_wasm(step_funcs, string_consts); + if let Some(next_step_id) = step { + let next_step = steps.get(next_step_id).unwrap(); + let next_step_index = (next_step_id, next_step).compile_wasm(step_funcs, string_consts, steps); vec![ I32WrapI64, If(WasmBlockType::Empty), //WasmBlockType::FunctionType(types::NOPARAM_I32)), @@ -555,30 +560,33 @@ fn instructions( pub trait CompileToWasm { fn compile_wasm( &self, - step_funcs: &mut IndexMap>, Function, BuildHasherDefault>, + step_funcs: &mut IndexMap, Function, BuildHasherDefault>, string_consts: &mut Vec, + steps: &BTreeMap, ) -> u32; } impl CompileToWasm for Thread { fn compile_wasm( &self, - step_funcs: &mut IndexMap>, Function, BuildHasherDefault>, + step_funcs: &mut IndexMap, Function, BuildHasherDefault>, string_consts: &mut Vec, + steps: &BTreeMap, ) -> u32 { - self.first_step().compile_wasm(step_funcs, string_consts) + (self.first_step(), steps.get(self.first_step()).unwrap()).compile_wasm(step_funcs, string_consts, steps) } } -impl CompileToWasm for Rc { +impl CompileToWasm for (&String, &Step) { fn compile_wasm( &self, - step_funcs: &mut IndexMap>, Function, BuildHasherDefault>, + step_funcs: &mut IndexMap, Function, BuildHasherDefault>, string_consts: &mut Vec, + steps: &BTreeMap, ) -> u32 { - if step_funcs.contains_key(&Some(Rc::clone(self))) { + if step_funcs.contains_key(&Some(self.0.clone())) { return step_funcs - .get_index_of(&Some(Rc::clone(self))) + .get_index_of(&Some(self.0.clone())) .unwrap() .try_into() .expect("IndexMap index out of bounds"); @@ -591,14 +599,14 @@ impl CompileToWasm for Rc { ValType::I32, ]; let mut func = Function::new_with_locals_types(locals); - for op in self.opcodes() { - let instrs = instructions(op, self.context(), step_funcs, string_consts); + for op in self.1.opcodes() { + let instrs = instructions(op, self.1.context(), step_funcs, string_consts, steps); for instr in instrs { func.instruction(&instr); } } func.instruction(&Instruction::End); - step_funcs.insert(Some(Rc::clone(self)), func); + step_funcs.insert(Some(self.0.clone()), func); (step_funcs.len() - 1) .try_into() .expect("step_funcs length out of bounds") @@ -1097,11 +1105,11 @@ impl From for WebWasmFile { let mut string_consts = vec![String::from("false"), String::from("true")]; - let mut step_funcs: IndexMap>, Function, _> = Default::default(); + let mut step_funcs: IndexMap, Function, _> = Default::default(); step_funcs.insert(None, noop_func); for thread in project.threads { - let first_idx = thread.compile_wasm(&mut step_funcs, &mut string_consts); + let first_idx = thread.compile_wasm(&mut step_funcs, &mut string_consts, &project.steps); thread_indices.push((thread.start().clone(), first_idx)); }