Bug 1113240 - Allow optimizing nursery-allocated getters/setters in Ion. r=bhackett

This commit is contained in:
Jan de Mooij 2015-01-31 14:52:04 +01:00
parent f34f010bc1
commit 0743c11f2d
29 changed files with 464 additions and 34 deletions

View File

@ -0,0 +1,17 @@
var threshold = getJitCompilerOptions()["ion.warmup.trigger"] + 101;
function bar(i) {
if (!i)
with (this) {}; // Don't inline.
if (i === threshold)
minorgc();
}
function f() {
var o2 = Object.create({get foo() { return this.x; }, set foo(x) { this.x = x + 1; }});
for (var i=0; i<2000; i++) {
o2.foo = i;
assertEq(o2.foo, i + 1);
bar(i);
}
}
f();

View File

@ -3334,14 +3334,6 @@ IsCacheableGetPropCall(JSContext *cx, JSObject *obj, JSObject *holder, Shape *sh
return false;
JSFunction *func = &shape->getterObject()->as<JSFunction>();
// Information from get prop call ICs may be used directly from Ion code,
// and should not be nursery allocated.
if (IsInsideNursery(holder) || IsInsideNursery(func)) {
*isTemporarilyUnoptimizable = true;
return false;
}
if (func->isNative()) {
*isScripted = false;
return true;
@ -3457,13 +3449,6 @@ IsCacheableSetPropCall(JSContext *cx, JSObject *obj, JSObject *holder, Shape *sh
JSFunction *func = &shape->setterObject()->as<JSFunction>();
// Information from set prop call ICs may be used directly from Ion code,
// and should not be nursery allocated.
if (IsInsideNursery(holder) || IsInsideNursery(func)) {
*isTemporarilyUnoptimizable = true;
return false;
}
if (func->isNative()) {
*isScripted = false;
return true;

View File

@ -2154,6 +2154,19 @@ CodeGenerator::visitPointer(LPointer *lir)
masm.movePtr(ImmPtr(lir->ptr()), ToRegister(lir->output()));
}
void
CodeGenerator::visitNurseryObject(LNurseryObject *lir)
{
Register output = ToRegister(lir->output());
uint32_t index = lir->mir()->index();
// Store a dummy JSObject pointer. We will fix it up on the main thread,
// in JitCode::fixupNurseryObjects. The low bit is set to distinguish
// it from a real JSObject pointer.
JSObject *ptr = reinterpret_cast<JSObject*>((uintptr_t(index) << 1) | 1);
masm.movePtr(ImmGCPtr(IonNurseryPtr(ptr)), output);
}
void
CodeGenerator::visitSlots(LSlots *lir)
{
@ -7383,6 +7396,9 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints)
}
#endif
// Replace dummy JSObject pointers embedded by LNurseryObject.
code->fixupNurseryObjects(cx, gen->nurseryObjects());
// The correct state for prebarriers is unknown until the end of compilation,
// since a GC can occur during code generation. All barriers are emitted
// off-by-default, and are toggled on here if necessary.

View File

@ -111,6 +111,7 @@ class CodeGenerator : public CodeGeneratorSpecific
void visitLambdaArrow(LLambdaArrow *lir);
void visitLambdaForSingleton(LLambdaForSingleton *lir);
void visitPointer(LPointer *lir);
void visitNurseryObject(LNurseryObject *lir);
void visitSlots(LSlots *lir);
void visitLoadSlotT(LLoadSlotT *lir);
void visitLoadSlotV(LLoadSlotV *lir);

View File

@ -163,7 +163,8 @@ JitRuntime::JitRuntime()
osrTempData_(nullptr),
mutatingBackedgeList_(false),
ionReturnOverride_(MagicValue(JS_ARG_POISON)),
jitcodeGlobalTable_(nullptr)
jitcodeGlobalTable_(nullptr),
hasIonNurseryObjects_(false)
{
}
@ -653,6 +654,17 @@ JitCode::trace(JSTracer *trc)
}
}
void
JitCode::fixupNurseryObjects(JSContext *cx, const ObjectVector &nurseryObjects)
{
if (nurseryObjects.empty() || !dataRelocTableBytes_)
return;
uint8_t *start = code_ + dataRelocTableOffset();
CompactBufferReader reader(start, start + dataRelocTableBytes_);
MacroAssembler::FixupNurseryObjects(cx, this, reader, nurseryObjects);
}
void
JitCode::finalize(FreeOp *fop)
{
@ -1671,6 +1683,62 @@ AttachFinishedCompilations(JSContext *cx)
}
}
void
MIRGenerator::traceNurseryObjects(JSTracer *trc)
{
MarkObjectRootRange(trc, nurseryObjects_.length(), nurseryObjects_.begin(), "ion-nursery-objects");
}
class MarkOffThreadNurseryObjects : public gc::BufferableRef
{
public:
void mark(JSTracer *trc);
};
void
MarkOffThreadNurseryObjects::mark(JSTracer *trc)
{
JSRuntime *rt = trc->runtime();
MOZ_ASSERT(rt->jitRuntime()->hasIonNurseryObjects());
rt->jitRuntime()->setHasIonNurseryObjects(false);
AutoLockHelperThreadState lock;
if (!HelperThreadState().threads)
return;
// Trace nursery objects of any builders which haven't started yet.
GlobalHelperThreadState::IonBuilderVector &worklist = HelperThreadState().ionWorklist();
for (size_t i = 0; i < worklist.length(); i++) {
jit::IonBuilder *builder = worklist[i];
if (builder->script()->runtimeFromAnyThread() == rt)
builder->traceNurseryObjects(trc);
}
// Trace nursery objects of in-progress entries.
for (size_t i = 0; i < HelperThreadState().threadCount; i++) {
HelperThread &helper = HelperThreadState().threads[i];
if (helper.ionBuilder && helper.ionBuilder->script()->runtimeFromAnyThread() == rt)
helper.ionBuilder->traceNurseryObjects(trc);
}
// Trace nursery objects of any completed entries.
GlobalHelperThreadState::IonBuilderVector &finished = HelperThreadState().ionFinishedList();
for (size_t i = 0; i < finished.length(); i++) {
jit::IonBuilder *builder = finished[i];
if (builder->script()->runtimeFromAnyThread() == rt)
builder->traceNurseryObjects(trc);
}
// Trace nursery objects of lazy-linked builders.
jit::IonBuilder *builder = HelperThreadState().ionLazyLinkList().getFirst();
while (builder) {
if (builder->script()->runtimeFromAnyThread() == rt)
builder->traceNurseryObjects(trc);
builder = builder->getNext();
}
}
static inline bool
OffThreadCompilationAvailable(JSContext *cx)
{
@ -1866,6 +1934,15 @@ IonCompile(JSContext *cx, JSScript *script,
JitSpew(JitSpew_IonLogs, "Can't log script %s:%d. (Compiled on background thread.)",
builderScript->filename(), builderScript->lineno());
JSRuntime *rt = cx->runtime();
if (!builder->nurseryObjects().empty() && !rt->jitRuntime()->hasIonNurseryObjects()) {
// Ensure the builder's nursery objects are marked when a nursery
// GC happens on the main thread.
MarkOffThreadNurseryObjects mark;
rt->gc.storeBuffer.putGeneric(mark);
rt->jitRuntime()->setHasIonNurseryObjects(true);
}
if (!StartOffThreadIonCompile(cx, builder)) {
JitSpew(JitSpew_IonAbort, "Unable to start off-thread ion compilation.");
return AbortReason_Alloc;

View File

@ -216,6 +216,37 @@ IonBuilder::spew(const char *message)
#endif
}
MInstruction *
IonBuilder::constantMaybeNursery(JSObject *obj)
{
MOZ_ASSERT(obj);
if (!IsInsideNursery(obj))
return constant(ObjectValue(*obj));
// If |obj| is in the nursery, we have to add it to the list of nursery
// objects that get traced during off-thread compilation. We use
// MNurseryObject to ensure we will patch the code with the right
// pointer after codegen is done.
size_t index = UINT32_MAX;
for (size_t i = 0, len = nurseryObjects_.length(); i < len; i++) {
if (nurseryObjects_[i] == obj) {
index = i;
break;
}
}
if (index == UINT32_MAX) {
if (!nurseryObjects_.append(obj))
return nullptr;
index = nurseryObjects_.length() - 1;
}
MNurseryObject *ins = MNurseryObject::New(alloc(), obj, index, constraints());
current->add(ins);
return ins;
}
static inline int32_t
GetJumpOffset(jsbytecode *pc)
{
@ -5838,6 +5869,9 @@ bool
IonBuilder::testShouldDOMCall(types::TypeSet *inTypes,
JSFunction *func, JSJitInfo::OpType opType)
{
if (IsInsideNursery(func))
return false;
if (!func->isNative() || !func->jitInfo())
return false;
@ -9297,9 +9331,7 @@ IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyN
}
}
if (!type->hasTenuredProto())
return false;
JSObject *proto = type->proto().toObjectOrNull();
JSObject *proto = type->protoMaybeInNursery().toObjectOrNull();
if (proto == foundProto)
break;
if (!proto) {
@ -9307,7 +9339,7 @@ IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyN
// object's prototype chain.
return false;
}
type = types::TypeObjectKey::get(type->proto().toObjectOrNull());
type = types::TypeObjectKey::get(proto);
}
}
@ -9336,14 +9368,14 @@ IonBuilder::freezePropertiesForCommonPrototype(types::TemporaryTypeSet *types, P
// Don't mark the proto. It will be held down by the shape
// guard. This allows us to use properties found on prototypes
// with properties unknown to TI.
if (type->proto() == TaggedProto(foundProto))
if (type->protoMaybeInNursery() == TaggedProto(foundProto))
break;
type = types::TypeObjectKey::get(type->proto().toObjectOrNull());
type = types::TypeObjectKey::get(type->protoMaybeInNursery().toObjectOrNull());
}
}
}
inline bool
bool
IonBuilder::testCommonGetterSetter(types::TemporaryTypeSet *types, PropertyName *name,
bool isGetter, JSObject *foundProto, Shape *lastProperty,
MDefinition **guard,
@ -9385,7 +9417,7 @@ IonBuilder::testCommonGetterSetter(types::TemporaryTypeSet *types, PropertyName
return true;
}
MInstruction *wrapper = constant(ObjectValue(*foundProto));
MInstruction *wrapper = constantMaybeNursery(foundProto);
*guard = addShapeGuard(wrapper, lastProperty, Bailout_ShapeGuard);
return true;
}
@ -10049,7 +10081,7 @@ IonBuilder::getPropTryCommonGetter(bool *emitted, MDefinition *obj, PropertyName
// Make sure there's enough room
if (!current->ensureHasSlots(2))
return false;
pushConstant(ObjectValue(*commonGetter));
current->push(constantMaybeNursery(commonGetter));
current->push(obj);
@ -10089,7 +10121,8 @@ IonBuilder::getPropTryCommonGetter(bool *emitted, MDefinition *obj, PropertyName
}
}
if (!makeCall(commonGetter, callInfo, false))
JSFunction *tenuredCommonGetter = IsInsideNursery(commonGetter) ? nullptr : commonGetter;
if (!makeCall(tenuredCommonGetter, callInfo, false))
return false;
// If the getter could have been inlined, don't track success. The call to
@ -10497,8 +10530,7 @@ IonBuilder::setPropTryCommonSetter(bool *emitted, MDefinition *obj,
if (!current->ensureHasSlots(3))
return false;
pushConstant(ObjectValue(*commonSetter));
current->push(constantMaybeNursery(commonSetter));
current->push(obj);
current->push(value);
@ -10528,7 +10560,8 @@ IonBuilder::setPropTryCommonSetter(bool *emitted, MDefinition *obj,
}
}
MCall *call = makeCallHelper(commonSetter, callInfo, false);
JSFunction *tenuredCommonSetter = IsInsideNursery(commonSetter) ? nullptr : commonSetter;
MCall *call = makeCallHelper(tenuredCommonSetter, callInfo, false);
if (!call)
return false;

