Skip to content

Commit c50a992

Browse files
committed
Argument length and argument constant optimizations
1 parent d1524eb commit c50a992

File tree

8 files changed

+100
-16
lines changed

8 files changed

+100
-16
lines changed

lib/Backend/Func.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ Func::Func(JitArenaAllocator *alloc, JITTimeWorkItem * workItem,
5858
m_bailoutReturnValueSym(nullptr),
5959
m_hasBailedOutSym(nullptr),
6060
m_inlineeFrameStartSym(nullptr),
61+
inlineeStart(nullptr),
6162
m_regsUsed(0),
6263
m_fg(nullptr),
6364
m_labelCount(0),
@@ -92,6 +93,7 @@ Func::Func(JitArenaAllocator *alloc, JITTimeWorkItem * workItem,
9293
hasInlinee(false),
9394
thisOrParentInlinerHasArguments(false),
9495
hasStackArgs(false),
96+
hasArgLenAndConstOpt(false),
9597
hasImplicitParamLoad(false),
9698
hasThrow(false),
9799
hasNonSimpleParams(false),
@@ -334,6 +336,8 @@ Func::Codegen(JitArenaAllocator *alloc, JITTimeWorkItem * workItem,
334336
case RejitReason::DisableStackArgOpt:
335337
outputData->disableStackArgOpt = TRUE;
336338
break;
339+
case RejitReason::DisableStackArgLenAndConstOpt:
340+
break;
337341
case RejitReason::DisableSwitchOptExpectingInteger:
338342
case RejitReason::DisableSwitchOptExpectingString:
339343
outputData->disableSwitchOpt = TRUE;

lib/Backend/Func.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,17 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece;
517517
m_inlineeFrameStartSym = sym;
518518
}
519519

520+
void SetInlineeStart(IR::Instr *inlineeStartInstr)
521+
{
522+
Assert(inlineeStart == nullptr);
523+
inlineeStart = inlineeStartInstr;
524+
}
525+
526+
IR::Instr* GetInlineeStart()
527+
{
528+
return inlineeStart;
529+
}
530+
520531
IR::SymOpnd *GetInlineeArgCountSlotOpnd()
521532
{
522533
return GetInlineeOpndAtOffset(Js::Constants::InlineeMetaArgIndex_Argc * MachPtr);
@@ -721,6 +732,7 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece;
721732
bool hasBailout: 1;
722733
bool hasBailoutInEHRegion : 1;
723734
bool hasStackArgs: 1;
735+
bool hasArgLenAndConstOpt : 1;
724736
bool hasImplicitParamLoad : 1; // True if there is a load of CallInfo, FunctionObject
725737
bool hasThrow : 1;
726738
bool hasUnoptimizedArgumentsAccess : 1; // True if there are any arguments access beyond the simple case of this.apply pattern
@@ -1030,6 +1042,7 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece;
10301042
Func * const topFunc;
10311043
Func * const parentFunc;
10321044
StackSym * m_inlineeFrameStartSym;
1045+
IR::Instr * inlineeStart;
10331046
uint maxInlineeArgOutSize;
10341047
const bool m_isBackgroundJIT;
10351048
bool hasInstrNumber;

lib/Backend/GlobOpt.cpp

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ GlobOpt::Optimize()
174174

175175
// Still need to run the dead store phase to calculate the live reg on back edge
176176
this->BackwardPass(Js::DeadStorePhase);
177-
CannotAllocateArgumentsObjectOnStack();
177+
CannotAllocateArgumentsObjectOnStack(nullptr);
178178
return;
179179
}
180180

@@ -883,7 +883,7 @@ GlobOpt::ToTypeSpec(BVSparse<JitArenaAllocator> *bv, BasicBlock *block, IRType t
883883
// instruction itself should disable arguments object optimization.
884884
if(block->globOptData.argObjSyms && block->globOptData.IsArgumentsSymID(id))
885885
{
886-
CannotAllocateArgumentsObjectOnStack();
886+
CannotAllocateArgumentsObjectOnStack(insertBeforeInstr->m_func);
887887
}
888888

889889
if (block->globOptData.liveVarSyms->Test(id))
@@ -1508,7 +1508,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
15081508

15091509
if (instr->m_func->GetJITFunctionBody()->GetInParamsCount() != 1 && !instr->m_func->IsStackArgsEnabled())
15101510
{
1511-
CannotAllocateArgumentsObjectOnStack();
1511+
CannotAllocateArgumentsObjectOnStack(instr->m_func);
15121512
}
15131513
else
15141514
{
@@ -1523,7 +1523,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
15231523
// In the debug mode, we don't want to optimize away the aliases. Since we may have to show them on the inspection.
15241524
if (((!AreFromSameBytecodeFunc(src1->AsRegOpnd(), dst->AsRegOpnd()) || this->currentBlock->loop) && instr->m_opcode != Js::OpCode::BytecodeArgOutCapture) || this->func->IsJitInDebugMode())
15251525
{
1526-
CannotAllocateArgumentsObjectOnStack();
1526+
CannotAllocateArgumentsObjectOnStack(instr->m_func);
15271527
return;
15281528
}
15291529
if(!dst->AsRegOpnd()->GetStackSym()->m_nonEscapingArgObjAlias)
@@ -1546,7 +1546,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
15461546
}
15471547

15481548
SymID id = 0;
1549-
1549+
15501550
switch(instr->m_opcode)
15511551
{
15521552
case Js::OpCode::LdElemI_A:
@@ -1557,7 +1557,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
15571557
if (indexOpnd && CurrentBlockData()->IsArgumentsSymID(indexOpnd->m_sym->m_id))
15581558
{
15591559
// Pathological test cases such as a[arguments]
1560-
CannotAllocateArgumentsObjectOnStack();
1560+
CannotAllocateArgumentsObjectOnStack(instr->m_func);
15611561
return;
15621562
}
15631563

@@ -1646,7 +1646,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
16461646
WritePerfHint(PerfHints::HeapArgumentsCreated, instr->m_func, instr->GetByteCodeOffset());
16471647
}
16481648
#endif
1649-
CannotAllocateArgumentsObjectOnStack();
1649+
CannotAllocateArgumentsObjectOnStack(instr->m_func);
16501650
return;
16511651
}
16521652
}
@@ -1664,7 +1664,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
16641664
WritePerfHint(PerfHints::HeapArgumentsCreated, instr->m_func, instr->GetByteCodeOffset());
16651665
}
16661666
#endif
1667-
CannotAllocateArgumentsObjectOnStack();
1667+
CannotAllocateArgumentsObjectOnStack(instr->m_func);
16681668
return;
16691669
}
16701670
}
@@ -1683,7 +1683,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
16831683
WritePerfHint(PerfHints::HeapArgumentsModification, instr->m_func, instr->GetByteCodeOffset());
16841684
}
16851685
#endif
1686-
CannotAllocateArgumentsObjectOnStack();
1686+
CannotAllocateArgumentsObjectOnStack(instr->m_func);
16871687
return;
16881688
}
16891689
}
@@ -1697,7 +1697,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
16971697
WritePerfHint(PerfHints::HeapArgumentsModification, instr->m_func, instr->GetByteCodeOffset());
16981698
}
16991699
#endif
1700-
CannotAllocateArgumentsObjectOnStack();
1700+
CannotAllocateArgumentsObjectOnStack(instr->m_func);
17011701
return;
17021702
}
17031703
CurrentBlockData()->ClearArgumentsSym(dst->AsRegOpnd());
@@ -2442,6 +2442,7 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved)
24422442
OptimizeChecks(instr);
24432443
OptArraySrc(&instr, &src1Val, &src2Val);
24442444
OptNewScObject(&instr, src1Val);
2445+
OptArgLenAndConst(instr, &src1Val);
24452446

