Skip to content

Commit d52a802

Browse files
committed
Enabling Stack Args when there is presence of nested functions.
Premise: The initial implementation of Stack Args was disabled when there is any nested function. We were missing a bunch of opportunities in the real world web pages - where nested function is a common thing. This PR enables this optimization for nested function. Summary: Stack Args will work partially when nested functions are present. We will attempt to remove only Heap Arguments Object creation instr and retain scope object creation instr (whenever required). Details: ByteCodeGeneration: - We track if there are any params being closure captured by any nested functions - We give up the optimization if this is case. - We track if there are any non-local references inside the nested function. - We do Stack Args partially here - i.e. we attempt to remove Heap Arguments Object creation but still create scope object - as scope object is required for FrameDisplay. IRBuilder: - Scope object tracking has been moved from Forward pass (Globopt) to this phase. - This enables us to mark the scope obj sym to be live during the first backward pass. Backward pass: - Scope Object Sym is kept alive in all code paths. - This is to facilitate Bailout to record the live Scope object Sym, whenever required. - Reason for doing is this because - Scope object has to be implicitly live whenever Heap Arguments object is live. - When we restore HeapArguments object in the bail out path, it expects the scope object also to be restored - if one was created. - We do not know detailed information about Heap arguments obj syms(aliasing etc.) until we complete Forward Pass. - And we want to avoid dead sym clean up (in this case, scope object though not explicitly live, it is live implicitly) during Block merging in the forward pass. Globopt pass: - Extra tracking for instrs having arguments object is done. Dead Store pass: - Scope obj creation instruction is not dead stored - It will be a MOV NULL instr in the Lowerer. - All instrs related cached scope obj is deadstored. Reason is to dead store the cached scope object creation instruction itself. - InitCachedFuncs is deadstored. - GetCachedFunc is replaced with NewScFunc instr. Lowerer pass: - If Stack Args optimization is still turned on and the parser has hinted that Scope obj creation is not required, then scope object creation instruction will be turned to a MOV NULL. - We retain the instruction and not remove it because we want the value of the scope object to be restored - That's when we can always rely on the bail out path to restore some valid value (NULL or the scope object itself). BailOut path: - We use the restored scope object value (we track this by using a flag on the bailoutInfo) and copy the param values to it. Added UT and some more traces Fixing a bug with the Type of Scope object. Type wasn't being transitioned correctly in cases where we remove LdHeapArguments opcode. Took care of review comments. Review Comments
1 parent 3176422 commit d52a802

34 files changed

+437
-102
lines changed

lib/Backend/BackwardPass.cpp

Lines changed: 105 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,23 @@ BackwardPass::InsertArgInsForFormals()
295295
}
296296
}
297297

298+
void
299+
BackwardPass::MarkScopeObjSymUseForStackArgOpt()
300+
{
301+
IR::Instr * instr = this->currentInstr;
302+
if (tag == Js::DeadStorePhase)
303+
{
304+
if (instr->DoStackArgsOpt(this->func) && instr->m_func->GetScopeObjSym() != nullptr)
305+
{
306+
if (this->currentBlock->byteCodeUpwardExposedUsed == nullptr)
307+
{
308+
this->currentBlock->byteCodeUpwardExposedUsed = JitAnew(this->tempAlloc, BVSparse<JitArenaAllocator>, this->tempAlloc);
309+
}
310+
this->currentBlock->byteCodeUpwardExposedUsed->Set(instr->m_func->GetScopeObjSym()->m_id);
311+
}
312+
}
313+
}
314+
298315
void
299316
BackwardPass::ProcessBailOnStackArgsOutOfActualsRange()
300317
{
@@ -1912,17 +1929,6 @@ BackwardPass::ProcessBailOutInfo(IR::Instr * instr)
19121929
BVSparse<JitArenaAllocator> * byteCodeUpwardExposedUsed = byteCodeUsesInstr->byteCodeUpwardExposedUsed;
19131930
if (byteCodeUpwardExposedUsed != nullptr)
19141931
{
1915-
//Stack Args for Formals optimization.
1916-
//We will restore the scope object in the bail out path, when we restore Heap arguments object.
1917-
//So we don't to mark the sym for scope object separately for restoration.
1918-
//When StackArgs for formal opt is ON , Scope object sym is used by formals access only, which will be replaced by ArgIns and Ld_A
1919-
//So it is ok to clear the bit here.
1920-
//Clearing the bit in byteCodeUpwardExposedUsed here.
1921-
if (instr->m_func->IsStackArgsEnabled() && instr->m_func->GetScopeObjSym())
1922-
{
1923-
byteCodeUpwardExposedUsed->Clear(instr->m_func->GetScopeObjSym()->m_id);
1924-
}
1925-
19261932
this->currentBlock->byteCodeUpwardExposedUsed->Or(byteCodeUpwardExposedUsed);
19271933
#if DBG
19281934
FOREACH_BITSET_IN_SPARSEBV(symId, byteCodeUpwardExposedUsed)
@@ -2540,6 +2546,7 @@ BackwardPass::ProcessBlock(BasicBlock * block)
25402546
continue;
25412547
}
25422548

2549+
MarkScopeObjSymUseForStackArgOpt();
25432550
ProcessBailOnStackArgsOutOfActualsRange();
25442551

25452552
if (ProcessNoImplicitCallUses(instr) || this->ProcessBailOutInfo(instr))
@@ -2554,7 +2561,7 @@ BackwardPass::ProcessBlock(BasicBlock * block)
25542561
continue;
25552562
}
25562563

