Bug 1449787 - Make static atom pointers `constexpr`. r=froydnj,emilio
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 04 Oct 2018 09:16:11 +1000
changeset 495339 41216e689dda9bafb0ce5cf852d517a1c0b9727f
parent 495338 66b689d8aa4d337e43e4253abd88e5bcceeeca99
child 495340 b1c901b2d6619e0ab52a7d7fb3bc9082204eeb57
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj, emilio
bugs1449787
milestone64.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 1449787 - Make static atom pointers `constexpr`. r=froydnj,emilio This saves one word per static atom, per process. The `nsGkAtoms` change is only a small part of this commit. In regen_atoms.py: - There is now only one link name per platform: nsGkAtoms::sAtoms[]. - But there is a new constant per atom, giving the index into nsGkAtoms::sAtoms[]. - And the `atom!` macro for each atom indexes into nsGkAtoms::sAtoms[] using the index constant. - A couple of `*mut` pointers are now `*const`. Elsewhere, the `(nsStaticAtom*)` casts within the `AppendElement()` calls are necessary to avoid link errors, presumably due to some template instantiation wrinkle.
layout/style/CounterStyleManager.cpp
layout/style/nsCSSAnonBoxes.h
layout/style/nsCSSPseudoElements.h
layout/style/nsMediaFeatures.cpp
layout/xul/tree/nsTreeBodyFrame.cpp
servo/components/style/gecko/regen_atoms.py
servo/components/style/gecko_string_cache/mod.rs
widget/gtk/nsNativeThemeGTK.cpp
xpcom/ds/nsGkAtoms.cpp
xpcom/ds/nsGkAtoms.h
--- a/layout/style/CounterStyleManager.cpp
+++ b/layout/style/CounterStyleManager.cpp
@@ -568,17 +568,17 @@ SystemUsesNegativeSign(uint8_t aSystem)
     default:
       return false;
   }
 }
 
 class BuiltinCounterStyle : public CounterStyle
 {
 public:
-  constexpr BuiltinCounterStyle(int32_t aStyle, nsStaticAtom** aName)
+  constexpr BuiltinCounterStyle(int32_t aStyle, nsStaticAtom* aName)
     : CounterStyle(aStyle)
     , mName(aName)
   {
   }
 
   nsStaticAtom* GetStyleName() const final;
   virtual void GetPrefix(nsAString& aResult) override;
   virtual void GetSuffix(nsAString& aResult) override;
@@ -608,23 +608,23 @@ protected:
   {
   }
 
 private:
   // The atom for the name of the builtin counter style.
   // Extra indirection to point to nsGkAtoms members rather than the
   // nsAtom, because members of nsGkAtoms are updated at runtime but
   // we want to construct BuiltinCounterStyle at compile time.
-  nsStaticAtom** const mName;
+  nsStaticAtom* const mName;
 };
 
 /* virtual */ nsStaticAtom*
 BuiltinCounterStyle::GetStyleName() const
 {
-  return *mName;
+  return mName;
 }
 
 /* virtual */ void
 BuiltinCounterStyle::GetPrefix(nsAString& aResult)
 {
   aResult.Truncate();
 }
 
