diff --git a/lib/cretonne/meta/cdsl/isa.py b/lib/cretonne/meta/cdsl/isa.py index 29a3698304..f65c2ad1fc 100644 --- a/lib/cretonne/meta/cdsl/isa.py +++ b/lib/cretonne/meta/cdsl/isa.py @@ -23,6 +23,9 @@ try: # instructions. InstSpec = Union[MaybeBoundInst, Apply] BranchRange = Sequence[int] + # A recipe predicate consisting of an ISA predicate and an instruction + # predicate. + RecipePred = Tuple[PredNode, PredNode] except ImportError: pass @@ -62,7 +65,7 @@ class TargetISA(object): Finish the definition of a target ISA after adding all CPU modes and settings. - This computes some derived properties that are used in multilple + This computes some derived properties that are used in multiple places. :returns self: @@ -86,6 +89,9 @@ class TargetISA(object): recipe.number = len(rcps) rcps.add(recipe) self.all_recipes.append(recipe) + # Make sure ISA predicates are registered. + if recipe.isap: + self.settings.number_predicate(recipe.isap) def _collect_predicates(self): # type: () -> None @@ -336,6 +342,19 @@ class EncRecipe(object): o2i[o] = i return (i2o, o2i) + def recipe_pred(self): + # type: () -> RecipePred + """ + Get the combined recipe predicate which includes both the ISA predicate + and the instruction predicate. + + Return `None` if this recipe has neither predicate. + """ + if self.isap is None and self.instp is None: + return None + else: + return (self.isap, self.instp) + class Encoding(object): """ @@ -386,9 +405,9 @@ class Encoding(object): self.recipe = recipe self.encbits = encbits - # Combine recipe predicates with the manually specified ones. - self.instp = And.combine(recipe.instp, instp) - self.isap = And.combine(recipe.isap, isap) + # Record specific predicates. Note that the recipe also has predicates. + self.instp = instp + self.isap = isap def __str__(self): # type: () -> str diff --git a/lib/cretonne/meta/gen_encoding.py b/lib/cretonne/meta/gen_encoding.py index de8b138cf6..5f49cb42c6 100644 --- a/lib/cretonne/meta/gen_encoding.py +++ b/lib/cretonne/meta/gen_encoding.py @@ -11,11 +11,12 @@ are allocated. This is the information available to us: -- The instruction to be encoded as an `Inst` reference. -- The data-flow graph containing the instruction, giving us access to the - `InstructionData` representation and the types of all values involved. -- A target ISA instance with shared and ISA-specific settings for evaluating - ISA predicates. +- The instruction to be encoded as an `InstructionData` reference. +- The controlling type variable. +- The data-flow graph giving us access to the types of all values involved. + This is needed for testing any secondary type variables. +- A `PredicateView` reference for the ISA-specific settings for evaluating ISA + predicates. - The currently active CPU mode is determined by the ISA. ## Level 1 table lookup @@ -62,7 +63,7 @@ from cdsl.predicates import FieldPredicate try: from typing import Sequence, Set, Tuple, List, Dict, Iterable, DefaultDict, TYPE_CHECKING # noqa if TYPE_CHECKING: - from cdsl.isa import TargetISA, OperandConstraint, Encoding, CPUMode, EncRecipe # noqa + from cdsl.isa import TargetISA, OperandConstraint, Encoding, CPUMode, EncRecipe, RecipePred # noqa from cdsl.predicates import PredNode, PredLeaf # noqa from cdsl.types import ValueType # noqa from cdsl.instructions import Instruction # noqa @@ -77,8 +78,8 @@ def emit_instp(instp, fmt): Emit code for matching an instruction predicate against an `InstructionData` reference called `inst`. - The generated code is a pattern match that falls through if the instruction - has an unexpected format. This should lead to a panic. + The generated code is an `if let` pattern match that falls through if the + instruction has an unexpected format. This should lead to a panic. """ iform = instp.predicate_context() @@ -94,11 +95,10 @@ def emit_instp(instp, fmt): fnames.add(p.field.rust_name()) fields = ', '.join(sorted(fnames)) - with fmt.indented('{} => {{'.format(instp.number), '}'): - with fmt.indented( - 'if let InstructionData::{} {{ {}, .. }} = *inst {{' - .format(iform.name, fields), '}'): - fmt.line('return {};'.format(instp.rust_predicate(0))) + with fmt.indented( + 'if let InstructionData::{} {{ {}, .. }} = *inst {{' + .format(iform.name, fields), '}'): + fmt.line('return {};'.format(instp.rust_predicate(0))) def emit_instps(instps, fmt): @@ -122,7 +122,8 @@ def emit_instps(instps, fmt): fmt.line('use ir::instructions::InstructionFormat;') with fmt.indented('match instp_idx {', '}'): for instp in instps: - emit_instp(instp, fmt) + with fmt.indented('{} => {{'.format(instp.number), '}'): + emit_instp(instp, fmt) fmt.line('_ => panic!("Invalid instruction predicate")') # The match cases will fall through if the instruction format is wrong. @@ -132,6 +133,55 @@ def emit_instps(instps, fmt): fmt.line(' instp_idx);') +def emit_recipe_predicates(recipes, fmt): + # type: (Sequence[EncRecipe], srcgen.Formatter) -> None + """ + Emit private functions for checking recipe predicates as well as a static + `RECIPE_PREDICATES` array indexed by recipe number. + + A recipe predicate is a combination of an ISA predicate and an instruction + predicates. Many recipes have identical predicates. + """ + # Table for uniquing recipe predicates. Maps predicate to generated + # function name. + pname = dict() # type: Dict[RecipePred, str] + + # Generate unique recipe predicates. + for rcp in recipes: + p = rcp.recipe_pred() + if p is None or p in pname: + continue + name = 'recipe_predicate_{}'.format(rcp.name.lower()) + pname[p] = name + isap, instp = p + + # Generate the predicate function. + with fmt.indented( + 'fn {}({}: ::settings::PredicateView, ' + 'inst: &InstructionData) -> bool {{' + .format( + name, + 'isap' if isap else '_'), '}'): + if isap: + with fmt.indented( + 'if isap.test({})'.format(isap.number), + '}'): + fmt.line('return false;') + emit_instp(instp, fmt) + fmt.line('unreachable!();') + + # Generate the static table. + with fmt.indented( + 'pub static RECIPE_PREDICATES: [RecipePredicate; {}] = [' + .format(len(recipes)), '];'): + for rcp in recipes: + p = rcp.recipe_pred() + if p is None: + fmt.line('None,') + else: + fmt.format('Some({}),', pname[p]) + + # Encoding lists are represented as u16 arrays. CODE_BITS = 16 PRED_BITS = 12 @@ -604,8 +654,11 @@ def emit_recipe_sizing(isa, fmt): def gen_isa(isa, fmt): # type: (TargetISA, srcgen.Formatter) -> None - # First assign numbers to relevant instruction predicates and generate the - # check_instp() function.. + + # Make the `RECIPE_PREDICATES` table. + emit_recipe_predicates(isa.all_recipes, fmt) + + # Generate the check_instp() function.. emit_instps(isa.all_instps, fmt) # Level1 tables, one per CPU mode diff --git a/lib/cretonne/src/isa/arm32/enc_tables.rs b/lib/cretonne/src/isa/arm32/enc_tables.rs index 9fdfbcda95..e33d47f12d 100644 --- a/lib/cretonne/src/isa/arm32/enc_tables.rs +++ b/lib/cretonne/src/isa/arm32/enc_tables.rs @@ -4,7 +4,7 @@ use ir::InstructionData; use ir::types; use isa::EncInfo; use isa::constraints::*; -use isa::enc_tables::{Level1Entry, Level2Entry}; +use isa::enc_tables::*; use isa::encoding::RecipeSizing; include!(concat!(env!("OUT_DIR"), "/encoding-arm32.rs")); diff --git a/lib/cretonne/src/isa/arm32/mod.rs b/lib/cretonne/src/isa/arm32/mod.rs index 5675d19d7a..06852e4ad2 100644 --- a/lib/cretonne/src/isa/arm32/mod.rs +++ b/lib/cretonne/src/isa/arm32/mod.rs @@ -73,6 +73,7 @@ impl TargetIsa for Isa { .and_then(|enclist_offset| { Ok(Encodings::new(enclist_offset, &enc_tables::ENCLISTS[..], + &enc_tables::RECIPE_PREDICATES[..], inst, enc_tables::check_instp, self.isa_flags.predicate_view())) diff --git a/lib/cretonne/src/isa/arm64/enc_tables.rs b/lib/cretonne/src/isa/arm64/enc_tables.rs index cdfc255748..889a5a7dbe 100644 --- a/lib/cretonne/src/isa/arm64/enc_tables.rs +++ b/lib/cretonne/src/isa/arm64/enc_tables.rs @@ -4,7 +4,7 @@ use ir::InstructionData; use ir::types; use isa::EncInfo; use isa::constraints::*; -use isa::enc_tables::{Level1Entry, Level2Entry}; +use isa::enc_tables::*; use isa::encoding::RecipeSizing; include!(concat!(env!("OUT_DIR"), "/encoding-arm64.rs")); diff --git a/lib/cretonne/src/isa/arm64/mod.rs b/lib/cretonne/src/isa/arm64/mod.rs index e9aea5aacf..26e4ea9dfe 100644 --- a/lib/cretonne/src/isa/arm64/mod.rs +++ b/lib/cretonne/src/isa/arm64/mod.rs @@ -66,6 +66,7 @@ impl TargetIsa for Isa { .and_then(|enclist_offset| { Ok(Encodings::new(enclist_offset, &enc_tables::ENCLISTS[..], + &enc_tables::RECIPE_PREDICATES[..], inst, enc_tables::check_instp, self.isa_flags.predicate_view())) diff --git a/lib/cretonne/src/isa/enc_tables.rs b/lib/cretonne/src/isa/enc_tables.rs index 33ec38d3c1..843cbf6a1d 100644 --- a/lib/cretonne/src/isa/enc_tables.rs +++ b/lib/cretonne/src/isa/enc_tables.rs @@ -9,6 +9,13 @@ use isa::{Encoding, Legalize}; use settings::PredicateView; use std::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 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. @@ -147,6 +154,7 @@ pub struct Encodings<'a> { inst: &'a InstructionData, instp: fn(&InstructionData, EncListEntry) -> bool, isa_predicates: PredicateView<'a>, + recipe_predicates: &'static [RecipePredicate], } impl<'a> Encodings<'a> { @@ -155,8 +163,9 @@ impl<'a> Encodings<'a> { /// # Parameters /// /// - `offset` an offset into encoding list returned by `lookup_enclist` function. - /// - `inst` the current instruction. /// - `enclist` a list of encoding entries. + /// - `recipe_predicates` is a slice of recipe predicate functions. + /// - `inst` the current instruction. /// - `instp` an instruction predicate number to be evaluated on the current instruction. /// - `isa_predicate_bytes` an ISA flags as a slice of bytes to evaluate an ISA predicate number /// on the current instruction. @@ -166,6 +175,7 @@ impl<'a> Encodings<'a> { /// or `None`. pub fn new(offset: usize, enclist: &'static [EncListEntry], + recipe_predicates: &'static [RecipePredicate], inst: &'a InstructionData, instp: fn(&InstructionData, EncListEntry) -> bool, isa_predicates: PredicateView<'a>) @@ -176,6 +186,15 @@ impl<'a> Encodings<'a> { inst, instp, isa_predicates, + recipe_predicates, + } + } + + /// Check if the predicate for `recipe` is satisfied. + fn check_recipe(&self, recipe: u16) -> bool { + match self.recipe_predicates[recipe as usize] { + Some(p) => p(self.isa_predicates, self.inst), + None => true, } } } @@ -188,13 +207,13 @@ impl<'a> Iterator for Encodings<'a> { let pred = self.enclist[self.offset]; if pred <= CODE_ALWAYS { // This is an instruction predicate followed by recipe and encbits entries. + self.offset += 3; if pred == CODE_ALWAYS || (self.instp)(self.inst, pred) { - let encoding = Encoding::new(self.enclist[self.offset + 1], - self.enclist[self.offset + 2]); - self.offset += 3; - return Some(encoding); - } else { - self.offset += 3; + let recipe = self.enclist[self.offset - 2]; + if self.check_recipe(recipe) { + let encoding = Encoding::new(recipe, self.enclist[self.offset - 1]); + return Some(encoding); + } } } else { // This is an ISA predicate entry. diff --git a/lib/cretonne/src/isa/intel/enc_tables.rs b/lib/cretonne/src/isa/intel/enc_tables.rs index e72da36b1e..47ea5dbe11 100644 --- a/lib/cretonne/src/isa/intel/enc_tables.rs +++ b/lib/cretonne/src/isa/intel/enc_tables.rs @@ -4,7 +4,7 @@ use ir::types; use ir::{Opcode, InstructionData}; use isa::EncInfo; use isa::constraints::*; -use isa::enc_tables::{Level1Entry, Level2Entry}; +use isa::enc_tables::*; use isa::encoding::RecipeSizing; use predicates; use super::registers::*; diff --git a/lib/cretonne/src/isa/intel/mod.rs b/lib/cretonne/src/isa/intel/mod.rs index 35d339a740..c459b49bd7 100644 --- a/lib/cretonne/src/isa/intel/mod.rs +++ b/lib/cretonne/src/isa/intel/mod.rs @@ -73,6 +73,7 @@ impl TargetIsa for Isa { .and_then(|enclist_offset| { Ok(Encodings::new(enclist_offset, &enc_tables::ENCLISTS[..], + &enc_tables::RECIPE_PREDICATES[..], inst, enc_tables::check_instp, self.isa_flags.predicate_view())) diff --git a/lib/cretonne/src/isa/riscv/enc_tables.rs b/lib/cretonne/src/isa/riscv/enc_tables.rs index 8ced0c681f..0dddebdbac 100644 --- a/lib/cretonne/src/isa/riscv/enc_tables.rs +++ b/lib/cretonne/src/isa/riscv/enc_tables.rs @@ -5,7 +5,7 @@ use ir::types; use ir::{Opcode, InstructionData}; use isa::EncInfo; use isa::constraints::*; -use isa::enc_tables::{Level1Entry, Level2Entry}; +use isa::enc_tables::*; use isa::encoding::RecipeSizing; use predicates; use super::registers::*; diff --git a/lib/cretonne/src/isa/riscv/mod.rs b/lib/cretonne/src/isa/riscv/mod.rs index 869ea4c9cb..d09bfbd028 100644 --- a/lib/cretonne/src/isa/riscv/mod.rs +++ b/lib/cretonne/src/isa/riscv/mod.rs @@ -73,6 +73,7 @@ impl TargetIsa for Isa { .and_then(|enclist_offset| { Ok(Encodings::new(enclist_offset, &enc_tables::ENCLISTS[..], + &enc_tables::RECIPE_PREDICATES[..], inst, enc_tables::check_instp, self.isa_flags.predicate_view()))