use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SystemzOpcode {
L, ST, LA, LG, STG,
A, S, M, D, AG, SG,
N, O, X, NG, OG, XG,
C, CG, CLI, TM,
J, BRC, BR, BCR,
BAL, BALR, BASR, LGR,
LTGR, NOP, BRK, SPM, EPSW, IPM,
SLL, SRL, SLA, SRA, SLLG, SRLG, SLAG, SRAG,
RLL,
LD, STD, AD, SD, MD, DD, CD, }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SystemzFormat {
RR, RX, RI, RIL, RRE, RXY, SI, RS, RSY, SS, S, }
#[derive(Debug, Clone)]
pub struct SystemzInstrDesc {
pub opcode: SystemzOpcode,
pub mnemonic: &'static str,
pub format: SystemzFormat,
pub is_terminator: bool,
pub is_branch: bool,
pub is_call: bool,
pub is_return: bool,
pub may_load: bool,
pub may_store: bool,
pub has_side_effects: bool,
pub is_compare: bool,
pub is_commutative: bool,
pub size_bytes: u8,
}
pub struct SystemzInstrInfo {
desc_map: HashMap<SystemzOpcode, SystemzInstrDesc>,
}
impl SystemzInstrInfo {
pub fn new() -> Self {
let mut info = SystemzInstrInfo {
desc_map: HashMap::new(),
};
info.register_all();
info
}
pub fn get_desc(&self, opcode: SystemzOpcode) -> Option<&SystemzInstrDesc> {
self.desc_map.get(&opcode)
}
pub fn is_terminator(&self, opcode: SystemzOpcode) -> bool {
self.desc_map
.get(&opcode)
.map_or(false, |d| d.is_terminator)
}
pub fn is_branch(&self, opcode: SystemzOpcode) -> bool {
self.desc_map.get(&opcode).map_or(false, |d| d.is_branch)
}
fn add(&mut self, desc: SystemzInstrDesc) {
self.desc_map.insert(desc.opcode, desc);
}
fn register_all(&mut self) {
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::L,
mnemonic: "l",
format: SystemzFormat::RX,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: true,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::ST,
mnemonic: "st",
format: SystemzFormat::RX,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: true,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::LA,
mnemonic: "la",
format: SystemzFormat::RX,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::LG,
mnemonic: "lg",
format: SystemzFormat::RXY,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: true,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 6,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::STG,
mnemonic: "stg",
format: SystemzFormat::RXY,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: true,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 6,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::A,
mnemonic: "a",
format: SystemzFormat::RX,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: true,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::S,
mnemonic: "s",
format: SystemzFormat::RX,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::M,
mnemonic: "m",
format: SystemzFormat::RX,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: true,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::D,
mnemonic: "d",
format: SystemzFormat::RX,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::AG,
mnemonic: "ag",
format: SystemzFormat::RXY,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: true,
size_bytes: 6,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::SG,
mnemonic: "sg",
format: SystemzFormat::RXY,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 6,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::N,
mnemonic: "n",
format: SystemzFormat::RX,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: true,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::O,
mnemonic: "o",
format: SystemzFormat::RX,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: true,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::X,
mnemonic: "x",
format: SystemzFormat::RX,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: true,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::NG,
mnemonic: "ng",
format: SystemzFormat::RXY,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: true,
size_bytes: 6,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::OG,
mnemonic: "og",
format: SystemzFormat::RXY,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: true,
size_bytes: 6,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::XG,
mnemonic: "xg",
format: SystemzFormat::RXY,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: true,
size_bytes: 6,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::C,
mnemonic: "c",
format: SystemzFormat::RX,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: true,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::CG,
mnemonic: "cg",
format: SystemzFormat::RXY,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: true,
is_commutative: false,
size_bytes: 6,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::CLI,
mnemonic: "cli",
format: SystemzFormat::SI,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: true,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::TM,
mnemonic: "tm",
format: SystemzFormat::SI,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: true,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::J,
mnemonic: "j",
format: SystemzFormat::RX,
is_terminator: true,
is_branch: true,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::BRC,
mnemonic: "brc",
format: SystemzFormat::RI,
is_terminator: false,
is_branch: true,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::BR,
mnemonic: "br",
format: SystemzFormat::RR,
is_terminator: true,
is_branch: true,
is_call: false,
is_return: true,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 2,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::BCR,
mnemonic: "bcr",
format: SystemzFormat::RR,
is_terminator: false,
is_branch: true,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 2,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::BAL,
mnemonic: "bal",
format: SystemzFormat::RX,
is_terminator: false,
is_branch: false,
is_call: true,
is_return: false,
may_load: false,
may_store: true,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::BALR,
mnemonic: "balr",
format: SystemzFormat::RR,
is_terminator: false,
is_branch: false,
is_call: true,
is_return: false,
may_load: false,
may_store: true,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 2,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::BASR,
mnemonic: "basr",
format: SystemzFormat::RR,
is_terminator: false,
is_branch: false,
is_call: true,
is_return: false,
may_load: false,
may_store: true,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 2,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::LGR,
mnemonic: "lgr",
format: SystemzFormat::RRE,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::LTGR,
mnemonic: "ltgr",
format: SystemzFormat::RRE,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: true,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::NOP,
mnemonic: "nop",
format: SystemzFormat::RR,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 2,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::BRK,
mnemonic: "brk",
format: SystemzFormat::RR,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: true,
is_compare: false,
is_commutative: false,
size_bytes: 2,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::SPM,
mnemonic: "spm",
format: SystemzFormat::RR,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: true,
is_compare: false,
is_commutative: false,
size_bytes: 2,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::SLL,
mnemonic: "sll",
format: SystemzFormat::RS,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::SRL,
mnemonic: "srl",
format: SystemzFormat::RS,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::SLA,
mnemonic: "sla",
format: SystemzFormat::RS,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::SRA,
mnemonic: "sra",
format: SystemzFormat::RS,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::SLLG,
mnemonic: "sllg",
format: SystemzFormat::RSY,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 6,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::SRLG,
mnemonic: "srlg",
format: SystemzFormat::RSY,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 6,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::LD,
mnemonic: "ld",
format: SystemzFormat::RX,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: true,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::STD,
mnemonic: "std",
format: SystemzFormat::RX,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: true,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::AD,
mnemonic: "ad",
format: SystemzFormat::RX,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: true,
size_bytes: 4,
});
self.add(SystemzInstrDesc {
opcode: SystemzOpcode::SD,
mnemonic: "sd",
format: SystemzFormat::RX,
is_terminator: false,
is_branch: false,
is_call: false,
is_return: false,
may_load: false,
may_store: false,
has_side_effects: false,
is_compare: false,
is_commutative: false,
size_bytes: 4,
});
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_all_opcodes_registered() {
let info = SystemzInstrInfo::new();
assert!(info.get_desc(SystemzOpcode::L).is_some());
assert!(info.get_desc(SystemzOpcode::LA).is_some());
assert!(info.get_desc(SystemzOpcode::NOP).is_some());
}
#[test]
fn test_terminators() {
let info = SystemzInstrInfo::new();
assert!(info.is_terminator(SystemzOpcode::J));
assert!(info.is_terminator(SystemzOpcode::BR));
assert!(!info.is_terminator(SystemzOpcode::L));
}
#[test]
fn test_branches() {
let info = SystemzInstrInfo::new();
assert!(info.is_branch(SystemzOpcode::J));
assert!(info.is_branch(SystemzOpcode::BRC));
assert!(info.is_branch(SystemzOpcode::BR));
assert!(!info.is_branch(SystemzOpcode::A));
}
#[test]
fn test_calls() {
let info = SystemzInstrInfo::new();
assert!(info.get_desc(SystemzOpcode::BAL).unwrap().is_call);
assert!(info.get_desc(SystemzOpcode::BASR).unwrap().is_call);
assert!(!info.get_desc(SystemzOpcode::L).unwrap().is_call);
}
#[test]
fn test_loads_stores() {
let info = SystemzInstrInfo::new();
assert!(info.get_desc(SystemzOpcode::L).unwrap().may_load);
assert!(!info.get_desc(SystemzOpcode::L).unwrap().may_store);
assert!(info.get_desc(SystemzOpcode::ST).unwrap().may_store);
assert!(!info.get_desc(SystemzOpcode::ST).unwrap().may_load);
}
#[test]
fn test_instruction_sizes() {
let info = SystemzInstrInfo::new();
assert_eq!(info.get_desc(SystemzOpcode::BR).unwrap().size_bytes, 2);
assert_eq!(info.get_desc(SystemzOpcode::L).unwrap().size_bytes, 4);
assert_eq!(info.get_desc(SystemzOpcode::LG).unwrap().size_bytes, 6);
}
#[test]
fn test_instruction_formats() {
let info = SystemzInstrInfo::new();
assert_eq!(
info.get_desc(SystemzOpcode::BR).unwrap().format,
SystemzFormat::RR
);
assert_eq!(
info.get_desc(SystemzOpcode::L).unwrap().format,
SystemzFormat::RX
);
assert_eq!(
info.get_desc(SystemzOpcode::BRC).unwrap().format,
SystemzFormat::RI
);
assert_eq!(
info.get_desc(SystemzOpcode::LG).unwrap().format,
SystemzFormat::RXY
);
}
#[test]
fn test_commutative_ops() {
let info = SystemzInstrInfo::new();
assert!(info.get_desc(SystemzOpcode::A).unwrap().is_commutative);
assert!(!info.get_desc(SystemzOpcode::S).unwrap().is_commutative);
assert!(info.get_desc(SystemzOpcode::N).unwrap().is_commutative);
}
#[test]
fn test_compare_ops() {
let info = SystemzInstrInfo::new();
assert!(info.get_desc(SystemzOpcode::C).unwrap().is_compare);
assert!(info.get_desc(SystemzOpcode::CLI).unwrap().is_compare);
assert!(info.get_desc(SystemzOpcode::TM).unwrap().is_compare);
}
#[test]
fn test_side_effects() {
let info = SystemzInstrInfo::new();
assert!(info.get_desc(SystemzOpcode::SPM).unwrap().has_side_effects);
assert!(info.get_desc(SystemzOpcode::BRK).unwrap().has_side_effects);
assert!(!info.get_desc(SystemzOpcode::L).unwrap().has_side_effects);
}
#[test]
fn test_format_enum_variants() {
assert_ne!(SystemzFormat::RR, SystemzFormat::RX);
assert_ne!(SystemzFormat::RX, SystemzFormat::RXY);
assert_ne!(SystemzFormat::RI, SystemzFormat::RIL);
}
}