Skip to content

Commit

Permalink
[MERGE #5702 @yullin-ms] Argument length and argument constant optimi…
Browse files Browse the repository at this point in the history
…zation

Merge pull request #5702 from yullin-ms:argLenAndConstOpt

Optimize LdLen_A argument length and LdElemI_A for the argument[constants]
  • Loading branch information
yullin-ms committed Sep 20, 2018
2 parents 565eee0 + 37feadb commit 4b72289
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 18 deletions.
6 changes: 4 additions & 2 deletions lib/Backend/BackwardPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7842,10 +7842,12 @@ BackwardPass::ProcessInlineeEnd(IR::Instr* instr)
}
if (this->tag == Js::BackwardPhase)
{
if (!GlobOpt::DoInlineArgsOpt(instr->m_func))
// Commenting out to allow for argument length and argument[constant] optimization
// Will revisit in phase two
/*if (!GlobOpt::DoInlineArgsOpt(instr->m_func))
{
return;
}
}*/

// This adds a use for function sym as part of InlineeStart & all the syms referenced by the args.
// It ensure they do not get cleared from the copy prop sym map.
Expand Down
7 changes: 7 additions & 0 deletions lib/Backend/Func.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Func::Func(JitArenaAllocator *alloc, JITTimeWorkItem * workItem,
m_bailoutReturnValueSym(nullptr),
m_hasBailedOutSym(nullptr),
m_inlineeFrameStartSym(nullptr),
inlineeStart(nullptr),
m_regsUsed(0),
m_fg(nullptr),
m_labelCount(0),
Expand Down Expand Up @@ -92,6 +93,7 @@ Func::Func(JitArenaAllocator *alloc, JITTimeWorkItem * workItem,
hasInlinee(false),
thisOrParentInlinerHasArguments(false),
hasStackArgs(false),
hasArgLenAndConstOpt(false),
hasImplicitParamLoad(false),
hasThrow(false),
hasNonSimpleParams(false),
Expand Down Expand Up @@ -301,8 +303,10 @@ Func::Codegen(JitArenaAllocator *alloc, JITTimeWorkItem * workItem,
Js::ScriptContextProfiler *const codeGenProfiler, const bool isBackgroundJIT)
{
bool rejit;
int rejitCounter = 0;
do
{
Assert(rejitCounter < 25);
Func func(alloc, workItem, threadContextInfo,
scriptContextInfo, outputData, epInfo, runtimeInfo,
polymorphicInlineCacheInfo, codeGenAllocators,
Expand Down Expand Up @@ -334,6 +338,8 @@ Func::Codegen(JitArenaAllocator *alloc, JITTimeWorkItem * workItem,
case RejitReason::DisableStackArgOpt:
outputData->disableStackArgOpt = TRUE;
break;
case RejitReason::DisableStackArgLenAndConstOpt:
break;
case RejitReason::DisableSwitchOptExpectingInteger:
case RejitReason::DisableSwitchOptExpectingString:
outputData->disableSwitchOpt = TRUE;
Expand All @@ -360,6 +366,7 @@ Func::Codegen(JitArenaAllocator *alloc, JITTimeWorkItem * workItem,
}

rejit = true;
rejitCounter++;
}
// Either the entry point has a reference to the number now, or we failed to code gen and we
// don't need to numbers, we can flush the completed page now.
Expand Down
13 changes: 13 additions & 0 deletions lib/Backend/Func.h
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,17 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece;
m_inlineeFrameStartSym = sym;
}

void SetInlineeStart(IR::Instr *inlineeStartInstr)
{
Assert(inlineeStart == nullptr);
inlineeStart = inlineeStartInstr;
}

IR::Instr* GetInlineeStart()
{
return inlineeStart;
}

IR::SymOpnd *GetInlineeArgCountSlotOpnd()
{
return GetInlineeOpndAtOffset(Js::Constants::InlineeMetaArgIndex_Argc * MachPtr);
Expand Down Expand Up @@ -721,6 +732,7 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece;
bool hasBailout: 1;
bool hasBailoutInEHRegion : 1;
bool hasStackArgs: 1;
bool hasArgLenAndConstOpt : 1;
bool hasImplicitParamLoad : 1; // True if there is a load of CallInfo, FunctionObject
bool hasThrow : 1;
bool hasUnoptimizedArgumentsAccess : 1; // True if there are any arguments access beyond the simple case of this.apply pattern
Expand Down Expand Up @@ -1030,6 +1042,7 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece;
Func * const topFunc;
Func * const parentFunc;
StackSym * m_inlineeFrameStartSym;
IR::Instr * inlineeStart;
uint maxInlineeArgOutSize;
const bool m_isBackgroundJIT;
bool hasInstrNumber;
Expand Down
95 changes: 83 additions & 12 deletions lib/Backend/GlobOpt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ GlobOpt::Optimize()

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

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

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

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

SymID id = 0;

switch(instr->m_opcode)
{
case Js::OpCode::LdElemI_A:
Expand All @@ -1561,7 +1561,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
if (indexOpnd && CurrentBlockData()->IsArgumentsSymID(indexOpnd->m_sym->m_id))
{
// Pathological test cases such as a[arguments]
CannotAllocateArgumentsObjectOnStack();
CannotAllocateArgumentsObjectOnStack(instr->m_func);
return;
}

Expand Down Expand Up @@ -1650,7 +1650,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
WritePerfHint(PerfHints::HeapArgumentsCreated, instr->m_func, instr->GetByteCodeOffset());
}
#endif
CannotAllocateArgumentsObjectOnStack();
CannotAllocateArgumentsObjectOnStack(instr->m_func);
return;
}
}
Expand All @@ -1668,7 +1668,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
WritePerfHint(PerfHints::HeapArgumentsCreated, instr->m_func, instr->GetByteCodeOffset());
}
#endif
CannotAllocateArgumentsObjectOnStack();
CannotAllocateArgumentsObjectOnStack(instr->m_func);
return;
}
}
Expand All @@ -1687,7 +1687,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
WritePerfHint(PerfHints::HeapArgumentsModification, instr->m_func, instr->GetByteCodeOffset());
}
#endif
CannotAllocateArgumentsObjectOnStack();
CannotAllocateArgumentsObjectOnStack(instr->m_func);
return;
}
}
Expand All @@ -1701,7 +1701,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
WritePerfHint(PerfHints::HeapArgumentsModification, instr->m_func, instr->GetByteCodeOffset());
}
#endif
CannotAllocateArgumentsObjectOnStack();
CannotAllocateArgumentsObjectOnStack(instr->m_func);
return;
}
CurrentBlockData()->ClearArgumentsSym(dst->AsRegOpnd());
Expand Down Expand Up @@ -2446,6 +2446,7 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved)
OptimizeChecks(instr);
OptArraySrc(&instr, &src1Val, &src2Val);
OptNewScObject(&instr, src1Val);
OptArgLenAndConst(instr, &src1Val);

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

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

