Skip to content

Commit b12c9f8

Browse files
committed
Redefer function bodies that are not currently being executed and are
eligible for deferred parsing (e.g., not arrow functions, not functions-in-block). This is experimental behavior, off by default. Define an 'on' mode in which all eligible functions are redeferred on GC, as well as a 'stress' mode in which all candidates are redeferred on each stack probe. This change is built on a previous PR that refactors the FunctionBody hierarchy to make it easier to toggle between deferred and fully-compiled states.
1 parent 4bc98ae commit b12c9f8

22 files changed

+696
-462
lines changed

lib/Backend/Opnd.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3234,7 +3234,7 @@ Opnd::GetAddrDescription(__out_ecount(count) char16 *const description, const si
32343234

32353235
case IR::AddrOpndKindDynamicFunctionInfo:
32363236
DumpAddress(address, printToConsole, skipMaskedAddress);
3237-
DumpFunctionInfo(&buffer, &n, (Js::FunctionInfo *)address, printToConsole);
3237+
DumpFunctionInfo(&buffer, &n, ((Js::FunctionBody *)address)->GetFunctionInfo(), printToConsole);
32383238
break;
32393239

32403240
case IR::AddrOpndKindDynamicFunctionBody:

lib/Common/ConfigFlagsList.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ PHASE(All)
1010
PHASE(Parse)
1111
PHASE(RegexCompile)
1212
PHASE(DeferParse)
13+
PHASE(Redeferral)
1314
PHASE(DeferEventHandlers)
1415
PHASE(FunctionSourceInfoParse)
1516
PHASE(StringTemplateParse)

lib/Parser/Parse.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4881,7 +4881,8 @@ bool Parser::ParseFncDeclHelper(ParseNodePtr pnodeFnc, LPCOLESTR pNameHint, usho
48814881
BOOL isDeferredFnc = IsDeferredFnc();
48824882
AnalysisAssert(isDeferredFnc || pnodeFnc);
48834883
isTopLevelDeferredFunc =
4884-
(!isDeferredFnc
4884+
(!fLambda
4885+
&& !isDeferredFnc
48854886
&& DeferredParse(pnodeFnc->sxFnc.functionId)
48864887
&& (!pnodeFnc->sxFnc.IsNested() || CONFIG_FLAG(DeferNested))
48874888
// Don't defer if this is a function expression not contained in a statement or other expression.
@@ -4892,6 +4893,8 @@ bool Parser::ParseFncDeclHelper(ParseNodePtr pnodeFnc, LPCOLESTR pNameHint, usho
48924893
&& !fModule
48934894
);
48944895

4896+
pnodeFnc->sxFnc.SetCanBeDeferred(isTopLevelDeferredFunc);
4897+
48954898
if (!fLambda &&
48964899
!isDeferredFnc &&
48974900
!isLikelyIIFE &&
@@ -10560,7 +10563,7 @@ void Parser::InitPids()
1056010563
wellKnownPropertyPids._star = m_phtbl->PidHashNameLen(_u("*"), sizeof("*") - 1);
1056110564
}
1056210565

10563-
void Parser::RestoreScopeInfo(Js::FunctionBody* functionBody)
10566+
void Parser::RestoreScopeInfo(Js::ParseableFunctionInfo* functionBody)
1056410567
{
1056510568
if (!functionBody)
1056610569
{
@@ -10616,7 +10619,7 @@ void Parser::RestoreScopeInfo(Js::FunctionBody* functionBody)
1061610619
scopeInfo->GetScopeInfo(this, nullptr, nullptr, scope);
1061710620
}
1061810621

10619-
void Parser::FinishScopeInfo(Js::FunctionBody *functionBody)
10622+
void Parser::FinishScopeInfo(Js::ParseableFunctionInfo *functionBody)
1062010623
{
1062110624
if (!functionBody)
1062210625
{

lib/Parser/Parse.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -967,8 +967,8 @@ class Parser
967967
void RemovePrevPidRef(IdentPtr pid, PidRefStack *lastRef);
968968
void SetPidRefsInScopeDynamic(IdentPtr pid, int blockId);
969969

970-
void RestoreScopeInfo(Js::FunctionBody* functionBody);
971-
void FinishScopeInfo(Js::FunctionBody* functionBody);
970+
void RestoreScopeInfo(Js::ParseableFunctionInfo* functionBody);
971+
void FinishScopeInfo(Js::ParseableFunctionInfo* functionBody);
972972

973973
BOOL PnodeLabelNoAST(IdentToken* pToken, LabelId* pLabelIdList);
974974
LabelId* CreateLabelId(IdentToken* pToken);

lib/Parser/ptree.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ struct PnFnc
246246
#endif
247247
RestorePoint *pRestorePoint;
248248
DeferredFunctionStub *deferredStub;
249+
bool canBeDeferred;
249250

250251
static const int32 MaxStackClosureAST = 800000;
251252

@@ -316,6 +317,7 @@ struct PnFnc
316317
void SetUsesArguments(bool set = true) { SetFlags(kFunctionUsesArguments, set); }
317318
void SetIsDefaultModuleExport(bool set = true) { SetFlags(kFunctionIsDefaultModuleExport, set); }
318319
void SetNestedFuncEscapes(bool set = true) { nestedFuncEscapes = set; }
320+
void SetCanBeDeferred(bool set = true) { canBeDeferred = set; }
319321

320322
bool CallsEval() const { return HasFlags(kFunctionCallsEval); }
321323
bool ChildCallsEval() const { return HasFlags(kFunctionChildCallsEval); }
@@ -353,6 +355,7 @@ struct PnFnc
353355
bool UsesArguments() const { return HasFlags(kFunctionUsesArguments); }
354356
bool IsDefaultModuleExport() const { return HasFlags(kFunctionIsDefaultModuleExport); }
355357
bool NestedFuncEscapes() const { return nestedFuncEscapes; }
358+
bool CanBeDeferred() const { return canBeDeferred; }
356359

357360
size_t LengthInBytes()
358361
{

lib/Runtime/Base/FunctionBody.cpp

Lines changed: 128 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -328,16 +328,6 @@ namespace Js
328328
void
329329
FunctionBody::CopySourceInfo(ParseableFunctionInfo* originalFunctionInfo)
330330
{
331-
this->m_sourceIndex = originalFunctionInfo->GetSourceIndex();
332-
this->m_cchStartOffset = originalFunctionInfo->StartInDocument();
333-
this->m_cchLength = originalFunctionInfo->LengthInChars();
334-
this->m_lineNumber = originalFunctionInfo->GetRelativeLineNumber();
335-
this->m_columnNumber = originalFunctionInfo->GetRelativeColumnNumber();
336-
this->m_isEval = originalFunctionInfo->IsEval();
337-
this->m_isDynamicFunction = originalFunctionInfo->IsDynamicFunction();
338-
this->m_cbStartOffset = originalFunctionInfo->StartOffset();
339-
this->m_cbLength = originalFunctionInfo->LengthInBytes();
340-
341331
this->FinishSourceInfo();
342332
}
343333

@@ -453,7 +443,6 @@ namespace Js
453443
loopInterpreterLimit(CONFIG_FLAG(LoopInterpretCount)),
454444
savedPolymorphicCacheState(0),
455445
debuggerScopeIndex(0),
456-
flags(Flags_HasNoExplicitReturnValue),
457446
m_hasFinally(false),
458447
#if ENABLE_PROFILE_INFO
459448
dynamicProfileInfo(nullptr),
@@ -564,6 +553,32 @@ namespace Js
564553
InitDisableInlineSpread();
565554
}
566555

556+
void FunctionBody::RedeferFunction()
557+
{
558+
Assert(this->CanBeDeferred());
559+
PHASE_PRINT_TRACE(Js::RedeferralPhase, this, L"Redeferring function %d.%d: %s\n",
560+
GetSourceContextId(), GetLocalFunctionId(),
561+
GetDisplayName() ? GetDisplayName() : L"(Anonymous function)");
562+
563+
ParseableFunctionInfo * parseableFunctionInfo =
564+
Js::ParseableFunctionInfo::NewDeferredFunctionFromFunctionBody(this);
565+
566+
FunctionInfo * functionInfo = this->GetFunctionInfo();
567+
functionInfo->SetAttributes((FunctionInfo::Attributes)(functionInfo->GetAttributes() | FunctionInfo::Attributes::DeferredParse));
568+
functionInfo->SetFunctionProxy(parseableFunctionInfo);
569+
570+
// In either case register the function reference
571+
// GetScriptContext()->GetLibrary()->RegisterDynamicFunctionReference(parseableFunctionInfo);
572+
573+
this->MapFunctionObjectTypes([&](DynamicType* type)
574+
{
575+
Assert(type->GetTypeId() == TypeIds_Function);
576+
577+
ScriptFunctionType* functionType = (ScriptFunctionType*)type;
578+
functionType->SetEntryPoint(GetScriptContext()->DeferredParsingThunk);
579+
});
580+
}
581+
567582
void FunctionBody::SetDefaultFunctionEntryPointInfo(FunctionEntryPointInfo* entryPointInfo, const JavascriptMethod originalEntryPoint)
568583
{
569584
Assert(entryPointInfo);
@@ -1040,9 +1055,10 @@ namespace Js
10401055
}
10411056
}
10421057

1043-
void ParseableFunctionInfo::Copy(FunctionBody* other)
1058+
void ParseableFunctionInfo::Copy(ParseableFunctionInfo * other)
10441059
{
10451060
#define CopyDeferParseField(field) other->field = this->field;
1061+
CopyDeferParseField(flags);
10461062
CopyDeferParseField(m_isDeclaration);
10471063
CopyDeferParseField(m_isAccessor);
10481064
CopyDeferParseField(m_isStrictMode);
@@ -1067,8 +1083,21 @@ namespace Js
10671083
other->SetDeferredStubs(this->GetDeferredStubs());
10681084
CopyDeferParseField(m_isAsmjsMode);
10691085
CopyDeferParseField(m_isAsmJsFunction);
1070-
#undef CopyDeferParseField
10711086

1087+
CopyDeferParseField(m_sourceIndex);
1088+
CopyDeferParseField(m_cchStartOffset);
1089+
CopyDeferParseField(m_cchLength);
1090+
CopyDeferParseField(m_lineNumber);
1091+
CopyDeferParseField(m_columnNumber);
1092+
CopyDeferParseField(m_cbStartOffset);
1093+
CopyDeferParseField(m_cbLength);
1094+
1095+
#undef CopyDeferParseField
1096+
}
1097+
1098+
void ParseableFunctionInfo::Copy(FunctionBody* other)
1099+
{
1100+
this->Copy(static_cast<ParseableFunctionInfo*>(other));
10721101
other->CopySourceInfo(this);
10731102
}
10741103

@@ -1137,6 +1166,7 @@ namespace Js
11371166
#if DYNAMIC_INTERPRETER_THUNK
11381167
m_dynamicInterpreterThunk(nullptr),
11391168
#endif
1169+
flags(Flags_HasNoExplicitReturnValue),
11401170
m_hasBeenParsed(false),
11411171
m_isGlobalFunc(false),
11421172
m_isDeclaration(false),
@@ -1207,6 +1237,41 @@ namespace Js
12071237
this->SetOriginalEntryPoint(DefaultEntryThunk);
12081238
}
12091239

1240+
ParseableFunctionInfo::ParseableFunctionInfo(ParseableFunctionInfo * proxy) :
1241+
FunctionProxy(proxy->GetScriptContext(), proxy->GetUtf8SourceInfo(), proxy->GetFunctionNumber()),
1242+
#if DYNAMIC_INTERPRETER_THUNK
1243+
m_dynamicInterpreterThunk(nullptr),
1244+
#endif
1245+
m_hasBeenParsed(false),
1246+
m_isNamedFunctionExpression(proxy->GetIsNamedFunctionExpression()),
1247+
m_isNameIdentifierRef (proxy->GetIsNameIdentifierRef()),
1248+
m_isStaticNameFunction(proxy->GetIsStaticNameFunction()),
1249+
m_reportedInParamCount(proxy->GetReportedInParamsCount()),
1250+
m_reparsed(proxy->IsReparsed())
1251+
#if DBG
1252+
,m_wasEverAsmjsMode(proxy->m_wasEverAsmjsMode)
1253+
#endif
1254+
{
1255+
proxy->Copy(this);
1256+
1257+
FunctionInfo * functionInfo = proxy->GetFunctionInfo();
1258+
this->functionInfo = functionInfo;
1259+
1260+
uint nestedCount = proxy->GetNestedCount();
1261+
if (nestedCount > 0)
1262+
{
1263+
nestedArray = RecyclerNewPlusZ(m_scriptContext->GetRecycler(),
1264+
nestedCount*sizeof(FunctionProxy*), NestedArray, nestedCount);
1265+
}
1266+
else
1267+
{
1268+
nestedArray = nullptr;
1269+
}
1270+
1271+
SetBoundPropertyRecords(proxy->GetBoundPropertyRecords());
1272+
SetDisplayName(proxy->GetDisplayName(), proxy->GetDisplayNameLength(), proxy->GetShortDisplayNameOffset());
1273+
}
1274+
12101275
ParseableFunctionInfo* ParseableFunctionInfo::New(ScriptContext* scriptContext, int nestedCount,
12111276
LocalFunctionId functionId, Utf8SourceInfo* sourceInfo, const char16* displayName, uint displayNameLength, uint displayShortNameOffset, Js::PropertyRecordList* propertyRecords, FunctionInfo::Attributes attributes)
12121277
{
@@ -1247,6 +1312,38 @@ namespace Js
12471312
propertyRecords);
12481313
}
12491314

1315+
ParseableFunctionInfo *
1316+
ParseableFunctionInfo::NewDeferredFunctionFromFunctionBody(FunctionBody * functionBody)
1317+
{
1318+
ScriptContext * scriptContext = functionBody->GetScriptContext();
1319+
FunctionInfo * functionInfo = functionBody->GetFunctionInfo();
1320+
uint nestedCount = functionBody->GetNestedCount();
1321+
1322+
ParseableFunctionInfo * info = RecyclerNewWithBarrierFinalized(scriptContext->GetRecycler(),
1323+
ParseableFunctionInfo,
1324+
functionBody);
1325+
1326+
// Create new entry point info
1327+
info->m_defaultEntryPointInfo = RecyclerNew(scriptContext->GetRecycler(), ProxyEntryPointInfo, scriptContext->DeferredParsingThunk);
1328+
1329+
// New allocation is done at this point, so update existing structures
1330+
// Adjust functionInfo attributes, point to new proxy
1331+
functionInfo->SetAttributes((FunctionInfo::Attributes)(functionInfo->GetAttributes() | FunctionInfo::Attributes::DeferredParse));
1332+
functionInfo->SetFunctionProxy(info);
1333+
functionInfo->SetOriginalEntryPoint(DefaultEntryThunk);
1334+
1335+
// Initialize nested function array, update back pointers
1336+
for (uint i = 0; i < nestedCount; i++)
1337+
{
1338+
FunctionProxy * nestedProxy = functionBody->GetNestedFunc(i);
1339+
info->SetNestedFunc(nestedProxy, i, 0);
1340+
}
1341+
1342+
// Update function objects
1343+
1344+
return info;
1345+
}
1346+
12501347
DWORD_PTR FunctionProxy::GetSecondaryHostSourceContext() const
12511348
{
12521349
return this->GetUtf8SourceInfo()->GetSecondaryHostSourceContext();
@@ -1734,6 +1831,11 @@ namespace Js
17341831
this->Copy(funcBody);
17351832
PERF_COUNTER_DEC(Code, DeferredFunction);
17361833

1834+
this->UpdateFunctionBodyImpl(funcBody);
1835+
// FunctionInfo * functionInfo = this->GetFunctionInfo();
1836+
// funcBody->SetFunctionInfo(functionInfo);
1837+
// functionInfo->SetFunctionProxy(funcBody);
1838+
17371839
if (!this->GetSourceContextInfo()->IsDynamic())
17381840
{
17391841
PHASE_PRINT_TESTTRACE1(Js::DeferParsePhase, _u("TestTrace: Deferred function parsed - ID: %d; Display Name: %s; Length: %d; Nested Function Count: %d; Utf8SourceInfo: %d; Source Length: %d; Is Top Level: %s; Source Url: %s\n"), m_functionNumber, m_displayName, this->m_cchLength, this->GetNestedCount(), this->m_utf8SourceInfo->GetSourceInfoId(), this->m_utf8SourceInfo->GetCchLength(), this->GetIsTopLevel() ? _u("True") : _u("False"), this->GetSourceContextInfo()->url);
@@ -1870,7 +1972,7 @@ namespace Js
18701972
hrParser = ps.ParseSourceWithOffset(&parseTree, pszStart, offset, length, charOffset, isCesu8, grfscr, &se,
18711973
&nextFunctionId, funcBody->GetRelativeLineNumber(), funcBody->GetSourceContextInfo(),
18721974
funcBody);
1873-
Assert(FAILED(hrParser) || nextFunctionId == funcBody->deferredParseNextFunctionId || isDebugOrAsmJsReparse || isByteCodeDeserialization);
1975+
// Assert(FAILED(hrParser) || nextFunctionId == funcBody->deferredParseNextFunctionId || isDebugOrAsmJsReparse || isByteCodeDeserialization);
18741976

18751977
if (FAILED(hrParser))
18761978
{
@@ -1956,7 +2058,7 @@ namespace Js
19562058
// Restore if the function has nameIdentifier reference, as that name on the left side will not be parsed again while deferparse.
19572059
funcBody->SetIsNameIdentifierRef(this->GetIsNameIdentifierRef());
19582060

1959-
this->UpdateFunctionBodyImpl(funcBody);
2061+
// this->UpdateFunctionBodyImpl(funcBody);
19602062
this->m_hasBeenParsed = true;
19612063
returnFunctionBody = funcBody;
19622064
}
@@ -3420,33 +3522,34 @@ namespace Js
34203522
}
34213523
}
34223524

3423-
void FunctionBody::SetStackNestedFuncParent(FunctionBody * parentFunctionBody)
3525+
void FunctionBody::SetStackNestedFuncParent(FunctionInfo * parentFunctionInfo)
34243526
{
3527+
FunctionBody * parentFunctionBody = parentFunctionInfo->GetFunctionBody();
34253528
Assert(this->GetStackNestedFuncParent() == nullptr);
34263529
Assert(CanDoStackNestedFunc());
34273530
Assert(parentFunctionBody->DoStackNestedFunc());
34283531

3429-
this->SetAuxPtr(AuxPointerType::StackNestedFuncParent, this->GetScriptContext()->GetRecycler()->CreateWeakReferenceHandle(parentFunctionBody));
3532+
this->SetAuxPtr(AuxPointerType::StackNestedFuncParent, this->GetScriptContext()->GetRecycler()->CreateWeakReferenceHandle(parentFunctionInfo));
34303533
}
34313534

3432-
FunctionBody * FunctionBody::GetStackNestedFuncParentStrongRef()
3535+
FunctionInfo * FunctionBody::GetStackNestedFuncParentStrongRef()
34333536
{
34343537
Assert(this->GetStackNestedFuncParent() != nullptr);
34353538
return this->GetStackNestedFuncParent()->Get();
34363539
}
34373540

3438-
RecyclerWeakReference<FunctionBody> * FunctionBody::GetStackNestedFuncParent()
3541+
RecyclerWeakReference<FunctionInfo> * FunctionBody::GetStackNestedFuncParent()
34393542
{
3440-
return static_cast<RecyclerWeakReference<FunctionBody>*>(this->GetAuxPtr(AuxPointerType::StackNestedFuncParent));
3543+
return static_cast<RecyclerWeakReference<FunctionInfo>*>(this->GetAuxPtr(AuxPointerType::StackNestedFuncParent));
34413544
}
34423545

3443-
FunctionBody * FunctionBody::GetAndClearStackNestedFuncParent()
3546+
FunctionInfo * FunctionBody::GetAndClearStackNestedFuncParent()
34443547
{
34453548
if (this->GetAuxPtr(AuxPointerType::StackNestedFuncParent))
34463549
{
3447-
FunctionBody * parentFunctionBody = GetStackNestedFuncParentStrongRef();
3550+
FunctionInfo * parentFunctionInfo = GetStackNestedFuncParentStrongRef();
34483551
ClearStackNestedFuncParent();
3449-
return parentFunctionBody;
3552+
return parentFunctionInfo;
34503553
}
34513554
return nullptr;
34523555
}
@@ -4098,7 +4201,7 @@ namespace Js
40984201
}
40994202
#endif
41004203

4101-
void FunctionBody::SetIsNonUserCode(bool set)
4204+
void ParseableFunctionInfo::SetIsNonUserCode(bool set)
41024205
{
41034206
// Mark current function as a non-user code, so that we can distinguish cases where exceptions are
41044207
// caught in non-user code (see ProbeContainer::HasAllowedForException).
@@ -4107,7 +4210,7 @@ namespace Js
41074210
// Propagate setting for all functions in this scope (nested).
41084211
this->ForEachNestedFunc([&](FunctionProxy* proxy, uint32 index)
41094212
{
4110-
Js::FunctionBody * pBody = proxy->GetFunctionBody();
4213+
ParseableFunctionInfo * pBody = proxy->GetParseableFunctionInfo();
41114214
if (pBody != nullptr)
41124215
{
41134216
pBody->SetIsNonUserCode(set);

0 commit comments

Comments
 (0)