Skip to content

Commit

Permalink
Cleanup empty function and related functions
Browse files Browse the repository at this point in the history
This change does the following :

- Eliminates stack probe for leaf functions with small stack footprint

- Eliminates arg saves on stack for a leaf function with no arg usage

- Eliminate redundant null store on stack

-----------------------------------------------------------------------------
Empty function before :
-----------------------------------------------------------------------------
Function empty ( (chakra-core#1.1), chakra-core#2)                      Instr Count:38

                       FunctionEntry
    (rax).i64       =  MOV            0xXXXXXXXX (&StackLimit).u64
    (rax).i64       =  MOV            [(rax).i64].i64
    (rax).i64       =  ADD            (rax).i64, 0x000000001BD0.u64
                       JO             $L4
                       CMP            (rsp).i64, (rax).i64
                       JLE            $L4
                       NOP            4 (0x4).i8
                       NOP            2 (0x2).i8
    arg5(s8)<32>.i64 = MOV            (r9).i64
    arg4(s7)<24>.i64 = MOV            (r8).i64
    arg3(s6)<16>.i64 = MOV            (rdx).i64
    arg2(s5)<8>.i64 =  MOV            (rcx).i64
                       PrologStart
                       PUSH           (rbp).i64
    (rbp).i64       =  MOV            (rsp).i64
    (rsp).i64       =  SUB            (rsp).i64, 64 (0x40).i32
                       PrologEnd
    (rax).u32       =  XOR            (rax).u32, (rax).u32
    s4<-8>.i64      =  MOV            (rax).i64
    s3(rax).u64     =  MOV            0xXXXXXXXX (&CallCount).u64
                       CMP            [s3(rax).u64].u8, 255 (0xFF).u8
                       JEQ            $L3
    [s3(rax).u64].u8 = INC            [s3(rax).u64].u8
$L3:
    s0(rax)[Undefined].var = MOV      0xXXXXXXXX (undefined)[Undefined].var

  Line   7: }
  Col    1: ^
                       StatementBoundary  #0
                       StatementBoundary  #-1
    (rsp).i64       =  MOV            (rbp).i64
    (rbp).i64       =  POP
                       RET            0 (0x0).i32, (rax).i64
                       FunctionExit
$L4: [helper]
    (rdx).i64       =  MOV            0xXXXXXXXX (ScriptContext).u64
    (rcx).i64       =  MOV            0x000000001BD0.u64
    (rax).i64       =  MOV            ProbeCurrentStack.u64
                       JMP            (rax).i64
                       StatementBoundary  #-
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------

Empty function after :
-----------------------------------------------------------------------------
Function empty ( (chakra-core#1.1), chakra-core#2)                      Instr Count:18

                       FunctionEntry
                       PrologStart
                       PUSH           (rbp).i64
    (rbp).i64       =  MOV            (rsp).i64
    (rsp).i64       =  SUB            (rsp).i64, 32 (0x20).i32
                       PrologEnd
    s3(rax).u64     =  MOV            0xXXXXXXXX (&CallCount).u64
                       CMP            [s3(rax).u64].u8, 255 (0xFF).u8
                       JEQ            $L3
    [s3(rax).u64].u8 = INC            [s3(rax).u64].u8
$L3:
    s0(rax)[Undefined].var = MOV      0xXXXXXXXX (undefined)[Undefined].var

  Line   7: }
  Col    1: ^
                       StatementBoundary  #0
                       StatementBoundary  #-1
    (rsp).i64       =  MOV            (rbp).i64
    (rbp).i64       =  POP
                       RET            0 (0x0).i32, (rax).i64
                       FunctionExit
-----------------------------------------------------------------------------
  • Loading branch information
Meghana Gupta committed Jul 25, 2016
1 parent f5d756e commit d24e187
Show file tree
Hide file tree
Showing 15 changed files with 106 additions and 11 deletions.
4 changes: 3 additions & 1 deletion lib/Backend/DbCheckPostLower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,11 @@ DbCheckPostLower::Check()
Assert(instr->GetDst()->AsRegOpnd()->GetReg() != RegNOREG);
}
break;
case Js::OpCode::CALL:
Assert(!instr->m_func->IsTrueLeaf());
break;
}
#endif
break;
}
} NEXT_INSTR_IN_FUNC_EDITING;
}
Expand Down
8 changes: 6 additions & 2 deletions lib/Backend/Encoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,12 @@ Encoder::ShortenBranchesAndLabelAlign(BYTE **codeStart, ptrdiff_t *codeSize)
BYTE* buffEnd = buffStart + *codeSize;
ptrdiff_t newCodeSize = *codeSize;

