Bug 1276908 - Optimise post barriers in the JIT for constant objects r=terrence r=jandem
authorJon Coppeard <jcoppeard@mozilla.com>
Tue, 21 Jun 2016 15:30:35 +0100
changeset 302212 b8b6dd03d8fd907fcbea09caa44a6a937947e1c7
parent 302211 85911372f2765079241357835fe84901b49c24c2
child 302213 c2becf602688b56146321cb7c1f4d6297bb86bd1
push id30356
push usercbook@mozilla.com
push dateWed, 22 Jun 2016 11:45:58 +0000
treeherdermozilla-central@4e17dca08962 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence, jandem
bugs1276908
milestone50.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 1276908 - Optimise post barriers in the JIT for constant objects r=terrence r=jandem
js/src/gc/Heap.h
js/src/gc/StoreBuffer.h
js/src/jit/CodeGenerator.cpp
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -683,16 +683,20 @@ class Arena
     }
 
     template <typename T>
     size_t finalize(FreeOp* fop, AllocKind thingKind, size_t thingSize);
 
     static void staticAsserts();
 
     void unmarkAll();
+
+    static size_t offsetOfBufferedCells() {
+        return offsetof(Arena, bufferedCells);
+    }
 };
 
 static_assert(ArenaZoneOffset == offsetof(Arena, zone),
               "The hardcoded API zone offset must match the actual offset.");
 
 static_assert(sizeof(Arena) == ArenaSize,
               "ArenaSize must match the actual size of the Arena structure.");
 
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -473,16 +473,23 @@ class ArenaCellSet
     static ArenaCellSet Empty;
 
     static size_t getCellIndex(const TenuredCell* cell);
     static void getWordIndexAndMask(size_t cellIndex, size_t* wordp, uint32_t* maskp);
 
     // Attempt to trigger a minor GC if free space in the nursery (where these
     // objects are allocated) falls below this threshold.
     static const size_t NurseryFreeThresholdBytes = 64 * 1024;
+
+    static size_t offsetOfArena() {
+        return offsetof(ArenaCellSet, arena);
+    }
+    static size_t offsetOfBits() {
+        return offsetof(ArenaCellSet, bits);
+    }
 };
 
 ArenaCellSet* AllocateWholeCellSet(Arena* arena);
 
 } /* namespace gc */
 } /* namespace js */
 
 #endif /* gc_StoreBuffer_h */
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3436,55 +3436,103 @@ class OutOfLineCallPostWriteBarrier : pu
         return lir_;
     }
     const LAllocation* object() const {
         return object_;
     }
 };
 
 static void
-EmitPostWriteBarrier(MacroAssembler& masm, Register objreg, bool isGlobal,
-                     AllocatableGeneralRegisterSet regs)
-{
+EmitStoreBufferCheckForConstant(MacroAssembler& masm, JSObject* object,
+                                AllocatableGeneralRegisterSet& regs, Label* exit, Label* callVM)
+{
+    Register temp = regs.takeAny();
+
+    const gc::TenuredCell* cell = &object->asTenured();
+    gc::Arena* arena = cell->arena();
+
+    Register cells = temp;
+    masm.loadPtr(AbsoluteAddress(&arena->bufferedCells), cells);
+
+    size_t index = gc::ArenaCellSet::getCellIndex(cell);
+    size_t word;
+    uint32_t mask;
+    gc::ArenaCellSet::getWordIndexAndMask(index, &word, &mask);
+    size_t offset = gc::ArenaCellSet::offsetOfBits() + word * sizeof(uint32_t);
+
+    masm.branchTest32(Assembler::NonZero, Address(cells, offset), Imm32(mask), exit);
+
+    // Check whether this is the sentinel set and if so call the VM to allocate
+    // one for this arena.
+    masm.branchPtr(Assembler::Equal, Address(cells, gc::ArenaCellSet::offsetOfArena()),
+                   ImmPtr(nullptr), callVM);
+
+    // Add the cell to the set.
+    masm.or32(Imm32(mask), Address(cells, offset));
+    masm.jump(exit);
+
+    regs.add(temp);
+}
+
+static void
+EmitPostWriteBarrier(MacroAssembler& masm, Register objreg, JSObject* maybeConstant, bool isGlobal,
+                     AllocatableGeneralRegisterSet& regs)
+{
+    MOZ_ASSERT_IF(isGlobal, maybeConstant);
+
+    Label callVM;
+    Label exit;
+
+    // We already have a fast path to check whether a global is in the store
+    // buffer.
+    if (!isGlobal && maybeConstant)
+        EmitStoreBufferCheckForConstant(masm, maybeConstant, regs, &exit, &callVM);
+
+    // Call into the VM to barrier the write.
+    masm.bind(&callVM);
+
     Register runtimereg = regs.takeAny();
     masm.mov(ImmPtr(GetJitContext()->runtime), runtimereg);
 
     void (*fun)(JSRuntime*, JSObject*) = isGlobal ? PostGlobalWriteBarrier : PostWriteBarrier;
     masm.setupUnalignedABICall(regs.takeAny());
     masm.passABIArg(runtimereg);
     masm.passABIArg(objreg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, fun));
+
+    masm.bind(&exit);
 }
 
 void
 CodeGenerator::emitPostWriteBarrier(const LAllocation* obj)
 {
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
 
     Register objreg;
+    JSObject* object = nullptr;
     bool isGlobal = false;
     if (obj->isConstant()) {
-        JSObject* object = &obj->toConstant()->toObject();
+        object = &obj->toConstant()->toObject();
         isGlobal = object->is<GlobalObject>();
         objreg = regs.takeAny();
         masm.movePtr(ImmGCPtr(object), objreg);
     } else {
         objreg = ToRegister(obj);
         regs.takeUnchecked(objreg);
     }
 
-    EmitPostWriteBarrier(masm, objreg, isGlobal, regs);
+    EmitPostWriteBarrier(masm, objreg, object, isGlobal, regs);
 }
 
 void
 CodeGenerator::emitPostWriteBarrier(Register objreg)
 {
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
     regs.takeUnchecked(objreg);
-    EmitPostWriteBarrier(masm, objreg, false, regs);
+    EmitPostWriteBarrier(masm, objreg, nullptr, false, regs);
 }
 
 void
 CodeGenerator::visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier* ool)
 {
     saveLiveVolatile(ool->lir());
     const LAllocation* obj = ool->object();
     emitPostWriteBarrier(obj);