| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- // Shared interpreter loop
- //
- // This holds the single definition of the interpreter loop.
- // It allows for configurable copies of the loop that do extra work without
- // impacting the mainline performance. (for example the debug loop can simply
- // check a bit without concern for impacting the nondebug mode.)
- #if defined(INTERPRETER_ASMJS)
- #define INTERPRETER_OPCODE OpCodeAsmJs
- #define TRACING_FUNC &InterpreterStackFrame::TraceAsmJsOpCode
- #else
- #define INTERPRETER_OPCODE OpCode
- #define TRACING_FUNC &InterpreterStackFrame::TraceOpCode
- #endif
- #ifdef PROVIDE_INTERPRETER_STMTS
- #define READ_OP ReadOp_WPreviousStmtTracking<INTERPRETER_OPCODE, ByteCodeReader::ReadByteOp, TRACING_FUNC>
- #define READ_EXT_OP ReadOp_WPreviousStmtTracking<INTERPRETER_OPCODE, ByteCodeReader::ReadExtOp, TRACING_FUNC>
- #else
- #define READ_OP ReadOp<INTERPRETER_OPCODE, ByteCodeReader::ReadByteOp, TRACING_FUNC>
- #define READ_EXT_OP ReadOp<INTERPRETER_OPCODE, ByteCodeReader::ReadExtOp, TRACING_FUNC>
- #endif
- #ifdef PROVIDE_DEBUGGING
- #define DEBUGGING_LOOP 1
- #else
- #define DEBUGGING_LOOP 0
- #endif
- #ifdef PROVIDE_INTERPRETERPROFILE
- #define INTERPRETERPROFILE 1
- #define PROFILEDOP(prof, unprof) prof
- #else
- #define INTERPRETERPROFILE 0
- #define PROFILEDOP(prof, unprof) unprof
- #endif
- //two layers of macros are necessary to get arguments to the invocation of the top level macro expanded.
- #define CONCAT_TOKENS_AGAIN(loopName, fnSuffix) loopName ## fnSuffix
- #define CONCAT_TOKENS(loopName, fnSuffix) CONCAT_TOKENS_AGAIN(loopName, fnSuffix)
- #define PROCESS_OPCODE_FN_NAME(fnSuffix) CONCAT_TOKENS(INTERPRETERLOOPNAME, fnSuffix)
- const byte* Js::InterpreterStackFrame::PROCESS_OPCODE_FN_NAME(ExtendedOpcodePrefix)(const byte* ip)
- {
- INTERPRETER_OPCODE op = READ_EXT_OP(ip);
- switch (op)
- {
- #define EXDEF2(x, op, func) PROCESS_##x(op, func)
- #define EXDEF3(x, op, func, y) PROCESS_##x(op, func, y)
- #define EXDEF2_WMS(x, op, func) PROCESS_##x##_COMMON(op, func, _Small)
- #define EXDEF3_WMS(x, op, func, y) PROCESS_##x##_COMMON(op, func, y, _Small)
- #define EXDEF4_WMS(x, op, func, y, t) PROCESS_##x##_COMMON(op, func, y, _Small, t)
- #include "InterpreterHandler.inl"
- default:
- // Help the C++ optimizer by declaring that the cases we
- // have above are sufficient
- AssertMsg(false, "dispatch to bad opcode");
- __assume(false);
- };
- return ip;
- }
- const byte* Js::InterpreterStackFrame::PROCESS_OPCODE_FN_NAME(MediumLayoutPrefix)(const byte* ip, Var& yieldValue)
- {
- INTERPRETER_OPCODE op = READ_OP(ip);
- switch (op)
- {
- #ifndef INTERPRETER_ASMJS
- case INTERPRETER_OPCODE::Yield:
- m_reader.Reg2_Medium(ip);
- yieldValue = GetReg(GetFunctionBody()->GetYieldRegister());
- break;
- #endif
- #define DEF2_WMS(x, op, func) PROCESS_##x##_COMMON(op, func, _Medium)
- #define DEF3_WMS(x, op, func, y) PROCESS_##x##_COMMON(op, func, y, _Medium)
- #define DEF4_WMS(x, op, func, y, t) PROCESS_##x##_COMMON(op, func, y, _Medium, t)
- #include "InterpreterHandler.inl"
- default:
- // Help the C++ optimizer by declaring that the cases we
- // have above are sufficient
- AssertMsg(false, "dispatch to bad opcode");
- __assume(false);
- }
- return ip;
- }
- const byte* Js::InterpreterStackFrame::PROCESS_OPCODE_FN_NAME(ExtendedMediumLayoutPrefix)(const byte* ip)
- {
- INTERPRETER_OPCODE op = READ_EXT_OP(ip);
- switch (op)
- {
- #define EXDEF2_WMS(x, op, func) PROCESS_##x##_COMMON(op, func, _Medium)
- #define EXDEF3_WMS(x, op, func, y) PROCESS_##x##_COMMON(op, func, y, _Medium)
- #define EXDEF4_WMS(x, op, func, y, t) PROCESS_##x##_COMMON(op, func, y, _Medium, t)
- #include "InterpreterHandler.inl"
- default:
- // Help the C++ optimizer by declaring that the cases we
- // have above are sufficient
- AssertMsg(false, "dispatch to bad opcode");
- __assume(false);
- };
- return ip;
- }
- const byte* Js::InterpreterStackFrame::PROCESS_OPCODE_FN_NAME(LargeLayoutPrefix)(const byte* ip, Var& yieldValue)
- {
- INTERPRETER_OPCODE op = READ_OP(ip);
- switch (op)
- {
- #ifndef INTERPRETER_ASMJS
- case INTERPRETER_OPCODE::Yield:
- m_reader.Reg2_Large(ip);
- yieldValue = GetReg(GetFunctionBody()->GetYieldRegister());
- break;
- #endif
- #define DEF2_WMS(x, op, func) PROCESS_##x##_COMMON(op, func, _Large)
- #define DEF3_WMS(x, op, func, y) PROCESS_##x##_COMMON(op, func, y, _Large)
- #define DEF4_WMS(x, op, func, y, t) PROCESS_##x##_COMMON(op, func, y, _Large, t)
- #include "InterpreterHandler.inl"
- default:
- // Help the C++ optimizer by declaring that the cases we
- // have above are sufficient
- AssertMsg(false, "dispatch to bad opcode");
- __assume(false);
- }
- return ip;
- }
- const byte* Js::InterpreterStackFrame::PROCESS_OPCODE_FN_NAME(ExtendedLargeLayoutPrefix)(const byte* ip)
- {
- INTERPRETER_OPCODE op = READ_EXT_OP(ip);
- switch (op)
- {
- #define EXDEF2_WMS(x, op, func) PROCESS_##x##_COMMON(op, func, _Large)
- #define EXDEF3_WMS(x, op, func, y) PROCESS_##x##_COMMON(op, func, y, _Large)
- #define EXDEF4_WMS(x, op, func, y, t) PROCESS_##x##_COMMON(op, func, y, _Large, t)
- #include "InterpreterHandler.inl"
- default:
- // Help the C++ optimizer by declaring that the cases we
- // have above are sufficient
- AssertMsg(false, "dispatch to bad opcode");
- __assume(false);
- };
- return ip;
- }
- #if defined (DBG)
- // Win8 516184: Huge switch with lots of labels each having a few locals on ARM.DBG causes each occurrence
- // of this function (call of a javascript function in interpreter mode) to take 7+KB stack space
- // (without optimizations the compiler accounts for ALL locals inside case labels when allocating space on stack
- // for locals - SP does not change inside the function). On other platforms this is still huge but better than ARM.
- // So, for DBG turn on optimizations to prevent this huge loss of stack.
- #pragma optimize("g", on)
- #endif
- Js::Var Js::InterpreterStackFrame::INTERPRETERLOOPNAME()
- {
- PROBE_STACK(scriptContext, Js::Constants::MinStackInterpreter);
- if (!this->closureInitDone)
- {
- // If this is the start of the function, then we've waited until after the stack probe above
- // to set up the FD/SS pointers, so do it now.
- Assert(this->m_reader.GetCurrentOffset() == 0);
- this->InitializeClosures();
- }
- Assert(this->returnAddress != nullptr);
- AssertMsg(!this->GetFunctionBody()->GetUsesArgumentsObject() || m_arguments == NULL || Js::ArgumentsObject::Is(m_arguments), "Bad arguments!");
- // IP Passing in the interpreter:
- // We keep a local copy of the bytecode's instruction pointer and
- // pass it by reference to the bytecode reader.
- // This allows the optimizer to recognize that the local (held in a register)
- // dominates the copy in the reader.
- // The effect is our dispatch loop is significantly smaller in the common case
- // on optimized builds.
- //
- // For checked builds this does mean we are incrementing 2 different counters to
- // track the ip.
- const byte* ip = m_reader.GetIP();
- while (true)
- {
- INTERPRETER_OPCODE op = READ_OP(ip);
- #ifdef ENABLE_BASIC_TELEMETRY
- if( TELEMETRY_OPCODE_OFFSET_ENABLED )
- {
- OpcodeTelemetry& opcodeTelemetry = this->scriptContext->GetTelemetry().GetOpcodeTelemetry();
- opcodeTelemetry.ProgramLocationFunctionId ( this->function->GetFunctionInfo()->GetLocalFunctionId() );
- opcodeTelemetry.ProgramLocationBytecodeOffset( this->m_reader.GetCurrentOffset() );
- }
- #endif
- #if DEBUGGING_LOOP
- if (this->scriptContext->GetThreadContext()->GetDebugManager()->stepController.IsActive() &&
- this->scriptContext->GetThreadContext()->GetDebugManager()->stepController.IsStepComplete_AllowingFalsePositives(this))
- {
- // BrLong is used for branch island, we don't want to break over there, as they don't belong to any statement. Just skip this.
- if (!InterpreterStackFrame::IsBrLong(op, ip) && !this->m_functionBody->GetUtf8SourceInfo()->GetIsLibraryCode())
- {
- uint prevOffset = m_reader.GetCurrentOffset();
- #if ENABLE_TTD
- bool bpTaken = (!this->scriptContext->GetThreadContext()->IsRuntimeInTTDMode()) || this->scriptContext->GetThreadContext()->TTDExecutionInfo->ProcessBPInfoPreBreak(this->m_functionBody, this->scriptContext->GetThreadContext()->TTDLog);
- if(bpTaken)
- {
- InterpreterHaltState haltState(STOP_STEPCOMPLETE, m_functionBody);
- this->scriptContext->GetDebugContext()->GetProbeContainer()->DispatchStepHandler(&haltState, &op);
- }
- #else
- InterpreterHaltState haltState(STOP_STEPCOMPLETE, m_functionBody);
- this->scriptContext->GetDebugContext()->GetProbeContainer()->DispatchStepHandler(&haltState, &op);
- #endif
- #if ENABLE_TTD
- if(bpTaken && this->scriptContext->GetThreadContext()->IsRuntimeInTTDMode())
- {
- this->scriptContext->GetThreadContext()->TTDExecutionInfo->ProcessBPInfoPostBreak(this->m_functionBody);
- }
- #endif
- if (prevOffset != m_reader.GetCurrentOffset())
- {
- // The location of the statement has been changed, setnextstatement was called.
- // Reset m_outParams and m_outSp as before SetNext was called, we could be in the middle of StartCall.
- // It's fine to do because SetNext can only be done to a statement -- function-level destination,
- // and can't land to an expression inside call.
- ResetOut();
- ip = m_reader.GetIP();
- continue;
- }
- }
- }
- // The break opcode will be handled later in the switch block.
- if (op != OpCode::Break && this->scriptContext->GetThreadContext()->GetDebugManager()->asyncBreakController.IsBreak())
- {
- if (!InterpreterStackFrame::IsBrLong(op, ip) && !this->m_functionBody->GetUtf8SourceInfo()->GetIsLibraryCode())
- {
- uint prevOffset = m_reader.GetCurrentOffset();
- #if ENABLE_TTD
- bool bpTaken = (!this->scriptContext->GetThreadContext()->IsRuntimeInTTDMode()) || this->scriptContext->GetThreadContext()->TTDExecutionInfo->ProcessBPInfoPreBreak(this->m_functionBody, this->scriptContext->GetThreadContext()->TTDLog);
- if(bpTaken)
- {
- InterpreterHaltState haltState(STOP_ASYNCBREAK, m_functionBody);
- this->scriptContext->GetDebugContext()->GetProbeContainer()->DispatchAsyncBreak(&haltState);
- }
- #else
- InterpreterHaltState haltState(STOP_ASYNCBREAK, m_functionBody);
- this->scriptContext->GetDebugContext()->GetProbeContainer()->DispatchAsyncBreak(&haltState);
- #endif
- #if ENABLE_TTD
- if(bpTaken && this->scriptContext->GetThreadContext()->IsRuntimeInTTDMode())
- {
- this->scriptContext->GetThreadContext()->TTDExecutionInfo->ProcessBPInfoPostBreak(this->m_functionBody);
- }
- #endif
- if (prevOffset != m_reader.GetCurrentOffset())
- {
- // The location of the statement has been changed, setnextstatement was called.
- ip = m_reader.GetIP();
- continue;
- }
- }
- }
- SWAP_BP_FOR_OPCODE:
- #endif
- switch (op)
- {
- case INTERPRETER_OPCODE::Ret:
- {
- //
- // Return "Reg: 0" as the return-value.
- // - JavaScript functions always return a value, and this value is always
- // accessible to the caller. For an empty "return;" or exiting the end of the
- // function's body, it is assumed that the byte-code author
- // (ByteCodeGenerator) will load 'undefined' into R0.
- // - If R0 has not explicitly been set, it will contain whatever garbage value
- // was last set.
- //
- this->retOffset = m_reader.GetCurrentOffset();
- m_reader.Empty(ip);
- return GetReg((RegSlot)0);
- }
- #ifndef INTERPRETER_ASMJS
- case INTERPRETER_OPCODE::Yield:
- {
- m_reader.Reg2_Small(ip);
- return GetReg(GetFunctionBody()->GetYieldRegister());
- }
- #endif
- #define DEF2(x, op, func) PROCESS_##x(op, func)
- #define DEF3(x, op, func, y) PROCESS_##x(op, func, y)
- #define DEF2_WMS(x, op, func) PROCESS_##x##_COMMON(op, func, _Small)
- #define DEF3_WMS(x, op, func, y) PROCESS_##x##_COMMON(op, func, y, _Small)
- #define DEF4_WMS(x, op, func, y, t) PROCESS_##x##_COMMON(op, func, y, _Small, t)
- #include "InterpreterHandler.inl"
- #ifndef INTERPRETER_ASMJS
- case INTERPRETER_OPCODE::Leave:
- // Return the continuation address to the helper.
- // This tells the helper that control left the scope without completing the try/handler,
- // which is particularly significant when executing a finally.
- m_reader.Empty(ip);
- return (Var)this->m_reader.GetCurrentOffset();
- case INTERPRETER_OPCODE::LeaveNull:
- // Return to the helper without specifying a continuation address,
- // indicating that the handler completed without jumping, so exception processing
- // should continue.
- m_reader.Empty(ip);
- return nullptr;
- #endif
- #if ENABLE_PROFILE_INFO && !defined(INTERPRETER_ASMJS)
- // Aborting the current interpreter loop to switch the profile mode
- #define CHECK_SWITCH_PROFILE_MODE() if (switchProfileMode) return nullptr;
- #else
- #define CHECK_SWITCH_PROFILE_MODE()
- #endif
- #ifndef INTERPRETER_ASMJS
- #define CHECK_YIELD_VALUE() if (yieldValue != nullptr) return yieldValue;
- #else
- #define CHECK_YIELD_VALUE() Unused(yieldValue);
- #endif
- #define ExtendedCase(opcode) \
- case INTERPRETER_OPCODE::opcode: \
- ip = PROCESS_OPCODE_FN_NAME(opcode)(ip); \
- CHECK_SWITCH_PROFILE_MODE(); \
- break;
- ExtendedCase(ExtendedOpcodePrefix)
- ExtendedCase(ExtendedMediumLayoutPrefix)
- ExtendedCase(ExtendedLargeLayoutPrefix)
- case INTERPRETER_OPCODE::MediumLayoutPrefix:
- {
- Var yieldValue = nullptr;
- ip = PROCESS_OPCODE_FN_NAME(MediumLayoutPrefix)(ip, yieldValue);
- CHECK_YIELD_VALUE();
- CHECK_SWITCH_PROFILE_MODE();
- break;
- }
- case INTERPRETER_OPCODE::LargeLayoutPrefix:
- {
- Var yieldValue = nullptr;
- ip = PROCESS_OPCODE_FN_NAME(LargeLayoutPrefix)(ip, yieldValue);
- CHECK_YIELD_VALUE();
- CHECK_SWITCH_PROFILE_MODE();
- break;
- }
- case INTERPRETER_OPCODE::EndOfBlock:
- {
- // Note that at this time though ip was advanced by 'OpCode op = ReadByteOp<INTERPRETER_OPCODE>(ip)',
- // we haven't advanced m_reader.m_currentLocation yet, thus m_reader.m_currentLocation still points to EndOfBLock,
- // and that +1 will point to 1st byte past the buffer.
- Assert(m_reader.GetCurrentOffset() + sizeof(byte) == m_functionBody->GetByteCode()->GetLength());
- //
- // Reached an "OpCode::EndOfBlock" so need to exit this interpreter loop because
- // there is no more byte-code to execute.
- // - This prevents us from accessing random memory as byte-codes.
- // - Functions should contain an "OpCode::Ret" instruction to organize an
- // orderly return.
- //
- #if DEBUGGING_LOOP
- // However, during debugging an exception can be skipped which causes the
- // statement that caused to exception to be skipped. If this statement is
- // the statement that contains the OpCode::Ret then the EndOfBlock will
- // be executed. In these cases it is sufficient to return undefined.
- return this->scriptContext->GetLibrary()->GetUndefined();
- #else
- return nullptr;
- #endif
- }
- #ifndef INTERPRETER_ASMJS
- case INTERPRETER_OPCODE::Break:
- {
- #if DEBUGGING_LOOP
- // The reader has already advanced the IP:
- if (this->m_functionBody->ProbeAtOffset(m_reader.GetCurrentOffset(), &op))
- {
- uint prevOffset = m_reader.GetCurrentOffset();
- #if ENABLE_TTD
- bool bpTaken = (!this->scriptContext->GetThreadContext()->IsRuntimeInTTDMode()) || this->scriptContext->GetThreadContext()->TTDExecutionInfo->ProcessBPInfoPreBreak(this->m_functionBody, this->scriptContext->GetThreadContext()->TTDLog);
- if(bpTaken)
- {
- InterpreterHaltState haltState(STOP_BREAKPOINT, m_functionBody);
- this->scriptContext->GetDebugContext()->GetProbeContainer()->DispatchProbeHandlers(&haltState);
- }
- #else
- InterpreterHaltState haltState(STOP_BREAKPOINT, m_functionBody);
- this->scriptContext->GetDebugContext()->GetProbeContainer()->DispatchProbeHandlers(&haltState);
- #endif
- #if ENABLE_TTD
- if(bpTaken && this->scriptContext->GetThreadContext()->IsRuntimeInTTDMode())
- {
- this->scriptContext->GetThreadContext()->TTDExecutionInfo->ProcessBPInfoPostBreak(this->m_functionBody);
- }
- #endif
- if (prevOffset != m_reader.GetCurrentOffset())
- {
- // The location of the statement has been changed, setnextstatement was called.
- ip = m_reader.GetIP();
- continue;
- }
- // Jump back to the start of the switch.
- goto SWAP_BP_FOR_OPCODE;
- }
- else
- {
- #if DEBUGGING_LOOP
- // an inline break statement rather than a probe
- if (!this->scriptContext->GetThreadContext()->GetDebugManager()->stepController.ContinueFromInlineBreakpoint())
- {
- uint prevOffset = m_reader.GetCurrentOffset();
- #if ENABLE_TTD
- bool bpTaken = (!this->scriptContext->GetThreadContext()->IsRuntimeInTTDMode()) || this->scriptContext->GetThreadContext()->TTDExecutionInfo->ProcessBPInfoPreBreak(this->m_functionBody, this->scriptContext->GetThreadContext()->TTDLog);
- if(bpTaken)
- {
- InterpreterHaltState haltState(STOP_INLINEBREAKPOINT, m_functionBody);
- this->scriptContext->GetDebugContext()->GetProbeContainer()->DispatchInlineBreakpoint(&haltState);
- }
- #else
- InterpreterHaltState haltState(STOP_INLINEBREAKPOINT, m_functionBody);
- this->scriptContext->GetDebugContext()->GetProbeContainer()->DispatchInlineBreakpoint(&haltState);
- #endif
- #if ENABLE_TTD
- if(bpTaken && this->scriptContext->GetThreadContext()->IsRuntimeInTTDMode())
- {
- this->scriptContext->GetThreadContext()->TTDExecutionInfo->ProcessBPInfoPostBreak(this->m_functionBody);
- }
- #endif
- if (prevOffset != m_reader.GetCurrentOffset())
- {
- // The location of the statement has been changed, setnextstatement was called.
- ip = m_reader.GetIP();
- continue;
- }
- }
- #endif
- // Consume after dispatching
- m_reader.Empty(ip);
- }
- #else
- m_reader.Empty(ip);
- #endif
- break;
- }
- #endif
- default:
- // Help the C++ optimizer by declaring that the cases we
- // have above are sufficient
- AssertMsg(false, "dispatch to bad opcode");
- __assume(false);
- }
- }
- }
- #if defined (DBG)
- // Restore optimizations to what's specified by the /O switch.
- #pragma optimize("", on)
- #endif
- #undef READ_OP
- #undef READ_EXT_OP
- #undef TRACING_FUNC
- #undef DEBUGGING_LOOP
- #undef INTERPRETERPROFILE
- #undef PROFILEDOP
- #undef INTERPRETER_OPCODE
- #undef CHECK_SWITCH_PROFILE_MODE
- #undef CHECK_YIELD_VALUE
|