YJIT: shink local types from 8 to 4 bytes (#8597)
* Shink local types from 8 to 4 bytes, context from 21 to 17 bytes Use repr(packed) * Add comment about Type being limited to 4 bits
This commit is contained in:
parent
7d1abd5d31
commit
13bde94a9f
103
yjit/src/core.rs
103
yjit/src/core.rs
@ -42,7 +42,7 @@ pub type IseqIdx = u16;
|
|||||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
Unknown,
|
Unknown = 0,
|
||||||
UnknownImm,
|
UnknownImm,
|
||||||
UnknownHeap,
|
UnknownHeap,
|
||||||
Nil,
|
Nil,
|
||||||
@ -64,6 +64,10 @@ pub enum Type {
|
|||||||
|
|
||||||
BlockParamProxy, // A special sentinel value indicating the block parameter should be read from
|
BlockParamProxy, // A special sentinel value indicating the block parameter should be read from
|
||||||
// the current surrounding cfp
|
// the current surrounding cfp
|
||||||
|
|
||||||
|
// The context currently relies on types taking at most 4 bits (max value 15)
|
||||||
|
// to encode, so if we add any more, we will need to refactor the context,
|
||||||
|
// or we could remove HeapSymbol, which is currently unused.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default initialization
|
// Default initialization
|
||||||
@ -438,7 +442,8 @@ impl RegTemps {
|
|||||||
/// Code generation context
|
/// Code generation context
|
||||||
/// Contains information we can use to specialize/optimize code
|
/// Contains information we can use to specialize/optimize code
|
||||||
/// There are a lot of context objects so we try to keep the size small.
|
/// There are a lot of context objects so we try to keep the size small.
|
||||||
#[derive(Clone, Default, Eq, Hash, PartialEq, Debug)]
|
#[derive(Copy, Clone, Default, Eq, Hash, PartialEq, Debug)]
|
||||||
|
#[repr(packed)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
// Number of values currently on the temporary stack
|
// Number of values currently on the temporary stack
|
||||||
stack_size: u8,
|
stack_size: u8,
|
||||||
@ -457,7 +462,8 @@ pub struct Context {
|
|||||||
chain_depth_return_landing: u8,
|
chain_depth_return_landing: u8,
|
||||||
|
|
||||||
// Local variable types we keep track of
|
// Local variable types we keep track of
|
||||||
local_types: [Type; MAX_LOCAL_TYPES],
|
// We store 8 local types, requiring 4 bits each, for a total of 32 bits
|
||||||
|
local_types: u32,
|
||||||
|
|
||||||
// Type we track for self
|
// Type we track for self
|
||||||
self_type: Type,
|
self_type: Type,
|
||||||
@ -1711,7 +1717,7 @@ impl Context {
|
|||||||
MapToLocal => {
|
MapToLocal => {
|
||||||
let idx = mapping.get_local_idx();
|
let idx = mapping.get_local_idx();
|
||||||
assert!((idx as usize) < MAX_LOCAL_TYPES);
|
assert!((idx as usize) < MAX_LOCAL_TYPES);
|
||||||
return self.local_types[idx as usize];
|
return self.get_local_type(idx.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1719,8 +1725,14 @@ impl Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the currently tracked type for a local variable
|
/// Get the currently tracked type for a local variable
|
||||||
pub fn get_local_type(&self, idx: usize) -> Type {
|
pub fn get_local_type(&self, local_idx: usize) -> Type {
|
||||||
*self.local_types.get(idx).unwrap_or(&Type::Unknown)
|
if local_idx >= MAX_LOCAL_TYPES {
|
||||||
|
return Type::Unknown
|
||||||
|
} else {
|
||||||
|
// Each type is stored in 4 bits
|
||||||
|
let type_bits = (self.local_types >> (4 * local_idx)) & 0b1111;
|
||||||
|
unsafe { transmute::<u8, Type>(type_bits as u8) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Upgrade (or "learn") the type of an instruction operand
|
/// Upgrade (or "learn") the type of an instruction operand
|
||||||
@ -1756,7 +1768,9 @@ impl Context {
|
|||||||
MapToLocal => {
|
MapToLocal => {
|
||||||
let idx = mapping.get_local_idx() as usize;
|
let idx = mapping.get_local_idx() as usize;
|
||||||
assert!(idx < MAX_LOCAL_TYPES);
|
assert!(idx < MAX_LOCAL_TYPES);
|
||||||
self.local_types[idx].upgrade(opnd_type);
|
let mut new_type = self.get_local_type(idx);
|
||||||
|
new_type.upgrade(opnd_type);
|
||||||
|
self.set_local_type(idx, new_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1814,26 +1828,26 @@ impl Context {
|
|||||||
|
|
||||||
/// Set the type of a local variable
|
/// Set the type of a local variable
|
||||||
pub fn set_local_type(&mut self, local_idx: usize, local_type: Type) {
|
pub fn set_local_type(&mut self, local_idx: usize, local_type: Type) {
|
||||||
let ctx = self;
|
|
||||||
|
|
||||||
// If type propagation is disabled, store no types
|
// If type propagation is disabled, store no types
|
||||||
if get_option!(no_type_prop) {
|
if get_option!(no_type_prop) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if local_idx >= MAX_LOCAL_TYPES {
|
if local_idx >= MAX_LOCAL_TYPES {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If any values on the stack map to this local we must detach them
|
// If any values on the stack map to this local we must detach them
|
||||||
for mapping in ctx.temp_mapping.iter_mut() {
|
for mapping_idx in 0..self.temp_mapping.len() {
|
||||||
*mapping = match mapping.get_kind() {
|
let mapping = self.temp_mapping[mapping_idx];
|
||||||
MapToStack => *mapping,
|
self.temp_mapping[mapping_idx] = match mapping.get_kind() {
|
||||||
MapToSelf => *mapping,
|
MapToStack => mapping,
|
||||||
|
MapToSelf => mapping,
|
||||||
MapToLocal => {
|
MapToLocal => {
|
||||||
let idx = mapping.get_local_idx();
|
let idx = mapping.get_local_idx();
|
||||||
if idx as usize == local_idx {
|
if idx as usize == local_idx {
|
||||||
TempMapping::map_to_stack(ctx.local_types[idx as usize])
|
let local_type = self.get_local_type(local_idx.into());
|
||||||
|
TempMapping::map_to_stack(local_type)
|
||||||
} else {
|
} else {
|
||||||
TempMapping::map_to_local(idx)
|
TempMapping::map_to_local(idx)
|
||||||
}
|
}
|
||||||
@ -1841,7 +1855,12 @@ impl Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.local_types[local_idx] = local_type;
|
// Update the type bits
|
||||||
|
let type_bits = local_type as u32;
|
||||||
|
assert!(type_bits <= 0b1111);
|
||||||
|
let mask_bits = (0b1111 as u32) << (4 * local_idx);
|
||||||
|
let shifted_bits = type_bits << (4 * local_idx);
|
||||||
|
self.local_types = (self.local_types & !mask_bits) | shifted_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Erase local variable type information
|
/// Erase local variable type information
|
||||||
@ -1849,15 +1868,17 @@ impl Context {
|
|||||||
pub fn clear_local_types(&mut self) {
|
pub fn clear_local_types(&mut self) {
|
||||||
// When clearing local types we must detach any stack mappings to those
|
// When clearing local types we must detach any stack mappings to those
|
||||||
// locals. Even if local values may have changed, stack values will not.
|
// locals. Even if local values may have changed, stack values will not.
|
||||||
for mapping in self.temp_mapping.iter_mut() {
|
|
||||||
|
for mapping_idx in 0..self.temp_mapping.len() {
|
||||||
|
let mapping = self.temp_mapping[mapping_idx];
|
||||||
if mapping.get_kind() == MapToLocal {
|
if mapping.get_kind() == MapToLocal {
|
||||||
let idx = mapping.get_local_idx();
|
let local_idx = mapping.get_local_idx() as usize;
|
||||||
*mapping = TempMapping::map_to_stack(self.local_types[idx as usize]);
|
self.temp_mapping[mapping_idx] = TempMapping::map_to_stack(self.get_local_type(local_idx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the local types
|
// Clear the local types
|
||||||
self.local_types = [Type::default(); MAX_LOCAL_TYPES];
|
self.local_types = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute a difference score for two context objects
|
/// Compute a difference score for two context objects
|
||||||
@ -1902,9 +1923,9 @@ impl Context {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// For each local type we track
|
// For each local type we track
|
||||||
for i in 0..src.local_types.len() {
|
for i in 0.. MAX_LOCAL_TYPES {
|
||||||
let t_src = src.local_types[i];
|
let t_src = src.get_local_type(i);
|
||||||
let t_dst = dst.local_types[i];
|
let t_dst = dst.get_local_type(i);
|
||||||
diff += match t_src.diff(t_dst) {
|
diff += match t_src.diff(t_dst) {
|
||||||
TypeDiff::Compatible(diff) => diff,
|
TypeDiff::Compatible(diff) => diff,
|
||||||
TypeDiff::Incompatible => return TypeDiff::Incompatible,
|
TypeDiff::Incompatible => return TypeDiff::Incompatible,
|
||||||
@ -3237,11 +3258,45 @@ impl<T> RefUnchecked for Cell<T> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use crate::core::*;
|
use crate::core::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn type_size() {
|
||||||
|
// Check that we can store types in 4 bits,
|
||||||
|
// and all local types in 32 bits
|
||||||
|
assert_eq!(mem::size_of::<Type>(), 1);
|
||||||
|
assert!(Type::BlockParamProxy as usize <= 0b1111);
|
||||||
|
assert!(MAX_LOCAL_TYPES * 4 <= 32);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tempmapping_size() {
|
fn tempmapping_size() {
|
||||||
assert_eq!(mem::size_of::<TempMapping>(), 1);
|
assert_eq!(mem::size_of::<TempMapping>(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn local_types() {
|
||||||
|
let mut ctx = Context::default();
|
||||||
|
|
||||||
|
for i in 0..MAX_LOCAL_TYPES {
|
||||||
|
ctx.set_local_type(i, Type::Fixnum);
|
||||||
|
assert_eq!(ctx.get_local_type(i), Type::Fixnum);
|
||||||
|
ctx.set_local_type(i, Type::BlockParamProxy);
|
||||||
|
assert_eq!(ctx.get_local_type(i), Type::BlockParamProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.set_local_type(0, Type::Fixnum);
|
||||||
|
ctx.clear_local_types();
|
||||||
|
assert!(ctx.get_local_type(0) == Type::Unknown);
|
||||||
|
|
||||||
|
// Make sure we don't accidentally set bits incorrectly
|
||||||
|
let mut ctx = Context::default();
|
||||||
|
ctx.set_local_type(0, Type::Fixnum);
|
||||||
|
assert_eq!(ctx.get_local_type(0), Type::Fixnum);
|
||||||
|
ctx.set_local_type(2, Type::Fixnum);
|
||||||
|
ctx.set_local_type(1, Type::BlockParamProxy);
|
||||||
|
assert_eq!(ctx.get_local_type(0), Type::Fixnum);
|
||||||
|
assert_eq!(ctx.get_local_type(2), Type::Fixnum);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tempmapping() {
|
fn tempmapping() {
|
||||||
let t = TempMapping::map_to_stack(Type::Unknown);
|
let t = TempMapping::map_to_stack(Type::Unknown);
|
||||||
@ -3259,7 +3314,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn context_size() {
|
fn context_size() {
|
||||||
assert_eq!(mem::size_of::<Context>(), 21);
|
assert_eq!(mem::size_of::<Context>(), 17);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user