#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PpcCpu {
Power4,
Power5,
Power5Plus,
Power6,
Power7,
Power8,
Power9,
Power10,
Power11,
Ppc440,
Ppc450,
Ppc460,
Ppc464,
Ppc465,
Ppc470,
Ppc476,
Ppc476Fp,
E500,
E500v2,
E500Mc,
E5500,
E6500,
Ppc601,
Ppc603,
Ppc603e,
Ppc604,
Ppc604e,
Ppc740,
Ppc7400,
Ppc7450,
Ppc750,
CellBE,
Generic,
}
impl PpcCpu {
pub fn from_name(name: &str) -> Option<PpcCpu> {
match name.to_lowercase().as_str() {
"power4" | "ppc970" | "970" => Some(PpcCpu::Power4),
"power5" | "ppc970fx" | "970fx" => Some(PpcCpu::Power5),
"power5+" | "ppc970mp" | "970mp" => Some(PpcCpu::Power5Plus),
"power6" | "ppc970gx" => Some(PpcCpu::Power6),
"power7" | "pwr7" => Some(PpcCpu::Power7),
"power8" | "pwr8" => Some(PpcCpu::Power8),
"power9" | "pwr9" => Some(PpcCpu::Power9),
"power10" | "pwr10" => Some(PpcCpu::Power10),
"power11" | "pwr11" => Some(PpcCpu::Power11),
"ppc440" | "440" => Some(PpcCpu::Ppc440),
"ppc450" | "450" => Some(PpcCpu::Ppc450),
"ppc460" | "460" => Some(PpcCpu::Ppc460),
"ppc464" | "464" => Some(PpcCpu::Ppc464),
"ppc465" | "465" => Some(PpcCpu::Ppc465),
"ppc470" | "470" => Some(PpcCpu::Ppc470),
"ppc476" | "476" => Some(PpcCpu::Ppc476),
"ppc476fp" | "476fp" => Some(PpcCpu::Ppc476Fp),
"e500" | "8540" | "8548" => Some(PpcCpu::E500),
"e500v2" | "8541" | "8555" => Some(PpcCpu::E500v2),
"e500mc" | "8547" | "8572" => Some(PpcCpu::E500Mc),
"e5500" => Some(PpcCpu::E5500),
"e6500" => Some(PpcCpu::E6500),
"601" | "ppc601" => Some(PpcCpu::Ppc601),
"603" | "ppc603" => Some(PpcCpu::Ppc603),
"603e" | "ppc603e" => Some(PpcCpu::Ppc603e),
"604" | "ppc604" => Some(PpcCpu::Ppc604),
"604e" | "ppc604e" => Some(PpcCpu::Ppc604e),
"740" | "7400" | "ppc7400" | "g4" => Some(PpcCpu::Ppc7400),
"7450" | "ppc7450" | "g4+" => Some(PpcCpu::Ppc7450),
"750" | "ppc750" | "g3" => Some(PpcCpu::Ppc750),
"cell" | "cellbe" | "cell/b.e." => Some(PpcCpu::CellBE),
"generic" | "ppc" | "powerpc" => Some(PpcCpu::Generic),
_ => None,
}
}
pub fn name(self) -> &'static str {
match self {
PpcCpu::Power4 => "power4",
PpcCpu::Power5 => "power5",
PpcCpu::Power5Plus => "power5+",
PpcCpu::Power6 => "power6",
PpcCpu::Power7 => "power7",
PpcCpu::Power8 => "power8",
PpcCpu::Power9 => "power9",
PpcCpu::Power10 => "power10",
PpcCpu::Power11 => "power11",
PpcCpu::Ppc440 => "ppc440",
PpcCpu::Ppc450 => "ppc450",
PpcCpu::Ppc460 => "ppc460",
PpcCpu::Ppc464 => "ppc464",
PpcCpu::Ppc465 => "ppc465",
PpcCpu::Ppc470 => "ppc470",
PpcCpu::Ppc476 => "ppc476",
PpcCpu::Ppc476Fp => "ppc476fp",
PpcCpu::E500 => "e500",
PpcCpu::E500v2 => "e500v2",
PpcCpu::E500Mc => "e500mc",
PpcCpu::E5500 => "e5500",
PpcCpu::E6500 => "e6500",
PpcCpu::Ppc601 => "ppc601",
PpcCpu::Ppc603 => "ppc603",
PpcCpu::Ppc603e => "ppc603e",
PpcCpu::Ppc604 => "ppc604",
PpcCpu::Ppc604e => "ppc604e",
PpcCpu::Ppc740 => "ppc740",
PpcCpu::Ppc7400 => "ppc7400",
PpcCpu::Ppc7450 => "ppc7450",
PpcCpu::Ppc750 => "ppc750",
PpcCpu::CellBE => "cellbe",
PpcCpu::Generic => "generic",
}
}
pub fn is_64bit(self) -> bool {
matches!(self,
PpcCpu::Power4 | PpcCpu::Power5 | PpcCpu::Power5Plus |
PpcCpu::Power6 | PpcCpu::Power7 | PpcCpu::Power8 |
PpcCpu::Power9 | PpcCpu::Power10 | PpcCpu::Power11 |
PpcCpu::E5500 | PpcCpu::E6500 | PpcCpu::Generic
)
}
pub fn is_embedded(self) -> bool {
matches!(self,
PpcCpu::Ppc440 | PpcCpu::Ppc450 | PpcCpu::Ppc460 |
PpcCpu::Ppc464 | PpcCpu::Ppc465 | PpcCpu::Ppc470 |
PpcCpu::Ppc476 | PpcCpu::Ppc476Fp |
PpcCpu::E500 | PpcCpu::E500v2 | PpcCpu::E500Mc |
PpcCpu::E5500 | PpcCpu::E6500
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PpcFeatureSet {
pub has_altivec: bool,
pub has_vsx: bool,
pub has_mma: bool,
pub has_dfp: bool,
pub has_fp: bool,
pub has_spe: bool,
pub has_vle: bool,
pub has_prefix: bool,
pub has_pcrelative: bool,
pub has_htm: bool, pub has_ldbrx: bool, pub has_icbt: bool, pub has_darn: bool, pub has_radix_mmu: bool, pub has_smt: bool,
pub smt_threads: u32,
pub isa_2_00: bool,
pub isa_2_01: bool,
pub isa_2_02: bool,
pub isa_2_03: bool,
pub isa_2_04: bool,
pub isa_2_05: bool,
pub isa_2_06: bool,
pub isa_2_07: bool,
pub isa_3_0: bool,
pub isa_3_1: bool,
pub isa_future: bool,
}
impl Default for PpcFeatureSet {
fn default() -> Self {
PpcFeatureSet {
has_altivec: false,
has_vsx: false,
has_mma: false,
has_dfp: false,
has_fp: true,
has_spe: false,
has_vle: false,
has_prefix: false,
has_pcrelative: false,
has_htm: false,
has_ldbrx: false,
has_icbt: false,
has_darn: false,
has_radix_mmu: false,
has_smt: false,
smt_threads: 1,
isa_2_00: false,
isa_2_01: false,
isa_2_02: false,
isa_2_03: false,
isa_2_04: false,
isa_2_05: false,
isa_2_06: false,
isa_2_07: true,
isa_3_0: false,
isa_3_1: false,
isa_future: false,
}
}
}
impl PpcFeatureSet {
pub fn for_cpu(cpu: PpcCpu) -> Self {
let mut fs = PpcFeatureSet::default();
match cpu {
PpcCpu::Power4 => {
fs.isa_2_00 = true;
fs.has_smt = true;
fs.smt_threads = 2;
}
PpcCpu::Power5 | PpcCpu::Power5Plus => {
fs.isa_2_02 = true;
fs.has_smt = true;
fs.smt_threads = 2;
}
PpcCpu::Power6 => {
fs.isa_2_05 = true;
fs.isa_2_04 = true;
fs.has_dfp = true;
fs.has_altivec = true;
fs.has_smt = true;
fs.smt_threads = 2;
}
PpcCpu::Power7 => {
fs.isa_2_06 = true;
fs.isa_2_05 = true;
fs.has_vsx = true;
fs.has_altivec = true;
fs.has_dfp = true;
fs.has_ldbrx = true;
fs.has_smt = true;
fs.smt_threads = 4;
}
PpcCpu::Power8 => {
fs.isa_2_07 = true;
fs.isa_2_06 = true;
fs.has_vsx = true;
fs.has_altivec = true;
fs.has_dfp = true;
fs.has_htm = true;
fs.has_ldbrx = true;
fs.has_icbt = true;
fs.has_smt = true;
fs.smt_threads = 8;
}
PpcCpu::Power9 => {
fs.isa_3_0 = true;
fs.isa_2_07 = true;
fs.has_vsx = true;
fs.has_altivec = true;
fs.has_dfp = true;
fs.has_darn = true;
fs.has_radix_mmu = true;
fs.has_htm = true;
fs.has_ldbrx = true;
fs.has_icbt = true;
fs.has_smt = true;
fs.smt_threads = 4;
}
PpcCpu::Power10 => {
fs.isa_3_1 = true;
fs.isa_3_0 = true;
fs.has_vsx = true;
fs.has_altivec = true;
fs.has_mma = true;
fs.has_prefix = true;
fs.has_pcrelative = true;
fs.has_dfp = true;
fs.has_darn = true;
fs.has_radix_mmu = true;
fs.has_htm = false; fs.has_ldbrx = true;
fs.has_icbt = true;
fs.has_smt = true;
fs.smt_threads = 4;
}
PpcCpu::Power11 => {
fs.isa_3_1 = true;
fs.isa_3_0 = true;
fs.isa_future = true;
fs.has_vsx = true;
fs.has_altivec = true;
fs.has_mma = true;
fs.has_prefix = true;
fs.has_pcrelative = true;
fs.has_dfp = true;
fs.has_darn = true;
fs.has_radix_mmu = true;
fs.has_smt = true;
fs.smt_threads = 4;
}
PpcCpu::Ppc440 | PpcCpu::Ppc450 | PpcCpu::Ppc460 |
PpcCpu::Ppc464 | PpcCpu::Ppc465 => {
fs.isa_2_03 = true;
fs.has_fp = false;
fs.has_vle = false;
}
PpcCpu::Ppc470 | PpcCpu::Ppc476 => {
fs.isa_2_03 = true;
fs.has_fp = true;
}
PpcCpu::Ppc476Fp => {
fs.isa_2_03 = true;
fs.has_fp = true;
}
PpcCpu::E500 | PpcCpu::E500v2 => {
fs.isa_2_03 = true;
fs.has_spe = true;
fs.has_fp = false;
}
PpcCpu::E500Mc => {
fs.isa_2_06 = true;
fs.has_fp = true;
}
PpcCpu::E5500 => {
fs.isa_2_06 = true;
fs.has_fp = true;
fs.has_vle = true;
}
PpcCpu::E6500 => {
fs.isa_2_07 = true;
fs.has_altivec = true;
fs.has_fp = true;
fs.has_vle = true;
}
PpcCpu::Ppc601 | PpcCpu::Ppc603 | PpcCpu::Ppc603e |
PpcCpu::Ppc604 | PpcCpu::Ppc604e | PpcCpu::Ppc740 |
PpcCpu::Ppc750 => {
fs.isa_2_03 = true;
}
PpcCpu::Ppc7400 | PpcCpu::Ppc7450 => {
fs.isa_2_03 = true;
fs.has_altivec = true;
}
PpcCpu::CellBE => {
fs.isa_2_06 = true;
fs.has_altivec = true;
}
PpcCpu::Generic => {
fs.isa_2_07 = true;
fs.has_vsx = true;
fs.has_altivec = true;
fs.has_dfp = true;
fs.has_fp = true;
fs.has_smt = true;
fs.smt_threads = 4;
}
}
fs.validate();
fs
}
pub fn validate(&mut self) {
if self.has_vsx {
self.has_altivec = true;
self.has_fp = true;
}
if self.has_mma {
self.has_vsx = true;
self.has_altivec = true;
}
if self.has_dfp {
self.has_fp = true;
}
if self.has_radix_mmu {
self.isa_3_0 = true;
}
if self.has_prefix {
self.isa_3_1 = true;
}
if self.has_pcrelative {
self.isa_3_1 = true;
}
}
pub fn with_isa(mut self, isa: &str) -> Self {
match isa {
"2.03" => self.isa_2_03 = true,
"2.04" => self.isa_2_04 = true,
"2.05" => self.isa_2_05 = true,
"2.06" => self.isa_2_06 = true,
"2.07" => self.isa_2_07 = true,
"3.0" => { self.isa_3_0 = true; self.isa_2_07 = true; }
"3.1" => {
self.isa_3_1 = true;
self.isa_3_0 = true;
self.isa_2_07 = true;
}
"future" => {
self.isa_future = true;
self.isa_3_1 = true;
self.isa_3_0 = true;
self.isa_2_07 = true;
}
_ => {}
}
self.validate();
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PpcAbi {
ElfV1,
ElfV2,
Aix,
Darwin,
ElfEabi,
ElfSpe,
}
impl PpcAbi {
pub fn from_name(name: &str) -> Option<PpcAbi> {
match name.to_lowercase().as_str() {
"elfv1" | "elfv1_64" | "elf64v1" => Some(PpcAbi::ElfV1),
"elfv2" | "elfv2_64" | "elf64v2" => Some(PpcAbi::ElfV2),
"aix" | "xcoff" | "aix_xcoff" => Some(PpcAbi::Aix),
"darwin" | "macho" => Some(PpcAbi::Darwin),
"elf_eabi" | "eabi" | "ppc_elf" => Some(PpcAbi::ElfEabi),
"elf_spe" | "spe" => Some(PpcAbi::ElfSpe),
_ => None,
}
}
pub fn is_64bit(self) -> bool {
matches!(self, PpcAbi::ElfV1 | PpcAbi::ElfV2 | PpcAbi::Aix)
}
pub fn is_little_endian(self) -> bool {
matches!(self, PpcAbi::ElfV2)
}
pub fn default_abi_for_cpu(cpu: PpcCpu) -> PpcAbi {
if cpu.is_64bit() {
PpcAbi::ElfV2 } else if cpu.is_embedded() {
PpcAbi::ElfEabi
} else {
PpcAbi::ElfV1
}
}
pub fn stack_alignment(self) -> u32 {
match self {
PpcAbi::ElfV2 => 32, PpcAbi::ElfV1 | PpcAbi::Aix => 16,
PpcAbi::Darwin => 16,
PpcAbi::ElfEabi | PpcAbi::ElfSpe => 8,
}
}
pub fn red_zone_size(self) -> u32 {
match self {
PpcAbi::ElfV2 => 512, PpcAbi::ElfV1 => 288, PpcAbi::Aix => 0, PpcAbi::Darwin => 224,
PpcAbi::ElfEabi | PpcAbi::ElfSpe => 0,
}
}
pub fn toc_register(self) -> Option<u32> {
match self {
PpcAbi::ElfV1 | PpcAbi::ElfV2 => Some(2), PpcAbi::Aix => Some(2),
PpcAbi::Darwin | PpcAbi::ElfEabi | PpcAbi::ElfSpe => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PpcSchedModel {
Power4,
Power5,
Power6,
Power7,
Power8,
Power9,
Power10,
Power11,
Generic,
Embedded,
}
impl PpcSchedModel {
pub fn for_cpu(cpu: PpcCpu) -> PpcSchedModel {
match cpu {
PpcCpu::Power4 => PpcSchedModel::Power4,
PpcCpu::Power5 | PpcCpu::Power5Plus => PpcSchedModel::Power5,
PpcCpu::Power6 => PpcSchedModel::Power6,
PpcCpu::Power7 => PpcSchedModel::Power7,
PpcCpu::Power8 => PpcSchedModel::Power8,
PpcCpu::Power9 => PpcSchedModel::Power9,
PpcCpu::Power10 => PpcSchedModel::Power10,
PpcCpu::Power11 => PpcSchedModel::Power11,
PpcCpu::Ppc440 | PpcCpu::Ppc450 | PpcCpu::Ppc460 |
PpcCpu::Ppc464 | PpcCpu::Ppc465 | PpcCpu::Ppc470 |
PpcCpu::Ppc476 | PpcCpu::Ppc476Fp |
PpcCpu::E500 | PpcCpu::E500v2 | PpcCpu::E500Mc |
PpcCpu::E5500 | PpcCpu::E6500 => PpcSchedModel::Embedded,
_ => PpcSchedModel::Generic,
}
}
pub fn issue_width(self) -> u32 {
match self {
PpcSchedModel::Power4 | PpcSchedModel::Power5 | PpcSchedModel::Power6 => 2,
PpcSchedModel::Power7 => 6,
PpcSchedModel::Power8 => 8,
PpcSchedModel::Power9 => 6,
PpcSchedModel::Power10 => 8,
PpcSchedModel::Power11 => 8,
PpcSchedModel::Generic => 4,
PpcSchedModel::Embedded => 1,
}
}
pub fn max_smt_threads(self) -> u32 {
match self {
PpcSchedModel::Power4 => 2,
PpcSchedModel::Power5 | PpcSchedModel::Power6 => 2,
PpcSchedModel::Power7 => 4,
PpcSchedModel::Power8 => 8,
PpcSchedModel::Power9 | PpcSchedModel::Power10 | PpcSchedModel::Power11 => 4,
PpcSchedModel::Generic => 4,
PpcSchedModel::Embedded => 1,
}
}
pub fn num_execution_units(self) -> u32 {
match self {
PpcSchedModel::Power4 => 8,
PpcSchedModel::Power5 => 8,
PpcSchedModel::Power6 => 8,
PpcSchedModel::Power7 => 12,
PpcSchedModel::Power8 => 16,
PpcSchedModel::Power9 => 10,
PpcSchedModel::Power10 => 12,
PpcSchedModel::Power11 => 14,
PpcSchedModel::Generic => 8,
PpcSchedModel::Embedded => 2,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PpcTuningFlags {
pub prefer_indexed_addressing: bool,
pub prefer_update_forms: bool,
pub prefer_mask_instructions: bool,
pub schedule_loads_early: bool,
pub aggressive_fma: bool,
pub enable_software_pipelining: bool,
pub favor_vector_length_matching: bool,
pub prefer_cr_logical: bool,
pub loop_unroll_factor: u32,
pub max_memcpy_inline_size: usize,
pub prefer_dform_loads: bool,
pub bi_endian: bool,
pub prefetch_distance: u32,
pub tune_for_spe: bool,
pub enable_store_gathering: bool,
}
impl Default for PpcTuningFlags {
fn default() -> Self {
PpcTuningFlags {
prefer_indexed_addressing: true,
prefer_update_forms: true,
prefer_mask_instructions: false,
schedule_loads_early: true,
aggressive_fma: false,
enable_software_pipelining: false,
favor_vector_length_matching: false,
prefer_cr_logical: false,
loop_unroll_factor: 4,
max_memcpy_inline_size: 128,
prefer_dform_loads: true,
bi_endian: false,
prefetch_distance: 0,
tune_for_spe: false,
enable_store_gathering: false,
}
}
}
impl PpcTuningFlags {
pub fn for_cpu(cpu: PpcCpu) -> Self {
let mut tf = PpcTuningFlags::default();
match cpu {
PpcCpu::Power4 | PpcCpu::Power5 | PpcCpu::Power5Plus => {
tf.prefer_update_forms = true;
tf.loop_unroll_factor = 4;
}
PpcCpu::Power6 => {
tf.schedule_loads_early = true;
tf.prefer_dform_loads = true;
tf.loop_unroll_factor = 8;
}
PpcCpu::Power7 => {
tf.prefer_update_forms = true;
tf.schedule_loads_early = true;
tf.aggressive_fma = false;
tf.loop_unroll_factor = 4;
tf.prefetch_distance = 3;
}
PpcCpu::Power8 => {
tf.prefer_mask_instructions = true;
tf.prefer_update_forms = true;
tf.schedule_loads_early = true;
tf.favor_vector_length_matching = true;
tf.enable_store_gathering = true;
tf.prefetch_distance = 3;
tf.loop_unroll_factor = 8;
tf.max_memcpy_inline_size = 256;
}
PpcCpu::Power9 => {
tf.prefer_mask_instructions = true;
tf.schedule_loads_early = true;
tf.favor_vector_length_matching = true;
tf.aggressive_fma = true;
tf.enable_software_pipelining = true;
tf.enable_store_gathering = true;
tf.prefetch_distance = 4;
tf.loop_unroll_factor = 4;
}
PpcCpu::Power10 => {
tf.prefer_mask_instructions = true;
tf.schedule_loads_early = true;
tf.favor_vector_length_matching = true;
tf.aggressive_fma = true;
tf.enable_software_pipelining = true;
tf.enable_store_gathering = true;
tf.prefer_dform_loads = true;
tf.prefetch_distance = 4;
tf.loop_unroll_factor = 4;
tf.max_memcpy_inline_size = 512;
}
PpcCpu::Power11 => {
tf.prefer_mask_instructions = true;
tf.schedule_loads_early = true;
tf.favor_vector_length_matching = true;
tf.aggressive_fma = true;
tf.enable_software_pipelining = true;
tf.enable_store_gathering = true;
tf.prefetch_distance = 6;
tf.loop_unroll_factor = 8;
tf.max_memcpy_inline_size = 512;
}
PpcCpu::E500 | PpcCpu::E500v2 => {
tf.tune_for_spe = true;
tf.prefer_indexed_addressing = false;
tf.loop_unroll_factor = 2;
tf.max_memcpy_inline_size = 64;
}
PpcCpu::E500Mc | PpcCpu::E5500 | PpcCpu::E6500 => {
tf.loop_unroll_factor = 2;
tf.max_memcpy_inline_size = 64;
}
PpcCpu::Ppc440 | PpcCpu::Ppc450 | PpcCpu::Ppc460 |
PpcCpu::Ppc464 | PpcCpu::Ppc465 | PpcCpu::Ppc470 |
PpcCpu::Ppc476 | PpcCpu::Ppc476Fp => {
tf.prefer_indexed_addressing = false;
tf.prefer_update_forms = false;
tf.schedule_loads_early = false;
tf.loop_unroll_factor = 2;
tf.max_memcpy_inline_size = 64;
}
_ => {}
}
tf
}
pub fn with_bi_endian(mut self, bi: bool) -> Self {
self.bi_endian = bi;
self
}
}
#[derive(Debug, Clone)]
pub struct PpcSubtargetFull {
pub cpu: PpcCpu,
pub features: PpcFeatureSet,
pub abi: PpcAbi,
pub sched_model: PpcSchedModel,
pub tuning: PpcTuningFlags,
pub target_cpu_name: String,
pub target_triple: String,
}
impl PpcSubtargetFull {
pub fn new(cpu: PpcCpu) -> Self {
let features = PpcFeatureSet::for_cpu(cpu);
let abi = PpcAbi::default_abi_for_cpu(cpu);
let sched = PpcSchedModel::for_cpu(cpu);
let tuning = PpcTuningFlags::for_cpu(cpu);
let triple = Self::default_triple(cpu, abi);
PpcSubtargetFull {
cpu,
features,
abi,
sched_model: sched,
tuning,
target_cpu_name: cpu.name().to_string(),
target_triple: triple,
}
}
pub fn from_cpu_name(name: &str) -> Option<Self> {
PpcCpu::from_name(name).map(PpcSubtargetFull::new)
}
pub fn with_abi(mut self, abi: PpcAbi) -> Self {
self.abi = abi;
self.target_triple = Self::default_triple(self.cpu, abi);
self
}
pub fn with_isa_level(mut self, isa: &str) -> Self {
self.features = self.features.with_isa(isa);
self
}
pub fn with_features(mut self, features: PpcFeatureSet) -> Self {
self.features = features;
self
}
fn default_triple(cpu: PpcCpu, abi: PpcAbi) -> String {
let endian = if abi.is_little_endian() { "le" } else { "" };
if cpu.is_64bit() {
format!("powerpc64{}-unknown-linux-gnu", endian)
} else {
format!("powerpc-unknown-linux-gnu")
}
}
pub fn is_64bit(&self) -> bool { self.cpu.is_64bit() }
pub fn has_vsx(&self) -> bool { self.features.has_vsx }
pub fn has_mma(&self) -> bool { self.features.has_mma }
pub fn has_prefix(&self) -> bool { self.features.has_prefix }
pub fn has_altivec(&self) -> bool { self.features.has_altivec }
pub fn data_layout_string(&self) -> String {
if self.is_64bit() {
if self.abi.is_little_endian() {
"e-m:e-i64:64-n32:64".to_string()
} else {
"E-m:e-i64:64-n32:64".to_string()
}
} else {
"E-m:e-p:32:32-i64:64-n32".to_string()
}
}
pub fn num_gprs(&self) -> u32 { 32 }
pub fn num_fprs(&self) -> u32 {
if self.features.has_fp { 32 } else { 0 }
}
pub fn num_vsx_regs(&self) -> u32 {
if self.features.has_vsx { 64 } else { 0 }
}
pub fn num_vrs(&self) -> u32 {
if self.features.has_altivec { 32 } else { 0 }
}
pub fn feature_string(&self) -> String {
let mut feats = Vec::new();
if self.features.has_altivec { feats.push("+altivec"); }
if self.features.has_vsx { feats.push("+vsx"); }
if self.features.has_mma { feats.push("+mma"); }
if self.features.has_dfp { feats.push("+dfp"); }
if self.features.has_spe { feats.push("+spe"); }
if self.features.has_vle { feats.push("+vle"); }
if self.features.has_prefix { feats.push("+prefix"); }
if self.features.has_pcrelative { feats.push("+pcrelative-memops"); }
if self.features.has_htm { feats.push("+htm"); }
feats.join(",")
}
pub fn supports_feature(&self, feature: &str) -> bool {
match feature {
"altivec" | "vmx" => self.features.has_altivec,
"vsx" => self.features.has_vsx,
"mma" => self.features.has_mma,
"dfp" => self.features.has_dfp,
"fp" | "fpu" => self.features.has_fp,
"spe" => self.features.has_spe,
"vle" => self.features.has_vle,
"prefix" | "prefixed" => self.features.has_prefix,
"pcrelative-memops" => self.features.has_pcrelative,
"htm" | "transactional-memory" => self.features.has_htm,
"darn" => self.features.has_darn,
"radix-mmu" => self.features.has_radix_mmu,
"smt" => self.features.has_smt,
_ => false,
}
}
pub fn dispatch_width(&self) -> u32 {
self.sched_model.issue_width()
}
pub fn max_smt_threads(&self) -> u32 {
self.sched_model.max_smt_threads()
}
}
impl Default for PpcSubtargetFull {
fn default() -> Self {
PpcSubtargetFull::new(PpcCpu::Generic)
}
}
pub struct PpcSubtargetDb;
impl PpcSubtargetDb {
pub fn all_cpu_names() -> Vec<&'static str> {
vec![
"power4", "power5", "power5+", "power6", "power7",
"power8", "power9", "power10", "power11",
"ppc440", "ppc450", "ppc460", "ppc464", "ppc465",
"ppc470", "ppc476", "ppc476fp",
"e500", "e500v2", "e500mc", "e5500", "e6500",
"ppc601", "ppc603", "ppc603e", "ppc604", "ppc604e",
"ppc740", "ppc7400", "ppc7450", "ppc750",
"cellbe", "generic",
]
}
pub fn cpus_with_feature(feature: &str) -> Vec<PpcCpu> {
Self::all_cpu_names()
.iter()
.filter_map(|name| PpcCpu::from_name(name))
.filter(|cpu| {
let st = PpcSubtargetFull::new(*cpu);
st.supports_feature(feature)
})
.collect()
}
pub fn cpus_64bit() -> Vec<PpcCpu> {
Self::all_cpu_names()
.iter()
.filter_map(|name| PpcCpu::from_name(name))
.filter(|cpu| cpu.is_64bit())
.collect()
}
pub fn cpus_embedded() -> Vec<PpcCpu> {
Self::all_cpu_names()
.iter()
.filter_map(|name| PpcCpu::from_name(name))
.filter(|cpu| cpu.is_embedded())
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cpu_from_name_valid() {
assert_eq!(PpcCpu::from_name("power9").unwrap(), PpcCpu::Power9);
assert_eq!(PpcCpu::from_name("POWER10").unwrap(), PpcCpu::Power10);
assert_eq!(PpcCpu::from_name("cellbe").unwrap(), PpcCpu::CellBE);
assert_eq!(PpcCpu::from_name("e500").unwrap(), PpcCpu::E500);
assert_eq!(PpcCpu::from_name("generic").unwrap(), PpcCpu::Generic);
}
#[test]
fn test_cpu_from_name_invalid() {
assert!(PpcCpu::from_name("x86_64").is_none());
assert!(PpcCpu::from_name("").is_none());
}
#[test]
fn test_cpu_is_64bit() {
assert!(PpcCpu::Power9.is_64bit());
assert!(!PpcCpu::Ppc440.is_64bit());
assert!(!PpcCpu::E500.is_64bit());
}
#[test]
fn test_cpu_is_embedded() {
assert!(PpcCpu::E6500.is_embedded());
assert!(PpcCpu::Ppc476.is_embedded());
assert!(!PpcCpu::Power9.is_embedded());
assert!(!PpcCpu::Ppc601.is_embedded());
}
#[test]
fn test_feature_set_power9() {
let fs = PpcFeatureSet::for_cpu(PpcCpu::Power9);
assert!(fs.has_vsx);
assert!(fs.has_altivec);
assert!(fs.has_dfp);
assert!(fs.has_fp);
assert!(fs.has_smt);
assert!(fs.smt_threads == 4);
assert!(!fs.has_mma);
assert!(!fs.has_prefix);
assert!(fs.isa_3_0);
}
#[test]
fn test_feature_set_power10() {
let fs = PpcFeatureSet::for_cpu(PpcCpu::Power10);
assert!(fs.has_vsx);
assert!(fs.has_mma);
assert!(fs.has_prefix);
assert!(fs.has_pcrelative);
assert!(!fs.has_htm);
assert!(fs.isa_3_1);
}
#[test]
fn test_feature_set_vsx_implies_altivec() {
let mut fs = PpcFeatureSet::default();
fs.has_vsx = true;
fs.has_altivec = false;
fs.validate();
assert!(fs.has_altivec);
assert!(fs.has_fp);
}
#[test]
fn test_feature_set_mma_implies_vsx() {
let mut fs = PpcFeatureSet::default();
fs.has_mma = true;
fs.validate();
assert!(fs.has_vsx);
assert!(fs.has_altivec);
}
#[test]
fn test_feature_set_dfp_implies_fp() {
let mut fs = PpcFeatureSet::default();
fs.has_fp = false;
fs.has_dfp = true;
fs.validate();
assert!(fs.has_fp);
}
#[test]
fn test_abi_selection() {
assert_eq!(PpcAbi::from_name("elfv2"), Some(PpcAbi::ElfV2));
assert_eq!(PpcAbi::from_name("aix"), Some(PpcAbi::Aix));
assert!(PpcAbi::ElfV2.is_little_endian());
assert!(!PpcAbi::ElfV1.is_little_endian());
}
#[test]
fn test_abi_stack_alignment() {
assert_eq!(PpcAbi::ElfV2.stack_alignment(), 32);
assert_eq!(PpcAbi::ElfV1.stack_alignment(), 16);
assert_eq!(PpcAbi::ElfEabi.stack_alignment(), 8);
}
#[test]
fn test_abi_red_zone() {
assert_eq!(PpcAbi::ElfV2.red_zone_size(), 512);
assert_eq!(PpcAbi::ElfV1.red_zone_size(), 288);
assert_eq!(PpcAbi::Aix.red_zone_size(), 0);
}
#[test]
fn test_abi_64bit_detection() {
assert!(PpcAbi::ElfV1.is_64bit());
assert!(PpcAbi::ElfV2.is_64bit());
assert!(PpcAbi::Aix.is_64bit());
assert!(!PpcAbi::Darwin.is_64bit());
assert!(!PpcAbi::ElfEabi.is_64bit());
}
#[test]
fn test_sched_model_issue_width() {
assert_eq!(PpcSchedModel::Power7.issue_width(), 6);
assert_eq!(PpcSchedModel::Power8.issue_width(), 8);
assert_eq!(PpcSchedModel::Power9.issue_width(), 6);
assert_eq!(PpcSchedModel::Embedded.issue_width(), 1);
}
#[test]
fn test_sched_model_for_cpu() {
assert_eq!(PpcSchedModel::for_cpu(PpcCpu::Power9), PpcSchedModel::Power9);
assert_eq!(PpcSchedModel::for_cpu(PpcCpu::E6500), PpcSchedModel::Embedded);
}
#[test]
fn test_tuning_flags_power10() {
let tf = PpcTuningFlags::for_cpu(PpcCpu::Power10);
assert!(tf.aggressive_fma);
assert!(tf.enable_software_pipelining);
assert!(tf.enable_store_gathering);
assert_eq!(tf.max_memcpy_inline_size, 512);
}
#[test]
fn test_tuning_flags_spe() {
let tf = PpcTuningFlags::for_cpu(PpcCpu::E500);
assert!(tf.tune_for_spe);
assert!(!tf.prefer_indexed_addressing);
}
#[test]
fn test_subtarget_from_cpu_name() {
let st = PpcSubtargetFull::from_cpu_name("power9").unwrap();
assert_eq!(st.cpu, PpcCpu::Power9);
assert!(st.has_vsx());
assert!(!st.has_mma());
}
#[test]
fn test_subtarget_data_layout() {
let st_be = PpcSubtargetFull::new(PpcCpu::Power9);
assert!(st_be.data_layout_string().starts_with("E-"));
let st_le = PpcSubtargetFull::new(PpcCpu::Power9).with_abi(PpcAbi::ElfV2);
assert!(st_le.data_layout_string().starts_with("e-"));
}
#[test]
fn test_subtarget_feature_string() {
let st = PpcSubtargetFull::new(PpcCpu::Power10);
let fs = st.feature_string();
assert!(fs.contains("+vsx"));
assert!(fs.contains("+mma"));
assert!(fs.contains("+prefix"));
}
#[test]
fn test_subtarget_db_all_cpus() {
let names = PpcSubtargetDb::all_cpu_names();
assert!(!names.is_empty());
assert!(names.contains(&"power9"));
}
#[test]
fn test_subtarget_db_64bit_cpus() {
let cpus = PpcSubtargetDb::cpus_64bit();
assert!(cpus.contains(&PpcCpu::Power9));
assert!(!cpus.contains(&PpcCpu::E500));
}
#[test]
fn test_subtarget_num_regs() {
let st = PpcSubtargetFull::new(PpcCpu::Power10);
assert_eq!(st.num_gprs(), 32);
assert_eq!(st.num_fprs(), 32);
assert_eq!(st.num_vsx_regs(), 64);
assert_eq!(st.num_vrs(), 32);
}
#[test]
fn test_abi_toc_register() {
assert_eq!(PpcAbi::ElfV2.toc_register(), Some(2));
assert_eq!(PpcAbi::Darwin.toc_register(), None);
assert_eq!(PpcAbi::ElfEabi.toc_register(), None);
}
#[test]
fn test_default_subtarget() {
let st = PpcSubtargetFull::default();
assert_eq!(st.cpu, PpcCpu::Generic);
assert!(st.has_vsx());
}
#[test]
fn test_feature_string_contains() {
let st = PpcSubtargetFull::new(PpcCpu::Power8);
let fs = st.feature_string();
assert!(fs.contains("+htm"));
assert!(fs.contains("+altivec"));
}
#[test]
fn test_missing_features_e500() {
let st = PpcSubtargetFull::new(PpcCpu::E500);
assert!(!st.has_vsx());
assert!(!st.has_mma());
assert!(!st.features.has_fp);
assert!(st.features.has_spe);
assert_eq!(st.num_fprs(), 0);
}
#[test]
fn test_bi_endian_flag() {
let tf = PpcTuningFlags::for_cpu(PpcCpu::Power9).with_bi_endian(true);
assert!(tf.bi_endian);
}
#[test]
fn test_prefix_implies_isa_31() {
let mut fs = PpcFeatureSet::default();
fs.has_prefix = true;
fs.validate();
assert!(fs.isa_3_1);
}
#[test]
fn test_supports_feature_enumeration() {
let st = PpcSubtargetFull::new(PpcCpu::Power10);
assert!(st.supports_feature("vsx"));
assert!(st.supports_feature("mma"));
assert!(st.supports_feature("prefix"));
assert!(!st.supports_feature("htm"));
assert!(!st.supports_feature("spe"));
}
#[test]
fn test_dispatch_width() {
assert_eq!(
PpcSubtargetFull::new(PpcCpu::Power8).dispatch_width(),
8
);
assert_eq!(
PpcSubtargetFull::new(PpcCpu::E500).dispatch_width(),
1
);
}
}