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_derive = "0.1.3"
wabt = "0.7"
lazy_static = "1.2"
quickcheck = "0.7"
[badges]
maintenance = { status = "experimental" }

View File

@@ -160,6 +160,7 @@ impl CodeGenSession {
}
}
#[derive(Debug)]
pub struct TranslatedCodeSection {
exec_buf: ExecutableBuffer,
func_starts: Vec<AssemblyOffset>,
@@ -170,6 +171,10 @@ impl TranslatedCodeSection {
let offset = self.func_starts[idx];
self.exec_buf.ptr(offset)
}
pub fn disassemble(&self) {
::disassemble::disassemble(&*self.exec_buf).unwrap();
}
}
pub struct Context<'a> {
@@ -238,14 +243,64 @@ fn pop_i32(ctx: &mut Context) -> GPR {
gpr
}
pub fn add_i32(ctx: &mut Context) {
pub fn i32_add(ctx: &mut Context) {
let op0 = pop_i32(ctx);
let op1 = pop_i32(ctx);
dynasm!(ctx.asm
; add Rd(op0), Rd(op1)
; add Rd(op1), Rd(op0)
);
push_i32(ctx, op0);
ctx.regs.release_scratch_gpr(op1);
push_i32(ctx, 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 {
@@ -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) {
let stack_slots = stack_slots;
// Align stack slots to the nearest even number. This is required
// by x86-64 ABI.
let aligned_stack_slots = (stack_slots + 1) & !1;

View File

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

View File

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

View File

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

View File

@@ -18,20 +18,33 @@ fn empty() {
let _ = translate_wat("(module (func))");
}
#[test]
fn adds() {
const CASES: &[(u32, u32, u32)] = &[(5, 3, 8), (0, 228, 228), (u32::max_value(), 1, 0)];
macro_rules! binop_test {
($op:ident, $func:path) => {
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#"
(module
(func (param i32) (param i32) (result i32) (i32.add (get_local 0) (get_local 1)))
)
"#;
for (a, b, expected) in CASES {
assert_eq!(execute_wat(code, *a, *b), *expected);
}
lazy_static! {
static ref TRANSLATED: TranslatedModule = translate_wat(CODE);
}
unsafe { TRANSLATED.execute_func::<(u32, u32), u32>(0, (a, b)) == $func(a, b) }
}
}
};
}
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]
fn relop_eq() {
const CASES: &[(u32, u32, u32)] = &[