From 0e5e8a62c85e717ad53bd1b8756c1a6f2aec4ac8 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Tue, 7 Jul 2020 13:37:37 -0700 Subject: [PATCH] Add `DerivedFunction` for doubling lane widths and halving the number of lanes (i.e. merging) Certain operations (e.g. widening) will have operands with types like `NxM` but will return results with types like `(N*2)x(M/2)` (double the lane width, halve the number of lanes; maintain the same number of vector bits). This is equivalent to applying two `DerivedFunction`s to the type: `DerivedFunction::DoubleWidth` then `DerivedFunction::HalfVector`. Since there is no easy way to apply multiple `DerivedFunction`s (e.g. most of the logic is one-level deep, https://github.com/bytecodealliance/wasmtime/blob/1d5a678124e0f035f7614cafe43066c834a5113b/cranelift/codegen/meta/src/gen_inst.rs#L618-L621), I added `DerivedFunction::MergeLanes` to do the necessary type conversion. --- cranelift/codegen/meta/src/cdsl/typevar.rs | 29 +++++++++++++++++++++- cranelift/codegen/src/ir/instructions.rs | 8 ++++++ cranelift/codegen/src/ir/types.rs | 13 +++++++++- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/cranelift/codegen/meta/src/cdsl/typevar.rs b/cranelift/codegen/meta/src/cdsl/typevar.rs index 0c0b2e9647..752b458b2a 100644 --- a/cranelift/codegen/meta/src/cdsl/typevar.rs +++ b/cranelift/codegen/meta/src/cdsl/typevar.rs @@ -211,6 +211,24 @@ impl TypeVar { "can't double 256 lanes" ); } + DerivedFunc::MergeLanes => { + assert!( + ts.ints.is_empty() || *ts.ints.iter().max().unwrap() < MAX_BITS, + "can't double all integer types" + ); + assert!( + ts.floats.is_empty() || *ts.floats.iter().max().unwrap() < MAX_FLOAT_BITS, + "can't double all float types" + ); + assert!( + ts.bools.is_empty() || *ts.bools.iter().max().unwrap() < MAX_BITS, + "can't double all boolean types" + ); + assert!( + *ts.lanes.iter().min().unwrap() > 1, + "can't halve a scalar type" + ); + } DerivedFunc::LaneOf | DerivedFunc::AsBool => { /* no particular assertions */ } } @@ -248,6 +266,9 @@ impl TypeVar { pub fn split_lanes(&self) -> TypeVar { self.derived(DerivedFunc::SplitLanes) } + pub fn merge_lanes(&self) -> TypeVar { + self.derived(DerivedFunc::MergeLanes) + } /// Constrain the range of types this variable can assume to a subset of those in the typeset /// ts. @@ -355,6 +376,7 @@ pub(crate) enum DerivedFunc { HalfVector, DoubleVector, SplitLanes, + MergeLanes, } impl DerivedFunc { @@ -367,6 +389,7 @@ impl DerivedFunc { DerivedFunc::HalfVector => "half_vector", DerivedFunc::DoubleVector => "double_vector", DerivedFunc::SplitLanes => "split_lanes", + DerivedFunc::MergeLanes => "merge_lanes", } } @@ -377,6 +400,8 @@ impl DerivedFunc { DerivedFunc::DoubleWidth => Some(DerivedFunc::HalfWidth), DerivedFunc::HalfVector => Some(DerivedFunc::DoubleVector), DerivedFunc::DoubleVector => Some(DerivedFunc::HalfVector), + DerivedFunc::MergeLanes => Some(DerivedFunc::SplitLanes), + DerivedFunc::SplitLanes => Some(DerivedFunc::MergeLanes), _ => None, } } @@ -462,6 +487,7 @@ impl TypeSet { DerivedFunc::HalfVector => self.half_vector(), DerivedFunc::DoubleVector => self.double_vector(), DerivedFunc::SplitLanes => self.half_width().double_vector(), + DerivedFunc::MergeLanes => self.double_width().half_vector(), } } @@ -601,7 +627,8 @@ impl TypeSet { DerivedFunc::DoubleWidth => self.half_width(), DerivedFunc::HalfVector => self.double_vector(), DerivedFunc::DoubleVector => self.half_vector(), - DerivedFunc::SplitLanes => self.half_vector().double_width(), + DerivedFunc::SplitLanes => self.double_width().half_vector(), + DerivedFunc::MergeLanes => self.half_width().double_vector(), } } diff --git a/cranelift/codegen/src/ir/instructions.rs b/cranelift/codegen/src/ir/instructions.rs index f835bd5f4a..2ba730b687 100644 --- a/cranelift/codegen/src/ir/instructions.rs +++ b/cranelift/codegen/src/ir/instructions.rs @@ -584,6 +584,9 @@ enum OperandConstraint { /// This operand is `ctrlType.split_lanes()`. SplitLanes, + + /// This operand is `ctrlType.merge_lanes()`. + MergeLanes, } impl OperandConstraint { @@ -615,6 +618,11 @@ impl OperandConstraint { .split_lanes() .expect("invalid type for split_lanes"), ), + MergeLanes => Bound( + ctrl_type + .merge_lanes() + .expect("invalid type for merge_lanes"), + ), } } } diff --git a/cranelift/codegen/src/ir/types.rs b/cranelift/codegen/src/ir/types.rs index 319f3ae66f..c669839da5 100644 --- a/cranelift/codegen/src/ir/types.rs +++ b/cranelift/codegen/src/ir/types.rs @@ -284,7 +284,7 @@ impl Type { /// Split the lane width in half and double the number of lanes to maintain the same bit-width. /// - /// If this is a scalar type of n bits, it produces a SIMD vector type of (n/2)x2. + /// If this is a scalar type of `n` bits, it produces a SIMD vector type of `(n/2)x2`. pub fn split_lanes(self) -> Option { match self.half_width() { Some(half_width) => half_width.by(2), @@ -292,6 +292,17 @@ impl Type { } } + /// Merge lanes to half the number of lanes and double the lane width to maintain the same + /// bit-width. + /// + /// If this is a scalar type, it will return `None`. + pub fn merge_lanes(self) -> Option { + match self.double_width() { + Some(double_width) => double_width.half_vector(), + None => None, + } + } + /// Index of this type, for use with hash tables etc. pub fn index(self) -> usize { usize::from(self.0)