winch: Add support for integer multiplication in x64. (#5769)

This commit adds support for the `<i32|i64>.mul` WebAssembly instructions in x64.
This commit is contained in:
Saúl Cabrera
2023-02-13 16:20:36 -05:00
committed by GitHub
parent 19f337e29b
commit 91c8114f00
21 changed files with 370 additions and 0 deletions

View File

@@ -147,6 +147,10 @@ impl Masm for MacroAssembler {
todo!()
}
fn mul(&mut self, _dst: RegImm, _lhs: RegImm, _rhs: RegImm, _size: OperandSize) {
todo!()
}
fn zero(&mut self, reg: Reg) {
self.asm.load_constant(0, reg);
}

View File

@@ -242,6 +242,50 @@ impl Assembler {
});
}
/// Signed multiplication instruction.
pub fn mul(&mut self, src: Operand, dst: Operand, size: OperandSize) {
match &(src, dst) {
(Operand::Imm(imm), Operand::Reg(dst)) => {
if let Ok(val) = i32::try_from(*imm) {
self.mul_ir(val, *dst, size);
} else {
let scratch = regs::scratch();
self.mov_ir(*imm as u64, scratch, size);
self.mul_rr(scratch, *dst, size);
}
}
(Operand::Reg(src), Operand::Reg(dst)) => self.mul_rr(*src, *dst, size),
_ => panic!(
"Invalid operand combination for mul; src = {:?} dst = {:?}",
src, dst
),
}
}
/// Multiply immediate and register.
pub fn mul_ir(&mut self, imm: i32, dst: Reg, size: OperandSize) {
let imm = RegMemImm::imm(imm as u32);
self.emit(Inst::AluRmiR {
size: size.into(),
op: AluRmiROpcode::Mul,
src1: dst.into(),
src2: GprMemImm::new(imm).expect("valid immediate"),
dst: dst.into(),
});
}
/// Multiply register and register.
pub fn mul_rr(&mut self, src: Reg, dst: Reg, size: OperandSize) {
self.emit(Inst::AluRmiR {
size: size.into(),
op: AluRmiROpcode::Mul,
src1: dst.into(),
src2: src.into(),
dst: dst.into(),
});
}
/// Add instruction variants.
pub fn add(&mut self, src: Operand, dst: Operand, size: OperandSize) {
match &(src, dst) {

View File

@@ -139,6 +139,19 @@ impl Masm for MacroAssembler {
self.asm.sub(src, dst, size);
}
fn mul(&mut self, dst: RegImm, lhs: RegImm, rhs: RegImm, size: OperandSize) {
let (src, dst): (Operand, Operand) = if dst == lhs {
(rhs.into(), dst.into())
} else {
panic!(
"the destination and first source argument must be the same, dst={:?}, lhs={:?}",
dst, lhs
);
};
self.asm.mul(src, dst, size);
}
fn epilogue(&mut self, locals_size: u32) {
assert!(self.sp_offset == locals_size);

View File

@@ -91,6 +91,9 @@ pub(crate) trait MacroAssembler {
/// Perform subtraction operation.
fn sub(&mut self, dst: RegImm, lhs: RegImm, rhs: RegImm, size: OperandSize);
/// Perform multiplication operation.
fn mul(&mut self, dst: RegImm, lhs: RegImm, rhs: RegImm, size: OperandSize);
/// Push the register to the stack, returning the offset.
fn push(&mut self, src: Reg) -> u32;

View File

@@ -36,6 +36,8 @@ macro_rules! def_unsupported {
(emit I32Add $($rest:tt)*) => {};
(emit I64Add $($rest:tt)*) => {};
(emit I32Sub $($rest:tt)*) => {};
(emit I32Mul $($rest:tt)*) => {};
(emit I64Mul $($rest:tt)*) => {};
(emit I64Sub $($rest:tt)*) => {};
(emit LocalGet $($rest:tt)*) => {};
(emit LocalSet $($rest:tt)*) => {};
@@ -86,6 +88,20 @@ where
});
}
fn visit_i32_mul(&mut self) {
self.context
.i32_binop(&mut self.regalloc, &mut |masm: &mut M, 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| {
masm.mul(dst, dst, src, size);
});
}
fn visit_end(&mut self) {}
fn visit_local_get(&mut self, index: u32) {