View File

@ -238,6 +238,8 @@ class IonBuilder
void trackActionableAbort(const char *message);
void spew(const char *message);
MInstruction *constantMaybeNursery(JSObject *obj);
JSFunction *getSingleCallTarget(types::TemporaryTypeSet *calleeTypes);
bool getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constructing,
ObjectVector &targets, uint32_t maxTargets);

View File

@ -32,6 +32,8 @@ class CodeOffsetLabel;
class PatchableBackedge;
class IonBuilder;
typedef Vector<JSObject *, 4, JitAllocPolicy> ObjectVector;
class JitCode : public gc::TenuredCell
{
protected:
@ -112,6 +114,8 @@ class JitCode : public gc::TenuredCell
invalidated_ = true;
}
void fixupNurseryObjects(JSContext *cx, const ObjectVector &nurseryObjects);
void setHasBytecodeMap() {
hasBytecodeMap_ = true;
}

View File

@ -235,6 +235,8 @@ class JitRuntime
// Global table of jitcode native address => bytecode address mappings.
JitcodeGlobalTable *jitcodeGlobalTable_;
bool hasIonNurseryObjects_;
private:
JitCode *generateLazyLinkStub(JSContext *cx);
JitCode *generateProfilerExitFrameTailStub(JSContext *cx);
@ -390,6 +392,13 @@ class JitRuntime
ionReturnOverride_ = v;
}
bool hasIonNurseryObjects() const {
return hasIonNurseryObjects_;
}
void setHasIonNurseryObjects(bool b) {
hasIonNurseryObjects_ = b;
}
bool hasJitcodeGlobalTable() const {
return jitcodeGlobalTable_ != nullptr;
}

