Bug 925962 - Track expected contents of stack type sets in compiler constraints, r=jandem.

This commit is contained in:
Brian Hackett 2013-10-17 10:21:05 -06:00
parent 43da216a34
commit dda9035be8
13 changed files with 484 additions and 343 deletions

View File

@ -5836,12 +5836,16 @@ CodeGenerator::visitOutOfLineUnboxFloatingPoint(OutOfLineUnboxFloatingPoint *ool
typedef bool (*GetPropertyFn)(JSContext *, HandleValue, HandlePropertyName, MutableHandleValue);
static const VMFunction GetPropertyInfo = FunctionInfo<GetPropertyFn>(GetProperty);
static const VMFunction CallPropertyInfo = FunctionInfo<GetPropertyFn>(CallProperty);
bool
CodeGenerator::visitCallGetProperty(LCallGetProperty *lir)
{
pushArg(ImmGCPtr(lir->mir()->name()));
pushArg(ToValue(lir, LCallGetProperty::Value));
if (lir->mir()->callprop())
return callVM(CallPropertyInfo, lir);
return callVM(GetPropertyInfo, lir);
}

View File

@ -1489,6 +1489,7 @@ AttachFinishedCompilations(JSContext *cx)
if (!ion || !cx->runtime()->workerThreadState)
return;
types::AutoEnterAnalysis enterTypes(cx);
AutoLockWorkerThreadState lock(*cx->runtime()->workerThreadState);
OffThreadCompilationVector &compilations = ion->finishedOffThreadCompilations();
@ -1507,8 +1508,6 @@ AttachFinishedCompilations(JSContext *cx)
// previously, though any GC activity would discard the builder.
codegen->masm.constructRoot(cx);
types::AutoEnterAnalysis enterTypes(cx);
bool success;
{
// Release the worker thread lock and root the compiler for GC.
@ -1597,7 +1596,9 @@ IonCompile(JSContext *cx, JSScript *script,
AutoFlushCache afc("IonCompile", cx->runtime()->ionRuntime());
AutoTempAllocatorRooter root(cx, temp);
types::CompilerConstraintList *constraints = alloc->new_<types::CompilerConstraintList>();
types::CompilerConstraintList *constraints = types::NewCompilerConstraintList();
if (!constraints)
return AbortReason_Alloc;
IonBuilder *builder = alloc->new_<IonBuilder>(cx, temp, graph, constraints,
&inspector, info, baselineFrame);
@ -2339,7 +2340,8 @@ 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);
JS_ASSERT(co.isValid());
if (!co.isValid())
continue;
CancelOffThreadIonCompile(co.script()->compartment(), co.script());
@ -2369,7 +2371,9 @@ 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);
JS_ASSERT(co.isValid());
if (!co.isValid())
continue;
ExecutionMode executionMode = co.mode();
JSScript *script = co.script();
IonScript *ionScript = co.ion();

View File

@ -2092,9 +2092,9 @@ jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
AutoTempAllocatorRooter root(cx, &temp);
types::CompilerConstraintList constraints;
types::CompilerConstraintList *constraints = types::NewCompilerConstraintList();
BaselineInspector inspector(cx, fun->nonLazyScript());
IonBuilder builder(cx, &temp, &graph, &constraints,
IonBuilder builder(cx, &temp, &graph, constraints,
&inspector, &info, /* baselineFrame = */ nullptr);
if (!builder.build()) {

View File

@ -47,6 +47,10 @@ 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),
@ -511,6 +515,12 @@ 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;
@ -615,8 +625,6 @@ IonBuilder::build()
if (!processIterators())
return false;
types::TypeScript::AddFreezeConstraints(cx, script());
JS_ASSERT(loopDepth_ == 0);
abortReason_ = AbortReason_NoAbort;
return true;
@ -765,7 +773,6 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi
if (!traverseBytecode())
return false;
types::TypeScript::AddFreezeConstraints(cx, script());
return true;
}
@ -836,23 +843,25 @@ IonBuilder::initParameters()
// interpreter and didn't accumulate type information, try to use that OSR
// frame to determine possible initial types for 'this' and parameters.
types::StackTypeSet *thisTypes = types::TypeScript::ThisTypes(script());
if (thisTypes->empty() && baselineFrame_)
thisTypes->addType(cx, types::GetValueType(baselineFrame_->thisValue()));
if (thisTypes->empty() && baselineFrame_) {
if (!thisTypes->addType(types::GetValueType(baselineFrame_->thisValue()), temp_->lifoAlloc()))
return false;
}
MParameter *param = MParameter::New(MParameter::THIS_SLOT, cloneTypeSet(thisTypes));
MParameter *param = MParameter::New(MParameter::THIS_SLOT, thisTypes);
current->add(param);
current->initSlot(info().thisSlot(), param);
for (uint32_t i = 0; i < info().nargs(); i++) {
types::StackTypeSet *argTypes = types::TypeScript::ArgTypes(script(), i);
if (argTypes->empty() && baselineFrame_ &&
types::TemporaryTypeSet *types = &argTypes[i];
if (types->empty() && baselineFrame_ &&
!script_->baselineScript()->modifiesArguments())
{
argTypes->addType(cx, types::GetValueType(baselineFrame_->argv()[i]));
if (!types->addType(types::GetValueType(baselineFrame_->argv()[i]), temp_->lifoAlloc()))
return false;
}
param = MParameter::New(i, cloneTypeSet(argTypes));
param = MParameter::New(i, types);
current->add(param);
current->initSlot(info().argSlotUnchecked(i), param);
}
@ -944,7 +953,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 = GetIonContext()->temp->lifoAlloc()->new_<types::TemporaryTypeSet>(ntype);
typeSet = temp_->lifoAlloc()->new_<types::TemporaryTypeSet>(ntype);
if (!typeSet)
return false;
MInstruction *barrier = MTypeBarrier::New(def, typeSet);
@ -2977,7 +2986,7 @@ IonBuilder::jsop_condswitch()
JS_ASSERT(curCase < defaultTarget && defaultTarget <= exitpc);
// Allocate the current graph state.
CFGState state = CFGState::CondSwitch(exitpc, defaultTarget);
CFGState state = CFGState::CondSwitch(this, exitpc, defaultTarget);
if (!state.condswitch.bodies || !state.condswitch.bodies->init(nbBodies))
return ControlStatus_Error;
@ -2990,12 +2999,12 @@ IonBuilder::jsop_condswitch()
}
IonBuilder::CFGState
IonBuilder::CFGState::CondSwitch(jsbytecode *exitpc, jsbytecode *defaultTarget)
IonBuilder::CFGState::CondSwitch(IonBuilder *builder, jsbytecode *exitpc, jsbytecode *defaultTarget)
{
CFGState state;
state.state = COND_SWITCH_CASE;
state.stopAt = nullptr;
state.condswitch.bodies = (FixedList<MBasicBlock *> *)GetIonContext()->temp->allocate(
state.condswitch.bodies = (FixedList<MBasicBlock *> *)builder->temp_->allocate(
sizeof(FixedList<MBasicBlock *>));
state.condswitch.currentIdx = 0;
state.condswitch.defaultTarget = defaultTarget;
@ -3752,14 +3761,15 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target)
{
types::StackTypeSet *types = types::TypeScript::ThisTypes(calleeScript);
if (!types->unknown()) {
MTypeBarrier *barrier = MTypeBarrier::New(callInfo.thisArg(), cloneTypeSet(types));
MTypeBarrier *barrier =
MTypeBarrier::New(callInfo.thisArg(), types->clone(temp_->lifoAlloc()));
current->add(barrier);
callInfo.setThis(barrier);
}
}
// Start inlining.
LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc();
LifoAlloc *alloc = temp_->lifoAlloc();
CompileInfo *info = alloc->new_<CompileInfo>(calleeScript, target,
(jsbytecode *)nullptr, callInfo.constructing(),
this->info().executionMode());
@ -4595,7 +4605,8 @@ IonBuilder::createThisScripted(MDefinition *callee)
getPropCache->setIdempotent();
getProto = getPropCache;
} else {
MCallGetProperty *callGetProp = MCallGetProperty::New(callee, cx->names().prototype);
MCallGetProperty *callGetProp = MCallGetProperty::New(callee, cx->names().prototype,
/* callprop = */ false);
callGetProp->setIdempotent();
getProto = callGetProp;
}
@ -4901,15 +4912,17 @@ 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::StackTypeSet *observed = types::TypeScript::BytecodeTypes(script(), pc);
if (observed->empty() && observed->noConstraints()) {
types::TemporaryTypeSet *observed = bytecodeTypes(pc);
if (observed->empty()) {
if (BytecodeFlowsToBitop(pc)) {
observed->addType(cx, types::Type::Int32Type());
if (!observed->addType(types::Type::Int32Type(), temp_->lifoAlloc()))
return false;
} 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.
observed->addType(cx, types::Type::DoubleType());
if (!observed->addType(types::Type::DoubleType(), temp_->lifoAlloc()))
return false;
}
}
@ -5041,7 +5054,7 @@ TestAreKnownDOMTypes(JSContext *cx, types::TypeSet *inTypes)
static bool
ArgumentTypesMatch(MDefinition *def, types::TemporaryTypeSet *calleeTypes)
ArgumentTypesMatch(MDefinition *def, types::StackTypeSet *calleeTypes)
{
if (def->resultTypeSet()) {
JS_ASSERT(def->type() == MIRType_Value || def->mightBeType(def->type()));
@ -5070,15 +5083,15 @@ IonBuilder::testNeedsArgumentCheck(JSContext *cx, JSFunction *target, CallInfo &
if (!targetScript->types)
return true;
if (!ArgumentTypesMatch(callInfo.thisArg(), cloneTypeSet(types::TypeScript::ThisTypes(targetScript))))
if (!ArgumentTypesMatch(callInfo.thisArg(), 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), cloneTypeSet(types::TypeScript::ArgTypes(targetScript, i))))
if (!ArgumentTypesMatch(callInfo.getArg(i), types::TypeScript::ArgTypes(targetScript, i)))
return true;
}
for (size_t i = callInfo.argc(); i < target->nargs; i++) {
if (!cloneTypeSet(types::TypeScript::ArgTypes(targetScript, i))->mightBeType(JSVAL_TYPE_UNDEFINED))
if (!types::TypeScript::ArgTypes(targetScript, i)->mightBeType(JSVAL_TYPE_UNDEFINED))
return true;
}
@ -5249,8 +5262,6 @@ 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
@ -5875,7 +5886,7 @@ IonBuilder::newPendingLoopHeader(MBasicBlock *predecessor, jsbytecode *pc, bool
: MIRTypeFromValueType(existingValue.extractNonDoubleType());
types::Type ntype = types::GetValueType(existingValue);
types::TemporaryTypeSet *typeSet =
GetIonContext()->temp->lifoAlloc()->new_<types::TemporaryTypeSet>(ntype);
temp_->lifoAlloc()->new_<types::TemporaryTypeSet>(ntype);
if (!typeSet)
return nullptr;
phi->addBackedgeType(type, typeSet);
@ -6229,10 +6240,9 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc
}
}
types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
types::TemporaryTypeSet *types = bytecodeTypes(pc);
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), staticType,
name, baseTypes, /* updateObserved = */ true);
types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
name, types, /* updateObserved = */ true);
// If the property is permanent, a shape guard isn't necessary.
@ -6742,9 +6752,8 @@ IonBuilder::getElemTryCache(bool *emitted, MDefinition *obj, MDefinition *index)
// Emit GetElementCache.
types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), obj, nullptr, baseTypes);
types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
types::TemporaryTypeSet *types = bytecodeTypes(pc);
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), obj, nullptr, types);
// Always add a barrier if the index might be a string, so that the cache
// can attach stubs for particular properties.
@ -6782,19 +6791,17 @@ IonBuilder::getElemTryCache(bool *emitted, MDefinition *obj, MDefinition *index)
bool
IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index)
{
types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
types::TemporaryTypeSet *types = bytecodeTypes(pc);
if (JSOp(*pc) == JSOP_CALLELEM && !index->mightBeType(MIRType_String) && baseTypes->noConstraints()) {
if (JSOp(*pc) == JSOP_CALLELEM && !index->mightBeType(MIRType_String)) {
// 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(cx, obj, nullptr, baseTypes))
if (!AddObjectsForPropertyRead(obj, nullptr, types))
return false;
}
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), obj, nullptr, baseTypes);
types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), obj, nullptr, types);
bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj);
// Reads which are on holes in the object do not have to bail out if
@ -8048,10 +8055,9 @@ IonBuilder::jsop_getprop(PropertyName *name)
if (!getPropTryArgumentsLength(&emitted) || emitted)
return emitted;
types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
types::TemporaryTypeSet *types = bytecodeTypes(pc);
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(),
current->peek(-1), name, baseTypes);
types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
current->peek(-1), name, types);
// Try to hardcode known constants.
if (!getPropTryConstant(&emitted, name, types) || emitted)
@ -8061,9 +8067,9 @@ 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 || baseTypes->empty()) {
if (info().executionMode() == DefinitePropertiesAnalysis || types->empty()) {
MDefinition *obj = current->pop();
MCallGetProperty *call = MCallGetProperty::New(obj, name);
MCallGetProperty *call = MCallGetProperty::New(obj, name, *pc == JSOP_CALLPROP);
current->add(call);
current->push(call);
return resumeAfter(call) && pushTypeBarrier(call, types, true);
@ -8091,7 +8097,7 @@ IonBuilder::jsop_getprop(PropertyName *name)
// Emit a call.
MDefinition *obj = current->pop();
MCallGetProperty *call = MCallGetProperty::New(obj, name);
MCallGetProperty *call = MCallGetProperty::New(obj, name, *pc == JSOP_CALLPROP);
current->add(call);
current->push(call);
if (!resumeAfter(call))
@ -8995,11 +9001,9 @@ IonBuilder::jsop_setarg(uint32_t arg)
op->resultTypeSet() &&
op->resultTypeSet()->empty())
{
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());
JS_ASSERT(op->resultTypeSet() == &argTypes[arg]);
if (!argTypes[arg].addType(types::Type::UnknownType(), temp_->lifoAlloc()))
return false;
}
}
}
@ -9057,9 +9061,8 @@ IonBuilder::jsop_this()
return true;
}
types::TemporaryTypeSet *types = cloneTypeSet(types::TypeScript::ThisTypes(script()));
if (types && (types->getKnownTypeTag() == JSVAL_TYPE_OBJECT ||
(types->empty() && baselineFrame_ && baselineFrame_->thisValue().isObject())))
if (thisTypes->getKnownTypeTag() == JSVAL_TYPE_OBJECT ||
(thisTypes->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
@ -9473,17 +9476,7 @@ IonBuilder::addShapeGuard(MDefinition *obj, Shape *const shape, BailoutKind bail
types::TemporaryTypeSet *
IonBuilder::bytecodeTypes(jsbytecode *pc)
{
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());
return types::TypeScript::BytecodeTypes(script(), pc, &typeArrayHint, typeArray);
}
TypeRepresentationSetHash *

View File

@ -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(jsbytecode *exitpc, jsbytecode *defaultTarget);
static CFGState CondSwitch(IonBuilder *builder, jsbytecode *exitpc, jsbytecode *defaultTarget);
static CFGState Label(jsbytecode *exitpc);
static CFGState Try(jsbytecode *exitpc, MBasicBlock *successor);
};
@ -531,7 +531,6 @@ class IonBuilder : public MIRGenerator
BoolVector &choiceSet);
// Native inlining helpers.
types::StackTypeSet *getOriginalInlineReturnTypeSet();
types::TemporaryTypeSet *getInlineReturnTypeSet();
MIRType getInlineReturnType();
@ -645,7 +644,6 @@ 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
@ -717,6 +715,9 @@ class IonBuilder : public MIRGenerator
return analysis_;
}
types::TemporaryTypeSet *thisTypes, *argTypes, *typeArray;
uint32_t typeArrayHint;
GSNCache gsn;
jsbytecode *pc;

