Bug 995336 - Use IonBuilder for arguments usage analysis, r=jandem.

This commit is contained in:
Brian Hackett 2014-04-25 13:01:37 -06:00
Родитель 1bfbc15260
Коммит d6253b9c6e
21 изменённых файлов: 372 добавлений и 376 удалений

Просмотреть файл

@ -300,7 +300,6 @@ static const PhaseInfo phases[] = {
{ PHASE_DISCARD_TI, "Discard TI", PHASE_DISCARD_ANALYSIS },
{ PHASE_FREE_TI_ARENA, "Free TI Arena", PHASE_DISCARD_ANALYSIS },
{ PHASE_SWEEP_TYPES, "Sweep Types", PHASE_DISCARD_ANALYSIS },
{ PHASE_CLEAR_SCRIPT_ANALYSIS, "Clear Script Analysis", PHASE_DISCARD_ANALYSIS },
{ PHASE_SWEEP_OBJECT, "Sweep Object", PHASE_SWEEP },
{ PHASE_SWEEP_STRING, "Sweep String", PHASE_SWEEP },
{ PHASE_SWEEP_SCRIPT, "Sweep Script", PHASE_SWEEP },

Просмотреть файл

@ -52,7 +52,6 @@ enum Phase {
PHASE_DISCARD_TI,
PHASE_FREE_TI_ARENA,
PHASE_SWEEP_TYPES,
PHASE_CLEAR_SCRIPT_ANALYSIS,
PHASE_SWEEP_OBJECT,
PHASE_SWEEP_STRING,
PHASE_SWEEP_SCRIPT,

Просмотреть файл

@ -10,6 +10,7 @@
#include "jit/BaselineIC.h"
#include "jit/BaselineJIT.h"
#include "jit/FixedList.h"
#include "jit/IonAnalysis.h"
#include "jit/IonLinker.h"
#include "jit/IonSpewer.h"
#ifdef JS_ION_PERF
@ -79,16 +80,9 @@ BaselineCompiler::compile()
AutoTraceLog logScript(logger, TraceLogCreateTextId(logger, script));
AutoTraceLog logCompile(logger, TraceLogger::BaselineCompilation);
if (!script->ensureHasTypes(cx))
if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx))
return Method_Error;
// Only need to analyze scripts which are marked |argumensHasVarBinding|, to
// compute |needsArgsObj| flag.
if (script->argumentsHasVarBinding()) {
if (!script->ensureRanAnalysis(cx))
return Method_Error;
}
// Pin analysis info during compilation.
types::AutoEnterAnalysis autoEnterAnalysis(cx);
@ -240,18 +234,7 @@ BaselineCompiler::compile()
baselineScript->toggleSPS(true);
uint32_t *bytecodeMap = baselineScript->bytecodeTypeMap();
uint32_t added = 0;
for (jsbytecode *pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) {
JSOp op = JSOp(*pc);
if (js_CodeSpec[op].format & JOF_TYPESET) {
bytecodeMap[added++] = script->pcToOffset(pc);
if (added == script->nTypeSets())
break;
}
}
JS_ASSERT(added == script->nTypeSets());
types::FillBytecodeTypeMap(script, bytecodeMap);
// The last entry in the last index found, and is used to avoid binary
// searches for the sought entry when queries are in linear order.

Просмотреть файл