View File

@ -683,6 +683,16 @@ class LValue : public LInstructionHelper<BOX_PIECES, 0, 0>
}
};
class LNurseryObject : public LInstructionHelper<1, 0, 0>
{
public:
LIR_HEADER(NurseryObject);
MNurseryObject *mir() const {
return mir_->toNurseryObject();
}
};
// Clone an object literal such as we are not modifying the object contained in
// the sources.
class LCloneLiteral : public LCallInstructionHelper<1, 1, 0>

View File

@ -335,6 +335,7 @@
_(AssertRangeV) \
_(LexicalCheck) \
_(ThrowUninitializedLexical) \
_(NurseryObject) \
_(Debugger)
#if defined(JS_CODEGEN_X86)

View File

@ -4003,6 +4003,12 @@ LIRGenerator::visitDebugger(MDebugger *ins)
add(lir, ins);
}
void
LIRGenerator::visitNurseryObject(MNurseryObject *ins)
{
define(new(alloc()) LNurseryObject(), ins);
}
static void
SpewResumePoint(MBasicBlock *block, MInstruction *ins, MResumePoint *resumePoint)
{

View File

@ -287,6 +287,7 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitLexicalCheck(MLexicalCheck *ins);
void visitThrowUninitializedLexical(MThrowUninitializedLexical *ins);
void visitDebugger(MDebugger *ins);
void visitNurseryObject(MNurseryObject *ins);
};
} // namespace jit

