use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MSAElementSize {
Byte, Halfword, Word, Dword, }
impl MSAElementSize {
pub fn bits(&self) -> u8 {
match self {
MSAElementSize::Byte => 8,
MSAElementSize::Halfword => 16,
MSAElementSize::Word => 32,
MSAElementSize::Dword => 64,
}
}
pub fn suffix(&self) -> &'static str {
match self {
MSAElementSize::Byte => ".B",
MSAElementSize::Halfword => ".H",
MSAElementSize::Word => ".W",
MSAElementSize::Dword => ".D",
}
}
pub fn elements_per_128bit(&self) -> u8 {
128 / self.bits()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MSAOpcode {
ADDV,
SUBV,
MULV,
DIV_S,
DIV_U,
MOD_S,
MOD_U,
ADDS_S,
ADDS_U,
SUBS_S,
SUBS_U,
AVE_S,
AVE_U,
AVER_S,
AVER_U,
MIN_S,
MIN_U,
MAX_S,
MAX_U,
DPADD_S,
DPADD_U,
DPSUB_S,
DPSUB_U,
MADDV,
MSUBV,
ASUB_S,
ASUB_U,
FADD,
FSUB,
FMUL,
FDIV,
FMIN,
FMAX,
FMIN_A,
FMAX_A,
FEXP2,
FLOG2,
FRINT,
FCLASS,
FTRUNC_S,
FTRUNC_U,
FSQRT,
FRCP,
FRSQRT,
FMADD,
FMSUB,
CEQ,
CLE_S,
CLE_U,
CLT_S,
CLT_U,
ANDV,
ORV,
XORV,
NORV,
SLLV,
SRLV,
SRAV,
BCLRV,
BSETV,
BNEGV,
FILL,
SPLAT,
SPLATI,
INSERT,
INSVE,
COPY_S,
COPY_U,
VSHF,
ILVR,
ILVL,
ILVEV,
ILVOD,
PCKEV,
PCKOD,
LD,
ST,
LDI, PCNT,
NLOC,
NLZC,
BINSL,
BINSR,
BMNZI,
BMZI,
BSELI,
SHF,
}
impl fmt::Display for MSAOpcode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
pub const MSA_VECTOR_REG_COUNT: usize = 32;
pub const MSA_VECTOR_REG_BITS: usize = 128;
pub const MSA_VECTOR_REG_BYTES: usize = 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MSAControlReg {
MSAIR, MSACSR, MSAFR, }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MSACondition {
AllTrue,
AnyTrue,
AllFalse,
AnyFalse,
}
#[derive(Debug, Clone)]
pub struct MSAInstrDesc {
pub opcode: MSAOpcode,
pub element_size: Option<MSAElementSize>,
pub mnemnonic: String,
pub is_float: bool,
pub is_unsigned: bool,
pub is_saturating: bool,
pub num_operands: u8,
pub is_commutative: bool,
}
impl MSAInstrDesc {
pub fn new(opcode: MSAOpcode) -> Self {
MSAInstrDesc {
opcode,
element_size: None,
mnemnonic: String::new(),
is_float: false,
is_unsigned: false,
is_saturating: false,
num_operands: 3, is_commutative: false,
}
}
pub fn with_size(mut self, es: MSAElementSize) -> Self {
self.element_size = Some(es);
self
}
pub fn with_float(mut self) -> Self {
self.is_float = true;
self
}
pub fn with_unsigned(mut self) -> Self {
self.is_unsigned = true;
self
}
}
#[derive(Debug, Clone)]
pub struct MSAInstructionSelector {
pub is_64bit: bool,
pub has_msa: bool,
pub vreg_map: HashMap<usize, usize>,
pub selected_ops: Vec<MSASelectedOp>,
}
#[derive(Debug, Clone)]
pub struct MSASelectedOp {
pub opcode: MSAOpcode,
pub element_size: Option<MSAElementSize>,
pub dst_reg: usize,
pub src1_reg: usize,
pub src2_reg: Option<usize>,
pub immediate: Option<i64>,
pub offset: Option<i64>,
pub base_reg: Option<usize>,
}
impl MSAInstructionSelector {
pub fn new(is_64bit: bool) -> Self {
MSAInstructionSelector {
is_64bit,
has_msa: true,
vreg_map: HashMap::new(),
selected_ops: Vec::new(),
}
}
pub fn select_addv(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::ADDV,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_subv(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::SUBV,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_mulv(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::MULV,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_div_sv(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::DIV_S,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_div_uv(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::DIV_U,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_adds_s(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::ADDS_S,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_fadd(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::FADD,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_fsub(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::FSUB,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_fmul(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::FMUL,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_fdiv(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::FDIV,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_fmin(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::FMIN,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_fmax(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::FMAX,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_ceq(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::CEQ,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_cle_s(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::CLE_S,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_clt_s(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::CLT_S,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_cle_u(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::CLE_U,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_clt_u(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::CLT_U,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_andv(&mut self, dst: usize, src1: usize, src2: usize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::ANDV,
element_size: None,
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_orv(&mut self, dst: usize, src1: usize, src2: usize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::ORV,
element_size: None,
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_xorv(&mut self, dst: usize, src1: usize, src2: usize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::XORV,
element_size: None,
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_norv(&mut self, dst: usize, src1: usize, src2: usize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::NORV,
element_size: None,
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_sllv(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::SLLV,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_srlv(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::SRLV,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_srav(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::SRAV,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_bclrv(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::BCLRV,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_bsetv(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::BSETV,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_bnegv(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::BNEGV,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_vshf(&mut self, dst: usize, src1: usize, src2: usize, control: usize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::VSHF,
element_size: None,
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(control),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_ilvr(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::ILVR,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_ilvl(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::ILVL,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_ilvev(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::ILVEV,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_ilvod(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::ILVOD,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_pckev(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::PCKEV,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_pckod(&mut self, dst: usize, src1: usize, src2: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::PCKOD,
element_size: Some(es),
dst_reg: dst,
src1_reg: src1,
src2_reg: Some(src2),
immediate: None,
offset: None,
base_reg: None,
});
}
pub fn select_insert(&mut self, dst: usize, src: usize, element: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::INSERT,
element_size: Some(es),
dst_reg: dst,
src1_reg: src,
src2_reg: None,
immediate: Some(element as i64),
offset: None,
base_reg: None,
});
}
pub fn select_insve(&mut self, dst: usize, src: usize, element: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::INSVE,
element_size: Some(es),
dst_reg: dst,
src1_reg: src,
src2_reg: None,
immediate: Some(element as i64),
offset: None,
base_reg: None,
});
}
pub fn select_copy_s(
&mut self,
gpr: usize,
vec_reg: usize,
element: usize,
es: MSAElementSize,
) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::COPY_S,
element_size: Some(es),
dst_reg: gpr,
src1_reg: vec_reg,
src2_reg: None,
immediate: Some(element as i64),
offset: None,
base_reg: None,
});
}
pub fn select_copy_u(
&mut self,
gpr: usize,
vec_reg: usize,
element: usize,
es: MSAElementSize,
) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::COPY_U,
element_size: Some(es),
dst_reg: gpr,
src1_reg: vec_reg,
src2_reg: None,
immediate: Some(element as i64),
offset: None,
base_reg: None,
});
}
pub fn select_splat(&mut self, dst: usize, src: usize, element: usize, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::SPLAT,
element_size: Some(es),
dst_reg: dst,
src1_reg: src,
src2_reg: None,
immediate: Some(element as i64),
offset: None,
base_reg: None,
});
}
pub fn select_ld(&mut self, dst: usize, base: usize, offset: i64, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::LD,
element_size: Some(es),
dst_reg: dst,
src1_reg: base,
src2_reg: None,
immediate: None,
offset: Some(offset),
base_reg: Some(base),
});
}
pub fn select_st(&mut self, src: usize, base: usize, offset: i64, es: MSAElementSize) {
self.selected_ops.push(MSASelectedOp {
opcode: MSAOpcode::ST,
element_size: Some(es),
dst_reg: 0,
src1_reg: src,
src2_reg: None,
immediate: None,
offset: Some(offset),
base_reg: Some(base),
});
}
}
pub fn lower_vector_add_to_msa(
isel: &mut MSAInstructionSelector,
dst: usize,
src1: usize,
src2: usize,
element_bits: u8,
is_float: bool,
) {
let es = match element_bits {
8 => MSAElementSize::Byte,
16 => MSAElementSize::Halfword,
32 => MSAElementSize::Word,
64 => MSAElementSize::Dword,
_ => MSAElementSize::Word,
};
if is_float {
isel.select_fadd(dst, src1, src2, es);
} else {
isel.select_addv(dst, src1, src2, es);
}
}
pub fn lower_vector_sub_to_msa(
isel: &mut MSAInstructionSelector,
dst: usize,
src1: usize,
src2: usize,
element_bits: u8,
is_float: bool,
) {
let es = match element_bits {
8 => MSAElementSize::Byte,
16 => MSAElementSize::Halfword,
32 => MSAElementSize::Word,
64 => MSAElementSize::Dword,
_ => MSAElementSize::Word,
};
if is_float {
isel.select_fsub(dst, src1, src2, es);
} else {
isel.select_subv(dst, src1, src2, es);
}
}
pub fn lower_vector_mul_to_msa(
isel: &mut MSAInstructionSelector,
dst: usize,
src1: usize,
src2: usize,
element_bits: u8,
is_float: bool,
) {
let es = match element_bits {
8 => MSAElementSize::Byte,
16 => MSAElementSize::Halfword,
32 => MSAElementSize::Word,
64 => MSAElementSize::Dword,
_ => MSAElementSize::Word,
};
if is_float {
isel.select_fmul(dst, src1, src2, es);
} else {
isel.select_mulv(dst, src1, src2, es);
}
}
pub fn lower_vector_div_to_msa(
isel: &mut MSAInstructionSelector,
dst: usize,
src1: usize,
src2: usize,
element_bits: u8,
is_float: bool,
is_unsigned: bool,
) {
let es = match element_bits {
8 => MSAElementSize::Byte,
16 => MSAElementSize::Halfword,
32 => MSAElementSize::Word,
64 => MSAElementSize::Dword,
_ => MSAElementSize::Word,
};
if is_float {
isel.select_fdiv(dst, src1, src2, es);
} else if is_unsigned {
isel.select_div_uv(dst, src1, src2, es);
} else {
isel.select_div_sv(dst, src1, src2, es);
}
}
pub fn lower_vector_cmp_to_msa(
isel: &mut MSAInstructionSelector,
dst: usize,
src1: usize,
src2: usize,
element_bits: u8,
cmp_type: &str,
) {
let es = match element_bits {
8 => MSAElementSize::Byte,
16 => MSAElementSize::Halfword,
32 => MSAElementSize::Word,
64 => MSAElementSize::Dword,
_ => MSAElementSize::Word,
};
match cmp_type {
"eq" => isel.select_ceq(dst, src1, src2, es),
"sle" => isel.select_cle_s(dst, src1, src2, es),
"slt" => isel.select_clt_s(dst, src1, src2, es),
"ule" => isel.select_cle_u(dst, src1, src2, es),
"ult" => isel.select_clt_u(dst, src1, src2, es),
_ => {}
}
}
pub fn lower_vector_logical_to_msa(
isel: &mut MSAInstructionSelector,
dst: usize,
src1: usize,
src2: usize,
op: &str,
) {
match op {
"and" => isel.select_andv(dst, src1, src2),
"or" => isel.select_orv(dst, src1, src2),
"xor" => isel.select_xorv(dst, src1, src2),
"nor" => isel.select_norv(dst, src1, src2),
_ => {}
}
}
pub fn emit_msa_assembly(ops: &[MSASelectedOp]) -> Vec<String> {
let mut asm = Vec::new();
for op in ops {
let es_suffix = op.element_size.map(|es| es.suffix()).unwrap_or("");
let line = match op.opcode {
MSAOpcode::ADDV => format!(
" addv{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::SUBV => format!(
" subv{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::MULV => format!(
" mulv{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::FADD => format!(
" fadd{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::FSUB => format!(
" fsub{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::FMUL => format!(
" fmul{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::FDIV => format!(
" fdiv{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::FMIN => format!(
" fmin{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::FMAX => format!(
" fmax{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::CEQ => format!(
" ceq{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::CLE_S => format!(
" cle_s{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::CLT_S => format!(
" clt_s{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::ANDV => format!(
" and.v $w{}, $w{}, $w{}",
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::ORV => format!(
" or.v $w{}, $w{}, $w{}",
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::XORV => format!(
" xor.v $w{}, $w{}, $w{}",
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::NORV => format!(
" nor.v $w{}, $w{}, $w{}",
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::SLLV => format!(
" sll{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::SRLV => format!(
" srl{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::SRAV => format!(
" sra{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::ILVR => format!(
" ilvr{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::ILVL => format!(
" ilvl{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::ILVEV => format!(
" ilvev{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::ILVOD => format!(
" ilvod{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::PCKEV => format!(
" pckev{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::PCKOD => format!(
" pckod{} $w{}, $w{}, $w{}",
es_suffix,
op.dst_reg,
op.src1_reg,
op.src2_reg.unwrap_or(0)
),
MSAOpcode::INSERT => format!(
" insert{} $w{}[{}], $w{}",
es_suffix,
op.dst_reg,
op.immediate.unwrap_or(0),
op.src1_reg
),
MSAOpcode::COPY_S => format!(
" copy_s{} ${}, $w{}[{}]",
es_suffix,
op.dst_reg,
op.src1_reg,
op.immediate.unwrap_or(0)
),
MSAOpcode::COPY_U => format!(
" copy_u{} ${}, $w{}[{}]",
es_suffix,
op.dst_reg,
op.src1_reg,
op.immediate.unwrap_or(0)
),
MSAOpcode::SPLAT => format!(
" splat{} $w{}, $w{}[{}]",
es_suffix,
op.dst_reg,
op.src1_reg,
op.immediate.unwrap_or(0)
),
MSAOpcode::LD => format!(
" ld{} $w{}, {}({})",
es_suffix,
op.dst_reg,
op.offset.unwrap_or(0),
op.base_reg.unwrap_or(0)
),
MSAOpcode::ST => format!(
" st{} $w{}, {}({})",
es_suffix,
op.src1_reg,
op.offset.unwrap_or(0),
op.base_reg.unwrap_or(0)
),
_ => format!(" ; unsupported MSA op: {:?}", op.opcode),
};
asm.push(line);
}
asm
}
pub fn emit_msa_function(func_name: &str, ops: &[MSASelectedOp]) -> String {
let mut out = String::new();
out.push_str(&format!("{}:\n", func_name));
out.push_str(" .set msa\n");
for line in emit_msa_assembly(ops) {
out.push_str(&line);
out.push('\n');
}
out.push_str(" jr $ra\n");
out.push_str(" nop\n");
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_msa_element_size_bits() {
assert_eq!(MSAElementSize::Byte.bits(), 8);
assert_eq!(MSAElementSize::Halfword.bits(), 16);
assert_eq!(MSAElementSize::Word.bits(), 32);
assert_eq!(MSAElementSize::Dword.bits(), 64);
}
#[test]
fn test_msa_element_size_elements() {
assert_eq!(MSAElementSize::Byte.elements_per_128bit(), 16);
assert_eq!(MSAElementSize::Word.elements_per_128bit(), 4);
}
#[test]
fn test_select_addv() {
let mut isel = MSAInstructionSelector::new(true);
isel.select_addv(0, 1, 2, MSAElementSize::Word);
assert_eq!(isel.selected_ops.len(), 1);
assert_eq!(isel.selected_ops[0].opcode, MSAOpcode::ADDV);
}
#[test]
fn test_select_fadd() {
let mut isel = MSAInstructionSelector::new(true);
isel.select_fadd(0, 1, 2, MSAElementSize::Word);
assert_eq!(isel.selected_ops[0].opcode, MSAOpcode::FADD);
}
#[test]
fn test_select_all_compare() {
let mut isel = MSAInstructionSelector::new(true);
isel.select_ceq(0, 1, 2, MSAElementSize::Word);
isel.select_cle_s(0, 1, 2, MSAElementSize::Word);
isel.select_clt_s(0, 1, 2, MSAElementSize::Word);
isel.select_cle_u(0, 1, 2, MSAElementSize::Word);
isel.select_clt_u(0, 1, 2, MSAElementSize::Word);
assert_eq!(isel.selected_ops.len(), 5);
}
#[test]
fn test_select_logical() {
let mut isel = MSAInstructionSelector::new(true);
isel.select_andv(0, 1, 2);
isel.select_orv(0, 1, 2);
isel.select_xorv(0, 1, 2);
isel.select_norv(0, 1, 2);
assert_eq!(isel.selected_ops.len(), 4);
}
#[test]
fn test_select_shifts() {
let mut isel = MSAInstructionSelector::new(true);
isel.select_sllv(0, 1, 2, MSAElementSize::Word);
isel.select_srlv(0, 1, 2, MSAElementSize::Word);
isel.select_srav(0, 1, 2, MSAElementSize::Word);
assert_eq!(isel.selected_ops.len(), 3);
}
#[test]
fn test_select_bit_ops() {
let mut isel = MSAInstructionSelector::new(true);
isel.select_bclrv(0, 1, 2, MSAElementSize::Word);
isel.select_bsetv(0, 1, 2, MSAElementSize::Word);
isel.select_bnegv(0, 1, 2, MSAElementSize::Word);
assert_eq!(isel.selected_ops.len(), 3);
}
#[test]
fn test_select_permute() {
let mut isel = MSAInstructionSelector::new(true);
isel.select_vshf(0, 1, 2, 3);
isel.select_ilvr(0, 1, 2, MSAElementSize::Word);
isel.select_ilvl(0, 1, 2, MSAElementSize::Word);
isel.select_ilvev(0, 1, 2, MSAElementSize::Word);
isel.select_ilvod(0, 1, 2, MSAElementSize::Word);
isel.select_pckev(0, 1, 2, MSAElementSize::Word);
isel.select_pckod(0, 1, 2, MSAElementSize::Word);
assert_eq!(isel.selected_ops.len(), 7);
}
#[test]
fn test_select_insert_extract() {
let mut isel = MSAInstructionSelector::new(true);
isel.select_insert(0, 1, 0, MSAElementSize::Word);
isel.select_insve(0, 1, 1, MSAElementSize::Word);
isel.select_copy_s(2, 1, 0, MSAElementSize::Word);
isel.select_copy_u(2, 1, 0, MSAElementSize::Word);
isel.select_splat(0, 1, 2, MSAElementSize::Word);
assert_eq!(isel.selected_ops.len(), 5);
}
#[test]
fn test_select_load_store() {
let mut isel = MSAInstructionSelector::new(true);
isel.select_ld(0, 4, 0, MSAElementSize::Word);
isel.select_st(0, 4, 8, MSAElementSize::Word);
assert_eq!(isel.selected_ops.len(), 2);
}
#[test]
fn test_lower_vector_add() {
let mut isel = MSAInstructionSelector::new(true);
lower_vector_add_to_msa(&mut isel, 0, 1, 2, 32, false);
assert_eq!(isel.selected_ops[0].opcode, MSAOpcode::ADDV);
}
#[test]
fn test_lower_vector_float_add() {
let mut isel = MSAInstructionSelector::new(true);
lower_vector_add_to_msa(&mut isel, 0, 1, 2, 32, true);
assert_eq!(isel.selected_ops[0].opcode, MSAOpcode::FADD);
}
#[test]
fn test_lower_vector_cmp() {
let mut isel = MSAInstructionSelector::new(true);
lower_vector_cmp_to_msa(&mut isel, 0, 1, 2, 32, "eq");
assert_eq!(isel.selected_ops[0].opcode, MSAOpcode::CEQ);
}
#[test]
fn test_lower_vector_logical() {
let mut isel = MSAInstructionSelector::new(true);
lower_vector_logical_to_msa(&mut isel, 0, 1, 2, "xor");
assert_eq!(isel.selected_ops[0].opcode, MSAOpcode::XORV);
}
#[test]
fn test_emit_addv_assembly() {
let mut isel = MSAInstructionSelector::new(true);
isel.select_addv(0, 1, 2, MSAElementSize::Word);
let asm = emit_msa_assembly(&isel.selected_ops);
assert!(asm[0].contains("addv.w"));
assert!(asm[0].contains("$w0"));
}
#[test]
fn test_emit_fadd_assembly() {
let mut isel = MSAInstructionSelector::new(true);
isel.select_fadd(0, 1, 2, MSAElementSize::Word);
let asm = emit_msa_assembly(&isel.selected_ops);
assert!(asm[0].contains("fadd.w"));
}
#[test]
fn test_emit_ld_assembly() {
let mut isel = MSAInstructionSelector::new(true);
isel.select_ld(0, 4, 0, MSAElementSize::Word);
let asm = emit_msa_assembly(&isel.selected_ops);
assert!(asm[0].contains("ld.w"));
}
#[test]
fn test_emit_st_assembly() {
let mut isel = MSAInstructionSelector::new(true);
isel.select_st(1, 4, 16, MSAElementSize::Dword);
let asm = emit_msa_assembly(&isel.selected_ops);
assert!(asm[0].contains("st.d"));
}
#[test]
fn test_emit_full_function() {
let mut isel = MSAInstructionSelector::new(true);
isel.select_ld(0, 4, 0, MSAElementSize::Word);
isel.select_ld(1, 4, 16, MSAElementSize::Word);
isel.select_fadd(2, 0, 1, MSAElementSize::Word);
isel.select_st(2, 4, 32, MSAElementSize::Word);
let func = emit_msa_function("msa_vec_add", &isel.selected_ops);
assert!(func.contains("msa_vec_add:"));
assert!(func.contains("fadd.w"));
assert!(func.contains("jr $ra"));
}
#[test]
fn test_msa_all_element_sizes() {
let mut isel = MSAInstructionSelector::new(true);
for es in &[
MSAElementSize::Byte,
MSAElementSize::Halfword,
MSAElementSize::Word,
MSAElementSize::Dword,
] {
isel.select_addv(0, 1, 2, *es);
}
assert_eq!(isel.selected_ops.len(), 4);
}
}