Generate type check predicates for secondary type variables.

The encoding tables are keyed by the controlling type variable only. We
need to distinguish different encodings for instructions with multiple
type variables.

Add a TypePredicate instruction predicate which can check the type of an
instruction value operand. Combine type checks into the instruction
predicate for instructions with more than one type variable.

Add Intel encodings for fcvt_from_sint.f32.i64 which can now be
distinguished from fcvt_from_sint.f32.i32.
This commit is contained in:
Jakob Stoklund Olesen
2017-07-26 08:12:16 -07:00
parent 9067fe7f99
commit 6da734221a
9 changed files with 156 additions and 32 deletions

View File

@@ -21,9 +21,9 @@ ebb0:
[-,%xmm10] v11 = fcvt_from_sint.f32 v1 ; bin: f3 44 0f 2a d6 [-,%xmm10] v11 = fcvt_from_sint.f32 v1 ; bin: f3 44 0f 2a d6
; asm: cvtsi2ssq %rax, %xmm5 ; 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 ; 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. ; Binary arithmetic.
@@ -86,9 +86,9 @@ ebb0:
[-,%xmm10] v11 = fcvt_from_sint.f64 v1 ; bin: f2 44 0f 2a d6 [-,%xmm10] v11 = fcvt_from_sint.f64 v1 ; bin: f2 44 0f 2a d6
; asm: cvtsi2sdq %rax, %xmm5 ; 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 ; 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. ; Binary arithmetic.

View File

@@ -11,6 +11,29 @@ except ImportError:
pass 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): class InstructionFormat(object):
""" """
Every instruction opcode has a corresponding instruction format which Every instruction opcode has a corresponding instruction format which
@@ -48,6 +71,7 @@ class InstructionFormat(object):
def __init__(self, *kinds, **kwargs): def __init__(self, *kinds, **kwargs):
# type: (*Union[OperandKind, Tuple[str, OperandKind]], **Any) -> None # noqa # type: (*Union[OperandKind, Tuple[str, OperandKind]], **Any) -> None # noqa
self.name = kwargs.get('name', None) # type: str self.name = kwargs.get('name', None) # type: str
self.parent = instruction_context
# The number of value operands stored in the format, or `None` when # The number of value operands stored in the format, or `None` when
# `has_value_list` is set. # `has_value_list` is set.

View File

@@ -1,7 +1,7 @@
"""Defining instruction set architectures.""" """Defining instruction set architectures."""
from __future__ import absolute_import from __future__ import absolute_import
from collections import OrderedDict from collections import OrderedDict
from .predicates import And from .predicates import And, TypePredicate
from .registers import RegClass, Register, Stack from .registers import RegClass, Register, Stack
from .ast import Apply from .ast import Apply
from .types import ValueType from .types import ValueType
@@ -405,6 +405,13 @@ class Encoding(object):
self.recipe = recipe self.recipe = recipe
self.encbits = encbits 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. # Record specific predicates. Note that the recipe also has predicates.
self.instp = instp self.instp = instp
self.isap = isap self.isap = isap

View File

@@ -23,14 +23,19 @@ predicate, the context is the instruction format.
""" """
from __future__ import absolute_import from __future__ import absolute_import
from functools import reduce from functools import reduce
from .formats import instruction_context
try: try:
from typing import Sequence, Tuple, Set, Any, Union, TYPE_CHECKING # noqa from typing import Sequence, Tuple, Set, Any, Union, TYPE_CHECKING # noqa
if TYPE_CHECKING: 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 from .settings import BoolSetting, SettingGroup # noqa
PredContext = Union[SettingGroup, InstructionFormat] from .types import ValueType # noqa
PredLeaf = Union[BoolSetting, 'FieldPredicate'] from .typevar import TypeVar # noqa
PredContext = Union[SettingGroup, InstructionFormat,
InstructionContext]
PredLeaf = Union[BoolSetting, 'FieldPredicate', 'TypePredicate']
PredNode = Union[PredLeaf, 'Predicate'] PredNode = Union[PredLeaf, 'Predicate']
except ImportError: except ImportError:
pass 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 If a is a parent of b or b is a parent of a, return the descendant of the
two. 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): if _is_parent(a, b):
return b return b
@@ -293,3 +298,69 @@ class IsUnsignedInt(FieldPredicate):
self.scale = scale self.scale = scale
assert width >= 0 and width <= 64 assert width >= 0 and width <= 64
assert scale >= 0 and scale < width 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())

View File

