[RFC] Dynamic Vector Support (#4200)
Introduce a new concept in the IR that allows a producer to create dynamic vector types. An IR function can now contain global value(s) that represent a dynamic scaling factor, for a given fixed-width vector type. A dynamic type is then created by 'multiplying' the corresponding global value with a fixed-width type. These new types can be used just like the existing types and the type system has a set of hard-coded dynamic types, such as I32X4XN, which the user defined types map onto. The dynamic types are also used explicitly to create dynamic stack slots, which have no set size like their existing counterparts. New IR instructions are added to access these new stack entities. Currently, during codegen, the dynamic scaling factor has to be lowered to a constant so the dynamic slots do eventually have a compile-time known size, as do spill slots. The current lowering for aarch64 just targets Neon, using a dynamic scale of 1. Copyright (c) 2022, Arm Limited.
This commit is contained in:
@@ -20,6 +20,7 @@ pub(crate) enum ValueType {
|
||||
Reference(ReferenceType),
|
||||
Special(SpecialType),
|
||||
Vector(VectorType),
|
||||
DynamicVector(DynamicVectorType),
|
||||
}
|
||||
|
||||
impl ValueType {
|
||||
@@ -44,6 +45,7 @@ impl ValueType {
|
||||
ValueType::Reference(r) => r.doc(),
|
||||
ValueType::Special(s) => s.doc(),
|
||||
ValueType::Vector(ref v) => v.doc(),
|
||||
ValueType::DynamicVector(ref v) => v.doc(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +56,7 @@ impl ValueType {
|
||||
ValueType::Reference(r) => r.lane_bits(),
|
||||
ValueType::Special(s) => s.lane_bits(),
|
||||
ValueType::Vector(ref v) => v.lane_bits(),
|
||||
ValueType::DynamicVector(ref v) => v.lane_bits(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +80,7 @@ impl ValueType {
|
||||
ValueType::Reference(r) => r.number(),
|
||||
ValueType::Special(s) => s.number(),
|
||||
ValueType::Vector(ref v) => v.number(),
|
||||
ValueType::DynamicVector(ref v) => v.number(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +102,7 @@ impl fmt::Display for ValueType {
|
||||
ValueType::Reference(r) => r.fmt(f),
|
||||
ValueType::Special(s) => s.fmt(f),
|
||||
ValueType::Vector(ref v) => v.fmt(f),
|
||||
ValueType::DynamicVector(ref v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,6 +135,13 @@ impl From<VectorType> for ValueType {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a ValueType from a given dynamic vector type.
|
||||
impl From<DynamicVectorType> for ValueType {
|
||||
fn from(vector: DynamicVectorType) -> Self {
|
||||
ValueType::DynamicVector(vector)
|
||||
}
|
||||
}
|
||||
|
||||
/// A concrete scalar type that can appear as a vector lane too.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(crate) enum LaneType {
|
||||
@@ -230,6 +242,10 @@ impl LaneType {
|
||||
ValueType::Vector(VectorType::new(self, lanes.into()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_dynamic(self, lanes: u16) -> ValueType {
|
||||
ValueType::DynamicVector(DynamicVectorType::new(self, lanes.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LaneType {
|
||||
@@ -380,6 +396,80 @@ impl fmt::Debug for VectorType {
|
||||
}
|
||||
}
|
||||
|
||||
/// A concrete dynamic SIMD vector type.
|
||||
///
|
||||
/// A vector type has a lane type which is an instance of `LaneType`,
|
||||
/// and a positive number of lanes.
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct DynamicVectorType {
|
||||
base: LaneType,
|
||||
unscaled_lanes: u64,
|
||||
}
|
||||
|
||||
impl DynamicVectorType {
|
||||
/// Initialize a new type with `base` lane type and a minimum number of lanes.
|
||||
pub fn new(base: LaneType, unscaled_lanes: u64) -> Self {
|
||||
Self {
|
||||
base,
|
||||
unscaled_lanes,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a string containing the documentation comment for this vector type.
|
||||
pub fn doc(&self) -> String {
|
||||
format!(
|
||||
"A dynamically-scaled SIMD vector with a minimum of {} lanes containing `{}` bits each.",
|
||||
self.unscaled_lanes,
|
||||
self.base
|
||||
)
|
||||
}
|
||||
|
||||
/// Return the number of bits in a lane.
|
||||
pub fn lane_bits(&self) -> u64 {
|
||||
self.base.lane_bits()
|
||||
}
|
||||
|
||||
/// Return the number of lanes.
|
||||
pub fn minimum_lane_count(&self) -> u64 {
|
||||
self.unscaled_lanes
|
||||
}
|
||||
|
||||
/// Return the lane type.
|
||||
pub fn lane_type(&self) -> LaneType {
|
||||
self.base
|
||||
}
|
||||
|
||||
/// Find the unique number associated with this vector type.
|
||||
///
|
||||
/// Dynamic vector types are encoded in the same manner as `VectorType`,
|
||||
/// with lane type in the low 4 bits and the log2(lane_count). We add the
|
||||
/// `VECTOR_BASE` to move these numbers into the range beyond the fixed
|
||||
/// SIMD types.
|
||||
pub fn number(&self) -> u16 {
|
||||
let base_num = u32::from(self.base.number());
|
||||
let lanes_log_2: u32 = 63 - self.minimum_lane_count().leading_zeros();
|
||||
let num = 0x80 + (lanes_log_2 << 4) + base_num;
|
||||
num as u16
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DynamicVectorType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}x{}xN", self.base, self.minimum_lane_count())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for DynamicVectorType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"DynamicVectorType(base={}, lanes={})",
|
||||
self.base,
|
||||
self.minimum_lane_count(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A concrete scalar type that is neither a vector nor a lane type.
|
||||
///
|
||||
/// Special types cannot be used to form vectors.
|
||||
|
||||
@@ -68,10 +68,19 @@ impl TypeVar {
|
||||
ValueType::Vector(vec_type) => {
|
||||
(vec_type.lane_type(), vec_type.lane_count() as RangeBound)
|
||||
}
|
||||
ValueType::DynamicVector(vec_type) => (
|
||||
vec_type.lane_type(),
|
||||
vec_type.minimum_lane_count() as RangeBound,
|
||||
),
|
||||
};
|
||||
|
||||
builder = builder.simd_lanes(num_lanes..num_lanes);
|
||||
|
||||
// Only generate dynamic types for multiple lanes.
|
||||
if num_lanes > 1 {
|
||||
builder = builder.dynamic_simd_lanes(num_lanes..num_lanes);
|
||||
}
|
||||
|
||||
let builder = match scalar_type {
|
||||
LaneType::Int(int_type) => {
|
||||
let bits = int_type as RangeBound;
|
||||
@@ -229,7 +238,9 @@ impl TypeVar {
|
||||
"can't halve a scalar type"
|
||||
);
|
||||
}
|
||||
DerivedFunc::LaneOf | DerivedFunc::AsBool => { /* no particular assertions */ }
|
||||
DerivedFunc::LaneOf | DerivedFunc::AsBool | DerivedFunc::DynamicToVector => {
|
||||
/* no particular assertions */
|
||||
}
|
||||
}
|
||||
|
||||
TypeVar {
|
||||
@@ -269,6 +280,9 @@ impl TypeVar {
|
||||
pub fn merge_lanes(&self) -> TypeVar {
|
||||
self.derived(DerivedFunc::MergeLanes)
|
||||
}
|
||||
pub fn dynamic_to_vector(&self) -> TypeVar {
|
||||
self.derived(DerivedFunc::DynamicToVector)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<TypeVar> for &TypeVar {
|
||||
@@ -331,6 +345,7 @@ pub(crate) enum DerivedFunc {
|
||||
DoubleVector,
|
||||
SplitLanes,
|
||||
MergeLanes,
|
||||
DynamicToVector,
|
||||
}
|
||||
|
||||
impl DerivedFunc {
|
||||
@@ -344,6 +359,7 @@ impl DerivedFunc {
|
||||
DerivedFunc::DoubleVector => "double_vector",
|
||||
DerivedFunc::SplitLanes => "split_lanes",
|
||||
DerivedFunc::MergeLanes => "merge_lanes",
|
||||
DerivedFunc::DynamicToVector => "dynamic_to_vector",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -385,6 +401,7 @@ macro_rules! num_set {
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct TypeSet {
|
||||
pub lanes: NumSet,
|
||||
pub dynamic_lanes: NumSet,
|
||||
pub ints: NumSet,
|
||||
pub floats: NumSet,
|
||||
pub bools: NumSet,
|
||||
@@ -395,6 +412,7 @@ pub(crate) struct TypeSet {
|
||||
impl TypeSet {
|
||||
fn new(
|
||||
lanes: NumSet,
|
||||
dynamic_lanes: NumSet,
|
||||
ints: NumSet,
|
||||
floats: NumSet,
|
||||
bools: NumSet,
|
||||
@@ -403,6 +421,7 @@ impl TypeSet {
|
||||
) -> Self {
|
||||
Self {
|
||||
lanes,
|
||||
dynamic_lanes,
|
||||
ints,
|
||||
floats,
|
||||
bools,
|
||||
@@ -415,6 +434,8 @@ impl TypeSet {
|
||||
pub fn size(&self) -> usize {
|
||||
self.lanes.len()
|
||||
* (self.ints.len() + self.floats.len() + self.bools.len() + self.refs.len())
|
||||
+ self.dynamic_lanes.len()
|
||||
* (self.ints.len() + self.floats.len() + self.bools.len() + self.refs.len())
|
||||
+ self.specials.len()
|
||||
}
|
||||
|
||||
@@ -429,6 +450,7 @@ impl TypeSet {
|
||||
DerivedFunc::DoubleVector => self.double_vector(),
|
||||
DerivedFunc::SplitLanes => self.half_width().double_vector(),
|
||||
DerivedFunc::MergeLanes => self.double_width().half_vector(),
|
||||
DerivedFunc::DynamicToVector => self.dynamic_to_vector(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,6 +529,19 @@ impl TypeSet {
|
||||
copy
|
||||
}
|
||||
|
||||
fn dynamic_to_vector(&self) -> TypeSet {
|
||||
let mut copy = self.clone();
|
||||
copy.lanes = NumSet::from_iter(
|
||||
self.dynamic_lanes
|
||||
.iter()
|
||||
.filter(|&&x| x < MAX_LANES)
|
||||
.map(|&x| x),
|
||||
);
|
||||
copy.specials = Vec::new();
|
||||
copy.dynamic_lanes = NumSet::new();
|
||||
copy
|
||||
}
|
||||
|
||||
fn concrete_types(&self) -> Vec<ValueType> {
|
||||
let mut ret = Vec::new();
|
||||
for &num_lanes in &self.lanes {
|
||||
@@ -523,6 +558,17 @@ impl TypeSet {
|
||||
ret.push(ReferenceType::ref_from_bits(bits).into());
|
||||
}
|
||||
}
|
||||
for &num_lanes in &self.dynamic_lanes {
|
||||
for &bits in &self.ints {
|
||||
ret.push(LaneType::int_from_bits(bits).to_dynamic(num_lanes));
|
||||
}
|
||||
for &bits in &self.floats {
|
||||
ret.push(LaneType::float_from_bits(bits).to_dynamic(num_lanes));
|
||||
}
|
||||
for &bits in &self.bools {
|
||||
ret.push(LaneType::bool_from_bits(bits).to_dynamic(num_lanes));
|
||||
}
|
||||
}
|
||||
for &special in &self.specials {
|
||||
ret.push(special.into());
|
||||
}
|
||||
@@ -548,6 +594,12 @@ impl fmt::Debug for TypeSet {
|
||||
Vec::from_iter(self.lanes.iter().map(|x| x.to_string())).join(", ")
|
||||
));
|
||||
}
|
||||
if !self.dynamic_lanes.is_empty() {
|
||||
subsets.push(format!(
|
||||
"dynamic_lanes={{{}}}",
|
||||
Vec::from_iter(self.dynamic_lanes.iter().map(|x| x.to_string())).join(", ")
|
||||
));
|
||||
}
|
||||
if !self.ints.is_empty() {
|
||||
subsets.push(format!(
|
||||
"ints={{{}}}",
|
||||
@@ -591,6 +643,7 @@ pub(crate) struct TypeSetBuilder {
|
||||
refs: Interval,
|
||||
includes_scalars: bool,
|
||||
simd_lanes: Interval,
|
||||
dynamic_simd_lanes: Interval,
|
||||
specials: Vec<SpecialType>,
|
||||
}
|
||||
|
||||
@@ -603,6 +656,7 @@ impl TypeSetBuilder {
|
||||
refs: Interval::None,
|
||||
includes_scalars: true,
|
||||
simd_lanes: Interval::None,
|
||||
dynamic_simd_lanes: Interval::None,
|
||||
specials: Vec::new(),
|
||||
}
|
||||
}
|
||||
@@ -636,6 +690,11 @@ impl TypeSetBuilder {
|
||||
self.simd_lanes = interval.into();
|
||||
self
|
||||
}
|
||||
pub fn dynamic_simd_lanes(mut self, interval: impl Into<Interval>) -> Self {
|
||||
assert!(self.dynamic_simd_lanes == Interval::None);
|
||||
self.dynamic_simd_lanes = interval.into();
|
||||
self
|
||||
}
|
||||
pub fn specials(mut self, specials: Vec<SpecialType>) -> Self {
|
||||
assert!(self.specials.is_empty());
|
||||
self.specials = specials;
|
||||
@@ -652,6 +711,7 @@ impl TypeSetBuilder {
|
||||
|
||||
TypeSet::new(
|
||||
range_to_set(self.simd_lanes.to_range(min_lanes..MAX_LANES, Some(1))),
|
||||
range_to_set(self.dynamic_simd_lanes.to_range(2..MAX_LANES, None)),
|
||||
range_to_set(self.ints.to_range(8..MAX_BITS, None)),
|
||||
range_to_set(self.floats.to_range(32..64, None)),
|
||||
bools,
|
||||
@@ -770,6 +830,50 @@ fn test_typevar_builder() {
|
||||
assert!(type_set.bools.is_empty());
|
||||
assert!(type_set.specials.is_empty());
|
||||
|
||||
let type_set = TypeSetBuilder::new()
|
||||
.floats(Interval::All)
|
||||
.simd_lanes(Interval::All)
|
||||
.includes_scalars(false)
|
||||
.build();
|
||||
assert_eq!(type_set.lanes, num_set![2, 4, 8, 16, 32, 64, 128, 256]);
|
||||
assert_eq!(type_set.floats, num_set![32, 64]);
|
||||
assert!(type_set.dynamic_lanes.is_empty());
|
||||
assert!(type_set.ints.is_empty());
|
||||
assert!(type_set.bools.is_empty());
|
||||
assert!(type_set.specials.is_empty());
|
||||
|
||||
let type_set = TypeSetBuilder::new()
|
||||
.ints(Interval::All)
|
||||
.bools(Interval::All)
|
||||
.floats(Interval::All)
|
||||
.dynamic_simd_lanes(Interval::All)
|
||||
.includes_scalars(false)
|
||||
.build();
|
||||
assert_eq!(
|
||||
type_set.dynamic_lanes,
|
||||
num_set![2, 4, 8, 16, 32, 64, 128, 256]
|
||||
);
|
||||
assert_eq!(type_set.ints, num_set![8, 16, 32, 64, 128]);
|
||||
assert_eq!(type_set.bools, num_set![1, 8, 16, 32, 64, 128]);
|
||||
assert_eq!(type_set.floats, num_set![32, 64]);
|
||||
assert_eq!(type_set.lanes, num_set![1]);
|
||||
assert!(type_set.specials.is_empty());
|
||||
|
||||
let type_set = TypeSetBuilder::new()
|
||||
.floats(Interval::All)
|
||||
.dynamic_simd_lanes(Interval::All)
|
||||
.includes_scalars(false)
|
||||
.build();
|
||||
assert_eq!(
|
||||
type_set.dynamic_lanes,
|
||||
num_set![2, 4, 8, 16, 32, 64, 128, 256]
|
||||
);
|
||||
assert_eq!(type_set.floats, num_set![32, 64]);
|
||||
assert_eq!(type_set.lanes, num_set![1]);
|
||||
assert!(type_set.ints.is_empty());
|
||||
assert!(type_set.bools.is_empty());
|
||||
assert!(type_set.specials.is_empty());
|
||||
|
||||
let type_set = TypeSetBuilder::new().ints(16..64).build();
|
||||
assert_eq!(type_set.lanes, num_set![1]);
|
||||
assert_eq!(type_set.ints, num_set![16, 32, 64]);
|
||||
@@ -778,6 +882,45 @@ fn test_typevar_builder() {
|
||||
assert!(type_set.specials.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dynamic_to_vector() {
|
||||
// We don't generate single lane dynamic types, so the maximum number of
|
||||
// lanes we support is 128, as MAX_BITS is 256.
|
||||
assert_eq!(
|
||||
TypeSetBuilder::new()
|
||||
.dynamic_simd_lanes(Interval::All)
|
||||
.ints(Interval::All)
|
||||
.build()
|
||||
.dynamic_to_vector(),
|
||||
TypeSetBuilder::new()
|
||||
.simd_lanes(2..128)
|
||||
.ints(Interval::All)
|
||||
.build()
|
||||
);
|
||||
assert_eq!(
|
||||
TypeSetBuilder::new()
|
||||
.dynamic_simd_lanes(Interval::All)
|
||||
.bools(Interval::All)
|
||||
.build()
|
||||
.dynamic_to_vector(),
|
||||
TypeSetBuilder::new()
|
||||
.simd_lanes(2..128)
|
||||
.bools(Interval::All)
|
||||
.build()
|
||||
);
|
||||
assert_eq!(
|
||||
TypeSetBuilder::new()
|
||||
.dynamic_simd_lanes(Interval::All)
|
||||
.floats(Interval::All)
|
||||
.build()
|
||||
.dynamic_to_vector(),
|
||||
TypeSetBuilder::new()
|
||||
.simd_lanes(2..128)
|
||||
.floats(Interval::All)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_typevar_builder_too_high_bound_panic() {
|
||||
|
||||
@@ -688,6 +688,7 @@ pub(crate) fn gen_typesets_table(type_sets: &UniqueTable<TypeSet>, fmt: &mut For
|
||||
fmt.indent(|fmt| {
|
||||
fmt.comment(typeset_to_string(ts));
|
||||
gen_bitset(&ts.lanes, "lanes", 16, fmt);
|
||||
gen_bitset(&ts.dynamic_lanes, "dynamic_lanes", 16, fmt);
|
||||
gen_bitset(&ts.ints, "ints", 8, fmt);
|
||||
gen_bitset(&ts.floats, "floats", 8, fmt);
|
||||
gen_bitset(&ts.bools, "bools", 8, fmt);
|
||||
|
||||
@@ -119,7 +119,7 @@ fn gen_to_and_from_str(name: &str, values: &[&'static str], fmt: &mut Formatter)
|
||||
});
|
||||
fmtln!(fmt, "}");
|
||||
|
||||
fmtln!(fmt, "impl str::FromStr for {} {{", name);
|
||||
fmtln!(fmt, "impl core::str::FromStr for {} {{", name);
|
||||
fmt.indent(|fmt| {
|
||||
fmtln!(fmt, "type Err = ();");
|
||||
fmtln!(fmt, "fn from_str(s: &str) -> Result<Self, Self::Err> {");
|
||||
|
||||
@@ -33,6 +33,19 @@ fn emit_vectors(bits: u64, fmt: &mut srcgen::Formatter) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit definition for all dynamic vector types with `bits` total size.
|
||||
fn emit_dynamic_vectors(bits: u64, fmt: &mut srcgen::Formatter) {
|
||||
let vec_size: u64 = bits / 8;
|
||||
for vec in cdsl_types::ValueType::all_lane_types()
|
||||
.map(|ty| (ty, cdsl_types::ValueType::from(ty).membytes()))
|
||||
.filter(|&(_, lane_size)| lane_size != 0 && lane_size < vec_size)
|
||||
.map(|(ty, lane_size)| (ty, vec_size / lane_size))
|
||||
.map(|(ty, lanes)| cdsl_types::DynamicVectorType::new(ty, lanes))
|
||||
{
|
||||
emit_type(&cdsl_types::ValueType::from(vec), fmt);
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit types using the given formatter object.
|
||||
fn emit_types(fmt: &mut srcgen::Formatter) {
|
||||
// Emit all of the special types, such as types for CPU flags.
|
||||
@@ -51,8 +64,10 @@ fn emit_types(fmt: &mut srcgen::Formatter) {
|
||||
}
|
||||
|
||||
// Emit vector definitions for common SIMD sizes.
|
||||
// Emit dynamic vector definitions.
|
||||
for vec_size in &[64_u64, 128, 256, 512] {
|
||||
emit_vectors(*vec_size, fmt);
|
||||
emit_dynamic_vectors(*vec_size, fmt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@ pub(crate) struct EntityRefs {
|
||||
/// A reference to a stack slot declared in the function preamble.
|
||||
pub(crate) stack_slot: OperandKind,
|
||||
|
||||
/// A reference to a dynamic_stack slot declared in the function preamble.
|
||||
pub(crate) dynamic_stack_slot: OperandKind,
|
||||
|
||||
/// A reference to a global value.
|
||||
pub(crate) global_value: OperandKind,
|
||||
|
||||
@@ -52,6 +55,12 @@ impl EntityRefs {
|
||||
),
|
||||
stack_slot: new("stack_slot", "ir::StackSlot", "A stack slot"),
|
||||
|
||||
dynamic_stack_slot: new(
|
||||
"dynamic_stack_slot",
|
||||
"ir::DynamicStackSlot",
|
||||
"A dynamic stack slot",
|
||||
),
|
||||
|
||||
global_value: new("global_value", "ir::GlobalValue", "A global value."),
|
||||
|
||||
sig_ref: new("sig_ref", "ir::SigRef", "A function signature."),
|
||||
|
||||
@@ -34,6 +34,8 @@ pub(crate) struct Formats {
|
||||
pub(crate) shuffle: Rc<InstructionFormat>,
|
||||
pub(crate) stack_load: Rc<InstructionFormat>,
|
||||
pub(crate) stack_store: Rc<InstructionFormat>,
|
||||
pub(crate) dynamic_stack_load: Rc<InstructionFormat>,
|
||||
pub(crate) dynamic_stack_store: Rc<InstructionFormat>,
|
||||
pub(crate) store: Rc<InstructionFormat>,
|
||||
pub(crate) store_no_offset: Rc<InstructionFormat>,
|
||||
pub(crate) table_addr: Rc<InstructionFormat>,
|
||||
@@ -230,6 +232,15 @@ impl Formats {
|
||||
.imm(&imm.offset32)
|
||||
.build(),
|
||||
|
||||
dynamic_stack_load: Builder::new("DynamicStackLoad")
|
||||
.imm(&entities.dynamic_stack_slot)
|
||||
.build(),
|
||||
|
||||
dynamic_stack_store: Builder::new("DynamicStackStore")
|
||||
.value()
|
||||
.imm(&entities.dynamic_stack_slot)
|
||||
.build(),
|
||||
|
||||
// Accessing a WebAssembly heap.
|
||||
heap_addr: Builder::new("HeapAddr")
|
||||
.imm(&entities.heap)
|
||||
|
||||
@@ -427,6 +427,7 @@ fn define_simd_lane_access(
|
||||
.floats(Interval::All)
|
||||
.bools(Interval::All)
|
||||
.simd_lanes(Interval::All)
|
||||
.dynamic_simd_lanes(Interval::All)
|
||||
.includes_scalars(false)
|
||||
.build(),
|
||||
);
|
||||
@@ -706,6 +707,7 @@ pub(crate) fn define(
|
||||
TypeSetBuilder::new()
|
||||
.ints(Interval::All)
|
||||
.simd_lanes(Interval::All)
|
||||
.dynamic_simd_lanes(Interval::All)
|
||||
.build(),
|
||||
);
|
||||
|
||||
@@ -785,6 +787,7 @@ pub(crate) fn define(
|
||||
.floats(Interval::All)
|
||||
.simd_lanes(Interval::All)
|
||||
.refs(Interval::All)
|
||||
.dynamic_simd_lanes(Interval::All)
|
||||
.build(),
|
||||
);
|
||||
|
||||
@@ -793,6 +796,7 @@ pub(crate) fn define(
|
||||
let addr = &Operand::new("addr", iAddr);
|
||||
|
||||
let SS = &Operand::new("SS", &entities.stack_slot);
|
||||
let DSS = &Operand::new("DSS", &entities.dynamic_stack_slot);
|
||||
let Offset = &Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address");
|
||||
let x = &Operand::new("x", Mem).with_doc("Value to be stored");
|
||||
let a = &Operand::new("a", Mem).with_doc("Value loaded");
|
||||
@@ -1163,7 +1167,51 @@ pub(crate) fn define(
|
||||
.operands_out(vec![addr]),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"dynamic_stack_load",
|
||||
r#"
|
||||
Load a value from a dynamic stack slot.
|
||||
|
||||
This is a polymorphic instruction that can load any value type which
|
||||
has a memory representation.
|
||||
"#,
|
||||
&formats.dynamic_stack_load,
|
||||
)
|
||||
.operands_in(vec![DSS])
|
||||
.operands_out(vec![a])
|
||||
.can_load(true),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"dynamic_stack_store",
|
||||
r#"
|
||||
Store a value to a dynamic stack slot.
|
||||
|
||||
This is a polymorphic instruction that can store any dynamic value type with a
|
||||
memory representation.
|
||||
"#,
|
||||
&formats.dynamic_stack_store,
|
||||
)
|
||||
.operands_in(vec![x, DSS])
|
||||
.can_store(true),
|
||||
);
|
||||
|
||||
let GV = &Operand::new("GV", &entities.global_value);
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"dynamic_stack_addr",
|
||||
r#"
|
||||
Get the address of a dynamic stack slot.
|
||||
|
||||
Compute the absolute address of the first byte of a dynamic stack slot.
|
||||
"#,
|
||||
&formats.dynamic_stack_load,
|
||||
)
|
||||
.operands_in(vec![DSS])
|
||||
.operands_out(vec![addr]),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
@@ -2786,6 +2834,7 @@ pub(crate) fn define(
|
||||
TypeSetBuilder::new()
|
||||
.floats(Interval::All)
|
||||
.simd_lanes(Interval::All)
|
||||
.dynamic_simd_lanes(Interval::All)
|
||||
.build(),
|
||||
);
|
||||
let Cond = &Operand::new("Cond", &imm.floatcc);
|
||||
@@ -3409,6 +3458,7 @@ pub(crate) fn define(
|
||||
TypeSetBuilder::new()
|
||||
.ints(16..64)
|
||||
.simd_lanes(2..8)
|
||||
.dynamic_simd_lanes(2..8)
|
||||
.includes_scalars(false)
|
||||
.build(),
|
||||
);
|
||||
@@ -3479,6 +3529,7 @@ pub(crate) fn define(
|
||||
TypeSetBuilder::new()
|
||||
.ints(8..32)
|
||||
.simd_lanes(4..16)
|
||||
.dynamic_simd_lanes(4..16)
|
||||
.includes_scalars(false)
|
||||
.build(),
|
||||
);
|
||||
@@ -4063,4 +4114,30 @@ pub(crate) fn define(
|
||||
)
|
||||
.other_side_effects(true),
|
||||
);
|
||||
|
||||
let TxN = &TypeVar::new(
|
||||
"TxN",
|
||||
"A dynamic vector type",
|
||||
TypeSetBuilder::new()
|
||||
.ints(Interval::All)
|
||||
.floats(Interval::All)
|
||||
.bools(Interval::All)
|
||||
.dynamic_simd_lanes(Interval::All)
|
||||
.build(),
|
||||
);
|
||||
let x = &Operand::new("x", TxN).with_doc("The dynamic vector to extract from");
|
||||
let y = &Operand::new("y", &imm.uimm8).with_doc("128-bit vector index");
|
||||
let a = &Operand::new("a", &TxN.dynamic_to_vector()).with_doc("New fixed vector");
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"extract_vector",
|
||||
r#"
|
||||
Return a fixed length sub vector, extracted from a dynamic vector.
|
||||
"#,
|
||||
&formats.binary_imm8,
|
||||
)
|
||||
.operands_in(vec![x, y])
|
||||
.operands_out(vec![a]),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user