diff --git a/winch/codegen/src/codegen/context.rs b/winch/codegen/src/codegen/context.rs new file mode 100644 index 0000000000..94b3da496f --- /dev/null +++ b/winch/codegen/src/codegen/context.rs @@ -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(&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(&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(&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( + &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( + 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(&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(&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( + 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); + } + }); + } +} diff --git a/winch/codegen/src/codegen.rs b/winch/codegen/src/codegen/mod.rs similarity index 52% rename from winch/codegen/src/codegen.rs rename to winch/codegen/src/codegen/mod.rs index 7114db7708..aa4ab537f3 100644 --- a/winch/codegen/src/codegen.rs +++ b/winch/codegen/src/codegen/mod.rs @@ -1,89 +1,12 @@ use crate::{ abi::{ABISig, ABI}, - frame::Frame, - masm::{MacroAssembler, OperandSize, RegImm}, - regalloc::RegAlloc, - stack::{Stack, Val}, + masm::{MacroAssembler, OperandSize}, }; use anyhow::Result; use wasmparser::{BinaryReader, FuncValidator, ValType, ValidatorResources, VisitOperator}; -/// The code generation context. -pub(crate) struct CodeGenContext<'a, M> -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(&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(&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)); - } - } -} +mod context; +pub(crate) use context::*; /// The code generation abstraction. pub(crate) struct CodeGen<'a, M> @@ -97,22 +20,22 @@ where sig: ABISig, /// The code generation context. - pub context: CodeGenContext<'a, M>, + pub context: CodeGenContext<'a>, - /// The register allocator. - pub regalloc: RegAlloc, + /// The MacroAssembler. + pub masm: &'a mut M, } impl<'a, M> CodeGen<'a, M> where M: MacroAssembler, { - pub fn new(context: CodeGenContext<'a, M>, sig: ABISig, regalloc: RegAlloc) -> Self { + pub fn new(masm: &'a mut M, context: CodeGenContext<'a>, sig: ABISig) -> Self { Self { word_size: ::word_bytes(), sig, context, - regalloc, + masm, } } @@ -131,10 +54,8 @@ where // TODO stack checks fn emit_start(&mut self) -> Result<()> { - self.context.masm.prologue(); - self.context - .masm - .reserve_stack(self.context.frame.locals_size); + self.masm.prologue(); + self.masm.reserve_stack(self.context.frame.locals_size); Ok(()) } @@ -145,10 +66,10 @@ where ) -> Result<()> { self.spill_register_arguments(); 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(), self.word_size, - &mut self.regalloc, + &mut self.context.regalloc, ); while !body.eof() { @@ -185,7 +106,7 @@ where // Emit the usual function end instruction sequence. pub fn emit_end(&mut self) -> Result<()> { self.handle_abi_result(); - self.context.masm.epilogue(self.context.frame.locals_size); + self.masm.epilogue(self.context.frame.locals_size); Ok(()) } @@ -206,14 +127,14 @@ where .frame .get_local(index as u32) .expect("valid local slot at location"); - let addr = self.context.masm.local_address(local); + let addr = self.masm.local_address(local); let src = arg .get_reg() .expect("arg should be associated to a register"); match &ty { - ValType::I32 => self.context.masm.store(src.into(), addr, OperandSize::S32), - ValType::I64 => self.context.masm.store(src.into(), addr, OperandSize::S64), + ValType::I32 => self.masm.store(src.into(), addr, OperandSize::S32), + ValType::I64 => self.masm.store(src.into(), addr, OperandSize::S64), _ => panic!("Unsupported type {:?}", ty), } }); @@ -225,8 +146,8 @@ where } let named_reg = self.sig.result.result_reg(); let reg = self - .regalloc - .pop_to_named_reg(&mut self.context, named_reg, OperandSize::S64); - self.regalloc.free_gpr(reg); + .context + .pop_to_named_reg(self.masm, named_reg, OperandSize::S64); + self.context.regalloc.free_gpr(reg); } } diff --git a/winch/codegen/src/isa/aarch64/masm.rs b/winch/codegen/src/isa/aarch64/masm.rs index 687bee93f4..89efe72cc6 100644 --- a/winch/codegen/src/isa/aarch64/masm.rs +++ b/winch/codegen/src/isa/aarch64/masm.rs @@ -5,8 +5,9 @@ use super::{ }; use crate::{ abi::local::LocalSlot, + codegen::CodeGenContext, isa::reg::Reg, - masm::{MacroAssembler as Masm, OperandSize, RegImm}, + masm::{DivKind, MacroAssembler as Masm, OperandSize, RegImm}, }; use cranelift_codegen::{settings, Final, MachBufferFinalized}; @@ -151,6 +152,10 @@ impl Masm for MacroAssembler { todo!() } + fn div(&mut self, _context: &mut CodeGenContext, _kind: DivKind, _size: OperandSize) { + todo!() + } + fn zero(&mut self, reg: Reg) { self.asm.load_constant(0, reg); } diff --git a/winch/codegen/src/isa/aarch64/mod.rs b/winch/codegen/src/isa/aarch64/mod.rs index 7e62e1aeea..4f8d3048c7 100644 --- a/winch/codegen/src/isa/aarch64/mod.rs +++ b/winch/codegen/src/isa/aarch64/mod.rs @@ -82,8 +82,8 @@ impl TargetIsa for Aarch64 { let frame = Frame::new(&abi_sig, &mut body, &mut validator, &abi)?; // TODO: Add floating point bitmask let regalloc = RegAlloc::new(RegSet::new(ALL_GPR, 0), scratch()); - let codegen_context = CodeGenContext::new(&mut masm, stack, &frame); - let mut codegen = CodeGen::new::(codegen_context, abi_sig, regalloc); + let codegen_context = CodeGenContext::new(regalloc, stack, &frame); + let mut codegen = CodeGen::new::(&mut masm, codegen_context, abi_sig); codegen.emit(&mut body, validator)?; Ok(masm.finalize()) diff --git a/winch/codegen/src/isa/x64/asm.rs b/winch/codegen/src/isa/x64/asm.rs index 0f4e0376cb..5b2286404b 100644 --- a/winch/codegen/src/isa/x64/asm.rs +++ b/winch/codegen/src/isa/x64/asm.rs @@ -1,11 +1,14 @@ //! Assembler library implementation for x64. -use crate::{isa::reg::Reg, masm::OperandSize}; +use crate::{ + isa::reg::Reg, + masm::{DivKind, OperandSize}, +}; use cranelift_codegen::{ isa::x64::{ args::{ - self, AluRmiROpcode, Amode, ExtMode, FromWritableReg, Gpr, GprMem, GprMemImm, RegMem, - RegMemImm, SyntheticAmode, WritableGpr, + self, AluRmiROpcode, Amode, DivOrRemKind, ExtMode, FromWritableReg, Gpr, GprMem, + GprMemImm, RegMem, RegMemImm, SyntheticAmode, WritableGpr, }, settings as x64_settings, EmitInfo, EmitState, Inst, }, @@ -61,6 +64,15 @@ impl From for args::OperandSize { } } +impl From for DivOrRemKind { + fn from(kind: DivKind) -> Self { + match kind { + DivKind::Signed => DivOrRemKind::SignedDiv, + DivKind::Unsigned => DivOrRemKind::UnsignedDiv, + } + } +} + /// Low level assembler implementation for x64. pub(crate) struct Assembler { /// 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. pub fn mul_ir(&mut self, imm: i32, dst: Reg, size: OperandSize) { let imm = RegMemImm::imm(imm as u32); diff --git a/winch/codegen/src/isa/x64/masm.rs b/winch/codegen/src/isa/x64/masm.rs index 6c32e7b969..758e72fa5c 100644 --- a/winch/codegen/src/isa/x64/masm.rs +++ b/winch/codegen/src/isa/x64/masm.rs @@ -1,11 +1,11 @@ use super::{ address::Address, asm::{Assembler, Operand}, - regs::{rbp, rsp}, + regs::{self, rbp, rsp}, }; -use crate::abi::LocalSlot; 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}; /// x64 MacroAssembler. @@ -152,6 +152,28 @@ impl Masm for MacroAssembler { 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) { assert!(self.sp_offset == locals_size); diff --git a/winch/codegen/src/isa/x64/mod.rs b/winch/codegen/src/isa/x64/mod.rs index 3f72c2c5f0..e44f3470f3 100644 --- a/winch/codegen/src/isa/x64/mod.rs +++ b/winch/codegen/src/isa/x64/mod.rs @@ -87,8 +87,8 @@ impl TargetIsa for X64 { let frame = Frame::new(&abi_sig, &mut body, &mut validator, &abi)?; // TODO Add in floating point bitmask let regalloc = RegAlloc::new(RegSet::new(ALL_GPR, 0), regs::scratch()); - let codegen_context = CodeGenContext::new(&mut masm, stack, &frame); - let mut codegen = CodeGen::new::(codegen_context, abi_sig, regalloc); + let codegen_context = CodeGenContext::new(regalloc, stack, &frame); + let mut codegen = CodeGen::new::(&mut masm, codegen_context, abi_sig); codegen.emit(&mut body, validator)?; diff --git a/winch/codegen/src/masm.rs b/winch/codegen/src/masm.rs index 56bc278970..1f4507be59 100644 --- a/winch/codegen/src/masm.rs +++ b/winch/codegen/src/masm.rs @@ -1,9 +1,18 @@ use crate::abi::{align_to, LocalSlot}; +use crate::codegen::CodeGenContext; use crate::isa::reg::Reg; use crate::regalloc::RegAlloc; use cranelift_codegen::{Final, MachBufferFinalized}; use std::{fmt::Debug, ops::Range}; +#[derive(Eq, PartialEq)] +pub(crate) enum DivKind { + /// Signed division. + Signed, + /// Unsigned division. + Unsigned, +} + /// Operand size, in bits. #[derive(Copy, Clone, Eq, PartialEq)] pub(crate) enum OperandSize { @@ -94,6 +103,19 @@ pub(crate) trait MacroAssembler { /// Perform multiplication operation. 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. fn push(&mut self, src: Reg) -> u32; diff --git a/winch/codegen/src/regalloc.rs b/winch/codegen/src/regalloc.rs index bdfd384862..9cd95213a6 100644 --- a/winch/codegen/src/regalloc.rs +++ b/winch/codegen/src/regalloc.rs @@ -1,11 +1,4 @@ -use crate::{ - codegen::CodeGenContext, - frame::Frame, - isa::reg::Reg, - masm::{MacroAssembler, OperandSize, RegImm}, - regset::RegSet, - stack::Val, -}; +use crate::{isa::reg::Reg, regset::RegSet}; /// The register allocator. /// @@ -30,79 +23,26 @@ impl RegAlloc { 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( - &mut self, - context: &mut CodeGenContext, - 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( - &mut self, - context: &mut CodeGenContext, - 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( - 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, /// spilling if none available. - pub fn any_gpr(&mut self, context: &mut CodeGenContext) -> Reg { + pub fn any_gpr(&mut self, spill: &mut F) -> Reg + where + F: FnMut(&mut RegAlloc), + { self.regset.any_gpr().unwrap_or_else(|| { - self.spill(context); + spill(self); self.regset.any_gpr().expect("any gpr to be available") }) } /// Request a specific general purpose register, /// spilling if not available. - pub fn gpr(&mut self, context: &mut CodeGenContext, named: Reg) -> Reg { + pub fn gpr(&mut self, named: Reg, spill: &mut F) -> Reg + where + F: FnMut(&mut RegAlloc), + { self.regset.gpr(named).unwrap_or_else(|| { - self.spill(context); + spill(self); self.regset .gpr(named) .expect(&format!("gpr {:?} to be available", named)) @@ -113,33 +53,4 @@ impl RegAlloc { pub fn free_gpr(&mut self, reg: 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(&mut self, context: &mut CodeGenContext) { - 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); - } - _ => {} - }); - } } diff --git a/winch/codegen/src/visitor.rs b/winch/codegen/src/visitor.rs index f948fb94e2..072c704041 100644 --- a/winch/codegen/src/visitor.rs +++ b/winch/codegen/src/visitor.rs @@ -5,7 +5,7 @@ //! machine code emitter. use crate::codegen::CodeGen; -use crate::masm::{MacroAssembler, OperandSize, RegImm}; +use crate::masm::{DivKind, MacroAssembler, OperandSize, RegImm}; use crate::stack::Val; use wasmparser::ValType; use wasmparser::VisitOperator; @@ -37,6 +37,10 @@ macro_rules! def_unsupported { (emit I64Add $($rest:tt)*) => {}; (emit I32Sub $($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 I64Sub $($rest:tt)*) => {}; (emit LocalGet $($rest:tt)*) => {}; @@ -62,46 +66,74 @@ where fn visit_i32_add(&mut self) { 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); }); } fn visit_i64_add(&mut self) { 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); }); } fn visit_i32_sub(&mut self) { 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); }); } fn visit_i64_sub(&mut self) { 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); }); } fn visit_i32_mul(&mut self) { 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); }); } fn visit_i64_mul(&mut self) { 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); }); } + 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_local_get(&mut self, index: u32) { @@ -124,10 +156,10 @@ where .get_local(index) .expect(&format!("vald local at slot = {}", index)); let size: OperandSize = slot.ty.into(); - let src = self.regalloc.pop_to_reg(context, size); - let addr = context.masm.local_address(&slot); - context.masm.store(RegImm::reg(src), addr, size); - self.regalloc.free_gpr(src); + let src = self.context.pop_to_reg(self.masm, size); + let addr = self.masm.local_address(&slot); + self.masm.store(RegImm::reg(src), addr, size); + self.context.regalloc.free_gpr(src); } wasmparser::for_each_operator!(def_unsupported); diff --git a/winch/filetests/filetests/x64/i32_divs/const.wat b/winch/filetests/filetests/x64/i32_divs/const.wat new file mode 100644 index 0000000000..c32078198f --- /dev/null +++ b/winch/filetests/filetests/x64/i32_divs/const.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i32_divs/one_zero.wat b/winch/filetests/filetests/x64/i32_divs/one_zero.wat new file mode 100644 index 0000000000..1ef0d78855 --- /dev/null +++ b/winch/filetests/filetests/x64/i32_divs/one_zero.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i32_divs/overflow.wat b/winch/filetests/filetests/x64/i32_divs/overflow.wat new file mode 100644 index 0000000000..7f2499a1b8 --- /dev/null +++ b/winch/filetests/filetests/x64/i32_divs/overflow.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i32_divs/params.wat b/winch/filetests/filetests/x64/i32_divs/params.wat new file mode 100644 index 0000000000..ec999077ba --- /dev/null +++ b/winch/filetests/filetests/x64/i32_divs/params.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i32_divs/zero_zero.wat b/winch/filetests/filetests/x64/i32_divs/zero_zero.wat new file mode 100644 index 0000000000..807b7f7eb8 --- /dev/null +++ b/winch/filetests/filetests/x64/i32_divs/zero_zero.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i32_divu/const.wat b/winch/filetests/filetests/x64/i32_divu/const.wat new file mode 100644 index 0000000000..d7be12fd17 --- /dev/null +++ b/winch/filetests/filetests/x64/i32_divu/const.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i32_divu/one_zero.wat b/winch/filetests/filetests/x64/i32_divu/one_zero.wat new file mode 100644 index 0000000000..8eb16cfb28 --- /dev/null +++ b/winch/filetests/filetests/x64/i32_divu/one_zero.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i32_divu/params.wat b/winch/filetests/filetests/x64/i32_divu/params.wat new file mode 100644 index 0000000000..f3a57b8648 --- /dev/null +++ b/winch/filetests/filetests/x64/i32_divu/params.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i32_divu/signed.wat b/winch/filetests/filetests/x64/i32_divu/signed.wat new file mode 100644 index 0000000000..925c2a038e --- /dev/null +++ b/winch/filetests/filetests/x64/i32_divu/signed.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i32_divu/zero_zero.wat b/winch/filetests/filetests/x64/i32_divu/zero_zero.wat new file mode 100644 index 0000000000..c67d1a06ce --- /dev/null +++ b/winch/filetests/filetests/x64/i32_divu/zero_zero.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i64_divs/const.wat b/winch/filetests/filetests/x64/i64_divs/const.wat new file mode 100644 index 0000000000..840f1ad5f9 --- /dev/null +++ b/winch/filetests/filetests/x64/i64_divs/const.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i64_divs/one_zero.wat b/winch/filetests/filetests/x64/i64_divs/one_zero.wat new file mode 100644 index 0000000000..c274f531e0 --- /dev/null +++ b/winch/filetests/filetests/x64/i64_divs/one_zero.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i64_divs/overflow.wat b/winch/filetests/filetests/x64/i64_divs/overflow.wat new file mode 100644 index 0000000000..18630116b1 --- /dev/null +++ b/winch/filetests/filetests/x64/i64_divs/overflow.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i64_divs/params.wat b/winch/filetests/filetests/x64/i64_divs/params.wat new file mode 100644 index 0000000000..d52e28cff6 --- /dev/null +++ b/winch/filetests/filetests/x64/i64_divs/params.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i64_divs/zero_zero.wat b/winch/filetests/filetests/x64/i64_divs/zero_zero.wat new file mode 100644 index 0000000000..4a08ab1053 --- /dev/null +++ b/winch/filetests/filetests/x64/i64_divs/zero_zero.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i64_divu/const.wat b/winch/filetests/filetests/x64/i64_divu/const.wat new file mode 100644 index 0000000000..3e6b33ba96 --- /dev/null +++ b/winch/filetests/filetests/x64/i64_divu/const.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i64_divu/one_zero.wat b/winch/filetests/filetests/x64/i64_divu/one_zero.wat new file mode 100644 index 0000000000..53bddb442c --- /dev/null +++ b/winch/filetests/filetests/x64/i64_divu/one_zero.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i64_divu/params.wat b/winch/filetests/filetests/x64/i64_divu/params.wat new file mode 100644 index 0000000000..75aeddef3c --- /dev/null +++ b/winch/filetests/filetests/x64/i64_divu/params.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i64_divu/signed.wat b/winch/filetests/filetests/x64/i64_divu/signed.wat new file mode 100644 index 0000000000..6fb06fa866 --- /dev/null +++ b/winch/filetests/filetests/x64/i64_divu/signed.wat @@ -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 diff --git a/winch/filetests/filetests/x64/i64_divu/zero_zero.wat b/winch/filetests/filetests/x64/i64_divu/zero_zero.wat new file mode 100644 index 0000000000..d0c087eee9 --- /dev/null +++ b/winch/filetests/filetests/x64/i64_divu/zero_zero.wat @@ -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