From 08ab10373a5a3015f8b35a261a54169d8e05cb6d Mon Sep 17 00:00:00 2001 From: Brian Hackett <bhackett1024@gmail.com> Date: Thu, 3 Oct 2013 21:44:13 -0600 Subject: [PATCH] Bug 921902 - Separate generation and attaching of heap property type constraints, r=jandem. --- js/src/frontend/BytecodeEmitter.cpp | 15 +- js/src/frontend/FoldConstants.cpp | 6 +- js/src/gc/Zone.cpp | 4 +- js/src/gc/Zone.h | 2 +- js/src/jit/CodeGenerator.cpp | 17 +- js/src/jit/CodeGenerator.h | 2 +- js/src/jit/CompileInfo.h | 13 - js/src/jit/ExecutionModeInlines.h | 11 - js/src/jit/Ion.cpp | 76 ++- js/src/jit/IonAnalysis.cpp | 27 +- js/src/jit/IonBuilder.cpp | 591 +++++++++------------ js/src/jit/IonBuilder.h | 46 +- js/src/jit/IonCaches.cpp | 6 +- js/src/jit/IonCode.h | 3 +- js/src/jit/MCallOptimize.cpp | 94 ++-- js/src/jit/MIR.cpp | 324 +++++------- js/src/jit/MIR.h | 30 +- js/src/jsgc.cpp | 6 +- js/src/jsinfer.cpp | 779 +++++++++++++++------------- js/src/jsinfer.h | 212 ++++---- js/src/jsinferinlines.h | 174 ++----- 21 files changed, 1125 insertions(+), 1313 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 242f0205e651..404618f656b9 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -5846,9 +5846,22 @@ EmitObject(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { /* Emit an index for t[2] for later consumption by JSOP_INITELEM. */ ParseNode *pn3 = pn2->pn_left; + bool isIndex = false; if (pn3->isKind(PNK_NUMBER)) { if (!EmitNumberOp(cx, pn3->pn_dval, bce)) return false; + isIndex = true; + } else { + // The parser already checked for atoms representing indexes and + // used PNK_NUMBER instead, but also watch for ids which TI treats + // as indexes for simpliciation of downstream analysis. + JS_ASSERT(pn3->isKind(PNK_NAME) || pn3->isKind(PNK_STRING)); + jsid id = NameToId(pn3->pn_atom->asPropertyName()); + if (id != types::IdToTypeId(id)) { + if (!EmitTree(cx, bce, pn3)) + return false; + isIndex = true; + } } /* Emit code for the property initializer. */ @@ -5863,7 +5876,7 @@ EmitObject(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) if (op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER) obj = nullptr; - if (pn3->isKind(PNK_NUMBER)) { + if (isIndex) { obj = nullptr; switch (op) { case JSOP_INITPROP: op = JSOP_INITELEM; break; diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 838f91542fd1..bb2418496416 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -16,6 +16,7 @@ #include "vm/NumericConversions.h" #include "jscntxtinlines.h" +#include "jsinferinlines.h" using namespace js; using namespace js::frontend; @@ -762,10 +763,11 @@ Fold(ExclusiveContext *cx, ParseNode **pnp, } } - if (name) { + if (name && NameToId(name) == types::IdToTypeId(NameToId(name))) { // Optimization 3: We have pn1["foo"] where foo is not an index. // Convert to a property access (like pn1.foo) which we optimize - // better downstream. + // better downstream. Don't bother with this for names which TI + // considers to be indexes, to simplify downstream analysis. ParseNode *expr = handler.newPropertyAccess(pn->pn_left, name, pn->pn_pos.end); if (!expr) return false; diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index 1c9cc10d9cbd..f56b824ea48b 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -183,7 +183,7 @@ Zone::sweepBreakpoints(FreeOp *fop) } void -Zone::discardJitCode(FreeOp *fop, bool discardConstraints) +Zone::discardJitCode(FreeOp *fop) { #ifdef JS_ION if (isPreservingCode()) { @@ -227,7 +227,7 @@ Zone::discardJitCode(FreeOp *fop, bool discardConstraints) if (comp->ionCompartment()) comp->ionCompartment()->optimizedStubSpace()->free(); - comp->types.sweepCompilerOutputs(fop, discardConstraints); + comp->types.clearCompilerOutputs(fop); } } #endif diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index a4c5017ccdb4..51c278aeb090 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -258,7 +258,7 @@ struct Zone : public JS::shadow::Zone, void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone> &finder); - void discardJitCode(js::FreeOp *fop, bool discardConstraints); + void discardJitCode(js::FreeOp *fop); void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *typePool); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 6e2bbf9d7db6..a57b0d624dd8 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -5708,17 +5708,18 @@ CodeGenerator::generate() } bool -CodeGenerator::link() +CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) { - JSContext *cx = GetIonContext()->cx; + JSScript *script = gen->info().script(); + ExecutionMode executionMode = gen->info().executionMode(); + JS_ASSERT(!HasIonScript(script, executionMode)); // Check to make sure we didn't have a mid-build invalidation. If so, we // will trickle to jit::Compile() and return Method_Skipped. - if (cx->compartment()->types.compiledInfo.compilerOutput(cx)->isInvalidated()) + types::RecompileInfo recompileInfo; + if (!types::FinishCompilation(cx, script, executionMode, constraints, &recompileInfo)) return true; - ExecutionMode executionMode = gen->info().executionMode(); - uint32_t scriptFrameSize = frameClass_ == FrameSizeClass::None() ? frameDepth_ : FrameSizeClass::FromDepth(frameDepth_).frameSize(); @@ -5733,7 +5734,8 @@ CodeGenerator::link() AddPossibleCallees(cx, graph.mir(), callTargets); IonScript *ionScript = - IonScript::New(cx, graph.totalSlotCount(), scriptFrameSize, snapshots_.size(), + IonScript::New(cx, recompileInfo, + graph.totalSlotCount(), scriptFrameSize, snapshots_.size(), bailouts_.length(), graph.numConstants(), safepointIndices_.length(), osiIndices_.length(), cacheList_.length(), runtimeData_.length(), @@ -5766,9 +5768,6 @@ CodeGenerator::link() return false; } - JSScript *script = gen->info().script(); - JS_ASSERT(!HasIonScript(script, executionMode)); - ionScript->setMethod(code); ionScript->setSkipArgCheckEntryOffset(getSkipArgCheckEntryOffset()); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index e68d71f1980d..3d199f6d7342 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -53,7 +53,7 @@ class CodeGenerator : public CodeGeneratorSpecific public: bool generate(); bool generateAsmJS(); - bool link(); + bool link(JSContext *cx, types::CompilerConstraintList *constraints); bool visitLabel(LLabel *lir); bool visitNop(LNop *lir); diff --git a/js/src/jit/CompileInfo.h b/js/src/jit/CompileInfo.h index 5ab72278b300..833ba69f94cf 100644 --- a/js/src/jit/CompileInfo.h +++ b/js/src/jit/CompileInfo.h @@ -28,19 +28,6 @@ CountArgSlots(JSScript *script, JSFunction *fun) return StartArgSlot(script, fun) + (fun ? fun->nargs + 1 : 0); } -enum ExecutionMode { - // Normal JavaScript execution - SequentialExecution, - - // JavaScript code to be executed in parallel worker threads, - // e.g. by ParallelArray - ParallelExecution, - - // MIR analysis performed when invoking 'new' on a script, to determine - // definite properties - DefinitePropertiesAnalysis -}; - // Not as part of the enum so we don't get warnings about unhandled enum // values. static const unsigned NumExecutionModes = ParallelExecution + 1; diff --git a/js/src/jit/ExecutionModeInlines.h b/js/src/jit/ExecutionModeInlines.h index 47356b6e83d9..cc63a134e6e7 100644 --- a/js/src/jit/ExecutionModeInlines.h +++ b/js/src/jit/ExecutionModeInlines.h @@ -101,17 +101,6 @@ CompilingOffThread(HandleScript script, ExecutionMode cmode) MOZ_ASSUME_UNREACHABLE("No such execution mode"); } -static inline types::CompilerOutput::Kind -CompilerOutputKind(ExecutionMode cmode) -{ - switch (cmode) { - case SequentialExecution: return types::CompilerOutput::Ion; - case ParallelExecution: return types::CompilerOutput::ParallelIon; - default:; - } - MOZ_ASSUME_UNREACHABLE("No such execution mode"); -} - } // namespace jit } // namespace js diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 84fac4dfc7c1..793e03cb2203 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -518,11 +518,8 @@ jit::FinishOffThreadBuilder(IonBuilder *builder) ExecutionMode executionMode = builder->info().executionMode(); // Clean up if compilation did not succeed. - if (CompilingOffThread(builder->script(), executionMode)) { - types::TypeCompartment &types = builder->script()->compartment()->types; - builder->recompileInfo.compilerOutput(types)->invalidate(); + if (CompilingOffThread(builder->script(), executionMode)) SetIonScript(builder->script(), executionMode, nullptr); - } // The builder is allocated into its LifoAlloc, so destroying that will // destroy the builder and all other data accumulated during compilation, @@ -748,7 +745,8 @@ IonScript::IonScript() static const int DataAlignment = sizeof(void *); IonScript * -IonScript::New(JSContext *cx, uint32_t frameSlots, uint32_t frameSize, size_t snapshotsSize, +IonScript::New(JSContext *cx, types::RecompileInfo recompileInfo, + uint32_t frameSlots, uint32_t frameSize, size_t snapshotsSize, size_t bailoutEntries, size_t constants, size_t safepointIndices, size_t osiIndices, size_t cacheEntries, size_t runtimeSize, size_t safepointsSize, size_t callTargetEntries, size_t backedgeEntries) @@ -835,7 +833,7 @@ IonScript::New(JSContext *cx, uint32_t frameSlots, uint32_t frameSize, size_t sn script->frameSlots_ = frameSlots; script->frameSize_ = frameSize; - script->recompileInfo_ = cx->compartment()->types.compiledInfo; + script->recompileInfo_ = recompileInfo; return script; } @@ -1510,17 +1508,13 @@ AttachFinishedCompilations(JSContext *cx) types::AutoEnterAnalysis enterTypes(cx); - ExecutionMode executionMode = builder->info().executionMode(); - types::AutoEnterCompilation enterCompiler(cx, CompilerOutputKind(executionMode)); - enterCompiler.initExisting(builder->recompileInfo); - bool success; { // Release the worker thread lock and root the compiler for GC. AutoTempAllocatorRooter root(cx, &builder->temp()); AutoUnlockWorkerThreadState unlock(cx->runtime()); AutoFlushCache afc("AttachFinishedCompilations", cx->runtime()->ionRuntime()); - success = codegen->link(); + success = codegen->link(cx, builder->constraints()); } if (!success) { @@ -1601,13 +1595,11 @@ IonCompile(JSContext *cx, JSScript *script, AutoFlushCache afc("IonCompile", cx->runtime()->ionRuntime()); - types::AutoEnterCompilation enterCompiler(cx, CompilerOutputKind(executionMode)); - if (!enterCompiler.init(script)) - return AbortReason_Disable; - AutoTempAllocatorRooter root(cx, temp); + types::CompilerConstraintList *constraints = alloc->new_<types::CompilerConstraintList>(); - IonBuilder *builder = alloc->new_<IonBuilder>(cx, temp, graph, &inspector, info, baselineFrame); + IonBuilder *builder = alloc->new_<IonBuilder>(cx, temp, graph, constraints, + &inspector, info, baselineFrame); if (!builder) return AbortReason_Alloc; @@ -1652,7 +1644,7 @@ IonCompile(JSContext *cx, JSScript *script, return AbortReason_Disable; } - bool success = codegen->link(); + bool success = codegen->link(cx, builder->constraints()); IonSpewEndFunction(); @@ -2343,25 +2335,27 @@ jit::Invalidate(types::TypeCompartment &types, FreeOp *fop, // Add an invalidation reference to all invalidated IonScripts to indicate // to the traversal which frames have been invalidated. - bool anyInvalidation = false; + size_t numInvalidations = 0; for (size_t i = 0; i < invalid.length(); i++) { const types::CompilerOutput &co = *invalid[i].compilerOutput(types); - switch (co.kind()) { - case types::CompilerOutput::Ion: - case types::CompilerOutput::ParallelIon: - JS_ASSERT(co.isValid()); - IonSpew(IonSpew_Invalidate, " Invalidate %s:%u, IonScript %p", - co.script->filename(), co.script->lineno, co.ion()); + JS_ASSERT(co.isValid()); - // Keep the ion script alive during the invalidation and flag this - // ionScript as being invalidated. This increment is removed by the - // loop after the calls to InvalidateActivation. - co.ion()->incref(); - anyInvalidation = true; - } + CancelOffThreadIonCompile(co.script()->compartment(), co.script()); + + if (!co.ion()) + continue; + + IonSpew(IonSpew_Invalidate, " Invalidate %s:%u, IonScript %p", + co.script()->filename(), co.script()->lineno, co.ion()); + + // Keep the ion script alive during the invalidation and flag this + // ionScript as being invalidated. This increment is removed by the + // loop after the calls to InvalidateActivation. + co.ion()->incref(); + numInvalidations++; } - if (!anyInvalidation) { + if (!numInvalidations) { IonSpew(IonSpew_Invalidate, " No IonScript invalidation."); return; } @@ -2374,22 +2368,18 @@ jit::Invalidate(types::TypeCompartment &types, FreeOp *fop, // until its last invalidated frame is destroyed. for (size_t i = 0; i < invalid.length(); i++) { types::CompilerOutput &co = *invalid[i].compilerOutput(types); - ExecutionMode executionMode = SequentialExecution; - switch (co.kind()) { - case types::CompilerOutput::Ion: - break; - case types::CompilerOutput::ParallelIon: - executionMode = ParallelExecution; - break; - } JS_ASSERT(co.isValid()); - JSScript *script = co.script; - IonScript *ionScript = GetIonScript(script, executionMode); + ExecutionMode executionMode = co.mode(); + JSScript *script = co.script(); + IonScript *ionScript = co.ion(); + if (!ionScript) + continue; SetIonScript(script, executionMode, nullptr); ionScript->detachDependentAsmJSModules(fop); ionScript->decref(fop); co.invalidate(); + numInvalidations--; // Wait for the scripts to get warm again before doing another // compile, unless either: @@ -2405,6 +2395,10 @@ jit::Invalidate(types::TypeCompartment &types, FreeOp *fop, if (resetUses && executionMode != ParallelExecution) script->resetUseCount(); } + + // Make sure we didn't leak references by invalidating the same IonScript + // multiple times in the above loop. + JS_ASSERT(!numInvalidations); } void diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index 4175e4043725..2a186017f6e6 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -1907,7 +1907,7 @@ AnalyzePoppedThis(JSContext *cx, types::TypeObject *type, MDefinition *thisValue, MInstruction *ins, bool definitelyExecuted, HandleObject baseobj, Vector<types::TypeNewScript::Initializer> *initializerList, - Vector<jsid> *accessedProperties, + Vector<PropertyName *> *accessedProperties, bool *phandled) { // Determine the effect that a use of the |this| value when calling |new| @@ -1922,17 +1922,15 @@ AnalyzePoppedThis(JSContext *cx, types::TypeObject *type, // Don't use GetAtomId here, we need to watch for SETPROP on // integer properties and bail out. We can't mark the aggregate // JSID_VOID type property as being in a definite slot. - RootedId id(cx, NameToId(setprop->name())); - if (types::IdToTypeId(id) != id || - id == NameToId(cx->names().classPrototype) || - id == NameToId(cx->names().proto) || - id == NameToId(cx->names().constructor)) + if (setprop->name() == cx->names().classPrototype || + setprop->name() == cx->names().proto || + setprop->name() == cx->names().constructor) { return true; } // Ignore assignments to properties that were already written to. - if (baseobj->nativeLookup(cx, id)) { + if (baseobj->nativeLookup(cx, NameToId(setprop->name()))) { *phandled = true; return true; } @@ -1940,7 +1938,7 @@ AnalyzePoppedThis(JSContext *cx, types::TypeObject *type, // Don't add definite properties for properties that were already // read in the constructor. for (size_t i = 0; i < accessedProperties->length(); i++) { - if ((*accessedProperties)[i] == id) + if ((*accessedProperties)[i] == setprop->name()) return true; } @@ -1953,13 +1951,14 @@ AnalyzePoppedThis(JSContext *cx, types::TypeObject *type, if (!definitelyExecuted) return true; - if (!types::AddClearDefiniteGetterSetterForPrototypeChain(cx, type, id)) { + if (!types::AddClearDefiniteGetterSetterForPrototypeChain(cx, type, NameToId(setprop->name()))) { // The prototype chain already contains a getter/setter for this // property, or type information is too imprecise. return true; } DebugOnly<unsigned> slotSpan = baseobj->slotSpan(); + RootedId id(cx, NameToId(setprop->name())); RootedValue value(cx, UndefinedValue()); if (!DefineNativeProperty(cx, baseobj, id, value, nullptr, nullptr, JSPROP_ENUMERATE, 0, 0, DNP_SKIP_TYPE)) @@ -2015,9 +2014,7 @@ AnalyzePoppedThis(JSContext *cx, types::TypeObject *type, * definite property before it is assigned could incorrectly hit. */ RootedId id(cx, NameToId(get->name())); - if (types::IdToTypeId(id) != id) - return true; - if (!baseobj->nativeLookup(cx, id) && !accessedProperties->append(id.get())) + if (!baseobj->nativeLookup(cx, id) && !accessedProperties->append(get->name())) return false; if (!types::AddClearDefiniteGetterSetterForPrototypeChain(cx, type, id)) { @@ -2066,7 +2063,7 @@ jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun, types::TypeScript::SetThis(cx, fun->nonLazyScript(), types::Type::ObjectType(type)); - Vector<jsid> accessedProperties(cx); + Vector<PropertyName *> accessedProperties(cx); LifoAlloc alloc(types::TypeZone::TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE); @@ -2085,8 +2082,10 @@ jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun, AutoTempAllocatorRooter root(cx, &temp); + types::CompilerConstraintList constraints; BaselineInspector inspector(cx, fun->nonLazyScript()); - IonBuilder builder(cx, &temp, &graph, &inspector, &info, /* baselineFrame = */ nullptr); + IonBuilder builder(cx, &temp, &graph, &constraints, + &inspector, &info, /* baselineFrame = */ nullptr); if (!builder.build()) { if (builder.abortReason() == AbortReason_Alloc) diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 51c373d3af27..9ad69d6c55b3 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -34,16 +34,18 @@ using namespace js; using namespace js::jit; using mozilla::DebugOnly; +using mozilla::Maybe; IonBuilder::IonBuilder(JSContext *cx, TempAllocator *temp, MIRGraph *graph, + types::CompilerConstraintList *constraints, BaselineInspector *inspector, CompileInfo *info, BaselineFrame *baselineFrame, size_t inliningDepth, uint32_t loopDepth) : MIRGenerator(cx->compartment(), temp, graph, info), backgroundCodegen_(nullptr), - recompileInfo(cx->compartment()->types.compiledInfo), cx(cx), baselineFrame_(baselineFrame), abortReason_(AbortReason_Disable), + constraints_(constraints), analysis_(info->script()), loopDepth_(loopDepth), callerResumePoint_(nullptr), @@ -249,8 +251,8 @@ IonBuilder::canEnterInlinedFunction(JSFunction *target) if (!targetScript->compileAndGo) return false; - types::TypeObject *targetType = target->getType(cx); - if (!targetType || targetType->unknownProperties()) + types::TypeObjectKey *targetType = types::TypeObjectKey::get(target); + if (targetType->unknownProperties()) return false; return true; @@ -3762,7 +3764,7 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target) // Build the graph. JS_ASSERT(!cx->isExceptionPending()); - IonBuilder inlineBuilder(cx, &temp(), &graph(), &inspector, info, nullptr, + IonBuilder inlineBuilder(cx, &temp(), &graph(), constraints(), &inspector, info, nullptr, inliningDepth_ + 1, loopDepth_); if (!inlineBuilder.buildInline(this, outerResumePoint, callInfo)) { if (cx->isExceptionPending()) { @@ -3956,12 +3958,9 @@ IonBuilder::makeInliningDecision(JSFunction *target, CallInfo &callInfo) } } - JS_ASSERT(!target->hasLazyType()); - types::TypeObject *targetType = target->getType(cx); - JS_ASSERT(targetType && !targetType->unknownProperties()); - // TI calls ObjectStateChange to trigger invalidation of the caller. - types::HeapTypeSet::WatchObjectStateChange(cx, targetType); + types::TypeObjectKey *targetType = types::TypeObjectKey::get(target); + targetType->watchStateChange(constraints()); return true; } @@ -4607,16 +4606,14 @@ IonBuilder::getSingletonPrototype(JSFunction *target) { if (!target || !target->hasSingletonType()) return nullptr; - types::TypeObject *targetType = target->getType(cx); + types::TypeObjectKey *targetType = types::TypeObjectKey::get(target); if (targetType->unknownProperties()) return nullptr; jsid protoid = NameToId(cx->names().classPrototype); - types::HeapTypeSet *protoTypes = targetType->getProperty(cx, protoid); - if (!protoTypes) - return nullptr; + types::HeapTypeSetKey protoProperty = targetType->property(protoid); - return protoTypes->getSingleton(cx); + return protoProperty.singleton(constraints()); } MDefinition * @@ -4644,8 +4641,9 @@ IonBuilder::createThisScriptedSingleton(JSFunction *target, MDefinition *callee) return nullptr; // Trigger recompilation if the templateObject changes. - if (templateObject->type()->hasNewScript()) - types::HeapTypeSet::WatchObjectStateChange(cx, templateObject->type()); + types::TypeObjectKey *templateType = types::TypeObjectKey::get(templateObject); + if (templateType->newScript()) + templateType->watchStateChange(constraints()); MCreateThisWithTemplate *createThis = MCreateThisWithTemplate::New(templateObject); current->add(createThis); @@ -4996,20 +4994,11 @@ TestShouldDOMCall(JSContext *cx, types::TypeSet *inTypes, JSFunction *func, return false; for (unsigned i = 0; i < inTypes->getObjectCount(); i++) { - types::TypeObject *curType = inTypes->getTypeObject(i); + types::TypeObjectKey *curType = inTypes->getObject(i); + if (!curType) + continue; - if (!curType) { - JSObject *curObj = inTypes->getSingleObject(i); - - if (!curObj) - continue; - - curType = curObj->getType(cx); - if (!curType) - return false; - } - - RootedObject protoRoot(cx, curType->proto); + RootedObject protoRoot(cx, curType->proto().toObjectOrNull()); if (!instanceChecker(protoRoot, jinfo->protoID, jinfo->depth)) return false; } @@ -5026,24 +5015,13 @@ TestAreKnownDOMTypes(JSContext *cx, types::TypeSet *inTypes) // First iterate to make sure they all are DOM objects, then freeze all of // them as such if they are. for (unsigned i = 0; i < inTypes->getObjectCount(); i++) { - types::TypeObject *curType = inTypes->getTypeObject(i); - - if (!curType) { - JSObject *curObj = inTypes->getSingleObject(i); - - // Skip holes in TypeSets. - if (!curObj) - continue; - - curType = curObj->getType(cx); - if (!curType) - return false; - } + types::TypeObjectKey *curType = inTypes->getObject(i); + if (!curType) + continue; if (curType->unknownProperties()) return false; - - if (!(curType->clasp->flags & JSCLASS_IS_DOMJSCLASS)) + if (!(curType->clasp()->flags & JSCLASS_IS_DOMJSCLASS)) return false; } @@ -5388,7 +5366,7 @@ IonBuilder::jsop_newarray(uint32_t count) } types::TemporaryTypeSet::DoubleConversion conversion = - bytecodeTypes(pc)->convertDoubleElements(cx); + bytecodeTypes(pc)->convertDoubleElements(constraints()); if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles) templateObject->setShouldConvertDoubleElements(); @@ -5463,16 +5441,14 @@ IonBuilder::jsop_initelem_array() // intializer, and that arrays are marked as non-packed when writing holes // to them during initialization. bool needStub = false; - types::TypeObject *initializer = obj->resultTypeSet()->getTypeObject(0); + types::TypeObjectKey *initializer = obj->resultTypeSet()->getObject(0); if (value->isConstant() && value->toConstant()->value().isMagic(JS_ELEMENTS_HOLE)) { - if (!(initializer->flags & types::OBJECT_FLAG_NON_PACKED)) + if (!initializer->hasFlags(constraints(), types::OBJECT_FLAG_NON_PACKED)) needStub = true; } else if (!initializer->unknownProperties()) { - types::HeapTypeSet *elemTypes = initializer->getProperty(cx, JSID_VOID); - if (!elemTypes) - return false; - if (!TypeSetIncludes(elemTypes, value->type(), value->resultTypeSet())) { - elemTypes->addFreeze(cx); + types::HeapTypeSetKey elemTypes = initializer->property(JSID_VOID); + if (!TypeSetIncludes(elemTypes.actualTypes, value->type(), value->resultTypeSet())) { + elemTypes.freeze(constraints()); needStub = true; } } @@ -5514,14 +5490,14 @@ IonBuilder::jsop_initelem_array() } static bool -CanEffectlesslyCallLookupGenericOnObject(JSContext *cx, JSObject *obj, jsid id) +CanEffectlesslyCallLookupGenericOnObject(JSContext *cx, JSObject *obj, PropertyName *name) { while (obj) { if (!obj->isNative()) return false; if (obj->getClass()->ops.lookupGeneric) return false; - if (obj->nativeLookup(cx, id)) + if (obj->nativeLookup(cx, NameToId(name))) return true; if (obj->getClass()->resolve != JS_ResolveStub && obj->getClass()->resolve != (JSResolveOp)fun_resolve) @@ -5539,12 +5515,12 @@ IonBuilder::jsop_initprop(PropertyName *name) RootedObject templateObject(cx, obj->toNewObject()->templateObject()); - RootedId id(cx, NameToId(name)); - if (!CanEffectlesslyCallLookupGenericOnObject(cx, templateObject, id)) + if (!CanEffectlesslyCallLookupGenericOnObject(cx, templateObject, name)) return abort("INITPROP template object is special"); RootedObject holder(cx); RootedShape shape(cx); + RootedId id(cx, NameToId(name)); bool res = LookupPropertyWithFlags(cx, templateObject, id, 0, &holder, &shape); if (!res) @@ -5557,13 +5533,9 @@ IonBuilder::jsop_initprop(PropertyName *name) return resumeAfter(init); } - bool writeNeedsBarrier = false; - if (!PropertyWriteNeedsTypeBarrier(cx, current, &obj, name, &value, /* canModify = */ true, - &writeNeedsBarrier)) + if (PropertyWriteNeedsTypeBarrier(constraints(), current, + &obj, name, &value, /* canModify = */ true)) { - return false; - } - if (writeNeedsBarrier) { // JSOP_NEWINIT becomes an MNewObject without preconfigured properties. MInitProp *init = MInitProp::New(obj, name, value); current->add(init); @@ -5574,9 +5546,8 @@ IonBuilder::jsop_initprop(PropertyName *name) current->add(MPostWriteBarrier::New(obj, value)); bool needsBarrier = true; - if ((id == types::IdToTypeId(id)) && - obj->resultTypeSet() && - !obj->resultTypeSet()->propertyNeedsBarrier(cx, id)) + if (obj->resultTypeSet() && + !obj->resultTypeSet()->propertyNeedsBarrier(constraints(), id)) { needsBarrier = false; } @@ -5977,9 +5948,9 @@ IonBuilder::maybeInsertResume() return resumeAfter(ins); } -static inline bool -TestSingletonProperty(JSContext *cx, JSObject *obj, JSObject *singleton, - jsid id, bool *isKnownConstant) +bool +IonBuilder::testSingletonProperty(JSObject *obj, JSObject *singleton, + PropertyName *name, bool *isKnownConstant) { // We would like to completely no-op property/global accesses which can // produce only a particular JSObject. When indicating the access result is @@ -5996,15 +5967,11 @@ TestSingletonProperty(JSContext *cx, JSObject *obj, JSObject *singleton, *isKnownConstant = false; - if (id != types::IdToTypeId(id)) - return true; - - if (!CanEffectlesslyCallLookupGenericOnObject(cx, obj, id)) + if (!CanEffectlesslyCallLookupGenericOnObject(cx, obj, name)) return true; RootedObject objRoot(cx, obj); - RootedId idRoot(cx, id); - + RootedId idRoot(cx, NameToId(name)); RootedObject holder(cx); RootedShape shape(cx); if (!JSObject::lookupGeneric(cx, objRoot, idRoot, &holder, &shape)) @@ -6023,21 +5990,16 @@ TestSingletonProperty(JSContext *cx, JSObject *obj, JSObject *singleton, // before |holder|, and that |holder| only has the result object for its // property. while (true) { - types::TypeObject *objType = obj->getType(cx); - if (!objType) - return false; + types::TypeObjectKey *objType = types::TypeObjectKey::get(obj); if (objType->unknownProperties()) return true; - types::HeapTypeSet *property = objType->getProperty(cx, id); - if (!property) - return false; + types::HeapTypeSetKey property = objType->property(idRoot); if (obj != holder) { - if (!property->empty()) + if (property.notEmpty(constraints())) return true; - property->addFreeze(cx); } else { - if (property->getSingleton(cx) != singleton) + if (property.singleton(constraints()) != singleton) return true; break; } @@ -6049,11 +6011,11 @@ TestSingletonProperty(JSContext *cx, JSObject *obj, JSObject *singleton, return true; } -static inline bool -TestSingletonPropertyTypes(JSContext *cx, MDefinition *obj, JSObject *singleton, - JSObject *globalObj, jsid id, - bool *isKnownConstant, bool *testObject, - bool *testString) +bool +IonBuilder::testSingletonPropertyTypes(MDefinition *obj, JSObject *singleton, + JSObject *globalObj, PropertyName *name, + bool *isKnownConstant, bool *testObject, + bool *testString) { // As for TestSingletonProperty, but the input is any value in a type set // rather than a specific object. If testObject is set then the constant @@ -6071,12 +6033,9 @@ TestSingletonPropertyTypes(JSContext *cx, MDefinition *obj, JSObject *singleton, if (types && types->unknownObject()) return true; - if (id != types::IdToTypeId(id)) - return true; - JSObject *objectSingleton = types ? types->getSingleton() : nullptr; if (objectSingleton) - return TestSingletonProperty(cx, objectSingleton, singleton, id, isKnownConstant); + return testSingletonProperty(objectSingleton, singleton, name, isKnownConstant); if (!globalObj) return true; @@ -6111,32 +6070,20 @@ TestSingletonPropertyTypes(JSContext *cx, MDefinition *obj, JSObject *singleton, // find a prototype common to all the objects; if that prototype // has the singleton property, the access will not be on a missing property. for (unsigned i = 0; i < types->getObjectCount(); i++) { - types::TypeObject *object = types->getTypeObject(i); - if (!object) { - // Try to get it through the singleton. - JSObject *curObj = types->getSingleObject(i); - // As per the comment in jsinfer.h, there can be holes in - // TypeSets, so just skip over them. - if (!curObj) - continue; - object = curObj->getType(cx); - if (!object) - return false; - } + types::TypeObjectKey *object = types->getObject(i); + if (!object) + continue; if (object->unknownProperties()) return true; - types::HeapTypeSet *property = object->getProperty(cx, id); - if (!property) - return false; - if (!property->empty()) + types::HeapTypeSetKey property = object->property(NameToId(name)); + if (property.notEmpty(constraints())) return true; - property->addFreeze(cx); - if (object->proto) { + if (JSObject *proto = object->proto().toObjectOrNull()) { // Test this type. bool thoughtConstant = false; - if (!TestSingletonProperty(cx, object->proto, singleton, id, &thoughtConstant)) + if (!testSingletonProperty(proto, singleton, name, &thoughtConstant)) return false; if (!thoughtConstant) return true; @@ -6158,7 +6105,7 @@ TestSingletonPropertyTypes(JSContext *cx, MDefinition *obj, JSObject *singleton, if (!js_GetClassPrototype(cx, key, &proto, nullptr)) return false; - return TestSingletonProperty(cx, proto, singleton, id, isKnownConstant); + return testSingletonProperty(proto, singleton, name, isKnownConstant); } // Given an observed type set, annotates the IR as much as possible: @@ -6263,29 +6210,21 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc return true; } - types::TypeObject *staticType = staticObject->getType(cx); - if (!staticType) - return false; - types::HeapTypeSet *propertyTypes = nullptr; + types::TypeObjectKey *staticType = types::TypeObjectKey::get(staticObject); + Maybe<types::HeapTypeSetKey> propertyTypes; if (!staticType->unknownProperties()) { - propertyTypes = staticType->getProperty(cx, id); - if (!propertyTypes) - return false; - } - if (propertyTypes && propertyTypes->isConfiguredProperty(cx, staticType)) { - // The property has been reconfigured as non-configurable, non-enumerable - // or non-writable. - *psucceeded = false; - return true; + propertyTypes.construct(staticType->property(id)); + if (propertyTypes.ref().configured(constraints(), staticType)) { + // The property has been reconfigured as non-configurable, non-enumerable + // or non-writable. + *psucceeded = false; + return true; + } } types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc); - bool barrier; - if (!PropertyReadNeedsTypeBarrier(cx, staticType, name, baseTypes, /* updateObserved = */ true, - &barrier)) - { - return false; - } + bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(), staticType, + name, baseTypes, /* updateObserved = */ true); types::TemporaryTypeSet *types = cloneTypeSet(baseTypes); // If the property is permanent, a shape guard isn't necessary. @@ -6297,7 +6236,7 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc if (singleton) { // Try to inline a known constant value. bool isKnownConstant; - if (!TestSingletonProperty(cx, staticObject, singleton, id, &isKnownConstant)) + if (!testSingletonProperty(staticObject, singleton, name, &isKnownConstant)) return false; if (isKnownConstant) return pushConstant(ObjectValue(*singleton)); @@ -6311,9 +6250,9 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc MInstruction *obj = MConstant::New(ObjectValue(*staticObject)); current->add(obj); - // If we have a property typeset, the isOwnProperty call will trigger recompilation if - // the property is deleted or reconfigured. - if (!propertyTypes && shape->configurable()) + // If we have a property typeset, the HeapTypeSetIsConfigured call will + // trigger recompilation if the property is deleted or reconfigured. + if (propertyTypes.empty() && shape->configurable()) obj = addShapeGuard(obj, staticObject->lastProperty(), Bailout_ShapeGuard); MIRType rvalType = MIRTypeFromValueType(types->getKnownTypeTag()); @@ -6374,21 +6313,18 @@ IonBuilder::setStaticName(JSObject *staticObject, PropertyName *name) if (!shape || !shape->hasDefaultSetter() || !shape->writable() || !shape->hasSlot()) return jsop_setprop(name); - types::TypeObject *staticType = staticObject->getType(cx); - if (!staticType) - return false; - types::HeapTypeSet *propertyTypes = nullptr; - if (!staticType->unknownProperties()) { - propertyTypes = staticType->getProperty(cx, id); - if (!propertyTypes) - return false; - } - if (!propertyTypes || propertyTypes->isConfiguredProperty(cx, staticType)) { + types::TypeObjectKey *staticType = types::TypeObjectKey::get(staticObject); + if (staticType->unknownProperties()) + return jsop_setprop(name); + + types::HeapTypeSetKey propertyTypes = staticType->property(id); + if (propertyTypes.configured(constraints(), staticType)) { // The property has been reconfigured as non-configurable, non-enumerable // or non-writable. return jsop_setprop(name); } - if (!TypeSetIncludes(propertyTypes, value->type(), value->resultTypeSet())) + + if (!TypeSetIncludes(propertyTypes.actualTypes, value->type(), value->resultTypeSet())) return jsop_setprop(name); current->pop(); @@ -6397,12 +6333,6 @@ IonBuilder::setStaticName(JSObject *staticObject, PropertyName *name) MDefinition *obj = current->pop(); JS_ASSERT(&obj->toConstant()->value().toObject() == staticObject); - // If we have a property type set, the isOwnProperty call will trigger recompilation - // if the property is deleted or reconfigured. Without TI, we always need a shape guard - // to guard against the property being reconfigured as non-writable. - if (!propertyTypes) - obj = addShapeGuard(obj, staticObject->lastProperty(), Bailout_ShapeGuard); - if (NeedsPostBarrier(info(), value)) current->add(MPostWriteBarrier::New(obj, value)); @@ -6411,13 +6341,13 @@ IonBuilder::setStaticName(JSObject *staticObject, PropertyName *name) // |undefined| value; if |undefined| is assigned at a later point, it will be added // to the type set. MIRType slotType = MIRType_None; - if (propertyTypes && !staticObject->getSlot(shape->slot()).isUndefined()) { - JSValueType knownType = propertyTypes->getKnownTypeTag(cx); + if (!staticObject->getSlot(shape->slot()).isUndefined()) { + JSValueType knownType = propertyTypes.knownTypeTag(constraints()); if (knownType != JSVAL_TYPE_UNKNOWN) slotType = MIRTypeFromValueType(knownType); } - bool needsBarrier = !propertyTypes || propertyTypes->needsBarrier(cx); + bool needsBarrier = propertyTypes.needsBarrier(constraints()); return storeSlot(obj, shape, value, needsBarrier, slotType); } @@ -6575,7 +6505,7 @@ IonBuilder::getElemTryDense(bool *emitted, MDefinition *obj, MDefinition *index) // Don't generate a fast path if there have been bounds check failures // and this access might be on a sparse property. - if (ElementAccessHasExtraIndexedProperty(cx, obj) && failedBoundsCheck_) + if (ElementAccessHasExtraIndexedProperty(constraints(), obj) && failedBoundsCheck_) return true; // Don't generate a fast path if this pc has seen negative indexes accessed, @@ -6603,7 +6533,7 @@ IonBuilder::getElemTryTypedStatic(bool *emitted, MDefinition *obj, MDefinition * if (!LIRGenerator::allowStaticTypedArrayAccesses()) return true; - if (ElementAccessHasExtraIndexedProperty(cx, obj)) + if (ElementAccessHasExtraIndexedProperty(constraints(), obj)) return true; if (!obj->resultTypeSet()) @@ -6803,9 +6733,7 @@ IonBuilder::getElemTryCache(bool *emitted, MDefinition *obj, MDefinition *index) // Emit GetElementCache. types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc); - bool barrier; - if (!PropertyReadNeedsTypeBarrier(cx, obj, nullptr, baseTypes, &barrier)) - return false; + bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(), obj, nullptr, baseTypes); types::TemporaryTypeSet *types = cloneTypeSet(baseTypes); // Always add a barrier if the index might be a string, so that the cache @@ -6827,7 +6755,7 @@ IonBuilder::getElemTryCache(bool *emitted, MDefinition *obj, MDefinition *index) // Spice up type information. if (index->type() == MIRType_Int32 && !barrier) { - bool needHoleCheck = !ElementAccessIsPacked(cx, obj); + bool needHoleCheck = !ElementAccessIsPacked(constraints(), obj); JSValueType knownType = GetElemKnownType(needHoleCheck, types); if (knownType != JSVAL_TYPE_UNKNOWN && knownType != JSVAL_TYPE_DOUBLE) @@ -6854,19 +6782,17 @@ IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index) return false; } - bool barrier; - if (!PropertyReadNeedsTypeBarrier(cx, obj, nullptr, baseTypes, &barrier)) - return false; + bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(), obj, nullptr, baseTypes); types::TemporaryTypeSet *types = cloneTypeSet(baseTypes); - bool needsHoleCheck = !ElementAccessIsPacked(cx, obj); + bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj); // Reads which are on holes in the object do not have to bail out if // undefined values have been observed at this access site and the access // cannot hit another indexed property on the object or its prototypes. bool readOutOfBounds = types->hasType(types::Type::UndefinedType()) && - !ElementAccessHasExtraIndexedProperty(cx, obj); + !ElementAccessHasExtraIndexedProperty(constraints(), obj); JSValueType knownType = JSVAL_TYPE_UNKNOWN; if (!barrier) @@ -6903,7 +6829,7 @@ IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index) !needsHoleCheck && knownType == JSVAL_TYPE_DOUBLE && objTypes && - objTypes->convertDoubleElements(cx) == types::TemporaryTypeSet::AlwaysConvertToDoubles; + objTypes->convertDoubleElements(constraints()) == types::TemporaryTypeSet::AlwaysConvertToDoubles; if (loadDouble) elements = addConvertElementsToDoubles(elements); @@ -6959,7 +6885,8 @@ IonBuilder::getTypedArrayElements(MDefinition *obj) // The 'data' pointer can change in rare circumstances // (ArrayBufferObject::changeContents). - types::HeapTypeSet::WatchObjectStateChange(cx, tarr->getType(cx)); + types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarr); + tarrType->watchStateChange(constraints()); obj->setFoldedUnchecked(); return MConstantElements::New(data); @@ -7162,7 +7089,7 @@ IonBuilder::setElemTryTypedStatic(bool *emitted, MDefinition *object, if (!LIRGenerator::allowStaticTypedArrayAccesses()) return true; - if (ElementAccessHasExtraIndexedProperty(cx, object)) + if (ElementAccessHasExtraIndexedProperty(constraints(), object)) return true; if (!object->resultTypeSet()) @@ -7226,19 +7153,16 @@ IonBuilder::setElemTryDense(bool *emitted, MDefinition *object, if (!ElementAccessIsDenseNative(object, index)) return true; - bool needsBarrier; - if (!PropertyWriteNeedsTypeBarrier(cx, current, &object, nullptr, &value, - /* canModify = */ true, &needsBarrier)) + if (PropertyWriteNeedsTypeBarrier(constraints(), current, + &object, nullptr, &value, /* canModify = */ true)) { - return false; - } - if (needsBarrier) return true; + } if (!object->resultTypeSet()) return true; types::TemporaryTypeSet::DoubleConversion conversion = - object->resultTypeSet()->convertDoubleElements(cx); + object->resultTypeSet()->convertDoubleElements(constraints()); // If AmbiguousDoubleConversion, only handle int32 values for now. if (conversion == types::TemporaryTypeSet::AmbiguousDoubleConversion && @@ -7249,7 +7173,7 @@ IonBuilder::setElemTryDense(bool *emitted, MDefinition *object, // Don't generate a fast path if there have been bounds check failures // and this access might be on a sparse property. - if (ElementAccessHasExtraIndexedProperty(cx, object) && failedBoundsCheck_) + if (ElementAccessHasExtraIndexedProperty(constraints(), object) && failedBoundsCheck_) return true; // Emit dense setelem variant. @@ -7292,15 +7216,11 @@ IonBuilder::setElemTryCache(bool *emitted, MDefinition *object, if (!icInspect.sawDenseWrite() && !icInspect.sawTypedArrayWrite()) return true; - bool needsBarrier; - if (!PropertyWriteNeedsTypeBarrier(cx, current, &object, nullptr, &value, - /* canModify = */ true, &needsBarrier)) + if (PropertyWriteNeedsTypeBarrier(constraints(), current, + &object, nullptr, &value, /* canModify = */ true)) { - return false; - } - - if (needsBarrier) return true; + } // Emit SetElementCache. MInstruction *ins = MSetElementCache::New(object, index, value, script()->strict); @@ -7319,15 +7239,12 @@ IonBuilder::jsop_setelem_dense(types::TemporaryTypeSet::DoubleConversion convers SetElemSafety safety, MDefinition *obj, MDefinition *id, MDefinition *value) { - MIRType elementType; - if (!DenseNativeElementType(cx, obj, &elementType)) - return false; - - bool packed = ElementAccessIsPacked(cx, obj); + MIRType elementType = DenseNativeElementType(constraints(), obj); + bool packed = ElementAccessIsPacked(constraints(), obj); // Writes which are on holes in the object do not have to bail out if they // cannot hit another indexed property on the object or its prototypes. - bool writeOutOfBounds = !ElementAccessHasExtraIndexedProperty(cx, obj); + bool writeOutOfBounds = !ElementAccessHasExtraIndexedProperty(constraints(), obj); if (NeedsPostBarrier(info(), value)) current->add(MPostWriteBarrier::New(obj, value)); @@ -7416,7 +7333,7 @@ IonBuilder::jsop_setelem_dense(types::TemporaryTypeSet::DoubleConversion convers } // Determine whether a write barrier is required. - if (obj->resultTypeSet()->propertyNeedsBarrier(cx, JSID_VOID)) + if (obj->resultTypeSet()->propertyNeedsBarrier(constraints(), JSID_VOID)) store->setNeedsBarrier(); if (elementType != MIRType_None && packed) @@ -7522,7 +7439,7 @@ IonBuilder::jsop_length_fastPath() if (objTypes && objTypes->getKnownClass() == &ArrayObject::class_ && - !objTypes->hasObjectFlags(cx, types::OBJECT_FLAG_LENGTH_OVERFLOW)) + !objTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_LENGTH_OVERFLOW)) { current->pop(); MElements *elements = MElements::New(obj); @@ -7645,29 +7562,22 @@ IonBuilder::jsop_rest() return true; } -inline types::HeapTypeSet * -GetDefiniteSlot(JSContext *cx, types::TemporaryTypeSet *types, JSAtom *atom) +bool +IonBuilder::getDefiniteSlot(types::TemporaryTypeSet *types, PropertyName *name, + types::HeapTypeSetKey *property) { if (!types || types->unknownObject() || types->getObjectCount() != 1) - return nullptr; + return false; - types::TypeObject *type = types->getTypeObject(0); - if (!type || type->unknownProperties()) - return nullptr; + types::TypeObjectKey *type = types->getObject(0); + if (type->unknownProperties()) + return false; - jsid id = AtomToId(atom); - if (id != types::IdToTypeId(id)) - return nullptr; + jsid id = NameToId(name); - types::HeapTypeSet *propertyTypes = type->getProperty(cx, id); - if (!propertyTypes || - !propertyTypes->definiteProperty() || - propertyTypes->isConfiguredProperty(cx, type)) - { - return nullptr; - } - - return propertyTypes; + *property = type->property(id); + return property->actualTypes->definiteProperty() && + !property->configured(constraints(), type); } bool @@ -7701,20 +7611,19 @@ TestClassHasAccessorHook(const Class *clasp, bool isGetter) } inline bool -TestTypeHasOwnProperty(JSContext *cx, types::TypeObject *typeObj, jsid id, bool &cont) +TestTypeHasOwnProperty(types::TypeObjectKey *typeObj, PropertyName *name, bool &cont) { cont = true; - types::HeapTypeSet *propSet = typeObj->getProperty(cx, types::IdToTypeId(id)); - if (!propSet) - return false; - if (!propSet->empty()) + types::HeapTypeSetKey propSet = typeObj->property(NameToId(name)); + if (!propSet.actualTypes->empty()) cont = false; // Note: Callers must explicitly freeze the property type set later on if optimizing. return true; } inline bool -TestCommonAccessorProtoChain(JSContext *cx, jsid id, bool isGetter, JSObject *foundProto, +TestCommonAccessorProtoChain(JSContext *cx, PropertyName *name, + bool isGetter, JSObject *foundProto, JSObject *obj, bool &cont) { cont = false; @@ -7732,10 +7641,7 @@ TestCommonAccessorProtoChain(JSContext *cx, jsid id, bool isGetter, JSObject *fo // property, even if there are unknown types higher in the prototype // chain. if (curObj != foundProto) { - types::TypeObject *typeObj = curObj->getType(cx); - if (!typeObj) - return false; - + types::TypeObjectKey *typeObj = types::TypeObjectKey::get(curObj); if (typeObj->unknownProperties()) return true; @@ -7749,7 +7655,7 @@ TestCommonAccessorProtoChain(JSContext *cx, jsid id, bool isGetter, JSObject *fo // prototype chain, we need to fault in the sets anyway, as we need // to freeze on them. bool lcont; - if (!TestTypeHasOwnProperty(cx, typeObj, id, lcont)) + if (!TestTypeHasOwnProperty(typeObj, name, lcont)) return false; if (!lcont) return true; @@ -7762,7 +7668,8 @@ TestCommonAccessorProtoChain(JSContext *cx, jsid id, bool isGetter, JSObject *fo } inline bool -SearchCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, jsid id, bool isGetter, +SearchCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, + PropertyName *name, bool isGetter, JSObject *&found, JSObject *&foundProto, bool &cont) { cont = false; @@ -7771,8 +7678,7 @@ SearchCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, jsid id, boo // Non-Singleton type if (!curObj) { - types::TypeObject *typeObj = types->getTypeObject(i); - + types::TypeObjectKey *typeObj = types->getObject(i); if (!typeObj) continue; @@ -7781,19 +7687,19 @@ SearchCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, jsid id, boo // If the class of the object has a hook, we can't // inline, as we would need to call the hook. - if (TestClassHasAccessorHook(typeObj->clasp, isGetter)) + if (TestClassHasAccessorHook(typeObj->clasp(), isGetter)) return true; // If the type has an own property, we can't be sure we don't shadow // the chain. bool lcont; - if (!TestTypeHasOwnProperty(cx, typeObj, id, lcont)) + if (!TestTypeHasOwnProperty(typeObj, name, lcont)) return false; if (!lcont) return true; // Otherwise try using the prototype. - curObj = typeObj->proto; + curObj = typeObj->proto().toObjectOrNull(); } else { // We can't optimize setters on watched singleton objects. A getter // on an own property can be protected with the prototype @@ -7804,10 +7710,10 @@ SearchCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, jsid id, boo // Turns out that we need to check for a property lookup op, else we // will end up calling it mid-compilation. - if (!CanEffectlesslyCallLookupGenericOnObject(cx, curObj, id)) + if (!CanEffectlesslyCallLookupGenericOnObject(cx, curObj, name)) return true; - RootedId idRoot(cx, id); + RootedId idRoot(cx, NameToId(name)); RootedObject proto(cx); RootedShape shape(cx); if (!JSObject::lookupGeneric(cx, curObj, idRoot, &proto, &shape)) @@ -7847,7 +7753,7 @@ SearchCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, jsid id, boo return true; bool lcont; - if (!TestCommonAccessorProtoChain(cx, id, isGetter, foundProto, curObj, lcont)) + if (!TestCommonAccessorProtoChain(cx, name, isGetter, foundProto, curObj, lcont)) return false; if (!lcont) return true; @@ -7856,51 +7762,39 @@ SearchCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, jsid id, boo return true; } -inline bool -FreezePropTypeSets(JSContext *cx, types::TemporaryTypeSet *types, JSObject *foundProto, jsid id) +bool +IonBuilder::freezePropTypeSets(types::TemporaryTypeSet *types, + JSObject *foundProto, PropertyName *name) { - types::TypeObject *curType; for (unsigned i = 0; i < types->getObjectCount(); i++) { - curType = types->getTypeObject(i); - JSObject *obj = nullptr; - if (!curType) { - obj = types->getSingleObject(i); - if (!obj) - continue; - - curType = obj->getType(cx); - if (!curType) - return false; - } - // If we found a Singleton object's own-property, there's nothing to // freeze. - if (obj != foundProto) { - // Walk the prototype chain. Everyone has to have the property, since we - // just checked, so propSet cannot be nullptr. - jsid typeId = types::IdToTypeId(id); - while (true) { - types::HeapTypeSet *propSet = curType->getProperty(cx, typeId); - // This assert is now assured, since we have faulted them in - // above. - JS_ASSERT(propSet && propSet->empty()); - propSet->addFreeze(cx); - // Don't mark the proto. It will be held down by the shape - // guard. This allows us tp use properties found on prototypes - // with properties unknown to TI. - if (curType->proto == foundProto) - break; - curType = curType->proto->getType(cx); - if (!curType) - return false; - } + if (types->getSingleObject(i) == foundProto) + continue; + + types::TypeObjectKey *type = types->getObject(i); + if (!type) + continue; + + // Walk the prototype chain. Everyone has to have the property, since we + // just checked, so propSet cannot be NULL. + while (true) { + types::HeapTypeSetKey property = type->property(NameToId(name)); + JS_ALWAYS_TRUE(!property.notEmpty(constraints())); + + // Don't mark the proto. It will be held down by the shape + // guard. This allows us to use properties found on prototypes + // with properties unknown to TI. + if (type->proto() == foundProto) + break; + type = types::TypeObjectKey::get(type->proto().toObjectOrNull()); } } return true; } inline bool -IonBuilder::testCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, jsid id, +IonBuilder::testCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, PropertyName *name, JSFunction **funcp, bool isGetter, bool *isDOM, MDefinition **guardOut) { @@ -7917,7 +7811,7 @@ IonBuilder::testCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, js // Iterate down all the types to see if they all have the same getter or // setter. bool cont; - if (!SearchCommonPropFunc(cx, types, id, isGetter, found, foundProto, cont)) + if (!SearchCommonPropFunc(cx, types, name, isGetter, found, foundProto, cont)) return false; if (!cont) return true; @@ -7944,7 +7838,7 @@ IonBuilder::testCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, js // Now we have to freeze all the property typesets to ensure there isn't a // lower shadowing getter or setter installed in the future. - if (!FreezePropTypeSets(cx, types, foundProto, id)) + if (!freezePropTypeSets(types, foundProto, name)) return false; *funcp = &found->as<JSFunction>(); @@ -7957,9 +7851,7 @@ bool IonBuilder::annotateGetPropertyCache(JSContext *cx, MDefinition *obj, MGetPropertyCache *getPropCache, types::TemporaryTypeSet *objTypes, types::TemporaryTypeSet *pushedTypes) { - jsid id = NameToId(getPropCache->name()); - if (id != types::IdToTypeId(id)) - return true; + PropertyName *name = getPropCache->name(); // Ensure every pushed value is a singleton. if (pushedTypes->unknownObject() || pushedTypes->baseFlags() != 0) @@ -7985,30 +7877,25 @@ IonBuilder::annotateGetPropertyCache(JSContext *cx, MDefinition *obj, MGetProper // Ensure that the relevant property typeset for each type object is // is a single-object typeset containing a JSFunction for (unsigned int i = 0; i < objCount; i++) { - types::TypeObject *typeObj = objTypes->getTypeObject(i); - if (!typeObj || typeObj->unknownProperties() || !typeObj->proto) + types::TypeObject *baseTypeObj = objTypes->getTypeObject(i); + if (!baseTypeObj) + continue; + types::TypeObjectKey *typeObj = types::TypeObjectKey::get(baseTypeObj); + if (typeObj->unknownProperties() || !typeObj->proto().isObject()) continue; - types::HeapTypeSet *ownTypes = typeObj->getProperty(cx, id); - if (!ownTypes) + types::HeapTypeSetKey ownTypes = typeObj->property(NameToId(name)); + if (ownTypes.notEmpty(constraints())) continue; - if (!ownTypes->empty()) - continue; - ownTypes->addFreeze(cx); - JSObject *singleton = nullptr; - JSObject *proto = typeObj->proto; + JSObject *proto = typeObj->proto().toObject(); while (true) { - types::TypeObject *protoType = proto->getType(cx); - if (!protoType) - return false; + types::TypeObjectKey *protoType = types::TypeObjectKey::get(proto); if (!protoType->unknownProperties()) { - types::HeapTypeSet *protoTypes = protoType->getProperty(cx, id); - if (!protoTypes) - return false; + types::HeapTypeSetKey property = protoType->property(NameToId(name)); - singleton = protoTypes->getSingleton(cx); + singleton = property.singleton(constraints()); if (singleton) { if (singleton->is<JSFunction>()) break; @@ -8024,14 +7911,14 @@ IonBuilder::annotateGetPropertyCache(JSContext *cx, MDefinition *obj, MGetProper continue; bool knownConstant = false; - if (!TestSingletonProperty(cx, proto, singleton, id, &knownConstant)) + if (!testSingletonProperty(proto, singleton, name, &knownConstant)) return false; // Don't add cases corresponding to non-observed pushes if (!pushedTypes->hasType(types::Type::ObjectType(singleton))) continue; - if (!inlinePropTable->addEntry(typeObj, &singleton->as<JSFunction>())) + if (!inlinePropTable->addEntry(baseTypeObj, &singleton->as<JSFunction>())) return false; } @@ -8138,8 +8025,6 @@ IonBuilder::storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool n bool IonBuilder::jsop_getprop(PropertyName *name) { - jsid id = NameToId(name); - bool emitted = false; // Try to optimize arguments.length. @@ -8147,13 +8032,12 @@ IonBuilder::jsop_getprop(PropertyName *name) return emitted; types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc); - bool barrier; - if (!PropertyReadNeedsTypeBarrier(cx, current->peek(-1), name, baseTypes, &barrier)) - return false; + bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(), + current->peek(-1), name, baseTypes); types::TemporaryTypeSet *types = cloneTypeSet(baseTypes); // Try to hardcode known constants. - if (!getPropTryConstant(&emitted, id, types) || emitted) + if (!getPropTryConstant(&emitted, name, types) || emitted) return emitted; // Except when loading constants above, always use a call if we are doing @@ -8168,7 +8052,7 @@ IonBuilder::jsop_getprop(PropertyName *name) } // Try to emit loads from known binary data blocks - if (!getPropTryTypedObject(&emitted, id, types) || emitted) + if (!getPropTryTypedObject(&emitted, name, types) || emitted) return emitted; // Try to emit loads from definite slots. @@ -8176,15 +8060,15 @@ IonBuilder::jsop_getprop(PropertyName *name) return emitted; // Try to inline a common property getter, or make a call. - if (!getPropTryCommonGetter(&emitted, id, types) || emitted) + if (!getPropTryCommonGetter(&emitted, name, types) || emitted) return emitted; // Try to emit a monomorphic/polymorphic access based on baseline caches. - if (!getPropTryInlineAccess(&emitted, name, id, barrier, types) || emitted) + if (!getPropTryInlineAccess(&emitted, name, barrier, types) || emitted) return emitted; // Try to emit a polymorphic cache. - if (!getPropTryCache(&emitted, name, id, barrier, types) || emitted) + if (!getPropTryCache(&emitted, name, barrier, types) || emitted) return emitted; // Emit a call. @@ -8215,7 +8099,8 @@ IonBuilder::getPropTryArgumentsLength(bool *emitted) } bool -IonBuilder::getPropTryConstant(bool *emitted, jsid id, types::TemporaryTypeSet *types) +IonBuilder::getPropTryConstant(bool *emitted, PropertyName *name, + types::TemporaryTypeSet *types) { JS_ASSERT(*emitted == false); JSObject *singleton = types ? types->getSingleton() : nullptr; @@ -8225,7 +8110,7 @@ IonBuilder::getPropTryConstant(bool *emitted, jsid id, types::TemporaryTypeSet * JSObject *global = &script()->global(); bool isConstant, testObject, testString; - if (!TestSingletonPropertyTypes(cx, current->peek(-1), singleton, global, id, + if (!testSingletonPropertyTypes(current->peek(-1), singleton, global, name, &isConstant, &testObject, &testString)) return false; @@ -8253,14 +8138,13 @@ IonBuilder::getPropTryConstant(bool *emitted, jsid id, types::TemporaryTypeSet * } bool -IonBuilder::getPropTryTypedObject(bool *emitted, - jsid id, +IonBuilder::getPropTryTypedObject(bool *emitted, PropertyName *name, types::TemporaryTypeSet *resultTypes) { TypeRepresentationSet fieldTypeReprs; int32_t fieldOffset; size_t fieldIndex; - if (!lookupTypedObjectField(current->peek(-1), id, &fieldOffset, + if (!lookupTypedObjectField(current->peek(-1), name, &fieldOffset, &fieldTypeReprs, &fieldIndex)) return false; if (fieldTypeReprs.empty()) @@ -8372,8 +8256,8 @@ IonBuilder::getPropTryDefiniteSlot(bool *emitted, PropertyName *name, bool barrier, types::TemporaryTypeSet *types) { JS_ASSERT(*emitted == false); - types::TypeSet *propTypes = GetDefiniteSlot(cx, current->peek(-1)->resultTypeSet(), name); - if (!propTypes) + types::HeapTypeSetKey property; + if (!getDefiniteSlot(current->peek(-1)->resultTypeSet(), name, &property)) return true; MDefinition *obj = current->pop(); @@ -8384,7 +8268,7 @@ IonBuilder::getPropTryDefiniteSlot(bool *emitted, PropertyName *name, useObj = guard; } - MLoadFixedSlot *fixed = MLoadFixedSlot::New(useObj, propTypes->definiteSlot()); + MLoadFixedSlot *fixed = MLoadFixedSlot::New(useObj, property.actualTypes->definiteSlot()); if (!barrier) fixed->setResultType(MIRTypeFromValueType(types->getKnownTypeTag())); @@ -8399,7 +8283,8 @@ IonBuilder::getPropTryDefiniteSlot(bool *emitted, PropertyName *name, } bool -IonBuilder::getPropTryCommonGetter(bool *emitted, jsid id, types::TemporaryTypeSet *types) +IonBuilder::getPropTryCommonGetter(bool *emitted, PropertyName *name, + types::TemporaryTypeSet *types) { JS_ASSERT(*emitted == false); JSFunction *commonGetter; @@ -8408,7 +8293,7 @@ IonBuilder::getPropTryCommonGetter(bool *emitted, jsid id, types::TemporaryTypeS types::TemporaryTypeSet *objTypes = current->peek(-1)->resultTypeSet(); - if (!testCommonPropFunc(cx, objTypes, id, &commonGetter, true, &isDOM, &guard)) + if (!testCommonPropFunc(cx, objTypes, name, &commonGetter, true, &isDOM, &guard)) return false; if (!commonGetter) return true; @@ -8483,7 +8368,7 @@ CanInlinePropertyOpShapes(const BaselineInspector::ShapeVector &shapes) } bool -IonBuilder::getPropTryInlineAccess(bool *emitted, PropertyName *name, jsid id, +IonBuilder::getPropTryInlineAccess(bool *emitted, PropertyName *name, bool barrier, types::TemporaryTypeSet *types) { JS_ASSERT(*emitted == false); @@ -8510,7 +8395,7 @@ IonBuilder::getPropTryInlineAccess(bool *emitted, PropertyName *name, jsid id, Shape *objShape = shapes[0]; obj = addShapeGuard(obj, objShape, Bailout_ShapeGuard); - Shape *shape = objShape->search(cx, id); + Shape *shape = objShape->search(cx, NameToId(name)); JS_ASSERT(shape); if (!loadSlot(obj, shape, rvalType, barrier, types)) @@ -8525,7 +8410,7 @@ IonBuilder::getPropTryInlineAccess(bool *emitted, PropertyName *name, jsid id, for (size_t i = 0; i < shapes.length(); i++) { Shape *objShape = shapes[i]; - Shape *shape = objShape->search(cx, id); + Shape *shape = objShape->search(cx, NameToId(name)); JS_ASSERT(shape); if (!load->addShape(objShape, shape)) return false; @@ -8544,7 +8429,7 @@ IonBuilder::getPropTryInlineAccess(bool *emitted, PropertyName *name, jsid id, } bool -IonBuilder::getPropTryCache(bool *emitted, PropertyName *name, jsid id, +IonBuilder::getPropTryCache(bool *emitted, PropertyName *name, bool barrier, types::TemporaryTypeSet *types) { JS_ASSERT(*emitted == false); @@ -8572,11 +8457,7 @@ IonBuilder::getPropTryCache(bool *emitted, PropertyName *name, jsid id, if (obj->type() == MIRType_Object && !invalidatedIdempotentCache() && info().executionMode() != ParallelExecution) { - bool idempotent; - if (!PropertyReadIsIdempotent(cx, obj, name, &idempotent)) - return false; - - if (idempotent) + if (PropertyReadIsIdempotent(constraints(), obj, name)) load->setIdempotent(); } @@ -8603,8 +8484,8 @@ IonBuilder::getPropTryCache(bool *emitted, PropertyName *name, jsid id, // Caches can read values from prototypes, so update the barrier to // reflect such possible values. - if (!barrier && !PropertyReadOnPrototypeNeedsTypeBarrier(cx, obj, name, types, &barrier)) - return false; + if (!barrier) + barrier = PropertyReadOnPrototypeNeedsTypeBarrier(cx, constraints(), obj, name, types); MIRType rvalType = MIRTypeFromValueType(types->getKnownTypeTag()); if (barrier || IsNullOrUndefined(rvalType)) @@ -8635,7 +8516,6 @@ IonBuilder::jsop_setprop(PropertyName *name) MDefinition *value = current->pop(); MDefinition *obj = current->pop(); - jsid id = NameToId(name); bool emitted = false; // Always use a call if we are doing the definite properties analysis and @@ -8652,19 +8532,15 @@ IonBuilder::jsop_setprop(PropertyName *name) current->add(MPostWriteBarrier::New(obj, value)); // Try to inline a common property setter, or make a call. - if (!setPropTryCommonSetter(&emitted, obj, name, id, value) || emitted) + if (!setPropTryCommonSetter(&emitted, obj, name, value) || emitted) return emitted; types::TemporaryTypeSet *objTypes = obj->resultTypeSet(); - bool barrier; - if (!PropertyWriteNeedsTypeBarrier(cx, current, &obj, name, &value, - /* canModify = */ true, &barrier)) - { - return false; - } + bool barrier = PropertyWriteNeedsTypeBarrier(constraints(), current, &obj, name, &value, + /* canModify = */ true); // Try to emit stores to known binary data blocks - if (!setPropTryTypedObject(&emitted, obj, id, value) || emitted) + if (!setPropTryTypedObject(&emitted, obj, name, value) || emitted) return emitted; // Try to emit store from definite slots. @@ -8672,7 +8548,7 @@ IonBuilder::jsop_setprop(PropertyName *name) return emitted; // Try to emit a monomorphic/polymorphic store based on baseline caches. - if (!setPropTryInlineAccess(&emitted, obj, name, id, value, barrier, objTypes) || emitted) + if (!setPropTryInlineAccess(&emitted, obj, name, value, barrier, objTypes) || emitted) return emitted; // Try to emit a polymorphic cache. @@ -8688,8 +8564,7 @@ IonBuilder::jsop_setprop(PropertyName *name) bool IonBuilder::setPropTryCommonSetter(bool *emitted, MDefinition *obj, - PropertyName *name, jsid id, - MDefinition *value) + PropertyName *name, MDefinition *value) { JS_ASSERT(*emitted == false); @@ -8697,7 +8572,7 @@ IonBuilder::setPropTryCommonSetter(bool *emitted, MDefinition *obj, bool isDOM; types::TemporaryTypeSet *objTypes = obj->resultTypeSet(); - if (!testCommonPropFunc(cx, objTypes, id, &commonSetter, false, &isDOM, nullptr)) + if (!testCommonPropFunc(cx, objTypes, name, &commonSetter, false, &isDOM, nullptr)) return false; if (!commonSetter) @@ -8801,12 +8676,12 @@ IonBuilder::setPropTryCommonDOMSetter(bool *emitted, MDefinition *obj, bool IonBuilder::setPropTryTypedObject(bool *emitted, MDefinition *obj, - jsid id, MDefinition *value) + PropertyName *name, MDefinition *value) { TypeRepresentationSet fieldTypeReprs; int32_t fieldOffset; size_t fieldIndex; - if (!lookupTypedObjectField(obj, id, &fieldOffset, &fieldTypeReprs, + if (!lookupTypedObjectField(obj, name, &fieldOffset, &fieldTypeReprs, &fieldIndex)) return false; if (fieldTypeReprs.empty()) @@ -8869,15 +8744,15 @@ IonBuilder::setPropTryDefiniteSlot(bool *emitted, MDefinition *obj, if (barrier) return true; - types::HeapTypeSet *propTypes = GetDefiniteSlot(cx, objTypes, name); - if (!propTypes) + types::HeapTypeSetKey property; + if (!getDefiniteSlot(obj->resultTypeSet(), name, &property)) return true; - MStoreFixedSlot *fixed = MStoreFixedSlot::New(obj, propTypes->definiteSlot(), value); + MStoreFixedSlot *fixed = MStoreFixedSlot::New(obj, property.actualTypes->definiteSlot(), value); current->add(fixed); current->push(value); - if (propTypes->needsBarrier(cx)) + if (property.needsBarrier(constraints())) fixed->setNeedsBarrier(); if (!resumeAfter(fixed)) @@ -8889,7 +8764,7 @@ IonBuilder::setPropTryDefiniteSlot(bool *emitted, MDefinition *obj, bool IonBuilder::setPropTryInlineAccess(bool *emitted, MDefinition *obj, - PropertyName *name, jsid id, + PropertyName *name, MDefinition *value, bool barrier, types::TemporaryTypeSet *objTypes) { @@ -8921,7 +8796,7 @@ IonBuilder::setPropTryInlineAccess(bool *emitted, MDefinition *obj, Shape *shape = objShape->search(cx, NameToId(name)); JS_ASSERT(shape); - bool needsBarrier = objTypes->propertyNeedsBarrier(cx, id); + bool needsBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name)); if (!storeSlot(obj, shape, value, needsBarrier)) return false; } else { @@ -8934,13 +8809,13 @@ IonBuilder::setPropTryInlineAccess(bool *emitted, MDefinition *obj, for (size_t i = 0; i < shapes.length(); i++) { Shape *objShape = shapes[i]; - Shape *shape = objShape->search(cx, id); + Shape *shape = objShape->search(cx, NameToId(name)); JS_ASSERT(shape); if (!ins->addShape(objShape, shape)) return false; } - if (objTypes->propertyNeedsBarrier(cx, id)) + if (objTypes->propertyNeedsBarrier(constraints(), NameToId(name))) ins->setNeedsBarrier(); if (!resumeAfter(ins)) @@ -8961,8 +8836,7 @@ IonBuilder::setPropTryCache(bool *emitted, MDefinition *obj, // Emit SetPropertyCache. MSetPropertyCache *ins = MSetPropertyCache::New(obj, value, name, script()->strict, barrier); - jsid id = NameToId(name); - if (!objTypes || objTypes->propertyNeedsBarrier(cx, id)) + if (!objTypes || objTypes->propertyNeedsBarrier(constraints(), NameToId(name))) ins->setNeedsBarrier(); current->add(ins); @@ -9316,10 +9190,8 @@ IonBuilder::hasStaticScopeObject(ScopeCoordinate sc, JSObject **pcall) if (!outerScript || !outerScript->treatAsRunOnce) return false; - types::TypeObject *funType = outerScript->function()->getType(cx); - if (!funType) - return false; - if (types::HeapTypeSet::HasObjectFlags(cx, funType, types::OBJECT_FLAG_RUNONCE_INVALIDATED)) + types::TypeObjectKey *funType = types::TypeObjectKey::get(outerScript->function()); + if (funType->hasFlags(constraints(), types::OBJECT_FLAG_RUNONCE_INVALIDATED)) return false; // The script this aliased var operation is accessing will run only once, @@ -9459,8 +9331,11 @@ IonBuilder::jsop_in() MDefinition *obj = current->peek(-1); MDefinition *id = current->peek(-2); - if (ElementAccessIsDenseNative(obj, id) && !ElementAccessHasExtraIndexedProperty(cx, obj)) + if (ElementAccessIsDenseNative(obj, id) && + !ElementAccessHasExtraIndexedProperty(constraints(), obj)) + { return jsop_in_dense(); + } current->pop(); current->pop(); @@ -9478,7 +9353,7 @@ IonBuilder::jsop_in_dense() MDefinition *obj = current->pop(); MDefinition *id = current->pop(); - bool needsHoleCheck = !ElementAccessIsPacked(cx, obj); + bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj); // Ensure id is an integer. MInstruction *idInt32 = MToInt32::New(id); @@ -9515,13 +9390,13 @@ IonBuilder::jsop_instanceof() if (!rhsObject || !rhsObject->is<JSFunction>() || rhsObject->isBoundFunction()) break; - types::TypeObject *rhsType = rhsObject->getType(cx); - if (!rhsType || rhsType->unknownProperties()) + types::TypeObjectKey *rhsType = types::TypeObjectKey::get(rhsObject); + if (rhsType->unknownProperties()) break; - types::HeapTypeSet *protoTypes = - rhsType->getProperty(cx, NameToId(cx->names().classPrototype)); - JSObject *protoObject = protoTypes ? protoTypes->getSingleton(cx) : nullptr; + types::HeapTypeSetKey protoProperty = + rhsType->property(NameToId(cx->names().classPrototype)); + JSObject *protoObject = protoProperty.singleton(constraints()); if (!protoObject) break; @@ -9706,7 +9581,7 @@ IonBuilder::loadTypedObjectData(MDefinition *typedObj, // pair could be determined. bool IonBuilder::lookupTypedObjectField(MDefinition *typedObj, - jsid id, + PropertyName *name, int32_t *fieldOffset, TypeRepresentationSet *fieldTypeReprs, size_t *fieldIndex) @@ -9719,9 +9594,9 @@ IonBuilder::lookupTypedObjectField(MDefinition *typedObj, if (!objTypeReprs.allOfKind(TypeRepresentation::Struct)) return true; - // Determine the type/offset of the field `id`, if any. + // Determine the type/offset of the field `name`, if any. size_t offset; - if (!objTypeReprs.fieldNamed(*this, id, &offset, + if (!objTypeReprs.fieldNamed(*this, NameToId(name), &offset, fieldTypeReprs, fieldIndex)) return false; if (fieldTypeReprs->empty()) diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index c6278154f18e..21fafd790a89 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -206,6 +206,7 @@ class IonBuilder : public MIRGenerator public: IonBuilder(JSContext *cx, TempAllocator *temp, MIRGraph *graph, + types::CompilerConstraintList *constraints, BaselineInspector *inspector, CompileInfo *info, BaselineFrame *baselineFrame, size_t inliningDepth = 0, uint32_t loopDepth = 0); @@ -358,13 +359,15 @@ class IonBuilder : public MIRGenerator // jsop_getprop() helpers. bool getPropTryArgumentsLength(bool *emitted); - bool getPropTryConstant(bool *emitted, jsid id, types::TemporaryTypeSet *types); + bool getPropTryConstant(bool *emitted, PropertyName *name, + types::TemporaryTypeSet *types); bool getPropTryDefiniteSlot(bool *emitted, PropertyName *name, bool barrier, types::TemporaryTypeSet *types); - bool getPropTryCommonGetter(bool *emitted, jsid id, types::TemporaryTypeSet *types); - bool getPropTryInlineAccess(bool *emitted, PropertyName *name, jsid id, + bool getPropTryCommonGetter(bool *emitted, PropertyName *name, + types::TemporaryTypeSet *types); + bool getPropTryInlineAccess(bool *emitted, PropertyName *name, bool barrier, types::TemporaryTypeSet *types); - bool getPropTryTypedObject(bool *emitted, jsid id, + bool getPropTryTypedObject(bool *emitted, PropertyName *name, types::TemporaryTypeSet *resultTypes); bool getPropTryScalarPropOfTypedObject(bool *emitted, int32_t fieldOffset, @@ -375,14 +378,13 @@ class IonBuilder : public MIRGenerator TypeRepresentationSet fieldTypeReprs, size_t fieldIndex, types::TemporaryTypeSet *resultTypes); - bool getPropTryCache(bool *emitted, PropertyName *name, jsid id, + bool getPropTryCache(bool *emitted, PropertyName *name, bool barrier, types::TemporaryTypeSet *types); bool needsToMonitorMissingProperties(types::TemporaryTypeSet *types); // jsop_setprop() helpers. bool setPropTryCommonSetter(bool *emitted, MDefinition *obj, - PropertyName *name, jsid id, - MDefinition *value); + PropertyName *name, MDefinition *value); bool setPropTryCommonDOMSetter(bool *emitted, MDefinition *obj, MDefinition *value, JSFunction *setter, bool isDOM); @@ -390,11 +392,10 @@ class IonBuilder : public MIRGenerator PropertyName *name, MDefinition *value, bool barrier, types::TemporaryTypeSet *objTypes); bool setPropTryInlineAccess(bool *emitted, MDefinition *obj, - PropertyName *name, jsid id, - MDefinition *value, bool barrier, + PropertyName *name, MDefinition *value, bool barrier, types::TemporaryTypeSet *objTypes); bool setPropTryTypedObject(bool *emitted, MDefinition *obj, - jsid id, MDefinition *value); + PropertyName *name, MDefinition *value); bool setPropTryCache(bool *emitted, MDefinition *obj, PropertyName *name, MDefinition *value, bool barrier, types::TemporaryTypeSet *objTypes); @@ -403,7 +404,7 @@ class IonBuilder : public MIRGenerator bool lookupTypeRepresentationSet(MDefinition *typedObj, TypeRepresentationSet *out); bool lookupTypedObjectField(MDefinition *typedObj, - jsid id, + PropertyName *name, int32_t *fieldOffset, TypeRepresentationSet *fieldTypeReprs, size_t *fieldIndex); @@ -623,7 +624,7 @@ class IonBuilder : public MIRGenerator MDefinition *patchInlinedReturns(CallInfo &callInfo, MIRGraphExits &exits, MBasicBlock *bottom); inline bool testCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, - jsid id, JSFunction **funcp, + PropertyName *name, JSFunction **funcp, bool isGetter, bool *isDOM, MDefinition **guardOut); @@ -632,6 +633,17 @@ class IonBuilder : public MIRGenerator MGetPropertyCache *getInlineableGetPropertyCache(CallInfo &callInfo); + bool testSingletonProperty(JSObject *obj, JSObject *singleton, PropertyName *name, + bool *isKnownConstant); + bool testSingletonPropertyTypes(MDefinition *obj, JSObject *singleton, + JSObject *globalObj, PropertyName *name, + bool *isKnownConstant, bool *testObject, + bool *testString); + bool getDefiniteSlot(types::TemporaryTypeSet *types, PropertyName *name, + types::HeapTypeSetKey *property); + bool freezePropTypeSets(types::TemporaryTypeSet *types, + JSObject *foundProto, PropertyName *name); + types::TemporaryTypeSet *bytecodeTypes(jsbytecode *pc); types::TemporaryTypeSet *cloneTypeSet(types::StackTypeSet *types); @@ -659,9 +671,6 @@ class IonBuilder : public MIRGenerator CodeGenerator *backgroundCodegen_; public: - // Compilation index for this attempt. - types::RecompileInfo const recompileInfo; - void clearForBackEnd(); JSScript *script() const { return script_.get(); } @@ -673,6 +682,10 @@ class IonBuilder : public MIRGenerator TypeRepresentationSetHash *getOrCreateReprSetHash(); // fallible + types::CompilerConstraintList *constraints() { + return constraints_; + } + bool isInlineBuilder() const { return callerBuilder_ != NULL; } @@ -685,6 +698,9 @@ class IonBuilder : public MIRGenerator AbortReason abortReason_; ScopedJSDeletePtr<TypeRepresentationSetHash> reprSetHash_; + // Constraints for recording dependencies on type information. + types::CompilerConstraintList *constraints_; + // 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 1188a63d459c..19ca8741403a 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -1964,8 +1964,7 @@ SetPropertyIC::attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject if (checkTypeset) { TypedOrValueRegister valReg = value().reg(); - RootedId id(cx, types::IdToTypeId(AtomToId(name()))); - types::HeapTypeSet *propTypes = type->maybeGetProperty(cx, id); + types::HeapTypeSet *propTypes = type->maybeGetProperty(cx, NameToId(name())); JS_ASSERT(propTypes); JS_ASSERT(!propTypes->unknown()); @@ -2613,8 +2612,7 @@ IsPropertySetInlineable(JSContext *cx, const SetPropertyIC &cache, HandleObject bool shouldCheck = false; types::TypeObject *type = obj->getType(cx); if (cache.needsTypeBarrier() && !type->unknownProperties()) { - RootedId typeId(cx, types::IdToTypeId(id)); - types::HeapTypeSet *propTypes = type->maybeGetProperty(cx, typeId); + types::HeapTypeSet *propTypes = type->maybeGetProperty(cx, id); if (!propTypes) return false; if (!propTypes->unknown()) { diff --git a/js/src/jit/IonCode.h b/js/src/jit/IonCode.h index db6e2c3bd60f..3f776f292a0b 100644 --- a/js/src/jit/IonCode.h +++ b/js/src/jit/IonCode.h @@ -328,7 +328,8 @@ struct IonScript // Do not call directly, use IonScript::New. This is public for cx->new_. IonScript(); - static IonScript *New(JSContext *cx, uint32_t frameLocals, uint32_t frameSize, + static IonScript *New(JSContext *cx, types::RecompileInfo recompileInfo, + uint32_t frameLocals, uint32_t frameSize, size_t snapshotsSize, size_t snapshotEntries, size_t constants, size_t safepointIndexEntries, size_t osiIndexEntries, size_t cacheEntries, size_t runtimeSize, size_t safepointsSize, diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 9a2d826a89ea..f075e47a5672 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -212,18 +212,17 @@ IonBuilder::inlineArray(CallInfo &callInfo) initLength = callInfo.argc(); allocating = MNewArray::NewArray_Allocating; - types::TypeObject *type = types::TypeScript::InitObject(cx, script(), pc, JSProto_Array); - if (!type) + types::TypeObject *baseType = types::TypeScript::InitObject(cx, script(), pc, JSProto_Array); + if (!baseType) return InliningStatus_Error; + types::TypeObjectKey *type = types::TypeObjectKey::get(baseType); if (!type->unknownProperties()) { - types::HeapTypeSet *elemTypes = type->getProperty(cx, JSID_VOID); - if (!elemTypes) - return InliningStatus_Error; + types::HeapTypeSetKey elemTypes = type->property(JSID_VOID); for (uint32_t i = 0; i < initLength; i++) { MDefinition *value = callInfo.getArg(i); - if (!TypeSetIncludes(elemTypes, value->type(), value->resultTypeSet())) { - elemTypes->addFreeze(cx); + if (!TypeSetIncludes(elemTypes.actualTypes, value->type(), value->resultTypeSet())) { + elemTypes.freeze(constraints()); return InliningStatus_NotInlined; } } @@ -251,7 +250,7 @@ IonBuilder::inlineArray(CallInfo &callInfo) return InliningStatus_Error; types::TemporaryTypeSet::DoubleConversion conversion = - getInlineReturnTypeSet()->convertDoubleElements(cx); + getInlineReturnTypeSet()->convertDoubleElements(constraints()); if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles) templateObject->setShouldConvertDoubleElements(); @@ -320,23 +319,21 @@ IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode) types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet(); if (!thisTypes || thisTypes->getKnownClass() != &ArrayObject::class_) return InliningStatus_NotInlined; - if (thisTypes->hasObjectFlags(cx, unhandledFlags)) + if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) return InliningStatus_NotInlined; RootedScript scriptRoot(cx, script()); - if (types::ArrayPrototypeHasIndexedProperty(cx, scriptRoot)) + if (types::ArrayPrototypeHasIndexedProperty(constraints(), scriptRoot)) return InliningStatus_NotInlined; callInfo.unwrapArgs(); types::StackTypeSet *returnTypes = getOriginalInlineReturnTypeSet(); - bool needsHoleCheck = thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED); + bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED); bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType()); - bool barrier; - if (!PropertyReadNeedsTypeBarrier(cx, callInfo.thisArg(), nullptr, returnTypes, &barrier)) - return InliningStatus_Error; - + bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(), + callInfo.thisArg(), nullptr, returnTypes); if (barrier) returnType = MIRType_Value; @@ -363,14 +360,11 @@ IonBuilder::inlineArrayPush(CallInfo &callInfo) MDefinition *obj = callInfo.thisArg(); MDefinition *value = callInfo.getArg(0); - bool writeNeedsBarrier; - if (!PropertyWriteNeedsTypeBarrier(cx, current, &obj, nullptr, &value, - /* canModify = */ false, &writeNeedsBarrier)) + if (PropertyWriteNeedsTypeBarrier(constraints(), current, + &obj, nullptr, &value, /* canModify = */ false)) { - return InliningStatus_Error; - } - if (writeNeedsBarrier) return InliningStatus_NotInlined; + } JS_ASSERT(obj == callInfo.thisArg() && value == callInfo.getArg(0)); if (getInlineReturnType() != MIRType_Int32) @@ -381,17 +375,18 @@ IonBuilder::inlineArrayPush(CallInfo &callInfo) types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet(); if (!thisTypes || thisTypes->getKnownClass() != &ArrayObject::class_) return InliningStatus_NotInlined; - if (thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPARSE_INDEXES | + if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES | types::OBJECT_FLAG_LENGTH_OVERFLOW)) { return InliningStatus_NotInlined; } RootedScript scriptRoot(cx, script()); - if (types::ArrayPrototypeHasIndexedProperty(cx, scriptRoot)) + if (types::ArrayPrototypeHasIndexedProperty(constraints(), scriptRoot)) return InliningStatus_NotInlined; - types::TemporaryTypeSet::DoubleConversion conversion = thisTypes->convertDoubleElements(cx); + types::TemporaryTypeSet::DoubleConversion conversion = + thisTypes->convertDoubleElements(constraints()); if (conversion == types::TemporaryTypeSet::AmbiguousDoubleConversion) return InliningStatus_NotInlined; @@ -440,7 +435,7 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo) if (thisTypes->getKnownClass() != &ArrayObject::class_) return InliningStatus_NotInlined; - if (thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPARSE_INDEXES | + if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES | types::OBJECT_FLAG_LENGTH_OVERFLOW)) { return InliningStatus_NotInlined; @@ -448,7 +443,7 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo) if (argTypes->getKnownClass() != &ArrayObject::class_) return InliningStatus_NotInlined; - if (argTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPARSE_INDEXES | + if (argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES | types::OBJECT_FLAG_LENGTH_OVERFLOW)) { return InliningStatus_NotInlined; @@ -456,7 +451,7 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo) // Watch out for indexed properties on the prototype. RootedScript scriptRoot(cx, script()); - if (types::ArrayPrototypeHasIndexedProperty(cx, scriptRoot)) + if (types::ArrayPrototypeHasIndexedProperty(constraints(), scriptRoot)) return InliningStatus_NotInlined; // Require the 'this' types to have a specific type matching the current @@ -464,18 +459,20 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo) if (thisTypes->getObjectCount() != 1) return InliningStatus_NotInlined; - types::TypeObject *thisType = thisTypes->getTypeObject(0); - if (!thisType || - thisType->unknownProperties() || - &thisType->proto->global() != &script()->global()) + types::TypeObject *baseThisType = thisTypes->getTypeObject(0); + if (!baseThisType) + return InliningStatus_NotInlined; + types::TypeObjectKey *thisType = types::TypeObjectKey::get(baseThisType); + if (thisType->unknownProperties() || + &thisType->proto().toObject()->global() != &script()->global()) { return InliningStatus_NotInlined; } // Don't inline if 'this' is packed and the argument may not be packed // (the result array will reuse the 'this' type). - if (!thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED) && - argTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED)) + if (!thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED) && + argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED)) { return InliningStatus_NotInlined; } @@ -483,38 +480,30 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo) // Constraints modeling this concat have not been generated by inference, // so check that type information already reflects possible side effects of // this call. - types::HeapTypeSet *thisElemTypes = thisType->getProperty(cx, JSID_VOID); - if (!thisElemTypes) - return InliningStatus_Error; + types::HeapTypeSetKey thisElemTypes = thisType->property(JSID_VOID); types::TemporaryTypeSet *resTypes = getInlineReturnTypeSet(); if (!resTypes->hasType(types::Type::ObjectType(thisType))) return InliningStatus_NotInlined; for (unsigned i = 0; i < argTypes->getObjectCount(); i++) { - if (argTypes->getSingleObject(i)) - return InliningStatus_NotInlined; - - types::TypeObject *argType = argTypes->getTypeObject(i); + types::TypeObjectKey *argType = argTypes->getObject(i); if (!argType) continue; if (argType->unknownProperties()) return InliningStatus_NotInlined; - types::HeapTypeSet *elemTypes = argType->getProperty(cx, JSID_VOID); - if (!elemTypes) - return InliningStatus_Error; - - if (!elemTypes->knownSubset(cx, thisElemTypes)) + types::HeapTypeSetKey elemTypes = argType->property(JSID_VOID); + if (!elemTypes.knownSubset(constraints(), thisElemTypes)) return InliningStatus_NotInlined; } // Inline the call. - JSObject *templateObj = NewDenseEmptyArray(cx, thisType->proto, TenuredObject); + JSObject *templateObj = NewDenseEmptyArray(cx, thisType->proto().toObject(), TenuredObject); if (!templateObj) return InliningStatus_Error; - templateObj->setType(thisType); + templateObj->setType(baseThisType); callInfo.unwrapArgs(); @@ -1068,12 +1057,9 @@ IonBuilder::inlineUnsafePutElements(CallInfo &callInfo) bool writeNeedsBarrier = false; if (isDenseNative) { - if (!PropertyWriteNeedsTypeBarrier(cx, current, &obj, nullptr, &elem, - /* canModify = */ false, - &writeNeedsBarrier)) - { - return InliningStatus_Error; - } + writeNeedsBarrier = PropertyWriteNeedsTypeBarrier(constraints(), current, + &obj, nullptr, &elem, + /* canModify = */ false); } // We can only inline setelem on dense arrays that do not need type @@ -1135,7 +1121,7 @@ IonBuilder::inlineUnsafeSetDenseArrayElement(CallInfo &callInfo, uint32_t base) MDefinition *elem = callInfo.getArg(base + 2); types::TemporaryTypeSet::DoubleConversion conversion = - obj->resultTypeSet()->convertDoubleElements(cx); + obj->resultTypeSet()->convertDoubleElements(constraints()); if (!jsop_setelem_dense(conversion, SetElem_Unsafe, obj, id, elem)) return false; return true; diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index e90fb1014ab4..4bf7c6c3d693 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -27,6 +27,7 @@ using namespace js; using namespace js::jit; using mozilla::DoublesAreIdentical; +using mozilla::Maybe; void MDefinition::PrintOpcodeName(FILE *fp, MDefinition::Opcode op) @@ -2681,129 +2682,107 @@ jit::ElementAccessIsTypedArray(MDefinition *obj, MDefinition *id, } bool -jit::ElementAccessIsPacked(JSContext *cx, MDefinition *obj) +jit::ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefinition *obj) { types::TemporaryTypeSet *types = obj->resultTypeSet(); - return types && !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED); + return types && !types->hasObjectFlags(constraints, types::OBJECT_FLAG_NON_PACKED); } bool -jit::ElementAccessHasExtraIndexedProperty(JSContext *cx, MDefinition *obj) +jit::ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints, + MDefinition *obj) { types::TemporaryTypeSet *types = obj->resultTypeSet(); - if (!types || types->hasObjectFlags(cx, types::OBJECT_FLAG_LENGTH_OVERFLOW)) + if (!types || types->hasObjectFlags(constraints, types::OBJECT_FLAG_LENGTH_OVERFLOW)) return true; - return types::TypeCanHaveExtraIndexedProperties(cx, types); + return types::TypeCanHaveExtraIndexedProperties(constraints, types); } -bool -jit::DenseNativeElementType(JSContext *cx, MDefinition *obj, MIRType *result) +MIRType +jit::DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj) { - JS_ASSERT(result); - *result = MIRType_None; - types::TemporaryTypeSet *types = obj->resultTypeSet(); MIRType elementType = MIRType_None; unsigned count = types->getObjectCount(); for (unsigned i = 0; i < count; i++) { - types::TypeObject *object; - if (!types->getTypeOrSingleObject(cx, i, &object)) - return false; - + types::TypeObjectKey *object = types->getObject(i); if (!object) continue; if (object->unknownProperties()) - return true; + return MIRType_None; - types::HeapTypeSet *elementTypes = object->getProperty(cx, JSID_VOID); - if (!elementTypes) - return true; + types::HeapTypeSetKey elementTypes = object->property(JSID_VOID); - MIRType type = MIRTypeFromValueType(elementTypes->getKnownTypeTag(cx)); + MIRType type = MIRTypeFromValueType(elementTypes.knownTypeTag(constraints)); if (type == MIRType_None) - return true; + return MIRType_None; if (elementType == MIRType_None) elementType = type; else if (elementType != type) - return true; + return MIRType_None; } - *result = elementType; - return true; + return elementType; } static bool -PropertyReadNeedsTypeBarrier(JSContext *cx, types::TypeObject *object, PropertyName *name, - types::TypeSet *observed, bool *result) +PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints, + types::TypeObjectKey *object, PropertyName *name, + types::TypeSet *observed) { - jsid id = name ? types::IdToTypeId(NameToId(name)) : JSID_VOID; - // If the object being read from has types for the property which haven't // been observed at this access site, the read could produce a new type and // a barrier is needed. Note that this only covers reads from properties // which are accounted for by type information, i.e. native data properties // and elements. - JS_ASSERT(result); - *result = false; - if (object->unknownProperties()) { - *result = true; + if (object->unknownProperties()) return true; - } - types::HeapTypeSet *property = object->getProperty(cx, id); - if (!property) { - *result = true; + jsid id = name ? NameToId(name) : JSID_VOID; + types::HeapTypeSetKey property = object->property(id); + if (!TypeSetIncludes(observed, MIRType_Value, property.actualTypes)) return true; - } - - if (!TypeSetIncludes(observed, MIRType_Value, property)) { - *result = true; - return true; - } // Type information for singleton objects is not required to reflect the // initial 'undefined' value for native properties, in particular global // variables declared with 'var'. Until the property is assigned a value // other than undefined, a barrier is required. - if (name && object->singleton && object->singleton->isNative()) { - Shape *shape = object->singleton->nativeLookup(cx, name); + if (name && object->singleton() && object->singleton()->isNative()) { + Shape *shape = object->singleton()->nativeLookup(cx, name); if (shape && shape->hasDefaultGetter() && - object->singleton->nativeGetSlot(shape->slot()).isUndefined()) + object->singleton()->nativeGetSlot(shape->slot()).isUndefined()) { - *result = true; return true; } } - property->addFreeze(cx); - *result = false; - return true; + property.freeze(constraints); + return false; } bool -jit::PropertyReadNeedsTypeBarrier(JSContext *cx, types::TypeObject *object, PropertyName *name, - types::StackTypeSet *observed, bool updateObserved, bool *result) +jit::PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints, + types::TypeObjectKey *object, PropertyName *name, + types::StackTypeSet *observed, bool updateObserved) { - jsid id = name ? types::IdToTypeId(NameToId(name)) : JSID_VOID; - // 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() && observed->noConstraints() && !JSID_IS_VOID(id)) { - JSObject *obj = object->singleton ? object->singleton : object->proto; + if (updateObserved && observed->empty() && observed->noConstraints() && name) { + JSObject *obj = object->singleton() ? object->singleton() : object->proto().toObjectOrNull(); while (obj) { if (!obj->isNative()) break; Value v; - if (HasDataProperty(cx, obj, id, &v)) { + if (HasDataProperty(cx, obj, NameToId(name), &v)) { if (v.isUndefined()) break; observed->addType(cx, types::GetValueType(v)); @@ -2813,111 +2792,85 @@ jit::PropertyReadNeedsTypeBarrier(JSContext *cx, types::TypeObject *object, Prop } } - return PropertyReadNeedsTypeBarrier(cx, object, name, observed, result); + return PropertyReadNeedsTypeBarrier(cx, constraints, object, name, observed); } bool -jit::PropertyReadNeedsTypeBarrier(JSContext *cx, MDefinition *obj, PropertyName *name, - types::StackTypeSet *observed, bool *result) +jit::PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints, + MDefinition *obj, PropertyName *name, + types::StackTypeSet *observed) { - JS_ASSERT(result); - *result = false; - if (observed->unknown()) - return true; + return false; types::TypeSet *types = obj->resultTypeSet(); - if (!types || types->unknownObject()) { - *result = true; + if (!types || types->unknownObject()) return true; - } bool updateObserved = types->getObjectCount() == 1; for (size_t i = 0; i < types->getObjectCount(); i++) { - types::TypeObject *object; - if (!types->getTypeOrSingleObject(cx, i, &object)) - return false; - + types::TypeObjectKey *object = types->getObject(i); if (object) { - if (!PropertyReadNeedsTypeBarrier(cx, object, name, observed, updateObserved, result)) - return false; - - if (*result) + if (PropertyReadNeedsTypeBarrier(cx, constraints, object, name, + observed, updateObserved)) + { return true; + } } } - return true; + return false; } bool -jit::PropertyReadOnPrototypeNeedsTypeBarrier(JSContext *cx, MDefinition *obj, PropertyName *name, - types::TemporaryTypeSet *observed, bool *result) +jit::PropertyReadOnPrototypeNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints, + MDefinition *obj, PropertyName *name, + types::TemporaryTypeSet *observed) { - JS_ASSERT(result); - *result = false; - if (observed->unknown()) - return true; - - types::TypeSet *types = obj->resultTypeSet(); - if (!types || types->unknownObject()) { - *result = true; - return true; - } - - for (size_t i = 0; i < types->getObjectCount(); i++) { - types::TypeObject *object; - if (!types->getTypeOrSingleObject(cx, i, &object)) - return false; - - if (!object) - continue; - while (object->proto) { - object = object->proto->getType(cx); - if (!object) - return false; - - if (!PropertyReadNeedsTypeBarrier(cx, object, name, observed, result)) - return false; - if (*result) - return true; - } - } - - return true; -} - -bool -jit::PropertyReadIsIdempotent(JSContext *cx, MDefinition *obj, PropertyName *name, bool *result) -{ - JS_ASSERT(result); - *result = false; - // Determine if reading a property from obj is likely to be idempotent. - - jsid id = types::IdToTypeId(NameToId(name)); + return false; types::TypeSet *types = obj->resultTypeSet(); if (!types || types->unknownObject()) return true; for (size_t i = 0; i < types->getObjectCount(); i++) { - types::TypeObject *object; - if (!types->getTypeOrSingleObject(cx, i, &object)) - return false; - - if (object) { - if (object->unknownProperties()) - return true; - - // Check if the property has been reconfigured or is a getter. - types::HeapTypeSet *property = object->getProperty(cx, id); - if (!property || property->isConfiguredProperty(cx, object)) + types::TypeObjectKey *object = types->getObject(i); + if (!object) + continue; + while (object->proto().isObject()) { + object = types::TypeObjectKey::get(object->proto().toObject()); + if (PropertyReadNeedsTypeBarrier(cx, constraints, object, name, observed)) return true; } } - *result = true; + return false; +} + +bool +jit::PropertyReadIsIdempotent(types::CompilerConstraintList *constraints, + MDefinition *obj, PropertyName *name) +{ + // Determine if reading a property from obj is likely to be idempotent. + + types::TypeSet *types = obj->resultTypeSet(); + if (!types || types->unknownObject()) + return false; + + for (size_t i = 0; i < types->getObjectCount(); i++) { + types::TypeObjectKey *object = types->getObject(i); + if (object) { + if (object->unknownProperties()) + return false; + + // Check if the property has been reconfigured or is a getter. + types::HeapTypeSetKey property = object->property(NameToId(name)); + if (property.configured(constraints, object)) + return false; + } + } + return true; } @@ -2936,8 +2889,6 @@ jit::AddObjectsForPropertyRead(JSContext *cx, MDefinition *obj, PropertyName *na return true; } - jsid id = name ? types::IdToTypeId(NameToId(name)) : JSID_VOID; - for (size_t i = 0; i < types->getObjectCount(); i++) { types::TypeObject *object; if (!types->getTypeOrSingleObject(cx, i, &object)) @@ -2951,6 +2902,7 @@ jit::AddObjectsForPropertyRead(JSContext *cx, MDefinition *obj, PropertyName *na return true; } + jsid id = name ? NameToId(name) : JSID_VOID; types::HeapTypeSet *property = object->getProperty(cx, id); if (property->unknownObject()) { observed->addType(cx, types::Type::AnyObjectType()); @@ -2969,52 +2921,51 @@ jit::AddObjectsForPropertyRead(JSContext *cx, MDefinition *obj, PropertyName *na } static bool -TryAddTypeBarrierForWrite(JSContext *cx, MBasicBlock *current, types::TemporaryTypeSet *objTypes, - jsid id, MDefinition **pvalue) +TryAddTypeBarrierForWrite(types::CompilerConstraintList *constraints, + MBasicBlock *current, types::TemporaryTypeSet *objTypes, + PropertyName *name, MDefinition **pvalue) { // Return whether pvalue was modified to include a type barrier ensuring // that writing the value to objTypes/id will not require changing type // information. - // All objects in the set must have the same types for id. Otherwise, we + // All objects in the set must have the same types for name. Otherwise, we // could bail out without subsequently triggering a type change that // invalidates the compiled code. - types::HeapTypeSet *aggregateProperty = nullptr; + Maybe<types::HeapTypeSetKey> aggregateProperty; for (size_t i = 0; i < objTypes->getObjectCount(); i++) { - types::TypeObject *object; - if (!objTypes->getTypeOrSingleObject(cx, i, &object)) - return false; - + types::TypeObjectKey *object = objTypes->getObject(i); if (!object) continue; if (object->unknownProperties()) return false; - types::HeapTypeSet *property = object->getProperty(cx, id); - if (!property) - return false; - - if (TypeSetIncludes(property, (*pvalue)->type(), (*pvalue)->resultTypeSet())) + jsid id = name ? NameToId(name) : JSID_VOID; + types::HeapTypeSetKey property = object->property(id); + if (TypeSetIncludes(property.actualTypes, (*pvalue)->type(), (*pvalue)->resultTypeSet())) return false; // This freeze is not required for correctness, but ensures that we // will recompile if the property types change and the barrier can // potentially be removed. - property->addFreeze(cx); + property.freeze(constraints); - if (aggregateProperty) { - if (!aggregateProperty->isSubset(property) || !property->isSubset(aggregateProperty)) - return false; + if (aggregateProperty.empty()) { + aggregateProperty.construct(property); } else { - aggregateProperty = property; + if (!aggregateProperty.ref().actualTypes->isSubset(property.actualTypes) || + !property.actualTypes->isSubset(aggregateProperty.ref().actualTypes)) + { + return false; + } } } - JS_ASSERT(aggregateProperty); + JS_ASSERT(!aggregateProperty.empty()); - MIRType propertyType = MIRTypeFromValueType(aggregateProperty->getKnownTypeTag(cx)); + MIRType propertyType = MIRTypeFromValueType(aggregateProperty.ref().knownTypeTag(constraints)); switch (propertyType) { case MIRType_Boolean: case MIRType_Int32: @@ -3039,7 +2990,8 @@ TryAddTypeBarrierForWrite(JSContext *cx, MBasicBlock *current, types::TemporaryT if ((*pvalue)->type() != MIRType_Value) return false; - types::TemporaryTypeSet *types = aggregateProperty->clone(GetIonContext()->temp->lifoAlloc()); + types::TemporaryTypeSet *types = + aggregateProperty.ref().actualTypes->clone(GetIonContext()->temp->lifoAlloc()); if (!types) return false; @@ -3062,13 +3014,10 @@ AddTypeGuard(MBasicBlock *current, MDefinition *obj, types::TypeObject *typeObje } bool -jit::PropertyWriteNeedsTypeBarrier(JSContext *cx, MBasicBlock *current, MDefinition **pobj, - PropertyName *name, MDefinition **pvalue, bool canModify, - bool *result) +jit::PropertyWriteNeedsTypeBarrier(types::CompilerConstraintList *constraints, + MBasicBlock *current, MDefinition **pobj, + PropertyName *name, MDefinition **pvalue, bool canModify) { - JS_ASSERT(result); - *result = false; - // If any value being written is not reflected in the type information for // objects which obj could represent, a type barrier is needed when writing // the value. As for propertyReadNeedsTypeBarrier, this only applies for @@ -3076,12 +3025,8 @@ jit::PropertyWriteNeedsTypeBarrier(JSContext *cx, MBasicBlock *current, MDefinit // properties and elements. types::TemporaryTypeSet *types = (*pobj)->resultTypeSet(); - if (!types || types->unknownObject()) { - *result = true; + if (!types || types->unknownObject()) return true; - } - - jsid id = name ? types::IdToTypeId(NameToId(name)) : JSID_VOID; // If all of the objects being written to have property types which already // reflect the value, no barrier at all is needed. Additionally, if all @@ -3090,78 +3035,59 @@ jit::PropertyWriteNeedsTypeBarrier(JSContext *cx, MBasicBlock *current, MDefinit bool success = true; for (size_t i = 0; i < types->getObjectCount(); i++) { - types::TypeObject *object; - if (!types->getTypeOrSingleObject(cx, i, &object)) - return false; - + types::TypeObjectKey *object = types->getObject(i); if (!object || object->unknownProperties()) continue; // TI doesn't track TypedArray objects and should never insert a type // barrier for them. - if (object->getTypedArrayType() < ScalarTypeRepresentation::TYPE_MAX) + if (IsTypedArrayClass(object->clasp())) continue; - types::HeapTypeSet *property = object->getProperty(cx, id); - if (!property) { - success = false; - break; - } - if (!TypeSetIncludes(property, (*pvalue)->type(), (*pvalue)->resultTypeSet())) { + jsid id = name ? NameToId(name) : JSID_VOID; + types::HeapTypeSetKey property = object->property(id); + if (!TypeSetIncludes(property.actualTypes, (*pvalue)->type(), (*pvalue)->resultTypeSet())) { // Either pobj or pvalue needs to be modified to filter out the // types which the value could have but are not in the property, // or a VM call is required. A VM call is always required if pobj // and pvalue cannot be modified. - if (!canModify) { - *result = true; + if (!canModify) return true; - } - success = TryAddTypeBarrierForWrite(cx, current, types, id, pvalue); + success = TryAddTypeBarrierForWrite(constraints, current, types, name, pvalue); break; } } if (success) - return true; + return false; // If all of the objects except one have property types which reflect the // value, and the remaining object has no types at all for the property, // add a guard that the object does not have that remaining object's type. - if (types->getObjectCount() <= 1) { - *result = true; + if (types->getObjectCount() <= 1) return true; - } types::TypeObject *excluded = nullptr; for (size_t i = 0; i < types->getObjectCount(); i++) { - types::TypeObject *object; - if (!types->getTypeOrSingleObject(cx, i, &object)) - return false; - + types::TypeObjectKey *object = types->getObject(i); if (!object || object->unknownProperties()) continue; - if (object->getTypedArrayType() < ScalarTypeRepresentation::TYPE_MAX) + if (IsTypedArrayClass(object->clasp())) continue; - types::HeapTypeSet *property = object->getProperty(cx, id); - if (!property) { - *result = true; - return true; - } - - if (TypeSetIncludes(property, (*pvalue)->type(), (*pvalue)->resultTypeSet())) + jsid id = name ? NameToId(name) : JSID_VOID; + types::HeapTypeSetKey property = object->property(id); + if (TypeSetIncludes(property.actualTypes, (*pvalue)->type(), (*pvalue)->resultTypeSet())) continue; - if (!property->empty() || excluded) { - *result = true; + if (!property.actualTypes->empty() || excluded) return true; - } - excluded = object; + excluded = object->isTypeObject() ? object->asTypeObject() : object->asSingleObject()->getType(GetIonContext()->cx); } JS_ASSERT(excluded); *pobj = AddTypeGuard(current, *pobj, excluded, /* bailOnEquality = */ true); - return true; + return false; } diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index cce614860a0a..352d158c34c3 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -8985,21 +8985,27 @@ typedef Vector<MDefinition *, 8, IonAllocPolicy> MDefinitionVector; bool ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id); bool ElementAccessIsTypedArray(MDefinition *obj, MDefinition *id, ScalarTypeRepresentation::Type *arrayType); -bool ElementAccessIsPacked(JSContext *cx, MDefinition *obj); -bool ElementAccessHasExtraIndexedProperty(JSContext *cx, MDefinition *obj); -bool DenseNativeElementType(JSContext *cx, MDefinition *obj, MIRType *result); -bool PropertyReadNeedsTypeBarrier(JSContext *cx, types::TypeObject *object, PropertyName *name, - types::StackTypeSet *observed, bool updateObserved, bool *result); -bool PropertyReadNeedsTypeBarrier(JSContext *cx, MDefinition *obj, PropertyName *name, - types::StackTypeSet *observed, bool *result); -bool PropertyReadOnPrototypeNeedsTypeBarrier(JSContext *cx, MDefinition *obj, PropertyName *name, - types::TemporaryTypeSet *observed, bool *result); -bool PropertyReadIsIdempotent(JSContext *cx, MDefinition *obj, PropertyName *name, bool *result); +bool ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefinition *obj); +bool ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints, + MDefinition *obj); +MIRType DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj); +bool PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints, + types::TypeObjectKey *object, PropertyName *name, + types::StackTypeSet *observed, bool updateObserved); +bool PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints, + MDefinition *obj, PropertyName *name, + types::StackTypeSet *observed); +bool PropertyReadOnPrototypeNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints, + MDefinition *obj, PropertyName *name, + types::TemporaryTypeSet *observed); +bool PropertyReadIsIdempotent(types::CompilerConstraintList *constraints, + MDefinition *obj, PropertyName *name); bool AddObjectsForPropertyRead(JSContext *cx, MDefinition *obj, PropertyName *name, types::StackTypeSet *observed); -bool PropertyWriteNeedsTypeBarrier(JSContext *cx, MBasicBlock *current, MDefinition **pobj, +bool PropertyWriteNeedsTypeBarrier(types::CompilerConstraintList *constraints, + MBasicBlock *current, MDefinition **pobj, PropertyName *name, MDefinition **pvalue, - bool canModify, bool *result); + bool canModify); } // namespace jit } // namespace js diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index d8058a582d34..8801b0be2faf 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2864,7 +2864,7 @@ BeginMarkPhase(JSRuntime *rt) if (rt->gcIsIncremental) { for (GCZonesIter zone(rt); !zone.done(); zone.next()) { gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_DISCARD_CODE); - zone->discardJitCode(rt->defaultFreeOp(), false); + zone->discardJitCode(rt->defaultFreeOp()); } } @@ -3739,7 +3739,7 @@ BeginSweepingZoneGroup(JSRuntime *rt) for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_DISCARD_CODE); - zone->discardJitCode(&fop, !zone->isPreservingCode()); + zone->discardJitCode(&fop); } bool releaseTypes = ReleaseObservedTypes(rt); @@ -5009,7 +5009,7 @@ js::ReleaseAllJITCode(FreeOp *fop) /* Sweep now invalidated compiler outputs from each compartment. */ for (CompartmentsIter comp(fop->runtime()); !comp.done(); comp.next()) - comp->types.sweepCompilerOutputs(fop, false); + comp->types.clearCompilerOutputs(fop); #endif } diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 4fa6a23854b7..a1921efceda5 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -522,43 +522,237 @@ TypeSet::unionSets(TypeSet *a, TypeSet *b, LifoAlloc *alloc) } ///////////////////////////////////////////////////////////////////// -// Freeze constraints +// Compiler constraints ///////////////////////////////////////////////////////////////////// +// Compiler constraints overview +// +// Constraints generated during Ion compilation capture assumptions made about +// heap properties that will trigger invalidation of the resulting Ion code if +// the constraint is violated. Constraints can only be attached to type sets on +// the main thread, so to allow compilation to occur almost entirely off thread +// the generation is split into two phases. +// +// During compilation, CompilerConstraint values are constructed in a list, +// recording the heap property type set which was read from and its expected +// contents, along with the assumption made about those contents. +// +// At the end of compilation, when linking the result on the main thread, the +// list of compiler constraints are read and converted to type constraints and +// attached to the type sets. If the property type sets have changed so that the +// assumptions no longer hold then the compilation is aborted and its result +// discarded. + +static LifoAlloc *IonAlloc() { + return jit::GetIonContext()->temp->lifoAlloc(); +} + +// Superclass of all constraints generated during Ion compilation. These may +// be allocated off the main thread, using the current Ion context's allocator. +class types::CompilerConstraint +{ + public: + // Property being queried by the compiler. + HeapTypeSetKey property; + + // Contents of the property at the point when the query was performed. This + // may differ from the actual property types later in compilation as the + // main thread performs side effects. + TemporaryTypeSet *expected; + + CompilerConstraint(const HeapTypeSetKey &property) + : property(property), + expected(property.actualTypes->clone(IonAlloc())) + { + // Note: CompilerConstraintList::add watches for OOM under clone(). + } + + // Generate the type constraint recording the assumption made by this + // compilation. Returns true if the assumption originally made still holds. + virtual bool generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo) = 0; +}; + +void +CompilerConstraintList::add(CompilerConstraint *constraint) +{ + if (!constraint || !constraint->expected || !constraints.append(constraint)) + setFailed(); +} + namespace { -/* Constraint which triggers recompilation of a script if any type is added to a type set. */ -class TypeConstraintFreeze : public TypeConstraint +template <typename T> +class CompilerConstraintInstance : public CompilerConstraint +{ + T data; + + public: + CompilerConstraintInstance<T>(const HeapTypeSetKey &property, const T &data) + : CompilerConstraint(property), data(data) + {} + + bool generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo); +}; + +// Constraint generated from a CompilerConstraint when linking the compilation. +template <typename T> +class TypeCompilerConstraint : public TypeConstraint +{ + // Compilation which this constraint may invalidate. + RecompileInfo compilation; + + T data; + + public: + TypeCompilerConstraint<T>(RecompileInfo compilation, const T &data) + : compilation(compilation), data(data) + {} + + const char *kind() { return data.kind(); } + + void newType(JSContext *cx, TypeSet *source, Type type) { + if (data.invalidateOnNewType(type)) + cx->compartment()->types.addPendingRecompile(cx, compilation); + } + + void newPropertyState(JSContext *cx, TypeSet *source) { + if (data.invalidateOnNewPropertyState(source)) + cx->compartment()->types.addPendingRecompile(cx, compilation); + } + + void newObjectState(JSContext *cx, TypeObject *object, bool force) { + if (data.invalidateOnNewObjectState(object, force)) + cx->compartment()->types.addPendingRecompile(cx, compilation); + } +}; + +template <typename T> +bool +CompilerConstraintInstance<T>::generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo) +{ + if (!data.constraintHolds(cx, property, expected)) + return false; + + property.actualTypes->add(cx, cx->typeLifoAlloc().new_<TypeCompilerConstraint<T> >(recompileInfo, data), + /* callExisting = */ false); + return true; +} + +} /* anonymous namespace */ + +const Class * +TypeObjectKey::clasp() +{ + return isTypeObject() ? asTypeObject()->clasp : asSingleObject()->getClass(); +} + +TaggedProto +TypeObjectKey::proto() +{ + return isTypeObject() ? asTypeObject()->proto : asSingleObject()->getProto(); +} + +JSObject * +TypeObjectKey::singleton() +{ + return isTypeObject() ? asTypeObject()->singleton : asSingleObject(); +} + +TypeNewScript * +TypeObjectKey::newScript() +{ + if (isTypeObject()) { + TypeObjectAddendum *addendum = asTypeObject()->addendum; + if (addendum && addendum->isNewScript()) + return addendum->asNewScript(); + } + return NULL; +} + +bool +TypeObjectKey::unknownProperties() +{ + JSContext *cx = jit::GetIonContext()->cx; + TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject(); + if (!type) + MOZ_CRASH(); + return type->unknownProperties(); +} + +HeapTypeSetKey +TypeObjectKey::property(jsid id) +{ + JSContext *cx = jit::GetIonContext()->cx; + TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject(); + if (!type) + MOZ_CRASH(); + HeapTypeSetKey property; + property.actualTypes = type->getProperty(cx, id); + if (!property.actualTypes) + MOZ_CRASH(); + return property; +} + +bool +types::FinishCompilation(JSContext *cx, JSScript *script, jit::ExecutionMode executionMode, + CompilerConstraintList *constraints, RecompileInfo *precompileInfo) +{ + if (constraints->failed()) + return false; + + CompilerOutput co(script, executionMode); + + TypeCompartment &types = cx->compartment()->types; + if (!types.constrainedOutputs) { + types.constrainedOutputs = cx->new_< Vector<CompilerOutput> >(cx); + if (!types.constrainedOutputs) + return false; + } + + uint32_t index = types.constrainedOutputs->length(); + if (!types.constrainedOutputs->append(co)) + return false; + + *precompileInfo = RecompileInfo(index); + + for (size_t i = 0; i < constraints->length(); i++) { + CompilerConstraint *constraint = constraints->get(i); + if (!constraint->generateTypeConstraint(cx, *precompileInfo)) { + types.constrainedOutputs->back().invalidate(); + return false; + } + } + + return true; +} + +namespace { + +// Constraint which triggers recompilation of a script if any type is added to a type set. */ +class ConstraintDataFreeze { public: - RecompileInfo info; - - /* Whether a new type has already been added, triggering recompilation. */ - bool typeAdded; - - TypeConstraintFreeze(RecompileInfo info) - : info(info), typeAdded(false) - {} + ConstraintDataFreeze() {} const char *kind() { return "freeze"; } - void newType(JSContext *cx, TypeSet *source, Type type) - { - if (typeAdded) - return; + bool invalidateOnNewType(Type type) { return true; } + bool invalidateOnNewPropertyState(TypeSet *property) { return false; } + bool invalidateOnNewObjectState(TypeObject *object, bool force) { return false; } - typeAdded = true; - cx->compartment()->types.addPendingRecompile(cx, info); + bool constraintHolds(JSContext *cx, + const HeapTypeSetKey &property, TemporaryTypeSet *expected) + { + return property.actualTypes->isSubset(expected); } }; } /* anonymous namespace */ void -HeapTypeSet::addFreeze(JSContext *cx) +HeapTypeSetKey::freeze(CompilerConstraintList *constraints) { - add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreeze>( - cx->compartment()->types.compiledInfo), false); + constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreeze> >(*this, ConstraintDataFreeze())); } static inline JSValueType @@ -610,33 +804,6 @@ TemporaryTypeSet::getKnownTypeTag() return type; } -JSValueType -HeapTypeSet::getKnownTypeTag(JSContext *cx) -{ - TypeFlags flags = baseFlags(); - JSValueType type; - - if (baseObjectCount()) - type = flags ? JSVAL_TYPE_UNKNOWN : JSVAL_TYPE_OBJECT; - else - type = GetValueTypeFromTypeFlags(flags); - - if (type != JSVAL_TYPE_UNKNOWN) - addFreeze(cx); - - /* - * If the type set is totally empty then it will be treated as unknown, - * but we still need to record the dependency as adding a new type can give - * it a definite type tag. This is not needed if there are enough types - * that the exact tag is unknown, as it will stay unknown as more types are - * added to the set. - */ - DebugOnly<bool> empty = flags == 0 && baseObjectCount() == 0; - JS_ASSERT_IF(empty, type == JSVAL_TYPE_UNKNOWN); - - return type; -} - bool TemporaryTypeSet::mightBeType(JSValueType type) { @@ -649,42 +816,151 @@ TemporaryTypeSet::mightBeType(JSValueType type) return baseFlags() & PrimitiveTypeFlag(type); } +JSValueType +HeapTypeSetKey::knownTypeTag(CompilerConstraintList *constraints) +{ + if (actualTypes->unknown()) + return JSVAL_TYPE_UNKNOWN; + + TypeFlags flags = actualTypes->baseFlags() & ~TYPE_FLAG_ANYOBJECT; + JSValueType type; + + if (actualTypes->unknownObject() || actualTypes->getObjectCount()) + type = flags ? JSVAL_TYPE_UNKNOWN : JSVAL_TYPE_OBJECT; + else + type = GetValueTypeFromTypeFlags(flags); + + if (type != JSVAL_TYPE_UNKNOWN) + freeze(constraints); + + /* + * If the type set is totally empty then it will be treated as unknown, + * but we still need to record the dependency as adding a new type can give + * it a definite type tag. This is not needed if there are enough types + * that the exact tag is unknown, as it will stay unknown as more types are + * added to the set. + */ + JS_ASSERT_IF(actualTypes->empty(), type == JSVAL_TYPE_UNKNOWN); + + return type; +} + +bool +HeapTypeSetKey::notEmpty(CompilerConstraintList *constraints) +{ + if (!actualTypes->empty()) + return true; + freeze(constraints); + return false; +} + +bool +HeapTypeSetKey::knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other) +{ + if (!actualTypes->isSubset(other.actualTypes)) + return false; + freeze(constraints); + return true; +} + +JSObject * +TemporaryTypeSet::getSingleton() +{ + if (baseFlags() != 0 || baseObjectCount() != 1) + return NULL; + + return getSingleObject(0); +} + +JSObject * +HeapTypeSetKey::singleton(CompilerConstraintList *constraints) +{ + if (actualTypes->baseFlags() != 0 || actualTypes->getObjectCount() != 1) + return NULL; + + JSObject *obj = actualTypes->getSingleObject(0); + + if (obj) + freeze(constraints); + + return obj; +} + +bool +HeapTypeSetKey::needsBarrier(CompilerConstraintList *constraints) +{ + bool result = actualTypes->unknownObject() + || actualTypes->getObjectCount() > 0 + || actualTypes->hasAnyFlag(TYPE_FLAG_STRING); + if (!result) + freeze(constraints); + return result; +} + namespace { -/* Constraint which triggers recompilation if an object acquires particular flags. */ -class TypeConstraintFreezeObjectFlags : public TypeConstraint +// Constraint which triggers recompilation if an object acquires particular flags. +class ConstraintDataFreezeObjectFlags { public: - RecompileInfo info; + // Object being queried. + TypeObjectKey *object; - /* Flags we are watching for on this object. */ + // Flags we are watching for on this object. TypeObjectFlags flags; - /* Whether the object has already been marked as having one of the flags. */ - bool marked; - - TypeConstraintFreezeObjectFlags(RecompileInfo info, TypeObjectFlags flags) - : info(info), flags(flags), - marked(false) + ConstraintDataFreezeObjectFlags(TypeObjectKey *object, TypeObjectFlags flags) + : object(object), flags(flags) {} const char *kind() { return "freezeObjectFlags"; } - void newType(JSContext *cx, TypeSet *source, Type type) {} + bool invalidateOnNewType(Type type) { return false; } + bool invalidateOnNewPropertyState(TypeSet *property) { return false; } + bool invalidateOnNewObjectState(TypeObject *object, bool force) { + return flags ? object->hasAnyFlags(flags) : force; + } - void newObjectState(JSContext *cx, TypeObject *object, bool force) + bool constraintHolds(JSContext *cx, + const HeapTypeSetKey &property, TemporaryTypeSet *expected) { - if (!marked && (object->hasAnyFlags(flags) || (!flags && force))) { - marked = true; - cx->compartment()->types.addPendingRecompile(cx, info); - } + // FIXME: There is not yet any way to test if constraints with no + // associated flags (i.e. those invalidated via |force|) still hold. + TypeObject *type = object->isSingleObject() + ? object->asSingleObject()->type() + : object->asTypeObject(); + return !type->hasAnyFlags(flags); } }; } /* anonymous namespace */ bool -TemporaryTypeSet::hasObjectFlags(JSContext *cx, TypeObjectFlags flags) +TypeObjectKey::hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags) +{ + JS_ASSERT(flags); + + JSContext *cx = jit::GetIonContext()->cx; + TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject(); + if (!type) + MOZ_CRASH(); + if (type->hasAnyFlags(flags)) + return true; + + HeapTypeSetKey objectProperty = property(JSID_EMPTY); + constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> >(objectProperty, ConstraintDataFreezeObjectFlags(this, flags))); + return false; +} + +void +TypeObjectKey::watchStateChange(CompilerConstraintList *constraints) +{ + HeapTypeSetKey objectProperty = property(JSID_EMPTY); + constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> >(objectProperty, ConstraintDataFreezeObjectFlags(this, 0))); +} + +bool +TemporaryTypeSet::hasObjectFlags(CompilerConstraintList *constraints, TypeObjectFlags flags) { if (unknownObject()) return true; @@ -696,47 +972,16 @@ TemporaryTypeSet::hasObjectFlags(JSContext *cx, TypeObjectFlags flags) if (baseObjectCount() == 0) return true; - RootedObject obj(cx); unsigned count = getObjectCount(); for (unsigned i = 0; i < count; i++) { - TypeObject *object = getTypeObject(i); - if (!object) { - if (!(obj = getSingleObject(i))) - continue; - if (!(object = obj->getType(cx))) - return true; - } - if (object->hasAnyFlags(flags)) + TypeObjectKey *object = getObject(i); + if (object && object->hasFlags(constraints, flags)) return true; - - /* - * Add a constraint on the the object to pick up changes in the - * object's properties. - */ - HeapTypeSet *types = object->getProperty(cx, JSID_EMPTY); - if (!types) - return true; - types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>( - cx->compartment()->types.compiledInfo, flags), false); } return false; } -bool -HeapTypeSet::HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags) -{ - if (object->hasAnyFlags(flags)) - return true; - - HeapTypeSet *types = object->getProperty(cx, JSID_EMPTY); - if (!types) - return true; - types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>( - cx->compartment()->types.compiledInfo, flags), false); - return false; -} - static inline void ObjectStateChange(ExclusiveContext *cxArg, TypeObject *object, bool markingUnknown, bool force) { @@ -763,49 +1008,63 @@ ObjectStateChange(ExclusiveContext *cxArg, TypeObject *object, bool markingUnkno } } -void -HeapTypeSet::WatchObjectStateChange(JSContext *cx, TypeObject *obj) -{ - JS_ASSERT(!obj->unknownProperties()); - HeapTypeSet *types = obj->getProperty(cx, JSID_EMPTY); - if (!types) - return; - - /* - * Use a constraint which triggers recompilation when markStateChange is - * called, which will set 'force' to true. - */ - types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>( - cx->compartment()->types.compiledInfo, - 0)); -} +static void +CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun); namespace { -class TypeConstraintFreezeConfiguredProperty : public TypeConstraint +class ConstraintDataFreezeConfiguredProperty { public: - RecompileInfo info; + TypeObjectKey *object; - TypeConstraintFreezeConfiguredProperty(RecompileInfo info) - : info(info) + ConstraintDataFreezeConfiguredProperty(TypeObjectKey *object) + : object(object) {} - const char *kind() { return "freezeOwnProperty"; } + const char *kind() { return "freezeConfiguredProperty"; } - void newType(JSContext *cx, TypeSet *source, Type type) {} + bool invalidateOnNewType(Type type) { return false; } + bool invalidateOnNewPropertyState(TypeSet *property) { + return property->configuredProperty(); + } + bool invalidateOnNewObjectState(TypeObject *object, bool force) { return false; } - void newPropertyState(JSContext *cx, TypeSet *source) + bool constraintHolds(JSContext *cx, + const HeapTypeSetKey &property, TemporaryTypeSet *expected) { - if (source->configuredProperty()) - cx->compartment()->types.addPendingRecompile(cx, info); + // Everywhere compiled code depends on definite properties associated + // with a type object's newScript, we need to make sure there are + // constraints in place which will mark those properties as configured + // should the definite properties be invalidated. + TypeObject *type = object->isSingleObject() + ? object->asSingleObject()->type() + : object->asTypeObject(); + if (type->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) { + type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE; + if (type->hasNewScript()) { + CheckNewScriptProperties(cx, type, type->newScript()->fun); + } else { + JS_ASSERT(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED); + type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE; + } + } + + return !property.actualTypes->configuredProperty(); } }; } /* anonymous namespace */ -static void -CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, HandleFunction fun); +bool +HeapTypeSetKey::configured(CompilerConstraintList *constraints, TypeObjectKey *type) +{ + if (actualTypes->configuredProperty()) + return true; + + constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeConfiguredProperty> >(*this, ConstraintDataFreezeConfiguredProperty(type))); + return false; +} bool TypeObject::incrementTenureCount() @@ -822,46 +1081,6 @@ TypeObject::incrementTenureCount() return count >= MaxJITAllocTenures; } -bool -HeapTypeSet::isConfiguredProperty(JSContext *cx, TypeObject *object) -{ - /* - * Everywhere compiled code depends on definite properties associated with - * a type object's newScript, we need to make sure there are constraints - * in place which will mark those properties as configured should the - * definite properties be invalidated. - */ - if (object->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) { - object->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE; - if (object->hasNewScript()) { - Rooted<TypeObject*> typeObj(cx, object); - RootedFunction fun(cx, object->newScript()->fun); - CheckNewScriptProperties(cx, typeObj, fun); - } else { - JS_ASSERT(object->flags & OBJECT_FLAG_ADDENDUM_CLEARED); - object->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE; - } - } - - if (configuredProperty()) - return true; - - add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeConfiguredProperty>( - cx->compartment()->types.compiledInfo), false); - return false; -} - -bool -HeapTypeSet::knownNonEmpty(JSContext *cx) -{ - if (baseFlags() != 0 || baseObjectCount() != 0) - return true; - - addFreeze(cx); - - return false; -} - bool TemporaryTypeSet::filtersType(const TemporaryTypeSet *other, Type filteredType) const { @@ -890,7 +1109,7 @@ TemporaryTypeSet::filtersType(const TemporaryTypeSet *other, Type filteredType) } TemporaryTypeSet::DoubleConversion -TemporaryTypeSet::convertDoubleElements(JSContext *cx) +TemporaryTypeSet::convertDoubleElements(CompilerConstraintList *constraints) { if (unknownObject() || !getObjectCount()) return AmbiguousDoubleConversion; @@ -900,33 +1119,25 @@ TemporaryTypeSet::convertDoubleElements(JSContext *cx) bool dontConvert = false; for (unsigned i = 0; i < getObjectCount(); i++) { - TypeObject *type = getTypeObject(i); - if (!type) { - if (JSObject *obj = getSingleObject(i)) { - type = obj->getType(cx); - if (!type) - return AmbiguousDoubleConversion; - } else { - continue; - } - } + TypeObjectKey *type = getObject(i); + if (!type) + continue; if (type->unknownProperties()) { alwaysConvert = false; continue; } - HeapTypeSet *types = type->getProperty(cx, JSID_VOID); - if (!types) - return AmbiguousDoubleConversion; - - types->addFreeze(cx); + HeapTypeSetKey property = type->property(JSID_VOID); + property.freeze(constraints); // We can't convert to double elements for objects which do not have // double in their element types (as the conversion may render the type // information incorrect), nor for non-array objects (as their elements // may point to emptyObjectElements, which cannot be converted). - if (!types->hasType(Type::DoubleType()) || type->clasp != &ArrayObject::class_) { + if (!property.actualTypes->hasType(Type::DoubleType()) || + type->clasp() != &ArrayObject::class_) + { dontConvert = true; alwaysConvert = false; continue; @@ -935,8 +1146,8 @@ TemporaryTypeSet::convertDoubleElements(JSContext *cx) // Only bother with converting known packed arrays whose possible // element types are int or double. Other arrays require type tests // when elements are accessed regardless of the conversion. - if (types->getKnownTypeTag(cx) == JSVAL_TYPE_DOUBLE && - !HeapTypeSet::HasObjectFlags(cx, type, OBJECT_FLAG_NON_PACKED)) + if (property.knownTypeTag(constraints) == JSVAL_TYPE_DOUBLE && + !type->hasFlags(constraints, OBJECT_FLAG_NON_PACKED)) { maybeConvert = true; } else { @@ -955,17 +1166,6 @@ TemporaryTypeSet::convertDoubleElements(JSContext *cx) return DontConvertToDoubles; } -bool -HeapTypeSet::knownSubset(JSContext *cx, HeapTypeSet *other) -{ - if (!isSubset(other)) - return false; - - addFreeze(cx); - - return true; -} - const Class * TemporaryTypeSet::getKnownClass() { @@ -1086,61 +1286,23 @@ TemporaryTypeSet::getCommonPrototype() return proto; } -JSObject * -TemporaryTypeSet::getSingleton() -{ - if (baseFlags() != 0 || baseObjectCount() != 1) - return NULL; - - return getSingleObject(0); -} - -JSObject * -HeapTypeSet::getSingleton(JSContext *cx) -{ - if (baseFlags() != 0 || baseObjectCount() != 1) - return NULL; - - RootedObject obj(cx, getSingleObject(0)); - - if (obj) - addFreeze(cx); - - return obj; -} - bool -HeapTypeSet::needsBarrier(JSContext *cx) +TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList *constraints, jsid id) { - bool result = unknownObject() - || getObjectCount() > 0 - || hasAnyFlag(TYPE_FLAG_STRING); - if (!result) - addFreeze(cx); - return result; -} - -bool -TemporaryTypeSet::propertyNeedsBarrier(JSContext *cx, jsid id) -{ - RootedId typeId(cx, IdToTypeId(id)); - if (unknownObject()) return true; for (unsigned i = 0; i < getObjectCount(); i++) { - if (getSingleObject(i)) + TypeObjectKey *type = getObject(i); + if (!type) + continue; + + if (type->unknownProperties()) return true; - if (types::TypeObject *otype = getTypeObject(i)) { - if (otype->unknownProperties()) - return true; - - if (types::HeapTypeSet *propTypes = otype->maybeGetProperty(cx, typeId)) { - if (propTypes->needsBarrier(cx)) - return true; - } - } + HeapTypeSetKey property = type->property(id); + if (property.needsBarrier(constraints)) + return true; } return false; @@ -1193,7 +1355,6 @@ class TypeConstraintFreezeStack : public TypeConstraint TypeCompartment::TypeCompartment() { PodZero(this); - compiledInfo.outputIndex = RecompileInfo::NoCompilerRunning; } void @@ -1471,18 +1632,16 @@ ClassCanHaveExtraProperties(const Class *clasp) } static inline bool -PrototypeHasIndexedProperty(JSContext *cx, JSObject *obj) +PrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSObject *obj) { do { - TypeObject *type = obj->getType(cx); - if (!type) - return true; - if (ClassCanHaveExtraProperties(type->clasp)) + TypeObjectKey *type = TypeObjectKey::get(obj); + if (ClassCanHaveExtraProperties(type->clasp())) return true; if (type->unknownProperties()) return true; - HeapTypeSet *indexTypes = type->getProperty(cx, JSID_VOID); - if (!indexTypes || indexTypes->isConfiguredProperty(cx, type) || indexTypes->knownNonEmpty(cx)) + HeapTypeSetKey index = type->property(JSID_VOID); + if (index.configured(constraints, type) || index.notEmpty(constraints)) return true; obj = obj->getProto(); } while (obj); @@ -1491,20 +1650,19 @@ PrototypeHasIndexedProperty(JSContext *cx, JSObject *obj) } bool -types::ArrayPrototypeHasIndexedProperty(JSContext *cx, HandleScript script) +types::ArrayPrototypeHasIndexedProperty(CompilerConstraintList *constraints, + HandleScript script) { - if (!cx->typeInferenceEnabled() || !script->compileAndGo) - return true; - - JSObject *proto = script->global().getOrCreateArrayPrototype(cx); + JSObject *proto = script->global().getOrCreateArrayPrototype(jit::GetIonContext()->cx); if (!proto) return true; - return PrototypeHasIndexedProperty(cx, proto); + return PrototypeHasIndexedProperty(constraints, proto); } bool -types::TypeCanHaveExtraIndexedProperties(JSContext *cx, TemporaryTypeSet *types) +types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints, + TemporaryTypeSet *types) { const Class *clasp = types->getKnownClass(); @@ -1514,14 +1672,14 @@ types::TypeCanHaveExtraIndexedProperties(JSContext *cx, TemporaryTypeSet *types) if (!clasp || (ClassCanHaveExtraProperties(clasp) && !IsTypedArrayClass(clasp))) return true; - if (types->hasObjectFlags(cx, types::OBJECT_FLAG_SPARSE_INDEXES)) + if (types->hasObjectFlags(constraints, types::OBJECT_FLAG_SPARSE_INDEXES)) return true; JSObject *proto = types->getCommonPrototype(); if (!proto) return true; - return PrototypeHasIndexedProperty(cx, proto); + return PrototypeHasIndexedProperty(constraints, proto); } bool @@ -1622,35 +1780,9 @@ void TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info) { CompilerOutput *co = info.compilerOutput(cx); - if (!co) + if (!co || !co->isValid() || co->pendingInvalidation()) return; - if (co->pendingRecompilation) - return; - - if (co->isValid()) - CancelOffThreadIonCompile(cx->compartment(), co->script); - - if (compiledInfo.outputIndex == info.outputIndex) { - /* Tell Ion to discard generated code when it's done. */ - JS_ASSERT(compiledInfo.outputIndex != RecompileInfo::NoCompilerRunning); - JS_ASSERT(co->kind() == CompilerOutput::Ion || co->kind() == CompilerOutput::ParallelIon); - co->invalidate(); - return; - } - - if (!co->isValid()) { - JS_ASSERT(co->script == NULL); - return; - } - -#if defined(JS_ION) - if (!co->script->hasAnyIonScript()) { - /* Scripts which haven't been compiled yet don't need to be recompiled. */ - return; - } -#endif - if (!pendingRecompiles) { pendingRecompiles = cx->new_< Vector<RecompileInfo> >(cx); if (!pendingRecompiles) { @@ -1659,21 +1791,15 @@ TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info) } } -#if DEBUG - for (size_t i = 0; i < pendingRecompiles->length(); i++) { - RecompileInfo pr = (*pendingRecompiles)[i]; - JS_ASSERT(info.outputIndex != pr.outputIndex); - } -#endif - if (!pendingRecompiles->append(info)) { cx->compartment()->types.setPendingNukeTypes(cx); return; } - InferSpew(ISpewOps, "addPendingRecompile: %p:%s:%d", co->script, co->script->filename(), co->script->lineno); + InferSpew(ISpewOps, "addPendingRecompile: %p:%s:%d", + co->script(), co->script()->filename(), co->script()->lineno); - co->setPendingRecompilation(); + co->setPendingInvalidation(); } void @@ -1697,30 +1823,9 @@ TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script) addPendingRecompile(cx, script->parallelIonScript()->recompileInfo()); #endif - /* - * Remind Ion not to save the compile code if generating type - * inference information mid-compilation causes an invalidation of the - * script being compiled. - */ - if (compiledInfo.outputIndex != RecompileInfo::NoCompilerRunning) { - CompilerOutput *co = compiledInfo.compilerOutput(cx); - if (!co) { - if (script->compartment() != cx->compartment()) - MOZ_CRASH(); - return; - } - - JS_ASSERT(co->kind() == CompilerOutput::Ion || co->kind() == CompilerOutput::ParallelIon); - - if (co->script == script) - co->invalidate(); - } - - /* - * When one script is inlined into another the caller listens to state - * changes on the callee's script, so trigger these to force recompilation - * of any such callers. - */ + // When one script is inlined into another the caller listens to state + // changes on the callee's script, so trigger these to force recompilation + // of any such callers. if (script->function() && !script->function()->hasLazyType()) ObjectStateChange(cx, script->function()->type(), false, true); } @@ -2781,8 +2886,10 @@ types::AddClearDefiniteFunctionUsesInScript(JSContext *cx, TypeObject *type, * newScript on the type after they were cleared by a GC. */ static void -CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, HandleFunction fun) +CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun) { + JS_ASSERT(cx->compartment()->activeAnalysis); + #ifdef JS_ION if (type->unknownProperties()) return; @@ -2832,8 +2939,9 @@ CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, HandleFunction fu * than we will use for subsequent new objects. Generate an object with the * appropriate final shape. */ + Rooted<TypeObject *> rootedType(cx, type); RootedShape shape(cx, baseobj->lastProperty()); - baseobj = NewReshapedObject(cx, type, baseobj->getParent(), kind, shape); + baseobj = NewReshapedObject(cx, rootedType, baseobj->getParent(), kind, shape); if (!baseobj || !type->addDefiniteProperties(cx, baseobj) || !initializerList.append(done)) @@ -3706,8 +3814,6 @@ TypeCompartment::sweep(FreeOp *fop) pendingArray = NULL; pendingCapacity = 0; - - sweepCompilerOutputs(fop, true); } void @@ -3732,36 +3838,15 @@ TypeCompartment::sweepShapes(FreeOp *fop) } void -TypeCompartment::sweepCompilerOutputs(FreeOp *fop, bool discardConstraints) +TypeCompartment::clearCompilerOutputs(FreeOp *fop) { if (constrainedOutputs) { - if (discardConstraints) { - JS_ASSERT(compiledInfo.outputIndex == RecompileInfo::NoCompilerRunning); -#if DEBUG - for (unsigned i = 0; i < constrainedOutputs->length(); i++) { - CompilerOutput &co = (*constrainedOutputs)[i]; - JS_ASSERT(!co.isValid()); - } -#endif - - fop->delete_(constrainedOutputs); - constrainedOutputs = NULL; - } else { - // Constraints have captured an index to the constrained outputs - // vector. Thus, we invalidate all compilations except the one - // which is potentially running now. - size_t len = constrainedOutputs->length(); - for (unsigned i = 0; i < len; i++) { - if (i != compiledInfo.outputIndex) { - CompilerOutput &co = (*constrainedOutputs)[i]; - JS_ASSERT(!co.isValid()); - co.invalidate(); - } - } - } + fop->delete_(constrainedOutputs); + constrainedOutputs = NULL; } if (pendingRecompiles) { + JS_ASSERT(pendingRecompiles->length() == 0); fop->delete_(pendingRecompiles); pendingRecompiles = NULL; } diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 352aaee7c33c..f59800cfc966 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -104,6 +104,20 @@ class CallObject; namespace jit { struct IonScript; + class IonAllocPolicy; + + enum ExecutionMode { + // Normal JavaScript execution + SequentialExecution, + + // JavaScript code to be executed in parallel worker threads, + // e.g. by ParallelArray + ParallelExecution, + + // MIR analysis performed when invoking 'new' on a script, to determine + // definite properties + DefinitePropertiesAnalysis + }; } namespace analyze { @@ -115,11 +129,7 @@ namespace types { class TypeCompartment; class TypeSet; -/* Type set entry for either a JSObject with singleton type or a non-singleton TypeObject. */ -struct TypeObjectKey { - static intptr_t keyBits(TypeObjectKey *obj) { return (intptr_t) obj; } - static TypeObjectKey *getKey(TypeObjectKey *obj) { return obj; } -}; +struct TypeObjectKey; /* * Information about a single concrete type. We pack this into a single word, @@ -260,7 +270,7 @@ public: /* * For constraints attached to an object property's type set, mark the - * property as having been configured or received an own property. + * property as having been configured. */ virtual void newPropertyState(JSContext *cx, TypeSet *source) {} @@ -553,48 +563,10 @@ class HeapTypeSet : public TypeSet { public: HeapTypeSet() { flags |= TYPE_FLAG_HEAP_SET; } - - /* Completely freeze the contents of this type set. */ - void addFreeze(JSContext *cx); - - /* - * Watch for a generic object state change on a type object. This currently - * includes reallocations of slot pointers for global objects, and changes - * to newScript data on types. - */ - static void WatchObjectStateChange(JSContext *cx, TypeObject *object); - - /* Whether an object has any of a set of flags. */ - static bool HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags); - - /* - * For type sets on a property, return true if the property has any 'own' - * values assigned. If configurable is set, return 'true' if the property - * has additionally been reconfigured as non-configurable, non-enumerable - * or non-writable (this only applies to properties that have changed after - * having been created, not to e.g. properties non-writable on creation). - */ - bool isConfiguredProperty(JSContext *cx, TypeObject *object); - - /* Get whether this type set is non-empty. */ - bool knownNonEmpty(JSContext *cx); - - /* Get whether this type set is known to be a subset of other. */ - bool knownSubset(JSContext *cx, HeapTypeSet *other); - - /* Get the single value which can appear in this type set, otherwise NULL. */ - JSObject *getSingleton(JSContext *cx); - - /* - * Whether a location with this TypeSet needs a write barrier (i.e., whether - * it can hold GC things). The type set is frozen if no barrier is needed. - */ - bool needsBarrier(JSContext *cx); - - /* Get any type tag which all values in this set must have. */ - JSValueType getKnownTypeTag(JSContext *cx); }; +class CompilerConstraintList; + class TemporaryTypeSet : public TypeSet { public: @@ -644,7 +616,7 @@ class TemporaryTypeSet : public TypeSet } /* Whether the type set contains objects with any of a set of flags. */ - bool hasObjectFlags(JSContext *cx, TypeObjectFlags flags); + bool hasObjectFlags(CompilerConstraintList *constraints, TypeObjectFlags flags); /* Get the class shared by all objects in this set, or NULL. */ const Class *getKnownClass(); @@ -668,7 +640,7 @@ class TemporaryTypeSet : public TypeSet JSObject *getSingleton(); /* Whether any objects in the type set needs a barrier on id. */ - bool propertyNeedsBarrier(JSContext *cx, jsid id); + bool propertyNeedsBarrier(CompilerConstraintList *constraints, jsid id); /* * Whether this set contains all types in other, except (possibly) the @@ -694,7 +666,7 @@ class TemporaryTypeSet : public TypeSet * Whether known double optimizations are possible for element accesses on * objects in this type set. */ - DoubleConversion convertDoubleElements(JSContext *cx); + DoubleConversion convertDoubleElements(CompilerConstraintList *constraints); }; inline StackTypeSet * @@ -993,15 +965,6 @@ struct TypeObject : gc::BarrieredCell<TypeObject> inline unsigned getPropertyCount(); inline Property *getProperty(unsigned i); - /* Get the typed array element type if clasp is a typed array. */ - inline int getTypedArrayType(); - - /* - * Get the global object which all objects of this type are parented to, - * or NULL if there is none known. - */ - //inline JSObject *getGlobal(); - /* Tenure counter management. */ /* @@ -1119,11 +1082,11 @@ UseNewTypeForClone(JSFunction *fun); * indexed property. */ bool -ArrayPrototypeHasIndexedProperty(JSContext *cx, HandleScript script); +ArrayPrototypeHasIndexedProperty(CompilerConstraintList *constraints, HandleScript script); /* Whether obj or any of its prototypes have an indexed property. */ bool -TypeCanHaveExtraIndexedProperties(JSContext *cx, TemporaryTypeSet *types); +TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints, TemporaryTypeSet *types); /* Persistent type information for a script, retained across GCs. */ class TypeScript @@ -1204,56 +1167,116 @@ typedef HashMap<ObjectTableKey,ObjectTableEntry,ObjectTableKey,SystemAllocPolicy struct AllocationSiteKey; typedef HashMap<AllocationSiteKey,ReadBarriered<TypeObject>,AllocationSiteKey,SystemAllocPolicy> AllocationSiteTable; +class HeapTypeSetKey; + +/* Type set entry for either a JSObject with singleton type or a non-singleton TypeObject. */ +struct TypeObjectKey { + static intptr_t keyBits(TypeObjectKey *obj) { return (intptr_t) obj; } + static TypeObjectKey *getKey(TypeObjectKey *obj) { return obj; } + + static TypeObjectKey *get(JSObject *obj) { + JS_ASSERT(obj); + return (TypeObjectKey *) (uintptr_t(obj) | 1); + } + static TypeObjectKey *get(TypeObject *obj) { + JS_ASSERT(obj); + return (TypeObjectKey *) obj; + } + + bool isTypeObject() { + return (uintptr_t(this) & 1) == 0; + } + bool isSingleObject() { + return (uintptr_t(this) & 1) != 0; + } + + TypeObject *asTypeObject() { + JS_ASSERT(isTypeObject()); + return (TypeObject *) this; + } + JSObject *asSingleObject() { + JS_ASSERT(isSingleObject()); + return (JSObject *) (uintptr_t(this) & ~1); + } + + const Class *clasp(); + TaggedProto proto(); + JSObject *singleton(); + TypeNewScript *newScript(); + + bool unknownProperties(); + bool hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags); + void watchStateChange(CompilerConstraintList *constraints); + HeapTypeSetKey property(jsid id); +}; + +class HeapTypeSetKey +{ + public: + HeapTypeSet *actualTypes; + + void freeze(CompilerConstraintList *constraints); + JSValueType knownTypeTag(CompilerConstraintList *constraints); + bool configured(CompilerConstraintList *constraints, TypeObjectKey *type); + bool notEmpty(CompilerConstraintList *constraints); + bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other); + JSObject *singleton(CompilerConstraintList *constraints); + bool needsBarrier(CompilerConstraintList *constraints); +}; + /* * Information about the result of the compilation of a script. This structure * stored in the TypeCompartment is indexed by the RecompileInfo. This - * indirection enable the invalidation of all constraints related to the same - * compilation. The compiler output is build by the AutoEnterCompilation. + * indirection enables the invalidation of all constraints related to the same + * compilation. */ -struct CompilerOutput +class CompilerOutput { - enum Kind { - Ion, - ParallelIon - }; + // If this compilation has not been invalidated, the associated script and + // kind of compilation being performed. + JSScript *script_; + unsigned mode_ : 2; - JSScript *script; + // Whether this compilation is about to be invalidated. + bool pendingInvalidation_ : 1; - // This integer will always be a member of CompilerOutput::Kind, - // but, for portability, bitfields are limited to bool, int, and - // unsigned int. You should really use the accessor below. - unsigned kindInt : 2; - bool pendingRecompilation : 1; + public: + CompilerOutput() + : script_(NULL), mode_(0), pendingInvalidation_(false) + {} - CompilerOutput(); + CompilerOutput(JSScript *script, jit::ExecutionMode mode) + : script_(script), mode_(mode), pendingInvalidation_(false) + {} - Kind kind() const { return static_cast<Kind>(kindInt); } - void setKind(Kind k) { kindInt = k; } + JSScript *script() const { return script_; } + inline jit::ExecutionMode mode() const { return static_cast<jit::ExecutionMode>(mode_); } - jit::IonScript *ion() const; + inline jit::IonScript *ion() const; - bool isValid() const; - - void setPendingRecompilation() { - pendingRecompilation = true; + bool isValid() const { + return script_ != NULL; } void invalidate() { - script = NULL; + script_ = NULL; } - bool isInvalidated() const { - return script == NULL; + + void setPendingInvalidation() { + pendingInvalidation_ = true; + } + bool pendingInvalidation() { + return pendingInvalidation_; } }; -struct RecompileInfo +class RecompileInfo { - static const uint32_t NoCompilerRunning = uint32_t(-1); uint32_t outputIndex; - RecompileInfo() - : outputIndex(NoCompilerRunning) - { - } + public: + RecompileInfo(uint32_t outputIndex = uint32_t(-1)) + : outputIndex(outputIndex) + {} bool operator == (const RecompileInfo &o) const { return outputIndex == o.outputIndex; @@ -1293,13 +1316,6 @@ struct TypeCompartment /* Pending recompilations to perform before execution of JIT code can resume. */ Vector<RecompileInfo> *pendingRecompiles; - /* - * Script currently being compiled. All constraints which look for type - * changes inducing recompilation are keyed to this script. Note: script - * compilation is not reentrant. - */ - RecompileInfo compiledInfo; - /* Table for referencing types of objects keyed to an allocation site. */ AllocationSiteTable *allocationSiteTable; @@ -1359,7 +1375,7 @@ struct TypeCompartment void sweep(FreeOp *fop); void sweepShapes(FreeOp *fop); - void sweepCompilerOutputs(FreeOp *fop, bool discardConstraints); + void clearCompilerOutputs(FreeOp *fop); void finalizeObjects(); diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index feb8bbb2d9e9..2cabf38efb43 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -16,6 +16,7 @@ #include "jsanalyze.h" #include "builtin/ParallelArray.h" +#include "jit/ExecutionModeInlines.h" #include "vm/ArrayObject.h" #include "vm/BooleanObject.h" #include "vm/NumberObject.h" @@ -87,63 +88,21 @@ namespace types { // CompilerOutput & RecompileInfo ///////////////////////////////////////////////////////////////////// -inline -CompilerOutput::CompilerOutput() - : script(NULL), - kindInt(Ion), - pendingRecompilation(false) -{ -} - inline jit::IonScript * CompilerOutput::ion() const { #ifdef JS_ION + // Note: If type constraints are generated before compilation has finished + // (i.e. after IonBuilder but before CodeGenerator::link) then a valid + // CompilerOutput may not yet have an associated IonScript. JS_ASSERT(isValid()); - switch (kind()) { - case Ion: return script->ionScript(); - case ParallelIon: return script->parallelIonScript(); - } + jit::IonScript *ion = jit::GetIonScript(script(), mode()); + JS_ASSERT(ion != ION_COMPILING_SCRIPT); + return ion; #endif MOZ_ASSUME_UNREACHABLE("Invalid kind of CompilerOutput"); } -inline bool -CompilerOutput::isValid() const -{ - if (!script) - return false; - -#if defined(DEBUG) && defined(JS_ION) - TypeCompartment &types = script->compartment()->types; -#endif - - switch (kind()) { - case Ion: -#ifdef JS_ION - if (script->hasIonScript()) { - JS_ASSERT(this == script->ionScript()->recompileInfo().compilerOutput(types)); - return true; - } - if (script->isIonCompilingOffThread()) - return true; -#endif - return false; - - case ParallelIon: -#ifdef JS_ION - if (script->hasParallelIonScript()) { - JS_ASSERT(this == script->parallelIonScript()->recompileInfo().compilerOutput(types)); - return true; - } - if (script->isParallelIonCompilingOffThread()) - return true; -#endif - return false; - } - return false; -} - inline CompilerOutput* RecompileInfo::compilerOutput(TypeCompartment &types) const { @@ -349,75 +308,6 @@ struct AutoEnterAnalysis } }; -/* - * Structure marking the currently compiled script, for constraints which can - * trigger recompilation. - */ -struct AutoEnterCompilation -{ - JSContext *cx; - RecompileInfo &info; - CompilerOutput::Kind kind; - - AutoEnterCompilation(JSContext *cx, CompilerOutput::Kind kind) - : cx(cx), - info(cx->compartment()->types.compiledInfo), - kind(kind) - { - JS_ASSERT(cx->compartment()->activeAnalysis); - JS_ASSERT(info.outputIndex == RecompileInfo::NoCompilerRunning); - } - - bool init(JSScript *script) - { - CompilerOutput co; - co.script = script; - co.setKind(kind); - - JS_ASSERT(!co.isValid()); - TypeCompartment &types = cx->compartment()->types; - if (!types.constrainedOutputs) { - types.constrainedOutputs = cx->new_< Vector<CompilerOutput> >(cx); - if (!types.constrainedOutputs) { - types.setPendingNukeTypes(cx); - return false; - } - } - - info.outputIndex = types.constrainedOutputs->length(); - // I hope we GC before we reach 64k of compilation attempts. - if (info.outputIndex >= RecompileInfo::NoCompilerRunning) - return false; - - if (!types.constrainedOutputs->append(co)) { - info.outputIndex = RecompileInfo::NoCompilerRunning; - return false; - } - return true; - } - - void initExisting(RecompileInfo oldInfo) - { - // Initialize the active compilation index from that produced during a - // previous compilation, for finishing an off thread compilation. - info = oldInfo; - } - - ~AutoEnterCompilation() - { - // Handle failure cases of init. - if (info.outputIndex >= RecompileInfo::NoCompilerRunning) - return; - - JS_ASSERT(info.outputIndex < cx->compartment()->types.constrainedOutputs->length()); - CompilerOutput *co = info.compilerOutput(cx); - if (!co->isValid()) - co->invalidate(); - - info.outputIndex = RecompileInfo::NoCompilerRunning; - } -}; - ///////////////////////////////////////////////////////////////////// // Interface functions ///////////////////////////////////////////////////////////////////// @@ -1315,14 +1205,14 @@ inline JSObject * TypeSet::getSingleObject(unsigned i) const { TypeObjectKey *key = getObject(i); - return (uintptr_t(key) & 1) ? (JSObject *)(uintptr_t(key) ^ 1) : NULL; + return (key && key->isSingleObject()) ? key->asSingleObject() : NULL; } inline TypeObject * TypeSet::getTypeObject(unsigned i) const { TypeObjectKey *key = getObject(i); - return (key && !(uintptr_t(key) & 1)) ? (TypeObject *) key : NULL; + return (key && key->isTypeObject()) ? key->asTypeObject() : NULL; } inline bool @@ -1470,14 +1360,6 @@ TypeObject::getProperty(unsigned i) return propertySet[i]; } -inline int -TypeObject::getTypedArrayType() -{ - if (IsTypedArrayClass(clasp)) - return clasp - &TypedArrayObject::classes[0]; - return ScalarTypeRepresentation::TYPE_MAX; -} - inline void TypeObjectAddendum::writeBarrierPre(TypeObjectAddendum *type) { @@ -1510,6 +1392,44 @@ TypeNewScript::writeBarrierPre(TypeNewScript *newScript) #endif } +// Allocate a CompilerOutput for a finished compilation and generate the type +// constraints for the compilation. Returns whether the type constraints +// still hold. +bool +FinishCompilation(JSContext *cx, JSScript *script, jit::ExecutionMode executionMode, + CompilerConstraintList *constraints, RecompileInfo *precompileInfo); + +class CompilerConstraint; +class CompilerConstraintList +{ + // Generated constraints. + Vector<CompilerConstraint *, 0, jit::IonAllocPolicy> constraints; + + // OOM during generation of some constraint. + bool failed_; + + public: + CompilerConstraintList() + : failed_(false) + {} + + void add(CompilerConstraint *constraint); + + size_t length() { + return constraints.length(); + } + CompilerConstraint *get(size_t i) { + return constraints[i]; + } + + bool failed() { + return failed_; + } + void setFailed() { + failed_ = true; + } +}; + } } /* namespace js::types */ inline bool