[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:
Sam Parker
2022-07-07 20:54:39 +01:00
committed by GitHub
parent 9ae060a12a
commit 9c43749dfe
69 changed files with 2422 additions and 294 deletions

View File

@@ -4,10 +4,18 @@
//!
use crate::entity::PrimaryMap;
use crate::ir::entities::{DynamicStackSlot, DynamicType};
use crate::ir::StackSlot;
use core::fmt;
use core::str::FromStr;
/// imports only needed for testing.
#[allow(unused_imports)]
use crate::ir::{DynamicTypeData, GlobalValueData};
#[allow(unused_imports)]
use crate::ir::types::*;
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
@@ -25,6 +33,9 @@ pub enum StackSlotKind {
/// An explicit stack slot. This is a chunk of stack memory for use by the `stack_load`
/// and `stack_store` instructions.
ExplicitSlot,
/// An explicit stack slot for dynamic vector types. This is a chunk of stack memory
/// for use by the `dynamic_stack_load` and `dynamic_stack_store` instructions.
ExplicitDynamicSlot,
}
impl FromStr for StackSlotKind {
@@ -34,6 +45,7 @@ impl FromStr for StackSlotKind {
use self::StackSlotKind::*;
match s {
"explicit_slot" => Ok(ExplicitSlot),
"explicit_dynamic_slot" => Ok(ExplicitDynamicSlot),
_ => Err(()),
}
}
@@ -44,6 +56,7 @@ impl fmt::Display for StackSlotKind {
use self::StackSlotKind::*;
f.write_str(match *self {
ExplicitSlot => "explicit_slot",
ExplicitDynamicSlot => "explicit_dynamic_slot",
})
}
}
@@ -68,11 +81,15 @@ impl StackSlotData {
/// Get the alignment in bytes of this stack slot given the stack pointer alignment.
pub fn alignment(&self, max_align: StackSize) -> StackSize {
debug_assert!(max_align.is_power_of_two());
// We want to find the largest power of two that divides both `self.size` and `max_align`.
// That is the same as isolating the rightmost bit in `x`.
let x = self.size | max_align;
// C.f. Hacker's delight.
x & x.wrapping_neg()
if self.kind == StackSlotKind::ExplicitDynamicSlot {
max_align
} else {
// We want to find the largest power of two that divides both `self.size` and `max_align`.
// That is the same as isolating the rightmost bit in `x`.
let x = self.size | max_align;
// C.f. Hacker's delight.
x & x.wrapping_neg()
}
}
}
@@ -82,9 +99,43 @@ impl fmt::Display for StackSlotData {
}
}
/// Contents of a dynamic stack slot.
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct DynamicStackSlotData {
/// The kind of stack slot.
pub kind: StackSlotKind,
/// The type of this slot.
pub dyn_ty: DynamicType,
}
impl DynamicStackSlotData {
/// Create a stack slot with the specified byte size.
pub fn new(kind: StackSlotKind, dyn_ty: DynamicType) -> Self {
assert!(kind == StackSlotKind::ExplicitDynamicSlot);
Self { kind, dyn_ty }
}
/// Get the alignment in bytes of this stack slot given the stack pointer alignment.
pub fn alignment(&self, max_align: StackSize) -> StackSize {
debug_assert!(max_align.is_power_of_two());
max_align
}
}
impl fmt::Display for DynamicStackSlotData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.kind, self.dyn_ty)
}
}
/// All allocated stack slots.
pub type StackSlots = PrimaryMap<StackSlot, StackSlotData>;
/// All allocated dynamic stack slots.
pub type DynamicStackSlots = PrimaryMap<DynamicStackSlot, DynamicStackSlotData>;
#[cfg(test)]
mod tests {
use super::*;
@@ -95,16 +146,56 @@ mod tests {
fn stack_slot() {
let mut func = Function::new();
let ss0 = func.create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4));
let ss1 = func.create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 8));
let ss0 = func.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4));
let ss1 = func.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 8));
assert_eq!(ss0.to_string(), "ss0");
assert_eq!(ss1.to_string(), "ss1");
assert_eq!(func.stack_slots[ss0].size, 4);
assert_eq!(func.stack_slots[ss1].size, 8);
assert_eq!(func.sized_stack_slots[ss0].size, 4);
assert_eq!(func.sized_stack_slots[ss1].size, 8);
assert_eq!(func.stack_slots[ss0].to_string(), "explicit_slot 4");
assert_eq!(func.stack_slots[ss1].to_string(), "explicit_slot 8");
assert_eq!(func.sized_stack_slots[ss0].to_string(), "explicit_slot 4");
assert_eq!(func.sized_stack_slots[ss1].to_string(), "explicit_slot 8");
}
#[test]
fn dynamic_stack_slot() {
let mut func = Function::new();
let int_vector_ty = I32X4;
let fp_vector_ty = F64X2;
let scale0 = GlobalValueData::DynScaleTargetConst {
vector_type: int_vector_ty,
};
let scale1 = GlobalValueData::DynScaleTargetConst {
vector_type: fp_vector_ty,
};
let gv0 = func.create_global_value(scale0);
let gv1 = func.create_global_value(scale1);
let dtd0 = DynamicTypeData::new(int_vector_ty, gv0);
let dtd1 = DynamicTypeData::new(fp_vector_ty, gv1);
let dt0 = func.dfg.make_dynamic_ty(dtd0);
let dt1 = func.dfg.make_dynamic_ty(dtd1);
let dss0 = func.create_dynamic_stack_slot(DynamicStackSlotData::new(
StackSlotKind::ExplicitDynamicSlot,
dt0,
));
let dss1 = func.create_dynamic_stack_slot(DynamicStackSlotData::new(
StackSlotKind::ExplicitDynamicSlot,
dt1,
));
assert_eq!(dss0.to_string(), "dss0");
assert_eq!(dss1.to_string(), "dss1");
assert_eq!(
func.dynamic_stack_slots[dss0].to_string(),
"explicit_dynamic_slot dt0"
);
assert_eq!(
func.dynamic_stack_slots[dss1].to_string(),
"explicit_dynamic_slot dt1"
);
}
#[test]