bool CodeGenWorkItem::ShouldSpeculativelyJitBasedOnProfile() const { Js::FunctionBody* functionBody = this->GetFunctionBody(); uint loopPercentage = (functionBody->GetByteCodeInLoopCount()*100) / (functionBody->GetByteCodeCount() + 1); uint straightLineSize = functionBody->GetByteCodeCount() - functionBody->GetByteCodeInLoopCount(); // This ensures only small and loopy functions are prejitted. if(loopPercentage >= 50 || straightLineSize < 300) { Js::SourceDynamicProfileManager* profileManager = functionBody->GetSourceContextInfo()->sourceDynamicProfileManager; if(profileManager != nullptr) { functionBody->SetIsSpeculativeJitCandidate(); if(!functionBody->HasDynamicProfileInfo()) { return false; } Js::ExecutionFlags executionFlags = profileManager->IsFunctionExecuted(functionBody->GetLocalFunctionId()); if(executionFlags == Js::ExecutionFlags_Executed) { return true; } } } return false; }
void SnapshotExtractor::ExtractSlotArrayIfNeeded(Js::ScriptContext* ctx, Js::Var* scope) { if(this->m_marks.IsMarked(scope)) { NSSnapValues::SlotArrayInfo* slotInfo = this->m_pendingSnap->GetNextAvailableSlotArrayEntry(); Js::ScopeSlots slots(scope); slotInfo->SlotId = TTD_CONVERT_VAR_TO_PTR_ID(scope); slotInfo->ScriptContextLogId = ctx->ScriptContextLogTag; slotInfo->SlotCount = slots.GetCount(); slotInfo->Slots = this->m_pendingSnap->GetSnapshotSlabAllocator().SlabAllocateArray<TTDVar>(slotInfo->SlotCount); for(uint32 j = 0; j < slotInfo->SlotCount; ++j) { slotInfo->Slots[j] = slots.Get(j); } if(slots.IsFunctionScopeSlotArray()) { Js::FunctionBody* fb = slots.GetFunctionBody(); slotInfo->isFunctionBodyMetaData = true; slotInfo->OptFunctionBodyId = TTD_CONVERT_FUNCTIONBODY_TO_PTR_ID(fb); #if ENABLE_TTD_INTERNAL_DIAGNOSTICS Js::PropertyId* propertyIds = fb->GetPropertyIdsForScopeSlotArray(); slotInfo->DebugPIDArray = this->m_pendingSnap->GetSnapshotSlabAllocator().SlabAllocateArray<Js::PropertyId>(slotInfo->SlotCount); for(uint32 j = 0; j < slotInfo->SlotCount; ++j) { slotInfo->DebugPIDArray[j] = propertyIds[j]; } #endif } else { slotInfo->isFunctionBodyMetaData = false; slotInfo->OptFunctionBodyId = TTD_INVALID_PTR_ID; #if ENABLE_TTD_INTERNAL_DIAGNOSTICS slotInfo->DebugPIDArray = this->m_pendingSnap->GetSnapshotSlabAllocator().SlabAllocateArray<Js::PropertyId>(slotInfo->SlotCount); for(uint32 j = 0; j < slotInfo->SlotCount; ++j) { slotInfo->DebugPIDArray[j] = (Js::PropertyId)0; } #endif } this->m_marks.ClearMark(scope); } }
void SnapshotExtractor::MarkFunctionBody(Js::FunctionBody* fb) { if(this->m_marks.MarkAndTestAddr<MarkTableTag::FunctionBodyTag>(fb)) { Js::FunctionBody* currfb = fb->GetScriptContext()->TTDContextInfo->ResolveParentBody(fb); while(currfb != nullptr && this->m_marks.MarkAndTestAddr<MarkTableTag::FunctionBodyTag>(currfb)) { currfb = currfb->GetScriptContext()->TTDContextInfo->ResolveParentBody(currfb); } } }
void JsrtDebugUtils::AddFileNameOrScriptTypeToObject(Js::DynamicObject* object, Js::Utf8SourceInfo* utf8SourceInfo) { if (utf8SourceInfo->IsDynamic()) { AssertMsg(utf8SourceInfo->GetSourceContextInfo()->url == nullptr, "How come dynamic code have a url?"); Js::FunctionBody* anyFunctionBody = utf8SourceInfo->GetAnyParsedFunction(); Assert(anyFunctionBody != nullptr); LPCWSTR sourceName = anyFunctionBody->GetSourceName(); JsrtDebugUtils::AddPropertyToObject(object, JsrtDebugPropertyId::scriptType, sourceName, wcslen(sourceName), utf8SourceInfo->GetScriptContext()); } else { // url can be nullptr if JsParseScript/JsRunScript didn't passed any const char16* url = utf8SourceInfo->GetSourceContextInfo()->url == nullptr ? _u("") : utf8SourceInfo->GetSourceContextInfo()->url; JsrtDebugUtils::AddPropertyToObject(object, JsrtDebugPropertyId::fileName, url, wcslen(url), utf8SourceInfo->GetScriptContext()); } }
Js::FunctionBody* TTDebuggerSourceLocation::ResolveAssociatedSourceInfo(Js::ScriptContext* ctx) const { Js::FunctionBody* resBody = ctx->TTDContextInfo->FindFunctionBodyByFileName(this->m_sourceFile); while(true) { for(uint32 i = 0; i < resBody->GetNestedCount(); ++i) { Js::ParseableFunctionInfo* ipfi = resBody->GetNestedFunc(i)->EnsureDeserialized(); Js::FunctionBody* ifb = JsSupport::ForceAndGetFunctionBody(ipfi); if(this->m_functionLine == ifb->GetLineNumber() && this->m_functionColumn == ifb->GetColumnNumber()) { return ifb; } //if it starts on a larger line or if same line but larger column then we don't contain the target AssertMsg(ifb->GetLineNumber() < this->m_functionLine || (ifb->GetLineNumber() == this->m_functionLine && ifb->GetColumnNumber() < this->m_functionColumn), "We went to far but didn't find our function??"); uint32 endLine = UINT32_MAX; uint32 endColumn = UINT32_MAX; if(i + 1 < resBody->GetNestedCount()) { Js::ParseableFunctionInfo* ipfinext = resBody->GetNestedFunc(i + 1)->EnsureDeserialized(); Js::FunctionBody* ifbnext = JsSupport::ForceAndGetFunctionBody(ipfinext); endLine = ifbnext->GetLineNumber(); endColumn = ifbnext->GetColumnNumber(); } if(endLine > this->m_functionLine || (endLine == this->m_functionLine && endColumn > this->m_functionColumn)) { resBody = ifb; break; } } } AssertMsg(false, "We should never get here!!!"); return nullptr; }
void Sym::Dump(IRDumpFlags flags, const ValueType valueType) { bool const AsmDumpMode = flags & IRDumpFlags_AsmDumpMode; bool const SimpleForm = !!(flags & IRDumpFlags_SimpleForm); if (AsmDumpMode) { if (this->IsStackSym() && this->AsStackSym()->IsArgSlotSym()) { Output::Print(L"arg "); } else if (this->IsStackSym() && this->AsStackSym()->IsParamSlotSym()) { Output::Print(L"param "); } } else { if (this->IsStackSym() && this->AsStackSym()->IsArgSlotSym()) { if (this->AsStackSym()->m_isInlinedArgSlot) { Output::Print(L"iarg%d", this->AsStackSym()->GetArgSlotNum()); } else { Output::Print(L"arg%d", this->AsStackSym()->GetArgSlotNum()); } Output::Print(L"(s%d)", m_id); } else if (this->IsStackSym() && this->AsStackSym()->IsParamSlotSym()) { Output::Print(L"prm%d", this->AsStackSym()->GetParamSlotNum()); } else { if (!this->IsPropertySym() || !SimpleForm) { Output::Print(L"s%d", m_id); } if (this->IsStackSym()) { if(Js::Configuration::Global.flags.Debug && this->AsStackSym()->HasByteCodeRegSlot()) { StackSym* sym = this->AsStackSym(); Js::FunctionBody* functionBody = sym->GetByteCodeFunc()->GetJnFunction(); if(functionBody->GetPropertyIdOnRegSlotsContainer()) { if(functionBody->IsNonTempLocalVar(sym->GetByteCodeRegSlot())) { uint index = sym->GetByteCodeRegSlot() - functionBody->GetConstantCount(); Js::PropertyId propertyId = functionBody->GetPropertyIdOnRegSlotsContainer()->propertyIdsForRegSlots[index]; Output::Print(L"(%s)", functionBody->GetScriptContext()->GetPropertyNameLocked(propertyId)->GetBuffer()); } } } if (this->AsStackSym()->IsVar()) { if (this->AsStackSym()->HasObjectTypeSym() && !SimpleForm) { Output::Print(L"<s%d>", this->AsStackSym()->GetObjectTypeSym()->m_id); } } else { StackSym *varSym = this->AsStackSym()->GetVarEquivSym(nullptr); if (varSym) { Output::Print(L"(s%d)", varSym->m_id); } } if (!SimpleForm) { if (this->AsStackSym()->m_builtInIndex != Js::BuiltinFunction::None) { Output::Print(L"[ffunc]"); } } } } if(IsStackSym()) { IR::Opnd::DumpValueType(valueType); } } if (this->IsPropertySym()) { PropertySym *propertySym = this->AsPropertySym(); if (!SimpleForm) { Output::Print(L"("); } Js::ScriptContext* scriptContext; switch (propertySym->m_fieldKind) { case PropertyKindData: { propertySym->m_stackSym->Dump(flags, valueType); scriptContext = propertySym->m_func->GetScriptContext(); Js::PropertyRecord const* fieldName = scriptContext->GetPropertyNameLocked(propertySym->m_propertyId); Output::Print(L"->%s", fieldName->GetBuffer()); break; } case PropertyKindSlots: case PropertyKindSlotArray: propertySym->m_stackSym->Dump(flags, valueType); Output::Print(L"[%d]", propertySym->m_propertyId); break; case PropertyKindLocalSlots: propertySym->m_stackSym->Dump(flags, valueType); Output::Print(L"l[%d]", propertySym->m_propertyId); break; default: AssertMsg(0, "Unknown field kind"); break; } if (!SimpleForm) { Output::Print(L")"); } } }
bool StepController::IsStepComplete(InterpreterHaltState* haltState, HaltCallback * haltCallback, OpCode originalOpcode) { int currentFrameCount = haltState->framePointers->Count(); AssertMsg(currentFrameCount > 0, "In IsStepComplete we must have at least one frame."); FunctionBody* body = haltState->framePointers->Peek()->GetJavascriptFunction()->GetFunctionBody(); bool canPossiblyHalt = haltCallback->CanHalt(haltState); OUTPUT_TRACE(Js::DebuggerPhase, _u("StepController::IsStepComplete(): stepType = %d "), stepType); uint scriptId = GetScriptId(body); AssertMsg(scriptId != InvalidScriptId, "scriptId cannot be 'invalid-reserved'"); int byteOffset = haltState->GetCurrentOffset(); bool fCanHalt = false; if (this->frameCountWhenSet > currentFrameCount && STEP_DOCUMENT != stepType) { // all steps match once the frame they started on has popped. fCanHalt = canPossiblyHalt; } else if (STEP_DOCUMENT == stepType) { OUTPUT_TRACE(Js::DebuggerPhase, _u("StepController::IsStepComplete(): docId when set=%d, currentDocId = %d, can Halt = %d, will halt = %d "), this->scriptIdWhenSet, scriptId, canPossiblyHalt, fCanHalt); fCanHalt = (scriptId != this->scriptIdWhenSet) && canPossiblyHalt; } else if (STEP_IN != stepType && this->frameCountWhenSet < currentFrameCount) { // Only step into allows the stack to be deeper OUTPUT_TRACE(Js::DebuggerPhase, _u("StepController::IsStepComplete(stepType = %d) returning false "), stepType); return false; } else if (STEP_OUT == stepType) { fCanHalt = this->frameCountWhenSet > currentFrameCount && canPossiblyHalt; } else if (nullptr != this->statementMap && this->statementMap->isSubexpression && STEP_IN != stepType) { // Only step into started from subexpression is allowed to stop on another subexpression Js::FunctionBody* pCurrentFuncBody = haltState->GetFunction(); Js::FunctionBody::StatementMap* map = pCurrentFuncBody->GetMatchingStatementMapFromByteCode(byteOffset, false); if (nullptr != map && map->isSubexpression) // Execute remaining Subexpressions { fCanHalt = false; } else { Js::FunctionBody::StatementMap* outerMap = pCurrentFuncBody->GetMatchingStatementMapFromByteCode(this->statementMap->byteCodeSpan.begin, true); if (nullptr != outerMap && map == outerMap) // Execute the rest of current regular statement { fCanHalt = false; } else { fCanHalt = canPossiblyHalt; } } } else { // Match if we are no longer on the original statement. Stepping means move off current statement. if (body != this->body || NULL == this->statementMap || !this->statementMap->byteCodeSpan.Includes(byteOffset)) { fCanHalt = canPossiblyHalt; } } // At this point we are verifying of global return opcode. // The global returns are alway added as a zero range begin with zero. if (fCanHalt && originalOpcode == OpCode::Ret) { Js::FunctionBody* pCurrentFuncBody = haltState->GetFunction(); Js::FunctionBody::StatementMap* map = pCurrentFuncBody->GetMatchingStatementMapFromByteCode(byteOffset, true); fCanHalt = !FunctionBody::IsDummyGlobalRetStatement(&map->sourceSpan); if (fCanHalt) { // We are breaking at last line of function, imagine '}' AddReturnToReturnedValueContainer(); } } OUTPUT_TRACE(Js::DebuggerPhase, _u("StepController::IsStepComplete(stepType = %d) returning %d "), stepType, fCanHalt); return fCanHalt; }