use crate::abi::{self, align_to, LocalSlot};
use crate::codegen::CodeGenContext;
use crate::isa::reg::Reg;
use crate::regalloc::RegAlloc;
use cranelift_codegen::{Final, MachBufferFinalized, MachLabel};
use std::{fmt::Debug, ops::Range};
use wasmtime_environ::PtrSize;
#[derive(Eq, PartialEq)]
pub(crate) enum DivKind {
Signed,
Unsigned,
}
pub(crate) enum RemKind {
Signed,
Unsigned,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(crate) enum CmpKind {
Eq,
Ne,
LtS,
LtU,
GtS,
GtU,
LeS,
LeU,
GeS,
GeU,
}
pub(crate) enum ShiftKind {
Shl,
ShrS,
ShrU,
Rotl,
Rotr,
}
#[derive(Copy, Debug, Clone, Eq, PartialEq)]
pub(crate) enum OperandSize {
S32,
S64,
}
impl OperandSize {
pub fn num_bits(&self) -> i32 {
match self {
OperandSize::S32 => 32,
OperandSize::S64 => 64,
}
}
pub fn log2(&self) -> u8 {
match self {
OperandSize::S32 => 5,
OperandSize::S64 => 6,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum RegImm {
Reg(Reg),
Imm(i64),
}
#[derive(Clone)]
pub(crate) enum CalleeKind {
Indirect(Reg),
Direct(u32),
}
impl RegImm {
pub fn reg(r: Reg) -> Self {
RegImm::Reg(r)
}
pub fn imm(imm: i64) -> Self {
RegImm::Imm(imm)
}
}
impl From<Reg> for RegImm {
fn from(r: Reg) -> Self {
Self::Reg(r)
}
}
pub(crate) trait MacroAssembler {
type Address: Copy;
type Ptr: PtrSize;
type ABI: abi::ABI;
fn prologue(&mut self);
fn epilogue(&mut self, locals_size: u32);
fn reserve_stack(&mut self, bytes: u32);
fn free_stack(&mut self, bytes: u32);
fn reset_stack_pointer(&mut self, offset: u32);
fn local_address(&mut self, local: &LocalSlot) -> Self::Address;
fn address_from_sp(&self, offset: u32) -> Self::Address;
fn address_at_sp(&self, offset: u32) -> Self::Address;
fn address_at_reg(&self, reg: Reg, offset: u32) -> Self::Address;
fn call(&mut self, stack_args_size: u32, f: impl FnMut(&mut Self) -> CalleeKind) -> u32;
fn sp_offset(&self) -> u32;
fn store(&mut self, src: RegImm, dst: Self::Address, size: OperandSize);
fn load(&mut self, src: Self::Address, dst: Reg, size: OperandSize);
fn pop(&mut self, dst: Reg);
fn mov(&mut self, src: RegImm, dst: RegImm, size: OperandSize);
fn add(&mut self, dst: RegImm, lhs: RegImm, rhs: RegImm, size: OperandSize);
fn sub(&mut self, dst: RegImm, lhs: RegImm, rhs: RegImm, size: OperandSize);
fn mul(&mut self, dst: RegImm, lhs: RegImm, rhs: RegImm, size: OperandSize);
fn and(&mut self, dst: RegImm, lhs: RegImm, rhs: RegImm, size: OperandSize);
fn or(&mut self, dst: RegImm, lhs: RegImm, rhs: RegImm, size: OperandSize);
fn xor(&mut self, dst: RegImm, lhs: RegImm, rhs: RegImm, size: OperandSize);
fn shift(&mut self, context: &mut CodeGenContext, kind: ShiftKind, size: OperandSize);
fn div(&mut self, context: &mut CodeGenContext, kind: DivKind, size: OperandSize);
fn rem(&mut self, context: &mut CodeGenContext, kind: RemKind, size: OperandSize);
fn cmp_with_set(&mut self, src: RegImm, dst: RegImm, kind: CmpKind, size: OperandSize);
fn clz(&mut self, src: Reg, dst: Reg, size: OperandSize);
fn ctz(&mut self, src: Reg, dst: Reg, size: OperandSize);
fn push(&mut self, src: Reg) -> u32;
fn finalize(self) -> MachBufferFinalized<Final>;
fn zero(&mut self, reg: Reg);
fn popcnt(&mut self, context: &mut CodeGenContext, size: OperandSize);
fn zero_mem_range(&mut self, mem: &Range<u32>, regalloc: &mut RegAlloc) {
let word_size = <Self::ABI as abi::ABI>::word_bytes();
if mem.is_empty() {
return;
}
let start = if mem.start % word_size == 0 {
mem.start
} else {
assert!(mem.start % 4 == 0);
let start = align_to(mem.start, word_size);
let addr: Self::Address = self.local_address(&LocalSlot::i32(start));
self.store(RegImm::imm(0), addr, OperandSize::S32);
assert!(start % word_size == 0);
start
};
let end = align_to(mem.end, word_size);
let slots = (end - start) / word_size;
if slots == 1 {
let slot = LocalSlot::i64(start + word_size);
let addr: Self::Address = self.local_address(&slot);
self.store(RegImm::imm(0), addr, OperandSize::S64);
} else {
let zero = regalloc.scratch;
self.zero(zero);
let zero = RegImm::reg(zero);
for step in (start..end).into_iter().step_by(word_size as usize) {
let slot = LocalSlot::i64(step + word_size);
let addr: Self::Address = self.local_address(&slot);
self.store(zero, addr, OperandSize::S64);
}
}
}
fn get_label(&mut self) -> MachLabel;
fn bind(&mut self, label: MachLabel);
fn branch(
&mut self,
kind: CmpKind,
lhs: RegImm,
rhs: RegImm,
taken: MachLabel,
size: OperandSize,
);
fn jmp(&mut self, target: MachLabel);
fn unreachable(&mut self);
}