This commit adds the `wasmtime settings` command to print out available Cranelift settings for a target (defaults to the host). The compile command has been updated to remove the Cranelift ISA options in favor of encouraging users to use `wasmtime settings` to discover what settings are available. This will reduce the maintenance cost for syncing the compile command with Cranelift ISA flags.
447 lines
12 KiB
Rust
447 lines
12 KiB
Rust
use std::iter;
|
|
|
|
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
|
pub(crate) struct BoolSettingIndex(usize);
|
|
|
|
#[derive(Hash, PartialEq, Eq)]
|
|
pub(crate) struct BoolSetting {
|
|
pub default: bool,
|
|
pub bit_offset: u8,
|
|
pub predicate_number: u8,
|
|
}
|
|
|
|
#[derive(Hash, PartialEq, Eq)]
|
|
pub(crate) enum SpecificSetting {
|
|
Bool(BoolSetting),
|
|
Enum(Vec<&'static str>),
|
|
Num(u8),
|
|
}
|
|
|
|
#[derive(Hash, PartialEq, Eq)]
|
|
pub(crate) struct Setting {
|
|
pub name: &'static str,
|
|
pub description: &'static str,
|
|
pub comment: &'static str,
|
|
pub specific: SpecificSetting,
|
|
pub byte_offset: u8,
|
|
}
|
|
|
|
impl Setting {
|
|
pub fn default_byte(&self) -> u8 {
|
|
match self.specific {
|
|
SpecificSetting::Bool(BoolSetting {
|
|
default,
|
|
bit_offset,
|
|
..
|
|
}) => {
|
|
if default {
|
|
1 << bit_offset
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
SpecificSetting::Enum(_) => 0,
|
|
SpecificSetting::Num(default) => default,
|
|
}
|
|
}
|
|
|
|
fn byte_for_value(&self, v: bool) -> u8 {
|
|
match self.specific {
|
|
SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => {
|
|
if v {
|
|
1 << bit_offset
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
_ => panic!("byte_for_value shouldn't be used for non-boolean settings."),
|
|
}
|
|
}
|
|
|
|
fn byte_mask(&self) -> u8 {
|
|
match self.specific {
|
|
SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => 1 << bit_offset,
|
|
_ => panic!("byte_for_value shouldn't be used for non-boolean settings."),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Hash, PartialEq, Eq)]
|
|
pub(crate) struct PresetIndex(usize);
|
|
|
|
#[derive(Hash, PartialEq, Eq)]
|
|
pub(crate) enum PresetType {
|
|
BoolSetting(BoolSettingIndex),
|
|
OtherPreset(PresetIndex),
|
|
}
|
|
|
|
impl Into<PresetType> for BoolSettingIndex {
|
|
fn into(self) -> PresetType {
|
|
PresetType::BoolSetting(self)
|
|
}
|
|
}
|
|
impl Into<PresetType> for PresetIndex {
|
|
fn into(self) -> PresetType {
|
|
PresetType::OtherPreset(self)
|
|
}
|
|
}
|
|
|
|
#[derive(Hash, PartialEq, Eq)]
|
|
pub(crate) struct Preset {
|
|
pub name: &'static str,
|
|
pub description: &'static str,
|
|
values: Vec<BoolSettingIndex>,
|
|
}
|
|
|
|
impl Preset {
|
|
pub fn layout(&self, group: &SettingGroup) -> Vec<(u8, u8)> {
|
|
let mut layout: Vec<(u8, u8)> = iter::repeat((0, 0))
|
|
.take(group.settings_size as usize)
|
|
.collect();
|
|
for bool_index in &self.values {
|
|
let setting = &group.settings[bool_index.0];
|
|
let mask = setting.byte_mask();
|
|
let val = setting.byte_for_value(true);
|
|
assert!((val & !mask) == 0);
|
|
let (ref mut l_mask, ref mut l_val) =
|
|
*layout.get_mut(setting.byte_offset as usize).unwrap();
|
|
*l_mask |= mask;
|
|
*l_val = (*l_val & !mask) | val;
|
|
}
|
|
layout
|
|
}
|
|
}
|
|
|
|
pub(crate) struct SettingGroup {
|
|
pub name: &'static str,
|
|
pub settings: Vec<Setting>,
|
|
pub bool_start_byte_offset: u8,
|
|
pub settings_size: u8,
|
|
pub presets: Vec<Preset>,
|
|
pub predicates: Vec<Predicate>,
|
|
}
|
|
|
|
impl SettingGroup {
|
|
fn num_bool_settings(&self) -> u8 {
|
|
self.settings
|
|
.iter()
|
|
.filter(|s| {
|
|
if let SpecificSetting::Bool(_) = s.specific {
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
})
|
|
.count() as u8
|
|
}
|
|
|
|
pub fn byte_size(&self) -> u8 {
|
|
let num_predicates = self.num_bool_settings() + (self.predicates.len() as u8);
|
|
self.bool_start_byte_offset + (num_predicates + 7) / 8
|
|
}
|
|
|
|
pub fn get_bool(&self, name: &'static str) -> (BoolSettingIndex, &Self) {
|
|
for (i, s) in self.settings.iter().enumerate() {
|
|
if let SpecificSetting::Bool(_) = s.specific {
|
|
if s.name == name {
|
|
return (BoolSettingIndex(i), self);
|
|
}
|
|
}
|
|
}
|
|
panic!("Should have found bool setting by name.");
|
|
}
|
|
|
|
pub fn predicate_by_name(&self, name: &'static str) -> SettingPredicateNumber {
|
|
self.predicates
|
|
.iter()
|
|
.find(|pred| pred.name == name)
|
|
.unwrap_or_else(|| panic!("unknown predicate {}", name))
|
|
.number
|
|
}
|
|
}
|
|
|
|
/// This is the basic information needed to track the specific parts of a setting when building
|
|
/// them.
|
|
pub(crate) enum ProtoSpecificSetting {
|
|
Bool(bool),
|
|
Enum(Vec<&'static str>),
|
|
Num(u8),
|
|
}
|
|
|
|
/// This is the information provided during building for a setting.
|
|
struct ProtoSetting {
|
|
name: &'static str,
|
|
description: &'static str,
|
|
comment: &'static str,
|
|
specific: ProtoSpecificSetting,
|
|
}
|
|
|
|
#[derive(Hash, PartialEq, Eq)]
|
|
pub(crate) enum PredicateNode {
|
|
OwnedBool(BoolSettingIndex),
|
|
SharedBool(&'static str, &'static str),
|
|
Not(Box<PredicateNode>),
|
|
And(Box<PredicateNode>, Box<PredicateNode>),
|
|
}
|
|
|
|
impl Into<PredicateNode> for BoolSettingIndex {
|
|
fn into(self) -> PredicateNode {
|
|
PredicateNode::OwnedBool(self)
|
|
}
|
|
}
|
|
impl<'a> Into<PredicateNode> for (BoolSettingIndex, &'a SettingGroup) {
|
|
fn into(self) -> PredicateNode {
|
|
let (index, group) = (self.0, self.1);
|
|
let setting = &group.settings[index.0];
|
|
PredicateNode::SharedBool(group.name, setting.name)
|
|
}
|
|
}
|
|
|
|
impl PredicateNode {
|
|
fn render(&self, group: &SettingGroup) -> String {
|
|
match *self {
|
|
PredicateNode::OwnedBool(bool_setting_index) => format!(
|
|
"{}.{}()",
|
|
group.name, group.settings[bool_setting_index.0].name
|
|
),
|
|
PredicateNode::SharedBool(ref group_name, ref bool_name) => {
|
|
format!("{}.{}()", group_name, bool_name)
|
|
}
|
|
PredicateNode::And(ref lhs, ref rhs) => {
|
|
format!("{} && {}", lhs.render(group), rhs.render(group))
|
|
}
|
|
PredicateNode::Not(ref node) => format!("!({})", node.render(group)),
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ProtoPredicate {
|
|
pub name: &'static str,
|
|
node: PredicateNode,
|
|
}
|
|
|
|
pub(crate) type SettingPredicateNumber = u8;
|
|
|
|
pub(crate) struct Predicate {
|
|
pub name: &'static str,
|
|
node: PredicateNode,
|
|
pub number: SettingPredicateNumber,
|
|
}
|
|
|
|
impl Predicate {
|
|
pub fn render(&self, group: &SettingGroup) -> String {
|
|
self.node.render(group)
|
|
}
|
|
}
|
|
|
|
pub(crate) struct SettingGroupBuilder {
|
|
name: &'static str,
|
|
settings: Vec<ProtoSetting>,
|
|
presets: Vec<Preset>,
|
|
predicates: Vec<ProtoPredicate>,
|
|
}
|
|
|
|
impl SettingGroupBuilder {
|
|
pub fn new(name: &'static str) -> Self {
|
|
Self {
|
|
name,
|
|
settings: Vec::new(),
|
|
presets: Vec::new(),
|
|
predicates: Vec::new(),
|
|
}
|
|
}
|
|
|
|
fn add_setting(
|
|
&mut self,
|
|
name: &'static str,
|
|
description: &'static str,
|
|
comment: &'static str,
|
|
specific: ProtoSpecificSetting,
|
|
) {
|
|
self.settings.push(ProtoSetting {
|
|
name,
|
|
description,
|
|
comment,
|
|
specific,
|
|
})
|
|
}
|
|
|
|
pub fn add_bool(
|
|
&mut self,
|
|
name: &'static str,
|
|
description: &'static str,
|
|
comment: &'static str,
|
|
default: bool,
|
|
) -> BoolSettingIndex {
|
|
assert!(
|
|
self.predicates.is_empty(),
|
|
"predicates must be added after the boolean settings"
|
|
);
|
|
self.add_setting(
|
|
name,
|
|
description,
|
|
comment,
|
|
ProtoSpecificSetting::Bool(default),
|
|
);
|
|
BoolSettingIndex(self.settings.len() - 1)
|
|
}
|
|
|
|
pub fn add_enum(
|
|
&mut self,
|
|
name: &'static str,
|
|
description: &'static str,
|
|
comment: &'static str,
|
|
values: Vec<&'static str>,
|
|
) {
|
|
self.add_setting(
|
|
name,
|
|
description,
|
|
comment,
|
|
ProtoSpecificSetting::Enum(values),
|
|
);
|
|
}
|
|
|
|
pub fn add_num(
|
|
&mut self,
|
|
name: &'static str,
|
|
description: &'static str,
|
|
comment: &'static str,
|
|
default: u8,
|
|
) {
|
|
self.add_setting(
|
|
name,
|
|
description,
|
|
comment,
|
|
ProtoSpecificSetting::Num(default),
|
|
);
|
|
}
|
|
|
|
pub fn add_predicate(&mut self, name: &'static str, node: PredicateNode) {
|
|
self.predicates.push(ProtoPredicate { name, node });
|
|
}
|
|
|
|
pub fn add_preset(
|
|
&mut self,
|
|
name: &'static str,
|
|
description: &'static str,
|
|
args: Vec<PresetType>,
|
|
) -> PresetIndex {
|
|
let mut values = Vec::new();
|
|
for arg in args {
|
|
match arg {
|
|
PresetType::OtherPreset(index) => {
|
|
values.extend(self.presets[index.0].values.iter());
|
|
}
|
|
PresetType::BoolSetting(index) => values.push(index),
|
|
}
|
|
}
|
|
self.presets.push(Preset {
|
|
name,
|
|
description,
|
|
values,
|
|
});
|
|
PresetIndex(self.presets.len() - 1)
|
|
}
|
|
|
|
/// Compute the layout of the byte vector used to represent this settings
|
|
/// group.
|
|
///
|
|
/// The byte vector contains the following entries in order:
|
|
///
|
|
/// 1. Byte-sized settings like `NumSetting` and `EnumSetting`.
|
|
/// 2. `BoolSetting` settings.
|
|
/// 3. Precomputed named predicates.
|
|
/// 4. Other numbered predicates, including parent predicates that need to be accessible by
|
|
/// number.
|
|
///
|
|
/// Set `self.settings_size` to the length of the byte vector prefix that
|
|
/// contains the settings. All bytes after that are computed, not
|
|
/// configured.
|
|
///
|
|
/// Set `self.boolean_offset` to the beginning of the numbered predicates,
|
|
/// 2. in the list above.
|
|
///
|
|
/// Assign `byte_offset` and `bit_offset` fields in all settings.
|
|
pub fn build(self) -> SettingGroup {
|
|
let mut group = SettingGroup {
|
|
name: self.name,
|
|
settings: Vec::new(),
|
|
bool_start_byte_offset: 0,
|
|
settings_size: 0,
|
|
presets: Vec::new(),
|
|
predicates: Vec::new(),
|
|
};
|
|
|
|
let mut byte_offset = 0;
|
|
|
|
// Assign the non-boolean settings first.
|
|
for s in &self.settings {
|
|
let specific = match s.specific {
|
|
ProtoSpecificSetting::Bool(..) => continue,
|
|
ProtoSpecificSetting::Enum(ref values) => SpecificSetting::Enum(values.clone()),
|
|
ProtoSpecificSetting::Num(default) => SpecificSetting::Num(default),
|
|
};
|
|
|
|
group.settings.push(Setting {
|
|
name: s.name,
|
|
description: s.description,
|
|
comment: s.comment,
|
|
byte_offset,
|
|
specific,
|
|
});
|
|
|
|
byte_offset += 1;
|
|
}
|
|
|
|
group.bool_start_byte_offset = byte_offset;
|
|
|
|
let mut predicate_number = 0;
|
|
|
|
// Then the boolean settings.
|
|
for s in &self.settings {
|
|
let default = match s.specific {
|
|
ProtoSpecificSetting::Bool(default) => default,
|
|
ProtoSpecificSetting::Enum(_) | ProtoSpecificSetting::Num(_) => continue,
|
|
};
|
|
group.settings.push(Setting {
|
|
name: s.name,
|
|
description: s.description,
|
|
comment: s.comment,
|
|
byte_offset: byte_offset + predicate_number / 8,
|
|
specific: SpecificSetting::Bool(BoolSetting {
|
|
default,
|
|
bit_offset: predicate_number % 8,
|
|
predicate_number,
|
|
}),
|
|
});
|
|
predicate_number += 1;
|
|
}
|
|
|
|
assert!(
|
|
group.predicates.is_empty(),
|
|
"settings_size is the byte size before adding predicates"
|
|
);
|
|
group.settings_size = group.byte_size();
|
|
|
|
// Sort predicates by name to ensure the same order as the Python code.
|
|
let mut predicates = self.predicates;
|
|
predicates.sort_by_key(|predicate| predicate.name);
|
|
|
|
group
|
|
.predicates
|
|
.extend(predicates.into_iter().map(|predicate| {
|
|
let number = predicate_number;
|
|
predicate_number += 1;
|
|
Predicate {
|
|
name: predicate.name,
|
|
node: predicate.node,
|
|
number,
|
|
}
|
|
}));
|
|
|
|
group.presets.extend(self.presets);
|
|
|
|
group
|
|
}
|
|
}
|