| //------------------------------------------------------------------------------------------------------- |
| // Copyright (C) Microsoft. All rights reserved. |
| // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. |
| //------------------------------------------------------------------------------------------------------- |
| #include "Backend.h" |
| |
| #include "Library/ForInObjectEnumerator.h" |
| |
| #pragma prefast(push) |
| #pragma prefast(disable:28652, "Prefast complains that the OR are causing the compiler to emit dynamic initializers and the variable to be allocated in read/write mem...") |
| |
| static const IR::BailOutKind c_debuggerBailOutKindForCall = |
| IR::BailOutForceByFlag | IR::BailOutStackFrameBase | IR::BailOutBreakPointInFunction | IR::BailOutLocalValueChanged | IR::BailOutIgnoreException | IR::BailOutStep; |
| static const IR::BailOutKind c_debuggerBaseBailOutKindForHelper = IR::BailOutIgnoreException | IR::BailOutForceByFlag; |
| |
| #pragma prefast(pop) |
| |
| uint |
| IRBuilder::AddStatementBoundary(uint statementIndex, uint offset) |
| { |
| // Under debugger we use full stmt map, so that we know exactly start and end of each user stmt. |
| // We insert additional instrs in between statements, such as ProfiledLoopStart, for these bytecode reader acts as |
| // there is "unknown" stmt boundary with statementIndex == -1. Don't add stmt boundary for that as later |
| // it may cause issues, e.g. see WinBlue 218307. |
| if (!(statementIndex == Js::Constants::NoStatementIndex && this->m_func->IsJitInDebugMode())) |
| { |
| IR::PragmaInstr* pragmaInstr = IR::PragmaInstr::New(Js::OpCode::StatementBoundary, statementIndex, m_func); |
| this->AddInstr(pragmaInstr, offset); |
| } |
| |
| #ifdef BAILOUT_INJECTION |
| if (!this->m_func->IsOOPJIT()) |
| { |
| // Don't inject bailout if the function have trys |
| if (!this->m_func->GetTopFunc()->HasTry() && (statementIndex != Js::Constants::NoStatementIndex)) |
| { |
| if (Js::Configuration::Global.flags.IsEnabled(Js::BailOutFlag) && !this->m_func->GetJITFunctionBody()->IsLibraryCode()) |
| { |
| ULONG line; |
| LONG col; |
| |
| // Since we're on a separate thread, don't allow the line cache to be allocated in the Recycler. |
| if (((Js::FunctionBody*)m_func->GetJITFunctionBody()->GetAddr())->GetLineCharOffset(this->m_jnReader.GetCurrentOffset(), &line, &col, false /*canAllocateLineCache*/)) |
| { |
| line++; |
| if (Js::Configuration::Global.flags.BailOut.Contains(line, (uint32)col) || Js::Configuration::Global.flags.BailOut.Contains(line, (uint32)-1)) |
| { |
| this->InjectBailOut(offset); |
| } |
| } |
| } |
| else if (Js::Configuration::Global.flags.IsEnabled(Js::BailOutAtEveryLineFlag)) |
| { |
| this->InjectBailOut(offset); |
| } |
| } |
| } |
| #endif |
| return m_statementReader.MoveNextStatementBoundary(); |
| } |
| |
| // Add conditional bailout for breaking into interpreter debug thunk - for fast F12. |
| void |
| IRBuilder::InsertBailOutForDebugger(uint byteCodeOffset, IR::BailOutKind kind, IR::Instr* insertBeforeInstr /* default nullptr */) |
| { |
| Assert(m_func->IsJitInDebugMode()); |
| Assert(byteCodeOffset != Js::Constants::NoByteCodeOffset); |
| |
| BailOutInfo * bailOutInfo = JitAnew(m_func->m_alloc, BailOutInfo, byteCodeOffset, m_func); |
| IR::BailOutInstr * instr = IR::BailOutInstr::New(Js::OpCode::BailForDebugger, kind, bailOutInfo, bailOutInfo->bailOutFunc); |
| if (insertBeforeInstr) |
| { |
| InsertInstr(instr, insertBeforeInstr); |
| } |
| else |
| { |
| this->AddInstr(instr, m_lastInstr->GetByteCodeOffset()); |
| } |
| } |
| |
| bool |
| IRBuilder::DoBailOnNoProfile() |
| { |
| if (PHASE_OFF(Js::BailOnNoProfilePhase, this->m_func->GetTopFunc())) |
| { |
| return false; |
| } |
| |
| Func *const topFunc = m_func->GetTopFunc(); |
| if(topFunc->GetWorkItem()->GetProfiledIterations() == 0) |
| { |
| // The top function has not been profiled yet. Some switch must have been used to force jitting. This is not a |
| // real-world case, but for the purpose of testing the JIT, it's beneficial to generate code in unprofiled paths. |
| return false; |
| } |
| |
| if (m_func->HasProfileInfo() && m_func->GetReadOnlyProfileInfo()->IsNoProfileBailoutsDisabled()) |
| { |
| return false; |
| } |
| |
| if (!m_func->DoGlobOpt()) |
| { |
| return false; |
| } |
| |
| #ifdef ENABLE_DEBUG_CONFIG_OPTIONS |
| if (this->m_func->GetTopFunc() != this->m_func && Js::Configuration::Global.flags.IsEnabled(Js::ForceJITLoopBodyFlag)) |
| { |
| // No profile data for loop bodies with -force... |
| return false; |
| } |
| #endif |
| |
| if (!this->m_func->HasProfileInfo()) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void |
| IRBuilder::InsertBailOnNoProfile(uint offset) |
| { |
| Assert(DoBailOnNoProfile()); |
| |
| if (this->callTreeHasSomeProfileInfo) |
| { |
| return; |
| } |
| |
| IR::Instr *startCall = nullptr; |
| int count = 0; |
| FOREACH_SLIST_ENTRY(IR::Instr *, argInstr, this->m_argStack) |
| { |
| if (argInstr->m_opcode == Js::OpCode::StartCall) |
| { |
| startCall = argInstr; |
| count++; |
| if (count > 1) |
| { |
| return; |
| } |
| } |
| } NEXT_SLIST_ENTRY; |
| |
| AnalysisAssert(startCall); |
| |
| if (startCall->m_prev->m_opcode != Js::OpCode::BailOnNoProfile) |
| { |
| InsertBailOnNoProfile(startCall); |
| } |
| } |
| |
| void IRBuilder::InsertBailOnNoProfile(IR::Instr *const insertBeforeInstr) |
| { |
| Assert(DoBailOnNoProfile()); |
| |
| IR::Instr *const bailOnNoProfileInstr = IR::Instr::New(Js::OpCode::BailOnNoProfile, m_func); |
| InsertInstr(bailOnNoProfileInstr, insertBeforeInstr); |
| } |
| |
| #ifdef BAILOUT_INJECTION |
| void |
| IRBuilder::InjectBailOut(uint offset) |
| { |
| if(m_func->IsSimpleJit()) |
| { |
| return; // bailout injection is only applicable to full JIT |
| } |
| |
| IR::IntConstOpnd * opnd = IR::IntConstOpnd::New(0, TyInt32, m_func); |
| uint bailOutOffset = offset; |
| if (bailOutOffset == Js::Constants::NoByteCodeOffset) |
| { |
| bailOutOffset = m_lastInstr->GetByteCodeOffset(); |
| } |
| BailOutInfo * bailOutInfo = JitAnew(m_func->m_alloc, BailOutInfo, bailOutOffset, m_func); |
| IR::BailOutInstr * instr = IR::BailOutInstr::New(Js::OpCode::BailOnEqual, IR::BailOutInjected, bailOutInfo, bailOutInfo->bailOutFunc); |
| instr->SetSrc1(opnd); |
| instr->SetSrc2(opnd); |
| this->AddInstr(instr, offset); |
| } |
| |
| void |
| IRBuilder::CheckBailOutInjection(Js::OpCode opcode) |
| { |
| // Detect these sequence and disable Bailout injection between them: |
| // LdStackArgPtr |
| // LdArgCnt |
| // ApplyArgs |
| // This assumes that LdStackArgPtr, LdArgCnt and ApplyArgs can |
| // only be emitted in these sequence. This will need to be modified if it changes. |
| // |
| // Also insert a single bailout before the beginning of a switch case block for all the case labels. |
| switch(opcode) |
| { |
| case Js::OpCode::LdStackArgPtr: |
| Assert(!seenLdStackArgPtr); |
| Assert(!expectApplyArg); |
| seenLdStackArgPtr = true; |
| break; |
| case Js::OpCode::LdArgCnt: |
| Assert(seenLdStackArgPtr); |
| Assert(!expectApplyArg); |
| expectApplyArg = true; |
| break; |
| case Js::OpCode::ApplyArgs: |
| Assert(seenLdStackArgPtr); |
| Assert(expectApplyArg); |
| seenLdStackArgPtr = false; |
| expectApplyArg = false; |
| break; |
| |
| case Js::OpCode::BeginSwitch: |
| case Js::OpCode::ProfiledBeginSwitch: |
| Assert(!seenProfiledBeginSwitch); |
| seenProfiledBeginSwitch = true; |
| break; |
| case Js::OpCode::EndSwitch: |
| Assert(seenProfiledBeginSwitch); |
| seenProfiledBeginSwitch = false; |
| break; |
| |
| default: |
| Assert(!seenLdStackArgPtr); |
| Assert(!expectApplyArg); |
| break; |
| } |
| } |
| #endif |
| |
| bool |
| IRBuilder::IsLoopBody() const |
| { |
| return m_func->IsLoopBody(); |
| } |
| |
| bool |
| IRBuilder::IsLoopBodyInTry() const |
| { |
| return m_func->IsLoopBodyInTry(); |
| } |
| |
| bool |
| IRBuilder::IsLoopBodyReturnIPInstr(IR::Instr * instr) const |
| { |
| IR::Opnd * dst = instr->GetDst(); |
| return (dst && dst->IsRegOpnd() && dst->AsRegOpnd()->m_sym == m_loopBodyRetIPSym); |
| } |
| |
| |
| bool |
| IRBuilder::IsLoopBodyOuterOffset(uint offset) const |
| { |
| if (!IsLoopBody()) |
| { |
| return false; |
| } |
| |
| return (offset >= m_func->m_workItem->GetLoopHeader()->endOffset || offset < m_func->m_workItem->GetLoopHeader()->startOffset); |
| } |
| |
| uint |
| IRBuilder::GetLoopBodyExitInstrOffset() const |
| { |
| // End of loop body, start of StSlot and Ret instruction at endOffset + 1 |
| return m_func->m_workItem->GetLoopHeader()->endOffset + 1; |
| } |
| |
| Js::RegSlot |
| IRBuilder::GetEnvReg() const |
| { |
| return m_func->GetJITFunctionBody()->GetEnvReg(); |
| } |
| |
| Js::RegSlot |
| IRBuilder::GetEnvRegForInnerFrameDisplay() const |
| { |
| Js::RegSlot envReg = m_func->GetJITFunctionBody()->GetLocalFrameDisplayReg(); |
| if (envReg == Js::Constants::NoRegister) |
| { |
| envReg = this->GetEnvReg(); |
| } |
| |
| return envReg; |
| } |
| |
| void |
| IRBuilder::AddEnvOpndForInnerFrameDisplay(IR::Instr *instr, uint offset) |
| { |
| Js::RegSlot envReg = this->GetEnvRegForInnerFrameDisplay(); |
| if (envReg != Js::Constants::NoRegister) |
| { |
| IR::RegOpnd *src2Opnd; |
| if (envReg == m_func->GetJITFunctionBody()->GetLocalFrameDisplayReg() && |
| m_func->DoStackFrameDisplay() && |
| m_func->IsTopFunc()) |
| { |
| src2Opnd = IR::RegOpnd::New(TyVar, m_func); |
| this->AddInstr( |
| IR::Instr::New( |
| Js::OpCode::LdSlot, src2Opnd, |
| this->BuildFieldOpnd(Js::OpCode::LdSlot, m_func->GetLocalFrameDisplaySym()->m_id, 0, (Js::PropertyIdIndexType)-1, PropertyKindSlots), |
| m_func), |
| offset); |
| } |
| else |
| { |
| src2Opnd = this->BuildSrcOpnd(envReg); |
| } |
| instr->SetSrc2(src2Opnd); |
| } |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::Build |
| /// |
| /// IRBuilder main entry point. Read the bytecode for this function and |
| /// generate IR. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| void |
| IRBuilder::Build() |
| { |
| m_funcAlloc = m_func->m_alloc; |
| |
| |
| NoRecoverMemoryJitArenaAllocator localAlloc(_u("BE-IRBuilder"), m_funcAlloc->GetPageAllocator(), Js::Throw::OutOfMemory); |
| m_tempAlloc = &localAlloc; |
| |
| uint32 offset; |
| uint32 statementIndex = m_statementReader.GetStatementIndex(); |
| |
| m_argStack = JitAnew(m_tempAlloc, SList<IR::Instr *>, m_tempAlloc); |
| |
| this->branchRelocList = JitAnew(m_tempAlloc, SList<BranchReloc *>, m_tempAlloc); |
| Func * topFunc = this->m_func->GetTopFunc(); |
| if (topFunc->HasTry() && |
| ((!topFunc->IsLoopBody() && !PHASE_OFF(Js::OptimizeTryCatchPhase, topFunc)) || |
| (topFunc->HasFinally() && !topFunc->IsLoopBody() && !PHASE_OFF(Js::OptimizeTryFinallyPhase, topFunc)) || |
| (topFunc->IsSimpleJit() && topFunc->GetJITFunctionBody()->DoJITLoopBody()) || // should be relaxed as more bailouts are added in Simple Jit |
| topFunc->IsLoopBodyInTryFinally())) // We need accurate flow when we are full jitting loop bodies which have try finally |
| { |
| this->handlerOffsetStack = JitAnew(m_tempAlloc, SList<handlerStackElementType>, m_tempAlloc); |
| } |
| |
| this->firstTemp = m_func->GetJITFunctionBody()->GetFirstTmpReg(); |
| Js::RegSlot tempCount = m_func->GetJITFunctionBody()->GetTempCount(); |
| if (tempCount > 0) |
| { |
| this->tempMap = AnewArrayZ(m_tempAlloc, SymID, tempCount); |
| this->fbvTempUsed = BVFixed::New<JitArenaAllocator>(tempCount, m_tempAlloc); |
| } |
| else |
| { |
| this->tempMap = nullptr; |
| this->fbvTempUsed = nullptr; |
| } |
| |
| m_func->m_headInstr = IR::EntryInstr::New(Js::OpCode::FunctionEntry, m_func); |
| m_func->m_exitInstr = IR::ExitInstr::New(Js::OpCode::FunctionExit, m_func); |
| m_func->m_tailInstr = m_func->m_exitInstr; |
| m_func->m_headInstr->InsertAfter(m_func->m_tailInstr); |
| |
| if (m_func->GetJITFunctionBody()->IsParamAndBodyScopeMerged() || this->IsLoopBody()) |
| { |
| this->SetParamScopeDone(); |
| } |
| |
| if (m_func->GetJITFunctionBody()->GetLocalClosureReg() != Js::Constants::NoRegister) |
| { |
| m_func->InitLocalClosureSyms(); |
| } |
| |
| m_functionStartOffset = m_jnReader.GetCurrentOffset(); |
| m_lastInstr = m_func->m_headInstr; |
| |
| AssertMsg(sizeof(SymID) >= sizeof(Js::RegSlot), "sizeof(SymID) != sizeof(Js::RegSlot)!!"); |
| |
| // Skip the last EndOfBlock opcode |
| Assert(!OpCodeAttr::HasMultiSizeLayout(Js::OpCode::EndOfBlock)); |
| uint32 lastOffset = m_func->GetJITFunctionBody()->GetByteCodeLength() - Js::OpCodeUtil::EncodedSize(Js::OpCode::EndOfBlock, Js::SmallLayout); |
| uint32 offsetToInstructionCount = lastOffset; |
| if (this->IsLoopBody()) |
| { |
| // LdSlot needs to cover all the register, including the temps, because we might treat |
| // those as if they are local for the value of the with statement |
| this->m_ldSlots = BVFixed::New<JitArenaAllocator>(m_func->GetJITFunctionBody()->GetLocalsCount(), m_tempAlloc); |
| this->m_stSlots = BVFixed::New<JitArenaAllocator>(m_func->GetJITFunctionBody()->GetFirstTmpReg(), m_tempAlloc); |
| this->m_loopBodyRetIPSym = StackSym::New(TyMachReg, this->m_func); |
| #if DBG |
| if (m_func->GetJITFunctionBody()->GetTempCount() != 0) |
| { |
| this->m_usedAsTemp = BVFixed::New<JitArenaAllocator>(m_func->GetJITFunctionBody()->GetTempCount(), m_tempAlloc); |
| } |
| #endif |
| |
| lastOffset = m_func->m_workItem->GetLoopHeader()->endOffset; |
| AssertOrFailFast(lastOffset < m_func->GetJITFunctionBody()->GetByteCodeLength()); |
| // Ret is created at lastOffset + 1, so we need lastOffset + 2 entries |
| offsetToInstructionCount = lastOffset + 2; |
| |
| // Compute the offset of the start of the locals array as a Var index. |
| size_t localsOffset = Js::InterpreterStackFrame::GetOffsetOfLocals(); |
| Assert(localsOffset % sizeof(Js::Var) == 0); |
| this->m_loopBodyLocalsStartSlot = (Js::PropertyId)(localsOffset / sizeof(Js::Var)); |
| } |
| |
| m_offsetToInstructionCount = offsetToInstructionCount; |
| m_offsetToInstruction = JitAnewArrayZ(m_tempAlloc, IR::Instr *, offsetToInstructionCount); |
| |
| #ifdef BYTECODE_BRANCH_ISLAND |
| longBranchMap = JitAnew(m_tempAlloc, LongBranchMap, m_tempAlloc); |
| #endif |
| |
| m_switchBuilder.Init(m_func, m_tempAlloc, false); |
| |
| this->LoadNativeCodeData(); |
| |
| this->BuildConstantLoads(); |
| this->BuildGeneratorPreamble(); |
| |
| if (!this->IsLoopBody() && m_func->GetJITFunctionBody()->HasImplicitArgIns()) |
| { |
| this->BuildImplicitArgIns(); |
| } |
| |
| if (!this->IsLoopBody() && m_func->GetJITFunctionBody()->HasRestParameter()) |
| { |
| this->BuildArgInRest(); |
| } |
| |
| if (m_func->IsJitInDebugMode()) |
| { |
| // This is first bailout in the function, the locals at stack have not initialized to undefined, so do not restore them. |
| this->InsertBailOutForDebugger(m_functionStartOffset, IR::BailOutForceByFlag | IR::BailOutBreakPointInFunction | IR::BailOutStep, nullptr); |
| } |
| |
| #ifdef BAILOUT_INJECTION |
| // Start bailout inject after the constant and arg load. We don't bailout before that |
| IR::Instr * lastInstr = m_lastInstr; |
| #endif |
| |
| offset = Js::Constants::NoByteCodeOffset; |
| if (!this->IsLoopBody()) |
| { |
| IR::Instr *instr; |
| |
| // Do the implicit operations LdEnv, NewScopeSlots, LdFrameDisplay, as indicated by function body attributes. |
| Js::RegSlot envReg = m_func->GetJITFunctionBody()->GetEnvReg(); |
| if (envReg != Js::Constants::NoRegister && !this->RegIsConstant(envReg)) |
| { |
| Js::OpCode newOpcode; |
| Js::RegSlot thisReg = m_func->GetJITFunctionBody()->GetThisRegForEventHandler(); |
| IR::RegOpnd *srcOpnd = nullptr; |
| IR::RegOpnd *dstOpnd = nullptr; |
| if (thisReg != Js::Constants::NoRegister) |
| { |
| this->BuildArgIn0(offset, thisReg); |
| |
| srcOpnd = BuildSrcOpnd(thisReg); |
| newOpcode = Js::OpCode::LdHandlerScope; |
| } |
| else |
| { |
| newOpcode = Js::OpCode::LdEnv; |
| } |
| dstOpnd = BuildDstOpnd(envReg); |
| instr = IR::Instr::New(newOpcode, dstOpnd, m_func); |
| if (srcOpnd) |
| { |
| instr->SetSrc1(srcOpnd); |
| } |
| if (dstOpnd->m_sym->m_isSingleDef) |
| { |
| dstOpnd->m_sym->m_isNotNumber = true; |
| } |
| this->AddInstr(instr, offset); |
| } |
| |
| Js::RegSlot funcExprScopeReg = m_func->GetJITFunctionBody()->GetFuncExprScopeReg(); |
| IR::RegOpnd *frameDisplayOpnd = nullptr; |
| if (funcExprScopeReg != Js::Constants::NoRegister) |
| { |
| IR::RegOpnd *funcExprScopeOpnd = BuildDstOpnd(funcExprScopeReg); |
| instr = IR::Instr::New(Js::OpCode::NewPseudoScope, funcExprScopeOpnd, m_func); |
| this->AddInstr(instr, offset); |
| } |
| |
| Js::RegSlot closureReg = m_func->GetJITFunctionBody()->GetLocalClosureReg(); |
| IR::RegOpnd *closureOpnd = nullptr; |
| if (closureReg != Js::Constants::NoRegister) |
| { |
| Assert(!this->RegIsConstant(closureReg)); |
| if (m_func->DoStackScopeSlots()) |
| { |
| closureOpnd = IR::RegOpnd::New(TyVar, m_func); |
| } |
| else |
| { |
| closureOpnd = this->BuildDstOpnd(closureReg); |
| } |
| if (m_func->GetJITFunctionBody()->HasScopeObject()) |
| { |
| if (m_func->GetJITFunctionBody()->HasCachedScopePropIds()) |
| { |
| this->BuildInitCachedScope(0, offset); |
| } |
| else |
| { |
| instr = IR::Instr::New(Js::OpCode::NewScopeObject, closureOpnd, m_func); |
| this->AddInstr(instr, offset); |
| } |
| } |
| else |
| { |
| Js::OpCode op = |
| m_func->DoStackScopeSlots() ? Js::OpCode::NewStackScopeSlots : Js::OpCode::NewScopeSlots; |
| |
| uint size = m_func->GetJITFunctionBody()->IsParamAndBodyScopeMerged() ? m_func->GetJITFunctionBody()->GetScopeSlotArraySize() : m_func->GetJITFunctionBody()->GetParamScopeSlotArraySize(); |
| IR::Opnd * srcOpnd = IR::IntConstOpnd::New(size + Js::ScopeSlots::FirstSlotIndex, TyUint32, m_func); |
| instr = IR::Instr::New(op, closureOpnd, srcOpnd, m_func); |
| this->AddInstr(instr, offset); |
| } |
| if (closureOpnd->m_sym->m_isSingleDef) |
| { |
| closureOpnd->m_sym->m_isNotNumber = true; |
| } |
| |
| if (m_func->DoStackScopeSlots()) |
| { |
| // Init the stack closure sym and use it to save the scope slot pointer. |
| this->AddInstr( |
| IR::Instr::New( |
| Js::OpCode::InitLocalClosure, this->BuildDstOpnd(m_func->GetLocalClosureSym()->m_id), m_func), |
| offset); |
| |
| this->AddInstr( |
| IR::Instr::New( |
| Js::OpCode::StSlot, |
| this->BuildFieldOpnd( |
| Js::OpCode::StSlot, m_func->GetLocalClosureSym()->m_id, 0, (Js::PropertyIdIndexType)-1, PropertyKindSlots), |
| closureOpnd, m_func), |
| offset); |
| } |
| } |
| |
| Js::RegSlot frameDisplayReg = m_func->GetJITFunctionBody()->GetLocalFrameDisplayReg(); |
| if (frameDisplayReg != Js::Constants::NoRegister) |
| { |
| Assert(!this->RegIsConstant(frameDisplayReg)); |
| |
| Js::OpCode op = m_func->DoStackScopeSlots() ? Js::OpCode::NewStackFrameDisplay : Js::OpCode::LdFrameDisplay; |
| if (funcExprScopeReg != Js::Constants::NoRegister) |
| { |
| // Insert the function expression scope ahead of any enclosing scopes. |
| IR::RegOpnd * funcExprScopeOpnd = BuildSrcOpnd(funcExprScopeReg); |
| frameDisplayOpnd = closureReg != Js::Constants::NoRegister ? IR::RegOpnd::New(TyVar, m_func) : BuildDstOpnd(frameDisplayReg); |
| instr = IR::Instr::New(Js::OpCode::LdFrameDisplay, frameDisplayOpnd, funcExprScopeOpnd, m_func); |
| if (envReg != Js::Constants::NoRegister) |
| { |
| instr->SetSrc2(this->BuildSrcOpnd(envReg)); |
| } |
| this->AddInstr(instr, (uint)-1); |
| } |
| |
| if (closureReg != Js::Constants::NoRegister) |
| { |
| IR::RegOpnd *dstOpnd; |
| if (m_func->DoStackScopeSlots() && m_func->IsTopFunc()) |
| { |
| dstOpnd = IR::RegOpnd::New(TyVar, m_func); |
| } |
| else |
| { |
| dstOpnd = this->BuildDstOpnd(frameDisplayReg); |
| } |
| instr = IR::Instr::New(op, dstOpnd, closureOpnd, m_func); |
| if (frameDisplayOpnd != nullptr) |
| { |
| // We're building on an intermediate LdFrameDisplay result. |
| instr->SetSrc2(frameDisplayOpnd); |
| } |
| else if (envReg != Js::Constants::NoRegister) |
| { |
| // We're building on the environment created by the enclosing function. |
| instr->SetSrc2(this->BuildSrcOpnd(envReg)); |
| } |
| this->AddInstr(instr, offset); |
| if (dstOpnd->m_sym->m_isSingleDef) |
| { |
| dstOpnd->m_sym->m_isNotNumber = true; |
| } |
| |
| if (m_func->DoStackFrameDisplay()) |
| { |
| // Use the stack closure sym to save the frame display pointer. |
| this->AddInstr( |
| IR::Instr::New( |
| Js::OpCode::InitLocalClosure, this->BuildDstOpnd(m_func->GetLocalFrameDisplaySym()->m_id), m_func), |
| offset); |
| |
| this->AddInstr( |
| IR::Instr::New( |
| Js::OpCode::StSlot, |
| this->BuildFieldOpnd(Js::OpCode::StSlot, m_func->GetLocalFrameDisplaySym()->m_id, 0, (Js::PropertyIdIndexType)-1, PropertyKindSlots), |
| dstOpnd, m_func), |
| offset); |
| } |
| } |
| } |
| } |
| |
| offset = m_functionStartOffset; |
| if (m_statementReader.AtStatementBoundary(&m_jnReader)) |
| { |
| statementIndex = this->AddStatementBoundary(statementIndex, offset); |
| } |
| |
| // For label instr we can add bailout only after all labels were finalized. Put to list/add in the end. |
| JsUtil::BaseDictionary<IR::Instr*, int, JitArenaAllocator> ignoreExBranchInstrToOffsetMap(m_tempAlloc); |
| |
| Js::LayoutSize layoutSize; |
| IR::Instr* lastProcessedInstrForJITLoopBody = m_func->m_headInstr; |
| for (Js::OpCode newOpcode = m_jnReader.ReadOp(layoutSize); (uint)m_jnReader.GetCurrentOffset() <= lastOffset; newOpcode = m_jnReader.ReadOp(layoutSize)) |
| { |
| Assert(newOpcode != Js::OpCode::EndOfBlock); |
| |
| #ifdef BAILOUT_INJECTION |
| if (!this->m_func->GetTopFunc()->HasTry() |
| #ifdef BYTECODE_BRANCH_ISLAND |
| && newOpcode != Js::OpCode::BrLong // Don't inject bailout on BrLong as they are just redirecting to a different offset anyways |
| #endif |
| ) |
| { |
| if (!this->m_func->IsOOPJIT()) |
| { |
| if (!seenLdStackArgPtr && !seenProfiledBeginSwitch) |
| { |
| if (Js::Configuration::Global.flags.IsEnabled(Js::BailOutByteCodeFlag)) |
| { |
| ThreadContext * threadContext = this->m_func->GetScriptContext()->GetThreadContext(); |
| if (Js::Configuration::Global.flags.BailOutByteCode.Contains(threadContext->bailOutByteCodeLocationCount)) |
| { |
| this->InjectBailOut(offset); |
| } |
| } |
| else if (Js::Configuration::Global.flags.IsEnabled(Js::BailOutAtEveryByteCodeFlag)) |
| { |
| this->InjectBailOut(offset); |
| } |
| } |
| |
| CheckBailOutInjection(newOpcode); |
| } |
| } |
| #endif |
| AssertOrFailFastMsg(Js::OpCodeUtil::IsValidByteCodeOpcode(newOpcode), "Error getting opcode from m_jnReader.Op()"); |
| |
| uint layoutAndSize = layoutSize * Js::OpLayoutType::Count + Js::OpCodeUtil::GetOpCodeLayout(newOpcode); |
| switch(layoutAndSize) |
| { |
| #define LAYOUT_TYPE(layout) \ |
| case Js::OpLayoutType::layout: \ |
| Assert(layoutSize == Js::SmallLayout); \ |
| this->Build##layout(newOpcode, offset); \ |
| break; |
| #define LAYOUT_TYPE_WMS(layout) \ |
| case Js::SmallLayout * Js::OpLayoutType::Count + Js::OpLayoutType::layout: \ |
| this->Build##layout<Js::SmallLayoutSizePolicy>(newOpcode, offset); \ |
| break; \ |
| case Js::MediumLayout * Js::OpLayoutType::Count + Js::OpLayoutType::layout: \ |
| this->Build##layout<Js::MediumLayoutSizePolicy>(newOpcode, offset); \ |
| break; \ |
| case Js::LargeLayout * Js::OpLayoutType::Count + Js::OpLayoutType::layout: \ |
| this->Build##layout<Js::LargeLayoutSizePolicy>(newOpcode, offset); \ |
| break; |
| #include "ByteCode/LayoutTypes.h" |
| |
| default: |
| AssertMsg(0, "Unimplemented layout"); |
| break; |
| } |
| |
| #ifdef BAILOUT_INJECTION |
| if (!this->m_func->IsOOPJIT()) |
| { |
| if (!this->m_func->GetTopFunc()->HasTry() && Js::Configuration::Global.flags.IsEnabled(Js::BailOutByteCodeFlag)) |
| { |
| ThreadContext * threadContext = this->m_func->GetScriptContext()->GetThreadContext(); |
| if (lastInstr != m_lastInstr) |
| { |
| lastInstr = lastInstr->GetNextRealInstr(); |
| if (lastInstr->HasBailOutInfo()) |
| { |
| lastInstr = lastInstr->m_next; |
| } |
| lastInstr->bailOutByteCodeLocation = threadContext->bailOutByteCodeLocationCount; |
| lastInstr = m_lastInstr; |
| } |
| threadContext->bailOutByteCodeLocationCount++; |
| } |
| } |
| #endif |
| |
| if (IsLoopBodyInTry() && lastProcessedInstrForJITLoopBody != m_lastInstr) |
| { |
| // traverse in backward so we get new/later value of given symId for storing instead of the earlier/stale |
| // symId value. m_stSlots is used to prevent multiple stores to the same symId. |
| FOREACH_INSTR_BACKWARD_EDITING_IN_RANGE( |
| instr, |
| instrPrev, |
| m_lastInstr, |
| lastProcessedInstrForJITLoopBody->m_next) |
| { |
| if (instr->GetDst() && instr->GetDst()->IsRegOpnd() && instr->GetDst()->GetStackSym()->HasByteCodeRegSlot()) |
| { |
| StackSym * dstSym = instr->GetDst()->GetStackSym(); |
| Js::RegSlot dstRegSlot = dstSym->GetByteCodeRegSlot(); |
| if (!this->RegIsTemp(dstRegSlot) && !this->RegIsConstant(dstRegSlot)) |
| { |
| SymID symId = dstSym->m_id; |
| |
| AssertOrFailFast(symId < m_stSlots->Length()); |
| if (this->m_stSlots->Test(symId)) |
| { |
| // For jitted loop bodies that are in a try block, we consider any symbol that has a |
| // non-temp bytecode reg slot, to be write-through. Hence, generating StSlots at all |
| // defs for such symbols |
| IR::Instr * stSlot = this->GenerateLoopBodyStSlot(dstRegSlot); |
| AddInstr(stSlot, Js::Constants::NoByteCodeOffset); |
| |
| this->m_stSlots->Clear(symId); |
| } |
| else |
| { |
| Assert(dstSym->m_isCatchObjectSym); |
| } |
| } |
| } |
| } NEXT_INSTR_BACKWARD_EDITING_IN_RANGE; |
| |
| lastProcessedInstrForJITLoopBody = m_lastInstr; |
| } |
| |
| offset = m_jnReader.GetCurrentOffset(); |
| |
| if (m_func->IsJitInDebugMode()) |
| { |
| bool needBailoutForHelper = CONFIG_FLAG(EnableContinueAfterExceptionWrappersForHelpers) && |
| (OpCodeAttr::NeedsPostOpDbgBailOut(newOpcode) || |
| (m_lastInstr->m_opcode == Js::OpCode::CallHelper && m_lastInstr->GetSrc1() && |
| HelperMethodAttributes::CanThrow(m_lastInstr->GetSrc1()->AsHelperCallOpnd()->m_fnHelper))); |
| |
| if (needBailoutForHelper) |
| { |
| // Insert bailout after return from a helper call. |
| // For now use offset of next instr, when we get & ignore exception, we replace this with next statement offset. |
| if (m_lastInstr->IsBranchInstr()) |
| { |
| // Debugger bailout on branches goes to different block which can become dead. Keep bailout with real instr. |
| // Can't convert to bailout at this time, can do that only after branches are finalized, remember for later. |
| ignoreExBranchInstrToOffsetMap.Add(m_lastInstr, offset); |
| } |
| else if ( |
| m_lastInstr->m_opcode == Js::OpCode::Throw || |
| m_lastInstr->m_opcode == Js::OpCode::RuntimeReferenceError || |
| m_lastInstr->m_opcode == Js::OpCode::RuntimeTypeError) |
| { |
| uint32 lastInstrOffset = m_lastInstr->GetByteCodeOffset(); |
| |
| AssertOrFailFast(lastInstrOffset < m_offsetToInstructionCount); |
| #if DBG |
| __analysis_assume(lastInstrOffset < this->m_offsetToInstructionCount); |
| #endif |
| bool isLastInstrUpdateNeeded = m_offsetToInstruction[lastInstrOffset] == m_lastInstr; |
| |
| BailOutInfo * bailOutInfo = JitAnew(this->m_func->m_alloc, BailOutInfo, offset, this->m_func); |
| m_lastInstr = m_lastInstr->ConvertToBailOutInstr(bailOutInfo, c_debuggerBaseBailOutKindForHelper, true); |
| |
| if (isLastInstrUpdateNeeded) |
| { |
| m_offsetToInstruction[lastInstrOffset] = m_lastInstr; |
| } |
| } |
| else |
| { |
| IR::BailOutKind bailOutKind = c_debuggerBaseBailOutKindForHelper; |
| if (OpCodeAttr::HasImplicitCall(newOpcode) || OpCodeAttr::OpndHasImplicitCall(newOpcode)) |
| { |
| // When we get out of e.g. valueOf called by a helper (e.g. Add_A) during stepping, |
| // we need to bail out to continue debugging calling function in interpreter, |
| // essentially this is similar to bail out on return from a method. |
| bailOutKind |= c_debuggerBailOutKindForCall; |
| } |
| |
| this->InsertBailOutForDebugger(offset, bailOutKind); |
| } |
| } |
| } |
| |
| while (m_statementReader.AtStatementBoundary(&m_jnReader)) |
| { |
| statementIndex = this->AddStatementBoundary(statementIndex, offset); |
| } |
| } |
| |
| if (Js::Constants::NoStatementIndex != statementIndex) |
| { |
| // If we are inside a user statement then create a trailing line pragma instruction |
| statementIndex = this->AddStatementBoundary(statementIndex, Js::Constants::NoByteCodeOffset); |
| } |
| |
| if (IsLoopBody()) |
| { |
| // Insert the LdSlot/StSlot and Ret |
| IR::Opnd * retOpnd = this->InsertLoopBodyReturnIPInstr(offset, offset); |
| |
| // Restore and Ret are at the last offset + 1 |
| GenerateLoopBodySlotAccesses(lastOffset + 1); |
| |
| InsertDoneLoopBodyLoopCounter(lastOffset); |
| |
| IR::Instr * retInstr = IR::Instr::New(Js::OpCode::Ret, m_func); |
| retInstr->SetSrc1(retOpnd); |
| this->AddInstr(retInstr, lastOffset + 1); |
| } |
| |
| // Now fix up the targets for all the branches we've introduced. |
| |
| InsertLabels(); |
| |
| Assert(!this->handlerOffsetStack || this->handlerOffsetStack->Empty()); |
| |
| // Insert bailout for ignore exception for labels, after all labels were finalized. |
| ignoreExBranchInstrToOffsetMap.Map([this](IR::Instr* instr, int byteCodeOffset) { |
| BailOutInfo * bailOutInfo = JitAnew(this->m_func->m_alloc, BailOutInfo, byteCodeOffset, this->m_func); |
| instr->ConvertToBailOutInstr(bailOutInfo, c_debuggerBaseBailOutKindForHelper, true); |
| }); |
| |
| // Now that we know whether the func is a leaf or not, decide whether we'll emit fast paths. |
| // Do this once and for all, per-func, since the source size on the ThreadContext will be |
| // changing while we JIT. |
| |
| if (this->m_func->IsTopFunc()) |
| { |
| this->m_func->SetDoFastPaths(); |
| this->EmitClosureRangeChecks(); |
| } |
| } |
| |
| void |
| IRBuilder::EmitClosureRangeChecks() |
| { |
| if (m_func->frameDisplayCheckTable) |
| { |
| // Frame display checks. Again, chain to the instruction (LdEnv/LdSlot). |
| FOREACH_HASHTABLE_ENTRY(FrameDisplayCheckRecord*, bucket, m_func->frameDisplayCheckTable) |
| { |
| StackSym *stackSym = m_func->m_symTable->FindStackSym(bucket.value); |
| Assert(stackSym && stackSym->m_instrDef); |
| |
| IR::Instr *instrDef = stackSym->m_instrDef; |
| IR::Instr *insertInstr = instrDef->m_next; |
| IR::RegOpnd *dstOpnd = instrDef->UnlinkDst()->AsRegOpnd(); |
| IR::Instr *instr = IR::Instr::New(Js::OpCode::FrameDisplayCheck, dstOpnd, m_func); |
| |
| dstOpnd = IR::RegOpnd::New(TyVar, m_func); |
| instrDef->SetDst(dstOpnd); |
| instr->SetSrc1(dstOpnd); |
| |
| // Attach the two-dimensional check info. |
| IR::AddrOpnd *recordOpnd = IR::AddrOpnd::New(bucket.element, IR::AddrOpndKindDynamicMisc, m_func, true); |
| instr->SetSrc2(recordOpnd); |
| |
| insertInstr->InsertBefore(instr); |
| } |
| NEXT_HASHTABLE_ENTRY; |
| } |
| |
| // If not a loop, but there are loops and trys, restore scope slot pointer and FD |
| if (!m_func->IsLoopBody() && m_func->HasTry() && m_func->GetJITFunctionBody()->GetByteCodeInLoopCount() != 0) |
| { |
| BVSparse<JitArenaAllocator> * bv = nullptr; |
| if (m_func->GetLocalClosureSym() && m_func->GetLocalClosureSym()->HasByteCodeRegSlot()) |
| { |
| bv = JitAnew(m_func->m_alloc, BVSparse<JitArenaAllocator>, m_func->m_alloc); |
| bv->Set(m_func->GetLocalClosureSym()->m_id); |
| } |
| if (m_func->GetLocalFrameDisplaySym() && m_func->GetLocalFrameDisplaySym()->HasByteCodeRegSlot()) |
| { |
| if (!bv) |
| { |
| bv = JitAnew(m_func->m_alloc, BVSparse<JitArenaAllocator>, m_func->m_alloc); |
| } |
| bv->Set(m_func->GetLocalFrameDisplaySym()->m_id); |
| } |
| if (bv) |
| { |
| |
| FOREACH_INSTR_IN_FUNC_BACKWARD(instr, m_func) |
| { |
| if (instr->m_opcode == Js::OpCode::Ret) |
| { |
| IR::ByteCodeUsesInstr * byteCodeUse = IR::ByteCodeUsesInstr::New(instr); |
| byteCodeUse->SetBV(bv); |
| instr->InsertBefore(byteCodeUse); |
| break; |
| } |
| } |
| NEXT_INSTR_IN_FUNC_BACKWARD; |
| } |
| } |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::InsertLabels |
| /// |
| /// Insert label instructions at the offsets recorded in the branch reloc list. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| void |
| IRBuilder::InsertLabels() |
| { |
| AssertMsg(this->branchRelocList, "Malformed branch reloc list"); |
| |
| SList<BranchReloc *>::Iterator iter(this->branchRelocList); |
| |
| while (iter.Next()) |
| { |
| IR::LabelInstr * labelInstr = nullptr; |
| BranchReloc * reloc = iter.Data(); |
| IR::BranchInstr * branchInstr = reloc->GetBranchInstr(); |
| uint offset = reloc->GetOffset(); |
| uint const branchOffset = reloc->GetBranchOffset(); |
| |
| Assert(!IsLoopBody() || offset <= GetLoopBodyExitInstrOffset()); |
| |
| if(branchInstr->m_opcode == Js::OpCode::MultiBr) |
| { |
| IR::MultiBranchInstr * multiBranchInstr = branchInstr->AsBranchInstr()->AsMultiBrInstr(); |
| |
| multiBranchInstr->UpdateMultiBrTargetOffsets([&](uint32 offset) -> IR::LabelInstr * |
| { |
| labelInstr = this->CreateLabel(branchInstr, offset); |
| multiBranchInstr->ChangeLabelRef(nullptr, labelInstr); |
| return labelInstr; |
| }); |
| } |
| else |
| { |
| labelInstr = this->CreateLabel(branchInstr, offset); |
| branchInstr->SetTarget(labelInstr); |
| } |
| |
| if (!reloc->IsNotBackEdge() && branchOffset >= offset) |
| { |
| bool wasLoopTop = labelInstr->m_isLoopTop; |
| labelInstr->m_isLoopTop = true; |
| |
| if (m_func->IsJitInDebugMode()) |
| { |
| // Add bailout for Async Break. |
| IR::BranchInstr* backEdgeBranchInstr = reloc->GetBranchInstr(); |
| this->InsertBailOutForDebugger(backEdgeBranchInstr->GetByteCodeOffset(), IR::BailOutForceByFlag | IR::BailOutBreakPointInFunction, backEdgeBranchInstr); |
| } |
| |
| if (!wasLoopTop && m_loopCounterSym) |
| { |
| this->InsertIncrLoopBodyLoopCounter(labelInstr); |
| } |
| |
| } |
| } |
| } |
| |
| IR::LabelInstr * |
| IRBuilder::CreateLabel(IR::BranchInstr * branchInstr, uint& offset) |
| { |
| IR::LabelInstr * labelInstr; |
| IR::Instr * targetInstr; |
| |
| for (;;) |
| { |
| AssertOrFailFast(offset < m_offsetToInstructionCount); |
| targetInstr = this->m_offsetToInstruction[offset]; |
| if (targetInstr != nullptr) |
| { |
| #ifdef BYTECODE_BRANCH_ISLAND |
| // If we have a long branch, remap it to the target offset |
| if (targetInstr == VirtualLongBranchInstr) |
| { |
| offset = ResolveVirtualLongBranch(branchInstr, offset); |
| continue; |
| } |
| #endif |
| break; |
| } |
| offset++; |
| } |
| |
| IR::Instr *instrPrev = targetInstr->m_prev; |
| |
| if (instrPrev) |
| { |
| instrPrev = targetInstr->GetPrevRealInstrOrLabel(); |
| } |
| |
| if (instrPrev && instrPrev->IsLabelInstr() && instrPrev->GetByteCodeOffset() == offset) |
| { |
| // Found an existing label at the right offset. Just reuse it. |
| labelInstr = instrPrev->AsLabelInstr(); |
| } |
| else |
| { |
| // No label at the desired offset. Create one. |
| |
| labelInstr = IR::LabelInstr::New(Js::OpCode::Label, this->m_func); |
| labelInstr->SetByteCodeOffset(offset); |
| if (instrPrev) |
| { |
| instrPrev->InsertAfter(labelInstr); |
| } |
| else |
| { |
| targetInstr->InsertBefore(labelInstr); |
| } |
| } |
| return labelInstr; |
| } |
| |
| void IRBuilder::InsertInstr(IR::Instr *instr, IR::Instr* insertBeforeInstr) |
| { |
| AssertOrFailFast(insertBeforeInstr->GetByteCodeOffset() < m_offsetToInstructionCount); |
| instr->SetByteCodeOffset(insertBeforeInstr); |
| uint32 offset = insertBeforeInstr->GetByteCodeOffset(); |
| if (m_offsetToInstruction[offset] == insertBeforeInstr) |
| { |
| m_offsetToInstruction[offset] = instr; |
| } |
| insertBeforeInstr->InsertBefore(instr); |
| |
| #if DBG_DUMP |
| if (PHASE_TRACE(Js::IRBuilderPhase, m_func->GetTopFunc())) |
| { |
| instr->Dump(); |
| } |
| #endif |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::AddInstr |
| /// |
| /// Add an instruction to the current instr list. Also add this instr to |
| /// offsetToinstruction table to patch branches/labels afterwards. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| void |
| IRBuilder::AddInstr(IR::Instr *instr, uint32 offset) |
| { |
| m_lastInstr->InsertAfter(instr); |
| if (offset != Js::Constants::NoByteCodeOffset) |
| { |
| AssertOrFailFast(offset < m_offsetToInstructionCount); |
| if (m_offsetToInstruction[offset] == nullptr) |
| { |
| m_offsetToInstruction[offset] = instr; |
| } |
| else |
| { |
| Assert(m_lastInstr->GetByteCodeOffset() == offset); |
| } |
| if (instr->GetByteCodeOffset() == Js::Constants::NoByteCodeOffset) |
| { |
| instr->SetByteCodeOffset(offset); |
| } |
| } |
| else |
| { |
| instr->SetByteCodeOffset(m_lastInstr->GetByteCodeOffset()); |
| } |
| m_lastInstr = instr; |
| |
| Func *topFunc = this->m_func->GetTopFunc(); |
| if (!topFunc->GetHasTempObjectProducingInstr()) |
| { |
| if (OpCodeAttr::TempObjectProducing(instr->m_opcode)) |
| { |
| topFunc->SetHasTempObjectProducingInstr(true); |
| } |
| } |
| |
| #if DBG_DUMP |
| if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::IRBuilderPhase, this->m_func->GetTopFunc()->GetSourceContextId(), this->m_func->GetTopFunc()->GetLocalFunctionId())) |
| { |
| instr->Dump(); |
| } |
| #endif |
| } |
| |
| IR::IndirOpnd * |
| IRBuilder::BuildIndirOpnd(IR::RegOpnd *baseReg, IR::RegOpnd *indexReg) |
| { |
| IR::IndirOpnd *indirOpnd = IR::IndirOpnd::New(baseReg, indexReg, TyVar, m_func); |
| return indirOpnd; |
| } |
| |
| IR::IndirOpnd * |
| IRBuilder::BuildIndirOpnd(IR::RegOpnd *baseReg, uint32 offset) |
| { |
| IR::IndirOpnd *indirOpnd = IR::IndirOpnd::New(baseReg, offset, TyVar, m_func); |
| return indirOpnd; |
| } |
| |
| #if DBG_DUMP || defined(ENABLE_IR_VIEWER) |
| IR::IndirOpnd * |
| IRBuilder::BuildIndirOpnd(IR::RegOpnd *baseReg, uint32 offset, const char16 *desc) |
| { |
| IR::IndirOpnd *indirOpnd = IR::IndirOpnd::New(baseReg, offset, TyVar, desc, m_func); |
| return indirOpnd; |
| } |
| #endif |
| |
| IR::SymOpnd * |
| IRBuilder::BuildFieldOpnd(Js::OpCode newOpcode, Js::RegSlot reg, Js::PropertyId propertyId, Js::PropertyIdIndexType propertyIdIndex, PropertyKind propertyKind, uint inlineCacheIndex) |
| { |
| AssertOrFailFast(inlineCacheIndex < m_func->GetJITFunctionBody()->GetInlineCacheCount() || inlineCacheIndex == Js::Constants::NoInlineCacheIndex); |
| PropertySym * propertySym = BuildFieldSym(reg, propertyId, propertyIdIndex, inlineCacheIndex, propertyKind); |
| IR::SymOpnd * symOpnd; |
| |
| // If we plan to apply object type optimization to this instruction or if we intend to emit a fast path using an inline |
| // cache, we will need a property sym operand. |
| if (OpCodeAttr::FastFldInstr(newOpcode) || inlineCacheIndex != (uint)-1) |
| { |
| Assert(propertyKind == PropertyKindData); |
| symOpnd = IR::PropertySymOpnd::New(propertySym, inlineCacheIndex, TyVar, this->m_func); |
| |
| if (inlineCacheIndex != (uint)-1 && propertySym->m_loadInlineCacheIndex == (uint)-1) |
| { |
| if (GlobOpt::IsPREInstrCandidateLoad(newOpcode)) |
| { |
| propertySym->m_loadInlineCacheIndex = inlineCacheIndex; |
| propertySym->m_loadInlineCacheFunc = this->m_func; |
| } |
| } |
| } |
| else |
| { |
| symOpnd = IR::SymOpnd::New(propertySym, TyVar, this->m_func); |
| } |
| |
| return symOpnd; |
| } |
| |
| PropertySym * |
| IRBuilder::BuildFieldSym(Js::RegSlot reg, Js::PropertyId propertyId, Js::PropertyIdIndexType propertyIdIndex, uint inlineCacheIndex, PropertyKind propertyKind) |
| { |
| PropertySym * propertySym; |
| SymID symId = this->BuildSrcStackSymID(reg); |
| |
| AssertMsg(m_func->m_symTable->FindStackSym(symId), "Tried to use an undefined stacksym?"); |
| |
| propertySym = PropertySym::FindOrCreate(symId, propertyId, propertyIdIndex, inlineCacheIndex, propertyKind, m_func); |
| |
| return propertySym; |
| } |
| |
| SymID |
| IRBuilder::BuildSrcStackSymID(Js::RegSlot regSlot) |
| { |
| SymID symID; |
| |
| if (this->RegIsTemp(regSlot)) |
| { |
| // This is a use of a temp. Map the reg slot to its sym ID. |
| // !!!NOTE: always process an instruction's temp uses before its temp defs!!! |
| symID = this->GetMappedTemp(regSlot); |
| if (symID == 0) |
| { |
| // We might have temps that are live through the loop body via "with" statement |
| // We need to treat those as if they are locals and don't remap them |
| Assert(this->IsLoopBody()); |
| Assert(!this->m_usedAsTemp->Test(regSlot - m_func->GetJITFunctionBody()->GetFirstTmpReg())); |
| |
| symID = static_cast<SymID>(regSlot); |
| this->SetMappedTemp(regSlot, symID); |
| this->EnsureLoopBodyLoadSlot(symID); |
| } |
| this->SetTempUsed(regSlot, TRUE); |
| } |
| else |
| { |
| symID = static_cast<SymID>(regSlot); |
| if (IsLoopBody() && !this->RegIsConstant(regSlot)) |
| { |
| this->EnsureLoopBodyLoadSlot(symID); |
| } |
| } |
| return symID; |
| } |
| |
| IR::RegOpnd * |
| IRBuilder::EnsureLoopBodyForInEnumeratorArrayOpnd() |
| { |
| Assert(this->IsLoopBody()); |
| IR::RegOpnd * loopBodyForInEnumeratorArrayOpnd = this->m_loopBodyForInEnumeratorArrayOpnd; |
| if (loopBodyForInEnumeratorArrayOpnd == nullptr) |
| { |
| loopBodyForInEnumeratorArrayOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func); |
| this->m_loopBodyForInEnumeratorArrayOpnd = loopBodyForInEnumeratorArrayOpnd; |
| StackSym *loopParamSym = m_func->EnsureLoopParamSym(); |
| IR::RegOpnd *loopParamOpnd = IR::RegOpnd::New(loopParamSym, TyMachPtr, m_func); |
| |
| IR::Instr * ldInstr = IR::Instr::New(Js::OpCode::Ld_A, loopBodyForInEnumeratorArrayOpnd, |
| IR::IndirOpnd::New(loopParamOpnd, Js::InterpreterStackFrame::GetOffsetOfForInEnumerators(), TyMachPtr, this->m_func), |
| this->m_func); |
| m_func->m_headInstr->InsertAfter(ldInstr); |
| } |
| return loopBodyForInEnumeratorArrayOpnd; |
| } |
| |
| IR::Opnd * |
| IRBuilder::BuildForInEnumeratorOpnd(uint forInLoopLevel) |
| { |
| Assert(forInLoopLevel < this->m_func->GetJITFunctionBody()->GetForInLoopDepth()); |
| if (!this->IsLoopBody()) |
| { |
| StackSym *stackSym = StackSym::New(TyMisc, this->m_func); |
| stackSym->m_offset = forInLoopLevel; |
| return IR::SymOpnd::New(stackSym, TyMachPtr, this->m_func); |
| } |
| return IR::IndirOpnd::New( |
| EnsureLoopBodyForInEnumeratorArrayOpnd(), forInLoopLevel * sizeof(Js::ForInObjectEnumerator), TyMachPtr, this->m_func); |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildSrcOpnd |
| /// |
| /// Create a StackSym and return a RegOpnd for this RegSlot. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| IR::RegOpnd * |
| IRBuilder::BuildSrcOpnd(Js::RegSlot srcRegSlot, IRType type) |
| { |
| StackSym * symSrc = m_func->m_symTable->FindStackSym(BuildSrcStackSymID(srcRegSlot)); |
| AssertMsg(symSrc, "Tried to use an undefined stack slot?"); |
| IR::RegOpnd *regOpnd = IR::RegOpnd::New(symSrc, type, m_func); |
| |
| return regOpnd; |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildDstOpnd |
| /// |
| /// Create a StackSym and return a RegOpnd for this RegSlot. |
| /// If the RegSlot is '0', it may have multiple defs, so use FindOrCreate. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| IR::RegOpnd * |
| IRBuilder::BuildDstOpnd(Js::RegSlot dstRegSlot, IRType type, bool isCatchObjectSym) |
| { |
| StackSym * symDst; |
| SymID symID; |
| |
| if (this->RegIsTemp(dstRegSlot)) |
| { |
| #if DBG |
| if (this->IsLoopBody()) |
| { |
| // If we are doing loop body, and a temp reg slot is loaded via LdSlot |
| // That means that we have detected that the slot is live coming in to the loop. |
| // This would only happen for the value of a "with" statement, so there shouldn't |
| // be any def for those |
| Assert(!this->m_ldSlots->Test(dstRegSlot)); |
| this->m_usedAsTemp->Set(dstRegSlot - m_func->GetJITFunctionBody()->GetFirstTmpReg()); |
| } |
| #endif |
| |
| // This is a def of a temp. Create a new sym ID for it if it's been used since its last def. |
| // !!!NOTE: always process an instruction's temp uses before its temp defs!!! |
| if (this->GetTempUsed(dstRegSlot)) |
| { |
| symID = m_func->m_symTable->NewID(); |
| this->SetTempUsed(dstRegSlot, FALSE); |
| this->SetMappedTemp(dstRegSlot, symID); |
| } |
| else |
| { |
| symID = this->GetMappedTemp(dstRegSlot); |
| // The temp hasn't been used since its last def. There are 2 possibilities: |
| if (symID == 0) |
| { |
| // First time we've seen the temp. Just use the number that the front end gave it. |
| symID = static_cast<SymID>(dstRegSlot); |
| this->SetMappedTemp(dstRegSlot, symID); |
| } |
| } |
| |
| } |
| else |
| { |
| symID = static_cast<SymID>(dstRegSlot); |
| if (this->RegIsConstant(dstRegSlot)) |
| { |
| // Don't need to track constant registers for bailout. Don't set the byte code register for constant. |
| dstRegSlot = Js::Constants::NoRegister; |
| } |
| else if (IsLoopBody()) |
| { |
| // Loop body and not constants |
| this->SetLoopBodyStSlot(symID, isCatchObjectSym); |
| |
| // We need to make sure that the symbols is loaded as well |
| // so that the sym will be defined on all path. |
| this->EnsureLoopBodyLoadSlot(symID, isCatchObjectSym); |
| } |
| } |
| |
| symDst = StackSym::FindOrCreate(symID, dstRegSlot, m_func); |
| |
| // Always reset isSafeThis to false. We'll set it to true for singleDef cases, |
| // but want to reset it to false if it is multi-def. |
| // NOTE: We could handle the multiDef if they are all safe, but it probably isn't very common. |
| symDst->m_isSafeThis = false; |
| |
| IR::RegOpnd *regOpnd = IR::RegOpnd::New(symDst, type, m_func); |
| return regOpnd; |
| } |
| |
| void |
| IRBuilder::BuildImplicitArgIns() |
| { |
| Js::RegSlot startReg = m_func->GetJITFunctionBody()->GetConstCount() - 1; |
| for (Js::ArgSlot i = 1; i < m_func->GetJITFunctionBody()->GetInParamsCount(); i++) |
| { |
| this->BuildArgIn((uint32)-1, startReg + i, i); |
| } |
| } |
| |
| #if DBG_DUMP || defined(ENABLE_IR_VIEWER) |
| #define POINTER_OFFSET(opnd, c, field) \ |
| BuildIndirOpnd((opnd), c::Get##field##Offset(), _u(#c) _u(".") _u(#field)) |
| #else |
| #define POINTER_OFFSET(opnd, c, field) \ |
| BuildIndirOpnd((opnd), c::Get##field##Offset()) |
| #endif |
| |
| void |
| IRBuilder::BuildGeneratorPreamble() |
| { |
| if (!this->m_func->GetJITFunctionBody()->IsCoroutine()) |
| { |
| return; |
| } |
| |
| // Build code to check if the generator already has state and if it does then jump to the corresponding resume point. |
| // Otherwise jump to the start of the function. The generator object is the first argument by convention established |
| // in JavascriptGenerator::EntryNext/EntryReturn/EntryThrow. |
| // |
| // s1 = Ld_A prm1 |
| // s2 = Ld_A s1[offset of JavascriptGenerator::frame] |
| // BrAddr_A s2 nullptr $startOfFunc |
| // s3 = Ld_A s2[offset of InterpreterStackFrame::m_reader.m_currentLocation] |
| // s4 = Ld_A s2[offset of InterpreterStackFrame::m_reader.m_startLocation] |
| // s5 = Sub_I4 s3 s4 |
| // GeneratorResumeJumpTable s5 |
| // $startOfFunc: |
| // |
| |
| StackSym *genParamSym = StackSym::NewParamSlotSym(1, this->m_func); |
| this->m_func->SetArgOffset(genParamSym, LowererMD::GetFormalParamOffset() * MachPtr); |
| |
| IR::SymOpnd *genParamOpnd = IR::SymOpnd::New(genParamSym, TyMachPtr, this->m_func); |
| IR::RegOpnd *genRegOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func); |
| IR::Instr *instr = IR::Instr::New(Js::OpCode::Ld_A, genRegOpnd, genParamOpnd, this->m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| IR::RegOpnd *genFrameOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func); |
| instr = IR::Instr::New(Js::OpCode::Ld_A, genFrameOpnd, POINTER_OFFSET(genRegOpnd, Js::JavascriptGenerator, Frame), this->m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| IR::LabelInstr *labelInstr = IR::LabelInstr::New(Js::OpCode::Label, this->m_func); |
| IR::BranchInstr *branchInstr = IR::BranchInstr::New(Js::OpCode::BrAddr_A, labelInstr, genFrameOpnd, IR::AddrOpnd::NewNull(this->m_func), this->m_func); |
| this->AddInstr(branchInstr, Js::Constants::NoByteCodeOffset); |
| |
| IR::RegOpnd *curLocOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func); |
| instr = IR::Instr::New(Js::OpCode::Ld_A, curLocOpnd, POINTER_OFFSET(genFrameOpnd, Js::InterpreterStackFrame, CurrentLocation), this->m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| IR::RegOpnd *startLocOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func); |
| instr = IR::Instr::New(Js::OpCode::Ld_A, startLocOpnd, POINTER_OFFSET(genFrameOpnd, Js::InterpreterStackFrame, StartLocation), this->m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| IR::RegOpnd *curOffsetOpnd = IR::RegOpnd::New(TyUint32, this->m_func); |
| instr = IR::Instr::New(Js::OpCode::Sub_I4, curOffsetOpnd, curLocOpnd, startLocOpnd, this->m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| instr = IR::Instr::New(Js::OpCode::GeneratorResumeJumpTable, this->m_func); |
| instr->SetSrc1(curOffsetOpnd); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| this->AddInstr(labelInstr, Js::Constants::NoByteCodeOffset); |
| } |
| |
| void |
| IRBuilder::LoadNativeCodeData() |
| { |
| if (m_func->IsOOPJIT() && m_func->IsTopFunc()) |
| { |
| IR::RegOpnd * nativeDataOpnd = IR::RegOpnd::New(TyVar, m_func); |
| IR::Instr * instr = IR::Instr::New(Js::OpCode::LdNativeCodeData, nativeDataOpnd, m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| m_func->SetNativeCodeDataSym(nativeDataOpnd->GetStackSym()); |
| } |
| } |
| |
| void |
| IRBuilder::BuildConstantLoads() |
| { |
| Js::RegSlot count = m_func->GetJITFunctionBody()->GetConstCount(); |
| |
| for (Js::RegSlot reg = Js::FunctionBody::FirstRegSlot; reg < count; reg++) |
| { |
| intptr_t varConst = m_func->GetJITFunctionBody()->GetConstantVar(reg); |
| Assert(varConst != 0); |
| Js::TypeId type = m_func->GetJITFunctionBody()->GetConstantType(reg); |
| |
| IR::RegOpnd *dstOpnd = this->BuildDstOpnd(reg); |
| Assert(this->RegIsConstant(reg)); |
| dstOpnd->m_sym->SetIsFromByteCodeConstantTable(); |
| // TODO: be more precise about this |
| ValueType valueType; |
| IR::Instr *instr = nullptr; |
| switch (type) |
| { |
| case Js::TypeIds_Number: |
| valueType = ValueType::Number; |
| instr = IR::Instr::NewConstantLoad(dstOpnd, varConst, valueType, m_func |
| #if !FLOATVAR |
| , m_func->IsOOPJIT() ? m_func->GetJITFunctionBody()->GetConstAsT<Js::JavascriptNumber>(reg) : nullptr |
| #endif |
| ); |
| break; |
| case Js::TypeIds_String: |
| { |
| valueType = ValueType::String; |
| if (m_func->IsOOPJIT()) |
| { |
| // must be either PropertyString or LiteralString |
| JITRecyclableObject * jitObj = m_func->GetJITFunctionBody()->GetConstantContent(reg); |
| JITJavascriptString * constStr = JITJavascriptString::FromVar(jitObj); |
| instr = IR::Instr::NewConstantLoad(dstOpnd, varConst, valueType, m_func, constStr); |
| } |
| else |
| { |
| instr = IR::Instr::NewConstantLoad(dstOpnd, varConst, valueType, m_func); |
| } |
| break; |
| } |
| case Js::TypeIds_Limit: |
| valueType = ValueType::FromTypeId(type, false); |
| instr = IR::Instr::NewConstantLoad(dstOpnd, varConst, valueType, m_func); |
| break; |
| default: |
| valueType = ValueType::FromTypeId(type, false); |
| instr = IR::Instr::NewConstantLoad(dstOpnd, varConst, valueType, m_func, |
| m_func->IsOOPJIT() ? m_func->GetJITFunctionBody()->GetConstAsT<Js::RecyclableObject>(reg) : nullptr); |
| break; |
| } |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| } |
| |
| } |
| |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildReg1 |
| /// |
| /// Build IR instr for a Reg1 instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg1<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->R0); |
| } |
| |
| BuildReg1(newOpcode, offset, layout->R0); |
| } |
| |
| void |
| IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0) |
| { |
| if (newOpcode == Js::OpCode::ArgIn0) |
| { |
| this->BuildArgIn0(offset, R0); |
| return; |
| } |
| |
| IR::Instr * instr; |
| Js::RegSlot srcRegOpnd, dstRegSlot; |
| srcRegOpnd = dstRegSlot = R0; |
| |
| IR::Opnd * srcOpnd = nullptr; |
| bool isNotInt = false; |
| bool dstIsCatchObject = false; |
| ValueType dstValueType; |
| switch (newOpcode) |
| { |
| case Js::OpCode::LdLetHeapArguments: |
| { |
| this->m_func->SetHasNonSimpleParams(); |
| //FallThrough to next case block! |
| } |
| case Js::OpCode::LdHeapArguments: |
| { |
| if (this->m_func->GetJITFunctionBody()->NeedScopeObjectForArguments(m_func->GetHasNonSimpleParams())) |
| { |
| Js::RegSlot regFrameObj = m_func->GetJITFunctionBody()->GetLocalClosureReg(); |
| Assert(regFrameObj != Js::Constants::NoRegister); |
| srcOpnd = BuildSrcOpnd(regFrameObj); |
| if (m_func->GetJITFunctionBody()->GetInParamsCount() > 1) |
| { |
| m_func->SetScopeObjSym(srcOpnd->GetStackSym()); |
| } |
| } |
| else |
| { |
| srcOpnd = IR::AddrOpnd::New( |
| m_func->GetScriptContextInfo()->GetNullAddr(), IR::AddrOpndKindDynamicVar, m_func, true); |
| } |
| IR::RegOpnd * dstOpnd = BuildDstOpnd(R0); |
| instr = IR::Instr::New(newOpcode, dstOpnd, srcOpnd, m_func); |
| this->AddInstr(instr, offset); |
| StackSym * dstSym = dstOpnd->m_sym; |
| if (dstSym->m_isSingleDef) |
| { |
| dstSym->m_isSafeThis = true; |
| dstSym->m_isNotNumber = true; |
| } |
| return; |
| } |
| case Js::OpCode::LdLetHeapArgsCached: |
| { |
| this->m_func->SetHasNonSimpleParams(); |
| //Fallthrough to next case block! |
| } |
| case Js::OpCode::LdHeapArgsCached: |
| if (!m_func->GetJITFunctionBody()->HasScopeObject()) |
| { |
| Js::Throw::FatalInternalError(); |
| } |
| srcOpnd = BuildSrcOpnd(m_func->GetJITFunctionBody()->GetLocalClosureReg()); |
| if (m_func->GetJITFunctionBody()->GetInParamsCount() > 1) |
| { |
| m_func->SetScopeObjSym(srcOpnd->GetStackSym()); |
| } |
| isNotInt = true; |
| break; |
| |
| case Js::OpCode::LdLocalObj: |
| if (!m_func->GetJITFunctionBody()->HasScopeObject()) |
| { |
| Js::Throw::FatalInternalError(); |
| } |
| srcOpnd = BuildSrcOpnd(m_func->GetJITFunctionBody()->GetLocalClosureReg()); |
| isNotInt = true; |
| newOpcode = Js::OpCode::Ld_A; |
| break; |
| |
| case Js::OpCode::LdParamObj: |
| if (!m_func->GetJITFunctionBody()->HasScopeObject()) |
| { |
| Js::Throw::FatalInternalError(); |
| } |
| srcOpnd = BuildSrcOpnd(m_func->GetJITFunctionBody()->GetParamClosureReg()); |
| isNotInt = true; |
| newOpcode = Js::OpCode::Ld_A; |
| break; |
| |
| case Js::OpCode::Throw: |
| { |
| srcOpnd = this->BuildSrcOpnd(srcRegOpnd); |
| if ((this->handlerOffsetStack && !this->handlerOffsetStack->Empty()) || |
| finallyBlockLevel > 0) |
| { |
| newOpcode = Js::OpCode::EHThrow; |
| } |
| instr = IR::Instr::New(newOpcode, m_func); |
| instr->SetSrc1(srcOpnd); |
| |
| this->AddInstr(instr, offset); |
| |
| if(DoBailOnNoProfile()) |
| { |
| //So optimistically assume it doesn't throw and introduce bailonnoprofile here. |
| //If there are continuous bailout bailonnoprofile will be disabled. |
| InsertBailOnNoProfile(instr); |
| } |
| return; |
| } |
| |
| case Js::OpCode::LdC_A_Null: |
| { |
| const auto addrOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetNullAddr(), IR::AddrOpndKindDynamicVar, m_func, true); |
| addrOpnd->SetValueType(ValueType::Null); |
| srcOpnd = addrOpnd; |
| newOpcode = Js::OpCode::Ld_A; |
| break; |
| } |
| |
| case Js::OpCode::LdUndef: |
| { |
| const auto addrOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetUndefinedAddr(), IR::AddrOpndKindDynamicVar, m_func, true); |
| addrOpnd->SetValueType(ValueType::Undefined); |
| srcOpnd = addrOpnd; |
| newOpcode = Js::OpCode::Ld_A; |
| break; |
| } |
| |
| case Js::OpCode::LdInfinity: |
| { |
| const auto floatConstOpnd = IR::FloatConstOpnd::New(Js::JavascriptNumber::POSITIVE_INFINITY, TyFloat64, m_func); |
| srcOpnd = floatConstOpnd; |
| newOpcode = Js::OpCode::LdC_A_R8; |
| break; |
| } |
| |
| case Js::OpCode::LdNaN: |
| { |
| const auto floatConstOpnd = IR::FloatConstOpnd::New(Js::JavascriptNumber::NaN, TyFloat64, m_func); |
| srcOpnd = floatConstOpnd; |
| newOpcode = Js::OpCode::LdC_A_R8; |
| break; |
| } |
| |
| case Js::OpCode::LdFalse: |
| { |
| const auto addrOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetFalseAddr(), IR::AddrOpndKindDynamicVar, m_func, true); |
| addrOpnd->SetValueType(ValueType::Boolean); |
| srcOpnd = addrOpnd; |
| newOpcode = Js::OpCode::Ld_A; |
| break; |
| } |
| |
| case Js::OpCode::LdTrue: |
| { |
| const auto addrOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetTrueAddr(), IR::AddrOpndKindDynamicVar, m_func, true); |
| addrOpnd->SetValueType(ValueType::Boolean); |
| srcOpnd = addrOpnd; |
| newOpcode = Js::OpCode::Ld_A; |
| break; |
| } |
| |
| case Js::OpCode::NewScObjectSimple: |
| dstValueType = ValueType::GetObject(ObjectType::Object); |
| // fall-through |
| case Js::OpCode::LdFuncExpr: |
| m_func->DisableCanDoInlineArgOpt(); |
| break; |
| case Js::OpCode::LdEnv: |
| case Js::OpCode::LdHomeObj: |
| case Js::OpCode::LdFuncObj: |
| isNotInt = TRUE; |
| break; |
| |
| case Js::OpCode::Unused: |
| // Don't generate anything. Just indicate that the temp reg is used. |
| Assert(this->RegIsTemp(dstRegSlot)); |
| this->SetTempUsed(dstRegSlot, TRUE); |
| return; |
| |
| case Js::OpCode::InitUndecl: |
| srcOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetUndeclBlockVarAddr(), IR::AddrOpndKindDynamicVar, m_func, true); |
| srcOpnd->SetValueType(ValueType::PrimitiveOrObject); |
| newOpcode = Js::OpCode::Ld_A; |
| break; |
| |
| case Js::OpCode::ChkUndecl: |
| srcOpnd = BuildSrcOpnd(srcRegOpnd); |
| instr = IR::Instr::New(Js::OpCode::ChkUndecl, m_func); |
| instr->SetSrc1(srcOpnd); |
| this->AddInstr(instr, offset); |
| return; |
| |
| case Js::OpCode::Catch: |
| if (this->handlerOffsetStack) |
| { |
| AssertOrFailFast(!this->handlerOffsetStack->Empty()); |
| AssertOrFailFast(this->handlerOffsetStack->Top().Second() == true); |
| this->handlerOffsetStack->Pop(); |
| } |
| dstIsCatchObject = true; |
| break; |
| |
| case Js::OpCode::LdChakraLib: |
| { |
| const auto addrOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetChakraLibAddr(), IR::AddrOpndKindDynamicVar, m_func, true); |
| addrOpnd->SetValueType(ValueType::PrimitiveOrObject); |
| srcOpnd = addrOpnd; |
| newOpcode = Js::OpCode::Ld_A; |
| break; |
| } |
| } |
| |
| IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot, TyVar, dstIsCatchObject); |
| dstOpnd->SetValueType(dstValueType); |
| StackSym * dstSym = dstOpnd->m_sym; |
| dstSym->m_isCatchObjectSym = dstIsCatchObject; |
| |
| instr = IR::Instr::New(newOpcode, dstOpnd, m_func); |
| if (srcOpnd) |
| { |
| instr->SetSrc1(srcOpnd); |
| if (dstSym->m_isSingleDef) |
| { |
| if (srcOpnd->IsHelperCallOpnd()) |
| { |
| // Don't do anything |
| } |
| else if (srcOpnd->IsIntConstOpnd()) |
| { |
| dstSym->SetIsIntConst(srcOpnd->AsIntConstOpnd()->GetValue()); |
| } |
| else if (srcOpnd->IsFloatConstOpnd()) |
| { |
| dstSym->SetIsFloatConst(); |
| } |
| else if (srcOpnd->IsAddrOpnd()) |
| { |
| dstSym->m_isConst = true; |
| dstSym->m_isNotNumber = true; |
| } |
| } |
| } |
| if (isNotInt && dstSym->m_isSingleDef) |
| { |
| dstSym->m_isNotNumber = true; |
| } |
| |
| this->AddInstr(instr, offset); |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildReg2 |
| /// |
| /// Build IR instr for a Reg2 instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildReg2(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode) || newOpcode == Js::OpCode::ProfiledStrictLdThis); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg2<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->R0); |
| this->DoClosureRegCheck(layout->R1); |
| } |
| |
| BuildReg2(newOpcode, offset, layout->R0, layout->R1, m_jnReader.GetCurrentOffset()); |
| } |
| |
| void |
| IRBuilder::BuildReg2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0, Js::RegSlot R1, uint32 nextOffset) |
| { |
| IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(R1); |
| StackSym * symSrc1 = src1Opnd->m_sym; |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::SpreadObjectLiteral: |
| // fall through |
| case Js::OpCode::SetComputedNameVar: |
| { |
| IR::Instr *instr = IR::Instr::New(newOpcode, m_func); |
| instr->SetSrc1(this->BuildSrcOpnd(R0)); |
| instr->SetSrc2(src1Opnd); |
| this->AddInstr(instr, offset); |
| return; |
| } |
| case Js::OpCode::LdFuncExprFrameDisplay: |
| { |
| IR::RegOpnd *dstOpnd = IR::RegOpnd::New(TyVar, m_func); |
| IR::Instr *instr = IR::Instr::New(Js::OpCode::LdFrameDisplay, dstOpnd, src1Opnd, m_func); |
| Js::RegSlot envReg = this->GetEnvReg(); |
| if (envReg != Js::Constants::NoRegister) |
| { |
| instr->SetSrc2(BuildSrcOpnd(envReg)); |
| } |
| this->AddInstr(instr, offset); |
| |
| IR::RegOpnd *src2Opnd = dstOpnd; |
| src1Opnd = BuildSrcOpnd(R0); |
| dstOpnd = BuildDstOpnd(m_func->GetJITFunctionBody()->GetLocalFrameDisplayReg()); |
| instr = IR::Instr::New(Js::OpCode::LdFrameDisplay, dstOpnd, src1Opnd, src2Opnd, m_func); |
| dstOpnd->m_sym->m_isNotNumber = true; |
| this->AddInstr(instr, offset); |
| return; |
| } |
| } |
| |
| IR::RegOpnd * dstOpnd = this->BuildDstOpnd(R0); |
| StackSym * dstSym = dstOpnd->m_sym; |
| |
| IR::Instr * instr = nullptr; |
| switch (newOpcode) |
| { |
| case Js::OpCode::Ld_A: |
| if (symSrc1->m_builtInIndex != Js::BuiltinFunction::None) |
| { |
| // Note: don't set dstSym->m_builtInIndex to None here (see Win8 399972) |
| dstSym->m_builtInIndex = symSrc1->m_builtInIndex; |
| } |
| break; |
| |
| case Js::OpCode::ProfiledStrictLdThis: |
| newOpcode = Js::OpCode::StrictLdThis; |
| if (m_func->HasProfileInfo()) |
| { |
| dstOpnd->SetValueType(m_func->GetReadOnlyProfileInfo()->GetThisInfo().valueType); |
| } |
| |
| if (m_func->DoSimpleJitDynamicProfile()) |
| { |
| IR::JitProfilingInstr* newInstr = IR::JitProfilingInstr::New(Js::OpCode::StrictLdThis, dstOpnd, src1Opnd, m_func); |
| instr = newInstr; |
| } |
| break; |
| case Js::OpCode::Delete_A: |
| dstOpnd->SetValueType(ValueType::Boolean); |
| break; |
| case Js::OpCode::BeginSwitch: |
| m_switchBuilder.BeginSwitch(); |
| newOpcode = Js::OpCode::Ld_A; |
| break; |
| case Js::OpCode::LdArrHead: |
| src1Opnd->SetValueType( |
| ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(false).SetArrayTypeId(Js::TypeIds_Array)); |
| src1Opnd->SetValueTypeFixed(); |
| break; |
| |
| case Js::OpCode::LdInnerFrameDisplayNoParent: |
| { |
| instr = IR::Instr::New(Js::OpCode::LdInnerFrameDisplay, dstOpnd, src1Opnd, m_func); |
| this->AddEnvOpndForInnerFrameDisplay(instr, offset); |
| if (dstSym->m_isSingleDef) |
| { |
| dstSym->m_isNotNumber = true; |
| } |
| this->AddInstr(instr, offset); |
| |
| return; |
| } |
| |
| case Js::OpCode::Conv_Str: |
| dstOpnd->SetValueType(ValueType::String); |
| break; |
| |
| case Js::OpCode::Yield: |
| instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, m_func); |
| this->AddInstr(instr, offset); |
| this->m_lastInstr = instr->ConvertToBailOutInstr(instr, IR::BailOutForGeneratorYield); |
| |
| IR::LabelInstr* label = IR::LabelInstr::New(Js::OpCode::Label, m_func); |
| label->m_hasNonBranchRef = true; |
| this->AddInstr(label, Js::Constants::NoByteCodeOffset); |
| |
| this->m_func->AddYieldOffsetResumeLabel(nextOffset, label); |
| |
| return; |
| } |
| |
| if (instr == nullptr) |
| { |
| instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, m_func); |
| } |
| |
| this->AddInstr(instr, offset); |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildProfiledReg2 |
| /// |
| /// Build IR instr for a profiled Reg2 instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiledReg2(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_Reg2<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->R0); |
| this->DoClosureRegCheck(layout->R1); |
| } |
| |
| BuildProfiledReg2(newOpcode, offset, layout->R0, layout->R1, layout->profileId); |
| } |
| |
| void |
| IRBuilder::BuildProfiledReg2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot srcRegSlot, Js::ProfileId profileId) |
| { |
| bool switchFound = false; |
| |
| Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode); |
| |
| Assert(newOpcode == Js::OpCode::BeginSwitch); |
| |
| IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(srcRegSlot); |
| IR::RegOpnd * dstOpnd; |
| |
| if(srcRegSlot == dstRegSlot) |
| { |
| //if the operands are the same for BeginSwitch, don't build a new operand in IR. |
| dstOpnd = src1Opnd; |
| } |
| else |
| { |
| dstOpnd = this->BuildDstOpnd(dstRegSlot); |
| } |
| |
| m_switchBuilder.BeginSwitch(); |
| switchFound = true; |
| newOpcode = Js::OpCode::Ld_A; // BeginSwitch is originally equivalent to Ld_A |
| |
| IR::Instr *instr; |
| |
| if (m_func->DoSimpleJitDynamicProfile()) |
| { |
| // Since we're in simplejit, we want to keep track of the profileid: |
| IR::JitProfilingInstr *profiledInstr = IR::JitProfilingInstr::New(newOpcode, dstOpnd, src1Opnd, m_func); |
| profiledInstr->profileId = profileId; |
| profiledInstr->isBeginSwitch = newOpcode == Js::OpCode::Ld_A; |
| instr = profiledInstr; |
| } |
| else |
| { |
| IR::ProfiledInstr *profiledInstr = IR::ProfiledInstr::New(newOpcode, dstOpnd, src1Opnd, m_func); |
| instr = profiledInstr; |
| profiledInstr->u.FldInfo() = Js::FldInfo(); |
| } |
| |
| this->AddInstr(instr, offset); |
| |
| if(switchFound && instr->IsProfiledInstr()) |
| { |
| m_switchBuilder.SetProfiledInstruction(instr, profileId); |
| } |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildReg3 |
| /// |
| /// Build IR instr for a Reg3 instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildReg3(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg3<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func) && newOpcode != Js::OpCode::NewInnerScopeSlots) |
| { |
| this->DoClosureRegCheck(layout->R0); |
| this->DoClosureRegCheck(layout->R1); |
| this->DoClosureRegCheck(layout->R2); |
| } |
| |
| BuildReg3(newOpcode, offset, layout->R0, layout->R1, layout->R2, Js::Constants::NoProfileId); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiledReg3(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_Reg3<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->R0); |
| this->DoClosureRegCheck(layout->R1); |
| this->DoClosureRegCheck(layout->R2); |
| } |
| |
| Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode); |
| BuildReg3(newOpcode, offset, layout->R0, layout->R1, layout->R2, layout->profileId); |
| } |
| |
| void |
| IRBuilder::BuildReg3(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot, |
| Js::RegSlot src2RegSlot, Js::ProfileId profileId) |
| { |
| IR::Instr * instr; |
| |
| if (newOpcode == Js::OpCode::NewInnerScopeSlots) |
| { |
| if (dstRegSlot >= m_func->GetJITFunctionBody()->GetInnerScopeCount()) |
| { |
| Js::Throw::FatalInternalError(); |
| } |
| newOpcode = Js::OpCode::NewScopeSlotsWithoutPropIds; |
| dstRegSlot += m_func->GetJITFunctionBody()->GetFirstInnerScopeReg(); |
| instr = IR::Instr::New(newOpcode, BuildDstOpnd(dstRegSlot), |
| IR::IntConstOpnd::New(src1RegSlot, TyVar, m_func), |
| IR::IntConstOpnd::New(src2RegSlot, TyVar, m_func), |
| m_func); |
| if (instr->GetDst()->AsRegOpnd()->m_sym->m_isSingleDef) |
| { |
| instr->GetDst()->AsRegOpnd()->m_sym->m_isNotNumber = true; |
| } |
| this->AddInstr(instr, offset); |
| return; |
| } |
| |
| IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(src1RegSlot); |
| IR::RegOpnd * src2Opnd = this->BuildSrcOpnd(src2RegSlot); |
| IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot); |
| StackSym * dstSym = dstOpnd->m_sym; |
| |
| bool isProfiledInstr = (profileId != Js::Constants::NoProfileId); |
| bool wasNotProfiled = false; |
| const Js::LdElemInfo * ldElemInfo = nullptr; |
| |
| if (isProfiledInstr && newOpcode == Js::OpCode::IsIn) |
| { |
| if (!DoLoadInstructionArrayProfileInfo()) |
| { |
| isProfiledInstr = false; |
| } |
| else |
| { |
| ldElemInfo = this->m_func->GetReadOnlyProfileInfo()->GetLdElemInfo(profileId); |
| ValueType arrayType = ldElemInfo->GetArrayType(); |
| wasNotProfiled = !ldElemInfo->WasProfiled(); |
| |
| if (arrayType.IsLikelyNativeArray() && !AllowNativeArrayProfileInfo()) |
| { |
| arrayType = arrayType.SetArrayTypeId(Js::TypeIds_Array); |
| |
| // An opnd's value type will get replaced in the forward phase when it is not fixed. Store the array type in the ProfiledInstr. |
| Js::LdElemInfo *const newLdElemInfo = JitAnew(m_func->m_alloc, Js::LdElemInfo, *ldElemInfo); |
| newLdElemInfo->arrayType = arrayType; |
| ldElemInfo = newLdElemInfo; |
| } |
| |
| src2Opnd->SetValueType(arrayType); |
| |
| if (m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTry()) |
| { |
| isProfiledInstr = false; |
| } |
| } |
| } |
| |
| if (isProfiledInstr) |
| { |
| if (m_func->DoSimpleJitDynamicProfile()) |
| { |
| instr = IR::JitProfilingInstr::New(newOpcode, dstOpnd, src1Opnd, src2Opnd, m_func); |
| instr->AsJitProfilingInstr()->profileId = profileId; |
| } |
| else |
| { |
| instr = IR::ProfiledInstr::New(newOpcode, dstOpnd, src1Opnd, src2Opnd, m_func); |
| if (newOpcode == Js::OpCode::IsIn) |
| { |
| instr->AsProfiledInstr()->u.ldElemInfo = ldElemInfo; |
| } |
| else |
| { |
| instr->AsProfiledInstr()->u.profileId = profileId; |
| } |
| } |
| } |
| else |
| { |
| instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, src2Opnd, m_func); |
| } |
| |
| this->AddInstr(instr, offset); |
| |
| if (wasNotProfiled && DoBailOnNoProfile()) |
| { |
| InsertBailOnNoProfile(instr); |
| } |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::LdHandlerScope: |
| case Js::OpCode::NewScopeSlotsWithoutPropIds: |
| if (dstSym->m_isSingleDef) |
| { |
| dstSym->m_isNotNumber = true; |
| } |
| break; |
| |
| case Js::OpCode::LdInnerFrameDisplay: |
| if (dstSym->m_isSingleDef) |
| { |
| dstSym->m_isNotNumber = true; |
| } |
| break; |
| } |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildReg3C |
| /// |
| /// Build IR instr for a Reg3C instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildReg3C(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg3C<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->R0); |
| this->DoClosureRegCheck(layout->R1); |
| this->DoClosureRegCheck(layout->R2); |
| } |
| |
| BuildReg3C(newOpcode, offset, layout->R0, layout->R1, layout->R2, layout->inlineCacheIndex); |
| } |
| |
| void |
| IRBuilder::BuildReg3C(Js::OpCode newOpCode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot, |
| Js::RegSlot src2RegSlot, Js::CacheId inlineCacheIndex) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpCode)); |
| |
| IR::Instr * instr; |
| IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(src1RegSlot); |
| IR::RegOpnd * src2Opnd = this->BuildSrcOpnd(src2RegSlot); |
| IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot); |
| |
| instr = IR::Instr::New(Js::OpCode::ArgOut_A, IR::RegOpnd::New(TyVar, m_func), src2Opnd, m_func); |
| this->AddInstr(instr, offset); |
| |
| instr = IR::Instr::New(Js::OpCode::ArgOut_A, IR::RegOpnd::New(TyVar, m_func), src1Opnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| instr = IR::Instr::New(newOpCode, dstOpnd, IR::IntConstOpnd::New(inlineCacheIndex, TyUint32, m_func), instr->GetDst(), m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildReg4 |
| /// |
| /// Build IR instr for a Reg4 instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildReg4(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg4<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->R0); |
| this->DoClosureRegCheck(layout->R1); |
| this->DoClosureRegCheck(layout->R2); |
| this->DoClosureRegCheck(layout->R3); |
| } |
| |
| BuildReg4(newOpcode, offset, layout->R0, layout->R1, layout->R2, layout->R3); |
| } |
| |
| void |
| IRBuilder::BuildReg4(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot, |
| Js::RegSlot src2RegSlot, Js::RegSlot src3RegSlot) |
| { |
| IR::Instr * instr = nullptr; |
| Assert(newOpcode == Js::OpCode::Concat3 || newOpcode == Js::OpCode::Restify); |
| |
| IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(src1RegSlot); |
| IR::RegOpnd * src2Opnd = this->BuildSrcOpnd(src2RegSlot); |
| IR::RegOpnd * src3Opnd = this->BuildSrcOpnd(src3RegSlot); |
| |
| if (newOpcode == Js::OpCode::Restify) |
| { |
| IR::RegOpnd * src0Opnd = this->BuildSrcOpnd(dstRegSlot); |
| instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src3Opnd, m_func); |
| this->AddInstr(instr, offset); |
| |
| instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src2Opnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src1Opnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src0Opnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| IR::Opnd *firstArg = instr->GetDst(); |
| instr = IR::Instr::New(newOpcode, m_func); |
| instr->SetSrc1(firstArg); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| return; |
| } |
| |
| IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot); |
| |
| IR::RegOpnd * str1Opnd = InsertConvPrimStr(src1Opnd, offset, true); |
| IR::RegOpnd * str2Opnd = InsertConvPrimStr(src2Opnd, Js::Constants::NoByteCodeOffset, true); |
| IR::RegOpnd * str3Opnd = InsertConvPrimStr(src3Opnd, Js::Constants::NoByteCodeOffset, true); |
| |
| // Need to insert a byte code use for src1/src2 that if ConvPrimStr of the src2/src3 bail out |
| // we will restore it. |
| bool src1HasByteCodeRegSlot = src1Opnd->m_sym->HasByteCodeRegSlot(); |
| bool src2HasByteCodeRegSlot = src2Opnd->m_sym->HasByteCodeRegSlot(); |
| if (src1HasByteCodeRegSlot || src2HasByteCodeRegSlot) |
| { |
| IR::ByteCodeUsesInstr * byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, Js::Constants::NoByteCodeOffset); |
| if (src1HasByteCodeRegSlot) |
| { |
| byteCodeUse->Set(src1Opnd); |
| } |
| if (src2HasByteCodeRegSlot) |
| { |
| byteCodeUse->Set(src2Opnd); |
| } |
| this->AddInstr(byteCodeUse, Js::Constants::NoByteCodeOffset); |
| } |
| |
| if (!PHASE_OFF(Js::BackendConcatExprOptPhase, this->m_func)) |
| { |
| IR::RegOpnd* tmpDstOpnd1 = IR::RegOpnd::New(StackSym::New(this->m_func), TyVar, this->m_func); |
| IR::RegOpnd* tmpDstOpnd2 = IR::RegOpnd::New(StackSym::New(this->m_func), TyVar, this->m_func); |
| IR::RegOpnd* tmpDstOpnd3 = IR::RegOpnd::New(StackSym::New(this->m_func), TyVar, this->m_func); |
| |
| instr = IR::Instr::New(Js::OpCode::SetConcatStrMultiItemBE, tmpDstOpnd1, str1Opnd, m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| instr = IR::Instr::New(Js::OpCode::SetConcatStrMultiItemBE, tmpDstOpnd2, str2Opnd, tmpDstOpnd1, m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| instr = IR::Instr::New(Js::OpCode::SetConcatStrMultiItemBE, tmpDstOpnd3, str3Opnd, tmpDstOpnd2, m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| IR::IntConstOpnd * countIntConstOpnd = IR::IntConstOpnd::New(3, TyUint32, m_func, true); |
| instr = IR::Instr::New(Js::OpCode::NewConcatStrMultiBE, dstOpnd, countIntConstOpnd, tmpDstOpnd3, m_func); |
| dstOpnd->SetValueType(ValueType::String); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| } |
| else |
| { |
| instr = IR::Instr::New(Js::OpCode::NewConcatStrMulti, dstOpnd, IR::IntConstOpnd::New(3, TyUint32, m_func, true), m_func); |
| dstOpnd->SetValueType(ValueType::String); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| instr = IR::Instr::New(Js::OpCode::SetConcatStrMultiItem, IR::IndirOpnd::New(dstOpnd, 0, TyVar, m_func), str1Opnd, m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| instr = IR::Instr::New(Js::OpCode::SetConcatStrMultiItem, IR::IndirOpnd::New(dstOpnd, 1, TyVar, m_func), str2Opnd, m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| instr = IR::Instr::New(Js::OpCode::SetConcatStrMultiItem, IR::IndirOpnd::New(dstOpnd, 2, TyVar, m_func), str3Opnd, m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| } |
| } |
| |
| IR::RegOpnd * |
| IRBuilder::InsertConvPrimStr(IR::RegOpnd * srcOpnd, uint offset, bool forcePreOpBailOutIfNeeded) |
| { |
| IR::RegOpnd * strOpnd = IR::RegOpnd::New(TyVar, this->m_func); |
| IR::Instr * instr = IR::Instr::New(Js::OpCode::Conv_PrimStr, strOpnd, srcOpnd, m_func); |
| instr->forcePreOpBailOutIfNeeded = forcePreOpBailOutIfNeeded; |
| strOpnd->SetValueType(ValueType::String); |
| strOpnd->SetValueTypeFixed(); |
| this->AddInstr(instr, offset); |
| return strOpnd; |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildReg2B1(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg2B1<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->R0); |
| this->DoClosureRegCheck(layout->R1); |
| } |
| |
| BuildReg2B1(newOpcode, offset, layout->R0, layout->R1, layout->B2); |
| } |
| |
| void |
| IRBuilder::BuildReg2B1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot srcRegSlot, byte index) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| Assert(newOpcode == Js::OpCode::SetConcatStrMultiItem); |
| |
| IR::Instr * instr; |
| IR::RegOpnd * srcOpnd = this->BuildSrcOpnd(srcRegSlot); |
| IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot); |
| |
| IR::IndirOpnd * indir1Opnd = IR::IndirOpnd::New(dstOpnd, index, TyVar, m_func); |
| |
| dstOpnd->SetValueType(ValueType::String); |
| |
| instr = IR::Instr::New(Js::OpCode::SetConcatStrMultiItem, indir1Opnd, InsertConvPrimStr(srcOpnd, offset, true), m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildReg3B1(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg3B1<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->R0); |
| this->DoClosureRegCheck(layout->R1); |
| this->DoClosureRegCheck(layout->R2); |
| } |
| |
| BuildReg3B1(newOpcode, offset, layout->R0, layout->R1, layout->R2, layout->B3); |
| } |
| |
| void |
| IRBuilder::BuildReg3B1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot, |
| Js::RegSlot src2RegSlot, uint8 index) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| IR::Instr * instr; |
| IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(src1RegSlot); |
| IR::RegOpnd * src2Opnd = this->BuildSrcOpnd(src2RegSlot); |
| IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot); |
| dstOpnd->SetValueType(ValueType::String); |
| |
| IR::Instr * newConcatStrMulti = nullptr; |
| switch (newOpcode) |
| { |
| case Js::OpCode::NewConcatStrMulti: |
| |
| newConcatStrMulti = IR::Instr::New(Js::OpCode::NewConcatStrMulti, dstOpnd, IR::IntConstOpnd::New(index, TyUint32, m_func), m_func); |
| index = 0; |
| break; |
| case Js::OpCode::SetConcatStrMultiItem2: |
| break; |
| default: |
| Assert(false); |
| }; |
| IR::IndirOpnd * indir1Opnd = IR::IndirOpnd::New(dstOpnd, index, TyVar, m_func); |
| IR::IndirOpnd * indir2Opnd = IR::IndirOpnd::New(dstOpnd, index + 1, TyVar, m_func); |
| |
| // Need to do the to str first, as they may have side effects. |
| IR::RegOpnd * str1Opnd = InsertConvPrimStr(src1Opnd, offset, true); |
| IR::RegOpnd * str2Opnd = InsertConvPrimStr(src2Opnd, Js::Constants::NoByteCodeOffset, true); |
| |
| // Need to insert a byte code use for src1 so that if ConvPrimStr of the src2 bail out |
| // we will restore it. |
| if (src1Opnd->m_sym->HasByteCodeRegSlot()) |
| { |
| IR::ByteCodeUsesInstr * byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, Js::Constants::NoByteCodeOffset); |
| byteCodeUse->Set(src1Opnd); |
| this->AddInstr(byteCodeUse, Js::Constants::NoByteCodeOffset); |
| } |
| |
| if (newConcatStrMulti) |
| { |
| // Allocate the concat str after the ConvToStr |
| this->AddInstr(newConcatStrMulti, Js::Constants::NoByteCodeOffset); |
| } |
| instr = IR::Instr::New(Js::OpCode::SetConcatStrMultiItem, indir1Opnd, str1Opnd, m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| instr = IR::Instr::New(Js::OpCode::SetConcatStrMultiItem, indir2Opnd, str2Opnd, m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildReg5 |
| /// |
| /// Build IR instr for a Reg5 instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildReg5(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg5<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->R0); |
| this->DoClosureRegCheck(layout->R1); |
| this->DoClosureRegCheck(layout->R2); |
| this->DoClosureRegCheck(layout->R3); |
| this->DoClosureRegCheck(layout->R4); |
| } |
| |
| BuildReg5(newOpcode, offset, layout->R0, layout->R1, layout->R2, layout->R3, layout->R4); |
| } |
| |
| void |
| IRBuilder::BuildReg5(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot, |
| Js::RegSlot src2RegSlot, Js::RegSlot src3RegSlot, Js::RegSlot src4RegSlot) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| IR::Instr * instr; |
| IR::RegOpnd * dstOpnd; |
| IR::RegOpnd * src1Opnd; |
| IR::RegOpnd * src2Opnd; |
| IR::RegOpnd * src3Opnd; |
| IR::RegOpnd * src4Opnd; |
| // We can't support instructions with more than 2 srcs. Instead create a CallHelper instructions, |
| // and pass the srcs as ArgOut_A instructions. |
| src1Opnd = this->BuildSrcOpnd(src1RegSlot); |
| src2Opnd = this->BuildSrcOpnd(src2RegSlot); |
| src3Opnd = this->BuildSrcOpnd(src3RegSlot); |
| src4Opnd = this->BuildSrcOpnd(src4RegSlot); |
| dstOpnd = this->BuildDstOpnd(dstRegSlot); |
| |
| instr = IR::Instr::New(Js::OpCode::ArgOut_A, IR::RegOpnd::New(TyVar, m_func), src4Opnd, m_func); |
| this->AddInstr(instr, offset); |
| |
| instr = IR::Instr::New(Js::OpCode::ArgOut_A, IR::RegOpnd::New(TyVar, m_func), src3Opnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| instr = IR::Instr::New(Js::OpCode::ArgOut_A, IR::RegOpnd::New(TyVar, m_func), src2Opnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| instr = IR::Instr::New(Js::OpCode::ArgOut_A, IR::RegOpnd::New(TyVar, m_func), src1Opnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| IR::HelperCallOpnd *helperOpnd; |
| |
| switch (newOpcode) { |
| case Js::OpCode::ApplyArgs: |
| helperOpnd=IR::HelperCallOpnd::New(IR::HelperOp_OP_ApplyArgs, this->m_func); |
| break; |
| default: |
| AssertMsg(UNREACHED, "Unknown Reg5 opcode"); |
| Fatal(); |
| } |
| instr = IR::Instr::New(Js::OpCode::CallHelper, dstOpnd, helperOpnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| } |
| |
| void |
| IRBuilder::BuildW1(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| unsigned short C1; |
| |
| const unaligned Js::OpLayoutW1 *regLayout = m_jnReader.W1(); |
| C1 = regLayout->C1; |
| |
| IR::Instr * instr; |
| IntConstType value = C1; |
| IR::IntConstOpnd * srcOpnd; |
| |
| srcOpnd = IR::IntConstOpnd::New(value, TyInt32, m_func); |
| instr = IR::Instr::New(newOpcode, m_func); |
| instr->SetSrc1(srcOpnd); |
| |
| this->AddInstr(instr, offset); |
| |
| if (newOpcode == Js::OpCode::RuntimeReferenceError || newOpcode == Js::OpCode::RuntimeTypeError) |
| { |
| if (DoBailOnNoProfile()) |
| { |
| // RuntimeReferenceError are extremely rare as they are guaranteed to throw. Insert BailonNoProfile to optimize this code path. |
| // If there are continues bailout bailonnoprofile will be disabled. |
| InsertBailOnNoProfile(instr); |
| } |
| } |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildUnsigned1(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Unsigned1<SizePolicy>>(); |
| BuildUnsigned1(newOpcode, offset, layout->C1); |
| } |
| |
| void |
| IRBuilder::BuildUnsigned1(Js::OpCode newOpcode, uint32 offset, uint32 num) |
| { |
| switch (newOpcode) |
| { |
| case Js::OpCode::EmitTmpRegCount: |
| // Note: EmitTmpRegCount is inserted when debugging, not needed for jit. |
| // It's only needed by the debugger to see how many tmp regs are active. |
| Assert(m_func->IsJitInDebugMode()); |
| return; |
| |
| case Js::OpCode::NewBlockScope: |
| case Js::OpCode::NewPseudoScope: |
| { |
| if (num >= m_func->GetJITFunctionBody()->GetInnerScopeCount()) |
| { |
| Js::Throw::FatalInternalError(); |
| } |
| Js::RegSlot dstRegSlot = num + m_func->GetJITFunctionBody()->GetFirstInnerScopeReg(); |
| IR::RegOpnd * dstOpnd = BuildDstOpnd(dstRegSlot); |
| IR::Instr * instr = IR::Instr::New(newOpcode, dstOpnd, m_func); |
| this->AddInstr(instr, offset); |
| if (dstOpnd->m_sym->m_isSingleDef) |
| { |
| dstOpnd->m_sym->m_isNotNumber = true; |
| } |
| break; |
| } |
| |
| case Js::OpCode::CloneInnerScopeSlots: |
| case Js::OpCode::CloneBlockScope: |
| { |
| if (num >= m_func->GetJITFunctionBody()->GetInnerScopeCount()) |
| { |
| Js::Throw::FatalInternalError(); |
| } |
| Js::RegSlot srcRegSlot = num + m_func->GetJITFunctionBody()->GetFirstInnerScopeReg(); |
| IR::RegOpnd * srcOpnd = BuildSrcOpnd(srcRegSlot); |
| IR::Instr * instr = IR::Instr::New(newOpcode, m_func); |
| instr->SetSrc1(srcOpnd); |
| this->AddInstr(instr, offset); |
| break; |
| } |
| |
| case Js::OpCode::ProfiledLoopBodyStart: |
| { |
| // This opcode is removed from the IR when we aren't doing Profiling SimpleJit or not jitting loop bodies |
| if (m_func->DoSimpleJitDynamicProfile() && m_func->GetJITFunctionBody()->DoJITLoopBody()) |
| { |
| // Attach a register to the dest of this instruction to communicate whether we should bail out (the deciding of this is done in lowering) |
| IR::Opnd* fullJitExists = IR::RegOpnd::New(TyUint8, m_func); |
| auto start = m_lastInstr; |
| |
| if (start->m_opcode == Js::OpCode::InitLoopBodyCount) |
| { |
| Assert(this->IsLoopBody()); |
| start = start->m_prev; |
| } |
| |
| Assert(start->m_opcode == Js::OpCode::ProfiledLoopStart && start->GetDst()); |
| IR::JitProfilingInstr* instr = IR::JitProfilingInstr::New(Js::OpCode::ProfiledLoopBodyStart, fullJitExists, start->GetDst(), m_func); |
| // profileId is used here to represent the loop number |
| instr->loopNumber = num; |
| this->AddInstr(instr, offset); |
| |
| // If fullJitExists isn't 0, bail out so that we can get the fulljitted version |
| BailOutInfo * bailOutInfo = JitAnew(m_func->m_alloc, BailOutInfo, instr->GetByteCodeOffset(), m_func); |
| IR::BailOutInstr * bailInstr = IR::BailOutInstr::New(Js::OpCode::BailOnNotEqual, IR::BailOnSimpleJitToFullJitLoopBody, bailOutInfo, bailOutInfo->bailOutFunc); |
| bailInstr->SetSrc1(fullJitExists); |
| bailInstr->SetSrc2(IR::IntConstOpnd::New(0, TyUint8, m_func, true)); |
| this->AddInstr(bailInstr, offset); |
| |
| } |
| |
| Js::ImplicitCallFlags flags = Js::ImplicitCall_HasNoInfo; |
| Js::LoopFlags loopFlags; |
| if (this->m_func->HasProfileInfo()) |
| { |
| flags = m_func->GetReadOnlyProfileInfo()->GetLoopImplicitCallFlags(num); |
| loopFlags = m_func->GetReadOnlyProfileInfo()->GetLoopFlags(num); |
| } |
| |
| // Put a label the instruction stream to carry the profile info |
| IR::ProfiledLabelInstr * labelInstr = IR::ProfiledLabelInstr::New(Js::OpCode::Label, this->m_func, flags, loopFlags); |
| #if DBG |
| labelInstr->loopNum = num; |
| #endif |
| m_lastInstr->InsertAfter(labelInstr); |
| m_lastInstr = labelInstr; |
| |
| // Set it to the offset to the start of the loop |
| labelInstr->SetByteCodeOffset(m_jnReader.GetCurrentOffset()); |
| break; |
| } |
| |
| case Js::OpCode::LoopBodyStart: |
| break; |
| |
| case Js::OpCode::ProfiledLoopStart: |
| { |
| AssertOrFailFast(num < m_func->GetJITFunctionBody()->GetLoopCount()); |
| // If we're in profiling SimpleJit and jitting loop bodies, we need to keep this until lowering. |
| if (m_func->DoSimpleJitDynamicProfile() && m_func->GetJITFunctionBody()->DoJITLoopBody()) |
| { |
| // In order for the JIT engine to correctly allocate registers we need to have this set up before lowering. |
| |
| // There may be 0 to many LoopEnds, but there will only ever be one LoopStart |
| Assert(!this->m_saveLoopImplicitCallFlags[num]); |
| |
| const auto ty = Lowerer::GetImplicitCallFlagsType(); |
| auto saveOpnd = IR::RegOpnd::New(ty, m_func); |
| this->m_saveLoopImplicitCallFlags[num] = saveOpnd; |
| // Note that we insert this instruction /before/ the actual ProfiledLoopStart opcode. This is because Lowering is backwards |
| // and this is just a fake instruction which is only used to pass on the saveOpnd; this instruction will eventually be removed. |
| auto instr = IR::JitProfilingInstr::New(Js::OpCode::Ld_A, saveOpnd, IR::MemRefOpnd::New((intptr_t)0, ty, m_func), m_func); |
| instr->isLoopHelper = true; |
| this->AddInstr(instr, offset); |
| |
| instr = IR::JitProfilingInstr::New(Js::OpCode::ProfiledLoopStart, IR::RegOpnd::New(TyMachPtr, m_func), nullptr, m_func); |
| instr->loopNumber = num; |
| this->AddInstr(instr, offset); |
| } |
| |
| if (this->IsLoopBody() && !m_loopCounterSym) |
| { |
| InsertInitLoopBodyLoopCounter(num); |
| } |
| break; |
| } |
| |
| case Js::OpCode::ProfiledLoopEnd: |
| { |
| AssertOrFailFast(num < m_func->GetJITFunctionBody()->GetLoopCount()); |
| // TODO: Decide whether we want the implicit loop call flags to be recorded in simplejitted loop bodies |
| if (m_func->DoSimpleJitDynamicProfile() && m_func->GetJITFunctionBody()->DoJITLoopBody()) |
| { |
| Assert(this->m_saveLoopImplicitCallFlags[num]); |
| |
| // In profiling simplejit we need this opcode in order to restore the implicit call flags |
| auto instr = IR::JitProfilingInstr::New(Js::OpCode::ProfiledLoopEnd, nullptr, this->m_saveLoopImplicitCallFlags[num], m_func); |
| this->AddInstr(instr, offset); |
| instr->loopNumber = num; |
| } |
| |
| if (!this->IsLoopBody()) |
| { |
| break; |
| } |
| |
| // In the early exit case (return), we generated ProfiledLoopEnd for all the outer loop. |
| // If we see one of these profile loop, just load the IP of the immediate outer loop of the loop body being JIT'ed |
| // and then skip all the other loops using the fact that we have already loaded the return IP |
| |
| if (IsLoopBodyReturnIPInstr(m_lastInstr)) |
| { |
| // Already loaded the loop IP sym, skip |
| break; |
| } |
| |
| // See we are ending an outer loop and load the return IP to the ProfiledLoopEnd opcode |
| // instead of following the normal branch |
| |
| const JITLoopHeaderIDL * loopHeader = m_func->GetJITFunctionBody()->GetLoopHeaderData(num); |
| if (m_func->GetJITFunctionBody()->GetLoopHeaderAddr(num) != m_func->m_workItem->GetLoopHeaderAddr() && |
| JITTimeFunctionBody::LoopContains(loopHeader, m_func->m_workItem->GetLoopHeader())) |
| { |
| this->InsertLoopBodyReturnIPInstr(offset, offset); |
| } |
| else |
| { |
| Assert(JITTimeFunctionBody::LoopContains(m_func->m_workItem->GetLoopHeader(), loopHeader)); |
| } |
| break; |
| } |
| |
| case Js::OpCode::InvalCachedScope: |
| { |
| // The reg and constant are both src operands. |
| IR::Instr* instr = IR::Instr::New(Js::OpCode::InvalCachedScope, m_func); |
| IR::RegOpnd *envOpnd = this->BuildSrcOpnd(m_func->GetJITFunctionBody()->GetEnvReg()); |
| instr->SetSrc1(envOpnd); |
| IR::IntConstOpnd *envIndex = IR::IntConstOpnd::New(num, TyInt32, m_func, true); |
| instr->SetSrc2(envIndex); |
| this->AddInstr(instr, offset); |
| return; |
| } |
| |
| default: |
| Assert(false); |
| __assume(false); |
| } |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiledReg1Unsigned1(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_Reg1Unsigned1<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->R0); |
| } |
| |
| BuildProfiledReg1Unsigned1(newOpcode, offset, layout->R0, layout->C1, layout->profileId); |
| } |
| |
| void |
| IRBuilder::BuildProfiledReg1Unsigned1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0, int32 C1, Js::ProfileId profileId) |
| { |
| Assert(newOpcode == Js::OpCode::ProfiledNewScArray || newOpcode == Js::OpCode::ProfiledInitForInEnumerator); |
| Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode); |
| |
| if (newOpcode == Js::OpCode::InitForInEnumerator) |
| { |
| IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(R0); |
| IR::Opnd * src2Opnd = this->BuildForInEnumeratorOpnd(C1); |
| IR::Instr *instr = IR::ProfiledInstr::New(Js::OpCode::InitForInEnumerator, nullptr, src1Opnd, src2Opnd, m_func); |
| instr->AsProfiledInstr()->u.profileId = profileId; |
| this->AddInstr(instr, offset); |
| return; |
| } |
| |
| IR::Instr *instr; |
| Js::RegSlot dstRegSlot = R0; |
| IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot); |
| |
| StackSym * dstSym = dstOpnd->m_sym; |
| int32 value = C1; |
| IR::IntConstOpnd * srcOpnd; |
| |
| srcOpnd = IR::IntConstOpnd::New(value, TyInt32, m_func); |
| if (m_func->DoSimpleJitDynamicProfile()) |
| { |
| instr = IR::JitProfilingInstr::New(newOpcode, dstOpnd, srcOpnd, m_func); |
| instr->AsJitProfilingInstr()->profileId = profileId; |
| } |
| else |
| { |
| instr = IR::ProfiledInstr::New(newOpcode, dstOpnd, srcOpnd, m_func); |
| instr->AsProfiledInstr()->u.profileId = profileId; |
| } |
| |
| this->AddInstr(instr, offset); |
| |
| if (dstSym->m_isSingleDef) |
| { |
| dstSym->m_isSafeThis = true; |
| dstSym->m_isNotNumber = true; |
| } |
| |
| // Undefined values in array literals ([0, undefined, 1]) are treated as missing values in some versions |
| Js::ArrayCallSiteInfo *arrayInfo = nullptr; |
| if (m_func->HasArrayInfo()) |
| { |
| arrayInfo = m_func->GetReadOnlyProfileInfo()->GetArrayCallSiteInfo(profileId); |
| } |
| Js::TypeId arrayTypeId = Js::TypeIds_Array; |
| if (arrayInfo && !m_func->IsJitInDebugMode() && Js::JavascriptArray::HasInlineHeadSegment(value)) |
| { |
| if (arrayInfo->IsNativeIntArray()) |
| { |
| arrayTypeId = Js::TypeIds_NativeIntArray; |
| } |
| else if (arrayInfo->IsNativeFloatArray()) |
| { |
| arrayTypeId = Js::TypeIds_NativeFloatArray; |
| } |
| } |
| dstOpnd->SetValueType(ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(true).SetArrayTypeId(arrayTypeId)); |
| if (dstOpnd->GetValueType().HasVarElements()) |
| { |
| dstOpnd->SetValueTypeFixed(); |
| } |
| else |
| { |
| dstOpnd->SetValueType(dstOpnd->GetValueType().ToLikely()); |
| } |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildReg1Unsigned1(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg1Unsigned1<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->R0); |
| } |
| |
| BuildReg1Unsigned1(newOpcode, offset, layout->R0, layout->C1); |
| } |
| |
| void |
| IRBuilder::BuildReg1Unsigned1(Js::OpCode newOpcode, uint offset, Js::RegSlot R0, int32 C1) |
| { |
| switch (newOpcode) |
| { |
| case Js::OpCode::NewRegEx: |
| this->BuildRegexFromPattern(R0, C1, offset); |
| return; |
| |
| case Js::OpCode::LdInnerScope: |
| { |
| IR::RegOpnd * srcOpnd = BuildSrcOpnd(this->InnerScopeIndexToRegSlot(C1)); |
| IR::RegOpnd * dstOpnd = BuildDstOpnd(R0); |
| IR::Instr * instr = IR::Instr::New(Js::OpCode::Ld_A, dstOpnd, srcOpnd, m_func); |
| if (dstOpnd->m_sym->m_isSingleDef) |
| { |
| dstOpnd->m_sym->m_isNotNumber = true; |
| } |
| this->AddInstr(instr, offset); |
| return; |
| } |
| |
| case Js::OpCode::LdIndexedFrameDisplayNoParent: |
| { |
| newOpcode = Js::OpCode::LdFrameDisplay; |
| IR::RegOpnd *srcOpnd = this->BuildSrcOpnd(this->InnerScopeIndexToRegSlot(C1)); |
| IR::RegOpnd *dstOpnd = this->BuildDstOpnd(R0); |
| IR::Instr *instr = IR::Instr::New(newOpcode, dstOpnd, srcOpnd, m_func); |
| this->AddEnvOpndForInnerFrameDisplay(instr, offset); |
| if (dstOpnd->m_sym->m_isSingleDef) |
| { |
| dstOpnd->m_sym->m_isNotNumber = true; |
| } |
| this->AddInstr(instr, offset); |
| return; |
| } |
| |
| case Js::OpCode::GetCachedFunc: |
| { |
| IR::RegOpnd *src1Opnd = this->BuildSrcOpnd(m_func->GetJITFunctionBody()->GetLocalClosureReg()); |
| IR::Opnd *src2Opnd = IR::IntConstOpnd::New(C1, TyUint32, m_func); |
| IR::RegOpnd *dstOpnd = this->BuildDstOpnd(R0); |
| IR::Instr *instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, src2Opnd, m_func); |
| if (dstOpnd->m_sym->m_isSingleDef) |
| { |
| dstOpnd->m_sym->m_isNotNumber = true; |
| } |
| this->AddInstr(instr, offset); |
| return; |
| } |
| |
| case Js::OpCode::InitForInEnumerator: |
| { |
| IR::Instr *instr = IR::Instr::New(Js::OpCode::InitForInEnumerator, m_func); |
| instr->SetSrc1(this->BuildSrcOpnd(R0)); |
| instr->SetSrc2(this->BuildForInEnumeratorOpnd(C1)); |
| this->AddInstr(instr, offset); |
| return; |
| } |
| } |
| |
| |
| IR::RegOpnd * dstOpnd = this->BuildDstOpnd(R0); |
| |
| StackSym * dstSym = dstOpnd->m_sym; |
| IntConstType value = C1; |
| IR::IntConstOpnd * srcOpnd = IR::IntConstOpnd::New(value, TyInt32, m_func); |
| IR::Instr * instr = IR::Instr::New(newOpcode, dstOpnd, srcOpnd, m_func); |
| |
| this->AddInstr(instr, offset); |
| |
| if (newOpcode == Js::OpCode::NewScopeSlots) |
| { |
| this->AddInstr( |
| IR::Instr::New( |
| Js::OpCode::Ld_A, IR::RegOpnd::New(m_func->GetLocalClosureSym(), TyVar, m_func), dstOpnd, m_func), |
| (uint32)-1); |
| } |
| |
| if (dstSym->m_isSingleDef) |
| { |
| switch (newOpcode) |
| { |
| case Js::OpCode::NewScArray: |
| case Js::OpCode::NewScArrayWithMissingValues: |
| dstSym->m_isSafeThis = true; |
| dstSym->m_isNotNumber = true; |
| break; |
| } |
| } |
| |
| if (newOpcode == Js::OpCode::NewScArray || newOpcode == Js::OpCode::NewScArrayWithMissingValues) |
| { |
| // Undefined values in array literals ([0, undefined, 1]) are treated as missing values in some versions |
| dstOpnd->SetValueType( |
| ValueType::GetObject(ObjectType::Array) |
| .SetHasNoMissingValues(newOpcode == Js::OpCode::NewScArray) |
| .SetArrayTypeId(Js::TypeIds_Array)); |
| dstOpnd->SetValueTypeFixed(); |
| } |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildReg2Int1 |
| /// |
| /// Build IR instr for a Reg2I4 instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildReg2Int1(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Reg2Int1<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->R0); |
| this->DoClosureRegCheck(layout->R1); |
| } |
| |
| BuildReg2Int1(newOpcode, offset, layout->R0, layout->R1, layout->C1); |
| } |
| |
| void |
| IRBuilder::BuildReg2Int1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot srcRegSlot, int32 value) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| IR::Instr * instr; |
| |
| if (newOpcode == Js::OpCode::LdIndexedFrameDisplay) |
| { |
| newOpcode = Js::OpCode::LdFrameDisplay; |
| if ((uint)value >= m_func->GetJITFunctionBody()->GetInnerScopeCount()) |
| { |
| Js::Throw::FatalInternalError(); |
| } |
| IR::RegOpnd *src1Opnd = this->BuildSrcOpnd(value + m_func->GetJITFunctionBody()->GetFirstInnerScopeReg()); |
| IR::RegOpnd *src2Opnd = this->BuildSrcOpnd(srcRegSlot); |
| IR::RegOpnd *dstOpnd = this->BuildDstOpnd(dstRegSlot); |
| instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, src2Opnd, m_func); |
| if (dstOpnd->m_sym->m_isSingleDef) |
| { |
| dstOpnd->m_sym->m_isNotNumber = true; |
| } |
| this->AddInstr(instr, offset); |
| return; |
| } |
| |
| IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(srcRegSlot); |
| IR::IntConstOpnd * src2Opnd = IR::IntConstOpnd::New(value, TyInt32, m_func); |
| IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot); |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::ProfiledLdThis: |
| newOpcode = Js::OpCode::LdThis; |
| if(m_func->HasProfileInfo()) |
| { |
| dstOpnd->SetValueType(m_func->GetReadOnlyProfileInfo()->GetThisInfo().valueType); |
| } |
| |
| if(m_func->DoSimpleJitDynamicProfile()) |
| { |
| instr = IR::JitProfilingInstr::New(newOpcode, dstOpnd, src1Opnd, src2Opnd, m_func); |
| |
| // Break out since we just made the instr |
| break; |
| } |
| // fall-through |
| |
| |
| default: |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, src2Opnd, m_func); |
| break; |
| } |
| |
| this->AddInstr(instr, offset); |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildElementC |
| /// |
| /// Build IR instr for an ElementC instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildElementScopedC(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementScopedC<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| } |
| |
| BuildElementScopedC(newOpcode, offset, layout->Value, layout->PropertyIdIndex); |
| } |
| |
| void |
| IRBuilder::BuildElementScopedC(Js::OpCode newOpcode, uint32 offset, Js::RegSlot regSlot, Js::PropertyIdIndexType propertyIdIndex) |
| { |
| IR::Instr * instr; |
| Js::PropertyId propertyId = m_func->GetJITFunctionBody()->GetReferencedPropertyId(propertyIdIndex); |
| PropertyKind propertyKind = PropertyKindData; |
| IR::RegOpnd * regOpnd; |
| Js::RegSlot fieldRegSlot = this->GetEnvRegForEvalCode(); |
| IR::SymOpnd * fieldSymOpnd = this->BuildFieldOpnd(newOpcode, fieldRegSlot, propertyId, propertyIdIndex, propertyKind); |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::ScopedEnsureNoRedeclFld: |
| { |
| regOpnd = this->BuildSrcOpnd(regSlot); |
| instr = IR::Instr::New(newOpcode, fieldSymOpnd, regOpnd, m_func); |
| break; |
| } |
| |
| case Js::OpCode::ScopedDeleteFld: |
| case Js::OpCode::ScopedDeleteFldStrict: |
| { |
| Assert(this->m_func->GetScriptContextInfo()->GetAddr() == this->m_func->GetTopFunc()->GetScriptContextInfo()->GetAddr()); |
| regOpnd = this->BuildDstOpnd(regSlot); |
| instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, m_func); |
| break; |
| } |
| |
| case Js::OpCode::ScopedInitFunc: |
| { |
| // Implicit root object as default instance |
| IR::Opnd * instance2Opnd = this->BuildSrcOpnd(Js::FunctionBody::RootObjectRegSlot); |
| regOpnd = this->BuildSrcOpnd(regSlot); |
| instr = IR::Instr::New(newOpcode, fieldSymOpnd, regOpnd, instance2Opnd, m_func); |
| break; |
| } |
| |
| default: |
| AssertMsg(UNREACHED, "Unknown ElementScopedC opcode"); |
| Fatal(); |
| } |
| |
| this->AddInstr(instr, offset); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildElementC(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementC<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| this->DoClosureRegCheck(layout->Instance); |
| } |
| |
| BuildElementC(newOpcode, offset, layout->Instance, layout->Value, layout->PropertyIdIndex); |
| } |
| |
| void |
| IRBuilder::BuildElementC(Js::OpCode newOpcode, uint32 offset, Js::RegSlot fieldRegSlot, Js::RegSlot regSlot, Js::PropertyIdIndexType propertyIdIndex) |
| { |
| IR::Instr * instr; |
| Js::PropertyId propertyId = m_func->GetJITFunctionBody()->GetReferencedPropertyId(propertyIdIndex); |
| PropertyKind propertyKind = PropertyKindData; |
| IR::SymOpnd * fieldSymOpnd = this->BuildFieldOpnd(newOpcode, fieldRegSlot, propertyId, propertyIdIndex, propertyKind); |
| IR::RegOpnd * regOpnd; |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::DeleteFld: |
| case Js::OpCode::DeleteRootFld: |
| case Js::OpCode::DeleteFldStrict: |
| case Js::OpCode::DeleteRootFldStrict: |
| // Load |
| regOpnd = this->BuildDstOpnd(regSlot); |
| instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, m_func); |
| break; |
| |
| case Js::OpCode::InitSetFld: |
| case Js::OpCode::InitGetFld: |
| case Js::OpCode::InitClassMemberGet: |
| case Js::OpCode::InitClassMemberSet: |
| case Js::OpCode::InitProto: |
| case Js::OpCode::StFuncExpr: |
| // Store |
| regOpnd = this->BuildSrcOpnd(regSlot); |
| instr = IR::Instr::New(newOpcode, fieldSymOpnd, regOpnd, m_func); |
| break; |
| |
| default: |
| AssertMsg(UNREACHED, "Unknown ElementC opcode"); |
| Fatal(); |
| } |
| |
| this->AddInstr(instr, offset); |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildElementSlot |
| /// |
| /// Build IR instr for an ElementSlot instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| IR::Instr * |
| IRBuilder::BuildProfiledSlotLoad(Js::OpCode loadOp, IR::RegOpnd *dstOpnd, IR::SymOpnd *srcOpnd, Js::ProfileId profileId, bool *pUnprofiled) |
| { |
| IR::Instr * instr = nullptr; |
| |
| if (m_func->DoSimpleJitDynamicProfile()) |
| { |
| instr = IR::JitProfilingInstr::New(loadOp, dstOpnd, srcOpnd, m_func); |
| instr->AsJitProfilingInstr()->profileId = profileId; |
| } |
| else if(this->m_func->HasProfileInfo()) |
| { |
| instr = IR::ProfiledInstr::New(loadOp, dstOpnd, srcOpnd, m_func); |
| instr->AsProfiledInstr()->u.FldInfo().valueType = |
| this->m_func->GetReadOnlyProfileInfo()->GetSlotLoad(profileId); |
| *pUnprofiled = instr->AsProfiledInstr()->u.FldInfo().valueType.IsUninitialized(); |
| #if ENABLE_DEBUG_CONFIG_OPTIONS |
| if(Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::DynamicProfilePhase)) |
| { |
| const ValueType valueType(instr->AsProfiledInstr()->u.FldInfo().valueType); |
| char valueTypeStr[VALUE_TYPE_MAX_STRING_SIZE]; |
| valueType.ToString(valueTypeStr); |
| char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE]; |
| Output::Print(_u("TestTrace function %s (#%s) ValueType = %S "), m_func->GetJITFunctionBody()->GetDisplayName(), m_func->GetDebugNumberSet(debugStringBuffer), valueTypeStr); |
| instr->DumpTestTrace(); |
| } |
| #endif |
| } |
| |
| return instr; |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildElementSlot(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementSlot<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| this->DoClosureRegCheck(layout->Instance); |
| } |
| |
| BuildElementSlot(newOpcode, offset, layout->Instance, layout->Value, layout->SlotIndex, Js::Constants::NoProfileId); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiledElementSlot(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_ElementSlot<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| this->DoClosureRegCheck(layout->Instance); |
| } |
| |
| Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode); |
| BuildElementSlot(newOpcode, offset, layout->Instance, layout->Value, layout->SlotIndex, layout->profileId); |
| } |
| |
| void |
| IRBuilder::BuildElementSlot(Js::OpCode newOpcode, uint32 offset, Js::RegSlot fieldRegSlot, Js::RegSlot regSlot, |
| int32 slotId, Js::ProfileId profileId) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| IR::Instr * instr; |
| IR::RegOpnd * regOpnd; |
| |
| IR::SymOpnd * fieldSymOpnd; |
| PropertyKind propertyKind = PropertyKindSlots; |
| PropertySym * fieldSym; |
| StackSym * stackFuncPtrSym = nullptr; |
| bool isLdSlotThatWasNotProfiled = false; |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::NewInnerStackScFunc: |
| stackFuncPtrSym = this->EnsureStackFuncPtrSym(); |
| // fall through |
| case Js::OpCode::NewInnerScFunc: |
| newOpcode = Js::OpCode::NewScFunc; |
| goto NewScFuncCommon; |
| |
| case Js::OpCode::NewInnerScGenFunc: |
| newOpcode = Js::OpCode::NewScGenFunc; |
| NewScFuncCommon: |
| { |
| IR::Opnd * functionBodySlotOpnd = IR::IntConstOpnd::New(slotId, TyInt32, m_func, true); |
| IR::Opnd * environmentOpnd = this->BuildSrcOpnd(fieldRegSlot); |
| regOpnd = this->BuildDstOpnd(regSlot); |
| if (stackFuncPtrSym) |
| { |
| IR::RegOpnd * dataOpnd = IR::RegOpnd::New(TyVar, m_func); |
| instr = IR::Instr::New(Js::OpCode::NewScFuncData, dataOpnd, environmentOpnd, IR::RegOpnd::New(stackFuncPtrSym, TyVar, m_func), m_func); |
| this->AddInstr(instr, offset); |
| |
| instr = IR::Instr::New(newOpcode, regOpnd, functionBodySlotOpnd, dataOpnd, m_func); |
| } |
| else |
| { |
| instr = IR::Instr::New(newOpcode, regOpnd, functionBodySlotOpnd, environmentOpnd, m_func); |
| } |
| if (regOpnd->m_sym->m_isSingleDef) |
| { |
| regOpnd->m_sym->m_isSafeThis = true; |
| regOpnd->m_sym->m_isNotNumber = true; |
| } |
| this->AddInstr(instr, offset); |
| return; |
| } |
| |
| case Js::OpCode::NewScFuncHomeObj: |
| case Js::OpCode::NewScGenFuncHomeObj: |
| { |
| Js::FunctionInfoPtrPtr infoRef = m_func->GetJITFunctionBody()->GetNestedFuncRef(slotId); |
| IR::AddrOpnd * functionBodySlotOpnd = IR::AddrOpnd::New((Js::Var)infoRef, IR::AddrOpndKindDynamicMisc, m_func); |
| IR::Opnd * environmentOpnd = GetEnvironmentOperand(offset); |
| IR::Opnd * homeObjOpnd = this->BuildSrcOpnd(fieldRegSlot); |
| regOpnd = this->BuildDstOpnd(regSlot); |
| |
| instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), homeObjOpnd, m_func); |
| this->AddInstr(instr, offset); |
| |
| instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), functionBodySlotOpnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, offset); |
| |
| instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), environmentOpnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, offset); |
| |
| instr = IR::Instr::New(newOpcode, regOpnd, instr->GetDst(), m_func); |
| |
| if (regOpnd->m_sym->m_isSingleDef) |
| { |
| regOpnd->m_sym->m_isSafeThis = true; |
| regOpnd->m_sym->m_isNotNumber = true; |
| } |
| this->AddInstr(instr, offset); |
| return; |
| } |
| |
| case Js::OpCode::LdObjSlot: |
| newOpcode = Js::OpCode::LdSlot; |
| goto ObjSlotCommon; |
| |
| case Js::OpCode::StObjSlot: |
| newOpcode = Js::OpCode::StSlot; |
| goto ObjSlotCommon; |
| |
| case Js::OpCode::StObjSlotChkUndecl: |
| newOpcode = Js::OpCode::StSlotChkUndecl; |
| |
| ObjSlotCommon: |
| regOpnd = IR::RegOpnd::New(TyVar, m_func); |
| fieldSymOpnd = this->BuildFieldOpnd(newOpcode, fieldRegSlot, (Js::DynamicObject::GetOffsetOfAuxSlots())/sizeof(Js::Var), (Js::PropertyIdIndexType)-1, PropertyKindSlotArray); |
| instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldSymOpnd, m_func); |
| this->AddInstr(instr, offset); |
| |
| fieldSym = PropertySym::New(regOpnd->m_sym, slotId, (uint32)-1, (uint)-1, PropertyKindSlots, m_func); |
| fieldSymOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func); |
| |
| if (newOpcode == Js::OpCode::StSlot || newOpcode == Js::OpCode::StSlotChkUndecl) |
| { |
| goto StSlotCommon; |
| } |
| goto LdSlotCommon; |
| |
| case Js::OpCode::LdSlotArr: |
| propertyKind = PropertyKindSlotArray; |
| case Js::OpCode::LdSlot: |
| // Load |
| fieldSymOpnd = this->BuildFieldOpnd(newOpcode, fieldRegSlot, slotId, (Js::PropertyIdIndexType)-1, propertyKind); |
| |
| LdSlotCommon: |
| regOpnd = this->BuildDstOpnd(regSlot); |
| |
| instr = nullptr; |
| if (profileId != Js::Constants::NoProfileId) |
| { |
| instr = this->BuildProfiledSlotLoad(newOpcode, regOpnd, fieldSymOpnd, profileId, &isLdSlotThatWasNotProfiled); |
| } |
| if (!instr) |
| { |
| instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, m_func); |
| } |
| break; |
| |
| case Js::OpCode::StSlot: |
| case Js::OpCode::StSlotChkUndecl: |
| // Store |
| fieldSymOpnd = this->BuildFieldOpnd(newOpcode, fieldRegSlot, slotId, (Js::PropertyIdIndexType)-1, propertyKind); |
| |
| StSlotCommon: |
| regOpnd = this->BuildSrcOpnd(regSlot); |
| |
| instr = IR::Instr::New(newOpcode, fieldSymOpnd, regOpnd, m_func); |
| if (newOpcode == Js::OpCode::StSlotChkUndecl) |
| { |
| // ChkUndecl includes an implicit read of the destination. Communicate the liveness by using the destination in src2. |
| instr->SetSrc2(fieldSymOpnd); |
| } |
| break; |
| |
| case Js::OpCode::StPropIdArrFromVar: |
| { |
| IR::RegOpnd * src0Opnd = this->BuildSrcOpnd(fieldRegSlot); |
| IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(regSlot); |
| IntConstType value = slotId; |
| IR::IntConstOpnd * valOpnd = IR::IntConstOpnd::New(value, TyInt32, m_func); |
| |
| instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src1Opnd, m_func); |
| this->AddInstr(instr, offset); |
| offset = Js::Constants::NoByteCodeOffset; |
| |
| instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), valOpnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, offset); |
| |
| instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src0Opnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, offset); |
| |
| IR::Opnd * firstArg = instr->GetDst(); |
| instr = IR::Instr::New(newOpcode, m_func); |
| instr->SetSrc1(firstArg); |
| break; |
| } |
| |
| default: |
| AssertMsg(UNREACHED, "Unknown ElementSlot opcode"); |
| Fatal(); |
| } |
| |
| this->AddInstr(instr, offset); |
| |
| if(isLdSlotThatWasNotProfiled && DoBailOnNoProfile()) |
| { |
| InsertBailOnNoProfile(instr); |
| } |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildElementSlotI1(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementSlotI1<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| } |
| |
| BuildElementSlotI1(newOpcode, offset, layout->Value, layout->SlotIndex, Js::Constants::NoProfileId); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiledElementSlotI1(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_ElementSlotI1<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| } |
| |
| Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode); |
| BuildElementSlotI1(newOpcode, offset, layout->Value, layout->SlotIndex, layout->profileId); |
| } |
| |
| void |
| IRBuilder::BuildElementSlotI1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot regSlot, |
| int32 slotId, Js::ProfileId profileId) |
| { |
| IR::RegOpnd *regOpnd; |
| IR::SymOpnd *fieldOpnd; |
| IR::Instr *instr = nullptr; |
| IR::ByteCodeUsesInstr *byteCodeUse; |
| PropertySym *fieldSym = nullptr; |
| StackSym * stackFuncPtrSym = nullptr; |
| SymID symID = m_func->GetJITFunctionBody()->GetLocalClosureReg(); |
| bool isLdSlotThatWasNotProfiled = false; |
| StackSym* closureSym = m_func->GetLocalClosureSym(); |
| |
| uint scopeSlotSize = this->IsParamScopeDone() ? m_func->GetJITFunctionBody()->GetScopeSlotArraySize() : m_func->GetJITFunctionBody()->GetParamScopeSlotArraySize(); |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::LdParamSlot: |
| scopeSlotSize = m_func->GetJITFunctionBody()->GetParamScopeSlotArraySize(); |
| closureSym = m_func->GetParamClosureSym(); |
| symID = m_func->GetJITFunctionBody()->GetParamClosureReg(); |
| // Fall through |
| |
| case Js::OpCode::LdLocalSlot: |
| if (!PHASE_OFF(Js::ClosureRangeCheckPhase, m_func)) |
| { |
| if ((uint32)slotId >= scopeSlotSize + Js::ScopeSlots::FirstSlotIndex) |
| { |
| Js::Throw::FatalInternalError(); |
| } |
| } |
| |
| if (closureSym->HasByteCodeRegSlot()) |
| { |
| byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, offset); |
| byteCodeUse->SetNonOpndSymbol(closureSym->m_id); |
| this->AddInstr(byteCodeUse, offset); |
| } |
| |
| // Read the scope slot pointer back using the stack closure sym. |
| newOpcode = Js::OpCode::LdSlot; |
| if (m_func->DoStackFrameDisplay()) |
| { |
| // Read the scope slot pointer back using the stack closure sym. |
| fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, closureSym->m_id, 0, (Js::PropertyIdIndexType)-1, PropertyKindSlotArray); |
| |
| regOpnd = IR::RegOpnd::New(TyVar, m_func); |
| instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func); |
| this->AddInstr(instr, offset); |
| symID = regOpnd->m_sym->m_id; |
| |
| if (IsLoopBody()) |
| { |
| fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, closureSym->m_id, slotId, (Js::PropertyIdIndexType)-1, PropertyKindSlotArray); |
| } |
| } |
| else if (IsLoopBody()) |
| { |
| this->EnsureLoopBodyLoadSlot(symID); |
| } |
| |
| fieldSym = PropertySym::FindOrCreate(symID, slotId, (uint32)-1, (uint)-1, PropertyKindSlots, m_func); |
| fieldOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func); |
| regOpnd = this->BuildDstOpnd(regSlot); |
| instr = nullptr; |
| if (profileId != Js::Constants::NoProfileId) |
| { |
| instr = this->BuildProfiledSlotLoad(Js::OpCode::LdSlot, regOpnd, fieldOpnd, profileId, &isLdSlotThatWasNotProfiled); |
| } |
| if (!instr) |
| { |
| instr = IR::Instr::New(Js::OpCode::LdSlot, regOpnd, fieldOpnd, m_func); |
| } |
| this->AddInstr(instr, offset); |
| |
| break; |
| |
| case Js::OpCode::LdParamObjSlot: |
| closureSym = m_func->GetParamClosureSym(); |
| symID = m_func->GetJITFunctionBody()->GetParamClosureReg(); |
| newOpcode = Js::OpCode::LdLocalObjSlot; |
| // Fall through |
| |
| case Js::OpCode::LdLocalObjSlot: |
| if (closureSym->HasByteCodeRegSlot()) |
| { |
| byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, offset); |
| byteCodeUse->SetNonOpndSymbol(closureSym->m_id); |
| this->AddInstr(byteCodeUse, offset); |
| } |
| |
| fieldOpnd = this->BuildFieldOpnd(newOpcode, symID, (Js::DynamicObject::GetOffsetOfAuxSlots()) / sizeof(Js::Var), (Js::PropertyIdIndexType) - 1, PropertyKindSlotArray); |
| regOpnd = IR::RegOpnd::New(TyVar, m_func); |
| instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func); |
| this->AddInstr(instr, offset); |
| |
| fieldSym = PropertySym::New(regOpnd->m_sym, slotId, (uint32)-1, (uint)-1, PropertyKindSlots, m_func); |
| fieldOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func); |
| |
| regOpnd = this->BuildDstOpnd(regSlot); |
| instr = nullptr; |
| newOpcode = Js::OpCode::LdSlot; |
| if (profileId != Js::Constants::NoProfileId) |
| { |
| instr = this->BuildProfiledSlotLoad(newOpcode, regOpnd, fieldOpnd, profileId, &isLdSlotThatWasNotProfiled); |
| } |
| if (!instr) |
| { |
| instr = IR::Instr::New(newOpcode, regOpnd, fieldOpnd, m_func); |
| } |
| this->AddInstr(instr, offset); |
| break; |
| |
| case Js::OpCode::StParamSlot: |
| case Js::OpCode::StParamSlotChkUndecl: |
| scopeSlotSize = m_func->GetJITFunctionBody()->GetParamScopeSlotArraySize(); |
| closureSym = m_func->GetParamClosureSym(); |
| symID = m_func->GetJITFunctionBody()->GetParamClosureReg(); |
| newOpcode = newOpcode == Js::OpCode::StParamSlot ? Js::OpCode::StLocalSlot : Js::OpCode::StLocalSlotChkUndecl; |
| // Fall through |
| |
| case Js::OpCode::StLocalSlot: |
| case Js::OpCode::StLocalSlotChkUndecl: |
| if (!PHASE_OFF(Js::ClosureRangeCheckPhase, m_func)) |
| { |
| if ((uint32)slotId >= scopeSlotSize + Js::ScopeSlots::FirstSlotIndex) |
| { |
| Js::Throw::FatalInternalError(); |
| } |
| } |
| |
| if (closureSym->HasByteCodeRegSlot()) |
| { |
| byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, offset); |
| byteCodeUse->SetNonOpndSymbol(closureSym->m_id); |
| this->AddInstr(byteCodeUse, offset); |
| } |
| |
| newOpcode = newOpcode == Js::OpCode::StLocalSlot ? Js::OpCode::StSlot : Js::OpCode::StSlotChkUndecl; |
| if (m_func->DoStackFrameDisplay()) |
| { |
| regOpnd = IR::RegOpnd::New(TyVar, m_func); |
| // Read the scope slot pointer back using the stack closure sym. |
| fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, closureSym->m_id, 0, (Js::PropertyIdIndexType)-1, PropertyKindSlotArray); |
| |
| instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func); |
| this->AddInstr(instr, offset); |
| symID = regOpnd->m_sym->m_id; |
| |
| if (IsLoopBody()) |
| { |
| fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, closureSym->m_id, slotId, (Js::PropertyIdIndexType)-1, PropertyKindSlotArray); |
| } |
| } |
| else |
| { |
| if (IsLoopBody()) |
| { |
| this->EnsureLoopBodyLoadSlot(symID); |
| } |
| } |
| fieldSym = PropertySym::FindOrCreate(symID, slotId, (uint32)-1, (uint)-1, PropertyKindSlots, m_func); |
| fieldOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func); |
| regOpnd = this->BuildSrcOpnd(regSlot); |
| instr = IR::Instr::New(newOpcode, fieldOpnd, regOpnd, m_func); |
| this->AddInstr(instr, offset); |
| if (newOpcode == Js::OpCode::StSlotChkUndecl) |
| { |
| instr->SetSrc2(fieldOpnd); |
| } |
| |
| break; |
| |
| case Js::OpCode::StParamObjSlot: |
| case Js::OpCode::StParamObjSlotChkUndecl: |
| closureSym = m_func->GetParamClosureSym(); |
| symID = m_func->GetJITFunctionBody()->GetParamClosureReg(); |
| newOpcode = newOpcode == Js::OpCode::StParamObjSlot ? Js::OpCode::StLocalObjSlot : Js::OpCode::StLocalObjSlotChkUndecl; |
| // Fall through |
| |
| case Js::OpCode::StLocalObjSlot: |
| case Js::OpCode::StLocalObjSlotChkUndecl: |
| if (closureSym->HasByteCodeRegSlot()) |
| { |
| byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, offset); |
| byteCodeUse->SetNonOpndSymbol(closureSym->m_id); |
| this->AddInstr(byteCodeUse, offset); |
| } |
| |
| regOpnd = IR::RegOpnd::New(TyVar, m_func); |
| fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, symID, (Js::DynamicObject::GetOffsetOfAuxSlots())/sizeof(Js::Var), (Js::PropertyIdIndexType)-1, PropertyKindSlotArray); |
| instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func); |
| this->AddInstr(instr, offset); |
| |
| newOpcode = newOpcode == Js::OpCode::StLocalObjSlot ? Js::OpCode::StSlot : Js::OpCode::StSlotChkUndecl; |
| fieldSym = PropertySym::New(regOpnd->m_sym, slotId, (uint32)-1, (uint)-1, PropertyKindSlots, m_func); |
| fieldOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func); |
| regOpnd = this->BuildSrcOpnd(regSlot); |
| |
| instr = IR::Instr::New(newOpcode, fieldOpnd, regOpnd, m_func); |
| if (newOpcode == Js::OpCode::StSlotChkUndecl) |
| { |
| // ChkUndecl includes an implicit read of the destination. Communicate the liveness by using the destination in src2. |
| instr->SetSrc2(fieldOpnd); |
| } |
| this->AddInstr(instr, offset); |
| break; |
| |
| case Js::OpCode::LdEnvObj: |
| fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, this->GetEnvReg(), slotId, (Js::PropertyIdIndexType)-1, PropertyKindSlotArray); |
| regOpnd = this->BuildDstOpnd(regSlot); |
| instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func); |
| this->AddInstr(instr, offset); |
| |
| m_func->GetTopFunc()->AddFrameDisplayCheck(fieldOpnd); |
| break; |
| |
| case Js::OpCode::NewStackScFunc: |
| stackFuncPtrSym = this->EnsureStackFuncPtrSym(); |
| newOpcode = Js::OpCode::NewScFunc; |
| // fall through |
| case Js::OpCode::NewScFunc: |
| goto NewScFuncCommon; |
| |
| case Js::OpCode::NewScGenFunc: |
| newOpcode = Js::OpCode::NewScGenFunc; |
| NewScFuncCommon: |
| { |
| IR::Opnd * functionBodySlotOpnd = IR::IntConstOpnd::New(slotId, TyInt32, m_func, true); |
| |
| IR::Opnd *environmentOpnd = GetEnvironmentOperand(offset); |
| regOpnd = this->BuildDstOpnd(regSlot); |
| if (stackFuncPtrSym) |
| { |
| IR::RegOpnd * dataOpnd = IR::RegOpnd::New(TyVar, m_func); |
| instr = IR::Instr::New(Js::OpCode::NewScFuncData, dataOpnd, environmentOpnd, |
| IR::RegOpnd::New(stackFuncPtrSym, TyVar, m_func), m_func); |
| this->AddInstr(instr, offset); |
| instr = IR::Instr::New(newOpcode, regOpnd, functionBodySlotOpnd, dataOpnd, m_func); |
| } |
| else |
| { |
| instr = IR::Instr::New(newOpcode, regOpnd, functionBodySlotOpnd, environmentOpnd, m_func); |
| } |
| if (regOpnd->m_sym->m_isSingleDef) |
| { |
| regOpnd->m_sym->m_isSafeThis = true; |
| regOpnd->m_sym->m_isNotNumber = true; |
| } |
| this->AddInstr(instr, offset); |
| return; |
| } |
| |
| default: |
| Assert(0); |
| } |
| |
| |
| if(isLdSlotThatWasNotProfiled && DoBailOnNoProfile()) |
| { |
| InsertBailOnNoProfile(instr); |
| } |
| } |
| |
| IR::Opnd* |
| IRBuilder::GetEnvironmentOperand(uint32 offset) |
| { |
| StackSym* sym = nullptr; |
| // The byte code doesn't refer directly to a closure environment. Get the implicit one |
| // that's pointed to by the function body. |
| if (m_func->DoStackFrameDisplay() && m_func->GetLocalFrameDisplaySym()) |
| { |
| // Read the scope slot pointer back using the stack closure sym. |
| IR::Opnd *fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, m_func->GetLocalFrameDisplaySym()->m_id, 0, (Js::PropertyIdIndexType) - 1, PropertyKindSlotArray); |
| IR::RegOpnd *regOpnd = IR::RegOpnd::New(TyVar, m_func); |
| this->AddInstr( |
| IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func), |
| offset); |
| sym = regOpnd->m_sym; |
| } |
| else |
| { |
| SymID symID; |
| symID = this->GetEnvRegForInnerFrameDisplay(); |
| Assert(symID != Js::Constants::NoRegister); |
| if (IsLoopBody() && !RegIsConstant(symID)) |
| { |
| this->EnsureLoopBodyLoadSlot(symID); |
| } |
| |
| if (m_func->DoStackNestedFunc() && symID == GetEnvReg()) |
| { |
| // Environment is not guaranteed constant during this function because it could become boxed during execution, |
| // so load the environment every time you need it. |
| IR::RegOpnd *regOpnd = IR::RegOpnd::New(TyVar, m_func); |
| this->AddInstr( |
| IR::Instr::New(Js::OpCode::LdEnv, regOpnd, m_func), |
| offset); |
| sym = regOpnd->m_sym; |
| } |
| else |
| { |
| sym = StackSym::FindOrCreate(symID, (Js::RegSlot)symID, m_func); |
| } |
| } |
| |
| return IR::RegOpnd::New(sym, TyVar, m_func); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildElementSlotI2(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementSlotI2<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| } |
| |
| BuildElementSlotI2(newOpcode, offset, layout->Value, layout->SlotIndex1, layout->SlotIndex2, Js::Constants::NoProfileId); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiledElementSlotI2(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_ElementSlotI2<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| } |
| |
| Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode); |
| BuildElementSlotI2(newOpcode, offset, layout->Value, layout->SlotIndex1, layout->SlotIndex2, layout->profileId); |
| } |
| |
| void |
| IRBuilder::BuildElementSlotI2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot regSlot, |
| int32 slotId1, int32 slotId2, Js::ProfileId profileId) |
| { |
| IR::RegOpnd *regOpnd; |
| IR::SymOpnd *fieldOpnd; |
| IR::Instr *instr; |
| PropertySym *fieldSym; |
| bool isLdSlotThatWasNotProfiled = false; |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::LdModuleSlot: |
| case Js::OpCode::StModuleSlot: |
| { |
| Field(Js::Var)* moduleExportVarArrayAddr = Js::JavascriptOperators::OP_GetModuleExportSlotArrayAddress(slotId1, slotId2, m_func->GetScriptContextInfo()); |
| IR::AddrOpnd* addrOpnd = IR::AddrOpnd::New(moduleExportVarArrayAddr, IR::AddrOpndKindConstantAddress, m_func, true); |
| regOpnd = IR::RegOpnd::New(TyVar, m_func); |
| instr = IR::Instr::New(Js::OpCode::Ld_A, regOpnd, addrOpnd, m_func); |
| this->AddInstr(instr, offset); |
| |
| fieldSym = PropertySym::New(regOpnd->m_sym, slotId2, (uint32)-1, (uint)-1, PropertyKindSlots, m_func); |
| fieldOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func); |
| |
| if (newOpcode == Js::OpCode::LdModuleSlot) |
| { |
| newOpcode = Js::OpCode::LdSlot; |
| regOpnd = this->BuildDstOpnd(regSlot); |
| instr = IR::Instr::New(newOpcode, regOpnd, fieldOpnd, m_func); |
| } |
| else |
| { |
| Assert(newOpcode == Js::OpCode::StModuleSlot); |
| newOpcode = Js::OpCode::StSlot; |
| regOpnd = this->BuildSrcOpnd(regSlot); |
| instr = IR::Instr::New(newOpcode, fieldOpnd, regOpnd, m_func); |
| } |
| |
| this->AddInstr(instr, offset); |
| break; |
| } |
| |
| case Js::OpCode::LdEnvSlot: |
| case Js::OpCode::LdEnvObjSlot: |
| case Js::OpCode::StEnvSlot: |
| case Js::OpCode::StEnvSlotChkUndecl: |
| case Js::OpCode::StEnvObjSlot: |
| case Js::OpCode::StEnvObjSlotChkUndecl: |
| |
| fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, this->GetEnvReg(), slotId1, (Js::PropertyIdIndexType)-1, PropertyKindSlotArray); |
| regOpnd = IR::RegOpnd::New(TyVar, m_func); |
| instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func); |
| this->AddInstr(instr, offset); |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::LdEnvObjSlot: |
| case Js::OpCode::StEnvObjSlot: |
| case Js::OpCode::StEnvObjSlotChkUndecl: |
| |
| m_func->GetTopFunc()->AddFrameDisplayCheck(fieldOpnd, (uint32)-1); |
| fieldSym = PropertySym::New(regOpnd->m_sym, (Js::DynamicObject::GetOffsetOfAuxSlots())/sizeof(Js::Var), |
| (uint32)-1, (uint)-1, PropertyKindSlotArray, m_func); |
| fieldOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func); |
| regOpnd = IR::RegOpnd::New(TyVar, m_func); |
| instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func); |
| this->AddInstr(instr, offset); |
| break; |
| |
| default: |
| m_func->GetTopFunc()->AddFrameDisplayCheck(fieldOpnd, slotId2); |
| break; |
| } |
| |
| fieldSym = PropertySym::New(regOpnd->m_sym, slotId2, (uint32)-1, (uint)-1, PropertyKindSlots, m_func); |
| fieldOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func); |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::LdEnvSlot: |
| case Js::OpCode::LdEnvObjSlot: |
| newOpcode = Js::OpCode::LdSlot; |
| regOpnd = this->BuildDstOpnd(regSlot); |
| instr = nullptr; |
| if (profileId != Js::Constants::NoProfileId) |
| { |
| instr = this->BuildProfiledSlotLoad(newOpcode, regOpnd, fieldOpnd, profileId, &isLdSlotThatWasNotProfiled); |
| } |
| if (!instr) |
| { |
| instr = IR::Instr::New(newOpcode, regOpnd, fieldOpnd, m_func); |
| } |
| break; |
| |
| default: |
| newOpcode = |
| newOpcode == Js::OpCode::StEnvSlot || newOpcode == Js::OpCode::StEnvObjSlot ? Js::OpCode::StSlot : Js::OpCode::StSlotChkUndecl; |
| regOpnd = this->BuildSrcOpnd(regSlot); |
| instr = IR::Instr::New(newOpcode, fieldOpnd, regOpnd, m_func); |
| if (newOpcode == Js::OpCode::StSlotChkUndecl) |
| { |
| // ChkUndecl includes an implicit read of the destination. Communicate the liveness by using the destination in src2. |
| instr->SetSrc2(fieldOpnd); |
| } |
| break; |
| } |
| this->AddInstr(instr, offset); |
| if(isLdSlotThatWasNotProfiled && DoBailOnNoProfile()) |
| { |
| InsertBailOnNoProfile(instr); |
| } |
| break; |
| |
| case Js::OpCode::StInnerObjSlot: |
| case Js::OpCode::StInnerObjSlotChkUndecl: |
| case Js::OpCode::StInnerSlot: |
| case Js::OpCode::StInnerSlotChkUndecl: |
| if ((uint)slotId1 >= m_func->GetJITFunctionBody()->GetInnerScopeCount()) |
| { |
| Js::Throw::FatalInternalError(); |
| } |
| regOpnd = this->BuildSrcOpnd(regSlot); |
| slotId1 += this->m_func->GetJITFunctionBody()->GetFirstInnerScopeReg(); |
| if ((uint)slotId1 >= this->m_func->GetJITFunctionBody()->GetLocalsCount()) |
| { |
| Js::Throw::FatalInternalError(); |
| } |
| if (newOpcode == Js::OpCode::StInnerObjSlot || newOpcode == Js::OpCode::StInnerObjSlotChkUndecl) |
| { |
| IR::RegOpnd * slotOpnd = IR::RegOpnd::New(TyVar, m_func); |
| fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, slotId1, (Js::DynamicObject::GetOffsetOfAuxSlots())/sizeof(Js::Var), (Js::PropertyIdIndexType)-1, PropertyKindSlotArray); |
| instr = IR::Instr::New(Js::OpCode::LdSlotArr, slotOpnd, fieldOpnd, m_func); |
| this->AddInstr(instr, offset); |
| |
| PropertySym *propertySym = PropertySym::New(slotOpnd->m_sym, slotId2, (uint32)-1, (uint)-1, PropertyKindSlots, m_func); |
| fieldOpnd = IR::PropertySymOpnd::New(propertySym, (Js::CacheId)-1, TyVar, m_func); |
| } |
| else |
| { |
| fieldOpnd = this->BuildFieldOpnd(Js::OpCode::StSlot, slotId1, slotId2, (Js::PropertyIdIndexType)-1, PropertyKindSlots); |
| } |
| newOpcode = |
| newOpcode == Js::OpCode::StInnerObjSlot || newOpcode == Js::OpCode::StInnerSlot ? |
| Js::OpCode::StSlot : Js::OpCode::StSlotChkUndecl; |
| instr = IR::Instr::New(newOpcode, fieldOpnd, regOpnd, m_func); |
| if (newOpcode == Js::OpCode::StSlotChkUndecl) |
| { |
| // ChkUndecl includes an implicit read of the destination. Communicate the liveness by using the destination in src2. |
| instr->SetSrc2(fieldOpnd); |
| } |
| this->AddInstr(instr, offset); |
| |
| break; |
| |
| case Js::OpCode::LdInnerSlot: |
| case Js::OpCode::LdInnerObjSlot: |
| if ((uint)slotId1 >= m_func->GetJITFunctionBody()->GetInnerScopeCount()) |
| { |
| Js::Throw::FatalInternalError(); |
| } |
| slotId1 += this->m_func->GetJITFunctionBody()->GetFirstInnerScopeReg(); |
| if ((uint)slotId1 >= this->m_func->GetJITFunctionBody()->GetLocalsCount()) |
| { |
| Js::Throw::FatalInternalError(); |
| } |
| if (newOpcode == Js::OpCode::LdInnerObjSlot) |
| { |
| IR::RegOpnd * slotOpnd = IR::RegOpnd::New(TyVar, m_func); |
| fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, slotId1, (Js::DynamicObject::GetOffsetOfAuxSlots())/sizeof(Js::Var), (Js::PropertyIdIndexType)-1, PropertyKindSlotArray); |
| instr = IR::Instr::New(Js::OpCode::LdSlotArr, slotOpnd, fieldOpnd, m_func); |
| this->AddInstr(instr, offset); |
| |
| PropertySym *propertySym = PropertySym::New(slotOpnd->m_sym, slotId2, (uint32)-1, (uint)-1, PropertyKindSlots, m_func); |
| fieldOpnd = IR::PropertySymOpnd::New(propertySym, (Js::CacheId)-1, TyVar, m_func); |
| } |
| else |
| { |
| fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlot, slotId1, slotId2, (Js::PropertyIdIndexType)-1, PropertyKindSlots); |
| } |
| regOpnd = this->BuildDstOpnd(regSlot); |
| instr = IR::Instr::New(Js::OpCode::LdSlot, regOpnd, fieldOpnd, m_func); |
| this->AddInstr(instr, offset); |
| |
| break; |
| |
| default: |
| AssertMsg(false, "Unsupported opcode in BuildElementSlotI2"); |
| break; |
| } |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildElementSlotI3(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementSlotI3<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| this->DoClosureRegCheck(layout->Instance); |
| this->DoClosureRegCheck(layout->HomeObj); |
| } |
| |
| BuildElementSlotI3(newOpcode, offset, layout->Instance, layout->Value, layout->SlotIndex, layout->HomeObj, Js::Constants::NoProfileId); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiledElementSlotI3(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_ElementSlotI3<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| this->DoClosureRegCheck(layout->Instance); |
| this->DoClosureRegCheck(layout->HomeObj); |
| } |
| |
| Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode); |
| BuildElementSlotI3(newOpcode, offset, layout->Instance, layout->Value, layout->SlotIndex, layout->HomeObj, layout->profileId); |
| } |
| |
| void |
| IRBuilder::BuildElementSlotI3(Js::OpCode newOpcode, uint32 offset, Js::RegSlot fieldRegSlot, Js::RegSlot regSlot, |
| int32 slotId, Js::RegSlot homeObj, Js::ProfileId profileId) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| IR::Instr * instr; |
| IR::RegOpnd * regOpnd; |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::NewInnerScFuncHomeObj: |
| newOpcode = Js::OpCode::NewScFuncHomeObj; |
| goto NewScFuncCommon; |
| |
| case Js::OpCode::NewInnerScGenFuncHomeObj: |
| newOpcode = Js::OpCode::NewScGenFuncHomeObj; |
| |
| NewScFuncCommon: |
| { |
| Js::FunctionInfoPtrPtr infoRef = m_func->GetJITFunctionBody()->GetNestedFuncRef(slotId); |
| IR::AddrOpnd * functionBodySlotOpnd = IR::AddrOpnd::New((Js::Var)infoRef, IR::AddrOpndKindDynamicMisc, m_func); |
| IR::Opnd * environmentOpnd = this->BuildSrcOpnd(fieldRegSlot); |
| IR::Opnd * homeObjOpnd = this->BuildSrcOpnd(homeObj); |
| regOpnd = this->BuildDstOpnd(regSlot); |
| |
| instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), homeObjOpnd, m_func); |
| this->AddInstr(instr, offset); |
| |
| instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), functionBodySlotOpnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, offset); |
| |
| instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), environmentOpnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, offset); |
| |
| instr = IR::Instr::New(newOpcode, regOpnd, instr->GetDst(), m_func); |
| |
| if (regOpnd->m_sym->m_isSingleDef) |
| { |
| regOpnd->m_sym->m_isSafeThis = true; |
| regOpnd->m_sym->m_isNotNumber = true; |
| } |
| this->AddInstr(instr, offset); |
| return; |
| } |
| |
| default: |
| AssertMsg(UNREACHED, "Unknown ElementSlotI3 opcode"); |
| Fatal(); |
| } |
| } |
| |
| IR::SymOpnd * |
| IRBuilder::BuildLoopBodySlotOpnd(SymID symId) |
| { |
| Assert(!this->RegIsConstant((Js::RegSlot)symId)); |
| |
| // Get the interpreter frame instance that was passed in. |
| StackSym *loopParamSym = m_func->EnsureLoopParamSym(); |
| |
| PropertySym * fieldSym = PropertySym::FindOrCreate(loopParamSym->m_id, (Js::PropertyId)(symId + this->m_loopBodyLocalsStartSlot), (uint32)-1, (uint)-1, PropertyKindLocalSlots, m_func); |
| return IR::SymOpnd::New(fieldSym, TyVar, m_func); |
| } |
| |
| void |
| IRBuilder::EnsureLoopBodyLoadSlot(SymID symId, bool isCatchObjectSym) |
| { |
| // No need to emit LdSlot for a catch object. In fact, if we do, we might be loading an uninitialized value from the slot. |
| if (isCatchObjectSym) |
| { |
| return; |
| } |
| StackSym * symDst = StackSym::FindOrCreate(symId, (Js::RegSlot)symId, m_func); |
| if (symDst->m_isCatchObjectSym) |
| { |
| return; |
| } |
| AssertOrFailFast(symId < m_ldSlots->Length()); |
| if (this->m_ldSlots->TestAndSet(symId)) |
| { |
| return; |
| } |
| |
| IR::SymOpnd * fieldSymOpnd = this->BuildLoopBodySlotOpnd(symId); |
| IR::RegOpnd * dstOpnd = IR::RegOpnd::New(symDst, TyVar, m_func); |
| IR::Instr * ldSlotInstr; |
| |
| ValueType symValueType; |
| if(m_func->GetWorkItem()->HasSymIdToValueTypeMap() && m_func->GetWorkItem()->TryGetValueType(symId, &symValueType)) |
| { |
| ldSlotInstr = IR::ProfiledInstr::New(Js::OpCode::LdSlot, dstOpnd, fieldSymOpnd, m_func); |
| ldSlotInstr->AsProfiledInstr()->u.FldInfo().valueType = symValueType; |
| } |
| else |
| { |
| ldSlotInstr = IR::Instr::New(Js::OpCode::LdSlot, dstOpnd, fieldSymOpnd, m_func); |
| } |
| |
| m_func->m_headInstr->InsertAfter(ldSlotInstr); |
| if (m_lastInstr == m_func->m_headInstr) |
| { |
| m_lastInstr = ldSlotInstr; |
| } |
| } |
| |
| void |
| IRBuilder::SetLoopBodyStSlot(SymID symID, bool isCatchObjectSym) |
| { |
| if (this->m_func->HasTry() && !PHASE_OFF(Js::JITLoopBodyInTryCatchPhase, this->m_func)) |
| { |
| // No need to emit StSlot for a catch object. In fact, if we do, we might be storing an uninitialized value to the slot. |
| if (isCatchObjectSym) |
| { |
| return; |
| } |
| StackSym * dstSym = StackSym::FindOrCreate(symID, (Js::RegSlot)symID, m_func); |
| Assert(dstSym); |
| if (dstSym->m_isCatchObjectSym) |
| { |
| return; |
| } |
| } |
| AssertOrFailFast(symID < m_stSlots->Length()); |
| this->m_stSlots->Set(symID); |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildElementCP |
| /// |
| /// Build IR instr for an ElementCP or ElementRootCP instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| IR::Instr * |
| IRBuilder::BuildProfiledFieldLoad(Js::OpCode loadOp, IR::RegOpnd *dstOpnd, IR::SymOpnd *srcOpnd, Js::CacheId inlineCacheIndex, bool *pUnprofiled) |
| { |
| IR::Instr * instr = nullptr; |
| |
| // Prefer JitProfilingInstr if we're in simplejit |
| if (m_func->DoSimpleJitDynamicProfile()) |
| { |
| instr = IR::JitProfilingInstr::New(loadOp, dstOpnd, srcOpnd, m_func); |
| } |
| else if (this->m_func->HasProfileInfo()) |
| { |
| instr = IR::ProfiledInstr::New(loadOp, dstOpnd, srcOpnd, m_func); |
| instr->AsProfiledInstr()->u.FldInfo() = *(m_func->GetReadOnlyProfileInfo()->GetFldInfo(inlineCacheIndex)); |
| *pUnprofiled = !instr->AsProfiledInstr()->u.FldInfo().WasLdFldProfiled(); |
| dstOpnd->SetValueType(instr->AsProfiledInstr()->u.FldInfo().valueType); |
| #if ENABLE_DEBUG_CONFIG_OPTIONS |
| if(Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::DynamicProfilePhase)) |
| { |
| const ValueType valueType(instr->AsProfiledInstr()->u.FldInfo().valueType); |
| char valueTypeStr[VALUE_TYPE_MAX_STRING_SIZE]; |
| valueType.ToString(valueTypeStr); |
| char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE]; |
| Output::Print(_u("TestTrace function %s (%s) ValueType = %i "), m_func->GetJITFunctionBody()->GetDisplayName(), m_func->GetDebugNumberSet(debugStringBuffer), valueTypeStr); |
| instr->DumpTestTrace(); |
| } |
| #endif |
| } |
| |
| return instr; |
| } |
| |
| Js::RegSlot IRBuilder::GetEnvRegForEvalCode() const |
| { |
| if (m_func->GetJITFunctionBody()->IsStrictMode() && m_func->GetJITFunctionBody()->IsGlobalFunc()) |
| { |
| return m_func->GetJITFunctionBody()->GetLocalFrameDisplayReg(); |
| } |
| else |
| { |
| return GetEnvReg(); |
| } |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildElementP(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementP<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| } |
| |
| BuildElementP(newOpcode, offset, layout->Value, layout->inlineCacheIndex); |
| } |
| |
| void |
| IRBuilder::BuildElementP(Js::OpCode newOpcode, uint32 offset, Js::RegSlot regSlot, Js::CacheId inlineCacheIndex) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| IR::Instr * instr; |
| IR::RegOpnd * regOpnd; |
| IR::Opnd * srcOpnd; |
| IR::SymOpnd * fieldSymOpnd; |
| Js::PropertyId propertyId; |
| bool isProfiled = OpCodeAttr::IsProfiledOp(newOpcode); |
| bool isLdFldThatWasNotProfiled = false; |
| |
| if (isProfiled) |
| { |
| Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode); |
| } |
| |
| propertyId = this->m_func->GetJITFunctionBody()->GetPropertyIdFromCacheId(inlineCacheIndex); |
| |
| Js::RegSlot instance = this->GetEnvRegForEvalCode(); |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::LdLocalFld: |
| if (m_func->GetLocalClosureSym()->HasByteCodeRegSlot()) |
| { |
| IR::ByteCodeUsesInstr * byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, offset); |
| byteCodeUse->SetNonOpndSymbol(m_func->GetLocalClosureSym()->m_id); |
| this->AddInstr(byteCodeUse, offset); |
| } |
| |
| newOpcode = Js::OpCode::LdFld; |
| fieldSymOpnd = this->BuildFieldOpnd(newOpcode, m_func->GetJITFunctionBody()->GetLocalClosureReg(), propertyId, (Js::PropertyIdIndexType)-1, PropertyKindData, inlineCacheIndex); |
| if (fieldSymOpnd->IsPropertySymOpnd()) |
| { |
| fieldSymOpnd->AsPropertySymOpnd()->TryDisableRuntimePolymorphicCache(); |
| } |
| regOpnd = this->BuildDstOpnd(regSlot); |
| |
| instr = nullptr; |
| if (isProfiled) |
| { |
| instr = this->BuildProfiledFieldLoad(newOpcode, regOpnd, fieldSymOpnd, inlineCacheIndex, &isLdFldThatWasNotProfiled); |
| } |
| |
| // If it hasn't been set yet |
| if (!instr) |
| { |
| instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, m_func); |
| } |
| break; |
| |
| case Js::OpCode::StLocalFld: |
| if (m_func->GetLocalClosureSym()->HasByteCodeRegSlot()) |
| { |
| IR::ByteCodeUsesInstr * byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, offset); |
| byteCodeUse->SetNonOpndSymbol(m_func->GetLocalClosureSym()->m_id); |
| this->AddInstr(byteCodeUse, offset); |
| } |
| |
| fieldSymOpnd = this->BuildFieldOpnd(newOpcode, m_func->GetJITFunctionBody()->GetLocalClosureReg(), propertyId, (Js::PropertyIdIndexType)-1, PropertyKindData, inlineCacheIndex); |
| if (fieldSymOpnd->IsPropertySymOpnd()) |
| { |
| fieldSymOpnd->AsPropertySymOpnd()->TryDisableRuntimePolymorphicCache(); |
| } |
| srcOpnd = this->BuildSrcOpnd(regSlot); |
| newOpcode = Js::OpCode::StFld; |
| goto stCommon; |
| |
| case Js::OpCode::InitLocalFld: |
| case Js::OpCode::InitLocalLetFld: |
| case Js::OpCode::InitUndeclLocalLetFld: |
| case Js::OpCode::InitUndeclLocalConstFld: |
| { |
| if (m_func->GetLocalClosureSym()->HasByteCodeRegSlot()) |
| { |
| IR::ByteCodeUsesInstr * byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, offset); |
| byteCodeUse->SetNonOpndSymbol(m_func->GetLocalClosureSym()->m_id); |
| this->AddInstr(byteCodeUse, offset); |
| } |
| |
| fieldSymOpnd = this->BuildFieldOpnd(newOpcode, m_func->GetJITFunctionBody()->GetLocalClosureReg(), propertyId, (Js::PropertyIdIndexType)-1, PropertyKindData, inlineCacheIndex); |
| // Store |
| if (newOpcode == Js::OpCode::InitUndeclLocalLetFld) |
| { |
| srcOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetUndeclBlockVarAddr(), IR::AddrOpndKindDynamicVar, this->m_func, true); |
| srcOpnd->SetValueType(ValueType::PrimitiveOrObject); |
| newOpcode = Js::OpCode::InitLetFld; |
| } |
| else if (newOpcode == Js::OpCode::InitUndeclLocalConstFld) |
| { |
| srcOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetUndeclBlockVarAddr(), IR::AddrOpndKindDynamicVar, this->m_func, true); |
| srcOpnd->SetValueType(ValueType::PrimitiveOrObject); |
| newOpcode = Js::OpCode::InitConstFld; |
| } |
| else |
| { |
| srcOpnd = this->BuildSrcOpnd(regSlot); |
| newOpcode = newOpcode == Js::OpCode::InitLocalFld ? Js::OpCode::InitFld : Js::OpCode::InitLetFld; |
| } |
| |
| stCommon: |
| instr = nullptr; |
| if (isProfiled) |
| { |
| if (m_func->DoSimpleJitDynamicProfile()) |
| { |
| instr = IR::JitProfilingInstr::New(newOpcode, fieldSymOpnd, srcOpnd, m_func); |
| } |
| else if (this->m_func->HasProfileInfo()) |
| { |
| instr = IR::ProfiledInstr::New(newOpcode, fieldSymOpnd, srcOpnd, m_func); |
| instr->AsProfiledInstr()->u.FldInfo() = *(m_func->GetReadOnlyProfileInfo()->GetFldInfo(inlineCacheIndex)); |
| } |
| } |
| |
| // If it hasn't been set yet |
| if (!instr) |
| { |
| instr = IR::Instr::New(newOpcode, fieldSymOpnd, srcOpnd, m_func); |
| } |
| break; |
| } |
| |
| case Js::OpCode::ScopedLdFld: |
| case Js::OpCode::ScopedLdFldForTypeOf: |
| { |
| Assert(!isProfiled); |
| Assert(this->m_func->GetScriptContextInfo()->GetAddr() == this->m_func->GetTopFunc()->GetScriptContextInfo()->GetAddr()); |
| |
| fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instance, propertyId, (Js::PropertyIdIndexType)-1, PropertyKindData, inlineCacheIndex); |
| |
| regOpnd = this->BuildDstOpnd(regSlot); |
| instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, m_func); |
| break; |
| } |
| |
| case Js::OpCode::ScopedStFld: |
| case Js::OpCode::ConsoleScopedStFld: |
| case Js::OpCode::ScopedStFldStrict: |
| case Js::OpCode::ConsoleScopedStFldStrict: |
| { |
| Assert(!isProfiled); |
| |
| fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instance, propertyId, (Js::PropertyIdIndexType)-1, PropertyKindData, inlineCacheIndex); |
| |
| // Implicit root object as default instance |
| IR::Opnd * instance2Opnd = this->BuildSrcOpnd(Js::FunctionBody::RootObjectRegSlot); |
| regOpnd = this->BuildSrcOpnd(regSlot); |
| instr = IR::Instr::New(newOpcode, fieldSymOpnd, regOpnd, instance2Opnd, m_func); |
| break; |
| } |
| |
| default: |
| AssertMsg(UNREACHED, "Unknown ElementP opcode"); |
| Fatal(); |
| } |
| |
| this->AddInstr(instr, offset); |
| |
| if(isLdFldThatWasNotProfiled && DoBailOnNoProfile()) |
| { |
| InsertBailOnNoProfile(instr); |
| } |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildElementPIndexed(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementPIndexed<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| } |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::InitInnerFld: |
| newOpcode = Js::OpCode::InitFld; |
| goto initinnerfldcommon; |
| |
| case Js::OpCode::InitInnerLetFld: |
| newOpcode = Js::OpCode::InitLetFld; |
| // fall through |
| initinnerfldcommon: |
| case Js::OpCode::InitUndeclLetFld: |
| case Js::OpCode::InitUndeclConstFld: |
| BuildElementCP(newOpcode, offset, InnerScopeIndexToRegSlot(layout->scopeIndex), layout->Value, layout->inlineCacheIndex); |
| break; |
| |
| default: |
| AssertMsg(false, "Unknown opcode for ElementPIndexed"); |
| break; |
| } |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildElementCP(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementCP<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| this->DoClosureRegCheck(layout->Instance); |
| } |
| |
| BuildElementCP(newOpcode, offset, layout->Instance, layout->Value, layout->inlineCacheIndex); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildElementRootCP(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementRootCP<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| } |
| |
| BuildElementCP(newOpcode, offset, Js::FunctionBody::RootObjectRegSlot, layout->Value, layout->inlineCacheIndex); |
| } |
| |
| void |
| IRBuilder::BuildElementCP(Js::OpCode newOpcode, uint32 offset, Js::RegSlot instance, Js::RegSlot regSlot, Js::CacheId inlineCacheIndex) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| Js::PropertyId propertyId; |
| bool isProfiled = OpCodeAttr::IsProfiledOp(newOpcode); |
| |
| if (isProfiled) |
| { |
| Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode); |
| } |
| |
| propertyId = m_func->GetJITFunctionBody()->GetPropertyIdFromCacheId(inlineCacheIndex); |
| |
| IR::SymOpnd * fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instance, propertyId, (Js::PropertyIdIndexType)-1, PropertyKindData, inlineCacheIndex); |
| IR::RegOpnd * regOpnd; |
| |
| IR::Instr * instr = nullptr; |
| bool isLdFldThatWasNotProfiled = false; |
| switch (newOpcode) |
| { |
| case Js::OpCode::LdFldForTypeOf: |
| case Js::OpCode::LdFld: |
| case Js::OpCode::LdLen_A: |
| if (fieldSymOpnd->IsPropertySymOpnd()) |
| { |
| fieldSymOpnd->AsPropertySymOpnd()->TryDisableRuntimePolymorphicCache(); |
| } |
| case Js::OpCode::LdFldForCallApplyTarget: |
| case Js::OpCode::LdRootFldForTypeOf: |
| case Js::OpCode::LdRootFld: |
| case Js::OpCode::LdMethodFld: |
| case Js::OpCode::LdRootMethodFld: |
| case Js::OpCode::ScopedLdMethodFld: |
| // Load |
| // LdMethodFromFlags is backend only. Don't need to be added here. |
| regOpnd = this->BuildDstOpnd(regSlot); |
| |
| if (isProfiled) |
| { |
| instr = this->BuildProfiledFieldLoad(newOpcode, regOpnd, fieldSymOpnd, inlineCacheIndex, &isLdFldThatWasNotProfiled); |
| } |
| |
| // If it hasn't been set yet |
| if (!instr) |
| { |
| instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, m_func); |
| } |
| |
| if (newOpcode == Js::OpCode::LdFld || |
| newOpcode == Js::OpCode::LdFldForCallApplyTarget || |
| newOpcode == Js::OpCode::LdMethodFld || |
| newOpcode == Js::OpCode::LdRootMethodFld || |
| newOpcode == Js::OpCode::ScopedLdMethodFld) |
| { |
| // Check whether we're loading (what appears to be) a built-in method. |
| Js::BuiltinFunction builtInIndex = Js::BuiltinFunction::None; |
| PropertySym *fieldSym = fieldSymOpnd->m_sym->AsPropertySym(); |
| this->CheckBuiltIn(fieldSym, &builtInIndex); |
| regOpnd->m_sym->m_builtInIndex = builtInIndex; |
| } |
| break; |
| |
| case Js::OpCode::StFld: |
| if (fieldSymOpnd->IsPropertySymOpnd()) |
| { |
| fieldSymOpnd->AsPropertySymOpnd()->TryDisableRuntimePolymorphicCache(); |
| } |
| case Js::OpCode::InitFld: |
| case Js::OpCode::InitRootFld: |
| case Js::OpCode::InitLetFld: |
| case Js::OpCode::InitRootLetFld: |
| case Js::OpCode::InitConstFld: |
| case Js::OpCode::InitRootConstFld: |
| case Js::OpCode::InitUndeclLetFld: |
| case Js::OpCode::InitUndeclConstFld: |
| case Js::OpCode::InitClassMember: |
| case Js::OpCode::StRootFld: |
| case Js::OpCode::StFldStrict: |
| case Js::OpCode::StRootFldStrict: |
| { |
| IR::Opnd *srcOpnd; |
| // Store |
| if (newOpcode == Js::OpCode::InitUndeclLetFld) |
| { |
| srcOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetUndeclBlockVarAddr(), IR::AddrOpndKindDynamicVar, this->m_func, true); |
| srcOpnd->SetValueType(ValueType::PrimitiveOrObject); |
| newOpcode = Js::OpCode::InitLetFld; |
| } |
| else if (newOpcode == Js::OpCode::InitUndeclConstFld) |
| { |
| srcOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetUndeclBlockVarAddr(), IR::AddrOpndKindDynamicVar, this->m_func, true); |
| srcOpnd->SetValueType(ValueType::PrimitiveOrObject); |
| newOpcode = Js::OpCode::InitConstFld; |
| } |
| else |
| { |
| srcOpnd = this->BuildSrcOpnd(regSlot); |
| } |
| |
| if (isProfiled) |
| { |
| if (m_func->DoSimpleJitDynamicProfile()) |
| { |
| instr = IR::JitProfilingInstr::New(newOpcode, fieldSymOpnd, srcOpnd, m_func); |
| } |
| else if (this->m_func->HasProfileInfo()) |
| { |
| |
| instr = IR::ProfiledInstr::New(newOpcode, fieldSymOpnd, srcOpnd, m_func); |
| instr->AsProfiledInstr()->u.FldInfo() = *(m_func->GetReadOnlyProfileInfo()->GetFldInfo(inlineCacheIndex)); |
| } |
| } |
| |
| // If it hasn't been set yet |
| if (!instr) |
| { |
| instr = IR::Instr::New(newOpcode, fieldSymOpnd, srcOpnd, m_func); |
| } |
| break; |
| } |
| |
| default: |
| AssertMsg(UNREACHED, "Unknown ElementCP opcode"); |
| Fatal(); |
| } |
| |
| this->AddInstr(instr, offset); |
| |
| if(isLdFldThatWasNotProfiled && DoBailOnNoProfile()) |
| { |
| InsertBailOnNoProfile(instr); |
| } |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiledElementCP(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_ElementCP<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| this->DoClosureRegCheck(layout->Instance); |
| } |
| |
| BuildProfiledElementCP(newOpcode, offset, layout->Instance, layout->Value, layout->inlineCacheIndex, layout->profileId); |
| } |
| |
| void |
| IRBuilder::BuildProfiledElementCP(Js::OpCode newOpcode, uint32 offset, Js::RegSlot instance, Js::RegSlot regSlot, Js::CacheId inlineCacheIndex, Js::ProfileId profileId) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode); |
| |
| Assert(newOpcode == Js::OpCode::LdLen_A); |
| |
| Js::PropertyId propertyId = m_func->GetJITFunctionBody()->GetPropertyIdFromCacheId(inlineCacheIndex); |
| IR::SymOpnd * fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instance, propertyId, (Js::PropertyIdIndexType) - 1, PropertyKindData, inlineCacheIndex); |
| IR::RegOpnd * dstOpnd = this->BuildDstOpnd(regSlot); |
| |
| bool isProfiled = (profileId != Js::Constants::NoProfileId); |
| ValueType arrayType = ValueType::Uninitialized; |
| const Js::LdLenInfo * ldLenInfo = nullptr; |
| |
| if (m_func->HasProfileInfo()) |
| { |
| ldLenInfo = m_func->GetReadOnlyProfileInfo()->GetLdLenInfo(profileId); |
| arrayType = (ldLenInfo->GetArrayType()); |
| if (arrayType.IsLikelyNativeArray() && !AllowNativeArrayProfileInfo()) |
| { |
| // An opnd's value type will get replaced in the forward phase when it is not fixed. Store the array type in the ProfiledInstr. |
| arrayType = arrayType.SetArrayTypeId(Js::TypeIds_Array); |
| } |
| |
| fieldSymOpnd->SetValueType(arrayType); |
| |
| if (m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTry()) |
| { |
| isProfiled = false; |
| } |
| } |
| else |
| { |
| isProfiled = false; |
| } |
| |
| bool wasNotProfiled = false; |
| IR::Instr *instr = nullptr; |
| |
| if (isProfiled) |
| { |
| instr = this->BuildProfiledFieldLoad(newOpcode, dstOpnd, fieldSymOpnd, inlineCacheIndex, &wasNotProfiled); |
| } |
| |
| if (instr == nullptr) |
| { |
| instr = IR::Instr::New(newOpcode, dstOpnd, fieldSymOpnd, m_func); |
| } |
| else if (instr->IsJitProfilingInstr()) |
| { |
| instr->AsJitProfilingInstr()->profileId = profileId; |
| } |
| else if (instr->IsProfiledInstr()) |
| { |
| instr->AsProfiledInstr()->u.LdLenInfo() = *ldLenInfo; |
| instr->AsProfiledInstr()->u.LdLenInfo().arrayType = arrayType; |
| } |
| |
| this->AddInstr(instr, offset); |
| |
| if (wasNotProfiled && DoBailOnNoProfile()) |
| { |
| InsertBailOnNoProfile(instr); |
| } |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildElementC2 |
| /// |
| /// Build IR instr for an ElementC2 instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildElementScopedC2(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementScopedC2<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| this->DoClosureRegCheck(layout->Value2); |
| } |
| |
| BuildElementScopedC2(newOpcode, offset, layout->Value2, layout->Value, layout->PropertyIdIndex); |
| } |
| |
| void |
| IRBuilder::BuildElementScopedC2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot value2Slot, |
| Js::RegSlot regSlot, Js::PropertyIdIndexType propertyIdIndex) |
| { |
| IR::Instr * instr = nullptr; |
| |
| Js::PropertyId propertyId; |
| IR::RegOpnd * regOpnd; |
| IR::RegOpnd * value2Opnd; |
| IR::SymOpnd * fieldSymOpnd; |
| |
| Js::RegSlot instanceSlot = this->GetEnvRegForEvalCode(); |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::ScopedLdInst: |
| { |
| propertyId = m_func->GetJITFunctionBody()->GetReferencedPropertyId(propertyIdIndex); |
| fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instanceSlot, propertyId, propertyIdIndex, PropertyKindData); |
| regOpnd = this->BuildDstOpnd(regSlot); |
| value2Opnd = this->BuildDstOpnd(value2Slot); |
| |
| IR::Instr *newInstr = IR::Instr::New(Js::OpCode::Unused, value2Opnd, m_func); |
| this->AddInstr(newInstr, offset); |
| |
| instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, newInstr->GetDst(), m_func); |
| this->AddInstr(instr, offset); |
| } |
| break; |
| |
| default: |
| AssertMsg(UNREACHED, "Unknown ElementC2 opcode"); |
| Fatal(); |
| } |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildElementC2(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementC2<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| this->DoClosureRegCheck(layout->Value2); |
| this->DoClosureRegCheck(layout->Instance); |
| } |
| |
| BuildElementC2(newOpcode, offset, layout->Instance, layout->Value2, layout->Value, layout->PropertyIdIndex); |
| } |
| |
| void |
| IRBuilder::BuildElementC2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot instanceSlot, Js::RegSlot value2Slot, |
| Js::RegSlot regSlot, Js::PropertyIdIndexType propertyIdIndex) |
| { |
| IR::Instr * instr = nullptr; |
| |
| Js::PropertyId propertyId; |
| IR::RegOpnd * regOpnd; |
| IR::RegOpnd * value2Opnd; |
| IR::SymOpnd * fieldSymOpnd; |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::ProfiledLdSuperFld: |
| Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode); |
| // fall-through |
| |
| case Js::OpCode::LdSuperFld: |
| { |
| propertyId = m_func->GetJITFunctionBody()->GetPropertyIdFromCacheId(propertyIdIndex); |
| fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instanceSlot, propertyId, (Js::PropertyIdIndexType) - 1, PropertyKindData, propertyIdIndex); |
| if (fieldSymOpnd->IsPropertySymOpnd()) |
| { |
| fieldSymOpnd->AsPropertySymOpnd()->TryDisableRuntimePolymorphicCache(); |
| } |
| |
| value2Opnd = this->BuildSrcOpnd(value2Slot); |
| regOpnd = this->BuildDstOpnd(regSlot); |
| |
| instr = IR::ProfiledInstr::New(newOpcode, regOpnd, fieldSymOpnd, value2Opnd, m_func); |
| instr->AsProfiledInstr()->u.FldInfo() = *(m_func->GetReadOnlyProfileInfo()->GetFldInfo(propertyIdIndex)); |
| this->AddInstr(instr, offset); |
| } |
| break; |
| |
| case Js::OpCode::ProfiledStSuperFld: |
| Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode); |
| // fall-through |
| |
| case Js::OpCode::StSuperFld: |
| { |
| propertyId = m_func->GetJITFunctionBody()->GetPropertyIdFromCacheId(propertyIdIndex); |
| fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instanceSlot, propertyId, (Js::PropertyIdIndexType) - 1, PropertyKindData, propertyIdIndex); |
| if (fieldSymOpnd->IsPropertySymOpnd()) |
| { |
| fieldSymOpnd->AsPropertySymOpnd()->TryDisableRuntimePolymorphicCache(); |
| } |
| |
| regOpnd = this->BuildSrcOpnd(regSlot); |
| value2Opnd = this->BuildSrcOpnd(value2Slot); |
| |
| instr = IR::ProfiledInstr::New(newOpcode, fieldSymOpnd, regOpnd, value2Opnd, m_func); |
| instr->AsProfiledInstr()->u.FldInfo() = *(m_func->GetReadOnlyProfileInfo()->GetFldInfo(propertyIdIndex)); |
| this->AddInstr(instr, offset); |
| break; |
| } |
| |
| default: |
| AssertMsg(UNREACHED, "Unknown ElementC2 opcode"); |
| Fatal(); |
| } |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildElementU |
| /// |
| /// Build IR instr for an ElementU or ElementRootU instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildElementU(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementU<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Instance); |
| } |
| |
| BuildElementU(newOpcode, offset, layout->Instance, layout->PropertyIdIndex); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildElementRootU(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementRootU<SizePolicy>>(); |
| BuildElementU(newOpcode, offset, Js::FunctionBody::RootObjectRegSlot, layout->PropertyIdIndex); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildElementScopedU(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementScopedU<SizePolicy>>(); |
| BuildElementU(newOpcode, offset, GetEnvReg(), layout->PropertyIdIndex); |
| } |
| |
| void |
| IRBuilder::BuildElementU(Js::OpCode newOpcode, uint32 offset, Js::RegSlot instance, Js::PropertyIdIndexType propertyIdIndex) |
| { |
| IR::Instr * instr; |
| IR::RegOpnd * regOpnd; |
| IR::SymOpnd * fieldSymOpnd; |
| Js::PropertyId propertyId = m_func->GetJITFunctionBody()->GetReferencedPropertyId(propertyIdIndex); |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::LdLocalElemUndef: |
| if (m_func->GetLocalClosureSym()->HasByteCodeRegSlot()) |
| { |
| IR::ByteCodeUsesInstr * byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, offset); |
| byteCodeUse->SetNonOpndSymbol(m_func->GetLocalClosureSym()->m_id); |
| this->AddInstr(byteCodeUse, offset); |
| } |
| |
| instance = m_func->GetJITFunctionBody()->GetLocalClosureReg(); |
| newOpcode = Js::OpCode::LdElemUndef; |
| fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instance, propertyId, propertyIdIndex, PropertyKindData); |
| instr = IR::Instr::New(newOpcode, fieldSymOpnd, m_func); |
| break; |
| |
| // fall through |
| case Js::OpCode::LdElemUndefScoped: |
| { |
| // Store |
| PropertyKind propertyKind = PropertyKindData; |
| fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instance, propertyId, propertyIdIndex, propertyKind); |
| // Implicit root object as default instance |
| regOpnd = this->BuildSrcOpnd(Js::FunctionBody::RootObjectRegSlot); |
| |
| instr = IR::Instr::New(newOpcode, fieldSymOpnd, regOpnd, m_func); |
| break; |
| } |
| case Js::OpCode::ClearAttributes: |
| { |
| instr = IR::Instr::New(newOpcode, m_func); |
| IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(instance); |
| IR::IntConstOpnd * src2Opnd = IR::IntConstOpnd::New(propertyId, TyInt32, m_func); |
| |
| instr->SetSrc1(src1Opnd); |
| instr->SetSrc2(src2Opnd); |
| break; |
| } |
| |
| case Js::OpCode::StLocalFuncExpr: |
| fieldSymOpnd = this->BuildFieldOpnd(newOpcode, m_func->GetJITFunctionBody()->GetLocalClosureReg(), propertyId, propertyIdIndex, PropertyKindData); |
| regOpnd = this->BuildSrcOpnd(instance); |
| newOpcode = Js::OpCode::StFuncExpr; |
| instr = IR::Instr::New(newOpcode, fieldSymOpnd, regOpnd, m_func); |
| break; |
| |
| case Js::OpCode::DeleteLocalFld: |
| newOpcode = Js::OpCode::DeleteFld; |
| fieldSymOpnd = BuildFieldOpnd(newOpcode, m_func->GetJITFunctionBody()->GetLocalClosureReg(), propertyId, propertyIdIndex, PropertyKindData); |
| regOpnd = BuildDstOpnd(instance); |
| instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, m_func); |
| break; |
| |
| default: |
| { |
| fieldSymOpnd = this->BuildFieldOpnd(newOpcode, instance, propertyId, propertyIdIndex, PropertyKindData); |
| |
| instr = IR::Instr::New(newOpcode, fieldSymOpnd, m_func); |
| break; |
| } |
| } |
| |
| this->AddInstr(instr, offset); |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildAuxiliary |
| /// |
| /// Build IR instr for an Auxiliary instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| void |
| IRBuilder::BuildAuxNoReg(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| IR::Instr * instr; |
| const unaligned Js::OpLayoutAuxNoReg *auxInsn = m_jnReader.AuxNoReg(); |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::InitCachedFuncs: |
| { |
| IR::Opnd *src1Opnd = this->BuildSrcOpnd(m_func->GetJITFunctionBody()->GetLocalClosureReg()); |
| IR::Opnd *src2Opnd = this->BuildSrcOpnd(m_func->GetJITFunctionBody()->GetLocalFrameDisplayReg()); |
| IR::Opnd *src3Opnd = this->BuildAuxArrayOpnd(AuxArrayValue::AuxFuncInfoArray, auxInsn->Offset); |
| |
| instr = IR::Instr::New(Js::OpCode::ArgOut_A, IR::RegOpnd::New(TyVar, m_func), src3Opnd, m_func); |
| this->AddInstr(instr, offset); |
| |
| instr = IR::Instr::New(Js::OpCode::ArgOut_A, IR::RegOpnd::New(TyVar, m_func), src2Opnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| instr = IR::Instr::New(Js::OpCode::ArgOut_A, IR::RegOpnd::New(TyVar, m_func), src1Opnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| IR::HelperCallOpnd *helperOpnd; |
| |
| helperOpnd = IR::HelperCallOpnd::New(IR::HelperOP_InitCachedFuncs, this->m_func); |
| src2Opnd = instr->GetDst(); |
| |
| instr = IR::Instr::New(Js::OpCode::CallHelper, m_func); |
| instr->SetSrc1(helperOpnd); |
| instr->SetSrc2(src2Opnd); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| return; |
| } |
| |
| default: |
| { |
| AssertMsg(UNREACHED, "Unknown AuxNoReg opcode"); |
| Fatal(); |
| break; |
| } |
| } |
| } |
| |
| void |
| IRBuilder::BuildAuxiliary(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| const unaligned Js::OpLayoutAuxiliary *auxInsn = m_jnReader.Auxiliary(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(auxInsn->R0); |
| } |
| |
| IR::Instr *instr; |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::NewScObjectLiteral: |
| { |
| int literalObjectId = auxInsn->C1; |
| |
| IR::RegOpnd * dstOpnd; |
| IR::Opnd* srcOpnd; |
| |
| Js::RegSlot dstRegSlot = auxInsn->R0; |
| |
| // The property ID array needs to be both relocatable and available (so we can |
| // get the slot capacity), so we need to just pass the offset to lower and let |
| // lower take it from there... |
| srcOpnd = IR::IntConstOpnd::New(auxInsn->Offset, TyUint32, m_func); |
| dstOpnd = this->BuildDstOpnd(dstRegSlot); |
| dstOpnd->SetValueType(ValueType::GetObject(ObjectType::Object)); |
| instr = IR::Instr::New(newOpcode, dstOpnd, srcOpnd, m_func); |
| |
| // Because we're going to be making decisions based off the value, we have to defer |
| // this until we get to lowering. |
| instr->SetSrc2(IR::IntConstOpnd::New(literalObjectId, TyUint32, m_func)); |
| |
| if (dstOpnd->m_sym->m_isSingleDef) |
| { |
| dstOpnd->m_sym->m_isSafeThis = true; |
| } |
| break; |
| } |
| |
| case Js::OpCode::LdPropIds: |
| { |
| IR::RegOpnd * dstOpnd; |
| IR::Opnd* srcOpnd; |
| |
| Js::RegSlot dstRegSlot = auxInsn->R0; |
| |
| srcOpnd = this->BuildAuxArrayOpnd(AuxArrayValue::AuxPropertyIdArray, auxInsn->Offset); |
| dstOpnd = this->BuildDstOpnd(dstRegSlot); |
| instr = IR::Instr::New(newOpcode, dstOpnd, srcOpnd, m_func); |
| |
| if (dstOpnd->m_sym->m_isSingleDef) |
| { |
| dstOpnd->m_sym->m_isNotNumber = true; |
| } |
| |
| break; |
| } |
| |
| case Js::OpCode::NewScIntArray: |
| { |
| IR::RegOpnd* dstOpnd; |
| IR::Opnd* src1Opnd; |
| |
| src1Opnd = this->BuildAuxArrayOpnd(AuxArrayValue::AuxIntArray, auxInsn->Offset); |
| dstOpnd = this->BuildDstOpnd(auxInsn->R0); |
| |
| instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, m_func); |
| |
| const Js::TypeId arrayTypeId = m_func->IsJitInDebugMode() ? Js::TypeIds_Array : Js::TypeIds_NativeIntArray; |
| dstOpnd->SetValueType( |
| ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(true).SetArrayTypeId(arrayTypeId)); |
| dstOpnd->SetValueTypeFixed(); |
| |
| break; |
| } |
| |
| case Js::OpCode::NewScFltArray: |
| { |
| IR::RegOpnd* dstOpnd; |
| IR::Opnd* src1Opnd; |
| |
| src1Opnd = this->BuildAuxArrayOpnd(AuxArrayValue::AuxFloatArray, auxInsn->Offset); |
| dstOpnd = this->BuildDstOpnd(auxInsn->R0); |
| |
| instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, m_func); |
| |
| const Js::TypeId arrayTypeId = m_func->IsJitInDebugMode() ? Js::TypeIds_Array : Js::TypeIds_NativeFloatArray; |
| dstOpnd->SetValueType( |
| ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(true).SetArrayTypeId(arrayTypeId)); |
| dstOpnd->SetValueTypeFixed(); |
| |
| break; |
| } |
| |
| case Js::OpCode::StArrSegItem_A: |
| { |
| IR::RegOpnd* src1Opnd; |
| IR::Opnd* src2Opnd; |
| |
| src1Opnd = this->BuildSrcOpnd(auxInsn->R0); |
| src2Opnd = this->BuildAuxArrayOpnd(AuxArrayValue::AuxVarsArray, auxInsn->Offset); |
| |
| instr = IR::Instr::New(newOpcode, m_func); |
| instr->SetSrc1(src1Opnd); |
| instr->SetSrc2(src2Opnd); |
| |
| break; |
| } |
| |
| case Js::OpCode::NewScObject_A: |
| { |
| const Js::VarArrayVarCount *vars = (Js::VarArrayVarCount *)m_func->GetJITFunctionBody()->ReadFromAuxContextData(auxInsn->Offset); |
| |
| int count = Js::TaggedInt::ToInt32(vars->count); |
| |
| StackSym * symDst; |
| IR::SymOpnd * dstOpnd; |
| IR::Opnd * src1Opnd; |
| |
| // |
| // PUSH all the parameters on the auxiliary context, to the stack |
| // |
| for (int i=0;i<count; i++) |
| { |
| m_argsOnStack++; |
| |
| symDst = m_func->m_symTable->GetArgSlotSym((uint16)(i + 2)); |
| if (symDst == nullptr || (uint16)(i + 2) != (i + 2)) |
| { |
| AssertMsg(UNREACHED, "Arg count too big..."); |
| Fatal(); |
| } |
| |
| dstOpnd = IR::SymOpnd::New(symDst, TyVar, m_func); |
| src1Opnd = IR::AddrOpnd::New(vars->elements[i], IR::AddrOpndKindDynamicVar, this->m_func, true); |
| instr = IR::Instr::New(Js::OpCode::ArgOut_A, dstOpnd, src1Opnd, m_func); |
| |
| this->AddInstr(instr, offset); |
| |
| m_argStack->Push(instr); |
| } |
| |
| BuildCallI_Helper(Js::OpCode::NewScObject, offset, (Js::RegSlot)auxInsn->R0, (Js::RegSlot)auxInsn->C1, (Js::ArgSlot)count+1, Js::Constants::NoProfileId); |
| return; |
| } |
| |
| default: |
| { |
| AssertMsg(UNREACHED, "Unknown Auxiliary opcode"); |
| Fatal(); |
| break; |
| } |
| } |
| |
| this->AddInstr(instr, offset); |
| } |
| |
| void |
| IRBuilder::BuildProfiledAuxiliary(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| const unaligned Js::OpLayoutDynamicProfile<Js::OpLayoutAuxiliary> *auxInsn = m_jnReader.ProfiledAuxiliary(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(auxInsn->R0); |
| } |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::ProfiledNewScIntArray: |
| { |
| Js::ProfileId profileId = static_cast<Js::ProfileId>(auxInsn->profileId); |
| IR::RegOpnd* dstOpnd; |
| IR::Opnd* src1Opnd; |
| |
| src1Opnd = this->BuildAuxArrayOpnd(AuxArrayValue::AuxIntArray, auxInsn->Offset); |
| dstOpnd = this->BuildDstOpnd(auxInsn->R0); |
| |
| Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode); |
| IR::Instr *instr; |
| Js::ArrayCallSiteInfo *arrayInfo = nullptr; |
| Js::TypeId arrayTypeId = Js::TypeIds_Array; |
| |
| |
| if (m_func->DoSimpleJitDynamicProfile()) |
| { |
| instr = IR::JitProfilingInstr::New(newOpcode, dstOpnd, src1Opnd, m_func); |
| instr->AsJitProfilingInstr()->profileId = profileId; |
| } |
| else if (m_func->HasArrayInfo()) |
| { |
| instr = IR::ProfiledInstr::New(newOpcode, dstOpnd, src1Opnd, m_func); |
| instr->AsProfiledInstr()->u.profileId = profileId; |
| arrayInfo = m_func->GetReadOnlyProfileInfo()->GetArrayCallSiteInfo(profileId); |
| if (arrayInfo && !m_func->IsJitInDebugMode()) |
| { |
| if (arrayInfo->IsNativeIntArray()) |
| { |
| arrayTypeId = Js::TypeIds_NativeIntArray; |
| } |
| else if (arrayInfo->IsNativeFloatArray()) |
| { |
| arrayTypeId = Js::TypeIds_NativeFloatArray; |
| } |
| } |
| } |
| else |
| { |
| instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, m_func); |
| } |
| |
| ValueType dstValueType( |
| ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(true).SetArrayTypeId(arrayTypeId)); |
| if (dstValueType.IsLikelyNativeArray()) |
| { |
| dstOpnd->SetValueType(dstValueType.ToLikely()); |
| } |
| else |
| { |
| dstOpnd->SetValueType(dstValueType); |
| dstOpnd->SetValueTypeFixed(); |
| } |
| |
| StackSym *dstSym = dstOpnd->AsRegOpnd()->m_sym; |
| if (dstSym->m_isSingleDef) |
| { |
| dstSym->m_isSafeThis = true; |
| dstSym->m_isNotNumber = true; |
| } |
| this->AddInstr(instr, offset); |
| |
| break; |
| } |
| |
| case Js::OpCode::ProfiledNewScFltArray: |
| { |
| Js::ProfileId profileId = static_cast<Js::ProfileId>(auxInsn->profileId); |
| IR::RegOpnd* dstOpnd; |
| IR::Opnd* src1Opnd; |
| |
| src1Opnd = this->BuildAuxArrayOpnd(AuxArrayValue::AuxFloatArray, auxInsn->Offset); |
| dstOpnd = this->BuildDstOpnd(auxInsn->R0); |
| |
| Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode); |
| IR::Instr *instr; |
| |
| Js::ArrayCallSiteInfo *arrayInfo = nullptr; |
| |
| if (m_func->DoSimpleJitDynamicProfile()) |
| { |
| instr = IR::JitProfilingInstr::New(newOpcode, dstOpnd, src1Opnd, m_func); |
| instr->AsJitProfilingInstr()->profileId = profileId; |
| // Keep arrayInfo null because we aren't using profile data in profiling simplejit |
| } |
| else |
| { |
| instr = IR::ProfiledInstr::New(newOpcode, dstOpnd, src1Opnd, m_func); |
| instr->AsProfiledInstr()->u.profileId = profileId; |
| if (m_func->HasArrayInfo()) { |
| arrayInfo = m_func->GetReadOnlyProfileInfo()->GetArrayCallSiteInfo(profileId); |
| } |
| } |
| |
| Js::TypeId arrayTypeId; |
| if (arrayInfo && arrayInfo->IsNativeFloatArray()) |
| { |
| arrayTypeId = Js::TypeIds_NativeFloatArray; |
| } |
| else |
| { |
| arrayTypeId = Js::TypeIds_Array; |
| } |
| |
| ValueType dstValueType( |
| ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(true).SetArrayTypeId(arrayTypeId)); |
| if (dstValueType.IsLikelyNativeArray()) |
| { |
| dstOpnd->SetValueType(dstValueType.ToLikely()); |
| } |
| else |
| { |
| dstOpnd->SetValueType(dstValueType); |
| dstOpnd->SetValueTypeFixed(); |
| } |
| |
| StackSym *dstSym = dstOpnd->AsRegOpnd()->m_sym; |
| if (dstSym->m_isSingleDef) |
| { |
| dstSym->m_isSafeThis = true; |
| dstSym->m_isNotNumber = true; |
| } |
| |
| this->AddInstr(instr, offset); |
| |
| break; |
| } |
| |
| default: |
| { |
| AssertMsg(UNREACHED, "Unknown Auxiliary opcode"); |
| Fatal(); |
| break; |
| } |
| |
| } |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildReg2Aux |
| /// |
| /// Build IR instr for a Reg2Aux instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| void IRBuilder::BuildInitCachedScope(int auxOffset, int offset) |
| { |
| IR::Instr * instr; |
| IR::RegOpnd * dstOpnd; |
| IR::RegOpnd * src1Opnd; |
| IR::AddrOpnd * src2Opnd; |
| IR::Opnd* src3Opnd; |
| IR::Opnd* formalsAreLetDeclOpnd; |
| |
| src2Opnd = IR::AddrOpnd::New(m_func->GetJITFunctionBody()->GetFormalsPropIdArrayAddr(), IR::AddrOpndKindDynamicMisc, m_func); |
| Js::PropertyIdArray * propIds = m_func->GetJITFunctionBody()->GetFormalsPropIdArray(); |
| src3Opnd = this->BuildAuxObjectLiteralTypeRefOpnd(Js::ActivationObjectEx::GetLiteralObjectRef(propIds)); |
| dstOpnd = this->BuildDstOpnd(m_func->GetJITFunctionBody()->GetLocalClosureReg()); |
| |
| formalsAreLetDeclOpnd = IR::IntConstOpnd::New(propIds->hasNonSimpleParams, TyUint8, m_func); |
| |
| instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), formalsAreLetDeclOpnd, m_func); |
| this->AddInstr(instr, offset); |
| |
| instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src3Opnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src2Opnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| // Disable opt that normally gets disabled when we see LdFuncExpr in the byte code. |
| m_func->DisableCanDoInlineArgOpt(); |
| src1Opnd = IR::RegOpnd::New(TyVar, m_func); |
| IR::Instr * instrLdFuncExpr = IR::Instr::New(Js::OpCode::LdFuncExpr, src1Opnd, m_func); |
| this->AddInstr(instrLdFuncExpr, Js::Constants::NoByteCodeOffset); |
| |
| instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src1Opnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| instr = IR::Instr::New(Js::OpCode::InitCachedScope, dstOpnd, instr->GetDst(), m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| } |
| |
| void |
| IRBuilder::BuildReg2Aux(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| const unaligned Js::OpLayoutReg2Aux *auxInsn = m_jnReader.Reg2Aux(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(auxInsn->R0); |
| this->DoClosureRegCheck(auxInsn->R1); |
| } |
| |
| IR::Instr *instr; |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::SpreadArrayLiteral: |
| { |
| IR::RegOpnd * dstOpnd; |
| IR::RegOpnd * src1Opnd; |
| IR::Opnd* src2Opnd; |
| |
| Js::RegSlot dstRegSlot = auxInsn->R0; |
| Js::RegSlot srcRegSlot = auxInsn->R1; |
| |
| src1Opnd = this->BuildSrcOpnd(srcRegSlot); |
| |
| src2Opnd = this->BuildAuxArrayOpnd(AuxArrayValue::AuxIntArray, auxInsn->Offset); |
| dstOpnd = this->BuildDstOpnd(dstRegSlot); |
| |
| instr = IR::Instr::New(Js::OpCode::SpreadArrayLiteral, dstOpnd, src1Opnd, src2Opnd, m_func); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| if (dstOpnd->m_sym->m_isSingleDef) |
| { |
| dstOpnd->m_sym->m_isNotNumber = true; |
| } |
| break; |
| } |
| |
| default: |
| { |
| AssertMsg(UNREACHED, "Unknown Reg2Aux opcode"); |
| Fatal(); |
| break; |
| } |
| } |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildElementI |
| /// |
| /// Build IR instr for an ElementI instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildElementI(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementI<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| this->DoClosureRegCheck(layout->Instance); |
| this->DoClosureRegCheck(layout->Element); |
| } |
| |
| BuildElementI(newOpcode, offset, layout->Instance, layout->Element, layout->Value, Js::Constants::NoProfileId); |
| } |
| |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiledElementI(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_ElementI<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| this->DoClosureRegCheck(layout->Instance); |
| this->DoClosureRegCheck(layout->Element); |
| } |
| |
| Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode); |
| BuildElementI(newOpcode, offset, layout->Instance, layout->Element, layout->Value, layout->profileId); |
| } |
| |
| void |
| IRBuilder::BuildElementI(Js::OpCode newOpcode, uint32 offset, Js::RegSlot baseRegSlot, Js::RegSlot indexRegSlot, |
| Js::RegSlot regSlot, Js::ProfileId profileId) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| ValueType arrayType; |
| const Js::LdElemInfo *ldElemInfo = nullptr; |
| const Js::StElemInfo *stElemInfo = nullptr; |
| bool isProfiledLoad = false; |
| bool isProfiledStore = false; |
| bool isProfiledInstr = (profileId != Js::Constants::NoProfileId); |
| bool isLdElemOrStElemThatWasNotProfiled = false; |
| |
| if (isProfiledInstr) |
| { |
| switch (newOpcode) |
| { |
| case Js::OpCode::LdElemI_A: |
| if (!DoLoadInstructionArrayProfileInfo()) |
| { |
| break; |
| } |
| ldElemInfo = this->m_func->GetReadOnlyProfileInfo()->GetLdElemInfo(profileId); |
| arrayType = ldElemInfo->GetArrayType(); |
| isLdElemOrStElemThatWasNotProfiled = !ldElemInfo->WasProfiled(); |
| isProfiledLoad = true; |
| break; |
| |
| case Js::OpCode::StElemI_A: |
| case Js::OpCode::StElemI_A_Strict: |
| if (!DoLoadInstructionArrayProfileInfo()) |
| { |
| break; |
| } |
| isProfiledStore = true; |
| stElemInfo = this->m_func->GetReadOnlyProfileInfo()->GetStElemInfo(profileId); |
| arrayType = stElemInfo->GetArrayType(); |
| isLdElemOrStElemThatWasNotProfiled = !stElemInfo->WasProfiled(); |
| break; |
| } |
| } |
| |
| IR::Instr * instr; |
| IR::RegOpnd * regOpnd; |
| |
| IR::IndirOpnd * indirOpnd; |
| indirOpnd = this->BuildIndirOpnd(this->BuildSrcOpnd(baseRegSlot), this->BuildSrcOpnd(indexRegSlot)); |
| |
| if (isProfiledLoad || isProfiledStore) |
| { |
| if(arrayType.IsLikelyNativeArray() && !AllowNativeArrayProfileInfo()) |
| { |
| arrayType = arrayType.SetArrayTypeId(Js::TypeIds_Array); |
| |
| // An opnd's value type will get replaced in the forward phase when it is not fixed. Store the array type in the |
| // ProfiledInstr. |
| if(isProfiledLoad) |
| { |
| Js::LdElemInfo *const newLdElemInfo = JitAnew(m_func->m_alloc, Js::LdElemInfo, *ldElemInfo); |
| newLdElemInfo->arrayType = arrayType; |
| ldElemInfo = newLdElemInfo; |
| } |
| else |
| { |
| Js::StElemInfo *const newStElemInfo = JitAnew(m_func->m_alloc, Js::StElemInfo, *stElemInfo); |
| newStElemInfo->arrayType = arrayType; |
| stElemInfo = newStElemInfo; |
| } |
| } |
| indirOpnd->GetBaseOpnd()->SetValueType(arrayType); |
| |
| if (m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTry()) |
| { |
| isProfiledLoad = false; |
| isProfiledStore = false; |
| } |
| } |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::LdMethodElem: |
| case Js::OpCode::LdElemI_A: |
| case Js::OpCode::DeleteElemI_A: |
| case Js::OpCode::DeleteElemIStrict_A: |
| case Js::OpCode::TypeofElem: |
| { |
| // Evaluate to register |
| |
| regOpnd = this->BuildDstOpnd(regSlot); |
| |
| if (m_func->DoSimpleJitDynamicProfile() && isProfiledInstr) |
| { |
| instr = IR::JitProfilingInstr::New(newOpcode, regOpnd, indirOpnd, m_func); |
| instr->AsJitProfilingInstr()->profileId = profileId; |
| } |
| else if (isProfiledLoad) |
| { |
| instr = IR::ProfiledInstr::New(newOpcode, regOpnd, indirOpnd, m_func); |
| instr->AsProfiledInstr()->u.ldElemInfo = ldElemInfo; |
| } |
| else |
| { |
| instr = IR::Instr::New(newOpcode, regOpnd, indirOpnd, m_func); |
| } |
| break; |
| } |
| |
| case Js::OpCode::StElemI_A: |
| case Js::OpCode::StElemI_A_Strict: |
| { |
| // Store |
| |
| regOpnd = this->BuildSrcOpnd(regSlot); |
| |
| if (m_func->DoSimpleJitDynamicProfile() && isProfiledInstr) |
| { |
| instr = IR::JitProfilingInstr::New(newOpcode, indirOpnd, regOpnd, m_func); |
| instr->AsJitProfilingInstr()->profileId = profileId; |
| } |
| else if (isProfiledStore) |
| { |
| instr = IR::ProfiledInstr::New(newOpcode, indirOpnd, regOpnd, m_func); |
| instr->AsProfiledInstr()->u.stElemInfo = stElemInfo; |
| } |
| else |
| { |
| instr = IR::Instr::New(newOpcode, indirOpnd, regOpnd, m_func); |
| } |
| break; |
| } |
| |
| case Js::OpCode::InitSetElemI: |
| case Js::OpCode::InitGetElemI: |
| case Js::OpCode::InitComputedProperty: |
| case Js::OpCode::InitClassMemberComputedName: |
| case Js::OpCode::InitClassMemberGetComputedName: |
| case Js::OpCode::InitClassMemberSetComputedName: |
| { |
| |
| regOpnd = this->BuildSrcOpnd(regSlot); |
| |
| instr = IR::Instr::New(newOpcode, indirOpnd, regOpnd, m_func); |
| break; |
| } |
| |
| default: |
| AssertMsg(false, "Unknown ElementI opcode"); |
| return; |
| |
| } |
| |
| this->AddInstr(instr, offset); |
| |
| if(isLdElemOrStElemThatWasNotProfiled && DoBailOnNoProfile()) |
| { |
| InsertBailOnNoProfile(instr); |
| } |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildElementUnsigned1 |
| /// |
| /// Build IR instr for an ElementUnsigned1 instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildElementUnsigned1(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ElementUnsigned1<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Value); |
| this->DoClosureRegCheck(layout->Instance); |
| } |
| |
| BuildElementUnsigned1(newOpcode, offset, layout->Instance, layout->Element, layout->Value); |
| } |
| |
| void |
| IRBuilder::BuildElementUnsigned1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot baseRegSlot, uint32 index, Js::RegSlot regSlot) |
| { |
| // This is an array-style access with a constant (integer) index. |
| // Embed the index in the indir opnd as a constant offset. |
| |
| IR::Instr * instr; |
| |
| const bool simpleJit = m_func->DoSimpleJitDynamicProfile(); |
| |
| IR::RegOpnd * regOpnd; |
| IR::IndirOpnd * indirOpnd; |
| IR::RegOpnd * baseOpnd; |
| Js::OpCode opcode; |
| switch (newOpcode) |
| { |
| case Js::OpCode::StArrItemI_CI4: |
| { |
| baseOpnd = this->BuildSrcOpnd(baseRegSlot); |
| |
| // This instruction must not create missing values in the array |
| baseOpnd->SetValueType( |
| ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(false).SetArrayTypeId(Js::TypeIds_Array)); |
| baseOpnd->SetValueTypeFixed(); |
| |
| // In the case of simplejit, we won't know the exact type of array used until run time. Due to this, |
| // we must use the specialized version of StElemC in Lowering. |
| opcode = simpleJit ? Js::OpCode::StElemC : Js::OpCode::StElemI_A; |
| break; |
| } |
| |
| case Js::OpCode::StArrItemC_CI4: |
| { |
| baseOpnd = IR::RegOpnd::New(TyVar, m_func); |
| // Insert LdArrHead as the next instr and clear the offset to avoid duplication. |
| IR::RegOpnd *const arrayOpnd = this->BuildSrcOpnd(baseRegSlot); |
| |
| // This instruction must not create missing values in the array |
| arrayOpnd->SetValueType( |
| ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(false).SetArrayTypeId(Js::TypeIds_Array)); |
| arrayOpnd->SetValueTypeFixed(); |
| |
| this->AddInstr(IR::Instr::New(Js::OpCode::LdArrHead, baseOpnd, arrayOpnd, m_func), offset); |
| offset = Js::Constants::NoByteCodeOffset; |
| opcode = Js::OpCode::StArrSegElemC; |
| break; |
| } |
| case Js::OpCode::StArrSegItem_CI4: |
| { |
| baseOpnd = this->BuildSrcOpnd(baseRegSlot, TyVar); |
| |
| // This instruction must not create missing values in the array |
| |
| opcode = Js::OpCode::StArrSegElemC; |
| break; |
| } |
| case Js::OpCode::StArrInlineItem_CI4: |
| { |
| baseOpnd = this->BuildSrcOpnd(baseRegSlot); |
| |
| IR::Opnd *defOpnd = baseOpnd->m_sym->m_instrDef ? baseOpnd->m_sym->m_instrDef->GetDst() : nullptr; |
| if (!defOpnd) |
| { |
| // The array sym may be multi-def because of oddness in the renumbering of temps -- for instance, |
| // if there's a loop increment expression whose result is unused (ExprGen only, probably). |
| FOREACH_INSTR_BACKWARD(tmpInstr, m_func->m_exitInstr->m_prev) |
| { |
| if (tmpInstr->GetDst()) |
| { |
| if (tmpInstr->GetDst()->IsEqual(baseOpnd)) |
| { |
| defOpnd = tmpInstr->GetDst(); |
| break; |
| } |
| else if (tmpInstr->m_opcode == Js::OpCode::StElemC && |
| tmpInstr->GetDst()->AsIndirOpnd()->GetBaseOpnd()->IsEqual(baseOpnd)) |
| { |
| defOpnd = tmpInstr->GetDst()->AsIndirOpnd()->GetBaseOpnd(); |
| break; |
| } |
| } |
| } |
| NEXT_INSTR_BACKWARD; |
| } |
| AnalysisAssert(defOpnd); |
| |
| // This instruction must not create missing values in the array |
| baseOpnd->SetValueType(defOpnd->GetValueType()); |
| |
| opcode = Js::OpCode::StElemC; |
| break; |
| } |
| default: |
| AssertMsg(false, "Unknown ElementUnsigned1 opcode"); |
| return; |
| |
| } |
| |
| indirOpnd = this->BuildIndirOpnd(baseOpnd, index); |
| regOpnd = this->BuildSrcOpnd(regSlot); |
| if (simpleJit) |
| { |
| instr = IR::JitProfilingInstr::New(opcode, indirOpnd, regOpnd, m_func); |
| } |
| else if(opcode == Js::OpCode::StElemC && !baseOpnd->GetValueType().IsUninitialized()) |
| { |
| // An opnd's value type will get replaced in the forward phase when it is not fixed. Store the array type in the |
| // ProfiledInstr. |
| IR::ProfiledInstr *const profiledInstr = IR::ProfiledInstr::New(opcode, indirOpnd, regOpnd, m_func); |
| Js::StElemInfo *const stElemInfo = JitAnew(m_func->m_alloc, Js::StElemInfo); |
| stElemInfo->arrayType = baseOpnd->GetValueType(); |
| profiledInstr->u.stElemInfo = stElemInfo; |
| instr = profiledInstr; |
| } |
| else |
| { |
| instr = IR::Instr::New(opcode, indirOpnd, regOpnd, m_func); |
| } |
| |
| this->AddInstr(instr, offset); |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildArgIn |
| /// |
| /// Build IR instr for an ArgIn instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| void |
| IRBuilder::BuildArgIn0(uint32 offset, Js::RegSlot dstRegSlot) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(Js::OpCode::ArgIn0)); |
| BuildArgIn(offset, dstRegSlot, 0); |
| } |
| |
| void |
| IRBuilder::BuildArgIn(uint32 offset, Js::RegSlot dstRegSlot, uint16 argument) |
| { |
| IR::Instr * instr; |
| IR::SymOpnd * srcOpnd; |
| IR::RegOpnd * dstOpnd; |
| StackSym * symSrc = StackSym::NewParamSlotSym(argument + 1, m_func); |
| |
| this->m_func->SetArgOffset(symSrc, (argument + LowererMD::GetFormalParamOffset()) * MachPtr); |
| |
| srcOpnd = IR::SymOpnd::New(symSrc, TyVar, m_func); |
| dstOpnd = this->BuildDstOpnd(dstRegSlot); |
| |
| if (!this->m_func->IsLoopBody() && this->m_func->HasProfileInfo()) |
| { |
| // Skip "this" pointer; "this" profile data is captured by ProfiledLdThis. |
| // Subtract 1 to skip "this" pointer, subtract 1 again to get the index to index into profileData->parameterInfo. |
| int paramSlotIndex = symSrc->GetParamSlotNum() - 2; |
| if (paramSlotIndex >= 0) |
| { |
| ValueType profiledValueType; |
| profiledValueType = this->m_func->GetReadOnlyProfileInfo()->GetParameterInfo(static_cast<Js::ArgSlot>(paramSlotIndex)); |
| dstOpnd->SetValueType(profiledValueType); |
| } |
| } |
| |
| instr = IR::Instr::New(Js::OpCode::ArgIn_A, dstOpnd, srcOpnd, m_func); |
| this->AddInstr(instr, offset); |
| } |
| |
| void |
| IRBuilder::BuildArgInRest() |
| { |
| IR::RegOpnd * dstOpnd = this->BuildDstOpnd(m_func->GetJITFunctionBody()->GetRestParamRegSlot()); |
| IR::Instr *instr = IR::Instr::New(Js::OpCode::ArgIn_Rest, dstOpnd, m_func); |
| this->AddInstr(instr, (uint32)-1); |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildArg |
| /// |
| /// Build IR instr for an ArgOut instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildArgNoSrc(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_ArgNoSrc<SizePolicy>>(); |
| BuildArg(Js::OpCode::ArgOut_A, offset, layout->Arg, this->GetEnvRegForInnerFrameDisplay()); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildArg(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Arg<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Reg); |
| } |
| |
| BuildArg(newOpcode, offset, layout->Arg, layout->Reg); |
| } |
| |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiledArg(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_Arg<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Reg); |
| } |
| |
| newOpcode = Js::OpCode::ArgOut_A; |
| BuildArg(newOpcode, offset, layout->Arg, layout->Reg); |
| } |
| |
| void |
| IRBuilder::BuildArg(Js::OpCode newOpcode, uint32 offset, Js::ArgSlot argument, Js::RegSlot srcRegSlot) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| IR::Instr * instr; |
| IRType type = TyVar; |
| if (newOpcode == Js::OpCode::ArgOut_ANonVar) |
| { |
| newOpcode = Js::OpCode::ArgOut_A; |
| type = TyMachPtr; |
| } |
| m_argsOnStack++; |
| StackSym * symDst; |
| |
| Assert(argument < USHRT_MAX); |
| symDst = m_func->m_symTable->GetArgSlotSym((uint16)(argument+1)); |
| if (symDst == nullptr || (uint16)(argument + 1) != (argument + 1)) |
| { |
| AssertMsg(UNREACHED, "Arg count too big..."); |
| Fatal(); |
| } |
| |
| IR::SymOpnd * dstOpnd = IR::SymOpnd::New(symDst, type, m_func); |
| IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(srcRegSlot, type); |
| instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, m_func); |
| |
| this->AddInstr(instr, offset); |
| |
| m_argStack->Push(instr); |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildStartCall |
| /// |
| /// Build IR instr for a StartCall instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| void |
| IRBuilder::BuildStartCall(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(newOpcode == Js::OpCode::StartCall); |
| |
| const unaligned Js::OpLayoutStartCall * regLayout = m_jnReader.StartCall(); |
| Js::ArgSlot ArgCount = regLayout->ArgCount; |
| |
| IR::Instr * instr; |
| IR::RegOpnd * dstOpnd; |
| |
| // Dst of StartCall is always r0... Let's give it a new dst such that it can |
| // be singleDef. |
| |
| dstOpnd = IR::RegOpnd::New(TyVar, m_func); |
| |
| #if DBG |
| m_callsOnStack++; |
| #endif |
| |
| IntConstType value = ArgCount; |
| IR::IntConstOpnd * srcOpnd; |
| |
| srcOpnd = IR::IntConstOpnd::New(value, TyInt32, m_func); |
| instr = IR::Instr::New(newOpcode, dstOpnd, srcOpnd, m_func); |
| |
| this->AddInstr(instr, offset); |
| |
| // Keep a stack of arg instructions such that we can link them up once we see |
| // the call that consumes them. |
| |
| m_argStack->Push(instr); |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildCallI |
| /// |
| /// Build IR instr for a CallI instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildCallI(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(Js::OpCodeUtil::IsCallOp(newOpcode) || newOpcode == Js::OpCode::NewScObject || newOpcode == Js::OpCode::NewScObjArray); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_CallI<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Return); |
| this->DoClosureRegCheck(layout->Function); |
| } |
| |
| BuildCallI_Helper(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, Js::Constants::NoProfileId); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildCallIFlags(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(Js::OpCodeUtil::IsCallOp(newOpcode) || newOpcode == Js::OpCode::NewScObject || newOpcode == Js::OpCode::NewScObjArray); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_CallIFlags<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Return); |
| this->DoClosureRegCheck(layout->Function); |
| } |
| |
| IR::Instr* instr = BuildCallI_Helper(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, Js::Constants::NoProfileId, layout->callFlags); |
| Assert(instr->m_opcode == Js::OpCode::CallIFlags); |
| if (instr->m_opcode == Js::OpCode::CallIFlags) |
| { |
| instr->m_opcode = |
| layout->callFlags == (Js::CallFlags::CallFlags_NewTarget | Js::CallFlags::CallFlags_New | Js::CallFlags::CallFlags_ExtraArg) ? Js::OpCode::CallINewTargetNew : |
| layout->callFlags == Js::CallFlags::CallFlags_New ? Js::OpCode::CallINew : |
| instr->m_opcode; |
| } |
| } |
| |
| void IRBuilder::BuildLdSpreadIndices(uint32 offset, uint32 spreadAuxOffset) |
| { |
| // Link up the LdSpreadIndices instr to be the first in the arg chain. This will allow us to find it in Lowerer easier. |
| IR::Opnd *auxArg = this->BuildAuxArrayOpnd(AuxArrayValue::AuxIntArray, spreadAuxOffset); |
| IR::Instr *instr = IR::Instr::New(Js::OpCode::LdSpreadIndices, m_func); |
| instr->SetSrc1(auxArg); |
| |
| // Create the link to the first arg. |
| Js::RegSlot lastArg = m_argStack->Head()->GetDst()->AsSymOpnd()->GetStackSym()->GetArgSlotNum(); |
| instr->SetDst(IR::SymOpnd::New(m_func->m_symTable->GetArgSlotSym((uint16) (lastArg + 1)), TyVar, m_func)); |
| this->AddInstr(instr, Js::Constants::NoByteCodeOffset); |
| |
| m_argStack->Push(instr); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildCallIExtended(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_CallIExtended<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Return); |
| this->DoClosureRegCheck(layout->Function); |
| } |
| |
| BuildCallIExtended(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->Options, layout->SpreadAuxOffset); |
| } |
| |
| IR::Instr* |
| IRBuilder::BuildCallIExtended(Js::OpCode newOpcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function, |
| Js::ArgSlot argCount, Js::CallIExtendedOptions options, uint32 spreadAuxOffset, Js::CallFlags flags) |
| { |
| if (options & Js::CallIExtended_SpreadArgs) |
| { |
| BuildLdSpreadIndices(offset, spreadAuxOffset); |
| } |
| return BuildCallI_Helper(newOpcode, offset, returnValue, function, argCount, Js::Constants::NoProfileId, flags); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildCallIWithICIndex(Js::OpCode newOpcode, uint32 offset) |
| { |
| AssertMsg(false, "NYI"); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildCallIFlagsWithICIndex(Js::OpCode newOpcode, uint32 offset) |
| { |
| AssertMsg(false, "NYI"); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildCallIExtendedFlags(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_CallIExtendedFlags<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Return); |
| this->DoClosureRegCheck(layout->Function); |
| } |
| |
| IR::Instr* instr = BuildCallIExtended(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->Options, layout->SpreadAuxOffset, layout->callFlags); |
| |
| Assert(instr->m_opcode == Js::OpCode::CallIExtendedFlags); |
| if (instr->m_opcode == Js::OpCode::CallIExtendedFlags) |
| { |
| instr->m_opcode = |
| layout->callFlags == Js::CallFlags::CallFlags_ExtraArg ? Js::OpCode::CallIEval : |
| layout->callFlags == Js::CallFlags::CallFlags_New ? Js::OpCode::CallIExtendedNew : |
| layout->callFlags == (Js::CallFlags::CallFlags_New | Js::CallFlags::CallFlags_ExtraArg | Js::CallFlags::CallFlags_NewTarget) ? Js::OpCode::CallIExtendedNewTargetNew : |
| instr->m_opcode; |
| } |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildCallIExtendedWithICIndex(Js::OpCode newOpcode, uint32 offset) |
| { |
| AssertMsg(false, "NYI"); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildCallIExtendedFlagsWithICIndex(Js::OpCode newOpcode, uint32 offset) |
| { |
| AssertMsg(false, "NYI"); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiledCallIFlagsWithICIndex(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOpWithICIndex(newOpcode) || Js::OpCodeUtil::IsProfiledCallOpWithICIndex(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIFlagsWithICIndex<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Return); |
| this->DoClosureRegCheck(layout->Function); |
| } |
| |
| IR::Instr* instr = BuildProfiledCallIWithICIndex(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId, layout->inlineCacheIndex); |
| Assert(instr->m_opcode == Js::OpCode::CallIFlags); |
| if (instr->m_opcode == Js::OpCode::CallIFlags) |
| { |
| instr->m_opcode = |
| layout->callFlags == (Js::CallFlags::CallFlags_NewTarget | Js::CallFlags::CallFlags_New | Js::CallFlags::CallFlags_ExtraArg) ? Js::OpCode::CallINewTargetNew : |
| layout->callFlags == Js::CallFlags::CallFlags_New ? Js::OpCode::CallINew : |
| instr->m_opcode; |
| } |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiledCallIWithICIndex(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOpWithICIndex(newOpcode) || Js::OpCodeUtil::IsProfiledCallOpWithICIndex(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Return); |
| this->DoClosureRegCheck(layout->Function); |
| } |
| |
| BuildProfiledCallIWithICIndex(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId, layout->inlineCacheIndex); |
| } |
| |
| IR::Instr* |
| IRBuilder::BuildProfiledCallIWithICIndex(Js::OpCode opcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function, |
| Js::ArgSlot argCount, Js::ProfileId profileId, Js::InlineCacheIndex inlineCacheIndex) |
| { |
| return BuildProfiledCallI(opcode, offset, returnValue, function, argCount, profileId, Js::CallFlags_None, inlineCacheIndex); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiledCallIExtendedFlags(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOp(newOpcode) || Js::OpCodeUtil::IsProfiledCallOp(newOpcode) |
| || Js::OpCodeUtil::IsProfiledReturnTypeCallOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIExtendedFlags<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Return); |
| this->DoClosureRegCheck(layout->Function); |
| } |
| |
| IR::Instr* instr = BuildProfiledCallIExtended(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId, layout->Options, layout->SpreadAuxOffset, layout->callFlags); |
| Assert(instr->m_opcode == Js::OpCode::CallIExtendedFlags); |
| if (instr->m_opcode == Js::OpCode::CallIExtendedFlags) |
| { |
| instr->m_opcode = |
| layout->callFlags == Js::CallFlags::CallFlags_ExtraArg ? Js::OpCode::CallIEval : |
| layout->callFlags == Js::CallFlags::CallFlags_New ? Js::OpCode::CallIExtendedNew : |
| layout->callFlags == (Js::CallFlags::CallFlags_New | Js::CallFlags::CallFlags_ExtraArg | Js::CallFlags::CallFlags_NewTarget) ? Js::OpCode::CallIExtendedNewTargetNew : |
| instr->m_opcode; |
| } |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiledCallIExtendedWithICIndex(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOpWithICIndex(newOpcode) || Js::OpCodeUtil::IsProfiledCallOpWithICIndex(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIExtendedWithICIndex<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Return); |
| this->DoClosureRegCheck(layout->Function); |
| } |
| |
| BuildProfiledCallIExtendedWithICIndex(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId, layout->Options, layout->SpreadAuxOffset); |
| } |
| |
| void |
| IRBuilder::BuildProfiledCallIExtendedWithICIndex(Js::OpCode opcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function, |
| Js::ArgSlot argCount, Js::ProfileId profileId, Js::CallIExtendedOptions options, uint32 spreadAuxOffset) |
| { |
| BuildProfiledCallIExtended(opcode, offset, returnValue, function, argCount, profileId, options, spreadAuxOffset); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiledCallIExtendedFlagsWithICIndex(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOpWithICIndex(newOpcode) || Js::OpCodeUtil::IsProfiledCallOpWithICIndex(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIExtendedFlagsWithICIndex<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Return); |
| this->DoClosureRegCheck(layout->Function); |
| } |
| |
| IR::Instr* instr = BuildProfiledCallIExtended(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId, layout->Options, layout->SpreadAuxOffset); |
| Assert(instr->m_opcode == Js::OpCode::CallIExtendedFlags); |
| if (instr->m_opcode == Js::OpCode::CallIExtendedFlags) |
| { |
| instr->m_opcode = |
| layout->callFlags == Js::CallFlags::CallFlags_ExtraArg ? Js::OpCode::CallIEval : |
| layout->callFlags == Js::CallFlags::CallFlags_New ? Js::OpCode::CallIExtendedNew : |
| layout->callFlags == (Js::CallFlags::CallFlags_New | Js::CallFlags::CallFlags_ExtraArg | Js::CallFlags::CallFlags_NewTarget) ? Js::OpCode::CallIExtendedNewTargetNew : |
| instr->m_opcode; |
| } |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiledCallI(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOp(newOpcode) || Js::OpCodeUtil::IsProfiledCallOp(newOpcode) |
| || Js::OpCodeUtil::IsProfiledReturnTypeCallOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallI<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Return); |
| this->DoClosureRegCheck(layout->Function); |
| } |
| |
| BuildProfiledCallI(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiledCallIFlags(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOp(newOpcode) || Js::OpCodeUtil::IsProfiledCallOp(newOpcode) |
| || Js::OpCodeUtil::IsProfiledReturnTypeCallOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIFlags<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Return); |
| this->DoClosureRegCheck(layout->Function); |
| } |
| |
| IR::Instr* instr = BuildProfiledCallI(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId, layout->callFlags); |
| Assert(instr->m_opcode == Js::OpCode::CallIFlags); |
| if (instr->m_opcode == Js::OpCode::CallIFlags) |
| { |
| instr->m_opcode = |
| layout->callFlags == (Js::CallFlags::CallFlags_NewTarget | Js::CallFlags::CallFlags_New | Js::CallFlags::CallFlags_ExtraArg) ? Js::OpCode::CallINewTargetNew : |
| layout->callFlags == Js::CallFlags::CallFlags_New ? Js::OpCode::CallINew : |
| instr->m_opcode; |
| } |
| } |
| |
| IR::Instr * |
| IRBuilder::BuildProfiledCallI(Js::OpCode opcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function, |
| Js::ArgSlot argCount, Js::ProfileId profileId, Js::CallFlags flags, Js::InlineCacheIndex inlineCacheIndex) |
| { |
| Js::OpCode newOpcode; |
| ValueType returnType; |
| bool isProtectedByNoProfileBailout = false; |
| |
| if (opcode == Js::OpCode::ProfiledNewScObject || opcode == Js::OpCode::ProfiledNewScObjectWithICIndex |
| || opcode == Js::OpCode::ProfiledNewScObjectSpread) |
| { |
| newOpcode = opcode; |
| Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(newOpcode); |
| Assert(newOpcode == Js::OpCode::NewScObject || newOpcode == Js::OpCode::NewScObjectSpread); |
| if (!this->m_func->HasProfileInfo()) |
| { |
| returnType = ValueType::GetObject(ObjectType::UninitializedObject); |
| } |
| else |
| { |
| // If we have profile data, make use of it |
| returnType = this->m_func->GetReadOnlyProfileInfo()->GetReturnType(opcode, profileId); |
| } |
| } |
| else |
| { |
| if (this->m_func->HasProfileInfo()) |
| { |
| returnType = this->m_func->GetReadOnlyProfileInfo()->GetReturnType(opcode, profileId); |
| } |
| |
| if (opcode < Js::OpCode::ProfiledReturnTypeCallI) |
| { |
| newOpcode = Js::OpCodeUtil::ConvertProfiledCallOpToNonProfiled(opcode); |
| if(DoBailOnNoProfile()) |
| { |
| if(this->m_func->GetWorkItem()->GetJITTimeInfo()) |
| { |
| const FunctionJITTimeInfo *inlinerData = this->m_func->GetWorkItem()->GetJITTimeInfo(); |
| if (!(this->IsLoopBody() && PHASE_OFF(Js::InlineInJitLoopBodyPhase, this->m_func)) |
| && inlinerData && inlinerData->GetInlineesBV()) |
| { |
| AssertOrFailFast(profileId < inlinerData->GetInlineesBV()->Length()); |
| if (!inlinerData->GetInlineesBV()->Test(profileId) |
| #if DBG |
| || (PHASE_STRESS(Js::BailOnNoProfilePhase, this->m_func->GetTopFunc()) |
| && (CONFIG_FLAG(SkipFuncCountForBailOnNoProfile) < 0 |
| || this->m_func->m_callSiteCount >= (uint)CONFIG_FLAG(SkipFuncCountForBailOnNoProfile))) |
| #endif |
| ) |
| { |
| this->InsertBailOnNoProfile(offset); |
| isProtectedByNoProfileBailout = true; |
| } |
| } |
| |
| if (!isProtectedByNoProfileBailout) |
| { |
| this->callTreeHasSomeProfileInfo = true; |
| } |
| } |
| #if DBG |
| this->m_func->m_callSiteCount++; |
| #endif |
| } |
| } |
| else |
| { |
| // Changing this opcode into a non ReturnTypeCall* opcode is done in BuildCallI_Helper |
| newOpcode = opcode; |
| } |
| } |
| IR::Instr * callInstr = BuildCallI_Helper(newOpcode, offset, returnValue, function, argCount, profileId, flags, inlineCacheIndex); |
| callInstr->isCallInstrProtectedByNoProfileBailout = isProtectedByNoProfileBailout; |
| |
| if (callInstr->GetDst() && (callInstr->GetDst()->GetValueType().IsUninitialized() || callInstr->GetDst()->GetValueType() == ValueType::UninitializedObject)) |
| { |
| callInstr->GetDst()->SetValueType(returnType); |
| } |
| return callInstr; |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiledCallIExtended(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOp(newOpcode) || Js::OpCodeUtil::IsProfiledCallOp(newOpcode) |
| || Js::OpCodeUtil::IsProfiledReturnTypeCallOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIExtended<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Return); |
| this->DoClosureRegCheck(layout->Function); |
| } |
| |
| BuildProfiledCallIExtended(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId, layout->Options, layout->SpreadAuxOffset); |
| } |
| |
| IR::Instr * |
| IRBuilder::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) |
| { |
| if (options & Js::CallIExtended_SpreadArgs) |
| { |
| BuildLdSpreadIndices(offset, spreadAuxOffset); |
| } |
| return BuildProfiledCallI(opcode, offset, returnValue, function, argCount, profileId, flags); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiled2CallI(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile2<Js::OpLayoutT_CallI<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Return); |
| this->DoClosureRegCheck(layout->Function); |
| } |
| |
| BuildProfiled2CallI(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId, layout->profileId2); |
| } |
| |
| void |
| IRBuilder::BuildProfiled2CallI(Js::OpCode opcode, uint32 offset, Js::RegSlot returnValue, Js::RegSlot function, |
| Js::ArgSlot argCount, Js::ProfileId profileId, Js::ProfileId profileId2) |
| { |
| Assert(opcode == Js::OpCode::ProfiledNewScObjArray || opcode == Js::OpCode::ProfiledNewScObjArraySpread); |
| Js::OpCodeUtil::ConvertNonCallOpToNonProfiled(opcode); |
| |
| Js::OpCode useOpcode = opcode; |
| // We either want to provide the array profile id (profileId2) to the native array creation or the call profileid (profileId) |
| // to the call to NewScObject |
| Js::ProfileId useProfileId = profileId2; |
| Js::TypeId arrayTypeId = Js::TypeIds_Array; |
| if (returnValue != Js::Constants::NoRegister) |
| { |
| Js::ArrayCallSiteInfo *arrayCallSiteInfo = nullptr; |
| if (m_func->HasArrayInfo()) |
| { |
| arrayCallSiteInfo = m_func->GetReadOnlyProfileInfo()->GetArrayCallSiteInfo(profileId2); |
| } |
| if (arrayCallSiteInfo && !m_func->IsJitInDebugMode()) |
| { |
| if (arrayCallSiteInfo->IsNativeIntArray()) |
| { |
| arrayTypeId = Js::TypeIds_NativeIntArray; |
| } |
| else if (arrayCallSiteInfo->IsNativeFloatArray()) |
| { |
| arrayTypeId = Js::TypeIds_NativeFloatArray; |
| } |
| } |
| else |
| { |
| useOpcode = (opcode == Js::OpCode::NewScObjArraySpread) ? Js::OpCode::NewScObjectSpread : Js::OpCode::NewScObject; |
| useProfileId = profileId; |
| } |
| } |
| else |
| { |
| useOpcode = (opcode == Js::OpCode::NewScObjArraySpread) ? Js::OpCode::NewScObjectSpread : Js::OpCode::NewScObject; |
| useProfileId = profileId; |
| } |
| IR::Instr * callInstr = BuildCallI_Helper(useOpcode, offset, returnValue, function, argCount, useProfileId); |
| if (callInstr->GetDst()) |
| { |
| callInstr->GetDst()->SetValueType( |
| ValueType::GetObject(ObjectType::Array).ToLikely().SetHasNoMissingValues(true).SetArrayTypeId(arrayTypeId)); |
| } |
| if (callInstr->IsJitProfilingInstr()) |
| { |
| // If we happened to decide in BuildCallI_Helper that this should be a jit profiling instr, then save the fact that it is |
| // a "new Array(args, ...)" call and also save the array profile id (profileId2) |
| callInstr->AsJitProfilingInstr()->isNewArray = true; |
| callInstr->AsJitProfilingInstr()->arrayProfileId = profileId2; |
| // Double check that this profileId made it to the JitProfilingInstr like we expect it to. |
| Assert(callInstr->AsJitProfilingInstr()->profileId == profileId); |
| } |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildProfiled2CallIExtended(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutDynamicProfile2<Js::OpLayoutT_CallIExtended<SizePolicy>>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Return); |
| this->DoClosureRegCheck(layout->Function); |
| } |
| |
| BuildProfiled2CallIExtended(newOpcode, offset, layout->Return, layout->Function, layout->ArgCount, layout->profileId, layout->profileId2, layout->Options, layout->SpreadAuxOffset); |
| } |
| |
| void |
| IRBuilder::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) |
| { |
| if (options & Js::CallIExtended_SpreadArgs) |
| { |
| BuildLdSpreadIndices(offset, spreadAuxOffset); |
| } |
| BuildProfiled2CallI(opcode, offset, returnValue, function, argCount, profileId, profileId2); |
| } |
| |
| IR::Instr * |
| IRBuilder::BuildCallI_Helper(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot Src1RegSlot, Js::ArgSlot ArgCount, Js::ProfileId profileId, Js::CallFlags flags, Js::InlineCacheIndex inlineCacheIndex) |
| { |
| IR::Instr * instr; |
| IR::RegOpnd * dstOpnd; |
| IR::RegOpnd * src1Opnd; |
| StackSym * symDst; |
| |
| src1Opnd = this->BuildSrcOpnd(Src1RegSlot); |
| if (dstRegSlot == Js::Constants::NoRegister) |
| { |
| dstOpnd = nullptr; |
| symDst = nullptr; |
| } |
| else |
| { |
| dstOpnd = this->BuildDstOpnd(dstRegSlot); |
| symDst = dstOpnd->m_sym; |
| } |
| |
| const bool jitProfiling = m_func->DoSimpleJitDynamicProfile(); |
| bool profiledReturn = false; |
| if (Js::OpCodeUtil::IsProfiledReturnTypeCallOp(newOpcode)) |
| { |
| profiledReturn = true; |
| newOpcode = Js::OpCodeUtil::ConvertProfiledReturnTypeCallOpToNonProfiled(newOpcode); |
| |
| // If we're profiling in the jitted code we want to propagate the profileId |
| // If we're using profile data instead of collecting it, we don't want to |
| // use the profile data from a return type call (this was previously done in IRBuilder::BuildProfiledCallI) |
| if (!jitProfiling) |
| { |
| profileId = Js::Constants::NoProfileId; |
| } |
| } |
| |
| if (profileId != Js::Constants::NoProfileId) |
| { |
| if (jitProfiling) |
| { |
| // In SimpleJit we want this call to be a profiled call after being jitted |
| instr = IR::JitProfilingInstr::New(newOpcode, dstOpnd, src1Opnd, m_func); |
| instr->AsJitProfilingInstr()->profileId = profileId; |
| instr->AsJitProfilingInstr()->isProfiledReturnCall = profiledReturn; |
| instr->AsJitProfilingInstr()->inlineCacheIndex = inlineCacheIndex; |
| } |
| else |
| { |
| instr = IR::ProfiledInstr::New(newOpcode, dstOpnd, src1Opnd, m_func); |
| instr->AsProfiledInstr()->u.profileId = profileId; |
| } |
| } |
| else |
| { |
| instr = IR::Instr::New(newOpcode, m_func); |
| instr->SetSrc1(src1Opnd); |
| if (dstOpnd != nullptr) |
| { |
| instr->SetDst(dstOpnd); |
| } |
| } |
| |
| if (dstOpnd && newOpcode == Js::OpCode::NewScObject) |
| { |
| dstOpnd->SetValueType(ValueType::GetObject(ObjectType::UninitializedObject)); |
| } |
| |
| if (symDst && symDst->m_isSingleDef) |
| { |
| switch (instr->m_opcode) |
| { |
| case Js::OpCode::NewScObject: |
| case Js::OpCode::NewScObjectSpread: |
| case Js::OpCode::NewScObjectLiteral: |
| case Js::OpCode::NewScObjArray: |
| case Js::OpCode::NewScObjArraySpread: |
| symDst->m_isSafeThis = true; |
| symDst->m_isNotNumber = true; |
| break; |
| } |
| } |
| |
| this->AddInstr(instr, offset); |
| |
| this->BuildCallCommon(instr, symDst, ArgCount, flags); |
| |
| return instr; |
| } |
| |
| void |
| IRBuilder::BuildCallCommon(IR::Instr * instr, StackSym * symDst, Js::ArgSlot argCount, Js::CallFlags flags) |
| { |
| Js::OpCode newOpcode = instr->m_opcode; |
| |
| IR::Instr * argInstr = nullptr; |
| IR::Instr * prevInstr = instr; |
| #if DBG |
| int count = 0; |
| #endif |
| |
| // Link all the args of this call by creating a def/use chain through the src2. |
| AssertOrFailFast(!m_argStack->Empty()); |
| for (argInstr = m_argStack->Pop(); |
| argInstr && !m_argStack->Empty() && argInstr->m_opcode != Js::OpCode::StartCall; |
| argInstr = m_argStack->Pop()) |
| { |
| prevInstr->SetSrc2(argInstr->GetDst()); |
| prevInstr = argInstr; |
| #if DBG |
| count++; |
| #endif |
| } |
| AssertOrFailFast(argInstr == nullptr || argInstr->m_opcode == Js::OpCode::StartCall); |
| |
| if (m_argStack->Empty()) |
| { |
| this->callTreeHasSomeProfileInfo = false; |
| } |
| |
| if (newOpcode == Js::OpCode::NewScObject || newOpcode == Js::OpCode::NewScObjArray |
| || newOpcode == Js::OpCode::NewScObjectSpread || newOpcode == Js::OpCode::NewScObjArraySpread) |
| { |
| #if DBG |
| count++; |
| #endif |
| m_argsOnStack++; |
| } |
| |
| argCount = Js::CallInfo::GetArgCountWithExtraArgs(flags, argCount); |
| |
| if (argInstr) |
| { |
| prevInstr->SetSrc2(argInstr->GetDst()); |
| AssertMsg(instr->m_prev->m_opcode == Js::OpCode::LdSpreadIndices |
| // All non-spread calls need StartCall to have the same number of args |
| || (argInstr->GetSrc1()->IsIntConstOpnd() |
| && argInstr->GetSrc1()->AsIntConstOpnd()->GetValue() == count |
| && count == argCount), "StartCall has wrong number of arguments..."); |
| } |
| else |
| { |
| AssertMsg(false, "Expect StartCall on other opcodes..."); |
| } |
| |
| // Update Func if this is the highest amount of stack we've used so far |
| // to push args. |
| #if DBG |
| m_callsOnStack--; |
| #endif |
| if (m_func->m_argSlotsForFunctionsCalled < m_argsOnStack) |
| m_func->m_argSlotsForFunctionsCalled = m_argsOnStack; |
| #if DBG |
| if (m_callsOnStack == 0) |
| Assert(m_argsOnStack == argCount); |
| #endif |
| m_argsOnStack -= argCount; |
| |
| if (m_func->IsJitInDebugMode()) |
| { |
| // Insert bailout after return from a call, script or library function call. |
| this->InsertBailOutForDebugger( |
| m_jnReader.GetCurrentOffset(), // bailout will resume at the offset of next instr. |
| c_debuggerBailOutKindForCall); |
| } |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildClass |
| /// |
| /// Build IR instr for an InitClass instruction. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildClass(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_Class<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->Constructor); |
| this->DoClosureRegCheck(layout->Extends); |
| } |
| |
| BuildClass(newOpcode, offset, layout->Constructor, layout->Extends); |
| } |
| |
| void |
| IRBuilder::BuildClass(Js::OpCode newOpcode, uint32 offset, Js::RegSlot constructor, Js::RegSlot extends) |
| { |
| Assert(newOpcode == Js::OpCode::InitClass); |
| |
| IR::Instr * insn = IR::Instr::New(newOpcode, m_func); |
| insn->SetSrc1(this->BuildSrcOpnd(constructor)); |
| |
| if (extends != Js::Constants::NoRegister) |
| { |
| insn->SetSrc2(this->BuildSrcOpnd(extends)); |
| } |
| |
| this->AddInstr(insn, offset); |
| } |
| |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildBrReg1 |
| /// |
| /// Build IR instr for a BrReg1 instruction. |
| /// This is a conditional branch with a single source operand (e.g., "if (x)" ...) |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildBrReg1(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_BrReg1<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->R1); |
| } |
| |
| BuildBrReg1(newOpcode, offset, m_jnReader.GetCurrentOffset() + layout->RelativeJumpOffset, layout->R1); |
| } |
| |
| void |
| IRBuilder::BuildBrReg1(Js::OpCode newOpcode, uint32 offset, uint targetOffset, Js::RegSlot srcRegSlot) |
| { |
| IR::BranchInstr * branchInstr; |
| IR::RegOpnd * srcOpnd; |
| srcOpnd = this->BuildSrcOpnd(srcRegSlot); |
| |
| if (newOpcode == Js::OpCode::BrNotUndecl_A) { |
| IR::AddrOpnd *srcOpnd2 = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetUndeclBlockVarAddr(), |
| IR::AddrOpndKindDynamicVar, this->m_func); |
| branchInstr = IR::BranchInstr::New(Js::OpCode::BrNotAddr_A, nullptr, srcOpnd, srcOpnd2, m_func); |
| } else { |
| branchInstr = IR::BranchInstr::New(newOpcode, nullptr, srcOpnd, m_func); |
| } |
| |
| this->AddBranchInstr(branchInstr, offset, targetOffset); |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildBrReg2 |
| /// |
| /// Build IR instr for a BrReg2 instruction. |
| /// This is a conditional branch with a 2 source operands (e.g., "if (x == y)" ...) |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildBrReg2(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_BrReg2<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->R1); |
| this->DoClosureRegCheck(layout->R2); |
| } |
| |
| BuildBrReg2(newOpcode, offset, m_jnReader.GetCurrentOffset() + layout->RelativeJumpOffset, layout->R1, layout->R2); |
| } |
| |
| template <typename SizePolicy> |
| void |
| IRBuilder::BuildBrReg1Unsigned1(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(newOpcode == Js::OpCode::BrOnEmpty |
| /* || newOpcode == Js::OpCode::BrOnNotEmpty */ // BrOnNotEmpty not generate by the byte code |
| ); |
| |
| Assert(!OpCodeAttr::IsProfiledOp(newOpcode)); |
| Assert(OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| auto layout = m_jnReader.GetLayout<Js::OpLayoutT_BrReg1Unsigned1<SizePolicy>>(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(layout->R1); |
| } |
| |
| BuildBrBReturn(newOpcode, offset, layout->R1, layout->C2, m_jnReader.GetCurrentOffset() + layout->RelativeJumpOffset); |
| } |
| |
| void |
| IRBuilder::BuildBrBReturn(Js::OpCode newOpcode, uint32 offset, Js::RegSlot DestRegSlot, uint32 forInLoopLevel, uint32 targetOffset) |
| { |
| IR::Opnd *srcOpnd = this->BuildForInEnumeratorOpnd(forInLoopLevel); |
| IR::RegOpnd * destOpnd = this->BuildDstOpnd(DestRegSlot); |
| IR::BranchInstr * branchInstr = IR::BranchInstr::New(newOpcode, destOpnd, nullptr, srcOpnd, m_func); |
| this->AddBranchInstr(branchInstr, offset, targetOffset); |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::BrOnEmpty: |
| destOpnd->SetValueType(ValueType::String); |
| break; |
| default: |
| Assert(false); |
| break; |
| }; |
| } |
| |
| void |
| IRBuilder::BuildBrReg2(Js::OpCode newOpcode, uint32 offset, uint targetOffset, Js::RegSlot R1, Js::RegSlot R2) |
| { |
| IR::BranchInstr * branchInstr; |
| |
| if (newOpcode == Js::OpCode::BrOnEmpty |
| /* || newOpcode == Js::OpCode::BrOnNotEmpty */ // BrOnNotEmpty not generate by the byte code |
| ) |
| { |
| BuildBrBReturn(newOpcode, offset, R1, R2, targetOffset); |
| return; |
| } |
| |
| IR::RegOpnd * src1Opnd; |
| IR::RegOpnd * src2Opnd; |
| |
| src1Opnd = this->BuildSrcOpnd(R1); |
| src2Opnd = this->BuildSrcOpnd(R2); |
| |
| if (newOpcode == Js::OpCode::Case) |
| { |
| // generating branches for Cases is entirely handled |
| // by the SwitchIRBuilder |
| |
| m_switchBuilder.OnCase(src1Opnd, src2Opnd, offset, targetOffset); |
| |
| #ifdef BYTECODE_BRANCH_ISLAND |
| // Make sure that if there are branch island between the cases, we consume it first |
| EnsureConsumeBranchIsland(); |
| #endif |
| |
| // some instructions can't be optimized past, such as LdFld for objects. In these cases we have |
| // to inform the SwitchBuilder to flush any optimized cases that it has stored up to this point |
| // peeks the next opcode - to check if it is not a case statement (for example: the next instr can be a LdFld for objects) |
| Js::OpCode peekOpcode = m_jnReader.PeekOp(); |
| if (peekOpcode != Js::OpCode::Case && peekOpcode != Js::OpCode::EndSwitch) |
| { |
| m_switchBuilder.FlushCases(m_jnReader.GetCurrentOffset()); |
| } |
| } |
| else |
| { |
| branchInstr = IR::BranchInstr::New(newOpcode, nullptr, src1Opnd, src2Opnd, m_func); |
| this->AddBranchInstr(branchInstr, offset, targetOffset); |
| } |
| } |
| |
| void |
| IRBuilder::BuildEmpty(Js::OpCode newOpcode, uint32 offset) |
| { |
| IR::Instr *instr; |
| |
| m_jnReader.Empty(); |
| |
| instr = IR::Instr::New(newOpcode, m_func); |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::CommitScope: |
| { |
| IR::RegOpnd * src1Opnd; |
| |
| src1Opnd = this->BuildSrcOpnd(m_func->GetJITFunctionBody()->GetLocalClosureReg()); |
| |
| IR::LabelInstr *labelNull = IR::LabelInstr::New(Js::OpCode::Label, this->m_func); |
| |
| IR::RegOpnd * funcExprOpnd = IR::RegOpnd::New(TyVar, m_func); |
| instr = IR::Instr::New(Js::OpCode::LdFuncExpr, funcExprOpnd, m_func); |
| this->AddInstr(instr, offset); |
| |
| IR::BranchInstr *branchInstr = IR::BranchInstr::New(Js::OpCode::BrFncCachedScopeNeq, labelNull, |
| funcExprOpnd, src1Opnd, this->m_func); |
| this->AddInstr(branchInstr, offset); |
| |
| instr = IR::Instr::New(newOpcode, this->m_func); |
| instr->SetSrc1(src1Opnd); |
| |
| this->AddInstr(instr, offset); |
| |
| this->AddInstr(labelNull, Js::Constants::NoByteCodeOffset); |
| return; |
| } |
| case Js::OpCode::Ret: |
| { |
| IR::RegOpnd *regOpnd = BuildDstOpnd(0); |
| instr->SetSrc1(regOpnd); |
| this->AddInstr(instr, offset); |
| break; |
| } |
| |
| case Js::OpCode::Leave: |
| { |
| IR::BranchInstr * branchInstr; |
| IR::LabelInstr * labelInstr; |
| |
| if (this->handlerOffsetStack && !this->handlerOffsetStack->Empty() && this->handlerOffsetStack->Top().Second()) |
| { |
| // If the try region has a break block, we don't want the Flowgraph to move all of that code out of the loop |
| // because an exception will bring the control back into the loop. The branch out of the loop (which is the |
| // reason for the code to be a break block) can still be moved out though. |
| // |
| // "BrOnException $catch" is inserted before Leave's in the try region to instrument flow from the try region |
| // to the catch region (which is in the loop). |
| IR::BranchInstr * brOnException = IR::BranchInstr::New(Js::OpCode::BrOnException, nullptr, this->m_func); |
| this->AddBranchInstr(brOnException, offset, this->handlerOffsetStack->Top().First()); |
| } |
| |
| labelInstr = IR::LabelInstr::New(Js::OpCode::Label, this->m_func); |
| branchInstr = IR::BranchInstr::New(newOpcode, labelInstr, this->m_func); |
| this->AddInstr(branchInstr, offset); |
| this->AddInstr(labelInstr, Js::Constants::NoByteCodeOffset); |
| break; |
| } |
| |
| case Js::OpCode::LeaveNull: |
| finallyBlockLevel--; |
| this->AddInstr(instr, offset); |
| break; |
| |
| case Js::OpCode::Finally: |
| if (this->handlerOffsetStack) |
| { |
| AssertOrFailFast(!this->handlerOffsetStack->Empty()); |
| AssertOrFailFast(this->handlerOffsetStack->Top().Second() == false); |
| this->handlerOffsetStack->Pop(); |
| } |
| finallyBlockLevel++; |
| this->AddInstr(IR::Instr::New(Js::OpCode::Finally, this->m_func), offset); |
| break; |
| |
| case Js::OpCode::Break: |
| if (m_func->IsJitInDebugMode()) |
| { |
| // Add explicit bailout. |
| this->InsertBailOutForDebugger(offset, IR::BailOutExplicit); |
| } |
| else |
| { |
| // Default behavior, let's keep it for now, removed in lowerer. |
| this->AddInstr(instr, offset); |
| } |
| break; |
| |
| case Js::OpCode::BeginBodyScope: |
| { |
| // This marks the end of a param socpe which is not merged with body scope. |
| // So we have to first cache the closure so that we can use it to copy the initial values for |
| // body syms from corresponding param syms (LdParamSlot). Body should get its own scope slot. |
| Assert(!this->IsParamScopeDone()); |
| this->SetParamScopeDone(); |
| |
| IR::Opnd * localClosureOpnd; |
| if (this->m_func->GetLocalClosureSym() != nullptr) |
| { |
| localClosureOpnd = IR::RegOpnd::New(this->m_func->GetLocalClosureSym(), TyVar, this->m_func); |
| } |
| else |
| { |
| AssertOrFailFast(this->m_func->GetJITFunctionBody()->GetScopeSlotArraySize() == 0 && !this->m_func->GetJITFunctionBody()->HasScopeObject()); |
| localClosureOpnd = IR::IntConstOpnd::New(0, TyVar, this->m_func); |
| } |
| |
| this->AddInstr( |
| IR::Instr::New( |
| Js::OpCode::Ld_A, |
| this->BuildDstOpnd(this->m_func->GetJITFunctionBody()->GetParamClosureReg()), |
| localClosureOpnd, |
| this->m_func), |
| offset); |
| |
| // Create a new local closure for the body when either body scope has scope slots allocated or |
| // eval is present which can leak declarations. |
| if (this->m_func->GetJITFunctionBody()->GetScopeSlotArraySize() > 0 || this->m_func->GetJITFunctionBody()->HasScopeObject()) |
| { |
| if (this->m_func->GetJITFunctionBody()->HasScopeObject()) |
| { |
| if (this->m_func->GetJITFunctionBody()->HasCachedScopePropIds()) |
| { |
| this->BuildInitCachedScope(0, Js::Constants::NoByteCodeOffset); |
| } |
| else |
| { |
| this->AddInstr( |
| IR::Instr::New( |
| Js::OpCode::NewScopeObject, |
| this->BuildDstOpnd(this->m_func->GetJITFunctionBody()->GetLocalClosureReg()), |
| m_func), |
| Js::Constants::NoByteCodeOffset); |
| } |
| } |
| else |
| { |
| this->AddInstr( |
| IR::Instr::New( |
| Js::OpCode::NewScopeSlots, |
| this->BuildDstOpnd(this->m_func->GetJITFunctionBody()->GetLocalClosureReg()), |
| IR::IntConstOpnd::New(this->m_func->GetJITFunctionBody()->GetScopeSlotArraySize() + Js::ScopeSlots::FirstSlotIndex, TyUint32, this->m_func), |
| m_func), |
| Js::Constants::NoByteCodeOffset); |
| } |
| |
| IR::Instr* lfd = IR::Instr::New( |
| Js::OpCode::LdFrameDisplay, |
| this->BuildDstOpnd(this->m_func->GetJITFunctionBody()->GetLocalFrameDisplayReg()), |
| this->BuildDstOpnd(this->m_func->GetJITFunctionBody()->GetLocalClosureReg()), |
| this->BuildDstOpnd(this->m_func->GetJITFunctionBody()->GetLocalFrameDisplayReg()), |
| this->m_func); |
| this->AddInstr( |
| lfd, |
| Js::Constants::NoByteCodeOffset); |
| lfd->isNonFastPathFrameDisplay = true; |
| } |
| break; |
| } |
| |
| default: |
| this->AddInstr(instr, offset); |
| break; |
| } |
| } |
| |
| #ifdef BYTECODE_BRANCH_ISLAND |
| void |
| IRBuilder::EnsureConsumeBranchIsland() |
| { |
| if (m_jnReader.PeekOp() == Js::OpCode::Br) |
| { |
| // Save the old offset |
| uint offset = m_jnReader.GetCurrentOffset(); |
| |
| // Read the potentially a branch around |
| Js::LayoutSize layoutSize; |
| Js::OpCode opcode = m_jnReader.ReadOp(layoutSize); |
| Assert(opcode == Js::OpCode::Br); |
| Assert(layoutSize == Js::SmallLayout); |
| const unaligned Js::OpLayoutBr * playout = m_jnReader.Br(); |
| unsigned int targetOffset = m_jnReader.GetCurrentOffset() + playout->RelativeJumpOffset; |
| |
| uint branchIslandOffset = m_jnReader.GetCurrentOffset(); |
| if (branchIslandOffset == targetOffset) |
| { |
| // branch to next, there is no long branch |
| m_jnReader.SetCurrentOffset(offset); |
| return; |
| } |
| |
| // Ignore all the BrLong |
| while (m_jnReader.PeekOp() == Js::OpCode::BrLong) |
| { |
| opcode = m_jnReader.ReadOp(layoutSize); |
| Assert(opcode == Js::OpCode::BrLong); |
| Assert(layoutSize == Js::SmallLayout); |
| m_jnReader.BrLong(); |
| } |
| |
| // Confirm that is a branch around |
| if ((uint)m_jnReader.GetCurrentOffset() == targetOffset) |
| { |
| // Really consume the branch island |
| m_jnReader.SetCurrentOffset(branchIslandOffset); |
| ConsumeBranchIsland(); |
| |
| // Mark the virtual branch around as a redirect long branch as well |
| // so that if it is the target of another branch, it will just keep pass |
| // the branch island |
| Assert(longBranchMap); |
| Assert(offset < m_offsetToInstructionCount); |
| Assert(m_offsetToInstruction[offset] == nullptr); |
| m_offsetToInstruction[offset] = VirtualLongBranchInstr; |
| longBranchMap->Add(offset, targetOffset); |
| } |
| else |
| { |
| // Reset the offset |
| m_jnReader.SetCurrentOffset(offset); |
| } |
| } |
| } |
| |
| IR::Instr * const IRBuilder::VirtualLongBranchInstr = (IR::Instr *)-1; |
| |
| void |
| IRBuilder::ConsumeBranchIsland() |
| { |
| do |
| { |
| uint32 offset = m_jnReader.GetCurrentOffset(); |
| Js::LayoutSize layoutSize; |
| Js::OpCode opcode = m_jnReader.ReadOp(layoutSize); |
| Assert(opcode == Js::OpCode::BrLong); |
| Assert(layoutSize == Js::SmallLayout); |
| BuildBrLong(Js::OpCode::BrLong, offset); |
| } |
| while (m_jnReader.PeekOp() == Js::OpCode::BrLong); |
| } |
| |
| void |
| IRBuilder::BuildBrLong(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(newOpcode == Js::OpCode::BrLong); |
| Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| Assert(offset != Js::Constants::NoByteCodeOffset); |
| |
| const unaligned Js::OpLayoutBrLong *branchInsn = m_jnReader.BrLong(); |
| unsigned int targetOffset = m_jnReader.GetCurrentOffset() + branchInsn->RelativeJumpOffset; |
| |
| Assert(offset < m_offsetToInstructionCount); |
| Assert(m_offsetToInstruction[offset] == nullptr); |
| |
| // BrLong are also just the target of another branch, just set a virtual long branch instr |
| // and remap the original branch to the actual destination in ResolveVirtualLongBranch |
| m_offsetToInstruction[offset] = VirtualLongBranchInstr; |
| longBranchMap->Add(offset, targetOffset); |
| } |
| |
| |
| uint |
| IRBuilder::ResolveVirtualLongBranch(IR::BranchInstr * branchInstr, uint offset) |
| { |
| Assert(longBranchMap); |
| uint32 targetOffset; |
| if (!longBranchMap->TryGetValue(offset, &targetOffset)) |
| { |
| // If we see a VirtualLongBranchInstr, we must have a mapping to the real target offset |
| Assert(false); |
| Fatal(); |
| } |
| |
| // If this is a jump out of the loop body we need to load the return IP and jump to the loop exit instead |
| if (!IsLoopBodyOuterOffset(targetOffset)) |
| { |
| return targetOffset; |
| } |
| |
| // Multi branch shouldn't be exiting a loop |
| Assert(branchInstr->m_opcode != Js::OpCode::MultiBr); |
| |
| // Don't load the return IP if it is already loaded (for the case of early exit) |
| if (!IsLoopBodyReturnIPInstr(branchInstr->m_prev)) |
| { |
| IR::Instr * returnIPInstr = CreateLoopBodyReturnIPInstr(targetOffset, branchInstr->GetByteCodeOffset()); |
| |
| // Any jump to this branch to jump to the return IP load instr first |
| uint32 branchInstrByteCodeOffset = branchInstr->GetByteCodeOffset(); |
| Assert(this->m_offsetToInstruction[branchInstrByteCodeOffset] == branchInstr || |
| (this->m_offsetToInstruction[branchInstrByteCodeOffset]->HasBailOutInfo() && |
| this->m_offsetToInstruction[branchInstrByteCodeOffset]->GetBailOutKind() == IR::BailOutInjected)); |
| |
| InsertInstr(returnIPInstr, branchInstr); |
| } |
| return GetLoopBodyExitInstrOffset(); |
| } |
| #endif |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildBr |
| /// |
| /// Build IR instr for a Br (unconditional branch) instruction. |
| /// or TryCatch/TryFinally |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| void |
| IRBuilder::BuildBr(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| IR::BranchInstr * branchInstr; |
| const unaligned Js::OpLayoutBr *branchInsn = m_jnReader.Br(); |
| unsigned int targetOffset = m_jnReader.GetCurrentOffset() + branchInsn->RelativeJumpOffset; |
| #ifdef BYTECODE_BRANCH_ISLAND |
| bool isLongBranchIsland = (m_jnReader.PeekOp() == Js::OpCode::BrLong); |
| if (isLongBranchIsland) |
| { |
| ConsumeBranchIsland(); |
| } |
| #endif |
| |
| if(newOpcode == Js::OpCode::EndSwitch) |
| { |
| m_switchBuilder.EndSwitch(offset, targetOffset); |
| return; |
| } |
| #ifdef PERF_HINT |
| else if (PHASE_TRACE1(Js::PerfHintPhase) && (newOpcode == Js::OpCode::TryCatch || newOpcode == Js::OpCode::TryFinally) ) |
| { |
| WritePerfHint(PerfHints::HasTryBlock, this->m_func, offset); |
| } |
| #endif |
| |
| #ifdef BYTECODE_BRANCH_ISLAND |
| if (isLongBranchIsland && (targetOffset == (uint)m_jnReader.GetCurrentOffset())) |
| { |
| // Branch to next (probably after consume branch island), try to not emit the branch |
| |
| // Mark the jump around instruction as a virtual long branch as well so we can just |
| // fall through instead of branch to exit |
| Assert(offset < m_offsetToInstructionCount); |
| if (m_offsetToInstruction[offset] == nullptr) |
| { |
| m_offsetToInstruction[offset] = VirtualLongBranchInstr; |
| longBranchMap->Add(offset, targetOffset); |
| return; |
| } |
| |
| // We may have already create an instruction on this offset as a statement boundary |
| // or in the bailout at every byte code case. |
| |
| // The statement boundary case only happens if we have emitted the long branch island |
| // after an existing no fall through instruction, but that instruction also happen to be |
| // branch to next. We will just generate an actual branch to next instruction. |
| |
| Assert(m_offsetToInstruction[offset]->m_opcode == Js::OpCode::StatementBoundary |
| || (Js::Configuration::Global.flags.IsEnabled(Js::BailOutAtEveryByteCodeFlag) |
| && m_offsetToInstruction[offset]->m_opcode == Js::OpCode::BailOnEqual)); |
| } |
| #endif |
| |
| if ((newOpcode == Js::OpCode::TryCatch) && this->handlerOffsetStack) |
| { |
| this->handlerOffsetStack->Push(Pair<uint, bool>(targetOffset, true)); |
| } |
| else if ((newOpcode == Js::OpCode::TryFinally) && this->handlerOffsetStack) |
| { |
| this->handlerOffsetStack->Push(Pair<uint, bool>(targetOffset, false)); |
| } |
| branchInstr = IR::BranchInstr::New(newOpcode, nullptr, m_func); |
| this->AddBranchInstr(branchInstr, offset, targetOffset); |
| } |
| |
| void |
| IRBuilder::BuildBrS(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| IR::BranchInstr * branchInstr; |
| const unaligned Js::OpLayoutBrS *branchInsn = m_jnReader.BrS(); |
| |
| unsigned int targetOffset = m_jnReader.GetCurrentOffset() + branchInsn->RelativeJumpOffset; |
| |
| branchInstr = IR::BranchInstr::New(newOpcode, nullptr, |
| IR::IntConstOpnd::New(branchInsn->val, |
| TyInt32, m_func),m_func); |
| this->AddBranchInstr(branchInstr, offset, targetOffset); |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildBrProperty |
| /// |
| /// Build IR instr for a BrProperty instruction. |
| /// This is a conditional branch that tests whether the given property |
| /// is present on the given instance. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| void |
| IRBuilder::BuildBrProperty(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| const unaligned Js::OpLayoutBrProperty *branchInsn = m_jnReader.BrProperty(); |
| |
| if (!PHASE_OFF(Js::ClosureRegCheckPhase, m_func)) |
| { |
| this->DoClosureRegCheck(branchInsn->Instance); |
| } |
| |
| IR::BranchInstr * branchInstr; |
| Js::PropertyId propertyId = |
| m_func->GetJITFunctionBody()->GetReferencedPropertyId(branchInsn->PropertyIdIndex); |
| unsigned int targetOffset = m_jnReader.GetCurrentOffset() + branchInsn->RelativeJumpOffset; |
| IR::SymOpnd * fieldSymOpnd = this->BuildFieldOpnd(newOpcode, branchInsn->Instance, propertyId, branchInsn->PropertyIdIndex, PropertyKindData); |
| |
| branchInstr = IR::BranchInstr::New(newOpcode, nullptr, fieldSymOpnd, m_func); |
| this->AddBranchInstr(branchInstr, offset, targetOffset); |
| } |
| |
| void |
| IRBuilder::BuildBrLocalProperty(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| switch (newOpcode) |
| { |
| case Js::OpCode::BrOnNoLocalProperty: |
| newOpcode = Js::OpCode::BrOnNoProperty; |
| break; |
| |
| default: |
| Assert(0); |
| break; |
| } |
| |
| const unaligned Js::OpLayoutBrLocalProperty *branchInsn = m_jnReader.BrLocalProperty(); |
| |
| if (m_func->GetLocalClosureSym()->HasByteCodeRegSlot()) |
| { |
| IR::ByteCodeUsesInstr * byteCodeUse = IR::ByteCodeUsesInstr::New(m_func, offset); |
| byteCodeUse->SetNonOpndSymbol(m_func->GetLocalClosureSym()->m_id); |
| this->AddInstr(byteCodeUse, offset); |
| } |
| |
| IR::BranchInstr * branchInstr; |
| Js::PropertyId propertyId = |
| m_func->GetJITFunctionBody()->GetReferencedPropertyId(branchInsn->PropertyIdIndex); |
| unsigned int targetOffset = m_jnReader.GetCurrentOffset() + branchInsn->RelativeJumpOffset; |
| IR::SymOpnd * fieldSymOpnd = this->BuildFieldOpnd(newOpcode, m_func->GetJITFunctionBody()->GetLocalClosureReg(), propertyId, branchInsn->PropertyIdIndex, PropertyKindData); |
| |
| branchInstr = IR::BranchInstr::New(newOpcode, nullptr, fieldSymOpnd, m_func); |
| this->AddBranchInstr(branchInstr, offset, targetOffset); |
| } |
| |
| void |
| IRBuilder::BuildBrEnvProperty(Js::OpCode newOpcode, uint32 offset) |
| { |
| Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode)); |
| |
| const unaligned Js::OpLayoutBrEnvProperty *branchInsn = m_jnReader.BrEnvProperty(); |
| IR::Instr *instr; |
| IR::BranchInstr * branchInstr; |
| IR::RegOpnd *regOpnd; |
| IR::SymOpnd *fieldOpnd; |
| PropertySym *fieldSym; |
| |
| fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, this->GetEnvReg(), branchInsn->SlotIndex, (Js::PropertyIdIndexType)-1, PropertyKindSlotArray); |
| regOpnd = IR::RegOpnd::New(TyVar, m_func); |
| instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func); |
| this->AddInstr(instr, offset); |
| |
| Js::PropertyId propertyId = |
| m_func->GetJITFunctionBody()->GetReferencedPropertyId(branchInsn->PropertyIdIndex); |
| unsigned int targetOffset = m_jnReader.GetCurrentOffset() + branchInsn->RelativeJumpOffset;\ |
| fieldSym = PropertySym::New(regOpnd->m_sym, propertyId, branchInsn->PropertyIdIndex, (uint)-1, PropertyKindData, m_func); |
| fieldOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func); |
| |
| branchInstr = IR::BranchInstr::New(Js::OpCode::BrOnNoProperty, nullptr, fieldOpnd, m_func); |
| this->AddBranchInstr(branchInstr, offset, targetOffset); |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::AddBranchInstr |
| /// |
| /// Create a branch/offset pair which will be fixed up at the end of the |
| /// IRBuilder phase and add the instruction |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| BranchReloc * |
| IRBuilder::AddBranchInstr(IR::BranchInstr * branchInstr, uint32 offset, uint32 targetOffset) |
| { |
| AssertOrFailFast(targetOffset <= m_func->GetJITFunctionBody()->GetByteCodeLength()); |
| // |
| // Loop jitting would be done only till the LoopEnd |
| // Any branches beyond that offset are for the return stmt |
| // |
| if (IsLoopBodyOuterOffset(targetOffset)) |
| { |
| // if we have loaded the loop IP sym from the ProfiledLoopEnd then don't add it here |
| if (!IsLoopBodyReturnIPInstr(m_lastInstr)) |
| { |
| this->InsertLoopBodyReturnIPInstr(targetOffset, offset); |
| } |
| |
| // Jump the restore StSlot and Ret instruction |
| targetOffset = GetLoopBodyExitInstrOffset(); |
| } |
| |
| BranchReloc * reloc = nullptr; |
| reloc = this->CreateRelocRecord(branchInstr, offset, targetOffset); |
| |
| this->AddInstr(branchInstr, offset); |
| return reloc; |
| } |
| |
| BranchReloc * |
| IRBuilder::CreateRelocRecord(IR::BranchInstr * branchInstr, uint32 offset, uint32 targetOffset) |
| { |
| BranchReloc * reloc = JitAnew(this->m_tempAlloc, BranchReloc, branchInstr, offset, targetOffset); |
| this->branchRelocList->Prepend(reloc); |
| return reloc; |
| } |
| ///---------------------------------------------------------------------------- |
| /// |
| /// IRBuilder::BuildRegexFromPattern |
| /// |
| /// Build a new RegEx instruction. Simply construct a var to hold the regex |
| /// and load it as an immediate into a register. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| void |
| IRBuilder::BuildRegexFromPattern(Js::RegSlot dstRegSlot, uint32 patternIndex, uint32 offset) |
| { |
| IR::Instr * instr; |
| |
| IR::RegOpnd* dstOpnd = this->BuildDstOpnd(dstRegSlot); |
| dstOpnd->SetValueType(ValueType::GetObject(ObjectType::RegExp)); |
| |
| IR::Opnd * regexOpnd = IR::AddrOpnd::New(m_func->GetJITFunctionBody()->GetLiteralRegexAddr(patternIndex), IR::AddrOpndKindDynamicMisc, this->m_func); |
| |
| instr = IR::Instr::New(Js::OpCode::NewRegEx, dstOpnd, regexOpnd, this->m_func); |
| this->AddInstr(instr, offset); |
| } |
| |
| |
| bool |
| IRBuilder::IsFloatFunctionCallsite(Js::BuiltinFunction index, size_t argc) |
| { |
| return Js::JavascriptLibrary::IsFloatFunctionCallsite(index, argc); |
| } |
| |
| void |
| IRBuilder::CheckBuiltIn(PropertySym * propertySym, Js::BuiltinFunction *puBuiltInIndex) |
| { |
| Js::BuiltinFunction index = Js::BuiltinFunction::None; |
| |
| // Check whether the propertySym appears to be a built-in. |
| if (propertySym->m_fieldKind != PropertyKindData) |
| { |
| return; |
| } |
| |
| index = Js::JavascriptLibrary::GetBuiltinFunctionForPropId(propertySym->m_propertyId); |
| if (index == Js::BuiltinFunction::None) |
| { |
| return; |
| } |
| |
| // If the target is one of the Math built-ins, see whether the stack sym is the |
| // global "Math". |
| if (Js::JavascriptLibrary::IsFltFunc(index)) |
| { |
| if (!propertySym->m_stackSym->m_isSingleDef) |
| { |
| return; |
| } |
| |
| IR::Instr *instr = propertySym->m_stackSym->m_instrDef; |
| AssertMsg(instr != nullptr, "Single-def stack sym w/o def instr?"); |
| |
| if (instr->m_opcode != Js::OpCode::LdRootFld && instr->m_opcode != Js::OpCode::LdRootFldForTypeOf) |
| { |
| return; |
| } |
| |
| IR::Opnd * opnd = instr->GetSrc1(); |
| AssertMsg(opnd != nullptr && opnd->IsSymOpnd() && opnd->AsSymOpnd()->m_sym->IsPropertySym(), |
| "LdRootFld w/o propertySym src?"); |
| |
| if (opnd->AsSymOpnd()->m_sym->AsPropertySym()->m_propertyId != Js::PropertyIds::Math) |
| { |
| return; |
| } |
| } |
| |
| *puBuiltInIndex = index; |
| } |
| |
| StackSym * |
| IRBuilder::EnsureStackFuncPtrSym() |
| { |
| StackSym * sym = this->m_stackFuncPtrSym; |
| if (sym) |
| { |
| return sym; |
| } |
| |
| if (m_func->IsLoopBody() && m_func->DoStackNestedFunc()) |
| { |
| Assert(m_func->IsTopFunc()); |
| sym = StackSym::New(TyVar, m_func); |
| this->m_stackFuncPtrSym = sym; |
| } |
| |
| return sym; |
| } |
| |
| void |
| IRBuilder::GenerateLoopBodySlotAccesses(uint offset) |
| { |
| // |
| // The interpreter instance is passed as 0th argument to the JITted loop body function. |
| // Always load the argument, then use it to generate any necessary store-slots. |
| // |
| uint16 argument = 0; |
| |
| 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(); |
| IR::RegOpnd *loopParamOpnd = IR::RegOpnd::New(loopParamSym, TyMachPtr, m_func); |
| |
| IR::Instr *instrArgIn = IR::Instr::New(Js::OpCode::ArgIn_A, loopParamOpnd, srcOpnd, m_func); |
| m_func->m_headInstr->InsertAfter(instrArgIn); |
| |
| StackSym *stackFuncPtrSym = this->m_stackFuncPtrSym; |
| if (stackFuncPtrSym) |
| { |
| PropertySym * fieldSym = PropertySym::FindOrCreate(loopParamSym->m_id, (Js::PropertyId)(Js::InterpreterStackFrame::GetOffsetOfStackNestedFunctions() / sizeof(Js::Var)), (uint32)-1, (uint)-1, PropertyKindLocalSlots, m_func); |
| IR::SymOpnd * opndPtrRef = IR::SymOpnd::New(fieldSym, TyVar, m_func); |
| IR::Instr * instrPtrInit = IR::Instr::New(Js::OpCode::LdSlot, IR::RegOpnd::New(stackFuncPtrSym, TyVar, m_func), opndPtrRef, m_func); |
| instrArgIn->InsertAfter(instrPtrInit); |
| } |
| |
| GenerateLoopBodyStSlots(loopParamSym->m_id, offset); |
| } |
| |
| void |
| IRBuilder::GenerateLoopBodyStSlots(SymID loopParamSymId, uint offset) |
| { |
| if (this->m_stSlots->Count() == 0) |
| { |
| return; |
| } |
| |
| FOREACH_BITSET_IN_FIXEDBV(regSlot, this->m_stSlots) |
| { |
| this->GenerateLoopBodyStSlot(regSlot, offset); |
| } |
| NEXT_BITSET_IN_FIXEDBV; |
| } |
| |
| IR::Instr * |
| IRBuilder::GenerateLoopBodyStSlot(Js::RegSlot regSlot, uint offset) |
| { |
| Assert(!this->RegIsConstant((Js::RegSlot)regSlot)); |
| |
| StackSym *loopParamSym = m_func->EnsureLoopParamSym(); |
| PropertySym * fieldSym = PropertySym::FindOrCreate(loopParamSym->m_id, (Js::PropertyId)(regSlot + this->m_loopBodyLocalsStartSlot), (uint32)-1, (uint)-1, PropertyKindLocalSlots, m_func); |
| IR::SymOpnd * fieldSymOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func); |
| |
| IR::RegOpnd * regOpnd = this->BuildSrcOpnd((Js::RegSlot)regSlot); |
| #if !FLOATVAR |
| Js::OpCode opcode = Js::OpCode::StSlotBoxTemp; |
| #else |
| Js::OpCode opcode = Js::OpCode::StSlot; |
| #endif |
| IR::Instr * stSlotInstr = IR::Instr::New(opcode, fieldSymOpnd, regOpnd, m_func); |
| if (offset != Js::Constants::NoByteCodeOffset) |
| { |
| this->AddInstr(stSlotInstr, offset); |
| return nullptr; |
| } |
| else |
| { |
| return stSlotInstr; |
| } |
| } |
| |
| IR::Instr * |
| IRBuilder::CreateLoopBodyReturnIPInstr(uint targetOffset, uint offset) |
| { |
| IR::RegOpnd * retOpnd = IR::RegOpnd::New(m_loopBodyRetIPSym, TyMachReg, m_func); |
| IR::IntConstOpnd * exitOffsetOpnd = IR::IntConstOpnd::New(targetOffset, TyMachReg, m_func); |
| return IR::Instr::New(Js::OpCode::Ld_I4, retOpnd, exitOffsetOpnd, m_func); |
| } |
| |
| IR::Opnd * |
| IRBuilder::InsertLoopBodyReturnIPInstr(uint targetOffset, uint offset) |
| { |
| IR::Instr * setRetValueInstr = CreateLoopBodyReturnIPInstr(targetOffset, offset); |
| this->AddInstr(setRetValueInstr, offset); |
| return setRetValueInstr->GetDst(); |
| } |
| |
| void |
| IRBuilder::InsertDoneLoopBodyLoopCounter(uint32 lastOffset) |
| { |
| if (m_loopCounterSym == nullptr) |
| { |
| return; |
| } |
| |
| IR::Instr * loopCounterStoreInstr = IR::Instr::New(Js::OpCode::StLoopBodyCount, m_func); |
| IR::RegOpnd *countRegOpnd = IR::RegOpnd::New(m_loopCounterSym, TyInt32, this->m_func); |
| countRegOpnd->SetIsJITOptimizedReg(true); |
| |
| loopCounterStoreInstr->SetSrc1(countRegOpnd); |
| this->AddInstr(loopCounterStoreInstr, lastOffset + 1); |
| |
| return; |
| } |
| |
| void |
| IRBuilder::InsertIncrLoopBodyLoopCounter(IR::LabelInstr *loopTopLabelInstr) |
| { |
| Assert(this->IsLoopBody()); |
| |
| IR::RegOpnd *loopCounterOpnd = IR::RegOpnd::New(m_loopCounterSym, TyInt32, this->m_func); |
| IR::Instr * incr = IR::Instr::New(Js::OpCode::IncrLoopBodyCount, loopCounterOpnd, loopCounterOpnd, this->m_func); |
| loopCounterOpnd->SetIsJITOptimizedReg(true); |
| |
| IR::Instr* nextRealInstr = loopTopLabelInstr->GetNextRealInstr(); |
| InsertInstr(incr, nextRealInstr); |
| } |
| |
| void |
| IRBuilder::InsertInitLoopBodyLoopCounter(uint loopNum) |
| { |
| Assert(this->IsLoopBody()); |
| |
| intptr_t loopHeader = m_func->GetJITFunctionBody()->GetLoopHeaderAddr(loopNum); |
| Assert(m_func->GetWorkItem()->GetLoopHeaderAddr() == loopHeader); //Init only once |
| |
| m_loopCounterSym = StackSym::New(TyVar, this->m_func); |
| |
| IR::RegOpnd* loopCounterOpnd = IR::RegOpnd::New(m_loopCounterSym, TyVar, this->m_func); |
| loopCounterOpnd->SetIsJITOptimizedReg(true); |
| |
| IR::Instr * initInstr = IR::Instr::New(Js::OpCode::InitLoopBodyCount, loopCounterOpnd, this->m_func); |
| m_lastInstr->InsertAfter(initInstr); |
| m_lastInstr = initInstr; |
| initInstr->SetByteCodeOffset(m_jnReader.GetCurrentOffset()); |
| } |
| |
| IR::AddrOpnd * |
| IRBuilder::BuildAuxArrayOpnd(AuxArrayValue auxArrayType, uint32 auxArrayOffset) |
| { |
| switch (auxArrayType) |
| { |
| case AuxArrayValue::AuxPropertyIdArray: |
| case AuxArrayValue::AuxIntArray: |
| case AuxArrayValue::AuxFloatArray: |
| case AuxArrayValue::AuxVarsArray: |
| case AuxArrayValue::AuxFuncInfoArray: |
| case AuxArrayValue::AuxVarArrayVarCount: |
| { |
| IR::AddrOpnd * opnd = IR::AddrOpnd::New(m_func->GetJITFunctionBody()->GetAuxDataAddr(auxArrayOffset), IR::AddrOpndKindDynamicAuxBufferRef, m_func); |
| opnd->m_metadata = m_func->GetJITFunctionBody()->ReadFromAuxData(auxArrayOffset); |
| return opnd; |
| } |
| default: |
| Assert(UNREACHED); |
| return nullptr; |
| } |
| } |
| |
| IR::Opnd * |
| IRBuilder::BuildAuxObjectLiteralTypeRefOpnd(int objectId) |
| { |
| return IR::AddrOpnd::New(m_func->GetJITFunctionBody()->GetObjectLiteralTypeRef(objectId), IR::AddrOpndKindDynamicMisc, this->m_func); |
| } |
| |
| void |
| IRBuilder::DoClosureRegCheck(Js::RegSlot reg) |
| { |
| if (reg == Js::Constants::NoRegister) |
| { |
| return; |
| } |
| if (reg == m_func->GetJITFunctionBody()->GetEnvReg() || |
| reg == m_func->GetJITFunctionBody()->GetLocalClosureReg() || |
| reg == m_func->GetJITFunctionBody()->GetLocalFrameDisplayReg() || |
| reg == m_func->GetJITFunctionBody()->GetParamClosureReg()) |
| { |
| Js::Throw::FatalInternalError(); |
| } |
| } |
| |
| Js::RegSlot |
| IRBuilder::InnerScopeIndexToRegSlot(uint32 index) const |
| { |
| if (index >= m_func->GetJITFunctionBody()->GetInnerScopeCount()) |
| { |
| Js::Throw::FatalInternalError(); |
| } |
| Js::RegSlot reg = m_func->GetJITFunctionBody()->GetFirstInnerScopeReg() + index; |
| if (reg >= m_func->GetJITFunctionBody()->GetLocalsCount()) |
| { |
| Js::Throw::FatalInternalError(); |
| } |
| return reg; |
| } |
| |
| bool |
| IRBuilder::DoLoadInstructionArrayProfileInfo() |
| { |
| return !(!this->m_func->HasProfileInfo() || |
| ( |
| PHASE_OFF(Js::TypedArrayPhase, this->m_func->GetTopFunc()) && |
| PHASE_OFF(Js::ArrayCheckHoistPhase, this->m_func) |
| )); |
| } |
| |
| bool |
| IRBuilder::AllowNativeArrayProfileInfo() |
| { |
| return !((!(m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTry()) && m_func->GetWeakFuncRef() && !m_func->HasArrayInfo()) || |
| m_func->IsJitInDebugMode()); |
| } |