--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4,16 +4,17 @@
* 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 "jit/CodeGenerator.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
+#include "mozilla/MathAlgorithms.h"
#include "jslibmath.h"
#include "jsmath.h"
#include "jsnum.h"
#include "jsprf.h"
#include "builtin/Eval.h"
#include "builtin/TypedObject.h"
@@ -3607,32 +3608,118 @@ CodeGenerator::visitNewObjectVMCall(LNew
if (ReturnReg != objReg)
masm.movePtr(ReturnReg, objReg);
restoreLive(lir);
return true;
}
+static bool
+ShouldInitFixedSlots(LInstruction *lir, JSObject *templateObj)
+{
+ // Look for StoreFixedSlot instructions following an object allocation
+ // that write to this object before a GC is triggered or this object is
+ // passed to a VM call. If all fixed slots will be initialized, the
+ // allocation code doesn't need to set the slots to |undefined|.
+
+ uint32_t nfixed = templateObj->numUsedFixedSlots();
+ if (nfixed == 0)
+ return false;
+
+ // Only optimize if all fixed slots are initially |undefined|, so that we
+ // can assume incremental pre-barriers are not necessary. See also the
+ // comment below.
+ for (uint32_t slot = 0; slot < nfixed; slot++) {
+ if (!templateObj->getSlot(slot).isUndefined())
+ return true;
+ }
+
+ // Keep track of the fixed slots that are initialized. initializedSlots is
+ // a bit mask with a bit for each slot.
+ MOZ_ASSERT(nfixed <= JSObject::MAX_FIXED_SLOTS);
+ static_assert(JSObject::MAX_FIXED_SLOTS <= 32, "Slot bits must fit in 32 bits");
+ uint32_t initializedSlots = 0;
+ uint32_t numInitialized = 0;
+
+ MInstruction *allocMir = lir->mirRaw()->toInstruction();
+ MBasicBlock *block = allocMir->block();
+
+ // Skip the allocation instruction.
+ MInstructionIterator iter = block->begin(allocMir);
+ MOZ_ASSERT(*iter == allocMir);
+ iter++;
+
+ while (true) {
+ for (; iter != block->end(); iter++) {
+ if (iter->isNop() || iter->isConstant() || iter->isPostWriteBarrier()) {
+ // These instructions won't trigger a GC or read object slots.
+ continue;
+ }
+
+ if (iter->isStoreFixedSlot()) {
+ MStoreFixedSlot *store = iter->toStoreFixedSlot();
+ if (store->object() != allocMir)
+ return true;
+
+ // We may not initialize this object slot on allocation, so the
+ // pre-barrier could read uninitialized memory. Simply disable
+ // the barrier for this store: the object was just initialized
+ // so the barrier is not necessary.
+ store->setNeedsBarrier(false);
+
+ uint32_t slot = store->slot();
+ MOZ_ASSERT(slot < nfixed);
+ if ((initializedSlots & (1 << slot)) == 0) {
+ numInitialized++;
+ initializedSlots |= (1 << slot);
+
+ if (numInitialized == nfixed) {
+ // All fixed slots will be initialized.
+ MOZ_ASSERT(mozilla::CountPopulation32(initializedSlots) == nfixed);
+ return false;
+ }
+ }
+ continue;
+ }
+
+ if (iter->isGoto()) {
+ block = iter->toGoto()->target();
+ if (block->numPredecessors() != 1)
+ return true;
+ break;
+ }
+
+ // Unhandled instruction, assume it bails or reads object slots.
+ return true;
+ }
+ iter = block->begin();
+ }
+
+ MOZ_ASSUME_UNREACHABLE("Shouldn't get here");
+}
+
bool
CodeGenerator::visitNewObject(LNewObject *lir)
{
JS_ASSERT(gen->info().executionMode() == SequentialExecution);
Register objReg = ToRegister(lir->output());
Register tempReg = ToRegister(lir->temp());
JSObject *templateObject = lir->mir()->templateObject();
if (lir->mir()->shouldUseVM())
return visitNewObjectVMCall(lir);
OutOfLineNewObject *ool = new(alloc()) OutOfLineNewObject(lir);
if (!addOutOfLineCode(ool))
return false;
- masm.createGCObject(objReg, tempReg, templateObject, lir->mir()->initialHeap(), ool->entry());
+ bool initFixedSlots = ShouldInitFixedSlots(lir, templateObject);
+ masm.createGCObject(objReg, tempReg, templateObject, lir->mir()->initialHeap(), ool->entry(),
+ initFixedSlots);
masm.bind(ool->rejoin());
return true;
}
bool
CodeGenerator::visitOutOfLineNewObject(OutOfLineNewObject *ool)
{
@@ -3657,17 +3744,19 @@ CodeGenerator::visitNewDeclEnvObject(LNe
// If we have a template object, we can inline call object creation.
OutOfLineCode *ool = oolCallVM(NewDeclEnvObjectInfo, lir,
(ArgList(), ImmGCPtr(info.funMaybeLazy()),
Imm32(gc::DefaultHeap)),
StoreRegisterTo(objReg));
if (!ool)
return false;
- masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, ool->entry());
+ bool initFixedSlots = ShouldInitFixedSlots(lir, templateObj);
+ masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, ool->entry(),
+ initFixedSlots);
masm.bind(ool->rejoin());
return true;
}
typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleShape, HandleTypeObject);
static const VMFunction NewCallObjectInfo =
FunctionInfo<NewCallObjectFn>(NewCallObject);
@@ -3683,17 +3772,19 @@ CodeGenerator::visitNewCallObject(LNewCa
OutOfLineCode *ool = oolCallVM(NewCallObjectInfo, lir,
(ArgList(), ImmGCPtr(templateObj->lastProperty()),
ImmGCPtr(templateObj->type())),
StoreRegisterTo(objReg));
if (!ool)
return false;
// Inline call object creation, using the OOL path only for tricky cases.
- masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, ool->entry());
+ bool initFixedSlots = ShouldInitFixedSlots(lir, templateObj);
+ masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, ool->entry(),
+ initFixedSlots);
masm.bind(ool->rejoin());
return true;
}
typedef JSObject *(*NewSingletonCallObjectFn)(JSContext *, HandleShape);
static const VMFunction NewSingletonCallObjectInfo =
FunctionInfo<NewSingletonCallObjectFn>(NewSingletonCallObject);
@@ -4033,17 +4124,19 @@ CodeGenerator::visitCreateThisWithTempla
if (!ool)
return false;
// Allocate. If the FreeList is empty, call to VM, which may GC.
masm.newGCThing(objReg, tempReg, templateObject, lir->mir()->initialHeap(), ool->entry());
// Initialize based on the templateObject.
masm.bind(ool->rejoin());
- masm.initGCThing(objReg, tempReg, templateObject);
+
+ bool initFixedSlots = ShouldInitFixedSlots(lir, templateObject);
+ masm.initGCThing(objReg, tempReg, templateObject, initFixedSlots);
return true;
}
typedef JSObject *(*NewIonArgumentsObjectFn)(JSContext *cx, IonJSFrameLayout *frame, HandleObject);
static const VMFunction NewIonArgumentsObjectInfo =
FunctionInfo<NewIonArgumentsObjectFn>((NewIonArgumentsObjectFn) ArgumentsObject::createForIon);
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -586,24 +586,24 @@ MacroAssembler::newGCThing(Register resu
gc::AllocKind allocKind = templateObj->tenuredGetAllocKind();
JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
allocateObject(result, temp, allocKind, templateObj->numDynamicSlots(), initialHeap, fail);
}
void
MacroAssembler::createGCObject(Register obj, Register temp, JSObject *templateObj,
- gc::InitialHeap initialHeap, Label *fail)
+ gc::InitialHeap initialHeap, Label *fail, bool initFixedSlots)
{
uint32_t nDynamicSlots = templateObj->numDynamicSlots();
gc::AllocKind allocKind = templateObj->tenuredGetAllocKind();
JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
allocateObject(obj, temp, allocKind, nDynamicSlots, initialHeap, fail);
- initGCThing(obj, temp, templateObj);
+ initGCThing(obj, temp, templateObj, initFixedSlots);
}
// Inlined equivalent of gc::AllocateNonObject, without failure case handling.
// Non-object allocation does not need to worry about slots, so can take a
// simpler path.
void
MacroAssembler::allocateNonObject(Register result, Register temp, gc::AllocKind allocKind, Label *fail)
@@ -737,56 +737,60 @@ FindStartOfUndefinedSlots(JSObject *temp
for (uint32_t first = nslots; first != 0; --first) {
if (templateObj->getSlot(first - 1) != UndefinedValue())
return first;
}
return 0;
}
void
-MacroAssembler::initGCSlots(Register obj, Register slots, JSObject *templateObj)
+MacroAssembler::initGCSlots(Register obj, Register slots, JSObject *templateObj,
+ bool initFixedSlots)
{
// Slots of non-array objects are required to be initialized.
// Use the values currently in the template object.
uint32_t nslots = templateObj->lastProperty()->slotSpan(templateObj->getClass());
if (nslots == 0)
return;
- uint32_t nfixed = Min(templateObj->numFixedSlots(), nslots);
+ uint32_t nfixed = templateObj->numUsedFixedSlots();
uint32_t ndynamic = templateObj->numDynamicSlots();
// Attempt to group slot writes such that we minimize the amount of
// duplicated data we need to embed in code and load into registers. In
// general, most template object slots will be undefined except for any
// reserved slots. Since reserved slots come first, we split the object
// logically into independent non-UndefinedValue writes to the head and
// duplicated writes of UndefinedValue to the tail. For the majority of
// objects, the "tail" will be the entire slot range.
uint32_t startOfUndefined = FindStartOfUndefinedSlots(templateObj, nslots);
JS_ASSERT(startOfUndefined <= nfixed); // Reserved slots must be fixed.
// Copy over any preserved reserved slots.
copySlotsFromTemplate(obj, templateObj, 0, startOfUndefined);
// Fill the rest of the fixed slots with undefined.
- fillSlotsWithUndefined(Address(obj, JSObject::getFixedSlotOffset(startOfUndefined)), slots,
- startOfUndefined, nfixed);
+ if (initFixedSlots) {
+ fillSlotsWithUndefined(Address(obj, JSObject::getFixedSlotOffset(startOfUndefined)), slots,
+ startOfUndefined, nfixed);
+ }
if (ndynamic) {
// We are short one register to do this elegantly. Borrow the obj
// register briefly for our slots base address.
push(obj);
loadPtr(Address(obj, JSObject::offsetOfSlots()), obj);
fillSlotsWithUndefined(Address(obj, 0), slots, 0, ndynamic);
pop(obj);
}
}
void
-MacroAssembler::initGCThing(Register obj, Register slots, JSObject *templateObj)
+MacroAssembler::initGCThing(Register obj, Register slots, JSObject *templateObj,
+ bool initFixedSlots)
{
// Fast initialization of an empty object returned by allocateObject().
JS_ASSERT(!templateObj->hasDynamicElements());
storePtr(ImmGCPtr(templateObj->lastProperty()), Address(obj, JSObject::offsetOfShape()));
storePtr(ImmGCPtr(templateObj->type()), Address(obj, JSObject::offsetOfType()));
if (templateObj->hasDynamicSlots())
@@ -813,17 +817,17 @@ MacroAssembler::initGCThing(Register obj
store32(Imm32(templateObj->shouldConvertDoubleElements()
? ObjectElements::CONVERT_DOUBLE_ELEMENTS
: 0),
Address(obj, elementsOffset + ObjectElements::offsetOfFlags()));
JS_ASSERT(!templateObj->hasPrivate());
} else {
storePtr(ImmPtr(emptyObjectElements), Address(obj, JSObject::offsetOfElements()));
- initGCSlots(obj, slots, templateObj);
+ initGCSlots(obj, slots, templateObj, initFixedSlots);
if (templateObj->hasPrivate()) {
uint32_t nfixed = templateObj->numFixedSlots();
storePtr(ImmPtr(templateObj->getPrivate()),
Address(obj, JSObject::getPrivateDataOffset(nfixed)));
}
}
}
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -797,27 +797,28 @@ class MacroAssembler : public MacroAssem
size_t nDynamicSlots, gc::InitialHeap initialHeap, Label *fail);
void freeSpanAllocate(Register result, Register temp, gc::AllocKind allocKind, Label *fail);
void allocateObject(Register result, Register slots, gc::AllocKind allocKind,
uint32_t nDynamicSlots, gc::InitialHeap initialHeap, Label *fail);
void allocateNonObject(Register result, Register temp, gc::AllocKind allocKind, Label *fail);
void copySlotsFromTemplate(Register obj, const JSObject *templateObj,
uint32_t start, uint32_t end);
void fillSlotsWithUndefined(Address addr, Register temp, uint32_t start, uint32_t end);
- void initGCSlots(Register obj, Register temp, JSObject *templateObj);
+ void initGCSlots(Register obj, Register temp, JSObject *templateObj, bool initFixedSlots);
public:
void callMallocStub(size_t nbytes, Register result, Label *fail);
void callFreeStub(Register slots);
void createGCObject(Register result, Register temp, JSObject *templateObj,
- gc::InitialHeap initialHeap, Label *fail);
+ gc::InitialHeap initialHeap, Label *fail, bool initFixedSlots = true);
void newGCThing(Register result, Register temp, JSObject *templateObj,
gc::InitialHeap initialHeap, Label *fail);
- void initGCThing(Register obj, Register temp, JSObject *templateObj);
+ void initGCThing(Register obj, Register temp, JSObject *templateObj,
+ bool initFixedSlots = true);
void newGCString(Register result, Register temp, Label *fail);
void newGCFatInlineString(Register result, Register temp, Label *fail);
void newGCThingPar(Register result, Register cx, Register tempReg1, Register tempReg2,
gc::AllocKind allocKind, Label *fail);
void newGCThingPar(Register result, Register cx, Register tempReg1, Register tempReg2,
JSObject *templateObject, Label *fail);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -6995,18 +6995,18 @@ class MStoreFixedSlot
}
AliasSet getAliasSet() const {
return AliasSet::Store(AliasSet::FixedSlot);
}
bool needsBarrier() const {
return needsBarrier_;
}
- void setNeedsBarrier() {
- needsBarrier_ = true;
+ void setNeedsBarrier(bool needsBarrier = true) {
+ needsBarrier_ = needsBarrier;
}
};
typedef Vector<JSObject *, 4, IonAllocPolicy> ObjectVector;
typedef Vector<bool, 4, IonAllocPolicy> BoolVector;
class InlinePropertyTable : public TempObject
{
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -566,16 +566,20 @@ class ObjectImpl : public gc::BarrieredC
types::TypeObject *typeRaw() const {
return type_;
}
uint32_t numFixedSlots() const {
return reinterpret_cast<const shadow::Object *>(this)->numFixedSlots();
}
+ uint32_t numUsedFixedSlots() const {
+ uint32_t nslots = lastProperty()->slotSpan(getClass());
+ return Min(nslots, numFixedSlots());
+ }
/*
* Whether this is the only object which has its specified type. This
* object will have its type constructed lazily as needed by analysis.
*/
bool hasSingletonType() const {
return !!type_->singleton();
}