tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 509967b1d6fbe5c7178cda42356bbe47c71835d4
parent c6046cc05bf0c8f7bce23521b3401f1d7c380088
Author: Atila Butkovits <abutkovits@mozilla.com>
Date:   Mon, 20 Oct 2025 19:55:39 +0300

Revert "Bug 1991402 - Part 20: Add exhaustive test for built-in objects. r=spidermonkey-reviewers,jandem" for causing bustages at CacheIR.cpp.

This reverts commit 0e723420e06dfa157bf01cde88ca68400471973c.

Revert "Bug 1991402 - Part 19: Make (Specialized)BindFunctionResult shared CacheIR instructions. r=jandem"

This reverts commit 5b4768d1dd6709c9e7e08b67fc94664396902d1c.

Revert "Bug 1991402 - Part 18: Make ArrayJoinResult a shared CacheIR instruction. r=jandem"

This reverts commit 62273e72609efc2f03a1a562befb22e93b3c329f.

Revert "Bug 1991402 - Part 17: Make {PackedArray,Arguments}SliceResult shared CacheIR instructions. r=jandem"

This reverts commit 2cfc9651020f7415b97d36eda8a4d26210b7e507.

Revert "Bug 1991402 - Part 16: Support LoadStringAtResult in Ion ICs. r=jandem"

This reverts commit ed55b76e9d66855ad187b4c9b43a071a879086f9.

Revert "Bug 1991402 - Part 15: Support in CallNumberToString Ion ICs. r=jandem"

This reverts commit edaeec2a44db3552ff122004a05db13a2bddd6a6.

Revert "Bug 1991402 - Part 14: Implement LoadFixedSlotTypedResult for IonCacheIRCompiler. r=jandem"

This reverts commit d822c1b481c92a5b7cf998528204bfaafa4853ca.

Revert "Bug 1991402 - Part 13: Make MathRandomResult a shared CacheIR instruction. r=jandem"

This reverts commit c104700c3faed0bc4fde9e414340b776b5c2988d.

Revert "Bug 1991402 - Part 12: Convert RegExp getters. r=jandem"

This reverts commit 8a6600bfda89f3a61f6309ed0f109728130d28d8.

Revert "Bug 1991402 - Part 11: Convert (Shared)ArrayBuffer getters. r=jandem"

This reverts commit c4692f4209d521f00fac507337bfd7eba8d02227.

Revert "Bug 1991402 - Part 10: Convert DataView getters. r=jandem"

This reverts commit 99d7fb18a57a0696df663401f4a229727676cf13.

Revert "Bug 1991402 - Part 9: Convert TypedArray getters. r=jandem"

This reverts commit 2cf5974bdb57bcd4ab0032929e0b6c19d70e65fb.

Revert "Bug 1991402 - Part 8: Convert Map getters. r=jandem"

This reverts commit 54d2c4effea83c8e93c7ac72a054930dbdb68e0c.

Revert "Bug 1991402 - Part 7: Avoid duplicate shape guards for SetSize. r=jandem"

This reverts commit af8ce243a20edba08431d3236750dff2abe472a2.

Revert "Bug 1991402 - Part 6: Convert Set inlinable getters. r=jandem"

This reverts commit 027e7a837bf84a8fde14fbbda7e06d90b8bb5c6f.

Revert "Bug 1991402 - Part 5: Inline natives in super-get accesses. r=jandem"

This reverts commit 68d78760c1542142ee7a53016bb3f8d1dbadd38a.

Revert "Bug 1991402 - Part 4: Use InlinableNativeIRGenerator for inlinable native getters. r=jandem"

This reverts commit f32734c044aaf4c436a1cbdd3ab45008fbc4d05e.

Revert "Bug 1991402 - Part 3: Pass callee and bound-target to InlinableNativeIRGenerator. r=jandem"

This reverts commit 054f99ef4170623a1efa8f0a35b0e40f73b727ae.

Revert "Bug 1991402 - Part 2: Add IRGenerator::jsop() to get the current JSOp. r=jandem"

This reverts commit 7ff5a9581d81fdd93100bf11e2f9def144b990c9.

Revert "Bug 1991402 - Part 1: Share more code in GetPropIRGenerator::tryAttachPrimitive. r=jandem"

This reverts commit f8b6234c5cc8c0d31b6087b7f03d4de42ef242bf.

Diffstat:
Mjs/public/PropertySpec.h | 3---
Mjs/src/builtin/DataViewObject.cpp | 6++----
Mjs/src/builtin/DataViewObject.h | 8++++++++
Mjs/src/builtin/MapObject.cpp | 4++--
Mjs/src/builtin/MapObject.h | 8++++++++
Mjs/src/builtin/RegExp.cpp | 17++++++++---------
Mjs/src/builtin/RegExp.h | 2+-
Djs/src/jit-test/tests/cacheir/inlinable-native-accessor-1.js | 91-------------------------------------------------------------------------------
Djs/src/jit-test/tests/cacheir/inlinable-native-accessor-2.js | 30------------------------------
Djs/src/jit-test/tests/cacheir/inlinable-native-accessor-3.js | 23-----------------------
Djs/src/jit-test/tests/cacheir/inlinable-native-accessor-4.js | 27---------------------------
Djs/src/jit-test/tests/cacheir/inlinable-native-accessor-5.js | 31-------------------------------
Djs/src/jit-test/tests/cacheir/inlinable-native-accessor-6.js | 82-------------------------------------------------------------------------------
Djs/src/jit-test/tests/cacheir/inlinable-native-accessor-7.js | 147-------------------------------------------------------------------------------
Mjs/src/jit-test/tests/regexp/flag-getters.js | 20--------------------
Mjs/src/jit/BaselineCacheIRCompiler.cpp | 249+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mjs/src/jit/BaselineIC.cpp | 24+++++++++++-------------
Mjs/src/jit/BaselineIC.h | 5+++--
Mjs/src/jit/CacheIR.cpp | 1157++++++++++++++++++++++++++++++++++++-------------------------------------------
Mjs/src/jit/CacheIRCompiler.cpp | 229++-----------------------------------------------------------------------------
Mjs/src/jit/CacheIRGenerator.h | 112++++++++++++++++++++++---------------------------------------------------------
Mjs/src/jit/CacheIROps.yaml | 12++++++------
Mjs/src/jit/InlinableNatives.cpp | 16----------------
Mjs/src/jit/InlinableNatives.h | 18------------------
Mjs/src/jit/IonCacheIRCompiler.cpp | 93++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mjs/src/jit/IonCacheIRCompiler.h | 4----
Mjs/src/jit/IonIC.cpp | 4++--
Mjs/src/jit/MIR.cpp | 18+++++++++++-------
Mjs/src/jit/MIROps.yaml | 3+--
Mjs/src/vm/ArrayBufferObject.cpp | 4+---
Mjs/src/vm/ArrayBufferObject.h | 4++++
Mjs/src/vm/PortableBaselineInterpret.cpp | 4++--
Mjs/src/vm/RegExpObject.cpp | 38++++++++++++++++++++++++++++++++++++++
Mjs/src/vm/RegExpObject.h | 2++
Mjs/src/vm/SharedArrayObject.cpp | 4+---
Mjs/src/vm/SharedArrayObject.h | 4++++
Mjs/src/vm/TypedArrayObject.cpp | 23++++++++++++++++++-----
Mjs/src/vm/TypedArrayObject.h | 6++++++
38 files changed, 1002 insertions(+), 1530 deletions(-)

