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:
@@ -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):
|
||||
|
||||
@@ -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,8 +383,12 @@ 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()):
|
||||
# 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)
|
||||
@@ -392,6 +396,28 @@ class Apply(Expr):
|
||||
|
||||
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
|
||||
|
||||
def copy(self, m):
|
||||
# type: (VarAtomMap) -> Apply
|
||||
"""
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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")')
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user