mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 980263 - Part 1: Disable Ion OSR for loops nested in expressions r=jandem
This commit is contained in:
parent
8933900a95
commit
5024e7e61c
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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()) {
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user