void
GlobOpt::OptArgLenAndConst(IR::Instr* instr, Value** src1Val)
{
if (instr->usesStackArgumentsObject && instr->IsInlined())
{
IR::Opnd* src1 = instr->GetSrc1();
auto replaceInstr = [&](IR::Opnd* newopnd)
{
this->CaptureByteCodeSymUses(instr);
instr->m_opcode = Js::OpCode::Ld_A;
instr->ReplaceSrc1(newopnd);
if (instr->HasBailOutInfo())
{
instr->ClearBailOutInfo();
}
*src1Val = this->OptSrc(instr->GetSrc1(), &instr);
instr->m_func->hasArgLenAndConstOpt = true;
};
Assert(CurrentBlockData()->IsArgumentsOpnd(src1));
switch(instr->m_opcode)
{
case Js::OpCode::LdLen_A:
{
IR::AddrOpnd* newopnd = IR::AddrOpnd::New(Js::TaggedInt::ToVarUnchecked(instr->m_func->actualCount - 1), IR::AddrOpndKindConstantVar, instr->m_func);
replaceInstr(newopnd);
break;
}

case Js::OpCode::LdElemI_A:
{
IR::IndirOpnd* indirOpndSrc1 = src1->AsIndirOpnd();
if (!indirOpndSrc1->GetIndexOpnd())
{
int argIndex = indirOpndSrc1->GetOffset() + 1;
IR::Instr* defInstr = nullptr;
IR::Instr* inlineeStart = instr->m_func->GetInlineeStart();
inlineeStart->IterateArgInstrs([&](IR::Instr* argInstr) {
StackSym *argSym = argInstr->GetDst()->AsSymOpnd()->m_sym->AsStackSym();
if (argSym->GetArgSlotNum() - 1 == argIndex)
{
defInstr = argInstr;
return true;
}
return false;
});
// If we cannot find the right instruction. I.E. When calling arguments[2] and no arguments were passed to the func
if (defInstr == nullptr)
{
IR::Opnd * undefined = IR::AddrOpnd::New(instr->m_func->GetScriptContextInfo()->GetUndefinedAddr(), IR::AddrOpndKindDynamicVar, instr->m_func, true);
undefined->SetValueType(ValueType::Undefined);
replaceInstr(undefined);
}
else
{
replaceInstr(defInstr->GetSrc1());
}
}
break;
}
}
}
}

