commit 25553ff0fe4665a51149d8be27881a7920c2bbba
parent 3d0935940c65ed6ca3053ba1575b53604014f108
Author: Steve Fink <sfink@mozilla.com>
Date: Tue, 14 Oct 2025 19:24:31 +0000
Bug 1935829 - Use tagged ptrs for EphemeronEdge r=jonco
Differential Revision: https://phabricator.services.mozilla.com/D266262
Diffstat:
3 files changed, 35 insertions(+), 14 deletions(-)
diff --git a/js/src/gc/GCMarker.h b/js/src/gc/GCMarker.h
@@ -53,16 +53,34 @@ class MarkStackIter;
class ParallelMarkTask;
class UnmarkGrayTracer;
-// Ephemeron edges have two source nodes and one target, and mark the target
-// with the minimum (least-marked) color of the sources. Currently, one of
-// those sources will always be a WeakMapBase, so this will refer to its color
-// at the time the edge is traced through. The other source's color will be
-// given by the current mark color of the GCMarker.
-struct EphemeronEdge {
- MarkColor color;
- Cell* target;
-
- EphemeronEdge(MarkColor color_, Cell* cell) : color(color_), target(cell) {}
+// Ephemerons are edges from a source to a target that are only materialized
+// into a table when the owner is marked. (The owner is something like a
+// WeakMap, which contains a set of ephemerons each going from a WeakMap key to
+// its value.) When marking a ephemeron, only the color of the owner is needed:
+// the target is marked with the minimum (least-marked) color of the owner and
+// source. So an EphemeronEdge need store only the owner color and the target
+// pointer, which can fit into a tagged pointer since targets are aligned Cells.
+//
+// Note: if the owner's color changes, new EphemeronEdges will be created for
+// it.
+class EphemeronEdge {
+ static constexpr uintptr_t ColorMask = 0x3;
+ static_assert(uintptr_t(MarkColor::Gray) <= ColorMask);
+ static_assert(uintptr_t(MarkColor::Black) <= ColorMask);
+ static_assert(ColorMask < CellAlignBytes);
+
+ uintptr_t taggedTarget;
+
+ public:
+ EphemeronEdge(MarkColor color, Cell* cell)
+ : taggedTarget(uintptr_t(cell) | uintptr_t(color)) {
+ MOZ_ASSERT((uintptr_t(cell) & ColorMask) == 0);
+ }
+
+ MarkColor color() const { return MarkColor(taggedTarget & ColorMask); }
+ Cell* target() const {
+ return reinterpret_cast<Cell*>(taggedTarget & ~ColorMask);
+ }
};
using EphemeronEdgeVector = Vector<EphemeronEdge, 2, js::SystemAllocPolicy>;
diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp
@@ -742,10 +742,10 @@ void GCMarker::markEphemeronEdges(EphemeronEdgeVector& edges,
DebugOnly<size_t> initialLength = edges.length();
for (auto& edge : edges) {
- MarkColor targetColor = std::min(srcColor, MarkColor(edge.color));
+ MarkColor targetColor = std::min(srcColor, MarkColor(edge.color()));
MOZ_ASSERT(markColor() >= targetColor);
if (targetColor == markColor()) {
- ApplyGCThingTyped(edge.target, edge.target->getTraceKind(),
+ ApplyGCThingTyped(edge.target(), edge.target()->getTraceKind(),
[this](auto t) {
markAndTraverse<MarkingOptions::MarkImplicitEdges>(t);
});
@@ -762,7 +762,7 @@ void GCMarker::markEphemeronEdges(EphemeronEdgeVector& edges,
// delegate zone to get marked later, look up an edge in this table, and
// then try to mark something in a Zone that is no longer marking.
if (srcColor == MarkColor::Black && markColor() == MarkColor::Black) {
- edges.eraseIf([](auto& edge) { return edge.color == MarkColor::Black; });
+ edges.eraseIf([](auto& edge) { return edge.color() == MarkColor::Black; });
}
}
diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp
@@ -257,7 +257,10 @@ static void EraseIf(js::gc::EphemeronEdgeVector& entries, Pred pred) {
static void SweepEphemeronEdgesWhileMinorSweeping(
js::gc::EphemeronEdgeVector& entries) {
EraseIf(entries, [](js::gc::EphemeronEdge& edge) -> bool {
- return IsAboutToBeFinalizedDuringMinorSweep(&edge.target);
+ Cell* target = edge.target();
+ bool dead = IsAboutToBeFinalizedDuringMinorSweep(&target);
+ edge = js::gc::EphemeronEdge(edge.color(), target);
+ return dead;
});
}