зеркало из https://github.com/mozilla/gecko-dev.git
Bug 864468: IonMonkey: Skip argument type checks when type is known to match, r=jandem
This commit is contained in:
Родитель
3e22fefe23
Коммит
3fca9cbc83
|
@ -1568,7 +1568,10 @@ CodeGenerator::visitCallKnown(LCallKnown *call)
|
|||
masm.loadPtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg);
|
||||
|
||||
// Load script jitcode.
|
||||
if (call->mir()->needsArgCheck())
|
||||
masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &uncompiled);
|
||||
else
|
||||
masm.loadBaselineOrIonNoArgCheck(objreg, objreg, executionMode, &uncompiled);
|
||||
|
||||
// Nestle the StackPointer up to the argument vector.
|
||||
masm.freeStack(unusedStack);
|
||||
|
@ -2252,7 +2255,7 @@ CodeGenerator::maybeCreateScriptCounts()
|
|||
MResumePoint *resume = block->entryResumePoint();
|
||||
while (resume->caller())
|
||||
resume = resume->caller();
|
||||
uint32_t offset = resume->pc() - script->code;
|
||||
DebugOnly<uint32_t> offset = resume->pc() - script->code;
|
||||
JS_ASSERT(offset < script->length);
|
||||
}
|
||||
|
||||
|
@ -4902,6 +4905,10 @@ CodeGenerator::generate()
|
|||
return false;
|
||||
}
|
||||
|
||||
// Remember the entry offset to skip the argument check.
|
||||
masm.flushBuffer();
|
||||
setSkipArgCheckEntryOffset(masm.size());
|
||||
|
||||
if (!generatePrologue())
|
||||
return false;
|
||||
if (!generateBody())
|
||||
|
@ -4951,6 +4958,7 @@ CodeGenerator::link()
|
|||
graph.mir().numCallTargets());
|
||||
|
||||
ionScript->setMethod(code);
|
||||
ionScript->setSkipArgCheckEntryOffset(getSkipArgCheckEntryOffset());
|
||||
|
||||
SetIonScript(script, executionMode, ionScript);
|
||||
|
||||
|
|
|
@ -556,6 +556,7 @@ IonScript::IonScript()
|
|||
deoptTable_(NULL),
|
||||
osrPc_(NULL),
|
||||
osrEntryOffset_(0),
|
||||
skipArgCheckEntryOffset_(0),
|
||||
invalidateEpilogueOffset_(0),
|
||||
invalidateEpilogueDataOffset_(0),
|
||||
numBailouts_(0),
|
||||
|
|
|
@ -434,9 +434,8 @@ IonBuilder::build()
|
|||
if (!current)
|
||||
return false;
|
||||
|
||||
IonSpew(IonSpew_Scripts, "Analyzing script %s:%d (%p) (usecount=%d) (maxloopcount=%d)",
|
||||
script()->filename(), script()->lineno, (void *)script(), (int)script()->getUseCount(),
|
||||
(int)script()->getMaxLoopCount());
|
||||
IonSpew(IonSpew_Scripts, "Analyzing script %s:%d (%p) (usecount=%d)",
|
||||
script()->filename(), script()->lineno, (void *)script(), (int)script()->getUseCount());
|
||||
|
||||
if (!graph().addScript(script()))
|
||||
return false;
|
||||
|
@ -4750,6 +4749,49 @@ TestAreKnownDOMTypes(JSContext *cx, types::TypeSet *inTypes)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
ArgumentTypesMatch(MDefinition *def, types::StackTypeSet *calleeTypes)
|
||||
{
|
||||
if (def->resultTypeSet()) {
|
||||
JS_ASSERT(def->type() == MIRType_Value || def->mightBeType(def->type()));
|
||||
return def->resultTypeSet()->isSubset(calleeTypes);
|
||||
}
|
||||
|
||||
if (def->type() == MIRType_Value)
|
||||
return false;
|
||||
|
||||
return calleeTypes->mightBeType(ValueTypeFromMIRType(def->type()));
|
||||
}
|
||||
|
||||
static bool
|
||||
TestNeedsArgumentCheck(JSContext *cx, HandleFunction target, CallInfo &callInfo)
|
||||
{
|
||||
// If we have a known target, check if the caller arg types are a subset of callee.
|
||||
// Since typeset accumulates and can't decrease that means we don't need to check
|
||||
// the arguments anymore.
|
||||
if (!target->isInterpreted())
|
||||
return true;
|
||||
|
||||
RootedScript targetScript(cx, target->nonLazyScript());
|
||||
if (!targetScript->hasAnalysis())
|
||||
return true;
|
||||
|
||||
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), types::TypeScript::ArgTypes(targetScript, i)))
|
||||
return true;
|
||||
}
|
||||
for (size_t i = callInfo.argc(); i < target->nargs; i++) {
|
||||
if (!types::TypeScript::ArgTypes(targetScript, i)->mightBeType(JSVAL_TYPE_UNDEFINED))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
MCall *
|
||||
IonBuilder::makeCallHelper(HandleFunction target, CallInfo &callInfo, bool cloneAtCallsite)
|
||||
{
|
||||
|
@ -4819,6 +4861,7 @@ IonBuilder::makeCallHelper(HandleFunction target, CallInfo &callInfo, bool clone
|
|||
current->add(newThis);
|
||||
|
||||
thisArg = newThis;
|
||||
callInfo.setThis(newThis);
|
||||
}
|
||||
|
||||
// Pass |this| and function.
|
||||
|
@ -4844,6 +4887,9 @@ IonBuilder::makeCallHelper(HandleFunction target, CallInfo &callInfo, bool clone
|
|||
}
|
||||
}
|
||||
|
||||
if (target && !TestNeedsArgumentCheck(cx, target, callInfo))
|
||||
call->disableArgCheck();
|
||||
|
||||
call->initFunction(callInfo.fun());
|
||||
|
||||
current->add(call);
|
||||
|
|
|
@ -160,6 +160,9 @@ struct IonScript
|
|||
// Offset to OSR entrypoint from method_->raw(), or 0.
|
||||
uint32_t osrEntryOffset_;
|
||||
|
||||
// Offset to entrypoint skipping type arg check from method_->raw().
|
||||
uint32_t skipArgCheckEntryOffset_;
|
||||
|
||||
// Offset of the invalidation epilogue (which pushes this IonScript
|
||||
// and calls the invalidation thunk).
|
||||
uint32_t invalidateEpilogueOffset_;
|
||||
|
@ -302,6 +305,9 @@ struct IonScript
|
|||
static inline size_t offsetOfOsrEntryOffset() {
|
||||
return offsetof(IonScript, osrEntryOffset_);
|
||||
}
|
||||
static inline size_t offsetOfSkipArgCheckEntryOffset() {
|
||||
return offsetof(IonScript, skipArgCheckEntryOffset_);
|
||||
}
|
||||
|
||||
public:
|
||||
IonCode *method() const {
|
||||
|
@ -327,6 +333,13 @@ struct IonScript
|
|||
uint32_t osrEntryOffset() const {
|
||||
return osrEntryOffset_;
|
||||
}
|
||||
void setSkipArgCheckEntryOffset(uint32_t offset) {
|
||||
JS_ASSERT(!skipArgCheckEntryOffset_);
|
||||
skipArgCheckEntryOffset_ = offset;
|
||||
}
|
||||
uint32_t getSkipArgCheckEntryOffset() const {
|
||||
return skipArgCheckEntryOffset_;
|
||||
}
|
||||
bool containsCodeAddress(uint8_t *addr) const {
|
||||
return method()->raw() <= addr && addr <= method()->raw() + method()->instructionsSize();
|
||||
}
|
||||
|
|
|
@ -963,6 +963,38 @@ MacroAssembler::loadBaselineOrIonRaw(Register script, Register dest, ExecutionMo
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::loadBaselineOrIonNoArgCheck(Register script, Register dest, ExecutionMode mode,
|
||||
Label *failure)
|
||||
{
|
||||
if (mode == SequentialExecution) {
|
||||
loadPtr(Address(script, JSScript::offsetOfBaselineOrIonSkipArgCheck()), dest);
|
||||
if (failure)
|
||||
branchTestPtr(Assembler::Zero, dest, dest, failure);
|
||||
} else {
|
||||
// Find second register to get the offset to skip argument check
|
||||
Register offset = script;
|
||||
if (script == dest) {
|
||||
GeneralRegisterSet regs(GeneralRegisterSet::All());
|
||||
regs.take(dest);
|
||||
offset = regs.takeAny();
|
||||
}
|
||||
|
||||
loadPtr(Address(script, JSScript::offsetOfParallelIonScript()), dest);
|
||||
if (failure)
|
||||
branchPtr(Assembler::BelowOrEqual, dest, ImmWord(ION_COMPILING_SCRIPT), failure);
|
||||
|
||||
Push(offset);
|
||||
load32(Address(script, IonScript::offsetOfSkipArgCheckEntryOffset()), offset);
|
||||
|
||||
loadPtr(Address(dest, IonScript::offsetOfMethod()), dest);
|
||||
loadPtr(Address(dest, IonCode::offsetOfCode()), dest);
|
||||
addPtr(offset, dest);
|
||||
|
||||
Pop(offset);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::loadBaselineFramePtr(Register framePtr, Register dest)
|
||||
{
|
||||
|
|
|
@ -874,6 +874,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
}
|
||||
|
||||
void loadBaselineOrIonRaw(Register script, Register dest, ExecutionMode mode, Label *failure);
|
||||
void loadBaselineOrIonNoArgCheck(Register callee, Register dest, ExecutionMode mode, Label *failure);
|
||||
|
||||
void loadBaselineFramePtr(Register framePtr, Register dest);
|
||||
|
||||
|
|
|
@ -1365,11 +1365,14 @@ class MCall
|
|||
// Original value of argc from the bytecode.
|
||||
uint32_t numActualArgs_;
|
||||
|
||||
bool needsArgCheck_;
|
||||
|
||||
MCall(JSFunction *target, uint32_t numActualArgs, bool construct)
|
||||
: construct_(construct),
|
||||
target_(target),
|
||||
targetScript_(NULL),
|
||||
numActualArgs_(numActualArgs)
|
||||
numActualArgs_(numActualArgs),
|
||||
needsArgCheck_(true)
|
||||
{
|
||||
setResultType(MIRType_Value);
|
||||
}
|
||||
|
@ -1387,6 +1390,14 @@ class MCall
|
|||
return setOperand(FunctionOperandIndex, func);
|
||||
}
|
||||
|
||||
bool needsArgCheck() const {
|
||||
return needsArgCheck_;
|
||||
}
|
||||
|
||||
void disableArgCheck() {
|
||||
needsArgCheck_ = false;
|
||||
}
|
||||
|
||||
MPrepareCall *getPrepareCall() {
|
||||
return getOperand(PrepareCallOperandIndex)->toPrepareCall();
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph, Mac
|
|||
lastOsiPointOffset_(0),
|
||||
sps_(&gen->compartment->rt->spsProfiler, &lastPC_),
|
||||
osrEntryOffset_(0),
|
||||
skipArgCheckEntryOffset_(0),
|
||||
frameDepth_(graph->localSlotCount() * sizeof(STACK_SLOT_SIZE) +
|
||||
graph->argumentSlotCount() * sizeof(Value))
|
||||
{
|
||||
|
|
|
@ -91,6 +91,18 @@ class CodeGeneratorShared : public LInstructionVisitor
|
|||
return osrEntryOffset_;
|
||||
}
|
||||
|
||||
// The offset of the first instruction of the body.
|
||||
// This skips the arguments type checks.
|
||||
size_t skipArgCheckEntryOffset_;
|
||||
|
||||
inline void setSkipArgCheckEntryOffset(size_t offset) {
|
||||
JS_ASSERT(skipArgCheckEntryOffset_ == 0);
|
||||
skipArgCheckEntryOffset_ = offset;
|
||||
}
|
||||
inline size_t getSkipArgCheckEntryOffset() const {
|
||||
return skipArgCheckEntryOffset_;
|
||||
}
|
||||
|
||||
typedef js::Vector<SafepointIndex, 8, SystemAllocPolicy> SafepointIndices;
|
||||
|
||||
bool markArgumentSlots(LSafepoint *safepoint);
|
||||
|
|
|
@ -1146,11 +1146,6 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode, bool
|
|||
RootedScript script(cx);
|
||||
SET_SCRIPT(regs.fp()->script());
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
/* Reset the loop count on the script we're entering. */
|
||||
script->resetLoopCount();
|
||||
#endif
|
||||
|
||||
#if JS_TRACE_LOGGING
|
||||
AutoTraceLog logger(TraceLogging::defaultLogger(),
|
||||
TraceLogging::INTERPRETER_START,
|
||||
|
@ -1383,10 +1378,6 @@ ADD_EMPTY_CASE(JSOP_TRY)
|
|||
END_EMPTY_CASES
|
||||
|
||||
BEGIN_CASE(JSOP_LOOPHEAD)
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
script->incrLoopCount();
|
||||
#endif
|
||||
END_CASE(JSOP_LOOPHEAD)
|
||||
|
||||
BEGIN_CASE(JSOP_LABEL)
|
||||
|
@ -2424,9 +2415,6 @@ BEGIN_CASE(JSOP_FUNCALL)
|
|||
regs.fp()->setUseNewType();
|
||||
|
||||
SET_SCRIPT(regs.fp()->script());
|
||||
#ifdef JS_METHODJIT
|
||||
script->resetLoopCount();
|
||||
#endif
|
||||
|
||||
#ifdef JS_ION
|
||||
if (!newType && ion::IsEnabled(cx)) {
|
||||
|
|
|
@ -1916,11 +1916,7 @@ JSScript::isShortRunning()
|
|||
{
|
||||
return length < 100 &&
|
||||
hasAnalysis() &&
|
||||
!analysis()->hasFunctionCalls()
|
||||
#ifdef JS_METHODJIT
|
||||
&& getMaxLoopCount() < 40
|
||||
#endif
|
||||
;
|
||||
!analysis()->hasFunctionCalls();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -2883,11 +2879,15 @@ void
|
|||
JSScript::updateBaselineOrIonRaw()
|
||||
{
|
||||
#ifdef JS_ION
|
||||
if (hasIonScript())
|
||||
if (hasIonScript()) {
|
||||
baselineOrIonRaw = ion->method()->raw();
|
||||
else if (hasBaselineScript())
|
||||
baselineOrIonSkipArgCheck = ion->method()->raw() + ion->getSkipArgCheckEntryOffset();
|
||||
} else if (hasBaselineScript()) {
|
||||
baselineOrIonRaw = baseline->method()->raw();
|
||||
else
|
||||
baselineOrIonSkipArgCheck = baseline->method()->raw();
|
||||
} else {
|
||||
baselineOrIonRaw = NULL;
|
||||
baselineOrIonSkipArgCheck = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -412,10 +412,7 @@ class JSScript : public js::gc::Cell
|
|||
uint32_t useCount; /* Number of times the script has been called
|
||||
* or has had backedges taken. Reset if the
|
||||
* script's JIT code is forcibly discarded. */
|
||||
|
||||
uint32_t maxLoopCount; /* Maximum loop count that has been encountered. */
|
||||
uint32_t loopCount; /* Number of times a LOOPHEAD has been encountered.
|
||||
after a LOOPENTRY. Modified only by interpreter. */
|
||||
uint32_t PADDING32;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Unique identifier within the compartment for this script, used for
|
||||
|
@ -429,7 +426,6 @@ class JSScript : public js::gc::Cell
|
|||
|
||||
private:
|
||||
uint16_t PADDING16;
|
||||
|
||||
uint16_t version; /* JS version under which script was compiled */
|
||||
|
||||
public:
|
||||
|
@ -603,6 +599,7 @@ class JSScript : public js::gc::Cell
|
|||
* if there's no Baseline or Ion script.
|
||||
*/
|
||||
uint8_t *baselineOrIonRaw;
|
||||
uint8_t *baselineOrIonSkipArgCheck;
|
||||
|
||||
public:
|
||||
bool hasIonScript() const {
|
||||
|
@ -683,6 +680,9 @@ class JSScript : public js::gc::Cell
|
|||
static size_t offsetOfBaselineOrIonRaw() {
|
||||
return offsetof(JSScript, baselineOrIonRaw);
|
||||
}
|
||||
static size_t offsetOfBaselineOrIonSkipArgCheck() {
|
||||
return offsetof(JSScript, baselineOrIonSkipArgCheck);
|
||||
}
|
||||
|
||||
/*
|
||||
* Original compiled function for the script, if it has a function.
|
||||
|
@ -809,22 +809,6 @@ class JSScript : public js::gc::Cell
|
|||
static size_t offsetOfUseCount() { return offsetof(JSScript, useCount); }
|
||||
void resetUseCount() { useCount = 0; }
|
||||
|
||||
void resetLoopCount() {
|
||||
if (loopCount > maxLoopCount)
|
||||
maxLoopCount = loopCount;
|
||||
loopCount = 0;
|
||||
}
|
||||
|
||||
void incrLoopCount() {
|
||||
++loopCount;
|
||||
}
|
||||
|
||||
uint32_t getMaxLoopCount() {
|
||||
if (loopCount > maxLoopCount)
|
||||
maxLoopCount = loopCount;
|
||||
return maxLoopCount;
|
||||
}
|
||||
|
||||
/*
|
||||
* Size of the JITScript and all sections. If |mallocSizeOf| is NULL, the
|
||||
* size is computed analytically. (This method is implemented in
|
||||
|
|
Загрузка…
Ссылка в новой задаче