Operators.

This commit is contained in:
losfair
2019-02-19 20:25:09 +08:00
parent 5583e96d96
commit 93d2713bde

View File

@ -52,6 +52,22 @@ impl Register {
_ => unreachable!(), _ => unreachable!(),
} }
} }
pub fn is_used(&self, stack: &ValueStack) -> bool {
use self::Register::*;
for val in &stack.values {
match val.location {
ValueLocation::Register(x) => {
if Register::from_scratch_reg(x) == *self {
return true;
}
}
ValueLocation::Stack => break,
}
}
false
}
} }
#[derive(Default)] #[derive(Default)]
@ -202,6 +218,136 @@ impl X64FunctionCode {
} }
Ok(()) Ok(())
} }
/// Emits a binary operator.
///
/// Guarantees that the first Register parameter to callback `f` will never be `Register::RAX`.
fn emit_binop_i32<F: FnOnce(&mut Assembler, &ValueStack, Register, Register)>(
assembler: &mut Assembler,
value_stack: &mut ValueStack,
f: F,
) -> Result<(), CodegenError> {
let (a, b) = value_stack.pop2()?;
if a.ty != WpType::I32 || b.ty != WpType::I32 {
return Err(CodegenError {
message: "I32Add type mismatch",
});
}
value_stack.push(WpType::I32);
if a.location.is_register() && b.location.is_register() {
// output is in a_reg.
f(
assembler,
value_stack,
Register::from_scratch_reg(a.location.get_register()?),
Register::from_scratch_reg(b.location.get_register()?),
);
} else if a.location.is_register() {
dynasm!(
assembler
; mov eax, [rsp]
; add rsp, 4
);
f(
assembler,
value_stack,
Register::from_scratch_reg(a.location.get_register()?),
Register::RAX,
);
} else if b.location.is_register() {
unreachable!();
} else {
dynasm!(
assembler
; push rcx
; mov ecx, [rsp + 12]
; mov eax, [rsp + 8]
);
f(assembler, value_stack, Register::RCX, Register::RAX);
dynasm!(
assembler
; mov [rsp + 12], ecx
; pop rcx
; add rsp, 4
);
}
Ok(())
}
fn emit_div_i32(
assembler: &mut Assembler,
value_stack: &ValueStack,
left: Register,
right: Register,
signed: bool,
out: Register,
) {
let dx_used = Register::RDX.is_used(value_stack);
if dx_used {
dynasm!(
assembler
; push rdx
);
}
if right == Register::RAX {
dynasm!(
assembler
; push rax
; mov eax, Rd(left as u8)
; mov edx, 0
; mov Rd(left as u8), [rsp]
);
if signed {
dynasm!(
assembler
; idiv Rd(left as u8)
);
} else {
dynasm!(
assembler
; div Rd(left as u8)
);
}
dynasm!(
assembler
; mov Rd(left as u8), Rd(out as u8)
; pop rax
);
} else {
dynasm!(
assembler
; mov eax, Rd(left as u8)
; mov edx, 0
);
if signed {
dynasm!(
assembler
; idiv Rd(right as u8)
);
} else {
dynasm!(
assembler
; div Rd(right as u8)
);
}
dynasm!(
assembler
; mov Rd(left as u8), Rd(out as u8)
);
}
if dx_used {
dynasm!(
assembler
; pop rdx
);
}
}
} }
impl FunctionCodeGenerator for X64FunctionCode { impl FunctionCodeGenerator for X64FunctionCode {
@ -329,46 +475,104 @@ impl FunctionCodeGenerator for X64FunctionCode {
} }
} }
Operator::I32Add => { Operator::I32Add => {
let (a, b) = self.value_stack.pop2()?; Self::emit_binop_i32(
if a.ty != WpType::I32 || b.ty != WpType::I32 { assembler,
return Err(CodegenError { &mut self.value_stack,
message: "I32Add type mismatch", |assembler, value_stack, left, right| {
}); dynasm!(
} assembler
self.value_stack.push(WpType::I32); ; add Rd(left as u8), Rd(right as u8)
)
if a.location.is_register() && b.location.is_register() { },
let (a_reg, b_reg) = ( )?;
Register::from_scratch_reg(a.location.get_register()?), }
Register::from_scratch_reg(b.location.get_register()?), Operator::I32Sub => {
); Self::emit_binop_i32(
// output is in a_reg. assembler,
dynasm!( &mut self.value_stack,
assembler |assembler, value_stack, left, right| {
; add Rd(a_reg as u8), Rd(b_reg as u8) dynasm!(
); assembler
} else if a.location.is_register() { ; sub Rd(left as u8), Rd(right as u8)
let a_reg = Register::from_scratch_reg(a.location.get_register()?); )
dynasm!( },
assembler )?;
; mov eax, [rsp] }
; add rsp, 4 Operator::I32Mul => {
; add Rd(a_reg as u8), eax Self::emit_binop_i32(
); assembler,
} else if b.location.is_register() { &mut self.value_stack,
unreachable!(); |assembler, value_stack, left, right| {
} else { dynasm!(
dynasm!( assembler
assembler ; imul Rd(left as u8), Rd(right as u8)
; push rcx )
; mov eax, [rsp + 12] },
; mov ecx, [rsp + 8] )?;
; add eax, ecx }
; mov [rsp + 12], eax Operator::I32DivU => {
; pop rcx Self::emit_binop_i32(
; add rsp, 4 assembler,
); &mut self.value_stack,
} |assembler, value_stack, left, right| {
Self::emit_div_i32(
assembler,
value_stack,
left,
right,
false,
Register::RAX,
);
},
)?;
}
Operator::I32DivS => {
Self::emit_binop_i32(
assembler,
&mut self.value_stack,
|assembler, value_stack, left, right| {
Self::emit_div_i32(
assembler,
value_stack,
left,
right,
true,
Register::RAX,
);
},
)?;
}
Operator::I32RemU => {
Self::emit_binop_i32(
assembler,
&mut self.value_stack,
|assembler, value_stack, left, right| {
Self::emit_div_i32(
assembler,
value_stack,
left,
right,
false,
Register::RDX,
);
},
)?;
}
Operator::I32RemS => {
Self::emit_binop_i32(
assembler,
&mut self.value_stack,
|assembler, value_stack, left, right| {
Self::emit_div_i32(
assembler,
value_stack,
left,
right,
true,
Register::RDX,
);
},
)?;
} }
Operator::Drop => { Operator::Drop => {
let info = self.value_stack.pop()?; let info = self.value_stack.pop()?;