mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-09 04:25:38 +00:00
Bug 921902 - Separate generation and attaching of heap property type constraints, r=jandem.
This commit is contained in:
parent
6ff2ca8bc7
commit
08ab10373a
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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() {
|
||||
|
@ -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()) {
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
212
js/src/jsinfer.h
212
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();
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user