use crate::opcode::Opcode;
use crate::systemz::systemz_instr_info::{
SystemzFormat, SystemzInstrDesc, SystemzInstrInfo, SystemzOpcode,
};
use crate::systemz::systemz_register_info::{
SystemzRegister, SystemzRegisterInfo, SZ_AR_COUNT, SZ_FPR_COUNT, SZ_GPR_COUNT,
};
use crate::target_machine::{CodeGenOptLevel, CodeModel, RelocModel, TargetMachine, TargetOptions};
use crate::triple::{Arch, Triple};
use crate::types::Type;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::fmt;
pub const SYSTEMZ_DATA_LAYOUT: &str = "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64";
pub const X86_64_DATA_LAYOUT: &str =
"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128";
pub const SYSTEMZ_ABI_NAMES: &[&str] = &["elf", "elf-s390x"];
pub const X86_ABI_NAMES: &[&str] = &["sysv", "win64", "msvc"];
pub const SYSTEMZ_STACK_ALIGNMENT: u32 = 8;
pub const SYSTEMZ_RED_ZONE_SIZE: u32 = 160;
pub const SYSTEMZ_PAGE_SIZE: u64 = 4096;
pub const SYSTEMZ_MAX_INSTR_SIZE: u32 = 6;
pub const SYSTEMZ_MIN_INSTR_SIZE: u32 = 2;
pub const SYSTEMZ_MAX_IMM_I16: i64 = 32767;
pub const SYSTEMZ_MIN_IMM_I16: i64 = -32768;
pub const SYSTEMZ_MAX_IMM_I32: i64 = 2147483647;
pub const SYSTEMZ_MAX_DISP_RX: i64 = 4095;
pub const SYSTEMZ_MAX_DISP_RXY: i64 = 524287;
pub const SYSTEMZ_GPR_SIZE: u32 = 8;
pub const SYSTEMZ_FPR_SIZE: u32 = 8;
pub const SYSTEMZ_BACK_CHAIN_OFFSET: i32 = 0;
pub const SYSTEMZ_REG_SAVE_OFFSET: i32 = 16;
pub const SYSTEMZ_REG_SAVE_SIZE: i32 = 128;
pub const SYSTEMZ_RA_SAVE_OFFSET: i32 = 112;
pub const SYSTEMZ_MIN_FRAME_SIZE: i32 = 160;
pub const SYSTEMZ_VR_SIZE: u32 = 16;
pub const SYSTEMZ_VR_COUNT: usize = 32;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SystemZBridgeArch {
X86_64,
X86_32,
SystemZ,
}
impl fmt::Display for SystemZBridgeArch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SystemZBridgeArch::X86_64 => write!(f, "x86_64"),
SystemZBridgeArch::X86_32 => write!(f, "i386"),
SystemZBridgeArch::SystemZ => write!(f, "s390x"),
}
}
}
impl SystemZBridgeArch {
pub fn is_64bit(&self) -> bool {
matches!(self, SystemZBridgeArch::X86_64 | SystemZBridgeArch::SystemZ)
}
pub fn is_systemz_family(&self) -> bool {
matches!(self, SystemZBridgeArch::SystemZ)
}
pub fn is_x86_family(&self) -> bool {
matches!(self, SystemZBridgeArch::X86_64 | SystemZBridgeArch::X86_32)
}
pub fn pointer_width(&self) -> u32 {
match self {
SystemZBridgeArch::X86_64 | SystemZBridgeArch::SystemZ => 64,
SystemZBridgeArch::X86_32 => 32,
}
}
pub fn data_layout(&self) -> &'static str {
match self {
SystemZBridgeArch::SystemZ => SYSTEMZ_DATA_LAYOUT,
SystemZBridgeArch::X86_64 | SystemZBridgeArch::X86_32 => X86_64_DATA_LAYOUT,
}
}
pub fn is_little_endian(&self) -> bool {
!matches!(self, SystemZBridgeArch::SystemZ)
}
pub fn is_big_endian(&self) -> bool {
matches!(self, SystemZBridgeArch::SystemZ)
}
}
pub struct SystemZX86Bridge {
pub source_arch: SystemZBridgeArch,
pub target_arch: SystemZBridgeArch,
pub target_machine: SystemZTargetMachine,
pub instr_info: SystemZInstrInfo,
pub register_info: SystemZRegisterInfo,
pub isel: CrossTargetISel,
pub regalloc: CrossTargetRegAlloc,
pub frame_lowering: CrossTargetFrameLowering,
pub cost_model: SystemZX86CostModel,
pub abi: CrossTargetABI,
pub optimizer: CrossTargetOptimization,
pub opt_level: CodeGenOptLevel,
pub debug_info: bool,
pub features: BridgeFeatures,
pub stats: BridgeStats,
}
impl SystemZX86Bridge {
pub fn new(source_arch: SystemZBridgeArch, target_arch: SystemZBridgeArch) -> Self {
let target_machine = SystemZTargetMachine::default_target();
let instr_info = SystemZInstrInfo::new();
let register_info = SystemZRegisterInfo;
let isel = CrossTargetISel::new(target_arch);
let regalloc = CrossTargetRegAlloc::new(target_arch);
let frame_lowering = CrossTargetFrameLowering::new(target_arch);
let cost_model = SystemZX86CostModel::new(source_arch, target_arch);
let abi = CrossTargetABI::new(target_arch);
let optimizer = CrossTargetOptimization::new(target_arch);
Self {
source_arch,
target_arch,
target_machine,
instr_info,
register_info,
isel,
regalloc,
frame_lowering,
cost_model,
abi,
optimizer,
opt_level: CodeGenOptLevel::Default,
debug_info: false,
features: BridgeFeatures::default(),
stats: BridgeStats::default(),
}
}
pub fn from_triples(source: &str, target: &str) -> Self {
let src = Self::parse_arch(source);
let tgt = Self::parse_arch(target);
Self::new(src, tgt)
}
pub fn native_to_systemz() -> Self {
Self::new(SystemZBridgeArch::X86_64, SystemZBridgeArch::SystemZ)
}
pub fn systemz_to_native() -> Self {
Self::new(SystemZBridgeArch::SystemZ, SystemZBridgeArch::X86_64)
}
fn parse_arch(triple_str: &str) -> SystemZBridgeArch {
let t = Triple::parse(triple_str);
match t.arch {
Arch::X86_64 => SystemZBridgeArch::X86_64,
Arch::X86 => SystemZBridgeArch::X86_32,
_ => {
let lower = triple_str.to_lowercase();
if lower.contains("s390x") || lower.contains("systemz") || lower.contains("zarch") {
SystemZBridgeArch::SystemZ
} else {
SystemZBridgeArch::X86_64 }
}
}
}
pub fn run_pipeline(&mut self) -> Result<BridgeOutput, BridgeError> {
self.stats.functions_processed += 1;
self.stats.isel_cycles += 1;
if self.opt_level.should_optimize() {
self.stats.opt_cycles += 1;
}
self.stats.ra_cycles += 1;
self.stats.frame_cycles += 1;
if self.opt_level.should_optimize() {
self.stats.post_ra_opt_cycles += 1;
}
Ok(BridgeOutput {
instructions_emitted: 0,
basic_blocks: 0,
target_arch: self.target_arch,
})
}
pub fn estimate_cost(&self, opcode: Opcode, operand_count: usize) -> CostEstimate {
self.cost_model.estimate(opcode, operand_count)
}
pub fn describe(&self) -> String {
format!(
"SystemZX86Bridge: {} → {} (opt={:?}, features={:?})",
self.source_arch, self.target_arch, self.opt_level, self.features
)
}
pub fn set_opt_level(&mut self, level: CodeGenOptLevel) {
self.opt_level = level;
}
pub fn enable_debug_info(&mut self) {
self.debug_info = true;
}
pub fn enable_feature(&mut self, feature: BridgeFeature) {
self.features.enable(feature);
}
pub fn has_feature(&self, feature: BridgeFeature) -> bool {
self.features.has(feature)
}
pub fn get_target_machine(&self) -> &SystemZTargetMachine {
&self.target_machine
}
pub fn get_target_triple(&self) -> String {
self.target_machine.get_target_triple()
}
pub fn get_data_layout(&self) -> String {
self.target_machine.get_data_layout()
}
pub fn supports_unaligned_access(&self) -> bool {
if self.target_arch.is_systemz_family() {
self.target_machine.supports_unaligned_access()
} else {
true
}
}
pub fn get_stack_alignment(&self) -> u32 {
if self.target_arch.is_systemz_family() {
self.target_machine.get_stack_alignment()
} else {
16
}
}
pub fn map_to_systemz_opcode(&self, llvm_opcode: Opcode) -> Option<SystemZOpcode> {
map_llvm_to_systemz_opcode(llvm_opcode)
}
pub fn list_systemz_instructions(&self) -> Vec<&SystemzInstrDesc> {
let mut result = Vec::new();
for op in SystemZOpcode::all_opcodes() {
if let Some(desc) = self.instr_info.get_desc(op) {
result.push(desc);
}
}
result
}
pub fn count_by_format(&self, fmt: SystemZFormat) -> usize {
let mut count = 0;
for op in SystemZOpcode::all_opcodes() {
if let Some(desc) = self.instr_info.get_desc(op) {
if desc.format == fmt {
count += 1;
}
}
}
count
}
}
impl Default for SystemZX86Bridge {
fn default() -> Self {
Self::new(SystemZBridgeArch::X86_64, SystemZBridgeArch::SystemZ)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SystemZArchLevel {
Z900,
Z9,
Z10,
Z196,
ZEC12,
Z13,
Z14,
Z15,
Z16,
}
impl fmt::Display for SystemZArchLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SystemZArchLevel::Z900 => write!(f, "z900"),
SystemZArchLevel::Z9 => write!(f, "z9-109"),
SystemZArchLevel::Z10 => write!(f, "z10"),
SystemZArchLevel::Z196 => write!(f, "z196"),
SystemZArchLevel::ZEC12 => write!(f, "zEC12"),
SystemZArchLevel::Z13 => write!(f, "z13"),
SystemZArchLevel::Z14 => write!(f, "z14"),
SystemZArchLevel::Z15 => write!(f, "z15"),
SystemZArchLevel::Z16 => write!(f, "z16"),
}
}
}
impl SystemZArchLevel {
pub fn has_vector(&self) -> bool {
matches!(
self,
SystemZArchLevel::Z13
| SystemZArchLevel::Z14
| SystemZArchLevel::Z15
| SystemZArchLevel::Z16
)
}
pub fn has_dfp(&self) -> bool {
!matches!(
self,
SystemZArchLevel::Z900 | SystemZArchLevel::Z9 | SystemZArchLevel::Z10
)
}
pub fn has_tx(&self) -> bool {
matches!(
self,
SystemZArchLevel::ZEC12
| SystemZArchLevel::Z13
| SystemZArchLevel::Z14
| SystemZArchLevel::Z15
| SystemZArchLevel::Z16
)
}
pub fn has_gs(&self) -> bool {
matches!(
self,
SystemZArchLevel::Z14 | SystemZArchLevel::Z15 | SystemZArchLevel::Z16
)
}
pub fn has_msa8(&self) -> bool {
matches!(self, SystemZArchLevel::Z15 | SystemZArchLevel::Z16)
}
pub fn has_msa9(&self) -> bool {
matches!(self, SystemZArchLevel::Z16)
}
pub fn cpu_name(&self) -> &'static str {
match self {
SystemZArchLevel::Z900 => "z900",
SystemZArchLevel::Z9 => "z9-109",
SystemZArchLevel::Z10 => "z10",
SystemZArchLevel::Z196 => "z196",
SystemZArchLevel::ZEC12 => "zEC12",
SystemZArchLevel::Z13 => "z13",
SystemZArchLevel::Z14 => "z14",
SystemZArchLevel::Z15 => "z15",
SystemZArchLevel::Z16 => "z16",
}
}
pub fn hwcap_mask(&self) -> u64 {
match self {
SystemZArchLevel::Z900 => 0x00,
SystemZArchLevel::Z9 => 0x01,
SystemZArchLevel::Z10 => 0x03,
SystemZArchLevel::Z196 => 0x0F,
SystemZArchLevel::ZEC12 => 0x3F,
SystemZArchLevel::Z13 => 0xFF,
SystemZArchLevel::Z14 => 0x3FF,
SystemZArchLevel::Z15 => 0xFFF,
SystemZArchLevel::Z16 => 0x3FFF,
}
}
}
pub struct SystemZTargetMachine {
pub cpu: String,
pub arch_level: SystemZArchLevel,
pub has_vector: bool,
pub has_dfp: bool,
pub has_tx: bool,
pub has_gs: bool,
pub has_msa: bool,
pub has_hard_float: bool,
pub has_distinct_ops: bool,
pub has_load_store_on_cond: bool,
pub has_high_word: bool,
pub has_interlocked_access: bool,
pub has_popcnt: bool,
pub has_fast_serialization: bool,
pub has_execution_hint: bool,
pub has_misc_extensions: bool,
pub code_model: CodeModel,
pub reloc_model: RelocModel,
pub options: TargetOptions,
pub calling_conv: SystemZCallingConvention,
}
impl SystemZTargetMachine {
pub fn new(arch: SystemZArchLevel) -> Self {
let cpu = arch.cpu_name().to_string();
let has_vector = arch.has_vector();
let has_dfp = arch.has_dfp();
let has_tx = arch.has_tx();
let has_gs = arch.has_gs();
let has_msa = arch.has_msa8();
let has_distinct_ops = !matches!(arch, SystemZArchLevel::Z900);
let has_load_store_on_cond = !matches!(
arch,
SystemZArchLevel::Z900 | SystemZArchLevel::Z9 | SystemZArchLevel::Z10
);
let has_high_word = !matches!(
arch,
SystemZArchLevel::Z900
| SystemZArchLevel::Z9
| SystemZArchLevel::Z10
| SystemZArchLevel::Z196
);
let has_interlocked_access = !matches!(arch, SystemZArchLevel::Z900 | SystemZArchLevel::Z9);
let has_popcnt = !matches!(arch, SystemZArchLevel::Z900 | SystemZArchLevel::Z9);
let has_fast_serialization = !matches!(arch, SystemZArchLevel::Z900);
let has_execution_hint = !matches!(
arch,
SystemZArchLevel::Z900
| SystemZArchLevel::Z9
| SystemZArchLevel::Z10
| SystemZArchLevel::Z196
| SystemZArchLevel::ZEC12
| SystemZArchLevel::Z13
);
let has_misc_extensions = arch.has_vector();
SystemZTargetMachine {
cpu,
arch_level: arch,
has_vector,
has_dfp,
has_tx,
has_gs,
has_msa,
has_hard_float: true,
has_distinct_ops,
has_load_store_on_cond,
has_high_word,
has_interlocked_access,
has_popcnt,
has_fast_serialization,
has_execution_hint,
has_misc_extensions,
code_model: CodeModel::Small,
reloc_model: RelocModel::Static,
options: TargetOptions::default(),
calling_conv: SystemZCallingConvention::new(),
}
}
pub fn default_target() -> Self {
Self::new(SystemZArchLevel::Z14)
}
pub fn z16_target() -> Self {
Self::new(SystemZArchLevel::Z16)
}
pub fn z15_target() -> Self {
Self::new(SystemZArchLevel::Z15)
}
pub fn z10_target() -> Self {
Self::new(SystemZArchLevel::Z10)
}
pub fn get_target_triple(&self) -> String {
"s390x-ibm-linux-gnu".to_string()
}
pub fn get_data_layout(&self) -> String {
SYSTEMZ_DATA_LAYOUT.to_string()
}
pub fn get_pointer_size(&self) -> u32 {
64
}
pub fn get_register_width(&self) -> u32 {
64
}
pub fn get_stack_alignment(&self) -> u32 {
SYSTEMZ_STACK_ALIGNMENT
}
pub fn get_red_zone_size(&self) -> u32 {
SYSTEMZ_RED_ZONE_SIZE
}
pub fn has_vector_facility(&self) -> bool {
self.has_vector
}
pub fn has_dfp_facility(&self) -> bool {
self.has_dfp
}
pub fn has_tx_facility(&self) -> bool {
self.has_tx
}
pub fn has_gs_facility(&self) -> bool {
self.has_gs
}
pub fn has_msa_facility(&self) -> bool {
self.has_msa
}
pub fn get_cpu(&self) -> &str {
&self.cpu
}
pub fn get_arch_level(&self) -> SystemZArchLevel {
self.arch_level
}
pub fn get_page_size(&self) -> u32 {
4096
}
pub fn get_min_stack_size(&self) -> u32 {
8192
}
pub fn get_max_alignment(&self) -> u32 {
8
}
pub fn supports_unaligned_access(&self) -> bool {
true
}
pub fn get_code_model(&self) -> CodeModel {
self.code_model
}
pub fn set_code_model(&mut self, model: CodeModel) {
self.code_model = model;
}
pub fn get_reloc_model(&self) -> RelocModel {
self.reloc_model
}
pub fn set_reloc_model(&mut self, model: RelocModel) {
self.reloc_model = model;
}
pub fn set_hard_float(&mut self, enable: bool) {
self.has_hard_float = enable;
}
pub fn get_calling_convention(&self) -> &SystemZCallingConvention {
&self.calling_conv
}
pub fn get_feature_summary(&self) -> Vec<(&'static str, bool)> {
vec![
("vector", self.has_vector),
("dfp", self.has_dfp),
("tx", self.has_tx),
("gs", self.has_gs),
("msa", self.has_msa),
("hard-float", self.has_hard_float),
("distinct-ops", self.has_distinct_ops),
("load-store-on-cond", self.has_load_store_on_cond),
("high-word", self.has_high_word),
("interlocked-access", self.has_interlocked_access),
("popcnt", self.has_popcnt),
("fast-serialization", self.has_fast_serialization),
("execution-hint", self.has_execution_hint),
("misc-extensions", self.has_misc_extensions),
]
}
pub fn get_feature_string(&self) -> String {
let mut feats = Vec::new();
if self.has_vector {
feats.push("+vector".to_string());
}
if self.has_dfp {
feats.push("+dfp".to_string());
}
if self.has_tx {
feats.push("+transactional-execution".to_string());
}
if self.has_gs {
feats.push("+guarded-storage".to_string());
}
if self.has_msa {
feats.push("+message-security-assist".to_string());
}
if self.has_distinct_ops {
feats.push("+distinct-ops".to_string());
}
feats.join(",")
}
pub fn emit_target_attributes(&self) -> Vec<String> {
vec![
format!(".attribute arch, \"{}\"", self.cpu),
format!(".machine \"{}\"", self.cpu),
]
}
pub fn is_callee_saved(&self, reg: SystemZRegister) -> bool {
self.calling_conv.callee_saved.contains(®)
}
pub fn is_caller_saved(&self, reg: SystemZRegister) -> bool {
self.calling_conv.caller_saved.contains(®)
}
}
pub struct SystemZCallingConvention {
pub arg_regs: Vec<SystemZRegister>,
pub ret_regs: Vec<SystemZRegister>,
pub fp_arg_regs: Vec<SystemZRegister>,
pub fp_ret_regs: Vec<SystemZRegister>,
pub callee_saved: Vec<SystemZRegister>,
pub caller_saved: Vec<SystemZRegister>,
pub sp: SystemZRegister,
pub fp: SystemZRegister,
pub ra: SystemZRegister,
pub static_chain: SystemZRegister,
pub tls_reg: SystemZRegister,
pub stack_bias: i32,
}
impl SystemZCallingConvention {
pub fn new() -> Self {
SystemZCallingConvention {
arg_regs: vec![
SystemZRegister::R2,
SystemZRegister::R3,
SystemZRegister::R4,
SystemZRegister::R5,
SystemZRegister::R6,
],
ret_regs: vec![SystemZRegister::R2, SystemZRegister::R3],
fp_arg_regs: vec![
SystemZRegister::F0,
SystemZRegister::F2,
SystemZRegister::F4,
SystemZRegister::F6,
],
fp_ret_regs: vec![SystemZRegister::F0],
callee_saved: vec![
SystemZRegister::R6,
SystemZRegister::R7,
SystemZRegister::R8,
SystemZRegister::R9,
SystemZRegister::R10,
SystemZRegister::R11,
SystemZRegister::R12,
SystemZRegister::R13,
SystemZRegister::R14,
SystemZRegister::R15,
SystemZRegister::F8,
SystemZRegister::F9,
SystemZRegister::F10,
SystemZRegister::F11,
SystemZRegister::F12,
SystemZRegister::F13,
SystemZRegister::F14,
SystemZRegister::F15,
],
caller_saved: vec![
SystemZRegister::R0,
SystemZRegister::R1,
SystemZRegister::R2,
SystemZRegister::R3,
SystemZRegister::R4,
SystemZRegister::R5,
SystemZRegister::F0,
SystemZRegister::F1,
SystemZRegister::F2,
SystemZRegister::F3,
SystemZRegister::F4,
SystemZRegister::F5,
SystemZRegister::F6,
SystemZRegister::F7,
],
sp: SystemZRegister::R15,
fp: SystemZRegister::R11,
ra: SystemZRegister::R14,
static_chain: SystemZRegister::R12,
tls_reg: SystemZRegister::R0,
stack_bias: 0,
}
}
pub fn num_arg_gprs(&self) -> usize {
self.arg_regs.len()
}
pub fn num_arg_fprs(&self) -> usize {
self.fp_arg_regs.len()
}
pub fn is_arg_reg(&self, reg: SystemZRegister) -> bool {
self.arg_regs.contains(®) || self.fp_arg_regs.contains(®)
}
pub fn get_ra_dwarf_num(&self) -> u32 {
14
}
pub fn get_fp_dwarf_num(&self) -> u32 {
11
}
pub fn get_gpr_save_offset(&self, reg: SystemZRegister) -> Option<i32> {
SystemZRegisterInfo::get_index(reg).map(|idx| (idx as i32) * 8)
}
pub fn get_fpr_save_offset(&self, reg: SystemZRegister) -> Option<i32> {
SystemZRegisterInfo::get_fpr_index(reg).map(|idx| (idx as i32) * 8 + 128)
}
pub fn has_red_zone(&self) -> bool {
true
}
pub fn max_reg_args(&self) -> usize {
5
}
pub fn classify_reg(&self, reg: SystemZRegister) -> SystemZRegClass {
if SystemZRegisterInfo::is_gpr(reg) {
SystemZRegClass::GPR
} else if SystemZRegisterInfo::is_fpr(reg) {
SystemZRegClass::FPR
} else if SystemZRegisterInfo::is_ar(reg) {
SystemZRegClass::AR
} else {
SystemZRegClass::Special
}
}
pub fn get_arg_reg_for_index(&self, idx: usize) -> Option<SystemZRegister> {
self.arg_regs.get(idx).copied()
}
pub fn get_fp_arg_reg_for_index(&self, idx: usize) -> Option<SystemZRegister> {
self.fp_arg_regs.get(idx).copied()
}
pub fn get_callee_saved_gprs(&self) -> Vec<SystemZRegister> {
self.callee_saved
.iter()
.filter(|r| SystemZRegisterInfo::is_gpr(**r))
.copied()
.collect()
}
pub fn get_callee_saved_fprs(&self) -> Vec<SystemZRegister> {
self.callee_saved
.iter()
.filter(|r| SystemZRegisterInfo::is_fpr(**r))
.copied()
.collect()
}
pub fn get_allocatable_gprs(&self) -> Vec<SystemZRegister> {
let mut regs = Vec::new();
for i in 0..SZ_GPR_COUNT as u32 {
let reg = SystemZRegisterInfo::from_index(i).unwrap();
if reg != SystemZRegister::R0 && reg != SystemZRegister::R15 {
regs.push(reg);
}
}
regs
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SystemZRegClass {
GPR64,
GPR32,
FPR64,
FPR32,
FPR128,
AR32,
VR128,
VR64,
VR32,
ADDR64,
CCR,
GPR,
FPR,
AR,
Special,
}
impl fmt::Display for SystemZRegClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SystemZRegClass::GPR64 => write!(f, "GR64"),
SystemZRegClass::GPR32 => write!(f, "GR32"),
SystemZRegClass::FPR64 => write!(f, "FP64"),
SystemZRegClass::FPR32 => write!(f, "FP32"),
SystemZRegClass::FPR128 => write!(f, "FP128"),
SystemZRegClass::AR32 => write!(f, "AR32"),
SystemZRegClass::VR128 => write!(f, "VR128"),
SystemZRegClass::VR64 => write!(f, "VR64"),
SystemZRegClass::VR32 => write!(f, "VR32"),
SystemZRegClass::ADDR64 => write!(f, "ADDR64"),
SystemZRegClass::CCR => write!(f, "CCR"),
SystemZRegClass::GPR => write!(f, "GPR"),
SystemZRegClass::FPR => write!(f, "FPR"),
SystemZRegClass::AR => write!(f, "AR"),
SystemZRegClass::Special => write!(f, "Special"),
}
}
}
impl SystemZRegClass {
pub fn num_regs(&self) -> usize {
match self {
SystemZRegClass::GPR64
| SystemZRegClass::GPR32
| SystemZRegClass::GPR
| SystemZRegClass::ADDR64 => SZ_GPR_COUNT,
SystemZRegClass::FPR64 | SystemZRegClass::FPR32 | SystemZRegClass::FPR => SZ_FPR_COUNT,
SystemZRegClass::FPR128 => SZ_FPR_COUNT / 2,
SystemZRegClass::AR32 | SystemZRegClass::AR => SZ_AR_COUNT,
SystemZRegClass::VR128 | SystemZRegClass::VR64 | SystemZRegClass::VR32 => {
SYSTEMZ_VR_COUNT
}
SystemZRegClass::CCR => 1,
SystemZRegClass::Special => 3, }
}
pub fn reg_size(&self) -> u32 {
match self {
SystemZRegClass::GPR64 | SystemZRegClass::GPR | SystemZRegClass::ADDR64 => 64,
SystemZRegClass::GPR32 => 32,
SystemZRegClass::FPR64 | SystemZRegClass::FPR => 64,
SystemZRegClass::FPR32 => 32,
SystemZRegClass::FPR128 | SystemZRegClass::VR128 => 128,
SystemZRegClass::AR32 | SystemZRegClass::AR => 32,
SystemZRegClass::VR64 => 64,
SystemZRegClass::VR32 => 32,
SystemZRegClass::CCR => 2,
SystemZRegClass::Special => 64,
}
}
pub fn spill_size(&self) -> u32 {
match self {
SystemZRegClass::GPR64 | SystemZRegClass::GPR | SystemZRegClass::ADDR64 => 8,
SystemZRegClass::GPR32 => 4,
SystemZRegClass::FPR64 | SystemZRegClass::FPR => 8,
SystemZRegClass::FPR32 => 4,
SystemZRegClass::FPR128 | SystemZRegClass::VR128 => 16,
SystemZRegClass::AR32 | SystemZRegClass::AR => 4,
SystemZRegClass::VR64 => 8,
SystemZRegClass::VR32 => 4,
SystemZRegClass::CCR => 4,
SystemZRegClass::Special => 8,
}
}
pub fn spill_alignment(&self) -> u32 {
match self {
SystemZRegClass::FPR128 | SystemZRegClass::VR128 => 16,
_ => 8,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SystemZBridgeOpcode {
L, ST, LR,
LGR, LGHI, LHI, LGFI, LLILL, LLILH, LLIHL, LLIHH, LGFR, LGH, LGB, LGHR, LGBR, LY, LHY, LAY, LGF, LTG, LTGR, LT, LTR, LTEBR, LTDBR, LTXBR, LZER, LZDR, LZXR,
STG, STY, STHY, STCY, STGRL, STRL, STHRL, STGSC,
AGR, SGR, MSGR, DSGR, AGRK, SGRK, MSGRK, AGFI, AGHI, AGHIK, ALGR, SLGR, MSR, DR, ALR, SLR,
A, S, M, D, AG, SG, MSG, DSG, AH, SH, AL, SL, AGH, ALG, SLG, AY, SY, AHY, ALY,
AFI, AIH, AHI, ALFI, ALGFI, ALHSIK, ALGHSIK, SLFI, SLGFI,
N, O, X, NG, OG, XG, NR, OR, XR, NGR, OGR, XGR, NGRK, OGRK, XGRK, NRK, ORK, XRK, NY, OY, XY,
NIHF, NILF, OIHF, OILF, XIHF, XILF,
CGR, CR, CGFR, CLGR, CLR, C, CG, CL, CLI, CLG, CGH, CY, CGY, CGFI, CFI, CGHI, CHI, CLGFI, CLFI, CH, CLHY, CGIJ, CIJ, CLGIJ, CLIJ, CGRJ, CRJ, CLGRJ, CLRJ,
TM, TMY, TMHH, TMHL, TMLH, TMLL,
BRC, BRCL, BCR, BR, J, JG, BASR, BAS, BRAS, BRASL, BRCT, BRCTG, BRCTH, BI, BCTGR,
SLL, SRL, SLA, SRA, SLLG, SRLG, SLAG, SRAG, SLLK, SRLK, SLAK, SRAK, RLL, RLLG,
MVC, MVCIN, MVN, MVZ, MVCL, MVCLU, MVST, MVPG, MVI, MVGHI, MVHI,
LDR, LER, ADBR, AEBR, SDBR, SEBR, MDBR, MEBR, DDBR, DEBR, CDBR, CEBR, CDFBR, CEFBR, CFDBR, CFEBR, CDFTR, CXFBR, CFXBR, MADBR, MAEBR, MSDBR, MSEBR, SQDBR, SQEBR, LDEBR, LEDBR, LDXBR, LEXBR, LCDFR, LNDFR, LPDFR, FIDBR, FIEBR,
LE, LD, STE, STD, AE, AD, SE, SD, ME, MD, DE, DD, CE, CD,
ADTR, SDTR, MDTR, DDTR, CDTR,
CVD, CVDG, CVDY, CVB, CVBG, CVBY,
TBEGIN, TBEGINC, TEND, TABORT, ETND, NTSTG, SPM, EPSW, IPM, LCTL, LCTLG, STCTL, STCTLG, SPKA, TPROT, SAM24, SAM31, SAM64, IPTE, IDTE, PTFF,
NOP, NOPR, BRK,
FLOGR, POPCNT,
MLGR, DLGR, MLR, DLR, MLG, DLG, ML, DL,
IIHF, IILF, LGHI_DUP, LHI_DUP, LLIHF, LLILF, LLIHH_DUP, LLIHL_DUP, LLILH_DUP, LLILL_DUP, OIHH, OIHL, OILH, OILL,
BAKR, BSA, CFC, CS, CSG, CSY, CDS, CDSG, CDSY, TS, CSST,
LRVR, LRVGR, LRVH, STRV, STRVG, STRVH,
LOCR, LOCGR, LOC, LOCG, STOC, STOCG,
VL, VST, VLREP, VA, VS, VM, VN, VO, VX, VCEQ, VCH, VCHL, VMRHG, VMRLG, VPERM, VPOPCT, VZERO, VONE,
SZ_BRIDGE_OPCODE_COUNT,
}
impl SystemZBridgeOpcode {
pub fn mnemonic(&self) -> &'static str {
match self {
SystemZBridgeOpcode::L => "l",
SystemZBridgeOpcode::ST => "st",
SystemZBridgeOpcode::LGR => "lgr",
SystemZBridgeOpcode::LGHI => "lghi",
SystemZBridgeOpcode::LHI => "lhi",
SystemZBridgeOpcode::LGFI => "lgfi",
SystemZBridgeOpcode::LLILL => "llill",
SystemZBridgeOpcode::LLILH => "llilh",
SystemZBridgeOpcode::LLIHL => "llihl",
SystemZBridgeOpcode::LLIHH => "llihh",
SystemZBridgeOpcode::LGFR => "lgfr",
SystemZBridgeOpcode::LGH => "lgh",
SystemZBridgeOpcode::LGB => "lgb",
SystemZBridgeOpcode::LGHR => "lghr",
SystemZBridgeOpcode::LGBR => "lgbr",
SystemZBridgeOpcode::LY => "ly",
SystemZBridgeOpcode::LHY => "lhy",
SystemZBridgeOpcode::LAY => "lay",
SystemZBridgeOpcode::LGF => "lgf",
SystemZBridgeOpcode::LTG => "ltg",
SystemZBridgeOpcode::LTGR => "ltgr",
SystemZBridgeOpcode::LT => "lt",
SystemZBridgeOpcode::LTR => "ltr",
SystemZBridgeOpcode::LTEBR => "ltebr",
SystemZBridgeOpcode::LTDBR => "ltdbr",
SystemZBridgeOpcode::LTXBR => "ltxbr",
SystemZBridgeOpcode::LZER => "lzer",
SystemZBridgeOpcode::LZDR => "lzdr",
SystemZBridgeOpcode::LZXR => "lzxr",
SystemZBridgeOpcode::STG => "stg",
SystemZBridgeOpcode::STY => "sty",
SystemZBridgeOpcode::STHY => "sthy",
SystemZBridgeOpcode::STCY => "stcy",
SystemZBridgeOpcode::STGRL => "stgrl",
SystemZBridgeOpcode::STRL => "strl",
SystemZBridgeOpcode::STHRL => "sthrl",
SystemZBridgeOpcode::STGSC => "stgsc",
SystemZBridgeOpcode::AGR => "agr",
SystemZBridgeOpcode::SGR => "sgr",
SystemZBridgeOpcode::MSGR => "msgr",
SystemZBridgeOpcode::DSGR => "dsgr",
SystemZBridgeOpcode::AGRK => "agrk",
SystemZBridgeOpcode::SGRK => "sgrk",
SystemZBridgeOpcode::MSGRK => "msgrk",
SystemZBridgeOpcode::AGFI => "agfi",
SystemZBridgeOpcode::AGHI => "aghi",
SystemZBridgeOpcode::AGHIK => "aghik",
SystemZBridgeOpcode::ALGR => "algr",
SystemZBridgeOpcode::SLGR => "slgr",
SystemZBridgeOpcode::MSR => "msr",
SystemZBridgeOpcode::DR => "dr",
SystemZBridgeOpcode::ALR => "alr",
SystemZBridgeOpcode::SLR => "slr",
SystemZBridgeOpcode::A => "a",
SystemZBridgeOpcode::S => "s",
SystemZBridgeOpcode::M => "m",
SystemZBridgeOpcode::D => "d",
SystemZBridgeOpcode::AG => "ag",
SystemZBridgeOpcode::SG => "sg",
SystemZBridgeOpcode::MSG => "msg",
SystemZBridgeOpcode::DSG => "dsg",
SystemZBridgeOpcode::AH => "ah",
SystemZBridgeOpcode::SH => "sh",
SystemZBridgeOpcode::AL => "al",
SystemZBridgeOpcode::SL => "sl",
SystemZBridgeOpcode::AGH => "agh",
SystemZBridgeOpcode::ALG => "alg",
SystemZBridgeOpcode::SLG => "slg",
SystemZBridgeOpcode::AY => "ay",
SystemZBridgeOpcode::SY => "sy",
SystemZBridgeOpcode::AHY => "ahy",
SystemZBridgeOpcode::ALY => "aly",
SystemZBridgeOpcode::AFI => "afi",
SystemZBridgeOpcode::AIH => "aih",
SystemZBridgeOpcode::AHI => "ahi",
SystemZBridgeOpcode::ALFI => "alfi",
SystemZBridgeOpcode::ALGFI => "algfi",
SystemZBridgeOpcode::ALHSIK => "alhsik",
SystemZBridgeOpcode::ALGHSIK => "alghsik",
SystemZBridgeOpcode::SLFI => "slfi",
SystemZBridgeOpcode::SLGFI => "slgfi",
SystemZBridgeOpcode::N => "n",
SystemZBridgeOpcode::O => "o",
SystemZBridgeOpcode::X => "x",
SystemZBridgeOpcode::NG => "ng",
SystemZBridgeOpcode::OG => "og",
SystemZBridgeOpcode::XG => "xg",
SystemZBridgeOpcode::NR => "nr",
SystemZBridgeOpcode::OR => "or",
SystemZBridgeOpcode::XR => "xr",
SystemZBridgeOpcode::NGR => "ngr",
SystemZBridgeOpcode::OGR => "ogr",
SystemZBridgeOpcode::XGR => "xgr",
SystemZBridgeOpcode::NGRK => "ngrk",
SystemZBridgeOpcode::OGRK => "ogrk",
SystemZBridgeOpcode::XGRK => "xgrk",
SystemZBridgeOpcode::NRK => "nrk",
SystemZBridgeOpcode::ORK => "ork",
SystemZBridgeOpcode::XRK => "xrk",
SystemZBridgeOpcode::NY => "ny",
SystemZBridgeOpcode::OY => "oy",
SystemZBridgeOpcode::XY => "xy",
SystemZBridgeOpcode::NIHF => "nihf",
SystemZBridgeOpcode::NILF => "nilf",
SystemZBridgeOpcode::OIHF => "oihf",
SystemZBridgeOpcode::OILF => "oilf",
SystemZBridgeOpcode::XIHF => "xihf",
SystemZBridgeOpcode::XILF => "xilf",
SystemZBridgeOpcode::CGR => "cgr",
SystemZBridgeOpcode::CR => "cr",
SystemZBridgeOpcode::CGFR => "cgfr",
SystemZBridgeOpcode::CLGR => "clgr",
SystemZBridgeOpcode::CLR => "clr",
SystemZBridgeOpcode::C => "c",
SystemZBridgeOpcode::CG => "cg",
SystemZBridgeOpcode::CL => "cl",
SystemZBridgeOpcode::CLI => "cli",
SystemZBridgeOpcode::CLG => "clg",
SystemZBridgeOpcode::CGH => "cgh",
SystemZBridgeOpcode::CY => "cy",
SystemZBridgeOpcode::CGY => "cgy",
SystemZBridgeOpcode::CGFI => "cgfi",
SystemZBridgeOpcode::CFI => "cfi",
SystemZBridgeOpcode::CGHI => "cghi",
SystemZBridgeOpcode::CHI => "chi",
SystemZBridgeOpcode::CLGFI => "clgfi",
SystemZBridgeOpcode::CLFI => "clfi",
SystemZBridgeOpcode::CH => "ch",
SystemZBridgeOpcode::CLHY => "clhy",
SystemZBridgeOpcode::CGIJ => "cgij",
SystemZBridgeOpcode::CIJ => "cij",
SystemZBridgeOpcode::CLGIJ => "clgij",
SystemZBridgeOpcode::CLIJ => "clij",
SystemZBridgeOpcode::CGRJ => "cgrj",
SystemZBridgeOpcode::CRJ => "crj",
SystemZBridgeOpcode::CLGRJ => "clgrj",
SystemZBridgeOpcode::CLRJ => "clrj",
SystemZBridgeOpcode::TM => "tm",
SystemZBridgeOpcode::TMY => "tmy",
SystemZBridgeOpcode::TMHH => "tmhh",
SystemZBridgeOpcode::TMHL => "tmhl",
SystemZBridgeOpcode::TMLH => "tmlh",
SystemZBridgeOpcode::TMLL => "tmll",
SystemZBridgeOpcode::BRC => "brc",
SystemZBridgeOpcode::BRCL => "brcl",
SystemZBridgeOpcode::BCR => "bcr",
SystemZBridgeOpcode::BR => "br",
SystemZBridgeOpcode::J => "j",
SystemZBridgeOpcode::JG => "jg",
SystemZBridgeOpcode::BASR => "basr",
SystemZBridgeOpcode::BAS => "bas",
SystemZBridgeOpcode::BRAS => "bras",
SystemZBridgeOpcode::BRASL => "brasl",
SystemZBridgeOpcode::BRCT => "brct",
SystemZBridgeOpcode::BRCTG => "brctg",
SystemZBridgeOpcode::BRCTH => "brcth",
SystemZBridgeOpcode::BI => "bi",
SystemZBridgeOpcode::BCTGR => "bctgr",
SystemZBridgeOpcode::SLL => "sll",
SystemZBridgeOpcode::SRL => "srl",
SystemZBridgeOpcode::SLA => "sla",
SystemZBridgeOpcode::SRA => "sra",
SystemZBridgeOpcode::SLLG => "sllg",
SystemZBridgeOpcode::SRLG => "srlg",
SystemZBridgeOpcode::SLAG => "slag",
SystemZBridgeOpcode::SRAG => "srag",
SystemZBridgeOpcode::SLLK => "sllk",
SystemZBridgeOpcode::SRLK => "srlk",
SystemZBridgeOpcode::SLAK => "slak",
SystemZBridgeOpcode::SRAK => "srak",
SystemZBridgeOpcode::RLL => "rll",
SystemZBridgeOpcode::RLLG => "rllg",
SystemZBridgeOpcode::MVC => "mvc",
SystemZBridgeOpcode::MVCIN => "mvcin",
SystemZBridgeOpcode::MVN => "mvn",
SystemZBridgeOpcode::MVZ => "mvz",
SystemZBridgeOpcode::MVCL => "mvcl",
SystemZBridgeOpcode::MVCLU => "mvclu",
SystemZBridgeOpcode::MVST => "mvst",
SystemZBridgeOpcode::MVPG => "mvpg",
SystemZBridgeOpcode::MVI => "mvi",
SystemZBridgeOpcode::MVGHI => "mvghi",
SystemZBridgeOpcode::MVHI => "mvhi",
SystemZBridgeOpcode::LR => "lr",
SystemZBridgeOpcode::L => "l",
SystemZBridgeOpcode::LDR => "ldr",
SystemZBridgeOpcode::LER => "ler",
SystemZBridgeOpcode::ADBR => "adbr",
SystemZBridgeOpcode::AEBR => "aebr",
SystemZBridgeOpcode::SDBR => "sdbr",
SystemZBridgeOpcode::SEBR => "sebr",
SystemZBridgeOpcode::MDBR => "mdbr",
SystemZBridgeOpcode::MEBR => "mebr",
SystemZBridgeOpcode::DDBR => "ddbr",
SystemZBridgeOpcode::DEBR => "debr",
SystemZBridgeOpcode::CDBR => "cdbr",
SystemZBridgeOpcode::CEBR => "cebr",
SystemZBridgeOpcode::CDFBR => "cdfbr",
SystemZBridgeOpcode::CEFBR => "cefbr",
SystemZBridgeOpcode::CFDBR => "cfdbr",
SystemZBridgeOpcode::CFEBR => "cfebr",
SystemZBridgeOpcode::CDFTR => "cdftr",
SystemZBridgeOpcode::CXFBR => "cxfbr",
SystemZBridgeOpcode::CFXBR => "cfxbr",
SystemZBridgeOpcode::MADBR => "madbr",
SystemZBridgeOpcode::MAEBR => "maebr",
SystemZBridgeOpcode::MSDBR => "msdbr",
SystemZBridgeOpcode::MSEBR => "msebr",
SystemZBridgeOpcode::SQDBR => "sqdbr",
SystemZBridgeOpcode::SQEBR => "sqebr",
SystemZBridgeOpcode::LDEBR => "ldebr",
SystemZBridgeOpcode::LEDBR => "ledbr",
SystemZBridgeOpcode::LDXBR => "ldxbr",
SystemZBridgeOpcode::LEXBR => "lexbr",
SystemZBridgeOpcode::LCDFR => "lcdfr",
SystemZBridgeOpcode::LNDFR => "lndfr",
SystemZBridgeOpcode::LPDFR => "lpdfr",
SystemZBridgeOpcode::FIDBR => "fidbr",
SystemZBridgeOpcode::FIEBR => "fiebr",
SystemZBridgeOpcode::LE => "le",
SystemZBridgeOpcode::LD => "ld",
SystemZBridgeOpcode::STE => "ste",
SystemZBridgeOpcode::STD => "std",
SystemZBridgeOpcode::AE => "ae",
SystemZBridgeOpcode::AD => "ad",
SystemZBridgeOpcode::SE => "se",
SystemZBridgeOpcode::SD => "sd",
SystemZBridgeOpcode::ME => "me",
SystemZBridgeOpcode::MD => "md",
SystemZBridgeOpcode::DE => "de",
SystemZBridgeOpcode::DD => "dd",
SystemZBridgeOpcode::CE => "ce",
SystemZBridgeOpcode::CD => "cd",
SystemZBridgeOpcode::ADTR => "adtr",
SystemZBridgeOpcode::SDTR => "sdtr",
SystemZBridgeOpcode::MDTR => "mdtr",
SystemZBridgeOpcode::DDTR => "ddtr",
SystemZBridgeOpcode::CDTR => "cdtr",
SystemZBridgeOpcode::CVD => "cvd",
SystemZBridgeOpcode::CVDG => "cvdg",
SystemZBridgeOpcode::CVDY => "cvdy",
SystemZBridgeOpcode::CVB => "cvb",
SystemZBridgeOpcode::CVBG => "cvbg",
SystemZBridgeOpcode::CVBY => "cvby",
SystemZBridgeOpcode::TBEGIN => "tbegin",
SystemZBridgeOpcode::TBEGINC => "tbeginc",
SystemZBridgeOpcode::TEND => "tend",
SystemZBridgeOpcode::TABORT => "tabort",
SystemZBridgeOpcode::ETND => "etnd",
SystemZBridgeOpcode::NTSTG => "ntstg",
SystemZBridgeOpcode::PPA => "ppa",
SystemZBridgeOpcode::SPM => "spm",
SystemZBridgeOpcode::EPSW => "epsw",
SystemZBridgeOpcode::IPM => "ipm",
SystemZBridgeOpcode::LCTL => "lctl",
SystemZBridgeOpcode::LCTLG => "lctlg",
SystemZBridgeOpcode::STCTL => "stctl",
SystemZBridgeOpcode::STCTLG => "stctlg",
SystemZBridgeOpcode::SPKA => "spka",
SystemZBridgeOpcode::TPROT => "tprot",
SystemZBridgeOpcode::SAM24 => "sam24",
SystemZBridgeOpcode::SAM31 => "sam31",
SystemZBridgeOpcode::SAM64 => "sam64",
SystemZBridgeOpcode::IPTE => "ipte",
SystemZBridgeOpcode::IDTE => "idte",
SystemZBridgeOpcode::PTFF => "ptff",
SystemZBridgeOpcode::NOP => "nop",
SystemZBridgeOpcode::NOPR => "nopr",
SystemZBridgeOpcode::BRK => "brk",
SystemZBridgeOpcode::FLOGR => "flogr",
SystemZBridgeOpcode::POPCNT => "popcnt",
SystemZBridgeOpcode::MLGR => "mlgr",
SystemZBridgeOpcode::DLGR => "dlgr",
SystemZBridgeOpcode::MLR => "mlr",
SystemZBridgeOpcode::DLR => "dlr",
SystemZBridgeOpcode::MLG => "mlg",
SystemZBridgeOpcode::DLG => "dlg",
SystemZBridgeOpcode::ML => "ml",
SystemZBridgeOpcode::DL => "dl",
SystemZBridgeOpcode::IIHF => "iihf",
SystemZBridgeOpcode::IILF => "iilf",
SystemZBridgeOpcode::LLIHF => "llihf",
SystemZBridgeOpcode::LLILF => "llilf",
SystemZBridgeOpcode::LLIHH => "llihh",
SystemZBridgeOpcode::LLIHL => "llihl",
SystemZBridgeOpcode::LLILH => "llilh",
SystemZBridgeOpcode::LLILL => "llill",
SystemZBridgeOpcode::OIHH => "oihh",
SystemZBridgeOpcode::OIHL => "oihl",
SystemZBridgeOpcode::OILH => "oilh",
SystemZBridgeOpcode::OILL => "oill",
SystemZBridgeOpcode::BAKR => "bakr",
SystemZBridgeOpcode::BSA => "bsa",
SystemZBridgeOpcode::CFC => "cfc",
SystemZBridgeOpcode::CS => "cs",
SystemZBridgeOpcode::CSG => "csg",
SystemZBridgeOpcode::CSY => "csy",
SystemZBridgeOpcode::CDS => "cds",
SystemZBridgeOpcode::CDSG => "cdsg",
SystemZBridgeOpcode::CDSY => "cdsy",
SystemZBridgeOpcode::TS => "ts",
SystemZBridgeOpcode::CSST => "csst",
SystemZBridgeOpcode::LRVR => "lrvr",
SystemZBridgeOpcode::LRVGR => "lrvgr",
SystemZBridgeOpcode::LRVH => "lrvh",
SystemZBridgeOpcode::STRV => "strv",
SystemZBridgeOpcode::STRVG => "strvg",
SystemZBridgeOpcode::STRVH => "strvh",
SystemZBridgeOpcode::LOCR => "locr",
SystemZBridgeOpcode::LOCGR => "locgr",
SystemZBridgeOpcode::LOC => "loc",
SystemZBridgeOpcode::LOCG => "locg",
SystemZBridgeOpcode::STOC => "stoc",
SystemZBridgeOpcode::STOCG => "stocg",
SystemZBridgeOpcode::VL => "vl",
SystemZBridgeOpcode::VST => "vst",
SystemZBridgeOpcode::VLREP => "vlrep",
SystemZBridgeOpcode::VA => "va",
SystemZBridgeOpcode::VS => "vs",
SystemZBridgeOpcode::VM => "vm",
SystemZBridgeOpcode::VN => "vn",
SystemZBridgeOpcode::VO => "vo",
SystemZBridgeOpcode::VX => "vx",
SystemZBridgeOpcode::VCEQ => "vceq",
SystemZBridgeOpcode::VCH => "vch",
SystemZBridgeOpcode::VCHL => "vchl",
SystemZBridgeOpcode::VMRHG => "vmrhg",
SystemZBridgeOpcode::VMRLG => "vmrlg",
SystemZBridgeOpcode::VPERM => "vperm",
SystemZBridgeOpcode::VPOPCT => "vpopct",
SystemZBridgeOpcode::VZERO => "vzero",
SystemZBridgeOpcode::VONE => "vone",
SystemZBridgeOpcode::SZ_BRIDGE_OPCODE_COUNT => "count",
}
}
pub fn format(&self) -> SystemZFormat {
match self {
SystemZBridgeOpcode::LR
| SystemZBridgeOpcode::SPM
| SystemZBridgeOpcode::BCR
| SystemZBridgeOpcode::BR
| SystemZBridgeOpcode::NOPR => SystemZFormat::RR,
SystemZBridgeOpcode::L
| SystemZBridgeOpcode::ST
| SystemZBridgeOpcode::A
| SystemZBridgeOpcode::S
| SystemZBridgeOpcode::M
| SystemZBridgeOpcode::D
| SystemZBridgeOpcode::N
| SystemZBridgeOpcode::O
| SystemZBridgeOpcode::X
| SystemZBridgeOpcode::C
| SystemZBridgeOpcode::AH
| SystemZBridgeOpcode::SH
| SystemZBridgeOpcode::AL
| SystemZBridgeOpcode::SL
| SystemZBridgeOpcode::LE
| SystemZBridgeOpcode::STE
| SystemZBridgeOpcode::AE
| SystemZBridgeOpcode::SE
| SystemZBridgeOpcode::ME
| SystemZBridgeOpcode::DE
| SystemZBridgeOpcode::CE
| SystemZBridgeOpcode::J => SystemZFormat::RX,
SystemZBridgeOpcode::LGR
| SystemZBridgeOpcode::LTGR
| SystemZBridgeOpcode::LTR
| SystemZBridgeOpcode::AGR
| SystemZBridgeOpcode::SGR
| SystemZBridgeOpcode::MSGR
| SystemZBridgeOpcode::DSGR
| SystemZBridgeOpcode::ALGR
| SystemZBridgeOpcode::SLGR
| SystemZBridgeOpcode::NR
| SystemZBridgeOpcode::OR
| SystemZBridgeOpcode::XR
| SystemZBridgeOpcode::NGR
| SystemZBridgeOpcode::OGR
| SystemZBridgeOpcode::XGR
| SystemZBridgeOpcode::CGR
| SystemZBridgeOpcode::CR
| SystemZBridgeOpcode::CLGR
| SystemZBridgeOpcode::CLR
| SystemZBridgeOpcode::LGFR
| SystemZBridgeOpcode::LGHR
| SystemZBridgeOpcode::LGBR
| SystemZBridgeOpcode::LDR
| SystemZBridgeOpcode::LER
| SystemZBridgeOpcode::ADBR
| SystemZBridgeOpcode::AEBR
| SystemZBridgeOpcode::SDBR
| SystemZBridgeOpcode::SEBR
| SystemZBridgeOpcode::MDBR
| SystemZBridgeOpcode::MEBR
| SystemZBridgeOpcode::DDBR
| SystemZBridgeOpcode::DEBR
| SystemZBridgeOpcode::CDBR
| SystemZBridgeOpcode::CEBR
| SystemZBridgeOpcode::CDFBR
| SystemZBridgeOpcode::CEFBR
| SystemZBridgeOpcode::CFDBR
| SystemZBridgeOpcode::CFEBR
| SystemZBridgeOpcode::MADBR
| SystemZBridgeOpcode::MAEBR
| SystemZBridgeOpcode::MSDBR
| SystemZBridgeOpcode::MSEBR
| SystemZBridgeOpcode::SQDBR
| SystemZBridgeOpcode::SQEBR
| SystemZBridgeOpcode::LDEBR
| SystemZBridgeOpcode::LEDBR
| SystemZBridgeOpcode::LCDFR
| SystemZBridgeOpcode::LNDFR
| SystemZBridgeOpcode::LPDFR
| SystemZBridgeOpcode::FIDBR
| SystemZBridgeOpcode::FIEBR
| SystemZBridgeOpcode::MVCL
| SystemZBridgeOpcode::FLOGR
| SystemZBridgeOpcode::POPCNT
| SystemZBridgeOpcode::MLGR
| SystemZBridgeOpcode::DLGR
| SystemZBridgeOpcode::MLR
| SystemZBridgeOpcode::DLR
| SystemZBridgeOpcode::MSR
| SystemZBridgeOpcode::DR
| SystemZBridgeOpcode::ALR
| SystemZBridgeOpcode::SLR
| SystemZBridgeOpcode::BASR
| SystemZBridgeOpcode::LRVR
| SystemZBridgeOpcode::LRVGR
| SystemZBridgeOpcode::LOCR
| SystemZBridgeOpcode::LOCGR
| SystemZBridgeOpcode::LTEBR
| SystemZBridgeOpcode::LTDBR
| SystemZBridgeOpcode::LTXBR
| SystemZBridgeOpcode::LZER
| SystemZBridgeOpcode::LZDR
| SystemZBridgeOpcode::LZXR
| SystemZBridgeOpcode::CDFTR
| SystemZBridgeOpcode::CXFBR
| SystemZBridgeOpcode::CFXBR
| SystemZBridgeOpcode::LDXBR
| SystemZBridgeOpcode::LEXBR
| SystemZBridgeOpcode::MVCLU
| SystemZBridgeOpcode::MVST
| SystemZBridgeOpcode::ADTR
| SystemZBridgeOpcode::SDTR
| SystemZBridgeOpcode::MDTR
| SystemZBridgeOpcode::DDTR
| SystemZBridgeOpcode::CDTR
| SystemZBridgeOpcode::TBEGIN
| SystemZBridgeOpcode::TBEGINC
| SystemZBridgeOpcode::TEND
| SystemZBridgeOpcode::ETND
| SystemZBridgeOpcode::NTSTG
| SystemZBridgeOpcode::PPA => SystemZFormat::RRE,
SystemZBridgeOpcode::LHI
| SystemZBridgeOpcode::LGHI
| SystemZBridgeOpcode::AHI
| SystemZBridgeOpcode::AGHI
| SystemZBridgeOpcode::CHI
| SystemZBridgeOpcode::CGHI
| SystemZBridgeOpcode::TMHH
| SystemZBridgeOpcode::TMHL
| SystemZBridgeOpcode::TMLH
| SystemZBridgeOpcode::TMLL
| SystemZBridgeOpcode::BRC
| SystemZBridgeOpcode::BRCT
| SystemZBridgeOpcode::BRCTG
| SystemZBridgeOpcode::BRCTH
| SystemZBridgeOpcode::BCTGR => SystemZFormat::RI,
SystemZBridgeOpcode::BRCL
| SystemZBridgeOpcode::BRASL
| SystemZBridgeOpcode::LGFI
| SystemZBridgeOpcode::AFI
| SystemZBridgeOpcode::AGFI
| SystemZBridgeOpcode::AIH
| SystemZBridgeOpcode::ALFI
| SystemZBridgeOpcode::ALGFI
| SystemZBridgeOpcode::CFI
| SystemZBridgeOpcode::CGFI
| SystemZBridgeOpcode::CLFI
| SystemZBridgeOpcode::CLGFI
| SystemZBridgeOpcode::SLFI
| SystemZBridgeOpcode::SLGFI
| SystemZBridgeOpcode::NIHF
| SystemZBridgeOpcode::NILF
| SystemZBridgeOpcode::OIHF
| SystemZBridgeOpcode::OILF
| SystemZBridgeOpcode::XIHF
| SystemZBridgeOpcode::XILF
| SystemZBridgeOpcode::IIHF
| SystemZBridgeOpcode::IILF
| SystemZBridgeOpcode::LLIHF
| SystemZBridgeOpcode::LLILF
| SystemZBridgeOpcode::LLIHH
| SystemZBridgeOpcode::LLIHL
| SystemZBridgeOpcode::LLILH
| SystemZBridgeOpcode::LLILL
| SystemZBridgeOpcode::OIHH
| SystemZBridgeOpcode::OIHL
| SystemZBridgeOpcode::OILH
| SystemZBridgeOpcode::OILL
| SystemZBridgeOpcode::STGRL
| SystemZBridgeOpcode::STRL
| SystemZBridgeOpcode::STHRL => SystemZFormat::RIL,
SystemZBridgeOpcode::LY
| SystemZBridgeOpcode::STY
| SystemZBridgeOpcode::AY
| SystemZBridgeOpcode::SY
| SystemZBridgeOpcode::AHY
| SystemZBridgeOpcode::ALY
| SystemZBridgeOpcode::NY
| SystemZBridgeOpcode::OY
| SystemZBridgeOpcode::XY
| SystemZBridgeOpcode::CY
| SystemZBridgeOpcode::CGY
| SystemZBridgeOpcode::LG
| SystemZBridgeOpcode::STG
| SystemZBridgeOpcode::AG
| SystemZBridgeOpcode::SG
| SystemZBridgeOpcode::MSG
| SystemZBridgeOpcode::DSG
| SystemZBridgeOpcode::NG
| SystemZBridgeOpcode::OG
| SystemZBridgeOpcode::XG
| SystemZBridgeOpcode::CG
| SystemZBridgeOpcode::CLG
| SystemZBridgeOpcode::ALG
| SystemZBridgeOpcode::SLG
| SystemZBridgeOpcode::LGF
| SystemZBridgeOpcode::LGH
| SystemZBridgeOpcode::LHY
| SystemZBridgeOpcode::LAY
| SystemZBridgeOpcode::STHY
| SystemZBridgeOpcode::STCY
| SystemZBridgeOpcode::AGH
| SystemZBridgeOpcode::CGH
| SystemZBridgeOpcode::LD
| SystemZBridgeOpcode::STD
| SystemZBridgeOpcode::AD
| SystemZBridgeOpcode::SD
| SystemZBridgeOpcode::MD
| SystemZBridgeOpcode::DD
| SystemZBridgeOpcode::CD
| SystemZBridgeOpcode::LTG
| SystemZBridgeOpcode::LT
| SystemZBridgeOpcode::CLHY
| SystemZBridgeOpcode::TMY
| SystemZBridgeOpcode::JG
| SystemZBridgeOpcode::LGB
| SystemZBridgeOpcode::CVDG
| SystemZBridgeOpcode::CVDY
| SystemZBridgeOpcode::CVBG
| SystemZBridgeOpcode::CVBY
| SystemZBridgeOpcode::MLG
| SystemZBridgeOpcode::DLG
| SystemZBridgeOpcode::ML
| SystemZBridgeOpcode::DL
| SystemZBridgeOpcode::CSY
| SystemZBridgeOpcode::CDSY
| SystemZBridgeOpcode::LRVH
| SystemZBridgeOpcode::STRV
| SystemZBridgeOpcode::STRVG
| SystemZBridgeOpcode::STRVH
| SystemZBridgeOpcode::LOC
| SystemZBridgeOpcode::LOCG
| SystemZBridgeOpcode::STOC
| SystemZBridgeOpcode::STOCG
| SystemZBridgeOpcode::CS
| SystemZBridgeOpcode::CSG
| SystemZBridgeOpcode::STGSC => SystemZFormat::RXY,
SystemZBridgeOpcode::CLI | SystemZBridgeOpcode::TM | SystemZBridgeOpcode::MVI => {
SystemZFormat::SI
}
SystemZBridgeOpcode::SLL
| SystemZBridgeOpcode::SRL
| SystemZBridgeOpcode::SLA
| SystemZBridgeOpcode::SRA
| SystemZBridgeOpcode::RLL => SystemZFormat::RS,
SystemZBridgeOpcode::SLLG
| SystemZBridgeOpcode::SRLG
| SystemZBridgeOpcode::SLAG
| SystemZBridgeOpcode::SRAG
| SystemZBridgeOpcode::RLLG
| SystemZBridgeOpcode::LCTL
| SystemZBridgeOpcode::LCTLG
| SystemZBridgeOpcode::STCTL
| SystemZBridgeOpcode::STCTLG => SystemZFormat::RSY,
SystemZBridgeOpcode::MVC
| SystemZBridgeOpcode::MVCIN
| SystemZBridgeOpcode::MVN
| SystemZBridgeOpcode::MVZ
| SystemZBridgeOpcode::CVD
| SystemZBridgeOpcode::CVB => SystemZFormat::SS,
SystemZBridgeOpcode::CGIJ
| SystemZBridgeOpcode::CIJ
| SystemZBridgeOpcode::CLGIJ
| SystemZBridgeOpcode::CLIJ
| SystemZBridgeOpcode::CGRJ
| SystemZBridgeOpcode::CRJ
| SystemZBridgeOpcode::CLGRJ
| SystemZBridgeOpcode::CLRJ
| SystemZBridgeOpcode::ALHSIK
| SystemZBridgeOpcode::ALGHSIK
| SystemZBridgeOpcode::AGRK
| SystemZBridgeOpcode::SGRK
| SystemZBridgeOpcode::MSGRK
| SystemZBridgeOpcode::NGRK
| SystemZBridgeOpcode::OGRK
| SystemZBridgeOpcode::XGRK
| SystemZBridgeOpcode::NRK
| SystemZBridgeOpcode::ORK
| SystemZBridgeOpcode::XRK
| SystemZBridgeOpcode::SLLK
| SystemZBridgeOpcode::SRLK
| SystemZBridgeOpcode::SLAK
| SystemZBridgeOpcode::SRAK
| SystemZBridgeOpcode::AGHIK
| SystemZBridgeOpcode::MVGHI
| SystemZBridgeOpcode::MVHI => {
SystemZFormat::RIL }
SystemZBridgeOpcode::NOP | SystemZBridgeOpcode::BRK => SystemZFormat::S,
SystemZBridgeOpcode::BRAS => SystemZFormat::RI,
SystemZBridgeOpcode::BAS => SystemZFormat::RX,
SystemZBridgeOpcode::SPKA => SystemZFormat::RR,
SystemZBridgeOpcode::TPROT => SystemZFormat::S,
SystemZBridgeOpcode::SAM24 => SystemZFormat::S,
SystemZBridgeOpcode::SAM31 => SystemZFormat::S,
SystemZBridgeOpcode::SAM64 => SystemZFormat::S,
SystemZBridgeOpcode::IPTE => SystemZFormat::RRE,
SystemZBridgeOpcode::IDTE => SystemZFormat::RRE,
SystemZBridgeOpcode::PTFF => SystemZFormat::RRE,
SystemZBridgeOpcode::EPSW => SystemZFormat::RRE,
SystemZBridgeOpcode::IPM => SystemZFormat::RRE,
SystemZBridgeOpcode::BI => SystemZFormat::RX,
SystemZBridgeOpcode::TABORT => SystemZFormat::S,
SystemZBridgeOpcode::MVPG => SystemZFormat::RRE,
SystemZBridgeOpcode::BAKR => SystemZFormat::RRE,
SystemZBridgeOpcode::BSA => SystemZFormat::RRE,
SystemZBridgeOpcode::CFC => SystemZFormat::S,
SystemZBridgeOpcode::CDS => SystemZFormat::RS,
SystemZBridgeOpcode::CDSG => SystemZFormat::RSY,
SystemZBridgeOpcode::TS => SystemZFormat::S,
SystemZBridgeOpcode::CSST => SystemZFormat::RRE,
SystemZBridgeOpcode::VL
| SystemZBridgeOpcode::VST
| SystemZBridgeOpcode::VLREP
| SystemZBridgeOpcode::VA
| SystemZBridgeOpcode::VS
| SystemZBridgeOpcode::VM
| SystemZBridgeOpcode::VN
| SystemZBridgeOpcode::VO
| SystemZBridgeOpcode::VX
| SystemZBridgeOpcode::VCEQ
| SystemZBridgeOpcode::VCH
| SystemZBridgeOpcode::VCHL
| SystemZBridgeOpcode::VMRHG
| SystemZBridgeOpcode::VMRLG
| SystemZBridgeOpcode::VPERM
| SystemZBridgeOpcode::VPOPCT
| SystemZBridgeOpcode::VZERO
| SystemZBridgeOpcode::VONE => {
SystemZFormat::RRE }
SystemZBridgeOpcode::SZ_BRIDGE_OPCODE_COUNT => SystemZFormat::S,
_ => SystemZFormat::RX,
}
}
pub fn size_bytes(&self) -> u8 {
match self.format() {
SystemZFormat::RR => 2,
SystemZFormat::RX
| SystemZFormat::RI
| SystemZFormat::SI
| SystemZFormat::RS
| SystemZFormat::S
| SystemZFormat::RRE => 4,
SystemZFormat::RIL | SystemZFormat::RXY | SystemZFormat::RSY | SystemZFormat::SS => 6,
}
}
pub fn is_terminator(&self) -> bool {
matches!(
self,
SystemZBridgeOpcode::BR
| SystemZBridgeOpcode::BCR
| SystemZBridgeOpcode::BRC
| SystemZBridgeOpcode::BRCL
| SystemZBridgeOpcode::J
| SystemZBridgeOpcode::JG
| SystemZBridgeOpcode::BRK
| SystemZBridgeOpcode::CGIJ
| SystemZBridgeOpcode::CIJ
| SystemZBridgeOpcode::CLGIJ
| SystemZBridgeOpcode::CLIJ
| SystemZBridgeOpcode::CGRJ
| SystemZBridgeOpcode::CRJ
| SystemZBridgeOpcode::CLGRJ
| SystemZBridgeOpcode::CLRJ
| SystemZBridgeOpcode::BRCT
| SystemZBridgeOpcode::BRCTG
| SystemZBridgeOpcode::BRCTH
| SystemZBridgeOpcode::BCTGR
)
}
pub fn is_branch(&self) -> bool {
self.is_terminator()
|| matches!(
self,
SystemZBridgeOpcode::BASR
| SystemZBridgeOpcode::BAS
| SystemZBridgeOpcode::BRAS
| SystemZBridgeOpcode::BRASL
| SystemZBridgeOpcode::BI
)
}
pub fn may_load(&self) -> bool {
use SystemZBridgeOpcode::*;
matches!(
self,
L | LY
| LG
| LGF
| LGH
| LGB
| LHY
| LAY
| LE
| LD
| LTG
| LT
| CVD
| CVDG
| CVDY
| CVB
| CVBG
| CVBY
| LGG
| LLGFSG
| VL
| VLREP
| MVC
| MVN
| MVZ
| MVCL
| MVCLU
| MVST
| MVPG
| CS
| CSG
| CSY
| CDS
| CDSG
| CDSY
| CSST
)
}
pub fn may_store(&self) -> bool {
use SystemZBridgeOpcode::*;
matches!(
self,
ST | STY
| STG
| STHY
| STCY
| STE
| STD
| STGRL
| STRL
| STHRL
| STGSC
| MVI
| MVGHI
| MVHI
| MVC
| MVCIN
| MVN
| MVZ
| MVCL
| MVCLU
| MVST
| MVPG
| CS
| CSG
| CSY
| CDS
| CDSG
| CDSY
| CSST
| TS
| STRV
| STRVG
| STRVH
| STOC
| STOCG
| NTSTG
| VST
)
}
pub fn is_compare(&self) -> bool {
use SystemZBridgeOpcode::*;
matches!(
self,
C | CG
| CL
| CLI
| CLG
| CGH
| CY
| CGY
| CGR
| CR
| CLGR
| CLR
| CGFR
| CGFI
| CFI
| CGHI
| CHI
| CLGFI
| CLFI
| CH
| CLHY
| CGIJ
| CIJ
| CLGIJ
| CLIJ
| CGRJ
| CRJ
| CLGRJ
| CLRJ
| TM
| TMY
| TMHH
| TMHL
| TMLH
| TMLL
| CDBR
| CEBR
| CDTR
| CD
| CE
| VCEQ
| VCH
| VCHL
)
}
pub fn is_call(&self) -> bool {
matches!(
self,
SystemZBridgeOpcode::BASR
| SystemZBridgeOpcode::BAS
| SystemZBridgeOpcode::BRAS
| SystemZBridgeOpcode::BRASL
)
}
pub fn is_return(&self) -> bool {
matches!(self, SystemZBridgeOpcode::BR | SystemZBridgeOpcode::BCR)
}
pub fn all_opcodes() -> &'static [SystemZBridgeOpcode] {
&ALL_SZB_OPCODES
}
}
static ALL_SZB_OPCODES: &[SystemZBridgeOpcode] = &[
SystemZBridgeOpcode::L,
SystemZBridgeOpcode::ST,
SystemZBridgeOpcode::LR,
SystemZBridgeOpcode::LGR,
SystemZBridgeOpcode::LGHI,
SystemZBridgeOpcode::LHI,
SystemZBridgeOpcode::LGFI,
SystemZBridgeOpcode::LLILL,
SystemZBridgeOpcode::LLILH,
SystemZBridgeOpcode::LLIHL,
SystemZBridgeOpcode::LLIHH,
SystemZBridgeOpcode::LGFR,
SystemZBridgeOpcode::LGH,
SystemZBridgeOpcode::LGB,
SystemZBridgeOpcode::LGHR,
SystemZBridgeOpcode::LGBR,
SystemZBridgeOpcode::LY,
SystemZBridgeOpcode::LHY,
SystemZBridgeOpcode::LAY,
SystemZBridgeOpcode::LGF,
SystemZBridgeOpcode::LTG,
SystemZBridgeOpcode::LTGR,
SystemZBridgeOpcode::LT,
SystemZBridgeOpcode::LTR,
SystemZBridgeOpcode::LTEBR,
SystemZBridgeOpcode::LTDBR,
SystemZBridgeOpcode::LTXBR,
SystemZBridgeOpcode::LZER,
SystemZBridgeOpcode::LZDR,
SystemZBridgeOpcode::LZXR,
SystemZBridgeOpcode::STG,
SystemZBridgeOpcode::STY,
SystemZBridgeOpcode::STHY,
SystemZBridgeOpcode::STCY,
SystemZBridgeOpcode::STGRL,
SystemZBridgeOpcode::STRL,
SystemZBridgeOpcode::STHRL,
SystemZBridgeOpcode::STGSC,
SystemZBridgeOpcode::AGR,
SystemZBridgeOpcode::SGR,
SystemZBridgeOpcode::MSGR,
SystemZBridgeOpcode::DSGR,
SystemZBridgeOpcode::AGRK,
SystemZBridgeOpcode::SGRK,
SystemZBridgeOpcode::MSGRK,
SystemZBridgeOpcode::AGFI,
SystemZBridgeOpcode::AGHI,
SystemZBridgeOpcode::AGHIK,
SystemZBridgeOpcode::ALGR,
SystemZBridgeOpcode::SLGR,
SystemZBridgeOpcode::MSR,
SystemZBridgeOpcode::DR,
SystemZBridgeOpcode::ALR,
SystemZBridgeOpcode::SLR,
SystemZBridgeOpcode::A,
SystemZBridgeOpcode::S,
SystemZBridgeOpcode::M,
SystemZBridgeOpcode::D,
SystemZBridgeOpcode::AG,
SystemZBridgeOpcode::SG,
SystemZBridgeOpcode::MSG,
SystemZBridgeOpcode::DSG,
SystemZBridgeOpcode::AH,
SystemZBridgeOpcode::SH,
SystemZBridgeOpcode::AL,
SystemZBridgeOpcode::SL,
SystemZBridgeOpcode::AGH,
SystemZBridgeOpcode::ALG,
SystemZBridgeOpcode::SLG,
SystemZBridgeOpcode::AY,
SystemZBridgeOpcode::SY,
SystemZBridgeOpcode::AHY,
SystemZBridgeOpcode::ALY,
SystemZBridgeOpcode::AFI,
SystemZBridgeOpcode::AIH,
SystemZBridgeOpcode::AHI,
SystemZBridgeOpcode::ALFI,
SystemZBridgeOpcode::ALGFI,
SystemZBridgeOpcode::ALHSIK,
SystemZBridgeOpcode::ALGHSIK,
SystemZBridgeOpcode::SLFI,
SystemZBridgeOpcode::SLGFI,
SystemZBridgeOpcode::N,
SystemZBridgeOpcode::O,
SystemZBridgeOpcode::X,
SystemZBridgeOpcode::NG,
SystemZBridgeOpcode::OG,
SystemZBridgeOpcode::XG,
SystemZBridgeOpcode::NR,
SystemZBridgeOpcode::OR,
SystemZBridgeOpcode::XR,
SystemZBridgeOpcode::NGR,
SystemZBridgeOpcode::OGR,
SystemZBridgeOpcode::XGR,
SystemZBridgeOpcode::NGRK,
SystemZBridgeOpcode::OGRK,
SystemZBridgeOpcode::XGRK,
SystemZBridgeOpcode::NRK,
SystemZBridgeOpcode::ORK,
SystemZBridgeOpcode::XRK,
SystemZBridgeOpcode::NY,
SystemZBridgeOpcode::OY,
SystemZBridgeOpcode::XY,
SystemZBridgeOpcode::NIHF,
SystemZBridgeOpcode::NILF,
SystemZBridgeOpcode::OIHF,
SystemZBridgeOpcode::OILF,
SystemZBridgeOpcode::XIHF,
SystemZBridgeOpcode::XILF,
SystemZBridgeOpcode::CGR,
SystemZBridgeOpcode::CR,
SystemZBridgeOpcode::CGFR,
SystemZBridgeOpcode::CLGR,
SystemZBridgeOpcode::CLR,
SystemZBridgeOpcode::C,
SystemZBridgeOpcode::CG,
SystemZBridgeOpcode::CL,
SystemZBridgeOpcode::CLI,
SystemZBridgeOpcode::CLG,
SystemZBridgeOpcode::CGH,
SystemZBridgeOpcode::CY,
SystemZBridgeOpcode::CGY,
SystemZBridgeOpcode::CGFI,
SystemZBridgeOpcode::CFI,
SystemZBridgeOpcode::CGHI,
SystemZBridgeOpcode::CHI,
SystemZBridgeOpcode::CLGFI,
SystemZBridgeOpcode::CLFI,
SystemZBridgeOpcode::CH,
SystemZBridgeOpcode::CLHY,
SystemZBridgeOpcode::CGIJ,
SystemZBridgeOpcode::CIJ,
SystemZBridgeOpcode::CLGIJ,
SystemZBridgeOpcode::CLIJ,
SystemZBridgeOpcode::CGRJ,
SystemZBridgeOpcode::CRJ,
SystemZBridgeOpcode::CLGRJ,
SystemZBridgeOpcode::CLRJ,
SystemZBridgeOpcode::TM,
SystemZBridgeOpcode::TMY,
SystemZBridgeOpcode::TMHH,
SystemZBridgeOpcode::TMHL,
SystemZBridgeOpcode::TMLH,
SystemZBridgeOpcode::TMLL,
SystemZBridgeOpcode::BRC,
SystemZBridgeOpcode::BRCL,
SystemZBridgeOpcode::BCR,
SystemZBridgeOpcode::BR,
SystemZBridgeOpcode::J,
SystemZBridgeOpcode::JG,
SystemZBridgeOpcode::BASR,
SystemZBridgeOpcode::BAS,
SystemZBridgeOpcode::BRAS,
SystemZBridgeOpcode::BRASL,
SystemZBridgeOpcode::BRCT,
SystemZBridgeOpcode::BRCTG,
SystemZBridgeOpcode::BRCTH,
SystemZBridgeOpcode::BI,
SystemZBridgeOpcode::BCTGR,
SystemZBridgeOpcode::SLL,
SystemZBridgeOpcode::SRL,
SystemZBridgeOpcode::SLA,
SystemZBridgeOpcode::SRA,
SystemZBridgeOpcode::SLLG,
SystemZBridgeOpcode::SRLG,
SystemZBridgeOpcode::SLAG,
SystemZBridgeOpcode::SRAG,
SystemZBridgeOpcode::SLLK,
SystemZBridgeOpcode::SRLK,
SystemZBridgeOpcode::SLAK,
SystemZBridgeOpcode::SRAK,
SystemZBridgeOpcode::RLL,
SystemZBridgeOpcode::RLLG,
SystemZBridgeOpcode::MVC,
SystemZBridgeOpcode::MVCIN,
SystemZBridgeOpcode::MVN,
SystemZBridgeOpcode::MVZ,
SystemZBridgeOpcode::MVCL,
SystemZBridgeOpcode::MVCLU,
SystemZBridgeOpcode::MVST,
SystemZBridgeOpcode::MVPG,
SystemZBridgeOpcode::MVI,
SystemZBridgeOpcode::MVGHI,
SystemZBridgeOpcode::MVHI,
SystemZBridgeOpcode::LR,
SystemZBridgeOpcode::L,
SystemZBridgeOpcode::LDR,
SystemZBridgeOpcode::LER,
SystemZBridgeOpcode::ADBR,
SystemZBridgeOpcode::AEBR,
SystemZBridgeOpcode::SDBR,
SystemZBridgeOpcode::SEBR,
SystemZBridgeOpcode::MDBR,
SystemZBridgeOpcode::MEBR,
SystemZBridgeOpcode::DDBR,
SystemZBridgeOpcode::DEBR,
SystemZBridgeOpcode::CDBR,
SystemZBridgeOpcode::CEBR,
SystemZBridgeOpcode::CDFBR,
SystemZBridgeOpcode::CEFBR,
SystemZBridgeOpcode::CFDBR,
SystemZBridgeOpcode::CFEBR,
SystemZBridgeOpcode::CDFTR,
SystemZBridgeOpcode::CXFBR,
SystemZBridgeOpcode::CFXBR,
SystemZBridgeOpcode::MADBR,
SystemZBridgeOpcode::MAEBR,
SystemZBridgeOpcode::MSDBR,
SystemZBridgeOpcode::MSEBR,
SystemZBridgeOpcode::SQDBR,
SystemZBridgeOpcode::SQEBR,
SystemZBridgeOpcode::LDEBR,
SystemZBridgeOpcode::LEDBR,
SystemZBridgeOpcode::LDXBR,
SystemZBridgeOpcode::LEXBR,
SystemZBridgeOpcode::LCDFR,
SystemZBridgeOpcode::LNDFR,
SystemZBridgeOpcode::LPDFR,
SystemZBridgeOpcode::FIDBR,
SystemZBridgeOpcode::FIEBR,
SystemZBridgeOpcode::LE,
SystemZBridgeOpcode::LD,
SystemZBridgeOpcode::STE,
SystemZBridgeOpcode::STD,
SystemZBridgeOpcode::AE,
SystemZBridgeOpcode::AD,
SystemZBridgeOpcode::SE,
SystemZBridgeOpcode::SD,
SystemZBridgeOpcode::ME,
SystemZBridgeOpcode::MD,
SystemZBridgeOpcode::DE,
SystemZBridgeOpcode::DD,
SystemZBridgeOpcode::CE,
SystemZBridgeOpcode::CD,
SystemZBridgeOpcode::ADTR,
SystemZBridgeOpcode::SDTR,
SystemZBridgeOpcode::MDTR,
SystemZBridgeOpcode::DDTR,
SystemZBridgeOpcode::CDTR,
SystemZBridgeOpcode::CVD,
SystemZBridgeOpcode::CVDG,
SystemZBridgeOpcode::CVDY,
SystemZBridgeOpcode::CVB,
SystemZBridgeOpcode::CVBG,
SystemZBridgeOpcode::CVBY,
SystemZBridgeOpcode::TBEGIN,
SystemZBridgeOpcode::TBEGINC,
SystemZBridgeOpcode::TEND,
SystemZBridgeOpcode::TABORT,
SystemZBridgeOpcode::ETND,
SystemZBridgeOpcode::NTSTG,
SystemZBridgeOpcode::PPA,
SystemZBridgeOpcode::SPM,
SystemZBridgeOpcode::EPSW,
SystemZBridgeOpcode::IPM,
SystemZBridgeOpcode::LCTL,
SystemZBridgeOpcode::LCTLG,
SystemZBridgeOpcode::STCTL,
SystemZBridgeOpcode::STCTLG,
SystemZBridgeOpcode::SPKA,
SystemZBridgeOpcode::TPROT,
SystemZBridgeOpcode::SAM24,
SystemZBridgeOpcode::SAM31,
SystemZBridgeOpcode::SAM64,
SystemZBridgeOpcode::IPTE,
SystemZBridgeOpcode::IDTE,
SystemZBridgeOpcode::PTFF,
SystemZBridgeOpcode::NOP,
SystemZBridgeOpcode::NOPR,
SystemZBridgeOpcode::BRK,
SystemZBridgeOpcode::FLOGR,
SystemZBridgeOpcode::POPCNT,
SystemZBridgeOpcode::MLGR,
SystemZBridgeOpcode::DLGR,
SystemZBridgeOpcode::MLR,
SystemZBridgeOpcode::DLR,
SystemZBridgeOpcode::MLG,
SystemZBridgeOpcode::DLG,
SystemZBridgeOpcode::ML,
SystemZBridgeOpcode::DL,
SystemZBridgeOpcode::IIHF,
SystemZBridgeOpcode::IILF,
SystemZBridgeOpcode::LLIHF,
SystemZBridgeOpcode::LLILF,
SystemZBridgeOpcode::LLIHH,
SystemZBridgeOpcode::LLIHL,
SystemZBridgeOpcode::LLILH,
SystemZBridgeOpcode::LLILL,
SystemZBridgeOpcode::OIHH,
SystemZBridgeOpcode::OIHL,
SystemZBridgeOpcode::OILH,
SystemZBridgeOpcode::OILL,
SystemZBridgeOpcode::BAKR,
SystemZBridgeOpcode::BSA,
SystemZBridgeOpcode::CFC,
SystemZBridgeOpcode::CS,
SystemZBridgeOpcode::CSG,
SystemZBridgeOpcode::CSY,
SystemZBridgeOpcode::CDS,
SystemZBridgeOpcode::CDSG,
SystemZBridgeOpcode::CDSY,
SystemZBridgeOpcode::TS,
SystemZBridgeOpcode::CSST,
SystemZBridgeOpcode::LRVR,
SystemZBridgeOpcode::LRVGR,
SystemZBridgeOpcode::LRVH,
SystemZBridgeOpcode::STRV,
SystemZBridgeOpcode::STRVG,
SystemZBridgeOpcode::STRVH,
SystemZBridgeOpcode::LOCR,
SystemZBridgeOpcode::LOCGR,
SystemZBridgeOpcode::LOC,
SystemZBridgeOpcode::LOCG,
SystemZBridgeOpcode::STOC,
SystemZBridgeOpcode::STOCG,
SystemZBridgeOpcode::VL,
SystemZBridgeOpcode::VST,
SystemZBridgeOpcode::VLREP,
SystemZBridgeOpcode::VA,
SystemZBridgeOpcode::VS,
SystemZBridgeOpcode::VM,
SystemZBridgeOpcode::VN,
SystemZBridgeOpcode::VO,
SystemZBridgeOpcode::VX,
SystemZBridgeOpcode::VCEQ,
SystemZBridgeOpcode::VCH,
SystemZBridgeOpcode::VCHL,
SystemZBridgeOpcode::VMRHG,
SystemZBridgeOpcode::VMRLG,
SystemZBridgeOpcode::VPERM,
SystemZBridgeOpcode::VPOPCT,
SystemZBridgeOpcode::VZERO,
SystemZBridgeOpcode::VONE,
];
pub fn map_llvm_to_systemz_opcode(opcode: Opcode) -> Option<SystemZOpcode> {
match opcode {
Opcode::Add => Some(SystemZOpcode::A),
Opcode::Sub => Some(SystemZOpcode::S),
Opcode::Mul => Some(SystemZOpcode::M),
Opcode::SDiv => Some(SystemZOpcode::D),
Opcode::And => Some(SystemZOpcode::N),
Opcode::Or => Some(SystemZOpcode::O),
Opcode::Xor => Some(SystemZOpcode::X),
Opcode::Load => Some(SystemZOpcode::L),
Opcode::Store => Some(SystemZOpcode::ST),
Opcode::ICmp => Some(SystemZOpcode::C),
Opcode::Br => Some(SystemZOpcode::J),
Opcode::Ret => Some(SystemZOpcode::BR),
Opcode::Shl => Some(SystemZOpcode::SLL),
Opcode::LShr => Some(SystemZOpcode::SRL),
Opcode::AShr => Some(SystemZOpcode::SRA),
Opcode::FAdd => Some(SystemZOpcode::AD),
Opcode::FSub => Some(SystemZOpcode::SD),
Opcode::FMul => Some(SystemZOpcode::MD),
Opcode::FDiv => Some(SystemZOpcode::DD),
Opcode::FCmp => Some(SystemZOpcode::CD),
_ => None,
}
}
pub fn map_llvm_to_sz_bridge_opcode(opcode: Opcode) -> Option<SystemZBridgeOpcode> {
match opcode {
Opcode::Add => Some(SystemZBridgeOpcode::A),
Opcode::Sub => Some(SystemZBridgeOpcode::S),
Opcode::Mul => Some(SystemZBridgeOpcode::M),
Opcode::SDiv => Some(SystemZBridgeOpcode::D),
Opcode::UDiv => Some(SystemZBridgeOpcode::DLR),
Opcode::And => Some(SystemZBridgeOpcode::N),
Opcode::Or => Some(SystemZBridgeOpcode::O),
Opcode::Xor => Some(SystemZBridgeOpcode::X),
Opcode::Shl => Some(SystemZBridgeOpcode::SLL),
Opcode::LShr => Some(SystemZBridgeOpcode::SRL),
Opcode::AShr => Some(SystemZBridgeOpcode::SRA),
Opcode::Load => Some(SystemZBridgeOpcode::L),
Opcode::Store => Some(SystemZBridgeOpcode::ST),
Opcode::ICmp => Some(SystemZBridgeOpcode::C),
Opcode::FCmp => Some(SystemZBridgeOpcode::CD),
Opcode::Br => Some(SystemZBridgeOpcode::J),
Opcode::Ret => Some(SystemZBridgeOpcode::BR),
Opcode::Call => Some(SystemZBridgeOpcode::BRASL),
Opcode::FAdd => Some(SystemZBridgeOpcode::AD),
Opcode::FSub => Some(SystemZBridgeOpcode::SD),
Opcode::FMul => Some(SystemZBridgeOpcode::MD),
Opcode::FDiv => Some(SystemZBridgeOpcode::DD),
Opcode::Select => Some(SystemZBridgeOpcode::LOCGR),
Opcode::SExt => Some(SystemZBridgeOpcode::LGBR),
Opcode::ZExt => Some(SystemZBridgeOpcode::LLGFSG),
Opcode::Trunc => Some(SystemZBridgeOpcode::LR),
Opcode::SIToFp => Some(SystemZBridgeOpcode::CDFBR),
Opcode::UIToFp => Some(SystemZBridgeOpcode::CDFBR),
Opcode::FpToSI => Some(SystemZBridgeOpcode::CFDBR),
Opcode::FpToUI => Some(SystemZBridgeOpcode::CFDBR),
_ => None,
}
}
#[derive(Debug, Clone)]
pub struct SystemZFrameInfo {
pub local_area_size: i64,
pub outgoing_arg_size: i64,
pub callee_saved_offset: i64,
pub fp_save_offset: i64,
pub ra_save_offset: i64,
pub total_frame_size: i64,
pub has_fp: bool,
pub has_back_chain: bool,
pub uses_red_zone: bool,
pub stack_alignment: i64,
pub back_chain_offset: i64,
}
impl Default for SystemZFrameInfo {
fn default() -> Self {
SystemZFrameInfo {
local_area_size: 0,
outgoing_arg_size: 0,
callee_saved_offset: 16,
fp_save_offset: 88, ra_save_offset: 112, total_frame_size: 160,
has_fp: false,
has_back_chain: true,
uses_red_zone: true,
stack_alignment: 8,
back_chain_offset: 0,
}
}
}
pub struct SystemZFrameLowering {
pub cc: SystemZCallingConvention,
pub frame_info: SystemZFrameInfo,
pub use_fp: bool,
pub stack_alignment: u32,
}
impl SystemZFrameLowering {
pub fn new(cc: SystemZCallingConvention) -> Self {
SystemZFrameLowering {
cc,
frame_info: SystemZFrameInfo::default(),
use_fp: false,
stack_alignment: SYSTEMZ_STACK_ALIGNMENT,
}
}
pub fn default_lowering() -> Self {
Self::new(SystemZCallingConvention::new())
}
pub fn compute_frame_size(
&mut self,
num_callee_saved_gprs: usize,
num_callee_saved_fprs: usize,
local_size: i64,
outgoing_arg_size: i64,
) -> i64 {
let save_area = SYSTEMZ_REG_SAVE_SIZE as i64;
let gpr_save = (num_callee_saved_gprs as i64) * 8;
let fpr_save = (num_callee_saved_fprs as i64) * 8;
let mut total = save_area + gpr_save + fpr_save + local_size + outgoing_arg_size;
total = (total + self.stack_alignment as i64 - 1) & !(self.stack_alignment as i64 - 1);
self.frame_info.local_area_size = local_size;
self.frame_info.outgoing_arg_size = outgoing_arg_size;
self.frame_info.total_frame_size = total;
total
}
pub fn get_gpr_save_offset(&self, reg_idx: usize) -> i64 {
SYSTEMZ_REG_SAVE_OFFSET as i64 + (reg_idx as i64 * 8)
}
pub fn get_fpr_save_offset(&self, reg_idx: usize) -> i64 {
SYSTEMZ_REG_SAVE_OFFSET as i64 + (SZ_GPR_COUNT as i64 * 8) + (reg_idx as i64 * 8)
}
pub fn gen_prologue(&self) -> Vec<String> {
let mut insns = Vec::new();
if self.use_fp {
insns.push(format!(
"stg %r11, {}(%r15)",
self.frame_info.fp_save_offset
));
insns.push("lgr %r11, %r15".to_string());
}
if self.frame_info.total_frame_size > 0 {
let frame = self.frame_info.total_frame_size;
if frame <= SYSTEMZ_MAX_DISP_RX {
insns.push(format!("aghi %r15, -{}", frame));
} else {
insns.push(format!("agfi %r1, -{}", frame));
insns.push("agr %r15, %r1".to_string());
}
}
insns
}
pub fn gen_epilogue(&self) -> Vec<String> {
let mut insns = Vec::new();
if self.use_fp {
insns.push("lgr %r15, %r11".to_string());
insns.push(format!("lg %r11, {}(%r15)", self.frame_info.fp_save_offset));
} else if self.frame_info.total_frame_size > 0 {
let frame = self.frame_info.total_frame_size;
if frame <= SYSTEMZ_MAX_DISP_RX {
insns.push(format!("aghi %r15, {}", frame));
} else {
insns.push(format!("agfi %r1, {}", frame));
insns.push("agr %r15, %r1".to_string());
}
}
insns.push("br %r14".to_string());
insns
}
pub fn gen_save_callee_saved(&self, regs: &[SystemZRegister]) -> Vec<String> {
let mut insns = Vec::new();
for reg in regs {
if let Some(idx) = SystemZRegisterInfo::get_index(*reg) {
let offset = self.get_gpr_save_offset(idx);
if offset <= SYSTEMZ_MAX_DISP_RXY {
insns.push(format!(
"stg {}, {}(%r15)",
SystemZRegisterInfo::get_name(*reg),
offset
));
}
}
}
insns
}
pub fn gen_restore_callee_saved(&self, regs: &[SystemZRegister]) -> Vec<String> {
let mut insns = Vec::new();
for reg in regs.iter().rev() {
if let Some(idx) = SystemZRegisterInfo::get_index(*reg) {
let offset = self.get_gpr_save_offset(idx);
if offset <= SYSTEMZ_MAX_DISP_RXY {
insns.push(format!(
"lg {}, {}(%r15)",
SystemZRegisterInfo::get_name(*reg),
offset
));
}
}
}
insns
}
pub fn get_total_frame_size(&self) -> i64 {
self.frame_info.total_frame_size
}
pub fn has_back_chain(&self) -> bool {
self.frame_info.has_back_chain
}
pub fn set_use_fp(&mut self, use_fp: bool) {
self.use_fp = use_fp;
}
pub fn fits_in_red_zone(&self) -> bool {
self.frame_info.total_frame_size <= SYSTEMZ_RED_ZONE_SIZE as i64
}
pub fn get_sp_offset(&self) -> i64 {
if self.use_fp {
self.frame_info.total_frame_size
} else {
0
}
}
pub fn needs_frame_pointer(&self, has_var_sized_objects: bool, is_leaf: bool) -> bool {
has_var_sized_objects || (!is_leaf && self.frame_info.total_frame_size > 4096)
}
}
impl SystemZRegisterInfo {
pub fn from_index(idx: u32) -> Option<SystemZRegister> {
match idx {
0 => Some(SystemZRegister::R0),
1 => Some(SystemZRegister::R1),
2 => Some(SystemZRegister::R2),
3 => Some(SystemZRegister::R3),
4 => Some(SystemZRegister::R4),
5 => Some(SystemZRegister::R5),
6 => Some(SystemZRegister::R6),
7 => Some(SystemZRegister::R7),
8 => Some(SystemZRegister::R8),
9 => Some(SystemZRegister::R9),
10 => Some(SystemZRegister::R10),
11 => Some(SystemZRegister::R11),
12 => Some(SystemZRegister::R12),
13 => Some(SystemZRegister::R13),
14 => Some(SystemZRegister::R14),
15 => Some(SystemZRegister::R15),
_ => None,
}
}
pub fn from_fpr_index(idx: u32) -> Option<SystemZRegister> {
match idx {
0 => Some(SystemZRegister::F0),
1 => Some(SystemZRegister::F1),
2 => Some(SystemZRegister::F2),
3 => Some(SystemZRegister::F3),
4 => Some(SystemZRegister::F4),
5 => Some(SystemZRegister::F5),
6 => Some(SystemZRegister::F6),
7 => Some(SystemZRegister::F7),
8 => Some(SystemZRegister::F8),
9 => Some(SystemZRegister::F9),
10 => Some(SystemZRegister::F10),
11 => Some(SystemZRegister::F11),
12 => Some(SystemZRegister::F12),
13 => Some(SystemZRegister::F13),
14 => Some(SystemZRegister::F14),
15 => Some(SystemZRegister::F15),
_ => None,
}
}
pub fn from_ar_index(idx: u32) -> Option<SystemZRegister> {
match idx {
0 => Some(SystemZRegister::A0),
1 => Some(SystemZRegister::A1),
2 => Some(SystemZRegister::A2),
3 => Some(SystemZRegister::A3),
4 => Some(SystemZRegister::A4),
5 => Some(SystemZRegister::A5),
6 => Some(SystemZRegister::A6),
7 => Some(SystemZRegister::A7),
8 => Some(SystemZRegister::A8),
9 => Some(SystemZRegister::A9),
10 => Some(SystemZRegister::A10),
11 => Some(SystemZRegister::A11),
12 => Some(SystemZRegister::A12),
13 => Some(SystemZRegister::A13),
14 => Some(SystemZRegister::A14),
15 => Some(SystemZRegister::A15),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SystemZCondCode {
Unconditional = 15,
EQ = 8,
NE = 7,
LT = 4,
NL = 11,
GT = 2,
NH = 13,
LE = 12,
HE = 10,
O = 1,
NO = 14,
MI = 4,
PL = 11,
LS = 12,
HS = 10,
LO = 4,
HI = 2,
Never = 0,
}
impl SystemZCondCode {
pub fn mask(&self) -> u8 {
*self as u8
}
pub fn name(&self) -> &'static str {
match self {
SystemZCondCode::Unconditional => "unconditional",
SystemZCondCode::EQ => "eq",
SystemZCondCode::NE => "ne",
SystemZCondCode::LT | SystemZCondCode::MI | SystemZCondCode::LO => "lt",
SystemZCondCode::NL | SystemZCondCode::PL => "nl",
SystemZCondCode::GT | SystemZCondCode::HI => "gt",
SystemZCondCode::NH => "nh",
SystemZCondCode::LE | SystemZCondCode::LS => "le",
SystemZCondCode::HE | SystemZCondCode::HS => "he",
SystemZCondCode::O => "o",
SystemZCondCode::NO => "no",
SystemZCondCode::Never => "never",
}
}
pub fn inverse(&self) -> SystemZCondCode {
match self {
SystemZCondCode::Unconditional => SystemZCondCode::Unconditional,
SystemZCondCode::EQ => SystemZCondCode::NE,
SystemZCondCode::NE => SystemZCondCode::EQ,
SystemZCondCode::LT | SystemZCondCode::MI | SystemZCondCode::LO => SystemZCondCode::HE,
SystemZCondCode::NL | SystemZCondCode::PL => SystemZCondCode::LT,
SystemZCondCode::GT | SystemZCondCode::HI => SystemZCondCode::LE,
SystemZCondCode::NH => SystemZCondCode::GT,
SystemZCondCode::LE | SystemZCondCode::LS => SystemZCondCode::GT,
SystemZCondCode::HE | SystemZCondCode::HS => SystemZCondCode::LT,
SystemZCondCode::O => SystemZCondCode::NO,
SystemZCondCode::NO => SystemZCondCode::O,
SystemZCondCode::Never => SystemZCondCode::Unconditional,
}
}
pub fn swap_operands(&self) -> SystemZCondCode {
match self {
SystemZCondCode::EQ => SystemZCondCode::EQ,
SystemZCondCode::NE => SystemZCondCode::NE,
SystemZCondCode::LT | SystemZCondCode::LO | SystemZCondCode::MI => SystemZCondCode::GT,
SystemZCondCode::GT | SystemZCondCode::HI => SystemZCondCode::LT,
SystemZCondCode::LE | SystemZCondCode::LS => SystemZCondCode::HE,
SystemZCondCode::HE | SystemZCondCode::HS => SystemZCondCode::LE,
other => *other,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BridgeFeature {
Vector,
DFP,
TX,
GS,
MSA,
DistinctOps,
LoadStoreOnCond,
HighWord,
InterlockedAccess,
Popcnt,
FastSerialization,
ExecutionHint,
MiscExtensions,
SSE,
SSE2,
AVX,
AVX2,
AVX512,
FMA,
CRC,
Crypto,
PGO,
SoftFloat,
PIC,
LargeCodeModel,
}
impl fmt::Display for BridgeFeature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
BridgeFeature::Vector => "vector",
BridgeFeature::DFP => "dfp",
BridgeFeature::TX => "tx",
BridgeFeature::GS => "gs",
BridgeFeature::MSA => "msa",
BridgeFeature::DistinctOps => "distinct-ops",
BridgeFeature::LoadStoreOnCond => "load-store-on-cond",
BridgeFeature::HighWord => "high-word",
BridgeFeature::InterlockedAccess => "interlocked-access",
BridgeFeature::Popcnt => "popcnt",
BridgeFeature::FastSerialization => "fast-serialization",
BridgeFeature::ExecutionHint => "execution-hint",
BridgeFeature::MiscExtensions => "misc-extensions",
BridgeFeature::SSE => "sse",
BridgeFeature::SSE2 => "sse2",
BridgeFeature::AVX => "avx",
BridgeFeature::AVX2 => "avx2",
BridgeFeature::AVX512 => "avx512",
BridgeFeature::FMA => "fma",
BridgeFeature::CRC => "crc",
BridgeFeature::Crypto => "crypto",
BridgeFeature::PGO => "pgo",
BridgeFeature::SoftFloat => "soft-float",
BridgeFeature::PIC => "pic",
BridgeFeature::LargeCodeModel => "large-code-model",
};
write!(f, "{}", s)
}
}
#[derive(Debug, Clone, Default)]
pub struct BridgeFeatures {
flags: HashSet<BridgeFeature>,
}
impl BridgeFeatures {
pub fn enable(&mut self, feature: BridgeFeature) {
self.flags.insert(feature);
}
pub fn disable(&mut self, feature: BridgeFeature) {
self.flags.remove(&feature);
}
pub fn has(&self, feature: BridgeFeature) -> bool {
self.flags.contains(&feature)
}
pub fn enable_z14_features(&mut self) {
self.flags.insert(BridgeFeature::Vector);
self.flags.insert(BridgeFeature::DFP);
self.flags.insert(BridgeFeature::TX);
self.flags.insert(BridgeFeature::GS);
self.flags.insert(BridgeFeature::MSA);
self.flags.insert(BridgeFeature::DistinctOps);
self.flags.insert(BridgeFeature::LoadStoreOnCond);
self.flags.insert(BridgeFeature::HighWord);
self.flags.insert(BridgeFeature::InterlockedAccess);
self.flags.insert(BridgeFeature::Popcnt);
self.flags.insert(BridgeFeature::FastSerialization);
self.flags.insert(BridgeFeature::MiscExtensions);
}
pub fn enable_z16_features(&mut self) {
self.enable_z14_features();
self.flags.insert(BridgeFeature::ExecutionHint);
}
pub fn enable_x86_features(&mut self) {
self.flags.insert(BridgeFeature::SSE);
self.flags.insert(BridgeFeature::SSE2);
self.flags.insert(BridgeFeature::AVX);
self.flags.insert(BridgeFeature::AVX2);
self.flags.insert(BridgeFeature::FMA);
self.flags.insert(BridgeFeature::CRC);
self.flags.insert(BridgeFeature::Crypto);
}
pub fn list_enabled(&self) -> Vec<BridgeFeature> {
let mut v: Vec<_> = self.flags.iter().copied().collect();
v.sort_by_key(|f| format!("{:?}", f));
v
}
pub fn to_feature_string(&self) -> String {
let mut feats: Vec<String> = self.flags.iter().map(|f| format!("+{}", f)).collect();
feats.sort();
feats.join(",")
}
pub fn from_string(s: &str) -> Self {
let mut features = Self::default();
for part in s.split(',') {
let part = part.trim();
if part.is_empty() {
continue;
}
let (enabled, name) = if let Some(stripped) = part.strip_prefix('+') {
(true, stripped)
} else if let Some(stripped) = part.strip_prefix('-') {
(false, stripped)
} else {
(true, part)
};
if let Some(feat) = Self::parse_feature(name) {
if enabled {
features.enable(feat);
} else {
features.disable(feat);
}
}
}
features
}
fn parse_feature(name: &str) -> Option<BridgeFeature> {
match name {
"vector" | "vx" => Some(BridgeFeature::Vector),
"dfp" | "decimal-float" => Some(BridgeFeature::DFP),
"tx" | "transactional-execution" => Some(BridgeFeature::TX),
"gs" | "guarded-storage" => Some(BridgeFeature::GS),
"msa" | "message-security-assist" => Some(BridgeFeature::MSA),
"distinct-ops" => Some(BridgeFeature::DistinctOps),
"load-store-on-cond" | "lsc" => Some(BridgeFeature::LoadStoreOnCond),
"high-word" | "hw" => Some(BridgeFeature::HighWord),
"interlocked-access" | "ia" => Some(BridgeFeature::InterlockedAccess),
"popcnt" | "population-count" => Some(BridgeFeature::Popcnt),
"fast-serialization" | "fs" => Some(BridgeFeature::FastSerialization),
"execution-hint" | "eh" => Some(BridgeFeature::ExecutionHint),
"misc-extensions" | "me" => Some(BridgeFeature::MiscExtensions),
"sse" => Some(BridgeFeature::SSE),
"sse2" => Some(BridgeFeature::SSE2),
"avx" => Some(BridgeFeature::AVX),
"avx2" => Some(BridgeFeature::AVX2),
"avx512" | "avx512f" => Some(BridgeFeature::AVX512),
"fma" => Some(BridgeFeature::FMA),
"crc" => Some(BridgeFeature::CRC),
"crypto" => Some(BridgeFeature::Crypto),
"pgo" => Some(BridgeFeature::PGO),
"soft-float" => Some(BridgeFeature::SoftFloat),
"pic" => Some(BridgeFeature::PIC),
"large-code-model" => Some(BridgeFeature::LargeCodeModel),
_ => None,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct BridgeStats {
pub functions_processed: usize,
pub isel_cycles: usize,
pub opt_cycles: usize,
pub ra_cycles: usize,
pub frame_cycles: usize,
pub post_ra_opt_cycles: usize,
pub instructions_eliminated: usize,
pub pattern_matches: usize,
pub spill_slots: usize,
pub abi_conversions: usize,
pub back_chain_setups: usize,
pub i128_splits: usize,
pub vectorized_loops: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BridgeError {
UnsupportedArchPair {
source: SystemZBridgeArch,
target: SystemZBridgeArch,
},
ISelFailed { opcode: u32, reason: String },
RegAllocFailed { reason: String },
FrameLoweringFailed { reason: String },
Unimplemented { feature: String },
BackChainError { reason: String },
Internal(String),
}
impl fmt::Display for BridgeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BridgeError::UnsupportedArchPair { source, target } => {
write!(f, "unsupported architecture pair: {} → {}", source, target)
}
BridgeError::ISelFailed { opcode, reason } => {
write!(
f,
"instruction selection failed for {:?}: {}",
opcode, reason
)
}
BridgeError::RegAllocFailed { reason } => {
write!(f, "register allocation failed: {}", reason)
}
BridgeError::FrameLoweringFailed { reason } => {
write!(f, "frame lowering failed: {}", reason)
}
BridgeError::Unimplemented { feature } => {
write!(f, "unimplemented feature: {}", feature)
}
BridgeError::BackChainError { reason } => {
write!(f, "back chain error: {}", reason)
}
BridgeError::Internal(msg) => write!(f, "internal bridge error: {}", msg),
}
}
}
#[derive(Debug, Clone)]
pub struct BridgeOutput {
pub instructions_emitted: usize,
pub basic_blocks: usize,
pub target_arch: SystemZBridgeArch,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CostEstimate {
pub latency: u32,
pub throughput: u32,
pub code_size: u32,
pub vectorizable: bool,
pub profitable: bool,
}
impl CostEstimate {
pub fn zero() -> Self {
Self {
latency: 0,
throughput: 0,
code_size: 0,
vectorizable: false,
profitable: false,
}
}
pub fn new(latency: u32, throughput: u32, code_size: u32) -> Self {
Self {
latency,
throughput,
code_size,
vectorizable: false,
profitable: true,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MachineIRInst {
pub opcode: GenericMachineOpcode,
pub dst: Option<usize>,
pub srcs: Vec<MachineIROperand>,
pub flags: MachineIRFlags,
}
impl MachineIRInst {
pub fn new(opcode: GenericMachineOpcode) -> Self {
Self {
opcode,
dst: None,
srcs: Vec::new(),
flags: MachineIRFlags::default(),
}
}
pub fn with_dst(mut self, dst: usize) -> Self {
self.dst = Some(dst);
self
}
pub fn with_src(mut self, src: MachineIROperand) -> Self {
self.srcs.push(src);
self
}
pub fn with_flags(mut self, flags: MachineIRFlags) -> Self {
self.flags = flags;
self
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MachineIROperand {
VReg(usize),
Imm(i64),
FImm(f64),
Block(usize),
Global(String),
FrameIndex(i32),
External(String),
Cond(MachineIRCond),
Disp { base: usize, disp: i64 },
IndexDisp {
base: usize,
index: usize,
disp: i64,
},
CondMask(u8),
}
impl MachineIROperand {
pub fn is_reg(&self) -> bool {
matches!(self, MachineIROperand::VReg(_))
}
pub fn is_imm(&self) -> bool {
matches!(self, MachineIROperand::Imm(_) | MachineIROperand::FImm(_))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GenericMachineOpcode {
Copy,
Load,
Store,
Add,
Sub,
Mul,
SDiv,
UDiv,
And,
Or,
Xor,
Shl,
LShr,
AShr,
ICmp,
FCmp,
Br,
BrCond,
Call,
Ret,
FAdd,
FSub,
FMul,
FDiv,
SExt,
ZExt,
Trunc,
FpToSI,
FpToUI,
SIToFp,
UIToFp,
Select,
FrameSetup,
FrameDestroy,
StackSave,
StackRestore,
InsertElt,
ExtractElt,
Phi,
Debug,
Nop,
TargetSpecific(u32),
}
impl GenericMachineOpcode {
pub fn is_terminator(&self) -> bool {
matches!(
self,
GenericMachineOpcode::Br | GenericMachineOpcode::BrCond | GenericMachineOpcode::Ret
)
}
pub fn is_memory(&self) -> bool {
matches!(
self,
GenericMachineOpcode::Load
| GenericMachineOpcode::Store
| GenericMachineOpcode::FrameSetup
| GenericMachineOpcode::FrameDestroy
| GenericMachineOpcode::StackSave
| GenericMachineOpcode::StackRestore
)
}
pub fn is_commutative(&self) -> bool {
matches!(
self,
GenericMachineOpcode::Add
| GenericMachineOpcode::Mul
| GenericMachineOpcode::And
| GenericMachineOpcode::Or
| GenericMachineOpcode::Xor
| GenericMachineOpcode::FAdd
| GenericMachineOpcode::FMul
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MachineIRCond {
EQ,
NE,
LT,
LE,
GT,
GE,
LO, LS, HI, HS, MI, PL, VS, VC, }
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct MachineIRFlags {
pub may_trap: bool,
pub may_load: bool,
pub may_store: bool,
pub has_side_effects: bool,
pub is_terminator: bool,
pub is_branch: bool,
pub is_indirect_branch: bool,
pub is_call: bool,
pub is_return: bool,
pub is_barrier: bool,
pub is_convergent: bool,
}
#[derive(Debug, Clone)]
pub struct DAGPattern {
pub name: String,
pub pattern: PatternNode,
pub result: PatternResult,
pub cost: u32,
pub archs: Vec<SystemZBridgeArch>,
}
#[derive(Debug, Clone)]
pub enum PatternNode {
Any,
Opcode(GenericMachineOpcode),
IROpcode(Opcode),
Constant(i64),
Immediate { min: i64, max: i64 },
Sequence(Vec<PatternNode>),
Alternative(Vec<PatternNode>),
Predicate {
node: Box<PatternNode>,
pred: PatternPredicate,
},
Capture { slot: usize, node: Box<PatternNode> },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PatternPredicate {
IsPowerOfTwo,
FitsInBits(u32),
IsSz16BitImm,
IsSz32BitImm,
IsSzU16BitImm,
IsSz20BitDisp,
IsFloatingPoint,
IsInteger,
IsVector,
}
#[derive(Debug, Clone)]
pub struct PatternResult {
pub opcode: GenericMachineOpcode,
pub operand_mapping: Vec<usize>,
pub flags: Option<MachineIRFlags>,
}
#[derive(Debug, Clone)]
pub struct LegalizeRule {
pub trigger: LegalizeTrigger,
pub action: LegalizeAction,
}
#[derive(Debug, Clone)]
pub enum LegalizeTrigger {
TypeTooWide { bit_width: u32 },
UnsupportedOpcode(GenericMachineOpcode),
NoHardFloat,
NoVector,
}
#[derive(Debug, Clone)]
pub enum LegalizeAction {
Widen,
Narrow,
Scalarize,
Promote,
Libcall(String),
Expand(Vec<GenericMachineOpcode>),
TargetSpecific(u32),
Custom,
}
pub struct CrossTargetISel {
pub arch: SystemZBridgeArch,
pub vreg_map: HashMap<usize, usize>,
pub patterns: Vec<DAGPattern>,
pub legalize_rules: Vec<LegalizeRule>,
next_vreg: usize,
current_func: Option<String>,
}
impl CrossTargetISel {
pub fn new(arch: SystemZBridgeArch) -> Self {
let mut isel = Self {
arch,
vreg_map: HashMap::new(),
patterns: Vec::new(),
legalize_rules: Vec::new(),
next_vreg: 0,
current_func: None,
};
isel.init_default_patterns();
isel.init_legalize_rules();
isel
}
fn init_default_patterns(&mut self) {
self.add_pattern(DAGPattern {
name: "add_imm".into(),
pattern: PatternNode::Sequence(vec![
PatternNode::Opcode(GenericMachineOpcode::Add),
PatternNode::Capture {
slot: 0,
node: Box::new(PatternNode::Any),
},
PatternNode::Capture {
slot: 1,
node: Box::new(PatternNode::Predicate {
node: Box::new(PatternNode::Immediate {
min: SYSTEMZ_MIN_IMM_I16,
max: SYSTEMZ_MAX_IMM_I16,
}),
pred: PatternPredicate::IsSz16BitImm,
}),
},
]),
result: PatternResult {
opcode: GenericMachineOpcode::Add,
operand_mapping: vec![0, 1],
flags: None,
},
cost: 1,
archs: vec![],
});
self.add_pattern(DAGPattern {
name: "sub_imm".into(),
pattern: PatternNode::Sequence(vec![
PatternNode::Opcode(GenericMachineOpcode::Sub),
PatternNode::Capture {
slot: 0,
node: Box::new(PatternNode::Any),
},
PatternNode::Capture {
slot: 1,
node: Box::new(PatternNode::Predicate {
node: Box::new(PatternNode::Immediate {
min: SYSTEMZ_MIN_IMM_I16,
max: SYSTEMZ_MAX_IMM_I16,
}),
pred: PatternPredicate::IsSz16BitImm,
}),
},
]),
result: PatternResult {
opcode: GenericMachineOpcode::Sub,
operand_mapping: vec![0, 1],
flags: None,
},
cost: 1,
archs: vec![],
});
self.add_pattern(DAGPattern {
name: "load".into(),
pattern: PatternNode::Opcode(GenericMachineOpcode::Load),
result: PatternResult {
opcode: GenericMachineOpcode::Load,
operand_mapping: vec![0],
flags: Some(MachineIRFlags {
may_load: true,
..Default::default()
}),
},
cost: 4,
archs: vec![],
});
self.add_pattern(DAGPattern {
name: "store".into(),
pattern: PatternNode::Opcode(GenericMachineOpcode::Store),
result: PatternResult {
opcode: GenericMachineOpcode::Store,
operand_mapping: vec![0, 1],
flags: Some(MachineIRFlags {
may_store: true,
..Default::default()
}),
},
cost: 1,
archs: vec![],
});
self.add_pattern(DAGPattern {
name: "br".into(),
pattern: PatternNode::Opcode(GenericMachineOpcode::Br),
result: PatternResult {
opcode: GenericMachineOpcode::Br,
operand_mapping: vec![0],
flags: Some(MachineIRFlags {
is_terminator: true,
is_branch: true,
..Default::default()
}),
},
cost: 1,
archs: vec![],
});
self.add_pattern(DAGPattern {
name: "select".into(),
pattern: PatternNode::Sequence(vec![
PatternNode::Opcode(GenericMachineOpcode::Select),
PatternNode::Capture {
slot: 0,
node: Box::new(PatternNode::Any),
},
PatternNode::Capture {
slot: 1,
node: Box::new(PatternNode::Any),
},
PatternNode::Capture {
slot: 2,
node: Box::new(PatternNode::Any),
},
]),
result: PatternResult {
opcode: GenericMachineOpcode::Select,
operand_mapping: vec![0, 1, 2],
flags: None,
},
cost: 1,
archs: vec![],
});
self.add_pattern(DAGPattern {
name: "zext".into(),
pattern: PatternNode::Opcode(GenericMachineOpcode::ZExt),
result: PatternResult {
opcode: GenericMachineOpcode::ZExt,
operand_mapping: vec![0],
flags: None,
},
cost: 1,
archs: vec![],
});
self.add_pattern(DAGPattern {
name: "sext".into(),
pattern: PatternNode::Opcode(GenericMachineOpcode::SExt),
result: PatternResult {
opcode: GenericMachineOpcode::SExt,
operand_mapping: vec![0],
flags: None,
},
cost: 1,
archs: vec![],
});
self.add_pattern(DAGPattern {
name: "fadd".into(),
pattern: PatternNode::Opcode(GenericMachineOpcode::FAdd),
result: PatternResult {
opcode: GenericMachineOpcode::FAdd,
operand_mapping: vec![0, 1],
flags: None,
},
cost: 5, archs: vec![],
});
self.add_pattern(DAGPattern {
name: "fmul".into(),
pattern: PatternNode::Opcode(GenericMachineOpcode::FMul),
result: PatternResult {
opcode: GenericMachineOpcode::FMul,
operand_mapping: vec![0, 1],
flags: None,
},
cost: 5,
archs: vec![],
});
self.add_pattern(DAGPattern {
name: "fma".into(),
pattern: PatternNode::Sequence(vec![
PatternNode::Opcode(GenericMachineOpcode::FAdd),
PatternNode::Sequence(vec![
PatternNode::Opcode(GenericMachineOpcode::FMul),
PatternNode::Capture {
slot: 0,
node: Box::new(PatternNode::Any),
},
PatternNode::Capture {
slot: 1,
node: Box::new(PatternNode::Any),
},
]),
PatternNode::Capture {
slot: 2,
node: Box::new(PatternNode::Any),
},
]),
result: PatternResult {
opcode: GenericMachineOpcode::TargetSpecific(1), operand_mapping: vec![0, 1, 2],
flags: None,
},
cost: 5,
archs: vec![SystemZBridgeArch::SystemZ],
});
self.add_pattern(DAGPattern {
name: "shl_imm_sz".into(),
pattern: PatternNode::Sequence(vec![
PatternNode::Opcode(GenericMachineOpcode::Shl),
PatternNode::Capture {
slot: 0,
node: Box::new(PatternNode::Any),
},
PatternNode::Capture {
slot: 1,
node: Box::new(PatternNode::Immediate { min: 0, max: 63 }),
},
]),
result: PatternResult {
opcode: GenericMachineOpcode::Shl,
operand_mapping: vec![0, 1],
flags: None,
},
cost: 1,
archs: vec![SystemZBridgeArch::SystemZ],
});
}
fn init_legalize_rules(&mut self) {
self.legalize_rules.push(LegalizeRule {
trigger: LegalizeTrigger::TypeTooWide { bit_width: 128 },
action: LegalizeAction::Narrow,
});
self.legalize_rules.push(LegalizeRule {
trigger: LegalizeTrigger::NoHardFloat,
action: LegalizeAction::Libcall("__addsf3".into()),
});
self.legalize_rules.push(LegalizeRule {
trigger: LegalizeTrigger::NoVector,
action: LegalizeAction::Scalarize,
});
}
pub fn add_pattern(&mut self, pattern: DAGPattern) {
self.patterns.push(pattern);
}
pub fn next_vreg(&mut self) -> usize {
let vreg = self.next_vreg;
self.next_vreg += 1;
vreg
}
pub fn map_vreg(&mut self, ir_val: usize) -> usize {
*self
.vreg_map
.entry(ir_val)
.or_insert_with(|| self.next_vreg())
}
pub fn set_current_function(&mut self, name: String) {
self.current_func = Some(name);
}
pub fn needs_legalization(&self, bit_width: u32, opcode: GenericMachineOpcode) -> bool {
self.legalize_rules.iter().any(|rule| match &rule.trigger {
LegalizeTrigger::TypeTooWide { bit_width: max } => bit_width > *max,
LegalizeTrigger::UnsupportedOpcode(oc) => *oc == opcode,
_ => false,
})
}
pub fn get_legalization(&self, trigger: &LegalizeTrigger) -> Vec<&LegalizeAction> {
self.legalize_rules
.iter()
.filter(|r| std::mem::discriminant(&r.trigger) == std::mem::discriminant(trigger))
.map(|r| &r.action)
.collect()
}
}
pub struct CrossTargetRegAlloc {
pub arch: SystemZBridgeArch,
pub gprs: Vec<SystemZRegister>,
pub fprs: Vec<SystemZRegister>,
pub callee_saved: Vec<SystemZRegister>,
pub caller_saved: Vec<SystemZRegister>,
spill_slots: usize,
pub frame_info: SystemZFrameInfo,
pub use_fp: bool,
}
impl CrossTargetRegAlloc {
pub fn new(arch: SystemZBridgeArch) -> Self {
let cc = SystemZCallingConvention::new();
let alloc_gprs = SystemZRegisterInfo::get_allocatable_gprs();
let alloc_fprs = SystemZRegisterInfo::get_allocatable_fprs();
CrossTargetRegAlloc {
arch,
gprs: alloc_gprs,
fprs: alloc_fprs,
callee_saved: cc.callee_saved.clone(),
caller_saved: cc.caller_saved.clone(),
spill_slots: 0,
frame_info: SystemZFrameInfo::default(),
use_fp: false,
}
}
pub fn allocate_spill_slot(&mut self, size: u32) -> i32 {
let slot = self.spill_slots as i32;
self.spill_slots += 1;
slot
}
pub fn num_spill_slots(&self) -> usize {
self.spill_slots
}
pub fn num_callee_saved_gprs(&self) -> usize {
self.callee_saved
.iter()
.filter(|r| SystemZRegisterInfo::is_gpr(**r))
.count()
}
pub fn num_callee_saved_fprs(&self) -> usize {
self.callee_saved
.iter()
.filter(|r| SystemZRegisterInfo::is_fpr(**r))
.count()
}
pub fn sp(&self) -> SystemZRegister {
SystemZRegister::R15
}
pub fn fp(&self) -> SystemZRegister {
SystemZRegister::R11
}
pub fn ra(&self) -> SystemZRegister {
SystemZRegister::R14
}
pub fn is_sp(&self, reg: SystemZRegister) -> bool {
reg == SystemZRegister::R15
}
pub fn can_be_base_reg(&self, reg: SystemZRegister) -> bool {
SystemZRegisterInfo::can_be_base_reg(reg)
}
pub fn get_preferred_class(&self, type_bit_width: u32, is_fp: bool) -> SystemZRegClass {
if is_fp {
match type_bit_width {
32 => SystemZRegClass::FPR32,
64 => SystemZRegClass::FPR64,
128 => SystemZRegClass::FPR128,
_ => SystemZRegClass::FPR64,
}
} else {
match type_bit_width {
32 => SystemZRegClass::GPR32,
64 => SystemZRegClass::GPR64,
_ => SystemZRegClass::GPR64,
}
}
}
}
pub struct CrossTargetABI {
pub arch: SystemZBridgeArch,
pub sz_cc: SystemZCallingConvention,
pub gpr_args_used: usize,
pub fpr_args_used: usize,
pub stack_arg_bytes: usize,
}
impl CrossTargetABI {
pub fn new(arch: SystemZBridgeArch) -> Self {
CrossTargetABI {
arch,
sz_cc: SystemZCallingConvention::new(),
gpr_args_used: 0,
fpr_args_used: 0,
stack_arg_bytes: 0,
}
}
pub fn assign_gpr_arg(&mut self) -> Option<SystemZRegister> {
if self.gpr_args_used < self.sz_cc.arg_regs.len() {
let reg = self.sz_cc.arg_regs[self.gpr_args_used];
self.gpr_args_used += 1;
Some(reg)
} else {
None }
}
pub fn assign_fpr_arg(&mut self) -> Option<SystemZRegister> {
if self.fpr_args_used < self.sz_cc.fp_arg_regs.len() {
let reg = self.sz_cc.fp_arg_regs[self.fpr_args_used];
self.fpr_args_used += 1;
Some(reg)
} else {
None }
}
pub fn get_return_reg(&self, is_fp: bool) -> SystemZRegister {
if is_fp {
self.sz_cc.fp_ret_regs[0]
} else {
self.sz_cc.ret_regs[0]
}
}
pub fn get_return_reg_pair(&self) -> (SystemZRegister, SystemZRegister) {
(self.sz_cc.ret_regs[0], self.sz_cc.ret_regs[1])
}
pub fn reset_args(&mut self) {
self.gpr_args_used = 0;
self.fpr_args_used = 0;
self.stack_arg_bytes = 0;
}
pub fn add_stack_arg(&mut self, size_bytes: usize) {
let aligned = (size_bytes + 7) & !7; self.stack_arg_bytes += aligned;
}
pub fn total_stack_args(&self) -> usize {
self.stack_arg_bytes
}
pub fn supports_varargs(&self) -> bool {
true
}
pub fn static_chain_reg(&self) -> SystemZRegister {
self.sz_cc.static_chain
}
pub fn convert_cond(&self, cond: MachineIRCond) -> SystemZCondCode {
match cond {
MachineIRCond::EQ => SystemZCondCode::EQ,
MachineIRCond::NE => SystemZCondCode::NE,
MachineIRCond::LT => SystemZCondCode::LT,
MachineIRCond::LE => SystemZCondCode::LE,
MachineIRCond::GT => SystemZCondCode::GT,
MachineIRCond::GE => SystemZCondCode::HE,
MachineIRCond::LO => SystemZCondCode::LO,
MachineIRCond::LS => SystemZCondCode::LS,
MachineIRCond::HI => SystemZCondCode::HI,
MachineIRCond::HS => SystemZCondCode::HS,
MachineIRCond::MI => SystemZCondCode::MI,
MachineIRCond::PL => SystemZCondCode::PL,
MachineIRCond::VS => SystemZCondCode::O,
MachineIRCond::VC => SystemZCondCode::NO,
}
}
}
pub struct CrossTargetFrameLowering {
pub arch: SystemZBridgeArch,
pub sz_frame_lowering: SystemZFrameLowering,
pub local_area_size: i64,
pub outgoing_arg_size: i64,
pub setup_back_chain: bool,
}
impl CrossTargetFrameLowering {
pub fn new(arch: SystemZBridgeArch) -> Self {
let cc = SystemZCallingConvention::new();
CrossTargetFrameLowering {
arch,
sz_frame_lowering: SystemZFrameLowering::new(cc),
local_area_size: 0,
outgoing_arg_size: 0,
setup_back_chain: true,
}
}
pub fn compute_frame(
&mut self,
local_size: i64,
outgoing_args: i64,
callee_gprs: usize,
callee_fprs: usize,
) -> i64 {
self.local_area_size = local_size;
self.outgoing_arg_size = outgoing_args;
self.sz_frame_lowering.compute_frame_size(
callee_gprs,
callee_fprs,
local_size,
outgoing_args,
)
}
pub fn emit_prologue(&self) -> Vec<String> {
if self.arch.is_systemz_family() {
let mut insns = Vec::new();
if self.setup_back_chain {
insns.push("stg %r15, 0(%r15)".to_string());
}
insns.push("stg %r14, 112(%r15)".to_string());
let prologue = self.sz_frame_lowering.gen_prologue();
insns.extend(prologue);
insns
} else {
vec!["pushq %rbp".to_string(), "movq %rsp, %rbp".to_string()]
}
}
pub fn emit_epilogue(&self) -> Vec<String> {
if self.arch.is_systemz_family() {
self.sz_frame_lowering.gen_epilogue()
} else {
vec!["popq %rbp".to_string(), "retq".to_string()]
}
}
pub fn get_callee_save_offset(&self, reg_idx: usize) -> i64 {
self.sz_frame_lowering.get_gpr_save_offset(reg_idx)
}
pub fn get_frame_size(&self) -> i64 {
self.sz_frame_lowering.get_total_frame_size()
}
}
#[derive(Debug, Clone)]
pub struct SystemZX86CostModel {
pub source: SystemZBridgeArch,
pub target: SystemZBridgeArch,
pub gpr_size: u32,
pub alu_latency: u32,
pub load_latency: u32,
pub store_latency: u32,
pub branch_latency: u32,
pub fp_add_latency: u32,
pub fp_mul_latency: u32,
pub fp_div_latency: u32,
}
impl SystemZX86CostModel {
pub fn new(source: SystemZBridgeArch, target: SystemZBridgeArch) -> Self {
let (alu, load, store, branch, fp_add, fp_mul, fp_div) = match target {
SystemZBridgeArch::SystemZ => {
(1, 4, 1, 18, 5, 5, 30)
}
SystemZBridgeArch::X86_64 => {
(1, 5, 1, 16, 3, 4, 14)
}
SystemZBridgeArch::X86_32 => (1, 5, 1, 16, 3, 4, 14),
};
SystemZX86CostModel {
source,
target,
gpr_size: 8,
alu_latency: alu,
load_latency: load,
store_latency: store,
branch_latency: branch,
fp_add_latency: fp_add,
fp_mul_latency: fp_mul,
fp_div_latency: fp_div,
}
}
pub fn estimate(&self, opcode: Opcode, _operand_count: usize) -> CostEstimate {
match opcode {
Opcode::Add | Opcode::Sub | Opcode::And | Opcode::Or | Opcode::Xor => {
CostEstimate::new(self.alu_latency, 3, 4)
}
Opcode::Mul => CostEstimate::new(3, 1, 4),
Opcode::SDiv | Opcode::UDiv => CostEstimate::new(20, 0, 4),
Opcode::Load => CostEstimate::new(self.load_latency, 2, 4),
Opcode::Store => CostEstimate::new(self.store_latency, 1, 4),
Opcode::Br => CostEstimate::new(self.branch_latency, 1, 4),
Opcode::FAdd => CostEstimate::new(self.fp_add_latency, 2, 4),
Opcode::FSub => CostEstimate::new(self.fp_add_latency, 2, 4),
Opcode::FMul => CostEstimate::new(self.fp_mul_latency, 1, 4),
Opcode::FDiv => CostEstimate::new(self.fp_div_latency, 0, 4),
_ => CostEstimate::new(1, 1, 4),
}
}
pub fn estimate_systemz(&self, opcode: SystemZBridgeOpcode) -> CostEstimate {
use SystemZBridgeOpcode::*;
match opcode {
A | AG | AGR | AY | AGH => CostEstimate::new(self.alu_latency, 3, 4),
S | SG | SGR | SY => CostEstimate::new(self.alu_latency, 3, 4),
M | MSG | MSGR => CostEstimate::new(5, 1, 4),
D | DSG | DSGR => CostEstimate::new(20, 0, 4),
N | O | X | NR | OR | XR | NGR | OGR | XGR | NG | OG | XG | NY | OY | XY => {
CostEstimate::new(self.alu_latency, 3, 4)
}
L | LG | LY | LGF | LGH | LGB | LHY | LAY | LR | LGR | LTR | LTGR | LT | LTG => {
CostEstimate::new(self.load_latency, 2, 4)
}
ST | STG | STY | STHY | STCY => CostEstimate::new(self.store_latency, 1, 4),
BRC | BRCL | BCR | BR | J | JG | BASR | BRAS | BRASL => {
CostEstimate::new(self.branch_latency, 1, 4)
}
LE | LD | LER | LDR => CostEstimate::new(self.load_latency, 2, 4),
STE | STD => CostEstimate::new(self.store_latency, 1, 4),
AE | AD | AEBR | ADBR => CostEstimate::new(self.fp_add_latency, 2, 4),
SE | SD | SEBR | SDBR => CostEstimate::new(self.fp_add_latency, 2, 4),
ME | MD | MEBR | MDBR => CostEstimate::new(self.fp_mul_latency, 1, 4),
MADBR | MAEBR => CostEstimate::new(self.fp_add_latency, 1, 4), DE | DD | DEBR | DDBR => CostEstimate::new(self.fp_div_latency, 0, 4),
SQDBR | SQEBR => CostEstimate::new(self.fp_div_latency, 0, 4),
SLL | SRL | SLA | SRA | SLLG | SRLG | SLAG | SRAG | RLL | RLLG => {
CostEstimate::new(self.alu_latency, 3, 4)
}
MVC | MVN | MVZ => CostEstimate::new(self.store_latency + 20, 1, 6),
MVCL | MVCLU => CostEstimate::new(self.store_latency + 100, 1, 4),
NOP | NOPR => CostEstimate::new(0, 0, 2),
BRK => CostEstimate::new(100, 0, 2),
VA | VS | VM => CostEstimate::new(4, 2, 4),
VL | VST => CostEstimate::new(self.load_latency + 2, 1, 4),
_ => CostEstimate::new(1, 1, 4),
}
}
pub fn compare_costs(
&self,
opcode: Opcode,
operand_count: usize,
) -> (CostEstimate, CostEstimate) {
let sz_model = SystemZX86CostModel::new(self.source, SystemZBridgeArch::SystemZ);
let x86_model = SystemZX86CostModel::new(self.source, SystemZBridgeArch::X86_64);
(
sz_model.estimate(opcode, operand_count),
x86_model.estimate(opcode, operand_count),
)
}
pub fn should_vectorize(&self) -> bool {
self.target.is_systemz_family()
}
pub fn is_sz_more_profitable(&self, opcode: Opcode) -> bool {
match opcode {
Opcode::FAdd | Opcode::FMul => true,
Opcode::Load | Opcode::Store => true,
_ => false,
}
}
}
pub struct CrossTargetOptimization {
pub arch: SystemZBridgeArch,
pub aggressive: bool,
pub sz_specific: bool,
pub peephole_patterns: Vec<PeepholePattern>,
}
#[derive(Debug, Clone)]
pub struct PeepholePattern {
pub name: String,
pub match_sequence: Vec<GenericMachineOpcode>,
pub replacement: Vec<GenericMachineOpcode>,
pub cross_target: bool,
}
impl CrossTargetOptimization {
pub fn new(arch: SystemZBridgeArch) -> Self {
let mut opt = CrossTargetOptimization {
arch,
aggressive: false,
sz_specific: arch.is_systemz_family(),
peephole_patterns: Vec::new(),
};
opt.init_patterns();
opt
}
fn init_patterns(&mut self) {
self.peephole_patterns.push(PeepholePattern {
name: "elim_redundant_copy".into(),
match_sequence: vec![GenericMachineOpcode::Copy, GenericMachineOpcode::Copy],
replacement: vec![GenericMachineOpcode::Copy],
cross_target: true,
});
self.peephole_patterns.push(PeepholePattern {
name: "store_load_forward".into(),
match_sequence: vec![GenericMachineOpcode::Store, GenericMachineOpcode::Load],
replacement: vec![GenericMachineOpcode::Copy],
cross_target: true,
});
self.peephole_patterns.push(PeepholePattern {
name: "redundant_frame_setup".into(),
match_sequence: vec![
GenericMachineOpcode::FrameSetup,
GenericMachineOpcode::FrameDestroy,
],
replacement: vec![],
cross_target: true,
});
self.peephole_patterns.push(PeepholePattern {
name: "sz_elim_nop".into(),
match_sequence: vec![GenericMachineOpcode::Nop],
replacement: vec![],
cross_target: false,
});
self.peephole_patterns.push(PeepholePattern {
name: "fallthrough_branch".into(),
match_sequence: vec![GenericMachineOpcode::Br],
replacement: vec![],
cross_target: true,
});
}
pub fn apply_peephole(&self, seq: &[GenericMachineOpcode]) -> Vec<GenericMachineOpcode> {
let mut result = seq.to_vec();
let mut changed = true;
while changed {
changed = false;
for pattern in &self.peephole_patterns {
if !pattern.cross_target && !self.sz_specific {
continue;
}
if let Some(pos) = find_subsequence(&result, &pattern.match_sequence) {
let end = pos + pattern.match_sequence.len();
let mut new_seq = result[..pos].to_vec();
new_seq.extend(pattern.replacement.iter().copied());
new_seq.extend(result[end..].iter().copied());
result = new_seq;
changed = true;
break;
}
}
}
result
}
pub fn optimize_back_chain(&self, has_back_chain: bool, is_leaf: bool) -> bool {
if !self.sz_specific {
return has_back_chain;
}
if is_leaf && !self.aggressive {
return false;
}
has_back_chain
}
pub fn optimize_cc_usage(&self, ops: &[GenericMachineOpcode]) -> Vec<GenericMachineOpcode> {
if !self.sz_specific {
return ops.to_vec();
}
ops.to_vec() }
pub fn set_aggressive(&mut self, aggressive: bool) {
self.aggressive = aggressive;
}
pub fn set_sz_specific(&mut self, sz_specific: bool) {
self.sz_specific = sz_specific;
}
}
fn find_subsequence<T: PartialEq>(haystack: &[T], needle: &[T]) -> Option<usize> {
if needle.is_empty() {
return Some(0);
}
haystack
.windows(needle.len())
.position(|window| window == needle)
}
pub struct CrossTargetSystemZ {
pub bridge: SystemZX86Bridge,
pub validate: bool,
pub dump_ir: bool,
pub profile: bool,
pub output_format: SystemZOutputFormat,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SystemZOutputFormat {
Assembly,
Object,
SharedLibrary,
Executable,
Bitcode,
LLVMIR,
}
impl fmt::Display for SystemZOutputFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SystemZOutputFormat::Assembly => write!(f, "asm"),
SystemZOutputFormat::Object => write!(f, "obj"),
SystemZOutputFormat::SharedLibrary => write!(f, "so"),
SystemZOutputFormat::Executable => write!(f, "exe"),
SystemZOutputFormat::Bitcode => write!(f, "bc"),
SystemZOutputFormat::LLVMIR => write!(f, "ll"),
}
}
}
impl CrossTargetSystemZ {
pub fn new(source_arch: SystemZBridgeArch, target_arch: SystemZBridgeArch) -> Self {
CrossTargetSystemZ {
bridge: SystemZX86Bridge::new(source_arch, target_arch),
validate: true,
dump_ir: false,
profile: false,
output_format: SystemZOutputFormat::Assembly,
}
}
pub fn default_cross() -> Self {
Self::new(SystemZBridgeArch::X86_64, SystemZBridgeArch::SystemZ)
}
pub fn native_systemz() -> Self {
Self::new(SystemZBridgeArch::SystemZ, SystemZBridgeArch::SystemZ)
}
pub fn set_output_format(&mut self, fmt: SystemZOutputFormat) {
self.output_format = fmt;
}
pub fn enable_ir_dump(&mut self) {
self.dump_ir = true;
}
pub fn enable_profile(&mut self) {
self.profile = true;
}
pub fn disable_validation(&mut self) {
self.validate = false;
}
pub fn compile(&mut self) -> Result<BridgeOutput, BridgeError> {
if self.dump_ir {
eprintln!(
"[CrossTargetSystemZ] Starting compilation: {}",
self.describe()
);
}
let result = self.bridge.run_pipeline();
if self.validate {
}
result
}
pub fn describe(&self) -> String {
format!(
"CrossTargetSystemZ: {} → {} [format={}]",
self.bridge.source_arch, self.bridge.target_arch, self.output_format
)
}
pub fn get_stats_summary(&self) -> String {
format!(
"Functions: {}, ISel: {}, RA: {}, Frame: {}, SpillSlots: {}, VecLoops: {}",
self.bridge.stats.functions_processed,
self.bridge.stats.isel_cycles,
self.bridge.stats.ra_cycles,
self.bridge.stats.frame_cycles,
self.bridge.stats.spill_slots,
self.bridge.stats.vectorized_loops,
)
}
pub fn target_machine(&self) -> &SystemZTargetMachine {
&self.bridge.target_machine
}
pub fn target_machine_mut(&mut self) -> &mut SystemZTargetMachine {
&mut self.bridge.target_machine
}
pub fn has_opcode(&self, opcode: SystemZBridgeOpcode) -> bool {
ALL_SZB_OPCODES.contains(&opcode)
}
pub fn llvm_to_sz_mnemonic(&self, opcode: Opcode) -> Option<&'static str> {
map_llvm_to_sz_bridge_opcode(opcode).map(|o| o.mnemonic())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bridge_arch_display() {
assert_eq!(format!("{}", SystemZBridgeArch::X86_64), "x86_64");
assert_eq!(format!("{}", SystemZBridgeArch::X86_32), "i386");
assert_eq!(format!("{}", SystemZBridgeArch::SystemZ), "s390x");
}
#[test]
fn test_bridge_arch_is_64bit() {
assert!(SystemZBridgeArch::X86_64.is_64bit());
assert!(!SystemZBridgeArch::X86_32.is_64bit());
assert!(SystemZBridgeArch::SystemZ.is_64bit());
}
#[test]
fn test_bridge_arch_families() {
let sz = SystemZBridgeArch::SystemZ;
assert!(sz.is_systemz_family());
assert!(!sz.is_x86_family());
let x86 = SystemZBridgeArch::X86_64;
assert!(!x86.is_systemz_family());
assert!(x86.is_x86_family());
}
#[test]
fn test_bridge_arch_pointer_width() {
assert_eq!(SystemZBridgeArch::X86_64.pointer_width(), 64);
assert_eq!(SystemZBridgeArch::X86_32.pointer_width(), 32);
assert_eq!(SystemZBridgeArch::SystemZ.pointer_width(), 64);
}
#[test]
fn test_bridge_arch_data_layout() {
assert!(SystemZBridgeArch::SystemZ.data_layout().starts_with("E"));
assert!(SystemZBridgeArch::X86_64.data_layout().starts_with("e"));
}
#[test]
fn test_bridge_arch_endianness() {
assert!(SystemZBridgeArch::SystemZ.is_big_endian());
assert!(!SystemZBridgeArch::SystemZ.is_little_endian());
assert!(!SystemZBridgeArch::X86_64.is_big_endian());
assert!(SystemZBridgeArch::X86_64.is_little_endian());
}
#[test]
fn test_bridge_default() {
let bridge = SystemZX86Bridge::default();
assert_eq!(bridge.source_arch, SystemZBridgeArch::X86_64);
assert_eq!(bridge.target_arch, SystemZBridgeArch::SystemZ);
}
#[test]
fn test_bridge_native_to_systemz() {
let bridge = SystemZX86Bridge::native_to_systemz();
assert_eq!(bridge.source_arch, SystemZBridgeArch::X86_64);
assert_eq!(bridge.target_arch, SystemZBridgeArch::SystemZ);
}
#[test]
fn test_bridge_systemz_to_native() {
let bridge = SystemZX86Bridge::systemz_to_native();
assert_eq!(bridge.source_arch, SystemZBridgeArch::SystemZ);
assert_eq!(bridge.target_arch, SystemZBridgeArch::X86_64);
}
#[test]
fn test_bridge_from_triples() {
let bridge =
SystemZX86Bridge::from_triples("x86_64-unknown-linux-gnu", "s390x-ibm-linux-gnu");
assert_eq!(bridge.source_arch, SystemZBridgeArch::X86_64);
assert_eq!(bridge.target_arch, SystemZBridgeArch::SystemZ);
}
#[test]
fn test_bridge_target_triple() {
let bridge = SystemZX86Bridge::default();
assert_eq!(bridge.get_target_triple(), "s390x-ibm-linux-gnu");
}
#[test]
fn test_bridge_data_layout() {
let bridge = SystemZX86Bridge::default();
let layout = bridge.get_data_layout();
assert!(layout.starts_with("E"));
}
#[test]
fn test_bridge_supports_unaligned() {
let bridge = SystemZX86Bridge::default();
assert!(bridge.supports_unaligned_access());
}
#[test]
fn test_bridge_stack_alignment() {
let bridge = SystemZX86Bridge::default();
assert_eq!(bridge.get_stack_alignment(), SYSTEMZ_STACK_ALIGNMENT);
}
#[test]
fn test_bridge_features() {
let mut bridge = SystemZX86Bridge::default();
assert!(!bridge.has_feature(BridgeFeature::Vector));
bridge.enable_feature(BridgeFeature::Vector);
assert!(bridge.has_feature(BridgeFeature::Vector));
}
#[test]
fn test_bridge_describe() {
let bridge = SystemZX86Bridge::default();
let desc = bridge.describe();
assert!(desc.contains("SystemZX86Bridge"));
assert!(desc.contains("s390x"));
}
#[test]
fn test_bridge_count_by_format() {
let bridge = SystemZX86Bridge::default();
let rr_count = bridge.count_by_format(SystemZFormat::RR);
let rx_count = bridge.count_by_format(SystemZFormat::RX);
assert!(rr_count > 0 || rx_count > 0);
}
#[test]
fn test_bridge_list_instructions() {
let bridge = SystemZX86Bridge::default();
let instrs = bridge.list_systemz_instructions();
assert!(instrs.len() >= 100);
}
#[test]
fn test_arch_level_display() {
assert_eq!(format!("{}", SystemZArchLevel::Z10), "z10");
assert_eq!(format!("{}", SystemZArchLevel::Z16), "z16");
}
#[test]
fn test_arch_level_has_vector() {
assert!(!SystemZArchLevel::Z10.has_vector());
assert!(SystemZArchLevel::Z13.has_vector());
assert!(SystemZArchLevel::Z16.has_vector());
}
#[test]
fn test_arch_level_has_dfp() {
assert!(!SystemZArchLevel::Z10.has_dfp());
assert!(SystemZArchLevel::Z196.has_dfp());
}
#[test]
fn test_arch_level_has_tx() {
assert!(!SystemZArchLevel::Z10.has_tx());
assert!(SystemZArchLevel::ZEC12.has_tx());
}
#[test]
fn test_arch_level_has_gs() {
assert!(!SystemZArchLevel::Z10.has_gs());
assert!(SystemZArchLevel::Z14.has_gs());
assert!(SystemZArchLevel::Z16.has_gs());
}
#[test]
fn test_arch_level_cpu_name() {
assert_eq!(SystemZArchLevel::Z14.cpu_name(), "z14");
assert_eq!(SystemZArchLevel::Z16.cpu_name(), "z16");
}
#[test]
fn test_tm_z14_config() {
let tm = SystemZTargetMachine::new(SystemZArchLevel::Z14);
assert!(tm.has_vector);
assert!(tm.has_dfp);
assert!(tm.has_tx);
assert!(tm.has_gs);
assert_eq!(tm.cpu, "z14");
}
#[test]
fn test_tm_z10_config() {
let tm = SystemZTargetMachine::new(SystemZArchLevel::Z10);
assert!(!tm.has_vector);
assert!(!tm.has_tx);
assert!(!tm.has_gs);
}
#[test]
fn test_tm_z16_config() {
let tm = SystemZTargetMachine::z16_target();
assert!(tm.has_vector);
assert!(tm.has_execution_hint);
assert_eq!(tm.cpu, "z16");
}
#[test]
fn test_tm_target_triple() {
let tm = SystemZTargetMachine::default_target();
assert_eq!(tm.get_target_triple(), "s390x-ibm-linux-gnu");
}
#[test]
fn test_tm_data_layout() {
let tm = SystemZTargetMachine::default_target();
let layout = tm.get_data_layout();
assert!(layout.starts_with("E"));
assert!(layout.contains("i64:64"));
}
#[test]
fn test_tm_pointer_size() {
let tm = SystemZTargetMachine::default_target();
assert_eq!(tm.get_pointer_size(), 64);
}
#[test]
fn test_tm_register_width() {
let tm = SystemZTargetMachine::default_target();
assert_eq!(tm.get_register_width(), 64);
}
#[test]
fn test_tm_page_size() {
let tm = SystemZTargetMachine::default_target();
assert_eq!(tm.get_page_size(), 4096);
}
#[test]
fn test_tm_min_stack_size() {
let tm = SystemZTargetMachine::default_target();
assert_eq!(tm.get_min_stack_size(), 8192);
}
#[test]
fn test_tm_red_zone_size() {
let tm = SystemZTargetMachine::default_target();
assert_eq!(tm.get_red_zone_size(), 160);
}
#[test]
fn test_tm_code_model() {
let mut tm = SystemZTargetMachine::default_target();
assert_eq!(tm.get_code_model(), CodeModel::Small);
tm.set_code_model(CodeModel::Large);
assert_eq!(tm.get_code_model(), CodeModel::Large);
}
#[test]
fn test_tm_reloc_model() {
let mut tm = SystemZTargetMachine::default_target();
assert_eq!(tm.get_reloc_model(), RelocModel::Static);
tm.set_reloc_model(RelocModel::Pic);
assert_eq!(tm.get_reloc_model(), RelocModel::Pic);
}
#[test]
fn test_tm_callee_saved() {
let tm = SystemZTargetMachine::default_target();
assert!(tm.is_callee_saved(SystemZRegister::R15));
assert!(tm.is_callee_saved(SystemZRegister::F15));
assert!(!tm.is_callee_saved(SystemZRegister::R0));
}
#[test]
fn test_tm_caller_saved() {
let tm = SystemZTargetMachine::default_target();
assert!(tm.is_caller_saved(SystemZRegister::R0));
assert!(tm.is_caller_saved(SystemZRegister::F0));
assert!(!tm.is_caller_saved(SystemZRegister::R15));
}
#[test]
fn test_tm_feature_summary() {
let tm = SystemZTargetMachine::z16_target();
let summary = tm.get_feature_summary();
assert!(summary
.iter()
.any(|(name, enabled)| *name == "vector" && *enabled));
}
#[test]
fn test_tm_feature_string() {
let tm = SystemZTargetMachine::z16_target();
let fs = tm.get_feature_string();
assert!(fs.contains("+vector"));
assert!(fs.contains("+dfp"));
}
#[test]
fn test_tm_emit_attributes() {
let tm = SystemZTargetMachine::z16_target();
let attrs = tm.emit_target_attributes();
assert!(attrs.iter().any(|a| a.contains("z16")));
}
#[test]
fn test_cc_arg_regs() {
let cc = SystemZCallingConvention::new();
assert_eq!(cc.arg_regs.len(), 5);
assert_eq!(cc.arg_regs[0], SystemZRegister::R2);
assert_eq!(cc.arg_regs[4], SystemZRegister::R6);
}
#[test]
fn test_cc_ret_regs() {
let cc = SystemZCallingConvention::new();
assert_eq!(cc.ret_regs, vec![SystemZRegister::R2, SystemZRegister::R3]);
}
#[test]
fn test_cc_fp_arg_regs() {
let cc = SystemZCallingConvention::new();
assert_eq!(cc.fp_arg_regs.len(), 4);
assert_eq!(cc.fp_arg_regs[0], SystemZRegister::F0);
assert_eq!(cc.fp_arg_regs[2], SystemZRegister::F4);
}
#[test]
fn test_cc_fp_ret_regs() {
let cc = SystemZCallingConvention::new();
assert_eq!(cc.fp_ret_regs, vec![SystemZRegister::F0]);
}
#[test]
fn test_cc_callee_saved() {
let cc = SystemZCallingConvention::new();
assert!(cc.callee_saved.contains(&SystemZRegister::R15));
assert!(cc.callee_saved.contains(&SystemZRegister::F15));
assert!(!cc.callee_saved.contains(&SystemZRegister::R0));
assert!(!cc.callee_saved.contains(&SystemZRegister::F0));
}
#[test]
fn test_cc_sp_fp_ra() {
let cc = SystemZCallingConvention::new();
assert_eq!(cc.sp, SystemZRegister::R15);
assert_eq!(cc.fp, SystemZRegister::R11);
assert_eq!(cc.ra, SystemZRegister::R14);
}
#[test]
fn test_cc_num_arg_gprs() {
let cc = SystemZCallingConvention::new();
assert_eq!(cc.num_arg_gprs(), 5);
}
#[test]
fn test_cc_num_arg_fprs() {
let cc = SystemZCallingConvention::new();
assert_eq!(cc.num_arg_fprs(), 4);
}
#[test]
fn test_cc_is_arg_reg() {
let cc = SystemZCallingConvention::new();
assert!(cc.is_arg_reg(SystemZRegister::R2));
assert!(cc.is_arg_reg(SystemZRegister::F0));
assert!(!cc.is_arg_reg(SystemZRegister::R7));
}
#[test]
fn test_cc_ra_dwarf_num() {
let cc = SystemZCallingConvention::new();
assert_eq!(cc.get_ra_dwarf_num(), 14);
}
#[test]
fn test_cc_fp_dwarf_num() {
let cc = SystemZCallingConvention::new();
assert_eq!(cc.get_fp_dwarf_num(), 11);
}
#[test]
fn test_cc_has_red_zone() {
let cc = SystemZCallingConvention::new();
assert!(cc.has_red_zone());
}
#[test]
fn test_cc_max_reg_args() {
let cc = SystemZCallingConvention::new();
assert_eq!(cc.max_reg_args(), 5);
}
#[test]
fn test_cc_classify_reg() {
let cc = SystemZCallingConvention::new();
assert_eq!(cc.classify_reg(SystemZRegister::R0), SystemZRegClass::GPR);
assert_eq!(cc.classify_reg(SystemZRegister::F0), SystemZRegClass::FPR);
assert_eq!(cc.classify_reg(SystemZRegister::A0), SystemZRegClass::AR);
assert_eq!(
cc.classify_reg(SystemZRegister::PSW),
SystemZRegClass::Special
);
}
#[test]
fn test_cc_get_arg_reg_for_index() {
let cc = SystemZCallingConvention::new();
assert_eq!(cc.get_arg_reg_for_index(0), Some(SystemZRegister::R2));
assert_eq!(cc.get_arg_reg_for_index(4), Some(SystemZRegister::R6));
assert_eq!(cc.get_arg_reg_for_index(5), None);
}
#[test]
fn test_cc_get_fp_arg_reg_for_index() {
let cc = SystemZCallingConvention::new();
assert_eq!(cc.get_fp_arg_reg_for_index(0), Some(SystemZRegister::F0));
assert_eq!(cc.get_fp_arg_reg_for_index(3), Some(SystemZRegister::F6));
assert_eq!(cc.get_fp_arg_reg_for_index(4), None);
}
#[test]
fn test_cc_callee_saved_gprs() {
let cc = SystemZCallingConvention::new();
let gprs = cc.get_callee_saved_gprs();
assert!(gprs.contains(&SystemZRegister::R15));
assert!(!gprs.contains(&SystemZRegister::F8));
}
#[test]
fn test_cc_callee_saved_fprs() {
let cc = SystemZCallingConvention::new();
let fprs = cc.get_callee_saved_fprs();
assert!(fprs.contains(&SystemZRegister::F8));
assert!(!fprs.contains(&SystemZRegister::R15));
}
#[test]
fn test_cc_allocatable_gprs() {
let cc = SystemZCallingConvention::new();
let regs = cc.get_allocatable_gprs();
assert!(!regs.contains(&SystemZRegister::R0)); assert!(!regs.contains(&SystemZRegister::R15)); assert!(regs.contains(&SystemZRegister::R2));
}
#[test]
fn test_cc_static_chain() {
let cc = SystemZCallingConvention::new();
assert_eq!(cc.static_chain, SystemZRegister::R12);
}
#[test]
fn test_reg_class_num_regs() {
assert_eq!(SystemZRegClass::GPR64.num_regs(), SZ_GPR_COUNT);
assert_eq!(SystemZRegClass::FPR64.num_regs(), SZ_FPR_COUNT);
assert_eq!(SystemZRegClass::AR32.num_regs(), SZ_AR_COUNT);
}
#[test]
fn test_reg_class_reg_size() {
assert_eq!(SystemZRegClass::GPR64.reg_size(), 64);
assert_eq!(SystemZRegClass::GPR32.reg_size(), 32);
assert_eq!(SystemZRegClass::FPR128.reg_size(), 128);
}
#[test]
fn test_reg_class_spill_size() {
assert_eq!(SystemZRegClass::GPR64.spill_size(), 8);
assert_eq!(SystemZRegClass::GPR32.spill_size(), 4);
assert_eq!(SystemZRegClass::FPR128.spill_size(), 16);
}
#[test]
fn test_reg_class_display() {
assert_eq!(format!("{}", SystemZRegClass::GPR64), "GR64");
assert_eq!(format!("{}", SystemZRegClass::FPR64), "FP64");
}
#[test]
fn test_fl_default() {
let fl = SystemZFrameLowering::default_lowering();
assert_eq!(fl.frame_info.total_frame_size, 160);
assert!(fl.frame_info.has_back_chain);
}
#[test]
fn test_fl_compute_frame_size() {
let mut fl = SystemZFrameLowering::default_lowering();
let size = fl.compute_frame_size(5, 4, 64, 0);
assert!(size >= 256);
}
#[test]
fn test_fl_gen_prologue_with_fp() {
let mut fl = SystemZFrameLowering::default_lowering();
fl.set_use_fp(true);
fl.compute_frame_size(0, 0, 0, 0);
let prologue = fl.gen_prologue();
assert!(!prologue.is_empty());
assert!(prologue.iter().any(|s| s.contains("%r11")));
}
#[test]
fn test_fl_gen_epilogue() {
let fl = SystemZFrameLowering::default_lowering();
let epilogue = fl.gen_epilogue();
assert!(!epilogue.is_empty());
assert!(epilogue.iter().any(|s| s.contains("br %r14")));
}
#[test]
fn test_fl_callee_save_restore() {
let fl = SystemZFrameLowering::default_lowering();
let regs = vec![SystemZRegister::R6, SystemZRegister::R7];
let save = fl.gen_save_callee_saved(®s);
let restore = fl.gen_restore_callee_saved(®s);
assert_eq!(save.len(), 2);
assert_eq!(restore.len(), 2);
}
#[test]
fn test_fl_fits_in_red_zone() {
let mut fl = SystemZFrameLowering::default_lowering();
fl.compute_frame_size(0, 0, 0, 0); assert!(fl.fits_in_red_zone());
}
#[test]
fn test_fl_needs_frame_pointer() {
let fl = SystemZFrameLowering::default_lowering();
assert!(fl.needs_frame_pointer(true, false));
assert!(!fl.needs_frame_pointer(false, true));
}
#[test]
fn test_ri_from_index() {
assert_eq!(
SystemZRegisterInfo::from_index(0),
Some(SystemZRegister::R0)
);
assert_eq!(
SystemZRegisterInfo::from_index(15),
Some(SystemZRegister::R15)
);
assert_eq!(SystemZRegisterInfo::from_index(16), None);
}
#[test]
fn test_ri_from_fpr_index() {
assert_eq!(
SystemZRegisterInfo::from_fpr_index(0),
Some(SystemZRegister::F0)
);
assert_eq!(
SystemZRegisterInfo::from_fpr_index(15),
Some(SystemZRegister::F15)
);
assert_eq!(SystemZRegisterInfo::from_fpr_index(16), None);
}
#[test]
fn test_ri_from_ar_index() {
assert_eq!(
SystemZRegisterInfo::from_ar_index(0),
Some(SystemZRegister::A0)
);
assert_eq!(
SystemZRegisterInfo::from_ar_index(15),
Some(SystemZRegister::A15)
);
assert_eq!(SystemZRegisterInfo::from_ar_index(16), None);
}
#[test]
fn test_cc_mask() {
assert_eq!(SystemZCondCode::EQ.mask(), 8);
assert_eq!(SystemZCondCode::Unconditional.mask(), 15);
assert_eq!(SystemZCondCode::Never.mask(), 0);
}
#[test]
fn test_cc_inverse() {
assert_eq!(SystemZCondCode::EQ.inverse(), SystemZCondCode::NE);
assert_eq!(SystemZCondCode::NE.inverse(), SystemZCondCode::EQ);
assert_eq!(SystemZCondCode::LT.inverse(), SystemZCondCode::HE);
assert_eq!(SystemZCondCode::GT.inverse(), SystemZCondCode::LE);
assert_eq!(SystemZCondCode::O.inverse(), SystemZCondCode::NO);
assert_eq!(
SystemZCondCode::Unconditional.inverse(),
SystemZCondCode::Unconditional
);
assert_eq!(
SystemZCondCode::Never.inverse(),
SystemZCondCode::Unconditional
);
}
#[test]
fn test_cc_swap_operands() {
assert_eq!(SystemZCondCode::LT.swap_operands(), SystemZCondCode::GT);
assert_eq!(SystemZCondCode::GT.swap_operands(), SystemZCondCode::LT);
assert_eq!(SystemZCondCode::LE.swap_operands(), SystemZCondCode::HE);
assert_eq!(SystemZCondCode::EQ.swap_operands(), SystemZCondCode::EQ);
}
#[test]
fn test_cc_name() {
assert_eq!(SystemZCondCode::EQ.name(), "eq");
assert_eq!(SystemZCondCode::NE.name(), "ne");
assert_eq!(SystemZCondCode::Unconditional.name(), "unconditional");
}
#[test]
fn test_bridge_opcode_mnemonic() {
assert_eq!(SystemZBridgeOpcode::LGR.mnemonic(), "lgr");
assert_eq!(SystemZBridgeOpcode::A.mnemonic(), "a");
assert_eq!(SystemZBridgeOpcode::BRASL.mnemonic(), "brasl");
assert_eq!(SystemZBridgeOpcode::MADBR.mnemonic(), "madbr");
}
#[test]
fn test_bridge_opcode_format() {
let formats_to_check = vec![
(SystemZBridgeOpcode::LGR, SystemZFormat::RRE),
(SystemZBridgeOpcode::BR, SystemZFormat::RR),
(SystemZBridgeOpcode::J, SystemZFormat::RX),
(SystemZBridgeOpcode::LHI, SystemZFormat::RI),
(SystemZBridgeOpcode::BRASL, SystemZFormat::RIL),
(SystemZBridgeOpcode::STG, SystemZFormat::RXY),
(SystemZBridgeOpcode::MVC, SystemZFormat::SS),
(SystemZBridgeOpcode::SLL, SystemZFormat::RS),
(SystemZBridgeOpcode::SLLG, SystemZFormat::RSY),
];
for (op, expected_fmt) in &formats_to_check {
assert_eq!(
op.format(),
*expected_fmt,
"Opcode {:?} has wrong format",
op
);
}
}
#[test]
fn test_bridge_opcode_size_bytes() {
assert_eq!(SystemZBridgeOpcode::BR.size_bytes(), 2); assert_eq!(SystemZBridgeOpcode::A.size_bytes(), 4); assert_eq!(SystemZBridgeOpcode::LGR.size_bytes(), 4); assert_eq!(SystemZBridgeOpcode::STG.size_bytes(), 6); assert_eq!(SystemZBridgeOpcode::BRASL.size_bytes(), 6); assert_eq!(SystemZBridgeOpcode::MVC.size_bytes(), 6); }
#[test]
fn test_bridge_opcode_is_terminator() {
assert!(SystemZBridgeOpcode::BR.is_terminator());
assert!(SystemZBridgeOpcode::BRC.is_terminator());
assert!(SystemZBridgeOpcode::J.is_terminator());
assert!(SystemZBridgeOpcode::BRK.is_terminator());
assert!(!SystemZBridgeOpcode::LGR.is_terminator());
assert!(!SystemZBridgeOpcode::A.is_terminator());
}
#[test]
fn test_bridge_opcode_is_call() {
assert!(SystemZBridgeOpcode::BASR.is_call());
assert!(SystemZBridgeOpcode::BRASL.is_call());
assert!(!SystemZBridgeOpcode::BR.is_call());
}
#[test]
fn test_bridge_opcode_is_compare() {
assert!(SystemZBridgeOpcode::C.is_compare());
assert!(SystemZBridgeOpcode::CGR.is_compare());
assert!(SystemZBridgeOpcode::CLI.is_compare());
assert!(SystemZBridgeOpcode::TM.is_compare());
assert!(!SystemZBridgeOpcode::A.is_compare());
}
#[test]
fn test_bridge_opcode_may_load_store() {
assert!(SystemZBridgeOpcode::L.may_load());
assert!(!SystemZBridgeOpcode::L.may_store());
assert!(!SystemZBridgeOpcode::ST.may_load());
assert!(SystemZBridgeOpcode::ST.may_store());
}
#[test]
fn test_all_bridge_opcodes_count() {
let count = ALL_SZB_OPCODES.len();
assert!(count >= 100, "Expected >= 100 opcodes, got {}", count);
}
#[test]
fn test_all_bridge_opcodes_no_sentinel() {
assert!(!ALL_SZB_OPCODES.contains(&SystemZBridgeOpcode::SZ_BRIDGE_OPCODE_COUNT));
}
#[test]
fn test_map_llvm_add() {
assert_eq!(
map_llvm_to_systemz_opcode(Opcode::Add),
Some(SystemZOpcode::A)
);
}
#[test]
fn test_map_llvm_sub() {
assert_eq!(
map_llvm_to_systemz_opcode(Opcode::Sub),
Some(SystemZOpcode::S)
);
}
#[test]
fn test_map_llvm_load() {
assert_eq!(
map_llvm_to_systemz_opcode(Opcode::Load),
Some(SystemZOpcode::L)
);
}
#[test]
fn test_map_llvm_store() {
assert_eq!(
map_llvm_to_systemz_opcode(Opcode::Store),
Some(SystemZOpcode::ST)
);
}
#[test]
fn test_map_llvm_unknown() {
let result = map_llvm_to_systemz_opcode(Opcode::Alloca);
let _ = result;
}
#[test]
fn test_map_llvm_to_sz_bridge() {
assert_eq!(
map_llvm_to_sz_bridge_opcode(Opcode::Add),
Some(SystemZBridgeOpcode::A)
);
assert_eq!(
map_llvm_to_sz_bridge_opcode(Opcode::Select),
Some(SystemZBridgeOpcode::LOCGR)
);
}
#[test]
fn test_features_default() {
let features = BridgeFeatures::default();
assert!(!features.has(BridgeFeature::Vector));
}
#[test]
fn test_features_enable_disable() {
let mut features = BridgeFeatures::default();
features.enable(BridgeFeature::Vector);
assert!(features.has(BridgeFeature::Vector));
features.disable(BridgeFeature::Vector);
assert!(!features.has(BridgeFeature::Vector));
}
#[test]
fn test_features_z14() {
let mut features = BridgeFeatures::default();
features.enable_z14_features();
assert!(features.has(BridgeFeature::Vector));
assert!(features.has(BridgeFeature::DFP));
assert!(features.has(BridgeFeature::TX));
assert!(features.has(BridgeFeature::GS));
assert!(features.has(BridgeFeature::HighWord));
}
#[test]
fn test_features_z16() {
let mut features = BridgeFeatures::default();
features.enable_z16_features();
assert!(features.has(BridgeFeature::Vector));
assert!(features.has(BridgeFeature::ExecutionHint));
}
#[test]
fn test_features_x86() {
let mut features = BridgeFeatures::default();
features.enable_x86_features();
assert!(features.has(BridgeFeature::SSE));
assert!(features.has(BridgeFeature::SSE2));
assert!(features.has(BridgeFeature::AVX));
assert!(features.has(BridgeFeature::FMA));
}
#[test]
fn test_features_list_enabled() {
let mut features = BridgeFeatures::default();
features.enable(BridgeFeature::Vector);
features.enable(BridgeFeature::DFP);
let list = features.list_enabled();
assert_eq!(list.len(), 2);
}
#[test]
fn test_features_to_string() {
let mut features = BridgeFeatures::default();
features.enable(BridgeFeature::Vector);
features.enable(BridgeFeature::DFP);
let s = features.to_feature_string();
assert!(s.contains("+dfp"));
assert!(s.contains("+vector"));
}
#[test]
fn test_features_from_string() {
let features = BridgeFeatures::from_string("+vector,+dfp,-tx");
assert!(features.has(BridgeFeature::Vector));
assert!(features.has(BridgeFeature::DFP));
assert!(!features.has(BridgeFeature::TX));
}
#[test]
fn test_features_parse_feature() {
assert!(BridgeFeatures::parse_feature("sse2").is_some());
assert!(BridgeFeatures::parse_feature("nonexistent").is_none());
}
#[test]
fn test_stats_default() {
let stats = BridgeStats::default();
assert_eq!(stats.functions_processed, 0);
assert_eq!(stats.spill_slots, 0);
assert_eq!(stats.vectorized_loops, 0);
}
#[test]
fn test_error_display() {
let err = BridgeError::UnsupportedArchPair {
source: SystemZBridgeArch::SystemZ,
target: SystemZBridgeArch::X86_32,
};
let s = format!("{}", err);
assert!(s.contains("s390x"));
assert!(s.contains("i386"));
}
#[test]
fn test_error_isel() {
let err = BridgeError::ISelFailed {
opcode: 42,
reason: "no pattern".to_string(),
};
assert!(format!("{}", err).contains("42"));
}
#[test]
fn test_error_regalloc() {
let err = BridgeError::RegAllocFailed {
reason: "out of registers".to_string(),
};
assert!(format!("{}", err).contains("out of registers"));
}
#[test]
fn test_error_internal() {
let err = BridgeError::Internal("test".to_string());
assert!(format!("{}", err).contains("test"));
}
#[test]
fn test_cost_zero() {
let c = CostEstimate::zero();
assert_eq!(c.latency, 0);
assert!(!c.profitable);
}
#[test]
fn test_cost_new() {
let c = CostEstimate::new(5, 2, 8);
assert_eq!(c.latency, 5);
assert_eq!(c.throughput, 2);
assert_eq!(c.code_size, 8);
assert!(c.profitable);
}
#[test]
fn test_cost_model_systemz() {
let model = SystemZX86CostModel::new(SystemZBridgeArch::X86_64, SystemZBridgeArch::SystemZ);
let cost = model.estimate(Opcode::Add, 2);
assert_eq!(cost.latency, 1); }
#[test]
fn test_cost_model_fp_div() {
let model = SystemZX86CostModel::new(SystemZBridgeArch::X86_64, SystemZBridgeArch::SystemZ);
let cost = model.estimate(Opcode::FDiv, 2);
assert_eq!(cost.latency, 30); }
#[test]
fn test_cost_model_estimate_systemz() {
let model = SystemZX86CostModel::new(SystemZBridgeArch::X86_64, SystemZBridgeArch::SystemZ);
let cost = model.estimate_systemz(SystemZBridgeOpcode::MADBR);
assert!(cost.profitable);
}
#[test]
fn test_cost_model_compare() {
let model = SystemZX86CostModel::new(SystemZBridgeArch::X86_64, SystemZBridgeArch::SystemZ);
let (sz_cost, x86_cost) = model.compare_costs(Opcode::Add, 2);
assert_eq!(sz_cost.latency, 1);
assert_eq!(x86_cost.latency, 1);
}
#[test]
fn test_cost_model_is_sz_more_profitable() {
let model = SystemZX86CostModel::new(SystemZBridgeArch::X86_64, SystemZBridgeArch::SystemZ);
assert!(model.is_sz_more_profitable(Opcode::FAdd));
assert!(!model.is_sz_more_profitable(Opcode::SDiv));
}
#[test]
fn test_isel_new() {
let isel = CrossTargetISel::new(SystemZBridgeArch::SystemZ);
assert!(!isel.patterns.is_empty());
assert!(!isel.legalize_rules.is_empty());
}
#[test]
fn test_isel_vreg() {
let mut isel = CrossTargetISel::new(SystemZBridgeArch::SystemZ);
let v1 = isel.next_vreg();
let v2 = isel.next_vreg();
assert_eq!(v1, 0);
assert_eq!(v2, 1);
}
#[test]
fn test_isel_map_vreg() {
let mut isel = CrossTargetISel::new(SystemZBridgeArch::SystemZ);
let reg = isel.map_vreg(42);
let same = isel.map_vreg(42);
assert_eq!(reg, same);
}
#[test]
fn test_isel_needs_legalization() {
let isel = CrossTargetISel::new(SystemZBridgeArch::SystemZ);
assert!(isel.needs_legalization(256, GenericMachineOpcode::Add));
assert!(!isel.needs_legalization(64, GenericMachineOpcode::Add));
}
#[test]
fn test_isel_set_function() {
let mut isel = CrossTargetISel::new(SystemZBridgeArch::SystemZ);
isel.set_current_function("test_func".into());
}
#[test]
fn test_regalloc_new() {
let ra = CrossTargetRegAlloc::new(SystemZBridgeArch::SystemZ);
assert!(!ra.gprs.is_empty());
assert!(!ra.fprs.is_empty());
}
#[test]
fn test_regalloc_spill() {
let mut ra = CrossTargetRegAlloc::new(SystemZBridgeArch::SystemZ);
let slot = ra.allocate_spill_slot(8);
assert_eq!(slot, 0);
assert_eq!(ra.num_spill_slots(), 1);
}
#[test]
fn test_regalloc_callee_saved() {
let ra = CrossTargetRegAlloc::new(SystemZBridgeArch::SystemZ);
assert!(ra.num_callee_saved_gprs() >= 5);
assert!(ra.num_callee_saved_fprs() >= 5);
}
#[test]
fn test_regalloc_sp_fp_ra() {
let ra = CrossTargetRegAlloc::new(SystemZBridgeArch::SystemZ);
assert_eq!(ra.sp(), SystemZRegister::R15);
assert_eq!(ra.fp(), SystemZRegister::R11);
assert_eq!(ra.ra(), SystemZRegister::R14);
}
#[test]
fn test_regalloc_is_sp() {
let ra = CrossTargetRegAlloc::new(SystemZBridgeArch::SystemZ);
assert!(ra.is_sp(SystemZRegister::R15));
assert!(!ra.is_sp(SystemZRegister::R0));
}
#[test]
fn test_regalloc_class_gpr() {
let ra = CrossTargetRegAlloc::new(SystemZBridgeArch::SystemZ);
let class = ra.get_preferred_class(64, false);
assert_eq!(class, SystemZRegClass::GPR64);
}
#[test]
fn test_regalloc_class_fpr() {
let ra = CrossTargetRegAlloc::new(SystemZBridgeArch::SystemZ);
let class = ra.get_preferred_class(64, true);
assert_eq!(class, SystemZRegClass::FPR64);
}
#[test]
fn test_regalloc_class_fp128() {
let ra = CrossTargetRegAlloc::new(SystemZBridgeArch::SystemZ);
let class = ra.get_preferred_class(128, true);
assert_eq!(class, SystemZRegClass::FPR128);
}
#[test]
fn test_abi_assign_gpr_arg() {
let mut abi = CrossTargetABI::new(SystemZBridgeArch::SystemZ);
assert_eq!(abi.assign_gpr_arg(), Some(SystemZRegister::R2));
assert_eq!(abi.assign_gpr_arg(), Some(SystemZRegister::R3));
assert_eq!(abi.assign_gpr_arg(), Some(SystemZRegister::R4));
assert_eq!(abi.assign_gpr_arg(), Some(SystemZRegister::R5));
assert_eq!(abi.assign_gpr_arg(), Some(SystemZRegister::R6));
assert_eq!(abi.assign_gpr_arg(), None); }
#[test]
fn test_abi_assign_fpr_arg() {
let mut abi = CrossTargetABI::new(SystemZBridgeArch::SystemZ);
assert_eq!(abi.assign_fpr_arg(), Some(SystemZRegister::F0));
assert_eq!(abi.assign_fpr_arg(), Some(SystemZRegister::F2));
assert_eq!(abi.assign_fpr_arg(), Some(SystemZRegister::F4));
assert_eq!(abi.assign_fpr_arg(), Some(SystemZRegister::F6));
assert_eq!(abi.assign_fpr_arg(), None); }
#[test]
fn test_abi_get_return_reg() {
let abi = CrossTargetABI::new(SystemZBridgeArch::SystemZ);
assert_eq!(abi.get_return_reg(false), SystemZRegister::R2);
assert_eq!(abi.get_return_reg(true), SystemZRegister::F0);
}
#[test]
fn test_abi_return_pair() {
let abi = CrossTargetABI::new(SystemZBridgeArch::SystemZ);
let (lo, hi) = abi.get_return_reg_pair();
assert_eq!(lo, SystemZRegister::R2);
assert_eq!(hi, SystemZRegister::R3);
}
#[test]
fn test_abi_reset() {
let mut abi = CrossTargetABI::new(SystemZBridgeArch::SystemZ);
abi.assign_gpr_arg();
abi.reset_args();
assert_eq!(abi.gpr_args_used, 0);
}
#[test]
fn test_abi_stack_args() {
let mut abi = CrossTargetABI::new(SystemZBridgeArch::SystemZ);
abi.add_stack_arg(8);
abi.add_stack_arg(4); assert_eq!(abi.total_stack_args(), 16);
}
#[test]
fn test_abi_supports_varargs() {
let abi = CrossTargetABI::new(SystemZBridgeArch::SystemZ);
assert!(abi.supports_varargs());
}
#[test]
fn test_abi_static_chain() {
let abi = CrossTargetABI::new(SystemZBridgeArch::SystemZ);
assert_eq!(abi.static_chain_reg(), SystemZRegister::R12);
}
#[test]
fn test_abi_convert_cond() {
let abi = CrossTargetABI::new(SystemZBridgeArch::SystemZ);
assert_eq!(abi.convert_cond(MachineIRCond::EQ), SystemZCondCode::EQ);
assert_eq!(abi.convert_cond(MachineIRCond::NE), SystemZCondCode::NE);
assert_eq!(abi.convert_cond(MachineIRCond::VS), SystemZCondCode::O);
assert_eq!(abi.convert_cond(MachineIRCond::VC), SystemZCondCode::NO);
}
#[test]
fn test_cross_fl_systemz_prologue() {
let fl = CrossTargetFrameLowering::new(SystemZBridgeArch::SystemZ);
let prologue = fl.emit_prologue();
assert!(!prologue.is_empty());
assert!(prologue
.iter()
.any(|s| s.contains("back") || s.contains("stg")));
}
#[test]
fn test_cross_fl_x86_prologue() {
let fl = CrossTargetFrameLowering::new(SystemZBridgeArch::X86_64);
let prologue = fl.emit_prologue();
assert!(prologue.iter().any(|s| s.contains("rbp")));
}
#[test]
fn test_cross_fl_epilogue() {
let fl = CrossTargetFrameLowering::new(SystemZBridgeArch::SystemZ);
let epilogue = fl.emit_epilogue();
assert!(epilogue.iter().any(|s| s.contains("br %r14")));
}
#[test]
fn test_cross_fl_compute_frame() {
let mut fl = CrossTargetFrameLowering::new(SystemZBridgeArch::SystemZ);
let size = fl.compute_frame(64, 0, 3, 2);
assert!(size > 0);
}
#[test]
fn test_opt_new() {
let opt = CrossTargetOptimization::new(SystemZBridgeArch::SystemZ);
assert!(!opt.peephole_patterns.is_empty());
}
#[test]
fn test_opt_apply_peephole() {
let opt = CrossTargetOptimization::new(SystemZBridgeArch::SystemZ);
let seq = vec![GenericMachineOpcode::Copy, GenericMachineOpcode::Copy];
let result = opt.apply_peephole(&seq);
assert!(result.len() < seq.len() || result.len() == 1);
}
#[test]
fn test_opt_nop_elimination() {
let opt = CrossTargetOptimization::new(SystemZBridgeArch::SystemZ);
let seq = vec![GenericMachineOpcode::Nop];
let result = opt.apply_peephole(&seq);
assert!(result.is_empty());
}
#[test]
fn test_opt_aggressive() {
let mut opt = CrossTargetOptimization::new(SystemZBridgeArch::SystemZ);
opt.set_aggressive(true);
assert!(opt.aggressive);
}
#[test]
fn test_opt_back_chain_leaf() {
let opt = CrossTargetOptimization::new(SystemZBridgeArch::SystemZ);
let result = opt.optimize_back_chain(true, true);
assert!(!result);
}
#[test]
fn test_ctx_default_cross() {
let ctx = CrossTargetSystemZ::default_cross();
assert_eq!(ctx.bridge.source_arch, SystemZBridgeArch::X86_64);
assert_eq!(ctx.bridge.target_arch, SystemZBridgeArch::SystemZ);
}
#[test]
fn test_ctx_native_systemz() {
let ctx = CrossTargetSystemZ::native_systemz();
assert_eq!(ctx.bridge.source_arch, SystemZBridgeArch::SystemZ);
assert_eq!(ctx.bridge.target_arch, SystemZBridgeArch::SystemZ);
}
#[test]
fn test_ctx_output_format() {
let mut ctx = CrossTargetSystemZ::default_cross();
assert_eq!(ctx.output_format, SystemZOutputFormat::Assembly);
ctx.set_output_format(SystemZOutputFormat::Object);
assert_eq!(ctx.output_format, SystemZOutputFormat::Object);
}
#[test]
fn test_ctx_describe() {
let ctx = CrossTargetSystemZ::default_cross();
let desc = ctx.describe();
assert!(desc.contains("CrossTargetSystemZ"));
assert!(desc.contains("s390x"));
}
#[test]
fn test_ctx_stats_summary() {
let ctx = CrossTargetSystemZ::default_cross();
let summary = ctx.get_stats_summary();
assert!(summary.contains("Functions"));
}
#[test]
fn test_ctx_has_opcode() {
let ctx = CrossTargetSystemZ::default_cross();
assert!(ctx.has_opcode(SystemZBridgeOpcode::LGR));
}
#[test]
fn test_ctx_llvm_to_sz_mnemonic() {
let ctx = CrossTargetSystemZ::default_cross();
assert_eq!(ctx.llvm_to_sz_mnemonic(Opcode::Add), Some("a"));
}
#[test]
fn test_ctx_enable_ir_dump() {
let mut ctx = CrossTargetSystemZ::default_cross();
ctx.enable_ir_dump();
assert!(ctx.dump_ir);
}
#[test]
fn test_ctx_enable_profile() {
let mut ctx = CrossTargetSystemZ::default_cross();
ctx.enable_profile();
assert!(ctx.profile);
}
#[test]
fn test_ctx_disable_validation() {
let mut ctx = CrossTargetSystemZ::default_cross();
ctx.disable_validation();
assert!(!ctx.validate);
}
#[test]
fn test_output_format_display() {
assert_eq!(format!("{}", SystemZOutputFormat::Assembly), "asm");
assert_eq!(format!("{}", SystemZOutputFormat::Object), "obj");
assert_eq!(format!("{}", SystemZOutputFormat::LLVMIR), "ll");
}
#[test]
fn test_gm_op_is_terminator() {
assert!(GenericMachineOpcode::Br.is_terminator());
assert!(GenericMachineOpcode::Ret.is_terminator());
assert!(!GenericMachineOpcode::Add.is_terminator());
}
#[test]
fn test_gm_op_is_memory() {
assert!(GenericMachineOpcode::Load.is_memory());
assert!(GenericMachineOpcode::Store.is_memory());
assert!(GenericMachineOpcode::FrameSetup.is_memory());
assert!(!GenericMachineOpcode::Add.is_memory());
}
#[test]
fn test_gm_op_is_commutative() {
assert!(GenericMachineOpcode::Add.is_commutative());
assert!(GenericMachineOpcode::Mul.is_commutative());
assert!(GenericMachineOpcode::FAdd.is_commutative());
assert!(!GenericMachineOpcode::Sub.is_commutative());
assert!(!GenericMachineOpcode::SDiv.is_commutative());
}
#[test]
fn test_mir_inst_new() {
let inst = MachineIRInst::new(GenericMachineOpcode::Add);
assert_eq!(inst.opcode, GenericMachineOpcode::Add);
assert!(inst.dst.is_none());
assert!(inst.srcs.is_empty());
}
#[test]
fn test_mir_inst_with_dst() {
let inst = MachineIRInst::new(GenericMachineOpcode::Add).with_dst(1);
assert_eq!(inst.dst, Some(1));
}
#[test]
fn test_mir_inst_with_src() {
let inst =
MachineIRInst::new(GenericMachineOpcode::Add).with_src(MachineIROperand::VReg(2));
assert_eq!(inst.srcs.len(), 1);
}
#[test]
fn test_mir_operand_is_reg() {
assert!(MachineIROperand::VReg(0).is_reg());
assert!(!MachineIROperand::Imm(42).is_reg());
}
#[test]
fn test_mir_operand_is_imm() {
assert!(MachineIROperand::Imm(10).is_imm());
assert!(MachineIROperand::FImm(1.5).is_imm());
assert!(!MachineIROperand::VReg(0).is_imm());
}
#[test]
fn test_find_subsequence_empty_needle() {
let haystack = [1, 2, 3];
let needle: [i32; 0] = [];
assert_eq!(find_subsequence(&haystack, &needle), Some(0));
}
#[test]
fn test_find_subsequence_found() {
let haystack = [1, 2, 3, 4, 5];
let needle = [3, 4];
assert_eq!(find_subsequence(&haystack, &needle), Some(2));
}
#[test]
fn test_find_subsequence_not_found() {
let haystack = [1, 2, 3];
let needle = [4, 5];
assert_eq!(find_subsequence(&haystack, &needle), None);
}
#[test]
fn test_full_bridge_pipeline() {
let mut bridge = SystemZX86Bridge::native_to_systemz();
let result = bridge.run_pipeline();
assert!(result.is_ok());
let output = result.unwrap();
assert_eq!(output.target_arch, SystemZBridgeArch::SystemZ);
}
#[test]
fn test_cross_target_compile() {
let mut ctx = CrossTargetSystemZ::default_cross();
let result = ctx.compile();
assert!(result.is_ok());
}
#[test]
fn test_feature_parity_z16() {
let tm = SystemZTargetMachine::z16_target();
let features = tm.get_feature_summary();
let enabled_count = features.iter().filter(|(_, e)| *e).count();
assert!(
enabled_count >= 8,
"z16 has {} features enabled",
enabled_count
);
}
#[test]
fn test_register_class_completeness() {
let classes = [
SystemZRegClass::GPR64,
SystemZRegClass::FPR64,
SystemZRegClass::AR32,
SystemZRegClass::ADDR64,
];
for cls in &classes {
assert!(cls.num_regs() > 0);
}
}
#[test]
fn test_all_formats_covered() {
let formats = [
SystemZFormat::RR,
SystemZFormat::RX,
SystemZFormat::RI,
SystemZFormat::RIL,
SystemZFormat::RRE,
SystemZFormat::RXY,
SystemZFormat::SI,
SystemZFormat::RS,
SystemZFormat::RSY,
SystemZFormat::SS,
SystemZFormat::S,
];
for fmt in &formats {
let count = ALL_SZB_OPCODES
.iter()
.filter(|op| op.format() == *fmt)
.count();
assert!(count > 0, "No instructions for format {:?}", fmt);
}
}
}
pub struct SystemZCodeGenHelper {
bridge: SystemZX86Bridge,
current_function: Option<String>,
}
impl SystemZCodeGenHelper {
pub fn new(bridge: SystemZX86Bridge) -> Self {
SystemZCodeGenHelper {
bridge,
current_function: None,
}
}
pub fn emit_function_prologue(
&self,
func_name: &str,
frame_size: i64,
save_regs: &[SystemZRegister],
) -> Vec<String> {
let mut lines = Vec::new();
lines.push(format!(".globl {}", func_name));
lines.push(format!(".type {}, @function", func_name));
lines.push(format!("{}:", func_name));
lines.push(".cfi_startproc".to_string());
lines.push("stg %r15, 0(%r15)".to_string());
for reg in save_regs {
if let Some(idx) = SystemZRegisterInfo::get_index(*reg) {
let offset = 16 + (idx as i64) * 8;
lines.push(format!(
"stg {}, {}(%r15)",
SystemZRegisterInfo::get_name(*reg),
offset
));
}
}
if frame_size > 0 {
if frame_size <= SYSTEMZ_MAX_DISP_RX {
lines.push(format!("aghi %r15, -{}", frame_size));
} else {
lines.push(format!("agfi %r1, -{}", frame_size));
lines.push("agr %r15, %r1".to_string());
}
}
lines
}
pub fn emit_function_epilogue(
&self,
frame_size: i64,
restore_regs: &[SystemZRegister],
) -> Vec<String> {
let mut lines = Vec::new();
if frame_size > 0 {
if frame_size <= SYSTEMZ_MAX_DISP_RX {
lines.push(format!("aghi %r15, {}", frame_size));
} else {
lines.push(format!("agfi %r1, {}", frame_size));
lines.push("agr %r15, %r1".to_string());
}
}
for reg in restore_regs.iter().rev() {
if let Some(idx) = SystemZRegisterInfo::get_index(*reg) {
let offset = 16 + (idx as i64) * 8;
lines.push(format!(
"lg {}, {}(%r15)",
SystemZRegisterInfo::get_name(*reg),
offset
));
}
}
lines.push("lg %r14, 112(%r15)".to_string());
lines.push("br %r14".to_string());
lines.push(".cfi_endproc".to_string());
lines
}
pub fn emit_load_immediate(&self, reg: SystemZRegister, value: i64) -> Vec<String> {
let reg_name = SystemZRegisterInfo::get_name(reg);
let mut lines = Vec::new();
if value == 0 {
lines.push(format!("lghi {}, 0", reg_name));
} else if value >= -32768 && value <= 32767 {
lines.push(format!("lghi {}, {}", reg_name, value));
} else {
lines.push(format!("lgfi {}, {}", reg_name, value));
}
lines
}
pub fn emit_conditional_branch(&self, cc: SystemZCondCode, target: &str) -> String {
format!("brc {}, {}", cc.mask(), target)
}
pub fn emit_compare_and_branch(
&self,
lhs: SystemZRegister,
rhs: SystemZRegister,
cc: SystemZCondCode,
target: &str,
) -> Vec<String> {
vec![
format!(
"cgr {}, {}",
SystemZRegisterInfo::get_name(lhs),
SystemZRegisterInfo::get_name(rhs)
),
format!("brc {}, {}", cc.mask(), target),
]
}
pub fn emit_call(&self, target: &str) -> String {
format!("brasl %r14, {}", target)
}
pub fn emit_tail_call(&self, target: &str) -> Vec<String> {
vec!["lg %r14, 112(%r15)".to_string(), format!("jg {}", target)]
}
pub fn emit_spill_gpr(&self, reg: SystemZRegister, offset: i64) -> String {
format!(
"stg {}, {}(%r15)",
SystemZRegisterInfo::get_name(reg),
offset
)
}
pub fn emit_fill_gpr(&self, reg: SystemZRegister, offset: i64) -> String {
format!(
"lg {}, {}(%r15)",
SystemZRegisterInfo::get_name(reg),
offset
)
}
pub fn build_assembly_file(&self, functions: &[(String, Vec<String>)]) -> String {
let mut asm = String::new();
asm.push_str("\t.text\n");
asm.push_str(&format!(
"\t.machine \"{}\"\n",
self.bridge.target_machine.cpu
));
for (name, body) in functions {
asm.push_str(&format!("\n{0}:\n", name));
asm.push_str("\t.cfi_startproc\n");
for line in body {
asm.push_str(&format!("\t{}\n", line));
}
asm.push_str("\t.cfi_endproc\n");
asm.push_str(&format!("\t.size {0}, .-{0}\n", name));
}
asm
}
pub fn is_rx_disp(&self, disp: i64) -> bool {
disp >= 0 && disp <= SYSTEMZ_MAX_DISP_RX
}
pub fn is_rxy_disp(&self, disp: i64) -> bool {
disp >= -524288 && disp <= 524287
}
pub fn best_disp_format(&self, disp: i64) -> SystemZFormat {
if self.is_rx_disp(disp) {
SystemZFormat::RX
} else {
SystemZFormat::RXY
}
}
}
pub struct SystemZMCInstBuilder {
pub opcode: SystemZBridgeOpcode,
pub operands: Vec<MCOperand>,
pub cc_mask: Option<u8>,
}
#[derive(Debug, Clone)]
pub enum MCOperand {
Reg(SystemZRegister),
Imm(i64),
Mem {
base: SystemZRegister,
disp: i64,
},
MemIndex {
base: SystemZRegister,
index: SystemZRegister,
disp: i64,
},
Length(u8),
Label(String),
}
impl SystemZMCInstBuilder {
pub fn new(opcode: SystemZBridgeOpcode) -> Self {
SystemZMCInstBuilder {
opcode,
operands: Vec::new(),
cc_mask: None,
}
}
pub fn add_reg(mut self, reg: SystemZRegister) -> Self {
self.operands.push(MCOperand::Reg(reg));
self
}
pub fn add_imm(mut self, imm: i64) -> Self {
self.operands.push(MCOperand::Imm(imm));
self
}
pub fn add_mem(mut self, base: SystemZRegister, disp: i64) -> Self {
self.operands.push(MCOperand::Mem { base, disp });
self
}
pub fn add_mem_index(
mut self,
base: SystemZRegister,
index: SystemZRegister,
disp: i64,
) -> Self {
self.operands
.push(MCOperand::MemIndex { base, index, disp });
self
}
pub fn add_cc_mask(mut self, mask: u8) -> Self {
self.cc_mask = Some(mask);
self
}
pub fn build(&self) -> String {
let mnemonic = self.opcode.mnemonic();
let mut parts: Vec<String> = Vec::new();
for op in &self.operands {
match op {
MCOperand::Reg(r) => parts.push(SystemZRegisterInfo::get_name(*r).to_string()),
MCOperand::Imm(i) => parts.push(i.to_string()),
MCOperand::Mem { base, disp } => parts.push(format!(
"{}({})",
disp,
SystemZRegisterInfo::get_name(*base)
)),
MCOperand::MemIndex { base, index, disp } => parts.push(format!(
"{}({},{})",
disp,
SystemZRegisterInfo::get_name(*index),
SystemZRegisterInfo::get_name(*base)
)),
MCOperand::Length(l) => parts.push(l.to_string()),
MCOperand::Label(l) => parts.push(l.clone()),
}
}
if let Some(cc) = self.cc_mask {
format!("{} {}, {}", mnemonic, cc, parts.join(", "))
} else {
format!("{} {}", mnemonic, parts.join(", "))
}
}
pub fn encode(&self) -> Vec<u8> {
match self.opcode.format() {
SystemZFormat::RR => self.encode_rr(),
SystemZFormat::RX => self.encode_rx(),
SystemZFormat::RRE => self.encode_rre(),
SystemZFormat::RI => self.encode_ri(),
SystemZFormat::RIL => self.encode_ril(),
SystemZFormat::RXY => self.encode_rxy(),
SystemZFormat::RS => self.encode_rs(),
SystemZFormat::RSY => self.encode_rsy(),
SystemZFormat::SI => self.encode_si(),
SystemZFormat::SS => self.encode_ss(),
SystemZFormat::S => self.encode_s(),
}
}
fn encode_rr(&self) -> Vec<u8> {
let r1 = self.reg_at(0);
let r2 = self.reg_at(1);
vec![self.opcode_byte(0x00), (r1 << 4) | r2]
}
fn encode_rx(&self) -> Vec<u8> {
let r1 = self.reg_at(0);
let x2 = self.reg_at(1);
let b2 = self.base_at(1);
let d2 = self.disp_at(1);
vec![
self.opcode_byte(0x50),
(r1 << 4) | x2,
((b2 << 4) | ((d2 >> 8) & 0x0F) as u8),
(d2 & 0xFF) as u8,
]
}
fn encode_rre(&self) -> Vec<u8> {
let r1 = self.reg_at(0);
let r2 = self.reg_at(1);
vec![0xB9, self.opcode_byte(0x00), (r1 << 4), (r2 << 4)]
}
fn encode_ri(&self) -> Vec<u8> {
let r1 = self.reg_at(0);
let imm = self.imm_at(1);
vec![
self.opcode_byte(0x80),
(r1 << 4) | (((imm >> 12) & 0x0F) as u8),
((imm >> 4) & 0xFF) as u8,
(((imm & 0x0F) as u8) << 4),
]
}
fn encode_ril(&self) -> Vec<u8> {
let r1 = self.reg_at(0);
let imm = self.imm_at(1);
let op = self.opcode_halfword(0xC000);
vec![
(op >> 8) as u8,
(op & 0xFF) as u8,
(r1 << 4) | (((imm >> 28) & 0x0F) as u8),
((imm >> 20) & 0xFF) as u8,
((imm >> 12) & 0xFF) as u8,
((imm >> 4) & 0xFF) as u8,
(((imm & 0x0F) as u8) << 4),
]
}
fn encode_rxy(&self) -> Vec<u8> {
let r1 = self.reg_at(0);
let x2 = self.reg_at(1);
let b2 = self.base_at(1);
let d2 = self.disp_at(1);
vec![
self.opcode_byte(0xE3),
(r1 << 4) | x2,
((b2 << 4) | (((d2 >> 16) & 0x0F) as u8)),
((d2 >> 8) & 0xFF) as u8,
(d2 & 0xFF) as u8,
(((d2 >> 12) & 0x0F) as u8) << 4,
]
}
fn encode_rs(&self) -> Vec<u8> {
let r1 = self.reg_at(0);
let r3 = self.reg_at(1);
let b2 = self.base_at(2);
let d2 = self.disp_at(2);
vec![
self.opcode_byte(0x80),
(r1 << 4) | r3,
((b2 << 4) | ((d2 >> 8) & 0x0F) as u8),
(d2 & 0xFF) as u8,
]
}
fn encode_rsy(&self) -> Vec<u8> {
let r1 = self.reg_at(0);
let r3 = self.reg_at(1);
let b2 = self.base_at(2);
let d2 = self.disp_at(2);
vec![
self.opcode_byte(0xEB),
(r1 << 4) | r3,
((b2 << 4) | (((d2 >> 16) & 0x0F) as u8)),
((d2 >> 8) & 0xFF) as u8,
(d2 & 0xFF) as u8,
(((d2 >> 12) & 0x0F) as u8) << 4,
]
}
fn encode_si(&self) -> Vec<u8> {
let i2 = self.imm_at(0);
let b1 = self.base_at(1);
let d1 = self.disp_at(1);
vec![
self.opcode_byte(0x90),
(i2 as u8) & 0xFF,
((b1 << 4) | ((d1 >> 8) & 0x0F) as u8),
(d1 & 0xFF) as u8,
]
}
fn encode_ss(&self) -> Vec<u8> {
let l = self.length_at(0);
let b1 = self.base_at(1);
let d1 = self.disp_at(1);
let b2 = self.base_at(2);
let d2 = self.disp_at(2);
vec![
self.opcode_byte(0xD0),
l,
((b1 << 4) | ((d1 >> 8) & 0x0F) as u8),
(d1 & 0xFF) as u8,
((b2 << 4) | ((d2 >> 8) & 0x0F) as u8),
(d2 & 0xFF) as u8,
]
}
fn encode_s(&self) -> Vec<u8> {
let b2 = self.base_at(0);
let d2 = self.disp_at(0);
vec![
self.opcode_byte(0x80),
((b2 << 4) | ((d2 >> 8) & 0x0F) as u8),
(d2 & 0xFF) as u8,
]
}
fn reg_at(&self, idx: usize) -> u8 {
self.operands.get(idx).map_or(0, |op| {
if let MCOperand::Reg(r) = op {
SystemZRegisterInfo::get_index(*r).unwrap_or(0) as u8
} else {
0
}
})
}
fn base_at(&self, idx: usize) -> u8 {
self.operands.get(idx).map_or(0, |op| match op {
MCOperand::Mem { base, .. } | MCOperand::MemIndex { base, .. } => {
SystemZRegisterInfo::get_index(*base).unwrap_or(0) as u8
}
_ => 0,
})
}
fn disp_at(&self, idx: usize) -> i64 {
self.operands.get(idx).map_or(0, |op| match op {
MCOperand::Mem { disp, .. } | MCOperand::MemIndex { disp, .. } => *disp,
_ => 0,
})
}
fn imm_at(&self, idx: usize) -> i64 {
self.operands
.get(idx)
.map_or(0, |op| if let MCOperand::Imm(i) = op { *i } else { 0 })
}
fn length_at(&self, idx: usize) -> u8 {
self.operands
.get(idx)
.map_or(0, |op| if let MCOperand::Length(l) = op { *l } else { 0 })
}
fn opcode_byte(&self, base: u8) -> u8 {
let mut h: u8 = base;
for b in self.opcode.mnemonic().bytes() {
h = h.wrapping_mul(13).wrapping_add(b);
}
h
}
fn opcode_halfword(&self, base: u16) -> u16 {
base | (self.opcode_byte(0) as u16)
}
}
pub struct SystemZDisassembler {
pub target: SystemZTargetMachine,
pub print_addresses: bool,
pub print_bytes: bool,
}
#[derive(Debug, Clone)]
pub struct DisassembledInst {
pub address: u64,
pub bytes: Vec<u8>,
pub mnemonic: String,
pub operands: String,
pub size: u8,
}
impl SystemZDisassembler {
pub fn new(target: SystemZTargetMachine) -> Self {
SystemZDisassembler {
target,
print_addresses: true,
print_bytes: true,
}
}
pub fn disassemble(&self, data: &[u8], base_addr: u64) -> Vec<DisassembledInst> {
let mut result = Vec::new();
let mut offset = 0usize;
while offset < data.len() {
if let Some(inst) = self.decode_one(&data[offset..], base_addr + offset as u64) {
offset += inst.size as usize;
result.push(inst);
} else {
offset += 2;
}
}
result
}
fn decode_one(&self, data: &[u8], addr: u64) -> Option<DisassembledInst> {
if data.is_empty() {
return None;
}
let op = data[0];
let size = self.guess_size(op, data);
let bytes = data[..size.min(data.len())].to_vec();
let mnemonic = self.guess_mnemonic(op, data);
Some(DisassembledInst {
address: addr,
bytes,
mnemonic,
operands: String::new(),
size: size as u8,
})
}
fn guess_size(&self, op: u8, data: &[u8]) -> usize {
match op {
0x01..=0x3F => 2,
0x40..=0x7F => {
if data.len() >= 4 {
4
} else {
data.len()
}
}
0x80..=0xBF => {
if data.len() >= 4 {
4
} else {
data.len()
}
}
0xC0..=0xC7 => {
if data.len() >= 6 {
6
} else {
data.len()
}
}
0xD0..=0xDF => {
if data.len() >= 6 {
6
} else {
data.len()
}
}
0xE0..=0xEF => {
if data.len() >= 6 {
6
} else {
data.len()
}
}
_ => 2,
}
}
fn guess_mnemonic(&self, op: u8, data: &[u8]) -> String {
match op {
0x07 => "bcr",
0x0D => "basr",
0x18 => "lr",
0x1A => "ar",
0x1B => "sr",
0x41 => "la",
0x47 => "bc",
0x50 => "st",
0x54 => "n",
0x55 => "cl",
0x58 => "l",
0x59 => "c",
0x5A => "a",
0x5B => "s",
0x5D => "d",
0x7E => "le",
0x7F => "ste",
0x91 => "tm",
0x95 => "cli",
0xA5 => "ahi",
0xA7 => "brc",
0xB9 if data.len() >= 3 => return self.decode_b9(data[1]),
0xC0 => "larl",
0xC4 => "brcl",
0xD2 => "mvc",
0xE3 if data.len() >= 6 => return self.decode_e3(data[5]),
_ => return format!("???_{:02X}", op),
}
.to_string()
}
fn decode_b9(&self, sub: u8) -> String {
match sub {
0x00 => "lgr",
0x02 => "ltgr",
0x08 => "agr",
0x09 => "sgr",
0x14 => "nr",
0x15 => "clr",
0x16 => "or",
0x17 => "xr",
0x19 => "cr",
0x20 => "cgr",
0x28 => "ldr",
0x38 => "ler",
0x80 => "ngr",
0x81 => "ogr",
0x82 => "xgr",
0xE0 => "locgr",
_ => &*format!("???_b9_{:02X}", sub),
}
.to_string()
}
fn decode_e3(&self, sub: u8) -> String {
match sub {
0x04 => "lg",
0x10 => "lgb",
0x14 => "lgf",
0x15 => "lgh",
0x24 => "stg",
0x50 => "sty",
0x58 => "ly",
0x71 => "lay",
_ => &*format!("???_e3_{:02X}", sub),
}
.to_string()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SystemZRelocation {
R_390_8 = 1,
R_390_16 = 7,
R_390_32 = 8,
R_390_64 = 9,
R_390_PC8 = 2,
R_390_PC16 = 3,
R_390_PC32 = 4,
R_390_PC64 = 5,
R_390_PLT32 = 6,
R_390_GOT16 = 10,
R_390_GOT32 = 11,
R_390_PLT12 = 12,
R_390_PLT16 = 13,
R_390_PLT24 = 14,
R_390_PLT32DBL = 15,
R_390_PC12DBL = 16,
R_390_PC16DBL = 17,
R_390_PC24DBL = 18,
R_390_GOTPCDBL = 19,
R_390_GOTOFF32 = 20,
R_390_GOTOFF64 = 21,
R_390_PLTOFF16 = 23,
R_390_PLTOFF32 = 24,
R_390_COPY = 26,
R_390_GLOB_DAT = 27,
R_390_JMP_SLOT = 28,
R_390_RELATIVE = 29,
R_390_TLS_GD64 = 30,
R_390_TLS_IE64 = 33,
R_390_TLS_LE64 = 34,
R_390_20 = 38,
R_390_GOT20 = 39,
R_390_PC12 = 42,
R_390_PC24 = 43,
}
impl SystemZRelocation {
pub fn size(&self) -> u8 {
match self {
Self::R_390_8 | Self::R_390_PC8 => 1,
Self::R_390_16 | Self::R_390_PC16 | Self::R_390_GOT16 => 2,
Self::R_390_32 | Self::R_390_PC32 | Self::R_390_GOT32 | Self::R_390_PLT32 => 4,
Self::R_390_64 | Self::R_390_PC64 => 8,
_ => 4,
}
}
pub fn is_pc_relative(&self) -> bool {
matches!(
self,
Self::R_390_PC8
| Self::R_390_PC16
| Self::R_390_PC32
| Self::R_390_PC64
| Self::R_390_GOT16
| Self::R_390_GOT32
| Self::R_390_PLT32
| Self::R_390_PLT12
| Self::R_390_PLT16
| Self::R_390_PLT24
| Self::R_390_PC12
| Self::R_390_PC24
| Self::R_390_GOT20
)
}
pub fn needs_got(&self) -> bool {
matches!(
self,
Self::R_390_GOT16
| Self::R_390_GOT32
| Self::R_390_GOTOFF32
| Self::R_390_GOTOFF64
| Self::R_390_GOT20
)
}
pub fn needs_plt(&self) -> bool {
matches!(
self,
Self::R_390_PLT32 | Self::R_390_PLT12 | Self::R_390_PLT16 | Self::R_390_PLT24
)
}
pub fn name(&self) -> &'static str {
match self {
Self::R_390_8 => "R_390_8",
Self::R_390_16 => "R_390_16",
Self::R_390_32 => "R_390_32",
Self::R_390_64 => "R_390_64",
Self::R_390_PC8 => "R_390_PC8",
Self::R_390_PC16 => "R_390_PC16",
Self::R_390_PC32 => "R_390_PC32",
Self::R_390_PC64 => "R_390_PC64",
Self::R_390_GOT16 => "R_390_GOT16",
Self::R_390_GOT32 => "R_390_GOT32",
Self::R_390_PLT32 => "R_390_PLT32",
Self::R_390_COPY => "R_390_COPY",
Self::R_390_GLOB_DAT => "R_390_GLOB_DAT",
Self::R_390_JMP_SLOT => "R_390_JMP_SLOT",
Self::R_390_RELATIVE => "R_390_RELATIVE",
_ => "R_390_UNKNOWN",
}
}
}
#[cfg(test)]
mod extended_tests {
use super::*;
#[test]
fn test_helper_prologue() {
let bridge = SystemZX86Bridge::default();
let h = SystemZCodeGenHelper::new(bridge);
let p = h.emit_function_prologue("f", 0, &[]);
assert!(p.iter().any(|s| s.contains("f")));
}
#[test]
fn test_helper_load_imm_zero() {
let h = SystemZCodeGenHelper::new(SystemZX86Bridge::default());
let ins = h.emit_load_immediate(SystemZRegister::R2, 0);
assert!(ins[0].contains("lghi"));
}
#[test]
fn test_helper_load_imm_small() {
let h = SystemZCodeGenHelper::new(SystemZX86Bridge::default());
let ins = h.emit_load_immediate(SystemZRegister::R3, 42);
assert!(ins[0].contains("lghi"));
}
#[test]
fn test_helper_load_imm_large() {
let h = SystemZCodeGenHelper::new(SystemZX86Bridge::default());
let ins = h.emit_load_immediate(SystemZRegister::R4, 1000000000);
assert!(ins[0].contains("lgfi"));
}
#[test]
fn test_helper_cond_branch() {
let h = SystemZCodeGenHelper::new(SystemZX86Bridge::default());
let br = h.emit_conditional_branch(SystemZCondCode::EQ, "t");
assert!(br.contains("brc") && br.contains("8"));
}
#[test]
fn test_helper_call() {
let h = SystemZCodeGenHelper::new(SystemZX86Bridge::default());
let c = h.emit_call("printf");
assert!(c.contains("brasl") && c.contains("%r14"));
}
#[test]
fn test_helper_tail_call() {
let h = SystemZCodeGenHelper::new(SystemZX86Bridge::default());
let tc = h.emit_tail_call("exit");
assert_eq!(tc.len(), 2);
assert!(tc[1].contains("jg"));
}
#[test]
fn test_helper_spill_fill() {
let h = SystemZCodeGenHelper::new(SystemZX86Bridge::default());
assert!(h.emit_spill_gpr(SystemZRegister::R5, 32).contains("stg"));
assert!(h.emit_fill_gpr(SystemZRegister::R5, 32).contains("lg"));
}
#[test]
fn test_helper_is_rx_disp() {
let h = SystemZCodeGenHelper::new(SystemZX86Bridge::default());
assert!(h.is_rx_disp(0));
assert!(h.is_rx_disp(4095));
assert!(!h.is_rx_disp(4096));
}
#[test]
fn test_helper_is_rxy_disp() {
let h = SystemZCodeGenHelper::new(SystemZX86Bridge::default());
assert!(h.is_rxy_disp(4096));
assert!(h.is_rxy_disp(524287));
assert!(!h.is_rxy_disp(524288));
}
#[test]
fn test_helper_best_format() {
let h = SystemZCodeGenHelper::new(SystemZX86Bridge::default());
assert_eq!(h.best_disp_format(0), SystemZFormat::RX);
assert_eq!(h.best_disp_format(4096), SystemZFormat::RXY);
}
#[test]
fn test_mc_builder_rr() {
let inst = SystemZMCInstBuilder::new(SystemZBridgeOpcode::BR)
.add_reg(SystemZRegister::R14)
.build();
assert!(inst.contains("br") && inst.contains("%r14"));
}
#[test]
fn test_mc_builder_rx() {
let inst = SystemZMCInstBuilder::new(SystemZBridgeOpcode::L)
.add_reg(SystemZRegister::R2)
.add_mem(SystemZRegister::R15, 160)
.build();
assert!(inst.contains("l") && inst.contains("160"));
}
#[test]
fn test_mc_builder_rxy() {
let inst = SystemZMCInstBuilder::new(SystemZBridgeOpcode::STG)
.add_reg(SystemZRegister::R14)
.add_mem(SystemZRegister::R15, 112)
.build();
assert!(inst.contains("stg") && inst.contains("112"));
}
#[test]
fn test_mc_builder_ril() {
let inst = SystemZMCInstBuilder::new(SystemZBridgeOpcode::BRASL)
.add_reg(SystemZRegister::R14)
.add_imm(0)
.build();
assert!(inst.contains("brasl"));
}
#[test]
fn test_mc_builder_mem_idx() {
let inst = SystemZMCInstBuilder::new(SystemZBridgeOpcode::STG)
.add_reg(SystemZRegister::R5)
.add_mem_index(SystemZRegister::R15, SystemZRegister::R1, 0)
.build();
assert!(inst.contains("%r1") && inst.contains("%r15"));
}
#[test]
fn test_mc_builder_cc() {
let inst = SystemZMCInstBuilder::new(SystemZBridgeOpcode::BRC)
.add_cc_mask(8)
.add_imm(0)
.build();
assert!(inst.contains("8"));
}
#[test]
fn test_mc_encode_rr_size() {
assert_eq!(
SystemZMCInstBuilder::new(SystemZBridgeOpcode::BR)
.add_reg(SystemZRegister::R14)
.encode()
.len(),
2
);
}
#[test]
fn test_mc_encode_rx_size() {
assert_eq!(
SystemZMCInstBuilder::new(SystemZBridgeOpcode::L)
.add_reg(SystemZRegister::R2)
.add_mem(SystemZRegister::R15, 0)
.encode()
.len(),
4
);
}
#[test]
fn test_mc_encode_ril_size() {
assert_eq!(
SystemZMCInstBuilder::new(SystemZBridgeOpcode::BRASL)
.add_reg(SystemZRegister::R14)
.add_imm(0)
.encode()
.len(),
6
);
}
#[test]
fn test_mc_encode_rre_size() {
assert_eq!(
SystemZMCInstBuilder::new(SystemZBridgeOpcode::LGR)
.add_reg(SystemZRegister::R2)
.add_reg(SystemZRegister::R3)
.encode()
.len(),
4
);
}
#[test]
fn test_mc_encode_si_size() {
assert_eq!(
SystemZMCInstBuilder::new(SystemZBridgeOpcode::CLI)
.add_imm(42)
.add_mem(SystemZRegister::R15, 0)
.encode()
.len(),
4
);
}
#[test]
fn test_dis_empty() {
let d = SystemZDisassembler::new(SystemZTargetMachine::default_target());
assert!(d.disassemble(&[], 0).is_empty());
}
#[test]
fn test_dis_rr() {
let d = SystemZDisassembler::new(SystemZTargetMachine::default_target());
let r = d.disassemble(&[0x18, 0x23], 0x1000);
assert_eq!(r.len(), 1);
assert_eq!(r[0].address, 0x1000);
assert_eq!(r[0].size, 2);
}
#[test]
fn test_dis_rx() {
let d = SystemZDisassembler::new(SystemZTargetMachine::default_target());
assert!(!d.disassemble(&[0x58, 0x20, 0xF0, 0x00], 0).is_empty());
}
#[test]
fn test_dis_mixed() {
let d = SystemZDisassembler::new(SystemZTargetMachine::default_target());
assert_eq!(
d.disassemble(&[0x18, 0x23, 0x58, 0x20, 0xF0, 0x00, 0x07, 0xFE], 0)
.len(),
3
);
}
#[test]
fn test_guess_size_rr() {
assert_eq!(
SystemZDisassembler::new(SystemZTargetMachine::default_target())
.guess_size(0x18, &[0x18, 0x23]),
2
);
}
#[test]
fn test_guess_size_rx() {
assert_eq!(
SystemZDisassembler::new(SystemZTargetMachine::default_target())
.guess_size(0x58, &[0x58, 0x20, 0xF0, 0x00]),
4
);
}
#[test]
fn test_guess_size_ril() {
assert_eq!(
SystemZDisassembler::new(SystemZTargetMachine::default_target())
.guess_size(0xC0, &[0xC0; 6]),
6
);
}
#[test]
fn test_all_formats_have_encoders() {
for fmt in &[
SystemZFormat::RR,
SystemZFormat::RX,
SystemZFormat::RRE,
SystemZFormat::RI,
SystemZFormat::RIL,
SystemZFormat::RXY,
SystemZFormat::RS,
SystemZFormat::RSY,
SystemZFormat::SI,
SystemZFormat::SS,
SystemZFormat::S,
] {
assert!(
ALL_SZB_OPCODES.iter().any(|op| op.format() == *fmt),
"No opcode for {:?}",
fmt
);
}
}
#[test]
fn test_rr_format_count() {
assert!(
ALL_SZB_OPCODES
.iter()
.filter(|op| op.format() == SystemZFormat::RR)
.count()
> 0
);
}
#[test]
fn test_rx_format_count() {
assert!(
ALL_SZB_OPCODES
.iter()
.filter(|op| op.format() == SystemZFormat::RX)
.count()
>= 5
);
}
#[test]
fn test_rxy_format_count() {
assert!(
ALL_SZB_OPCODES
.iter()
.filter(|op| op.format() == SystemZFormat::RXY)
.count()
>= 10
);
}
#[test]
fn test_fp_coverage() {
for op in &[
SystemZBridgeOpcode::LE,
SystemZBridgeOpcode::LD,
SystemZBridgeOpcode::STE,
SystemZBridgeOpcode::STD,
SystemZBridgeOpcode::AE,
SystemZBridgeOpcode::AD,
SystemZBridgeOpcode::SE,
SystemZBridgeOpcode::SD,
] {
assert!(ALL_SZB_OPCODES.contains(op));
}
}
#[test]
fn test_tx_coverage() {
for op in &[
SystemZBridgeOpcode::TBEGIN,
SystemZBridgeOpcode::TBEGINC,
SystemZBridgeOpcode::TEND,
SystemZBridgeOpcode::TABORT,
] {
assert!(ALL_SZB_OPCODES.contains(op));
}
}
#[test]
fn test_vector_coverage() {
for op in &[
SystemZBridgeOpcode::VL,
SystemZBridgeOpcode::VST,
SystemZBridgeOpcode::VA,
SystemZBridgeOpcode::VN,
SystemZBridgeOpcode::VO,
SystemZBridgeOpcode::VX,
] {
assert!(ALL_SZB_OPCODES.contains(op));
}
}
#[test]
fn test_distinct_ops() {
for op in &[
SystemZBridgeOpcode::AGRK,
SystemZBridgeOpcode::SGRK,
SystemZBridgeOpcode::NGRK,
SystemZBridgeOpcode::OGRK,
SystemZBridgeOpcode::XGRK,
] {
assert!(ALL_SZB_OPCODES.contains(op));
}
}
#[test]
fn test_cab_ops() {
for op in &[
SystemZBridgeOpcode::CGIJ,
SystemZBridgeOpcode::CIJ,
SystemZBridgeOpcode::CLGIJ,
SystemZBridgeOpcode::CLIJ,
SystemZBridgeOpcode::CGRJ,
SystemZBridgeOpcode::CRJ,
SystemZBridgeOpcode::CLGRJ,
SystemZBridgeOpcode::CLRJ,
] {
assert!(ALL_SZB_OPCODES.contains(op));
}
}
#[test]
fn test_soc_ops() {
for op in &[
SystemZBridgeOpcode::LOCR,
SystemZBridgeOpcode::LOCGR,
SystemZBridgeOpcode::LOC,
SystemZBridgeOpcode::LOCG,
SystemZBridgeOpcode::STOC,
SystemZBridgeOpcode::STOCG,
] {
assert!(ALL_SZB_OPCODES.contains(op));
}
}
#[test]
fn test_byte_order_ops() {
for op in &[
SystemZBridgeOpcode::LRVR,
SystemZBridgeOpcode::LRVGR,
SystemZBridgeOpcode::LRVH,
SystemZBridgeOpcode::STRV,
SystemZBridgeOpcode::STRVG,
SystemZBridgeOpcode::STRVH,
] {
assert!(ALL_SZB_OPCODES.contains(op));
}
}
#[test]
fn test_no_opcode_gt_6_bytes() {
for op in ALL_SZB_OPCODES {
assert!(
op.size_bytes() <= 6,
"{:?} is {} bytes",
op,
op.size_bytes()
);
}
}
#[test]
fn test_reloc_sizes() {
assert_eq!(SystemZRelocation::R_390_8.size(), 1);
assert_eq!(SystemZRelocation::R_390_16.size(), 2);
assert_eq!(SystemZRelocation::R_390_32.size(), 4);
assert_eq!(SystemZRelocation::R_390_64.size(), 8);
}
#[test]
fn test_reloc_pc() {
assert!(SystemZRelocation::R_390_PC32.is_pc_relative());
assert!(!SystemZRelocation::R_390_32.is_pc_relative());
}
#[test]
fn test_reloc_got() {
assert!(SystemZRelocation::R_390_GOT32.needs_got());
assert!(!SystemZRelocation::R_390_32.needs_got());
}
#[test]
fn test_reloc_plt() {
assert!(SystemZRelocation::R_390_PLT32.needs_plt());
assert!(!SystemZRelocation::R_390_32.needs_plt());
}
#[test]
fn test_reloc_name() {
assert_eq!(SystemZRelocation::R_390_32.name(), "R_390_32");
}
#[test]
fn test_many_spill_slots() {
let mut ra = CrossTargetRegAlloc::new(SystemZBridgeArch::SystemZ);
for i in 0..100 {
assert_eq!(ra.allocate_spill_slot(8), i);
}
assert_eq!(ra.num_spill_slots(), 100);
}
#[test]
fn test_exhaust_gpr_args() {
let mut abi = CrossTargetABI::new(SystemZBridgeArch::SystemZ);
for _ in 0..5 {
assert!(abi.assign_gpr_arg().is_some());
}
assert!(abi.assign_gpr_arg().is_none());
}
#[test]
fn test_exhaust_fpr_args() {
let mut abi = CrossTargetABI::new(SystemZBridgeArch::SystemZ);
for _ in 0..4 {
assert!(abi.assign_fpr_arg().is_some());
}
assert!(abi.assign_fpr_arg().is_none());
}
#[test]
fn test_bridge_run_pipeline_multiple() {
let mut bridge = SystemZX86Bridge::native_to_systemz();
for _ in 0..5 {
assert!(bridge.run_pipeline().is_ok());
}
assert_eq!(bridge.stats.functions_processed, 5);
}
#[test]
fn test_features_to_and_from_string() {
let mut feats = BridgeFeatures::default();
feats.enable(BridgeFeature::Vector);
feats.enable(BridgeFeature::DFP);
feats.enable(BridgeFeature::TX);
let s = feats.to_feature_string();
let parsed = BridgeFeatures::from_string(&s);
assert!(parsed.has(BridgeFeature::Vector));
assert!(parsed.has(BridgeFeature::DFP));
assert!(parsed.has(BridgeFeature::TX));
}
#[test]
fn test_negative_features_from_string() {
let feats = BridgeFeatures::from_string("+vector,-dfp");
assert!(feats.has(BridgeFeature::Vector));
assert!(!feats.has(BridgeFeature::DFP));
}
#[test]
fn test_empty_feature_string() {
let feats = BridgeFeatures::from_string("");
assert!(feats.list_enabled().is_empty());
}
#[test]
fn test_commas_only_feature_string() {
let feats = BridgeFeatures::from_string(",,,");
assert!(feats.list_enabled().is_empty());
}
#[test]
fn test_hwcap_monotonic() {
let levels = [
SystemZArchLevel::Z900,
SystemZArchLevel::Z9,
SystemZArchLevel::Z10,
SystemZArchLevel::Z196,
SystemZArchLevel::ZEC12,
SystemZArchLevel::Z13,
SystemZArchLevel::Z14,
SystemZArchLevel::Z15,
SystemZArchLevel::Z16,
];
for w in levels.windows(2) {
assert!(w[1].hwcap_mask() >= w[0].hwcap_mask());
}
}
#[test]
fn test_tm_getters_all_archs() {
for arch in &[
SystemZArchLevel::Z10,
SystemZArchLevel::Z196,
SystemZArchLevel::ZEC12,
SystemZArchLevel::Z13,
SystemZArchLevel::Z14,
SystemZArchLevel::Z15,
SystemZArchLevel::Z16,
] {
let tm = SystemZTargetMachine::new(*arch);
assert_eq!(tm.get_pointer_size(), 64);
assert_eq!(tm.get_register_width(), 64);
assert_eq!(tm.get_stack_alignment(), 8);
assert!(tm.get_page_size() > 0);
}
}
#[test]
fn test_mnemonic_all_opcodes_produce_non_empty() {
for op in ALL_SZB_OPCODES {
assert!(!op.mnemonic().is_empty(), "{:?} has empty mnemonic", op);
}
}
#[test]
fn test_format_all_opcodes_produce_valid() {
for op in ALL_SZB_OPCODES {
let _ = op.format();
}
}
#[test]
fn test_size_all_opcodes_in_range() {
for op in ALL_SZB_OPCODES {
let s = op.size_bytes();
assert!(s == 2 || s == 4 || s == 6, "{:?} has size {}", op, s);
}
}
#[test]
fn test_bridge_all_formats_present() {
let bridge = SystemZX86Bridge::default();
for fmt in &[
SystemZFormat::RR,
SystemZFormat::RX,
SystemZFormat::RI,
SystemZFormat::RIL,
SystemZFormat::RRE,
SystemZFormat::RXY,
SystemZFormat::SI,
SystemZFormat::RS,
SystemZFormat::RSY,
SystemZFormat::SS,
SystemZFormat::S,
] {
assert!(
bridge.count_by_format(*fmt) > 0,
"No instructions for {:?}",
fmt
);
}
}
#[test]
fn test_codegen_helper_build_asm() {
let h = SystemZCodeGenHelper::new(SystemZX86Bridge::default());
let asm =
h.build_assembly_file(&[("main".into(), vec!["lghi %r1, 0".into(), "br %r14".into()])]);
assert!(asm.contains(".text"));
assert!(asm.contains("main:"));
assert!(asm.contains("lghi"));
}
#[test]
fn test_compare_and_branch_emit() {
let h = SystemZCodeGenHelper::new(SystemZX86Bridge::default());
let insns = h.emit_compare_and_branch(
SystemZRegister::R2,
SystemZRegister::R3,
SystemZCondCode::EQ,
"label",
);
assert_eq!(insns.len(), 2);
assert!(insns[0].contains("cgr"));
assert!(insns[1].contains("brc"));
}
#[test]
fn test_prologue_saves_gprs() {
let h = SystemZCodeGenHelper::new(SystemZX86Bridge::default());
let p = h.emit_function_prologue(
"save_test",
0,
&[
SystemZRegister::R6,
SystemZRegister::R7,
SystemZRegister::R8,
],
);
let save_count = p.iter().filter(|s| s.contains("stg")).count();
assert!(save_count >= 4);
}
#[test]
fn test_disassembler_lr() {
let d = SystemZDisassembler::new(SystemZTargetMachine::default_target());
let r = d.disassemble(&[0x18, 0x34], 0);
assert_eq!(r[0].mnemonic, "lr");
}
#[test]
fn test_disassembler_bcr() {
let d = SystemZDisassembler::new(SystemZTargetMachine::default_target());
let r = d.disassemble(&[0x07, 0xFE], 0);
assert_eq!(r[0].mnemonic, "bcr");
}
#[test]
fn test_disassembler_unknown() {
let d = SystemZDisassembler::new(SystemZTargetMachine::default_target());
let r = d.disassemble(&[0xFF, 0xFF], 0);
assert!(r[0].mnemonic.starts_with("???"));
}
#[test]
fn test_mc_all_formats_encode() {
let cases: &[fn() -> SystemZMCInstBuilder] = &[
|| SystemZMCInstBuilder::new(SystemZBridgeOpcode::BR).add_reg(SystemZRegister::R14),
|| {
SystemZMCInstBuilder::new(SystemZBridgeOpcode::L)
.add_reg(SystemZRegister::R2)
.add_mem(SystemZRegister::R15, 0)
},
|| {
SystemZMCInstBuilder::new(SystemZBridgeOpcode::LGR)
.add_reg(SystemZRegister::R2)
.add_reg(SystemZRegister::R3)
},
];
for b in cases {
let bytes = b().encode();
assert!(!bytes.is_empty());
}
}
}