Basic form of a function call
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
#![allow(dead_code)] // for now
|
#![allow(dead_code)] // for now
|
||||||
|
|
||||||
use error::Error;
|
|
||||||
use dynasmrt::x64::Assembler;
|
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.
|
/// Size of a pointer on the target in bytes.
|
||||||
const WORD_SIZE: u32 = 8;
|
const WORD_SIZE: u32 = 8;
|
||||||
@@ -45,10 +46,7 @@ impl GPRs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn release(&mut self, gpr: GPR) {
|
fn release(&mut self, gpr: GPR) {
|
||||||
assert!(
|
assert!(!self.is_free(gpr), "released register was already free",);
|
||||||
!self.is_free(gpr),
|
|
||||||
"released register was already free",
|
|
||||||
);
|
|
||||||
self.bits |= 1 << gpr;
|
self.bits |= 1 << gpr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,14 +93,7 @@ fn abi_loc_for_arg(pos: u32) -> ArgLocation {
|
|||||||
// TODO: This assumes only system-v calling convention.
|
// 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.
|
// All rest arguments are passed on the stack.
|
||||||
const ARGS_IN_GPRS: &'static [GPR] = &[
|
const ARGS_IN_GPRS: &'static [GPR] = &[RDI, RSI, RDX, RCX, R8, R9];
|
||||||
RDI,
|
|
||||||
RSI,
|
|
||||||
RDX,
|
|
||||||
RCX,
|
|
||||||
R8,
|
|
||||||
R9,
|
|
||||||
];
|
|
||||||
|
|
||||||
if let Some(®) = ARGS_IN_GPRS.get(pos as usize) {
|
if let Some(®) = ARGS_IN_GPRS.get(pos as usize) {
|
||||||
ArgLocation::Reg(reg)
|
ArgLocation::Reg(reg)
|
||||||
@@ -117,33 +108,54 @@ fn abi_loc_for_arg(pos: u32) -> ArgLocation {
|
|||||||
|
|
||||||
pub struct CodeGenSession {
|
pub struct CodeGenSession {
|
||||||
assembler: Assembler,
|
assembler: Assembler,
|
||||||
func_starts: Vec<AssemblyOffset>,
|
func_starts: Vec<(Option<AssemblyOffset>, DynamicLabel)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CodeGenSession {
|
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::<Vec<_>>();
|
||||||
|
|
||||||
CodeGenSession {
|
CodeGenSession {
|
||||||
assembler: Assembler::new().unwrap(),
|
assembler,
|
||||||
func_starts: Vec::new(),
|
func_starts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_context(&mut self) -> Context {
|
pub fn new_context(&mut self, func_idx: u32) -> Context {
|
||||||
let start_offset = self.assembler.offset();
|
{
|
||||||
self.func_starts.push(start_offset);
|
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 {
|
Context {
|
||||||
asm: &mut self.assembler,
|
asm: &mut self.assembler,
|
||||||
start: start_offset,
|
func_starts: &self.func_starts,
|
||||||
regs: Registers::new(),
|
regs: Registers::new(),
|
||||||
sp_depth: StackDepth(0),
|
sp_depth: StackDepth(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_translated_code_section(self) -> Result<TranslatedCodeSection, Error> {
|
pub fn into_translated_code_section(self) -> Result<TranslatedCodeSection, Error> {
|
||||||
let exec_buf = self.assembler
|
let exec_buf = self
|
||||||
|
.assembler
|
||||||
.finalize()
|
.finalize()
|
||||||
.map_err(|_asm| Error::Assembler("assembler error".to_owned()))?;
|
.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::<Vec<_>>();
|
||||||
|
Ok(TranslatedCodeSection {
|
||||||
|
exec_buf,
|
||||||
|
func_starts,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,19 +173,12 @@ impl TranslatedCodeSection {
|
|||||||
|
|
||||||
pub struct Context<'a> {
|
pub struct Context<'a> {
|
||||||
asm: &'a mut Assembler,
|
asm: &'a mut Assembler,
|
||||||
start: AssemblyOffset,
|
func_starts: &'a Vec<(Option<AssemblyOffset>, DynamicLabel)>,
|
||||||
regs: Registers,
|
regs: Registers,
|
||||||
/// Each push and pop on the value stack increments or decrements this value by 1 respectively.
|
/// Each push and pop on the value stack increments or decrements this value by 1 respectively.
|
||||||
sp_depth: StackDepth,
|
sp_depth: StackDepth,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Context<'a> {
|
|
||||||
/// Returns the offset of the first instruction.
|
|
||||||
fn start(&self) -> AssemblyOffset {
|
|
||||||
self.start
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Label in code.
|
/// Label in code.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct Label(DynamicLabel);
|
pub struct Label(DynamicLabel);
|
||||||
@@ -331,6 +336,13 @@ pub fn copy_incoming_arg(ctx: &mut Context, arg_pos: u32) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn call_direct(ctx: &mut Context, index: u32) {
|
||||||
|
let label = &ctx.func_starts[index as usize].1;
|
||||||
|
dynasm!(ctx.asm
|
||||||
|
; call =>*label
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn prologue(ctx: &mut Context, stack_slots: u32) {
|
pub fn prologue(ctx: &mut Context, stack_slots: u32) {
|
||||||
// Align stack slots to the nearest even number. This is required
|
// Align stack slots to the nearest even number. This is required
|
||||||
// by x86-64 ABI.
|
// by x86-64 ABI.
|
||||||
@@ -346,7 +358,11 @@ pub fn prologue(ctx: &mut Context, stack_slots: u32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn epilogue(ctx: &mut Context) {
|
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
|
dynasm!(ctx.asm
|
||||||
; mov rsp, rbp
|
; mov rsp, rbp
|
||||||
; pop rbp
|
; pop rbp
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use backend::*;
|
use backend::*;
|
||||||
use module::TranslationContext;
|
|
||||||
use error::Error;
|
use error::Error;
|
||||||
use wasmparser::{FuncType, FunctionBody, Operator, Type};
|
use module::TranslationContext;
|
||||||
|
use wasmparser::{FunctionBody, Operator, Type};
|
||||||
|
|
||||||
// TODO: Use own declared `Type` enum.
|
// TODO: Use own declared `Type` enum.
|
||||||
|
|
||||||
@@ -90,11 +90,12 @@ impl ControlFrame {
|
|||||||
pub fn translate(
|
pub fn translate(
|
||||||
session: &mut CodeGenSession,
|
session: &mut CodeGenSession,
|
||||||
translation_ctx: &TranslationContext,
|
translation_ctx: &TranslationContext,
|
||||||
func_type: &FuncType,
|
func_idx: u32,
|
||||||
body: &FunctionBody,
|
body: &FunctionBody,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let locals = body.get_locals_reader()?;
|
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 arg_count = func_type.params.len() as u32;
|
||||||
let return_ty = if func_type.returns.len() > 0 {
|
let return_ty = if func_type.returns.len() > 0 {
|
||||||
func_type.returns[0]
|
func_type.returns[0]
|
||||||
@@ -108,7 +109,7 @@ pub fn translate(
|
|||||||
framesize += count;
|
framesize += count;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ctx = session.new_context();
|
let mut ctx = session.new_context(func_idx);
|
||||||
let operators = body.get_operators_reader()?;
|
let operators = body.get_operators_reader()?;
|
||||||
|
|
||||||
prologue(&mut ctx, framesize);
|
prologue(&mut ctx, framesize);
|
||||||
@@ -217,8 +218,9 @@ pub fn translate(
|
|||||||
get_local_i32(&mut ctx, local_index);
|
get_local_i32(&mut ctx, local_index);
|
||||||
}
|
}
|
||||||
Operator::Call { function_index } => {
|
Operator::Call { function_index } => {
|
||||||
// TODO: find out the signature of this function
|
let callee_ty = translation_ctx.func_type(function_index);
|
||||||
// this requires to generalize the function types infrasturcture
|
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
|
// TODO: ensure that this function is locally defined
|
||||||
// We would like to support imported functions at some point
|
// 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.
|
// TODO: pop arguments and move them in appropriate positions.
|
||||||
// only 6 for now.
|
// only 6 for now.
|
||||||
|
|
||||||
// TODO: jump to the specified position
|
call_direct(&mut ctx, function_index);
|
||||||
// this requires us saving function start locations in codegensession.
|
|
||||||
|
|
||||||
panic!()
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
trap(&mut ctx);
|
trap(&mut ctx);
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ impl TranslationContext {
|
|||||||
let func_ty_idx = self.func_ty_indicies[func_idx as usize];
|
let func_ty_idx = self.func_ty_indicies[func_idx as usize];
|
||||||
&self.types[func_ty_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.
|
/// Translate from a slice of bytes holding a wasm module.
|
||||||
@@ -155,8 +157,7 @@ pub fn translate(data: &[u8]) -> Result<TranslatedModule, Error> {
|
|||||||
|
|
||||||
if let SectionCode::Code = section.code {
|
if let SectionCode::Code = section.code {
|
||||||
let code = section.get_code_section_reader()?;
|
let code = section.get_code_section_reader()?;
|
||||||
output.translated_code_section =
|
output.translated_code_section = Some(translate_sections::code(code, &ctx)?);
|
||||||
Some(translate_sections::code(code, &ctx)?);
|
|
||||||
|
|
||||||
reader.skip_custom_sections()?;
|
reader.skip_custom_sections()?;
|
||||||
if reader.eof() {
|
if reader.eof() {
|
||||||
|
|||||||
23
src/tests.rs
23
src/tests.rs
@@ -20,11 +20,7 @@ fn empty() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn adds() {
|
fn adds() {
|
||||||
const CASES: &[(usize, usize, usize)] = &[
|
const CASES: &[(usize, usize, usize)] = &[(5, 3, 8), (0, 228, 228), (usize::max_value(), 1, 0)];
|
||||||
(5, 3, 8),
|
|
||||||
(0, 228, 228),
|
|
||||||
(usize::max_value(), 1, 0),
|
|
||||||
];
|
|
||||||
|
|
||||||
let code = r#"
|
let code = r#"
|
||||||
(module
|
(module
|
||||||
@@ -110,4 +106,21 @@ fn if_without_result() {
|
|||||||
assert_eq!(execute_wat(code, 2, 3), 2);
|
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.
|
// TODO: Add a test that checks argument passing via the stack.
|
||||||
|
|||||||
@@ -85,12 +85,12 @@ pub fn element(elements: ElementSectionReader) -> Result<(), Error> {
|
|||||||
/// Parses the Code section of the wasm module.
|
/// Parses the Code section of the wasm module.
|
||||||
pub fn code(
|
pub fn code(
|
||||||
code: CodeSectionReader,
|
code: CodeSectionReader,
|
||||||
translation_ctx: &TranslationContext
|
translation_ctx: &TranslationContext,
|
||||||
) -> Result<TranslatedCodeSection, Error> {
|
) -> Result<TranslatedCodeSection, Error> {
|
||||||
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() {
|
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, idx as u32, &body?)?;
|
||||||
function_body::translate(&mut session, translation_ctx, &func_ty, &body?)?;
|
|
||||||
}
|
}
|
||||||
Ok(session.into_translated_code_section()?)
|
Ok(session.into_translated_code_section()?)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user