@@ -952,17 +952,17 @@ BuiltinCounterStyle::GetInitialCounterTe
     default:
       MOZ_ASSERT_UNREACHABLE("Unknown builtin counter style");
       return false;
   }
 }
 
 static constexpr BuiltinCounterStyle gBuiltinStyleTable[] = {
 #define BUILTIN_COUNTER_STYLE(value_, atom_) \
-  { NS_STYLE_LIST_STYLE_ ## value_, &nsGkAtoms::atom_ },
+  { NS_STYLE_LIST_STYLE_ ## value_, nsGkAtoms::atom_ },
 #include "BuiltinCounterStyleList.h"
 #undef BUILTIN_COUNTER_STYLE
 };
 
 #define BUILTIN_COUNTER_STYLE(value_, atom_) \
   static_assert(gBuiltinStyleTable[NS_STYLE_LIST_STYLE_ ## value_].GetStyle() \
                 == NS_STYLE_LIST_STYLE_ ## value_, "Builtin counter style " \
                 #atom_ " has unmatched index and value.");
--- a/layout/style/nsCSSAnonBoxes.h
+++ b/layout/style/nsCSSAnonBoxes.h
@@ -90,20 +90,20 @@ public:
   // test true for IsNonInheritingAnonBox.
   static NonInheriting NonInheritingTypeForPseudoTag(nsAtom* aPseudo);
 
 #ifdef DEBUG
   static void AssertAtoms();
 #endif
 
   // Alias nsCSSAnonBoxes::foo() to alias nsGkAtoms::AnonBox_foo.
-  // XXX Once nsGkAtoms::AnonBox_foo become constexpr variables, these can too.
-  // See bug 1449787.
   #define CSS_ANON_BOX(name_, value_)                     \
-    static constexpr nsICSSAnonBoxPseudo* const& name_()  \
+    static nsICSSAnonBoxPseudo* name_()                   \
     {                                                     \
-      return nsGkAtoms::AnonBox_##name_;                  \
+      return const_cast<nsICSSAnonBoxPseudo*>(            \
+        static_cast<const nsICSSAnonBoxPseudo*>(          \
+          nsGkAtoms::AnonBox_##name_));                   \
     }
   #include "nsCSSAnonBoxList.h"
   #undef CSS_ANON_BOX
 };
 
 #endif /* nsCSSAnonBoxes_h___ */
--- a/layout/style/nsCSSPseudoElements.h
+++ b/layout/style/nsCSSPseudoElements.h
@@ -99,22 +99,22 @@ public:
   }
 
 public:
 #ifdef DEBUG
   static void AssertAtoms();
 #endif
 
   // Alias nsCSSPseudoElements::foo() to alias nsGkAtoms::foo.
-  // XXX Once nsGkAtoms::foo become constexpr variables, these can too.
-  // See bug 1449787.
   #define CSS_PSEUDO_ELEMENT(name_, value_, flags_)       \
-    static constexpr nsICSSPseudoElement* const& name_()  \
+    static nsICSSPseudoElement* name_()                   \
     {                                                     \
-      return nsGkAtoms::PseudoElement_##name_;            \
+      return const_cast<nsICSSPseudoElement*>(            \
+        static_cast<const nsICSSPseudoElement*>(          \
+          nsGkAtoms::PseudoElement_##name_));             \
     }
   #include "nsCSSPseudoElementList.h"
   #undef CSS_PSEUDO_ELEMENT
 
   static Type GetPseudoType(nsAtom* aAtom, EnabledState aEnabledState);
 
   // Get the atom for a given Type. aType must be < CSSPseudoElementType::Count.
   // This only ever returns static atoms, so it's fine to return a raw pointer.
--- a/layout/style/nsMediaFeatures.cpp
+++ b/layout/style/nsMediaFeatures.cpp
@@ -311,127 +311,146 @@ nsMediaFeatures::InitSystemMetrics()
 
   /***************************************************************************
    * ANY METRICS ADDED HERE SHOULD ALSO BE ADDED AS MEDIA QUERIES BELOW      *
    ***************************************************************************/
 
   int32_t metricResult =
     LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollArrowStyle);
   if (metricResult & LookAndFeel::eScrollArrow_StartBackward) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_scrollbar_start_backward);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_scrollbar_start_backward);
   }
   if (metricResult & LookAndFeel::eScrollArrow_StartForward) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_scrollbar_start_forward);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_scrollbar_start_forward);
   }
   if (metricResult & LookAndFeel::eScrollArrow_EndBackward) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_scrollbar_end_backward);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_scrollbar_end_backward);
   }
   if (metricResult & LookAndFeel::eScrollArrow_EndForward) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_scrollbar_end_forward);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_scrollbar_end_forward);
   }
 
   metricResult =
     LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollSliderStyle);
   if (metricResult != LookAndFeel::eScrollThumbStyle_Normal) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_scrollbar_thumb_proportional);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_scrollbar_thumb_proportional);
   }
 
   metricResult =
     LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars);
   if (metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_overlay_scrollbars);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_overlay_scrollbars);
   }
 
   metricResult =
     LookAndFeel::GetInt(LookAndFeel::eIntID_MenuBarDrag);
   if (metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_menubar_drag);