View File

@ -156,16 +156,10 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSNative native)
return InliningStatus_NotInlined;
}
types::StackTypeSet *
IonBuilder::getOriginalInlineReturnTypeSet()
{
return types::TypeScript::BytecodeTypes(script(), pc);
}
types::TemporaryTypeSet *
IonBuilder::getInlineReturnTypeSet()
{
return cloneTypeSet(getOriginalInlineReturnTypeSet());
return bytecodeTypes(pc);
}
MIRType
@ -328,7 +322,7 @@ IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode)
callInfo.unwrapArgs();
types::StackTypeSet *returnTypes = getOriginalInlineReturnTypeSet();
types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet();
bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED);
bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType());
@ -346,7 +340,7 @@ IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode)
if (!resumeAfter(ins))
return InliningStatus_Error;
if (!pushTypeBarrier(ins, cloneTypeSet(returnTypes), barrier))
if (!pushTypeBarrier(ins, returnTypes, barrier))
return InliningStatus_Error;
return InliningStatus_Inlined;

View File

@ -2571,7 +2571,7 @@ InlinePropertyTable::buildTypeSetForFunction(JSFunction *func) const
return nullptr;
for (size_t i = 0; i < numEntries(); i++) {
if (entries_[i]->func == func) {
if (!types->addObject(types::Type::ObjectType(entries_[i]->typeObj).objectKey(), alloc))
if (!types->addType(types::Type::ObjectType(entries_[i]->typeObj), alloc))
return nullptr;
}
}
@ -2818,11 +2818,11 @@ bool
jit::PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
types::CompilerConstraintList *constraints,
types::TypeObjectKey *object, PropertyName *name,
types::StackTypeSet *observed, bool updateObserved)
types::TemporaryTypeSet *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() && observed->noConstraints() && name) {
if (updateObserved && observed->empty() && name) {
JSObject *obj = object->singleton() ? object->singleton() : object->proto().toObjectOrNull();
while (obj) {
@ -2837,7 +2837,8 @@ jit::PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
if (!property.maybeTypes()->enumerateTypes(&types))
return false;
if (types.length()) {
observed->addType(cx, types[0]);
if (!observed->addType(types[0], GetIonContext()->temp->lifoAlloc()))
return false;
break;
}
}
@ -2854,7 +2855,7 @@ bool
jit::PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
types::CompilerConstraintList *constraints,
MDefinition *obj, PropertyName *name,
types::StackTypeSet *observed)
types::TemporaryTypeSet *observed)
{
if (observed->unknown())
return false;
@ -2931,45 +2932,39 @@ jit::PropertyReadIsIdempotent(types::CompilerConstraintList *constraints,
}
bool
jit::AddObjectsForPropertyRead(JSContext *cx, MDefinition *obj, PropertyName *name,
types::StackTypeSet *observed)
jit::AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name,
types::TemporaryTypeSet *observed)
{
// Add objects to observed which *could* be observed by reading name from obj,
// to hopefully avoid unnecessary type barriers and code invalidations.
JS_ASSERT(observed->noConstraints());
LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc();
types::TemporaryTypeSet *types = obj->resultTypeSet();
if (!types || types->unknownObject()) {
observed->addType(cx, types::Type::AnyObjectType());
return true;
}
if (!types || types->unknownObject())
return observed->addType(types::Type::AnyObjectType(), alloc);
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)
continue;
if (object->unknownProperties()) {
observed->addType(cx, types::Type::AnyObjectType());
return true;
}
if (object->unknownProperties())
return observed->addType(types::Type::AnyObjectType(), alloc);
jsid id = name ? NameToId(name) : JSID_VOID;
types::HeapTypeSet *property = object->getProperty(cx, id);
if (property->unknownObject()) {
observed->addType(cx, types::Type::AnyObjectType());
return true;
}
types::HeapTypeSetKey property = object->property(id);
types::HeapTypeSet *types = property.maybeTypes();
if (!types)
continue;
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));
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;
}
}

