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:
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
Reference in New Issue
Block a user