cranelift_codegen_meta/cdsl/
settings.rs

1use std::iter;
2
3#[derive(Clone, Copy, Hash, PartialEq, Eq)]
4pub(crate) struct BoolSettingIndex(usize);
5
6#[derive(Hash, PartialEq, Eq)]
7pub(crate) struct BoolSetting {
8    pub default: bool,
9    pub bit_offset: u8,
10    pub predicate_number: u8,
11}
12
13#[derive(Hash, PartialEq, Eq)]
14pub(crate) enum SpecificSetting {
15    Bool(BoolSetting),
16    Enum(Vec<&'static str>),
17    Num(u8),
18}
19
20#[derive(Hash, PartialEq, Eq)]
21pub(crate) struct Setting {
22    pub name: &'static str,
23    pub description: &'static str,
24    pub comment: &'static str,
25    pub specific: SpecificSetting,
26    pub byte_offset: u8,
27}
28
29impl Setting {
30    pub fn default_byte(&self) -> u8 {
31        match self.specific {
32            SpecificSetting::Bool(BoolSetting {
33                default,
34                bit_offset,
35                ..
36            }) => {
37                if default {
38                    1 << bit_offset
39                } else {
40                    0
41                }
42            }
43            SpecificSetting::Enum(_) => 0,
44            SpecificSetting::Num(default) => default,
45        }
46    }
47
48    fn byte_for_value(&self, v: bool) -> u8 {
49        match self.specific {
50            SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => {
51                if v {
52                    1 << bit_offset
53                } else {
54                    0
55                }
56            }
57            _ => panic!("byte_for_value shouldn't be used for non-boolean settings."),
58        }
59    }
60
61    fn byte_mask(&self) -> u8 {
62        match self.specific {
63            SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => 1 << bit_offset,
64            _ => panic!("byte_for_value shouldn't be used for non-boolean settings."),
65        }
66    }
67}
68
69#[derive(Hash, PartialEq, Eq, Copy, Clone)]
70pub(crate) struct PresetIndex(usize);
71
72#[derive(Hash, PartialEq, Eq)]
73pub(crate) enum PresetType {
74    BoolSetting(BoolSettingIndex),
75    OtherPreset(PresetIndex),
76}
77
78impl From<BoolSettingIndex> for PresetType {
79    fn from(bool_setting_index: BoolSettingIndex) -> Self {
80        PresetType::BoolSetting(bool_setting_index)
81    }
82}
83impl From<PresetIndex> for PresetType {
84    fn from(value: PresetIndex) -> Self {
85        PresetType::OtherPreset(value)
86    }
87}
88
89#[derive(Hash, PartialEq, Eq)]
90pub(crate) struct Preset {
91    pub name: &'static str,
92    pub description: &'static str,
93    values: Vec<BoolSettingIndex>,
94}
95
96impl Preset {
97    pub fn layout(&self, group: &SettingGroup) -> Vec<(u8, u8)> {
98        let mut layout: Vec<(u8, u8)> = iter::repeat((0, 0))
99            .take(group.settings_size as usize)
100            .collect();
101        for bool_index in &self.values {
102            let setting = &group.settings[bool_index.0];
103            let mask = setting.byte_mask();
104            let val = setting.byte_for_value(true);
105            assert!((val & !mask) == 0);
106            let (ref mut l_mask, ref mut l_val) =
107                *layout.get_mut(setting.byte_offset as usize).unwrap();
108            *l_mask |= mask;
109            *l_val = (*l_val & !mask) | val;
110        }
111        layout
112    }
113
114    pub fn setting_names<'a>(
115        &'a self,
116        group: &'a SettingGroup,
117    ) -> impl Iterator<Item = &'static str> + 'a {
118        self.values
119            .iter()
120            .map(|bool_index| group.settings[bool_index.0].name)
121    }
122}
123
124pub(crate) struct SettingGroup {
125    pub name: &'static str,
126    pub settings: Vec<Setting>,
127    pub bool_start_byte_offset: u8,
128    pub settings_size: u8,
129    pub presets: Vec<Preset>,
130}
131
132impl SettingGroup {
133    fn num_bool_settings(&self) -> u8 {
134        self.settings
135            .iter()
136            .filter(|s| matches!(s.specific, SpecificSetting::Bool(_)))
137            .count() as u8
138    }
139
140    pub fn byte_size(&self) -> u8 {
141        self.bool_start_byte_offset + (self.num_bool_settings() + 7) / 8
142    }
143}
144
145/// This is the basic information needed to track the specific parts of a setting when building
146/// them.
147pub(crate) enum ProtoSpecificSetting {
148    Bool(bool),
149    Enum(Vec<&'static str>),
150    Num(u8),
151}
152
153/// This is the information provided during building for a setting.
154struct ProtoSetting {
155    name: &'static str,
156    description: &'static str,
157    comment: &'static str,
158    specific: ProtoSpecificSetting,
159}
160
161pub(crate) struct SettingGroupBuilder {
162    name: &'static str,
163    settings: Vec<ProtoSetting>,
164    presets: Vec<Preset>,
165}
166
167impl SettingGroupBuilder {
168    pub fn new(name: &'static str) -> Self {
169        Self {
170            name,
171            settings: Vec::new(),
172            presets: Vec::new(),
173        }
174    }
175
176    fn add_setting(
177        &mut self,
178        name: &'static str,
179        description: &'static str,
180        comment: &'static str,
181        specific: ProtoSpecificSetting,
182    ) {
183        self.settings.push(ProtoSetting {
184            name,
185            description,
186            comment,
187            specific,
188        })
189    }
190
191    pub fn add_bool(
192        &mut self,
193        name: &'static str,
194        description: &'static str,
195        comment: &'static str,
196        default: bool,
197    ) -> BoolSettingIndex {
198        self.add_setting(
199            name,
200            description,
201            comment,
202            ProtoSpecificSetting::Bool(default),
203        );
204        BoolSettingIndex(self.settings.len() - 1)
205    }
206
207    pub fn add_enum(
208        &mut self,
209        name: &'static str,
210        description: &'static str,
211        comment: &'static str,
212        values: Vec<&'static str>,
213    ) {
214        self.add_setting(
215            name,
216            description,
217            comment,
218            ProtoSpecificSetting::Enum(values),
219        );
220    }
221
222    pub fn add_num(
223        &mut self,
224        name: &'static str,
225        description: &'static str,
226        comment: &'static str,
227        default: u8,
228    ) {
229        self.add_setting(
230            name,
231            description,
232            comment,
233            ProtoSpecificSetting::Num(default),
234        );
235    }
236
237    pub fn add_preset(
238        &mut self,
239        name: &'static str,
240        description: &'static str,
241        args: Vec<PresetType>,
242    ) -> PresetIndex {
243        let mut values = Vec::new();
244        for arg in args {
245            match arg {
246                PresetType::OtherPreset(index) => {
247                    values.extend(self.presets[index.0].values.iter());
248                }
249                PresetType::BoolSetting(index) => values.push(index),
250            }
251        }
252        self.presets.push(Preset {
253            name,
254            description,
255            values,
256        });
257        PresetIndex(self.presets.len() - 1)
258    }
259
260    /// Compute the layout of the byte vector used to represent this settings
261    /// group.
262    ///
263    /// The byte vector contains the following entries in order:
264    ///
265    /// 1. Byte-sized settings like `NumSetting` and `EnumSetting`.
266    /// 2. `BoolSetting` settings.
267    ///
268    /// Set `self.settings_size` to the length of the byte vector prefix that
269    /// contains the settings. All bytes after that are computed, not
270    /// configured.
271    ///
272    /// Set `self.boolean_offset` to the beginning of the numbered predicates,
273    /// 2. in the list above.
274    ///
275    /// Assign `byte_offset` and `bit_offset` fields in all settings.
276    pub fn build(self) -> SettingGroup {
277        let mut group = SettingGroup {
278            name: self.name,
279            settings: Vec::new(),
280            bool_start_byte_offset: 0,
281            settings_size: 0,
282            presets: Vec::new(),
283        };
284
285        let mut byte_offset = 0;
286
287        // Assign the non-boolean settings first.
288        for s in &self.settings {
289            let specific = match s.specific {
290                ProtoSpecificSetting::Bool(..) => continue,
291                ProtoSpecificSetting::Enum(ref values) => SpecificSetting::Enum(values.clone()),
292                ProtoSpecificSetting::Num(default) => SpecificSetting::Num(default),
293            };
294
295            group.settings.push(Setting {
296                name: s.name,
297                description: s.description,
298                comment: s.comment,
299                byte_offset,
300                specific,
301            });
302
303            byte_offset += 1;
304        }
305
306        group.bool_start_byte_offset = byte_offset;
307
308        let mut predicate_number = 0;
309
310        // Then the boolean settings.
311        for s in &self.settings {
312            let default = match s.specific {
313                ProtoSpecificSetting::Bool(default) => default,
314                ProtoSpecificSetting::Enum(_) | ProtoSpecificSetting::Num(_) => continue,
315            };
316            group.settings.push(Setting {
317                name: s.name,
318                description: s.description,
319                comment: s.comment,
320                byte_offset: byte_offset + predicate_number / 8,
321                specific: SpecificSetting::Bool(BoolSetting {
322                    default,
323                    bit_offset: predicate_number % 8,
324                    predicate_number,
325                }),
326            });
327            predicate_number += 1;
328        }
329
330        group.settings_size = group.byte_size();
331
332        group.presets.extend(self.presets);
333
334        group
335    }
336}