зеркало из https://github.com/mozilla/gecko-dev.git
Bug 995336 - Use IonBuilder for arguments usage analysis, r=jandem.
This commit is contained in:
Родитель
1bfbc15260
Коммит
d6253b9c6e
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче