Bug 864468: IonMonkey: Skip argument type checks when type is known to match, r=jandem

This commit is contained in:
Hannes Verschore 2013-05-08 09:54:39 +02:00
Родитель 3e22fefe23
Коммит 3fca9cbc83
12 изменённых файлов: 146 добавлений и 49 удалений

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

@ -1568,7 +1568,10 @@ CodeGenerator::visitCallKnown(LCallKnown *call)
masm.loadPtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg); masm.loadPtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg);
// Load script jitcode. // Load script jitcode.
masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &uncompiled); 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. // Nestle the StackPointer up to the argument vector.
masm.freeStack(unusedStack); masm.freeStack(unusedStack);
@ -2252,7 +2255,7 @@ CodeGenerator::maybeCreateScriptCounts()
MResumePoint *resume = block->entryResumePoint(); MResumePoint *resume = block->entryResumePoint();
while (resume->caller()) while (resume->caller())
resume = 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); JS_ASSERT(offset < script->length);
} }
@ -4902,6 +4905,10 @@ CodeGenerator::generate()
return false; return false;
} }
// Remember the entry offset to skip the argument check.
masm.flushBuffer();
setSkipArgCheckEntryOffset(masm.size());
if (!generatePrologue()) if (!generatePrologue())
return false; return false;
if (!generateBody()) if (!generateBody())
@ -4951,6 +4958,7 @@ CodeGenerator::link()
graph.mir().numCallTargets()); graph.mir().numCallTargets());
ionScript->setMethod(code); ionScript->setMethod(code);
ionScript->setSkipArgCheckEntryOffset(getSkipArgCheckEntryOffset());
SetIonScript(script, executionMode, ionScript); SetIonScript(script, executionMode, ionScript);

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

@ -556,6 +556,7 @@ IonScript::IonScript()
deoptTable_(NULL), deoptTable_(NULL),
osrPc_(NULL), osrPc_(NULL),
osrEntryOffset_(0), osrEntryOffset_(0),
skipArgCheckEntryOffset_(0),
invalidateEpilogueOffset_(0), invalidateEpilogueOffset_(0),
invalidateEpilogueDataOffset_(0), invalidateEpilogueDataOffset_(0),
numBailouts_(0), numBailouts_(0),

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

