Legalize unsigned-to-float conversions for Intel 64.

Also make sure we generate type checks for the controlling type variable
in legalization patterns. This is not needed for encodings since the
encoding tables are already keyed on the controlling type variable.
This commit is contained in:
Jakob Stoklund Olesen
2017-09-28 09:05:39 -07:00
parent 979a22f548
commit 34146435e5
6 changed files with 172 additions and 10 deletions

View File

@@ -49,25 +49,47 @@ ebb0(v0: i32):
return v1
}
function %f32_convert_u_i32(i32) -> f32 {
ebb0(v0: i32):
v1 = fcvt_from_uint.f32 v0
return v1
}
function %f64_convert_s_i32(i32) -> f64 {
ebb0(v0: i32):
v1 = fcvt_from_sint.f64 v0
return v1
}
function %f64_convert_u_i32(i32) -> f64 {
ebb0(v0: i32):
v1 = fcvt_from_uint.f64 v0
return v1
}
function %f32_convert_s_i64(i64) -> f32 {
ebb0(v0: i64):
v1 = fcvt_from_sint.f32 v0
return v1
}
function %f32_convert_u_i64(i64) -> f32 {
ebb0(v0: i64):
v1 = fcvt_from_uint.f32 v0
return v1
}
function %f64_convert_s_i64(i64) -> f64 {
ebb0(v0: i64):
v1 = fcvt_from_sint.f64 v0
return v1
}
; TODO: f*_convert_u_i* (Don't exist on Intel).
function %f64_convert_u_i64(i64) -> f64 {
ebb0(v0: i64):
v1 = fcvt_from_uint.f64 v0
return v1
}
function %i32_reinterpret_f32(f32) -> i32 {
ebb0(v0: f32):

View File

@@ -7,7 +7,7 @@ for patern matching an rewriting of cretonne instructions.
from __future__ import absolute_import
from . import instructions
from .typevar import TypeVar
from .predicates import IsEqual, And, TypePredicate
from .predicates import IsEqual, And, TypePredicate, CtrlTypePredicate
try:
from typing import Union, Tuple, Sequence, TYPE_CHECKING, Dict, List # noqa
@@ -383,12 +383,38 @@ class Apply(Expr):
pred = And.combine(pred, IsEqual(ffield, arg))
# Add checks for any bound type variables.
for bound_ty, tv in zip(self.typevars, self.inst.all_typevars()):
if bound_ty is None:
continue
type_chk = TypePredicate.typevar_check(self.inst, tv, bound_ty)
pred = And.combine(pred, type_chk)
# Add checks for any bound secondary type variables.
# We can't check the controlling type variable this way since it may
# not appear as the type of an operand.
if len(self.typevars) > 1:
for bound_ty, tv in zip(self.typevars[1:],
self.inst.other_typevars):
if bound_ty is None:
continue
type_chk = TypePredicate.typevar_check(self.inst, tv, bound_ty)
pred = And.combine(pred, type_chk)
return pred
def inst_predicate_with_ctrl_typevar(self):
# type: () -> PredNode
"""
Same as `inst_predicate()`, but also check the controlling type
variable.
"""
pred = self.inst_predicate()
if len(self.typevars) > 0:
bound_ty = self.typevars[0]
type_chk = None # type: PredNode
if bound_ty is not None:
# Prefer to look at the types of input operands.
if self.inst.use_typevar_operand:
type_chk = TypePredicate.typevar_check(
self.inst, self.inst.ctrl_typevar, bound_ty)
else:
type_chk = CtrlTypePredicate(bound_ty)
pred = And.combine(pred, type_chk)
return pred

View File

@@ -35,7 +35,8 @@ try:
from .typevar import TypeVar # noqa
PredContext = Union[SettingGroup, InstructionFormat,
InstructionContext]
PredLeaf = Union[BoolSetting, 'FieldPredicate', 'TypePredicate']
PredLeaf = Union[BoolSetting, 'FieldPredicate', 'TypePredicate',
'CtrlTypePredicate']
PredNode = Union[PredLeaf, 'Predicate']
# A predicate key is a (recursive) tuple of primitive types that
# uniquely describes a predicate. It is used for interning.
@@ -373,3 +374,42 @@ class TypePredicate(object):
"""
return 'dfg.value_type(args[{}]) == {}'.format(
self.value_arg, self.value_type.rust_name())
class CtrlTypePredicate(object):
"""
An instruction predicate that checks the controlling type variable
:param value_type: The required value type.
"""
def __init__(self, value_type):
# type: (ValueType) -> None
assert value_type is not None
self.value_type = value_type
def __str__(self):
# type: () -> str
return 'ctrl_typevar:{}'.format(self.value_type)
def predicate_context(self):
# type: () -> PredContext
return instruction_context
def predicate_key(self):
# type: () -> PredKey
return ('ctrltypecheck', self.value_type.name)
def predicate_leafs(self, leafs):
# type: (Set[PredLeaf]) -> None
leafs.add(self)
def rust_predicate(self, prec):
# type: (int) -> str
"""
Return Rust code for evaluating this predicate.
It is assumed that the context has `dfg` and `inst` variables.
"""
return 'dfg.ctrl_typevar(inst) == {}'.format(
self.value_type.rust_name())

View File

@@ -199,7 +199,7 @@ def unwrap_inst(iref, node, fmt):
n = expr.inst.value_opnums.index(opnum)
fmt.format('dfg.resolve_aliases(args[{}]),', n)
# Evaluate the instruction predicate, if any.
instp = expr.inst_predicate()
instp = expr.inst_predicate_with_ctrl_typevar()
fmt.line(instp.rust_predicate(0) if instp else 'true')
fmt.outdented_line('} else {')
fmt.line('unreachable!("bad instruction format")')

View File

@@ -96,3 +96,6 @@ for cc, rev_cc in [
# We need to modify the CFG for min/max legalization.
intel_expand.custom_legalize(insts.fmin, 'expand_minmax')
intel_expand.custom_legalize(insts.fmax, 'expand_minmax')
# Conversions from unsigned need special handling.
intel_expand.custom_legalize(insts.fcvt_from_uint, 'expand_fcvt_from_uint')

View File

@@ -62,6 +62,7 @@ fn expand_minmax(inst: ir::Inst, func: &mut ir::Function, cfg: &mut ControlFlowG
// Test for case 1) ordered and not equal.
let mut pos = FuncCursor::new(func).at_inst(inst);
pos.use_srcloc(inst);
let cmp_ueq = pos.ins().fcmp(FloatCC::UnorderedOrEqual, x, y);
pos.ins().brnz(cmp_ueq, ueq_ebb, &[]);
@@ -101,3 +102,73 @@ fn expand_minmax(inst: ir::Inst, func: &mut ir::Function, cfg: &mut ControlFlowG
cfg.recompute_ebb(pos.func, uno_ebb);
cfg.recompute_ebb(pos.func, done);
}
/// Intel has no unsigned-to-float conversions. We handle the easy case of zero-extending i32 to
/// i64 with a pattern, the rest needs more code.
fn expand_fcvt_from_uint(inst: ir::Inst, func: &mut ir::Function, cfg: &mut ControlFlowGraph) {
use ir::condcodes::IntCC;
let x;
match func.dfg[inst] {
ir::InstructionData::Unary {
opcode: ir::Opcode::FcvtFromUint,
arg,
} => x = arg,
_ => panic!("Need fcvt_from_uint: {}", func.dfg.display_inst(inst, None)),
}
let xty = func.dfg.value_type(x);
let result = func.dfg.first_result(inst);
let ty = func.dfg.value_type(result);
let mut pos = FuncCursor::new(func).at_inst(inst);
pos.use_srcloc(inst);
// Conversion from unsigned 32-bit is easy on x86-64.
// TODO: This should be guarded by an ISA check.
if xty == ir::types::I32 {
let wide = pos.ins().uextend(ir::types::I64, x);
pos.func.dfg.replace(inst).fcvt_from_sint(ty, wide);
return;
}
let old_ebb = pos.func.layout.pp_ebb(inst);
// EBB handling the case where x < 0.
let neg_ebb = pos.func.dfg.make_ebb();
// Final EBB with one argument representing the final result value.
let done = pos.func.dfg.make_ebb();
// Move the `inst` result value onto the `done` EBB.
pos.func.dfg.clear_results(inst);
pos.func.dfg.attach_ebb_arg(done, result);
// If x as a signed int is not negative, we can use the existing `fcvt_from_sint` instruction.
let is_neg = pos.ins().icmp_imm(IntCC::SignedLessThan, x, 0);
pos.ins().brnz(is_neg, neg_ebb, &[]);
// Easy case: just use a signed conversion.
let posres = pos.ins().fcvt_from_sint(ty, x);
pos.ins().jump(done, &[posres]);
// Now handle the negative case.
pos.insert_ebb(neg_ebb);
// Divide x by two to get it in range for the signed conversion, keep the LSB, and scale it
// back up on the FP side.
let ihalf = pos.ins().ushr_imm(x, 1);
let lsb = pos.ins().band_imm(x, 1);
let ifinal = pos.ins().bor(ihalf, lsb);
let fhalf = pos.ins().fcvt_from_sint(ty, ifinal);
let negres = pos.ins().fadd(fhalf, fhalf);
// Recycle the original instruction as a jump.
pos.func.dfg.replace(inst).jump(done, &[negres]);
// Finally insert a label for the completion.
pos.next_inst();
pos.insert_ebb(done);
cfg.recompute_ebb(pos.func, old_ebb);
cfg.recompute_ebb(pos.func, neg_ebb);
cfg.recompute_ebb(pos.func, done);
}