RelocList* relocList = m_encoderMD.GetRelocList();

if (relocList == nullptr)
{
return false;
}

#if DBG
// Sanity check
Expand All @@ -661,8 +667,6 @@ Encoder::ShortenBranchesAndLabelAlign(BYTE **codeStart, ptrdiff_t *codeSize)
, &m_origPragmaInstrToRecordOffset
, &m_origOffsetBuffer );

RelocList* relocList = m_encoderMD.GetRelocList();
Assert(relocList != nullptr);
// Here we mark BRs to be shortened and adjust Labels and relocList entries offsets.
uint32 offsetBuffIndex = 0, pragmaInstToRecordOffsetIndex = 0, inlineeFrameRecordsIndex = 0, inlineeFrameMapIndex = 0;
int32 totalBytesSaved = 0;
Expand Down
2 changes: 2 additions & 0 deletions lib/Backend/Func.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ Func::Func(JitArenaAllocator *alloc, CodeGenWorkItem* workItem, const Js::Functi
hasInlinee(false),
thisOrParentInlinerHasArguments(false),
hasStackArgs(false),
hasImplicitParamLoad(false),
hasThrow(false),
hasNonSimpleParams(false),
hasUnoptimizedArgumentsAcccess(false),
hasApplyTargetInlining(false),
Expand Down
31 changes: 30 additions & 1 deletion lib/Backend/Func.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,12 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece;
Assert(this->m_jnFunction); // For now we always have a function body
return this->m_jnFunction->GetHasFinally();
}
bool HasThis() const
{
Assert(this->IsTopFunc());
Assert(this->m_jnFunction); // For now we always have a function body
return this->m_jnFunction->GetHasThis();
}
Js::ArgSlot GetInParamsCount() const
{
Assert(this->IsTopFunc());
Expand All @@ -275,7 +281,22 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece;
Assert(this->m_jnFunction); // For now we always have a function body
return this->m_jnFunction->GetIsGlobalFunc();
}

bool IsGeneratorFunc() const
{
Assert(this->IsTopFunc());
Assert(this->m_jnFunction); // For now we always have a function body
return this->m_jnFunction->IsGenerator();
}
bool IsLambda() const
{
Assert(this->IsTopFunc());
Assert(this->m_jnFunction); // For now we always have a function body
return this->m_jnFunction->IsLambda();
}
bool IsTrueLeaf() const
{
return !GetHasCalls() && !GetHasImplicitCalls();
}
RecyclerWeakReference<Js::FunctionBody> *GetWeakFuncRef() const;
Js::FunctionBody * GetJnFunction() const { return m_jnFunction; }

Expand Down Expand Up @@ -546,6 +567,8 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece;
bool hasBailout: 1;
bool hasBailoutInEHRegion : 1;
bool hasStackArgs: 1;
bool hasImplicitParamLoad : 1; // True if there is a load of CallInfo, FunctionObject
bool hasThrow : 1;
bool hasUnoptimizedArgumentsAcccess : 1; // True if there are any arguments access beyond the simple case of this.apply pattern
bool m_canDoInlineArgsOpt : 1;
bool hasApplyTargetInlining:1;
Expand Down Expand Up @@ -629,6 +652,12 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece;
return isStackArgsEnabled;
}

bool GetHasImplicitParamLoad() const { return this->hasImplicitParamLoad; }
void SetHasImplicitParamLoad() { this->hasImplicitParamLoad = true; }

bool GetHasThrow() const { return this->hasThrow; }
void SetHasThrow() { this->hasThrow = true; }

bool GetHasUnoptimizedArgumentsAcccess() const { return this->hasUnoptimizedArgumentsAcccess; }
void SetHasUnoptimizedArgumentsAccess(bool args)
{
Expand Down
3 changes: 3 additions & 0 deletions lib/Backend/IRBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1539,6 +1539,8 @@ IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0)

