From 637966dc7fb890dec6e8e38d0944bedb1d4393ab Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 25 Jul 2017 16:33:35 -0700 Subject: [PATCH] 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. --- lib/cretonne/src/isa/arm32/mod.rs | 23 +++--- lib/cretonne/src/isa/arm64/mod.rs | 23 +++--- lib/cretonne/src/isa/enc_tables.rs | 115 ++++++++++++++++++----------- lib/cretonne/src/isa/intel/mod.rs | 23 +++--- lib/cretonne/src/isa/mod.rs | 6 +- lib/cretonne/src/isa/riscv/mod.rs | 23 +++--- 6 files changed, 116 insertions(+), 97 deletions(-) diff --git a/lib/cretonne/src/isa/arm32/mod.rs b/lib/cretonne/src/isa/arm32/mod.rs index 1e5cde38db..f9a44db258 100644 --- a/lib/cretonne/src/isa/arm32/mod.rs +++ b/lib/cretonne/src/isa/arm32/mod.rs @@ -10,7 +10,7 @@ use binemit::{CodeSink, MemoryCodeSink, emit_function}; use super::super::settings as shared_settings; use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, Encodings}; use isa::Builder as IsaBuilder; -use isa::{TargetIsa, RegInfo, RegClass, EncInfo, Legalize}; +use isa::{TargetIsa, RegInfo, RegClass, EncInfo}; use ir; use regalloc; @@ -62,22 +62,19 @@ impl TargetIsa for Isa { } fn legal_encodings<'a>(&'a self, - _dfg: &'a ir::DataFlowGraph, + dfg: &'a ir::DataFlowGraph, inst: &'a ir::InstructionData, ctrl_typevar: ir::Type) - -> Result, Legalize> { + -> Encodings<'a> { lookup_enclist(ctrl_typevar, - inst.opcode(), + inst, + dfg, self.cpumode, - &enc_tables::LEVEL2[..]) - .and_then(|enclist_offset| { - Ok(Encodings::new(enclist_offset, - &enc_tables::ENCLISTS[..], - &enc_tables::RECIPE_PREDICATES[..], - &enc_tables::INST_PREDICATES[..], - inst, - self.isa_flags.predicate_view())) - }) + &enc_tables::LEVEL2[..], + &enc_tables::ENCLISTS[..], + &enc_tables::RECIPE_PREDICATES[..], + &enc_tables::INST_PREDICATES[..], + self.isa_flags.predicate_view()) } fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) { diff --git a/lib/cretonne/src/isa/arm64/mod.rs b/lib/cretonne/src/isa/arm64/mod.rs index 4dd873e17b..f530d4b0b2 100644 --- a/lib/cretonne/src/isa/arm64/mod.rs +++ b/lib/cretonne/src/isa/arm64/mod.rs @@ -10,7 +10,7 @@ use binemit::{CodeSink, MemoryCodeSink, emit_function}; use super::super::settings as shared_settings; use isa::enc_tables::{lookup_enclist, Encodings}; use isa::Builder as IsaBuilder; -use isa::{TargetIsa, RegInfo, RegClass, EncInfo, Legalize}; +use isa::{TargetIsa, RegInfo, RegClass, EncInfo}; use ir; use regalloc; @@ -55,22 +55,19 @@ impl TargetIsa for Isa { } fn legal_encodings<'a>(&'a self, - _dfg: &'a ir::DataFlowGraph, + dfg: &'a ir::DataFlowGraph, inst: &'a ir::InstructionData, ctrl_typevar: ir::Type) - -> Result, Legalize> { + -> Encodings<'a> { lookup_enclist(ctrl_typevar, - inst.opcode(), + inst, + dfg, &enc_tables::LEVEL1_A64[..], - &enc_tables::LEVEL2[..]) - .and_then(|enclist_offset| { - Ok(Encodings::new(enclist_offset, - &enc_tables::ENCLISTS[..], - &enc_tables::RECIPE_PREDICATES[..], - &enc_tables::INST_PREDICATES[..], - inst, - self.isa_flags.predicate_view())) - }) + &enc_tables::LEVEL2[..], + &enc_tables::ENCLISTS[..], + &enc_tables::RECIPE_PREDICATES[..], + &enc_tables::INST_PREDICATES[..], + self.isa_flags.predicate_view()) } fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) { diff --git a/lib/cretonne/src/isa/enc_tables.rs b/lib/cretonne/src/isa/enc_tables.rs index 95e79b2982..402bf8f06d 100644 --- a/lib/cretonne/src/isa/enc_tables.rs +++ b/lib/cretonne/src/isa/enc_tables.rs @@ -4,8 +4,8 @@ //! `lib/cretonne/meta/gen_encoding.py`. use constant_hash::{Table, probe}; -use ir::{Type, Opcode, InstructionData}; -use isa::{Encoding, Legalize}; +use ir::{Type, Opcode, DataFlowGraph, InstructionData}; +use isa::Encoding; use settings::PredicateView; use std::ops::Range; @@ -97,45 +97,59 @@ impl + Copy> Table for [Level2Entry] { } } -/// 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 /// list. /// -/// Returns an offset into the ISA's `ENCLIST` table, or `None` if the opcode/type combination is -/// not legal. -pub fn lookup_enclist(ctrl_typevar: Type, - opcode: Opcode, - level1_table: &[Level1Entry], - level2_table: &[Level2Entry]) - -> Result +/// Returns an iterator that produces legal encodings for `inst`. +pub fn lookup_enclist<'a, OffT1, OffT2>(ctrl_typevar: Type, + inst: &'a InstructionData, + _dfg: &'a DataFlowGraph, + level1_table: &'static [Level1Entry], + level2_table: &'static [Level2Entry], + enclist: &'static [EncListEntry], + recipe_preds: &'static [RecipePredicate], + inst_preds: &'static [InstPredicate], + isa_preds: PredicateView<'a>) + -> Encodings<'a> where OffT1: Into + Copy, OffT2: Into + Copy { - match probe(level1_table, ctrl_typevar, ctrl_typevar.index()) { + 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. - let l1ent = &level1_table[l1idx]; - Err(l1ent.legalize.into()) + (!0, level1_table[l1idx].legalize) } Ok(l1idx) => { // We have a valid level 1 entry for this type. let l1ent = &level1_table[l1idx]; - match level2_table.get(l1ent.range()) { + let offset = match level2_table.get(l1ent.range()) { Some(l2tab) => { - probe(l2tab, opcode, opcode as usize) - .map(|l2idx| l2tab[l2idx].offset.into() as usize) - .map_err(|_| l1ent.legalize.into()) + let opcode = inst.opcode(); + match probe(l2tab, opcode, opcode as usize) { + Ok(l2idx) => l2tab[l2idx].offset.into() as usize, + Err(_) => !0, + } } - None => { - // The l1ent range is invalid. This means that we just have a customized - // legalization code for this type. The level 2 table is empty. - Err(l1ent.legalize.into()) - } - } + // 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, + enclist, + recipe_preds, + inst_preds, + isa_preds) } /// Encoding list entry. @@ -153,11 +167,13 @@ const PRED_START: usize = 0x1000; 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, - isa_predicates: PredicateView<'a>, enclist: &'static [EncListEntry], - recipe_predicates: &'static [RecipePredicate], - inst_predicates: &'static [InstPredicate], + recipe_preds: &'static [RecipePredicate], + inst_preds: &'static [InstPredicate], + isa_preds: PredicateView<'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 /// or `None`. pub fn new(offset: usize, - enclist: &'static [EncListEntry], - recipe_predicates: &'static [RecipePredicate], - inst_predicates: &'static [InstPredicate], + legalize: LegalizeCode, inst: &'a InstructionData, - isa_predicates: PredicateView<'a>) + enclist: &'static [EncListEntry], + recipe_preds: &'static [RecipePredicate], + inst_preds: &'static [InstPredicate], + isa_preds: PredicateView<'a>) -> Self { Encodings { offset, - enclist, inst, - isa_predicates, - recipe_predicates, - inst_predicates, + legalize, + isa_preds, + 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. fn check_recipe(&self, rpred: RecipePredicate) -> bool { match rpred { - Some(p) => p(self.isa_predicates, self.inst), + 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_predicates.get(pred) { + if let Some(&p) = self.inst_preds.get(pred) { p(self.inst) } else { - let pred = pred - self.inst_predicates.len(); - self.isa_predicates.test(pred) + let pred = pred - self.inst_preds.len(); + self.isa_preds.test(pred) } } } @@ -211,7 +239,7 @@ impl<'a> Iterator for Encodings<'a> { // Check for "recipe+bits". 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; if entry & 1 == 0 { self.offset += 2; // Next entry. @@ -226,7 +254,9 @@ impl<'a> Iterator for Encodings<'a> { // Check for "stop with legalize". 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. @@ -237,7 +267,8 @@ impl<'a> Iterator for Encodings<'a> { if self.check_pred(pred) { self.offset += 1; } else if skip == 0 { - self.offset = !0 // This means stop. + self.offset = !0; // Stop. + return None; } else { self.offset += 1 + skip; } diff --git a/lib/cretonne/src/isa/intel/mod.rs b/lib/cretonne/src/isa/intel/mod.rs index 85445ac701..9d31727b14 100644 --- a/lib/cretonne/src/isa/intel/mod.rs +++ b/lib/cretonne/src/isa/intel/mod.rs @@ -10,7 +10,7 @@ use binemit::{CodeSink, MemoryCodeSink, emit_function}; use super::super::settings as shared_settings; use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, Encodings}; use isa::Builder as IsaBuilder; -use isa::{TargetIsa, RegInfo, RegClass, EncInfo, Legalize}; +use isa::{TargetIsa, RegInfo, RegClass, EncInfo}; use ir; use regalloc; @@ -62,22 +62,19 @@ impl TargetIsa for Isa { } fn legal_encodings<'a>(&'a self, - _dfg: &'a ir::DataFlowGraph, + dfg: &'a ir::DataFlowGraph, inst: &'a ir::InstructionData, ctrl_typevar: ir::Type) - -> Result, Legalize> { + -> Encodings<'a> { lookup_enclist(ctrl_typevar, - inst.opcode(), + inst, + dfg, self.cpumode, - &enc_tables::LEVEL2[..]) - .and_then(|enclist_offset| { - Ok(Encodings::new(enclist_offset, - &enc_tables::ENCLISTS[..], - &enc_tables::RECIPE_PREDICATES[..], - &enc_tables::INST_PREDICATES[..], - inst, - self.isa_flags.predicate_view())) - }) + &enc_tables::LEVEL2[..], + &enc_tables::ENCLISTS[..], + &enc_tables::RECIPE_PREDICATES[..], + &enc_tables::INST_PREDICATES[..], + self.isa_flags.predicate_view()) } fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) { diff --git a/lib/cretonne/src/isa/mod.rs b/lib/cretonne/src/isa/mod.rs index 7b25aec8ec..6c43040825 100644 --- a/lib/cretonne/src/isa/mod.rs +++ b/lib/cretonne/src/isa/mod.rs @@ -156,7 +156,7 @@ pub trait TargetIsa { dfg: &'a ir::DataFlowGraph, inst: &'a ir::InstructionData, ctrl_typevar: ir::Type) - -> Result, Legalize>; + -> Encodings<'a>; /// Encode an instruction after determining it is legal. /// @@ -169,8 +169,8 @@ pub trait TargetIsa { inst: &ir::InstructionData, ctrl_typevar: ir::Type) -> Result { - self.legal_encodings(dfg, inst, ctrl_typevar) - .and_then(|mut iter| iter.next().ok_or(Legalize::Expand)) + let mut iter = self.legal_encodings(dfg, inst, ctrl_typevar); + iter.next().ok_or(iter.legalize().into()) } /// Get a data structure describing the instruction encodings in this ISA. diff --git a/lib/cretonne/src/isa/riscv/mod.rs b/lib/cretonne/src/isa/riscv/mod.rs index 3fe675b7a4..18f0172f86 100644 --- a/lib/cretonne/src/isa/riscv/mod.rs +++ b/lib/cretonne/src/isa/riscv/mod.rs @@ -10,7 +10,7 @@ use super::super::settings as shared_settings; use binemit::{CodeSink, MemoryCodeSink, emit_function}; use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, Encodings}; use isa::Builder as IsaBuilder; -use isa::{TargetIsa, RegInfo, RegClass, EncInfo, Legalize}; +use isa::{TargetIsa, RegInfo, RegClass, EncInfo}; use ir; use regalloc; @@ -62,22 +62,19 @@ impl TargetIsa for Isa { } fn legal_encodings<'a>(&'a self, - _dfg: &'a ir::DataFlowGraph, + dfg: &'a ir::DataFlowGraph, inst: &'a ir::InstructionData, ctrl_typevar: ir::Type) - -> Result, Legalize> { + -> Encodings<'a> { lookup_enclist(ctrl_typevar, - inst.opcode(), + inst, + dfg, self.cpumode, - &enc_tables::LEVEL2[..]) - .and_then(|enclist_offset| { - Ok(Encodings::new(enclist_offset, - &enc_tables::ENCLISTS[..], - &enc_tables::RECIPE_PREDICATES[..], - &enc_tables::INST_PREDICATES[..], - inst, - self.isa_flags.predicate_view())) - }) + &enc_tables::LEVEL2[..], + &enc_tables::ENCLISTS[..], + &enc_tables::RECIPE_PREDICATES[..], + &enc_tables::INST_PREDICATES[..], + self.isa_flags.predicate_view()) } fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) {