mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Back out 755ecb4d6e2c and 7ea09c8bf385 (bug 925962) for bustage
CLOSED TREE
This commit is contained in:
parent
8a94a69b2e
commit
f885d57f27
@ -1596,9 +1596,7 @@ IonCompile(JSContext *cx, JSScript *script,
|
||||
AutoFlushCache afc("IonCompile", cx->runtime()->ionRuntime());
|
||||
|
||||
AutoTempAllocatorRooter root(cx, temp);
|
||||
types::CompilerConstraintList *constraints = types::NewCompilerConstraintList();
|
||||
if (!constraints)
|
||||
return AbortReason_Alloc;
|
||||
types::CompilerConstraintList *constraints = alloc->new_<types::CompilerConstraintList>();
|
||||
|
||||
IonBuilder *builder = alloc->new_<IonBuilder>(cx, temp, graph, constraints,
|
||||
&inspector, info, baselineFrame);
|
||||
@ -2340,8 +2338,7 @@ jit::Invalidate(types::TypeCompartment &types, FreeOp *fop,
|
||||
size_t numInvalidations = 0;
|
||||
for (size_t i = 0; i < invalid.length(); i++) {
|
||||
const types::CompilerOutput &co = *invalid[i].compilerOutput(types);
|
||||
if (!co.isValid())
|
||||
continue;
|
||||
JS_ASSERT(co.isValid());
|
||||
|
||||
CancelOffThreadIonCompile(co.script()->compartment(), co.script());
|
||||
|
||||
@ -2371,9 +2368,7 @@ 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);
|
||||
if (!co.isValid())
|
||||
continue;
|
||||
|
||||
JS_ASSERT(co.isValid());
|
||||
ExecutionMode executionMode = co.mode();
|
||||
JSScript *script = co.script();
|
||||
IonScript *ionScript = co.ion();
|
||||
|
@ -2082,9 +2082,9 @@ jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
|
||||
|
||||
AutoTempAllocatorRooter root(cx, &temp);
|
||||
|
||||
types::CompilerConstraintList *constraints = types::NewCompilerConstraintList();
|
||||
types::CompilerConstraintList constraints;
|
||||
BaselineInspector inspector(cx, fun->nonLazyScript());
|
||||
IonBuilder builder(cx, &temp, &graph, constraints,
|
||||
IonBuilder builder(cx, &temp, &graph, &constraints,
|
||||
&inspector, &info, /* baselineFrame = */ nullptr);
|
||||
|
||||
if (!builder.build()) {
|
||||
|
@ -47,10 +47,6 @@ IonBuilder::IonBuilder(JSContext *cx, TempAllocator *temp, MIRGraph *graph,
|
||||
abortReason_(AbortReason_Disable),
|
||||
constraints_(constraints),
|
||||
analysis_(info->script()),
|
||||
thisTypes(NULL),
|
||||
argTypes(NULL),
|
||||
typeArray(NULL),
|
||||
typeArrayHint(0),
|
||||
loopDepth_(loopDepth),
|
||||
callerResumePoint_(nullptr),
|
||||
callerBuilder_(nullptr),
|
||||
@ -515,12 +511,6 @@ IonBuilder::init()
|
||||
if (!script()->ensureHasBytecodeTypeMap(cx))
|
||||
return false;
|
||||
|
||||
if (!types::TypeScript::FreezeTypeSets(constraints(), script(),
|
||||
&thisTypes, &argTypes, &typeArray))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!analysis().init(cx))
|
||||
return false;
|
||||
|
||||
@ -625,6 +615,8 @@ IonBuilder::build()
|
||||
if (!processIterators())
|
||||
return false;
|
||||
|
||||
types::TypeScript::AddFreezeConstraints(cx, script());
|
||||
|
||||
JS_ASSERT(loopDepth_ == 0);
|
||||
abortReason_ = AbortReason_NoAbort;
|
||||
return true;
|
||||
@ -773,6 +765,7 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi
|
||||
if (!traverseBytecode())
|
||||
return false;
|
||||
|
||||
types::TypeScript::AddFreezeConstraints(cx, script());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -843,25 +836,23 @@ IonBuilder::initParameters()
|
||||
// interpreter and didn't accumulate type information, try to use that OSR
|
||||
// frame to determine possible initial types for 'this' and parameters.
|
||||
|
||||
if (thisTypes->empty() && baselineFrame_) {
|
||||
if (!thisTypes->addType(types::GetValueType(baselineFrame_->thisValue()), temp_->lifoAlloc()))
|
||||
return false;
|
||||
}
|
||||
types::StackTypeSet *thisTypes = types::TypeScript::ThisTypes(script());
|
||||
if (thisTypes->empty() && baselineFrame_)
|
||||
thisTypes->addType(cx, types::GetValueType(baselineFrame_->thisValue()));
|
||||
|
||||
MParameter *param = MParameter::New(MParameter::THIS_SLOT, thisTypes);
|
||||
MParameter *param = MParameter::New(MParameter::THIS_SLOT, cloneTypeSet(thisTypes));
|
||||
current->add(param);
|
||||
current->initSlot(info().thisSlot(), param);
|
||||
|
||||
for (uint32_t i = 0; i < info().nargs(); i++) {
|
||||
types::TemporaryTypeSet *types = &argTypes[i];
|
||||
if (types->empty() && baselineFrame_ &&
|
||||
types::StackTypeSet *argTypes = types::TypeScript::ArgTypes(script(), i);
|
||||
if (argTypes->empty() && baselineFrame_ &&
|
||||
!script_->baselineScript()->modifiesArguments())
|
||||
{
|
||||
if (!types->addType(types::GetValueType(baselineFrame_->argv()[i]), temp_->lifoAlloc()))
|
||||
return false;
|
||||
argTypes->addType(cx, types::GetValueType(baselineFrame_->argv()[i]));
|
||||
}
|
||||
|
||||
param = MParameter::New(i, types);
|
||||
param = MParameter::New(i, cloneTypeSet(argTypes));
|
||||
current->add(param);
|
||||
current->initSlot(info().argSlotUnchecked(i), param);
|
||||
}
|
||||
@ -953,7 +944,7 @@ IonBuilder::addOsrValueTypeBarrier(uint32_t slot, MInstruction **def_,
|
||||
// No unbox instruction will be added below, so check the type by
|
||||
// adding a type barrier for a singleton type set.
|
||||
types::Type ntype = types::Type::PrimitiveType(ValueTypeFromMIRType(type));
|
||||
typeSet = temp_->lifoAlloc()->new_<types::TemporaryTypeSet>(ntype);
|
||||
typeSet = GetIonContext()->temp->lifoAlloc()->new_<types::TemporaryTypeSet>(ntype);
|
||||
if (!typeSet)
|
||||
return false;
|
||||
MInstruction *barrier = MTypeBarrier::New(def, typeSet);
|
||||
@ -2986,7 +2977,7 @@ IonBuilder::jsop_condswitch()
|
||||
JS_ASSERT(curCase < defaultTarget && defaultTarget <= exitpc);
|
||||
|
||||
// Allocate the current graph state.
|
||||
CFGState state = CFGState::CondSwitch(this, exitpc, defaultTarget);
|
||||
CFGState state = CFGState::CondSwitch(exitpc, defaultTarget);
|
||||
if (!state.condswitch.bodies || !state.condswitch.bodies->init(nbBodies))
|
||||
return ControlStatus_Error;
|
||||
|
||||
@ -2999,12 +2990,12 @@ IonBuilder::jsop_condswitch()
|
||||
}
|
||||
|
||||
IonBuilder::CFGState
|
||||
IonBuilder::CFGState::CondSwitch(IonBuilder *builder, jsbytecode *exitpc, jsbytecode *defaultTarget)
|
||||
IonBuilder::CFGState::CondSwitch(jsbytecode *exitpc, jsbytecode *defaultTarget)
|
||||
{
|
||||
CFGState state;
|
||||
state.state = COND_SWITCH_CASE;
|
||||
state.stopAt = nullptr;
|
||||
state.condswitch.bodies = (FixedList<MBasicBlock *> *)builder->temp_->allocate(
|
||||
state.condswitch.bodies = (FixedList<MBasicBlock *> *)GetIonContext()->temp->allocate(
|
||||
sizeof(FixedList<MBasicBlock *>));
|
||||
state.condswitch.currentIdx = 0;
|
||||
state.condswitch.defaultTarget = defaultTarget;
|
||||
@ -3761,15 +3752,14 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target)
|
||||
{
|
||||
types::StackTypeSet *types = types::TypeScript::ThisTypes(calleeScript);
|
||||
if (!types->unknown()) {
|
||||
MTypeBarrier *barrier =
|
||||
MTypeBarrier::New(callInfo.thisArg(), types->clone(temp_->lifoAlloc()));
|
||||
MTypeBarrier *barrier = MTypeBarrier::New(callInfo.thisArg(), cloneTypeSet(types));
|
||||
current->add(barrier);
|
||||
callInfo.setThis(barrier);
|
||||
}
|
||||
}
|
||||
|
||||
// Start inlining.
|
||||
LifoAlloc *alloc = temp_->lifoAlloc();
|
||||
LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc();
|
||||
CompileInfo *info = alloc->new_<CompileInfo>(calleeScript, target,
|
||||
(jsbytecode *)nullptr, callInfo.constructing(),
|
||||
this->info().executionMode());
|
||||
@ -4911,17 +4901,15 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing)
|
||||
{
|
||||
// If this call has never executed, try to seed the observed type set
|
||||
// based on how the call result is used.
|
||||
types::TemporaryTypeSet *observed = bytecodeTypes(pc);
|
||||
if (observed->empty()) {
|
||||
types::StackTypeSet *observed = types::TypeScript::BytecodeTypes(script(), pc);
|
||||
if (observed->empty() && observed->noConstraints()) {
|
||||
if (BytecodeFlowsToBitop(pc)) {
|
||||
if (!observed->addType(types::Type::Int32Type(), temp_->lifoAlloc()))
|
||||
return false;
|
||||
observed->addType(cx, types::Type::Int32Type());
|
||||
} else if (*GetNextPc(pc) == JSOP_POS) {
|
||||
// Note: this is lame, overspecialized on the code patterns used
|
||||
// by asm.js and should be replaced by a more general mechanism.
|
||||
// See bug 870847.
|
||||
if (!observed->addType(types::Type::DoubleType(), temp_->lifoAlloc()))
|
||||
return false;
|
||||
observed->addType(cx, types::Type::DoubleType());
|
||||
}
|
||||
}
|
||||
|
||||
@ -5053,7 +5041,7 @@ TestAreKnownDOMTypes(JSContext *cx, types::TypeSet *inTypes)
|
||||
|
||||
|
||||
static bool
|
||||
ArgumentTypesMatch(MDefinition *def, types::StackTypeSet *calleeTypes)
|
||||
ArgumentTypesMatch(MDefinition *def, types::TemporaryTypeSet *calleeTypes)
|
||||
{
|
||||
if (def->resultTypeSet()) {
|
||||
JS_ASSERT(def->type() == MIRType_Value || def->mightBeType(def->type()));
|
||||
@ -5082,15 +5070,15 @@ IonBuilder::testNeedsArgumentCheck(JSContext *cx, JSFunction *target, CallInfo &
|
||||
if (!targetScript->types)
|
||||
return true;
|
||||
|
||||
if (!ArgumentTypesMatch(callInfo.thisArg(), types::TypeScript::ThisTypes(targetScript)))
|
||||
if (!ArgumentTypesMatch(callInfo.thisArg(), cloneTypeSet(types::TypeScript::ThisTypes(targetScript))))
|
||||
return true;
|
||||
uint32_t expected_args = Min<uint32_t>(callInfo.argc(), target->nargs);
|
||||
for (size_t i = 0; i < expected_args; i++) {
|
||||
if (!ArgumentTypesMatch(callInfo.getArg(i), types::TypeScript::ArgTypes(targetScript, i)))
|
||||
if (!ArgumentTypesMatch(callInfo.getArg(i), cloneTypeSet(types::TypeScript::ArgTypes(targetScript, i))))
|
||||
return true;
|
||||
}
|
||||
for (size_t i = callInfo.argc(); i < target->nargs; i++) {
|
||||
if (!types::TypeScript::ArgTypes(targetScript, i)->mightBeType(JSVAL_TYPE_UNDEFINED))
|
||||
if (!cloneTypeSet(types::TypeScript::ArgTypes(targetScript, i))->mightBeType(JSVAL_TYPE_UNDEFINED))
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -5261,6 +5249,8 @@ IonBuilder::jsop_eval(uint32_t argc)
|
||||
if (!info().fun())
|
||||
return abort("Direct eval in global code");
|
||||
|
||||
types::TemporaryTypeSet *thisTypes = cloneTypeSet(types::TypeScript::ThisTypes(script()));
|
||||
|
||||
// The 'this' value for the outer and eval scripts must be the
|
||||
// same. This is not guaranteed if a primitive string/number/etc.
|
||||
// is passed through to the eval invoke as the primitive may be
|
||||
@ -5885,7 +5875,7 @@ IonBuilder::newPendingLoopHeader(MBasicBlock *predecessor, jsbytecode *pc, bool
|
||||
: MIRTypeFromValueType(existingValue.extractNonDoubleType());
|
||||
types::Type ntype = types::GetValueType(existingValue);
|
||||
types::TemporaryTypeSet *typeSet =
|
||||
temp_->lifoAlloc()->new_<types::TemporaryTypeSet>(ntype);
|
||||
GetIonContext()->temp->lifoAlloc()->new_<types::TemporaryTypeSet>(ntype);
|
||||
if (!typeSet)
|
||||
return nullptr;
|
||||
phi->addBackedgeType(type, typeSet);
|
||||
@ -6239,9 +6229,10 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc
|
||||
}
|
||||
}
|
||||
|
||||
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
||||
types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
|
||||
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), staticType,
|
||||
name, types, /* updateObserved = */ true);
|
||||
name, baseTypes, /* updateObserved = */ true);
|
||||
types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
|
||||
|
||||
// If the property is permanent, a shape guard isn't necessary.
|
||||
|
||||
@ -6751,8 +6742,9 @@ IonBuilder::getElemTryCache(bool *emitted, MDefinition *obj, MDefinition *index)
|
||||
|
||||
// Emit GetElementCache.
|
||||
|
||||
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
||||
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), obj, nullptr, types);
|
||||
types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
|
||||
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), obj, nullptr, baseTypes);
|
||||
types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
|
||||
|
||||
// Always add a barrier if the index might be a string, so that the cache
|
||||
// can attach stubs for particular properties.
|
||||
@ -6790,17 +6782,19 @@ IonBuilder::getElemTryCache(bool *emitted, MDefinition *obj, MDefinition *index)
|
||||
bool
|
||||
IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index)
|
||||
{
|
||||
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
||||
types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
|
||||
|
||||
if (JSOp(*pc) == JSOP_CALLELEM && !index->mightBeType(MIRType_String)) {
|
||||
if (JSOp(*pc) == JSOP_CALLELEM && !index->mightBeType(MIRType_String) && baseTypes->noConstraints()) {
|
||||
// Indexed call on an element of an array. Populate the observed types
|
||||
// with any objects that could be in the array, to avoid extraneous
|
||||
// type barriers.
|
||||
if (!AddObjectsForPropertyRead(obj, nullptr, types))
|
||||
if (!AddObjectsForPropertyRead(cx, obj, nullptr, baseTypes))
|
||||
return false;
|
||||
}
|
||||
|
||||
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), obj, nullptr, types);
|
||||
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), obj, nullptr, baseTypes);
|
||||
types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
|
||||
|
||||
bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj);
|
||||
|
||||
// Reads which are on holes in the object do not have to bail out if
|
||||
@ -8048,9 +8042,10 @@ IonBuilder::jsop_getprop(PropertyName *name)
|
||||
if (!getPropTryArgumentsLength(&emitted) || emitted)
|
||||
return emitted;
|
||||
|
||||
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
||||
types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
|
||||
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(),
|
||||
current->peek(-1), name, types);
|
||||
current->peek(-1), name, baseTypes);
|
||||
types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
|
||||
|
||||
// Try to hardcode known constants.
|
||||
if (!getPropTryConstant(&emitted, name, types) || emitted)
|
||||
@ -8060,7 +8055,7 @@ IonBuilder::jsop_getprop(PropertyName *name)
|
||||
// the definite properties analysis and not actually emitting code, to
|
||||
// simplify later analysis. Also skip deeper analysis if there are no known
|
||||
// types for this operation, as it will always invalidate when executing.
|
||||
if (info().executionMode() == DefinitePropertiesAnalysis || types->empty()) {
|
||||
if (info().executionMode() == DefinitePropertiesAnalysis || baseTypes->empty()) {
|
||||
MDefinition *obj = current->pop();
|
||||
MCallGetProperty *call = MCallGetProperty::New(obj, name);
|
||||
current->add(call);
|
||||
@ -8994,9 +8989,11 @@ IonBuilder::jsop_setarg(uint32_t arg)
|
||||
op->resultTypeSet() &&
|
||||
op->resultTypeSet()->empty())
|
||||
{
|
||||
JS_ASSERT(op->resultTypeSet() == &argTypes[arg]);
|
||||
if (!argTypes[arg].addType(types::Type::UnknownType(), temp_->lifoAlloc()))
|
||||
return false;
|
||||
types::TypeSet *argTypes = types::TypeScript::ArgTypes(script(), arg);
|
||||
|
||||
// Update both the original and cloned type set.
|
||||
argTypes->addType(cx, types::Type::UnknownType());
|
||||
op->resultTypeSet()->addType(cx, types::Type::UnknownType());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9054,8 +9051,9 @@ IonBuilder::jsop_this()
|
||||
return true;
|
||||
}
|
||||
|
||||
if (thisTypes->getKnownTypeTag() == JSVAL_TYPE_OBJECT ||
|
||||
(thisTypes->empty() && baselineFrame_ && baselineFrame_->thisValue().isObject()))
|
||||
types::TemporaryTypeSet *types = cloneTypeSet(types::TypeScript::ThisTypes(script()));
|
||||
if (types && (types->getKnownTypeTag() == JSVAL_TYPE_OBJECT ||
|
||||
(types->empty() && baselineFrame_ && baselineFrame_->thisValue().isObject())))
|
||||
{
|
||||
// This is safe, because if the entry type of |this| is an object, it
|
||||
// will necessarily be an object throughout the entire function. OSR
|
||||
@ -9469,7 +9467,17 @@ IonBuilder::addShapeGuard(MDefinition *obj, Shape *const shape, BailoutKind bail
|
||||
types::TemporaryTypeSet *
|
||||
IonBuilder::bytecodeTypes(jsbytecode *pc)
|
||||
{
|
||||
return types::TypeScript::BytecodeTypes(script(), pc, &typeArrayHint, typeArray);
|
||||
return cloneTypeSet(types::TypeScript::BytecodeTypes(script(), pc));
|
||||
}
|
||||
|
||||
types::TemporaryTypeSet *
|
||||
IonBuilder::cloneTypeSet(types::StackTypeSet *types)
|
||||
{
|
||||
// Clone a type set so that it can be stored into the MIR and accessed
|
||||
// during off thread compilation. This is necessary because main thread
|
||||
// updates to type sets can race with reads in the compiler backend, and
|
||||
// after bug 804676 this code can be removed.
|
||||
return types->clone(GetIonContext()->temp->lifoAlloc());
|
||||
}
|
||||
|
||||
TypeRepresentationSetHash *
|
||||
|
@ -197,7 +197,7 @@ class IonBuilder : public MIRGenerator
|
||||
static CFGState IfElse(jsbytecode *trueEnd, jsbytecode *falseEnd, MBasicBlock *ifFalse);
|
||||
static CFGState AndOr(jsbytecode *join, MBasicBlock *joinStart);
|
||||
static CFGState TableSwitch(jsbytecode *exitpc, MTableSwitch *ins);
|
||||
static CFGState CondSwitch(IonBuilder *builder, jsbytecode *exitpc, jsbytecode *defaultTarget);
|
||||
static CFGState CondSwitch(jsbytecode *exitpc, jsbytecode *defaultTarget);
|
||||
static CFGState Label(jsbytecode *exitpc);
|
||||
static CFGState Try(jsbytecode *exitpc, MBasicBlock *successor);
|
||||
};
|
||||
@ -531,6 +531,7 @@ class IonBuilder : public MIRGenerator
|
||||
BoolVector &choiceSet);
|
||||
|
||||
// Native inlining helpers.
|
||||
types::StackTypeSet *getOriginalInlineReturnTypeSet();
|
||||
types::TemporaryTypeSet *getInlineReturnTypeSet();
|
||||
MIRType getInlineReturnType();
|
||||
|
||||
@ -644,6 +645,7 @@ class IonBuilder : public MIRGenerator
|
||||
JSObject *foundProto, PropertyName *name);
|
||||
|
||||
types::TemporaryTypeSet *bytecodeTypes(jsbytecode *pc);
|
||||
types::TemporaryTypeSet *cloneTypeSet(types::StackTypeSet *types);
|
||||
|
||||
// Use one of the below methods for updating the current block, rather than
|
||||
// updating |current| directly. setCurrent() should only be used in cases
|
||||
@ -715,9 +717,6 @@ class IonBuilder : public MIRGenerator
|
||||
return analysis_;
|
||||
}
|
||||
|
||||
types::TemporaryTypeSet *thisTypes, *argTypes, *typeArray;
|
||||
uint32_t typeArrayHint;
|
||||
|
||||
GSNCache gsn;
|
||||
|
||||
jsbytecode *pc;
|
||||
|
@ -156,10 +156,16 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSNative native)
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
types::StackTypeSet *
|
||||
IonBuilder::getOriginalInlineReturnTypeSet()
|
||||
{
|
||||
return types::TypeScript::BytecodeTypes(script(), pc);
|
||||
}
|
||||
|
||||
types::TemporaryTypeSet *
|
||||
IonBuilder::getInlineReturnTypeSet()
|
||||
{
|
||||
return bytecodeTypes(pc);
|
||||
return cloneTypeSet(getOriginalInlineReturnTypeSet());
|
||||
}
|
||||
|
||||
MIRType
|
||||
@ -322,7 +328,7 @@ IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode)
|
||||
|
||||
callInfo.unwrapArgs();
|
||||
|
||||
types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet();
|
||||
types::StackTypeSet *returnTypes = getOriginalInlineReturnTypeSet();
|
||||
bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED);
|
||||
bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType());
|
||||
|
||||
@ -340,7 +346,7 @@ IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode)
|
||||
if (!resumeAfter(ins))
|
||||
return InliningStatus_Error;
|
||||
|
||||
if (!pushTypeBarrier(ins, returnTypes, barrier))
|
||||
if (!pushTypeBarrier(ins, cloneTypeSet(returnTypes), barrier))
|
||||
return InliningStatus_Error;
|
||||
|
||||
return InliningStatus_Inlined;
|
||||
|
@ -2564,7 +2564,7 @@ InlinePropertyTable::buildTypeSetForFunction(JSFunction *func) const
|
||||
return nullptr;
|
||||
for (size_t i = 0; i < numEntries(); i++) {
|
||||
if (entries_[i]->func == func) {
|
||||
if (!types->addType(types::Type::ObjectType(entries_[i]->typeObj), alloc))
|
||||
if (!types->addObject(types::Type::ObjectType(entries_[i]->typeObj).objectKey(), alloc))
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@ -2810,11 +2810,11 @@ bool
|
||||
jit::PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
|
||||
types::CompilerConstraintList *constraints,
|
||||
types::TypeObjectKey *object, PropertyName *name,
|
||||
types::TemporaryTypeSet *observed, bool updateObserved)
|
||||
types::StackTypeSet *observed, bool updateObserved)
|
||||
{
|
||||
// If this access has never executed, try to add types to the observed set
|
||||
// according to any property which exists on the object or its prototype.
|
||||
if (updateObserved && observed->empty() && name) {
|
||||
if (updateObserved && observed->empty() && observed->noConstraints() && name) {
|
||||
JSObject *obj = object->singleton() ? object->singleton() : object->proto().toObjectOrNull();
|
||||
|
||||
while (obj) {
|
||||
@ -2829,8 +2829,7 @@ jit::PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
|
||||
if (!property.maybeTypes()->enumerateTypes(&types))
|
||||
return false;
|
||||
if (types.length()) {
|
||||
if (!observed->addType(types[0], GetIonContext()->temp->lifoAlloc()))
|
||||
return false;
|
||||
observed->addType(cx, types[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2847,7 +2846,7 @@ bool
|
||||
jit::PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
|
||||
types::CompilerConstraintList *constraints,
|
||||
MDefinition *obj, PropertyName *name,
|
||||
types::TemporaryTypeSet *observed)
|
||||
types::StackTypeSet *observed)
|
||||
{
|
||||
if (observed->unknown())
|
||||
return false;
|
||||
@ -2924,39 +2923,45 @@ jit::PropertyReadIsIdempotent(types::CompilerConstraintList *constraints,
|
||||
}
|
||||
|
||||
bool
|
||||
jit::AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name,
|
||||
types::TemporaryTypeSet *observed)
|
||||
jit::AddObjectsForPropertyRead(JSContext *cx, MDefinition *obj, PropertyName *name,
|
||||
types::StackTypeSet *observed)
|
||||
{
|
||||
// Add objects to observed which *could* be observed by reading name from obj,
|
||||
// to hopefully avoid unnecessary type barriers and code invalidations.
|
||||
|
||||
LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc();
|
||||
JS_ASSERT(observed->noConstraints());
|
||||
|
||||
types::TemporaryTypeSet *types = obj->resultTypeSet();
|
||||
if (!types || types->unknownObject())
|
||||
return observed->addType(types::Type::AnyObjectType(), alloc);
|
||||
if (!types || types->unknownObject()) {
|
||||
observed->addType(cx, types::Type::AnyObjectType());
|
||||
return true;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < types->getObjectCount(); i++) {
|
||||
types::TypeObjectKey *object = types->getObject(i);
|
||||
types::TypeObject *object;
|
||||
if (!types->getTypeOrSingleObject(cx, i, &object))
|
||||
return false;
|
||||
|
||||
if (!object)
|
||||
continue;
|
||||
|
||||
if (object->unknownProperties())
|
||||
return observed->addType(types::Type::AnyObjectType(), alloc);
|
||||
if (object->unknownProperties()) {
|
||||
observed->addType(cx, types::Type::AnyObjectType());
|
||||
return true;
|
||||
}
|
||||
|
||||
jsid id = name ? NameToId(name) : JSID_VOID;
|
||||
types::HeapTypeSetKey property = object->property(id);
|
||||
types::HeapTypeSet *types = property.maybeTypes();
|
||||
if (!types)
|
||||
continue;
|
||||
types::HeapTypeSet *property = object->getProperty(cx, id);
|
||||
if (property->unknownObject()) {
|
||||
observed->addType(cx, types::Type::AnyObjectType());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (types->unknownObject())
|
||||
return observed->addType(types::Type::AnyObjectType(), alloc);
|
||||
|
||||
for (size_t i = 0; i < types->getObjectCount(); i++) {
|
||||
types::TypeObjectKey *object = types->getObject(i);
|
||||
if (object && !observed->addType(types::Type::ObjectType(object), alloc))
|
||||
return false;
|
||||
for (size_t i = 0; i < property->getObjectCount(); i++) {
|
||||
if (types::TypeObject *object = property->getTypeObject(i))
|
||||
observed->addType(cx, types::Type::ObjectType(object));
|
||||
else if (JSObject *object = property->getSingleObject(i))
|
||||
observed->addType(cx, types::Type::ObjectType(object));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8996,18 +8996,18 @@ MIRType DenseNativeElementType(types::CompilerConstraintList *constraints, MDefi
|
||||
bool PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
|
||||
types::CompilerConstraintList *constraints,
|
||||
types::TypeObjectKey *object, PropertyName *name,
|
||||
types::TemporaryTypeSet *observed, bool updateObserved);
|
||||
types::StackTypeSet *observed, bool updateObserved);
|
||||
bool PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
|
||||
types::CompilerConstraintList *constraints,
|
||||
MDefinition *obj, PropertyName *name,
|
||||
types::TemporaryTypeSet *observed);
|
||||
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(MDefinition *obj, PropertyName *name,
|
||||
types::TemporaryTypeSet *observed);
|
||||
bool AddObjectsForPropertyRead(JSContext *cx, MDefinition *obj, PropertyName *name,
|
||||
types::StackTypeSet *observed);
|
||||
bool PropertyWriteNeedsTypeBarrier(types::CompilerConstraintList *constraints,
|
||||
MBasicBlock *current, MDefinition **pobj,
|
||||
PropertyName *name, MDefinition **pvalue,
|
||||
|
@ -315,18 +315,6 @@ TemporaryTypeSet::TemporaryTypeSet(Type type)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TypeSet::mightBeType(JSValueType type)
|
||||
{
|
||||
if (unknown())
|
||||
return true;
|
||||
|
||||
if (type == JSVAL_TYPE_OBJECT)
|
||||
return unknownObject() || baseObjectCount() != 0;
|
||||
|
||||
return baseFlags() & PrimitiveTypeFlag(type);
|
||||
}
|
||||
|
||||
bool
|
||||
TypeSet::isSubset(TypeSet *other)
|
||||
{
|
||||
@ -469,11 +457,9 @@ TypeSet::print()
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TypeSet::clone(LifoAlloc *alloc, TemporaryTypeSet *result) const
|
||||
TemporaryTypeSet *
|
||||
TypeSet::clone(LifoAlloc *alloc) const
|
||||
{
|
||||
JS_ASSERT(result->empty());
|
||||
|
||||
unsigned objectCount = baseObjectCount();
|
||||
unsigned capacity = (objectCount >= 2) ? HashSetCapacity(objectCount) : 0;
|
||||
|
||||
@ -481,23 +467,38 @@ TypeSet::clone(LifoAlloc *alloc, TemporaryTypeSet *result) const
|
||||
if (capacity) {
|
||||
newSet = alloc->newArray<TypeObjectKey*>(capacity);
|
||||
if (!newSet)
|
||||
return false;
|
||||
return nullptr;
|
||||
PodCopy(newSet, objectSet, capacity);
|
||||
}
|
||||
|
||||
uint32_t newFlags = flags & ~(TYPE_FLAG_STACK_SET | TYPE_FLAG_HEAP_SET);
|
||||
TemporaryTypeSet *res = alloc->new_<TemporaryTypeSet>(newFlags, capacity ? newSet : objectSet);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
|
||||
new(result) TemporaryTypeSet(newFlags, capacity ? newSet : objectSet);
|
||||
return true;
|
||||
return res;
|
||||
}
|
||||
|
||||
TemporaryTypeSet *
|
||||
TypeSet::clone(LifoAlloc *alloc) const
|
||||
bool
|
||||
TemporaryTypeSet::addObject(TypeObjectKey *key, LifoAlloc *alloc)
|
||||
{
|
||||
TemporaryTypeSet *res = alloc->new_<TemporaryTypeSet>();
|
||||
if (!res || !clone(alloc, res))
|
||||
return nullptr;
|
||||
return res;
|
||||
uint32_t objectCount = baseObjectCount();
|
||||
TypeObjectKey **pentry = HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
|
||||
(*alloc, objectSet, objectCount, key);
|
||||
if (!pentry)
|
||||
return false;
|
||||
if (*pentry)
|
||||
return true;
|
||||
*pentry = key;
|
||||
|
||||
setBaseObjectCount(objectCount);
|
||||
|
||||
if (objectCount == TYPE_FLAG_OBJECT_COUNT_LIMIT) {
|
||||
flags |= TYPE_FLAG_ANYOBJECT;
|
||||
clearObjects();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ TemporaryTypeSet *
|
||||
@ -511,12 +512,12 @@ TypeSet::unionSets(TypeSet *a, TypeSet *b, LifoAlloc *alloc)
|
||||
if (!res->unknownObject()) {
|
||||
for (size_t i = 0; i < a->getObjectCount() && !res->unknownObject(); i++) {
|
||||
TypeObjectKey *key = a->getObject(i);
|
||||
if (key && !res->addType(Type::ObjectType(key), alloc))
|
||||
if (key && !res->addObject(key, alloc))
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t i = 0; i < b->getObjectCount() && !res->unknownObject(); i++) {
|
||||
TypeObjectKey *key = b->getObject(i);
|
||||
if (key && !res->addType(Type::ObjectType(key), alloc))
|
||||
if (key && !res->addObject(key, alloc))
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@ -554,11 +555,9 @@ static LifoAlloc *IonAlloc() {
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Superclass of all constraints generated during Ion compilation. These may
|
||||
// be allocated off the main thread, using the current Ion context's allocator.
|
||||
class CompilerConstraint
|
||||
class types::CompilerConstraint
|
||||
{
|
||||
public:
|
||||
// Property being queried by the compiler.
|
||||
@ -579,138 +578,15 @@ class CompilerConstraint
|
||||
virtual bool generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo) = 0;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class types::CompilerConstraintList
|
||||
void
|
||||
CompilerConstraintList::add(CompilerConstraint *constraint)
|
||||
{
|
||||
public:
|
||||
struct FrozenScript
|
||||
{
|
||||
JSScript *script;
|
||||
TemporaryTypeSet *thisTypes;
|
||||
TemporaryTypeSet *argTypes;
|
||||
TemporaryTypeSet *bytecodeTypes;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
#ifdef JS_ION
|
||||
// Constraints generated on heap properties.
|
||||
Vector<CompilerConstraint *, 0, jit::IonAllocPolicy> constraints;
|
||||
|
||||
// Scripts whose stack type sets were frozen for the compilation.
|
||||
Vector<FrozenScript, 1, jit::IonAllocPolicy> frozenScripts;
|
||||
#endif
|
||||
|
||||
// OOM during generation of some constraint.
|
||||
bool failed_;
|
||||
|
||||
public:
|
||||
CompilerConstraintList()
|
||||
: failed_(false)
|
||||
{}
|
||||
|
||||
void add(CompilerConstraint *constraint) {
|
||||
#ifdef JS_ION
|
||||
if (!constraint || !constraints.append(constraint))
|
||||
setFailed();
|
||||
if (!constraint || !constraints.append(constraint))
|
||||
setFailed();
|
||||
#else
|
||||
MOZ_CRASH();
|
||||
MOZ_CRASH();
|
||||
#endif
|
||||
}
|
||||
|
||||
void freezeScript(JSScript *script,
|
||||
TemporaryTypeSet *thisTypes,
|
||||
TemporaryTypeSet *argTypes,
|
||||
TemporaryTypeSet *bytecodeTypes)
|
||||
{
|
||||
#ifdef JS_ION
|
||||
FrozenScript entry;
|
||||
entry.script = script;
|
||||
entry.thisTypes = thisTypes;
|
||||
entry.argTypes = argTypes;
|
||||
entry.bytecodeTypes = bytecodeTypes;
|
||||
if (!frozenScripts.append(entry))
|
||||
setFailed();
|
||||
#else
|
||||
MOZ_CRASH();
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t length() {
|
||||
#ifdef JS_ION
|
||||
return constraints.length();
|
||||
#else
|
||||
MOZ_CRASH();
|
||||
#endif
|
||||
}
|
||||
|
||||
CompilerConstraint *get(size_t i) {
|
||||
#ifdef JS_ION
|
||||
return constraints[i];
|
||||
#else
|
||||
MOZ_CRASH();
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t numFrozenScripts() {
|
||||
#ifdef JS_ION
|
||||
return frozenScripts.length();
|
||||
#else
|
||||
MOZ_CRASH();
|
||||
#endif
|
||||
}
|
||||
|
||||
const FrozenScript &frozenScript(size_t i) {
|
||||
#ifdef JS_ION
|
||||
return frozenScripts[i];
|
||||
#else
|
||||
MOZ_CRASH();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool failed() {
|
||||
return failed_;
|
||||
}
|
||||
void setFailed() {
|
||||
failed_ = true;
|
||||
}
|
||||
};
|
||||
|
||||
CompilerConstraintList *
|
||||
types::NewCompilerConstraintList()
|
||||
{
|
||||
return IonAlloc()->new_<CompilerConstraintList>();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
TypeScript::FreezeTypeSets(CompilerConstraintList *constraints, JSScript *script,
|
||||
TemporaryTypeSet **pThisTypes,
|
||||
TemporaryTypeSet **pArgTypes,
|
||||
TemporaryTypeSet **pBytecodeTypes)
|
||||
{
|
||||
LifoAlloc *alloc = IonAlloc();
|
||||
StackTypeSet *existing = script->types->typeArray();
|
||||
|
||||
size_t count = NumTypeSets(script);
|
||||
TemporaryTypeSet *types = alloc->newArrayUninitialized<TemporaryTypeSet>(count);
|
||||
if (!types)
|
||||
return false;
|
||||
PodZero(types, count);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (!existing[i].clone(alloc, &types[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
*pThisTypes = types + (ThisTypes(script) - existing);
|
||||
*pArgTypes = (script->function() && script->function()->nargs)
|
||||
? (types + (ArgTypes(script, 0) - existing))
|
||||
: NULL;
|
||||
*pBytecodeTypes = types;
|
||||
|
||||
constraints->freezeScript(script, *pThisTypes, *pArgTypes, *pBytecodeTypes);
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -870,61 +746,6 @@ HeapTypeSetKey::instantiate(JSContext *cx)
|
||||
return maybeTypes_ != NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckFrozenTypeSet(JSContext *cx, TemporaryTypeSet *frozen, StackTypeSet *actual)
|
||||
{
|
||||
// Return whether the types frozen for a script during compilation are
|
||||
// still valid. Also check for any new types added to the frozen set during
|
||||
// compilation, and add them to the actual stack type sets. These new types
|
||||
// indicate places where the compiler relaxed its possible inputs to be
|
||||
// more tolerant of potential new types.
|
||||
|
||||
if (!actual->isSubset(frozen))
|
||||
return false;
|
||||
|
||||
if (!frozen->isSubset(actual)) {
|
||||
TypeSet::TypeList list;
|
||||
frozen->enumerateTypes(&list);
|
||||
|
||||
for (size_t i = 0; i < list.length(); i++) {
|
||||
// Note: On OOM this will preserve the type set's contents.
|
||||
actual->addType(cx, list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/*
|
||||
* As for TypeConstraintFreeze, but describes an implicit freeze constraint
|
||||
* added for stack types within a script. Applies to all compilations of the
|
||||
* script, not just a single one.
|
||||
*/
|
||||
class TypeConstraintFreezeStack : public TypeConstraint
|
||||
{
|
||||
JSScript *script_;
|
||||
|
||||
public:
|
||||
TypeConstraintFreezeStack(JSScript *script)
|
||||
: script_(script)
|
||||
{}
|
||||
|
||||
const char *kind() { return "freezeStack"; }
|
||||
|
||||
void newType(JSContext *cx, TypeSet *source, Type type)
|
||||
{
|
||||
/*
|
||||
* Unlike TypeConstraintFreeze, triggering this constraint once does
|
||||
* not disable it on future changes to the type set.
|
||||
*/
|
||||
cx->compartment()->types.addPendingRecompile(cx, script_);
|
||||
}
|
||||
};
|
||||
|
||||
} /* anonymous namespace */
|
||||
|
||||
bool
|
||||
types::FinishCompilation(JSContext *cx, JSScript *script, ExecutionMode executionMode,
|
||||
CompilerConstraintList *constraints, RecompileInfo *precompileInfo)
|
||||
@ -955,36 +776,7 @@ types::FinishCompilation(JSContext *cx, JSScript *script, ExecutionMode executio
|
||||
succeeded = false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
|
||||
const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i);
|
||||
JS_ASSERT(entry.script->types);
|
||||
|
||||
if (!CheckFrozenTypeSet(cx, entry.thisTypes, types::TypeScript::ThisTypes(entry.script)))
|
||||
succeeded = false;
|
||||
unsigned nargs = entry.script->function() ? entry.script->function()->nargs : 0;
|
||||
for (size_t i = 0; i < nargs; i++) {
|
||||
if (!CheckFrozenTypeSet(cx, &entry.argTypes[i], types::TypeScript::ArgTypes(entry.script, i)))
|
||||
succeeded = false;
|
||||
}
|
||||
for (size_t i = 0; i < entry.script->nTypeSets; i++) {
|
||||
if (!CheckFrozenTypeSet(cx, &entry.bytecodeTypes[i], &entry.script->types->typeArray()[i]))
|
||||
succeeded = false;
|
||||
}
|
||||
|
||||
// If necessary, add constraints to trigger invalidation on the script
|
||||
// after any future changes to the stack type sets.
|
||||
if (entry.script->hasFreezeConstraints)
|
||||
continue;
|
||||
entry.script->hasFreezeConstraints = true;
|
||||
|
||||
size_t count = TypeScript::NumTypeSets(entry.script);
|
||||
|
||||
StackTypeSet *array = entry.script->types->typeArray();
|
||||
for (size_t i = 0; i < count; i++)
|
||||
array[i].add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeStack>(entry.script), false);
|
||||
}
|
||||
|
||||
if (!succeeded || types.constrainedOutputs->back().pendingInvalidation()) {
|
||||
if (!succeeded) {
|
||||
types.constrainedOutputs->back().invalidate();
|
||||
return false;
|
||||
}
|
||||
@ -1072,6 +864,18 @@ TemporaryTypeSet::getKnownTypeTag()
|
||||
return type;
|
||||
}
|
||||
|
||||
bool
|
||||
TemporaryTypeSet::mightBeType(JSValueType type)
|
||||
{
|
||||
if (unknown())
|
||||
return true;
|
||||
|
||||
if (type == JSVAL_TYPE_OBJECT)
|
||||
return unknownObject() || baseObjectCount() != 0;
|
||||
|
||||
return baseFlags() & PrimitiveTypeFlag(type);
|
||||
}
|
||||
|
||||
JSValueType
|
||||
HeapTypeSetKey::knownTypeTag(CompilerConstraintList *constraints)
|
||||
{
|
||||
@ -1667,6 +1471,36 @@ TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList *constraints, jsid
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/*
|
||||
* As for TypeConstraintFreeze, but describes an implicit freeze constraint
|
||||
* added for stack types within a script. Applies to all compilations of the
|
||||
* script, not just a single one.
|
||||
*/
|
||||
class TypeConstraintFreezeStack : public TypeConstraint
|
||||
{
|
||||
JSScript *script_;
|
||||
|
||||
public:
|
||||
TypeConstraintFreezeStack(JSScript *script)
|
||||
: script_(script)
|
||||
{}
|
||||
|
||||
const char *kind() { return "freezeStack"; }
|
||||
|
||||
void newType(JSContext *cx, TypeSet *source, Type type)
|
||||
{
|
||||
/*
|
||||
* Unlike TypeConstraintFreeze, triggering this constraint once does
|
||||
* not disable it on future changes to the type set.
|
||||
*/
|
||||
cx->compartment()->types.addPendingRecompile(cx, script_);
|
||||
}
|
||||
};
|
||||
|
||||
} /* anonymous namespace */
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// TypeCompartment
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
@ -4229,6 +4063,40 @@ TypeScript::destroy()
|
||||
js_free(this);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
TypeScript::AddFreezeConstraints(JSContext *cx, JSScript *script)
|
||||
{
|
||||
if (script->hasFreezeConstraints)
|
||||
return;
|
||||
script->hasFreezeConstraints = true;
|
||||
|
||||
/*
|
||||
* Adding freeze constraints to a script ensures that code for the script
|
||||
* will be recompiled any time any type set for stack values in the script
|
||||
* change: these type sets are implicitly frozen during compilation.
|
||||
*
|
||||
* To ensure this occurs, we don't need to add freeze constraints to the
|
||||
* type sets for every stack value, but rather only the input type sets
|
||||
* to analysis of the stack in a script. The contents of the stack sets
|
||||
* are completely determined by these input sets and by any dynamic types
|
||||
* in the script (for which TypeDynamicResult will trigger recompilation).
|
||||
*
|
||||
* Add freeze constraints to each input type set, which includes sets for
|
||||
* all arguments, locals, and monitored type sets in the script. This
|
||||
* includes all type sets in the TypeScript except the script's return
|
||||
* value types.
|
||||
*/
|
||||
|
||||
size_t count = TypeScript::NumTypeSets(script);
|
||||
|
||||
TypeSet *array = script->types->typeArray();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
TypeSet *types = &array[i];
|
||||
JS_ASSERT(types->isStackSet());
|
||||
types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeStack>(script), false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *typePool)
|
||||
{
|
||||
@ -4406,7 +4274,7 @@ TypeScript::printTypes(JSContext *cx, HandleScript script) const
|
||||
PrintBytecode(cx, script, pc);
|
||||
|
||||
if (js_CodeSpec[*pc].format & JOF_TYPESET) {
|
||||
StackTypeSet *types = TypeScript::BytecodeTypes(script, pc);
|
||||
TypeSet *types = TypeScript::BytecodeTypes(script, pc);
|
||||
fprintf(stderr, " typeset %u:", unsigned(types - typeArray()));
|
||||
types->print();
|
||||
fprintf(stderr, "\n");
|
||||
|
@ -512,7 +512,9 @@ class TypeSet
|
||||
TypeFlags baseFlags() const { return flags & TYPE_FLAG_BASE_MASK; }
|
||||
bool unknown() const { return !!(flags & TYPE_FLAG_UNKNOWN); }
|
||||
bool unknownObject() const { return !!(flags & (TYPE_FLAG_UNKNOWN | TYPE_FLAG_ANYOBJECT)); }
|
||||
|
||||
bool empty() const { return !baseFlags() && !baseObjectCount(); }
|
||||
bool noConstraints() const { return constraintList == nullptr; }
|
||||
|
||||
bool hasAnyFlag(TypeFlags flags) const {
|
||||
JS_ASSERT((flags & TYPE_FLAG_BASE_MASK) == flags);
|
||||
@ -531,9 +533,6 @@ class TypeSet
|
||||
/* Join two type sets into a new set. The result should not be modified further. */
|
||||
static TemporaryTypeSet *unionSets(TypeSet *a, TypeSet *b, LifoAlloc *alloc);
|
||||
|
||||
/* Add a type to this set using the specified allocator. */
|
||||
inline bool addType(Type type, LifoAlloc *alloc, bool *padded = NULL);
|
||||
|
||||
/*
|
||||
* Add a type to this set, calling any constraint handlers if this is a new
|
||||
* possible type.
|
||||
@ -576,9 +575,6 @@ class TypeSet
|
||||
return flags & TYPE_FLAG_HEAP_SET;
|
||||
}
|
||||
|
||||
/* Whether any values in this set might have the specified type. */
|
||||
bool mightBeType(JSValueType type);
|
||||
|
||||
/*
|
||||
* Get whether this type set is known to be a subset of other.
|
||||
* This variant doesn't freeze constraints. That variant is called knownSubset
|
||||
@ -594,9 +590,11 @@ class TypeSet
|
||||
inline StackTypeSet *toStackSet();
|
||||
inline HeapTypeSet *toHeapSet();
|
||||
|
||||
// Clone a type set into an arbitrary allocator.
|
||||
/*
|
||||
* Clone a type set into an arbitrary allocator. The result should not be
|
||||
* modified further.
|
||||
*/
|
||||
TemporaryTypeSet *clone(LifoAlloc *alloc) const;
|
||||
bool clone(LifoAlloc *alloc, TemporaryTypeSet *result) const;
|
||||
|
||||
protected:
|
||||
uint32_t baseObjectCount() const {
|
||||
@ -621,9 +619,6 @@ class HeapTypeSet : public TypeSet
|
||||
|
||||
class CompilerConstraintList;
|
||||
|
||||
CompilerConstraintList *
|
||||
NewCompilerConstraintList();
|
||||
|
||||
class TemporaryTypeSet : public TypeSet
|
||||
{
|
||||
public:
|
||||
@ -636,6 +631,9 @@ class TemporaryTypeSet : public TypeSet
|
||||
JS_ASSERT(!isStackSet() && !isHeapSet());
|
||||
}
|
||||
|
||||
/* Add an object to this set using the specified allocator. */
|
||||
bool addObject(TypeObjectKey *key, LifoAlloc *alloc);
|
||||
|
||||
/*
|
||||
* Constraints for JIT compilation.
|
||||
*
|
||||
@ -648,6 +646,9 @@ class TemporaryTypeSet : public TypeSet
|
||||
/* Get any type tag which all values in this set must have. */
|
||||
JSValueType getKnownTypeTag();
|
||||
|
||||
/* Whether any values in this set might have the specified type. */
|
||||
bool mightBeType(JSValueType type);
|
||||
|
||||
bool isMagicArguments() { return getKnownTypeTag() == JSVAL_TYPE_MAGIC; }
|
||||
|
||||
/* Whether this value may be an object. */
|
||||
@ -1156,7 +1157,7 @@ class TypeScript
|
||||
|
||||
public:
|
||||
/* Array of type type sets for variables and JOF_TYPESET ops. */
|
||||
StackTypeSet *typeArray() const { return (StackTypeSet *) (uintptr_t(this) + sizeof(TypeScript)); }
|
||||
TypeSet *typeArray() const { return (TypeSet *) (uintptr_t(this) + sizeof(TypeScript)); }
|
||||
|
||||
static inline unsigned NumTypeSets(JSScript *script);
|
||||
|
||||
@ -1166,9 +1167,8 @@ class TypeScript
|
||||
/* Get the type set for values observed at an opcode. */
|
||||
static inline StackTypeSet *BytecodeTypes(JSScript *script, jsbytecode *pc);
|
||||
|
||||
template <typename TYPESET>
|
||||
static inline TYPESET *BytecodeTypes(JSScript *script, jsbytecode *pc,
|
||||
uint32_t *hint, TYPESET *typeArray);
|
||||
/* Get the default 'new' object for a given standard class, per the script's global. */
|
||||
static inline TypeObject *StandardType(JSContext *cx, JSProtoKey kind);
|
||||
|
||||
/* Get a type object for an allocation site in this script. */
|
||||
static inline TypeObject *InitObject(JSContext *cx, JSScript *script, jsbytecode *pc,
|
||||
@ -1195,16 +1195,7 @@ class TypeScript
|
||||
static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg,
|
||||
const js::Value &value);
|
||||
|
||||
/*
|
||||
* Freeze all the stack type sets in a script, for a compilation. Returns
|
||||
* copies of the type sets which will be checked against the actual ones
|
||||
* under FinishCompilation, to detect any type changes.
|
||||
*/
|
||||
static bool FreezeTypeSets(CompilerConstraintList *constraints, JSScript *script,
|
||||
TemporaryTypeSet **pThisTypes,
|
||||
TemporaryTypeSet **pArgTypes,
|
||||
TemporaryTypeSet **pBytecodeTypes);
|
||||
|
||||
static void AddFreezeConstraints(JSContext *cx, JSScript *script);
|
||||
static void Purge(JSContext *cx, HandleScript script);
|
||||
|
||||
static void Sweep(FreeOp *fop, JSScript *script);
|
||||
@ -1219,15 +1210,6 @@ class TypeScript
|
||||
#endif
|
||||
};
|
||||
|
||||
class RecompileInfo;
|
||||
|
||||
// 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, ExecutionMode executionMode,
|
||||
CompilerConstraintList *constraints, RecompileInfo *precompileInfo);
|
||||
|
||||
struct ArrayTableKey;
|
||||
typedef HashMap<ArrayTableKey,ReadBarriered<TypeObject>,ArrayTableKey,SystemAllocPolicy> ArrayTypeTable;
|
||||
|
||||
|
@ -588,7 +588,8 @@ TypeScript::NumTypeSets(JSScript *script)
|
||||
/* static */ inline StackTypeSet *
|
||||
TypeScript::ThisTypes(JSScript *script)
|
||||
{
|
||||
return script->types->typeArray() + script->nTypeSets + js::analyze::ThisSlot();
|
||||
TypeSet *types = script->types->typeArray() + script->nTypeSets + js::analyze::ThisSlot();
|
||||
return types->toStackSet();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -601,28 +602,29 @@ TypeScript::ThisTypes(JSScript *script)
|
||||
TypeScript::ArgTypes(JSScript *script, unsigned i)
|
||||
{
|
||||
JS_ASSERT(i < script->function()->nargs);
|
||||
return script->types->typeArray() + script->nTypeSets + js::analyze::ArgSlot(i);
|
||||
TypeSet *types = script->types->typeArray() + script->nTypeSets + js::analyze::ArgSlot(i);
|
||||
return types->toStackSet();
|
||||
}
|
||||
|
||||
template <typename TYPESET>
|
||||
/* static */ inline TYPESET *
|
||||
TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *hint, TYPESET *typeArray)
|
||||
/* static */ inline StackTypeSet *
|
||||
TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET);
|
||||
JS_ASSERT(script->types && script->types->bytecodeMap);
|
||||
uint32_t *bytecodeMap = script->types->bytecodeMap;
|
||||
uint32_t *hint = bytecodeMap + script->nTypeSets;
|
||||
uint32_t offset = pc - script->code;
|
||||
JS_ASSERT(offset < script->length);
|
||||
|
||||
// See if this pc is the next typeset opcode after the last one looked up.
|
||||
if (bytecodeMap[*hint + 1] == offset && (*hint + 1) < script->nTypeSets) {
|
||||
(*hint)++;
|
||||
return typeArray + *hint;
|
||||
return script->types->typeArray()->toStackSet() + *hint;
|
||||
}
|
||||
|
||||
// See if this pc is the same as the last one looked up.
|
||||
if (bytecodeMap[*hint] == offset)
|
||||
return typeArray + *hint;
|
||||
return script->types->typeArray()->toStackSet() + *hint;
|
||||
|
||||
// Fall back to a binary search.
|
||||
size_t bottom = 0;
|
||||
@ -644,15 +646,16 @@ TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *hint, TYPE
|
||||
JS_ASSERT(bytecodeMap[mid] == offset || mid == top);
|
||||
|
||||
*hint = mid;
|
||||
return typeArray + *hint;
|
||||
return script->types->typeArray()->toStackSet() + *hint;
|
||||
}
|
||||
|
||||
/* static */ inline StackTypeSet *
|
||||
TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc)
|
||||
/* static */ inline TypeObject *
|
||||
TypeScript::StandardType(JSContext *cx, JSProtoKey key)
|
||||
{
|
||||
JS_ASSERT(CurrentThreadCanAccessRuntime(script->runtimeFromMainThread()));
|
||||
uint32_t *hint = script->types->bytecodeMap + script->nTypeSets;
|
||||
return BytecodeTypes(script, pc, hint, script->types->typeArray());
|
||||
RootedObject proto(cx);
|
||||
if (!js_GetClassPrototype(cx, key, &proto, nullptr))
|
||||
return nullptr;
|
||||
return cx->getNewType(GetClassForProtoKey(key), proto.get());
|
||||
}
|
||||
|
||||
struct AllocationSiteKey : public DefaultHasher<AllocationSiteKey> {
|
||||
@ -1106,65 +1109,61 @@ TypeSet::clearObjects()
|
||||
objectSet = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
TypeSet::addType(Type type, LifoAlloc *alloc, bool *padded)
|
||||
inline void
|
||||
TypeSet::addType(ExclusiveContext *cxArg, Type type)
|
||||
{
|
||||
JS_ASSERT_IF(padded, !*padded);
|
||||
JS_ASSERT(cxArg->compartment()->activeAnalysis);
|
||||
|
||||
// Temporary type sets use a separate LifoAlloc for storage.
|
||||
JS_ASSERT_IF(!type.isUnknown() && !type.isAnyObject() && type.isObject(),
|
||||
isStackSet() || isHeapSet());
|
||||
|
||||
if (unknown())
|
||||
return true;
|
||||
return;
|
||||
|
||||
if (type.isUnknown()) {
|
||||
flags |= TYPE_FLAG_BASE_MASK;
|
||||
clearObjects();
|
||||
JS_ASSERT(unknown());
|
||||
if (padded)
|
||||
*padded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type.isPrimitive()) {
|
||||
} else if (type.isPrimitive()) {
|
||||
TypeFlags flag = PrimitiveTypeFlag(type.primitive());
|
||||
if (flags & flag)
|
||||
return true;
|
||||
return;
|
||||
|
||||
/* If we add float to a type set it is also considered to contain int. */
|
||||
if (flag == TYPE_FLAG_DOUBLE)
|
||||
flag |= TYPE_FLAG_INT32;
|
||||
|
||||
flags |= flag;
|
||||
if (padded)
|
||||
*padded = true;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (flags & TYPE_FLAG_ANYOBJECT)
|
||||
return;
|
||||
if (type.isAnyObject())
|
||||
goto unknownObject;
|
||||
|
||||
if (flags & TYPE_FLAG_ANYOBJECT)
|
||||
return true;
|
||||
if (type.isAnyObject())
|
||||
goto unknownObject;
|
||||
|
||||
{
|
||||
uint32_t objectCount = baseObjectCount();
|
||||
TypeObjectKey *object = type.objectKey();
|
||||
TypeObjectKey **pentry = HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
|
||||
(*alloc, objectSet, objectCount, object);
|
||||
if (!pentry)
|
||||
return false;
|
||||
(cxArg->typeLifoAlloc(), objectSet, objectCount, object);
|
||||
if (!pentry) {
|
||||
cxArg->compartment()->types.setPendingNukeTypes(cxArg);
|
||||
return;
|
||||
}
|
||||
if (*pentry)
|
||||
return true;
|
||||
return;
|
||||
*pentry = object;
|
||||
|
||||
setBaseObjectCount(objectCount);
|
||||
|
||||
if (objectCount == TYPE_FLAG_OBJECT_COUNT_LIMIT)
|
||||
goto unknownObject;
|
||||
}
|
||||
|
||||
if (type.isTypeObject()) {
|
||||
TypeObject *nobject = type.typeObject();
|
||||
JS_ASSERT(!nobject->singleton);
|
||||
if (nobject->unknownProperties())
|
||||
goto unknownObject;
|
||||
if (type.isTypeObject()) {
|
||||
TypeObject *nobject = type.typeObject();
|
||||
JS_ASSERT(!nobject->singleton);
|
||||
if (nobject->unknownProperties())
|
||||
goto unknownObject;
|
||||
}
|
||||
}
|
||||
|
||||
if (false) {
|
||||
@ -1174,27 +1173,6 @@ TypeSet::addType(Type type, LifoAlloc *alloc, bool *padded)
|
||||
clearObjects();
|
||||
}
|
||||
|
||||
if (padded)
|
||||
*padded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void
|
||||
TypeSet::addType(ExclusiveContext *cxArg, Type type)
|
||||
{
|
||||
JS_ASSERT(cxArg->compartment()->activeAnalysis);
|
||||
|
||||
// Temporary type sets use a separate LifoAlloc for storage.
|
||||
JS_ASSERT(isStackSet() || isHeapSet());
|
||||
|
||||
bool added = false;
|
||||
if (!addType(type, &cxArg->typeLifoAlloc(), &added)) {
|
||||
cxArg->compartment()->types.setPendingNukeTypes(cxArg);
|
||||
return;
|
||||
}
|
||||
if (!added)
|
||||
return;
|
||||
|
||||
InferSpew(ISpewOps, "addType: %sT%p%s %s",
|
||||
InferSpewColor(this), this, InferSpewColorReset(),
|
||||
TypeString(type));
|
||||
@ -1444,6 +1422,54 @@ 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, ExecutionMode executionMode,
|
||||
CompilerConstraintList *constraints, RecompileInfo *precompileInfo);
|
||||
|
||||
class CompilerConstraint;
|
||||
class CompilerConstraintList
|
||||
{
|
||||
#ifdef JS_ION
|
||||
// Generated constraints.
|
||||
Vector<CompilerConstraint *, 0, jit::IonAllocPolicy> constraints;
|
||||
#endif
|
||||
|
||||
// OOM during generation of some constraint.
|
||||
bool failed_;
|
||||
|
||||
public:
|
||||
CompilerConstraintList()
|
||||
: failed_(false)
|
||||
{}
|
||||
|
||||
void add(CompilerConstraint *constraint);
|
||||
|
||||
size_t length() {
|
||||
#ifdef JS_ION
|
||||
return constraints.length();
|
||||
#else
|
||||
MOZ_CRASH();
|
||||
#endif
|
||||
}
|
||||
CompilerConstraint *get(size_t i) {
|
||||
#ifdef JS_ION
|
||||
return constraints[i];
|
||||
#else
|
||||
MOZ_CRASH();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool failed() {
|
||||
return failed_;
|
||||
}
|
||||
void setFailed() {
|
||||
failed_ = true;
|
||||
}
|
||||
};
|
||||
|
||||
} } /* namespace js::types */
|
||||
|
||||
inline bool
|
||||
|
Loading…
Reference in New Issue
Block a user