diff --git a/winch/codegen/src/isa/aarch64/masm.rs b/winch/codegen/src/isa/aarch64/masm.rs index 65c5790e95..687bee93f4 100644 --- a/winch/codegen/src/isa/aarch64/masm.rs +++ b/winch/codegen/src/isa/aarch64/masm.rs @@ -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); } diff --git a/winch/codegen/src/isa/x64/asm.rs b/winch/codegen/src/isa/x64/asm.rs index 765f41dbe2..0f4e0376cb 100644 --- a/winch/codegen/src/isa/x64/asm.rs +++ b/winch/codegen/src/isa/x64/asm.rs @@ -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) { diff --git a/winch/codegen/src/isa/x64/masm.rs b/winch/codegen/src/isa/x64/masm.rs index 5e5fbedd36..6c32e7b969 100644 --- a/winch/codegen/src/isa/x64/masm.rs +++ b/winch/codegen/src/isa/x64/masm.rs @@ -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); diff --git a/winch/codegen/src/masm.rs b/winch/codegen/src/masm.rs index 508efe4bac..56bc278970 100644 --- a/winch/codegen/src/masm.rs +++ b/winch/codegen/src/masm.rs @@ -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; diff --git a/winch/codegen/src/visitor.rs b/winch/codegen/src/visitor.rs index b6c4b093c8..f948fb94e2 100644 --- a/winch/codegen/src/visitor.rs +++ b/winch/codegen/src/visitor.rs @@ -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) { diff --git a/winch/filetests/filetests/x64/i32_mul/const.wat b/winch/filetests/filetests/x64/i32_mul/const.wat new file mode 100644 index 0000000000..8290e86741 --- /dev/null +++ b/winch/filetests/filetests/x64/i32_mul/const.wat @@ -0,0 +1,15 @@ +;;! target = "x86_64" + +(module + (func (result i32) + (i32.const 10) + (i32.const 20) + (i32.mul) + ) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: b80a000000 mov eax, 0xa +;; 9: 6bc014 imul eax, eax, 0x14 +;; c: 5d pop rbp +;; d: c3 ret diff --git a/winch/filetests/filetests/x64/i32_mul/locals.wat b/winch/filetests/filetests/x64/i32_mul/locals.wat new file mode 100644 index 0000000000..9da54fca92 --- /dev/null +++ b/winch/filetests/filetests/x64/i32_mul/locals.wat @@ -0,0 +1,33 @@ +;;! target = "x86_64" + +(module + (func (result i32) + (local $foo i32) + (local $bar i32) + + (i32.const 10) + (local.set $foo) + + (i32.const 20) + (local.set $bar) + + (local.get $foo) + (local.get $bar) + i32.mul + ) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 48c7042400000000 mov qword ptr [rsp], 0 +;; 10: b80a000000 mov eax, 0xa +;; 15: 89442404 mov dword ptr [rsp + 4], eax +;; 19: b814000000 mov eax, 0x14 +;; 1e: 890424 mov dword ptr [rsp], eax +;; 21: 8b0424 mov eax, dword ptr [rsp] +;; 24: 8b4c2404 mov ecx, dword ptr [rsp + 4] +;; 28: 0fafc8 imul ecx, eax +;; 2b: 4889c8 mov rax, rcx +;; 2e: 4883c408 add rsp, 8 +;; 32: 5d pop rbp +;; 33: c3 ret diff --git a/winch/filetests/filetests/x64/i32_mul/max.wat b/winch/filetests/filetests/x64/i32_mul/max.wat new file mode 100644 index 0000000000..20dcf82cb3 --- /dev/null +++ b/winch/filetests/filetests/x64/i32_mul/max.wat @@ -0,0 +1,14 @@ +;;! target = "x86_64" +(module + (func (result i32) + (i32.const 0x7fffffff) + (i32.const -1) + (i32.mul) + ) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: b8ffffff7f mov eax, 0x7fffffff +;; 9: 6bc0ff imul eax, eax, -1 +;; c: 5d pop rbp +;; d: c3 ret diff --git a/winch/filetests/filetests/x64/i32_mul/max_one.wat b/winch/filetests/filetests/x64/i32_mul/max_one.wat new file mode 100644 index 0000000000..9f1bf1b66c --- /dev/null +++ b/winch/filetests/filetests/x64/i32_mul/max_one.wat @@ -0,0 +1,15 @@ +;;! target = "x86_64" + +(module + (func (result i32) + (i32.const 0x80000000) + (i32.const -1) + (i32.mul) + ) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: b800000080 mov eax, 0x80000000 +;; 9: 6bc0ff imul eax, eax, -1 +;; c: 5d pop rbp +;; d: c3 ret diff --git a/winch/filetests/filetests/x64/i32_mul/mixed.wat b/winch/filetests/filetests/x64/i32_mul/mixed.wat new file mode 100644 index 0000000000..6db1fe4953 --- /dev/null +++ b/winch/filetests/filetests/x64/i32_mul/mixed.wat @@ -0,0 +1,15 @@ +;;! target = "x86_64" + +(module + (func (result i32) + (i32.const -1) + (i32.const 1) + (i32.mul) + ) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: b8ffffffff mov eax, 0xffffffff +;; 9: 6bc001 imul eax, eax, 1 +;; c: 5d pop rbp +;; d: c3 ret diff --git a/winch/filetests/filetests/x64/i32_mul/params.wat b/winch/filetests/filetests/x64/i32_mul/params.wat new file mode 100644 index 0000000000..41de00aaa1 --- /dev/null +++ b/winch/filetests/filetests/x64/i32_mul/params.wat @@ -0,0 +1,21 @@ +;;! target = "x86_64" + +(module + (func (param i32) (param i32) (result i32) + (local.get 0) + (local.get 1) + (i32.mul) + ) +) +;; 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: 8b0424 mov eax, dword ptr [rsp] +;; 12: 8b4c2404 mov ecx, dword ptr [rsp + 4] +;; 16: 0fafc8 imul ecx, eax +;; 19: 4889c8 mov rax, rcx +;; 1c: 4883c408 add rsp, 8 +;; 20: 5d pop rbp +;; 21: c3 ret diff --git a/winch/filetests/filetests/x64/i32_mul/signed.wat b/winch/filetests/filetests/x64/i32_mul/signed.wat new file mode 100644 index 0000000000..c5ab0f9091 --- /dev/null +++ b/winch/filetests/filetests/x64/i32_mul/signed.wat @@ -0,0 +1,15 @@ +;;! target = "x86_64" + +(module + (func (result i32) + (i32.const -1) + (i32.const -1) + (i32.mul) + ) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: b8ffffffff mov eax, 0xffffffff +;; 9: 6bc0ff imul eax, eax, -1 +;; c: 5d pop rbp +;; d: c3 ret diff --git a/winch/filetests/filetests/x64/i32_mul/unsigned_with_zero.wat b/winch/filetests/filetests/x64/i32_mul/unsigned_with_zero.wat new file mode 100644 index 0000000000..22ac571657 --- /dev/null +++ b/winch/filetests/filetests/x64/i32_mul/unsigned_with_zero.wat @@ -0,0 +1,15 @@ +;;! target = "x86_64" + +(module + (func (result i32) + (i32.const 1) + (i32.const 0) + (i32.mul) + ) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: b801000000 mov eax, 1 +;; 9: 6bc000 imul eax, eax, 0 +;; c: 5d pop rbp +;; d: c3 ret diff --git a/winch/filetests/filetests/x64/i64_mul/const.wat b/winch/filetests/filetests/x64/i64_mul/const.wat new file mode 100644 index 0000000000..b13e59b43e --- /dev/null +++ b/winch/filetests/filetests/x64/i64_mul/const.wat @@ -0,0 +1,15 @@ +;;! target = "x86_64" + +(module + (func (result i64) + (i64.const 10) + (i64.const 20) + (i64.mul) + ) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 48c7c00a000000 mov rax, 0xa +;; b: 486bc014 imul rax, rax, 0x14 +;; f: 5d pop rbp +;; 10: c3 ret diff --git a/winch/filetests/filetests/x64/i64_mul/locals.wat b/winch/filetests/filetests/x64/i64_mul/locals.wat new file mode 100644 index 0000000000..0593336f19 --- /dev/null +++ b/winch/filetests/filetests/x64/i64_mul/locals.wat @@ -0,0 +1,35 @@ +;;! target = "x86_64" + +(module + (func (result i64) + (local $foo i64) + (local $bar i64) + + (i64.const 10) + (local.set $foo) + + (i64.const 20) + (local.set $bar) + + (local.get $foo) + (local.get $bar) + i64.mul + ) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec10 sub rsp, 0x10 +;; 8: 4531db xor r11d, r11d +;; b: 4c895c2408 mov qword ptr [rsp + 8], r11 +;; 10: 4c891c24 mov qword ptr [rsp], r11 +;; 14: 48c7c00a000000 mov rax, 0xa +;; 1b: 4889442408 mov qword ptr [rsp + 8], rax +;; 20: 48c7c014000000 mov rax, 0x14 +;; 27: 48890424 mov qword ptr [rsp], rax +;; 2b: 488b0424 mov rax, qword ptr [rsp] +;; 2f: 488b4c2408 mov rcx, qword ptr [rsp + 8] +;; 34: 480fafc8 imul rcx, rax +;; 38: 4889c8 mov rax, rcx +;; 3b: 4883c410 add rsp, 0x10 +;; 3f: 5d pop rbp +;; 40: c3 ret diff --git a/winch/filetests/filetests/x64/i64_mul/max.wat b/winch/filetests/filetests/x64/i64_mul/max.wat new file mode 100644 index 0000000000..a5f30f09a7 --- /dev/null +++ b/winch/filetests/filetests/x64/i64_mul/max.wat @@ -0,0 +1,15 @@ +;;! target = "x86_64" +(module + (func (result i64) + (i64.const 0x7fffffffffffffff) + (i64.const -1) + (i64.mul) + ) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 48b8ffffffffffffff7f +;; movabs rax, 0x7fffffffffffffff +;; e: 486bc0ff imul rax, rax, -1 +;; 12: 5d pop rbp +;; 13: c3 ret diff --git a/winch/filetests/filetests/x64/i64_mul/max_one.wat b/winch/filetests/filetests/x64/i64_mul/max_one.wat new file mode 100644 index 0000000000..f89aeb8b31 --- /dev/null +++ b/winch/filetests/filetests/x64/i64_mul/max_one.wat @@ -0,0 +1,16 @@ +;;! target = "x86_64" + +(module + (func (result i64) + (i64.const 0x8000000000000000) + (i64.const -1) + (i64.mul) + ) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 48b80000000000000080 +;; movabs rax, 0x8000000000000000 +;; e: 486bc0ff imul rax, rax, -1 +;; 12: 5d pop rbp +;; 13: c3 ret diff --git a/winch/filetests/filetests/x64/i64_mul/mixed.wat b/winch/filetests/filetests/x64/i64_mul/mixed.wat new file mode 100644 index 0000000000..e71f61babd --- /dev/null +++ b/winch/filetests/filetests/x64/i64_mul/mixed.wat @@ -0,0 +1,15 @@ +;;! target = "x86_64" + +(module + (func (result i64) + (i64.const -1) + (i64.const 1) + (i64.mul) + ) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 48c7c0ffffffff mov rax, 0xffffffffffffffff +;; b: 486bc001 imul rax, rax, 1 +;; f: 5d pop rbp +;; 10: c3 ret diff --git a/winch/filetests/filetests/x64/i64_mul/params.wat b/winch/filetests/filetests/x64/i64_mul/params.wat new file mode 100644 index 0000000000..6e6997fb87 --- /dev/null +++ b/winch/filetests/filetests/x64/i64_mul/params.wat @@ -0,0 +1,21 @@ +;;! target = "x86_64" + +(module + (func (param i64) (param i64) (result i64) + (local.get 0) + (local.get 1) + (i64.mul) + ) +) +;; 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: 488b0424 mov rax, qword ptr [rsp] +;; 15: 488b4c2408 mov rcx, qword ptr [rsp + 8] +;; 1a: 480fafc8 imul rcx, rax +;; 1e: 4889c8 mov rax, rcx +;; 21: 4883c410 add rsp, 0x10 +;; 25: 5d pop rbp +;; 26: c3 ret diff --git a/winch/filetests/filetests/x64/i64_mul/signed.wat b/winch/filetests/filetests/x64/i64_mul/signed.wat new file mode 100644 index 0000000000..fc3ca9bcfa --- /dev/null +++ b/winch/filetests/filetests/x64/i64_mul/signed.wat @@ -0,0 +1,15 @@ +;;! target = "x86_64" + +(module + (func (result i64) + (i64.const -1) + (i64.const -1) + (i64.mul) + ) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 48c7c0ffffffff mov rax, 0xffffffffffffffff +;; b: 486bc0ff imul rax, rax, -1 +;; f: 5d pop rbp +;; 10: c3 ret diff --git a/winch/filetests/filetests/x64/i64_mul/unsigned_with_zero.wat b/winch/filetests/filetests/x64/i64_mul/unsigned_with_zero.wat new file mode 100644 index 0000000000..f0ab1e282e --- /dev/null +++ b/winch/filetests/filetests/x64/i64_mul/unsigned_with_zero.wat @@ -0,0 +1,15 @@ +;;! target = "x86_64" + +(module + (func (result i64) + (i64.const 1) + (i64.const 0) + (i64.mul) + ) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 48c7c001000000 mov rax, 1 +;; b: 486bc000 imul rax, rax, 0 +;; f: 5d pop rbp +;; 10: c3 ret