winch: Add support for the <i32|i64>.div_* instructions (#5807)
* Refactor the structure and responsibilities of `CodeGenContext` This commit refactors how the `CodeGenContext` is used throughout the code generation process, making it easier to pass it around when more flexibility is desired in the MacroAssembler to perform the lowering of certain instructions. As of this change, the responsibility of the `CodeGenContext` is to provide an interface for operations that require an orchestration between the register allocator, the value stack and function's frame. The MacroAssembler is removed from the CodeGenContext as is passed as a dependency where needed, effectly using it as an independent code generation interface only. By giving more responsibilities to the `CodeGenContext` we can clearly separate the concerns of the register allocator, which previously did more than it should (e.g. popping values and spilling). This change ultimately allows passing in the `CodeGenContext` to the `MacroAssembler` when a given instruction cannot be generically described through a common interface. Allowing each implementation to decide the best way to lower a particular instruction. * winch: Add support for the WebAssembly `<i32|i64>.div_*` instructions Given that some architectures have very specific requirements on how to handle division, this change uses `CodeGenContext` as a dependency to the `div` MacroAssembler instruction to ensure that each implementation can decide on how to lower the division. This approach also allows -- in architectures where division can be expressed as an ordinary binary operation -- to rely on the `CodeGenContext::i32_binop` or `CodeGenContext::i64_binop` helpers.
This commit is contained in:
204
winch/codegen/src/codegen/context.rs
Normal file
204
winch/codegen/src/codegen/context.rs
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
use crate::{
|
||||||
|
frame::Frame,
|
||||||
|
masm::{MacroAssembler, OperandSize, RegImm},
|
||||||
|
reg::Reg,
|
||||||
|
regalloc::RegAlloc,
|
||||||
|
stack::{Stack, Val},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The code generation context.
|
||||||
|
/// The code generation context is made up of three
|
||||||
|
/// essential data structures:
|
||||||
|
///
|
||||||
|
/// * The register allocator, in charge of keeping the inventory of register
|
||||||
|
/// availability.
|
||||||
|
/// * The value stack, which keeps track of the state of the values
|
||||||
|
/// after each operation.
|
||||||
|
/// * The current function's frame.
|
||||||
|
///
|
||||||
|
/// These data structures normally require cooperating with each other
|
||||||
|
/// to perform most of the operations needed during the code
|
||||||
|
/// generation process. The code generation context should
|
||||||
|
/// be generally used as the single entry point to access
|
||||||
|
/// the compound functionality provided by its elements.
|
||||||
|
pub(crate) struct CodeGenContext<'a> {
|
||||||
|
/// The register allocator.
|
||||||
|
pub regalloc: RegAlloc,
|
||||||
|
/// The value stack.
|
||||||
|
pub stack: Stack,
|
||||||
|
/// The current function's frame.
|
||||||
|
pub frame: &'a Frame,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CodeGenContext<'a> {
|
||||||
|
/// Create a new code generation context.
|
||||||
|
pub fn new(regalloc: RegAlloc, stack: Stack, frame: &'a Frame) -> Self {
|
||||||
|
Self {
|
||||||
|
regalloc,
|
||||||
|
stack,
|
||||||
|
frame,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request a specific general purpose register to the register allocator,
|
||||||
|
/// spilling if not available.
|
||||||
|
pub fn gpr<M: MacroAssembler>(&mut self, named: Reg, masm: &mut M) -> Reg {
|
||||||
|
self.regalloc.gpr(named, &mut |regalloc| {
|
||||||
|
Self::spill(&mut self.stack, regalloc, &self.frame, masm)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request the next avaiable general purpose register to the register allocator,
|
||||||
|
/// spilling if no registers are available.
|
||||||
|
pub fn any_gpr<M: MacroAssembler>(&mut self, masm: &mut M) -> Reg {
|
||||||
|
self.regalloc
|
||||||
|
.any_gpr(&mut |regalloc| Self::spill(&mut self.stack, regalloc, &self.frame, masm))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Free the given general purpose register.
|
||||||
|
pub fn free_gpr(&mut self, reg: Reg) {
|
||||||
|
self.regalloc.free_gpr(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads the stack top value into a register, if it isn't already one;
|
||||||
|
/// spilling if there are no registers available.
|
||||||
|
pub fn pop_to_reg<M: MacroAssembler>(&mut self, masm: &mut M, size: OperandSize) -> Reg {
|
||||||
|
if let Some(reg) = self.stack.pop_reg() {
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dst = self.any_gpr(masm);
|
||||||
|
let val = self.stack.pop().expect("a value at stack top");
|
||||||
|
Self::move_val_to_reg(val, dst, masm, self.frame, size);
|
||||||
|
dst
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the stack top contains the given register. The register
|
||||||
|
/// gets allocated otherwise, potentially causing a spill.
|
||||||
|
/// Once the requested register is allocated, the value at the top of the stack
|
||||||
|
/// gets loaded into the register.
|
||||||
|
pub fn pop_to_named_reg<M: MacroAssembler>(
|
||||||
|
&mut self,
|
||||||
|
masm: &mut M,
|
||||||
|
named: Reg,
|
||||||
|
size: OperandSize,
|
||||||
|
) -> Reg {
|
||||||
|
if let Some(reg) = self.stack.pop_named_reg(named) {
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dst = self.gpr(named, masm);
|
||||||
|
let val = self.stack.pop().expect("a value at stack top");
|
||||||
|
Self::move_val_to_reg(val, dst, masm, self.frame, size);
|
||||||
|
dst
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_val_to_reg<M: MacroAssembler>(
|
||||||
|
src: Val,
|
||||||
|
dst: Reg,
|
||||||
|
masm: &mut M,
|
||||||
|
frame: &Frame,
|
||||||
|
size: OperandSize,
|
||||||
|
) {
|
||||||
|
match src {
|
||||||
|
Val::Reg(src) => masm.mov(RegImm::reg(src), RegImm::reg(dst), size),
|
||||||
|
Val::I32(imm) => masm.mov(RegImm::imm(imm.into()), RegImm::reg(dst), size),
|
||||||
|
Val::I64(imm) => masm.mov(RegImm::imm(imm), RegImm::reg(dst), size),
|
||||||
|
Val::Local(index) => {
|
||||||
|
let slot = frame
|
||||||
|
.get_local(index)
|
||||||
|
.expect(&format!("valid locat at index = {}", index));
|
||||||
|
let addr = masm.local_address(&slot);
|
||||||
|
masm.load(addr, dst, slot.ty.into());
|
||||||
|
}
|
||||||
|
v => panic!("Unsupported value {:?}", v),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prepares arguments for emitting an i32 binary operation.
|
||||||
|
pub fn i32_binop<F, M>(&mut self, masm: &mut M, emit: &mut F)
|
||||||
|
where
|
||||||
|
F: FnMut(&mut M, RegImm, RegImm, OperandSize),
|
||||||
|
M: MacroAssembler,
|
||||||
|
{
|
||||||
|
let top = self.stack.peek().expect("value at stack top");
|
||||||
|
|
||||||
|
if top.is_i32_const() {
|
||||||
|
let val = self
|
||||||
|
.stack
|
||||||
|
.pop_i32_const()
|
||||||
|
.expect("i32 const value at stack top");
|
||||||
|
let reg = self.pop_to_reg(masm, OperandSize::S32);
|
||||||
|
emit(
|
||||||
|
masm,
|
||||||
|
RegImm::reg(reg),
|
||||||
|
RegImm::imm(val as i64),
|
||||||
|
OperandSize::S32,
|
||||||
|
);
|
||||||
|
self.stack.push(Val::reg(reg));
|
||||||
|
} else {
|
||||||
|
let src = self.pop_to_reg(masm, OperandSize::S32);
|
||||||
|
let dst = self.pop_to_reg(masm, OperandSize::S32);
|
||||||
|
emit(masm, dst.into(), src.into(), OperandSize::S32);
|
||||||
|
self.regalloc.free_gpr(src);
|
||||||
|
self.stack.push(Val::reg(dst));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prepares arguments for emitting an i64 binary operation.
|
||||||
|
pub fn i64_binop<F, M>(&mut self, masm: &mut M, emit: &mut F)
|
||||||
|
where
|
||||||
|
F: FnMut(&mut M, RegImm, RegImm, OperandSize),
|
||||||
|
M: MacroAssembler,
|
||||||
|
{
|
||||||
|
let top = self.stack.peek().expect("value at stack top");
|
||||||
|
if top.is_i64_const() {
|
||||||
|
let val = self
|
||||||
|
.stack
|
||||||
|
.pop_i64_const()
|
||||||
|
.expect("i64 const value at stack top");
|
||||||
|
let reg = self.pop_to_reg(masm, OperandSize::S64);
|
||||||
|
emit(masm, RegImm::reg(reg), RegImm::imm(val), OperandSize::S64);
|
||||||
|
self.stack.push(Val::reg(reg));
|
||||||
|
} else {
|
||||||
|
let src = self.pop_to_reg(masm, OperandSize::S64);
|
||||||
|
let dst = self.pop_to_reg(masm, OperandSize::S64);
|
||||||
|
emit(masm, dst.into(), src.into(), OperandSize::S64);
|
||||||
|
self.regalloc.free_gpr(src);
|
||||||
|
self.stack.push(Val::reg(dst));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Spill locals and registers to memory.
|
||||||
|
// TODO optimize the spill range;
|
||||||
|
//
|
||||||
|
// At any point in the program, the stack
|
||||||
|
// might already contain Memory entries;
|
||||||
|
// we could effectively ignore that range;
|
||||||
|
// only focusing on the range that contains
|
||||||
|
// spillable values.
|
||||||
|
fn spill<M: MacroAssembler>(
|
||||||
|
stack: &mut Stack,
|
||||||
|
regalloc: &mut RegAlloc,
|
||||||
|
frame: &Frame,
|
||||||
|
masm: &mut M,
|
||||||
|
) {
|
||||||
|
stack.inner_mut().iter_mut().for_each(|v| match v {
|
||||||
|
Val::Reg(r) => {
|
||||||
|
let offset = masm.push(*r);
|
||||||
|
regalloc.free_gpr(*r);
|
||||||
|
*v = Val::Memory(offset);
|
||||||
|
}
|
||||||
|
Val::Local(index) => {
|
||||||
|
let slot = frame.get_local(*index).expect("valid local at slot");
|
||||||
|
let addr = masm.local_address(&slot);
|
||||||
|
masm.load(addr, regalloc.scratch, slot.ty.into());
|
||||||
|
let offset = masm.push(regalloc.scratch);
|
||||||
|
*v = Val::Memory(offset);
|
||||||
|
}
|
||||||
|
v => {
|
||||||
|
println!("trying to spill something unknown {:?}", v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,89 +1,12 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
abi::{ABISig, ABI},
|
abi::{ABISig, ABI},
|
||||||
frame::Frame,
|
masm::{MacroAssembler, OperandSize},
|
||||||
masm::{MacroAssembler, OperandSize, RegImm},
|
|
||||||
regalloc::RegAlloc,
|
|
||||||
stack::{Stack, Val},
|
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use wasmparser::{BinaryReader, FuncValidator, ValType, ValidatorResources, VisitOperator};
|
use wasmparser::{BinaryReader, FuncValidator, ValType, ValidatorResources, VisitOperator};
|
||||||
|
|
||||||
/// The code generation context.
|
mod context;
|
||||||
pub(crate) struct CodeGenContext<'a, M>
|
pub(crate) use context::*;
|
||||||
where
|
|
||||||
M: MacroAssembler,
|
|
||||||
{
|
|
||||||
pub masm: &'a mut M,
|
|
||||||
pub stack: Stack,
|
|
||||||
pub frame: &'a Frame,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, M> CodeGenContext<'a, M>
|
|
||||||
where
|
|
||||||
M: MacroAssembler,
|
|
||||||
{
|
|
||||||
/// Create a new code generation context.
|
|
||||||
pub fn new(masm: &'a mut M, stack: Stack, frame: &'a Frame) -> Self {
|
|
||||||
Self { masm, stack, frame }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prepares arguments for emitting an i32 binary operation.
|
|
||||||
pub fn i32_binop<F>(&mut self, regalloc: &mut RegAlloc, emit: &mut F)
|
|
||||||
where
|
|
||||||
F: FnMut(&mut M, RegImm, RegImm, OperandSize),
|
|
||||||
{
|
|
||||||
let top = self.stack.peek().expect("value at stack top");
|
|
||||||
|
|
||||||
if top.is_i32_const() {
|
|
||||||
let val = self
|
|
||||||
.stack
|
|
||||||
.pop_i32_const()
|
|
||||||
.expect("i32 const value at stack top");
|
|
||||||
let reg = regalloc.pop_to_reg(self, OperandSize::S32);
|
|
||||||
emit(
|
|
||||||
&mut self.masm,
|
|
||||||
RegImm::reg(reg),
|
|
||||||
RegImm::imm(val as i64),
|
|
||||||
OperandSize::S32,
|
|
||||||
);
|
|
||||||
self.stack.push(Val::reg(reg));
|
|
||||||
} else {
|
|
||||||
let src = regalloc.pop_to_reg(self, OperandSize::S32);
|
|
||||||
let dst = regalloc.pop_to_reg(self, OperandSize::S32);
|
|
||||||
emit(&mut self.masm, dst.into(), src.into(), OperandSize::S32);
|
|
||||||
regalloc.free_gpr(src);
|
|
||||||
self.stack.push(Val::reg(dst));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prepares arguments for emitting an i64 binary operation.
|
|
||||||
pub fn i64_binop<F>(&mut self, regalloc: &mut RegAlloc, emit: &mut F)
|
|
||||||
where
|
|
||||||
F: FnMut(&mut M, RegImm, RegImm, OperandSize),
|
|
||||||
{
|
|
||||||
let top = self.stack.peek().expect("value at stack top");
|
|
||||||
if top.is_i64_const() {
|
|
||||||
let val = self
|
|
||||||
.stack
|
|
||||||
.pop_i64_const()
|
|
||||||
.expect("i64 const value at stack top");
|
|
||||||
let reg = regalloc.pop_to_reg(self, OperandSize::S64);
|
|
||||||
emit(
|
|
||||||
&mut self.masm,
|
|
||||||
RegImm::reg(reg),
|
|
||||||
RegImm::imm(val),
|
|
||||||
OperandSize::S64,
|
|
||||||
);
|
|
||||||
self.stack.push(Val::reg(reg));
|
|
||||||
} else {
|
|
||||||
let src = regalloc.pop_to_reg(self, OperandSize::S64);
|
|
||||||
let dst = regalloc.pop_to_reg(self, OperandSize::S64);
|
|
||||||
emit(&mut self.masm, dst.into(), src.into(), OperandSize::S64);
|
|
||||||
regalloc.free_gpr(src);
|
|
||||||
self.stack.push(Val::reg(dst));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The code generation abstraction.
|
/// The code generation abstraction.
|
||||||
pub(crate) struct CodeGen<'a, M>
|
pub(crate) struct CodeGen<'a, M>
|
||||||
@@ -97,22 +20,22 @@ where
|
|||||||
sig: ABISig,
|
sig: ABISig,
|
||||||
|
|
||||||
/// The code generation context.
|
/// The code generation context.
|
||||||
pub context: CodeGenContext<'a, M>,
|
pub context: CodeGenContext<'a>,
|
||||||
|
|
||||||
/// The register allocator.
|
/// The MacroAssembler.
|
||||||
pub regalloc: RegAlloc,
|
pub masm: &'a mut M,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, M> CodeGen<'a, M>
|
impl<'a, M> CodeGen<'a, M>
|
||||||
where
|
where
|
||||||
M: MacroAssembler,
|
M: MacroAssembler,
|
||||||
{
|
{
|
||||||
pub fn new<A: ABI>(context: CodeGenContext<'a, M>, sig: ABISig, regalloc: RegAlloc) -> Self {
|
pub fn new<A: ABI>(masm: &'a mut M, context: CodeGenContext<'a>, sig: ABISig) -> Self {
|
||||||
Self {
|
Self {
|
||||||
word_size: <A as ABI>::word_bytes(),
|
word_size: <A as ABI>::word_bytes(),
|
||||||
sig,
|
sig,
|
||||||
context,
|
context,
|
||||||
regalloc,
|
masm,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,10 +54,8 @@ where
|
|||||||
|
|
||||||
// TODO stack checks
|
// TODO stack checks
|
||||||
fn emit_start(&mut self) -> Result<()> {
|
fn emit_start(&mut self) -> Result<()> {
|
||||||
self.context.masm.prologue();
|
self.masm.prologue();
|
||||||
self.context
|
self.masm.reserve_stack(self.context.frame.locals_size);
|
||||||
.masm
|
|
||||||
.reserve_stack(self.context.frame.locals_size);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,10 +66,10 @@ where
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.spill_register_arguments();
|
self.spill_register_arguments();
|
||||||
let defined_locals_range = &self.context.frame.defined_locals_range;
|
let defined_locals_range = &self.context.frame.defined_locals_range;
|
||||||
self.context.masm.zero_mem_range(
|
self.masm.zero_mem_range(
|
||||||
defined_locals_range.as_range(),
|
defined_locals_range.as_range(),
|
||||||
self.word_size,
|
self.word_size,
|
||||||
&mut self.regalloc,
|
&mut self.context.regalloc,
|
||||||
);
|
);
|
||||||
|
|
||||||
while !body.eof() {
|
while !body.eof() {
|
||||||
@@ -185,7 +106,7 @@ where
|
|||||||
// Emit the usual function end instruction sequence.
|
// Emit the usual function end instruction sequence.
|
||||||
pub fn emit_end(&mut self) -> Result<()> {
|
pub fn emit_end(&mut self) -> Result<()> {
|
||||||
self.handle_abi_result();
|
self.handle_abi_result();
|
||||||
self.context.masm.epilogue(self.context.frame.locals_size);
|
self.masm.epilogue(self.context.frame.locals_size);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,14 +127,14 @@ where
|
|||||||
.frame
|
.frame
|
||||||
.get_local(index as u32)
|
.get_local(index as u32)
|
||||||
.expect("valid local slot at location");
|
.expect("valid local slot at location");
|
||||||
let addr = self.context.masm.local_address(local);
|
let addr = self.masm.local_address(local);
|
||||||
let src = arg
|
let src = arg
|
||||||
.get_reg()
|
.get_reg()
|
||||||
.expect("arg should be associated to a register");
|
.expect("arg should be associated to a register");
|
||||||
|
|
||||||
match &ty {
|
match &ty {
|
||||||
ValType::I32 => self.context.masm.store(src.into(), addr, OperandSize::S32),
|
ValType::I32 => self.masm.store(src.into(), addr, OperandSize::S32),
|
||||||
ValType::I64 => self.context.masm.store(src.into(), addr, OperandSize::S64),
|
ValType::I64 => self.masm.store(src.into(), addr, OperandSize::S64),
|
||||||
_ => panic!("Unsupported type {:?}", ty),
|
_ => panic!("Unsupported type {:?}", ty),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -225,8 +146,8 @@ where
|
|||||||
}
|
}
|
||||||
let named_reg = self.sig.result.result_reg();
|
let named_reg = self.sig.result.result_reg();
|
||||||
let reg = self
|
let reg = self
|
||||||
.regalloc
|
.context
|
||||||
.pop_to_named_reg(&mut self.context, named_reg, OperandSize::S64);
|
.pop_to_named_reg(self.masm, named_reg, OperandSize::S64);
|
||||||
self.regalloc.free_gpr(reg);
|
self.context.regalloc.free_gpr(reg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,9 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
abi::local::LocalSlot,
|
abi::local::LocalSlot,
|
||||||
|
codegen::CodeGenContext,
|
||||||
isa::reg::Reg,
|
isa::reg::Reg,
|
||||||
masm::{MacroAssembler as Masm, OperandSize, RegImm},
|
masm::{DivKind, MacroAssembler as Masm, OperandSize, RegImm},
|
||||||
};
|
};
|
||||||
use cranelift_codegen::{settings, Final, MachBufferFinalized};
|
use cranelift_codegen::{settings, Final, MachBufferFinalized};
|
||||||
|
|
||||||
@@ -151,6 +152,10 @@ impl Masm for MacroAssembler {
|
|||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn div(&mut self, _context: &mut CodeGenContext, _kind: DivKind, _size: OperandSize) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
fn zero(&mut self, reg: Reg) {
|
fn zero(&mut self, reg: Reg) {
|
||||||
self.asm.load_constant(0, reg);
|
self.asm.load_constant(0, reg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ impl TargetIsa for Aarch64 {
|
|||||||
let frame = Frame::new(&abi_sig, &mut body, &mut validator, &abi)?;
|
let frame = Frame::new(&abi_sig, &mut body, &mut validator, &abi)?;
|
||||||
// TODO: Add floating point bitmask
|
// TODO: Add floating point bitmask
|
||||||
let regalloc = RegAlloc::new(RegSet::new(ALL_GPR, 0), scratch());
|
let regalloc = RegAlloc::new(RegSet::new(ALL_GPR, 0), scratch());
|
||||||
let codegen_context = CodeGenContext::new(&mut masm, stack, &frame);
|
let codegen_context = CodeGenContext::new(regalloc, stack, &frame);
|
||||||
let mut codegen = CodeGen::new::<abi::Aarch64ABI>(codegen_context, abi_sig, regalloc);
|
let mut codegen = CodeGen::new::<abi::Aarch64ABI>(&mut masm, codegen_context, abi_sig);
|
||||||
|
|
||||||
codegen.emit(&mut body, validator)?;
|
codegen.emit(&mut body, validator)?;
|
||||||
Ok(masm.finalize())
|
Ok(masm.finalize())
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
//! Assembler library implementation for x64.
|
//! Assembler library implementation for x64.
|
||||||
|
|
||||||
use crate::{isa::reg::Reg, masm::OperandSize};
|
use crate::{
|
||||||
|
isa::reg::Reg,
|
||||||
|
masm::{DivKind, OperandSize},
|
||||||
|
};
|
||||||
use cranelift_codegen::{
|
use cranelift_codegen::{
|
||||||
isa::x64::{
|
isa::x64::{
|
||||||
args::{
|
args::{
|
||||||
self, AluRmiROpcode, Amode, ExtMode, FromWritableReg, Gpr, GprMem, GprMemImm, RegMem,
|
self, AluRmiROpcode, Amode, DivOrRemKind, ExtMode, FromWritableReg, Gpr, GprMem,
|
||||||
RegMemImm, SyntheticAmode, WritableGpr,
|
GprMemImm, RegMem, RegMemImm, SyntheticAmode, WritableGpr,
|
||||||
},
|
},
|
||||||
settings as x64_settings, EmitInfo, EmitState, Inst,
|
settings as x64_settings, EmitInfo, EmitState, Inst,
|
||||||
},
|
},
|
||||||
@@ -61,6 +64,15 @@ impl From<OperandSize> for args::OperandSize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<DivKind> for DivOrRemKind {
|
||||||
|
fn from(kind: DivKind) -> Self {
|
||||||
|
match kind {
|
||||||
|
DivKind::Signed => DivOrRemKind::SignedDiv,
|
||||||
|
DivKind::Unsigned => DivOrRemKind::UnsignedDiv,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Low level assembler implementation for x64.
|
/// Low level assembler implementation for x64.
|
||||||
pub(crate) struct Assembler {
|
pub(crate) struct Assembler {
|
||||||
/// The machine instruction buffer.
|
/// The machine instruction buffer.
|
||||||
@@ -262,6 +274,31 @@ impl Assembler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Signed/unsigned division.
|
||||||
|
///
|
||||||
|
/// Emits a sequence of instructions to ensure the correctness of
|
||||||
|
/// the division invariants. This function assumes that the
|
||||||
|
/// caller has correctly allocated the dividend as `(rdx:rax)` and
|
||||||
|
/// accounted for the quotient to be stored in `rax`.
|
||||||
|
pub fn div(&mut self, divisor: Reg, dst: (Reg, Reg), kind: DivKind, size: OperandSize) {
|
||||||
|
let tmp = if size == OperandSize::S64 && kind == DivKind::Signed {
|
||||||
|
Some(regs::scratch())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
self.emit(Inst::CheckedDivOrRemSeq {
|
||||||
|
kind: kind.into(),
|
||||||
|
size: size.into(),
|
||||||
|
divisor: divisor.into(),
|
||||||
|
dividend_lo: dst.0.into(),
|
||||||
|
dividend_hi: dst.1.into(),
|
||||||
|
dst_quotient: dst.0.into(),
|
||||||
|
dst_remainder: dst.1.into(),
|
||||||
|
tmp: tmp.map(|reg| reg.into()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Multiply immediate and register.
|
/// Multiply immediate and register.
|
||||||
pub fn mul_ir(&mut self, imm: i32, dst: Reg, size: OperandSize) {
|
pub fn mul_ir(&mut self, imm: i32, dst: Reg, size: OperandSize) {
|
||||||
let imm = RegMemImm::imm(imm as u32);
|
let imm = RegMemImm::imm(imm as u32);
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use super::{
|
use super::{
|
||||||
address::Address,
|
address::Address,
|
||||||
asm::{Assembler, Operand},
|
asm::{Assembler, Operand},
|
||||||
regs::{rbp, rsp},
|
regs::{self, rbp, rsp},
|
||||||
};
|
};
|
||||||
use crate::abi::LocalSlot;
|
|
||||||
use crate::isa::reg::Reg;
|
use crate::isa::reg::Reg;
|
||||||
use crate::masm::{MacroAssembler as Masm, OperandSize, RegImm};
|
use crate::masm::{DivKind, MacroAssembler as Masm, OperandSize, RegImm};
|
||||||
|
use crate::{abi::LocalSlot, codegen::CodeGenContext, stack::Val};
|
||||||
use cranelift_codegen::{isa::x64::settings as x64_settings, settings, Final, MachBufferFinalized};
|
use cranelift_codegen::{isa::x64::settings as x64_settings, settings, Final, MachBufferFinalized};
|
||||||
|
|
||||||
/// x64 MacroAssembler.
|
/// x64 MacroAssembler.
|
||||||
@@ -152,6 +152,28 @@ impl Masm for MacroAssembler {
|
|||||||
self.asm.mul(src, dst, size);
|
self.asm.mul(src, dst, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn div(&mut self, context: &mut CodeGenContext, kind: DivKind, size: OperandSize) {
|
||||||
|
// Allocate rdx:rax.
|
||||||
|
let rdx = context.gpr(regs::rdx(), self);
|
||||||
|
let rax = context.gpr(regs::rax(), self);
|
||||||
|
|
||||||
|
// Allocate the divisor, which can be any gpr.
|
||||||
|
let divisor = context.pop_to_reg(self, size);
|
||||||
|
|
||||||
|
// Mark rax as allocatable.
|
||||||
|
context.regalloc.free_gpr(rax);
|
||||||
|
// Move the top value to rax.
|
||||||
|
let rax = context.pop_to_named_reg(self, rax, size);
|
||||||
|
self.asm.div(divisor, (rax, rdx), kind, size);
|
||||||
|
|
||||||
|
// Free the divisor and rdx.
|
||||||
|
context.free_gpr(divisor);
|
||||||
|
context.free_gpr(rdx);
|
||||||
|
|
||||||
|
// Push the quotient.
|
||||||
|
context.stack.push(Val::reg(rax));
|
||||||
|
}
|
||||||
|
|
||||||
fn epilogue(&mut self, locals_size: u32) {
|
fn epilogue(&mut self, locals_size: u32) {
|
||||||
assert!(self.sp_offset == locals_size);
|
assert!(self.sp_offset == locals_size);
|
||||||
|
|
||||||
|
|||||||
@@ -87,8 +87,8 @@ impl TargetIsa for X64 {
|
|||||||
let frame = Frame::new(&abi_sig, &mut body, &mut validator, &abi)?;
|
let frame = Frame::new(&abi_sig, &mut body, &mut validator, &abi)?;
|
||||||
// TODO Add in floating point bitmask
|
// TODO Add in floating point bitmask
|
||||||
let regalloc = RegAlloc::new(RegSet::new(ALL_GPR, 0), regs::scratch());
|
let regalloc = RegAlloc::new(RegSet::new(ALL_GPR, 0), regs::scratch());
|
||||||
let codegen_context = CodeGenContext::new(&mut masm, stack, &frame);
|
let codegen_context = CodeGenContext::new(regalloc, stack, &frame);
|
||||||
let mut codegen = CodeGen::new::<abi::X64ABI>(codegen_context, abi_sig, regalloc);
|
let mut codegen = CodeGen::new::<abi::X64ABI>(&mut masm, codegen_context, abi_sig);
|
||||||
|
|
||||||
codegen.emit(&mut body, validator)?;
|
codegen.emit(&mut body, validator)?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,18 @@
|
|||||||
use crate::abi::{align_to, LocalSlot};
|
use crate::abi::{align_to, LocalSlot};
|
||||||
|
use crate::codegen::CodeGenContext;
|
||||||
use crate::isa::reg::Reg;
|
use crate::isa::reg::Reg;
|
||||||
use crate::regalloc::RegAlloc;
|
use crate::regalloc::RegAlloc;
|
||||||
use cranelift_codegen::{Final, MachBufferFinalized};
|
use cranelift_codegen::{Final, MachBufferFinalized};
|
||||||
use std::{fmt::Debug, ops::Range};
|
use std::{fmt::Debug, ops::Range};
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq)]
|
||||||
|
pub(crate) enum DivKind {
|
||||||
|
/// Signed division.
|
||||||
|
Signed,
|
||||||
|
/// Unsigned division.
|
||||||
|
Unsigned,
|
||||||
|
}
|
||||||
|
|
||||||
/// Operand size, in bits.
|
/// Operand size, in bits.
|
||||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||||
pub(crate) enum OperandSize {
|
pub(crate) enum OperandSize {
|
||||||
@@ -94,6 +103,19 @@ pub(crate) trait MacroAssembler {
|
|||||||
/// Perform multiplication operation.
|
/// Perform multiplication operation.
|
||||||
fn mul(&mut self, dst: RegImm, lhs: RegImm, rhs: RegImm, size: OperandSize);
|
fn mul(&mut self, dst: RegImm, lhs: RegImm, rhs: RegImm, size: OperandSize);
|
||||||
|
|
||||||
|
/// Perform division operation.
|
||||||
|
/// Division is special in that some architectures have specific
|
||||||
|
/// expectations regarding the location of the instruction
|
||||||
|
/// arguments and regarding the location of the quotient /
|
||||||
|
/// remainder. To free the caller from having to deal with the
|
||||||
|
/// architecure specific contraints we give this function access
|
||||||
|
/// to the code generation context, allowing each implementation
|
||||||
|
/// to decide the lowering path. For cases in which division is a
|
||||||
|
/// unconstrained binary operation, the caller can decide to use
|
||||||
|
/// the `CodeGenContext::i32_binop` or `CodeGenContext::i64_binop`
|
||||||
|
/// functions.
|
||||||
|
fn div(&mut self, context: &mut CodeGenContext, kind: DivKind, size: OperandSize);
|
||||||
|
|
||||||
/// Push the register to the stack, returning the offset.
|
/// Push the register to the stack, returning the offset.
|
||||||
fn push(&mut self, src: Reg) -> u32;
|
fn push(&mut self, src: Reg) -> u32;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
use crate::{
|
use crate::{isa::reg::Reg, regset::RegSet};
|
||||||
codegen::CodeGenContext,
|
|
||||||
frame::Frame,
|
|
||||||
isa::reg::Reg,
|
|
||||||
masm::{MacroAssembler, OperandSize, RegImm},
|
|
||||||
regset::RegSet,
|
|
||||||
stack::Val,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The register allocator.
|
/// The register allocator.
|
||||||
///
|
///
|
||||||
@@ -30,79 +23,26 @@ impl RegAlloc {
|
|||||||
Self { regset, scratch }
|
Self { regset, scratch }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads the stack top value into a register, if it isn't already one;
|
|
||||||
/// spilling if there are no registers available.
|
|
||||||
pub fn pop_to_reg<M: MacroAssembler>(
|
|
||||||
&mut self,
|
|
||||||
context: &mut CodeGenContext<M>,
|
|
||||||
size: OperandSize,
|
|
||||||
) -> Reg {
|
|
||||||
if let Some(reg) = context.stack.pop_reg() {
|
|
||||||
return reg;
|
|
||||||
}
|
|
||||||
|
|
||||||
let dst = self.any_gpr(context);
|
|
||||||
let val = context.stack.pop().expect("a value at stack top");
|
|
||||||
Self::move_val_to_reg(val, dst, context.masm, context.frame, size);
|
|
||||||
dst
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if the stack top contains the given register. The register
|
|
||||||
/// gets allocated otherwise, potentially causing a spill.
|
|
||||||
/// Once the requested register is allocated, the value at the top of the stack
|
|
||||||
/// gets loaded into the register.
|
|
||||||
pub fn pop_to_named_reg<M: MacroAssembler>(
|
|
||||||
&mut self,
|
|
||||||
context: &mut CodeGenContext<M>,
|
|
||||||
named: Reg,
|
|
||||||
size: OperandSize,
|
|
||||||
) -> Reg {
|
|
||||||
if let Some(reg) = context.stack.pop_named_reg(named) {
|
|
||||||
return reg;
|
|
||||||
}
|
|
||||||
|
|
||||||
let dst = self.gpr(context, named);
|
|
||||||
let val = context.stack.pop().expect("a value at stack top");
|
|
||||||
Self::move_val_to_reg(val, dst, context.masm, context.frame, size);
|
|
||||||
dst
|
|
||||||
}
|
|
||||||
|
|
||||||
fn move_val_to_reg<M: MacroAssembler>(
|
|
||||||
src: Val,
|
|
||||||
dst: Reg,
|
|
||||||
masm: &mut M,
|
|
||||||
frame: &Frame,
|
|
||||||
size: OperandSize,
|
|
||||||
) {
|
|
||||||
match src {
|
|
||||||
Val::Reg(src) => masm.mov(RegImm::reg(src), RegImm::reg(dst), size),
|
|
||||||
Val::I32(imm) => masm.mov(RegImm::imm(imm.into()), RegImm::reg(dst), size),
|
|
||||||
Val::I64(imm) => masm.mov(RegImm::imm(imm), RegImm::reg(dst), size),
|
|
||||||
Val::Local(index) => {
|
|
||||||
let slot = frame
|
|
||||||
.get_local(index)
|
|
||||||
.expect(&format!("valid locat at index = {}", index));
|
|
||||||
let addr = masm.local_address(&slot);
|
|
||||||
masm.load(addr, dst, slot.ty.into());
|
|
||||||
}
|
|
||||||
v => panic!("Unsupported value {:?}", v),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allocate the next available general purpose register,
|
/// Allocate the next available general purpose register,
|
||||||
/// spilling if none available.
|
/// spilling if none available.
|
||||||
pub fn any_gpr<M: MacroAssembler>(&mut self, context: &mut CodeGenContext<M>) -> Reg {
|
pub fn any_gpr<F>(&mut self, spill: &mut F) -> Reg
|
||||||
|
where
|
||||||
|
F: FnMut(&mut RegAlloc),
|
||||||
|
{
|
||||||
self.regset.any_gpr().unwrap_or_else(|| {
|
self.regset.any_gpr().unwrap_or_else(|| {
|
||||||
self.spill(context);
|
spill(self);
|
||||||
self.regset.any_gpr().expect("any gpr to be available")
|
self.regset.any_gpr().expect("any gpr to be available")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request a specific general purpose register,
|
/// Request a specific general purpose register,
|
||||||
/// spilling if not available.
|
/// spilling if not available.
|
||||||
pub fn gpr<M: MacroAssembler>(&mut self, context: &mut CodeGenContext<M>, named: Reg) -> Reg {
|
pub fn gpr<F>(&mut self, named: Reg, spill: &mut F) -> Reg
|
||||||
|
where
|
||||||
|
F: FnMut(&mut RegAlloc),
|
||||||
|
{
|
||||||
self.regset.gpr(named).unwrap_or_else(|| {
|
self.regset.gpr(named).unwrap_or_else(|| {
|
||||||
self.spill(context);
|
spill(self);
|
||||||
self.regset
|
self.regset
|
||||||
.gpr(named)
|
.gpr(named)
|
||||||
.expect(&format!("gpr {:?} to be available", named))
|
.expect(&format!("gpr {:?} to be available", named))
|
||||||
@@ -113,33 +53,4 @@ impl RegAlloc {
|
|||||||
pub fn free_gpr(&mut self, reg: Reg) {
|
pub fn free_gpr(&mut self, reg: Reg) {
|
||||||
self.regset.free_gpr(reg);
|
self.regset.free_gpr(reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spill locals and registers to memory.
|
|
||||||
// TODO optimize the spill range;
|
|
||||||
//
|
|
||||||
// At any point in the program, the stack
|
|
||||||
// might already contain Memory entries;
|
|
||||||
// we could effectively ignore that range;
|
|
||||||
// only focusing on the range that contains
|
|
||||||
// spillable values.
|
|
||||||
fn spill<M: MacroAssembler>(&mut self, context: &mut CodeGenContext<M>) {
|
|
||||||
context.stack.inner_mut().iter_mut().for_each(|v| match v {
|
|
||||||
Val::Reg(r) => {
|
|
||||||
let offset = context.masm.push(*r);
|
|
||||||
self.free_gpr(*r);
|
|
||||||
*v = Val::Memory(offset);
|
|
||||||
}
|
|
||||||
Val::Local(index) => {
|
|
||||||
let slot = context
|
|
||||||
.frame
|
|
||||||
.get_local(*index)
|
|
||||||
.expect("valid local at slot");
|
|
||||||
let addr = context.masm.local_address(&slot);
|
|
||||||
context.masm.load(addr, self.scratch, slot.ty.into());
|
|
||||||
let offset = context.masm.push(self.scratch);
|
|
||||||
*v = Val::Memory(offset);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
//! machine code emitter.
|
//! machine code emitter.
|
||||||
|
|
||||||
use crate::codegen::CodeGen;
|
use crate::codegen::CodeGen;
|
||||||
use crate::masm::{MacroAssembler, OperandSize, RegImm};
|
use crate::masm::{DivKind, MacroAssembler, OperandSize, RegImm};
|
||||||
use crate::stack::Val;
|
use crate::stack::Val;
|
||||||
use wasmparser::ValType;
|
use wasmparser::ValType;
|
||||||
use wasmparser::VisitOperator;
|
use wasmparser::VisitOperator;
|
||||||
@@ -37,6 +37,10 @@ macro_rules! def_unsupported {
|
|||||||
(emit I64Add $($rest:tt)*) => {};
|
(emit I64Add $($rest:tt)*) => {};
|
||||||
(emit I32Sub $($rest:tt)*) => {};
|
(emit I32Sub $($rest:tt)*) => {};
|
||||||
(emit I32Mul $($rest:tt)*) => {};
|
(emit I32Mul $($rest:tt)*) => {};
|
||||||
|
(emit I32DivS $($rest:tt)*) => {};
|
||||||
|
(emit I32DivU $($rest:tt)*) => {};
|
||||||
|
(emit I64DivS $($rest:tt)*) => {};
|
||||||
|
(emit I64DivU $($rest:tt)*) => {};
|
||||||
(emit I64Mul $($rest:tt)*) => {};
|
(emit I64Mul $($rest:tt)*) => {};
|
||||||
(emit I64Sub $($rest:tt)*) => {};
|
(emit I64Sub $($rest:tt)*) => {};
|
||||||
(emit LocalGet $($rest:tt)*) => {};
|
(emit LocalGet $($rest:tt)*) => {};
|
||||||
@@ -62,46 +66,74 @@ where
|
|||||||
|
|
||||||
fn visit_i32_add(&mut self) {
|
fn visit_i32_add(&mut self) {
|
||||||
self.context
|
self.context
|
||||||
.i32_binop(&mut self.regalloc, &mut |masm: &mut M, dst, src, size| {
|
.i32_binop(self.masm, &mut |masm, dst, src, size| {
|
||||||
masm.add(dst, dst, src, size);
|
masm.add(dst, dst, src, size);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_i64_add(&mut self) {
|
fn visit_i64_add(&mut self) {
|
||||||
self.context
|
self.context
|
||||||
.i64_binop(&mut self.regalloc, &mut |masm: &mut M, dst, src, size| {
|
.i64_binop(self.masm, &mut |masm, dst, src, size| {
|
||||||
masm.add(dst, dst, src, size);
|
masm.add(dst, dst, src, size);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_i32_sub(&mut self) {
|
fn visit_i32_sub(&mut self) {
|
||||||
self.context
|
self.context
|
||||||
.i32_binop(&mut self.regalloc, &mut |masm: &mut M, dst, src, size| {
|
.i32_binop(self.masm, &mut |masm, dst, src, size| {
|
||||||
masm.sub(dst, dst, src, size);
|
masm.sub(dst, dst, src, size);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_i64_sub(&mut self) {
|
fn visit_i64_sub(&mut self) {
|
||||||
self.context
|
self.context
|
||||||
.i64_binop(&mut self.regalloc, &mut |masm: &mut M, dst, src, size| {
|
.i64_binop(self.masm, &mut |masm, dst, src, size| {
|
||||||
masm.sub(dst, dst, src, size);
|
masm.sub(dst, dst, src, size);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_i32_mul(&mut self) {
|
fn visit_i32_mul(&mut self) {
|
||||||
self.context
|
self.context
|
||||||
.i32_binop(&mut self.regalloc, &mut |masm: &mut M, dst, src, size| {
|
.i32_binop(self.masm, &mut |masm, dst, src, size| {
|
||||||
masm.mul(dst, dst, src, size);
|
masm.mul(dst, dst, src, size);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_i64_mul(&mut self) {
|
fn visit_i64_mul(&mut self) {
|
||||||
self.context
|
self.context
|
||||||
.i64_binop(&mut self.regalloc, &mut |masm: &mut M, dst, src, size| {
|
.i64_binop(self.masm, &mut |masm, dst, src, size| {
|
||||||
masm.mul(dst, dst, src, size);
|
masm.mul(dst, dst, src, size);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visit_i32_div_s(&mut self) {
|
||||||
|
use DivKind::*;
|
||||||
|
use OperandSize::*;
|
||||||
|
|
||||||
|
self.masm.div(&mut self.context, Signed, S32);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_i32_div_u(&mut self) {
|
||||||
|
use DivKind::*;
|
||||||
|
use OperandSize::*;
|
||||||
|
|
||||||
|
self.masm.div(&mut self.context, Unsigned, S32);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_i64_div_s(&mut self) {
|
||||||
|
use DivKind::*;
|
||||||
|
use OperandSize::*;
|
||||||
|
|
||||||
|
self.masm.div(&mut self.context, Signed, S64);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_i64_div_u(&mut self) {
|
||||||
|
use DivKind::*;
|
||||||
|
use OperandSize::*;
|
||||||
|
|
||||||
|
self.masm.div(&mut self.context, Unsigned, S64);
|
||||||
|
}
|
||||||
|
|
||||||
fn visit_end(&mut self) {}
|
fn visit_end(&mut self) {}
|
||||||
|
|
||||||
fn visit_local_get(&mut self, index: u32) {
|
fn visit_local_get(&mut self, index: u32) {
|
||||||
@@ -124,10 +156,10 @@ where
|
|||||||
.get_local(index)
|
.get_local(index)
|
||||||
.expect(&format!("vald local at slot = {}", index));
|
.expect(&format!("vald local at slot = {}", index));
|
||||||
let size: OperandSize = slot.ty.into();
|
let size: OperandSize = slot.ty.into();
|
||||||
let src = self.regalloc.pop_to_reg(context, size);
|
let src = self.context.pop_to_reg(self.masm, size);
|
||||||
let addr = context.masm.local_address(&slot);
|
let addr = self.masm.local_address(&slot);
|
||||||
context.masm.store(RegImm::reg(src), addr, size);
|
self.masm.store(RegImm::reg(src), addr, size);
|
||||||
self.regalloc.free_gpr(src);
|
self.context.regalloc.free_gpr(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
wasmparser::for_each_operator!(def_unsupported);
|
wasmparser::for_each_operator!(def_unsupported);
|
||||||
|
|||||||
25
winch/filetests/filetests/x64/i32_divs/const.wat
Normal file
25
winch/filetests/filetests/x64/i32_divs/const.wat
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (result i32)
|
||||||
|
(i32.const 20)
|
||||||
|
(i32.const 10)
|
||||||
|
(i32.div_s)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: b90a000000 mov ecx, 0xa
|
||||||
|
;; 9: b814000000 mov eax, 0x14
|
||||||
|
;; e: 83f900 cmp ecx, 0
|
||||||
|
;; 11: 0f8502000000 jne 0x19
|
||||||
|
;; 17: 0f0b ud2
|
||||||
|
;; 19: 83f9ff cmp ecx, -1
|
||||||
|
;; 1c: 0f850e000000 jne 0x30
|
||||||
|
;; 22: 81f800000080 cmp eax, 0x80000000
|
||||||
|
;; 28: 0f8502000000 jne 0x30
|
||||||
|
;; 2e: 0f0b ud2
|
||||||
|
;; 30: 99 cdq
|
||||||
|
;; 31: f7f9 idiv ecx
|
||||||
|
;; 33: 5d pop rbp
|
||||||
|
;; 34: c3 ret
|
||||||
25
winch/filetests/filetests/x64/i32_divs/one_zero.wat
Normal file
25
winch/filetests/filetests/x64/i32_divs/one_zero.wat
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (result i32)
|
||||||
|
(i32.const 1)
|
||||||
|
(i32.const 0)
|
||||||
|
(i32.div_s)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: b900000000 mov ecx, 0
|
||||||
|
;; 9: b801000000 mov eax, 1
|
||||||
|
;; e: 83f900 cmp ecx, 0
|
||||||
|
;; 11: 0f8502000000 jne 0x19
|
||||||
|
;; 17: 0f0b ud2
|
||||||
|
;; 19: 83f9ff cmp ecx, -1
|
||||||
|
;; 1c: 0f850e000000 jne 0x30
|
||||||
|
;; 22: 81f800000080 cmp eax, 0x80000000
|
||||||
|
;; 28: 0f8502000000 jne 0x30
|
||||||
|
;; 2e: 0f0b ud2
|
||||||
|
;; 30: 99 cdq
|
||||||
|
;; 31: f7f9 idiv ecx
|
||||||
|
;; 33: 5d pop rbp
|
||||||
|
;; 34: c3 ret
|
||||||
25
winch/filetests/filetests/x64/i32_divs/overflow.wat
Normal file
25
winch/filetests/filetests/x64/i32_divs/overflow.wat
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (result i32)
|
||||||
|
(i32.const 0x80000000)
|
||||||
|
(i32.const -1)
|
||||||
|
(i32.div_s)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: b9ffffffff mov ecx, 0xffffffff
|
||||||
|
;; 9: b800000080 mov eax, 0x80000000
|
||||||
|
;; e: 83f900 cmp ecx, 0
|
||||||
|
;; 11: 0f8502000000 jne 0x19
|
||||||
|
;; 17: 0f0b ud2
|
||||||
|
;; 19: 83f9ff cmp ecx, -1
|
||||||
|
;; 1c: 0f850e000000 jne 0x30
|
||||||
|
;; 22: 81f800000080 cmp eax, 0x80000000
|
||||||
|
;; 28: 0f8502000000 jne 0x30
|
||||||
|
;; 2e: 0f0b ud2
|
||||||
|
;; 30: 99 cdq
|
||||||
|
;; 31: f7f9 idiv ecx
|
||||||
|
;; 33: 5d pop rbp
|
||||||
|
;; 34: c3 ret
|
||||||
29
winch/filetests/filetests/x64/i32_divs/params.wat
Normal file
29
winch/filetests/filetests/x64/i32_divs/params.wat
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (param i32) (param i32) (result i32)
|
||||||
|
(local.get 0)
|
||||||
|
(local.get 1)
|
||||||
|
(i32.div_s)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: 4883ec08 sub rsp, 8
|
||||||
|
;; 8: 897c2404 mov dword ptr [rsp + 4], edi
|
||||||
|
;; c: 893424 mov dword ptr [rsp], esi
|
||||||
|
;; f: 8b0c24 mov ecx, dword ptr [rsp]
|
||||||
|
;; 12: 8b442404 mov eax, dword ptr [rsp + 4]
|
||||||
|
;; 16: 83f900 cmp ecx, 0
|
||||||
|
;; 19: 0f8502000000 jne 0x21
|
||||||
|
;; 1f: 0f0b ud2
|
||||||
|
;; 21: 83f9ff cmp ecx, -1
|
||||||
|
;; 24: 0f850e000000 jne 0x38
|
||||||
|
;; 2a: 81f800000080 cmp eax, 0x80000000
|
||||||
|
;; 30: 0f8502000000 jne 0x38
|
||||||
|
;; 36: 0f0b ud2
|
||||||
|
;; 38: 99 cdq
|
||||||
|
;; 39: f7f9 idiv ecx
|
||||||
|
;; 3b: 4883c408 add rsp, 8
|
||||||
|
;; 3f: 5d pop rbp
|
||||||
|
;; 40: c3 ret
|
||||||
25
winch/filetests/filetests/x64/i32_divs/zero_zero.wat
Normal file
25
winch/filetests/filetests/x64/i32_divs/zero_zero.wat
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (result i32)
|
||||||
|
(i32.const 0)
|
||||||
|
(i32.const 0)
|
||||||
|
(i32.div_s)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: b900000000 mov ecx, 0
|
||||||
|
;; 9: b800000000 mov eax, 0
|
||||||
|
;; e: 83f900 cmp ecx, 0
|
||||||
|
;; 11: 0f8502000000 jne 0x19
|
||||||
|
;; 17: 0f0b ud2
|
||||||
|
;; 19: 83f9ff cmp ecx, -1
|
||||||
|
;; 1c: 0f850e000000 jne 0x30
|
||||||
|
;; 22: 81f800000080 cmp eax, 0x80000000
|
||||||
|
;; 28: 0f8502000000 jne 0x30
|
||||||
|
;; 2e: 0f0b ud2
|
||||||
|
;; 30: 99 cdq
|
||||||
|
;; 31: f7f9 idiv ecx
|
||||||
|
;; 33: 5d pop rbp
|
||||||
|
;; 34: c3 ret
|
||||||
20
winch/filetests/filetests/x64/i32_divu/const.wat
Normal file
20
winch/filetests/filetests/x64/i32_divu/const.wat
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (result i32)
|
||||||
|
(i32.const 20)
|
||||||
|
(i32.const 10)
|
||||||
|
(i32.div_u)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: b90a000000 mov ecx, 0xa
|
||||||
|
;; 9: b814000000 mov eax, 0x14
|
||||||
|
;; e: 83f900 cmp ecx, 0
|
||||||
|
;; 11: 0f8502000000 jne 0x19
|
||||||
|
;; 17: 0f0b ud2
|
||||||
|
;; 19: ba00000000 mov edx, 0
|
||||||
|
;; 1e: f7f1 div ecx
|
||||||
|
;; 20: 5d pop rbp
|
||||||
|
;; 21: c3 ret
|
||||||
20
winch/filetests/filetests/x64/i32_divu/one_zero.wat
Normal file
20
winch/filetests/filetests/x64/i32_divu/one_zero.wat
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (result i32)
|
||||||
|
(i32.const 1)
|
||||||
|
(i32.const 0)
|
||||||
|
(i32.div_u)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: b900000000 mov ecx, 0
|
||||||
|
;; 9: b801000000 mov eax, 1
|
||||||
|
;; e: 83f900 cmp ecx, 0
|
||||||
|
;; 11: 0f8502000000 jne 0x19
|
||||||
|
;; 17: 0f0b ud2
|
||||||
|
;; 19: ba00000000 mov edx, 0
|
||||||
|
;; 1e: f7f1 div ecx
|
||||||
|
;; 20: 5d pop rbp
|
||||||
|
;; 21: c3 ret
|
||||||
24
winch/filetests/filetests/x64/i32_divu/params.wat
Normal file
24
winch/filetests/filetests/x64/i32_divu/params.wat
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (param i32) (param i32) (result i32)
|
||||||
|
(local.get 0)
|
||||||
|
(local.get 1)
|
||||||
|
(i32.div_u)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: 4883ec08 sub rsp, 8
|
||||||
|
;; 8: 897c2404 mov dword ptr [rsp + 4], edi
|
||||||
|
;; c: 893424 mov dword ptr [rsp], esi
|
||||||
|
;; f: 8b0c24 mov ecx, dword ptr [rsp]
|
||||||
|
;; 12: 8b442404 mov eax, dword ptr [rsp + 4]
|
||||||
|
;; 16: 83f900 cmp ecx, 0
|
||||||
|
;; 19: 0f8502000000 jne 0x21
|
||||||
|
;; 1f: 0f0b ud2
|
||||||
|
;; 21: ba00000000 mov edx, 0
|
||||||
|
;; 26: f7f1 div ecx
|
||||||
|
;; 28: 4883c408 add rsp, 8
|
||||||
|
;; 2c: 5d pop rbp
|
||||||
|
;; 2d: c3 ret
|
||||||
20
winch/filetests/filetests/x64/i32_divu/signed.wat
Normal file
20
winch/filetests/filetests/x64/i32_divu/signed.wat
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (result i32)
|
||||||
|
(i32.const -1)
|
||||||
|
(i32.const -1)
|
||||||
|
(i32.div_u)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: b9ffffffff mov ecx, 0xffffffff
|
||||||
|
;; 9: b8ffffffff mov eax, 0xffffffff
|
||||||
|
;; e: 83f900 cmp ecx, 0
|
||||||
|
;; 11: 0f8502000000 jne 0x19
|
||||||
|
;; 17: 0f0b ud2
|
||||||
|
;; 19: ba00000000 mov edx, 0
|
||||||
|
;; 1e: f7f1 div ecx
|
||||||
|
;; 20: 5d pop rbp
|
||||||
|
;; 21: c3 ret
|
||||||
20
winch/filetests/filetests/x64/i32_divu/zero_zero.wat
Normal file
20
winch/filetests/filetests/x64/i32_divu/zero_zero.wat
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (result i32)
|
||||||
|
(i32.const 0)
|
||||||
|
(i32.const 0)
|
||||||
|
(i32.div_u)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: b900000000 mov ecx, 0
|
||||||
|
;; 9: b800000000 mov eax, 0
|
||||||
|
;; e: 83f900 cmp ecx, 0
|
||||||
|
;; 11: 0f8502000000 jne 0x19
|
||||||
|
;; 17: 0f0b ud2
|
||||||
|
;; 19: ba00000000 mov edx, 0
|
||||||
|
;; 1e: f7f1 div ecx
|
||||||
|
;; 20: 5d pop rbp
|
||||||
|
;; 21: c3 ret
|
||||||
27
winch/filetests/filetests/x64/i64_divs/const.wat
Normal file
27
winch/filetests/filetests/x64/i64_divs/const.wat
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (result i64)
|
||||||
|
(i64.const 20)
|
||||||
|
(i64.const 10)
|
||||||
|
(i64.div_s)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: 48c7c10a000000 mov rcx, 0xa
|
||||||
|
;; b: 48c7c014000000 mov rax, 0x14
|
||||||
|
;; 12: 4883f900 cmp rcx, 0
|
||||||
|
;; 16: 0f8502000000 jne 0x1e
|
||||||
|
;; 1c: 0f0b ud2
|
||||||
|
;; 1e: 4883f9ff cmp rcx, -1
|
||||||
|
;; 22: 0f8515000000 jne 0x3d
|
||||||
|
;; 28: 49bb0000000000000080
|
||||||
|
;; movabs r11, 0x8000000000000000
|
||||||
|
;; 32: 4c39d8 cmp rax, r11
|
||||||
|
;; 35: 0f8502000000 jne 0x3d
|
||||||
|
;; 3b: 0f0b ud2
|
||||||
|
;; 3d: 4899 cqo
|
||||||
|
;; 3f: 48f7f9 idiv rcx
|
||||||
|
;; 42: 5d pop rbp
|
||||||
|
;; 43: c3 ret
|
||||||
27
winch/filetests/filetests/x64/i64_divs/one_zero.wat
Normal file
27
winch/filetests/filetests/x64/i64_divs/one_zero.wat
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (result i64)
|
||||||
|
(i64.const 1)
|
||||||
|
(i64.const 0)
|
||||||
|
(i64.div_s)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: 48c7c100000000 mov rcx, 0
|
||||||
|
;; b: 48c7c001000000 mov rax, 1
|
||||||
|
;; 12: 4883f900 cmp rcx, 0
|
||||||
|
;; 16: 0f8502000000 jne 0x1e
|
||||||
|
;; 1c: 0f0b ud2
|
||||||
|
;; 1e: 4883f9ff cmp rcx, -1
|
||||||
|
;; 22: 0f8515000000 jne 0x3d
|
||||||
|
;; 28: 49bb0000000000000080
|
||||||
|
;; movabs r11, 0x8000000000000000
|
||||||
|
;; 32: 4c39d8 cmp rax, r11
|
||||||
|
;; 35: 0f8502000000 jne 0x3d
|
||||||
|
;; 3b: 0f0b ud2
|
||||||
|
;; 3d: 4899 cqo
|
||||||
|
;; 3f: 48f7f9 idiv rcx
|
||||||
|
;; 42: 5d pop rbp
|
||||||
|
;; 43: c3 ret
|
||||||
28
winch/filetests/filetests/x64/i64_divs/overflow.wat
Normal file
28
winch/filetests/filetests/x64/i64_divs/overflow.wat
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (result i64)
|
||||||
|
(i64.const 0x8000000000000000)
|
||||||
|
(i64.const -1)
|
||||||
|
(i64.div_s)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: 48c7c1ffffffff mov rcx, 0xffffffffffffffff
|
||||||
|
;; b: 48b80000000000000080
|
||||||
|
;; movabs rax, 0x8000000000000000
|
||||||
|
;; 15: 4883f900 cmp rcx, 0
|
||||||
|
;; 19: 0f8502000000 jne 0x21
|
||||||
|
;; 1f: 0f0b ud2
|
||||||
|
;; 21: 4883f9ff cmp rcx, -1
|
||||||
|
;; 25: 0f8515000000 jne 0x40
|
||||||
|
;; 2b: 49bb0000000000000080
|
||||||
|
;; movabs r11, 0x8000000000000000
|
||||||
|
;; 35: 4c39d8 cmp rax, r11
|
||||||
|
;; 38: 0f8502000000 jne 0x40
|
||||||
|
;; 3e: 0f0b ud2
|
||||||
|
;; 40: 4899 cqo
|
||||||
|
;; 42: 48f7f9 idiv rcx
|
||||||
|
;; 45: 5d pop rbp
|
||||||
|
;; 46: c3 ret
|
||||||
31
winch/filetests/filetests/x64/i64_divs/params.wat
Normal file
31
winch/filetests/filetests/x64/i64_divs/params.wat
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (param i64) (param i64) (result i64)
|
||||||
|
(local.get 0)
|
||||||
|
(local.get 1)
|
||||||
|
(i64.div_s)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: 4883ec10 sub rsp, 0x10
|
||||||
|
;; 8: 48897c2408 mov qword ptr [rsp + 8], rdi
|
||||||
|
;; d: 48893424 mov qword ptr [rsp], rsi
|
||||||
|
;; 11: 488b0c24 mov rcx, qword ptr [rsp]
|
||||||
|
;; 15: 488b442408 mov rax, qword ptr [rsp + 8]
|
||||||
|
;; 1a: 4883f900 cmp rcx, 0
|
||||||
|
;; 1e: 0f8502000000 jne 0x26
|
||||||
|
;; 24: 0f0b ud2
|
||||||
|
;; 26: 4883f9ff cmp rcx, -1
|
||||||
|
;; 2a: 0f8515000000 jne 0x45
|
||||||
|
;; 30: 49bb0000000000000080
|
||||||
|
;; movabs r11, 0x8000000000000000
|
||||||
|
;; 3a: 4c39d8 cmp rax, r11
|
||||||
|
;; 3d: 0f8502000000 jne 0x45
|
||||||
|
;; 43: 0f0b ud2
|
||||||
|
;; 45: 4899 cqo
|
||||||
|
;; 47: 48f7f9 idiv rcx
|
||||||
|
;; 4a: 4883c410 add rsp, 0x10
|
||||||
|
;; 4e: 5d pop rbp
|
||||||
|
;; 4f: c3 ret
|
||||||
27
winch/filetests/filetests/x64/i64_divs/zero_zero.wat
Normal file
27
winch/filetests/filetests/x64/i64_divs/zero_zero.wat
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (result i64)
|
||||||
|
(i64.const 0)
|
||||||
|
(i64.const 0)
|
||||||
|
(i64.div_s)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: 48c7c100000000 mov rcx, 0
|
||||||
|
;; b: 48c7c000000000 mov rax, 0
|
||||||
|
;; 12: 4883f900 cmp rcx, 0
|
||||||
|
;; 16: 0f8502000000 jne 0x1e
|
||||||
|
;; 1c: 0f0b ud2
|
||||||
|
;; 1e: 4883f9ff cmp rcx, -1
|
||||||
|
;; 22: 0f8515000000 jne 0x3d
|
||||||
|
;; 28: 49bb0000000000000080
|
||||||
|
;; movabs r11, 0x8000000000000000
|
||||||
|
;; 32: 4c39d8 cmp rax, r11
|
||||||
|
;; 35: 0f8502000000 jne 0x3d
|
||||||
|
;; 3b: 0f0b ud2
|
||||||
|
;; 3d: 4899 cqo
|
||||||
|
;; 3f: 48f7f9 idiv rcx
|
||||||
|
;; 42: 5d pop rbp
|
||||||
|
;; 43: c3 ret
|
||||||
20
winch/filetests/filetests/x64/i64_divu/const.wat
Normal file
20
winch/filetests/filetests/x64/i64_divu/const.wat
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (result i64)
|
||||||
|
(i64.const 20)
|
||||||
|
(i64.const 10)
|
||||||
|
(i64.div_u)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: 48c7c10a000000 mov rcx, 0xa
|
||||||
|
;; b: 48c7c014000000 mov rax, 0x14
|
||||||
|
;; 12: 4883f900 cmp rcx, 0
|
||||||
|
;; 16: 0f8502000000 jne 0x1e
|
||||||
|
;; 1c: 0f0b ud2
|
||||||
|
;; 1e: ba00000000 mov edx, 0
|
||||||
|
;; 23: 48f7f1 div rcx
|
||||||
|
;; 26: 5d pop rbp
|
||||||
|
;; 27: c3 ret
|
||||||
20
winch/filetests/filetests/x64/i64_divu/one_zero.wat
Normal file
20
winch/filetests/filetests/x64/i64_divu/one_zero.wat
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (result i64)
|
||||||
|
(i64.const 1)
|
||||||
|
(i64.const 0)
|
||||||
|
(i64.div_u)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: 48c7c100000000 mov rcx, 0
|
||||||
|
;; b: 48c7c001000000 mov rax, 1
|
||||||
|
;; 12: 4883f900 cmp rcx, 0
|
||||||
|
;; 16: 0f8502000000 jne 0x1e
|
||||||
|
;; 1c: 0f0b ud2
|
||||||
|
;; 1e: ba00000000 mov edx, 0
|
||||||
|
;; 23: 48f7f1 div rcx
|
||||||
|
;; 26: 5d pop rbp
|
||||||
|
;; 27: c3 ret
|
||||||
24
winch/filetests/filetests/x64/i64_divu/params.wat
Normal file
24
winch/filetests/filetests/x64/i64_divu/params.wat
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (param i64) (param i64) (result i64)
|
||||||
|
(local.get 0)
|
||||||
|
(local.get 1)
|
||||||
|
(i64.div_u)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: 4883ec10 sub rsp, 0x10
|
||||||
|
;; 8: 48897c2408 mov qword ptr [rsp + 8], rdi
|
||||||
|
;; d: 48893424 mov qword ptr [rsp], rsi
|
||||||
|
;; 11: 488b0c24 mov rcx, qword ptr [rsp]
|
||||||
|
;; 15: 488b442408 mov rax, qword ptr [rsp + 8]
|
||||||
|
;; 1a: 4883f900 cmp rcx, 0
|
||||||
|
;; 1e: 0f8502000000 jne 0x26
|
||||||
|
;; 24: 0f0b ud2
|
||||||
|
;; 26: ba00000000 mov edx, 0
|
||||||
|
;; 2b: 48f7f1 div rcx
|
||||||
|
;; 2e: 4883c410 add rsp, 0x10
|
||||||
|
;; 32: 5d pop rbp
|
||||||
|
;; 33: c3 ret
|
||||||
20
winch/filetests/filetests/x64/i64_divu/signed.wat
Normal file
20
winch/filetests/filetests/x64/i64_divu/signed.wat
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (result i64)
|
||||||
|
(i64.const -1)
|
||||||
|
(i64.const -1)
|
||||||
|
(i64.div_u)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: 48c7c1ffffffff mov rcx, 0xffffffffffffffff
|
||||||
|
;; b: 48c7c0ffffffff mov rax, 0xffffffffffffffff
|
||||||
|
;; 12: 4883f900 cmp rcx, 0
|
||||||
|
;; 16: 0f8502000000 jne 0x1e
|
||||||
|
;; 1c: 0f0b ud2
|
||||||
|
;; 1e: ba00000000 mov edx, 0
|
||||||
|
;; 23: 48f7f1 div rcx
|
||||||
|
;; 26: 5d pop rbp
|
||||||
|
;; 27: c3 ret
|
||||||
20
winch/filetests/filetests/x64/i64_divu/zero_zero.wat
Normal file
20
winch/filetests/filetests/x64/i64_divu/zero_zero.wat
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
;;! target = "x86_64"
|
||||||
|
|
||||||
|
(module
|
||||||
|
(func (result i64)
|
||||||
|
(i64.const 0)
|
||||||
|
(i64.const 0)
|
||||||
|
(i64.div_u)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;; 0: 55 push rbp
|
||||||
|
;; 1: 4889e5 mov rbp, rsp
|
||||||
|
;; 4: 48c7c100000000 mov rcx, 0
|
||||||
|
;; b: 48c7c000000000 mov rax, 0
|
||||||
|
;; 12: 4883f900 cmp rcx, 0
|
||||||
|
;; 16: 0f8502000000 jne 0x1e
|
||||||
|
;; 1c: 0f0b ud2
|
||||||
|
;; 1e: ba00000000 mov edx, 0
|
||||||
|
;; 23: 48f7f1 div rcx
|
||||||
|
;; 26: 5d pop rbp
|
||||||
|
;; 27: c3 ret
|
||||||
Reference in New Issue
Block a user