use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::fmt;
use crate::riscv::riscv_calling_convention::{
RiscVArgClass, RiscVCallFrame, RiscVCallingConvention,
};
use crate::riscv::riscv_frame_lowering::{RiscVFrameInfo, RiscVFrameLowering};
use crate::riscv::riscv_instr_info::{
RiscVInstrDesc, RiscVInstrInfo, RiscVOpcode, RiscVOperandType,
};
use crate::riscv::riscv_register_info::{
RiscVRegClass, RiscVRegisterInfo, FPR_BASE, GPR_BASE, RV_FPR_COUNT, RV_GPR_COUNT, RV_MAX_REG_ID,
};
use crate::riscv::riscv_target_machine::{
CodeModel, OptimizationLevel, RelocModel, RiscVTargetMachine, DEFAULT_RV32_DATA_LAYOUT,
DEFAULT_RV64_DATA_LAYOUT,
};
use crate::riscv::RiscVPeepholeOptimizer;
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 RISCVX86Bridge {
pub riscv_instr_info: RiscVInstrInfo,
pub x86_instr_info: X86InstrInfo,
pub riscv_reg_info: RiscVRegisterInfo,
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,
}
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,
}
}
}
#[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, "RISC-V ↔ 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 RISCVX86Bridge {
pub fn new() -> Self {
Self {
riscv_instr_info: RiscVInstrInfo::new(),
x86_instr_info: X86InstrInfo::new(),
riscv_reg_info: RiscVRegisterInfo,
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 RISC-V ↔ 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_riscv(&mut self, x86_opcode: &X86Opcode) -> Option<BridgeInstruction> {
self.stats.pattern_translations += 1;
match x86_opcode {
X86Opcode::ADD => Some(BridgeInstruction::DirectMap(RiscVOpcode::ADD)),
X86Opcode::SUB => Some(BridgeInstruction::DirectMap(RiscVOpcode::SUB)),
X86Opcode::AND => Some(BridgeInstruction::DirectMap(RiscVOpcode::AND)),
X86Opcode::OR => Some(BridgeInstruction::DirectMap(RiscVOpcode::OR)),
X86Opcode::XOR => Some(BridgeInstruction::DirectMap(RiscVOpcode::XOR)),
X86Opcode::MUL => Some(BridgeInstruction::Expanded(vec![RiscVOpcode::MUL])),
X86Opcode::IMUL => Some(BridgeInstruction::Expanded(vec![
RiscVOpcode::MUL,
RiscVOpcode::MULH,
])),
X86Opcode::DIV => Some(BridgeInstruction::Expanded(vec![
RiscVOpcode::DIV,
RiscVOpcode::REM,
])),
X86Opcode::IDIV => Some(BridgeInstruction::Expanded(vec![
RiscVOpcode::DIV,
RiscVOpcode::REM,
])),
X86Opcode::CMP => Some(BridgeInstruction::CompareMap {
slt: RiscVOpcode::SLT,
sltu: RiscVOpcode::SLTU,
}),
X86Opcode::MOV => Some(BridgeInstruction::DirectMap(RiscVOpcode::ADDI)),
X86Opcode::LEA => Some(BridgeInstruction::ArithmeticExpand(vec![
RiscVOpcode::SLLI,
RiscVOpcode::ADDI,
RiscVOpcode::ADD,
])),
X86Opcode::SHL => Some(BridgeInstruction::DirectMap(RiscVOpcode::SLL)),
X86Opcode::SHR => Some(BridgeInstruction::DirectMap(RiscVOpcode::SRL)),
X86Opcode::SAR => Some(BridgeInstruction::DirectMap(RiscVOpcode::SRA)),
X86Opcode::JMP => Some(BridgeInstruction::DirectMap(RiscVOpcode::JAL)),
X86Opcode::CALL => Some(BridgeInstruction::DirectMap(RiscVOpcode::CALL)),
X86Opcode::RET => Some(BridgeInstruction::DirectMap(RiscVOpcode::RET)),
X86Opcode::NOP => Some(BridgeInstruction::DirectMap(RiscVOpcode::NOP)),
X86Opcode::TEST => Some(BridgeInstruction::CompareMap {
slt: RiscVOpcode::SLT,
sltu: RiscVOpcode::SLTU,
}),
X86Opcode::PUSH => Some(BridgeInstruction::MemoryExpand(vec![
RiscVOpcode::ADDI,
RiscVOpcode::SW,
])),
X86Opcode::POP => Some(BridgeInstruction::MemoryExpand(vec![
RiscVOpcode::LW,
RiscVOpcode::ADDI,
])),
_ => {
self.stats.failed_transfers += 1;
None
}
}
}
pub fn translate_riscv_to_x86(
&mut self,
riscv_opcode: &RiscVOpcode,
) -> Option<BridgeInstruction> {
self.stats.pattern_translations += 1;
match riscv_opcode {
RiscVOpcode::ADD | RiscVOpcode::ADDI => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::ADD))
}
RiscVOpcode::SUB => Some(BridgeInstruction::DirectMapX86(X86Opcode::SUB)),
RiscVOpcode::AND | RiscVOpcode::ANDI => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::AND))
}
RiscVOpcode::OR | RiscVOpcode::ORI => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::OR))
}
RiscVOpcode::XOR | RiscVOpcode::XORI => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::XOR))
}
RiscVOpcode::SLL | RiscVOpcode::SLLI => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::SHL))
}
RiscVOpcode::SRL | RiscVOpcode::SRLI => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::SHR))
}
RiscVOpcode::SRA | RiscVOpcode::SRAI => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::SAR))
}
RiscVOpcode::MUL => Some(BridgeInstruction::DirectMapX86(X86Opcode::IMUL)),
RiscVOpcode::DIV => Some(BridgeInstruction::DirectMapX86(X86Opcode::IDIV)),
RiscVOpcode::REM => Some(BridgeInstruction::ExpandedX86(vec![
X86Opcode::IDIV,
X86Opcode::MOV,
])),
RiscVOpcode::SLT | RiscVOpcode::SLTI => {
Some(BridgeInstruction::DirectMapX86(X86Opcode::CMP))
}
RiscVOpcode::JAL => Some(BridgeInstruction::DirectMapX86(X86Opcode::JMP)),
RiscVOpcode::JALR => Some(BridgeInstruction::DirectMapX86(X86Opcode::JMP)),
RiscVOpcode::CALL => Some(BridgeInstruction::DirectMapX86(X86Opcode::CALL)),
RiscVOpcode::RET => Some(BridgeInstruction::DirectMapX86(X86Opcode::RET)),
RiscVOpcode::NOP => Some(BridgeInstruction::DirectMapX86(X86Opcode::NOP)),
RiscVOpcode::LB => Some(BridgeInstruction::DirectMapX86(X86Opcode::MOV)),
RiscVOpcode::LW => Some(BridgeInstruction::DirectMapX86(X86Opcode::MOV)),
RiscVOpcode::LD => Some(BridgeInstruction::DirectMapX86(X86Opcode::MOV)),
RiscVOpcode::SB => Some(BridgeInstruction::DirectMapX86(X86Opcode::MOV)),
RiscVOpcode::SW => Some(BridgeInstruction::DirectMapX86(X86Opcode::MOV)),
RiscVOpcode::SD => Some(BridgeInstruction::DirectMapX86(X86Opcode::MOV)),
_ => {
self.stats.failed_transfers += 1;
None
}
}
}
pub fn match_shared_isel(&mut self, op_name: &str, is_riscv: bool) -> Option<SharedISelMatch> {
if self.config.collect_stats {
self.stats.shared_isel_matches += 1;
}
self.shared_isel_patterns.find(op_name, is_riscv)
}
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!("[RISCV-X86-Bridge] {}", msg);
}
}
}
impl Default for RISCVX86Bridge {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for RISCVX86Bridge {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RISCVX86Bridge")
.field("config", &self.config)
.field("stats", &self.stats)
.finish()
}
}
#[derive(Debug, Clone)]
pub enum BridgeInstruction {
DirectMap(RiscVOpcode),
DirectMapX86(X86Opcode),
Expanded(Vec<RiscVOpcode>),
ExpandedX86(Vec<X86Opcode>),
CompareMap { slt: RiscVOpcode, sltu: RiscVOpcode },
ArithmeticExpand(Vec<RiscVOpcode>),
MemoryExpand(Vec<RiscVOpcode>),
FloatMap {
riscv: Vec<RiscVOpcode>,
x86: Vec<X86Opcode>,
},
}
pub struct CrossTargetRISCV {
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: TargetFeatureSet,
}
impl CrossTargetRISCV {
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: TargetFeatureSet::default(),
}
}
pub fn with_features(features: TargetFeatureSet) -> Self {
let mut ctx = Self::new();
ctx.features = features;
ctx
}
}
impl Default for CrossTargetRISCV {
fn default() -> Self {
Self::new()
}
}
pub struct SharedISelPatterns {
patterns: HashMap<String, SharedPattern>,
riscv_variants: HashMap<String, Vec<RiscVOpcode>>,
x86_variants: HashMap<String, Vec<X86Opcode>>,
categories: HashMap<PatternCategory, Vec<String>>,
}
#[derive(Debug, Clone)]
pub struct SharedPattern {
pub name: String,
pub category: PatternCategory,
pub riscv_seq: Vec<RiscVOpcode>,
pub x86_seq: Vec<X86Opcode>,
pub riscv_cost: u32,
pub x86_cost: u32,
pub is_commutative: bool,
pub min_operands: usize,
pub max_operands: usize,
pub riscv_features: Vec<String>,
pub x86_features: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PatternCategory {
IntegerArithmetic,
IntegerComparison,
Logical,
Shift,
MemoryAccess,
ControlFlow,
FloatArithmetic,
FloatComparison,
Conversion,
ConstantMaterialization,
VectorOperation,
AtomicOperation,
}
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::FloatArithmetic => write!(f, "FloatArithmetic"),
Self::FloatComparison => write!(f, "FloatComparison"),
Self::Conversion => write!(f, "Conversion"),
Self::ConstantMaterialization => write!(f, "ConstantMaterialization"),
Self::VectorOperation => write!(f, "VectorOperation"),
Self::AtomicOperation => write!(f, "AtomicOperation"),
}
}
}
impl SharedISelPatterns {
pub fn new() -> Self {
Self {
patterns: HashMap::new(),
riscv_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_float_patterns();
self.load_conversion_patterns();
self.load_constant_patterns();
self.load_vector_patterns();
self.load_atomic_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_with_carry".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![RiscVOpcode::ADD, RiscVOpcode::SLTU, RiscVOpcode::ADD],
x86_seq: vec![X86Opcode::ADC],
riscv_cost: 3,
x86_cost: 1,
is_commutative: true,
min_operands: 3,
max_operands: 4,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "sub_with_borrow".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![RiscVOpcode::SUB, RiscVOpcode::SLTU, RiscVOpcode::SUB],
x86_seq: vec![X86Opcode::SBB],
riscv_cost: 3,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 3,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "mul_full_64".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![RiscVOpcode::MUL, RiscVOpcode::MULHU],
x86_seq: vec![X86Opcode::MUL],
riscv_cost: 6,
x86_cost: 3,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["m".into()],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "mul_full_signed_64".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![RiscVOpcode::MUL, RiscVOpcode::MULH],
x86_seq: vec![X86Opcode::IMUL],
riscv_cost: 6,
x86_cost: 3,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["m".into()],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "clear_lowest_set_bit".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![RiscVOpcode::ADDI, RiscVOpcode::AND],
x86_seq: vec![X86Opcode::BLSR],
riscv_cost: 2,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec![],
x86_features: vec!["bmi1".into()],
});
self.add_pattern(SharedPattern {
name: "isolate_lowest_set_bit".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![RiscVOpcode::NEG, RiscVOpcode::AND],
x86_seq: vec![X86Opcode::BLSI],
riscv_cost: 2,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec![],
x86_features: vec!["bmi1".into()],
});
self.add_pattern(SharedPattern {
name: "count_trailing_zeros".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![RiscVOpcode::CTZ],
x86_seq: vec![X86Opcode::TZCNT],
riscv_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec!["zbb".into()],
x86_features: vec!["bmi1".into()],
});
self.add_pattern(SharedPattern {
name: "count_leading_zeros".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![RiscVOpcode::CLZ],
x86_seq: vec![X86Opcode::LZCNT],
riscv_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec!["zbb".into()],
x86_features: vec!["lzcnt".into()],
});
self.add_pattern(SharedPattern {
name: "popcount".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![RiscVOpcode::CPOP],
x86_seq: vec![X86Opcode::POPCNT],
riscv_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec!["zbb".into()],
x86_features: vec!["popcnt".into()],
});
self.add_pattern(SharedPattern {
name: "byte_reverse".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![RiscVOpcode::REV8],
x86_seq: vec![X86Opcode::BSWAP],
riscv_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec!["zbb".into()],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "rotate_right".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![RiscVOpcode::ROR],
x86_seq: vec![X86Opcode::ROR],
riscv_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["zbb".into()],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "sign_extend_byte".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![RiscVOpcode::SEXT_B],
x86_seq: vec![X86Opcode::MOVSX],
riscv_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec!["zbb".into()],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "sign_extend_halfword".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![RiscVOpcode::SEXT_H],
x86_seq: vec![X86Opcode::MOVSX],
riscv_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec!["zbb".into()],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "add_then_multiply".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![RiscVOpcode::ADD, RiscVOpcode::MUL],
x86_seq: vec![X86Opcode::LEA, X86Opcode::IMUL],
riscv_cost: 4,
x86_cost: 3,
is_commutative: false,
min_operands: 3,
max_operands: 3,
riscv_features: vec!["m".into()],
x86_features: vec![],
});
}
fn load_integer_comparison_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "set_if_less_than".into(),
category: PatternCategory::IntegerComparison,
riscv_seq: vec![RiscVOpcode::SLT],
x86_seq: vec![X86Opcode::CMP, X86Opcode::SETL],
riscv_cost: 1,
x86_cost: 2,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "set_if_less_than_unsigned".into(),
category: PatternCategory::IntegerComparison,
riscv_seq: vec![RiscVOpcode::SLTU],
x86_seq: vec![X86Opcode::CMP, X86Opcode::SETB],
riscv_cost: 1,
x86_cost: 2,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "set_if_equal".into(),
category: PatternCategory::IntegerComparison,
riscv_seq: vec![RiscVOpcode::SUB, RiscVOpcode::SLTIU, RiscVOpcode::XORI],
x86_seq: vec![X86Opcode::CMP, X86Opcode::SETE],
riscv_cost: 3,
x86_cost: 2,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "set_if_not_equal".into(),
category: PatternCategory::IntegerComparison,
riscv_seq: vec![RiscVOpcode::SUB, RiscVOpcode::SNEZ],
x86_seq: vec![X86Opcode::CMP, X86Opcode::SETNE],
riscv_cost: 2,
x86_cost: 2,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "conditional_move".into(),
category: PatternCategory::IntegerComparison,
riscv_seq: vec![RiscVOpcode::BEQ, RiscVOpcode::MV],
x86_seq: vec![X86Opcode::CMP, X86Opcode::CMOVNE],
riscv_cost: 2,
x86_cost: 2,
is_commutative: false,
min_operands: 3,
max_operands: 3,
riscv_features: vec![],
x86_features: vec!["cmov".into()],
});
}
fn load_logical_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "and_not".into(),
category: PatternCategory::Logical,
riscv_seq: vec![RiscVOpcode::ANDN],
x86_seq: vec![X86Opcode::ANDN],
riscv_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["zbb".into()],
x86_features: vec!["bmi1".into()],
});
self.add_pattern(SharedPattern {
name: "or_not".into(),
category: PatternCategory::Logical,
riscv_seq: vec![RiscVOpcode::ORN],
x86_seq: vec![X86Opcode::OR, X86Opcode::NOT],
riscv_cost: 1,
x86_cost: 2,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["zbb".into()],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "xnor".into(),
category: PatternCategory::Logical,
riscv_seq: vec![RiscVOpcode::XNOR],
x86_seq: vec![X86Opcode::XOR, X86Opcode::NOT],
riscv_cost: 1,
x86_cost: 2,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["zbb".into()],
x86_features: vec![],
});
}
fn load_shift_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "variable_shift_left".into(),
category: PatternCategory::Shift,
riscv_seq: vec![RiscVOpcode::SLL],
x86_seq: vec![X86Opcode::SHL],
riscv_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "variable_shift_right_logical".into(),
category: PatternCategory::Shift,
riscv_seq: vec![RiscVOpcode::SRL],
x86_seq: vec![X86Opcode::SHR],
riscv_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "variable_shift_right_arithmetic".into(),
category: PatternCategory::Shift,
riscv_seq: vec![RiscVOpcode::SRA],
x86_seq: vec![X86Opcode::SAR],
riscv_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "byte_swap_via_shift".into(),
category: PatternCategory::Shift,
riscv_seq: vec![
RiscVOpcode::SLLI,
RiscVOpcode::ANDI,
RiscVOpcode::SRLI,
RiscVOpcode::OR,
RiscVOpcode::SLLI,
RiscVOpcode::SRLI,
RiscVOpcode::OR,
],
x86_seq: vec![X86Opcode::BSWAP],
riscv_cost: 7,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec![],
x86_features: vec![],
});
}
fn load_memory_access_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "load_word".into(),
category: PatternCategory::MemoryAccess,
riscv_seq: vec![RiscVOpcode::LW],
x86_seq: vec![X86Opcode::MOV],
riscv_cost: 4,
x86_cost: 3,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "store_word".into(),
category: PatternCategory::MemoryAccess,
riscv_seq: vec![RiscVOpcode::SW],
x86_seq: vec![X86Opcode::MOV],
riscv_cost: 4,
x86_cost: 3,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "load_doubleword".into(),
category: PatternCategory::MemoryAccess,
riscv_seq: vec![RiscVOpcode::LD],
x86_seq: vec![X86Opcode::MOV],
riscv_cost: 4,
x86_cost: 3,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "store_doubleword".into(),
category: PatternCategory::MemoryAccess,
riscv_seq: vec![RiscVOpcode::SD],
x86_seq: vec![X86Opcode::MOV],
riscv_cost: 4,
x86_cost: 3,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "load_byte_signed".into(),
category: PatternCategory::MemoryAccess,
riscv_seq: vec![RiscVOpcode::LB],
x86_seq: vec![X86Opcode::MOVSX],
riscv_cost: 4,
x86_cost: 3,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "load_byte_unsigned".into(),
category: PatternCategory::MemoryAccess,
riscv_seq: vec![RiscVOpcode::LBU],
x86_seq: vec![X86Opcode::MOVZX],
riscv_cost: 4,
x86_cost: 3,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "load_halfword_signed".into(),
category: PatternCategory::MemoryAccess,
riscv_seq: vec![RiscVOpcode::LH],
x86_seq: vec![X86Opcode::MOVSX],
riscv_cost: 4,
x86_cost: 3,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "load_halfword_unsigned".into(),
category: PatternCategory::MemoryAccess,
riscv_seq: vec![RiscVOpcode::LHU],
x86_seq: vec![X86Opcode::MOVZX],
riscv_cost: 4,
x86_cost: 3,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec![],
});
}
fn load_control_flow_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "unconditional_branch".into(),
category: PatternCategory::ControlFlow,
riscv_seq: vec![RiscVOpcode::JAL],
x86_seq: vec![X86Opcode::JMP],
riscv_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "branch_if_equal".into(),
category: PatternCategory::ControlFlow,
riscv_seq: vec![RiscVOpcode::BEQ],
x86_seq: vec![X86Opcode::CMP, X86Opcode::JE],
riscv_cost: 1,
x86_cost: 2,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "branch_if_not_equal".into(),
category: PatternCategory::ControlFlow,
riscv_seq: vec![RiscVOpcode::BNE],
x86_seq: vec![X86Opcode::CMP, X86Opcode::JNE],
riscv_cost: 1,
x86_cost: 2,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "indirect_jump".into(),
category: PatternCategory::ControlFlow,
riscv_seq: vec![RiscVOpcode::JALR],
x86_seq: vec![X86Opcode::JMP],
riscv_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "function_call".into(),
category: PatternCategory::ControlFlow,
riscv_seq: vec![RiscVOpcode::CALL],
x86_seq: vec![X86Opcode::CALL],
riscv_cost: 2,
x86_cost: 2,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "function_return".into(),
category: PatternCategory::ControlFlow,
riscv_seq: vec![RiscVOpcode::RET],
x86_seq: vec![X86Opcode::RET],
riscv_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 0,
max_operands: 0,
riscv_features: vec![],
x86_features: vec![],
});
}
fn load_float_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "fadd_single".into(),
category: PatternCategory::FloatArithmetic,
riscv_seq: vec![RiscVOpcode::FADD_S],
x86_seq: vec![X86Opcode::ADDSS],
riscv_cost: 3,
x86_cost: 3,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["f".into()],
x86_features: vec!["sse".into()],
});
self.add_pattern(SharedPattern {
name: "fadd_double".into(),
category: PatternCategory::FloatArithmetic,
riscv_seq: vec![RiscVOpcode::FADD_D],
x86_seq: vec![X86Opcode::ADDSD],
riscv_cost: 3,
x86_cost: 3,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["d".into()],
x86_features: vec!["sse2".into()],
});
self.add_pattern(SharedPattern {
name: "fmul_single".into(),
category: PatternCategory::FloatArithmetic,
riscv_seq: vec![RiscVOpcode::FMUL_S],
x86_seq: vec![X86Opcode::MULSS],
riscv_cost: 3,
x86_cost: 3,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["f".into()],
x86_features: vec!["sse".into()],
});
self.add_pattern(SharedPattern {
name: "fmul_double".into(),
category: PatternCategory::FloatArithmetic,
riscv_seq: vec![RiscVOpcode::FMUL_D],
x86_seq: vec![X86Opcode::MULSD],
riscv_cost: 5,
x86_cost: 5,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["d".into()],
x86_features: vec!["sse2".into()],
});
self.add_pattern(SharedPattern {
name: "fmadd_single".into(),
category: PatternCategory::FloatArithmetic,
riscv_seq: vec![RiscVOpcode::FMADD_S],
x86_seq: vec![X86Opcode::VFMADD132SS],
riscv_cost: 3,
x86_cost: 4,
is_commutative: false,
min_operands: 3,
max_operands: 3,
riscv_features: vec!["f".into()],
x86_features: vec!["fma".into()],
});
self.add_pattern(SharedPattern {
name: "fmadd_double".into(),
category: PatternCategory::FloatArithmetic,
riscv_seq: vec![RiscVOpcode::FMADD_D],
x86_seq: vec![X86Opcode::VFMADD132SD],
riscv_cost: 5,
x86_cost: 4,
is_commutative: false,
min_operands: 3,
max_operands: 3,
riscv_features: vec!["d".into()],
x86_features: vec!["fma".into()],
});
self.add_pattern(SharedPattern {
name: "fdiv_single".into(),
category: PatternCategory::FloatArithmetic,
riscv_seq: vec![RiscVOpcode::FDIV_S],
x86_seq: vec![X86Opcode::DIVSS],
riscv_cost: 12,
x86_cost: 10,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["f".into()],
x86_features: vec!["sse".into()],
});
self.add_pattern(SharedPattern {
name: "fdiv_double".into(),
category: PatternCategory::FloatArithmetic,
riscv_seq: vec![RiscVOpcode::FDIV_D],
x86_seq: vec![X86Opcode::DIVSD],
riscv_cost: 20,
x86_cost: 14,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["d".into()],
x86_features: vec!["sse2".into()],
});
self.add_pattern(SharedPattern {
name: "fsqrt_single".into(),
category: PatternCategory::FloatArithmetic,
riscv_seq: vec![RiscVOpcode::FSQRT_S],
x86_seq: vec![X86Opcode::SQRTSS],
riscv_cost: 12,
x86_cost: 10,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec!["f".into()],
x86_features: vec!["sse".into()],
});
self.add_pattern(SharedPattern {
name: "fsqrt_double".into(),
category: PatternCategory::FloatArithmetic,
riscv_seq: vec![RiscVOpcode::FSQRT_D],
x86_seq: vec![X86Opcode::SQRTSD],
riscv_cost: 20,
x86_cost: 14,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec!["d".into()],
x86_features: vec!["sse2".into()],
});
self.add_pattern(SharedPattern {
name: "fcmp_eq_single".into(),
category: PatternCategory::FloatComparison,
riscv_seq: vec![RiscVOpcode::FEQ_S],
x86_seq: vec![X86Opcode::UCOMISS, X86Opcode::SETE],
riscv_cost: 1,
x86_cost: 2,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["f".into()],
x86_features: vec!["sse".into()],
});
self.add_pattern(SharedPattern {
name: "fcmp_lt_single".into(),
category: PatternCategory::FloatComparison,
riscv_seq: vec![RiscVOpcode::FLT_S],
x86_seq: vec![X86Opcode::UCOMISS, X86Opcode::SETB],
riscv_cost: 1,
x86_cost: 2,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["f".into()],
x86_features: vec!["sse".into()],
});
self.add_pattern(SharedPattern {
name: "fcmp_le_single".into(),
category: PatternCategory::FloatComparison,
riscv_seq: vec![RiscVOpcode::FLE_S],
x86_seq: vec![X86Opcode::UCOMISS, X86Opcode::SETBE],
riscv_cost: 1,
x86_cost: 2,
is_commutative: false,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["f".into()],
x86_features: vec!["sse".into()],
});
}
fn load_conversion_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "int_to_float_single_signed".into(),
category: PatternCategory::Conversion,
riscv_seq: vec![RiscVOpcode::FCVT_S_W],
x86_seq: vec![X86Opcode::CVTSI2SS],
riscv_cost: 5,
x86_cost: 5,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec!["f".into()],
x86_features: vec!["sse".into()],
});
self.add_pattern(SharedPattern {
name: "float_to_int_single_signed".into(),
category: PatternCategory::Conversion,
riscv_seq: vec![RiscVOpcode::FCVT_W_S],
x86_seq: vec![X86Opcode::CVTTSS2SI],
riscv_cost: 5,
x86_cost: 5,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec!["f".into()],
x86_features: vec!["sse".into()],
});
self.add_pattern(SharedPattern {
name: "int64_to_double_signed".into(),
category: PatternCategory::Conversion,
riscv_seq: vec![RiscVOpcode::FCVT_D_L],
x86_seq: vec![X86Opcode::CVTSI2SD],
riscv_cost: 5,
x86_cost: 6,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec!["d".into()],
x86_features: vec!["sse2".into()],
});
self.add_pattern(SharedPattern {
name: "double_to_int64_signed".into(),
category: PatternCategory::Conversion,
riscv_seq: vec![RiscVOpcode::FCVT_L_D],
x86_seq: vec![X86Opcode::CVTTSD2SI],
riscv_cost: 5,
x86_cost: 6,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec!["d".into()],
x86_features: vec!["sse2".into()],
});
self.add_pattern(SharedPattern {
name: "single_to_double".into(),
category: PatternCategory::Conversion,
riscv_seq: vec![RiscVOpcode::FCVT_D_S],
x86_seq: vec![X86Opcode::CVTSS2SD],
riscv_cost: 2,
x86_cost: 2,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec!["d".into()],
x86_features: vec!["sse2".into()],
});
self.add_pattern(SharedPattern {
name: "double_to_single".into(),
category: PatternCategory::Conversion,
riscv_seq: vec![RiscVOpcode::FCVT_S_D],
x86_seq: vec![X86Opcode::CVTSD2SS],
riscv_cost: 2,
x86_cost: 2,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec!["d".into()],
x86_features: vec!["sse2".into()],
});
self.add_pattern(SharedPattern {
name: "float_to_int_move".into(),
category: PatternCategory::Conversion,
riscv_seq: vec![RiscVOpcode::FMV_X_W],
x86_seq: vec![X86Opcode::MOVD],
riscv_cost: 2,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec!["f".into()],
x86_features: vec!["sse2".into()],
});
self.add_pattern(SharedPattern {
name: "int_to_float_move".into(),
category: PatternCategory::Conversion,
riscv_seq: vec![RiscVOpcode::FMV_W_X],
x86_seq: vec![X86Opcode::MOVD],
riscv_cost: 2,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec!["f".into()],
x86_features: vec!["sse2".into()],
});
}
fn load_constant_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "load_upper_immediate".into(),
category: PatternCategory::ConstantMaterialization,
riscv_seq: vec![RiscVOpcode::LUI],
x86_seq: vec![X86Opcode::MOV],
riscv_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "load_i32_constant".into(),
category: PatternCategory::ConstantMaterialization,
riscv_seq: vec![RiscVOpcode::LUI, RiscVOpcode::ADDI],
x86_seq: vec![X86Opcode::MOV],
riscv_cost: 2,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "load_i64_constant".into(),
category: PatternCategory::ConstantMaterialization,
riscv_seq: vec![
RiscVOpcode::LUI,
RiscVOpcode::ADDI,
RiscVOpcode::SLLI,
RiscVOpcode::ADDI,
RiscVOpcode::SLLI,
RiscVOpcode::ADDI,
],
x86_seq: vec![X86Opcode::MOV],
riscv_cost: 6,
x86_cost: 1,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec![],
x86_features: vec![],
});
}
fn load_vector_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "vadd_int".into(),
category: PatternCategory::VectorOperation,
riscv_seq: vec![RiscVOpcode::VADD_VV],
x86_seq: vec![X86Opcode::PADDD],
riscv_cost: 1,
x86_cost: 1,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["v".into()],
x86_features: vec!["sse2".into()],
});
self.add_pattern(SharedPattern {
name: "vfadd_float".into(),
category: PatternCategory::VectorOperation,
riscv_seq: vec![RiscVOpcode::VFADD_VV],
x86_seq: vec![X86Opcode::ADDPS],
riscv_cost: 3,
x86_cost: 3,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["v".into()],
x86_features: vec!["sse".into()],
});
self.add_pattern(SharedPattern {
name: "vfmul_float".into(),
category: PatternCategory::VectorOperation,
riscv_seq: vec![RiscVOpcode::VFMUL_VV],
x86_seq: vec![X86Opcode::MULPS],
riscv_cost: 3,
x86_cost: 3,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["v".into()],
x86_features: vec!["sse".into()],
});
self.add_pattern(SharedPattern {
name: "vmul_int32".into(),
category: PatternCategory::VectorOperation,
riscv_seq: vec![RiscVOpcode::VMUL_VV],
x86_seq: vec![X86Opcode::PMULLD],
riscv_cost: 3,
x86_cost: 3,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["v".into()],
x86_features: vec!["sse4_1".into()],
});
self.add_pattern(SharedPattern {
name: "vmerge".into(),
category: PatternCategory::VectorOperation,
riscv_seq: vec![RiscVOpcode::VMERGE_VVM],
x86_seq: vec![X86Opcode::BLENDVPS],
riscv_cost: 1,
x86_cost: 1,
is_commutative: false,
min_operands: 3,
max_operands: 3,
riscv_features: vec!["v".into()],
x86_features: vec!["sse4_1".into()],
});
}
fn load_atomic_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "atomic_swap".into(),
category: PatternCategory::AtomicOperation,
riscv_seq: vec![RiscVOpcode::AMOSWAP_W],
x86_seq: vec![X86Opcode::XCHG],
riscv_cost: 10,
x86_cost: 10,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["a".into()],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "atomic_add".into(),
category: PatternCategory::AtomicOperation,
riscv_seq: vec![RiscVOpcode::AMOADD_W],
x86_seq: vec![X86Opcode::XADD],
riscv_cost: 10,
x86_cost: 10,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec!["a".into()],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "atomic_cas".into(),
category: PatternCategory::AtomicOperation,
riscv_seq: vec![RiscVOpcode::LR_W, RiscVOpcode::SC_W],
x86_seq: vec![X86Opcode::CMPXCHG],
riscv_cost: 15,
x86_cost: 10,
is_commutative: false,
min_operands: 3,
max_operands: 3,
riscv_features: vec!["a".into()],
x86_features: vec![],
});
}
pub fn find(&self, op_name: &str, _is_riscv: bool) -> Option<SharedISelMatch> {
self.patterns.get(op_name).map(|p| SharedISelMatch {
pattern: p.clone(),
riscv_sequence: p.riscv_seq.clone(),
x86_sequence: p.x86_seq.clone(),
estimated_riscv_cost: p.riscv_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 riscv_sequence: Vec<RiscVOpcode>,
pub x86_sequence: Vec<X86Opcode>,
pub estimated_riscv_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<TargetArch>,
pub match_pattern: Vec<String>,
pub replacement: Vec<ReplacementStep>,
pub priority: u32,
pub is_optimization: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TargetArch {
RISCV32,
RISCV64,
X86,
X86_64,
ARM32,
ARM64,
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: "combine_add_add_to_address".into(),
targets: vec![TargetArch::All],
match_pattern: vec!["add".into(), "add".into(), "const".into()],
replacement: vec![ReplacementStep {
operation: "lea".into(),
inputs: vec![0, 1, 2],
output: "result".into(),
metadata: {
let mut m = HashMap::new();
m.insert("scale".into(), "1".into());
m
},
}],
priority: 100,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "lower_select_to_cmov".into(),
targets: vec![TargetArch::X86, TargetArch::X86_64, TargetArch::RISCV64],
match_pattern: vec![
"select".into(),
"cond".into(),
"true_val".into(),
"false_val".into(),
],
replacement: vec![ReplacementStep {
operation: "cmov".into(),
inputs: vec![0, 2, 3],
output: "result".into(),
metadata: HashMap::new(),
}],
priority: 90,
is_optimization: false,
});
self.add_rule(LoweringRule {
name: "lower_switch_to_jump_table".into(),
targets: vec![TargetArch::All],
match_pattern: vec!["switch".into(), "value".into()],
replacement: vec![ReplacementStep {
operation: "jumptable".into(),
inputs: vec![1],
output: "table".into(),
metadata: HashMap::new(),
}],
priority: 80,
is_optimization: false,
});
self.add_rule(LoweringRule {
name: "combine_loads_to_load_pair".into(),
targets: vec![TargetArch::RISCV64, TargetArch::ARM64],
match_pattern: vec!["load".into(), "load".into()],
replacement: vec![ReplacementStep {
operation: "load_pair".into(),
inputs: vec![0, 1],
output: "pair".into(),
metadata: HashMap::new(),
}],
priority: 85,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "combine_stores_to_store_pair".into(),
targets: vec![TargetArch::RISCV64, TargetArch::ARM64],
match_pattern: vec!["store".into(), "store".into()],
replacement: vec![ReplacementStep {
operation: "store_pair".into(),
inputs: vec![0, 1],
output: "".into(),
metadata: HashMap::new(),
}],
priority: 85,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "lower_memcpy_target_specific".into(),
targets: vec![TargetArch::All],
match_pattern: vec!["memcpy".into(), "dest".into(), "src".into(), "size".into()],
replacement: vec![ReplacementStep {
operation: "memcpy_expand".into(),
inputs: vec![1, 2, 3],
output: "".into(),
metadata: HashMap::new(),
}],
priority: 70,
is_optimization: false,
});
self.add_rule(LoweringRule {
name: "lower_memset_target_specific".into(),
targets: vec![TargetArch::All],
match_pattern: vec![
"memset".into(),
"dest".into(),
"value".into(),
"size".into(),
],
replacement: vec![ReplacementStep {
operation: "memset_expand".into(),
inputs: vec![1, 2, 3],
output: "".into(),
metadata: HashMap::new(),
}],
priority: 70,
is_optimization: false,
});
self.add_rule(LoweringRule {
name: "combine_mul_add_to_madd".into(),
targets: vec![TargetArch::All],
match_pattern: vec!["add".into(), "mul".into(), "const".into()],
replacement: vec![ReplacementStep {
operation: "madd".into(),
inputs: vec![0, 1, 2],
output: "result".into(),
metadata: HashMap::new(),
}],
priority: 95,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "combine_shift_and_to_bfext".into(),
targets: vec![TargetArch::RISCV64, TargetArch::RISCV32, TargetArch::ARM64],
match_pattern: vec!["and".into(), "srl".into(), "const".into()],
replacement: vec![ReplacementStep {
operation: "bfext".into(),
inputs: vec![0, 1, 2],
output: "result".into(),
metadata: HashMap::new(),
}],
priority: 90,
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),
target_archs: 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 != "const" && 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 target_archs: Vec<TargetArch>,
pub is_optimization: bool,
}
pub struct SharedRegisterAllocContext {
pub riscv_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,
Pbqp,
Fast,
}
impl AllocationStrategy {
pub fn as_str(&self) -> &str {
match self {
Self::LinearScan => "linear-scan",
Self::Greedy => "greedy",
Self::Pbqp => "pbqp",
Self::Fast => "fast",
}
}
}
impl SharedRegisterAllocContext {
pub fn new() -> Self {
Self {
riscv_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.riscv_classes.push(SharedRegClass {
id: 0,
name: "GPR".into(),
registers: (0..32).collect(),
alignment: 8,
size_bits: 64,
is_allocatable: true,
copy_cost: 1,
});
self.riscv_classes.push(SharedRegClass {
id: 1,
name: "FPR".into(),
registers: (32..64).collect(),
alignment: 8,
size_bits: 64,
is_allocatable: true,
copy_cost: 1,
});
self.riscv_classes.push(SharedRegClass {
id: 2,
name: "VR".into(),
registers: (64..96).collect(),
alignment: 16,
size_bits: 256,
is_allocatable: true,
copy_cost: 3,
});
self.x86_classes.push(SharedRegClass {
id: 0,
name: "GPR64".into(),
registers: (0..16).collect(),
alignment: 8,
size_bits: 64,
is_allocatable: true,
copy_cost: 1,
});
self.x86_classes.push(SharedRegClass {
id: 1,
name: "GPR32".into(),
registers: (0..8).collect(),
alignment: 4,
size_bits: 32,
is_allocatable: true,
copy_cost: 1,
});
self.x86_classes.push(SharedRegClass {
id: 2,
name: "XMM".into(),
registers: (16..32).collect(),
alignment: 16,
size_bits: 128,
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) * 8,
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)
}
}
impl Default for SharedRegisterAllocContext {
fn default() -> Self {
let mut ctx = Self::new();
ctx.initialize();
ctx
}
}
#[derive(Debug)]
pub struct CrossTargetRegisterAlloc {
pub context: SharedRegisterAllocContext,
pub riscv_constraints: Vec<RegisterConstraint>,
pub x86_constraints: Vec<RegisterConstraint>,
pub aliases: Vec<RegisterAlias>,
}
#[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,
AlignmentRequired,
}
#[derive(Debug, Clone)]
pub struct RegisterAlias {
pub primary: u32,
pub alias: u32,
pub relation: AliasRelation,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AliasRelation {
SubRegister,
SuperRegister,
Equivalent,
}
impl CrossTargetRegisterAlloc {
pub fn new() -> Self {
Self {
context: SharedRegisterAllocContext::new(),
riscv_constraints: Vec::new(),
x86_constraints: Vec::new(),
aliases: 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: 16,
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 is_64bit: 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: 16,
is_64bit: 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 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 is_64bit: bool,
pub max_immediate: i64,
pub lui_max: i64,
}
impl CrossTargetConstMaterializer {
pub fn new() -> Self {
Self {
is_64bit: true,
max_immediate: 2047, lui_max: 0xFFFFFFFFFFFFF000u64 as i64,
}
}
pub fn materialize_riscv(&self, value: i64) -> Vec<RiscVOpcode> {
if value == 0 {
return vec![RiscVOpcode::ADDI]; }
if value >= -2048 && value <= 2047 {
return vec![RiscVOpcode::ADDI];
}
let lower = value & 0xFFF;
let upper = value - lower;
if lower == 0 {
return vec![RiscVOpcode::LUI];
}
if self.is_64bit && (value < -(1i64 << 31) || value > (1i64 << 31) - 1) {
vec![
RiscVOpcode::LUI,
RiscVOpcode::ADDI,
RiscVOpcode::SLLI,
RiscVOpcode::ADDI,
RiscVOpcode::SLLI,
RiscVOpcode::ADDI,
]
} else {
vec![RiscVOpcode::LUI, RiscVOpcode::ADDI]
}
}
pub fn materialize_x86_cost(&self, value: i64) -> u32 {
if value == 0 {
return 1; }
if self.is_64bit && (value < -(1i64 << 31) || value > (1i64 << 31) - 1) {
2 } 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;
self.branch_stats.backward_branches = 0;
self.branch_stats.forward_branches = 0;
self.branch_stats.indirect_branches = 0;
let mut distances = Vec::new();
for (i, instr) in instrs.iter().enumerate() {
let lower = instr.to_lowercase();
if lower.contains("jmp")
|| lower.contains("j")
|| lower.contains("b")
|| lower.contains("ret")
{
self.branch_stats.total_branches += 1;
if lower.starts_with("b") && !lower.contains("jal") {
self.branch_stats.conditional_branches += 1;
} else if lower.contains("jal") || lower.contains("call") {
self.branch_stats.unconditional_branches += 1;
} else if lower.contains("ret") || lower.starts_with("jr") {
self.branch_stats.indirect_branches += 1;
} else {
self.branch_stats.unconditional_branches += 1;
}
}
}
if !distances.is_empty() {
self.branch_stats.avg_branch_distance =
distances.iter().sum::<i64>() as f64 / distances.len() as f64;
}
}
pub fn compare(&self, riscv_branches: u64, x86_branches: u64) -> BranchComparison {
let ratio = if x86_branches > 0 {
riscv_branches as f64 / x86_branches as f64
} else {
f64::INFINITY
};
BranchComparison {
riscv_branch_count: riscv_branches,
x86_branch_count: x86_branches,
ratio,
assessment: if ratio < 0.9 {
"RISC-V uses fewer branches".into()
} else if ratio > 1.1 {
"X86 uses fewer branches".into()
} else {
"Similar branch counts".into()
},
}
}
}
#[derive(Debug, Clone)]
pub struct BranchComparison {
pub riscv_branch_count: u64,
pub x86_branch_count: u64,
pub ratio: f64,
pub assessment: String,
}
#[derive(Debug, Clone)]
pub struct BridgeRISCVTargetMachine {
pub triple: String,
pub cpu: String,
pub features: String,
pub is_64bit: bool,
pub data_layout: String,
pub opt_level: OptimizationLevel,
pub reloc_model: RelocModel,
pub code_model: CodeModel,
pub extension_flags: ExtensionFlags,
}
#[derive(Debug, Clone, Default)]
pub struct ExtensionFlags {
pub xlen: u32,
pub has_m: bool,
pub has_a: bool,
pub has_f: bool,
pub has_d: bool,
pub has_c: bool,
pub has_v: bool,
pub has_zba: bool,
pub has_zbb: bool,
pub has_zbc: bool,
pub has_zbs: bool,
pub has_zicsr: bool,
pub has_zifencei: bool,
pub has_zfh: bool,
pub has_zfhmin: bool,
pub has_zk: bool,
pub has_zkn: bool,
pub has_zks: bool,
pub has_zihintpause: bool,
pub has_zicbom: bool,
pub has_zicboz: bool,
pub has_zicbop: bool,
pub has_zawrs: bool,
pub has_svinval: bool,
pub has_svnapot: bool,
pub has_svpbmt: bool,
pub has_h: bool,
}
impl ExtensionFlags {
pub fn parse(ext_str: &str) -> Self {
let mut flags = ExtensionFlags::default();
let lower = ext_str.to_lowercase();
if lower.starts_with("rv64") {
flags.xlen = 64;
} else if lower.starts_with("rv32") {
flags.xlen = 32;
}
for ch in lower.chars().skip(2) {
match ch {
'm' => flags.has_m = true,
'a' => flags.has_a = true,
'f' => flags.has_f = true,
'd' => flags.has_d = true,
'c' => flags.has_c = true,
'v' => flags.has_v = true,
'h' => flags.has_h = true,
'_' => break, _ => {}
}
}
let multi_letter_start = lower.find('_');
if let Some(start) = multi_letter_start {
let multi_part = &lower[start..];
for ext in multi_part.split('_').skip(1) {
if ext.is_empty() {
continue;
}
match ext {
"zba" => flags.has_zba = true,
"zbb" => flags.has_zbb = true,
"zbc" => flags.has_zbc = true,
"zbs" => flags.has_zbs = true,
"zicsr" => flags.has_zicsr = true,
"zifencei" => flags.has_zifencei = true,
"zfh" => flags.has_zfh = true,
"zfhmin" => flags.has_zfhmin = true,
"zk" => flags.has_zk = true,
"zkn" => flags.has_zkn = true,
"zks" => flags.has_zks = true,
"zihintpause" => flags.has_zihintpause = true,
"zicbom" => flags.has_zicbom = true,
"zicboz" => flags.has_zicboz = true,
"zicbop" => flags.has_zicbop = true,
"zawrs" => flags.has_zawrs = true,
"svinval" => flags.has_svinval = true,
"svnapot" => flags.has_svnapot = true,
"svpbmt" => flags.has_svpbmt = true,
_ => {}
}
}
}
if flags.has_d {
flags.has_f = true; }
if flags.has_v {
flags.has_f = true;
flags.has_d = true;
}
flags
}
pub fn to_isa_string(&self) -> String {
let mut s = format!("rv{}i", self.xlen);
if self.has_m {
s.push('m');
}
if self.has_a {
s.push('a');
}
if self.has_f {
s.push('f');
}
if self.has_d {
s.push('d');
}
if self.has_c {
s.push('c');
}
if self.has_v {
s.push('v');
}
if self.has_h {
s.push('h');
}
let mut multi = Vec::new();
if self.has_zba {
multi.push("zba");
}
if self.has_zbb {
multi.push("zbb");
}
if self.has_zbc {
multi.push("zbc");
}
if self.has_zbs {
multi.push("zbs");
}
if self.has_zicsr {
multi.push("zicsr");
}
if self.has_zifencei {
multi.push("zifencei");
}
if self.has_zfh {
multi.push("zfh");
}
if self.has_zfhmin {
multi.push("zfhmin");
}
if self.has_zk {
multi.push("zk");
}
if self.has_zkn {
multi.push("zkn");
}
if self.has_zks {
multi.push("zks");
}
if self.has_zihintpause {
multi.push("zihintpause");
}
if self.has_zicbom {
multi.push("zicbom");
}
if self.has_zicboz {
multi.push("zicboz");
}
if self.has_zicbop {
multi.push("zicbop");
}
if self.has_zawrs {
multi.push("zawrs");
}
if self.has_svinval {
multi.push("svinval");
}
if self.has_svnapot {
multi.push("svnapot");
}
if self.has_svpbmt {
multi.push("svpbmt");
}
for m in &multi {
s.push('_');
s.push_str(m);
}
s
}
pub fn from_flags(
xlen: u32,
has_m: bool,
has_a: bool,
has_f: bool,
has_d: bool,
has_c: bool,
has_v: bool,
has_zba: bool,
has_zbb: bool,
has_zbc: bool,
has_zbs: bool,
) -> Self {
Self {
xlen,
has_m,
has_a,
has_f,
has_d,
has_c,
has_v,
has_zba,
has_zbb,
has_zbc,
has_zbs,
has_zicsr: true,
has_zifencei: true,
..Default::default()
}
}
pub fn list_enabled(&self) -> Vec<String> {
let mut exts = Vec::new();
exts.push(format!("rv{}i", self.xlen));
if self.has_m {
exts.push("m".into());
}
if self.has_a {
exts.push("a".into());
}
if self.has_f {
exts.push("f".into());
}
if self.has_d {
exts.push("d".into());
}
if self.has_c {
exts.push("c".into());
}
if self.has_v {
exts.push("v".into());
}
if self.has_h {
exts.push("h".into());
}
if self.has_zba {
exts.push("zba".into());
}
if self.has_zbb {
exts.push("zbb".into());
}
if self.has_zbc {
exts.push("zbc".into());
}
if self.has_zbs {
exts.push("zbs".into());
}
if self.has_zicsr {
exts.push("zicsr".into());
}
if self.has_zifencei {
exts.push("zifencei".into());
}
if self.has_zfh {
exts.push("zfh".into());
}
if self.has_zfhmin {
exts.push("zfhmin".into());
}
if self.has_zk {
exts.push("zk".into());
}
if self.has_zkn {
exts.push("zkn".into());
}
if self.has_zks {
exts.push("zks".into());
}
exts
}
}
pub const RV32_DATA_LAYOUT: &str = "e-m:e-p:32:32-i64:64-n32-S128";
pub const RV64_DATA_LAYOUT: &str = "e-m:e-p:64:64-i64:64-i128:128-n64-S128";
impl BridgeRISCVTargetMachine {
pub fn new(triple: &str) -> Self {
let lower = triple.to_lowercase();
let is_64bit = lower.starts_with("riscv64");
let data_layout = if is_64bit {
RV64_DATA_LAYOUT.to_string()
} else {
RV32_DATA_LAYOUT.to_string()
};
let ext_flags = if lower.starts_with("rv") {
ExtensionFlags::parse(triple)
} else {
let mut flags = ExtensionFlags::default();
flags.xlen = if is_64bit { 64 } else { 32 };
flags
};
let features = Self::build_features_string(&ext_flags);
Self {
triple: triple.to_string(),
cpu: if is_64bit {
"generic-rv64".to_string()
} else {
"generic-rv32".to_string()
},
features,
is_64bit,
data_layout,
opt_level: OptimizationLevel::O0,
reloc_model: RelocModel::Static,
code_model: CodeModel::Small,
extension_flags: ext_flags,
}
}
fn build_features_string(flags: &ExtensionFlags) -> String {
let mut parts = Vec::new();
if flags.has_m {
parts.push("+m".to_string());
}
if flags.has_a {
parts.push("+a".to_string());
}
if flags.has_f {
parts.push("+f".to_string());
}
if flags.has_d {
parts.push("+d".to_string());
}
if flags.has_c {
parts.push("+c".to_string());
}
if flags.has_v {
parts.push("+v".to_string());
}
if flags.has_zba {
parts.push("+zba".to_string());
}
if flags.has_zbb {
parts.push("+zbb".to_string());
}
if flags.has_zbc {
parts.push("+zbc".to_string());
}
if flags.has_zbs {
parts.push("+zbs".to_string());
}
if flags.has_zicsr {
parts.push("+zicsr".to_string());
}
if flags.has_zifencei {
parts.push("+zifencei".to_string());
}
if flags.has_zfh {
parts.push("+zfh".to_string());
}
if flags.has_zfhmin {
parts.push("+zfhmin".to_string());
}
if flags.has_zk {
parts.push("+zk".to_string());
}
if flags.has_zkn {
parts.push("+zkn".to_string());
}
if flags.has_zks {
parts.push("+zks".to_string());
}
if flags.has_h {
parts.push("+h".to_string());
}
parts.join(",")
}
pub fn to_target_machine(&self) -> RiscVTargetMachine {
RiscVTargetMachine {
triple: self.triple.clone(),
cpu: self.cpu.clone(),
features: self.features.clone(),
is_64bit: self.is_64bit,
data_layout: self.data_layout.clone(),
opt_level: self.opt_level,
reloc_model: self.reloc_model,
code_model: self.code_model,
}
}
pub fn from_target_machine(tm: &RiscVTargetMachine) -> Self {
let ext_flags = ExtensionFlags::parse(&tm.triple);
Self {
triple: tm.triple.clone(),
cpu: tm.cpu.clone(),
features: tm.features.clone(),
is_64bit: tm.is_64bit,
data_layout: tm.data_layout.clone(),
opt_level: tm.opt_level,
reloc_model: tm.reloc_model,
code_model: tm.code_model,
extension_flags: ext_flags,
}
}
pub fn with_opt_level(mut self, level: OptimizationLevel) -> Self {
self.opt_level = level;
self
}
pub fn with_reloc_model(mut self, model: RelocModel) -> Self {
self.reloc_model = model;
self
}
pub fn with_code_model(mut self, model: CodeModel) -> Self {
self.code_model = model;
self
}
pub fn get_triple(&self) -> &str {
&self.triple
}
pub fn get_data_layout(&self) -> &str {
&self.data_layout
}
pub fn is_64bit(&self) -> bool {
self.is_64bit
}
pub fn detect_64bit_from_triple(triple: &str) -> bool {
let lower = triple.to_lowercase();
lower.starts_with("riscv64") || lower.contains("riscv64")
}
pub fn detect_32bit_from_triple(triple: &str) -> bool {
let lower = triple.to_lowercase();
lower.starts_with("riscv32") || lower.contains("riscv32")
}
pub fn describe(&self) -> String {
format!(
"RISC-V Target Machine:\n Triple: {}\n CPU: {}\n Features: {}\n XLEN: {}\n Data Layout: {}\n Opt Level: {}\n Reloc Model: {}\n Code Model: {}",
self.triple,
self.cpu,
self.features,
self.extension_flags.xlen,
self.data_layout,
self.opt_level,
self.reloc_model,
self.code_model,
)
}
}
#[derive(Debug, Clone, Default)]
pub struct TargetFeatureSet {
pub enabled: HashSet<String>,
pub disabled: HashSet<String>,
}
impl TargetFeatureSet {
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 is_disabled(&self, feature: &str) -> bool {
self.disabled.contains(feature)
}
pub fn from_riscv_extensions(flags: &ExtensionFlags) -> Self {
let mut fs = Self::new();
if flags.has_m {
fs.enable("m");
}
if flags.has_a {
fs.enable("a");
}
if flags.has_f {
fs.enable("f");
}
if flags.has_d {
fs.enable("d");
}
if flags.has_c {
fs.enable("c");
}
if flags.has_v {
fs.enable("v");
}
if flags.has_zba {
fs.enable("zba");
}
if flags.has_zbb {
fs.enable("zbb");
}
if flags.has_zbc {
fs.enable("zbc");
}
if flags.has_zbs {
fs.enable("zbs");
}
fs
}
}
pub struct BridgeRISCVInstrInfo {
pub core: RiscVInstrInfo,
pub integer_ops: Vec<RiscVOpcode>,
pub muldiv_ops: Vec<RiscVOpcode>,
pub atomic_ops: Vec<RiscVOpcode>,
pub float_single_ops: Vec<RiscVOpcode>,
pub float_double_ops: Vec<RiscVOpcode>,
pub compressed_ops: Vec<RiscVOpcode>,
pub vector_ops: Vec<RiscVOpcode>,
pub bitmanip_ops: Vec<RiscVOpcode>,
pub csr_ops: Vec<RiscVOpcode>,
pub pseudo_ops: Vec<RiscVOpcode>,
pub branch_ops: Vec<RiscVOpcode>,
pub load_ops: Vec<RiscVOpcode>,
pub store_ops: Vec<RiscVOpcode>,
pub terminator_ops: Vec<RiscVOpcode>,
}
impl BridgeRISCVInstrInfo {
pub fn new() -> Self {
let mut info = Self {
core: RiscVInstrInfo::new(),
integer_ops: Vec::new(),
muldiv_ops: Vec::new(),
atomic_ops: Vec::new(),
float_single_ops: Vec::new(),
float_double_ops: Vec::new(),
compressed_ops: Vec::new(),
vector_ops: Vec::new(),
bitmanip_ops: Vec::new(),
csr_ops: Vec::new(),
pseudo_ops: Vec::new(),
branch_ops: Vec::new(),
load_ops: Vec::new(),
store_ops: Vec::new(),
terminator_ops: Vec::new(),
};
info.categorize();
info
}
fn categorize(&mut self) {
self.integer_ops = vec![
RiscVOpcode::LUI,
RiscVOpcode::AUIPC,
RiscVOpcode::JAL,
RiscVOpcode::JALR,
RiscVOpcode::BEQ,
RiscVOpcode::BNE,
RiscVOpcode::BLT,
RiscVOpcode::BGE,
RiscVOpcode::BLTU,
RiscVOpcode::BGEU,
RiscVOpcode::LB,
RiscVOpcode::LH,
RiscVOpcode::LW,
RiscVOpcode::LBU,
RiscVOpcode::LHU,
RiscVOpcode::LD,
RiscVOpcode::LWU,
RiscVOpcode::SB,
RiscVOpcode::SH,
RiscVOpcode::SW,
RiscVOpcode::SD,
RiscVOpcode::ADDI,
RiscVOpcode::SLTI,
RiscVOpcode::SLTIU,
RiscVOpcode::XORI,
RiscVOpcode::ORI,
RiscVOpcode::ANDI,
RiscVOpcode::SLLI,
RiscVOpcode::SRLI,
RiscVOpcode::SRAI,
RiscVOpcode::ADD,
RiscVOpcode::SUB,
RiscVOpcode::SLL,
RiscVOpcode::SLT,
RiscVOpcode::SLTU,
RiscVOpcode::XOR,
RiscVOpcode::SRL,
RiscVOpcode::SRA,
RiscVOpcode::OR,
RiscVOpcode::AND,
RiscVOpcode::FENCE,
RiscVOpcode::FENCE_I,
RiscVOpcode::ECALL,
RiscVOpcode::EBREAK,
RiscVOpcode::ADDIW,
RiscVOpcode::SLLIW,
RiscVOpcode::SRLIW,
RiscVOpcode::SRAIW,
RiscVOpcode::ADDW,
RiscVOpcode::SUBW,
RiscVOpcode::SLLW,
RiscVOpcode::SRLW,
RiscVOpcode::SRAW,
];
self.branch_ops = vec![
RiscVOpcode::BEQ,
RiscVOpcode::BNE,
RiscVOpcode::BLT,
RiscVOpcode::BGE,
RiscVOpcode::BLTU,
RiscVOpcode::BGEU,
RiscVOpcode::BEQZ,
RiscVOpcode::BNEZ,
RiscVOpcode::BLEZ,
RiscVOpcode::BGEZ,
RiscVOpcode::BLTZ,
RiscVOpcode::BGTZ,
];
self.terminator_ops = vec![
RiscVOpcode::JAL,
RiscVOpcode::JALR,
RiscVOpcode::CALL,
RiscVOpcode::TAIL,
RiscVOpcode::RET,
RiscVOpcode::ECALL,
RiscVOpcode::EBREAK,
];
self.load_ops = vec![
RiscVOpcode::LB,
RiscVOpcode::LH,
RiscVOpcode::LW,
RiscVOpcode::LD,
RiscVOpcode::LBU,
RiscVOpcode::LHU,
RiscVOpcode::LWU,
RiscVOpcode::FLW,
RiscVOpcode::FLD,
];
self.store_ops = vec![
RiscVOpcode::SB,
RiscVOpcode::SH,
RiscVOpcode::SW,
RiscVOpcode::SD,
RiscVOpcode::FSW,
RiscVOpcode::FSD,
];
self.muldiv_ops = vec![
RiscVOpcode::MUL,
RiscVOpcode::MULH,
RiscVOpcode::MULHSU,
RiscVOpcode::MULHU,
RiscVOpcode::DIV,
RiscVOpcode::DIVU,
RiscVOpcode::REM,
RiscVOpcode::REMU,
RiscVOpcode::MULW,
RiscVOpcode::DIVW,
RiscVOpcode::DIVUW,
RiscVOpcode::REMW,
RiscVOpcode::REMUW,
];
self.atomic_ops = vec![
RiscVOpcode::LR_W,
RiscVOpcode::SC_W,
RiscVOpcode::LR_D,
RiscVOpcode::SC_D,
RiscVOpcode::AMOSWAP_W,
RiscVOpcode::AMOADD_W,
RiscVOpcode::AMOXOR_W,
RiscVOpcode::AMOAND_W,
RiscVOpcode::AMOOR_W,
RiscVOpcode::AMOMIN_W,
RiscVOpcode::AMOMAX_W,
RiscVOpcode::AMOMINU_W,
RiscVOpcode::AMOMAXU_W,
RiscVOpcode::AMOSWAP_D,
RiscVOpcode::AMOADD_D,
RiscVOpcode::AMOXOR_D,
RiscVOpcode::AMOAND_D,
RiscVOpcode::AMOOR_D,
RiscVOpcode::AMOMIN_D,
RiscVOpcode::AMOMAX_D,
RiscVOpcode::AMOMINU_D,
RiscVOpcode::AMOMAXU_D,
];
self.float_single_ops = vec![
RiscVOpcode::FLW,
RiscVOpcode::FSW,
RiscVOpcode::FMADD_S,
RiscVOpcode::FMSUB_S,
RiscVOpcode::FNMSUB_S,
RiscVOpcode::FNMADD_S,
RiscVOpcode::FADD_S,
RiscVOpcode::FSUB_S,
RiscVOpcode::FMUL_S,
RiscVOpcode::FDIV_S,
RiscVOpcode::FSQRT_S,
RiscVOpcode::FSGNJ_S,
RiscVOpcode::FSGNJN_S,
RiscVOpcode::FSGNJX_S,
RiscVOpcode::FMIN_S,
RiscVOpcode::FMAX_S,
RiscVOpcode::FCVT_W_S,
RiscVOpcode::FCVT_WU_S,
RiscVOpcode::FMV_X_W,
RiscVOpcode::FEQ_S,
RiscVOpcode::FLT_S,
RiscVOpcode::FLE_S,
RiscVOpcode::FCLASS_S,
RiscVOpcode::FCVT_S_W,
RiscVOpcode::FCVT_S_WU,
RiscVOpcode::FMV_W_X,
RiscVOpcode::FCVT_L_S,
RiscVOpcode::FCVT_LU_S,
RiscVOpcode::FCVT_S_L,
RiscVOpcode::FCVT_S_LU,
];
self.float_double_ops = vec![
RiscVOpcode::FLD,
RiscVOpcode::FSD,
RiscVOpcode::FMADD_D,
RiscVOpcode::FMSUB_D,
RiscVOpcode::FNMSUB_D,
RiscVOpcode::FNMADD_D,
RiscVOpcode::FADD_D,
RiscVOpcode::FSUB_D,
RiscVOpcode::FMUL_D,
RiscVOpcode::FDIV_D,
RiscVOpcode::FSQRT_D,
RiscVOpcode::FSGNJ_D,
RiscVOpcode::FSGNJN_D,
RiscVOpcode::FSGNJX_D,
RiscVOpcode::FMIN_D,
RiscVOpcode::FMAX_D,
RiscVOpcode::FCVT_W_D,
RiscVOpcode::FCVT_WU_D,
RiscVOpcode::FMV_X_D,
RiscVOpcode::FEQ_D,
RiscVOpcode::FLT_D,
RiscVOpcode::FLE_D,
RiscVOpcode::FCLASS_D,
RiscVOpcode::FCVT_D_W,
RiscVOpcode::FCVT_D_WU,
RiscVOpcode::FMV_D_X,
RiscVOpcode::FCVT_L_D,
RiscVOpcode::FCVT_LU_D,
RiscVOpcode::FCVT_D_L,
RiscVOpcode::FCVT_D_LU,
RiscVOpcode::FCVT_S_D,
RiscVOpcode::FCVT_D_S,
];
self.compressed_ops = vec![
RiscVOpcode::C_ADDI4SPN,
RiscVOpcode::C_LW,
RiscVOpcode::C_LD,
RiscVOpcode::C_FLW,
RiscVOpcode::C_FLD,
RiscVOpcode::C_SW,
RiscVOpcode::C_SD,
RiscVOpcode::C_FSW,
RiscVOpcode::C_FSD,
RiscVOpcode::C_NOP,
RiscVOpcode::C_ADDI,
RiscVOpcode::C_ADDIW,
RiscVOpcode::C_ADDI16SP,
RiscVOpcode::C_LI,
RiscVOpcode::C_LUI,
RiscVOpcode::C_SLLI,
RiscVOpcode::C_SRAI,
RiscVOpcode::C_SRLI,
RiscVOpcode::C_J,
RiscVOpcode::C_JAL,
RiscVOpcode::C_BEQZ,
RiscVOpcode::C_BNEZ,
RiscVOpcode::C_MV,
RiscVOpcode::C_ADD,
RiscVOpcode::C_JR,
RiscVOpcode::C_JALR,
RiscVOpcode::C_EBREAK,
RiscVOpcode::C_SWSP,
RiscVOpcode::C_SDSP,
RiscVOpcode::C_FSWSP,
RiscVOpcode::C_FSDSP,
RiscVOpcode::C_LWSP,
RiscVOpcode::C_LDSP,
RiscVOpcode::C_FLWSP,
RiscVOpcode::C_FLDSP,
RiscVOpcode::C_SUB,
RiscVOpcode::C_XOR,
RiscVOpcode::C_OR,
RiscVOpcode::C_AND,
RiscVOpcode::C_ANDI,
];
self.vector_ops = vec![
RiscVOpcode::VADD_VV,
RiscVOpcode::VADD_VX,
RiscVOpcode::VADD_VI,
RiscVOpcode::VSUB_VV,
RiscVOpcode::VSUB_VX,
RiscVOpcode::VMUL_VV,
RiscVOpcode::VMUL_VX,
RiscVOpcode::VMULH_VV,
RiscVOpcode::VMULH_VX,
RiscVOpcode::VDIVU_VV,
RiscVOpcode::VDIV_VV,
RiscVOpcode::VREMU_VV,
RiscVOpcode::VREM_VV,
RiscVOpcode::VFADD_VV,
RiscVOpcode::VFADD_VF,
RiscVOpcode::VFSUB_VV,
RiscVOpcode::VFSUB_VF,
RiscVOpcode::VFMUL_VV,
RiscVOpcode::VFMUL_VF,
RiscVOpcode::VFDIV_VV,
RiscVOpcode::VFDIV_VF,
RiscVOpcode::VFMADD_VV,
RiscVOpcode::VFMADD_VF,
RiscVOpcode::VFMSUB_VV,
RiscVOpcode::VFMSUB_VF,
RiscVOpcode::VFSQRT_V,
];
self.bitmanip_ops = vec![
RiscVOpcode::ANDN,
RiscVOpcode::ORN,
RiscVOpcode::XNOR,
RiscVOpcode::CLZ,
RiscVOpcode::CTZ,
RiscVOpcode::CPOP,
RiscVOpcode::MAX,
RiscVOpcode::MAXU,
RiscVOpcode::MIN,
RiscVOpcode::MINU,
RiscVOpcode::SEXT_B,
RiscVOpcode::SEXT_H,
RiscVOpcode::ZEXT_H,
RiscVOpcode::ROL,
RiscVOpcode::ROR,
RiscVOpcode::RORI,
RiscVOpcode::ORC_B,
RiscVOpcode::REV8,
RiscVOpcode::BCLR,
RiscVOpcode::BCLRI,
RiscVOpcode::BSET,
RiscVOpcode::BSETI,
RiscVOpcode::BINV,
RiscVOpcode::BINVI,
RiscVOpcode::BEXT,
RiscVOpcode::BEXTI,
RiscVOpcode::SH1ADD,
RiscVOpcode::SH2ADD,
RiscVOpcode::SH3ADD,
];
self.csr_ops = vec![
RiscVOpcode::CSRRW,
RiscVOpcode::CSRRS,
RiscVOpcode::CSRRC,
RiscVOpcode::CSRRWI,
RiscVOpcode::CSRRSI,
RiscVOpcode::CSRRCI,
];
self.pseudo_ops = vec![
RiscVOpcode::NOP,
RiscVOpcode::MV,
RiscVOpcode::NOT,
RiscVOpcode::NEG,
RiscVOpcode::SEQZ,
RiscVOpcode::SNEZ,
RiscVOpcode::LI,
RiscVOpcode::LA,
RiscVOpcode::J,
RiscVOpcode::JR,
RiscVOpcode::RET,
RiscVOpcode::CALL,
RiscVOpcode::TAIL,
];
}
pub fn get_all_categorized(&self) -> Vec<(&str, &Vec<RiscVOpcode>)> {
vec![
("Integer Base", &self.integer_ops),
("Multiply/Divide (M)", &self.muldiv_ops),
("Atomics (A)", &self.atomic_ops),
("Float Single (F)", &self.float_single_ops),
("Float Double (D)", &self.float_double_ops),
("Compressed (C)", &self.compressed_ops),
("Vector (V)", &self.vector_ops),
("Bit Manipulation (Zb*)", &self.bitmanip_ops),
("CSR", &self.csr_ops),
("Pseudo", &self.pseudo_ops),
]
}
pub fn count_category(&self, category: &str) -> usize {
match category {
"integer" => self.integer_ops.len(),
"muldiv" => self.muldiv_ops.len(),
"atomic" => self.atomic_ops.len(),
"float_single" => self.float_single_ops.len(),
"float_double" => self.float_double_ops.len(),
"compressed" => self.compressed_ops.len(),
"vector" => self.vector_ops.len(),
"bitmanip" => self.bitmanip_ops.len(),
"csr" => self.csr_ops.len(),
"pseudo" => self.pseudo_ops.len(),
_ => 0,
}
}
pub fn total_count(&self) -> usize {
self.integer_ops.len()
+ self.muldiv_ops.len()
+ self.atomic_ops.len()
+ self.float_single_ops.len()
+ self.float_double_ops.len()
+ self.compressed_ops.len()
+ self.vector_ops.len()
+ self.bitmanip_ops.len()
+ self.csr_ops.len()
+ self.pseudo_ops.len()
}
pub fn requires_m(&self, opcode: &RiscVOpcode) -> bool {
self.muldiv_ops.contains(opcode)
}
pub fn requires_a(&self, opcode: &RiscVOpcode) -> bool {
self.atomic_ops.contains(opcode)
}
pub fn requires_f(&self, opcode: &RiscVOpcode) -> bool {
self.float_single_ops.contains(opcode)
}
pub fn requires_d(&self, opcode: &RiscVOpcode) -> bool {
self.float_double_ops.contains(opcode)
}
pub fn requires_c(&self, opcode: &RiscVOpcode) -> bool {
self.compressed_ops.contains(opcode)
}
pub fn requires_v(&self, opcode: &RiscVOpcode) -> bool {
self.vector_ops.contains(opcode)
}
pub fn requires_bitmanip(&self, opcode: &RiscVOpcode) -> bool {
self.bitmanip_ops.contains(opcode)
}
pub fn list_for_extension(&self, ext: &str) -> Vec<&RiscVOpcode> {
match ext {
"m" => self.muldiv_ops.iter().collect(),
"a" => self.atomic_ops.iter().collect(),
"f" => self.float_single_ops.iter().collect(),
"d" => self.float_double_ops.iter().collect(),
"c" => self.compressed_ops.iter().collect(),
"v" => self.vector_ops.iter().collect(),
"zb" | "zba" | "zbb" | "zbc" | "zbs" => self.bitmanip_ops.iter().collect(),
_ => vec![],
}
}
pub fn find_x86_equivalent(&self, rv_opcode: &RiscVOpcode) -> Option<&'static str> {
match rv_opcode {
RiscVOpcode::ADD | RiscVOpcode::ADDI => Some("ADD"),
RiscVOpcode::SUB => Some("SUB"),
RiscVOpcode::AND | RiscVOpcode::ANDI => Some("AND"),
RiscVOpcode::OR | RiscVOpcode::ORI => Some("OR"),
RiscVOpcode::XOR | RiscVOpcode::XORI => Some("XOR"),
RiscVOpcode::SLL | RiscVOpcode::SLLI => Some("SHL"),
RiscVOpcode::SRL | RiscVOpcode::SRLI => Some("SHR"),
RiscVOpcode::SRA | RiscVOpcode::SRAI => Some("SAR"),
RiscVOpcode::MUL => Some("IMUL"),
RiscVOpcode::DIV => Some("IDIV"),
RiscVOpcode::REM => Some("IDIV"),
RiscVOpcode::SLT | RiscVOpcode::SLTI => Some("CMP+SETL"),
RiscVOpcode::SLTU => Some("CMP+SETB"),
RiscVOpcode::JAL | RiscVOpcode::JALR => Some("JMP"),
RiscVOpcode::BEQ => Some("JE"),
RiscVOpcode::BNE => Some("JNE"),
RiscVOpcode::LB | RiscVOpcode::LW | RiscVOpcode::LD => Some("MOV"),
RiscVOpcode::SB | RiscVOpcode::SW | RiscVOpcode::SD => Some("MOV"),
RiscVOpcode::CALL => Some("CALL"),
RiscVOpcode::RET => Some("RET"),
RiscVOpcode::NOP => Some("NOP"),
RiscVOpcode::FADD_S => Some("ADDSS"),
RiscVOpcode::FADD_D => Some("ADDSD"),
RiscVOpcode::FMUL_S => Some("MULSS"),
RiscVOpcode::FMUL_D => Some("MULSD"),
RiscVOpcode::FDIV_S => Some("DIVSS"),
RiscVOpcode::FDIV_D => Some("DIVSD"),
RiscVOpcode::FSQRT_S => Some("SQRTSS"),
RiscVOpcode::FSQRT_D => Some("SQRTSD"),
RiscVOpcode::FCVT_W_S => Some("CVTTSS2SI"),
RiscVOpcode::FCVT_W_D => Some("CVTTSD2SI"),
RiscVOpcode::FCVT_S_W => Some("CVTSI2SS"),
RiscVOpcode::FCVT_D_W => Some("CVTSI2SD"),
RiscVOpcode::CLZ => Some("LZCNT"),
RiscVOpcode::CTZ => Some("TZCNT"),
RiscVOpcode::CPOP => Some("POPCNT"),
RiscVOpcode::ANDN => Some("ANDN"),
RiscVOpcode::REV8 => Some("BSWAP"),
RiscVOpcode::ROR => Some("ROR"),
RiscVOpcode::ROL => Some("ROL"),
_ => None,
}
}
}
impl Default for BridgeRISCVInstrInfo {
fn default() -> Self {
Self::new()
}
}
pub const RV_GPR_NAMES: [&str; 32] = [
"zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1", "a0", "a1", "a2", "a3", "a4",
"a5", "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4",
"t5", "t6",
];
pub const RV_FPR_NAMES: [&str; 32] = [
"ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7", "fs0", "fs1", "fa0", "fa1", "fa2",
"fa3", "fa4", "fa5", "fa6", "fa7", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7", "fs8", "fs9",
"fs10", "fs11", "ft8", "ft9", "ft10", "ft11",
];
pub const RV_VR_NAMES: [&str; 32] = [
"v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", "v10", "v11", "v12", "v13", "v14",
"v15", "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27",
"v28", "v29", "v30", "v31",
];
pub struct BridgeRISCvRegisterInfo {
pub core: RiscVRegisterInfo,
pub name_to_idx: HashMap<String, u32>,
pub abi_to_idx: HashMap<String, u32>,
}
impl BridgeRISCvRegisterInfo {
pub fn new() -> Self {
let mut name_to_idx = HashMap::new();
let mut abi_to_idx = HashMap::new();
for i in 0..32u32 {
let x_name = format!("x{}", i);
let abi_name = RV_GPR_NAMES[i as usize].to_string();
name_to_idx.insert(x_name, i);
abi_to_idx.insert(abi_name, i);
}
for i in 0..32u32 {
let f_name = format!("f{}", i);
let abi_name = RV_FPR_NAMES[i as usize].to_string();
name_to_idx.insert(f_name, i + 32);
abi_to_idx.insert(abi_name, i + 32);
}
for i in 0..32u32 {
let v_name = format!("v{}", i);
name_to_idx.insert(v_name, i + 64);
}
Self {
core: RiscVRegisterInfo,
name_to_idx,
abi_to_idx,
}
}
pub fn lookup(&self, name: &str) -> Option<u32> {
self.name_to_idx
.get(name)
.copied()
.or_else(|| self.abi_to_idx.get(name).copied())
}
pub fn gpr_abi_name(&self, idx: u32) -> &str {
if (idx as usize) < RV_GPR_NAMES.len() {
RV_GPR_NAMES[idx as usize]
} else {
"unknown"
}
}
pub fn fpr_abi_name(&self, idx: u32) -> &str {
if (idx as usize) < RV_FPR_NAMES.len() {
RV_FPR_NAMES[idx as usize]
} else {
"unknown"
}
}
pub fn vr_name(&self, idx: u32) -> &str {
if (idx as usize) < RV_VR_NAMES.len() {
RV_VR_NAMES[idx as usize]
} else {
"unknown"
}
}
pub fn is_gpr(&self, idx: u32) -> bool {
idx < 32
}
pub fn is_fpr(&self, idx: u32) -> bool {
idx >= 32 && idx < 64
}
pub fn is_vr(&self, idx: u32) -> bool {
idx >= 64 && idx < 96
}
pub fn callee_saved_gprs() -> Vec<u32> {
vec![
1, 2, 3, 8, 9, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, ]
}
pub fn callee_saved_fprs() -> Vec<u32> {
vec![
40, 41, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, ]
}
pub fn caller_saved_gprs() -> Vec<u32> {
vec![
5, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17, 28, 29, 30, 31, ]
}
pub fn arg_regs() -> Vec<u32> {
(10..=17).collect()
}
pub fn fp_arg_regs() -> Vec<u32> {
(42..=49).collect() }
pub fn return_regs() -> Vec<u32> {
vec![10, 11] }
pub fn fp_return_regs() -> Vec<u32> {
vec![42, 43] }
pub fn map_to_x86(&self, rv_reg: u32) -> Option<&'static str> {
match rv_reg {
0 => Some("zero"), 1 => Some("ra"), 2 => Some("rsp"), 3 => Some("gp (no X86 eq.)"), 4 => Some("fs/gs (no direct eq.)"), 5..=7 => Some("rcx/rdx/r8-like"), 8..=9 => Some("rbx/rbp-like"), 10..=17 => Some("rdi/rsi/rdx/rcx/r8/r9-like"), 28..=31 => Some("r10/r11/r12/r13-like"), 32..=63 => None, _ => None,
}
}
}
impl Default for BridgeRISCvRegisterInfo {
fn default() -> Self {
Self::new()
}
}
pub struct BridgeRISCVFrameLowering {
pub core: RiscVFrameLowering,
pub frame_info: RiscVFrameInfo,
pub shared_ctx: SharedFrameLoweringContext,
}
impl BridgeRISCVFrameLowering {
pub fn new_rv64() -> Self {
Self {
core: RiscVFrameLowering::new(true),
frame_info: RiscVFrameInfo::new(true),
shared_ctx: SharedFrameLoweringContext {
is_64bit: true,
..Default::default()
},
}
}
pub fn new_rv32() -> Self {
Self {
core: RiscVFrameLowering::new(false),
frame_info: RiscVFrameInfo::new(false),
shared_ctx: SharedFrameLoweringContext {
is_64bit: false,
..Default::default()
},
}
}
pub fn prologue_asm(&self) -> String {
let mut asm = Vec::new();
if self.shared_ctx.has_frame_pointer {
let reg_width = if self.shared_ctx.is_64bit { 8 } else { 4 };
asm.push(format!("addi sp, sp, -{}", self.shared_ctx.frame_size));
if reg_width == 8 {
asm.push("sd ra, 8(sp)".to_string());
asm.push("sd s0, 0(sp)".to_string());
} else {
asm.push("sw ra, 4(sp)".to_string());
asm.push("sw s0, 0(sp)".to_string());
}
asm.push("addi s0, sp, 0".to_string());
} else if self.shared_ctx.frame_size > 0 {
asm.push(format!("addi sp, sp, -{}", self.shared_ctx.frame_size));
}
asm.join("\n")
}
pub fn epilogue_asm(&self) -> String {
let mut asm = Vec::new();
if self.shared_ctx.has_frame_pointer {
asm.push("addi sp, s0, 0".to_string());
let reg_width = if self.shared_ctx.is_64bit { 8 } else { 4 };
if reg_width == 8 {
asm.push("ld ra, 8(sp)".to_string());
asm.push("ld s0, 0(sp)".to_string());
} else {
asm.push("lw ra, 4(sp)".to_string());
asm.push("lw s0, 0(sp)".to_string());
}
asm.push(format!("addi sp, sp, {}", self.shared_ctx.frame_size));
} else if self.shared_ctx.frame_size > 0 {
asm.push(format!("addi sp, sp, {}", self.shared_ctx.frame_size));
}
asm.push("ret".to_string());
asm.join("\n")
}
pub fn compute_frame_size(&mut self, local_area_size: u32, num_saved_regs: u32) -> u32 {
let reg_width = if self.shared_ctx.is_64bit { 8 } else { 4 };
let saved_regs_size = num_saved_regs * reg_width;
let total = local_area_size + saved_regs_size;
let aligned = (total + 15) & !15;
self.shared_ctx.frame_size = aligned;
aligned
}
pub fn set_has_calls(&mut self, has_calls: bool) {
self.shared_ctx.has_calls = has_calls;
}
pub fn enable_frame_pointer(&mut self, enable: bool) {
self.shared_ctx.has_frame_pointer = enable;
}
}
impl Default for BridgeRISCVFrameLowering {
fn default() -> Self {
Self::new_rv64()
}
}
pub struct BridgeRISCVCallingConvention {
pub core: RiscVCallingConvention,
pub abi_name: String,
pub is_64bit: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RiscVABI {
ILP32,
ILP32F,
ILP32D,
LP64,
LP64F,
LP64D,
}
impl RiscVABI {
pub fn as_str(&self) -> &str {
match self {
Self::ILP32 => "ilp32",
Self::ILP32F => "ilp32f",
Self::ILP32D => "ilp32d",
Self::LP64 => "lp64",
Self::LP64F => "lp64f",
Self::LP64D => "lp64d",
}
}
pub fn is_64bit(&self) -> bool {
matches!(self, Self::LP64 | Self::LP64F | Self::LP64D)
}
pub fn has_fp_regs(&self) -> bool {
matches!(
self,
Self::ILP32F | Self::ILP32D | Self::LP64F | Self::LP64D
)
}
pub fn xlen(&self) -> u32 {
if self.is_64bit() {
64
} else {
32
}
}
pub fn parse(s: &str) -> Option<Self> {
match s.to_lowercase().as_str() {
"ilp32" => Some(Self::ILP32),
"ilp32f" => Some(Self::ILP32F),
"ilp32d" => Some(Self::ILP32D),
"lp64" => Some(Self::LP64),
"lp64f" => Some(Self::LP64F),
"lp64d" => Some(Self::LP64D),
_ => None,
}
}
pub fn default_for_xlen(xlen: u32) -> Self {
match xlen {
64 => Self::LP64D,
_ => Self::ILP32D,
}
}
}
impl BridgeRISCVCallingConvention {
pub fn new(is_64bit: bool) -> Self {
let abi_name = if is_64bit {
"lp64d".to_string()
} else {
"ilp32d".to_string()
};
Self {
core: RiscVCallingConvention::default(),
abi_name,
is_64bit,
}
}
pub fn with_abi(abi: RiscVABI) -> Self {
Self {
core: RiscVCallingConvention::default(),
abi_name: abi.as_str().to_string(),
is_64bit: abi.is_64bit(),
}
}
pub fn int_arg_regs(&self) -> Vec<u32> {
(10..=17).collect()
}
pub fn fp_arg_regs(&self) -> Vec<u32> {
(42..=49).collect()
}
pub fn int_return_regs(&self) -> Vec<u32> {
vec![10, 11]
}
pub fn fp_return_regs(&self) -> Vec<u32> {
vec![42, 43]
}
pub fn callee_saved_regs(&self) -> Vec<u32> {
let mut regs = vec![
1, 2, 8, 9, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, ];
if self.abi_name.contains('f') || self.abi_name.contains('d') {
regs.extend(&[40, 41, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59]);
}
regs
}
pub fn caller_saved_regs(&self) -> Vec<u32> {
let mut regs = vec![
5, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17, 28, 29, 30, 31, ];
if self.abi_name.contains('f') || self.abi_name.contains('d') {
regs.extend(&[
32, 33, 34, 35, 36, 37, 38, 39, 42, 43, 44, 45, 46, 47, 48, 49, 60, 61, 62, 63, ]);
}
regs
}
pub fn stack_alignment(&self) -> u32 {
16
}
pub fn red_zone_size(&self) -> u32 {
0
}
pub fn describe(&self) -> String {
format!(
"RISC-V Calling Convention ({})\n Integer args: a0-a7 (x10-x17)\n FP args: fa0-fa7 (f10-f17)\n Integer return: a0-a1 (x10-x11)\n FP return: fa0-fa1 (f10-f11)\n Stack alignment: {} bytes\n Red zone: {} bytes\n Callee-saved: {} GPRs + {} FPRs",
self.abi_name,
self.stack_alignment(),
self.red_zone_size(),
self.callee_saved_regs().len() - self.callee_saved_regs().iter().filter(|r| **r >= 32).count(),
self.callee_saved_regs().iter().filter(|r| **r >= 32).count(),
)
}
pub fn classify_struct_arg(&self, size_bytes: u32, has_float_fields: bool) -> ArgPassingClass {
let xlen = if self.is_64bit { 8 } else { 4 };
let max_int_reg_size = 2 * xlen;
if has_float_fields && self.abi_name.contains('f') {
if size_bytes <= 8 {
ArgPassingClass::FloatRegister
} else if size_bytes <= 16 {
ArgPassingClass::FloatRegisterPair
} else {
ArgPassingClass::Memory
}
} else if size_bytes <= max_int_reg_size {
if size_bytes <= xlen {
ArgPassingClass::IntegerRegister
} else {
ArgPassingClass::IntegerRegisterPair
}
} else {
ArgPassingClass::Memory
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArgPassingClass {
IntegerRegister,
IntegerRegisterPair,
FloatRegister,
FloatRegisterPair,
Memory,
}
impl fmt::Display for ArgPassingClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IntegerRegister => write!(f, "IntegerRegister"),
Self::IntegerRegisterPair => write!(f, "IntegerRegisterPair"),
Self::FloatRegister => write!(f, "FloatRegister"),
Self::FloatRegisterPair => write!(f, "FloatRegisterPair"),
Self::Memory => write!(f, "Memory"),
}
}
}
pub struct X86RISCVComparisons {
pub isa_comparison: ISetComparison,
pub code_size_comparison: CodeSizeComparison,
pub perf_comparison: PerformanceComparison,
pub opt_comparison: OptimizationComparison,
pub metrics: ComparisonMetrics,
}
#[derive(Debug, Default, Clone)]
pub struct ComparisonMetrics {
pub comparisons_run: u64,
pub avg_code_size_ratio: f64,
pub avg_perf_ratio: f64,
pub avg_instr_count_ratio: f64,
pub avg_reg_pressure_ratio: f64,
}
impl X86RISCVComparisons {
pub fn new() -> Self {
Self {
isa_comparison: ISetComparison::new(),
code_size_comparison: CodeSizeComparison::new(),
perf_comparison: PerformanceComparison::new(),
opt_comparison: OptimizationComparison::new(),
metrics: ComparisonMetrics::default(),
}
}
pub fn run_all(&mut self) {
self.isa_comparison.compare();
self.code_size_comparison.analyze();
self.perf_comparison.analyze();
self.opt_comparison.analyze();
self.metrics.comparisons_run += 1;
}
pub fn generate_report(&self) -> String {
let mut report = String::new();
report.push_str("=== X86 ↔ RISC-V Cross-Target Comparison Report ===\n\n");
report.push_str(&self.isa_comparison.report());
report.push_str("\n");
report.push_str(&self.code_size_comparison.report());
report.push_str("\n");
report.push_str(&self.perf_comparison.report());
report.push_str("\n");
report.push_str(&self.opt_comparison.report());
report
}
}
impl Default for X86RISCVComparisons {
fn default() -> Self {
Self::new()
}
}
pub struct ISetComparison {
pub x86_int_alu_count: usize,
pub riscv_int_alu_count: usize,
pub x86_fp_count: usize,
pub riscv_fp_count: usize,
pub x86_simd_count: usize,
pub riscv_vector_count: usize,
pub x86_addr_modes: usize,
pub riscv_addr_modes: usize,
pub x86_encoding_variability: f64,
pub riscv_encoding_variability: f64,
pub summary: String,
}
impl ISetComparison {
pub fn new() -> Self {
Self {
x86_int_alu_count: 0,
riscv_int_alu_count: 0,
x86_fp_count: 0,
riscv_fp_count: 0,
x86_simd_count: 0,
riscv_vector_count: 0,
x86_addr_modes: 0,
riscv_addr_modes: 0,
x86_encoding_variability: 0.0,
riscv_encoding_variability: 0.0,
summary: String::new(),
}
}
pub fn compare(&mut self) {
self.x86_int_alu_count = 45;
self.riscv_int_alu_count = 35;
self.x86_fp_count = 30;
self.riscv_fp_count = 56;
self.x86_simd_count = 350;
self.riscv_vector_count = 200;
self.x86_addr_modes = 8;
self.riscv_addr_modes = 2;
self.x86_encoding_variability = 15.0;
self.riscv_encoding_variability = 2.0;
self.summary = self.build_summary();
}
fn build_summary(&self) -> String {
let mut s = String::from("Instruction Set Summary:\n");
s.push_str(&format!(
" X86 integer ALU ops: {}\n",
self.x86_int_alu_count
));
s.push_str(&format!(
" RISC-V integer ALU ops: {}\n",
self.riscv_int_alu_count
));
s.push_str(&format!(" X86 FP ops: {}\n", self.x86_fp_count));
s.push_str(&format!(
" RISC-V FP ops: {}\n",
self.riscv_fp_count
));
s.push_str(&format!(
" X86 SIMD/Vector ops: {}\n",
self.x86_simd_count
));
s.push_str(&format!(
" RISC-V Vector ops: {}\n",
self.riscv_vector_count
));
s.push_str(&format!(
" X86 addressing modes: {}\n",
self.x86_addr_modes
));
s.push_str(&format!(
" RISC-V addressing modes: {}\n",
self.riscv_addr_modes
));
s.push_str(&format!(
" X86 encoding range: {} bytes\n",
self.x86_encoding_variability as u32
));
s.push_str(&format!(" RISC-V encoding range: {} or 32 bits\n", 16));
s.push_str("\nKey Differences:\n");
s.push_str(" - X86 is CISC with variable-length encoding (1-15 bytes)\n");
s.push_str(" - RISC-V is RISC with fixed 32-bit encoding (+ 16-bit compressed)\n");
s.push_str(" - X86 has memory operands in ALU instructions\n");
s.push_str(" - RISC-V is load-store; ALU ops are register-only\n");
s.push_str(" - X86 has implicit flag setting on most ops\n");
s.push_str(" - RISC-V uses explicit compare (SLT/SLTU) and branch instructions\n");
s
}
pub fn report(&self) -> String {
if self.summary.is_empty() {
return "ISA comparison not yet run.".to_string();
}
self.summary.clone()
}
}
pub struct CodeSizeComparison {
pub x86_avg_instr_size: f64,
pub riscv_avg_instr_size: f64,
pub size_ratio: f64,
pub ratio_with_compressed: f64,
pub instr_count_ratio: f64,
pub summary: String,
}
impl CodeSizeComparison {
pub fn new() -> Self {
Self {
x86_avg_instr_size: 0.0,
riscv_avg_instr_size: 0.0,
size_ratio: 0.0,
ratio_with_compressed: 0.0,
instr_count_ratio: 0.0,
summary: String::new(),
}
}
pub fn analyze(&mut self) {
self.x86_avg_instr_size = 3.5;
self.riscv_avg_instr_size = 4.0;
self.ratio_with_compressed = 3.0;
self.instr_count_ratio = 1.15;
self.size_ratio =
(self.instr_count_ratio * self.riscv_avg_instr_size) / self.x86_avg_instr_size;
self.summary = self.build_summary();
}
fn build_summary(&self) -> String {
format!(
"Code Size Comparison:\n\
X86 avg instr size: {:.1} bytes (variable 1-15)\n\
RISC-V avg instr size: {:.1} bytes (fixed 32-bit)\n\
RISC-V with C ext: {:.1} bytes (mixed 16/32-bit)\n\
Instruction count ratio (RV/X86): {:.2}x\n\
Code size ratio (standard RV): {:.2}x\n\
Code size ratio (RV with C): {:.2}x\n\
\n\
Assessment:\n\
- Without C extension, RISC-V typically produces ~{:.0}% larger code\n\
- With C extension, RISC-V can match or slightly exceed X86 density\n\
- X86's variable-length encoding gives best density for simple ops\n\
- RISC-V's fixed encoding simplifies decoding and pipelining\n",
self.x86_avg_instr_size,
self.riscv_avg_instr_size,
self.ratio_with_compressed,
self.instr_count_ratio,
self.size_ratio,
(self.instr_count_ratio * self.ratio_with_compressed) / self.x86_avg_instr_size,
(self.size_ratio - 1.0) * 100.0,
)
}
pub fn report(&self) -> String {
if self.summary.is_empty() {
return "Code size comparison not yet run.".to_string();
}
self.summary.clone()
}
}
pub struct PerformanceComparison {
pub x86_ipc: f64,
pub riscv_ipc: f64,
pub x86_branch_pred: f64,
pub riscv_branch_pred: f64,
pub x86_cache_miss_rate: f64,
pub riscv_cache_miss_rate: f64,
pub x86_pipeline_depth: u32,
pub riscv_pipeline_depth: u32,
pub x86_reg_pressure: f64,
pub riscv_reg_pressure: f64,
pub summary: String,
}
impl PerformanceComparison {
pub fn new() -> Self {
Self {
x86_ipc: 0.0,
riscv_ipc: 0.0,
x86_branch_pred: 0.0,
riscv_branch_pred: 0.0,
x86_cache_miss_rate: 0.0,
riscv_cache_miss_rate: 0.0,
x86_pipeline_depth: 0,
riscv_pipeline_depth: 0,
x86_reg_pressure: 0.0,
riscv_reg_pressure: 0.0,
summary: String::new(),
}
}
pub fn analyze(&mut self) {
self.x86_ipc = 3.5;
self.riscv_ipc = 2.5;
self.x86_branch_pred = 0.97; self.riscv_branch_pred = 0.95;
self.x86_cache_miss_rate = 0.03;
self.riscv_cache_miss_rate = 0.03;
self.x86_pipeline_depth = 14; self.riscv_pipeline_depth = 8;
self.x86_reg_pressure = 0.65; self.riscv_reg_pressure = 0.35;
self.summary = self.build_summary();
}
fn build_summary(&self) -> String {
format!(
"Performance Characteristics:\n\
X86 typical IPC: {:.1}\n\
RISC-V typical IPC: {:.1}\n\
X86 branch predict: {:.0}%\n\
RISC-V branch predict: {:.0}%\n\
X86 pipeline depth: {}\n\
RISC-V pipeline depth: {}\n\
X86 register pressure: {:.2}\n\
RISC-V register pressure: {:.2}\n\
\n\
Key Insights:\n\
- X86 generally achieves higher IPC due to micro-op fusion and\n\
sophisticated out-of-order execution\n\
- RISC-V's larger register file (31 vs 16 GPRs) reduces spill/\n\
fill overhead, especially in complex functions\n\
- RISC-V's simpler decode enables shorter pipeline, reducing\n\
branch mispredict penalty\n\
- For most general-purpose code, X86 has ~{:.0}% performance\n\
advantage at equal frequency, but RISC-V can close the gap\n\
with higher clock speeds and better power efficiency\n",
self.x86_ipc,
self.riscv_ipc,
self.x86_branch_pred * 100.0,
self.riscv_branch_pred * 100.0,
self.x86_pipeline_depth,
self.riscv_pipeline_depth,
self.x86_reg_pressure,
self.riscv_reg_pressure,
((self.x86_ipc / self.riscv_ipc) - 1.0) * 100.0,
)
}
pub fn report(&self) -> String {
if self.summary.is_empty() {
return "Performance comparison not yet run.".to_string();
}
self.summary.clone()
}
}
pub struct OptimizationComparison {
pub x86_strategies: Vec<String>,
pub riscv_strategies: Vec<String>,
pub shared_strategies: Vec<String>,
pub summary: String,
}
impl OptimizationComparison {
pub fn new() -> Self {
Self {
x86_strategies: Vec::new(),
riscv_strategies: Vec::new(),
shared_strategies: Vec::new(),
summary: String::new(),
}
}
pub fn analyze(&mut self) {
self.x86_strategies = vec![
"Instruction fusion (cmp+jmp, dec+jne, etc.)",
"LEA-based address computation and small arithmetic",
"Memory operand folding (combine load+op)",
"Micro-op cache optimization",
"Macro-op fusion for branch pairs",
"Partial register stall avoidance",
"Stack engine optimization (push/pop elimination)",
"Register renaming for false dependency breaking",
"Loop stream detector / LSD optimization",
"X86-specific intrinsics for SIMD (SSE/AVX/AVX-512)",
]
.into_iter()
.map(String::from)
.collect();
self.riscv_strategies = vec![
"Compressed instruction generation (C extension)",
"LUI+ADDI constant materialization optimization",
"Load-store pair formation for adjacent accesses",
"Branch relaxation (short vs long branches)",
"Table-based jump for dense switch statements",
"AUIPC-based PC-relative addressing optimization",
"Zero-register (x0) utilization for moves and comparisons",
"Fused multiply-add (FMADD/FMSUB) generation",
"Vector length-agnostic (VLA) code generation",
"Linker relaxation (GP-relative, PC-relative, etc.)",
]
.into_iter()
.map(String::from)
.collect();
self.shared_strategies = vec![
"Constant folding and propagation",
"Dead code elimination",
"Common subexpression elimination (CSE)",
"Loop-invariant code motion (LICM)",
"Loop unrolling with heuristics",
"Strength reduction",
"Induction variable simplification",
"Tail call optimization",
"Inlining with cost model",
"Register allocation (greedy/graph coloring)",
"Instruction scheduling for pipeline efficiency",
"Peephole optimization",
"Branch prediction hint placement",
"If-conversion (cmov, select)",
]
.into_iter()
.map(String::from)
.collect();
self.summary = self.build_summary();
}
fn build_summary(&self) -> String {
format!(
"Optimization Strategy Comparison:\n\
X86-specific strategies: {}\n\
RISC-V-specific strategies: {}\n\
Shared strategies: {}\n\
\n\
Key Differences:\n\
- X86 optimizations focus heavily on instruction fusion and\n\
micro-architectural features (uop cache, LSD, stack engine)\n\
- RISC-V optimizations emphasize code density (C extension),\n\
linker relaxation, and exploiting the larger register file\n\
- Both benefit from standard compiler optimizations (CSE, LICM,\n\
inlining, loop unrolling, etc.)\n\
- RISC-V's load-store nature means memory operand folding is not\n\
possible; instead load-pair and store-pair formation is used\n\
- X86's variable-length encoding requires size optimization\n\
strategies that don't apply to RISC-V's fixed encoding\n\
- RISC-V's compressed extension provides ~25-30% code size\n\
reduction with minimal decode penalty\n",
self.x86_strategies.len(),
self.riscv_strategies.len(),
self.shared_strategies.len(),
)
}
pub fn report(&self) -> String {
if self.summary.is_empty() {
return "Optimization comparison not yet run.".to_string();
}
self.summary.clone()
}
}
pub fn build_riscv_to_x86_map() -> HashMap<RiscVOpcode, String> {
let mut map = HashMap::new();
map.insert(RiscVOpcode::ADD, "ADD".into());
map.insert(RiscVOpcode::ADDI, "ADD".into());
map.insert(RiscVOpcode::SUB, "SUB".into());
map.insert(RiscVOpcode::AND, "AND".into());
map.insert(RiscVOpcode::ANDI, "AND".into());
map.insert(RiscVOpcode::OR, "OR".into());
map.insert(RiscVOpcode::ORI, "OR".into());
map.insert(RiscVOpcode::XOR, "XOR".into());
map.insert(RiscVOpcode::XORI, "XOR".into());
map.insert(RiscVOpcode::SLL, "SHL".into());
map.insert(RiscVOpcode::SLLI, "SHL".into());
map.insert(RiscVOpcode::SRL, "SHR".into());
map.insert(RiscVOpcode::SRLI, "SHR".into());
map.insert(RiscVOpcode::SRA, "SAR".into());
map.insert(RiscVOpcode::SRAI, "SAR".into());
map.insert(RiscVOpcode::MUL, "IMUL".into());
map.insert(RiscVOpcode::DIV, "IDIV".into());
map.insert(RiscVOpcode::REM, "IDIV+MOV".into());
map.insert(RiscVOpcode::SLT, "CMP+SETL".into());
map.insert(RiscVOpcode::SLTU, "CMP+SETB".into());
map.insert(RiscVOpcode::JAL, "JMP".into());
map.insert(RiscVOpcode::JALR, "JMP".into());
map.insert(RiscVOpcode::BEQ, "JE".into());
map.insert(RiscVOpcode::BNE, "JNE".into());
map.insert(RiscVOpcode::BLT, "JL".into());
map.insert(RiscVOpcode::BGE, "JGE".into());
map.insert(RiscVOpcode::LB, "MOVSX".into());
map.insert(RiscVOpcode::LW, "MOV".into());
map.insert(RiscVOpcode::LD, "MOV".into());
map.insert(RiscVOpcode::SB, "MOV".into());
map.insert(RiscVOpcode::SW, "MOV".into());
map.insert(RiscVOpcode::SD, "MOV".into());
map.insert(RiscVOpcode::CALL, "CALL".into());
map.insert(RiscVOpcode::RET, "RET".into());
map.insert(RiscVOpcode::NOP, "NOP".into());
map.insert(RiscVOpcode::LUI, "MOV".into());
map.insert(RiscVOpcode::AUIPC, "LEA".into());
map.insert(RiscVOpcode::FADD_S, "ADDSS".into());
map.insert(RiscVOpcode::FADD_D, "ADDSD".into());
map.insert(RiscVOpcode::FSUB_S, "SUBSS".into());
map.insert(RiscVOpcode::FSUB_D, "SUBSD".into());
map.insert(RiscVOpcode::FMUL_S, "MULSS".into());
map.insert(RiscVOpcode::FMUL_D, "MULSD".into());
map.insert(RiscVOpcode::FDIV_S, "DIVSS".into());
map.insert(RiscVOpcode::FDIV_D, "DIVSD".into());
map
}
pub fn build_x86_to_riscv_map() -> HashMap<String, RiscVOpcode> {
let mut map = HashMap::new();
map.insert("ADD".into(), RiscVOpcode::ADD);
map.insert("SUB".into(), RiscVOpcode::SUB);
map.insert("AND".into(), RiscVOpcode::AND);
map.insert("OR".into(), RiscVOpcode::OR);
map.insert("XOR".into(), RiscVOpcode::XOR);
map.insert("SHL".into(), RiscVOpcode::SLL);
map.insert("SHR".into(), RiscVOpcode::SRL);
map.insert("SAR".into(), RiscVOpcode::SRA);
map.insert("IMUL".into(), RiscVOpcode::MUL);
map.insert("IDIV".into(), RiscVOpcode::DIV);
map.insert("JMP".into(), RiscVOpcode::JAL);
map.insert("JE".into(), RiscVOpcode::BEQ);
map.insert("JNE".into(), RiscVOpcode::BNE);
map.insert("JL".into(), RiscVOpcode::BLT);
map.insert("JGE".into(), RiscVOpcode::BGE);
map.insert("CALL".into(), RiscVOpcode::CALL);
map.insert("RET".into(), RiscVOpcode::RET);
map.insert("NOP".into(), RiscVOpcode::NOP);
map.insert("ADDSS".into(), RiscVOpcode::FADD_S);
map.insert("ADDSD".into(), RiscVOpcode::FADD_D);
map.insert("SUBSS".into(), RiscVOpcode::FSUB_S);
map.insert("SUBSD".into(), RiscVOpcode::FSUB_D);
map.insert("MULSS".into(), RiscVOpcode::FMUL_S);
map.insert("MULSD".into(), RiscVOpcode::FMUL_D);
map.insert("DIVSS".into(), RiscVOpcode::FDIV_S);
map.insert("DIVSD".into(), RiscVOpcode::FDIV_D);
map.insert("MOV".into(), RiscVOpcode::ADDI);
map
}
pub fn riscv_opcode_cost(opcode: &RiscVOpcode) -> u32 {
match opcode {
RiscVOpcode::ADD
| RiscVOpcode::ADDI
| RiscVOpcode::SUB
| RiscVOpcode::AND
| RiscVOpcode::ANDI
| RiscVOpcode::OR
| RiscVOpcode::ORI
| RiscVOpcode::XOR
| RiscVOpcode::XORI
| RiscVOpcode::SLL
| RiscVOpcode::SLLI
| RiscVOpcode::SRL
| RiscVOpcode::SRLI
| RiscVOpcode::SRA
| RiscVOpcode::SRAI
| RiscVOpcode::SLT
| RiscVOpcode::SLTI
| RiscVOpcode::SLTU
| RiscVOpcode::SLTIU
| RiscVOpcode::LUI
| RiscVOpcode::AUIPC => 1,
RiscVOpcode::MUL | RiscVOpcode::MULW => 3,
RiscVOpcode::MULH | RiscVOpcode::MULHU | RiscVOpcode::MULHSU => 3,
RiscVOpcode::DIV | RiscVOpcode::DIVU | RiscVOpcode::DIVW | RiscVOpcode::DIVUW => 32,
RiscVOpcode::REM | RiscVOpcode::REMU | RiscVOpcode::REMW | RiscVOpcode::REMUW => 32,
RiscVOpcode::BEQ
| RiscVOpcode::BNE
| RiscVOpcode::BLT
| RiscVOpcode::BGE
| RiscVOpcode::BLTU
| RiscVOpcode::BGEU => 1,
RiscVOpcode::JAL | RiscVOpcode::JALR => 1,
RiscVOpcode::CALL | RiscVOpcode::RET => 2,
RiscVOpcode::LB
| RiscVOpcode::LH
| RiscVOpcode::LW
| RiscVOpcode::LBU
| RiscVOpcode::LHU
| RiscVOpcode::LWU
| RiscVOpcode::LD => 4,
RiscVOpcode::FLW => 4,
RiscVOpcode::FLD => 4,
RiscVOpcode::SB | RiscVOpcode::SH | RiscVOpcode::SW | RiscVOpcode::SD => 1,
RiscVOpcode::FSW | RiscVOpcode::FSD => 1,
RiscVOpcode::FADD_S | RiscVOpcode::FSUB_S => 3,
RiscVOpcode::FMUL_S => 3,
RiscVOpcode::FDIV_S => 12,
RiscVOpcode::FSQRT_S => 12,
RiscVOpcode::FADD_D | RiscVOpcode::FSUB_D => 3,
RiscVOpcode::FMUL_D => 5,
RiscVOpcode::FDIV_D => 20,
RiscVOpcode::FSQRT_D => 20,
RiscVOpcode::FMADD_S
| RiscVOpcode::FMSUB_S
| RiscVOpcode::FNMSUB_S
| RiscVOpcode::FNMADD_S => 3,
RiscVOpcode::FMADD_D
| RiscVOpcode::FMSUB_D
| RiscVOpcode::FNMSUB_D
| RiscVOpcode::FNMADD_D => 5,
RiscVOpcode::FCVT_W_S
| RiscVOpcode::FCVT_WU_S
| RiscVOpcode::FCVT_S_W
| RiscVOpcode::FCVT_S_WU => 5,
RiscVOpcode::FCVT_L_S
| RiscVOpcode::FCVT_LU_S
| RiscVOpcode::FCVT_S_L
| RiscVOpcode::FCVT_S_LU => 5,
RiscVOpcode::FCVT_W_D
| RiscVOpcode::FCVT_WU_D
| RiscVOpcode::FCVT_D_W
| RiscVOpcode::FCVT_D_WU => 5,
RiscVOpcode::FCVT_L_D
| RiscVOpcode::FCVT_LU_D
| RiscVOpcode::FCVT_D_L
| RiscVOpcode::FCVT_D_LU => 5,
RiscVOpcode::FCVT_S_D | RiscVOpcode::FCVT_D_S => 2,
RiscVOpcode::FENCE | RiscVOpcode::FENCE_I | RiscVOpcode::FENCE_TSO => 10,
RiscVOpcode::ECALL | RiscVOpcode::EBREAK => 100,
RiscVOpcode::LR_W | RiscVOpcode::LR_D => 10,
RiscVOpcode::SC_W | RiscVOpcode::SC_D => 10,
RiscVOpcode::AMOSWAP_W
| RiscVOpcode::AMOADD_W
| RiscVOpcode::AMOXOR_W
| RiscVOpcode::AMOAND_W
| RiscVOpcode::AMOOR_W
| RiscVOpcode::AMOMIN_W
| RiscVOpcode::AMOMAX_W
| RiscVOpcode::AMOMINU_W
| RiscVOpcode::AMOMAXU_W => 10,
_ => 2,
}
}
pub fn x86_opcode_cost(opcode: &X86Opcode) -> u32 {
match opcode {
X86Opcode::ADD
| X86Opcode::SUB
| X86Opcode::AND
| X86Opcode::OR
| X86Opcode::XOR
| X86Opcode::NOT
| X86Opcode::NEG
| X86Opcode::INC
| X86Opcode::DEC => 1,
X86Opcode::SHL | X86Opcode::SHR | X86Opcode::SAR => 1,
X86Opcode::MOV | X86Opcode::MOVSX | X86Opcode::MOVZX => 1,
X86Opcode::LEA => 1,
X86Opcode::CMP | X86Opcode::TEST => 1,
X86Opcode::MUL | X86Opcode::IMUL => 3,
X86Opcode::DIV | X86Opcode::IDIV => 32,
X86Opcode::JMP
| X86Opcode::JE
| X86Opcode::JNE
| X86Opcode::JL
| X86Opcode::JGE
| X86Opcode::JLE
| X86Opcode::JG => 1,
X86Opcode::CALL => 2,
X86Opcode::RET => 2,
X86Opcode::ADDSS | X86Opcode::ADDSD => 3,
X86Opcode::SUBSS | X86Opcode::SUBSD => 3,
X86Opcode::MULSS | X86Opcode::MULSD => 3,
X86Opcode::DIVSS => 10,
X86Opcode::DIVSD => 14,
X86Opcode::SQRTSS => 10,
X86Opcode::SQRTSD => 14,
X86Opcode::ADDPS | X86Opcode::ADDPD => 3,
X86Opcode::MULPS | X86Opcode::MULPD => 3,
X86Opcode::CVTSI2SS | X86Opcode::CVTSI2SD => 5,
X86Opcode::CVTTSS2SI | X86Opcode::CVTTSD2SI => 5,
X86Opcode::CVTSS2SD | X86Opcode::CVTSD2SS => 2,
X86Opcode::ANDN | X86Opcode::BLSI | X86Opcode::BLSR | X86Opcode::BLSMSK => 1,
X86Opcode::LZCNT | X86Opcode::TZCNT | X86Opcode::POPCNT => 3,
X86Opcode::BSWAP => 1,
X86Opcode::PUSH | X86Opcode::POP => 1,
_ => 2,
}
}
pub fn compare_instruction_cost(rv_opcode: &RiscVOpcode, x86_opcode: &X86Opcode) -> CostComparison {
let rv_cost = riscv_opcode_cost(rv_opcode);
let x86_cost = x86_opcode_cost(x86_opcode);
let ratio = rv_cost as f64 / x86_cost.max(1) as f64;
CostComparison {
operation: format!("{:?} vs {:?}", rv_opcode, x86_opcode),
riscv_cost: rv_cost,
x86_cost,
ratio,
assessment: if ratio < 0.8 {
"RISC-V is faster".into()
} else if ratio > 1.2 {
"X86 is faster".into()
} else {
"Similar cost".into()
},
}
}
#[derive(Debug, Clone)]
pub struct CostComparison {
pub operation: String,
pub riscv_cost: u32,
pub x86_cost: u32,
pub ratio: f64,
pub assessment: String,
}
pub struct CrossTargetOptimizationTransfer {
pub x86_to_riscv_rules: Vec<TransferRule>,
pub riscv_to_x86_rules: Vec<TransferRule>,
pub transfer_stats: TransferStats,
}
#[derive(Debug, Clone)]
pub struct TransferRule {
pub name: String,
pub source: TargetArch,
pub destination: TargetArch,
pub source_pattern: Vec<String>,
pub dest_pattern: Vec<String>,
pub estimated_benefit: f64,
pub validated: bool,
}
#[derive(Debug, Default, Clone)]
pub struct TransferStats {
pub total_attempts: u64,
pub successful: u64,
pub beneficial: u64,
pub harmful: u64,
pub avg_benefit: f64,
}
impl CrossTargetOptimizationTransfer {
pub fn new() -> Self {
Self {
x86_to_riscv_rules: Vec::new(),
riscv_to_x86_rules: Vec::new(),
transfer_stats: TransferStats::default(),
}
}
pub fn load_rules(&mut self) {
self.x86_to_riscv_rules = vec![
TransferRule {
name: "fold_mem_operand_to_load_op".into(),
source: TargetArch::X86,
destination: TargetArch::RISCV64,
source_pattern: vec!["op_with_mem".into()],
dest_pattern: vec!["load".into(), "reg_op".into()],
estimated_benefit: 0.02,
validated: false,
},
TransferRule {
name: "lea_to_shift_add".into(),
source: TargetArch::X86,
destination: TargetArch::RISCV64,
source_pattern: vec!["lea".into(), "base".into(), "index".into(), "scale".into()],
dest_pattern: vec!["slli".into(), "add".into()],
estimated_benefit: 0.01,
validated: true,
},
TransferRule {
name: "cmov_to_select".into(),
source: TargetArch::X86,
destination: TargetArch::RISCV64,
source_pattern: vec!["cmov".into(), "cond".into(), "t".into(), "f".into()],
dest_pattern: vec!["select".into(), "cond".into(), "t".into(), "f".into()],
estimated_benefit: 0.005,
validated: true,
},
TransferRule {
name: "setcc_to_slt_seq".into(),
source: TargetArch::X86,
destination: TargetArch::RISCV64,
source_pattern: vec!["setcc".into(), "cond".into()],
dest_pattern: vec!["slt".into(), "sltu".into()],
estimated_benefit: 0.0,
validated: true,
},
];
self.riscv_to_x86_rules = vec![
TransferRule {
name: "compress_load_store_pairs".into(),
source: TargetArch::RISCV64,
destination: TargetArch::X86,
source_pattern: vec!["lw".into(), "lw".into()],
dest_pattern: vec!["mov".into(), "mov".into()],
estimated_benefit: 0.01,
validated: false,
},
TransferRule {
name: "fuse_branch_compare".into(),
source: TargetArch::RISCV64,
destination: TargetArch::X86,
source_pattern: vec!["slt".into(), "bne".into()],
dest_pattern: vec!["cmp".into(), "jne".into()],
estimated_benefit: 0.02,
validated: true,
},
TransferRule {
name: "use_zero_reg_for_compare".into(),
source: TargetArch::RISCV64,
destination: TargetArch::X86,
source_pattern: vec!["slti".into(), "x0".into()],
dest_pattern: vec!["test".into()],
estimated_benefit: 0.005,
validated: true,
},
];
}
pub fn record_attempt(&mut self) {
self.transfer_stats.total_attempts += 1;
}
pub fn record_success(&mut self, benefit: f64) {
self.transfer_stats.successful += 1;
if benefit > 0.0 {
self.transfer_stats.beneficial += 1;
} else {
self.transfer_stats.harmful += 1;
}
let n = self.transfer_stats.successful as f64;
self.transfer_stats.avg_benefit =
(self.transfer_stats.avg_benefit * (n - 1.0) + benefit) / n;
}
pub fn summary(&self) -> String {
format!(
"Cross-Target Transfer Stats:\n Attempts: {}\n Successful: {}\n Beneficial: {}\n Harmful: {}\n Avg Benefit: {:.4}",
self.transfer_stats.total_attempts,
self.transfer_stats.successful,
self.transfer_stats.beneficial,
self.transfer_stats.harmful,
self.transfer_stats.avg_benefit,
)
}
}
impl Default for CrossTargetOptimizationTransfer {
fn default() -> Self {
let mut transfer = Self::new();
transfer.load_rules();
transfer
}
}
pub struct EncodingComparison {
pub x86_encoding: EncodingProfile,
pub riscv_encoding: EncodingProfile,
pub overhead: EncodingOverhead,
}
#[derive(Debug, Clone)]
pub struct EncodingProfile {
pub min_instr_len: u32,
pub max_instr_len: u32,
pub is_fixed_length: bool,
pub num_formats: u32,
pub has_implicit_operands: bool,
pub prefix_bytes: u32,
pub opcode_bytes: u32,
pub has_modrm_sib: bool,
pub has_embedded_immediate: bool,
pub displacement_bits: u32,
}
#[derive(Debug, Clone)]
pub struct EncodingOverhead {
pub x86_avg_bytes: f64,
pub riscv_avg_bytes: f64,
pub riscv_compressed_avg_bytes: f64,
pub ratio_std: f64,
pub ratio_compressed: f64,
pub summary: String,
}
impl EncodingComparison {
pub fn new() -> Self {
let x86 = EncodingProfile {
min_instr_len: 1,
max_instr_len: 15,
is_fixed_length: false,
num_formats: 20,
has_implicit_operands: true,
prefix_bytes: 4,
opcode_bytes: 3,
has_modrm_sib: true,
has_embedded_immediate: true,
displacement_bits: 32,
};
let riscv = EncodingProfile {
min_instr_len: 2, max_instr_len: 4, is_fixed_length: true,
num_formats: 6, has_implicit_operands: false,
prefix_bytes: 0,
opcode_bytes: 0, has_modrm_sib: false,
has_embedded_immediate: true,
displacement_bits: 20,
};
let overhead = EncodingOverhead {
x86_avg_bytes: 3.5,
riscv_avg_bytes: 4.0,
riscv_compressed_avg_bytes: 3.0,
ratio_std: 4.0 / 3.5,
ratio_compressed: 3.0 / 3.5,
summary: String::new(),
};
let mut enc = Self {
x86_encoding: x86,
riscv_encoding: riscv,
overhead,
};
enc.overhead.summary = enc.build_summary();
enc
}
fn build_summary(&self) -> String {
format!(
"Encoding Comparison:\n\
X86: variable length ({} to {} bytes), {} formats, ModR/M+SIB\n\
RISC-V: fixed length ({} bytes standard, {} bytes compressed), {} formats\n\
X86 avg bytes/instr: {:.1}\n\
RISC-V avg bytes/instr: {:.1} (std), {:.1} (compressed)\n\
Code density ratio (std/X86): {:.2}\n\
Code density ratio (comp/X86): {:.2}\n\
Assessment: RISC-V with C extension achieves comparable or\n\
better code density than X86 for most workloads, while\n\
maintaining the simplicity of fixed-length decode.",
self.x86_encoding.min_instr_len,
self.x86_encoding.max_instr_len,
self.x86_encoding.num_formats,
self.riscv_encoding.max_instr_len,
self.riscv_encoding.min_instr_len,
self.riscv_encoding.num_formats,
self.overhead.x86_avg_bytes,
self.overhead.riscv_avg_bytes,
self.overhead.riscv_compressed_avg_bytes,
self.overhead.ratio_std,
self.overhead.ratio_compressed,
)
}
pub fn report(&self) -> &str {
&self.overhead.summary
}
}
impl Default for EncodingComparison {
fn default() -> Self {
Self::new()
}
}
pub struct RegisterMapping {
pub rv_to_x86: HashMap<u32, &'static str>,
pub x86_to_rv: HashMap<&'static str, u32>,
pub rv_fpr_to_xmm: HashMap<u32, &'static str>,
pub xmm_to_rv_fpr: HashMap<&'static str, u32>,
}
impl RegisterMapping {
pub fn new() -> Self {
let mut rv_to_x86 = HashMap::new();
rv_to_x86.insert(0, "N/A (zero)");
rv_to_x86.insert(1, "N/A (return addr)");
rv_to_x86.insert(2, "RSP");
rv_to_x86.insert(5, "R8");
rv_to_x86.insert(6, "R9");
rv_to_x86.insert(7, "R10");
rv_to_x86.insert(8, "RBP");
rv_to_x86.insert(9, "RBX");
rv_to_x86.insert(10, "RDI");
rv_to_x86.insert(11, "RSI");
rv_to_x86.insert(12, "RDX");
rv_to_x86.insert(13, "RCX");
rv_to_x86.insert(14, "R8");
rv_to_x86.insert(15, "R9");
rv_to_x86.insert(28, "R12");
rv_to_x86.insert(29, "R13");
rv_to_x86.insert(30, "R14");
rv_to_x86.insert(31, "R15");
let mut x86_to_rv = HashMap::new();
x86_to_rv.insert("RSP", 2u32);
x86_to_rv.insert("RBP", 8u32);
x86_to_rv.insert("RBX", 9u32);
x86_to_rv.insert("RDI", 10u32);
x86_to_rv.insert("RSI", 11u32);
x86_to_rv.insert("RDX", 12u32);
x86_to_rv.insert("RCX", 13u32);
x86_to_rv.insert("R8", 14u32);
x86_to_rv.insert("R9", 15u32);
x86_to_rv.insert("R12", 28u32);
x86_to_rv.insert("R13", 29u32);
x86_to_rv.insert("R14", 30u32);
x86_to_rv.insert("R15", 31u32);
x86_to_rv.insert("RAX", 10u32);
let mut rv_fpr_to_xmm = HashMap::new();
for i in 0..16u32 {
rv_fpr_to_xmm.insert(i + 32, "XMM");
}
let mut xmm_to_rv_fpr = HashMap::new();
for i in 0..16u32 {
xmm_to_rv_fpr.insert("XMM", i + 32);
}
Self {
rv_to_x86,
x86_to_rv,
rv_fpr_to_xmm,
xmm_to_rv_fpr,
}
}
pub fn rv_gpr_to_x86_name(&self, rv_reg: u32) -> Option<&&str> {
self.rv_to_x86.get(&rv_reg)
}
pub fn x86_name_to_rv_gpr(&self, x86_name: &str) -> Option<u32> {
self.x86_to_rv.get(x86_name).copied()
}
pub fn mapped_rv_gprs(&self) -> Vec<u32> {
let mut regs: Vec<u32> = self.rv_to_x86.keys().copied().collect();
regs.sort();
regs
}
pub fn mapped_x86_names(&self) -> Vec<&&str> {
let mut names: Vec<&&str> = self.x86_to_rv.keys().collect();
names.sort();
names
}
}
impl Default for RegisterMapping {
fn default() -> Self {
Self::new()
}
}
pub struct PipelineAnalysis {
pub x86_pipeline: PipelineModel,
pub riscv_pipeline: PipelineModel,
pub comparison: PipelineComparison,
}
#[derive(Debug, Clone)]
pub struct PipelineModel {
pub depth: u32,
pub issue_width: u32,
pub num_exec_units: u32,
pub is_out_of_order: bool,
pub rob_size: u32,
pub ls_queue_entries: u32,
pub branch_predictor: String,
pub l1i_size_kb: u32,
pub l1d_size_kb: u32,
pub l2_size_kb: u32,
}
#[derive(Debug, Clone)]
pub struct PipelineComparison {
pub depth_ratio: f64,
pub issue_width_ratio: f64,
pub rob_size_ratio: f64,
pub summary: String,
}
impl PipelineAnalysis {
pub fn new() -> Self {
let x86 = PipelineModel {
depth: 14,
issue_width: 6,
num_exec_units: 10,
is_out_of_order: true,
rob_size: 352,
ls_queue_entries: 192,
branch_predictor: "TAGE + Perceptron".into(),
l1i_size_kb: 32,
l1d_size_kb: 48,
l2_size_kb: 2048,
};
let riscv = PipelineModel {
depth: 8,
issue_width: 4,
num_exec_units: 6,
is_out_of_order: true,
rob_size: 128,
ls_queue_entries: 64,
branch_predictor: "GShare + BTB".into(),
l1i_size_kb: 32,
l1d_size_kb: 32,
l2_size_kb: 1024,
};
let comparison = PipelineComparison {
depth_ratio: 8.0 / 14.0,
issue_width_ratio: 4.0 / 6.0,
rob_size_ratio: 128.0 / 352.0,
summary: String::new(),
};
let mut analysis = Self {
x86_pipeline: x86,
riscv_pipeline: riscv,
comparison,
};
analysis.comparison.summary = analysis.build_summary();
analysis
}
fn build_summary(&self) -> String {
format!(
"Pipeline Analysis:\n\
X86: {} stages, {} wide, {} exec units, OoO (ROB: {})\n\
RISC-V: {} stages, {} wide, {} exec units, OoO (ROB: {})\n\
Depth ratio: {:.2}x\n\
Width ratio: {:.2}x\n\
ROB ratio: {:.2}x\n\
Assessment: RISC-V's shallower pipeline reduces branch\n\
mispredict penalty, while X86's wider issue and larger ROB\n\
extract more ILP from complex code sequences.",
self.x86_pipeline.depth,
self.x86_pipeline.issue_width,
self.x86_pipeline.num_exec_units,
self.x86_pipeline.rob_size,
self.riscv_pipeline.depth,
self.riscv_pipeline.issue_width,
self.riscv_pipeline.num_exec_units,
self.riscv_pipeline.rob_size,
self.comparison.depth_ratio,
self.comparison.issue_width_ratio,
self.comparison.rob_size_ratio,
)
}
pub fn report(&self) -> &str {
&self.comparison.summary
}
}
impl Default for PipelineAnalysis {
fn default() -> Self {
Self::new()
}
}
pub struct CrossTargetCodeGenUtils {
pub sequences: HashMap<String, CrossTargetSequence>,
pub peephole_patterns: Vec<CrossTargetPeephole>,
}
#[derive(Debug, Clone)]
pub struct CrossTargetSequence {
pub name: String,
pub description: String,
pub riscv_ops: Vec<String>,
pub x86_ops: Vec<String>,
pub is_commutative: bool,
pub riscv_cost: u32,
pub x86_cost: u32,
}
#[derive(Debug, Clone)]
pub struct CrossTargetPeephole {
pub name: String,
pub match_pattern: Vec<String>,
pub replace_pattern: Vec<String>,
pub always_beneficial: bool,
}
impl CrossTargetCodeGenUtils {
pub fn new() -> Self {
let mut utils = Self {
sequences: HashMap::new(),
peephole_patterns: Vec::new(),
};
utils.load_sequences();
utils.load_peepholes();
utils
}
fn load_sequences(&mut self) {
self.sequences.insert(
"abs_i32".into(),
CrossTargetSequence {
name: "abs_i32".into(),
description: "Compute absolute value of i32".into(),
riscv_ops: vec!["srai".into(), "xor".into(), "sub".into()],
x86_ops: vec!["mov".into(), "cdq".into(), "xor".into(), "sub".into()],
is_commutative: false,
riscv_cost: 3,
x86_cost: 3,
},
);
self.sequences.insert(
"swap_regs".into(),
CrossTargetSequence {
name: "swap_regs".into(),
description: "Swap two register values".into(),
riscv_ops: vec!["xor".into(), "xor".into(), "xor".into()],
x86_ops: vec!["xchg".into()],
is_commutative: true,
riscv_cost: 3,
x86_cost: 1,
},
);
self.sequences.insert(
"clear_reg".into(),
CrossTargetSequence {
name: "clear_reg".into(),
description: "Set a register to zero".into(),
riscv_ops: vec!["addi".into()], x86_ops: vec!["xor".into()], is_commutative: false,
riscv_cost: 1,
x86_cost: 1,
},
);
self.sequences.insert(
"sext_i32_to_i64".into(),
CrossTargetSequence {
name: "sext_i32_to_i64".into(),
description: "Sign extend i32 to i64".into(),
riscv_ops: vec!["addiw".into()], x86_ops: vec!["movsxd".into()], is_commutative: false,
riscv_cost: 1,
x86_cost: 1,
},
);
self.sequences.insert(
"mul_by_const_10".into(),
CrossTargetSequence {
name: "mul_by_const_10".into(),
description: "Multiply by 10 using shifts and adds".into(),
riscv_ops: vec!["slli".into(), "add".into()],
x86_ops: vec!["lea".into()],
is_commutative: false,
riscv_cost: 2,
x86_cost: 1,
},
);
}
fn load_peepholes(&mut self) {
self.peephole_patterns = vec![
CrossTargetPeephole {
name: "remove_redundant_mov".into(),
match_pattern: vec!["mov".into(), "mov".into()],
replace_pattern: vec!["mov".into()],
always_beneficial: true,
},
CrossTargetPeephole {
name: "fold_const_add".into(),
match_pattern: vec!["addi".into(), "addi".into()],
replace_pattern: vec!["addi".into()],
always_beneficial: true,
},
CrossTargetPeephole {
name: "remove_and_self".into(),
match_pattern: vec!["and".into()],
replace_pattern: vec![], always_beneficial: true,
},
CrossTargetPeephole {
name: "remove_or_zero".into(),
match_pattern: vec!["or".into(), "const_0".into()],
replace_pattern: vec![], always_beneficial: true,
},
CrossTargetPeephole {
name: "fold_shift_pair".into(),
match_pattern: vec!["slli".into(), "srli".into()],
replace_pattern: vec!["andi".into()], always_beneficial: false,
},
];
}
pub fn list_sequences(&self) -> Vec<&String> {
self.sequences.keys().collect()
}
pub fn get_sequence(&self, name: &str) -> Option<&CrossTargetSequence> {
self.sequences.get(name)
}
pub fn list_peepholes(&self) -> Vec<&String> {
self.peephole_patterns.iter().map(|p| &p.name).collect()
}
}
impl Default for CrossTargetCodeGenUtils {
fn default() -> Self {
Self::new()
}
}
impl SharedISelPatterns {
pub fn load_extended_integer_patterns(&mut self) {
self.add_pattern(SharedPattern {
name: "cond_increment".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![RiscVOpcode::ADDI, RiscVOpcode::SLT, RiscVOpcode::ADD],
x86_seq: vec![X86Opcode::CMP, X86Opcode::ADC],
riscv_cost: 3,
x86_cost: 2,
is_commutative: false,
min_operands: 1,
max_operands: 2,
riscv_features: vec![],
x86_features: vec![],
});
self.add_pattern(SharedPattern {
name: "saturating_add_unsigned".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![RiscVOpcode::ADD, RiscVOpcode::SLTU, RiscVOpcode::OR],
x86_seq: vec![X86Opcode::ADD, X86Opcode::CMOVC],
riscv_cost: 3,
x86_cost: 2,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec!["cmov".into()],
});
self.add_pattern(SharedPattern {
name: "average_unsigned".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![
RiscVOpcode::ADD,
RiscVOpcode::SLTU,
RiscVOpcode::ADD,
RiscVOpcode::SRLI,
],
x86_seq: vec![X86Opcode::PAVGB],
riscv_cost: 4,
x86_cost: 1,
is_commutative: true,
min_operands: 2,
max_operands: 2,
riscv_features: vec![],
x86_features: vec!["sse".into()],
});
self.add_pattern(SharedPattern {
name: "bitfield_insert".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![
RiscVOpcode::SLLI,
RiscVOpcode::ANDI,
RiscVOpcode::ANDI,
RiscVOpcode::OR,
],
x86_seq: vec![X86Opcode::BEXTR],
riscv_cost: 4,
x86_cost: 1,
is_commutative: false,
min_operands: 3,
max_operands: 3,
riscv_features: vec![],
x86_features: vec!["bmi1".into()],
});
self.add_pattern(SharedPattern {
name: "is_power_of_two".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![RiscVOpcode::ADDI, RiscVOpcode::AND, RiscVOpcode::SLTIU],
x86_seq: vec![X86Opcode::BLSR, X86Opcode::TEST, X86Opcode::SETE],
riscv_cost: 3,
x86_cost: 3,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec![],
x86_features: vec!["bmi1".into()],
});
self.add_pattern(SharedPattern {
name: "round_up_power_of_two".into(),
category: PatternCategory::IntegerArithmetic,
riscv_seq: vec![
RiscVOpcode::ADDI,
RiscVOpcode::CLZ,
RiscVOpcode::ADDI,
RiscVOpcode::SLL,
],
x86_seq: vec![X86Opcode::LZCNT, X86Opcode::MOV, X86Opcode::SHL],
riscv_cost: 4,
x86_cost: 3,
is_commutative: false,
min_operands: 1,
max_operands: 1,
riscv_features: vec!["zbb".into()],
x86_features: vec!["lzcnt".into()],
});
}
}
impl RISCVX86Bridge {
pub fn analyze_register_pressure(
&self,
live_ranges: &[(u32, u32)], ) -> RegisterPressureAnalysis {
let mut max_pressure = 0u32;
let mut current = 0u32;
let mut events: Vec<(u32, bool)> = Vec::new();
for &(start, end) in live_ranges {
events.push((start, true)); events.push((end, false)); }
events.sort_by_key(|e| e.0);
for (_pos, is_start) in &events {
if *is_start {
current += 1;
max_pressure = max_pressure.max(current);
} else {
current = current.saturating_sub(1);
}
}
let x86_gpr_count = 16;
let riscv_gpr_count = 31;
RegisterPressureAnalysis {
max_live_ranges: max_pressure,
x86_available: x86_gpr_count,
riscv_available: riscv_gpr_count,
x86_pressure_ratio: max_pressure as f64 / x86_gpr_count as f64,
riscv_pressure_ratio: max_pressure as f64 / riscv_gpr_count as f64,
needs_spilling_on_x86: max_pressure > x86_gpr_count,
needs_spilling_on_riscv: max_pressure > riscv_gpr_count,
}
}
pub fn estimate_code_sizes(
&self,
instr_count: usize,
mem_ops_ratio: f64,
uses_compressed: bool,
) -> CodeSizeEstimate {
let riscv_base_size = 4.0; let riscv_comp_size = if uses_compressed { 2.5 } else { 0.0 };
let riscv_avg = if uses_compressed {
riscv_comp_size
} else {
riscv_base_size
};
let riscv_extra_instrs = (mem_ops_ratio * instr_count as f64 * 0.3) as usize;
let riscv_total_bytes = ((instr_count + riscv_extra_instrs) as f64 * riscv_avg) as u64;
let x86_avg = 3.5;
let x86_total_bytes = (instr_count as f64 * x86_avg) as u64;
CodeSizeEstimate {
riscv_estimated_bytes: riscv_total_bytes,
x86_estimated_bytes: x86_total_bytes,
riscv_extra_instructions: riscv_extra_instrs as u64,
ratio: riscv_total_bytes as f64 / x86_total_bytes.max(1) as f64,
}
}
pub fn generate_analysis_report(&self) -> String {
let mut report = String::new();
report.push_str("=== RISC-V ↔ X86 Bridge Analysis Report ===\n\n");
report.push_str("Configuration:\n");
report.push_str(&format!(
" Shared ISel: {}\n",
self.config.enable_shared_isel
));
report.push_str(&format!(
" Cross-Target Opt: {}\n",
self.config.enable_cross_target_opt
));
report.push_str(&format!(
" Shared RA: {}\n",
self.config.enable_shared_ra
));
report.push_str(&format!(
" Shared Frame: {}\n",
self.config.enable_shared_frame
));
report.push_str("\nShared ISel Patterns:\n");
report.push_str(&format!(
" Total patterns: {}\n",
self.shared_isel_patterns.pattern_count()
));
for cat in &[
PatternCategory::IntegerArithmetic,
PatternCategory::FloatArithmetic,
PatternCategory::MemoryAccess,
PatternCategory::ControlFlow,
] {
let count = self
.shared_isel_patterns
.list_patterns_by_category(*cat)
.len();
report.push_str(&format!(" {:30}: {}\n", cat.to_string(), count));
}
report.push_str("\nLowering Rules:\n");
report.push_str(&format!(
" Total rules: {}\n",
self.cross_target_rules.rule_count()
));
report.push_str("\nStatistics:\n");
report.push_str(&format!("{}", self.stats));
report
}
}
#[derive(Debug, Clone)]
pub struct RegisterPressureAnalysis {
pub max_live_ranges: u32,
pub x86_available: u32,
pub riscv_available: u32,
pub x86_pressure_ratio: f64,
pub riscv_pressure_ratio: f64,
pub needs_spilling_on_x86: bool,
pub needs_spilling_on_riscv: bool,
}
#[derive(Debug, Clone)]
pub struct CodeSizeEstimate {
pub riscv_estimated_bytes: u64,
pub x86_estimated_bytes: u64,
pub riscv_extra_instructions: u64,
pub ratio: f64,
}
impl CrossTargetLowering {
pub fn load_extended_rules(&mut self) {
self.add_rule(LoweringRule {
name: "combine_cmp_branch_into_one".into(),
targets: vec![TargetArch::X86, TargetArch::X86_64],
match_pattern: vec!["cmp".into(), "branch".into()],
replacement: vec![ReplacementStep {
operation: "cmp_branch_fused".into(),
inputs: vec![0, 1],
output: "".into(),
metadata: HashMap::new(),
}],
priority: 95,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "expand_large_immediate_riscv".into(),
targets: vec![TargetArch::RISCV32, TargetArch::RISCV64],
match_pattern: vec!["mov".into(), "large_imm".into()],
replacement: vec![
ReplacementStep {
operation: "lui".into(),
inputs: vec![1],
output: "hi".into(),
metadata: HashMap::new(),
},
ReplacementStep {
operation: "addi".into(),
inputs: vec![1],
output: "lo".into(),
metadata: HashMap::new(),
},
],
priority: 75,
is_optimization: false,
});
self.add_rule(LoweringRule {
name: "fuse_load_with_use".into(),
targets: vec![TargetArch::X86, TargetArch::X86_64],
match_pattern: vec!["load".into(), "use".into()],
replacement: vec![ReplacementStep {
operation: "use_with_mem_operand".into(),
inputs: vec![0, 1],
output: "".into(),
metadata: HashMap::new(),
}],
priority: 92,
is_optimization: true,
});
self.add_rule(LoweringRule {
name: "convert_switch_to_branch_tree".into(),
targets: vec![TargetArch::RISCV32, TargetArch::RISCV64],
match_pattern: vec!["switch".into()],
replacement: vec![ReplacementStep {
operation: "branch_tree".into(),
inputs: vec![0],
output: "".into(),
metadata: HashMap::new(),
}],
priority: 65,
is_optimization: false,
});
self.add_rule(LoweringRule {
name: "fold_load_store_same_addr".into(),
targets: vec![TargetArch::All],
match_pattern: vec!["load".into(), "store".into()],
replacement: vec![ReplacementStep {
operation: "forward_store".into(),
inputs: vec![0, 1],
output: "".into(),
metadata: HashMap::new(),
}],
priority: 88,
is_optimization: true,
});
}
}
#[cfg(test)]
mod extended_tests {
use super::*;
#[test]
fn test_transfer_creation() {
let transfer = CrossTargetOptimizationTransfer::new();
assert!(transfer.x86_to_riscv_rules.is_empty());
assert!(transfer.riscv_to_x86_rules.is_empty());
}
#[test]
fn test_transfer_default_loads_rules() {
let transfer = CrossTargetOptimizationTransfer::default();
assert!(!transfer.x86_to_riscv_rules.is_empty());
assert!(!transfer.riscv_to_x86_rules.is_empty());
}
#[test]
fn test_transfer_record_stats() {
let mut transfer = CrossTargetOptimizationTransfer::new();
transfer.record_attempt();
transfer.record_success(0.05);
transfer.record_success(-0.01);
assert_eq!(transfer.transfer_stats.total_attempts, 1);
assert_eq!(transfer.transfer_stats.successful, 2);
assert_eq!(transfer.transfer_stats.beneficial, 1);
assert_eq!(transfer.transfer_stats.harmful, 1);
}
#[test]
fn test_transfer_summary() {
let mut transfer = CrossTargetOptimizationTransfer::default();
transfer.record_attempt();
transfer.record_success(0.03);
let summary = transfer.summary();
assert!(summary.contains("Attempts"));
assert!(summary.contains("Successful"));
}
#[test]
fn test_encoding_comparison_default() {
let enc = EncodingComparison::default();
assert_eq!(enc.x86_encoding.min_instr_len, 1);
assert_eq!(enc.x86_encoding.max_instr_len, 15);
assert_eq!(enc.riscv_encoding.max_instr_len, 4);
assert_eq!(enc.riscv_encoding.min_instr_len, 2);
assert!(enc.riscv_encoding.is_fixed_length);
assert!(!enc.x86_encoding.is_fixed_length);
}
#[test]
fn test_encoding_comparison_report() {
let enc = EncodingComparison::default();
let report = enc.report();
assert!(report.contains("X86"));
assert!(report.contains("RISC-V"));
assert!(report.contains("variable"));
assert!(report.contains("fixed"));
}
#[test]
fn test_register_mapping_default() {
let map = RegisterMapping::default();
assert!(!map.rv_to_x86.is_empty());
assert!(!map.x86_to_rv.is_empty());
}
#[test]
fn test_register_mapping_lookup() {
let map = RegisterMapping::default();
assert_eq!(map.rv_gpr_to_x86_name(10), Some(&&"RDI"));
assert_eq!(map.x86_name_to_rv_gpr("RDI"), Some(10));
}
#[test]
fn test_register_mapping_sp() {
let map = RegisterMapping::default();
assert_eq!(map.rv_gpr_to_x86_name(2), Some(&&"RSP"));
assert_eq!(map.x86_name_to_rv_gpr("RSP"), Some(2));
}
#[test]
fn test_mapped_regs_sorted() {
let map = RegisterMapping::default();
let regs = map.mapped_rv_gprs();
for w in regs.windows(2) {
assert!(w[0] < w[1], "Mapped regs should be sorted");
}
}
#[test]
fn test_pipeline_analysis_default() {
let analysis = PipelineAnalysis::default();
assert!(analysis.x86_pipeline.depth > 0);
assert!(analysis.riscv_pipeline.depth > 0);
assert!(analysis.x86_pipeline.is_out_of_order);
}
#[test]
fn test_pipeline_analysis_report() {
let analysis = PipelineAnalysis::default();
let report = analysis.report();
assert!(report.contains("Pipeline"));
assert!(report.contains("X86"));
assert!(report.contains("RISC-V"));
}
#[test]
fn test_codegen_utils_default() {
let utils = CrossTargetCodeGenUtils::default();
assert!(!utils.sequences.is_empty());
assert!(!utils.peephole_patterns.is_empty());
}
#[test]
fn test_codegen_utils_get_sequence() {
let utils = CrossTargetCodeGenUtils::default();
let seq = utils.get_sequence("clear_reg");
assert!(seq.is_some());
assert_eq!(seq.unwrap().riscv_cost, 1);
assert_eq!(seq.unwrap().x86_cost, 1);
}
#[test]
fn test_codegen_utils_list_sequences() {
let utils = CrossTargetCodeGenUtils::default();
let list = utils.list_sequences();
assert!(list.contains(&&"abs_i32".to_string()));
assert!(list.contains(&&"swap_regs".to_string()));
assert!(list.contains(&&"clear_reg".to_string()));
}
#[test]
fn test_codegen_utils_list_peepholes() {
let utils = CrossTargetCodeGenUtils::default();
let peeps = utils.list_peepholes();
assert!(peeps.len() >= 3);
}
#[test]
fn test_extended_integer_patterns_loaded() {
let mut patterns = SharedISelPatterns::new();
patterns.load_patterns();
let count_before = patterns.pattern_count();
patterns.load_extended_integer_patterns();
let count_after = patterns.pattern_count();
assert!(count_after > count_before);
}
#[test]
fn test_extended_pattern_cond_increment() {
let mut patterns = SharedISelPatterns::new();
patterns.load_extended_integer_patterns();
let result = patterns.find("cond_increment", true);
assert!(result.is_some());
}
#[test]
fn test_register_pressure_analysis_empty() {
let bridge = RISCVX86Bridge::new();
let analysis = bridge.analyze_register_pressure(&[]);
assert_eq!(analysis.max_live_ranges, 0);
assert!(!analysis.needs_spilling_on_x86);
assert!(!analysis.needs_spilling_on_riscv);
}
#[test]
fn test_register_pressure_analysis_overlapping() {
let bridge = RISCVX86Bridge::new();
let ranges = &[(0, 10), (5, 15), (8, 12), (1, 9), (3, 7)];
let analysis = bridge.analyze_register_pressure(ranges);
assert!(analysis.max_live_ranges >= 3);
assert!(analysis.riscv_pressure_ratio > 0.0);
}
#[test]
fn test_register_pressure_high_on_x86() {
let bridge = RISCVX86Bridge::new();
let mut ranges = Vec::new();
for i in 0..20 {
ranges.push((i as u32, (i + 10) as u32));
}
let analysis = bridge.analyze_register_pressure(&ranges);
assert!(analysis.max_live_ranges >= 15);
assert!(analysis.needs_spilling_on_x86);
assert!(!analysis.needs_spilling_on_riscv);
}
#[test]
fn test_code_size_estimate() {
let bridge = RISCVX86Bridge::new();
let estimate = bridge.estimate_code_sizes(100, 0.3, true);
assert!(estimate.riscv_estimated_bytes > 0);
assert!(estimate.x86_estimated_bytes > 0);
assert!(estimate.ratio > 0.0);
}
#[test]
fn test_code_size_estimate_no_compressed() {
let bridge = RISCVX86Bridge::new();
let estimate = bridge.estimate_code_sizes(100, 0.3, false);
assert!(estimate.ratio >= 0.8);
}
#[test]
fn test_analysis_report() {
let bridge = RISCVX86Bridge::new();
let report = bridge.generate_analysis_report();
assert!(report.contains("Bridge Analysis Report"));
assert!(report.contains("Configuration"));
assert!(report.contains("Shared ISel Patterns"));
assert!(report.contains("Lowering Rules"));
assert!(report.contains("Statistics"));
}
#[test]
fn test_extended_rules_loaded() {
let mut rules = CrossTargetLowering::new();
rules.load_rules();
let count_before = rules.rule_count();
rules.load_extended_rules();
let count_after = rules.rule_count();
assert!(count_after > count_before);
}
#[test]
fn test_extended_rule_cmp_branch() {
let mut rules = CrossTargetLowering::new();
rules.load_extended_rules();
let rule = rules.get_rule("combine_cmp_branch_into_one");
assert!(rule.is_some());
assert!(rule.unwrap().is_optimization);
}
#[test]
fn test_extended_rule_expand_large_imm() {
let mut rules = CrossTargetLowering::new();
rules.load_extended_rules();
let rule = rules.get_rule("expand_large_immediate_riscv");
assert!(rule.is_some());
assert!(!rule.unwrap().is_optimization);
assert_eq!(rule.unwrap().replacement.len(), 2);
}
#[test]
fn test_transfer_rule_fields() {
let rule = TransferRule {
name: "test".into(),
source: TargetArch::X86,
destination: TargetArch::RISCV64,
source_pattern: vec!["op".into()],
dest_pattern: vec!["rop".into()],
estimated_benefit: 0.05,
validated: true,
};
assert_eq!(rule.name, "test");
assert_eq!(rule.source, TargetArch::X86);
assert_eq!(rule.destination, TargetArch::RISCV64);
assert!((rule.estimated_benefit - 0.05).abs() < 0.001);
assert!(rule.validated);
}
#[test]
fn test_pipeline_model_typical_values() {
let model = PipelineModel {
depth: 8,
issue_width: 4,
num_exec_units: 6,
is_out_of_order: true,
rob_size: 128,
ls_queue_entries: 64,
branch_predictor: "GShare".into(),
l1i_size_kb: 32,
l1d_size_kb: 32,
l2_size_kb: 1024,
};
assert_eq!(model.depth, 8);
assert!(model.is_out_of_order);
assert_eq!(model.l1d_size_kb, 32);
}
#[test]
fn test_cross_target_sequence_fields() {
let seq = CrossTargetSequence {
name: "test".into(),
description: "desc".into(),
riscv_ops: vec!["addi".into()],
x86_ops: vec!["mov".into()],
is_commutative: false,
riscv_cost: 1,
x86_cost: 1,
};
assert_eq!(seq.name, "test");
assert_eq!(seq.riscv_ops.len(), 1);
assert_eq!(seq.x86_ops.len(), 1);
assert_eq!(seq.riscv_cost, 1);
}
#[test]
fn test_code_size_estimate_fields() {
let est = CodeSizeEstimate {
riscv_estimated_bytes: 400,
x86_estimated_bytes: 350,
riscv_extra_instructions: 15,
ratio: 400.0 / 350.0,
};
assert!(est.riscv_estimated_bytes > 0);
assert!(est.x86_estimated_bytes > 0);
assert!(est.riscv_extra_instructions > 0);
assert!((est.ratio - 400.0 / 350.0).abs() < 0.001);
}
#[test]
fn test_register_pressure_analysis_fields() {
let analysis = RegisterPressureAnalysis {
max_live_ranges: 12,
x86_available: 16,
riscv_available: 31,
x86_pressure_ratio: 12.0 / 16.0,
riscv_pressure_ratio: 12.0 / 31.0,
needs_spilling_on_x86: false,
needs_spilling_on_riscv: false,
};
assert_eq!(analysis.max_live_ranges, 12);
assert!(!analysis.needs_spilling_on_x86);
assert!((analysis.x86_pressure_ratio - 0.75).abs() < 0.001);
}
#[test]
fn test_full_bridge_analysis_integration() {
let bridge = RISCVX86Bridge::new();
let ranges = &[(0, 15), (5, 20), (10, 18), (3, 12), (8, 22), (14, 25)];
let pressure = bridge.analyze_register_pressure(ranges);
assert!(pressure.max_live_ranges >= 2);
let estimate = bridge.estimate_code_sizes(50, 0.4, true);
assert!(estimate.riscv_estimated_bytes > 0);
assert!(estimate.x86_estimated_bytes > 0);
let report = bridge.generate_analysis_report();
assert!(!report.is_empty());
assert!(report.contains("Bridge Analysis Report"));
}
#[test]
fn test_cross_target_all_components_integration() {
let bridge = RISCVX86Bridge::new();
let cross = CrossTargetRISCV::new();
let transfer = CrossTargetOptimizationTransfer::default();
let encoding = EncodingComparison::default();
let reg_map = RegisterMapping::default();
let pipeline = PipelineAnalysis::default();
let codegen = CrossTargetCodeGenUtils::default();
let comparisons = X86RISCVComparisons::default();
assert!(bridge.shared_isel_patterns.pattern_count() > 0);
assert!(cross.isel_patterns.pattern_count() > 0);
assert!(!transfer.x86_to_riscv_rules.is_empty());
assert!(!encoding.report().is_empty());
assert!(!reg_map.rv_to_x86.is_empty());
assert!(!pipeline.report().is_empty());
assert!(!codegen.sequences.is_empty());
assert!(!comparisons.generate_report().is_empty());
}
}
#[cfg(test)]
mod original_tests {
use super::*;
#[test]
fn test_bridge_creation() {
let bridge = RISCVX86Bridge::new();
assert!(bridge.shared_isel_patterns.pattern_count() > 0);
assert!(bridge.cross_target_rules.rule_count() > 0);
}
#[test]
fn test_bridge_with_config() {
let config = BridgeConfig {
enable_shared_isel: false,
verbose: true,
..Default::default()
};
let bridge = RISCVX86Bridge::with_config(config);
assert!(!bridge.config.enable_shared_isel);
assert!(bridge.config.verbose);
}
#[test]
fn test_bridge_default_config() {
let config = BridgeConfig::default();
assert!(config.enable_shared_isel);
assert!(config.enable_cross_target_opt);
assert!(config.enable_shared_ra);
assert!(config.enable_shared_frame);
assert_eq!(config.max_pattern_depth, 8);
assert!((config.opt_transfer_threshold - 1.5).abs() < 0.001);
assert!(config.collect_stats);
assert!(!config.verbose);
}
#[test]
fn test_translate_x86_add_to_riscv() {
let mut bridge = RISCVX86Bridge::new();
let result = bridge.translate_x86_to_riscv(&X86Opcode::ADD);
assert!(result.is_some());
if let Some(BridgeInstruction::DirectMap(op)) = result {
assert_eq!(op, RiscVOpcode::ADD);
} else {
panic!("Expected DirectMap");
}
}
#[test]
fn test_translate_x86_sub_to_riscv() {
let mut bridge = RISCVX86Bridge::new();
let result = bridge.translate_x86_to_riscv(&X86Opcode::SUB);
assert!(result.is_some());
}
#[test]
fn test_translate_x86_mul_to_riscv() {
let mut bridge = RISCVX86Bridge::new();
let result = bridge.translate_x86_to_riscv(&X86Opcode::MUL);
assert!(result.is_some());
if let Some(BridgeInstruction::Expanded(ops)) = result {
assert!(ops.contains(&RiscVOpcode::MUL));
} else {
panic!("Expected Expanded");
}
}
#[test]
fn test_translate_x86_imul_to_riscv() {
let mut bridge = RISCVX86Bridge::new();
let result = bridge.translate_x86_to_riscv(&X86Opcode::IMUL);
assert!(result.is_some());
if let Some(BridgeInstruction::Expanded(ops)) = result {
assert!(ops.contains(&RiscVOpcode::MUL));
assert!(ops.contains(&RiscVOpcode::MULH));
} else {
panic!("Expected Expanded with MUL and MULH");
}
}
#[test]
fn test_translate_riscv_add_to_x86() {
let mut bridge = RISCVX86Bridge::new();
let result = bridge.translate_riscv_to_x86(&RiscVOpcode::ADD);
assert!(result.is_some());
}
#[test]
fn test_translate_riscv_sub_to_x86() {
let mut bridge = RISCVX86Bridge::new();
let result = bridge.translate_riscv_to_x86(&RiscVOpcode::SUB);
assert!(result.is_some());
}
#[test]
fn test_translate_unknown_x86_opcode() {
let mut bridge = RISCVX86Bridge::new();
let result = bridge.translate_x86_to_riscv(&X86Opcode::NOP);
assert!(result.is_some());
}
#[test]
fn test_translate_jump_family() {
let mut bridge = RISCVX86Bridge::new();
assert!(bridge.translate_x86_to_riscv(&X86Opcode::JMP).is_some());
assert!(bridge.translate_x86_to_riscv(&X86Opcode::CALL).is_some());
assert!(bridge.translate_x86_to_riscv(&X86Opcode::RET).is_some());
}
#[test]
fn test_bridge_stats_tracking() {
let mut bridge = RISCVX86Bridge::new();
bridge.stats = BridgeStats::default();
bridge.translate_x86_to_riscv(&X86Opcode::ADD);
assert!(bridge.stats.pattern_translations > 0);
bridge.translate_riscv_to_x86(&RiscVOpcode::ADD);
assert!(bridge.stats.pattern_translations >= 2);
}
#[test]
fn test_bridge_stats_reset() {
let mut bridge = RISCVX86Bridge::new();
bridge.translate_x86_to_riscv(&X86Opcode::ADD);
assert!(bridge.stats.pattern_translations > 0);
bridge.reset_stats();
assert_eq!(bridge.stats.pattern_translations, 0);
assert_eq!(bridge.stats.shared_isel_matches, 0);
}
#[test]
fn test_bridge_stats_display() {
let stats = BridgeStats {
shared_isel_matches: 10,
cross_target_opts: 5,
shared_ra_decisions: 20,
shared_frame_ops: 15,
pattern_translations: 100,
total_time_us: 5000,
successful_transfers: 95,
failed_transfers: 5,
};
let display = format!("{}", stats);
assert!(display.contains("10"));
assert!(display.contains("5"));
assert!(display.contains("20"));
assert!(display.contains("15"));
assert!(display.contains("100"));
assert!(display.contains("5000"));
assert!(display.contains("95"));
assert!(display.contains("5"));
}
#[test]
fn test_bridge_debug_format() {
let bridge = RISCVX86Bridge::new();
let debug = format!("{:?}", bridge);
assert!(debug.contains("RISCVX86Bridge"));
}
#[test]
fn test_cross_target_creation() {
let cross = CrossTargetRISCV::new();
assert!(cross.isel_patterns.pattern_count() > 0);
}
#[test]
fn test_cross_target_with_features() {
let features = TargetFeatureSet::default();
let cross = CrossTargetRISCV::with_features(features.clone());
assert_eq!(cross.features.enabled.len(), features.enabled.len());
}
#[test]
fn test_cross_target_default() {
let cross = CrossTargetRISCV::default();
assert!(cross.isel_patterns.pattern_count() > 0);
}
#[test]
fn test_shared_isel_patterns_creation() {
let patterns = SharedISelPatterns::new();
assert_eq!(patterns.pattern_count(), 0);
}
#[test]
fn test_shared_isel_patterns_default_loads_all() {
let patterns = SharedISelPatterns::default();
assert!(patterns.pattern_count() > 30);
}
#[test]
fn test_shared_isel_find_pattern() {
let patterns = SharedISelPatterns::default();
let result = patterns.find("add_with_carry", true);
assert!(result.is_some());
let m = result.unwrap();
assert_eq!(m.riscv_sequence.len(), 3);
assert_eq!(m.x86_sequence.len(), 1);
}
#[test]
fn test_shared_isel_find_missing_pattern() {
let patterns = SharedISelPatterns::default();
let result = patterns.find("nonexistent_pattern", true);
assert!(result.is_none());
}
#[test]
fn test_shared_isel_list_patterns() {
let patterns = SharedISelPatterns::default();
let list = patterns.list_patterns();
assert!(!list.is_empty());
}
#[test]
fn test_shared_isel_list_by_category() {
let patterns = SharedISelPatterns::default();
let arithmetic = patterns.list_patterns_by_category(PatternCategory::IntegerArithmetic);
assert!(!arithmetic.is_empty());
let control = patterns.list_patterns_by_category(PatternCategory::ControlFlow);
assert!(!control.is_empty());
}
#[test]
fn test_pattern_category_display() {
assert_eq!(
PatternCategory::IntegerArithmetic.to_string(),
"IntegerArithmetic"
);
assert_eq!(
PatternCategory::FloatArithmetic.to_string(),
"FloatArithmetic"
);
assert_eq!(PatternCategory::MemoryAccess.to_string(), "MemoryAccess");
assert_eq!(
PatternCategory::VectorOperation.to_string(),
"VectorOperation"
);
}
#[test]
fn test_shared_isel_pattern_count() {
let patterns = SharedISelPatterns::default();
let count = patterns.pattern_count();
assert!(count > 30, "Expected > 30 patterns, got {}", count);
}
#[test]
fn test_shared_isel_select_pattern() {
let patterns = SharedISelPatterns::default();
let result = patterns.find("mul_full_64", true);
assert!(result.is_some());
let m = result.unwrap();
assert!(m.riscv_sequence.contains(&RiscVOpcode::MUL));
assert!(m.riscv_sequence.contains(&RiscVOpcode::MULHU));
}
#[test]
fn test_shared_isel_float_pattern() {
let patterns = SharedISelPatterns::default();
let result = patterns.find("fadd_single", true);
assert!(result.is_some());
let m = result.unwrap();
assert!(m.riscv_sequence.contains(&RiscVOpcode::FADD_S));
}
#[test]
fn test_shared_isel_vector_pattern() {
let patterns = SharedISelPatterns::default();
let result = patterns.find("vadd_int", true);
assert!(result.is_some());
}
#[test]
fn test_shared_isel_atomic_pattern() {
let patterns = SharedISelPatterns::default();
let result = patterns.find("atomic_swap", true);
assert!(result.is_some());
}
#[test]
fn test_lowering_rules_creation() {
let rules = CrossTargetLowering::new();
assert_eq!(rules.rule_count(), 0);
}
#[test]
fn test_lowering_rules_default() {
let rules = CrossTargetLowering::default();
assert!(rules.rule_count() > 5);
}
#[test]
fn test_lowering_rules_apply() {
let rules = CrossTargetLowering::default();
let seq = &["add", "add", "const"];
let results = rules.apply(seq);
assert!(!results.is_empty());
}
#[test]
fn test_lowering_rules_apply_no_match() {
let rules = CrossTargetLowering::default();
let seq = &["xyz_unknown_op"];
let results = rules.apply(seq);
assert!(results.iter().all(|r| !r.matched));
}
#[test]
fn test_lowering_rules_get_rule() {
let rules = CrossTargetLowering::default();
let rule = rules.get_rule("combine_add_add_to_address");
assert!(rule.is_some());
assert_eq!(rule.unwrap().priority, 100);
}
#[test]
fn test_lowering_rules_list() {
let rules = CrossTargetLowering::default();
let list = rules.list_rules();
assert!(list.contains(&&"combine_add_add_to_address".to_string()));
}
#[test]
fn test_lowering_rules_optimization_flag() {
let rules = CrossTargetLowering::default();
let opt_rule = rules.get_rule("combine_add_add_to_address");
assert!(opt_rule.unwrap().is_optimization);
let lower_rule = rules.get_rule("lower_select_to_cmov");
assert!(!lower_rule.unwrap().is_optimization);
}
#[test]
fn test_shared_ra_context_creation() {
let ctx = SharedRegisterAllocContext::new();
assert!(ctx.riscv_classes.is_empty());
assert!(ctx.x86_classes.is_empty());
assert_eq!(ctx.strategy, AllocationStrategy::Greedy);
}
#[test]
fn test_shared_ra_context_initialize() {
let mut ctx = SharedRegisterAllocContext::new();
ctx.initialize();
assert!(!ctx.riscv_classes.is_empty());
assert!(!ctx.x86_classes.is_empty());
}
#[test]
fn test_shared_ra_context_default() {
let ctx = SharedRegisterAllocContext::default();
assert!(!ctx.riscv_classes.is_empty());
assert!(!ctx.x86_classes.is_empty());
}
#[test]
fn test_shared_ra_register_allocation() {
let mut ctx = SharedRegisterAllocContext::default();
ctx.allocate_register(100, 5);
assert_eq!(ctx.get_physical_reg(100), Some(5));
assert!(!ctx.is_register_available(5));
assert!(!ctx.is_spilled(100));
}
#[test]
fn test_shared_ra_register_free() {
let mut ctx = SharedRegisterAllocContext::default();
ctx.allocate_register(100, 5);
ctx.free_register(5);
assert!(ctx.is_register_available(5));
assert_eq!(ctx.get_physical_reg(100), None);
}
#[test]
fn test_shared_ra_spill_slot() {
let mut ctx = SharedRegisterAllocContext::default();
let slot = ctx.add_spill_slot(200, 8, 8);
assert_eq!(slot, 0);
assert!(ctx.is_spilled(200));
let info = ctx.get_spill_slot(200);
assert!(info.is_some());
assert_eq!(info.unwrap().size, 8);
}
#[test]
fn test_shared_ra_spill_weight() {
let ctx = SharedRegisterAllocContext::default();
let interval = LiveInterval {
vreg: 1,
start: 0,
end: 10,
reg_class: 0,
spill_weight: 0.0,
};
let weight = ctx.compute_spill_weight(&interval);
assert!(weight > 0.0);
}
#[test]
fn test_shared_ra_spill_weight_zero_length() {
let ctx = SharedRegisterAllocContext::default();
let interval = LiveInterval {
vreg: 1,
start: 5,
end: 5,
reg_class: 0,
spill_weight: 0.0,
};
let weight = ctx.compute_spill_weight(&interval);
assert_eq!(weight, 0.0);
}
#[test]
fn test_allocation_strategy_display() {
assert_eq!(AllocationStrategy::LinearScan.as_str(), "linear-scan");
assert_eq!(AllocationStrategy::Greedy.as_str(), "greedy");
assert_eq!(AllocationStrategy::Pbqp.as_str(), "pbqp");
assert_eq!(AllocationStrategy::Fast.as_str(), "fast");
}
#[test]
fn test_cross_target_ra_default() {
let ra = CrossTargetRegisterAlloc::default();
assert!(!ra.context.riscv_classes.is_empty());
}
#[test]
fn test_cross_target_ra_constraints() {
let ra = CrossTargetRegisterAlloc::new();
assert!(ra.riscv_constraints.is_empty());
assert!(ra.x86_constraints.is_empty());
}
#[test]
fn test_shared_frame_context_new() {
let ctx = SharedFrameLoweringContext::new();
assert_eq!(ctx.frame_size, 0);
assert_eq!(ctx.stack_alignment, 16);
assert!(ctx.is_64bit);
assert!(!ctx.has_frame_pointer);
}
#[test]
fn test_shared_frame_context_initialize() {
let mut ctx = SharedFrameLoweringContext::new();
ctx.frame_size = 128;
ctx.initialize();
assert_eq!(ctx.frame_size, 0);
}
#[test]
fn test_frame_callee_saved_slots() {
let mut ctx = SharedFrameLoweringContext::new();
ctx.add_callee_saved_slot(1, -8);
ctx.add_callee_saved_slot(8, -16);
assert_eq!(ctx.callee_saved_slots.len(), 2);
assert_eq!(ctx.get_callee_saved_offset(1), Some(-8));
assert_eq!(ctx.get_callee_saved_offset(8), Some(-16));
assert_eq!(ctx.get_callee_saved_offset(99), None);
}
#[test]
fn test_frame_sort_callee_saved() {
let mut ctx = SharedFrameLoweringContext::new();
ctx.add_callee_saved_slot(3, -16);
ctx.add_callee_saved_slot(1, -8);
ctx.add_callee_saved_slot(2, -24);
ctx.sort_callee_saved_slots();
assert_eq!(ctx.callee_saved_slots[0].1, -24); assert_eq!(ctx.callee_saved_slots[1].1, -16);
assert_eq!(ctx.callee_saved_slots[2].1, -8);
}
#[test]
fn test_cross_target_frame_default() {
let frame = CrossTargetFrameLowering::default();
assert_eq!(frame.stack_alignment, 16);
assert_eq!(frame.red_zone_size, 0);
assert!(frame.use_frame_pointer);
}
#[test]
fn test_const_materializer_zero() {
let mat = CrossTargetConstMaterializer::default();
let ops = mat.materialize_riscv(0);
assert_eq!(ops.len(), 1);
assert_eq!(ops[0], RiscVOpcode::ADDI);
}
#[test]
fn test_const_materializer_small_positive() {
let mat = CrossTargetConstMaterializer::default();
let ops = mat.materialize_riscv(42);
assert_eq!(ops.len(), 1);
assert_eq!(ops[0], RiscVOpcode::ADDI);
}
#[test]
fn test_const_materializer_medium() {
let mat = CrossTargetConstMaterializer::default();
let ops = mat.materialize_riscv(0x12345000);
assert!(ops.len() >= 2);
}
#[test]
fn test_const_materializer_large() {
let mat = CrossTargetConstMaterializer::default();
let ops = mat.materialize_riscv(0x123456789AB);
assert!(ops.len() >= 4);
}
#[test]
fn test_x86_const_cost() {
let mat = CrossTargetConstMaterializer::default();
assert_eq!(mat.materialize_x86_cost(0), 1);
assert_eq!(mat.materialize_x86_cost(42), 1);
}
#[test]
fn test_branch_analyzer_creation() {
let analyzer = CrossTargetBranchAnalyzer::new();
assert_eq!(analyzer.branch_stats.total_branches, 0);
}
#[test]
fn test_branch_analyzer_analyze() {
let mut analyzer = CrossTargetBranchAnalyzer::new();
let instrs = &["add", "jal", "beq", "ret", "lw", "addi", "bne", "sw"];
analyzer.analyze(instrs);
assert!(analyzer.branch_stats.total_branches > 0);
}
#[test]
fn test_branch_compare_equal() {
let analyzer = CrossTargetBranchAnalyzer::new();
let comp = analyzer.compare(10, 10);
assert!((comp.ratio - 1.0).abs() < 0.001);
assert!(comp.assessment.contains("Similar"));
}
#[test]
fn test_branch_compare_riscv_more() {
let analyzer = CrossTargetBranchAnalyzer::new();
let comp = analyzer.compare(15, 10);
assert!(comp.ratio > 1.0);
assert!(comp.assessment.contains("X86 uses fewer"));
}
#[test]
fn test_branch_compare_riscv_less() {
let analyzer = CrossTargetBranchAnalyzer::new();
let comp = analyzer.compare(8, 10);
assert!(comp.ratio < 0.9);
assert!(comp.assessment.contains("RISC-V uses fewer"));
}
#[test]
fn test_extension_flags_parse_rv64i() {
let flags = ExtensionFlags::parse("rv64i");
assert_eq!(flags.xlen, 64);
assert!(!flags.has_m);
assert!(!flags.has_a);
}
#[test]
fn test_extension_flags_parse_rv64imafdc() {
let flags = ExtensionFlags::parse("rv64imafdc");
assert_eq!(flags.xlen, 64);
assert!(flags.has_m);
assert!(flags.has_a);
assert!(flags.has_f);
assert!(flags.has_d);
assert!(flags.has_c);
}
#[test]
fn test_extension_flags_parse_rv32imc() {
let flags = ExtensionFlags::parse("rv32imc");
assert_eq!(flags.xlen, 32);
assert!(flags.has_m);
assert!(flags.has_c);
assert!(!flags.has_a);
assert!(!flags.has_f);
}
#[test]
fn test_extension_flags_parse_rv64imafdcv_zba_zbb() {
let flags = ExtensionFlags::parse("rv64imafdcv_zba_zbb");
assert_eq!(flags.xlen, 64);
assert!(flags.has_m);
assert!(flags.has_v);
assert!(flags.has_zba);
assert!(flags.has_zbb);
}
#[test]
fn test_extension_flags_parse_with_zicsr_zifencei() {
let flags = ExtensionFlags::parse("rv64i_zicsr_zifencei");
assert!(flags.has_zicsr);
assert!(flags.has_zifencei);
}
#[test]
fn test_extension_flags_parse_with_zfh() {
let flags = ExtensionFlags::parse("rv64imafdc_zfh");
assert!(flags.has_zfh);
}
#[test]
fn test_extension_flags_d_implies_f() {
let flags = ExtensionFlags::parse("rv64id");
assert!(flags.has_d);
assert!(flags.has_f); }
#[test]
fn test_extension_flags_v_implies_f_and_d() {
let flags = ExtensionFlags::parse("rv64iv");
assert!(flags.has_v);
assert!(flags.has_f); assert!(flags.has_d); }
#[test]
fn test_extension_flags_to_isa_string() {
let flags = ExtensionFlags::parse("rv64imafdc");
let s = flags.to_isa_string();
assert!(s.starts_with("rv64i"));
assert!(s.contains('m'));
assert!(s.contains('a'));
assert!(s.contains('f'));
assert!(s.contains('d'));
assert!(s.contains('c'));
}
#[test]
fn test_extension_flags_from_flags() {
let flags = ExtensionFlags::from_flags(
64, true, true, true, true, true, true, true, true, true, true,
);
assert_eq!(flags.xlen, 64);
assert!(flags.has_m);
assert!(flags.has_a);
assert!(flags.has_f);
assert!(flags.has_d);
assert!(flags.has_c);
assert!(flags.has_v);
assert!(flags.has_zba);
assert!(flags.has_zbb);
assert!(flags.has_zbc);
assert!(flags.has_zbs);
}
#[test]
fn test_extension_flags_list_enabled() {
let flags = ExtensionFlags::parse("rv64imac");
let exts = flags.list_enabled();
assert!(exts.contains(&"m".to_string()));
assert!(exts.contains(&"a".to_string()));
assert!(exts.contains(&"c".to_string()));
}
#[test]
fn test_extension_flags_default() {
let flags = ExtensionFlags::default();
assert_eq!(flags.xlen, 0);
assert!(!flags.has_m);
}
#[test]
fn test_bridge_target_machine_rv64() {
let tm = BridgeRISCVTargetMachine::new("riscv64-unknown-linux-gnu");
assert!(tm.is_64bit());
assert!(tm.get_data_layout().contains("p:64:64"));
assert!(tm.get_triple().contains("riscv64"));
}
#[test]
fn test_bridge_target_machine_rv32() {
let tm = BridgeRISCVTargetMachine::new("riscv32-unknown-elf");
assert!(!tm.is_64bit());
assert!(tm.get_data_layout().contains("p:32:32"));
}
#[test]
fn test_detect_64bit_from_triple() {
assert!(BridgeRISCVTargetMachine::detect_64bit_from_triple(
"riscv64-unknown-linux-gnu"
));
assert!(!BridgeRISCVTargetMachine::detect_64bit_from_triple(
"riscv32-unknown-elf"
));
assert!(BridgeRISCVTargetMachine::detect_64bit_from_triple(
"RISCV64-unknown-elf"
));
}
#[test]
fn test_detect_32bit_from_triple() {
assert!(BridgeRISCVTargetMachine::detect_32bit_from_triple(
"riscv32-unknown-elf"
));
assert!(!BridgeRISCVTargetMachine::detect_32bit_from_triple(
"riscv64-unknown-linux-gnu"
));
}
#[test]
fn test_target_machine_with_opt_level() {
let tm = BridgeRISCVTargetMachine::new("riscv64-unknown-elf")
.with_opt_level(OptimizationLevel::O3);
assert_eq!(tm.opt_level, OptimizationLevel::O3);
}
#[test]
fn test_target_machine_with_reloc_model() {
let tm =
BridgeRISCVTargetMachine::new("riscv64-unknown-elf").with_reloc_model(RelocModel::PIC);
assert_eq!(tm.reloc_model, RelocModel::PIC);
}
#[test]
fn test_target_machine_with_code_model() {
let tm =
BridgeRISCVTargetMachine::new("riscv64-unknown-elf").with_code_model(CodeModel::Medium);
assert_eq!(tm.code_model, CodeModel::Medium);
}
#[test]
fn test_target_machine_describe() {
let tm = BridgeRISCVTargetMachine::new("riscv64-unknown-linux-gnu");
let desc = tm.describe();
assert!(desc.contains("riscv64"));
assert!(desc.contains("Data Layout"));
}
#[test]
fn test_target_machine_to_from_conversion() {
let tm = BridgeRISCVTargetMachine::new("riscv64-unknown-linux-gnu");
let canonical = tm.to_target_machine();
assert_eq!(canonical.triple, tm.triple);
assert_eq!(canonical.is_64bit, tm.is_64bit);
let roundtrip = BridgeRISCVTargetMachine::from_target_machine(&canonical);
assert_eq!(roundtrip.triple, tm.triple);
assert_eq!(roundtrip.is_64bit, tm.is_64bit);
}
#[test]
fn test_target_machine_parse_isa_string() {
let tm = BridgeRISCVTargetMachine::new("rv64imafdc");
assert!(tm.extension_flags.has_m);
assert!(tm.extension_flags.has_a);
assert!(tm.extension_flags.has_f);
assert!(tm.extension_flags.has_d);
assert!(tm.extension_flags.has_c);
}
#[test]
fn test_feature_set_enable_disable() {
let mut fs = TargetFeatureSet::new();
assert!(!fs.has("m"));
fs.enable("m");
assert!(fs.has("m"));
fs.disable("m");
assert!(!fs.has("m"));
assert!(fs.is_disabled("m"));
}
#[test]
fn test_feature_set_from_extensions() {
let flags = ExtensionFlags::parse("rv64imafdc");
let fs = TargetFeatureSet::from_riscv_extensions(&flags);
assert!(fs.has("m"));
assert!(fs.has("a"));
assert!(fs.has("f"));
assert!(fs.has("d"));
assert!(fs.has("c"));
assert!(!fs.has("v"));
}
#[test]
fn test_bridge_instr_info_creation() {
let info = BridgeRISCVInstrInfo::new();
assert!(!info.integer_ops.is_empty());
assert!(!info.muldiv_ops.is_empty());
assert!(!info.float_single_ops.is_empty());
assert!(!info.float_double_ops.is_empty());
assert!(!info.compressed_ops.is_empty());
assert!(!info.vector_ops.is_empty());
assert!(!info.bitmanip_ops.is_empty());
assert!(!info.pseudo_ops.is_empty());
}
#[test]
fn test_bridge_instr_info_count_category() {
let info = BridgeRISCVInstrInfo::new();
assert!(info.count_category("integer") > 0);
assert!(info.count_category("muldiv") > 0);
assert!(info.count_category("float_double") > 0);
assert!(info.count_category("compressed") > 0);
}
#[test]
fn test_bridge_instr_info_total_count() {
let info = BridgeRISCVInstrInfo::new();
assert!(info.total_count() > 50);
}
#[test]
fn test_bridge_instr_info_requires_m() {
let info = BridgeRISCVInstrInfo::new();
assert!(info.requires_m(&RiscVOpcode::MUL));
assert!(info.requires_m(&RiscVOpcode::DIV));
assert!(!info.requires_m(&RiscVOpcode::ADD));
}
#[test]
fn test_bridge_instr_info_requires_a() {
let info = BridgeRISCVInstrInfo::new();
assert!(info.requires_a(&RiscVOpcode::AMOSWAP_W));
assert!(info.requires_a(&RiscVOpcode::LR_W));
assert!(!info.requires_a(&RiscVOpcode::ADD));
}
#[test]
fn test_bridge_instr_info_requires_f() {
let info = BridgeRISCVInstrInfo::new();
assert!(info.requires_f(&RiscVOpcode::FADD_S));
assert!(!info.requires_f(&RiscVOpcode::ADD));
}
#[test]
fn test_bridge_instr_info_requires_d() {
let info = BridgeRISCVInstrInfo::new();
assert!(info.requires_d(&RiscVOpcode::FADD_D));
assert!(!info.requires_d(&RiscVOpcode::FADD_S));
}
#[test]
fn test_bridge_instr_info_requires_c() {
let info = BridgeRISCVInstrInfo::new();
assert!(info.requires_c(&RiscVOpcode::C_NOP));
assert!(!info.requires_c(&RiscVOpcode::NOP));
}
#[test]
fn test_bridge_instr_info_requires_v() {
let info = BridgeRISCVInstrInfo::new();
assert!(info.requires_v(&RiscVOpcode::VADD_VV));
assert!(!info.requires_v(&RiscVOpcode::ADD));
}
#[test]
fn test_bridge_instr_info_requires_bitmanip() {
let info = BridgeRISCVInstrInfo::new();
assert!(info.requires_bitmanip(&RiscVOpcode::CLZ));
assert!(info.requires_bitmanip(&RiscVOpcode::ANDN));
assert!(!info.requires_bitmanip(&RiscVOpcode::ADD));
}
#[test]
fn test_bridge_instr_info_find_x86_equiv() {
let info = BridgeRISCVInstrInfo::new();
assert_eq!(info.find_x86_equivalent(&RiscVOpcode::ADD), Some("ADD"));
assert_eq!(info.find_x86_equivalent(&RiscVOpcode::SUB), Some("SUB"));
assert_eq!(info.find_x86_equivalent(&RiscVOpcode::MUL), Some("IMUL"));
assert_eq!(info.find_x86_equivalent(&RiscVOpcode::CLZ), Some("LZCNT"));
assert_eq!(info.find_x86_equivalent(&RiscVOpcode::REV8), Some("BSWAP"));
}
#[test]
fn test_bridge_instr_info_list_for_extension() {
let info = BridgeRISCVInstrInfo::new();
let m_ops = info.list_for_extension("m");
assert!(!m_ops.is_empty());
let a_ops = info.list_for_extension("a");
assert!(!a_ops.is_empty());
let f_ops = info.list_for_extension("f");
assert!(!f_ops.is_empty());
}
#[test]
fn test_bridge_instr_info_get_all_categorized() {
let info = BridgeRISCVInstrInfo::new();
let cats = info.get_all_categorized();
assert!(!cats.is_empty());
for (name, ops) in &cats {
assert!(!ops.is_empty(), "Category {} should have ops", name);
}
}
#[test]
fn test_bridge_reg_info_creation() {
let info = BridgeRISCvRegisterInfo::new();
assert_eq!(info.lookup("x0"), Some(0));
assert_eq!(info.lookup("zero"), Some(0));
assert_eq!(info.lookup("ra"), Some(1));
assert_eq!(info.lookup("sp"), Some(2));
assert_eq!(info.lookup("a0"), Some(10));
}
#[test]
fn test_bridge_reg_info_fpr_lookup() {
let info = BridgeRISCvRegisterInfo::new();
assert_eq!(info.lookup("f0"), Some(32));
assert_eq!(info.lookup("ft0"), Some(32));
assert_eq!(info.lookup("fa0"), Some(42));
}
#[test]
fn test_bridge_reg_info_vr_lookup() {
let info = BridgeRISCvRegisterInfo::new();
assert_eq!(info.lookup("v0"), Some(64));
assert_eq!(info.lookup("v31"), Some(95));
}
#[test]
fn test_bridge_reg_info_missing() {
let info = BridgeRISCvRegisterInfo::new();
assert_eq!(info.lookup("nonexistent"), None);
}
#[test]
fn test_bridge_reg_info_is_gpr_fpr_vr() {
let info = BridgeRISCvRegisterInfo::new();
assert!(info.is_gpr(0));
assert!(info.is_gpr(31));
assert!(!info.is_gpr(32));
assert!(info.is_fpr(32));
assert!(info.is_fpr(63));
assert!(!info.is_fpr(31));
assert!(info.is_vr(64));
assert!(info.is_vr(95));
assert!(!info.is_vr(32));
}
#[test]
fn test_bridge_reg_info_abi_names() {
let info = BridgeRISCvRegisterInfo::new();
assert_eq!(info.gpr_abi_name(0), "zero");
assert_eq!(info.gpr_abi_name(1), "ra");
assert_eq!(info.fpr_abi_name(0), "ft0");
assert_eq!(info.vr_name(0), "v0");
}
#[test]
fn test_bridge_reg_info_callee_saved() {
let callee = BridgeRISCvRegisterInfo::callee_saved_gprs();
assert!(callee.contains(&1)); assert!(callee.contains(&2)); assert!(callee.contains(&8)); }
#[test]
fn test_bridge_reg_info_caller_saved() {
let caller = BridgeRISCvRegisterInfo::caller_saved_gprs();
assert!(caller.contains(&5)); assert!(caller.contains(&10)); assert!(caller.contains(&28)); }
#[test]
fn test_bridge_reg_info_arg_regs() {
let args = BridgeRISCvRegisterInfo::arg_regs();
assert_eq!(args.len(), 8);
assert!(args.contains(&10)); assert!(args.contains(&17)); }
#[test]
fn test_bridge_reg_info_fp_arg_regs() {
let args = BridgeRISCvRegisterInfo::fp_arg_regs();
assert_eq!(args.len(), 8);
assert!(args.contains(&42)); }
#[test]
fn test_bridge_reg_info_return_regs() {
let ret = BridgeRISCvRegisterInfo::return_regs();
assert_eq!(ret, vec![10, 11]);
}
#[test]
fn test_bridge_reg_info_fp_return() {
let ret = BridgeRISCvRegisterInfo::fp_return_regs();
assert_eq!(ret, vec![42, 43]);
}
#[test]
fn test_map_to_x86() {
let info = BridgeRISCvRegisterInfo::new();
assert_eq!(info.map_to_x86(0), Some("zero"));
assert_eq!(info.map_to_x86(2), Some("rsp"));
assert!(info.map_to_x86(32).is_none()); }
#[test]
fn test_frame_lowering_rv64_creation() {
let fl = BridgeRISCVFrameLowering::new_rv64();
assert!(fl.shared_ctx.is_64bit);
}
#[test]
fn test_frame_lowering_rv32_creation() {
let fl = BridgeRISCVFrameLowering::new_rv32();
assert!(!fl.shared_ctx.is_64bit);
}
#[test]
fn test_frame_lowering_compute_size() {
let mut fl = BridgeRISCVFrameLowering::new_rv64();
let size = fl.compute_frame_size(32, 2);
assert!(size >= 32);
assert_eq!(size % 16, 0); }
#[test]
fn test_frame_lowering_prologue_asm_empty() {
let fl = BridgeRISCVFrameLowering::new_rv64();
let asm = fl.prologue_asm();
assert!(asm.is_empty() || asm.contains("sp"));
}
#[test]
fn test_frame_lowering_prologue_asm_with_frame() {
let mut fl = BridgeRISCVFrameLowering::new_rv64();
fl.enable_frame_pointer(true);
fl.shared_ctx.frame_size = 32;
let asm = fl.prologue_asm();
assert!(asm.contains("sd ra"));
assert!(asm.contains("sd s0"));
assert!(asm.contains("addi s0, sp"));
}
#[test]
fn test_frame_lowering_epilogue_asm() {
let mut fl = BridgeRISCVFrameLowering::new_rv64();
fl.enable_frame_pointer(true);
fl.shared_ctx.frame_size = 32;
let asm = fl.epilogue_asm();
assert!(asm.contains("ret"));
}
#[test]
fn test_frame_lowering_set_has_calls() {
let mut fl = BridgeRISCVFrameLowering::new_rv64();
assert!(!fl.shared_ctx.has_calls);
fl.set_has_calls(true);
assert!(fl.shared_ctx.has_calls);
}
#[test]
fn test_frame_lowering_default() {
let fl = BridgeRISCVFrameLowering::default();
assert!(fl.shared_ctx.is_64bit);
}
#[test]
fn test_abi_parse_valid() {
assert_eq!(RiscVABI::parse("ilp32"), Some(RiscVABI::ILP32));
assert_eq!(RiscVABI::parse("ILP32D"), Some(RiscVABI::ILP32D));
assert_eq!(RiscVABI::parse("lp64"), Some(RiscVABI::LP64));
assert_eq!(RiscVABI::parse("LP64D"), Some(RiscVABI::LP64D));
}
#[test]
fn test_abi_parse_invalid() {
assert_eq!(RiscVABI::parse("invalid"), None);
assert_eq!(RiscVABI::parse(""), None);
}
#[test]
fn test_abi_is_64bit() {
assert!(!RiscVABI::ILP32.is_64bit());
assert!(!RiscVABI::ILP32D.is_64bit());
assert!(RiscVABI::LP64.is_64bit());
assert!(RiscVABI::LP64D.is_64bit());
}
#[test]
fn test_abi_has_fp_regs() {
assert!(!RiscVABI::ILP32.has_fp_regs());
assert!(RiscVABI::ILP32F.has_fp_regs());
assert!(RiscVABI::ILP32D.has_fp_regs());
assert!(!RiscVABI::LP64.has_fp_regs());
assert!(RiscVABI::LP64F.has_fp_regs());
assert!(RiscVABI::LP64D.has_fp_regs());
}
#[test]
fn test_abi_xlen() {
assert_eq!(RiscVABI::ILP32.xlen(), 32);
assert_eq!(RiscVABI::LP64.xlen(), 64);
}
#[test]
fn test_abi_default_for_xlen() {
assert_eq!(RiscVABI::default_for_xlen(64), RiscVABI::LP64D);
assert_eq!(RiscVABI::default_for_xlen(32), RiscVABI::ILP32D);
}
#[test]
fn test_abi_as_str() {
assert_eq!(RiscVABI::ILP32.as_str(), "ilp32");
assert_eq!(RiscVABI::LP64D.as_str(), "lp64d");
}
#[test]
fn test_calling_conv_creation_rv64() {
let cc = BridgeRISCVCallingConvention::new(true);
assert!(cc.is_64bit);
assert_eq!(cc.abi_name, "lp64d");
}
#[test]
fn test_calling_conv_creation_rv32() {
let cc = BridgeRISCVCallingConvention::new(false);
assert!(!cc.is_64bit);
assert_eq!(cc.abi_name, "ilp32d");
}
#[test]
fn test_calling_conv_with_abi() {
let cc = BridgeRISCVCallingConvention::with_abi(RiscVABI::LP64);
assert!(cc.is_64bit);
assert_eq!(cc.abi_name, "lp64");
}
#[test]
fn test_calling_conv_int_arg_regs() {
let cc = BridgeRISCVCallingConvention::new(true);
let regs = cc.int_arg_regs();
assert_eq!(regs.len(), 8);
assert_eq!(regs[0], 10); assert_eq!(regs[7], 17); }
#[test]
fn test_calling_conv_fp_arg_regs() {
let cc = BridgeRISCVCallingConvention::new(true);
let regs = cc.fp_arg_regs();
assert_eq!(regs.len(), 8);
assert_eq!(regs[0], 42); }
#[test]
fn test_calling_conv_struct_classification() {
let cc = BridgeRISCVCallingConvention::new(true);
assert_eq!(
cc.classify_struct_arg(8, false),
ArgPassingClass::IntegerRegister
);
assert_eq!(
cc.classify_struct_arg(16, false),
ArgPassingClass::IntegerRegisterPair
);
assert_eq!(cc.classify_struct_arg(17, false), ArgPassingClass::Memory);
assert_eq!(
cc.classify_struct_arg(8, true),
ArgPassingClass::FloatRegister
);
}
#[test]
fn test_calling_conv_describe() {
let cc = BridgeRISCVCallingConvention::new(true);
let desc = cc.describe();
assert!(desc.contains("lp64d"));
assert!(desc.contains("a0-a7"));
assert!(desc.contains("fa0-fa7"));
}
#[test]
fn test_arg_passing_class_display() {
assert_eq!(
ArgPassingClass::IntegerRegister.to_string(),
"IntegerRegister"
);
assert_eq!(ArgPassingClass::Memory.to_string(), "Memory");
}
#[test]
fn test_comparisons_creation() {
let comp = X86RISCVComparisons::new();
assert!(comp.isa_comparison.summary.is_empty());
}
#[test]
fn test_comparisons_run_all() {
let mut comp = X86RISCVComparisons::new();
comp.run_all();
assert!(!comp.isa_comparison.summary.is_empty());
assert!(!comp.code_size_comparison.summary.is_empty());
assert!(!comp.perf_comparison.summary.is_empty());
assert!(!comp.opt_comparison.summary.is_empty());
}
#[test]
fn test_comparisons_report() {
let mut comp = X86RISCVComparisons::new();
comp.run_all();
let report = comp.generate_report();
assert!(report.contains("Cross-Target Comparison Report"));
assert!(report.contains("Instruction Set"));
assert!(report.contains("Code Size"));
assert!(report.contains("Performance"));
assert!(report.contains("Optimization"));
}
#[test]
fn test_isa_comparison_report() {
let mut isa = ISetComparison::new();
isa.compare();
let report = isa.report();
assert!(report.contains("X86 integer ALU"));
assert!(report.contains("RISC-V integer ALU"));
assert!(report.contains("CISC"));
assert!(report.contains("RISC"));
}
#[test]
fn test_code_size_comparison_report() {
let mut cs = CodeSizeComparison::new();
cs.analyze();
let report = cs.report();
assert!(report.contains("X86 avg instr size"));
assert!(report.contains("RISC-V avg instr size"));
}
#[test]
fn test_perf_comparison_report() {
let mut perf = PerformanceComparison::new();
perf.analyze();
let report = perf.report();
assert!(report.contains("IPC"));
assert!(report.contains("register pressure"));
}
#[test]
fn test_opt_comparison_report() {
let mut opt = OptimizationComparison::new();
opt.analyze();
let report = opt.report();
assert!(report.contains("X86-specific strategies"));
assert!(report.contains("RISC-V-specific strategies"));
}
#[test]
fn test_build_riscv_to_x86_map() {
let map = build_riscv_to_x86_map();
assert!(!map.is_empty());
assert_eq!(map.get(&RiscVOpcode::ADD), Some(&"ADD".to_string()));
assert_eq!(map.get(&RiscVOpcode::FADD_S), Some(&"ADDSS".to_string()));
}
#[test]
fn test_build_x86_to_riscv_map() {
let map = build_x86_to_riscv_map();
assert!(!map.is_empty());
assert_eq!(map.get("ADD"), Some(&RiscVOpcode::ADD));
}
#[test]
fn test_riscv_opcode_cost() {
assert_eq!(riscv_opcode_cost(&RiscVOpcode::ADD), 1);
assert_eq!(riscv_opcode_cost(&RiscVOpcode::MUL), 3);
assert_eq!(riscv_opcode_cost(&RiscVOpcode::DIV), 32);
assert_eq!(riscv_opcode_cost(&RiscVOpcode::LW), 4);
assert_eq!(riscv_opcode_cost(&RiscVOpcode::FDIV_D), 20);
assert_eq!(riscv_opcode_cost(&RiscVOpcode::ECALL), 100);
}
#[test]
fn test_x86_opcode_cost() {
assert_eq!(x86_opcode_cost(&X86Opcode::ADD), 1);
assert_eq!(x86_opcode_cost(&X86Opcode::IMUL), 3);
assert_eq!(x86_opcode_cost(&X86Opcode::DIV), 32);
assert_eq!(x86_opcode_cost(&X86Opcode::DIVSD), 14);
}
#[test]
fn test_compare_instruction_cost() {
let comp = compare_instruction_cost(&RiscVOpcode::ADD, &X86Opcode::ADD);
assert!((comp.ratio - 1.0).abs() < 0.001);
assert!(comp.assessment.contains("Similar"));
let comp2 = compare_instruction_cost(&RiscVOpcode::MUL, &X86Opcode::IMUL);
assert!(comp2.ratio <= 1.2);
}
#[test]
fn test_cost_comparison_fields() {
let comp = compare_instruction_cost(&RiscVOpcode::DIV, &X86Opcode::IDIV);
assert!(comp.riscv_cost > 0);
assert!(comp.x86_cost > 0);
assert!(!comp.operation.is_empty());
}
#[test]
fn test_rv64_data_layout_constant() {
assert!(RV64_DATA_LAYOUT.contains("p:64:64"));
assert!(RV64_DATA_LAYOUT.contains("i128:128"));
}
#[test]
fn test_rv32_data_layout_constant() {
assert!(RV32_DATA_LAYOUT.contains("p:32:32"));
assert!(!RV32_DATA_LAYOUT.contains("i128"));
}
#[test]
fn test_bridge_instruction_direct_map() {
let inst = BridgeInstruction::DirectMap(RiscVOpcode::ADD);
match inst {
BridgeInstruction::DirectMap(op) => assert_eq!(op, RiscVOpcode::ADD),
_ => panic!("Wrong variant"),
}
}
#[test]
fn test_bridge_instruction_expanded() {
let inst = BridgeInstruction::Expanded(vec![RiscVOpcode::MUL, RiscVOpcode::MULH]);
match inst {
BridgeInstruction::Expanded(ops) => assert_eq!(ops.len(), 2),
_ => panic!("Wrong variant"),
}
}
#[test]
fn test_bridge_instruction_compare_map() {
let inst = BridgeInstruction::CompareMap {
slt: RiscVOpcode::SLT,
sltu: RiscVOpcode::SLTU,
};
match inst {
BridgeInstruction::CompareMap { slt, sltu } => {
assert_eq!(slt, RiscVOpcode::SLT);
assert_eq!(sltu, RiscVOpcode::SLTU);
}
_ => panic!("Wrong variant"),
}
}
#[test]
fn test_full_bridge_workflow() {
let mut bridge = RISCVX86Bridge::new();
bridge.initialize();
let rv = bridge.translate_x86_to_riscv(&X86Opcode::ADD);
assert!(rv.is_some());
let x86 = bridge.translate_riscv_to_x86(&RiscVOpcode::SUB);
assert!(x86.is_some());
let pattern = bridge.match_shared_isel("add_with_carry", true);
assert!(pattern.is_some());
let results = bridge.apply_cross_target_opt(&["add", "add", "const"]);
assert!(!results.is_empty());
assert!(results.iter().any(|r| r.matched));
assert!(bridge.stats.pattern_translations > 0);
assert!(bridge.stats.shared_isel_matches > 0);
}
#[test]
fn test_target_machine_full_flow() {
let tm = BridgeRISCVTargetMachine::new("rv64imafdc");
let tm = tm
.with_opt_level(OptimizationLevel::O3)
.with_reloc_model(RelocModel::PIC)
.with_code_model(CodeModel::Medium);
assert_eq!(tm.opt_level, OptimizationLevel::O3);
assert_eq!(tm.reloc_model, RelocModel::PIC);
assert_eq!(tm.code_model, CodeModel::Medium);
assert!(tm.extension_flags.has_m);
assert!(tm.extension_flags.has_c);
}
#[test]
fn test_comparisons_full_flow() {
let mut comp = X86RISCVComparisons::new();
comp.run_all();
let report = comp.generate_report();
assert!(!report.is_empty());
assert!(report.contains("Instruction Set"));
assert!(report.contains("Code Size"));
assert!(report.contains("Performance"));
assert!(report.contains("Optimization"));
}
#[test]
fn test_call_conv_full_flow() {
let cc = BridgeRISCVCallingConvention::with_abi(RiscVABI::LP64D);
let cls = cc.classify_struct_arg(4, false);
assert_eq!(cls, ArgPassingClass::IntegerRegister);
let cls2 = cc.classify_struct_arg(12, false);
assert_eq!(cls2, ArgPassingClass::IntegerRegisterPair);
let cls3 = cc.classify_struct_arg(32, false);
assert_eq!(cls3, ArgPassingClass::Memory);
let cls4 = cc.classify_struct_arg(8, true);
assert_eq!(cls4, ArgPassingClass::FloatRegister);
}
#[test]
fn test_frame_lowering_full_flow() {
let mut fl = BridgeRISCVFrameLowering::new_rv64();
fl.enable_frame_pointer(true);
fl.set_has_calls(true);
let frame_size = fl.compute_frame_size(64, 3);
assert!(frame_size >= 64 + 3 * 8);
assert_eq!(frame_size % 16, 0);
let prologue = fl.prologue_asm();
assert!(prologue.contains("sd ra"));
assert!(prologue.contains("sd s0"));
let epilogue = fl.epilogue_asm();
assert!(epilogue.contains("ld ra"));
assert!(epilogue.contains("ret"));
}
#[test]
fn test_register_info_full_flow() {
let info = BridgeRISCvRegisterInfo::new();
assert_eq!(info.lookup("zero"), Some(0));
assert_eq!(info.lookup("ra"), Some(1));
assert_eq!(info.lookup("sp"), Some(2));
assert_eq!(info.lookup("a0"), Some(10));
let callee = BridgeRISCvRegisterInfo::callee_saved_gprs();
assert!(callee.contains(&1)); assert!(callee.contains(&8));
let args = BridgeRISCvRegisterInfo::arg_regs();
assert_eq!(args.len(), 8);
assert_eq!(args[0], 10); }
#[test]
fn test_shared_isel_comprehensive() {
let patterns = SharedISelPatterns::default();
let count = patterns.pattern_count();
assert!(count > 30);
assert!(
patterns
.list_patterns_by_category(PatternCategory::IntegerArithmetic)
.len()
> 5
);
assert!(
patterns
.list_patterns_by_category(PatternCategory::IntegerComparison)
.len()
> 2
);
assert!(
patterns
.list_patterns_by_category(PatternCategory::Logical)
.len()
> 1
);
assert!(
patterns
.list_patterns_by_category(PatternCategory::Shift)
.len()
> 2
);
assert!(
patterns
.list_patterns_by_category(PatternCategory::MemoryAccess)
.len()
> 5
);
assert!(
patterns
.list_patterns_by_category(PatternCategory::ControlFlow)
.len()
> 3
);
assert!(
patterns
.list_patterns_by_category(PatternCategory::FloatArithmetic)
.len()
> 5
);
assert!(
patterns
.list_patterns_by_category(PatternCategory::VectorOperation)
.len()
> 2
);
assert!(
patterns
.list_patterns_by_category(PatternCategory::AtomicOperation)
.len()
> 1
);
}
#[test]
fn test_lowering_rules_comprehensive() {
let rules = CrossTargetLowering::default();
let count = rules.rule_count();
assert!(count >= 8);
assert!(rules.get_rule("combine_add_add_to_address").is_some());
assert!(rules.get_rule("lower_select_to_cmov").is_some());
assert!(rules.get_rule("combine_mul_add_to_madd").is_some());
}
#[test]
fn test_extension_flags_comprehensive() {
let flags = ExtensionFlags::parse("rv64imafdcv_zba_zbb_zbc_zbs_zicsr_zifencei");
assert_eq!(flags.xlen, 64);
assert!(flags.has_m);
assert!(flags.has_a);
assert!(flags.has_f);
assert!(flags.has_d);
assert!(flags.has_c);
assert!(flags.has_v);
assert!(flags.has_zba);
assert!(flags.has_zbb);
assert!(flags.has_zbc);
assert!(flags.has_zbs);
assert!(flags.has_zicsr);
assert!(flags.has_zifencei);
let isa = flags.to_isa_string();
assert!(isa.contains("rv64i"));
assert!(isa.contains("_zba"));
assert!(isa.contains("_zifencei"));
}
#[test]
fn test_data_layout_constants_match_bridge() {
assert!(RV32_DATA_LAYOUT.contains("p:32:32"));
assert!(RV32_DATA_LAYOUT.starts_with("e-m:e"));
assert!(RV64_DATA_LAYOUT.contains("p:64:64"));
assert!(RV64_DATA_LAYOUT.contains("i128:128"));
}
#[test]
fn test_const_materializer_edge_cases() {
let mat = CrossTargetConstMaterializer::default();
assert_eq!(mat.materialize_riscv(0).len(), 1);
assert_eq!(mat.materialize_riscv(2047).len(), 1);
assert_eq!(mat.materialize_riscv(-2048).len(), 1);
let medium = mat.materialize_riscv(2048);
assert!(medium.len() >= 2);
}
#[test]
fn test_shared_ra_multiple_allocations() {
let mut ctx = SharedRegisterAllocContext::default();
ctx.allocate_register(1, 10);
ctx.allocate_register(2, 11);
ctx.allocate_register(3, 12);
assert_eq!(ctx.get_physical_reg(1), Some(10));
assert_eq!(ctx.get_physical_reg(2), Some(11));
assert_eq!(ctx.get_physical_reg(3), Some(12));
ctx.free_register(11);
assert!(ctx.is_register_available(11));
assert_eq!(ctx.get_physical_reg(2), None);
}
#[test]
fn test_shared_ra_spill_multiple() {
let mut ctx = SharedRegisterAllocContext::default();
let s0 = ctx.add_spill_slot(100, 8, 8);
let s1 = ctx.add_spill_slot(101, 8, 8);
let s2 = ctx.add_spill_slot(102, 4, 4);
assert_eq!(s0, 0);
assert_eq!(s1, 1);
assert_eq!(s2, 2);
assert!(ctx.is_spilled(100));
assert!(ctx.is_spilled(101));
assert!(ctx.is_spilled(102));
assert_eq!(ctx.get_spill_slot(100).unwrap().size, 8);
assert_eq!(ctx.get_spill_slot(102).unwrap().size, 4);
}
#[test]
fn test_bridge_instr_info_comprehensive_categories() {
let info = BridgeRISCVInstrInfo::new();
let cats = info.get_all_categorized();
for (name, ops) in &cats {
assert!(!ops.is_empty(), "Category '{}' is empty", name);
}
let m_ops = info.list_for_extension("m");
assert!(m_ops.contains(&&RiscVOpcode::MUL));
assert!(m_ops.contains(&&RiscVOpcode::DIV));
let f_ops = info.list_for_extension("f");
assert!(f_ops.contains(&&RiscVOpcode::FADD_S));
let a_ops = info.list_for_extension("a");
assert!(a_ops.contains(&&RiscVOpcode::AMOSWAP_W));
}
#[test]
fn test_bridge_stat_increment_pattern() {
let mut bridge = RISCVX86Bridge::new();
bridge.reset_stats();
for _ in 0..5 {
bridge.translate_x86_to_riscv(&X86Opcode::ADD);
}
assert_eq!(bridge.stats.pattern_translations, 5);
}
#[test]
fn test_shared_isel_memory_patterns_all() {
let patterns = SharedISelPatterns::default();
let mem_patterns = patterns.list_patterns_by_category(PatternCategory::MemoryAccess);
assert!(!mem_patterns.is_empty());
for name in &mem_patterns {
let m = patterns.find(name, true);
assert!(m.is_some(), "Missing pattern: {}", name);
let m = m.unwrap();
assert!(
!m.riscv_sequence.is_empty(),
"Empty RISC-V sequence for {}",
name
);
assert!(
!m.x86_sequence.is_empty(),
"Empty X86 sequence for {}",
name
);
}
}
#[test]
fn test_shared_isel_control_flow_patterns_complete() {
let patterns = SharedISelPatterns::default();
let cf_patterns = patterns.list_patterns_by_category(PatternCategory::ControlFlow);
let names: Vec<&str> = cf_patterns.iter().map(|s| s.as_str()).collect();
assert!(names.contains(&"unconditional_branch"));
assert!(names.contains(&"branch_if_equal"));
assert!(names.contains(&"function_call"));
assert!(names.contains(&"function_return"));
}
#[test]
fn test_shared_isel_float_patterns_have_correct_extension_requirements() {
let patterns = SharedISelPatterns::default();
let result = patterns.find("fadd_single", true);
assert!(result.is_some());
let m = result.unwrap();
assert!(m.riscv_features.contains(&"f".to_string()));
assert!(m.x86_features.contains(&"sse".to_string()));
let result2 = patterns.find("fadd_double", true);
assert!(result2.is_some());
let m2 = result2.unwrap();
assert!(m2.riscv_features.contains(&"d".to_string()));
}
#[test]
fn test_shared_isel_constant_pattern_cost_comparison() {
let patterns = SharedISelPatterns::default();
let result = patterns.find("load_i32_constant", true);
assert!(result.is_some());
let m = result.unwrap();
assert_eq!(m.riscv_sequence.len(), 2);
assert_eq!(m.x86_sequence.len(), 1);
assert_eq!(m.riscv_cost, 2);
assert_eq!(m.x86_cost, 1);
}
#[test]
fn test_shared_isel_bitmanip_patterns() {
let patterns = SharedISelPatterns::default();
assert!(patterns.find("count_leading_zeros", true).is_some());
assert!(patterns.find("count_trailing_zeros", true).is_some());
assert!(patterns.find("popcount", true).is_some());
assert!(patterns.find("byte_reverse", true).is_some());
assert!(patterns.find("rotate_right", true).is_some());
}
#[test]
fn test_lowering_rules_all_priority_ordered() {
let rules = CrossTargetLowering::default();
let list = rules.list_rules();
for name in &list {
let rule = rules.get_rule(name);
assert!(rule.is_some(), "Rule '{}' should exist", name);
}
}
#[test]
fn test_lowering_rules_memcpy_rule() {
let rules = CrossTargetLowering::default();
let rule = rules.get_rule("lower_memcpy_target_specific");
assert!(rule.is_some());
let r = rule.unwrap();
assert_eq!(r.match_pattern.len(), 4);
assert!(r.targets.contains(&TargetArch::All));
}
#[test]
fn test_lowering_rules_match_multiple() {
let rules = CrossTargetLowering::default();
let seq = &["add", "mul", "const"];
let results = rules.apply(seq);
assert!(results
.iter()
.any(|r| r.rule_name == "combine_mul_add_to_madd"));
}
#[test]
fn test_lowering_rules_target_filtering_present() {
let rules = CrossTargetLowering::default();
let rule = rules.get_rule("lower_select_to_cmov");
assert!(rule.is_some());
let r = rule.unwrap();
assert!(r.targets.contains(&TargetArch::X86));
assert!(r.targets.contains(&TargetArch::X86_64));
assert!(r.targets.contains(&TargetArch::RISCV64));
}
#[test]
fn test_shared_ra_reg_classes_have_registers() {
let ctx = SharedRegisterAllocContext::default();
for cls in &ctx.riscv_classes {
assert!(
!cls.registers.is_empty(),
"RISC-V class '{}' is empty",
cls.name
);
}
for cls in &ctx.x86_classes {
assert!(
!cls.registers.is_empty(),
"X86 class '{}' is empty",
cls.name
);
}
}
#[test]
fn test_shared_ra_riscv_has_three_classes() {
let ctx = SharedRegisterAllocContext::default();
assert_eq!(ctx.riscv_classes.len(), 3);
assert_eq!(ctx.riscv_classes[0].name, "GPR");
assert_eq!(ctx.riscv_classes[1].name, "FPR");
assert_eq!(ctx.riscv_classes[2].name, "VR");
}
#[test]
fn test_shared_ra_x86_has_three_classes() {
let ctx = SharedRegisterAllocContext::default();
assert_eq!(ctx.x86_classes.len(), 3);
assert_eq!(ctx.x86_classes[0].name, "GPR64");
assert_eq!(ctx.x86_classes[1].name, "GPR32");
assert_eq!(ctx.x86_classes[2].name, "XMM");
}
#[test]
fn test_shared_ra_reg_pressure_tracking() {
let mut ctx = SharedRegisterAllocContext::default();
ctx.reg_pressure.insert(5, 3);
ctx.reg_pressure.insert(10, 2);
assert_eq!(ctx.reg_pressure.get(&5), Some(&3));
assert_eq!(ctx.reg_pressure.get(&10), Some(&2));
}
#[test]
fn test_shared_ra_strategy_switching() {
let mut ctx = SharedRegisterAllocContext::default();
assert_eq!(ctx.strategy, AllocationStrategy::Greedy);
ctx.strategy = AllocationStrategy::LinearScan;
assert_eq!(ctx.strategy, AllocationStrategy::LinearScan);
ctx.strategy = AllocationStrategy::Fast;
assert_eq!(ctx.strategy, AllocationStrategy::Fast);
}
#[test]
fn test_shared_ra_multiple_vregs_same_phys() {
let mut ctx = SharedRegisterAllocContext::default();
ctx.allocate_register(10, 5);
ctx.allocate_register(11, 6);
assert_eq!(ctx.get_physical_reg(10), Some(5));
assert_eq!(ctx.get_physical_reg(11), Some(6));
}
#[test]
fn test_shared_ra_spill_slot_offsets_increase() {
let mut ctx = SharedRegisterAllocContext::default();
let s0 = ctx.add_spill_slot(1, 8, 8);
let s1 = ctx.add_spill_slot(2, 8, 8);
let s2 = ctx.add_spill_slot(3, 4, 4);
assert!(s0 < s1);
assert!(s1 < s2);
let o0 = ctx.get_spill_slot(1).unwrap().offset;
let o1 = ctx.get_spill_slot(2).unwrap().offset;
let o2 = ctx.get_spill_slot(3).unwrap().offset;
assert!(o0 > o1);
assert!(o1 > o2);
}
#[test]
fn test_frame_lowering_alignment_requirements() {
let mut fl = BridgeRISCVFrameLowering::new_rv64();
for size in &[0, 1, 7, 8, 15, 16, 31, 32, 63, 64, 127, 128] {
let frame = fl.compute_frame_size(*size, 0);
assert_eq!(frame % 16, 0, "Frame size {} should be 16-aligned", frame);
}
}
#[test]
fn test_frame_lowering_saved_regs_accounted() {
let mut fl = BridgeRISCVFrameLowering::new_rv64();
let frame = fl.compute_frame_size(0, 2);
assert!(frame >= 16);
assert_eq!(frame % 16, 0);
}
#[test]
fn test_frame_lowering_rv32_smaller_reg_width() {
let mut fl_rv64 = BridgeRISCVFrameLowering::new_rv64();
let mut fl_rv32 = BridgeRISCVFrameLowering::new_rv32();
let frame64 = fl_rv64.compute_frame_size(32, 2);
let frame32 = fl_rv32.compute_frame_size(32, 2);
assert!(frame32 <= frame64);
}
#[test]
fn test_frame_lowering_prologue_has_sp_adjustment() {
let mut fl = BridgeRISCVFrameLowering::new_rv64();
fl.shared_ctx.frame_size = 48;
let prologue = fl.prologue_asm();
assert!(prologue.contains("sp, sp"));
}
#[test]
fn test_frame_lowering_epilogue_restores_sp() {
let mut fl = BridgeRISCVFrameLowering::new_rv64();
fl.shared_ctx.frame_size = 32;
let epilogue = fl.epilogue_asm();
assert!(epilogue.contains("sp"));
assert!(epilogue.contains("ret"));
}
#[test]
fn test_all_abi_variants_parse() {
for abi_str in &["ilp32", "ilp32f", "ilp32d", "lp64", "lp64f", "lp64d"] {
let abi = RiscVABI::parse(abi_str);
assert!(abi.is_some(), "Should parse '{}'", abi_str);
}
}
#[test]
fn test_abi_case_insensitive() {
assert_eq!(RiscVABI::parse("LP64D"), Some(RiscVABI::LP64D));
assert_eq!(RiscVABI::parse("Lp64d"), Some(RiscVABI::LP64D));
assert_eq!(RiscVABI::parse("ILP32"), Some(RiscVABI::ILP32));
}
#[test]
fn test_calling_conv_callee_saved_count() {
let cc = BridgeRISCVCallingConvention::new(true);
let callee = cc.callee_saved_regs();
assert!(!callee.is_empty());
assert!(callee.len() >= 14);
}
#[test]
fn test_calling_conv_caller_saved_count() {
let cc = BridgeRISCVCallingConvention::new(true);
let caller = cc.caller_saved_regs();
assert!(!caller.is_empty());
assert!(caller.len() >= 15);
}
#[test]
fn test_calling_conv_int_fp_balance() {
let cc = BridgeRISCVCallingConvention::with_abi(RiscVABI::LP64D);
assert_eq!(cc.int_arg_regs().len(), 8);
assert_eq!(cc.fp_arg_regs().len(), 8);
assert_eq!(cc.int_return_regs().len(), 2);
assert_eq!(cc.fp_return_regs().len(), 2);
}
#[test]
fn test_struct_classification_boundary() {
let cc = BridgeRISCVCallingConvention::new(true);
assert_eq!(
cc.classify_struct_arg(16, false),
ArgPassingClass::IntegerRegisterPair
);
assert_eq!(cc.classify_struct_arg(17, false), ArgPassingClass::Memory);
assert_eq!(
cc.classify_struct_arg(4, false),
ArgPassingClass::IntegerRegister
);
}
#[test]
fn test_extension_flags_all_zk_variants() {
let flags = ExtensionFlags::parse("rv64i_zk_zkn_zks");
assert!(flags.has_zk);
assert!(flags.has_zkn);
assert!(flags.has_zks);
}
#[test]
fn test_extension_flags_hypervisor() {
let flags = ExtensionFlags::parse("rv64ih");
assert!(flags.has_h);
}
#[test]
fn test_extension_flags_cache_ops() {
let flags = ExtensionFlags::parse("rv64i_zicbom_zicboz_zicbop");
assert!(flags.has_zicbom);
assert!(flags.has_zicboz);
assert!(flags.has_zicbop);
}
#[test]
fn test_extension_flags_supervisor() {
let flags = ExtensionFlags::parse("rv64i_svinval_svnapot_svpbmt");
assert!(flags.has_svinval);
assert!(flags.has_svnapot);
assert!(flags.has_svpbmt);
}
#[test]
fn test_extension_flags_wait() {
let flags = ExtensionFlags::parse("rv64i_zawrs");
assert!(flags.has_zawrs);
}
#[test]
fn test_extension_flags_pause() {
let flags = ExtensionFlags::parse("rv64i_zihintpause");
assert!(flags.has_zihintpause);
}
#[test]
fn test_extension_flags_list_enabled_comprehensive() {
let flags = ExtensionFlags::parse("rv64imafdcv_zba_zbb");
let exts = flags.list_enabled();
assert!(exts.iter().any(|e| e == "rv64i"));
assert!(exts.iter().any(|e| e == "m"));
assert!(exts.iter().any(|e| e == "f"));
assert!(exts.iter().any(|e| e == "v"));
assert!(exts.iter().any(|e| e == "zba"));
assert!(exts.iter().any(|e| e == "zbb"));
}
#[test]
fn test_isa_comparison_fields_populated() {
let mut isa = ISetComparison::new();
isa.compare();
assert!(isa.x86_int_alu_count > 0);
assert!(isa.riscv_int_alu_count > 0);
assert!(isa.x86_fp_count > 0);
assert!(isa.riscv_fp_count > 0);
assert!(isa.x86_simd_count > 0);
assert!(isa.riscv_vector_count > 0);
assert!(isa.x86_addr_modes > 0);
assert!(isa.riscv_addr_modes > 0);
}
#[test]
fn test_code_size_comparison_ratios_reasonable() {
let mut cs = CodeSizeComparison::new();
cs.analyze();
assert!(cs.size_ratio > 0.5);
assert!(cs.size_ratio < 3.0);
assert!(cs.ratio_with_compressed <= cs.riscv_avg_instr_size);
}
#[test]
fn test_perf_comparison_values_reasonable() {
let mut perf = PerformanceComparison::new();
perf.analyze();
assert!(perf.x86_ipc > 1.0);
assert!(perf.riscv_ipc > 1.0);
assert!(perf.x86_branch_pred > 0.5 && perf.x86_branch_pred <= 1.0);
assert!(perf.riscv_branch_pred > 0.5 && perf.riscv_branch_pred <= 1.0);
assert!(perf.x86_pipeline_depth > 0);
assert!(perf.riscv_pipeline_depth > 0);
}
#[test]
fn test_opt_comparison_lists_populated() {
let mut opt = OptimizationComparison::new();
opt.analyze();
assert!(!opt.x86_strategies.is_empty());
assert!(!opt.riscv_strategies.is_empty());
assert!(!opt.shared_strategies.is_empty());
}
#[test]
fn test_comparison_metrics_default() {
let metrics = ComparisonMetrics::default();
assert_eq!(metrics.comparisons_run, 0);
assert_eq!(metrics.avg_code_size_ratio, 0.0);
assert_eq!(metrics.avg_perf_ratio, 0.0);
}
#[test]
fn test_translate_x86_bitwise_ops() {
let mut bridge = RISCVX86Bridge::new();
assert!(bridge.translate_x86_to_riscv(&X86Opcode::AND).is_some());
assert!(bridge.translate_x86_to_riscv(&X86Opcode::OR).is_some());
assert!(bridge.translate_x86_to_riscv(&X86Opcode::XOR).is_some());
}
#[test]
fn test_translate_x86_shift_ops() {
let mut bridge = RISCVX86Bridge::new();
assert!(bridge.translate_x86_to_riscv(&X86Opcode::SHL).is_some());
assert!(bridge.translate_x86_to_riscv(&X86Opcode::SHR).is_some());
assert!(bridge.translate_x86_to_riscv(&X86Opcode::SAR).is_some());
}
#[test]
fn test_translate_x86_memory_ops_expand() {
let mut bridge = RISCVX86Bridge::new();
let push_result = bridge.translate_x86_to_riscv(&X86Opcode::PUSH);
assert!(push_result.is_some());
match push_result.unwrap() {
BridgeInstruction::MemoryExpand(ops) => {
assert!(ops.contains(&RiscVOpcode::ADDI));
assert!(ops.contains(&RiscVOpcode::SW));
}
_ => panic!("Expected MemoryExpand"),
}
}
#[test]
fn test_translate_riscv_memory_load() {
let mut bridge = RISCVX86Bridge::new();
assert!(bridge.translate_riscv_to_x86(&RiscVOpcode::LW).is_some());
assert!(bridge.translate_riscv_to_x86(&RiscVOpcode::LD).is_some());
assert!(bridge.translate_riscv_to_x86(&RiscVOpcode::LB).is_some());
}
#[test]
fn test_translate_riscv_memory_store() {
let mut bridge = RISCVX86Bridge::new();
assert!(bridge.translate_riscv_to_x86(&RiscVOpcode::SW).is_some());
assert!(bridge.translate_riscv_to_x86(&RiscVOpcode::SD).is_some());
assert!(bridge.translate_riscv_to_x86(&RiscVOpcode::SB).is_some());
}
#[test]
fn test_translate_bidirectional_consistency() {
let mut bridge = RISCVX86Bridge::new();
let rv = bridge.translate_x86_to_riscv(&X86Opcode::ADD).unwrap();
match rv {
BridgeInstruction::DirectMap(op) => {
let back = bridge.translate_riscv_to_x86(&op);
assert!(back.is_some());
}
_ => {}
}
}
#[test]
fn test_riscv_opcode_cost_all_categories() {
assert!(riscv_opcode_cost(&RiscVOpcode::ADD) <= 2);
assert!(riscv_opcode_cost(&RiscVOpcode::MUL) >= 2);
assert!(riscv_opcode_cost(&RiscVOpcode::DIV) >= 10);
assert!(riscv_opcode_cost(&RiscVOpcode::LW) >= 3);
assert!(riscv_opcode_cost(&RiscVOpcode::FADD_S) >= 2);
assert!(riscv_opcode_cost(&RiscVOpcode::FDIV_D) >= 10);
assert!(riscv_opcode_cost(&RiscVOpcode::AMOSWAP_W) >= 5);
assert!(riscv_opcode_cost(&RiscVOpcode::ECALL) >= 50);
assert!(riscv_opcode_cost(&RiscVOpcode::FENCE) >= 5);
}
#[test]
fn test_x86_opcode_cost_all_categories() {
assert!(x86_opcode_cost(&X86Opcode::ADD) <= 2);
assert!(x86_opcode_cost(&X86Opcode::MUL) >= 2);
assert!(x86_opcode_cost(&X86Opcode::DIV) >= 10);
assert!(x86_opcode_cost(&X86Opcode::ADDSS) >= 2);
assert!(x86_opcode_cost(&X86Opcode::DIVSD) >= 10);
assert!(x86_opcode_cost(&X86Opcode::LZCNT) >= 2);
assert!(x86_opcode_cost(&X86Opcode::BSWAP) <= 2);
}
#[test]
fn test_compare_instruction_cost_various() {
let c1 = compare_instruction_cost(&RiscVOpcode::ADD, &X86Opcode::ADD);
assert!((c1.ratio - 1.0).abs() < 0.5);
let c2 = compare_instruction_cost(&RiscVOpcode::MUL, &X86Opcode::IMUL);
assert!((c2.ratio - 1.0).abs() < 0.5);
let c3 = compare_instruction_cost(&RiscVOpcode::LW, &X86Opcode::MOV);
assert!(c3.ratio > 0.5);
}
#[test]
fn test_translation_maps_consistency() {
let rv_to_x86 = build_riscv_to_x86_map();
let x86_to_rv = build_x86_to_riscv_map();
assert!(!rv_to_x86.is_empty());
assert!(!x86_to_rv.is_empty());
assert!(rv_to_x86.contains_key(&RiscVOpcode::ADD));
assert!(x86_to_rv.contains_key("ADD"));
}
#[test]
fn test_cost_comparison_has_assessment() {
let comp = compare_instruction_cost(&RiscVOpcode::ADD, &X86Opcode::ADD);
assert!(!comp.assessment.is_empty());
assert!(!comp.operation.is_empty());
}
#[test]
fn test_branch_analyzer_on_riscv_like_code() {
let mut analyzer = CrossTargetBranchAnalyzer::new();
let riscv_code = &["addi", "addi", "jal", "add", "beq", "lw", "sw", "ret"];
analyzer.analyze(riscv_code);
assert!(analyzer.branch_stats.total_branches > 0);
}
#[test]
fn test_branch_analyzer_on_x86_like_code() {
let mut analyzer = CrossTargetBranchAnalyzer::new();
let x86_code = &["mov", "add", "cmp", "jne", "mov", "call", "ret"];
analyzer.analyze(x86_code);
assert!(analyzer.branch_stats.total_branches > 0);
}
#[test]
fn test_branch_compare_zero_denominator() {
let analyzer = CrossTargetBranchAnalyzer::new();
let comp = analyzer.compare(10, 0);
assert!(comp.ratio.is_infinite());
}
#[test]
fn test_branch_compare_various_ratios() {
let analyzer = CrossTargetBranchAnalyzer::new();
let c1 = analyzer.compare(5, 10);
assert!(c1.ratio < 1.0);
let c2 = analyzer.compare(15, 10);
assert!(c2.ratio > 1.0);
let c3 = analyzer.compare(10, 10);
assert!((c3.ratio - 1.0).abs() < 0.001);
}
#[test]
fn test_const_materializer_negative_values() {
let mat = CrossTargetConstMaterializer::default();
let ops = mat.materialize_riscv(-1);
assert_eq!(ops.len(), 1);
let ops2 = mat.materialize_riscv(-0x123456789AB);
assert!(!ops2.is_empty());
}
#[test]
fn test_const_materializer_powers_of_two() {
let mat = CrossTargetConstMaterializer::default();
for shift in 0..48 {
let val = 1i64 << shift;
let ops = mat.materialize_riscv(val);
assert!(!ops.is_empty(), "Failed for 1<<{}", shift);
}
}
#[test]
fn test_x86_const_cost_64bit_boundary() {
let mat = CrossTargetConstMaterializer::default();
assert_eq!(mat.materialize_x86_cost(0x7FFFFFFF), 1);
assert_eq!(mat.materialize_x86_cost(0x1_00000000), 2);
}
#[test]
fn test_feature_set_enable_disable_toggle() {
let mut fs = TargetFeatureSet::new();
fs.enable("test");
assert!(fs.has("test"));
fs.disable("test");
assert!(!fs.has("test"));
assert!(fs.is_disabled("test"));
fs.enable("test");
assert!(fs.has("test"));
assert!(!fs.is_disabled("test"));
}
#[test]
fn test_feature_set_multiple_features() {
let mut fs = TargetFeatureSet::new();
fs.enable("m");
fs.enable("a");
fs.enable("f");
assert!(fs.has("m"));
assert!(fs.has("a"));
assert!(fs.has("f"));
assert!(!fs.has("d"));
}
#[test]
fn test_gpr_names_count() {
assert_eq!(RV_GPR_NAMES.len(), 32);
}
#[test]
fn test_fpr_names_count() {
assert_eq!(RV_FPR_NAMES.len(), 32);
}
#[test]
fn test_vr_names_count() {
assert_eq!(RV_VR_NAMES.len(), 32);
}
#[test]
fn test_gpr_names_well_known() {
assert_eq!(RV_GPR_NAMES[0], "zero");
assert_eq!(RV_GPR_NAMES[1], "ra");
assert_eq!(RV_GPR_NAMES[2], "sp");
assert_eq!(RV_GPR_NAMES[3], "gp");
assert_eq!(RV_GPR_NAMES[10], "a0");
assert_eq!(RV_GPR_NAMES[17], "a7");
}
#[test]
fn test_fpr_names_well_known() {
assert_eq!(RV_FPR_NAMES[10], "fa0");
assert_eq!(RV_FPR_NAMES[11], "fa1");
assert_eq!(RV_FPR_NAMES[0], "ft0");
assert_eq!(RV_FPR_NAMES[8], "fs0");
}
#[test]
fn test_target_machine_various_triples() {
let triples = [
"riscv64-unknown-linux-gnu",
"riscv32-unknown-elf",
"riscv64-linux-android",
"riscv64-unknown-freebsd",
];
for triple in &triples {
let tm = BridgeRISCVTargetMachine::new(triple);
assert!(!tm.get_triple().is_empty());
assert!(!tm.get_data_layout().is_empty());
}
}
#[test]
fn test_target_machine_isa_string_triples() {
let isa_strings = ["rv64imac", "rv32imc", "rv64imafdc", "rv64iv"];
for isa in &isa_strings {
let tm = BridgeRISCVTargetMachine::new(isa);
assert!(!tm.extension_flags.to_isa_string().is_empty());
}
}
#[test]
fn test_bridge_many_translations() {
let mut bridge = RISCVX86Bridge::new();
let x86_ops = [
X86Opcode::ADD,
X86Opcode::SUB,
X86Opcode::AND,
X86Opcode::OR,
X86Opcode::XOR,
X86Opcode::SHL,
X86Opcode::SHR,
X86Opcode::SAR,
X86Opcode::JMP,
X86Opcode::CALL,
X86Opcode::RET,
X86Opcode::NOP,
];
for op in &x86_ops {
let _ = bridge.translate_x86_to_riscv(op);
}
assert!(bridge.stats.pattern_translations >= x86_ops.len() as u64);
}
#[test]
fn test_patterns_are_unique_names() {
let patterns = SharedISelPatterns::default();
let names = patterns.list_patterns();
let mut seen = HashSet::new();
for name in &names {
assert!(seen.insert(*name), "Duplicate pattern name: {}", name);
}
}
#[test]
fn test_all_pattern_categories_have_entries() {
let patterns = SharedISelPatterns::default();
let categories = [
PatternCategory::IntegerArithmetic,
PatternCategory::IntegerComparison,
PatternCategory::Logical,
PatternCategory::Shift,
PatternCategory::MemoryAccess,
PatternCategory::ControlFlow,
PatternCategory::FloatArithmetic,
PatternCategory::FloatComparison,
PatternCategory::Conversion,
PatternCategory::ConstantMaterialization,
PatternCategory::VectorOperation,
PatternCategory::AtomicOperation,
];
for cat in &categories {
let entries = patterns.list_patterns_by_category(*cat);
assert!(!entries.is_empty(), "Category {:?} has no patterns", cat);
}
}
#[test]
fn test_lowering_rules_all_have_names() {
let rules = CrossTargetLowering::default();
for name in rules.list_rules() {
assert!(!name.is_empty());
}
}
#[test]
fn test_lowering_rules_no_empty_match_patterns() {
let rules = CrossTargetLowering::default();
for name in rules.list_rules() {
let rule = rules.get_rule(name).unwrap();
assert!(
!rule.match_pattern.is_empty(),
"Rule '{}' has empty match pattern",
name
);
}
}
#[test]
fn test_bridge_initialize_is_idempotent() {
let mut bridge = RISCVX86Bridge::new();
bridge.initialize();
let count_before = bridge.shared_isel_patterns.pattern_count();
bridge.initialize();
assert_eq!(bridge.shared_isel_patterns.pattern_count(), count_before);
}
#[test]
fn test_cross_target_riscv_roundtrip() {
let cross = CrossTargetRISCV::new();
let _cross2 = CrossTargetRISCV::default();
assert!(cross.isel_patterns.pattern_count() > 0);
}
#[test]
fn test_target_machine_feature_string_format() {
let flags = ExtensionFlags::from_flags(
64, true, true, true, true, true, false, false, false, false, false,
);
let features = BridgeRISCVTargetMachine::build_features_string(&flags);
assert!(features.contains("+m"));
assert!(features.contains("+a"));
assert!(features.contains("+f"));
assert!(features.contains("+d"));
assert!(features.contains("+c"));
assert!(!features.contains("+v"));
}
#[test]
fn test_shared_ra_live_interval_fields() {
let interval = LiveInterval {
vreg: 42,
start: 10,
end: 25,
reg_class: 0,
spill_weight: 2.5,
};
assert_eq!(interval.vreg, 42);
assert_eq!(interval.start, 10);
assert_eq!(interval.end, 25);
assert_eq!(interval.reg_class, 0);
assert!((interval.spill_weight - 2.5).abs() < 0.001);
}
#[test]
fn test_shared_reg_class_fields() {
let cls = SharedRegClass {
id: 1,
name: "Test".into(),
registers: vec![1, 2, 3],
alignment: 8,
size_bits: 64,
is_allocatable: true,
copy_cost: 1,
};
assert_eq!(cls.id, 1);
assert_eq!(cls.name, "Test");
assert_eq!(cls.registers.len(), 3);
assert_eq!(cls.size_bits, 64);
}
#[test]
fn test_spill_slot_info_fields() {
let slot = SpillSlotInfo {
slot: 3,
offset: -24,
size: 8,
alignment: 8,
};
assert_eq!(slot.slot, 3);
assert_eq!(slot.offset, -24);
assert_eq!(slot.size, 8);
assert_eq!(slot.alignment, 8);
}
#[test]
fn test_register_alias_fields() {
let alias = RegisterAlias {
primary: 0,
alias: 32,
relation: AliasRelation::SubRegister,
};
assert_eq!(alias.primary, 0);
assert_eq!(alias.alias, 32);
match alias.relation {
AliasRelation::SubRegister => {}
_ => panic!("Wrong relation"),
}
}
#[test]
fn test_replacement_step_fields() {
let step = ReplacementStep {
operation: "test_op".into(),
inputs: vec![0, 1],
output: "result".into(),
metadata: {
let mut m = HashMap::new();
m.insert("key".into(), "value".into());
m
},
};
assert_eq!(step.operation, "test_op");
assert_eq!(step.inputs.len(), 2);
assert_eq!(step.output, "result");
assert!(step.metadata.contains_key("key"));
}
#[test]
fn test_constraint_type_values() {
let reserved = ConstraintType::Reserved;
let fixed = ConstraintType::FixedAssignment;
let caller_saved = ConstraintType::CallerSaved;
let callee_saved = ConstraintType::CalleeSaved;
let aligned = ConstraintType::AlignmentRequired;
assert_ne!(reserved, fixed);
assert_ne!(caller_saved, callee_saved);
assert_ne!(aligned, reserved);
}
#[test]
fn test_target_arch_values() {
assert_eq!(TargetArch::RISCV32, TargetArch::RISCV32);
assert_ne!(TargetArch::X86, TargetArch::X86_64);
assert_eq!(TargetArch::All, TargetArch::All);
}
#[test]
fn test_bridge_instruction_variants_distinct() {
let direct = BridgeInstruction::DirectMap(RiscVOpcode::ADD);
let expanded = BridgeInstruction::Expanded(vec![RiscVOpcode::ADD]);
let compare = BridgeInstruction::CompareMap {
slt: RiscVOpcode::SLT,
sltu: RiscVOpcode::SLTU,
};
match (&direct, &expanded, &compare) {
(
BridgeInstruction::DirectMap(_),
BridgeInstruction::Expanded(_),
BridgeInstruction::CompareMap { .. },
) => {}
_ => panic!("Variants don't match"),
}
}
#[test]
fn test_optimization_level_enum_values() {
let o0 = OptimizationLevel::O0;
let o3 = OptimizationLevel::O3;
let os = OptimizationLevel::Os;
assert!(o0.as_str().contains('0'));
assert!(o3.as_str().contains('3'));
assert!(os.as_str().contains('s'));
assert!(!o0.is_optimizing());
assert!(o3.is_optimizing());
assert!(os.is_size_optimized());
}
#[test]
fn test_reloc_model_enum_values() {
let s = RelocModel::Static;
let pic = RelocModel::PIC;
assert_ne!(s, pic);
}
#[test]
fn test_code_model_enum_values() {
let small = CodeModel::Small;
let large = CodeModel::Large;
assert_ne!(small, large);
}
}