From 03fbbbdef717ce7599fbc6bba030296ae24f0ccc Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Mon, 16 Dec 2013 10:53:02 -0800 Subject: [PATCH] Bug 785905 - Build Ion MIR graph off thread, r=jandem. --- js/src/frontend/BytecodeEmitter.cpp | 8 +- js/src/gc/Marking.cpp | 42 ++-- js/src/gc/RootMarking.cpp | 2 +- js/src/jit/BaselineCompiler.cpp | 6 +- js/src/jit/BaselineIC.cpp | 174 ++++++------- js/src/jit/BaselineIC.h | 34 ++- js/src/jit/BaselineInspector.cpp | 38 +++ js/src/jit/BaselineJIT.cpp | 6 +- js/src/jit/BaselineJIT.h | 4 +- js/src/jit/CodeGenerator.cpp | 12 +- js/src/jit/CompileInfo.h | 15 +- js/src/jit/CompileWrappers.cpp | 9 + js/src/jit/CompileWrappers.h | 4 + js/src/jit/Ion.cpp | 56 +++-- js/src/jit/IonAnalysis.cpp | 12 +- js/src/jit/IonBuilder.cpp | 59 ++++- js/src/jit/IonBuilder.h | 15 +- js/src/jit/IonCaches.cpp | 11 +- js/src/jit/IonMacroAssembler.h | 6 +- js/src/jit/MIR.cpp | 16 +- js/src/jit/VMFunctions.cpp | 2 +- .../jit/shared/CodeGenerator-x86-shared.cpp | 2 +- js/src/jscntxt.cpp | 5 +- js/src/jscntxt.h | 72 +++++- js/src/jscompartment.cpp | 2 +- js/src/jsfun.cpp | 34 ++- js/src/jsfun.h | 30 ++- js/src/jsinfer.cpp | 231 ++++++++++++------ js/src/jsinfer.h | 141 +++++++++-- js/src/jsinferinlines.h | 157 +++++------- js/src/jsiter.cpp | 1 + js/src/jsobj.cpp | 39 ++- js/src/jsobj.h | 2 +- js/src/jsobjinlines.h | 30 ++- js/src/jsproxy.cpp | 2 +- js/src/jsproxy.h | 2 - js/src/jsscript.cpp | 5 +- js/src/jsscript.h | 9 +- js/src/jsscriptinlines.h | 5 +- js/src/jsworkers.cpp | 14 +- js/src/jswrapper.cpp | 4 +- js/src/vm/GlobalObject.cpp | 30 ++- js/src/vm/GlobalObject.h | 14 +- js/src/vm/Interpreter.cpp | 2 +- js/src/vm/ObjectImpl.cpp | 12 +- js/src/vm/ObjectImpl.h | 12 +- js/src/vm/Runtime.cpp | 85 ++++++- js/src/vm/Runtime.h | 99 +++++++- js/src/vm/SPSProfiler.cpp | 3 +- js/src/vm/SelfHosting.cpp | 3 +- js/src/vm/TypedArrayObject.cpp | 10 +- js/src/vm/TypedArrayObject.h | 4 +- 52 files changed, 1086 insertions(+), 506 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 7108177f385b..ddf2ca9c9e9f 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -2804,7 +2804,13 @@ frontend::EmitFunctionScript(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNo /* Initialize fun->script() so that the debugger has a valid fun->script(). */ RootedFunction fun(cx, bce->script->function()); JS_ASSERT(fun->isInterpreted()); - fun->setScript(bce->script); + + if (fun->isInterpretedLazy()) { + AutoLockForCompilation lock(cx); + fun->setUnlazifiedScript(bce->script); + } else { + fun->setScript(bce->script); + } bce->tellDebuggerAboutCompiledScript(cx); diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index d0e3bde29e8a..2b1393e0b22e 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -1129,23 +1129,17 @@ ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type) PushMarkStack(gcmarker, JSID_TO_STRING(prop->id)); } - if (TaggedProto(type->proto).isObject()) - PushMarkStack(gcmarker, type->proto); + if (type->proto().isObject()) + PushMarkStack(gcmarker, type->proto().toObject()); if (type->singleton && !type->lazy()) PushMarkStack(gcmarker, type->singleton); - if (type->addendum) { - switch (type->addendum->kind) { - case types::TypeObjectAddendum::NewScript: - PushMarkStack(gcmarker, type->newScript()->fun); - PushMarkStack(gcmarker, type->newScript()->templateObject); - break; - - case types::TypeObjectAddendum::TypedObject: - PushMarkStack(gcmarker, type->typedObject()->typeRepr->ownerObject()); - break; - } + if (type->hasNewScript()) { + PushMarkStack(gcmarker, type->newScript()->fun); + PushMarkStack(gcmarker, type->newScript()->templateObject); + } else if (type->hasTypedObject()) { + PushMarkStack(gcmarker, type->typedObject()->typeRepr->ownerObject()); } if (type->interpretedFunction) @@ -1162,23 +1156,17 @@ gc::MarkChildren(JSTracer *trc, types::TypeObject *type) MarkId(trc, &prop->id, "type_prop"); } - if (TaggedProto(type->proto).isObject()) - MarkObject(trc, &type->proto, "type_proto"); + if (type->proto().isObject()) + MarkObject(trc, &type->protoRaw(), "type_proto"); if (type->singleton && !type->lazy()) MarkObject(trc, &type->singleton, "type_singleton"); - if (type->addendum) { - switch (type->addendum->kind) { - case types::TypeObjectAddendum::NewScript: - MarkObject(trc, &type->newScript()->fun, "type_new_function"); - MarkObject(trc, &type->newScript()->templateObject, "type_new_template"); - break; - - case types::TypeObjectAddendum::TypedObject: - type->typedObject()->typeRepr->mark(trc); - break; - } + if (type->hasNewScript()) { + MarkObject(trc, &type->newScript()->fun, "type_new_function"); + MarkObject(trc, &type->newScript()->templateObject, "type_new_template"); + } else if (type->hasTypedObject()) { + type->typedObject()->typeRepr->mark(trc); } if (type->interpretedFunction) @@ -1441,7 +1429,7 @@ GCMarker::processMarkStackTop(SliceBudget &budget) PushMarkStack(this, shape); /* Call the trace hook if necessary. */ - const Class *clasp = type->clasp; + const Class *clasp = type->clasp(); if (clasp->trace) { JS_ASSERT_IF(runtime->gcMode() == JSGC_MODE_INCREMENTAL && runtime->gcIncrementalEnabled, diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index 541ad84d996c..40843d6cff40 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -47,7 +47,7 @@ MarkExactStackRoot(JSTracer *trc, Rooted *rooter, ThingRootKind kind) if (IsNullTaggedPointer(*addr)) return; - if (kind == THING_ROOT_OBJECT && *addr == Proxy::LazyProto) + if (kind == THING_ROOT_OBJECT && *addr == TaggedProto::LazyProto) return; switch (kind) { diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 38e2bc69b9b6..fa39e21b24ea 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -186,10 +186,8 @@ BaselineCompiler::compile() baselineScript->setMethod(code); baselineScript->setTemplateScope(templateScope); - script->setBaselineScript(baselineScript); - IonSpew(IonSpew_BaselineScripts, "Created BaselineScript %p (raw %p) for %s:%d", - (void *) script->baselineScript(), (void *) code->raw(), + (void *) baselineScript, (void *) code->raw(), script->filename(), script->lineno()); #ifdef JS_ION_PERF @@ -254,6 +252,8 @@ BaselineCompiler::compile() if (script->compartment()->debugMode()) baselineScript->setDebugMode(); + script->setBaselineScript(cx, baselineScript); + return Method_Compiled; } diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 0f7e235058c8..75e6308e5839 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -130,12 +130,16 @@ ICStubIterator::operator++() } void -ICStubIterator::unlink(Zone *zone) +ICStubIterator::unlink(JSContext *cx) { JS_ASSERT(currentStub_->next() != nullptr); JS_ASSERT(currentStub_ != fallbackStub_); JS_ASSERT(!unlinked_); - fallbackStub_->unlinkStub(zone, previousStub_, currentStub_); + + { + AutoLockForCompilation lock(cx); + fallbackStub_->unlinkStub(cx->zone(), previousStub_, currentStub_); + } // Mark the current iterator position as unlinked, so operator++ works properly. unlinked_ = true; @@ -485,7 +489,7 @@ ICFallbackStub::unlinkStubsWithKind(JSContext *cx, ICStub::Kind kind) { for (ICStubIterator iter = beginChain(); !iter.atEnd(); iter++) { if (iter->kind() == kind) - iter.unlink(cx->zone()); + iter.unlink(cx); } } @@ -1061,7 +1065,7 @@ DoProfilerFallback(JSContext *cx, BaselineFrame *frame, ICProfiler_Fallback *stu ICStub *optStub = compiler.getStub(compiler.getStubSpace(script)); if (!optStub) return false; - stub->addNewStub(optStub); + stub->addNewStub(cx, optStub); return true; } @@ -1146,8 +1150,10 @@ ICTypeMonitor_Fallback::addMonitorStubForValue(JSContext *cx, HandleScript scrip ICTypeMonitor_PrimitiveSet::Compiler compiler(cx, existingStub, type); ICStub *stub = existingStub ? compiler.updateStub() : compiler.getStub(compiler.getStubSpace(script)); - if (!stub) + if (!stub) { + js_ReportOutOfMemory(cx); return false; + } IonSpew(IonSpew_BaselineIC, " %s TypeMonitor stub %p for primitive type %d", existingStub ? "Modified existing" : "Created new", stub, type); @@ -1171,8 +1177,10 @@ ICTypeMonitor_Fallback::addMonitorStubForValue(JSContext *cx, HandleScript scrip ICTypeMonitor_SingleObject::Compiler compiler(cx, obj); ICStub *stub = compiler.getStub(compiler.getStubSpace(script)); - if (!stub) + if (!stub) { + js_ReportOutOfMemory(cx); return false; + } IonSpew(IonSpew_BaselineIC, " Added TypeMonitor stub %p for singleton %p", stub, obj.get()); @@ -1193,8 +1201,10 @@ ICTypeMonitor_Fallback::addMonitorStubForValue(JSContext *cx, HandleScript scrip ICTypeMonitor_TypeObject::Compiler compiler(cx, type); ICStub *stub = compiler.getStub(compiler.getStubSpace(script)); - if (!stub) + if (!stub) { + js_ReportOutOfMemory(cx); return false; + } IonSpew(IonSpew_BaselineIC, " Added TypeMonitor stub %p for TypeObject %p", stub, type.get()); @@ -1785,7 +1795,7 @@ DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub, if (!int32Stub) return false; - stub->addNewStub(int32Stub); + stub->addNewStub(cx, int32Stub); return true; } @@ -1803,7 +1813,7 @@ DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub, if (!doubleStub) return false; - stub->addNewStub(doubleStub); + stub->addNewStub(cx, doubleStub); return true; } @@ -1818,7 +1828,7 @@ DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub, if (!doubleStub) return false; - stub->addNewStub(doubleStub); + stub->addNewStub(cx, doubleStub); return true; } @@ -1829,7 +1839,7 @@ DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub, if (!booleanStub) return false; - stub->addNewStub(booleanStub); + stub->addNewStub(cx, booleanStub); return true; } @@ -1842,7 +1852,7 @@ DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub, if (!optStub) return false; - stub->addNewStub(optStub); + stub->addNewStub(cx, optStub); return true; } @@ -1854,7 +1864,7 @@ DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub, if (!stringStub) return false; - stub->addNewStub(stringStub); + stub->addNewStub(cx, stringStub); return true; } @@ -1866,7 +1876,7 @@ DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub, if (!objectStub) return false; - stub->addNewStub(objectStub); + stub->addNewStub(cx, objectStub); return true; } @@ -1884,7 +1894,7 @@ DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub, if (!objectStub) return false; - stub->addNewStub(objectStub); + stub->addNewStub(cx, objectStub); return true; } } @@ -2090,7 +2100,7 @@ ICCompare_ObjectWithUndefined::Compiler::generateStubCode(MacroAssembler &masm) Label emulatesUndefined; Register obj = masm.extractObject(objectOperand, ExtractTemp0); masm.loadPtr(Address(obj, JSObject::offsetOfType()), obj); - masm.loadPtr(Address(obj, offsetof(types::TypeObject, clasp)), obj); + masm.loadPtr(Address(obj, types::TypeObject::offsetOfClasp()), obj); masm.branchTest32(Assembler::NonZero, Address(obj, Class::offsetOfFlags()), Imm32(JSCLASS_EMULATES_UNDEFINED), @@ -2196,7 +2206,7 @@ DoToBoolFallback(JSContext *cx, BaselineFrame *frame, ICToBool_Fallback *stub, H if (!int32Stub) return false; - stub->addNewStub(int32Stub); + stub->addNewStub(cx, int32Stub); return true; } @@ -2207,7 +2217,7 @@ DoToBoolFallback(JSContext *cx, BaselineFrame *frame, ICToBool_Fallback *stub, H if (!doubleStub) return false; - stub->addNewStub(doubleStub); + stub->addNewStub(cx, doubleStub); return true; } @@ -2218,7 +2228,7 @@ DoToBoolFallback(JSContext *cx, BaselineFrame *frame, ICToBool_Fallback *stub, H if (!stringStub) return false; - stub->addNewStub(stringStub); + stub->addNewStub(cx, stringStub); return true; } @@ -2228,7 +2238,7 @@ DoToBoolFallback(JSContext *cx, BaselineFrame *frame, ICToBool_Fallback *stub, H if (!nilStub) return false; - stub->addNewStub(nilStub); + stub->addNewStub(cx, nilStub); return true; } @@ -2239,7 +2249,7 @@ DoToBoolFallback(JSContext *cx, BaselineFrame *frame, ICToBool_Fallback *stub, H if (!objStub) return false; - stub->addNewStub(objStub); + stub->addNewStub(cx, objStub); return true; } @@ -2531,11 +2541,11 @@ DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallbac } if (ret.isDouble()) - stub->setSawDoubleResult(); + stub->setSawDoubleResult(cx); // Check to see if a new stub should be generated. if (stub->numOptimizedStubs() >= ICBinaryArith_Fallback::MAX_OPTIMIZED_STUBS) { - stub->noteUnoptimizableOperands(); + stub->noteUnoptimizableOperands(cx); return true; } @@ -2548,7 +2558,7 @@ DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallbac ICStub *strcatStub = compiler.getStub(compiler.getStubSpace(script)); if (!strcatStub) return false; - stub->addNewStub(strcatStub); + stub->addNewStub(cx, strcatStub); return true; } @@ -2561,7 +2571,7 @@ DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallbac ICStub *strcatStub = compiler.getStub(compiler.getStubSpace(script)); if (!strcatStub) return false; - stub->addNewStub(strcatStub); + stub->addNewStub(cx, strcatStub); return true; } } @@ -2577,13 +2587,13 @@ DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallbac ICStub *arithStub = compiler.getStub(compiler.getStubSpace(script)); if (!arithStub) return false; - stub->addNewStub(arithStub); + stub->addNewStub(cx, arithStub); return true; } // Handle only int32 or double. if (!lhs.isNumber() || !rhs.isNumber()) { - stub->noteUnoptimizableOperands(); + stub->noteUnoptimizableOperands(cx); return true; } @@ -2607,7 +2617,7 @@ DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallbac ICStub *doubleStub = compiler.getStub(compiler.getStubSpace(script)); if (!doubleStub) return false; - stub->addNewStub(doubleStub); + stub->addNewStub(cx, doubleStub); return true; } default: @@ -2625,7 +2635,7 @@ DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallbac ICStub *int32Stub = compilerInt32.getStub(compilerInt32.getStubSpace(script)); if (!int32Stub) return false; - stub->addNewStub(int32Stub); + stub->addNewStub(cx, int32Stub); return true; } @@ -2644,7 +2654,7 @@ DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallbac ICStub *optStub = compiler.getStub(compiler.getStubSpace(script)); if (!optStub) return false; - stub->addNewStub(optStub); + stub->addNewStub(cx, optStub); return true; } default: @@ -2652,7 +2662,7 @@ DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallbac } } - stub->noteUnoptimizableOperands(); + stub->noteUnoptimizableOperands(cx); return true; } #if defined(_MSC_VER) @@ -3047,7 +3057,7 @@ DoUnaryArithFallback(JSContext *cx, BaselineFrame *frame, ICUnaryArith_Fallback ICStub *int32Stub = compiler.getStub(compiler.getStubSpace(script)); if (!int32Stub) return false; - stub->addNewStub(int32Stub); + stub->addNewStub(cx, int32Stub); return true; } @@ -3061,7 +3071,7 @@ DoUnaryArithFallback(JSContext *cx, BaselineFrame *frame, ICUnaryArith_Fallback ICStub *doubleStub = compiler.getStub(compiler.getStubSpace(script)); if (!doubleStub) return false; - stub->addNewStub(doubleStub); + stub->addNewStub(cx, doubleStub); return true; } @@ -3629,7 +3639,7 @@ RemoveExistingGetElemNativeStubs(JSContext *cx, ICGetElem_Fallback *stub, Handle // If the holder matches, but the holder's lastProperty doesn't match, then // this stub is invalid anyway. Unlink it. if (holder->lastProperty() != protoStub->holderShape()) { - iter.unlink(cx->zone()); + iter.unlink(cx); continue; } } else { @@ -3645,7 +3655,7 @@ RemoveExistingGetElemNativeStubs(JSContext *cx, ICGetElem_Fallback *stub, Handle // If the holder matches, but the holder's lastProperty doesn't match, then // this stub is invalid anyway. Unlink it. if (holder->lastProperty() != protoStub->holderShape()) { - iter.unlink(cx->zone()); + iter.unlink(cx); continue; } } @@ -3654,7 +3664,7 @@ RemoveExistingGetElemNativeStubs(JSContext *cx, ICGetElem_Fallback *stub, Handle // If the new stub needs atomization, and the old stub doesn't atomize, then // remove the old stub. if (needsAtomize && !getElemNativeStub->needsAtomize()) { - iter.unlink(cx->zone()); + iter.unlink(cx); continue; } @@ -3745,7 +3755,7 @@ static bool TryAttachNativeGetElemStub(JSContext *cx, HandleScript script, jsbyt if (!newStub) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); return true; } @@ -3797,7 +3807,7 @@ static bool TryAttachNativeGetElemStub(JSContext *cx, HandleScript script, jsbyt if (!newStub) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); return true; } @@ -3831,7 +3841,7 @@ TryAttachGetElemStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICGetEl if (!stringStub) return false; - stub->addNewStub(stringStub); + stub->addNewStub(cx, stringStub); return true; } @@ -3849,7 +3859,7 @@ TryAttachGetElemStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICGetEl if (!argsStub) return false; - stub->addNewStub(argsStub); + stub->addNewStub(cx, argsStub); return true; } @@ -3871,7 +3881,7 @@ TryAttachGetElemStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICGetEl if (!argsStub) return false; - stub->addNewStub(argsStub); + stub->addNewStub(cx, argsStub); return true; } } @@ -3886,7 +3896,7 @@ TryAttachGetElemStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICGetEl if (!denseStub) return false; - stub->addNewStub(denseStub); + stub->addNewStub(cx, denseStub); return true; } @@ -3920,7 +3930,7 @@ TryAttachGetElemStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICGetEl if (!typedArrayStub) return false; - stub->addNewStub(typedArrayStub); + stub->addNewStub(cx, typedArrayStub); return true; } @@ -3928,13 +3938,13 @@ TryAttachGetElemStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICGetEl // be cached by either Baseline or Ion. Indicate this in the cache so that // Ion does not generate a cache for this op. if (!obj->isNative() && !obj->is()) - stub->noteNonNativeAccess(); + stub->noteNonNativeAccess(cx); // GetElem operations which could access negative indexes generally can't // be optimized without the potential for bailouts, as we can't statically // determine that an object has no properties on such indexes. if (rhs.isNumber() && rhs.toNumber() < 0) - stub->noteNegativeIndex(); + stub->noteNegativeIndex(cx); return true; } @@ -4844,7 +4854,7 @@ RemoveExistingTypedArraySetElemStub(JSContext *cx, ICSetElem_Fallback *stub, Han // TypedArraySetElem stubs are only removed using this procedure if // being replaced with one that expects out of bounds index. JS_ASSERT(!iter->toSetElem_TypedArray()->expectOutOfBounds()); - iter.unlink(cx->zone()); + iter.unlink(cx); return true; } return false; @@ -5001,7 +5011,7 @@ DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub, if (!denseStub->addUpdateStubForValue(cx, script, obj, JSID_VOIDHANDLE, rhs)) return false; - stub->addNewStub(denseStub); + stub->addNewStub(cx, denseStub); } else if (!addingCase && !DenseSetElemStubExists(cx, ICStub::SetElem_Dense, stub, obj)) { @@ -5015,7 +5025,7 @@ DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub, if (!denseStub->addUpdateStubForValue(cx, script, obj, JSID_VOIDHANDLE, rhs)) return false; - stub->addNewStub(denseStub); + stub->addNewStub(cx, denseStub); } } @@ -5048,7 +5058,7 @@ DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub, if (!typedArrayStub) return false; - stub->addNewStub(typedArrayStub); + stub->addNewStub(cx, typedArrayStub); return true; } } @@ -5098,13 +5108,13 @@ ICSetElem_Fallback::Compiler::generateStubCode(MacroAssembler &masm) } void -BaselineScript::noteArrayWriteHole(uint32_t pcOffset) +BaselineScript::noteArrayWriteHole(JSContext *cx, uint32_t pcOffset) { ICEntry &entry = icEntryFromPCOffset(pcOffset); ICFallbackStub *stub = entry.fallbackStub(); if (stub->isSetElem_Fallback()) - stub->toSetElem_Fallback()->noteArrayWriteHole(); + stub->toSetElem_Fallback()->noteArrayWriteHole(cx); } // @@ -5626,7 +5636,7 @@ TryAttachGlobalNameStub(JSContext *cx, HandleScript script, ICGetName_Fallback * if (!newStub) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); return true; } @@ -5716,7 +5726,7 @@ TryAttachScopeNameStub(JSContext *cx, HandleScript script, ICGetName_Fallback *s if (!newStub) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); return true; } @@ -5923,7 +5933,7 @@ DoGetIntrinsicFallback(JSContext *cx, BaselineFrame *frame, ICGetIntrinsic_Fallb if (!newStub) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); return true; } @@ -5971,7 +5981,7 @@ TryAttachLengthStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub return false; *attached = true; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); return true; } @@ -5983,7 +5993,7 @@ TryAttachLengthStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub return false; *attached = true; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); return true; } @@ -6000,7 +6010,7 @@ TryAttachLengthStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub return false; *attached = true; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); return true; } if (obj->is()) { @@ -6012,7 +6022,7 @@ TryAttachLengthStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub return false; *attached = true; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); return true; } @@ -6028,7 +6038,7 @@ TryAttachLengthStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub return false; *attached = true; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); return true; } @@ -6111,7 +6121,7 @@ TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, if (!newStub) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); *attached = true; return true; } @@ -6142,7 +6152,7 @@ TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, if (!newStub) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); *attached = true; return true; } @@ -6189,7 +6199,7 @@ TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, } if (!newStub) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); *attached = true; return true; } @@ -6209,7 +6219,7 @@ TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICStub *newStub = compiler.getStub(compiler.getStubSpace(script)); if (!newStub) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); *attached = true; return true; } @@ -6263,7 +6273,7 @@ TryAttachPrimitiveGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc if (!newStub) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); *attached = true; return true; } @@ -6349,7 +6359,7 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub, } JS_ASSERT(!attached); - stub->noteUnoptimizableAccess(); + stub->noteUnoptimizableAccess(cx); return true; } @@ -7094,13 +7104,13 @@ ICGetProp_ArgumentsLength::Compiler::generateStubCode(MacroAssembler &masm) } void -BaselineScript::noteAccessedGetter(uint32_t pcOffset) +BaselineScript::noteAccessedGetter(JSContext *cx, uint32_t pcOffset) { ICEntry &entry = icEntryFromPCOffset(pcOffset); ICFallbackStub *stub = entry.fallbackStub(); if (stub->isGetProp_Fallback()) - stub->toGetProp_Fallback()->noteAccessedGetter(); + stub->toGetProp_Fallback()->noteAccessedGetter(cx); } // @@ -7141,7 +7151,7 @@ TryAttachSetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetPr if (!newStub->addUpdateStubForValue(cx, script, obj, id, rhs)) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); *attached = true; return true; } @@ -7159,7 +7169,7 @@ TryAttachSetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetPr if (!newStub->addUpdateStubForValue(cx, script, obj, id, rhs)) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); *attached = true; return true; } @@ -7181,7 +7191,7 @@ TryAttachSetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetPr if (!newStub) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); *attached = true; return true; } @@ -7200,7 +7210,7 @@ TryAttachSetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetPr if (!newStub) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); *attached = true; return true; } @@ -7271,7 +7281,7 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub, return true; JS_ASSERT(!attached); - stub->noteUnoptimizableAccess(); + stub->noteUnoptimizableAccess(cx); return true; } @@ -7775,7 +7785,7 @@ TryAttachFunApplyStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, if (!newStub) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); return true; } @@ -7792,7 +7802,7 @@ TryAttachFunApplyStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, if (!newStub) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); return true; } } @@ -7933,7 +7943,7 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsb stub->unlinkStubsWithKind(cx, ICStub::Call_Scripted); // Add new generalized stub. - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); return true; } @@ -7962,7 +7972,7 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsb if (!newStub) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); return true; } @@ -7999,7 +8009,7 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsb if (!newStub) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); return true; } @@ -9123,7 +9133,7 @@ DoIteratorMoreFallback(JSContext *cx, BaselineFrame *frame, ICIteratorMore_Fallb ICStub *newStub = compiler.getStub(compiler.getStubSpace(frame->script())); if (!newStub) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); } return true; @@ -9197,7 +9207,7 @@ DoIteratorNextFallback(JSContext *cx, BaselineFrame *frame, ICIteratorNext_Fallb return false; if (!res.isString() && !stub->hasNonStringResult()) - stub->setHasNonStringResult(); + stub->setHasNonStringResult(cx); if (iteratorObject->is() && !stub->hasStub(ICStub::IteratorNext_Native)) @@ -9206,7 +9216,7 @@ DoIteratorNextFallback(JSContext *cx, BaselineFrame *frame, ICIteratorNext_Fallb ICStub *newStub = compiler.getStub(compiler.getStubSpace(frame->script())); if (!newStub) return false; - stub->addNewStub(newStub); + stub->addNewStub(cx, newStub); } return true; @@ -9370,7 +9380,7 @@ DoTypeOfFallback(JSContext *cx, BaselineFrame *frame, ICTypeOf_Fallback *stub, H ICStub *typeOfStub = compiler.getStub(compiler.getStubSpace(frame->script())); if (!typeOfStub) return false; - stub->addNewStub(typeOfStub); + stub->addNewStub(cx, typeOfStub); } return true; @@ -9457,7 +9467,7 @@ DoRetSubFallback(JSContext *cx, BaselineFrame *frame, ICRetSub_Fallback *stub, if (!optStub) return false; - stub->addNewStub(optStub); + stub->addNewStub(cx, optStub); return true; } diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index c4c223551cdc..18a205b91ef2 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -516,7 +516,7 @@ class ICStubIterator return currentStub_ == (ICStub *) fallbackStub_; } - void unlink(Zone *zone); + void unlink(JSContext *cx); }; // @@ -687,6 +687,8 @@ class ICStub } inline void setNext(ICStub *stub) { + // Note: next_ only needs to be changed under the compilation lock for + // non-type-monitor/update ICs. next_ = stub; } @@ -833,7 +835,8 @@ class ICFallbackStub : public ICStub } // Add a new stub to the IC chain terminated by this fallback stub. - void addNewStub(ICStub *stub) { + void addNewStub(JSContext *cx, ICStub *stub) { + AutoLockForCompilation lock(cx); JS_ASSERT(*lastStubPtrAddr_ == this); JS_ASSERT(stub->next() == nullptr); stub->setNext(this); @@ -2439,13 +2442,15 @@ class ICBinaryArith_Fallback : public ICFallbackStub bool sawDoubleResult() const { return extra_ & SAW_DOUBLE_RESULT_BIT; } - void setSawDoubleResult() { + void setSawDoubleResult(JSContext *cx) { + AutoLockForCompilation lock(cx); extra_ |= SAW_DOUBLE_RESULT_BIT; } bool hadUnoptimizableOperands() const { return extra_ & UNOPTIMIZABLE_OPERANDS_BIT; } - void noteUnoptimizableOperands() { + void noteUnoptimizableOperands(JSContext *cx) { + AutoLockForCompilation lock(cx); extra_ |= UNOPTIMIZABLE_OPERANDS_BIT; } @@ -2846,14 +2851,16 @@ class ICGetElem_Fallback : public ICMonitoredFallbackStub return space->allocate(code); } - void noteNonNativeAccess() { + void noteNonNativeAccess(JSContext *cx) { + AutoLockForCompilation lock(cx); extra_ |= EXTRA_NON_NATIVE; } bool hasNonNativeAccess() const { return extra_ & EXTRA_NON_NATIVE; } - void noteNegativeIndex() { + void noteNegativeIndex(JSContext *cx) { + AutoLockForCompilation lock(cx); extra_ |= EXTRA_NEGATIVE_INDEX; } bool hasNegativeIndex() const { @@ -3441,7 +3448,8 @@ class ICSetElem_Fallback : public ICFallbackStub return space->allocate(code); } - void noteArrayWriteHole() { + void noteArrayWriteHole(JSContext *cx) { + AutoLockForCompilation lock(cx); extra_ = 1; } bool hasArrayWriteHole() const { @@ -4016,14 +4024,16 @@ class ICGetProp_Fallback : public ICMonitoredFallbackStub static const size_t UNOPTIMIZABLE_ACCESS_BIT = 0; static const size_t ACCESSED_GETTER_BIT = 1; - void noteUnoptimizableAccess() { + void noteUnoptimizableAccess(JSContext *cx) { + AutoLockForCompilation lock(cx); extra_ |= (1u << UNOPTIMIZABLE_ACCESS_BIT); } bool hadUnoptimizableAccess() const { return extra_ & (1u << UNOPTIMIZABLE_ACCESS_BIT); } - void noteAccessedGetter() { + void noteAccessedGetter(JSContext *cx) { + AutoLockForCompilation lock(cx); extra_ |= (1u << ACCESSED_GETTER_BIT); } bool hasAccessedGetter() const { @@ -4830,7 +4840,8 @@ class ICSetProp_Fallback : public ICFallbackStub } static const size_t UNOPTIMIZABLE_ACCESS_BIT = 0; - void noteUnoptimizableAccess() { + void noteUnoptimizableAccess(JSContext *cx) { + AutoLockForCompilation lock(cx); extra_ |= (1u << UNOPTIMIZABLE_ACCESS_BIT); } bool hadUnoptimizableAccess() const { @@ -5721,7 +5732,8 @@ class ICIteratorNext_Fallback : public ICFallbackStub return space->allocate(code); } - void setHasNonStringResult() { + void setHasNonStringResult(JSContext *cx) { + AutoLockForCompilation lock(cx); JS_ASSERT(extra_ == 0); extra_ = 1; } diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp index f7e4433326f5..720d6ef36894 100644 --- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -18,6 +18,8 @@ using mozilla::DebugOnly; bool SetElemICInspector::sawOOBDenseWrite() const { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + if (!icEntry_) return false; @@ -38,6 +40,8 @@ SetElemICInspector::sawOOBDenseWrite() const bool SetElemICInspector::sawOOBTypedArrayWrite() const { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + if (!icEntry_) return false; @@ -54,6 +58,8 @@ SetElemICInspector::sawOOBTypedArrayWrite() const bool SetElemICInspector::sawDenseWrite() const { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + if (!icEntry_) return false; @@ -68,6 +74,8 @@ SetElemICInspector::sawDenseWrite() const bool SetElemICInspector::sawTypedArrayWrite() const { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + if (!icEntry_) return false; @@ -82,6 +90,8 @@ SetElemICInspector::sawTypedArrayWrite() const bool BaselineInspector::maybeShapesForPropertyOp(jsbytecode *pc, ShapeVector &shapes) { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + // Return a list of shapes seen by the baseline IC for the current op. // An empty list indicates no shapes are known, or there was an uncacheable // access. @@ -139,6 +149,8 @@ BaselineInspector::maybeShapesForPropertyOp(jsbytecode *pc, ShapeVector &shapes) ICStub * BaselineInspector::monomorphicStub(jsbytecode *pc) { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + if (!hasBaselineScript()) return nullptr; @@ -156,6 +168,8 @@ BaselineInspector::monomorphicStub(jsbytecode *pc) bool BaselineInspector::dimorphicStub(jsbytecode *pc, ICStub **pfirst, ICStub **psecond) { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + if (!hasBaselineScript()) return false; @@ -176,6 +190,8 @@ BaselineInspector::dimorphicStub(jsbytecode *pc, ICStub **pfirst, ICStub **pseco MIRType BaselineInspector::expectedResultType(jsbytecode *pc) { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + // Look at the IC entries for this op to guess what type it will produce, // returning MIRType_None otherwise. @@ -222,6 +238,8 @@ CanUseInt32Compare(ICStub::Kind kind) MCompare::CompareType BaselineInspector::expectedCompareType(jsbytecode *pc) { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + ICStub *first = monomorphicStub(pc), *second = nullptr; if (!first && !dimorphicStub(pc, &first, &second)) return MCompare::Compare_Unknown; @@ -304,6 +322,8 @@ TryToSpecializeBinaryArithOp(ICStub **stubs, MIRType BaselineInspector::expectedBinaryArithSpecialization(jsbytecode *pc) { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + MIRType result; ICStub *stubs[2]; @@ -332,6 +352,8 @@ BaselineInspector::expectedBinaryArithSpecialization(jsbytecode *pc) bool BaselineInspector::hasSeenNonNativeGetElement(jsbytecode *pc) { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + if (!hasBaselineScript()) return false; @@ -346,6 +368,8 @@ BaselineInspector::hasSeenNonNativeGetElement(jsbytecode *pc) bool BaselineInspector::hasSeenNegativeIndexGetElement(jsbytecode *pc) { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + if (!hasBaselineScript()) return false; @@ -360,6 +384,8 @@ BaselineInspector::hasSeenNegativeIndexGetElement(jsbytecode *pc) bool BaselineInspector::hasSeenAccessedGetter(jsbytecode *pc) { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + if (!hasBaselineScript()) return false; @@ -374,6 +400,8 @@ BaselineInspector::hasSeenAccessedGetter(jsbytecode *pc) bool BaselineInspector::hasSeenNonStringIterNext(jsbytecode *pc) { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + JS_ASSERT(JSOp(*pc) == JSOP_ITERNEXT); if (!hasBaselineScript()) @@ -388,6 +416,8 @@ BaselineInspector::hasSeenNonStringIterNext(jsbytecode *pc) bool BaselineInspector::hasSeenDoubleResult(jsbytecode *pc) { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + if (!hasBaselineScript()) return false; @@ -407,6 +437,8 @@ BaselineInspector::hasSeenDoubleResult(jsbytecode *pc) JSObject * BaselineInspector::getTemplateObject(jsbytecode *pc) { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + if (!hasBaselineScript()) return nullptr; @@ -434,6 +466,8 @@ BaselineInspector::getTemplateObject(jsbytecode *pc) JSObject * BaselineInspector::getTemplateObjectForNative(jsbytecode *pc, Native native) { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + if (!hasBaselineScript()) return nullptr; @@ -467,6 +501,8 @@ BaselineInspector::templateCallObject() JSObject * BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter) { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + const ICEntry &entry = icEntryFromPC(pc); for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) { if (stub->isGetProp_CallScripted() || stub->isGetProp_CallNative()) { @@ -482,6 +518,8 @@ BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, J JSObject * BaselineInspector::commonSetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonSetter) { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + const ICEntry &entry = icEntryFromPC(pc); for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) { if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) { diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index 36e2867a888a..833a8ca0aaaf 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -237,7 +237,7 @@ jit::BaselineCompile(JSContext *cx, HandleScript script) JS_ASSERT_IF(status != Method_Compiled, !script->hasBaselineScript()); if (status == Method_CantCompile) - script->setBaselineScript(BASELINE_DISABLED_SCRIPT); + script->setBaselineScript(cx, BASELINE_DISABLED_SCRIPT); return status; } @@ -650,7 +650,7 @@ BaselineScript::copyPCMappingIndexEntries(const PCMappingIndexEntry *entries) uint8_t * BaselineScript::nativeCodeForPC(JSScript *script, jsbytecode *pc, PCMappingSlotInfo *slotInfo) { - JS_ASSERT(script->baselineScript() == this); + JS_ASSERT_IF(script->hasBaselineScript(), script->baselineScript() == this); uint32_t pcOffset = script->pcToOffset(pc); @@ -892,7 +892,7 @@ jit::FinishDiscardBaselineScript(FreeOp *fop, JSScript *script) } BaselineScript *baseline = script->baselineScript(); - script->setBaselineScript(nullptr); + script->setBaselineScript(nullptr, nullptr); BaselineScript::Destroy(fop, baseline); } diff --git a/js/src/jit/BaselineJIT.h b/js/src/jit/BaselineJIT.h index 473edf187112..a9a6dc409e91 100644 --- a/js/src/jit/BaselineJIT.h +++ b/js/src/jit/BaselineJIT.h @@ -289,8 +289,8 @@ struct BaselineScript void toggleSPS(bool enable); - void noteAccessedGetter(uint32_t pcOffset); - void noteArrayWriteHole(uint32_t pcOffset); + void noteAccessedGetter(JSContext *cx, uint32_t pcOffset); + void noteArrayWriteHole(JSContext *cx, uint32_t pcOffset); static size_t offsetOfFlags() { return offsetof(BaselineScript, flags_); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 8dbe1e277efa..aeffad793ec0 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -3389,14 +3389,14 @@ CodeGenerator::visitNewCallObject(LNewCallObject *lir) ool = oolCallVM(NewCallObjectInfo, lir, (ArgList(), ImmGCPtr(lir->mir()->block()->info().script()), ImmGCPtr(templateObj->lastProperty()), - ImmGCPtr(templateObj->hasLazyType() ? nullptr : templateObj->type()), + ImmGCPtr(templateObj->hasSingletonType() ? nullptr : templateObj->type()), ToRegister(lir->slots())), StoreRegisterTo(obj)); } else { ool = oolCallVM(NewCallObjectInfo, lir, (ArgList(), ImmGCPtr(lir->mir()->block()->info().script()), ImmGCPtr(templateObj->lastProperty()), - ImmGCPtr(templateObj->hasLazyType() ? nullptr : templateObj->type()), + ImmGCPtr(templateObj->hasSingletonType() ? nullptr : templateObj->type()), ImmPtr(nullptr)), StoreRegisterTo(obj)); } @@ -7409,8 +7409,7 @@ CodeGenerator::emitInstanceOf(LInstruction *ins, JSObject *prototypeObject) // out of the loop on Proxy::LazyProto. // Load the lhs's prototype. - masm.loadPtr(Address(objReg, JSObject::offsetOfType()), output); - masm.loadPtr(Address(output, offsetof(types::TypeObject, proto)), output); + masm.loadObjProto(objReg, output); Label testLazy; { @@ -7424,14 +7423,13 @@ CodeGenerator::emitInstanceOf(LInstruction *ins, JSObject *prototypeObject) masm.jump(&done); masm.bind(¬PrototypeObject); - JS_ASSERT(uintptr_t(Proxy::LazyProto) == 1); + JS_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1); // Test for nullptr or Proxy::LazyProto masm.branchPtr(Assembler::BelowOrEqual, output, ImmWord(1), &testLazy); // Load the current object's prototype. - masm.loadPtr(Address(output, JSObject::offsetOfType()), output); - masm.loadPtr(Address(output, offsetof(types::TypeObject, proto)), output); + masm.loadObjProto(output, output); masm.jump(&loopPrototypeChain); } diff --git a/js/src/jit/CompileInfo.h b/js/src/jit/CompileInfo.h index 874ad1487ebf..622a41b5d99c 100644 --- a/js/src/jit/CompileInfo.h +++ b/js/src/jit/CompileInfo.h @@ -45,9 +45,9 @@ class CompileInfo { public: CompileInfo(JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing, - ExecutionMode executionMode) + ExecutionMode executionMode, bool scriptNeedsArgsObj) : script_(script), fun_(fun), osrPc_(osrPc), constructing_(constructing), - executionMode_(executionMode) + executionMode_(executionMode), scriptNeedsArgsObj_(scriptNeedsArgsObj) { JS_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOP_LOOPENTRY); @@ -68,7 +68,7 @@ class CompileInfo CompileInfo(unsigned nlocals, ExecutionMode executionMode) : script_(nullptr), fun_(nullptr), osrPc_(nullptr), constructing_(false), - executionMode_(executionMode) + executionMode_(executionMode), scriptNeedsArgsObj_(false) { nimplicit_ = 0; nargs_ = 0; @@ -250,10 +250,10 @@ class CompileInfo return script()->argumentsAliasesFormals(); } bool needsArgsObj() const { - return script()->needsArgsObj(); + return scriptNeedsArgsObj_; } bool argsObjAliasesFormals() const { - return script()->argsObjAliasesFormals(); + return scriptNeedsArgsObj_ && !script()->strict(); } ExecutionMode executionMode() const { @@ -275,6 +275,11 @@ class CompileInfo jsbytecode *osrPc_; bool constructing_; ExecutionMode executionMode_; + + // Whether a script needs an arguments object is unstable over compilation + // since the arguments optimization could be marked as failed on the main + // thread, so cache a value here and use it throughout for consistency. + bool scriptNeedsArgsObj_; }; } // namespace jit diff --git a/js/src/jit/CompileWrappers.cpp b/js/src/jit/CompileWrappers.cpp index 58aaacf4a932..439bab359935 100644 --- a/js/src/jit/CompileWrappers.cpp +++ b/js/src/jit/CompileWrappers.cpp @@ -125,11 +125,13 @@ CompileRuntime::positiveInfinityValue() return runtime()->positiveInfinityValue; } +#ifdef DEBUG bool CompileRuntime::isInsideNursery(gc::Cell *cell) { return UninlinedIsInsideNursery(runtime(), cell); } +#endif const DOMCallbacks * CompileRuntime::DOMcallbacks() @@ -228,3 +230,10 @@ CompileCompartment::hasObjectMetadataCallback() { return compartment()->hasObjectMetadataCallback(); } + +AutoLockForCompilation::AutoLockForCompilation(CompileCompartment *compartment + MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) +{ + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + init(compartment->compartment()->runtimeFromAnyThread()); +} diff --git a/js/src/jit/CompileWrappers.h b/js/src/jit/CompileWrappers.h index 487e063ea516..7da0253f9083 100644 --- a/js/src/jit/CompileWrappers.h +++ b/js/src/jit/CompileWrappers.h @@ -66,7 +66,9 @@ class CompileRuntime const Value &NaNValue(); const Value &positiveInfinityValue(); +#ifdef DEBUG bool isInsideNursery(gc::Cell *cell); +#endif // DOM callbacks must be threadsafe (and will hopefully be removed soon). const DOMCallbacks *DOMcallbacks(); @@ -96,6 +98,8 @@ class CompileCompartment { JSCompartment *compartment(); + friend class js::AutoLockForCompilation; + public: static CompileCompartment *get(JSCompartment *comp); diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 547c5363c848..733f4a11067a 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -49,6 +49,8 @@ using namespace js; using namespace js::jit; +using mozilla::Maybe; + // Global variables. IonOptions jit::js_IonOptions; @@ -516,6 +518,8 @@ JitCompartment::ensureIonStubsExist(JSContext *cx) void jit::FinishOffThreadBuilder(IonBuilder *builder) { + builder->script()->runtimeFromMainThread()->removeCompilationThread(); + ExecutionMode executionMode = builder->info().executionMode(); // Clear the recompiling flag if it would have failed. @@ -1563,6 +1567,9 @@ AttachFinishedCompilations(JSContext *cx) // operation callback and can't propagate failures. cx->clearPendingException(); } + } else { + if (builder->abortReason() == AbortReason_Disable) + SetIonScript(builder->script(), builder->info().executionMode(), ION_DISABLED_SCRIPT); } FinishOffThreadBuilder(builder); @@ -1663,11 +1670,13 @@ IonCompile(JSContext *cx, JSScript *script, return AbortReason_Alloc; CompileInfo *info = alloc->new_(script, script->function(), osrPc, constructing, - executionMode); + executionMode, script->needsArgsObj()); if (!info) return AbortReason_Alloc; - BaselineInspector inspector(script); + BaselineInspector *inspector = alloc->new_(script); + if (!inspector) + return AbortReason_Alloc; BaselineFrameInspector *baselineFrameInspector = nullptr; if (baselineFrame) { @@ -1686,7 +1695,7 @@ IonCompile(JSContext *cx, JSScript *script, IonBuilder *builder = alloc->new_((JSContext *) nullptr, CompileCompartment::get(cx->compartment()), temp, graph, constraints, - &inspector, info, baselineFrameInspector); + inspector, info, baselineFrameInspector); if (!builder) return AbortReason_Alloc; @@ -1696,28 +1705,6 @@ IonCompile(JSContext *cx, JSScript *script, RootedScript builderScript(cx, builder->script()); IonSpewNewFunction(graph, builderScript); - mozilla::Maybe protect; - if (js_IonOptions.checkThreadSafety && - cx->runtime()->gcIncrementalState == gc::NO_INCREMENTAL && - !cx->runtime()->profilingScripts && - !cx->runtime()->spsProfiler.enabled()) - { - protect.construct(cx->runtime()); - } - - bool succeeded = builder->build(); - builder->clearForBackEnd(); - - if (!succeeded) { - if (cx->isExceptionPending()) { - IonSpew(IonSpew_Abort, "Builder raised exception."); - return AbortReason_Error; - } - - IonSpew(IonSpew_Abort, "Builder failed to build."); - return builder->abortReason(); - } - // If possible, compile the script off thread. if (OffThreadCompilationAvailable(cx)) { if (recompile) { @@ -1739,6 +1726,24 @@ IonCompile(JSContext *cx, JSScript *script, return AbortReason_NoAbort; } + Maybe ionCompiling; + ionCompiling.construct(); + + Maybe protect; + if (js_IonOptions.checkThreadSafety && + cx->runtime()->gcIncrementalState == gc::NO_INCREMENTAL && + !cx->runtime()->profilingScripts && + !cx->runtime()->spsProfiler.enabled()) + { + protect.construct(cx->runtime()); + } + + bool succeeded = builder->build(); + builder->clearForBackEnd(); + + if (!succeeded) + return builder->abortReason(); + ScopedJSDeletePtr codegen(CompileBackEnd(builder)); if (!codegen) { IonSpew(IonSpew_Abort, "Failed during back-end compilation."); @@ -1747,6 +1752,7 @@ IonCompile(JSContext *cx, JSScript *script, if (!protect.empty()) protect.destroy(); + ionCompiling.destroy(); bool success = codegen->link(cx, builder->constraints()); diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index 049c9de5c5c6..d70f534998bf 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -222,11 +222,10 @@ IsPhiObservable(MPhi *phi, Observability observe) if (fun && slot == info.thisSlot()) return true; - // If the function is heavyweight, and the Phi is of the |scopeChain| - // value, and the function may need an arguments object, then make sure - // to preserve the scope chain, because it may be needed to construct the - // arguments object during bailout. - if (fun && fun->isHeavyweight() && info.hasArguments() && slot == info.scopeChainSlot()) + // If the function may need an arguments object, then make sure to preserve + // the scope chain, because it may be needed to construct the arguments + // object during bailout. + if (fun && info.hasArguments() && slot == info.scopeChainSlot()) return true; // If the Phi is one of the formal argument, and we are using an argument @@ -2177,7 +2176,8 @@ jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun, MIRGraph graph(&temp); CompileInfo info(script, fun, /* osrPc = */ nullptr, /* constructing = */ false, - DefinitePropertiesAnalysis); + DefinitePropertiesAnalysis, + script->needsArgsObj()); AutoTempAllocatorRooter root(cx, &temp); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 934654267982..27d57b3609b2 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -135,16 +135,24 @@ IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp, Tem lazyArguments_(nullptr), inlineCallInfo_(nullptr) { - script_.init(info->script()); + script_ = info->script(); pc = info->startPC(); +#ifdef DEBUG + lock(); JS_ASSERT(script()->hasBaselineScript()); + unlock(); +#endif JS_ASSERT(!!analysisContext == (info->executionMode() == DefinitePropertiesAnalysis)); } void IonBuilder::clearForBackEnd() { + // This case should only be hit if there was a failure while building. + if (!lock_.empty()) + lock_.destroy(); + JS_ASSERT(!analysisContext); baselineFrame_ = nullptr; @@ -581,12 +589,16 @@ IonBuilder::pushLoop(CFGState::State initial, jsbytecode *stopAt, MBasicBlock *e bool IonBuilder::init() { + lock(); + if (!types::TypeScript::FreezeTypeSets(constraints(), script(), &thisTypes, &argTypes, &typeArray)) { return false; } + unlock(); + if (!analysis().init(alloc(), gsn)) return false; @@ -694,6 +706,8 @@ IonBuilder::build() if (!traverseBytecode()) return false; + unlock(); + if (!maybeAddOsrTypeBarriers()) return false; @@ -851,6 +865,7 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi if (!traverseBytecode()) return false; + unlock(); return true; } @@ -905,6 +920,9 @@ IonBuilder::initParameters() // interpreter and didn't accumulate type information, try to use that OSR // frame to determine possible initial types for 'this' and parameters. + // For unknownProperties() tests under addType. + lock(); + if (thisTypes->empty() && baselineFrame_) { if (!thisTypes->addType(baselineFrame_->thisType, alloc_->lifoAlloc())) return false; @@ -928,6 +946,8 @@ IonBuilder::initParameters() current->initSlot(info().argSlotUnchecked(i), param); } + unlock(); + return true; } @@ -950,6 +970,8 @@ IonBuilder::initScopeChain(MDefinition *callee) if (!script()->compileAndGo()) return abort("non-CNG global scripts are not supported"); + lock(); + if (JSFunction *fun = info().fun()) { if (!callee) { MCallee *calleeIns = MCallee::New(alloc()); @@ -975,6 +997,8 @@ IonBuilder::initScopeChain(MDefinition *callee) scope = constant(ObjectValue(script()->global())); } + unlock(); + current->setScopeChain(scope); return true; } @@ -1168,6 +1192,14 @@ IonBuilder::maybeAddOsrTypeBarriers() bool IonBuilder::traverseBytecode() { + // Always hold the compilation lock when traversing bytecode, though release + // it before reacquiring it every few opcodes so that the main thread does not + // block for long when updating compilation data. + lock(); + + size_t lockOpcodeCount = 0; + static const size_t LOCK_OPCODE_GRANULARITY = 5; + for (;;) { JS_ASSERT(pc < info().limitPC()); @@ -1242,6 +1274,12 @@ IonBuilder::traverseBytecode() if (!inspectOpcode(op)) return false; + if (++lockOpcodeCount == LOCK_OPCODE_GRANULARITY) { + unlock(); + lock(); + lockOpcodeCount = 0; + } + #ifdef DEBUG for (size_t i = 0; i < popped.length(); i++) { // Call instructions can discard PassArg instructions. Ignore them. @@ -3845,13 +3883,16 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target) LifoAlloc *lifoAlloc = alloc_->lifoAlloc(); CompileInfo *info = lifoAlloc->new_(calleeScript, target, (jsbytecode *)nullptr, callInfo.constructing(), - this->info().executionMode()); + this->info().executionMode(), + /* needsArgsObj = */ false); if (!info) return false; MIRGraphReturns returns(alloc()); AutoAccumulateReturns aar(graph(), returns); + unlock(); + // Build the graph. IonBuilder inlineBuilder(analysisContext, compartment, &alloc(), &graph(), constraints(), &inspector, info, nullptr, @@ -3875,6 +3916,8 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target) return false; } + lock(); + // Create return block. jsbytecode *postCall = GetNextPc(pc); MBasicBlock *returnBlock = newBlock(nullptr, postCall); @@ -4698,7 +4741,7 @@ IonBuilder::createThisScriptedSingleton(JSFunction *target, MDefinition *callee) JSObject *templateObject = inspector->getTemplateObject(pc); if (!templateObject || !templateObject->is()) return nullptr; - if (templateObject->getProto() != proto) + if (!templateObject->hasTenuredProto() || templateObject->getProto() != proto) return nullptr; if (!target->nonLazyScript()->types) @@ -5096,6 +5139,8 @@ IonBuilder::testShouldDOMCall(types::TypeSet *inTypes, if (!curType) continue; + if (!curType->hasTenuredProto()) + return false; JSObject *proto = curType->proto().toObjectOrNull(); if (!instanceChecker(proto, jinfo->protoID, jinfo->depth)) return false; @@ -6031,6 +6076,8 @@ IonBuilder::testSingletonProperty(JSObject *obj, PropertyName *name) if (ClassHasResolveHook(compartment, obj->getClass(), name)) return nullptr; + if (!obj->hasTenuredProto()) + return nullptr; obj = obj->getProto(); } @@ -6104,6 +6151,8 @@ IonBuilder::testSingletonPropertyTypes(MDefinition *obj, JSObject *singleton, Pr if (property.isOwnProperty(constraints())) return false; + if (!object->hasTenuredProto()) + return false; if (JSObject *proto = object->proto().toObjectOrNull()) { // Test this type. if (testSingletonProperty(proto, name) != singleton) @@ -7897,6 +7946,8 @@ IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyN return false; } + if (!type->hasTenuredProto()) + return false; JSObject *proto = type->proto().toObjectOrNull(); if (proto == foundProto) break; @@ -7996,7 +8047,7 @@ IonBuilder::annotateGetPropertyCache(MDefinition *obj, MGetPropertyCache *getPro if (!baseTypeObj) continue; types::TypeObjectKey *typeObj = types::TypeObjectKey::get(baseTypeObj); - if (typeObj->unknownProperties() || !typeObj->proto().isObject()) + if (typeObj->unknownProperties() || !typeObj->hasTenuredProto() || !typeObj->proto().isObject()) continue; const Class *clasp = typeObj->clasp(); diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index b3094b74e461..6839e1d9d958 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -724,7 +724,7 @@ class IonBuilder : public MIRGenerator } // A builder is inextricably tied to a particular script. - HeapPtrScript script_; + JSScript *script_; // If off thread compilation is successful, the final code generator is // attached here. Code has been generated, but not linked (there is not yet @@ -735,7 +735,7 @@ class IonBuilder : public MIRGenerator public: void clearForBackEnd(); - JSScript *script() const { return script_.get(); } + JSScript *script() const { return script_; } CodeGenerator *backgroundCodegen() const { return backgroundCodegen_; } void setBackgroundCodegen(CodeGenerator *codegen) { backgroundCodegen_ = codegen; } @@ -765,6 +765,17 @@ class IonBuilder : public MIRGenerator // Constraints for recording dependencies on type information. types::CompilerConstraintList *constraints_; + mozilla::Maybe lock_; + + void lock() { + if (!analysisContext) + lock_.construct(compartment); + } + void unlock() { + if (!analysisContext) + lock_.destroy(); + } + // Basic analysis information about the script. BytecodeAnalysis analysis_; BytecodeAnalysis &analysis() { diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index 188053a3a0b3..de66d8abf91c 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -468,7 +468,7 @@ GeneratePrototypeGuards(JSContext *cx, IonScript *ion, MacroAssembler &masm, JSO // Note: objectReg and scratchReg may be the same register, so we cannot // use objectReg in the rest of this function. masm.loadPtr(Address(objectReg, JSObject::offsetOfType()), scratchReg); - Address proto(scratchReg, offsetof(types::TypeObject, proto)); + Address proto(scratchReg, types::TypeObject::offsetOfProto()); masm.branchNurseryPtr(Assembler::NotEqual, proto, ImmMaybeNurseryPtr(obj->getProto()), failures); } @@ -796,11 +796,7 @@ GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm, Register lastReg = object; JS_ASSERT(scratchReg != object); while (proto) { - Address addrType(lastReg, JSObject::offsetOfType()); - masm.loadPtr(addrType, scratchReg); - Address addrProto(scratchReg, offsetof(types::TypeObject, proto)); - masm.loadPtr(addrProto, scratchReg); - Address addrShape(scratchReg, JSObject::offsetOfShape()); + masm.loadObjProto(lastReg, scratchReg); // Guard the shape of the current prototype. masm.branchPtr(Assembler::NotEqual, @@ -2584,8 +2580,7 @@ GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att Shape *protoShape = proto->lastProperty(); // load next prototype - masm.loadPtr(Address(protoReg, JSObject::offsetOfType()), protoReg); - masm.loadPtr(Address(protoReg, offsetof(types::TypeObject, proto)), protoReg); + masm.loadObjProto(protoReg, protoReg); // Ensure that its shape matches. masm.branchTestObjShape(Assembler::NotEqual, protoReg, protoShape, &failuresPopObject); diff --git a/js/src/jit/IonMacroAssembler.h b/js/src/jit/IonMacroAssembler.h index 35c9aaeb8fbe..2d5d85fc43d1 100644 --- a/js/src/jit/IonMacroAssembler.h +++ b/js/src/jit/IonMacroAssembler.h @@ -292,12 +292,12 @@ class MacroAssembler : public MacroAssemblerSpecific } void loadObjClass(Register objReg, Register dest) { loadPtr(Address(objReg, JSObject::offsetOfType()), dest); - loadPtr(Address(dest, offsetof(types::TypeObject, clasp)), dest); + loadPtr(Address(dest, types::TypeObject::offsetOfClasp()), dest); } void branchTestObjClass(Condition cond, Register obj, Register scratch, const js::Class *clasp, Label *label) { loadPtr(Address(obj, JSObject::offsetOfType()), scratch); - branchPtr(cond, Address(scratch, offsetof(types::TypeObject, clasp)), ImmPtr(clasp), label); + branchPtr(cond, Address(scratch, types::TypeObject::offsetOfClasp()), ImmPtr(clasp), label); } void branchTestObjShape(Condition cond, Register obj, const Shape *shape, Label *label) { branchPtr(cond, Address(obj, JSObject::offsetOfShape()), ImmGCPtr(shape), label); @@ -353,7 +353,7 @@ class MacroAssembler : public MacroAssemblerSpecific void loadObjProto(Register obj, Register dest) { loadPtr(Address(obj, JSObject::offsetOfType()), dest); - loadPtr(Address(dest, offsetof(types::TypeObject, proto)), dest); + loadPtr(Address(dest, types::TypeObject::offsetOfProto()), dest); } void loadStringLength(Register str, Register dest) { diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 6c3efded51fe..92cff151ffb4 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -2929,7 +2929,13 @@ jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx, // If this access has never executed, try to add types to the observed set // according to any property which exists on the object or its prototype. if (updateObserved && observed->empty() && name) { - JSObject *obj = object->singleton() ? object->singleton() : object->proto().toObjectOrNull(); + JSObject *obj; + if (object->singleton()) + obj = object->singleton(); + else if (object->hasTenuredProto()) + obj = object->proto().toObjectOrNull(); + else + obj = nullptr; while (obj) { if (!obj->getClass()->isNative()) @@ -2953,6 +2959,8 @@ jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx, } } + if (!obj->hasTenuredProto()) + break; obj = obj->getProto(); } } @@ -3004,7 +3012,11 @@ jit::PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *cons types::TypeObjectKey *object = types->getObject(i); if (!object) continue; - while (object->proto().isObject()) { + while (true) { + if (!object->hasTenuredProto()) + return true; + if (!object->proto().isObject()) + break; object = types::TypeObjectKey::get(object->proto().toObject()); if (PropertyReadNeedsTypeBarrier(constraints, object, name, observed)) return true; diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 717a2703df7a..061f84714bb1 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -946,7 +946,7 @@ AssertValidObjectPtr(JSContext *cx, JSObject *obj) JS_ASSERT(obj->runtimeFromMainThread() == cx->runtime()); JS_ASSERT_IF(!obj->hasLazyType(), - obj->type()->clasp == obj->lastProperty()->getObjectClass()); + obj->type()->clasp() == obj->lastProperty()->getObjectClass()); if (obj->isTenured()) { JS_ASSERT(obj->isAligned()); diff --git a/js/src/jit/shared/CodeGenerator-x86-shared.cpp b/js/src/jit/shared/CodeGenerator-x86-shared.cpp index 2a563f422cac..2163c0e7cb97 100644 --- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp @@ -1725,7 +1725,7 @@ CodeGeneratorX86Shared::visitGuardClass(LGuardClass *guard) Register tmp = ToRegister(guard->tempInt()); masm.loadPtr(Address(obj, JSObject::offsetOfType()), tmp); - masm.cmpPtr(Operand(tmp, offsetof(types::TypeObject, clasp)), ImmPtr(guard->mir()->getClass())); + masm.cmpPtr(Operand(tmp, types::TypeObject::offsetOfClasp()), ImmPtr(guard->mir()->getClass())); if (!bailoutIf(Assembler::NotEqual, guard->snapshot())) return false; return true; diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 602bac144815..cbc4567c0dd0 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -116,6 +116,7 @@ js::ExistingCloneFunctionAtCallsite(const CallsiteCloneTable &table, JSFunction JS_ASSERT(fun->nonLazyScript()->shouldCloneAtCallsite()); JS_ASSERT(!fun->nonLazyScript()->enclosingStaticScope()); JS_ASSERT(types::UseNewTypeForClone(fun)); + JS_ASSERT(CurrentThreadCanReadCompilationData()); /* * If we start allocating function objects in the nursery, then the callsite @@ -126,7 +127,7 @@ js::ExistingCloneFunctionAtCallsite(const CallsiteCloneTable &table, JSFunction if (!table.initialized()) return nullptr; - CallsiteCloneTable::Ptr p = table.lookup(CallsiteCloneKey(fun, script, script->pcToOffset(pc))); + CallsiteCloneTable::Ptr p = table.readonlyThreadsafeLookup(CallsiteCloneKey(fun, script, script->pcToOffset(pc))); if (p) return p->value(); @@ -153,6 +154,8 @@ js::CloneFunctionAtCallsite(JSContext *cx, HandleFunction fun, HandleScript scri typedef CallsiteCloneKey Key; typedef CallsiteCloneTable Table; + AutoLockForCompilation lock(cx); + Table &table = cx->compartment()->callsiteClones; if (!table.initialized() && !table.init()) return nullptr; diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 691e0a0c26f7..3aee93c84a20 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -32,7 +32,10 @@ js_ReportOverRecursed(js::ThreadSafeContext *cx); namespace js { -namespace jit { class IonContext; } +namespace jit { +class IonContext; +class CompileCompartment; +} struct CallsiteCloneKey { /* The original function that we are cloning. */ @@ -1038,7 +1041,9 @@ class AutoLockForExclusiveAccess if (runtime->numExclusiveThreads) { runtime->assertCanLock(JSRuntime::ExclusiveAccessLock); PR_Lock(runtime->exclusiveAccessLock); +#ifdef DEBUG runtime->exclusiveAccessOwner = PR_GetCurrentThread(); +#endif } else { JS_ASSERT(!runtime->mainThreadHasExclusiveAccess); runtime->mainThreadHasExclusiveAccess = true; @@ -1057,9 +1062,7 @@ class AutoLockForExclusiveAccess ~AutoLockForExclusiveAccess() { if (runtime->numExclusiveThreads) { JS_ASSERT(runtime->exclusiveAccessOwner == PR_GetCurrentThread()); -#ifdef DEBUG runtime->exclusiveAccessOwner = nullptr; -#endif PR_Unlock(runtime->exclusiveAccessLock); } else { JS_ASSERT(runtime->mainThreadHasExclusiveAccess); @@ -1083,6 +1086,69 @@ class AutoLockForExclusiveAccess MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; +class AutoLockForCompilation +{ +#ifdef JS_WORKER_THREADS + JSRuntime *runtime; + + void init(JSRuntime *rt) { + runtime = rt; + if (runtime->numCompilationThreads) { + runtime->assertCanLock(JSRuntime::CompilationLock); + PR_Lock(runtime->compilationLock); +#ifdef DEBUG + runtime->compilationLockOwner = PR_GetCurrentThread(); +#endif + } else { +#ifdef DEBUG + JS_ASSERT(!runtime->mainThreadHasCompilationLock); + runtime->mainThreadHasCompilationLock = true; +#endif + } + } + + public: + AutoLockForCompilation(ExclusiveContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + if (cx->isJSContext()) + init(cx->asJSContext()->runtime()); + else + runtime = nullptr; + } + AutoLockForCompilation(jit::CompileCompartment *compartment MOZ_GUARD_OBJECT_NOTIFIER_PARAM); + ~AutoLockForCompilation() { + if (runtime) { + if (runtime->numCompilationThreads) { + JS_ASSERT(runtime->compilationLockOwner == PR_GetCurrentThread()); +#ifdef DEBUG + runtime->compilationLockOwner = nullptr; +#endif + PR_Unlock(runtime->compilationLock); + } else { +#ifdef DEBUG + JS_ASSERT(runtime->mainThreadHasCompilationLock); + runtime->mainThreadHasCompilationLock = false; +#endif + } + } + } +#else // JS_WORKER_THREADS + public: + AutoLockForCompilation(ExclusiveContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + } + AutoLockForCompilation(jit::CompileCompartment *compartment MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + } + ~AutoLockForCompilation() { + // An empty destructor is needed to avoid warnings from clang about + // unused local variables of this type. + } +#endif // JS_WORKER_THREADS + + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + } /* namespace js */ #ifdef _MSC_VER diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 84f911528a8e..326593b23ef6 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -338,7 +338,7 @@ JSCompartment::wrap(JSContext *cx, MutableHandleObject obj, HandleObject existin return true; } - RootedObject proto(cx, Proxy::LazyProto); + RootedObject proto(cx, TaggedProto::LazyProto); RootedObject existing(cx, existingArg); if (existing) { /* Is it possible to reuse |existing|? */ diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 9d1624cb3e43..2eb8d021ae7f 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1124,25 +1124,21 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext *cx, HandleFuncti // THING_ROOT_LAZY_SCRIPT). AutoSuppressGC suppressGC(cx); - fun->flags_ &= ~INTERPRETED_LAZY; - fun->flags_ |= INTERPRETED; - RootedScript script(cx, lazy->maybeScript()); if (script) { - fun->initScript(script); + AutoLockForCompilation lock(cx); + fun->setUnlazifiedScript(script); return true; } - fun->initScript(nullptr); - if (fun != lazy->function()) { script = lazy->function()->getOrCreateScript(cx); - if (!script) { - fun->initLazyScript(lazy); + if (!script) return false; - } - fun->initScript(script); + + AutoLockForCompilation lock(cx); + fun->setUnlazifiedScript(script); return true; } @@ -1162,17 +1158,19 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext *cx, HandleFuncti if (script) { RootedObject enclosingScope(cx, lazy->enclosingScope()); RootedScript clonedScript(cx, CloneScript(cx, enclosingScope, fun, script)); - if (!clonedScript) { - fun->initLazyScript(lazy); + if (!clonedScript) return false; - } clonedScript->setSourceObject(lazy->sourceObject()); fun->initAtom(script->function()->displayAtom()); - fun->initScript(clonedScript); clonedScript->setFunction(fun); + { + AutoLockForCompilation lock(cx); + fun->setUnlazifiedScript(clonedScript); + } + CallNewScriptHook(cx, clonedScript, fun); lazy->initScript(clonedScript); @@ -1184,18 +1182,14 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext *cx, HandleFuncti // Parse and compile the script from source. SourceDataCache::AutoSuppressPurge asp(cx); const jschar *chars = lazy->source()->chars(cx, asp); - if (!chars) { - fun->initLazyScript(lazy); + if (!chars) return false; - } const jschar *lazyStart = chars + lazy->begin(); size_t lazyLength = lazy->end() - lazy->begin(); - if (!frontend::CompileLazyFunction(cx, lazy, lazyStart, lazyLength)) { - fun->initLazyScript(lazy); + if (!frontend::CompileLazyFunction(cx, lazy, lazyStart, lazyLength)) return false; - } script = fun->nonLazyScript(); diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 9638848e4aad..f0df951a511f 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -123,8 +123,6 @@ class JSFunction : public JSObject /* Possible attributes of an interpreted function: */ bool isFunctionPrototype() const { return flags() & IS_FUN_PROTO; } - bool isInterpretedLazy() const { return flags() & INTERPRETED_LAZY; } - bool hasScript() const { return flags() & INTERPRETED; } bool isExprClosure() const { return flags() & EXPR_CLOSURE; } bool hasGuessedAtom() const { return flags() & HAS_GUESSED_ATOM; } bool isLambda() const { return flags() & LAMBDA; } @@ -136,6 +134,17 @@ class JSFunction : public JSObject return flags() & SH_WRAPPABLE; } + // Functions can change between being lazily interpreted and having scripts + // when under the compilation lock. + bool isInterpretedLazy() const { + JS_ASSERT(js::CurrentThreadCanReadCompilationData()); + return flags() & INTERPRETED_LAZY; + } + bool hasScript() const { + JS_ASSERT(js::CurrentThreadCanReadCompilationData()); + return flags() & INTERPRETED; + } + bool hasJITCode() const { if (!hasScript()) return false; @@ -321,6 +330,7 @@ class JSFunction : public JSObject JSScript *nonLazyScript() const { JS_ASSERT(hasScript()); + JS_ASSERT(js::CurrentThreadCanReadCompilationData()); return u.i.s.script_; } @@ -331,11 +341,13 @@ class JSFunction : public JSObject js::LazyScript *lazyScript() const { JS_ASSERT(isInterpretedLazy() && u.i.s.lazy_); + JS_ASSERT(js::CurrentThreadCanReadCompilationData()); return u.i.s.lazy_; } js::LazyScript *lazyScriptOrNull() const { JS_ASSERT(isInterpretedLazy()); + JS_ASSERT(js::CurrentThreadCanReadCompilationData()); return u.i.s.lazy_; } @@ -357,15 +369,25 @@ class JSFunction : public JSObject bool isStarGenerator() const { return generatorKind() == js::StarGenerator; } void setScript(JSScript *script_) { - JS_ASSERT(isInterpreted()); + JS_ASSERT(hasScript()); mutableScript() = script_; } void initScript(JSScript *script_) { - JS_ASSERT(isInterpreted()); + JS_ASSERT(hasScript()); mutableScript().init(script_); } + void setUnlazifiedScript(JSScript *script) { + // Note: createScriptForLazilyInterpretedFunction triggers a barrier on + // lazy script before it is overwritten here. + JS_ASSERT(js::CurrentThreadCanWriteCompilationData()); + JS_ASSERT(isInterpretedLazy()); + flags_ &= ~INTERPRETED_LAZY; + flags_ |= INTERPRETED; + initScript(script); + } + void initLazyScript(js::LazyScript *lazy) { JS_ASSERT(isInterpreted()); flags_ &= ~INTERPRETED; diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 7fe019b6902e..5bfec3ee4129 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -685,6 +685,8 @@ TypeScript::FreezeTypeSets(CompilerConstraintList *constraints, JSScript *script TemporaryTypeSet **pArgTypes, TemporaryTypeSet **pBytecodeTypes) { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + LifoAlloc *alloc = constraints->alloc(); StackTypeSet *existing = script->types->typeArray(); @@ -791,13 +793,26 @@ CompilerConstraintInstance::generateTypeConstraint(JSContext *cx, RecompileIn const Class * TypeObjectKey::clasp() { - return isTypeObject() ? asTypeObject()->clasp : asSingleObject()->getClass(); + return isTypeObject() ? asTypeObject()->clasp() : asSingleObject()->getClass(); } TaggedProto TypeObjectKey::proto() { - return isTypeObject() ? TaggedProto(asTypeObject()->proto) : asSingleObject()->getTaggedProto(); + JS_ASSERT(hasTenuredProto()); + return isTypeObject() ? asTypeObject()->proto() : asSingleObject()->getTaggedProto(); +} + +bool +ObjectImpl::hasTenuredProto() const +{ + return type_->hasTenuredProto(); +} + +bool +TypeObjectKey::hasTenuredProto() +{ + return isTypeObject() ? asTypeObject()->hasTenuredProto() : asSingleObject()->hasTenuredProto(); } JSObject * @@ -836,6 +851,7 @@ HeapTypeSetKey TypeObjectKey::property(jsid id) { JS_ASSERT(!unknownProperties()); + JS_ASSERT(CurrentThreadCanReadCompilationData()); HeapTypeSetKey property; property.object_ = this; @@ -1429,8 +1445,10 @@ ObjectStateChange(ExclusiveContext *cxArg, TypeObject *object, bool markingUnkno HeapTypeSet *types = object->maybeGetProperty(JSID_EMPTY); /* Mark as unknown after getting the types, to avoid assertion. */ - if (markingUnknown) - object->flags |= OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES; + if (markingUnknown) { + AutoLockForCompilation lock(cxArg); + object->addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES); + } if (types) { if (JSContext *cx = cxArg->maybeJSContext()) { @@ -1678,6 +1696,9 @@ TemporaryTypeSet::getCommonPrototype() if (!object) continue; + if (!object->hasTenuredProto()) + return nullptr; + TaggedProto nproto = object->proto(); if (proto) { if (nproto != proto) @@ -1737,18 +1758,24 @@ TypeZone::init(JSContext *cx) } TypeObject * -TypeCompartment::newTypeObject(ExclusiveContext *cx, const Class *clasp, Handle proto, bool unknown) +TypeCompartment::newTypeObject(ExclusiveContext *cx, const Class *clasp, Handle proto, + TypeObjectFlags initialFlags) { JS_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject())); + if (!cx->typeInferenceEnabled()) + initialFlags |= OBJECT_FLAG_UNKNOWN_MASK; + + if (cx->isJSContext()) { + if (proto.isObject() && IsInsideNursery(cx->asJSContext()->runtime(), proto.toObject())) + initialFlags |= OBJECT_FLAG_NURSERY_PROTO; + } + TypeObject *object = gc::NewGCThing(cx, gc::FINALIZE_TYPE_OBJECT, sizeof(TypeObject), gc::TenuredHeap); if (!object) return nullptr; - new(object) TypeObject(clasp, proto, unknown); - - if (!cx->typeInferenceEnabled()) - object->flags |= OBJECT_FLAG_UNKNOWN_MASK; + new(object) TypeObject(clasp, proto, initialFlags); return object; } @@ -1871,12 +1898,11 @@ TypeCompartment::addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey ke return nullptr; Rooted tagged(cx, TaggedProto(proto)); - res = newTypeObject(cx, GetClassForProtoKey(key.kind), tagged); + res = newTypeObject(cx, GetClassForProtoKey(key.kind), tagged, OBJECT_FLAG_FROM_ALLOCATION_SITE); if (!res) { cx->compartment()->types.setPendingNukeTypes(cx); return nullptr; } - res->flags |= OBJECT_FLAG_FROM_ALLOCATION_SITE; key.script = keyScript; } @@ -2010,6 +2036,8 @@ PrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSObject *obj) HeapTypeSetKey index = type->property(JSID_VOID); if (index.configured(constraints) || index.isOwnProperty(constraints)) return true; + if (!obj->hasTenuredProto()) + return true; obj = obj->getProto(); } while (obj); @@ -2175,10 +2203,9 @@ void TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target) { JS_ASSERT(this == &cx->compartment()->types); - JS_ASSERT(!(target->flags & OBJECT_FLAG_SETS_MARKED_UNKNOWN)); + JS_ASSERT(!(target->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN)); JS_ASSERT(!target->singleton); JS_ASSERT(target->unknownProperties()); - target->flags |= OBJECT_FLAG_SETS_MARKED_UNKNOWN; AutoEnterAnalysis enter(cx); @@ -2219,6 +2246,9 @@ TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target) } } } + + AutoLockForCompilation lock(cx); + target->addFlags(OBJECT_FLAG_SETS_MARKED_UNKNOWN); } void @@ -2511,7 +2541,7 @@ TypeCompartment::fixObjectType(ExclusiveContext *cx, JSObject *obj) ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup); if (p) { - JS_ASSERT(obj->getProto() == p->value().object->proto); + JS_ASSERT(obj->getProto() == p->value().object->proto().toObject()); JS_ASSERT(obj->lastProperty() == p->value().shape); UpdateObjectTableEntryTypes(cx, p->value(), properties.begin(), properties.length()); @@ -2614,7 +2644,7 @@ TypeCompartment::newTypedObject(JSContext *cx, IdValuePair *properties, size_t n cx->clearPendingException(); return nullptr; } - JS_ASSERT(obj->getProto() == p->value().object->proto); + JS_ASSERT(obj->getProto() == p->value().object->proto().toObject()); RootedShape shape(cx, p->value().shape); if (!JSObject::setLastProperty(cx, obj, shape)) { @@ -2635,6 +2665,36 @@ TypeCompartment::newTypedObject(JSContext *cx, IdValuePair *properties, size_t n // TypeObject ///////////////////////////////////////////////////////////////////// +#ifdef DEBUG +void +TypeObject::assertCanAccessProto() +{ + // The proto pointer for type objects representing singletons may move. + JS_ASSERT_IF(singleton, CurrentThreadCanReadCompilationData()); + + // Any proto pointer which is in the nursery may be moved, and may not be + // accessed during off thread compilation. +#if defined(JSGC_GENERATIONAL) && defined(JS_WORKER_THREADS) + PerThreadData *pt = TlsPerThreadData.get(); + TaggedProto proto(proto_); + JS_ASSERT_IF(proto.isObject() && !proto.toObject()->isTenured(), + !pt || !pt->ionCompiling); +#endif +} +#endif // DEBUG + +void +TypeObject::setProto(JSContext *cx, TaggedProto proto) +{ + JS_ASSERT(CurrentThreadCanWriteCompilationData()); + JS_ASSERT(singleton); + + if (proto.isObject() && IsInsideNursery(cx->runtime(), proto.toObject())) + addFlags(OBJECT_FLAG_NURSERY_PROTO); + + setProtoUnchecked(proto); +} + static inline void UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, JSObject *obj, Shape *shape, bool indexed) @@ -2644,7 +2704,8 @@ UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, JSObject *obj, Shap if (shape->hasGetterValue() || shape->hasSetterValue()) { types->setConfiguredProperty(cx); - types->addType(cx, Type::UnknownType()); + if (!types->TypeSet::addType(Type::UnknownType(), &cx->typeLifoAlloc())) + cx->compartment()->types.setPendingNukeTypes(cx); } else if (shape->hasDefaultGetter() && shape->hasSlot()) { if (!indexed && types->canSetDefinite(shape->slot())) types->setDefinite(shape->slot()); @@ -2658,7 +2719,8 @@ UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, JSObject *obj, Shap */ if (indexed || !value.isUndefined() || !CanHaveEmptyPropertyTypesForOwnProperty(obj)) { Type type = GetValueType(value); - types->addType(cx, type); + if (!types->TypeSet::addType(type, &cx->typeLifoAlloc())) + cx->compartment()->types.setPendingNukeTypes(cx); } } } @@ -2696,7 +2758,8 @@ TypeObject::addProperty(ExclusiveContext *cx, jsid id, Property **pprop) const Value &value = singleton->getDenseElement(i); if (!value.isMagic(JS_ELEMENTS_HOLE)) { Type type = GetValueType(value); - base->types.addType(cx, type); + if (!base->types.TypeSet::addType(type, &cx->typeLifoAlloc())) + cx->compartment()->types.setPendingNukeTypes(cx); } } } else if (!JSID_IS_EMPTY(id)) { @@ -2871,7 +2934,7 @@ TypeObject::markStateChange(ExclusiveContext *cxArg) void TypeObject::setFlags(ExclusiveContext *cx, TypeObjectFlags flags) { - if ((this->flags & flags) == flags) + if (hasAllFlags(flags)) return; AutoEnterAnalysis enter(cx); @@ -2882,7 +2945,10 @@ TypeObject::setFlags(ExclusiveContext *cx, TypeObjectFlags flags) singleton->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON)); } - this->flags |= flags; + { + AutoLockForCompilation lock(cx); + addFlags(flags); + } InferSpew(ISpewOps, "%s: setFlags 0x%x", TypeObjectString(this), flags); @@ -2897,7 +2963,7 @@ TypeObject::markUnknown(ExclusiveContext *cx) JS_ASSERT(cx->compartment()->activeAnalysis); JS_ASSERT(!unknownProperties()); - if (!(flags & OBJECT_FLAG_ADDENDUM_CLEARED)) + if (!(flags() & OBJECT_FLAG_ADDENDUM_CLEARED)) clearAddendum(cx); InferSpew(ISpewOps, "UnknownProperties: %s", TypeObjectString(this)); @@ -2926,8 +2992,11 @@ TypeObject::markUnknown(ExclusiveContext *cx) void TypeObject::clearAddendum(ExclusiveContext *cx) { - JS_ASSERT(!(flags & OBJECT_FLAG_ADDENDUM_CLEARED)); - flags |= OBJECT_FLAG_ADDENDUM_CLEARED; + JS_ASSERT(!(flags() & OBJECT_FLAG_ADDENDUM_CLEARED)); + { + AutoLockForCompilation lock(cx); + addFlags(OBJECT_FLAG_ADDENDUM_CLEARED); + } /* * It is possible for the object to not have a new script or other @@ -3072,11 +3141,11 @@ TypeObject::clearTypedObjectAddendum(ExclusiveContext *cx) void TypeObject::print() { - TaggedProto tagged(proto); + TaggedProto tagged(proto()); fprintf(stderr, "%s : %s", - TypeObjectString(this), - tagged.isObject() ? TypeString(Type::ObjectType(proto)) - : (tagged.isLazy() ? "(lazy)" : "(null)")); + TypeObjectString(this), + tagged.isObject() ? TypeString(Type::ObjectType(tagged.toObject())) + : (tagged.isLazy() ? "(lazy)" : "(null)")); if (unknownProperties()) { fprintf(stderr, " unknown"); @@ -3142,7 +3211,7 @@ class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint * non-writable, both of which are indicated by the source type set * being marked as configured. */ - if (!(object->flags & OBJECT_FLAG_ADDENDUM_CLEARED) && source->configuredProperty()) + if (!(object->flags() & OBJECT_FLAG_ADDENDUM_CLEARED) && source->configuredProperty()) object->clearAddendum(cx); } @@ -3166,7 +3235,7 @@ types::AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject * * a permanent property in any transitive prototype, the definite * properties get cleared from the type. */ - RootedObject parent(cx, type->proto); + RootedObject parent(cx, type->proto().toObjectOrNull()); while (parent) { TypeObject *parentObject = parent->getType(cx); if (!parentObject || parentObject->unknownProperties()) @@ -3196,7 +3265,7 @@ class TypeConstraintClearDefiniteSingle : public TypeConstraint const char *kind() { return "clearDefiniteSingle"; } void newType(JSContext *cx, TypeSet *source, Type type) { - if (object->flags & OBJECT_FLAG_ADDENDUM_CLEARED) + if (object->flags() & OBJECT_FLAG_ADDENDUM_CLEARED) return; if (source->baseFlags() || source->getObjectCount() > 1) @@ -3279,9 +3348,9 @@ CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun) } if (baseobj->slotSpan() == 0 || - !!(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED)) + !!(type->flags() & OBJECT_FLAG_ADDENDUM_CLEARED)) { - if (type->addendum) + if (type->hasNewScript()) type->clearAddendum(cx); return; } @@ -3296,8 +3365,8 @@ CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun) type->clearAddendum(cx); return; } - JS_ASSERT(!type->addendum); - JS_ASSERT(!(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED)); + JS_ASSERT(!type->hasNewScript()); + JS_ASSERT(!(type->flags() & OBJECT_FLAG_ADDENDUM_CLEARED)); gc::AllocKind kind = gc::GetGCObjectKind(baseobj->slotSpan()); @@ -3336,7 +3405,11 @@ CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun) newScript = (TypeNewScript *) cx->calloc_(numBytes); #endif new (newScript) TypeNewScript(); - type->addendum = newScript; + + { + AutoLockForCompilation lock(cx); + type->setAddendum(newScript); + } if (!newScript) { cx->compartment()->types.setPendingNukeTypes(cx); @@ -3633,8 +3706,11 @@ JSObject::splicePrototype(JSContext *cx, const Class *clasp, Handle return true; } - type->clasp = clasp; - type->proto = proto.raw(); + { + AutoLockForCompilation lock(cx); + type->setClasp(clasp); + type->setProto(cx, proto); + } return true; } @@ -3651,8 +3727,22 @@ JSObject::makeLazyType(JSContext *cx, HandleObject obj) if (!fun->getOrCreateScript(cx)) return nullptr; } + + // Find flags which need to be specified immediately on the object. + // Don't track whether singletons are packed. + TypeObjectFlags initialFlags = OBJECT_FLAG_NON_PACKED; + + if (obj->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON)) + initialFlags |= OBJECT_FLAG_ITERATED; + + if (obj->isIndexed()) + initialFlags |= OBJECT_FLAG_SPARSE_INDEXES; + + if (obj->is() && obj->as().length() > INT32_MAX) + initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW; + Rooted proto(cx, obj->getTaggedProto()); - TypeObject *type = cx->compartment()->types.newTypeObject(cx, obj->getClass(), proto); + TypeObject *type = cx->compartment()->types.newTypeObject(cx, obj->getClass(), proto, initialFlags); if (!type) { if (cx->typeInferenceEnabled()) cx->compartment()->types.setPendingNukeTypes(cx); @@ -3674,24 +3764,10 @@ JSObject::makeLazyType(JSContext *cx, HandleObject obj) if (obj->is() && obj->as().isInterpreted()) type->interpretedFunction = &obj->as(); - if (obj->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON)) - type->flags |= OBJECT_FLAG_ITERATED; - - /* - * Adjust flags for objects which will have the wrong flags set by just - * looking at the class prototype key. - */ - - /* Don't track whether singletons are packed. */ - type->flags |= OBJECT_FLAG_NON_PACKED; - - if (obj->isIndexed()) - type->flags |= OBJECT_FLAG_SPARSE_INDEXES; - - if (obj->is() && obj->as().length() > INT32_MAX) - type->flags |= OBJECT_FLAG_LENGTH_OVERFLOW; - - obj->type_ = type; + { + AutoLockForCompilation lock(cx); + obj->type_ = type; + } return type; } @@ -3707,8 +3783,8 @@ TypeObjectWithNewScriptEntry::hash(const Lookup &lookup) /* static */ inline bool TypeObjectWithNewScriptEntry::match(const TypeObjectWithNewScriptEntry &key, const Lookup &lookup) { - return key.object->proto == lookup.matchProto.raw() && - key.object->clasp == lookup.clasp && + return key.object->proto() == lookup.matchProto && + key.object->clasp() == lookup.clasp && key.newFunction == lookup.newFunction; } @@ -3805,8 +3881,8 @@ ExclusiveContext::getNewType(const Class *clasp, TaggedProto proto, JSFunction * newTypeObjects.lookupForAdd(TypeObjectWithNewScriptSet::Lookup(clasp, proto, fun)); if (p) { TypeObject *type = p->object; - JS_ASSERT(type->clasp == clasp); - JS_ASSERT(type->proto.get() == proto.raw()); + JS_ASSERT(type->clasp() == clasp); + JS_ASSERT(type->proto() == proto); JS_ASSERT_IF(type->hasNewScript(), type->newScript()->fun == fun); return type; } @@ -3816,13 +3892,19 @@ ExclusiveContext::getNewType(const Class *clasp, TaggedProto proto, JSFunction * if (proto.isObject() && !proto.toObject()->setDelegate(this)) return nullptr; - bool markUnknown = - proto.isObject() - ? proto.toObject()->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN) - : true; + TypeObjectFlags initialFlags = 0; + if (!proto.isObject() || proto.toObject()->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN)) { + // The new type is not present in any type sets, so mark the object as + // unknown in all type sets it appears in. This allows the prototype of + // such objects to mutate freely without triggering an expensive walk of + // the compartment's type sets. (While scripts normally don't mutate + // __proto__, the browser will for proxies and such, and we need to + // accommodate this behavior). + initialFlags = OBJECT_FLAG_UNKNOWN_MASK | OBJECT_FLAG_SETS_MARKED_UNKNOWN; + } Rooted protoRoot(this, proto); - TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot, markUnknown); + TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot, initialFlags); if (!type) return nullptr; @@ -3872,17 +3954,6 @@ ExclusiveContext::getNewType(const Class *clasp, TaggedProto proto, JSFunction * } } - /* - * The new type is not present in any type sets, so mark the object as - * unknown in all type sets it appears in. This allows the prototype of - * such objects to mutate freely without triggering an expensive walk of - * the compartment's type sets. (While scripts normally don't mutate - * __proto__, the browser will for proxies and such, and we need to - * accommodate this behavior). - */ - if (type->unknownProperties()) - type->flags |= OBJECT_FLAG_SETS_MARKED_UNKNOWN; - return type; } @@ -3907,7 +3978,7 @@ ExclusiveContext::getLazyType(const Class *clasp, TaggedProto proto) } Rooted protoRoot(this, proto); - TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot, false); + TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot); if (!type) return nullptr; @@ -4159,8 +4230,8 @@ JSCompartment::sweepNewTypeObjectTable(TypeObjectWithNewScriptSet &table) } else if (entry.newFunction && IsObjectAboutToBeFinalized(&entry.newFunction)) { e.removeFront(); } else if (entry.object != e.front().object) { - TypeObjectWithNewScriptSet::Lookup lookup(entry.object->clasp, - entry.object->proto.get(), + TypeObjectWithNewScriptSet::Lookup lookup(entry.object->clasp(), + entry.object->proto(), entry.newFunction); e.rekeyFront(lookup, entry); } @@ -4428,7 +4499,7 @@ TypeObject::addTypedObjectAddendum(JSContext *cx, JS_ASSERT(repr); - if (flags & OBJECT_FLAG_ADDENDUM_CLEARED) + if (flags() & OBJECT_FLAG_ADDENDUM_CLEARED) return true; JS_ASSERT(!unknownProperties()); diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index e5d8ac01cfc3..ff7c27970b00 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -25,20 +25,38 @@ namespace js { +#ifdef DEBUG +bool CurrentThreadCanWriteCompilationData(); +bool CurrentThreadCanReadCompilationData(); +#endif + class TypeRepresentation; class TaggedProto { public: + static JSObject * const LazyProto; + TaggedProto() : proto(nullptr) {} TaggedProto(JSObject *proto) : proto(proto) {} uintptr_t toWord() const { return uintptr_t(proto); } - inline bool isLazy() const; - inline bool isObject() const; - inline JSObject *toObject() const; - inline JSObject *toObjectOrNull() const; + bool isLazy() const { + return proto == LazyProto; + } + bool isObject() const { + /* Skip nullptr and LazyProto. */ + return uintptr_t(proto) > uintptr_t(TaggedProto::LazyProto); + } + JSObject *toObject() const { + JS_ASSERT(isObject()); + return proto; + } + JSObject *toObjectOrNull() const { + JS_ASSERT(!proto || isObject()); + return proto; + } JSObject *raw() const { return proto; } bool operator ==(const TaggedProto &other) { return proto == other.proto; } @@ -77,10 +95,10 @@ class TaggedProtoOperations public: uintptr_t toWord() const { return value()->toWord(); } - inline bool isLazy() const; - inline bool isObject() const; - inline JSObject *toObject() const; - inline JSObject *toObjectOrNull() const; + inline bool isLazy() const { return value()->isLazy(); } + inline bool isObject() const { return value()->isObject(); } + inline JSObject *toObject() const { return value()->toObject(); } + inline JSObject *toObjectOrNull() const { return value()->toObjectOrNull(); } JSObject *raw() const { return value()->raw(); } }; @@ -383,6 +401,12 @@ enum MOZ_ENUM_TYPE(uint32_t) { /* If set, addendum information should not be installed on this object. */ OBJECT_FLAG_ADDENDUM_CLEARED = 0x2, + /* + * If set, the object's prototype might be in the nursery and can't be + * used during Ion compilation (which may be occurring off thread). + */ + OBJECT_FLAG_NURSERY_PROTO = 0x4, + /* * Whether we have ensured all type sets in the compartment contain * ANYOBJECT instead of this object. @@ -503,7 +527,7 @@ class TypeSet static TemporaryTypeSet *unionSets(TypeSet *a, TypeSet *b, LifoAlloc *alloc); /* Add a type to this set using the specified allocator. */ - inline bool addType(Type type, LifoAlloc *alloc, bool *padded = nullptr); + inline bool addType(Type type, LifoAlloc *alloc); /* Get a list of all types in this set. */ typedef Vector TypeList; @@ -857,11 +881,45 @@ struct TypeTypedObject : public TypeObjectAddendum /* Type information about an object accessed by a script. */ struct TypeObject : gc::BarrieredCell { - /* Class shared by objects using this type. */ - const Class *clasp; + private: + /* Class shared by object using this type. */ + const Class *clasp_; /* Prototype shared by objects using this type. */ - HeapPtrObject proto; + HeapPtrObject proto_; + +#ifdef DEBUG + void assertCanAccessProto(); +#else + void assertCanAccessProto() {} +#endif + + public: + + const Class *clasp() { + return clasp_; + } + + void setClasp(const Class *clasp) { + JS_ASSERT(CurrentThreadCanWriteCompilationData()); + JS_ASSERT(singleton); + clasp_ = clasp; + } + + TaggedProto proto() { + assertCanAccessProto(); + return TaggedProto(proto_); + } + + HeapPtrObject &protoRaw() { + // For use during marking, don't call otherwise. + return proto_; + } + + void setProto(JSContext *cx, TaggedProto proto); + void setProtoUnchecked(TaggedProto proto) { + proto_ = proto.raw(); + } /* * Whether there is a singleton JS object with this type. That JS object @@ -877,8 +935,9 @@ struct TypeObject : gc::BarrieredCell static const size_t LAZY_SINGLETON = 1; bool lazy() const { return singleton == (JSObject *) LAZY_SINGLETON; } + private: /* Flags for this object. */ - TypeObjectFlags flags; + TypeObjectFlags flags_; /* * This field allows various special classes of objects to attach @@ -891,23 +950,48 @@ struct TypeObject : gc::BarrieredCell * before the object escapes. */ HeapPtr addendum; + public: + + TypeObjectFlags flags() const { + JS_ASSERT(CurrentThreadCanReadCompilationData()); + return flags_; + } + + void addFlags(TypeObjectFlags flags) { + JS_ASSERT(CurrentThreadCanWriteCompilationData()); + flags_ |= flags; + } + + void clearFlags(TypeObjectFlags flags) { + JS_ASSERT(CurrentThreadCanWriteCompilationData()); + flags_ &= ~flags; + } bool hasNewScript() const { + JS_ASSERT(CurrentThreadCanReadCompilationData()); return addendum && addendum->isNewScript(); } TypeNewScript *newScript() { + JS_ASSERT(CurrentThreadCanReadCompilationData()); return addendum->asNewScript(); } bool hasTypedObject() { + JS_ASSERT(CurrentThreadCanReadCompilationData()); return addendum && addendum->isTypedObject(); } TypeTypedObject *typedObject() { + JS_ASSERT(CurrentThreadCanReadCompilationData()); return addendum->asTypedObject(); } + void setAddendum(TypeObjectAddendum *addendum) { + JS_ASSERT(CurrentThreadCanWriteCompilationData()); + this->addendum = addendum; + } + /* * Tag the type object for a binary data type descriptor, instance, * or handle with the type representation of the data it points at. @@ -919,6 +1003,7 @@ struct TypeObject : gc::BarrieredCell TypeTypedObject::Kind kind , TypeRepresentation *repr); + private: /* * Properties of this object. This may contain JSID_VOID, representing the * types of all integer indexes of the object, and/or JSID_EMPTY, holding @@ -953,6 +1038,7 @@ struct TypeObject : gc::BarrieredCell * might update the property with a new type. */ Property **propertySet; + public: /* If this is an interpreted function, the function object. */ HeapPtrFunction interpretedFunction; @@ -961,27 +1047,31 @@ struct TypeObject : gc::BarrieredCell uint32_t padding; #endif - inline TypeObject(const Class *clasp, TaggedProto proto, bool unknown); + inline TypeObject(const Class *clasp, TaggedProto proto, TypeObjectFlags initialFlags); bool hasAnyFlags(TypeObjectFlags flags) { JS_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); - return !!(this->flags & flags); + return !!(this->flags() & flags); } bool hasAllFlags(TypeObjectFlags flags) { JS_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); - return (this->flags & flags) == flags; + return (this->flags() & flags) == flags; } bool unknownProperties() { - JS_ASSERT_IF(flags & OBJECT_FLAG_UNKNOWN_PROPERTIES, + JS_ASSERT_IF(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES, hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK)); - return !!(flags & OBJECT_FLAG_UNKNOWN_PROPERTIES); + return !!(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES); } bool shouldPreTenure() { return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties(); } + bool hasTenuredProto() const { + return !(flags() & OBJECT_FLAG_NURSERY_PROTO); + } + gc::InitialHeap initialHeap(CompilerConstraintList *constraints); bool canPreTenure() { @@ -991,7 +1081,7 @@ struct TypeObject : gc::BarrieredCell // this bit reliably. if (unknownProperties()) return false; - return (flags & OBJECT_FLAG_FROM_ALLOCATION_SITE) || hasNewScript(); + return (flags() & OBJECT_FLAG_FROM_ALLOCATION_SITE) || hasNewScript(); } void setShouldPreTenure(ExclusiveContext *cx) { @@ -1046,12 +1136,20 @@ struct TypeObject : gc::BarrieredCell static inline ThingRootKind rootKind() { return THING_ROOT_TYPE_OBJECT; } + static inline uint32_t offsetOfClasp() { + return offsetof(TypeObject, clasp_); + } + + static inline uint32_t offsetOfProto() { + return offsetof(TypeObject, proto_); + } + private: inline uint32_t basePropertyCount() const; inline void setBasePropertyCount(uint32_t count); static void staticAsserts() { - JS_STATIC_ASSERT(offsetof(TypeObject, proto) == offsetof(js::shadow::TypeObject, proto)); + JS_STATIC_ASSERT(offsetof(TypeObject, proto_) == offsetof(js::shadow::TypeObject, proto)); } }; @@ -1247,6 +1345,7 @@ struct TypeObjectKey const Class *clasp(); TaggedProto proto(); + bool hasTenuredProto(); JSObject *singleton(); TypeNewScript *newScript(); @@ -1416,7 +1515,7 @@ struct TypeCompartment * js_ObjectClass). */ TypeObject *newTypeObject(ExclusiveContext *cx, const Class *clasp, Handle proto, - bool unknown = false); + TypeObjectFlags initialFlags = 0); /* Get or make an object for an allocation site, and add to the allocation site table. */ TypeObject *addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key); diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index 5d6f7b38f608..ab4186e83ef9 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -25,61 +25,6 @@ #include "jsanalyzeinlines.h" #include "jscntxtinlines.h" -inline bool -js::TaggedProto::isObject() const -{ - /* Skip nullptr and Proxy::LazyProto. */ - return uintptr_t(proto) > uintptr_t(Proxy::LazyProto); -} - -inline bool -js::TaggedProto::isLazy() const -{ - return proto == Proxy::LazyProto; -} - -inline JSObject * -js::TaggedProto::toObject() const -{ - JS_ASSERT(isObject()); - return proto; -} - -inline JSObject * -js::TaggedProto::toObjectOrNull() const -{ - JS_ASSERT(!proto || isObject()); - return proto; -} - -template -inline bool -js::TaggedProtoOperations::isLazy() const -{ - return value()->isLazy(); -} - -template -inline bool -js::TaggedProtoOperations::isObject() const -{ - return value()->isObject(); -} - -template -inline JSObject * -js::TaggedProtoOperations::toObject() const -{ - return value()->toObject(); -} - -template -inline JSObject * -js::TaggedProtoOperations::toObjectOrNull() const -{ - return value()->toObjectOrNull(); -} - namespace js { namespace types { @@ -534,7 +479,7 @@ MarkTypeObjectUnknownProperties(JSContext *cx, TypeObject *obj, if (cx->typeInferenceEnabled()) { if (!obj->unknownProperties()) obj->markUnknown(cx); - if (markSetsUnknown && !(obj->flags & OBJECT_FLAG_SETS_MARKED_UNKNOWN)) + if (markSetsUnknown && !(obj->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN)) cx->compartment()->types.markSetsUnknown(cx, obj); } } @@ -605,7 +550,8 @@ TypeScript::NumTypeSets(JSScript *script) /* static */ inline StackTypeSet * TypeScript::ThisTypes(JSScript *script) { - return script->types->typeArray() + script->nTypeSets() + js::analyze::ThisSlot(); + JS_ASSERT(CurrentThreadCanReadCompilationData()); + return script->types->typeArray() + script->nTypeSets() + analyze::ThisSlot(); } /* @@ -618,7 +564,8 @@ TypeScript::ThisTypes(JSScript *script) TypeScript::ArgTypes(JSScript *script, unsigned i) { JS_ASSERT(i < script->function()->nargs()); - return script->types->typeArray() + script->nTypeSets() + js::analyze::ArgSlot(i); + JS_ASSERT(CurrentThreadCanReadCompilationData()); + return script->types->typeArray() + script->nTypeSets() + analyze::ArgSlot(i); } template @@ -1089,10 +1036,8 @@ TypeSet::clearObjects() } bool -TypeSet::addType(Type type, LifoAlloc *alloc, bool *padded) +TypeSet::addType(Type type, LifoAlloc *alloc) { - JS_ASSERT_IF(padded, !*padded); - if (unknown()) return true; @@ -1100,8 +1045,6 @@ TypeSet::addType(Type type, LifoAlloc *alloc, bool *padded) flags |= TYPE_FLAG_BASE_MASK; clearObjects(); JS_ASSERT(unknown()); - if (padded) - *padded = true; return true; } @@ -1115,8 +1058,6 @@ TypeSet::addType(Type type, LifoAlloc *alloc, bool *padded) flag |= TYPE_FLAG_INT32; flags |= flag; - if (padded) - *padded = true; return true; } @@ -1156,8 +1097,6 @@ TypeSet::addType(Type type, LifoAlloc *alloc, bool *padded) clearObjects(); } - if (padded) - *padded = true; return true; } @@ -1166,13 +1105,16 @@ ConstraintTypeSet::addType(ExclusiveContext *cxArg, Type type) { JS_ASSERT(cxArg->compartment()->activeAnalysis); - bool added = false; - if (!TypeSet::addType(type, &cxArg->typeLifoAlloc(), &added)) { - cxArg->compartment()->types.setPendingNukeTypes(cxArg); + if (hasType(type)) return; + + { + AutoLockForCompilation lock(cxArg); + if (!TypeSet::addType(type, &cxArg->typeLifoAlloc())) { + cxArg->compartment()->types.setPendingNukeTypes(cxArg); + return; + } } - if (!added) - return; InferSpew(ISpewOps, "addType: %sT%p%s %s", InferSpewColor(this), this, InferSpewColorReset(), @@ -1275,7 +1217,7 @@ TypeSet::getObjectClass(unsigned i) const if (JSObject *object = getSingleObject(i)) return object->getClass(); if (TypeObject *object = getTypeObject(i)) - return object->clasp; + return object->clasp(); return nullptr; } @@ -1283,18 +1225,16 @@ TypeSet::getObjectClass(unsigned i) const // TypeObject ///////////////////////////////////////////////////////////////////// -inline TypeObject::TypeObject(const Class *clasp, TaggedProto proto, bool unknown) +inline TypeObject::TypeObject(const Class *clasp, TaggedProto proto, TypeObjectFlags initialFlags) { mozilla::PodZero(this); /* Inner objects may not appear on prototype chains. */ JS_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject); - this->clasp = clasp; - this->proto = proto.raw(); - - if (unknown) - flags |= OBJECT_FLAG_UNKNOWN_MASK; + this->clasp_ = clasp; + this->proto_ = proto.raw(); + this->flags_ = initialFlags; InferSpew(ISpewOps, "newObject: %s", TypeObjectString(this)); } @@ -1302,15 +1242,17 @@ inline TypeObject::TypeObject(const Class *clasp, TaggedProto proto, bool unknow inline uint32_t TypeObject::basePropertyCount() const { - return (flags & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT; + JS_ASSERT(CurrentThreadCanReadCompilationData()); + return (flags() & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT; } inline void TypeObject::setBasePropertyCount(uint32_t count) { + // Note: Callers must ensure they are performing threadsafe operations. JS_ASSERT(count <= OBJECT_FLAG_PROPERTY_COUNT_LIMIT); - flags = (flags & ~OBJECT_FLAG_PROPERTY_COUNT_MASK) - | (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT); + flags_ = (flags() & ~OBJECT_FLAG_PROPERTY_COUNT_MASK) + | (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT); } inline HeapTypeSet * @@ -1322,36 +1264,46 @@ TypeObject::getProperty(ExclusiveContext *cx, jsid id) JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); JS_ASSERT(!unknownProperties()); - uint32_t propertyCount = basePropertyCount(); - Property **pprop = HashSetInsert - (cx->typeLifoAlloc(), propertySet, propertyCount, id); - if (!pprop) { - cx->compartment()->types.setPendingNukeTypes(cx); - return nullptr; - } + if (HeapTypeSet *types = maybeGetProperty(id)) + return types; + + uint32_t propertyCount; + Property **pprop; + { + AutoLockForCompilation lock(cx); + + propertyCount = basePropertyCount(); + pprop = HashSetInsert + (cx->typeLifoAlloc(), propertySet, propertyCount, id); + if (!pprop) { + cx->compartment()->types.setPendingNukeTypes(cx); + return nullptr; + } + + JS_ASSERT(!*pprop); - if (!*pprop) { setBasePropertyCount(propertyCount); if (!addProperty(cx, id, pprop)) { setBasePropertyCount(0); propertySet = nullptr; return nullptr; } - if (propertyCount == OBJECT_FLAG_PROPERTY_COUNT_LIMIT) { - markUnknown(cx); + } - /* - * Return an arbitrary property in the object, as all have unknown - * type and are treated as configured. - */ - unsigned count = getPropertyCount(); - for (unsigned i = 0; i < count; i++) { - if (Property *prop = getProperty(i)) - return &prop->types; - } + if (propertyCount == OBJECT_FLAG_PROPERTY_COUNT_LIMIT) { + markUnknown(cx); - MOZ_ASSUME_UNREACHABLE("Missing property"); + /* + * Return an arbitrary property in the object, as all have unknown + * type and are treated as configured. + */ + unsigned count = getPropertyCount(); + for (unsigned i = 0; i < count; i++) { + if (Property *prop = getProperty(i)) + return &prop->types; } + + MOZ_ASSUME_UNREACHABLE("Missing property"); } return &(*pprop)->types; @@ -1363,6 +1315,7 @@ TypeObject::maybeGetProperty(jsid id) JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id)); JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); JS_ASSERT(!unknownProperties()); + JS_ASSERT(CurrentThreadCanReadCompilationData()); Property *prop = HashSetLookup (propertySet, basePropertyCount(), id); diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 00a2ebc25cc8..d45705cdf3c1 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -1937,6 +1937,7 @@ GlobalObject::initIteratorClasses(JSContext *cx, Handle global) if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto)) return false; + AutoLockForCompilation lock(cx); global->setSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto)); global->setConstructor(JSProto_GeneratorFunction, ObjectValue(*genFunction)); global->setPrototype(JSProto_GeneratorFunction, ObjectValue(*genFunctionProto)); diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index a9515a889c35..61ff5de58012 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1273,7 +1273,7 @@ NewObject(ExclusiveContext *cx, const Class *clasp, types::TypeObject *type_, JS if (!NewObjectMetadata(cx, &metadata)) return nullptr; - RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(type->proto), + RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, type->proto(), parent, metadata, kind)); if (!shape) return nullptr; @@ -1457,7 +1457,7 @@ js::NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, gc NewObjectCache &cache = cx->runtime()->newObjectCache; NewObjectCache::EntryIndex entry = -1; - if (parent == type->proto->getParent() && + if (parent == type->proto().toObject()->getParent() && newKind == GenericObject && !cx->compartment()->hasObjectMetadataCallback()) { @@ -1985,9 +1985,12 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved & * Swap the object's types, to restore their initial type information. * The prototypes and classes of the objects were swapped in ReserveForTradeGuts. */ - TypeObject *tmp = a->type_; - a->type_ = b->type_; - b->type_ = tmp; + { + AutoLockForCompilation lock(cx); + TypeObject *tmp = a->type_; + a->type_ = b->type_; + b->type_ = tmp; + } /* Don't try to swap a JSFunction for a plain function JSObject. */ JS_ASSERT_IF(a->is(), a->tenuredSizeOfThis() == b->tenuredSizeOfThis()); @@ -2019,9 +2022,12 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved & char tmp[mozilla::tl::Max::value]; JS_ASSERT(size <= sizeof(tmp)); - js_memcpy(tmp, a, size); - js_memcpy(a, b, size); - js_memcpy(b, tmp, size); + { + AutoLockForCompilation lock(cx); + js_memcpy(tmp, a, size); + js_memcpy(a, b, size); + js_memcpy(b, tmp, size); + } #ifdef JSGC_GENERATIONAL /* @@ -2059,9 +2065,12 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved & void *bpriv = b->hasPrivate() ? b->getPrivate() : nullptr; char tmp[sizeof(JSObject)]; - js_memcpy(&tmp, a, sizeof tmp); - js_memcpy(a, b, sizeof tmp); - js_memcpy(b, &tmp, sizeof tmp); + { + AutoLockForCompilation lock(cx); + js_memcpy(&tmp, a, sizeof tmp); + js_memcpy(a, b, sizeof tmp); + js_memcpy(b, &tmp, sizeof tmp); + } if (a->isNative()) a->shape_->setNumFixedSlots(reserved.newafixed); @@ -2967,7 +2976,11 @@ js::SetClassAndProto(JSContext *cx, HandleObject obj, MarkTypeObjectUnknownProperties(cx, obj->type(), true); MarkTypeObjectUnknownProperties(cx, type, true); - obj->setType(type); + { + AutoLockForCompilation lock(cx); + obj->setType(type); + } + *succeeded = true; return true; } @@ -4030,7 +4043,7 @@ NativeGetInline(JSContext *cx, case JSOP_GETPROP: case JSOP_CALLPROP: case JSOP_LENGTH: - script->baselineScript()->noteAccessedGetter(script->pcToOffset(pc)); + script->baselineScript()->noteAccessedGetter(cx, script->pcToOffset(pc)); break; default: break; diff --git a/js/src/jsobj.h b/js/src/jsobj.h index fabb240c24c5..0c4ffa3edd08 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -474,7 +474,7 @@ class JSObject : public js::ObjectImpl bool uninlinedIsProxy() const; JSObject *getProto() const { JS_ASSERT(!uninlinedIsProxy()); - return js::ObjectImpl::getProto(); + return getTaggedProto().toObjectOrNull(); } static inline bool getProto(JSContext *cx, js::HandleObject obj, js::MutableHandleObject protop); diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 71ca517b5689..674d4ea03c17 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -393,6 +393,9 @@ JSObject::clearType(JSContext *cx, js::HandleObject obj) inline void JSObject::setType(js::types::TypeObject *newType) { + // Note: This is usually called for newly created objects that haven't + // escaped to script yet, so don't require that the compilation lock be + // held here. JS_ASSERT(newType); JS_ASSERT(!hasSingletonType()); type_ = newType; @@ -405,7 +408,7 @@ JSObject::getProto(JSContext *cx, js::HandleObject obj, js::MutableHandleObject JS_ASSERT(obj->is()); return js::Proxy::getPrototypeOf(cx, obj, protop); } else { - protop.set(obj->js::ObjectImpl::getProto()); + protop.set(obj->getTaggedProto().toObjectOrNull()); return true; } } @@ -461,11 +464,11 @@ JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::Initi * make sure their presence is consistent with the shape. */ JS_ASSERT(shape && type); - JS_ASSERT(type->clasp == shape->getObjectClass()); - JS_ASSERT(type->clasp != &js::ArrayObject::class_); - JS_ASSERT(js::gc::GetGCKindSlots(kind, type->clasp) == shape->numFixedSlots()); - JS_ASSERT_IF(type->clasp->flags & JSCLASS_BACKGROUND_FINALIZE, IsBackgroundFinalized(kind)); - JS_ASSERT_IF(type->clasp->finalize, heap == js::gc::TenuredHeap); + JS_ASSERT(type->clasp() == shape->getObjectClass()); + JS_ASSERT(type->clasp() != &js::ArrayObject::class_); + JS_ASSERT(js::gc::GetGCKindSlots(kind, type->clasp()) == shape->numFixedSlots()); + JS_ASSERT_IF(type->clasp()->flags & JSCLASS_BACKGROUND_FINALIZE, IsBackgroundFinalized(kind)); + JS_ASSERT_IF(type->clasp()->finalize, heap == js::gc::TenuredHeap); JS_ASSERT_IF(extantSlots, dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan())); js::HeapSlot *slots = extantSlots; @@ -495,7 +498,7 @@ JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::Initi obj->slots = slots; obj->elements = js::emptyObjectElements; - const js::Class *clasp = type->clasp; + const js::Class *clasp = type->clasp(); if (clasp->hasPrivate()) obj->privateRef(shape->numFixedSlots()) = nullptr; @@ -512,9 +515,9 @@ JSObject::createArray(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc:: uint32_t length) { JS_ASSERT(shape && type); - JS_ASSERT(type->clasp == shape->getObjectClass()); - JS_ASSERT(type->clasp == &js::ArrayObject::class_); - JS_ASSERT_IF(type->clasp->finalize, heap == js::gc::TenuredHeap); + JS_ASSERT(type->clasp() == shape->getObjectClass()); + JS_ASSERT(type->clasp() == &js::ArrayObject::class_); + JS_ASSERT_IF(type->clasp()->finalize, heap == js::gc::TenuredHeap); /* * Arrays use their fixed slots to store elements, and must have enough @@ -979,8 +982,11 @@ DefineConstructorAndPrototype(JSContext *cx, Handle global, JS_ASSERT(!global->nativeLookup(cx, id)); /* Set these first in case AddTypePropertyId looks for this class. */ - global->setConstructor(key, ObjectValue(*ctor)); - global->setPrototype(key, ObjectValue(*proto)); + { + AutoLockForCompilation lock(cx); + global->setConstructor(key, ObjectValue(*ctor)); + global->setPrototype(key, ObjectValue(*proto)); + } global->setConstructorPropertySlot(key, ObjectValue(*ctor)); if (!global->addDataProperty(cx, id, GlobalObject::constructorPropertySlot(key), 0)) { diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index b1d4dcdd6605..1b1582516c2b 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -2748,7 +2748,7 @@ Proxy::defaultValue(JSContext *cx, HandleObject proxy, JSType hint, MutableHandl return proxy->as().handler()->defaultValue(cx, proxy, hint, vp); } -JSObject * const Proxy::LazyProto = reinterpret_cast(0x1); +JSObject * const TaggedProto::LazyProto = reinterpret_cast(0x1); bool Proxy::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject proto) diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 12cc9ceab9d1..e463388fd8d7 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -324,8 +324,6 @@ class Proxy /* IC entry path for handling __noSuchMethod__ on access. */ static bool callProp(JSContext *cx, HandleObject proxy, HandleObject reveiver, HandleId id, MutableHandleValue vp); - - static JSObject * const LazyProto; }; // Use these in places where you don't want to #include vm/ProxyObject.h. diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 7c25e8dbabe8..30c7feebe25f 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -3015,7 +3015,10 @@ JSScript::argumentsOptimizationFailed(JSContext *cx, HandleScript script) JS_ASSERT(!script->isGenerator()); - script->needsArgsObj_ = true; + { + AutoLockForCompilation lock(cx); + script->needsArgsObj_ = true; + } #ifdef JS_ION /* diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 93c309e498ca..7f03e3185827 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -977,7 +977,11 @@ class JSScript : public js::gc::BarrieredCell * that needsArgsObj is only called after the script has been analyzed. */ bool analyzedArgsUsage() const { return !needsArgsAnalysis_; } - bool needsArgsObj() const { JS_ASSERT(analyzedArgsUsage()); return needsArgsObj_; } + bool needsArgsObj() const { + JS_ASSERT(analyzedArgsUsage()); + JS_ASSERT(js::CurrentThreadCanReadCompilationData()); + return needsArgsObj_; + } void setNeedsArgsObj(bool needsArgsObj); static bool argumentsOptimizationFailed(JSContext *cx, js::HandleScript script); @@ -1027,6 +1031,7 @@ class JSScript : public js::gc::BarrieredCell } bool hasBaselineScript() const { + JS_ASSERT(js::CurrentThreadCanReadCompilationData()); return baseline && baseline != BASELINE_DISABLED_SCRIPT; } bool canBaselineCompile() const { @@ -1036,7 +1041,7 @@ class JSScript : public js::gc::BarrieredCell JS_ASSERT(hasBaselineScript()); return baseline; } - inline void setBaselineScript(js::jit::BaselineScript *baselineScript); + inline void setBaselineScript(JSContext *maybecx, js::jit::BaselineScript *baselineScript); void updateBaselineOrIonRaw(); diff --git a/js/src/jsscriptinlines.h b/js/src/jsscriptinlines.h index b498a626243a..67398e8d4f8b 100644 --- a/js/src/jsscriptinlines.h +++ b/js/src/jsscriptinlines.h @@ -125,11 +125,14 @@ JSScript::setIsCallsiteClone(JSObject *fun) { } inline void -JSScript::setBaselineScript(js::jit::BaselineScript *baselineScript) { +JSScript::setBaselineScript(JSContext *maybecx, js::jit::BaselineScript *baselineScript) { #ifdef JS_ION if (hasBaselineScript()) js::jit::BaselineScript::writeBarrierPre(tenuredZone(), baseline); #endif + mozilla::Maybe lock; + if (maybecx) + lock.construct(maybecx); baseline = baselineScript; updateBaselineOrIonRaw(); } diff --git a/js/src/jsworkers.cpp b/js/src/jsworkers.cpp index 0136601f6a2e..09abb061397c 100644 --- a/js/src/jsworkers.cpp +++ b/js/src/jsworkers.cpp @@ -91,6 +91,8 @@ js::StartOffThreadIonCompile(JSContext *cx, jit::IonBuilder *builder) if (!state.ionWorklist.append(builder)) return false; + cx->runtime()->addCompilationThread(); + state.notifyAll(WorkerThreadState::PRODUCER); return true; } @@ -615,7 +617,7 @@ WorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *toke iter.next()) { types::TypeObject *object = iter.get(); - TaggedProto proto(object->proto); + TaggedProto proto(object->proto()); if (!proto.isObject()) continue; @@ -626,7 +628,9 @@ WorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *toke JSObject *newProto = GetClassPrototypePure(&parseTask->scopeChain->global(), key); JS_ASSERT(newProto); - object->proto = newProto; + // Note: this is safe to do without requiring the compilation lock, as + // the new type is not yet available to compilation threads. + object->setProtoUnchecked(newProto); } // Move the parsed script and all its contents into the desired compartment. @@ -760,7 +764,11 @@ WorkerThread::handleIonWorkload(WorkerThreadState &state) jit::IonContext ictx(jit::CompileRuntime::get(runtime), jit::CompileCompartment::get(ionBuilder->script()->compartment()), &ionBuilder->alloc()); - ionBuilder->setBackgroundCodegen(jit::CompileBackEnd(ionBuilder)); + AutoEnterIonCompilation ionCompiling; + bool succeeded = ionBuilder->build(); + ionBuilder->clearForBackEnd(); + if (succeeded) + ionBuilder->setBackgroundCodegen(jit::CompileBackEnd(ionBuilder)); } state.lock(); diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 7a2e1c2c1587..413c630ecf13 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -48,7 +48,7 @@ Wrapper::New(JSContext *cx, JSObject *obj, JSObject *parent, Wrapper *handler) RootedValue priv(cx, ObjectValue(*obj)); ProxyOptions options; options.setCallable(obj->isCallable()); - return NewProxyObject(cx, handler, priv, Proxy::LazyProto, parent, options); + return NewProxyObject(cx, handler, priv, TaggedProto::LazyProto, parent, options); } JSObject * @@ -141,7 +141,7 @@ js::TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject { // Allow wrapping outer window proxies. JS_ASSERT(!obj->is() || obj->getClass()->ext.innerObject); - JS_ASSERT(wrappedProto == Proxy::LazyProto); + JS_ASSERT(wrappedProto == TaggedProto::LazyProto); return Wrapper::New(cx, obj, parent, &CrossCompartmentWrapper::singleton); } diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index aff455ebc495..954e08b7a1d8 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -669,7 +669,7 @@ GlobalObject::getSelfHostedFunction(JSContext *cx, HandleAtom selfHostedName, Ha RootedId shId(cx, AtomToId(selfHostedName)); RootedObject holder(cx, cx->global()->intrinsicsHolder()); - if (HasDataProperty(cx, holder, shId, funVal.address())) + if (cx->global()->maybeGetIntrinsicValue(shId, funVal.address())) return true; if (!cx->runtime()->maybeWrappedSelfHostedFunction(cx, shId, funVal)) @@ -685,5 +685,31 @@ GlobalObject::getSelfHostedFunction(JSContext *cx, HandleAtom selfHostedName, Ha fun->setExtendedSlot(0, StringValue(selfHostedName)); funVal.setObject(*fun); - return JSObject::defineGeneric(cx, holder, shId, funVal, nullptr, nullptr, 0); + return cx->global()->addIntrinsicValue(cx, shId, funVal); +} + +bool +GlobalObject::addIntrinsicValue(JSContext *cx, HandleId id, HandleValue value) +{ + RootedObject holder(cx, intrinsicsHolder()); + + // Work directly with the shape machinery underlying the object, so that we + // don't take the compilation lock until we are ready to update the object + // without triggering a GC. + + uint32_t slot = holder->slotSpan(); + RootedShape last(cx, holder->lastProperty()); + Rooted base(cx, last->base()->unowned()); + + StackShape child(base, id, slot, holder->numFixedSlots(), 0, 0, 0); + RootedShape shape(cx, cx->compartment()->propertyTree.getChild(cx, last, holder->numFixedSlots(), child)); + if (!shape) + return false; + + AutoLockForCompilation lock(cx); + if (!JSObject::setLastProperty(cx, holder, shape)) + return false; + + holder->setSlot(shape->slot(), value); + return true; } diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index 830fc509937d..efac12ea59b9 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -513,26 +513,30 @@ class GlobalObject : public JSObject return &getSlotRefForCompilation(INTRINSICS).toObject(); } - bool maybeGetIntrinsicValue(PropertyName *name, Value *vp) { + bool maybeGetIntrinsicValue(jsid id, Value *vp) { + JS_ASSERT(CurrentThreadCanReadCompilationData()); JSObject *holder = intrinsicsHolder(); - if (Shape *shape = holder->nativeLookupPure(name)) { + if (Shape *shape = holder->nativeLookupPure(id)) { *vp = holder->getSlot(shape->slot()); return true; } return false; } + bool maybeGetIntrinsicValue(PropertyName *name, Value *vp) { + return maybeGetIntrinsicValue(NameToId(name), vp); + } bool getIntrinsicValue(JSContext *cx, HandlePropertyName name, MutableHandleValue value) { if (maybeGetIntrinsicValue(name, value.address())) return true; - Rooted self(cx, this); if (!cx->runtime()->cloneSelfHostedValue(cx, name, value)) return false; - RootedObject holder(cx, self->intrinsicsHolder()); RootedId id(cx, NameToId(name)); - return JS_DefinePropertyById(cx, holder, id, value, nullptr, nullptr, 0); + return addIntrinsicValue(cx, id, value); } + bool addIntrinsicValue(JSContext *cx, HandleId id, HandleValue value); + bool setIntrinsicValue(JSContext *cx, PropertyName *name, HandleValue value) { #ifdef DEBUG RootedObject self(cx, this); diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index f36d344e93c6..32cceba9f052 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1294,7 +1294,7 @@ SetObjectElementOperation(JSContext *cx, Handle obj, HandleId id, con if ((uint32_t)i >= length) { // Annotate script if provided with information (e.g. baseline) if (script && script->hasBaselineScript() && *pc == JSOP_SETELEM) - script->baselineScript()->noteArrayWriteHole(script->pcToOffset(pc)); + script->baselineScript()->noteArrayWriteHole(cx, script->pcToOffset(pc)); } } #endif diff --git a/js/src/vm/ObjectImpl.cpp b/js/src/vm/ObjectImpl.cpp index 05f355ba9564..c0bf6451fb7d 100644 --- a/js/src/vm/ObjectImpl.cpp +++ b/js/src/vm/ObjectImpl.cpp @@ -360,7 +360,7 @@ js::ObjectImpl::markChildren(JSTracer *trc) MarkShape(trc, &shape_, "shape"); - const Class *clasp = type_->clasp; + const Class *clasp = type_->clasp(); JSObject *obj = asObjectPtr(); if (clasp->trace) clasp->trace(trc, obj); @@ -538,7 +538,7 @@ js::ArrayBufferDelegate(JSContext *cx, Handle obj) if (obj->getPrivate()) return static_cast(obj->getPrivate()); JSObject *delegate = NewObjectWithGivenProto(cx, &JSObject::class_, - obj->getProto(), nullptr); + obj->getTaggedProto(), nullptr); obj->setPrivateGCThing(delegate); return delegate; } @@ -684,7 +684,7 @@ js::GetProperty(JSContext *cx, Handle obj, Handle rece /* No property? Recur or bottom out. */ if (desc.isUndefined()) { - current = current->getProto(); + current = current->getTaggedProto().toObjectOrNull(); if (current) continue; @@ -746,7 +746,7 @@ js::GetElement(JSContext *cx, Handle obj, Handle recei /* No property? Recur or bottom out. */ if (desc.isUndefined()) { - current = current->getProto(); + current = current->getTaggedProto().toObjectOrNull(); if (current) continue; @@ -811,7 +811,7 @@ js::HasElement(JSContext *cx, Handle obj, uint32_t index, unsigned return true; } - current = current->getProto(); + current = current->getTaggedProto().toObjectOrNull(); if (current) continue; @@ -1009,7 +1009,7 @@ js::SetElement(JSContext *cx, Handle obj, Handle recei MOZ_ASSUME_UNREACHABLE("NYI: setting PropertyOp-based property"); } - current = current->getProto(); + current = current->getTaggedProto().toObjectOrNull(); if (current) continue; diff --git a/js/src/vm/ObjectImpl.h b/js/src/vm/ObjectImpl.h index d367507deae8..822a1180ca5e 100644 --- a/js/src/vm/ObjectImpl.h +++ b/js/src/vm/ObjectImpl.h @@ -980,12 +980,14 @@ class ObjectImpl : public gc::BarrieredCell /* These functions are public, and they should remain public. */ public: - JSObject * getProto() const { - return type_->proto; + js::TaggedProto getTaggedProto() const { + return type_->proto(); } + bool hasTenuredProto() const; + const Class *getClass() const { - return type_->clasp; + return type_->clasp(); } static inline bool @@ -1172,10 +1174,6 @@ class ObjectImpl : public gc::BarrieredCell */ public: - js::TaggedProto getTaggedProto() const { - return TaggedProto(getProto()); - } - Shape * lastProperty() const { MOZ_ASSERT(shape_); return shape_; diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 169921adeee0..cbcb213958c2 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -75,6 +75,9 @@ PerThreadData::PerThreadData(JSRuntime *runtime) asmJSActivationStack_(nullptr), dtoaState(nullptr), suppressGC(0), +#ifdef DEBUG + ionCompiling(false), +#endif activeCompilations(0) {} @@ -135,6 +138,12 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads) exclusiveAccessOwner(nullptr), mainThreadHasExclusiveAccess(false), numExclusiveThreads(0), + compilationLock(nullptr), +#ifdef DEBUG + compilationLockOwner(nullptr), + mainThreadHasCompilationLock(false), +#endif + numCompilationThreads(0), #endif systemZone(nullptr), numCompartments(0), @@ -362,6 +371,10 @@ JSRuntime::init(uint32_t maxbytes) exclusiveAccessLock = PR_NewLock(); if (!exclusiveAccessLock) return false; + + compilationLock = PR_NewLock(); + if (!compilationLock) + return false; #endif if (!mainThread.init()) @@ -483,6 +496,10 @@ JSRuntime::~JSRuntime() // Avoid bogus asserts during teardown. JS_ASSERT(!numExclusiveThreads); mainThreadHasExclusiveAccess = true; + + JS_ASSERT(!compilationLockOwner); + if (compilationLock) + PR_DestroyLock(compilationLock); #endif #ifdef JS_THREADSAFE @@ -816,7 +833,7 @@ JSRuntime::activeGCInAtomsZone() #if defined(DEBUG) && !defined(XP_WIN) -AutoProtectHeapForCompilation::AutoProtectHeapForCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) +AutoProtectHeapForIonCompilation::AutoProtectHeapForIonCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) : runtime(rt) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; @@ -834,7 +851,7 @@ AutoProtectHeapForCompilation::AutoProtectHeapForCompilation(JSRuntime *rt MOZ_G } } -AutoProtectHeapForCompilation::~AutoProtectHeapForCompilation() +AutoProtectHeapForIonCompilation::~AutoProtectHeapForIonCompilation() { JS_ASSERT(runtime->heapProtected_); JS_ASSERT(runtime->unprotectedArenas.empty()); @@ -936,7 +953,7 @@ js::CurrentThreadCanAccessZone(Zone *zone) return true; } -#endif +#endif // JS_THREADSAFE #ifdef DEBUG @@ -954,13 +971,73 @@ JSRuntime::assertCanLock(RuntimeLock which) JS_ASSERT_IF(workerThreadState, !workerThreadState->isLocked()); case OperationCallbackLock: JS_ASSERT(!currentThreadOwnsOperationCallbackLock()); + case CompilationLock: + JS_ASSERT(compilationLockOwner != PR_GetCurrentThread()); case GCLock: JS_ASSERT(gcLockOwner != PR_GetCurrentThread()); break; default: MOZ_CRASH(); } -#endif // JS_THREADSAFE +#endif // JS_WORKER_THREADS +} + +AutoEnterIonCompilation::AutoEnterIonCompilation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL) +{ + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + +#ifdef JS_WORKER_THREADS + PerThreadData *pt = js::TlsPerThreadData.get(); + JS_ASSERT(!pt->ionCompiling); + pt->ionCompiling = true; +#endif +} + +AutoEnterIonCompilation::~AutoEnterIonCompilation() +{ +#ifdef JS_WORKER_THREADS + PerThreadData *pt = js::TlsPerThreadData.get(); + JS_ASSERT(pt->ionCompiling); + pt->ionCompiling = false; +#endif +} + +bool +js::CurrentThreadCanWriteCompilationData() +{ +#ifdef JS_WORKER_THREADS + PerThreadData *pt = TlsPerThreadData.get(); + + // Data can only be read from during compilation. + if (pt->ionCompiling) + return false; + + // Ignore what threads with exclusive contexts are doing; these never have + // run scripts or have associated compilation threads. + JSRuntime *rt = pt->runtimeIfOnOwnerThread(); + if (!rt) + return true; + + return rt->currentThreadHasCompilationLock(); +#else + return true; +#endif +} + +bool +js::CurrentThreadCanReadCompilationData() +{ +#ifdef JS_WORKER_THREADS + PerThreadData *pt = TlsPerThreadData.get(); + + // Data can always be read from freely outside of compilation. + if (!pt || !pt->ionCompiling) + return true; + + return pt->runtime_->currentThreadHasCompilationLock(); +#else + return true; +#endif } #endif // DEBUG diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 0667445320bf..59b6a3c1c92b 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -535,6 +535,9 @@ class PerThreadData : public PerThreadDataFriendFields, friend class js::ActivationIterator; friend class js::jit::JitActivation; friend class js::AsmJSActivation; +#ifdef DEBUG + friend bool js::CurrentThreadCanReadCompilationData(); +#endif /* * Points to the most recent activation running on the thread. @@ -577,7 +580,12 @@ class PerThreadData : public PerThreadDataFriendFields, */ int32_t suppressGC; - // Whether there is an active compilation on this thread. +#ifdef DEBUG + // Whether this thread is actively Ion compiling. + bool ionCompiling; +#endif + + // Number of active bytecode compilation on this thread. unsigned activeCompilations; PerThreadData(JSRuntime *runtime); @@ -677,7 +685,8 @@ class MarkingValidator; typedef Vector ZoneVector; class AutoLockForExclusiveAccess; -class AutoProtectHeapForCompilation; +class AutoLockForCompilation; +class AutoProtectHeapForIonCompilation; void RecomputeStackLimit(JSRuntime *rt, StackKind kind); @@ -727,6 +736,7 @@ struct JSRuntime : public JS::shadow::Runtime, ExclusiveAccessLock, WorkerThreadStateLock, OperationCallbackLock, + CompilationLock, GCLock }; #ifdef DEBUG @@ -805,20 +815,44 @@ struct JSRuntime : public JS::shadow::Runtime, friend class js::AutoLockForExclusiveAccess; + /* + * Lock taken when using data that can be modified by the main thread but + * read by Ion compilation threads. Any time either the main thread writes + * such data or the compilation thread reads it, this lock must be taken. + * Note that no externally visible data is modified by the compilation + * thread, so the main thread never needs to take this lock when reading. + */ + PRLock *compilationLock; +#ifdef DEBUG + PRThread *compilationLockOwner; + bool mainThreadHasCompilationLock; +#endif + + /* Number of in flight Ion compilations. */ + size_t numCompilationThreads; + + friend class js::AutoLockForCompilation; +#ifdef DEBUG + friend bool js::CurrentThreadCanWriteCompilationData(); + friend bool js::CurrentThreadCanReadCompilationData(); +#endif + public: void setUsedByExclusiveThread(JS::Zone *zone); void clearUsedByExclusiveThread(JS::Zone *zone); #endif // JS_THREADSAFE && JS_ION +#ifdef DEBUG bool currentThreadHasExclusiveAccess() { -#if defined(JS_WORKER_THREADS) && defined(DEBUG) +#ifdef JS_WORKER_THREADS return (!numExclusiveThreads && mainThreadHasExclusiveAccess) || - exclusiveAccessOwner == PR_GetCurrentThread(); + exclusiveAccessOwner == PR_GetCurrentThread(); #else return true; #endif } +#endif // DEBUG bool exclusiveThreadsPresent() const { #ifdef JS_WORKER_THREADS @@ -828,6 +862,33 @@ struct JSRuntime : public JS::shadow::Runtime, #endif } + void addCompilationThread() { + numCompilationThreads++; + } + void removeCompilationThread() { + JS_ASSERT(numCompilationThreads); + numCompilationThreads--; + } + + bool compilationThreadsPresent() const { +#ifdef JS_WORKER_THREADS + return numCompilationThreads > 0; +#else + return false; +#endif + } + +#ifdef DEBUG + bool currentThreadHasCompilationLock() { +#ifdef JS_WORKER_THREADS + return (!numCompilationThreads && mainThreadHasCompilationLock) || + compilationLockOwner == PR_GetCurrentThread(); +#else + return true; +#endif + } +#endif // DEBUG + /* Embedders can use this zone however they wish. */ JS::Zone *systemZone; @@ -1431,7 +1492,7 @@ struct JSRuntime : public JS::shadow::Runtime, const char *numGrouping; #endif - friend class js::AutoProtectHeapForCompilation; + friend class js::AutoProtectHeapForIonCompilation; friend class js::AutoThreadSafeAccess; mozilla::DebugOnly heapProtected_; #ifdef DEBUG @@ -2049,16 +2110,36 @@ class RuntimeAllocPolicy extern const JSSecurityCallbacks NullSecurityCallbacks; -class AutoProtectHeapForCompilation +// Debugging RAII class which marks the current thread as performing an Ion +// compilation, for use by CurrentThreadCan{Read,Write}CompilationData +class AutoEnterIonCompilation +{ + public: +#ifdef DEBUG + AutoEnterIonCompilation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM); + ~AutoEnterIonCompilation(); +#else + AutoEnterIonCompilation(MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + } +#endif + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +// Debugging RAII class which protects the entire GC heap for the duration of an +// Ion compilation. When used only the main thread will be active and all +// accesses to GC things must be wrapped by an AutoThreadSafeAccess instance. +class AutoProtectHeapForIonCompilation { public: #if defined(DEBUG) && !defined(XP_WIN) JSRuntime *runtime; - AutoProtectHeapForCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM); - ~AutoProtectHeapForCompilation(); + AutoProtectHeapForIonCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM); + ~AutoProtectHeapForIonCompilation(); #else - AutoProtectHeapForCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + AutoProtectHeapForIonCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } diff --git a/js/src/vm/SPSProfiler.cpp b/js/src/vm/SPSProfiler.cpp index 8158a9800c01..61411664d5d6 100644 --- a/js/src/vm/SPSProfiler.cpp +++ b/js/src/vm/SPSProfiler.cpp @@ -56,13 +56,14 @@ SPSProfiler::enable(bool enabled) if (enabled_ == enabled) return; - enabled_ = enabled; /* * Ensure all future generated code will be instrumented, or that all * currently instrumented code is discarded */ ReleaseAllJITCode(rt->defaultFreeOp()); + enabled_ = enabled; + #ifdef JS_ION /* Toggle SPS-related jumps on baseline jitcode. * The call to |ReleaseAllJITCode| above will release most baseline jitcode, but not diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index d4c50beeb60e..8e46237b5d99 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -958,10 +958,11 @@ JSRuntime::cloneSelfHostedFunctionScript(JSContext *cx, Handle na JSScript *cscript = CloneScript(cx, NullPtr(), targetFun, sourceScript); if (!cscript) return false; - targetFun->setScript(cscript); cscript->setFunction(targetFun); + JS_ASSERT(sourceFun->nargs() == targetFun->nargs()); targetFun->setFlags(sourceFun->flags() | JSFunction::EXTENDED); + targetFun->setScript(cscript); return true; } diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 74339e88347d..1b939022765a 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -338,7 +338,7 @@ ArrayBufferObject::neuterViews(JSContext *cx, Handle buffer) size_t numViews = 0; for (view = GetViewList(buffer); view; view = view->nextView()) { numViews++; - view->neuter(); + view->neuter(cx); // Notify compiled jit code that the base pointer has moved. MarkObjectStateChange(cx, view); @@ -1182,8 +1182,10 @@ TypedArrayObject::isArrayIndex(jsid id, uint32_t *ip) } void -TypedArrayObject::neuter() +TypedArrayObject::neuter(JSContext *cx) { + AutoLockForCompilation lock(cx); + setSlot(LENGTH_SLOT, Int32Value(0)); setSlot(BYTELENGTH_SLOT, Int32Value(0)); setSlot(BYTEOFFSET_SLOT, Int32Value(0)); @@ -2625,12 +2627,12 @@ ArrayBufferViewObject::prependToViews(ArrayBufferViewObject *viewsHead) } void -ArrayBufferViewObject::neuter() +ArrayBufferViewObject::neuter(JSContext *cx) { if (is()) as().neuter(); else - as().neuter(); + as().neuter(cx); } // this default implementation is only valid for integer types diff --git a/js/src/vm/TypedArrayObject.h b/js/src/vm/TypedArrayObject.h index 275315ace116..7baa47cf8adb 100644 --- a/js/src/vm/TypedArrayObject.h +++ b/js/src/vm/TypedArrayObject.h @@ -276,7 +276,7 @@ class ArrayBufferViewObject : public JSObject void prependToViews(ArrayBufferViewObject *viewsHead); - void neuter(); + void neuter(JSContext *cx); static void trace(JSTracer *trc, JSObject *obj); }; @@ -353,7 +353,7 @@ class TypedArrayObject : public ArrayBufferViewObject inline bool isArrayIndex(jsid id, uint32_t *ip = nullptr); void copyTypedArrayElement(uint32_t index, MutableHandleValue vp); - void neuter(); + void neuter(JSContext *cx); static uint32_t slotWidth(int atype) { switch (atype) {