mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 487845 - TM: After deep-bailing, we can lirbuf->rewind() and then return to a dead code page. r=gal.
This commit is contained in:
parent
225dc1322f
commit
0616f91d10
@ -131,20 +131,14 @@ struct JSTraceMonitor {
|
||||
* !onTrace && !recorder: not on trace.
|
||||
* onTrace && recorder: recording a trace.
|
||||
* onTrace && !recorder: executing a trace.
|
||||
* !onTrace && recorder && !prohibitRecording:
|
||||
* !onTrace && recorder && !prohibitFlush:
|
||||
* not on trace; deep-aborted while recording.
|
||||
* !onTrace && recorder && prohibitRecording:
|
||||
* !onTrace && recorder && prohibitFlush:
|
||||
* not on trace; deep-bailed in SpiderMonkey code called from a
|
||||
* trace. JITted code is on the stack.
|
||||
*/
|
||||
JSPackedBool onTrace;
|
||||
|
||||
/*
|
||||
* Do not start recording after a deep bail. That would free JITted code
|
||||
* pages that we will later return to.
|
||||
*/
|
||||
JSPackedBool prohibitRecording;
|
||||
|
||||
/* See reservedObjects below. */
|
||||
JSPackedBool useReservedObjects;
|
||||
|
||||
@ -156,7 +150,15 @@ struct JSTraceMonitor {
|
||||
|
||||
struct GlobalState globalStates[MONITOR_N_GLOBAL_STATES];
|
||||
struct VMFragment* vmfragments[FRAGMENT_TABLE_SIZE];
|
||||
JSBool needFlush;
|
||||
|
||||
|
||||
/*
|
||||
* If nonzero, do not flush the JIT cache after a deep bail. That would
|
||||
* free JITted code pages that we will later return to. Instead, set
|
||||
* the needFlush flag so that it can be flushed later.
|
||||
*/
|
||||
uintN prohibitFlush;
|
||||
JSBool needFlush;
|
||||
|
||||
/*
|
||||
* reservedObjects is a linked list (via fslots[0]) of preallocated JSObjects.
|
||||
|
@ -2555,6 +2555,7 @@ checktype_fail_2:
|
||||
JS_REQUIRES_STACK void
|
||||
TraceRecorder::compile(JSTraceMonitor* tm)
|
||||
{
|
||||
JS_ASSERT(!tm->needFlush);
|
||||
Fragmento* fragmento = tm->fragmento;
|
||||
if (treeInfo->maxNativeStackSlots >= MAX_NATIVE_STACK_SLOTS) {
|
||||
debug_only_v(printf("Blacklist: excessive stack use.\n"));
|
||||
@ -2631,6 +2632,8 @@ TraceRecorder::closeLoop(JSTraceMonitor* tm, bool& demote)
|
||||
*/
|
||||
JS_ASSERT((*cx->fp->regs->pc == JSOP_LOOP || *cx->fp->regs->pc == JSOP_NOP) && !cx->fp->imacpc);
|
||||
|
||||
JS_ASSERT(!tm->needFlush);
|
||||
|
||||
bool stable;
|
||||
LIns* exitIns;
|
||||
Fragment* peer;
|
||||
@ -3101,6 +3104,52 @@ nanojit::Fragment::onDestroy()
|
||||
delete (TreeInfo *)vmprivate;
|
||||
}
|
||||
|
||||
static JS_REQUIRES_STACK void
|
||||
FlushJITCache(JSContext* cx)
|
||||
{
|
||||
if (!TRACING_ENABLED(cx))
|
||||
return;
|
||||
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
|
||||
debug_only_v(printf("Flushing cache.\n");)
|
||||
if (tm->recorder)
|
||||
js_AbortRecording(cx, "flush cache");
|
||||
TraceRecorder* tr;
|
||||
while ((tr = tm->abortStack) != NULL) {
|
||||
tr->removeFragmentoReferences();
|
||||
tr->deepAbort();
|
||||
tr->popAbortStack();
|
||||
}
|
||||
Fragmento* fragmento = tm->fragmento;
|
||||
if (fragmento) {
|
||||
if (tm->prohibitFlush) {
|
||||
debug_only_v(printf("Deferring fragmento flush due to deep bail.\n");)
|
||||
tm->needFlush = JS_TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
fragmento->clearFrags();
|
||||
#ifdef DEBUG
|
||||
JS_ASSERT(fragmento->labels);
|
||||
fragmento->labels->clear();
|
||||
#endif
|
||||
tm->lirbuf->rewind();
|
||||
for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) {
|
||||
VMFragment* f = tm->vmfragments[i];
|
||||
while (f) {
|
||||
VMFragment* next = f->next;
|
||||
fragmento->clearFragment(f);
|
||||
f = next;
|
||||
}
|
||||
tm->vmfragments[i] = NULL;
|
||||
}
|
||||
for (size_t i = 0; i < MONITOR_N_GLOBAL_STATES; ++i) {
|
||||
tm->globalStates[i].globalShape = -1;
|
||||
tm->globalStates[i].globalSlots->clear();
|
||||
}
|
||||
}
|
||||
tm->needFlush = JS_FALSE;
|
||||
}
|
||||
|
||||
static JS_REQUIRES_STACK bool
|
||||
js_DeleteRecorder(JSContext* cx)
|
||||
{
|
||||
@ -3115,7 +3164,7 @@ js_DeleteRecorder(JSContext* cx)
|
||||
*/
|
||||
if (JS_TRACE_MONITOR(cx).fragmento->assm()->error() == OutOMem
|
||||
|| js_OverfullFragmento(tm->fragmento, MAX_MEM_IN_MAIN_FRAGMENTO)) {
|
||||
js_FlushJITCache(cx);
|
||||
FlushJITCache(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3129,10 +3178,8 @@ static inline bool
|
||||
js_CheckGlobalObjectShape(JSContext* cx, JSTraceMonitor* tm, JSObject* globalObj,
|
||||
uint32 *shape=NULL, SlotList** slots=NULL)
|
||||
{
|
||||
if (tm->needFlush) {
|
||||
tm->needFlush = JS_FALSE;
|
||||
if (tm->needFlush)
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 globalShape = OBJ_SHAPE(globalObj);
|
||||
|
||||
@ -3189,7 +3236,7 @@ js_StartRecorder(JSContext* cx, VMSideExit* anchor, Fragment* f, TreeInfo* ti,
|
||||
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
|
||||
JS_ASSERT(f->root != f || !cx->fp->imacpc);
|
||||
|
||||
if (JS_TRACE_MONITOR(cx).prohibitRecording)
|
||||
if (JS_TRACE_MONITOR(cx).needFlush)
|
||||
return false;
|
||||
|
||||
/* start recording if no exception during construction */
|
||||
@ -3416,7 +3463,7 @@ js_RecordTree(JSContext* cx, JSTraceMonitor* tm, Fragment* f, jsbytecode* outer,
|
||||
|
||||
/* Make sure the global type map didn't change on us. */
|
||||
if (!js_CheckGlobalObjectShape(cx, tm, globalObj)) {
|
||||
js_FlushJITCache(cx);
|
||||
FlushJITCache(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3429,7 +3476,7 @@ js_RecordTree(JSContext* cx, JSTraceMonitor* tm, Fragment* f, jsbytecode* outer,
|
||||
f = getAnchor(&JS_TRACE_MONITOR(cx), f->root->ip, globalObj, globalShape);
|
||||
|
||||
if (!f) {
|
||||
js_FlushJITCache(cx);
|
||||
FlushJITCache(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3438,7 +3485,7 @@ js_RecordTree(JSContext* cx, JSTraceMonitor* tm, Fragment* f, jsbytecode* outer,
|
||||
|
||||
if (f->lirbuf->outOMem() ||
|
||||
js_OverfullFragmento(tm->fragmento, MAX_MEM_IN_MAIN_FRAGMENTO)) {
|
||||
js_FlushJITCache(cx);
|
||||
FlushJITCache(cx);
|
||||
debug_only_v(printf("Out of memory recording new tree, flushing cache.\n");)
|
||||
return false;
|
||||
}
|
||||
@ -3452,9 +3499,12 @@ js_RecordTree(JSContext* cx, JSTraceMonitor* tm, Fragment* f, jsbytecode* outer,
|
||||
ti->typeMap.captureTypes(cx, *globalSlots, 0/*callDepth*/);
|
||||
ti->nStackTypes = ti->typeMap.length() - globalSlots->length();
|
||||
|
||||
/* Check for duplicate entry type maps. This is always wrong and hints at trace explosion
|
||||
since we are trying to stabilize something without properly connecting peer edges. */
|
||||
#ifdef DEBUG
|
||||
#ifdef DEBUG
|
||||
/*
|
||||
* Check for duplicate entry type maps. This is always wrong and hints at
|
||||
* trace explosion since we are trying to stabilize something without
|
||||
* properly connecting peer edges.
|
||||
*/
|
||||
TreeInfo* ti_other;
|
||||
for (Fragment* peer = getLoop(tm, f->root->ip, globalObj, globalShape); peer != NULL;
|
||||
peer = peer->peer) {
|
||||
@ -3467,7 +3517,7 @@ js_RecordTree(JSContext* cx, JSTraceMonitor* tm, Fragment* f, jsbytecode* outer,
|
||||
ti->treeFileName = cx->fp->script->filename;
|
||||
ti->treeLineNumber = js_FramePCToLineNumber(cx, cx->fp);
|
||||
ti->treePCOffset = FramePCOffset(cx->fp);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* determine the native frame layout at the entry point */
|
||||
unsigned entryNativeStackSlots = ti->nStackTypes;
|
||||
@ -3503,6 +3553,7 @@ JS_REQUIRES_STACK static bool
|
||||
js_AttemptToStabilizeTree(JSContext* cx, VMSideExit* exit, jsbytecode* outer)
|
||||
{
|
||||
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
|
||||
JS_ASSERT(!tm->needFlush);
|
||||
VMFragment* from = (VMFragment*)exit->from->root;
|
||||
TreeInfo* from_ti = (TreeInfo*)from->vmprivate;
|
||||
|
||||
@ -3601,6 +3652,8 @@ js_AttemptToStabilizeTree(JSContext* cx, VMSideExit* exit, jsbytecode* outer)
|
||||
static JS_REQUIRES_STACK bool
|
||||
js_AttemptToExtendTree(JSContext* cx, VMSideExit* anchor, VMSideExit* exitedFrom, jsbytecode* outer)
|
||||
{
|
||||
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
|
||||
JS_ASSERT(!tm->needFlush);
|
||||
Fragment* f = anchor->from->root;
|
||||
JS_ASSERT(f->vmprivate);
|
||||
TreeInfo* ti = (TreeInfo*)f->vmprivate;
|
||||
@ -3746,7 +3799,7 @@ js_RecordLoopEdge(JSContext* cx, TraceRecorder* r, uintN& inlineCallCount)
|
||||
if (!f) {
|
||||
f = getAnchor(tm, cx->fp->regs->pc, globalObj, globalShape);
|
||||
if (!f) {
|
||||
js_FlushJITCache(cx);
|
||||
FlushJITCache(cx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -4073,6 +4126,10 @@ js_ExecuteTree(JSContext* cx, Fragment* f, uintN& inlineCallCount,
|
||||
|
||||
AUDIT(traceTriggered);
|
||||
|
||||
#ifdef DEBUG
|
||||
cx->interpState = NULL;
|
||||
#endif
|
||||
|
||||
JS_ASSERT(lr->exitType != LOOP_EXIT || !lr->calldepth);
|
||||
tm->onTrace = false;
|
||||
LeaveTree(*state, lr);
|
||||
@ -4166,7 +4223,9 @@ LeaveTree(InterpState& state, VMSideExit* lr)
|
||||
typeMap[innermost->numStackSlots - 1],
|
||||
(jsdouble *) state.sp + innermost->sp_adj / sizeof(jsdouble) - 1);
|
||||
}
|
||||
JS_TRACE_MONITOR(cx).prohibitRecording = false;
|
||||
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
|
||||
if (tm->prohibitFlush && --tm->prohibitFlush == 0 && tm->needFlush)
|
||||
FlushJITCache(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -4342,7 +4401,7 @@ js_MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount)
|
||||
SlotList* globalSlots = NULL;
|
||||
|
||||
if (!js_CheckGlobalObjectShape(cx, tm, globalObj, &globalShape, &globalSlots))
|
||||
js_FlushJITCache(cx);
|
||||
FlushJITCache(cx);
|
||||
|
||||
/* Do not enter the JIT code with a pending operation callback. */
|
||||
if (cx->operationCallbackFlag)
|
||||
@ -4355,7 +4414,7 @@ js_MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount)
|
||||
f = getAnchor(tm, pc, globalObj, globalShape);
|
||||
|
||||
if (!f) {
|
||||
js_FlushJITCache(cx);
|
||||
FlushJITCache(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -4470,7 +4529,7 @@ TraceRecorder::monitorRecording(JSContext* cx, TraceRecorder* tr, JSOp op)
|
||||
js_OverfullFragmento(JS_TRACE_MONITOR(cx).fragmento,
|
||||
MAX_MEM_IN_MAIN_FRAGMENTO)) {
|
||||
js_AbortRecording(cx, "no more LIR memory");
|
||||
js_FlushJITCache(cx);
|
||||
FlushJITCache(cx);
|
||||
return JSMRS_STOP;
|
||||
}
|
||||
|
||||
@ -4898,45 +4957,6 @@ js_OverfullFragmento(Fragmento *frago, size_t maxsz)
|
||||
return (frago->_stats.pages > (maxsz >> NJ_LOG2_PAGE_SIZE));
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK void
|
||||
js_FlushJITCache(JSContext* cx)
|
||||
{
|
||||
if (!TRACING_ENABLED(cx))
|
||||
return;
|
||||
debug_only_v(printf("Flushing cache.\n");)
|
||||
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
|
||||
if (tm->recorder)
|
||||
js_AbortRecording(cx, "flush cache");
|
||||
TraceRecorder* tr;
|
||||
while ((tr = tm->abortStack) != NULL) {
|
||||
tr->removeFragmentoReferences();
|
||||
tr->deepAbort();
|
||||
tr->popAbortStack();
|
||||
}
|
||||
Fragmento* fragmento = tm->fragmento;
|
||||
if (fragmento) {
|
||||
fragmento->clearFrags();
|
||||
#ifdef DEBUG
|
||||
JS_ASSERT(fragmento->labels);
|
||||
fragmento->labels->clear();
|
||||
#endif
|
||||
tm->lirbuf->rewind();
|
||||
for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) {
|
||||
VMFragment* f = tm->vmfragments[i];
|
||||
while(f) {
|
||||
VMFragment* next = f->next;
|
||||
fragmento->clearFragment(f);
|
||||
f = next;
|
||||
}
|
||||
tm->vmfragments[i] = NULL;
|
||||
}
|
||||
for (size_t i = 0; i < MONITOR_N_GLOBAL_STATES; ++i) {
|
||||
tm->globalStates[i].globalShape = -1;
|
||||
tm->globalStates[i].globalSlots->clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JS_FORCES_STACK JS_FRIEND_API(void)
|
||||
js_DeepBail(JSContext *cx)
|
||||
{
|
||||
@ -4946,7 +4966,8 @@ js_DeepBail(JSContext *cx)
|
||||
JS_ASSERT(cx->bailExit);
|
||||
|
||||
JS_TRACE_MONITOR(cx).onTrace = false;
|
||||
JS_TRACE_MONITOR(cx).prohibitRecording = true;
|
||||
JS_TRACE_MONITOR(cx).prohibitFlush++;
|
||||
debug_only_v(printf("Deep bail.\n");)
|
||||
LeaveTree(*cx->interpState, cx->bailExit);
|
||||
cx->bailExit = NULL;
|
||||
cx->interpState->builtinStatus |= JSBUILTIN_BAILED;
|
||||
|
@ -661,9 +661,6 @@ js_PurgeScriptFragments(JSContext* cx, JSScript* script);
|
||||
extern bool
|
||||
js_OverfullFragmento(nanojit::Fragmento *frago, size_t maxsz);
|
||||
|
||||
extern void
|
||||
js_FlushJITCache(JSContext* cx);
|
||||
|
||||
extern void
|
||||
js_PurgeJITOracle();
|
||||
|
||||
|
@ -4996,6 +4996,24 @@ function testDeepPropertyShadowing()
|
||||
}
|
||||
test(testDeepPropertyShadowing);
|
||||
|
||||
// Complicated whitebox test for bug 487845.
|
||||
function testGlobalShapeChangeAfterDeepBail() {
|
||||
function f(name) {
|
||||
this[name] = 1; // may change global shape
|
||||
for (var i = 0; i < 4; i++)
|
||||
; // MonitorLoopEdge eventually triggers assertion
|
||||
}
|
||||
|
||||
// When i==3, deep-bail, then change global shape enough times to exhaust
|
||||
// the array of GlobalStates.
|
||||
var arr = [[], [], [], ["bug0", "bug1", "bug2", "bug3", "bug4"]];
|
||||
for (var i = 0; i < arr.length; i++)
|
||||
arr[i].forEach(f);
|
||||
}
|
||||
test(testGlobalShapeChangeAfterDeepBail);
|
||||
for (let i = 0; i < 5; i++)
|
||||
delete this["bug" + i];
|
||||
|
||||
/*****************************************************************************
|
||||
* *
|
||||
* _____ _ _ _____ ______ _____ _______ *
|
||||
|
Loading…
Reference in New Issue
Block a user