Add quickcheck, implement simple binary operations

This commit is contained in:
Jef
2018-12-12 14:02:11 +01:00
parent 5b448ce3c7
commit 5bb7430976
6 changed files with 106 additions and 28 deletions

View File

@@ -16,6 +16,8 @@ capstone = "0.5.0"
failure = "0.1.3" failure = "0.1.3"
failure_derive = "0.1.3" failure_derive = "0.1.3"
wabt = "0.7" wabt = "0.7"
lazy_static = "1.2"
quickcheck = "0.7"
[badges] [badges]
maintenance = { status = "experimental" } maintenance = { status = "experimental" }

View File

@@ -160,6 +160,7 @@ impl CodeGenSession {
} }
} }
#[derive(Debug)]
pub struct TranslatedCodeSection { pub struct TranslatedCodeSection {
exec_buf: ExecutableBuffer, exec_buf: ExecutableBuffer,
func_starts: Vec<AssemblyOffset>, func_starts: Vec<AssemblyOffset>,
@@ -170,6 +171,10 @@ impl TranslatedCodeSection {
let offset = self.func_starts[idx]; let offset = self.func_starts[idx];
self.exec_buf.ptr(offset) self.exec_buf.ptr(offset)
} }
pub fn disassemble(&self) {
::disassemble::disassemble(&*self.exec_buf).unwrap();
}
} }
pub struct Context<'a> { pub struct Context<'a> {
@@ -238,14 +243,64 @@ fn pop_i32(ctx: &mut Context) -> GPR {
gpr gpr
} }
pub fn add_i32(ctx: &mut Context) { pub fn i32_add(ctx: &mut Context) {
let op0 = pop_i32(ctx); let op0 = pop_i32(ctx);
let op1 = pop_i32(ctx); let op1 = pop_i32(ctx);
dynasm!(ctx.asm dynasm!(ctx.asm
; add Rd(op0), Rd(op1) ; add Rd(op1), Rd(op0)
); );
push_i32(ctx, op0); push_i32(ctx, op1);
ctx.regs.release_scratch_gpr(op1); ctx.regs.release_scratch_gpr(op0);
}
pub fn i32_sub(ctx: &mut Context) {
let op0 = pop_i32(ctx);
let op1 = pop_i32(ctx);
dynasm!(ctx.asm
; sub Rd(op1), Rd(op0)
);
push_i32(ctx, op1);
ctx.regs.release_scratch_gpr(op0);
}
pub fn i32_and(ctx: &mut Context) {
let op0 = pop_i32(ctx);
let op1 = pop_i32(ctx);
dynasm!(ctx.asm
; and Rd(op1), Rd(op0)
);
push_i32(ctx, op1);
ctx.regs.release_scratch_gpr(op0);
}
pub fn i32_or(ctx: &mut Context) {
let op0 = pop_i32(ctx);
let op1 = pop_i32(ctx);
dynasm!(ctx.asm
; or Rd(op1), Rd(op0)
);
push_i32(ctx, op1);
ctx.regs.release_scratch_gpr(op0);
}
pub fn i32_xor(ctx: &mut Context) {
let op0 = pop_i32(ctx);
let op1 = pop_i32(ctx);
dynasm!(ctx.asm
; xor Rd(op1), Rd(op0)
);
push_i32(ctx, op1);
ctx.regs.release_scratch_gpr(op0);
}
pub fn i32_mul(ctx: &mut Context) {
let op0 = pop_i32(ctx);
let op1 = pop_i32(ctx);
dynasm!(ctx.asm
; imul Rd(op1), Rd(op0)
);
push_i32(ctx, op1);
ctx.regs.release_scratch_gpr(op0);
} }
fn sp_relative_offset(ctx: &mut Context, slot_idx: u32) -> i32 { fn sp_relative_offset(ctx: &mut Context, slot_idx: u32) -> i32 {
@@ -411,6 +466,7 @@ pub fn call_direct(ctx: &mut Context, index: u32, arg_arity: u32, return_arity:
} }
pub fn prologue(ctx: &mut Context, stack_slots: u32) { pub fn prologue(ctx: &mut Context, stack_slots: u32) {
let stack_slots = stack_slots;
// 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.
let aligned_stack_slots = (stack_slots + 1) & !1; let aligned_stack_slots = (stack_slots + 1) & !1;

View File

@@ -208,18 +208,15 @@ pub fn translate(
} }
} }
} }
Operator::I32Eq => { Operator::I32Eq => relop_eq_i32(&mut ctx),
relop_eq_i32(&mut ctx); Operator::I32Add => i32_add(&mut ctx),
} Operator::I32Sub => i32_sub(&mut ctx),
Operator::I32Add => { Operator::I32And => i32_and(&mut ctx),
add_i32(&mut ctx); Operator::I32Or => i32_or(&mut ctx),
} Operator::I32Xor => i32_xor(&mut ctx),
Operator::GetLocal { local_index } => { Operator::I32Mul => i32_mul(&mut ctx),
get_local_i32(&mut ctx, local_index); Operator::GetLocal { local_index } => get_local_i32(&mut ctx, local_index),
} Operator::I32Const { value } => literal_i32(&mut ctx, value),
Operator::I32Const { value } => {
literal_i32(&mut ctx, value);
}
Operator::Call { function_index } => { Operator::Call { function_index } => {
let callee_ty = translation_ctx.func_type(function_index); let callee_ty = translation_ctx.func_type(function_index);

View File

@@ -7,7 +7,10 @@ extern crate wasmparser;
#[macro_use] #[macro_use]
extern crate failure_derive; extern crate failure_derive;
extern crate dynasmrt; extern crate dynasmrt;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate quickcheck;
extern crate wabt; extern crate wabt;
mod backend; mod backend;

View File

@@ -52,6 +52,13 @@ impl TranslatedModule {
args.call(start_buf) args.call(start_buf)
} }
pub fn disassemble(&self) {
self.translated_code_section
.as_ref()
.expect("no code section")
.disassemble();
}
} }
#[derive(Default)] #[derive(Default)]

View File

@@ -18,19 +18,32 @@ fn empty() {
let _ = translate_wat("(module (func))"); let _ = translate_wat("(module (func))");
} }
#[test] macro_rules! binop_test {
fn adds() { ($op:ident, $func:path) => {
const CASES: &[(u32, u32, u32)] = &[(5, 3, 8), (0, 228, 228), (u32::max_value(), 1, 0)]; quickcheck! {
fn $op(a: u32, b: u32) -> bool {
static CODE: &str = concat!(
"(module (func (param i32) (param i32) (result i32) (i32.",
stringify!($op),
" (get_local 0) (get_local 1))))"
);
let code = r#" lazy_static! {
(module static ref TRANSLATED: TranslatedModule = translate_wat(CODE);
(func (param i32) (param i32) (result i32) (i32.add (get_local 0) (get_local 1))) }
)
"#; unsafe { TRANSLATED.execute_func::<(u32, u32), u32>(0, (a, b)) == $func(a, b) }
for (a, b, expected) in CASES {
assert_eq!(execute_wat(code, *a, *b), *expected);
} }
} }
};
}
binop_test!(add, u32::wrapping_add);
binop_test!(sub, u32::wrapping_sub);
binop_test!(and, std::ops::BitAnd::bitand);
binop_test!(or, std::ops::BitOr::bitor);
binop_test!(xor, std::ops::BitXor::bitxor);
binop_test!(mul, u32::wrapping_mul);
#[test] #[test]
fn relop_eq() { fn relop_eq() {