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 18, 2016
1 parent e2d2fd8 commit 0f6820c
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 8 deletions.
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
28 changes: 27 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,23 @@ 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
{
if (m_isLeaf && !GetHasImplicitCalls()) return true;
return false;
}
RecyclerWeakReference<Js::FunctionBody> *GetWeakFuncRef() const;
Js::FunctionBody * GetJnFunction() const { return m_jnFunction; }

Expand Down Expand Up @@ -546,6 +568,7 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece;
bool hasBailout: 1;
bool hasBailoutInEHRegion : 1;
bool hasStackArgs: 1;
bool hasFunctionObjectLoad : 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,9 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece;
return isStackArgsEnabled;
}

bool GetHasFunctionObjectLoad() const { return this->hasFunctionObjectLoad; }
void SetHasFunctionObjectLoad() { this->hasFunctionObjectLoad = true; }

bool GetHasUnoptimizedArgumentsAcccess() const { return this->hasUnoptimizedArgumentsAcccess; }
void SetHasUnoptimizedArgumentsAccess(bool args)
{
Expand Down
1 change: 1 addition & 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->SetHasFunctionObjectLoad();
}
else
{
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
30 changes: 26 additions & 4 deletions lib/Backend/amd64/LowererMDArch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ const Js::OpCode LowererMD::MDExtend32Opcode = Js::OpCode::MOVSXD;

extern const IRType RegTypes[RegNumCount];

bool
LowererMDArch::IsArgSaveRequired(Func *func) {
return (func->IsJitInDebugMode() ||
func->GetJnFunction()->GetIsAsmjsMode() ||
func->IsGeneratorFunc() || func->IsLambda() ||
func->GetHasFunctionObjectLoad() ||
!func->IsTrueLeaf() || func->GetHasArgumentObject() ||
func->HasThis() || func->argInsCount > 0);
}

BYTE
LowererMDArch::GetDefaultIndirScale()
{
Expand Down Expand Up @@ -1379,7 +1389,7 @@ LowererMDArch::LowerEntryInstr(IR::EntryInstr * entryInstr)
//
uint32 argSlotsForFunctionsCalled = this->m_func->m_argSlotsForFunctionsCalled;
// Stack is always reserved for at least 4 parameters.
if (argSlotsForFunctionsCalled < 4)
if (argSlotsForFunctionsCalled < 4 && IsArgSaveRequired(this->m_func))
argSlotsForFunctionsCalled = 4;

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
1 change: 1 addition & 0 deletions lib/Backend/amd64/LowererMDArch.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class LowererMDArch

IR::Instr * LoadCheckedFloat(IR::RegOpnd *opndOrig, IR::RegOpnd *opndFloat, IR::LabelInstr *labelInline, IR::LabelInstr *labelHelper, IR::Instr *instrInsert, const bool checkForNullInLoopBody = false);

static bool IsArgSaveRequired(Func *func);
static BYTE GetDefaultIndirScale();
static RegNum GetRegShiftCount();
static RegNum GetRegReturn(IRType type);
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
2 changes: 1 addition & 1 deletion lib/Runtime/Language/InterpreterLoop.inl
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ Var Js::InterpreterStackFrame::INTERPRETERLOOPNAME()
}

Assert(this->returnAddress != nullptr);
AssertMsg(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 0f6820c

Please sign in to comment.