Bug 1429126 - Rejigger cached anonymous box styles to use a union. r=emilio
authorBobby Holley <bobbyholley@gmail.com>
Mon, 08 Jan 2018 16:44:55 -0800
changeset 450281 b690a513d8595e37f26d037241dbdb641bf801ee
parent 450280 8f679a1bd3a99bd1d0e64fd941bd6776c9dc23e6
child 450282 a20954ce2d02d0c7da1eeb4885b34ef4360fa2d4
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1429126
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1429126 - Rejigger cached anonymous box styles to use a union. r=emilio MozReview-Commit-ID: I2b3wILKwNp
layout/style/CachedAnonBoxStyles.cpp
layout/style/CachedAnonBoxStyles.h
layout/style/ServoStyleContext.cpp
layout/style/ServoStyleContext.h
layout/style/moz.build
new file mode 100644
--- /dev/null
+++ b/layout/style/CachedAnonBoxStyles.cpp
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIMemoryReporter.h"
+#include "mozilla/CachedAnonBoxStyles.h"
+#include "mozilla/ServoStyleContext.h"
+
+namespace mozilla {
+
+void
+CachedAnonBoxStyles::Insert(ServoStyleContext* aStyle)
+{
+  MOZ_ASSERT(aStyle);
+  MOZ_ASSERT(aStyle->IsInheritingAnonBox());
+
+  if (IsEmpty()) {
+    RefPtr<ServoStyleContext> s = aStyle;
+    mBits = reinterpret_cast<uintptr_t>(s.forget().take());
+    MOZ_ASSERT(!IsEmpty() && !IsIndirect());
+  } else if (IsIndirect()) {
+    AsIndirect()->AppendElement(aStyle);
+  } else {
+    IndirectCache* cache = new IndirectCache();
+    cache->AppendElement(dont_AddRef(AsDirect()));
+    cache->AppendElement(aStyle);
+    mBits = reinterpret_cast<uintptr_t>(cache) | 1;
+    MOZ_ASSERT(IsIndirect());
+  }
+}
+
+ServoStyleContext*
+CachedAnonBoxStyles::Lookup(nsAtom* aAnonBox) const
+{
+  MOZ_ASSERT(nsCSSAnonBoxes::IsInheritingAnonBox(aAnonBox));
+  if (IsIndirect()) {
+    for (auto& style : *AsIndirect()) {
+      if (style->GetPseudo() == aAnonBox) {
+        return style;
+      }
+    }
+
+    return nullptr;
+  }
+
+  ServoStyleContext* direct = AsDirect();
+  return direct && direct->GetPseudo() == aAnonBox ? direct : nullptr;
+}
+
+void
+CachedAnonBoxStyles::AddSizeOfIncludingThis(nsWindowSizes& aSizes, size_t* aCVsSize) const
+{
+  if (IsIndirect()) {
+    for (auto& style : *AsIndirect()) {
+      if (!aSizes.mState.HaveSeenPtr(style)) {
+        style->AddSizeOfIncludingThis(aSizes, aCVsSize);
+      }
+    }
+
+    return;
+  }
+
+  ServoStyleContext* direct = AsDirect();
+  if (direct && !aSizes.mState.HaveSeenPtr(direct)) {
+    direct->AddSizeOfIncludingThis(aSizes, aCVsSize);
+  }
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/style/CachedAnonBoxStyles.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_CachedAnonBoxStyles_h
+#define mozilla_CachedAnonBoxStyles_h
+
+#include "nsAtom.h"
+#include "nsTArray.h"
+
+class nsWindowSizes;
+
+namespace mozilla {
+
+class ServoStyleContext;
+
+// Cache of anonymous box styles that inherit from a given style.
+//
+// To minimize memory footprint, the cache is word-sized with a tagged pointer
+// If there is only one entry, it's stored inline. If there are more, they're
+// stored in an out-of-line buffer. See bug 1429126 comment 0 and comment 1 for
+// the measurements and rationale that influenced the design.
+class CachedAnonBoxStyles
+{
+public:
+  void Insert(ServoStyleContext* aStyle);
+  ServoStyleContext* Lookup(nsAtom* aAnonBox) const;
+
+  CachedAnonBoxStyles() : mBits(0) {}
+  ~CachedAnonBoxStyles()
+  {
+    if (IsIndirect()) {
+      delete AsIndirect();
+    } else if (!IsEmpty()) {
+      RefPtr<ServoStyleContext> ref = dont_AddRef(AsDirect());
+    }
+  }
+
+  void AddSizeOfIncludingThis(nsWindowSizes& aSizes, size_t* aCVsSize) const;
+
+private:
+  // See bug 1429126 comment 1 for the choice of four here.
+  typedef AutoTArray<RefPtr<ServoStyleContext>, 4> IndirectCache;
+
+  bool IsEmpty() const { return !mBits; }
+  bool IsIndirect() const { return (mBits & 1); }
+
+  ServoStyleContext* AsDirect() const
+  {
+    MOZ_ASSERT(!IsIndirect());
+    return reinterpret_cast<ServoStyleContext*>(mBits);
+  }
+
+  IndirectCache* AsIndirect() const
+  {
+    MOZ_ASSERT(IsIndirect());
+    return reinterpret_cast<IndirectCache*>(mBits & ~1);
+  }
+
+  uintptr_t mBits;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_CachedAnonBoxStyles_h
--- a/layout/style/ServoStyleContext.cpp
+++ b/layout/style/ServoStyleContext.cpp
@@ -29,36 +29,16 @@ ServoStyleContext::ServoStyleContext(
   AddStyleBit(Servo_ComputedValues_GetStyleBits(this));
   MOZ_ASSERT(ComputedData());
 
   // No need to call ApplyStyleFixups here, since fixups are handled by Servo when
   // producing the ServoComputedData.
 }
 
 ServoStyleContext*
-ServoStyleContext::GetCachedInheritingAnonBoxStyle(nsAtom* aAnonBox) const
-{
-  MOZ_ASSERT(nsCSSAnonBoxes::IsInheritingAnonBox(aAnonBox));
-
-  // See the reasoning in SetCachedInheritingAnonBoxStyle to understand why we
-  // can't use the cache in this case.
-  if (IsInheritingAnonBox()) {
-    return nullptr;
-  }
-
-  auto* current = mNextInheritingAnonBoxStyle.get();
-
-  while (current && current->GetPseudo() != aAnonBox) {
-    current = current->mNextInheritingAnonBoxStyle.get();
-  }
-
-  return current;
-}
-
-ServoStyleContext*
 ServoStyleContext::GetCachedLazyPseudoStyle(CSSPseudoElementType aPseudo) const
 {
   MOZ_ASSERT(aPseudo != CSSPseudoElementType::NotPseudo &&
              aPseudo != CSSPseudoElementType::InheritingAnonBox &&
              aPseudo != CSSPseudoElementType::NonInheritingAnonBox);
   MOZ_ASSERT(!IsLazilyCascadedPseudoElement(), "Lazy pseudos can't inherit lazy pseudos");
 
   if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(aPseudo)) {
--- a/layout/style/ServoStyleContext.h
+++ b/layout/style/ServoStyleContext.h
@@ -7,16 +7,18 @@
 #ifndef mozilla_ServoStyleContext_h
 #define mozilla_ServoStyleContext_h
 
 #include "nsIMemoryReporter.h"
 #include "nsStyleContext.h"
 #include "nsWindowSizes.h"
 #include <algorithm>
 
+#include "mozilla/CachedAnonBoxStyles.h"
+
 namespace mozilla {
 
 namespace dom {
 class Element;
 } // namespace dom
 
 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoComputedValuesMallocEnclosingSizeOf)
 
@@ -40,36 +42,39 @@ public:
   }
 
   bool IsLazilyCascadedPseudoElement() const
   {
     return IsPseudoElement() &&
            !nsCSSPseudoElements::IsEagerlyCascadedInServo(GetPseudoType());
   }
 
-  ServoStyleContext* GetCachedInheritingAnonBoxStyle(nsAtom* aAnonBox) const;
+  ServoStyleContext* GetCachedInheritingAnonBoxStyle(nsAtom* aAnonBox) const
+  {
+    MOZ_ASSERT(nsCSSAnonBoxes::IsInheritingAnonBox(aAnonBox));
+
+    // This restriction is no longer necessary. We remove it in the next patch.
+    if (IsInheritingAnonBox()) {
+      return nullptr;
+    }
+
+    return mInheritingAnonBoxStyles.Lookup(aAnonBox);
+  }
 
   void SetCachedInheritedAnonBoxStyle(nsAtom* aAnonBox,
                                       ServoStyleContext* aStyle)
   {
     MOZ_ASSERT(!GetCachedInheritingAnonBoxStyle(aAnonBox));
-    MOZ_ASSERT(!aStyle->mNextInheritingAnonBoxStyle);
 
-    // NOTE(emilio): Since we use it to cache inheriting anon boxes in a linked
-    // list, we can't use that cache if the style we're inheriting from is an
-    // inheriting anon box itself, since otherwise our parent would mistakenly
-    // think that the style we're caching inherits from it.
-    //
-    // See the documentation of mNextInheritingAnonBoxStyle.
+    // This restriction is no longer necessary. We remove it in the next patch.
     if (IsInheritingAnonBox()) {
       return;
     }
 
-    mNextInheritingAnonBoxStyle.swap(aStyle->mNextInheritingAnonBoxStyle);
-    mNextInheritingAnonBoxStyle = aStyle;
+    mInheritingAnonBoxStyles.Insert(aStyle);
   }
 
   ServoStyleContext* GetCachedLazyPseudoStyle(CSSPseudoElementType aPseudo) const;
 
   void SetCachedLazyPseudoStyle(ServoStyleContext* aStyle)
   {
     MOZ_ASSERT(aStyle->GetPseudo() && !aStyle->IsAnonBox());
     MOZ_ASSERT(!GetCachedLazyPseudoStyle(aStyle->GetPseudoType()));
@@ -106,38 +111,31 @@ public:
   void AddSizeOfIncludingThis(nsWindowSizes& aSizes, size_t* aCVsSize) const
   {
     // Note: |this| sits within a servo_arc::Arc, i.e. it is preceded by a
     // refcount. So we need to measure it with a function that can handle an
     // interior pointer. We use ServoComputedValuesMallocEnclosingSizeOf to
     // clearly identify in DMD's output the memory measured here.
     *aCVsSize += ServoComputedValuesMallocEnclosingSizeOf(this);
     mSource.AddSizeOfExcludingThis(aSizes);
-
-    if (mNextInheritingAnonBoxStyle &&
-        !aSizes.mState.HaveSeenPtr(mNextInheritingAnonBoxStyle)) {
-      mNextInheritingAnonBoxStyle->AddSizeOfIncludingThis(aSizes, aCVsSize);
-    }
+    mInheritingAnonBoxStyles.AddSizeOfIncludingThis(aSizes, aCVsSize);
 
     if (mNextLazyPseudoStyle &&
         !aSizes.mState.HaveSeenPtr(mNextLazyPseudoStyle)) {
       mNextLazyPseudoStyle->AddSizeOfIncludingThis(aSizes, aCVsSize);
     }
   }
 
 private:
   nsPresContext* mPresContext;
   ServoComputedData mSource;
 
-  // A linked-list cache of inheriting anon boxes inheriting from this style _if
-  // the style isn't an inheriting anon-box_.
-  //
-  // Otherwise it represents the next entry in the cache of the parent style
-  // context.
-  RefPtr<ServoStyleContext> mNextInheritingAnonBoxStyle;
+  // A cache of inheriting anon boxes inheriting from this style _if the style
+  // isn't an inheriting anon-box_.
+  CachedAnonBoxStyles mInheritingAnonBoxStyles;
 
   // A linked-list cache of lazy pseudo styles inheriting from this style _if
   // the style isn't a lazy pseudo style itself_.
   //
   // Otherwise it represents the next entry in the cache of the parent style
   // context.
   //
   // Note that we store these separately from inheriting anonymous boxes so that
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -76,16 +76,17 @@ EXPORTS += [
     'nsStyleStructInlines.h',
     'nsStyleTransformMatrix.h',
     'nsStyleUtil.h',
 ]
 
 EXPORTS.mozilla += [
     'AnimationCollection.h',
     'BindingStyleRule.h',
+    'CachedAnonBoxStyles.h',
     'CSSEnabledState.h',
     'CSSStyleSheet.h',
     'CSSVariableDeclarations.h',
     'CSSVariableResolver.h',
     'CSSVariableValues.h',
     'DeclarationBlock.h',
     'DeclarationBlockInlines.h',
     'DocumentStyleRootIterator.h',
@@ -174,16 +175,17 @@ EXPORTS.mozilla.css += [
     'StreamLoader.h',
     'StyleRule.h',
     'URLMatchingFunction.h',
 ]
 
 UNIFIED_SOURCES += [
     'AnimationCollection.cpp',
     'BindingStyleRule.cpp',
+    'CachedAnonBoxStyles.cpp',
     'CounterStyleManager.cpp',
     'CSS.cpp',
     'CSSFontFeatureValuesRule.cpp',
     'CSSImportRule.cpp',
     'CSSKeyframeRule.cpp',
     'CSSKeyframesRule.cpp',
     'CSSLexer.cpp',
     'CSSMediaRule.cpp',