Bug 980263 - Part 1: Disable Ion OSR for loops nested in expressions r=jandem

This commit is contained in:
Andy Wingo 2014-03-07 08:44:38 +01:00
parent 8933900a95
commit 5024e7e61c
10 changed files with 116 additions and 37 deletions

View File

@ -82,6 +82,27 @@ struct frontend::StmtInfoBCE : public StmtInfoBase
}
};
namespace {
struct LoopStmtInfo : public StmtInfoBCE
{
int32_t stackDepth; // Stack depth when this loop was pushed.
uint32_t loopDepth; // Loop depth.
// Can we OSR into Ion from here? True unless there is non-loop state on the stack.
bool canIonOsr;
LoopStmtInfo(ExclusiveContext *cx) : StmtInfoBCE(cx) {}
static LoopStmtInfo* fromStmtInfo(StmtInfoBCE *stmt) {
JS_ASSERT(stmt->isLoop());
return static_cast<LoopStmtInfo*>(stmt);
}
};
} // anonymous namespace
BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent,
Parser<FullParseHandler> *parser, SharedContext *sc,
HandleScript script, bool insideEval, HandleScript evalCaller,
@ -440,23 +461,11 @@ EmitLoopEntry(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
return false;
}
/*
* Calculate loop depth. Note that this value is just a hint, so
* give up for deeply nested loops.
*/
uint32_t loopDepth = 0;
StmtInfoBCE *stmt = bce->topStmt;
while (stmt) {
if (stmt->isLoop()) {
loopDepth++;
if (loopDepth >= 5)
break;
}
stmt = stmt->down;
}
LoopStmtInfo *loop = LoopStmtInfo::fromStmtInfo(bce->topStmt);
JS_ASSERT(loop->loopDepth > 0);
JS_ASSERT(loopDepth > 0);
return Emit2(cx, bce, JSOP_LOOPENTRY, uint8_t(loopDepth)) >= 0;
uint8_t loopDepthAndFlags = PackLoopEntryDepthHintAndFlags(loop->loopDepth, loop->canIonOsr);
return Emit2(cx, bce, JSOP_LOOPENTRY, loopDepthAndFlags) >= 0;
}
/*
@ -661,12 +670,51 @@ BackPatch(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t last, jsbytecode
((stmt)->update = (top), (stmt)->breaks = (stmt)->continues = (-1))
static void
PushStatementBCE(BytecodeEmitter *bce, StmtInfoBCE *stmt, StmtType type, ptrdiff_t top)
PushStatementInner(BytecodeEmitter *bce, StmtInfoBCE *stmt, StmtType type, ptrdiff_t top)
{
SET_STATEMENT_TOP(stmt, top);
PushStatement(bce, stmt, type);
}
static void
PushStatementBCE(BytecodeEmitter *bce, StmtInfoBCE *stmt, StmtType type, ptrdiff_t top)
{
PushStatementInner(bce, stmt, type, top);
JS_ASSERT(!stmt->isLoop());
}
static void
PushLoopStatement(BytecodeEmitter *bce, LoopStmtInfo *stmt, StmtType type, ptrdiff_t top)
{
PushStatementInner(bce, stmt, type, top);
JS_ASSERT(stmt->isLoop());
LoopStmtInfo *downLoop = nullptr;
for (StmtInfoBCE *outer = stmt->down; outer; outer = outer->down) {
if (outer->isLoop()) {
downLoop = LoopStmtInfo::fromStmtInfo(outer);
break;
}
}
stmt->stackDepth = bce->stackDepth;
stmt->loopDepth = downLoop ? downLoop->loopDepth + 1 : 1;
int loopSlots;
if (type == STMT_FOR_OF_LOOP)
loopSlots = 2;
else if (type == STMT_FOR_IN_LOOP)
loopSlots = 1;
else
loopSlots = 0;
if (downLoop)
stmt->canIonOsr = (downLoop->canIonOsr &&
stmt->stackDepth == downLoop->stackDepth + loopSlots);
else
stmt->canIonOsr = stmt->stackDepth == loopSlots;
}
/*
* Return the enclosing lexical scope, which is the innermost enclosing static
* block object or compiler created function.
@ -4409,8 +4457,8 @@ EmitForOf(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
return false;
}
StmtInfoBCE stmtInfo(cx);
PushStatementBCE(bce, &stmtInfo, STMT_FOR_OF_LOOP, top);
LoopStmtInfo stmtInfo(cx);
PushLoopStatement(bce, &stmtInfo, STMT_FOR_OF_LOOP, top);
// Jump down to the loop condition to minimize overhead assuming at least
// one iteration, as the other loop forms do. Annotate so IonMonkey can
@ -4549,8 +4597,8 @@ EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
return false;
}
StmtInfoBCE stmtInfo(cx);
PushStatementBCE(bce, &stmtInfo, STMT_FOR_IN_LOOP, top);
LoopStmtInfo stmtInfo(cx);
PushLoopStatement(bce, &stmtInfo, STMT_FOR_IN_LOOP, top);
/* Annotate so IonMonkey can find the loop-closing jump. */
int noteIndex = NewSrcNote(cx, bce, SRC_FOR_IN);
@ -4635,8 +4683,8 @@ EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
static bool
EmitNormalFor(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
{
StmtInfoBCE stmtInfo(cx);
PushStatementBCE(bce, &stmtInfo, STMT_FOR_LOOP, top);
LoopStmtInfo stmtInfo(cx);
PushLoopStatement(bce, &stmtInfo, STMT_FOR_LOOP, top);
ParseNode *forHead = pn->pn_left;
ParseNode *forBody = pn->pn_right;
@ -4952,8 +5000,8 @@ EmitDo(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
if (top < 0)
return false;
StmtInfoBCE stmtInfo(cx);
PushStatementBCE(bce, &stmtInfo, STMT_DO_LOOP, top);
LoopStmtInfo stmtInfo(cx);
PushLoopStatement(bce, &stmtInfo, STMT_DO_LOOP, top);
if (!EmitLoopEntry(cx, bce, nullptr))
return false;
@ -5010,8 +5058,8 @@ EmitWhile(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
* . . .
* N N*(ifeq-fail; goto); ifeq-pass goto; N*ifne-pass; ifne-fail
*/
StmtInfoBCE stmtInfo(cx);
PushStatementBCE(bce, &stmtInfo, STMT_WHILE_LOOP, top);
LoopStmtInfo stmtInfo(cx);
PushLoopStatement(bce, &stmtInfo, STMT_WHILE_LOOP, top);
ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_WHILE);
if (noteIndex < 0)

View File

@ -612,7 +612,7 @@ BaselineCompiler::emitInterruptCheck()
}
bool
BaselineCompiler::emitUseCountIncrement()
BaselineCompiler::emitUseCountIncrement(bool allowOsr)
{
// Emit no use count increments or bailouts if Ion is not
// enabled, or if the script will never be Ion-compileable
@ -636,6 +636,12 @@ BaselineCompiler::emitUseCountIncrement()
return true;
}
// OSR not possible at this loop entry.
if (!allowOsr) {
JS_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY);
return true;
}
Label skipCall;
const OptimizationInfo *info = js_IonOptimizations.get(js_IonOptimizations.firstLevel());
@ -1064,7 +1070,7 @@ bool
BaselineCompiler::emit_JSOP_LOOPENTRY()
{
frame.syncStack(0);
return emitUseCountIncrement();
return emitUseCountIncrement(LoopEntryCanIonOsr(pc));
}
bool

View File

@ -215,7 +215,7 @@ class BaselineCompiler : public BaselineCompilerSpecific
bool emitStackCheck(bool earlyCheck=false);
bool emitInterruptCheck();
bool emitUseCountIncrement();
bool emitUseCountIncrement(bool allowOsr=true);
bool emitArgumentTypeChecks();
bool emitDebugPrologue();
bool emitDebugTrap();

View File

@ -765,6 +765,7 @@ EnsureCanEnterIon(JSContext *cx, ICUseCount_Fallback *stub, BaselineFrame *frame
bool isConstructing = IsTopFrameConstructing(cx);
MethodStatus stat;
if (isLoopEntry) {
JS_ASSERT(LoopEntryCanIonOsr(pc));
IonSpew(IonSpew_BaselineOSR, " Compile at loop entry!");
stat = CanEnterAtBranch(cx, script, frame, pc, isConstructing);
} else if (frame->isFunctionFrame()) {
@ -908,6 +909,8 @@ DoUseCountFallback(JSContext *cx, ICUseCount_Fallback *stub, BaselineFrame *fram
jsbytecode *pc = stub->icEntry()->pc(script);
bool isLoopEntry = JSOp(*pc) == JSOP_LOOPENTRY;
JS_ASSERT(!isLoopEntry || LoopEntryCanIonOsr(pc));
FallbackICSpew(cx, stub, "UseCount(%d)", isLoopEntry ? int(script->pcToOffset(pc)) : int(-1));
if (!script->canIonCompile()) {

View File

@ -1901,7 +1901,7 @@ Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode
{
JS_ASSERT(jit::IsIonEnabled(cx));
JS_ASSERT(jit::IsBaselineEnabled(cx));
JS_ASSERT_IF(osrPc != nullptr, (JSOp)*osrPc == JSOP_LOOPENTRY);
JS_ASSERT_IF(osrPc != nullptr, LoopEntryCanIonOsr(osrPc));
JS_ASSERT_IF(executionMode == ParallelExecution, !osrFrame && !osrPc);
JS_ASSERT_IF(executionMode == ParallelExecution, !HasIonScript(script, executionMode));
@ -2000,6 +2000,7 @@ jit::CanEnterAtBranch(JSContext *cx, JSScript *script, BaselineFrame *osrFrame,
{
JS_ASSERT(jit::IsIonEnabled(cx));
JS_ASSERT((JSOp)*pc == JSOP_LOOPENTRY);
JS_ASSERT(LoopEntryCanIonOsr(pc));
// Skip if the script has been disabled.
if (!script->canIonCompile())

View File

@ -5629,7 +5629,7 @@ IonBuilder::newBlock(MBasicBlock *predecessor, jsbytecode *pc, uint32_t loopDept
MBasicBlock *
IonBuilder::newOsrPreheader(MBasicBlock *predecessor, jsbytecode *loopEntry)
{
JS_ASSERT((JSOp)*loopEntry == JSOP_LOOPENTRY);
JS_ASSERT(LoopEntryCanIonOsr(loopEntry));
JS_ASSERT(loopEntry == info().osrPc());
// Create two blocks: one for the OSR entry with no predecessors, one for

View File

@ -86,7 +86,7 @@ OptimizationInfo::usesBeforeCompile(JSScript *script, jsbytecode *pc) const
// It's more efficient to enter outer loops, rather than inner loops, via OSR.
// To accomplish this, we use a slightly higher threshold for inner loops.
// Note that the loop depth is always > 0 so we will prefer non-OSR over OSR.
uint32_t loopDepth = GET_UINT8(pc);
uint32_t loopDepth = LoopEntryDepthHint(pc);
JS_ASSERT(loopDepth > 0);
return minUses + loopDepth * 100;
}

View File

@ -208,6 +208,24 @@ SET_UINT32_INDEX(jsbytecode *pc, uint32_t index)
#define LOCALNO_BITS 24
#define LOCALNO_LIMIT (1 << LOCALNO_BITS)
static inline unsigned
LoopEntryDepthHint(jsbytecode *pc)
{
JS_ASSERT(*pc == JSOP_LOOPENTRY);
return GET_UINT8(pc) & 0x7f;
}
static inline bool
LoopEntryCanIonOsr(jsbytecode *pc)
{
JS_ASSERT(*pc == JSOP_LOOPENTRY);
return GET_UINT8(pc) & 0x80;
}
static inline uint8_t
PackLoopEntryDepthHintAndFlags(unsigned loopDepth, bool canIonOsr)
{
return (loopDepth < 0x80 ? uint8_t(loopDepth) : 0x7f) | (canIonOsr ? 0x80 : 0);
}
/*
* Describes the 'hops' component of a JOF_SCOPECOORD opcode.
*

View File

@ -477,9 +477,12 @@
macro(JSOP_IMPLICITTHIS, 226, "implicitthis", "", 5, 0, 1, JOF_ATOM) \
\
/*
* This opcode is the target of the entry jump for some loop. The uint8 argument
* is the loop depth. This value starts at 1 and is just a hint: deeply
* nested loops all have the same value.
* This opcode is the target of the entry jump for some loop. The uint8
* argument is a bitfield. The lower 7 bits of the argument indicate the
* loop depth. This value starts at 1 and is just a hint: deeply nested
* loops all have the same value. The upper bit is set if Ion should be
* able to OSR at this point, which is true unless there is non-loop state
* on the stack.
*/ \
macro(JSOP_LOOPENTRY, 227, "loopentry", NULL, 2, 0, 0, JOF_UINT8)

View File

@ -23,7 +23,7 @@ namespace js {
* and saved versions. If deserialization fails, the data should be
* invalidated if possible.
*/
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 168);
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 169);
class XDRBuffer {
public: