use super::riscv_instr_info::RiscVOpcode;
use super::riscv_register_info::*;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RiscVVectorOpcode {
VAddVV,
VAddVX,
VAddVI,
VSubVV,
VSubVX,
VMulVV,
VMulVX,
VDivVV,
VDivVX,
VRSubVV,
VRSubVX,
VAndVV,
VAndVX,
VAndVI,
VOrVV,
VOrVX,
VOrVI,
VXorVV,
VXorVX,
VXorVI,
VSllVV,
VSllVX,
VSllVI,
VSrlVV,
VSrlVX,
VSrlVI,
VSraVV,
VSraVX,
VSraVI,
VMinVV,
VMinVX,
VMaxVV,
VMaxVX,
VMinuVV,
VMaxuVV,
VFAddVV,
VFAddVF,
VFSubVV,
VFSubVF,
VFMulVV,
VFMulVF,
VFDivVV,
VFDivVF,
VFSqrtV,
VFMAddVV,
VFMAddVF,
VFNMAddVV,
VFNMAddVF,
VFMSubVV,
VFMSubVF,
VFNMSubVV,
VFNMSubVF,
VFMinVV,
VFMinVF,
VFMaxVV,
VFMaxVF,
VFSgnjVV,
VFSgnjVF,
VFSgnjnVV,
VFSgnjxVV,
VMSeqVV,
VMSeqVX,
VMSeqVI,
VMSneVV,
VMSneVX,
VMSneVI,
VMSltVV,
VMSltVX,
VMSltuVV,
VMSltuVX,
VMSleVV,
VMSleVX,
VMSleVI,
VMSleuVV,
VMSgtVV,
VMSgtVX,
VMSgtuVV,
VMFEqVV,
VMFEqVF,
VMFNeVV,
VMFNeVF,
VMFLtVV,
VMFLtVF,
VMFLeVV,
VMFLeVF,
VMFGtVV,
VMFGtVF,
VMFGeVV,
VMFGeVF,
VRedSumVS,
VRedMaxVS,
VRedMinVS,
VRedAndVS,
VRedOrVS,
VRedXorVS,
VRedMaxuVS,
VRedMinuVS,
VFredosumVS,
VFredusumVS,
VFredmaxVS,
VFredminVS,
VSlideupVX,
VSlideupVI,
VSlidedownVX,
VSlidedownVI,
VSlide1upVX,
VSlide1downVX,
VFSlide1upVF,
VFSlide1downVF,
VLuxei8V,
VLuxei16V,
VLuxei32V,
VLuxei64V,
VLoxei8V,
VLoxei16V,
VLoxei32V,
VLoxei64V,
VSuxei8V,
VSuxei16V,
VSuxei32V,
VSuxei64V,
VSoxei8V,
VSoxei16V,
VSoxei32V,
VSoxei64V,
VCompressVM,
VRGatherVV,
VRGatherVX,
VRGatherei16VV,
VMvVV,
VMvVX,
VMvVI,
VMergeVVM,
VMergeVXM,
VMergeVIM,
VMVnV,
VMandMM,
VMnandMM,
VMandnMM,
VMxorMM,
VMorMM,
VMnorMM,
VMornMM,
VMxnorMM,
VMMvM,
VMCpyM,
VMClrM,
VMSetM,
VMNotM,
VMSbfM,
VMSifM,
VMsofM,
VSextVF2,
VSextVF4,
VSextVF8,
VZextVF2,
VZextVF4,
VZextVF8,
VFcvt_xu_fV,
VFcvt_x_fV,
VFcvt_rtz_xu_fV,
VFcvt_rtz_x_fV,
VFcvt_f_xuV,
VFcvt_f_xV,
VFcvt_f_fV,
VFwcvt_xu_fV,
VFwcvt_x_fV,
VFwcvt_rtz_xu_fV,
VFwcvt_rtz_x_fV,
VFwcvt_f_xuV,
VFwcvt_f_xV,
VFwcvt_f_fV,
VFncvt_xu_fV,
VFncvt_x_fV,
VFncvt_rtz_xu_fV,
VFncvt_rtz_x_fV,
VFncvt_f_xuV,
VFncvt_f_xV,
VFncvt_f_fV,
VFncvt_rod_f_fV,
}
impl RiscVVectorOpcode {
pub fn mnemonic(&self) -> &'static str {
match self {
RiscVVectorOpcode::VAddVV | RiscVVectorOpcode::VAddVX | RiscVVectorOpcode::VAddVI => {
"vadd"
}
RiscVVectorOpcode::VSubVV | RiscVVectorOpcode::VSubVX => "vsub",
RiscVVectorOpcode::VMulVV | RiscVVectorOpcode::VMulVX => "vmul",
RiscVVectorOpcode::VDivVV | RiscVVectorOpcode::VDivVX => "vdiv",
RiscVVectorOpcode::VRSubVV | RiscVVectorOpcode::VRSubVX => "vrsub",
RiscVVectorOpcode::VAndVV | RiscVVectorOpcode::VAndVX | RiscVVectorOpcode::VAndVI => {
"vand"
}
RiscVVectorOpcode::VOrVV | RiscVVectorOpcode::VOrVX | RiscVVectorOpcode::VOrVI => "vor",
RiscVVectorOpcode::VXorVV | RiscVVectorOpcode::VXorVX | RiscVVectorOpcode::VXorVI => {
"vxor"
}
RiscVVectorOpcode::VSllVV | RiscVVectorOpcode::VSllVX | RiscVVectorOpcode::VSllVI => {
"vsll"
}
RiscVVectorOpcode::VSrlVV | RiscVVectorOpcode::VSrlVX | RiscVVectorOpcode::VSrlVI => {
"vsrl"
}
RiscVVectorOpcode::VSraVV | RiscVVectorOpcode::VSraVX | RiscVVectorOpcode::VSraVI => {
"vsra"
}
RiscVVectorOpcode::VFAddVV | RiscVVectorOpcode::VFAddVF => "vfadd",
RiscVVectorOpcode::VFSubVV | RiscVVectorOpcode::VFSubVF => "vfsub",
RiscVVectorOpcode::VFMulVV | RiscVVectorOpcode::VFMulVF => "vfmul",
RiscVVectorOpcode::VFDivVV | RiscVVectorOpcode::VFDivVF => "vfdiv",
RiscVVectorOpcode::VFSqrtV => "vfsqrt",
RiscVVectorOpcode::VFMAddVV | RiscVVectorOpcode::VFMAddVF => "vfmadd",
RiscVVectorOpcode::VFNMAddVV | RiscVVectorOpcode::VFNMAddVF => "vfnmadd",
RiscVVectorOpcode::VFMSubVV | RiscVVectorOpcode::VFMSubVF => "vfmsub",
RiscVVectorOpcode::VFNMSubVV | RiscVVectorOpcode::VFNMSubVF => "vfnmsub",
RiscVVectorOpcode::VMSeqVV
| RiscVVectorOpcode::VMSeqVX
| RiscVVectorOpcode::VMSeqVI => "vmseq",
RiscVVectorOpcode::VMSneVV
| RiscVVectorOpcode::VMSneVX
| RiscVVectorOpcode::VMSneVI => "vmsne",
RiscVVectorOpcode::VMSltVV | RiscVVectorOpcode::VMSltVX => "vmslt",
RiscVVectorOpcode::VMSltuVV | RiscVVectorOpcode::VMSltuVX => "vmsltu",
RiscVVectorOpcode::VMSleVV
| RiscVVectorOpcode::VMSleVX
| RiscVVectorOpcode::VMSleVI => "vmsle",
RiscVVectorOpcode::VMSleuVV => "vmsleu",
RiscVVectorOpcode::VMSgtVV | RiscVVectorOpcode::VMSgtVX => "vmsgt",
RiscVVectorOpcode::VMSgtuVV => "vmsgtu",
RiscVVectorOpcode::VMFEqVV | RiscVVectorOpcode::VMFEqVF => "vmfeq",
RiscVVectorOpcode::VMFNeVV | RiscVVectorOpcode::VMFNeVF => "vmfne",
RiscVVectorOpcode::VMFLtVV | RiscVVectorOpcode::VMFLtVF => "vmflt",
RiscVVectorOpcode::VMFLeVV | RiscVVectorOpcode::VMFLeVF => "vmfle",
RiscVVectorOpcode::VMFGtVV | RiscVVectorOpcode::VMFGtVF => "vmfgt",
RiscVVectorOpcode::VMFGeVV | RiscVVectorOpcode::VMFGeVF => "vmfge",
RiscVVectorOpcode::VRedSumVS => "vredsum",
RiscVVectorOpcode::VRedMaxVS => "vredmax",
RiscVVectorOpcode::VRedMinVS => "vredmin",
RiscVVectorOpcode::VRedAndVS => "vredand",
RiscVVectorOpcode::VRedOrVS => "vredor",
RiscVVectorOpcode::VRedXorVS => "vredxor",
RiscVVectorOpcode::VRedMaxuVS => "vredmaxu",
RiscVVectorOpcode::VRedMinuVS => "vredminu",
RiscVVectorOpcode::VFredosumVS => "vfredosum",
RiscVVectorOpcode::VFredusumVS => "vfredusum",
RiscVVectorOpcode::VFredmaxVS => "vfredmax",
RiscVVectorOpcode::VFredminVS => "vfredmin",
RiscVVectorOpcode::VSlideupVX | RiscVVectorOpcode::VSlideupVI => "vslideup",
RiscVVectorOpcode::VSlidedownVX | RiscVVectorOpcode::VSlidedownVI => "vslidedown",
RiscVVectorOpcode::VSlide1upVX => "vslide1up",
RiscVVectorOpcode::VSlide1downVX => "vslide1down",
RiscVVectorOpcode::VFSlide1upVF => "vfslide1up",
RiscVVectorOpcode::VFSlide1downVF => "vfslide1down",
RiscVVectorOpcode::VLuxei8V => "vluxei8",
RiscVVectorOpcode::VLuxei16V => "vluxei16",
RiscVVectorOpcode::VLuxei32V => "vluxei32",
RiscVVectorOpcode::VLuxei64V => "vluxei64",
RiscVVectorOpcode::VLoxei8V => "vloxei8",
RiscVVectorOpcode::VLoxei16V => "vloxei16",
RiscVVectorOpcode::VLoxei32V => "vloxei32",
RiscVVectorOpcode::VLoxei64V => "vloxei64",
RiscVVectorOpcode::VSuxei8V => "vsuxei8",
RiscVVectorOpcode::VSuxei16V => "vsuxei16",
RiscVVectorOpcode::VSuxei32V => "vsuxei32",
RiscVVectorOpcode::VSuxei64V => "vsuxei64",
RiscVVectorOpcode::VSoxei8V => "vsoxei8",
RiscVVectorOpcode::VSoxei16V => "vsoxei16",
RiscVVectorOpcode::VSoxei32V => "vsoxei32",
RiscVVectorOpcode::VSoxei64V => "vsoxei64",
RiscVVectorOpcode::VCompressVM => "vcompress",
RiscVVectorOpcode::VRGatherVV | RiscVVectorOpcode::VRGatherVX => "vrgather",
RiscVVectorOpcode::VRGatherei16VV => "vrgatherei16",
RiscVVectorOpcode::VMvVV | RiscVVectorOpcode::VMvVX | RiscVVectorOpcode::VMvVI => "vmv",
RiscVVectorOpcode::VMergeVVM
| RiscVVectorOpcode::VMergeVXM
| RiscVVectorOpcode::VMergeVIM => "vmerge",
RiscVVectorOpcode::VMVnV => "vmvn",
RiscVVectorOpcode::VMandMM
| RiscVVectorOpcode::VMnandMM
| RiscVVectorOpcode::VMandnMM => "vmand",
RiscVVectorOpcode::VMorMM | RiscVVectorOpcode::VMnorMM | RiscVVectorOpcode::VMornMM => {
"vmor"
}
RiscVVectorOpcode::VMxorMM | RiscVVectorOpcode::VMxnorMM => "vmxor",
RiscVVectorOpcode::VMMvM | RiscVVectorOpcode::VMCpyM => "vmmv",
RiscVVectorOpcode::VMClrM => "vmclr",
RiscVVectorOpcode::VMSetM => "vmset",
RiscVVectorOpcode::VMNotM => "vmnot",
RiscVVectorOpcode::VMSbfM => "vmsbf",
RiscVVectorOpcode::VMSifM => "vmsif",
RiscVVectorOpcode::VMsofM => "vmsof",
RiscVVectorOpcode::VSextVF2
| RiscVVectorOpcode::VSextVF4
| RiscVVectorOpcode::VSextVF8 => "vsext",
RiscVVectorOpcode::VZextVF2
| RiscVVectorOpcode::VZextVF4
| RiscVVectorOpcode::VZextVF8 => "vzext",
_ => "v???",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RVVSEW {
E8 = 8,
E16 = 16,
E32 = 32,
E64 = 64,
}
impl RVVSEW {
pub fn bits(&self) -> u32 {
*self as u32
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RVVLMUL {
MF8, MF4, MF2, M1, M2, M4, M8, }
impl RVVLMUL {
pub fn fraction(&self) -> f64 {
match self {
RVVLMUL::MF8 => 0.125,
RVVLMUL::MF4 => 0.25,
RVVLMUL::MF2 => 0.5,
RVVLMUL::M1 => 1.0,
RVVLMUL::M2 => 2.0,
RVVLMUL::M4 => 4.0,
RVVLMUL::M8 => 8.0,
}
}
pub fn encoding(&self) -> u32 {
match self {
RVVLMUL::MF8 => 5,
RVVLMUL::MF4 => 6,
RVVLMUL::MF2 => 7,
RVVLMUL::M1 => 0,
RVVLMUL::M2 => 1,
RVVLMUL::M4 => 2,
RVVLMUL::M8 => 3,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RVVConfig {
pub sew: RVVSEW,
pub lmul: RVVLMUL,
pub vl: u64, pub vlen: u64, pub tail_agnostic: bool,
pub mask_agnostic: bool,
pub masked: bool, }
impl RVVConfig {
pub fn new(vlen_bits: u64) -> Self {
Self {
sew: RVVSEW::E32,
lmul: RVVLMUL::M1,
vl: vlen_bits / 32,
vlen: vlen_bits,
tail_agnostic: true,
mask_agnostic: true,
masked: false,
}
}
pub fn with_sew(mut self, sew: RVVSEW) -> Self {
self.sew = sew;
self.vl = (self.lmul.fraction() * (self.vlen as f64 / sew.bits() as f64)) as u64;
self
}
pub fn with_lmul(mut self, lmul: RVVLMUL) -> Self {
self.lmul = lmul;
self.vl = (lmul.fraction() * (self.vlen as f64 / self.sew.bits() as f64)) as u64;
self
}
pub fn with_mask(mut self) -> Self {
self.masked = true;
self
}
pub fn vreg_bits(&self) -> u64 {
(self.vlen as f64 * self.lmul.fraction()) as u64
}
pub fn is_valid(&self) -> bool {
let vlmax = (self.vlen as f64 * self.lmul.fraction() / self.sew.bits() as f64) as u64;
self.vl > 0 && self.vl <= vlmax
}
}
#[derive(Debug, Clone)]
pub struct RVVISelResult {
pub opcode: RiscVVectorOpcode,
pub dest_reg: u32,
pub src1_reg: u32,
pub src2_reg: u32,
pub mask_reg: Option<u32>,
pub config: RVVConfig,
pub immediate: Option<i64>,
}
pub struct RiscVVectorISel {
patterns: Vec<VectorISelPattern>,
vlen_bits: u64,
has_v_extension: bool,
config: RVVConfig,
vreg_map: HashMap<u32, u32>,
}
#[derive(Debug, Clone)]
struct VectorISelPattern {
ir_opcode: u32,
type_width: u32,
is_fp: bool,
scalar_op2: bool,
imm_op2: bool,
rvv_opcode: RiscVVectorOpcode,
}
impl RiscVVectorISel {
pub fn new(vlen_bits: u64, has_v_extension: bool) -> Self {
let mut patterns = vec![];
Self::register_integer_arith_patterns(&mut patterns);
Self::register_integer_logical_patterns(&mut patterns);
Self::register_integer_shift_patterns(&mut patterns);
Self::register_integer_compare_patterns(&mut patterns);
Self::register_fp_arith_patterns(&mut patterns);
Self::register_fp_compare_patterns(&mut patterns);
Self::register_fp_fma_patterns(&mut patterns);
Self::register_reduction_patterns(&mut patterns);
Self::register_slide_patterns(&mut patterns);
Self::register_gather_scatter_patterns(&mut patterns);
Self::register_permute_patterns(&mut patterns);
Self::register_mask_patterns(&mut patterns);
Self::register_conversion_patterns(&mut patterns);
Self {
patterns,
vlen_bits,
has_v_extension,
config: RVVConfig::new(vlen_bits),
vreg_map: HashMap::new(),
}
}
fn register_integer_arith_patterns(patterns: &mut Vec<VectorISelPattern>) {
let int_widths = [8, 16, 32, 64];
for &w in &int_widths {
patterns.push(VectorISelPattern {
ir_opcode: 0x01,
type_width: w,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VAddVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x02,
type_width: w,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VSubVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x03,
type_width: w,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VMulVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x01,
type_width: w,
is_fp: false,
scalar_op2: true,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VAddVX,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x02,
type_width: w,
is_fp: false,
scalar_op2: true,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VSubVX,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x01,
type_width: w,
is_fp: false,
scalar_op2: false,
imm_op2: true,
rvv_opcode: RiscVVectorOpcode::VAddVI,
});
}
}
fn register_integer_logical_patterns(patterns: &mut Vec<VectorISelPattern>) {
let int_widths = [8, 16, 32, 64];
for &w in &int_widths {
patterns.push(VectorISelPattern {
ir_opcode: 0x10,
type_width: w,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VAndVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x11,
type_width: w,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VOrVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x12,
type_width: w,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VXorVV,
});
}
}
fn register_integer_shift_patterns(patterns: &mut Vec<VectorISelPattern>) {
let int_widths = [8, 16, 32, 64];
for &w in &int_widths {
patterns.push(VectorISelPattern {
ir_opcode: 0x20,
type_width: w,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VSllVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x21,
type_width: w,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VSrlVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x22,
type_width: w,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VSraVV,
});
}
}
fn register_integer_compare_patterns(patterns: &mut Vec<VectorISelPattern>) {
let int_widths = [8, 16, 32, 64];
for &w in &int_widths {
patterns.push(VectorISelPattern {
ir_opcode: 0x30,
type_width: w,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VMSeqVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x31,
type_width: w,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VMSneVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x32,
type_width: w,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VMSltVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x33,
type_width: w,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VMSltuVV,
});
}
}
fn register_fp_arith_patterns(patterns: &mut Vec<VectorISelPattern>) {
let fp_widths = [16, 32, 64];
for &w in &fp_widths {
patterns.push(VectorISelPattern {
ir_opcode: 0x40,
type_width: w,
is_fp: true,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VFAddVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x41,
type_width: w,
is_fp: true,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VFSubVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x42,
type_width: w,
is_fp: true,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VFMulVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x43,
type_width: w,
is_fp: true,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VFDivVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x44,
type_width: w,
is_fp: true,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VFSqrtV,
});
}
}
fn register_fp_compare_patterns(patterns: &mut Vec<VectorISelPattern>) {
let fp_widths = [16, 32, 64];
for &w in &fp_widths {
patterns.push(VectorISelPattern {
ir_opcode: 0x50,
type_width: w,
is_fp: true,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VMFEqVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x51,
type_width: w,
is_fp: true,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VMFNeVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x52,
type_width: w,
is_fp: true,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VMFLtVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x53,
type_width: w,
is_fp: true,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VMFLeVV,
});
}
}
fn register_fp_fma_patterns(patterns: &mut Vec<VectorISelPattern>) {
let fp_widths = [16, 32, 64];
for &w in &fp_widths {
patterns.push(VectorISelPattern {
ir_opcode: 0x60,
type_width: w,
is_fp: true,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VFMAddVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x61,
type_width: w,
is_fp: true,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VFNMAddVV,
});
}
}
fn register_reduction_patterns(patterns: &mut Vec<VectorISelPattern>) {
patterns.push(VectorISelPattern {
ir_opcode: 0x70,
type_width: 32,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VRedSumVS,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x71,
type_width: 32,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VRedMaxVS,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x72,
type_width: 32,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VRedMinVS,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x73,
type_width: 32,
is_fp: true,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VFredusumVS,
});
}
fn register_slide_patterns(patterns: &mut Vec<VectorISelPattern>) {
patterns.push(VectorISelPattern {
ir_opcode: 0x80,
type_width: 32,
is_fp: false,
scalar_op2: true,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VSlideupVX,
});
patterns.push(VectorISelPattern {
ir_opcode: 0x81,
type_width: 32,
is_fp: false,
scalar_op2: true,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VSlidedownVX,
});
}
fn register_gather_scatter_patterns(patterns: &mut Vec<VectorISelPattern>) {
let widths = [8, 16, 32, 64];
for &w in &widths {
patterns.push(VectorISelPattern {
ir_opcode: 0x90,
type_width: w,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: match w {
8 => RiscVVectorOpcode::VLuxei8V,
16 => RiscVVectorOpcode::VLuxei16V,
32 => RiscVVectorOpcode::VLuxei32V,
_ => RiscVVectorOpcode::VLuxei64V,
},
});
patterns.push(VectorISelPattern {
ir_opcode: 0x91,
type_width: w,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: match w {
8 => RiscVVectorOpcode::VSuxei8V,
16 => RiscVVectorOpcode::VSuxei16V,
32 => RiscVVectorOpcode::VSuxei32V,
_ => RiscVVectorOpcode::VSuxei64V,
},
});
}
}
fn register_permute_patterns(patterns: &mut Vec<VectorISelPattern>) {
patterns.push(VectorISelPattern {
ir_opcode: 0xA0,
type_width: 32,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VRGatherVV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0xA1,
type_width: 32,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VMergeVVM,
});
}
fn register_mask_patterns(patterns: &mut Vec<VectorISelPattern>) {
patterns.push(VectorISelPattern {
ir_opcode: 0xB0,
type_width: 1,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VMandMM,
});
patterns.push(VectorISelPattern {
ir_opcode: 0xB1,
type_width: 1,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VMorMM,
});
patterns.push(VectorISelPattern {
ir_opcode: 0xB2,
type_width: 1,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VMxorMM,
});
patterns.push(VectorISelPattern {
ir_opcode: 0xB3,
type_width: 1,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VMNotM,
});
}
fn register_conversion_patterns(patterns: &mut Vec<VectorISelPattern>) {
patterns.push(VectorISelPattern {
ir_opcode: 0xC0,
type_width: 32,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VSextVF2,
});
patterns.push(VectorISelPattern {
ir_opcode: 0xC1,
type_width: 32,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VZextVF2,
});
patterns.push(VectorISelPattern {
ir_opcode: 0xC2,
type_width: 32,
is_fp: true,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VFcvt_x_fV,
});
patterns.push(VectorISelPattern {
ir_opcode: 0xC3,
type_width: 32,
is_fp: false,
scalar_op2: false,
imm_op2: false,
rvv_opcode: RiscVVectorOpcode::VFcvt_f_xV,
});
}
pub fn select(
&self,
ir_opcode: u32,
type_width: u32,
is_fp: bool,
scalar_op2: bool,
imm_op2: bool,
) -> Option<RiscVVectorOpcode> {
if !self.has_v_extension {
return None;
}
for pattern in &self.patterns {
if pattern.ir_opcode == ir_opcode
&& pattern.type_width == type_width
&& pattern.is_fp == is_fp
&& pattern.scalar_op2 == scalar_op2
&& pattern.imm_op2 == imm_op2
{
return Some(pattern.rvv_opcode);
}
}
None
}
pub fn sew_for_width(width: u32) -> Option<RVVSEW> {
match width {
8 => Some(RVVSEW::E8),
16 => Some(RVVSEW::E16),
32 => Some(RVVSEW::E32),
64 => Some(RVVSEW::E64),
_ => None,
}
}
pub fn vtype_encoding(config: &RVVConfig) -> u32 {
let sew_enc = match config.sew {
RVVSEW::E8 => 0,
RVVSEW::E16 => 1,
RVVSEW::E32 => 2,
RVVSEW::E64 => 3,
};
let lmul_enc = config.lmul.encoding();
let ta = if config.tail_agnostic { 1u32 } else { 0 };
let ma = if config.mask_agnostic { 1u32 } else { 0 };
(sew_enc & 0x7) | ((lmul_enc & 0x7) << 3) | (ta << 6) | (ma << 7)
}
pub fn alloc_vreg(&mut self, vreg: u32) -> u32 {
let next = self.vreg_map.len() as u32;
*self.vreg_map.entry(vreg).or_insert(next)
}
pub fn get_vreg(&self, vreg: u32) -> Option<u32> {
self.vreg_map.get(&vreg).copied()
}
pub fn set_config(&mut self, config: RVVConfig) {
self.config = config;
}
pub fn config(&self) -> &RVVConfig {
&self.config
}
pub fn has_v(&self) -> bool {
self.has_v_extension
}
}
impl Default for RiscVVectorISel {
fn default() -> Self {
Self::new(128, true)
}
}
pub struct RVVAsmEmitter;
impl RVVAsmEmitter {
pub fn emit(
opcode: RiscVVectorOpcode,
vd: u32,
vs1: u32,
vs2: u32,
config: &RVVConfig,
mask: Option<u32>,
) -> String {
let mut s = String::new();
if config.masked || mask.is_some() {
s.push_str("v0.t, ");
}
let mnemonic = opcode.mnemonic();
s.push_str(&format!(
"{mnemonic}.v{}{}",
match config.sew {
RVVSEW::E8 => "v",
RVVSEW::E16 => "v",
RVVSEW::E32 => "v",
RVVSEW::E64 => "v",
},
""
));
s.push_str(&format!(" v{vd}, v{vs1}, v{vs2}"));
if config.tail_agnostic {
s.push_str(", ta");
}
if config.mask_agnostic {
s.push_str(", ma");
}
s
}
pub fn emit_vsetvli(vl: u64, config: &RVVConfig) -> String {
format!(
"vsetvli x0, {}, e{}, m{}, ta, ma",
vl,
match config.sew {
RVVSEW::E8 => "8",
RVVSEW::E16 => "16",
RVVSEW::E32 => "32",
RVVSEW::E64 => "64",
},
match config.lmul {
RVVLMUL::MF8 => "f8",
RVVLMUL::MF4 => "f4",
RVVLMUL::MF2 => "f2",
RVVLMUL::M1 => "1",
RVVLMUL::M2 => "2",
RVVLMUL::M4 => "4",
RVVLMUL::M8 => "8",
}
)
}
pub fn emit_vle(
vd: u32,
base_reg: u32,
offset: i64,
sew: RVVSEW,
config: &RVVConfig,
) -> String {
let sew_suffix = match sew {
RVVSEW::E8 => "8",
RVVSEW::E16 => "16",
RVVSEW::E32 => "32",
RVVSEW::E64 => "64",
};
let mut s = format!("vle{sew_suffix}.v v{vd}, ({offset})");
if config.masked {
s.push_str(", v0.t");
}
s
}
pub fn emit_vse(
vs3: u32,
base_reg: u32,
offset: i64,
sew: RVVSEW,
config: &RVVConfig,
) -> String {
let sew_suffix = match sew {
RVVSEW::E8 => "8",
RVVSEW::E16 => "16",
RVVSEW::E32 => "32",
RVVSEW::E64 => "64",
};
let mut s = format!("vse{sew_suffix}.v v{vs3}, ({offset})");
if config.masked {
s.push_str(", v0.t");
}
s
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rvv_isel_add() {
let isel = RiscVVectorISel::new(128, true);
let op = isel.select(0x01, 32, false, false, false);
assert_eq!(op, Some(RiscVVectorOpcode::VAddVV));
}
#[test]
fn test_rvv_isel_add_scalar() {
let isel = RiscVVectorISel::new(128, true);
let op = isel.select(0x01, 32, false, true, false);
assert_eq!(op, Some(RiscVVectorOpcode::VAddVX));
}
#[test]
fn test_rvv_isel_fadd() {
let isel = RiscVVectorISel::new(128, true);
let op = isel.select(0x40, 32, true, false, false);
assert_eq!(op, Some(RiscVVectorOpcode::VFAddVV));
}
#[test]
fn test_rvv_isel_no_v_ext() {
let isel = RiscVVectorISel::new(128, false);
let op = isel.select(0x01, 32, false, false, false);
assert_eq!(op, None);
}
#[test]
fn test_sew_for_width() {
assert_eq!(RiscVVectorISel::sew_for_width(8), Some(RVVSEW::E8));
assert_eq!(RiscVVectorISel::sew_for_width(16), Some(RVVSEW::E16));
assert_eq!(RiscVVectorISel::sew_for_width(32), Some(RVVSEW::E32));
assert_eq!(RiscVVectorISel::sew_for_width(64), Some(RVVSEW::E64));
assert_eq!(RiscVVectorISel::sew_for_width(128), None);
}
#[test]
fn test_rvv_config() {
let config = RVVConfig::new(256);
assert_eq!(config.vlen, 256);
assert_eq!(config.sew, RVVSEW::E32);
assert_eq!(config.vl, 8);
let config2 = RVVConfig::new(256).with_sew(RVVSEW::E16);
assert_eq!(config2.vl, 16); }
#[test]
fn test_rvv_config_with_lmul() {
let config = RVVConfig::new(256)
.with_sew(RVVSEW::E32)
.with_lmul(RVVLMUL::M2);
assert_eq!(config.vl, 16); }
#[test]
fn test_rvv_vtype_encoding() {
let config = RVVConfig::new(128).with_sew(RVVSEW::E32);
let vtype = RiscVVectorISel::vtype_encoding(&config);
assert_eq!(vtype & 0x7, 2); assert_eq!((vtype >> 3) & 0x7, 0); assert_eq!((vtype >> 6) & 0x1, 1); assert_eq!((vtype >> 7) & 0x1, 1); }
#[test]
fn test_rvv_isel_masks() {
let isel = RiscVVectorISel::new(128, true);
let op = isel.select(0xB0, 1, false, false, false);
assert_eq!(op, Some(RiscVVectorOpcode::VMandMM));
let op2 = isel.select(0xB1, 1, false, false, false);
assert_eq!(op2, Some(RiscVVectorOpcode::VMorMM));
}
#[test]
fn test_rvv_isel_reductions() {
let isel = RiscVVectorISel::new(128, true);
let op = isel.select(0x70, 32, false, false, false);
assert_eq!(op, Some(RiscVVectorOpcode::VRedSumVS));
let op2 = isel.select(0x73, 32, true, false, false);
assert_eq!(op2, Some(RiscVVectorOpcode::VFredusumVS));
}
#[test]
fn test_rvv_isel_conversions() {
let isel = RiscVVectorISel::new(128, true);
let op = isel.select(0xC0, 32, false, false, false);
assert_eq!(op, Some(RiscVVectorOpcode::VSextVF2));
let op2 = isel.select(0xC1, 32, false, false, false);
assert_eq!(op2, Some(RiscVVectorOpcode::VZextVF2));
}
#[test]
fn test_vreg_allocation() {
let mut isel = RiscVVectorISel::new(128, true);
let p0 = isel.alloc_vreg(10);
let p1 = isel.alloc_vreg(20);
let p2 = isel.alloc_vreg(10); assert_eq!(p0, 0);
assert_eq!(p1, 1);
assert_eq!(p2, 0);
}
#[test]
fn test_asm_emitter_vsetvli() {
let config = RVVConfig::new(128).with_sew(RVVSEW::E32);
let asm = RVVAsmEmitter::emit_vsetvli(4, &config);
assert!(asm.contains("vsetvli"));
assert!(asm.contains("e32"));
assert!(asm.contains("m1"));
}
#[test]
fn test_asm_emitter_vector_op() {
let config = RVVConfig::new(128).with_sew(RVVSEW::E32);
let asm = RVVAsmEmitter::emit(RiscVVectorOpcode::VAddVV, 1, 2, 3, &config, None);
assert!(asm.contains("vadd"));
assert!(asm.contains("v1"));
}
#[test]
fn test_asm_emitter_masked() {
let mut config = RVVConfig::new(128).with_sew(RVVSEW::E32);
config.masked = true;
let asm = RVVAsmEmitter::emit(RiscVVectorOpcode::VAddVV, 1, 2, 3, &config, Some(0));
assert!(asm.contains("v0.t"));
assert!(asm.contains("vadd"));
}
#[test]
fn test_lmul_encoding() {
assert_eq!(RVVLMUL::MF8.encoding(), 5);
assert_eq!(RVVLMUL::MF4.encoding(), 6);
assert_eq!(RVVLMUL::MF2.encoding(), 7);
assert_eq!(RVVLMUL::M1.encoding(), 0);
assert_eq!(RVVLMUL::M2.encoding(), 1);
assert_eq!(RVVLMUL::M4.encoding(), 2);
assert_eq!(RVVLMUL::M8.encoding(), 3);
}
}