use crate::systemz::systemz_instr_info::{SystemzInstrInfo, SystemzOpcode};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SystemzSelOpcode {
Add,
Sub,
Mul,
SDiv,
UDiv,
SRem,
URem,
And,
Or,
Xor,
Shl,
Sra,
Srl,
Add64,
Sub64,
And64,
Or64,
Xor64,
Load,
Load8,
Load16,
Load32,
Load64,
Store,
Store8,
Store16,
Store32,
Store64,
LoadAddr,
Br,
BrCond,
Call,
Return,
Copy,
CopyToReg,
Constant32,
Constant64,
Setcc,
FrameIndex,
Cmp,
Cmp64,
}
#[derive(Debug, Clone)]
pub enum SystemzOperand {
Reg(u8), FpReg(u8), Imm16(i16),
Imm32(i32),
Imm64(i64),
Addr {
base: u8,
disp: i32,
index: Option<u8>,
},
Label(String),
CC, }
#[derive(Debug, Clone)]
pub struct SystemzSelNode {
pub opcode: SystemzSelOpcode,
pub operands: Vec<SystemzOperand>,
pub result_reg: Option<u8>,
}
pub struct SystemzInstructionSelector {
instr_info: SystemzInstrInfo,
}
impl SystemzInstructionSelector {
pub fn new() -> Self {
SystemzInstructionSelector {
instr_info: SystemzInstrInfo::new(),
}
}
pub fn select(&self, nodes: &[SystemzSelNode]) -> Vec<SystemzOpcode> {
let mut selected = Vec::new();
for node in nodes {
if let Some(opcodes) = self.select_node(node) {
selected.extend(opcodes);
}
}
selected
}
fn select_node(&self, node: &SystemzSelNode) -> Option<Vec<SystemzOpcode>> {
match node.opcode {
SystemzSelOpcode::Add => Some(vec![SystemzOpcode::A]),
SystemzSelOpcode::Sub => Some(vec![SystemzOpcode::S]),
SystemzSelOpcode::Mul => Some(vec![SystemzOpcode::M]),
SystemzSelOpcode::UDiv | SystemzSelOpcode::SDiv => Some(vec![SystemzOpcode::D]),
SystemzSelOpcode::And => Some(vec![SystemzOpcode::N]),
SystemzSelOpcode::Or => Some(vec![SystemzOpcode::O]),
SystemzSelOpcode::Xor => Some(vec![SystemzOpcode::X]),
SystemzSelOpcode::Add64 => Some(vec![SystemzOpcode::AG]),
SystemzSelOpcode::Sub64 => Some(vec![SystemzOpcode::SG]),
SystemzSelOpcode::And64 => Some(vec![SystemzOpcode::NG]),
SystemzSelOpcode::Or64 => Some(vec![SystemzOpcode::OG]),
SystemzSelOpcode::Xor64 => Some(vec![SystemzOpcode::XG]),
SystemzSelOpcode::Shl => Some(vec![SystemzOpcode::SLL]),
SystemzSelOpcode::Sra => Some(vec![SystemzOpcode::SRA]),
SystemzSelOpcode::Srl => Some(vec![SystemzOpcode::SRL]),
SystemzSelOpcode::Load | SystemzSelOpcode::Load32 => Some(vec![SystemzOpcode::L]),
SystemzSelOpcode::Load8 | SystemzSelOpcode::Load16 => Some(vec![SystemzOpcode::L]),
SystemzSelOpcode::Load64 => Some(vec![SystemzOpcode::LG]),
SystemzSelOpcode::LoadAddr => Some(vec![SystemzOpcode::LA]),
SystemzSelOpcode::Store | SystemzSelOpcode::Store32 => Some(vec![SystemzOpcode::ST]),
SystemzSelOpcode::Store8 | SystemzSelOpcode::Store16 => Some(vec![SystemzOpcode::ST]),
SystemzSelOpcode::Store64 => Some(vec![SystemzOpcode::STG]),
SystemzSelOpcode::Br => Some(vec![SystemzOpcode::J]),
SystemzSelOpcode::BrCond => Some(vec![SystemzOpcode::BRC]),
SystemzSelOpcode::Call => Some(vec![SystemzOpcode::BASR]),
SystemzSelOpcode::Return => Some(vec![SystemzOpcode::BR]),
SystemzSelOpcode::Copy | SystemzSelOpcode::CopyToReg => Some(vec![SystemzOpcode::LGR]),
SystemzSelOpcode::Constant32 | SystemzSelOpcode::Constant64 => {
Some(vec![SystemzOpcode::LA])
}
SystemzSelOpcode::Cmp => Some(vec![SystemzOpcode::C]),
SystemzSelOpcode::Cmp64 => Some(vec![SystemzOpcode::CG]),
SystemzSelOpcode::Setcc => Some(vec![SystemzOpcode::LTGR]),
SystemzSelOpcode::FrameIndex => Some(vec![SystemzOpcode::LA]),
_ => None,
}
}
pub fn is_legal(&self, node: &SystemzSelNode) -> bool {
match node.opcode {
SystemzSelOpcode::SRem | SystemzSelOpcode::URem => false,
_ => true,
}
}
pub fn requires_libcall(&self, node: &SystemzSelNode) -> bool {
matches!(node.opcode, SystemzSelOpcode::SRem | SystemzSelOpcode::URem)
}
pub fn get_instr_info(&self) -> &SystemzInstrInfo {
&self.instr_info
}
}
#[cfg(test)]
mod tests {
use super::*;
fn node(opcode: SystemzSelOpcode) -> SystemzSelNode {
SystemzSelNode {
opcode,
operands: vec![],
result_reg: None,
}
}
#[test]
fn test_select_add() {
let isel = SystemzInstructionSelector::new();
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Add)]),
vec![SystemzOpcode::A]
);
}
#[test]
fn test_select_sub() {
let isel = SystemzInstructionSelector::new();
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Sub)]),
vec![SystemzOpcode::S]
);
}
#[test]
fn test_select_and() {
let isel = SystemzInstructionSelector::new();
assert_eq!(
isel.select(&[node(SystemzSelOpcode::And)]),
vec![SystemzOpcode::N]
);
}
#[test]
fn test_select_or() {
let isel = SystemzInstructionSelector::new();
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Or)]),
vec![SystemzOpcode::O]
);
}
#[test]
fn test_select_xor() {
let isel = SystemzInstructionSelector::new();
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Xor)]),
vec![SystemzOpcode::X]
);
}
#[test]
fn test_select_64bit_ops() {
let isel = SystemzInstructionSelector::new();
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Add64)]),
vec![SystemzOpcode::AG]
);
assert_eq!(
isel.select(&[node(SystemzSelOpcode::And64)]),
vec![SystemzOpcode::NG]
);
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Xor64)]),
vec![SystemzOpcode::XG]
);
}
#[test]
fn test_select_shifts() {
let isel = SystemzInstructionSelector::new();
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Shl)]),
vec![SystemzOpcode::SLL]
);
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Sra)]),
vec![SystemzOpcode::SRA]
);
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Srl)]),
vec![SystemzOpcode::SRL]
);
}
#[test]
fn test_select_loads() {
let isel = SystemzInstructionSelector::new();
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Load)]),
vec![SystemzOpcode::L]
);
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Load64)]),
vec![SystemzOpcode::LG]
);
assert_eq!(
isel.select(&[node(SystemzSelOpcode::LoadAddr)]),
vec![SystemzOpcode::LA]
);
}
#[test]
fn test_select_stores() {
let isel = SystemzInstructionSelector::new();
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Store)]),
vec![SystemzOpcode::ST]
);
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Store64)]),
vec![SystemzOpcode::STG]
);
}
#[test]
fn test_select_control_flow() {
let isel = SystemzInstructionSelector::new();
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Br)]),
vec![SystemzOpcode::J]
);
assert_eq!(
isel.select(&[node(SystemzSelOpcode::BrCond)]),
vec![SystemzOpcode::BRC]
);
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Call)]),
vec![SystemzOpcode::BASR]
);
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Return)]),
vec![SystemzOpcode::BR]
);
}
#[test]
fn test_select_copy() {
let isel = SystemzInstructionSelector::new();
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Copy)]),
vec![SystemzOpcode::LGR]
);
assert_eq!(
isel.select(&[node(SystemzSelOpcode::CopyToReg)]),
vec![SystemzOpcode::LGR]
);
}
#[test]
fn test_select_compare() {
let isel = SystemzInstructionSelector::new();
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Cmp)]),
vec![SystemzOpcode::C]
);
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Cmp64)]),
vec![SystemzOpcode::CG]
);
}
#[test]
fn test_select_setcc() {
let isel = SystemzInstructionSelector::new();
assert_eq!(
isel.select(&[node(SystemzSelOpcode::Setcc)]),
vec![SystemzOpcode::LTGR]
);
}
#[test]
fn test_select_frame_index() {
let isel = SystemzInstructionSelector::new();
assert_eq!(
isel.select(&[node(SystemzSelOpcode::FrameIndex)]),
vec![SystemzOpcode::LA]
);
}
#[test]
fn test_select_div() {
let isel = SystemzInstructionSelector::new();
assert_eq!(
isel.select(&[node(SystemzSelOpcode::SDiv)]),
vec![SystemzOpcode::D]
);
}
#[test]
fn test_rem_requires_libcall() {
let isel = SystemzInstructionSelector::new();
assert!(isel.requires_libcall(&node(SystemzSelOpcode::SRem)));
assert!(isel.requires_libcall(&node(SystemzSelOpcode::URem)));
assert!(!isel.requires_libcall(&node(SystemzSelOpcode::Add)));
}
#[test]
fn test_is_legal() {
let isel = SystemzInstructionSelector::new();
assert!(isel.is_legal(&node(SystemzSelOpcode::Add)));
assert!(isel.is_legal(&node(SystemzSelOpcode::Load64)));
assert!(!isel.is_legal(&node(SystemzSelOpcode::SRem)));
}
#[test]
fn test_get_instr_info() {
let isel = SystemzInstructionSelector::new();
let info = isel.get_instr_info();
assert!(info.get_desc(SystemzOpcode::L).is_some());
assert!(info.get_desc(SystemzOpcode::AG).is_some());
}
#[test]
fn test_multiple_node_selection() {
let isel = SystemzInstructionSelector::new();
let nodes = vec![
node(SystemzSelOpcode::Add),
node(SystemzSelOpcode::Store),
node(SystemzSelOpcode::Return),
];
let result = isel.select(&nodes);
assert_eq!(
result,
vec![SystemzOpcode::A, SystemzOpcode::ST, SystemzOpcode::BR]
);
}
}