use crate::mc_streamer::x86_opcodes;
use crate::x86::x86_schedule_model::{ProcResource, SchedModel};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86MicroArchKind {
SandyBridge,
IvyBridge,
Haswell,
Broadwell,
SkylakeClient,
SkylakeServer,
KabyLake,
CoffeeLake,
CascadeLake,
CometLake,
IceLakeClient,
IceLakeServer,
TigerLake,
RocketLake,
AlderLakePcore,
AlderLakeEcore,
RaptorLakePcore,
RaptorLakeEcore,
SapphireRapids,
EmeraldRapids,
GraniteRapids,
SierraForest,
Zen1,
ZenPlus,
Zen2,
Zen3,
Zen4,
Zen5,
Zen5c,
Silvermont,
Airmont,
Goldmont,
GoldmontPlus,
Tremont,
Gracemont,
Crestmont,
KnightsLanding,
KnightsMill,
}
impl X86MicroArchKind {
pub fn name(&self) -> &'static str {
match self {
Self::SandyBridge => "Intel Sandy Bridge",
Self::IvyBridge => "Intel Ivy Bridge",
Self::Haswell => "Intel Haswell",
Self::Broadwell => "Intel Broadwell",
Self::SkylakeClient => "Intel Skylake (Client)",
Self::SkylakeServer => "Intel Skylake (Server)",
Self::KabyLake => "Intel Kaby Lake",
Self::CoffeeLake => "Intel Coffee Lake",
Self::CascadeLake => "Intel Cascade Lake",
Self::CometLake => "Intel Comet Lake",
Self::IceLakeClient => "Intel Ice Lake (Client)",
Self::IceLakeServer => "Intel Ice Lake (Server)",
Self::TigerLake => "Intel Tiger Lake",
Self::RocketLake => "Intel Rocket Lake",
Self::AlderLakePcore => "Intel Alder Lake (P-core / Golden Cove)",
Self::AlderLakeEcore => "Intel Alder Lake (E-core / Gracemont)",
Self::RaptorLakePcore => "Intel Raptor Lake (P-core / Raptor Cove)",
Self::RaptorLakeEcore => "Intel Raptor Lake (E-core / Gracemont)",
Self::SapphireRapids => "Intel Sapphire Rapids (Golden Cove Server)",
Self::EmeraldRapids => "Intel Emerald Rapids (Raptor Cove Server)",
Self::GraniteRapids => "Intel Granite Rapids (Redwood Cove Server)",
Self::SierraForest => "Intel Sierra Forest (Sierra Glen E-core)",
Self::Zen1 => "AMD Zen (Family 17h)",
Self::ZenPlus => "AMD Zen+ (Family 17h)",
Self::Zen2 => "AMD Zen 2 (Family 17h)",
Self::Zen3 => "AMD Zen 3 (Family 19h)",
Self::Zen4 => "AMD Zen 4 (Family 19h)",
Self::Zen5 => "AMD Zen 5 (Family 1Ah)",
Self::Zen5c => "AMD Zen 5c (Family 1Ah Dense)",
Self::Silvermont => "Intel Silvermont",
Self::Airmont => "Intel Airmont",
Self::Goldmont => "Intel Goldmont",
Self::GoldmontPlus => "Intel Goldmont Plus",
Self::Tremont => "Intel Tremont",
Self::Gracemont => "Intel Gracemont",
Self::Crestmont => "Intel Crestmont",
Self::KnightsLanding => "Intel Knights Landing (Xeon Phi)",
Self::KnightsMill => "Intel Knights Mill (Xeon Phi)",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SchedulerKind {
Unified { size: u32 },
Distributed {
entries: &'static [u32],
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BranchPredictorKind {
Bimodal,
TwoLevel,
Tournament,
TAGE,
Perceptron,
HashedPerceptron,
ITTAGE,
Custom(&'static str),
}
#[derive(Debug, Clone)]
pub struct CacheLevel {
pub size: u32,
pub associativity: u32,
pub latency: u32,
pub line_size: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LlcTopology {
Ring,
Mesh,
PerCcx,
PerTile,
None,
}
#[derive(Debug, Clone)]
pub struct TlbLevel {
pub entries: u32,
pub associativity: u32,
pub page_sizes: &'static str,
}
#[derive(Debug, Clone)]
pub struct X86MicroArch {
pub kind: X86MicroArchKind,
pub pipeline: PipelineConfig,
pub ports: Vec<ExecutionPort>,
pub execution_units: Vec<ExecutionUnit>,
pub scheduler: SchedulerConfig,
pub buffers: BufferConfig,
pub caches: CacheConfig,
pub tlbs: TlbConfig,
pub branch_predictor: BranchPredictorConfig,
pub features: MicroArchFeatures,
}
#[derive(Debug, Clone)]
pub struct PipelineConfig {
pub decode_width: u32,
pub dispatch_width: u32,
pub issue_width: u32,
pub retire_width: u32,
pub pipeline_depth: u32,
pub out_of_order: bool,
}
#[derive(Debug, Clone)]
pub struct ExecutionPort {
pub id: &'static str,
pub capabilities: PortCapabilities,
pub instance_count: u32,
}
#[derive(Debug, Clone, Copy)]
pub struct PortCapabilities {
pub has_int_alu: bool,
pub has_int_mul: bool,
pub has_int_div: bool,
pub has_branch: bool,
pub has_agu_load: bool,
pub has_agu_store: bool,
pub has_store_data: bool,
pub has_fpu_add: bool,
pub has_fpu_mul: bool,
pub has_fpu_div: bool,
pub has_simd_int: bool,
pub has_simd_fp: bool,
pub has_simd_shuffle: bool,
pub has_simd_fma: bool,
pub has_avx512_fp: bool,
pub has_avx512_int: bool,
pub has_pdep_pext: bool,
}
impl PortCapabilities {
pub const fn none() -> Self {
Self {
has_int_alu: false,
has_int_mul: false,
has_int_div: false,
has_branch: false,
has_agu_load: false,
has_agu_store: false,
has_store_data: false,
has_fpu_add: false,
has_fpu_mul: false,
has_fpu_div: false,
has_simd_int: false,
has_simd_fp: false,
has_simd_shuffle: false,
has_simd_fma: false,
has_avx512_fp: false,
has_avx512_int: false,
has_pdep_pext: false,
}
}
}
#[derive(Debug, Clone)]
pub struct ExecutionUnit {
pub unit_type: ExecutionUnitType,
pub count: u32,
pub typical_latency: u32,
pub reciprocal_throughput: f64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExecutionUnitType {
AluInt,
AluIntComplex,
AguLoad,
AguStore,
FpuAdd,
FpuMul,
FpuDiv,
FpuFma,
SimdInt,
SimdFp,
SimdShuffle,
SimdFma,
Branch,
IntDivider,
IntMultiplier,
}
#[derive(Debug, Clone)]
pub struct SchedulerConfig {
pub kind: SchedulerKind,
pub total_entries: u32,
}
#[derive(Debug, Clone)]
pub struct BufferConfig {
pub rob_size: u32,
pub load_buffer: u32,
pub store_buffer: u32,
pub line_fill_buffer: u32,
pub uop_queue_size: u32,
}
#[derive(Debug, Clone)]
pub struct CacheConfig {
pub l1i: CacheLevel,
pub l1d: CacheLevel,
pub l2: CacheLevel,
pub l3: Option<CacheLevel>,
pub l2_inclusive: bool,
pub l3_inclusive: bool,
pub llc_topology: LlcTopology,
}
#[derive(Debug, Clone)]
pub struct TlbConfig {
pub itlb: Vec<TlbLevel>,
pub dtlb: Vec<TlbLevel>,
pub stlb: Option<TlbLevel>,
}
#[derive(Debug, Clone)]
pub struct BranchPredictorConfig {
pub predictor_kind: BranchPredictorKind,
pub btb_entries: u32,
pub rsb_entries: u32,
pub indirect_branch_entries: u32,
pub mispredict_penalty: u32,
pub prediction_accuracy_percent: f64,
}
#[derive(Debug, Clone)]
pub struct MicroArchFeatures {
pub uop_cache_entries: u32,
pub uop_cache_associativity: u32,
pub has_lsd: bool,
pub has_macro_fusion: bool,
pub has_micro_fusion: bool,
pub has_avx512: bool,
pub has_amx: bool,
pub has_smt: bool,
pub smt_threads: u32,
pub phys_reg_int: u32,
pub phys_reg_fp: u32,
}
pub fn sandy_bridge() -> X86MicroArch {
X86MicroArch {
kind: X86MicroArchKind::SandyBridge,
pipeline: PipelineConfig {
decode_width: 4,
dispatch_width: 4,
issue_width: 6,
retire_width: 4,
pipeline_depth: 14,
out_of_order: true,
},
ports: vec![
ExecutionPort {
id: "0",
capabilities: PortCapabilities {
has_int_alu: true,
has_fpu_mul: true,
has_fpu_div: true,
has_simd_int: true,
has_simd_fp: true,
has_simd_fma: false,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "1",
capabilities: PortCapabilities {
has_int_alu: true,
has_fpu_add: true,
has_simd_int: true,
has_simd_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "2",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "3",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "4",
capabilities: PortCapabilities {
has_agu_store: true,
has_store_data: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "5",
capabilities: PortCapabilities {
has_int_alu: true,
has_branch: true,
has_simd_int: true,
has_simd_shuffle: true,
..PortCapabilities::none()
},
instance_count: 1,
},
],
execution_units: vec![
ExecutionUnit {
unit_type: ExecutionUnitType::AluInt,
count: 3,
typical_latency: 1,
reciprocal_throughput: 1.0 / 3.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguLoad,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguStore,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuAdd,
count: 1,
typical_latency: 3,
reciprocal_throughput: 1.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuMul,
count: 1,
typical_latency: 5,
reciprocal_throughput: 1.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuDiv,
count: 1,
typical_latency: 10,
reciprocal_throughput: 10.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::SimdInt,
count: 3,
typical_latency: 1,
reciprocal_throughput: 1.0 / 3.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::SimdFp,
count: 2,
typical_latency: 3,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::Branch,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
],
scheduler: SchedulerConfig {
kind: SchedulerKind::Unified { size: 54 },
total_entries: 54,
},
buffers: BufferConfig {
rob_size: 168,
load_buffer: 64,
store_buffer: 36,
line_fill_buffer: 10,
uop_queue_size: 28,
},
caches: CacheConfig {
l1i: CacheLevel {
size: 32768,
associativity: 8,
latency: 4,
line_size: 64,
},
l1d: CacheLevel {
size: 32768,
associativity: 8,
latency: 4,
line_size: 64,
},
l2: CacheLevel {
size: 262144,
associativity: 8,
latency: 12,
line_size: 64,
},
l3: Some(CacheLevel {
size: 8388608,
associativity: 16,
latency: 26,
line_size: 64,
}),
l2_inclusive: false,
l3_inclusive: true,
llc_topology: LlcTopology::Ring,
},
tlbs: TlbConfig {
itlb: vec![
TlbLevel {
entries: 64,
associativity: 4,
page_sizes: "4K",
},
TlbLevel {
entries: 32,
associativity: 4,
page_sizes: "2M",
},
],
dtlb: vec![
TlbLevel {
entries: 64,
associativity: 4,
page_sizes: "4K",
},
TlbLevel {
entries: 32,
associativity: 4,
page_sizes: "2M",
},
],
stlb: Some(TlbLevel {
entries: 512,
associativity: 4,
page_sizes: "4K",
}),
},
branch_predictor: BranchPredictorConfig {
predictor_kind: BranchPredictorKind::TwoLevel,
btb_entries: 4096,
rsb_entries: 16,
indirect_branch_entries: 0,
mispredict_penalty: 14,
prediction_accuracy_percent: 95.0,
},
features: MicroArchFeatures {
uop_cache_entries: 1536,
uop_cache_associativity: 8,
has_lsd: true,
has_macro_fusion: true,
has_micro_fusion: true,
has_avx512: false,
has_amx: false,
has_smt: true,
smt_threads: 2,
phys_reg_int: 160,
phys_reg_fp: 144,
},
}
}
pub fn ivy_bridge() -> X86MicroArch {
let mut m = sandy_bridge();
m.kind = X86MicroArchKind::IvyBridge;
for unit in &mut m.execution_units {
if unit.unit_type == ExecutionUnitType::FpuDiv {
unit.typical_latency = 8;
unit.reciprocal_throughput = 8.0;
}
}
m.branch_predictor.mispredict_penalty = 13;
m
}
pub fn haswell() -> X86MicroArch {
X86MicroArch {
kind: X86MicroArchKind::Haswell,
pipeline: PipelineConfig {
decode_width: 4,
dispatch_width: 4,
issue_width: 8,
retire_width: 4,
pipeline_depth: 14,
out_of_order: true,
},
ports: vec![
ExecutionPort {
id: "0",
capabilities: PortCapabilities {
has_int_alu: true,
has_fpu_add: true,
has_fpu_mul: true,
has_fpu_div: true,
has_simd_int: true,
has_simd_fp: true,
has_simd_fma: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "1",
capabilities: PortCapabilities {
has_int_alu: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_int: true,
has_simd_fp: true,
has_simd_fma: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "2",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "3",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "4",
capabilities: PortCapabilities {
has_agu_store: true,
has_store_data: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "5",
capabilities: PortCapabilities {
has_int_alu: true,
has_simd_shuffle: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "6",
capabilities: PortCapabilities {
has_int_alu: true,
has_branch: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "7",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
],
execution_units: vec![
ExecutionUnit {
unit_type: ExecutionUnitType::AluInt,
count: 4,
typical_latency: 1,
reciprocal_throughput: 0.25,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguLoad,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguStore,
count: 2,
typical_latency: 1,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuAdd,
count: 2,
typical_latency: 3,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuMul,
count: 2,
typical_latency: 5,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuFma,
count: 2,
typical_latency: 5,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuDiv,
count: 1,
typical_latency: 10,
reciprocal_throughput: 8.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::SimdInt,
count: 3,
typical_latency: 1,
reciprocal_throughput: 1.0 / 3.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::SimdFp,
count: 2,
typical_latency: 3,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::SimdFma,
count: 2,
typical_latency: 5,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::Branch,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
],
scheduler: SchedulerConfig {
kind: SchedulerKind::Unified { size: 60 },
total_entries: 60,
},
buffers: BufferConfig {
rob_size: 192,
load_buffer: 72,
store_buffer: 42,
line_fill_buffer: 10,
uop_queue_size: 56,
},
caches: CacheConfig {
l1i: CacheLevel {
size: 32768,
associativity: 8,
latency: 4,
line_size: 64,
},
l1d: CacheLevel {
size: 32768,
associativity: 8,
latency: 4,
line_size: 64,
},
l2: CacheLevel {
size: 262144,
associativity: 8,
latency: 11,
line_size: 64,
},
l3: Some(CacheLevel {
size: 8388608,
associativity: 16,
latency: 30,
line_size: 64,
}),
l2_inclusive: false,
l3_inclusive: true,
llc_topology: LlcTopology::Ring,
},
tlbs: TlbConfig {
itlb: vec![
TlbLevel {
entries: 128,
associativity: 4,
page_sizes: "4K",
},
TlbLevel {
entries: 8,
associativity: 0,
page_sizes: "2M/4M",
},
],
dtlb: vec![
TlbLevel {
entries: 64,
associativity: 4,
page_sizes: "4K",
},
TlbLevel {
entries: 32,
associativity: 4,
page_sizes: "2M",
},
],
stlb: Some(TlbLevel {
entries: 1024,
associativity: 8,
page_sizes: "4K+2M",
}),
},
branch_predictor: BranchPredictorConfig {
predictor_kind: BranchPredictorKind::Tournament,
btb_entries: 4096,
rsb_entries: 16,
indirect_branch_entries: 0,
mispredict_penalty: 15,
prediction_accuracy_percent: 96.5,
},
features: MicroArchFeatures {
uop_cache_entries: 1536,
uop_cache_associativity: 8,
has_lsd: true,
has_macro_fusion: true,
has_micro_fusion: true,
has_avx512: false,
has_amx: false,
has_smt: true,
smt_threads: 2,
phys_reg_int: 168,
phys_reg_fp: 168,
},
}
}
pub fn broadwell() -> X86MicroArch {
let mut m = haswell();
m.kind = X86MicroArchKind::Broadwell;
for unit in &mut m.execution_units {
match unit.unit_type {
ExecutionUnitType::FpuMul => {
unit.typical_latency = 3;
unit.reciprocal_throughput = 0.5;
}
ExecutionUnitType::FpuFma => {
unit.typical_latency = 5;
}
ExecutionUnitType::FpuDiv => {
unit.typical_latency = 8;
unit.reciprocal_throughput = 6.0;
}
_ => {}
}
}
m.scheduler = SchedulerConfig {
kind: SchedulerKind::Unified { size: 64 },
total_entries: 64,
};
m.branch_predictor.mispredict_penalty = 16;
m.caches.l3 = Some(CacheLevel {
size: 8388608,
associativity: 16,
latency: 32,
line_size: 64,
});
m
}
pub fn skylake_client() -> X86MicroArch {
X86MicroArch {
kind: X86MicroArchKind::SkylakeClient,
pipeline: PipelineConfig {
decode_width: 4,
dispatch_width: 6,
issue_width: 8,
retire_width: 8,
pipeline_depth: 14,
out_of_order: true,
},
ports: vec![
ExecutionPort {
id: "0",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_fpu_add: true,
has_fpu_mul: true,
has_fpu_div: true,
has_simd_int: true,
has_simd_fp: true,
has_simd_fma: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "1",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_int: true,
has_simd_fp: true,
has_simd_fma: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "2",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "3",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "4",
capabilities: PortCapabilities {
has_agu_store: true,
has_store_data: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "5",
capabilities: PortCapabilities {
has_int_alu: true,
has_simd_shuffle: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "6",
capabilities: PortCapabilities {
has_int_alu: true,
has_branch: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "7",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
],
execution_units: vec![
ExecutionUnit {
unit_type: ExecutionUnitType::AluInt,
count: 4,
typical_latency: 1,
reciprocal_throughput: 0.25,
},
ExecutionUnit {
unit_type: ExecutionUnitType::IntMultiplier,
count: 2,
typical_latency: 3,
reciprocal_throughput: 1.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguLoad,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguStore,
count: 2,
typical_latency: 1,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuAdd,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuMul,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuFma,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuDiv,
count: 1,
typical_latency: 12,
reciprocal_throughput: 6.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::SimdInt,
count: 3,
typical_latency: 1,
reciprocal_throughput: 1.0 / 3.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::SimdFp,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::SimdFma,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::Branch,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
],
scheduler: SchedulerConfig {
kind: SchedulerKind::Distributed { entries: &[97] },
total_entries: 97,
},
buffers: BufferConfig {
rob_size: 224,
load_buffer: 72,
store_buffer: 56,
line_fill_buffer: 10,
uop_queue_size: 64,
},
caches: CacheConfig {
l1i: CacheLevel {
size: 32768,
associativity: 8,
latency: 4,
line_size: 64,
},
l1d: CacheLevel {
size: 32768,
associativity: 8,
latency: 4,
line_size: 64,
},
l2: CacheLevel {
size: 262144,
associativity: 4,
latency: 12,
line_size: 64,
},
l3: Some(CacheLevel {
size: 8388608,
associativity: 16,
latency: 34,
line_size: 64,
}),
l2_inclusive: false,
l3_inclusive: true,
llc_topology: LlcTopology::Ring,
},
tlbs: TlbConfig {
itlb: vec![
TlbLevel {
entries: 128,
associativity: 8,
page_sizes: "4K",
},
TlbLevel {
entries: 8,
associativity: 0,
page_sizes: "2M/4M",
},
],
dtlb: vec![
TlbLevel {
entries: 64,
associativity: 4,
page_sizes: "4K",
},
TlbLevel {
entries: 32,
associativity: 4,
page_sizes: "2M/4M",
},
],
stlb: Some(TlbLevel {
entries: 1536,
associativity: 12,
page_sizes: "4K+2M",
}),
},
branch_predictor: BranchPredictorConfig {
predictor_kind: BranchPredictorKind::TAGE,
btb_entries: 5120,
rsb_entries: 16,
indirect_branch_entries: 512,
mispredict_penalty: 16,
prediction_accuracy_percent: 97.0,
},
features: MicroArchFeatures {
uop_cache_entries: 1536,
uop_cache_associativity: 8,
has_lsd: false,
has_macro_fusion: true,
has_micro_fusion: true,
has_avx512: false,
has_amx: false,
has_smt: true,
smt_threads: 2,
phys_reg_int: 180,
phys_reg_fp: 168,
},
}
}
pub fn skylake_server() -> X86MicroArch {
let mut m = skylake_client();
m.kind = X86MicroArchKind::SkylakeServer;
m.features.has_avx512 = true;
for port in &mut m.ports {
if port.id == "0" || port.id == "1" || port.id == "5" {
port.capabilities.has_avx512_fp = true;
port.capabilities.has_avx512_int = true;
}
}
m.caches.l2 = CacheLevel {
size: 1048576,
associativity: 16,
latency: 14,
line_size: 64,
};
m.caches.l3 = Some(CacheLevel {
size: 1408000,
associativity: 11,
latency: 50,
line_size: 64,
});
m.caches.l3_inclusive = false;
m.caches.llc_topology = LlcTopology::Mesh;
m.tlbs.stlb = Some(TlbLevel {
entries: 1536,
associativity: 6,
page_sizes: "4K+2M+1G",
});
m
}
pub fn kaby_lake() -> X86MicroArch {
let mut m = skylake_client();
m.kind = X86MicroArchKind::KabyLake;
m.caches.l3 = Some(CacheLevel {
size: 8388608,
associativity: 16,
latency: 34,
line_size: 64,
});
m
}
pub fn coffee_lake() -> X86MicroArch {
let mut m = skylake_client();
m.kind = X86MicroArchKind::CoffeeLake;
m.caches.l3 = Some(CacheLevel {
size: 12582912,
associativity: 16,
latency: 36,
line_size: 64,
});
m
}
pub fn cascade_lake() -> X86MicroArch {
let mut m = skylake_server();
m.kind = X86MicroArchKind::CascadeLake;
m.caches.l2 = CacheLevel {
size: 1048576,
associativity: 16,
latency: 14,
line_size: 64,
};
m.caches.l3 = Some(CacheLevel {
size: 40370176,
associativity: 11,
latency: 55,
line_size: 64,
});
m
}
pub fn comet_lake() -> X86MicroArch {
let mut m = skylake_client();
m.kind = X86MicroArchKind::CometLake;
m.caches.l3 = Some(CacheLevel {
size: 20971520,
associativity: 20,
latency: 40,
line_size: 64,
});
m
}
pub fn ice_lake_client() -> X86MicroArch {
X86MicroArch {
kind: X86MicroArchKind::IceLakeClient,
pipeline: PipelineConfig {
decode_width: 5,
dispatch_width: 6,
issue_width: 10,
retire_width: 8,
pipeline_depth: 14,
out_of_order: true,
},
ports: vec![
ExecutionPort {
id: "0",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_fpu_add: true,
has_fpu_mul: true,
has_fpu_div: true,
has_simd_int: true,
has_simd_fp: true,
has_simd_fma: true,
has_avx512_fp: true,
has_avx512_int: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "1",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_int: true,
has_simd_fp: true,
has_simd_fma: true,
has_avx512_fp: true,
has_avx512_int: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "2",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "3",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "4",
capabilities: PortCapabilities {
has_agu_store: true,
has_store_data: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "5",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_simd_shuffle: true,
has_simd_fma: true,
has_avx512_fp: true,
has_avx512_int: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "6",
capabilities: PortCapabilities {
has_int_alu: true,
has_branch: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "7",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "8",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "9",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
],
execution_units: vec![
ExecutionUnit {
unit_type: ExecutionUnitType::AluInt,
count: 4,
typical_latency: 1,
reciprocal_throughput: 0.25,
},
ExecutionUnit {
unit_type: ExecutionUnitType::IntMultiplier,
count: 3,
typical_latency: 3,
reciprocal_throughput: 1.0 / 3.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguLoad,
count: 2,
typical_latency: 5,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguStore,
count: 4,
typical_latency: 1,
reciprocal_throughput: 0.25,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuAdd,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuMul,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuFma,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuDiv,
count: 1,
typical_latency: 12,
reciprocal_throughput: 6.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::SimdInt,
count: 3,
typical_latency: 1,
reciprocal_throughput: 1.0 / 3.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::SimdFp,
count: 3,
typical_latency: 4,
reciprocal_throughput: 1.0 / 3.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::SimdFma,
count: 3,
typical_latency: 4,
reciprocal_throughput: 1.0 / 3.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::Branch,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
],
scheduler: SchedulerConfig {
kind: SchedulerKind::Distributed { entries: &[160] },
total_entries: 160,
},
buffers: BufferConfig {
rob_size: 352,
load_buffer: 128,
store_buffer: 72,
line_fill_buffer: 12,
uop_queue_size: 70,
},
caches: CacheConfig {
l1i: CacheLevel {
size: 32768,
associativity: 8,
latency: 4,
line_size: 64,
},
l1d: CacheLevel {
size: 49152,
associativity: 12,
latency: 5,
line_size: 64,
},
l2: CacheLevel {
size: 524288,
associativity: 8,
latency: 13,
line_size: 64,
},
l3: Some(CacheLevel {
size: 8388608,
associativity: 16,
latency: 36,
line_size: 64,
}),
l2_inclusive: false,
l3_inclusive: false,
llc_topology: LlcTopology::Ring,
},
tlbs: TlbConfig {
itlb: vec![
TlbLevel {
entries: 128,
associativity: 8,
page_sizes: "4K",
},
TlbLevel {
entries: 16,
associativity: 0,
page_sizes: "2M/4M",
},
],
dtlb: vec![
TlbLevel {
entries: 64,
associativity: 4,
page_sizes: "4K",
},
TlbLevel {
entries: 32,
associativity: 4,
page_sizes: "2M/4M",
},
],
stlb: Some(TlbLevel {
entries: 2048,
associativity: 16,
page_sizes: "4K+2M+1G",
}),
},
branch_predictor: BranchPredictorConfig {
predictor_kind: BranchPredictorKind::TAGE,
btb_entries: 6144,
rsb_entries: 20,
indirect_branch_entries: 1024,
mispredict_penalty: 17,
prediction_accuracy_percent: 97.5,
},
features: MicroArchFeatures {
uop_cache_entries: 2304,
uop_cache_associativity: 8,
has_lsd: false,
has_macro_fusion: true,
has_micro_fusion: true,
has_avx512: true,
has_amx: false,
has_smt: true,
smt_threads: 2,
phys_reg_int: 280,
phys_reg_fp: 224,
},
}
}
pub fn ice_lake_server() -> X86MicroArch {
let mut m = ice_lake_client();
m.kind = X86MicroArchKind::IceLakeServer;
m.caches.l2 = CacheLevel {
size: 1310720,
associativity: 20,
latency: 14,
line_size: 64,
};
m.caches.l3 = Some(CacheLevel {
size: 62914560,
associativity: 20,
latency: 55,
line_size: 64,
});
m.caches.llc_topology = LlcTopology::Mesh;
m
}
pub fn tiger_lake() -> X86MicroArch {
let mut m = ice_lake_client();
m.kind = X86MicroArchKind::TigerLake;
m.caches.l2 = CacheLevel {
size: 1310720,
associativity: 20,
latency: 14,
line_size: 64,
};
m.caches.l3 = Some(CacheLevel {
size: 12582912,
associativity: 12,
latency: 40,
line_size: 64,
});
m
}
pub fn rocket_lake() -> X86MicroArch {
let mut m = ice_lake_client();
m.kind = X86MicroArchKind::RocketLake;
m.caches.l2 = CacheLevel {
size: 524288,
associativity: 8,
latency: 13,
line_size: 64,
};
m.caches.l3 = Some(CacheLevel {
size: 16777216,
associativity: 16,
latency: 42,
line_size: 64,
});
m
}
pub fn alder_lake_pcore() -> X86MicroArch {
X86MicroArch {
kind: X86MicroArchKind::AlderLakePcore,
pipeline: PipelineConfig {
decode_width: 6,
dispatch_width: 8,
issue_width: 12,
retire_width: 8,
pipeline_depth: 15,
out_of_order: true,
},
ports: vec![
ExecutionPort {
id: "0",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_int_div: true,
has_fpu_add: true,
has_fpu_mul: true,
has_fpu_div: true,
has_simd_int: true,
has_simd_fp: true,
has_simd_fma: true,
has_avx512_fp: true,
has_avx512_int: true,
has_pdep_pext: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "1",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_int: true,
has_simd_fp: true,
has_simd_fma: true,
has_avx512_fp: true,
has_avx512_int: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "2",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "3",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "4",
capabilities: PortCapabilities {
has_agu_store: true,
has_store_data: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "5",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_simd_shuffle: true,
has_simd_fma: true,
has_avx512_fp: true,
has_avx512_int: true,
has_pdep_pext: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "6",
capabilities: PortCapabilities {
has_int_alu: true,
has_branch: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "7",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "8",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "9",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
],
execution_units: vec![
ExecutionUnit {
unit_type: ExecutionUnitType::AluInt,
count: 5,
typical_latency: 1,
reciprocal_throughput: 0.2,
},
ExecutionUnit {
unit_type: ExecutionUnitType::IntMultiplier,
count: 3,
typical_latency: 3,
reciprocal_throughput: 1.0 / 3.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::IntDivider,
count: 1,
typical_latency: 12,
reciprocal_throughput: 8.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguLoad,
count: 2,
typical_latency: 5,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguStore,
count: 4,
typical_latency: 1,
reciprocal_throughput: 0.25,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuAdd,
count: 2,
typical_latency: 3,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuMul,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuFma,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuDiv,
count: 1,
typical_latency: 14,
reciprocal_throughput: 8.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::SimdFma,
count: 3,
typical_latency: 4,
reciprocal_throughput: 1.0 / 3.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::SimdShuffle,
count: 2,
typical_latency: 1,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::Branch,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
],
scheduler: SchedulerConfig {
kind: SchedulerKind::Distributed { entries: &[512] },
total_entries: 512,
},
buffers: BufferConfig {
rob_size: 512,
load_buffer: 192,
store_buffer: 114,
line_fill_buffer: 16,
uop_queue_size: 72,
},
caches: CacheConfig {
l1i: CacheLevel {
size: 32768,
associativity: 8,
latency: 4,
line_size: 64,
},
l1d: CacheLevel {
size: 49152,
associativity: 12,
latency: 5,
line_size: 64,
},
l2: CacheLevel {
size: 1310720,
associativity: 10,
latency: 14,
line_size: 64,
},
l3: Some(CacheLevel {
size: 31457280,
associativity: 15,
latency: 44,
line_size: 64,
}),
l2_inclusive: false,
l3_inclusive: false,
llc_topology: LlcTopology::Ring,
},
tlbs: TlbConfig {
itlb: vec![
TlbLevel {
entries: 256,
associativity: 8,
page_sizes: "4K",
},
TlbLevel {
entries: 32,
associativity: 0,
page_sizes: "2M/4M",
},
],
dtlb: vec![
TlbLevel {
entries: 96,
associativity: 0,
page_sizes: "4K",
},
TlbLevel {
entries: 32,
associativity: 0,
page_sizes: "2M/4M",
},
],
stlb: Some(TlbLevel {
entries: 4096,
associativity: 16,
page_sizes: "4K+2M+1G",
}),
},
branch_predictor: BranchPredictorConfig {
predictor_kind: BranchPredictorKind::TAGE,
btb_entries: 12288,
rsb_entries: 32,
indirect_branch_entries: 2048,
mispredict_penalty: 19,
prediction_accuracy_percent: 98.0,
},
features: MicroArchFeatures {
uop_cache_entries: 4096,
uop_cache_associativity: 8,
has_lsd: false,
has_macro_fusion: true,
has_micro_fusion: true,
has_avx512: true,
has_amx: true,
has_smt: true,
smt_threads: 2,
phys_reg_int: 512,
phys_reg_fp: 320,
},
}
}
pub fn alder_lake_ecore() -> X86MicroArch {
X86MicroArch {
kind: X86MicroArchKind::AlderLakeEcore,
pipeline: PipelineConfig {
decode_width: 6,
dispatch_width: 6,
issue_width: 8,
retire_width: 8,
pipeline_depth: 13,
out_of_order: true,
},
ports: vec![
ExecutionPort {
id: "0",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_int: true,
has_simd_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "1",
capabilities: PortCapabilities {
has_int_alu: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "2",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "3",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "4",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "5",
capabilities: PortCapabilities {
has_int_alu: true,
has_simd_shuffle: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "6",
capabilities: PortCapabilities {
has_int_alu: true,
has_branch: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "7",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
],
execution_units: vec![
ExecutionUnit {
unit_type: ExecutionUnitType::AluInt,
count: 4,
typical_latency: 1,
reciprocal_throughput: 0.25,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguLoad,
count: 2,
typical_latency: 5,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguStore,
count: 2,
typical_latency: 1,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuAdd,
count: 2,
typical_latency: 3,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuMul,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::Branch,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
],
scheduler: SchedulerConfig {
kind: SchedulerKind::Distributed { entries: &[208] },
total_entries: 208,
},
buffers: BufferConfig {
rob_size: 256,
load_buffer: 32,
store_buffer: 22,
line_fill_buffer: 8,
uop_queue_size: 48,
},
caches: CacheConfig {
l1i: CacheLevel {
size: 65536,
associativity: 8,
latency: 4,
line_size: 64,
},
l1d: CacheLevel {
size: 32768,
associativity: 8,
latency: 5,
line_size: 64,
},
l2: CacheLevel {
size: 2097152,
associativity: 16,
latency: 17,
line_size: 64,
},
l3: Some(CacheLevel {
size: 3145728,
associativity: 12,
latency: 38,
line_size: 64,
}),
l2_inclusive: false,
l3_inclusive: false,
llc_topology: LlcTopology::Ring,
},
tlbs: TlbConfig {
itlb: vec![
TlbLevel {
entries: 64,
associativity: 8,
page_sizes: "4K",
},
TlbLevel {
entries: 8,
associativity: 0,
page_sizes: "2M",
},
],
dtlb: vec![
TlbLevel {
entries: 64,
associativity: 4,
page_sizes: "4K",
},
TlbLevel {
entries: 16,
associativity: 0,
page_sizes: "2M",
},
],
stlb: Some(TlbLevel {
entries: 1024,
associativity: 12,
page_sizes: "4K+2M",
}),
},
branch_predictor: BranchPredictorConfig {
predictor_kind: BranchPredictorKind::TAGE,
btb_entries: 6144,
rsb_entries: 16,
indirect_branch_entries: 512,
mispredict_penalty: 17,
prediction_accuracy_percent: 96.0,
},
features: MicroArchFeatures {
uop_cache_entries: 0,
uop_cache_associativity: 0,
has_lsd: false,
has_macro_fusion: true,
has_micro_fusion: true,
has_avx512: false,
has_amx: false,
has_smt: false,
smt_threads: 1,
phys_reg_int: 200,
phys_reg_fp: 160,
},
}
}
pub fn raptor_lake_pcore() -> X86MicroArch {
let mut m = alder_lake_pcore();
m.kind = X86MicroArchKind::RaptorLakePcore;
m.caches.l2 = CacheLevel {
size: 2097152,
associativity: 16,
latency: 15,
line_size: 64,
};
m.caches.l3 = Some(CacheLevel {
size: 37748736,
associativity: 18,
latency: 46,
line_size: 64,
});
m
}
pub fn raptor_lake_ecore() -> X86MicroArch {
let mut m = alder_lake_ecore();
m.kind = X86MicroArchKind::RaptorLakeEcore;
m.caches.l2 = CacheLevel {
size: 4194304,
associativity: 16,
latency: 17,
line_size: 64,
};
m
}
pub fn sapphire_rapids() -> X86MicroArch {
let mut m = alder_lake_pcore();
m.kind = X86MicroArchKind::SapphireRapids;
m.buffers.rob_size = 512;
m.buffers.load_buffer = 192;
m.buffers.store_buffer = 114;
m.caches.l2 = CacheLevel {
size: 2097152,
associativity: 16,
latency: 15,
line_size: 64,
};
m.caches.l3 = Some(CacheLevel {
size: 188743680,
associativity: 15,
latency: 55,
line_size: 64,
});
m.caches.llc_topology = LlcTopology::Mesh;
m
}
pub fn emerald_rapids() -> X86MicroArch {
let mut m = sapphire_rapids();
m.kind = X86MicroArchKind::EmeraldRapids;
m.caches.l3 = Some(CacheLevel {
size: 335544320,
associativity: 15,
latency: 58,
line_size: 64,
});
m
}
pub fn granite_rapids() -> X86MicroArch {
X86MicroArch {
kind: X86MicroArchKind::GraniteRapids,
pipeline: PipelineConfig {
decode_width: 8,
dispatch_width: 8,
issue_width: 12,
retire_width: 8,
pipeline_depth: 15,
out_of_order: true,
},
ports: vec![
ExecutionPort {
id: "0",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_int_div: true,
has_fpu_add: true,
has_fpu_mul: true,
has_fpu_div: true,
has_simd_int: true,
has_simd_fp: true,
has_simd_fma: true,
has_avx512_fp: true,
has_avx512_int: true,
has_pdep_pext: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "1",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_int: true,
has_simd_fp: true,
has_simd_fma: true,
has_avx512_fp: true,
has_avx512_int: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "2",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "3",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "4",
capabilities: PortCapabilities {
has_agu_store: true,
has_store_data: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "5",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_simd_shuffle: true,
has_simd_fma: true,
has_avx512_fp: true,
has_avx512_int: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "6",
capabilities: PortCapabilities {
has_int_alu: true,
has_branch: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "7",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "8",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "9",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "10",
capabilities: PortCapabilities {
has_simd_fma: true,
has_avx512_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "11",
capabilities: PortCapabilities {
has_simd_fma: true,
has_avx512_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
],
execution_units: vec![
ExecutionUnit {
unit_type: ExecutionUnitType::AluInt,
count: 5,
typical_latency: 1,
reciprocal_throughput: 0.2,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguLoad,
count: 2,
typical_latency: 5,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguStore,
count: 4,
typical_latency: 1,
reciprocal_throughput: 0.25,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuFma,
count: 4,
typical_latency: 4,
reciprocal_throughput: 0.25,
},
ExecutionUnit {
unit_type: ExecutionUnitType::SimdFma,
count: 4,
typical_latency: 4,
reciprocal_throughput: 0.25,
},
ExecutionUnit {
unit_type: ExecutionUnitType::Branch,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
],
scheduler: SchedulerConfig {
kind: SchedulerKind::Distributed { entries: &[576] },
total_entries: 576,
},
buffers: BufferConfig {
rob_size: 576,
load_buffer: 240,
store_buffer: 128,
line_fill_buffer: 20,
uop_queue_size: 80,
},
caches: CacheConfig {
l1i: CacheLevel {
size: 65536,
associativity: 8,
latency: 4,
line_size: 64,
},
l1d: CacheLevel {
size: 49152,
associativity: 12,
latency: 5,
line_size: 64,
},
l2: CacheLevel {
size: 2097152,
associativity: 16,
latency: 15,
line_size: 64,
},
l3: Some(CacheLevel {
size: 503316480,
associativity: 15,
latency: 60,
line_size: 64,
}),
l2_inclusive: false,
l3_inclusive: false,
llc_topology: LlcTopology::Mesh,
},
tlbs: TlbConfig {
itlb: vec![
TlbLevel {
entries: 256,
associativity: 8,
page_sizes: "4K",
},
TlbLevel {
entries: 32,
associativity: 0,
page_sizes: "2M/4M",
},
],
dtlb: vec![
TlbLevel {
entries: 96,
associativity: 0,
page_sizes: "4K",
},
TlbLevel {
entries: 32,
associativity: 0,
page_sizes: "2M/4M",
},
],
stlb: Some(TlbLevel {
entries: 8192,
associativity: 16,
page_sizes: "4K+2M+1G",
}),
},
branch_predictor: BranchPredictorConfig {
predictor_kind: BranchPredictorKind::TAGE,
btb_entries: 16384,
rsb_entries: 40,
indirect_branch_entries: 4096,
mispredict_penalty: 20,
prediction_accuracy_percent: 98.5,
},
features: MicroArchFeatures {
uop_cache_entries: 4096,
uop_cache_associativity: 8,
has_lsd: false,
has_macro_fusion: true,
has_micro_fusion: true,
has_avx512: true,
has_amx: true,
has_smt: true,
smt_threads: 2,
phys_reg_int: 512,
phys_reg_fp: 384,
},
}
}
pub fn sierra_forest() -> X86MicroArch {
X86MicroArch {
kind: X86MicroArchKind::SierraForest,
pipeline: PipelineConfig {
decode_width: 6,
dispatch_width: 6,
issue_width: 8,
retire_width: 8,
pipeline_depth: 14,
out_of_order: true,
},
ports: vec![
ExecutionPort {
id: "0",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_int: true,
has_simd_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "1",
capabilities: PortCapabilities {
has_int_alu: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "2",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "3",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "4",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "5",
capabilities: PortCapabilities {
has_int_alu: true,
has_simd_shuffle: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "6",
capabilities: PortCapabilities {
has_int_alu: true,
has_branch: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "7",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
],
execution_units: vec![
ExecutionUnit {
unit_type: ExecutionUnitType::AluInt,
count: 4,
typical_latency: 1,
reciprocal_throughput: 0.25,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguLoad,
count: 2,
typical_latency: 5,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguStore,
count: 2,
typical_latency: 1,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuAdd,
count: 2,
typical_latency: 3,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuMul,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::Branch,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
],
scheduler: SchedulerConfig {
kind: SchedulerKind::Distributed { entries: &[288] },
total_entries: 288,
},
buffers: BufferConfig {
rob_size: 288,
load_buffer: 40,
store_buffer: 28,
line_fill_buffer: 10,
uop_queue_size: 64,
},
caches: CacheConfig {
l1i: CacheLevel {
size: 65536,
associativity: 8,
latency: 4,
line_size: 64,
},
l1d: CacheLevel {
size: 32768,
associativity: 8,
latency: 5,
line_size: 64,
},
l2: CacheLevel {
size: 4194304,
associativity: 16,
latency: 18,
line_size: 64,
},
l3: Some(CacheLevel {
size: 100663296,
associativity: 15,
latency: 42,
line_size: 64,
}),
l2_inclusive: false,
l3_inclusive: false,
llc_topology: LlcTopology::Mesh,
},
tlbs: TlbConfig {
itlb: vec![
TlbLevel {
entries: 64,
associativity: 8,
page_sizes: "4K",
},
TlbLevel {
entries: 16,
associativity: 0,
page_sizes: "2M",
},
],
dtlb: vec![
TlbLevel {
entries: 64,
associativity: 4,
page_sizes: "4K",
},
TlbLevel {
entries: 32,
associativity: 0,
page_sizes: "2M",
},
],
stlb: Some(TlbLevel {
entries: 2048,
associativity: 12,
page_sizes: "4K+2M",
}),
},
branch_predictor: BranchPredictorConfig {
predictor_kind: BranchPredictorKind::TAGE,
btb_entries: 8192,
rsb_entries: 20,
indirect_branch_entries: 1024,
mispredict_penalty: 18,
prediction_accuracy_percent: 96.5,
},
features: MicroArchFeatures {
uop_cache_entries: 0,
uop_cache_associativity: 0,
has_lsd: false,
has_macro_fusion: true,
has_micro_fusion: true,
has_avx512: false,
has_amx: false,
has_smt: false,
smt_threads: 1,
phys_reg_int: 256,
phys_reg_fp: 192,
},
}
}
pub fn zen1() -> X86MicroArch {
X86MicroArch {
kind: X86MicroArchKind::Zen1,
pipeline: PipelineConfig {
decode_width: 4,
dispatch_width: 6,
issue_width: 6,
retire_width: 8,
pipeline_depth: 19,
out_of_order: true,
},
ports: vec![
ExecutionPort {
id: "ALU0",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_int_div: true,
has_branch: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "ALU1",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "ALU2",
capabilities: PortCapabilities {
has_int_alu: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "ALU3",
capabilities: PortCapabilities {
has_int_alu: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "AGU0",
capabilities: PortCapabilities {
has_agu_load: true,
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "AGU1",
capabilities: PortCapabilities {
has_agu_load: true,
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU0",
capabilities: PortCapabilities {
has_fpu_mul: true,
has_simd_fp: true,
has_simd_fma: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU1",
capabilities: PortCapabilities {
has_fpu_add: true,
has_simd_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU2",
capabilities: PortCapabilities {
has_fpu_mul: true,
has_simd_fp: true,
has_simd_fma: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU3",
capabilities: PortCapabilities {
has_fpu_add: true,
has_fpu_div: true,
has_simd_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
],
execution_units: vec![
ExecutionUnit {
unit_type: ExecutionUnitType::AluInt,
count: 4,
typical_latency: 1,
reciprocal_throughput: 0.25,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguLoad,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguStore,
count: 2,
typical_latency: 1,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuAdd,
count: 2,
typical_latency: 3,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuMul,
count: 2,
typical_latency: 3,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuFma,
count: 2,
typical_latency: 5,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuDiv,
count: 1,
typical_latency: 10,
reciprocal_throughput: 8.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::Branch,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
],
scheduler: SchedulerConfig {
kind: SchedulerKind::Distributed { entries: &[84, 36] },
total_entries: 120,
},
buffers: BufferConfig {
rob_size: 192,
load_buffer: 72,
store_buffer: 44,
line_fill_buffer: 8,
uop_queue_size: 28,
},
caches: CacheConfig {
l1i: CacheLevel {
size: 65536,
associativity: 4,
latency: 4,
line_size: 64,
},
l1d: CacheLevel {
size: 32768,
associativity: 8,
latency: 4,
line_size: 64,
},
l2: CacheLevel {
size: 524288,
associativity: 8,
latency: 12,
line_size: 64,
},
l3: Some(CacheLevel {
size: 8388608,
associativity: 16,
latency: 35,
line_size: 64,
}),
l2_inclusive: false,
l3_inclusive: false,
llc_topology: LlcTopology::PerCcx,
},
tlbs: TlbConfig {
itlb: vec![TlbLevel {
entries: 64,
associativity: 0,
page_sizes: "4K+2M+1G",
}],
dtlb: vec![TlbLevel {
entries: 64,
associativity: 0,
page_sizes: "4K+2M+1G",
}],
stlb: None,
},
branch_predictor: BranchPredictorConfig {
predictor_kind: BranchPredictorKind::Perceptron,
btb_entries: 4096,
rsb_entries: 32,
indirect_branch_entries: 1024,
mispredict_penalty: 19,
prediction_accuracy_percent: 97.0,
},
features: MicroArchFeatures {
uop_cache_entries: 2048,
uop_cache_associativity: 8,
has_lsd: false,
has_macro_fusion: true,
has_micro_fusion: true,
has_avx512: false,
has_amx: false,
has_smt: true,
smt_threads: 2,
phys_reg_int: 168,
phys_reg_fp: 160,
},
}
}
pub fn zen_plus() -> X86MicroArch {
let mut m = zen1();
m.kind = X86MicroArchKind::ZenPlus;
m.caches.l2 = CacheLevel {
size: 524288,
associativity: 8,
latency: 11,
line_size: 64,
};
m.caches.l3 = Some(CacheLevel {
size: 8388608,
associativity: 16,
latency: 33,
line_size: 64,
});
m
}
pub fn zen2() -> X86MicroArch {
X86MicroArch {
kind: X86MicroArchKind::Zen2,
pipeline: PipelineConfig {
decode_width: 4,
dispatch_width: 6,
issue_width: 7,
retire_width: 8,
pipeline_depth: 19,
out_of_order: true,
},
ports: vec![
ExecutionPort {
id: "ALU0",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_int_div: true,
has_branch: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "ALU1",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "ALU2",
capabilities: PortCapabilities {
has_int_alu: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "ALU3",
capabilities: PortCapabilities {
has_int_alu: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "AGU0",
capabilities: PortCapabilities {
has_agu_load: true,
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "AGU1",
capabilities: PortCapabilities {
has_agu_load: true,
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "AGU2",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU0",
capabilities: PortCapabilities {
has_fpu_mul: true,
has_simd_fp: true,
has_simd_fma: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU1",
capabilities: PortCapabilities {
has_fpu_add: true,
has_simd_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU2",
capabilities: PortCapabilities {
has_fpu_mul: true,
has_simd_fp: true,
has_simd_fma: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU3",
capabilities: PortCapabilities {
has_fpu_add: true,
has_fpu_div: true,
has_simd_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
],
execution_units: vec![
ExecutionUnit {
unit_type: ExecutionUnitType::AluInt,
count: 4,
typical_latency: 1,
reciprocal_throughput: 0.25,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguLoad,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguStore,
count: 3,
typical_latency: 1,
reciprocal_throughput: 1.0 / 3.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuAdd,
count: 2,
typical_latency: 3,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuMul,
count: 2,
typical_latency: 3,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuFma,
count: 2,
typical_latency: 5,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuDiv,
count: 1,
typical_latency: 10,
reciprocal_throughput: 8.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::Branch,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
],
scheduler: SchedulerConfig {
kind: SchedulerKind::Distributed { entries: &[92, 36] },
total_entries: 128,
},
buffers: BufferConfig {
rob_size: 224,
load_buffer: 84,
store_buffer: 48,
line_fill_buffer: 8,
uop_queue_size: 28,
},
caches: CacheConfig {
l1i: CacheLevel {
size: 32768,
associativity: 8,
latency: 4,
line_size: 64,
},
l1d: CacheLevel {
size: 32768,
associativity: 8,
latency: 4,
line_size: 64,
},
l2: CacheLevel {
size: 524288,
associativity: 8,
latency: 12,
line_size: 64,
},
l3: Some(CacheLevel {
size: 16777216,
associativity: 16,
latency: 37,
line_size: 64,
}),
l2_inclusive: false,
l3_inclusive: false,
llc_topology: LlcTopology::PerCcx,
},
tlbs: TlbConfig {
itlb: vec![TlbLevel {
entries: 64,
associativity: 0,
page_sizes: "4K+2M+1G",
}],
dtlb: vec![TlbLevel {
entries: 64,
associativity: 0,
page_sizes: "4K+2M+1G",
}],
stlb: None,
},
branch_predictor: BranchPredictorConfig {
predictor_kind: BranchPredictorKind::TAGE,
btb_entries: 6144,
rsb_entries: 32,
indirect_branch_entries: 1024,
mispredict_penalty: 19,
prediction_accuracy_percent: 97.5,
},
features: MicroArchFeatures {
uop_cache_entries: 4096,
uop_cache_associativity: 8,
has_lsd: false,
has_macro_fusion: true,
has_micro_fusion: true,
has_avx512: false,
has_amx: false,
has_smt: true,
smt_threads: 2,
phys_reg_int: 180,
phys_reg_fp: 160,
},
}
}
pub fn zen3() -> X86MicroArch {
X86MicroArch {
kind: X86MicroArchKind::Zen3,
pipeline: PipelineConfig {
decode_width: 4,
dispatch_width: 6,
issue_width: 8,
retire_width: 8,
pipeline_depth: 19,
out_of_order: true,
},
ports: vec![
ExecutionPort {
id: "ALU0",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_int_div: true,
has_branch: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "ALU1",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "ALU2",
capabilities: PortCapabilities {
has_int_alu: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "ALU3",
capabilities: PortCapabilities {
has_int_alu: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "AGU0",
capabilities: PortCapabilities {
has_agu_load: true,
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "AGU1",
capabilities: PortCapabilities {
has_agu_load: true,
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "AGU2",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU0",
capabilities: PortCapabilities {
has_fpu_mul: true,
has_simd_fp: true,
has_simd_fma: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU1",
capabilities: PortCapabilities {
has_fpu_add: true,
has_simd_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU2",
capabilities: PortCapabilities {
has_fpu_mul: true,
has_simd_fp: true,
has_simd_fma: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU3",
capabilities: PortCapabilities {
has_fpu_add: true,
has_fpu_div: true,
has_simd_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
],
execution_units: vec![
ExecutionUnit {
unit_type: ExecutionUnitType::AluInt,
count: 4,
typical_latency: 1,
reciprocal_throughput: 0.25,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguLoad,
count: 2,
typical_latency: 3,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguStore,
count: 3,
typical_latency: 1,
reciprocal_throughput: 1.0 / 3.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuAdd,
count: 2,
typical_latency: 3,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuMul,
count: 2,
typical_latency: 3,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuFma,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuDiv,
count: 1,
typical_latency: 10,
reciprocal_throughput: 8.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::Branch,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
],
scheduler: SchedulerConfig {
kind: SchedulerKind::Distributed { entries: &[96, 64] },
total_entries: 160,
},
buffers: BufferConfig {
rob_size: 256,
load_buffer: 116,
store_buffer: 64,
line_fill_buffer: 10,
uop_queue_size: 28,
},
caches: CacheConfig {
l1i: CacheLevel {
size: 32768,
associativity: 8,
latency: 4,
line_size: 64,
},
l1d: CacheLevel {
size: 32768,
associativity: 8,
latency: 3,
line_size: 64,
},
l2: CacheLevel {
size: 524288,
associativity: 8,
latency: 12,
line_size: 64,
},
l3: Some(CacheLevel {
size: 33554432,
associativity: 16,
latency: 40,
line_size: 64,
}),
l2_inclusive: false,
l3_inclusive: false,
llc_topology: LlcTopology::PerCcx,
},
tlbs: TlbConfig {
itlb: vec![TlbLevel {
entries: 64,
associativity: 0,
page_sizes: "4K+2M+1G",
}],
dtlb: vec![TlbLevel {
entries: 72,
associativity: 0,
page_sizes: "4K+2M+1G",
}],
stlb: None,
},
branch_predictor: BranchPredictorConfig {
predictor_kind: BranchPredictorKind::TAGE,
btb_entries: 8192,
rsb_entries: 32,
indirect_branch_entries: 1024,
mispredict_penalty: 19,
prediction_accuracy_percent: 97.8,
},
features: MicroArchFeatures {
uop_cache_entries: 4096,
uop_cache_associativity: 8,
has_lsd: false,
has_macro_fusion: true,
has_micro_fusion: true,
has_avx512: false,
has_amx: false,
has_smt: true,
smt_threads: 2,
phys_reg_int: 192,
phys_reg_fp: 160,
},
}
}
pub fn zen4() -> X86MicroArch {
X86MicroArch {
kind: X86MicroArchKind::Zen4,
pipeline: PipelineConfig {
decode_width: 4,
dispatch_width: 6,
issue_width: 9,
retire_width: 9,
pipeline_depth: 19,
out_of_order: true,
},
ports: vec![
ExecutionPort {
id: "ALU0",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_int_div: true,
has_branch: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "ALU1",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "ALU2",
capabilities: PortCapabilities {
has_int_alu: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "ALU3",
capabilities: PortCapabilities {
has_int_alu: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "AGU0",
capabilities: PortCapabilities {
has_agu_load: true,
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "AGU1",
capabilities: PortCapabilities {
has_agu_load: true,
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "AGU2",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU0",
capabilities: PortCapabilities {
has_fpu_mul: true,
has_simd_fp: true,
has_simd_fma: true,
has_avx512_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU1",
capabilities: PortCapabilities {
has_fpu_add: true,
has_simd_fp: true,
has_avx512_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU2",
capabilities: PortCapabilities {
has_fpu_mul: true,
has_simd_fp: true,
has_simd_fma: true,
has_avx512_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU3",
capabilities: PortCapabilities {
has_fpu_add: true,
has_fpu_div: true,
has_simd_fp: true,
has_avx512_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
],
execution_units: vec![
ExecutionUnit {
unit_type: ExecutionUnitType::AluInt,
count: 4,
typical_latency: 1,
reciprocal_throughput: 0.25,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguLoad,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguStore,
count: 3,
typical_latency: 1,
reciprocal_throughput: 1.0 / 3.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuAdd,
count: 2,
typical_latency: 2,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuMul,
count: 2,
typical_latency: 3,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuFma,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuDiv,
count: 1,
typical_latency: 13,
reciprocal_throughput: 8.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::Branch,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
],
scheduler: SchedulerConfig {
kind: SchedulerKind::Distributed { entries: &[96, 64] },
total_entries: 160,
},
buffers: BufferConfig {
rob_size: 320,
load_buffer: 118,
store_buffer: 64,
line_fill_buffer: 12,
uop_queue_size: 28,
},
caches: CacheConfig {
l1i: CacheLevel {
size: 32768,
associativity: 8,
latency: 4,
line_size: 64,
},
l1d: CacheLevel {
size: 32768,
associativity: 8,
latency: 4,
line_size: 64,
},
l2: CacheLevel {
size: 1048576,
associativity: 8,
latency: 12,
line_size: 64,
},
l3: Some(CacheLevel {
size: 33554432,
associativity: 16,
latency: 42,
line_size: 64,
}),
l2_inclusive: false,
l3_inclusive: false,
llc_topology: LlcTopology::PerCcx,
},
tlbs: TlbConfig {
itlb: vec![TlbLevel {
entries: 64,
associativity: 0,
page_sizes: "4K+2M+1G",
}],
dtlb: vec![TlbLevel {
entries: 72,
associativity: 0,
page_sizes: "4K+2M+1G",
}],
stlb: None,
},
branch_predictor: BranchPredictorConfig {
predictor_kind: BranchPredictorKind::TAGE,
btb_entries: 8192,
rsb_entries: 32,
indirect_branch_entries: 1536,
mispredict_penalty: 19,
prediction_accuracy_percent: 98.0,
},
features: MicroArchFeatures {
uop_cache_entries: 6750,
uop_cache_associativity: 8,
has_lsd: false,
has_macro_fusion: true,
has_micro_fusion: true,
has_avx512: true,
has_amx: false,
has_smt: true,
smt_threads: 2,
phys_reg_int: 224,
phys_reg_fp: 192,
},
}
}
pub fn zen5() -> X86MicroArch {
X86MicroArch {
kind: X86MicroArchKind::Zen5,
pipeline: PipelineConfig {
decode_width: 8,
dispatch_width: 8,
issue_width: 10,
retire_width: 10,
pipeline_depth: 19,
out_of_order: true,
},
ports: vec![
ExecutionPort {
id: "ALU0",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_int_div: true,
has_branch: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "ALU1",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "ALU2",
capabilities: PortCapabilities {
has_int_alu: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "ALU3",
capabilities: PortCapabilities {
has_int_alu: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "ALU4",
capabilities: PortCapabilities {
has_int_alu: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "ALU5",
capabilities: PortCapabilities {
has_int_alu: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "AGU0",
capabilities: PortCapabilities {
has_agu_load: true,
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "AGU1",
capabilities: PortCapabilities {
has_agu_load: true,
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "AGU2",
capabilities: PortCapabilities {
has_agu_load: true,
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "AGU3",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU0",
capabilities: PortCapabilities {
has_fpu_mul: true,
has_simd_fp: true,
has_simd_fma: true,
has_avx512_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU1",
capabilities: PortCapabilities {
has_fpu_add: true,
has_simd_fp: true,
has_avx512_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU2",
capabilities: PortCapabilities {
has_fpu_mul: true,
has_simd_fp: true,
has_simd_fma: true,
has_avx512_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU3",
capabilities: PortCapabilities {
has_fpu_add: true,
has_fpu_div: true,
has_simd_fp: true,
has_avx512_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU4",
capabilities: PortCapabilities {
has_fpu_mul: true,
has_simd_fp: true,
has_simd_fma: true,
has_avx512_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "FPU5",
capabilities: PortCapabilities {
has_fpu_add: true,
has_simd_fp: true,
has_avx512_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
],
execution_units: vec![
ExecutionUnit {
unit_type: ExecutionUnitType::AluInt,
count: 6,
typical_latency: 1,
reciprocal_throughput: 1.0 / 6.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguLoad,
count: 3,
typical_latency: 4,
reciprocal_throughput: 1.0 / 3.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguStore,
count: 4,
typical_latency: 1,
reciprocal_throughput: 0.25,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuAdd,
count: 3,
typical_latency: 2,
reciprocal_throughput: 1.0 / 3.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuMul,
count: 3,
typical_latency: 3,
reciprocal_throughput: 1.0 / 3.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuFma,
count: 3,
typical_latency: 4,
reciprocal_throughput: 1.0 / 3.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuDiv,
count: 1,
typical_latency: 13,
reciprocal_throughput: 6.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::Branch,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
],
scheduler: SchedulerConfig {
kind: SchedulerKind::Distributed {
entries: &[128, 80],
},
total_entries: 208,
},
buffers: BufferConfig {
rob_size: 448,
load_buffer: 144,
store_buffer: 88,
line_fill_buffer: 16,
uop_queue_size: 32,
},
caches: CacheConfig {
l1i: CacheLevel {
size: 32768,
associativity: 8,
latency: 4,
line_size: 64,
},
l1d: CacheLevel {
size: 49152,
associativity: 12,
latency: 4,
line_size: 64,
},
l2: CacheLevel {
size: 1048576,
associativity: 16,
latency: 12,
line_size: 64,
},
l3: Some(CacheLevel {
size: 33554432,
associativity: 16,
latency: 44,
line_size: 64,
}),
l2_inclusive: false,
l3_inclusive: false,
llc_topology: LlcTopology::PerCcx,
},
tlbs: TlbConfig {
itlb: vec![TlbLevel {
entries: 64,
associativity: 0,
page_sizes: "4K+2M+1G",
}],
dtlb: vec![TlbLevel {
entries: 96,
associativity: 0,
page_sizes: "4K+2M+1G",
}],
stlb: None,
},
branch_predictor: BranchPredictorConfig {
predictor_kind: BranchPredictorKind::TAGE,
btb_entries: 12288,
rsb_entries: 48,
indirect_branch_entries: 2048,
mispredict_penalty: 19,
prediction_accuracy_percent: 98.5,
},
features: MicroArchFeatures {
uop_cache_entries: 6750,
uop_cache_associativity: 8,
has_lsd: false,
has_macro_fusion: true,
has_micro_fusion: true,
has_avx512: true,
has_amx: false,
has_smt: true,
smt_threads: 2,
phys_reg_int: 288,
phys_reg_fp: 256,
},
}
}
pub fn zen5c() -> X86MicroArch {
let mut m = zen5();
m.kind = X86MicroArchKind::Zen5c;
m.caches.l2 = CacheLevel {
size: 1048576,
associativity: 8,
latency: 15,
line_size: 64,
};
m.caches.l3 = Some(CacheLevel {
size: 16777216,
associativity: 16,
latency: 48,
line_size: 64,
});
m.scheduler = SchedulerConfig {
kind: SchedulerKind::Distributed { entries: &[96, 64] },
total_entries: 160,
};
m
}
pub fn silvermont() -> X86MicroArch {
X86MicroArch {
kind: X86MicroArchKind::Silvermont,
pipeline: PipelineConfig {
decode_width: 2,
dispatch_width: 2,
issue_width: 2,
retire_width: 2,
pipeline_depth: 14,
out_of_order: true,
},
ports: vec![
ExecutionPort {
id: "0",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_int_div: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_int: true,
has_simd_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "1",
capabilities: PortCapabilities {
has_int_alu: true,
has_branch: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "2",
capabilities: PortCapabilities {
has_agu_load: true,
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "3",
capabilities: PortCapabilities {
has_agu_load: true,
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
],
execution_units: vec![
ExecutionUnit {
unit_type: ExecutionUnitType::AluInt,
count: 2,
typical_latency: 1,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguLoad,
count: 2,
typical_latency: 3,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguStore,
count: 2,
typical_latency: 1,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuAdd,
count: 1,
typical_latency: 4,
reciprocal_throughput: 1.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuMul,
count: 1,
typical_latency: 4,
reciprocal_throughput: 1.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::Branch,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
],
scheduler: SchedulerConfig {
kind: SchedulerKind::Unified { size: 32 },
total_entries: 32,
},
buffers: BufferConfig {
rob_size: 32,
load_buffer: 10,
store_buffer: 8,
line_fill_buffer: 4,
uop_queue_size: 0,
},
caches: CacheConfig {
l1i: CacheLevel {
size: 32768,
associativity: 8,
latency: 3,
line_size: 64,
},
l1d: CacheLevel {
size: 24576,
associativity: 6,
latency: 3,
line_size: 64,
},
l2: CacheLevel {
size: 1048576,
associativity: 16,
latency: 16,
line_size: 64,
},
l3: None,
l2_inclusive: false,
l3_inclusive: false,
llc_topology: LlcTopology::None,
},
tlbs: TlbConfig {
itlb: vec![TlbLevel {
entries: 32,
associativity: 0,
page_sizes: "4K+2M",
}],
dtlb: vec![
TlbLevel {
entries: 32,
associativity: 0,
page_sizes: "4K",
},
TlbLevel {
entries: 8,
associativity: 0,
page_sizes: "2M",
},
],
stlb: None,
},
branch_predictor: BranchPredictorConfig {
predictor_kind: BranchPredictorKind::TwoLevel,
btb_entries: 512,
rsb_entries: 8,
indirect_branch_entries: 0,
mispredict_penalty: 14,
prediction_accuracy_percent: 92.0,
},
features: MicroArchFeatures {
uop_cache_entries: 0,
uop_cache_associativity: 0,
has_lsd: false,
has_macro_fusion: false,
has_micro_fusion: false,
has_avx512: false,
has_amx: false,
has_smt: false,
smt_threads: 1,
phys_reg_int: 32,
phys_reg_fp: 32,
},
}
}
pub fn airmont() -> X86MicroArch {
let mut m = silvermont();
m.kind = X86MicroArchKind::Airmont;
m.caches.l2 = CacheLevel {
size: 1048576,
associativity: 16,
latency: 14,
line_size: 64,
};
m
}
pub fn goldmont() -> X86MicroArch {
X86MicroArch {
kind: X86MicroArchKind::Goldmont,
pipeline: PipelineConfig {
decode_width: 3,
dispatch_width: 3,
issue_width: 3,
retire_width: 3,
pipeline_depth: 15,
out_of_order: true,
},
ports: vec![
ExecutionPort {
id: "0",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_int_div: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_int: true,
has_simd_fp: true,
has_branch: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "1",
capabilities: PortCapabilities {
has_int_alu: true,
has_fpu_add: true,
has_simd_int: true,
has_simd_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "2",
capabilities: PortCapabilities {
has_agu_load: true,
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "3",
capabilities: PortCapabilities {
has_agu_load: true,
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
],
execution_units: vec![
ExecutionUnit {
unit_type: ExecutionUnitType::AluInt,
count: 2,
typical_latency: 1,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguLoad,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguStore,
count: 2,
typical_latency: 1,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuAdd,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuMul,
count: 1,
typical_latency: 4,
reciprocal_throughput: 1.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::Branch,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
],
scheduler: SchedulerConfig {
kind: SchedulerKind::Unified { size: 32 },
total_entries: 32,
},
buffers: BufferConfig {
rob_size: 72,
load_buffer: 12,
store_buffer: 10,
line_fill_buffer: 6,
uop_queue_size: 0,
},
caches: CacheConfig {
l1i: CacheLevel {
size: 32768,
associativity: 8,
latency: 3,
line_size: 64,
},
l1d: CacheLevel {
size: 24576,
associativity: 6,
latency: 3,
line_size: 64,
},
l2: CacheLevel {
size: 1048576,
associativity: 16,
latency: 17,
line_size: 64,
},
l3: None,
l2_inclusive: false,
l3_inclusive: false,
llc_topology: LlcTopology::None,
},
tlbs: TlbConfig {
itlb: vec![TlbLevel {
entries: 48,
associativity: 0,
page_sizes: "4K+2M",
}],
dtlb: vec![
TlbLevel {
entries: 48,
associativity: 0,
page_sizes: "4K",
},
TlbLevel {
entries: 8,
associativity: 0,
page_sizes: "2M",
},
],
stlb: None,
},
branch_predictor: BranchPredictorConfig {
predictor_kind: BranchPredictorKind::Tournament,
btb_entries: 1024,
rsb_entries: 12,
indirect_branch_entries: 0,
mispredict_penalty: 15,
prediction_accuracy_percent: 93.5,
},
features: MicroArchFeatures {
uop_cache_entries: 0,
uop_cache_associativity: 0,
has_lsd: false,
has_macro_fusion: false,
has_micro_fusion: false,
has_avx512: false,
has_amx: false,
has_smt: false,
smt_threads: 1,
phys_reg_int: 40,
phys_reg_fp: 32,
},
}
}
pub fn goldmont_plus() -> X86MicroArch {
let mut m = goldmont();
m.kind = X86MicroArchKind::GoldmontPlus;
m.buffers.rob_size = 80;
m.buffers.load_buffer = 14;
m.buffers.store_buffer = 12;
m.branch_predictor.btb_entries = 1280;
m.branch_predictor.prediction_accuracy_percent = 94.0;
m
}
pub fn tremont() -> X86MicroArch {
X86MicroArch {
kind: X86MicroArchKind::Tremont,
pipeline: PipelineConfig {
decode_width: 6,
dispatch_width: 6,
issue_width: 4,
retire_width: 4,
pipeline_depth: 13,
out_of_order: true,
},
ports: vec![
ExecutionPort {
id: "0",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_int: true,
has_simd_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "1",
capabilities: PortCapabilities {
has_int_alu: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "2",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "3",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "4",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "5",
capabilities: PortCapabilities {
has_int_alu: true,
has_simd_shuffle: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "6",
capabilities: PortCapabilities {
has_int_alu: true,
has_branch: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "7",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
],
execution_units: vec![
ExecutionUnit {
unit_type: ExecutionUnitType::AluInt,
count: 4,
typical_latency: 1,
reciprocal_throughput: 0.25,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguLoad,
count: 2,
typical_latency: 5,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguStore,
count: 2,
typical_latency: 1,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuAdd,
count: 2,
typical_latency: 3,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuMul,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::Branch,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
],
scheduler: SchedulerConfig {
kind: SchedulerKind::Distributed { entries: &[208] },
total_entries: 208,
},
buffers: BufferConfig {
rob_size: 208,
load_buffer: 32,
store_buffer: 22,
line_fill_buffer: 8,
uop_queue_size: 48,
},
caches: CacheConfig {
l1i: CacheLevel {
size: 32768,
associativity: 8,
latency: 4,
line_size: 64,
},
l1d: CacheLevel {
size: 32768,
associativity: 8,
latency: 5,
line_size: 64,
},
l2: CacheLevel {
size: 2097152,
associativity: 16,
latency: 17,
line_size: 64,
},
l3: None,
l2_inclusive: false,
l3_inclusive: false,
llc_topology: LlcTopology::None,
},
tlbs: TlbConfig {
itlb: vec![
TlbLevel {
entries: 64,
associativity: 8,
page_sizes: "4K",
},
TlbLevel {
entries: 8,
associativity: 0,
page_sizes: "2M",
},
],
dtlb: vec![
TlbLevel {
entries: 64,
associativity: 4,
page_sizes: "4K",
},
TlbLevel {
entries: 16,
associativity: 0,
page_sizes: "2M",
},
],
stlb: None,
},
branch_predictor: BranchPredictorConfig {
predictor_kind: BranchPredictorKind::Tournament,
btb_entries: 4096,
rsb_entries: 16,
indirect_branch_entries: 256,
mispredict_penalty: 16,
prediction_accuracy_percent: 95.0,
},
features: MicroArchFeatures {
uop_cache_entries: 0,
uop_cache_associativity: 0,
has_lsd: false,
has_macro_fusion: true,
has_micro_fusion: true,
has_avx512: false,
has_amx: false,
has_smt: false,
smt_threads: 1,
phys_reg_int: 168,
phys_reg_fp: 128,
},
}
}
pub fn gracemont() -> X86MicroArch {
alder_lake_ecore()
}
pub fn crestmont() -> X86MicroArch {
X86MicroArch {
kind: X86MicroArchKind::Crestmont,
pipeline: PipelineConfig {
decode_width: 6,
dispatch_width: 6,
issue_width: 8,
retire_width: 8,
pipeline_depth: 14,
out_of_order: true,
},
ports: vec![
ExecutionPort {
id: "0",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_int: true,
has_simd_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "1",
capabilities: PortCapabilities {
has_int_alu: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_fp: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "2",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "3",
capabilities: PortCapabilities {
has_agu_load: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "4",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "5",
capabilities: PortCapabilities {
has_int_alu: true,
has_simd_shuffle: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "6",
capabilities: PortCapabilities {
has_int_alu: true,
has_branch: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "7",
capabilities: PortCapabilities {
has_agu_store: true,
..PortCapabilities::none()
},
instance_count: 1,
},
],
execution_units: vec![
ExecutionUnit {
unit_type: ExecutionUnitType::AluInt,
count: 4,
typical_latency: 1,
reciprocal_throughput: 0.25,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguLoad,
count: 2,
typical_latency: 5,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguStore,
count: 2,
typical_latency: 1,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuAdd,
count: 2,
typical_latency: 3,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuMul,
count: 2,
typical_latency: 4,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::Branch,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
],
scheduler: SchedulerConfig {
kind: SchedulerKind::Distributed { entries: &[256] },
total_entries: 256,
},
buffers: BufferConfig {
rob_size: 256,
load_buffer: 36,
store_buffer: 24,
line_fill_buffer: 8,
uop_queue_size: 48,
},
caches: CacheConfig {
l1i: CacheLevel {
size: 65536,
associativity: 8,
latency: 4,
line_size: 64,
},
l1d: CacheLevel {
size: 32768,
associativity: 8,
latency: 5,
line_size: 64,
},
l2: CacheLevel {
size: 2097152,
associativity: 16,
latency: 16,
line_size: 64,
},
l3: Some(CacheLevel {
size: 3145728,
associativity: 12,
latency: 36,
line_size: 64,
}),
l2_inclusive: false,
l3_inclusive: false,
llc_topology: LlcTopology::Ring,
},
tlbs: TlbConfig {
itlb: vec![
TlbLevel {
entries: 64,
associativity: 8,
page_sizes: "4K",
},
TlbLevel {
entries: 8,
associativity: 0,
page_sizes: "2M",
},
],
dtlb: vec![
TlbLevel {
entries: 64,
associativity: 4,
page_sizes: "4K",
},
TlbLevel {
entries: 16,
associativity: 0,
page_sizes: "2M",
},
],
stlb: Some(TlbLevel {
entries: 1024,
associativity: 12,
page_sizes: "4K+2M",
}),
},
branch_predictor: BranchPredictorConfig {
predictor_kind: BranchPredictorKind::TAGE,
btb_entries: 6144,
rsb_entries: 16,
indirect_branch_entries: 512,
mispredict_penalty: 17,
prediction_accuracy_percent: 96.0,
},
features: MicroArchFeatures {
uop_cache_entries: 0,
uop_cache_associativity: 0,
has_lsd: false,
has_macro_fusion: true,
has_micro_fusion: true,
has_avx512: false,
has_amx: false,
has_smt: false,
smt_threads: 1,
phys_reg_int: 200,
phys_reg_fp: 160,
},
}
}
pub fn knights_landing() -> X86MicroArch {
X86MicroArch {
kind: X86MicroArchKind::KnightsLanding,
pipeline: PipelineConfig {
decode_width: 2,
dispatch_width: 2,
issue_width: 2,
retire_width: 2,
pipeline_depth: 14,
out_of_order: true,
},
ports: vec![
ExecutionPort {
id: "0",
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_fpu_add: true,
has_fpu_mul: true,
has_fpu_div: true,
has_simd_int: true,
has_simd_fp: true,
has_simd_fma: true,
has_avx512_fp: true,
has_avx512_int: true,
has_branch: true,
..PortCapabilities::none()
},
instance_count: 1,
},
ExecutionPort {
id: "1",
capabilities: PortCapabilities {
has_int_alu: true,
has_agu_load: true,
has_agu_store: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_fp: true,
has_simd_fma: true,
has_avx512_fp: true,
has_avx512_int: true,
..PortCapabilities::none()
},
instance_count: 1,
},
],
execution_units: vec![
ExecutionUnit {
unit_type: ExecutionUnitType::AluInt,
count: 2,
typical_latency: 1,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguLoad,
count: 1,
typical_latency: 4,
reciprocal_throughput: 1.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::AguStore,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
ExecutionUnit {
unit_type: ExecutionUnitType::FpuFma,
count: 2,
typical_latency: 6,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::SimdFma,
count: 2,
typical_latency: 6,
reciprocal_throughput: 0.5,
},
ExecutionUnit {
unit_type: ExecutionUnitType::Branch,
count: 1,
typical_latency: 1,
reciprocal_throughput: 1.0,
},
],
scheduler: SchedulerConfig {
kind: SchedulerKind::Unified { size: 72 },
total_entries: 72,
},
buffers: BufferConfig {
rob_size: 72,
load_buffer: 32,
store_buffer: 20,
line_fill_buffer: 10,
uop_queue_size: 0,
},
caches: CacheConfig {
l1i: CacheLevel {
size: 32768,
associativity: 8,
latency: 4,
line_size: 64,
},
l1d: CacheLevel {
size: 32768,
associativity: 8,
latency: 4,
line_size: 64,
},
l2: CacheLevel {
size: 1048576,
associativity: 16,
latency: 15,
line_size: 64,
},
l3: None,
l2_inclusive: false,
l3_inclusive: false,
llc_topology: LlcTopology::Mesh,
},
tlbs: TlbConfig {
itlb: vec![
TlbLevel {
entries: 64,
associativity: 4,
page_sizes: "4K",
},
TlbLevel {
entries: 8,
associativity: 0,
page_sizes: "2M",
},
],
dtlb: vec![
TlbLevel {
entries: 64,
associativity: 4,
page_sizes: "4K",
},
TlbLevel {
entries: 8,
associativity: 0,
page_sizes: "2M",
},
],
stlb: None,
},
branch_predictor: BranchPredictorConfig {
predictor_kind: BranchPredictorKind::TwoLevel,
btb_entries: 4096,
rsb_entries: 16,
indirect_branch_entries: 0,
mispredict_penalty: 10,
prediction_accuracy_percent: 93.0,
},
features: MicroArchFeatures {
uop_cache_entries: 0,
uop_cache_associativity: 0,
has_lsd: false,
has_macro_fusion: false,
has_micro_fusion: false,
has_avx512: true,
has_amx: false,
has_smt: true,
smt_threads: 4,
phys_reg_int: 64,
phys_reg_fp: 128,
},
}
}
pub fn knights_mill() -> X86MicroArch {
let mut m = knights_landing();
m.kind = X86MicroArchKind::KnightsMill;
for unit in &mut m.execution_units {
if unit.unit_type == ExecutionUnitType::FpuFma
|| unit.unit_type == ExecutionUnitType::SimdFma
{
unit.typical_latency = 5;
}
}
m
}
#[derive(Debug, Clone)]
pub struct X86InstructionLatency {
pub opcode: u32,
pub mnemonic: &'static str,
pub latency: u32,
pub reciprocal_throughput: f64,
pub fused_uops: u32,
pub unfused_uops: u32,
pub port_usage: &'static [&'static str],
}
pub fn instruction_latencies_for(kind: X86MicroArchKind) -> Vec<X86InstructionLatency> {
match kind {
X86MicroArchKind::SandyBridge | X86MicroArchKind::IvyBridge => sandy_bridge_latencies(),
X86MicroArchKind::Haswell | X86MicroArchKind::Broadwell => haswell_latencies(),
X86MicroArchKind::SkylakeClient
| X86MicroArchKind::SkylakeServer
| X86MicroArchKind::KabyLake
| X86MicroArchKind::CoffeeLake
| X86MicroArchKind::CascadeLake
| X86MicroArchKind::CometLake => skylake_latencies(),
X86MicroArchKind::IceLakeClient
| X86MicroArchKind::IceLakeServer
| X86MicroArchKind::TigerLake
| X86MicroArchKind::RocketLake => ice_lake_latencies(),
X86MicroArchKind::AlderLakePcore
| X86MicroArchKind::RaptorLakePcore
| X86MicroArchKind::SapphireRapids
| X86MicroArchKind::EmeraldRapids => golden_cove_latencies(),
X86MicroArchKind::GraniteRapids => granite_rapids_latencies(),
X86MicroArchKind::AlderLakeEcore | X86MicroArchKind::RaptorLakeEcore => {
gracemont_latencies()
}
X86MicroArchKind::SierraForest => sierra_forest_latencies(),
X86MicroArchKind::Zen1 | X86MicroArchKind::ZenPlus => zen1_latencies(),
X86MicroArchKind::Zen2 => zen2_latencies(),
X86MicroArchKind::Zen3 => zen3_latencies(),
X86MicroArchKind::Zen4 => zen4_latencies(),
X86MicroArchKind::Zen5 | X86MicroArchKind::Zen5c => zen5_latencies(),
X86MicroArchKind::Silvermont | X86MicroArchKind::Airmont => silvermont_latencies(),
X86MicroArchKind::Goldmont | X86MicroArchKind::GoldmontPlus => goldmont_latencies(),
X86MicroArchKind::Tremont => tremont_latencies(),
X86MicroArchKind::Gracemont => gracemont_latencies(),
X86MicroArchKind::Crestmont => crestmont_latencies(),
X86MicroArchKind::KnightsLanding | X86MicroArchKind::KnightsMill => {
knights_landing_latencies()
}
}
}
pub fn lookup_latency(kind: X86MicroArchKind, opcode: u32) -> Option<X86InstructionLatency> {
instruction_latencies_for(kind)
.into_iter()
.find(|l| l.opcode == opcode)
}
fn sandy_bridge_latencies() -> Vec<X86InstructionLatency> {
vec![
X86InstructionLatency {
opcode: x86_opcodes::NOP,
mnemonic: "NOP",
latency: 0,
reciprocal_throughput: 0.25,
fused_uops: 0,
unfused_uops: 0,
port_usage: &[],
},
X86InstructionLatency {
opcode: x86_opcodes::MOV,
mnemonic: "MOV r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5"],
},
X86InstructionLatency {
opcode: x86_opcodes::ADD,
mnemonic: "ADD r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5"],
},
X86InstructionLatency {
opcode: x86_opcodes::SUB,
mnemonic: "SUB r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5"],
},
X86InstructionLatency {
opcode: x86_opcodes::AND,
mnemonic: "AND r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5"],
},
X86InstructionLatency {
opcode: x86_opcodes::OR,
mnemonic: "OR r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5"],
},
X86InstructionLatency {
opcode: x86_opcodes::XOR,
mnemonic: "XOR r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5"],
},
X86InstructionLatency {
opcode: x86_opcodes::MUL,
mnemonic: "MUL r64",
latency: 3,
reciprocal_throughput: 1.0,
fused_uops: 2,
unfused_uops: 2,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::IMUL,
mnemonic: "IMUL r,r",
latency: 3,
reciprocal_throughput: 1.0,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::DIV,
mnemonic: "DIV r64",
latency: 22,
reciprocal_throughput: 22.0,
fused_uops: 1,
unfused_uops: 10,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::SHL,
mnemonic: "SHL r,cl",
latency: 1,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1"],
},
X86InstructionLatency {
opcode: x86_opcodes::SHR,
mnemonic: "SHR r,cl",
latency: 1,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1"],
},
X86InstructionLatency {
opcode: x86_opcodes::LEA,
mnemonic: "LEA r,[r+r]",
latency: 1,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["1", "5"],
},
X86InstructionLatency {
opcode: x86_opcodes::CMP,
mnemonic: "CMP r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5"],
},
X86InstructionLatency {
opcode: x86_opcodes::TEST,
mnemonic: "TEST r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5"],
},
X86InstructionLatency {
opcode: x86_opcodes::INC,
mnemonic: "INC r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5"],
},
X86InstructionLatency {
opcode: x86_opcodes::DEC,
mnemonic: "DEC r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5"],
},
X86InstructionLatency {
opcode: x86_opcodes::NOT,
mnemonic: "NOT r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5"],
},
X86InstructionLatency {
opcode: x86_opcodes::NEG,
mnemonic: "NEG r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5"],
},
X86InstructionLatency {
opcode: x86_opcodes::ADC,
mnemonic: "ADC r,r",
latency: 2,
reciprocal_throughput: 1.0,
fused_uops: 2,
unfused_uops: 2,
port_usage: &["0", "5"],
},
X86InstructionLatency {
opcode: x86_opcodes::SBB,
mnemonic: "SBB r,r",
latency: 2,
reciprocal_throughput: 1.0,
fused_uops: 2,
unfused_uops: 2,
port_usage: &["0", "5"],
},
X86InstructionLatency {
opcode: x86_opcodes::BSWAP,
mnemonic: "BSWAP r",
latency: 1,
reciprocal_throughput: 1.0,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["1"],
},
X86InstructionLatency {
opcode: x86_opcodes::BSF,
mnemonic: "BSF r,r",
latency: 3,
reciprocal_throughput: 1.0,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::BSR,
mnemonic: "BSR r,r",
latency: 3,
reciprocal_throughput: 1.0,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::PUSH,
mnemonic: "PUSH r",
latency: 1,
reciprocal_throughput: 1.0,
fused_uops: 1,
unfused_uops: 2,
port_usage: &["2", "3", "4"],
},
X86InstructionLatency {
opcode: x86_opcodes::POP,
mnemonic: "POP r",
latency: 1,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["2", "3"],
},
X86InstructionLatency {
opcode: x86_opcodes::CALL,
mnemonic: "CALL",
latency: 1,
reciprocal_throughput: 1.0,
fused_uops: 1,
unfused_uops: 2,
port_usage: &["4", "6"],
},
X86InstructionLatency {
opcode: x86_opcodes::RET,
mnemonic: "RET",
latency: 1,
reciprocal_throughput: 1.0,
fused_uops: 1,
unfused_uops: 3,
port_usage: &["2", "6"],
},
X86InstructionLatency {
opcode: x86_opcodes::JMP,
mnemonic: "JMP",
latency: 0,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["5"],
},
X86InstructionLatency {
opcode: x86_opcodes::JE,
mnemonic: "JE/JCC (taken)",
latency: 1,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["5"],
},
]
}
fn haswell_latencies() -> Vec<X86InstructionLatency> {
let mut base = sandy_bridge_latencies();
for entry in &mut base {
match entry.opcode {
x86_opcodes::MUL => {
entry.latency = 3;
entry.reciprocal_throughput = 1.0;
entry.port_usage = &["0"];
}
x86_opcodes::IMUL => {
entry.latency = 3;
entry.reciprocal_throughput = 1.0;
entry.port_usage = &["0"];
}
x86_opcodes::DIV => {
entry.latency = 22;
entry.reciprocal_throughput = 20.0;
}
x86_opcodes::SHL | x86_opcodes::SHR => {
entry.port_usage = &["0", "6"];
}
x86_opcodes::ADC | x86_opcodes::SBB => {
entry.latency = 2;
}
_ => {}
}
}
base.push(X86InstructionLatency {
opcode: x86_opcodes::CMOVO,
mnemonic: "CMOVcc r,r",
latency: 2,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "6"],
});
base.push(X86InstructionLatency {
opcode: x86_opcodes::CMOVE,
mnemonic: "CMOVE r,r",
latency: 2,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "6"],
});
base
}
fn skylake_latencies() -> Vec<X86InstructionLatency> {
let mut base = haswell_latencies();
for entry in &mut base {
match entry.opcode {
x86_opcodes::MOV => {
entry.port_usage = &["0", "1", "5", "6"];
}
x86_opcodes::ADD
| x86_opcodes::SUB
| x86_opcodes::AND
| x86_opcodes::OR
| x86_opcodes::XOR
| x86_opcodes::CMP
| x86_opcodes::TEST => {
entry.port_usage = &["0", "1", "5", "6"];
}
x86_opcodes::LEA => {
entry.port_usage = &["1", "5"];
}
x86_opcodes::IMUL => {
entry.latency = 3;
entry.reciprocal_throughput = 1.0;
entry.port_usage = &["0"];
}
x86_opcodes::DIV => {
entry.latency = 22;
entry.reciprocal_throughput = 18.0;
}
_ => {}
}
}
base
}
fn ice_lake_latencies() -> Vec<X86InstructionLatency> {
let mut base = skylake_latencies();
for entry in &mut base {
match entry.opcode {
x86_opcodes::MOV => {
entry.port_usage = &["0", "1", "5", "6"];
}
x86_opcodes::ADD
| x86_opcodes::SUB
| x86_opcodes::AND
| x86_opcodes::OR
| x86_opcodes::XOR
| x86_opcodes::CMP
| x86_opcodes::TEST => {
entry.port_usage = &["0", "1", "5", "6"];
}
x86_opcodes::IMUL => {
entry.latency = 3;
entry.reciprocal_throughput = 1.0;
entry.port_usage = &["0", "1", "5"];
}
x86_opcodes::DIV => {
entry.latency = 18;
entry.reciprocal_throughput = 14.0;
}
_ => {}
}
}
base
}
fn golden_cove_latencies() -> Vec<X86InstructionLatency> {
let mut base = ice_lake_latencies();
for entry in &mut base {
match entry.opcode {
x86_opcodes::DIV => {
entry.latency = 18;
entry.reciprocal_throughput = 12.0;
}
x86_opcodes::IMUL => {
entry.latency = 3;
entry.reciprocal_throughput = 0.5;
}
x86_opcodes::LEA => {
entry.latency = 1;
entry.reciprocal_throughput = 0.25;
}
_ => {}
}
}
base
}
fn granite_rapids_latencies() -> Vec<X86InstructionLatency> {
let mut base = golden_cove_latencies();
for entry in &mut base {
if entry.reciprocal_throughput > 0.0 {
entry.reciprocal_throughput *= 0.8;
}
}
base
}
fn gracemont_latencies() -> Vec<X86InstructionLatency> {
vec![
X86InstructionLatency {
opcode: x86_opcodes::NOP,
mnemonic: "NOP",
latency: 0,
reciprocal_throughput: 0.25,
fused_uops: 0,
unfused_uops: 0,
port_usage: &[],
},
X86InstructionLatency {
opcode: x86_opcodes::MOV,
mnemonic: "MOV r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5", "6"],
},
X86InstructionLatency {
opcode: x86_opcodes::ADD,
mnemonic: "ADD r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5", "6"],
},
X86InstructionLatency {
opcode: x86_opcodes::SUB,
mnemonic: "SUB r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5", "6"],
},
X86InstructionLatency {
opcode: x86_opcodes::MUL,
mnemonic: "MUL r64",
latency: 4,
reciprocal_throughput: 1.0,
fused_uops: 2,
unfused_uops: 2,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::IMUL,
mnemonic: "IMUL r,r",
latency: 4,
reciprocal_throughput: 1.0,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::DIV,
mnemonic: "DIV r64",
latency: 22,
reciprocal_throughput: 20.0,
fused_uops: 1,
unfused_uops: 10,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::LEA,
mnemonic: "LEA r,[r+r]",
latency: 1,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["1", "5"],
},
X86InstructionLatency {
opcode: x86_opcodes::CMP,
mnemonic: "CMP r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5", "6"],
},
X86InstructionLatency {
opcode: x86_opcodes::TEST,
mnemonic: "TEST r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5", "6"],
},
X86InstructionLatency {
opcode: x86_opcodes::JMP,
mnemonic: "JMP",
latency: 0,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["6"],
},
X86InstructionLatency {
opcode: x86_opcodes::JE,
mnemonic: "JE/JCC",
latency: 1,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["6"],
},
]
}
fn sierra_forest_latencies() -> Vec<X86InstructionLatency> {
gracemont_latencies()
}
fn zen1_latencies() -> Vec<X86InstructionLatency> {
vec![
X86InstructionLatency {
opcode: x86_opcodes::MOV,
mnemonic: "MOV r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["ALU0", "ALU1", "ALU2", "ALU3"],
},
X86InstructionLatency {
opcode: x86_opcodes::ADD,
mnemonic: "ADD r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["ALU0", "ALU1", "ALU2", "ALU3"],
},
X86InstructionLatency {
opcode: x86_opcodes::SUB,
mnemonic: "SUB r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["ALU0", "ALU1", "ALU2", "ALU3"],
},
X86InstructionLatency {
opcode: x86_opcodes::AND,
mnemonic: "AND r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["ALU0", "ALU1", "ALU2", "ALU3"],
},
X86InstructionLatency {
opcode: x86_opcodes::OR,
mnemonic: "OR r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["ALU0", "ALU1", "ALU2", "ALU3"],
},
X86InstructionLatency {
opcode: x86_opcodes::XOR,
mnemonic: "XOR r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["ALU0", "ALU1", "ALU2", "ALU3"],
},
X86InstructionLatency {
opcode: x86_opcodes::MUL,
mnemonic: "MUL r64",
latency: 3,
reciprocal_throughput: 1.0,
fused_uops: 2,
unfused_uops: 2,
port_usage: &["ALU0"],
},
X86InstructionLatency {
opcode: x86_opcodes::IMUL,
mnemonic: "IMUL r,r",
latency: 3,
reciprocal_throughput: 1.0,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["ALU0"],
},
X86InstructionLatency {
opcode: x86_opcodes::DIV,
mnemonic: "DIV r64",
latency: 18,
reciprocal_throughput: 16.0,
fused_uops: 1,
unfused_uops: 8,
port_usage: &["ALU0"],
},
X86InstructionLatency {
opcode: x86_opcodes::LEA,
mnemonic: "LEA r,[r+r]",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["ALU0", "ALU1", "ALU2", "ALU3"],
},
X86InstructionLatency {
opcode: x86_opcodes::CMP,
mnemonic: "CMP r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["ALU0", "ALU1", "ALU2", "ALU3"],
},
X86InstructionLatency {
opcode: x86_opcodes::JMP,
mnemonic: "JMP",
latency: 0,
reciprocal_throughput: 1.0,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["ALU0"],
},
X86InstructionLatency {
opcode: x86_opcodes::JE,
mnemonic: "JE/JCC",
latency: 1,
reciprocal_throughput: 1.0,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["ALU0"],
},
]
}
fn zen2_latencies() -> Vec<X86InstructionLatency> {
let mut base = zen1_latencies();
for entry in &mut base {
match entry.opcode {
x86_opcodes::DIV => {
entry.latency = 16;
entry.reciprocal_throughput = 14.0;
}
_ => {}
}
}
base
}
fn zen3_latencies() -> Vec<X86InstructionLatency> {
let mut base = zen2_latencies();
for entry in &mut base {
match entry.opcode {
x86_opcodes::DIV => {
entry.latency = 16;
entry.reciprocal_throughput = 12.0;
}
x86_opcodes::MUL => {
entry.latency = 3;
entry.reciprocal_throughput = 0.5;
}
_ => {}
}
}
base
}
fn zen4_latencies() -> Vec<X86InstructionLatency> {
let mut base = zen3_latencies();
for entry in &mut base {
match entry.opcode {
x86_opcodes::DIV => {
entry.latency = 16;
entry.reciprocal_throughput = 10.0;
}
_ => {}
}
}
base
}
fn zen5_latencies() -> Vec<X86InstructionLatency> {
let mut base = zen4_latencies();
for entry in &mut base {
match entry.opcode {
x86_opcodes::DIV => {
entry.latency = 14;
entry.reciprocal_throughput = 8.0;
}
x86_opcodes::MOV
| x86_opcodes::ADD
| x86_opcodes::SUB
| x86_opcodes::AND
| x86_opcodes::OR
| x86_opcodes::XOR => {
entry.reciprocal_throughput = 1.0 / 6.0;
entry.port_usage = &["ALU0", "ALU1", "ALU2", "ALU3", "ALU4", "ALU5"];
}
_ => {}
}
}
base
}
fn silvermont_latencies() -> Vec<X86InstructionLatency> {
vec![
X86InstructionLatency {
opcode: x86_opcodes::MOV,
mnemonic: "MOV r,r",
latency: 1,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1"],
},
X86InstructionLatency {
opcode: x86_opcodes::ADD,
mnemonic: "ADD r,r",
latency: 1,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1"],
},
X86InstructionLatency {
opcode: x86_opcodes::MUL,
mnemonic: "MUL r64",
latency: 5,
reciprocal_throughput: 5.0,
fused_uops: 1,
unfused_uops: 3,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::IMUL,
mnemonic: "IMUL r,r",
latency: 5,
reciprocal_throughput: 4.0,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::DIV,
mnemonic: "DIV r64",
latency: 22,
reciprocal_throughput: 22.0,
fused_uops: 1,
unfused_uops: 10,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::LEA,
mnemonic: "LEA r,[r+r]",
latency: 1,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1"],
},
]
}
fn goldmont_latencies() -> Vec<X86InstructionLatency> {
vec![
X86InstructionLatency {
opcode: x86_opcodes::MOV,
mnemonic: "MOV r,r",
latency: 1,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1"],
},
X86InstructionLatency {
opcode: x86_opcodes::ADD,
mnemonic: "ADD r,r",
latency: 1,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1"],
},
X86InstructionLatency {
opcode: x86_opcodes::MUL,
mnemonic: "MUL r64",
latency: 5,
reciprocal_throughput: 4.0,
fused_uops: 1,
unfused_uops: 3,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::IMUL,
mnemonic: "IMUL r,r",
latency: 4,
reciprocal_throughput: 3.0,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::DIV,
mnemonic: "DIV r64",
latency: 20,
reciprocal_throughput: 20.0,
fused_uops: 1,
unfused_uops: 10,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::LEA,
mnemonic: "LEA r,[r+r]",
latency: 1,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1"],
},
]
}
fn tremont_latencies() -> Vec<X86InstructionLatency> {
vec![
X86InstructionLatency {
opcode: x86_opcodes::MOV,
mnemonic: "MOV r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5", "6"],
},
X86InstructionLatency {
opcode: x86_opcodes::ADD,
mnemonic: "ADD r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5", "6"],
},
X86InstructionLatency {
opcode: x86_opcodes::SUB,
mnemonic: "SUB r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5", "6"],
},
X86InstructionLatency {
opcode: x86_opcodes::MUL,
mnemonic: "MUL r64",
latency: 4,
reciprocal_throughput: 2.0,
fused_uops: 2,
unfused_uops: 2,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::IMUL,
mnemonic: "IMUL r,r",
latency: 4,
reciprocal_throughput: 1.0,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::DIV,
mnemonic: "DIV r64",
latency: 20,
reciprocal_throughput: 18.0,
fused_uops: 1,
unfused_uops: 10,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::LEA,
mnemonic: "LEA r,[r+r]",
latency: 1,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["1", "5"],
},
X86InstructionLatency {
opcode: x86_opcodes::CMP,
mnemonic: "CMP r,r",
latency: 1,
reciprocal_throughput: 0.25,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1", "5", "6"],
},
]
}
fn crestmont_latencies() -> Vec<X86InstructionLatency> {
tremont_latencies()
}
fn knights_landing_latencies() -> Vec<X86InstructionLatency> {
vec![
X86InstructionLatency {
opcode: x86_opcodes::MOV,
mnemonic: "MOV r,r",
latency: 1,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1"],
},
X86InstructionLatency {
opcode: x86_opcodes::ADD,
mnemonic: "ADD r,r",
latency: 1,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1"],
},
X86InstructionLatency {
opcode: x86_opcodes::SUB,
mnemonic: "SUB r,r",
latency: 1,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1"],
},
X86InstructionLatency {
opcode: x86_opcodes::MUL,
mnemonic: "MUL r64",
latency: 6,
reciprocal_throughput: 4.0,
fused_uops: 2,
unfused_uops: 2,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::IMUL,
mnemonic: "IMUL r,r",
latency: 5,
reciprocal_throughput: 3.0,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::DIV,
mnemonic: "DIV r64",
latency: 24,
reciprocal_throughput: 24.0,
fused_uops: 1,
unfused_uops: 10,
port_usage: &["0"],
},
X86InstructionLatency {
opcode: x86_opcodes::LEA,
mnemonic: "LEA r,[r+r]",
latency: 1,
reciprocal_throughput: 0.5,
fused_uops: 1,
unfused_uops: 1,
port_usage: &["0", "1"],
},
]
}
#[derive(Debug, Clone)]
pub struct X86IssuePort {
pub name: &'static str,
pub resource: ProcResource,
pub write_latency: u32,
pub resource_cycles: u32,
pub has_bypass: bool,
pub bypass_cycles: u32,
pub capabilities: PortCapabilities,
}
#[derive(Debug, Clone)]
pub struct X86SchedulerModel {
pub kind: X86MicroArchKind,
pub issue_ports: Vec<X86IssuePort>,
pub load_to_use_latency: u32,
pub store_to_load_forwarding: u32,
pub default_int_write_latency: u32,
pub default_fp_write_latency: u32,
pub bypass_latencies: Vec<BypassEdge>,
}
#[derive(Debug, Clone)]
pub struct BypassEdge {
pub from: &'static str,
pub to: &'static str,
pub latency: u32,
}
pub fn scheduler_model_for(kind: X86MicroArchKind) -> X86SchedulerModel {
match kind {
X86MicroArchKind::SandyBridge | X86MicroArchKind::IvyBridge => {
sandy_bridge_scheduler_model()
}
X86MicroArchKind::Haswell | X86MicroArchKind::Broadwell => haswell_scheduler_model(),
X86MicroArchKind::SkylakeClient
| X86MicroArchKind::KabyLake
| X86MicroArchKind::CoffeeLake
| X86MicroArchKind::CometLake => skylake_scheduler_model(),
X86MicroArchKind::SkylakeServer | X86MicroArchKind::CascadeLake => {
skylake_server_scheduler_model()
}
X86MicroArchKind::IceLakeClient
| X86MicroArchKind::IceLakeServer
| X86MicroArchKind::TigerLake
| X86MicroArchKind::RocketLake => ice_lake_scheduler_model(),
X86MicroArchKind::AlderLakePcore | X86MicroArchKind::RaptorLakePcore => {
golden_cove_scheduler_model()
}
X86MicroArchKind::SapphireRapids | X86MicroArchKind::EmeraldRapids => {
sapphire_rapids_scheduler_model()
}
X86MicroArchKind::GraniteRapids => granite_rapids_scheduler_model(),
X86MicroArchKind::AlderLakeEcore
| X86MicroArchKind::RaptorLakeEcore
| X86MicroArchKind::Gracemont => gracemont_scheduler_model(),
X86MicroArchKind::SierraForest => sierra_forest_scheduler_model(),
X86MicroArchKind::Zen1 | X86MicroArchKind::ZenPlus => zen1_scheduler_model(),
X86MicroArchKind::Zen2 => zen2_scheduler_model(),
X86MicroArchKind::Zen3 => zen3_scheduler_model(),
X86MicroArchKind::Zen4 => zen4_scheduler_model(),
X86MicroArchKind::Zen5 | X86MicroArchKind::Zen5c => zen5_scheduler_model(),
X86MicroArchKind::Silvermont | X86MicroArchKind::Airmont => silvermont_scheduler_model(),
X86MicroArchKind::Goldmont | X86MicroArchKind::GoldmontPlus => goldmont_scheduler_model(),
X86MicroArchKind::Tremont => tremont_scheduler_model(),
X86MicroArchKind::Crestmont => crestmont_scheduler_model(),
X86MicroArchKind::KnightsLanding | X86MicroArchKind::KnightsMill => {
knights_landing_scheduler_model()
}
}
}
fn sandy_bridge_scheduler_model() -> X86SchedulerModel {
X86SchedulerModel {
kind: X86MicroArchKind::SandyBridge,
issue_ports: vec![
X86IssuePort {
name: "Port0",
resource: ProcResource::Port0,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
has_fpu_mul: true,
has_fpu_div: true,
has_simd_int: true,
has_simd_fp: true,
..PortCapabilities::none()
},
},
X86IssuePort {
name: "Port1",
resource: ProcResource::Port1,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
has_fpu_add: true,
has_simd_int: true,
has_simd_fp: true,
..PortCapabilities::none()
},
},
X86IssuePort {
name: "Port5",
resource: ProcResource::Port5,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
has_branch: true,
has_simd_int: true,
has_simd_shuffle: true,
..PortCapabilities::none()
},
},
],
load_to_use_latency: 4,
store_to_load_forwarding: 1,
default_int_write_latency: 1,
default_fp_write_latency: 3,
bypass_latencies: vec![
BypassEdge {
from: "Port0",
to: "Port0",
latency: 0,
},
BypassEdge {
from: "Port0",
to: "Port1",
latency: 0,
},
BypassEdge {
from: "Port0",
to: "Port5",
latency: 0,
},
BypassEdge {
from: "Port1",
to: "Port0",
latency: 0,
},
BypassEdge {
from: "Port1",
to: "Port1",
latency: 0,
},
BypassEdge {
from: "Port1",
to: "Port5",
latency: 0,
},
BypassEdge {
from: "Port5",
to: "Port0",
latency: 0,
},
BypassEdge {
from: "Port5",
to: "Port1",
latency: 0,
},
BypassEdge {
from: "Port5",
to: "Port5",
latency: 0,
},
],
}
}
fn haswell_scheduler_model() -> X86SchedulerModel {
let mut m = sandy_bridge_scheduler_model();
m.kind = X86MicroArchKind::Haswell;
m.issue_ports.push(X86IssuePort {
name: "Port6",
resource: ProcResource::Port6,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
has_branch: true,
..PortCapabilities::none()
},
});
for port in ["Port0", "Port1", "Port5", "Port6"] {
m.bypass_latencies.push(BypassEdge {
from: "Port6",
to: port,
latency: 0,
});
m.bypass_latencies.push(BypassEdge {
from: port,
to: "Port6",
latency: 0,
});
}
m.load_to_use_latency = 4;
m
}
fn skylake_scheduler_model() -> X86SchedulerModel {
let mut m = haswell_scheduler_model();
m.kind = X86MicroArchKind::SkylakeClient;
m.load_to_use_latency = 4;
m
}
fn skylake_server_scheduler_model() -> X86SchedulerModel {
let mut m = skylake_scheduler_model();
m.kind = X86MicroArchKind::SkylakeServer;
m.load_to_use_latency = 5;
m
}
fn ice_lake_scheduler_model() -> X86SchedulerModel {
let mut m = skylake_scheduler_model();
m.kind = X86MicroArchKind::IceLakeClient;
m.load_to_use_latency = 5;
m
}
fn golden_cove_scheduler_model() -> X86SchedulerModel {
X86SchedulerModel {
kind: X86MicroArchKind::AlderLakePcore,
issue_ports: vec![
X86IssuePort {
name: "Port0",
resource: ProcResource::Port0,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_int_div: true,
has_fpu_add: true,
has_fpu_mul: true,
has_fpu_div: true,
has_simd_int: true,
has_simd_fp: true,
has_simd_fma: true,
has_avx512_fp: true,
has_avx512_int: true,
has_pdep_pext: true,
..PortCapabilities::none()
},
},
X86IssuePort {
name: "Port1",
resource: ProcResource::Port1,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_int: true,
has_simd_fp: true,
has_simd_fma: true,
has_avx512_fp: true,
has_avx512_int: true,
..PortCapabilities::none()
},
},
X86IssuePort {
name: "Port5",
resource: ProcResource::Port5,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_simd_shuffle: true,
has_simd_fma: true,
has_avx512_fp: true,
has_avx512_int: true,
has_pdep_pext: true,
..PortCapabilities::none()
},
},
X86IssuePort {
name: "Port6",
resource: ProcResource::Port6,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
has_branch: true,
..PortCapabilities::none()
},
},
],
load_to_use_latency: 5,
store_to_load_forwarding: 1,
default_int_write_latency: 1,
default_fp_write_latency: 3,
bypass_latencies: {
let mut edges = Vec::new();
for from in ["Port0", "Port1", "Port5", "Port6"] {
for to in ["Port0", "Port1", "Port5", "Port6"] {
edges.push(BypassEdge {
from,
to,
latency: 0,
});
}
}
edges
},
}
}
fn sapphire_rapids_scheduler_model() -> X86SchedulerModel {
let mut m = golden_cove_scheduler_model();
m.kind = X86MicroArchKind::SapphireRapids;
m.load_to_use_latency = 5;
m
}
fn granite_rapids_scheduler_model() -> X86SchedulerModel {
let mut m = golden_cove_scheduler_model();
m.kind = X86MicroArchKind::GraniteRapids;
m.load_to_use_latency = 5;
m.issue_ports.push(X86IssuePort {
name: "Port10",
resource: ProcResource::Port10,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_simd_fma: true,
has_avx512_fp: true,
..PortCapabilities::none()
},
});
m.issue_ports.push(X86IssuePort {
name: "Port11",
resource: ProcResource::Port11,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_simd_fma: true,
has_avx512_fp: true,
..PortCapabilities::none()
},
});
m
}
fn gracemont_scheduler_model() -> X86SchedulerModel {
X86SchedulerModel {
kind: X86MicroArchKind::AlderLakeEcore,
issue_ports: vec![
X86IssuePort {
name: "Port0",
resource: ProcResource::Port0,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_int: true,
has_simd_fp: true,
..PortCapabilities::none()
},
},
X86IssuePort {
name: "Port1",
resource: ProcResource::Port1,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_fp: true,
..PortCapabilities::none()
},
},
X86IssuePort {
name: "Port5",
resource: ProcResource::Port5,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
has_simd_shuffle: true,
..PortCapabilities::none()
},
},
X86IssuePort {
name: "Port6",
resource: ProcResource::Port6,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
has_branch: true,
..PortCapabilities::none()
},
},
],
load_to_use_latency: 5,
store_to_load_forwarding: 1,
default_int_write_latency: 1,
default_fp_write_latency: 3,
bypass_latencies: {
let mut edges = Vec::new();
for from in ["Port0", "Port1", "Port5", "Port6"] {
for to in ["Port0", "Port1", "Port5", "Port6"] {
edges.push(BypassEdge {
from,
to,
latency: 0,
});
}
}
edges
},
}
}
fn sierra_forest_scheduler_model() -> X86SchedulerModel {
let mut m = gracemont_scheduler_model();
m.kind = X86MicroArchKind::SierraForest;
m
}
fn zen1_scheduler_model() -> X86SchedulerModel {
X86SchedulerModel {
kind: X86MicroArchKind::Zen1,
issue_ports: vec![
X86IssuePort {
name: "ALU0",
resource: ProcResource::ALU0,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_int_div: true,
has_branch: true,
..PortCapabilities::none()
},
},
X86IssuePort {
name: "ALU1",
resource: ProcResource::ALU1,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
..PortCapabilities::none()
},
},
X86IssuePort {
name: "ALU2",
resource: ProcResource::ALU2,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
..PortCapabilities::none()
},
},
X86IssuePort {
name: "ALU3",
resource: ProcResource::ALU3,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
..PortCapabilities::none()
},
},
],
load_to_use_latency: 4,
store_to_load_forwarding: 1,
default_int_write_latency: 1,
default_fp_write_latency: 3,
bypass_latencies: {
let mut edges = Vec::new();
for from in ["ALU0", "ALU1", "ALU2", "ALU3"] {
for to in ["ALU0", "ALU1", "ALU2", "ALU3"] {
edges.push(BypassEdge {
from,
to,
latency: 0,
});
}
}
edges
},
}
}
fn zen2_scheduler_model() -> X86SchedulerModel {
let mut m = zen1_scheduler_model();
m.kind = X86MicroArchKind::Zen2;
m
}
fn zen3_scheduler_model() -> X86SchedulerModel {
let mut m = zen1_scheduler_model();
m.kind = X86MicroArchKind::Zen3;
m.load_to_use_latency = 3;
m
}
fn zen4_scheduler_model() -> X86SchedulerModel {
let mut m = zen1_scheduler_model();
m.kind = X86MicroArchKind::Zen4;
m.load_to_use_latency = 4;
m
}
fn zen5_scheduler_model() -> X86SchedulerModel {
let mut m = zen1_scheduler_model();
m.kind = X86MicroArchKind::Zen5;
m.load_to_use_latency = 4;
m.issue_ports.push(X86IssuePort {
name: "ALU4",
resource: ProcResource::ALU4,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
..PortCapabilities::none()
},
});
m.issue_ports.push(X86IssuePort {
name: "ALU5",
resource: ProcResource::ALU5,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
..PortCapabilities::none()
},
});
m
}
fn silvermont_scheduler_model() -> X86SchedulerModel {
X86SchedulerModel {
kind: X86MicroArchKind::Silvermont,
issue_ports: vec![
X86IssuePort {
name: "Port0",
resource: ProcResource::Port0,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_int_div: true,
has_fpu_add: true,
has_fpu_mul: true,
..PortCapabilities::none()
},
},
X86IssuePort {
name: "Port1",
resource: ProcResource::Port1,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
has_branch: true,
..PortCapabilities::none()
},
},
],
load_to_use_latency: 3,
store_to_load_forwarding: 1,
default_int_write_latency: 1,
default_fp_write_latency: 4,
bypass_latencies: vec![
BypassEdge {
from: "Port0",
to: "Port0",
latency: 0,
},
BypassEdge {
from: "Port0",
to: "Port1",
latency: 0,
},
BypassEdge {
from: "Port1",
to: "Port0",
latency: 0,
},
BypassEdge {
from: "Port1",
to: "Port1",
latency: 0,
},
],
}
}
fn goldmont_scheduler_model() -> X86SchedulerModel {
let mut m = silvermont_scheduler_model();
m.kind = X86MicroArchKind::Goldmont;
m.load_to_use_latency = 4;
m
}
fn tremont_scheduler_model() -> X86SchedulerModel {
let mut m = gracemont_scheduler_model();
m.kind = X86MicroArchKind::Tremont;
m.load_to_use_latency = 5;
m
}
fn crestmont_scheduler_model() -> X86SchedulerModel {
let mut m = gracemont_scheduler_model();
m.kind = X86MicroArchKind::Crestmont;
m.load_to_use_latency = 5;
m
}
fn knights_landing_scheduler_model() -> X86SchedulerModel {
X86SchedulerModel {
kind: X86MicroArchKind::KnightsLanding,
issue_ports: vec![
X86IssuePort {
name: "Port0",
resource: ProcResource::Port0,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
has_int_mul: true,
has_fpu_add: true,
has_fpu_mul: true,
has_fpu_div: true,
has_simd_int: true,
has_simd_fp: true,
has_simd_fma: true,
has_avx512_fp: true,
has_avx512_int: true,
has_branch: true,
..PortCapabilities::none()
},
},
X86IssuePort {
name: "Port1",
resource: ProcResource::Port1,
write_latency: 1,
resource_cycles: 1,
has_bypass: true,
bypass_cycles: 1,
capabilities: PortCapabilities {
has_int_alu: true,
has_agu_load: true,
has_agu_store: true,
has_fpu_add: true,
has_fpu_mul: true,
has_simd_fp: true,
has_simd_fma: true,
has_avx512_fp: true,
has_avx512_int: true,
..PortCapabilities::none()
},
},
],
load_to_use_latency: 4,
store_to_load_forwarding: 1,
default_int_write_latency: 1,
default_fp_write_latency: 6,
bypass_latencies: vec![
BypassEdge {
from: "Port0",
to: "Port0",
latency: 0,
},
BypassEdge {
from: "Port0",
to: "Port1",
latency: 0,
},
BypassEdge {
from: "Port1",
to: "Port0",
latency: 0,
},
BypassEdge {
from: "Port1",
to: "Port1",
latency: 0,
},
],
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DataHazardKind {
RAW,
WAR,
WAW,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StructuralHazardKind {
PortConflict,
ResourceConflict,
SchedulerFull,
ROBFull,
LoadBufferFull,
StoreBufferFull,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ControlHazardKind {
BranchMispredict,
IndirectBranchMispredict,
ReturnMispredict,
}
#[derive(Debug, Clone)]
pub struct X86PipelineHazards {
pub kind: X86MicroArchKind,
pub branch_mispredict_penalty: u32,
pub indirect_branch_penalty: u32,
pub return_mispredict_penalty: u32,
pub has_register_renaming: bool,
pub has_store_to_load_forwarding: bool,
pub int_raw_hazard_latency: u32,
pub fp_raw_hazard_latency: u32,
pub port_conflict_penalty: u32,
}
pub fn pipeline_hazards_for(kind: X86MicroArchKind) -> X86PipelineHazards {
let arch = microarch_for(kind);
X86PipelineHazards {
kind,
branch_mispredict_penalty: arch.branch_predictor.mispredict_penalty,
indirect_branch_penalty: arch.branch_predictor.mispredict_penalty + 3,
return_mispredict_penalty: arch.branch_predictor.mispredict_penalty + 2,
has_register_renaming: arch.pipeline.out_of_order,
has_store_to_load_forwarding: arch.pipeline.out_of_order,
int_raw_hazard_latency: 1,
fp_raw_hazard_latency: {
let mut max_fp = 3;
for unit in &arch.execution_units {
if matches!(
unit.unit_type,
ExecutionUnitType::FpuAdd
| ExecutionUnitType::FpuMul
| ExecutionUnitType::FpuFma
) {
max_fp = max_fp.max(unit.typical_latency);
}
}
max_fp
},
port_conflict_penalty: 1,
}
}
#[derive(Debug, Clone)]
pub struct X86MicroFusion {
pub kind: X86MicroArchKind,
pub supported: bool,
pub load_op_fusion: bool,
pub store_op_fusion: bool,
pub rip_relative_fusion: bool,
pub cross_lane_fusion: bool,
}
pub fn micro_fusion_for(kind: X86MicroArchKind) -> X86MicroFusion {
match kind {
X86MicroArchKind::SandyBridge
| X86MicroArchKind::IvyBridge
| X86MicroArchKind::Haswell
| X86MicroArchKind::Broadwell
| X86MicroArchKind::SkylakeClient
| X86MicroArchKind::SkylakeServer
| X86MicroArchKind::KabyLake
| X86MicroArchKind::CoffeeLake
| X86MicroArchKind::CascadeLake
| X86MicroArchKind::CometLake
| X86MicroArchKind::IceLakeClient
| X86MicroArchKind::IceLakeServer
| X86MicroArchKind::TigerLake
| X86MicroArchKind::RocketLake
| X86MicroArchKind::AlderLakePcore
| X86MicroArchKind::RaptorLakePcore
| X86MicroArchKind::SapphireRapids
| X86MicroArchKind::EmeraldRapids
| X86MicroArchKind::GraniteRapids => X86MicroFusion {
kind,
supported: true,
load_op_fusion: true,
store_op_fusion: true,
rip_relative_fusion: true,
cross_lane_fusion: false,
},
X86MicroArchKind::AlderLakeEcore
| X86MicroArchKind::RaptorLakeEcore
| X86MicroArchKind::Gracemont
| X86MicroArchKind::Tremont
| X86MicroArchKind::Crestmont
| X86MicroArchKind::SierraForest => X86MicroFusion {
kind,
supported: true,
load_op_fusion: true,
store_op_fusion: true,
rip_relative_fusion: false,
cross_lane_fusion: false,
},
X86MicroArchKind::Zen1
| X86MicroArchKind::ZenPlus
| X86MicroArchKind::Zen2
| X86MicroArchKind::Zen3
| X86MicroArchKind::Zen4
| X86MicroArchKind::Zen5
| X86MicroArchKind::Zen5c => X86MicroFusion {
kind,
supported: true,
load_op_fusion: true,
store_op_fusion: false,
rip_relative_fusion: true,
cross_lane_fusion: false,
},
X86MicroArchKind::Silvermont
| X86MicroArchKind::Airmont
| X86MicroArchKind::Goldmont
| X86MicroArchKind::GoldmontPlus => X86MicroFusion {
kind,
supported: false,
load_op_fusion: false,
store_op_fusion: false,
rip_relative_fusion: false,
cross_lane_fusion: false,
},
X86MicroArchKind::KnightsLanding | X86MicroArchKind::KnightsMill => X86MicroFusion {
kind,
supported: false,
load_op_fusion: false,
store_op_fusion: false,
rip_relative_fusion: false,
cross_lane_fusion: false,
},
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MacroFusionPair {
CmpJcc,
TestJcc,
AddSubJcc,
IncDecJcc,
AndOrXorJcc,
DecJnz,
}
#[derive(Debug, Clone)]
pub struct X86MacroFusion {
pub kind: X86MicroArchKind,
pub supported: bool,
pub fused_pairs: Vec<MacroFusionPair>,
pub supports_imm32: bool,
pub supports_32byte_boundary_crossing: bool,
}
pub fn macro_fusion_for(kind: X86MicroArchKind) -> X86MacroFusion {
match kind {
X86MicroArchKind::SandyBridge | X86MicroArchKind::IvyBridge => X86MacroFusion {
kind,
supported: true,
fused_pairs: vec![MacroFusionPair::CmpJcc, MacroFusionPair::TestJcc],
supports_imm32: false,
supports_32byte_boundary_crossing: false,
},
X86MicroArchKind::Haswell
| X86MicroArchKind::Broadwell
| X86MicroArchKind::SkylakeClient
| X86MicroArchKind::SkylakeServer
| X86MicroArchKind::KabyLake
| X86MicroArchKind::CoffeeLake
| X86MicroArchKind::CascadeLake
| X86MicroArchKind::CometLake => X86MacroFusion {
kind,
supported: true,
fused_pairs: vec![
MacroFusionPair::CmpJcc,
MacroFusionPair::TestJcc,
MacroFusionPair::AddSubJcc,
MacroFusionPair::IncDecJcc,
MacroFusionPair::AndOrXorJcc,
],
supports_imm32: false,
supports_32byte_boundary_crossing: false,
},
X86MicroArchKind::IceLakeClient
| X86MicroArchKind::IceLakeServer
| X86MicroArchKind::TigerLake
| X86MicroArchKind::RocketLake
| X86MicroArchKind::AlderLakePcore
| X86MicroArchKind::RaptorLakePcore
| X86MicroArchKind::SapphireRapids
| X86MicroArchKind::EmeraldRapids
| X86MicroArchKind::GraniteRapids => X86MacroFusion {
kind,
supported: true,
fused_pairs: vec![
MacroFusionPair::CmpJcc,
MacroFusionPair::TestJcc,
MacroFusionPair::AddSubJcc,
MacroFusionPair::IncDecJcc,
MacroFusionPair::AndOrXorJcc,
MacroFusionPair::DecJnz,
],
supports_imm32: true,
supports_32byte_boundary_crossing: false,
},
X86MicroArchKind::AlderLakeEcore
| X86MicroArchKind::RaptorLakeEcore
| X86MicroArchKind::Gracemont
| X86MicroArchKind::Tremont
| X86MicroArchKind::Crestmont
| X86MicroArchKind::SierraForest => X86MacroFusion {
kind,
supported: true,
fused_pairs: vec![
MacroFusionPair::CmpJcc,
MacroFusionPair::TestJcc,
MacroFusionPair::AddSubJcc,
MacroFusionPair::IncDecJcc,
],
supports_imm32: false,
supports_32byte_boundary_crossing: false,
},
X86MicroArchKind::Zen1 | X86MicroArchKind::ZenPlus => X86MacroFusion {
kind,
supported: true,
fused_pairs: vec![MacroFusionPair::CmpJcc, MacroFusionPair::TestJcc],
supports_imm32: false,
supports_32byte_boundary_crossing: false,
},
X86MicroArchKind::Zen2
| X86MicroArchKind::Zen3
| X86MicroArchKind::Zen4
| X86MicroArchKind::Zen5
| X86MicroArchKind::Zen5c => X86MacroFusion {
kind,
supported: true,
fused_pairs: vec![
MacroFusionPair::CmpJcc,
MacroFusionPair::TestJcc,
MacroFusionPair::AddSubJcc,
],
supports_imm32: false,
supports_32byte_boundary_crossing: false,
},
X86MicroArchKind::Silvermont
| X86MicroArchKind::Airmont
| X86MicroArchKind::Goldmont
| X86MicroArchKind::GoldmontPlus
| X86MicroArchKind::KnightsLanding
| X86MicroArchKind::KnightsMill => X86MacroFusion {
kind,
supported: false,
fused_pairs: vec![],
supports_imm32: false,
supports_32byte_boundary_crossing: false,
},
}
}
pub fn microarch_for(kind: X86MicroArchKind) -> X86MicroArch {
match kind {
X86MicroArchKind::SandyBridge => sandy_bridge(),
X86MicroArchKind::IvyBridge => ivy_bridge(),
X86MicroArchKind::Haswell => haswell(),
X86MicroArchKind::Broadwell => broadwell(),
X86MicroArchKind::SkylakeClient => skylake_client(),
X86MicroArchKind::SkylakeServer => skylake_server(),
X86MicroArchKind::KabyLake => kaby_lake(),
X86MicroArchKind::CoffeeLake => coffee_lake(),
X86MicroArchKind::CascadeLake => cascade_lake(),
X86MicroArchKind::CometLake => comet_lake(),
X86MicroArchKind::IceLakeClient => ice_lake_client(),
X86MicroArchKind::IceLakeServer => ice_lake_server(),
X86MicroArchKind::TigerLake => tiger_lake(),
X86MicroArchKind::RocketLake => rocket_lake(),
X86MicroArchKind::AlderLakePcore => alder_lake_pcore(),
X86MicroArchKind::AlderLakeEcore => alder_lake_ecore(),
X86MicroArchKind::RaptorLakePcore => raptor_lake_pcore(),
X86MicroArchKind::RaptorLakeEcore => raptor_lake_ecore(),
X86MicroArchKind::SapphireRapids => sapphire_rapids(),
X86MicroArchKind::EmeraldRapids => emerald_rapids(),
X86MicroArchKind::GraniteRapids => granite_rapids(),
X86MicroArchKind::SierraForest => sierra_forest(),
X86MicroArchKind::Zen1 => zen1(),
X86MicroArchKind::ZenPlus => zen_plus(),
X86MicroArchKind::Zen2 => zen2(),
X86MicroArchKind::Zen3 => zen3(),
X86MicroArchKind::Zen4 => zen4(),
X86MicroArchKind::Zen5 => zen5(),
X86MicroArchKind::Zen5c => zen5c(),
X86MicroArchKind::Silvermont => silvermont(),
X86MicroArchKind::Airmont => airmont(),
X86MicroArchKind::Goldmont => goldmont(),
X86MicroArchKind::GoldmontPlus => goldmont_plus(),
X86MicroArchKind::Tremont => tremont(),
X86MicroArchKind::Gracemont => gracemont(),
X86MicroArchKind::Crestmont => crestmont(),
X86MicroArchKind::KnightsLanding => knights_landing(),
X86MicroArchKind::KnightsMill => knights_mill(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_all_microarch_models_construct() {
let all_kinds = vec![
X86MicroArchKind::SandyBridge,
X86MicroArchKind::IvyBridge,
X86MicroArchKind::Haswell,
X86MicroArchKind::Broadwell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::SkylakeServer,
X86MicroArchKind::KabyLake,
X86MicroArchKind::CoffeeLake,
X86MicroArchKind::CascadeLake,
X86MicroArchKind::CometLake,
X86MicroArchKind::IceLakeClient,
X86MicroArchKind::IceLakeServer,
X86MicroArchKind::TigerLake,
X86MicroArchKind::RocketLake,
X86MicroArchKind::AlderLakePcore,
X86MicroArchKind::AlderLakeEcore,
X86MicroArchKind::RaptorLakePcore,
X86MicroArchKind::RaptorLakeEcore,
X86MicroArchKind::SapphireRapids,
X86MicroArchKind::EmeraldRapids,
X86MicroArchKind::GraniteRapids,
X86MicroArchKind::SierraForest,
X86MicroArchKind::Zen1,
X86MicroArchKind::ZenPlus,
X86MicroArchKind::Zen2,
X86MicroArchKind::Zen3,
X86MicroArchKind::Zen4,
X86MicroArchKind::Zen5,
X86MicroArchKind::Zen5c,
X86MicroArchKind::Silvermont,
X86MicroArchKind::Airmont,
X86MicroArchKind::Goldmont,
X86MicroArchKind::GoldmontPlus,
X86MicroArchKind::Tremont,
X86MicroArchKind::Gracemont,
X86MicroArchKind::Crestmont,
X86MicroArchKind::KnightsLanding,
X86MicroArchKind::KnightsMill,
];
for kind in all_kinds {
let m = microarch_for(kind);
assert_eq!(m.kind, kind, "Wrong kind for {:?}", kind);
assert!(!m.pipeline.decode_width > 0);
assert!(m.pipeline.issue_width > 0);
assert!(m.buffers.rob_size > 0 || !m.pipeline.out_of_order);
assert!(m.caches.l1i.size > 0);
assert!(m.caches.l1d.size > 0);
}
}
#[test]
fn test_microarch_names() {
assert_eq!(X86MicroArchKind::Haswell.name(), "Intel Haswell");
assert_eq!(X86MicroArchKind::Zen4.name(), "AMD Zen 4 (Family 19h)");
assert_eq!(
X86MicroArchKind::AlderLakePcore.name(),
"Intel Alder Lake (P-core / Golden Cove)"
);
assert_eq!(
X86MicroArchKind::KnightsLanding.name(),
"Intel Knights Landing (Xeon Phi)"
);
}
#[test]
fn test_sandy_bridge_pipeline() {
let m = sandy_bridge();
assert_eq!(m.pipeline.decode_width, 4);
assert_eq!(m.pipeline.issue_width, 6);
assert_eq!(m.pipeline.retire_width, 4);
assert_eq!(m.buffers.rob_size, 168);
assert_eq!(m.features.uop_cache_entries, 1536);
assert!(m.features.has_macro_fusion);
assert_eq!(m.features.smt_threads, 2);
}
#[test]
fn test_haswell_pipeline() {
let m = haswell();
assert_eq!(m.pipeline.issue_width, 8);
assert_eq!(m.buffers.rob_size, 192);
assert_eq!(m.buffers.load_buffer, 72);
assert_eq!(m.buffers.store_buffer, 42);
}
#[test]
fn test_skylake_client_pipeline() {
let m = skylake_client();
assert_eq!(m.pipeline.decode_width, 4);
assert_eq!(m.pipeline.dispatch_width, 6);
assert_eq!(m.pipeline.issue_width, 8);
assert_eq!(m.pipeline.retire_width, 8);
assert_eq!(m.buffers.rob_size, 224);
assert_eq!(m.buffers.load_buffer, 72);
assert_eq!(m.buffers.store_buffer, 56);
}
#[test]
fn test_skylake_server_has_avx512() {
let m = skylake_server();
assert!(m.features.has_avx512);
assert!(m.caches.l2.size >= 1048576);
assert_eq!(m.caches.llc_topology, LlcTopology::Mesh);
}
#[test]
fn test_ice_lake_client_larger_l1d() {
let m = ice_lake_client();
assert_eq!(m.caches.l1d.size, 49152); assert_eq!(m.buffers.rob_size, 352);
assert_eq!(m.features.uop_cache_entries, 2304);
assert!(m.features.has_avx512);
}
#[test]
fn test_alder_lake_pcore_hybrid() {
let m = alder_lake_pcore();
assert_eq!(m.pipeline.decode_width, 6);
assert_eq!(m.buffers.rob_size, 512);
assert!(m.features.has_amx);
assert_eq!(m.features.uop_cache_entries, 4096);
}
#[test]
fn test_alder_lake_ecore_no_smt() {
let m = alder_lake_ecore();
assert!(!m.features.has_smt);
assert_eq!(m.features.smt_threads, 1);
assert!(!m.features.has_avx512);
assert_eq!(m.features.uop_cache_entries, 0);
}
#[test]
fn test_raptor_lake_pcore_bigger_l2() {
let m = raptor_lake_pcore();
assert_eq!(m.caches.l2.size, 2097152); }
#[test]
fn test_sapphire_rapids_mesh() {
let m = sapphire_rapids();
assert_eq!(m.caches.llc_topology, LlcTopology::Mesh);
assert!(m.features.has_avx512);
}
#[test]
fn test_granite_rapids_wider() {
let m = granite_rapids();
assert_eq!(m.pipeline.decode_width, 8);
assert_eq!(m.buffers.rob_size, 576);
assert!(m.buffers.load_buffer >= 200);
}
#[test]
fn test_sierra_forest_ecore_server() {
let m = sierra_forest();
assert!(!m.features.has_smt);
assert!(!m.features.has_avx512);
assert!(m.caches.l2.size >= 2097152);
}
#[test]
fn test_zen1_amd() {
let m = zen1();
assert_eq!(m.pipeline.decode_width, 4);
assert_eq!(m.pipeline.issue_width, 6);
assert_eq!(m.buffers.rob_size, 192);
assert!(m.features.uop_cache_entries > 0);
assert!(!m.features.has_avx512);
}
#[test]
fn test_zen2_chiplet() {
let m = zen2();
assert_eq!(m.buffers.rob_size, 224);
assert!(m.caches.l3.as_ref().unwrap().size >= 16777216);
assert_eq!(m.caches.llc_topology, LlcTopology::PerCcx);
}
#[test]
fn test_zen3_unified_ccx() {
let m = zen3();
assert_eq!(m.pipeline.issue_width, 8);
assert_eq!(m.buffers.rob_size, 256);
assert_eq!(m.caches.l1d.latency, 3); }
#[test]
fn test_zen4_avx512() {
let m = zen4();
assert!(m.features.has_avx512);
assert_eq!(m.buffers.rob_size, 320);
}
#[test]
fn test_zen5_wider() {
let m = zen5();
assert_eq!(m.pipeline.decode_width, 8);
assert_eq!(m.pipeline.issue_width, 10);
assert_eq!(m.buffers.rob_size, 448);
assert_eq!(m.caches.l1d.size, 49152);
}
#[test]
fn test_zen5c_dense() {
let m = zen5c();
assert_eq!(m.kind, X86MicroArchKind::Zen5c);
assert!(m.caches.l3.as_ref().unwrap().size <= 33554432);
}
#[test]
fn test_silvermont_atom() {
let m = silvermont();
assert_eq!(m.pipeline.issue_width, 2);
assert_eq!(m.buffers.rob_size, 32);
assert!(!m.features.has_smt);
assert_eq!(m.features.uop_cache_entries, 0);
}
#[test]
fn test_goldmont_triple_issue() {
let m = goldmont();
assert_eq!(m.pipeline.decode_width, 3);
assert_eq!(m.pipeline.issue_width, 3);
}
#[test]
fn test_tremont_dual_decode() {
let m = tremont();
assert_eq!(m.pipeline.decode_width, 6); assert_eq!(m.buffers.rob_size, 208);
}
#[test]
fn test_crestmont_ecore() {
let m = crestmont();
assert_eq!(m.kind, X86MicroArchKind::Crestmont);
assert!(!m.features.has_smt);
}
#[test]
fn test_knights_landing_phi() {
let m = knights_landing();
assert_eq!(m.features.smt_threads, 4);
assert!(m.features.has_avx512);
assert_eq!(m.caches.llc_topology, LlcTopology::Mesh);
}
#[test]
fn test_knights_mill_phi() {
let m = knights_mill();
assert_eq!(m.kind, X86MicroArchKind::KnightsMill);
}
#[test]
fn test_latencies_exist_for_all_microarchs() {
let kinds = [
X86MicroArchKind::SandyBridge,
X86MicroArchKind::Haswell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::IceLakeClient,
X86MicroArchKind::AlderLakePcore,
X86MicroArchKind::Zen1,
X86MicroArchKind::Silvermont,
];
for kind in &kinds {
let latencies = instruction_latencies_for(*kind);
assert!(!latencies.is_empty(), "No latencies for {:?}", kind);
}
}
#[test]
fn test_lookup_latency_mov() {
let latency = lookup_latency(X86MicroArchKind::SandyBridge, x86_opcodes::MOV);
assert!(latency.is_some());
let l = latency.unwrap();
assert_eq!(l.latency, 1);
assert_eq!(l.mnemonic, "MOV r,r");
}
#[test]
fn test_lookup_latency_add() {
let latency = lookup_latency(X86MicroArchKind::Haswell, x86_opcodes::ADD);
assert!(latency.is_some());
assert_eq!(latency.unwrap().latency, 1);
}
#[test]
fn test_lookup_latency_div() {
let latency = lookup_latency(X86MicroArchKind::SkylakeClient, x86_opcodes::DIV);
assert!(latency.is_some());
assert!(latency.unwrap().latency >= 18);
}
#[test]
fn test_lookup_latency_missing() {
let latency = lookup_latency(X86MicroArchKind::SandyBridge, 99999);
assert!(latency.is_none());
}
#[test]
fn test_zen1_latencies_have_amd_ports() {
let latencies = instruction_latencies_for(X86MicroArchKind::Zen1);
let add = latencies
.iter()
.find(|l| l.opcode == x86_opcodes::ADD)
.unwrap();
let has_alu_port = add.port_usage.iter().any(|p| p.starts_with("ALU"));
assert!(has_alu_port);
}
#[test]
fn test_intel_latencies_have_numeric_ports() {
let latencies = instruction_latencies_for(X86MicroArchKind::SkylakeClient);
let add = latencies
.iter()
.find(|l| l.opcode == x86_opcodes::ADD)
.unwrap();
let has_numeric_port = add.port_usage.iter().any(|p| p.parse::<u32>().is_ok());
assert!(has_numeric_port);
}
#[test]
fn test_scheduler_models_exist() {
for kind in &[
X86MicroArchKind::SandyBridge,
X86MicroArchKind::Haswell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::Zen1,
X86MicroArchKind::Silvermont,
] {
let m = scheduler_model_for(*kind);
assert!(!m.issue_ports.is_empty());
assert_eq!(m.kind, *kind);
}
}
#[test]
fn test_golden_cove_scheduler_has_fma() {
let m = scheduler_model_for(X86MicroArchKind::AlderLakePcore);
let has_fma_port = m.issue_ports.iter().any(|p| p.capabilities.has_simd_fma);
assert!(has_fma_port);
}
#[test]
fn test_scheduler_load_to_use_latency() {
let zen3_m = scheduler_model_for(X86MicroArchKind::Zen3);
let zen2_m = scheduler_model_for(X86MicroArchKind::Zen2);
assert!(zen3_m.load_to_use_latency <= zen2_m.load_to_use_latency);
}
#[test]
fn test_bypass_latencies_present() {
let m = scheduler_model_for(X86MicroArchKind::SkylakeClient);
assert!(!m.bypass_latencies.is_empty());
let has_intra = m
.bypass_latencies
.iter()
.any(|b| b.from == b.to && b.latency == 0);
assert!(has_intra);
}
#[test]
fn test_granite_rapids_has_extra_ports() {
let m = scheduler_model_for(X86MicroArchKind::GraniteRapids);
assert!(m.issue_ports.len() >= 10);
}
#[test]
fn test_pipeline_hazards_for_all() {
for kind in &[
X86MicroArchKind::SandyBridge,
X86MicroArchKind::Haswell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::Zen1,
X86MicroArchKind::Silvermont,
] {
let h = pipeline_hazards_for(*kind);
assert!(h.branch_mispredict_penalty > 0);
assert!(h.int_raw_hazard_latency > 0);
}
}
#[test]
fn test_out_of_order_has_register_renaming() {
let h = pipeline_hazards_for(X86MicroArchKind::Haswell);
assert!(h.has_register_renaming);
assert!(h.has_store_to_load_forwarding);
}
#[test]
fn test_branch_penalty_reasonable() {
let h = pipeline_hazards_for(X86MicroArchKind::GraniteRapids);
assert!(h.branch_mispredict_penalty >= 14);
assert!(h.branch_mispredict_penalty <= 25);
}
#[test]
fn test_micro_fusion_supported_on_modern_intel() {
let f = micro_fusion_for(X86MicroArchKind::SkylakeClient);
assert!(f.supported);
assert!(f.load_op_fusion);
assert!(f.store_op_fusion);
}
#[test]
fn test_micro_fusion_not_supported_on_atom() {
let f = micro_fusion_for(X86MicroArchKind::Silvermont);
assert!(!f.supported);
}
#[test]
fn test_micro_fusion_zen() {
let f = micro_fusion_for(X86MicroArchKind::Zen3);
assert!(f.supported);
assert!(f.load_op_fusion);
assert!(!f.store_op_fusion); }
#[test]
fn test_macro_fusion_haswell() {
let f = macro_fusion_for(X86MicroArchKind::Haswell);
assert!(f.supported);
assert!(f.fused_pairs.contains(&MacroFusionPair::CmpJcc));
assert!(f.fused_pairs.contains(&MacroFusionPair::TestJcc));
assert!(f.fused_pairs.contains(&MacroFusionPair::AddSubJcc));
}
#[test]
fn test_macro_fusion_icelake_has_dec_jnz() {
let f = macro_fusion_for(X86MicroArchKind::IceLakeClient);
assert!(f.fused_pairs.contains(&MacroFusionPair::DecJnz));
assert!(f.supports_imm32);
}
#[test]
fn test_macro_fusion_zen2() {
let f = macro_fusion_for(X86MicroArchKind::Zen2);
assert!(f.supported);
assert!(f.fused_pairs.contains(&MacroFusionPair::AddSubJcc));
}
#[test]
fn test_macro_fusion_not_supported_silvermont() {
let f = macro_fusion_for(X86MicroArchKind::Silvermont);
assert!(!f.supported);
assert!(f.fused_pairs.is_empty());
}
#[test]
fn test_cache_sizes_are_consistent() {
let m = skylake_client();
assert!(m.caches.l1d.size < m.caches.l2.size);
assert!(m.caches.l2.size < m.caches.l3.as_ref().unwrap().size);
}
#[test]
fn test_atom_no_l3() {
let m = silvermont();
assert!(m.caches.l3.is_none());
assert_eq!(m.caches.llc_topology, LlcTopology::None);
}
#[test]
fn test_knights_landing_no_l3() {
let m = knights_landing();
assert!(m.caches.l3.is_none());
}
#[test]
fn test_tlbs_have_entries() {
let m = skylake_client();
assert!(!m.tlbs.itlb.is_empty());
assert!(!m.tlbs.dtlb.is_empty());
assert!(m.tlbs.stlb.is_some());
}
#[test]
fn test_branch_predictor_modern_is_tage() {
let m = skylake_client();
assert!(matches!(
m.branch_predictor.predictor_kind,
BranchPredictorKind::TAGE
));
}
#[test]
fn test_amd_uses_perceptron() {
let m = zen1();
assert!(matches!(
m.branch_predictor.predictor_kind,
BranchPredictorKind::Perceptron
));
}
#[test]
fn test_atom_uses_simpler_predictor() {
let m = silvermont();
assert!(matches!(
m.branch_predictor.predictor_kind,
BranchPredictorKind::TwoLevel
));
}
#[test]
fn test_haswell_no_avx512() {
let m = haswell();
assert!(!m.features.has_avx512);
}
#[test]
fn test_alder_lake_pcore_has_amx() {
let m = alder_lake_pcore();
assert!(m.features.has_amx);
}
#[test]
fn test_zen4_has_avx512() {
let m = zen4();
assert!(m.features.has_avx512);
}
#[test]
fn test_ecores_no_smt() {
let kinds = [
X86MicroArchKind::AlderLakeEcore,
X86MicroArchKind::Silvermont,
X86MicroArchKind::Goldmont,
X86MicroArchKind::Tremont,
X86MicroArchKind::Crestmont,
];
for kind in &kinds {
let m = microarch_for(*kind);
assert!(
!m.features.has_smt || m.features.smt_threads <= 1,
"E-core {:?} should not have SMT",
kind
);
}
}
#[test]
fn test_microarch_for_all_kinds() {
let kinds = [
X86MicroArchKind::SandyBridge,
X86MicroArchKind::IvyBridge,
X86MicroArchKind::Haswell,
X86MicroArchKind::Broadwell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::SkylakeServer,
X86MicroArchKind::KabyLake,
X86MicroArchKind::CoffeeLake,
X86MicroArchKind::CascadeLake,
X86MicroArchKind::CometLake,
X86MicroArchKind::IceLakeClient,
X86MicroArchKind::IceLakeServer,
X86MicroArchKind::TigerLake,
X86MicroArchKind::RocketLake,
X86MicroArchKind::AlderLakePcore,
X86MicroArchKind::AlderLakeEcore,
X86MicroArchKind::RaptorLakePcore,
X86MicroArchKind::RaptorLakeEcore,
X86MicroArchKind::SapphireRapids,
X86MicroArchKind::EmeraldRapids,
X86MicroArchKind::GraniteRapids,
X86MicroArchKind::SierraForest,
X86MicroArchKind::Zen1,
X86MicroArchKind::ZenPlus,
X86MicroArchKind::Zen2,
X86MicroArchKind::Zen3,
X86MicroArchKind::Zen4,
X86MicroArchKind::Zen5,
X86MicroArchKind::Zen5c,
X86MicroArchKind::Silvermont,
X86MicroArchKind::Airmont,
X86MicroArchKind::Goldmont,
X86MicroArchKind::GoldmontPlus,
X86MicroArchKind::Tremont,
X86MicroArchKind::Gracemont,
X86MicroArchKind::Crestmont,
X86MicroArchKind::KnightsLanding,
X86MicroArchKind::KnightsMill,
];
let total = kinds.len();
let mut seen = 0;
for kind in kinds {
let m = microarch_for(kind);
if m.kind == kind {
seen += 1;
}
}
assert_eq!(seen, total, "Not all microarchitecture kinds are handled");
}
#[test]
fn test_all_models_have_valid_ports() {
let kinds = [
X86MicroArchKind::SandyBridge,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::Zen4,
X86MicroArchKind::Silvermont,
];
for kind in &kinds {
let m = microarch_for(*kind);
let has_capable_port = m.ports.iter().any(|p| {
p.capabilities.has_int_alu
|| p.capabilities.has_fpu_add
|| p.capabilities.has_agu_load
});
assert!(has_capable_port, "No capable ports for {:?}", kind);
}
}
#[test]
fn test_register_file_sizes_consistent() {
let m = skylake_client();
assert!(m.features.phys_reg_int >= 128);
assert!(m.features.phys_reg_fp >= 128);
}
#[test]
fn test_sandy_bridge_has_lsd() {
let m = sandy_bridge();
assert!(m.features.has_lsd);
}
#[test]
fn test_skylake_no_lsd() {
let m = skylake_client();
assert!(!m.features.has_lsd);
}
#[test]
fn test_latency_reciprocal_throughput_consistent() {
let latencies = instruction_latencies_for(X86MicroArchKind::SandyBridge);
for entry in &latencies {
if entry.latency > 0 {
assert!(
entry.reciprocal_throughput >= 0.0,
"Negative reciprocal throughput for {}",
entry.mnemonic
);
}
}
}
#[test]
fn test_all_latency_tables_have_basic_ops() {
let kinds = [
X86MicroArchKind::SandyBridge,
X86MicroArchKind::Haswell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::IceLakeClient,
X86MicroArchKind::AlderLakePcore,
X86MicroArchKind::AlderLakeEcore,
X86MicroArchKind::Zen1,
X86MicroArchKind::Zen2,
X86MicroArchKind::Zen3,
X86MicroArchKind::Zen4,
X86MicroArchKind::Zen5,
X86MicroArchKind::Silvermont,
X86MicroArchKind::Goldmont,
X86MicroArchKind::Tremont,
X86MicroArchKind::KnightsLanding,
];
for kind in &kinds {
let latencies = instruction_latencies_for(*kind);
assert!(
latencies.iter().any(|l| l.opcode == x86_opcodes::MOV),
"{:?} missing MOV",
kind
);
assert!(
latencies.iter().any(|l| l.opcode == x86_opcodes::ADD),
"{:?} missing ADD",
kind
);
}
}
#[test]
fn test_recursive_throughput_is_reasonable() {
for kind in &[
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::Zen3,
X86MicroArchKind::AlderLakePcore,
] {
let latencies = instruction_latencies_for(*kind);
for entry in &latencies {
assert!(entry.reciprocal_throughput >= 0.0);
assert!(entry.reciprocal_throughput <= 50.0);
}
}
}
#[test]
fn test_uop_counts_are_plausible() {
for kind in &[X86MicroArchKind::Haswell, X86MicroArchKind::IceLakeClient] {
let latencies = instruction_latencies_for(*kind);
for entry in &latencies {
assert!(entry.fused_uops <= 50);
assert!(entry.unfused_uops <= 100);
}
}
}
#[test]
fn test_pipeline_depths_are_reasonable() {
let m_skl = microarch_for(X86MicroArchKind::SkylakeClient);
let m_zn5 = microarch_for(X86MicroArchKind::Zen5);
let m_atom = microarch_for(X86MicroArchKind::Silvermont);
assert!(m_skl.pipeline.pipeline_depth >= 10);
assert!(m_skl.pipeline.pipeline_depth <= 20);
assert!(m_zn5.pipeline.pipeline_depth >= 10);
assert!(m_zn5.pipeline.pipeline_depth <= 25);
assert!(m_atom.pipeline.pipeline_depth >= 8);
assert!(m_atom.pipeline.pipeline_depth <= 20);
}
#[test]
fn test_scheduler_entries_match_documentation() {
let m_snb = microarch_for(X86MicroArchKind::SandyBridge);
assert_eq!(m_snb.scheduler.total_entries, 54);
let m_hsw = microarch_for(X86MicroArchKind::Haswell);
assert_eq!(m_hsw.scheduler.total_entries, 60);
let m_skl = microarch_for(X86MicroArchKind::SkylakeClient);
assert_eq!(m_skl.scheduler.total_entries, 97);
let m_adl = microarch_for(X86MicroArchKind::AlderLakePcore);
assert!(m_adl.scheduler.total_entries >= 400);
}
#[test]
fn test_rob_size_increases_across_generations() {
let m_snb = microarch_for(X86MicroArchKind::SandyBridge);
let m_hsw = microarch_for(X86MicroArchKind::Haswell);
let m_skl = microarch_for(X86MicroArchKind::SkylakeClient);
let m_icl = microarch_for(X86MicroArchKind::IceLakeClient);
let m_adl = microarch_for(X86MicroArchKind::AlderLakePcore);
assert!(m_hsw.buffers.rob_size >= m_snb.buffers.rob_size);
assert!(m_skl.buffers.rob_size >= m_hsw.buffers.rob_size);
assert!(m_icl.buffers.rob_size >= m_skl.buffers.rob_size);
assert!(m_adl.buffers.rob_size >= m_icl.buffers.rob_size);
}
#[test]
fn test_amd_rob_growth() {
let m_zn1 = microarch_for(X86MicroArchKind::Zen1);
let m_zn2 = microarch_for(X86MicroArchKind::Zen2);
let m_zn3 = microarch_for(X86MicroArchKind::Zen3);
let m_zn4 = microarch_for(X86MicroArchKind::Zen4);
let m_zn5 = microarch_for(X86MicroArchKind::Zen5);
assert!(m_zn2.buffers.rob_size > m_zn1.buffers.rob_size);
assert!(m_zn3.buffers.rob_size > m_zn2.buffers.rob_size);
assert!(m_zn4.buffers.rob_size > m_zn3.buffers.rob_size);
assert!(m_zn5.buffers.rob_size > m_zn4.buffers.rob_size);
}
#[test]
fn test_issue_width_growth() {
let m_snb = microarch_for(X86MicroArchKind::SandyBridge);
let m_adl = microarch_for(X86MicroArchKind::AlderLakePcore);
assert!(m_adl.pipeline.issue_width >= m_snb.pipeline.issue_width * 2 - 1);
}
#[test]
fn test_macro_fusion_pairs_are_distinct() {
let mf = macro_fusion_for(X86MicroArchKind::IceLakeClient);
let mut seen = std::collections::HashSet::new();
for pair in &mf.fused_pairs {
assert!(seen.insert(*pair as u8), "Duplicate fusion pair {:?}", pair);
}
}
#[test]
fn test_micro_fusion_intel_vs_amd() {
let intel = micro_fusion_for(X86MicroArchKind::SkylakeClient);
let amd = micro_fusion_for(X86MicroArchKind::Zen3);
assert!(intel.store_op_fusion);
assert!(!amd.store_op_fusion);
}
#[test]
fn test_macro_fusion_strictly_expands() {
let snb = macro_fusion_for(X86MicroArchKind::SandyBridge);
let hsw = macro_fusion_for(X86MicroArchKind::Haswell);
let icl = macro_fusion_for(X86MicroArchKind::IceLakeClient);
assert!(hsw.fused_pairs.len() >= snb.fused_pairs.len());
assert!(icl.fused_pairs.len() >= hsw.fused_pairs.len());
}
#[test]
fn test_cache_line_size_is_64_bytes() {
for kind in &[
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::Zen4,
X86MicroArchKind::AlderLakePcore,
X86MicroArchKind::Silvermont,
] {
let m = microarch_for(*kind);
assert_eq!(m.caches.l1i.line_size, 64);
assert_eq!(m.caches.l1d.line_size, 64);
assert_eq!(m.caches.l2.line_size, 64);
if let Some(ref l3) = m.caches.l3 {
assert_eq!(l3.line_size, 64);
}
}
}
#[test]
fn test_cache_latency_ordering() {
let m = microarch_for(X86MicroArchKind::Haswell);
assert!(m.caches.l1d.latency < m.caches.l2.latency);
assert!(m.caches.l2.latency < m.caches.l3.as_ref().unwrap().latency);
}
#[test]
fn test_l1i_l1d_same_size_typical() {
let m = microarch_for(X86MicroArchKind::SkylakeClient);
let equal = m.caches.l1i.size == m.caches.l1d.size;
assert!(m.caches.l1i.size >= 32768);
assert!(m.caches.l1d.size >= 32768);
}
#[test]
fn test_all_models_have_alu() {
for kind in &[
X86MicroArchKind::SandyBridge,
X86MicroArchKind::Haswell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::Zen1,
X86MicroArchKind::Silvermont,
] {
let m = microarch_for(*kind);
let has_alu = m
.execution_units
.iter()
.any(|u| u.unit_type == ExecutionUnitType::AluInt);
assert!(has_alu, "{:?} missing ALU", kind);
}
}
#[test]
fn test_all_models_have_load_store() {
for kind in &[
X86MicroArchKind::Haswell,
X86MicroArchKind::Zen3,
X86MicroArchKind::AlderLakePcore,
] {
let m = microarch_for(*kind);
assert!(m
.execution_units
.iter()
.any(|u| u.unit_type == ExecutionUnitType::AguLoad));
assert!(m
.execution_units
.iter()
.any(|u| u.unit_type == ExecutionUnitType::AguStore));
}
}
#[test]
fn test_haswell_has_fma() {
let m = microarch_for(X86MicroArchKind::Haswell);
let has_fma = m
.execution_units
.iter()
.any(|u| u.unit_type == ExecutionUnitType::FpuFma);
assert!(has_fma, "Haswell introduced FMA");
}
#[test]
fn test_sandy_bridge_no_fma() {
let m = microarch_for(X86MicroArchKind::SandyBridge);
let has_fma = m
.execution_units
.iter()
.any(|u| u.unit_type == ExecutionUnitType::FpuFma);
assert!(!has_fma, "Sandy Bridge did not have FMA");
}
#[test]
fn test_ports_have_non_empty_capabilities() {
for kind in &[
X86MicroArchKind::SandyBridge,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::Zen3,
] {
let m = microarch_for(*kind);
for port in &m.ports {
let caps = &port.capabilities;
let has_any = caps.has_int_alu
|| caps.has_int_mul
|| caps.has_int_div
|| caps.has_branch
|| caps.has_agu_load
|| caps.has_agu_store
|| caps.has_store_data
|| caps.has_fpu_add
|| caps.has_fpu_mul
|| caps.has_fpu_div
|| caps.has_simd_int
|| caps.has_simd_fp
|| caps.has_simd_shuffle
|| caps.has_simd_fma
|| caps.has_avx512_fp
|| caps.has_avx512_int
|| caps.has_pdep_pext;
assert!(
has_any,
"Port {} of {:?} has no capabilities",
port.id, kind
);
}
}
}
#[test]
fn test_some_port_handles_branches() {
for kind in &[
X86MicroArchKind::SandyBridge,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::Zen1,
X86MicroArchKind::AlderLakePcore,
] {
let m = microarch_for(*kind);
let has_branch_port = m.ports.iter().any(|p| p.capabilities.has_branch);
assert!(has_branch_port, "{:?} missing branch port", kind);
}
}
#[test]
fn test_phys_reg_count_plausible() {
let m_snb = microarch_for(X86MicroArchKind::SandyBridge);
let m_adl = microarch_for(X86MicroArchKind::AlderLakePcore);
assert!(m_snb.features.phys_reg_int >= 128);
assert!(m_adl.features.phys_reg_int >= 256);
assert!(m_adl.features.phys_reg_int > m_snb.features.phys_reg_int);
}
#[test]
fn test_modern_microarch_has_stlb() {
for kind in &[
X86MicroArchKind::Haswell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::IceLakeClient,
X86MicroArchKind::AlderLakePcore,
] {
let m = microarch_for(*kind);
assert!(m.tlbs.stlb.is_some(), "{:?} missing STLB", kind);
}
}
#[test]
fn test_tlb_levels_have_valid_entries() {
for kind in &[X86MicroArchKind::SkylakeClient, X86MicroArchKind::Zen3] {
let m = microarch_for(*kind);
for tlb in &m.tlbs.itlb {
assert!(tlb.entries > 0);
assert!(!tlb.page_sizes.is_empty());
}
for tlb in &m.tlbs.dtlb {
assert!(tlb.entries > 0);
assert!(!tlb.page_sizes.is_empty());
}
}
}
#[test]
fn test_branch_predictor_accuracy_improves() {
let m_snb = microarch_for(X86MicroArchKind::SandyBridge);
let m_icl = microarch_for(X86MicroArchKind::IceLakeClient);
let m_adl = microarch_for(X86MicroArchKind::AlderLakePcore);
assert!(
m_icl.branch_predictor.prediction_accuracy_percent
>= m_snb.branch_predictor.prediction_accuracy_percent - 1.0
);
assert!(
m_adl.branch_predictor.prediction_accuracy_percent
>= m_icl.branch_predictor.prediction_accuracy_percent - 1.0
);
}
#[test]
fn test_btb_size_grows() {
let m_snb = microarch_for(X86MicroArchKind::SandyBridge);
let m_adl = microarch_for(X86MicroArchKind::AlderLakePcore);
assert!(m_adl.branch_predictor.btb_entries > m_snb.branch_predictor.btb_entries);
}
#[test]
fn test_rsb_size_is_plausible() {
for kind in &[
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::Zen3,
X86MicroArchKind::AlderLakePcore,
] {
let m = microarch_for(*kind);
assert!(m.branch_predictor.rsb_entries >= 16);
assert!(m.branch_predictor.rsb_entries <= 64);
}
}
#[test]
fn test_uop_cache_only_on_modern_designs() {
let m_snb = microarch_for(X86MicroArchKind::SandyBridge);
let m_slm = microarch_for(X86MicroArchKind::Silvermont);
assert!(m_snb.features.uop_cache_entries > 0);
assert_eq!(m_slm.features.uop_cache_entries, 0);
}
#[test]
fn test_uop_cache_grows() {
let m_snb = microarch_for(X86MicroArchKind::SandyBridge);
let m_icl = microarch_for(X86MicroArchKind::IceLakeClient);
let m_adl = microarch_for(X86MicroArchKind::AlderLakePcore);
assert!(m_icl.features.uop_cache_entries >= m_snb.features.uop_cache_entries);
assert!(m_adl.features.uop_cache_entries >= m_icl.features.uop_cache_entries);
}
#[test]
fn test_hazards_indirect_branch_cost() {
let h = pipeline_hazards_for(X86MicroArchKind::SkylakeClient);
assert!(h.indirect_branch_penalty > h.branch_mispredict_penalty);
assert!(h.return_mispredict_penalty > h.branch_mispredict_penalty);
}
#[test]
fn test_hazards_out_of_order() {
let h_ooo = pipeline_hazards_for(X86MicroArchKind::Haswell);
assert!(h_ooo.has_register_renaming);
assert!(h_ooo.has_store_to_load_forwarding);
}
#[test]
fn test_scheduler_model_port_count_grows() {
let snb_sched = scheduler_model_for(X86MicroArchKind::SandyBridge);
let hsw_sched = scheduler_model_for(X86MicroArchKind::Haswell);
let adl_sched = scheduler_model_for(X86MicroArchKind::AlderLakePcore);
assert!(hsw_sched.issue_ports.len() >= snb_sched.issue_ports.len());
assert!(adl_sched.issue_ports.len() >= hsw_sched.issue_ports.len());
}
#[test]
fn test_bypass_graph_is_complete_for_int_ports() {
let m = scheduler_model_for(X86MicroArchKind::SkylakeClient);
let int_ports: Vec<&str> = m
.issue_ports
.iter()
.filter(|p| p.capabilities.has_int_alu)
.map(|p| p.name)
.collect();
for from in &int_ports {
for to in &int_ports {
let edge_exists = m
.bypass_latencies
.iter()
.any(|b| b.from == *from && b.to == *to);
assert!(
edge_exists,
"Missing bypass edge {} -> {} in {:?}",
from, to, m.kind
);
}
}
}
#[test]
fn test_gracemont_is_alderlake_ecore() {
let gc = gracemont();
let ecore = alder_lake_ecore();
assert_eq!(gc.pipeline.decode_width, ecore.pipeline.decode_width);
assert_eq!(gc.buffers.rob_size, ecore.buffers.rob_size);
}
#[test]
fn test_comet_lake_is_skylake_derivative() {
let cml = microarch_for(X86MicroArchKind::CometLake);
let skl = microarch_for(X86MicroArchKind::SkylakeClient);
assert_eq!(cml.pipeline.issue_width, skl.pipeline.issue_width);
assert_eq!(cml.pipeline.retire_width, skl.pipeline.retire_width);
}
#[test]
fn test_cascade_lake_has_avx512() {
let m = microarch_for(X86MicroArchKind::CascadeLake);
assert!(m.features.has_avx512);
assert_eq!(m.caches.llc_topology, LlcTopology::Mesh);
}
#[test]
fn test_tremont_to_gracemont_rob_growth() {
let m_tnt = microarch_for(X86MicroArchKind::Tremont);
let m_gcm = microarch_for(X86MicroArchKind::Gracemont);
assert!(m_gcm.buffers.rob_size >= m_tnt.buffers.rob_size);
}
#[test]
fn test_sierra_forest_derives_from_ecore() {
let m_sf = microarch_for(X86MicroArchKind::SierraForest);
let m_gcm = microarch_for(X86MicroArchKind::Gracemont);
assert!(!m_sf.features.has_smt);
assert!(!m_sf.features.has_avx512);
assert!(m_sf.caches.l2.size >= m_gcm.caches.l2.size);
}
#[test]
fn test_avx512_present_where_expected() {
let avx512_true = [
X86MicroArchKind::SkylakeServer,
X86MicroArchKind::CascadeLake,
X86MicroArchKind::IceLakeClient,
X86MicroArchKind::IceLakeServer,
X86MicroArchKind::TigerLake,
X86MicroArchKind::RocketLake,
X86MicroArchKind::AlderLakePcore,
X86MicroArchKind::RaptorLakePcore,
X86MicroArchKind::SapphireRapids,
X86MicroArchKind::EmeraldRapids,
X86MicroArchKind::GraniteRapids,
X86MicroArchKind::Zen4,
X86MicroArchKind::Zen5,
X86MicroArchKind::Zen5c,
X86MicroArchKind::KnightsLanding,
X86MicroArchKind::KnightsMill,
];
let avx512_false = [
X86MicroArchKind::SandyBridge,
X86MicroArchKind::IvyBridge,
X86MicroArchKind::Haswell,
X86MicroArchKind::Broadwell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::KabyLake,
X86MicroArchKind::CoffeeLake,
X86MicroArchKind::CometLake,
X86MicroArchKind::AlderLakeEcore,
X86MicroArchKind::RaptorLakeEcore,
X86MicroArchKind::SierraForest,
X86MicroArchKind::Zen1,
X86MicroArchKind::ZenPlus,
X86MicroArchKind::Zen2,
X86MicroArchKind::Zen3,
X86MicroArchKind::Silvermont,
X86MicroArchKind::Airmont,
X86MicroArchKind::Goldmont,
X86MicroArchKind::GoldmontPlus,
X86MicroArchKind::Tremont,
X86MicroArchKind::Gracemont,
X86MicroArchKind::Crestmont,
];
for kind in &avx512_true {
let m = microarch_for(*kind);
assert!(m.features.has_avx512, "{:?} should have AVX-512", kind);
}
for kind in &avx512_false {
let m = microarch_for(*kind);
assert!(!m.features.has_avx512, "{:?} should not have AVX-512", kind);
}
}
#[test]
fn test_smt_expected_configs() {
let smt2 = [
X86MicroArchKind::SandyBridge,
X86MicroArchKind::Haswell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::IceLakeClient,
X86MicroArchKind::AlderLakePcore,
X86MicroArchKind::SapphireRapids,
X86MicroArchKind::GraniteRapids,
X86MicroArchKind::Zen1,
X86MicroArchKind::Zen2,
X86MicroArchKind::Zen3,
X86MicroArchKind::Zen4,
X86MicroArchKind::Zen5,
];
let smt4 = [
X86MicroArchKind::KnightsLanding,
X86MicroArchKind::KnightsMill,
];
for kind in &smt2 {
let m = microarch_for(*kind);
assert!(m.features.has_smt, "{:?} should have SMT", kind);
assert_eq!(m.features.smt_threads, 2, "{:?} SMT threads", kind);
}
for kind in &smt4 {
let m = microarch_for(*kind);
assert_eq!(m.features.smt_threads, 4, "{:?} SMT threads", kind);
}
}
#[test]
fn test_amx_only_on_recent_intel() {
let amx_true = [
X86MicroArchKind::AlderLakePcore,
X86MicroArchKind::RaptorLakePcore,
X86MicroArchKind::SapphireRapids,
X86MicroArchKind::EmeraldRapids,
X86MicroArchKind::GraniteRapids,
];
for kind in &amx_true {
let m = microarch_for(*kind);
assert!(m.features.has_amx, "{:?} should have AMX", kind);
}
}
#[test]
fn test_load_buffer_gt_store_buffer() {
for kind in &[
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::Zen3,
X86MicroArchKind::AlderLakePcore,
X86MicroArchKind::Silvermont,
] {
let m = microarch_for(*kind);
assert!(m.buffers.load_buffer >= m.buffers.store_buffer / 2);
}
}
#[test]
fn test_line_fill_buffer_positive() {
for kind in &[
X86MicroArchKind::Haswell,
X86MicroArchKind::Zen1,
X86MicroArchKind::AlderLakePcore,
] {
let m = microarch_for(*kind);
assert!(m.buffers.line_fill_buffer > 0);
}
}
#[test]
fn test_l2_non_inclusive_modern() {
for kind in &[
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::IceLakeClient,
X86MicroArchKind::Zen1,
X86MicroArchKind::AlderLakePcore,
] {
let m = microarch_for(*kind);
assert!(!m.caches.l2_inclusive);
}
}
#[test]
fn test_llc_topology_variants() {
let m_skl = microarch_for(X86MicroArchKind::SkylakeClient);
let m_skx = microarch_for(X86MicroArchKind::SkylakeServer);
let m_zen = microarch_for(X86MicroArchKind::Zen1);
let m_atom = microarch_for(X86MicroArchKind::Silvermont);
assert_eq!(m_skl.caches.llc_topology, LlcTopology::Ring);
assert_eq!(m_skx.caches.llc_topology, LlcTopology::Mesh);
assert_eq!(m_zen.caches.llc_topology, LlcTopology::PerCcx);
assert_eq!(m_atom.caches.llc_topology, LlcTopology::None);
}
#[test]
fn test_retire_gte_decode_for_ooo() {
for kind in &[
X86MicroArchKind::Haswell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::IceLakeClient,
X86MicroArchKind::AlderLakePcore,
X86MicroArchKind::Zen1,
X86MicroArchKind::Zen3,
] {
let m = microarch_for(*kind);
if m.pipeline.out_of_order {
assert!(
m.pipeline.retire_width >= m.pipeline.decode_width,
"{:?} retire < decode",
kind
);
}
}
}
#[test]
fn test_issue_gte_dispatch() {
for kind in &[
X86MicroArchKind::SandyBridge,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::AlderLakePcore,
X86MicroArchKind::Zen3,
] {
let m = microarch_for(*kind);
assert!(m.pipeline.issue_width >= m.pipeline.dispatch_width);
}
}
#[test]
fn test_intel_core_generational_improvements() {
let generations: &[(X86MicroArchKind, u32, u32)] = &[
(X86MicroArchKind::SandyBridge, 168, 6),
(X86MicroArchKind::Haswell, 192, 8),
(X86MicroArchKind::Broadwell, 192, 8),
(X86MicroArchKind::SkylakeClient, 224, 8),
(X86MicroArchKind::IceLakeClient, 352, 10),
(X86MicroArchKind::AlderLakePcore, 512, 12),
(X86MicroArchKind::GraniteRapids, 576, 12),
];
for (kind, expected_rob, expected_issue) in generations {
let m = microarch_for(*kind);
assert_eq!(
m.buffers.rob_size, *expected_rob,
"{:?} ROB size mismatch",
kind
);
assert_eq!(
m.pipeline.issue_width, *expected_issue,
"{:?} issue width mismatch",
kind
);
}
}
#[test]
fn test_amd_zen_generational_improvements() {
let gens: &[(X86MicroArchKind, u32, u32)] = &[
(X86MicroArchKind::Zen1, 192, 6),
(X86MicroArchKind::Zen2, 224, 7),
(X86MicroArchKind::Zen3, 256, 8),
(X86MicroArchKind::Zen4, 320, 9),
(X86MicroArchKind::Zen5, 448, 10),
];
for (kind, expected_rob, expected_issue) in gens {
let m = microarch_for(*kind);
assert_eq!(
m.buffers.rob_size, *expected_rob,
"{:?} ROB size mismatch",
kind
);
assert_eq!(
m.pipeline.issue_width, *expected_issue,
"{:?} issue width mismatch",
kind
);
}
}
#[test]
fn test_atom_generational_improvements() {
let gens: &[(X86MicroArchKind, u32, u32)] = &[
(X86MicroArchKind::Silvermont, 32, 2),
(X86MicroArchKind::Goldmont, 72, 3),
(X86MicroArchKind::Tremont, 208, 4),
(X86MicroArchKind::Gracemont, 256, 8),
(X86MicroArchKind::Crestmont, 256, 8),
];
for (kind, expected_rob, expected_issue) in gens {
let m = microarch_for(*kind);
assert_eq!(
m.buffers.rob_size, *expected_rob,
"{:?} ROB size mismatch",
kind
);
assert_eq!(
m.pipeline.issue_width, *expected_issue,
"{:?} issue width mismatch",
kind
);
}
}
#[test]
fn test_latency_add_is_always_1_cycle() {
for kind in &[
X86MicroArchKind::SandyBridge,
X86MicroArchKind::Haswell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::IceLakeClient,
X86MicroArchKind::AlderLakePcore,
X86MicroArchKind::AlderLakeEcore,
X86MicroArchKind::Zen1,
X86MicroArchKind::Zen3,
X86MicroArchKind::Zen5,
X86MicroArchKind::Silvermont,
X86MicroArchKind::Goldmont,
X86MicroArchKind::Tremont,
X86MicroArchKind::KnightsLanding,
] {
let lat = lookup_latency(*kind, x86_opcodes::ADD);
assert!(lat.is_some(), "No ADD latency for {:?}", kind);
assert_eq!(lat.unwrap().latency, 1, "ADD latency != 1 for {:?}", kind);
}
}
#[test]
fn test_latency_div_is_high() {
for kind in &[
X86MicroArchKind::SandyBridge,
X86MicroArchKind::Haswell,
X86MicroArchKind::Zen1,
X86MicroArchKind::Silvermont,
] {
let lat = lookup_latency(*kind, x86_opcodes::DIV);
assert!(lat.is_some());
assert!(lat.unwrap().latency >= 16);
}
}
#[test]
fn test_latency_lea_is_fast() {
for kind in &[
X86MicroArchKind::SandyBridge,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::Zen1,
] {
let lat = lookup_latency(*kind, x86_opcodes::LEA);
assert!(lat.is_some());
assert!(lat.unwrap().latency <= 2);
}
}
#[test]
fn test_macro_fusion_pair_derives() {
let pair1 = MacroFusionPair::CmpJcc;
let pair2 = MacroFusionPair::CmpJcc;
let pair3 = MacroFusionPair::TestJcc;
assert_eq!(pair1, pair2);
assert_ne!(pair1, pair3);
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(pair1);
assert!(set.contains(&pair1));
assert!(!set.contains(&pair3));
}
#[test]
fn test_all_port_capabilities_are_used() {
let m = alder_lake_pcore();
let port0 = m.ports.iter().find(|p| p.id == "0").unwrap();
assert!(port0.capabilities.has_int_alu);
assert!(port0.capabilities.has_fpu_add);
assert!(port0.capabilities.has_avx512_fp);
assert!(port0.capabilities.has_pdep_pext);
assert!(!port0.capabilities.has_branch);
assert!(!port0.capabilities.has_agu_load);
assert!(!port0.capabilities.has_store_data);
}
#[test]
fn test_aggressive_load_ports() {
let m = skylake_client();
let load_ports: Vec<_> = m
.ports
.iter()
.filter(|p| p.capabilities.has_agu_load)
.collect();
assert_eq!(load_ports.len(), 2);
assert!(load_ports.iter().any(|p| p.id == "2"));
assert!(load_ports.iter().any(|p| p.id == "3"));
}
#[test]
fn test_port4_handles_store_data() {
for kind in &[
X86MicroArchKind::Haswell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::IceLakeClient,
X86MicroArchKind::AlderLakePcore,
] {
let m = microarch_for(*kind);
let port4 = m.ports.iter().find(|p| p.id == "4");
assert!(port4.is_some(), "{:?} missing port 4", kind);
assert!(
port4.unwrap().capabilities.has_store_data,
"{:?} port 4 missing store_data",
kind
);
}
}
#[test]
fn test_scheduler_bypass_is_symmetric() {
let m = scheduler_model_for(X86MicroArchKind::SkylakeClient);
for edge in &m.bypass_latencies {
if edge.from != edge.to {
let reverse = m
.bypass_latencies
.iter()
.find(|b| b.from == edge.to && b.to == edge.from);
assert!(
reverse.is_some(),
"Missing reverse bypass {} -> {} for {:?}",
edge.to,
edge.from,
m.kind
);
}
}
}
#[test]
fn test_hazard_penalties_consistent_with_model() {
let m = microarch_for(X86MicroArchKind::SkylakeClient);
let h = pipeline_hazards_for(X86MicroArchKind::SkylakeClient);
assert_eq!(
h.branch_mispredict_penalty,
m.branch_predictor.mispredict_penalty
);
}
#[test]
fn test_hazard_fp_latency_is_reasonable() {
for kind in &[
X86MicroArchKind::Haswell,
X86MicroArchKind::Zen3,
X86MicroArchKind::AlderLakePcore,
] {
let h = pipeline_hazards_for(*kind);
assert!(h.fp_raw_hazard_latency >= 3);
assert!(h.fp_raw_hazard_latency <= 8);
}
}
#[test]
fn test_emerald_rapids_large_l3() {
let m = emerald_rapids();
assert!(m.caches.l3.as_ref().unwrap().size >= 104857600);
}
#[test]
fn test_sapphire_rapids_l2() {
let m = sapphire_rapids();
assert_eq!(m.caches.l2.size, 2097152); }
#[test]
fn test_tiger_lake_l3() {
let m = tiger_lake();
assert_eq!(m.caches.l3.as_ref().unwrap().size, 12582912); }
#[test]
fn test_dispatch_width_growth() {
let m_snb = microarch_for(X86MicroArchKind::SandyBridge);
let m_adl = microarch_for(X86MicroArchKind::AlderLakePcore);
let m_gnr = microarch_for(X86MicroArchKind::GraniteRapids);
assert_eq!(m_snb.pipeline.dispatch_width, 4);
assert_eq!(m_adl.pipeline.dispatch_width, 8);
assert_eq!(m_gnr.pipeline.dispatch_width, 8);
}
#[test]
fn test_haswell_feature_flags() {
let m = haswell();
assert!(!m.features.has_avx512);
assert!(!m.features.has_amx);
assert!(m.features.has_smt);
assert!(m.features.has_macro_fusion);
assert!(m.features.has_micro_fusion);
}
#[test]
fn test_ice_lake_feature_flags() {
let m = ice_lake_client();
assert!(m.features.has_avx512);
assert!(!m.features.has_amx);
assert!(m.features.has_macro_fusion);
}
#[test]
fn test_alder_lake_ecore_feature_flags() {
let m = alder_lake_ecore();
assert!(!m.features.has_avx512);
assert!(!m.features.has_amx);
assert!(!m.features.has_smt);
assert_eq!(m.features.uop_cache_entries, 0);
}
#[test]
fn test_ivy_bridge_differs_from_sandy_bridge() {
let snb = sandy_bridge();
let ivb = ivy_bridge();
assert_ne!(snb.kind, ivb.kind);
assert_eq!(ivb.kind, X86MicroArchKind::IvyBridge);
let snb_div = snb
.execution_units
.iter()
.find(|u| u.unit_type == ExecutionUnitType::FpuDiv)
.unwrap();
let ivb_div = ivb
.execution_units
.iter()
.find(|u| u.unit_type == ExecutionUnitType::FpuDiv)
.unwrap();
assert!(ivb_div.typical_latency <= snb_div.typical_latency);
}
#[test]
fn test_broadwell_differs_from_haswell() {
let hsw = haswell();
let bdw = broadwell();
assert_ne!(hsw.kind, bdw.kind);
assert_eq!(bdw.kind, X86MicroArchKind::Broadwell);
assert!(bdw.scheduler.total_entries >= hsw.scheduler.total_entries);
let hsw_mul = hsw
.execution_units
.iter()
.find(|u| u.unit_type == ExecutionUnitType::FpuMul)
.unwrap();
let bdw_mul = bdw
.execution_units
.iter()
.find(|u| u.unit_type == ExecutionUnitType::FpuMul)
.unwrap();
assert!(bdw_mul.typical_latency <= hsw_mul.typical_latency);
}
#[test]
fn test_rocket_lake_backport() {
let m = rocket_lake();
assert_eq!(m.pipeline.decode_width, 5);
assert_eq!(m.buffers.rob_size, 352);
assert_eq!(m.caches.l2.size, 524288);
}
#[test]
fn test_pcore_larger_than_ecore() {
let pcore = alder_lake_pcore();
let ecore = alder_lake_ecore();
assert!(pcore.buffers.rob_size > ecore.buffers.rob_size);
assert!(pcore.buffers.load_buffer > ecore.buffers.load_buffer);
assert!(pcore.buffers.store_buffer > ecore.buffers.store_buffer);
assert!(pcore.ports.len() > ecore.ports.len());
}
#[test]
fn test_raptor_lake_vs_alder_lake() {
let adl = alder_lake_pcore();
let rpl = raptor_lake_pcore();
assert!(rpl.caches.l2.size > adl.caches.l2.size);
assert_eq!(rpl.pipeline.issue_width, adl.pipeline.issue_width);
}
#[test]
fn test_zen5c_smaller_than_zen5() {
let zn5 = zen5();
let zn5c = zen5c();
assert_eq!(zn5c.pipeline.issue_width, zn5.pipeline.issue_width);
assert!(zn5c.caches.l3.as_ref().unwrap().size <= zn5.caches.l3.as_ref().unwrap().size);
}
#[test]
fn test_all_predictor_kinds_are_valid() {
let kinds = [
X86MicroArchKind::SandyBridge,
X86MicroArchKind::Haswell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::IceLakeClient,
X86MicroArchKind::AlderLakePcore,
X86MicroArchKind::AlderLakeEcore,
X86MicroArchKind::GraniteRapids,
X86MicroArchKind::SierraForest,
X86MicroArchKind::Zen1,
X86MicroArchKind::Zen2,
X86MicroArchKind::Zen3,
X86MicroArchKind::Zen4,
X86MicroArchKind::Zen5,
X86MicroArchKind::Silvermont,
X86MicroArchKind::Goldmont,
X86MicroArchKind::Tremont,
X86MicroArchKind::KnightsLanding,
];
for kind in &kinds {
let m = microarch_for(*kind);
assert!(m.branch_predictor.btb_entries > 0, "{:?} has no BTB", kind);
assert!(m.branch_predictor.rsb_entries > 0, "{:?} has no RSB", kind);
assert!(
m.branch_predictor.prediction_accuracy_percent >= 85.0,
"{:?} accuracy too low: {}",
kind,
m.branch_predictor.prediction_accuracy_percent
);
assert!(
m.branch_predictor.prediction_accuracy_percent <= 100.0,
"{:?} accuracy too high: {}",
kind,
m.branch_predictor.prediction_accuracy_percent
);
}
}
#[test]
fn test_execution_unit_type_enum_complete() {
let all_types = [
ExecutionUnitType::AluInt,
ExecutionUnitType::AluIntComplex,
ExecutionUnitType::AguLoad,
ExecutionUnitType::AguStore,
ExecutionUnitType::FpuAdd,
ExecutionUnitType::FpuMul,
ExecutionUnitType::FpuDiv,
ExecutionUnitType::FpuFma,
ExecutionUnitType::SimdInt,
ExecutionUnitType::SimdFp,
ExecutionUnitType::SimdShuffle,
ExecutionUnitType::SimdFma,
ExecutionUnitType::Branch,
ExecutionUnitType::IntDivider,
ExecutionUnitType::IntMultiplier,
];
let m = alder_lake_pcore();
for t in &all_types {
let found = m.execution_units.iter().any(|u| u.unit_type == *t);
match t {
ExecutionUnitType::AluInt
| ExecutionUnitType::AguLoad
| ExecutionUnitType::AguStore
| ExecutionUnitType::Branch => {
assert!(found, "{:?} missing {:?}", m.kind, t);
}
_ => {}
}
}
}
#[test]
fn test_name_matches_doc_claim() {
assert!(X86MicroArchKind::SandyBridge
.name()
.contains("Sandy Bridge"));
assert!(X86MicroArchKind::Zen5.name().contains("Zen 5"));
assert!(X86MicroArchKind::GraniteRapids
.name()
.contains("Granite Rapids"));
assert!(X86MicroArchKind::KnightsLanding.name().contains("Xeon Phi"));
assert!(X86MicroArchKind::Silvermont.name().contains("Silvermont"));
assert!(X86MicroArchKind::Crestmont.name().contains("Crestmont"));
}
#[test]
fn test_latency_tables_no_duplicate_entries() {
for kind in &[
X86MicroArchKind::SandyBridge,
X86MicroArchKind::Haswell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::Zen1,
] {
let latencies = instruction_latencies_for(*kind);
let mut seen = std::collections::HashSet::new();
for entry in &latencies {
assert!(
seen.insert(entry.opcode),
"Duplicate opcode {} in {:?} latency table",
entry.opcode,
kind
);
}
}
}
#[test]
fn test_nop_always_zero_cost() {
for kind in &[
X86MicroArchKind::SandyBridge,
X86MicroArchKind::Haswell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::AlderLakeEcore,
X86MicroArchKind::Zen1,
] {
let lat = lookup_latency(*kind, x86_opcodes::NOP);
if let Some(l) = lat {
assert_eq!(l.latency, 0, "NOP latency != 0 for {:?}", kind);
assert_eq!(l.fused_uops, 0);
}
}
}
#[test]
fn test_total_kind_count() {
let all = [
X86MicroArchKind::SandyBridge,
X86MicroArchKind::IvyBridge,
X86MicroArchKind::Haswell,
X86MicroArchKind::Broadwell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::SkylakeServer,
X86MicroArchKind::KabyLake,
X86MicroArchKind::CoffeeLake,
X86MicroArchKind::CascadeLake,
X86MicroArchKind::CometLake,
X86MicroArchKind::IceLakeClient,
X86MicroArchKind::IceLakeServer,
X86MicroArchKind::TigerLake,
X86MicroArchKind::RocketLake,
X86MicroArchKind::AlderLakePcore,
X86MicroArchKind::AlderLakeEcore,
X86MicroArchKind::RaptorLakePcore,
X86MicroArchKind::RaptorLakeEcore,
X86MicroArchKind::SapphireRapids,
X86MicroArchKind::EmeraldRapids,
X86MicroArchKind::GraniteRapids,
X86MicroArchKind::SierraForest,
X86MicroArchKind::Zen1,
X86MicroArchKind::ZenPlus,
X86MicroArchKind::Zen2,
X86MicroArchKind::Zen3,
X86MicroArchKind::Zen4,
X86MicroArchKind::Zen5,
X86MicroArchKind::Zen5c,
X86MicroArchKind::Silvermont,
X86MicroArchKind::Airmont,
X86MicroArchKind::Goldmont,
X86MicroArchKind::GoldmontPlus,
X86MicroArchKind::Tremont,
X86MicroArchKind::Gracemont,
X86MicroArchKind::Crestmont,
X86MicroArchKind::KnightsLanding,
X86MicroArchKind::KnightsMill,
];
assert_eq!(all.len(), 38, "Expected 38 microarchitecture variants");
}
#[test]
fn test_kaby_lake_is_skylake_derivative() {
let skl = skylake_client();
let kbl = kaby_lake();
assert_eq!(kbl.pipeline.decode_width, skl.pipeline.decode_width);
assert_eq!(kbl.pipeline.issue_width, skl.pipeline.issue_width);
assert_eq!(kbl.buffers.rob_size, skl.buffers.rob_size);
}
#[test]
fn test_coffee_lake_comet_lake_same_pipeline() {
let cfl = coffee_lake();
let cml = comet_lake();
assert_eq!(cfl.pipeline.issue_width, cml.pipeline.issue_width);
assert_eq!(
cfl.features.uop_cache_entries,
cml.features.uop_cache_entries
);
}
#[test]
fn test_zen1_and_zen_plus_similar() {
let zn1 = zen1();
let znp = zen_plus();
assert_eq!(zn1.pipeline.decode_width, znp.pipeline.decode_width);
assert_eq!(zn1.pipeline.issue_width, znp.pipeline.issue_width);
}
#[test]
fn test_airmont_is_silvermont_derivative() {
let slm = silvermont();
let arm = airmont();
assert_eq!(slm.pipeline.issue_width, arm.pipeline.issue_width);
assert_ne!(slm.kind, arm.kind);
}
#[test]
fn test_goldmont_plus_is_goldmont_variant() {
let glm = goldmont();
let glp = goldmont_plus();
assert_eq!(glm.pipeline.decode_width, glp.pipeline.decode_width);
assert!(glp.buffers.rob_size >= glm.buffers.rob_size);
}
#[test]
fn test_div_latency_improves_over_generations() {
let snb_div = lookup_latency(X86MicroArchKind::SandyBridge, x86_opcodes::DIV).unwrap();
let icl_div = lookup_latency(X86MicroArchKind::IceLakeClient, x86_opcodes::DIV).unwrap();
let adl_div = lookup_latency(X86MicroArchKind::AlderLakePcore, x86_opcodes::DIV).unwrap();
assert!(icl_div.latency <= snb_div.latency + 2);
assert!(adl_div.latency <= icl_div.latency + 2);
}
#[test]
fn test_mul_latency_consistent_range() {
for kind in &[
X86MicroArchKind::SandyBridge,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::IceLakeClient,
] {
let lat = lookup_latency(*kind, x86_opcodes::MUL);
assert!(lat.is_some());
let l = lat.unwrap();
assert!(l.latency >= 3 && l.latency <= 5);
}
}
#[test]
fn test_intel_ports_are_numeric() {
let intel_kinds = [
X86MicroArchKind::SandyBridge,
X86MicroArchKind::Haswell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::IceLakeClient,
X86MicroArchKind::AlderLakePcore,
X86MicroArchKind::Silvermont,
X86MicroArchKind::Tremont,
X86MicroArchKind::KnightsLanding,
];
for kind in &intel_kinds {
let m = microarch_for(*kind);
for port in &m.ports {
assert!(port.id.parse::<u32>().is_ok());
}
}
}
#[test]
fn test_amd_ports_are_alphanumeric() {
let amd_kinds = [
X86MicroArchKind::Zen1,
X86MicroArchKind::Zen2,
X86MicroArchKind::Zen3,
X86MicroArchKind::Zen4,
X86MicroArchKind::Zen5,
];
for kind in &amd_kinds {
let m = microarch_for(*kind);
for port in &m.ports {
assert!(
port.id.starts_with("ALU")
|| port.id.starts_with("AGU")
|| port.id.starts_with("FPU")
);
}
}
}
#[test]
fn test_scheduler_ratio_is_plausible() {
let m = microarch_for(X86MicroArchKind::AlderLakePcore);
let ratio = m.scheduler.total_entries as f64 / m.buffers.rob_size as f64;
assert!(ratio > 0.2 && ratio < 1.5);
}
#[test]
fn test_buffer_sizes_monotonic() {
let m_snb = microarch_for(X86MicroArchKind::SandyBridge);
let m_hsw = microarch_for(X86MicroArchKind::Haswell);
let m_skl = microarch_for(X86MicroArchKind::SkylakeClient);
let m_icl = microarch_for(X86MicroArchKind::IceLakeClient);
let m_adl = microarch_for(X86MicroArchKind::AlderLakePcore);
let models = [&m_snb, &m_hsw, &m_skl, &m_icl, &m_adl];
for i in 1..models.len() {
assert!(models[i].buffers.rob_size >= models[i - 1].buffers.rob_size);
assert!(models[i].buffers.load_buffer >= models[i - 1].buffers.load_buffer);
}
}
#[test]
fn test_store_data_port_present() {
let hsw = haswell();
let store_data_ports: Vec<_> = hsw
.ports
.iter()
.filter(|p| p.capabilities.has_store_data)
.collect();
assert!(!store_data_ports.is_empty());
}
#[test]
fn test_microarch_is_materialized_consistently() {
for kind in &[
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::Zen3,
X86MicroArchKind::AlderLakePcore,
] {
let m1 = microarch_for(*kind);
let m2 = microarch_for(*kind);
assert_eq!(m1.pipeline.decode_width, m2.pipeline.decode_width);
assert_eq!(m1.buffers.rob_size, m2.buffers.rob_size);
assert_eq!(m1.ports.len(), m2.ports.len());
}
}
#[test]
fn test_all_models_have_nonzero_l1() {
for kind in &[
X86MicroArchKind::SandyBridge,
X86MicroArchKind::Haswell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::IceLakeClient,
X86MicroArchKind::AlderLakePcore,
X86MicroArchKind::AlderLakeEcore,
X86MicroArchKind::Zen1,
X86MicroArchKind::Zen5,
X86MicroArchKind::Silvermont,
X86MicroArchKind::Goldmont,
X86MicroArchKind::Tremont,
X86MicroArchKind::KnightsLanding,
] {
let m = microarch_for(*kind);
assert!(m.caches.l1i.size > 0);
assert!(m.caches.l1d.size > 0);
assert!(m.caches.l2.size > 0);
}
}
#[test]
fn test_associativity_is_power_of_2_or_full() {
let m = haswell();
fn is_power_of_2_or_0(n: u32) -> bool {
n == 0 || (n & (n - 1)) == 0
}
assert!(is_power_of_2_or_0(m.caches.l1i.associativity));
assert!(is_power_of_2_or_0(m.caches.l1d.associativity));
assert!(is_power_of_2_or_0(m.caches.l2.associativity));
}
#[test]
fn test_feature_flags_self_consistent() {
for kind in &[
X86MicroArchKind::AlderLakePcore,
X86MicroArchKind::SapphireRapids,
X86MicroArchKind::GraniteRapids,
] {
let m = microarch_for(*kind);
if m.features.has_amx {
assert!(m.features.has_avx512);
}
}
}
#[test]
fn test_version_names_are_unique() {
let mut names = std::collections::HashSet::new();
let all = [
X86MicroArchKind::SandyBridge,
X86MicroArchKind::IvyBridge,
X86MicroArchKind::Haswell,
X86MicroArchKind::Broadwell,
X86MicroArchKind::SkylakeClient,
X86MicroArchKind::SkylakeServer,
X86MicroArchKind::KabyLake,
X86MicroArchKind::CoffeeLake,
X86MicroArchKind::CascadeLake,
X86MicroArchKind::CometLake,
X86MicroArchKind::IceLakeClient,
X86MicroArchKind::IceLakeServer,
X86MicroArchKind::TigerLake,
X86MicroArchKind::RocketLake,
X86MicroArchKind::AlderLakePcore,
X86MicroArchKind::AlderLakeEcore,
X86MicroArchKind::RaptorLakePcore,
X86MicroArchKind::RaptorLakeEcore,
X86MicroArchKind::SapphireRapids,
X86MicroArchKind::EmeraldRapids,
X86MicroArchKind::GraniteRapids,
X86MicroArchKind::SierraForest,
X86MicroArchKind::Zen1,
X86MicroArchKind::ZenPlus,
X86MicroArchKind::Zen2,
X86MicroArchKind::Zen3,
X86MicroArchKind::Zen4,
X86MicroArchKind::Zen5,
X86MicroArchKind::Zen5c,
X86MicroArchKind::Silvermont,
X86MicroArchKind::Airmont,
X86MicroArchKind::Goldmont,
X86MicroArchKind::GoldmontPlus,
X86MicroArchKind::Tremont,
X86MicroArchKind::Gracemont,
X86MicroArchKind::Crestmont,
X86MicroArchKind::KnightsLanding,
X86MicroArchKind::KnightsMill,
];
for kind in &all {
let name = kind.name();
assert!(names.insert(name), "Duplicate name: {}", name);
}
}
}