View File

@ -676,6 +676,7 @@ MConstant::MConstant(const js::Value &vp, types::CompilerConstraintList *constra
if (vp.isObject()) {
// Create a singleton type set for the object. This isn't necessary for
// other types as the result type encodes all needed information.
MOZ_ASSERT(!IsInsideNursery(&vp.toObject()));
setResultTypeSet(MakeSingletonTypeSet(constraints, &vp.toObject()));
}
if (vp.isMagic() && vp.whyMagic() == JS_UNINITIALIZED_LEXICAL) {
@ -695,6 +696,7 @@ MConstant::MConstant(const js::Value &vp, types::CompilerConstraintList *constra
MConstant::MConstant(JSObject *obj)
: value_(ObjectValue(*obj))
{
MOZ_ASSERT(!IsInsideNursery(obj));
setResultType(MIRType_Object);
setMovable();
}
@ -804,6 +806,39 @@ MConstant::canProduceFloat32() const
return true;
}
MNurseryObject::MNurseryObject(JSObject *obj, uint32_t index, types::CompilerConstraintList *constraints)
: index_(index)
{
setResultType(MIRType_Object);
MOZ_ASSERT(IsInsideNursery(obj));
MOZ_ASSERT(!obj->hasSingletonType());
setResultTypeSet(MakeSingletonTypeSet(constraints, obj));
setMovable();
}
MNurseryObject *
MNurseryObject::New(TempAllocator &alloc, JSObject *obj, uint32_t index,
types::CompilerConstraintList *constraints)
{
return new(alloc) MNurseryObject(obj, index, constraints);
}
HashNumber
MNurseryObject::valueHash() const
{
return HashNumber(index_);
}
bool
MNurseryObject::congruentTo(const MDefinition *ins) const
{
if (!ins->isNurseryObject())
return false;
return ins->toNurseryObject()->index_ == index_;
}
MDefinition*
MSimdValueX4::foldsTo(TempAllocator &alloc)
{

View File

@ -1329,6 +1329,33 @@ class MConstant : public MNullaryInstruction
ALLOW_CLONE(MConstant)
};
class MNurseryObject : public MNullaryInstruction
{
// Index in MIRGenerator::nurseryObjects_.
uint32_t index_;
protected:
MNurseryObject(JSObject *obj, uint32_t index, types::CompilerConstraintList *constraints);
public:
INSTRUCTION_HEADER(NurseryObject)
static MNurseryObject *New(TempAllocator &alloc, JSObject *obj, uint32_t index,
types::CompilerConstraintList *constraints = nullptr);
HashNumber valueHash() const MOZ_OVERRIDE;
bool congruentTo(const MDefinition *ins) const MOZ_OVERRIDE;
uint32_t index() const {
return index_;
}
AliasSet getAliasSet() const MOZ_OVERRIDE {
return AliasSet::None();
}
ALLOW_CLONE(MNurseryObject)
};
// Generic constructor of SIMD valuesX4.
class MSimdValueX4
: public MQuaternaryInstruction,
@ -9825,7 +9852,7 @@ class MGuardObjectIdentity
: public MUnaryInstruction,
public SingleObjectPolicy::Data
{
AlwaysTenured<JSObject *> singleObject_;
AlwaysTenuredObject singleObject_;
bool bailOnEquality_;
MGuardObjectIdentity(MDefinition *obj, JSObject *singleObject, bool bailOnEquality)

View File

@ -193,6 +193,12 @@ class MIRGenerator
bool instrumentedProfiling_;
bool instrumentedProfilingIsCached_;
// List of nursery objects used by this compilation. Can be traced by a
// minor GC while compilation happens off-thread. This Vector should only
// be accessed on the main thread (IonBuilder, nursery GC or
// CodeGenerator::link).
ObjectVector nurseryObjects_;
void addAbortedNewScriptPropertiesType(types::TypeObject *type);
void setForceAbort() {
shouldForceAbort_ = true;
@ -210,6 +216,12 @@ class MIRGenerator
public:
const JitCompileOptions options;
void traceNurseryObjects(JSTracer *trc);
const ObjectVector &nurseryObjects() const {
return nurseryObjects_;
}
};
} // namespace jit

View File

@ -39,6 +39,7 @@ MIRGenerator::MIRGenerator(CompileCompartment *compartment, const JitCompileOpti
modifiesFrameArguments_(false),
instrumentedProfiling_(false),
instrumentedProfilingIsCached_(false),
nurseryObjects_(*alloc),
options(options)
{ }

View File

@ -12,6 +12,7 @@ namespace jit {
#define MIR_OPCODE_LIST(_) \
_(Constant) \
_(NurseryObject) \
_(SimdBox) \
_(SimdUnbox) \
_(SimdValueX4) \

View File

@ -805,6 +805,12 @@ TraceOneDataRelocation(JSTracer *trc, Iter *iter, MacroAssemblerARM *masm)
const void *prior = Assembler::GetPtr32Target(iter, &dest, &rs);
void *ptr = const_cast<void *>(prior);
// The low bit shouldn't be set. If it is, we probably got a dummy
// pointer inserted by CodeGenerator::visitNurseryObject, but we
// shouldn't be able to trigger GC before those are patched to their
// real values.
MOZ_ASSERT(!(uintptr_t(ptr) & 0x1));
// No barrier needed since these are constants.
gc::MarkGCThingUnbarriered(trc, &ptr, "ion-masm-ptr");
@ -846,6 +852,51 @@ Assembler::TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReade
::TraceDataRelocations(trc, code->raw(), reader, static_cast<MacroAssemblerARM *>(Dummy));
}
void
Assembler::FixupNurseryObjects(JSContext *cx, JitCode *code, CompactBufferReader &reader,
const ObjectVector &nurseryObjects)
{
MOZ_ASSERT(!nurseryObjects.empty());
uint8_t *buffer = code->raw();
bool hasNurseryPointers = false;
MacroAssemblerARM *masm = static_cast<MacroAssemblerARM *>(Dummy);
while (reader.more()) {
size_t offset = reader.readUnsigned();
InstructionIterator iter((Instruction*)(buffer + offset));
Instruction *ins = iter.cur();
Register dest;
Assembler::RelocStyle rs;
const void *prior = Assembler::GetPtr32Target(&iter, &dest, &rs);
void *ptr = const_cast<void *>(prior);
uintptr_t word = reinterpret_cast<uintptr_t>(ptr);
if (!(word & 0x1))
continue;
uint32_t index = word >> 1;
JSObject *obj = nurseryObjects[index];
masm->ma_movPatchable(Imm32(int32_t(obj)), dest, Assembler::Always, rs, ins);
if (rs != Assembler::L_LDR) {
// L_LDR won't cause any instructions to be updated.
AutoFlushICache::flush(uintptr_t(ins), 4);
AutoFlushICache::flush(uintptr_t(ins->next()), 4);
}
// Either all objects are still in the nursery, or all objects are
// tenured.
MOZ_ASSERT_IF(hasNurseryPointers, IsInsideNursery(obj));
if (!hasNurseryPointers && IsInsideNursery(obj))
hasNurseryPointers = true;
}
if (hasNurseryPointers)
cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(code);
}
void
Assembler::copyJumpRelocationTable(uint8_t *dest)
{

View File

@ -1597,6 +1597,9 @@ class Assembler : public AssemblerShared
static void TraceJumpRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader);
static void TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader);
static void FixupNurseryObjects(JSContext *cx, JitCode *code, CompactBufferReader &reader,
const ObjectVector &nurseryObjects);
static bool SupportsFloatingPoint() {
return HasVFP();
}

View File

@ -288,6 +288,12 @@ TraceOneDataRelocation(JSTracer *trc, Instruction *inst)
void *ptr = (void *)Assembler::ExtractLuiOriValue(inst, inst->next());
void *prior = ptr;
// The low bit shouldn't be set. If it is, we probably got a dummy
// pointer inserted by CodeGenerator::visitNurseryObject, but we
// shouldn't be able to trigger GC before those are patched to their
// real values.
MOZ_ASSERT(!(uintptr_t(ptr) & 0x1));
// No barrier needed since these are constants.
gc::MarkGCThingUnbarriered(trc, reinterpret_cast<void **>(&ptr), "ion-masm-ptr");
if (ptr != prior) {
@ -322,6 +328,43 @@ Assembler::TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReade
::TraceDataRelocations(trc, code->raw(), reader);
}
void
Assembler::FixupNurseryObjects(JSContext *cx, JitCode *code, CompactBufferReader &reader,
const ObjectVector &nurseryObjects)
{
MOZ_ASSERT(!nurseryObjects.empty());
uint8_t *buffer = code->raw();
bool hasNurseryPointers = false;
while (reader.more()) {
size_t offset = reader.readUnsigned();
Instruction *inst = (Instruction*)(buffer + offset);
void *ptr = (void *)Assembler::ExtractLuiOriValue(inst, inst->next());
uintptr_t word = uintptr_t(ptr);
if (!(word & 0x1))
continue;
uint32_t index = word >> 1;
JSObject *obj = nurseryObjects[index];
Assembler::UpdateLuiOriValue(inst, inst->next(), uint32_t(obj));
AutoFlushICache::flush(uintptr_t(inst), 8);
// Either all objects are still in the nursery, or all objects are
// tenured.
MOZ_ASSERT_IF(hasNurseryPointers, IsInsideNursery(obj));
if (!hasNurseryPointers && IsInsideNursery(obj))
hasNurseryPointers = true;
}
if (hasNurseryPointers)
cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(code);
}
void
Assembler::copyJumpRelocationTable(uint8_t *dest)
{

View File

@ -1012,6 +1012,9 @@ class Assembler : public AssemblerShared
static void TraceJumpRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader);
static void TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader);
static void FixupNurseryObjects(JSContext *cx, JitCode *code, CompactBufferReader &reader,
const ObjectVector &nurseryObjects);
static bool SupportsFloatingPoint() {
#if (defined(__mips_hard_float) && !defined(__mips_single_float)) || defined(JS_MIPS_SIMULATOR)
return true;

View File

@ -156,6 +156,12 @@ class MacroAssemblerNone : public Assembler
static void TraceJumpRelocations(JSTracer *, JitCode *, CompactBufferReader &) { MOZ_CRASH(); }
static void TraceDataRelocations(JSTracer *, JitCode *, CompactBufferReader &) { MOZ_CRASH(); }
static void FixupNurseryObjects(JSContext *, JitCode *, CompactBufferReader &,
const ObjectVector &)
{
MOZ_CRASH();
}
static bool SupportsFloatingPoint() { return false; }
static bool SupportsSimd() { return false; }

View File

@ -233,6 +233,22 @@ class ImmMaybeNurseryPtr
}
};
// Dummy value used for nursery pointers during Ion compilation, see
// LNurseryObject.
class IonNurseryPtr
{
const gc::Cell *ptr;
public:
friend class ImmGCPtr;
explicit IonNurseryPtr(const gc::Cell *ptr) : ptr(ptr)
{
MOZ_ASSERT(ptr);
MOZ_ASSERT(uintptr_t(ptr) & 0x1);
}
};
// Used for immediates which require relocation.
class ImmGCPtr
{
@ -248,6 +264,15 @@ class ImmGCPtr
MOZ_ASSERT(!IsCompilingAsmJS());
}
explicit ImmGCPtr(IonNurseryPtr ptr) : value(ptr.ptr)
{
MOZ_ASSERT(!IsPoisonedPtr(value));
MOZ_ASSERT(value);
// asm.js shouldn't be creating GC things
MOZ_ASSERT(!IsCompilingAsmJS());
}
private:
ImmGCPtr() : value(0) {}