2557-
if (CanDeadStoreInstrForScopeObjRemoval() && DeadStoreOrChangeInstrForScopeObjRemoval())
2564+
if (CanDeadStoreInstrForScopeObjRemoval() && DeadStoreOrChangeInstrForScopeObjRemoval(&instrPrev))
25582565
{
25592566
continue;
25602567
}
@@ -2636,7 +2643,7 @@ BackwardPass::ProcessBlock(BasicBlock * block)
26362643
{
26372644
case Js::OpCode::LdSlot:
26382645
{
2639-
DeadStoreOrChangeInstrForScopeObjRemoval();
2646+
DeadStoreOrChangeInstrForScopeObjRemoval(&instrPrev);
26402647
break;
26412648
}
26422649
case Js::OpCode::InlineArrayPush:
@@ -2889,19 +2896,17 @@ BackwardPass::CanDeadStoreInstrForScopeObjRemoval(Sym *sym) const
28892896
if (tag == Js::DeadStorePhase && this->currentInstr->m_func->IsStackArgsEnabled())
28902897
{
28912898
Func * currFunc = this->currentInstr->m_func;
2899+
bool doScopeObjCreation = currFunc->GetJITFunctionBody()->GetDoScopeObjectCreation();
28922900
switch (this->currentInstr->m_opcode)
28932901
{
2894-
case Js::OpCode::LdHeapArguments:
2895-
case Js::OpCode::LdHeapArgsCached:
2896-
case Js::OpCode::LdLetHeapArguments:
2897-
case Js::OpCode::LdLetHeapArgsCached:
2902+
case Js::OpCode::InitCachedScope:
28982903
{
2899-
if (this->currentInstr->GetSrc1()->IsScopeObjOpnd(currFunc))
2904+
if(!doScopeObjCreation && this->currentInstr->GetDst()->IsScopeObjOpnd(currFunc))
29002905
{
29012906
/*
2902-
* We don't really dead store these instructions. We just want the source sym of these instructions (which is the scope object)
2907+
* We don't really dead store this instruction. We just want the source sym of this instruction
29032908
* to NOT be tracked as USED by this instruction.
2904-
* In case of LdXXHeapArgsXXX opcodes, they will effectively be lowered to dest = MOV NULL, in the lowerer phase.
2909+
* This instr will effectively be lowered to dest = MOV NULLObject, in the lowerer phase.
29052910
*/
29062911
return true;
29072912
}
@@ -2916,13 +2921,23 @@ BackwardPass::CanDeadStoreInstrForScopeObjRemoval(Sym *sym) const
29162921
break;
29172922
}
29182923
case Js::OpCode::CommitScope:
2924+
case Js::OpCode::GetCachedFunc:
29192925
{
2920-
return this->currentInstr->GetSrc1()->IsScopeObjOpnd(currFunc);
2926+
return !doScopeObjCreation && this->currentInstr->GetSrc1()->IsScopeObjOpnd(currFunc);
29212927
}
29222928
case Js::OpCode::BrFncCachedScopeEq:
29232929
case Js::OpCode::BrFncCachedScopeNeq:
29242930
{
2925-
return this->currentInstr->GetSrc2()->IsScopeObjOpnd(currFunc);
2931+
return !doScopeObjCreation && this->currentInstr->GetSrc2()->IsScopeObjOpnd(currFunc);
2932+
}
2933+
case Js::OpCode::CallHelper:
2934+
{
2935+
if (!doScopeObjCreation && this->currentInstr->GetSrc1()->AsHelperCallOpnd()->m_fnHelper == IR::JnHelperMethod::HelperOP_InitCachedFuncs)
2936+
{
2937+
IR::RegOpnd * scopeObjOpnd = this->currentInstr->GetSrc2()->GetStackSym()->GetInstrDef()->GetSrc1()->AsRegOpnd();
2938+
return scopeObjOpnd->IsScopeObjOpnd(currFunc);
2939+
}
2940+
break;
29262941
}
29272942
}
29282943
}
@@ -2933,7 +2948,7 @@ BackwardPass::CanDeadStoreInstrForScopeObjRemoval(Sym *sym) const
29332948
* This is for Eliminating Scope Object Creation during Heap arguments optimization.
29342949
*/
29352950
bool
2936-
BackwardPass::DeadStoreOrChangeInstrForScopeObjRemoval()
2951+
BackwardPass::DeadStoreOrChangeInstrForScopeObjRemoval(IR::Instr ** pInstrPrev)
29372952
{
29382953
IR::Instr * instr = this->currentInstr;
29392954
Func * currFunc = instr->m_func;
@@ -2996,6 +3011,54 @@ BackwardPass::DeadStoreOrChangeInstrForScopeObjRemoval()
29963011
}
29973012
break;
29983013
}
3014+
case Js::OpCode::CallHelper:
3015+
{
3016+
//Remove the CALL and all its Argout instrs.
3017+
if (instr->GetSrc1()->AsHelperCallOpnd()->m_fnHelper == IR::JnHelperMethod::HelperOP_InitCachedFuncs)
3018+
{
3019+
IR::RegOpnd * scopeObjOpnd = instr->GetSrc2()->GetStackSym()->GetInstrDef()->GetSrc1()->AsRegOpnd();
3020+
if (scopeObjOpnd->IsScopeObjOpnd(currFunc))
3021+
{
3022+
IR::Instr * instrDef = instr;
3023+
IR::Instr * nextInstr = instr->m_next;
3024+
3025+
while (instrDef != nullptr)
3026+
{
3027+
IR::Instr * instrToDelete = instrDef;
3028+
if (instrDef->GetSrc2() != nullptr)
3029+
{
3030+
instrDef = instrDef->GetSrc2()->GetStackSym()->GetInstrDef();
3031+
Assert(instrDef->m_opcode == Js::OpCode::ArgOut_A);
3032+
}
3033+
else
3034+
{
3035+
instrDef = nullptr;
3036+
}
3037+
instrToDelete->Remove();
3038+
}
3039+
Assert(nextInstr != nullptr);
3040+
*pInstrPrev = nextInstr->m_prev;
3041+
return true;
3042+
}
3043+
}
3044+
break;
3045+
}
3046+
case Js::OpCode::GetCachedFunc:
3047+
{
3048+
// <dst> = GetCachedFunc <scopeObject>, <functionNum>
3049+
// is converted to
3050+
// <dst> = NewScFunc <functionNum>, <env: FrameDisplay>
3051+
3052+
if (instr->GetSrc1()->IsScopeObjOpnd(currFunc))
3053+
{
3054+
instr->m_opcode = Js::OpCode::NewScFunc;
3055+
IR::Opnd * intConstOpnd = instr->UnlinkSrc2();
3056+
3057+
instr->ReplaceSrc1(intConstOpnd);
3058+
instr->SetSrc2(IR::RegOpnd::New(currFunc->GetLocalFrameDisplaySym(), IRType::TyVar, currFunc));
3059+
}
3060+
break;
3061+
}
29993062
}
30003063
}
30013064
return false;
@@ -3005,7 +3068,7 @@ IR::Instr *
30053068
BackwardPass::TryChangeInstrForStackArgOpt()
30063069
{
30073070
IR::Instr * instr = this->currentInstr;
3008-
if (instr->DoStackArgsOpt(this->func))
3071+
if (tag == Js::DeadStorePhase && instr->DoStackArgsOpt(this->func))
30093072
{
30103073
switch (instr->m_opcode)
30113074
{
@@ -3035,6 +3098,22 @@ BackwardPass::TryChangeInstrForStackArgOpt()
30353098
}
30363099
}
30373100
}
3101+
3102+
/*
3103+
* Scope Object Sym is kept alive in all code paths.
3104+
* -This is to facilitate Bailout to record the live Scope object Sym, whenever required.
3105+
* -Reason for doing is this because - Scope object has to be implicitly live whenever Heap Arguments object is live.
3106+
* -When we restore HeapArguments object in the bail out path, it expects the scope object also to be restored - if one was created.
3107+
* -We do not know detailed information about Heap arguments obj syms(aliasing etc.) until we complete Forward Pass.
3108+
* -And we want to avoid dead sym clean up (in this case, scope object though not explicitly live, it is live implicitly) during Block merging in the forward pass.
3109+
* -Hence this is the optimal spot to do this.
3110+
*/
3111+
3112+
if (tag == Js::BackwardPhase && instr->m_func->GetScopeObjSym() != nullptr)
3113+
{
3114+
this->currentBlock->upwardExposedUses->Set(instr->m_func->GetScopeObjSym()->m_id);
3115+
}
3116+
30383117
return nullptr;
30393118
}
30403119

