diff --git a/js/src/ion/IonBuilder.h b/js/src/ion/IonBuilder.h index d00ea4057f9..734f9200850 100644 --- a/js/src/ion/IonBuilder.h +++ b/js/src/ion/IonBuilder.h @@ -441,7 +441,7 @@ class IonBuilder : public MIRGenerator InliningStatus inlineUnsafeSetElement(CallInfo &callInfo); bool inlineUnsafeSetDenseArrayElement(CallInfo &callInfo, uint32_t base); bool inlineUnsafeSetTypedArrayElement(CallInfo &callInfo, uint32_t base, int arrayType); - InliningStatus inlineForceSequentialOrInParallelSection(CallInfo &callInfo); + InliningStatus inlineShouldForceSequentialOrInParallelSection(CallInfo &callInfo); InliningStatus inlineNewDenseArray(CallInfo &callInfo); InliningStatus inlineNewDenseArrayForSequentialExecution(CallInfo &callInfo); InliningStatus inlineNewDenseArrayForParallelExecution(CallInfo &callInfo); diff --git a/js/src/ion/MCallOptimize.cpp b/js/src/ion/MCallOptimize.cpp index 8f2e33843c5..0e40f04e3e9 100644 --- a/js/src/ion/MCallOptimize.cpp +++ b/js/src/ion/MCallOptimize.cpp @@ -82,7 +82,25 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSNative native) if (native == intrinsic_UnsafeSetElement) return inlineUnsafeSetElement(callInfo); if (native == testingFunc_inParallelSection) - return inlineForceSequentialOrInParallelSection(callInfo); + return inlineShouldForceSequentialOrInParallelSection(callInfo); + if (native == intrinsic_NewDenseArray) + return inlineNewDenseArray(callInfo); + + // Self-hosting + if (native == intrinsic_ThrowError) + return inlineThrowError(callInfo); +#ifdef DEBUG + if (native == intrinsic_Dump) + return inlineDump(callInfo); +#endif + + // Parallel Array + if (native == intrinsic_UnsafeSetElement) + return inlineUnsafeSetElement(callInfo); + if (native == intrinsic_ShouldForceSequential) + return inlineShouldForceSequentialOrInParallelSection(callInfo); + if (native == testingFunc_inParallelSection) + return inlineShouldForceSequentialOrInParallelSection(callInfo); if (native == intrinsic_NewDenseArray) return inlineNewDenseArray(callInfo); @@ -1003,7 +1021,7 @@ IonBuilder::inlineUnsafeSetTypedArrayElement(CallInfo &callInfo, } IonBuilder::InliningStatus -IonBuilder::inlineForceSequentialOrInParallelSection(CallInfo &callInfo) +IonBuilder::inlineShouldForceSequentialOrInParallelSection(CallInfo &callInfo) { if (callInfo.constructing()) return InliningStatus_NotInlined; diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 368185490b6..31b9cb6d458 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -2257,8 +2257,7 @@ class ContextAllocPolicy JSBool intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp); JSBool intrinsic_NewDenseArray(JSContext *cx, unsigned argc, Value *vp); JSBool intrinsic_UnsafeSetElement(JSContext *cx, unsigned argc, Value *vp); -JSBool intrinsic_ForceSequential(JSContext *cx, unsigned argc, Value *vp); -JSBool intrinsic_NewParallelArray(JSContext *cx, unsigned argc, Value *vp); +JSBool intrinsic_ShouldForceSequential(JSContext *cx, unsigned argc, Value *vp); #ifdef DEBUG JSBool intrinsic_Dump(JSContext *cx, unsigned argc, Value *vp); diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index a5ad01f42d8..de8c4473e7d 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -13,6 +13,7 @@ #include "gc/Marking.h" +#include "vm/ParallelDo.h" #include "vm/ForkJoin.h" #include "vm/ThreadPool.h" @@ -170,7 +171,47 @@ intrinsic_DecompileArg(JSContext *cx, unsigned argc, Value *vp) return true; } +/* + * SetScriptHints(fun, flags): Sets various internal hints to the ion + * compiler for use when compiling |fun| or calls to |fun|. Flags + * should be a dictionary object. + * + * The function |fun| should be a self-hosted function (in particular, + * it *must* be a JS function). + * + * Possible flags: + * - |cloneAtCallsite: true| will hint that |fun| should be cloned + * each callsite to improve TI resolution. This is important for + * higher-order functions like |Array.map|. + */ +static JSBool +intrinsic_SetScriptHints(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + JS_ASSERT(args.length() >= 2); + JS_ASSERT(args[0].isObject() && args[0].toObject().isFunction()); + JS_ASSERT(args[1].isObject()); + + RootedFunction fun(cx, args[0].toObject().toFunction()); + RootedScript funScript(cx, fun->nonLazyScript()); + RootedObject flags(cx, &args[1].toObject()); + + RootedId id(cx); + RootedValue propv(cx); + + id = AtomToId(Atomize(cx, "cloneAtCallsite", strlen("cloneAtCallsite"))); + if (!JSObject::getGeneric(cx, flags, flags, id, &propv)) + return false; + if (ToBoolean(propv)) + funScript->shouldCloneAtCallsite = true; + + return true; +} + #ifdef DEBUG +/* + * Dump(val): Dumps a value for debugging, even in parallel mode. + */ JSBool js::intrinsic_Dump(JSContext *cx, unsigned argc, Value *vp) { @@ -183,10 +224,25 @@ js::intrinsic_Dump(JSContext *cx, unsigned argc, Value *vp) } #endif +/* + * ParallelSlices(): Returns the number of parallel slices that will + * be created by ParallelDo(). + */ +static JSBool +intrinsic_ParallelSlices(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + args.rval().setInt32(ForkJoinSlices(cx)); + return true; +} + +/* + * NewDenseArray(length): Allocates and returns a new dense array with + * the given length where all values are initialized to holes. + */ JSBool js::intrinsic_NewDenseArray(JSContext *cx, unsigned argc, Value *vp) { - // Usage: %NewDenseArray(length) CallArgs args = CallArgsFromVp(argc, vp); // Check that index is an int32 @@ -223,23 +279,22 @@ js::intrinsic_NewDenseArray(JSContext *cx, unsigned argc, Value *vp) return false; } +/* + * UnsafeSetElement(arr0, idx0, elem0, ..., arrN, idxN, elemN): For + * each set of (arr, idx, elem) arguments that are passed, performs + * the assignment |arr[idx] = elem|. |arr| must be either a dense array + * or a typed array. + * + * If |arr| is a dense array, the index must be an int32 less than the + * initialized length of |arr|. Use |%EnsureDenseResultArrayElements| + * to ensure that the initialized length is long enough. + * + * If |arr| is a typed array, the index must be an int32 less than the + * length of |arr|. + */ JSBool js::intrinsic_UnsafeSetElement(JSContext *cx, unsigned argc, Value *vp) { - // Usage: %UnsafeSetElement(arr0, idx0, elem0, - // ..., - // arrN, idxN, elemN) - // - // For each set of |(arr, idx, elem)| arguments that are passed, - // performs the assignment |arr[idx] = elem|. |arr| must be either - // a dense array or a typed array. - // - // If |arr| is a dense array, the index must be an int32 less than the - // initialized length of |arr|. Use |%EnsureDenseResultArrayElements| to - // ensure that the initialized length is long enough. - // - // If |arr| is a typed array, the index must be an int32 less than the - // length of |arr|. CallArgs args = CallArgsFromVp(argc, vp); if ((args.length() % 3) != 0) { @@ -276,6 +331,49 @@ js::intrinsic_UnsafeSetElement(JSContext *cx, unsigned argc, Value *vp) return true; } +/* + * ParallelTestsShouldPass(): Returns false if we are running in a + * mode (such as --ion-eager) that is known to cause additional + * bailouts or disqualifications for parallel array tests. + * + * This is needed because the parallel tests generally assert that, + * under normal conditions, they will run without bailouts or + * compilation failures, but this does not hold under "stress-testing" + * conditions like --ion-eager or --no-ti. However, running the tests + * under those conditions HAS exposed bugs and thus we do not wish to + * disable them entirely. Instead, we simply disable the assertions + * that state that no bailouts etc should occur. + */ +static JSBool +intrinsic_ParallelTestsShouldPass(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); +#if defined(JS_THREADSAFE) && defined(JS_ION) + args.rval().setBoolean(ion::IsEnabled(cx) && + !ion::js_IonOptions.eagerCompilation); +#else + args.rval().setBoolean(false); +#endif + return true; +} + +/* + * ShouldForceSequential(): Returns true if parallel ops should take + * the sequential fallback path. + */ +JSBool +js::intrinsic_ShouldForceSequential(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); +#ifdef JS_THREADSAFE + args.rval().setBoolean(cx->runtime->parallelWarmup || + InParallelSection()); +#else + args.rval().setBoolean(true); +#endif + return true; +} + /** * Returns the default locale as a well-formed, but not necessarily canonicalized, * BCP-47 language tag. @@ -305,12 +403,16 @@ JSFunctionSpec intrinsic_functions[] = { JS_FN("IsCallable", intrinsic_IsCallable, 1,0), JS_FN("ThrowError", intrinsic_ThrowError, 4,0), JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1,0), + JS_FN("SetScriptHints", intrinsic_SetScriptHints, 2,0), JS_FN("MakeConstructible", intrinsic_MakeConstructible, 1,0), JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0), JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0), - JS_FN("NewDenseArray", intrinsic_NewDenseArray, 1,0), + JS_FN("ParallelSlices", intrinsic_ParallelSlices, 0,0), + JS_FN("NewDenseArray", intrinsic_NewDenseArray, 1,0), JS_FN("UnsafeSetElement", intrinsic_UnsafeSetElement, 3,0), + JS_FN("ShouldForceSequential", intrinsic_ShouldForceSequential, 0,0), + JS_FN("ParallelTestsShouldPass", intrinsic_ParallelTestsShouldPass, 0,0), #ifdef DEBUG JS_FN("Dump", intrinsic_Dump, 1,0),