Path: blob/main/cranelift/codegen/src/settings.rs
1693 views
//! Shared settings module.1//!2//! This module defines data structures to access the settings defined in the meta language.3//!4//! Each settings group is translated to a `Flags` struct either in this module or in its5//! ISA-specific `settings` module. The struct provides individual getter methods for all of the6//! settings as well as computed predicate flags.7//!8//! The `Flags` struct is immutable once it has been created. A `Builder` instance is used to9//! create it.10//!11//! # Example12//! ```13//! use cranelift_codegen::settings::{self, Configurable};14//!15//! let mut b = settings::builder();16//! b.set("opt_level", "speed_and_size");17//!18//! let f = settings::Flags::new(b);19//! assert_eq!(f.opt_level(), settings::OptLevel::SpeedAndSize);20//! ```2122use crate::constant_hash::{probe, simple_hash};23use crate::isa::TargetIsa;24use alloc::boxed::Box;25use alloc::string::{String, ToString};26use core::fmt;27use core::str;2829/// A string-based configurator for settings groups.30///31/// The `Configurable` protocol allows settings to be modified by name before a finished `Flags`32/// struct is created.33pub trait Configurable {34/// Set the string value of any setting by name.35///36/// This can set any type of setting whether it is numeric, boolean, or enumerated.37fn set(&mut self, name: &str, value: &str) -> SetResult<()>;3839/// Enable a boolean setting or apply a preset.40///41/// If the identified setting isn't a boolean or a preset, a `BadType` error is returned.42fn enable(&mut self, name: &str) -> SetResult<()>;43}4445/// Represents the kind of setting.46#[derive(Clone, Copy, Debug, Eq, PartialEq)]47pub enum SettingKind {48/// The setting is an enumeration.49Enum,50/// The setting is a number.51Num,52/// The setting is a boolean.53Bool,54/// The setting is a preset.55Preset,56}5758/// Represents an available builder setting.59///60/// This is used for iterating settings in a builder.61#[derive(Clone, Copy, Debug)]62pub struct Setting {63/// The name of the setting.64pub name: &'static str,65/// The description of the setting.66pub description: &'static str,67/// The kind of the setting.68pub kind: SettingKind,69/// The supported values of the setting (for enum values).70pub values: Option<&'static [&'static str]>,71}7273/// Represents a setting value.74///75/// This is used for iterating values in `Flags`.76pub struct Value {77/// The name of the setting associated with this value.78pub name: &'static str,79pub(crate) detail: detail::Detail,80pub(crate) values: Option<&'static [&'static str]>,81pub(crate) value: u8,82}8384impl Value {85/// Gets the kind of setting.86pub fn kind(&self) -> SettingKind {87match &self.detail {88detail::Detail::Enum { .. } => SettingKind::Enum,89detail::Detail::Num => SettingKind::Num,90detail::Detail::Bool { .. } => SettingKind::Bool,91detail::Detail::Preset => unreachable!(),92}93}9495/// Gets the enum value if the value is from an enum setting.96pub fn as_enum(&self) -> Option<&'static str> {97self.values.map(|v| v[self.value as usize])98}99100/// Gets the numerical value if the value is from a num setting.101pub fn as_num(&self) -> Option<u8> {102match &self.detail {103detail::Detail::Num => Some(self.value),104_ => None,105}106}107108/// Gets the boolean value if the value is from a boolean setting.109pub fn as_bool(&self) -> Option<bool> {110match &self.detail {111detail::Detail::Bool { bit } => Some(self.value & (1 << bit) != 0),112_ => None,113}114}115116/// Builds a string from the current value117pub fn value_string(&self) -> String {118match self.kind() {119SettingKind::Enum => self.as_enum().map(|b| b.to_string()),120SettingKind::Num => self.as_num().map(|b| b.to_string()),121SettingKind::Bool => self.as_bool().map(|b| b.to_string()),122SettingKind::Preset => unreachable!(),123}124.unwrap()125}126}127128impl fmt::Display for Value {129fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {130if let Some(enum_variant) = self.as_enum() {131write!(f, "{}={}", self.name, enum_variant)132} else if let Some(num) = self.as_num() {133write!(f, "{}={}", self.name, num)134} else if let Some(b) = self.as_bool() {135if b {136write!(f, "{}=1", self.name)137} else {138write!(f, "{}=0", self.name)139}140} else {141unreachable!()142}143}144}145146/// Collect settings values based on a template.147#[derive(Clone, Hash)]148pub struct Builder {149template: &'static detail::Template,150bytes: Box<[u8]>,151}152153impl Builder {154/// Create a new builder with defaults and names from the given template.155pub fn new(tmpl: &'static detail::Template) -> Self {156Self {157template: tmpl,158bytes: tmpl.defaults.into(),159}160}161162/// Extract contents of builder once everything is configured.163pub fn state_for(&self, name: &str) -> &[u8] {164assert_eq!(name, self.template.name);165&self.bytes166}167168/// Iterates the available settings in the builder.169pub fn iter(&self) -> impl Iterator<Item = Setting> + use<> {170let template = self.template;171172template.descriptors.iter().map(move |d| {173let (kind, values) = match d.detail {174detail::Detail::Enum { last, enumerators } => {175let values = template.enums(last, enumerators);176(SettingKind::Enum, Some(values))177}178detail::Detail::Num => (SettingKind::Num, None),179detail::Detail::Bool { .. } => (SettingKind::Bool, None),180detail::Detail::Preset => (SettingKind::Preset, None),181};182183Setting {184name: d.name,185description: d.description,186kind,187values,188}189})190}191192/// Set the value of a single bit.193fn set_bit(&mut self, offset: usize, bit: u8, value: bool) {194let byte = &mut self.bytes[offset];195let mask = 1 << bit;196if value {197*byte |= mask;198} else {199*byte &= !mask;200}201}202203/// Apply a preset. The argument is a slice of (mask, value) bytes.204fn apply_preset(&mut self, values: &[(u8, u8)]) {205for (byte, &(mask, value)) in self.bytes.iter_mut().zip(values) {206*byte = (*byte & !mask) | value;207}208}209210/// Look up a descriptor by name.211fn lookup(&self, name: &str) -> SetResult<(usize, detail::Detail)> {212match probe(self.template, name, simple_hash(name)) {213Err(_) => Err(SetError::BadName(name.to_string())),214Ok(entry) => {215let d = &self.template.descriptors[self.template.hash_table[entry] as usize];216Ok((d.offset as usize, d.detail))217}218}219}220}221222fn parse_bool_value(value: &str) -> SetResult<bool> {223match value {224"true" | "on" | "yes" | "1" => Ok(true),225"false" | "off" | "no" | "0" => Ok(false),226_ => Err(SetError::BadValue("bool".to_string())),227}228}229230fn parse_enum_value(value: &str, choices: &[&str]) -> SetResult<u8> {231match choices.iter().position(|&tag| tag == value) {232Some(idx) => Ok(idx as u8),233None => Err(SetError::BadValue(format!(234"any among {}",235choices.join(", ")236))),237}238}239240impl Configurable for Builder {241fn enable(&mut self, name: &str) -> SetResult<()> {242use self::detail::Detail;243let (offset, detail) = self.lookup(name)?;244match detail {245Detail::Bool { bit } => {246self.set_bit(offset, bit, true);247Ok(())248}249Detail::Preset => {250self.apply_preset(&self.template.presets[offset..]);251Ok(())252}253_ => Err(SetError::BadType),254}255}256257fn set(&mut self, name: &str, value: &str) -> SetResult<()> {258use self::detail::Detail;259let (offset, detail) = self.lookup(name)?;260match detail {261Detail::Bool { bit } => {262self.set_bit(offset, bit, parse_bool_value(value)?);263}264Detail::Num => {265self.bytes[offset] = value266.parse()267.map_err(|_| SetError::BadValue("number".to_string()))?;268}269Detail::Enum { last, enumerators } => {270self.bytes[offset] =271parse_enum_value(value, self.template.enums(last, enumerators))?;272}273Detail::Preset => return Err(SetError::BadName(name.to_string())),274}275Ok(())276}277}278279/// An error produced when changing a setting.280#[derive(Debug, PartialEq, Eq)]281pub enum SetError {282/// No setting by this name exists.283BadName(String),284285/// Type mismatch for setting (e.g., setting an enum setting as a bool).286BadType,287288/// This is not a valid value for this setting.289BadValue(String),290}291292impl std::error::Error for SetError {}293294impl fmt::Display for SetError {295fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {296match self {297SetError::BadName(name) => write!(f, "No existing setting named '{name}'"),298SetError::BadType => {299write!(f, "Trying to set a setting with the wrong type")300}301SetError::BadValue(value) => {302write!(f, "Unexpected value for a setting, expected {value}")303}304}305}306}307308/// A result returned when changing a setting.309pub type SetResult<T> = Result<T, SetError>;310311/// Implementation details for generated code.312///313/// This module holds definitions that need to be public so the can be instantiated by generated314/// code in other modules.315pub mod detail {316use crate::constant_hash;317use core::fmt;318use core::hash::Hash;319320/// An instruction group template.321#[derive(Hash)]322pub struct Template {323/// Name of the instruction group.324pub name: &'static str,325/// List of setting descriptors.326pub descriptors: &'static [Descriptor],327/// Union of all enumerators.328pub enumerators: &'static [&'static str],329/// Hash table of settings.330pub hash_table: &'static [u16],331/// Default values.332pub defaults: &'static [u8],333/// Pairs of (mask, value) for presets.334pub presets: &'static [(u8, u8)],335}336337impl Template {338/// Get enumerators corresponding to a `Details::Enum`.339pub fn enums(&self, last: u8, enumerators: u16) -> &[&'static str] {340let from = enumerators as usize;341let len = usize::from(last) + 1;342&self.enumerators[from..from + len]343}344345/// Format a setting value as a TOML string. This is mostly for use by the generated346/// `Display` implementation.347pub fn format_toml_value(348&self,349detail: Detail,350byte: u8,351f: &mut fmt::Formatter,352) -> fmt::Result {353match detail {354Detail::Bool { bit } => write!(f, "{}", (byte & (1 << bit)) != 0),355Detail::Num => write!(f, "{byte}"),356Detail::Enum { last, enumerators } => {357if byte <= last {358let tags = self.enums(last, enumerators);359write!(f, "\"{}\"", tags[usize::from(byte)])360} else {361write!(f, "{byte}")362}363}364// Presets aren't printed. They are reflected in the other settings.365Detail::Preset { .. } => Ok(()),366}367}368}369370/// The template contains a hash table for by-name lookup.371impl<'a> constant_hash::Table<&'a str> for Template {372fn len(&self) -> usize {373self.hash_table.len()374}375376fn key(&self, idx: usize) -> Option<&'a str> {377let e = self.hash_table[idx] as usize;378if e < self.descriptors.len() {379Some(self.descriptors[e].name)380} else {381None382}383}384}385386/// A setting descriptor holds the information needed to generically set and print a setting.387///388/// Each settings group will be represented as a constant DESCRIPTORS array.389#[derive(Hash)]390pub struct Descriptor {391/// Lower snake-case name of setting as defined in meta.392pub name: &'static str,393394/// The description of the setting.395pub description: &'static str,396397/// Offset of byte containing this setting.398pub offset: u32,399400/// Additional details, depending on the kind of setting.401pub detail: Detail,402}403404/// The different kind of settings along with descriptor bits that depend on the kind.405#[derive(Clone, Copy, Hash)]406pub enum Detail {407/// A boolean setting only uses one bit, numbered from LSB.408Bool {409/// 0-7.410bit: u8,411},412413/// A numerical setting uses the whole byte.414Num,415416/// An Enum setting uses a range of enumerators.417Enum {418/// Numerical value of last enumerator, allowing for 1-256 enumerators.419last: u8,420421/// First enumerator in the ENUMERATORS table.422enumerators: u16,423},424425/// A preset is not an individual setting, it is a collection of settings applied at once.426///427/// The `Descriptor::offset` field refers to the `PRESETS` table.428Preset,429}430431impl Detail {432/// Check if a detail is a Detail::Preset. Useful because the Descriptor433/// offset field has a different meaning when the detail is a preset.434pub fn is_preset(self) -> bool {435match self {436Self::Preset => true,437_ => false,438}439}440}441}442443// Include code generated by `meta/gen_settings.rs`. This file contains a public `Flags` struct444// with an implementation for all of the settings defined in445// `cranelift-codegen/meta/src/shared/settings.rs`.446include!(concat!(env!("OUT_DIR"), "/settings.rs"));447448/// Wrapper containing flags and optionally a `TargetIsa` trait object.449///450/// A few passes need to access the flags but only optionally a target ISA. The `FlagsOrIsa`451/// wrapper can be used to pass either, and extract the flags so they are always accessible.452#[derive(Clone, Copy)]453pub struct FlagsOrIsa<'a> {454/// Flags are always present.455pub flags: &'a Flags,456457/// The ISA may not be present.458pub isa: Option<&'a dyn TargetIsa>,459}460461impl<'a> From<&'a Flags> for FlagsOrIsa<'a> {462fn from(flags: &'a Flags) -> FlagsOrIsa<'a> {463FlagsOrIsa { flags, isa: None }464}465}466467impl<'a> From<&'a dyn TargetIsa> for FlagsOrIsa<'a> {468fn from(isa: &'a dyn TargetIsa) -> FlagsOrIsa<'a> {469FlagsOrIsa {470flags: isa.flags(),471isa: Some(isa),472}473}474}475476#[cfg(test)]477mod tests {478use super::Configurable;479use super::SetError::*;480use super::{Flags, builder};481use alloc::string::ToString;482483#[test]484fn display_default() {485let b = builder();486let f = Flags::new(b);487let actual = f.to_string();488let expected = r#"[shared]489regalloc_algorithm = "backtracking"490opt_level = "none"491tls_model = "none"492stack_switch_model = "none"493libcall_call_conv = "isa_default"494probestack_size_log2 = 12495probestack_strategy = "outline"496bb_padding_log2_minus_one = 0497log2_min_function_alignment = 0498regalloc_checker = false499regalloc_verbose_logs = false500enable_alias_analysis = true501enable_verifier = true502enable_pcc = false503is_pic = false504use_colocated_libcalls = false505enable_float = true506enable_nan_canonicalization = false507enable_pinned_reg = false508enable_atomics = true509enable_safepoints = false510enable_llvm_abi_extensions = false511enable_multi_ret_implicit_sret = false512unwind_info = true513preserve_frame_pointers = false514machine_code_cfg_info = false515enable_probestack = false516enable_jump_tables = true517enable_heap_access_spectre_mitigation = true518enable_table_access_spectre_mitigation = true519enable_incremental_compilation_cache_checks = false520"#;521if actual != expected {522panic!(523"Default settings do not match expectations:\n\n{}",524similar::TextDiff::from_lines(expected, &actual)525.unified_diff()526.header("expected", "actual")527);528}529assert_eq!(f.opt_level(), super::OptLevel::None);530}531532#[test]533fn modify_bool() {534let mut b = builder();535assert_eq!(b.enable("not_there"), Err(BadName("not_there".to_string())));536assert_eq!(b.enable("enable_atomics"), Ok(()));537assert_eq!(b.set("enable_atomics", "false"), Ok(()));538539let f = Flags::new(b);540assert_eq!(f.enable_atomics(), false);541}542543#[test]544fn modify_string() {545let mut b = builder();546assert_eq!(547b.set("not_there", "true"),548Err(BadName("not_there".to_string()))549);550assert_eq!(551b.set("enable_atomics", ""),552Err(BadValue("bool".to_string()))553);554assert_eq!(555b.set("enable_atomics", "best"),556Err(BadValue("bool".to_string()))557);558assert_eq!(559b.set("opt_level", "true"),560Err(BadValue(561"any among none, speed, speed_and_size".to_string()562))563);564assert_eq!(b.set("opt_level", "speed"), Ok(()));565assert_eq!(b.set("enable_atomics", "0"), Ok(()));566567let f = Flags::new(b);568assert_eq!(f.enable_atomics(), false);569assert_eq!(f.opt_level(), super::OptLevel::Speed);570}571}572573574