Skip to content

Commit 01d34e7

Browse files
committed
Add x86 legalization for fcvt_from_uint.f32x4
This converts an `i32x4` into an `f32x4` with some rounding either by using an AVX512VL/F instruction--VCVTUDQ2PS--or a long sequence of SSE4.1 compatible instructions.
1 parent 23ed48f commit 01d34e7

File tree

6 files changed

+93
-1
lines changed

6 files changed

+93
-1
lines changed

cranelift/codegen/meta/src/isa/x86/legalize.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ fn define_simd(
380380
let bxor = insts.by_name("bxor");
381381
let extractlane = insts.by_name("extractlane");
382382
let fcmp = insts.by_name("fcmp");
383+
let fcvt_from_uint = insts.by_name("fcvt_from_uint");
383384
let fabs = insts.by_name("fabs");
384385
let fneg = insts.by_name("fneg");
385386
let iadd_imm = insts.by_name("iadd_imm");
@@ -788,6 +789,6 @@ fn define_simd(
788789
narrow.custom_legalize(ushr, "convert_ushr");
789790
narrow.custom_legalize(ishl, "convert_ishl");
790791

791-
// This lives in the expand group to avoid conflicting with, e.g., i128 legalizations.
792792
narrow_avx.custom_legalize(imul, "convert_i64x2_imul");
793+
narrow_avx.custom_legalize(fcvt_from_uint, "expand_fcvt_from_uint_vector");
793794
}

cranelift/codegen/meta/src/isa/x86/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
4848
x86_32.legalize_type(F32, x86_expand);
4949
x86_32.legalize_type(F64, x86_expand);
5050
x86_32.legalize_value_type(VectorType::new(I64.into(), 2), x86_narrow_avx);
51+
x86_32.legalize_value_type(VectorType::new(F32.into(), 4), x86_narrow_avx);
5152

5253
x86_64.legalize_monomorphic(expand_flags);
5354
x86_64.legalize_default(x86_narrow);
@@ -60,6 +61,7 @@ pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
6061
x86_64.legalize_type(F32, x86_expand);
6162
x86_64.legalize_type(F64, x86_expand);
6263
x86_64.legalize_value_type(VectorType::new(I64.into(), 2), x86_narrow_avx);
64+
x86_64.legalize_value_type(VectorType::new(F32.into(), 4), x86_narrow_avx);
6365

6466
let recipes = recipes::define(shared_defs, &settings, &regs);
6567

cranelift/codegen/src/isa/x86/enc_tables.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,9 @@ fn expand_minmax(
598598

599599
/// x86 has no unsigned-to-float conversions. We handle the easy case of zero-extending i32 to
600600
/// i64 with a pattern, the rest needs more code.
601+
///
602+
/// Note that this is the scalar implementation; for the vector implemenation see
603+
/// [expand_fcvt_from_uint_vector].
601604
fn expand_fcvt_from_uint(
602605
inst: ir::Inst,
603606
func: &mut ir::Function,
@@ -679,6 +682,56 @@ fn expand_fcvt_from_uint(
679682
cfg.recompute_block(pos.func, done);
680683
}
681684

685+
/// To convert packed unsigned integers to their float equivalents, we must legalize to a special
686+
/// AVX512 instruction (using MCSR rounding) or use a long sequence of instructions. This logic is
687+
/// separate from [expand_fcvt_from_uint] above (the scalar version), only due to how the transform
688+
/// groups are set up; TODO if we change the SIMD legalization groups, then this logic could be
689+
/// merged into [expand_fcvt_from_uint] (see https://github.com/bytecodealliance/wasmtime/issues/1745).
690+
fn expand_fcvt_from_uint_vector(
691+
inst: ir::Inst,
692+
func: &mut ir::Function,
693+
_cfg: &mut ControlFlowGraph,
694+
isa: &dyn TargetIsa,
695+
) {
696+
let mut pos = FuncCursor::new(func).at_inst(inst);
697+
pos.use_srcloc(inst);
698+
699+
if let ir::InstructionData::Unary {
700+
opcode: ir::Opcode::FcvtFromUint,
701+
arg,
702+
} = pos.func.dfg[inst]
703+
{
704+
let controlling_type = pos.func.dfg.ctrl_typevar(inst);
705+
if controlling_type == F32X4 {
706+
debug_assert_eq!(pos.func.dfg.value_type(arg), I32X4);
707+
let x86_isa = isa
708+
.as_any()
709+
.downcast_ref::<isa::x86::Isa>()
710+
.expect("the target ISA must be x86 at this point");
711+
if x86_isa.isa_flags.use_avx512vl_simd() || x86_isa.isa_flags.use_avx512f_simd() {
712+
// If we have certain AVX512 features, we can lower this instruction simply.
713+
pos.func.dfg.replace(inst).x86_vcvtudq2ps(arg);
714+
} else {
715+
// Otherwise, we default to a very lengthy SSE4.1-compatible sequence: PXOR,
716+
// PBLENDW, PSUB, CVTDQ2PS, PSRLD, CVTDQ2PS, ADDPS, ADDPS
717+
let bitcast_arg = pos.ins().raw_bitcast(I16X8, arg);
718+
let zero_constant = pos.func.dfg.constants.insert(vec![0; 16].into());
719+
let zero = pos.ins().vconst(I16X8, zero_constant);
720+
let low = pos.ins().x86_pblendw(zero, bitcast_arg, 0x55);
721+
let bitcast_low = pos.ins().raw_bitcast(I32X4, low);
722+
let high = pos.ins().isub(arg, bitcast_low);
723+
let convert_low = pos.ins().fcvt_from_sint(F32X4, bitcast_low);
724+
let shift_high = pos.ins().ushr_imm(high, 1);
725+
let convert_high = pos.ins().fcvt_from_sint(F32X4, shift_high);
726+
let double_high = pos.ins().fadd(convert_high, convert_high);
727+
pos.func.dfg.replace(inst).fadd(double_high, convert_low);
728+
}
729+
} else {
730+
unimplemented!("cannot legalize {}", pos.func.dfg.display_inst(inst, None))
731+
}
732+
}
733+
}
734+
682735
fn expand_fcvt_to_sint(
683736
inst: ir::Inst,
684737
func: &mut ir::Function,
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
test legalizer
2+
set enable_simd
3+
target x86_64 skylake has_avx512f=true
4+
5+
function %fcvt_from_uint(i32x4) -> f32x4 {
6+
block0(v0:i32x4):
7+
v1 = fcvt_from_uint.f32x4 v0
8+
; check: v1 = x86_vcvtudq2ps v0
9+
return v1
10+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
test legalizer
2+
set enable_simd
3+
target x86_64 skylake
4+
5+
function %fcvt_from_uint(i32x4) -> f32x4 {
6+
block0(v0:i32x4):
7+
v1 = fcvt_from_uint.f32x4 v0
8+
; check: v2 = raw_bitcast.i16x8 v0
9+
; nextln: v3 = vconst.i16x8 const0
10+
; nextln: v4 = x86_pblendw v3, v2, 85
11+
; nextln: v5 = raw_bitcast.i32x4 v4
12+
; nextln: v6 = isub v0, v5
13+
; nextln: v7 = fcvt_from_sint.f32x4 v5
14+
; nextln: v8 = ushr_imm v6, 1
15+
; nextln: v9 = fcvt_from_sint.f32x4 v8
16+
; nextln: v10 = fadd v9, v9
17+
; nextln: v1 = fadd v10, v7
18+
return v1
19+
}

cranelift/filetests/filetests/isa/x86/simd-conversion-run.clif

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,10 @@ block0:
1313
return v4
1414
}
1515
; run
16+
17+
function %fcvt_from_uint(i32x4) -> f32x4 {
18+
block0(v0:i32x4):
19+
v1 = fcvt_from_uint.f32x4 v0
20+
return v1
21+
}
22+
; run: %fcvt_from_uint([0 0 0 0]) == [0x0.0 0x0.0 0x0.0 0x0.0]

0 commit comments

Comments
 (0)