Bug 921902 - Separate generation and attaching of heap property type constraints, r=jandem.

This commit is contained in:
Brian Hackett 2013-10-03 21:44:13 -06:00
parent 6ff2ca8bc7
commit 08ab10373a
21 changed files with 1125 additions and 1313 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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());

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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() {

View File

@ -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()) {

View File

@ -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,

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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