View File

@ -7173,10 +7173,12 @@ class MCallGetProperty
{
CompilerRootPropertyName name_;
bool idempotent_;
bool callprop_;
MCallGetProperty(MDefinition *value, PropertyName *name)
MCallGetProperty(MDefinition *value, PropertyName *name, bool callprop)
: MUnaryInstruction(value), name_(name),
idempotent_(false)
idempotent_(false),
callprop_(callprop)
{
setResultType(MIRType_Value);
}
@ -7184,8 +7186,8 @@ class MCallGetProperty
public:
INSTRUCTION_HEADER(CallGetProperty)
static MCallGetProperty *New(MDefinition *value, PropertyName *name) {
return new MCallGetProperty(value, name);
static MCallGetProperty *New(MDefinition *value, PropertyName *name, bool callprop) {
return new MCallGetProperty(value, name, callprop);
}
MDefinition *value() const {
return getOperand(0);
@ -7193,6 +7195,9 @@ class MCallGetProperty
PropertyName *name() const {
return name_;
}
bool callprop() const {
return callprop_;
}
TypePolicy *typePolicy() {
return this;
}
@ -9005,18 +9010,18 @@ MIRType DenseNativeElementType(types::CompilerConstraintList *constraints, MDefi
bool PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
types::CompilerConstraintList *constraints,
types::TypeObjectKey *object, PropertyName *name,
types::StackTypeSet *observed, bool updateObserved);
types::TemporaryTypeSet *observed, bool updateObserved);
bool PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
types::CompilerConstraintList *constraints,
MDefinition *obj, PropertyName *name,
types::StackTypeSet *observed);
types::TemporaryTypeSet *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 AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name,
types::TemporaryTypeSet *observed);
bool PropertyWriteNeedsTypeBarrier(types::CompilerConstraintList *constraints,
MBasicBlock *current, MDefinition **pobj,
PropertyName *name, MDefinition **pvalue,

View File

@ -315,6 +315,18 @@ 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)
{
@ -457,9 +469,11 @@ TypeSet::print()
}
}
TemporaryTypeSet *
TypeSet::clone(LifoAlloc *alloc) const
bool
TypeSet::clone(LifoAlloc *alloc, TemporaryTypeSet *result) const
{
JS_ASSERT(result->empty());
unsigned objectCount = baseObjectCount();
unsigned capacity = (objectCount >= 2) ? HashSetCapacity(objectCount) : 0;
@ -467,38 +481,23 @@ TypeSet::clone(LifoAlloc *alloc) const
if (capacity) {
newSet = alloc->newArray<TypeObjectKey*>(capacity);
if (!newSet)
return nullptr;
return false;
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;
return res;
new(result) TemporaryTypeSet(newFlags, capacity ? newSet : objectSet);
return true;
}
bool
TemporaryTypeSet::addObject(TypeObjectKey *key, LifoAlloc *alloc)
TemporaryTypeSet *
TypeSet::clone(LifoAlloc *alloc) const
{
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;
TemporaryTypeSet *res = alloc->new_<TemporaryTypeSet>();
if (!res || !clone(alloc, res))
return nullptr;
return res;
}
/* static */ TemporaryTypeSet *
@ -512,12 +511,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->addObject(key, alloc))
if (key && !res->addType(Type::ObjectType(key), alloc))
return nullptr;
}
for (size_t i = 0; i < b->getObjectCount() && !res->unknownObject(); i++) {
TypeObjectKey *key = b->getObject(i);
if (key && !res->addObject(key, alloc))
if (key && !res->addType(Type::ObjectType(key), alloc))
return nullptr;
}
}
@ -555,9 +554,11 @@ 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 types::CompilerConstraint
class CompilerConstraint
{
public:
// Property being queried by the compiler.
@ -578,15 +579,138 @@ class types::CompilerConstraint
virtual bool generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo) = 0;
};
void
CompilerConstraintList::add(CompilerConstraint *constraint)
} // anonymous namespace
class types::CompilerConstraintList
{
public:
struct FrozenScript
{
JSScript *script;
TemporaryTypeSet *thisTypes;
TemporaryTypeSet *argTypes;
TemporaryTypeSet *bytecodeTypes;
};
private:
#ifdef JS_ION
if (!constraint || !constraints.append(constraint))
setFailed();
#else
MOZ_CRASH();
// 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();
#else
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 {
@ -746,6 +870,61 @@ 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)
@ -776,7 +955,36 @@ types::FinishCompilation(JSContext *cx, JSScript *script, ExecutionMode executio
succeeded = false;
}
if (!succeeded) {
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()) {
types.constrainedOutputs->back().invalidate();
return false;
}
@ -864,18 +1072,6 @@ 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)
{
@ -1471,36 +1667,6 @@ 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
/////////////////////////////////////////////////////////////////////
@ -4063,40 +4229,6 @@ 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)
{
@ -4274,7 +4406,7 @@ TypeScript::printTypes(JSContext *cx, HandleScript script) const
PrintBytecode(cx, script, pc);
if (js_CodeSpec[*pc].format & JOF_TYPESET) {
TypeSet *types = TypeScript::BytecodeTypes(script, pc);
StackTypeSet *types = TypeScript::BytecodeTypes(script, pc);
fprintf(stderr, " typeset %u:", unsigned(types - typeArray()));
types->print();
fprintf(stderr, "\n");

View File

@ -513,9 +513,7 @@ 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);
@ -534,6 +532,9 @@ 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,6 +577,9 @@ 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
@ -591,11 +595,9 @@ class TypeSet
inline StackTypeSet *toStackSet();
inline HeapTypeSet *toHeapSet();
/*
* Clone a type set into an arbitrary allocator. The result should not be
* modified further.
*/
// Clone a type set into an arbitrary allocator.
TemporaryTypeSet *clone(LifoAlloc *alloc) const;
bool clone(LifoAlloc *alloc, TemporaryTypeSet *result) const;
protected:
uint32_t baseObjectCount() const {
@ -620,6 +622,9 @@ class HeapTypeSet : public TypeSet
class CompilerConstraintList;
CompilerConstraintList *
NewCompilerConstraintList();
class TemporaryTypeSet : public TypeSet
{
public:
@ -632,9 +637,6 @@ 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.
*
@ -647,9 +649,6 @@ 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. */
@ -1158,7 +1157,7 @@ class TypeScript
public:
/* Array of type type sets for variables and JOF_TYPESET ops. */
TypeSet *typeArray() const { return (TypeSet *) (uintptr_t(this) + sizeof(TypeScript)); }
StackTypeSet *typeArray() const { return (StackTypeSet *) (uintptr_t(this) + sizeof(TypeScript)); }
static inline unsigned NumTypeSets(JSScript *script);
@ -1168,8 +1167,9 @@ class TypeScript
/* Get the type set for values observed at an opcode. */
static inline StackTypeSet *BytecodeTypes(JSScript *script, jsbytecode *pc);
/* Get the default 'new' object for a given standard class, per the script's global. */
static inline TypeObject *StandardType(JSContext *cx, JSProtoKey kind);
template <typename TYPESET>
static inline TYPESET *BytecodeTypes(JSScript *script, jsbytecode *pc,
uint32_t *hint, TYPESET *typeArray);
/* Get a type object for an allocation site in this script. */
static inline TypeObject *InitObject(JSContext *cx, JSScript *script, jsbytecode *pc,
@ -1196,7 +1196,16 @@ class TypeScript
static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg,
const js::Value &value);
static void AddFreezeConstraints(JSContext *cx, JSScript *script);
/*
* 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 Purge(JSContext *cx, HandleScript script);
static void Sweep(FreeOp *fop, JSScript *script);
@ -1211,6 +1220,15 @@ 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;

View File

@ -588,8 +588,7 @@ TypeScript::NumTypeSets(JSScript *script)
/* static */ inline StackTypeSet *
TypeScript::ThisTypes(JSScript *script)
{
TypeSet *types = script->types->typeArray() + script->nTypeSets + js::analyze::ThisSlot();
return types->toStackSet();
return script->types->typeArray() + script->nTypeSets + js::analyze::ThisSlot();
}
/*
@ -602,29 +601,28 @@ TypeScript::ThisTypes(JSScript *script)
TypeScript::ArgTypes(JSScript *script, unsigned i)
{
JS_ASSERT(i < script->function()->nargs);
TypeSet *types = script->types->typeArray() + script->nTypeSets + js::analyze::ArgSlot(i);
return types->toStackSet();
return script->types->typeArray() + script->nTypeSets + js::analyze::ArgSlot(i);
}
/* static */ inline StackTypeSet *
TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc)
template <typename TYPESET>
/* static */ inline TYPESET *
TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *hint, TYPESET *typeArray)
{
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 script->types->typeArray()->toStackSet() + *hint;
return typeArray + *hint;
}
// See if this pc is the same as the last one looked up.
if (bytecodeMap[*hint] == offset)
return script->types->typeArray()->toStackSet() + *hint;
return typeArray + *hint;
// Fall back to a binary search.
size_t bottom = 0;
@ -646,16 +644,15 @@ TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc)
JS_ASSERT(bytecodeMap[mid] == offset || mid == top);
*hint = mid;
return script->types->typeArray()->toStackSet() + *hint;
return typeArray + *hint;
}
/* static */ inline TypeObject *
TypeScript::StandardType(JSContext *cx, JSProtoKey key)
/* static */ inline StackTypeSet *
TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc)
{
RootedObject proto(cx);
if (!js_GetClassPrototype(cx, key, &proto, nullptr))
return nullptr;
return cx->getNewType(GetClassForProtoKey(key), proto.get());
JS_ASSERT(CurrentThreadCanAccessRuntime(script->runtimeFromMainThread()));
uint32_t *hint = script->types->bytecodeMap + script->nTypeSets;
return BytecodeTypes(script, pc, hint, script->types->typeArray());
}
struct AllocationSiteKey : public DefaultHasher<AllocationSiteKey> {
@ -1109,61 +1106,65 @@ TypeSet::clearObjects()
objectSet = nullptr;
}
inline void
TypeSet::addType(ExclusiveContext *cxArg, Type type)
bool
TypeSet::addType(Type type, LifoAlloc *alloc, bool *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());
JS_ASSERT_IF(padded, !*padded);
if (unknown())
return;
return true;
if (type.isUnknown()) {
flags |= TYPE_FLAG_BASE_MASK;
clearObjects();
JS_ASSERT(unknown());
} else if (type.isPrimitive()) {
if (padded)
*padded = true;
return true;
}
if (type.isPrimitive()) {
TypeFlags flag = PrimitiveTypeFlag(type.primitive());
if (flags & flag)
return;
return true;
/* 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;
} else {
if (flags & TYPE_FLAG_ANYOBJECT)
return;
if (type.isAnyObject())
goto unknownObject;
if (padded)
*padded = true;
return true;
}
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>
(cxArg->typeLifoAlloc(), objectSet, objectCount, object);
if (!pentry) {
cxArg->compartment()->types.setPendingNukeTypes(cxArg);
return;
}
(*alloc, objectSet, objectCount, object);
if (!pentry)
return false;
if (*pentry)
return;
return true;
*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) {
@ -1173,6 +1174,27 @@ TypeSet::addType(ExclusiveContext *cxArg, Type type)
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));
@ -1422,54 +1444,6 @@ 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

View File

@ -3446,6 +3446,24 @@ js::GetProperty(JSContext *cx, HandleValue v, HandlePropertyName name, MutableHa
return JSObject::getProperty(cx, obj, obj, name, vp);
}
bool
js::CallProperty(JSContext *cx, HandleValue v, HandlePropertyName name, MutableHandleValue vp)
{
if (!GetProperty(cx, v, name, vp))
return false;
#if JS_HAS_NO_SUCH_METHOD
if (JS_UNLIKELY(vp.isPrimitive()) && v.isObject())
{
RootedObject obj(cx, &v.toObject());
if (!OnUnknownMethod(cx, obj, StringValue(name), vp))
return false;
}
#endif
return true;
}
bool
js::GetScopeName(JSContext *cx, HandleObject scopeChain, HandlePropertyName name, MutableHandleValue vp)
{

View File

@ -354,6 +354,9 @@ Throw(JSContext *cx, HandleValue v);
bool
GetProperty(JSContext *cx, HandleValue value, HandlePropertyName name, MutableHandleValue vp);
bool
CallProperty(JSContext *cx, HandleValue value, HandlePropertyName name, MutableHandleValue vp);
bool
GetScopeName(JSContext *cx, HandleObject obj, HandlePropertyName name, MutableHandleValue vp);