@@ -58,8 +58,9 @@ from collections import OrderedDict, defaultdict
import math import math
from itertools import groupby from itertools import groupby
from cdsl.registers import RegClass, Register, Stack 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.settings import SettingGroup
from cdsl.formats import instruction_context, InstructionFormat
try: try:
from typing import Sequence, Set, Tuple, List, Dict, Iterable, DefaultDict, TYPE_CHECKING # noqa from typing import Sequence, Set, Tuple, List, Dict, Iterable, DefaultDict, TYPE_CHECKING # noqa
@@ -73,8 +74,8 @@ except ImportError:
pass pass
def emit_instp(instp, fmt): def emit_instp(instp, fmt, has_dfg=False):
# type: (PredNode, srcgen.Formatter) -> None # type: (PredNode, srcgen.Formatter, bool) -> None
""" """
Emit code for matching an instruction predicate against an Emit code for matching an instruction predicate against an
`InstructionData` reference called `inst`. `InstructionData` reference called `inst`.
@@ -84,22 +85,42 @@ def emit_instp(instp, fmt):
""" """
iform = instp.predicate_context() 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? # Which fields do we need in the InstructionData pattern match?
has_type_check = False
# Collect the leaf predicates. # Collect the leaf predicates.
leafs = set() # type: Set[PredLeaf] leafs = set() # type: Set[PredLeaf]
instp.predicate_leafs(leafs) instp.predicate_leafs(leafs)
# All the leafs are FieldPredicate instances. Here we just care about # All the leafs are FieldPredicate or TypePredicate instances. Here we just
# the field names. # care about the field names.
fnames = set() # type: Set[str] fnames = set() # type: Set[str]
for p in leafs: for p in leafs:
assert isinstance(p, FieldPredicate) if isinstance(p, FieldPredicate):
fnames.add(p.field.rust_name()) fnames.add(p.field.rust_name())
else:
assert isinstance(p, TypePredicate)
has_type_check = True
fields = ', '.join(sorted(fnames)) fields = ', '.join(sorted(fnames))
with fmt.indented( with fmt.indented(
'if let InstructionData::{} {{ {}, .. }} = *inst {{' 'if let ir::InstructionData::{} {{ {}, .. }} = *inst {{'
.format(iform.name, fields), '}'): .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): def emit_inst_predicates(instps, fmt):
@@ -111,11 +132,9 @@ def emit_inst_predicates(instps, fmt):
for instp in instps: for instp in instps:
name = 'inst_predicate_{}'.format(instp.number) name = 'inst_predicate_{}'.format(instp.number)
with fmt.indented( with fmt.indented(
'fn {}(inst: &InstructionData) -> bool {{' 'fn {}(dfg: &ir::DataFlowGraph, inst: &ir::InstructionData)'
.format(name), '-> bool {{'.format(name), '}'):
'}'): emit_instp(instp, fmt, has_dfg=True)
emit_instp(instp, fmt)
fmt.line('unreachable!();')
# Generate the static table. # Generate the static table.
with fmt.indented( with fmt.indented(
@@ -150,7 +169,7 @@ def emit_recipe_predicates(recipes, fmt):
# Generate the predicate function. # Generate the predicate function.
with fmt.indented( with fmt.indented(
'fn {}({}: ::settings::PredicateView, ' 'fn {}({}: ::settings::PredicateView, '
'inst: &InstructionData) -> bool {{' 'inst: &ir::InstructionData) -> bool {{'
.format( .format(
name, name,
'isap' if isap else '_'), '}'): 'isap' if isap else '_'), '}'):
@@ -160,7 +179,6 @@ def emit_recipe_predicates(recipes, fmt):
'}'): '}'):
fmt.line('return false;') fmt.line('return false;')
emit_instp(instp, fmt) emit_instp(instp, fmt)
fmt.line('unreachable!();')
# Generate the static table. # Generate the static table.
with fmt.indented( with fmt.indented(

View File

@@ -229,11 +229,13 @@ I64.enc(base.uextend.i64.i32, *r.umr(0x89))
# cvtsi2ss # cvtsi2ss
I32.enc(base.fcvt_from_sint.f32.i32, *r.furm(0xf3, 0x0f, 0x2A)) 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.rex(0xf3, 0x0f, 0x2A))
I64.enc(base.fcvt_from_sint.f32.i32, *r.furm(0xf3, 0x0f, 0x2A)) I64.enc(base.fcvt_from_sint.f32.i32, *r.furm(0xf3, 0x0f, 0x2A))
# cvtsi2sd # cvtsi2sd
I32.enc(base.fcvt_from_sint.f64.i32, *r.furm(0xf2, 0x0f, 0x2A)) 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.rex(0xf2, 0x0f, 0x2A))
I64.enc(base.fcvt_from_sint.f64.i32, *r.furm(0xf2, 0x0f, 0x2A)) I64.enc(base.fcvt_from_sint.f64.i32, *r.furm(0xf2, 0x0f, 0x2A))

