use serde::Deserialize;
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct GehlConfig {
pub lengths: Vec<u32>,
pub log_entries: u32,
pub weight_init: i8,
}
impl GehlConfig {
fn new(lengths: &[u32], log_entries: u32, weight_init: i8) -> Self {
Self { lengths: lengths.to_vec(), log_entries, weight_init }
}
}
impl Default for GehlConfig {
fn default() -> Self {
Self::new(&[], 0, 0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct LocalGehlConfig {
pub histories: usize,
pub index_shift: u32,
pub mix_pc: bool,
pub gehl: GehlConfig,
}
impl Default for LocalGehlConfig {
fn default() -> Self {
Self { histories: 16, index_shift: 2, mix_pc: false, gehl: GehlConfig::default() }
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct ScConfig {
pub log_bias: u32,
pub counter_bits: u32,
pub weight_bits: u32,
pub bias_weight_init: i8,
pub chooser_bits: u32,
pub threshold_bits: u32,
pub initial_threshold: i32,
pub per_pc_threshold_bits: u32,
pub per_pc_threshold_width: u32,
pub initial_per_pc_threshold: i32,
pub threshold_weight_step: i32,
pub halve_short_tables: bool,
pub imli_counter_bits: u32,
pub global: GehlConfig,
pub backward: GehlConfig,
pub path: GehlConfig,
pub local: Vec<LocalGehlConfig>,
pub imli: GehlConfig,
pub imli_history: GehlConfig,
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum ScConfigError {
#[error("{field} of {bits} bits is outside {min}..={max}")]
Width {
field: &'static str,
bits: u32,
min: u32,
max: u32,
},
#[error("{component} has {tables} tables; at most {MAX_GEHL_TABLES} are allowed")]
GehlTables {
component: &'static str,
tables: usize,
},
#[error("{component} reads {length} history bits; at most 64 are allowed")]
HistoryLength {
component: &'static str,
length: u32,
},
#[error("{0} local components; at most {MAX_LOCAL_HISTORIES} are allowed")]
LocalComponents(usize),
#[error("{0} local histories is not a power of two")]
LocalHistories(usize),
}
pub const MAX_GEHL_TABLES: usize = 8;
pub const MAX_LOCAL_HISTORIES: usize = 4;
impl ScConfig {
pub fn validate(&self) -> Result<(), ScConfigError> {
let widths = [
("counter_bits", self.counter_bits, 2, 8),
("weight_bits", self.weight_bits, 2, 8),
("chooser_bits", self.chooser_bits, 2, 8),
("threshold_bits", self.threshold_bits, 4, 24),
("per_pc_threshold_width", self.per_pc_threshold_width, 2, 24),
("log_bias", self.log_bias, 2, 24),
("per_pc_threshold_bits", self.per_pc_threshold_bits, 0, 24),
("imli_counter_bits", self.imli_counter_bits, 1, 16),
];
for (field, bits, min, max) in widths {
if !(min..=max).contains(&bits) {
return Err(ScConfigError::Width { field, bits, min, max });
}
}
if self.local.len() > MAX_LOCAL_HISTORIES {
return Err(ScConfigError::LocalComponents(self.local.len()));
}
for local in &self.local {
if !local.histories.is_power_of_two() {
return Err(ScConfigError::LocalHistories(local.histories));
}
if local.index_shift >= 64 {
return Err(ScConfigError::Width {
field: "local index_shift",
bits: local.index_shift,
min: 0,
max: 63,
});
}
}
let components = [
("global", &self.global),
("backward", &self.backward),
("path", &self.path),
("imli", &self.imli),
("imli_history", &self.imli_history),
]
.into_iter()
.chain(self.local.iter().map(|local| ("local", &local.gehl)));
for (component, gehl) in components {
gehl.validate(component, self.halve_short_tables)?;
}
Ok(())
}
}
impl GehlConfig {
fn validate(
&self,
component: &'static str,
halve_short_tables: bool,
) -> Result<(), ScConfigError> {
if self.lengths.is_empty() {
return Ok(());
}
if self.lengths.len() > MAX_GEHL_TABLES {
return Err(ScConfigError::GehlTables { component, tables: self.lengths.len() });
}
if let Some(&length) = self.lengths.iter().find(|&&length| length > 64) {
return Err(ScConfigError::HistoryLength { component, length });
}
let min = if halve_short_tables { 2 } else { 1 };
if !(min..=24).contains(&self.log_entries) {
return Err(ScConfigError::Width {
field: "GEHL log_entries",
bits: self.log_entries,
min,
max: 24,
});
}
Ok(())
}
}
impl Default for ScConfig {
fn default() -> Self {
Self {
log_bias: 8,
counter_bits: 6,
weight_bits: 6,
bias_weight_init: 4,
chooser_bits: 7,
threshold_bits: 12,
initial_threshold: 35,
per_pc_threshold_bits: 6,
per_pc_threshold_width: 8,
initial_per_pc_threshold: 0,
threshold_weight_step: 12,
halve_short_tables: true,
imli_counter_bits: 8,
global: GehlConfig::default(),
backward: GehlConfig::new(&[40, 24, 10], 10, 7),
path: GehlConfig::new(&[25, 16, 9], 9, 7),
local: vec![
LocalGehlConfig {
histories: 256,
index_shift: 2,
mix_pc: false,
gehl: GehlConfig::new(&[11, 6, 3], 10, 7),
},
LocalGehlConfig {
histories: 16,
index_shift: 5,
mix_pc: true,
gehl: GehlConfig::new(&[16, 11, 6], 9, 7),
},
LocalGehlConfig {
histories: 16,
index_shift: 10,
mix_pc: false,
gehl: GehlConfig::new(&[9, 4], 10, 7),
},
],
imli: GehlConfig::new(&[8], 8, 7),
imli_history: GehlConfig::new(&[10, 4], 9, 0),
}
}
}