diff --git a/filetests/isa/intel/binary64-float.cton b/filetests/isa/intel/binary64-float.cton index ba604ad43c..e8a7c574c1 100644 --- a/filetests/isa/intel/binary64-float.cton +++ b/filetests/isa/intel/binary64-float.cton @@ -21,9 +21,9 @@ ebb0: [-,%xmm10] v11 = fcvt_from_sint.f32 v1 ; bin: f3 44 0f 2a d6 ; asm: cvtsi2ssq %rax, %xmm5 - [-,%xmm5] v12 = fcvt_from_sint.f32 v2 ; TODO: f3 48 0f 2a e8 + [-,%xmm5] v12 = fcvt_from_sint.f32 v2 ; bin: f3 48 0f 2a e8 ; asm: cvtsi2ssq %r14, %xmm10 - [-,%xmm10] v13 = fcvt_from_sint.f32 v3 ; TODO: f3 4d 0f 2a d6 + [-,%xmm10] v13 = fcvt_from_sint.f32 v3 ; bin: f3 4d 0f 2a d6 ; Binary arithmetic. @@ -86,9 +86,9 @@ ebb0: [-,%xmm10] v11 = fcvt_from_sint.f64 v1 ; bin: f2 44 0f 2a d6 ; asm: cvtsi2sdq %rax, %xmm5 - [-,%xmm5] v12 = fcvt_from_sint.f64 v2 ; TODO: f2 48 0f 2a e8 + [-,%xmm5] v12 = fcvt_from_sint.f64 v2 ; bin: f2 48 0f 2a e8 ; asm: cvtsi2sdq %r14, %xmm10 - [-,%xmm10] v13 = fcvt_from_sint.f64 v3 ; TODO: f2 4d 0f 2a d6 + [-,%xmm10] v13 = fcvt_from_sint.f64 v3 ; bin: f2 4d 0f 2a d6 ; Binary arithmetic. diff --git a/lib/cretonne/meta/cdsl/formats.py b/lib/cretonne/meta/cdsl/formats.py index 0b7a72fe0a..aba83ed7a2 100644 --- a/lib/cretonne/meta/cdsl/formats.py +++ b/lib/cretonne/meta/cdsl/formats.py @@ -11,6 +11,29 @@ except ImportError: pass +class InstructionContext(object): + """ + Most instruction predicates refer to immediate fields of a specific + instruction format, so their `predicate_context()` method returns the + specific instruction format. + + Predicates that only care about the types of SSA values are independent of + the instruction format. They can be evaluated in the context of any + instruction. + + The singleton `InstructionContext` class serves as the predicate context + for these predicates. + """ + + def __init__(self): + # type: () -> None + self.name = 'inst' + + +# Singleton instance. +instruction_context = InstructionContext() + + class InstructionFormat(object): """ Every instruction opcode has a corresponding instruction format which @@ -48,6 +71,7 @@ class InstructionFormat(object): def __init__(self, *kinds, **kwargs): # type: (*Union[OperandKind, Tuple[str, OperandKind]], **Any) -> None # noqa self.name = kwargs.get('name', None) # type: str + self.parent = instruction_context # The number of value operands stored in the format, or `None` when # `has_value_list` is set. diff --git a/lib/cretonne/meta/cdsl/isa.py b/lib/cretonne/meta/cdsl/isa.py index f65c2ad1fc..563cf91e92 100644 --- a/lib/cretonne/meta/cdsl/isa.py +++ b/lib/cretonne/meta/cdsl/isa.py @@ -1,7 +1,7 @@ """Defining instruction set architectures.""" from __future__ import absolute_import from collections import OrderedDict -from .predicates import And +from .predicates import And, TypePredicate from .registers import RegClass, Register, Stack from .ast import Apply from .types import ValueType @@ -405,6 +405,13 @@ class Encoding(object): self.recipe = recipe self.encbits = encbits + + # Add secondary type variables to the instruction predicate. + if len(self.typevars) > 1: + for tv, vt in zip(self.inst.other_typevars, self.typevars[1:]): + typred = TypePredicate.typevar_check(self.inst, tv, vt) + instp = And.combine(instp, typred) + # Record specific predicates. Note that the recipe also has predicates. self.instp = instp self.isap = isap diff --git a/lib/cretonne/meta/cdsl/predicates.py b/lib/cretonne/meta/cdsl/predicates.py index 8a1cc147f9..62779c2d15 100644 --- a/lib/cretonne/meta/cdsl/predicates.py +++ b/lib/cretonne/meta/cdsl/predicates.py @@ -23,14 +23,19 @@ predicate, the context is the instruction format. """ from __future__ import absolute_import from functools import reduce +from .formats import instruction_context try: from typing import Sequence, Tuple, Set, Any, Union, TYPE_CHECKING # noqa if TYPE_CHECKING: - from .formats import InstructionFormat, FormatField # noqa + from .formats import InstructionFormat, InstructionContext, FormatField # noqa + from .instructions import Instruction # noqa from .settings import BoolSetting, SettingGroup # noqa - PredContext = Union[SettingGroup, InstructionFormat] - PredLeaf = Union[BoolSetting, 'FieldPredicate'] + from .types import ValueType # noqa + from .typevar import TypeVar # noqa + PredContext = Union[SettingGroup, InstructionFormat, + InstructionContext] + PredLeaf = Union[BoolSetting, 'FieldPredicate', 'TypePredicate'] PredNode = Union[PredLeaf, 'Predicate'] except ImportError: pass @@ -52,7 +57,7 @@ def _descendant(a, b): If a is a parent of b or b is a parent of a, return the descendant of the two. - If neiher is a parent of the other, return None. + If neither is a parent of the other, return None. """ if _is_parent(a, b): return b @@ -293,3 +298,69 @@ class IsUnsignedInt(FieldPredicate): self.scale = scale assert width >= 0 and width <= 64 assert scale >= 0 and scale < width + + +class TypePredicate(object): + """ + An instruction predicate that checks the type of an SSA argument value. + + Type predicates are used to implement encodings for instructions with + multiple type variables. The encoding tables are keyed by the controlling + type variable, type predicates check any secondary type variables. + + A type predicate is not bound to any specific instruction format. + + :param value_arg: Index of the value argument to type check. + :param value_type: The required value type. + """ + + def __init__(self, value_arg, value_type): + # type: (int, ValueType) -> None + assert value_arg >= 0 + assert value_type is not None + self.value_arg = value_arg + self.value_type = value_type + self.number = None # type: int + # All PredNode members must have a name field. This will never be set. + self.name = None # type: str + + def __str__(self): + # type: () -> str + return 'args[{}]:{}'.format(self.value_arg, self.value_type) + + def predicate_context(self): + # type: () -> PredContext + return instruction_context + + def predicate_leafs(self, leafs): + # type: (Set[PredLeaf]) -> None + leafs.add(self) + + @staticmethod + def typevar_check(inst, typevar, value_type): + # type: (Instruction, TypeVar, ValueType) -> TypePredicate + """ + Return a type check predicate for the given type variable in `inst`. + + The type variable must appear directly as the type of one of the + operands to `inst`, so this is only guaranteed to work for secondary + type variables. + + Find an `inst` value operand whose type is determined by `typevar` and + create a `TypePredicate` that checks that the type variable has the + value `value_type`. + """ + # Find the first value operand whose type is `typevar`. + value_arg = next(i for i, opnum in enumerate(inst.value_opnums) + if inst.ins[opnum].typevar == typevar) + return TypePredicate(value_arg, value_type) + + def rust_predicate(self, prec): + # type: (int) -> str + """ + Return Rust code for evaluating this predicate. + + It is assumed that the context has `dfg` and `args` variables. + """ + return 'dfg.value_type(args[{}]) == {}'.format( + self.value_arg, self.value_type.rust_name()) diff --git a/lib/cretonne/meta/gen_encoding.py b/lib/cretonne/meta/gen_encoding.py index b8915367fc..6f88aa70fb 100644 --- a/lib/cretonne/meta/gen_encoding.py +++ b/lib/cretonne/meta/gen_encoding.py @@ -58,8 +58,9 @@ from collections import OrderedDict, defaultdict import math from itertools import groupby from cdsl.registers import RegClass, Register, Stack -from cdsl.predicates import FieldPredicate +from cdsl.predicates import FieldPredicate, TypePredicate from cdsl.settings import SettingGroup +from cdsl.formats import instruction_context, InstructionFormat try: from typing import Sequence, Set, Tuple, List, Dict, Iterable, DefaultDict, TYPE_CHECKING # noqa @@ -73,8 +74,8 @@ except ImportError: pass -def emit_instp(instp, fmt): - # type: (PredNode, srcgen.Formatter) -> None +def emit_instp(instp, fmt, has_dfg=False): + # type: (PredNode, srcgen.Formatter, bool) -> None """ Emit code for matching an instruction predicate against an `InstructionData` reference called `inst`. @@ -84,22 +85,42 @@ def emit_instp(instp, fmt): """ iform = instp.predicate_context() + # Deal with pure type check predicates which apply to any instruction. + if iform == instruction_context: + fmt.line('let args = inst.arguments(&dfg.value_lists);') + fmt.format('return {};', instp.rust_predicate(0)) + return + + assert isinstance(iform, InstructionFormat) + # Which fields do we need in the InstructionData pattern match? + has_type_check = False # Collect the leaf predicates. leafs = set() # type: Set[PredLeaf] instp.predicate_leafs(leafs) - # All the leafs are FieldPredicate instances. Here we just care about - # the field names. + # All the leafs are FieldPredicate or TypePredicate instances. Here we just + # care about the field names. fnames = set() # type: Set[str] for p in leafs: - assert isinstance(p, FieldPredicate) - fnames.add(p.field.rust_name()) + if isinstance(p, FieldPredicate): + fnames.add(p.field.rust_name()) + else: + assert isinstance(p, TypePredicate) + has_type_check = True fields = ', '.join(sorted(fnames)) with fmt.indented( - 'if let InstructionData::{} {{ {}, .. }} = *inst {{' + 'if let ir::InstructionData::{} {{ {}, .. }} = *inst {{' .format(iform.name, fields), '}'): - fmt.line('return {};'.format(instp.rust_predicate(0))) + if has_type_check: + # We could implement this if we need to. + assert has_dfg, "Recipe predicates can't check type variables." + fmt.line('let args = inst.arguments(&dfg.value_lists);') + elif has_dfg: + # Silence dead argument warning. + fmt.line('let _ = dfg;') + fmt.format('return {};', instp.rust_predicate(0)) + fmt.line('unreachable!();') def emit_inst_predicates(instps, fmt): @@ -111,11 +132,9 @@ def emit_inst_predicates(instps, fmt): for instp in instps: name = 'inst_predicate_{}'.format(instp.number) with fmt.indented( - 'fn {}(inst: &InstructionData) -> bool {{' - .format(name), - '}'): - emit_instp(instp, fmt) - fmt.line('unreachable!();') + 'fn {}(dfg: &ir::DataFlowGraph, inst: &ir::InstructionData)' + '-> bool {{'.format(name), '}'): + emit_instp(instp, fmt, has_dfg=True) # Generate the static table. with fmt.indented( @@ -150,7 +169,7 @@ def emit_recipe_predicates(recipes, fmt): # Generate the predicate function. with fmt.indented( 'fn {}({}: ::settings::PredicateView, ' - 'inst: &InstructionData) -> bool {{' + 'inst: &ir::InstructionData) -> bool {{' .format( name, 'isap' if isap else '_'), '}'): @@ -160,7 +179,6 @@ def emit_recipe_predicates(recipes, fmt): '}'): fmt.line('return false;') emit_instp(instp, fmt) - fmt.line('unreachable!();') # Generate the static table. with fmt.indented( diff --git a/lib/cretonne/meta/isa/intel/encodings.py b/lib/cretonne/meta/isa/intel/encodings.py index df1a542101..b59dd8d9af 100644 --- a/lib/cretonne/meta/isa/intel/encodings.py +++ b/lib/cretonne/meta/isa/intel/encodings.py @@ -229,11 +229,13 @@ I64.enc(base.uextend.i64.i32, *r.umr(0x89)) # cvtsi2ss I32.enc(base.fcvt_from_sint.f32.i32, *r.furm(0xf3, 0x0f, 0x2A)) +I64.enc(base.fcvt_from_sint.f32.i64, *r.furm.rex(0xf3, 0x0f, 0x2A, w=1)) I64.enc(base.fcvt_from_sint.f32.i32, *r.furm.rex(0xf3, 0x0f, 0x2A)) I64.enc(base.fcvt_from_sint.f32.i32, *r.furm(0xf3, 0x0f, 0x2A)) # cvtsi2sd I32.enc(base.fcvt_from_sint.f64.i32, *r.furm(0xf2, 0x0f, 0x2A)) +I64.enc(base.fcvt_from_sint.f64.i64, *r.furm.rex(0xf2, 0x0f, 0x2A, w=1)) I64.enc(base.fcvt_from_sint.f64.i32, *r.furm.rex(0xf2, 0x0f, 0x2A)) I64.enc(base.fcvt_from_sint.f64.i32, *r.furm(0xf2, 0x0f, 0x2A)) diff --git a/lib/cretonne/src/isa/enc_tables.rs b/lib/cretonne/src/isa/enc_tables.rs index 402bf8f06d..d8bd9756d6 100644 --- a/lib/cretonne/src/isa/enc_tables.rs +++ b/lib/cretonne/src/isa/enc_tables.rs @@ -20,7 +20,7 @@ pub type RecipePredicate = Option bool>; /// /// 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(&InstructionData) -> bool; +pub type InstPredicate = fn(&DataFlowGraph, &InstructionData) -> bool; /// Legalization action to perform when no encoding can be found for an instruction. /// @@ -105,7 +105,7 @@ impl + Copy> Table for [Level2Entry] { /// 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, + dfg: &'a DataFlowGraph, level1_table: &'static [Level1Entry], level2_table: &'static [Level2Entry], enclist: &'static [EncListEntry], @@ -146,6 +146,7 @@ pub fn lookup_enclist<'a, OffT1, OffT2>(ctrl_typevar: Type, Encodings::new(offset, legalize, inst, + dfg, enclist, recipe_preds, inst_preds, @@ -170,6 +171,7 @@ pub struct Encodings<'a> { // Legalization code to use of no encoding is found. legalize: LegalizeCode, inst: &'a InstructionData, + dfg: &'a DataFlowGraph, enclist: &'static [EncListEntry], recipe_preds: &'static [RecipePredicate], inst_preds: &'static [InstPredicate], @@ -185,6 +187,7 @@ impl<'a> Encodings<'a> { pub fn new(offset: usize, legalize: LegalizeCode, inst: &'a InstructionData, + dfg: &'a DataFlowGraph, enclist: &'static [EncListEntry], recipe_preds: &'static [RecipePredicate], inst_preds: &'static [InstPredicate], @@ -193,6 +196,7 @@ impl<'a> Encodings<'a> { Encodings { offset, inst, + dfg, legalize, isa_preds, recipe_preds, @@ -222,7 +226,7 @@ impl<'a> Encodings<'a> { /// Check an instruction or isa predicate. fn check_pred(&self, pred: usize) -> bool { if let Some(&p) = self.inst_preds.get(pred) { - p(self.inst) + p(self.dfg, self.inst) } else { let pred = pred - self.inst_preds.len(); self.isa_preds.test(pred) diff --git a/lib/cretonne/src/isa/intel/enc_tables.rs b/lib/cretonne/src/isa/intel/enc_tables.rs index 47ea5dbe11..b3144f11e3 100644 --- a/lib/cretonne/src/isa/intel/enc_tables.rs +++ b/lib/cretonne/src/isa/intel/enc_tables.rs @@ -1,7 +1,6 @@ //! Encoding tables for Intel ISAs. -use ir::types; -use ir::{Opcode, InstructionData}; +use ir::{self, types, Opcode}; use isa::EncInfo; use isa::constraints::*; use isa::enc_tables::*; diff --git a/lib/cretonne/src/isa/riscv/enc_tables.rs b/lib/cretonne/src/isa/riscv/enc_tables.rs index 0dddebdbac..7e05879a3b 100644 --- a/lib/cretonne/src/isa/riscv/enc_tables.rs +++ b/lib/cretonne/src/isa/riscv/enc_tables.rs @@ -1,8 +1,7 @@ //! Encoding tables for RISC-V. use ir::condcodes::IntCC; -use ir::types; -use ir::{Opcode, InstructionData}; +use ir::{self, types, Opcode}; use isa::EncInfo; use isa::constraints::*; use isa::enc_tables::*;