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);
// 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