blob: 8123a70a9fa7981a56e0ee0cb3e8562625fcf05e [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
///---------------------------------------------------------------------------
///
/// class IRBuilder
///
/// To generate IR from the Jn bytecodes.
///
///---------------------------------------------------------------------------
class BranchReloc
{
public:
BranchReloc(IR::BranchInstr * instr, uint32 branchOffset, uint32 offs)
: branchInstr(instr), branchOffset(branchOffset), offset(offs), isNotBackEdge(false)
{ }
private:
IR::BranchInstr * branchInstr;
uint32 offset;
bool isNotBackEdge;
uint32 branchOffset;
public:
IR::BranchInstr * GetBranchInstr()
{
return this->branchInstr;
}
uint32 GetOffset() const
{
return this->offset;
}
uint32 GetBranchOffset() const
{
return this->branchOffset;
}
bool IsNotBackEdge() const
{
return this->isNotBackEdge;
}
void SetNotBackEdge()
{
this->isNotBackEdge = true;
}
};
class IRBuilder
{
friend struct IRBuilderSwitchAdapter;
public:
IRBuilder(Func * func)
: m_func(func)
, m_argsOnStack(0)
, m_loopBodyRetIPSym(nullptr)
, m_ldSlots(nullptr)
, m_loopCounterSym(nullptr)
, callTreeHasSomeProfileInfo(false)
, finallyBlockLevel(0)
, m_saveLoopImplicitCallFlags(nullptr)
, handlerOffsetStack(nullptr)
, m_switchAdapter(this)
, m_switchBuilder(&m_switchAdapter)
, m_stackFuncPtrSym(nullptr)
, m_loopBodyForInEnumeratorArrayOpnd(nullptr)
, m_paramScopeDone(false)
#if DBG
, m_callsOnStack(0)
, m_usedAsTemp(nullptr)
#endif
#ifdef BAILOUT_INJECTION
, seenLdStackArgPtr(false)
, expectApplyArg(false)
, seenProfiledBeginSwitch(false)
#endif
#ifdef BYTECODE_BRANCH_ISLAND
, longBranchMap(nullptr)
#endif
{
auto loopCount = func->GetJITFunctionBody()->GetLoopCount();
if (loopCount > 0) {
#if DBG
m_saveLoopImplicitCallFlags = AnewArrayZ(func->m_alloc, IR::Opnd*, loopCount);
#else
m_saveLoopImplicitCallFlags = AnewArray(func->m_alloc, IR::Opnd*, loopCount);
#endif
}
// Note: use original byte code without debugging probes, so that we don't jit BPs inserted by the user.
func->m_workItem->InitializeReader(&m_jnReader, &m_statementReader, func->m_alloc);
};
~IRBuilder()
{
Assert(m_func->GetJITFunctionBody()->GetLoopCount() == 0 || m_saveLoopImplicitCallFlags);
if (m_saveLoopImplicitCallFlags)
{
AdeleteArray(m_func->m_alloc, m_func->GetJITFunctionBody()->GetLoopCount(), m_saveLoopImplicitCallFlags);
}
}
void Build();
void InsertLabels();
IR::LabelInstr * CreateLabel(IR::BranchInstr * branchInstr, uint& offset);
private:
void InsertInstr(IR::Instr *instr, IR::Instr* insertBeforeInstr);
void AddInstr(IR::Instr *instr, uint32 offset);
BranchReloc * AddBranchInstr(IR::BranchInstr *instr, uint32 offset, uint32 targetOffset);
#ifdef BYTECODE_BRANCH_ISLAND
void ConsumeBranchIsland();
void EnsureConsumeBranchIsland();
uint ResolveVirtualLongBranch(IR::BranchInstr * branchInstr, uint offset);
#endif
BranchReloc * CreateRelocRecord(IR::BranchInstr * branchInstr, uint32 offset, uint32 targetOffset);
void BuildGeneratorPreamble();
void LoadNativeCodeData();
void BuildConstantLoads();
void BuildImplicitArgIns();
#define LAYOUT_TYPE(layout) \
void Build##layout(Js::OpCode newOpcode, uint32 offset);
#define LAYOUT_TYPE_WMS(layout) \
template <typename SizePolicy> void Build##layout(Js::OpCode newOpcode, uint32 offset);
#include "ByteCode/LayoutTypes.h"
void BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0);
void BuildReg2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0, Js::RegSlot R1, uint32 nextOffset);
void BuildProfiledReg2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot srcRegSlot, Js::ProfileId profileId);
void BuildReg3(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot,
Js::RegSlot src2RegSlot, Js::ProfileId profileId);
void BuildIsIn(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot, Js::RegSlot src2RegSlot, Js::ProfileId profileId);
void BuildReg3C(Js::OpCode newOpCode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot,
Js::RegSlot src2RegSlot, Js::CacheId inlineCacheIndex);
void BuildReg4(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot,
Js::RegSlot src2RegSlot, Js::RegSlot src3RegSlot);
void BuildReg2B1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot srcRegSlot, byte index);
void BuildReg3B1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot,
Js::RegSlot src2RegSlot, uint8 index);
void BuildReg5(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot,
Js::RegSlot src2RegSlot, Js::RegSlot src3RegSlot, Js::RegSlot src4RegSlot);
void BuildUnsigned1(Js::OpCode newOpcode, uint32 offset, uint32 C1);
void BuildReg1Unsigned1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0, int32 C1);
void BuildProfiledReg1Unsigned1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0, int32 C1, Js::ProfileId profileId);
void BuildReg2Int1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot srcRegSlot, int32 value);
void BuildElementC(Js::OpCode newOpcode, uint32 offset, Js::RegSlot fieldRegSlot, Js::RegSlot regSlot,
Js::PropertyIdIndexType propertyIdIndex);
void BuildElementScopedC(Js::OpCode newOpcode, uint32 offset, Js::RegSlot regSlot,
Js::PropertyIdIndexType propertyIdIndex);
void BuildElementSlot(Js::OpCode newOpcode, uint32 offset, Js::RegSlot fieldRegSlot, Js::RegSlot regSlot,
int32 slotId, Js::ProfileId profileId);
void BuildElementSlotI1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot regSlot,
int32 slotId, Js::ProfileId profileId);
void BuildElementSlotI2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot regSlot,
int32 slotId1, int32 slotId2, Js::ProfileId profileId);
void BuildElementSlotI3(Js::OpCode newOpcode, uint32 offset, Js::RegSlot fieldRegSlot, Js::RegSlot regSlot,
int32 slotId, Js::RegSlot homeObjLocation, Js::ProfileId profileId);
void BuildArgIn0(uint32 offset, Js::RegSlot R0);
void BuildArg(Js::OpCode newOpcode, uint32 offset, Js::ArgSlot argument, Js::RegSlot srcRegSlot);
void BuildArgIn(uint32 offset, Js::RegSlot dstRegSlot, uint16 argument);
void BuildArgInRest();
void BuildElementP(Js::OpCode newOpcode, uint32 offset, Js::RegSlot regSlot, Js::CacheId inlineCacheIndex);
void BuildElementCP(Js::OpCode newOpcode, uint32 offset, Js::RegSlot instance, Js::RegSlot regSlot, Js::CacheId inlineCacheIndex);
void BuildProfiledElementCP(Js::OpCode newOpcode, uint32 offset, Js::RegSlot instance, Js::RegSlot regSlot, Js::CacheId inlineCacheIndex, Js::ProfileId profileId);
void BuildElementC2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot instanceSlot, Js::RegSlot instance2Slot,
Js::RegSlot regSlot, Js::PropertyIdIndexType propertyIdIndex);
void BuildElementScopedC2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot instance2Slot,
Js::RegSlot regSlot, Js::PropertyIdIndexType propertyIdIndex);
void BuildElementU(Js::OpCode newOpcode, uint32 offset, Js::RegSlot instance, Js::PropertyIdIndexType propertyIdIndex);
void BuildElementI(Js::OpCode newOpcode, uint32 offset, Js::RegSlot baseRegSlot, Js::RegSlot indexRegSlot,
Js::RegSlot regSlot, Js::ProfileId profileId);
void BuildElementUnsigned1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot baseRegSlot, uint32 index, Js::RegSlot regSlot);
IR::Instr * BuildCallI_Helper(Js::OpCode newOpcode, uint32 offset, Js::RegSlot Return, Js::RegSlot Function, Js::ArgSlot ArgCount,
Js::ProfileId profileId, Js::CallFlags flags = Js::CallFlags_None, Js::InlineCacheIndex inlineCacheIndex = Js::Constants::NoInlineCacheIndex);
IR::Instr * BuildProfiledCallI(Js::OpCode opcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function,
Js::ArgSlot argCount, Js::ProfileId profileId, Js::CallFlags flags = Js::CallFlags_None, Js::InlineCacheIndex inlineCacheIndex = Js::Constants::NoInlineCacheIndex);
IR::Instr * BuildProfiledCallIExtended(Js::OpCode opcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function,
Js::ArgSlot argCount, Js::ProfileId profileId, Js::CallIExtendedOptions options, uint32 spreadAuxOffset, Js::CallFlags flags = Js::CallFlags_None);
IR::Instr * BuildProfiledCallIWithICIndex(Js::OpCode opcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function,
Js::ArgSlot argCount, Js::ProfileId profileId, Js::InlineCacheIndex inlineCacheIndex);
void BuildProfiledCallIExtendedFlags(Js::OpCode opcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function,
Js::ArgSlot argCount, Js::ProfileId profileId, Js::CallIExtendedOptions options, uint32 spreadAuxOffset);
void BuildProfiledCallIExtendedWithICIndex(Js::OpCode opcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function,
Js::ArgSlot argCount, Js::ProfileId profileId, Js::CallIExtendedOptions options, uint32 spreadAuxOffset);
void BuildProfiledCallIExtendedFlagsWithICIndex(Js::OpCode opcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function,
Js::ArgSlot argCount, Js::ProfileId profileId, Js::CallIExtendedOptions options, uint32 spreadAuxOffset);
void BuildProfiled2CallI(Js::OpCode opcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function,
Js::ArgSlot argCount, Js::ProfileId profileId, Js::ProfileId profileId2);
void BuildProfiled2CallIExtended(Js::OpCode opcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function,
Js::ArgSlot argCount, Js::ProfileId profileId, Js::ProfileId profileId2, Js::CallIExtendedOptions options, uint32 spreadAuxOffset);
void BuildLdSpreadIndices(uint32 offset, uint32 spreadAuxOffset);
IR::Instr * BuildCallIExtended(Js::OpCode newOpcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function,
Js::ArgSlot argCount, Js::CallIExtendedOptions options, uint32 spreadAuxOffset, Js::CallFlags flags = Js::CallFlags_None);
void BuildCallCommon(IR::Instr *instr, StackSym *symDst, Js::ArgSlot argCount, Js::CallFlags flags = Js::CallFlags_None);
void BuildRegexFromPattern(Js::RegSlot dstRegSlot, uint32 patternIndex, uint32 offset);
void BuildClass(Js::OpCode newOpcode, uint32 offset, Js::RegSlot constructor, Js::RegSlot extends);
void BuildBrReg1(Js::OpCode newOpcode, uint32 offset, uint targetOffset, Js::RegSlot srcRegSlot);
void BuildBrReg2(Js::OpCode newOpcode, uint32 offset, uint targetOffset, Js::RegSlot src1RegSlot, Js::RegSlot src2RegSlot);
void BuildBrBReturn(Js::OpCode newOpcode, uint32 offset, Js::RegSlot DestRegSlot, uint32 forInLoopLevel, uint32 targetOffset);
IR::IndirOpnd * BuildIndirOpnd(IR::RegOpnd *baseReg, IR::RegOpnd *indexReg);
IR::IndirOpnd * BuildIndirOpnd(IR::RegOpnd *baseReg, uint32 offset);
#if DBG_DUMP || defined(ENABLE_IR_VIEWER)
IR::IndirOpnd * BuildIndirOpnd(IR::RegOpnd *baseReg, uint32 offset, const char16 *desc);
#endif
IR::SymOpnd * BuildFieldOpnd(Js::OpCode newOpCode, Js::RegSlot reg, Js::PropertyId propertyId, Js::PropertyIdIndexType propertyIdIndex, PropertyKind propertyKind, uint inlineCacheIndex = -1);
PropertySym * BuildFieldSym(Js::RegSlot reg, Js::PropertyId propertyId, Js::PropertyIdIndexType propertyIdIndex, uint inlineCacheIndex, PropertyKind propertyKind);
SymID BuildSrcStackSymID(Js::RegSlot regSlot);
IR::RegOpnd * BuildDstOpnd(Js::RegSlot dstRegSlot, IRType type = TyVar, bool isCatchObjectSym = false);
IR::RegOpnd * BuildSrcOpnd(Js::RegSlot srcRegSlot, IRType type = TyVar);
IR::AddrOpnd * BuildAuxArrayOpnd(AuxArrayValue auxArrayType, uint32 auxArrayOffset);
IR::Opnd * BuildAuxObjectLiteralTypeRefOpnd(int objectId);
IR::Opnd * BuildForInEnumeratorOpnd(uint forInLoopLevel);
IR::RegOpnd * EnsureLoopBodyForInEnumeratorArrayOpnd();
private:
uint AddStatementBoundary(uint statementIndex, uint offset);
void CheckBuiltIn(PropertySym * propertySym, Js::BuiltinFunction *puBuiltInIndex);
bool IsFloatFunctionCallsite(Js::BuiltinFunction index, size_t argc);
IR::Instr * BuildProfiledFieldLoad(Js::OpCode loadOp, IR::RegOpnd *dstOpnd, IR::SymOpnd *srcOpnd, Js::CacheId inlineCacheIndex, bool *pUnprofiled);
IR::Instr * BuildProfiledSlotLoad(Js::OpCode loadOp, IR::RegOpnd *dstOpnd, IR::SymOpnd *srcOpnd, Js::ProfileId profileId, bool *pUnprofiled);
SymID GetMappedTemp(Js::RegSlot reg)
{
AssertMsg(this->RegIsTemp(reg), "Processing non-temp reg as a temp?");
AssertMsg(this->tempMap, "Processing non-temp reg without a temp map?");
Js::RegSlot tempIndex = reg - this->firstTemp;
AssertOrFailFast(tempIndex < m_func->GetJITFunctionBody()->GetTempCount());
return this->tempMap[tempIndex];
}
void SetMappedTemp(Js::RegSlot reg, SymID tempId)
{
AssertMsg(this->RegIsTemp(reg), "Processing non-temp reg as a temp?");
AssertMsg(this->tempMap, "Processing non-temp reg without a temp map?");
Js::RegSlot tempIndex = reg - this->firstTemp;
AssertOrFailFast(tempIndex < m_func->GetJITFunctionBody()->GetTempCount());
this->tempMap[tempIndex] = tempId;
}
BOOL GetTempUsed(Js::RegSlot reg)
{
AssertMsg(this->RegIsTemp(reg), "Processing non-temp reg as a temp?");
AssertMsg(this->fbvTempUsed, "Processing non-temp reg without a used BV?");
Js::RegSlot tempIndex = reg - this->firstTemp;
AssertOrFailFast(tempIndex < m_func->GetJITFunctionBody()->GetTempCount());
return this->fbvTempUsed->Test(tempIndex);
}
void SetTempUsed(Js::RegSlot reg, BOOL used)
{
AssertMsg(this->RegIsTemp(reg), "Processing non-temp reg as a temp?");
AssertMsg(this->fbvTempUsed, "Processing non-temp reg without a used BV?");
Js::RegSlot tempIndex = reg - this->firstTemp;
AssertOrFailFast(tempIndex < m_func->GetJITFunctionBody()->GetTempCount());
if (used)
{
this->fbvTempUsed->Set(tempIndex);
}
else
{
this->fbvTempUsed->Clear(tempIndex);
}
}
BOOL RegIsTemp(Js::RegSlot reg)
{
return reg >= this->firstTemp;
}
BOOL RegIsConstant(Js::RegSlot reg)
{
return reg > 0 && reg < m_func->GetJITFunctionBody()->GetConstCount();
}
bool IsParamScopeDone() const { return m_paramScopeDone; }
void SetParamScopeDone(bool done = true) { m_paramScopeDone = done; }
Js::RegSlot InnerScopeIndexToRegSlot(uint32) const;
Js::RegSlot GetEnvReg() const;
Js::RegSlot GetEnvRegForEvalCode() const;
Js::RegSlot GetEnvRegForInnerFrameDisplay() const;
void AddEnvOpndForInnerFrameDisplay(IR::Instr *instr, uint offset);
void EmitClosureRangeChecks();
void DoClosureRegCheck(Js::RegSlot reg);
void BuildInitCachedScope(int auxOffset, int offset);
void GenerateLoopBodySlotAccesses(uint offset);
void GenerateLoopBodyStSlots(SymID loopParamSymId, uint offset);
IR::Instr * GenerateLoopBodyStSlot(Js::RegSlot regSlot, uint offset = Js::Constants::NoByteCodeOffset);
bool IsLoopBody() const;
bool IsLoopBodyInTry() const;
uint GetLoopBodyExitInstrOffset() const;
IR::SymOpnd * BuildLoopBodySlotOpnd(SymID symId);
void EnsureLoopBodyLoadSlot(SymID symId, bool isCatchObjectSym = false);
void SetLoopBodyStSlot(SymID symID, bool isCatchObjectSym);
bool IsLoopBodyOuterOffset(uint offset) const;
bool IsLoopBodyReturnIPInstr(IR::Instr * instr) const;
IR::Opnd * InsertLoopBodyReturnIPInstr(uint targetOffset, uint offset);
IR::Instr * CreateLoopBodyReturnIPInstr(uint targetOffset, uint offset);
StackSym * EnsureStackFuncPtrSym();
void InsertBailOutForDebugger(uint offset, IR::BailOutKind kind, IR::Instr* insertBeforeInstr = nullptr);
void InsertBailOnNoProfile(uint offset);
void InsertBailOnNoProfile(IR::Instr *const insertBeforeInstr);
bool DoBailOnNoProfile();
void InsertIncrLoopBodyLoopCounter(IR::LabelInstr *loopTopLabelInstr);
void InsertInitLoopBodyLoopCounter(uint loopNum);
void InsertDoneLoopBodyLoopCounter(uint32 lastOffset);
IR::RegOpnd * InsertConvPrimStr(IR::RegOpnd * srcOpnd, uint offset, bool forcePreOpBailOutIfNeeded);
IR::Opnd * GetEnvironmentOperand(uint32 offset);
bool DoLoadInstructionArrayProfileInfo();
bool AllowNativeArrayProfileInfo();
#ifdef BAILOUT_INJECTION
void InjectBailOut(uint offset);
void CheckBailOutInjection(Js::OpCode opcode);
bool seenLdStackArgPtr;
bool expectApplyArg;
bool seenProfiledBeginSwitch;
#endif
JitArenaAllocator * m_tempAlloc;
JitArenaAllocator * m_funcAlloc;
Func * m_func;
IR::Instr * m_lastInstr;
IR::Instr ** m_offsetToInstruction;
uint32 m_offsetToInstructionCount;
uint32 m_functionStartOffset;
Js::ByteCodeReader m_jnReader;
Js::StatementReader<Js::FunctionBody::ArenaStatementMapList> m_statementReader;
SList<IR::Instr *> *m_argStack;
SList<BranchReloc*> *branchRelocList;
typedef Pair<uint, bool> handlerStackElementType;
SList<handlerStackElementType> *handlerOffsetStack;
SymID * tempMap;
BVFixed * fbvTempUsed;
Js::RegSlot firstTemp;
IRBuilderSwitchAdapter m_switchAdapter;
SwitchIRBuilder m_switchBuilder;
BVFixed * m_ldSlots;
BVFixed * m_stSlots;
#if DBG
BVFixed * m_usedAsTemp;
#endif
StackSym * m_loopBodyRetIPSym;
StackSym* m_loopCounterSym;
StackSym * m_stackFuncPtrSym;
bool m_paramScopeDone;
bool callTreeHasSomeProfileInfo;
uint finallyBlockLevel;
// Keep track of how many args we have on the stack whenever
// we make a call so that the max stack used over all calls can be
// used to estimate how much stack we should probe for at the
// beginning of a JITted function.
#if DBG
uint32 m_callsOnStack;
#endif
uint32 m_argsOnStack;
Js::PropertyId m_loopBodyLocalsStartSlot;
IR::Opnd** m_saveLoopImplicitCallFlags;
IR::RegOpnd * m_loopBodyForInEnumeratorArrayOpnd;
#ifdef BYTECODE_BRANCH_ISLAND
typedef JsUtil::BaseDictionary<uint32, uint32, JitArenaAllocator> LongBranchMap;
LongBranchMap * longBranchMap;
static IR::Instr * const VirtualLongBranchInstr;
#endif
};