use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::fmt;
use super::arc_instr_info::{ArcInstrDesc, ArcInstrInfo, ArcOpcode};
use crate::x86::x86_calling_convention::{X86CallFrame, X86CallingConvention};
use crate::x86::x86_frame_lowering::{X86FrameInfo, X86FrameLowering};
use crate::x86::x86_instr_info::{X86InstrDesc, X86InstrInfo, X86Opcode};
use crate::x86::x86_register_info::{X86RegisterInfo, X86_32_REG_COUNT, X86_64_REG_COUNT};
use crate::x86::x86_target_machine::X86TargetMachine;
use crate::x86::X86PeepholeOptimizer;
pub struct ARCX86Bridge {
pub arc_instr_info: ArcInstrInfo,
pub x86_instr_info: X86InstrInfo,
pub x86_reg_info: X86RegisterInfo,
pub shared_isel_patterns: SharedISelPatterns,
pub cross_target_rules: CrossTargetLowering,
pub shared_ra_context: SharedRegisterAllocContext,
pub shared_frame_context: SharedFrameLoweringContext,
pub config: BridgeConfig,
pub stats: BridgeStats,
}
#[derive(Debug, Clone)]
pub struct BridgeConfig {
pub enable_shared_isel: bool,
pub enable_cross_target_opt: bool,
pub enable_shared_ra: bool,
pub enable_shared_frame: bool,
pub max_pattern_depth: usize,
pub opt_transfer_threshold: f64,
pub collect_stats: bool,
pub verbose: bool,
pub arc_variant: String,
pub use_delay_slots: bool,
}
impl Default for BridgeConfig {
fn default() -> Self {
Self {
enable_shared_isel: true,
enable_cross_target_opt: true,
enable_shared_ra: true,
enable_shared_frame: true,
max_pattern_depth: 8,
opt_transfer_threshold: 1.5,
collect_stats: true,
verbose: false,
arc_variant: "archs".into(),
use_delay_slots: true,
}
}
}
#[derive(Debug, Default, Clone)]
pub struct BridgeStats {
pub shared_isel_matches: u64,
pub cross_target_opts: u64,
pub shared_ra_decisions: u64,
pub shared_frame_ops: u64,
pub pattern_translations: u64,
pub total_time_us: u64,
pub successful_transfers: u64,
pub failed_transfers: u64,
pub delay_slot_fills: u64,
}
impl fmt::Display for BridgeStats {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "ARC ↔ X86 Bridge Statistics:")?;
writeln!(
f,
" Shared ISel matches: {}",
self.shared_isel_matches
)?;
writeln!(
f,
" Cross-target optimizations: {}",
self.cross_target_opts
)?;
writeln!(
f,
" Shared RA decisions: {}",
self.shared_ra_decisions
)?;
writeln!(f, " Shared frame operations: {}", self.shared_frame_ops)?;
writeln!(
f,
" Pattern translations: {}",
self.pattern_translations
)?;
writeln!(
f,
" Successful transfers: {}",
self.successful_transfers
)?;
writeln!(f, " Failed transfers: {}", self.failed_transfers)?;
writeln!(f, " Delay slot fills: {}", self.delay_slot_fills)?;
write!(f, " Total time (est): {} µs", self.total_time_us)
}
}
impl ARCX86Bridge {
pub fn new() -> Self {
Self {
arc_instr_info: ArcInstrInfo::new(),
x86_instr_info: X86InstrInfo::new(),
x86_reg_info: X86RegisterInfo,
shared_isel_patterns: SharedISelPatterns::new(),
cross_target_rules: CrossTargetLowering::new(),
shared_ra_context: SharedRegisterAllocContext::new(),
shared_frame_context: SharedFrameLoweringContext::new(),
config: BridgeConfig::default(),
stats: BridgeStats::default(),
}
}
pub fn with_config(config: BridgeConfig) -> Self {
let mut bridge = Self::new();
bridge.config = config;
bridge
}
pub fn initialize(&mut self) {
if self.config.verbose {
self.log("Initializing ARC ↔ X86 bridge...");
}
self.shared_isel_patterns.load_patterns();
self.cross_target_rules.load_rules();
self.shared_ra_context.initialize();
self.shared_frame_context.initialize();
if self.config.verbose {
self.log("Bridge initialization complete.");
}
}
pub fn translate_x86_to_arc(&mut self, x86_opcode: &X86Opcode) -> Option<BridgeInstruction> {
self.stats.pattern_translations += 1;
match x86_opcode {
X86Opcode::ADD => Some(BridgeInstruction::DirectMap(ArcOpcode::ADD)),
X86Opcode::SUB => Some(BridgeInstruction::DirectMap(ArcOpcode::SUB)),
X86Opcode::AND => Some(BridgeInstruction::DirectMap(ArcOpcode::AND)),
X86Opcode::OR => Some(BridgeInstruction::DirectMap(ArcOpcode::OR)),
X86Opcode::XOR => Some(BridgeInstruction::DirectMap(ArcOpcode::XOR)),
X86Opcode::NOT => Some(BridgeInstruction::Expanded(vec![
ArcOpcode::XOR, ])),
X86Opcode::NEG => Some(BridgeInstruction::DirectMap(ArcOpcode::NEG)),
X86Opcode::INC => Some(BridgeInstruction::Expanded(vec![ArcOpcode::ADD])),
X86Opcode::DEC => Some(BridgeInstruction::Expanded(vec![ArcOpcode::SUB])),
X86Opcode::CMP => Some(BridgeInstruction::DirectMap(ArcOpcode::CMP)),
X86Opcode::TEST => {
Some(BridgeInstruction::DirectMap(ArcOpcode::AND)) }
X86Opcode::MOV => Some(BridgeInstruction::DirectMap(ArcOpcode::MOV)),
X86Opcode::MOVSX => Some(BridgeInstruction::DirectMap(ArcOpcode::SEXB)),
X86Opcode::MOVZX => Some(BridgeInstruction::DirectMap(ArcOpcode::EXTB)),
X86Opcode::SHL => Some(BridgeInstruction::DirectMap(ArcOpcode::ASL)),
X86Opcode::SHR => Some(BridgeInstruction::DirectMap(ArcOpcode::LSR)),
X86Opcode::SAR => Some(BridgeInstruction::DirectMap(ArcOpcode::ASR)),
X86Opcode::ROL => {
Some(BridgeInstruction::DirectMap(ArcOpcode::ROR)) }
X86Opcode::ROR => Some(BridgeInstruction::DirectMap(ArcOpcode::ROR)),
X86Opcode::JMP => Some(BridgeInstruction::DirectMap(ArcOpcode::B)),
X86Opcode::JE => Some(BridgeInstruction::DirectMap(ArcOpcode::BEQ)),
X86Opcode::JNE => Some(BridgeInstruction::DirectMap(ArcOpcode::BNE)),
X86Opcode::JG | X86Opcode::JA => Some(BridgeInstruction::DirectMap(ArcOpcode::BGT)),
X86Opcode::JGE | X86Opcode::JAE => Some(BridgeInstruction::DirectMap(ArcOpcode::BGE)),
X86Opcode::JL | X86Opcode::JB => Some(BridgeInstruction::DirectMap(ArcOpcode::BLT)),
X86Opcode::JLE | X86Opcode::JBE => Some(BridgeInstruction::DirectMap(ArcOpcode::BLE)),
X86Opcode::CALL => Some(BridgeInstruction::DirectMap(ArcOpcode::BL)),
X86Opcode::RET => {
Some(BridgeInstruction::DirectMap(ArcOpcode::J)) }
X86Opcode::NOP => Some(BridgeInstruction::DirectMap(ArcOpcode::NOP)),
X86Opcode::PUSH => Some(BridgeInstruction::Expanded(vec![
ArcOpcode::ST,
ArcOpcode::SUB,
])),
X86Opcode::POP => Some(BridgeInstruction::Expanded(vec![
ArcOpcode::LD,
ArcOpcode::ADD,
])),
X86Opcode::BSWAP => Some(BridgeInstruction::DirectMap(ArcOpcode::SWAP)),
X86Opcode::MUL => Some(BridgeInstruction::DirectMap(ArcOpcode::MPY)),
_ => {
self.stats.failed_transfers += 1;
None
}
}
}
pub fn translate_arc_to_x86(&mut self, arc_opcode: &ArcOpcode) -> Option<BridgeInstruction> {
self.stats.pattern_translations += 1;
match arc_opcode {
ArcOpcode::ADD | ArcOpcode::ADD1 | ArcOpcode::ADD2 | ArcOpcode::ADD3 => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::ADD))
}
ArcOpcode::SUB | ArcOpcode::SUB1 | ArcOpcode::SUB2 | ArcOpcode::SUB3 => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::SUB))
}
ArcOpcode::AND => Some(BridgeInstruction::DirectMapX86(X86Opcode::AND)),
ArcOpcode::OR => Some(BridgeInstruction::DirectMapX86(X86Opcode::OR)),
ArcOpcode::XOR => Some(BridgeInstruction::DirectMapX86(X86Opcode::XOR)),
ArcOpcode::BIC => Some(BridgeInstruction::ExpandedX86(vec![
X86Opcode::NOT,
X86Opcode::AND,
])),
ArcOpcode::BIC1 => Some(BridgeInstruction::ExpandedX86(vec![
X86Opcode::NOT,
X86Opcode::AND,
])),
ArcOpcode::ASL | ArcOpcode::ASL1 => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::SHL))
}
ArcOpcode::ASR | ArcOpcode::ASR1 => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::SAR))
}
ArcOpcode::LSR | ArcOpcode::LSR1 => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::SHR))
}
ArcOpcode::ROR => Some(BridgeInstruction::DirectMapX86(X86Opcode::ROR)),
ArcOpcode::MOV | ArcOpcode::MOV_S => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::MOV))
}
ArcOpcode::MOVHI => Some(BridgeInstruction::DirectMapX86(X86Opcode::MOV)),
ArcOpcode::CMP => Some(BridgeInstruction::DirectMapX86(X86Opcode::CMP)),
ArcOpcode::RCMP => Some(BridgeInstruction::DirectMapX86(X86Opcode::CMP)),
ArcOpcode::LD
| ArcOpcode::LD_S
| ArcOpcode::LDB
| ArcOpcode::LDB_S
| ArcOpcode::LDW
| ArcOpcode::LDW_S => Some(BridgeInstruction::DirectMapX86(X86Opcode::MOV)),
ArcOpcode::ST
| ArcOpcode::ST_S
| ArcOpcode::STB
| ArcOpcode::STB_S
| ArcOpcode::STW
| ArcOpcode::STW_S => Some(BridgeInstruction::DirectMapX86(X86Opcode::MOV)),
ArcOpcode::B | ArcOpcode::B_S => Some(BridgeInstruction::DirectMapX86(X86Opcode::JMP)),
ArcOpcode::BL | ArcOpcode::BL_S => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::CALL))
}
ArcOpcode::J | ArcOpcode::J_S => Some(BridgeInstruction::DirectMapX86(X86Opcode::JMP)),
ArcOpcode::JL | ArcOpcode::JL_S => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::CALL))
}
ArcOpcode::BR => Some(BridgeInstruction::DirectMapX86(X86Opcode::JMP)),
ArcOpcode::BEQ => Some(BridgeInstruction::DirectMapX86(X86Opcode::JE)),
ArcOpcode::BNE => Some(BridgeInstruction::DirectMapX86(X86Opcode::JNE)),
ArcOpcode::BGT => Some(BridgeInstruction::DirectMapX86(X86Opcode::JG)),
ArcOpcode::BGE => Some(BridgeInstruction::DirectMapX86(X86Opcode::JGE)),
ArcOpcode::BLT => Some(BridgeInstruction::DirectMapX86(X86Opcode::JL)),
ArcOpcode::BLE => Some(BridgeInstruction::DirectMapX86(X86Opcode::JLE)),
ArcOpcode::BHI => Some(BridgeInstruction::DirectMapX86(X86Opcode::JA)),
ArcOpcode::BHS => Some(BridgeInstruction::DirectMapX86(X86Opcode::JAE)),
ArcOpcode::BLO => Some(BridgeInstruction::DirectMapX86(X86Opcode::JB)),
ArcOpcode::BLS => Some(BridgeInstruction::DirectMapX86(X86Opcode::JBE)),
ArcOpcode::SEXB => Some(BridgeInstruction::DirectMapX86(X86Opcode::MOVSX)),
ArcOpcode::SEXW => Some(BridgeInstruction::DirectMapX86(X86Opcode::MOVSX)),
ArcOpcode::EXTB => Some(BridgeInstruction::DirectMapX86(X86Opcode::MOVZX)),
ArcOpcode::EXTW => Some(BridgeInstruction::DirectMapX86(X86Opcode::MOVZX)),
ArcOpcode::ABS => Some(BridgeInstruction::ExpandedX86(vec![
X86Opcode::MOV,
X86Opcode::SAR,
X86Opcode::XOR,
X86Opcode::SUB,
])),
ArcOpcode::NEG => Some(BridgeInstruction::DirectMapX86(X86Opcode::NEG)),
ArcOpcode::MAX => Some(BridgeInstruction::ExpandedX86(vec![
X86Opcode::CMP,
X86Opcode::CMOVG,
])),
ArcOpcode::MIN => Some(BridgeInstruction::ExpandedX86(vec![
X86Opcode::CMP,
X86Opcode::CMOVL,
])),
ArcOpcode::SWAP => Some(BridgeInstruction::DirectMapX86(X86Opcode::BSWAP)),
ArcOpcode::LP => Some(BridgeInstruction::ExpandedX86(vec![
X86Opcode::MOV,
X86Opcode::DEC,
X86Opcode::JNE,
])),
ArcOpcode::FLAG => Some(BridgeInstruction::DirectMapX86(X86Opcode::CMP)),
ArcOpcode::NOP => Some(BridgeInstruction::DirectMapX86(X86Opcode::NOP)),
_ => {
self.stats.failed_transfers += 1;
None
}
}
}
pub fn fill_delay_slot(
&mut self,
branch: ArcOpcode,
before: &[ArcOpcode],
) -> Option<ArcOpcode> {
self.stats.delay_slot_fills += 1;
for &instr in before.iter().rev() {
match instr {
ArcOpcode::MOV
| ArcOpcode::ADD
| ArcOpcode::SUB
| ArcOpcode::AND
| ArcOpcode::OR
| ArcOpcode::XOR
| ArcOpcode::NOP => {
return Some(instr);
}
_ => {}
}
}
Some(ArcOpcode::NOP)
}
pub fn match_shared_isel(&mut self, op_name: &str, is_arc: bool) -> Option<SharedISelMatch> {
if self.config.collect_stats {
self.stats.shared_isel_matches += 1;
}
self.shared_isel_patterns.find(op_name, is_arc)
}
pub fn apply_cross_target_opt(&mut self, ir_sequence: &[&str]) -> Vec<CrossTargetOptResult> {
self.stats.cross_target_opts += 1;
self.cross_target_rules.apply(ir_sequence)
}
pub fn get_shared_ra_context(&self) -> &SharedRegisterAllocContext {
&self.shared_ra_context
}
pub fn get_shared_ra_context_mut(&mut self) -> &mut SharedRegisterAllocContext {
&mut self.shared_ra_context
}
pub fn get_shared_frame_context(&self) -> &SharedFrameLoweringContext {
&self.shared_frame_context
}
pub fn get_shared_frame_context_mut(&mut self) -> &mut SharedFrameLoweringContext {
&mut self.shared_frame_context
}
pub fn reset_stats(&mut self) {
self.stats = BridgeStats::default();
}
fn log(&self, msg: &str) {
if self.config.verbose {
eprintln!("[ARC-X86-Bridge] {}", msg);
}
}
}
impl Default for ARCX86Bridge {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for ARCX86Bridge {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ARCX86Bridge")
.field("config", &self.config)
.field("stats", &self.stats)
.finish()
}
}
#[derive(Debug, Clone)]
pub enum BridgeInstruction {
DirectMap(ArcOpcode),
DirectMapX86(X86Opcode),
Expanded(Vec<ArcOpcode>),
ExpandedX86(Vec<X86Opcode>),
BranchWithDelay {
branch: ArcOpcode,
delay_slot: ArcOpcode,
},
}
pub struct CrossTargetARC {
pub isel_patterns: SharedISelPatterns,
pub lowering_rules: CrossTargetLowering,
pub regalloc_framework: CrossTargetRegisterAlloc,
pub frame_abstractions: CrossTargetFrameLowering,
pub const_materializer: CrossTargetConstMaterializer,
pub branch_analyzer: CrossTargetBranchAnalyzer,
pub delay_slot_scheduler: DelaySlotScheduler,
pub features: ARCFeatureSet,
}
impl CrossTargetARC {
pub fn new() -> Self {
Self {
isel_patterns: SharedISelPatterns::new(),
lowering_rules: CrossTargetLowering::new(),
regalloc_framework: CrossTargetRegisterAlloc::new(),
frame_abstractions: CrossTargetFrameLowering::new(),
const_materializer: CrossTargetConstMaterializer::new(),
branch_analyzer: CrossTargetBranchAnalyzer::new(),
delay_slot_scheduler: DelaySlotScheduler::new(),
features: ARCFeatureSet::default(),
}
}
pub fn with_features(features: ARCFeatureSet) -> Self {
let mut ctx = Self::new();
ctx.features = features;
ctx
}
}
impl Default for CrossTargetARC {
fn default() -> Self {
Self::new()
}
}
pub struct SharedISelPatterns {
patterns: HashMap<String, SharedPattern>,
arc_variants: HashMap<String, Vec<ArcOpcode>>,
x86_variants: HashMap<String, Vec<X86Opcode>>,
categories: HashMap<PatternCategory, Vec<String>>,
}
#[derive(Debug, Clone)]
pub struct SharedPattern {
pub name: String,
pub category: PatternCategory,
pub arc_seq: Vec<ArcOpcode>,
pub x86_seq: Vec<X86Opcode>,
pub arc_cost: u32,
pub x86_cost: u32,
pub is_commutative: bool,
pub min_operands: usize,
pub max_operands: usize,
pub benefits_from_delay_slot: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PatternCategory {
IntegerArithmetic,
IntegerComparison,
Logical,
Shift,
MemoryAccess,
ControlFlow,
BitManipulation,
Extension,
ConstantMaterialization,
ZeroOverheadLoop,
}
impl fmt::Display for PatternCategory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IntegerArithmetic => write!(f, "IntegerArithmetic"),
Self::IntegerComparison => write!(f, "IntegerComparison"),
Self::Logical => write!(f, "Logical"),
Self::Shift => write!(f, "Shift"),
Self::MemoryAccess => write!(f, "MemoryAccess"),
Self::ControlFlow => write!(f, "ControlFlow"),
Self::BitManipulation => write!(f, "BitManipulation"),
Self::Extension => write!(f, "Extension"),
Self::ConstantMaterialization => write!(f, "ConstantMaterialization"),
Self::ZeroOverheadLoop => write!(f, "ZeroOverheadLoop"),
}
}
}
impl SharedISelPatterns {
pub fn new() -> Self {
Self {
patterns: HashMap::new(),
arc_variants: HashMap::new(),
x86_variants: HashMap::new(),
categories: HashMap::new(),
}
}
pub fn load_patterns(&mut self) {
self.load_integer_arithmetic_patterns();
self.load_integer_comparison_patterns();
self.load_logical_patterns();
self.load_shift_patterns();
self.load_memory_access_patterns();
self.load_control_flow_patterns();
self.load_bit_manipulation_patterns();
self.load_extension_patterns();
self.load_constant_patterns();
self.load_zero_overhead_loop_patterns();
}
fn add_pattern(&mut self, pattern: SharedPattern) {
let name = pattern.name.clone();
let cat = pattern.category;
self.patterns.insert(name, pattern.clone());
self.categories
.entry(cat)
.or_insert_with(Vec::new)
.push(pattern.name);
}
fn load_integer_arithmetic_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "add_32bit".into(),
category: PatternCategory::IntegerArithmetic,
arc_seq: vec![ArcOpcode::ADD],
x86_seq: vec![X86Opcode::ADD],
arc_cost: 1,
x86_cost: 1,
is_commutative: true,
min_operands: 2,
max_operands: 3, benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "add_imm_small".into(),
category: PatternCategory::IntegerArithmetic,
arc_seq: vec![ArcOpcode::ADD1],
x86_seq: vec![X86Opcode::ADD],
arc_cost: 1,
x86_cost: 1,
is_commutative: true,
min_operands: 2,
max_operands: 2,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "sub_32bit".into(),
category: PatternCategory::IntegerArithmetic,
arc_seq: vec![ArcOpcode::SUB],
x86_seq: vec![X86Opcode::SUB],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 3,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "negate_32bit".into(),
category: PatternCategory::IntegerArithmetic,
arc_seq: vec![ArcOpcode::NEG],
x86_seq: vec![X86Opcode::NEG],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "absolute_value".into(),
category: PatternCategory::IntegerArithmetic,
arc_seq: vec![ArcOpcode::ABS],
x86_seq: vec![
X86Opcode::MOV,
X86Opcode::SAR,
X86Opcode::XOR,
X86Opcode::SUB,
],
arc_cost: 1,
x86_cost: 4,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "max_value".into(),
category: PatternCategory::IntegerArithmetic,
arc_seq: vec![ArcOpcode::MAX],
x86_seq: vec![X86Opcode::CMP, X86Opcode::CMOVG],
arc_cost: 1,
x86_cost: 2,
is_commutative: false,
min_operands: 2,
max_operands: 2,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "min_value".into(),
category: PatternCategory::IntegerArithmetic,
arc_seq: vec![ArcOpcode::MIN],
x86_seq: vec![X86Opcode::CMP, X86Opcode::CMOVL],
arc_cost: 1,
x86_cost: 2,
is_commutative: false,
min_operands: 2,
max_operands: 2,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "multiply_32bit".into(),
category: PatternCategory::IntegerArithmetic,
arc_seq: vec![ArcOpcode::MPY],
x86_seq: vec![X86Opcode::IMUL],
arc_cost: 2,
x86_cost: 3,
is_commutative: true,
min_operands: 2,
max_operands: 2,
benefits_from_delay_slot: false,
});
}
fn load_integer_comparison_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "compare_32bit".into(),
category: PatternCategory::IntegerComparison,
arc_seq: vec![ArcOpcode::CMP],
x86_seq: vec![X86Opcode::CMP],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "reverse_compare".into(),
category: PatternCategory::IntegerComparison,
arc_seq: vec![ArcOpcode::RCMP],
x86_seq: vec![X86Opcode::CMP],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "set_flag".into(),
category: PatternCategory::IntegerComparison,
arc_seq: vec![ArcOpcode::FLAG],
x86_seq: vec![X86Opcode::CMP],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: false,
});
}
fn load_logical_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "logical_and".into(),
category: PatternCategory::Logical,
arc_seq: vec![ArcOpcode::AND],
x86_seq: vec![X86Opcode::AND],
arc_cost: 1,
x86_cost: 1,
is_commutative: true,
min_operands: 2,
max_operands: 3,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "logical_or".into(),
category: PatternCategory::Logical,
arc_seq: vec![ArcOpcode::OR],
x86_seq: vec![X86Opcode::OR],
arc_cost: 1,
x86_cost: 1,
is_commutative: true,
min_operands: 2,
max_operands: 3,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "logical_xor".into(),
category: PatternCategory::Logical,
arc_seq: vec![ArcOpcode::XOR],
x86_seq: vec![X86Opcode::XOR],
arc_cost: 1,
x86_cost: 1,
is_commutative: true,
min_operands: 2,
max_operands: 3,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "bit_clear".into(),
category: PatternCategory::Logical,
arc_seq: vec![ArcOpcode::BIC],
x86_seq: vec![X86Opcode::NOT, X86Opcode::AND],
arc_cost: 1,
x86_cost: 2,
is_commutative: false,
min_operands: 2,
max_operands: 3,
benefits_from_delay_slot: false,
});
}
fn load_shift_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "shift_left".into(),
category: PatternCategory::Shift,
arc_seq: vec![ArcOpcode::ASL],
x86_seq: vec![X86Opcode::SHL],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 2,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "shift_right_logical".into(),
category: PatternCategory::Shift,
arc_seq: vec![ArcOpcode::LSR],
x86_seq: vec![X86Opcode::SHR],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 2,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "shift_right_arithmetic".into(),
category: PatternCategory::Shift,
arc_seq: vec![ArcOpcode::ASR],
x86_seq: vec![X86Opcode::SAR],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 2,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "rotate_right".into(),
category: PatternCategory::Shift,
arc_seq: vec![ArcOpcode::ROR],
x86_seq: vec![X86Opcode::ROR],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 2,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "variable_shift".into(),
category: PatternCategory::Shift,
arc_seq: vec![ArcOpcode::ASL], x86_seq: vec![X86Opcode::SHL],
arc_cost: 1,
x86_cost: 2, is_commutative: false,
min_operands: 2,
max_operands: 2,
benefits_from_delay_slot: false,
});
}
fn load_memory_access_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "load_word".into(),
category: PatternCategory::MemoryAccess,
arc_seq: vec![ArcOpcode::LD],
x86_seq: vec![X86Opcode::MOV],
arc_cost: 3,
x86_cost: 3,
is_commutative: false,
min_operands: 2,
max_operands: 2,
benefits_from_delay_slot: true,
});
self.add_pattern(SharedPattern {
name: "store_word".into(),
category: PatternCategory::MemoryAccess,
arc_seq: vec![ArcOpcode::ST],
x86_seq: vec![X86Opcode::MOV],
arc_cost: 3,
x86_cost: 3,
is_commutative: false,
min_operands: 2,
max_operands: 2,
benefits_from_delay_slot: true,
});
self.add_pattern(SharedPattern {
name: "load_byte".into(),
category: PatternCategory::MemoryAccess,
arc_seq: vec![ArcOpcode::LDB],
x86_seq: vec![X86Opcode::MOVZX],
arc_cost: 3,
x86_cost: 3,
is_commutative: false,
min_operands: 2,
max_operands: 2,
benefits_from_delay_slot: true,
});
self.add_pattern(SharedPattern {
name: "store_byte".into(),
category: PatternCategory::MemoryAccess,
arc_seq: vec![ArcOpcode::STB],
x86_seq: vec![X86Opcode::MOV],
arc_cost: 3,
x86_cost: 3,
is_commutative: false,
min_operands: 2,
max_operands: 2,
benefits_from_delay_slot: true,
});
self.add_pattern(SharedPattern {
name: "load_halfword".into(),
category: PatternCategory::MemoryAccess,
arc_seq: vec![ArcOpcode::LDW],
x86_seq: vec![X86Opcode::MOVZX],
arc_cost: 3,
x86_cost: 3,
is_commutative: false,
min_operands: 2,
max_operands: 2,
benefits_from_delay_slot: true,
});
self.add_pattern(SharedPattern {
name: "load_short".into(),
category: PatternCategory::MemoryAccess,
arc_seq: vec![ArcOpcode::LD_S],
x86_seq: vec![X86Opcode::MOV],
arc_cost: 3,
x86_cost: 2, is_commutative: false,
min_operands: 2,
max_operands: 2,
benefits_from_delay_slot: true,
});
}
fn load_control_flow_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "unconditional_branch".into(),
category: PatternCategory::ControlFlow,
arc_seq: vec![ArcOpcode::B],
x86_seq: vec![X86Opcode::JMP],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: true,
});
self.add_pattern(SharedPattern {
name: "short_branch".into(),
category: PatternCategory::ControlFlow,
arc_seq: vec![ArcOpcode::B_S],
x86_seq: vec![X86Opcode::JMP],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: true,
});
self.add_pattern(SharedPattern {
name: "branch_and_link".into(),
category: PatternCategory::ControlFlow,
arc_seq: vec![ArcOpcode::BL],
x86_seq: vec![X86Opcode::CALL],
arc_cost: 2,
x86_cost: 2,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: true,
});
self.add_pattern(SharedPattern {
name: "jump_indirect".into(),
category: PatternCategory::ControlFlow,
arc_seq: vec![ArcOpcode::J],
x86_seq: vec![X86Opcode::JMP],
arc_cost: 2,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "jump_and_link".into(),
category: PatternCategory::ControlFlow,
arc_seq: vec![ArcOpcode::JL],
x86_seq: vec![X86Opcode::CALL],
arc_cost: 2,
x86_cost: 2,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "branch_if_equal".into(),
category: PatternCategory::ControlFlow,
arc_seq: vec![ArcOpcode::BEQ],
x86_seq: vec![X86Opcode::JE],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: true,
});
self.add_pattern(SharedPattern {
name: "branch_if_not_equal".into(),
category: PatternCategory::ControlFlow,
arc_seq: vec![ArcOpcode::BNE],
x86_seq: vec![X86Opcode::JNE],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: true,
});
self.add_pattern(SharedPattern {
name: "branch_if_greater".into(),
category: PatternCategory::ControlFlow,
arc_seq: vec![ArcOpcode::BGT],
x86_seq: vec![X86Opcode::JG],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: true,
});
self.add_pattern(SharedPattern {
name: "branch_if_ge".into(),
category: PatternCategory::ControlFlow,
arc_seq: vec![ArcOpcode::BGE],
x86_seq: vec![X86Opcode::JGE],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: true,
});
self.add_pattern(SharedPattern {
name: "branch_if_less".into(),
category: PatternCategory::ControlFlow,
arc_seq: vec![ArcOpcode::BLT],
x86_seq: vec![X86Opcode::JL],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: true,
});
self.add_pattern(SharedPattern {
name: "branch_if_le".into(),
category: PatternCategory::ControlFlow,
arc_seq: vec![ArcOpcode::BLE],
x86_seq: vec![X86Opcode::JLE],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: true,
});
self.add_pattern(SharedPattern {
name: "nop".into(),
category: PatternCategory::ControlFlow,
arc_seq: vec![ArcOpcode::NOP],
x86_seq: vec![X86Opcode::NOP],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 0,
max_operands: 0,
benefits_from_delay_slot: false,
});
}
fn load_bit_manipulation_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "bic".into(),
category: PatternCategory::BitManipulation,
arc_seq: vec![ArcOpcode::BIC],
x86_seq: vec![X86Opcode::NOT, X86Opcode::AND],
arc_cost: 1,
x86_cost: 2,
is_commutative: false,
min_operands: 2,
max_operands: 3,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "word_swap".into(),
category: PatternCategory::BitManipulation,
arc_seq: vec![ArcOpcode::SWAP],
x86_seq: vec![X86Opcode::BSWAP],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: false,
});
}
fn load_extension_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "sign_extend_byte".into(),
category: PatternCategory::Extension,
arc_seq: vec![ArcOpcode::SEXB],
x86_seq: vec![X86Opcode::MOVSX],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "sign_extend_word".into(),
category: PatternCategory::Extension,
arc_seq: vec![ArcOpcode::SEXW],
x86_seq: vec![X86Opcode::MOVSX],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "zero_extend_byte".into(),
category: PatternCategory::Extension,
arc_seq: vec![ArcOpcode::EXTB],
x86_seq: vec![X86Opcode::MOVZX],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "zero_extend_word".into(),
category: PatternCategory::Extension,
arc_seq: vec![ArcOpcode::EXTW],
x86_seq: vec![X86Opcode::MOVZX],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: false,
});
}
fn load_constant_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "load_32bit_immediate".into(),
category: PatternCategory::ConstantMaterialization,
arc_seq: vec![ArcOpcode::MOVHI, ArcOpcode::OR],
x86_seq: vec![X86Opcode::MOV],
arc_cost: 2,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "load_zero".into(),
category: PatternCategory::ConstantMaterialization,
arc_seq: vec![ArcOpcode::XOR],
x86_seq: vec![X86Opcode::XOR],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "load_small_immediate".into(),
category: PatternCategory::ConstantMaterialization,
arc_seq: vec![ArcOpcode::MOV],
x86_seq: vec![X86Opcode::MOV],
arc_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
benefits_from_delay_slot: false,
});
}
fn load_zero_overhead_loop_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "zero_overhead_loop".into(),
category: PatternCategory::ZeroOverheadLoop,
arc_seq: vec![ArcOpcode::LP],
x86_seq: vec![X86Opcode::DEC, X86Opcode::JNE],
arc_cost: 0, x86_cost: 2, is_commutative: false,
min_operands: 1,
max_operands: 1,
benefits_from_delay_slot: false,
});
self.add_pattern(SharedPattern {
name: "manual_loop".into(),
category: PatternCategory::ZeroOverheadLoop,
arc_seq: vec![ArcOpcode::SUB, ArcOpcode::CMP, ArcOpcode::BNE],
x86_seq: vec![X86Opcode::DEC, X86Opcode::JNE],
arc_cost: 3,
x86_cost: 2,
is_commutative: false,
min_operands: 2,
max_operands: 2,
benefits_from_delay_slot: true,
});
}
pub fn find(&self, op_name: &str, _is_arc: bool) -> Option<SharedISelMatch> {
self.patterns.get(op_name).map(|p| SharedISelMatch {
pattern: p.clone(),
arc_sequence: p.arc_seq.clone(),
x86_sequence: p.x86_seq.clone(),
estimated_arc_cost: p.arc_cost,
estimated_x86_cost: p.x86_cost,
})
}
pub fn list_patterns(&self) -> Vec<&String> {
self.patterns.keys().collect()
}
pub fn list_patterns_by_category(&self, category: PatternCategory) -> Vec<&String> {
self.categories
.get(&category)
.map(|v| v.iter().collect())
.unwrap_or_default()
}
pub fn pattern_count(&self) -> usize {
self.patterns.len()
}
}
impl Default for SharedISelPatterns {
fn default() -> Self {
let mut patterns = Self::new();
patterns.load_patterns();
patterns
}
}
#[derive(Debug, Clone)]
pub struct SharedISelMatch {
pub pattern: SharedPattern,
pub arc_sequence: Vec<ArcOpcode>,
pub x86_sequence: Vec<X86Opcode>,
pub estimated_arc_cost: u32,
pub estimated_x86_cost: u32,
}
pub struct CrossTargetLowering {
rules: Vec<LoweringRule>,
rule_index: HashMap<String, usize>,
}
#[derive(Debug, Clone)]
pub struct LoweringRule {
pub name: String,
pub targets: Vec<EmbeddedTarget>,
pub match_pattern: Vec<String>,
pub replacement: Vec<ReplacementStep>,
pub priority: u32,
pub is_optimization: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EmbeddedTarget {
MSP430,
MSP430X,
AVR,
ARC,
ARCEM,
ARCHS,
X86,
X86_64,
All,
}
#[derive(Debug, Clone)]
pub struct ReplacementStep {
pub operation: String,
pub inputs: Vec<usize>,
pub output: String,
pub metadata: HashMap<String, String>,
}
impl CrossTargetLowering {
pub fn new() -> Self {
Self {
rules: Vec::new(),
rule_index: HashMap::new(),
}
}
pub fn load_rules(&mut self) {
self.add_rule(LoweringRule {
name: "hoist_to_delay_slot".into(),
targets: vec![
EmbeddedTarget::ARC,
EmbeddedTarget::ARCEM,
EmbeddedTarget::ARCHS,
],
match_pattern: vec!["instr".into(), "branch".into()],
replacement: vec![ReplacementStep {
operation: "branch_with_delay".into(),
inputs: vec![0, 1],
output: "".into(),
metadata: {
let mut m = HashMap::new();
m.insert("delay_slot".into(), "instr".into());
m
},
}],
priority: 98,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "convert_loop_to_lp".into(),
targets: vec![
EmbeddedTarget::ARC,
EmbeddedTarget::ARCEM,
EmbeddedTarget::ARCHS,
],
match_pattern: vec!["sub".into(), "cmp".into(), "bne".into()],
replacement: vec![ReplacementStep {
operation: "lp".into(),
inputs: vec![0],
output: "".into(),
metadata: {
let mut m = HashMap::new();
m.insert("loop_count".into(), "r0".into());
m
},
}],
priority: 95,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "combine_movhi_or".into(),
targets: vec![
EmbeddedTarget::ARC,
EmbeddedTarget::ARCEM,
EmbeddedTarget::ARCHS,
],
match_pattern: vec!["movhi".into(), "or".into()],
replacement: vec![ReplacementStep {
operation: "load_imm32".into(),
inputs: vec![0, 1],
output: "const".into(),
metadata: HashMap::new(),
}],
priority: 90,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "use_3operand_add".into(),
targets: vec![
EmbeddedTarget::ARC,
EmbeddedTarget::ARCEM,
EmbeddedTarget::ARCHS,
],
match_pattern: vec!["mov".into(), "add".into()],
replacement: vec![ReplacementStep {
operation: "add3".into(),
inputs: vec![0, 1],
output: "result".into(),
metadata: HashMap::new(),
}],
priority: 92,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "use_short_form_instructions".into(),
targets: vec![
EmbeddedTarget::ARC,
EmbeddedTarget::ARCEM,
EmbeddedTarget::ARCHS,
],
match_pattern: vec!["ld".into(), "small_offset".into()],
replacement: vec![ReplacementStep {
operation: "ld_s".into(),
inputs: vec![0, 1],
output: "value".into(),
metadata: {
let mut m = HashMap::new();
m.insert("max_offset".into(), "32".into());
m
},
}],
priority: 91,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "use_bic_instead_of_not_and".into(),
targets: vec![
EmbeddedTarget::ARC,
EmbeddedTarget::ARCEM,
EmbeddedTarget::ARCHS,
],
match_pattern: vec!["not".into(), "and".into()],
replacement: vec![ReplacementStep {
operation: "bic".into(),
inputs: vec![0, 1],
output: "result".into(),
metadata: HashMap::new(),
}],
priority: 93,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "combine_cmp_bcc".into(),
targets: vec![
EmbeddedTarget::ARC,
EmbeddedTarget::ARCEM,
EmbeddedTarget::ARCHS,
],
match_pattern: vec!["cmp".into(), "branch".into()],
replacement: vec![ReplacementStep {
operation: "conditional_branch".into(),
inputs: vec![0, 1],
output: "".into(),
metadata: HashMap::new(),
}],
priority: 85,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "use_abs".into(),
targets: vec![
EmbeddedTarget::ARC,
EmbeddedTarget::ARCEM,
EmbeddedTarget::ARCHS,
],
match_pattern: vec!["cmp".into(), "neg".into(), "cmov".into()],
replacement: vec![ReplacementStep {
operation: "abs".into(),
inputs: vec![1],
output: "result".into(),
metadata: HashMap::new(),
}],
priority: 90,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "use_max_instead_of_cmp_cmov".into(),
targets: vec![
EmbeddedTarget::ARC,
EmbeddedTarget::ARCEM,
EmbeddedTarget::ARCHS,
],
match_pattern: vec!["cmp".into(), "cmov".into()],
replacement: vec![ReplacementStep {
operation: "max_or_min".into(),
inputs: vec![0, 1],
output: "result".into(),
metadata: HashMap::new(),
}],
priority: 88,
is_optimization: true,
});
}
fn add_rule(&mut self, rule: LoweringRule) {
let name = rule.name.clone();
let idx = self.rules.len();
self.rule_index.insert(name, idx);
self.rules.push(rule);
}
pub fn apply(&self, ir_sequence: &[&str]) -> Vec<CrossTargetOptResult> {
let mut results = Vec::new();
for rule in &self.rules {
if let Some(repl) = self.try_match_rule(rule, ir_sequence) {
results.push(CrossTargetOptResult {
rule_name: rule.name.clone(),
matched: true,
replacement: Some(repl),
targets: rule.targets.clone(),
is_optimization: rule.is_optimization,
});
}
}
results
}
fn try_match_rule(&self, rule: &LoweringRule, ir_sequence: &[&str]) -> Option<String> {
if rule.match_pattern.len() > ir_sequence.len() {
return None;
}
for i in 0..=ir_sequence.len() - rule.match_pattern.len() {
let mut matched = true;
for (j, pat) in rule.match_pattern.iter().enumerate() {
if pat != "small_offset" && ir_sequence[i + j] != pat.as_str() {
matched = false;
break;
}
}
if matched {
return Some(
rule.replacement
.iter()
.map(|s| s.operation.clone())
.collect::<Vec<_>>()
.join(", "),
);
}
}
None
}
pub fn get_rule(&self, name: &str) -> Option<&LoweringRule> {
self.rule_index.get(name).map(|&idx| &self.rules[idx])
}
pub fn list_rules(&self) -> Vec<&String> {
self.rules.iter().map(|r| &r.name).collect()
}
pub fn rule_count(&self) -> usize {
self.rules.len()
}
}
impl Default for CrossTargetLowering {
fn default() -> Self {
let mut rules = Self::new();
rules.load_rules();
rules
}
}
#[derive(Debug, Clone)]
pub struct CrossTargetOptResult {
pub rule_name: String,
pub matched: bool,
pub replacement: Option<String>,
pub targets: Vec<EmbeddedTarget>,
pub is_optimization: bool,
}
pub struct SharedRegisterAllocContext {
pub arc_classes: Vec<SharedRegClass>,
pub x86_classes: Vec<SharedRegClass>,
pub virt_to_phys: HashMap<u32, u32>,
pub reg_pressure: HashMap<u32, u32>,
pub live_intervals: Vec<LiveInterval>,
pub strategy: AllocationStrategy,
pub spill_slots: HashMap<u32, SpillSlotInfo>,
pub allocated: BTreeSet<u32>,
}
#[derive(Debug, Clone)]
pub struct SharedRegClass {
pub id: u32,
pub name: String,
pub registers: Vec<u32>,
pub alignment: u32,
pub size_bits: u32,
pub is_allocatable: bool,
pub copy_cost: u32,
}
#[derive(Debug, Clone)]
pub struct LiveInterval {
pub vreg: u32,
pub start: usize,
pub end: usize,
pub reg_class: u32,
pub spill_weight: f64,
}
#[derive(Debug, Clone)]
pub struct SpillSlotInfo {
pub slot: u32,
pub offset: i32,
pub size: u32,
pub alignment: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AllocationStrategy {
LinearScan,
Greedy,
Fast,
}
impl AllocationStrategy {
pub fn as_str(&self) -> &str {
match self {
Self::LinearScan => "linear-scan",
Self::Greedy => "greedy",
Self::Fast => "fast",
}
}
}
impl SharedRegisterAllocContext {
pub fn new() -> Self {
Self {
arc_classes: Vec::new(),
x86_classes: Vec::new(),
virt_to_phys: HashMap::new(),
reg_pressure: HashMap::new(),
live_intervals: Vec::new(),
strategy: AllocationStrategy::Greedy,
spill_slots: HashMap::new(),
allocated: BTreeSet::new(),
}
}
pub fn initialize(&mut self) {
self.arc_classes.push(SharedRegClass {
id: 0,
name: "GPR32".into(),
registers: (0..32).collect(),
alignment: 4,
size_bits: 32,
is_allocatable: true,
copy_cost: 1,
});
self.arc_classes.push(SharedRegClass {
id: 1,
name: "ScratchRegs".into(),
registers: (1..13).collect(),
alignment: 4,
size_bits: 32,
is_allocatable: true,
copy_cost: 1,
});
self.arc_classes.push(SharedRegClass {
id: 2,
name: "CalleeSaved".into(),
registers: (13..26).collect(),
alignment: 4,
size_bits: 32,
is_allocatable: true,
copy_cost: 1,
});
self.arc_classes.push(SharedRegClass {
id: 3,
name: "SpecialRegs".into(),
registers: vec![26, 27, 28, 29, 30, 31],
alignment: 4,
size_bits: 32,
is_allocatable: false,
copy_cost: 0,
});
self.x86_classes.push(SharedRegClass {
id: 0,
name: "GPR32".into(),
registers: (0..8).collect(),
alignment: 4,
size_bits: 32,
is_allocatable: true,
copy_cost: 1,
});
self.x86_classes.push(SharedRegClass {
id: 1,
name: "GPR64".into(),
registers: (0..16).collect(),
alignment: 8,
size_bits: 64,
is_allocatable: true,
copy_cost: 1,
});
}
pub fn compute_spill_weight(&self, interval: &LiveInterval) -> f64 {
let length = (interval.end - interval.start) as f64;
if length <= 0.0 {
return 0.0;
}
length.ln() * 2.0
}
pub fn is_register_available(&self, reg: u32) -> bool {
!self.allocated.contains(®)
}
pub fn allocate_register(&mut self, vreg: u32, phys: u32) {
self.virt_to_phys.insert(vreg, phys);
self.allocated.insert(phys);
}
pub fn free_register(&mut self, phys: u32) {
self.allocated.remove(&phys);
self.virt_to_phys.retain(|_, v| *v != phys);
}
pub fn add_spill_slot(&mut self, vreg: u32, size: u32, alignment: u32) -> u32 {
let slot = self.spill_slots.len() as u32;
self.spill_slots.insert(
vreg,
SpillSlotInfo {
slot,
offset: -(slot as i32 + 1) * 4,
size,
alignment,
},
);
slot
}
pub fn get_physical_reg(&self, vreg: u32) -> Option<u32> {
self.virt_to_phys.get(&vreg).copied()
}
pub fn is_spilled(&self, vreg: u32) -> bool {
self.spill_slots.contains_key(&vreg)
}
pub fn get_spill_slot(&self, vreg: u32) -> Option<&SpillSlotInfo> {
self.spill_slots.get(&vreg)
}
pub fn build_arc_constraints(&self) -> Vec<RegisterConstraint> {
vec![
RegisterConstraint {
reg: 0,
constraint_type: ConstraintType::Reserved,
reason: "Zero register (r0)".into(),
},
RegisterConstraint {
reg: 26,
constraint_type: ConstraintType::FixedAssignment,
reason: "Global pointer (GP/r26)".into(),
},
RegisterConstraint {
reg: 27,
constraint_type: ConstraintType::FixedAssignment,
reason: "Frame pointer (FP/r27)".into(),
},
RegisterConstraint {
reg: 28,
constraint_type: ConstraintType::FixedAssignment,
reason: "Stack pointer (SP/r28)".into(),
},
RegisterConstraint {
reg: 29,
constraint_type: ConstraintType::FixedAssignment,
reason: "Interrupt link 1 (ILINK1/r29)".into(),
},
RegisterConstraint {
reg: 30,
constraint_type: ConstraintType::FixedAssignment,
reason: "Interrupt link 2 (ILINK2/r30)".into(),
},
RegisterConstraint {
reg: 31,
constraint_type: ConstraintType::FixedAssignment,
reason: "Branch link (BLINK/r31) — return address".into(),
},
RegisterConstraint {
reg: 8,
constraint_type: ConstraintType::FixedAssignment,
reason: "First return value (r0 for 32-bit, r0-r1 for 64-bit)".into(),
},
RegisterConstraint {
reg: 1,
constraint_type: ConstraintType::FixedAssignment,
reason: "Argument register 1 (r1)".into(),
},
RegisterConstraint {
reg: 2,
constraint_type: ConstraintType::FixedAssignment,
reason: "Argument register 2 (r2)".into(),
},
]
}
}
impl Default for SharedRegisterAllocContext {
fn default() -> Self {
let mut ctx = Self::new();
ctx.initialize();
ctx
}
}
#[derive(Debug)]
pub struct CrossTargetRegisterAlloc {
pub context: SharedRegisterAllocContext,
pub arc_constraints: Vec<RegisterConstraint>,
pub x86_constraints: Vec<RegisterConstraint>,
}
#[derive(Debug, Clone)]
pub struct RegisterConstraint {
pub reg: u32,
pub constraint_type: ConstraintType,
pub reason: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConstraintType {
Reserved,
FixedAssignment,
CallerSaved,
CalleeSaved,
}
impl CrossTargetRegisterAlloc {
pub fn new() -> Self {
Self {
context: SharedRegisterAllocContext::new(),
arc_constraints: Vec::new(),
x86_constraints: Vec::new(),
}
}
}
impl Default for CrossTargetRegisterAlloc {
fn default() -> Self {
let mut ra = Self::new();
ra.context.initialize();
ra
}
}
#[derive(Debug)]
pub struct CrossTargetFrameLowering {
pub stack_alignment: u32,
pub red_zone_size: u32,
pub max_call_frame_size: u32,
pub use_frame_pointer: bool,
pub local_area_offset_fixed: bool,
}
impl CrossTargetFrameLowering {
pub fn new() -> Self {
Self {
stack_alignment: 4,
red_zone_size: 0,
max_call_frame_size: 0,
use_frame_pointer: true,
local_area_offset_fixed: false,
}
}
}
impl Default for CrossTargetFrameLowering {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct SharedFrameLoweringContext {
pub frame_size: u32,
pub saved_regs_offset: i32,
pub local_area_offset: i32,
pub callee_saved_slots: Vec<(u32, i32)>,
pub has_frame_pointer: bool,
pub has_calls: bool,
pub stack_alignment: u32,
}
impl SharedFrameLoweringContext {
pub fn new() -> Self {
Self {
frame_size: 0,
saved_regs_offset: 0,
local_area_offset: 0,
callee_saved_slots: Vec::new(),
has_frame_pointer: false,
has_calls: false,
stack_alignment: 4,
}
}
pub fn initialize(&mut self) {
self.frame_size = 0;
self.saved_regs_offset = 0;
self.local_area_offset = 0;
self.callee_saved_slots.clear();
self.has_frame_pointer = false;
self.has_calls = false;
}
pub fn add_callee_saved_slot(&mut self, reg: u32, offset: i32) {
self.callee_saved_slots.push((reg, offset));
}
pub fn get_callee_saved_offset(&self, reg: u32) -> Option<i32> {
self.callee_saved_slots
.iter()
.find(|(r, _)| *r == reg)
.map(|(_, o)| *o)
}
pub fn sort_callee_saved_slots(&mut self) {
self.callee_saved_slots.sort_by_key(|(_, o)| *o);
}
}
impl Default for SharedFrameLoweringContext {
fn default() -> Self {
let mut ctx = Self::new();
ctx.initialize();
ctx
}
}
pub struct DelaySlotScheduler {
pub enabled: bool,
pub slots_filled: usize,
pub nops_inserted: usize,
}
impl DelaySlotScheduler {
pub fn new() -> Self {
Self {
enabled: true,
slots_filled: 0,
nops_inserted: 0,
}
}
pub fn schedule(&mut self, before: &[ArcOpcode], branch: ArcOpcode) -> DelaySlotResult {
if !self.enabled {
return DelaySlotResult {
filled: false,
hoisted_instr: None,
slot_instr: ArcOpcode::NOP,
};
}
for &instr in before.iter().rev() {
match instr {
ArcOpcode::MOV
| ArcOpcode::ADD
| ArcOpcode::SUB
| ArcOpcode::AND
| ArcOpcode::OR
| ArcOpcode::XOR
| ArcOpcode::LD
| ArcOpcode::LDB
| ArcOpcode::LDW => {
self.slots_filled += 1;
return DelaySlotResult {
filled: true,
hoisted_instr: Some(instr),
slot_instr: instr,
};
}
_ => {}
}
}
self.nops_inserted += 1;
DelaySlotResult {
filled: false,
hoisted_instr: None,
slot_instr: ArcOpcode::NOP,
}
}
}
#[derive(Debug, Clone)]
pub struct DelaySlotResult {
pub filled: bool,
pub hoisted_instr: Option<ArcOpcode>,
pub slot_instr: ArcOpcode,
}
pub struct CrossTargetConstMaterializer {
pub is_archs: bool,
pub max_s12_immediate: i32,
}
impl CrossTargetConstMaterializer {
pub fn new() -> Self {
Self {
is_archs: true,
max_s12_immediate: 2047,
}
}
pub fn materialize_arc(&self, value: i32) -> Vec<ArcOpcode> {
if value == 0 {
vec![ArcOpcode::XOR] } else if value >= -2048 && value <= 2047 {
vec![ArcOpcode::MOV]
} else if self.is_archs {
vec![ArcOpcode::MOVHI, ArcOpcode::OR]
} else {
vec![ArcOpcode::MOV, ArcOpcode::ADD]
}
}
pub fn materialize_x86_cost(&self, value: i32) -> u32 {
if value == 0 {
1
} else {
1
}
}
}
impl Default for CrossTargetConstMaterializer {
fn default() -> Self {
Self::new()
}
}
pub struct CrossTargetBranchAnalyzer {
pub branch_stats: BranchStats,
pub hints: Vec<BranchHint>,
}
#[derive(Debug, Default, Clone)]
pub struct BranchStats {
pub total_branches: u64,
pub conditional_branches: u64,
pub unconditional_branches: u64,
pub delay_slots: u64,
pub zero_overhead_loops: u64,
}
#[derive(Debug, Clone)]
pub struct BranchHint {
pub instr_idx: usize,
pub likely_taken: bool,
pub confidence: f64,
}
impl CrossTargetBranchAnalyzer {
pub fn new() -> Self {
Self {
branch_stats: BranchStats::default(),
hints: Vec::new(),
}
}
pub fn analyze(&mut self, instrs: &[&str]) {
self.branch_stats.total_branches = 0;
self.branch_stats.conditional_branches = 0;
self.branch_stats.unconditional_branches = 0;
for instr in instrs.iter() {
let lower = instr.to_lowercase();
if lower.starts_with('b') || lower.starts_with('j') || lower.contains("lp") {
self.branch_stats.total_branches += 1;
if lower == "b" || lower == "j" || lower == "b_s" || lower == "j_s" {
self.branch_stats.unconditional_branches += 1;
} else if lower == "lp" {
self.branch_stats.zero_overhead_loops += 1;
self.branch_stats.unconditional_branches += 1;
} else {
self.branch_stats.conditional_branches += 1;
}
}
}
}
pub fn compare(&self, arc_branches: u64, x86_branches: u64) -> BranchComparison {
let ratio = if x86_branches > 0 {
arc_branches as f64 / x86_branches as f64
} else {
f64::INFINITY
};
BranchComparison {
arc_branch_count: arc_branches,
x86_branch_count: x86_branches,
ratio,
assessment: if ratio < 0.9 {
"ARC uses fewer branches (likely due to ZOL)".into()
} else if ratio > 1.1 {
"X86 uses fewer branches".into()
} else {
"Similar branch counts".into()
},
}
}
}
impl Default for CrossTargetBranchAnalyzer {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct BranchComparison {
pub arc_branch_count: u64,
pub x86_branch_count: u64,
pub ratio: f64,
pub assessment: String,
}
#[derive(Debug, Clone, Default)]
pub struct ARCFeatureSet {
pub enabled: HashSet<String>,
pub disabled: HashSet<String>,
}
impl ARCFeatureSet {
pub fn new() -> Self {
Self {
enabled: HashSet::new(),
disabled: HashSet::new(),
}
}
pub fn enable(&mut self, feature: &str) {
self.disabled.remove(feature);
self.enabled.insert(feature.to_string());
}
pub fn disable(&mut self, feature: &str) {
self.enabled.remove(feature);
self.disabled.insert(feature.to_string());
}
pub fn has(&self, feature: &str) -> bool {
self.enabled.contains(feature)
}
pub fn arc700() -> Self {
let mut fs = Self::new();
fs.enable("arc700");
fs.enable("barrel_shifter");
fs.enable("norm");
fs.enable("swap");
fs.enable("delay_slots");
fs
}
pub fn arcem() -> Self {
let mut fs = Self::new();
fs.enable("arcem");
fs.enable("barrel_shifter");
fs.enable("norm");
fs.enable("swap");
fs.enable("delay_slots");
fs.enable("lp"); fs.enable("mpy");
fs.enable("div_rem");
fs
}
pub fn archs() -> Self {
let mut fs = Self::new();
fs.enable("archs");
fs.enable("barrel_shifter");
fs.enable("norm");
fs.enable("swap");
fs.enable("delay_slots");
fs.enable("lp");
fs.enable("mpy");
fs.enable("div_rem");
fs.enable("dual_issue");
fs.enable("movhi");
fs.enable("ll64"); fs
}
}
#[derive(Debug, Clone)]
pub struct BridgeARCTargetMachine {
pub triple: String,
pub cpu: String,
pub features: String,
pub arc_variant: String,
pub data_layout: String,
pub pointer_size: u32,
pub use_delay_slots: bool,
pub feature_set: ARCFeatureSet,
}
pub const ARC_DATA_LAYOUT: &str =
"e-m:e-p:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-f32:32:32-a:0:32-n32";
impl BridgeARCTargetMachine {
pub fn new(variant: &str) -> Self {
let lower = variant.to_lowercase();
let feature_set = match lower.as_str() {
"arc" => ARCFeatureSet::arc700(),
"arcem" => ARCFeatureSet::arcem(),
"archs" => ARCFeatureSet::archs(),
_ => ARCFeatureSet::archs(),
};
let features = Self::features_for_variant(&lower);
Self {
triple: format!("{}-unknown-elf", lower),
cpu: format!("{}-generic", lower),
features,
arc_variant: lower,
data_layout: ARC_DATA_LAYOUT.to_string(),
pointer_size: 4,
use_delay_slots: true,
feature_set,
}
}
fn features_for_variant(variant: &str) -> String {
match variant {
"arc" => "+barrel_shifter,+norm,+swap,+delay_slots".into(),
"arcem" => "+barrel_shifter,+norm,+swap,+delay_slots,+lp,+mpy,+div_rem".into(),
"archs" => concat!(
"+barrel_shifter,+norm,+swap,+delay_slots,+lp,",
"+mpy,+div_rem,+dual_issue,+movhi,+ll64"
)
.into(),
_ => "+barrel_shifter,+norm,+swap".into(),
}
}
pub fn new_arc700() -> Self {
Self::new("arc")
}
pub fn new_arcem() -> Self {
Self::new("arcem")
}
pub fn new_archs() -> Self {
Self::new("archs")
}
pub fn get_triple(&self) -> &str {
&self.triple
}
pub fn has_feature(&self, name: &str) -> bool {
self.feature_set.has(name)
}
pub fn has_delay_slots(&self) -> bool {
self.use_delay_slots
}
pub fn describe(&self) -> String {
format!(
"ARC Target Machine:\n Variant: {}\n CPU: {}\n Features: {}\n Delay Slots: {}\n Data Layout: {}\n Pointer Size: {} bits",
self.arc_variant,
self.cpu,
self.features,
self.use_delay_slots,
self.data_layout,
self.pointer_size * 8,
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ArcBridgeReg {
R0,
R1,
R2,
R3,
R4,
R5,
R6,
R7,
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15,
R16,
R17,
R18,
R19,
R20,
R21,
R22,
R23,
R24,
R25,
GP,
FP,
SP,
ILINK1,
ILINK2,
BLINK,
}
impl ArcBridgeReg {
pub fn idx(&self) -> u8 {
match self {
Self::R0 => 0,
Self::R1 => 1,
Self::R2 => 2,
Self::R3 => 3,
Self::R4 => 4,
Self::R5 => 5,
Self::R6 => 6,
Self::R7 => 7,
Self::R8 => 8,
Self::R9 => 9,
Self::R10 => 10,
Self::R11 => 11,
Self::R12 => 12,
Self::R13 => 13,
Self::R14 => 14,
Self::R15 => 15,
Self::R16 => 16,
Self::R17 => 17,
Self::R18 => 18,
Self::R19 => 19,
Self::R20 => 20,
Self::R21 => 21,
Self::R22 => 22,
Self::R23 => 23,
Self::R24 => 24,
Self::R25 => 25,
Self::GP => 26,
Self::FP => 27,
Self::SP => 28,
Self::ILINK1 => 29,
Self::ILINK2 => 30,
Self::BLINK => 31,
}
}
pub fn is_special(&self) -> bool {
matches!(
self,
Self::R0 | Self::GP | Self::FP | Self::SP | Self::ILINK1 | Self::ILINK2 | Self::BLINK
)
}
pub fn is_callee_saved(&self) -> bool {
let idx = self.idx();
idx >= 13 && idx <= 25
}
pub fn is_caller_saved(&self) -> bool {
let idx = self.idx();
idx >= 1 && idx <= 12
}
pub fn abi_name(&self) -> &str {
match self {
Self::R0 => "r0",
Self::R1 => "r1",
Self::R2 => "r2",
Self::R3 => "r3",
Self::R4 => "r4",
Self::R5 => "r5",
Self::R6 => "r6",
Self::R7 => "r7",
Self::R8 => "r8",
Self::R9 => "r9",
Self::R10 => "r10",
Self::R11 => "r11",
Self::R12 => "r12",
Self::R13 => "r13",
Self::R14 => "r14",
Self::R15 => "r15",
Self::R16 => "r16",
Self::R17 => "r17",
Self::R18 => "r18",
Self::R19 => "r19",
Self::R20 => "r20",
Self::R21 => "r21",
Self::R22 => "r22",
Self::R23 => "r23",
Self::R24 => "r24",
Self::R25 => "r25",
Self::GP => "gp",
Self::FP => "fp",
Self::SP => "sp",
Self::ILINK1 => "ilink1",
Self::ILINK2 => "ilink2",
Self::BLINK => "blink",
}
}
pub fn from_idx(idx: u8) -> Option<Self> {
match idx {
0 => Some(Self::R0),
1 => Some(Self::R1),
2 => Some(Self::R2),
3 => Some(Self::R3),
4 => Some(Self::R4),
5 => Some(Self::R5),
6 => Some(Self::R6),
7 => Some(Self::R7),
8 => Some(Self::R8),
9 => Some(Self::R9),
10 => Some(Self::R10),
11 => Some(Self::R11),
12 => Some(Self::R12),
13 => Some(Self::R13),
14 => Some(Self::R14),
15 => Some(Self::R15),
16 => Some(Self::R16),
17 => Some(Self::R17),
18 => Some(Self::R18),
19 => Some(Self::R19),
20 => Some(Self::R20),
21 => Some(Self::R21),
22 => Some(Self::R22),
23 => Some(Self::R23),
24 => Some(Self::R24),
25 => Some(Self::R25),
26 => Some(Self::GP),
27 => Some(Self::FP),
28 => Some(Self::SP),
29 => Some(Self::ILINK1),
30 => Some(Self::ILINK2),
31 => Some(Self::BLINK),
_ => None,
}
}
}
pub struct BridgeARCInstrInfo {
pub core: ArcInstrInfo,
pub arithmetic_ops: Vec<ArcOpcode>,
pub shift_ops: Vec<ArcOpcode>,
pub memory_ops: Vec<ArcOpcode>,
pub branch_ops: Vec<ArcOpcode>,
pub extension_ops: Vec<ArcOpcode>,
pub special_ops: Vec<ArcOpcode>,
pub short_ops: Vec<ArcOpcode>,
}
impl BridgeARCInstrInfo {
pub fn new() -> Self {
let mut info = Self {
core: ArcInstrInfo::new(),
arithmetic_ops: Vec::new(),
shift_ops: Vec::new(),
memory_ops: Vec::new(),
branch_ops: Vec::new(),
extension_ops: Vec::new(),
special_ops: Vec::new(),
short_ops: Vec::new(),
};
info.categorize();
info
}
fn categorize(&mut self) {
self.arithmetic_ops = vec![
ArcOpcode::ADD,
ArcOpcode::ADD1,
ArcOpcode::ADD2,
ArcOpcode::ADD3,
ArcOpcode::SUB,
ArcOpcode::SUB1,
ArcOpcode::SUB2,
ArcOpcode::SUB3,
ArcOpcode::AND,
ArcOpcode::OR,
ArcOpcode::XOR,
ArcOpcode::BIC,
ArcOpcode::BIC1,
ArcOpcode::MOV,
ArcOpcode::MOVHI,
ArcOpcode::CMP,
ArcOpcode::RCMP,
ArcOpcode::ABS,
ArcOpcode::NEG,
ArcOpcode::MAX,
ArcOpcode::MIN,
ArcOpcode::MPY,
ArcOpcode::FLAG,
];
self.shift_ops = vec![
ArcOpcode::ASL,
ArcOpcode::ASL1,
ArcOpcode::ASR,
ArcOpcode::ASR1,
ArcOpcode::LSR,
ArcOpcode::LSR1,
ArcOpcode::ROR,
];
self.memory_ops = vec![
ArcOpcode::LD,
ArcOpcode::LDB,
ArcOpcode::LDW,
ArcOpcode::ST,
ArcOpcode::STB,
ArcOpcode::STW,
];
self.branch_ops = vec![
ArcOpcode::B,
ArcOpcode::BL,
ArcOpcode::BEQ,
ArcOpcode::BNE,
ArcOpcode::BGT,
ArcOpcode::BGE,
ArcOpcode::BLT,
ArcOpcode::BLE,
ArcOpcode::BHI,
ArcOpcode::BHS,
ArcOpcode::BLO,
ArcOpcode::BLS,
ArcOpcode::J,
ArcOpcode::JL,
ArcOpcode::LP,
ArcOpcode::BR,
];
self.extension_ops = vec![
ArcOpcode::SEXB,
ArcOpcode::SEXW,
ArcOpcode::EXTB,
ArcOpcode::EXTW,
];
self.special_ops = vec![ArcOpcode::SWAP, ArcOpcode::NORM, ArcOpcode::NOP];
self.short_ops = vec![
ArcOpcode::MOV_S,
ArcOpcode::LD_S,
ArcOpcode::LDB_S,
ArcOpcode::LDW_S,
ArcOpcode::ST_S,
ArcOpcode::STB_S,
ArcOpcode::STW_S,
ArcOpcode::B_S,
ArcOpcode::BL_S,
ArcOpcode::J_S,
ArcOpcode::JL_S,
];
}
pub fn is_short_form(&self, opcode: ArcOpcode) -> bool {
self.short_ops.contains(&opcode)
}
pub fn instruction_counts(&self) -> InstructionCounts {
InstructionCounts {
arithmetic: self.arithmetic_ops.len() as u32,
shift: self.shift_ops.len() as u32,
memory: self.memory_ops.len() as u32,
branch: self.branch_ops.len() as u32,
extension: self.extension_ops.len() as u32,
special: self.special_ops.len() as u32,
short_form: self.short_ops.len() as u32,
}
}
}
#[derive(Debug, Clone)]
pub struct InstructionCounts {
pub arithmetic: u32,
pub shift: u32,
pub memory: u32,
pub branch: u32,
pub extension: u32,
pub special: u32,
pub short_form: u32,
}
#[derive(Debug, Clone)]
pub struct ARCCallingConventionBridge {
pub arg_regs: Vec<u32>,
pub ret_regs: Vec<u32>,
pub callee_saved: Vec<u32>,
pub caller_saved: Vec<u32>,
pub stack_alignment: u32,
}
impl ARCCallingConventionBridge {
pub fn new() -> Self {
Self {
arg_regs: (1..=8).collect(),
ret_regs: vec![8],
callee_saved: (13..=25).collect(),
caller_saved: (1..=12).collect(),
stack_alignment: 4,
}
}
pub fn get_arg_register(&self, n: usize) -> Option<u32> {
self.arg_regs.get(n).copied()
}
pub fn get_return_register(&self) -> u32 {
self.ret_regs[0]
}
pub fn is_callee_saved(&self, reg: u32) -> bool {
self.callee_saved.contains(®)
}
pub fn compare_with_x86(&self) -> CCComparison {
CCComparison {
arc_arg_regs: self.arg_regs.len() as u32,
x86_arg_regs: 6,
arc_ret_regs: self.ret_regs.len() as u32,
x86_ret_regs: 2,
arc_callee_saved: self.callee_saved.len() as u32,
x86_callee_saved: 5,
arc_caller_saved: self.caller_saved.len() as u32,
x86_caller_saved: 9,
}
}
}
#[derive(Debug, Clone)]
pub struct CCComparison {
pub arc_arg_regs: u32,
pub x86_arg_regs: u32,
pub arc_ret_regs: u32,
pub x86_ret_regs: u32,
pub arc_callee_saved: u32,
pub x86_callee_saved: u32,
pub arc_caller_saved: u32,
pub x86_caller_saved: u32,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bridge_creation() {
let bridge = ARCX86Bridge::new();
assert_eq!(bridge.config.arc_variant, "archs");
assert!(bridge.config.use_delay_slots);
}
#[test]
fn test_bridge_with_config() {
let config = BridgeConfig {
arc_variant: "arcem".into(),
use_delay_slots: false,
..BridgeConfig::default()
};
let bridge = ARCX86Bridge::with_config(config);
assert_eq!(bridge.config.arc_variant, "arcem");
assert!(!bridge.config.use_delay_slots);
}
#[test]
fn test_translate_x86_to_arc_add() {
let mut bridge = ARCX86Bridge::new();
let result = bridge.translate_x86_to_arc(&X86Opcode::ADD);
assert!(result.is_some());
match result.unwrap() {
BridgeInstruction::DirectMap(op) => assert_eq!(op, ArcOpcode::ADD),
_ => panic!("Expected DirectMap"),
}
}
#[test]
fn test_translate_arc_to_x86_b() {
let mut bridge = ARCX86Bridge::new();
let result = bridge.translate_arc_to_x86(&ArcOpcode::B);
assert!(result.is_some());
match result.unwrap() {
BridgeInstruction::DirectMapX86(op) => assert_eq!(op, X86Opcode::JMP),
_ => panic!("Expected DirectMapX86"),
}
}
#[test]
fn test_translate_arc_to_x86_bl() {
let mut bridge = ARCX86Bridge::new();
let result = bridge.translate_arc_to_x86(&ArcOpcode::BL);
assert!(result.is_some());
match result.unwrap() {
BridgeInstruction::DirectMapX86(op) => assert_eq!(op, X86Opcode::CALL),
_ => panic!("Expected DirectMapX86"),
}
}
#[test]
fn test_translate_arc_to_x86_sexb() {
let mut bridge = ARCX86Bridge::new();
let result = bridge.translate_arc_to_x86(&ArcOpcode::SEXB);
assert!(result.is_some());
match result.unwrap() {
BridgeInstruction::DirectMapX86(op) => assert_eq!(op, X86Opcode::MOVSX),
_ => panic!("Expected DirectMapX86"),
}
}
#[test]
fn test_translate_arc_to_x86_bic() {
let mut bridge = ARCX86Bridge::new();
let result = bridge.translate_arc_to_x86(&ArcOpcode::BIC);
assert!(result.is_some());
match result.unwrap() {
BridgeInstruction::ExpandedX86(seq) => {
assert_eq!(seq.len(), 2);
assert_eq!(seq[0], X86Opcode::NOT);
}
_ => panic!("Expected ExpandedX86"),
}
}
#[test]
fn test_delay_slot_fill() {
let mut bridge = ARCX86Bridge::new();
let before = vec![ArcOpcode::MOV, ArcOpcode::ADD, ArcOpcode::SUB];
let result = bridge.fill_delay_slot(ArcOpcode::B, &before);
assert!(result.is_some());
assert_eq!(result.unwrap(), ArcOpcode::MOV);
}
#[test]
fn test_delay_slot_fill_with_nop() {
let mut bridge = ARCX86Bridge::new();
let before = vec![ArcOpcode::B, ArcOpcode::BL, ArcOpcode::BEQ];
let result = bridge.fill_delay_slot(ArcOpcode::B, &before);
assert!(result.is_some());
assert_eq!(result.unwrap(), ArcOpcode::NOP); }
#[test]
fn test_pattern_loading() {
let patterns = SharedISelPatterns::default();
assert!(patterns.pattern_count() > 20);
}
#[test]
fn test_find_pattern() {
let patterns = SharedISelPatterns::default();
let result = patterns.find("add_32bit", true);
assert!(result.is_some());
let m = result.unwrap();
assert_eq!(m.arc_sequence[0], ArcOpcode::ADD);
assert_eq!(m.x86_sequence[0], X86Opcode::ADD);
}
#[test]
fn test_find_delay_slot_pattern() {
let patterns = SharedISelPatterns::default();
let result = patterns.find("unconditional_branch", true);
assert!(result.is_some());
let m = result.unwrap();
assert!(m.pattern.benefits_from_delay_slot);
}
#[test]
fn test_lowering_rules() {
let rules = CrossTargetLowering::default();
assert!(rules.rule_count() >= 8);
}
#[test]
fn test_apply_lowering_rule_delay_slot() {
let rules = CrossTargetLowering::default();
let results = rules.apply(&["instr", "branch"]);
assert!(!results.is_empty());
}
#[test]
fn test_ra_context() {
let ctx = SharedRegisterAllocContext::default();
assert_eq!(ctx.arc_classes.len(), 4);
assert_eq!(ctx.x86_classes.len(), 2);
}
#[test]
fn test_ra_allocate_free() {
let mut ctx = SharedRegisterAllocContext::new();
ctx.initialize();
ctx.allocate_register(200, 5); assert!(!ctx.is_register_available(5));
ctx.free_register(5);
assert!(ctx.is_register_available(5));
}
#[test]
fn test_target_machine_arc700() {
let tm = BridgeARCTargetMachine::new_arc700();
assert_eq!(tm.arc_variant, "arc");
assert!(tm.has_feature("barrel_shifter"));
assert!(!tm.has_feature("lp"));
}
#[test]
fn test_target_machine_arcem() {
let tm = BridgeARCTargetMachine::new_arcem();
assert_eq!(tm.arc_variant, "arcem");
assert!(tm.has_feature("lp"));
assert!(tm.has_feature("mpy"));
}
#[test]
fn test_target_machine_archs() {
let tm = BridgeARCTargetMachine::new_archs();
assert_eq!(tm.arc_variant, "archs");
assert!(tm.has_feature("dual_issue"));
assert!(tm.has_feature("movhi"));
assert!(tm.has_feature("ll64"));
}
#[test]
fn test_register_enumeration() {
assert_eq!(ArcBridgeReg::R0.idx(), 0);
assert_eq!(ArcBridgeReg::SP.idx(), 28);
assert_eq!(ArcBridgeReg::BLINK.idx(), 31);
}
#[test]
fn test_register_special() {
assert!(ArcBridgeReg::R0.is_special());
assert!(ArcBridgeReg::SP.is_special());
assert!(ArcBridgeReg::BLINK.is_special());
assert!(!ArcBridgeReg::R5.is_special());
}
#[test]
fn test_register_callee_caller_saved() {
assert!(ArcBridgeReg::R13.is_callee_saved());
assert!(ArcBridgeReg::R25.is_callee_saved());
assert!(!ArcBridgeReg::R12.is_callee_saved());
assert!(ArcBridgeReg::R5.is_caller_saved());
assert!(!ArcBridgeReg::R15.is_caller_saved()); }
#[test]
fn test_register_from_idx() {
assert_eq!(ArcBridgeReg::from_idx(0), Some(ArcBridgeReg::R0));
assert_eq!(ArcBridgeReg::from_idx(31), Some(ArcBridgeReg::BLINK));
assert_eq!(ArcBridgeReg::from_idx(32), None);
}
#[test]
fn test_calling_convention() {
let cc = ARCCallingConventionBridge::new();
assert_eq!(cc.arg_regs.len(), 8);
assert_eq!(cc.ret_regs, vec![8]);
assert_eq!(cc.get_return_register(), 8);
}
#[test]
fn test_calling_convention_args() {
let cc = ARCCallingConventionBridge::new();
assert_eq!(cc.get_arg_register(0), Some(1));
assert_eq!(cc.get_arg_register(7), Some(8));
assert_eq!(cc.get_arg_register(8), None);
}
#[test]
fn test_const_materializer_arc() {
let cm = CrossTargetConstMaterializer::new();
assert_eq!(cm.materialize_arc(0), vec![ArcOpcode::XOR]);
assert_eq!(cm.materialize_arc(42), vec![ArcOpcode::MOV]);
assert!(cm.materialize_arc(0x12345678).len() == 2); }
#[test]
fn test_delay_slot_scheduler() {
let mut sched = DelaySlotScheduler::new();
let before = vec![ArcOpcode::MOV, ArcOpcode::ADD, ArcOpcode::LD];
let result = sched.schedule(&before, ArcOpcode::B);
assert!(result.filled);
assert_eq!(result.slot_instr, ArcOpcode::MOV);
assert_eq!(sched.slots_filled, 1);
}
#[test]
fn test_delay_slot_scheduler_empty() {
let mut sched = DelaySlotScheduler::new();
let before: Vec<ArcOpcode> = vec![];
let result = sched.schedule(&before, ArcOpcode::B);
assert!(!result.filled);
assert_eq!(result.slot_instr, ArcOpcode::NOP);
assert_eq!(sched.nops_inserted, 1);
}
#[test]
fn test_instr_info_categorization() {
let info = BridgeARCInstrInfo::new();
let counts = info.instruction_counts();
assert!(counts.arithmetic >= 20);
assert!(counts.shift >= 7);
assert!(counts.memory >= 6);
assert!(counts.branch >= 14);
assert!(counts.extension >= 4);
assert!(counts.special >= 3);
assert!(counts.short_form >= 11);
}
#[test]
fn test_instr_info_short_form() {
let info = BridgeARCInstrInfo::new();
assert!(info.is_short_form(ArcOpcode::LD_S));
assert!(!info.is_short_form(ArcOpcode::LD));
assert!(info.is_short_form(ArcOpcode::B_S));
}
#[test]
fn test_feature_set() {
let fs = ARCFeatureSet::archs();
assert!(fs.has("archs"));
assert!(fs.has("dual_issue"));
assert!(!fs.has("nonexistent"));
}
#[test]
fn test_frame_context() {
let mut ctx = SharedFrameLoweringContext::new();
ctx.frame_size = 16;
ctx.add_callee_saved_slot(13, -4);
ctx.add_callee_saved_slot(14, -8);
assert_eq!(ctx.get_callee_saved_offset(13), Some(-4));
assert_eq!(ctx.get_callee_saved_offset(14), Some(-8));
}
#[test]
fn test_branch_analysis() {
let mut analyzer = CrossTargetBranchAnalyzer::new();
analyzer.analyze(&["b", "beq", "add", "lp", "mov", "bne"]);
assert_eq!(analyzer.branch_stats.total_branches, 4);
assert_eq!(analyzer.branch_stats.conditional_branches, 2);
assert_eq!(analyzer.branch_stats.unconditional_branches, 2);
assert_eq!(analyzer.branch_stats.zero_overhead_loops, 1);
}
}