void
GlobOpt::CaptureNoImplicitCallUses(
IR::Opnd *opnd,
Expand Down Expand Up @@ -15722,16 +15786,23 @@ GlobOpt::TrackArgumentsObject()
{
if (PHASE_OFF(Js::StackArgOptPhase, this->func))
{
this->CannotAllocateArgumentsObjectOnStack();
this->CannotAllocateArgumentsObjectOnStack(nullptr);
return false;
}

return func->GetHasStackArgs();
}

void
GlobOpt::CannotAllocateArgumentsObjectOnStack()
GlobOpt::CannotAllocateArgumentsObjectOnStack(Func * curFunc)
{
if (curFunc != nullptr && curFunc->hasArgLenAndConstOpt)
{
Assert(!curFunc->GetJITOutput()->GetOutputData()->disableStackArgOpt);
curFunc->GetJITOutput()->GetOutputData()->disableStackArgOpt = true;
throw Js::RejitException(RejitReason::DisableStackArgLenAndConstOpt);
}

func->SetHasStackArgs(false);

#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
Expand Down
3 changes: 2 additions & 1 deletion lib/Backend/GlobOpt.h
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,7 @@ class GlobOpt
IR::Instr* CreateBoundsCheckInstr(IR::Opnd* lowerBound, IR::Opnd* upperBound, int offset, IR::BailOutKind bailoutkind, BailOutInfo* bailoutInfo, Func* func);
IR::Instr* AttachBoundsCheckData(IR::Instr* instr, IR::Opnd* lowerBound, IR::Opnd* upperBound, int offset);
void OptArraySrc(IR::Instr **const instrRef, Value ** src1Val, Value ** src2Val);
void OptArgLenAndConst(IR::Instr* instr, Value** src1Val);

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

bool TrackArgumentsObject();
void CannotAllocateArgumentsObjectOnStack();
void CannotAllocateArgumentsObjectOnStack(Func * curFunc);

#if DBG
bool IsPropertySymId(SymID symId) const;
Expand Down
2 changes: 1 addition & 1 deletion lib/Backend/GlobOptBailOut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,7 @@ void GlobOpt::EndTrackingOfArgObjSymsForInlinee()
// This means there are arguments object symbols in the current function which are not in the current block.
// This could happen when one of the blocks has a throw and arguments object aliased in it and other blocks don't see it.
// Rare case, abort stack arguments optimization in this case.
CannotAllocateArgumentsObjectOnStack();
CannotAllocateArgumentsObjectOnStack(this->currentBlock->globOptData.curFunc);
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion lib/Backend/GlobOptBlockData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@ GlobOptBlockData::MergeBlockData(
{
if (!this->argObjSyms->Equal(fromData->argObjSyms))
{
this->globOpt->CannotAllocateArgumentsObjectOnStack();
this->globOpt->CannotAllocateArgumentsObjectOnStack(nullptr);
}
}

Expand Down
4 changes: 3 additions & 1 deletion lib/Backend/Inline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1360,12 +1360,13 @@ void Inline::InsertOneInlinee(IR::Instr* callInstr, IR::RegOpnd* returnValueOpnd
Js::ArgSlot actualCount = MapActuals(currentCallInstr, argOuts, Js::InlineeCallInfo::MaxInlineeArgoutCount, inlinee, (Js::ProfileId)callInstr->AsProfiledInstr()->u.profileId, &stackArgsArgOutExpanded);
Assert(actualCount > 0);
MapFormals(inlinee, argOuts, funcBody->GetInParamsCount(), actualCount, returnValueOpnd, currentCallInstr->GetSrc1(), symCallerThis, stackArgsArgOutExpanded, fixedFunctionSafeThis, argOuts);
inlinee->SetInlineeStart(currentCallInstr);
currentCallInstr->m_func = inlinee;

// Put the meta arguments that the stack walker expects to find on the stack.
// As all the argouts are shared among the inlinees, do this only once.
SetupInlineeFrame(inlinee, currentCallInstr, actualCount, currentCallInstr->GetSrc1());

IR::Instr* inlineeEndInstr = IR::Instr::New(Js::OpCode::InlineeEnd, inlinee);
inlineeEndInstr->SetByteCodeOffset(inlinee->m_tailInstr->GetPrevRealInstr());
inlineeEndInstr->SetSrc1(IR::IntConstOpnd::New(actualCount + Js::Constants::InlineeMetaArgCount, TyInt32, inlinee));
Expand Down Expand Up @@ -3967,6 +3968,7 @@ Inline::InlineFunctionCommon(IR::Instr *callInstr, bool originalCallTargetOpndIs
callInstr->m_opcode = Js::OpCode::InlineeStart;

// Set it to belong to the inlinee, so that we can use the actual count when lowering InlineeStart
inlinee->SetInlineeStart(callInstr);
callInstr->m_func = inlinee;
callInstr->SetDst(IR::RegOpnd::New(returnValueOpnd ? returnValueOpnd->GetType() : TyVar, inlinee));
// Put the meta arguments that the stack walker expects to find on the stack.
Expand Down
1 change: 1 addition & 0 deletions lib/Common/Common/RejitReasons.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,5 @@ REJIT_REASON(ModByPowerOf2)
REJIT_REASON(NoProfile)
REJIT_REASON(PowIntIntTypeSpecDisabled)
REJIT_REASON(DisableStackArgOpt)
REJIT_REASON(DisableStackArgLenAndConstOpt)
REJIT_REASON(OptimizeTryFinallyDisabled)

0 comments on commit 4b72289

Please sign in to comment.