diff --git a/js/public/PropertySpec.h b/js/public/PropertySpec.h @@ -366,9 +366,6 @@ constexpr uint8_t CheckAccessorAttrs() { #define JS_PSG(name, getter, attributes) \ JSPropertySpec::nativeAccessors(name, CheckAccessorAttrs<attributes>(), \ getter, nullptr) -#define JS_INLINABLE_PSG(name, getter, attributes, native) \ - JSPropertySpec::nativeAccessors(name, CheckAccessorAttrs<attributes>(), \ - getter, &js::jit::JitInfo_##native) #define JS_PSGS(name, getter, setter, attributes) \ JSPropertySpec::nativeAccessors(name, CheckAccessorAttrs<attributes>(), \ getter, nullptr, setter, nullptr) diff --git a/js/src/builtin/DataViewObject.cpp b/js/src/builtin/DataViewObject.cpp @@ -1211,10 +1211,8 @@ const JSFunctionSpec DataViewObject::methods[] = { const JSPropertySpec DataViewObject::properties[] = { JS_PSG("buffer", DataViewObject::bufferGetter, 0), - JS_INLINABLE_PSG("byteLength", DataViewObject::byteLengthGetter, 0, - DataViewByteLength), - JS_INLINABLE_PSG("byteOffset", DataViewObject::byteOffsetGetter, 0, - DataViewByteOffset), + JS_PSG("byteLength", DataViewObject::byteLengthGetter, 0), + JS_PSG("byteOffset", DataViewObject::byteOffsetGetter, 0), JS_STRING_SYM_PS(toStringTag, "DataView", JSPROP_READONLY), JS_PS_END, }; diff --git a/js/src/builtin/DataViewObject.h b/js/src/builtin/DataViewObject.h @@ -81,6 +81,14 @@ class DataViewObject : public ArrayBufferViewObject { return endOffset.isValid() && endOffset.value() <= byteLength; } + static bool isOriginalByteOffsetGetter(Native native) { + return native == byteOffsetGetter; + } + + static bool isOriginalByteLengthGetter(Native native) { + return native == byteLengthGetter; + } + static bool construct(JSContext* cx, unsigned argc, Value* vp); static bool getInt8Impl(JSContext* cx, const CallArgs& args); diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp @@ -371,7 +371,7 @@ const JSClass MapObject::protoClass_ = { }; const JSPropertySpec MapObject::properties[] = { - JS_INLINABLE_PSG("size", size, 0, MapSize), + JS_PSG("size", size, 0), JS_STRING_SYM_PS(toStringTag, "Map", JSPROP_READONLY), JS_PS_END, }; @@ -1242,7 +1242,7 @@ const JSClass SetObject::protoClass_ = { }; const JSPropertySpec SetObject::properties[] = { - JS_INLINABLE_PSG("size", size, 0, SetSize), + JS_PSG("size", size, 0), JS_STRING_SYM_PS(toStringTag, "Set", JSPROP_READONLY), JS_PS_END, }; diff --git a/js/src/builtin/MapObject.h b/js/src/builtin/MapObject.h @@ -181,6 +181,10 @@ class MapObject : public OrderedHashMapObject { [[nodiscard]] static bool set(JSContext* cx, unsigned argc, Value* vp); [[nodiscard]] static bool has(JSContext* cx, unsigned argc, Value* vp); + static bool isOriginalSizeGetter(Native native) { + return native == static_cast<Native>(MapObject::size); + } + private: static const ClassSpec classSpec_; static const JSClassOps classOps_; @@ -307,6 +311,10 @@ class SetObject : public OrderedHashSetObject { size_t sizeOfData(mozilla::MallocSizeOf mallocSizeOf); + static bool isOriginalSizeGetter(Native native) { + return native == static_cast<Native>(SetObject::size); + } + private: static const ClassSpec classSpec_; static const JSClassOps classOps_; diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp @@ -15,7 +15,6 @@ #include "frontend/FrontendContext.h" // AutoReportFrontendContext #include "frontend/TokenStream.h" #include "irregexp/RegExpAPI.h" -#include "jit/InlinableNatives.h" #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_NEWREGEXP_FLAGGED #include "js/PropertySpec.h" #include "js/RegExpFlags.h" // JS::RegExpFlag, JS::RegExpFlags @@ -965,15 +964,15 @@ bool js::regexp_unicodeSets(JSContext* cx, unsigned argc, JS::Value* vp) { const JSPropertySpec js::regexp_properties[] = { JS_SELF_HOSTED_GET("flags", "$RegExpFlagsGetter", 0), - JS_INLINABLE_PSG("hasIndices", regexp_hasIndices, 0, RegExpHasIndices), - JS_INLINABLE_PSG("global", regexp_global, 0, RegExpGlobal), - JS_INLINABLE_PSG("ignoreCase", regexp_ignoreCase, 0, RegExpIgnoreCase), - JS_INLINABLE_PSG("multiline", regexp_multiline, 0, RegExpMultiline), - JS_INLINABLE_PSG("dotAll", regexp_dotAll, 0, RegExpDotAll), + JS_PSG("hasIndices", regexp_hasIndices, 0), + JS_PSG("global", regexp_global, 0), + JS_PSG("ignoreCase", regexp_ignoreCase, 0), + JS_PSG("multiline", regexp_multiline, 0), + JS_PSG("dotAll", regexp_dotAll, 0), JS_PSG("source", regexp_source, 0), - JS_INLINABLE_PSG("sticky", regexp_sticky, 0, RegExpSticky), - JS_INLINABLE_PSG("unicode", regexp_unicode, 0, RegExpUnicode), - JS_INLINABLE_PSG("unicodeSets", regexp_unicodeSets, 0, RegExpUnicodeSets), + JS_PSG("sticky", regexp_sticky, 0), + JS_PSG("unicode", regexp_unicode, 0), + JS_PSG("unicodeSets", regexp_unicodeSets, 0), JS_PS_END, }; diff --git a/js/src/builtin/RegExp.h b/js/src/builtin/RegExp.h @@ -164,7 +164,7 @@ extern const JSFunctionSpec regexp_static_methods[]; extern const JSPropertySpec regexp_properties[]; extern const JSFunctionSpec regexp_methods[]; -// Used in OptimizeRegExpPrototypeFuse::checkInvariant. +// Used in RegExpObject::isOriginalFlagGetter. [[nodiscard]] extern bool regexp_hasIndices(JSContext* cx, unsigned argc, JS::Value* vp); [[nodiscard]] extern bool regexp_global(JSContext* cx, unsigned argc, diff --git a/js/src/jit-test/tests/cacheir/inlinable-native-accessor-1.js b/js/src/jit-test/tests/cacheir/inlinable-native-accessor-1.js @@ -1,91 +0,0 @@ -// Test calling an inlinable native accessor property as a normal function. - -// Set.prototype.size is an inlinable getter accessor. -var SetSize = Object.getOwnPropertyDescriptor(Set.prototype, "size").get; - -// Install "size" getter as a normal method on Set.prototype. -Set.prototype.getSize = SetSize; - -var sets = [ - new Set(), - new Set([1, 2, 3]), -]; - -// Call inlinable accessor as normal method. -function testInlinableAccessorAsMethod() { - for (var i = 0; i < 100; ++i) { - var set = sets[i & 1]; - assertEq(set.getSize(), set.size); - } -} -testInlinableAccessorAsMethod(); - -// Call inlinable accessor as through FunCall. -function testInlinableAccessorWithFunCall() { - for (var i = 0; i < 100; ++i) { - var set = sets[i & 1]; - assertEq(SetSize.call(set), set.size); - } -} -testInlinableAccessorWithFunCall(); - -// Call inlinable accessor as through FunApply. -function testInlinableAccessorWithFunApply() { - for (var i = 0; i < 100; ++i) { - var set = sets[i & 1]; - assertEq(SetSize.apply(set), set.size); - } -} -testInlinableAccessorWithFunApply(); - -// Call inlinable accessor as through bound FunCall. -function testInlinableAccessorWithBoundFunCall() { - var callSetSize = Function.prototype.call.bind(SetSize); - - for (var i = 0; i < 100; ++i) { - var set = sets[i & 1]; - assertEq(callSetSize(set), set.size); - } -} -testInlinableAccessorWithBoundFunCall(); - -// Call inlinable accessor as through bound FunCall. -function testInlinableAccessorWithBoundFunApply() { - var applySetSize = Function.prototype.apply.bind(SetSize); - - for (var i = 0; i < 100; ++i) { - var set = sets[i & 1]; - assertEq(applySetSize(set), set.size); - } -} -testInlinableAccessorWithBoundFunApply(); - -// Call inlinable accessor as bound function. -function testBoundInlinableAccessor() { - var boundSetSize = SetSize.bind(sets[0]); - - for (var i = 0; i < 100; ++i) { - assertEq(boundSetSize(), sets[0].size); - } -} -testBoundInlinableAccessor(); - -// Call inlinable accessor as bound function through FunCall. -function testBoundInlinableAccessorWithFunCall() { - var boundSetSize = SetSize.bind(sets[0]); - - for (var i = 0; i < 100; ++i) { - assertEq(boundSetSize.call(), sets[0].size); - } -} -testBoundInlinableAccessorWithFunCall(); - -// Call inlinable accessor as bound function through FunApply. -function testBoundInlinableAccessorWithFunApply() { - var boundSetSize = SetSize.bind(sets[0]); - - for (var i = 0; i < 100; ++i) { - assertEq(boundSetSize.apply(), sets[0].size); - } -} -testInlinableAccessorWithBoundFunApply(); diff --git a/js/src/jit-test/tests/cacheir/inlinable-native-accessor-2.js b/js/src/jit-test/tests/cacheir/inlinable-native-accessor-2.js @@ -1,30 +0,0 @@ -// Test calling an inlinable native function as an accessor property. - -// Install the inlinable Number.prototype.toString method as an accessor property. -Object.defineProperty(Number.prototype, "tostr", { - get: Number.prototype.toString, -}); - -function testWithPrimitive() { - var key = "tostr"; - for (var i = 0; i < 100; ++i) { - assertEq(i.tostr, i.toString()); - assertEq(i[key], i.toString()); - } -} -testWithPrimitive(); - -// Install the inlinable Date.prototype.getTime method as an accessor property. -Object.defineProperty(Date.prototype, "time", { - get: Date.prototype.getTime, -}); - -function testWithObject() { - var key = "time"; - for (var i = 0; i < 100; ++i) { - var d = new Date(i); - assertEq(d.time, d.getTime()); - assertEq(d[key], d.getTime()); - } -} -testWithObject(); diff --git a/js/src/jit-test/tests/cacheir/inlinable-native-accessor-3.js b/js/src/jit-test/tests/cacheir/inlinable-native-accessor-3.js @@ -1,23 +0,0 @@ -// Test calling an inlinable native function as an accessor on a WindowProxy. - -var window = newGlobal({useWindowProxy: true}); - -// GetPropIRGenerator::tryAttachWindowProxy only attaches a stub if the current -// script global matches the window proxy, therefore we have to evaluate the -// test in |window|'s global environment. -window.eval(` -var window = this; - -// Use Math.random because it can be called with any |this| value. -Object.defineProperty(window, "random", { - get: Math.random, -}); - -function testRandom() { - for (var i = 0; i < 100; ++i) { - var r = window.random; - assertEq(0 <= r && r < 1, true); - } -} -testRandom(); -`); diff --git a/js/src/jit-test/tests/cacheir/inlinable-native-accessor-4.js b/js/src/jit-test/tests/cacheir/inlinable-native-accessor-4.js @@ -1,27 +0,0 @@ -// Test calling an inlinable native function as an accessor when the native function uses alloc-sites. - -function testObject() { - var obj = Object.defineProperty({}, "object", { - get: Object, - }); - - for (var i = 0; i < 100; i++) { - var o = obj.object; - assertEq(typeof o, "object"); - assertEq(o !== null, true); - } -} -testObject(); - -function testArray() { - var obj = Object.defineProperty({}, "array", { - get: Array, - }); - - for (var i = 0; i < 100; i++) { - var a = obj.array; - assertEq(a.length, 0); - assertEq(Array.isArray(a), true); - } -} -testArray(); diff --git a/js/src/jit-test/tests/cacheir/inlinable-native-accessor-5.js b/js/src/jit-test/tests/cacheir/inlinable-native-accessor-5.js @@ -1,31 +0,0 @@ -// Test calling an inlinable native accessor through a Get{Prop,Elem}Super operation. - -class MySet extends Set { - get size() { - return super.size; - } -} - -function testWithClass() { - var sets = [ - new MySet(), - new MySet([1, 2, 3, 4]), - ]; - for (var i = 0; i < 100; ++i) { - var set = sets[i & 1]; - assertEq(set.size, (i & 1) * 4); - } -} -testWithClass(); - -function testWithReflect() { - var sets = [ - new Set(), - new Set([1, 2, 3, 4]), - ]; - for (var i = 0; i < 100; ++i) { - var set = sets[i & 1]; - assertEq(Reflect.get(Set.prototype, "size", set), (i & 1) * 4); - } -} -testWithReflect(); diff --git a/js/src/jit-test/tests/cacheir/inlinable-native-accessor-6.js b/js/src/jit-test/tests/cacheir/inlinable-native-accessor-6.js @@ -1,82 +0,0 @@ -setJitCompilerOption("ion.forceinlineCaches", 1); - -function testMathRandom() { - var obj = Object.defineProperty({}, "random", {get: Math.random}); - - for (var i = 0; i < 100; ++i) { - var r = obj.random; - assertEq(0 <= r && r < 1, true); - } -} -testMathRandom(); - -function testDateGetTime() { - Object.defineProperty(Date.prototype, "time", {get: Date.prototype.getTime}); - - var d = new Date(0); - for (var i = 0; i < 100; ++i) { - assertEq(d.time, 0); - } -} -testDateGetTime(); - -function testNumberToString() { - Object.defineProperty(Number.prototype, "tostr", {get: Number.prototype.toString}); - - for (var i = 0; i < 100; ++i) { - assertEq(0..tostr, "0"); - assertEq(0.5.tostr, "0.5"); - } -} -testNumberToString(); - -function testStringAt() { - Object.defineProperty(String.prototype, "at_", {get: String.prototype.at}); - - for (var i = 0; i < 100; ++i) { - assertEq("a".at_, "a"); - } -} -testStringAt(); - -function testArraySlice() { - Object.defineProperty(Array.prototype, "sliced", {get: Array.prototype.slice}); - - for (var i = 0; i < 100; ++i) { - assertEq([1, 2].sliced.length, 2); - } -} -testArraySlice(); - -function testArraySliceArguments() { - Object.defineProperty(arguments, "sliced", {get: Array.prototype.slice}); - - for (var i = 0; i < 100; ++i) { - assertEq(arguments.sliced.length, 0); - } -} -testArraySliceArguments(); - -function testArrayJoin() { - Object.defineProperty(Array.prototype, "joined", {get: Array.prototype.join}); - - for (var i = 0; i < 100; ++i) { - assertEq([].joined, ""); - assertEq(["a"].joined, "a"); - } -} -testArrayJoin(); - -function testFunctionBind() { - Object.defineProperty(Function.prototype, "bound", {get: Function.prototype.bind}); - - for (var i = 0; i < 100; ++i) { - // |SpecializedBindFunctionResult| CacheIROp. - assertEq(function(){ return i; }.bound(), i); - - // |BindFunctionResult| CacheIROp. - var r = Math.random.bound(); - assertEq(0 <= r && r < 1, true); - } -} -testFunctionBind(); diff --git a/js/src/jit-test/tests/cacheir/inlinable-native-accessor-7.js b/js/src/jit-test/tests/cacheir/inlinable-native-accessor-7.js @@ -1,147 +0,0 @@ -// Ensure we create Ion ICs to cover IonCacheIRCompiler code paths. -setJitCompilerOption("ion.forceinlineCaches", 1); - -// Ignore unhandled rejections when calling Promise and AsyncFunction methods. -ignoreUnhandledRejections(); - -// Function cloned for each test. -function test(v) { - for (var i = 0; i < 100; ++i) { - // |v.key| is a getter calling a built-in method or accessor. - v.key; - } -} - -// Add |fn| as a method on |holder| and then call it using |thisValue| as the this-value. -function runTest(holder, thisValue, key, fn) { - assertEq(typeof fn, "function"); - assertEq( - holder === thisValue || Object.prototype.isPrototypeOf.call(holder, Object(thisValue)), - true, - `${String(key)} can be found on thisValue when stored in holder` - ); - - // Add a prefix so we don't overwrite built-in methods. - var safeKey = "__" + String(key); - - Object.defineProperty(holder, safeKey, { - get: fn, - configurable: true, - }); - - try { - var t = Function(`return ${test.toString().replaceAll("key", safeKey)}`)(); - t(thisValue); - } catch { - // Intentionally ignore any errors. - } - - // Also test wrappers of primitive values. - if (Object(thisValue) !== thisValue) { - try { - var t = Function(`return ${test.toString().replaceAll("key", safeKey)}`)(); - t(Object(thisValue)); - } catch { - // Intentionally ignore any errors. - } - } -} - -// Test all methods and accessors of |object|. -function testForEach(object, holder, thisValue) { - for (var key of Reflect.ownKeys(object)) { - var desc = Reflect.getOwnPropertyDescriptor(object, key); - if (typeof desc.value === "function") - runTest(holder, thisValue, key, desc.value); - if (typeof desc.get === "function") - runTest(holder, thisValue, key, desc.get); - if (typeof desc.set === "function") - runTest(holder, thisValue, key, desc.set); - } -} - -var seenProto = new Set(); - -// Test along the prototype chain of |objectOrPrimitive|. -function testProto(objectOrPrimitive) { - var proto = Object.getPrototypeOf(objectOrPrimitive); - - while (proto) { - // Install methods on |proto| and then call wih |obj| as the this-value. - testForEach(proto, proto, objectOrPrimitive); - - // Cover all objects on the prototype chain. - proto = Reflect.getPrototypeOf(proto); - - // But skip already seen prototypes to ensure we don't spend too much time on this test. - if (seenProto.has(proto)) { - break; - } - seenProto.add(proto); - } -} - -// Test constructor of |objectOrPrimitive|. -function testConstructor(objectOrPrimitive) { - // Install constructor methods on the prototype object and then call with |objectOrPrimitive| - // as the this-value. - testForEach(obj.constructor, Object.getPrototypeOf(objectOrPrimitive), objectOrPrimitive); -} - -function testSingleton(singleton) { - var thisValue = {}; - testForEach(singleton, thisValue, thisValue); -} - -for (var obj of [ - // Fundamental Objects <https://tc39.es/ecma262/#sec-fundamental-objects>. - {}, - Function(), - false, - Symbol(), - new Error(), - - // Numbers and Dates <https://tc39.es/ecma262/#sec-numbers-and-dates> - 0, - 0n, - new Date(0), - - // Text Processing <https://tc39.es/ecma262/#sec-text-processing> - "", - /(?:)/, - - // Indexed Collections <https://tc39.es/ecma262/#sec-indexed-collections> - [], - new Int32Array(1), - new Uint8Array(1), - - // Keyed Collections <https://tc39.es/ecma262/#sec-keyed-collections> - new Map(), - new Set(), - new WeakMap(), - new WeakSet(), - - // Structured Data <https://tc39.es/ecma262/#sec-structured-data> - new ArrayBuffer(1), - new SharedArrayBuffer(1), - new DataView(new ArrayBuffer(8)), - - // Managing Memory <https://tc39.es/ecma262/#sec-managing-memory> - new WeakRef({}), - new FinalizationRegistry(() => {}), - - // Control Abstraction Objects <https://tc39.es/ecma262/#sec-control-abstraction-objects> - new class extends Iterator{}, - new Promise(() => {}), - (function*(){}).constructor, - (async function*(){}).constructor, - (async function(){}).constructor, -]) { - testProto(obj); - testConstructor(obj); -} - -testSingleton(Math); -testSingleton(Atomics); -testSingleton(JSON); -testSingleton(Reflect); diff --git a/js/src/jit-test/tests/regexp/flag-getters.js b/js/src/jit-test/tests/regexp/flag-getters.js @@ -10,16 +10,6 @@ function testGlobal() { } for (let i = 0; i < 2; ++i) testGlobal(); -function testHasIndices() { - const xs = [/a/, /b/d]; - - for (let i = 0; i < 200; ++i) { - let x = xs[i & 1]; - assertEq(x.hasIndices, !!(i & 1)); - } -} -for (let i = 0; i < 2; ++i) testHasIndices(); - function testIgnoreCase() { const xs = [/a/, /b/i]; @@ -60,16 +50,6 @@ function testUnicode() { } for (let i = 0; i < 2; ++i) testUnicode(); -function testUnicodeSets() { - const xs = [/a/, /b/v]; - - for (let i = 0; i < 200; ++i) { - let x = xs[i & 1]; - assertEq(x.unicodeSets, !!(i & 1)); - } -} -for (let i = 0; i < 2; ++i) testUnicodeSets(); - function testSticky() { const xs = [/a/, /b/y]; diff --git a/js/src/jit/BaselineCacheIRCompiler.cpp b/js/src/jit/BaselineCacheIRCompiler.cpp @@ -26,6 +26,7 @@ #include "js/friend/DOMProxy.h" // JS::ExpandoAndGeneration #include "proxy/DeadObjectProxy.h" #include "proxy/Proxy.h" +#include "util/DifferentialTesting.h" #include "util/Unicode.h" #include "vm/PortableBaselineInterpret.h" #include "vm/StaticStrings.h" @@ -1040,6 +1041,152 @@ bool BaselineCacheIRCompiler::emitAllocateAndStoreDynamicSlot( newShapeOffset, mozilla::Some(numNewSlotsOffset), preserveWrapper); } +bool BaselineCacheIRCompiler::emitArrayJoinResult(ObjOperandId objId, + StringOperandId sepId) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoOutputRegister output(*this); + Register obj = allocator.useRegister(masm, objId); + Register sep = allocator.useRegister(masm, sepId); + AutoScratchRegisterMaybeOutput scratch(allocator, masm, output); + + allocator.discardStack(masm); + + // Load obj->elements in scratch. + masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch); + Address lengthAddr(scratch, ObjectElements::offsetOfLength()); + + // If array length is 0, return empty string. + Label finished; + + { + Label arrayNotEmpty; + masm.branch32(Assembler::NotEqual, lengthAddr, Imm32(0), &arrayNotEmpty); + masm.movePtr(ImmGCPtr(cx_->names().empty_), scratch); + masm.tagValue(JSVAL_TYPE_STRING, scratch, output.valueReg()); + masm.jump(&finished); + masm.bind(&arrayNotEmpty); + } + + Label vmCall; + + // Otherwise, handle array length 1 case. + masm.branch32(Assembler::NotEqual, lengthAddr, Imm32(1), &vmCall); + + // But only if initializedLength is also 1. + Address initLength(scratch, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::NotEqual, initLength, Imm32(1), &vmCall); + + // And only if elem0 is a string. + Address elementAddr(scratch, 0); + masm.branchTestString(Assembler::NotEqual, elementAddr, &vmCall); + + // Store the value. + masm.loadValue(elementAddr, output.valueReg()); + masm.jump(&finished); + + // Otherwise call into the VM. + { + masm.bind(&vmCall); + + AutoStubFrame stubFrame(*this); + stubFrame.enter(masm, scratch); + + masm.Push(sep); + masm.Push(obj); + + using Fn = JSString* (*)(JSContext*, HandleObject, HandleString); + callVM<Fn, jit::ArrayJoin>(masm); + + stubFrame.leave(masm); + + masm.tagValue(JSVAL_TYPE_STRING, ReturnReg, output.valueReg()); + } + + masm.bind(&finished); + + return true; +} + +bool BaselineCacheIRCompiler::emitPackedArraySliceResult( + uint32_t templateObjectOffset, ObjOperandId arrayId, Int32OperandId beginId, + Int32OperandId endId) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoOutputRegister output(*this); + AutoScratchRegisterMaybeOutput scratch1(allocator, masm, output); + AutoScratchRegisterMaybeOutputType scratch2(allocator, masm, output); + + Register array = allocator.useRegister(masm, arrayId); + Register begin = allocator.useRegister(masm, beginId); + Register end = allocator.useRegister(masm, endId); + + FailurePath* failure; + if (!addFailurePath(&failure)) { + return false; + } + + masm.branchArrayIsNotPacked(array, scratch1, scratch2, failure->label()); + + allocator.discardStack(masm); + + AutoStubFrame stubFrame(*this); + stubFrame.enter(masm, scratch1); + + // Don't attempt to pre-allocate the object, instead always use the slow + // path. + ImmPtr result(nullptr); + + masm.Push(result); + masm.Push(end); + masm.Push(begin); + masm.Push(array); + + using Fn = + JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject); + callVM<Fn, ArraySliceDense>(masm); + + stubFrame.leave(masm); + + masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, output.valueReg()); + return true; +} + +bool BaselineCacheIRCompiler::emitArgumentsSliceResult( + uint32_t templateObjectOffset, ObjOperandId argsId, Int32OperandId beginId, + Int32OperandId endId) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoOutputRegister output(*this); + AutoScratchRegisterMaybeOutput scratch(allocator, masm, output); + + Register args = allocator.useRegister(masm, argsId); + Register begin = allocator.useRegister(masm, beginId); + Register end = allocator.useRegister(masm, endId); + + allocator.discardStack(masm); + + AutoStubFrame stubFrame(*this); + stubFrame.enter(masm, scratch); + + // Don't attempt to pre-allocate the object, instead always use the slow path. + ImmPtr result(nullptr); + + masm.Push(result); + masm.Push(end); + masm.Push(begin); + masm.Push(args); + + using Fn = + JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject); + callVM<Fn, ArgumentsSliceDense>(masm); + + stubFrame.leave(masm); + + masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, output.valueReg()); + return true; +} + bool BaselineCacheIRCompiler::emitIsArrayResult(ValOperandId inputId) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); @@ -1307,6 +1454,28 @@ bool BaselineCacheIRCompiler::emitStringFromCodePointResult( return emitStringFromCodeResult(codeId, StringCode::CodePoint); } +bool BaselineCacheIRCompiler::emitMathRandomResult(uint32_t rngOffset) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoOutputRegister output(*this); + AutoScratchRegister scratch1(allocator, masm); + AutoScratchRegister64 scratch2(allocator, masm); + AutoAvailableFloatRegister scratchFloat(*this, FloatReg0); + + Address rngAddr(stubAddress(rngOffset)); + masm.loadPtr(rngAddr, scratch1); + + masm.randomDouble(scratch1, scratchFloat, scratch2, + output.valueReg().toRegister64()); + + if (js::SupportDifferentialTesting()) { + masm.loadConstantDouble(0.0, scratchFloat); + } + + masm.boxDouble(scratchFloat, output.valueReg(), scratchFloat); + return true; +} + bool BaselineCacheIRCompiler::emitReflectGetPrototypeOfResult( ObjOperandId objId) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); @@ -4093,6 +4262,86 @@ bool BaselineCacheIRCompiler::emitNewFunctionCloneResult( return true; } +bool BaselineCacheIRCompiler::emitBindFunctionResult( + ObjOperandId targetId, uint32_t argc, uint32_t templateObjectOffset) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoOutputRegister output(*this); + AutoScratchRegister scratch(allocator, masm); + + Register target = allocator.useRegister(masm, targetId); + + allocator.discardStack(masm); + + AutoStubFrame stubFrame(*this); + stubFrame.enter(masm, scratch); + + // Push the arguments in reverse order. + for (uint32_t i = 0; i < argc; i++) { + Address argAddress(FramePointer, + BaselineStubFrameLayout::Size() + i * sizeof(Value)); + masm.pushValue(argAddress); + } + masm.moveStackPtrTo(scratch.get()); + + masm.Push(ImmWord(0)); // nullptr for maybeBound + masm.Push(Imm32(argc)); + masm.Push(scratch); + masm.Push(target); + + using Fn = BoundFunctionObject* (*)(JSContext*, Handle<JSObject*>, Value*, + uint32_t, Handle<BoundFunctionObject*>); + callVM<Fn, BoundFunctionObject::functionBindImpl>(masm); + + stubFrame.leave(masm); + masm.storeCallPointerResult(scratch); + + masm.tagValue(JSVAL_TYPE_OBJECT, scratch, output.valueReg()); + return true; +} + +bool BaselineCacheIRCompiler::emitSpecializedBindFunctionResult( + ObjOperandId targetId, uint32_t argc, uint32_t templateObjectOffset) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoOutputRegister output(*this); + AutoScratchRegisterMaybeOutput scratch1(allocator, masm); + AutoScratchRegister scratch2(allocator, masm); + + Register target = allocator.useRegister(masm, targetId); + + StubFieldOffset objectField(templateObjectOffset, StubField::Type::JSObject); + emitLoadStubField(objectField, scratch2); + + allocator.discardStack(masm); + + AutoStubFrame stubFrame(*this); + stubFrame.enter(masm, scratch1); + + // Push the arguments in reverse order. + for (uint32_t i = 0; i < argc; i++) { + Address argAddress(FramePointer, + BaselineStubFrameLayout::Size() + i * sizeof(Value)); + masm.pushValue(argAddress); + } + masm.moveStackPtrTo(scratch1.get()); + + masm.Push(scratch2); + masm.Push(Imm32(argc)); + masm.Push(scratch1); + masm.Push(target); + + using Fn = BoundFunctionObject* (*)(JSContext*, Handle<JSObject*>, Value*, + uint32_t, Handle<BoundFunctionObject*>); + callVM<Fn, BoundFunctionObject::functionBindSpecializedBaseline>(masm); + + stubFrame.leave(masm); + masm.storeCallPointerResult(scratch1); + + masm.tagValue(JSVAL_TYPE_OBJECT, scratch1, output.valueReg()); + return true; +} + bool BaselineCacheIRCompiler::emitCloseIterScriptedResult( ObjOperandId iterId, ObjOperandId calleeId, CompletionKind kind, uint32_t calleeNargs) { diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp @@ -718,7 +718,7 @@ bool DoGetElemFallback(JSContext* cx, BaselineFrame* frame, #endif TryAttachStub<GetPropIRGenerator>("GetElem", cx, frame, stub, - CacheKind::GetElem, lhs, rhs, lhs); + CacheKind::GetElem, lhs, rhs); if (!GetElementOperation(cx, lhs, rhs, res)) { return false; @@ -752,8 +752,7 @@ bool DoGetElemSuperFallback(JSContext* cx, BaselineFrame* frame, } TryAttachStub<GetPropIRGenerator>("GetElemSuper", cx, frame, stub, - CacheKind::GetElemSuper, lhs, rhs, - receiver); + CacheKind::GetElemSuper, lhs, rhs); return GetObjectElementOperation(cx, op, lhsObj, receiver, rhs, res); } @@ -1289,7 +1288,7 @@ bool FallbackICCodeCompiler::emit_LazyConstant() { // bool DoGetPropFallback(JSContext* cx, BaselineFrame* frame, - ICFallbackStub* stub, HandleValue val, + ICFallbackStub* stub, MutableHandleValue val, MutableHandleValue res) { stub->incrementEnteredCount(); MaybeNotifyWarp(frame->outerScript(), stub); @@ -1305,7 +1304,7 @@ bool DoGetPropFallback(JSContext* cx, BaselineFrame* frame, RootedValue idVal(cx, StringValue(name)); TryAttachStub<GetPropIRGenerator>("GetProp", cx, frame, stub, - CacheKind::GetProp, val, idVal, val); + CacheKind::GetProp, val, idVal); if (op == JSOp::GetBoundName) { RootedObject env(cx, &val.toObject()); @@ -1323,7 +1322,7 @@ bool DoGetPropFallback(JSContext* cx, BaselineFrame* frame, bool DoGetPropSuperFallback(JSContext* cx, BaselineFrame* frame, ICFallbackStub* stub, HandleValue receiver, - HandleValue val, MutableHandleValue res) { + MutableHandleValue val, MutableHandleValue res) { stub->incrementEnteredCount(); MaybeNotifyWarp(frame->outerScript(), stub); @@ -1347,8 +1346,7 @@ bool DoGetPropSuperFallback(JSContext* cx, BaselineFrame* frame, } TryAttachStub<GetPropIRGenerator>("GetPropSuper", cx, frame, stub, - CacheKind::GetPropSuper, val, idVal, - receiver); + CacheKind::GetPropSuper, val, idVal); if (!GetProperty(cx, valObj, receiver, name, res)) { return false; @@ -1371,7 +1369,7 @@ bool FallbackICCodeCompiler::emitGetProp(bool hasReceiver) { masm.pushBaselineFramePtr(FramePointer, R0.scratchReg()); using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, - HandleValue, HandleValue, MutableHandleValue); + HandleValue, MutableHandleValue, MutableHandleValue); if (!tailCallVM<Fn, DoGetPropSuperFallback>(masm)) { return false; } @@ -1385,7 +1383,7 @@ bool FallbackICCodeCompiler::emitGetProp(bool hasReceiver) { masm.pushBaselineFramePtr(FramePointer, R0.scratchReg()); using Fn = bool (*)(JSContext*, BaselineFrame*, ICFallbackStub*, - HandleValue, MutableHandleValue); + MutableHandleValue, MutableHandleValue); if (!tailCallVM<Fn, DoGetPropFallback>(masm)) { return false; } @@ -1646,7 +1644,7 @@ bool DoCallFallback(JSContext* cx, BaselineFrame* frame, ICFallbackStub* stub, // allowed to attach stubs. if (canAttachStub) { HandleValueArray args = HandleValueArray::fromMarkedLocation(argc, vp + 2); - CallIRGenerator gen(cx, script, pc, stub->state(), frame, argc, callee, + CallIRGenerator gen(cx, script, pc, op, stub->state(), frame, argc, callee, callArgs.thisv(), newTarget, args); switch (gen.tryAttachStub()) { case AttachDecision::NoAction: @@ -1737,8 +1735,8 @@ bool DoSpreadCallFallback(JSContext* cx, BaselineFrame* frame, HandleValueArray args = HandleValueArray::fromMarkedLocation( aobj->length(), aobj->getDenseElements()); - CallIRGenerator gen(cx, script, pc, stub->state(), frame, 1, callee, thisv, - newTarget, args); + CallIRGenerator gen(cx, script, pc, op, stub->state(), frame, 1, callee, + thisv, newTarget, args); switch (gen.tryAttachStub()) { case AttachDecision::NoAction: break; diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h @@ -391,12 +391,13 @@ extern bool DoLazyConstantFallback(JSContext* cx, BaselineFrame* frame, MutableHandleValue res); extern bool DoGetPropFallback(JSContext* cx, BaselineFrame* frame, - ICFallbackStub* stub, HandleValue val, + ICFallbackStub* stub, MutableHandleValue val, MutableHandleValue res); extern bool DoGetPropSuperFallback(JSContext* cx, BaselineFrame* frame, ICFallbackStub* stub, HandleValue receiver, - HandleValue val, MutableHandleValue res); + MutableHandleValue val, + MutableHandleValue res); extern bool DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICFallbackStub* stub, Value* stack, diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp @@ -300,12 +300,8 @@ gc::AllocSite* IRGenerator::maybeCreateAllocSite() { GetPropIRGenerator::GetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, ICState state, CacheKind cacheKind, HandleValue val, - HandleValue idVal, - HandleValue receiverVal) - : IRGenerator(cx, script, pc, cacheKind, state), - val_(val), - idVal_(idVal), - receiverVal_(receiverVal) {} + HandleValue idVal) + : IRGenerator(cx, script, pc, cacheKind, state), val_(val), idVal_(idVal) {} static void EmitLoadSlotResult(CacheIRWriter& writer, ObjOperandId holderId, NativeObject* holder, PropertyInfo prop) { @@ -465,6 +461,12 @@ AttachDecision GetPropIRGenerator::tryAttachStub() { if (nameOrSymbol) { TRY_ATTACH(tryAttachObjectLength(obj, objId, id)); + TRY_ATTACH(tryAttachTypedArray(obj, objId, id)); + TRY_ATTACH(tryAttachDataView(obj, objId, id)); + TRY_ATTACH(tryAttachArrayBufferMaybeShared(obj, objId, id)); + TRY_ATTACH(tryAttachRegExp(obj, objId, id)); + TRY_ATTACH(tryAttachMap(obj, objId, id)); + TRY_ATTACH(tryAttachSet(obj, objId, id)); TRY_ATTACH(tryAttachNative(obj, objId, id, receiverId)); TRY_ATTACH(tryAttachModuleNamespace(obj, objId, id)); TRY_ATTACH(tryAttachWindowProxy(obj, objId, id)); @@ -1117,16 +1119,6 @@ void GetPropIRGenerator::emitCallGetterResult(NativeGetPropKind kind, ObjOperandId objId, ValOperandId receiverId) { emitCallGetterResultGuards(obj, holder, id, prop, objId); - - if (kind == NativeGetPropKind::NativeGetter && - mode_ == ICState::Mode::Specialized) { - auto attached = tryAttachInlinableNativeGetter(holder, prop, receiverId); - if (attached != AttachDecision::NoAction) { - MOZ_ASSERT(attached == AttachDecision::Attach); - return; - } - } - emitCallGetterResultNoGuards(kind, obj, holder, prop, receiverId); } @@ -2440,24 +2432,411 @@ AttachDecision GetPropIRGenerator::tryAttachObjectLength(HandleObject obj, return AttachDecision::NoAction; } -AttachDecision GetPropIRGenerator::tryAttachInlinableNativeGetter( - NativeObject* holder, PropertyInfo prop, ValOperandId receiverId) { - MOZ_ASSERT(mode_ == ICState::Mode::Specialized); +AttachDecision GetPropIRGenerator::tryAttachTypedArray(HandleObject obj, + ObjOperandId objId, + HandleId id) { + if (!obj->is<TypedArrayObject>()) { + return AttachDecision::NoAction; + } - Rooted<JSFunction*> target(cx_, &holder->getGetter(prop)->as<JSFunction>()); - MOZ_ASSERT(target->isNativeWithoutJitEntry()); + if (mode_ != ICState::Mode::Specialized) { + return AttachDecision::NoAction; + } - Handle<Value> thisValue = receiverVal_; + // Receiver should be the object. + if (isSuper()) { + return AttachDecision::NoAction; + } - bool isSpread = false; - bool isSameRealm = cx_->realm() == target->realm(); - bool isConstructing = false; - CallFlags flags(isConstructing, isSpread, isSameRealm); + bool isLength = id.isAtom(cx_->names().length); + bool isByteOffset = id.isAtom(cx_->names().byteOffset); + if (!isLength && !isByteOffset && !id.isAtom(cx_->names().byteLength)) { + return AttachDecision::NoAction; + } - // Check for specific native-function optimizations. - InlinableNativeIRGenerator nativeGen(*this, target, thisValue, flags, - receiverId); - return nativeGen.tryAttachStub(); + NativeObject* holder = nullptr; + Maybe<PropertyInfo> prop; + NativeGetPropKind kind = + CanAttachNativeGetProp(cx_, obj, id, &holder, &prop, pc_); + if (kind != NativeGetPropKind::NativeGetter) { + return AttachDecision::NoAction; + } + + JSFunction& fun = holder->getGetter(*prop)->as<JSFunction>(); + if (isLength) { + if (!TypedArrayObject::isOriginalLengthGetter(fun.native())) { + return AttachDecision::NoAction; + } + } else if (isByteOffset) { + if (!TypedArrayObject::isOriginalByteOffsetGetter(fun.native())) { + return AttachDecision::NoAction; + } + } else { + if (!TypedArrayObject::isOriginalByteLengthGetter(fun.native())) { + return AttachDecision::NoAction; + } + } + + auto* tarr = &obj->as<TypedArrayObject>(); + + maybeEmitIdGuard(id); + // Emit all the normal guards for calling this native, but specialize + // callNativeGetterResult. + emitCallGetterResultGuards(tarr, holder, id, *prop, objId); + if (isLength) { + size_t length = tarr->length().valueOr(0); + if (!tarr->is<ResizableTypedArrayObject>()) { + if (length <= INT32_MAX) { + writer.loadArrayBufferViewLengthInt32Result(objId); + } else { + writer.loadArrayBufferViewLengthDoubleResult(objId); + } + } else { + if (length <= INT32_MAX) { + writer.resizableTypedArrayLengthInt32Result(objId); + } else { + writer.resizableTypedArrayLengthDoubleResult(objId); + } + } + trackAttached("GetProp.TypedArrayLength"); + } else if (isByteOffset) { + // byteOffset doesn't need to use different code paths for fixed-length and + // resizable TypedArrays. + size_t byteOffset = tarr->byteOffset().valueOr(0); + if (byteOffset <= INT32_MAX) { + writer.arrayBufferViewByteOffsetInt32Result(objId); + } else { + writer.arrayBufferViewByteOffsetDoubleResult(objId); + } + trackAttached("GetProp.TypedArrayByteOffset"); + } else { + size_t byteLength = tarr->byteLength().valueOr(0); + if (!tarr->is<ResizableTypedArrayObject>()) { + if (byteLength <= INT32_MAX) { + writer.typedArrayByteLengthInt32Result(objId); + } else { + writer.typedArrayByteLengthDoubleResult(objId); + } + } else { + if (byteLength <= INT32_MAX) { + writer.resizableTypedArrayByteLengthInt32Result(objId); + } else { + writer.resizableTypedArrayByteLengthDoubleResult(objId); + } + } + trackAttached("GetProp.TypedArrayByteLength"); + } + writer.returnFromIC(); + + return AttachDecision::Attach; +} + +AttachDecision GetPropIRGenerator::tryAttachDataView(HandleObject obj, + ObjOperandId objId, + HandleId id) { + if (!obj->is<DataViewObject>()) { + return AttachDecision::NoAction; + } + auto* dv = &obj->as<DataViewObject>(); + + if (mode_ != ICState::Mode::Specialized) { + return AttachDecision::NoAction; + } + + // Receiver should be the object. + if (isSuper()) { + return AttachDecision::NoAction; + } + + bool isByteOffset = id.isAtom(cx_->names().byteOffset); + if (!isByteOffset && !id.isAtom(cx_->names().byteLength)) { + return AttachDecision::NoAction; + } + + // byteOffset and byteLength both throw when the ArrayBuffer is detached. + if (dv->hasDetachedBuffer()) { + // The has-attached-arraybuffer guard is elided for immutable views. Assert + // we never see an immutable view with a detached buffer. + MOZ_ASSERT(!dv->is<ImmutableDataViewObject>(), + "immutable data views can't have their buffer detached"); + return AttachDecision::NoAction; + } + + // byteOffset and byteLength both throw when the ArrayBuffer is out-of-bounds. + if (dv->is<ResizableDataViewObject>() && + dv->as<ResizableDataViewObject>().isOutOfBounds()) { + return AttachDecision::NoAction; + } + + NativeObject* holder = nullptr; + Maybe<PropertyInfo> prop; + NativeGetPropKind kind = + CanAttachNativeGetProp(cx_, obj, id, &holder, &prop, pc_); + if (kind != NativeGetPropKind::NativeGetter) { + return AttachDecision::NoAction; + } + + auto& fun = holder->getGetter(*prop)->as<JSFunction>(); + if (isByteOffset) { + if (!DataViewObject::isOriginalByteOffsetGetter(fun.native())) { + return AttachDecision::NoAction; + } + } else { + if (!DataViewObject::isOriginalByteLengthGetter(fun.native())) { + return AttachDecision::NoAction; + } + } + + maybeEmitIdGuard(id); + // Emit all the normal guards for calling this native, but specialize + // callNativeGetterResult. + emitCallGetterResultGuards(dv, holder, id, *prop, objId); + + // Immutable array buffers can never get detached. + if (!dv->is<ImmutableDataViewObject>()) { + writer.guardHasAttachedArrayBuffer(objId); + } else { +#ifdef DEBUG + // Add a guard in debug-mode, so if the buffer unexpectedly got detached, + // we bail out and rely on the above assertion to fire. + writer.guardHasAttachedArrayBuffer(objId); +#endif + } + + // Resizable array buffers can get out-of-bounds when shrunk. + if (dv->is<ResizableDataViewObject>()) { + writer.guardResizableArrayBufferViewInBounds(objId); + } + + if (isByteOffset) { + // byteOffset doesn't need to use different code paths for fixed-length, + // resizable, or immutable DataViews. + size_t byteOffset = dv->byteOffset().valueOr(0); + if (byteOffset <= INT32_MAX) { + writer.arrayBufferViewByteOffsetInt32Result(objId); + } else { + writer.arrayBufferViewByteOffsetDoubleResult(objId); + } + trackAttached("GetProp.DataViewByteOffset"); + } else { + size_t byteLength = dv->byteLength().valueOr(0); + if (!dv->is<ResizableDataViewObject>()) { + if (byteLength <= INT32_MAX) { + writer.loadArrayBufferViewLengthInt32Result(objId); + } else { + writer.loadArrayBufferViewLengthDoubleResult(objId); + } + } else { + if (byteLength <= INT32_MAX) { + writer.resizableDataViewByteLengthInt32Result(objId); + } else { + writer.resizableDataViewByteLengthDoubleResult(objId); + } + } + trackAttached("GetProp.DataViewByteLength"); + } + writer.returnFromIC(); + + return AttachDecision::Attach; +} + +AttachDecision GetPropIRGenerator::tryAttachArrayBufferMaybeShared( + HandleObject obj, ObjOperandId objId, HandleId id) { + if (!obj->is<ArrayBufferObjectMaybeShared>()) { + return AttachDecision::NoAction; + } + auto* buf = &obj->as<ArrayBufferObjectMaybeShared>(); + + if (mode_ != ICState::Mode::Specialized) { + return AttachDecision::NoAction; + } + + // Receiver should be the object. + if (isSuper()) { + return AttachDecision::NoAction; + } + + if (!id.isAtom(cx_->names().byteLength)) { + return AttachDecision::NoAction; + } + + NativeObject* holder = nullptr; + Maybe<PropertyInfo> prop; + NativeGetPropKind kind = + CanAttachNativeGetProp(cx_, obj, id, &holder, &prop, pc_); + if (kind != NativeGetPropKind::NativeGetter) { + return AttachDecision::NoAction; + } + + auto& fun = holder->getGetter(*prop)->as<JSFunction>(); + if (buf->is<ArrayBufferObject>()) { + if (!ArrayBufferObject::isOriginalByteLengthGetter(fun.native())) { + return AttachDecision::NoAction; + } + } else { + if (!SharedArrayBufferObject::isOriginalByteLengthGetter(fun.native())) { + return AttachDecision::NoAction; + } + } + + maybeEmitIdGuard(id); + // Emit all the normal guards for calling this native, but specialize + // callNativeGetterResult. + emitCallGetterResultGuards(buf, holder, id, *prop, objId); + if (!buf->is<GrowableSharedArrayBufferObject>()) { + if (buf->byteLength() <= INT32_MAX) { + writer.loadArrayBufferByteLengthInt32Result(objId); + } else { + writer.loadArrayBufferByteLengthDoubleResult(objId); + } + } else { + if (buf->byteLength() <= INT32_MAX) { + writer.growableSharedArrayBufferByteLengthInt32Result(objId); + } else { + writer.growableSharedArrayBufferByteLengthDoubleResult(objId); + } + } + writer.returnFromIC(); + + trackAttached("GetProp.ArrayBufferMaybeSharedByteLength"); + return AttachDecision::Attach; +} + +AttachDecision GetPropIRGenerator::tryAttachRegExp(HandleObject obj, + ObjOperandId objId, + HandleId id) { + if (!obj->is<RegExpObject>()) { + return AttachDecision::NoAction; + } + auto* regExp = &obj->as<RegExpObject>(); + + if (mode_ != ICState::Mode::Specialized) { + return AttachDecision::NoAction; + } + + // Receiver should be the object. + if (isSuper()) { + return AttachDecision::NoAction; + } + + NativeObject* holder = nullptr; + Maybe<PropertyInfo> prop; + NativeGetPropKind kind = + CanAttachNativeGetProp(cx_, obj, id, &holder, &prop, pc_); + if (kind != NativeGetPropKind::NativeGetter) { + return AttachDecision::NoAction; + } + + auto& fun = holder->getGetter(*prop)->as<JSFunction>(); + JS::RegExpFlags flags = JS::RegExpFlag::NoFlags; + if (!RegExpObject::isOriginalFlagGetter(fun.native(), &flags)) { + return AttachDecision::NoAction; + } + + maybeEmitIdGuard(id); + // Emit all the normal guards for calling this native, but specialize + // callNativeGetterResult. + emitCallGetterResultGuards(regExp, holder, id, *prop, objId); + + writer.regExpFlagResult(objId, flags.value()); + writer.returnFromIC(); + + trackAttached("GetProp.RegExpFlag"); + return AttachDecision::Attach; +} + +AttachDecision GetPropIRGenerator::tryAttachMap(HandleObject obj, + ObjOperandId objId, + HandleId id) { + if (!obj->is<MapObject>()) { + return AttachDecision::NoAction; + } + auto* mapObj = &obj->as<MapObject>(); + + if (mode_ != ICState::Mode::Specialized) { + return AttachDecision::NoAction; + } + + // Receiver should be the object. + if (isSuper()) { + return AttachDecision::NoAction; + } + + if (!id.isAtom(cx_->names().size)) { + return AttachDecision::NoAction; + } + + NativeObject* holder = nullptr; + Maybe<PropertyInfo> prop; + NativeGetPropKind kind = + CanAttachNativeGetProp(cx_, obj, id, &holder, &prop, pc_); + if (kind != NativeGetPropKind::NativeGetter) { + return AttachDecision::NoAction; + } + + auto& fun = holder->getGetter(*prop)->as<JSFunction>(); + if (!MapObject::isOriginalSizeGetter(fun.native())) { + return AttachDecision::NoAction; + } + + maybeEmitIdGuard(id); + + // Emit all the normal guards for calling this native, but specialize + // callNativeGetterResult. + emitCallGetterResultGuards(mapObj, holder, id, *prop, objId); + + writer.mapSizeResult(objId); + writer.returnFromIC(); + + trackAttached("GetProp.MapSize"); + return AttachDecision::Attach; +} + +AttachDecision GetPropIRGenerator::tryAttachSet(HandleObject obj, + ObjOperandId objId, + HandleId id) { + if (!obj->is<SetObject>()) { + return AttachDecision::NoAction; + } + auto* setObj = &obj->as<SetObject>(); + + if (mode_ != ICState::Mode::Specialized) { + return AttachDecision::NoAction; + } + + // Receiver should be the object. + if (isSuper()) { + return AttachDecision::NoAction; + } + + if (!id.isAtom(cx_->names().size)) { + return AttachDecision::NoAction; + } + + NativeObject* holder = nullptr; + Maybe<PropertyInfo> prop; + NativeGetPropKind kind = + CanAttachNativeGetProp(cx_, obj, id, &holder, &prop, pc_); + if (kind != NativeGetPropKind::NativeGetter) { + return AttachDecision::NoAction; + } + + auto& fun = holder->getGetter(*prop)->as<JSFunction>(); + if (!SetObject::isOriginalSizeGetter(fun.native())) { + return AttachDecision::NoAction; + } + + maybeEmitIdGuard(id); + + // Emit all the normal guards for calling this native, but specialize + // callNativeGetterResult. + emitCallGetterResultGuards(setObj, holder, id, *prop, objId); + + writer.setSizeResult(objId); + writer.returnFromIC(); + + trackAttached("GetProp.SetSize"); + return AttachDecision::Attach; } AttachDecision GetPropIRGenerator::tryAttachFunction(HandleObject obj, @@ -2637,44 +3016,49 @@ AttachDecision GetPropIRGenerator::tryAttachPrimitive(ValOperandId valId, Maybe<PropertyInfo> prop; NativeGetPropKind kind = CanAttachNativeGetProp(cx_, proto, id, &holder, &prop, pc_); - if (kind == NativeGetPropKind::None) { - return AttachDecision::NoAction; - } - - if (val_.isNumber()) { - writer.guardIsNumber(valId); - } else { - writer.guardNonDoubleType(valId, val_.type()); - } - maybeEmitIdGuard(id); - - auto* nproto = &proto->as<NativeObject>(); - ObjOperandId protoId = writer.loadObject(nproto); - switch (kind) { - case NativeGetPropKind::Missing: { - EmitMissingPropResult(writer, nproto, protoId); - writer.returnFromIC(); - - trackAttached("GetProp.PrimitiveMissing"); - return AttachDecision::Attach; - } + case NativeGetPropKind::None: + return AttachDecision::NoAction; + case NativeGetPropKind::Missing: case NativeGetPropKind::Slot: { - emitLoadDataPropertyResult(nproto, holder, id, *prop, protoId); - writer.returnFromIC(); + auto* nproto = &proto->as<NativeObject>(); - trackAttached("GetProp.PrimitiveSlot"); + if (val_.isNumber()) { + writer.guardIsNumber(valId); + } else { + writer.guardNonDoubleType(valId, val_.type()); + } + maybeEmitIdGuard(id); + + ObjOperandId protoId = writer.loadObject(nproto); + if (kind == NativeGetPropKind::Slot) { + emitLoadDataPropertyResult(nproto, holder, id, *prop, protoId); + writer.returnFromIC(); + trackAttached("GetProp.PrimitiveSlot"); + } else { + EmitMissingPropResult(writer, nproto, protoId); + writer.returnFromIC(); + trackAttached("GetProp.PrimitiveMissing"); + } return AttachDecision::Attach; } case NativeGetPropKind::ScriptedGetter: case NativeGetPropKind::NativeGetter: { + auto* nproto = &proto->as<NativeObject>(); + + if (val_.isNumber()) { + writer.guardIsNumber(valId); + } else { + writer.guardNonDoubleType(valId, val_.type()); + } + maybeEmitIdGuard(id); + + ObjOperandId protoId = writer.loadObject(nproto); emitCallGetterResult(kind, nproto, holder, id, *prop, protoId, valId); trackAttached("GetProp.PrimitiveGetter"); return AttachDecision::Attach; } - case NativeGetPropKind::None: - break; } MOZ_CRASH("Bad NativeGetPropKind"); @@ -6129,11 +6513,12 @@ void OptimizeSpreadCallIRGenerator::trackAttached(const char* name) { } CallIRGenerator::CallIRGenerator(JSContext* cx, HandleScript script, - jsbytecode* pc, ICState state, + jsbytecode* pc, JSOp op, ICState state, BaselineFrame* frame, uint32_t argc, HandleValue callee, HandleValue thisval, HandleValue newTarget, HandleValueArray args) : IRGenerator(cx, script, pc, CacheKind::Call, state, frame), + op_(op), argc_(argc), callee_(callee), thisval_(thisval), @@ -6149,21 +6534,37 @@ BoundFunctionObject* InlinableNativeIRGenerator::boundCallee() const { return &callee()->as<BoundFunctionObject>(); } +bool InlinableNativeIRGenerator::isTargetBoundFunction() const { + switch (flags_.getArgFormat()) { + case CallFlags::Standard: + case CallFlags::Spread: + return false; + case CallFlags::FunCall: + case CallFlags::FunApplyArgsObj: + case CallFlags::FunApplyArray: + case CallFlags::FunApplyNullUndefined: + if (callee()->is<JSFunction>()) { + MOZ_ASSERT(generator_.thisval_.isObject()); + return generator_.thisval_.toObject().is<BoundFunctionObject>(); + } + return false; + case CallFlags::Unknown: + break; + } + MOZ_CRASH("Unsupported arg format"); +} + +BoundFunctionObject* InlinableNativeIRGenerator::boundTarget() const { + MOZ_ASSERT(isTargetBoundFunction()); + return &generator_.thisval_.toObject().as<BoundFunctionObject>(); +} + ObjOperandId InlinableNativeIRGenerator::emitNativeCalleeGuard( Int32OperandId argcId) { // Note: we rely on GuardSpecificFunction to also guard against the same // native from a different realm. MOZ_ASSERT(target_->isNativeWithoutJitEntry()); - // Guards already emitted in GetPropIRGenerator::emitCallGetterResultGuards. - if (isAccessorOp()) { - MOZ_ASSERT(flags_.getArgFormat() == CallFlags::Standard); - MOZ_ASSERT(!flags_.isConstructing()); - MOZ_ASSERT(!isCalleeBoundFunction()); - MOZ_ASSERT(!isTargetBoundFunction()); - return ObjOperandId(); - } - ValOperandId calleeValId; switch (flags_.getArgFormat()) { case CallFlags::Standard: @@ -6327,9 +6728,7 @@ ObjOperandId InlinableNativeIRGenerator::emitLoadArgsArray() { } MOZ_ASSERT(flags_.getArgFormat() == CallFlags::FunApplyArray); - return gen_.as<CallIRGenerator*>() - ->emitFunApplyArgsGuard(flags_.getArgFormat()) - .ref(); + return generator_.emitFunApplyArgsGuard(flags_.getArgFormat()).ref(); } ValOperandId InlinableNativeIRGenerator::loadBoundArgument( @@ -6351,15 +6750,6 @@ ValOperandId InlinableNativeIRGenerator::loadBoundArgument( } ValOperandId InlinableNativeIRGenerator::loadThis(ObjOperandId calleeId) { - // Accessor operations use the receiver as their |this| value. - if (isAccessorOp()) { - MOZ_ASSERT(flags_.getArgFormat() == CallFlags::Standard); - MOZ_ASSERT(!isTargetBoundFunction()); - MOZ_ASSERT(!isCalleeBoundFunction()); - MOZ_ASSERT(receiverId_.valid()); - return receiverId_; - } - switch (flags_.getArgFormat()) { case CallFlags::Standard: case CallFlags::Spread: @@ -6439,7 +6829,6 @@ ValOperandId InlinableNativeIRGenerator::loadArgument(ObjOperandId calleeId, flags_.getArgFormat() == CallFlags::FunApplyNullUndefined); MOZ_ASSERT_IF(flags_.getArgFormat() == CallFlags::FunApplyNullUndefined, isTargetBoundFunction() && hasBoundArguments()); - MOZ_ASSERT(!isAccessorOp(), "get property operations don't have arguments"); // Check if the |this| value is stored in the bound arguments. bool thisFromBoundArgs = flags_.getArgFormat() == CallFlags::FunCall && @@ -6804,7 +7193,6 @@ AttachDecision InlinableNativeIRGenerator::tryAttachArraySlice() { if (isPackedArray) { emitOptimisticClassGuard(objId, &thisval_.toObject(), GuardClassKind::Array); - writer.guardArrayIsPacked(objId); } else { auto* args = &thisval_.toObject().as<ArgumentsObject>(); @@ -6919,11 +7307,14 @@ AttachDecision InlinableNativeIRGenerator::tryAttachDataViewGet( ObjOperandId objId = writer.guardToObject(thisValId); if (dv->is<FixedLengthDataViewObject>()) { - emitOptimisticClassGuard(objId, dv, GuardClassKind::FixedLengthDataView); + emitOptimisticClassGuard(objId, &thisval_.toObject(), + GuardClassKind::FixedLengthDataView); } else if (dv->is<ImmutableDataViewObject>()) { - emitOptimisticClassGuard(objId, dv, GuardClassKind::ImmutableDataView); + emitOptimisticClassGuard(objId, &thisval_.toObject(), + GuardClassKind::ImmutableDataView); } else { - emitOptimisticClassGuard(objId, dv, GuardClassKind::ResizableDataView); + emitOptimisticClassGuard(objId, &thisval_.toObject(), + GuardClassKind::ResizableDataView); } // Convert offset to intPtr. @@ -6994,197 +7385,39 @@ AttachDecision InlinableNativeIRGenerator::tryAttachDataViewSet( // Guard |this| is a DataViewObject. ValOperandId thisValId = loadThis(calleeId); ObjOperandId objId = writer.guardToObject(thisValId); - - if (dv->is<FixedLengthDataViewObject>()) { - emitOptimisticClassGuard(objId, dv, GuardClassKind::FixedLengthDataView); - } else { - emitOptimisticClassGuard(objId, dv, GuardClassKind::ResizableDataView); - } - - // Convert offset to intPtr. - ValOperandId offsetId = loadArgument(calleeId, ArgumentKind::Arg0); - IntPtrOperandId intPtrOffsetId = - guardToIntPtrIndex(args_[0], offsetId, /* supportOOB = */ false); - - // Convert value to number or BigInt. - ValOperandId valueId = loadArgument(calleeId, ArgumentKind::Arg1); - OperandId numericValueId = emitNumericGuard(valueId, args_[1], type); - - BooleanOperandId boolLittleEndianId; - if (args_.length() > 2) { - ValOperandId littleEndianId = loadArgument(calleeId, ArgumentKind::Arg2); - boolLittleEndianId = writer.guardToBoolean(littleEndianId); - } else { - boolLittleEndianId = writer.loadBooleanConstant(false); - } - - auto viewKind = ToArrayBufferViewKind(dv); - writer.storeDataViewValueResult(objId, intPtrOffsetId, numericValueId, - boolLittleEndianId, type, viewKind); - - writer.returnFromIC(); - - trackAttached("DataViewSet"); - return AttachDecision::Attach; -} - -AttachDecision InlinableNativeIRGenerator::tryAttachDataViewByteLength() { - // Expecting no arguments. - if (args_.length() != 0) { - return AttachDecision::NoAction; - } - - // Ensure |this| is a DataViewObject. - if (!thisval_.isObject() || !thisval_.toObject().is<DataViewObject>()) { - return AttachDecision::NoAction; - } - - auto* dv = &thisval_.toObject().as<DataViewObject>(); - - // byteLength throws when the ArrayBuffer is detached. - if (dv->hasDetachedBuffer()) { - // The has-attached-arraybuffer guard is elided for immutable views. Assert - // we never see an immutable view with a detached buffer. - MOZ_ASSERT(!dv->is<ImmutableDataViewObject>(), - "immutable data views can't have their buffer detached"); - return AttachDecision::NoAction; - } - - // byteLength throws when the ArrayBuffer is out-of-bounds. - if (dv->is<ResizableDataViewObject>() && - dv->as<ResizableDataViewObject>().isOutOfBounds()) { - return AttachDecision::NoAction; - } - - // Initialize the input operand. - Int32OperandId argcId = initializeInputOperand(); - - // Guard callee is the 'byteLength' native function. - ObjOperandId calleeId = emitNativeCalleeGuard(argcId); - - // Guard |this| is a DataViewObject. - ValOperandId thisValId = loadThis(calleeId); - ObjOperandId objId = writer.guardToObject(thisValId); - - if (dv->is<FixedLengthDataViewObject>()) { - emitOptimisticClassGuard(objId, dv, GuardClassKind::FixedLengthDataView); - } else if (dv->is<ImmutableDataViewObject>()) { - emitOptimisticClassGuard(objId, dv, GuardClassKind::ImmutableDataView); - } else { - emitOptimisticClassGuard(objId, dv, GuardClassKind::ResizableDataView); - } - - // Immutable array buffers can never get detached. - if (!dv->is<ImmutableDataViewObject>()) { - writer.guardHasAttachedArrayBuffer(objId); - } else { -#ifdef DEBUG - // Add a guard in debug-mode, so if the buffer unexpectedly got detached, - // we bail out and rely on the above assertion to fire. - writer.guardHasAttachedArrayBuffer(objId); -#endif - } - - // Resizable array buffers can get out-of-bounds when shrunk. - if (dv->is<ResizableDataViewObject>()) { - writer.guardResizableArrayBufferViewInBounds(objId); - } - - size_t byteLength = dv->byteLength().valueOr(0); - if (!dv->is<ResizableDataViewObject>()) { - if (byteLength <= INT32_MAX) { - writer.loadArrayBufferViewLengthInt32Result(objId); - } else { - writer.loadArrayBufferViewLengthDoubleResult(objId); - } - } else { - if (byteLength <= INT32_MAX) { - writer.resizableDataViewByteLengthInt32Result(objId); - } else { - writer.resizableDataViewByteLengthDoubleResult(objId); - } - } - - writer.returnFromIC(); - - trackAttached("DataViewByteLength"); - return AttachDecision::Attach; -} - -AttachDecision InlinableNativeIRGenerator::tryAttachDataViewByteOffset() { - // Expecting no arguments. - if (args_.length() != 0) { - return AttachDecision::NoAction; - } - - // Ensure |this| is a DataViewObject. - if (!thisval_.isObject() || !thisval_.toObject().is<DataViewObject>()) { - return AttachDecision::NoAction; - } - - auto* dv = &thisval_.toObject().as<DataViewObject>(); - - // byteOffset throws when the ArrayBuffer is detached. - if (dv->hasDetachedBuffer()) { - // The has-attached-arraybuffer guard is elided for immutable views. Assert - // we never see an immutable view with a detached buffer. - MOZ_ASSERT(!dv->is<ImmutableDataViewObject>(), - "immutable data views can't have their buffer detached"); - return AttachDecision::NoAction; - } - - // byteOffset throws when the ArrayBuffer is out-of-bounds. - if (dv->is<ResizableDataViewObject>() && - dv->as<ResizableDataViewObject>().isOutOfBounds()) { - return AttachDecision::NoAction; - } - - // Initialize the input operand. - Int32OperandId argcId = initializeInputOperand(); - - // Guard callee is the 'byteLength' native function. - ObjOperandId calleeId = emitNativeCalleeGuard(argcId); - - // Guard |this| is a DataViewObject. - ValOperandId thisValId = loadThis(calleeId); - ObjOperandId objId = writer.guardToObject(thisValId); - - if (dv->is<FixedLengthDataViewObject>()) { - emitOptimisticClassGuard(objId, dv, GuardClassKind::FixedLengthDataView); - } else if (dv->is<ImmutableDataViewObject>()) { - emitOptimisticClassGuard(objId, dv, GuardClassKind::ImmutableDataView); - } else { - emitOptimisticClassGuard(objId, dv, GuardClassKind::ResizableDataView); - } - - // Immutable array buffers can never get detached. - if (!dv->is<ImmutableDataViewObject>()) { - writer.guardHasAttachedArrayBuffer(objId); + + if (dv->is<FixedLengthDataViewObject>()) { + emitOptimisticClassGuard(objId, &thisval_.toObject(), + GuardClassKind::FixedLengthDataView); } else { -#ifdef DEBUG - // Add a guard in debug-mode, so if the buffer unexpectedly got detached, - // we bail out and rely on the above assertion to fire. - writer.guardHasAttachedArrayBuffer(objId); -#endif + emitOptimisticClassGuard(objId, &thisval_.toObject(), + GuardClassKind::ResizableDataView); } - // Resizable array buffers can get out-of-bounds when shrunk. - if (dv->is<ResizableDataViewObject>()) { - writer.guardResizableArrayBufferViewInBounds(objId); - } + // Convert offset to intPtr. + ValOperandId offsetId = loadArgument(calleeId, ArgumentKind::Arg0); + IntPtrOperandId intPtrOffsetId = + guardToIntPtrIndex(args_[0], offsetId, /* supportOOB = */ false); + + // Convert value to number or BigInt. + ValOperandId valueId = loadArgument(calleeId, ArgumentKind::Arg1); + OperandId numericValueId = emitNumericGuard(valueId, args_[1], type); - // byteOffset doesn't need to use different code paths for fixed-length, - // resizable, or immutable DataViews. - size_t byteOffset = dv->byteOffset().valueOr(0); - if (byteOffset <= INT32_MAX) { - writer.arrayBufferViewByteOffsetInt32Result(objId); + BooleanOperandId boolLittleEndianId; + if (args_.length() > 2) { + ValOperandId littleEndianId = loadArgument(calleeId, ArgumentKind::Arg2); + boolLittleEndianId = writer.guardToBoolean(littleEndianId); } else { - writer.arrayBufferViewByteOffsetDoubleResult(objId); + boolLittleEndianId = writer.loadBooleanConstant(false); } + auto viewKind = ToArrayBufferViewKind(dv); + writer.storeDataViewValueResult(objId, intPtrOffsetId, numericValueId, + boolLittleEndianId, type, viewKind); + writer.returnFromIC(); - trackAttached("DataViewByteOffset"); + trackAttached("DataViewSet"); return AttachDecision::Attach; } @@ -7702,38 +7935,6 @@ AttachDecision InlinableNativeIRGenerator::tryAttachHasClass( return AttachDecision::Attach; } -AttachDecision InlinableNativeIRGenerator::tryAttachRegExpFlag( - JS::RegExpFlags flags) { - // Expecting no arguments. - if (args_.length() != 0) { - return AttachDecision::NoAction; - } - - // Ensure |this| is a RegExpObject. - if (!thisval_.isObject() || !thisval_.toObject().is<RegExpObject>()) { - return AttachDecision::NoAction; - } - - auto* regExp = &thisval_.toObject().as<RegExpObject>(); - - // Initialize the input operand. - Int32OperandId argcId = initializeInputOperand(); - - // Guard callee is the native RegExp getter function. - ObjOperandId calleeId = emitNativeCalleeGuard(argcId); - - // Guard |this| is a RegExpObject. - ValOperandId thisValId = loadThis(calleeId); - ObjOperandId objId = writer.guardToObject(thisValId); - writer.guardShapeForClass(objId, regExp->shape()); - - writer.regExpFlagResult(objId, flags.value()); - writer.returnFromIC(); - - trackAttached("RegExpFlag"); - return AttachDecision::Attach; -} - // Returns whether the .lastIndex property is a non-negative int32 value and is // still writable. static bool HasOptimizableLastIndexSlot(RegExpObject* regexp, JSContext* cx) { @@ -10724,7 +10925,7 @@ AttachDecision InlinableNativeIRGenerator::tryAttachSetSize() { // Guard |this| is a SetObject. ValOperandId thisValId = loadThis(calleeId); ObjOperandId objId = writer.guardToObject(thisValId); - emitOptimisticClassGuard(objId, &thisval_.toObject(), GuardClassKind::Set); + writer.guardClass(objId, GuardClassKind::Set); writer.setSizeResult(objId); writer.returnFromIC(); @@ -10966,35 +11167,6 @@ AttachDecision InlinableNativeIRGenerator::tryAttachMapSet() { return AttachDecision::Attach; } -AttachDecision InlinableNativeIRGenerator::tryAttachMapSize() { - // Ensure |this| is a MapObject. - if (!thisval_.isObject() || !thisval_.toObject().is<MapObject>()) { - return AttachDecision::NoAction; - } - - // Expecting no arguments. - if (args_.length() != 0) { - return AttachDecision::NoAction; - } - - // Initialize the input operand. - Int32OperandId argcId = initializeInputOperand(); - - // Guard callee is the 'size' native function. - ObjOperandId calleeId = emitNativeCalleeGuard(argcId); - - // Guard |this| is a MapObject. - ValOperandId thisValId = loadThis(calleeId); - ObjOperandId objId = writer.guardToObject(thisValId); - emitOptimisticClassGuard(objId, &thisval_.toObject(), GuardClassKind::Map); - - writer.mapSizeResult(objId); - writer.returnFromIC(); - - trackAttached("MapSize"); - return AttachDecision::Attach; -} - AttachDecision InlinableNativeIRGenerator::tryAttachWeakMapGet() { // Ensure |this| is a WeakMapObject. if (!thisval_.isObject() || !thisval_.toObject().is<WeakMapObject>()) { @@ -11254,8 +11426,8 @@ AttachDecision CallIRGenerator::tryAttachFunCall(HandleFunction callee) { : HandleValueArray::empty(); // Check for specific native-function optimizations. - InlinableNativeIRGenerator nativeGen(*this, callee, target, newTarget, - thisValue, args, targetFlags); + InlinableNativeIRGenerator nativeGen(*this, target, newTarget, thisValue, + args, targetFlags); TRY_ATTACH(nativeGen.tryAttachStub()); } @@ -11270,7 +11442,7 @@ AttachDecision CallIRGenerator::tryAttachFunCall(HandleFunction callee) { writer.callScriptedFunction(thisObjId, argcId, targetFlags, ClampFixedArgc(argc_)); } else { - writer.callNativeFunction(thisObjId, argcId, jsop(), target, targetFlags, + writer.callNativeFunction(thisObjId, argcId, op_, target, targetFlags, ClampFixedArgc(argc_)); } } else { @@ -11628,135 +11800,6 @@ AttachDecision InlinableNativeIRGenerator::tryAttachTypedArraySubarray() { return AttachDecision::Attach; } -AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayLength() { - // Expecting no arguments. - if (args_.length() != 0) { - return AttachDecision::NoAction; - } - - // Ensure |this| is a TypedArrayObject. - if (!thisval_.isObject() || !thisval_.toObject().is<TypedArrayObject>()) { - return AttachDecision::NoAction; - } - - auto* tarr = &thisval_.toObject().as<TypedArrayObject>(); - - // Initialize the input operand. - Int32OperandId argcId = initializeInputOperand(); - - // Guard callee is the 'length' native function. - ObjOperandId calleeId = emitNativeCalleeGuard(argcId); - - // Guard |this| is a TypedArrayObject. - ValOperandId thisValId = loadThis(calleeId); - ObjOperandId objId = writer.guardToObject(thisValId); - writer.guardShapeForClass(objId, tarr->shape()); - - size_t length = tarr->length().valueOr(0); - if (!tarr->is<ResizableTypedArrayObject>()) { - if (length <= INT32_MAX) { - writer.loadArrayBufferViewLengthInt32Result(objId); - } else { - writer.loadArrayBufferViewLengthDoubleResult(objId); - } - } else { - if (length <= INT32_MAX) { - writer.resizableTypedArrayLengthInt32Result(objId); - } else { - writer.resizableTypedArrayLengthDoubleResult(objId); - } - } - - writer.returnFromIC(); - - trackAttached("TypedArrayLength"); - return AttachDecision::Attach; -} - -AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayByteLength() { - // Expecting no arguments. - if (args_.length() != 0) { - return AttachDecision::NoAction; - } - - // Ensure |this| is a TypedArrayObject. - if (!thisval_.isObject() || !thisval_.toObject().is<TypedArrayObject>()) { - return AttachDecision::NoAction; - } - - auto* tarr = &thisval_.toObject().as<TypedArrayObject>(); - - // Initialize the input operand. - Int32OperandId argcId = initializeInputOperand(); - - // Guard callee is the 'byteLength' native function. - ObjOperandId calleeId = emitNativeCalleeGuard(argcId); - - // Guard |this| is a TypedArrayObject. - ValOperandId thisValId = loadThis(calleeId); - ObjOperandId objId = writer.guardToObject(thisValId); - writer.guardShapeForClass(objId, tarr->shape()); - - size_t byteLength = tarr->byteLength().valueOr(0); - if (!tarr->is<ResizableTypedArrayObject>()) { - if (byteLength <= INT32_MAX) { - writer.typedArrayByteLengthInt32Result(objId); - } else { - writer.typedArrayByteLengthDoubleResult(objId); - } - } else { - if (byteLength <= INT32_MAX) { - writer.resizableTypedArrayByteLengthInt32Result(objId); - } else { - writer.resizableTypedArrayByteLengthDoubleResult(objId); - } - } - - writer.returnFromIC(); - - trackAttached("TypedArrayByteLength"); - return AttachDecision::Attach; -} - -AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayByteOffset() { - // Expecting no arguments. - if (args_.length() != 0) { - return AttachDecision::NoAction; - } - - // Ensure |this| is a TypedArrayObject. - if (!thisval_.isObject() || !thisval_.toObject().is<TypedArrayObject>()) { - return AttachDecision::NoAction; - } - - auto* tarr = &thisval_.toObject().as<TypedArrayObject>(); - - // Initialize the input operand. - Int32OperandId argcId = initializeInputOperand(); - - // Guard callee is the 'byteLength' native function. - ObjOperandId calleeId = emitNativeCalleeGuard(argcId); - - // Guard |this| is a TypedArrayObject. - ValOperandId thisValId = loadThis(calleeId); - ObjOperandId objId = writer.guardToObject(thisValId); - writer.guardShapeForClass(objId, tarr->shape()); - - // byteOffset doesn't need to use different code paths for fixed-length and - // resizable TypedArrays. - size_t byteOffset = tarr->byteOffset().valueOr(0); - if (byteOffset <= INT32_MAX) { - writer.arrayBufferViewByteOffsetInt32Result(objId); - } else { - writer.arrayBufferViewByteOffsetDoubleResult(objId); - } - - writer.returnFromIC(); - - trackAttached("TypedArrayByteOffset"); - return AttachDecision::Attach; -} - AttachDecision InlinableNativeIRGenerator::tryAttachIsTypedArray( bool isPossiblyWrapped) { // Self-hosted code calls this with a single object argument. @@ -11857,101 +11900,6 @@ AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayLength( return AttachDecision::Attach; } -AttachDecision InlinableNativeIRGenerator::tryAttachArrayBufferByteLength() { - // Expecting no arguments. - if (args_.length() != 0) { - return AttachDecision::NoAction; - } - - // Ensure |this| is an ArrayBufferObject. - if (!thisval_.isObject() || !thisval_.toObject().is<ArrayBufferObject>()) { - return AttachDecision::NoAction; - } - - auto* buf = &thisval_.toObject().as<ArrayBufferObject>(); - - // Initialize the input operand. - Int32OperandId argcId = initializeInputOperand(); - - // Guard callee is the 'byteLength' native function. - ObjOperandId calleeId = emitNativeCalleeGuard(argcId); - - // Guard |this| is an ArrayBufferObject. - ValOperandId thisValId = loadThis(calleeId); - ObjOperandId objId = writer.guardToObject(thisValId); - - if (buf->is<FixedLengthArrayBufferObject>()) { - emitOptimisticClassGuard(objId, buf, - GuardClassKind::FixedLengthArrayBuffer); - } else if (buf->is<ImmutableArrayBufferObject>()) { - emitOptimisticClassGuard(objId, buf, GuardClassKind::ImmutableArrayBuffer); - } else { - emitOptimisticClassGuard(objId, buf, GuardClassKind::ResizableArrayBuffer); - } - - if (buf->byteLength() <= INT32_MAX) { - writer.loadArrayBufferByteLengthInt32Result(objId); - } else { - writer.loadArrayBufferByteLengthDoubleResult(objId); - } - - writer.returnFromIC(); - - trackAttached("ArrayBufferByteLength"); - return AttachDecision::Attach; -} - -AttachDecision -InlinableNativeIRGenerator::tryAttachSharedArrayBufferByteLength() { - // Expecting no arguments. - if (args_.length() != 0) { - return AttachDecision::NoAction; - } - - // Ensure |this| is a SharedArrayBufferObject. - if (!thisval_.isObject() || - !thisval_.toObject().is<SharedArrayBufferObject>()) { - return AttachDecision::NoAction; - } - - auto* buf = &thisval_.toObject().as<SharedArrayBufferObject>(); - - // Initialize the input operand. - Int32OperandId argcId = initializeInputOperand(); - - // Guard callee is the 'byteLength' native function. - ObjOperandId calleeId = emitNativeCalleeGuard(argcId); - - // Guard |this| is a SharedArrayBufferObject. - ValOperandId thisValId = loadThis(calleeId); - ObjOperandId objId = writer.guardToObject(thisValId); - - if (buf->is<FixedLengthSharedArrayBufferObject>()) { - emitOptimisticClassGuard(objId, buf, - GuardClassKind::FixedLengthSharedArrayBuffer); - - if (buf->byteLength() <= INT32_MAX) { - writer.loadArrayBufferByteLengthInt32Result(objId); - } else { - writer.loadArrayBufferByteLengthDoubleResult(objId); - } - } else { - emitOptimisticClassGuard(objId, buf, - GuardClassKind::GrowableSharedArrayBuffer); - - if (buf->byteLength() <= INT32_MAX) { - writer.growableSharedArrayBufferByteLengthInt32Result(objId); - } else { - writer.growableSharedArrayBufferByteLengthDoubleResult(objId); - } - } - - writer.returnFromIC(); - - trackAttached("SharedArrayBufferByteLength"); - return AttachDecision::Attach; -} - AttachDecision InlinableNativeIRGenerator::tryAttachIsConstructing() { // Self-hosted code calls this with no arguments in function scripts. MOZ_ASSERT(args_.length() == 0); @@ -12144,11 +12092,6 @@ AttachDecision InlinableNativeIRGenerator::tryAttachObjectConstructor() { gc::AllocSite* site = nullptr; PlainObject* templateObj = nullptr; if (args_.length() == 0) { - // Don't optimize if we can't create an alloc-site. - if (!BytecodeOpCanHaveAllocSite(op())) { - return AttachDecision::NoAction; - } - // Stub doesn't support metadata builder if (cx_->realm()->hasAllocationMetadataBuilder()) { return AttachDecision::NoAction; @@ -12200,11 +12143,6 @@ AttachDecision InlinableNativeIRGenerator::tryAttachObjectConstructor() { } AttachDecision InlinableNativeIRGenerator::tryAttachArrayConstructor() { - // Don't optimize if we can't create an alloc-site. - if (!BytecodeOpCanHaveAllocSite(op())) { - return AttachDecision::NoAction; - } - // Only optimize the |Array()| and |Array(n)| cases (with or without |new|) // for now. Note that self-hosted code calls this without |new| via std_Array. if (args_.length() > 1) { @@ -12766,8 +12704,8 @@ AttachDecision CallIRGenerator::tryAttachFunApply(HandleFunction calleeFunc) { aobj->length(), aobj->getDenseElements()); // Check for specific native-function optimizations. - InlinableNativeIRGenerator nativeGen(*this, calleeFunc, target, newTarget, - thisValue, args, targetFlags); + InlinableNativeIRGenerator nativeGen(*this, target, newTarget, thisValue, + args, targetFlags); TRY_ATTACH(nativeGen.tryAttachStub()); } if (format == CallFlags::FunApplyArray && @@ -12785,8 +12723,8 @@ AttachDecision CallIRGenerator::tryAttachFunApply(HandleFunction calleeFunc) { HandleValueArray args = HandleValueArray::empty(); // Check for specific native-function optimizations. - InlinableNativeIRGenerator nativeGen(*this, calleeFunc, target, newTarget, - thisValue, args, targetFlags); + InlinableNativeIRGenerator nativeGen(*this, target, newTarget, thisValue, + args, targetFlags); TRY_ATTACH(nativeGen.tryAttachStub()); } @@ -12816,7 +12754,7 @@ AttachDecision CallIRGenerator::tryAttachFunApply(HandleFunction calleeFunc) { if (isScripted) { writer.callScriptedFunction(thisObjId, argcId, targetFlags, fixedArgc); } else { - writer.callNativeFunction(thisObjId, argcId, jsop(), target, targetFlags, + writer.callNativeFunction(thisObjId, argcId, op_, target, targetFlags, fixedArgc); } } else { @@ -12986,8 +12924,8 @@ AttachDecision CallIRGenerator::tryAttachInlinableNative(HandleFunction callee, MOZ_ASSERT(flags.getArgFormat() == CallFlags::Standard || flags.getArgFormat() == CallFlags::Spread); - InlinableNativeIRGenerator nativeGen(*this, callee, callee, newTarget_, - thisval_, args_, flags); + InlinableNativeIRGenerator nativeGen(*this, callee, newTarget_, thisval_, + args_, flags); return nativeGen.tryAttachStub(); } @@ -13018,8 +12956,7 @@ AttachDecision InlinableNativeIRGenerator::tryAttachStub() { MOZ_ASSERT(target_->isNativeWithoutJitEntry()); // Special case functions are only optimized for normal calls. - if (!BytecodeCallOpCanHaveInlinableNative(op()) && - !BytecodeGetOpCanHaveInlinableNative(op())) { + if (!BytecodeCallOpCanHaveInlinableNative(op())) { return AttachDecision::NoAction; } @@ -13150,10 +13087,6 @@ AttachDecision InlinableNativeIRGenerator::tryAttachStub() { return tryAttachDataViewSet(Scalar::BigInt64); case InlinableNative::DataViewSetBigUint64: return tryAttachDataViewSet(Scalar::BigUint64); - case InlinableNative::DataViewByteLength: - return tryAttachDataViewByteLength(); - case InlinableNative::DataViewByteOffset: - return tryAttachDataViewByteOffset(); // Function natives. case InlinableNative::FunctionBind: @@ -13235,22 +13168,6 @@ AttachDecision InlinableNativeIRGenerator::tryAttachStub() { return tryAttachArrayIteratorPrototypeOptimizable(); // RegExp natives. - case InlinableNative::RegExpDotAll: - return tryAttachRegExpFlag(JS::RegExpFlag::DotAll); - case InlinableNative::RegExpGlobal: - return tryAttachRegExpFlag(JS::RegExpFlag::Global); - case InlinableNative::RegExpHasIndices: - return tryAttachRegExpFlag(JS::RegExpFlag::HasIndices); - case InlinableNative::RegExpIgnoreCase: - return tryAttachRegExpFlag(JS::RegExpFlag::IgnoreCase); - case InlinableNative::RegExpMultiline: - return tryAttachRegExpFlag(JS::RegExpFlag::Multiline); - case InlinableNative::RegExpSticky: - return tryAttachRegExpFlag(JS::RegExpFlag::Sticky); - case InlinableNative::RegExpUnicode: - return tryAttachRegExpFlag(JS::RegExpFlag::Unicode); - case InlinableNative::RegExpUnicodeSets: - return tryAttachRegExpFlag(JS::RegExpFlag::UnicodeSets); case InlinableNative::IsRegExpObject: return tryAttachHasClass(&RegExpObject::class_, /* isPossiblyWrapped = */ false); @@ -13449,12 +13366,6 @@ AttachDecision InlinableNativeIRGenerator::tryAttachStub() { return tryAttachTypedArraySet(); case InlinableNative::TypedArraySubarray: return tryAttachTypedArraySubarray(); - case InlinableNative::TypedArrayLength: - return tryAttachTypedArrayLength(); - case InlinableNative::TypedArrayByteLength: - return tryAttachTypedArrayByteLength(); - case InlinableNative::TypedArrayByteOffset: - return tryAttachTypedArrayByteOffset(); // TypedArray intrinsics. case InlinableNative::IntrinsicIsTypedArray: @@ -13531,8 +13442,6 @@ AttachDecision InlinableNativeIRGenerator::tryAttachStub() { return tryAttachMapDelete(); case InlinableNative::MapSet: return tryAttachMapSet(); - case InlinableNative::MapSize: - return tryAttachMapSize(); // Date natives and intrinsics. case InlinableNative::DateGetTime: @@ -13560,14 +13469,6 @@ AttachDecision InlinableNativeIRGenerator::tryAttachStub() { case InlinableNative::WeakSetHas: return tryAttachWeakSetHas(); - // ArrayBuffer natives. - case InlinableNative::ArrayBufferByteLength: - return tryAttachArrayBufferByteLength(); - - // SharedArrayBuffer natives. - case InlinableNative::SharedArrayBufferByteLength: - return tryAttachSharedArrayBufferByteLength(); - // Testing functions. case InlinableNative::TestBailout: if (js::SupportDifferentialTesting()) { @@ -13854,7 +13755,7 @@ AttachDecision CallIRGenerator::tryAttachCallNative(HandleFunction calleeFunc) { } else if (isSpecialized) { // Ensure callee matches this stub's callee writer.guardSpecificFunction(calleeObjId, calleeFunc); - writer.callNativeFunction(calleeObjId, argcId, jsop(), calleeFunc, flags, + writer.callNativeFunction(calleeObjId, argcId, op_, calleeFunc, flags, ClampFixedArgc(argc_)); trackAttached("Call.CallNative"); @@ -14102,20 +14003,16 @@ AttachDecision CallIRGenerator::tryAttachBoundNative( auto args = numBoundArgs != 0 ? concatenatedArgs : args_; // Check for specific native-function optimizations. - InlinableNativeIRGenerator nativeGen(*this, calleeObj, target, newTarget_, - thisValue, args, flags); + InlinableNativeIRGenerator nativeGen(*this, target, newTarget_, thisValue, + args, flags); return nativeGen.tryAttachStub(); } -static bool IsInlinableFunCallOrApply(JSOp op) { - return op == JSOp::Call || op == JSOp::CallContent || - op == JSOp::CallIgnoresRv; -} - AttachDecision CallIRGenerator::tryAttachBoundFunCall( Handle<BoundFunctionObject*> calleeObj) { // Only optimize fun_call for simple calls. - if (!IsInlinableFunCallOrApply(jsop())) { + if (op_ != JSOp::Call && op_ != JSOp::CallContent && + op_ != JSOp::CallIgnoresRv) { return AttachDecision::NoAction; } @@ -14211,15 +14108,16 @@ AttachDecision CallIRGenerator::tryAttachBoundFunCall( })(); // Check for specific native-function optimizations. - InlinableNativeIRGenerator nativeGen(*this, calleeObj, target, newTarget, - thisValue, args, targetFlags); + InlinableNativeIRGenerator nativeGen(*this, target, newTarget, thisValue, + args, targetFlags); return nativeGen.tryAttachStub(); } AttachDecision CallIRGenerator::tryAttachBoundFunApply( Handle<BoundFunctionObject*> calleeObj) { // Only optimize fun_apply for simple calls. - if (!IsInlinableFunCallOrApply(jsop())) { + if (op_ != JSOp::Call && op_ != JSOp::CallContent && + op_ != JSOp::CallIgnoresRv) { return AttachDecision::NoAction; } @@ -14298,8 +14196,8 @@ AttachDecision CallIRGenerator::tryAttachBoundFunApply( HandleValueArray args = HandleValueArray::empty(); // Check for specific native-function optimizations. - InlinableNativeIRGenerator nativeGen(*this, calleeObj, target, newTarget, - thisValue, args, targetFlags); + InlinableNativeIRGenerator nativeGen(*this, target, newTarget, thisValue, + args, targetFlags); return nativeGen.tryAttachStub(); } @@ -14382,8 +14280,8 @@ AttachDecision CallIRGenerator::tryAttachFunCallBound( auto args = numBoundArgs != 0 ? concatenatedArgs : callArgs; // Check for specific native-function optimizations. - InlinableNativeIRGenerator nativeGen(*this, callee, target, newTarget, - thisValue, args, targetFlags, bound); + InlinableNativeIRGenerator nativeGen(*this, target, newTarget, thisValue, + args, targetFlags); return nativeGen.tryAttachStub(); } @@ -14468,8 +14366,8 @@ AttachDecision CallIRGenerator::tryAttachFunApplyBound( } // Check for specific native-function optimizations. - InlinableNativeIRGenerator nativeGen(*this, callee, target, newTarget, - thisValue, args, targetFlags, bound); + InlinableNativeIRGenerator nativeGen(*this, target, newTarget, thisValue, + args, targetFlags); return nativeGen.tryAttachStub(); } @@ -14477,7 +14375,7 @@ AttachDecision CallIRGenerator::tryAttachStub() { AutoAssertNoPendingException aanpe(cx_); // Some opcodes are not yet supported. - switch (jsop()) { + switch (op_) { case JSOp::Call: case JSOp::CallContent: case JSOp::CallIgnoresRv: @@ -14527,7 +14425,8 @@ AttachDecision CallIRGenerator::tryAttachStub() { // Try inlining Function.prototype.{call,apply}. We don't use the // InlinableNative mechanism for this because we want to optimize these more // aggressively than other natives. - if (IsInlinableFunCallOrApply(jsop())) { + if (op_ == JSOp::Call || op_ == JSOp::CallContent || + op_ == JSOp::CallIgnoresRv) { TRY_ATTACH(tryAttachFunCall(calleeFunc)); TRY_ATTACH(tryAttachFunApply(calleeFunc)); TRY_ATTACH(tryAttachFunCallBound(calleeFunc)); diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp @@ -4548,79 +4548,6 @@ bool CacheIRCompiler::emitLoadFunctionNameResult(ObjOperandId objId) { return true; } -bool CacheIRCompiler::emitBindFunctionResult(ObjOperandId targetId, - uint32_t argc, - uint32_t templateObjectOffset) { - JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); - - AutoCallVM callvm(masm, this, allocator); - AutoScratchRegisterMaybeOutput scratch(allocator, masm, callvm.output()); - - Register target = allocator.useRegister(masm, targetId); - - callvm.prepare(); - - if (isBaseline()) { - // Push the arguments in reverse order. - for (uint32_t i = 0; i < argc; i++) { - Address argAddress(FramePointer, - BaselineStubFrameLayout::Size() + i * sizeof(Value)); - masm.pushValue(argAddress); - } - } else { - MOZ_ASSERT(argc == 0, "Call ICs not used in ion"); - } - masm.moveStackPtrTo(scratch.get()); - - masm.Push(ImmWord(0)); // nullptr for maybeBound - masm.Push(Imm32(argc)); - masm.Push(scratch); - masm.Push(target); - - using Fn = BoundFunctionObject* (*)(JSContext*, Handle<JSObject*>, Value*, - uint32_t, Handle<BoundFunctionObject*>); - callvm.call<Fn, BoundFunctionObject::functionBindImpl>(); - return true; -} - -bool CacheIRCompiler::emitSpecializedBindFunctionResult( - ObjOperandId targetId, uint32_t argc, uint32_t templateObjectOffset) { - JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); - - AutoCallVM callvm(masm, this, allocator); - AutoScratchRegisterMaybeOutput scratch1(allocator, masm, callvm.output()); - AutoScratchRegister scratch2(allocator, masm); - - Register target = allocator.useRegister(masm, targetId); - - StubFieldOffset objectField(templateObjectOffset, StubField::Type::JSObject); - emitLoadStubField(objectField, scratch2); - - callvm.prepare(); - - if (isBaseline()) { - // Push the arguments in reverse order. - for (uint32_t i = 0; i < argc; i++) { - Address argAddress(FramePointer, - BaselineStubFrameLayout::Size() + i * sizeof(Value)); - masm.pushValue(argAddress); - } - } else { - MOZ_ASSERT(argc == 0, "Call ICs not used in ion"); - } - masm.moveStackPtrTo(scratch1.get()); - - masm.Push(scratch2); - masm.Push(Imm32(argc)); - masm.Push(scratch1); - masm.Push(target); - - using Fn = BoundFunctionObject* (*)(JSContext*, Handle<JSObject*>, Value*, - uint32_t, Handle<BoundFunctionObject*>); - callvm.call<Fn, BoundFunctionObject::functionBindSpecializedBaseline>(); - return true; -} - bool CacheIRCompiler::emitLinearizeForCharAccess(StringOperandId strId, Int32OperandId indexId, StringOperandId resultId) { @@ -7045,28 +6972,6 @@ bool CacheIRCompiler::emitMathRoundToInt32Result(NumberOperandId inputId) { return true; } -bool CacheIRCompiler::emitMathRandomResult(uint32_t rngOffset) { - JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); - - AutoOutputRegister output(*this); - AutoScratchRegister scratch1(allocator, masm); - AutoScratchRegister64 scratch2(allocator, masm); - AutoScratchFloatRegister scratchFloat(this); - - StubFieldOffset offset(rngOffset, StubField::Type::RawPointer); - emitLoadStubField(offset, scratch1); - - masm.randomDouble(scratch1, scratchFloat, scratch2, - output.valueReg().toRegister64()); - - if (js::SupportDifferentialTesting()) { - masm.loadConstantDouble(0.0, scratchFloat); - } - - masm.boxDouble(scratchFloat, output.valueReg(), scratchFloat); - return true; -} - bool CacheIRCompiler::emitInt32MinMax(bool isMax, Int32OperandId firstId, Int32OperandId secondId, Int32OperandId resultId) { @@ -7407,125 +7312,6 @@ bool CacheIRCompiler::emitArrayPush(ObjOperandId objId, ValOperandId rhsId) { return true; } -bool CacheIRCompiler::emitPackedArraySliceResult(uint32_t templateObjectOffset, - ObjOperandId arrayId, - Int32OperandId beginId, - Int32OperandId endId) { - JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); - - AutoCallVM callvm(masm, this, allocator); - - Register array = allocator.useRegister(masm, arrayId); - Register begin = allocator.useRegister(masm, beginId); - Register end = allocator.useRegister(masm, endId); - - callvm.prepare(); - - // Don't attempt to pre-allocate the object, instead always use the slow path. - ImmPtr result(nullptr); - - masm.Push(result); - masm.Push(end); - masm.Push(begin); - masm.Push(array); - - using Fn = - JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject); - callvm.call<Fn, ArraySliceDense>(); - return true; -} - -bool CacheIRCompiler::emitArgumentsSliceResult(uint32_t templateObjectOffset, - ObjOperandId argsId, - Int32OperandId beginId, - Int32OperandId endId) { - JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); - - AutoCallVM callvm(masm, this, allocator); - - Register args = allocator.useRegister(masm, argsId); - Register begin = allocator.useRegister(masm, beginId); - Register end = allocator.useRegister(masm, endId); - - callvm.prepare(); - - // Don't attempt to pre-allocate the object, instead always use the slow path. - ImmPtr result(nullptr); - - masm.Push(result); - masm.Push(end); - masm.Push(begin); - masm.Push(args); - - using Fn = - JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject); - callvm.call<Fn, ArgumentsSliceDense>(); - return true; -} - -bool CacheIRCompiler::emitArrayJoinResult(ObjOperandId objId, - StringOperandId sepId) { - JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); - - AutoCallVM callvm(masm, this, allocator); - - Register obj = allocator.useRegister(masm, objId); - Register sep = allocator.useRegister(masm, sepId); - AutoScratchRegisterMaybeOutput scratch(allocator, masm, callvm.output()); - - // Discard the stack to ensure it's balanced when we skip the vm-call. - allocator.discardStack(masm); - - // Load obj->elements in scratch. - masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch); - Address lengthAddr(scratch, ObjectElements::offsetOfLength()); - - // If array length is 0, return empty string. - Label finished; - - { - Label arrayNotEmpty; - masm.branch32(Assembler::NotEqual, lengthAddr, Imm32(0), &arrayNotEmpty); - masm.movePtr(ImmGCPtr(cx_->names().empty_), scratch); - masm.tagValue(JSVAL_TYPE_STRING, scratch, callvm.outputValueReg()); - masm.jump(&finished); - masm.bind(&arrayNotEmpty); - } - - Label vmCall; - - // Otherwise, handle array length 1 case. - masm.branch32(Assembler::NotEqual, lengthAddr, Imm32(1), &vmCall); - - // But only if initializedLength is also 1. - Address initLength(scratch, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::NotEqual, initLength, Imm32(1), &vmCall); - - // And only if elem0 is a string. - Address elementAddr(scratch, 0); - masm.branchTestString(Assembler::NotEqual, elementAddr, &vmCall); - - // Store the value. - masm.loadValue(elementAddr, callvm.outputValueReg()); - masm.jump(&finished); - - // Otherwise call into the VM. - { - masm.bind(&vmCall); - - callvm.prepare(); - - masm.Push(sep); - masm.Push(obj); - - using Fn = JSString* (*)(JSContext*, HandleObject, HandleString); - callvm.call<Fn, jit::ArrayJoin>(); - } - - masm.bind(&finished); - return true; -} - bool CacheIRCompiler::emitStoreTypedArrayElement(ObjOperandId objId, Scalar::Type elementType, IntPtrOperandId indexId, @@ -10047,6 +9833,9 @@ bool CacheIRCompiler::emitCallNumberToString(NumberOperandId inputId, StringOperandId resultId) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + AutoAvailableFloatRegister floatScratch0(*this, FloatReg0); + + allocator.ensureDoubleRegister(masm, inputId, floatScratch0); Register result = allocator.defineRegister(masm, resultId); FailurePath* failure; @@ -10054,9 +9843,6 @@ bool CacheIRCompiler::emitCallNumberToString(NumberOperandId inputId, return false; } - AutoScratchFloatRegister scratchFloat(this, failure); - allocator.ensureDoubleRegister(masm, inputId, scratchFloat); - LiveRegisterSet volatileRegs = liveVolatileRegs(); volatileRegs.takeUnchecked(result); masm.PushRegsInMask(volatileRegs); @@ -10065,14 +9851,13 @@ bool CacheIRCompiler::emitCallNumberToString(NumberOperandId inputId, masm.setupUnalignedABICall(result); masm.loadJSContext(result); masm.passABIArg(result); - masm.passABIArg(scratchFloat, ABIType::Float64); + masm.passABIArg(floatScratch0, ABIType::Float64); masm.callWithABI<Fn, js::NumberToStringPure>(); masm.storeCallPointerResult(result); masm.PopRegsInMask(volatileRegs); - masm.branchPtr(Assembler::Equal, result, ImmPtr(nullptr), - scratchFloat.failure()); + masm.branchPtr(Assembler::Equal, result, ImmPtr(nullptr), failure->label()); return true; } @@ -12100,10 +11885,6 @@ template <> struct ReturnTypeToJSValueType<SetObject*> { static constexpr JSValueType result = JSVAL_TYPE_OBJECT; }; -template <> -struct ReturnTypeToJSValueType<BoundFunctionObject*> { - static constexpr JSValueType result = JSVAL_TYPE_OBJECT; -}; template <typename Fn> void AutoCallVM::storeResult() { diff --git a/js/src/jit/CacheIRGenerator.h b/js/src/jit/CacheIRGenerator.h @@ -31,8 +31,7 @@ class JSFunction; namespace JS { struct XrayJitInfo; -class RegExpFlags; -} // namespace JS +} namespace js { @@ -87,8 +86,6 @@ class MOZ_RAII IRGenerator { IRGenerator(const IRGenerator&) = delete; IRGenerator& operator=(const IRGenerator&) = delete; - JSOp jsop() const { return JSOp(*pc_); } - bool maybeGuardInt32Index(const Value& index, ValOperandId indexId, uint32_t* int32Index, Int32OperandId* int32IndexId); @@ -146,7 +143,6 @@ class MOZ_RAII IRGenerator { gc::AllocSite* maybeCreateAllocSite(); friend class CacheIRSpewer; - friend class InlinableNativeIRGenerator; public: explicit IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, @@ -166,17 +162,24 @@ class MOZ_RAII IRGenerator { class MOZ_RAII GetPropIRGenerator : public IRGenerator { HandleValue val_; HandleValue idVal_; - HandleValue receiverVal_; - - friend class InlinableNativeIRGenerator; AttachDecision tryAttachNative(HandleObject obj, ObjOperandId objId, HandleId id, ValOperandId receiverId); - AttachDecision tryAttachInlinableNativeGetter(NativeObject* holder, - PropertyInfo prop, - ValOperandId receiverId); AttachDecision tryAttachObjectLength(HandleObject obj, ObjOperandId objId, HandleId id); + AttachDecision tryAttachTypedArray(HandleObject obj, ObjOperandId objId, + HandleId id); + AttachDecision tryAttachDataView(HandleObject obj, ObjOperandId objId, + HandleId id); + AttachDecision tryAttachArrayBufferMaybeShared(HandleObject obj, + ObjOperandId objId, + HandleId id); + AttachDecision tryAttachRegExp(HandleObject obj, ObjOperandId objId, + HandleId id); + AttachDecision tryAttachMap(HandleObject obj, ObjOperandId objId, + HandleId id); + AttachDecision tryAttachSet(HandleObject obj, ObjOperandId objId, + HandleId id); AttachDecision tryAttachModuleNamespace(HandleObject obj, ObjOperandId objId, HandleId id); AttachDecision tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, @@ -287,7 +290,7 @@ class MOZ_RAII GetPropIRGenerator : public IRGenerator { public: GetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, ICState state, CacheKind cacheKind, HandleValue val, - HandleValue idVal, HandleValue receiverVal); + HandleValue idVal); AttachDecision tryAttachStub(); }; @@ -595,6 +598,7 @@ enum class ScriptedThisResult { NoAction, UninitializedThis, PlainObjectShape }; class MOZ_RAII CallIRGenerator : public IRGenerator { private: + JSOp op_; uint32_t argc_; HandleValue callee_; HandleValue thisval_; @@ -635,7 +639,7 @@ class MOZ_RAII CallIRGenerator : public IRGenerator { void trackAttached(const char* name /* must be a C string literal */); public: - CallIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, + CallIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, JSOp op, ICState state, BaselineFrame* frame, uint32_t argc, HandleValue callee, HandleValue thisval, HandleValue newTarget, HandleValueArray args); @@ -644,38 +648,28 @@ class MOZ_RAII CallIRGenerator : public IRGenerator { }; class MOZ_RAII InlinableNativeIRGenerator { - mozilla::Variant<CallIRGenerator*, GetPropIRGenerator*> gen_; - IRGenerator& generator_; + CallIRGenerator& generator_; CacheIRWriter& writer; JSContext* cx_; - HandleObject callee_; HandleFunction target_; HandleValue newTarget_; HandleValue thisval_; HandleValueArray args_; - Handle<BoundFunctionObject*> boundTarget_; CallFlags flags_; - uint32_t stackArgc_; - - // |this| for inlined accesor operations. - ValOperandId receiverId_; HandleScript script() const { return generator_.script_; } - JSObject* callee() const { return callee_; } + JSObject* callee() const { return &generator_.callee_.toObject(); } bool isFirstStub() const { return generator_.isFirstStub_; } - bool ignoresResult() const { return op() == JSOp::CallIgnoresRv; } - JSOp op() const { return generator_.jsop(); } - uint32_t stackArgc() const { return stackArgc_; } - - // Inlined native accessor for GetProp(Super) or GetElem(Super) operations. - bool isAccessorOp() const { return !IsInvokeOp(op()); } + bool ignoresResult() const { return generator_.op_ == JSOp::CallIgnoresRv; } + JSOp op() const { return generator_.op_; } + uint32_t stackArgc() const { return generator_.argc_; } bool isCalleeBoundFunction() const; BoundFunctionObject* boundCallee() const; - bool isTargetBoundFunction() const { return boundTarget_ != nullptr; } - BoundFunctionObject* boundTarget() const { return boundTarget_; } + bool isTargetBoundFunction() const; + BoundFunctionObject* boundTarget() const; ObjOperandId emitNativeCalleeGuard(Int32OperandId argcId); void emitOptimisticClassGuard(ObjOperandId objId, JSObject* obj, @@ -701,10 +695,6 @@ class MOZ_RAII InlinableNativeIRGenerator { bool hasBoundArguments() const; Int32OperandId initializeInputOperand() { - // Input operands are already initialized for inlined accessors. - if (isAccessorOp()) { - return Int32OperandId(); - } return Int32OperandId(writer.setInputOperandId(0)); } @@ -748,8 +738,6 @@ class MOZ_RAII InlinableNativeIRGenerator { AttachDecision tryAttachArrayIsArray(); AttachDecision tryAttachDataViewGet(Scalar::Type type); AttachDecision tryAttachDataViewSet(Scalar::Type type); - AttachDecision tryAttachDataViewByteLength(); - AttachDecision tryAttachDataViewByteOffset(); AttachDecision tryAttachFunctionBind(); AttachDecision tryAttachSpecializedFunctionBind( Handle<JSObject*> target, Handle<BoundFunctionObject*> templateObj); @@ -772,7 +760,6 @@ class MOZ_RAII InlinableNativeIRGenerator { AttachDecision tryAttachGuardToSharedArrayBuffer(); AttachDecision tryAttachHasClass(const JSClass* clasp, bool isPossiblyWrapped); - AttachDecision tryAttachRegExpFlag(JS::RegExpFlags flags); AttachDecision tryAttachRegExpMatcherSearcher(InlinableNative native); AttachDecision tryAttachRegExpSearcherLastLimit(); AttachDecision tryAttachRegExpHasCaptureGroups(); @@ -827,9 +814,6 @@ class MOZ_RAII InlinableNativeIRGenerator { AttachDecision tryAttachTypedArrayFill(); AttachDecision tryAttachTypedArraySet(); AttachDecision tryAttachTypedArraySubarray(); - AttachDecision tryAttachTypedArrayLength(); - AttachDecision tryAttachTypedArrayByteLength(); - AttachDecision tryAttachTypedArrayByteOffset(); AttachDecision tryAttachIsTypedArray(bool isPossiblyWrapped); AttachDecision tryAttachIsTypedArrayConstructor(); AttachDecision tryAttachTypedArrayLength(bool isPossiblyWrapped); @@ -881,59 +865,31 @@ class MOZ_RAII InlinableNativeIRGenerator { AttachDecision tryAttachMapGet(); AttachDecision tryAttachMapDelete(); AttachDecision tryAttachMapSet(); - AttachDecision tryAttachMapSize(); AttachDecision tryAttachDateGetTime(); AttachDecision tryAttachDateGet(DateComponent component); AttachDecision tryAttachWeakMapHas(); AttachDecision tryAttachWeakMapGet(); AttachDecision tryAttachWeakSetHas(); - AttachDecision tryAttachArrayBufferByteLength(); - AttachDecision tryAttachSharedArrayBufferByteLength(); #ifdef FUZZING_JS_FUZZILLI AttachDecision tryAttachFuzzilliHash(); #endif void trackAttached(const char* name /* must be a C string literal */) { - return gen_.match( - [&](auto* generator) { return generator->trackAttached(name); }); + return generator_.trackAttached(name); } public: - InlinableNativeIRGenerator(CallIRGenerator& generator, HandleObject callee, - HandleFunction target, HandleValue newTarget, - HandleValue thisValue, HandleValueArray args, - CallFlags flags, - Handle<BoundFunctionObject*> boundTarget = nullptr) - : gen_(&generator), - generator_(generator), + InlinableNativeIRGenerator(CallIRGenerator& generator, HandleFunction target, + HandleValue newTarget, HandleValue thisValue, + HandleValueArray args, CallFlags flags) + : generator_(generator), writer(generator.writer), cx_(generator.cx_), - callee_(callee), target_(target), newTarget_(newTarget), thisval_(thisValue), args_(args), - boundTarget_(boundTarget), - flags_(flags), - stackArgc_(generator.argc_), - receiverId_() {} - - InlinableNativeIRGenerator(GetPropIRGenerator& generator, - HandleFunction target, HandleValue thisValue, - CallFlags flags, ValOperandId receiverId) - : gen_(&generator), - generator_(generator), - writer(generator.writer), - cx_(generator.cx_), - callee_(target), - target_(target), - newTarget_(JS::NullHandleValue), - thisval_(thisValue), - args_(HandleValueArray::empty()), - boundTarget_(nullptr), - flags_(flags), - stackArgc_(0), - receiverId_(receiverId) {} + flags_(flags) {} AttachDecision tryAttachStub(); }; @@ -1122,19 +1078,13 @@ class MOZ_RAII LambdaIRGenerator : public IRGenerator { AttachDecision tryAttachFunctionClone(); }; -// Returns true for bytecode call ops that can use InlinableNativeIRGenerator. +// Returns true for bytecode ops that can use InlinableNativeIRGenerator. inline bool BytecodeCallOpCanHaveInlinableNative(JSOp op) { return op == JSOp::Call || op == JSOp::CallContent || op == JSOp::New || op == JSOp::NewContent || op == JSOp::CallIgnoresRv || op == JSOp::SpreadCall; } -// Returns true for bytecode get ops that can use InlinableNativeIRGenerator. -inline bool BytecodeGetOpCanHaveInlinableNative(JSOp op) { - return op == JSOp::GetProp || op == JSOp::GetElem || - op == JSOp::GetPropSuper || op == JSOp::GetElemSuper; -} - inline bool BytecodeOpCanHaveAllocSite(JSOp op) { return BytecodeCallOpCanHaveInlinableNative(op) || op == JSOp::NewArray || op == JSOp::NewObject || op == JSOp::NewInit || op == JSOp::CallIter || diff --git a/js/src/jit/CacheIROps.yaml b/js/src/jit/CacheIROps.yaml @@ -1175,7 +1175,7 @@ rhs: ValId - name: ArrayJoinResult - shared: true + shared: false transpile: true cost_estimate: 5 args: @@ -1204,7 +1204,7 @@ array: ObjId - name: PackedArraySliceResult - shared: true + shared: false transpile: true cost_estimate: 5 args: @@ -1214,7 +1214,7 @@ end: Int32Id - name: ArgumentsSliceResult - shared: true + shared: false transpile: true cost_estimate: 5 args: @@ -1688,7 +1688,7 @@ # Because Baseline stub code is shared by all realms in the Zone, this # instruction loads a pointer to the RNG from a stub field. - name: MathRandomResult - shared: true + shared: false transpile: true cost_estimate: 3 args: @@ -2254,7 +2254,7 @@ thisShape: ShapeField - name: BindFunctionResult - shared: true + shared: false transpile: true cost_estimate: 5 args: @@ -2263,7 +2263,7 @@ templateObject: ObjectField - name: SpecializedBindFunctionResult - shared: true + shared: false transpile: true cost_estimate: 4 args: diff --git a/js/src/jit/InlinableNatives.cpp b/js/src/jit/InlinableNatives.cpp @@ -267,7 +267,6 @@ bool js::jit::CanInlineNativeCrossRealm(InlinableNative native) { case InlinableNative::ArrayShift: case InlinableNative::ArrayPush: case InlinableNative::ArraySlice: - case InlinableNative::ArrayBufferByteLength: case InlinableNative::AtomicsCompareExchange: case InlinableNative::AtomicsExchange: case InlinableNative::AtomicsLoad: @@ -304,8 +303,6 @@ bool js::jit::CanInlineNativeCrossRealm(InlinableNative native) { case InlinableNative::DataViewSetFloat64: case InlinableNative::DataViewSetBigInt64: case InlinableNative::DataViewSetBigUint64: - case InlinableNative::DataViewByteLength: - case InlinableNative::DataViewByteOffset: case InlinableNative::DateGetTime: case InlinableNative::DateGetFullYear: case InlinableNative::DateGetMonth: @@ -320,25 +317,15 @@ bool js::jit::CanInlineNativeCrossRealm(InlinableNative native) { case InlinableNative::MapHas: case InlinableNative::MapDelete: case InlinableNative::MapSet: - case InlinableNative::MapSize: case InlinableNative::Number: case InlinableNative::NumberParseInt: case InlinableNative::NumberToString: case InlinableNative::ReflectGetPrototypeOf: - case InlinableNative::RegExpDotAll: - case InlinableNative::RegExpGlobal: - case InlinableNative::RegExpHasIndices: - case InlinableNative::RegExpIgnoreCase: - case InlinableNative::RegExpMultiline: - case InlinableNative::RegExpSticky: - case InlinableNative::RegExpUnicode: - case InlinableNative::RegExpUnicodeSets: case InlinableNative::SetConstructor: case InlinableNative::SetHas: case InlinableNative::SetDelete: case InlinableNative::SetAdd: case InlinableNative::SetSize: - case InlinableNative::SharedArrayBufferByteLength: case InlinableNative::String: case InlinableNative::StringToString: case InlinableNative::StringValueOf: @@ -370,9 +357,6 @@ bool js::jit::CanInlineNativeCrossRealm(InlinableNative native) { case InlinableNative::TypedArrayFill: case InlinableNative::TypedArraySet: case InlinableNative::TypedArraySubarray: - case InlinableNative::TypedArrayLength: - case InlinableNative::TypedArrayByteLength: - case InlinableNative::TypedArrayByteOffset: case InlinableNative::WeakMapGet: case InlinableNative::WeakMapHas: case InlinableNative::WeakSetHas: diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h @@ -39,8 +39,6 @@ _(ArrayPush) \ _(ArraySlice) \ \ - _(ArrayBufferByteLength) \ - \ _(AtomicsCompareExchange) \ _(AtomicsExchange) \ _(AtomicsLoad) \ @@ -81,8 +79,6 @@ _(DataViewSetFloat64) \ _(DataViewSetBigInt64) \ _(DataViewSetBigUint64) \ - _(DataViewByteLength) \ - _(DataViewByteOffset) \ \ _(DateGetTime) \ _(DateGetFullYear) \ @@ -112,7 +108,6 @@ _(MapGet) \ _(MapHas) \ _(MapSet) \ - _(MapSize) \ \ _(MathAbs) \ _(MathFloor) \ @@ -157,14 +152,6 @@ \ _(ReflectGetPrototypeOf) \ \ - _(RegExpDotAll) \ - _(RegExpGlobal) \ - _(RegExpHasIndices) \ - _(RegExpIgnoreCase) \ - _(RegExpMultiline) \ - _(RegExpSticky) \ - _(RegExpUnicode) \ - _(RegExpUnicodeSets) \ _(RegExpMatcher) \ _(RegExpSearcher) \ _(RegExpSearcherLastLimit) \ @@ -181,8 +168,6 @@ _(SetAdd) \ _(SetSize) \ \ - _(SharedArrayBufferByteLength) \ - \ _(String) \ _(StringToString) \ _(StringValueOf) \ @@ -218,9 +203,6 @@ _(TypedArrayFill) \ _(TypedArraySet) \ _(TypedArraySubarray) \ - _(TypedArrayLength) \ - _(TypedArrayByteLength) \ - _(TypedArrayByteOffset) \ \ _(TestBailout) \ _(TestAssertFloat32) \ diff --git a/js/src/jit/IonCacheIRCompiler.cpp b/js/src/jit/IonCacheIRCompiler.cpp @@ -940,8 +940,7 @@ bool IonCacheIRCompiler::emitLoadFixedSlotResult(ObjOperandId objId, bool IonCacheIRCompiler::emitLoadFixedSlotTypedResult(ObjOperandId objId, uint32_t offsetOffset, ValueType) { - // The type is only used by Warp. - return emitLoadFixedSlotResult(objId, offsetOffset); + MOZ_CRASH("Call ICs not used in ion"); } bool IonCacheIRCompiler::emitLoadDynamicSlotResult(ObjOperandId objId, @@ -1564,9 +1563,9 @@ bool IonCacheIRCompiler::emitAllocateAndStoreDynamicSlot( newShapeOffset, mozilla::Some(numNewSlotsOffset), preserveWrapper); } -bool IonCacheIRCompiler::emitLoadStringCharResult( - StringOperandId strId, Int32OperandId indexId, - StringCharOutOfBounds outOfBounds) { +bool IonCacheIRCompiler::emitLoadStringCharResult(StringOperandId strId, + Int32OperandId indexId, + bool handleOOB) { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); AutoOutputRegister output(*this); Register str = allocator.useRegister(masm, strId); @@ -1582,21 +1581,15 @@ bool IonCacheIRCompiler::emitLoadStringCharResult( // Bounds check, load string char. Label done; - Label tagResult; Label loadFailed; - if (outOfBounds == StringCharOutOfBounds::Failure) { + if (!handleOOB) { masm.spectreBoundsCheck32(index, Address(str, JSString::offsetOfLength()), scratch1, failure->label()); masm.loadStringChar(str, index, scratch1, scratch2, scratch3, failure->label()); } else { - if (outOfBounds == StringCharOutOfBounds::EmptyString) { - // Return the empty string for out-of-bounds access. - masm.movePtr(ImmGCPtr(cx_->runtime()->emptyString), scratch2); - } else { - // Return |undefined| for out-of-bounds access. - masm.moveValue(UndefinedValue(), output.valueReg()); - } + // Return the empty string for out-of-bounds access. + masm.movePtr(ImmGCPtr(cx_->runtime()->emptyString), scratch2); // This CacheIR op is always preceded by |LinearizeForCharAccess|, so we're // guaranteed to see no nested ropes. @@ -1608,9 +1601,9 @@ bool IonCacheIRCompiler::emitLoadStringCharResult( // Load StaticString for this char. For larger code units perform a VM call. Label vmCall; masm.lookupStaticString(scratch1, scratch2, cx_->staticStrings(), &vmCall); - masm.jump(&tagResult); + masm.jump(&done); - if (outOfBounds != StringCharOutOfBounds::Failure) { + if (handleOOB) { masm.bind(&loadFailed); masm.assumeUnreachable("loadStringChar can't fail for linear strings"); } @@ -1642,36 +1635,11 @@ bool IonCacheIRCompiler::emitLoadStringCharResult( masm.branchPtr(Assembler::Equal, scratch2, ImmWord(0), failure->label()); } - if (outOfBounds != StringCharOutOfBounds::UndefinedValue) { - masm.bind(&tagResult); - masm.bind(&done); - masm.tagValue(JSVAL_TYPE_STRING, scratch2, output.valueReg()); - } else { - masm.bind(&tagResult); - masm.tagValue(JSVAL_TYPE_STRING, scratch2, output.valueReg()); - masm.bind(&done); - } + masm.bind(&done); + masm.tagValue(JSVAL_TYPE_STRING, scratch2, output.valueReg()); return true; } -bool IonCacheIRCompiler::emitLoadStringCharResult(StringOperandId strId, - Int32OperandId indexId, - bool handleOOB) { - JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); - auto outOfBounds = handleOOB ? StringCharOutOfBounds::EmptyString - : StringCharOutOfBounds::Failure; - return emitLoadStringCharResult(strId, indexId, outOfBounds); -} - -bool IonCacheIRCompiler::emitLoadStringAtResult(StringOperandId strId, - Int32OperandId indexId, - bool handleOOB) { - JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); - auto outOfBounds = handleOOB ? StringCharOutOfBounds::UndefinedValue - : StringCharOutOfBounds::Failure; - return emitLoadStringCharResult(strId, indexId, outOfBounds); -} - bool IonCacheIRCompiler::emitCallNativeSetter(ObjOperandId receiverId, uint32_t setterOffset, ValOperandId rhsId, @@ -2308,6 +2276,17 @@ bool IonCacheIRCompiler::emitCallInlinedFunction(ObjOperandId calleeId, MOZ_CRASH("Call ICs not used in ion"); } +bool IonCacheIRCompiler::emitBindFunctionResult(ObjOperandId targetId, + uint32_t argc, + uint32_t templateObjectOffset) { + MOZ_CRASH("Call ICs not used in ion"); +} + +bool IonCacheIRCompiler::emitSpecializedBindFunctionResult( + ObjOperandId targetId, uint32_t argc, uint32_t templateObjectOffset) { + MOZ_CRASH("Call ICs not used in ion"); +} + bool IonCacheIRCompiler::emitLoadArgumentFixedSlot(ValOperandId resultId, uint8_t slotIndex) { MOZ_CRASH("Call ICs not used in ion"); @@ -2319,6 +2298,24 @@ bool IonCacheIRCompiler::emitLoadArgumentDynamicSlot(ValOperandId resultId, MOZ_CRASH("Call ICs not used in ion"); } +bool IonCacheIRCompiler::emitArrayJoinResult(ObjOperandId objId, + StringOperandId sepId) { + MOZ_CRASH("Call ICs not used in ion"); +} + +bool IonCacheIRCompiler::emitPackedArraySliceResult( + uint32_t templateObjectOffset, ObjOperandId arrayId, Int32OperandId beginId, + Int32OperandId endId) { + MOZ_CRASH("Call ICs not used in ion"); +} + +bool IonCacheIRCompiler::emitArgumentsSliceResult(uint32_t templateObjectOffset, + ObjOperandId argsId, + Int32OperandId beginId, + Int32OperandId endId) { + MOZ_CRASH("Call ICs not used in ion"); +} + bool IonCacheIRCompiler::emitIsArrayResult(ValOperandId inputId) { MOZ_CRASH("Call ICs not used in ion"); } @@ -2336,6 +2333,10 @@ bool IonCacheIRCompiler::emitStringFromCodePointResult(Int32OperandId codeId) { MOZ_CRASH("Call ICs not used in ion"); } +bool IonCacheIRCompiler::emitMathRandomResult(uint32_t rngOffset) { + MOZ_CRASH("Call ICs not used in ion"); +} + bool IonCacheIRCompiler::emitReflectGetPrototypeOfResult(ObjOperandId objId) { MOZ_CRASH("Call ICs not used in ion"); } @@ -2417,3 +2418,9 @@ bool IonCacheIRCompiler::emitRegExpHasCaptureGroupsResult( ObjOperandId regexpId, StringOperandId inputId) { MOZ_CRASH("Call ICs not used in ion"); } + +bool IonCacheIRCompiler::emitLoadStringAtResult(StringOperandId strId, + Int32OperandId indexId, + bool handleOOB) { + MOZ_CRASH("Call ICs not used in ion"); +} diff --git a/js/src/jit/IonCacheIRCompiler.h b/js/src/jit/IonCacheIRCompiler.h @@ -93,10 +93,6 @@ class MOZ_RAII IonCacheIRCompiler : public CacheIRCompiler { ValOperandId targetId, ObjOperandId receiverId, ObjOperandId handlerId, ObjOperandId trapId, IdType id, uint32_t nargsAndFlags); - enum class StringCharOutOfBounds { Failure, EmptyString, UndefinedValue }; - bool emitLoadStringCharResult(StringOperandId strId, Int32OperandId indexId, - StringCharOutOfBounds outOfBounds); - void pushStubCodePointer(); CACHE_IR_COMPILER_UNSHARED_GENERATED diff --git a/js/src/jit/IonIC.cpp b/js/src/jit/IonIC.cpp @@ -174,7 +174,7 @@ bool IonGetPropertyIC::update(JSContext* cx, HandleScript outerScript, MOZ_ASSERT(!val.isMagic()); TryAttachIonStub<GetPropIRGenerator>(cx, ic, ionScript, ic->kind(), val, - idVal, val); + idVal); if (ic->kind() == CacheKind::GetProp) { Rooted<PropertyName*> name(cx, idVal.toString()->asAtom().asPropertyName()); @@ -219,7 +219,7 @@ bool IonGetPropSuperIC::update(JSContext* cx, HandleScript outerScript, RootedValue val(cx, ObjectValue(*obj)); TryAttachIonStub<GetPropIRGenerator>(cx, ic, ionScript, ic->kind(), val, - idVal, receiver); + idVal); if (ic->kind() == CacheKind::GetPropSuper) { Rooted<PropertyName*> name(cx, idVal.toString()->asAtom().asPropertyName()); diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp @@ -7163,17 +7163,21 @@ MDefinition* MGuardSpecificInt32::foldsTo(TempAllocator& alloc) { return this; } -MDefinition* MGuardShape::foldsTo(TempAllocator& alloc) { - if (object()->isGuardShape() && - shape() == object()->toGuardShape()->shape()) { - return object(); +bool MCallBindVar::congruentTo(const MDefinition* ins) const { + if (!ins->isCallBindVar()) { + return false; } - return this; + return congruentIfOperandsEqual(ins); } bool MGuardShape::congruentTo(const MDefinition* ins) const { - return congruentIfOperandsEqual(ins) && - shape() == ins->toGuardShape()->shape(); + if (!ins->isGuardShape()) { + return false; + } + if (shape() != ins->toGuardShape()->shape()) { + return false; + } + return congruentIfOperandsEqual(ins); } AliasSet MGuardShape::getAliasSet() const { diff --git a/js/src/jit/MIROps.yaml b/js/src/jit/MIROps.yaml @@ -2174,7 +2174,7 @@ environmentChain: Object result_type: Object movable: true - congruent_to: if_operands_equal + congruent_to: custom alias_set: none generate_lir: true @@ -2189,7 +2189,6 @@ congruent_to: custom alias_set: custom might_alias: custom - folds_to: custom generate_lir: true lir_temps: 1 diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp @@ -32,7 +32,6 @@ #include "gc/Barrier.h" #include "gc/Memory.h" -#include "jit/InlinableNatives.h" #include "js/ArrayBuffer.h" #include "js/Conversions.h" #include "js/experimental/TypedData.h" // JS_IsArrayBufferViewObject @@ -347,8 +346,7 @@ static const JSFunctionSpec arraybuffer_proto_functions[] = { }; static const JSPropertySpec arraybuffer_proto_properties[] = { - JS_INLINABLE_PSG("byteLength", ArrayBufferObject::byteLengthGetter, 0, - ArrayBufferByteLength), + JS_PSG("byteLength", ArrayBufferObject::byteLengthGetter, 0), JS_PSG("maxByteLength", ArrayBufferObject::maxByteLengthGetter, 0), JS_PSG("resizable", ArrayBufferObject::resizableGetter, 0), JS_PSG("detached", ArrayBufferObject::detachedGetter, 0), diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h @@ -475,6 +475,10 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared { static bool class_constructor(JSContext* cx, unsigned argc, Value* vp); + static bool isOriginalByteLengthGetter(Native native) { + return native == byteLengthGetter; + } + static ArrayBufferObject* createForContents(JSContext* cx, size_t nbytes, BufferContents contents); diff --git a/js/src/vm/PortableBaselineInterpret.cpp b/js/src/vm/PortableBaselineInterpret.cpp @@ -5601,7 +5601,7 @@ DEFINE_IC(NewObject, 0, { DEFINE_IC(GetProp, 1, { IC_LOAD_VAL(value0, 0); PUSH_FALLBACK_IC_FRAME(); - if (!DoGetPropFallback(cx, ctx.frame, fallback, value0, &ctx.state.res)) { + if (!DoGetPropFallback(cx, ctx.frame, fallback, &value0, &ctx.state.res)) { goto error; } }); @@ -5610,7 +5610,7 @@ DEFINE_IC(GetPropSuper, 2, { IC_LOAD_VAL(value0, 1); IC_LOAD_VAL(value1, 0); PUSH_FALLBACK_IC_FRAME(); - if (!DoGetPropSuperFallback(cx, ctx.frame, fallback, value0, value1, + if (!DoGetPropSuperFallback(cx, ctx.frame, fallback, value0, &value1, &ctx.state.res)) { goto error; } diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp @@ -117,6 +117,44 @@ RegExpShared* RegExpObject::getShared(JSContext* cx, return createShared(cx, regexp); } +/* static */ +bool RegExpObject::isOriginalFlagGetter(JSNative native, RegExpFlags* mask) { + if (native == regexp_hasIndices) { + *mask = RegExpFlag::HasIndices; + return true; + } + if (native == regexp_global) { + *mask = RegExpFlag::Global; + return true; + } + if (native == regexp_ignoreCase) { + *mask = RegExpFlag::IgnoreCase; + return true; + } + if (native == regexp_multiline) { + *mask = RegExpFlag::Multiline; + return true; + } + if (native == regexp_dotAll) { + *mask = RegExpFlag::DotAll; + return true; + } + if (native == regexp_sticky) { + *mask = RegExpFlag::Sticky; + return true; + } + if (native == regexp_unicode) { + *mask = RegExpFlag::Unicode; + return true; + } + if (native == regexp_unicodeSets) { + *mask = RegExpFlag::UnicodeSets; + return true; + } + + return false; +} + static const ClassSpec RegExpObjectClassSpec = { GenericCreateConstructor<js::regexp_construct, 2, gc::AllocKind::FUNCTION>, GenericCreatePrototype<RegExpObject>, diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h @@ -159,6 +159,8 @@ class RegExpObject : public NativeObject { return flags.global() || flags.sticky(); } + static bool isOriginalFlagGetter(JSNative native, JS::RegExpFlags* mask); + static RegExpShared* getShared(JSContext* cx, Handle<RegExpObject*> regexp); bool hasShared() const { return !getFixedSlot(SHARED_SLOT).isUndefined(); } diff --git a/js/src/vm/SharedArrayObject.cpp b/js/src/vm/SharedArrayObject.cpp @@ -15,7 +15,6 @@ #include "gc/GCContext.h" #include "gc/Memory.h" #include "jit/AtomicOperations.h" -#include "jit/InlinableNatives.h" #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* #include "js/Prefs.h" #include "js/PropertySpec.h" @@ -958,8 +957,7 @@ static const JSFunctionSpec sharedarray_proto_functions[] = { }; static const JSPropertySpec sharedarray_proto_properties[] = { - JS_INLINABLE_PSG("byteLength", SharedArrayBufferObject::byteLengthGetter, 0, - SharedArrayBufferByteLength), + JS_PSG("byteLength", SharedArrayBufferObject::byteLengthGetter, 0), JS_PSG("maxByteLength", SharedArrayBufferObject::maxByteLengthGetter, 0), JS_PSG("growable", SharedArrayBufferObject::growableGetter, 0), JS_STRING_SYM_PS(toStringTag, "SharedArrayBuffer", JSPROP_READONLY), diff --git a/js/src/vm/SharedArrayObject.h b/js/src/vm/SharedArrayObject.h @@ -283,6 +283,10 @@ class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared { static bool slice(JSContext* cx, unsigned argc, Value* vp); + static bool isOriginalByteLengthGetter(Native native) { + return native == byteLengthGetter; + } + private: template <class SharedArrayBufferType> static SharedArrayBufferType* NewWith(JSContext* cx, diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp @@ -1840,12 +1840,10 @@ static bool TypedArray_toStringTagGetter(JSContext* cx, unsigned argc, } /* static */ const JSPropertySpec TypedArrayObject::protoAccessors[] = { - JS_INLINABLE_PSG("length", TypedArray_lengthGetter, 0, TypedArrayLength), + JS_PSG("length", TypedArray_lengthGetter, 0), JS_PSG("buffer", TypedArray_bufferGetter, 0), - JS_INLINABLE_PSG("byteLength", TypedArray_byteLengthGetter, 0, - TypedArrayByteLength), - JS_INLINABLE_PSG("byteOffset", TypedArray_byteOffsetGetter, 0, - TypedArrayByteOffset), + JS_PSG("byteLength", TypedArray_byteLengthGetter, 0), + JS_PSG("byteOffset", TypedArray_byteOffsetGetter, 0), JS_SYM_GET(toStringTag, TypedArray_toStringTagGetter, 0), JS_PS_END, }; @@ -6188,6 +6186,21 @@ const JSClass TypedArrayObject::protoClasses[Scalar::MaxTypedArrayViewType] = { #undef IMPL_TYPED_ARRAY_PROTO_CLASS }; +/* static */ +bool TypedArrayObject::isOriginalLengthGetter(Native native) { + return native == TypedArray_lengthGetter; +} + +/* static */ +bool TypedArrayObject::isOriginalByteOffsetGetter(Native native) { + return native == TypedArray_byteOffsetGetter; +} + +/* static */ +bool TypedArrayObject::isOriginalByteLengthGetter(Native native) { + return native == TypedArray_byteLengthGetter; +} + bool js::IsTypedArrayConstructor(const JSObject* obj) { #define CHECK_TYPED_ARRAY_CONSTRUCTOR(_, T, N) \ if (IsNativeFunction(obj, TypedArrayObjectTemplate<T>::class_constructor)) { \ diff --git a/js/src/vm/TypedArrayObject.h b/js/src/vm/TypedArrayObject.h @@ -136,6 +136,12 @@ class TypedArrayObject : public ArrayBufferViewObject { // Maximum allowed byte length for any typed array. static constexpr size_t ByteLengthLimit = ArrayBufferObject::ByteLengthLimit; + static bool isOriginalLengthGetter(Native native); + + static bool isOriginalByteOffsetGetter(Native native); + + static bool isOriginalByteLengthGetter(Native native); + /* Accessors and functions */ static bool sort(JSContext* cx, unsigned argc, Value* vp);