use crate::codegen::{MachineBasicBlock, MachineFunction, MachineInstr, MachineOperand, VirtReg};
use crate::opcode::Opcode;
use crate::sparc::sparc_instr_info::{
SparcInstrDesc, SparcInstrInfo, SparcOpcode, SparcOperandType,
};
use crate::sparc::sparc_register_info::{
SparcRegClass, SparcRegisterInfo, SPARC_FPR_BASE, SPARC_FPR_COUNT, SPARC_GPR_BASE,
SPARC_GPR_COUNT, SPARC_MAX_REG_ID,
};
use crate::target_machine::{CodeGenOptLevel, CodeModel, RelocModel, TargetMachine, TargetOptions};
use crate::triple::{Arch, Triple};
use crate::types::Type;
use crate::value::ValueRef;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::fmt;
pub const SPARC_V8_DATA_LAYOUT: &str = "E-m:e-p:32:32-i64:64-f128:64-n32-S64";
pub const SPARC_V9_DATA_LAYOUT: &str = "E-m:e-i64:64-n32:64-S128";
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 SPARC_ABI_NAMES: &[&str] = &["sparc", "sparcv9"];
pub const X86_ABI_NAMES: &[&str] = &["sysv", "win64", "msvc"];
pub const SPARC_REGS_PER_WINDOW: usize = 24;
pub const SPARC_NUM_WINDOWS: usize = 8;
pub const SPARC_TOTAL_GPRS: usize = SPARC_GPR_COUNT as usize;
pub const SPARC_V8_STACK_ALIGNMENT: u32 = 8;
pub const SPARC_V9_STACK_ALIGNMENT: u32 = 16;
pub const SPARC_RED_ZONE_SIZE: u32 = 0;
pub const SPARC_INSTR_SIZE: u32 = 4;
pub const SPARC_WINDOW_SAVE_AREA: u32 = 128;
pub const SPARC_MAX_IMM: i64 = 4095;
pub const SPARC_MIN_IMM: i64 = -4096;
pub const SPARC_MAX_BRANCH_DISP: i64 = 0x3F_FFFF;
pub const SPARC_MAX_CALL_DISP: i64 = 0x1FFF_FFFF;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BridgeArch {
X86_64,
X86_32,
Sparc,
Sparcv9,
}
impl fmt::Display for BridgeArch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BridgeArch::X86_64 => write!(f, "x86_64"),
BridgeArch::X86_32 => write!(f, "i386"),
BridgeArch::Sparc => write!(f, "sparc"),
BridgeArch::Sparcv9 => write!(f, "sparcv9"),
}
}
}
impl BridgeArch {
pub fn is_64bit(&self) -> bool {
matches!(self, BridgeArch::X86_64 | BridgeArch::Sparcv9)
}
pub fn is_sparc_family(&self) -> bool {
matches!(self, BridgeArch::Sparc | BridgeArch::Sparcv9)
}
pub fn is_x86_family(&self) -> bool {
matches!(self, BridgeArch::X86_64 | BridgeArch::X86_32)
}
pub fn pointer_width(&self) -> u32 {
match self {
BridgeArch::X86_64 | BridgeArch::Sparcv9 => 64,
BridgeArch::X86_32 | BridgeArch::Sparc => 32,
}
}
pub fn data_layout(&self) -> &'static str {
match self {
BridgeArch::Sparc => SPARC_V8_DATA_LAYOUT,
BridgeArch::Sparcv9 => SPARC_V9_DATA_LAYOUT,
BridgeArch::X86_64 | BridgeArch::X86_32 => X86_64_DATA_LAYOUT,
}
}
pub fn stack_alignment(&self) -> u32 {
match self {
BridgeArch::Sparc => SPARC_V8_STACK_ALIGNMENT,
BridgeArch::Sparcv9 => SPARC_V9_STACK_ALIGNMENT,
BridgeArch::X86_64 | BridgeArch::X86_32 => 16,
}
}
pub fn is_big_endian(&self) -> bool {
matches!(self, BridgeArch::Sparc | BridgeArch::Sparcv9)
}
}
pub struct SPARCX86Bridge {
pub source_arch: BridgeArch,
pub target_arch: BridgeArch,
pub isel: CrossTargetISel,
pub regalloc: CrossTargetRegAlloc,
pub frame_lowering: CrossTargetFrameLowering,
pub cost_model: SPARCX86CostModel,
pub abi: CrossTargetABI,
pub optimizer: CrossTargetOptimization,
pub opt_level: CodeGenOptLevel,
pub debug_info: bool,
pub features: BridgeFeatures,
pub stats: BridgeStats,
}
impl SPARCX86Bridge {
pub fn new(source_arch: BridgeArch, target_arch: BridgeArch) -> Self {
let isel = CrossTargetISel::new(target_arch);
let regalloc = CrossTargetRegAlloc::new(target_arch);
let frame_lowering = CrossTargetFrameLowering::new(target_arch);
let cost_model = SPARCX86CostModel::new(source_arch, target_arch);
let abi = CrossTargetABI::new(target_arch);
let optimizer = CrossTargetOptimization::new(target_arch);
Self {
source_arch,
target_arch,
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)
}
fn parse_arch(triple_str: &str) -> BridgeArch {
let t = Triple::parse(triple_str);
match t.arch {
Arch::X86_64 => BridgeArch::X86_64,
Arch::X86 => BridgeArch::X86_32,
Arch::Sparc => BridgeArch::Sparc,
Arch::Sparcv9 => BridgeArch::Sparcv9,
_ => BridgeArch::Sparcv9, }
}
pub fn run_pipeline(&mut self, mf: &mut MachineFunction) -> Result<BridgeOutput, BridgeError> {
self.stats.functions_processed += 1;
self.isel.select_instructions(mf)?;
self.stats.isel_cycles += 1;
if self.opt_level.should_optimize() {
self.optimizer.optimize(mf)?;
self.stats.opt_cycles += 1;
}
if self.source_arch.is_sparc_family() {
self.lower_register_windows(mf)?;
}
self.regalloc.allocate_registers(mf)?;
self.stats.ra_cycles += 1;
self.frame_lowering.lower_frame(mf)?;
self.stats.frame_cycles += 1;
if self.opt_level.should_optimize() {
self.optimizer.post_ra_optimize(mf)?;
self.stats.post_ra_opt_cycles += 1;
}
Ok(BridgeOutput {
instructions_emitted: mf.blocks.iter().map(|bb| bb.instructions.len()).sum(),
basic_blocks: mf.blocks.len(),
target_arch: self.target_arch,
})
}
fn lower_register_windows(&mut self, _mf: &mut MachineFunction) -> Result<(), BridgeError> {
Ok(())
}
pub fn estimate_cost(&self, opcode: Opcode, operand_count: usize) -> CostEstimate {
self.cost_model.estimate(opcode, operand_count)
}
pub fn describe(&self) -> String {
format!(
"SPARCX86Bridge: {} → {} (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 has_register_windows(&self) -> bool {
self.source_arch.is_sparc_family() || self.target_arch.is_sparc_family()
}
pub fn window_save_area_size(&self) -> u32 {
match self.target_arch {
BridgeArch::Sparc => 64, BridgeArch::Sparcv9 => 128, _ => 0,
}
}
}
impl Default for SPARCX86Bridge {
fn default() -> Self {
Self::new(BridgeArch::Sparcv9, BridgeArch::X86_64)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BridgeFeature {
V8,
V9,
VIS1,
VIS2,
VIS3,
VIS4,
HWMUL,
HWDIV,
FPU,
FMAF,
POPC,
CASA,
ASI,
LeafProc,
SSE,
SSE2,
AVX,
AVX2,
AVX512,
FMA,
PGO,
}
impl fmt::Display for BridgeFeature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
BridgeFeature::V8 => "v8",
BridgeFeature::V9 => "v9",
BridgeFeature::VIS1 => "vis",
BridgeFeature::VIS2 => "vis2",
BridgeFeature::VIS3 => "vis3",
BridgeFeature::VIS4 => "vis4",
BridgeFeature::HWMUL => "hwmul",
BridgeFeature::HWDIV => "hwdiv",
BridgeFeature::FPU => "fpu",
BridgeFeature::FMAF => "fmaf",
BridgeFeature::POPC => "popc",
BridgeFeature::CASA => "casa",
BridgeFeature::ASI => "asi",
BridgeFeature::LeafProc => "leaf_proc",
BridgeFeature::SSE => "sse",
BridgeFeature::SSE2 => "sse2",
BridgeFeature::AVX => "avx",
BridgeFeature::AVX2 => "avx2",
BridgeFeature::AVX512 => "avx512",
BridgeFeature::FMA => "fma",
BridgeFeature::PGO => "pgo",
};
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_sparc_v8(&mut self) {
self.flags.insert(BridgeFeature::V8);
self.flags.insert(BridgeFeature::FPU);
self.flags.insert(BridgeFeature::HWMUL);
}
pub fn enable_sparc_v9(&mut self) {
self.enable_sparc_v8();
self.flags.insert(BridgeFeature::V9);
self.flags.insert(BridgeFeature::HWDIV);
self.flags.insert(BridgeFeature::POPC);
self.flags.insert(BridgeFeature::CASA);
}
pub fn enable_vis1(&mut self) {
self.enable_sparc_v9();
self.flags.insert(BridgeFeature::VIS1);
}
pub fn enable_vis2(&mut self) {
self.enable_vis1();
self.flags.insert(BridgeFeature::VIS2);
}
pub fn enable_vis3(&mut self) {
self.enable_vis2();
self.flags.insert(BridgeFeature::VIS3);
self.flags.insert(BridgeFeature::FMAF);
}
pub fn enable_vis4(&mut self) {
self.enable_vis3();
self.flags.insert(BridgeFeature::VIS4);
}
pub fn enable_x86_baseline(&mut self) {
self.flags.insert(BridgeFeature::SSE);
self.flags.insert(BridgeFeature::SSE2);
}
pub fn enable_x86_avx(&mut self) {
self.enable_x86_baseline();
self.flags.insert(BridgeFeature::AVX);
self.flags.insert(BridgeFeature::AVX2);
self.flags.insert(BridgeFeature::FMA);
}
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 {
"v8" => Some(BridgeFeature::V8),
"v9" => Some(BridgeFeature::V9),
"vis" | "vis1" => Some(BridgeFeature::VIS1),
"vis2" => Some(BridgeFeature::VIS2),
"vis3" => Some(BridgeFeature::VIS3),
"vis4" => Some(BridgeFeature::VIS4),
"hwmul" => Some(BridgeFeature::HWMUL),
"hwdiv" => Some(BridgeFeature::HWDIV),
"fpu" => Some(BridgeFeature::FPU),
"fmaf" => Some(BridgeFeature::FMAF),
"popc" => Some(BridgeFeature::POPC),
"casa" => Some(BridgeFeature::CASA),
"asi" => Some(BridgeFeature::ASI),
"leaf_proc" => Some(BridgeFeature::LeafProc),
"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),
"pgo" => Some(BridgeFeature::PGO),
_ => 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 window_allocations: usize,
pub delay_slots_filled: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BridgeError {
UnsupportedArchPair {
source: BridgeArch,
target: BridgeArch,
},
ISelFailed {
opcode: u32,
reason: String,
},
RegAllocFailed {
reason: String,
},
FrameLoweringFailed {
reason: String,
},
Unimplemented {
feature: String,
},
WindowOverflow {
depth: usize,
max_windows: usize,
},
WindowUnderflow,
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::WindowOverflow { depth, max_windows } => {
write!(
f,
"register window overflow at depth {} (max: {})",
depth, max_windows
)
}
BridgeError::WindowUnderflow => {
write!(f, "register window underflow: RESTORE without SAVE")
}
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: BridgeArch,
}
#[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,
}
}
}
pub mod sparc_format {
pub const FMT1_CALL: u8 = 0;
pub const FMT2_SETHI: u8 = 1;
pub const FMT2_BRANCH: u8 = 2;
pub const FMT3_ALU: u8 = 3;
pub const FMT3_LOAD: u8 = 4;
pub const FMT3_STORE: u8 = 5;
pub const FMT3_FPU: u8 = 6;
pub const FMT3_VIS: u8 = 7;
}
pub mod sparc_cond {
pub const ALWAYS: u8 = 0x8;
pub const NEVER: u8 = 0x0;
pub const EQUAL: u8 = 0x1;
pub const NOT_EQUAL: u8 = 0x9;
pub const GREATER: u8 = 0xA;
pub const LESS_OR_EQUAL: u8 = 0x2;
pub const LESS: u8 = 0x3;
pub const GREATER_OR_EQUAL: u8 = 0xB;
pub const GREATER_UNSIGNED: u8 = 0xC;
pub const LESS_OR_EQUAL_UNSIGNED: u8 = 0x4;
pub const CARRY_CLEAR: u8 = 0xD;
pub const CARRY_SET: u8 = 0x5;
pub const POSITIVE: u8 = 0xE;
pub const NEGATIVE: u8 = 0x6;
pub const OVERFLOW_CLEAR: u8 = 0xF;
pub const OVERFLOW_SET: u8 = 0x7;
}
pub struct BridgeSPARCInstrInfo;
impl BridgeSPARCInstrInfo {
pub fn get_mnemonic(opcode: SparcOpcode) -> &'static str {
match opcode {
SparcOpcode::ADD => "add",
SparcOpcode::ADDcc => "addcc",
SparcOpcode::ADDC => "addc",
SparcOpcode::ADDCcc => "addccc",
SparcOpcode::SUB => "sub",
SparcOpcode::SUBcc => "subcc",
SparcOpcode::SUBC => "subc",
SparcOpcode::SUBCcc => "subccc",
SparcOpcode::MULX => "mulx",
SparcOpcode::SDIVX => "sdivx",
SparcOpcode::UDIVX => "udivx",
SparcOpcode::SMUL => "smul",
SparcOpcode::UMUL => "umul",
SparcOpcode::SDIV => "sdiv",
SparcOpcode::UDIV => "udiv",
SparcOpcode::TADDcc => "taddcc",
SparcOpcode::TSUBcc => "tsubcc",
SparcOpcode::TADDCCTV => "taddcctv",
SparcOpcode::TSUBCCTV => "tsubcctv",
SparcOpcode::AND => "and",
SparcOpcode::ANDcc => "andcc",
SparcOpcode::ANDN => "andn",
SparcOpcode::ANDNcc => "andncc",
SparcOpcode::OR => "or",
SparcOpcode::ORcc => "orcc",
SparcOpcode::ORN => "orn",
SparcOpcode::ORNcc => "orncc",
SparcOpcode::XOR => "xor",
SparcOpcode::XORcc => "xorcc",
SparcOpcode::XNOR => "xnor",
SparcOpcode::XNORcc => "xnorcc",
SparcOpcode::SLL => "sll",
SparcOpcode::SRL => "srl",
SparcOpcode::SRA => "sra",
SparcOpcode::SLLX => "sllx",
SparcOpcode::SRLX => "srlx",
SparcOpcode::SRAX => "srax",
SparcOpcode::LD => "ld",
SparcOpcode::LDUB => "ldub",
SparcOpcode::LDUH => "lduh",
SparcOpcode::LDSB => "ldsb",
SparcOpcode::LDSH => "ldsh",
SparcOpcode::LDD => "ldd",
SparcOpcode::LDSTUB => "ldstub",
SparcOpcode::LDSW => "ldsw",
SparcOpcode::LDX => "ldx",
SparcOpcode::ST => "st",
SparcOpcode::STB => "stb",
SparcOpcode::STH => "sth",
SparcOpcode::STD => "std",
SparcOpcode::STX => "stx",
_ => "unknown",
}
}
pub fn is_branch(opcode: SparcOpcode) -> bool {
matches!(
opcode,
SparcOpcode::BA
| SparcOpcode::BN
| SparcOpcode::BE
| SparcOpcode::BNE
| SparcOpcode::BG
| SparcOpcode::BLE
| SparcOpcode::BL
| SparcOpcode::BGE
| SparcOpcode::BGU
| SparcOpcode::BLEU
| SparcOpcode::BCC
| SparcOpcode::BCS
| SparcOpcode::BPOS
| SparcOpcode::BNEG
| SparcOpcode::BVC
| SparcOpcode::BVS
| SparcOpcode::FBA
| SparcOpcode::FBN
| SparcOpcode::FBU
| SparcOpcode::FBG
| SparcOpcode::FBL
| SparcOpcode::FBO
| SparcOpcode::FBE
)
}
pub fn is_terminator(opcode: SparcOpcode) -> bool {
matches!(
opcode,
SparcOpcode::BA
| SparcOpcode::JMPL
| SparcOpcode::RET
| SparcOpcode::RETL
| SparcOpcode::CALL
)
}
pub fn is_unconditional(opcode: SparcOpcode) -> bool {
matches!(
opcode,
SparcOpcode::BA
| SparcOpcode::CALL
| SparcOpcode::JMPL
| SparcOpcode::RET
| SparcOpcode::RETL
)
}
pub fn is_memory(opcode: SparcOpcode) -> bool {
matches!(
opcode,
SparcOpcode::LD
| SparcOpcode::LDUB
| SparcOpcode::LDUH
| SparcOpcode::LDSB
| SparcOpcode::LDSH
| SparcOpcode::LDD
| SparcOpcode::LDSTUB
| SparcOpcode::LDSW
| SparcOpcode::LDX
| SparcOpcode::ST
| SparcOpcode::STB
| SparcOpcode::STH
| SparcOpcode::STD
| SparcOpcode::STX
| SparcOpcode::LDF
| SparcOpcode::LDDF
| SparcOpcode::STF
| SparcOpcode::STDF
)
}
pub fn is_fpu(opcode: SparcOpcode) -> bool {
matches!(
opcode,
SparcOpcode::FADDS
| SparcOpcode::FADDD
| SparcOpcode::FSUBS
| SparcOpcode::FSUBD
| SparcOpcode::FMULS
| SparcOpcode::FMULD
| SparcOpcode::FDIVS
| SparcOpcode::FDIVD
| SparcOpcode::FSQRTS
| SparcOpcode::FSQRTD
| SparcOpcode::FMOVS
| SparcOpcode::FNEGS
| SparcOpcode::FABSS
| SparcOpcode::FITOS
| SparcOpcode::FITOD
| SparcOpcode::FSTOI
| SparcOpcode::FDTOI
)
}
pub fn is_vis(opcode: SparcOpcode) -> bool {
matches!(
opcode,
SparcOpcode::FZEROS
| SparcOpcode::FONES
| SparcOpcode::FAND
| SparcOpcode::FNAND
| SparcOpcode::FOR
| SparcOpcode::FNOR
| SparcOpcode::FXOR
| SparcOpcode::FXNOR
| SparcOpcode::FPADD
| SparcOpcode::FPSUB
)
}
pub fn sets_cc(opcode: SparcOpcode) -> bool {
let name = Self::get_mnemonic(opcode);
name.ends_with("cc")
|| matches!(
opcode,
SparcOpcode::SUBcc | SparcOpcode::TADDcc | SparcOpcode::TSUBcc
)
}
pub fn get_format(opcode: SparcOpcode) -> u8 {
if opcode == SparcOpcode::CALL {
return sparc_format::FMT1_CALL;
}
match opcode {
SparcOpcode::SETHI => sparc_format::FMT2_SETHI,
SparcOpcode::BA
| SparcOpcode::BN
| SparcOpcode::BE
| SparcOpcode::BNE
| SparcOpcode::BG
| SparcOpcode::BLE
| SparcOpcode::BL
| SparcOpcode::BGE
| SparcOpcode::BGU
| SparcOpcode::BLEU
| SparcOpcode::BCC
| SparcOpcode::BCS
| SparcOpcode::BPOS
| SparcOpcode::BNEG
| SparcOpcode::BVC
| SparcOpcode::BVS => sparc_format::FMT2_BRANCH,
_ => {
if Self::is_fpu(opcode) {
sparc_format::FMT3_FPU
} else if Self::is_vis(opcode) {
sparc_format::FMT3_VIS
} else if Self::is_memory(opcode) {
match opcode {
SparcOpcode::LD
| SparcOpcode::LDUB
| SparcOpcode::LDUH
| SparcOpcode::LDSB
| SparcOpcode::LDSH
| SparcOpcode::LDD
| SparcOpcode::LDSTUB
| SparcOpcode::LDSW
| SparcOpcode::LDX
| SparcOpcode::LDF
| SparcOpcode::LDDF => sparc_format::FMT3_LOAD,
_ => sparc_format::FMT3_STORE,
}
} else {
sparc_format::FMT3_ALU
}
}
}
}
pub fn all_opcodes() -> Vec<SparcOpcode> {
vec![
SparcOpcode::ADD,
SparcOpcode::ADDcc,
SparcOpcode::ADDC,
SparcOpcode::ADDCcc,
SparcOpcode::SUB,
SparcOpcode::SUBcc,
SparcOpcode::SUBC,
SparcOpcode::SUBCcc,
SparcOpcode::MULX,
SparcOpcode::SDIVX,
SparcOpcode::UDIVX,
SparcOpcode::SMUL,
SparcOpcode::UMUL,
SparcOpcode::SDIV,
SparcOpcode::UDIV,
SparcOpcode::TADDcc,
SparcOpcode::TSUBcc,
SparcOpcode::TADDCCTV,
SparcOpcode::TSUBCCTV,
SparcOpcode::AND,
SparcOpcode::ANDcc,
SparcOpcode::ANDN,
SparcOpcode::ANDNcc,
SparcOpcode::OR,
SparcOpcode::ORcc,
SparcOpcode::ORN,
SparcOpcode::ORNcc,
SparcOpcode::XOR,
SparcOpcode::XORcc,
SparcOpcode::XNOR,
SparcOpcode::XNORcc,
SparcOpcode::SLL,
SparcOpcode::SRL,
SparcOpcode::SRA,
SparcOpcode::SLLX,
SparcOpcode::SRLX,
SparcOpcode::SRAX,
SparcOpcode::LD,
SparcOpcode::LDUB,
SparcOpcode::LDUH,
SparcOpcode::LDSB,
SparcOpcode::LDSH,
SparcOpcode::LDD,
SparcOpcode::LDSTUB,
SparcOpcode::LDSW,
SparcOpcode::LDX,
SparcOpcode::ST,
SparcOpcode::STB,
SparcOpcode::STH,
SparcOpcode::STD,
SparcOpcode::STX,
]
}
pub fn opcode_count() -> usize {
58
}
}
pub mod sparc_regs {
pub const G0: u32 = 0;
pub const G1: u32 = 1;
pub const G2: u32 = 2;
pub const G3: u32 = 3;
pub const G4: u32 = 4;
pub const G5: u32 = 5;
pub const G6: u32 = 6;
pub const G7: u32 = 7;
pub const O0: u32 = 8;
pub const O1: u32 = 9;
pub const O2: u32 = 10;
pub const O3: u32 = 11;
pub const O4: u32 = 12;
pub const O5: u32 = 13;
pub const O6_SP: u32 = 14; pub const O7: u32 = 15;
pub const L0: u32 = 16;
pub const L1: u32 = 17;
pub const L2: u32 = 18;
pub const L3: u32 = 19;
pub const L4: u32 = 20;
pub const L5: u32 = 21;
pub const L6: u32 = 22;
pub const L7: u32 = 23;
pub const I0: u32 = 24;
pub const I1: u32 = 25;
pub const I2: u32 = 26;
pub const I3: u32 = 27;
pub const I4: u32 = 28;
pub const I5: u32 = 29;
pub const I6_FP: u32 = 30; pub const I7: u32 = 31;
pub const SP_REG: u32 = O6_SP;
pub const FP_REG: u32 = I6_FP;
}
pub struct BridgeSPARCRegisterInfo;
impl BridgeSPARCRegisterInfo {
pub fn reg_name(index: u32) -> String {
match index {
0..=7 => format!("%g{}", index),
8..=15 => format!("%o{}", index - 8),
16..=23 => format!("%l{}", index - 16),
24..=31 => format!("%i{}", index - 24),
_ => format!("%r{}", index),
}
}
pub fn abi_name(index: u32) -> &'static str {
match index {
sparc_regs::G0 => "%g0",
sparc_regs::O0 => "%o0",
sparc_regs::O1 => "%o1",
sparc_regs::O6_SP => "%sp",
sparc_regs::I6_FP => "%fp",
sparc_regs::I7 => "%i7",
_ => "",
}
}
pub fn is_callee_saved(index: u32) -> bool {
(24..=31).contains(&index) || (16..=23).contains(&index) || index == sparc_regs::O6_SP
}
pub fn is_caller_saved(index: u32) -> bool {
(8..=13).contains(&index) || index == sparc_regs::O7
}
pub fn arg_regs() -> Vec<u32> {
vec![8, 9, 10, 11, 12, 13]
}
pub fn return_reg() -> u32 {
sparc_regs::O0
}
pub fn stack_pointer_reg() -> u32 {
sparc_regs::SP_REG
}
pub fn frame_pointer_reg() -> u32 {
sparc_regs::FP_REG
}
pub fn return_address_reg() -> u32 {
sparc_regs::O7
}
pub fn zero_reg() -> u32 {
sparc_regs::G0
}
pub fn all_gprs() -> Vec<u32> {
(0..32).collect()
}
pub fn num_gprs() -> usize {
SPARC_GPR_COUNT as usize
}
pub fn num_fprs() -> usize {
SPARC_FPR_COUNT as usize
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WindowState {
Leaf,
Windowed,
Saved,
}
#[derive(Debug, Clone)]
pub struct SPARCStackFrame {
pub frame_size: u32,
pub window_save_area_size: u32,
pub local_area_offset: i32,
pub has_frame_pointer: bool,
pub is_leaf: bool,
pub window_state: WindowState,
pub saved_regs: Vec<(u32, i32)>,
pub has_calls: bool,
pub stack_alignment: u32,
}
impl SPARCStackFrame {
pub fn new(is_sparcv9: bool) -> Self {
let word_size: u32 = if is_sparcv9 { 8 } else { 4 };
Self {
frame_size: 0,
window_save_area_size: 16 * word_size,
local_area_offset: 0,
has_frame_pointer: true,
is_leaf: false,
window_state: WindowState::Leaf,
saved_regs: Vec::new(),
has_calls: false,
stack_alignment: if is_sparcv9 { 16 } else { 8 },
}
}
pub fn compute_frame_size(&mut self, local_size: u32) {
let mut total = local_size + self.window_save_area_size;
if self.is_leaf {
total -= self.window_save_area_size; }
self.frame_size = (total + self.stack_alignment - 1) & !(self.stack_alignment - 1);
}
pub fn emit_prologue(&self) -> Vec<u8> {
if self.is_leaf {
return vec![];
}
let frame_neg = (-(self.frame_size as i32)) as u32 & 0x1FFF;
let instr: u32 = (2 << 30) | (14 << 14) | (0x3C << 19) | (14 << 14) | (1 << 13) | frame_neg;
instr.to_be_bytes().to_vec()
}
pub fn emit_epilogue(&self) -> Vec<u8> {
if self.is_leaf {
let retl: u32 = (2 << 30) | (0x38 << 19) | (15 << 14) | (1 << 13) | 8;
return retl.to_be_bytes().to_vec();
}
let restore: u32 = (2 << 30) | (0x3D << 19);
let ret: u32 = (2 << 30) | (0x38 << 19) | (31 << 14) | (1 << 13) | 8;
let mut buf = Vec::new();
buf.extend_from_slice(&restore.to_be_bytes());
buf.extend_from_slice(&ret.to_be_bytes());
buf
}
}
pub struct BridgeSPARCCallingConvention;
impl BridgeSPARCCallingConvention {
pub fn arg_reg(n: usize) -> Option<u32> {
match n {
0..=5 => Some(sparc_regs::O0 + n as u32),
_ => None,
}
}
pub fn num_arg_regs() -> usize {
6
}
pub fn return_reg() -> u32 {
sparc_regs::O0
}
pub fn fp_return_reg() -> u32 {
0 }
pub fn caller_window_outs() -> Vec<u32> {
(sparc_regs::O0..=sparc_regs::O7).collect()
}
pub fn callee_saved_gprs() -> Vec<u32> {
let mut regs: Vec<u32> = (sparc_regs::I0..=sparc_regs::I7).collect();
regs.extend(sparc_regs::L0..=sparc_regs::L7);
regs
}
pub fn caller_saved_gprs() -> Vec<u32> {
let mut regs: Vec<u32> = vec![
sparc_regs::G1,
sparc_regs::G2,
sparc_regs::G3,
sparc_regs::G4,
];
regs.extend(sparc_regs::O0..=sparc_regs::O5);
regs.push(sparc_regs::O7);
regs
}
pub fn emit_save(frame_size: u32) -> Vec<u8> {
let frame = SPARCStackFrame::new(true);
let mut f = frame;
f.compute_frame_size(frame_size);
f.emit_prologue()
}
pub fn emit_restore() -> Vec<u8> {
let frame = SPARCStackFrame::new(true);
frame.emit_epilogue()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MachineIRInst {
pub opcode: GenericMachineOpcode,
pub dst: Option<VirtReg>,
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: VirtReg) -> 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(VirtReg),
Imm(i64),
FImm(f64),
Block(usize),
Global(String),
FrameIndex(i32),
External(String),
Cond(MachineIRCond),
}
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,
SExt,
ZExt,
Trunc,
Select,
FrameSetup,
FrameDestroy,
SparcSave,
SparcRestore,
SparcSethi,
SparcJmpl,
Phi,
Debug,
Nop,
TargetSpecific(u32),
}
impl GenericMachineOpcode {
pub fn is_terminator(&self) -> bool {
matches!(
self,
GenericMachineOpcode::Br | GenericMachineOpcode::Ret | GenericMachineOpcode::SparcJmpl
)
}
pub fn is_memory(&self) -> bool {
matches!(
self,
GenericMachineOpcode::Load
| GenericMachineOpcode::Store
| GenericMachineOpcode::FrameSetup
| GenericMachineOpcode::FrameDestroy
)
}
pub fn is_commutative(&self) -> bool {
matches!(
self,
GenericMachineOpcode::Add
| GenericMachineOpcode::Mul
| GenericMachineOpcode::And
| GenericMachineOpcode::Or
| GenericMachineOpcode::Xor
)
}
}
#[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<BridgeArch>,
}
#[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),
FitsIn13Bits,
FitsInSethi22,
}
#[derive(Debug, Clone)]
pub struct PatternResult {
pub opcode: GenericMachineOpcode,
pub operand_mapping: Vec<usize>,
pub flags: Option<MachineIRFlags>,
}
pub struct CrossTargetISel {
pub arch: BridgeArch,
pub vreg_map: HashMap<usize, VirtReg>,
pub patterns: Vec<DAGPattern>,
pub legalize_rules: Vec<LegalizeRule>,
next_vreg: usize,
current_func: Option<String>,
}
impl CrossTargetISel {
pub fn new(arch: BridgeArch) -> 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".into(),
pattern: PatternNode::Opcode(GenericMachineOpcode::Add),
result: PatternResult {
opcode: GenericMachineOpcode::Add,
operand_mapping: vec![0, 1],
flags: None,
},
cost: 1,
archs: vec![],
});
self.add_pattern(DAGPattern {
name: "sub".into(),
pattern: PatternNode::Opcode(GenericMachineOpcode::Sub),
result: PatternResult {
opcode: GenericMachineOpcode::Sub,
operand_mapping: vec![0, 1],
flags: None,
},
cost: 1,
archs: vec![],
});
self.add_pattern(DAGPattern {
name: "mul".into(),
pattern: PatternNode::Opcode(GenericMachineOpcode::Mul),
result: PatternResult {
opcode: GenericMachineOpcode::Mul,
operand_mapping: vec![0, 1],
flags: None,
},
cost: 3,
archs: vec![],
});
self.add_pattern(DAGPattern {
name: "and_imm13".into(),
pattern: PatternNode::Sequence(vec![
PatternNode::Opcode(GenericMachineOpcode::And),
PatternNode::Capture {
slot: 0,
node: Box::new(PatternNode::Any),
},
PatternNode::Predicate {
node: Box::new(PatternNode::Capture {
slot: 1,
node: Box::new(PatternNode::Any),
}),
pred: PatternPredicate::FitsIn13Bits,
},
]),
result: PatternResult {
opcode: GenericMachineOpcode::And,
operand_mapping: vec![0, 1],
flags: None,
},
cost: 1,
archs: vec![BridgeArch::Sparc, BridgeArch::Sparcv9],
});
self.add_pattern(DAGPattern {
name: "or_imm13".into(),
pattern: PatternNode::Sequence(vec![
PatternNode::Opcode(GenericMachineOpcode::Or),
PatternNode::Capture {
slot: 0,
node: Box::new(PatternNode::Any),
},
PatternNode::Predicate {
node: Box::new(PatternNode::Capture {
slot: 1,
node: Box::new(PatternNode::Any),
}),
pred: PatternPredicate::FitsIn13Bits,
},
]),
result: PatternResult {
opcode: GenericMachineOpcode::Or,
operand_mapping: vec![0, 1],
flags: None,
},
cost: 1,
archs: vec![BridgeArch::Sparc, BridgeArch::Sparcv9],
});
self.add_pattern(DAGPattern {
name: "xor".into(),
pattern: PatternNode::Opcode(GenericMachineOpcode::Xor),
result: PatternResult {
opcode: GenericMachineOpcode::Xor,
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: "ret".into(),
pattern: PatternNode::Opcode(GenericMachineOpcode::Ret),
result: PatternResult {
opcode: GenericMachineOpcode::Ret,
operand_mapping: vec![0],
flags: Some(MachineIRFlags {
is_terminator: true,
is_return: true,
..Default::default()
}),
},
cost: 1,
archs: vec![],
});
self.add_pattern(DAGPattern {
name: "call".into(),
pattern: PatternNode::Opcode(GenericMachineOpcode::Call),
result: PatternResult {
opcode: GenericMachineOpcode::Call,
operand_mapping: vec![0],
flags: Some(MachineIRFlags {
is_call: true,
has_side_effects: true,
..Default::default()
}),
},
cost: 1,
archs: vec![],
});
self.add_pattern(DAGPattern {
name: "sparc_save".into(),
pattern: PatternNode::Opcode(GenericMachineOpcode::SparcSave),
result: PatternResult {
opcode: GenericMachineOpcode::SparcSave,
operand_mapping: vec![0],
flags: Some(MachineIRFlags {
has_side_effects: true,
..Default::default()
}),
},
cost: 1,
archs: vec![BridgeArch::Sparc, BridgeArch::Sparcv9],
});
self.add_pattern(DAGPattern {
name: "sparc_restore".into(),
pattern: PatternNode::Opcode(GenericMachineOpcode::SparcRestore),
result: PatternResult {
opcode: GenericMachineOpcode::SparcRestore,
operand_mapping: vec![],
flags: Some(MachineIRFlags {
has_side_effects: true,
..Default::default()
}),
},
cost: 1,
archs: vec![BridgeArch::Sparc, BridgeArch::Sparcv9],
});
self.add_pattern(DAGPattern {
name: "sparc_sethi".into(),
pattern: PatternNode::Opcode(GenericMachineOpcode::SparcSethi),
result: PatternResult {
opcode: GenericMachineOpcode::SparcSethi,
operand_mapping: vec![0],
flags: None,
},
cost: 1,
archs: vec![BridgeArch::Sparc, BridgeArch::Sparcv9],
});
self.add_pattern(DAGPattern {
name: "copy".into(),
pattern: PatternNode::Opcode(GenericMachineOpcode::Copy),
result: PatternResult {
opcode: GenericMachineOpcode::Copy,
operand_mapping: vec![0],
flags: None,
},
cost: 0,
archs: vec![],
});
self.add_pattern(DAGPattern {
name: "select".into(),
pattern: PatternNode::Opcode(GenericMachineOpcode::Select),
result: PatternResult {
opcode: GenericMachineOpcode::Select,
operand_mapping: vec![0, 1, 2],
flags: None,
},
cost: 1,
archs: vec![],
});
}
fn init_legalize_rules(&mut self) {
self.legalize_rules.push(LegalizeRule {
name: "promote_i1_to_i8".into(),
from_type: TypeKindRepr::Integer(1),
to_type: TypeKindRepr::Integer(8),
action: LegalizeAction::Promote,
});
self.legalize_rules.push(LegalizeRule {
name: "promote_i8_to_i32".into(),
from_type: TypeKindRepr::Integer(8),
to_type: TypeKindRepr::Integer(32),
action: LegalizeAction::Promote,
});
self.legalize_rules.push(LegalizeRule {
name: "split_i128".into(),
from_type: TypeKindRepr::Integer(128),
to_type: TypeKindRepr::Integer(64),
action: LegalizeAction::Split,
});
}
pub fn add_pattern(&mut self, pattern: DAGPattern) {
self.patterns.push(pattern);
}
pub fn select_instructions(&mut self, mf: &mut MachineFunction) -> Result<(), BridgeError> {
self.current_func = Some(mf.name.clone());
self.vreg_map.clear();
self.assign_vregs(mf)?;
for bb in &mut mf.blocks {
self.lower_basic_block(bb)?;
}
Ok(())
}
fn assign_vregs(&mut self, mf: &MachineFunction) -> Result<(), BridgeError> {
self.next_vreg = 0;
for bb in &mf.blocks {
for inst in &bb.instructions {
if inst.def.is_some() {
self.next_vreg += 1;
self.vreg_map
.insert(self.next_vreg - 1, (self.next_vreg - 1) as VirtReg);
}
}
}
Ok(())
}
fn lower_basic_block(&mut self, bb: &mut MachineBasicBlock) -> Result<(), BridgeError> {
let mut lowered = Vec::new();
for inst in &bb.instructions {
let result = self.lower_instruction(inst)?;
lowered.extend(result);
}
bb.instructions = lowered;
Ok(())
}
pub fn lower_instruction(&self, inst: &MachineInstr) -> Result<Vec<MachineInstr>, BridgeError> {
let opcode = inst.opcode;
match self.lookup_pattern(opcode) {
Some(_) => Ok(vec![inst.clone()]),
None => Err(BridgeError::ISelFailed {
opcode,
reason: format!("no pattern for opcode {:?}", opcode),
}),
}
}
fn lookup_pattern(&self, opcode: u32) -> Option<&DAGPattern> {
self.patterns
.iter()
.find(|p| matches!(&p.pattern, PatternNode::IROpcode(o) if *o as u32 == opcode))
}
pub fn is_legal(&self, _opcode: GenericMachineOpcode, _ty: &TypeKindRepr) -> bool {
true
}
pub fn get_legalized_type(&self, ty: &TypeKindRepr) -> TypeKindRepr {
for rule in &self.legalize_rules {
if rule.from_type == *ty {
return rule.to_type.clone();
}
}
ty.clone()
}
pub fn allocate_vreg(&mut self) -> VirtReg {
let vreg = self.next_vreg as VirtReg;
self.next_vreg += 1;
vreg
}
pub fn pattern_count(&self) -> usize {
self.patterns.len()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TypeKindRepr {
Void,
Integer(u32),
Float(u32),
Pointer,
Vector(u32, Box<TypeKindRepr>),
Array(u32, Box<TypeKindRepr>),
Struct(Vec<TypeKindRepr>),
}
impl TypeKindRepr {
pub fn size_bits(&self) -> u32 {
match self {
TypeKindRepr::Void => 0,
TypeKindRepr::Integer(bits) | TypeKindRepr::Float(bits) => *bits,
TypeKindRepr::Pointer => 64,
TypeKindRepr::Vector(n, elem) => n * elem.size_bits(),
TypeKindRepr::Array(n, elem) => n * elem.size_bits(),
TypeKindRepr::Struct(fields) => fields.iter().map(|f| f.size_bits()).sum(),
}
}
pub fn is_integer(&self) -> bool {
matches!(self, TypeKindRepr::Integer(_))
}
pub fn is_float(&self) -> bool {
matches!(self, TypeKindRepr::Float(_))
}
}
#[derive(Debug, Clone)]
pub struct LegalizeRule {
pub name: String,
pub from_type: TypeKindRepr,
pub to_type: TypeKindRepr,
pub action: LegalizeAction,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LegalizeAction {
Promote,
Split,
Widen,
Narrow,
Expand,
SoftenFloat,
Scalarize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CrossRegClass {
GPR64,
GPR32,
FPR64,
FPR32,
FPR128,
VR256,
VR512,
Flags,
ICC,
FCC,
}
impl CrossRegClass {
pub fn size_bits(&self) -> u32 {
match self {
CrossRegClass::GPR64 => 64,
CrossRegClass::GPR32 => 32,
CrossRegClass::FPR64 => 64,
CrossRegClass::FPR32 => 32,
CrossRegClass::FPR128 => 128,
CrossRegClass::VR256 => 256,
CrossRegClass::VR512 => 512,
CrossRegClass::Flags | CrossRegClass::ICC | CrossRegClass::FCC => 0,
}
}
pub fn reg_count(&self, arch: BridgeArch) -> usize {
match arch {
BridgeArch::Sparc => match self {
CrossRegClass::GPR32 => SPARC_GPR_COUNT as usize,
CrossRegClass::FPR32 | CrossRegClass::FPR64 => SPARC_FPR_COUNT as usize,
_ => 0,
},
BridgeArch::Sparcv9 => match self {
CrossRegClass::GPR64 => SPARC_GPR_COUNT as usize,
CrossRegClass::FPR32 | CrossRegClass::FPR64 | CrossRegClass::FPR128 => {
SPARC_FPR_COUNT as usize
}
_ => 0,
},
BridgeArch::X86_64 => match self {
CrossRegClass::GPR64 | CrossRegClass::GPR32 => 16,
CrossRegClass::FPR128 | CrossRegClass::FPR64 => 16,
CrossRegClass::VR256 => 16,
CrossRegClass::VR512 => 32,
_ => 0,
},
BridgeArch::X86_32 => match self {
CrossRegClass::GPR32 => 8,
CrossRegClass::FPR128 | CrossRegClass::FPR64 => 8,
_ => 0,
},
}
}
pub fn name(&self) -> &'static str {
match self {
CrossRegClass::GPR64 => "GPR64",
CrossRegClass::GPR32 => "GPR32",
CrossRegClass::FPR64 => "FPR64",
CrossRegClass::FPR32 => "FPR32",
CrossRegClass::FPR128 => "FPR128",
CrossRegClass::VR256 => "VR256",
CrossRegClass::VR512 => "VR512",
CrossRegClass::Flags => "FLAGS",
CrossRegClass::ICC => "ICC",
CrossRegClass::FCC => "FCC",
}
}
pub fn to_sparc_reg_class(&self) -> Option<SparcRegClass> {
match self {
CrossRegClass::GPR64 | CrossRegClass::GPR32 => Some(SparcRegClass::GPR),
CrossRegClass::FPR32 => Some(SparcRegClass::FPR32),
CrossRegClass::FPR64 | CrossRegClass::FPR128 => Some(SparcRegClass::FPR64),
CrossRegClass::ICC => Some(SparcRegClass::Special),
_ => None,
}
}
}
impl fmt::Display for CrossRegClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CrossPhysReg {
pub number: u32,
pub class: CrossRegClass,
pub windowed: bool,
}
impl CrossPhysReg {
pub fn new(number: u32, class: CrossRegClass) -> Self {
Self {
number,
class,
windowed: false,
}
}
pub fn windowed(number: u32, class: CrossRegClass) -> Self {
Self {
number,
class,
windowed: true,
}
}
pub fn to_sparc_reg(&self) -> u32 {
self.number
}
pub fn to_x86_reg(&self) -> u32 {
self.number
}
}
impl fmt::Display for CrossPhysReg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.class.name(), self.number)
}
}
#[derive(Debug, Clone)]
pub struct AllocationOrder {
pub class: CrossRegClass,
pub order: Vec<CrossPhysReg>,
pub reserved: HashSet<CrossPhysReg>,
pub callee_saved: HashSet<CrossPhysReg>,
pub caller_saved: HashSet<CrossPhysReg>,
}
impl AllocationOrder {
pub fn new(class: CrossRegClass) -> Self {
Self {
class,
order: Vec::new(),
reserved: HashSet::new(),
callee_saved: HashSet::new(),
caller_saved: HashSet::new(),
}
}
pub fn sparcv9_defaults() -> Vec<Self> {
let mut orders = Vec::new();
let mut gpr64 = Self::new(CrossRegClass::GPR64);
for i in 0..=7u32 {
gpr64.order.push(CrossPhysReg::new(i, CrossRegClass::GPR64));
gpr64
.caller_saved
.insert(CrossPhysReg::new(i, CrossRegClass::GPR64));
}
for i in 8..=15u32 {
gpr64
.order
.push(CrossPhysReg::windowed(i, CrossRegClass::GPR64));
gpr64
.caller_saved
.insert(CrossPhysReg::windowed(i, CrossRegClass::GPR64));
}
for i in 16..=23u32 {
gpr64
.order
.push(CrossPhysReg::windowed(i, CrossRegClass::GPR64));
gpr64
.callee_saved
.insert(CrossPhysReg::windowed(i, CrossRegClass::GPR64));
}
for i in 24..=31u32 {
gpr64
.order
.push(CrossPhysReg::windowed(i, CrossRegClass::GPR64));
gpr64
.callee_saved
.insert(CrossPhysReg::windowed(i, CrossRegClass::GPR64));
}
gpr64
.reserved
.insert(CrossPhysReg::new(0, CrossRegClass::GPR64));
gpr64
.reserved
.insert(CrossPhysReg::windowed(14, CrossRegClass::GPR64));
orders.push(gpr64);
let mut fpr64 = Self::new(CrossRegClass::FPR64);
for i in 0..=31u32 {
fpr64.order.push(CrossPhysReg::new(i, CrossRegClass::FPR64));
fpr64
.caller_saved
.insert(CrossPhysReg::new(i, CrossRegClass::FPR64));
}
orders.push(fpr64);
orders
}
pub fn x86_64_defaults() -> Vec<Self> {
let mut orders = Vec::new();
let mut gpr64 = Self::new(CrossRegClass::GPR64);
for i in [0, 2, 1, 6, 7, 8, 9, 10, 11] {
gpr64
.order
.push(CrossPhysReg::new(i as u32, CrossRegClass::GPR64));
gpr64
.caller_saved
.insert(CrossPhysReg::new(i as u32, CrossRegClass::GPR64));
}
for i in [3, 12, 13, 14, 15, 5] {
gpr64
.order
.push(CrossPhysReg::new(i as u32, CrossRegClass::GPR64));
gpr64
.callee_saved
.insert(CrossPhysReg::new(i as u32, CrossRegClass::GPR64));
}
gpr64
.reserved
.insert(CrossPhysReg::new(4, CrossRegClass::GPR64));
orders.push(gpr64);
let mut fpr128 = Self::new(CrossRegClass::FPR128);
for i in 0..=15 {
fpr128
.order
.push(CrossPhysReg::new(i, CrossRegClass::FPR128));
fpr128
.caller_saved
.insert(CrossPhysReg::new(i, CrossRegClass::FPR128));
}
orders.push(fpr128);
orders
}
pub fn for_arch(arch: BridgeArch) -> Vec<Self> {
match arch {
BridgeArch::Sparc | BridgeArch::Sparcv9 => Self::sparcv9_defaults(),
BridgeArch::X86_64 | BridgeArch::X86_32 => Self::x86_64_defaults(),
}
}
}
#[derive(Debug, Clone)]
pub struct LiveInterval {
pub vreg: VirtReg,
pub class: CrossRegClass,
pub ranges: Vec<(usize, usize)>,
pub assigned: Option<CrossPhysReg>,
pub spilled: bool,
pub spill_slot: Option<i32>,
pub priority: f64,
}
impl LiveInterval {
pub fn new(vreg: VirtReg, class: CrossRegClass) -> Self {
Self {
vreg,
class,
ranges: Vec::new(),
assigned: None,
spilled: false,
spill_slot: None,
priority: 0.0,
}
}
pub fn add_range(&mut self, start: usize, end: usize) {
let mut merged = false;
for range in &mut self.ranges {
if start <= range.1 && end >= range.0 {
range.0 = range.0.min(start);
range.1 = range.1.max(end);
merged = true;
break;
}
}
if !merged {
self.ranges.push((start, end));
self.ranges.sort_by_key(|r| r.0);
}
}
pub fn overlaps(&self, other: &LiveInterval) -> bool {
for &(s1, e1) in &self.ranges {
for &(s2, e2) in &other.ranges {
if s1 <= e2 && s2 <= e1 {
return true;
}
}
}
false
}
pub fn compute_priority(&mut self) {
let total_length: usize = self.ranges.iter().map(|(s, e)| e - s).sum();
let range_count = self.ranges.len().max(1) as f64;
self.priority = total_length as f64 / range_count;
}
}
pub struct CrossTargetRegAlloc {
pub arch: BridgeArch,
pub alloc_orders: Vec<AllocationOrder>,
pub intervals: Vec<LiveInterval>,
pub assignments: HashMap<VirtReg, CrossPhysReg>,
pub spill_slots: Vec<SpillSlot>,
pub windows_active: bool,
pub window_depth: usize,
next_spill_slot: i32,
}
impl CrossTargetRegAlloc {
pub fn new(arch: BridgeArch) -> Self {
Self {
arch,
alloc_orders: AllocationOrder::for_arch(arch),
intervals: Vec::new(),
assignments: HashMap::new(),
spill_slots: Vec::new(),
windows_active: arch.is_sparc_family(),
window_depth: 0,
next_spill_slot: 0,
}
}
pub fn allocate_registers(&mut self, mf: &mut MachineFunction) -> Result<(), BridgeError> {
self.compute_live_intervals(mf)?;
for interval in &mut self.intervals {
interval.compute_priority();
}
self.linear_scan_allocate()?;
self.rewrite_instructions(mf)?;
self.insert_spill_code(mf)?;
Ok(())
}
fn compute_live_intervals(&mut self, mf: &MachineFunction) -> Result<(), BridgeError> {
self.intervals.clear();
let mut vregs: HashMap<VirtReg, (usize, usize, CrossRegClass)> = HashMap::new();
let mut inst_idx = 0;
for bb in &mf.blocks {
for inst in &bb.instructions {
let class = self.infer_reg_class(inst.opcode);
if let Some(dst) = &inst.def {
vregs
.entry(*dst)
.and_modify(|e| e.1 = inst_idx)
.or_insert((inst_idx, inst_idx, class));
}
for src in &inst.operands {
if let MachineOperand::Reg(vreg) = src {
vregs
.entry(*vreg)
.and_modify(|e| e.1 = inst_idx)
.or_insert((inst_idx, inst_idx, class));
}
}
inst_idx += 1;
}
}
for (vreg, (start, end, class)) in vregs {
let mut interval = LiveInterval::new(vreg, class);
interval.add_range(start, end);
self.intervals.push(interval);
}
self.intervals
.sort_by_key(|i| i.ranges.first().map(|r| r.0).unwrap_or(0));
Ok(())
}
fn infer_reg_class(&self, _opcode: u32) -> CrossRegClass {
if self.arch.is_64bit() {
CrossRegClass::GPR64
} else {
CrossRegClass::GPR32
}
}
fn linear_scan_allocate(&mut self) -> Result<(), BridgeError> {
self.intervals
.sort_by_key(|i| i.ranges.first().map(|r| r.0).unwrap_or(0));
let mut active: Vec<usize> = Vec::new();
for i in 0..self.intervals.len() {
let current_start = self.intervals[i].ranges.first().map(|r| r.0).unwrap_or(0);
active.retain(|&idx| {
self.intervals[idx]
.ranges
.last()
.map(|r| r.1 >= current_start)
.unwrap_or(false)
});
let class = self.intervals[i].class;
let order = self.alloc_orders.iter().find(|o| o.class == class);
if let Some(order) = order {
let assigned = self.try_allocate(&self.intervals[i], order, &active);
match assigned {
Some(reg) => {
self.intervals[i].assigned = Some(reg);
self.assignments.insert(self.intervals[i].vreg, reg);
}
None => self.spill_interval(i),
}
} else {
self.spill_interval(i);
}
active.push(i);
}
Ok(())
}
fn try_allocate(
&self,
interval: &LiveInterval,
order: &AllocationOrder,
active: &[usize],
) -> Option<CrossPhysReg> {
for reg in &order.order {
if order.reserved.contains(reg) {
continue;
}
let conflict = active.iter().any(|&idx| {
let other = &self.intervals[idx];
if let Some(assigned) = other.assigned {
if assigned == *reg && interval.overlaps(other) {
return true;
}
}
false
});
if !conflict {
return Some(*reg);
}
}
None
}
fn spill_interval(&mut self, idx: usize) {
let slot = SpillSlot {
index: self.next_spill_slot,
size: self.intervals[idx].class.size_bits() / 8,
alignment: (self.intervals[idx].class.size_bits() / 8).max(8),
vreg: self.intervals[idx].vreg,
};
self.next_spill_slot += 1;
self.intervals[idx].spilled = true;
self.intervals[idx].spill_slot = Some(slot.index);
self.spill_slots.push(slot);
}
fn rewrite_instructions(&self, _mf: &mut MachineFunction) -> Result<(), BridgeError> {
Ok(())
}
fn insert_spill_code(&self, _mf: &mut MachineFunction) -> Result<(), BridgeError> {
Ok(())
}
pub fn get_phys_reg(&self, vreg: VirtReg) -> Option<CrossPhysReg> {
self.assignments.get(&vreg).copied()
}
pub fn is_spilled(&self, vreg: VirtReg) -> bool {
self.intervals.iter().any(|i| i.vreg == vreg && i.spilled)
}
pub fn spill_slot_count(&self) -> usize {
self.spill_slots.len()
}
pub fn interval_count(&self) -> usize {
self.intervals.len()
}
pub fn enter_window(&mut self) -> Result<(), BridgeError> {
if self.window_depth >= SPARC_NUM_WINDOWS {
return Err(BridgeError::WindowOverflow {
depth: self.window_depth,
max_windows: SPARC_NUM_WINDOWS,
});
}
self.window_depth += 1;
Ok(())
}
pub fn exit_window(&mut self) -> Result<(), BridgeError> {
if self.window_depth == 0 {
return Err(BridgeError::WindowUnderflow);
}
self.window_depth -= 1;
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct SpillSlot {
pub index: i32,
pub size: u32,
pub alignment: u32,
pub vreg: VirtReg,
}
#[derive(Debug, Clone)]
pub struct CrossStackFrame {
pub frame_size: u32,
pub local_area_offset: i32,
pub has_frame_pointer: bool,
pub has_calls: bool,
pub max_call_frame_size: u32,
pub has_var_sized_objects: bool,
pub stack_alignment: u32,
pub red_zone_size: u32,
pub arch: BridgeArch,
pub window_save_area: u32,
pub is_leaf: bool,
}
impl CrossStackFrame {
pub fn new(arch: BridgeArch) -> Self {
let (stack_alignment, window_save_area) = match arch {
BridgeArch::Sparc => (SPARC_V8_STACK_ALIGNMENT, 64u32),
BridgeArch::Sparcv9 => (SPARC_V9_STACK_ALIGNMENT, 128u32),
BridgeArch::X86_64 => (16, 0),
BridgeArch::X86_32 => (16, 0),
};
Self {
frame_size: 0,
local_area_offset: 0,
has_frame_pointer: true,
has_calls: false,
max_call_frame_size: 0,
has_var_sized_objects: false,
stack_alignment,
red_zone_size: 0,
arch,
window_save_area,
is_leaf: false,
}
}
pub fn align_frame_size(size: u32, alignment: u32) -> u32 {
(size + alignment - 1) & !(alignment - 1)
}
pub fn compute_total_size(&mut self, local_size: u32) {
let mut total = local_size + self.max_call_frame_size;
if !self.is_leaf {
total += self.window_save_area;
}
self.frame_size = Self::align_frame_size(total, self.stack_alignment);
}
}
pub struct CrossTargetFrameLowering {
pub arch: BridgeArch,
pub frame: CrossStackFrame,
}
impl CrossTargetFrameLowering {
pub fn new(arch: BridgeArch) -> Self {
Self {
arch,
frame: CrossStackFrame::new(arch),
}
}
pub fn lower_frame(&mut self, mf: &mut MachineFunction) -> Result<(), BridgeError> {
self.frame.max_call_frame_size = self.compute_max_call_frame(mf);
self.frame.compute_total_size(0);
self.emit_prologue(mf)?;
self.emit_epilogue(mf)?;
Ok(())
}
fn compute_max_call_frame(&self, _mf: &MachineFunction) -> u32 {
match self.arch {
BridgeArch::Sparc => 92, BridgeArch::Sparcv9 => 176, BridgeArch::X86_64 => 32,
BridgeArch::X86_32 => 16,
}
}
fn emit_prologue(&mut self, _mf: &mut MachineFunction) -> Result<(), BridgeError> {
Ok(())
}
fn emit_epilogue(&mut self, _mf: &mut MachineFunction) -> Result<(), BridgeError> {
Ok(())
}
pub fn get_frame(&self) -> &CrossStackFrame {
&self.frame
}
pub fn needs_frame_lowering(&self) -> bool {
self.frame.frame_size > 0 || self.frame.has_calls || self.frame.has_var_sized_objects
}
}
pub struct CrossTargetABI {
pub arch: BridgeArch,
}
impl CrossTargetABI {
pub fn new(arch: BridgeArch) -> Self {
Self { arch }
}
pub fn lower_formal_arguments(&self, _mf: &mut MachineFunction) -> Result<(), BridgeError> {
Ok(())
}
pub fn lower_call(&self, _mf: &mut MachineFunction) -> Result<(), BridgeError> {
Ok(())
}
pub fn lower_return(&self, _mf: &mut MachineFunction) -> Result<(), BridgeError> {
Ok(())
}
}
pub struct SPARCX86CostModel {
pub source_arch: BridgeArch,
pub target_arch: BridgeArch,
pub cost_table: HashMap<GenericMachineOpcode, (u32, u32)>,
}
impl SPARCX86CostModel {
pub fn new(source_arch: BridgeArch, target_arch: BridgeArch) -> Self {
let mut model = Self {
source_arch,
target_arch,
cost_table: HashMap::new(),
};
model.init_cost_table();
model
}
fn init_cost_table(&mut self) {
self.cost_table.insert(GenericMachineOpcode::Add, (1, 1));
self.cost_table.insert(GenericMachineOpcode::Sub, (1, 1));
self.cost_table.insert(GenericMachineOpcode::Mul, (4, 3));
self.cost_table.insert(GenericMachineOpcode::SDiv, (34, 20));
self.cost_table.insert(GenericMachineOpcode::UDiv, (34, 20));
self.cost_table.insert(GenericMachineOpcode::And, (1, 1));
self.cost_table.insert(GenericMachineOpcode::Or, (1, 1));
self.cost_table.insert(GenericMachineOpcode::Xor, (1, 1));
self.cost_table.insert(GenericMachineOpcode::Shl, (1, 1));
self.cost_table.insert(GenericMachineOpcode::LShr, (1, 1));
self.cost_table.insert(GenericMachineOpcode::AShr, (1, 1));
self.cost_table.insert(GenericMachineOpcode::Load, (2, 4));
self.cost_table.insert(GenericMachineOpcode::Store, (1, 1));
self.cost_table.insert(GenericMachineOpcode::Br, (1, 1));
self.cost_table.insert(GenericMachineOpcode::BrCond, (1, 1));
self.cost_table.insert(GenericMachineOpcode::Call, (1, 3));
self.cost_table.insert(GenericMachineOpcode::Ret, (1, 1));
self.cost_table
.insert(GenericMachineOpcode::SparcSave, (1, 0));
self.cost_table
.insert(GenericMachineOpcode::SparcRestore, (1, 0));
self.cost_table
.insert(GenericMachineOpcode::SparcSethi, (1, 0));
}
pub fn estimate(&self, opcode: Opcode, _operand_count: usize) -> CostEstimate {
let generic = self.map_opcode(opcode);
if let Some(&(_, tgt_cost)) = self.cost_table.get(&generic) {
CostEstimate::new(tgt_cost, tgt_cost, tgt_cost * 4)
} else {
CostEstimate::new(1, 1, 4)
}
}
fn map_opcode(&self, _opcode: Opcode) -> GenericMachineOpcode {
GenericMachineOpcode::Add
}
pub fn target_cost(&self, opcode: GenericMachineOpcode) -> u32 {
self.cost_table
.get(&opcode)
.map(|&(_, tgt)| tgt)
.unwrap_or(1)
}
pub fn source_cost(&self, opcode: GenericMachineOpcode) -> u32 {
self.cost_table
.get(&opcode)
.map(|&(src, _)| src)
.unwrap_or(1)
}
}
#[derive(Debug, Clone, Default)]
struct OptStats {
delay_slots_filled: usize,
leaf_routines_optimized: usize,
}
pub struct CrossTargetOptimization {
pub arch: BridgeArch,
pub peephole_patterns: Vec<PeepholePattern>,
pub constant_folding: bool,
pub dead_code_elim: bool,
pub fill_delay_slots: bool,
pub optimize_leaf_routines: bool,
stats: OptStats,
}
impl CrossTargetOptimization {
pub fn new(arch: BridgeArch) -> Self {
Self {
arch,
peephole_patterns: Vec::new(),
constant_folding: true,
dead_code_elim: true,
fill_delay_slots: arch.is_sparc_family(),
optimize_leaf_routines: arch.is_sparc_family(),
stats: OptStats::default(),
}
}
pub fn optimize(&mut self, _mf: &mut MachineFunction) -> Result<(), BridgeError> {
if self.constant_folding {
self.run_constant_folding()?;
}
if self.dead_code_elim {
self.run_dce()?;
}
self.run_peephole()?;
if self.fill_delay_slots {
self.run_delay_slot_filling()?;
}
if self.optimize_leaf_routines {
self.run_leaf_optimization()?;
}
Ok(())
}
pub fn post_ra_optimize(&mut self, _mf: &mut MachineFunction) -> Result<(), BridgeError> {
Ok(())
}
fn run_constant_folding(&self) -> Result<(), BridgeError> {
Ok(())
}
fn run_dce(&self) -> Result<(), BridgeError> {
Ok(())
}
fn run_peephole(&self) -> Result<(), BridgeError> {
Ok(())
}
fn run_delay_slot_filling(&mut self) -> Result<(), BridgeError> {
self.stats.delay_slots_filled = 0; Ok(())
}
fn run_leaf_optimization(&mut self) -> Result<(), BridgeError> {
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct PeepholePattern {
pub name: String,
pub match_seq: Vec<GenericMachineOpcode>,
pub replace_seq: Vec<GenericMachineOpcode>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bridge_new() {
let bridge = SPARCX86Bridge::new(BridgeArch::Sparcv9, BridgeArch::X86_64);
assert_eq!(bridge.source_arch, BridgeArch::Sparcv9);
assert_eq!(bridge.target_arch, BridgeArch::X86_64);
}
#[test]
fn test_bridge_default() {
let bridge = SPARCX86Bridge::default();
assert_eq!(bridge.source_arch, BridgeArch::Sparcv9);
assert_eq!(bridge.target_arch, BridgeArch::X86_64);
}
#[test]
fn test_bridge_from_triples() {
let bridge = SPARCX86Bridge::from_triples("sparcv9", "x86_64");
assert!(bridge.source_arch.is_sparc_family());
assert!(bridge.target_arch.is_x86_family());
}
#[test]
fn test_bridge_describe() {
let bridge = SPARCX86Bridge::default();
let desc = bridge.describe();
assert!(desc.contains("SPARCX86Bridge"));
}
#[test]
fn test_bridge_set_opt_level() {
let mut bridge = SPARCX86Bridge::default();
bridge.set_opt_level(CodeGenOptLevel::Aggressive);
}
#[test]
fn test_bridge_features() {
let mut bridge = SPARCX86Bridge::default();
assert!(!bridge.has_feature(BridgeFeature::VIS1));
bridge.enable_feature(BridgeFeature::VIS1);
assert!(bridge.has_feature(BridgeFeature::VIS1));
}
#[test]
fn test_bridge_has_register_windows() {
let bridge = SPARCX86Bridge::default();
assert!(bridge.has_register_windows());
let bridge2 = SPARCX86Bridge::new(BridgeArch::X86_64, BridgeArch::X86_64);
assert!(!bridge2.has_register_windows());
}
#[test]
fn test_bridge_window_save_area() {
let bridge_v9 = SPARCX86Bridge::new(BridgeArch::Sparcv9, BridgeArch::X86_64);
assert_eq!(bridge_v9.window_save_area_size(), 128);
let bridge_v8 = SPARCX86Bridge::new(BridgeArch::Sparc, BridgeArch::X86_64);
assert_eq!(bridge_v8.window_save_area_size(), 64);
let bridge_x86 = SPARCX86Bridge::new(BridgeArch::X86_64, BridgeArch::X86_64);
assert_eq!(bridge_x86.window_save_area_size(), 0);
}
#[test]
fn test_arch_is_64bit() {
assert!(BridgeArch::X86_64.is_64bit());
assert!(!BridgeArch::X86_32.is_64bit());
assert!(!BridgeArch::Sparc.is_64bit());
assert!(BridgeArch::Sparcv9.is_64bit());
}
#[test]
fn test_arch_is_sparc_family() {
assert!(BridgeArch::Sparc.is_sparc_family());
assert!(BridgeArch::Sparcv9.is_sparc_family());
assert!(!BridgeArch::X86_64.is_sparc_family());
}
#[test]
fn test_arch_is_x86_family() {
assert!(BridgeArch::X86_64.is_x86_family());
assert!(BridgeArch::X86_32.is_x86_family());
assert!(!BridgeArch::Sparc.is_x86_family());
}
#[test]
fn test_arch_pointer_width() {
assert_eq!(BridgeArch::X86_64.pointer_width(), 64);
assert_eq!(BridgeArch::X86_32.pointer_width(), 32);
assert_eq!(BridgeArch::Sparc.pointer_width(), 32);
assert_eq!(BridgeArch::Sparcv9.pointer_width(), 64);
}
#[test]
fn test_arch_data_layout() {
assert!(BridgeArch::Sparc.data_layout().contains("E-m:e"));
assert!(BridgeArch::Sparcv9.data_layout().contains("E-m:e"));
assert!(BridgeArch::X86_64.data_layout().contains("e-m:e"));
}
#[test]
fn test_arch_stack_alignment() {
assert_eq!(BridgeArch::Sparc.stack_alignment(), 8);
assert_eq!(BridgeArch::Sparcv9.stack_alignment(), 16);
assert_eq!(BridgeArch::X86_64.stack_alignment(), 16);
}
#[test]
fn test_arch_is_big_endian() {
assert!(BridgeArch::Sparc.is_big_endian());
assert!(BridgeArch::Sparcv9.is_big_endian());
assert!(!BridgeArch::X86_64.is_big_endian());
}
#[test]
fn test_arch_display() {
assert_eq!(format!("{}", BridgeArch::X86_64), "x86_64");
assert_eq!(format!("{}", BridgeArch::Sparc), "sparc");
assert_eq!(format!("{}", BridgeArch::Sparcv9), "sparcv9");
}
#[test]
fn test_features_enable_disable() {
let mut features = BridgeFeatures::default();
assert!(!features.has(BridgeFeature::VIS1));
features.enable(BridgeFeature::VIS1);
assert!(features.has(BridgeFeature::VIS1));
features.disable(BridgeFeature::VIS1);
assert!(!features.has(BridgeFeature::VIS1));
}
#[test]
fn test_features_enable_sparc_v8() {
let mut features = BridgeFeatures::default();
features.enable_sparc_v8();
assert!(features.has(BridgeFeature::V8));
assert!(features.has(BridgeFeature::FPU));
assert!(features.has(BridgeFeature::HWMUL));
}
#[test]
fn test_features_enable_sparc_v9() {
let mut features = BridgeFeatures::default();
features.enable_sparc_v9();
assert!(features.has(BridgeFeature::V9));
assert!(features.has(BridgeFeature::HWDIV));
assert!(features.has(BridgeFeature::POPC));
assert!(features.has(BridgeFeature::CASA));
}
#[test]
fn test_features_enable_vis() {
let mut features = BridgeFeatures::default();
features.enable_vis1();
assert!(features.has(BridgeFeature::VIS1));
features.enable_vis2();
assert!(features.has(BridgeFeature::VIS2));
features.enable_vis3();
assert!(features.has(BridgeFeature::VIS3));
assert!(features.has(BridgeFeature::FMAF));
features.enable_vis4();
assert!(features.has(BridgeFeature::VIS4));
}
#[test]
fn test_features_enable_x86_avx() {
let mut features = BridgeFeatures::default();
features.enable_x86_avx();
assert!(features.has(BridgeFeature::AVX));
assert!(features.has(BridgeFeature::AVX2));
assert!(features.has(BridgeFeature::FMA));
}
#[test]
fn test_features_to_string() {
let mut features = BridgeFeatures::default();
features.enable(BridgeFeature::V9);
features.enable(BridgeFeature::VIS1);
let s = features.to_feature_string();
assert!(s.contains("+v9"));
assert!(s.contains("+vis"));
}
#[test]
fn test_features_from_string() {
let features = BridgeFeatures::from_string("+v9,+vis,-vis2");
assert!(features.has(BridgeFeature::V9));
assert!(features.has(BridgeFeature::VIS1));
assert!(!features.has(BridgeFeature::VIS2));
}
#[test]
fn test_sparc_instr_mnemonic_count() {
let all = BridgeSPARCInstrInfo::all_opcodes();
assert_eq!(all.len(), 55);
assert_eq!(BridgeSPARCInstrInfo::opcode_count(), 58); }
#[test]
fn test_sparc_instr_is_branch() {
assert!(BridgeSPARCInstrInfo::is_branch(SparcOpcode::BA));
assert!(BridgeSPARCInstrInfo::is_branch(SparcOpcode::BE));
assert!(!BridgeSPARCInstrInfo::is_branch(SparcOpcode::ADD));
}
#[test]
fn test_sparc_instr_is_terminator() {
assert!(BridgeSPARCInstrInfo::is_terminator(SparcOpcode::BA));
assert!(BridgeSPARCInstrInfo::is_terminator(SparcOpcode::RET));
assert!(BridgeSPARCInstrInfo::is_terminator(SparcOpcode::CALL));
}
#[test]
fn test_sparc_instr_is_unconditional() {
assert!(BridgeSPARCInstrInfo::is_unconditional(SparcOpcode::BA));
assert!(BridgeSPARCInstrInfo::is_unconditional(SparcOpcode::CALL));
assert!(!BridgeSPARCInstrInfo::is_unconditional(SparcOpcode::BE));
}
#[test]
fn test_sparc_instr_is_memory() {
assert!(BridgeSPARCInstrInfo::is_memory(SparcOpcode::LD));
assert!(BridgeSPARCInstrInfo::is_memory(SparcOpcode::ST));
assert!(!BridgeSPARCInstrInfo::is_memory(SparcOpcode::ADD));
}
#[test]
fn test_sparc_instr_is_fpu() {
assert!(BridgeSPARCInstrInfo::is_fpu(SparcOpcode::FADDS));
assert!(BridgeSPARCInstrInfo::is_fpu(SparcOpcode::FMULD));
assert!(!BridgeSPARCInstrInfo::is_fpu(SparcOpcode::ADD));
}
#[test]
fn test_sparc_instr_is_vis() {
assert!(BridgeSPARCInstrInfo::is_vis(SparcOpcode::FAND));
assert!(BridgeSPARCInstrInfo::is_vis(SparcOpcode::FPADD));
assert!(!BridgeSPARCInstrInfo::is_vis(SparcOpcode::ADD));
}
#[test]
fn test_sparc_instr_sets_cc() {
assert!(BridgeSPARCInstrInfo::sets_cc(SparcOpcode::ADDcc));
assert!(BridgeSPARCInstrInfo::sets_cc(SparcOpcode::SUBcc));
assert!(!BridgeSPARCInstrInfo::sets_cc(SparcOpcode::ADD));
}
#[test]
fn test_sparc_instr_get_format() {
assert_eq!(
BridgeSPARCInstrInfo::get_format(SparcOpcode::CALL),
sparc_format::FMT1_CALL
);
assert_eq!(
BridgeSPARCInstrInfo::get_format(SparcOpcode::SETHI),
sparc_format::FMT2_SETHI
);
assert_eq!(
BridgeSPARCInstrInfo::get_format(SparcOpcode::BA),
sparc_format::FMT2_BRANCH
);
assert_eq!(
BridgeSPARCInstrInfo::get_format(SparcOpcode::ADD),
sparc_format::FMT3_ALU
);
assert_eq!(
BridgeSPARCInstrInfo::get_format(SparcOpcode::LD),
sparc_format::FMT3_LOAD
);
assert_eq!(
BridgeSPARCInstrInfo::get_format(SparcOpcode::ST),
sparc_format::FMT3_STORE
);
assert_eq!(
BridgeSPARCInstrInfo::get_format(SparcOpcode::FADDS),
sparc_format::FMT3_FPU
);
}
#[test]
fn test_sparc_reg_name() {
assert_eq!(BridgeSPARCRegisterInfo::reg_name(0), "%g0");
assert_eq!(BridgeSPARCRegisterInfo::reg_name(8), "%o0");
assert_eq!(BridgeSPARCRegisterInfo::reg_name(16), "%l0");
assert_eq!(BridgeSPARCRegisterInfo::reg_name(24), "%i0");
}
#[test]
fn test_sparc_reg_abi_name() {
assert_eq!(BridgeSPARCRegisterInfo::abi_name(0), "%g0");
assert_eq!(BridgeSPARCRegisterInfo::abi_name(8), "%o0");
assert_eq!(BridgeSPARCRegisterInfo::abi_name(14), "%sp");
assert_eq!(BridgeSPARCRegisterInfo::abi_name(30), "%fp");
assert_eq!(BridgeSPARCRegisterInfo::abi_name(15), "%i7");
}
#[test]
fn test_sparc_reg_callee_saved() {
assert!(!BridgeSPARCRegisterInfo::is_callee_saved(0));
assert!(BridgeSPARCRegisterInfo::is_callee_saved(16)); assert!(BridgeSPARCRegisterInfo::is_callee_saved(24)); }
#[test]
fn test_sparc_reg_caller_saved() {
assert!(BridgeSPARCRegisterInfo::is_caller_saved(8)); assert!(BridgeSPARCRegisterInfo::is_caller_saved(13)); }
#[test]
fn test_sparc_reg_args() {
assert_eq!(
BridgeSPARCRegisterInfo::arg_regs(),
vec![8, 9, 10, 11, 12, 13]
);
}
#[test]
fn test_sparc_reg_return() {
assert_eq!(BridgeSPARCRegisterInfo::return_reg(), 8); }
#[test]
fn test_sparc_reg_sp() {
assert_eq!(BridgeSPARCRegisterInfo::stack_pointer_reg(), 14); }
#[test]
fn test_sparc_reg_fp() {
assert_eq!(BridgeSPARCRegisterInfo::frame_pointer_reg(), 30); }
#[test]
fn test_sparc_reg_zero() {
assert_eq!(BridgeSPARCRegisterInfo::zero_reg(), 0); }
#[test]
fn test_sparc_reg_all_gprs() {
assert_eq!(BridgeSPARCRegisterInfo::all_gprs().len(), 32);
}
#[test]
fn test_sparc_reg_num_gprs() {
assert_eq!(BridgeSPARCRegisterInfo::num_gprs(), 32);
}
#[test]
fn test_sparc_reg_num_fprs() {
assert_eq!(BridgeSPARCRegisterInfo::num_fprs(), 32);
}
#[test]
fn test_sparc_frame_new_v8() {
let frame = SPARCStackFrame::new(false);
assert_eq!(frame.window_save_area_size, 64); assert_eq!(frame.stack_alignment, 8);
}
#[test]
fn test_sparc_frame_new_v9() {
let frame = SPARCStackFrame::new(true);
assert_eq!(frame.window_save_area_size, 128); assert_eq!(frame.stack_alignment, 16);
}
#[test]
fn test_sparc_frame_compute_size() {
let mut frame = SPARCStackFrame::new(true);
frame.compute_frame_size(0);
assert!(frame.frame_size >= 128);
assert_eq!(frame.frame_size % 16, 0); }
#[test]
fn test_sparc_frame_compute_leaf() {
let mut frame = SPARCStackFrame::new(true);
frame.is_leaf = true;
frame.compute_frame_size(0);
assert_eq!(frame.frame_size, 0); }
#[test]
fn test_sparc_frame_prologue_not_empty() {
let frame = SPARCStackFrame::new(true);
let mut f = frame;
f.compute_frame_size(0);
let prologue = f.emit_prologue();
assert!(!prologue.is_empty());
assert_eq!(prologue.len(), 4); }
#[test]
fn test_sparc_frame_prologue_leaf_empty() {
let mut frame = SPARCStackFrame::new(true);
frame.is_leaf = true;
let prologue = frame.emit_prologue();
assert!(prologue.is_empty());
}
#[test]
fn test_sparc_frame_epilogue_not_empty() {
let frame = SPARCStackFrame::new(true);
let epilogue = frame.emit_epilogue();
assert!(!epilogue.is_empty());
assert_eq!(epilogue.len(), 8); }
#[test]
fn test_sparc_frame_epilogue_leaf() {
let mut frame = SPARCStackFrame::new(true);
frame.is_leaf = true;
let epilogue = frame.emit_epilogue();
assert!(!epilogue.is_empty());
assert_eq!(epilogue.len(), 4); }
#[test]
fn test_sparc_cc_arg_reg() {
assert_eq!(BridgeSPARCCallingConvention::arg_reg(0), Some(8)); assert_eq!(BridgeSPARCCallingConvention::arg_reg(5), Some(13)); assert_eq!(BridgeSPARCCallingConvention::arg_reg(6), None);
}
#[test]
fn test_sparc_cc_num_arg_regs() {
assert_eq!(BridgeSPARCCallingConvention::num_arg_regs(), 6);
}
#[test]
fn test_sparc_cc_return_reg() {
assert_eq!(BridgeSPARCCallingConvention::return_reg(), 8); }
#[test]
fn test_sparc_cc_fp_return_reg() {
assert_eq!(BridgeSPARCCallingConvention::fp_return_reg(), 0); }
#[test]
fn test_sparc_cc_callee_saved_count() {
let regs = BridgeSPARCCallingConvention::callee_saved_gprs();
assert_eq!(regs.len(), 16); }
#[test]
fn test_sparc_cc_caller_saved_count() {
let regs = BridgeSPARCCallingConvention::caller_saved_gprs();
assert_eq!(regs.len(), 11); }
#[test]
fn test_sparc_cc_emit_save() {
let bytes = BridgeSPARCCallingConvention::emit_save(0);
assert!(!bytes.is_empty());
assert!(bytes.len() % 4 == 0); }
#[test]
fn test_sparc_cc_emit_restore() {
let bytes = BridgeSPARCCallingConvention::emit_restore();
assert!(!bytes.is_empty());
assert_eq!(bytes.len(), 8); }
#[test]
fn test_isel_new() {
let isel = CrossTargetISel::new(BridgeArch::Sparcv9);
assert!(isel.pattern_count() > 0);
}
#[test]
fn test_isel_allocate_vreg() {
let mut isel = CrossTargetISel::new(BridgeArch::Sparcv9);
let v1 = isel.allocate_vreg();
let v2 = isel.allocate_vreg();
assert!(v1 < v2);
}
#[test]
fn test_isel_is_legal() {
let isel = CrossTargetISel::new(BridgeArch::Sparcv9);
assert!(isel.is_legal(GenericMachineOpcode::Add, &TypeKindRepr::Integer(64)));
}
#[test]
fn test_isel_get_legalized_type() {
let isel = CrossTargetISel::new(BridgeArch::Sparcv9);
let i1 = TypeKindRepr::Integer(1);
let legal = isel.get_legalized_type(&i1);
assert_eq!(legal, TypeKindRepr::Integer(8));
}
#[test]
fn test_reg_class_size_bits() {
assert_eq!(CrossRegClass::GPR64.size_bits(), 64);
assert_eq!(CrossRegClass::GPR32.size_bits(), 32);
assert_eq!(CrossRegClass::FPR64.size_bits(), 64);
}
#[test]
fn test_reg_class_sparc_count() {
assert_eq!(CrossRegClass::GPR64.reg_count(BridgeArch::Sparcv9), 32);
assert_eq!(CrossRegClass::FPR64.reg_count(BridgeArch::Sparcv9), 32);
}
#[test]
fn test_reg_class_x86_count() {
assert_eq!(CrossRegClass::GPR64.reg_count(BridgeArch::X86_64), 16);
}
#[test]
fn test_reg_class_name() {
assert_eq!(CrossRegClass::GPR64.name(), "GPR64");
assert_eq!(CrossRegClass::FPR64.name(), "FPR64");
assert_eq!(CrossRegClass::ICC.name(), "ICC");
}
#[test]
fn test_reg_class_to_sparc_reg_class() {
assert_eq!(
CrossRegClass::GPR64.to_sparc_reg_class(),
Some(SparcRegClass::GPR)
);
assert_eq!(
CrossRegClass::FPR32.to_sparc_reg_class(),
Some(SparcRegClass::FPR32)
);
assert_eq!(
CrossRegClass::ICC.to_sparc_reg_class(),
Some(SparcRegClass::Special)
);
}
#[test]
fn test_phys_reg_new() {
let reg = CrossPhysReg::new(0, CrossRegClass::GPR64);
assert_eq!(reg.number, 0);
assert!(!reg.windowed);
}
#[test]
fn test_phys_reg_windowed() {
let reg = CrossPhysReg::windowed(8, CrossRegClass::GPR64);
assert!(reg.windowed);
assert_eq!(reg.number, 8);
}
#[test]
fn test_phys_reg_to_sparc_reg() {
let reg = CrossPhysReg::new(8, CrossRegClass::GPR64);
assert_eq!(reg.to_sparc_reg(), 8);
}
#[test]
fn test_alloc_order_sparc_v9() {
let orders = AllocationOrder::sparcv9_defaults();
assert_eq!(orders.len(), 2); }
#[test]
fn test_alloc_order_x86() {
let orders = AllocationOrder::x86_64_defaults();
assert_eq!(orders.len(), 2); }
#[test]
fn test_alloc_order_for_arch() {
let sparc_orders = AllocationOrder::for_arch(BridgeArch::Sparcv9);
let x86_orders = AllocationOrder::for_arch(BridgeArch::X86_64);
assert!(!sparc_orders.is_empty());
assert!(!x86_orders.is_empty());
}
#[test]
fn test_live_interval_add_range() {
let mut interval = LiveInterval::new(0, CrossRegClass::GPR64);
interval.add_range(0, 10);
assert_eq!(interval.ranges.len(), 1);
}
#[test]
fn test_live_interval_overlap() {
let mut a = LiveInterval::new(0, CrossRegClass::GPR64);
a.add_range(0, 5);
let mut b = LiveInterval::new(1, CrossRegClass::GPR64);
b.add_range(3, 8);
assert!(a.overlaps(&b));
}
#[test]
fn test_live_interval_no_overlap() {
let mut a = LiveInterval::new(0, CrossRegClass::GPR64);
a.add_range(0, 5);
let mut b = LiveInterval::new(1, CrossRegClass::GPR64);
b.add_range(6, 10);
assert!(!a.overlaps(&b));
}
#[test]
fn test_live_interval_priority() {
let mut interval = LiveInterval::new(0, CrossRegClass::GPR64);
interval.add_range(0, 20);
interval.compute_priority();
assert!(interval.priority > 0.0);
}
#[test]
fn test_reg_alloc_new() {
let ra = CrossTargetRegAlloc::new(BridgeArch::Sparcv9);
assert!(ra.windows_active);
assert_eq!(ra.window_depth, 0);
assert!(ra.alloc_orders.len() > 0);
}
#[test]
fn test_reg_alloc_x86_no_windows() {
let ra = CrossTargetRegAlloc::new(BridgeArch::X86_64);
assert!(!ra.windows_active);
}
#[test]
fn test_reg_alloc_get_phys_reg_none() {
let ra = CrossTargetRegAlloc::new(BridgeArch::Sparcv9);
assert!(ra.get_phys_reg(0).is_none());
}
#[test]
fn test_reg_alloc_enter_exit_window() {
let mut ra = CrossTargetRegAlloc::new(BridgeArch::Sparcv9);
assert!(ra.enter_window().is_ok());
assert_eq!(ra.window_depth, 1);
assert!(ra.exit_window().is_ok());
assert_eq!(ra.window_depth, 0);
}
#[test]
fn test_reg_alloc_window_underflow() {
let mut ra = CrossTargetRegAlloc::new(BridgeArch::Sparcv9);
assert!(ra.exit_window().is_err());
}
#[test]
fn test_frame_new_sparcv9() {
let frame = CrossStackFrame::new(BridgeArch::Sparcv9);
assert_eq!(frame.stack_alignment, 16);
assert_eq!(frame.window_save_area, 128);
}
#[test]
fn test_frame_new_sparc() {
let frame = CrossStackFrame::new(BridgeArch::Sparc);
assert_eq!(frame.stack_alignment, 8);
assert_eq!(frame.window_save_area, 64);
}
#[test]
fn test_frame_compute_total_size_leaf() {
let mut frame = CrossStackFrame::new(BridgeArch::Sparcv9);
frame.is_leaf = true;
frame.compute_total_size(48);
assert_eq!(frame.frame_size, 48); }
#[test]
fn test_frame_compute_total_size_windowed() {
let mut frame = CrossStackFrame::new(BridgeArch::Sparcv9);
frame.is_leaf = false;
frame.compute_total_size(0);
assert_eq!(frame.frame_size, 128); }
#[test]
fn test_frame_align() {
assert_eq!(CrossStackFrame::align_frame_size(10, 16), 16);
assert_eq!(CrossStackFrame::align_frame_size(16, 16), 16);
assert_eq!(CrossStackFrame::align_frame_size(17, 16), 32);
}
#[test]
fn test_bridge_error_display() {
let err = BridgeError::UnsupportedArchPair {
source: BridgeArch::Sparcv9,
target: BridgeArch::X86_32,
};
let msg = format!("{}", err);
assert!(msg.contains("sparcv9"));
assert!(msg.contains("i386"));
}
#[test]
fn test_bridge_error_window_overflow() {
let err = BridgeError::WindowOverflow {
depth: 8,
max_windows: 8,
};
let msg = format!("{}", err);
assert!(msg.contains("window overflow"));
}
#[test]
fn test_bridge_error_window_underflow() {
let err = BridgeError::WindowUnderflow;
let msg = format!("{}", err);
assert!(msg.contains("underflow"));
}
#[test]
fn test_cost_model_new() {
let model = SPARCX86CostModel::new(BridgeArch::Sparcv9, BridgeArch::X86_64);
assert!(!model.cost_table.is_empty());
}
#[test]
fn test_cost_model_target_cost() {
let model = SPARCX86CostModel::new(BridgeArch::Sparcv9, BridgeArch::X86_64);
let cost = model.target_cost(GenericMachineOpcode::Add);
assert!(cost > 0);
}
#[test]
fn test_cost_model_source_cost() {
let model = SPARCX86CostModel::new(BridgeArch::Sparcv9, BridgeArch::X86_64);
let cost = model.source_cost(GenericMachineOpcode::Add);
assert!(cost > 0);
}
#[test]
fn test_cost_model_sparc_specific() {
let model = SPARCX86CostModel::new(BridgeArch::Sparcv9, BridgeArch::X86_64);
assert_eq!(model.target_cost(GenericMachineOpcode::SparcSave), 0);
assert_eq!(model.source_cost(GenericMachineOpcode::SparcSave), 1);
}
#[test]
fn test_optimizer_sparc_features() {
let opt = CrossTargetOptimization::new(BridgeArch::Sparcv9);
assert!(opt.fill_delay_slots);
assert!(opt.optimize_leaf_routines);
}
#[test]
fn test_optimizer_x86_no_sparc_features() {
let opt = CrossTargetOptimization::new(BridgeArch::X86_64);
assert!(!opt.fill_delay_slots);
assert!(!opt.optimize_leaf_routines);
}
#[test]
fn test_sparc_constants() {
assert_eq!(SPARC_GPR_COUNT, 32);
assert_eq!(SPARC_FPR_COUNT, 32);
assert_eq!(SPARC_INSTR_SIZE, 4);
}
#[test]
fn test_sparc_imm_constants() {
assert_eq!(SPARC_MAX_IMM, 4095);
assert_eq!(SPARC_MIN_IMM, -4096);
}
#[test]
fn test_sparc_branch_disp() {
assert!(SPARC_MAX_BRANCH_DISP > 0);
assert!(SPARC_MAX_CALL_DISP > SPARC_MAX_BRANCH_DISP);
}
#[test]
fn test_sparc_window_constants() {
assert_eq!(SPARC_REGS_PER_WINDOW, 24);
assert!(SPARC_NUM_WINDOWS >= 2);
assert!(SPARC_WINDOW_SAVE_AREA > 0);
}
#[test]
fn test_machine_ir_inst_new() {
let inst = MachineIRInst::new(GenericMachineOpcode::Add);
assert!(inst.dst.is_none());
assert!(inst.srcs.is_empty());
}
#[test]
fn test_machine_ir_inst_with_dst() {
let inst = MachineIRInst::new(GenericMachineOpcode::Add).with_dst(42);
assert_eq!(inst.dst, Some(42));
}
#[test]
fn test_generic_opcode_is_terminator() {
assert!(GenericMachineOpcode::Br.is_terminator());
assert!(GenericMachineOpcode::Ret.is_terminator());
assert!(GenericMachineOpcode::SparcJmpl.is_terminator());
assert!(!GenericMachineOpcode::Add.is_terminator());
}
#[test]
fn test_generic_opcode_is_commutative() {
assert!(GenericMachineOpcode::Add.is_commutative());
assert!(!GenericMachineOpcode::Sub.is_commutative());
}
#[test]
fn test_type_kind_size_bits() {
assert_eq!(TypeKindRepr::Integer(32).size_bits(), 32);
assert_eq!(TypeKindRepr::Float(64).size_bits(), 64);
}
#[test]
fn test_type_kind_is_integer_float() {
assert!(TypeKindRepr::Integer(64).is_integer());
assert!(!TypeKindRepr::Integer(64).is_float());
assert!(TypeKindRepr::Float(32).is_float());
}
#[test]
fn test_bridge_output() {
let output = BridgeOutput {
instructions_emitted: 100,
basic_blocks: 5,
target_arch: BridgeArch::Sparcv9,
};
assert_eq!(output.instructions_emitted, 100);
}
#[test]
fn test_cost_estimate_zero() {
let cost = CostEstimate::zero();
assert_eq!(cost.latency, 0);
}
#[test]
fn test_cost_estimate_new() {
let cost = CostEstimate::new(5, 2, 16);
assert_eq!(cost.latency, 5);
}
#[test]
fn test_bridge_stats_default() {
let stats = BridgeStats::default();
assert_eq!(stats.functions_processed, 0);
assert_eq!(stats.window_allocations, 0);
assert_eq!(stats.delay_slots_filled, 0);
}
#[test]
fn test_bridge_integration_describe() {
let mut bridge = SPARCX86Bridge::default();
bridge.enable_feature(BridgeFeature::VIS3);
let desc = bridge.describe();
assert!(!desc.is_empty());
}
#[test]
fn test_window_enter_exit_multiple() {
let mut ra = CrossTargetRegAlloc::new(BridgeArch::Sparcv9);
for _ in 0..5 {
assert!(ra.enter_window().is_ok());
}
assert_eq!(ra.window_depth, 5);
for _ in 0..5 {
assert!(ra.exit_window().is_ok());
}
assert_eq!(ra.window_depth, 0);
}
#[test]
fn test_window_overflow_at_limit() {
let mut ra = CrossTargetRegAlloc::new(BridgeArch::Sparcv9);
for _ in 0..SPARC_NUM_WINDOWS {
assert!(ra.enter_window().is_ok());
}
assert!(ra.enter_window().is_err());
}
#[test]
fn test_window_underflow_at_zero() {
let mut ra = CrossTargetRegAlloc::new(BridgeArch::Sparcv9);
assert!(ra.exit_window().is_err());
}
#[test]
fn test_window_state_reset() {
let mut ra = CrossTargetRegAlloc::new(BridgeArch::Sparcv9);
ra.enter_window().unwrap();
ra.enter_window().unwrap();
ra.exit_window().unwrap();
ra.exit_window().unwrap();
assert_eq!(ra.window_depth, 0);
}
#[test]
fn test_all_formats_unique() {
assert_ne!(sparc_format::FMT1_CALL, sparc_format::FMT2_SETHI);
assert_ne!(sparc_format::FMT2_BRANCH, sparc_format::FMT3_ALU);
assert_ne!(sparc_format::FMT3_FPU, sparc_format::FMT3_VIS);
assert_ne!(sparc_format::FMT3_LOAD, sparc_format::FMT3_STORE);
}
#[test]
fn test_branch_ops_have_fmt2_branch() {
for op in &[
SparcOpcode::BA,
SparcOpcode::BE,
SparcOpcode::BNE,
SparcOpcode::BG,
SparcOpcode::BLE,
] {
assert_eq!(
BridgeSPARCInstrInfo::get_format(*op),
sparc_format::FMT2_BRANCH
);
}
}
#[test]
fn test_load_ops_have_fmt3_load() {
for op in &[
SparcOpcode::LD,
SparcOpcode::LDUB,
SparcOpcode::LDSW,
SparcOpcode::LDX,
] {
assert_eq!(
BridgeSPARCInstrInfo::get_format(*op),
sparc_format::FMT3_LOAD
);
}
}
#[test]
fn test_store_ops_have_fmt3_store() {
for op in &[
SparcOpcode::ST,
SparcOpcode::STB,
SparcOpcode::STH,
SparcOpcode::STD,
SparcOpcode::STX,
] {
assert_eq!(
BridgeSPARCInstrInfo::get_format(*op),
sparc_format::FMT3_STORE
);
}
}
#[test]
fn test_sethi_is_fmt2() {
assert_eq!(
BridgeSPARCInstrInfo::get_format(SparcOpcode::SETHI),
sparc_format::FMT2_SETHI
);
}
#[test]
fn test_call_is_fmt1() {
assert_eq!(
BridgeSPARCInstrInfo::get_format(SparcOpcode::CALL),
sparc_format::FMT1_CALL
);
}
#[test]
fn test_sparc_cond_constants() {
assert_eq!(sparc_cond::ALWAYS, 0x8);
assert_eq!(sparc_cond::NEVER, 0x0);
assert_eq!(sparc_cond::EQUAL, 0x1);
assert_eq!(sparc_cond::NOT_EQUAL, 0x9);
assert_ne!(sparc_cond::ALWAYS, sparc_cond::NEVER);
}
#[test]
fn test_sparc_cond_all_unique() {
let conds = [
sparc_cond::ALWAYS,
sparc_cond::NEVER,
sparc_cond::EQUAL,
sparc_cond::NOT_EQUAL,
sparc_cond::GREATER,
sparc_cond::LESS_OR_EQUAL,
sparc_cond::LESS,
sparc_cond::GREATER_OR_EQUAL,
sparc_cond::GREATER_UNSIGNED,
sparc_cond::LESS_OR_EQUAL_UNSIGNED,
sparc_cond::CARRY_CLEAR,
sparc_cond::CARRY_SET,
sparc_cond::POSITIVE,
sparc_cond::NEGATIVE,
sparc_cond::OVERFLOW_CLEAR,
sparc_cond::OVERFLOW_SET,
];
let mut seen = HashSet::new();
for &c in &conds {
assert!(!seen.contains(&c));
seen.insert(c);
}
assert_eq!(seen.len(), 16);
}
#[test]
fn test_sparc_reg_names_all_32() {
for i in 0..32u32 {
let name = BridgeSPARCRegisterInfo::reg_name(i);
assert!(!name.is_empty());
assert!(name.starts_with('%'));
}
}
#[test]
fn test_sparc_reg_names_out_of_bounds() {
assert_eq!(BridgeSPARCRegisterInfo::reg_name(32), "%r32");
assert_eq!(BridgeSPARCRegisterInfo::reg_name(100), "%r100");
}
#[test]
fn test_sparc_abi_names_key_regs() {
assert_eq!(BridgeSPARCRegisterInfo::abi_name(0), "%g0");
assert_eq!(BridgeSPARCRegisterInfo::abi_name(8), "%o0");
assert_eq!(BridgeSPARCRegisterInfo::abi_name(14), "%sp");
assert_eq!(BridgeSPARCRegisterInfo::abi_name(30), "%fp");
assert_eq!(BridgeSPARCRegisterInfo::abi_name(31), "%i7");
}
#[test]
fn test_sparc_abi_name_unknown() {
assert_eq!(BridgeSPARCRegisterInfo::abi_name(5), "");
}
#[test]
fn test_sparc_frame_prologue_encoding() {
let mut f = SPARCStackFrame::new(true);
f.compute_frame_size(0);
let bytes = f.emit_prologue();
assert_eq!(bytes[0] >> 6, 2);
}
#[test]
fn test_sparc_epilogue_has_restore_and_ret() {
let f = SPARCStackFrame::new(true);
let bytes = f.emit_epilogue();
assert_eq!(bytes.len(), 8);
assert_eq!(bytes[0] >> 6, 2);
assert_eq!(bytes[4] >> 6, 2);
}
#[test]
fn test_sparc_retl_format() {
let mut f = SPARCStackFrame::new(true);
f.is_leaf = true;
let bytes = f.emit_epilogue();
assert_eq!(bytes.len(), 4);
assert_eq!(bytes[0] >> 6, 2);
}
#[test]
fn test_sparc_cc_all_args_in_range() {
for n in 0..6 {
let reg = BridgeSPARCCallingConvention::arg_reg(n).unwrap();
assert!(reg >= 8 && reg <= 13);
}
}
#[test]
fn test_sparc_cc_save_encoding() {
let bytes = BridgeSPARCCallingConvention::emit_save(176);
assert!(!bytes.is_empty());
}
#[test]
fn test_window_regs_disjoint() {
for i in 0..7 {
if i != 0 {
assert!(!BridgeSPARCRegisterInfo::is_callee_saved(i));
}
}
}
#[test]
fn test_sparc_g0_is_special() {
assert_eq!(BridgeSPARCRegisterInfo::zero_reg(), 0);
assert_eq!(BridgeSPARCRegisterInfo::abi_name(0), "%g0");
}
#[test]
fn test_isel_has_sparc_patterns() {
let isel = CrossTargetISel::new(BridgeArch::Sparcv9);
let names: Vec<&str> = isel.patterns.iter().map(|p| p.name.as_str()).collect();
assert!(names.contains(&"sparc_save"));
assert!(names.contains(&"sparc_restore"));
assert!(names.contains(&"sparc_sethi"));
}
#[test]
fn test_isel_sparc_patterns_restricted() {
let isel = CrossTargetISel::new(BridgeArch::Sparcv9);
for p in &isel.patterns {
if p.name.starts_with("sparc_") {
assert!(!p.archs.is_empty());
for arch in &p.archs {
assert!(arch.is_sparc_family());
}
}
}
}
#[test]
fn test_alloc_order_g0_reserved() {
let orders = AllocationOrder::sparcv9_defaults();
let gpr = orders
.iter()
.find(|o| o.class == CrossRegClass::GPR64)
.unwrap();
assert!(gpr.reserved.iter().any(|r| r.number == 0));
}
#[test]
fn test_alloc_order_sp_reserved() {
let orders = AllocationOrder::sparcv9_defaults();
let gpr = orders
.iter()
.find(|o| o.class == CrossRegClass::GPR64)
.unwrap();
assert!(gpr.reserved.iter().any(|r| r.number == 14));
}
#[test]
fn test_alloc_order_windowed_marked() {
let orders = AllocationOrder::sparcv9_defaults();
let gpr = orders
.iter()
.find(|o| o.class == CrossRegClass::GPR64)
.unwrap();
for reg in &gpr.order {
if reg.number >= 8 && reg.number <= 31 {
assert!(reg.windowed);
}
}
}
#[test]
fn test_alloc_order_no_duplicates() {
let orders = AllocationOrder::sparcv9_defaults();
for order in &orders {
let mut seen = HashSet::new();
for reg in &order.order {
assert!(!seen.contains(reg));
seen.insert(*reg);
}
}
}
#[test]
fn test_frame_lowering_sparc_windowed() {
let mut fl = CrossTargetFrameLowering::new(BridgeArch::Sparcv9);
fl.frame.is_leaf = false;
fl.frame.compute_total_size(0);
assert_eq!(fl.frame.frame_size, 128);
}
#[test]
fn test_frame_lowering_sparc_leaf() {
let mut fl = CrossTargetFrameLowering::new(BridgeArch::Sparcv9);
fl.frame.is_leaf = true;
fl.frame.compute_total_size(0);
assert_eq!(fl.frame.frame_size, 0);
}
#[test]
fn test_frame_lowering_sparcv8_window() {
let mut fl = CrossTargetFrameLowering::new(BridgeArch::Sparc);
fl.frame.is_leaf = false;
fl.frame.compute_total_size(0);
assert_eq!(fl.frame.frame_size, 64);
}
#[test]
fn test_cost_model_division_expensive() {
let model = SPARCX86CostModel::new(BridgeArch::Sparcv9, BridgeArch::X86_64);
let div = model.source_cost(GenericMachineOpcode::SDiv);
let add = model.source_cost(GenericMachineOpcode::Add);
assert!(div > add);
}
#[test]
fn test_cost_model_save_has_cost() {
let model = SPARCX86CostModel::new(BridgeArch::Sparcv9, BridgeArch::X86_64);
let save_cost = model.source_cost(GenericMachineOpcode::SparcSave);
assert_eq!(save_cost, 1);
}
#[test]
fn test_cost_model_x86_load() {
let model = SPARCX86CostModel::new(BridgeArch::Sparcv9, BridgeArch::X86_64);
let load_cost = model.target_cost(GenericMachineOpcode::Load);
assert!(load_cost > 1);
}
#[test]
fn test_optimizer_delay_slots() {
let opt = CrossTargetOptimization::new(BridgeArch::Sparcv9);
assert!(opt.fill_delay_slots);
assert!(opt.optimize_leaf_routines);
}
#[test]
fn test_optimizer_x86_baseline() {
let opt = CrossTargetOptimization::new(BridgeArch::X86_64);
assert!(opt.constant_folding);
assert!(opt.dead_code_elim);
}
#[test]
fn test_sparc_imm13_range() {
assert_eq!(SPARC_MIN_IMM, -4096);
assert_eq!(SPARC_MAX_IMM, 4095);
assert_eq!((SPARC_MAX_IMM - SPARC_MIN_IMM) as u64, 8191);
}
#[test]
fn test_sparc_call_disp_vs_branch() {
assert!(SPARC_MAX_CALL_DISP > SPARC_MAX_BRANCH_DISP);
}
#[test]
fn test_mir_flags_default() {
let flags = MachineIRFlags::default();
assert!(!flags.may_trap);
assert!(!flags.is_terminator);
assert!(!flags.is_call);
}
#[test]
fn test_mir_flags_branch() {
let flags = MachineIRFlags {
is_terminator: true,
is_branch: true,
..Default::default()
};
assert!(flags.is_terminator);
assert!(flags.is_branch);
}
#[test]
fn test_features_roundtrip_empty() {
let features = BridgeFeatures::default();
let s = features.to_feature_string();
let parsed = BridgeFeatures::from_string(&s);
assert_eq!(features.list_enabled(), parsed.list_enabled());
}
#[test]
fn test_features_roundtrip_vis() {
let mut features = BridgeFeatures::default();
features.enable_vis2();
let s = features.to_feature_string();
let parsed = BridgeFeatures::from_string(&s);
assert!(parsed.has(BridgeFeature::VIS1));
assert!(parsed.has(BridgeFeature::VIS2));
}
#[test]
fn test_features_from_string_negative() {
let features = BridgeFeatures::from_string("+v9,-fpu");
assert!(features.has(BridgeFeature::V9));
assert!(!features.has(BridgeFeature::FPU));
}
#[test]
fn test_features_from_string_empty() {
let features = BridgeFeatures::from_string("");
assert!(features.list_enabled().is_empty());
}
#[test]
fn test_legalize_actions_count() {
let actions = [
LegalizeAction::Promote,
LegalizeAction::Split,
LegalizeAction::Widen,
LegalizeAction::Narrow,
LegalizeAction::Expand,
LegalizeAction::SoftenFloat,
LegalizeAction::Scalarize,
];
assert_eq!(actions.len(), 7);
}
#[test]
fn test_type_kind_vector_size() {
let v4i32 = TypeKindRepr::Vector(4, Box::new(TypeKindRepr::Integer(32)));
assert_eq!(v4i32.size_bits(), 128);
}
#[test]
fn test_type_kind_struct_size() {
let s = TypeKindRepr::Struct(vec![TypeKindRepr::Integer(32), TypeKindRepr::Float(64)]);
assert_eq!(s.size_bits(), 96);
}
#[test]
fn test_type_kind_array_size() {
let a = TypeKindRepr::Array(8, Box::new(TypeKindRepr::Integer(8)));
assert_eq!(a.size_bits(), 64);
}
#[test]
fn test_sparc_v8_vs_v9_layout() {
assert!(SPARC_V8_DATA_LAYOUT.contains("p:32:32"));
assert!(SPARC_V9_DATA_LAYOUT.contains("i64:64"));
}
#[test]
fn test_sparc_all_mnemonics_non_empty() {
for op in BridgeSPARCInstrInfo::all_opcodes() {
let m = BridgeSPARCInstrInfo::get_mnemonic(op);
assert!(!m.is_empty());
}
}
#[test]
fn test_cc_setting_instructions() {
let cc_setters: Vec<SparcOpcode> = BridgeSPARCInstrInfo::all_opcodes()
.into_iter()
.filter(|op| BridgeSPARCInstrInfo::sets_cc(*op))
.collect();
for op in &cc_setters {
let mnemonic = BridgeSPARCInstrInfo::get_mnemonic(*op);
assert!(mnemonic.ends_with("cc") || mnemonic == "subcc");
}
}
#[test]
fn test_bridge_sparc_to_x86() {
let bridge = SPARCX86Bridge::new(BridgeArch::Sparcv9, BridgeArch::X86_64);
assert!(bridge.source_arch.is_sparc_family());
assert!(bridge.target_arch.is_x86_family());
}
#[test]
fn test_bridge_x86_to_sparc() {
let bridge = SPARCX86Bridge::new(BridgeArch::X86_64, BridgeArch::Sparcv9);
assert!(bridge.source_arch.is_x86_family());
assert!(bridge.target_arch.is_sparc_family());
}
#[test]
fn test_bridge_sparc_to_sparc() {
let bridge = SPARCX86Bridge::new(BridgeArch::Sparc, BridgeArch::Sparcv9);
assert!(bridge.source_arch.is_sparc_family());
assert!(bridge.target_arch.is_sparc_family());
}
#[test]
fn test_bridge_32bit_to_sparcv8() {
let bridge = SPARCX86Bridge::new(BridgeArch::X86_32, BridgeArch::Sparc);
assert!(!bridge.source_arch.is_64bit());
assert!(!bridge.target_arch.is_64bit());
}
#[test]
fn test_stats_defaults_zero() {
let stats = BridgeStats::default();
assert_eq!(stats.functions_processed, 0);
assert_eq!(stats.isel_cycles, 0);
assert_eq!(stats.opt_cycles, 0);
assert_eq!(stats.ra_cycles, 0);
assert_eq!(stats.frame_cycles, 0);
}
#[test]
fn test_stats_accumulation() {
let mut stats = BridgeStats::default();
stats.functions_processed += 1;
stats.window_allocations += 3;
stats.delay_slots_filled += 10;
assert_eq!(stats.functions_processed, 1);
assert_eq!(stats.window_allocations, 3);
assert_eq!(stats.delay_slots_filled, 10);
}
#[test]
fn test_mir_operand_vreg() {
let op = MachineIROperand::VReg(10);
assert!(op.is_reg());
assert!(!op.is_imm());
}
#[test]
fn test_mir_operand_imm() {
let op = MachineIROperand::Imm(-42);
assert!(!op.is_reg());
assert!(op.is_imm());
}
#[test]
fn test_mir_operand_fimm() {
let op = MachineIROperand::FImm(3.14);
assert!(!op.is_reg());
assert!(op.is_imm());
}
#[test]
fn test_mir_inst_builder() {
let inst = MachineIRInst::new(GenericMachineOpcode::Add)
.with_dst(1)
.with_src(MachineIROperand::VReg(2))
.with_src(MachineIROperand::Imm(42));
assert_eq!(inst.dst, Some(1));
assert_eq!(inst.srcs.len(), 2);
}
#[test]
fn test_endianness() {
assert!(BridgeArch::Sparc.is_big_endian());
assert!(BridgeArch::Sparcv9.is_big_endian());
assert!(!BridgeArch::X86_64.is_big_endian());
assert!(!BridgeArch::X86_32.is_big_endian());
}
#[test]
fn test_layout_endian_marker() {
assert!(BridgeArch::Sparcv9.data_layout().starts_with('E'));
assert!(BridgeArch::X86_64.data_layout().starts_with('e'));
}
#[test]
fn test_pattern_predicate_variants() {
let preds = [
PatternPredicate::IsPowerOfTwo,
PatternPredicate::FitsInBits(12),
PatternPredicate::FitsIn13Bits,
PatternPredicate::FitsInSethi22,
];
assert_eq!(preds.len(), 4);
}
#[test]
fn test_pattern_node_variants() {
let nodes = vec![
PatternNode::Any,
PatternNode::Opcode(GenericMachineOpcode::Add),
PatternNode::Constant(42),
PatternNode::Immediate { min: 0, max: 100 },
PatternNode::Sequence(vec![]),
PatternNode::Alternative(vec![]),
];
assert_eq!(nodes.len(), 6);
}
#[test]
fn test_parse_arch_sparc() {
let bridge = SPARCX86Bridge::from_triples("sparc", "x86_64");
assert!(bridge.source_arch.is_sparc_family());
}
#[test]
fn test_parse_arch_x86() {
let bridge = SPARCX86Bridge::from_triples("x86_64", "sparcv9");
assert!(bridge.source_arch.is_x86_family());
}
#[test]
fn test_parse_arch_unknown() {
let bridge = SPARCX86Bridge::from_triples("unknown-arch", "x86_64");
assert!(bridge.source_arch.is_sparc_family());
}
#[test]
fn test_error_display_internal() {
let err = BridgeError::Internal("test error".to_string());
let msg = format!("{}", err);
assert!(msg.contains("test error"));
}
#[test]
fn test_error_display_reg_alloc() {
let err = BridgeError::RegAllocFailed {
reason: "out of registers".to_string(),
};
let msg = format!("{}", err);
assert!(msg.contains("out of registers"));
}
#[test]
fn test_error_display_isel() {
let err = BridgeError::ISelFailed {
opcode: 123,
reason: "unsupported".to_string(),
};
let msg = format!("{}", err);
assert!(msg.contains("123"));
}
#[test]
fn test_error_display_frame() {
let err = BridgeError::FrameLoweringFailed {
reason: "stack too large".to_string(),
};
let msg = format!("{}", err);
assert!(msg.contains("stack too large"));
}
#[test]
fn test_error_display_unimplemented() {
let err = BridgeError::Unimplemented {
feature: "VIS4 crypto".to_string(),
};
let msg = format!("{}", err);
assert!(msg.contains("VIS4"));
}
#[test]
fn test_error_partial_eq() {
assert_eq!(
BridgeError::Internal("a".into()),
BridgeError::Internal("a".into())
);
assert_ne!(
BridgeError::Internal("a".into()),
BridgeError::Internal("b".into())
);
}
#[test]
fn test_error_clone() {
let err = BridgeError::Unimplemented {
feature: "test".to_string(),
};
let cloned = err.clone();
assert_eq!(err, cloned);
}
#[test]
fn test_opt_stats_default() {
let stats = OptStats::default();
assert_eq!(stats.delay_slots_filled, 0);
assert_eq!(stats.leaf_routines_optimized, 0);
}
#[test]
fn test_opt_stats_mutable() {
let mut stats = OptStats::default();
stats.delay_slots_filled = 42;
stats.leaf_routines_optimized = 7;
assert_eq!(stats.delay_slots_filled, 42);
assert_eq!(stats.leaf_routines_optimized, 7);
}
#[test]
fn test_asi_feature() {
let mut features = BridgeFeatures::default();
features.enable(BridgeFeature::ASI);
assert!(features.has(BridgeFeature::ASI));
}
#[test]
fn test_leaf_proc_feature() {
let mut features = BridgeFeatures::default();
features.enable(BridgeFeature::LeafProc);
assert!(features.has(BridgeFeature::LeafProc));
}
#[test]
fn test_num_fprs() {
assert_eq!(BridgeSPARCRegisterInfo::num_fprs(), 32);
}
#[test]
fn test_reg_constants() {
assert_eq!(SPARC_GPR_COUNT, 32);
assert_eq!(SPARC_FPR_COUNT, 32);
}
#[test]
fn test_frame_has_fp_true() {
let frame = CrossStackFrame::new(BridgeArch::Sparcv9);
assert!(frame.has_frame_pointer);
}
#[test]
fn test_frame_red_zone_zero() {
let frame = CrossStackFrame::new(BridgeArch::Sparcv9);
assert_eq!(frame.red_zone_size, 0);
}
#[test]
fn test_fl_needs_lowering_calls() {
let mut fl = CrossTargetFrameLowering::new(BridgeArch::Sparcv9);
fl.frame.has_calls = true;
assert!(fl.needs_frame_lowering());
}
#[test]
fn test_fl_needs_lowering_var_sized() {
let mut fl = CrossTargetFrameLowering::new(BridgeArch::Sparcv9);
fl.frame.has_var_sized_objects = true;
assert!(fl.needs_frame_lowering());
}
#[test]
fn test_bridge_output_all_fields() {
let output = BridgeOutput {
instructions_emitted: 500,
basic_blocks: 12,
target_arch: BridgeArch::Sparcv9,
};
assert_eq!(output.instructions_emitted, 500);
assert_eq!(output.basic_blocks, 12);
}
#[test]
fn test_cost_estimate_profitable() {
let cost = CostEstimate::new(1, 1, 4);
assert!(cost.profitable);
assert!(!cost.vectorizable);
}
#[test]
fn test_alloc_order_disjoint_sets() {
let orders = AllocationOrder::sparcv9_defaults();
for order in &orders {
for reg in &order.callee_saved {
assert!(!order.caller_saved.contains(reg));
}
for reg in &order.reserved {
assert!(!order.order.contains(reg));
}
}
}
#[test]
fn test_x86_alloc_order_disjoint() {
let orders = AllocationOrder::x86_64_defaults();
for order in &orders {
for reg in &order.callee_saved {
assert!(!order.caller_saved.contains(reg));
}
for reg in &order.reserved {
assert!(!order.order.contains(reg));
}
}
}
#[test]
fn test_live_interval_multi_range() {
let mut i = LiveInterval::new(0, CrossRegClass::GPR64);
i.add_range(0, 5);
i.add_range(10, 15);
assert_eq!(i.ranges.len(), 2);
}
#[test]
fn test_live_interval_merge_adjacent() {
let mut i = LiveInterval::new(0, CrossRegClass::GPR64);
i.add_range(0, 5);
i.add_range(4, 10);
assert_eq!(i.ranges.len(), 1);
assert_eq!(i.ranges[0], (0, 10));
}
#[test]
fn test_sparc_jmpl_terminator() {
assert!(GenericMachineOpcode::SparcJmpl.is_terminator());
}
#[test]
fn test_sparc_save_not_terminator() {
assert!(!GenericMachineOpcode::SparcSave.is_terminator());
}
#[test]
fn test_cross_phys_reg_display() {
let reg = CrossPhysReg::new(8, CrossRegClass::GPR64);
let s = format!("{}", reg);
assert!(s.contains("GPR64"));
assert!(s.contains("8"));
}
#[test]
fn test_windowed_reg_display() {
let reg = CrossPhysReg::windowed(16, CrossRegClass::GPR64);
assert!(reg.windowed);
}
#[test]
fn test_peephole_pattern_creation() {
let p = PeepholePattern {
name: "nop_slot".into(),
match_seq: vec![GenericMachineOpcode::Nop],
replace_seq: vec![],
};
assert_eq!(p.name, "nop_slot");
}
#[test]
fn test_mir_operand_block() {
let op = MachineIROperand::Block(0);
assert!(!op.is_reg());
assert!(!op.is_imm());
}
#[test]
fn test_max_reg_id() {
assert!(SPARC_MAX_REG_ID > 31);
}
#[test]
fn test_sparc_reg_constants_in_range() {
assert!(SPARC_GPR_BASE >= 0);
assert!(SPARC_FPR_BASE > SPARC_GPR_BASE);
}
#[test]
fn test_abi_names_match_x86() {
assert!(X86_ABI_NAMES.contains(&"sysv"));
assert!(X86_ABI_NAMES.contains(&"win64"));
}
#[test]
fn test_abi_names_match_sparc() {
assert!(SPARC_ABI_NAMES.contains(&"sparc"));
assert!(SPARC_ABI_NAMES.contains(&"sparcv9"));
}
#[test]
fn test_generic_opcode_is_memory_load_store() {
assert!(GenericMachineOpcode::Load.is_memory());
assert!(GenericMachineOpcode::Store.is_memory());
assert!(!GenericMachineOpcode::Add.is_memory());
}
#[test]
fn test_isel_get_legalized_type_split() {
let isel = CrossTargetISel::new(BridgeArch::Sparcv9);
let i128 = TypeKindRepr::Integer(128);
let legal = isel.get_legalized_type(&i128);
assert_eq!(legal, TypeKindRepr::Integer(64));
}
#[test]
fn test_isel_get_legalized_type_promote() {
let isel = CrossTargetISel::new(BridgeArch::Sparcv9);
let i1 = TypeKindRepr::Integer(1);
let legal = isel.get_legalized_type(&i1);
assert_eq!(legal, TypeKindRepr::Integer(8));
}
#[test]
fn test_sparc_return_addr_reg() {
assert_eq!(
BridgeSPARCRegisterInfo::return_address_reg(),
sparc_regs::O7
);
}
#[test]
fn test_sparc_cc_fp_return() {
assert_eq!(BridgeSPARCCallingConvention::fp_return_reg(), 0);
}
#[test]
fn test_sparc_cc_caller_window_outs() {
let outs = BridgeSPARCCallingConvention::caller_window_outs();
assert_eq!(outs.len(), 8);
assert_eq!(outs[0], sparc_regs::O0);
assert_eq!(outs[7], sparc_regs::O7);
}
#[test]
fn test_reg_alloc_x86_no_windows() {
let ra = CrossTargetRegAlloc::new(BridgeArch::X86_64);
assert!(!ra.windows_active);
}
#[test]
fn test_reg_alloc_sparc_has_windows() {
let ra = CrossTargetRegAlloc::new(BridgeArch::Sparcv9);
assert!(ra.windows_active);
}
#[test]
fn test_sparc_bridge_leaf_optimization_flag() {
let mut bridge = SPARCX86Bridge::default();
bridge.enable_feature(BridgeFeature::LeafProc);
assert!(bridge.has_feature(BridgeFeature::LeafProc));
}
#[test]
fn test_bridge_can_set_opt_level_aggressive() {
let mut bridge = SPARCX86Bridge::default();
bridge.set_opt_level(CodeGenOptLevel::Aggressive);
}
#[test]
fn test_bridge_can_set_opt_level_none() {
let mut bridge = SPARCX86Bridge::default();
bridge.set_opt_level(CodeGenOptLevel::None);
}
#[test]
fn test_sparc_ptr_width_32_vs_64() {
assert_eq!(BridgeArch::Sparc.pointer_width(), 32);
assert_eq!(BridgeArch::Sparcv9.pointer_width(), 64);
}
#[test]
fn test_sparc_big_endian_both_v8_v9() {
assert!(BridgeArch::Sparc.is_big_endian());
assert!(BridgeArch::Sparcv9.is_big_endian());
}
#[test]
fn test_sparc_instr_size_constant() {
assert_eq!(SPARC_INSTR_SIZE, 4);
}
#[test]
fn test_sparc_red_zone_zero() {
assert_eq!(SPARC_RED_ZONE_SIZE, 0);
}
#[test]
fn test_sparc_window_constants() {
assert_eq!(SPARC_REGS_PER_WINDOW, 24);
assert_eq!(SPARC_WINDOW_SAVE_AREA, 128);
assert!(SPARC_NUM_WINDOWS >= 2);
}
#[test]
fn test_sparc_max_reg_id_constant() {
assert!(SPARC_MAX_REG_ID > 60);
}
#[test]
fn test_sparc_gpr_fpr_bases() {
assert!(SPARC_FPR_BASE > SPARC_GPR_BASE);
}
#[test]
fn test_features_list_enabled_sorted() {
let mut features = BridgeFeatures::default();
features.enable(BridgeFeature::V9);
features.enable(BridgeFeature::VIS1);
let list = features.list_enabled();
assert_eq!(list.len(), 2);
}
#[test]
fn test_sparc_opcode_count() {
assert!(BridgeSPARCInstrInfo::opcode_count() > 50);
}
#[test]
fn test_sparc_all_opcodes_count() {
let all = BridgeSPARCInstrInfo::all_opcodes();
assert!(all.len() > 50);
}
#[test]
fn test_sparc_alu_format() {
for op in &[
SparcOpcode::ADD,
SparcOpcode::SUB,
SparcOpcode::AND,
SparcOpcode::OR,
SparcOpcode::XOR,
] {
assert_eq!(
BridgeSPARCInstrInfo::get_format(*op),
sparc_format::FMT3_ALU
);
}
}
#[test]
fn test_fpu_format() {
for op in &[SparcOpcode::FADDS, SparcOpcode::FSUBD, SparcOpcode::FMULS] {
assert_eq!(
BridgeSPARCInstrInfo::get_format(*op),
sparc_format::FMT3_FPU
);
}
}
#[test]
fn test_vis_format() {
for op in &[SparcOpcode::FAND, SparcOpcode::FPADD] {
assert_eq!(
BridgeSPARCInstrInfo::get_format(*op),
sparc_format::FMT3_VIS
);
}
}
#[test]
fn test_reg_names_global() {
for i in 0..8 {
let name = BridgeSPARCRegisterInfo::reg_name(i);
assert!(name.contains('g'));
}
}
#[test]
fn test_reg_names_out() {
for i in 8..16 {
let name = BridgeSPARCRegisterInfo::reg_name(i);
assert!(name.contains('o'));
}
}
#[test]
fn test_reg_names_local() {
for i in 16..24 {
let name = BridgeSPARCRegisterInfo::reg_name(i);
assert!(name.contains('l'));
}
}
#[test]
fn test_reg_names_in() {
for i in 24..32 {
let name = BridgeSPARCRegisterInfo::reg_name(i);
assert!(name.contains('i'));
}
}
#[test]
fn test_all_reg_classes_have_name() {
let classes = [
CrossRegClass::GPR64,
CrossRegClass::GPR32,
CrossRegClass::FPR64,
CrossRegClass::FPR32,
CrossRegClass::FPR128,
CrossRegClass::ICC,
CrossRegClass::FCC,
];
for cls in &classes {
assert!(!cls.name().is_empty());
}
}
#[test]
fn test_all_reg_classes_display() {
for cls in &[
CrossRegClass::GPR64,
CrossRegClass::GPR32,
CrossRegClass::FPR64,
CrossRegClass::ICC,
CrossRegClass::FCC,
] {
let s = format!("{}", cls);
assert!(!s.is_empty());
}
}
#[test]
fn test_cross_phys_reg_to_x86() {
let reg = CrossPhysReg::new(5, CrossRegClass::GPR64);
assert_eq!(reg.to_x86_reg(), 5);
}
#[test]
fn test_cross_phys_reg_to_sparc() {
let reg = CrossPhysReg::new(8, CrossRegClass::GPR64);
assert_eq!(reg.to_sparc_reg(), 8);
}
#[test]
fn test_spill_slot_creation() {
let slot = SpillSlot {
index: 0,
size: 8,
alignment: 8,
vreg: 7,
};
assert_eq!(slot.size, 8);
assert_eq!(slot.vreg, 7);
}
#[test]
fn test_legalize_rule_creation() {
let rule = LegalizeRule {
name: "test".into(),
from_type: TypeKindRepr::Integer(1),
to_type: TypeKindRepr::Integer(8),
action: LegalizeAction::Promote,
};
assert_eq!(rule.action, LegalizeAction::Promote);
}
#[test]
fn test_mir_inst_with_flags() {
let flags = MachineIRFlags {
may_trap: true,
..Default::default()
};
let inst = MachineIRInst::new(GenericMachineOpcode::SDiv).with_flags(flags);
assert!(inst.flags.may_trap);
}
#[test]
fn test_pattern_node_sequence() {
let node = PatternNode::Sequence(vec![PatternNode::Any, PatternNode::Any]);
match node {
PatternNode::Sequence(v) => assert_eq!(v.len(), 2),
_ => panic!("expected Sequence"),
}
}
#[test]
fn test_pattern_node_alternative() {
let node =
PatternNode::Alternative(vec![PatternNode::Constant(0), PatternNode::Constant(1)]);
match node {
PatternNode::Alternative(v) => assert_eq!(v.len(), 2),
_ => panic!("expected Alternative"),
}
}
#[test]
fn test_dag_pattern_with_arch_restriction() {
let pattern = DAGPattern {
name: "sparc_only".into(),
pattern: PatternNode::Any,
result: PatternResult {
opcode: GenericMachineOpcode::SparcSave,
operand_mapping: vec![],
flags: None,
},
cost: 1,
archs: vec![BridgeArch::Sparcv9],
};
assert_eq!(pattern.archs.len(), 1);
}
#[test]
fn test_peephole_pattern_create() {
let p = PeepholePattern {
name: "remove_nop".into(),
match_seq: vec![GenericMachineOpcode::Nop],
replace_seq: vec![],
};
assert_eq!(p.match_seq.len(), 1);
}
#[test]
fn test_live_interval_spilled_state() {
let mut interval = LiveInterval::new(3, CrossRegClass::GPR64);
assert!(!interval.spilled);
interval.spilled = true;
assert!(interval.spilled);
}
#[test]
fn test_live_interval_assigned_state() {
let mut interval = LiveInterval::new(3, CrossRegClass::GPR64);
assert!(interval.assigned.is_none());
interval.assigned = Some(CrossPhysReg::new(5, CrossRegClass::GPR64));
assert!(interval.assigned.is_some());
}
#[test]
fn test_reg_alloc_get_phys_reg() {
let ra = CrossTargetRegAlloc::new(BridgeArch::X86_64);
assert!(ra.get_phys_reg(999).is_none());
}
#[test]
fn test_reg_alloc_is_spilled() {
let ra = CrossTargetRegAlloc::new(BridgeArch::X86_64);
assert!(!ra.is_spilled(0));
}
#[test]
fn test_frame_lowering_get_frame() {
let fl = CrossTargetFrameLowering::new(BridgeArch::Sparcv9);
let frame = fl.get_frame();
assert!(frame.has_frame_pointer);
}
#[test]
fn test_abi_lower_formal_args_ok() {
let abi = CrossTargetABI::new(BridgeArch::Sparcv9);
let _ = abi.arch;
}
#[test]
fn test_cost_model_map_opcode_stub() {
let model = SPARCX86CostModel::new(BridgeArch::Sparcv9, BridgeArch::X86_64);
assert_eq!(model.target_cost(GenericMachineOpcode::Add), 1);
}
#[test]
fn test_optimizer_post_ra_ok() {
let mut opt = CrossTargetOptimization::new(BridgeArch::Sparcv9);
}
#[test]
fn test_mir_flags_call() {
let flags = MachineIRFlags {
is_call: true,
has_side_effects: true,
..Default::default()
};
assert!(flags.is_call);
assert!(flags.has_side_effects);
}
#[test]
fn test_mir_flags_return() {
let flags = MachineIRFlags {
is_return: true,
is_terminator: true,
..Default::default()
};
assert!(flags.is_return);
assert!(flags.is_terminator);
}
#[test]
fn test_generic_opcode_count() {
let _ = GenericMachineOpcode::Add;
let _ = GenericMachineOpcode::Sub;
let _ = GenericMachineOpcode::SparcSave;
let _ = GenericMachineOpcode::SparcRestore;
}
#[test]
fn test_type_kind_repr_is_integer() {
assert!(TypeKindRepr::Integer(64).is_integer());
assert!(!TypeKindRepr::Float(32).is_integer());
assert!(!TypeKindRepr::Pointer.is_integer());
}
#[test]
fn test_type_kind_repr_is_float() {
assert!(TypeKindRepr::Float(64).is_float());
assert!(!TypeKindRepr::Integer(32).is_float());
}
#[test]
fn test_cost_estimate_clone_copy() {
let cost = CostEstimate::new(3, 2, 8);
let c2 = cost;
assert_eq!(c2.latency, 3);
}
#[test]
fn test_bridge_output_clone() {
let out = BridgeOutput {
instructions_emitted: 12,
basic_blocks: 3,
target_arch: BridgeArch::Sparcv9,
};
let c = out.clone();
assert_eq!(c.instructions_emitted, 12);
}
#[test]
fn test_sparc_imm_range_fits_13_signed() {
assert_eq!(SPARC_MIN_IMM, -(1 << 12));
assert_eq!(SPARC_MAX_IMM, (1 << 12) - 1);
}
#[test]
fn test_sparc_branch_disp_is_22bit() {
assert_eq!(SPARC_MAX_BRANCH_DISP, (1 << 22) - 1);
}
#[test]
fn test_sparc_call_disp_is_30bit() {
assert_eq!(SPARC_MAX_CALL_DISP, (1 << 30) - 1);
}
#[test]
fn test_reg_class_sparc_no_vr() {
assert_eq!(CrossRegClass::VR256.reg_count(BridgeArch::Sparcv9), 0);
assert_eq!(CrossRegClass::VR512.reg_count(BridgeArch::Sparcv9), 0);
}
#[test]
fn test_reg_class_x86_has_vr() {
assert!(CrossRegClass::VR256.reg_count(BridgeArch::X86_64) > 0);
}
#[test]
fn test_features_to_string_baseline() {
let mut f = BridgeFeatures::default();
f.enable(BridgeFeature::V9);
f.enable(BridgeFeature::FPU);
let s = f.to_feature_string();
assert!(s.contains("+fpu"));
assert!(s.contains("+v9"));
}
#[test]
fn test_alloc_order_for_sparc_has_gpr_and_fpr() {
let orders = AllocationOrder::for_arch(BridgeArch::Sparcv9);
assert!(orders.iter().any(|o| o.class == CrossRegClass::GPR64));
assert!(orders.iter().any(|o| o.class == CrossRegClass::FPR64));
}
#[test]
fn test_frame_align_sparc_v8() {
assert_eq!(CrossStackFrame::align_frame_size(0, 8), 0);
assert_eq!(CrossStackFrame::align_frame_size(1, 8), 8);
assert_eq!(CrossStackFrame::align_frame_size(8, 8), 8);
assert_eq!(CrossStackFrame::align_frame_size(9, 8), 16);
}
#[test]
fn test_frame_align_sparc_v9() {
assert_eq!(CrossStackFrame::align_frame_size(0, 16), 0);
assert_eq!(CrossStackFrame::align_frame_size(1, 16), 16);
assert_eq!(CrossStackFrame::align_frame_size(16, 16), 16);
assert_eq!(CrossStackFrame::align_frame_size(17, 16), 32);
}
#[test]
fn test_fl_needs_lowering_false_default() {
let fl = CrossTargetFrameLowering::new(BridgeArch::Sparcv9);
assert!(!fl.needs_frame_lowering());
}
#[test]
fn test_sparc_frame_compute_with_locals() {
let mut frame = SPARCStackFrame::new(true);
frame.compute_frame_size(64);
assert!(frame.frame_size >= 128 + 64);
assert_eq!(frame.frame_size % 16, 0);
}
#[test]
fn test_sparc_frame_compute_v8_with_locals() {
let mut frame = SPARCStackFrame::new(false); frame.compute_frame_size(32);
assert!(frame.frame_size >= 64 + 32);
assert_eq!(frame.frame_size % 8, 0);
}
#[test]
fn test_cond_codes_all_have_unique_values() {
let mut seen = HashSet::new();
let conds: &[u8] = &[
sparc_cond::ALWAYS,
sparc_cond::NEVER,
sparc_cond::EQUAL,
sparc_cond::NOT_EQUAL,
sparc_cond::GREATER,
sparc_cond::LESS_OR_EQUAL,
sparc_cond::LESS,
sparc_cond::GREATER_OR_EQUAL,
sparc_cond::GREATER_UNSIGNED,
sparc_cond::LESS_OR_EQUAL_UNSIGNED,
sparc_cond::CARRY_CLEAR,
sparc_cond::CARRY_SET,
sparc_cond::POSITIVE,
sparc_cond::NEGATIVE,
sparc_cond::OVERFLOW_CLEAR,
sparc_cond::OVERFLOW_SET,
];
for &c in conds {
seen.insert(c);
}
assert_eq!(seen.len(), conds.len());
}
#[test]
fn test_sparc_reg_sp_constant() {
assert_eq!(sparc_regs::SP_REG, 14);
}
#[test]
fn test_sparc_reg_fp_constant() {
assert_eq!(sparc_regs::FP_REG, 30);
}
#[test]
fn test_bridge_various_arch_pairs() {
let pairs = [
(BridgeArch::Sparcv9, BridgeArch::X86_64),
(BridgeArch::Sparc, BridgeArch::X86_32),
(BridgeArch::X86_64, BridgeArch::Sparcv9),
];
for &(src, tgt) in &pairs {
let bridge = SPARCX86Bridge::new(src, tgt);
assert_eq!(bridge.source_arch, src);
assert_eq!(bridge.target_arch, tgt);
}
}
#[test]
fn test_parse_arch_various_sparc_triples() {
for triple in &["sparc", "sparcv9", "sparc64"] {
let bridge = SPARCX86Bridge::from_triples(triple, "x86_64");
assert!(bridge.source_arch.is_sparc_family());
}
}
#[test]
fn test_parse_arch_various_x86_triples() {
for triple in &["x86_64", "i386"] {
let bridge = SPARCX86Bridge::from_triples("sparcv9", triple);
assert!(bridge.target_arch.is_x86_family());
}
}
#[test]
fn test_mir_operand_cond() {
let op = MachineIROperand::Cond(MachineIRCond::EQ);
assert!(!op.is_reg());
assert!(!op.is_imm());
}
#[test]
fn test_mir_operand_block() {
let op = MachineIROperand::Block(42);
assert!(!op.is_reg());
assert!(!op.is_imm());
}
#[test]
fn test_mir_operand_global() {
let op = MachineIROperand::Global("main".into());
assert!(!op.is_reg());
assert!(!op.is_imm());
}
#[test]
fn test_mir_operand_frame_index() {
let op = MachineIROperand::FrameIndex(-16);
assert!(!op.is_reg());
assert!(!op.is_imm());
}
#[test]
fn test_mir_operand_external() {
let op = MachineIROperand::External("malloc".into());
assert!(!op.is_reg());
assert!(!op.is_imm());
}
#[test]
fn test_isel_legalize_type_noop_for_legal() {
let isel = CrossTargetISel::new(BridgeArch::Sparcv9);
let i64 = TypeKindRepr::Integer(64);
let legal = isel.get_legalized_type(&i64);
assert_eq!(legal, TypeKindRepr::Integer(64));
}
#[test]
fn test_isel_legalize_type_promote_i1() {
let isel = CrossTargetISel::new(BridgeArch::Sparcv9);
let i1 = TypeKindRepr::Integer(1);
let legal = isel.get_legalized_type(&i1);
assert_eq!(legal, TypeKindRepr::Integer(8));
}
#[test]
fn test_isel_legalize_type_promote_i8() {
let isel = CrossTargetISel::new(BridgeArch::Sparcv9);
let i8 = TypeKindRepr::Integer(8);
let legal = isel.get_legalized_type(&i8);
assert_eq!(legal, TypeKindRepr::Integer(32));
}
#[test]
fn test_isel_legalize_type_split_i128() {
let isel = CrossTargetISel::new(BridgeArch::Sparcv9);
let i128 = TypeKindRepr::Integer(128);
let legal = isel.get_legalized_type(&i128);
assert_eq!(legal, TypeKindRepr::Integer(64));
}
#[test]
fn test_isel_add_pattern_check() {
let mut isel = CrossTargetISel::new(BridgeArch::Sparcv9);
let count_before = isel.pattern_count();
isel.add_pattern(DAGPattern {
name: "custom_test".into(),
pattern: PatternNode::Any,
result: PatternResult {
opcode: GenericMachineOpcode::Nop,
operand_mapping: vec![],
flags: None,
},
cost: 99,
archs: vec![],
});
assert_eq!(isel.pattern_count(), count_before + 1);
}
#[test]
fn test_sparc_bridge_feature_count_total() {
let feats = [
BridgeFeature::V8,
BridgeFeature::V9,
BridgeFeature::VIS1,
BridgeFeature::VIS2,
BridgeFeature::VIS3,
BridgeFeature::VIS4,
BridgeFeature::HWMUL,
BridgeFeature::HWDIV,
BridgeFeature::FPU,
BridgeFeature::FMAF,
BridgeFeature::POPC,
BridgeFeature::CASA,
BridgeFeature::ASI,
BridgeFeature::LeafProc,
BridgeFeature::SSE,
BridgeFeature::SSE2,
BridgeFeature::AVX,
BridgeFeature::AVX2,
BridgeFeature::AVX512,
BridgeFeature::FMA,
BridgeFeature::PGO,
];
assert_eq!(feats.len(), 21);
}
#[test]
fn test_sparc_regs_g0_through_g7() {
assert_eq!(sparc_regs::G0, 0);
assert_eq!(sparc_regs::G7, 7);
}
#[test]
fn test_sparc_regs_o0_through_o7() {
assert_eq!(sparc_regs::O0, 8);
assert_eq!(sparc_regs::O7, 15);
}
#[test]
fn test_sparc_regs_l0_through_l7() {
assert_eq!(sparc_regs::L0, 16);
assert_eq!(sparc_regs::L7, 23);
}
#[test]
fn test_sparc_regs_i0_through_i7() {
assert_eq!(sparc_regs::I0, 24);
assert_eq!(sparc_regs::I7, 31);
}
#[test]
fn test_sparc_window_save_area_size_v8() {
let frame = SPARCStackFrame::new(false);
assert_eq!(frame.window_save_area_size, 64);
}
#[test]
fn test_sparc_window_save_area_size_v9() {
let frame = SPARCStackFrame::new(true);
assert_eq!(frame.window_save_area_size, 128);
}
#[test]
fn test_cost_model_estimate_stub() {
let model = SPARCX86CostModel::new(BridgeArch::Sparcv9, BridgeArch::X86_64);
let _cost = CostEstimate::new(1, 1, 4);
}
#[test]
fn test_opt_stats_mutability() {
let mut stats = OptStats::default();
stats.delay_slots_filled += 1;
stats.leaf_routines_optimized += 1;
assert_eq!(stats.delay_slots_filled, 1);
assert_eq!(stats.leaf_routines_optimized, 1);
}
#[test]
fn test_sparc_cc_callee_saved_count() {
let regs = BridgeSPARCCallingConvention::callee_saved_gprs();
assert_eq!(regs.len(), 16);
}
#[test]
fn test_sparc_cc_caller_saved_count() {
let regs = BridgeSPARCCallingConvention::caller_saved_gprs();
assert_eq!(regs.len(), 11);
}
#[test]
fn test_sparc_all_gprs_count_32() {
let regs = BridgeSPARCRegisterInfo::all_gprs();
assert_eq!(regs.len(), 32);
}
#[test]
fn test_sparc_frame_prologue_always_valid() {
for size in &[0, 64, 128, 256, 1024] {
let mut f = SPARCStackFrame::new(true);
f.compute_frame_size(*size);
let bytes = f.emit_prologue();
assert!(!bytes.is_empty());
assert_eq!(bytes.len() % 4, 0);
}
}
#[test]
fn test_sparc_frame_epilogue_always_valid() {
for leaf in &[true, false] {
let mut f = SPARCStackFrame::new(true);
f.is_leaf = *leaf;
let bytes = f.emit_epilogue();
assert!(!bytes.is_empty());
assert_eq!(bytes.len() % 4, 0);
}
}
}