Add float operations
This commit is contained in:
455
src/backend.rs
455
src/backend.rs
@@ -1,6 +1,6 @@
|
|||||||
#![allow(dead_code)] // for now
|
#![allow(dead_code)] // for now
|
||||||
|
|
||||||
use microwasm::{SignlessType, F32, F64, I32, I64};
|
use microwasm::{SignlessType, Type, F32, F64, I32, I64};
|
||||||
|
|
||||||
use self::registers::*;
|
use self::registers::*;
|
||||||
use dynasmrt::x64::Assembler;
|
use dynasmrt::x64::Assembler;
|
||||||
@@ -25,7 +25,35 @@ pub enum GPR {
|
|||||||
Rx(RegId),
|
Rx(RegId),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
|
pub enum GPRType {
|
||||||
|
Rq,
|
||||||
|
Rx,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SignlessType> for GPRType {
|
||||||
|
fn from(other: SignlessType) -> GPRType {
|
||||||
|
match other {
|
||||||
|
I32 | I64 => GPRType::Rq,
|
||||||
|
F32 | F64 => GPRType::Rx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SignlessType> for Option<GPRType> {
|
||||||
|
fn from(other: SignlessType) -> Self {
|
||||||
|
Some(other.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl GPR {
|
impl GPR {
|
||||||
|
fn type_(&self) -> GPRType {
|
||||||
|
match self {
|
||||||
|
GPR::Rq(_) => GPRType::Rq,
|
||||||
|
GPR::Rx(_) => GPRType::Rx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn rq(self) -> Option<RegId> {
|
fn rq(self) -> Option<RegId> {
|
||||||
match self {
|
match self {
|
||||||
GPR::Rq(r) => Some(r),
|
GPR::Rq(r) => Some(r),
|
||||||
@@ -308,20 +336,15 @@ impl Registers {
|
|||||||
scratch_counts.1[gpr as usize]
|
scratch_counts.1[gpr as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add function that takes a scratch register if possible
|
pub fn take(&mut self, ty: impl Into<GPRType>) -> GPR {
|
||||||
// but otherwise gives a fresh stack location.
|
let (mk_gpr, scratch_counts) = match ty.into() {
|
||||||
pub fn take_64(&mut self) -> GPR {
|
GPRType::Rq => (GPR::Rq as fn(_) -> _, &mut self.scratch_64),
|
||||||
let out = self.scratch_64.0.take();
|
GPRType::Rx => (GPR::Rx as fn(_) -> _, &mut self.scratch_128),
|
||||||
self.scratch_64.1[out as usize] += 1;
|
};
|
||||||
GPR::Rq(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Add function that takes a scratch register if possible
|
let out = scratch_counts.0.take();
|
||||||
// but otherwise gives a fresh stack location.
|
scratch_counts.1[out as usize] += 1;
|
||||||
pub fn take_128(&mut self) -> GPR {
|
mk_gpr(out)
|
||||||
let out = self.scratch_128.0.take();
|
|
||||||
self.scratch_128.1[out as usize] += 1;
|
|
||||||
GPR::Rx(out)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn release(&mut self, gpr: GPR) {
|
pub fn release(&mut self, gpr: GPR) {
|
||||||
@@ -430,8 +453,8 @@ const FLOAT_ARGS_IN_GPRS: &[GPR] = &[XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, X
|
|||||||
const FLOAT_RETURN_GPRS: &[GPR] = &[XMM0, XMM1];
|
const FLOAT_RETURN_GPRS: &[GPR] = &[XMM0, XMM1];
|
||||||
// List of scratch registers taken from https://wiki.osdev.org/System_V_ABI
|
// List of scratch registers taken from https://wiki.osdev.org/System_V_ABI
|
||||||
const SCRATCH_REGS: &[GPR] = &[
|
const SCRATCH_REGS: &[GPR] = &[
|
||||||
RSI, RDX, RCX, R8, R9, RAX, R10, R11, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, XMM8, XMM9,
|
RSI, RDX, RCX, R8, R9, RAX, R10, R11, XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, XMM8,
|
||||||
XMM10, XMM11, XMM12, XMM13, XMM14, XMM15,
|
XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15,
|
||||||
];
|
];
|
||||||
const VMCTX: RegId = rq::RDI;
|
const VMCTX: RegId = rq::RDI;
|
||||||
|
|
||||||
@@ -643,14 +666,14 @@ macro_rules! unop {
|
|||||||
),
|
),
|
||||||
ValueLocation::Stack(offset) => {
|
ValueLocation::Stack(offset) => {
|
||||||
let offset = self.adjusted_offset(offset);
|
let offset = self.adjusted_offset(offset);
|
||||||
let temp = self.block_state.regs.take_64();
|
let temp = self.block_state.regs.take(Type::for_::<$typ>());
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; $instr $reg_ty(temp.rq().unwrap()), [rsp + offset]
|
; $instr $reg_ty(temp.rq().unwrap()), [rsp + offset]
|
||||||
);
|
);
|
||||||
ValueLocation::Reg(temp)
|
ValueLocation::Reg(temp)
|
||||||
}
|
}
|
||||||
ValueLocation::Reg(reg) => {
|
ValueLocation::Reg(reg) => {
|
||||||
let temp = self.block_state.regs.take_64();
|
let temp = self.block_state.regs.take(Type::for_::<$typ>());
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; $instr $reg_ty(temp.rq().unwrap()), $reg_ty(reg.rq().unwrap())
|
; $instr $reg_ty(temp.rq().unwrap()), $reg_ty(reg.rq().unwrap())
|
||||||
);
|
);
|
||||||
@@ -665,7 +688,7 @@ macro_rules! unop {
|
|||||||
|
|
||||||
// TODO: Support immediate `count` parameters
|
// TODO: Support immediate `count` parameters
|
||||||
macro_rules! shift {
|
macro_rules! shift {
|
||||||
($name:ident, $reg_ty:ident, $instr:ident, $const_fallback:expr) => {
|
($name:ident, $reg_ty:ident, $instr:ident, $const_fallback:expr, $ty:expr) => {
|
||||||
pub fn $name(&mut self) {
|
pub fn $name(&mut self) {
|
||||||
enum RestoreRcx {
|
enum RestoreRcx {
|
||||||
MoveValBack(GPR),
|
MoveValBack(GPR),
|
||||||
@@ -676,7 +699,7 @@ macro_rules! shift {
|
|||||||
let mut val = self.pop();
|
let mut val = self.pop();
|
||||||
|
|
||||||
if val == ValueLocation::Reg(RCX) {
|
if val == ValueLocation::Reg(RCX) {
|
||||||
val = ValueLocation::Reg(self.into_temp_reg(val));
|
val = ValueLocation::Reg(self.into_temp_reg($ty, val));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Maybe allocate `RCX`, write `count` to it and then free `count`.
|
// TODO: Maybe allocate `RCX`, write `count` to it and then free `count`.
|
||||||
@@ -688,7 +711,7 @@ macro_rules! shift {
|
|||||||
let out = if self.block_state.regs.is_free(RCX) {
|
let out = if self.block_state.regs.is_free(RCX) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let new_reg = self.block_state.regs.take_64();
|
let new_reg = self.block_state.regs.take(I32);
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; mov Rq(new_reg.rq().unwrap()), rcx
|
; mov Rq(new_reg.rq().unwrap()), rcx
|
||||||
);
|
);
|
||||||
@@ -722,7 +745,7 @@ macro_rules! shift {
|
|||||||
self.block_state.regs.mark_used(RCX);
|
self.block_state.regs.mark_used(RCX);
|
||||||
count = ValueLocation::Reg(RCX);
|
count = ValueLocation::Reg(RCX);
|
||||||
|
|
||||||
let reg = self.into_reg(val);
|
let reg = self.into_reg($ty, val);
|
||||||
|
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; $instr $reg_ty(reg.rq().unwrap()), cl
|
; $instr $reg_ty(reg.rq().unwrap()), cl
|
||||||
@@ -751,7 +774,7 @@ macro_rules! cmp_i32 {
|
|||||||
let out = if let Some(i) = left.imm_i32() {
|
let out = if let Some(i) = left.imm_i32() {
|
||||||
match right {
|
match right {
|
||||||
ValueLocation::Stack(offset) => {
|
ValueLocation::Stack(offset) => {
|
||||||
let result = self.block_state.regs.take_64();
|
let result = self.block_state.regs.take(I32);
|
||||||
let offset = self.adjusted_offset(offset);
|
let offset = self.adjusted_offset(offset);
|
||||||
|
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
@@ -762,7 +785,7 @@ macro_rules! cmp_i32 {
|
|||||||
ValueLocation::Reg(result)
|
ValueLocation::Reg(result)
|
||||||
}
|
}
|
||||||
ValueLocation::Reg(rreg) => {
|
ValueLocation::Reg(rreg) => {
|
||||||
let result = self.block_state.regs.take_64();
|
let result = self.block_state.regs.take(I32);
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; xor Rd(result.rq().unwrap()), Rd(result.rq().unwrap())
|
; xor Rd(result.rq().unwrap()), Rd(result.rq().unwrap())
|
||||||
; cmp Rd(rreg.rq().unwrap()), i
|
; cmp Rd(rreg.rq().unwrap()), i
|
||||||
@@ -781,11 +804,11 @@ macro_rules! cmp_i32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let lreg = self.into_reg(left);
|
let lreg = self.into_reg(I32, left);
|
||||||
// TODO: Make `into_reg` take an `&mut`?
|
// TODO: Make `into_reg` take an `&mut`?
|
||||||
left = ValueLocation::Reg(lreg);
|
left = ValueLocation::Reg(lreg);
|
||||||
|
|
||||||
let result = self.block_state.regs.take_64();
|
let result = self.block_state.regs.take(I32);
|
||||||
|
|
||||||
match right {
|
match right {
|
||||||
ValueLocation::Stack(offset) => {
|
ValueLocation::Stack(offset) => {
|
||||||
@@ -832,7 +855,7 @@ macro_rules! cmp_i64 {
|
|||||||
let out = if let Some(i) = left.imm_i64() {
|
let out = if let Some(i) = left.imm_i64() {
|
||||||
match right {
|
match right {
|
||||||
ValueLocation::Stack(offset) => {
|
ValueLocation::Stack(offset) => {
|
||||||
let result = self.block_state.regs.take_64();
|
let result = self.block_state.regs.take(I64);
|
||||||
let offset = self.adjusted_offset(offset);
|
let offset = self.adjusted_offset(offset);
|
||||||
if let Some(i) = i.try_into() {
|
if let Some(i) = i.try_into() {
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
@@ -846,7 +869,7 @@ macro_rules! cmp_i64 {
|
|||||||
ValueLocation::Reg(result)
|
ValueLocation::Reg(result)
|
||||||
}
|
}
|
||||||
ValueLocation::Reg(rreg) => {
|
ValueLocation::Reg(rreg) => {
|
||||||
let result = self.block_state.regs.take_64();
|
let result = self.block_state.regs.take(I64);
|
||||||
if let Some(i) = i.try_into() {
|
if let Some(i) = i.try_into() {
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; xor Rd(result.rq().unwrap()), Rd(result.rq().unwrap())
|
; xor Rd(result.rq().unwrap()), Rd(result.rq().unwrap())
|
||||||
@@ -869,10 +892,10 @@ macro_rules! cmp_i64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let lreg = self.into_reg(left);
|
let lreg = self.into_reg(I64, left);
|
||||||
left = ValueLocation::Reg(lreg);
|
left = ValueLocation::Reg(lreg);
|
||||||
|
|
||||||
let result = self.block_state.regs.take_64();
|
let result = self.block_state.regs.take(I64);
|
||||||
|
|
||||||
match right {
|
match right {
|
||||||
ValueLocation::Stack(offset) => {
|
ValueLocation::Stack(offset) => {
|
||||||
@@ -916,97 +939,111 @@ macro_rules! cmp_i64 {
|
|||||||
|
|
||||||
macro_rules! commutative_binop_i32 {
|
macro_rules! commutative_binop_i32 {
|
||||||
($name:ident, $instr:ident, $const_fallback:expr) => {
|
($name:ident, $instr:ident, $const_fallback:expr) => {
|
||||||
pub fn $name(&mut self) {
|
commutative_binop!(
|
||||||
let op0 = self.pop();
|
$name,
|
||||||
let op1 = self.pop();
|
$instr,
|
||||||
|
$const_fallback,
|
||||||
if let Some(i1) = op1.imm_i32() {
|
Rd,
|
||||||
if let Some(i0) = op0.imm_i32() {
|
rq,
|
||||||
self.push(ValueLocation::Immediate($const_fallback(i1, i0).into()));
|
I32,
|
||||||
return;
|
imm_i32,
|
||||||
}
|
|this: &mut Context<_>, op1: GPR, i| dynasm!(this.asm
|
||||||
}
|
; $instr Rd(op1.rq().unwrap()), i
|
||||||
|
)
|
||||||
let (op1, op0) = match op1 {
|
);
|
||||||
ValueLocation::Reg(_) => (self.into_temp_reg(op1), op0),
|
|
||||||
_ => if op0.immediate().is_some() {
|
|
||||||
(self.into_temp_reg(op1), op0)
|
|
||||||
} else {
|
|
||||||
(self.into_temp_reg(op0), op1)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match op0 {
|
|
||||||
ValueLocation::Reg(reg) => {
|
|
||||||
dynasm!(self.asm
|
|
||||||
; $instr Rd(op1.rq().unwrap()), Rd(reg.rq().unwrap())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ValueLocation::Stack(offset) => {
|
|
||||||
let offset = self.adjusted_offset(offset);
|
|
||||||
dynasm!(self.asm
|
|
||||||
; $instr Rd(op1.rq().unwrap()), [rsp + offset]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ValueLocation::Immediate(i) => {
|
|
||||||
dynasm!(self.asm
|
|
||||||
; $instr Rd(op1.rq().unwrap()), i.as_i32().unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.free_value(op0);
|
|
||||||
self.push(ValueLocation::Reg(op1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! commutative_binop_i64 {
|
macro_rules! commutative_binop_i64 {
|
||||||
($name:ident, $instr:ident, $const_fallback:expr) => {
|
($name:ident, $instr:ident, $const_fallback:expr) => {
|
||||||
|
commutative_binop!(
|
||||||
|
$name,
|
||||||
|
$instr,
|
||||||
|
$const_fallback,
|
||||||
|
Rq,
|
||||||
|
rq,
|
||||||
|
I64,
|
||||||
|
imm_i64,
|
||||||
|
|this: &mut Context<_>, op1: GPR, i| dynasm!(this.asm
|
||||||
|
; $instr Rq(op1.rq().unwrap()), i
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! commutative_binop_f32 {
|
||||||
|
($name:ident, $instr:ident, $const_fallback:expr) => {
|
||||||
|
commutative_binop!(
|
||||||
|
$name,
|
||||||
|
$instr,
|
||||||
|
$const_fallback,
|
||||||
|
Rx,
|
||||||
|
rx,
|
||||||
|
F32,
|
||||||
|
imm_f32,
|
||||||
|
|_, _, _| unreachable!()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! commutative_binop_f64 {
|
||||||
|
($name:ident, $instr:ident, $const_fallback:expr) => {
|
||||||
|
commutative_binop!(
|
||||||
|
$name,
|
||||||
|
$instr,
|
||||||
|
$const_fallback,
|
||||||
|
Rx,
|
||||||
|
rx,
|
||||||
|
F64,
|
||||||
|
imm_f64,
|
||||||
|
|_, _, _| unreachable!()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! commutative_binop {
|
||||||
|
($name:ident, $instr:ident, $const_fallback:expr, $reg_ty:ident, $reg_fn:ident, $ty:expr, $imm_fn:ident, $direct_imm:expr) => {
|
||||||
pub fn $name(&mut self) {
|
pub fn $name(&mut self) {
|
||||||
let op0 = self.pop();
|
let op0 = self.pop();
|
||||||
let op1 = self.pop();
|
let op1 = self.pop();
|
||||||
|
|
||||||
if let Some(i1) = op1.imm_i64() {
|
if let Some(i1) = op1.$imm_fn() {
|
||||||
if let Some(i0) = op0.imm_i64() {
|
if let Some(i0) = op0.$imm_fn() {
|
||||||
self.block_state.stack.push(ValueLocation::Immediate($const_fallback(i1, i0).into()));
|
self.block_state.stack.push(ValueLocation::Immediate($const_fallback(i1, i0).into()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (op1, op0) = match op1 {
|
let (op1, op0) = match op1 {
|
||||||
ValueLocation::Reg(reg) => (reg, op0),
|
ValueLocation::Reg(_) => (self.into_temp_reg($ty, op1), op0),
|
||||||
_ => if op0.immediate().is_some() {
|
_ => if op0.immediate().is_some() {
|
||||||
(self.into_temp_reg(op1), op0)
|
(self.into_temp_reg($ty, op1), op0)
|
||||||
} else {
|
} else {
|
||||||
(self.into_temp_reg(op0), op1)
|
(self.into_temp_reg($ty, op0), op1)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match op0 {
|
match op0 {
|
||||||
ValueLocation::Reg(reg) => {
|
ValueLocation::Reg(reg) => {
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; $instr Rq(op1.rq().unwrap()), Rq(reg.rq().unwrap())
|
; $instr $reg_ty(op1.$reg_fn().unwrap()), $reg_ty(reg.$reg_fn().unwrap())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ValueLocation::Stack(offset) => {
|
ValueLocation::Stack(offset) => {
|
||||||
let offset = self.adjusted_offset(offset);
|
let offset = self.adjusted_offset(offset);
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; $instr Rq(op1.rq().unwrap()), [rsp + offset]
|
; $instr $reg_ty(op1.$reg_fn().unwrap()), [rsp + offset]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ValueLocation::Immediate(i) => {
|
ValueLocation::Immediate(i) => {
|
||||||
let i = i.as_i64().unwrap();
|
if let Some(i) = i.as_int().and_then(|i| i.try_into()) {
|
||||||
if let Some(i) = i.try_into() {
|
$direct_imm(self, op1, i);
|
||||||
dynasm!(self.asm
|
|
||||||
; $instr Rq(op1.rq().unwrap()), i
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
let scratch = self.block_state.regs.take_64();
|
let scratch = self.block_state.regs.take($ty);
|
||||||
|
self.immediate_to_reg(scratch, i);
|
||||||
|
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; mov Rq(scratch.rq().unwrap()), QWORD i
|
; $instr $reg_ty(op1.$reg_fn().unwrap()), $reg_ty(scratch.$reg_fn().unwrap())
|
||||||
; $instr Rq(op1.rq().unwrap()), Rq(scratch.rq().unwrap())
|
|
||||||
);
|
);
|
||||||
|
|
||||||
self.block_state.regs.release(scratch);
|
self.block_state.regs.release(scratch);
|
||||||
@@ -1021,7 +1058,7 @@ macro_rules! commutative_binop_i64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! load {
|
macro_rules! load {
|
||||||
($name:ident, $reg_ty:ident, $instruction_name:expr) => {
|
($name:ident, $reg_ty:ident, $instruction_name:expr, $out_ty:expr) => {
|
||||||
pub fn $name(&mut self, offset: u32) -> Result<(), Error> {
|
pub fn $name(&mut self, offset: u32) -> Result<(), Error> {
|
||||||
fn load_to_reg<_M: ModuleContext>(
|
fn load_to_reg<_M: ModuleContext>(
|
||||||
ctx: &mut Context<_M>,
|
ctx: &mut Context<_M>,
|
||||||
@@ -1029,7 +1066,7 @@ macro_rules! load {
|
|||||||
(offset, runtime_offset): (i32, Result<i32, GPR>)
|
(offset, runtime_offset): (i32, Result<i32, GPR>)
|
||||||
) {
|
) {
|
||||||
let vmctx_mem_ptr_offset = ctx.module_context.offset_of_memory_ptr() as i32;
|
let vmctx_mem_ptr_offset = ctx.module_context.offset_of_memory_ptr() as i32;
|
||||||
let mem_ptr_reg = ctx.block_state.regs.take_64();
|
let mem_ptr_reg = ctx.block_state.regs.take(I64);
|
||||||
dynasm!(ctx.asm
|
dynasm!(ctx.asm
|
||||||
; mov Rq(mem_ptr_reg.rq().unwrap()), [Rq(VMCTX) + vmctx_mem_ptr_offset]
|
; mov Rq(mem_ptr_reg.rq().unwrap()), [Rq(VMCTX) + vmctx_mem_ptr_offset]
|
||||||
);
|
);
|
||||||
@@ -1052,14 +1089,14 @@ macro_rules! load {
|
|||||||
|
|
||||||
let base = self.pop();
|
let base = self.pop();
|
||||||
|
|
||||||
let temp = self.block_state.regs.take_64();
|
let temp = self.block_state.regs.take($out_ty);
|
||||||
|
|
||||||
match base {
|
match base {
|
||||||
ValueLocation::Immediate(i) => {
|
ValueLocation::Immediate(i) => {
|
||||||
load_to_reg(self, temp, (offset as _, Ok(i.as_i32().unwrap())));
|
load_to_reg(self, temp, (offset as _, Ok(i.as_i32().unwrap())));
|
||||||
}
|
}
|
||||||
base => {
|
base => {
|
||||||
let gpr = self.into_reg(base);
|
let gpr = self.into_reg(I32, base);
|
||||||
load_to_reg(self, temp, (offset as _, Err(gpr)));
|
load_to_reg(self, temp, (offset as _, Err(gpr)));
|
||||||
self.block_state.regs.release(gpr);
|
self.block_state.regs.release(gpr);
|
||||||
}
|
}
|
||||||
@@ -1073,7 +1110,7 @@ macro_rules! load {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! store {
|
macro_rules! store {
|
||||||
($name:ident, $reg_ty:ident, $size:ident, $instruction_name:expr) => {
|
($name:ident, $reg_ty:ident, $size:ident, $instruction_name:expr, $in_ty:expr) => {
|
||||||
pub fn $name(&mut self, offset: u32) -> Result<(), Error> {
|
pub fn $name(&mut self, offset: u32) -> Result<(), Error> {
|
||||||
fn store_from_reg<_M: ModuleContext>(
|
fn store_from_reg<_M: ModuleContext>(
|
||||||
ctx: &mut Context<_M>,
|
ctx: &mut Context<_M>,
|
||||||
@@ -1081,7 +1118,7 @@ macro_rules! store {
|
|||||||
(offset, runtime_offset): (i32, Result<i32, GPR>)
|
(offset, runtime_offset): (i32, Result<i32, GPR>)
|
||||||
) {
|
) {
|
||||||
let vmctx_mem_ptr_offset = ctx.module_context.offset_of_memory_ptr() as i32;
|
let vmctx_mem_ptr_offset = ctx.module_context.offset_of_memory_ptr() as i32;
|
||||||
let mem_ptr_reg = ctx.block_state.regs.take_64();
|
let mem_ptr_reg = ctx.block_state.regs.take(I64);
|
||||||
dynasm!(ctx.asm
|
dynasm!(ctx.asm
|
||||||
; mov Rq(mem_ptr_reg.rq().unwrap()), [Rq(VMCTX) + vmctx_mem_ptr_offset]
|
; mov Rq(mem_ptr_reg.rq().unwrap()), [Rq(VMCTX) + vmctx_mem_ptr_offset]
|
||||||
);
|
);
|
||||||
@@ -1105,14 +1142,16 @@ macro_rules! store {
|
|||||||
let src = self.pop();
|
let src = self.pop();
|
||||||
let base = self.pop();
|
let base = self.pop();
|
||||||
|
|
||||||
let src_reg = self.into_reg(src);
|
let src_reg = self.into_reg($in_ty, src);
|
||||||
|
// TODO
|
||||||
|
debug_assert!(stringify!($reg_ty) == "Rq" || stringify!($reg_ty) == "Rd");
|
||||||
|
|
||||||
match base {
|
match base {
|
||||||
ValueLocation::Immediate(i) => {
|
ValueLocation::Immediate(i) => {
|
||||||
store_from_reg(self, src_reg, (offset as i32, Ok(i.as_i32().unwrap())));
|
store_from_reg(self, src_reg, (offset as i32, Ok(i.as_i32().unwrap())));
|
||||||
}
|
}
|
||||||
base => {
|
base => {
|
||||||
let gpr = self.into_reg(base);
|
let gpr = self.into_reg(I32, base);
|
||||||
store_from_reg(self, src_reg, (offset as i32, Err(gpr)));
|
store_from_reg(self, src_reg, (offset as i32, Err(gpr)));
|
||||||
self.block_state.regs.release(gpr);
|
self.block_state.regs.release(gpr);
|
||||||
}
|
}
|
||||||
@@ -1227,8 +1266,8 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let reg = self.into_reg(val);
|
let reg = self.into_reg(I32, val);
|
||||||
let out = self.block_state.regs.take_64();
|
let out = self.block_state.regs.take(I32);
|
||||||
|
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; xor Rd(out.rq().unwrap()), Rd(out.rq().unwrap())
|
; xor Rd(out.rq().unwrap()), Rd(out.rq().unwrap())
|
||||||
@@ -1251,8 +1290,8 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let reg = self.into_reg(val);
|
let reg = self.into_reg(I64, val);
|
||||||
let out = self.block_state.regs.take_64();
|
let out = self.block_state.regs.take(I64);
|
||||||
|
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; xor Rd(out.rq().unwrap()), Rd(out.rq().unwrap())
|
; xor Rd(out.rq().unwrap()), Rd(out.rq().unwrap())
|
||||||
@@ -1272,7 +1311,7 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
|
|
||||||
f(self);
|
f(self);
|
||||||
|
|
||||||
let predicate = self.into_reg(val);
|
let predicate = self.into_reg(I32, val);
|
||||||
|
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; test Rd(predicate.rq().unwrap()), Rd(predicate.rq().unwrap())
|
; test Rd(predicate.rq().unwrap()), Rd(predicate.rq().unwrap())
|
||||||
@@ -1289,7 +1328,7 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
|
|
||||||
f(self);
|
f(self);
|
||||||
|
|
||||||
let predicate = self.into_reg(val);
|
let predicate = self.into_reg(I32, val);
|
||||||
|
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; test Rd(predicate.rq().unwrap()), Rd(predicate.rq().unwrap())
|
; test Rd(predicate.rq().unwrap()), Rd(predicate.rq().unwrap())
|
||||||
@@ -1316,7 +1355,7 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if self.block_state.depth.0 > depth.0 {
|
} else if self.block_state.depth.0 > depth.0 {
|
||||||
let trash = self.block_state.regs.take_64();
|
let trash = self.block_state.regs.take(I64);
|
||||||
// TODO: We need to preserve ZF on `br_if` so we use `push`/`pop` but that isn't
|
// TODO: We need to preserve ZF on `br_if` so we use `push`/`pop` but that isn't
|
||||||
// necessary on (for example) `br`.
|
// necessary on (for example) `br`.
|
||||||
for _ in 0..self.block_state.depth.0 - depth.0 {
|
for _ in 0..self.block_state.depth.0 - depth.0 {
|
||||||
@@ -1373,7 +1412,7 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
let val = self.pop();
|
let val = self.pop();
|
||||||
// TODO: We can use stack slots for values already on the stack but we
|
// TODO: We can use stack slots for values already on the stack but we
|
||||||
// don't refcount stack slots right now
|
// don't refcount stack slots right now
|
||||||
let loc = CCLoc::Reg(self.into_temp_reg(val));
|
let loc = CCLoc::Reg(self.into_temp_reg(None, val));
|
||||||
|
|
||||||
out.push(loc);
|
out.push(loc);
|
||||||
}
|
}
|
||||||
@@ -1387,7 +1426,6 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn immediate_to_reg(&mut self, reg: GPR, val: Value) {
|
fn immediate_to_reg(&mut self, reg: GPR, val: Value) {
|
||||||
// TODO: Floats
|
|
||||||
match reg {
|
match reg {
|
||||||
GPR::Rq(r) => {
|
GPR::Rq(r) => {
|
||||||
let val = val.as_bytes();
|
let val = val.as_bytes();
|
||||||
@@ -1402,7 +1440,7 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
GPR::Rx(r) => {
|
GPR::Rx(r) => {
|
||||||
let temp = self.block_state.regs.take_64();
|
let temp = self.block_state.regs.take(I64);
|
||||||
self.immediate_to_reg(temp, val);
|
self.immediate_to_reg(temp, val);
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; movq Rx(r), Rq(temp.rq().unwrap())
|
; movq Rx(r), Rq(temp.rq().unwrap())
|
||||||
@@ -1421,7 +1459,7 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
let in_offset = self.adjusted_offset(in_offset);
|
let in_offset = self.adjusted_offset(in_offset);
|
||||||
let out_offset = self.adjusted_offset(out_offset);
|
let out_offset = self.adjusted_offset(out_offset);
|
||||||
if in_offset != out_offset {
|
if in_offset != out_offset {
|
||||||
let gpr = self.block_state.regs.take_64();
|
let gpr = self.block_state.regs.take(I64);
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; mov Rq(gpr.rq().unwrap()), [rsp + in_offset]
|
; mov Rq(gpr.rq().unwrap()), [rsp + in_offset]
|
||||||
; mov [rsp + out_offset], Rq(gpr.rq().unwrap())
|
; mov [rsp + out_offset], Rq(gpr.rq().unwrap())
|
||||||
@@ -1458,7 +1496,7 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
; mov DWORD [rsp + out_offset], i as i32
|
; mov DWORD [rsp + out_offset], i as i32
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let scratch = self.block_state.regs.take_64();
|
let scratch = self.block_state.regs.take(I64);
|
||||||
|
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; mov Rq(scratch.rq().unwrap()), QWORD i
|
; mov Rq(scratch.rq().unwrap()), QWORD i
|
||||||
@@ -1497,12 +1535,12 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
}
|
}
|
||||||
(GPR::Rx(in_reg), GPR::Rq(out_reg)) => {
|
(GPR::Rx(in_reg), GPR::Rq(out_reg)) => {
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; movq Rx(out_reg), Rq(in_reg)
|
; movq Rq(out_reg), Rx(in_reg)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(GPR::Rq(in_reg), GPR::Rx(out_reg)) => {
|
(GPR::Rq(in_reg), GPR::Rx(out_reg)) => {
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; movq Rq(out_reg), Rx(in_reg)
|
; movq Rx(out_reg), Rq(in_reg)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(GPR::Rx(in_reg), GPR::Rx(out_reg)) => {
|
(GPR::Rx(in_reg), GPR::Rx(out_reg)) => {
|
||||||
@@ -1558,10 +1596,10 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
self.block_state.depth = cc.stack_depth;
|
self.block_state.depth = cc.stack_depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
load!(i32_load, Rd, "i32.load");
|
load!(i32_load, Rd, "i32.load", I32);
|
||||||
load!(i64_load, Rq, "i64.load");
|
load!(i64_load, Rq, "i64.load", I64);
|
||||||
store!(i32_store, Rd, DWORD, "i32.store");
|
store!(i32_store, Rd, DWORD, "i32.store", I32);
|
||||||
store!(i64_store, Rq, QWORD, "i64.store");
|
store!(i64_store, Rq, QWORD, "i64.store", I64);
|
||||||
|
|
||||||
fn push_physical(&mut self, value: ValueLocation) -> ValueLocation {
|
fn push_physical(&mut self, value: ValueLocation) -> ValueLocation {
|
||||||
self.block_state.depth.reserve(1);
|
self.block_state.depth.reserve(1);
|
||||||
@@ -1580,9 +1618,9 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
ValueLocation::Immediate(imm) => {
|
ValueLocation::Immediate(imm) => {
|
||||||
let gpr = self.block_state.regs.take_64();
|
let gpr = self.block_state.regs.take(I64);
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; mov Rq(gpr.rq().unwrap()), QWORD imm.as_int().unwrap()
|
; mov Rq(gpr.rq().unwrap()), QWORD imm.as_bytes()
|
||||||
; push Rq(gpr.rq().unwrap())
|
; push Rq(gpr.rq().unwrap())
|
||||||
);
|
);
|
||||||
self.block_state.regs.release(gpr);
|
self.block_state.regs.release(gpr);
|
||||||
@@ -1633,24 +1671,15 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Puts this value into a register so that it can be efficiently read
|
/// Puts this value into a register so that it can be efficiently read
|
||||||
fn into_reg(&mut self, val: ValueLocation) -> GPR {
|
// TODO: We should allow choosing which reg type we want to allocate here (Rx/Rq)
|
||||||
|
fn into_reg(&mut self, ty: impl Into<Option<GPRType>>, val: ValueLocation) -> GPR {
|
||||||
match val {
|
match val {
|
||||||
ValueLocation::Reg(r) => r,
|
ValueLocation::Reg(r) if ty.into().map(|t| t == r.type_()).unwrap_or(true) => r,
|
||||||
ValueLocation::Immediate(i) => {
|
val => {
|
||||||
let scratch = self.block_state.regs.take_64();
|
let scratch = self.block_state.regs.take(ty.into().unwrap_or(GPRType::Rq));
|
||||||
self.immediate_to_reg(scratch, i);
|
|
||||||
scratch
|
|
||||||
}
|
|
||||||
ValueLocation::Stack(offset) => {
|
|
||||||
// TODO: We can use `pop` if the number of usages is 1
|
|
||||||
// Even better, with an SSE-like `Value` abstraction
|
|
||||||
// we can make it so we only load it once.
|
|
||||||
let offset = self.adjusted_offset(offset);
|
|
||||||
let scratch = self.block_state.regs.take_64();
|
|
||||||
|
|
||||||
dynasm!(self.asm
|
self.copy_value(&val, &mut ValueLocation::Reg(scratch));
|
||||||
; mov Rq(scratch.rq().unwrap()), [rsp + offset]
|
self.free_value(val);
|
||||||
);
|
|
||||||
|
|
||||||
scratch
|
scratch
|
||||||
}
|
}
|
||||||
@@ -1659,22 +1688,25 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
|
|
||||||
/// Puts this value into a temporary register so that operations
|
/// Puts this value into a temporary register so that operations
|
||||||
/// on that register don't write to a local.
|
/// on that register don't write to a local.
|
||||||
fn into_temp_reg(&mut self, val: ValueLocation) -> GPR {
|
fn into_temp_reg(&mut self, ty: impl Into<Option<GPRType>>, val: ValueLocation) -> GPR {
|
||||||
|
// If we have `None` as the type then it always matches (`.unwrap_or(true)`)
|
||||||
match val {
|
match val {
|
||||||
ValueLocation::Reg(r) => {
|
ValueLocation::Reg(r) => {
|
||||||
if self.block_state.regs.num_usages(r) <= 1 {
|
let ty = ty.into();
|
||||||
assert_eq!(self.block_state.regs.num_usages(r), 1);
|
let type_matches = ty.map(|t| t == r.type_()).unwrap_or(true);
|
||||||
|
|
||||||
|
if self.block_state.regs.num_usages(r) <= 1 && type_matches {
|
||||||
r
|
r
|
||||||
} else {
|
} else {
|
||||||
let new_reg = self.block_state.regs.take_64();
|
let scratch = self.block_state.regs.take(ty.unwrap_or(GPRType::Rq));
|
||||||
self.block_state.regs.release(r);
|
|
||||||
dynasm!(self.asm
|
self.copy_value(&val, &mut ValueLocation::Reg(scratch));
|
||||||
; mov Rq(new_reg.rq().unwrap()), Rq(r.rq().unwrap())
|
self.free_value(val);
|
||||||
);
|
|
||||||
new_reg
|
scratch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val => self.into_reg(val),
|
val => self.into_reg(ty, val),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1697,17 +1729,84 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
commutative_binop_i64!(i64_or, or, |a, b| a | b);
|
commutative_binop_i64!(i64_or, or, |a, b| a | b);
|
||||||
commutative_binop_i64!(i64_xor, xor, |a, b| a ^ b);
|
commutative_binop_i64!(i64_xor, xor, |a, b| a ^ b);
|
||||||
|
|
||||||
shift!(i32_shl, Rd, shl, |a, b| (a as i32).wrapping_shl(b as _));
|
commutative_binop_f32!(f32_add, addss, |a: wasmparser::Ieee32, b: wasmparser::Ieee32| wasmparser::Ieee32(
|
||||||
shift!(i32_shr_s, Rd, sar, |a, b| (a as i32).wrapping_shr(b as _));
|
(f32::from_bits(a.bits()) + f32::from_bits(b.bits())).to_bits()
|
||||||
shift!(i32_shr_u, Rd, shr, |a, b| (a as u32).wrapping_shr(b as _));
|
));
|
||||||
shift!(i32_rotl, Rd, rol, |a, b| (a as u32).rotate_left(b as _));
|
commutative_binop_f64!(f64_add, addsd, |a: wasmparser::Ieee64, b: wasmparser::Ieee64| wasmparser::Ieee64(
|
||||||
shift!(i32_rotr, Rd, ror, |a, b| (a as u32).rotate_right(b as _));
|
(f64::from_bits(a.bits()) + f64::from_bits(b.bits())).to_bits()
|
||||||
|
));
|
||||||
|
|
||||||
shift!(i64_shl, Rq, shl, |a, b| (a as i64).wrapping_shl(b as _));
|
shift!(
|
||||||
shift!(i64_shr_s, Rq, sar, |a, b| (a as i64).wrapping_shr(b as _));
|
i32_shl,
|
||||||
shift!(i64_shr_u, Rq, shr, |a, b| (a as u64).wrapping_shr(b as _));
|
Rd,
|
||||||
shift!(i64_rotl, Rq, rol, |a, b| (a as u64).rotate_left(b as _));
|
shl,
|
||||||
shift!(i64_rotr, Rq, ror, |a, b| (a as u64).rotate_right(b as _));
|
|a, b| (a as i32).wrapping_shl(b as _),
|
||||||
|
I32
|
||||||
|
);
|
||||||
|
shift!(
|
||||||
|
i32_shr_s,
|
||||||
|
Rd,
|
||||||
|
sar,
|
||||||
|
|a, b| (a as i32).wrapping_shr(b as _),
|
||||||
|
I32
|
||||||
|
);
|
||||||
|
shift!(
|
||||||
|
i32_shr_u,
|
||||||
|
Rd,
|
||||||
|
shr,
|
||||||
|
|a, b| (a as u32).wrapping_shr(b as _),
|
||||||
|
I32
|
||||||
|
);
|
||||||
|
shift!(
|
||||||
|
i32_rotl,
|
||||||
|
Rd,
|
||||||
|
rol,
|
||||||
|
|a, b| (a as u32).rotate_left(b as _),
|
||||||
|
I32
|
||||||
|
);
|
||||||
|
shift!(
|
||||||
|
i32_rotr,
|
||||||
|
Rd,
|
||||||
|
ror,
|
||||||
|
|a, b| (a as u32).rotate_right(b as _),
|
||||||
|
I32
|
||||||
|
);
|
||||||
|
|
||||||
|
shift!(
|
||||||
|
i64_shl,
|
||||||
|
Rq,
|
||||||
|
shl,
|
||||||
|
|a, b| (a as i64).wrapping_shl(b as _),
|
||||||
|
I64
|
||||||
|
);
|
||||||
|
shift!(
|
||||||
|
i64_shr_s,
|
||||||
|
Rq,
|
||||||
|
sar,
|
||||||
|
|a, b| (a as i64).wrapping_shr(b as _),
|
||||||
|
I64
|
||||||
|
);
|
||||||
|
shift!(
|
||||||
|
i64_shr_u,
|
||||||
|
Rq,
|
||||||
|
shr,
|
||||||
|
|a, b| (a as u64).wrapping_shr(b as _),
|
||||||
|
I64
|
||||||
|
);
|
||||||
|
shift!(
|
||||||
|
i64_rotl,
|
||||||
|
Rq,
|
||||||
|
rol,
|
||||||
|
|a, b| (a as u64).rotate_left(b as _),
|
||||||
|
I64
|
||||||
|
);
|
||||||
|
shift!(
|
||||||
|
i64_rotr,
|
||||||
|
Rq,
|
||||||
|
ror,
|
||||||
|
|a, b| (a as u64).rotate_right(b as _),
|
||||||
|
I64
|
||||||
|
);
|
||||||
|
|
||||||
// `sub` is not commutative, so we have to handle it differently (we _must_ use the `op1`
|
// `sub` is not commutative, so we have to handle it differently (we _must_ use the `op1`
|
||||||
// temp register as the output)
|
// temp register as the output)
|
||||||
@@ -1724,7 +1823,7 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let op1 = self.into_temp_reg(op1);
|
let op1 = self.into_temp_reg(I32, op1);
|
||||||
match op0 {
|
match op0 {
|
||||||
ValueLocation::Reg(reg) => {
|
ValueLocation::Reg(reg) => {
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
@@ -1771,12 +1870,12 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (op1, op0) = match op1 {
|
let (op1, op0) = match op1 {
|
||||||
ValueLocation::Reg(_) => (self.into_temp_reg(op1), op0),
|
ValueLocation::Reg(_) => (self.into_temp_reg(I64, op1), op0),
|
||||||
_ => {
|
_ => {
|
||||||
if op0.immediate().is_some() {
|
if op0.immediate().is_some() {
|
||||||
(self.into_temp_reg(op1), op0)
|
(self.into_temp_reg(I64, op1), op0)
|
||||||
} else {
|
} else {
|
||||||
(self.into_temp_reg(op0), op1)
|
(self.into_temp_reg(I64, op0), op1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1826,7 +1925,7 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let op1 = self.into_temp_reg(op1);
|
let op1 = self.into_temp_reg(I64, op1);
|
||||||
match op0 {
|
match op0 {
|
||||||
ValueLocation::Reg(reg) => {
|
ValueLocation::Reg(reg) => {
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
@@ -1866,12 +1965,12 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (op1, op0) = match op1 {
|
let (op1, op0) = match op1 {
|
||||||
ValueLocation::Reg(_) => (self.into_temp_reg(op1), op0),
|
ValueLocation::Reg(_) => (self.into_temp_reg(I32, op1), op0),
|
||||||
_ => {
|
_ => {
|
||||||
if op0.immediate().is_some() {
|
if op0.immediate().is_some() {
|
||||||
(self.into_temp_reg(op1), op0)
|
(self.into_temp_reg(I32, op1), op0)
|
||||||
} else {
|
} else {
|
||||||
(self.into_temp_reg(op0), op1)
|
(self.into_temp_reg(I32, op0), op1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1915,7 +2014,7 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
let reg = self.into_reg(other);
|
let reg = self.into_reg(I32, other);
|
||||||
|
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; test Rd(reg.rq().unwrap()), Rd(reg.rq().unwrap())
|
; test Rd(reg.rq().unwrap()), Rd(reg.rq().unwrap())
|
||||||
@@ -1925,21 +2024,33 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let out = self.block_state.regs.take_64();
|
let out_gpr = self.block_state.regs.take(GPRType::Rq);
|
||||||
|
|
||||||
// TODO: Can do this better for variables on stack
|
// TODO: Can do this better for variables on stack
|
||||||
let reg = self.into_reg(else_);
|
macro_rules! cmov_helper {
|
||||||
|
($instr:ident, $val:expr) => {
|
||||||
|
match $val {
|
||||||
|
ValueLocation::Stack(offset) => {
|
||||||
|
let offset = self.adjusted_offset(offset);
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; cmovz Rq(out.rq().unwrap()), Rq(reg.rq().unwrap())
|
; $instr Rq(out_gpr.rq().unwrap()), [rsp + offset]
|
||||||
);
|
);
|
||||||
self.block_state.regs.release(reg);
|
}
|
||||||
let reg = self.into_reg(then);
|
other => {
|
||||||
|
let scratch = self.into_reg(GPRType::Rq, other);
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; cmovnz Rq(out.rq().unwrap()), Rq(reg.rq().unwrap())
|
; $instr Rq(out_gpr.rq().unwrap()), Rq(scratch.rq().unwrap())
|
||||||
);
|
);
|
||||||
self.block_state.regs.release(reg);
|
self.block_state.regs.release(scratch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.push(ValueLocation::Reg(out));
|
cmov_helper!(cmovz, else_);
|
||||||
|
cmov_helper!(cmovnz, then);
|
||||||
|
|
||||||
|
self.push(ValueLocation::Reg(out_gpr));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pick(&mut self, depth: u32) {
|
pub fn pick(&mut self, depth: u32) {
|
||||||
@@ -1956,7 +2067,7 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
self.block_state.stack.push(v);
|
self.block_state.stack.push(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn literal(&mut self, imm: Value) {
|
pub fn const_(&mut self, imm: Value) {
|
||||||
self.push(ValueLocation::Immediate(imm));
|
self.push(ValueLocation::Immediate(imm));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2034,7 +2145,7 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
if offset == -(WORD_SIZE as i32) {
|
if offset == -(WORD_SIZE as i32) {
|
||||||
self.push_physical(val);
|
self.push_physical(val);
|
||||||
} else {
|
} else {
|
||||||
let gpr = self.into_reg(val);
|
let gpr = self.into_reg(GPRType::Rq, val);
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; mov [rsp + offset], Rq(gpr.rq().unwrap())
|
; mov [rsp + offset], Rq(gpr.rq().unwrap())
|
||||||
);
|
);
|
||||||
@@ -2108,8 +2219,8 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let callee = self.pop();
|
let callee = self.pop();
|
||||||
let callee = self.into_temp_reg(callee);
|
let callee = self.into_temp_reg(I32, callee);
|
||||||
let temp0 = self.block_state.regs.take_64();
|
let temp0 = self.block_state.regs.take(I64);
|
||||||
|
|
||||||
for &loc in &locs {
|
for &loc in &locs {
|
||||||
if let CCLoc::Reg(r) = loc {
|
if let CCLoc::Reg(r) = loc {
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ use crate::microwasm::*;
|
|||||||
use crate::module::{quickhash, ModuleContext, SigType, Signature};
|
use crate::module::{quickhash, ModuleContext, SigType, Signature};
|
||||||
use either::{Either, Left, Right};
|
use either::{Either, Left, Right};
|
||||||
use multi_mut::HashMapMultiMut;
|
use multi_mut::HashMapMultiMut;
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, convert::TryInto, hash::Hash};
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Block {
|
struct Block {
|
||||||
@@ -27,6 +26,47 @@ impl Block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn translate_wasm<M: ModuleContext>(
|
||||||
|
session: &mut CodeGenSession<M>,
|
||||||
|
func_idx: u32,
|
||||||
|
body: &wasmparser::FunctionBody,
|
||||||
|
) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
for<'any> &'any M::Signature: Into<OpSig>,
|
||||||
|
{
|
||||||
|
let ty = session.module_context.func_type(func_idx);
|
||||||
|
|
||||||
|
if false {
|
||||||
|
let mut microwasm = vec![];
|
||||||
|
|
||||||
|
let microwasm_conv = MicrowasmConv::new(
|
||||||
|
session.module_context,
|
||||||
|
ty.params().iter().map(SigType::to_microwasm_type),
|
||||||
|
ty.returns().iter().map(SigType::to_microwasm_type),
|
||||||
|
body,
|
||||||
|
);
|
||||||
|
|
||||||
|
for ops in microwasm_conv {
|
||||||
|
microwasm.extend(ops?);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{}", crate::microwasm::dis(func_idx, µwasm));
|
||||||
|
}
|
||||||
|
|
||||||
|
let microwasm_conv = MicrowasmConv::new(
|
||||||
|
session.module_context,
|
||||||
|
ty.params().iter().map(SigType::to_microwasm_type),
|
||||||
|
ty.returns().iter().map(SigType::to_microwasm_type),
|
||||||
|
body,
|
||||||
|
);
|
||||||
|
|
||||||
|
translate(
|
||||||
|
session,
|
||||||
|
func_idx,
|
||||||
|
microwasm_conv.flat_map(|i| i.expect("TODO: Make this not panic")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn translate<M: ModuleContext, I, L>(
|
pub fn translate<M: ModuleContext, I, L>(
|
||||||
session: &mut CodeGenSession<M>,
|
session: &mut CodeGenSession<M>,
|
||||||
func_idx: u32,
|
func_idx: u32,
|
||||||
@@ -315,8 +355,10 @@ where
|
|||||||
Operator::Clz(Size::_64) => ctx.i64_clz(),
|
Operator::Clz(Size::_64) => ctx.i64_clz(),
|
||||||
Operator::Ctz(Size::_64) => ctx.i64_ctz(),
|
Operator::Ctz(Size::_64) => ctx.i64_ctz(),
|
||||||
Operator::Popcnt(Size::_64) => ctx.i64_popcnt(),
|
Operator::Popcnt(Size::_64) => ctx.i64_popcnt(),
|
||||||
|
Operator::Add(F64) => ctx.f64_add(),
|
||||||
|
Operator::Add(F32) => ctx.f32_add(),
|
||||||
Operator::Drop(range) => ctx.drop(range),
|
Operator::Drop(range) => ctx.drop(range),
|
||||||
Operator::Const(val) => ctx.literal(val),
|
Operator::Const(val) => ctx.const_(val),
|
||||||
Operator::Load { ty: I32, memarg } => ctx.i32_load(memarg.offset)?,
|
Operator::Load { ty: I32, memarg } => ctx.i32_load(memarg.offset)?,
|
||||||
Operator::Load { ty: I64, memarg } => ctx.i64_load(memarg.offset)?,
|
Operator::Load { ty: I64, memarg } => ctx.i64_load(memarg.offset)?,
|
||||||
Operator::Store { ty: I32, memarg } => ctx.i32_store(memarg.offset)?,
|
Operator::Store { ty: I32, memarg } => ctx.i32_store(memarg.offset)?,
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
const_slice_len,
|
const_slice_len,
|
||||||
never_type,
|
never_type,
|
||||||
alloc_layout_extra,
|
alloc_layout_extra,
|
||||||
try_from
|
try_from,
|
||||||
|
try_trait,
|
||||||
)]
|
)]
|
||||||
#![plugin(dynasm)]
|
#![plugin(dynasm)]
|
||||||
|
|
||||||
@@ -44,5 +45,5 @@ mod translate_sections;
|
|||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
pub use backend::CodeGenSession;
|
pub use backend::CodeGenSession;
|
||||||
pub use function_body::translate as translate_function;
|
pub use function_body::translate_wasm as translate_function;
|
||||||
pub use module::{translate, ExecutableModule, ModuleContext, Signature, TranslatedModule};
|
pub use module::{translate, ExecutableModule, ModuleContext, Signature, TranslatedModule};
|
||||||
|
|||||||
288
src/microwasm.rs
288
src/microwasm.rs
@@ -1,9 +1,12 @@
|
|||||||
use crate::module::ModuleContext;
|
use crate::module::{ModuleContext, SigType, Signature};
|
||||||
|
use cranelift_codegen::ir::Signature as CraneliftSignature;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{
|
use std::{
|
||||||
|
convert::TryFrom,
|
||||||
fmt,
|
fmt,
|
||||||
iter::{self, FromIterator},
|
iter::{self, FromIterator},
|
||||||
ops::RangeInclusive,
|
ops::RangeInclusive,
|
||||||
|
option::NoneError,
|
||||||
};
|
};
|
||||||
use wasmparser::{
|
use wasmparser::{
|
||||||
FunctionBody, Ieee32, Ieee64, MemoryImmediate, Operator as WasmOperator, OperatorsReader,
|
FunctionBody, Ieee32, Ieee64, MemoryImmediate, Operator as WasmOperator, OperatorsReader,
|
||||||
@@ -180,6 +183,52 @@ pub enum Type<I> {
|
|||||||
Float(Size),
|
Float(Size),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait IntoType<T> {
|
||||||
|
fn into_type() -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoType<SignlessType> for i32 {
|
||||||
|
fn into_type() -> SignlessType {
|
||||||
|
I32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoType<SignlessType> for i64 {
|
||||||
|
fn into_type() -> SignlessType {
|
||||||
|
I64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoType<SignlessType> for u32 {
|
||||||
|
fn into_type() -> SignlessType {
|
||||||
|
I32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoType<SignlessType> for u64 {
|
||||||
|
fn into_type() -> SignlessType {
|
||||||
|
I64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoType<SignlessType> for f32 {
|
||||||
|
fn into_type() -> SignlessType {
|
||||||
|
F32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoType<SignlessType> for f64 {
|
||||||
|
fn into_type() -> SignlessType {
|
||||||
|
F64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> Type<I> {
|
||||||
|
pub fn for_<T: IntoType<Self>>() -> Self {
|
||||||
|
T::into_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for SignfulType {
|
impl fmt::Display for SignfulType {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
@@ -251,6 +300,14 @@ impl SignlessType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<wasmparser::Type> for SignlessType {
|
||||||
|
type Error = NoneError;
|
||||||
|
|
||||||
|
fn try_from(other: wasmparser::Type) -> Result<SignlessType, NoneError> {
|
||||||
|
Ok(SignlessType::from_wasm(other)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BrTable<L> {
|
pub struct BrTable<L> {
|
||||||
targets: Vec<L>,
|
targets: Vec<L>,
|
||||||
@@ -713,8 +770,10 @@ enum ControlFrameKind {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
struct ControlFrame {
|
struct ControlFrame {
|
||||||
id: u32,
|
id: u32,
|
||||||
|
arguments: u32,
|
||||||
returns: u32,
|
returns: u32,
|
||||||
kind: ControlFrameKind,
|
kind: ControlFrameKind,
|
||||||
}
|
}
|
||||||
@@ -760,7 +819,6 @@ pub struct MicrowasmConv<'a, 'b, M> {
|
|||||||
internal: OperatorsReader<'a>,
|
internal: OperatorsReader<'a>,
|
||||||
module: &'b M,
|
module: &'b M,
|
||||||
current_id: u32,
|
current_id: u32,
|
||||||
num_locals: u32,
|
|
||||||
control_frames: Vec<ControlFrame>,
|
control_frames: Vec<ControlFrame>,
|
||||||
unreachable: bool,
|
unreachable: bool,
|
||||||
}
|
}
|
||||||
@@ -801,17 +859,20 @@ impl OpSig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&'_ wasmparser::FuncType> for OpSig {
|
impl<T> From<&'_ T> for OpSig
|
||||||
fn from(other: &wasmparser::FuncType) -> Self {
|
where
|
||||||
|
T: Signature,
|
||||||
|
{
|
||||||
|
fn from(other: &T) -> Self {
|
||||||
OpSig::new(
|
OpSig::new(
|
||||||
other
|
other
|
||||||
.params
|
.params()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|t| SigT::Concrete(Type::from_wasm(*t).unwrap())),
|
.map(|t| SigT::Concrete(t.to_microwasm_type())),
|
||||||
other
|
other
|
||||||
.returns
|
.returns()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|t| SigT::Concrete(Type::from_wasm(*t).unwrap())),
|
.map(|t| SigT::Concrete(t.to_microwasm_type())),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -820,6 +881,56 @@ impl<'a, 'b, M: ModuleContext> MicrowasmConv<'a, 'b, M>
|
|||||||
where
|
where
|
||||||
for<'any> &'any M::Signature: Into<OpSig>,
|
for<'any> &'any M::Signature: Into<OpSig>,
|
||||||
{
|
{
|
||||||
|
pub fn new(
|
||||||
|
context: &'b M,
|
||||||
|
params: impl IntoIterator<Item = SignlessType>,
|
||||||
|
returns: impl IntoIterator<Item = SignlessType>,
|
||||||
|
reader: &'a FunctionBody,
|
||||||
|
) -> Self {
|
||||||
|
// TODO: Don't panic!
|
||||||
|
let locals_reader = reader
|
||||||
|
.get_locals_reader()
|
||||||
|
.expect("Failed to get locals reader");
|
||||||
|
let mut locals = Vec::from_iter(params);
|
||||||
|
let mut consts = Vec::new();
|
||||||
|
|
||||||
|
for loc in locals_reader {
|
||||||
|
let (count, ty) = loc.expect("Getting local failed");
|
||||||
|
let ty = Type::from_wasm(ty).expect("Invalid local type");
|
||||||
|
locals.extend(std::iter::repeat(ty).take(count as _));
|
||||||
|
consts.extend(
|
||||||
|
std::iter::repeat(ty)
|
||||||
|
.map(Value::default_for_type)
|
||||||
|
.take(count as _),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let num_locals = locals.len() as _;
|
||||||
|
|
||||||
|
let mut out = Self {
|
||||||
|
is_done: false,
|
||||||
|
stack: locals,
|
||||||
|
module: context,
|
||||||
|
consts_to_emit: Some(consts),
|
||||||
|
internal: reader
|
||||||
|
.get_operators_reader()
|
||||||
|
.expect("Failed to get operators reader"),
|
||||||
|
current_id: 0,
|
||||||
|
control_frames: vec![],
|
||||||
|
unreachable: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = out.next_id();
|
||||||
|
out.control_frames.push(ControlFrame {
|
||||||
|
id,
|
||||||
|
arguments: num_locals,
|
||||||
|
returns: returns.into_iter().count() as _,
|
||||||
|
kind: ControlFrameKind::Function,
|
||||||
|
});
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
fn op_sig(&self, op: &WasmOperator) -> OpSig {
|
fn op_sig(&self, op: &WasmOperator) -> OpSig {
|
||||||
use self::SigT::T;
|
use self::SigT::T;
|
||||||
use std::iter::{empty as none, once};
|
use std::iter::{empty as none, once};
|
||||||
@@ -1111,8 +1222,12 @@ where
|
|||||||
fn apply_op(&mut self, sig: OpSig) {
|
fn apply_op(&mut self, sig: OpSig) {
|
||||||
let mut ty_param = None;
|
let mut ty_param = None;
|
||||||
|
|
||||||
for p in sig.input.into_iter().rev() {
|
for p in sig.input.iter().rev() {
|
||||||
let stack_ty = self.stack.pop().expect("Stack is empty");
|
let stack_ty = self
|
||||||
|
.stack
|
||||||
|
.pop()
|
||||||
|
.unwrap_or_else(|| panic!("Stack is empty (while processing {:?})", sig));
|
||||||
|
|
||||||
let ty = match p {
|
let ty = match p {
|
||||||
SigT::T => {
|
SigT::T => {
|
||||||
if let Some(t) = ty_param {
|
if let Some(t) = ty_param {
|
||||||
@@ -1122,7 +1237,7 @@ where
|
|||||||
stack_ty
|
stack_ty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SigT::Concrete(ty) => ty,
|
SigT::Concrete(ty) => *ty,
|
||||||
};
|
};
|
||||||
|
|
||||||
debug_assert_eq!(ty, stack_ty);
|
debug_assert_eq!(ty, stack_ty);
|
||||||
@@ -1153,57 +1268,6 @@ where
|
|||||||
|
|
||||||
for _ in self.stack.drain(internal_range) {}
|
for _ in self.stack.drain(internal_range) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// I don't know if we need to know the return type
|
|
||||||
pub fn new(
|
|
||||||
context: &'b M,
|
|
||||||
params: impl IntoIterator<Item = SignlessType>,
|
|
||||||
returns: impl IntoIterator<Item = SignlessType>,
|
|
||||||
reader: &'a FunctionBody,
|
|
||||||
) -> Self {
|
|
||||||
// TODO: Don't panic!
|
|
||||||
let locals_reader = reader
|
|
||||||
.get_locals_reader()
|
|
||||||
.expect("Failed to get locals reader");
|
|
||||||
let mut locals = Vec::from_iter(params);
|
|
||||||
let mut consts = Vec::new();
|
|
||||||
|
|
||||||
for loc in locals_reader {
|
|
||||||
let (count, ty) = loc.expect("Getting local failed");
|
|
||||||
let ty = Type::from_wasm(ty).expect("Invalid local type");
|
|
||||||
locals.extend(std::iter::repeat(ty).take(count as _));
|
|
||||||
consts.extend(
|
|
||||||
std::iter::repeat(ty)
|
|
||||||
.map(Value::default_for_type)
|
|
||||||
.take(count as _),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let num_locals = locals.len() as _;
|
|
||||||
|
|
||||||
let mut out = Self {
|
|
||||||
is_done: false,
|
|
||||||
stack: locals,
|
|
||||||
module: context,
|
|
||||||
num_locals,
|
|
||||||
consts_to_emit: Some(consts),
|
|
||||||
internal: reader
|
|
||||||
.get_operators_reader()
|
|
||||||
.expect("Failed to get operators reader"),
|
|
||||||
current_id: 0,
|
|
||||||
control_frames: vec![],
|
|
||||||
unreachable: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let id = out.next_id();
|
|
||||||
out.control_frames.push(ControlFrame {
|
|
||||||
id,
|
|
||||||
returns: returns.into_iter().count() as _,
|
|
||||||
kind: ControlFrameKind::Function,
|
|
||||||
});
|
|
||||||
|
|
||||||
out
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b, M: ModuleContext> Iterator for MicrowasmConv<'a, 'b, M>
|
impl<'a, 'b, M: ModuleContext> Iterator for MicrowasmConv<'a, 'b, M>
|
||||||
@@ -1215,12 +1279,13 @@ where
|
|||||||
fn next(&mut self) -> Option<wasmparser::Result<SmallVec<[OperatorFromWasm; 1]>>> {
|
fn next(&mut self) -> Option<wasmparser::Result<SmallVec<[OperatorFromWasm; 1]>>> {
|
||||||
macro_rules! to_drop {
|
macro_rules! to_drop {
|
||||||
($block:expr) => {{
|
($block:expr) => {{
|
||||||
let first_non_local_depth = $block.returns;
|
let block = &$block;
|
||||||
|
let first_non_local_depth = block.returns;
|
||||||
|
|
||||||
(|| {
|
(|| {
|
||||||
let last_non_local_depth = (self.stack.len() as u32)
|
let last_non_local_depth = (self.stack.len() as u32)
|
||||||
.checked_sub(1)?
|
.checked_sub(1)?
|
||||||
.checked_sub(self.num_locals + 1)?;
|
.checked_sub(block.arguments)?;
|
||||||
|
|
||||||
if first_non_local_depth <= last_non_local_depth {
|
if first_non_local_depth <= last_non_local_depth {
|
||||||
Some(first_non_local_depth..=last_non_local_depth)
|
Some(first_non_local_depth..=last_non_local_depth)
|
||||||
@@ -1266,6 +1331,8 @@ where
|
|||||||
if depth == 0 {
|
if depth == 0 {
|
||||||
let block = self.control_frames.last_mut().expect("Failed");
|
let block = self.control_frames.last_mut().expect("Failed");
|
||||||
|
|
||||||
|
self.stack = block.params().unwrap().to_vec();
|
||||||
|
|
||||||
if let ControlFrameKind::If { has_else, .. } = &mut block.kind {
|
if let ControlFrameKind::If { has_else, .. } = &mut block.kind {
|
||||||
*has_else = true;
|
*has_else = true;
|
||||||
}
|
}
|
||||||
@@ -1332,6 +1399,7 @@ where
|
|||||||
let id = self.next_id();
|
let id = self.next_id();
|
||||||
self.control_frames.push(ControlFrame {
|
self.control_frames.push(ControlFrame {
|
||||||
id,
|
id,
|
||||||
|
arguments: self.stack.len() as u32,
|
||||||
returns: if ty == wasmparser::Type::EmptyBlockType {
|
returns: if ty == wasmparser::Type::EmptyBlockType {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
@@ -1350,6 +1418,7 @@ where
|
|||||||
let id = self.next_id();
|
let id = self.next_id();
|
||||||
self.control_frames.push(ControlFrame {
|
self.control_frames.push(ControlFrame {
|
||||||
id,
|
id,
|
||||||
|
arguments: self.stack.len() as u32,
|
||||||
returns: if ty == wasmparser::Type::EmptyBlockType {
|
returns: if ty == wasmparser::Type::EmptyBlockType {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
@@ -1372,6 +1441,7 @@ where
|
|||||||
let params = self.block_params();
|
let params = self.block_params();
|
||||||
self.control_frames.push(ControlFrame {
|
self.control_frames.push(ControlFrame {
|
||||||
id,
|
id,
|
||||||
|
arguments: self.stack.len() as u32,
|
||||||
returns: if ty == wasmparser::Type::EmptyBlockType {
|
returns: if ty == wasmparser::Type::EmptyBlockType {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
@@ -1507,7 +1577,11 @@ where
|
|||||||
if let Some(_to_drop) = to_drop {
|
if let Some(_to_drop) = to_drop {
|
||||||
// TODO: We want to generate an intermediate block here, but that might cause
|
// TODO: We want to generate an intermediate block here, but that might cause
|
||||||
// us to generate a spurious `jmp`.
|
// us to generate a spurious `jmp`.
|
||||||
unimplemented!()
|
unimplemented!(
|
||||||
|
"We don't yet support passing different numbers of arguments \
|
||||||
|
to each half of a `br_if` - supporting this efficiently without \
|
||||||
|
complicating the backend might be difficult"
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
smallvec![
|
smallvec![
|
||||||
Operator::block(params, label),
|
Operator::block(params, label),
|
||||||
@@ -1519,7 +1593,7 @@ where
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WasmOperator::BrTable { .. } => unimplemented!(),
|
WasmOperator::BrTable { .. } => unimplemented!("{:?}", op),
|
||||||
WasmOperator::Return => {
|
WasmOperator::Return => {
|
||||||
self.unreachable = true;
|
self.unreachable = true;
|
||||||
|
|
||||||
@@ -1634,8 +1708,8 @@ where
|
|||||||
WasmOperator::I64Const { value } => smallvec![Operator::Const(Value::I64(value))],
|
WasmOperator::I64Const { value } => smallvec![Operator::Const(Value::I64(value))],
|
||||||
WasmOperator::F32Const { value } => smallvec![Operator::Const(Value::F32(value))],
|
WasmOperator::F32Const { value } => smallvec![Operator::Const(Value::F32(value))],
|
||||||
WasmOperator::F64Const { value } => smallvec![Operator::Const(Value::F64(value))],
|
WasmOperator::F64Const { value } => smallvec![Operator::Const(Value::F64(value))],
|
||||||
WasmOperator::RefNull => unimplemented!(),
|
WasmOperator::RefNull => unimplemented!("{:?}", op),
|
||||||
WasmOperator::RefIsNull => unimplemented!(),
|
WasmOperator::RefIsNull => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I32Eqz => smallvec![Operator::Eqz(Size::_32)],
|
WasmOperator::I32Eqz => smallvec![Operator::Eqz(Size::_32)],
|
||||||
WasmOperator::I32Eq => smallvec![Operator::Eq(I32)],
|
WasmOperator::I32Eq => smallvec![Operator::Eq(I32)],
|
||||||
WasmOperator::I32Ne => smallvec![Operator::Ne(I32)],
|
WasmOperator::I32Ne => smallvec![Operator::Ne(I32)],
|
||||||
@@ -1734,49 +1808,49 @@ where
|
|||||||
WasmOperator::F64Min => smallvec![Operator::Min(Size::_64)],
|
WasmOperator::F64Min => smallvec![Operator::Min(Size::_64)],
|
||||||
WasmOperator::F64Max => smallvec![Operator::Max(Size::_64)],
|
WasmOperator::F64Max => smallvec![Operator::Max(Size::_64)],
|
||||||
WasmOperator::F64Copysign => smallvec![Operator::Copysign(Size::_64)],
|
WasmOperator::F64Copysign => smallvec![Operator::Copysign(Size::_64)],
|
||||||
WasmOperator::I32WrapI64 => unimplemented!(),
|
WasmOperator::I32WrapI64 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I32TruncSF32 => unimplemented!(),
|
WasmOperator::I32TruncSF32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I32TruncUF32 => unimplemented!(),
|
WasmOperator::I32TruncUF32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I32TruncSF64 => unimplemented!(),
|
WasmOperator::I32TruncSF64 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I32TruncUF64 => unimplemented!(),
|
WasmOperator::I32TruncUF64 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I64ExtendSI32 => unimplemented!(),
|
WasmOperator::I64ExtendSI32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I64ExtendUI32 => unimplemented!(),
|
WasmOperator::I64ExtendUI32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I64TruncSF32 => unimplemented!(),
|
WasmOperator::I64TruncSF32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I64TruncUF32 => unimplemented!(),
|
WasmOperator::I64TruncUF32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I64TruncSF64 => unimplemented!(),
|
WasmOperator::I64TruncSF64 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I64TruncUF64 => unimplemented!(),
|
WasmOperator::I64TruncUF64 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::F32ConvertSI32 => unimplemented!(),
|
WasmOperator::F32ConvertSI32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::F32ConvertUI32 => unimplemented!(),
|
WasmOperator::F32ConvertUI32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::F32ConvertSI64 => unimplemented!(),
|
WasmOperator::F32ConvertSI64 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::F32ConvertUI64 => unimplemented!(),
|
WasmOperator::F32ConvertUI64 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::F32DemoteF64 => unimplemented!(),
|
WasmOperator::F32DemoteF64 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::F64ConvertSI32 => unimplemented!(),
|
WasmOperator::F64ConvertSI32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::F64ConvertUI32 => unimplemented!(),
|
WasmOperator::F64ConvertUI32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::F64ConvertSI64 => unimplemented!(),
|
WasmOperator::F64ConvertSI64 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::F64ConvertUI64 => unimplemented!(),
|
WasmOperator::F64ConvertUI64 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::F64PromoteF32 => unimplemented!(),
|
WasmOperator::F64PromoteF32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I32ReinterpretF32 => unimplemented!(),
|
WasmOperator::I32ReinterpretF32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I64ReinterpretF64 => unimplemented!(),
|
WasmOperator::I64ReinterpretF64 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::F32ReinterpretI32 => unimplemented!(),
|
WasmOperator::F32ReinterpretI32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::F64ReinterpretI64 => unimplemented!(),
|
WasmOperator::F64ReinterpretI64 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I32Extend8S => unimplemented!(),
|
WasmOperator::I32Extend8S => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I32Extend16S => unimplemented!(),
|
WasmOperator::I32Extend16S => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I64Extend8S => unimplemented!(),
|
WasmOperator::I64Extend8S => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I64Extend16S => unimplemented!(),
|
WasmOperator::I64Extend16S => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I64Extend32S => unimplemented!(),
|
WasmOperator::I64Extend32S => unimplemented!("{:?}", op),
|
||||||
|
|
||||||
// 0xFC operators
|
// 0xFC operators
|
||||||
// Non-trapping Float-to-int Conversions
|
// Non-trapping Float-to-int Conversions
|
||||||
WasmOperator::I32TruncSSatF32 => unimplemented!(),
|
WasmOperator::I32TruncSSatF32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I32TruncUSatF32 => unimplemented!(),
|
WasmOperator::I32TruncUSatF32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I32TruncSSatF64 => unimplemented!(),
|
WasmOperator::I32TruncSSatF64 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I32TruncUSatF64 => unimplemented!(),
|
WasmOperator::I32TruncUSatF64 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I64TruncSSatF32 => unimplemented!(),
|
WasmOperator::I64TruncSSatF32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I64TruncUSatF32 => unimplemented!(),
|
WasmOperator::I64TruncUSatF32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I64TruncSSatF64 => unimplemented!(),
|
WasmOperator::I64TruncSSatF64 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I64TruncUSatF64 => unimplemented!(),
|
WasmOperator::I64TruncUSatF64 => unimplemented!("{:?}", op),
|
||||||
|
|
||||||
_ => unimplemented!(),
|
other => unimplemented!("{:?}", other),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -334,10 +334,18 @@ impl SigType for AbiParam {
|
|||||||
fn to_microwasm_type(&self) -> microwasm::SignlessType {
|
fn to_microwasm_type(&self) -> microwasm::SignlessType {
|
||||||
use microwasm::{Size::*, Type::*};
|
use microwasm::{Size::*, Type::*};
|
||||||
|
|
||||||
if self.value_type == ir::Type::int(32).unwrap() {
|
if self.value_type.is_int() {
|
||||||
Int(_32)
|
match self.value_type.bits() {
|
||||||
} else if self.value_type == ir::Type::int(64).unwrap() {
|
32 => Int(_32),
|
||||||
Int(_64)
|
64 => Int(_64),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
} else if self.value_type.is_float() {
|
||||||
|
match self.value_type.bits() {
|
||||||
|
32 => Float(_32),
|
||||||
|
64 => Float(_64),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|||||||
47
src/tests.rs
47
src/tests.rs
@@ -290,6 +290,53 @@ mod op64 {
|
|||||||
binop_test!(rotr, |a, b| (a as u64).rotate_right(b as _) as i64);
|
binop_test!(rotr, |a, b| (a as u64).rotate_right(b as _) as i64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod opf32 {
|
||||||
|
use super::translate_wat;
|
||||||
|
|
||||||
|
quickcheck! {
|
||||||
|
fn as_params(a: f64, b: f64) -> bool {
|
||||||
|
const CODE: &str = r#"(module
|
||||||
|
(func (param f64) (param f64) (result f64)
|
||||||
|
(f64.add (get_local 0) (get_local 1))))
|
||||||
|
"#;
|
||||||
|
translate_wat(CODE).execute_func::<(f64, f64), f64>(0, (a, b)) == Ok(a + b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lit_lit(a: f64, b: f64) -> bool {
|
||||||
|
translate_wat(&format!("
|
||||||
|
(module (func (result f64)
|
||||||
|
(f64.add (f64.const {left}) (f64.const {right}))))
|
||||||
|
", left = a, right = b)).execute_func::<(), f64>(0, ()) == Ok(a + b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lit_reg(a: f64, b: f64) -> bool {
|
||||||
|
use std::sync::Once;
|
||||||
|
|
||||||
|
let translated = translate_wat(&format!("
|
||||||
|
(module (func (param f64) (result f64)
|
||||||
|
(f64.add (f64.const {left}) (get_local 0))))
|
||||||
|
", left = a));
|
||||||
|
static ONCE: Once = Once::new();
|
||||||
|
ONCE.call_once(|| translated.disassemble());
|
||||||
|
|
||||||
|
translated.execute_func::<(f64,), f64>(0, (b,)) == Ok(a + b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reg_lit(a: f64, b: f64) -> bool {
|
||||||
|
use std::sync::Once;
|
||||||
|
|
||||||
|
let translated = translate_wat(&format!("
|
||||||
|
(module (func (param f64) (result f64)
|
||||||
|
(f64.add (get_local 0) (f64.const {right}))))
|
||||||
|
", right = b));
|
||||||
|
static ONCE: Once = Once::new();
|
||||||
|
ONCE.call_once(|| translated.disassemble());
|
||||||
|
|
||||||
|
translated.execute_func::<(f64,), f64>(0, (a,)) == Ok(a + b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
quickcheck! {
|
quickcheck! {
|
||||||
fn if_then_else(a: u32, b: u32) -> bool {
|
fn if_then_else(a: u32, b: u32) -> bool {
|
||||||
const CODE: &str = r#"
|
const CODE: &str = r#"
|
||||||
|
|||||||
@@ -114,20 +114,6 @@ pub fn code(
|
|||||||
let mut session = CodeGenSession::new(func_count, translation_ctx);
|
let mut session = CodeGenSession::new(func_count, translation_ctx);
|
||||||
for (idx, body) in code.into_iter().enumerate() {
|
for (idx, body) in code.into_iter().enumerate() {
|
||||||
let body = body?;
|
let body = body?;
|
||||||
let mut microwasm_conv = MicrowasmConv::new(
|
|
||||||
translation_ctx,
|
|
||||||
translation_ctx
|
|
||||||
.func_type(idx as _)
|
|
||||||
.params
|
|
||||||
.iter()
|
|
||||||
.map(|t| MWType::from_wasm(*t).unwrap()),
|
|
||||||
translation_ctx
|
|
||||||
.func_type(idx as _)
|
|
||||||
.returns
|
|
||||||
.iter()
|
|
||||||
.map(|t| MWType::from_wasm(*t).unwrap()),
|
|
||||||
&body,
|
|
||||||
);
|
|
||||||
|
|
||||||
if true {
|
if true {
|
||||||
let mut microwasm = vec![];
|
let mut microwasm = vec![];
|
||||||
@@ -154,10 +140,10 @@ pub fn code(
|
|||||||
println!("{}", crate::microwasm::dis(idx, µwasm));
|
println!("{}", crate::microwasm::dis(idx, µwasm));
|
||||||
}
|
}
|
||||||
|
|
||||||
function_body::translate(
|
function_body::translate_wasm(
|
||||||
&mut session,
|
&mut session,
|
||||||
idx as u32,
|
idx as u32,
|
||||||
microwasm_conv.flat_map(|i| i.expect("TODO: Make this not panic")),
|
&body,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Ok(session.into_translated_code_section()?)
|
Ok(session.into_translated_code_section()?)
|
||||||
|
|||||||
Reference in New Issue
Block a user