case Js::OpCode::Throw:
{
this->m_func->SetHasThrow();

srcOpnd = this->BuildSrcOpnd(srcRegOpnd);

if (this->catchOffsetStack && !this->catchOffsetStack->Empty())
Expand Down Expand Up @@ -7137,6 +7139,7 @@ IRBuilder::GenerateLoopBodySlotAccesses(uint offset)
StackSym *symSrc = StackSym::NewParamSlotSym(argument + 1, m_func);
symSrc->m_offset = (argument + LowererMD::GetFormalParamOffset()) * MachPtr;
symSrc->m_allocated = true;
m_func->SetHasImplicitParamLoad();
IR::SymOpnd *srcOpnd = IR::SymOpnd::New(symSrc, TyVar, m_func);

StackSym *loopParamSym = m_func->EnsureLoopParamSym();
Expand Down
1 change: 1 addition & 0 deletions lib/Backend/IRBuilderAsmJs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3186,6 +3186,7 @@ IRBuilderAsmJs::GenerateLoopBodySlotAccesses(uint offset)
StackSym *symSrc = StackSym::NewParamSlotSym(argument + 1, m_func);
symSrc->m_offset = (argument + LowererMD::GetFormalParamOffset()) * MachPtr;
symSrc->m_allocated = true;
m_func->SetHasImplicitParamLoad();
IR::SymOpnd *srcOpnd = IR::SymOpnd::New(symSrc, TyMachPtr, m_func);

StackSym *loopParamSym = m_func->EnsureLoopParamSym();
Expand Down
8 changes: 8 additions & 0 deletions lib/Backend/Lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10328,6 +10328,13 @@ Lowerer::HasSideEffects(IR::Instr *instr)
return instr->HasAnySideEffects();
}

bool Lowerer::IsArgSaveRequired(Func *func) {
return (!func->IsTrueLeaf() || func->IsJitInDebugMode() ||
// GetHasImplicitParamLoad covers generators, asmjs,
// and other javascript functions that implicitly read from the arg stack slots
func->GetHasThrow() || func->GetHasImplicitParamLoad() || func->HasThis() || func->argInsCount > 0);
}

IR::Instr*
Lowerer::GenerateFastInlineBuiltInMathRandom(IR::Instr* instr)
{
Expand Down Expand Up @@ -10639,6 +10646,7 @@ Lowerer::LoadCallInfo(IR::Instr * instrInsert)
IR::SymOpnd * generatorSymOpnd = IR::SymOpnd::New(generatorSym, TyMachPtr, func);
IR::RegOpnd * generatorRegOpnd = IR::RegOpnd::New(TyMachPtr, func);
LowererMD::CreateAssign(generatorRegOpnd, generatorSymOpnd, instrInsert);
func->SetHasImplicitParamLoad();

IR::IndirOpnd * indirOpnd = IR::IndirOpnd::New(generatorRegOpnd, Js::JavascriptGenerator::GetCallInfoOffset(), TyMachPtr, func);
IR::Instr * instr = LowererMD::CreateAssign(IR::RegOpnd::New(TyMachPtr, func), indirOpnd, instrInsert);
Expand Down
1 change: 1 addition & 0 deletions lib/Backend/Lower.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class Lowerer

StackSym * GetTempNumberSym(IR::Opnd * opnd, bool isTempTransferred);
static bool HasSideEffects(IR::Instr *instr);
static bool IsArgSaveRequired(Func *func);

#if DBG
static bool ValidOpcodeAfterLower(IR::Instr* instr, Func * func);
Expand Down
3 changes: 3 additions & 0 deletions lib/Backend/LowerMDShared.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2060,6 +2060,7 @@ LowererMD::LoadFunctionObjectOpnd(IR::Instr *instr, IR::Opnd *&functionObjOpnd)
instr->InsertBefore(mov1);
functionObjOpnd = mov1->GetDst()->AsRegOpnd();
instrPrev = mov1;
instr->m_func->SetHasImplicitParamLoad();
}
else
{
Expand Down Expand Up @@ -8050,6 +8051,7 @@ LowererMD::GetImplicitParamSlotSym(Js::ArgSlot argSlot, Func * func)

StackSym * stackSym = StackSym::NewParamSlotSym(argSlot, func);
func->SetArgOffset(stackSym, (2 + argSlot) * MachPtr);
func->SetHasImplicitParamLoad();
return stackSym;
}