View File

@ -67,6 +67,12 @@ TraceDataRelocations(JSTracer *trc, uint8_t *buffer, CompactBufferReader &reader
}
#endif
// The low bit shouldn't be set. If it is, we probably got a dummy
// pointer inserted by CodeGenerator::visitNurseryObject, but we
// shouldn't be able to trigger GC before those are patched to their
// real values.
MOZ_ASSERT(!(*reinterpret_cast<uintptr_t *>(ptr) & 0x1));
// No barrier needed since these are constants.
gc::MarkGCThingUnbarriered(trc, ptr, "ion-masm-ptr");
}
@ -79,6 +85,45 @@ AssemblerX86Shared::TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBu
::TraceDataRelocations(trc, code->raw(), reader);
}
void
AssemblerX86Shared::FixupNurseryObjects(JSContext *cx, JitCode *code, CompactBufferReader &reader,
const ObjectVector &nurseryObjects)
{
MOZ_ASSERT(!nurseryObjects.empty());
uint8_t *buffer = code->raw();
bool hasNurseryPointers = false;
while (reader.more()) {
size_t offset = reader.readUnsigned();
void **ptr = X86Encoding::GetPointerRef(buffer + offset);
uintptr_t *word = reinterpret_cast<uintptr_t *>(ptr);
#ifdef JS_PUNBOX64
if (*word >> JSVAL_TAG_SHIFT)
continue; // This is a Value.
#endif
if (!(*word & 0x1))
continue;
uint32_t index = *word >> 1;
JSObject *obj = nurseryObjects[index];
*word = uintptr_t(obj);
// Either all objects are still in the nursery, or all objects are
// tenured.
MOZ_ASSERT_IF(hasNurseryPointers, IsInsideNursery(obj));
if (!hasNurseryPointers && IsInsideNursery(obj))
hasNurseryPointers = true;
}
if (hasNurseryPointers)
cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(code);
}
void
AssemblerX86Shared::trace(JSTracer *trc)
{

View File

@ -327,6 +327,9 @@ class AssemblerX86Shared : public AssemblerShared
static void TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader);
static void FixupNurseryObjects(JSContext *cx, JitCode *code, CompactBufferReader &reader,
const ObjectVector &nurseryObjects);
// MacroAssemblers hold onto gcthings, so they are traced by the GC.
void trace(JSTracer *trc);

