mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-23 02:05:42 +00:00
Bug 1113240 - Allow optimizing nursery-allocated getters/setters in Ion. r=bhackett
This commit is contained in:
parent
f34f010bc1
commit
0743c11f2d
17
js/src/jit-test/tests/ion/nursery-getter-setter.js
Normal file
17
js/src/jit-test/tests/ion/nursery-getter-setter.js
Normal 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();
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -335,6 +335,7 @@
|
||||
_(AssertRangeV) \
|
||||
_(LexicalCheck) \
|
||||
_(ThrowUninitializedLexical) \
|
||||
_(NurseryObject) \
|
||||
_(Debugger)
|
||||
|
||||
#if defined(JS_CODEGEN_X86)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -39,6 +39,7 @@ MIRGenerator::MIRGenerator(CompileCompartment *compartment, const JitCompileOpti
|
||||
modifiesFrameArguments_(false),
|
||||
instrumentedProfiling_(false),
|
||||
instrumentedProfilingIsCached_(false),
|
||||
nurseryObjects_(*alloc),
|
||||
options(options)
|
||||
{ }
|
||||
|
||||
|
@ -12,6 +12,7 @@ namespace jit {
|
||||
|
||||
#define MIR_OPCODE_LIST(_) \
|
||||
_(Constant) \
|
||||
_(NurseryObject) \
|
||||
_(SimdBox) \
|
||||
_(SimdUnbox) \
|
||||
_(SimdValueX4) \
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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) {}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user