24462447
instr = this->OptPeep(instr, src1Val, src2Val);
24472448

@@ -13083,6 +13084,61 @@ GlobOpt::OptArraySrc(IR::Instr ** const instrRef, Value ** src1Val, Value ** src
1308313084
arraySrcOpt.Optimize();
1308413085
}
1308513086

13087+
void
13088+
GlobOpt::OptArgLenAndConst(IR::Instr* instr, Value** src1Val)
13089+
{
13090+
if (instr->m_func->IsStackArgsEnabled() && instr->IsInlined())
13091+
{
13092+
IR::Opnd* src1 = instr->GetSrc1();
13093+
auto replaceInstr = [&](IR::Instr* instr, IR::Opnd* newopnd, Value** src1Val)
13094+
{
13095+
this->CaptureByteCodeSymUses(instr);
13096+
instr->m_opcode = Js::OpCode::Ld_A;
13097+
instr->ReplaceSrc1(newopnd);
13098+
if (instr->HasBailOutInfo())
13099+
{
13100+
instr->ClearBailOutInfo();
13101+
}
13102+
*src1Val = this->OptSrc(instr->GetSrc1(), &instr);
13103+
instr->m_func->hasArgLenAndConstOpt = true;
13104+
};
13105+
switch(instr->m_opcode)
13106+
{
13107+
case Js::OpCode::LdLen_A:
13108+
{
13109+
if (CurrentBlockData()->IsArgumentsOpnd(src1))
13110+
{
13111+
IR::AddrOpnd* newopnd = IR::AddrOpnd::New(Js::TaggedInt::ToVarUnchecked(instr->m_func->actualCount - 1), IR::AddrOpndKindConstantVar, instr->m_func);
13112+
replaceInstr(instr, newopnd, src1Val);
13113+
}
13114+
break;
13115+
}
13116+
13117+
case Js::OpCode::LdElemI_A:
13118+
{
13119+
IR::IndirOpnd* indirOpndSrc1 = src1->AsIndirOpnd();
13120+
if (!indirOpndSrc1->GetIndexOpnd() && CurrentBlockData()->IsArgumentsOpnd(src1))
13121+
{
13122+
int argIndex = indirOpndSrc1->GetOffset() + 1;
13123+
IR::Instr* defInstr = nullptr;
13124+
IR::Instr* inlineeStart = instr->m_func->GetInlineeStart();
13125+
inlineeStart->IterateArgInstrs([&](IR::Instr* argInstr) {
13126+
StackSym *argSym = argInstr->GetDst()->AsSymOpnd()->m_sym->AsStackSym();
13127+
if (argSym->GetArgSlotNum() - 1 == argIndex)
13128+
{
13129+
defInstr = argInstr;
13130+
return true;
13131+
}
13132+
return false;
13133+
});
13134+
replaceInstr(instr, defInstr->GetSrc1(), src1Val);
13135+
}
13136+
break;
13137+
}
13138+
}
13139+
}
13140+
}
13141+
1308613142
void
1308713143
GlobOpt::CaptureNoImplicitCallUses(
1308813144
IR::Opnd *opnd,
@@ -15609,16 +15665,23 @@ GlobOpt::TrackArgumentsObject()
1560915665
{
1561015666
if (PHASE_OFF(Js::StackArgOptPhase, this->func))
1561115667
{
15612-
this->CannotAllocateArgumentsObjectOnStack();
15668+
this->CannotAllocateArgumentsObjectOnStack(nullptr);
1561315669
return false;
1561415670
}
1561515671

1561615672
return func->GetHasStackArgs();
1561715673
}
1561815674

1561915675
void
15620-
GlobOpt::CannotAllocateArgumentsObjectOnStack()
15676+
GlobOpt::CannotAllocateArgumentsObjectOnStack(Func * curFunc)
1562115677
{
15678+
if (curFunc != nullptr && curFunc->hasArgLenAndConstOpt)
15679+
{
15680+
Assert(!curFunc->GetJITOutput()->GetOutputData()->disableStackArgOpt);
15681+
curFunc->GetJITOutput()->GetOutputData()->disableStackArgOpt = true;
15682+
throw Js::RejitException(RejitReason::DisableStackArgLenAndConstOpt);
15683+
}
15684+
1562215685
func->SetHasStackArgs(false);
1562315686

1562415687
#ifdef ENABLE_DEBUG_CONFIG_OPTIONS

lib/Backend/GlobOpt.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,7 @@ class GlobOpt
688688
IR::Instr* CreateBoundsCheckInstr(IR::Opnd* lowerBound, IR::Opnd* upperBound, int offset, IR::BailOutKind bailoutkind, BailOutInfo* bailoutInfo, Func* func);
689689
IR::Instr* AttachBoundsCheckData(IR::Instr* instr, IR::Opnd* lowerBound, IR::Opnd* upperBound, int offset);
690690
void OptArraySrc(IR::Instr **const instrRef, Value ** src1Val, Value ** src2Val);
691+
void OptArgLenAndConst(IR::Instr* instr, Value** src1Val);
691692

692693
private:
693694
void TrackIntSpecializedAddSubConstant(IR::Instr *const instr, const AddSubConstantInfo *const addSubConstantInfo, Value *const dstValue, const bool updateSourceBounds);
@@ -911,7 +912,7 @@ class GlobOpt
911912
void UpdateObjPtrValueType(IR::Opnd * opnd, IR::Instr * instr);
912913

913914
bool TrackArgumentsObject();
914-
void CannotAllocateArgumentsObjectOnStack();
915+
void CannotAllocateArgumentsObjectOnStack(Func * curFunc);
915916

916917
#if DBG
917918
bool IsPropertySymId(SymID symId) const;

lib/Backend/GlobOptBailOut.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -849,7 +849,7 @@ void GlobOpt::EndTrackingOfArgObjSymsForInlinee()
849849
// This means there are arguments object symbols in the current function which are not in the current block.
850850
// This could happen when one of the blocks has a throw and arguments object aliased in it and other blocks don't see it.
851851
// Rare case, abort stack arguments optimization in this case.
852-
CannotAllocateArgumentsObjectOnStack();
852+
CannotAllocateArgumentsObjectOnStack(this->currentBlock->globOptData.curFunc);
853853
}
854854
else
855855
{

lib/Backend/GlobOptBlockData.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ GlobOptBlockData::MergeBlockData(
660660
{
661661
if (!this->argObjSyms->Equal(fromData->argObjSyms))
662662
{
663-
this->globOpt->CannotAllocateArgumentsObjectOnStack();
663+
this->globOpt->CannotAllocateArgumentsObjectOnStack(nullptr);
664664
}
665665
}
666666

lib/Backend/Inline.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1360,12 +1360,13 @@ void Inline::InsertOneInlinee(IR::Instr* callInstr, IR::RegOpnd* returnValueOpnd
13601360
Js::ArgSlot actualCount = MapActuals(currentCallInstr, argOuts, Js::InlineeCallInfo::MaxInlineeArgoutCount, inlinee, (Js::ProfileId)callInstr->AsProfiledInstr()->u.profileId, &stackArgsArgOutExpanded);
13611361
Assert(actualCount > 0);
13621362
MapFormals(inlinee, argOuts, funcBody->GetInParamsCount(), actualCount, returnValueOpnd, currentCallInstr->GetSrc1(), symCallerThis, stackArgsArgOutExpanded, fixedFunctionSafeThis, argOuts);
1363+
inlinee->SetInlineeStart(currentCallInstr);
13631364
currentCallInstr->m_func = inlinee;
13641365

13651366
// Put the meta arguments that the stack walker expects to find on the stack.
13661367
// As all the argouts are shared among the inlinees, do this only once.
13671368
SetupInlineeFrame(inlinee, currentCallInstr, actualCount, currentCallInstr->GetSrc1());
1368-
1369+
13691370
IR::Instr* inlineeEndInstr = IR::Instr::New(Js::OpCode::InlineeEnd, inlinee);
13701371
inlineeEndInstr->SetByteCodeOffset(inlinee->m_tailInstr->GetPrevRealInstr());
13711372
inlineeEndInstr->SetSrc1(IR::IntConstOpnd::New(actualCount + Js::Constants::InlineeMetaArgCount, TyInt32, inlinee));
@@ -3967,6 +3968,7 @@ Inline::InlineFunctionCommon(IR::Instr *callInstr, bool originalCallTargetOpndIs
39673968
callInstr->m_opcode = Js::OpCode::InlineeStart;
39683969

39693970
// Set it to belong to the inlinee, so that we can use the actual count when lowering InlineeStart
3971+
inlinee->SetInlineeStart(callInstr);
39703972
callInstr->m_func = inlinee;
39713973
callInstr->SetDst(IR::RegOpnd::New(returnValueOpnd ? returnValueOpnd->GetType() : TyVar, inlinee));
39723974
// Put the meta arguments that the stack walker expects to find on the stack.

lib/Common/Common/RejitReasons.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,5 @@ REJIT_REASON(ModByPowerOf2)
4949
REJIT_REASON(NoProfile)
5050
REJIT_REASON(PowIntIntTypeSpecDisabled)
5151
REJIT_REASON(DisableStackArgOpt)
52+
REJIT_REASON(DisableStackArgLenAndConstOpt)
5253
REJIT_REASON(OptimizeTryFinallyDisabled)

0 commit comments

Comments
 (0)