Skip to content

Commit 4b72289

Browse files
committed
[MERGE #5702 @yullin-ms] Argument length and argument constant optimization
Merge pull request #5702 from yullin-ms:argLenAndConstOpt Optimize LdLen_A argument length and LdElemI_A for the argument[constants]
2 parents 565eee0 + 37feadb commit 4b72289

9 files changed

+115
-18
lines changed

lib/Backend/BackwardPass.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -7842,10 +7842,12 @@ BackwardPass::ProcessInlineeEnd(IR::Instr* instr)
78427842
}
78437843
if (this->tag == Js::BackwardPhase)
78447844
{
7845-
if (!GlobOpt::DoInlineArgsOpt(instr->m_func))
7845+
// Commenting out to allow for argument length and argument[constant] optimization
7846+
// Will revisit in phase two
7847+
/*if (!GlobOpt::DoInlineArgsOpt(instr->m_func))
78467848
{
78477849
return;
7848-
}
7850+
}*/
78497851

78507852
// This adds a use for function sym as part of InlineeStart & all the syms referenced by the args.
78517853
// It ensure they do not get cleared from the copy prop sym map.

lib/Backend/Func.cpp

+7
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),
@@ -301,8 +303,10 @@ Func::Codegen(JitArenaAllocator *alloc, JITTimeWorkItem * workItem,
301303
Js::ScriptContextProfiler *const codeGenProfiler, const bool isBackgroundJIT)
302304
{
303305
bool rejit;
306+
int rejitCounter = 0;
304307
do
305308
{
309+
Assert(rejitCounter < 25);
306310
Func func(alloc, workItem, threadContextInfo,
307311
scriptContextInfo, outputData, epInfo, runtimeInfo,
308312
polymorphicInlineCacheInfo, codeGenAllocators,
@@ -334,6 +338,8 @@ Func::Codegen(JitArenaAllocator *alloc, JITTimeWorkItem * workItem,
334338
case RejitReason::DisableStackArgOpt:
335339
outputData->disableStackArgOpt = TRUE;
336340
break;
341+
case RejitReason::DisableStackArgLenAndConstOpt:
342+
break;
337343
case RejitReason::DisableSwitchOptExpectingInteger:
338344
case RejitReason::DisableSwitchOptExpectingString:
339345
outputData->disableSwitchOpt = TRUE;
@@ -360,6 +366,7 @@ Func::Codegen(JitArenaAllocator *alloc, JITTimeWorkItem * workItem,
360366
}
361367

362368
rejit = true;
369+
rejitCounter++;
363370
}
364371
// Either the entry point has a reference to the number now, or we failed to code gen and we
365372
// don't need to numbers, we can flush the completed page now.

lib/Backend/Func.h

+13
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

+83-12
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ GlobOpt::Optimize()
175175

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

@@ -887,7 +887,7 @@ GlobOpt::ToTypeSpec(BVSparse<JitArenaAllocator> *bv, BasicBlock *block, IRType t
887887
// instruction itself should disable arguments object optimization.
888888
if(block->globOptData.argObjSyms && block->globOptData.IsArgumentsSymID(id))
889889
{
890-
CannotAllocateArgumentsObjectOnStack();
890+
CannotAllocateArgumentsObjectOnStack(nullptr);
891891
}
892892

893893
if (block->globOptData.liveVarSyms->Test(id))
@@ -1512,7 +1512,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
15121512

15131513
if (instr->m_func->GetJITFunctionBody()->GetInParamsCount() != 1 && !instr->m_func->IsStackArgsEnabled())
15141514
{
1515-
CannotAllocateArgumentsObjectOnStack();
1515+
CannotAllocateArgumentsObjectOnStack(instr->m_func);
15161516
}
15171517
else
15181518
{
@@ -1527,7 +1527,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
15271527
// In the debug mode, we don't want to optimize away the aliases. Since we may have to show them on the inspection.
15281528
if (((!AreFromSameBytecodeFunc(src1->AsRegOpnd(), dst->AsRegOpnd()) || this->currentBlock->loop) && instr->m_opcode != Js::OpCode::BytecodeArgOutCapture) || this->func->IsJitInDebugMode())
15291529
{
1530-
CannotAllocateArgumentsObjectOnStack();
1530+
CannotAllocateArgumentsObjectOnStack(instr->m_func);
15311531
return;
15321532
}
15331533
if(!dst->AsRegOpnd()->GetStackSym()->m_nonEscapingArgObjAlias)
@@ -1550,7 +1550,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
15501550
}
15511551

15521552
SymID id = 0;
1553-
1553+
15541554
switch(instr->m_opcode)
15551555
{
15561556
case Js::OpCode::LdElemI_A:
@@ -1561,7 +1561,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
15611561
if (indexOpnd && CurrentBlockData()->IsArgumentsSymID(indexOpnd->m_sym->m_id))
15621562
{
15631563
// Pathological test cases such as a[arguments]
1564-
CannotAllocateArgumentsObjectOnStack();
1564+
CannotAllocateArgumentsObjectOnStack(instr->m_func);
15651565
return;
15661566
}
15671567

@@ -1650,7 +1650,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
16501650
WritePerfHint(PerfHints::HeapArgumentsCreated, instr->m_func, instr->GetByteCodeOffset());
16511651
}
16521652
#endif
1653-
CannotAllocateArgumentsObjectOnStack();
1653+
CannotAllocateArgumentsObjectOnStack(instr->m_func);
16541654
return;
16551655
}
16561656
}
@@ -1668,7 +1668,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
16681668
WritePerfHint(PerfHints::HeapArgumentsCreated, instr->m_func, instr->GetByteCodeOffset());
16691669
}
16701670
#endif
1671-
CannotAllocateArgumentsObjectOnStack();
1671+
CannotAllocateArgumentsObjectOnStack(instr->m_func);
16721672
return;
16731673
}
16741674
}
@@ -1687,7 +1687,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
16871687
WritePerfHint(PerfHints::HeapArgumentsModification, instr->m_func, instr->GetByteCodeOffset());
16881688
}
16891689
#endif
1690-
CannotAllocateArgumentsObjectOnStack();
1690+
CannotAllocateArgumentsObjectOnStack(instr->m_func);
16911691
return;
16921692
}
16931693
}
@@ -1701,7 +1701,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
17011701
WritePerfHint(PerfHints::HeapArgumentsModification, instr->m_func, instr->GetByteCodeOffset());
17021702
}
17031703
#endif
1704-
CannotAllocateArgumentsObjectOnStack();
1704+
CannotAllocateArgumentsObjectOnStack(instr->m_func);
17051705
return;
17061706
}
17071707
CurrentBlockData()->ClearArgumentsSym(dst->AsRegOpnd());
@@ -2446,6 +2446,7 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved)
24462446
OptimizeChecks(instr);
24472447
OptArraySrc(&instr, &src1Val, &src2Val);
24482448
OptNewScObject(&instr, src1Val);
2449+
OptArgLenAndConst(instr, &src1Val);
24492450