+    sSystemMetrics->AppendElement((nsStaticAtom*)nsGkAtoms::_moz_menubar_drag);
   }
 
   nsresult rv =
     LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsDefaultTheme, &metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_windows_default_theme);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_windows_default_theme);
   }
 
   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_MacGraphiteTheme, &metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_mac_graphite_theme);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_mac_graphite_theme);
   }
 
   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_MacYosemiteTheme, &metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_mac_yosemite_theme);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_mac_yosemite_theme);
   }
 
   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsAccentColorInTitlebar, &metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_windows_accent_color_in_titlebar);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_windows_accent_color_in_titlebar);
   }
 
   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_DWMCompositor, &metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_windows_compositor);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_windows_compositor);
   }
 
   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsGlass, &metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_windows_glass);
+    sSystemMetrics->AppendElement((nsStaticAtom*)nsGkAtoms::_moz_windows_glass);
   }
 
   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsClassic, &metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_windows_classic);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_windows_classic);
   }
 
   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_TouchEnabled, &metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_touch_enabled);
+    sSystemMetrics->AppendElement((nsStaticAtom*)nsGkAtoms::_moz_touch_enabled);
   }
 
   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_SwipeAnimationEnabled,
                            &metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_swipe_animation_enabled);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_swipe_animation_enabled);
   }
 
   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_GTKCSDAvailable,
                            &metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_gtk_csd_available);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_gtk_csd_available);
   }
 
   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_GTKCSDTransparentBackground,
                            &metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_gtk_csd_transparent_background);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_gtk_csd_transparent_background);
   }
 
   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_GTKCSDMinimizeButton,
                            &metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_gtk_csd_minimize_button);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_gtk_csd_minimize_button);
   }
 
   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_GTKCSDMaximizeButton,
                            &metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_gtk_csd_maximize_button);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_gtk_csd_maximize_button);
   }
 
   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_GTKCSDCloseButton,
                            &metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_gtk_csd_close_button);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_gtk_csd_close_button);
   }
 
   metricResult =
     LookAndFeel::GetInt(LookAndFeel::eIntID_SystemUsesDarkTheme);
   if (metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::_moz_system_dark_theme);
+    sSystemMetrics->AppendElement(
+      (nsStaticAtom*)nsGkAtoms::_moz_system_dark_theme);
   }
 }
 
 /* static */ void
 nsMediaFeatures::FreeSystemMetrics()
 {
   delete sSystemMetrics;
   sSystemMetrics = nullptr;
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -1842,130 +1842,130 @@ nsTreeBodyFrame::EndUpdateBatch()
 void
 nsTreeBodyFrame::PrefillPropertyArray(int32_t aRowIndex, nsTreeColumn* aCol)
 {
   MOZ_ASSERT(!aCol || aCol->GetFrame(), "invalid column passed");
   mScratchArray.Clear();
 
   // focus
   if (mFocused)
-    mScratchArray.AppendElement(nsGkAtoms::focus);
+    mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::focus);
   else
-    mScratchArray.AppendElement(nsGkAtoms::blur);
+    mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::blur);
 
   // sort
   bool sorted = false;
   mView->IsSorted(&sorted);
   if (sorted)
-    mScratchArray.AppendElement(nsGkAtoms::sorted);
+    mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::sorted);
 
   // drag session
   if (mSlots && mSlots->mIsDragging)
-    mScratchArray.AppendElement(nsGkAtoms::dragSession);
+    mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::dragSession);
 
   if (aRowIndex != -1) {
     if (aRowIndex == mMouseOverRow)
-      mScratchArray.AppendElement(nsGkAtoms::hover);
+      mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::hover);
 
     nsCOMPtr<nsITreeSelection> selection;
     mView->GetSelection(getter_AddRefs(selection));
 
     if (selection) {
       // selected
       bool isSelected;
       selection->IsSelected(aRowIndex, &isSelected);
       if (isSelected)
-        mScratchArray.AppendElement(nsGkAtoms::selected);
+        mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::selected);
 
       // current
       int32_t currentIndex;
       selection->GetCurrentIndex(&currentIndex);
       if (aRowIndex == currentIndex)
