1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
//! Support types for generated encoding tables.
//!
//! This module contains types and functions for working with the encoding tables generated by
//! `cranelift-codegen/meta/src/gen_encodings.rs`.

use crate::constant_hash::{probe, Table};
use crate::ir::{Function, InstructionData, Opcode, Type};
use crate::isa::{Encoding, Legalize};
use crate::settings::PredicateView;
use core::ops::Range;

/// A recipe predicate.
///
/// This is a predicate function capable of testing ISA and instruction predicates simultaneously.
///
/// A None predicate is always satisfied.
pub type RecipePredicate = Option<fn(PredicateView, &InstructionData) -> bool>;

/// An instruction predicate.
///
/// This is a predicate function that needs to be tested in addition to the recipe predicate. It
/// can't depend on ISA settings.
pub type InstPredicate = fn(&Function, &InstructionData) -> bool;

/// Legalization action to perform when no encoding can be found for an instruction.
///
/// This is an index into an ISA-specific table of legalization actions.
pub type LegalizeCode = u8;

/// Level 1 hash table entry.
///
/// One level 1 hash table is generated per CPU mode. This table is keyed by the controlling type
/// variable, using `INVALID` for non-polymorphic instructions.
///
/// The hash table values are references to level 2 hash tables, encoded as an offset in `LEVEL2`
/// where the table begins, and the binary logarithm of its length. All the level 2 hash tables
/// have a power-of-two size.
///
/// Entries are generic over the offset type. It will typically be `u32` or `u16`, depending on the
/// size of the `LEVEL2` table.
///
/// Empty entries are encoded with a `!0` value for `log2len` which will always be out of range.
/// Entries that have a `legalize` value but no level 2 table have an `offset` field that is out of
/// bounds.
pub struct Level1Entry<OffT: Into<u32> + Copy> {
    pub ty: Type,
    pub log2len: u8,
    pub legalize: LegalizeCode,
    pub offset: OffT,
}

impl<OffT: Into<u32> + Copy> Level1Entry<OffT> {
    /// Get the level 2 table range indicated by this entry.
    fn range(&self) -> Range<usize> {
        let b = self.offset.into() as usize;
        b..b + (1 << self.log2len)
    }
}

impl<OffT: Into<u32> + Copy> Table<Type> for [Level1Entry<OffT>] {
    fn len(&self) -> usize {
        self.len()
    }

    fn key(&self, idx: usize) -> Option<Type> {
        if self[idx].log2len != !0 {
            Some(self[idx].ty)
        } else {
            None
        }
    }
}

/// Level 2 hash table entry.
///
/// The second level hash tables are keyed by `Opcode`, and contain an offset into the `ENCLISTS`
/// table where the encoding recipes for the instruction are stored.
///
/// Entries are generic over the offset type which depends on the size of `ENCLISTS`. A `u16`
/// offset allows the entries to be only 32 bits each. There is no benefit to dropping down to `u8`
/// for tiny ISAs. The entries won't shrink below 32 bits since the opcode is expected to be 16
/// bits.
///
/// Empty entries are encoded with a `NotAnOpcode` `opcode` field.
pub struct Level2Entry<OffT: Into<u32> + Copy> {
    pub opcode: Option<Opcode>,
    pub offset: OffT,
}

impl<OffT: Into<u32> + Copy> Table<Opcode> for [Level2Entry<OffT>] {
    fn len(&self) -> usize {
        self.len()
    }

    fn key(&self, idx: usize) -> Option<Opcode> {
        self[idx].opcode
    }
}

/// Two-level hash table lookup and iterator construction.
///
/// Given the controlling type variable and instruction opcode, find the corresponding encoding
/// list.
///
/// Returns an iterator that produces legal encodings for `inst`.
pub fn lookup_enclist<'a, OffT1, OffT2>(
    ctrl_typevar: Type,
    inst: &'a InstructionData,
    func: &'a Function,
    level1_table: &'static [Level1Entry<OffT1>],
    level2_table: &'static [Level2Entry<OffT2>],
    enclist: &'static [EncListEntry],
    legalize_actions: &'static [Legalize],
    recipe_preds: &'static [RecipePredicate],
    inst_preds: &'static [InstPredicate],
    isa_preds: PredicateView<'a>,
) -> Encodings<'a>
where
    OffT1: Into<u32> + Copy,
    OffT2: Into<u32> + Copy,
{
    let (offset, legalize) = match probe(level1_table, ctrl_typevar, ctrl_typevar.index()) {
        Err(l1idx) => {
            // No level 1 entry found for the type.
            // We have a sentinel entry with the default legalization code.
            (!0, level1_table[l1idx].legalize)
        }
        Ok(l1idx) => {
            // We have a valid level 1 entry for this type.
            let l1ent = &level1_table[l1idx];
            let offset = match level2_table.get(l1ent.range()) {
                Some(l2tab) => {
                    let opcode = inst.opcode();
                    match probe(l2tab, opcode, opcode as usize) {
                        Ok(l2idx) => l2tab[l2idx].offset.into() as usize,
                        Err(_) => !0,
                    }
                }
                // The l1ent range is invalid. This means that we just have a customized
                // legalization code for this type. The level 2 table is empty.
                None => !0,
            };
            (offset, l1ent.legalize)
        }
    };

    // Now we have an offset into `enclist` that is `!0` when no encoding list could be found.
    // The default legalization code is always valid.
    Encodings::new(
        offset,
        legalize,
        inst,
        func,
        enclist,
        legalize_actions,
        recipe_preds,
        inst_preds,
        isa_preds,
    )
}