View File

@@ -20,7 +20,7 @@ pub type RecipePredicate = Option<fn(PredicateView, &InstructionData) -> bool>;
/// ///
/// This is a predicate function that needs to be tested in addition to the recipe predicate. It /// This is a predicate function that needs to be tested in addition to the recipe predicate. It
/// can't depend on ISA settings. /// 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. /// Legalization action to perform when no encoding can be found for an instruction.
/// ///
@@ -105,7 +105,7 @@ impl<OffT: Into<u32> + Copy> Table<Opcode> for [Level2Entry<OffT>] {
/// Returns an iterator that produces legal encodings for `inst`. /// Returns an iterator that produces legal encodings for `inst`.
pub fn lookup_enclist<'a, OffT1, OffT2>(ctrl_typevar: Type, pub fn lookup_enclist<'a, OffT1, OffT2>(ctrl_typevar: Type,
inst: &'a InstructionData, inst: &'a InstructionData,
_dfg: &'a DataFlowGraph, dfg: &'a DataFlowGraph,
level1_table: &'static [Level1Entry<OffT1>], level1_table: &'static [Level1Entry<OffT1>],
level2_table: &'static [Level2Entry<OffT2>], level2_table: &'static [Level2Entry<OffT2>],
enclist: &'static [EncListEntry], enclist: &'static [EncListEntry],
@@ -146,6 +146,7 @@ pub fn lookup_enclist<'a, OffT1, OffT2>(ctrl_typevar: Type,
Encodings::new(offset, Encodings::new(offset,
legalize, legalize,
inst, inst,
dfg,
enclist, enclist,
recipe_preds, recipe_preds,
inst_preds, inst_preds,
@@ -170,6 +171,7 @@ pub struct Encodings<'a> {
// Legalization code to use of no encoding is found. // Legalization code to use of no encoding is found.
legalize: LegalizeCode, legalize: LegalizeCode,
inst: &'a InstructionData, inst: &'a InstructionData,
dfg: &'a DataFlowGraph,
enclist: &'static [EncListEntry], enclist: &'static [EncListEntry],
recipe_preds: &'static [RecipePredicate], recipe_preds: &'static [RecipePredicate],
inst_preds: &'static [InstPredicate], inst_preds: &'static [InstPredicate],
@@ -185,6 +187,7 @@ impl<'a> Encodings<'a> {
pub fn new(offset: usize, pub fn new(offset: usize,
legalize: LegalizeCode, legalize: LegalizeCode,
inst: &'a InstructionData, inst: &'a InstructionData,
dfg: &'a DataFlowGraph,
enclist: &'static [EncListEntry], enclist: &'static [EncListEntry],
recipe_preds: &'static [RecipePredicate], recipe_preds: &'static [RecipePredicate],
inst_preds: &'static [InstPredicate], inst_preds: &'static [InstPredicate],
@@ -193,6 +196,7 @@ impl<'a> Encodings<'a> {
Encodings { Encodings {
offset, offset,
inst, inst,
dfg,
legalize, legalize,
isa_preds, isa_preds,
recipe_preds, recipe_preds,
@@ -222,7 +226,7 @@ impl<'a> Encodings<'a> {
/// 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_preds.get(pred) { if let Some(&p) = self.inst_preds.get(pred) {
p(self.inst) p(self.dfg, self.inst)
} else { } else {
let pred = pred - self.inst_preds.len(); let pred = pred - self.inst_preds.len();
self.isa_preds.test(pred) self.isa_preds.test(pred)

View File

@@ -1,7 +1,6 @@
//! Encoding tables for Intel ISAs. //! Encoding tables for Intel ISAs.
use ir::types; use ir::{self, types, Opcode};
use ir::{Opcode, InstructionData};
use isa::EncInfo; use isa::EncInfo;
use isa::constraints::*; use isa::constraints::*;
use isa::enc_tables::*; use isa::enc_tables::*;

View File

@@ -1,8 +1,7 @@
//! Encoding tables for RISC-V. //! Encoding tables for RISC-V.
use ir::condcodes::IntCC; use ir::condcodes::IntCC;
use ir::types; use ir::{self, types, Opcode};
use ir::{Opcode, InstructionData};
use isa::EncInfo; use isa::EncInfo;
use isa::constraints::*; use isa::constraints::*;
use isa::enc_tables::*; use isa::enc_tables::*;