@@ -6610,13 +6689,8 @@ BackwardPass::DeadStoreInstr(IR::Instr *instr)
66106689
#endif
66116690
PropertySym *unusedPropertySym = nullptr;
66126691

6613-
// Do not track the Scope Obj - we will be restoring it in the bailout path while restoring Heap arguments object.
6614-
// See InterpreterStackFrame::TrySetFrameObjectInHeapArgObj
6615-
if (!(instr->m_func->IsStackArgsEnabled() && instr->m_opcode == Js::OpCode::LdSlotArr &&
6616-
instr->GetSrc1() && instr->GetSrc1()->IsScopeObjOpnd(instr->m_func)))
6617-
{
6618-
GlobOpt::TrackByteCodeSymUsed(instr, this->currentBlock->byteCodeUpwardExposedUsed, &unusedPropertySym);
6619-
}
6692+
GlobOpt::TrackByteCodeSymUsed(instr, this->currentBlock->byteCodeUpwardExposedUsed, &unusedPropertySym);
6693+
66206694
#if DBG
66216695
BVSparse<JitArenaAllocator> tempBv2(this->tempAlloc);
66226696
tempBv2.Copy(this->currentBlock->byteCodeUpwardExposedUsed);

lib/Backend/BackwardPass.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ class BackwardPass
3030
IR::Instr * TryChangeInstrForStackArgOpt();
3131
void InsertArgInsForFormals();
3232
void ProcessBailOnStackArgsOutOfActualsRange();
33-
bool DeadStoreOrChangeInstrForScopeObjRemoval();
33+
void MarkScopeObjSymUseForStackArgOpt();
34+
bool DeadStoreOrChangeInstrForScopeObjRemoval(IR::Instr ** pInstrPrev);
3435
void ProcessUse(IR::Opnd * opnd);
3536
bool ProcessDef(IR::Opnd * opnd);
3637
void ProcessTransfers(IR::Instr * instr);