View File

@ -1074,6 +1074,12 @@ TypeObjectKey::proto()
return isTypeObject() ? asTypeObject()->proto() : asSingleObject()->getTaggedProto();
}
TaggedProto
TypeObjectKey::protoMaybeInNursery()
{
return isTypeObject() ? asTypeObject()->proto() : asSingleObject()->getTaggedProto();
}
bool
JSObject::hasTenuredProto() const
{

View File

@ -1521,10 +1521,8 @@ struct TypeObjectKey
static intptr_t keyBits(TypeObjectKey *obj) { return (intptr_t) obj; }
static TypeObjectKey *getKey(TypeObjectKey *obj) { return obj; }
static TypeObjectKey *get(JSObject *obj) {
MOZ_ASSERT(obj);
return (TypeObjectKey *) (uintptr_t(obj) | 1);
}
static inline TypeObjectKey *get(JSObject *obj);
static TypeObjectKey *get(TypeObject *obj) {
MOZ_ASSERT(obj);
return (TypeObjectKey *) obj;
@ -1545,6 +1543,7 @@ struct TypeObjectKey
const Class *clasp();
TaggedProto proto();
TaggedProto protoMaybeInNursery();
bool hasTenuredProto();
JSObject *singleton();
TypeNewScript *newScript();

View File

@ -122,6 +122,14 @@ TypeObjectKey::asSingleObject()
return res;
}
/* static */ inline TypeObjectKey *
TypeObjectKey::get(JSObject *obj)
{
if (obj->hasSingletonType())
return (TypeObjectKey *) (uintptr_t(obj) | 1);
return TypeObjectKey::get(obj->type());
}
/* static */ inline Type
Type::ObjectType(JSObject *obj)
{