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:
Saúl Cabrera
2023-02-17 17:42:03 -05:00
committed by GitHub
parent 853ff787f3
commit 7ec925122d
30 changed files with 851 additions and 220 deletions

View File

@@ -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);