Skip to content

Commit

Permalink
style: Optimize storage of ApplicableDeclaration again.
Browse files Browse the repository at this point in the history
So that we don't regress perf.

Differential Revision: https://phabricator.services.mozilla.com/D52576
  • Loading branch information
emilio committed Nov 30, 2019
1 parent 349492b commit 1f2c1f5
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 11 deletions.
28 changes: 19 additions & 9 deletions components/style/applicable_declarations.rs
Expand Up @@ -19,27 +19,37 @@ use smallvec::SmallVec;
/// However, it may depend a lot on workload, and stack space is cheap.
pub type ApplicableDeclarationList = SmallVec<[ApplicableDeclarationBlock; 16]>;

/// Blink uses 18 bits to store source order, and does not check overflow [1].
/// That's a limit that could be reached in realistic webpages, so we use
/// 24 bits and enforce defined behavior in the overflow case.
///
/// [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/css/
/// RuleSet.h?l=128&rcl=90140ab80b84d0f889abc253410f44ed54ae04f3
const SOURCE_ORDER_SHIFT: usize = 0;
const SOURCE_ORDER_BITS: usize = 24;
const SOURCE_ORDER_MAX: u32 = (1 << SOURCE_ORDER_BITS) - 1;
const SOURCE_ORDER_MASK: u32 = SOURCE_ORDER_MAX << SOURCE_ORDER_SHIFT;

/// We pack the cascade level in a single byte, see CascadeLevel::to_byte_lossy
/// for the different trade-offs there.
const CASCADE_LEVEL_SHIFT: usize = SOURCE_ORDER_BITS;

/// Stores the source order of a block, the cascade level it belongs to, and the
/// counter needed to handle Shadow DOM cascade order properly.
///
/// FIXME(emilio): Optimize storage.
#[derive(Clone, Copy, Eq, MallocSizeOf, PartialEq, Debug)]
struct ApplicableDeclarationBits {
source_order: u32,
cascade_level: CascadeLevel,
}
struct ApplicableDeclarationBits(u32);

impl ApplicableDeclarationBits {
fn new(source_order: u32, cascade_level: CascadeLevel) -> Self {
Self { source_order, cascade_level }
Self((source_order & SOURCE_ORDER_MASK) | ((cascade_level.to_byte_lossy() as u32) << CASCADE_LEVEL_SHIFT))
}

fn source_order(&self) -> u32 {
self.source_order
self.0 & SOURCE_ORDER_MASK
}

fn level(&self) -> CascadeLevel {
self.cascade_level
CascadeLevel::from_byte((self.0 >> CASCADE_LEVEL_SHIFT) as u8)
}
}

Expand Down
68 changes: 66 additions & 2 deletions components/style/rule_tree/mod.rs
Expand Up @@ -681,12 +681,76 @@ pub enum CascadeLevel {
/// User-agent important rules.
UAImportant,
/// Transitions
///
/// NB: If this changes from being last, change from_byte below.
Transitions,
}

impl CascadeLevel {
/// Pack this cascade level in a single byte.
///
/// We have 10 levels, which we can represent with 4 bits, and then a
/// cascade order optionally, which we can clamp to three bits max, and
/// represent with a fourth bit for the sign.
///
/// So this creates: SOOODDDD
///
/// Where `S` is the sign of the order (one if negative, 0 otherwise), `O`
/// is the absolute value of the order, and `D`s are the discriminant.
#[inline]
pub fn to_byte_lossy(&self) -> u8 {
let (discriminant, order) = match *self {
Self::UANormal => (0, 0),
Self::UserNormal => (1, 0),
Self::PresHints => (2, 0),
Self::AuthorNormal { shadow_cascade_order } => (3, shadow_cascade_order.0),
Self::SMILOverride => (4, 0),
Self::Animations => (5, 0),
Self::AuthorImportant { shadow_cascade_order } => (6, shadow_cascade_order.0),
Self::UserImportant => (7, 0),
Self::UAImportant => (8, 0),
Self::Transitions => (9, 0),
};

debug_assert_eq!(discriminant & 0xf, discriminant);
if order == 0 {
return discriminant;
}

let negative = order < 0;
let value = std::cmp::min(order.abs() as u8, 0b111);
(negative as u8) << 7 | value << 4 | discriminant
}

/// Convert back from the single-byte representation of the cascade level
/// explained above.
#[inline]
pub fn from_byte(b: u8) -> Self {
let order = {
let abs = ((b & 0b01110000) >> 4) as i8;
let negative = b & 0b10000000 != 0;
if negative { -abs } else { abs }
};
let discriminant = b & 0xf;
let level = match discriminant {
0 => Self::UANormal,
1 => Self::UserNormal,
2 => Self::PresHints,
3 => return Self::AuthorNormal {
shadow_cascade_order: ShadowCascadeOrder(order),
},
4 => Self::SMILOverride,
5 => Self::Animations,
6 => return Self::AuthorImportant {
shadow_cascade_order: ShadowCascadeOrder(order),
},
7 => Self::UserImportant,
8 => Self::UAImportant,
9 => Self::Transitions,
_ => unreachable!("Didn't expect {} as a discriminant", discriminant),
};
debug_assert_eq!(order, 0, "Didn't expect an order value for {:?}", level);
level
}

/// Select a lock guard for this level
pub fn guard<'a>(&self, guards: &'a StylesheetGuards<'a>) -> &'a SharedRwLockReadGuard<'a> {
match *self {
Expand Down

0 comments on commit 1f2c1f5

Please sign in to comment.