-        mScratchArray.AppendElement(nsGkAtoms::current);
+        mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::current);
 
       // active
       if (aCol) {
         RefPtr<nsTreeColumn> currentColumn;
         selection->GetCurrentColumn(getter_AddRefs(currentColumn));
         if (aCol == currentColumn)
-          mScratchArray.AppendElement(nsGkAtoms::active);
+          mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::active);
       }
     }
 
     // container or leaf
     bool isContainer = false;
     mView->IsContainer(aRowIndex, &isContainer);
     if (isContainer) {
-      mScratchArray.AppendElement(nsGkAtoms::container);
+      mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::container);
 
       // open or closed
       bool isOpen = false;
       mView->IsContainerOpen(aRowIndex, &isOpen);
       if (isOpen)
-        mScratchArray.AppendElement(nsGkAtoms::open);
+        mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::open);
       else
-        mScratchArray.AppendElement(nsGkAtoms::closed);
+        mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::closed);
     }
     else {
-      mScratchArray.AppendElement(nsGkAtoms::leaf);
+      mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::leaf);
     }
 
     // drop orientation
     if (mSlots && mSlots->mDropAllowed && mSlots->mDropRow == aRowIndex) {
       if (mSlots->mDropOrient == nsITreeView::DROP_BEFORE)
-        mScratchArray.AppendElement(nsGkAtoms::dropBefore);
+        mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::dropBefore);
       else if (mSlots->mDropOrient == nsITreeView::DROP_ON)
-        mScratchArray.AppendElement(nsGkAtoms::dropOn);
+        mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::dropOn);
       else if (mSlots->mDropOrient == nsITreeView::DROP_AFTER)
-        mScratchArray.AppendElement(nsGkAtoms::dropAfter);
+        mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::dropAfter);
     }
 
     // odd or even
     if (aRowIndex % 2)
-      mScratchArray.AppendElement(nsGkAtoms::odd);
+      mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::odd);
     else
-      mScratchArray.AppendElement(nsGkAtoms::even);
+      mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::even);
 
     Element* baseContent = GetBaseElement();
     if (baseContent && baseContent->HasAttr(kNameSpaceID_None, nsGkAtoms::editing))
-      mScratchArray.AppendElement(nsGkAtoms::editing);
+      mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::editing);
 
     // multiple columns
     if (mColumns->GetColumnAt(1))
-      mScratchArray.AppendElement(nsGkAtoms::multicol);
+      mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::multicol);
   }
 
   if (aCol) {
     mScratchArray.AppendElement(aCol->GetAtom());
 
     if (aCol->IsPrimary())
-      mScratchArray.AppendElement(nsGkAtoms::primary);
+      mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::primary);
 
     if (aCol->GetType() == TreeColumn_Binding::TYPE_CHECKBOX) {
-      mScratchArray.AppendElement(nsGkAtoms::checkbox);
+      mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::checkbox);
 
       if (aRowIndex != -1) {
         nsAutoString value;
         mView->GetCellValue(aRowIndex, aCol, value);
         if (value.EqualsLiteral("true"))
-          mScratchArray.AppendElement(nsGkAtoms::checked);
+          mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::checked);
       }
     }
 
     // Read special properties from attributes on the column content node
     if (aCol->mContent->AttrValueIs(kNameSpaceID_None,
                                     nsGkAtoms::insertbefore,
                                     nsGkAtoms::_true,
                                     eCaseMatters))
-      mScratchArray.AppendElement(nsGkAtoms::insertbefore);
+      mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::insertbefore);
     if (aCol->mContent->AttrValueIs(kNameSpaceID_None,
                                     nsGkAtoms::insertafter,
                                     nsGkAtoms::_true,
                                     eCaseMatters))