24502451
instr = this->OptPeep(instr, src1Val, src2Val);
24512452

@@ -13196,6 +13197,69 @@ GlobOpt::OptArraySrc(IR::Instr ** const instrRef, Value ** src1Val, Value ** src
1319613197
arraySrcOpt.Optimize();
1319713198
}
1319813199

13200+
void
13201+
GlobOpt::OptArgLenAndConst(IR::Instr* instr, Value** src1Val)
13202+
{
13203+
if (instr->usesStackArgumentsObject && instr->IsInlined())
13204+
{
13205+
IR::Opnd* src1 = instr->GetSrc1();
13206+
auto replaceInstr = [&](IR::Opnd* newopnd)
13207+
{
13208+
this->CaptureByteCodeSymUses(instr);
13209+
instr->m_opcode = Js::OpCode::Ld_A;
13210+
instr->ReplaceSrc1(newopnd);
13211+
if (instr->HasBailOutInfo())
13212+
{
13213+
instr->ClearBailOutInfo();
13214+
}
13215+
*src1Val = this->OptSrc(instr->GetSrc1(), &instr);
13216+
instr->m_func->hasArgLenAndConstOpt = true;
13217+
};
13218+
Assert(CurrentBlockData()->IsArgumentsOpnd(src1));
13219+
switch(instr->m_opcode)
13220+
{
13221+
case Js::OpCode::LdLen_A:
13222+
{
13223+
IR::AddrOpnd* newopnd = IR::AddrOpnd::New(Js::TaggedInt::ToVarUnchecked(instr->m_func->actualCount - 1), IR::AddrOpndKindConstantVar, instr->m_func);
13224+
replaceInstr(newopnd);
13225+
break;
13226+
}
13227+
13228+
case Js::OpCode::LdElemI_A:
13229+
{
13230+
IR::IndirOpnd* indirOpndSrc1 = src1->AsIndirOpnd();
13231+
if (!indirOpndSrc1->GetIndexOpnd())
13232+
{
13233+
int argIndex = indirOpndSrc1->GetOffset() + 1;
13234+
IR::Instr* defInstr = nullptr;
13235+
IR::Instr* inlineeStart = instr->m_func->GetInlineeStart();
13236+
inlineeStart->IterateArgInstrs([&](IR::Instr* argInstr) {
13237+
StackSym *argSym = argInstr->GetDst()->AsSymOpnd()->m_sym->AsStackSym();
13238+
if (argSym->GetArgSlotNum() - 1 == argIndex)
13239+
{
13240+
defInstr = argInstr;
13241+
return true;
13242+
}
13243+
return false;
13244+
});
13245+
// If we cannot find the right instruction. I.E. When calling arguments[2] and no arguments were passed to the func
13246+
if (defInstr == nullptr)
13247+
{
13248+
IR::Opnd * undefined = IR::AddrOpnd::New(instr->m_func->GetScriptContextInfo()->GetUndefinedAddr(), IR::AddrOpndKindDynamicVar, instr->m_func, true);
13249+
undefined->SetValueType(ValueType::Undefined);
13250+
replaceInstr(undefined);
13251+
}
13252+
else
13253+
{
13254+
replaceInstr(defInstr->GetSrc1());
13255+
}
13256+
}
13257+
break;
13258+
}
13259+
}
13260+
}
13261+
}
13262+
1319913263
void
1320013264
GlobOpt::CaptureNoImplicitCallUses(
1320113265
IR::Opnd *opnd,
@@ -15722,16 +15786,23 @@ GlobOpt::TrackArgumentsObject()
1572215786
{
1572315787
if (PHASE_OFF(Js::StackArgOptPhase, this->func))
1572415788
{
15725-
this->CannotAllocateArgumentsObjectOnStack();
15789+
this->CannotAllocateArgumentsObjectOnStack(nullptr);
1572615790
return false;
1572715791
}
1572815792

1572915793
return func->GetHasStackArgs();
1573015794
}
1573115795

1573215796
void
15733-
GlobOpt::CannotAllocateArgumentsObjectOnStack()
15797+
GlobOpt::CannotAllocateArgumentsObjectOnStack(Func * curFunc)
1573415798
{
15799+
if (curFunc != nullptr && curFunc->hasArgLenAndConstOpt)
15800+
{
15801+
Assert(!curFunc->GetJITOutput()->GetOutputData()->disableStackArgOpt);
15802+
curFunc->GetJITOutput()->GetOutputData()->disableStackArgOpt = true;
15803+
throw Js::RejitException(RejitReason::DisableStackArgLenAndConstOpt);
15804+
}
15805+
1573515806
func->SetHasStackArgs(false);
1573615807

1573715808
#ifdef ENABLE_DEBUG_CONFIG_OPTIONS

lib/Backend/GlobOpt.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,7 @@ class GlobOpt
690690
IR::Instr* CreateBoundsCheckInstr(IR::Opnd* lowerBound, IR::Opnd* upperBound, int offset, IR::BailOutKind bailoutkind, BailOutInfo* bailoutInfo, Func* func);
691691
IR::Instr* AttachBoundsCheckData(IR::Instr* instr, IR::Opnd* lowerBound, IR::Opnd* upperBound, int offset);
692692
void OptArraySrc(IR::Instr **const instrRef, Value ** src1Val, Value ** src2Val);
693+
void OptArgLenAndConst(IR::Instr* instr, Value** src1Val);
693694

694695
private:
695696
void TrackIntSpecializedAddSubConstant(IR::Instr *const instr, const AddSubConstantInfo *const addSubConstantInfo, Value *const dstValue, const bool updateSourceBounds);
@@ -913,7 +914,7 @@ class GlobOpt
913914
void UpdateObjPtrValueType(IR::Opnd * opnd, IR::Instr * instr);
914915

915916
bool TrackArgumentsObject();
916-
void CannotAllocateArgumentsObjectOnStack();
917+
void CannotAllocateArgumentsObjectOnStack(Func * curFunc);
917918

918919
#if DBG
919920
bool IsPropertySymId(SymID symId) const;

lib/Backend/GlobOptBailOut.cpp

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

lib/Backend/GlobOptBlockData.cpp

+1-1
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

+3-1
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

+1
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)