@ -434,9 +434,8 @@ IonBuilder::build()
if (!current) if (!current)
return false; return false;
IonSpew(IonSpew_Scripts, "Analyzing script %s:%d (%p) (usecount=%d) (maxloopcount=%d)", IonSpew(IonSpew_Scripts, "Analyzing script %s:%d (%p) (usecount=%d)",
script()->filename(), script()->lineno, (void *)script(), (int)script()->getUseCount(), script()->filename(), script()->lineno, (void *)script(), (int)script()->getUseCount());
(int)script()->getMaxLoopCount());
if (!graph().addScript(script())) if (!graph().addScript(script()))
return false; return false;
@ -4750,6 +4749,49 @@ TestAreKnownDOMTypes(JSContext *cx, types::TypeSet *inTypes)
return false; 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 * MCall *
IonBuilder::makeCallHelper(HandleFunction target, CallInfo &callInfo, bool cloneAtCallsite) IonBuilder::makeCallHelper(HandleFunction target, CallInfo &callInfo, bool cloneAtCallsite)
{ {
@ -4819,6 +4861,7 @@ IonBuilder::makeCallHelper(HandleFunction target, CallInfo &callInfo, bool clone
current->add(newThis); current->add(newThis);
thisArg = newThis; thisArg = newThis;
callInfo.setThis(newThis);
} }
// Pass |this| and function. // 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()); call->initFunction(callInfo.fun());
current->add(call); current->add(call);

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

@ -46,8 +46,8 @@ class IonCode : public gc::Cell
uint32_t jumpRelocTableBytes_; // Size of the jump relocation table. uint32_t jumpRelocTableBytes_; // Size of the jump relocation table.
uint32_t dataRelocTableBytes_; // Size of the data relocation table. uint32_t dataRelocTableBytes_; // Size of the data relocation table.
uint32_t preBarrierTableBytes_; // Size of the prebarrier table. uint32_t preBarrierTableBytes_; // Size of the prebarrier table.
JSBool invalidated_; // Whether the code object has been invalidated. JSBool invalidated_; // Whether the code object has been invalidated.
// This is necessary to prevent GC tracing. // This is necessary to prevent GC tracing.
#if JS_BITS_PER_WORD == 32 #if JS_BITS_PER_WORD == 32
// Ensure IonCode is gc::Cell aligned. // Ensure IonCode is gc::Cell aligned.
@ -160,6 +160,9 @@ struct IonScript
// Offset to OSR entrypoint from method_->raw(), or 0. // Offset to OSR entrypoint from method_->raw(), or 0.
uint32_t osrEntryOffset_; 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 // Offset of the invalidation epilogue (which pushes this IonScript
// and calls the invalidation thunk). // and calls the invalidation thunk).
uint32_t invalidateEpilogueOffset_; uint32_t invalidateEpilogueOffset_;
@ -302,6 +305,9 @@ struct IonScript
static inline size_t offsetOfOsrEntryOffset() { static inline size_t offsetOfOsrEntryOffset() {
return offsetof(IonScript, osrEntryOffset_); return offsetof(IonScript, osrEntryOffset_);
} }
static inline size_t offsetOfSkipArgCheckEntryOffset() {
return offsetof(IonScript, skipArgCheckEntryOffset_);
}
public: public:
IonCode *method() const { IonCode *method() const {
@ -327,6 +333,13 @@ struct IonScript
uint32_t osrEntryOffset() const { uint32_t osrEntryOffset() const {
return osrEntryOffset_; return osrEntryOffset_;
} }
void setSkipArgCheckEntryOffset(uint32_t offset) {
JS_ASSERT(!skipArgCheckEntryOffset_);
skipArgCheckEntryOffset_ = offset;
}
uint32_t getSkipArgCheckEntryOffset() const {
return skipArgCheckEntryOffset_;
}
bool containsCodeAddress(uint8_t *addr) const { bool containsCodeAddress(uint8_t *addr) const {
return method()->raw() <= addr && addr <= method()->raw() + method()->instructionsSize(); 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 void
MacroAssembler::loadBaselineFramePtr(Register framePtr, Register dest) 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 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); void loadBaselineFramePtr(Register framePtr, Register dest);

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

@ -1365,11 +1365,14 @@ class MCall
// Original value of argc from the bytecode. // Original value of argc from the bytecode.
uint32_t numActualArgs_; uint32_t numActualArgs_;
bool needsArgCheck_;
MCall(JSFunction *target, uint32_t numActualArgs, bool construct) MCall(JSFunction *target, uint32_t numActualArgs, bool construct)
: construct_(construct), : construct_(construct),
target_(target), target_(target),
targetScript_(NULL), targetScript_(NULL),
numActualArgs_(numActualArgs) numActualArgs_(numActualArgs),
needsArgCheck_(true)
{ {
setResultType(MIRType_Value); setResultType(MIRType_Value);
} }
@ -1387,6 +1390,14 @@ class MCall
return setOperand(FunctionOperandIndex, func); return setOperand(FunctionOperandIndex, func);
} }
bool needsArgCheck() const {
return needsArgCheck_;
}
void disableArgCheck() {
needsArgCheck_ = false;
}
MPrepareCall *getPrepareCall() { MPrepareCall *getPrepareCall() {
return getOperand(PrepareCallOperandIndex)->toPrepareCall(); return getOperand(PrepareCallOperandIndex)->toPrepareCall();
} }

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

@ -48,6 +48,7 @@ CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph, Mac
lastOsiPointOffset_(0), lastOsiPointOffset_(0),
sps_(&gen->compartment->rt->spsProfiler, &lastPC_), sps_(&gen->compartment->rt->spsProfiler, &lastPC_),
osrEntryOffset_(0), osrEntryOffset_(0),
skipArgCheckEntryOffset_(0),
frameDepth_(graph->localSlotCount() * sizeof(STACK_SLOT_SIZE) + frameDepth_(graph->localSlotCount() * sizeof(STACK_SLOT_SIZE) +
graph->argumentSlotCount() * sizeof(Value)) graph->argumentSlotCount() * sizeof(Value))
{ {

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

@ -91,6 +91,18 @@ class CodeGeneratorShared : public LInstructionVisitor
return osrEntryOffset_; 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; typedef js::Vector<SafepointIndex, 8, SystemAllocPolicy> SafepointIndices;
bool markArgumentSlots(LSafepoint *safepoint); bool markArgumentSlots(LSafepoint *safepoint);

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

@ -1146,11 +1146,6 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode, bool
RootedScript script(cx); RootedScript script(cx);
SET_SCRIPT(regs.fp()->script()); 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 #if JS_TRACE_LOGGING
AutoTraceLog logger(TraceLogging::defaultLogger(), AutoTraceLog logger(TraceLogging::defaultLogger(),
TraceLogging::INTERPRETER_START, TraceLogging::INTERPRETER_START,
@ -1383,10 +1378,6 @@ ADD_EMPTY_CASE(JSOP_TRY)
END_EMPTY_CASES END_EMPTY_CASES
BEGIN_CASE(JSOP_LOOPHEAD) BEGIN_CASE(JSOP_LOOPHEAD)
#ifdef JS_METHODJIT
script->incrLoopCount();
#endif
END_CASE(JSOP_LOOPHEAD) END_CASE(JSOP_LOOPHEAD)
BEGIN_CASE(JSOP_LABEL) BEGIN_CASE(JSOP_LABEL)
@ -2424,9 +2415,6 @@ BEGIN_CASE(JSOP_FUNCALL)
regs.fp()->setUseNewType(); regs.fp()->setUseNewType();
SET_SCRIPT(regs.fp()->script()); SET_SCRIPT(regs.fp()->script());
#ifdef JS_METHODJIT
script->resetLoopCount();
#endif
#ifdef JS_ION #ifdef JS_ION
if (!newType && ion::IsEnabled(cx)) { if (!newType && ion::IsEnabled(cx)) {

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

@ -1916,11 +1916,7 @@ JSScript::isShortRunning()
{ {
return length < 100 && return length < 100 &&
hasAnalysis() && hasAnalysis() &&
!analysis()->hasFunctionCalls() !analysis()->hasFunctionCalls();
#ifdef JS_METHODJIT
&& getMaxLoopCount() < 40
#endif
;
} }
bool bool
@ -2883,11 +2879,15 @@ void
JSScript::updateBaselineOrIonRaw() JSScript::updateBaselineOrIonRaw()
{ {
#ifdef JS_ION #ifdef JS_ION
if (hasIonScript()) if (hasIonScript()) {
baselineOrIonRaw = ion->method()->raw(); baselineOrIonRaw = ion->method()->raw();
else if (hasBaselineScript()) baselineOrIonSkipArgCheck = ion->method()->raw() + ion->getSkipArgCheckEntryOffset();
} else if (hasBaselineScript()) {
baselineOrIonRaw = baseline->method()->raw(); baselineOrIonRaw = baseline->method()->raw();
else baselineOrIonSkipArgCheck = baseline->method()->raw();
} else {
baselineOrIonRaw = NULL; baselineOrIonRaw = NULL;
baselineOrIonSkipArgCheck = NULL;
}
#endif #endif
} }

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

@ -412,10 +412,7 @@ class JSScript : public js::gc::Cell
uint32_t useCount; /* Number of times the script has been called uint32_t useCount; /* Number of times the script has been called
* or has had backedges taken. Reset if the * or has had backedges taken. Reset if the
* script's JIT code is forcibly discarded. */ * script's JIT code is forcibly discarded. */
uint32_t PADDING32;
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. */
#ifdef DEBUG #ifdef DEBUG
// Unique identifier within the compartment for this script, used for // Unique identifier within the compartment for this script, used for
@ -429,7 +426,6 @@ class JSScript : public js::gc::Cell
private: private:
uint16_t PADDING16; uint16_t PADDING16;
uint16_t version; /* JS version under which script was compiled */ uint16_t version; /* JS version under which script was compiled */
public: public:
@ -603,6 +599,7 @@ class JSScript : public js::gc::Cell
* if there's no Baseline or Ion script. * if there's no Baseline or Ion script.
*/ */
uint8_t *baselineOrIonRaw; uint8_t *baselineOrIonRaw;
uint8_t *baselineOrIonSkipArgCheck;
public: public:
bool hasIonScript() const { bool hasIonScript() const {
@ -683,6 +680,9 @@ class JSScript : public js::gc::Cell
static size_t offsetOfBaselineOrIonRaw() { static size_t offsetOfBaselineOrIonRaw() {
return offsetof(JSScript, baselineOrIonRaw); return offsetof(JSScript, baselineOrIonRaw);
} }
static size_t offsetOfBaselineOrIonSkipArgCheck() {
return offsetof(JSScript, baselineOrIonSkipArgCheck);
}
/* /*
* Original compiled function for the script, if it has a function. * 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); } static size_t offsetOfUseCount() { return offsetof(JSScript, useCount); }
void resetUseCount() { useCount = 0; } 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 of the JITScript and all sections. If |mallocSizeOf| is NULL, the
* size is computed analytically. (This method is implemented in * size is computed analytically. (This method is implemented in