Expand Down Expand Up @@ -8783,6 +8785,7 @@ void LowererMD::GenerateFastInlineBuiltInCall(IR::Instr* instr, IR::JnHelperMeth
IR::Instr *floatCall = IR::Instr::New(Js::OpCode::CALL, floatCallDst, s1, this->m_func);
instr->InsertBefore(floatCall);
#endif
instr->m_func->SetHasCalls();
// Save the result.
instr->m_opcode = Js::OpCode::MOVSD;
instr->SetSrc1(floatCall->GetDst());
Expand Down
9 changes: 9 additions & 0 deletions lib/Backend/amd64/EncoderMD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1533,6 +1533,10 @@ EncoderMD::FixMaps(uint32 brOffset, uint32 bytesSaved, uint32 *inlineeFrameRecor
void
EncoderMD::ApplyRelocs(size_t codeBufferAddress_)
{
if (m_relocList == nullptr)
{
return;
}

for (int32 i = 0; i < m_relocList->Count(); i++)
{
Expand Down Expand Up @@ -1601,6 +1605,11 @@ EncoderMD::VerifyRelocList(BYTE *buffStart, BYTE *buffEnd)
{
BYTE *last_pc = 0, *pc;

if (m_relocList == nullptr)
{
return;
}

for (int32 i = 0; i < m_relocList->Count(); i ++)
{
EncodeRelocAndLabels &p = m_relocList->Item(i);
Expand Down
34 changes: 28 additions & 6 deletions lib/Backend/amd64/LowererMDArch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,9 @@ LowererMDArch::LowerCall(IR::Instr * callInstr, uint32 argCount)
IR::Instr *retInstr = callInstr;
callInstr->m_opcode = Js::OpCode::CALL;

// This is required here due to calls create during lowering
callInstr->m_func->SetHasCalls();

if (callInstr->GetDst())
{
IR::Opnd * dstOpnd;
Expand Down Expand Up @@ -1378,9 +1381,16 @@ LowererMDArch::LowerEntryInstr(IR::EntryInstr * entryInstr)
// should always be 16 byte aligned.
//
uint32 argSlotsForFunctionsCalled = this->m_func->m_argSlotsForFunctionsCalled;
// Stack is always reserved for at least 4 parameters.
if (argSlotsForFunctionsCalled < 4)
argSlotsForFunctionsCalled = 4;

if (Lowerer::IsArgSaveRequired(this->m_func))
{
if (argSlotsForFunctionsCalled < 4)
argSlotsForFunctionsCalled = 4;
}
else
{
argSlotsForFunctionsCalled = 0;
}

uint32 stackArgsSize = MachPtr * (argSlotsForFunctionsCalled + 1);

Expand Down Expand Up @@ -1459,7 +1469,11 @@ LowererMDArch::LowerEntryInstr(IR::EntryInstr * entryInstr)
// Zero-initialize dedicated arguments slot.
IR::Instr *movRax0 = nullptr;
IR::Opnd *raxOpnd = nullptr;
if (this->m_func->HasArgumentSlot())

if (this->m_func->HasArgumentSlot() && (this->m_func->IsStackArgsEnabled() ||
this->m_func->IsJitInDebugMode() ||
// disabling apply inlining leads to explicit load from the zero-inited slot
this->m_func->GetJnFunction()->IsInlineApplyDisabled()))
{
// TODO: Support mov [rbp - n], IMM64
raxOpnd = IR::RegOpnd::New(nullptr, RegRAX, TyUint32, this->m_func);
Expand Down Expand Up @@ -1514,11 +1528,11 @@ LowererMDArch::LowerEntryInstr(IR::EntryInstr * entryInstr)
//
// Now store all the arguments in the register in the stack slots
//
this->MovArgFromReg2Stack(entryInstr, RegRCX, 1);
Js::AsmJsFunctionInfo* asmJsFuncInfo = m_func->GetJnFunction()->GetAsmJsFunctionInfoWithLock();
if (m_func->GetJnFunction()->GetIsAsmjsMode() && !m_func->IsLoopBody())
{
uint16 offset = 2;
this->MovArgFromReg2Stack(entryInstr, RegRCX, 1);
for (uint16 i = 0; i < asmJsFuncInfo->GetArgCount() && i < 3; i++)
{
switch (asmJsFuncInfo->GetArgType(i).which())
Expand Down Expand Up @@ -1585,8 +1599,9 @@ LowererMDArch::LowerEntryInstr(IR::EntryInstr * entryInstr)
}
}
}
else
else if (argSlotsForFunctionsCalled)
{
this->MovArgFromReg2Stack(entryInstr, RegRCX, 1);
this->MovArgFromReg2Stack(entryInstr, RegRDX, 2);
this->MovArgFromReg2Stack(entryInstr, RegR8, 3);
this->MovArgFromReg2Stack(entryInstr, RegR9, 4);
Expand Down Expand Up @@ -1643,6 +1658,13 @@ LowererMDArch::GeneratePrologueStackProbe(IR::Instr *entryInstr, IntConstType fr
// $done:
//

// Do not insert stack probe for leaf functions which have low stack footprint
if (this->m_func->IsTrueLeaf() &&
frameSize - Js::Constants::MinStackJIT < Js::Constants::MaxStackSizeForNoProbe)
{
return;
}

IR::LabelInstr *helperLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
IR::Instr *insertInstr = entryInstr->m_next;
IR::Instr *instr;
Expand Down
4 changes: 4 additions & 0 deletions lib/Backend/arm/LowerMD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,9 @@ LowererMD::LowerCall(IR::Instr * callInstr, Js::ArgSlot argCount)
IR::Opnd *targetOpnd = callInstr->GetSrc1();
AssertMsg(targetOpnd, "Call without a target?");

// This is required here due to calls created during lowering
callInstr->m_func->SetHasCalls();

if (targetOpnd->IsRegOpnd())
{
// Indirect call
Expand Down Expand Up @@ -7788,6 +7791,7 @@ LowererMD::GetImplicitParamSlotSym(Js::ArgSlot argSlot, Func * func)
// be confused with arg slot number from javascript
StackSym * stackSym = StackSym::NewParamSlotSym(argSlot, func);
func->SetArgOffset(stackSym, argSlot * MachPtr);
func->SetHasImplicitParamLoad();
return stackSym;
}

Expand Down
3 changes: 3 additions & 0 deletions lib/Backend/i386/LowererMDArch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1137,6 +1137,9 @@ LowererMDArch::LowerCall(IR::Instr * callInstr, uint32 argCount, RegNum regNum)
IR::Instr *retInstr = callInstr;
callInstr->m_opcode = Js::OpCode::CALL;

// This is required here due to calls created during lowering
callInstr->m_func->SetHasCalls();

if (callInstr->GetDst())
{
IR::Opnd * dstOpnd = callInstr->GetDst();
Expand Down
3 changes: 3 additions & 0 deletions lib/Runtime/Base/Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ namespace Js
// Minimum stack required to be able to execute a JITted Javascript function.
static const unsigned MinStackJIT = 0x930 * WIN64_STACK_FACTOR;

// Maximum stack space allowed to avoid generating ProbeCurrentStack
static const unsigned MaxStackSizeForNoProbe = 0x64;

#if (defined(_M_ARM32_OR_ARM64) || defined(_M_AMD64))
#if DBG
static const unsigned MinStackInterpreter = 0x4000; // 16 KB
Expand Down
3 changes: 2 additions & 1 deletion lib/Runtime/Language/InterpreterLoop.inl
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@ Var Js::InterpreterStackFrame::INTERPRETERLOOPNAME()
}

Assert(this->returnAddress != nullptr);
AssertMsg(m_arguments == NULL || Js::ArgumentsObject::Is(m_arguments), "Bad arguments!");
AssertMsg(!this->GetFunctionBody()->GetUsesArgumentsObject() || m_arguments == NULL || Js::ArgumentsObject::Is(m_arguments), "Bad arguments!");

// IP Passing in the interpreter:
// We keep a local copy of the bytecode's instruction pointer and
// pass it by reference to the bytecode reader.
Expand Down

0 comments on commit d24e187

Please sign in to comment.