use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum X86MicroArch {
Skylake,
IceLake,
AlderLakePcore,
AlderLakeEcore,
SapphireRapids,
GraniteRapids,
Zen3,
Zen4,
Zen5,
}
impl X86MicroArch {
pub fn name(&self) -> &'static str {
match self {
X86MicroArch::Skylake => "skylake",
X86MicroArch::IceLake => "icelake",
X86MicroArch::AlderLakePcore => "alderlake_pcore",
X86MicroArch::AlderLakeEcore => "alderlake_ecore",
X86MicroArch::SapphireRapids => "sapphirerapids",
X86MicroArch::GraniteRapids => "graniterapids",
X86MicroArch::Zen3 => "zen3",
X86MicroArch::Zen4 => "zen4",
X86MicroArch::Zen5 => "zen5",
}
}
pub fn is_intel(&self) -> bool {
matches!(
self,
X86MicroArch::Skylake
| X86MicroArch::IceLake
| X86MicroArch::AlderLakePcore
| X86MicroArch::AlderLakeEcore
| X86MicroArch::SapphireRapids
| X86MicroArch::GraniteRapids
)
}
pub fn is_amd(&self) -> bool {
matches!(
self,
X86MicroArch::Zen3 | X86MicroArch::Zen4 | X86MicroArch::Zen5
)
}
pub fn is_pcore(&self) -> bool {
!matches!(self, X86MicroArch::AlderLakeEcore)
}
pub fn is_ecore(&self) -> bool {
matches!(self, X86MicroArch::AlderLakeEcore)
}
pub fn has_avx512(&self) -> bool {
matches!(
self,
X86MicroArch::Skylake
| X86MicroArch::IceLake
| X86MicroArch::SapphireRapids
| X86MicroArch::GraniteRapids
| X86MicroArch::Zen4
| X86MicroArch::Zen5
)
}
pub fn issue_width(&self) -> u32 {
match self {
X86MicroArch::Skylake => 4,
X86MicroArch::IceLake => 5,
X86MicroArch::AlderLakePcore => 6,
X86MicroArch::AlderLakeEcore => 5,
X86MicroArch::SapphireRapids => 6,
X86MicroArch::GraniteRapids => 8,
X86MicroArch::Zen3 => 6,
X86MicroArch::Zen4 => 6,
X86MicroArch::Zen5 => 8,
}
}
pub fn rob_size(&self) -> u32 {
match self {
X86MicroArch::Skylake => 224,
X86MicroArch::IceLake => 352,
X86MicroArch::AlderLakePcore => 512,
X86MicroArch::AlderLakeEcore => 256,
X86MicroArch::SapphireRapids => 512,
X86MicroArch::GraniteRapids => 640,
X86MicroArch::Zen3 => 256,
X86MicroArch::Zen4 => 320,
X86MicroArch::Zen5 => 448,
}
}
pub fn load_latency(&self) -> u32 {
match self {
X86MicroArch::Skylake => 4,
X86MicroArch::IceLake => 5,
X86MicroArch::AlderLakePcore => 5,
X86MicroArch::AlderLakeEcore => 5,
X86MicroArch::SapphireRapids => 5,
X86MicroArch::GraniteRapids => 5,
X86MicroArch::Zen3 => 4,
X86MicroArch::Zen4 => 4,
X86MicroArch::Zen5 => 4,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PortMask(pub u64);
impl PortMask {
pub const NONE: PortMask = PortMask(0);
pub const P0: PortMask = PortMask(1 << 0);
pub const P1: PortMask = PortMask(1 << 1);
pub const P2: PortMask = PortMask(1 << 2);
pub const P3: PortMask = PortMask(1 << 3);
pub const P4: PortMask = PortMask(1 << 4);
pub const P5: PortMask = PortMask(1 << 5);
pub const P6: PortMask = PortMask(1 << 6);
pub const P7: PortMask = PortMask(1 << 7);
pub const P8: PortMask = PortMask(1 << 8);
pub const P9: PortMask = PortMask(1 << 9);
pub const P10: PortMask = PortMask(1 << 10);
pub const P11: PortMask = PortMask(1 << 11);
pub const P12: PortMask = PortMask(1 << 12);
pub const P13: PortMask = PortMask(1 << 13);
pub const P015: PortMask = PortMask(PortMask::P0.0 | PortMask::P1.0 | PortMask::P5.0);
pub const P0156: PortMask = PortMask(PortMask::P015.0 | PortMask::P6.0);
pub const P06: PortMask = PortMask(PortMask::P0.0 | PortMask::P6.0);
pub const P15: PortMask = PortMask(PortMask::P1.0 | PortMask::P5.0);
pub const P23: PortMask = PortMask(PortMask::P2.0 | PortMask::P3.0);
pub const P237: PortMask = PortMask(PortMask::P23.0 | PortMask::P7.0);
pub const P01: PortMask = PortMask(PortMask::P0.0 | PortMask::P1.0);
pub const P05: PortMask = PortMask(PortMask::P0.0 | PortMask::P5.0);
pub const P156: PortMask = PortMask(PortMask::P1.0 | PortMask::P5.0 | PortMask::P6.0);
pub const P234: PortMask = PortMask(PortMask::P2.0 | PortMask::P3.0 | PortMask::P4.0);
pub const P238: PortMask = PortMask(PortMask::P2.0 | PortMask::P3.0 | PortMask::P8.0);
pub const P23_P01: PortMask = PortMask(PortMask::P23.0 | PortMask::P01.0);
pub const P237_P4: PortMask = PortMask(PortMask::P237.0 | PortMask::P4.0);
pub const P237_P0156: PortMask = PortMask(PortMask::P237.0 | PortMask::P0156.0);
pub const P015689: PortMask = PortMask(
PortMask::P0.0
| PortMask::P1.0
| PortMask::P5.0
| PortMask::P6.0
| PortMask::P8.0
| PortMask::P9.0,
);
pub const P015678: PortMask = PortMask(
PortMask::P0.0
| PortMask::P1.0
| PortMask::P5.0
| PortMask::P6.0
| PortMask::P7.0
| PortMask::P8.0,
);
pub fn contains(self, other: PortMask) -> bool {
(self.0 & other.0) != 0
}
pub fn count(self) -> u32 {
self.0.count_ones()
}
pub fn is_subset_of(self, other: PortMask) -> bool {
(self.0 & !other.0) == 0
}
}
impl fmt::Display for PortMask {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0 == 0 {
return write!(f, "none");
}
let mut first = true;
for i in 0..14 {
if (self.0 >> i) & 1 != 0 {
if !first {
write!(f, ",")?;
}
write!(f, "p{}", i)?;
first = false;
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct InstrLatencyRecord {
pub mnemonic: &'static str,
pub size: &'static str,
pub reg_reg: bool,
pub reg_mem: bool,
pub latency: u32,
pub reciprocal_throughput: f64,
pub uops: u32,
pub uops_unfused: u32,
pub ports: PortMask,
pub zero_latency: bool,
pub variable_latency: bool,
pub latency_min: Option<u32>,
pub latency_max: Option<u32>,
pub uses_divider: bool,
pub can_macro_fuse: bool,
pub can_micro_fuse: bool,
pub has_bypass: bool,
pub bypass_from: Option<&'static [(BypassProducer, u32)]>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BypassProducer {
IntegerAlu,
IntegerMul,
IntegerDiv,
FloatAlu,
FloatDiv,
SimdAlu,
SimdShuffle,
SimdMul,
LoadL1,
LoadL2,
LoadL3,
LoadMem,
AddressGen,
Branch,
StringOp,
}
impl BypassProducer {
pub fn name(&self) -> &'static str {
match self {
BypassProducer::IntegerAlu => "INT_ALU",
BypassProducer::IntegerMul => "INT_MUL",
BypassProducer::IntegerDiv => "INT_DIV",
BypassProducer::FloatAlu => "FP_ALU",
BypassProducer::FloatDiv => "FP_DIV",
BypassProducer::SimdAlu => "SIMD_ALU",
BypassProducer::SimdShuffle => "SIMD_SHUFFLE",
BypassProducer::SimdMul => "SIMD_MUL",
BypassProducer::LoadL1 => "LOAD_L1",
BypassProducer::LoadL2 => "LOAD_L2",
BypassProducer::LoadL3 => "LOAD_L3",
BypassProducer::LoadMem => "LOAD_MEM",
BypassProducer::AddressGen => "ADDR_GEN",
BypassProducer::Branch => "BRANCH",
BypassProducer::StringOp => "STRING",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StoreLoadForwardClass {
ExactMatch,
LoadContainsStore,
StoreContainsLoad,
Overlapping,
Disjoint,
SizeMismatch,
}
#[derive(Debug, Clone)]
pub struct StoreLoadForwardRule {
pub uarch: X86MicroArch,
pub class: StoreLoadForwardClass,
pub penalty_cycles: u32,
pub store_buffer_only: bool,
pub requires_store_completion: bool,
pub blocked_by_unaligned: bool,
pub blocked_by_line_cross: bool,
pub blocked_by_page_cross: bool,
pub description: &'static str,
}
#[derive(Debug, Clone)]
pub struct PipelineStageTiming {
pub stage: PipelineStage,
pub cycles: u32,
pub overlappable: bool,
pub bottleneck: Option<&'static str>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PipelineStage {
Fetch1,
Fetch2,
Decode,
PreDecode,
Rename,
Allocate,
Schedule,
Dispatch,
Execute,
Writeback,
Retire,
}
impl PipelineStage {
pub fn name(&self) -> &'static str {
match self {
PipelineStage::Fetch1 => "FETCH1",
PipelineStage::Fetch2 => "FETCH2",
PipelineStage::Decode => "DECODE",
PipelineStage::PreDecode => "PRE-DECODE",
PipelineStage::Rename => "RENAME",
PipelineStage::Allocate => "ALLOCATE",
PipelineStage::Schedule => "SCHEDULE",
PipelineStage::Dispatch => "DISPATCH",
PipelineStage::Execute => "EXECUTE",
PipelineStage::Writeback => "WRITEBACK",
PipelineStage::Retire => "RETIRE",
}
}
}
#[derive(Debug, Clone)]
pub struct UarchLatencyTable {
pub uarch: X86MicroArch,
pub description: &'static str,
pub records: HashMap<(String, String, bool), InstrLatencyRecord>,
pub records_by_mnemonic: HashMap<String, Vec<usize>>,
pub record_vec: Vec<InstrLatencyRecord>,
pub store_load_rules: Vec<StoreLoadForwardRule>,
pub pipeline_stages: HashMap<String, Vec<PipelineStageTiming>>,
pub bypass_matrix: HashMap<(BypassProducer, BypassProducer), u32>,
}
impl UarchLatencyTable {
pub fn new(uarch: X86MicroArch, description: &'static str) -> Self {
UarchLatencyTable {
uarch,
description,
records: HashMap::new(),
records_by_mnemonic: HashMap::new(),
record_vec: Vec::new(),
store_load_rules: Vec::new(),
pipeline_stages: HashMap::new(),
bypass_matrix: HashMap::new(),
}
}
pub fn add_record(&mut self, record: InstrLatencyRecord) -> usize {
let idx = self.record_vec.len();
let key = (
record.mnemonic.to_string(),
record.size.to_string(),
record.reg_reg,
);
self.records.insert(key.clone(), record.clone());
self.records_by_mnemonic
.entry(record.mnemonic.to_string())
.or_default()
.push(idx);
self.record_vec.push(record);
idx
}
pub fn lookup(&self, mnemonic: &str, size: &str, reg_reg: bool) -> Option<&InstrLatencyRecord> {
let key = (mnemonic.to_string(), size.to_string(), reg_reg);
self.records.get(&key)
}
pub fn lookup_by_mnemonic(&self, mnemonic: &str) -> Vec<&InstrLatencyRecord> {
self.records_by_mnemonic
.get(mnemonic)
.map(|indices| {
indices
.iter()
.filter_map(|&i| self.record_vec.get(i))
.collect()
})
.unwrap_or_default()
}
pub fn get_latency(&self, mnemonic: &str, size: &str, reg_reg: bool) -> Option<u32> {
self.lookup(mnemonic, size, reg_reg).map(|r| r.latency)
}
pub fn get_throughput(&self, mnemonic: &str, size: &str, reg_reg: bool) -> Option<f64> {
self.lookup(mnemonic, size, reg_reg)
.map(|r| r.reciprocal_throughput)
}
pub fn get_uops(&self, mnemonic: &str, size: &str, reg_reg: bool) -> Option<u32> {
self.lookup(mnemonic, size, reg_reg).map(|r| r.uops)
}
pub fn get_ports(&self, mnemonic: &str, size: &str, reg_reg: bool) -> Option<PortMask> {
self.lookup(mnemonic, size, reg_reg).map(|r| r.ports)
}
pub fn is_zero_latency(&self, mnemonic: &str, size: &str, reg_reg: bool) -> bool {
self.lookup(mnemonic, size, reg_reg)
.map(|r| r.zero_latency)
.unwrap_or(false)
}
pub fn add_store_load_rule(&mut self, rule: StoreLoadForwardRule) {
self.store_load_rules.push(rule);
}
pub fn get_forward_penalty(&self, class: StoreLoadForwardClass) -> Option<u32> {
self.store_load_rules
.iter()
.find(|r| r.class == class)
.map(|r| r.penalty_cycles)
}
pub fn add_pipeline_stages(&mut self, class: &str, stages: Vec<PipelineStageTiming>) {
self.pipeline_stages.insert(class.to_string(), stages);
}
pub fn set_bypass(&mut self, producer: BypassProducer, consumer: BypassProducer, cycles: u32) {
self.bypass_matrix.insert((producer, consumer), cycles);
}
pub fn get_bypass(&self, producer: BypassProducer, consumer: BypassProducer) -> Option<u32> {
self.bypass_matrix.get(&(producer, consumer)).copied()
}
pub fn len(&self) -> usize {
self.record_vec.len()
}
pub fn is_empty(&self) -> bool {
self.record_vec.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct X86LatencyTables {
pub tables: HashMap<X86MicroArch, UarchLatencyTable>,
pub architectures: Vec<X86MicroArch>,
}
impl X86LatencyTables {
pub fn new() -> Self {
let mut db = X86LatencyTables {
tables: HashMap::new(),
architectures: Vec::new(),
};
db.populate();
db
}
pub fn get(&self, uarch: X86MicroArch) -> Option<&UarchLatencyTable> {
self.tables.get(&uarch)
}
pub fn get_latency(
&self,
uarch: X86MicroArch,
mnemonic: &str,
size: &str,
reg_reg: bool,
) -> Option<u32> {
self.get(uarch)
.and_then(|t| t.get_latency(mnemonic, size, reg_reg))
}
pub fn get_throughput(
&self,
uarch: X86MicroArch,
mnemonic: &str,
size: &str,
reg_reg: bool,
) -> Option<f64> {
self.get(uarch)
.and_then(|t| t.get_throughput(mnemonic, size, reg_reg))
}
pub fn get_uops(
&self,
uarch: X86MicroArch,
mnemonic: &str,
size: &str,
reg_reg: bool,
) -> Option<u32> {
self.get(uarch)
.and_then(|t| t.get_uops(mnemonic, size, reg_reg))
}
pub fn get_best_latency(&self, mnemonic: &str, size: &str, reg_reg: bool) -> Option<u32> {
self.tables
.values()
.filter_map(|t| t.get_latency(mnemonic, size, reg_reg))
.min()
}
pub fn get_worst_latency(&self, mnemonic: &str, size: &str, reg_reg: bool) -> Option<u32> {
self.tables
.values()
.filter_map(|t| t.get_latency(mnemonic, size, reg_reg))
.max()
}
pub fn get_avg_latency(&self, mnemonic: &str, size: &str, reg_reg: bool) -> Option<f64> {
let vals: Vec<u32> = self
.tables
.values()
.filter_map(|t| t.get_latency(mnemonic, size, reg_reg))
.collect();
if vals.is_empty() {
return None;
}
Some(vals.iter().sum::<u32>() as f64 / vals.len() as f64)
}
pub fn can_be_zero_latency(&self, mnemonic: &str, size: &str, reg_reg: bool) -> bool {
self.tables
.values()
.any(|t| t.is_zero_latency(mnemonic, size, reg_reg))
}
pub fn total_records(&self) -> usize {
self.tables.values().map(|t| t.len()).sum()
}
pub fn all_architectures(&self) -> &[X86MicroArch] {
&self.architectures
}
fn populate(&mut self) {
self.architectures = vec![
X86MicroArch::Skylake,
X86MicroArch::IceLake,
X86MicroArch::AlderLakePcore,
X86MicroArch::AlderLakeEcore,
X86MicroArch::SapphireRapids,
X86MicroArch::GraniteRapids,
X86MicroArch::Zen3,
X86MicroArch::Zen4,
X86MicroArch::Zen5,
];
self.build_skylake();
self.build_ice_lake();
self.build_alderlake_pcore();
self.build_alderlake_ecore();
self.build_sapphire_rapids();
self.build_granite_rapids();
self.build_zen3();
self.build_zen4();
self.build_zen5();
}
fn add_inst(
table: &mut UarchLatencyTable,
mnemonic: &'static str,
size: &'static str,
latency: u32,
throughput: f64,
uops: u32,
ports: PortMask,
reg_reg: bool,
reg_mem: bool,
) {
let record = InstrLatencyRecord {
mnemonic,
size,
reg_reg,
reg_mem,
latency,
reciprocal_throughput: throughput,
uops,
uops_unfused: uops,
ports,
zero_latency: false,
variable_latency: false,
latency_min: None,
latency_max: None,
uses_divider: false,
can_macro_fuse: false,
can_micro_fuse: reg_mem,
has_bypass: false,
bypass_from: None,
};
table.add_record(record);
}
fn add_div_inst(
table: &mut UarchLatencyTable,
mnemonic: &'static str,
size: &'static str,
latency_min: u32,
latency_max: u32,
throughput: f64,
uops: u32,
ports: PortMask,
) {
let record = InstrLatencyRecord {
mnemonic,
size,
reg_reg: true,
reg_mem: false,
latency: latency_max,
reciprocal_throughput: throughput,
uops,
uops_unfused: uops,
ports,
zero_latency: false,
variable_latency: true,
latency_min: Some(latency_min),
latency_max: Some(latency_max),
uses_divider: true,
can_macro_fuse: false,
can_micro_fuse: false,
has_bypass: false,
bypass_from: None,
};
table.add_record(record);
}
fn add_zero_latency(table: &mut UarchLatencyTable, mnemonic: &'static str, size: &'static str) {
let record = InstrLatencyRecord {
mnemonic,
size,
reg_reg: true,
reg_mem: false,
latency: 0,
reciprocal_throughput: 0.25,
uops: 0,
uops_unfused: 0,
ports: PortMask::NONE,
zero_latency: true,
variable_latency: false,
latency_min: None,
latency_max: None,
uses_divider: false,
can_macro_fuse: true,
can_micro_fuse: false,
has_bypass: false,
bypass_from: None,
};
table.add_record(record);
}
fn build_skylake(&mut self) {
let mut t = UarchLatencyTable::new(X86MicroArch::Skylake, "Skylake Client (14nm, 2015)");
Self::add_inst(
&mut t,
"ADD",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"ADD",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"SUB",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"SUB",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"AND",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"AND",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(&mut t, "OR", "32", 1, 0.25, 1, PortMask::P0156, true, false);
Self::add_inst(&mut t, "OR", "64", 1, 0.25, 1, PortMask::P0156, true, false);
Self::add_inst(
&mut t,
"NOT",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"NOT",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"NEG",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"NEG",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"INC",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"INC",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"DEC",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"DEC",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_zero_latency(&mut t, "XOR", "32");
Self::add_zero_latency(&mut t, "XOR", "64");
Self::add_zero_latency(&mut t, "SUB", "32_zero");
Self::add_zero_latency(&mut t, "SUB", "64_zero");
Self::add_zero_latency(&mut t, "PXOR", "128");
Self::add_zero_latency(&mut t, "XORPS", "128");
Self::add_zero_latency(&mut t, "XORPD", "128");
Self::add_inst(
&mut t,
"XOR",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"XOR",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(&mut t, "SHL", "32", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SHL", "64", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SHR", "32", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SHR", "64", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SAR", "32", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SAR", "64", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "ROL", "32", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "ROR", "32", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(
&mut t,
"SHL",
"32_cl",
1,
1.0,
2,
PortMask::P06,
true,
false,
);
Self::add_inst(
&mut t,
"SHL",
"64_cl",
1,
1.0,
2,
PortMask::P06,
true,
false,
);
Self::add_inst(
&mut t,
"SHR",
"32_cl",
1,
1.0,
2,
PortMask::P06,
true,
false,
);
Self::add_inst(
&mut t,
"SHR",
"64_cl",
1,
1.0,
2,
PortMask::P06,
true,
false,
);
Self::add_inst(
&mut t,
"SAR",
"32_cl",
1,
1.0,
2,
PortMask::P06,
true,
false,
);
Self::add_inst(
&mut t,
"SAR",
"64_cl",
1,
1.0,
2,
PortMask::P06,
true,
false,
);
Self::add_inst(&mut t, "IMUL", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "IMUL", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(
&mut t,
"IMUL",
"32x32_64",
4,
1.0,
1,
PortMask::P1,
true,
false,
);
Self::add_inst(
&mut t,
"IMUL",
"64x64_128",
4,
1.0,
1,
PortMask::P1,
true,
false,
);
Self::add_inst(&mut t, "MUL", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "MUL", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_div_inst(&mut t, "DIV", "32", 22, 29, 6.0, 9, PortMask::P06);
Self::add_div_inst(&mut t, "DIV", "64", 39, 95, 21.0, 36, PortMask::P06);
Self::add_div_inst(&mut t, "IDIV", "32", 22, 29, 6.0, 9, PortMask::P06);
Self::add_div_inst(&mut t, "IDIV", "64", 39, 95, 21.0, 36, PortMask::P06);
Self::add_inst(
&mut t,
"LEA",
"32_simple",
1,
0.25,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"64_simple",
1,
0.25,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"32_complex",
3,
1.0,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"64_complex",
3,
1.0,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"MOV",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"MOV",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"MOVSX",
"8_32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"MOVSX",
"8_64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"MOVSX",
"16_32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"MOVSX",
"16_64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"MOVSX",
"32_64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"MOVZX",
"8_32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"MOVZX",
"8_64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"MOVZX",
"16_32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"MOVZX",
"16_64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"CMOVcc",
"32",
2,
0.5,
2,
PortMask::P06,
true,
false,
);
Self::add_inst(
&mut t,
"CMOVcc",
"64",
2,
0.5,
2,
PortMask::P06,
true,
false,
);
Self::add_inst(&mut t, "BSWAP", "32", 1, 0.5, 1, PortMask::P15, true, false);
Self::add_inst(&mut t, "BSWAP", "64", 1, 0.5, 1, PortMask::P15, true, false);
Self::add_inst(
&mut t,
"MOV",
"32_ld",
4,
0.5,
1,
PortMask::P23,
false,
true,
);
Self::add_inst(
&mut t,
"MOV",
"64_ld",
4,
0.5,
1,
PortMask::P23,
false,
true,
);
Self::add_inst(
&mut t,
"ADD",
"32_mem",
5,
0.5,
1,
PortMask::P237_P0156,
false,
true,
);
Self::add_inst(
&mut t,
"ADD",
"64_mem",
5,
0.5,
1,
PortMask::P237_P0156,
false,
true,
);
Self::add_inst(
&mut t,
"SUB",
"32_mem",
5,
0.5,
1,
PortMask::P237_P0156,
false,
true,
);
Self::add_inst(
&mut t,
"AND",
"32_mem",
5,
0.5,
1,
PortMask::P237_P0156,
false,
true,
);
Self::add_inst(
&mut t,
"OR",
"32_mem",
5,
0.5,
1,
PortMask::P237_P0156,
false,
true,
);
Self::add_inst(
&mut t,
"XOR",
"32_mem",
5,
0.5,
1,
PortMask::P237_P0156,
false,
true,
);
Self::add_inst(
&mut t,
"MOV",
"32_st",
1,
1.0,
1,
PortMask::P237_P4,
true,
true,
);
Self::add_inst(
&mut t,
"MOV",
"64_st",
1,
1.0,
1,
PortMask::P237_P4,
true,
true,
);
Self::add_inst(&mut t, "ANDN", "32", 1, 0.5, 1, PortMask::P15, true, false);
Self::add_inst(&mut t, "ANDN", "64", 1, 0.5, 1, PortMask::P15, true, false);
Self::add_inst(&mut t, "BEXTR", "32", 2, 0.5, 2, PortMask::P1, true, false);
Self::add_inst(&mut t, "BEXTR", "64", 2, 0.5, 2, PortMask::P1, true, false);
Self::add_inst(&mut t, "BLSI", "32", 1, 0.5, 1, PortMask::P15, true, false);
Self::add_inst(
&mut t,
"BLSMSK",
"32",
1,
0.5,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(&mut t, "BLSR", "32", 1, 0.5, 1, PortMask::P15, true, false);
Self::add_inst(&mut t, "BZHI", "32", 1, 0.5, 1, PortMask::P15, true, false);
Self::add_inst(&mut t, "BZHI", "64", 1, 0.5, 1, PortMask::P15, true, false);
Self::add_inst(&mut t, "MULX", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "MULX", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PDEP", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PDEP", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PEXT", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PEXT", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "RORX", "32", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "RORX", "64", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SARX", "32", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SARX", "64", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SHLX", "32", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SHLX", "64", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SHRX", "32", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SHRX", "64", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "POPCNT", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "POPCNT", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "LZCNT", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "LZCNT", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "TZCNT", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "TZCNT", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "ADDSS", "32", 4, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "ADDSD", "64", 4, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "SUBSS", "32", 4, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "SUBSD", "64", 4, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULSS", "32", 4, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULSD", "64", 4, 0.5, 1, PortMask::P01, true, false);
Self::add_div_inst(&mut t, "DIVSS", "32", 11, 11, 3.0, 3, PortMask::P0);
Self::add_div_inst(&mut t, "DIVSD", "64", 14, 14, 4.0, 3, PortMask::P0);
Self::add_div_inst(&mut t, "SQRTSS", "32", 12, 12, 6.0, 3, PortMask::P0);
Self::add_div_inst(&mut t, "SQRTSD", "64", 18, 18, 6.0, 3, PortMask::P0);
Self::add_inst(
&mut t,
"CVTSI2SS",
"32_to_f32",
6,
1.0,
2,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"CVTSI2SD",
"32_to_f64",
6,
1.0,
2,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"CVTSI2SS",
"64_to_f32",
6,
1.0,
2,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"CVTSI2SD",
"64_to_f64",
6,
1.0,
2,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"CVTTSS2SI",
"f32_to_32",
6,
1.0,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"CVTTSD2SI",
"f64_to_32",
6,
1.0,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VFMADD132SS",
"32",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VFMADD132SD",
"64",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VFMSUB132SS",
"32",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VFMSUB132SD",
"64",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VFNMADD132SS",
"32",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VFNMADD132SD",
"64",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"ADDPS",
"128",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"ADDPD",
"128",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"SUBPS",
"128",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"SUBPD",
"128",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"MULPS",
"128",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"MULPD",
"128",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"DIVPS",
"128",
11,
3.0,
3,
PortMask::P0,
true,
false,
);
Self::add_inst(
&mut t,
"DIVPD",
"128",
14,
4.0,
3,
PortMask::P0,
true,
false,
);
Self::add_inst(
&mut t,
"SQRTPS",
"128",
12,
6.0,
3,
PortMask::P0,
true,
false,
);
Self::add_inst(
&mut t,
"SQRTPD",
"128",
18,
6.0,
3,
PortMask::P0,
true,
false,
);
Self::add_inst(
&mut t,
"ADDPS",
"128_mem",
8,
0.5,
1,
PortMask::P23_P01,
false,
true,
);
Self::add_inst(
&mut t,
"MULPS",
"128_mem",
8,
0.5,
1,
PortMask::P23_P01,
false,
true,
);
Self::add_inst(
&mut t,
"PBLENDVB",
"128",
2,
1.0,
2,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"BLENDVPS",
"128",
2,
1.0,
2,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"BLENDVPD",
"128",
2,
1.0,
2,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"PSHUFB",
"128",
1,
0.5,
1,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"PSHUFD",
"128",
1,
0.5,
1,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"SHUFPS",
"128",
1,
0.5,
1,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"SHUFPD",
"128",
1,
0.5,
1,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"PUNPCKLDQ",
"128",
1,
0.5,
1,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"PUNPCKHDQ",
"128",
1,
0.5,
1,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"PACKSSDW",
"128",
1,
0.5,
1,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"PACKUSWB",
"128",
1,
0.5,
1,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"INSERTPS",
"128",
1,
0.5,
1,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"EXTRACTPS",
"128",
3,
1.0,
2,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"PADDB",
"128",
1,
0.5,
1,
PortMask::P015,
true,
false,
);
Self::add_inst(
&mut t,
"PADDW",
"128",
1,
0.5,
1,
PortMask::P015,
true,
false,
);
Self::add_inst(
&mut t,
"PADDD",
"128",
1,
0.5,
1,
PortMask::P015,
true,
false,
);
Self::add_inst(
&mut t,
"PADDQ",
"128",
1,
0.5,
1,
PortMask::P015,
true,
false,
);
Self::add_inst(
&mut t,
"PSUBB",
"128",
1,
0.5,
1,
PortMask::P015,
true,
false,
);
Self::add_inst(
&mut t,
"PSUBD",
"128",
1,
0.5,
1,
PortMask::P015,
true,
false,
);
Self::add_inst(
&mut t,
"PMULLD",
"128",
10,
2.0,
2,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"PMULLW",
"128",
5,
1.0,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"PMULHW",
"128",
5,
1.0,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"PMULHUW",
"128",
5,
1.0,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"PMULDQ",
"128",
5,
1.0,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"PMULUDQ",
"128",
5,
1.0,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPS",
"128",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPD",
"128",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"128",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPD",
"128",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPS",
"256",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPD",
"256",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VSUBPS",
"256",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VSUBPD",
"256",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"256",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPD",
"256",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VDIVPS",
"256",
11,
5.0,
3,
PortMask::P0,
true,
false,
);
Self::add_inst(
&mut t,
"VDIVPD",
"256",
14,
8.0,
3,
PortMask::P0,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPS",
"256_mem",
8,
0.5,
1,
PortMask::P23_P01,
false,
true,
);
Self::add_inst(
&mut t,
"VADDPS",
"512",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPD",
"512",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"512",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPD",
"512",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VDIVPS",
"512",
11,
10.0,
3,
PortMask::P0,
true,
false,
);
Self::add_inst(
&mut t,
"VDIVPD",
"512",
14,
16.0,
3,
PortMask::P0,
true,
false,
);
Self::add_inst(&mut t, "JMP", "rel8", 1, 0.5, 1, PortMask::P6, true, false);
Self::add_inst(&mut t, "Jcc", "taken", 1, 0.5, 1, PortMask::P6, true, false);
Self::add_inst(
&mut t,
"Jcc",
"not_taken",
1,
0.5,
1,
PortMask::P6,
true,
false,
);
Self::add_inst(&mut t, "CALL", "near", 1, 1.0, 1, PortMask::P6, true, false);
Self::add_inst(&mut t, "RET", "near", 1, 1.0, 1, PortMask::P6, true, false);
Self::add_inst(
&mut t,
"CMP",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"CMP",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"TEST",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"TEST",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(&mut t, "NOP", "1", 1, 0.25, 1, PortMask::P0156, true, false);
Self::add_inst(&mut t, "NOP", "2", 1, 0.25, 1, PortMask::P0156, true, false);
Self::add_inst(&mut t, "NOP", "3", 1, 0.25, 1, PortMask::P0156, true, false);
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::Skylake,
class: StoreLoadForwardClass::ExactMatch,
penalty_cycles: 0,
store_buffer_only: true,
requires_store_completion: false,
blocked_by_unaligned: false,
blocked_by_line_cross: false,
blocked_by_page_cross: false,
description: "Exact-address match: fast forward in 0 extra cycles",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::Skylake,
class: StoreLoadForwardClass::LoadContainsStore,
penalty_cycles: 0,
store_buffer_only: true,
requires_store_completion: false,
blocked_by_unaligned: false,
blocked_by_line_cross: false,
blocked_by_page_cross: false,
description: "Load fully contains store: fast forward in 0 extra cycles",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::Skylake,
class: StoreLoadForwardClass::StoreContainsLoad,
penalty_cycles: 7,
store_buffer_only: false,
requires_store_completion: true,
blocked_by_unaligned: true,
blocked_by_line_cross: true,
blocked_by_page_cross: true,
description: "Store contains load: no forward, ~7 cycles penalty",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::Skylake,
class: StoreLoadForwardClass::Overlapping,
penalty_cycles: 7,
store_buffer_only: false,
requires_store_completion: true,
blocked_by_unaligned: true,
blocked_by_line_cross: true,
blocked_by_page_cross: true,
description: "Overlapping but not containing: no forward, ~7 cycles",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::Skylake,
class: StoreLoadForwardClass::Disjoint,
penalty_cycles: 0,
store_buffer_only: false,
requires_store_completion: false,
blocked_by_unaligned: false,
blocked_by_line_cross: false,
blocked_by_page_cross: false,
description: "No overlap: no forwarding needed, normal load",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::Skylake,
class: StoreLoadForwardClass::SizeMismatch,
penalty_cycles: 7,
store_buffer_only: false,
requires_store_completion: true,
blocked_by_unaligned: true,
blocked_by_line_cross: true,
blocked_by_page_cross: true,
description: "Address match but size mismatch (e.g., store byte, load word)",
});
t.add_pipeline_stages(
"INT_ALU",
vec![
PipelineStageTiming {
stage: PipelineStage::Fetch1,
cycles: 1,
overlappable: true,
bottleneck: None,
},
PipelineStageTiming {
stage: PipelineStage::Decode,
cycles: 1,
overlappable: true,
bottleneck: Some("4-wide decode"),
},
PipelineStageTiming {
stage: PipelineStage::Rename,
cycles: 1,
overlappable: true,
bottleneck: None,
},
PipelineStageTiming {
stage: PipelineStage::Schedule,
cycles: 0,
overlappable: true,
bottleneck: None,
},
PipelineStageTiming {
stage: PipelineStage::Dispatch,
cycles: 1,
overlappable: true,
bottleneck: Some("4-wide dispatch"),
},
PipelineStageTiming {
stage: PipelineStage::Execute,
cycles: 1,
overlappable: false,
bottleneck: None,
},
PipelineStageTiming {
stage: PipelineStage::Retire,
cycles: 1,
overlappable: true,
bottleneck: Some("4-wide retire"),
},
],
);
t.set_bypass(BypassProducer::IntegerAlu, BypassProducer::IntegerAlu, 1);
t.set_bypass(BypassProducer::IntegerAlu, BypassProducer::IntegerMul, 0);
t.set_bypass(BypassProducer::IntegerMul, BypassProducer::IntegerAlu, 1);
t.set_bypass(BypassProducer::IntegerMul, BypassProducer::IntegerMul, 0);
t.set_bypass(BypassProducer::LoadL1, BypassProducer::IntegerAlu, 0);
t.set_bypass(BypassProducer::LoadL1, BypassProducer::FloatAlu, 0);
t.set_bypass(BypassProducer::FloatAlu, BypassProducer::FloatAlu, 1);
t.set_bypass(BypassProducer::FloatAlu, BypassProducer::SimdAlu, 1);
t.set_bypass(BypassProducer::SimdAlu, BypassProducer::SimdAlu, 1);
self.tables.insert(X86MicroArch::Skylake, t);
}
fn build_ice_lake(&mut self) {
let mut t = UarchLatencyTable::new(
X86MicroArch::IceLake,
"Ice Lake Client (10nm Sunny Cove, 2019)",
);
Self::add_inst(
&mut t,
"ADD",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"ADD",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"SUB",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"SUB",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"AND",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"AND",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(&mut t, "OR", "32", 1, 0.25, 1, PortMask::P0156, true, false);
Self::add_inst(&mut t, "OR", "64", 1, 0.25, 1, PortMask::P0156, true, false);
Self::add_inst(
&mut t,
"NOT",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"NOT",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"NEG",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"NEG",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_zero_latency(&mut t, "XOR", "32");
Self::add_zero_latency(&mut t, "XOR", "64");
Self::add_zero_latency(&mut t, "PXOR", "128");
Self::add_inst(
&mut t,
"XOR",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"XOR",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_div_inst(&mut t, "DIV", "32", 16, 20, 3.5, 9, PortMask::P06);
Self::add_div_inst(&mut t, "DIV", "64", 28, 60, 14.0, 36, PortMask::P06);
Self::add_div_inst(&mut t, "IDIV", "32", 16, 20, 3.5, 9, PortMask::P06);
Self::add_div_inst(&mut t, "IDIV", "64", 28, 60, 14.0, 36, PortMask::P06);
Self::add_inst(&mut t, "IMUL", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "IMUL", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "MULX", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "MULX", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(
&mut t,
"MOV",
"32_ld",
5,
0.5,
1,
PortMask::P23,
false,
true,
);
Self::add_inst(
&mut t,
"MOV",
"64_ld",
5,
0.5,
1,
PortMask::P23,
false,
true,
);
Self::add_inst(
&mut t,
"LEA",
"32_simple",
1,
0.25,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"64_simple",
1,
0.25,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"32_complex",
3,
1.0,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"64_complex",
3,
1.0,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(&mut t, "SHL", "32", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SHL", "64", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SHR", "32", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SHR", "64", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SAR", "32", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SAR", "64", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "ANDN", "32", 1, 0.5, 1, PortMask::P15, true, false);
Self::add_inst(&mut t, "ANDN", "64", 1, 0.5, 1, PortMask::P15, true, false);
Self::add_inst(&mut t, "PDEP", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PDEP", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PEXT", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PEXT", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "SHLX", "32", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SHLX", "64", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SHRX", "32", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SHRX", "64", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SARX", "32", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SARX", "64", 1, 0.5, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "ADDSS", "32", 4, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "ADDSD", "64", 4, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULSS", "32", 4, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULSD", "64", 4, 0.5, 1, PortMask::P01, true, false);
Self::add_div_inst(&mut t, "DIVSS", "32", 11, 11, 3.0, 3, PortMask::P0);
Self::add_div_inst(&mut t, "DIVSD", "64", 14, 14, 4.0, 3, PortMask::P0);
Self::add_div_inst(&mut t, "SQRTSS", "32", 12, 12, 6.0, 3, PortMask::P0);
Self::add_div_inst(&mut t, "SQRTSD", "64", 18, 18, 6.0, 3, PortMask::P0);
Self::add_inst(
&mut t,
"VADDPS",
"256",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPD",
"256",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"256",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPD",
"256",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPS",
"512",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPD",
"512",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"512",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPD",
"512",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VPSHUFB",
"128",
1,
0.5,
1,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"VPSHUFB",
"256",
1,
1.0,
2,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"VPSHUFD",
"128",
1,
0.5,
1,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"VSHUFPS",
"128",
1,
0.5,
1,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"VSHUFPD",
"128",
1,
0.5,
1,
PortMask::P5,
true,
false,
);
Self::add_inst(&mut t, "POPCNT", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "POPCNT", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "LZCNT", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "LZCNT", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "TZCNT", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "TZCNT", "64", 3, 1.0, 1, PortMask::P1, true, false);
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::IceLake,
class: StoreLoadForwardClass::ExactMatch,
penalty_cycles: 0,
store_buffer_only: true,
requires_store_completion: false,
blocked_by_unaligned: false,
blocked_by_line_cross: false,
blocked_by_page_cross: false,
description: "Exact-address match: fast forward 0 cycles",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::IceLake,
class: StoreLoadForwardClass::StoreContainsLoad,
penalty_cycles: 9, store_buffer_only: false,
requires_store_completion: true,
blocked_by_unaligned: true,
blocked_by_line_cross: true,
blocked_by_page_cross: true,
description: "Store contains load: no forward, ~9 cycles penalty",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::IceLake,
class: StoreLoadForwardClass::SizeMismatch,
penalty_cycles: 9,
store_buffer_only: false,
requires_store_completion: true,
blocked_by_unaligned: true,
blocked_by_line_cross: true,
blocked_by_page_cross: true,
description: "Size mismatch (e.g., store byte/word, load dword)",
});
t.set_bypass(BypassProducer::IntegerAlu, BypassProducer::IntegerAlu, 1);
t.set_bypass(BypassProducer::IntegerMul, BypassProducer::IntegerAlu, 1);
t.set_bypass(BypassProducer::LoadL1, BypassProducer::IntegerAlu, 0);
t.set_bypass(BypassProducer::FloatAlu, BypassProducer::FloatAlu, 1);
t.set_bypass(BypassProducer::SimdAlu, BypassProducer::SimdAlu, 1);
self.tables.insert(X86MicroArch::IceLake, t);
}
fn build_alderlake_pcore(&mut self) {
let mut t = UarchLatencyTable::new(
X86MicroArch::AlderLakePcore,
"Alder Lake P-core (Intel 7 Golden Cove, 2021)",
);
Self::add_inst(
&mut t,
"ADD",
"32",
1,
0.17,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"ADD",
"64",
1,
0.17,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"SUB",
"32",
1,
0.17,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"SUB",
"64",
1,
0.17,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"AND",
"32",
1,
0.17,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"AND",
"64",
1,
0.17,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(&mut t, "OR", "32", 1, 0.17, 1, PortMask::P0156, true, false);
Self::add_inst(&mut t, "OR", "64", 1, 0.17, 1, PortMask::P0156, true, false);
Self::add_zero_latency(&mut t, "XOR", "32");
Self::add_zero_latency(&mut t, "XOR", "64");
Self::add_div_inst(&mut t, "DIV", "32", 11, 15, 2.5, 9, PortMask::P06);
Self::add_div_inst(&mut t, "DIV", "64", 18, 45, 9.0, 36, PortMask::P06);
Self::add_div_inst(&mut t, "IDIV", "32", 11, 15, 2.5, 9, PortMask::P06);
Self::add_div_inst(&mut t, "IDIV", "64", 18, 45, 9.0, 36, PortMask::P06);
Self::add_inst(&mut t, "IMUL", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "IMUL", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "MULX", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "MULX", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PDEP", "32", 2, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PDEP", "64", 2, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PEXT", "32", 2, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PEXT", "64", 2, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(
&mut t,
"LEA",
"32_simple",
1,
0.17,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"64_simple",
1,
0.17,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"32_complex",
3,
1.0,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"64_complex",
3,
1.0,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"MOV",
"32_ld",
5,
0.33,
1,
PortMask::P23,
false,
true,
);
Self::add_inst(
&mut t,
"MOV",
"64_ld",
5,
0.33,
1,
PortMask::P23,
false,
true,
);
Self::add_inst(&mut t, "SHL", "32", 1, 0.33, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SHL", "64", 1, 0.33, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SHLX", "32", 1, 0.33, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SHLX", "64", 1, 0.33, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SHRX", "32", 1, 0.33, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SHRX", "64", 1, 0.33, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SARX", "32", 1, 0.33, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "SARX", "64", 1, 0.33, 1, PortMask::P06, true, false);
Self::add_inst(&mut t, "ADDSS", "32", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "ADDSD", "64", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULSS", "32", 4, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULSD", "64", 4, 0.5, 1, PortMask::P01, true, false);
Self::add_div_inst(&mut t, "DIVSS", "32", 11, 11, 3.0, 3, PortMask::P0);
Self::add_div_inst(&mut t, "DIVSD", "64", 13, 13, 4.0, 3, PortMask::P0);
Self::add_inst(
&mut t,
"VADDPS",
"256",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"256",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPS",
"512",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"512",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::AlderLakePcore,
class: StoreLoadForwardClass::ExactMatch,
penalty_cycles: 0,
store_buffer_only: true,
requires_store_completion: false,
blocked_by_unaligned: false,
blocked_by_line_cross: false,
blocked_by_page_cross: false,
description: "Exact-address match: fast forward 0 cycles",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::AlderLakePcore,
class: StoreLoadForwardClass::StoreContainsLoad,
penalty_cycles: 5, store_buffer_only: false,
requires_store_completion: true,
blocked_by_unaligned: true,
blocked_by_line_cross: true,
blocked_by_page_cross: true,
description: "Store contains load: improved forwarding, ~5 cycles penalty",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::AlderLakePcore,
class: StoreLoadForwardClass::SizeMismatch,
penalty_cycles: 5,
store_buffer_only: false,
requires_store_completion: true,
blocked_by_unaligned: true,
blocked_by_line_cross: true,
blocked_by_page_cross: true,
description: "Size mismatch: improved penalty ~5 cycles",
});
t.set_bypass(BypassProducer::IntegerAlu, BypassProducer::IntegerAlu, 1);
t.set_bypass(BypassProducer::IntegerMul, BypassProducer::IntegerAlu, 1);
t.set_bypass(BypassProducer::LoadL1, BypassProducer::IntegerAlu, 0);
t.set_bypass(BypassProducer::FloatAlu, BypassProducer::FloatAlu, 1);
t.set_bypass(BypassProducer::SimdAlu, BypassProducer::SimdAlu, 1);
self.tables.insert(X86MicroArch::AlderLakePcore, t);
}
fn build_alderlake_ecore(&mut self) {
let mut t = UarchLatencyTable::new(
X86MicroArch::AlderLakeEcore,
"Alder Lake E-core (Intel 7 Gracemont, 2021)",
);
Self::add_inst(&mut t, "ADD", "32", 1, 0.33, 1, PortMask::P015, true, false);
Self::add_inst(&mut t, "ADD", "64", 1, 0.33, 1, PortMask::P015, true, false);
Self::add_inst(&mut t, "SUB", "32", 1, 0.33, 1, PortMask::P015, true, false);
Self::add_inst(&mut t, "SUB", "64", 1, 0.33, 1, PortMask::P015, true, false);
Self::add_inst(&mut t, "AND", "32", 1, 0.33, 1, PortMask::P015, true, false);
Self::add_inst(&mut t, "AND", "64", 1, 0.33, 1, PortMask::P015, true, false);
Self::add_inst(&mut t, "OR", "32", 1, 0.33, 1, PortMask::P015, true, false);
Self::add_inst(&mut t, "OR", "64", 1, 0.33, 1, PortMask::P015, true, false);
Self::add_zero_latency(&mut t, "XOR", "32");
Self::add_zero_latency(&mut t, "XOR", "64");
Self::add_div_inst(&mut t, "DIV", "32", 17, 25, 5.0, 12, PortMask::P06);
Self::add_div_inst(&mut t, "DIV", "64", 32, 78, 17.0, 42, PortMask::P06);
Self::add_div_inst(&mut t, "IDIV", "32", 17, 25, 5.0, 12, PortMask::P06);
Self::add_div_inst(&mut t, "IDIV", "64", 32, 78, 17.0, 42, PortMask::P06);
Self::add_inst(&mut t, "IMUL", "32", 4, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "IMUL", "64", 4, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "MULX", "32", 4, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "MULX", "64", 4, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PDEP", "32", 4, 2.0, 2, PortMask::P1, true, false);
Self::add_inst(&mut t, "PDEP", "64", 4, 2.0, 2, PortMask::P1, true, false);
Self::add_inst(&mut t, "PEXT", "32", 4, 2.0, 2, PortMask::P1, true, false);
Self::add_inst(&mut t, "PEXT", "64", 4, 2.0, 2, PortMask::P1, true, false);
Self::add_inst(
&mut t,
"LEA",
"32_simple",
1,
0.33,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"64_simple",
1,
0.33,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"32_complex",
3,
1.0,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"64_complex",
3,
1.0,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"MOV",
"32_ld",
5,
0.5,
1,
PortMask::P23,
false,
true,
);
Self::add_inst(
&mut t,
"MOV",
"64_ld",
5,
0.5,
1,
PortMask::P23,
false,
true,
);
Self::add_inst(&mut t, "ADDSS", "32", 4, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "ADDSD", "64", 4, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULSS", "32", 4, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULSD", "64", 4, 0.5, 1, PortMask::P01, true, false);
Self::add_div_inst(&mut t, "DIVSS", "32", 14, 14, 5.0, 3, PortMask::P0);
Self::add_div_inst(&mut t, "DIVSD", "64", 18, 18, 7.0, 3, PortMask::P0);
Self::add_inst(
&mut t,
"VADDPS",
"128",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPS",
"256",
4,
1.0,
2,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"128",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"256",
4,
1.0,
2,
PortMask::P01,
true,
false,
);
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::AlderLakeEcore,
class: StoreLoadForwardClass::ExactMatch,
penalty_cycles: 0,
store_buffer_only: true,
requires_store_completion: false,
blocked_by_unaligned: false,
blocked_by_line_cross: false,
blocked_by_page_cross: false,
description: "Exact-address match: fast forward 0 cycles",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::AlderLakeEcore,
class: StoreLoadForwardClass::StoreContainsLoad,
penalty_cycles: 7,
store_buffer_only: false,
requires_store_completion: true,
blocked_by_unaligned: true,
blocked_by_line_cross: true,
blocked_by_page_cross: true,
description: "Store contains load: ~7 cycles penalty on Gracemont",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::AlderLakeEcore,
class: StoreLoadForwardClass::SizeMismatch,
penalty_cycles: 7,
store_buffer_only: false,
requires_store_completion: true,
blocked_by_unaligned: true,
blocked_by_line_cross: true,
blocked_by_page_cross: true,
description: "Size mismatch: ~7 cycles penalty",
});
t.set_bypass(BypassProducer::IntegerAlu, BypassProducer::IntegerAlu, 1);
t.set_bypass(BypassProducer::IntegerMul, BypassProducer::IntegerAlu, 1);
t.set_bypass(BypassProducer::LoadL1, BypassProducer::IntegerAlu, 0);
self.tables.insert(X86MicroArch::AlderLakeEcore, t);
}
fn build_sapphire_rapids(&mut self) {
let mut t = UarchLatencyTable::new(
X86MicroArch::SapphireRapids,
"Sapphire Rapids (Intel 7 Golden Cove server, 2023)",
);
Self::add_inst(
&mut t,
"ADD",
"32",
1,
0.17,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"ADD",
"64",
1,
0.17,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"SUB",
"32",
1,
0.17,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"SUB",
"64",
1,
0.17,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"AND",
"32",
1,
0.17,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"AND",
"64",
1,
0.17,
1,
PortMask::P0156,
true,
false,
);
Self::add_zero_latency(&mut t, "XOR", "32");
Self::add_zero_latency(&mut t, "XOR", "64");
Self::add_div_inst(&mut t, "DIV", "32", 11, 15, 2.5, 9, PortMask::P06);
Self::add_div_inst(&mut t, "DIV", "64", 18, 45, 9.0, 36, PortMask::P06);
Self::add_div_inst(&mut t, "IDIV", "32", 11, 15, 2.5, 9, PortMask::P06);
Self::add_div_inst(&mut t, "IDIV", "64", 18, 45, 9.0, 36, PortMask::P06);
Self::add_inst(&mut t, "IMUL", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "IMUL", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "MULX", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "MULX", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PDEP", "32", 2, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PDEP", "64", 2, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PEXT", "32", 2, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PEXT", "64", 2, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(
&mut t,
"LEA",
"32_simple",
1,
0.17,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"64_simple",
1,
0.17,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"32_complex",
3,
1.0,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"64_complex",
3,
1.0,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"MOV",
"32_ld",
5,
0.33,
1,
PortMask::P23,
false,
true,
);
Self::add_inst(
&mut t,
"MOV",
"64_ld",
5,
0.33,
1,
PortMask::P23,
false,
true,
);
Self::add_inst(&mut t, "ADDSS", "32", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "ADDSD", "64", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULSS", "32", 4, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULSD", "64", 4, 0.5, 1, PortMask::P01, true, false);
Self::add_div_inst(&mut t, "DIVSS", "32", 11, 11, 3.0, 3, PortMask::P0);
Self::add_div_inst(&mut t, "DIVSD", "64", 13, 13, 4.0, 3, PortMask::P0);
Self::add_inst(
&mut t,
"VADDPS",
"512",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPD",
"512",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"512",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPD",
"512",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPS",
"256",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"256",
4,
0.5,
1,
PortMask::P01,
true,
false,
);
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::SapphireRapids,
class: StoreLoadForwardClass::ExactMatch,
penalty_cycles: 0,
store_buffer_only: true,
requires_store_completion: false,
blocked_by_unaligned: false,
blocked_by_line_cross: false,
blocked_by_page_cross: false,
description: "Exact-address match: fast forward 0 cycles",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::SapphireRapids,
class: StoreLoadForwardClass::StoreContainsLoad,
penalty_cycles: 5,
store_buffer_only: false,
requires_store_completion: true,
blocked_by_unaligned: true,
blocked_by_line_cross: true,
blocked_by_page_cross: true,
description: "Store contains load: ~5 cycles penalty",
});
t.set_bypass(BypassProducer::IntegerAlu, BypassProducer::IntegerAlu, 1);
t.set_bypass(BypassProducer::IntegerMul, BypassProducer::IntegerAlu, 1);
t.set_bypass(BypassProducer::LoadL1, BypassProducer::IntegerAlu, 0);
t.set_bypass(BypassProducer::FloatAlu, BypassProducer::FloatAlu, 1);
t.set_bypass(BypassProducer::SimdAlu, BypassProducer::SimdAlu, 1);
self.tables.insert(X86MicroArch::SapphireRapids, t);
}
fn build_granite_rapids(&mut self) {
let mut t = UarchLatencyTable::new(
X86MicroArch::GraniteRapids,
"Granite Rapids (Intel 3 Redwood Cove, 2024)",
);
Self::add_inst(
&mut t,
"ADD",
"32",
1,
0.13,
1,
PortMask::P015689,
true,
false,
);
Self::add_inst(
&mut t,
"ADD",
"64",
1,
0.13,
1,
PortMask::P015689,
true,
false,
);
Self::add_inst(
&mut t,
"SUB",
"32",
1,
0.13,
1,
PortMask::P015689,
true,
false,
);
Self::add_inst(
&mut t,
"SUB",
"64",
1,
0.13,
1,
PortMask::P015689,
true,
false,
);
Self::add_inst(
&mut t,
"AND",
"32",
1,
0.13,
1,
PortMask::P015689,
true,
false,
);
Self::add_inst(
&mut t,
"AND",
"64",
1,
0.13,
1,
PortMask::P015689,
true,
false,
);
Self::add_inst(
&mut t,
"OR",
"32",
1,
0.13,
1,
PortMask::P015689,
true,
false,
);
Self::add_inst(
&mut t,
"OR",
"64",
1,
0.13,
1,
PortMask::P015689,
true,
false,
);
Self::add_zero_latency(&mut t, "XOR", "32");
Self::add_zero_latency(&mut t, "XOR", "64");
Self::add_div_inst(&mut t, "DIV", "32", 9, 12, 2.0, 9, PortMask::P06);
Self::add_div_inst(&mut t, "DIV", "64", 14, 30, 6.0, 36, PortMask::P06);
Self::add_div_inst(&mut t, "IDIV", "32", 9, 12, 2.0, 9, PortMask::P06);
Self::add_div_inst(&mut t, "IDIV", "64", 14, 30, 6.0, 36, PortMask::P06);
Self::add_inst(&mut t, "IMUL", "32", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "IMUL", "64", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULX", "32", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULX", "64", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "PDEP", "32", 1, 0.5, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PDEP", "64", 1, 0.5, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PEXT", "32", 1, 0.5, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "PEXT", "64", 1, 0.5, 1, PortMask::P1, true, false);
Self::add_inst(
&mut t,
"LEA",
"32_simple",
1,
0.13,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"64_simple",
1,
0.13,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"32_complex",
3,
1.0,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"64_complex",
3,
1.0,
1,
PortMask::P15,
true,
false,
);
Self::add_inst(
&mut t,
"MOV",
"32_ld",
5,
0.25,
1,
PortMask::P234,
false,
true,
);
Self::add_inst(
&mut t,
"MOV",
"64_ld",
5,
0.25,
1,
PortMask::P234,
false,
true,
);
Self::add_inst(&mut t, "ADDSS", "32", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "ADDSD", "64", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULSS", "32", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULSD", "64", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_div_inst(&mut t, "DIVSS", "32", 11, 11, 3.0, 3, PortMask::P0);
Self::add_div_inst(&mut t, "DIVSD", "64", 13, 13, 4.0, 3, PortMask::P0);
Self::add_div_inst(&mut t, "SQRTSS", "32", 10, 10, 5.0, 3, PortMask::P0);
Self::add_div_inst(&mut t, "SQRTSD", "64", 15, 15, 5.0, 3, PortMask::P0);
Self::add_inst(
&mut t,
"VADDPS",
"256",
3,
0.33,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPS",
"512",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"256",
3,
0.33,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"512",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPD",
"512",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPD",
"512",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::GraniteRapids,
class: StoreLoadForwardClass::ExactMatch,
penalty_cycles: 0,
store_buffer_only: true,
requires_store_completion: false,
blocked_by_unaligned: false,
blocked_by_line_cross: false,
blocked_by_page_cross: false,
description: "Exact-address match: fast forward 0 cycles",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::GraniteRapids,
class: StoreLoadForwardClass::StoreContainsLoad,
penalty_cycles: 5,
store_buffer_only: false,
requires_store_completion: true,
blocked_by_unaligned: true,
blocked_by_line_cross: true,
blocked_by_page_cross: true,
description: "Store contains load: ~5 cycles penalty",
});
t.set_bypass(BypassProducer::IntegerAlu, BypassProducer::IntegerAlu, 1);
t.set_bypass(BypassProducer::IntegerMul, BypassProducer::IntegerAlu, 1);
t.set_bypass(BypassProducer::LoadL1, BypassProducer::IntegerAlu, 0);
t.set_bypass(BypassProducer::FloatAlu, BypassProducer::FloatAlu, 1);
t.set_bypass(BypassProducer::SimdAlu, BypassProducer::SimdAlu, 1);
self.tables.insert(X86MicroArch::GraniteRapids, t);
}
fn build_zen3(&mut self) {
let mut t = UarchLatencyTable::new(
X86MicroArch::Zen3,
"AMD Zen 3 (TSMC N7, 2020 — Vermeer/Cezanne/Milan)",
);
Self::add_inst(
&mut t,
"ADD",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"ADD",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"SUB",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"SUB",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"AND",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"AND",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(&mut t, "OR", "32", 1, 0.25, 1, PortMask::P0156, true, false);
Self::add_inst(&mut t, "OR", "64", 1, 0.25, 1, PortMask::P0156, true, false);
Self::add_inst(
&mut t,
"XOR",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"XOR",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_div_inst(&mut t, "DIV", "32", 8, 12, 2.0, 9, PortMask::P06);
Self::add_div_inst(&mut t, "DIV", "64", 13, 22, 5.5, 36, PortMask::P06);
Self::add_div_inst(&mut t, "IDIV", "32", 8, 12, 2.0, 9, PortMask::P06);
Self::add_div_inst(&mut t, "IDIV", "64", 13, 22, 5.5, 36, PortMask::P06);
Self::add_inst(&mut t, "IMUL", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "IMUL", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "MULX", "32", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(&mut t, "MULX", "64", 3, 1.0, 1, PortMask::P1, true, false);
Self::add_inst(
&mut t,
"PDEP",
"32",
10,
5.0,
8,
PortMask::P015,
true,
false,
);
Self::add_inst(
&mut t,
"PDEP",
"64",
14,
7.0,
12,
PortMask::P015,
true,
false,
);
Self::add_inst(
&mut t,
"PEXT",
"32",
10,
5.0,
8,
PortMask::P015,
true,
false,
);
Self::add_inst(
&mut t,
"PEXT",
"64",
14,
7.0,
12,
PortMask::P015,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"32_simple",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"64_simple",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"32_complex",
3,
1.0,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"64_complex",
3,
1.0,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"MOV",
"32_ld",
4,
0.33,
1,
PortMask::P237,
false,
true,
);
Self::add_inst(
&mut t,
"MOV",
"64_ld",
4,
0.33,
1,
PortMask::P237,
false,
true,
);
Self::add_inst(&mut t, "ADDSS", "32", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "ADDSD", "64", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULSS", "32", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULSD", "64", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_div_inst(&mut t, "DIVSS", "32", 13, 13, 5.0, 3, PortMask::P0);
Self::add_div_inst(&mut t, "DIVSD", "64", 15, 15, 5.0, 3, PortMask::P0);
Self::add_inst(
&mut t,
"VADDPS",
"128",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPS",
"256",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPD",
"128",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPD",
"256",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"128",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"256",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPD",
"128",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPD",
"256",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VPSHUFB",
"128",
1,
0.5,
1,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"VPSHUFB",
"256",
1,
1.0,
2,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"VSHUFPS",
"128",
1,
0.5,
1,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"VSHUFPS",
"256",
1,
1.0,
2,
PortMask::P5,
true,
false,
);
Self::add_inst(
&mut t,
"POPCNT",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"POPCNT",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"LZCNT",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"LZCNT",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"TZCNT",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"TZCNT",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::Zen3,
class: StoreLoadForwardClass::ExactMatch,
penalty_cycles: 0,
store_buffer_only: true,
requires_store_completion: false,
blocked_by_unaligned: false,
blocked_by_line_cross: false,
blocked_by_page_cross: false,
description: "Exact-address match: fast forward 0 cycles",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::Zen3,
class: StoreLoadForwardClass::LoadContainsStore,
penalty_cycles: 0,
store_buffer_only: true,
requires_store_completion: false,
blocked_by_unaligned: false,
blocked_by_line_cross: false,
blocked_by_page_cross: false,
description: "Load contains store: fast forward in 0 cycles",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::Zen3,
class: StoreLoadForwardClass::StoreContainsLoad,
penalty_cycles: 12,
store_buffer_only: false,
requires_store_completion: true,
blocked_by_unaligned: true,
blocked_by_line_cross: true,
blocked_by_page_cross: true,
description: "Store contains load: ~12 cycles penalty on Zen 3",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::Zen3,
class: StoreLoadForwardClass::SizeMismatch,
penalty_cycles: 12,
store_buffer_only: false,
requires_store_completion: true,
blocked_by_unaligned: true,
blocked_by_line_cross: true,
blocked_by_page_cross: true,
description: "Size mismatch: ~12 cycles penalty on Zen 3",
});
t.set_bypass(BypassProducer::IntegerAlu, BypassProducer::IntegerAlu, 1);
t.set_bypass(BypassProducer::IntegerMul, BypassProducer::IntegerAlu, 1);
t.set_bypass(BypassProducer::LoadL1, BypassProducer::IntegerAlu, 0);
t.set_bypass(BypassProducer::FloatAlu, BypassProducer::FloatAlu, 1);
t.set_bypass(BypassProducer::SimdAlu, BypassProducer::SimdAlu, 1);
self.tables.insert(X86MicroArch::Zen3, t);
}
fn build_zen4(&mut self) {
let mut t = UarchLatencyTable::new(
X86MicroArch::Zen4,
"AMD Zen 4 (TSMC N5, 2022 — Raphael/Genoa)",
);
Self::add_inst(
&mut t,
"ADD",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"ADD",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"SUB",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"SUB",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"AND",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"AND",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(&mut t, "OR", "32", 1, 0.25, 1, PortMask::P0156, true, false);
Self::add_inst(&mut t, "OR", "64", 1, 0.25, 1, PortMask::P0156, true, false);
Self::add_inst(
&mut t,
"XOR",
"32",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"XOR",
"64",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_div_inst(&mut t, "DIV", "32", 8, 12, 2.0, 9, PortMask::P06);
Self::add_div_inst(&mut t, "DIV", "64", 12, 20, 5.0, 36, PortMask::P06);
Self::add_div_inst(&mut t, "IDIV", "32", 8, 12, 2.0, 9, PortMask::P06);
Self::add_div_inst(&mut t, "IDIV", "64", 12, 20, 5.0, 36, PortMask::P06);
Self::add_inst(&mut t, "IMUL", "32", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "IMUL", "64", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULX", "32", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULX", "64", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(
&mut t,
"PDEP",
"32",
10,
5.0,
8,
PortMask::P015,
true,
false,
);
Self::add_inst(
&mut t,
"PDEP",
"64",
14,
7.0,
12,
PortMask::P015,
true,
false,
);
Self::add_inst(
&mut t,
"PEXT",
"32",
10,
5.0,
8,
PortMask::P015,
true,
false,
);
Self::add_inst(
&mut t,
"PEXT",
"64",
14,
7.0,
12,
PortMask::P015,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"32_simple",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"64_simple",
1,
0.25,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"32_complex",
3,
1.0,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"64_complex",
3,
1.0,
1,
PortMask::P0156,
true,
false,
);
Self::add_inst(
&mut t,
"MOV",
"32_ld",
4,
0.33,
1,
PortMask::P237,
false,
true,
);
Self::add_inst(
&mut t,
"MOV",
"64_ld",
4,
0.33,
1,
PortMask::P237,
false,
true,
);
Self::add_inst(&mut t, "ADDSS", "32", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "ADDSD", "64", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULSS", "32", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULSD", "64", 3, 0.5, 1, PortMask::P01, true, false);
Self::add_div_inst(&mut t, "DIVSS", "32", 12, 12, 4.5, 3, PortMask::P0);
Self::add_div_inst(&mut t, "DIVSD", "64", 14, 14, 4.5, 3, PortMask::P0);
Self::add_inst(
&mut t,
"VADDPS",
"256",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPS",
"512",
3,
1.0,
2,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"256",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"512",
3,
1.0,
2,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPD",
"256",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPD",
"512",
3,
1.0,
2,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPD",
"256",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPD",
"512",
3,
1.0,
2,
PortMask::P01,
true,
false,
);
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::Zen4,
class: StoreLoadForwardClass::ExactMatch,
penalty_cycles: 0,
store_buffer_only: true,
requires_store_completion: false,
blocked_by_unaligned: false,
blocked_by_line_cross: false,
blocked_by_page_cross: false,
description: "Exact-address match: fast forward 0 cycles",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::Zen4,
class: StoreLoadForwardClass::LoadContainsStore,
penalty_cycles: 0,
store_buffer_only: true,
requires_store_completion: false,
blocked_by_unaligned: false,
blocked_by_line_cross: false,
blocked_by_page_cross: false,
description: "Load contains store: fast forward 0 cycles",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::Zen4,
class: StoreLoadForwardClass::StoreContainsLoad,
penalty_cycles: 11,
store_buffer_only: false,
requires_store_completion: true,
blocked_by_unaligned: true,
blocked_by_line_cross: true,
blocked_by_page_cross: true,
description: "Store contains load: ~11 cycles penalty on Zen 4",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::Zen4,
class: StoreLoadForwardClass::SizeMismatch,
penalty_cycles: 11,
store_buffer_only: false,
requires_store_completion: true,
blocked_by_unaligned: true,
blocked_by_line_cross: true,
blocked_by_page_cross: true,
description: "Size mismatch: ~11 cycles penalty on Zen 4",
});
t.set_bypass(BypassProducer::IntegerAlu, BypassProducer::IntegerAlu, 1);
t.set_bypass(BypassProducer::IntegerMul, BypassProducer::IntegerAlu, 1);
t.set_bypass(BypassProducer::LoadL1, BypassProducer::IntegerAlu, 0);
t.set_bypass(BypassProducer::FloatAlu, BypassProducer::FloatAlu, 1);
t.set_bypass(BypassProducer::SimdAlu, BypassProducer::SimdAlu, 1);
self.tables.insert(X86MicroArch::Zen4, t);
}
fn build_zen5(&mut self) {
let mut t = UarchLatencyTable::new(
X86MicroArch::Zen5,
"AMD Zen 5 (TSMC N4, 2024 — Granite Ridge/Turin)",
);
Self::add_inst(
&mut t,
"ADD",
"32",
1,
0.13,
1,
PortMask::P015678,
true,
false,
);
Self::add_inst(
&mut t,
"ADD",
"64",
1,
0.13,
1,
PortMask::P015678,
true,
false,
);
Self::add_inst(
&mut t,
"SUB",
"32",
1,
0.13,
1,
PortMask::P015678,
true,
false,
);
Self::add_inst(
&mut t,
"SUB",
"64",
1,
0.13,
1,
PortMask::P015678,
true,
false,
);
Self::add_inst(
&mut t,
"AND",
"32",
1,
0.13,
1,
PortMask::P015678,
true,
false,
);
Self::add_inst(
&mut t,
"AND",
"64",
1,
0.13,
1,
PortMask::P015678,
true,
false,
);
Self::add_inst(
&mut t,
"OR",
"32",
1,
0.13,
1,
PortMask::P015678,
true,
false,
);
Self::add_inst(
&mut t,
"OR",
"64",
1,
0.13,
1,
PortMask::P015678,
true,
false,
);
Self::add_inst(
&mut t,
"XOR",
"32",
1,
0.13,
1,
PortMask::P015678,
true,
false,
);
Self::add_inst(
&mut t,
"XOR",
"64",
1,
0.13,
1,
PortMask::P015678,
true,
false,
);
Self::add_div_inst(&mut t, "DIV", "32", 7, 10, 1.5, 9, PortMask::P06);
Self::add_div_inst(&mut t, "DIV", "64", 10, 16, 3.5, 36, PortMask::P06);
Self::add_div_inst(&mut t, "IDIV", "32", 7, 10, 1.5, 9, PortMask::P06);
Self::add_div_inst(&mut t, "IDIV", "64", 10, 16, 3.5, 36, PortMask::P06);
Self::add_inst(&mut t, "IMUL", "32", 3, 0.33, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "IMUL", "64", 3, 0.33, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULX", "32", 3, 0.33, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULX", "64", 3, 0.33, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "PDEP", "32", 9, 4.5, 8, PortMask::P015, true, false);
Self::add_inst(
&mut t,
"PDEP",
"64",
12,
6.0,
12,
PortMask::P015,
true,
false,
);
Self::add_inst(&mut t, "PEXT", "32", 9, 4.5, 8, PortMask::P015, true, false);
Self::add_inst(
&mut t,
"PEXT",
"64",
12,
6.0,
12,
PortMask::P015,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"32_simple",
1,
0.13,
1,
PortMask::P015678,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"64_simple",
1,
0.13,
1,
PortMask::P015678,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"32_complex",
3,
1.0,
1,
PortMask::P015678,
true,
false,
);
Self::add_inst(
&mut t,
"LEA",
"64_complex",
3,
1.0,
1,
PortMask::P015678,
true,
false,
);
Self::add_inst(
&mut t,
"MOV",
"32_ld",
4,
0.25,
1,
PortMask::P238,
false,
true,
);
Self::add_inst(
&mut t,
"MOV",
"64_ld",
4,
0.25,
1,
PortMask::P238,
false,
true,
);
Self::add_inst(&mut t, "ADDSS", "32", 2, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "ADDSD", "64", 2, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULSS", "32", 2, 0.5, 1, PortMask::P01, true, false);
Self::add_inst(&mut t, "MULSD", "64", 2, 0.5, 1, PortMask::P01, true, false);
Self::add_div_inst(&mut t, "DIVSS", "32", 11, 11, 4.0, 3, PortMask::P0);
Self::add_div_inst(&mut t, "DIVSD", "64", 13, 13, 4.0, 3, PortMask::P0);
Self::add_inst(
&mut t,
"VADDPS",
"128",
2,
0.25,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPS",
"256",
2,
0.25,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPS",
"512",
2,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VADDPD",
"512",
2,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"128",
3,
0.25,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"256",
3,
0.25,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPS",
"512",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
Self::add_inst(
&mut t,
"VMULPD",
"512",
3,
0.5,
1,
PortMask::P01,
true,
false,
);
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::Zen5,
class: StoreLoadForwardClass::ExactMatch,
penalty_cycles: 0,
store_buffer_only: true,
requires_store_completion: false,
blocked_by_unaligned: false,
blocked_by_line_cross: false,
blocked_by_page_cross: false,
description: "Exact-address match: fast forward 0 cycles",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::Zen5,
class: StoreLoadForwardClass::LoadContainsStore,
penalty_cycles: 0,
store_buffer_only: true,
requires_store_completion: false,
blocked_by_unaligned: false,
blocked_by_line_cross: false,
blocked_by_page_cross: false,
description: "Load contains store: fast forward 0 cycles",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::Zen5,
class: StoreLoadForwardClass::StoreContainsLoad,
penalty_cycles: 9,
store_buffer_only: false,
requires_store_completion: true,
blocked_by_unaligned: true,
blocked_by_line_cross: true,
blocked_by_page_cross: true,
description: "Store contains load: ~9 cycles penalty on Zen 5",
});
t.add_store_load_rule(StoreLoadForwardRule {
uarch: X86MicroArch::Zen5,
class: StoreLoadForwardClass::SizeMismatch,
penalty_cycles: 9,
store_buffer_only: false,
requires_store_completion: true,
blocked_by_unaligned: true,
blocked_by_line_cross: true,
blocked_by_page_cross: true,
description: "Size mismatch: ~9 cycles penalty on Zen 5",
});
t.set_bypass(BypassProducer::IntegerAlu, BypassProducer::IntegerAlu, 1);
t.set_bypass(BypassProducer::IntegerMul, BypassProducer::IntegerAlu, 1);
t.set_bypass(BypassProducer::LoadL1, BypassProducer::IntegerAlu, 0);
t.set_bypass(BypassProducer::FloatAlu, BypassProducer::FloatAlu, 1);
t.set_bypass(BypassProducer::SimdAlu, BypassProducer::SimdAlu, 1);
self.tables.insert(X86MicroArch::Zen5, t);
}
}
impl Default for X86LatencyTables {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_all_tables_exist() {
let db = X86LatencyTables::new();
assert!(db.get(X86MicroArch::Skylake).is_some());
assert!(db.get(X86MicroArch::IceLake).is_some());
assert!(db.get(X86MicroArch::AlderLakePcore).is_some());
assert!(db.get(X86MicroArch::AlderLakeEcore).is_some());
assert!(db.get(X86MicroArch::SapphireRapids).is_some());
assert!(db.get(X86MicroArch::GraniteRapids).is_some());
assert!(db.get(X86MicroArch::Zen3).is_some());
assert!(db.get(X86MicroArch::Zen4).is_some());
assert!(db.get(X86MicroArch::Zen5).is_some());
}
#[test]
fn test_skylake_table_not_empty() {
let db = X86LatencyTables::new();
let skl = db.get(X86MicroArch::Skylake).unwrap();
assert!(skl.len() > 50);
}
#[test]
fn test_add_latency_across_uarchs() {
let db = X86LatencyTables::new();
for uarch in db.all_architectures() {
let lat = db.get_latency(*uarch, "ADD", "32", true);
assert!(lat.is_some(), "ADD 32 reg missing for {:?}", uarch);
let lat = lat.unwrap();
assert!(lat <= 3, "ADD 32 latency {} too high for {:?}", lat, uarch);
}
}
#[test]
fn test_div_latency_drops_over_generations() {
let db = X86LatencyTables::new();
let skl_div32 = db
.get_latency(X86MicroArch::Skylake, "DIV", "32", true)
.unwrap();
let icl_div32 = db
.get_latency(X86MicroArch::IceLake, "DIV", "32", true)
.unwrap();
let adl_div32 = db
.get_latency(X86MicroArch::AlderLakePcore, "DIV", "32", true)
.unwrap();
let gnr_div32 = db
.get_latency(X86MicroArch::GraniteRapids, "DIV", "32", true)
.unwrap();
assert!(
gnr_div32 <= adl_div32,
"Granite Rapids DIV should be faster or equal to Alder Lake"
);
assert!(
icl_div32 <= skl_div32,
"Ice Lake DIV should be faster or equal to Skylake"
);
}
#[test]
fn test_xor_zero_latency_intel() {
let db = X86LatencyTables::new();
for uarch in &[
X86MicroArch::Skylake,
X86MicroArch::IceLake,
X86MicroArch::AlderLakePcore,
X86MicroArch::SapphireRapids,
X86MicroArch::GraniteRapids,
] {
let is_zero = db.get(*uarch).unwrap().is_zero_latency("XOR", "32", true);
assert!(is_zero, "XOR should be zero-latency on {:?}", uarch);
}
}
#[test]
fn test_xor_not_zero_latency_amd() {
let db = X86LatencyTables::new();
for uarch in &[X86MicroArch::Zen3, X86MicroArch::Zen4, X86MicroArch::Zen5] {
let is_zero = db.get(*uarch).unwrap().is_zero_latency("XOR", "32", true);
assert!(!is_zero, "XOR should NOT be zero-latency on {:?}", uarch);
}
}
#[test]
fn test_load_latency() {
let db = X86LatencyTables::new();
assert_eq!(
db.get_latency(X86MicroArch::Skylake, "MOV", "32_ld", false),
Some(4)
);
assert_eq!(
db.get_latency(X86MicroArch::IceLake, "MOV", "32_ld", false),
Some(5)
);
assert_eq!(
db.get_latency(X86MicroArch::Zen3, "MOV", "32_ld", false),
Some(4)
);
assert_eq!(
db.get_latency(X86MicroArch::Zen5, "MOV", "32_ld", false),
Some(4)
);
}
#[test]
fn test_throughput_improvement_over_generations() {
let db = X86LatencyTables::new();
let skl_tput = db
.get_throughput(X86MicroArch::Skylake, "ADD", "32", true)
.unwrap();
let gnr_tput = db
.get_throughput(X86MicroArch::GraniteRapids, "ADD", "32", true)
.unwrap();
let zen5_tput = db
.get_throughput(X86MicroArch::Zen5, "ADD", "32", true)
.unwrap();
assert!(gnr_tput <= skl_tput);
assert!(zen5_tput <= skl_tput);
}
#[test]
fn test_store_load_forward_rules() {
let db = X86LatencyTables::new();
for uarch in db.all_architectures() {
let table = db.get(*uarch).unwrap();
let has_exact = table
.store_load_rules
.iter()
.any(|r| r.class == StoreLoadForwardClass::ExactMatch);
assert!(has_exact, "No ExactMatch rule for {:?}", uarch);
}
}
#[test]
fn test_bypass_matrix() {
let db = X86LatencyTables::new();
for uarch in db.all_architectures() {
let table = db.get(*uarch).unwrap();
let bypass = table.get_bypass(BypassProducer::IntegerAlu, BypassProducer::IntegerAlu);
assert!(bypass.is_some(), "No ALU→ALU bypass for {:?}", uarch);
assert!(bypass.unwrap() <= 2);
}
}
#[test]
fn test_port_masks_valid() {
let db = X86LatencyTables::new();
let skl = db.get(X86MicroArch::Skylake).unwrap();
let add_ports = skl.get_ports("ADD", "32", true).unwrap();
assert!(add_ports.contains(PortMask::P0));
assert!(add_ports.contains(PortMask::P1));
assert!(add_ports.contains(PortMask::P5));
assert!(add_ports.contains(PortMask::P6));
let shl_ports = skl.get_ports("SHL", "32", true).unwrap();
assert!(shl_ports.contains(PortMask::P0));
assert!(shl_ports.contains(PortMask::P6));
assert!(!shl_ports.contains(PortMask::P1));
let mul_ports = skl.get_ports("IMUL", "32", true).unwrap();
assert_eq!(mul_ports, PortMask::P1);
}
#[test]
fn test_uarch_metadata() {
assert_eq!(X86MicroArch::Skylake.name(), "skylake");
assert!(X86MicroArch::IceLake.is_intel());
assert!(X86MicroArch::Zen3.is_amd());
assert!(!X86MicroArch::Zen4.is_ecore());
assert!(X86MicroArch::AlderLakeEcore.is_ecore());
assert!(X86MicroArch::AlderLakePcore.is_pcore());
assert!(X86MicroArch::GraniteRapids.has_avx512());
assert!(!X86MicroArch::AlderLakeEcore.has_avx512());
}
#[test]
fn test_issue_width_grows() {
assert!(X86MicroArch::Skylake.issue_width() <= X86MicroArch::GraniteRapids.issue_width());
assert!(X86MicroArch::Zen3.issue_width() <= X86MicroArch::Zen5.issue_width());
}
#[test]
fn test_rob_size_grows() {
assert!(X86MicroArch::Skylake.rob_size() <= X86MicroArch::GraniteRapids.rob_size());
assert!(X86MicroArch::Zen3.rob_size() <= X86MicroArch::Zen5.rob_size());
}
#[test]
fn test_best_worst_avg_latency() {
let db = X86LatencyTables::new();
let best = db.get_best_latency("ADD", "32", true);
let worst = db.get_worst_latency("ADD", "32", true);
let avg = db.get_avg_latency("ADD", "32", true);
assert!(best.is_some());
assert!(worst.is_some());
assert!(avg.is_some());
assert!(best.unwrap() <= worst.unwrap());
assert!(avg.unwrap() >= best.unwrap() as f64);
assert!(avg.unwrap() <= worst.unwrap() as f64);
}
#[test]
fn test_zero_latency_check() {
let db = X86LatencyTables::new();
assert!(db.can_be_zero_latency("XOR", "32", true));
assert!(!db.can_be_zero_latency("ADD", "32", true));
}
#[test]
fn test_total_records() {
let db = X86LatencyTables::new();
let total = db.total_records();
assert!(total > 200, "Expected >200 records, got {}", total);
}
#[test]
fn test_lookup_by_mnemonic() {
let db = X86LatencyTables::new();
let skl = db.get(X86MicroArch::Skylake).unwrap();
let adds = skl.lookup_by_mnemonic("ADD");
assert!(!adds.is_empty());
let has_32 = adds.iter().any(|r| r.size == "32" && r.reg_reg);
assert!(has_32);
}
#[test]
fn test_ecore_vs_pcore_divider() {
let db = X86LatencyTables::new();
let pcore_div = db
.get_latency(X86MicroArch::AlderLakePcore, "DIV", "64", true)
.unwrap();
let ecore_div = db
.get_latency(X86MicroArch::AlderLakeEcore, "DIV", "64", true)
.unwrap();
assert!(ecore_div >= pcore_div, "E-core DIV should be >= P-core DIV");
}
#[test]
fn test_pdep_slow_on_amd_fast_on_intel() {
let db = X86LatencyTables::new();
let intel_lat = db
.get_latency(X86MicroArch::AlderLakePcore, "PDEP", "32", true)
.unwrap();
let amd_lat = db
.get_latency(X86MicroArch::Zen3, "PDEP", "32", true)
.unwrap();
assert!(amd_lat > intel_lat, "PDEP should be slower on AMD");
}
#[test]
fn test_port_mask_display() {
let mask = PortMask::P0156;
let s = format!("{}", mask);
assert!(s.contains("p0"));
assert!(s.contains("p1"));
assert!(s.contains("p5"));
assert!(s.contains("p6"));
}
#[test]
fn test_port_mask_operations() {
let a = PortMask::P0156;
assert!(a.contains(PortMask::P0));
assert!(a.contains(PortMask::P6));
assert!(!a.contains(PortMask::P2));
assert_eq!(a.count(), 4);
assert!(PortMask::P01.is_subset_of(PortMask::P0156));
assert!(!PortMask::P0156.is_subset_of(PortMask::P01));
}
#[test]
fn test_variable_latency_instructions() {
let db = X86LatencyTables::new();
let skl = db.get(X86MicroArch::Skylake).unwrap();
let div_recs = skl.lookup_by_mnemonic("DIV");
for rec in &div_recs {
assert!(rec.variable_latency);
assert!(rec.latency_min.is_some());
assert!(rec.latency_max.is_some());
assert!(rec.uses_divider);
}
}
#[test]
fn test_default_latency_tables() {
let db = X86LatencyTables::default();
assert!(db.total_records() > 0);
}
#[test]
fn test_pipeline_stages_present() {
let db = X86LatencyTables::new();
let skl = db.get(X86MicroArch::Skylake).unwrap();
let stages = skl.pipeline_stages.get("INT_ALU");
assert!(stages.is_some(), "INT_ALU pipeline stages missing for SKL");
let stages = stages.unwrap();
assert!(!stages.is_empty());
assert!(stages.iter().any(|s| s.stage == PipelineStage::Execute));
assert!(stages.iter().any(|s| s.stage == PipelineStage::Retire));
}
#[test]
fn test_bypass_producer_names() {
assert_eq!(BypassProducer::IntegerAlu.name(), "INT_ALU");
assert_eq!(BypassProducer::LoadL1.name(), "LOAD_L1");
assert_eq!(BypassProducer::SimdShuffle.name(), "SIMD_SHUFFLE");
}
#[test]
fn test_pipeline_stage_names() {
assert_eq!(PipelineStage::Fetch1.name(), "FETCH1");
assert_eq!(PipelineStage::Decode.name(), "DECODE");
assert_eq!(PipelineStage::Retire.name(), "RETIRE");
}
#[test]
fn test_store_forward_penalty_lookup() {
let db = X86LatencyTables::new();
let skl = db.get(X86MicroArch::Skylake).unwrap();
assert_eq!(
skl.get_forward_penalty(StoreLoadForwardClass::ExactMatch),
Some(0)
);
assert!(
skl.get_forward_penalty(StoreLoadForwardClass::StoreContainsLoad)
.unwrap()
> 0
);
}
#[test]
fn test_avx512_support() {
let db = X86LatencyTables::new();
assert!(X86MicroArch::Skylake.has_avx512());
assert!(X86MicroArch::SapphireRapids.has_avx512());
assert!(X86MicroArch::GraniteRapids.has_avx512());
assert!(X86MicroArch::Zen4.has_avx512());
assert!(X86MicroArch::Zen5.has_avx512());
assert!(!X86MicroArch::AlderLakeEcore.has_avx512());
for uarch in &[
X86MicroArch::SapphireRapids,
X86MicroArch::GraniteRapids,
X86MicroArch::Zen4,
X86MicroArch::Zen5,
] {
let table = db.get(*uarch).unwrap();
let found = table.lookup("VADDPS", "512", true);
assert!(found.is_some(), "VADDPS 512 missing for {:?}", uarch);
}
}
#[test]
fn test_zen4_avx512_double_pumped() {
let db = X86LatencyTables::new();
let zen4 = db.get(X86MicroArch::Zen4).unwrap();
let lat_256 = zen4.get_latency("VADDPS", "256", true);
let lat_512 = zen4.get_latency("VADDPS", "512", true);
assert_eq!(lat_256, lat_512); }
#[test]
fn test_zen5_full_width_avx512() {
let db = X86LatencyTables::new();
let zen5 = db.get(X86MicroArch::Zen5).unwrap();
let tput_256 = zen5.get_throughput("VADDPS", "256", true).unwrap();
let tput_512 = zen5.get_throughput("VADDPS", "512", true).unwrap();
assert!(tput_512 >= tput_256);
}
}