@ -304,6 +304,9 @@ TryToSpecializeBinaryArithOp(ICStub **stubs,
MIRType
BaselineInspector::expectedBinaryArithSpecialization(jsbytecode *pc)
{
if (!hasBaselineScript())
return MIRType_None;
MIRType result;
ICStub *stubs[2];
@ -449,6 +452,9 @@ BaselineInspector::getTemplateObjectForNative(jsbytecode *pc, Native native)
DeclEnvObject *
BaselineInspector::templateDeclEnvObject()
{
if (!hasBaselineScript())
return nullptr;
JSObject *res = &templateCallObject()->as<ScopeObject>().enclosingScope();
JS_ASSERT(res);
@ -458,6 +464,9 @@ BaselineInspector::templateDeclEnvObject()
CallObject *
BaselineInspector::templateCallObject()
{
if (!hasBaselineScript())
return nullptr;
JSObject *res = baselineScript()->templateScope();
JS_ASSERT(res);
@ -467,6 +476,9 @@ BaselineInspector::templateCallObject()
JSObject *
BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter)
{
if (!hasBaselineScript())
return nullptr;
const ICEntry &entry = icEntryFromPC(pc);
for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
if (stub->isGetProp_CallScripted() ||
@ -485,6 +497,9 @@ BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, J
JSObject *
BaselineInspector::commonSetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonSetter)
{
if (!hasBaselineScript())
return nullptr;
const ICEntry &entry = icEntryFromPC(pc);
for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) {

Просмотреть файл

@ -297,6 +297,10 @@ class CompileInfo
return executionMode_;
}
bool executionModeIsAnalysis() const {
return executionMode_ == DefinitePropertiesAnalysis || executionMode_ == ArgumentsUsageAnalysis;
}
bool isParallelExecution() const {
return executionMode_ == ParallelExecution;
}

Просмотреть файл

@ -67,6 +67,7 @@ CanIonCompile(JSScript *script, ExecutionMode cmode)
case SequentialExecution: return script->canIonCompile();
case ParallelExecution: return script->canParallelIonCompile();
case DefinitePropertiesAnalysis: return true;
case ArgumentsUsageAnalysis: return true;
default:;
}
MOZ_ASSUME_UNREACHABLE("No such execution mode");

Просмотреть файл

@ -1940,11 +1940,6 @@ CheckScript(JSContext *cx, JSScript *script, bool osr)
return false;
}
if (!script->analyzedArgsUsage() && !script->ensureRanAnalysis(cx)) {
IonSpew(IonSpew_Abort, "OOM under ensureRanAnalysis");
return false;
}
if (!script->compileAndGo()) {
IonSpew(IonSpew_Abort, "not compile-and-go");
return false;

Просмотреть файл

@ -2241,7 +2241,7 @@ jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
IonContext ictx(cx, &temp);
if (!cx->compartment()->ensureJitCompartmentExists(cx))
return Method_Error;
return false;
if (!script->hasBaselineScript()) {
MethodStatus status = BaselineCompile(cx, script);
@ -2301,7 +2301,6 @@ jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
// appear in the graph.
Vector<MInstruction *> instructions(cx);
Vector<MDefinition *> useWorklist(cx);
for (MUseDefIterator uses(thisValue); uses; uses++) {
MDefinition *use = uses.def();
@ -2361,3 +2360,114 @@ jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
return true;
}
static bool
ArgumentsUseCanBeLazy(JSContext *cx, JSScript *script, MInstruction *ins, size_t index)
{
// We can read the frame's arguments directly for f.apply(x, arguments).
if (ins->isCall()) {
if (*ins->toCall()->resumePoint()->pc() == JSOP_FUNAPPLY &&
ins->toCall()->numActualArgs() == 2 &&
index == MCall::IndexOfArgument(1))
{
return true;
}
}
// arguments[i] can read fp->canonicalActualArg(i) directly.
if (ins->isCallGetElement() && index == 0)
return true;
// arguments.length length can read fp->numActualArgs() directly.
if (ins->isCallGetProperty() && index == 0 && ins->toCallGetProperty()->name() == cx->names().length)
return true;
return false;
}
bool
jit::AnalyzeArgumentsUsage(JSContext *cx, JSScript *scriptArg)
{
RootedScript script(cx, scriptArg);
types::AutoEnterAnalysis enter(cx);
JS_ASSERT(!script->analyzedArgsUsage());
// Treat the script as needing an arguments object until we determine it
// does not need one. This both allows us to easily see where the arguments
// object can escape through assignments to the function's named arguments,
// and also simplifies handling of early returns.
script->setNeedsArgsObj(true);
if (!jit::IsIonEnabled(cx) || !script->compileAndGo())
return true;
static const uint32_t MAX_SCRIPT_SIZE = 10000;
if (script->length() > MAX_SCRIPT_SIZE)
return true;
if (!script->ensureHasTypes(cx))
return false;
LifoAlloc alloc(types::TypeZone::TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
TempAllocator temp(&alloc);
IonContext ictx(cx, &temp);
if (!cx->compartment()->ensureJitCompartmentExists(cx))
return false;
MIRGraph graph(&temp);
CompileInfo info(script, script->functionNonDelazifying(),
/* osrPc = */ nullptr, /* constructing = */ false,
ArgumentsUsageAnalysis,
/* needsArgsObj = */ true);
AutoTempAllocatorRooter root(cx, &temp);
const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(Optimization_Normal);
types::CompilerConstraintList *constraints = types::NewCompilerConstraintList(temp);
if (!constraints)
return false;
BaselineInspector inspector(script);
const JitCompileOptions options(cx);
IonBuilder builder(nullptr, CompileCompartment::get(cx->compartment()), options, &temp, &graph, constraints,
&inspector, &info, optimizationInfo, /* baselineFrame = */ nullptr);
if (!builder.build()) {
if (builder.abortReason() == AbortReason_Alloc)
return false;
return true;
}
if (!SplitCriticalEdges(graph))
return false;
if (!RenumberBlocks(graph))
return false;
if (!BuildDominatorTree(graph))
return false;
if (!EliminatePhis(&builder, graph, AggressiveObservability))
return false;
MDefinition *argumentsValue = graph.begin()->getSlot(info.argsObjSlot());
for (MUseDefIterator uses(argumentsValue); uses; uses++) {
MDefinition *use = uses.def();
// Don't track |arguments| through assignments to phis.
if (!use->isInstruction())
return true;
if (!ArgumentsUseCanBeLazy(cx, script, use->toInstruction(), uses.index()))
return true;
}
script->setNeedsArgsObj(false);
return true;
}

Просмотреть файл

@ -7,6 +7,8 @@
#ifndef jit_IonAnalysis_h
#define jit_IonAnalysis_h
#ifdef JS_ION
// This file declares various analysis passes that operate on MIR.
#include "jit/IonAllocPolicy.h"
@ -136,7 +138,12 @@ AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
types::TypeObject *type, HandleObject baseobj,
Vector<types::TypeNewScript::Initializer> *initializerList);
bool
AnalyzeArgumentsUsage(JSContext *cx, JSScript *script);
} // namespace jit
} // namespace js
#endif // JS_ION
#endif /* jit_IonAnalysis_h */

Просмотреть файл

@ -124,6 +124,7 @@ IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp,
argTypes(nullptr),
typeArray(nullptr),
typeArrayHint(0),
bytecodeTypeMap(nullptr),
loopDepth_(loopDepth),
callerResumePoint_(nullptr),
callerBuilder_(nullptr),
@ -145,7 +146,7 @@ IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp,
script_ = info->script();
pc = info->startPC();
JS_ASSERT(script()->hasBaselineScript());
JS_ASSERT(script()->hasBaselineScript() == (info->executionMode() != ArgumentsUsageAnalysis));
JS_ASSERT(!!analysisContext == (info->executionMode() == DefinitePropertiesAnalysis));
}
@ -609,6 +610,17 @@ IonBuilder::init()
if (!analysis().init(alloc(), gsn))
return false;
// The baseline script normally has the bytecode type map, but compute
// it ourselves if we do not have a baseline script.
if (script()->hasBaselineScript()) {
bytecodeTypeMap = script()->baselineScript()->bytecodeTypeMap();
} else {
bytecodeTypeMap = alloc_->lifoAlloc()->newArrayUninitialized<uint32_t>(script()->nTypeSets());
if (!bytecodeTypeMap)
return false;
types::FillBytecodeTypeMap(script(), bytecodeTypeMap);
}
return true;
}
@ -986,8 +998,10 @@ IonBuilder::initScopeChain(MDefinition *callee)
scope = MFunctionEnvironment::New(alloc(), callee);
current->add(scope);
// This reproduce what is done in CallObject::createForFunction
if (fun->isHeavyweight()) {
// This reproduce what is done in CallObject::createForFunction. Skip
// this for analyses, as the script might not have a baseline script
// with template objects yet.
if (fun->isHeavyweight() && !info().executionModeIsAnalysis()) {
if (fun->isNamedLambda()) {
scope = createDeclEnvObject(callee, scope);
if (!scope)
@ -3573,7 +3587,12 @@ IonBuilder::jsop_try()
return abort("Has try-finally");
// Try-catch within inline frames is not yet supported.
JS_ASSERT(script()->uninlineable() && !isInlineBuilder());
JS_ASSERT(!isInlineBuilder());
// Try-catch during the arguments usage analysis is not yet supported. Code
// accessing the arguments within the 'catch' block is not accounted for.
if (info().executionMode() == ArgumentsUsageAnalysis)
return abort("Try-catch during arguments usage analysis");
graph().setHasTryBlock();
@ -4096,6 +4115,10 @@ IonBuilder::makeInliningDecision(JSFunction *target, CallInfo &callInfo)
if (target == nullptr)
return InliningDecision_DontInline;
// Never inline during the arguments usage analysis.
if (info().executionMode() == ArgumentsUsageAnalysis)
return InliningDecision_DontInline;
// Native functions provide their own detection in inlineNativeCall().
if (target->isNative())
return InliningDecision_Inline;
@ -5641,14 +5664,8 @@ IonBuilder::jsop_initprop(PropertyName *name)
// In parallel execution, we never require write barriers. See
// forkjoin.cpp for more information.
switch (info().executionMode()) {
case SequentialExecution:
case DefinitePropertiesAnalysis:
break;
case ParallelExecution:
if (info().executionMode() == ParallelExecution)
needsBarrier = false;
break;
}
if (templateObject->isFixedSlot(shape->slot())) {
MStoreFixedSlot *store = MStoreFixedSlot::New(alloc(), obj, shape->slot(), value);
@ -6664,6 +6681,21 @@ IonBuilder::jsop_getelem()
MDefinition *index = current->pop();
MDefinition *obj = current->pop();
// Always use a call if we are performing analysis and not actually
// emitting code, to simplify later analysis.
if (info().executionModeIsAnalysis()) {
MInstruction *ins = MCallGetElement::New(alloc(), obj, index);
current->add(ins);
current->push(ins);
if (!resumeAfter(ins))
return false;
types::TemporaryTypeSet *types = bytecodeTypes(pc);
return pushTypeBarrier(ins, types, true);
}
bool emitted = false;
if (!getElemTryTypedObject(&emitted, obj, index) || emitted)
@ -8536,11 +8568,11 @@ IonBuilder::jsop_getprop(PropertyName *name)
bool barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
current->peek(-1), name, types);
// Always use a call if we are doing the definite properties analysis and
// Always use a call if we are performing 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().executionModeIsAnalysis() || types->empty()) {
MDefinition *obj = current->peek(-1);
MCallGetProperty *call = MCallGetProperty::New(alloc(), obj, name, *pc == JSOP_CALLPROP);
current->add(call);
@ -8549,7 +8581,7 @@ IonBuilder::jsop_getprop(PropertyName *name)
// constants read off the prototype chain, to allow inlining later on.
// In this case we still need the getprop call so that the later
// analysis knows when the |this| value has been read from.
if (info().executionMode() == DefinitePropertiesAnalysis) {
if (info().executionModeIsAnalysis()) {
if (!getPropTryConstant(&emitted, name, types) || emitted)
return emitted;
}
@ -9016,7 +9048,7 @@ IonBuilder::jsop_setprop(PropertyName *name)
// Always use a call if we are doing the definite properties analysis and
// not actually emitting code, to simplify later analysis.
if (info().executionMode() == DefinitePropertiesAnalysis) {
if (info().executionModeIsAnalysis()) {
MInstruction *ins = MCallSetProperty::New(alloc(), obj, value, name, script()->strict());
current->add(ins);
current->push(value);
@ -9602,11 +9634,10 @@ IonBuilder::jsop_this()
return true;
}
// If we are doing a definite properties analysis, we don't yet know the
// |this| type as its type object is being created right now. Instead of
// bailing out just push the |this| slot, as this code won't actually
// execute and it does not matter whether |this| is primitive.
if (info().executionMode() == DefinitePropertiesAnalysis) {
// If we are doing an analysis, we might not yet know the type of |this|.
// Instead of bailing out just push the |this| slot, as this code won't
// actually execute and it does not matter whether |this| is primitive.
if (info().executionModeIsAnalysis()) {
current->pushSlot(info().thisSlot());
return true;
}
@ -10007,7 +10038,7 @@ IonBuilder::addShapeGuard(MDefinition *obj, Shape *const shape, BailoutKind bail
types::TemporaryTypeSet *
IonBuilder::bytecodeTypes(jsbytecode *pc)
{
return types::TypeScript::BytecodeTypes(script(), pc, &typeArrayHint, typeArray);
return types::TypeScript::BytecodeTypes(script(), pc, bytecodeTypeMap, &typeArrayHint, typeArray);
}
TypeDescrSetHash *

Просмотреть файл

@ -837,6 +837,7 @@ class IonBuilder : public MIRGenerator
types::TemporaryTypeSet *thisTypes, *argTypes, *typeArray;
uint32_t typeArrayHint;
uint32_t *bytecodeTypeMap;
GSNCache gsn;
ScopeCoordinateNameCache scopeCoordinateNameCache;

Просмотреть файл

@ -1482,12 +1482,6 @@ IonBuilder::inlineForceSequentialOrInParallelSection(CallInfo &callInfo)
ExecutionMode executionMode = info().executionMode();
switch (executionMode) {
case SequentialExecution:
case DefinitePropertiesAnalysis:
// In sequential mode, leave as is, because we'd have to
// access the "in warmup" flag of the runtime.
return InliningStatus_NotInlined;
case ParallelExecution: {
// During Parallel Exec, we always force sequential, so
// replace with true. This permits UCE to eliminate the
@ -1498,6 +1492,11 @@ IonBuilder::inlineForceSequentialOrInParallelSection(CallInfo &callInfo)
current->push(ins);
return InliningStatus_Inlined;
}
default:
// In sequential mode, leave as is, because we'd have to
// access the "in warmup" flag of the runtime.
return InliningStatus_NotInlined;
}
MOZ_ASSUME_UNREACHABLE("Invalid execution mode");
@ -1522,11 +1521,6 @@ IonBuilder::inlineForkJoinGetSlice(CallInfo &callInfo)
callInfo.setImplicitlyUsedUnchecked();
switch (info().executionMode()) {
case SequentialExecution:
case DefinitePropertiesAnalysis:
// ForkJoinGetSlice acts as identity for sequential execution.
current->push(callInfo.getArg(0));
return InliningStatus_Inlined;
case ParallelExecution:
if (LIRGenerator::allowInlineForkJoinGetSlice()) {
MForkJoinGetSlice *getSlice = MForkJoinGetSlice::New(alloc(),
@ -1536,6 +1530,11 @@ IonBuilder::inlineForkJoinGetSlice(CallInfo &callInfo)
return InliningStatus_Inlined;
}
return InliningStatus_NotInlined;
default:
// ForkJoinGetSlice acts as identity for sequential execution.
current->push(callInfo.getArg(0));
return InliningStatus_Inlined;
}
MOZ_ASSUME_UNREACHABLE("Invalid execution mode");
@ -1551,11 +1550,10 @@ IonBuilder::inlineNewDenseArray(CallInfo &callInfo)
// par. mode we use inlined MIR.
ExecutionMode executionMode = info().executionMode();
switch (executionMode) {
case SequentialExecution:
case DefinitePropertiesAnalysis:
return inlineNewDenseArrayForSequentialExecution(callInfo);
case ParallelExecution:
return inlineNewDenseArrayForParallelExecution(callInfo);
default:
return inlineNewDenseArrayForSequentialExecution(callInfo);
}
MOZ_ASSUME_UNREACHABLE("unknown ExecutionMode");

Просмотреть файл

@ -447,6 +447,104 @@ ConstraintTypeSet::addConstraint(JSContext *cx, TypeConstraint *constraint, bool
return true;
}
void
TypeSet::clearObjects()
{
setBaseObjectCount(0);
objectSet = nullptr;
}
void
TypeSet::addType(Type type, LifoAlloc *alloc)
{
if (unknown())
return;
if (type.isUnknown()) {
flags |= TYPE_FLAG_BASE_MASK;
clearObjects();
JS_ASSERT(unknown());
return;
}
if (type.isPrimitive()) {
TypeFlags flag = PrimitiveTypeFlag(type.primitive());
if (flags & flag)
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;
return;
}
if (flags & TYPE_FLAG_ANYOBJECT)
return;
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)
goto unknownObject;
if (*pentry)
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 (false) {
unknownObject:
flags |= TYPE_FLAG_ANYOBJECT;
clearObjects();
}
}
void
ConstraintTypeSet::addType(ExclusiveContext *cxArg, Type type)
{
JS_ASSERT(cxArg->compartment()->activeAnalysis);
if (hasType(type))
return;
TypeSet::addType(type, &cxArg->typeLifoAlloc());
if (type.isObjectUnchecked() && unknownObject())
type = Type::AnyObjectType();
InferSpew(ISpewOps, "addType: %sT%p%s %s",
InferSpewColor(this), this, InferSpewColorReset(),
TypeString(type));
/* Propagate the type to all constraints. */
if (JSContext *cx = cxArg->maybeJSContext()) {
TypeConstraint *constraint = constraintList;
while (constraint) {
constraint->newType(cx, this, type);
constraint = constraint->next;
}
} else {
JS_ASSERT(!constraintList);
}
}
void
TypeSet::print()
{
@ -1075,7 +1173,7 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu
return true;
}
static void
MOZ_NEVER_INLINE void
CheckDefinitePropertiesTypeSet(JSContext *cx, TemporaryTypeSet *frozen, StackTypeSet *actual)
{
// The definite properties analysis happens on the main thread, so no new
@ -1123,6 +1221,9 @@ types::FinishDefinitePropertiesAnalysis(JSContext *cx, CompilerConstraintList *c
JSScript *script = entry.script;
JS_ASSERT(script->types);
if (!script->types)
MOZ_CRASH();
CheckDefinitePropertiesTypeSet(cx, entry.thisTypes, TypeScript::ThisTypes(script));
unsigned nargs = script->functionNonDelazifying()
@ -1922,81 +2023,6 @@ TypeCompartment::newTypeObject(ExclusiveContext *cx, const Class *clasp, Handle<
return object;
}
static inline jsbytecode *
PreviousOpcode(HandleScript script, jsbytecode *pc)
{
ScriptAnalysis *analysis = script->analysis();
JS_ASSERT(analysis->isReachable(pc));
if (pc == script->code())
return nullptr;
for (pc--;; pc--) {
if (analysis->isReachable(pc))
break;
}
return pc;
}
/*
* If pc is an array initializer within an outer multidimensional array
* initializer, find the opcode of the previous newarray. nullptr otherwise.
*/
static inline jsbytecode *
FindPreviousInnerInitializer(HandleScript script, jsbytecode *initpc)
{
if (!script->hasAnalysis())
return nullptr;
if (!script->analysis()->isReachable(initpc))
return nullptr;
/*
* Pattern match the following bytecode, which will appear between
* adjacent initializer elements:
*
* endinit (for previous initializer)
* initelem_array (for previous initializer)
* newarray
*/
if (*initpc != JSOP_NEWARRAY)
return nullptr;
jsbytecode *last = PreviousOpcode(script, initpc);
if (!last || *last != JSOP_INITELEM_ARRAY)
return nullptr;
last = PreviousOpcode(script, last);
if (!last || *last != JSOP_ENDINIT)
return nullptr;
/*
* Find the start of the previous initializer. Keep track of initializer
* depth to skip over inner initializers within the previous one (e.g. for
* arrays with three or more dimensions).
*/
size_t initDepth = 0;
jsbytecode *previnit;
for (previnit = last; previnit; previnit = PreviousOpcode(script, previnit)) {
if (*previnit == JSOP_ENDINIT)
initDepth++;
if (*previnit == JSOP_NEWINIT ||
*previnit == JSOP_NEWARRAY ||
*previnit == JSOP_NEWOBJECT)
{
if (--initDepth == 0)
break;
}
}
if (!previnit || *previnit != JSOP_NEWARRAY)
return nullptr;
return previnit;
}
TypeObject *
TypeCompartment::addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key)
{
@ -2015,24 +2041,8 @@ TypeCompartment::addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey ke
TypeObject *res = nullptr;
/*
* If this is an array initializer nested in another array initializer,
* try to reuse the type objects from earlier elements to avoid
* distinguishing elements of the outer array unnecessarily.
*/
jsbytecode *pc = key.script->offsetToPC(key.offset);
RootedScript keyScript(cx, key.script);
jsbytecode *prev = FindPreviousInnerInitializer(keyScript, pc);
if (prev) {
AllocationSiteKey nkey;
nkey.script = key.script;
nkey.offset = key.script->pcToOffset(prev);
nkey.kind = JSProto_Array;
AllocationSiteTable::Ptr p = cx->compartment()->types.allocationSiteTable->lookup(nkey);
if (p)
res = p->value();
}
if (!res) {
RootedObject proto(cx);
@ -3488,6 +3498,21 @@ IsAboutToBeFinalized(TypeObjectKey *key)
return isAboutToBeFinalized;
}
void
types::FillBytecodeTypeMap(JSScript *script, uint32_t *bytecodeMap)
{
uint32_t added = 0;
for (jsbytecode *pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) {
JSOp op = JSOp(*pc);
if (js_CodeSpec[op].format & JOF_TYPESET) {
bytecodeMap[added++] = script->pcToOffset(pc);
if (added == script->nTypeSets())
break;
}
}
JS_ASSERT(added == script->nTypeSets());
}
void
types::TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
{
@ -3609,28 +3634,6 @@ JSScript::makeTypes(JSContext *cx)
}
#endif
return analyzedArgsUsage() || ensureRanAnalysis(cx);
}
bool
JSScript::makeAnalysis(JSContext *cx)
{
JS_ASSERT(types && !types->analysis);
AutoEnterAnalysis enter(cx);
types->analysis = cx->typeLifoAlloc().new_<ScriptAnalysis>(this);
if (!types->analysis)
return false;
RootedScript self(cx, this);
if (!self->types->analysis->analyzeBytecode(cx)) {
self->types->analysis = nullptr;
return false;
}
return true;
}
@ -4359,6 +4362,8 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes, bool *oom)
/* Sweep and find compressed indexes for each compiler output. */
size_t newCompilerOutputCount = 0;
#ifdef JS_ION
if (compilerOutputs) {
for (size_t i = 0; i < compilerOutputs->length(); i++) {
CompilerOutput &output = (*compilerOutputs)[i];
@ -4373,6 +4378,7 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes, bool *oom)
}
}
}
#endif
{
gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_TI);
@ -4384,11 +4390,15 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes, bool *oom)
if (releaseTypes) {
if (script->hasParallelIonScript()) {
#ifdef JS_ION
// It's possible that we preserved the parallel
// IonScript. The heuristic for their preservation is
// independent of general JIT code preservation.
MOZ_ASSERT(jit::ShouldPreserveParallelJITCode(rt, script));
script->parallelIonScript()->recompileInfoRef().shouldSweep(*this);
#else
MOZ_CRASH();
#endif
} else {
script->types->destroy();
script->types = nullptr;
@ -4440,14 +4450,6 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes, bool *oom)
JS_ALWAYS_TRUE(compilerOutputs->resize(newCompilerOutputCount));
}
{
gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_CLEAR_SCRIPT_ANALYSIS);
for (CellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
script->clearAnalysis();
}
}
{
gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_FREE_TI_ARENA);
rt->freeLifoAlloc.transferFrom(&oldAlloc);
@ -4577,4 +4579,3 @@ TypeDescr &
js::types::TypeTypedObject::descr() {
return descr_->as<TypeDescr>();
}

Просмотреть файл

@ -150,7 +150,13 @@ enum ExecutionMode {
* MIR analysis performed when invoking 'new' on a script, to determine
* definite properties. Used by the optimizing JIT.
*/
DefinitePropertiesAnalysis
DefinitePropertiesAnalysis,
/*
* MIR analysis performed when executing a script which uses its arguments,
* when it is not known whether a lazy arguments value can be used.
*/
ArgumentsUsageAnalysis
};
/*
@ -186,10 +192,6 @@ namespace jit {
class TempAllocator;
}
namespace analyze {
class ScriptAnalysis;
}
namespace types {
class TypeZone;
@ -544,7 +546,7 @@ class TypeSet
static TemporaryTypeSet *unionSets(TypeSet *a, TypeSet *b, LifoAlloc *alloc);
/* Add a type to this set using the specified allocator. */
inline void addType(Type type, LifoAlloc *alloc);
void addType(Type type, LifoAlloc *alloc);
/* Get a list of all types in this set. */
typedef Vector<Type, 1, SystemAllocPolicy> TypeList;
@ -598,7 +600,7 @@ class TypeSet
}
inline void setBaseObjectCount(uint32_t count);
inline void clearObjects();
void clearObjects();
};
/* Superclass common to stack and heap type sets. */
@ -614,7 +616,7 @@ class ConstraintTypeSet : public TypeSet
* Add a type to this set, calling any constraint handlers if this is a new
* possible type.
*/
inline void addType(ExclusiveContext *cx, Type type);
void addType(ExclusiveContext *cx, Type type);
/* Add a new constraint to this set. */
bool addConstraint(JSContext *cx, TypeConstraint *constraint, bool callExisting = true);
@ -1242,9 +1244,6 @@ class TypeScript
{
friend class ::JSScript;
/* Analysis information for the script, cleared on each GC. */
analyze::ScriptAnalysis *analysis;
public:
/* Array of type type sets for variables and JOF_TYPESET ops. */
StackTypeSet *typeArray() const { return (StackTypeSet *) (uintptr_t(this) + sizeof(TypeScript)); }
@ -1258,7 +1257,7 @@ class TypeScript
static inline StackTypeSet *BytecodeTypes(JSScript *script, jsbytecode *pc);
template <typename TYPESET>
static inline TYPESET *BytecodeTypes(JSScript *script, jsbytecode *pc,
static inline TYPESET *BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *bytecodeMap,
uint32_t *hint, TYPESET *typeArray);
/* Get a type object for an allocation site in this script. */
@ -1310,6 +1309,9 @@ class TypeScript
#endif
};
void
FillBytecodeTypeMap(JSScript *script, uint32_t *bytecodeMap);
class RecompileInfo;
// Allocate a CompilerOutput for a finished compilation and generate the type

Просмотреть файл

@ -567,15 +567,10 @@ TypeScript::ArgTypes(JSScript *script, unsigned i)
template <typename TYPESET>
/* static */ inline TYPESET *
TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *hint, TYPESET *typeArray)
TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *bytecodeMap,
uint32_t *hint, TYPESET *typeArray)
{
JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET);
#ifdef JS_ION
uint32_t *bytecodeMap = script->baselineScript()->bytecodeTypeMap();
#else
uint32_t *bytecodeMap = nullptr;
MOZ_CRASH();
#endif
uint32_t offset = script->pcToOffset(pc);
// See if this pc is the next typeset opcode after the last one looked up.
@ -617,11 +612,11 @@ TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc)
JS_ASSERT(CurrentThreadCanAccessRuntime(script->runtimeFromMainThread()));
#ifdef JS_ION
uint32_t *hint = script->baselineScript()->bytecodeTypeMap() + script->nTypeSets();
return BytecodeTypes(script, pc, script->baselineScript()->bytecodeTypeMap(),
hint, script->types->typeArray());
#else
uint32_t *hint = nullptr;
MOZ_CRASH();
#endif
return BytecodeTypes(script, pc, hint, script->types->typeArray());
}
struct AllocationSiteKey : public DefaultHasher<AllocationSiteKey> {
@ -1023,104 +1018,6 @@ TypeSet::setBaseObjectCount(uint32_t count)
| (count << TYPE_FLAG_OBJECT_COUNT_SHIFT);
}
inline void
TypeSet::clearObjects()
{
setBaseObjectCount(0);
objectSet = nullptr;
}
void
TypeSet::addType(Type type, LifoAlloc *alloc)
{
if (unknown())
return;
if (type.isUnknown()) {
flags |= TYPE_FLAG_BASE_MASK;
clearObjects();
JS_ASSERT(unknown());
return;
}
if (type.isPrimitive()) {
TypeFlags flag = PrimitiveTypeFlag(type.primitive());
if (flags & flag)
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;
return;
}
if (flags & TYPE_FLAG_ANYOBJECT)
return;
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)
goto unknownObject;
if (*pentry)
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 (false) {
unknownObject:
flags |= TYPE_FLAG_ANYOBJECT;
clearObjects();
}
}
inline void
ConstraintTypeSet::addType(ExclusiveContext *cxArg, Type type)
{
JS_ASSERT(cxArg->compartment()->activeAnalysis);
if (hasType(type))
return;
TypeSet::addType(type, &cxArg->typeLifoAlloc());
if (type.isObjectUnchecked() && unknownObject())
type = Type::AnyObjectType();
InferSpew(ISpewOps, "addType: %sT%p%s %s",
InferSpewColor(this), this, InferSpewColorReset(),
TypeString(type));
/* Propagate the type to all constraints. */
if (JSContext *cx = cxArg->maybeJSContext()) {
TypeConstraint *constraint = constraintList;
while (constraint) {
constraint->newType(cx, this, type);
constraint = constraint->next;
}
} else {
JS_ASSERT(!constraintList);
}
}
inline void
HeapTypeSet::newPropertyState(ExclusiveContext *cxArg)
{
@ -1356,39 +1253,6 @@ JSScript::ensureHasTypes(JSContext *cx)
return types || makeTypes(cx);
}
inline bool
JSScript::ensureRanAnalysis(JSContext *cx)
{
js::types::AutoEnterAnalysis aea(cx);
if (!ensureHasTypes(cx))
return false;
if (!hasAnalysis() && !makeAnalysis(cx))
return false;
JS_ASSERT(hasAnalysis());
return true;
}
inline bool
JSScript::hasAnalysis()
{
return types && types->analysis;
}
inline js::analyze::ScriptAnalysis *
JSScript::analysis()
{
JS_ASSERT(hasAnalysis());
return types->analysis;
}
inline void
JSScript::clearAnalysis()
{
if (types)
types->analysis = nullptr;
}
namespace js {
template <>

Просмотреть файл

@ -284,19 +284,6 @@ js_DumpPCCounts(JSContext *cx, HandleScript script, js::Sprinter *sp)
// Bytecode Parser
/////////////////////////////////////////////////////////////////////
// Ensure that script analysis reports the same stack depth.
static void
AssertStackDepth(JSScript *script, uint32_t offset, uint32_t stackDepth) {
/*
* If this assertion fails, run the failing test case under gdb and use the
* following gdb command to understand the execution path of this function.
*
* call js_DumpScriptDepth(cx, script, pc)
*/
if (script->hasAnalysis())
script->analysis()->assertMatchingStackDepthAtOffset(offset, stackDepth);
}
namespace {
class BytecodeParser
@ -500,7 +487,6 @@ BytecodeParser::addJump(uint32_t offset, uint32_t *currentOffset,
code = alloc().new_<Bytecode>();
if (!code)
return false;
AssertStackDepth(script_, offset, stackDepth);
if (!code->captureOffsetStack(alloc(), offsetStack, stackDepth)) {
reportOOM();
return false;
@ -653,7 +639,6 @@ BytecodeParser::parse()
reportOOM();
return false;
}
AssertStackDepth(script_, successorOffset, stackDepth);
if (!nextcode->captureOffsetStack(alloc(), offsetStack, stackDepth)) {
reportOOM();
return false;

Просмотреть файл

@ -3452,13 +3452,17 @@ void
JSScript::setArgumentsHasVarBinding()
{
argsHasVarBinding_ = true;
#ifdef JS_ION
needsArgsAnalysis_ = true;
#else
// The arguments analysis is performed by IonBuilder.
needsArgsObj_ = true;
#endif
}
void
JSScript::setNeedsArgsObj(bool needsArgsObj)
{
JS_ASSERT(!analyzedArgsUsage());
JS_ASSERT_IF(needsArgsObj, argumentsHasVarBinding());
needsArgsAnalysis_ = false;
needsArgsObj_ = needsArgsObj;

Просмотреть файл

@ -50,10 +50,6 @@ class Shape;
class WatchpointMap;
class NestedScopeObject;
namespace analyze {
class ScriptAnalysis;
}
namespace frontend {
class BytecodeEmitter;
}
@ -1133,7 +1129,7 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
/*
* As an optimization, even when argsHasLocalBinding, the function prologue
* may not need to create an arguments object. This is determined by
* needsArgsObj which is set by ScriptAnalysis::analyzeSSA before running
* needsArgsObj which is set by AnalyzeArgumentsUsage before running
* the script the first time. When !needsArgsObj, the prologue may simply
* write MagicValue(JS_OPTIMIZED_ARGUMENTS) to 'arguments's slot and any
* uses of 'arguments' will be guaranteed to handle this magic value.
@ -1141,6 +1137,7 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
* that needsArgsObj is only called after the script has been analyzed.
*/
bool analyzedArgsUsage() const { return !needsArgsAnalysis_; }
inline bool ensureHasAnalyzedArgsUsage(JSContext *cx);
bool needsArgsObj() const {
JS_ASSERT(analyzedArgsUsage());
return needsArgsObj_;
@ -1316,19 +1313,6 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
/* Ensure the script has a TypeScript. */
inline bool ensureHasTypes(JSContext *cx);
/*
* Ensure the script has bytecode analysis information. Performed when the
* script first runs, or first runs after a TypeScript GC purge.
*/
inline bool ensureRanAnalysis(JSContext *cx);
/* Ensure the script has type inference analysis information. */
inline bool ensureRanInference(JSContext *cx);
inline bool hasAnalysis();
inline void clearAnalysis();
inline js::analyze::ScriptAnalysis *analysis();
inline js::GlobalObject &global() const;
js::GlobalObject &uninlinedGlobal() const;
@ -1341,7 +1325,6 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
private:
bool makeTypes(JSContext *cx);
bool makeAnalysis(JSContext *cx);
public:
uint32_t getUseCount() const {

Просмотреть файл

@ -11,6 +11,7 @@
#include "jit/AsmJSLink.h"
#include "jit/BaselineJIT.h"
#include "jit/IonAnalysis.h"
#include "vm/ScopeObject.h"
#include "jscompartmentinlines.h"
@ -173,4 +174,16 @@ JSScript::setBaselineScript(JSContext *maybecx, js::jit::BaselineScript *baselin
updateBaselineOrIonRaw();
}
inline bool
JSScript::ensureHasAnalyzedArgsUsage(JSContext *cx)
{
if (analyzedArgsUsage())
return true;
#ifdef JS_ION
return js::jit::AnalyzeArgumentsUsage(cx, this);
#else
MOZ_CRASH();
#endif
}
#endif /* jsscriptinlines_h */

Просмотреть файл

@ -33,6 +33,7 @@
#include "builtin/Eval.h"
#include "jit/BaselineJIT.h"
#include "jit/Ion.h"
#include "jit/IonAnalysis.h"
#include "js/OldDebugAPI.h"
#include "vm/Debugger.h"
#include "vm/Opcodes.h"
@ -2865,7 +2866,7 @@ CASE(JSOP_TABLESWITCH)
CASE(JSOP_ARGUMENTS)
JS_ASSERT(!REGS.fp()->fun()->hasRest());
if (!script->analyzedArgsUsage() && !script->ensureRanAnalysis(cx))
if (!script->ensureHasAnalyzedArgsUsage(cx))
goto error;
if (script->needsArgsObj()) {
ArgumentsObject *obj = ArgumentsObject::createExpected(cx, REGS.fp());

Просмотреть файл

@ -1150,7 +1150,7 @@ class DebugScopeProxy : public BaseProxyHandler
if (scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) {
CallObject &callobj = scope->as<CallObject>();
RootedScript script(cx, callobj.callee().nonLazyScript());
if (!script->ensureHasTypes(cx))
if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx))
return false;
Bindings &bindings = script->bindings;