use crate::hexagon::hexagon_instr_info::{
HexagonInstrDesc, HexagonInstrInfo, HexagonOpcode, HexagonOperandType,
};
use crate::hexagon::hexagon_register_info::{
HexagonRegClass, HexagonRegisterInfo, HEXAGON_GPR_BASE, HEXAGON_GPR_COUNT, HEXAGON_MAX_REG_ID,
HEXAGON_PRED_BASE, HEXAGON_VEC_BASE,
};
use crate::hexagon::{
HEXAGON_ENDIANNESS, HEXAGON_PAGE_SIZE, HEXAGON_RED_ZONE_SIZE, HEXAGON_STACK_ALIGNMENT,
};
use crate::target_machine::{CodeGenOptLevel, CodeModel, RelocModel, TargetOptions};
use std::collections::HashMap;
use std::fmt;
pub const X86_GPR_COUNT: usize = 16;
pub const X86_XMM_COUNT: usize = 16;
pub const X86_YMM_COUNT: usize = 16;
pub const HEXAGON_INSTR_SIZE: u32 = 4;
pub const HEXAGON_VLIW_PACKET_SIZE: u32 = 16;
pub const HEXAGON_HVX_VECTOR_SIZE: u32 = 128;
pub const X86_MAX_INSTR_SIZE: u32 = 15;
pub const X86_AVG_INSTR_SIZE: u32 = 4;
pub const X86_RED_ZONE_SIZE: u32 = 128;
pub const X86_STACK_ALIGNMENT: u32 = 16;
pub const X86_PAGE_SIZE: u32 = 4096;
pub const HEXAGON_LOOP_COUNT: usize = 2;
pub const HEXAGON_MAX_SIMM16: i32 = 32767;
pub const HEXAGON_MIN_SIMM16: i32 = -32768;
pub fn hexagon_gpr_to_x86(hex_reg: u16) -> Option<&'static str> {
match hex_reg {
7000 => Some("rax"),
7001 => Some("rdx"),
7002 => Some("rdi"),
7003 => Some("rsi"),
7004 => Some("rcx"),
7005 => Some("r8"),
7006 => Some("r9"),
7007 => Some("r10"),
7008 => Some("r11"),
7009 => Some("rax"),
7010 => Some("rcx"),
7011 => Some("rdx"),
7012 => Some("r8"),
7013 => Some("r9"),
7014 => Some("r10"),
7015 => Some("r11"),
7016 => Some("rbx"),
7017 => Some("r12"),
7018 => Some("r13"),
7019 => Some("r14"),
7020 => Some("r15"),
7021 => Some("rbx"),
7022 => Some("r12"),
7023 => Some("r13"),
7024 => Some("r14"),
7025 => Some("r15"),
7026 => Some("rbx"),
7027 => Some("r12"),
7028 => None,
7029 => Some("rsp"),
7030 => Some("rbp"),
7031 => None,
_ => None,
}
}
pub fn x86_gpr_to_hexagon(x86_reg: &str) -> Option<u16> {
match x86_reg {
"rax" | "eax" | "ax" | "al" => Some(7000),
"rbx" | "ebx" | "bx" | "bl" => Some(7016),
"rcx" | "ecx" | "cx" | "cl" => Some(7004),
"rdx" | "edx" | "dx" | "dl" => Some(7001),
"rsi" | "esi" | "si" | "sil" => Some(7003),
"rdi" | "edi" | "di" | "dil" => Some(7002),
"rsp" | "esp" | "sp" | "spl" => Some(7029),
"rbp" | "ebp" | "bp" | "bpl" => Some(7030),
"r8" | "r8d" | "r8w" | "r8b" => Some(7005),
"r9" | "r9d" | "r9w" | "r9b" => Some(7006),
"r10" | "r10d" | "r10w" | "r10b" => Some(7007),
"r11" | "r11d" | "r11w" | "r11b" => Some(7008),
"r12" | "r12d" | "r12w" | "r12b" => Some(7017),
"r13" | "r13d" | "r13w" | "r13b" => Some(7018),
"r14" | "r14d" | "r14w" | "r14b" => Some(7019),
"r15" | "r15d" | "r15w" | "r15b" => Some(7020),
_ => None,
}
}
pub fn hexagon_pred_to_x86_cc(pred_reg: u16) -> Option<&'static str> {
match pred_reg {
7032 => Some("always"),
7033 => Some("e"),
7034 => Some("ne"),
7035 => Some("never"),
_ => None,
}
}
pub fn hexagon_vec_to_x86(vec_reg: u16) -> Option<&'static str> {
if vec_reg >= 7040 && vec_reg <= 7071 {
let idx = (vec_reg - 7040) as usize;
const XMM_NAMES: [&str; 32] = [
"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9",
"xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", "xmm0", "xmm1", "xmm2", "xmm3",
"xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13",
"xmm14", "xmm15",
];
if idx < 32 {
Some(XMM_NAMES[idx])
} else {
None
}
} else {
None
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum HexagonArch {
V5,
V60,
V62,
V65,
V66,
V67,
V68,
V69,
}
impl fmt::Display for HexagonArch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HexagonArch::V5 => write!(f, "hexagonv5"),
HexagonArch::V60 => write!(f, "hexagonv60"),
HexagonArch::V62 => write!(f, "hexagonv62"),
HexagonArch::V65 => write!(f, "hexagonv65"),
HexagonArch::V66 => write!(f, "hexagonv66"),
HexagonArch::V67 => write!(f, "hexagonv67"),
HexagonArch::V68 => write!(f, "hexagonv68"),
HexagonArch::V69 => write!(f, "hexagonv69"),
}
}
}
impl HexagonArch {
pub fn parse(s: &str) -> Option<Self> {
match s {
"hexagonv5" => Some(HexagonArch::V5),
"hexagonv60" | "v60" => Some(HexagonArch::V60),
"hexagonv62" | "v62" => Some(HexagonArch::V62),
"hexagonv65" | "v65" => Some(HexagonArch::V65),
"hexagonv66" | "v66" => Some(HexagonArch::V66),
"hexagonv67" | "v67" => Some(HexagonArch::V67),
"hexagonv68" | "v68" => Some(HexagonArch::V68),
"hexagonv69" | "v69" => Some(HexagonArch::V69),
_ => None,
}
}
pub fn has_hvx(&self) -> bool {
matches!(
self,
HexagonArch::V65
| HexagonArch::V66
| HexagonArch::V67
| HexagonArch::V68
| HexagonArch::V69
)
}
pub fn has_hw_loops(&self) -> bool {
matches!(
self,
HexagonArch::V60
| HexagonArch::V62
| HexagonArch::V65
| HexagonArch::V66
| HexagonArch::V67
| HexagonArch::V68
| HexagonArch::V69
)
}
pub fn issue_width(&self) -> usize {
match self {
HexagonArch::V5 => 2,
HexagonArch::V60 | HexagonArch::V62 => 3,
_ => 4,
}
}
pub fn is_v6x(&self) -> bool {
!matches!(self, HexagonArch::V5)
}
pub fn data_layout(&self) -> &'static str {
"e-m:e-p:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:32-n32-S32"
}
pub fn stack_alignment(&self) -> u32 {
8
}
pub fn is_64bit(&self) -> bool {
false
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum HexagonArchFamily {
V5Family,
V6Scalar,
V6HVX,
}
impl From<HexagonArch> for HexagonArchFamily {
fn from(arch: HexagonArch) -> Self {
match arch {
HexagonArch::V5 => HexagonArchFamily::V5Family,
HexagonArch::V60 | HexagonArch::V62 => HexagonArchFamily::V6Scalar,
_ => HexagonArchFamily::V6HVX,
}
}
}
#[derive(Clone)]
pub struct HexagonX86Bridge {
pub triple: String,
pub hexagon_arch: HexagonArch,
pub is_hexagon_primary: bool,
pub is_64bit: bool,
pub hexagon_abi: HexagonAbi,
pub x86_cc: X86CallingConventionVariant,
pub hexagon_instr_info: HexagonInstrInfo,
pub hexagon_reg_info: HexagonRegisterInfo,
pub reg_map_hex_to_x86: HashMap<u16, String>,
pub reg_map_x86_to_hex: HashMap<String, u16>,
pub vreg_allocations: HashMap<u32, AllocatedReg>,
pub spill_slots: Vec<SpillSlot>,
pub cost_weights: CostWeights,
pub cost_data: Vec<InstructionCostEntry>,
pub has_hvx: bool,
pub has_hw_loops: bool,
pub enable_vliw_packets: bool,
pub has_sse42: bool,
pub has_avx2: bool,
pub has_avx512: bool,
pub pattern_cache: HashMap<u32, SharedPattern>,
pub frame_info: BridgeFrameInfo,
pub call_conv: BridgeCallingConvention,
pub vliw_scheduler: VLIWScheduler,
pub opt_level: CodeGenOptLevel,
pub code_model: CodeModel,
pub reloc_model: RelocModel,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum HexagonAbi {
SVR4,
SVR4HVX,
BareMetal,
}
impl Default for HexagonAbi {
fn default() -> Self {
HexagonAbi::SVR4
}
}
impl fmt::Display for HexagonAbi {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HexagonAbi::SVR4 => write!(f, "svr4"),
HexagonAbi::SVR4HVX => write!(f, "svr4-hvx"),
HexagonAbi::BareMetal => write!(f, "baremetal"),
}
}
}
impl HexagonAbi {
pub fn num_int_arg_regs(&self) -> usize {
4
}
pub fn num_int_ret_regs(&self) -> usize {
2
}
pub fn num_hvx_arg_regs(&self) -> usize {
match self {
HexagonAbi::SVR4HVX => 8,
_ => 0,
}
}
pub fn stack_alignment(&self) -> u32 {
match self {
HexagonAbi::SVR4HVX => 128,
_ => 8,
}
}
pub fn uses_hvx_cc(&self) -> bool {
matches!(self, HexagonAbi::SVR4HVX)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86CallingConventionVariant {
SysV64,
Win64,
CDecl32,
StdCall32,
FastCall32,
}
impl Default for X86CallingConventionVariant {
fn default() -> Self {
X86CallingConventionVariant::SysV64
}
}
impl fmt::Display for X86CallingConventionVariant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86CallingConventionVariant::SysV64 => write!(f, "sysv64"),
X86CallingConventionVariant::Win64 => write!(f, "win64"),
X86CallingConventionVariant::CDecl32 => write!(f, "cdecl32"),
X86CallingConventionVariant::StdCall32 => write!(f, "stdcall32"),
X86CallingConventionVariant::FastCall32 => write!(f, "fastcall32"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AllocatedReg {
pub vreg: u32,
pub hexagon_reg: u16,
pub x86_reg: String,
pub is_spill: bool,
pub spill_slot: Option<usize>,
pub reg_class: RegClassKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RegClassKind {
GPR32,
GPR64,
Predicate,
HVX64,
HVX128,
XMM,
YMM,
ZMM,
Flag,
}
impl RegClassKind {
pub fn size_bytes(&self) -> usize {
match self {
RegClassKind::GPR32 => 4,
RegClassKind::GPR64 => 8,
RegClassKind::Predicate => 1,
RegClassKind::HVX64 => 64,
RegClassKind::HVX128 => 128,
RegClassKind::XMM => 16,
RegClassKind::YMM => 32,
RegClassKind::ZMM => 64,
RegClassKind::Flag => 0,
}
}
pub fn is_integer(&self) -> bool {
matches!(self, RegClassKind::GPR32 | RegClassKind::GPR64)
}
pub fn is_vector(&self) -> bool {
matches!(
self,
RegClassKind::HVX64
| RegClassKind::HVX128
| RegClassKind::XMM
| RegClassKind::YMM
| RegClassKind::ZMM
)
}
pub fn is_hexagon(&self) -> bool {
matches!(
self,
RegClassKind::GPR32
| RegClassKind::GPR64
| RegClassKind::Predicate
| RegClassKind::HVX64
| RegClassKind::HVX128
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SpillSlot {
pub offset: i32,
pub size: u32,
pub alignment: u32,
pub is_callee_saved: bool,
pub hexagon_reg: Option<u16>,
pub x86_reg: Option<String>,
pub reg_class: Option<RegClassKind>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CostWeights {
pub size_weight: f64,
pub latency_weight: f64,
pub throughput_weight: f64,
pub reg_pressure_weight: f64,
pub power_weight: f64,
pub vliw_util_weight: f64,
pub target_bias: f64,
}
impl Default for CostWeights {
fn default() -> Self {
CostWeights {
size_weight: 0.20,
latency_weight: 0.30,
throughput_weight: 0.20,
reg_pressure_weight: 0.10,
power_weight: 0.10,
vliw_util_weight: 0.05,
target_bias: 0.0,
}
}
}
#[derive(Debug, Clone)]
pub struct InstructionCostEntry {
pub ir_opcode: u32,
pub hexagon_mnemonic: String,
pub x86_mnemonic: String,
pub hexagon_cost: TargetInstructionCost,
pub x86_cost: TargetInstructionCost,
pub winner: CostWinner,
pub is_vliw: bool,
pub vliw_slot: Option<u8>,
}
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct TargetInstructionCost {
pub latency: u32,
pub rthroughput: f64,
pub size: u32,
pub uops: u32,
pub gpr_used: u32,
pub vec_used: u32,
pub pred_used: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CostWinner {
Hexagon,
X86,
Tie,
HexagonUnsupported,
X86Unsupported,
}
impl fmt::Display for CostWinner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CostWinner::Hexagon => write!(f, "Hexagon"),
CostWinner::X86 => write!(f, "X86"),
CostWinner::Tie => write!(f, "Tie"),
CostWinner::HexagonUnsupported => write!(f, "HexagonUnsupported"),
CostWinner::X86Unsupported => write!(f, "X86Unsupported"),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct BridgeFrameInfo {
pub frame_size: u32,
pub num_callee_saved_gprs: u32,
pub num_callee_saved_vecs: u32,
pub local_area_offset: i32,
pub spill_area_offset: i32,
pub has_frame_pointer: bool,
pub is_leaf: bool,
pub has_variable_alloca: bool,
pub has_struct_ret: bool,
pub outgoing_args_size: u32,
pub max_call_frame_size: u32,
pub frame_alignment: u32,
pub caller_frame_size: u32,
pub use_allocframe: bool,
}
impl BridgeFrameInfo {
pub fn new() -> Self {
BridgeFrameInfo {
frame_size: 0,
num_callee_saved_gprs: 0,
num_callee_saved_vecs: 0,
local_area_offset: 0,
spill_area_offset: 0,
has_frame_pointer: true,
is_leaf: true,
has_variable_alloca: false,
has_struct_ret: false,
outgoing_args_size: 0,
max_call_frame_size: 0,
frame_alignment: HEXAGON_STACK_ALIGNMENT,
caller_frame_size: 0,
use_allocframe: true,
}
}
pub fn compute_frame_layout(&mut self) -> u32 {
let mut offset: u32 = 0;
offset += self.max_call_frame_size;
self.outgoing_args_size = self.max_call_frame_size;
let spill_gprs = self.num_callee_saved_gprs * 4;
let spill_vecs = self.num_callee_saved_vecs * 128;
offset += spill_gprs + spill_vecs;
self.spill_area_offset = offset as i32;
self.local_area_offset = offset as i32;
let aligned = align_up(offset, self.frame_alignment);
self.frame_size = aligned;
aligned
}
pub fn emit_hexagon_prologue(&self) -> String {
let mut code = String::new();
if !self.use_allocframe {
if self.frame_size > 0 {
code.push_str(&format!("\tr29 = sub(r29, #{});\n", self.frame_size));
}
if self.has_frame_pointer {
code.push_str("\tr30 = r29;\n");
if self.frame_size > 0 {
code.push_str(&format!("\tr30 = add(r30, #{});\n", self.frame_size));
}
}
} else {
if self.has_frame_pointer {
code.push_str(&format!("\tallocframe(r29, #{});\n", self.frame_size));
} else {
code.push_str(&format!("\tallocframe(#{});\n", self.frame_size));
}
}
if self.num_callee_saved_gprs > 0 {
code.push_str("\t// Callee-saved GPRs saved in spill area\n");
}
if self.num_callee_saved_vecs > 0 {
code.push_str("\t// Callee-saved HVX registers saved in spill area\n");
}
code
}
pub fn emit_hexagon_epilogue(&self) -> String {
let mut code = String::new();
if self.num_callee_saved_vecs > 0 {
code.push_str("\t// Restore callee-saved HVX registers\n");
}
if self.num_callee_saved_gprs > 0 {
code.push_str("\t// Restore callee-saved GPRs\n");
}
if self.use_allocframe {
code.push_str("\tdeallocframe;\n");
} else {
if self.has_frame_pointer {
code.push_str("\tr29 = r30;\n");
}
if self.frame_size > 0 {
code.push_str(&format!("\tr29 = add(r29, #{});\n", self.frame_size));
}
}
code.push_str("\tjumpr r31;\n");
code
}
pub fn emit_x86_prologue(&self) -> String {
let mut code = String::new();
code.push_str("\tpushq\t%rbp\n");
code.push_str("\tmovq\t%rsp, %rbp\n");
if self.frame_size > 0 {
let aligned = align_up(self.frame_size, 16);
if aligned > 0 {
code.push_str(&format!("\tsubq\t${}, %rsp\n", aligned));
}
}
code
}
pub fn emit_x86_epilogue(&self) -> String {
"\tmovq\t%rbp, %rsp\n\tpopq\t%rbp\n\tretq\n".to_string()
}
}
fn align_up(value: u32, align: u32) -> u32 {
if align == 0 {
value
} else {
(value + align - 1) & !(align - 1)
}
}
#[derive(Debug, Clone)]
pub struct BridgeCallingConvention {
pub hexagon_abi: HexagonAbi,
pub x86_cc: X86CallingConventionVariant,
pub hexagon_int_arg_regs: Vec<u16>,
pub hexagon_int_ret_regs: Vec<u16>,
pub hexagon_hvx_arg_regs: Vec<u16>,
pub hexagon_hvx_ret_regs: Vec<u16>,
pub x86_int_arg_regs: Vec<String>,
pub x86_int_ret_reg: String,
pub x86_xmm_arg_regs: Vec<String>,
pub x86_xmm_ret_reg: String,
pub hexagon_callee_saved_gprs: Vec<u16>,
pub hexagon_callee_saved_hvx: Vec<u16>,
pub x86_callee_saved_gprs: Vec<String>,
pub arg_allocations: Vec<ArgAllocation>,
}
impl BridgeCallingConvention {
pub fn new(hexagon_abi: HexagonAbi, x86_cc: X86CallingConventionVariant) -> Self {
let hex_int_args = vec![7000, 7001, 7002, 7003];
let hex_int_rets = vec![7000, 7001];
let hex_hvx_args = if hexagon_abi.uses_hvx_cc() {
(0..8).map(|i| 7040 + i).collect()
} else {
vec![]
};
let hex_hvx_rets = if hexagon_abi.uses_hvx_cc() {
vec![7040, 7041]
} else {
vec![]
};
let x86_int_args = vec![
"rdi".into(),
"rsi".into(),
"rdx".into(),
"rcx".into(),
"r8".into(),
"r9".into(),
];
let x86_xmm_args = (0..8).map(|i| format!("xmm{}", i)).collect();
let hex_callee = (16..28)
.map(|i| 7000 + i as u16)
.filter(|&r| r != 7028)
.collect();
let hex_callee_hvx = (16..32).map(|i| 7040 + i as u16).collect();
let x86_callee = vec![
"rbx".into(),
"rbp".into(),
"r12".into(),
"r13".into(),
"r14".into(),
"r15".into(),
];
BridgeCallingConvention {
hexagon_abi,
x86_cc,
hexagon_int_arg_regs: hex_int_args,
hexagon_int_ret_regs: hex_int_rets,
hexagon_hvx_arg_regs: hex_hvx_args,
hexagon_hvx_ret_regs: hex_hvx_rets,
x86_int_arg_regs: x86_int_args,
x86_int_ret_reg: "rax".into(),
x86_xmm_arg_regs: x86_xmm_args,
x86_xmm_ret_reg: "xmm0".into(),
hexagon_callee_saved_gprs: hex_callee,
hexagon_callee_saved_hvx: hex_callee_hvx,
x86_callee_saved_gprs: x86_callee,
arg_allocations: vec![],
}
}
pub fn num_int_arg_regs(&self) -> usize {
self.hexagon_int_arg_regs.len()
}
pub fn num_hvx_arg_regs(&self) -> usize {
self.hexagon_hvx_arg_regs.len()
}
pub fn classify_hexagon_type(&self, size_bytes: u32, _align: u32) -> ArgClass {
if size_bytes <= 4 {
ArgClass::Integer
} else if size_bytes <= 8 {
ArgClass::IntegerPair
} else if self.hexagon_abi.uses_hvx_cc() && size_bytes <= 128 {
ArgClass::HVXVector
} else {
ArgClass::Memory
}
}
pub fn classify_x86_type(&self, _size_bytes: u32, _align: u32) -> ArgClass {
ArgClass::Integer
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArgClass {
Integer,
IntegerPair,
HVXVector,
SSE,
Memory,
NoClass,
}
#[derive(Debug, Clone)]
pub struct ArgAllocation {
pub arg_index: u32,
pub hexagon_reg: Option<u16>,
pub x86_reg: Option<String>,
pub stack_offset: Option<i32>,
pub arg_class: ArgClass,
pub size: u32,
}
#[derive(Debug, Clone)]
pub struct VLIWScheduler {
pub enabled: bool,
pub max_slots: usize,
pub current_packet: Vec<VLIWSlot>,
pub packets: Vec<VLIWPacket>,
pub resource_state: VLIWResourceState,
pub dependencies: Vec<VLIWDependency>,
}
#[derive(Debug, Clone)]
pub struct VLIWSlot {
pub opcode: HexagonOpcode,
pub slot: u8,
pub dest: Option<u16>,
pub srcs: Vec<u16>,
pub immediate: Option<i64>,
pub mnemonic: String,
pub predicated: bool,
pub predicate_reg: Option<u16>,
pub predicate_inverted: bool,
}
#[derive(Debug, Clone)]
pub struct VLIWPacket {
pub slots: Vec<VLIWSlot>,
pub address: u64,
pub is_endloop: bool,
pub has_branch: bool,
}
#[derive(Debug, Clone, Default)]
pub struct VLIWResourceState {
pub slot0_used: bool,
pub slot1_used: bool,
pub slot2_used: bool,
pub slot3_used: bool,
pub alu0_busy: bool,
pub alu1_busy: bool,
pub mem_busy: bool,
pub branch_busy: bool,
pub mpy_busy: bool,
pub hvx_busy: bool,
}
#[derive(Debug, Clone)]
pub struct VLIWDependency {
pub producer: usize,
pub consumer: usize,
pub reg: u16,
pub is_raw: bool,
pub latency: u32,
}
impl VLIWScheduler {
pub fn new(enabled: bool, issue_width: usize) -> Self {
VLIWScheduler {
enabled,
max_slots: issue_width.min(4),
current_packet: vec![],
packets: vec![],
resource_state: VLIWResourceState::default(),
dependencies: vec![],
}
}
pub fn try_emit(&mut self, slot: VLIWSlot) -> bool {
if !self.enabled {
self.packets.push(VLIWPacket {
slots: vec![slot],
address: 0,
is_endloop: false,
has_branch: false,
});
return true;
}
if self.current_packet.len() >= self.max_slots {
return false;
}
if self.check_conflict(&slot) {
return false;
}
self.reserve(&slot);
self.current_packet.push(slot);
true
}
pub fn finish_packet(&mut self, address: u64) {
if !self.current_packet.is_empty() {
let has_branch = self.current_packet.iter().any(|s| {
matches!(
s.opcode,
HexagonOpcode::JUMP
| HexagonOpcode::JUMPR
| HexagonOpcode::JUMPr
| HexagonOpcode::CALL
| HexagonOpcode::CALLR
)
});
let is_endloop = self
.current_packet
.iter()
.any(|s| matches!(s.opcode, HexagonOpcode::ENDLOOP0 | HexagonOpcode::ENDLOOP1));
self.packets.push(VLIWPacket {
slots: std::mem::take(&mut self.current_packet),
address,
is_endloop,
has_branch,
});
self.resource_state = VLIWResourceState::default();
}
}
fn is_alu(&self, op: HexagonOpcode) -> bool {
matches!(
op,
HexagonOpcode::ADD
| HexagonOpcode::SUB
| HexagonOpcode::AND
| HexagonOpcode::OR
| HexagonOpcode::XOR
| HexagonOpcode::NEG
| HexagonOpcode::NOT
| HexagonOpcode::COMBINE
| HexagonOpcode::SXT
| HexagonOpcode::ZXT
| HexagonOpcode::ABS
| HexagonOpcode::LSL
| HexagonOpcode::LSR
| HexagonOpcode::ASR
| HexagonOpcode::ROR
| HexagonOpcode::ROL
)
}
fn is_mul(&self, op: HexagonOpcode) -> bool {
matches!(
op,
HexagonOpcode::MPY
| HexagonOpcode::MPYU
| HexagonOpcode::MPYI
| HexagonOpcode::MPYIH
| HexagonOpcode::MPYH
| HexagonOpcode::MPYHH
| HexagonOpcode::MPYSAT
)
}
fn is_mem(&self, op: HexagonOpcode) -> bool {
matches!(
op,
HexagonOpcode::LDW
| HexagonOpcode::LDB
| HexagonOpcode::LDH
| HexagonOpcode::LDD
| HexagonOpcode::LDUW
| HexagonOpcode::STW
| HexagonOpcode::STB
| HexagonOpcode::STH
| HexagonOpcode::STD
| HexagonOpcode::MEMW
| HexagonOpcode::MEMD
)
}
fn is_branch(&self, op: HexagonOpcode) -> bool {
matches!(
op,
HexagonOpcode::JUMP
| HexagonOpcode::JUMPR
| HexagonOpcode::JUMPr
| HexagonOpcode::CALL
| HexagonOpcode::CALLR
| HexagonOpcode::JMP
| HexagonOpcode::JMPR
| HexagonOpcode::LOOP0
| HexagonOpcode::LOOP1
| HexagonOpcode::ENDLOOP0
| HexagonOpcode::ENDLOOP1
)
}
fn is_hvx(&self, op: HexagonOpcode) -> bool {
matches!(
op,
HexagonOpcode::VADD
| HexagonOpcode::VSUB
| HexagonOpcode::VMPY
| HexagonOpcode::VMPA
| HexagonOpcode::VSHUFF
| HexagonOpcode::VSHUFFE
| HexagonOpcode::VSHUFFO
| HexagonOpcode::VALIGN
| HexagonOpcode::VLUT32
| HexagonOpcode::VSPLAT
| HexagonOpcode::VEXTRACT
| HexagonOpcode::VADDW
| HexagonOpcode::VSUBW
| HexagonOpcode::VMULW
| HexagonOpcode::VMADDW
| HexagonOpcode::VAND
| HexagonOpcode::VOR
| HexagonOpcode::VXOR
| HexagonOpcode::VNOT
| HexagonOpcode::VMINW
| HexagonOpcode::VMAXW
| HexagonOpcode::VCMPEQW
| HexagonOpcode::VCMPGTW
)
}
fn check_conflict(&self, slot: &VLIWSlot) -> bool {
if self.is_alu(slot.opcode)
&& self.resource_state.alu0_busy
&& self.resource_state.alu1_busy
{
return true;
}
if self.is_mul(slot.opcode) && self.resource_state.mpy_busy {
return true;
}
if self.is_mem(slot.opcode) && self.resource_state.mem_busy {
return true;
}
if self.is_branch(slot.opcode) && self.resource_state.branch_busy {
return true;
}
if self.is_hvx(slot.opcode) && self.resource_state.hvx_busy {
return true;
}
false
}
fn reserve(&mut self, slot: &VLIWSlot) {
if self.is_alu(slot.opcode) {
if !self.resource_state.alu0_busy {
self.resource_state.alu0_busy = true;
} else {
self.resource_state.alu1_busy = true;
}
}
if self.is_mul(slot.opcode) {
self.resource_state.mpy_busy = true;
}
if self.is_mem(slot.opcode) {
self.resource_state.mem_busy = true;
}
if self.is_branch(slot.opcode) {
self.resource_state.branch_busy = true;
}
if self.is_hvx(slot.opcode) {
self.resource_state.hvx_busy = true;
}
}
pub fn packet_count(&self) -> usize {
self.packets.len()
}
pub fn instruction_count(&self) -> usize {
self.packets.iter().map(|p| p.slots.len()).sum()
}
pub fn utilization(&self) -> f64 {
if self.packets.is_empty() || self.max_slots == 0 {
0.0
} else {
let total = self.packets.len() * self.max_slots;
let used: usize = self.packets.iter().map(|p| p.slots.len()).sum();
used as f64 / total as f64
}
}
}
#[derive(Debug, Clone)]
pub struct SharedPattern {
pub opcode: u32,
pub hexagon_sequence: Vec<PatternInstruction>,
pub x86_sequence: Vec<PatternInstruction>,
pub is_identical: bool,
pub hexagon_cost: u32,
pub x86_cost: u32,
pub vliw_slot: Option<u8>,
pub can_predicate: bool,
}
#[derive(Debug, Clone)]
pub struct PatternInstruction {
pub mnemonic: String,
pub operands: Vec<PatternOperand>,
pub has_def: bool,
pub is_terminator: bool,
pub is_branch: bool,
}
#[derive(Debug, Clone)]
pub struct PatternOperand {
pub kind: PatternOperandKind,
pub vreg: Option<u32>,
pub immediate: Option<i64>,
pub reg_name: Option<String>,
pub reg_class: Option<RegClassKind>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PatternOperandKind {
Def,
Use,
Immediate,
Memory,
BranchTarget,
ConditionCode,
PacketBoundary,
Predicate,
}
#[derive(Debug, Clone)]
pub struct CrossTargetIRInst {
pub opcode: u32,
pub dest: Option<u32>,
pub srcs: Vec<u32>,
pub immediate: Option<i64>,
pub type_code: u8,
pub is_terminator: bool,
pub mem_addr: Option<u32>,
pub branch_target: Option<String>,
pub condition: Option<CrossTargetCond>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CrossTargetCond {
EQ,
NE,
LT,
LE,
GT,
GE,
LTU,
LEU,
GTU,
GEU,
Always,
Never,
}
#[derive(Debug, Clone)]
pub struct CrossTargetMBB {
pub label: String,
pub instructions: Vec<CrossTargetIRInst>,
pub successors: Vec<String>,
pub predecessors: Vec<String>,
pub is_entry: bool,
pub has_fallthrough: bool,
}
#[derive(Debug, Clone)]
pub struct CrossTargetMF {
pub name: String,
pub blocks: Vec<CrossTargetMBB>,
pub num_vregs: u32,
pub frame_size: u32,
pub is_varargs: bool,
}
impl HexagonX86Bridge {
pub fn new(triple: &str) -> Self {
let hexagon_arch = Self::parse_arch(triple);
let mut bridge = Self {
triple: triple.to_string(),
hexagon_arch,
is_hexagon_primary: triple.contains("hexagon"),
is_64bit: false,
hexagon_abi: HexagonAbi::default(),
x86_cc: X86CallingConventionVariant::SysV64,
hexagon_instr_info: HexagonInstrInfo::new(),
hexagon_reg_info: HexagonRegisterInfo,
reg_map_hex_to_x86: HashMap::new(),
reg_map_x86_to_hex: HashMap::new(),
vreg_allocations: HashMap::new(),
spill_slots: vec![],
cost_weights: CostWeights::default(),
cost_data: vec![],
has_hvx: hexagon_arch.has_hvx(),
has_hw_loops: hexagon_arch.has_hw_loops(),
enable_vliw_packets: true,
has_sse42: true,
has_avx2: false,
has_avx512: false,
pattern_cache: HashMap::new(),
frame_info: BridgeFrameInfo::new(),
call_conv: BridgeCallingConvention::new(
HexagonAbi::default(),
X86CallingConventionVariant::SysV64,
),
vliw_scheduler: VLIWScheduler::new(true, hexagon_arch.issue_width()),
opt_level: CodeGenOptLevel::Default,
code_model: CodeModel::Small,
reloc_model: RelocModel::Static,
};
bridge.build_reg_maps();
bridge.init_patterns();
bridge
}
fn parse_arch(triple: &str) -> HexagonArch {
if triple.contains("v69") {
HexagonArch::V69
} else if triple.contains("v68") {
HexagonArch::V68
} else if triple.contains("v67") {
HexagonArch::V67
} else if triple.contains("v66") {
HexagonArch::V66
} else if triple.contains("v65") {
HexagonArch::V65
} else if triple.contains("v62") {
HexagonArch::V62
} else if triple.contains("v60") {
HexagonArch::V60
} else {
HexagonArch::V5
}
}
fn build_reg_maps(&mut self) {
for reg in 7000..=7031u16 {
if let Some(n) = hexagon_gpr_to_x86(reg) {
self.reg_map_hex_to_x86.insert(reg, n.to_string());
self.reg_map_x86_to_hex.insert(n.to_string(), reg);
}
}
if self.has_hvx {
for reg in 7040..=7071u16 {
if let Some(n) = hexagon_vec_to_x86(reg) {
self.reg_map_hex_to_x86.insert(reg, n.to_string());
}
}
}
}
fn init_patterns(&mut self) {
self.add_alu();
self.add_mem();
self.add_branch();
if self.has_hvx {
self.add_vector();
}
self.add_pseudo();
}
fn mk_alu_pat(
&self,
opcode: u32,
hname: &str,
xname: &str,
cost: u32,
slot: u8,
pred: bool,
) -> SharedPattern {
SharedPattern {
opcode,
hexagon_sequence: vec![PatternInstruction {
mnemonic: hname.into(),
operands: vec![
PatternOperand {
kind: PatternOperandKind::Def,
vreg: Some(0),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
PatternOperand {
kind: PatternOperandKind::Use,
vreg: Some(1),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
PatternOperand {
kind: PatternOperandKind::Use,
vreg: Some(2),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
],
has_def: true,
is_terminator: false,
is_branch: false,
}],
x86_sequence: vec![PatternInstruction {
mnemonic: xname.into(),
operands: vec![
PatternOperand {
kind: PatternOperandKind::Use,
vreg: Some(2),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
PatternOperand {
kind: PatternOperandKind::Def,
vreg: Some(0),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
],
has_def: true,
is_terminator: false,
is_branch: false,
}],
is_identical: false,
hexagon_cost: cost,
x86_cost: cost,
vliw_slot: Some(slot),
can_predicate: pred,
}
}
fn add_alu(&mut self) {
for (op, h, x) in &[
(0u32, "add", "addl"),
(1, "sub", "subl"),
(2, "and", "andl"),
(3, "or", "orl"),
(4, "xor", "xorl"),
] {
self.pattern_cache
.insert(*op, self.mk_alu_pat(*op, h, x, 1, 0, true));
}
self.pattern_cache
.insert(10, self.mk_alu_pat(10, "mpy", "imull", 3, 2, true));
for (op, h, x) in &[
(20u32, "lsl", "shll"),
(21, "lsr", "shrl"),
(22, "asr", "sarl"),
] {
self.pattern_cache
.insert(*op, self.mk_alu_pat(*op, h, x, 1, 1, true));
}
self.pattern_cache.insert(
40,
SharedPattern {
opcode: 40,
hexagon_sequence: vec![PatternInstruction {
mnemonic: "p1 = cmpeq".into(),
operands: vec![
PatternOperand {
kind: PatternOperandKind::Predicate,
vreg: None,
immediate: None,
reg_name: Some("p1".into()),
reg_class: Some(RegClassKind::Predicate),
},
PatternOperand {
kind: PatternOperandKind::Use,
vreg: Some(1),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
PatternOperand {
kind: PatternOperandKind::Use,
vreg: Some(2),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
],
has_def: true,
is_terminator: false,
is_branch: false,
}],
x86_sequence: vec![
PatternInstruction {
mnemonic: "cmpl".into(),
operands: vec![
PatternOperand {
kind: PatternOperandKind::Use,
vreg: Some(2),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
PatternOperand {
kind: PatternOperandKind::Use,
vreg: Some(1),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
],
has_def: false,
is_terminator: false,
is_branch: false,
},
PatternInstruction {
mnemonic: "sete".into(),
operands: vec![PatternOperand {
kind: PatternOperandKind::Def,
vreg: Some(0),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
}],
has_def: true,
is_terminator: false,
is_branch: false,
},
],
is_identical: false,
hexagon_cost: 1,
x86_cost: 2,
vliw_slot: Some(0),
can_predicate: false,
},
);
}
fn add_mem(&mut self) {
for (op, h, x) in &[
(50u32, "r0 = memw", "movl"),
(51, "r0 = memb", "movsbl"),
(52, "r0 = memh", "movswl"),
] {
self.pattern_cache.insert(
*op,
SharedPattern {
opcode: *op,
hexagon_sequence: vec![PatternInstruction {
mnemonic: h.to_string(),
operands: vec![
PatternOperand {
kind: PatternOperandKind::Def,
vreg: Some(0),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
PatternOperand {
kind: PatternOperandKind::Memory,
vreg: Some(1),
immediate: Some(0),
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
],
has_def: true,
is_terminator: false,
is_branch: false,
}],
x86_sequence: vec![PatternInstruction {
mnemonic: x.to_string(),
operands: vec![
PatternOperand {
kind: PatternOperandKind::Memory,
vreg: Some(1),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
PatternOperand {
kind: PatternOperandKind::Def,
vreg: Some(0),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
],
has_def: true,
is_terminator: false,
is_branch: false,
}],
is_identical: false,
hexagon_cost: 1,
x86_cost: 1,
vliw_slot: Some(0),
can_predicate: true,
},
);
}
self.pattern_cache.insert(
60,
SharedPattern {
opcode: 60,
hexagon_sequence: vec![PatternInstruction {
mnemonic: "memw".into(),
operands: vec![
PatternOperand {
kind: PatternOperandKind::Memory,
vreg: Some(1),
immediate: Some(0),
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
PatternOperand {
kind: PatternOperandKind::Use,
vreg: Some(0),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
],
has_def: false,
is_terminator: false,
is_branch: false,
}],
x86_sequence: vec![PatternInstruction {
mnemonic: "movl".into(),
operands: vec![
PatternOperand {
kind: PatternOperandKind::Use,
vreg: Some(0),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
PatternOperand {
kind: PatternOperandKind::Memory,
vreg: Some(1),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
],
has_def: false,
is_terminator: false,
is_branch: false,
}],
is_identical: false,
hexagon_cost: 1,
x86_cost: 1,
vliw_slot: Some(0),
can_predicate: true,
},
);
}
fn add_branch(&mut self) {
for (op, h, x) in &[(80u32, "jump", "jmp"), (82, "call", "call")] {
self.pattern_cache.insert(
*op,
SharedPattern {
opcode: *op,
hexagon_sequence: vec![PatternInstruction {
mnemonic: h.to_string(),
operands: vec![PatternOperand {
kind: PatternOperandKind::BranchTarget,
vreg: None,
immediate: None,
reg_name: None,
reg_class: None,
}],
has_def: false,
is_terminator: true,
is_branch: true,
}],
x86_sequence: vec![PatternInstruction {
mnemonic: x.to_string(),
operands: vec![PatternOperand {
kind: PatternOperandKind::BranchTarget,
vreg: None,
immediate: None,
reg_name: None,
reg_class: None,
}],
has_def: false,
is_terminator: true,
is_branch: true,
}],
is_identical: false,
hexagon_cost: 1,
x86_cost: 1,
vliw_slot: Some(3),
can_predicate: false,
},
);
}
self.pattern_cache.insert(
81,
SharedPattern {
opcode: 81,
hexagon_sequence: vec![PatternInstruction {
mnemonic: "jumpr".into(),
operands: vec![PatternOperand {
kind: PatternOperandKind::Use,
vreg: None,
immediate: None,
reg_name: Some("r31".into()),
reg_class: Some(RegClassKind::GPR32),
}],
has_def: false,
is_terminator: true,
is_branch: true,
}],
x86_sequence: vec![PatternInstruction {
mnemonic: "ret".into(),
operands: vec![],
has_def: false,
is_terminator: true,
is_branch: true,
}],
is_identical: false,
hexagon_cost: 1,
x86_cost: 1,
vliw_slot: Some(3),
can_predicate: false,
},
);
self.pattern_cache.insert(
90,
SharedPattern {
opcode: 90,
hexagon_sequence: vec![PatternInstruction {
mnemonic: "if (p1) jump".into(),
operands: vec![
PatternOperand {
kind: PatternOperandKind::Predicate,
vreg: None,
immediate: None,
reg_name: Some("p1".into()),
reg_class: Some(RegClassKind::Predicate),
},
PatternOperand {
kind: PatternOperandKind::BranchTarget,
vreg: None,
immediate: None,
reg_name: None,
reg_class: None,
},
],
has_def: false,
is_terminator: true,
is_branch: true,
}],
x86_sequence: vec![PatternInstruction {
mnemonic: "jg".into(),
operands: vec![PatternOperand {
kind: PatternOperandKind::BranchTarget,
vreg: None,
immediate: None,
reg_name: None,
reg_class: None,
}],
has_def: false,
is_terminator: true,
is_branch: true,
}],
is_identical: false,
hexagon_cost: 1,
x86_cost: 1,
vliw_slot: Some(3),
can_predicate: false,
},
);
if self.has_hw_loops {
self.pattern_cache.insert(
85,
SharedPattern {
opcode: 85,
hexagon_sequence: vec![PatternInstruction {
mnemonic: "loop0".into(),
operands: vec![
PatternOperand {
kind: PatternOperandKind::BranchTarget,
vreg: None,
immediate: None,
reg_name: None,
reg_class: None,
},
PatternOperand {
kind: PatternOperandKind::Use,
vreg: Some(0),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
],
has_def: false,
is_terminator: false,
is_branch: false,
}],
x86_sequence: vec![PatternInstruction {
mnemonic: "loop_body".into(),
operands: vec![PatternOperand {
kind: PatternOperandKind::Immediate,
vreg: None,
immediate: Some(0),
reg_name: None,
reg_class: None,
}],
has_def: false,
is_terminator: false,
is_branch: false,
}],
is_identical: false,
hexagon_cost: 0,
x86_cost: 1,
vliw_slot: Some(3),
can_predicate: false,
},
);
}
}
fn add_vector(&mut self) {
for (op, h, x, cost) in &[
(120u32, "v0 = vadd", "vpaddd", 2u32),
(121, "v0 = vsub", "vpsubd", 2),
(122, "v0 = vmpy", "vpmulld", 4),
(130, "v0 = vand", "vpand", 1),
(131, "v0 = vor", "vpor", 1),
(132, "v0 = vxor", "vpxor", 1),
] {
self.pattern_cache.insert(
*op,
SharedPattern {
opcode: *op,
hexagon_sequence: vec![PatternInstruction {
mnemonic: h.to_string(),
operands: vec![
PatternOperand {
kind: PatternOperandKind::Def,
vreg: None,
immediate: None,
reg_name: Some("v0".into()),
reg_class: Some(RegClassKind::HVX128),
},
PatternOperand {
kind: PatternOperandKind::Use,
vreg: None,
immediate: None,
reg_name: Some("v1".into()),
reg_class: Some(RegClassKind::HVX128),
},
PatternOperand {
kind: PatternOperandKind::Use,
vreg: None,
immediate: None,
reg_name: Some("v2".into()),
reg_class: Some(RegClassKind::HVX128),
},
],
has_def: true,
is_terminator: false,
is_branch: false,
}],
x86_sequence: vec![PatternInstruction {
mnemonic: x.to_string(),
operands: vec![
PatternOperand {
kind: PatternOperandKind::Def,
vreg: None,
immediate: None,
reg_name: Some("xmm0".into()),
reg_class: Some(RegClassKind::XMM),
},
PatternOperand {
kind: PatternOperandKind::Use,
vreg: None,
immediate: None,
reg_name: Some("xmm1".into()),
reg_class: Some(RegClassKind::XMM),
},
PatternOperand {
kind: PatternOperandKind::Use,
vreg: None,
immediate: None,
reg_name: Some("xmm2".into()),
reg_class: Some(RegClassKind::XMM),
},
],
has_def: true,
is_terminator: false,
is_branch: false,
}],
is_identical: false,
hexagon_cost: *cost,
x86_cost: 1,
vliw_slot: Some(0),
can_predicate: false,
},
);
}
}
fn add_pseudo(&mut self) {
self.pattern_cache.insert(
140,
SharedPattern {
opcode: 140,
hexagon_sequence: vec![PatternInstruction {
mnemonic: "r0 = r1".into(),
operands: vec![
PatternOperand {
kind: PatternOperandKind::Def,
vreg: Some(0),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
PatternOperand {
kind: PatternOperandKind::Use,
vreg: Some(1),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
],
has_def: true,
is_terminator: false,
is_branch: false,
}],
x86_sequence: vec![PatternInstruction {
mnemonic: "movl".into(),
operands: vec![
PatternOperand {
kind: PatternOperandKind::Use,
vreg: Some(1),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
PatternOperand {
kind: PatternOperandKind::Def,
vreg: Some(0),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
],
has_def: true,
is_terminator: false,
is_branch: false,
}],
is_identical: false,
hexagon_cost: 1,
x86_cost: 1,
vliw_slot: Some(0),
can_predicate: true,
},
);
self.pattern_cache.insert(
141,
SharedPattern {
opcode: 141,
hexagon_sequence: vec![PatternInstruction {
mnemonic: "r0 = #imm".into(),
operands: vec![
PatternOperand {
kind: PatternOperandKind::Def,
vreg: Some(0),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
PatternOperand {
kind: PatternOperandKind::Immediate,
vreg: None,
immediate: Some(0),
reg_name: None,
reg_class: None,
},
],
has_def: true,
is_terminator: false,
is_branch: false,
}],
x86_sequence: vec![PatternInstruction {
mnemonic: "movl".into(),
operands: vec![
PatternOperand {
kind: PatternOperandKind::Immediate,
vreg: None,
immediate: Some(0),
reg_name: None,
reg_class: None,
},
PatternOperand {
kind: PatternOperandKind::Def,
vreg: Some(0),
immediate: None,
reg_name: None,
reg_class: Some(RegClassKind::GPR32),
},
],
has_def: true,
is_terminator: false,
is_branch: false,
}],
is_identical: false,
hexagon_cost: 1,
x86_cost: 1,
vliw_slot: Some(0),
can_predicate: true,
},
);
self.pattern_cache.insert(
142,
SharedPattern {
opcode: 142,
hexagon_sequence: vec![PatternInstruction {
mnemonic: "nop".into(),
operands: vec![],
has_def: false,
is_terminator: false,
is_branch: false,
}],
x86_sequence: vec![PatternInstruction {
mnemonic: "nop".into(),
operands: vec![],
has_def: false,
is_terminator: false,
is_branch: false,
}],
is_identical: true,
hexagon_cost: 0,
x86_cost: 0,
vliw_slot: Some(0),
can_predicate: false,
},
);
}
pub fn with_features(triple: &str, features: &[&str]) -> Self {
let mut bridge = Self::new(triple);
for feat in features {
match *feat {
"hvx" => bridge.has_hvx = true,
"hwloops" => bridge.has_hw_loops = true,
"sse42" | "sse4.2" => bridge.has_sse42 = true,
"avx2" => bridge.has_avx2 = true,
"avx512" => bridge.has_avx512 = true,
_ => {}
}
}
bridge
}
pub fn set_opt_level(&mut self, level: CodeGenOptLevel) {
self.opt_level = level;
}
pub fn set_code_model(&mut self, model: CodeModel) {
self.code_model = model;
}
pub fn calculate_frame(&mut self, _local: u32, num_cs: u32, has_fp: bool, is_leaf: bool) {
self.frame_info.has_frame_pointer = has_fp;
self.frame_info.is_leaf = is_leaf;
self.frame_info.num_callee_saved_gprs = num_cs;
self.frame_info.compute_frame_layout();
}
pub fn allocate_vreg(&mut self, vreg: u32, class: RegClassKind) -> AllocatedReg {
let (hex_reg, x86_reg) = match class {
RegClassKind::GPR32 | RegClassKind::GPR64 => {
let hr = 7000 + (vreg % 28) as u16;
let xn = hexagon_gpr_to_x86(hr).unwrap_or("rax").to_string();
(hr, xn)
}
RegClassKind::HVX64 | RegClassKind::HVX128 => {
let vr = 7040 + (vreg % 16) as u16;
let xn = hexagon_vec_to_x86(vr).unwrap_or("xmm0").to_string();
(vr, xn)
}
_ => (0, format!("xmm{}", vreg % 16)),
};
let alloc = AllocatedReg {
vreg,
hexagon_reg: hex_reg,
x86_reg,
is_spill: false,
spill_slot: None,
reg_class: class,
};
self.vreg_allocations.insert(vreg, alloc.clone());
alloc
}
pub fn find_pattern(&self, opcode: u32) -> Option<&SharedPattern> {
self.pattern_cache.get(&opcode)
}
pub fn generate_hexagon_code(&self, ir: &[CrossTargetIRInst]) -> String {
let mut c = self.frame_info.emit_hexagon_prologue();
for inst in ir {
if let Some(p) = self.pattern_cache.get(&inst.opcode) {
if let Some(i) = p.hexagon_sequence.first() {
c.push_str(&format!("\t{}\n", i.mnemonic));
}
} else {
c.push_str(&format!("\t// Unlowered IR opcode {}\n", inst.opcode));
}
}
c.push_str(&self.frame_info.emit_hexagon_epilogue());
c
}
pub fn generate_x86_code(&self, ir: &[CrossTargetIRInst]) -> String {
let mut c = self.frame_info.emit_x86_prologue();
for inst in ir {
if let Some(p) = self.pattern_cache.get(&inst.opcode) {
if let Some(i) = p.x86_sequence.first() {
c.push_str(&format!("\t{}\n", i.mnemonic));
}
} else {
c.push_str(&format!("\t// Unlowered IR opcode {}\n", inst.opcode));
}
}
c.push_str(&self.frame_info.emit_x86_epilogue());
c
}
pub fn generate_cross_target(&self, ir: &[CrossTargetIRInst]) -> (String, String) {
(self.generate_hexagon_code(ir), self.generate_x86_code(ir))
}
pub fn compare_costs(&self, ir: &[CrossTargetIRInst]) -> CostComparison {
let mut ht = 0u32;
let mut xt = 0u32;
let mut hu = 0;
let mut xu = 0;
for inst in ir {
if let Some(p) = self.pattern_cache.get(&inst.opcode) {
ht += p.hexagon_cost;
xt += p.x86_cost;
} else {
hu += 1;
xu += 1;
}
}
let winner = if hu > 0 && xu > 0 {
CostWinner::Tie
} else if ht < xt {
CostWinner::Hexagon
} else if xt < ht {
CostWinner::X86
} else {
CostWinner::Tie
};
let n = ir.len();
CostComparison {
hexagon_total_cost: ht,
x86_total_cost: xt,
hexagon_unsupported_count: hu,
x86_unsupported_count: xu,
winner,
instruction_count: n,
average_hexagon_cost: if n == 0 { 0.0 } else { ht as f64 / n as f64 },
average_x86_cost: if n == 0 { 0.0 } else { xt as f64 / n as f64 },
}
}
pub fn add_pattern(&mut self, opcode: u32, pattern: SharedPattern) {
self.pattern_cache.insert(opcode, pattern);
}
pub fn pattern_count(&self) -> usize {
self.pattern_cache.len()
}
pub fn reset(&mut self) {
self.vreg_allocations.clear();
self.spill_slots.clear();
self.vliw_scheduler =
VLIWScheduler::new(self.enable_vliw_packets, self.hexagon_arch.issue_width());
self.frame_info = BridgeFrameInfo::new();
self.cost_data.clear();
}
pub fn dump_vliw_schedule(&self) -> String {
let mut o = format!(
"VLIW packets: {} total, {} instructions, utilization: {:.2}%\n",
self.vliw_scheduler.packet_count(),
self.vliw_scheduler.instruction_count(),
self.vliw_scheduler.utilization() * 100.0
);
for (i, pkt) in self.vliw_scheduler.packets.iter().enumerate() {
o.push_str(&format!(
" Packet {} ({} slots, branch: {}, endloop: {}):\n",
i,
pkt.slots.len(),
pkt.has_branch,
pkt.is_endloop
));
for s in &pkt.slots {
if s.predicated {
let neg = if s.predicate_inverted { "!" } else { "" };
o.push_str(&format!(
" [slot{}] if ({}p{}) {}\n",
s.slot,
neg,
s.predicate_reg
.map(|r| format!("{}", r - HEXAGON_PRED_BASE))
.unwrap_or_else(|| "?".into()),
s.mnemonic
));
} else {
o.push_str(&format!(" [slot{}] {}\n", s.slot, s.mnemonic));
}
}
}
o
}
}
#[derive(Debug, Clone)]
pub struct CostComparison {
pub hexagon_total_cost: u32,
pub x86_total_cost: u32,
pub hexagon_unsupported_count: usize,
pub x86_unsupported_count: usize,
pub winner: CostWinner,
pub instruction_count: usize,
pub average_hexagon_cost: f64,
pub average_x86_cost: f64,
}
impl fmt::Display for CostComparison {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Hexagon ↔ X86 Cost Comparison:")?;
writeln!(
f,
" Instruction count: {}",
self.instruction_count
)?;
writeln!(
f,
" Hexagon total cost: {}",
self.hexagon_total_cost
)?;
writeln!(f, " X86 total cost: {}", self.x86_total_cost)?;
writeln!(
f,
" Hexagon avg cost: {:.2}",
self.average_hexagon_cost
)?;
writeln!(
f,
" X86 avg cost: {:.2}",
self.average_x86_cost
)?;
writeln!(
f,
" Hexagon unsupported: {}",
self.hexagon_unsupported_count
)?;
writeln!(
f,
" X86 unsupported: {}",
self.x86_unsupported_count
)?;
writeln!(f, " Winner: {}", self.winner)
}
}
#[derive(Debug, Clone)]
pub struct MicroarchitectureCosts {
pub hexagon_microarch: String,
pub x86_microarch: String,
pub hexagon_cycles: HashMap<u32, u32>,
pub x86_cycles: HashMap<u32, u32>,
pub hexagon_sizes: HashMap<u32, u32>,
pub x86_sizes: HashMap<u32, u32>,
}
impl Default for MicroarchitectureCosts {
fn default() -> Self {
let mut hc = HashMap::new();
let mut xc = HashMap::new();
let mut hs = HashMap::new();
let mut xs = HashMap::new();
for op in 0..5 {
hc.insert(op, 1);
xc.insert(op, 1);
hs.insert(op, 4);
xs.insert(op, 2);
}
for op in 10..13 {
hc.insert(op, 3);
xc.insert(op, 3);
hs.insert(op, 4);
xs.insert(op, 3);
}
for op in 20..25 {
hc.insert(op, 1);
xc.insert(op, 1);
hs.insert(op, 4);
xs.insert(op, 2);
}
for op in 50..56 {
hc.insert(op, 2);
xc.insert(op, 2);
hs.insert(op, 4);
xs.insert(op, 3);
}
for op in 60..65 {
hc.insert(op, 1);
xc.insert(op, 1);
hs.insert(op, 4);
xs.insert(op, 3);
}
for op in 80..90 {
hc.insert(op, 1);
xc.insert(op, 1);
hs.insert(op, 4);
xs.insert(op, 2);
}
for op in 120..140 {
hc.insert(op, 2);
xc.insert(op, 1);
hs.insert(op, 4);
xs.insert(op, 5);
}
MicroarchitectureCosts {
hexagon_microarch: "Hexagon V66".into(),
x86_microarch: "Intel Skylake".into(),
hexagon_cycles: hc,
x86_cycles: xc,
hexagon_sizes: hs,
x86_sizes: xs,
}
}
}
#[derive(Clone)]
pub struct HexagonX86TargetMachine {
pub bridge: HexagonX86Bridge,
pub triple: String,
pub data_layout: String,
pub options: TargetOptions,
}
impl HexagonX86TargetMachine {
pub fn new(triple: &str) -> Self {
let bridge = HexagonX86Bridge::new(triple);
let dl = bridge.hexagon_arch.data_layout();
Self {
bridge,
triple: triple.into(),
data_layout: dl.into(),
options: TargetOptions::default(),
}
}
pub fn with_features(triple: &str, features: &[&str]) -> Self {
let bridge = HexagonX86Bridge::with_features(triple, features);
let dl = bridge.hexagon_arch.data_layout();
Self {
bridge,
triple: triple.into(),
data_layout: dl.into(),
options: TargetOptions::default(),
}
}
pub fn get_arch(&self) -> HexagonArch {
self.bridge.hexagon_arch
}
pub fn has_hvx(&self) -> bool {
self.bridge.has_hvx
}
pub fn has_hw_loops(&self) -> bool {
self.bridge.has_hw_loops
}
pub fn generate_hexagon(&self, ir: &[CrossTargetIRInst]) -> String {
self.bridge.generate_hexagon_code(ir)
}
pub fn generate_x86(&self, ir: &[CrossTargetIRInst]) -> String {
self.bridge.generate_x86_code(ir)
}
pub fn compare_costs(&self, ir: &[CrossTargetIRInst]) -> CostComparison {
self.bridge.compare_costs(ir)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_v60() {
let b = HexagonX86Bridge::new("hexagonv60-unknown-linux");
assert!(b.is_hexagon_primary);
assert_eq!(b.hexagon_arch, HexagonArch::V60);
assert!(b.pattern_count() >= 20);
}
#[test]
fn test_parse_all() {
for (s, e) in &[
("hexagonv5", HexagonArch::V5),
("hexagonv60", HexagonArch::V60),
("hexagonv62", HexagonArch::V62),
("hexagonv65", HexagonArch::V65),
("hexagonv66", HexagonArch::V66),
("hexagonv67", HexagonArch::V67),
("hexagonv68", HexagonArch::V68),
("hexagonv69", HexagonArch::V69),
] {
assert_eq!(HexagonArch::parse(s).unwrap(), *e);
}
}
#[test]
fn test_parse_none() {
assert!(HexagonArch::parse("armv7").is_none());
}
#[test]
fn test_has_hvx() {
assert!(!HexagonArch::V60.has_hvx());
assert!(HexagonArch::V65.has_hvx());
}
#[test]
fn test_has_hw_loops() {
assert!(!HexagonArch::V5.has_hw_loops());
assert!(HexagonArch::V60.has_hw_loops());
}
#[test]
fn test_issue_width() {
assert_eq!(HexagonArch::V5.issue_width(), 2);
assert_eq!(HexagonArch::V69.issue_width(), 4);
}
#[test]
fn test_gpr_to_x86() {
assert_eq!(hexagon_gpr_to_x86(7000), Some("rax"));
assert_eq!(hexagon_gpr_to_x86(7029), Some("rsp"));
assert_eq!(hexagon_gpr_to_x86(7030), Some("rbp"));
assert_eq!(hexagon_gpr_to_x86(7031), None);
assert_eq!(hexagon_gpr_to_x86(8000), None);
}
#[test]
fn test_x86_to_hex() {
assert_eq!(x86_gpr_to_hexagon("rax"), Some(7000));
assert_eq!(x86_gpr_to_hexagon("rsp"), Some(7029));
assert_eq!(x86_gpr_to_hexagon("r12"), Some(7017));
assert_eq!(x86_gpr_to_hexagon("rip"), None);
}
#[test]
fn test_pred_to_cc() {
assert_eq!(hexagon_pred_to_x86_cc(7032), Some("always"));
assert_eq!(hexagon_pred_to_x86_cc(7035), Some("never"));
assert_eq!(hexagon_pred_to_x86_cc(7099), None);
}
#[test]
fn test_vec_to_x86() {
assert_eq!(hexagon_vec_to_x86(7040), Some("xmm0"));
assert_eq!(hexagon_vec_to_x86(7055), Some("xmm15"));
assert_eq!(hexagon_vec_to_x86(7039), None);
}
#[test]
fn test_reg_maps() {
let b = HexagonX86Bridge::new("hexagonv65-unknown-linux");
assert!(!b.reg_map_hex_to_x86.is_empty());
assert!(b.reg_map_hex_to_x86.contains_key(&7029));
assert!(b.reg_map_x86_to_hex.contains_key("rsp"));
}
#[test]
fn test_abi_num_args() {
assert_eq!(HexagonAbi::SVR4.num_int_arg_regs(), 4);
assert_eq!(HexagonAbi::SVR4HVX.num_hvx_arg_regs(), 8);
assert_eq!(HexagonAbi::BareMetal.num_hvx_arg_regs(), 0);
}
#[test]
fn test_abi_uses_hvx() {
assert!(!HexagonAbi::SVR4.uses_hvx_cc());
assert!(HexagonAbi::SVR4HVX.uses_hvx_cc());
}
#[test]
fn test_cc_construction() {
let cc =
BridgeCallingConvention::new(HexagonAbi::SVR4, X86CallingConventionVariant::SysV64);
assert_eq!(cc.hexagon_int_arg_regs.len(), 4);
assert_eq!(cc.x86_int_arg_regs.len(), 6);
assert_eq!(cc.hexagon_callee_saved_gprs.len(), 11);
assert_eq!(cc.x86_callee_saved_gprs.len(), 6);
}
#[test]
fn test_hvx_cc() {
let cc =
BridgeCallingConvention::new(HexagonAbi::SVR4HVX, X86CallingConventionVariant::SysV64);
assert_eq!(cc.hexagon_hvx_arg_regs.len(), 8);
assert_eq!(cc.hexagon_hvx_ret_regs.len(), 2);
}
#[test]
fn test_classify() {
let cc =
BridgeCallingConvention::new(HexagonAbi::SVR4, X86CallingConventionVariant::SysV64);
assert_eq!(cc.classify_hexagon_type(4, 4), ArgClass::Integer);
assert_eq!(cc.classify_hexagon_type(8, 8), ArgClass::IntegerPair);
}
#[test]
fn test_hvx_classify() {
let cc =
BridgeCallingConvention::new(HexagonAbi::SVR4HVX, X86CallingConventionVariant::SysV64);
assert_eq!(cc.classify_hexagon_type(128, 128), ArgClass::HVXVector);
}
#[test]
fn test_frame_default() {
let fi = BridgeFrameInfo::new();
assert_eq!(fi.frame_size, 0);
assert!(fi.has_frame_pointer);
}
#[test]
fn test_frame_layout() {
let mut fi = BridgeFrameInfo::new();
fi.num_callee_saved_gprs = 4;
fi.max_call_frame_size = 32;
let s = fi.compute_frame_layout();
assert!(s >= 48);
assert_eq!(s % 8, 0);
}
#[test]
fn test_hex_prologue() {
let fi = BridgeFrameInfo::new();
assert!(fi.emit_hexagon_prologue().contains("allocframe"));
}
#[test]
fn test_hex_epilogue() {
assert!(BridgeFrameInfo::new()
.emit_hexagon_epilogue()
.contains("jumpr r31"));
}
#[test]
fn test_x86_prologue() {
assert!(BridgeFrameInfo::new().emit_x86_prologue().contains("pushq"));
}
#[test]
fn test_x86_epilogue() {
assert!(BridgeFrameInfo::new().emit_x86_epilogue().contains("retq"));
}
#[test]
fn test_align() {
assert_eq!(align_up(0, 8), 0);
assert_eq!(align_up(1, 8), 8);
assert_eq!(align_up(9, 8), 16);
}
#[test]
fn test_vliw_new() {
let s = VLIWScheduler::new(true, 4);
assert!(s.enabled);
assert_eq!(s.max_slots, 4);
}
#[test]
fn test_vliw_emit() {
let mut s = VLIWScheduler::new(true, 4);
assert!(s.try_emit(VLIWSlot {
opcode: HexagonOpcode::ADD,
slot: 0,
dest: Some(7000),
srcs: vec![7001, 7002],
immediate: None,
mnemonic: "add".into(),
predicated: false,
predicate_reg: None,
predicate_inverted: false
}));
assert_eq!(s.current_packet.len(), 1);
}
#[test]
fn test_vliw_both_alus() {
let mut s = VLIWScheduler::new(true, 4);
s.try_emit(VLIWSlot {
opcode: HexagonOpcode::ADD,
slot: 0,
dest: Some(7000),
srcs: vec![7001, 7002],
immediate: None,
mnemonic: "a".into(),
predicated: false,
predicate_reg: None,
predicate_inverted: false,
});
s.try_emit(VLIWSlot {
opcode: HexagonOpcode::SUB,
slot: 1,
dest: Some(7003),
srcs: vec![7004, 7005],
immediate: None,
mnemonic: "s".into(),
predicated: false,
predicate_reg: None,
predicate_inverted: false,
});
assert!(!s.try_emit(VLIWSlot {
opcode: HexagonOpcode::XOR,
slot: 2,
dest: Some(7006),
srcs: vec![7007, 7008],
immediate: None,
mnemonic: "x".into(),
predicated: false,
predicate_reg: None,
predicate_inverted: false
}));
}
#[test]
fn test_vliw_finish() {
let mut s = VLIWScheduler::new(true, 4);
s.try_emit(VLIWSlot {
opcode: HexagonOpcode::ADD,
slot: 0,
dest: Some(7000),
srcs: vec![7001, 7002],
immediate: None,
mnemonic: "a".into(),
predicated: false,
predicate_reg: None,
predicate_inverted: false,
});
s.finish_packet(0x1000);
assert_eq!(s.packet_count(), 1);
assert!(s.current_packet.is_empty());
}
#[test]
fn test_vliw_mem_conflict() {
let mut s = VLIWScheduler::new(true, 4);
assert!(s.try_emit(VLIWSlot {
opcode: HexagonOpcode::LDW,
slot: 0,
dest: Some(7000),
srcs: vec![7001],
immediate: None,
mnemonic: "ld".into(),
predicated: false,
predicate_reg: None,
predicate_inverted: false
}));
assert!(!s.try_emit(VLIWSlot {
opcode: HexagonOpcode::STW,
slot: 1,
dest: None,
srcs: vec![7002, 7003],
immediate: None,
mnemonic: "st".into(),
predicated: false,
predicate_reg: None,
predicate_inverted: false
}));
}
#[test]
fn test_find_add() {
let b = HexagonX86Bridge::new("hexagonv60-unknown-linux");
assert!(b.find_pattern(0).is_some());
}
#[test]
fn test_find_unknown() {
let b = HexagonX86Bridge::new("hexagonv60-unknown-linux");
assert!(b.find_pattern(999).is_none());
}
#[test]
fn test_gen_hex() {
let b = HexagonX86Bridge::new("hexagonv60-unknown-linux");
let c = b.generate_hexagon_code(&[CrossTargetIRInst {
opcode: 0,
dest: Some(0),
srcs: vec![1, 2],
immediate: None,
type_code: 0,
is_terminator: false,
mem_addr: None,
branch_target: None,
condition: None,
}]);
assert!(c.contains("jumpr r31"));
}
#[test]
fn test_gen_x86() {
let b = HexagonX86Bridge::new("hexagonv60-unknown-linux");
let c = b.generate_x86_code(&[CrossTargetIRInst {
opcode: 0,
dest: Some(0),
srcs: vec![1, 2],
immediate: None,
type_code: 0,
is_terminator: false,
mem_addr: None,
branch_target: None,
condition: None,
}]);
assert!(c.contains("retq"));
}
#[test]
fn test_cross() {
let b = HexagonX86Bridge::new("hexagonv60-unknown-linux");
let (h, x) = b.generate_cross_target(&[CrossTargetIRInst {
opcode: 0,
dest: Some(0),
srcs: vec![1, 2],
immediate: None,
type_code: 0,
is_terminator: false,
mem_addr: None,
branch_target: None,
condition: None,
}]);
assert!(!h.is_empty());
assert!(!x.is_empty());
}
#[test]
fn test_compare() {
let b = HexagonX86Bridge::new("hexagonv60-unknown-linux");
let ir = vec![
CrossTargetIRInst {
opcode: 0,
dest: Some(0),
srcs: vec![1, 2],
immediate: None,
type_code: 0,
is_terminator: false,
mem_addr: None,
branch_target: None,
condition: None,
},
CrossTargetIRInst {
opcode: 10,
dest: Some(3),
srcs: vec![0, 4],
immediate: None,
type_code: 0,
is_terminator: false,
mem_addr: None,
branch_target: None,
condition: None,
},
];
let c = b.compare_costs(&ir);
assert_eq!(c.instruction_count, 2);
assert!(c.hexagon_total_cost > 0);
}
#[test]
fn test_empty_compare() {
let c = HexagonX86Bridge::new("hexagonv60-unknown-linux").compare_costs(&[]);
assert_eq!(c.instruction_count, 0);
}
#[test]
fn test_winner_display() {
assert_eq!(format!("{}", CostWinner::Hexagon), "Hexagon");
}
#[test]
fn test_cost_display() {
let c = CostComparison {
hexagon_total_cost: 10,
x86_total_cost: 8,
hexagon_unsupported_count: 0,
x86_unsupported_count: 0,
winner: CostWinner::X86,
instruction_count: 4,
average_hexagon_cost: 2.5,
average_x86_cost: 2.0,
};
assert!(format!("{}", c).contains("Winner: X86"));
}
#[test]
fn test_tm() {
let tm = HexagonX86TargetMachine::new("hexagonv5-unknown-linux");
assert_eq!(tm.get_arch(), HexagonArch::V5);
assert!(!tm.has_hvx());
}
#[test]
fn test_tm_hvx() {
assert!(HexagonX86TargetMachine::new("hexagonv65-unknown-linux").has_hvx());
}
#[test]
fn test_tm_features() {
let tm =
HexagonX86TargetMachine::with_features("hexagonv60-unknown-linux", &["hvx", "avx2"]);
assert!(tm.bridge.has_hvx);
assert!(tm.bridge.has_avx2);
}
#[test]
fn test_rc_sizes() {
assert_eq!(RegClassKind::GPR32.size_bytes(), 4);
assert_eq!(RegClassKind::HVX128.size_bytes(), 128);
assert_eq!(RegClassKind::XMM.size_bytes(), 16);
assert_eq!(RegClassKind::Flag.size_bytes(), 0);
}
#[test]
fn test_rc_is() {
assert!(RegClassKind::GPR32.is_integer());
assert!(RegClassKind::HVX128.is_vector());
assert!(RegClassKind::GPR32.is_hexagon());
assert!(!RegClassKind::XMM.is_hexagon());
}
#[test]
fn test_alloc_vreg() {
let mut b = HexagonX86Bridge::new("hexagonv60-unknown-linux");
let a = b.allocate_vreg(5, RegClassKind::GPR32);
assert_eq!(a.vreg, 5);
assert!(!a.is_spill);
assert!(a.hexagon_reg >= 7000 && a.hexagon_reg <= 7031);
}
#[test]
fn test_alloc_hvx() {
let mut b = HexagonX86Bridge::new("hexagonv65-unknown-linux");
let a = b.allocate_vreg(3, RegClassKind::HVX128);
assert!(a.hexagon_reg >= 7040 && a.hexagon_reg <= 7071);
assert!(a.x86_reg.starts_with("xmm"));
}
#[test]
fn test_microarch() {
let m = MicroarchitectureCosts::default();
assert!(m.hexagon_cycles.len() > 20);
assert!(m.x86_cycles.len() > 20);
}
#[test]
fn test_reset() {
let mut b = HexagonX86Bridge::new("hexagonv60-unknown-linux");
b.allocate_vreg(1, RegClassKind::GPR32);
b.reset();
assert!(b.vreg_allocations.is_empty());
assert_eq!(b.vliw_scheduler.packet_count(), 0);
}
#[test]
fn test_mir() {
let inst = CrossTargetIRInst {
opcode: 42,
dest: None,
srcs: vec![],
immediate: None,
type_code: 0,
is_terminator: false,
mem_addr: None,
branch_target: None,
condition: None,
};
assert_eq!(inst.opcode, 42);
assert!(!inst.is_terminator);
}
#[test]
fn test_mbb() {
let m = CrossTargetMBB {
label: "e".into(),
instructions: vec![],
successors: vec!["x".into()],
predecessors: vec![],
is_entry: true,
has_fallthrough: true,
};
assert!(m.is_entry);
assert_eq!(m.successors.len(), 1);
}
#[test]
fn test_clone() {
let b = HexagonX86Bridge::new("hexagonv60-unknown-linux");
let c = b.clone();
assert_eq!(b.triple, c.triple);
assert_eq!(b.hexagon_arch, c.hexagon_arch);
}
#[test]
fn test_add_pat() {
let mut b = HexagonX86Bridge::new("hexagonv60-unknown-linux");
let n = b.pattern_count();
b.add_pattern(
999,
SharedPattern {
opcode: 999,
hexagon_sequence: vec![],
x86_sequence: vec![],
is_identical: true,
hexagon_cost: 1,
x86_cost: 1,
vliw_slot: None,
can_predicate: false,
},
);
assert_eq!(b.pattern_count(), n + 1);
}
#[test]
fn test_hvx_more_patterns() {
assert!(
HexagonX86Bridge::new("hexagonv65-unknown-linux").pattern_count()
>= HexagonX86Bridge::new("hexagonv5-unknown-linux").pattern_count()
);
}
#[test]
fn test_dump_vliw() {
let mut s = VLIWScheduler::new(true, 4);
s.try_emit(VLIWSlot {
opcode: HexagonOpcode::ADD,
slot: 0,
dest: Some(7000),
srcs: vec![7001, 7002],
immediate: None,
mnemonic: "add".into(),
predicated: true,
predicate_reg: Some(7033),
predicate_inverted: false,
});
s.finish_packet(0x1000);
let mut b = HexagonX86Bridge::new("hexagonv60-unknown-linux");
b.vliw_scheduler = s;
let o = b.dump_vliw_schedule();
assert!(o.contains("Packet 0"));
assert!(o.contains("if (p1)"));
}
#[test]
fn test_arch_family() {
assert_eq!(
HexagonArchFamily::from(HexagonArch::V5),
HexagonArchFamily::V5Family
);
assert_eq!(
HexagonArchFamily::from(HexagonArch::V65),
HexagonArchFamily::V6HVX
);
}
#[test]
fn test_is_v6x() {
assert!(!HexagonArch::V5.is_v6x());
assert!(HexagonArch::V60.is_v6x());
}
#[test]
fn test_abis_display() {
assert_eq!(format!("{}", HexagonAbi::SVR4), "svr4");
assert_eq!(format!("{}", HexagonAbi::SVR4HVX), "svr4-hvx");
}
#[test]
fn test_x86cc_display() {
assert_eq!(format!("{}", X86CallingConventionVariant::SysV64), "sysv64");
}
#[test]
fn test_total_coverage() {
for a in &[
"hexagonv5",
"hexagonv60",
"hexagonv62",
"hexagonv65",
"hexagonv66",
"hexagonv67",
"hexagonv68",
"hexagonv69",
] {
assert!(!HexagonX86Bridge::new(&format!("{}-unknown-linux", a))
.triple
.is_empty());
}
}
}