/// Encoding list entry.
///
/// Encoding lists are represented as sequences of u16 words.
pub type EncListEntry = u16;

/// Number of bits used to represent a predicate. c.f. `meta/src/gen_encodings.rs`.
const PRED_BITS: u8 = 12;
const PRED_MASK: usize = (1 << PRED_BITS) - 1;
/// First code word representing a predicate check. c.f. `meta/src/gen_encodings.rs`.
const PRED_START: usize = 0x1000;

/// An iterator over legal encodings for the instruction.
pub struct Encodings<'a> {
    // Current offset into `enclist`, or out of bounds after we've reached the end.
    offset: usize,
    // Legalization code to use of no encoding is found.
    legalize: LegalizeCode,
    inst: &'a InstructionData,
    func: &'a Function,
    enclist: &'static [EncListEntry],
    legalize_actions: &'static [Legalize],
    recipe_preds: &'static [RecipePredicate],
    inst_preds: &'static [InstPredicate],
    isa_preds: PredicateView<'a>,
}

impl<'a> Encodings<'a> {
    /// Creates a new instance of `Encodings`.
    ///
    /// This iterator provides search for encodings that applies to the given instruction. The
    /// encoding lists are laid out such that first call to `next` returns valid entry in the list
    /// or `None`.
    pub fn new(
        offset: usize,
        legalize: LegalizeCode,
        inst: &'a InstructionData,
        func: &'a Function,
        enclist: &'static [EncListEntry],
        legalize_actions: &'static [Legalize],
        recipe_preds: &'static [RecipePredicate],
        inst_preds: &'static [InstPredicate],
        isa_preds: PredicateView<'a>,
    ) -> Self {
        Encodings {
            offset,
            inst,
            func,
            legalize,
            isa_preds,
            recipe_preds,
            inst_preds,
            enclist,
            legalize_actions,
        }
    }

    /// Get the legalization action that caused the enumeration of encodings to stop.
    /// This can be the default legalization action for the type or a custom code for the
    /// instruction.
    ///
    /// This method must only be called after the iterator returns `None`.
    pub fn legalize(&self) -> Legalize {
        debug_assert_eq!(self.offset, !0, "Premature Encodings::legalize()");
        self.legalize_actions[self.legalize as usize]
    }

    /// Check if the `rpred` recipe predicate is satisfied.
    fn check_recipe(&self, rpred: RecipePredicate) -> bool {
        match rpred {
            Some(p) => p(self.isa_preds, self.inst),
            None => true,
        }
    }

    /// Check an instruction or isa predicate.
    fn check_pred(&self, pred: usize) -> bool {
        if let Some(&p) = self.inst_preds.get(pred) {
            p(self.func, self.inst)
        } else {
            let pred = pred - self.inst_preds.len();
            self.isa_preds.test(pred)
        }
    }
}

impl<'a> Iterator for Encodings<'a> {
    type Item = Encoding;

    fn next(&mut self) -> Option<Encoding> {
        while let Some(entryref) = self.enclist.get(self.offset) {
            let entry = *entryref as usize;

            // Check for "recipe+bits".
            let recipe = entry >> 1;
            if let Some(&rpred) = self.recipe_preds.get(recipe) {
                let bits = self.offset + 1;
                if entry & 1 == 0 {
                    self.offset += 2; // Next entry.
                } else {
                    self.offset = !0; // Stop.
                }
                if self.check_recipe(rpred) {
                    return Some(Encoding::new(recipe as u16, self.enclist[bits]));
                }
                continue;
            }

            // Check for "stop with legalize".
            if entry < PRED_START {
                self.legalize = (entry - 2 * self.recipe_preds.len()) as LegalizeCode;
                self.offset = !0; // Stop.
                return None;
            }

            // Finally, this must be a predicate entry.
            let pred_entry = entry - PRED_START;
            let skip = pred_entry >> PRED_BITS;
            let pred = pred_entry & PRED_MASK;

            if self.check_pred(pred) {
                self.offset += 1;
            } else if skip == 0 {
                self.offset = !0; // Stop.
                return None;
            } else {
                self.offset += 1 + skip;
            }
        }
        None
    }
}