Get rid of unneeded local.set/get in common cases

This commit is contained in:
Brian Carroll 2021-10-06 20:07:20 +01:00
parent d6bba482ee
commit af823fe5a8
2 changed files with 86 additions and 62 deletions

View file

@ -265,62 +265,71 @@ impl<'a> WasmBackend<'a> {
} }
} }
/// Load a symbol, e.g. for passing to a function call /// Load symbols to the top of the VM stack
fn load_symbol(&mut self, sym: &Symbol) { fn load_symbols(&mut self, symbols: &[Symbol]) {
let storage = self.get_symbol_storage(sym).to_owned(); if self.instructions.verify_stack_match(symbols) {
match storage { // The symbols were already at the top of the stack, do nothing!
SymbolStorage::VirtualMachineStack { // This should be quite common due to the structure of the Mono IR
vm_state, return;
value_type, }
size, for sym in symbols.iter() {
} => { let storage = self.get_symbol_storage(sym).to_owned();
let next_local_id = self.get_next_local_id(); match storage {
let maybe_next_vm_state = SymbolStorage::VirtualMachineStack {
self.instructions.load_symbol(*sym, vm_state, next_local_id); vm_state,
match maybe_next_vm_state { value_type,
// The act of loading the value changed the VM state, so update it size,
Some(next_vm_state) => { } => {
self.symbol_storage_map.insert( let next_local_id = self.get_next_local_id();
*sym, let maybe_next_vm_state =
SymbolStorage::VirtualMachineStack { self.instructions.load_symbol(*sym, vm_state, next_local_id);
vm_state: next_vm_state, match maybe_next_vm_state {
value_type, // The act of loading the value changed the VM state, so update it
size, Some(next_vm_state) => {
}, self.symbol_storage_map.insert(
); *sym,
} SymbolStorage::VirtualMachineStack {
None => { vm_state: next_vm_state,
// Loading the value required creating a new local, because value_type,
// it was not in a convenient position in the VM stack. size,
self.locals.push(Local::new(1, value_type)); },
self.symbol_storage_map.insert( );
*sym, }
SymbolStorage::Local { None => {
local_id: next_local_id, // Loading the value required creating a new local, because
value_type, // it was not in a convenient position in the VM stack.
size, self.locals.push(Local::new(1, value_type));
}, self.symbol_storage_map.insert(
); *sym,
SymbolStorage::Local {
local_id: next_local_id,
value_type,
size,
},
);
}
} }
} }
} SymbolStorage::Local { local_id, .. }
SymbolStorage::Local { local_id, .. } | SymbolStorage::StackMemory {
| SymbolStorage::StackMemory { location: StackMemoryLocation::PointerArg(local_id),
location: StackMemoryLocation::PointerArg(local_id), ..
.. } => {
} => { self.instructions.push(GetLocal(local_id.0));
self.instructions.push(GetLocal(local_id.0)); self.instructions.set_top_symbol(*sym);
} }
SymbolStorage::StackMemory { SymbolStorage::StackMemory {
location: StackMemoryLocation::FrameOffset(offset), location: StackMemoryLocation::FrameOffset(offset),
.. ..
} => { } => {
self.instructions.extend(&[ self.instructions.extend(&[
GetLocal(self.stack_frame_pointer.unwrap().0), GetLocal(self.stack_frame_pointer.unwrap().0),
I32Const(offset as i32), I32Const(offset as i32),
I32Add, I32Add,
]); ]);
self.instructions.set_top_symbol(*sym);
}
} }
} }
} }
@ -415,7 +424,7 @@ impl<'a> WasmBackend<'a> {
} }
_ => { _ => {
self.load_symbol(sym); self.load_symbols(&[*sym]);
self.instructions.push(Br(self.block_depth)); // jump to end of function (for stack frame pop) self.instructions.push(Br(self.block_depth)); // jump to end of function (for stack frame pop)
} }
} }
@ -541,9 +550,7 @@ impl<'a> WasmBackend<'a> {
arguments, arguments,
}) => match call_type { }) => match call_type {
CallType::ByName { name: func_sym, .. } => { CallType::ByName { name: func_sym, .. } => {
for arg in *arguments { self.load_symbols(*arguments);
self.load_symbol(arg);
}
let function_location = self.proc_symbol_map.get(func_sym).ok_or(format!( let function_location = self.proc_symbol_map.get(func_sym).ok_or(format!(
"Cannot find function {:?} called from {:?}", "Cannot find function {:?} called from {:?}",
func_sym, sym func_sym, sym
@ -691,7 +698,7 @@ impl<'a> WasmBackend<'a> {
} }
}; };
self.instructions.push(GetLocal(to_ptr.0)); self.instructions.push(GetLocal(to_ptr.0));
self.load_symbol(&from_symbol); self.load_symbols(&[from_symbol]);
self.instructions.push(store_instruction); self.instructions.push(store_instruction);
size size
} }
@ -704,9 +711,7 @@ impl<'a> WasmBackend<'a> {
args: &'a [Symbol], args: &'a [Symbol],
return_layout: &Layout<'a>, return_layout: &Layout<'a>,
) -> Result<(), String> { ) -> Result<(), String> {
for arg in args { self.load_symbols(args);
self.load_symbol(arg);
}
let wasm_layout = WasmLayout::new(return_layout); let wasm_layout = WasmLayout::new(return_layout);
self.build_instructions_lowlevel(lowlevel, wasm_layout.value_type())?; self.build_instructions_lowlevel(lowlevel, wasm_layout.value_type())?;
Ok(()) Ok(())
@ -723,7 +728,6 @@ impl<'a> WasmBackend<'a> {
// For those, we'll need to pre-process each argument before the main op, // For those, we'll need to pre-process each argument before the main op,
// so simple arrays of instructions won't work. But there are common patterns. // so simple arrays of instructions won't work. But there are common patterns.
let instructions: &[Instruction] = match lowlevel { let instructions: &[Instruction] = match lowlevel {
// Wasm type might not be enough, may need to sign-extend i8 etc. Maybe in load_symbol?
LowLevel::NumAdd => match return_value_type { LowLevel::NumAdd => match return_value_type {
ValueType::I32 => &[I32Add], ValueType::I32 => &[I32Add],
ValueType::I64 => &[I64Add], ValueType::I64 => &[I64Add],

View file

@ -133,6 +133,26 @@ impl CodeBuilder {
VirtualMachineSymbolState::Pushed { pushed_at } VirtualMachineSymbolState::Pushed { pushed_at }
} }
/// Verify if a sequence of symbols is at the top of the stack
pub fn verify_stack_match(&self, symbols: &[Symbol]) -> bool {
let n_symbols = symbols.len();
let stack_depth = self.vm_stack.len();
if n_symbols > stack_depth {
return false;
}
let offset = stack_depth - n_symbols;
for (i, sym) in symbols.iter().enumerate() {
match self.vm_stack[offset + i] {
Some(stack_symbol) if stack_symbol == *sym => {}
_ => {
return false;
}
}
}
true
}
pub fn load_symbol( pub fn load_symbol(
&mut self, &mut self,
symbol: Symbol, symbol: Symbol,