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    pub predicates: Vec<Predicate>,
131}
132
133impl SettingGroup {
134    fn num_bool_settings(&self) -> u8 {
135        self.settings
136            .iter()
137            .filter(|s| matches!(s.specific, SpecificSetting::Bool(_)))
138            .count() as u8
139    }
140
141    pub fn byte_size(&self) -> u8 {
142        let num_predicates = self.num_bool_settings() + (self.predicates.len() as u8);
143        self.bool_start_byte_offset + (num_predicates + 7) / 8
144    }
145}
146
147/// This is the basic information needed to track the specific parts of a setting when building
148/// them.
149pub(crate) enum ProtoSpecificSetting {
150    Bool(bool),
151    Enum(Vec<&'static str>),
152    Num(u8),
153}
154
155/// This is the information provided during building for a setting.
156struct ProtoSetting {
157    name: &'static str,
158    description: &'static str,
159    comment: &'static str,
160    specific: ProtoSpecificSetting,
161}
162
163#[derive(Hash, PartialEq, Eq)]
164pub(crate) enum PredicateNode {
165    OwnedBool(BoolSettingIndex),
166    SharedBool(&'static str, &'static str),
167    And(Box<PredicateNode>, Box<PredicateNode>),
168}
169
170impl From<BoolSettingIndex> for PredicateNode {
171    fn from(bool_setting_index: BoolSettingIndex) -> Self {
172        PredicateNode::OwnedBool(bool_setting_index)
173    }
174}
175
176impl<'a> From<(BoolSettingIndex, &'a SettingGroup)> for PredicateNode {
177    fn from(val: (BoolSettingIndex, &'a SettingGroup)) -> Self {
178        let (index, group) = (val.0, val.1);
179        let setting = &group.settings[index.0];
180        PredicateNode::SharedBool(group.name, setting.name)
181    }
182}
183
184impl PredicateNode {
185    fn render(&self, group: &SettingGroup) -> String {
186        match *self {
187            PredicateNode::OwnedBool(bool_setting_index) => format!(
188                "{}.{}()",
189                group.name, group.settings[bool_setting_index.0].name
190            ),
191            PredicateNode::SharedBool(ref group_name, ref bool_name) => {
192                format!("{group_name}.{bool_name}()")
193            }
194            PredicateNode::And(ref lhs, ref rhs) => {
195                format!("{} && {}", lhs.render(group), rhs.render(group))
196            }
197        }
198    }
199}
200
201struct ProtoPredicate {
202    pub name: &'static str,
203    node: PredicateNode,
204}
205
206pub(crate) type SettingPredicateNumber = u8;
207
208pub(crate) struct Predicate {
209    pub name: &'static str,
210    node: PredicateNode,
211    pub number: SettingPredicateNumber,
212}
213
214impl Predicate {
215    pub fn render(&self, group: &SettingGroup) -> String {
216        self.node.render(group)
217    }
218}
219
220pub(crate) struct SettingGroupBuilder {
221    name: &'static str,
222    settings: Vec<ProtoSetting>,
223    presets: Vec<Preset>,
224    predicates: Vec<ProtoPredicate>,
225}
226
227impl SettingGroupBuilder {
228    pub fn new(name: &'static str) -> Self {
229        Self {
230            name,
231            settings: Vec::new(),
232            presets: Vec::new(),
233            predicates: Vec::new(),
234        }
235    }
236
237    fn add_setting(
238        &mut self,
239        name: &'static str,
240        description: &'static str,
241        comment: &'static str,
242        specific: ProtoSpecificSetting,
243    ) {
244        self.settings.push(ProtoSetting {
245            name,
246            description,
247            comment,
248            specific,
249        })
250    }
251
252    pub fn add_bool(
253        &mut self,
254        name: &'static str,
255        description: &'static str,
256        comment: &'static str,
257        default: bool,
258    ) -> BoolSettingIndex {
259        assert!(
260            self.predicates.is_empty(),
261            "predicates must be added after the boolean settings"
262        );
263        self.add_setting(
264            name,
265            description,
266            comment,
267            ProtoSpecificSetting::Bool(default),
268        );
269        BoolSettingIndex(self.settings.len() - 1)
270    }
271
272    pub fn add_enum(
273        &mut self,
274        name: &'static str,
275        description: &'static str,
276        comment: &'static str,
277        values: Vec<&'static str>,
278    ) {
279        self.add_setting(
280            name,
281            description,
282            comment,
283            ProtoSpecificSetting::Enum(values),
284        );
285    }
286
287    pub fn add_num(
288        &mut self,
289        name: &'static str,
290        description: &'static str,
291        comment: &'static str,
292        default: u8,
293    ) {
294        self.add_setting(
295            name,
296            description,
297            comment,
298            ProtoSpecificSetting::Num(default),
299        );
300    }
301
302    pub fn add_predicate(&mut self, name: &'static str, node: PredicateNode) {
303        self.predicates.push(ProtoPredicate { name, node });
304    }
305
306    pub fn add_preset(
307        &mut self,
308        name: &'static str,
309        description: &'static str,
310        args: Vec<PresetType>,
311    ) -> PresetIndex {
312        let mut values = Vec::new();
313        for arg in args {
314            match arg {
315                PresetType::OtherPreset(index) => {
316                    values.extend(self.presets[index.0].values.iter());
317                }
318                PresetType::BoolSetting(index) => values.push(index),
319            }
320        }
321        self.presets.push(Preset {
322            name,
323            description,
324            values,
325        });
326        PresetIndex(self.presets.len() - 1)
327    }
328
329    /// Compute the layout of the byte vector used to represent this settings
330    /// group.
331    ///
332    /// The byte vector contains the following entries in order:
333    ///
334    /// 1. Byte-sized settings like `NumSetting` and `EnumSetting`.
335    /// 2. `BoolSetting` settings.
336    /// 3. Precomputed named predicates.
337    /// 4. Other numbered predicates, including parent predicates that need to be accessible by
338    ///    number.
339    ///
340    /// Set `self.settings_size` to the length of the byte vector prefix that
341    /// contains the settings. All bytes after that are computed, not
342    /// configured.
343    ///
344    /// Set `self.boolean_offset` to the beginning of the numbered predicates,
345    /// 2. in the list above.
346    ///
347    /// Assign `byte_offset` and `bit_offset` fields in all settings.
348    pub fn build(self) -> SettingGroup {
349        let mut group = SettingGroup {
350            name: self.name,
351            settings: Vec::new(),
352            bool_start_byte_offset: 0,
353            settings_size: 0,
354            presets: Vec::new(),
355            predicates: Vec::new(),
356        };
357
358        let mut byte_offset = 0;
359
360        // Assign the non-boolean settings first.
361        for s in &self.settings {
362            let specific = match s.specific {
363                ProtoSpecificSetting::Bool(..) => continue,
364                ProtoSpecificSetting::Enum(ref values) => SpecificSetting::Enum(values.clone()),
365                ProtoSpecificSetting::Num(default) => SpecificSetting::Num(default),
366            };
367
368            group.settings.push(Setting {
369                name: s.name,
370                description: s.description,
371                comment: s.comment,
372                byte_offset,
373                specific,
374            });
375
376            byte_offset += 1;
377        }
378
379        group.bool_start_byte_offset = byte_offset;
380
381        let mut predicate_number = 0;
382
383        // Then the boolean settings.
384        for s in &self.settings {
385            let default = match s.specific {
386                ProtoSpecificSetting::Bool(default) => default,
387                ProtoSpecificSetting::Enum(_) | ProtoSpecificSetting::Num(_) => continue,
388            };
389            group.settings.push(Setting {
390                name: s.name,
391                description: s.description,
392                comment: s.comment,
393                byte_offset: byte_offset + predicate_number / 8,
394                specific: SpecificSetting::Bool(BoolSetting {
395                    default,
396                    bit_offset: predicate_number % 8,
397                    predicate_number,
398                }),
399            });
400            predicate_number += 1;
401        }
402
403        assert!(
404            group.predicates.is_empty(),
405            "settings_size is the byte size before adding predicates"
406        );
407        group.settings_size = group.byte_size();
408
409        // Sort predicates by name to ensure the same order as the Python code.
410        let mut predicates = self.predicates;
411        predicates.sort_by_key(|predicate| predicate.name);
412
413        group
414            .predicates
415            .extend(predicates.into_iter().map(|predicate| {
416                let number = predicate_number;
417                predicate_number += 1;
418                Predicate {
419                    name: predicate.name,
420                    node: predicate.node,
421                    number,
422                }
423            }));
424
425        group.presets.extend(self.presets);
426
427        group
428    }
429}