use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::fmt;
use super::avr_instr_info::{AvrInstrDesc, AvrInstrInfo, AvrOpcode};
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 AVRX86Bridge {
pub avr_instr_info: AvrInstrInfo,
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 avr_family: String,
}
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: 6,
opt_transfer_threshold: 1.5,
collect_stats: true,
verbose: false,
avr_family: "avr5".into(),
}
}
}
#[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,
}
impl fmt::Display for BridgeStats {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "AVR ↔ 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)?;
write!(f, " Total time (est): {} µs", self.total_time_us)
}
}
impl AVRX86Bridge {
pub fn new() -> Self {
Self {
avr_instr_info: AvrInstrInfo::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 AVR ↔ 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_avr(&mut self, x86_opcode: &X86Opcode) -> Option<BridgeInstruction> {
self.stats.pattern_translations += 1;
match x86_opcode {
X86Opcode::ADD => Some(BridgeInstruction::DirectMap(AvrOpcode::ADD)),
X86Opcode::ADC => Some(BridgeInstruction::DirectMap(AvrOpcode::ADC)),
X86Opcode::SUB => Some(BridgeInstruction::DirectMap(AvrOpcode::SUB)),
X86Opcode::SBB => Some(BridgeInstruction::DirectMap(AvrOpcode::SBC)),
X86Opcode::AND => Some(BridgeInstruction::DirectMap(AvrOpcode::AND)),
X86Opcode::OR => Some(BridgeInstruction::DirectMap(AvrOpcode::OR)),
X86Opcode::XOR => Some(BridgeInstruction::DirectMap(AvrOpcode::EOR)),
X86Opcode::NOT => Some(BridgeInstruction::DirectMap(AvrOpcode::COM)),
X86Opcode::NEG => Some(BridgeInstruction::DirectMap(AvrOpcode::NEG)),
X86Opcode::INC => Some(BridgeInstruction::DirectMap(AvrOpcode::INC)),
X86Opcode::DEC => Some(BridgeInstruction::DirectMap(AvrOpcode::DEC)),
X86Opcode::CMP => Some(BridgeInstruction::DirectMap(AvrOpcode::SUB)), X86Opcode::TEST => {
Some(BridgeInstruction::DirectMap(AvrOpcode::AND)) }
X86Opcode::MOV => Some(BridgeInstruction::DirectMap(AvrOpcode::MOV)),
X86Opcode::MOVSX => Some(BridgeInstruction::Expanded(vec![
AvrOpcode::MOV,
AvrOpcode::LSL,
AvrOpcode::SBC,
])),
X86Opcode::MOVZX => Some(BridgeInstruction::Expanded(vec![
AvrOpcode::LDI,
AvrOpcode::MOV,
])),
X86Opcode::PUSH => Some(BridgeInstruction::Expanded(vec![
AvrOpcode::ST,
AvrOpcode::SBIW,
])),
X86Opcode::POP => Some(BridgeInstruction::Expanded(vec![
AvrOpcode::LD,
AvrOpcode::ADIW,
])),
X86Opcode::SHL => Some(BridgeInstruction::DirectMap(AvrOpcode::LSL)),
X86Opcode::SHR => Some(BridgeInstruction::DirectMap(AvrOpcode::LSR)),
X86Opcode::SAR => Some(BridgeInstruction::DirectMap(AvrOpcode::ASR)),
X86Opcode::ROL => Some(BridgeInstruction::DirectMap(AvrOpcode::ROL)),
X86Opcode::ROR => Some(BridgeInstruction::DirectMap(AvrOpcode::ROR)),
X86Opcode::JMP => Some(BridgeInstruction::DirectMap(AvrOpcode::JMP)),
X86Opcode::JE => Some(BridgeInstruction::Expanded(vec![
AvrOpcode::CP,
AvrOpcode::BREQ,
])),
X86Opcode::JNE => Some(BridgeInstruction::Expanded(vec![
AvrOpcode::CP,
AvrOpcode::BRNE,
])),
X86Opcode::JB => Some(BridgeInstruction::Expanded(vec![
AvrOpcode::CP,
AvrOpcode::BRCS,
])),
X86Opcode::JAE => Some(BridgeInstruction::Expanded(vec![
AvrOpcode::CP,
AvrOpcode::BRCC,
])),
X86Opcode::JL => Some(BridgeInstruction::Expanded(vec![
AvrOpcode::CP,
AvrOpcode::BRLT,
])),
X86Opcode::JGE => Some(BridgeInstruction::Expanded(vec![
AvrOpcode::CP,
AvrOpcode::BRGE,
])),
X86Opcode::CALL => Some(BridgeInstruction::DirectMap(AvrOpcode::CALL)),
X86Opcode::RET => Some(BridgeInstruction::DirectMap(AvrOpcode::RET)),
X86Opcode::NOP => Some(BridgeInstruction::DirectMap(AvrOpcode::NOP)),
X86Opcode::MUL => Some(BridgeInstruction::DirectMap(AvrOpcode::MUL)),
X86Opcode::IMUL => Some(BridgeInstruction::DirectMap(AvrOpcode::MULS)),
_ => {
self.stats.failed_transfers += 1;
None
}
}
}
pub fn translate_avr_to_x86(&mut self, avr_opcode: &AvrOpcode) -> Option<BridgeInstruction> {
self.stats.pattern_translations += 1;
match avr_opcode {
AvrOpcode::ADD => Some(BridgeInstruction::DirectMapX86(X86Opcode::ADD)),
AvrOpcode::ADC => Some(BridgeInstruction::DirectMapX86(X86Opcode::ADC)),
AvrOpcode::ADIW => Some(BridgeInstruction::DirectMapX86(X86Opcode::ADD)),
AvrOpcode::SUB => Some(BridgeInstruction::DirectMapX86(X86Opcode::SUB)),
AvrOpcode::SUBI => Some(BridgeInstruction::DirectMapX86(X86Opcode::SUB)),
AvrOpcode::SBC => Some(BridgeInstruction::DirectMapX86(X86Opcode::SBB)),
AvrOpcode::SBCI => Some(BridgeInstruction::DirectMapX86(X86Opcode::SBB)),
AvrOpcode::SBIW => Some(BridgeInstruction::DirectMapX86(X86Opcode::SUB)),
AvrOpcode::INC => Some(BridgeInstruction::DirectMapX86(X86Opcode::INC)),
AvrOpcode::DEC => Some(BridgeInstruction::DirectMapX86(X86Opcode::DEC)),
AvrOpcode::MUL => Some(BridgeInstruction::DirectMapX86(X86Opcode::MUL)),
AvrOpcode::MULS => Some(BridgeInstruction::DirectMapX86(X86Opcode::IMUL)),
AvrOpcode::MULSU => Some(BridgeInstruction::DirectMapX86(X86Opcode::IMUL)),
AvrOpcode::AND | AvrOpcode::ANDI => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::AND))
}
AvrOpcode::OR | AvrOpcode::ORI => Some(BridgeInstruction::DirectMapX86(X86Opcode::OR)),
AvrOpcode::EOR => Some(BridgeInstruction::DirectMapX86(X86Opcode::XOR)),
AvrOpcode::COM => Some(BridgeInstruction::DirectMapX86(X86Opcode::NOT)),
AvrOpcode::NEG => Some(BridgeInstruction::DirectMapX86(X86Opcode::NEG)),
AvrOpcode::SBR => Some(BridgeInstruction::DirectMapX86(X86Opcode::OR)),
AvrOpcode::CBR => Some(BridgeInstruction::ExpandedX86(vec![
X86Opcode::NOT,
X86Opcode::AND,
])),
AvrOpcode::LSL => Some(BridgeInstruction::DirectMapX86(X86Opcode::SHL)),
AvrOpcode::LSR => Some(BridgeInstruction::DirectMapX86(X86Opcode::SHR)),
AvrOpcode::ROL => Some(BridgeInstruction::DirectMapX86(X86Opcode::ROL)),
AvrOpcode::ROR => Some(BridgeInstruction::DirectMapX86(X86Opcode::ROR)),
AvrOpcode::ASR => Some(BridgeInstruction::DirectMapX86(X86Opcode::SAR)),
AvrOpcode::SWAP => Some(BridgeInstruction::ExpandedX86(vec![
X86Opcode::MOV,
X86Opcode::SHL,
X86Opcode::SHR,
X86Opcode::OR,
])),
AvrOpcode::RJMP | AvrOpcode::IJMP | AvrOpcode::JMP => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::JMP))
}
AvrOpcode::RCALL | AvrOpcode::ICALL | AvrOpcode::CALL => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::CALL))
}
AvrOpcode::RET => Some(BridgeInstruction::DirectMapX86(X86Opcode::RET)),
AvrOpcode::RETI => Some(BridgeInstruction::DirectMapX86(X86Opcode::RET)),
AvrOpcode::CPSE => Some(BridgeInstruction::ExpandedX86(vec![
X86Opcode::CMP,
X86Opcode::CMOVE,
])),
AvrOpcode::SBRC => Some(BridgeInstruction::ExpandedX86(vec![
X86Opcode::TEST,
X86Opcode::CMOVE,
])),
AvrOpcode::SBRS => Some(BridgeInstruction::ExpandedX86(vec![
X86Opcode::TEST,
X86Opcode::CMOVNE,
])),
AvrOpcode::SBIC => Some(BridgeInstruction::ExpandedX86(vec![
X86Opcode::TEST,
X86Opcode::CMOVE,
])),
AvrOpcode::SBIS => Some(BridgeInstruction::ExpandedX86(vec![
X86Opcode::TEST,
X86Opcode::CMOVNE,
])),
AvrOpcode::MOV | AvrOpcode::MOVW => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::MOV))
}
AvrOpcode::LDI => Some(BridgeInstruction::DirectMapX86(X86Opcode::MOV)),
AvrOpcode::LD | AvrOpcode::LD_X | AvrOpcode::LD_Y | AvrOpcode::LD_Z => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::MOV))
}
AvrOpcode::ST | AvrOpcode::ST_X | AvrOpcode::ST_Y | AvrOpcode::ST_Z => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::MOV))
}
AvrOpcode::LDS => Some(BridgeInstruction::DirectMapX86(X86Opcode::MOV)),
AvrOpcode::STS => Some(BridgeInstruction::DirectMapX86(X86Opcode::MOV)),
AvrOpcode::LPM | AvrOpcode::LPM_Z => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::MOV))
}
AvrOpcode::ELPM => Some(BridgeInstruction::DirectMapX86(X86Opcode::MOV)),
AvrOpcode::PUSH => Some(BridgeInstruction::DirectMapX86(X86Opcode::PUSH)),
AvrOpcode::POP => Some(BridgeInstruction::DirectMapX86(X86Opcode::POP)),
AvrOpcode::BSET => Some(BridgeInstruction::DirectMapX86(X86Opcode::OR)),
AvrOpcode::BCLR => Some(BridgeInstruction::ExpandedX86(vec![
X86Opcode::NOT,
X86Opcode::AND,
])),
AvrOpcode::SBI | AvrOpcode::CBI => Some(BridgeInstruction::ExpandedX86(vec![
X86Opcode::OR,
X86Opcode::AND,
])),
AvrOpcode::BST => Some(BridgeInstruction::ExpandedX86(vec![
X86Opcode::TEST,
X86Opcode::SETNE,
])),
AvrOpcode::BLD => Some(BridgeInstruction::ExpandedX86(vec![
X86Opcode::AND,
X86Opcode::OR,
])),
AvrOpcode::NOP => Some(BridgeInstruction::DirectMapX86(X86Opcode::NOP)),
AvrOpcode::SLEEP => Some(BridgeInstruction::DirectMapX86(X86Opcode::NOP)),
AvrOpcode::WDR => Some(BridgeInstruction::DirectMapX86(X86Opcode::NOP)),
AvrOpcode::CP | AvrOpcode::CPC | AvrOpcode::CPI => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::CMP))
}
_ => {
self.stats.failed_transfers += 1;
None
}
}
}
pub fn match_shared_isel(&mut self, op_name: &str, is_avr: bool) -> Option<SharedISelMatch> {
if self.config.collect_stats {
self.stats.shared_isel_matches += 1;
}
self.shared_isel_patterns.find(op_name, is_avr)
}
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!("[AVR-X86-Bridge] {}", msg);
}
}
}
impl Default for AVRX86Bridge {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for AVRX86Bridge {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AVRX86Bridge")
.field("config", &self.config)
.field("stats", &self.stats)
.finish()
}
}
#[derive(Debug, Clone)]
pub enum BridgeInstruction {
DirectMap(AvrOpcode),
DirectMapX86(X86Opcode),
Expanded(Vec<AvrOpcode>),
ExpandedX86(Vec<X86Opcode>),
CompareMapX86 { cmp: X86Opcode, branch: X86Opcode },
}
pub struct CrossTargetAVR {
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 features: AVRFeatureSet,
}
impl CrossTargetAVR {
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(),
features: AVRFeatureSet::default(),
}
}
pub fn with_features(features: AVRFeatureSet) -> Self {
let mut ctx = Self::new();
ctx.features = features;
ctx
}
}
impl Default for CrossTargetAVR {
fn default() -> Self {
Self::new()
}
}
pub struct SharedISelPatterns {
patterns: HashMap<String, SharedPattern>,
avr_variants: HashMap<String, Vec<AvrOpcode>>,
x86_variants: HashMap<String, Vec<X86Opcode>>,
categories: HashMap<PatternCategory, Vec<String>>,
}
#[derive(Debug, Clone)]
pub struct SharedPattern {
pub name: String,
pub category: PatternCategory,
pub avr_seq: Vec<AvrOpcode>,
pub x86_seq: Vec<X86Opcode>,
pub avr_cost: u32,
pub x86_cost: u32,
pub is_commutative: bool,
pub min_operands: usize,
pub max_operands: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PatternCategory {
IntegerArithmetic,
IntegerComparison,
Logical,
Shift,
MemoryAccess,
ControlFlow,
BitManipulation,
IOOperation,
ConstantMaterialization,
}
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::IOOperation => write!(f, "IOOperation"),
Self::ConstantMaterialization => write!(f, "ConstantMaterialization"),
}
}
}
impl SharedISelPatterns {
pub fn new() -> Self {
Self {
patterns: HashMap::new(),
avr_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_io_patterns();
self.load_constant_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_8bit".into(),
category: PatternCategory::IntegerArithmetic,
avr_seq: vec![AvrOpcode::ADD],
x86_seq: vec![X86Opcode::ADD],
avr_cost: 1,
x86_cost: 1,
is_commutative: true,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "add_with_carry".into(),
category: PatternCategory::IntegerArithmetic,
avr_seq: vec![AvrOpcode::ADC],
x86_seq: vec![X86Opcode::ADC],
avr_cost: 1,
x86_cost: 1,
is_commutative: true,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "sub_8bit".into(),
category: PatternCategory::IntegerArithmetic,
avr_seq: vec![AvrOpcode::SUB],
x86_seq: vec![X86Opcode::SUB],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "sub_with_borrow".into(),
category: PatternCategory::IntegerArithmetic,
avr_seq: vec![AvrOpcode::SBC],
x86_seq: vec![X86Opcode::SBB],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "sub_immediate".into(),
category: PatternCategory::IntegerArithmetic,
avr_seq: vec![AvrOpcode::SUBI],
x86_seq: vec![X86Opcode::SUB],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "add_immediate_word".into(),
category: PatternCategory::IntegerArithmetic,
avr_seq: vec![AvrOpcode::ADIW],
x86_seq: vec![X86Opcode::ADD],
avr_cost: 2,
x86_cost: 1,
is_commutative: true,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "sub_immediate_word".into(),
category: PatternCategory::IntegerArithmetic,
avr_seq: vec![AvrOpcode::SBIW],
x86_seq: vec![X86Opcode::SUB],
avr_cost: 2,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "increment".into(),
category: PatternCategory::IntegerArithmetic,
avr_seq: vec![AvrOpcode::INC],
x86_seq: vec![X86Opcode::INC],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "decrement".into(),
category: PatternCategory::IntegerArithmetic,
avr_seq: vec![AvrOpcode::DEC],
x86_seq: vec![X86Opcode::DEC],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "negate".into(),
category: PatternCategory::IntegerArithmetic,
avr_seq: vec![AvrOpcode::NEG],
x86_seq: vec![X86Opcode::NEG],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "multiply_unsigned_8bit".into(),
category: PatternCategory::IntegerArithmetic,
avr_seq: vec![AvrOpcode::MUL],
x86_seq: vec![X86Opcode::MUL],
avr_cost: 2,
x86_cost: 3,
is_commutative: true,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "multiply_signed_8bit".into(),
category: PatternCategory::IntegerArithmetic,
avr_seq: vec![AvrOpcode::MULS],
x86_seq: vec![X86Opcode::IMUL],
avr_cost: 2,
x86_cost: 3,
is_commutative: true,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "multiply_mixed_sign_8bit".into(),
category: PatternCategory::IntegerArithmetic,
avr_seq: vec![AvrOpcode::MULSU],
x86_seq: vec![X86Opcode::IMUL],
avr_cost: 2,
x86_cost: 3,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "add_16bit".into(),
category: PatternCategory::IntegerArithmetic,
avr_seq: vec![AvrOpcode::ADD, AvrOpcode::ADC],
x86_seq: vec![X86Opcode::ADD],
avr_cost: 2,
x86_cost: 1,
is_commutative: true,
min_operands: 2,
max_operands: 2,
});
}
fn load_integer_comparison_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "compare".into(),
category: PatternCategory::IntegerComparison,
avr_seq: vec![AvrOpcode::CP],
x86_seq: vec![X86Opcode::CMP],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "compare_with_carry".into(),
category: PatternCategory::IntegerComparison,
avr_seq: vec![AvrOpcode::CPC],
x86_seq: vec![X86Opcode::CMP, X86Opcode::SBB],
avr_cost: 1,
x86_cost: 2,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "compare_immediate".into(),
category: PatternCategory::IntegerComparison,
avr_seq: vec![AvrOpcode::CPI],
x86_seq: vec![X86Opcode::CMP],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "skip_if_equal".into(),
category: PatternCategory::IntegerComparison,
avr_seq: vec![AvrOpcode::CPSE],
x86_seq: vec![X86Opcode::CMP, X86Opcode::JE],
avr_cost: 1,
x86_cost: 2,
is_commutative: true,
min_operands: 2,
max_operands: 2,
});
}
fn load_logical_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "logical_and".into(),
category: PatternCategory::Logical,
avr_seq: vec![AvrOpcode::AND],
x86_seq: vec![X86Opcode::AND],
avr_cost: 1,
x86_cost: 1,
is_commutative: true,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "and_immediate".into(),
category: PatternCategory::Logical,
avr_seq: vec![AvrOpcode::ANDI],
x86_seq: vec![X86Opcode::AND],
avr_cost: 1,
x86_cost: 1,
is_commutative: true,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "logical_or".into(),
category: PatternCategory::Logical,
avr_seq: vec![AvrOpcode::OR],
x86_seq: vec![X86Opcode::OR],
avr_cost: 1,
x86_cost: 1,
is_commutative: true,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "or_immediate".into(),
category: PatternCategory::Logical,
avr_seq: vec![AvrOpcode::ORI],
x86_seq: vec![X86Opcode::OR],
avr_cost: 1,
x86_cost: 1,
is_commutative: true,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "logical_xor".into(),
category: PatternCategory::Logical,
avr_seq: vec![AvrOpcode::EOR],
x86_seq: vec![X86Opcode::XOR],
avr_cost: 1,
x86_cost: 1,
is_commutative: true,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "complement".into(),
category: PatternCategory::Logical,
avr_seq: vec![AvrOpcode::COM],
x86_seq: vec![X86Opcode::NOT],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "set_bits_register".into(),
category: PatternCategory::Logical,
avr_seq: vec![AvrOpcode::SBR],
x86_seq: vec![X86Opcode::OR],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "clear_bits_register".into(),
category: PatternCategory::Logical,
avr_seq: vec![AvrOpcode::CBR],
x86_seq: vec![X86Opcode::AND, X86Opcode::NOT],
avr_cost: 1,
x86_cost: 2,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
}
fn load_shift_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "shift_left_logical".into(),
category: PatternCategory::Shift,
avr_seq: vec![AvrOpcode::LSL],
x86_seq: vec![X86Opcode::SHL],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "shift_right_logical".into(),
category: PatternCategory::Shift,
avr_seq: vec![AvrOpcode::LSR],
x86_seq: vec![X86Opcode::SHR],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "shift_right_arithmetic".into(),
category: PatternCategory::Shift,
avr_seq: vec![AvrOpcode::ASR],
x86_seq: vec![X86Opcode::SAR],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "rotate_left_carry".into(),
category: PatternCategory::Shift,
avr_seq: vec![AvrOpcode::ROL],
x86_seq: vec![X86Opcode::ROL],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "rotate_right_carry".into(),
category: PatternCategory::Shift,
avr_seq: vec![AvrOpcode::ROR],
x86_seq: vec![X86Opcode::ROR],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "swap_nibbles".into(),
category: PatternCategory::Shift,
avr_seq: vec![AvrOpcode::SWAP],
x86_seq: vec![X86Opcode::ROR],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
}
fn load_memory_access_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "load_direct".into(),
category: PatternCategory::MemoryAccess,
avr_seq: vec![AvrOpcode::LDS],
x86_seq: vec![X86Opcode::MOV],
avr_cost: 2,
x86_cost: 3,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "store_direct".into(),
category: PatternCategory::MemoryAccess,
avr_seq: vec![AvrOpcode::STS],
x86_seq: vec![X86Opcode::MOV],
avr_cost: 2,
x86_cost: 3,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "load_indirect_z".into(),
category: PatternCategory::MemoryAccess,
avr_seq: vec![AvrOpcode::LD_Z],
x86_seq: vec![X86Opcode::MOV],
avr_cost: 2,
x86_cost: 3,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "store_indirect_z".into(),
category: PatternCategory::MemoryAccess,
avr_seq: vec![AvrOpcode::ST_Z],
x86_seq: vec![X86Opcode::MOV],
avr_cost: 2,
x86_cost: 3,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "load_post_inc".into(),
category: PatternCategory::MemoryAccess,
avr_seq: vec![AvrOpcode::LD_ZP],
x86_seq: vec![X86Opcode::MOV, X86Opcode::INC],
avr_cost: 2,
x86_cost: 2,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "load_pre_dec".into(),
category: PatternCategory::MemoryAccess,
avr_seq: vec![AvrOpcode::LD_ZM],
x86_seq: vec![X86Opcode::DEC, X86Opcode::MOV],
avr_cost: 2,
x86_cost: 2,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "store_post_inc".into(),
category: PatternCategory::MemoryAccess,
avr_seq: vec![AvrOpcode::ST_ZP],
x86_seq: vec![X86Opcode::MOV, X86Opcode::INC],
avr_cost: 2,
x86_cost: 2,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "push".into(),
category: PatternCategory::MemoryAccess,
avr_seq: vec![AvrOpcode::PUSH],
x86_seq: vec![X86Opcode::PUSH],
avr_cost: 2,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "pop".into(),
category: PatternCategory::MemoryAccess,
avr_seq: vec![AvrOpcode::POP],
x86_seq: vec![X86Opcode::POP],
avr_cost: 2,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "load_program_memory".into(),
category: PatternCategory::MemoryAccess,
avr_seq: vec![AvrOpcode::LPM_Z],
x86_seq: vec![X86Opcode::MOV],
avr_cost: 3,
x86_cost: 3,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "extended_load_program_memory".into(),
category: PatternCategory::MemoryAccess,
avr_seq: vec![AvrOpcode::ELPM],
x86_seq: vec![X86Opcode::MOV],
avr_cost: 3,
x86_cost: 3,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "move_word".into(),
category: PatternCategory::MemoryAccess,
avr_seq: vec![AvrOpcode::MOVW],
x86_seq: vec![X86Opcode::MOV],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
}
fn load_control_flow_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "relative_jump".into(),
category: PatternCategory::ControlFlow,
avr_seq: vec![AvrOpcode::RJMP],
x86_seq: vec![X86Opcode::JMP],
avr_cost: 2,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "indirect_jump".into(),
category: PatternCategory::ControlFlow,
avr_seq: vec![AvrOpcode::IJMP],
x86_seq: vec![X86Opcode::JMP],
avr_cost: 2,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "extended_indirect_jump".into(),
category: PatternCategory::ControlFlow,
avr_seq: vec![AvrOpcode::EIJMP],
x86_seq: vec![X86Opcode::JMP],
avr_cost: 2,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "absolute_jump".into(),
category: PatternCategory::ControlFlow,
avr_seq: vec![AvrOpcode::JMP],
x86_seq: vec![X86Opcode::JMP],
avr_cost: 3,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "relative_call".into(),
category: PatternCategory::ControlFlow,
avr_seq: vec![AvrOpcode::RCALL],
x86_seq: vec![X86Opcode::CALL],
avr_cost: 3,
x86_cost: 2,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "indirect_call".into(),
category: PatternCategory::ControlFlow,
avr_seq: vec![AvrOpcode::ICALL],
x86_seq: vec![X86Opcode::CALL],
avr_cost: 3,
x86_cost: 2,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "extended_indirect_call".into(),
category: PatternCategory::ControlFlow,
avr_seq: vec![AvrOpcode::EICALL],
x86_seq: vec![X86Opcode::CALL],
avr_cost: 4,
x86_cost: 2,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "absolute_call".into(),
category: PatternCategory::ControlFlow,
avr_seq: vec![AvrOpcode::CALL],
x86_seq: vec![X86Opcode::CALL],
avr_cost: 4,
x86_cost: 2,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "return".into(),
category: PatternCategory::ControlFlow,
avr_seq: vec![AvrOpcode::RET],
x86_seq: vec![X86Opcode::RET],
avr_cost: 4,
x86_cost: 1,
is_commutative: false,
min_operands: 0,
max_operands: 0,
});
self.add_pattern(SharedPattern {
name: "return_from_interrupt".into(),
category: PatternCategory::ControlFlow,
avr_seq: vec![AvrOpcode::RETI],
x86_seq: vec![X86Opcode::RET],
avr_cost: 4,
x86_cost: 1,
is_commutative: false,
min_operands: 0,
max_operands: 0,
});
self.add_pattern(SharedPattern {
name: "nop".into(),
category: PatternCategory::ControlFlow,
avr_seq: vec![AvrOpcode::NOP],
x86_seq: vec![X86Opcode::NOP],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 0,
max_operands: 0,
});
self.add_pattern(SharedPattern {
name: "skip_if_bit_cleared".into(),
category: PatternCategory::ControlFlow,
avr_seq: vec![AvrOpcode::SBRC],
x86_seq: vec![X86Opcode::TEST, X86Opcode::JE],
avr_cost: 1,
x86_cost: 2,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "skip_if_bit_set".into(),
category: PatternCategory::ControlFlow,
avr_seq: vec![AvrOpcode::SBRS],
x86_seq: vec![X86Opcode::TEST, X86Opcode::JNE],
avr_cost: 1,
x86_cost: 2,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "branch_if_bit_cleared".into(),
category: PatternCategory::ControlFlow,
avr_seq: vec![AvrOpcode::BRBC],
x86_seq: vec![X86Opcode::JAE],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "branch_if_bit_set".into(),
category: PatternCategory::ControlFlow,
avr_seq: vec![AvrOpcode::BRBS],
x86_seq: vec![X86Opcode::JB],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
}
fn load_bit_manipulation_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "bit_set_sreg".into(),
category: PatternCategory::BitManipulation,
avr_seq: vec![AvrOpcode::BSET],
x86_seq: vec![X86Opcode::OR],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "bit_clear_sreg".into(),
category: PatternCategory::BitManipulation,
avr_seq: vec![AvrOpcode::BCLR],
x86_seq: vec![X86Opcode::AND, X86Opcode::NOT],
avr_cost: 1,
x86_cost: 2,
is_commutative: false,
min_operands: 1,
max_operands: 1,
});
self.add_pattern(SharedPattern {
name: "bit_store_from_t".into(),
category: PatternCategory::BitManipulation,
avr_seq: vec![AvrOpcode::BST],
x86_seq: vec![X86Opcode::BT],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "bit_load_to_t".into(),
category: PatternCategory::BitManipulation,
avr_seq: vec![AvrOpcode::BLD],
x86_seq: vec![X86Opcode::BT, X86Opcode::CMOVNE],
avr_cost: 1,
x86_cost: 2,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
}
fn load_io_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "io_read".into(),
category: PatternCategory::IOOperation,
avr_seq: vec![AvrOpcode::IN],
x86_seq: vec![X86Opcode::MOV],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "io_write".into(),
category: PatternCategory::IOOperation,
avr_seq: vec![AvrOpcode::OUT],
x86_seq: vec![X86Opcode::MOV],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "io_set_bit".into(),
category: PatternCategory::IOOperation,
avr_seq: vec![AvrOpcode::SBI],
x86_seq: vec![X86Opcode::OR],
avr_cost: 2,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "io_clear_bit".into(),
category: PatternCategory::IOOperation,
avr_seq: vec![AvrOpcode::CBI],
x86_seq: vec![X86Opcode::AND, X86Opcode::NOT],
avr_cost: 2,
x86_cost: 2,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "skip_io_bit_cleared".into(),
category: PatternCategory::IOOperation,
avr_seq: vec![AvrOpcode::SBIC],
x86_seq: vec![X86Opcode::TEST, X86Opcode::JE],
avr_cost: 1,
x86_cost: 2,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "skip_io_bit_set".into(),
category: PatternCategory::IOOperation,
avr_seq: vec![AvrOpcode::SBIS],
x86_seq: vec![X86Opcode::TEST, X86Opcode::JNE],
avr_cost: 1,
x86_cost: 2,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
}
fn load_constant_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "load_immediate".into(),
category: PatternCategory::ConstantMaterialization,
avr_seq: vec![AvrOpcode::LDI],
x86_seq: vec![X86Opcode::MOV],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "load_zero".into(),
category: PatternCategory::ConstantMaterialization,
avr_seq: vec![AvrOpcode::EOR],
x86_seq: vec![X86Opcode::XOR],
avr_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
self.add_pattern(SharedPattern {
name: "load_16bit_constant".into(),
category: PatternCategory::ConstantMaterialization,
avr_seq: vec![AvrOpcode::LDI, AvrOpcode::LDI],
x86_seq: vec![X86Opcode::MOV],
avr_cost: 2,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
});
}
pub fn find(&self, op_name: &str, _is_avr: bool) -> Option<SharedISelMatch> {
self.patterns.get(op_name).map(|p| SharedISelMatch {
pattern: p.clone(),
avr_sequence: p.avr_seq.clone(),
x86_sequence: p.x86_seq.clone(),
estimated_avr_cost: p.avr_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 avr_sequence: Vec<AvrOpcode>,
pub x86_sequence: Vec<X86Opcode>,
pub estimated_avr_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,
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: "lower_16bit_add_to_add_adc".into(),
targets: vec![EmbeddedTarget::AVR],
match_pattern: vec!["add_i16".into(), "a".into(), "b".into()],
replacement: vec![ReplacementStep {
operation: "add_adc_pair".into(),
inputs: vec![1, 2],
output: "result".into(),
metadata: HashMap::new(),
}],
priority: 90,
is_optimization: false,
});
self.add_rule(LoweringRule {
name: "combine_ldi_ldi_to_movw".into(),
targets: vec![EmbeddedTarget::AVR],
match_pattern: vec!["ldi".into(), "ldi".into()],
replacement: vec![ReplacementStep {
operation: "movw".into(),
inputs: vec![0, 1],
output: "pair".into(),
metadata: HashMap::new(),
}],
priority: 95,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "lower_mul_to_mul".into(),
targets: vec![EmbeddedTarget::AVR],
match_pattern: vec!["mul_i8".into(), "a".into(), "b".into()],
replacement: vec![ReplacementStep {
operation: "mul".into(),
inputs: vec![1, 2],
output: "result".into(),
metadata: HashMap::new(),
}],
priority: 85,
is_optimization: false,
});
self.add_rule(LoweringRule {
name: "lower_memcpy_to_ld_st_loop".into(),
targets: vec![EmbeddedTarget::AVR],
match_pattern: vec!["memcpy".into(), "dest".into(), "src".into(), "size".into()],
replacement: vec![ReplacementStep {
operation: "memcpy_avr_loop".into(),
inputs: vec![1, 2, 3],
output: "".into(),
metadata: HashMap::new(),
}],
priority: 70,
is_optimization: false,
});
self.add_rule(LoweringRule {
name: "use_io_for_small_addresses".into(),
targets: vec![EmbeddedTarget::AVR],
match_pattern: vec!["load".into(), "addr_ioport".into()],
replacement: vec![ReplacementStep {
operation: "in".into(),
inputs: vec![1],
output: "value".into(),
metadata: {
let mut m = HashMap::new();
m.insert("io_range".into(), "0x00-0x3F".into());
m
},
}],
priority: 92,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "use_skip_for_conditional".into(),
targets: vec![EmbeddedTarget::AVR],
match_pattern: vec!["cmp".into(), "branch".into(), "instr".into()],
replacement: vec![ReplacementStep {
operation: "skip_next".into(),
inputs: vec![0, 2],
output: "".into(),
metadata: HashMap::new(),
}],
priority: 88,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "use_sbi_cbi_for_bit_io".into(),
targets: vec![EmbeddedTarget::AVR],
match_pattern: vec!["in".into(), "ori".into(), "out".into()],
replacement: vec![ReplacementStep {
operation: "sbi".into(),
inputs: vec![0, 2],
output: "".into(),
metadata: HashMap::new(),
}],
priority: 93,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "combine_eor_self_with_next".into(),
targets: vec![EmbeddedTarget::AVR],
match_pattern: vec!["eor".into(), "add".into()],
replacement: vec![ReplacementStep {
operation: "mov".into(),
inputs: vec![1],
output: "result".into(),
metadata: HashMap::new(),
}],
priority: 90,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "use_rjmp_instead_of_jmp".into(),
targets: vec![EmbeddedTarget::AVR],
match_pattern: vec!["jmp".into(), "target".into()],
replacement: vec![ReplacementStep {
operation: "rjmp".into(),
inputs: vec![1],
output: "".into(),
metadata: {
let mut m = HashMap::new();
m.insert("max_range".into(), "±2K".into());
m
},
}],
priority: 94,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "use_rcall_instead_of_call".into(),
targets: vec![EmbeddedTarget::AVR],
match_pattern: vec!["call".into(), "target".into()],
replacement: vec![ReplacementStep {
operation: "rcall".into(),
inputs: vec![1],
output: "".into(),
metadata: {
let mut m = HashMap::new();
m.insert("max_range".into(), "±2K".into());
m
},
}],
priority: 94,
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 != "target" && ir_sequence[i + j] != pat.as_str() {
matched = false;
break;
}
}
if matched {
let desc = rule
.replacement
.iter()
.map(|s| s.operation.clone())
.collect::<Vec<_>>()
.join(", ");
return Some(desc);
}
}
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 avr_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 {
avr_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.avr_classes.push(SharedRegClass {
id: 0,
name: "GPR8".into(),
registers: (0..32).collect(),
alignment: 1,
size_bits: 8,
is_allocatable: true,
copy_cost: 1,
});
self.avr_classes.push(SharedRegClass {
id: 1,
name: "DREGS".into(),
registers: vec![24, 25, 26, 27, 28, 29, 30, 31],
alignment: 1,
size_bits: 8,
is_allocatable: true,
copy_cost: 1,
});
self.avr_classes.push(SharedRegClass {
id: 2,
name: "PTRREGS".into(),
registers: vec![26, 27, 28, 29, 30, 31],
alignment: 1,
size_bits: 8,
is_allocatable: true,
copy_cost: 2,
});
self.avr_classes.push(SharedRegClass {
id: 3,
name: "CalleeSaved".into(),
registers: (2..18).collect(),
alignment: 1,
size_bits: 8,
is_allocatable: true,
copy_cost: 1,
});
self.avr_classes.push(SharedRegClass {
id: 4,
name: "CallerSaved".into(),
registers: (18..32).collect(),
alignment: 1,
size_bits: 8,
is_allocatable: true,
copy_cost: 1,
});
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),
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_avr_cc_constraints(&self) -> Vec<RegisterConstraint> {
vec![
RegisterConstraint {
reg: 0,
constraint_type: ConstraintType::FixedAssignment,
reason: "Temporary/scratch register (R0)".into(),
},
RegisterConstraint {
reg: 1,
constraint_type: ConstraintType::FixedAssignment,
reason: "Zero register (R1)".into(),
},
RegisterConstraint {
reg: 29,
constraint_type: ConstraintType::FixedAssignment,
reason: "Frame pointer - Y high (R29)".into(),
},
RegisterConstraint {
reg: 28,
constraint_type: ConstraintType::FixedAssignment,
reason: "Frame pointer - Y low (R28)".into(),
},
RegisterConstraint {
reg: 31,
constraint_type: ConstraintType::FixedAssignment,
reason: "Z pointer - high (R31)".into(),
},
RegisterConstraint {
reg: 30,
constraint_type: ConstraintType::FixedAssignment,
reason: "Z pointer - low (R30)".into(),
},
RegisterConstraint {
reg: 24,
constraint_type: ConstraintType::FixedAssignment,
reason: "Return value - first (R24)".into(),
},
RegisterConstraint {
reg: 25,
constraint_type: ConstraintType::FixedAssignment,
reason: "Return value - second (R25)".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 avr_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(),
avr_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: 1,
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,
pub uses_y_pointer: bool,
}
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: 1,
uses_y_pointer: true,
}
}
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 compute_prologue_size(&self) -> u32 {
let mut size = 0u32;
size += self.callee_saved_slots.len() as u32 * 2; if self.has_frame_pointer && self.uses_y_pointer {
size += 4; }
if self.frame_size > 0 {
size += 2;
}
size
}
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 CrossTargetConstMaterializer {
pub has_lpm: bool,
pub max_immediate_8bit: i16,
}
impl CrossTargetConstMaterializer {
pub fn new() -> Self {
Self {
has_lpm: true,
max_immediate_8bit: 255,
}
}
pub fn materialize_avr_8bit(&self, value: u8) -> Vec<AvrOpcode> {
if value == 0 {
vec![AvrOpcode::EOR] } else if value <= 63 {
vec![AvrOpcode::LDI] } else if value <= 255 {
vec![AvrOpcode::LDI]
} else {
vec![AvrOpcode::LDI, AvrOpcode::LDI]
}
}
pub fn materialize_avr_16bit(&self, value: u16) -> Vec<AvrOpcode> {
let lo = (value & 0xFF) as u8;
let hi = ((value >> 8) & 0xFF) as u8;
if lo == 0 && hi == 0 {
vec![AvrOpcode::EOR, AvrOpcode::EOR]
} else {
vec![AvrOpcode::LDI, AvrOpcode::LDI] }
}
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 backward_branches: u64,
pub forward_branches: u64,
pub avg_branch_distance: f64,
pub indirect_branches: 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("br") || lower.contains("jmp") || lower.contains("call") {
self.branch_stats.total_branches += 1;
if lower.starts_with("br") {
self.branch_stats.conditional_branches += 1;
} else {
self.branch_stats.unconditional_branches += 1;
}
}
}
}
pub fn compare(&self, avr_branches: u64, x86_branches: u64) -> BranchComparison {
let ratio = if x86_branches > 0 {
avr_branches as f64 / x86_branches as f64
} else {
f64::INFINITY
};
BranchComparison {
avr_branch_count: avr_branches,
x86_branch_count: x86_branches,
ratio,
assessment: if ratio < 0.9 {
"AVR uses fewer branches".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 avr_branch_count: u64,
pub x86_branch_count: u64,
pub ratio: f64,
pub assessment: String,
}
#[derive(Debug, Clone, Default)]
pub struct AVRFeatureSet {
pub enabled: HashSet<String>,
pub disabled: HashSet<String>,
}
impl AVRFeatureSet {
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 atmega328p() -> Self {
let mut fs = Self::new();
fs.enable("avr5");
fs.enable("mul");
fs.enable("movw");
fs.enable("lpm");
fs.enable("elpm");
fs.enable("jmp_call");
fs.enable("adiw_sbiw");
fs
}
pub fn attiny85() -> Self {
let mut fs = Self::new();
fs.enable("avr25");
fs.enable("lpm");
fs.enable("movw");
fs
}
pub fn atxmega() -> Self {
let mut fs = Self::new();
fs.enable("avrxmega");
fs.enable("mul");
fs.enable("movw");
fs.enable("lpm");
fs.enable("elpm");
fs.enable("jmp_call");
fs.enable("adiw_sbiw");
fs.enable("xch");
fs.enable("las");
fs.enable("lac");
fs.enable("lat");
fs
}
}
#[derive(Debug, Clone)]
pub struct BridgeAVRTargetMachine {
pub triple: String,
pub cpu: String,
pub features: String,
pub avr_family: String,
pub data_layout: String,
pub pointer_size: u32,
pub flash_size: u32,
pub sram_size: u32,
pub eeprom_size: u32,
pub feature_set: AVRFeatureSet,
}
pub const AVR_FAMILIES: &[&str] = &[
"avr1", "avr2", "avr25", "avr3", "avr35", "avr4", "avr5", "avr51", "avr6", "avrxmega",
];
pub const AVR_DATA_LAYOUT: &str = "e-p:16:16-i8:8-i16:16-i32:32-a:0:16-n8";
impl BridgeAVRTargetMachine {
pub fn new(family: &str) -> Self {
let fm = family.to_lowercase();
let feature_str = Self::features_for_family(&fm);
let flash = Self::flash_for_family(&fm);
let sram = Self::sram_for_family(&fm);
Self {
triple: format!("avr-unknown-elf-{}", fm),
cpu: format!("atmega-{}", fm),
features: feature_str.clone(),
avr_family: fm.clone(),
data_layout: AVR_DATA_LAYOUT.to_string(),
pointer_size: 2,
flash_size: flash,
sram_size: sram,
eeprom_size: 1024,
feature_set: Self::parse_features(&feature_str),
}
}
fn features_for_family(family: &str) -> String {
match family {
"avr1" => "".into(),
"avr2" | "avr25" => "+lpm,+movw".into(),
"avr3" | "avr35" => "+lpm,+movw,+adiw_sbiw".into(),
"avr4" | "avr5" | "avr51" => "+mul,+movw,+lpm,+elpm,+jmp_call,+adiw_sbiw".into(),
"avr6" => "+mul,+movw,+lpm,+elpm,+jmp_call,+adiw_sbiw".into(),
"avrxmega" => "+mul,+movw,+lpm,+elpm,+jmp_call,+adiw_sbiw,+xch,+las,+lac,+lat".into(),
_ => "+mul,+movw,+lpm,+adiw_sbiw".into(),
}
}
fn flash_for_family(family: &str) -> u32 {
match family {
"avr1" => 1024,
"avr2" => 2048,
"avr25" => 8192,
"avr3" => 4096,
"avr35" => 16384,
"avr4" => 8192,
"avr5" => 32768,
"avr51" => 131072,
"avr6" => 262144,
"avrxmega" => 393216,
_ => 32768,
}
}
fn sram_for_family(family: &str) -> u32 {
match family {
"avr1" => 128,
"avr2" => 128,
"avr25" => 512,
"avr3" => 256,
"avr35" => 1024,
"avr4" => 512,
"avr5" => 2048,
"avr51" => 16384,
"avr6" => 8192,
"avrxmega" => 32768,
_ => 2048,
}
}
fn parse_features(features: &str) -> AVRFeatureSet {
let mut fs = AVRFeatureSet::new();
for feat in features.split(',') {
let f = feat.trim();
if f.starts_with('+') {
fs.enable(&f[1..]);
} else if f.starts_with('-') {
fs.disable(&f[1..]);
} else if !f.is_empty() {
fs.enable(f);
}
}
fs
}
pub fn new_atmega328p() -> Self {
Self::new("avr5")
}
pub fn new_attiny85() -> Self {
Self::new("avr25")
}
pub fn new_atxmega() -> Self {
Self::new("avrxmega")
}
pub fn get_triple(&self) -> &str {
&self.triple
}
pub fn has_feature(&self, name: &str) -> bool {
self.feature_set.has(name)
}
pub fn describe(&self) -> String {
format!(
"AVR Target Machine:\n Family: {}\n CPU: {}\n Features: {}\n Flash: {} bytes\n SRAM: {} bytes\n EEPROM: {} bytes\n Data Layout: {}",
self.avr_family,
self.cpu,
self.features,
self.flash_size,
self.sram_size,
self.eeprom_size,
self.data_layout,
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AvrBridgeReg {
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,
R26,
R27,
R28,
R29,
R30,
R31,
}
impl AvrBridgeReg {
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::R26 => 26,
Self::R27 => 27,
Self::R28 => 28,
Self::R29 => 29,
Self::R30 => 30,
Self::R31 => 31,
}
}
pub fn is_special(&self) -> bool {
matches!(self, Self::R0 | Self::R1)
}
pub fn is_callee_saved(&self) -> bool {
let idx = self.idx();
(idx >= 2 && idx <= 17) || idx == 28 || idx == 29
}
pub fn can_use_ldi(&self) -> bool {
self.idx() >= 16
}
pub fn is_x_ptr(&self) -> bool {
matches!(self, Self::R26 | Self::R27)
}
pub fn is_y_ptr(&self) -> bool {
matches!(self, Self::R28 | Self::R29)
}
pub fn is_z_ptr(&self) -> bool {
matches!(self, Self::R30 | Self::R31)
}
pub fn ptr_pair(&self) -> Option<AvrBridgeReg> {
match self {
Self::R26 => Some(Self::R27),
Self::R27 => Some(Self::R26),
Self::R28 => Some(Self::R29),
Self::R29 => Some(Self::R28),
Self::R30 => Some(Self::R31),
Self::R31 => Some(Self::R30),
_ => None,
}
}
pub fn all_regs() -> Vec<AvrBridgeReg> {
(0..32).map(|i| Self::from_idx(i).unwrap()).collect()
}
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::R26),
27 => Some(Self::R27),
28 => Some(Self::R28),
29 => Some(Self::R29),
30 => Some(Self::R30),
31 => Some(Self::R31),
_ => None,
}
}
}
pub struct BridgeAVRInstrInfo {
pub core: AvrInstrInfo,
pub arithmetic_ops: Vec<AvrOpcode>,
pub logical_ops: Vec<AvrOpcode>,
pub shift_ops: Vec<AvrOpcode>,
pub branch_ops: Vec<AvrOpcode>,
pub data_transfer_ops: Vec<AvrOpcode>,
pub bit_ops: Vec<AvrOpcode>,
pub io_ops: Vec<AvrOpcode>,
pub control_ops: Vec<AvrOpcode>,
pub terminator_ops: Vec<AvrOpcode>,
}
impl BridgeAVRInstrInfo {
pub fn new() -> Self {
let mut info = Self {
core: AvrInstrInfo::new(),
arithmetic_ops: Vec::new(),
logical_ops: Vec::new(),
shift_ops: Vec::new(),
branch_ops: Vec::new(),
data_transfer_ops: Vec::new(),
bit_ops: Vec::new(),
io_ops: Vec::new(),
control_ops: Vec::new(),
terminator_ops: Vec::new(),
};
info.categorize();
info
}
fn categorize(&mut self) {
self.arithmetic_ops = vec![
AvrOpcode::ADD,
AvrOpcode::ADC,
AvrOpcode::ADIW,
AvrOpcode::SUB,
AvrOpcode::SUBI,
AvrOpcode::SBC,
AvrOpcode::SBCI,
AvrOpcode::SBIW,
AvrOpcode::INC,
AvrOpcode::DEC,
AvrOpcode::MUL,
AvrOpcode::MULS,
AvrOpcode::MULSU,
];
self.logical_ops = vec![
AvrOpcode::AND,
AvrOpcode::ANDI,
AvrOpcode::OR,
AvrOpcode::ORI,
AvrOpcode::EOR,
AvrOpcode::COM,
AvrOpcode::NEG,
AvrOpcode::SBR,
AvrOpcode::CBR,
];
self.shift_ops = vec![
AvrOpcode::LSL,
AvrOpcode::LSR,
AvrOpcode::ROL,
AvrOpcode::ROR,
AvrOpcode::ASR,
AvrOpcode::SWAP,
];
self.branch_ops = vec![
AvrOpcode::RJMP,
AvrOpcode::IJMP,
AvrOpcode::EIJMP,
AvrOpcode::JMP,
AvrOpcode::RCALL,
AvrOpcode::ICALL,
AvrOpcode::EICALL,
AvrOpcode::CALL,
AvrOpcode::RET,
AvrOpcode::RETI,
];
self.data_transfer_ops = vec![
AvrOpcode::MOV,
AvrOpcode::MOVW,
AvrOpcode::LDI,
AvrOpcode::LD,
AvrOpcode::ST,
AvrOpcode::LDS,
AvrOpcode::STS,
AvrOpcode::LD_X,
AvrOpcode::LD_XP,
AvrOpcode::LD_XM,
AvrOpcode::ST_X,
AvrOpcode::ST_XP,
AvrOpcode::ST_XM,
AvrOpcode::LD_Y,
AvrOpcode::LD_YP,
AvrOpcode::LD_YM,
AvrOpcode::ST_Y,
AvrOpcode::ST_YP,
AvrOpcode::ST_YM,
AvrOpcode::LD_Z,
AvrOpcode::LD_ZP,
AvrOpcode::LD_ZM,
AvrOpcode::ST_Z,
AvrOpcode::ST_ZP,
AvrOpcode::ST_ZM,
AvrOpcode::LPM,
AvrOpcode::LPM_Z,
AvrOpcode::LPM_ZP,
AvrOpcode::ELPM,
AvrOpcode::PUSH,
AvrOpcode::POP,
];
self.bit_ops = vec![
AvrOpcode::BSET,
AvrOpcode::BCLR,
AvrOpcode::SBI,
AvrOpcode::CBI,
AvrOpcode::BST,
AvrOpcode::BLD,
AvrOpcode::SEC,
AvrOpcode::CLC,
AvrOpcode::SEN,
AvrOpcode::CLN,
AvrOpcode::SEZ,
AvrOpcode::CLZ,
AvrOpcode::SEI,
AvrOpcode::CLI,
AvrOpcode::SES,
AvrOpcode::CLS,
AvrOpcode::SEV,
AvrOpcode::CLV,
AvrOpcode::SET,
AvrOpcode::CLT,
AvrOpcode::SEH,
AvrOpcode::CLH,
];
self.io_ops = vec![
AvrOpcode::IN,
AvrOpcode::OUT,
AvrOpcode::SBI,
AvrOpcode::CBI,
AvrOpcode::SBIC,
AvrOpcode::SBIS,
];
self.control_ops = vec![AvrOpcode::NOP, AvrOpcode::SLEEP, AvrOpcode::WDR];
self.terminator_ops = vec![
AvrOpcode::RJMP,
AvrOpcode::IJMP,
AvrOpcode::JMP,
AvrOpcode::EIJMP,
AvrOpcode::RET,
AvrOpcode::RETI,
];
}
pub fn instruction_counts(&self) -> InstructionCounts {
InstructionCounts {
arithmetic: self.arithmetic_ops.len() as u32,
logical: self.logical_ops.len() as u32,
shift: self.shift_ops.len() as u32,
branch: self.branch_ops.len() as u32,
data_transfer: self.data_transfer_ops.len() as u32,
bit: self.bit_ops.len() as u32,
io: self.io_ops.len() as u32,
control: self.control_ops.len() as u32,
}
}
}
#[derive(Debug, Clone)]
pub struct InstructionCounts {
pub arithmetic: u32,
pub logical: u32,
pub shift: u32,
pub branch: u32,
pub data_transfer: u32,
pub bit: u32,
pub io: u32,
pub control: u32,
}
#[derive(Debug, Clone)]
pub struct AVRCallingConventionBridge {
pub arg_regs: Vec<u32>,
pub ret_regs: Vec<u32>,
pub callee_saved: Vec<u32>,
pub caller_saved: Vec<u32>,
pub stack_alignment: u32,
pub ptr_regs: Vec<u32>,
}
impl AVRCallingConventionBridge {
pub fn new() -> Self {
Self {
arg_regs: (8..=25).rev().collect(),
ret_regs: vec![24, 25],
callee_saved: {
let mut v: Vec<u32> = (2..=17).collect();
v.push(28);
v.push(29);
v
},
caller_saved: {
let mut v = vec![0u32];
(18..=27).for_each(|i| v.push(i));
v.push(30);
v.push(31);
v
},
stack_alignment: 1,
ptr_regs: vec![26, 27, 28, 29, 30, 31],
}
}
pub fn get_arg_register(&self, n: usize) -> Option<u32> {
if n < self.arg_regs.len() {
Some(self.arg_regs[n])
} else {
None
}
}
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) -> CCComparisonX86 {
CCComparisonX86 {
avr_arg_regs: self.arg_regs.len() as u32,
x86_arg_regs: 6,
avr_ret_regs: self.ret_regs.len() as u32,
x86_ret_regs: 2,
avr_callee_saved: self.callee_saved.len() as u32,
x86_callee_saved: 5,
avr_caller_saved: self.caller_saved.len() as u32,
x86_caller_saved: 9,
}
}
}
#[derive(Debug, Clone)]
pub struct CCComparisonX86 {
pub avr_arg_regs: u32,
pub x86_arg_regs: u32,
pub avr_ret_regs: u32,
pub x86_ret_regs: u32,
pub avr_callee_saved: u32,
pub x86_callee_saved: u32,
pub avr_caller_saved: u32,
pub x86_caller_saved: u32,
}
#[derive(Debug, Clone)]
pub struct AVRIONameSpace {
pub io_registers: HashMap<u8, String>,
}
impl AVRIONameSpace {
pub fn new() -> Self {
let mut io = Self {
io_registers: HashMap::new(),
};
io.populate_standard();
io
}
fn populate_standard(&mut self) {
let standard_io: &[(u8, &str)] = &[
(0x00, "PINA"),
(0x01, "DDRA"),
(0x02, "PORTA"),
(0x03, "PINB"),
(0x04, "DDRB"),
(0x05, "PORTB"),
(0x06, "PINC"),
(0x07, "DDRC"),
(0x08, "PORTC"),
(0x09, "PIND"),
(0x0A, "DDRD"),
(0x0B, "PORTD"),
(0x0C, "PINE"),
(0x0D, "DDRE"),
(0x0E, "PORTE"),
(0x0F, "PINF"),
(0x10, "DDRF"),
(0x11, "PORTF"),
(0x15, "TIFR0"),
(0x16, "TIFR1"),
(0x17, "TIFR2"),
(0x1B, "PCIFR"),
(0x1C, "EIFR"),
(0x1D, "EIMSK"),
(0x1E, "GPIOR0"),
(0x1F, "EECR"),
(0x20, "EEDR"),
(0x21, "EEARL"),
(0x22, "EEARH"),
(0x23, "GTCCR"),
(0x24, "TCCR0A"),
(0x25, "TCCR0B"),
(0x26, "TCNT0"),
(0x27, "OCR0A"),
(0x28, "OCR0B"),
(0x2A, "GPIOR1"),
(0x2B, "GPIOR2"),
(0x2C, "SPCR"),
(0x2D, "SPSR"),
(0x2E, "SPDR"),
(0x30, "ACSR"),
(0x33, "SMCR"),
(0x34, "MCUSR"),
(0x35, "MCUCR"),
(0x37, "SPMCSR"),
(0x3D, "SPL"),
(0x3E, "SPH"),
(0x3F, "SREG"),
];
for &(addr, name) in standard_io {
self.io_registers.insert(addr, name.to_string());
}
}
pub fn get_name(&self, addr: u8) -> Option<&String> {
self.io_registers.get(&addr)
}
pub fn get_addr(&self, name: &str) -> Option<u8> {
self.io_registers
.iter()
.find(|(_, n)| n.as_str() == name)
.map(|(a, _)| *a)
}
pub fn is_io_range(&self, addr: u8) -> bool {
addr <= 0x3F
}
pub fn is_sbi_cbi_range(&self, addr: u8) -> bool {
addr <= 0x1F
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bridge_creation() {
let bridge = AVRX86Bridge::new();
assert_eq!(bridge.config.avr_family, "avr5");
assert!(!bridge.config.verbose);
}
#[test]
fn test_translate_x86_to_avr_add() {
let mut bridge = AVRX86Bridge::new();
let result = bridge.translate_x86_to_avr(&X86Opcode::ADD);
assert!(result.is_some());
match result.unwrap() {
BridgeInstruction::DirectMap(op) => assert_eq!(op, AvrOpcode::ADD),
_ => panic!("Expected DirectMap"),
}
}
#[test]
fn test_translate_avr_to_x86_eor() {
let mut bridge = AVRX86Bridge::new();
let result = bridge.translate_avr_to_x86(&AvrOpcode::EOR);
assert!(result.is_some());
match result.unwrap() {
BridgeInstruction::DirectMapX86(op) => assert_eq!(op, X86Opcode::XOR),
_ => panic!("Expected DirectMapX86"),
}
}
#[test]
fn test_translate_avr_to_x86_com() {
let mut bridge = AVRX86Bridge::new();
let result = bridge.translate_avr_to_x86(&AvrOpcode::COM);
assert!(result.is_some());
match result.unwrap() {
BridgeInstruction::DirectMapX86(op) => assert_eq!(op, X86Opcode::NOT),
_ => panic!("Expected DirectMapX86"),
}
}
#[test]
fn test_pattern_loading() {
let patterns = SharedISelPatterns::default();
assert!(patterns.pattern_count() > 30);
}
#[test]
fn test_find_pattern() {
let patterns = SharedISelPatterns::default();
let result = patterns.find("add_8bit", true);
assert!(result.is_some());
let m = result.unwrap();
assert_eq!(m.avr_sequence[0], AvrOpcode::ADD);
assert_eq!(m.x86_sequence[0], X86Opcode::ADD);
}
#[test]
fn test_lowering_rules() {
let rules = CrossTargetLowering::default();
assert!(rules.rule_count() >= 8);
}
#[test]
fn test_ra_context() {
let ctx = SharedRegisterAllocContext::default();
assert_eq!(ctx.avr_classes.len(), 5);
assert_eq!(ctx.x86_classes.len(), 2);
}
#[test]
fn test_ra_allocate_free() {
let mut ctx = SharedRegisterAllocContext::new();
ctx.initialize();
ctx.allocate_register(100, 18); assert!(ctx.is_register_available(19));
assert!(!ctx.is_register_available(18));
ctx.free_register(18);
assert!(ctx.is_register_available(18));
}
#[test]
fn test_target_machine_atmega() {
let tm = BridgeAVRTargetMachine::new_atmega328p();
assert_eq!(tm.avr_family, "avr5");
assert_eq!(tm.pointer_size, 2);
assert!(tm.has_feature("mul"));
}
#[test]
fn test_target_machine_attiny() {
let tm = BridgeAVRTargetMachine::new_attiny85();
assert_eq!(tm.avr_family, "avr25");
assert!(!tm.has_feature("mul")); }
#[test]
fn test_target_machine_describe() {
let tm = BridgeAVRTargetMachine::new_atxmega();
let desc = tm.describe();
assert!(desc.contains("avrxmega"));
}
#[test]
fn test_avr_families_list() {
assert_eq!(AVR_FAMILIES.len(), 10);
assert!(AVR_FAMILIES.contains(&"avr5"));
assert!(AVR_FAMILIES.contains(&"avrxmega"));
}
#[test]
fn test_register_enumeration() {
let regs = AvrBridgeReg::all_regs();
assert_eq!(regs.len(), 32);
assert!(AvrBridgeReg::R0.is_special());
assert!(AvrBridgeReg::R1.is_special());
assert!(!AvrBridgeReg::R5.is_special());
}
#[test]
fn test_register_callee_saved() {
assert!(AvrBridgeReg::R2.is_callee_saved());
assert!(AvrBridgeReg::R17.is_callee_saved());
assert!(!AvrBridgeReg::R18.is_callee_saved());
}
#[test]
fn test_register_ldi_capable() {
assert!(!AvrBridgeReg::R0.can_use_ldi());
assert!(AvrBridgeReg::R16.can_use_ldi());
assert!(AvrBridgeReg::R31.can_use_ldi());
}
#[test]
fn test_register_ptr_pairs() {
assert_eq!(AvrBridgeReg::R26.ptr_pair(), Some(AvrBridgeReg::R27));
assert_eq!(AvrBridgeReg::R27.ptr_pair(), Some(AvrBridgeReg::R26));
assert_eq!(AvrBridgeReg::R5.ptr_pair(), None);
}
#[test]
fn test_calling_convention() {
let cc = AVRCallingConventionBridge::new();
assert_eq!(cc.arg_regs.len(), 18); assert_eq!(cc.ret_regs, vec![24, 25]);
assert_eq!(cc.get_return_register(), 24);
}
#[test]
fn test_calling_convention_args() {
let cc = AVRCallingConventionBridge::new();
assert_eq!(cc.get_arg_register(0), Some(25)); assert_eq!(cc.get_arg_register(1), Some(24)); assert_eq!(cc.get_arg_register(17), Some(8)); assert_eq!(cc.get_arg_register(18), None); }
#[test]
fn test_io_space() {
let io = AVRIONameSpace::new();
assert_eq!(io.get_name(0x3F), Some(&"SREG".to_string()));
assert_eq!(io.get_name(0x3D), Some(&"SPL".to_string()));
assert!(io.is_io_range(0x3F));
assert!(!io.is_io_range(0x40));
assert!(io.is_sbi_cbi_range(0x1F));
assert!(!io.is_sbi_cbi_range(0x20));
}
#[test]
fn test_io_addr_lookup() {
let io = AVRIONameSpace::new();
assert_eq!(io.get_addr("SREG"), Some(0x3F));
assert_eq!(io.get_addr("NONEXISTENT"), None);
}
#[test]
fn test_const_materializer() {
let cm = CrossTargetConstMaterializer::new();
assert_eq!(cm.materialize_avr_8bit(0), vec![AvrOpcode::EOR]);
assert_eq!(cm.materialize_avr_8bit(42), vec![AvrOpcode::LDI]);
assert_eq!(
cm.materialize_avr_16bit(0),
vec![AvrOpcode::EOR, AvrOpcode::EOR]
);
}
#[test]
fn test_frame_context() {
let mut ctx = SharedFrameLoweringContext::new();
ctx.has_frame_pointer = true;
ctx.uses_y_pointer = true;
ctx.frame_size = 4;
ctx.add_callee_saved_slot(2, -1);
ctx.add_callee_saved_slot(3, -2);
assert_eq!(ctx.compute_prologue_size(), 10); }
#[test]
fn test_instr_info_categorization() {
let info = BridgeAVRInstrInfo::new();
let counts = info.instruction_counts();
assert!(counts.arithmetic >= 13);
assert!(counts.logical >= 9);
assert!(counts.shift >= 6);
assert!(counts.branch >= 10);
assert!(counts.io >= 6);
}
#[test]
fn test_feature_set() {
let fs = AVRFeatureSet::atmega328p();
assert!(fs.has("avr5"));
assert!(fs.has("mul"));
assert!(fs.has("movw"));
assert!(!fs.has("xch"));
}
}