-      mScratchArray.AppendElement(nsGkAtoms::insertafter);
+      mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::insertafter);
   }
 }
 
 nsITheme*
 nsTreeBodyFrame::GetTwistyRect(int32_t aRowIndex,
                                nsTreeColumn* aColumn,
                                nsRect& aImageRect,
                                nsRect& aTwistyRect,
--- a/servo/components/style/gecko/regen_atoms.py
+++ b/servo/components/style/gecko/regen_atoms.py
@@ -15,65 +15,41 @@ sys.path.insert(0, os.path.join(os.path.
 
 import build
 
 
 # Matches lines like `GK_ATOM(foo, "foo", 0x12345678, nsStaticAtom, PseudoElementAtom)`.
 PATTERN = re.compile('^GK_ATOM\(([^,]*),[^"]*"([^"]*)",\s*(0x[0-9a-f]+),\s*([^,]*),\s*([^)]*)\)',
                      re.MULTILINE)
 FILE = "include/nsGkAtomList.h"
-CLASS = "nsGkAtoms"
-
-
-def gnu_symbolify(ident):
-    return "_ZN{}{}{}{}E".format(len(CLASS), CLASS, len(ident), ident)
-
-
-def msvc64_symbolify(ident, ty):
-    return "?{}@{}@@2PEAV{}@@EA".format(ident, CLASS, ty)
-
-
-def msvc32_symbolify(ident, ty):
-    # Prepend "\x01" to avoid LLVM prefixing the mangled name with "_".
-    # See https://github.com/rust-lang/rust/issues/36097
-    return "\\x01?{}@{}@@2PAV{}@@A".format(ident, CLASS, ty)
 
 
 def map_atom(ident):
     if ident in {"box", "loop", "match", "mod", "ref",
                  "self", "type", "use", "where", "in"}:
         return ident + "_"
     return ident
 
 
 class Atom:
     def __init__(self, ident, value, hash, ty, atom_type):
-        self.ident = "{}_{}".format(CLASS, ident)
+        self.ident = "nsGkAtoms_{}".format(ident)
         self.original_ident = ident
         self.value = value
         self.hash = hash
         # The Gecko type: "nsStaticAtom", "nsICSSPseudoElement", or "nsIAnonBoxPseudo"
         self.ty = ty
         # The type of atom: "Atom", "PseudoElement", "NonInheritingAnonBox",
         # or "InheritingAnonBox"
         self.atom_type = atom_type
         if self.is_pseudo() or self.is_anon_box():
             self.pseudo_ident = (ident.split("_", 1))[1]
         if self.is_anon_box():
             assert self.is_inheriting_anon_box() or self.is_non_inheriting_anon_box()
 
-    def gnu_symbol(self):
-        return gnu_symbolify(self.original_ident)
-
-    def msvc32_symbol(self):
-        return msvc32_symbolify(self.original_ident, self.ty)
-
-    def msvc64_symbol(self):
-        return msvc64_symbolify(self.original_ident, self.ty)
-
     def type(self):
         return self.ty
 
     def capitalized_pseudo(self):
         return self.pseudo_ident[0].upper() + self.pseudo_ident[1:]
 
     def is_pseudo(self):
         return self.atom_type == "PseudoElementAtom"
@@ -143,29 +119,28 @@ PRELUDE = '''
 
 // Autogenerated file created by components/style/gecko/regen_atoms.py.
 // DO NOT EDIT DIRECTLY
 '''[1:]
 
 IMPORTS = '''
 use gecko_bindings::structs::nsStaticAtom;
 use string_cache::Atom;
-
 '''
 
 UNSAFE_STATIC = '''
 #[inline(always)]
-pub unsafe fn atom_from_static(ptr: *mut nsStaticAtom) -> Atom {
+pub unsafe fn atom_from_static(ptr: *const nsStaticAtom) -> Atom {
     Atom::from_static(ptr)
 }
 '''
 
-ATOM_TEMPLATE = '''
+SATOMS_TEMPLATE = '''
             #[link_name = \"{link_name}\"]
-            pub static {name}: *mut {type};
+            pub static nsGkAtoms_sAtoms: *const nsStaticAtom;
 '''[1:]
 
 CFG_IF_TEMPLATE = '''
 cfg_if! {{
     if #[cfg(not(target_env = "msvc"))] {{
         extern {{
 {gnu}\
         }}
@@ -173,56 +148,61 @@ cfg_if! {{
         extern {{
 {msvc64}\
         }}
     }} else {{
         extern {{
 {msvc32}\
         }}
     }}
-}}
+}}\n
 '''
 
+CONST_TEMPLATE = '''
+pub const k_{name}: isize = {index};
+'''[1:]
+
 RULE_TEMPLATE = '''
 ("{atom}") =>
     {{{{
+        use $crate::string_cache::atom_macro;
         #[allow(unsafe_code)] #[allow(unused_unsafe)]
-        unsafe {{ $crate::string_cache::atom_macro::atom_from_static ($crate::string_cache::atom_macro::{name} as *mut _) }}
+        unsafe {{ atom_macro::atom_from_static(atom_macro::nsGkAtoms_sAtoms.offset(atom_macro::k_{name})) }}
     }}}};
 '''[1:]
 
 MACRO_TEMPLATE = '''
 #[macro_export]
 macro_rules! atom {{
 {body}\
 }}
 '''
 
-
 def write_atom_macro(atoms, file_name):
-    def get_symbols(func):
-        return ''.join([ATOM_TEMPLATE.format(name=atom.ident,
-                                             link_name=func(atom),
-                                             type=atom.type()) for atom in atoms])
-
     with FileAvoidWrite(file_name) as f:
         f.write(PRELUDE)
         f.write(IMPORTS)
-
-        for ty in sorted(set([atom.type() for atom in atoms])):
-            if ty != "nsStaticAtom":
-                f.write("pub enum {} {{}}\n".format(ty))
-
         f.write(UNSAFE_STATIC)
 
-        gnu_symbols = get_symbols(Atom.gnu_symbol)
-        msvc32_symbols = get_symbols(Atom.msvc32_symbol)
-        msvc64_symbols = get_symbols(Atom.msvc64_symbol)
+        gnu_name='_ZN9nsGkAtoms6sAtomsE'
+        gnu_symbols = SATOMS_TEMPLATE.format(link_name=gnu_name)
+
+        # Prepend "\x01" to avoid LLVM prefixing the mangled name with "_".
+        # See https://github.com/rust-lang/rust/issues/36097
+        msvc32_name = '\\x01?sAtoms@nsGkAtoms@@0QBVnsStaticAtom@@B'
+        msvc32_symbols = SATOMS_TEMPLATE.format(link_name=msvc32_name)
+
+        msvc64_name = '?sAtoms@nsGkAtoms@@0QEBVnsStaticAtom@@EB'
+        msvc64_symbols = SATOMS_TEMPLATE.format(link_name=msvc64_name)
+
         f.write(CFG_IF_TEMPLATE.format(gnu=gnu_symbols, msvc32=msvc32_symbols, msvc64=msvc64_symbols))
 
+        consts = [CONST_TEMPLATE.format(name=atom.ident, index=i) for (i, atom) in enumerate(atoms)]
+        f.write('{}'.format(''.join(consts)))
+
         macro_rules = [RULE_TEMPLATE.format(atom=atom.value, name=atom.ident) for atom in atoms]
         f.write(MACRO_TEMPLATE.format(body=''.join(macro_rules)))
 
 
 def write_pseudo_elements(atoms, target_filename):
     pseudos = []
     for atom in atoms:
         if atom.type() == "nsICSSPseudoElement" or atom.type() == "nsICSSAnonBoxPseudo":
--- a/servo/components/style/gecko_string_cache/mod.rs
+++ b/servo/components/style/gecko_string_cache/mod.rs
@@ -1,14 +1,18 @@
 /* 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/. */
 
 #![allow(unsafe_code)]
 
+// This is needed for the constants in atom_macro.rs, because we have some
+// atoms whose names differ only by case, e.g. datetime and dateTime.
+#![allow(non_upper_case_globals)]
+
 //! A drop-in replacement for string_cache, but backed by Gecko `nsAtom`s.
 
 use gecko_bindings::bindings::Gecko_AddRefAtom;
 use gecko_bindings::bindings::Gecko_Atomize;
 use gecko_bindings::bindings::Gecko_Atomize16;
 use gecko_bindings::bindings::Gecko_ReleaseAtom;
 use gecko_bindings::structs::{nsAtom, nsAtom_AtomKind, nsDynamicAtom, nsStaticAtom};
 use nsstring::{nsAString, nsStr};
@@ -275,17 +279,17 @@ impl Atom {
 
     /// Creates an atom from an static atom pointer without checking in release
     /// builds.
     ///
     /// Right now it's only used by the atom macro, and ideally it should keep
     /// that way, now we have sugar for is_static, creating atoms using
     /// Atom::from_raw should involve almost no overhead.
     #[inline]
-    pub unsafe fn from_static(ptr: *mut nsStaticAtom) -> Self {
+    pub unsafe fn from_static(ptr: *const nsStaticAtom) -> Self {
         let atom = Atom(ptr as *mut WeakAtom);
         debug_assert!(
             atom.is_static(),
             "Called from_static for a non-static atom!"
         );
         atom
     }
 
--- a/widget/gtk/nsNativeThemeGTK.cpp
+++ b/widget/gtk/nsNativeThemeGTK.cpp
@@ -320,18 +320,18 @@ nsNativeThemeGTK::GetGtkWidgetAndState(S
                aWidgetType == StyleAppearance::MozMenulistButton) {
       aState->active &= aState->inHover;
     } else if (aWidgetType == StyleAppearance::Treetwisty ||
                aWidgetType == StyleAppearance::Treetwistyopen) {
       nsTreeBodyFrame *treeBodyFrame = do_QueryFrame(aFrame);
       if (treeBodyFrame) {
         const mozilla::AtomArray& atoms =
           treeBodyFrame->GetPropertyArrayForCurrentDrawingItem();
-        aState->selected = atoms.Contains(nsGkAtoms::selected);
-        aState->inHover = atoms.Contains(nsGkAtoms::hover);
+        aState->selected = atoms.Contains((nsStaticAtom*)nsGkAtoms::selected);
+        aState->inHover = atoms.Contains((nsStaticAtom*)nsGkAtoms::hover);
       }
     }
 
     if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) {
       // For these widget types, some element (either a child or parent)
       // actually has element focus, so we check the focused attribute
       // to see whether to draw in the focused state.
       if (aWidgetType == StyleAppearance::NumberInput ||
--- a/xpcom/ds/nsGkAtoms.cpp
+++ b/xpcom/ds/nsGkAtoms.cpp
@@ -59,39 +59,8 @@ extern constexpr GkAtoms gGkAtoms = {
   }
 };
 
 } // namespace detail
 } // namespace mozilla
 
 const nsStaticAtom* const nsGkAtoms::sAtoms = mozilla::detail::gGkAtoms.mAtoms;
 
-// Definition of the pointer to the static atom.
-//
-// Expansion of the example GK_ATOM entries in nsGkAtoms.h:
-//
-//   nsStaticAtom* nsGkAtoms::a =
-//     const_cast<nsStaticAtom*>(
-//       static_cast<const nsStaticAtom*>(
-//         &mozilla::detail::gGkAtoms.mAtoms[
-//           static_cast<size_t>(mozilla::detail::GkAtoms::Atoms::a)]));
-//
-//   nsICSSPseudoElement* nsGkAtoms::bb =
-//     const_cast<nsICSSPseudoElement*>(
-//       static_cast<const nsICSSPseudoElement*>(
-//         &mozilla::detail::gGkAtoms.mAtoms[
-//           static_cast<size_t>(mozilla::detail::GkAtoms::Atoms::bb)]));
-//
-//   nsICSSAnonBoxPseudo* nsGkAtoms::ccc =
-//     const_cast<nsICSSAnonBoxPseudo*>(
-//       static_cast<const nsICSSAnonBoxPseudo*>(
-//         &mozilla::detail::gGkAtoms.mAtoms[
-//           static_cast<size_t>(mozilla::detail::GkAtoms::Atoms::ccc)]));
-//
-#define GK_ATOM(name_, value_, hash_, type_, atom_type_)                       \
-  type_* nsGkAtoms::name_ =                                                    \
-    const_cast<type_*>(                                                        \
-      static_cast<const type_*>(                                               \
-        &mozilla::detail::gGkAtoms.mAtoms[                                     \
-          static_cast<size_t>(mozilla::detail::GkAtoms::Atoms::name_)]));
-#include "nsGkAtomList.h"
-#undef GK_ATOM
-
--- a/xpcom/ds/nsGkAtoms.h
+++ b/xpcom/ds/nsGkAtoms.h
@@ -23,23 +23,22 @@
 //   pointer to the string literal because then the atoms won't end up in
 //   .rodata. Therefore the string literals and the atoms must be arranged in a
 //   way such that a numeric index can be used instead. This numeric index
 //   (nsStaticAtom::mStringOffset) must be computable at compile-time to keep
 //   the static atom constexpr. It should also not be too large (a uint32_t is
 //   reasonable).
 //
 // - Each static atom stores the hash value of its associated string literal;
-//   it's used in various ways. The hash value must be computed at
+//   it's used in various ways. The hash value must be specified at
 //   compile-time, to keep the static atom constexpr.
 //
 // - As well as accessing each static atom via array indexing, we need an
-//   individual pointer, e.g. nsGkAtoms::foo. Ideally this would be constexpr
-//   so it doesn't take up any space in memory. Unfortunately MSVC's constexpr
-//   support is buggy and so this isn't possible yet. See bug 1449787.
+//   individual pointer, e.g. nsGkAtoms::foo. We want this to be constexpr so
+//   it doesn't take up any space in memory.
 //
 // - The array of static atoms can't be in a .h file, because it's a huge
 //   constexpr expression, which would blow out compile times. But the
 //   individual pointers for the static atoms must be in a .h file so they are
 //   public.
 //
 // nsGkAtoms below defines static atoms in a way that satisfies these
 // constraints. It uses nsGkAtomList.h, which defines the names and values of
@@ -105,16 +104,26 @@ struct GkAtoms
     #include "nsGkAtomList.h"
     #undef GK_ATOM
     AtomsCount
   };
 
   const nsStaticAtom mAtoms[static_cast<size_t>(Atoms::AtomsCount)];
 };
 
+// The GkAtoms instance is `extern const` so it can be defined in a .cpp file.
+//
+// XXX: The NS_EXTERNAL_VIS is necessary to work around an apparent GCC bug:
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87494
+#if defined(__GNUC__) && !defined(__clang__)
+extern NS_EXTERNAL_VIS const GkAtoms gGkAtoms;
+#else
+extern const GkAtoms gGkAtoms;
+#endif
+
 } // namespace detail
 } // namespace mozilla
 
 // This class holds the pointers to the individual atoms.
 class nsGkAtoms
 {
 private:
   friend void NS_InitAtomTable();
@@ -129,25 +138,41 @@ private:
 
 public:
   static nsStaticAtom* GetAtomByIndex(size_t aIndex)
   {
     MOZ_ASSERT(aIndex < sAtomsLen);
     return const_cast<nsStaticAtom*>(&sAtoms[aIndex]);
   }
 
-  // The declaration of the pointer to each static atom.
+  // The definition of the pointer to each static atom.
+  //
+  // These types are not `static constexpr <type>* const` -- even though these
+  // atoms are immutable -- because they are often passed to functions with
+  // `nsAtom*` parameters that can be passed both dynamic and static atoms.
   //
   // Expansion of the example GK_ATOM entries above:
   //
-  //   static nsStaticAtom* a;
-  //   static nsICSSPseudoElement* bb;
-  //   static nsICSSAnonBoxPseudo* ccc;
+  //   static constexpr nsStaticAtom* a =
+  //     const_cast<nsStaticAtom*>(
+  //       &mozilla::detail::gGkAtoms.mAtoms[
+  //         static_cast<size_t>(mozilla::detail::GkAtoms::Atoms::a)]);
+  //
+  //   static constexpr nsStaticAtom* bb =
+  //     const_cast<nsStaticAtom*>(
+  //       &mozilla::detail::gGkAtoms.mAtoms[
+  //         static_cast<size_t>(mozilla::detail::GkAtoms::Atoms::bb)]);
   //
-  // XXX: Eventually this should be combined with its definition and the
-  // pointer should be made `constexpr`. See bug 1449787.
-  #define GK_ATOM(name_, value_, hash_, type_, atom_type_) \
-    static type_* name_;
+  //   static constexpr nsStaticAtom* ccc =
+  //     const_cast<nsStaticAtom*>(
+  //       &mozilla::detail::gGkAtoms.mAtoms[
+  //         static_cast<size_t>(mozilla::detail::GkAtoms::Atoms::ccc)]);
+  //
+  #define GK_ATOM(name_, value_, hash_, type_, atom_type_)                    \
+    static constexpr nsStaticAtom* name_ =                                    \
+      const_cast<nsStaticAtom*>(                                              \
+        &mozilla::detail::gGkAtoms.mAtoms[                                    \
+          static_cast<size_t>(mozilla::detail::GkAtoms::Atoms::name_)]);
   #include "nsGkAtomList.h"
   #undef GK_ATOM
 };
 
 #endif /* nsGkAtoms_h___ */