From 4ebb22f8bc00956fb670d290449ffc338450cfba Mon Sep 17 00:00:00 2001 From: losfair Date: Tue, 12 Feb 2019 23:15:57 +0800 Subject: [PATCH] Calling conventions, value stack, and runtime stack layout. --- lib/dynasm-backend/src/codegen.rs | 1 + lib/dynasm-backend/src/codegen_x64.rs | 129 ++++++++++++++++++++------ lib/dynasm-backend/src/lib.rs | 1 + lib/dynasm-backend/src/parse.rs | 1 + lib/dynasm-backend/src/stack.rs | 109 ++++++++++++++++++++++ 5 files changed, 215 insertions(+), 26 deletions(-) create mode 100644 lib/dynasm-backend/src/stack.rs diff --git a/lib/dynasm-backend/src/codegen.rs b/lib/dynasm-backend/src/codegen.rs index e1d690ddd..1f39e02f2 100644 --- a/lib/dynasm-backend/src/codegen.rs +++ b/lib/dynasm-backend/src/codegen.rs @@ -7,6 +7,7 @@ pub trait ModuleCodeGenerator { pub trait FunctionCodeGenerator { fn feed_param(&mut self, ty: WpType) -> Result<(), CodegenError>; fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), CodegenError>; + fn begin_body(&mut self) -> Result<(), CodegenError>; fn feed_opcode(&mut self, op: Operator) -> Result<(), CodegenError>; fn finalize(&mut self) -> Result<(), CodegenError>; } diff --git a/lib/dynasm-backend/src/codegen_x64.rs b/lib/dynasm-backend/src/codegen_x64.rs index 5675fac1b..e866097f8 100644 --- a/lib/dynasm-backend/src/codegen_x64.rs +++ b/lib/dynasm-backend/src/codegen_x64.rs @@ -1,5 +1,6 @@ use super::codegen::*; -use dynasmrt::{x64::Assembler, DynasmApi}; +use super::stack::ValueStack; +use dynasmrt::{x64::Assembler, DynamicLabel, DynasmApi, DynasmLabelApi}; use wasmparser::{Operator, Type as WpType}; #[derive(Default)] @@ -8,11 +9,18 @@ pub struct X64ModuleCodeGenerator { } pub struct X64FunctionCode { + id: usize, + begin_label: DynamicLabel, + cleanup_label: DynamicLabel, assembler: Option, locals: Vec, + num_params: usize, current_stack_offset: usize, + callee_managed_stack_offset: usize, + value_stack: ValueStack, } +#[derive(Copy, Clone, Debug)] struct Local { ty: WpType, stack_offset: usize, @@ -26,20 +34,32 @@ impl X64ModuleCodeGenerator { impl ModuleCodeGenerator for X64ModuleCodeGenerator { fn next_function(&mut self) -> Result<&mut X64FunctionCode, CodegenError> { + let mut assembler = match self.functions.last_mut() { + Some(x) => x.assembler.take().unwrap(), + None => match Assembler::new() { + Ok(x) => x, + Err(_) => { + return Err(CodegenError { + message: "cannot initialize assembler", + }) + } + }, + }; + let begin_label = assembler.new_dynamic_label(); + dynasm!( + assembler + ; => begin_label + ); let code = X64FunctionCode { - assembler: Some(match self.functions.last_mut() { - Some(x) => x.assembler.take().unwrap(), - None => match Assembler::new() { - Ok(x) => x, - Err(_) => { - return Err(CodegenError { - message: "cannot initialize assembler", - }) - } - }, - }), + id: self.functions.len(), + begin_label: begin_label, + cleanup_label: assembler.new_dynamic_label(), + assembler: Some(assembler), locals: vec![], + num_params: 0, current_stack_offset: 0, + callee_managed_stack_offset: 0, + value_stack: ValueStack::new(13), }; self.functions.push(code); Ok(self.functions.last_mut().unwrap()) @@ -49,41 +69,98 @@ impl ModuleCodeGenerator for X64ModuleCodeGenerator { impl FunctionCodeGenerator for X64FunctionCode { fn feed_param(&mut self, ty: WpType) -> Result<(), CodegenError> { let size = get_size_of_type(&ty)?; - self.current_stack_offset -= size; + self.current_stack_offset += size; self.locals.push(Local { ty: ty, stack_offset: self.current_stack_offset, }); - // TODO: load parameter values onto stack... + self.num_params += 1; Ok(()) } fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), CodegenError> { let size = get_size_of_type(&ty)?; - let assembler = self.assembler.as_mut().unwrap(); - - dynasm!( - assembler - ; xor rax, rax - ); - for _ in 0..n { // FIXME: check range of n - self.current_stack_offset -= size; + self.current_stack_offset += size; + self.callee_managed_stack_offset += size; self.locals.push(Local { ty: ty, stack_offset: self.current_stack_offset, }); - dynasm!( - assembler - ; mov [rsp - (self.current_stack_offset as i32)], rax - ); } Ok(()) } + fn begin_body(&mut self) -> Result<(), CodegenError> { + let assembler = self.assembler.as_mut().unwrap(); + dynasm!( + assembler + ; mov rax, rsp + ; sub rsp, self.callee_managed_stack_offset as i32 + ; xor rcx, rcx + ); + + for local in &self.locals[self.num_params..] { + let size = get_size_of_type(&local.ty)?; + dynasm!( + assembler + ; sub rax, size as i32 + ); + if size == 4 { + dynasm!( + assembler + ; mov [rax], ecx + ); + } else if size == 8 { + dynasm!( + assembler + ; mov [rax], rcx + ); + } else { + return Err(CodegenError { + message: "unsupported size for type", + }); + } + } + dynasm!( + assembler + ; push rbp + ; mov rbp, rsp + ); + Ok(()) + } fn feed_opcode(&mut self, op: Operator) -> Result<(), CodegenError> { + let assembler = self.assembler.as_mut().unwrap(); + match op { + Operator::GetLocal { local_index } => { + let local_index = local_index as usize; + if local_index >= self.locals.len() { + return Err(CodegenError { + message: "local out of bounds", + }); + } + let local = self.locals[local_index]; + dynasm!( + assembler + ; mov rax, rbp + ; add rax, (self.current_stack_offset - local.stack_offset) as i32 + // TODO: How should we dynamically specify a register? + ); + } + _ => unimplemented!(), + } Ok(()) } fn finalize(&mut self) -> Result<(), CodegenError> { + let assembler = self.assembler.as_mut().unwrap(); + dynasm!( + assembler + ; ud2 + ; => self.cleanup_label + ; mov rsp, rbp + ; pop rbp + ; add rsp, self.current_stack_offset as i32 + ; ret + ); Ok(()) } } diff --git a/lib/dynasm-backend/src/lib.rs b/lib/dynasm-backend/src/lib.rs index b421ad7f7..95aa18e8f 100644 --- a/lib/dynasm-backend/src/lib.rs +++ b/lib/dynasm-backend/src/lib.rs @@ -9,3 +9,4 @@ extern crate dynasm; mod codegen; mod codegen_x64; mod parse; +mod stack; diff --git a/lib/dynasm-backend/src/parse.rs b/lib/dynasm-backend/src/parse.rs index 32fa18ee0..bc5ef9a32 100644 --- a/lib/dynasm-backend/src/parse.rs +++ b/lib/dynasm-backend/src/parse.rs @@ -281,6 +281,7 @@ pub fn read_module, FCG: FunctionCodeGenerator>( let (count, ty) = local?; fcg.feed_local(ty, count as usize)?; } + fcg.begin_body()?; for op in item.get_operators_reader()? { let op = op?; fcg.feed_opcode(op)?; diff --git a/lib/dynasm-backend/src/stack.rs b/lib/dynasm-backend/src/stack.rs new file mode 100644 index 000000000..16937fc7c --- /dev/null +++ b/lib/dynasm-backend/src/stack.rs @@ -0,0 +1,109 @@ +use dynasmrt::DynamicLabel; +use wasmparser::Type as WpType; + +/*#[repr(u8)] +#[derive(Copy, Clone, Debug)] +pub enum RegisterName { + RDI, + RSI, + RDX, + RCX, + R8, + R9, + R10, + R11, + RBX, + R12, + R13, + R14, + R15, + Invalid, +}*/ + +#[derive(Debug)] +pub struct ControlFrame { + pub label: DynamicLabel, + pub loop_like: bool, + pub returns: Vec, + pub value_stack_depth_before: usize, +} + +#[derive(Debug)] +pub struct ControlStack { + pub frames: Vec, +} + +#[derive(Debug)] +pub struct ValueStack { + pub num_regs: u8, + pub values: Vec, +} + +#[derive(Copy, Clone, Debug)] +pub struct ValueInfo { + pub ty: WpType, + pub location: ValueLocation, +} + +#[derive(Copy, Clone, Debug)] +pub enum ValueLocation { + Register(u8), + Stack, +} + +impl ValueStack { + pub fn new(num_regs: u8) -> ValueStack { + ValueStack { + num_regs: num_regs, + values: vec![], + } + } + + fn next_location(&self, loc: &ValueLocation) -> ValueLocation { + match *loc { + ValueLocation::Register(x) => { + if x >= self.num_regs - 1 { + ValueLocation::Stack + } else { + ValueLocation::Register(x + 1) + } + } + ValueLocation::Stack => ValueLocation::Stack, + } + } + + pub fn push(&mut self, ty: WpType) -> ValueLocation { + let loc = self + .values + .last() + .map(|x| self.next_location(&x.location)) + .unwrap_or(ValueLocation::Register(0)); + self.values.push(ValueInfo { + ty: ty, + location: loc, + }); + loc + } + + pub fn pop(&mut self) -> Option { + self.values.pop() + } + + pub fn pop2(&mut self) -> Option<(ValueInfo, ValueInfo)> { + if self.values.len() < 2 { + None + } else { + let v2 = self.values.pop().unwrap(); + let v1 = self.values.pop().unwrap(); + Some((v1, v2)) + } + } + + pub fn peek(&self) -> Option { + self.values.last().cloned() + } + + pub fn reset_depth(&mut self, target_depth: usize) { + self.values.truncate(target_depth); + } +}