use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum LanaiOpcode {
Add = 22000,
Addc = 22001,
Sub = 22002,
Subb = 22003,
And = 22004,
Or = 22005,
Xor = 22006,
Sh = 22007,
Sel = 22008,
Ld = 22010,
St = 22011,
Ldh = 22012,
Sth = 22013,
Ldb = 22014,
Stb = 22015,
Br = 22020,
Brcc = 22021,
Brc = 22022,
Sa = 22023,
Bt = 22024,
Popc = 22030,
Leadz = 22031,
Trailz = 22032,
AddI = 22040,
AndI = 22041,
OrI = 22042,
XorI = 22043,
ShI = 22044,
LdI = 22045,
LdhI = 22046,
LdbI = 22047,
StI = 22048,
SthI = 22049,
StbI = 22050,
Nop = 22060,
Mov = 22061,
Li = 22062,
J = 22063,
Jal = 22064,
Jalr = 22065,
Ret = 22066,
}
impl LanaiOpcode {
pub fn as_u32(self) -> u32 {
self as u32
}
pub fn is_branch(self) -> bool {
matches!(
self,
LanaiOpcode::Br
| LanaiOpcode::Brcc
| LanaiOpcode::Brc
| LanaiOpcode::Bt
| LanaiOpcode::J
| LanaiOpcode::Jal
| LanaiOpcode::Jalr
)
}
pub fn is_return(self) -> bool {
matches!(self, LanaiOpcode::Ret)
}
pub fn is_load(self) -> bool {
matches!(
self,
LanaiOpcode::Ld
| LanaiOpcode::Ldh
| LanaiOpcode::Ldb
| LanaiOpcode::LdI
| LanaiOpcode::LdhI
| LanaiOpcode::LdbI
)
}
pub fn is_store(self) -> bool {
matches!(
self,
LanaiOpcode::St
| LanaiOpcode::Sth
| LanaiOpcode::Stb
| LanaiOpcode::StI
| LanaiOpcode::SthI
| LanaiOpcode::StbI
)
}
pub fn is_alu(self) -> bool {
matches!(
self,
LanaiOpcode::Add
| LanaiOpcode::Addc
| LanaiOpcode::Sub
| LanaiOpcode::Subb
| LanaiOpcode::And
| LanaiOpcode::Or
| LanaiOpcode::Xor
| LanaiOpcode::Sh
| LanaiOpcode::Sel
| LanaiOpcode::Popc
| LanaiOpcode::Leadz
| LanaiOpcode::Trailz
)
}
pub fn is_pseudo(self) -> bool {
matches!(
self,
LanaiOpcode::Nop
| LanaiOpcode::Mov
| LanaiOpcode::Li
| LanaiOpcode::J
| LanaiOpcode::Jal
| LanaiOpcode::Jalr
| LanaiOpcode::Ret
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LanaiOperandType {
GPR,
SReg,
Imm16,
Disp26,
CC,
Func,
Label,
}
impl LanaiOperandType {
pub fn is_register(self) -> bool {
matches!(self, LanaiOperandType::GPR | LanaiOperandType::SReg)
}
pub fn is_immediate(self) -> bool {
matches!(self, LanaiOperandType::Imm16 | LanaiOperandType::Disp26)
}
}
#[derive(Debug, Clone)]
pub struct LanaiInstrDesc {
pub opcode: LanaiOpcode,
pub mnemonic: &'static str,
pub num_operands: u8,
pub is_terminator: bool,
pub is_branch: bool,
pub is_call: bool,
pub is_return: bool,
pub is_load: bool,
pub is_store: bool,
pub is_compare: bool,
pub has_side_effects: bool,
pub operand_types: &'static [LanaiOperandType],
}
const GPR_GPR_GPR: &[LanaiOperandType] = &[
LanaiOperandType::GPR,
LanaiOperandType::GPR,
LanaiOperandType::GPR,
];
const GPR_GPR: &[LanaiOperandType] = &[LanaiOperandType::GPR, LanaiOperandType::GPR];
const GPR_GPR_IMM: &[LanaiOperandType] = &[
LanaiOperandType::GPR,
LanaiOperandType::GPR,
LanaiOperandType::Imm16,
];
const GPR_IMM: &[LanaiOperandType] = &[LanaiOperandType::GPR, LanaiOperandType::Imm16];
const LABEL: &[LanaiOperandType] = &[LanaiOperandType::Label];
const CC_LABEL: &[LanaiOperandType] = &[LanaiOperandType::CC, LanaiOperandType::Label];
pub struct LanaiInstrInfo {
descriptors: HashMap<LanaiOpcode, LanaiInstrDesc>,
by_mnemonic: HashMap<String, LanaiOpcode>,
}
impl LanaiInstrInfo {
pub fn new() -> Self {
let mut info = LanaiInstrInfo {
descriptors: HashMap::with_capacity(64),
by_mnemonic: HashMap::with_capacity(64),
};
info.init_descriptors();
info
}
fn add(&mut self, desc: LanaiInstrDesc) {
self.by_mnemonic
.insert(desc.mnemonic.to_string(), desc.opcode);
self.descriptors.insert(desc.opcode, desc);
}
fn init_descriptors(&mut self) {
use LanaiOpcode::*;
use LanaiOperandType::*;
self.add(i(Add, "add", 3, GPR_GPR_GPR));
self.add(i(Addc, "addc", 3, GPR_GPR_GPR));
self.add(i(Sub, "sub", 3, GPR_GPR_GPR));
self.add(i(Subb, "subb", 3, GPR_GPR_GPR));
self.add(i(And, "and", 3, GPR_GPR_GPR));
self.add(i(Or, "or", 3, GPR_GPR_GPR));
self.add(i(Xor, "xor", 3, GPR_GPR_GPR));
self.add(i(Sh, "sh", 3, GPR_GPR_GPR));
self.add(i_comp(Sel, "sel", 3, GPR_GPR_GPR));
self.add(load(Ld, "ld"));
self.add(store(St, "st"));
self.add(load(Ldh, "ldh"));
self.add(store(Sth, "sth"));
self.add(load(Ldb, "ldb"));
self.add(store(Stb, "stb"));
self.add(term(Br, "br", 1, LABEL));
self.add(term_cc(Brcc, "brcc"));
self.add(term_cc(Brc, "brc"));
self.add(i(Sa, "sa", 2, GPR_GPR));
self.add(i(Bt, "bt", 2, GPR_GPR));
self.add(i(Popc, "popc", 2, GPR_GPR));
self.add(i(Leadz, "leadz", 2, GPR_GPR));
self.add(i(Trailz, "trailz", 2, GPR_GPR));
self.add(i(AddI, "add_i", 3, GPR_GPR_IMM));
self.add(i(AndI, "and_i", 3, GPR_GPR_IMM));
self.add(i(OrI, "or_i", 3, GPR_GPR_IMM));
self.add(i(XorI, "xor_i", 3, GPR_GPR_IMM));
self.add(i(ShI, "sh_i", 3, GPR_GPR_IMM));
self.add(load(LdI, "ld_i"));
self.add(load(LdhI, "ldh_i"));
self.add(load(LdbI, "ldb_i"));
self.add(store(StI, "st_i"));
self.add(store(SthI, "sth_i"));
self.add(store(StbI, "stb_i"));
self.add(i(Nop, "nop", 0, &[]));
self.add(i(Mov, "mov", 2, GPR_GPR));
self.add(i(Li, "li", 2, GPR_IMM));
self.add(pseudo_branch(J, "j"));
self.add(pseudo_call(Jal, "jal"));
self.add(pseudo_call(Jalr, "jalr"));
self.add(term_ret(Ret, "ret"));
}
pub fn get(&self, opcode: LanaiOpcode) -> Option<&LanaiInstrDesc> {
self.descriptors.get(&opcode)
}
pub fn get_mnemonic(&self, opcode: LanaiOpcode) -> &'static str {
self.descriptors
.get(&opcode)
.map(|d| d.mnemonic)
.unwrap_or("INVALID")
}
pub fn find_by_mnemonic(&self, mnemonic: &str) -> Option<LanaiOpcode> {
self.by_mnemonic.get(mnemonic).copied()
}
pub fn is_terminator(&self, opcode: LanaiOpcode) -> bool {
self.descriptors
.get(&opcode)
.map(|d| d.is_terminator)
.unwrap_or(false)
}
pub fn may_load(&self, opcode: LanaiOpcode) -> bool {
self.descriptors
.get(&opcode)
.map(|d| d.is_load)
.unwrap_or(false)
}
pub fn may_store(&self, opcode: LanaiOpcode) -> bool {
self.descriptors
.get(&opcode)
.map(|d| d.is_store)
.unwrap_or(false)
}
}
impl Default for LanaiInstrInfo {
fn default() -> Self {
Self::new()
}
}
fn i(
opcode: LanaiOpcode,
mnemonic: &'static str,
num_operands: u8,
operand_types: &'static [LanaiOperandType],
) -> LanaiInstrDesc {
LanaiInstrDesc {
opcode,
mnemonic,
num_operands,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
is_load: false,
is_store: false,
is_compare: false,
has_side_effects: false,
operand_types,
}
}
fn i_comp(
opcode: LanaiOpcode,
mnemonic: &'static str,
num_operands: u8,
operand_types: &'static [LanaiOperandType],
) -> LanaiInstrDesc {
LanaiInstrDesc {
opcode,
mnemonic,
num_operands,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
is_load: false,
is_store: false,
is_compare: true,
has_side_effects: false,
operand_types,
}
}
fn load(opcode: LanaiOpcode, mnemonic: &'static str) -> LanaiInstrDesc {
LanaiInstrDesc {
opcode,
mnemonic,
num_operands: 2,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
is_load: true,
is_store: false,
is_compare: false,
has_side_effects: true,
operand_types: GPR_GPR,
}
}
fn store(opcode: LanaiOpcode, mnemonic: &'static str) -> LanaiInstrDesc {
LanaiInstrDesc {
opcode,
mnemonic,
num_operands: 2,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
is_load: false,
is_store: true,
is_compare: false,
has_side_effects: true,
operand_types: GPR_GPR,
}
}
fn term(
opcode: LanaiOpcode,
mnemonic: &'static str,
num_operands: u8,
operand_types: &'static [LanaiOperandType],
) -> LanaiInstrDesc {
LanaiInstrDesc {
opcode,
mnemonic,
num_operands,
is_terminator: true,
is_branch: true,
is_call: false,
is_return: false,
is_load: false,
is_store: false,
is_compare: false,
has_side_effects: true,
operand_types,
}
}
fn term_cc(opcode: LanaiOpcode, mnemonic: &'static str) -> LanaiInstrDesc {
LanaiInstrDesc {
opcode,
mnemonic,
num_operands: 2,
is_terminator: true,
is_branch: true,
is_call: false,
is_return: false,
is_load: false,
is_store: false,
is_compare: false,
has_side_effects: true,
operand_types: CC_LABEL,
}
}
fn term_ret(opcode: LanaiOpcode, mnemonic: &'static str) -> LanaiInstrDesc {
LanaiInstrDesc {
opcode,
mnemonic,
num_operands: 0,
is_terminator: true,
is_branch: false,
is_call: false,
is_return: true,
is_load: false,
is_store: false,
is_compare: false,
has_side_effects: true,
operand_types: &[],
}
}
fn pseudo_branch(opcode: LanaiOpcode, mnemonic: &'static str) -> LanaiInstrDesc {
LanaiInstrDesc {
opcode,
mnemonic,
num_operands: 1,
is_terminator: true,
is_branch: true,
is_call: false,
is_return: false,
is_load: false,
is_store: false,
is_compare: false,
has_side_effects: true,
operand_types: LABEL,
}
}
fn pseudo_call(opcode: LanaiOpcode, mnemonic: &'static str) -> LanaiInstrDesc {
LanaiInstrDesc {
opcode,
mnemonic,
num_operands: 1,
is_terminator: false,
is_branch: false,
is_call: true,
is_return: false,
is_load: false,
is_store: false,
is_compare: false,
has_side_effects: true,
operand_types: LABEL,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_mnemonic() {
let info = LanaiInstrInfo::new();
assert_eq!(info.get_mnemonic(LanaiOpcode::Add), "add");
assert_eq!(info.get_mnemonic(LanaiOpcode::Nop), "nop");
assert_eq!(info.get_mnemonic(LanaiOpcode::Ret), "ret");
}
#[test]
fn test_find_by_mnemonic() {
let info = LanaiInstrInfo::new();
assert_eq!(info.find_by_mnemonic("add"), Some(LanaiOpcode::Add));
assert_eq!(info.find_by_mnemonic("j"), Some(LanaiOpcode::J));
assert_eq!(info.find_by_mnemonic("mov"), Some(LanaiOpcode::Mov));
}
#[test]
fn test_terminators() {
let info = LanaiInstrInfo::new();
assert!(info.is_terminator(LanaiOpcode::Br));
assert!(info.is_terminator(LanaiOpcode::Ret));
assert!(!info.is_terminator(LanaiOpcode::Add));
}
#[test]
fn test_load_store() {
let info = LanaiInstrInfo::new();
assert!(info.may_load(LanaiOpcode::Ld));
assert!(info.may_store(LanaiOpcode::St));
assert!(!info.may_load(LanaiOpcode::Add));
}
}