Add support for legalization codes in the encoding tables.

The new encoding format allows entries that mean "stop with this
legalization code" which makes it possible to configure legalization
actions per instruction, instead of only per controlling type variable.

This patch adds the Rust side of the legalization codes:

- Add an `Encodings::legalize()` method on the encoding iterator which
  can be called after the iterator has returned `None`. The returned
  code is either the default legalization action for the type, or a
  specific code encountered in the encoding list.

- Change `lookup_enclist` to return a full iterator instead of just an
  offset. The two-phase lookup can bail at multiple points, each time
  with a default legalization code from the level 1 table. This default
  legalization code is stored in the returned iterator.

- Change all the implementations of legal_encodings() in the ISA
  implementations.

This change means that we don't need to return a Result any longer. The
`Encodings` iterator can be empty with an associated legalization code.
This commit is contained in:
Jakob Stoklund Olesen
2017-07-25 16:33:35 -07:00
parent 5a2bb8ba32
commit 637966dc7f
6 changed files with 116 additions and 97 deletions

View File

@@ -10,7 +10,7 @@ use binemit::{CodeSink, MemoryCodeSink, emit_function};
use super::super::settings as shared_settings; use super::super::settings as shared_settings;
use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, Encodings}; use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, Encodings};
use isa::Builder as IsaBuilder; use isa::Builder as IsaBuilder;
use isa::{TargetIsa, RegInfo, RegClass, EncInfo, Legalize}; use isa::{TargetIsa, RegInfo, RegClass, EncInfo};
use ir; use ir;
use regalloc; use regalloc;
@@ -62,22 +62,19 @@ impl TargetIsa for Isa {
} }
fn legal_encodings<'a>(&'a self, fn legal_encodings<'a>(&'a self,
_dfg: &'a ir::DataFlowGraph, dfg: &'a ir::DataFlowGraph,
inst: &'a ir::InstructionData, inst: &'a ir::InstructionData,
ctrl_typevar: ir::Type) ctrl_typevar: ir::Type)
-> Result<Encodings<'a>, Legalize> { -> Encodings<'a> {
lookup_enclist(ctrl_typevar, lookup_enclist(ctrl_typevar,
inst.opcode(), inst,
dfg,
self.cpumode, self.cpumode,
&enc_tables::LEVEL2[..]) &enc_tables::LEVEL2[..],
.and_then(|enclist_offset| { &enc_tables::ENCLISTS[..],
Ok(Encodings::new(enclist_offset, &enc_tables::RECIPE_PREDICATES[..],
&enc_tables::ENCLISTS[..], &enc_tables::INST_PREDICATES[..],
&enc_tables::RECIPE_PREDICATES[..], self.isa_flags.predicate_view())
&enc_tables::INST_PREDICATES[..],
inst,
self.isa_flags.predicate_view()))
})
} }
fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) { fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) {

View File

@@ -10,7 +10,7 @@ use binemit::{CodeSink, MemoryCodeSink, emit_function};
use super::super::settings as shared_settings; use super::super::settings as shared_settings;
use isa::enc_tables::{lookup_enclist, Encodings}; use isa::enc_tables::{lookup_enclist, Encodings};
use isa::Builder as IsaBuilder; use isa::Builder as IsaBuilder;
use isa::{TargetIsa, RegInfo, RegClass, EncInfo, Legalize}; use isa::{TargetIsa, RegInfo, RegClass, EncInfo};
use ir; use ir;
use regalloc; use regalloc;
@@ -55,22 +55,19 @@ impl TargetIsa for Isa {
} }
fn legal_encodings<'a>(&'a self, fn legal_encodings<'a>(&'a self,
_dfg: &'a ir::DataFlowGraph, dfg: &'a ir::DataFlowGraph,
inst: &'a ir::InstructionData, inst: &'a ir::InstructionData,
ctrl_typevar: ir::Type) ctrl_typevar: ir::Type)
-> Result<Encodings<'a>, Legalize> { -> Encodings<'a> {
lookup_enclist(ctrl_typevar, lookup_enclist(ctrl_typevar,
inst.opcode(), inst,
dfg,
&enc_tables::LEVEL1_A64[..], &enc_tables::LEVEL1_A64[..],
&enc_tables::LEVEL2[..]) &enc_tables::LEVEL2[..],
.and_then(|enclist_offset| { &enc_tables::ENCLISTS[..],
Ok(Encodings::new(enclist_offset, &enc_tables::RECIPE_PREDICATES[..],
&enc_tables::ENCLISTS[..], &enc_tables::INST_PREDICATES[..],
&enc_tables::RECIPE_PREDICATES[..], self.isa_flags.predicate_view())
&enc_tables::INST_PREDICATES[..],
inst,
self.isa_flags.predicate_view()))
})
} }
fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) { fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) {

View File

@@ -4,8 +4,8 @@
//! `lib/cretonne/meta/gen_encoding.py`. //! `lib/cretonne/meta/gen_encoding.py`.
use constant_hash::{Table, probe}; use constant_hash::{Table, probe};
use ir::{Type, Opcode, InstructionData}; use ir::{Type, Opcode, DataFlowGraph, InstructionData};
use isa::{Encoding, Legalize}; use isa::Encoding;
use settings::PredicateView; use settings::PredicateView;
use std::ops::Range; use std::ops::Range;
@@ -97,45 +97,59 @@ impl<OffT: Into<u32> + Copy> Table<Opcode> for [Level2Entry<OffT>] {
} }
} }
/// Two-level hash table lookup. /// Two-level hash table lookup and iterator construction.
/// ///
/// Given the controlling type variable and instruction opcode, find the corresponding encoding /// Given the controlling type variable and instruction opcode, find the corresponding encoding
/// list. /// list.
/// ///
/// Returns an offset into the ISA's `ENCLIST` table, or `None` if the opcode/type combination is /// Returns an iterator that produces legal encodings for `inst`.
/// not legal. pub fn lookup_enclist<'a, OffT1, OffT2>(ctrl_typevar: Type,
pub fn lookup_enclist<OffT1, OffT2>(ctrl_typevar: Type, inst: &'a InstructionData,
opcode: Opcode, _dfg: &'a DataFlowGraph,
level1_table: &[Level1Entry<OffT1>], level1_table: &'static [Level1Entry<OffT1>],
level2_table: &[Level2Entry<OffT2>]) level2_table: &'static [Level2Entry<OffT2>],
-> Result<usize, Legalize> enclist: &'static [EncListEntry],
recipe_preds: &'static [RecipePredicate],
inst_preds: &'static [InstPredicate],
isa_preds: PredicateView<'a>)
-> Encodings<'a>
where OffT1: Into<u32> + Copy, where OffT1: Into<u32> + Copy,
OffT2: Into<u32> + Copy OffT2: Into<u32> + Copy
{ {
match probe(level1_table, ctrl_typevar, ctrl_typevar.index()) { let (offset, legalize) = match probe(level1_table, ctrl_typevar, ctrl_typevar.index()) {
Err(l1idx) => { Err(l1idx) => {
// No level 1 entry found for the type. // No level 1 entry found for the type.
// We have a sentinel entry with the default legalization code. // We have a sentinel entry with the default legalization code.
let l1ent = &level1_table[l1idx]; (!0, level1_table[l1idx].legalize)
Err(l1ent.legalize.into())
} }
Ok(l1idx) => { Ok(l1idx) => {
// We have a valid level 1 entry for this type. // We have a valid level 1 entry for this type.
let l1ent = &level1_table[l1idx]; let l1ent = &level1_table[l1idx];
match level2_table.get(l1ent.range()) { let offset = match level2_table.get(l1ent.range()) {
Some(l2tab) => { Some(l2tab) => {
probe(l2tab, opcode, opcode as usize) let opcode = inst.opcode();
.map(|l2idx| l2tab[l2idx].offset.into() as usize) match probe(l2tab, opcode, opcode as usize) {
.map_err(|_| l1ent.legalize.into()) Ok(l2idx) => l2tab[l2idx].offset.into() as usize,
Err(_) => !0,
}
} }
None => { // The l1ent range is invalid. This means that we just have a customized
// The l1ent range is invalid. This means that we just have a customized // legalization code for this type. The level 2 table is empty.
// legalization code for this type. The level 2 table is empty. None => !0,
Err(l1ent.legalize.into()) };
} (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,
enclist,
recipe_preds,
inst_preds,
isa_preds)
} }
/// Encoding list entry. /// Encoding list entry.
@@ -153,11 +167,13 @@ const PRED_START: usize = 0x1000;
pub struct Encodings<'a> { pub struct Encodings<'a> {
// Current offset into `enclist`, or out of bounds after we've reached the end. // Current offset into `enclist`, or out of bounds after we've reached the end.
offset: usize, offset: usize,
// Legalization code to use of no encoding is found.
legalize: LegalizeCode,
inst: &'a InstructionData, inst: &'a InstructionData,
isa_predicates: PredicateView<'a>,
enclist: &'static [EncListEntry], enclist: &'static [EncListEntry],
recipe_predicates: &'static [RecipePredicate], recipe_preds: &'static [RecipePredicate],
inst_predicates: &'static [InstPredicate], inst_preds: &'static [InstPredicate],
isa_preds: PredicateView<'a>,
} }
impl<'a> Encodings<'a> { impl<'a> Encodings<'a> {
@@ -167,37 +183,49 @@ impl<'a> Encodings<'a> {
/// encoding lists are laid out such that first call to `next` returns valid entry in the list /// encoding lists are laid out such that first call to `next` returns valid entry in the list
/// or `None`. /// or `None`.
pub fn new(offset: usize, pub fn new(offset: usize,
enclist: &'static [EncListEntry], legalize: LegalizeCode,
recipe_predicates: &'static [RecipePredicate],
inst_predicates: &'static [InstPredicate],
inst: &'a InstructionData, inst: &'a InstructionData,
isa_predicates: PredicateView<'a>) enclist: &'static [EncListEntry],
recipe_preds: &'static [RecipePredicate],
inst_preds: &'static [InstPredicate],
isa_preds: PredicateView<'a>)
-> Self { -> Self {
Encodings { Encodings {
offset, offset,
enclist,
inst, inst,
isa_predicates, legalize,
recipe_predicates, isa_preds,
inst_predicates, recipe_preds,
inst_preds,
enclist,
} }
} }
/// 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) -> LegalizeCode {
debug_assert_eq!(self.offset, !0, "Premature Encodings::legalize()");
self.legalize
}
/// Check if the `rpred` recipe predicate s satisfied. /// Check if the `rpred` recipe predicate s satisfied.
fn check_recipe(&self, rpred: RecipePredicate) -> bool { fn check_recipe(&self, rpred: RecipePredicate) -> bool {
match rpred { match rpred {
Some(p) => p(self.isa_predicates, self.inst), Some(p) => p(self.isa_preds, self.inst),
None => true, None => true,
} }
} }
/// Check an instruction or isa predicate. /// Check an instruction or isa predicate.
fn check_pred(&self, pred: usize) -> bool { fn check_pred(&self, pred: usize) -> bool {
if let Some(&p) = self.inst_predicates.get(pred) { if let Some(&p) = self.inst_preds.get(pred) {
p(self.inst) p(self.inst)
} else { } else {
let pred = pred - self.inst_predicates.len(); let pred = pred - self.inst_preds.len();
self.isa_predicates.test(pred) self.isa_preds.test(pred)
} }
} }
} }
@@ -211,7 +239,7 @@ impl<'a> Iterator for Encodings<'a> {
// Check for "recipe+bits". // Check for "recipe+bits".
let recipe = entry >> 1; let recipe = entry >> 1;
if let Some(&rpred) = self.recipe_predicates.get(recipe) { if let Some(&rpred) = self.recipe_preds.get(recipe) {
let bits = self.offset + 1; let bits = self.offset + 1;
if entry & 1 == 0 { if entry & 1 == 0 {
self.offset += 2; // Next entry. self.offset += 2; // Next entry.
@@ -226,7 +254,9 @@ impl<'a> Iterator for Encodings<'a> {
// Check for "stop with legalize". // Check for "stop with legalize".
if entry < PRED_START { if entry < PRED_START {
unimplemented!(); self.legalize = (entry - 2 * self.recipe_preds.len()) as LegalizeCode;
self.offset = !0; // Stop.
return None;
} }
// Finally, this must be a predicate entry. // Finally, this must be a predicate entry.
@@ -237,7 +267,8 @@ impl<'a> Iterator for Encodings<'a> {
if self.check_pred(pred) { if self.check_pred(pred) {
self.offset += 1; self.offset += 1;
} else if skip == 0 { } else if skip == 0 {
self.offset = !0 // This means stop. self.offset = !0; // Stop.
return None;
} else { } else {
self.offset += 1 + skip; self.offset += 1 + skip;
} }

View File

@@ -10,7 +10,7 @@ use binemit::{CodeSink, MemoryCodeSink, emit_function};
use super::super::settings as shared_settings; use super::super::settings as shared_settings;
use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, Encodings}; use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, Encodings};
use isa::Builder as IsaBuilder; use isa::Builder as IsaBuilder;
use isa::{TargetIsa, RegInfo, RegClass, EncInfo, Legalize}; use isa::{TargetIsa, RegInfo, RegClass, EncInfo};
use ir; use ir;
use regalloc; use regalloc;
@@ -62,22 +62,19 @@ impl TargetIsa for Isa {
} }
fn legal_encodings<'a>(&'a self, fn legal_encodings<'a>(&'a self,
_dfg: &'a ir::DataFlowGraph, dfg: &'a ir::DataFlowGraph,
inst: &'a ir::InstructionData, inst: &'a ir::InstructionData,
ctrl_typevar: ir::Type) ctrl_typevar: ir::Type)
-> Result<Encodings<'a>, Legalize> { -> Encodings<'a> {
lookup_enclist(ctrl_typevar, lookup_enclist(ctrl_typevar,
inst.opcode(), inst,
dfg,
self.cpumode, self.cpumode,
&enc_tables::LEVEL2[..]) &enc_tables::LEVEL2[..],
.and_then(|enclist_offset| { &enc_tables::ENCLISTS[..],
Ok(Encodings::new(enclist_offset, &enc_tables::RECIPE_PREDICATES[..],
&enc_tables::ENCLISTS[..], &enc_tables::INST_PREDICATES[..],
&enc_tables::RECIPE_PREDICATES[..], self.isa_flags.predicate_view())
&enc_tables::INST_PREDICATES[..],
inst,
self.isa_flags.predicate_view()))
})
} }
fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) { fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) {

View File

@@ -156,7 +156,7 @@ pub trait TargetIsa {
dfg: &'a ir::DataFlowGraph, dfg: &'a ir::DataFlowGraph,
inst: &'a ir::InstructionData, inst: &'a ir::InstructionData,
ctrl_typevar: ir::Type) ctrl_typevar: ir::Type)
-> Result<Encodings<'a>, Legalize>; -> Encodings<'a>;
/// Encode an instruction after determining it is legal. /// Encode an instruction after determining it is legal.
/// ///
@@ -169,8 +169,8 @@ pub trait TargetIsa {
inst: &ir::InstructionData, inst: &ir::InstructionData,
ctrl_typevar: ir::Type) ctrl_typevar: ir::Type)
-> Result<Encoding, Legalize> { -> Result<Encoding, Legalize> {
self.legal_encodings(dfg, inst, ctrl_typevar) let mut iter = self.legal_encodings(dfg, inst, ctrl_typevar);
.and_then(|mut iter| iter.next().ok_or(Legalize::Expand)) iter.next().ok_or(iter.legalize().into())
} }
/// Get a data structure describing the instruction encodings in this ISA. /// Get a data structure describing the instruction encodings in this ISA.

View File

@@ -10,7 +10,7 @@ use super::super::settings as shared_settings;
use binemit::{CodeSink, MemoryCodeSink, emit_function}; use binemit::{CodeSink, MemoryCodeSink, emit_function};
use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, Encodings}; use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, Encodings};
use isa::Builder as IsaBuilder; use isa::Builder as IsaBuilder;
use isa::{TargetIsa, RegInfo, RegClass, EncInfo, Legalize}; use isa::{TargetIsa, RegInfo, RegClass, EncInfo};
use ir; use ir;
use regalloc; use regalloc;
@@ -62,22 +62,19 @@ impl TargetIsa for Isa {
} }
fn legal_encodings<'a>(&'a self, fn legal_encodings<'a>(&'a self,
_dfg: &'a ir::DataFlowGraph, dfg: &'a ir::DataFlowGraph,
inst: &'a ir::InstructionData, inst: &'a ir::InstructionData,
ctrl_typevar: ir::Type) ctrl_typevar: ir::Type)
-> Result<Encodings<'a>, Legalize> { -> Encodings<'a> {
lookup_enclist(ctrl_typevar, lookup_enclist(ctrl_typevar,
inst.opcode(), inst,
dfg,
self.cpumode, self.cpumode,
&enc_tables::LEVEL2[..]) &enc_tables::LEVEL2[..],
.and_then(|enclist_offset| { &enc_tables::ENCLISTS[..],
Ok(Encodings::new(enclist_offset, &enc_tables::RECIPE_PREDICATES[..],
&enc_tables::ENCLISTS[..], &enc_tables::INST_PREDICATES[..],
&enc_tables::RECIPE_PREDICATES[..], self.isa_flags.predicate_view())
&enc_tables::INST_PREDICATES[..],
inst,
self.isa_flags.predicate_view()))
})
} }
fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) { fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) {