diff --git a/src/backend.rs b/src/backend.rs index e504df7d1e..b3cf12a91b 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -1,8 +1,9 @@ #![allow(dead_code)] // for now -use error::Error; use dynasmrt::x64::Assembler; -use dynasmrt::{DynasmApi, DynasmLabelApi, AssemblyOffset, ExecutableBuffer, DynamicLabel}; +use dynasmrt::{AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi, ExecutableBuffer}; +use error::Error; +use std::iter; /// Size of a pointer on the target in bytes. const WORD_SIZE: u32 = 8; @@ -45,10 +46,7 @@ impl GPRs { } fn release(&mut self, gpr: GPR) { - assert!( - !self.is_free(gpr), - "released register was already free", - ); + assert!(!self.is_free(gpr), "released register was already free",); self.bits |= 1 << gpr; } @@ -93,22 +91,15 @@ enum ArgLocation { /// Get a location for an argument at the given position. fn abi_loc_for_arg(pos: u32) -> ArgLocation { // TODO: This assumes only system-v calling convention. - // In system-v calling convention the first 6 arguments are passed via registers. + // In system-v calling convention the first 6 arguments are passed via registers. // All rest arguments are passed on the stack. - const ARGS_IN_GPRS: &'static [GPR] = &[ - RDI, - RSI, - RDX, - RCX, - R8, - R9, - ]; + const ARGS_IN_GPRS: &'static [GPR] = &[RDI, RSI, RDX, RCX, R8, R9]; if let Some(®) = ARGS_IN_GPRS.get(pos as usize) { ArgLocation::Reg(reg) } else { let stack_pos = pos - ARGS_IN_GPRS.len() as u32; - // +2 is because the first argument is located right after the saved frame pointer slot + // +2 is because the first argument is located right after the saved frame pointer slot // and the incoming return address. let stack_offset = ((stack_pos + 2) * WORD_SIZE) as i32; ArgLocation::Stack(stack_offset) @@ -117,33 +108,54 @@ fn abi_loc_for_arg(pos: u32) -> ArgLocation { pub struct CodeGenSession { assembler: Assembler, - func_starts: Vec, + func_starts: Vec<(Option, DynamicLabel)>, } impl CodeGenSession { - pub fn new() -> Self { + pub fn new(func_count: u32) -> Self { + let mut assembler = Assembler::new().unwrap(); + let func_starts = iter::repeat_with(|| (None, assembler.new_dynamic_label())) + .take(func_count as usize) + .collect::>(); + CodeGenSession { - assembler: Assembler::new().unwrap(), - func_starts: Vec::new(), + assembler, + func_starts, } } - pub fn new_context(&mut self) -> Context { - let start_offset = self.assembler.offset(); - self.func_starts.push(start_offset); + pub fn new_context(&mut self, func_idx: u32) -> Context { + { + let func_start = &mut self.func_starts[func_idx as usize]; + + // At this point we now the exact start address of this function. Save it + // and define dynamic label at this location. + func_start.0 = Some(self.assembler.offset()); + self.assembler.dynamic_label(func_start.1); + } + Context { asm: &mut self.assembler, - start: start_offset, + func_starts: &self.func_starts, regs: Registers::new(), sp_depth: StackDepth(0), } } - pub fn into_translated_code_section(self) -> Result { - let exec_buf = self.assembler + pub fn into_translated_code_section(self) -> Result { + let exec_buf = self + .assembler .finalize() .map_err(|_asm| Error::Assembler("assembler error".to_owned()))?; - Ok(TranslatedCodeSection { exec_buf, func_starts: self.func_starts }) + let func_starts = self + .func_starts + .iter() + .map(|(offset, _)| offset.unwrap()) + .collect::>(); + Ok(TranslatedCodeSection { + exec_buf, + func_starts, + }) } } @@ -161,19 +173,12 @@ impl TranslatedCodeSection { pub struct Context<'a> { asm: &'a mut Assembler, - start: AssemblyOffset, + func_starts: &'a Vec<(Option, DynamicLabel)>, regs: Registers, /// Each push and pop on the value stack increments or decrements this value by 1 respectively. sp_depth: StackDepth, } -impl<'a> Context<'a> { - /// Returns the offset of the first instruction. - fn start(&self) -> AssemblyOffset { - self.start - } -} - /// Label in code. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Label(DynamicLabel); @@ -184,8 +189,8 @@ pub fn create_label(ctx: &mut Context) -> Label { } /// Define the given label at the current position. -/// -/// Multiple labels can be defined at the same position. However, a label +/// +/// Multiple labels can be defined at the same position. However, a label /// can be defined only once. pub fn define_label(ctx: &mut Context, label: Label) { ctx.asm.dynamic_label(label.0); @@ -327,7 +332,14 @@ pub fn copy_incoming_arg(ctx: &mut Context, arg_pos: u32) { // And then move a value from a register into local variable area on the stack. let offset = sp_relative_offset(ctx, arg_pos); dynasm!(ctx.asm - ; mov [rsp + offset], Rq(reg) + ; mov [rsp + offset], Rq(reg) + ); +} + +pub fn call_direct(ctx: &mut Context, index: u32) { + let label = &ctx.func_starts[index as usize].1; + dynasm!(ctx.asm + ; call =>*label ); } @@ -346,7 +358,11 @@ pub fn prologue(ctx: &mut Context, stack_slots: u32) { } pub fn epilogue(ctx: &mut Context) { - assert_eq!(ctx.sp_depth, StackDepth(0), "imbalanced pushes and pops detected"); + assert_eq!( + ctx.sp_depth, + StackDepth(0), + "imbalanced pushes and pops detected" + ); dynasm!(ctx.asm ; mov rsp, rbp ; pop rbp diff --git a/src/function_body.rs b/src/function_body.rs index d0f83fab4b..18733c644a 100644 --- a/src/function_body.rs +++ b/src/function_body.rs @@ -1,7 +1,7 @@ use backend::*; -use module::TranslationContext; use error::Error; -use wasmparser::{FuncType, FunctionBody, Operator, Type}; +use module::TranslationContext; +use wasmparser::{FunctionBody, Operator, Type}; // TODO: Use own declared `Type` enum. @@ -90,11 +90,12 @@ impl ControlFrame { pub fn translate( session: &mut CodeGenSession, translation_ctx: &TranslationContext, - func_type: &FuncType, + func_idx: u32, body: &FunctionBody, ) -> Result<(), Error> { let locals = body.get_locals_reader()?; + let func_type = translation_ctx.func_type(func_idx); let arg_count = func_type.params.len() as u32; let return_ty = if func_type.returns.len() > 0 { func_type.returns[0] @@ -108,7 +109,7 @@ pub fn translate( framesize += count; } - let mut ctx = session.new_context(); + let mut ctx = session.new_context(func_idx); let operators = body.get_operators_reader()?; prologue(&mut ctx, framesize); @@ -217,8 +218,9 @@ pub fn translate( get_local_i32(&mut ctx, local_index); } Operator::Call { function_index } => { - // TODO: find out the signature of this function - // this requires to generalize the function types infrasturcture + let callee_ty = translation_ctx.func_type(function_index); + assert!(callee_ty.params.len() == 0, "is not supported"); + assert!(callee_ty.returns.len() == 0, "is not supported"); // TODO: ensure that this function is locally defined // We would like to support imported functions at some point @@ -226,10 +228,7 @@ pub fn translate( // TODO: pop arguments and move them in appropriate positions. // only 6 for now. - // TODO: jump to the specified position - // this requires us saving function start locations in codegensession. - - panic!() + call_direct(&mut ctx, function_index); } _ => { trap(&mut ctx); diff --git a/src/module.rs b/src/module.rs index e2c71ba989..52f0d61009 100644 --- a/src/module.rs +++ b/src/module.rs @@ -39,6 +39,8 @@ impl TranslationContext { let func_ty_idx = self.func_ty_indicies[func_idx as usize]; &self.types[func_ty_idx as usize] } + + // TODO: type of a global } /// Translate from a slice of bytes holding a wasm module. @@ -155,8 +157,7 @@ pub fn translate(data: &[u8]) -> Result { if let SectionCode::Code = section.code { let code = section.get_code_section_reader()?; - output.translated_code_section = - Some(translate_sections::code(code, &ctx)?); + output.translated_code_section = Some(translate_sections::code(code, &ctx)?); reader.skip_custom_sections()?; if reader.eof() { diff --git a/src/tests.rs b/src/tests.rs index 4a505d6983..980d100bc4 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -20,11 +20,7 @@ fn empty() { #[test] fn adds() { - const CASES: &[(usize, usize, usize)] = &[ - (5, 3, 8), - (0, 228, 228), - (usize::max_value(), 1, 0), - ]; + const CASES: &[(usize, usize, usize)] = &[(5, 3, 8), (0, 228, 228), (usize::max_value(), 1, 0)]; let code = r#" (module @@ -110,4 +106,21 @@ fn if_without_result() { assert_eq!(execute_wat(code, 2, 3), 2); } +#[test] +fn function_call() { + let code = r#" +(module + (func (param i32) (param i32) (result i32) + (call 1) + (get_local 0) + ) + + (func + ) +) + "#; + + assert_eq!(execute_wat(code, 2, 3), 2); +} + // TODO: Add a test that checks argument passing via the stack. diff --git a/src/translate_sections.rs b/src/translate_sections.rs index 431285b42f..94d248d517 100644 --- a/src/translate_sections.rs +++ b/src/translate_sections.rs @@ -85,12 +85,12 @@ pub fn element(elements: ElementSectionReader) -> Result<(), Error> { /// Parses the Code section of the wasm module. pub fn code( code: CodeSectionReader, - translation_ctx: &TranslationContext + translation_ctx: &TranslationContext, ) -> Result { - let mut session = CodeGenSession::new(); + let func_count = code.get_count(); + let mut session = CodeGenSession::new(func_count); for (idx, body) in code.into_iter().enumerate() { - let func_ty = translation_ctx.func_type(idx as u32); - function_body::translate(&mut session, translation_ctx, &func_ty, &body?)?; + function_body::translate(&mut session, translation_ctx, idx as u32, &body?)?; } Ok(session.into_translated_code_section()?) }