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 637966dc7f
commit 84aeb3eb56
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
; 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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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())

View File

@@ -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)
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(

View File

@@ -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))

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
/// 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<OffT: Into<u32> + Copy> Table<Opcode> for [Level2Entry<OffT>] {
/// 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<OffT1>],
level2_table: &'static [Level2Entry<OffT2>],
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)

View File

@@ -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::*;

View File

@@ -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::*;