Var SIMDBool8x16Lib::EntryAnyTrue(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); Assert(!(callInfo.Flags & CallFlags_New)); if (args.Info.Count >= 2 && JavascriptSIMDBool8x16::Is(args[1])) { JavascriptSIMDBool8x16 *a = JavascriptSIMDBool8x16::FromVar(args[1]); Assert(a); bool result = SIMDBool32x4Operation::OpAnyTrue(a->GetValue()); return JavascriptBoolean::ToVar(result, scriptContext); } JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdBool8x16TypeMismatch, _u("AnyTrue")); }
void BackgroundJobProcessor::Run(ParallelThreadData* threadData) { JS_ETW(EventWriteJSCRIPT_NATIVECODEGEN_START(this, 0)); ArenaAllocator threadArena(_u("ThreadArena"), threadData->GetPageAllocator(), Js::Throw::OutOfMemory); threadData->threadArena = &threadArena; { // Make sure we take decommit action before the threadArena is torn down, in case the // thread context goes away and the loop exits. struct AutoDecommit { AutoDecommit(JobProcessor *proc, ParallelThreadData *data) : processor(proc), threadData(data) {} ~AutoDecommit() { processor->ForEachManager([this](JobManager *manager) { manager->OnDecommit(this->threadData); }); } ParallelThreadData *threadData; JobProcessor *processor; } autoDecommit(this, threadData); criticalSection.Enter(); while (!IsClosed() || (jobs.Head() && jobs.Head()->IsCritical())) { Job *job = jobs.UnlinkFromBeginning(); if(!job) { // No jobs in queue, wait for one Assert(!IsClosed()); Assert(!threadData->isWaitingForJobs); threadData->isWaitingForJobs = true; criticalSection.Leave(); JS_ETW(EventWriteJSCRIPT_NATIVECODEGEN_STOP(this, 0)); if (threadService->HasCallback()) { // We have a thread service, so simply return the thread back now. // When new jobs are submitted, we will be called to process again. return; } WaitForJobReadyOrShutdown(threadData); JS_ETW(EventWriteJSCRIPT_NATIVECODEGEN_START(this, 0)); criticalSection.Enter(); threadData->isWaitingForJobs = false; continue; } Assert(numJobs != 0); --numJobs; threadData->currentJob = job; criticalSection.Leave(); const bool succeeded = Process(job, threadData); criticalSection.Enter(); threadData->currentJob = 0; JobManager *const manager = job->Manager(); JobProcessed(manager, job, succeeded); // the job may be deleted during this and should not be used afterwards Assert(manager->numJobsAddedToProcessor != 0); --manager->numJobsAddedToProcessor; if(manager->isWaitable) { WaitableJobManager *const waitableManager = static_cast<WaitableJobManager *>(manager); Assert(!(waitableManager->jobBeingWaitedUpon && waitableManager->isWaitingForQueuedJobs)); if(waitableManager->jobBeingWaitedUpon == job) { waitableManager->jobBeingWaitedUpon = 0; waitableManager->jobBeingWaitedUponProcessed.Set(); } else if(waitableManager->isWaitingForQueuedJobs && manager->numJobsAddedToProcessor == 0) { waitableManager->isWaitingForQueuedJobs = false; waitableManager->queuedJobsProcessed.Set(); } } if(manager->numJobsAddedToProcessor == 0) LastJobProcessed(manager); // the manager may be deleted during this and should not be used afterwards } criticalSection.Leave(); JS_ETW(EventWriteJSCRIPT_NATIVECODEGEN_STOP(this, 0)); } }
// // Load persisted scope info. // void ScopeInfo::GetScopeInfo(Parser *parser, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, Scope* scope) { ScriptContext* scriptContext; ArenaAllocator* alloc; // Load scope attributes and push onto scope stack. scope->SetIsDynamic(this->isDynamic); if (this->isObject) { scope->SetIsObject(); } scope->SetMustInstantiate(this->mustInstantiate); if (!this->GetCanMergeWithBodyScope()) { scope->SetCannotMergeWithBodyScope(); } scope->SetHasOwnLocalInClosure(this->hasLocalInClosure); if (parser) { scriptContext = parser->GetScriptContext(); alloc = parser->GetAllocator(); } else { TRACE_BYTECODE(_u("\nRestore ScopeInfo: %s #symbols: %d %s\n"), funcInfo->name, symbolCount, isObject ? _u("isObject") : _u("")); Assert(!this->isCached || scope == funcInfo->GetBodyScope()); funcInfo->SetHasCachedScope(this->isCached); byteCodeGenerator->PushScope(scope); // The scope is already populated, so we're done. return; } // Load scope symbols // On first access to the scopeinfo, replace the ID's with PropertyRecord*'s to save the dictionary lookup // on later accesses. Replace them all before allocating Symbol's to prevent inconsistency on OOM. if (!this->areNamesCached && !PHASE_OFF1(Js::CacheScopeInfoNamesPhase)) { for (int i = 0; i < symbolCount; i++) { PropertyId propertyId = GetSymbolId(i); if (propertyId != 0) // There may be empty slots, e.g. "arguments" may have no slot { PropertyRecord const* name = scriptContext->GetPropertyName(propertyId); this->SetPropertyName(i, name); } } this->areNamesCached = true; } for (int i = 0; i < symbolCount; i++) { PropertyRecord const* name = nullptr; if (this->areNamesCached) { name = this->GetPropertyName(i); } else { PropertyId propertyId = GetSymbolId(i); if (propertyId != 0) // There may be empty slots, e.g. "arguments" may have no slot { name = scriptContext->GetPropertyName(propertyId); } } if (name != nullptr) { SymbolType symbolType = GetSymbolType(i); SymbolName symName(name->GetBuffer(), name->GetLength()); Symbol *sym = Anew(alloc, Symbol, symName, nullptr, symbolType); sym->SetScopeSlot(static_cast<PropertyId>(i)); sym->SetIsBlockVar(GetIsBlockVariable(i)); if (GetHasFuncAssignment(i)) { sym->RestoreHasFuncAssignment(); } scope->AddNewSymbol(sym); if (parser) { parser->RestorePidRefForSym(sym); } TRACE_BYTECODE(_u("%12s %d\n"), sym->GetName().GetBuffer(), sym->GetScopeSlot()); } } this->scope = scope; DebugOnly(scope->isRestored = true); }
Js::FunctionInfo *InliningDecider::Inline(Js::FunctionBody *const inliner, Js::FunctionInfo* functionInfo, bool isConstructorCall, bool isPolymorphicCall, uint16 constantArgInfo, Js::ProfileId callSiteId, uint recursiveInlineDepth, bool allowRecursiveInlining) { #if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS) char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE]; char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE]; #endif Js::FunctionProxy * proxy = functionInfo->GetFunctionProxy(); if (proxy && proxy->IsFunctionBody()) { if (isLoopBody && PHASE_OFF(Js::InlineInJitLoopBodyPhase, this->topFunc)) { INLINE_TESTTRACE_VERBOSE(_u("INLINING: Skip Inline: Jit loop body: %s (%s)\n"), this->topFunc->GetDisplayName(), this->topFunc->GetDebugNumberSet(debugStringBuffer)); return nullptr; } // Note: disable inline for debugger, as we can't bailout at return from function. // Alternative can be generate this bailout as part of inline, which can be done later as perf improvement. const auto inlinee = proxy->GetFunctionBody(); Assert(this->jitMode == ExecutionMode::FullJit); if (PHASE_OFF(Js::InlinePhase, inlinee) || PHASE_OFF(Js::GlobOptPhase, inlinee) || !ContinueInliningUserDefinedFunctions(this->bytecodeInlinedCount) || this->isInDebugMode) { return nullptr; } if (functionInfo->IsDeferred() || inlinee->GetByteCode() == nullptr) { // DeferredParse... INLINE_TESTTRACE(_u("INLINING: Skip Inline: No bytecode\tInlinee: %s (%s)\tCaller: %s (%s)\n"), inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2)); return nullptr; } #ifdef _M_IX86 if (inlinee->GetHasTry()) { INLINE_TESTTRACE(_u("INLINING: Skip Inline: Has try\tInlinee: %s (%s)\tCaller: %s (%s)\n"), inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2)); return nullptr; } #endif // This is a hard limit as the argOuts array is statically sized. if (inlinee->GetInParamsCount() > Js::InlineeCallInfo::MaxInlineeArgoutCount) { INLINE_TESTTRACE(_u("INLINING: Skip Inline: Params count greater then MaxInlineeArgoutCount\tInlinee: %s (%s)\tParamcount: %d\tMaxInlineeArgoutCount: %d\tCaller: %s (%s)\n"), inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inlinee->GetInParamsCount(), Js::InlineeCallInfo::MaxInlineeArgoutCount, inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2)); return nullptr; } // Wasm functions can have no params if (inlinee->GetInParamsCount() == 0 && !inlinee->GetIsAsmjsMode()) { // Inline candidate has no params, not even a this pointer. This can only be the global function, // which we shouldn't inline. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Params count is zero!\tInlinee: %s (%s)\tParamcount: %d\tCaller: %s (%s)\n"), inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inlinee->GetInParamsCount(), inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2)); return nullptr; } if (inlinee->GetDontInline()) { INLINE_TESTTRACE(_u("INLINING: Skip Inline: Do not inline\tInlinee: %s (%s)\tCaller: %s (%s)\n"), inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2)); return nullptr; } // Do not inline a call to a class constructor if it isn't part of a new expression since the call will throw a TypeError anyway. if (inlinee->IsClassConstructor() && !isConstructorCall) { INLINE_TESTTRACE(_u("INLINING: Skip Inline: Class constructor without new keyword\tInlinee: %s (%s)\tCaller: %s (%s)\n"), inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2)); return nullptr; } if (!DeciderInlineIntoInliner(inlinee, inliner, isConstructorCall, isPolymorphicCall, constantArgInfo, recursiveInlineDepth, allowRecursiveInlining)) { return nullptr; } #if defined(ENABLE_DEBUG_CONFIG_OPTIONS) TraceInlining(inliner, inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inlinee->GetByteCodeCount(), this->topFunc, this->bytecodeInlinedCount, inlinee, callSiteId, this->isLoopBody); #endif this->bytecodeInlinedCount += inlinee->GetByteCodeCount(); return inlinee->GetFunctionInfo(); } Js::OpCode builtInInlineCandidateOpCode; ValueType builtInReturnType; GetBuiltInInfo(functionInfo, &builtInInlineCandidateOpCode, &builtInReturnType); if(builtInInlineCandidateOpCode == 0 && builtInReturnType.IsUninitialized()) { return nullptr; } Assert(this->jitMode == ExecutionMode::FullJit); if (builtInInlineCandidateOpCode != 0 && ( PHASE_OFF(Js::InlinePhase, inliner) || PHASE_OFF(Js::GlobOptPhase, inliner) || isConstructorCall )) { return nullptr; } // Note: for built-ins at this time we don't have enough data (the instr) to decide whether it's going to be inlined. return functionInfo; }
const char16* JavascriptSIMDInt8x16::GetFullBuiltinName(char16** aBuffer, const char16* name) { Assert(aBuffer && *aBuffer); swprintf_s(*aBuffer, SIMD_STRING_BUFFER_MAX, _u("SIMD.Int8x16.%s"), name); return *aBuffer; }
template<> void PrintTmpRegisterDeAllocation<AsmJsSIMDValue>(RegSlot loc) { if (PHASE_ON1(AsmjsTmpRegisterAllocationPhase)) Output::Print(_u("-SIMD%d\n"), loc); }
namespace Js { const INT64 DateUtilities::ticksPerMillisecond = 10000; const double DateUtilities::ticksPerMillisecondDouble = 10000.0; const INT64 DateUtilities::ticksPerSecond = ticksPerMillisecond * 1000; const INT64 DateUtilities::ticksPerMinute = ticksPerSecond * 60; const INT64 DateUtilities::ticksPerHour = ticksPerMinute * 60; const INT64 DateUtilities::ticksPerDay = ticksPerHour * 24; const INT64 DateUtilities::jsEpochMilliseconds = 11644473600000; const INT64 DateUtilities::jsEpochTicks = jsEpochMilliseconds * ticksPerMillisecond; // The day numbers for the months of a leap year. static const int g_rgday[12] = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, }; const double g_kdblJanuary1st1970 = 25569.0; const char16 g_rgpszDay[7][4] = { _u("Sun"), _u("Mon"), _u("Tue"), _u("Wed"), _u("Thu"), _u("Fri"), _u("Sat") }; const char16 g_rgpszMonth[12][4] = { _u("Jan"), _u("Feb"), _u("Mar"), _u("Apr"), _u("May"), _u("Jun"), _u("Jul"), _u("Aug"), _u("Sep"), _u("Oct"), _u("Nov"), _u("Dec") }; const char16 g_rgpszZone[8][4] = { _u("EST"), _u("EDT"), _u("CST"), _u("CDT"), _u("MST"), _u("MDT"), _u("PST"), _u("PDT") }; // // Convert an ES5 date based on double to a WinRT DateTime // DateTime is the number of ticks that have elapsed since 1/1/1601 00:00:00 in 100ns precision // If we return a failure HRESULT other than E_INVALIDARG, the es5 date can't be expressed // in the WinRT scheme // HRESULT DateUtilities::ES5DateToWinRTDate(double es5Date, __out INT64* pRet) { Assert(pRet != NULL); if (pRet == NULL) { return E_INVALIDARG; } INT64 es5DateAsInt64 = NumberUtilities::TryToInt64(es5Date); if (!NumberUtilities::IsValidTryToInt64(es5DateAsInt64)) return INTSAFE_E_ARITHMETIC_OVERFLOW; // First, we rebase it to the WinRT epoch, then we convert the time in milliseconds to ticks INT64 numTicks; if (!Int64Math::Add(es5DateAsInt64, jsEpochMilliseconds, &numTicks)) { INT64 adjustedTicks = 0; if (!Int64Math::Mul(numTicks, ticksPerMillisecond, &adjustedTicks)) { (*pRet) = adjustedTicks; return S_OK; } } return INTSAFE_E_ARITHMETIC_OVERFLOW; } ///------------------------------------------------------------------------------ /// Get a time value from SYSTEMTIME structure. /// /// Returns number of milliseconds since Jan 1, 1970 ///------------------------------------------------------------------------------ double DateUtilities::TimeFromSt(SYSTEMTIME *pst) { return TvFromDate(pst->wYear,pst->wMonth-1,pst->wDay-1, DayTimeFromSt(pst)); } ///------------------------------------------------------------------------------ /// Get a time value from SYSTEMTIME structure within a day /// /// Returns number of milliseconds since 12:00 AM ///------------------------------------------------------------------------------ double DateUtilities::DayTimeFromSt(SYSTEMTIME *pst) { return (pst->wHour * 3600000.0) + (pst->wMinute * 60000.0) + (pst->wSecond * 1000.0) + pst->wMilliseconds; } ///------------------------------------------------------------------------------ /// Get a time value from (year, mon, day, time) values. ///------------------------------------------------------------------------------ double DateUtilities::TvFromDate(double year, double mon, double day, double time) { // For positive month, use fast path: '/' and '%' rather than 'floor()' and 'fmod()'. // But make sure there is no overflow when casting double -> int -- WOOB 1142298. if (mon >= 0 && mon <= INT_MAX) { year += ((int)mon) / 12; mon = ((int)mon) % 12; } else { year += floor(mon/12); mon = DblModPos(mon,12); } day += DayFromYear(year); AssertMsg(mon >= 0 && mon <= 11, "'mon' must be in the range of [0..11]."); day += g_rgday[(int)mon]; if (mon >= 2 && !FLeap((int)year)) { day -= 1; } return day * 86400000 + time; } ///------------------------------------------------------------------------------ /// Get the non-negative remainder. ///------------------------------------------------------------------------------ double DateUtilities::DblModPos(double dbl, double dblDen) { AssertMsg(dblDen > 0, "value not positive"); dbl = fmod(dbl, dblDen); if (dbl < 0) { dbl += dblDen; } AssertMsg(dbl >= 0 && dbl < dblDen, ""); return dbl; } ///------------------------------------------------------------------------------ /// DayFromYear is: /// 365 * y + floor((y+1)/4) - floor((y+69)/100) + floor((y+369)/400). /// where y is the calendar year minus 1970. ///------------------------------------------------------------------------------ double DateUtilities::DayFromYear(double year) { double day = 365 * (year -= 1970); if (day > 0) { day += ((int)((year + 1) / 4)) - ((int)((year + 69) / 100)) + ((int)((year + 369) / 400)); } else { day += floor((year + 1) / 4) - floor((year + 69) / 100) + floor((year + 369) / 400); } return day; } ///------------------------------------------------------------------------------ /// Return whether the given year is a leap year. ///------------------------------------------------------------------------------ bool DateUtilities::FLeap(int year) { return (0 == (year & 3)) && (0 != (year % 100) || 0 == (year % 400)); } ///------------------------------------------------------------------------------ /// Get the first day of the year, and if the first day is bigger than the passed day, then get the first day of the previous year. ///------------------------------------------------------------------------------ /*static*/ int DateUtilities::GetDayMinAndUpdateYear(int day, int &year) { int dayMin = (int)DayFromYear(year); if (day < dayMin) { year--; dayMin = (int)DayFromYear(year); } return dayMin; } ///------------------------------------------------------------------------------ /// Converts the time value relative to Jan 1, 1970 into a YMD. /// /// The year number y and day number d relative to Jan 1, 1970 satisfy the /// inequalities: /// floor((400*d-82)/146097) <= y <= floor((400*d+398)/146097) /// These inequalities get us within one of the correct answer for the year. /// We then use DayFromYear to adjust if necessary. ///------------------------------------------------------------------------------ void DateUtilities::GetYmdFromTv(double tv, DateTime::YMD *pymd) { // AssertMem(pymd); int day; int dayMin; int yday; if (tv > 0) { day = (int)(tv / 86400000); pymd->time = (int)DblModPos(tv, 86400000); pymd->wday = (day + 4) % 7; pymd->year = 1970 + (int)((400 * (double)day + 398) / 146097); dayMin = GetDayMinAndUpdateYear(day, pymd->year); pymd->yt = (int)((dayMin + 4) % 7); } else { day = (int)floor(tv / 86400000); pymd->time = (int)DblModPos(tv, 86400000); pymd->wday = (int)DblModPos(day + 4, 7); pymd->year = 1970 + (int)floor(((400 * (double)day + 398) / 146097)); dayMin = GetDayMinAndUpdateYear(day, pymd->year); pymd->yt = (int)DblModPos(dayMin + 4, 7); } yday = (int)(day - dayMin); // Assert(yday >= 0 && (yday < 365 || yday == 365 && FLeap(pymd->year))); pymd->yday = yday; if (FLeap(pymd->year)) { pymd->yt += 7; } else if (yday >= 59) { yday++; } // Get the month. if (yday < 182) { if (yday < 60) { pymd->mon = 0 + ((yday >= 31) ? 1 : 0); } else if (yday < 121) { pymd->mon = 2 + ((yday >= 91) ? 1 : 0); } else { pymd->mon = 4 + ((yday >= 152) ? 1 : 0); } } else { if (yday < 244) { pymd->mon = 6 + ((yday >= 213) ? 1 : 0); } else if (yday < 305) { pymd->mon = 8 + ((yday >= 274) ? 1 : 0); } else { pymd->mon = 10 + ((yday >= 335) ? 1 : 0); } } // Assert(pymd->mon >= 0 && pymd->mon < 12); pymd->mday = yday - g_rgday[pymd->mon]; } void DateUtilities::GetYearFromTv(double tv, int &year, int &yearType) { // AssertMem(pymd); int day; int dayMin; if (tv > 0) { day = (int)(tv / 86400000); year = 1970 + (int)((400 * (double)day + 398) / 146097); dayMin = GetDayMinAndUpdateYear(day, year); yearType = (int)((dayMin + 4) % 7); } else { day = (int)floor(tv / 86400000); year = 1970 + (int)floor(((400 * (double)day + 398) / 146097)); dayMin = GetDayMinAndUpdateYear(day, year); yearType = (int)DblModPos(dayMin + 4, 7); } if (FLeap(year)) { yearType += 7; } } double DateUtilities::JsLocalTimeFromVarDate(double dbl) { // So that the arithmetic works even for negative dates, convert the // date to the _actual number of days_ since 0000h 12/30/1899. if (dbl < 0.0) dbl = 2.0 * ceil(dbl) - dbl; // Get the local time value. dbl = (dbl - g_kdblJanuary1st1970) * 86400000; if (NumberUtilities::IsNan(dbl)) { return dbl; } return NumberUtilities::IsFinite(dbl) ? floor( dbl + 0.5) : dbl; } }
Var SIMDUint8x16Lib::EntrySubSaturate(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); Assert(!(callInfo.Flags & CallFlags_New)); // If any of the args are missing, then it is Undefined type which causes TypeError exception. // strict type on both operands if (args.Info.Count >= 3 && JavascriptSIMDUint8x16::Is(args[1]) && JavascriptSIMDUint8x16::Is(args[2])) { JavascriptSIMDUint8x16 *a = JavascriptSIMDUint8x16::FromVar(args[1]); JavascriptSIMDUint8x16 *b = JavascriptSIMDUint8x16::FromVar(args[2]); Assert(a && b); SIMDValue result, aValue, bValue; aValue = a->GetValue(); bValue = b->GetValue(); result = SIMDUint8x16Operation::OpSubSaturate(aValue, bValue); return JavascriptSIMDUint8x16::New(&result, scriptContext); } JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdUint8x16TypeMismatch, _u("subSaturate")); }
Var SIMDUint8x16Lib::EntrySelect(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); Assert(!(callInfo.Flags & CallFlags_New)); if (args.Info.Count >= 4 && JavascriptSIMDBool8x16::Is(args[1]) && JavascriptSIMDUint8x16::Is(args[2]) && JavascriptSIMDUint8x16::Is(args[3])) { JavascriptSIMDBool8x16 *m = JavascriptSIMDBool8x16::FromVar(args[1]); JavascriptSIMDUint8x16 *t = JavascriptSIMDUint8x16::FromVar(args[2]); JavascriptSIMDUint8x16 *f = JavascriptSIMDUint8x16::FromVar(args[3]); Assert(m && t && f); SIMDValue result, maskValue, trueValue, falseValue; maskValue = m->GetValue(); trueValue = t->GetValue(); falseValue = f->GetValue(); result = SIMDInt32x4Operation::OpSelect(maskValue, trueValue, falseValue); return JavascriptSIMDUint8x16::New(&result, scriptContext); } JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdUint8x16TypeMismatch, _u("select")); }
Var SIMDUint8x16Lib::EntryShiftRightByScalar(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); Assert(!(callInfo.Flags & CallFlags_New)); if (args.Info.Count >= 3 && JavascriptSIMDUint8x16::Is(args[1])) { JavascriptSIMDUint8x16 *a = JavascriptSIMDUint8x16::FromVar(args[1]); Assert(a); SIMDValue result, aValue; aValue = a->GetValue(); Var countVar = args[2]; // {int} bits Bit count int8 count = JavascriptConversion::ToInt8(countVar, scriptContext); result = SIMDUint8x16Operation::OpShiftRightByScalar(aValue, count); return JavascriptSIMDUint8x16::New(&result, scriptContext); } JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdUint8x16TypeMismatch, _u("shiftRightByScalar")); }
Var SIMDUint8x16Lib::EntryReplaceLane(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); Assert(!(callInfo.Flags & CallFlags_New)); // first arg has to be of type Uint8x16, so cannot be missing. if (args.Info.Count >= 4 && JavascriptSIMDUint8x16::Is(args[1])) { // if value arg is missing, then it is undefined. Var laneVar = args.Info.Count >= 4 ? args[2] : scriptContext->GetLibrary()->GetUndefined(); Var argVal = args.Info.Count >= 4 ? args[3] : scriptContext->GetLibrary()->GetUndefined(); uint8 value = JavascriptConversion::ToInt8(argVal, scriptContext); SIMDValue result = SIMD128ReplaceLane<JavascriptSIMDUint8x16, 16, uint8>(args[1], laneVar, value, scriptContext); return JavascriptSIMDUint8x16::New(&result, scriptContext); } JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdUint8x16TypeMismatch, _u("ReplaceLane")); }
Var SIMDUint8x16Lib::EntryCheck(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); Assert(!(callInfo.Flags & CallFlags_New)); if (args.Info.Count >= 2 && JavascriptSIMDUint8x16::Is(args[1])) { return args[1]; } JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdUint8x16TypeMismatch, _u("Uint8x16")); }
Var SIMDUint8x16Lib::EntryNot(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); Assert(!(callInfo.Flags & CallFlags_New)); if (args.Info.Count >= 2 && JavascriptSIMDUint8x16::Is(args[1])) { JavascriptSIMDUint8x16 *a = JavascriptSIMDUint8x16::FromVar(args[1]); Assert(a); SIMDValue value, result; value = a->GetValue(); result = SIMDInt32x4Operation::OpNot(value); return JavascriptSIMDUint8x16::New(&result, scriptContext); } JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdUint8x16TypeMismatch, _u("not")); }
//Lane Access Var SIMDBool8x16Lib::EntryExtractLane(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); Assert(!(callInfo.Flags & CallFlags_New)); if (args.Info.Count >= 3 && JavascriptSIMDBool8x16::Is(args[1])) { // if value arg is missing, then it is undefined. Var laneVar = args.Info.Count >= 3 ? args[2] : scriptContext->GetLibrary()->GetUndefined(); bool result = (SIMDUtils::SIMD128ExtractLane<JavascriptSIMDBool8x16, 16, int8>(args[1], laneVar, scriptContext)) ? true : false; return JavascriptBoolean::ToVar(result, scriptContext); } JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdBool8x16TypeMismatch, _u("ExtractLane")); }
void ConfigFlagsTable::PrintUsageString() { Output::Print(_u("List of Phases:\n")); for(int i = 0; i < PhaseCount; i++) { if (i % 4 == 0) { Output::Print(_u("\n ")); } Output::Print(_u("%-40ls "), PhaseNames[i]); } Output::Print(_u("\n\nList of flags:\n\n")); for(int i = 0; i < FlagCount; i++) { Output::Print(_u("%60ls "), FlagNames[i]); switch(GetFlagType(Flag(i))) { case InvalidFlagType: break; case FlagString: Output::Print(_u("[:String] ")); break; case FlagPhases: Output::Print(_u("[:Phase] ")); break; case FlagNumber: Output::Print(_u("[:Number] ")); break; case FlagBoolean: Output::Print(_u(" ")); break; case FlagNumberSet: Output::Print(_u("[:NumberSet] ")); break; case FlagNumberPairSet: Output::Print(_u("[:NumberPairSet] ")); break; case FlagNumberTrioSet: Output::Print(_u("[:NumberTrioSet] ")); break; case FlagNumberRange: Output::Print(_u("[:NumberRange] ")); break; default: Assert(false); __assume(false); } Output::Print(_u("%ls\n"), FlagDescriptions[i]); } }
Var SIMDUint8x16Lib::EntryFromInt32x4Bits(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); Assert(!(callInfo.Flags & CallFlags_New)); if (args.Info.Count >= 2 && JavascriptSIMDInt32x4::Is(args[1])) { JavascriptSIMDInt32x4 *instance = JavascriptSIMDInt32x4::FromVar(args[1]); Assert(instance); return SIMDConvertTypeFromBits<JavascriptSIMDInt32x4, JavascriptSIMDUint8x16>(instance, scriptContext); } JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdUint8x16TypeMismatch, _u("fromInt32x4Bits")); }
template<> void PrintTmpRegisterAllocation<int>(RegSlot loc) { if (PHASE_ON1(AsmjsTmpRegisterAllocationPhase)) Output::Print(_u("+I%d\n"), loc); }
LPVOID PreReservedVirtualAllocWrapper::Alloc(LPVOID lpAddress, size_t dwSize, DWORD allocationType, DWORD protectFlags, bool isCustomHeapAllocation) { Assert(this); AssertMsg(isCustomHeapAllocation, "PreReservation used for allocations other than CustomHeap?"); AssertMsg(AutoSystemInfo::Data.IsCFGEnabled() || PHASE_FORCE1(Js::PreReservedHeapAllocPhase), "PreReservation without CFG ?"); Assert(dwSize != 0); { AutoCriticalSection autocs(&this->cs); //Return nullptr, if no space to Reserve if (EnsurePreReservedRegionInternal() == nullptr) { PreReservedHeapTrace(_u("No space to pre-reserve memory with %d pages. Returning NULL\n"), PreReservedAllocationSegmentCount * AutoSystemInfo::Data.GetAllocationGranularityPageCount()); return nullptr; } char * addressToReserve = nullptr; uint freeSegmentsBVIndex = BVInvalidIndex; size_t requestedNumOfSegments = dwSize / (AutoSystemInfo::Data.GetAllocationGranularityPageSize()); Assert(requestedNumOfSegments <= MAXUINT32); if (lpAddress == nullptr) { Assert(requestedNumOfSegments != 0); AssertMsg(dwSize % AutoSystemInfo::Data.GetAllocationGranularityPageSize() == 0, "dwSize should be aligned with Allocation Granularity"); do { freeSegmentsBVIndex = freeSegments.GetNextBit(freeSegmentsBVIndex + 1); //Return nullptr, if we don't have free/decommit pages to allocate if ((freeSegments.Length() - freeSegmentsBVIndex < requestedNumOfSegments) || freeSegmentsBVIndex == BVInvalidIndex) { PreReservedHeapTrace(_u("No more space to commit in PreReserved Memory region.\n")); return nullptr; } } while (!freeSegments.TestRange(freeSegmentsBVIndex, static_cast<uint>(requestedNumOfSegments))); uint offset = freeSegmentsBVIndex * AutoSystemInfo::Data.GetAllocationGranularityPageSize(); addressToReserve = (char*) preReservedStartAddress + offset; //Check if the region is not already in MEM_COMMIT state. MEMORY_BASIC_INFORMATION memBasicInfo; size_t bytes = VirtualQuery(addressToReserve, &memBasicInfo, sizeof(memBasicInfo)); if (bytes == 0 || memBasicInfo.RegionSize < requestedNumOfSegments * AutoSystemInfo::Data.GetAllocationGranularityPageSize() || memBasicInfo.State == MEM_COMMIT ) { CustomHeap_BadPageState_fatal_error((ULONG_PTR)this); return nullptr; } } else { //Check If the lpAddress is within the range of the preReserved Memory Region Assert(((char*) lpAddress) >= (char*) preReservedStartAddress || ((char*) lpAddress + dwSize) < GetPreReservedEndAddress()); addressToReserve = (char*) lpAddress; freeSegmentsBVIndex = (uint) ((addressToReserve - (char*) preReservedStartAddress) / AutoSystemInfo::Data.GetAllocationGranularityPageSize()); #if DBG uint numOfSegments = (uint)ceil((double)dwSize / (double)AutoSystemInfo::Data.GetAllocationGranularityPageSize()); Assert(numOfSegments != 0); Assert(freeSegmentsBVIndex + numOfSegments - 1 < freeSegments.Length()); Assert(!freeSegments.TestRange(freeSegmentsBVIndex, numOfSegments)); #endif } AssertMsg(freeSegmentsBVIndex < PreReservedAllocationSegmentCount, "Invalid BitVector index calculation?"); AssertMsg(dwSize % AutoSystemInfo::PageSize == 0, "COMMIT is managed at AutoSystemInfo::PageSize granularity"); char * allocatedAddress = nullptr; if ((allocationType & MEM_COMMIT) != 0) { #if defined(ENABLE_JIT_CLAMP) AutoEnableDynamicCodeGen enableCodeGen; #endif #if defined(_CONTROL_FLOW_GUARD) if (AutoSystemInfo::Data.IsCFGEnabled()) { DWORD oldProtect = 0; DWORD allocProtectFlags = 0; if (AutoSystemInfo::Data.IsCFGEnabled()) { allocProtectFlags = PAGE_EXECUTE_RW_TARGETS_INVALID; } else { allocProtectFlags = PAGE_EXECUTE_READWRITE; } allocatedAddress = (char *)VirtualAlloc(addressToReserve, dwSize, MEM_COMMIT, allocProtectFlags); if (allocatedAddress != nullptr) { VirtualProtect(allocatedAddress, dwSize, protectFlags, &oldProtect); AssertMsg(oldProtect == (PAGE_EXECUTE_READWRITE), "CFG Bitmap gets allocated and bits will be set to invalid only upon passing these flags."); } } else #endif { allocatedAddress = (char *)VirtualAlloc(addressToReserve, dwSize, MEM_COMMIT, protectFlags); } } else { // Just return the uncommitted address if we didn't ask to commit it. allocatedAddress = addressToReserve; } // Keep track of the committed pages within the preReserved Memory Region if (lpAddress == nullptr && allocatedAddress != nullptr) { Assert(allocatedAddress == addressToReserve); Assert(requestedNumOfSegments != 0); freeSegments.ClearRange(freeSegmentsBVIndex, static_cast<uint>(requestedNumOfSegments)); } PreReservedHeapTrace(_u("MEM_COMMIT: StartAddress: 0x%p of size: 0x%x * 0x%x bytes \n"), allocatedAddress, requestedNumOfSegments, AutoSystemInfo::Data.GetAllocationGranularityPageSize()); return allocatedAddress; } }
const char16 * AsmJsType::toChars() const { switch (which_) { case Double: return _u("double"); case MaybeDouble: return _u("double?"); case DoubleLit: return _u("doublelit"); case Float: return _u("float"); case Floatish: return _u("floatish"); case FloatishDoubleLit: return _u("FloatishDoubleLit"); case MaybeFloat: return _u("float?"); case Fixnum: return _u("fixnum"); case Int: return _u("int"); case Signed: return _u("signed"); case Unsigned: return _u("unsigned"); case Intish: return _u("intish"); case Void: return _u("void"); case Int32x4: return _u("SIMD.Int32x4"); case Bool32x4: return _u("SIMD.Bool32x4"); case Bool16x8: return _u("SIMD.Bool16x8"); case Bool8x16: return _u("SIMD.Bool8x16"); case Float32x4: return _u("SIMD.Float32x4"); case Float64x2: return _u("SIMD.Float64x2"); case Int16x8: return _u("SIMD.Int16x8"); case Int8x16: return _u("SIMD.Int8x16"); case Uint32x4: return _u("SIMD.Uint32x4"); case Uint16x8: return _u("SIMD.Uint16x8"); case Uint8x16: return _u("SIMD.Uint8x16"); } Assert(false); return _u("none"); }
AutoEnableDynamicCodeGen::AutoEnableDynamicCodeGen(bool enable) : enabled(false) { if (enable == false) { return; } // // Snap the dynamic code generation policy for this process so that we // don't need to resolve APIs and query it each time. We expect the policy // to have been established upfront. // if (processPolicyObtained == false) { AutoCriticalSection autocs(&processPolicyCS); if (processPolicyObtained == false) { PGET_PROCESS_MITIGATION_POLICY_PROC GetProcessMitigationPolicyProc = nullptr; HMODULE module = GetModuleHandleW(_u("api-ms-win-core-processthreads-l1-1-3.dll")); if (module != nullptr) { GetProcessMitigationPolicyProc = (PGET_PROCESS_MITIGATION_POLICY_PROC) GetProcAddress(module, "GetProcessMitigationPolicy"); SetThreadInformationProc = (PSET_THREAD_INFORMATION_PROC) GetProcAddress(module, "SetThreadInformation"); GetThreadInformationProc = (PGET_THREAD_INFORMATION_PROC) GetProcAddress(module, "GetThreadInformation"); } if ((GetProcessMitigationPolicyProc == nullptr) || (!GetProcessMitigationPolicyProc(GetCurrentProcess(), ProcessDynamicCodePolicy, (PPROCESS_MITIGATION_DYNAMIC_CODE_POLICY) &processPolicy, sizeof(processPolicy)))) { processPolicy.ProhibitDynamicCode = 0; } processPolicyObtained = true; } } // // The process is not prohibiting dynamic code or does not allow threads // to opt out. In either case, return to the caller. // // N.B. It is OK that this policy is mutable at runtime. If a process // really does not allow thread opt-out, then the call below will fail // benignly. // if ((processPolicy.ProhibitDynamicCode == 0) || (processPolicy.AllowThreadOptOut == 0)) { return; } if (SetThreadInformationProc == nullptr || GetThreadInformationProc == nullptr) { return; } // // If dynamic code is already allowed for this thread, then don't attempt to allow it again. // DWORD threadPolicy; if ((GetThreadInformationProc(GetCurrentThread(), ThreadDynamicCodePolicy, &threadPolicy, sizeof(DWORD))) && (threadPolicy == THREAD_DYNAMIC_CODE_ALLOW)) { return; } threadPolicy = THREAD_DYNAMIC_CODE_ALLOW; BOOL result = SetThreadInformationProc(GetCurrentThread(), ThreadDynamicCodePolicy, &threadPolicy, sizeof(DWORD)); Assert(result); enabled = true; }
const char16 *GetVtableName(VTableValue value) { switch (value) { #if !defined(_M_X64) case VtableJavascriptNumber: return _u("vtable JavascriptNumber"); break; #endif case VtableDynamicObject: return _u("vtable DynamicObject"); break; case VtableInvalid: return _u("vtable Invalid"); break; case VtablePropertyString: return _u("vtable PropertyString"); break; case VtableJavascriptBoolean: return _u("vtable JavascriptBoolean"); break; case VtableJavascriptArray: return _u("vtable JavascriptArray"); break; case VtableInt8Array: return _u("vtable Int8Array"); break; case VtableUint8Array: return _u("vtable Uint8Array"); break; case VtableUint8ClampedArray: return _u("vtable Uint8ClampedArray"); break; case VtableInt16Array: return _u("vtable Int16Array"); break; case VtableUint16Array: return _u("vtable Uint16Array"); break; case VtableInt32Array: return _u("vtable Int32Array"); break; case VtableUint32Array: return _u("vtable Uint32Array"); break; case VtableFloat32Array: return _u("vtable Float32Array"); break; case VtableFloat64Array: return _u("vtable Float64Array"); break; case VtableJavascriptPixelArray: return _u("vtable JavascriptPixelArray"); break; case VtableInt64Array: return _u("vtable Int64Array"); break; case VtableUint64Array: return _u("vtable Uint64Array"); break; case VtableInt8VirtualArray: return _u("vtable Int8VirtualArray"); break; case VtableUint8VirtualArray: return _u("vtable Uint8VirtualArray"); break; case VtableUint8ClampedVirtualArray: return _u("vtable Uint8ClampedVirtualArray"); break; case VtableInt16VirtualArray: return _u("vtable Int16VirtualArray"); break; case VtableUint16VirtualArray: return _u("vtable Uint16VirtualArray"); break; case VtableInt32VirtualArray: return _u("vtable Int32VirtualArray"); break; case VtableUint32VirtualArray: return _u("vtable Uint32VirtualArray"); break; case VtableFloat32VirtualArray: return _u("vtable Float32VirtualArray"); break; case VtableFloat64VirtualArray: return _u("vtable Float64VirtualArray"); break; case VtableBoolArray: return _u("vtable BoolArray"); break; case VtableCharArray: return _u("vtable CharArray"); break; case VtableNativeIntArray: return _u("vtable NativeIntArray"); break; case VtableNativeFloatArray: return _u("vtable NativeFloatArray"); break; case VtableJavascriptNativeIntArray: return _u("vtable JavascriptNativeIntArray"); break; case VtableJavascriptRegExp: return _u("vtable JavascriptRegExp"); break; case VtableStackScriptFunction: return _u("vtable StackScriptFunction"); break; case VtableConcatStringMulti: return _u("vtable ConcatStringMulti"); break; case VtableCompoundString: return _u("vtable CompoundString"); break; default: Assert(false); break; } return _u("vtable unknown"); }
void * UnboxAsmJsArguments(ScriptFunction* func, Var * origArgs, char * argDst, CallInfo callInfo) { void * address = reinterpret_cast<void*>(func->GetEntryPointInfo()->jsMethod); Assert(address); AsmJsFunctionInfo* info = func->GetFunctionBody()->GetAsmJsFunctionInfo(); ScriptContext* scriptContext = func->GetScriptContext(); #if ENABLE_DEBUG_CONFIG_OPTIONS bool allowTestInputs = CONFIG_FLAG(WasmI64); #endif ArgumentReader reader(&callInfo, origArgs); uint actualArgCount = reader.Info.Count - 1; // -1 for ScriptFunction argDst = argDst + MachPtr; // add one first so as to skip the ScriptFunction argument for (ArgSlot i = 0; i < info->GetArgCount(); i++) { if (info->GetArgType(i).isInt()) { int32 intVal; if (i < actualArgCount) { #if ENABLE_DEBUG_CONFIG_OPTIONS if (allowTestInputs && JavascriptString::Is(*origArgs)) { intVal = (int32)ConvertStringToInt64(*origArgs, scriptContext); } else #endif intVal = JavascriptMath::ToInt32(*origArgs, scriptContext); } else { intVal = 0; } #if TARGET_64 *(int64*)(argDst) = 0; #endif *(int32*)argDst = intVal; argDst = argDst + MachPtr; } else if (info->GetArgType(i).isInt64()) { #if ENABLE_DEBUG_CONFIG_OPTIONS if (!allowTestInputs) #endif { JavascriptError::ThrowTypeError(scriptContext, WASMERR_InvalidTypeConversion); } #if ENABLE_DEBUG_CONFIG_OPTIONS int64 val; if (i < actualArgCount) { if (JavascriptString::Is(*origArgs)) { val = ConvertStringToInt64(*origArgs, scriptContext); } else if (JavascriptObject::Is(*origArgs)) { RecyclableObject* object = RecyclableObject::FromVar(*origArgs); PropertyRecord const * lowPropRecord = nullptr; PropertyRecord const * highPropRecord = nullptr; scriptContext->GetOrAddPropertyRecord(_u("low"), (int)wcslen(_u("low")), &lowPropRecord); scriptContext->GetOrAddPropertyRecord(_u("high"), (int)wcslen(_u("high")), &highPropRecord); Var low = JavascriptOperators::OP_GetProperty(object, lowPropRecord->GetPropertyId(), scriptContext); Var high = JavascriptOperators::OP_GetProperty(object, highPropRecord->GetPropertyId(), scriptContext); uint64 lowVal = JavascriptMath::ToInt32(low, scriptContext); uint64 highVal = JavascriptMath::ToInt32(high, scriptContext); val = (highVal << 32) | (lowVal & 0xFFFFFFFF); } else { int32 intVal = JavascriptMath::ToInt32(*origArgs, scriptContext); val = (int64)intVal; } } else { val = 0; } *(int64*)(argDst) = val; argDst += sizeof(int64); #endif } else if (info->GetArgType(i).isFloat()) { float floatVal; if (i < actualArgCount) { #if ENABLE_DEBUG_CONFIG_OPTIONS if (allowTestInputs && JavascriptString::Is(*origArgs)) { int32 val = (int32)ConvertStringToInt64(*origArgs, scriptContext); floatVal = *(float*)&val; } else #endif floatVal = (float)(JavascriptConversion::ToNumber(*origArgs, scriptContext)); } else { floatVal = (float)(JavascriptNumber::NaN); } #if TARGET_64 *(int64*)(argDst) = 0; #endif *(float*)argDst = floatVal; argDst = argDst + MachPtr; } else if (info->GetArgType(i).isDouble()) { double doubleVal; if (i < actualArgCount) { #if ENABLE_DEBUG_CONFIG_OPTIONS if (allowTestInputs && JavascriptString::Is(*origArgs)) { int64 val = ConvertStringToInt64(*origArgs, scriptContext); doubleVal = *(double*)&val; } else #endif doubleVal = JavascriptConversion::ToNumber(*origArgs, scriptContext); } else { doubleVal = JavascriptNumber::NaN; } *(double*)argDst = doubleVal; argDst = argDst + sizeof(double); } else if (info->GetArgType(i).isSIMD()) { // Todo:: support test input for wasm.simd JavascriptError::ThrowTypeError(scriptContext, WASMERR_InvalidTypeConversion); } else { Assert(UNREACHED); JavascriptError::ThrowTypeError(scriptContext, WASMERR_InvalidTypeConversion); } ++origArgs; } AsmJsModuleInfo::EnsureHeapAttached(func); // for convenience, lets take the opportunity to return the asm.js entrypoint address return address; }
// This only enables collection of the inlinee data, we are much more aggressive here. // Actual decision of whether something is inlined or not is taken in CommitInlineIntoInliner bool InliningDecider::DeciderInlineIntoInliner(Js::FunctionBody * inlinee, Js::FunctionBody * inliner, bool isConstructorCall, bool isPolymorphicCall, uint16 constantArgInfo, uint recursiveInlineDepth, bool allowRecursiveInlining) { if (!CanRecursivelyInline(inlinee, inliner, allowRecursiveInlining, recursiveInlineDepth)) { return false; } if (inliner->GetIsAsmjsMode() != inlinee->GetIsAsmjsMode()) { return false; } // Force inline all Js Builtins functions // The existing JsBuiltInForceInline flag can work only when we explictly create scriptFunction // We can also have methods that we define on the prototype like next of ArrayIterator for which we don't explictly create a script function // TODO (megupta) : use forceInline for methods defined on the prototype using ObjectDefineProperty if (inlinee->GetSourceContextId() == Js::Constants::JsBuiltInSourceContextId || PHASE_FORCE(Js::InlinePhase, this->topFunc) || PHASE_FORCE(Js::InlinePhase, inliner) || PHASE_FORCE(Js::InlinePhase, inlinee)) { return true; } if (PHASE_OFF(Js::InlinePhase, this->topFunc) || PHASE_OFF(Js::InlinePhase, inliner) || PHASE_OFF(Js::InlinePhase, inlinee)) { return false; } if (PHASE_FORCE(Js::InlineTreePhase, this->topFunc) || PHASE_FORCE(Js::InlineTreePhase, inliner)) { return true; } if (PHASE_FORCE(Js::InlineAtEveryCallerPhase, inlinee)) { return true; } uint inlineeByteCodeCount = inlinee->GetByteCodeWithoutLDACount(); // Heuristics are hit in the following order (Note *order* is important) // 1. Leaf function: If the inlinee is a leaf (but not a constructor or a polymorphic call) inline threshold is LeafInlineThreshold (60). Also it can have max 1 loop // 2. Constant Function Argument: If the inlinee candidate has a constant argument and that argument is used for branching, then the inline threshold is ConstantArgumentInlineThreshold (157) // 3. InlineThreshold: If an inlinee candidate exceeds InlineThreshold just don't inline no matter what. // Following are additional constraint for an inlinee which meets InlineThreshold (Rule no 3) // 4. Rule for inlinee with loops: // 4a. Only single loop in inlinee is permitted. // 4b. Should not have polymorphic field access. // 4c. Should not be a constructor. // 4d. Should meet LoopInlineThreshold (25) // 5. Rule for polymorphic inlinee: // 4a. Should meet PolymorphicInlineThreshold (32) // 6. Rule for constructors: // 5a. Always inline if inlinee has polymorphic field access (as we have cloned runtime data). // 5b. If inlinee is monomorphic, inline only small constructors. They are governed by ConstructorInlineThreshold (21) // 7. Rule for inlinee which is not interpreted enough (as we might not have all the profile data): // 7a. As of now it is still governed by the InlineThreshold. Plan to play with this in future. // 8. Rest should be inlined. uint16 mask = constantArgInfo & inlinee->m_argUsedForBranch; if (mask && inlineeByteCodeCount < (uint)CONFIG_FLAG(ConstantArgumentInlineThreshold)) { return true; } int inlineThreshold = threshold.inlineThreshold; if (!isPolymorphicCall && !isConstructorCall && IsInlineeLeaf(inlinee) && (inlinee->GetLoopCount() <= 2)) { // Inlinee is a leaf function if (inlinee->GetLoopCount() == 0 || GetNumberOfInlineesWithLoop() <= (uint)threshold.maxNumberOfInlineesWithLoop) // Don't inlinee too many inlinees with loops. { // Negative LeafInlineThreshold disable the threshold if (threshold.leafInlineThreshold >= 0) { inlineThreshold += threshold.leafInlineThreshold - threshold.inlineThreshold; } } } #if ENABLE_DEBUG_CONFIG_OPTIONS char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE]; char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE]; char16 debugStringBuffer3[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE]; #endif if (inlinee->GetHasLoops()) { if (threshold.loopInlineThreshold < 0 || // Negative LoopInlineThreshold disable inlining with loop GetNumberOfInlineesWithLoop() >(uint)threshold.maxNumberOfInlineesWithLoop || // See if we are inlining too many inlinees with loops. (inlinee->GetLoopCount() > 2) || // Allow at most 2 loops. inlinee->GetHasNestedLoop() || // Nested loops are not a good inlinee candidate isConstructorCall || // If the function is constructor with loops, don't inline. PHASE_OFF(Js::InlineFunctionsWithLoopsPhase, this->topFunc)) { INLINE_TESTTRACE(_u("INLINING: Skip Inline: Has loops \tBytecode size: %d \tgetNumberOfInlineesWithLoop: %d\tloopCount: %d\thasNestedLoop: %d\tisConstructorCall:%d\tInlinee: %s (%s)\tCaller: %s (%s) \tRoot: %s (%s)\n"), inlinee->GetByteCodeCount(), GetNumberOfInlineesWithLoop(), inlinee->GetLoopCount(), inlinee->GetHasNestedLoop(), isConstructorCall, inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2), topFunc->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3)); // Don't inline function with loops return false; } else { inlineThreshold -= (threshold.inlineThreshold > threshold.loopInlineThreshold) ? threshold.inlineThreshold - threshold.loopInlineThreshold : 0; } } if (isPolymorphicCall) { if (threshold.polymorphicInlineThreshold < 0 || // Negative PolymorphicInlineThreshold disable inlining isConstructorCall) { INLINE_TESTTRACE(_u("INLINING: Skip Inline: Polymorphic call under PolymorphicInlineThreshold: %d \tBytecode size: %d\tInlinee: %s (%s)\tCaller: %s (%s) \tRoot: %s (%s)\n"), threshold.polymorphicInlineThreshold, inlinee->GetByteCodeCount(), inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2), topFunc->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3)); return false; } else { inlineThreshold -= (threshold.inlineThreshold > threshold.polymorphicInlineThreshold) ? threshold.inlineThreshold - threshold.polymorphicInlineThreshold : 0; } } if (isConstructorCall) { #pragma prefast(suppress: 6285, "logical-or of constants is by design") if (PHASE_OFF(Js::InlineConstructorsPhase, this->topFunc) || PHASE_OFF(Js::InlineConstructorsPhase, inliner) || PHASE_OFF(Js::InlineConstructorsPhase, inlinee) || !CONFIG_FLAG(CloneInlinedPolymorphicCaches)) { return false; } if (PHASE_FORCE(Js::InlineConstructorsPhase, this->topFunc) || PHASE_FORCE(Js::InlineConstructorsPhase, inliner) || PHASE_FORCE(Js::InlineConstructorsPhase, inlinee)) { return true; } if (inlinee->HasDynamicProfileInfo() && inlinee->GetAnyDynamicProfileInfo()->HasPolymorphicFldAccess()) { // As of now this is not dependent on bytecodeInlinedThreshold. return true; } // Negative ConstructorInlineThreshold always disable constructor inlining if (threshold.constructorInlineThreshold < 0) { INLINE_TESTTRACE(_u("INLINING: Skip Inline: Constructor with no polymorphic field access \tBytecode size: %d\tInlinee: %s (%s)\tCaller: %s (%s) \tRoot: %s (%s)\n"), inlinee->GetByteCodeCount(), inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2), topFunc->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3)); // Don't inline constructor that does not have a polymorphic field access, or if cloning polymorphic inline // caches is disabled return false; } else { inlineThreshold -= (threshold.inlineThreshold > threshold.constructorInlineThreshold) ? threshold.inlineThreshold - threshold.constructorInlineThreshold : 0; } } if (threshold.forLoopBody) { inlineThreshold /= CONFIG_FLAG(InlineInLoopBodyScaleDownFactor); } if (inlineThreshold > 0 && inlineeByteCodeCount <= (uint)inlineThreshold) { if (inlinee->GetLoopCount()) { IncrementNumberOfInlineesWithLoop(); } return true; } else { INLINE_TESTTRACE(_u("INLINING: Skip Inline: Too long \tBytecode size: %d\tThreshold: %d\tInlinee: %s (%s)\tCaller: %s (%s) \tRoot: %s (%s)\n"), inlinee->GetByteCodeCount(), inlineThreshold, inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2), topFunc->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3)); return false; } }
Js::Var JSONParser::Parse(LPCWSTR str, int length) { if (length > MIN_CACHE_LENGTH) { if (!this->arenaAllocatorObject) { this->arenaAllocatorObject = scriptContext->GetTemporaryGuestAllocator(_u("JSONParse")); this->arenaAllocator = arenaAllocatorObject->GetAllocator(); } } m_scanner.Init(str, length, &m_token, scriptContext, str, this->arenaAllocator); Scan(); Js::Var ret = ParseObject(); if (m_token.tk != tkEOF) { m_scanner.ThrowSyntaxError(JSERR_JsonSyntax); } return ret; }
namespace Js { DEFINE_RECYCLER_TRACKER_WEAKREF_PERF_COUNTER(Type); InternalString Type::ObjectTypeNameString = InternalString(_u("object"), 6); InternalString Type::UndefinedTypeNameString = InternalString(_u("undefined"), 9); InternalString Type::BooleanTypeNameString = InternalString(_u("boolean"), 7); InternalString Type::StringTypeNameString = InternalString(_u("string"), 6); InternalString Type::NumberTypeNameString = InternalString(_u("number"), 6); InternalString Type::FunctionTypeNameString = InternalString(_u("function"), 8); Type::Type(ScriptContext* scriptContext, TypeId typeId, RecyclableObject* prototype, JavascriptMethod entryPoint) : javascriptLibrary(scriptContext->GetLibrary()), typeId(typeId), prototype(prototype), propertyCache(nullptr), flags(TypeFlagMask_None) { #ifdef PROFILE_TYPES if (typeId < sizeof(scriptContext->typeCount)/sizeof(int)) { scriptContext->typeCount[typeId]++; } #endif this->entryPoint = entryPoint != nullptr ? entryPoint : RecyclableObject::DefaultEntryPoint; if (prototype) { Assert(! CrossSite::NeedMarshalVar(prototype,scriptContext)); prototype->SetIsPrototype(); } } Type::Type(Type * type) : typeId(type->typeId), javascriptLibrary(type->javascriptLibrary), prototype(type->prototype), entryPoint(type->entryPoint), flags(type->flags), propertyCache(nullptr) { #ifdef PROFILE_TYPES if (typeId < sizeof(javascriptLibrary->GetScriptContext()->typeCount)/sizeof(int)) { javascriptLibrary->GetScriptContext()->typeCount[typeId]++; } #endif Assert(! (prototype && CrossSite::NeedMarshalVar(prototype, javascriptLibrary->GetScriptContext()))); // If the type property cache is copied over to this new type, then if a property ID caused the type to be changed for // the purpose of invalidating caches due to the property being deleted or its attributes being changed, then the cache // for that property ID must be cleared on this new type after the type property cache is copied. Also, types are not // changed consistently to use this copy constructor, so those would need to be fixed as well. if(type->AreThisAndPrototypesEnsuredToHaveOnlyWritableDataProperties()) { SetAreThisAndPrototypesEnsuredToHaveOnlyWritableDataProperties(true); } if(type->IsFalsy()) { SetIsFalsy(true); } } ScriptContext * Type::GetScriptContext() const { return GetLibrary()->GetScriptContext(); } Recycler * Type::GetRecycler() const { return GetLibrary()->GetRecycler(); } TypePropertyCache *Type::GetPropertyCache() { return propertyCache; } TypePropertyCache *Type::CreatePropertyCache() { Assert(!propertyCache); propertyCache = RecyclerNew(GetRecycler(), TypePropertyCache); return propertyCache; } void Type::SetAreThisAndPrototypesEnsuredToHaveOnlyWritableDataProperties(const bool truth) { if (truth) { if (GetScriptContext()->IsClosed()) { // The cache is disabled after the script context is closed, to avoid issues between being closed and being deleted, // where the cache of these types in JavascriptLibrary may be reclaimed at any point return; } flags |= TypeFlagMask_AreThisAndPrototypesEnsuredToHaveOnlyWritableDataProperties; javascriptLibrary->TypeAndPrototypesAreEnsuredToHaveOnlyWritableDataProperties(this); } else { flags &= ~TypeFlagMask_AreThisAndPrototypesEnsuredToHaveOnlyWritableDataProperties; } } BOOL Type::AreThisAndPrototypesEnsuredToHaveOnlyWritableDataProperties() const { return flags & TypeFlagMask_AreThisAndPrototypesEnsuredToHaveOnlyWritableDataProperties; } BOOL Type::IsFalsy() const { return flags & TypeFlagMask_IsFalsy; } void Type::SetIsFalsy(const bool truth) { if (truth) { Assert(this->GetScriptContext()->GetThreadContext()->CanBeFalsy(this->GetTypeId())); flags |= TypeFlagMask_IsFalsy; } else { flags &= ~TypeFlagMask_IsFalsy; } } void Type::SetHasSpecialPrototype(const bool truth) { if (truth) { flags |= TypeFlagMask_HasSpecialPrototype; } else { flags &= ~TypeFlagMask_HasSpecialPrototype; } } uint32 Type::GetOffsetOfTypeId() { return offsetof(Type, typeId); } uint32 Type::GetOffsetOfFlags() { return offsetof(Type, flags); } uint32 Type::GetOffsetOfEntryPoint() { return offsetof(Type, entryPoint); } uint32 Type::GetOffsetOfPrototype() { return offsetof(Type, prototype); } #if defined(PROFILE_RECYCLER_ALLOC) && defined(RECYCLER_DUMP_OBJECT_GRAPH) bool Type::DumpObjectFunction(type_info const * typeinfo, bool isArray, void * objectAddress) { if (isArray) { // Don't deal with array return false; } Output::Print(_u("%S{%x} %p"), typeinfo->name(), ((Type *)objectAddress)->GetTypeId(), objectAddress); return true; } #endif }
///---------------------------------------------------------------------------- /// /// ConfigFlagsTable::SetAsBoolean /// /// Set the value of a boolean flag. If the flag is a parent flag, all children flag // will be set accordingly. /// ///---------------------------------------------------------------------------- void ConfigFlagsTable::SetAsBoolean(Flag flag, Boolean value) { AssertMsg(this->GetFlagType(flag) == FlagBoolean, "flag not a boolean type"); Boolean* settingAsBoolean = this->GetAsBoolean(flag); Assert(settingAsBoolean != nullptr); Output::VerboseNote(_u("FLAG %s = %d\n"), FlagNames[(int) flag], value); *settingAsBoolean = value; // check if parent flag if (this->IsParentFlag(flag)) { // parent flag, will iterate through all child flags Flag childFlag = GetNextChildFlag(flag, /* no currentChildFlag */ InvalidFlag); while (childFlag != InvalidFlag) { Boolean childDefaultValue = GetDefaultValueAsBoolean(childFlag); // if the parent flag is TRUE, the children flag values are based on their default values // if the parent flag is FALSE, the children flag values are FALSE (always - as disabled) Boolean childValue = value == TRUE ? childDefaultValue : FALSE; Output::VerboseNote(_u("FLAG %s = %d - setting child flag %s = %d\n"), FlagNames[(int) flag], value, FlagNames[(int) childFlag], childValue); this->SetAsBoolean(childFlag, childValue); // get next child flag childFlag = GetNextChildFlag(flag, /* currentChildFlag */ childFlag); } } #ifdef ENABLE_DEBUG_CONFIG_OPTIONS // in case the flag is marked as 'callback' - to call the method #define FLAG(type, name, description, defaultValue, parentName, hasCallback) FLAGCALLBACK##hasCallback(type, name) #define FLAGCALLBACKFALSE(type, name) #define FLAGCALLBACKTRUE(type, name) FLAGDOCALLBACK##type(name) // define an overload for each FlagTypes - type // * all defaults we don't care about - should assert #define FLAGDOCALLBACKNumberRange(name) Assert(false); #define FLAGDOCALLBACKPhases(name) Assert(false); #define FLAGDOCALLBACKString(name) Assert(false); #define FLAGDOCALLBACKNumber(name) Assert(false); #define FLAGDOCALLBACKNumberSet(name) Assert(false); #define FLAGDOCALLBACKNumberPairSet(name) Assert(false); #define FLAGDOCALLBACKNumberTrioSet(name) Assert(false); // * and those we do care about #define FLAGDOCALLBACKBoolean(name) if( flag == name##Flag ) this->FlagSetCallback_##name(value); #include "ConfigFlagsList.h" #undef FLAGDOCALLBACKBoolean #undef FLAGDOCALLBACKNumberRange #undef FLAGDOCALLBACKNumberPairSet #undef FLAGDOCALLBACKNumberTrioSet #undef FLAGDOCALLBACKNumberSet #undef FLAGDOCALLBACKNumber #undef FLAGDOCALLBACKString #undef FLAGDOCALLBACKPhases #undef FLAGCALLBACKTRUE #undef FLAGCALLBACKFALSE #undef FLAG #endif }
void BackgroundJobProcessor::InitializeParallelThreadData(AllocationPolicyManager* policyManager, bool disableParallelThreads) { if (!disableParallelThreads) { InitializeThreadCount(); } else { this->maxThreadCount = 1; } Assert(this->maxThreadCount >= 1); this->parallelThreadData = HeapNewArrayZ(ParallelThreadData*, this->maxThreadCount); for (uint i = 0; i < this->maxThreadCount; i++) { this->parallelThreadData[i] = HeapNewNoThrow(ParallelThreadData, policyManager); if (this->parallelThreadData[i] == nullptr) { if (i == 0) { HeapDeleteArray(this->maxThreadCount, this->parallelThreadData); Js::Throw::OutOfMemory(); } // At least one thread is created, continue break; } this->parallelThreadData[i]->processor = this; // Make sure to create the thread suspended so the thread handle can be assigned before the thread starts running this->parallelThreadData[i]->threadHandle = reinterpret_cast<HANDLE>(_beginthreadex(0, 0, &StaticThreadProc, this->parallelThreadData[i], CREATE_SUSPENDED, 0)); if (!this->parallelThreadData[i]->threadHandle) { HeapDelete(parallelThreadData[i]); parallelThreadData[i] = nullptr; if (i == 0) { Js::Throw::OutOfMemory(); } // At least one thread is created, continue break; } if (ResumeThread(this->parallelThreadData[i]->threadHandle) == static_cast<DWORD>(-1)) { CloseHandle(this->parallelThreadData[i]->threadHandle); HeapDelete(parallelThreadData[i]); this->parallelThreadData[i] = nullptr; if (i == 0) { Js::Throw::OutOfMemory(); } // At least one thread is created, continue break; } this->threadCount++; // Wait for the thread to fully start. This is necessary because Close may be called before the thread starts and if // Close is called while holding the loader lock during DLL_THREAD_DETACH, the thread may be stuck waiting for the // loader lock for DLL_THREAD_ATTACH to start up, and Close would then end up waiting forever, causing a deadlock. WaitWithThreadForThreadStartedOrClosingEvent(this->parallelThreadData[i]); this->parallelThreadData[i]->threadStartedOrClosing.Reset(); // after this, the event will be used to wait for the thread to close #if DBG_DUMP if (i < (sizeof(DebugThreadNames) / sizeof(DebugThreadNames[i]))) { this->parallelThreadData[i]->backgroundPageAllocator.debugName = DebugThreadNames[i]; } else { this->parallelThreadData[i]->backgroundPageAllocator.debugName = _u("BackgroundJobProcessor thread"); } #endif } Assert(this->threadCount >= 1); }
void ConfigFlagsTable::TranslateFlagConfiguration() { const auto VerifyExecutionModeLimits = [this]() { const Number zero = static_cast<Number>(0); const Number maxUint8 = static_cast<Number>(static_cast<uint8>(-1)); // entry point call count is uint8 const Number maxUint16 = static_cast<Number>(static_cast<uint16>(-1)); #if ENABLE_DEBUG_CONFIG_OPTIONS Assert(MinInterpretCount >= zero); Assert(MinInterpretCount <= maxUint16); Assert(MaxInterpretCount >= zero); Assert(MaxInterpretCount <= maxUint16); Assert(MinSimpleJitRunCount >= zero); Assert(MinSimpleJitRunCount <= maxUint8); Assert(MaxSimpleJitRunCount >= zero); Assert(MaxSimpleJitRunCount <= maxUint8); Assert(SimpleJitAfter >= zero); Assert(SimpleJitAfter <= maxUint8); Assert(FullJitAfter >= zero); Assert(FullJitAfter <= maxUint16); #endif Assert(AutoProfilingInterpreter0Limit >= zero); Assert(AutoProfilingInterpreter0Limit <= maxUint16); Assert(ProfilingInterpreter0Limit >= zero); Assert(ProfilingInterpreter0Limit <= maxUint16); Assert(AutoProfilingInterpreter1Limit >= zero); Assert(AutoProfilingInterpreter1Limit <= maxUint16); Assert(SimpleJitLimit >= zero); Assert(SimpleJitLimit <= maxUint8); Assert(ProfilingInterpreter1Limit >= zero); Assert(ProfilingInterpreter1Limit <= maxUint16); Assert( ( AutoProfilingInterpreter0Limit + ProfilingInterpreter0Limit + AutoProfilingInterpreter1Limit + SimpleJitLimit + ProfilingInterpreter1Limit ) <= maxUint16); }; VerifyExecutionModeLimits(); #if ENABLE_DEBUG_CONFIG_OPTIONS #if !DISABLE_JIT if(ForceDynamicProfile) { Force.Enable(DynamicProfilePhase); } if(ForceJITLoopBody) { Force.Enable(JITLoopBodyPhase); } #endif if(NoDeferParse) { Off.Enable(DeferParsePhase); } #endif #if ENABLE_DEBUG_CONFIG_OPTIONS && !DISABLE_JIT bool dontEnforceLimitsForSimpleJitAfterOrFullJitAfter = false; if((IsEnabled(MinInterpretCountFlag) || IsEnabled(MaxInterpretCountFlag)) && !(IsEnabled(SimpleJitAfterFlag) || IsEnabled(FullJitAfterFlag))) { if(Off.IsEnabled(SimpleJitPhase)) { Enable(FullJitAfterFlag); if(IsEnabled(MaxInterpretCountFlag)) { FullJitAfter = MaxInterpretCount; } else { FullJitAfter = MinInterpretCount; dontEnforceLimitsForSimpleJitAfterOrFullJitAfter = true; } } else { Enable(SimpleJitAfterFlag); if(IsEnabled(MaxInterpretCountFlag)) { SimpleJitAfter = MaxInterpretCount; } else { SimpleJitAfter = MinInterpretCount; dontEnforceLimitsForSimpleJitAfterOrFullJitAfter = true; } if((IsEnabled(MinInterpretCountFlag) && IsEnabled(MinSimpleJitRunCountFlag)) || IsEnabled(MaxSimpleJitRunCountFlag)) { Enable(FullJitAfterFlag); FullJitAfter = SimpleJitAfter; if(IsEnabled(MaxSimpleJitRunCountFlag)) { FullJitAfter += MaxSimpleJitRunCount; } else { FullJitAfter += MinSimpleJitRunCount; Assert(dontEnforceLimitsForSimpleJitAfterOrFullJitAfter); } } } } // Configure execution mode limits do { if(IsEnabled(AutoProfilingInterpreter0LimitFlag) || IsEnabled(ProfilingInterpreter0LimitFlag) || IsEnabled(AutoProfilingInterpreter1LimitFlag) || IsEnabled(SimpleJitLimitFlag) || IsEnabled(ProfilingInterpreter1LimitFlag)) { break; } if(IsEnabled(ExecutionModeLimitsFlag)) { uint autoProfilingInterpreter0Limit; uint profilingInterpreter0Limit; uint autoProfilingInterpreter1Limit; uint simpleJitLimit; uint profilingInterpreter1Limit; const int scannedCount = swscanf_s( static_cast<LPCWSTR>(ExecutionModeLimits), _u("%u.%u.%u.%u.%u"), &autoProfilingInterpreter0Limit, &profilingInterpreter0Limit, &autoProfilingInterpreter1Limit, &simpleJitLimit, &profilingInterpreter1Limit); Assert(scannedCount == 5); Enable(AutoProfilingInterpreter0LimitFlag); Enable(ProfilingInterpreter0LimitFlag); Enable(AutoProfilingInterpreter1LimitFlag); Enable(SimpleJitLimitFlag); Enable(ProfilingInterpreter1LimitFlag); AutoProfilingInterpreter0Limit = autoProfilingInterpreter0Limit; ProfilingInterpreter0Limit = profilingInterpreter0Limit; AutoProfilingInterpreter1Limit = autoProfilingInterpreter1Limit; SimpleJitLimit = simpleJitLimit; ProfilingInterpreter1Limit = profilingInterpreter1Limit; break; } if(!NewSimpleJit) { // Use the defaults for old simple JIT. The flags are not enabled here because the values can be changed later // based on other flags, only the defaults values are adjusted here. AutoProfilingInterpreter0Limit = DEFAULT_CONFIG_AutoProfilingInterpreter0Limit; ProfilingInterpreter0Limit = DEFAULT_CONFIG_ProfilingInterpreter0Limit; CompileAssert( DEFAULT_CONFIG_AutoProfilingInterpreter0Limit <= DEFAULT_CONFIG_AutoProfilingInterpreterLimit_OldSimpleJit); AutoProfilingInterpreter1Limit = DEFAULT_CONFIG_AutoProfilingInterpreterLimit_OldSimpleJit - DEFAULT_CONFIG_AutoProfilingInterpreter0Limit; CompileAssert(DEFAULT_CONFIG_ProfilingInterpreter0Limit <= DEFAULT_CONFIG_SimpleJitLimit_OldSimpleJit); SimpleJitLimit = DEFAULT_CONFIG_SimpleJitLimit_OldSimpleJit - DEFAULT_CONFIG_ProfilingInterpreter0Limit; ProfilingInterpreter1Limit = 0; VerifyExecutionModeLimits(); } if (IsEnabled(SimpleJitAfterFlag)) { Enable(AutoProfilingInterpreter0LimitFlag); Enable(ProfilingInterpreter0LimitFlag); Enable(AutoProfilingInterpreter1LimitFlag); Enable(EnforceExecutionModeLimitsFlag); { Js::Number iterationsNeeded = SimpleJitAfter; ProfilingInterpreter0Limit = min(ProfilingInterpreter0Limit, iterationsNeeded); iterationsNeeded -= ProfilingInterpreter0Limit; AutoProfilingInterpreter0Limit = iterationsNeeded; AutoProfilingInterpreter1Limit = 0; } if(IsEnabled(FullJitAfterFlag)) { Enable(SimpleJitLimitFlag); Enable(ProfilingInterpreter1LimitFlag); Assert(SimpleJitAfter <= FullJitAfter); Js::Number iterationsNeeded = FullJitAfter - SimpleJitAfter; Js::Number profilingIterationsNeeded = min(NewSimpleJit ? DEFAULT_CONFIG_MinProfileIterations : DEFAULT_CONFIG_MinProfileIterations_OldSimpleJit, FullJitAfter) - ProfilingInterpreter0Limit; if(NewSimpleJit) { ProfilingInterpreter1Limit = min(ProfilingInterpreter1Limit, iterationsNeeded); iterationsNeeded -= ProfilingInterpreter1Limit; profilingIterationsNeeded -= ProfilingInterpreter1Limit; SimpleJitLimit = iterationsNeeded; } else { SimpleJitLimit = iterationsNeeded; profilingIterationsNeeded -= min(SimpleJitLimit, profilingIterationsNeeded); ProfilingInterpreter1Limit = 0; } if(profilingIterationsNeeded != 0) { Js::Number iterationsToMove = min(AutoProfilingInterpreter1Limit, profilingIterationsNeeded); AutoProfilingInterpreter1Limit -= iterationsToMove; ProfilingInterpreter0Limit += iterationsToMove; profilingIterationsNeeded -= iterationsToMove; iterationsToMove = min(AutoProfilingInterpreter0Limit, profilingIterationsNeeded); AutoProfilingInterpreter0Limit -= iterationsToMove; ProfilingInterpreter0Limit += iterationsToMove; profilingIterationsNeeded -= iterationsToMove; Assert(profilingIterationsNeeded == 0); } Assert( ( AutoProfilingInterpreter0Limit + ProfilingInterpreter0Limit + AutoProfilingInterpreter1Limit + SimpleJitLimit + ProfilingInterpreter1Limit ) == FullJitAfter); } Assert( ( AutoProfilingInterpreter0Limit + ProfilingInterpreter0Limit + AutoProfilingInterpreter1Limit ) == SimpleJitAfter); EnforceExecutionModeLimits = true; break; } if(IsEnabled(FullJitAfterFlag)) { Enable(AutoProfilingInterpreter0LimitFlag); Enable(ProfilingInterpreter0LimitFlag); Enable(AutoProfilingInterpreter1LimitFlag); Enable(SimpleJitLimitFlag); Enable(ProfilingInterpreter1LimitFlag); Enable(EnforceExecutionModeLimitsFlag); Js::Number iterationsNeeded = FullJitAfter; if(NewSimpleJit) { ProfilingInterpreter1Limit = min(ProfilingInterpreter1Limit, iterationsNeeded); iterationsNeeded -= ProfilingInterpreter1Limit; } else { ProfilingInterpreter1Limit = 0; SimpleJitLimit = min(SimpleJitLimit, iterationsNeeded); iterationsNeeded -= SimpleJitLimit; } ProfilingInterpreter0Limit = min(ProfilingInterpreter0Limit, iterationsNeeded); iterationsNeeded -= ProfilingInterpreter0Limit; if(NewSimpleJit) { SimpleJitLimit = min(SimpleJitLimit, iterationsNeeded); iterationsNeeded -= SimpleJitLimit; } AutoProfilingInterpreter0Limit = min(AutoProfilingInterpreter0Limit, iterationsNeeded); iterationsNeeded -= AutoProfilingInterpreter0Limit; AutoProfilingInterpreter1Limit = iterationsNeeded; Assert( ( AutoProfilingInterpreter0Limit + ProfilingInterpreter0Limit + AutoProfilingInterpreter1Limit + SimpleJitLimit + ProfilingInterpreter1Limit ) == FullJitAfter); EnforceExecutionModeLimits = true; break; } if (IsEnabled(MaxTemplatizedJitRunCountFlag)) { if (MaxTemplatizedJitRunCount >= 0) { MinTemplatizedJitRunCount = MaxTemplatizedJitRunCount; } } if (IsEnabled(MaxAsmJsInterpreterRunCountFlag)) { if (MaxAsmJsInterpreterRunCount >= 0) { MinAsmJsInterpreterRunCount = MaxAsmJsInterpreterRunCount; } } } while(false); #endif if( ( #ifdef ENABLE_PREJIT Prejit || #endif ForceNative ) && !NoNative) { Enable(AutoProfilingInterpreter0LimitFlag); Enable(ProfilingInterpreter0LimitFlag); Enable(AutoProfilingInterpreter1LimitFlag); Enable(EnforceExecutionModeLimitsFlag); // Override any relevant automatic configuration above AutoProfilingInterpreter0Limit = 0; ProfilingInterpreter0Limit = 0; AutoProfilingInterpreter1Limit = 0; #if ENABLE_DEBUG_CONFIG_OPTIONS if(Off.IsEnabled(SimpleJitPhase)) { Enable(SimpleJitLimitFlag); Enable(ProfilingInterpreter1LimitFlag); SimpleJitLimit = 0; ProfilingInterpreter1Limit = 0; } #endif EnforceExecutionModeLimits = true; } VerifyExecutionModeLimits(); }
bool Debugger::CompareOrWriteBaselineFile(LPCSTR fileName) { AutoRestoreContext autoRestoreContext(this->m_context); // Pass in undefined for 'this' JsValueRef undefinedRef; IfJsrtErrorFailLogAndRetFalse(ChakraRTInterface::JsGetUndefinedValue(&undefinedRef)); JsValueRef result = JS_INVALID_REFERENCE; bool testPassed = false; if (HostConfigFlags::flags.dbgbaselineIsEnabled) { this->CallFunction("Verify", &result); JsValueRef numberVal; IfJsrtErrorFailLogAndRetFalse(ChakraRTInterface::JsConvertValueToNumber(result, &numberVal)); int val; IfJsrtErrorFailLogAndRetFalse(ChakraRTInterface::JsNumberToInt(numberVal, &val)); testPassed = (val == 0); } if (!testPassed) { this->CallFunction("GetOutputJson", &result); AutoString baselineData; IfJsrtErrorFailLogAndRetFalse(baselineData.Initialize(result)); char16 baselineFilename[256]; swprintf_s(baselineFilename, HostConfigFlags::flags.dbgbaselineIsEnabled ? _u("%S.dbg.baseline.rebase") : _u("%S.dbg.baseline"), fileName); FILE *file = nullptr; if (_wfopen_s(&file, baselineFilename, _u("wt")) != 0) { return false; } if (file != nullptr) { int countWritten = static_cast<int>(fwrite(baselineData.GetString(), sizeof(char), baselineData.GetLength(), file)); Assert(baselineData.GetLength() <= INT_MAX); if (countWritten != (int)baselineData.GetLength()) { Assert(false); return false; } fclose(file); } if (!HostConfigFlags::flags.dbgbaselineIsEnabled) { wprintf(_u("No baseline file specified, baseline created at '%s'\n"), baselineFilename); } else { Helpers::LogError(_u("Rebase file created at '%s'\n"), baselineFilename); } } return true; }
Js::Var WabtInterface::EntryConvertWast2Wasm(RecyclableObject* function, CallInfo callInfo, ...) { ScriptContext* scriptContext = function->GetScriptContext(); PROBE_STACK(function->GetScriptContext(), Constants::MinStackDefault); ARGUMENTS(args, callInfo); AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); Assert(!(callInfo.Flags & CallFlags_New)); if (args.Info.Count < 2 || !JavascriptString::Is(args[1])) { JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedBufferSource); } bool isSpecText = false; if (args.Info.Count > 2) { // optional config object if (!JavascriptOperators::IsObject(args[2])) { JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject, _u("config")); } DynamicObject * configObject = JavascriptObject::FromVar(args[2]); Js::Var isSpecVar = JavascriptOperators::OP_GetProperty(configObject, PropertyIds::spec, scriptContext); isSpecText = JavascriptConversion::ToBool(isSpecVar, scriptContext); } ArenaAllocator arena(_u("Wast2Wasm"), scriptContext->GetThreadContext()->GetPageAllocator(), Throw::OutOfMemory); Context context; size_t wastSize; char* wastBuffer = nullptr; ENTER_PINNED_SCOPE(JavascriptString, string); string = (JavascriptString*)args[1]; const char16* str = string->GetString(); context.allocator = &arena; context.scriptContext = scriptContext; size_t origSize = string->GetLength(); auto allocator = [&arena](size_t size) {return (utf8char_t*)AnewArray(&arena, byte, size);}; utf8::WideStringToNarrow(allocator, str, origSize, &wastBuffer, &wastSize); LEAVE_PINNED_SCOPE(); // string try { ChakraWabt::SpecContext spec; ChakraWabt::ChakraContext wabtCtx; wabtCtx.user_data = &context; wabtCtx.createBuffer = CreateBuffer; wabtCtx.features.sign_extends = CONFIG_FLAG(WasmSignExtends); if (isSpecText) { wabtCtx.spec = &spec; spec.setProperty = SetProperty; spec.int32ToVar = Int32ToVar; spec.int64ToVar = Int64ToVar; spec.stringToVar = StringToVar; spec.createObject = CreateObject; spec.createArray = CreateArray; spec.push = Push; } void* result = ChakraWabt::ConvertWast2Wasm(wabtCtx, wastBuffer, (uint)wastSize, isSpecText); if (result == nullptr) { return scriptContext->GetLibrary()->GetUndefined(); } return result; } catch (ChakraWabt::Error& e) { JavascriptError::ThrowTypeErrorVar(scriptContext, WABTERR_WabtError, NarrowStringToWide(&context, e.message)); } }