lib/Backend/BailOut.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,12 @@ BailOutRecord::RestoreValue(IR::BailOutKind bailOutKind, Js::JavascriptCallStack
997997
}
998998
}
999999

1000+
Js::RegSlot localClosureReg = newInstance->function->GetFunctionBody()->GetLocalClosureRegister();
1001+
if (regSlot == localClosureReg)
1002+
{
1003+
this->globalBailOutRecordTable->isScopeObjRestored = true;
1004+
}
1005+
10001006
values[regSlot] = value;
10011007

10021008
BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u("\n"));
@@ -1651,9 +1657,13 @@ BailOutRecord::BailOutHelper(Js::JavascriptCallStackLayout * layout, Js::ScriptF
16511657

16521658
if (bailOutRecord->globalBailOutRecordTable->hasStackArgOpt)
16531659
{
1654-
newInstance->TrySetFrameObjectInHeapArgObj(functionScriptContext, bailOutRecord->globalBailOutRecordTable->hasNonSimpleParams);
1660+
newInstance->TrySetFrameObjectInHeapArgObj(functionScriptContext, bailOutRecord->globalBailOutRecordTable->hasNonSimpleParams,
1661+
bailOutRecord->globalBailOutRecordTable->isScopeObjRestored);
16551662
}
1656-
1663+
1664+
//Reset the value for tracking the restoration during next bail out.
1665+
bailOutRecord->globalBailOutRecordTable->isScopeObjRestored = false;
1666+
16571667
uint32 innerScopeCount = executeFunction->GetInnerScopeCount();
16581668
for (uint32 i = 0; i < innerScopeCount; i++)
16591669
{

lib/Backend/BailOut.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,7 @@ struct GlobalBailOutRecordDataTable
471471
bool isLoopBody;
472472
bool hasNonSimpleParams;
473473
bool hasStackArgOpt;
474+
bool isScopeObjRestored;
474475
void Finalize(NativeCodeData::Allocator *allocator, JitArenaAllocator *tempAlloc);
475476
void AddOrUpdateRow(JitArenaAllocator *allocator, uint32 bailOutRecordId, uint32 regSlot, bool isFloat, bool isInt,
476477
bool isSimd128F4, bool isSimd128I4, bool isSimd128I8, bool isSimd128I16, bool isSimd128U4, bool isSimd128U8, bool isSimd128U16, bool isSimd128B4, bool isSimd128B8, bool isSimd128B16,

lib/Backend/FlowGraph.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2590,7 +2590,18 @@ FlowGraph::RemoveInstr(IR::Instr *instr, GlobOpt * globOpt)
25902590
return instr;
25912591
}
25922592

2593+
/*
2594+
* Scope object has to be implicitly live whenever Heap Arguments object is live.
2595+
* - When we restore HeapArguments object in the bail out path, it expects the scope object also to be restored - if one was created.
2596+
*/
25932597
Js::OpCode opcode = instr->m_opcode;
2598+
if (opcode == Js::OpCode::LdElemI_A && instr->DoStackArgsOpt(this->func) &&
2599+
globOpt->IsArgumentsOpnd(instr->GetSrc1()) && instr->m_func->GetScopeObjSym())
2600+
{
2601+
IR::Instr * byteCodeUsesInstr = IR::ByteCodeUsesInstr::New(instr, instr->m_func->GetScopeObjSym()->m_id);
2602+
instr->InsertAfter(byteCodeUsesInstr);
2603+
}
2604+
25942605
IR::ByteCodeUsesInstr * newByteCodeUseInstr = globOpt->ConvertToByteCodeUses(instr);
25952606
if (newByteCodeUseInstr != nullptr)
25962607
{

lib/Backend/GlobOpt.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3944,7 +3944,7 @@ GlobOpt::TrackInstrsForScopeObjectRemoval(IR::Instr * instr)
39443944

39453945
if (instr->m_opcode == Js::OpCode::Ld_A && src1->IsRegOpnd())
39463946
{
3947-
AssertMsg(!src1->IsScopeObjOpnd(instr->m_func), "There can be no aliasing for scope object.");
3947+
AssertMsg(!instr->m_func->IsStackArgsEnabled() || !src1->IsScopeObjOpnd(instr->m_func), "There can be no aliasing for scope object.");
39483948
}
39493949

39503950
// The following is to track formals array for Stack Arguments optimization with Formals
@@ -4063,7 +4063,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
40634063
StackSym * scopeObjSym = instr->GetSrc1()->GetStackSym();
40644064
Assert(scopeObjSym);
40654065
Assert(scopeObjSym->GetInstrDef()->m_opcode == Js::OpCode::InitCachedScope || scopeObjSym->GetInstrDef()->m_opcode == Js::OpCode::NewScopeObject);
4066-
instr->m_func->SetScopeObjSym(scopeObjSym);
4066+
Assert(instr->m_func->GetScopeObjSym() == scopeObjSym);
40674067

40684068
if (PHASE_VERBOSE_TRACE1(Js::StackArgFormalsOptPhase))
40694069
{
@@ -4134,6 +4134,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
41344134
{
41354135
instr->usesStackArgumentsObject = true;
41364136
}
4137+
41374138
break;
41384139
}
41394140
case Js::OpCode::LdLen_A:
@@ -4147,6 +4148,11 @@ GlobOpt::OptArguments(IR::Instr *instr)
41474148
}
41484149
case Js::OpCode::ArgOut_A_InlineBuiltIn:
41494150
{
4151+
if (IsArgumentsOpnd(src1))
4152+
{
4153+
instr->usesStackArgumentsObject = true;
4154+
}
4155+
41504156
if (IsArgumentsOpnd(src1) &&
41514157
src1->AsRegOpnd()->m_sym->GetInstrDef()->m_opcode == Js::OpCode::BytecodeArgOutCapture)
41524158
{
@@ -4180,7 +4186,14 @@ GlobOpt::OptArguments(IR::Instr *instr)
41804186
case Js::OpCode::BailOnNotStackArgs:
41814187
case Js::OpCode::ArgOut_A_FromStackArgs:
41824188
case Js::OpCode::BytecodeArgOutUse:
4189+
{
4190+
if (src1 && IsArgumentsOpnd(src1))
4191+
{
4192+
instr->usesStackArgumentsObject = true;
4193+
}
4194+
41834195
break;
4196+
}
41844197

41854198
default:
41864199
{

lib/Backend/GlobOpt.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1292,6 +1292,7 @@ class GlobOpt
12921292

12931293
IR::ByteCodeUsesInstr * ConvertToByteCodeUses(IR::Instr * isntr);
12941294
bool GetIsAsmJSFunc()const{ return isAsmJSFunc; };
1295+
BOOLEAN IsArgumentsOpnd(IR::Opnd* opnd);
12951296
private:
12961297
bool IsLoopPrePass() const { return this->prePassLoop != nullptr; }
12971298
void OptBlock(BasicBlock *block);
@@ -1335,7 +1336,6 @@ class GlobOpt
13351336
IR::Instr * SetTypeCheckBailOut(IR::Opnd *opnd, IR::Instr *instr, BailOutInfo *bailOutInfo);
13361337
void OptArguments(IR::Instr *Instr);
13371338
void TrackInstrsForScopeObjectRemoval(IR::Instr * instr);
1338-
BOOLEAN IsArgumentsOpnd(IR::Opnd* opnd);
13391339
bool AreFromSameBytecodeFunc(IR::RegOpnd* src1, IR::RegOpnd* dst);
13401340
void TrackArgumentsSym(IR::RegOpnd* opnd);
13411341
void ClearArgumentsSym(IR::RegOpnd* opnd);

lib/Backend/IRBuilder.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,6 +1589,10 @@ IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0)
15891589
Js::RegSlot regFrameObj = m_func->GetJITFunctionBody()->GetLocalClosureReg();
15901590
Assert(regFrameObj != Js::Constants::NoRegister);
15911591
srcOpnd = BuildSrcOpnd(regFrameObj);
1592+
if (m_func->GetJITFunctionBody()->GetInParamsCount() > 1)
1593+
{
1594+
m_func->SetScopeObjSym(srcOpnd->GetStackSym());
1595+
}
15921596
}
15931597
else
15941598
{
@@ -1617,6 +1621,10 @@ IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0)
16171621
Js::Throw::FatalInternalError();
16181622
}
16191623
srcOpnd = BuildSrcOpnd(m_func->GetJITFunctionBody()->GetLocalClosureReg());
1624+
if (m_func->GetJITFunctionBody()->GetInParamsCount() > 1)
1625+
{
1626+
m_func->SetScopeObjSym(srcOpnd->GetStackSym());
1627+
}
16201628
isNotInt = true;
16211629
break;
16221630

0 commit comments

Comments
 (0)