Skip to content

Commit 07b62fd

Browse files
committed
1 parent 54be7f5 commit 07b62fd

File tree

8 files changed

+108
-44
lines changed

8 files changed

+108
-44
lines changed

lib/Backend/BailOut.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,6 +1403,7 @@ BailOutRecord::BailOutHelper(Js::JavascriptCallStackLayout * layout, Js::ScriptF
14031403
//
14041404
Js::Arguments generatorArgs = generator->GetArguments();
14051405
Js::InterpreterStackFrame::Setup setup(function, generatorArgs, true, isInlinee);
1406+
Assert(setup.GetStackAllocationVarCount() == 0);
14061407
size_t varAllocCount = setup.GetAllocationVarCount();
14071408
size_t varSizeInBytes = varAllocCount * sizeof(Js::Var);
14081409
DWORD_PTR stackAddr = reinterpret_cast<DWORD_PTR>(&generator); // as mentioned above, use any stack address from this frame to ensure correct debugging functionality
@@ -1415,11 +1416,14 @@ BailOutRecord::BailOutHelper(Js::JavascriptCallStackLayout * layout, Js::ScriptF
14151416
// Allocate invalidVar on GC instead of stack since this InterpreterStackFrame will out live the current real frame
14161417
Js::Var invalidVar = (Js::RecyclableObject*)RecyclerNewPlusLeaf(functionScriptContext->GetRecycler(), sizeof(Js::RecyclableObject), Js::Var);
14171418
memset(invalidVar, 0xFE, sizeof(Js::RecyclableObject));
1418-
newInstance = setup.InitializeAllocation(allocation, false, false, loopHeaderArray, stackAddr, invalidVar);
1419-
#else
1420-
newInstance = setup.InitializeAllocation(allocation, false, false, loopHeaderArray, stackAddr);
14211419
#endif
14221420

1421+
newInstance = setup.InitializeAllocation(allocation, nullptr, false, false, loopHeaderArray, stackAddr
1422+
#if DBG
1423+
, invalidVar
1424+
#endif
1425+
);
1426+
14231427
newInstance->m_reader.Create(executeFunction);
14241428

14251429
generator->SetFrame(newInstance, varSizeInBytes);
@@ -1429,18 +1433,28 @@ BailOutRecord::BailOutHelper(Js::JavascriptCallStackLayout * layout, Js::ScriptF
14291433
{
14301434
Js::InterpreterStackFrame::Setup setup(function, args, true, isInlinee);
14311435
size_t varAllocCount = setup.GetAllocationVarCount();
1432-
size_t varSizeInBytes = varAllocCount * sizeof(Js::Var);
1436+
size_t stackVarAllocCount = setup.GetStackAllocationVarCount();
1437+
size_t varSizeInBytes;
1438+
Js::Var *stackAllocation = nullptr;
14331439

14341440
// If the locals area exceeds a certain limit, allocate it from a private arena rather than
14351441
// this frame. The current limit is based on an old assert on the number of locals we would allow here.
1436-
if (varAllocCount > Js::InterpreterStackFrame::LocalsThreshold)
1442+
if ((varAllocCount + stackVarAllocCount) > Js::InterpreterStackFrame::LocalsThreshold)
14371443
{
14381444
ArenaAllocator *tmpAlloc = nullptr;
14391445
fReleaseAlloc = functionScriptContext->EnsureInterpreterArena(&tmpAlloc);
1446+
varSizeInBytes = varAllocCount * sizeof(Js::Var);
14401447
allocation = (Js::Var*)tmpAlloc->Alloc(varSizeInBytes);
1448+
if (stackVarAllocCount != 0)
1449+
{
1450+
size_t stackVarSizeInBytes = stackVarAllocCount * sizeof(Js::Var);
1451+
PROBE_STACK_PARTIAL_INITIALIZED_BAILOUT_FRAME(functionScriptContext, Js::Constants::MinStackInterpreter + stackVarSizeInBytes, returnAddress);
1452+
stackAllocation = (Js::Var*)_alloca(stackVarSizeInBytes);
1453+
}
14411454
}
14421455
else
14431456
{
1457+
varSizeInBytes = (varAllocCount + stackVarAllocCount) * sizeof(Js::Var);
14441458
PROBE_STACK_PARTIAL_INITIALIZED_BAILOUT_FRAME(functionScriptContext, Js::Constants::MinStackInterpreter + varSizeInBytes, returnAddress);
14451459
allocation = (Js::Var*)_alloca(varSizeInBytes);
14461460
}
@@ -1465,11 +1479,14 @@ BailOutRecord::BailOutHelper(Js::JavascriptCallStackLayout * layout, Js::ScriptF
14651479
#if DBG
14661480
Js::Var invalidStackVar = (Js::RecyclableObject*)_alloca(sizeof(Js::RecyclableObject));
14671481
memset(invalidStackVar, 0xFE, sizeof(Js::RecyclableObject));
1468-
newInstance = setup.InitializeAllocation(allocation, false, false, loopHeaderArray, frameStackAddr, invalidStackVar);
1469-
#else
1470-
newInstance = setup.InitializeAllocation(allocation, false, false, loopHeaderArray, frameStackAddr);
14711482
#endif
14721483

1484+
newInstance = setup.InitializeAllocation(allocation, stackAllocation, false, false, loopHeaderArray, frameStackAddr
1485+
#if DBG
1486+
, invalidStackVar
1487+
#endif
1488+
);
1489+
14731490
newInstance->m_reader.Create(executeFunction);
14741491
}
14751492

lib/Runtime/Base/ScriptContext.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,6 +1485,11 @@ namespace Js
14851485
this->GetThreadContext()->RegisterScriptContext(this);
14861486
}
14871487

1488+
bool ScriptContext::ExceedsStackNestedFuncCount(uint count)
1489+
{
1490+
return count >= (InterpreterStackFrame::LocalsThreshold / (sizeof(StackScriptFunction) / sizeof(Var)));
1491+
}
1492+
14881493
#ifdef ENABLE_SCRIPT_DEBUGGING
14891494
ArenaAllocator* ScriptContext::AllocatorForDiagnostics()
14901495
{

lib/Runtime/Base/ScriptContext.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,8 @@ namespace Js
10891089
ScriptConfiguration const * GetConfig(void) const { return &config; }
10901090
CharClassifier const * GetCharClassifier(void) const;
10911091

1092+
static bool ExceedsStackNestedFuncCount(uint count);
1093+
10921094
ThreadContext * GetThreadContext() const { return threadContext; }
10931095

10941096
static const int MaxEvalSourceSize = 400;

lib/Runtime/ByteCode/ByteCodeGenerator.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1839,7 +1839,8 @@ bool ByteCodeGenerator::CanStackNestedFunc(FuncInfo * funcInfo, bool trace)
18391839
Assert(!funcInfo->IsGlobalFunction());
18401840
bool const doStackNestedFunc = !funcInfo->HasMaybeEscapedNestedFunc() && !IsInDebugMode()
18411841
&& !funcInfo->byteCodeFunction->IsCoroutine()
1842-
&& !funcInfo->byteCodeFunction->IsModule();
1842+
&& !funcInfo->byteCodeFunction->IsModule()
1843+
&& !Js::ScriptContext::ExceedsStackNestedFuncCount(funcInfo->root->nestedCount);
18431844
if (!doStackNestedFunc)
18441845
{
18451846
return false;

lib/Runtime/Language/InterpreterStackFrame.cpp

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,33 +1022,35 @@ namespace Js
10221022
uint forInVarCount = bailedOut ? 0 : (this->executeFunction->GetForInLoopDepth() * (sizeof(Js::ForInObjectEnumerator) / sizeof(Var)));
10231023
this->varAllocCount = k_stackFrameVarCount + localCount + this->executeFunction->GetOutParamMaxDepth() + forInVarCount +
10241024
extraVarCount + this->executeFunction->GetInnerScopeCount();
1025+
this->stackVarAllocCount = 0;
10251026

10261027
if (this->executeFunction->DoStackNestedFunc() && this->executeFunction->GetNestedCount() != 0)
10271028
{
10281029
// Track stack funcs...
1029-
this->varAllocCount += (sizeof(StackScriptFunction) * this->executeFunction->GetNestedCount()) / sizeof(Var);
1030+
this->stackVarAllocCount += (sizeof(StackScriptFunction) * this->executeFunction->GetNestedCount()) / sizeof(Var);
10301031
if (!this->bailedOutOfInlinee)
10311032
{
10321033
// Frame display (if environment depth is statically known)...
10331034
if (this->executeFunction->DoStackFrameDisplay())
10341035
{
10351036
uint16 envDepth = this->executeFunction->GetEnvDepth();
10361037
Assert(envDepth != (uint16)-1);
1037-
this->varAllocCount += sizeof(FrameDisplay) / sizeof(Var) + (envDepth + 1);
1038+
this->stackVarAllocCount += sizeof(FrameDisplay) / sizeof(Var) + (envDepth + 1);
10381039
}
10391040
// ...and scope slots (if any)
10401041
if (this->executeFunction->DoStackScopeSlots())
10411042
{
10421043
uint32 scopeSlots = this->executeFunction->scopeSlotArraySize;
10431044
Assert(scopeSlots != 0);
1044-
this->varAllocCount += scopeSlots + Js::ScopeSlots::FirstSlotIndex;
1045+
this->stackVarAllocCount += scopeSlots + Js::ScopeSlots::FirstSlotIndex;
10451046
}
10461047
}
10471048
}
10481049
}
10491050

10501051
InterpreterStackFrame *
1051-
InterpreterStackFrame::Setup::InitializeAllocation(__in_ecount(varAllocCount) Var * allocation, bool initParams, bool profileParams, LoopHeader* loopHeaderArray, DWORD_PTR stackAddr
1052+
InterpreterStackFrame::Setup::InitializeAllocation(__in_ecount(varAllocCount) Var * allocation, __in_ecount(stackVarAllocCount) Var * stackAllocation
1053+
, bool initParams, bool profileParams, LoopHeader* loopHeaderArray, DWORD_PTR stackAddr
10521054
#if DBG
10531055
, Var invalidStackVar
10541056
#endif
@@ -1183,29 +1185,35 @@ namespace Js
11831185

11841186
if (this->executeFunction->DoStackNestedFunc() && this->executeFunction->GetNestedCount() != 0)
11851187
{
1186-
newInstance->InitializeStackFunctions((StackScriptFunction *)nextAllocBytes);
1187-
nextAllocBytes = nextAllocBytes + sizeof(StackScriptFunction) * this->executeFunction->GetNestedCount();
1188+
char * stackAllocBytes = (stackAllocation != nullptr) ? (char*)stackAllocation : nextAllocBytes;
1189+
1190+
newInstance->InitializeStackFunctions((StackScriptFunction *)stackAllocBytes);
1191+
stackAllocBytes += sizeof(StackScriptFunction) * this->executeFunction->GetNestedCount();
11881192

11891193
if (!this->bailedOutOfInlinee)
11901194
{
11911195
if (this->executeFunction->DoStackFrameDisplay())
11921196
{
11931197
uint16 envDepth = this->executeFunction->GetEnvDepth();
11941198
Assert(envDepth != (uint16)-1);
1195-
newInstance->localFrameDisplay = (FrameDisplay*)nextAllocBytes;
1199+
newInstance->localFrameDisplay = (FrameDisplay*)stackAllocBytes;
11961200
newInstance->localFrameDisplay->SetLength(0); // Start with no scopes. It will get set in NewFrameDisplay
1197-
nextAllocBytes += sizeof(FrameDisplay) + (envDepth + 1) * sizeof(Var);
1201+
stackAllocBytes += sizeof(FrameDisplay) + (envDepth + 1) * sizeof(Var);
11981202
}
11991203

12001204
if (this->executeFunction->DoStackScopeSlots())
12011205
{
12021206
uint32 scopeSlots = this->executeFunction->scopeSlotArraySize;
12031207
Assert(scopeSlots != 0);
1204-
ScopeSlots((Field(Var)*)nextAllocBytes).SetCount(0); // Start with count as 0. It will get set in NewScopeSlots
1205-
newInstance->localClosure = nextAllocBytes;
1206-
nextAllocBytes += (scopeSlots + ScopeSlots::FirstSlotIndex) * sizeof(Var);
1208+
ScopeSlots((Field(Var)*)stackAllocBytes).SetCount(0); // Start with count as 0. It will get set in NewScopeSlots
1209+
newInstance->localClosure = stackAllocBytes;
1210+
stackAllocBytes += (scopeSlots + ScopeSlots::FirstSlotIndex) * sizeof(Var);
12071211
}
12081212
}
1213+
if (stackAllocation == nullptr)
1214+
{
1215+
nextAllocBytes = stackAllocBytes;
1216+
}
12091217
}
12101218
#if ENABLE_PROFILE_INFO
12111219
if (Js::DynamicProfileInfo::EnableImplicitCallFlags(this->executeFunction))
@@ -1755,6 +1763,7 @@ namespace Js
17551763
ScriptContext* functionScriptContext = function->GetScriptContext();
17561764
Arguments generatorArgs = generator->GetArguments();
17571765
InterpreterStackFrame::Setup setup(function, generatorArgs);
1766+
Assert(setup.GetStackAllocationVarCount() == 0);
17581767
size_t varAllocCount = setup.GetAllocationVarCount();
17591768
size_t varSizeInBytes = varAllocCount * sizeof(Var);
17601769
DWORD_PTR stackAddr = reinterpret_cast<DWORD_PTR>(&generator); // use any stack address from this frame to ensure correct debugging functionality
@@ -1768,11 +1777,14 @@ namespace Js
17681777
Js::RecyclableObject* invalidVar = (Js::RecyclableObject*)RecyclerNewPlusLeaf(functionScriptContext->GetRecycler(), sizeof(Js::RecyclableObject), Var);
17691778
AnalysisAssert(invalidVar);
17701779
memset(reinterpret_cast<void*>(invalidVar), 0xFE, sizeof(Js::RecyclableObject));
1771-
newInstance = setup.InitializeAllocation(allocation, executeFunction->GetHasImplicitArgIns(), doProfile, loopHeaderArray, stackAddr, invalidVar);
1772-
#else
1773-
newInstance = setup.InitializeAllocation(allocation, executeFunction->GetHasImplicitArgIns(), doProfile, loopHeaderArray, stackAddr);
17741780
#endif
17751781

1782+
newInstance = setup.InitializeAllocation(allocation, nullptr, executeFunction->GetHasImplicitArgIns(), doProfile, loopHeaderArray, stackAddr
1783+
#if DBG
1784+
, invalidVar
1785+
#endif
1786+
);
1787+
17761788
newInstance->m_reader.Create(executeFunction);
17771789

17781790
generator->SetFrame(newInstance, varSizeInBytes);
@@ -1914,26 +1926,36 @@ namespace Js
19141926
{
19151927
InterpreterStackFrame::Setup setup(function, args);
19161928
size_t varAllocCount = setup.GetAllocationVarCount();
1917-
size_t varSizeInBytes = varAllocCount * sizeof(Var);
1929+
size_t stackVarAllocCount = setup.GetStackAllocationVarCount();
1930+
size_t varSizeInBytes;
19181931

19191932
//
19201933
// Allocate a new InterpreterStackFrame instance on the interpreter's virtual stack.
19211934
//
19221935
DWORD_PTR stackAddr;
19231936

19241937
Var* allocation;
1938+
Var* stackAllocation = nullptr;
19251939

19261940
// If the locals area exceeds a certain limit, allocate it from a private arena rather than
19271941
// this frame. The current limit is based on an old assert on the number of locals we would allow here.
1928-
if (varAllocCount > InterpreterStackFrame::LocalsThreshold)
1942+
if ((varAllocCount + stackVarAllocCount) > InterpreterStackFrame::LocalsThreshold)
19291943
{
19301944
ArenaAllocator *tmpAlloc = nullptr;
19311945
fReleaseAlloc = functionScriptContext->EnsureInterpreterArena(&tmpAlloc);
1946+
varSizeInBytes = varAllocCount * sizeof(Var);
19321947
allocation = (Var*)tmpAlloc->Alloc(varSizeInBytes);
19331948
stackAddr = reinterpret_cast<DWORD_PTR>(&allocation); // use a stack address so the debugger stepping logic works (step-out, for example, compares stack depths to determine when to complete the step)
1949+
if (stackVarAllocCount != 0)
1950+
{
1951+
size_t stackVarSizeInBytes = stackVarAllocCount * sizeof(Var);
1952+
PROBE_STACK_PARTIAL_INITIALIZED_INTERPRETER_FRAME(functionScriptContext, Js::Constants::MinStackInterpreter + stackVarSizeInBytes);
1953+
stackAllocation = (Var*)_alloca(stackVarSizeInBytes);
1954+
}
19341955
}
19351956
else
19361957
{
1958+
varSizeInBytes = (varAllocCount + stackVarAllocCount) * sizeof(Var);
19371959
PROBE_STACK_PARTIAL_INITIALIZED_INTERPRETER_FRAME(functionScriptContext, Js::Constants::MinStackInterpreter + varSizeInBytes);
19381960
allocation = (Var*)_alloca(varSizeInBytes);
19391961
#if DBG
@@ -1966,11 +1988,14 @@ namespace Js
19661988
#if DBG
19671989
Js::RecyclableObject * invalidStackVar = (Js::RecyclableObject*)_alloca(sizeof(Js::RecyclableObject));
19681990
memset(reinterpret_cast<void*>(invalidStackVar), 0xFE, sizeof(Js::RecyclableObject));
1969-
newInstance = setup.InitializeAllocation(allocation, executeFunction->GetHasImplicitArgIns() && !isAsmJs, doProfile, loopHeaderArray, stackAddr, invalidStackVar);
1970-
#else
1971-
newInstance = setup.InitializeAllocation(allocation, executeFunction->GetHasImplicitArgIns() && !isAsmJs, doProfile, loopHeaderArray, stackAddr);
19721991
#endif
19731992

1993+
newInstance = setup.InitializeAllocation(allocation, stackAllocation, executeFunction->GetHasImplicitArgIns() && !isAsmJs, doProfile, loopHeaderArray, stackAddr
1994+
#if DBG
1995+
, invalidStackVar
1996+
#endif
1997+
);
1998+
19741999
newInstance->m_reader.Create(executeFunction);
19752000
}
19762001
//
@@ -2784,22 +2809,32 @@ namespace Js
27842809
// after reparsing, we want to also use a new interpreter stack frame, as it will have different characteristics than the asm.js version
27852810
InterpreterStackFrame::Setup setup(funcObj, m_inParams, m_inSlotsCount);
27862811
size_t varAllocCount = setup.GetAllocationVarCount();
2787-
size_t varSizeInBytes = varAllocCount * sizeof(Var);
2812+
size_t stackVarAllocCount = setup.GetStackAllocationVarCount();
2813+
size_t varSizeInBytes;
27882814

27892815
Var* allocation = nullptr;
2816+
Var* stackAllocation = nullptr;
27902817
DWORD_PTR stackAddr;
27912818
bool fReleaseAlloc = false;
2792-
if (varAllocCount > InterpreterStackFrame::LocalsThreshold)
2819+
if ((varAllocCount + stackVarAllocCount) > InterpreterStackFrame::LocalsThreshold)
27932820
{
27942821
ArenaAllocator *tmpAlloc = nullptr;
27952822
fReleaseAlloc = GetScriptContext()->EnsureInterpreterArena(&tmpAlloc);
2823+
varSizeInBytes = varAllocCount * sizeof(Var);
27962824
allocation = (Var*)tmpAlloc->Alloc(varSizeInBytes);
2825+
if (stackVarAllocCount != 0)
2826+
{
2827+
size_t stackVarSizeInBytes = stackVarAllocCount * sizeof(Var);
2828+
PROBE_STACK_PARTIAL_INITIALIZED_INTERPRETER_FRAME(GetScriptContext(), Js::Constants::MinStackInterpreter + stackVarSizeInBytes);
2829+
stackAllocation = (Var*)_alloca(stackVarSizeInBytes);
2830+
}
27972831
// use a stack address so the debugger stepping logic works (step-out, for example, compares stack depths to determine when to complete the step)
27982832
// debugger stepping does not matter here, but it's worth being consistent with normal stack frame
27992833
stackAddr = reinterpret_cast<DWORD_PTR>(&allocation);
28002834
}
28012835
else
28022836
{
2837+
varSizeInBytes = (varAllocCount + stackVarAllocCount) * sizeof(Var);
28032838
PROBE_STACK_PARTIAL_INITIALIZED_INTERPRETER_FRAME(GetScriptContext(), Js::Constants::MinStackInterpreter + varSizeInBytes);
28042839
allocation = (Var*)_alloca(varSizeInBytes);
28052840
stackAddr = reinterpret_cast<DWORD_PTR>(allocation);
@@ -2808,10 +2843,14 @@ namespace Js
28082843
#if DBG
28092844
Var invalidStackVar = (Js::RecyclableObject*)_alloca(sizeof(Js::RecyclableObject));
28102845
memset(invalidStackVar, 0xFE, sizeof(Js::RecyclableObject));
2811-
InterpreterStackFrame * newInstance = newInstance = setup.InitializeAllocation(allocation, funcObj->GetFunctionBody()->GetHasImplicitArgIns(), doProfile, nullptr, stackAddr, invalidStackVar);
2812-
#else
2813-
InterpreterStackFrame * newInstance = newInstance = setup.InitializeAllocation(allocation, funcObj->GetFunctionBody()->GetHasImplicitArgIns(), doProfile, nullptr, stackAddr);
28142846
#endif
2847+
2848+
InterpreterStackFrame * newInstance = setup.InitializeAllocation(allocation, stackAllocation, funcObj->GetFunctionBody()->GetHasImplicitArgIns(), doProfile, nullptr, stackAddr
2849+
#if DBG
2850+
, invalidStackVar
2851+
#endif
2852+
);
2853+
28152854
newInstance->m_reader.Create(funcObj->GetFunctionBody());
28162855
// now that we have set up the new frame, let's interpret it!
28172856
funcObj->GetFunctionBody()->BeginExecution();

0 commit comments

Comments
 (0)