Bug 1364815 - Optimize away many (virtual) calls to QueryFrame. r=dholbert
authorMats Palmgren <mats@mozilla.com>
Sun, 10 Sep 2017 00:50:16 +0200
changeset 429430 826f9585e662de250319e3774ecb461aac08f051
parent 429429 6fb89b1414abb106067b2e2845f07347bb843eb4
child 429431 1f0f7fc025eeea09549179d46ead4121c6b21859
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert
bugs1364815
milestone57.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 1364815 - Optimize away many (virtual) calls to QueryFrame. r=dholbert do_QueryFrame from one frame type to another frame type can compare mClass first, and if successful just downcast the pointer to the target frame type. If unsuccessful, or for do_QueryFrame calls involving other types, we must still call QueryFrame. MozReview-Commit-ID: 5MVfmuOYwdE
layout/generic/nsFrameIdList.h
layout/generic/nsIFrame.h
layout/generic/nsQueryFrame.h
--- a/layout/generic/nsFrameIdList.h
+++ b/layout/generic/nsFrameIdList.h
@@ -141,16 +141,22 @@ FRAME_ID(nsTextFrame, Text, Leaf)
 FRAME_ID(nsTitleBarFrame, Box, NotLeaf)
 FRAME_ID(nsTreeBodyFrame, LeafBox, Leaf)
 FRAME_ID(nsTreeColFrame, Box, NotLeaf)
 FRAME_ID(nsVideoFrame, HTMLVideo, Leaf)
 FRAME_ID(nsXULLabelFrame, XULLabel, NotLeaf)
 FRAME_ID(nsXULScrollFrame, Scroll, NotLeaf)
 FRAME_ID(ViewportFrame, Viewport, NotLeaf)
 
+// The following ABSTRACT_FRAME_IDs needs to come after the above
+// FRAME_IDs, because we have two separate enums, one that includes
+// only FRAME_IDs and another which includes both and we depend on
+// FRAME_IDs to have the same number in both.
+// See ClassID (the former) and FrameIID in nsQueryFrame.h.
+
 // Non-concrete classes (for FrameIID use)
 ABSTRACT_FRAME_ID(nsContainerFrame)
 ABSTRACT_FRAME_ID(nsFormControlFrame)
 ABSTRACT_FRAME_ID(nsIFrame)
 ABSTRACT_FRAME_ID(nsLeafFrame)
 ABSTRACT_FRAME_ID(nsMathMLContainerFrame)
 ABSTRACT_FRAME_ID(nsRubyContentFrame)
 ABSTRACT_FRAME_ID(nsSplittableFrame)
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -4481,16 +4481,23 @@ private:
   void operator delete[](void*) = delete;
 
   void Init(nsIFrame* aFrame);
 
   AutoWeakFrame*  mPrev;
   nsIFrame*       mFrame;
 };
 
+// Use nsIFrame's fast-path to avoid QueryFrame:
+inline do_QueryFrameHelper<nsIFrame>
+do_QueryFrame(AutoWeakFrame& s)
+{
+  return do_QueryFrameHelper<nsIFrame>(s.GetFrame());
+}
+
 /**
  * @see AutoWeakFrame
  */
 class MOZ_HEAP_CLASS WeakFrame
 {
 public:
   WeakFrame() : mFrame(nullptr) {}
 
@@ -4538,16 +4545,23 @@ public:
   nsIFrame* GetFrame() const { return mFrame; }
 
 private:
   void Init(nsIFrame* aFrame);
 
   nsIFrame* mFrame;
 };
 
+// Use nsIFrame's fast-path to avoid QueryFrame:
+inline do_QueryFrameHelper<nsIFrame>
+do_QueryFrame(WeakFrame& s)
+{
+  return do_QueryFrameHelper<nsIFrame>(s.GetFrame());
+}
+
 inline bool
 nsFrameList::ContinueRemoveFrame(nsIFrame* aFrame)
 {
   MOZ_ASSERT(!aFrame->GetPrevSibling() || !aFrame->GetNextSibling(),
              "Forgot to call StartRemoveFrame?");
   if (aFrame == mLastChild) {
     MOZ_ASSERT(!aFrame->GetNextSibling(), "broken frame list");
     nsIFrame* prevSibling = aFrame->GetPrevSibling();
--- a/layout/generic/nsQueryFrame.h
+++ b/layout/generic/nsQueryFrame.h
@@ -77,34 +77,71 @@ public:
 #include "nsFrameIdList.h"
 #undef FRAME_ID
 #undef ABSTRACT_FRAME_ID
   };
 
   virtual void* QueryFrame(FrameIID id) = 0;
 };
 
-class do_QueryFrame
+class nsIFrame;
+
+template<class Source>
+class do_QueryFrameHelper
 {
 public:
-  explicit do_QueryFrame(nsQueryFrame *s) : mRawPtr(s) { }
+  explicit do_QueryFrameHelper(Source* s) : mRawPtr(s) { }
 
   // The return and argument types here are arbitrarily selected so no
   // corresponding member function exists.
-  typedef void (do_QueryFrame::* MatchNullptr)(double, float);
+  typedef void (do_QueryFrameHelper::* MatchNullptr)(double, float);
   // Implicit constructor for nullptr, trick borrowed from already_AddRefed.
-  MOZ_IMPLICIT do_QueryFrame(MatchNullptr aRawPtr) : mRawPtr(nullptr) {}
+  MOZ_IMPLICIT do_QueryFrameHelper(MatchNullptr aRawPtr) : mRawPtr(nullptr) {}
 
   template<class Dest>
   operator Dest*() {
     static_assert(mozilla::IsSame<Dest, typename Dest::Has_NS_DECL_QUERYFRAME_TARGET>::value,
                   "Dest must declare itself as a queryframe target");
-    if (!mRawPtr)
+    if (!mRawPtr) {
       return nullptr;
-
+    }
+    if (Dest* f = FastQueryFrame<Source, Dest>::QueryFrame(mRawPtr)) {
+      MOZ_ASSERT(f ==
+                 reinterpret_cast<Dest*>(mRawPtr->QueryFrame(Dest::kFrameIID)),
+                 "fast and slow paths should give the same result");
+      return f;
+    }
     return reinterpret_cast<Dest*>(mRawPtr->QueryFrame(Dest::kFrameIID));
   }
 
 private:
-  nsQueryFrame *mRawPtr;
+  // For non-nsIFrame types there is no fast-path.
+  template<class Src, class Dst, typename = void, typename = void>
+  struct FastQueryFrame
+  {
+    static Dst* QueryFrame(Src* aFrame) { return nullptr; }
+  };
+  
+  // Specialization for any nsIFrame type to any nsIFrame type -- if the source
+  // instance's mClass matches kFrameIID of the destination type then
+  // downcasting is safe.
+  template<class Src, class Dst>
+  struct FastQueryFrame<Src, Dst,
+    typename mozilla::EnableIf<mozilla::IsBaseOf<nsIFrame, Src>::value>::type,
+    typename mozilla::EnableIf<mozilla::IsBaseOf<nsIFrame, Dst>::value>::Type>
+  {
+    static Dst* QueryFrame(Src* aFrame) {
+      return nsQueryFrame::FrameIID(aFrame->mClass) == Dst::kFrameIID ?
+        reinterpret_cast<Dst*>(aFrame) : nullptr;
+    }
+  };
+
+  Source* mRawPtr;
 };
 
+template<class T>
+inline do_QueryFrameHelper<T>
+do_QueryFrame(T* s)
+{
+  return do_QueryFrameHelper<T>(s);
+}
+
 #endif // nsQueryFrame_h