Diff
Modified: trunk/JSTests/ChangeLog (268793 => 268794)
--- trunk/JSTests/ChangeLog 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/JSTests/ChangeLog 2020-10-21 14:06:02 UTC (rev 268794)
@@ -1,3 +1,25 @@
+2020-10-21 Caitlin Potter <ca...@igalia.com>
+
+ [JSC] support op_get_private_name in DFG and FTL
+ https://bugs.webkit.org/show_bug.cgi?id=214861
+
+ Reviewed by Filip Pizlo.
+
+ * microbenchmarks/class-fields-private/monomorphic-get-private-field.js: Added.
+ * microbenchmarks/class-fields-private/polymorphic-get-private-field.js: Added.
+ * stress/dfg-get-private-name-by-id-generic.js: Added.
+ * stress/dfg-get-private-name-by-id-osr-bad-identifier.js: Added.
+ * stress/dfg-get-private-name-by-id.js: Added.
+ * stress/dfg-get-private-name-by-offset-osr-bad-identifier.js: Added.
+ * stress/dfg-get-private-name-by-offset-osr-bad-structure.js: Added.
+ * stress/dfg-get-private-name-by-offset.js: Added.
+ * stress/dfg-get-private-name-by-val-generic.js: Added.
+ * stress/ftl-get-private-name-by-id.js: Added.
+ * stress/ftl-get-private-name-by-offset-multi.js: Added.
+ * stress/get-private-name-with-constant-ident.js: Added.
+ * stress/get-private-name-with-constant-symbol.js: Added.
+ * stress/get-private-name-with-different-symbol.js: Added.
+
2020-10-20 Saam Barati <sbar...@apple.com>
Don't OSR exit to bc#0 for FTL argument type checks during loop OSR entry
Added: trunk/JSTests/microbenchmarks/class-fields-private/monomorphic-get-private-field.js (0 => 268794)
--- trunk/JSTests/microbenchmarks/class-fields-private/monomorphic-get-private-field.js (rev 0)
+++ trunk/JSTests/microbenchmarks/class-fields-private/monomorphic-get-private-field.js 2020-10-21 14:06:02 UTC (rev 268794)
@@ -0,0 +1,20 @@
+//@ requireOptions("--usePrivateClassFields=true")
+
+class C {
+ #field;
+
+ constructor(i) {
+ this.#field = i;
+ }
+
+ getField() {
+ return this.#field;
+ }
+}
+noInline(C.prototype.getField);
+
+let c = new C("test");
+for (let i = 0; i < 5000000; i++) {
+ if (c.getField() !== "test")
+ throw new Error("unexpected field value");
+}
Added: trunk/JSTests/microbenchmarks/class-fields-private/polymorphic-get-private-field.js (0 => 268794)
--- trunk/JSTests/microbenchmarks/class-fields-private/polymorphic-get-private-field.js (rev 0)
+++ trunk/JSTests/microbenchmarks/class-fields-private/polymorphic-get-private-field.js 2020-10-21 14:06:02 UTC (rev 268794)
@@ -0,0 +1,34 @@
+//@ requireOptions("--usePrivateClassFields=true")
+
+class C {
+ #field;
+
+ setField(value) {
+ this.#field = value;
+ }
+
+ getField() {
+ return this.#field;
+ }
+}
+noInline(C.prototype.getField);
+
+let c1 = new C();
+c1.foo = 0;
+c1.setField("a");
+
+let c2 = new C();
+c2.bar = 0;
+c2.setField("b");
+
+let c3 = new C();
+c3.baz = 0;
+c3.setField("c");
+
+let arr = [c1, c2, c3];
+let values = ["a", "b", "c"];
+for (let i = 0; i < 5000000; i++) {
+ if (arr[i % arr.length].getField() !== values[i % values.length])
+ throw new Error("unexpected field value");
+}
+
Added: trunk/JSTests/stress/dfg-get-private-name-by-id-generic.js (0 => 268794)
--- trunk/JSTests/stress/dfg-get-private-name-by-id-generic.js (rev 0)
+++ trunk/JSTests/stress/dfg-get-private-name-by-id-generic.js 2020-10-21 14:06:02 UTC (rev 268794)
@@ -0,0 +1,76 @@
+//@ requireOptions("--allowUnsupportedTiers=true", "--usePrivateClassFields=true")
+
+// Convoluted program which will compile turn GetPrivateName into GetPrivateNameById. GetById
+// will never OSR here due to bad structures, but will instead repatch to the generic case.
+
+let assert = {
+ equals: function (a, e) {
+ if (a !== e)
+ throw new Error(`Expected: ${e} but got: ${a}`);
+ },
+ throws: function(exception, functor) {
+ let threwException;
+ try {
+ functor();
+ threwException = false;
+ } catch(e) {
+ threwException = true;
+ if (!e instanceof exception)
+ throw new Error(`Expected to throw a ${exception.name} but it threw: ${e}`);
+ }
+
+ if (!threwException)
+ throw new Error(`Expected to throw a ${exception.name} but did not throw`);
+ }
+};
+
+class Base {
+ constructor(i) {
+ if (i & 2)
+ this.commonExtraStuff = true;
+ if (i & 4)
+ this.megaExtraStuff = true;
+ if (i > 10000) {
+ if (i & 1)
+ this.superWhatever = true;
+ }
+
+ }
+}
+
+class C extends Base {
+ #field = 'test';
+
+ constructor(i) {
+ super(i);
+ if (i > 5000)
+ this.extraStuff = true;
+ if (i === 9999)
+ this.moreStuff = true;
+ }
+
+ setField(v) {
+ this.#field = v;
+ }
+
+ getField() {
+ return this.#field;
+ }
+}
+noInline(C.prototype.constructor);
+noDFG(C.prototype.constructor);
+noFTL(C.prototype.constructor);
+noInline(C.prototype.setField);
+noInline(C.prototype.getField);
+noDFG(C.prototype.setField);
+noFTL(C.prototype.setField);
+
+for (let i = 0; i < 20000; ++i) {
+ let c = new C(i);
+ assert.equals(c.getField(), 'test');
+ c.setField('foo' + i);
+ assert.equals(c.getField(), 'foo' + i);
+}
+
+let c = new C(100);
+assert.throws(TypeError, () => c.getField.call({}));
Added: trunk/JSTests/stress/dfg-get-private-name-by-id-osr-bad-identifier.js (0 => 268794)
--- trunk/JSTests/stress/dfg-get-private-name-by-id-osr-bad-identifier.js (rev 0)
+++ trunk/JSTests/stress/dfg-get-private-name-by-id-osr-bad-identifier.js 2020-10-21 14:06:02 UTC (rev 268794)
@@ -0,0 +1,76 @@
+//@ requireOptions("--allowUnsupportedTiers=true", "--usePrivateClassFields=true")
+
+// Convoluted program which will compile turn GetPrivateName into GetPrivateNameById, and then OSRExit
+// from getField() due to a CheckIsConstant failure when a different identifier is used.
+
+let assert = {
+ equals: function (a, e) {
+ if (a !== e)
+ throw new Error(`Expected: ${e} but got: ${a}`);
+ },
+ throws: function(exception, functor) {
+ let threwException;
+ try {
+ functor();
+ threwException = false;
+ } catch(e) {
+ threwException = true;
+ if (!e instanceof exception)
+ throw new Error(`Expected to throw a ${exception.name} but it threw: ${e}`);
+ }
+
+ if (!threwException)
+ throw new Error(`Expected to throw a ${exception.name} but did not throw`);
+ }
+};
+
+class Base {
+ constructor(i) {
+ if (i & 1)
+ this.differentStructure = `extra${i}`;
+ }
+}
+
+function factoryClass() {
+ class C extends Base {
+ #field = 'test';
+
+ setField(v) {
+ this.#field = v;
+ }
+
+ getField() {
+ return this.#field;
+ }
+ }
+ noInline(C.prototype.setField);
+ noInline(C.prototype.getField);
+ noDFG(C.prototype.setField);
+ noFTL(C.prototype.setField);
+
+ return C;
+}
+
+let C = factoryClass();
+
+for (let i = 0; i < 10000; i++) {
+ let c = new C(i);
+ assert.equals(c.getField(), 'test');
+ c.setField('foo' + i);
+ assert.equals(c.getField(), 'foo' + i);
+}
+
+let C2 = factoryClass(); // invalidate first version of setField due to Scope invalidation point
+
+// Triggers new version without folding get_from_scope
+for (let i = 0; i < 10000; i++) {
+ let c = new C(i);
+ assert.equals(c.getField(), 'test');
+ c.setField('foo' + i);
+ assert.equals(c.getField(), 'foo' + i);
+}
+
+let c2 = new C2();
+assert.throws(TypeError, function () {
+ c2.getField.call(new C()); // trigger OSR exit due to bad constant value
+});
Added: trunk/JSTests/stress/dfg-get-private-name-by-id.js (0 => 268794)
--- trunk/JSTests/stress/dfg-get-private-name-by-id.js (rev 0)
+++ trunk/JSTests/stress/dfg-get-private-name-by-id.js 2020-10-21 14:06:02 UTC (rev 268794)
@@ -0,0 +1,98 @@
+//@requireOptions("--allowUnsupportedTiers=true", "--usePrivateClassFields=true")
+
+// With many incompatible structure variants, this test checks the GetPrivateName lowering in DFG when
+// reducing to GetByOffset is not possible.
+
+function assert(expr, message) {
+ if (!expr)
+ throw new Error(`Assertion Failed: ${message}`);
+}
+Object.assign(assert, {
+ equals(actual, expected) {
+ assert(actual === expected, `expected ${expected} but found ${actual}`);
+ },
+ throws(fn, errorType) {
+ try {
+ fn();
+ } catch (e) {
+ if (typeof errorType === "function")
+ assert(e instanceof errorType, `expected to throw ${errorType.name} but threw ${e}`);
+ return;
+ }
+ assert(false, `expected to throw, but no exception was thrown.`);
+ }
+});
+
+
+let bases = [
+ {
+ },
+ {
+ a: 1,
+ },
+ {
+ b: 1,
+ a: 2,
+ },
+ {
+ c: 1,
+ d: 2,
+ a: 3,
+ },
+ {
+ },
+ {
+ z: 1,
+ a: 2,
+ b: 3,
+ q: 4,
+ },
+ {
+ a: 1,
+ d: 2,
+ z: 3,
+ x: 4,
+ f: 5,
+ },
+ {
+ g: 1,
+ b: 2,
+ d: 3,
+ q: 4,
+ x: 5,
+ d: 6,
+ }
+];
+class Base {
+ constructor(i) {
+ if (i < 100)
+ return Object.assign({}, bases[i & 3]);
+ return Object.assign({}, bases[i % bases.length]);
+ }
+}
+class C extends Base {
+ #private = "private";
+ getPrivate() { return this.#private; }
+}
+noInline(C.constructor);
+noDFG(C.constructor);
+noFTL(C.constructor);
+noFTL(C.prototype.getPrivate);
+noInline(C.prototype.getPrivate);
+
+let getPrivate = C.prototype.getPrivate;
+
+function test(o) {
+ assert.equals(getPrivate.call(o), "private");
+}
+
+test(new C(0), 0);
+test(new C(0), 0);
+test(new C(0), 0);
+for (var i = 0; i < 1000; ++i) {
+ test(new C(i));
+ optimizeNextInvocation(test);
+}
+assert.throws(() => {
+ test({})
+});
Added: trunk/JSTests/stress/dfg-get-private-name-by-offset-osr-bad-identifier.js (0 => 268794)
--- trunk/JSTests/stress/dfg-get-private-name-by-offset-osr-bad-identifier.js (rev 0)
+++ trunk/JSTests/stress/dfg-get-private-name-by-offset-osr-bad-identifier.js 2020-10-21 14:06:02 UTC (rev 268794)
@@ -0,0 +1,69 @@
+//@ requireOptions("--allowUnsupportedTiers=true", "--usePrivateClassFields=true")
+
+// Convoluted program which will compile turn GetPrivateName into GetByOffset, and then OSRExit
+// from getField() due to a CheckIsConstant failure when a different identifier is used.
+
+let assert = {
+ equals: function (a, e) {
+ if (a !== e)
+ throw new Error(`Expected: ${e} but got: ${a}`);
+ },
+ throws: function(exception, functor) {
+ let threwException;
+ try {
+ functor();
+ threwException = false;
+ } catch(e) {
+ threwException = true;
+ if (!e instanceof exception)
+ throw new Error(`Expected to throw a ${exception.name} but it threw: ${e}`);
+ }
+
+ if (!threwException)
+ throw new Error(`Expected to throw a ${exception.name} but did not throw`);
+ }
+};
+
+function factoryClass() {
+ class C {
+ #field = 'test';
+
+ setField(v) {
+ this.#field = v;
+ }
+
+ getField() {
+ return this.#field;
+ }
+ }
+ noInline(C.prototype.setField);
+ noInline(C.prototype.getField);
+ noDFG(C.prototype.setField);
+ noFTL(C.prototype.setField);
+
+ return C;
+}
+
+let C = factoryClass();
+
+for (let i = 0; i < 10000; i++) {
+ let c = new C();
+ assert.equals(c.getField(), 'test');
+ c.setField('foo' + i);
+ assert.equals(c.getField(), 'foo' + i);
+}
+
+let C2 = factoryClass(); // invalidate first version of setField due to Scope invalidation point
+
+// Triggers new version without folding get_from_scope
+for (let i = 0; i < 10000; i++) {
+ let c = new C();
+ assert.equals(c.getField(), 'test');
+ c.setField('foo' + i);
+ assert.equals(c.getField(), 'foo' + i);
+}
+
+let c2 = new C2();
+assert.throws(TypeError, function () {
+ c2.getField.call(new C()); // trigger OSR exit due to bad constant value
+});
Added: trunk/JSTests/stress/dfg-get-private-name-by-offset-osr-bad-structure.js (0 => 268794)
--- trunk/JSTests/stress/dfg-get-private-name-by-offset-osr-bad-structure.js (rev 0)
+++ trunk/JSTests/stress/dfg-get-private-name-by-offset-osr-bad-structure.js 2020-10-21 14:06:02 UTC (rev 268794)
@@ -0,0 +1,62 @@
+//@ requireOptions("--allowUnsupportedTiers=true", "--usePrivateClassFields=true")
+
+// Convoluted program which will compile turn GetPrivateName into GetByOffset, and then OSRExit
+// from getField() due to a CheckStructure failure
+
+let assert = {
+ equals: function (a, e) {
+ if (a !== e)
+ throw new Error(`Expected: ${e} but got: ${a}`);
+ },
+ throws: function(exception, functor) {
+ let threwException;
+ try {
+ functor();
+ threwException = false;
+ } catch(e) {
+ threwException = true;
+ if (!e instanceof exception)
+ throw new Error(`Expected to throw a ${exception.name} but it threw: ${e}`);
+ }
+
+ if (!threwException)
+ throw new Error(`Expected to throw a ${exception.name} but did not throw`);
+ }
+};
+
+class C {
+ #field = 'test';
+
+ constructor(i) {
+ if (i > 5000)
+ this.extraStuff = true; // Invalidate loop due to structure change
+ if (i === 9999)
+ this.moreStuff = true; // Trigger a BadCache OSR on get_private_name
+ }
+
+ setField(v) {
+ this.#field = v;
+ }
+
+ getField() {
+ return this.#field;
+ }
+}
+noInline(C.prototype.constructor);
+noDFG(C.prototype.constructor);
+noFTL(C.prototype.constructor);
+noInline(C.prototype.setField);
+noInline(C.prototype.getField);
+noDFG(C.prototype.setField);
+noFTL(C.prototype.setField);
+
+for (let i = 0; i < 10000; ++i) {
+ let c = new C(i);
+ assert.equals(c.getField(), 'test');
+ c.setField('foo' + i);
+ assert.equals(c.getField(), 'foo' + i);
+}
+
+// Check semantic failure still functions
+let c = new C(100);
+assert.throws(TypeError, () => c.getField.call({}));
Added: trunk/JSTests/stress/dfg-get-private-name-by-offset.js (0 => 268794)
--- trunk/JSTests/stress/dfg-get-private-name-by-offset.js (rev 0)
+++ trunk/JSTests/stress/dfg-get-private-name-by-offset.js 2020-10-21 14:06:02 UTC (rev 268794)
@@ -0,0 +1,62 @@
+//@requireOptions("--allowUnsupportedTiers=true", "--usePrivateClassFields=true")
+
+// With no incompatible structure variants, GetPrivateName is converted to GetByOffset
+// during bytecode parsing. We test that it's still semantically valid.
+
+function assert(expr, message) {
+ if (!expr)
+ throw new Error(`Assertion Failed: ${message}`);
+}
+Object.assign(assert, {
+ equals(actual, expected) {
+ assert(actual === expected, `expected ${expected} but found ${actual}`);
+ },
+ throws(fn, errorType) {
+ try {
+ fn();
+ } catch (e) {
+ if (typeof errorType === "function")
+ assert(e instanceof errorType, `expected to throw ${errorType.name} but threw ${e}`);
+ return;
+ }
+ assert(false, `expected to throw, but no exception was thrown.`);
+ }
+});
+
+class C {
+ #private = "private";
+ getPrivate(o) { return o.#private; }
+ constructor(i) {
+ if (i === 995)
+ return {};
+ }
+}
+noInline(C.constructor);
+noDFG(C.constructor);
+noFTL(C.constructor);
+noFTL(C.prototype.getPrivate);
+
+let getPrivate = C.prototype.getPrivate;
+
+function test(o) {
+ return getPrivate(o);
+}
+neverInlineFunction(test);
+
+// Warmup
+test(new C, 0);
+test(new C, 0);
+test(new C, 0);
+
+var i;
+for (var j = 0; j < 2; ++j) {
+ assert.throws(() => {
+ for (i = 0; i < 2000; ++i) {
+ assert.equals(test(new C(i)), "private");
+ optimizeNextInvocation(test);
+ }
+ }, TypeError);
+
+ // We should throw on iteration 995, because this will result in an invalid load.
+ assert.equals(i, 995);
+}
Added: trunk/JSTests/stress/dfg-get-private-name-by-val-generic.js (0 => 268794)
--- trunk/JSTests/stress/dfg-get-private-name-by-val-generic.js (rev 0)
+++ trunk/JSTests/stress/dfg-get-private-name-by-val-generic.js 2020-10-21 14:06:02 UTC (rev 268794)
@@ -0,0 +1,75 @@
+//@ requireOptions("--allowUnsupportedTiers=true", "--usePrivateClassFields=true")
+
+// Convoluted program which will compile turn GetPrivateName into JITGetByVal IC. GetByVal
+// will never OSR here due to bad structures, but will instead repatch to the generic case.
+
+let assert = {
+ equals: function (a, e) {
+ if (a !== e)
+ throw new Error(`Expected: ${e} but got: ${a}`);
+ },
+ throws: function(exception, functor) {
+ let threwException;
+ try {
+ functor();
+ threwException = false;
+ } catch(e) {
+ threwException = true;
+ if (!e instanceof exception)
+ throw new Error(`Expected to throw a ${exception.name} but it threw: ${e}`);
+ }
+
+ if (!threwException)
+ throw new Error(`Expected to throw a ${exception.name} but did not throw`);
+ }
+};
+
+class Base {
+ constructor(i) {
+ if (i & 1)
+ this.megaCommonExtraStuff = true;
+ if (i & 2)
+ this.commonExtraStuff = true;
+ if (i & 4)
+ this.moreExtraStuff = true;
+ if (i & 8)
+ this.soMuchExtraStuff = true;
+ }
+}
+
+class C extends Base {
+ #field = 'test';
+
+ constructor(i) {
+ super(i);
+ if (i > 5000)
+ this.extraStuff = true;
+ if (i === 9999)
+ this.moreStuff = true;
+ }
+
+ setField(v) {
+ this.#field = v;
+ }
+
+ getField() {
+ return this.#field;
+ }
+}
+noInline(C.prototype.constructor);
+noDFG(C.prototype.constructor);
+noFTL(C.prototype.constructor);
+noInline(C.prototype.setField);
+noInline(C.prototype.getField);
+noDFG(C.prototype.setField);
+noFTL(C.prototype.setField);
+
+for (let i = 0; i < 10000; ++i) {
+ let c = new C(i);
+ assert.equals(c.getField(), 'test');
+ c.setField('foo' + i);
+ assert.equals(c.getField(), 'foo' + i);
+}
+
+let c = new C(100);
+assert.throws(TypeError, () => c.getField.call({}));
Added: trunk/JSTests/stress/ftl-get-private-name-by-id.js (0 => 268794)
--- trunk/JSTests/stress/ftl-get-private-name-by-id.js (rev 0)
+++ trunk/JSTests/stress/ftl-get-private-name-by-id.js 2020-10-21 14:06:02 UTC (rev 268794)
@@ -0,0 +1,107 @@
+//@requireOptions("--allowUnsupportedTiers=true", "--usePrivateClassFields=true", "--useAccessInlining=false")
+
+// With many incompatible structure variants, this test checks the GetPrivateName lowering in DFG when
+// reducing to GetByOffset is not possible.
+// This test disables inlining loads to GetByOffset in order to prevent reducing the GetPrivateNameById to MultiGetByOffset in the
+// DFGConstantFoldingPhase. It is conceivably possible for this scenario to be encountered without disabling inline access.
+
+function assert(expr, message) {
+ if (!expr)
+ throw new Error(`Assertion Failed: ${message}`);
+}
+Object.assign(assert, {
+ equals(actual, expected) {
+ assert(actual === expected, `expected ${expected} but found ${actual}`);
+ },
+ throws(fn, errorType) {
+ try {
+ fn();
+ } catch (e) {
+ if (typeof errorType === "function")
+ assert(e instanceof errorType, `expected to throw ${errorType.name} but threw ${e}`);
+ return;
+ }
+ assert(false, `expected to throw, but no exception was thrown.`);
+ }
+});
+
+let bases = [
+ {
+ },
+ {
+ a: 1,
+ },
+ {
+ b: 1,
+ a: 2,
+ },
+ {
+ c: 1,
+ d: 2,
+ a: 3,
+ },
+ {
+ },
+ {
+ z: 1,
+ a: 2,
+ b: 3,
+ q: 4,
+ },
+ {
+ a: 1,
+ d: 2,
+ z: 3,
+ x: 4,
+ f: 5,
+ },
+ {
+ g: 1,
+ b: 2,
+ d: 3,
+ q: 4,
+ x: 5,
+ d: 6,
+ },
+ {
+ z: 1,
+ q: 2,
+ f: 3,
+ y: 4,
+ a: 5,
+ 0: 6,
+ k: 7,
+ },
+];
+class Base {
+ constructor(i) {
+ if (i < 100)
+ return Object.assign({}, bases[i & 3]);
+ return Object.assign({}, bases[i % bases.length]);
+ }
+}
+class C extends Base {
+ #private = "private";
+ getPrivate() { return this.#private; }
+}
+noInline(C.constructor);
+noDFG(C.constructor);
+noFTL(C.constructor);
+noInline(C.prototype.getPrivate);
+
+let getPrivate = C.prototype.getPrivate;
+
+function test(o) {
+ assert.equals(getPrivate.call(o), "private");
+}
+
+test(new C(0), 0);
+test(new C(0), 0);
+test(new C(0), 0);
+for (var i = 0; !isFinalTier(test) && i < 10000; ++i) {
+ test(new C(i));
+ optimizeNextInvocation(test);
+}
+assert.throws(() => {
+ test({})
+});
Added: trunk/JSTests/stress/ftl-get-private-name-by-offset-multi.js (0 => 268794)
--- trunk/JSTests/stress/ftl-get-private-name-by-offset-multi.js (rev 0)
+++ trunk/JSTests/stress/ftl-get-private-name-by-offset-multi.js 2020-10-21 14:06:02 UTC (rev 268794)
@@ -0,0 +1,87 @@
+//@requireOptions("--allowUnsupportedTiers=true", "--usePrivateClassFields=true")
+
+// With many incompatible structure variants, this test will fail to generate GetByOffset dwuring DFGByteCodeParsing.
+// During FTL compilation, it will try again in DFGConstantFoldingPhase.
+function assert(expr, message) {
+ if (!expr)
+ throw new Error(`Assertion Failed: ${message}`);
+}
+Object.assign(assert, {
+ equals(actual, expected) {
+ assert(actual === expected, `expected ${expected} but found ${actual}`);
+ },
+ throws(fn, errorType) {
+ try {
+ fn();
+ } catch (e) {
+ if (typeof errorType === "function")
+ assert(e instanceof errorType, `expected to throw ${errorType.name} but threw ${e}`);
+ return;
+ }
+ assert(false, `expected to throw, but no exception was thrown.`);
+ }
+});
+
+let bases = [
+ {
+ },
+ {
+ a: 1,
+ },
+ {
+ b: 1,
+ a: 2,
+ },
+ {
+ c: 1,
+ d: 2,
+ a: 3,
+ },
+ {
+ },
+ {
+ z: 1,
+ a: 2,
+ b: 3,
+ q: 4,
+ },
+ {
+ a: 1,
+ d: 2,
+ z: 3,
+ x: 4,
+ f: 5,
+ },
+];
+class Base {
+ constructor(i) {
+ if (i < 100)
+ return Object.assign({}, bases[i & 3]);
+ return Object.assign({}, bases[i % bases.length]);
+ }
+}
+class C extends Base {
+ #private = "private";
+ getPrivate() { return this.#private; }
+}
+noInline(C.constructor);
+noDFG(C.constructor);
+noFTL(C.constructor);
+noInline(C.prototype.getPrivate);
+
+let getPrivate = C.prototype.getPrivate;
+
+function test(o) {
+ assert.equals(getPrivate.call(o), "private");
+}
+
+test(new C(0), 0);
+test(new C(0), 0);
+test(new C(0), 0);
+for (var i = 0; !isFinalTier(test) && i < 10000; ++i) {
+ test(new C(i));
+ optimizeNextInvocation(test);
+}
+assert.throws(() => {
+ test({})
+});
Added: trunk/JSTests/stress/get-private-name-with-constant-ident.js (0 => 268794)
--- trunk/JSTests/stress/get-private-name-with-constant-ident.js (rev 0)
+++ trunk/JSTests/stress/get-private-name-with-constant-ident.js 2020-10-21 14:06:02 UTC (rev 268794)
@@ -0,0 +1,53 @@
+//@ requireOptions("--allowUnsupportedTiers=true", "--usePrivateClassFields=true")
+
+function assert(expr, message) {
+ if (!expr)
+ throw new Error(`Assertion Failed: ${message}`);
+}
+Object.assign(assert, {
+ equals(actual, expected) {
+ assert(actual === expected, `expected ${expected} but found ${actual}`);
+ },
+ throws(fn, errorType) {
+ try {
+ fn();
+ } catch (e) {
+ if (typeof errorType === "function")
+ assert(e instanceof errorType, `expected to throw ${errorType.name} but threw ${e}`);
+ return;
+ }
+ assert(false, `expected to throw, but no exception was thrown.`);
+ }
+});
+
+let i = 0;
+
+class C {
+ #field = this.init();
+
+ init() {
+ if (i % 2)
+ this.anotherField = i;
+ return 'test';
+ }
+
+ setField(v) {
+ this.#field = v;
+ }
+
+ getField() {
+ return this.#field;
+ }
+}
+noInline(C.prototype.setField);
+noInline(C.prototype.getField);
+noDFG(C.prototype.setField);
+noFTL(C.prototype.setField);
+
+for (; i < 10000; i++) {
+ count = i;
+ let c = new C();
+ assert.equals(c.getField(), 'test');
+ c.setField('foo' + i);
+ assert.equals(c.getField(), 'foo' + i);
+}
Added: trunk/JSTests/stress/get-private-name-with-constant-symbol.js (0 => 268794)
--- trunk/JSTests/stress/get-private-name-with-constant-symbol.js (rev 0)
+++ trunk/JSTests/stress/get-private-name-with-constant-symbol.js 2020-10-21 14:06:02 UTC (rev 268794)
@@ -0,0 +1,53 @@
+//@ requireOptions("--allowUnsupportedTiers=true", "--usePrivateClassFields=true")
+
+function assert(expr, message) {
+ if (!expr)
+ throw new Error(`Assertion Failed: ${message}`);
+}
+Object.assign(assert, {
+ equals(actual, expected) {
+ assert(actual === expected, `expected ${expected} but found ${actual}`);
+ },
+ throws(fn, errorType) {
+ try {
+ fn();
+ } catch (e) {
+ if (typeof errorType === "function")
+ assert(e instanceof errorType, `expected to throw ${errorType.name} but threw ${e}`);
+ return;
+ }
+ assert(false, `expected to throw, but no exception was thrown.`);
+ }
+});
+
+let i = 0;
+
+class C {
+ #field = this.init();
+
+ init() {
+ if (i % 2)
+ this.anotherField = i;
+ return 'test';
+ }
+
+ setField(v) {
+ this.#field = v;
+ }
+
+ getField() {
+ return this.#field;
+ }
+}
+noInline(C.prototype.setField);
+noInline(C.prototype.getField);
+noDFG(C.prototype.setField);
+noFTL(C.prototype.setField);
+
+for (; i < 10000; i++) {
+ count = i;
+ let c = new C();
+ assert.equals(c.getField(), 'test');
+ c.setField('foo' + i);
+ assert.equals(c.getField(), 'foo' + i);
+}
Added: trunk/JSTests/stress/get-private-name-with-different-symbol.js (0 => 268794)
--- trunk/JSTests/stress/get-private-name-with-different-symbol.js (rev 0)
+++ trunk/JSTests/stress/get-private-name-with-different-symbol.js 2020-10-21 14:06:02 UTC (rev 268794)
@@ -0,0 +1,54 @@
+//@ requireOptions("--allowUnsupportedTiers=true", "--usePrivateClassFields=true")
+
+function assert(expr, message) {
+ if (!expr)
+ throw new Error(`Assertion Failed: ${message}`);
+}
+Object.assign(assert, {
+ equals(actual, expected) {
+ assert(actual === expected, `expected ${expected} but found ${actual}`);
+ },
+ throws(fn, errorType) {
+ try {
+ fn();
+ } catch (e) {
+ if (typeof errorType === "function")
+ assert(e instanceof errorType, `expected to throw ${errorType.name} but threw ${e}`);
+ return;
+ }
+ assert(false, `expected to throw, but no exception was thrown.`);
+ }
+});
+
+function classDeclaration() {
+ class C {
+ #field;
+
+ setField(v) {
+ this.#field = v;
+ }
+
+ getField() {
+ return this.#field;
+ }
+ }
+
+ noDFG(C.prototype.constructor);
+ noFTL(C.prototype.constructor);
+ noInline(C.prototype.setField);
+ noDFG(C.prototype.setField);
+ noFTL(C.prototype.setField);
+
+ noInline(C.prototype.getField);
+
+ return C;
+}
+noInline(classDeclaration);
+let C1 = classDeclaration();
+let C2 = classDeclaration();
+
+for (let i = 0; i < 10000; i++) {
+ let c = i % 2 ? new C1 : new C2;
+ c.setField('test' + i);
+ assert.equals(c.getField(), 'test' + i);
+}
Modified: trunk/Source/_javascript_Core/ChangeLog (268793 => 268794)
--- trunk/Source/_javascript_Core/ChangeLog 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/ChangeLog 2020-10-21 14:06:02 UTC (rev 268794)
@@ -1,3 +1,80 @@
+2020-10-21 Caitlin Potter <ca...@igalia.com>
+
+ [JSC] support op_get_private_name in DFG and FTL
+ https://bugs.webkit.org/show_bug.cgi?id=214861
+
+ Reviewed by Filip Pizlo.
+
+ Adds DFG/FTL support for op_get_private_name.
+
+ During DFG bytecode parsing, we will attempt, if deemed possible by
+ the information available, to output a GetByOffset operation. If a
+ single private field identifier is used in all cases (the common case),
+ but there are too many structure variants, a GetPrivateNameById
+ operation is emitted instead. Failing that, the GetPrivateName
+ operation is produced, which produces a GetByVal IC like in the
+ baseline JIT.
+
+ In FTL, GetPrivateNameByID can be reduced to [Multi]GetByOffset in the
+ DFGConstantFoldingPhase, or a GetByID IC when lowering to B3.
+
+ * bytecode/GetByStatus.cpp:
+ (JSC::GetByStatus::computeFromLLInt):
+ * bytecode/StructureStubInfo.h:
+ (JSC::appropriateOptimizingGetByIdFunction):
+ (JSC::appropriateGenericGetByIdFunction):
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::simplifyGetByStatus):
+ (JSC::DFG::ByteCodeParser::handleGetById):
+ (JSC::DFG::ByteCodeParser::handleGetPrivateNameById):
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGConstantFoldingPhase.cpp:
+ (JSC::DFG::ConstantFoldingPhase::foldConstants):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::convertToGetByOffset):
+ (JSC::DFG::Node::convertToMultiGetByOffset):
+ (JSC::DFG::Node::hasCacheableIdentifier):
+ (JSC::DFG::Node::hasHeapPrediction):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileGetPrivateName):
+ (JSC::DFG::SpeculativeJIT::compileGetPrivateNameByVal):
+ (JSC::DFG::SpeculativeJIT::compileGetPrivateNameById):
+ * dfg/DFGSpeculativeJIT.h:
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+ (JSC::FTL::DFG::LowerDFGToB3::getPrivateName):
+ (JSC::FTL::DFG::LowerDFGToB3::compileGetPrivateName):
+ (JSC::FTL::DFG::LowerDFGToB3::compileGetPrivateNameById):
+ * jit/ICStats.h:
+ * jit/JITOperations.cpp:
+ (JSC::getPrivateName):
+ (JSC::JSC_DEFINE_JIT_OPERATION):
+ * jit/JITOperations.h:
+ * jit/Repatch.cpp:
+ (JSC::appropriateOptimizingGetByFunction):
+ (JSC::appropriateGetByFunction):
+ (JSC::tryCacheGetBy):
+ * jit/Repatch.h:
+ * runtime/OptionsList.h:
+
2020-10-20 Saam Barati <sbar...@apple.com>
Don't OSR exit to bc#0 for FTL argument type checks during loop OSR entry
Modified: trunk/Source/_javascript_Core/bytecode/GetByStatus.cpp (268793 => 268794)
--- trunk/Source/_javascript_Core/bytecode/GetByStatus.cpp 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/bytecode/GetByStatus.cpp 2020-10-21 14:06:02 UTC (rev 268794)
@@ -108,6 +108,11 @@
break;
}
+ case op_get_private_name:
+ // FIXME: Consider using LLInt caches or IC information to populate GetByStatus
+ // https://bugs.webkit.org/show_bug.cgi?id=217245
+ return GetByStatus(NoInformation, false);
+
default: {
ASSERT_NOT_REACHED();
return GetByStatus(NoInformation, false);
Modified: trunk/Source/_javascript_Core/bytecode/StructureStubInfo.h (268793 => 268794)
--- trunk/Source/_javascript_Core/bytecode/StructureStubInfo.h 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/bytecode/StructureStubInfo.h 2020-10-21 14:06:02 UTC (rev 268794)
@@ -404,6 +404,8 @@
return operationTryGetByIdOptimize;
case AccessType::GetByIdDirect:
return operationGetByIdDirectOptimize;
+ case AccessType::GetPrivateName:
+ return operationGetPrivateNameByIdOptimize;
case AccessType::GetByIdWithThis:
default:
ASSERT_NOT_REACHED();
@@ -420,6 +422,8 @@
return operationTryGetByIdGeneric;
case AccessType::GetByIdDirect:
return operationGetByIdDirectGeneric;
+ case AccessType::GetPrivateName:
+ return operationGetPrivateNameByIdGeneric;
case AccessType::GetByIdWithThis:
default:
ASSERT_NOT_REACHED();
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (268793 => 268794)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2020-10-21 14:06:02 UTC (rev 268794)
@@ -3348,6 +3348,7 @@
makeHeapTopForNode(node);
break;
+ case GetPrivateNameById:
case GetByIdDirect:
case GetByIdDirectFlush:
case GetById:
@@ -3383,6 +3384,7 @@
break;
}
+ case GetPrivateName:
case GetByValWithThis:
case GetByIdWithThis:
clobberWorld();
Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (268793 => 268794)
--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2020-10-21 14:06:02 UTC (rev 268794)
@@ -244,8 +244,11 @@
template<typename Op>
void parseGetById(const Instruction*);
+ void simplifyGetByStatus(Node* base, GetByStatus&);
void handleGetById(
VirtualRegister destination, SpeculatedType, Node* base, CacheableIdentifier, unsigned identifierNumber, GetByStatus, AccessType, BytecodeIndex osrExitIndex);
+ void handleGetPrivateNameById(
+ VirtualRegister destination, SpeculatedType prediction, Node* base, CacheableIdentifier, unsigned identifierNumber, GetByStatus);
void emitPutById(
Node* base, CacheableIdentifier, Node* value, const PutByIdStatus&, bool isDirect, ECMAMode);
void handlePutById(
@@ -4530,9 +4533,7 @@
return handlePutByOffset(base, identifier, variant.offset(), value);
}
-void ByteCodeParser::handleGetById(
- VirtualRegister destination, SpeculatedType prediction, Node* base, CacheableIdentifier identifier, unsigned identifierNumber,
- GetByStatus getByStatus, AccessType type, BytecodeIndex osrExitIndex)
+void ByteCodeParser::simplifyGetByStatus(Node* base, GetByStatus& getByStatus)
{
// Attempt to reduce the set of things in the GetByStatus.
if (base->op() == NewObject) {
@@ -4549,6 +4550,13 @@
if (ok)
getByStatus.filter(base->structure().get());
}
+}
+
+void ByteCodeParser::handleGetById(
+ VirtualRegister destination, SpeculatedType prediction, Node* base, CacheableIdentifier identifier, unsigned identifierNumber,
+ GetByStatus getByStatus, AccessType type, BytecodeIndex osrExitIndex)
+{
+ simplifyGetByStatus(base, getByStatus);
NodeType getById;
if (type == AccessType::GetById)
@@ -4716,6 +4724,76 @@
getter, numberOfParameters - 1, registerOffset, *variant.callLinkStatus(), prediction);
}
+// A variant on handleGetById which is more limited in scope
+void ByteCodeParser::handleGetPrivateNameById(
+ VirtualRegister destination, SpeculatedType prediction, Node* base, CacheableIdentifier identifier, unsigned identifierNumber, GetByStatus getByStatus)
+{
+ simplifyGetByStatus(base, getByStatus);
+
+ ASSERT(!getByStatus.isCustom());
+ ASSERT(!getByStatus.makesCalls());
+ if (!getByStatus.isSimple() || !getByStatus.numVariants() || !Options::useAccessInlining()) {
+ set(destination,
+ addToGraph(GetPrivateNameById, OpInfo(identifier), OpInfo(prediction), base, nullptr));
+ return;
+ }
+
+ if (getByStatus.numVariants() > 1) {
+ if (!m_graph.m_plan.isFTL()
+ || !Options::usePolymorphicAccessInlining()
+ || getByStatus.numVariants() > Options::maxPolymorphicAccessInliningListSize()) {
+ set(destination,
+ addToGraph(GetPrivateNameById, OpInfo(identifier), OpInfo(prediction), base, nullptr));
+ return;
+ }
+
+ addToGraph(FilterGetByStatus, OpInfo(m_graph.m_plan.recordedStatuses().addGetByStatus(currentCodeOrigin(), getByStatus)), base);
+
+ Vector<MultiGetByOffsetCase, 2> cases;
+
+ for (const GetByIdVariant& variant : getByStatus.variants()) {
+ ASSERT(variant.intrinsic() == NoIntrinsic);
+ ASSERT(variant.conditionSet().isEmpty());
+
+ GetByOffsetMethod method = GetByOffsetMethod::load(variant.offset());
+ cases.append(MultiGetByOffsetCase(*m_graph.addStructureSet(variant.structureSet()), method));
+ }
+
+ if (UNLIKELY(m_graph.compilation()))
+ m_graph.compilation()->noticeInlinedGetById();
+
+ // 2) Emit a MultiGetByOffset
+ MultiGetByOffsetData* data = ""
+ data->cases = cases;
+ data->identifierNumber = identifierNumber;
+ set(destination,
+ addToGraph(MultiGetByOffset, OpInfo(data), OpInfo(prediction), base));
+ return;
+ }
+
+ // FIXME: If we use the GetByStatus for anything then we should record it and insert a node
+ // after everything else (like the GetByOffset or whatever) that will filter the recorded
+ // GetByStatus. That means that the constant folder also needs to do the same!
+ addToGraph(FilterGetByStatus, OpInfo(m_graph.m_plan.recordedStatuses().addGetByStatus(currentCodeOrigin(), getByStatus)), base);
+
+ ASSERT(getByStatus.numVariants() == 1);
+ GetByIdVariant variant = getByStatus[0];
+
+ Node* loadedValue = load(prediction, base, identifierNumber, variant);
+ if (!loadedValue) {
+ set(destination,
+ addToGraph(GetPrivateNameById, OpInfo(identifier), OpInfo(prediction), base, nullptr));
+ return;
+ }
+
+ if (UNLIKELY(m_graph.compilation()))
+ m_graph.compilation()->noticeInlinedGetById();
+
+ ASSERT(!variant.callLinkStatus());
+ if (variant.intrinsic() == NoIntrinsic)
+ set(destination, loadedValue);
+}
+
void ByteCodeParser::handleDeleteById(
VirtualRegister destination, Node* base, CacheableIdentifier identifier,
unsigned identifierNumber, DeleteByStatus deleteByStatus, ECMAMode ecmaMode)
@@ -6345,6 +6423,41 @@
NEXT_OPCODE(op_put_setter_by_val);
}
+ case op_get_private_name: {
+ auto bytecode = currentInstruction->as<OpGetPrivateName>();
+ SpeculatedType prediction = getPredictionWithoutOSRExit();
+ Node* base = get(bytecode.m_base);
+ Node* property = get(bytecode.m_property);
+ bool compileSingleIdentifier = false;
+
+ GetByStatus getByStatus = GetByStatus::computeFor(m_inlineStackTop->m_profiledBlock, m_inlineStackTop->m_baselineMap, m_icContextStack, currentCodeOrigin());
+
+ CacheableIdentifier identifier;
+ unsigned identifierNumber = 0;
+ if (!m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadIdent)
+ && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadType)
+ && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadConstantValue)) {
+
+ identifier = getByStatus.singleIdentifier();
+ if (identifier) {
+ identifierNumber = m_graph.identifiers().ensure(identifier.uid());
+ ASSERT(identifier.isSymbolCell());
+ FrozenValue* frozen = m_graph.freezeStrong(identifier.cell());
+ addToGraph(CheckIsConstant, OpInfo(frozen), property);
+ compileSingleIdentifier = true;
+ }
+ }
+
+ if (compileSingleIdentifier)
+ handleGetPrivateNameById(bytecode.m_dst, prediction, base, identifier, identifierNumber, getByStatus);
+ else {
+ Node* node = addToGraph(GetPrivateName, OpInfo(), OpInfo(prediction), base, property);
+ m_exitOK = false;
+ set(bytecode.m_dst, node);
+ }
+ NEXT_OPCODE(op_get_private_name);
+ }
+
case op_del_by_id: {
auto bytecode = currentInstruction->as<OpDelById>();
Node* base = get(bytecode.m_base);
Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (268793 => 268794)
--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2020-10-21 14:06:02 UTC (rev 268794)
@@ -673,6 +673,8 @@
case PutSetterByVal:
case PutPrivateName:
case PutPrivateNameById:
+ case GetPrivateName:
+ case GetPrivateNameById:
case DefineDataProperty:
case DefineAccessorProperty:
case DeleteById:
Modified: trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp (268793 => 268794)
--- trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp 2020-10-21 14:06:02 UTC (rev 268794)
@@ -599,7 +599,8 @@
case GetByIdDirect:
case GetByIdDirectFlush:
case GetById:
- case GetByIdFlush: {
+ case GetByIdFlush:
+ case GetPrivateNameById: {
Edge childEdge = node->child1();
Node* child = childEdge.node();
UniquedStringImpl* uid = node->cacheableIdentifier().uid();
Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (268793 => 268794)
--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2020-10-21 14:06:02 UTC (rev 268794)
@@ -324,6 +324,8 @@
case PutSetterByVal:
case PutPrivateName:
case PutPrivateNameById:
+ case GetPrivateName:
+ case GetPrivateNameById:
case PutStack:
case PutToArguments:
case RegExpExec:
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (268793 => 268794)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2020-10-21 14:06:02 UTC (rev 268794)
@@ -1780,6 +1780,17 @@
break;
}
+ case GetPrivateName:
+ case GetPrivateNameById:
+ if (node->child1()->shouldSpeculateCell())
+ fixEdge<CellUse>(node->child1());
+ else
+ fixEdge<UntypedUse>(node->child1());
+
+ if (!node->hasCacheableIdentifier())
+ fixEdge<SymbolUse>(node->child2());
+ break;
+
case DeleteByVal: {
if (node->child1()->shouldSpeculateCell()) {
fixEdge<CellUse>(node->child1());
Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (268793 => 268794)
--- trunk/Source/_javascript_Core/dfg/DFGNode.h 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h 2020-10-21 14:06:02 UTC (rev 268794)
@@ -597,7 +597,7 @@
void convertToGetByOffset(StorageAccessData& data, Edge storage, Edge base)
{
- ASSERT(m_op == GetById || m_op == GetByIdFlush || m_op == GetByIdDirect || m_op == GetByIdDirectFlush || m_op == MultiGetByOffset);
+ ASSERT(m_op == GetById || m_op == GetByIdFlush || m_op == GetByIdDirect || m_op == GetByIdDirectFlush || m_op == GetPrivateNameById || m_op == MultiGetByOffset);
m_opInfo = &data;
children.setChild1(storage);
children.setChild2(base);
@@ -607,7 +607,7 @@
void convertToMultiGetByOffset(MultiGetByOffsetData* data)
{
- RELEASE_ASSERT(m_op == GetById || m_op == GetByIdFlush || m_op == GetByIdDirect || m_op == GetByIdDirectFlush);
+ RELEASE_ASSERT(m_op == GetById || m_op == GetByIdFlush || m_op == GetByIdDirect || m_op == GetByIdDirectFlush || m_op == GetPrivateNameById);
m_opInfo = data;
child1().setUseKind(CellUse);
m_op = MultiGetByOffset;
@@ -1082,6 +1082,7 @@
case GetByIdWithThis:
case GetByIdDirect:
case GetByIdDirectFlush:
+ case GetPrivateNameById:
case DeleteById:
case InById:
case PutById:
@@ -1766,6 +1767,8 @@
case TryGetById:
case GetByVal:
case GetByValWithThis:
+ case GetPrivateName:
+ case GetPrivateNameById:
case Call:
case DirectCall:
case TailCallInlinedCaller:
Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (268793 => 268794)
--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2020-10-21 14:06:02 UTC (rev 268794)
@@ -242,6 +242,8 @@
macro(NukeStructureAndSetButterfly, NodeMustGenerate) \
macro(CheckArray, NodeMustGenerate) \
macro(CheckArrayOrEmpty, NodeMustGenerate) \
+ macro(GetPrivateName, NodeResultJS | NodeMustGenerate) \
+ macro(GetPrivateNameById, NodeResultJS | NodeMustGenerate) \
/* This checks if the edge is a typed array and if it is neutered. */ \
macro(CheckNeutered, NodeMustGenerate) \
macro(Arrayify, NodeMustGenerate) \
Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (268793 => 268794)
--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2020-10-21 14:06:02 UTC (rev 268794)
@@ -875,6 +875,8 @@
case TryGetById:
case GetByValWithThis:
case GetByOffset:
+ case GetPrivateName:
+ case GetPrivateNameById:
case MultiGetByOffset:
case GetDirectPname:
case Call:
Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (268793 => 268794)
--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2020-10-21 14:06:02 UTC (rev 268794)
@@ -529,6 +529,8 @@
case PutSetterByVal:
case PutPrivateName:
case PutPrivateNameById:
+ case GetPrivateName:
+ case GetPrivateNameById:
case DefineDataProperty:
case DefineAccessorProperty:
case Arrayify:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (268793 => 268794)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2020-10-21 14:06:02 UTC (rev 268794)
@@ -3468,6 +3468,109 @@
jsValueResult(resultRegs, node);
}
+void SpeculativeJIT::compileGetPrivateName(Node* node)
+{
+ if (node->hasCacheableIdentifier())
+ return compileGetPrivateNameById(node);
+
+ switch (m_graph.child(node, 0).useKind()) {
+ case CellUse: {
+ SpeculateCellOperand base(this, m_graph.child(node, 0));
+ SpeculateCellOperand property(this, m_graph.child(node, 1));
+
+ compileGetPrivateNameByVal(node, JSValueRegs::payloadOnly(base.gpr()), JSValueRegs::payloadOnly(property.gpr()));
+ break;
+ }
+ case UntypedUse: {
+ JSValueOperand base(this, m_graph.child(node, 0));
+ SpeculateCellOperand property(this, m_graph.child(node, 1));
+
+ compileGetPrivateNameByVal(node, base.jsValueRegs(), JSValueRegs::payloadOnly(property.gpr()));
+ break;
+ }
+ default:
+ DFG_CRASH(m_jit.graph(), node, "Bad use kind");
+ }
+}
+
+void SpeculativeJIT::compileGetPrivateNameByVal(Node* node, JSValueRegs base, JSValueRegs property)
+{
+ DFG_ASSERT(m_jit.graph(), node, node->op() == GetPrivateName);
+ DFG_ASSERT(m_jit.graph(), node, m_graph.child(node, 1).useKind() == SymbolUse);
+ speculateSymbol(m_graph.child(node, 1));
+
+ JSValueRegsTemporary result(this);
+ CodeOrigin codeOrigin = node->origin.semantic;
+ CallSiteIndex callSite = m_jit.recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded(codeOrigin, m_stream->size());
+ RegisterSet usedRegisters = this->usedRegisters();
+
+ JITCompiler::JumpList slowCases;
+ const bool baseIsKnownCell = m_state.forNode(m_graph.child(node, 0)).isType(SpecCell);
+ if (!baseIsKnownCell)
+ slowCases.append(m_jit.branchIfNotCell(base));
+
+ JITGetByValGenerator gen(
+ m_jit.codeBlock(), codeOrigin, callSite, AccessType::GetPrivateName, usedRegisters,
+ base, property, result.regs());
+ gen.stubInfo()->propertyIsSymbol = true;
+ gen.generateFastPath(m_jit);
+
+ slowCases.append(gen.slowPathJump());
+
+ auto makeSlowPathCall = [&](auto base) {
+ return slowPathCall(
+ slowCases, this, operationGetPrivateNameOptimize,
+ result.regs(), TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(codeOrigin)), gen.stubInfo(),
+ base, CCallHelpers::CellValue(property.payloadGPR()));
+ };
+
+ std::unique_ptr<SlowPathGenerator> slowPath = baseIsKnownCell
+ ? makeSlowPathCall(CCallHelpers::CellValue(base.payloadGPR()))
+ : makeSlowPathCall(base);
+
+ m_jit.addGetByVal(gen, slowPath.get());
+ addSlowPathGenerator(WTFMove(slowPath));
+
+ jsValueResult(result.regs(), node, DataFormatJS);
+}
+
+void SpeculativeJIT::compileGetPrivateNameById(Node* node)
+{
+ switch (m_graph.child(node, 0).useKind()) {
+ case CellUse: {
+ SpeculateCellOperand base(this, m_graph.child(node, 0));
+ JSValueRegsTemporary result(this, Reuse, base);
+
+ JSValueRegs baseRegs = JSValueRegs::payloadOnly(base.gpr());
+ JSValueRegs resultRegs = result.regs();
+
+ cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->cacheableIdentifier(), JITCompiler::Jump(), NeedToSpill, AccessType::GetPrivateName);
+
+ jsValueResult(resultRegs, node, DataFormatJS);
+ break;
+ }
+
+ case UntypedUse: {
+ JSValueOperand base(this, m_graph.child(node, 0));
+ JSValueRegsTemporary result(this, Reuse, base);
+
+ JSValueRegs baseRegs = base.jsValueRegs();
+ JSValueRegs resultRegs = result.regs();
+
+ JITCompiler::Jump notCell = m_jit.branchIfNotCell(baseRegs);
+
+ cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->cacheableIdentifier(), notCell, NeedToSpill, AccessType::GetPrivateName);
+
+ jsValueResult(resultRegs, node, DataFormatJS);
+ break;
+ }
+
+ default:
+ DFG_CRASH(m_jit.graph(), node, "Bad use kind");
+ break;
+ }
+}
+
void SpeculativeJIT::compilePutByValForCellWithString(Node* node, Edge& child1, Edge& child2, Edge& child3)
{
SpeculateCellOperand arg1(this, child1);
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (268793 => 268794)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2020-10-21 14:06:02 UTC (rev 268794)
@@ -1282,7 +1282,11 @@
void compileGetByValOnDirectArguments(Node*);
void compileGetByValOnScopedArguments(Node*);
-
+
+ void compileGetPrivateName(Node*);
+ void compileGetPrivateNameById(Node*);
+ void compileGetPrivateNameByVal(Node*, JSValueRegs base, JSValueRegs property);
+
void compileGetScope(Node*);
void compileSkipScope(Node*);
void compileGetGlobalObject(Node*);
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (268793 => 268794)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2020-10-21 14:06:02 UTC (rev 268794)
@@ -2241,6 +2241,12 @@
break;
}
+ case GetPrivateName:
+ case GetPrivateNameById: {
+ compileGetPrivateName(node);
+ break;
+ }
+
case GetByVal: {
switch (node->arrayMode().type()) {
case Array::SelectUsingPredictions:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (268793 => 268794)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2020-10-21 14:06:02 UTC (rev 268794)
@@ -2617,6 +2617,12 @@
break;
}
+ case GetPrivateName:
+ case GetPrivateNameById: {
+ compileGetPrivateName(node);
+ break;
+ }
+
case GetByVal: {
switch (node->arrayMode().type()) {
case Array::AnyTypedArray:
Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (268793 => 268794)
--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2020-10-21 14:06:02 UTC (rev 268794)
@@ -397,6 +397,8 @@
case PutByValWithThis:
case PutPrivateName:
case PutPrivateNameById:
+ case GetPrivateName:
+ case GetPrivateNameById:
case MatchStructure:
case FilterCallLinkStatus:
case FilterGetByStatus:
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (268793 => 268794)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2020-10-21 14:06:02 UTC (rev 268794)
@@ -948,6 +948,12 @@
case GetByIdDirectFlush:
compileGetById(AccessType::GetByIdDirect);
break;
+ case GetPrivateName:
+ compileGetPrivateName();
+ break;
+ case GetPrivateNameById:
+ compileGetPrivateNameById();
+ break;
case InById:
compileInById();
break;
@@ -3941,6 +3947,138 @@
setJSValue(result);
}
+ LValue getPrivateName(LValue base, LValue property)
+ {
+ Node* node = m_node;
+ PatchpointValue* patchpoint = m_out.patchpoint(Int64);
+ patchpoint->appendSomeRegister(base);
+ patchpoint->appendSomeRegister(property);
+ patchpoint->append(m_notCellMask, ValueRep::lateReg(GPRInfo::notCellMaskRegister));
+ patchpoint->append(m_numberTag, ValueRep::lateReg(GPRInfo::numberTagRegister));
+ patchpoint->clobber(RegisterSet::macroScratchRegisters());
+
+ RefPtr<PatchpointExceptionHandle> exceptionHandle = preparePatchpointForExceptions(patchpoint);
+
+ State* state = &m_ftlState;
+ bool baseIsCell = abstractValue(node->child1()).isType(SpecCell);
+ patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+ AllowMacroScratchRegisterUsage allowScratch(jit);
+
+ CallSiteIndex callSiteIndex = state->jitCode->common.codeOrigins->addUniqueCallSiteIndex(node->origin.semantic);
+
+ // This is the direct exit target for operation calls.
+ Box<CCallHelpers::JumpList> exceptions = exceptionHandle->scheduleExitCreation(params)->jumps(jit);
+
+ // This is the exit for call IC's created by the IC for getters. We don't have
+ // to do anything weird other than call this, since it will associate the exit with
+ // the callsite index.
+ exceptionHandle->scheduleExitCreationForUnwind(params, callSiteIndex);
+
+ GPRReg resultGPR = params[0].gpr();
+ GPRReg baseGPR = params[1].gpr();
+ GPRReg propertyGPR = params[2].gpr();
+
+ auto generator = Box<JITGetByValGenerator>::create(
+ jit.codeBlock(), node->origin.semantic, callSiteIndex, AccessType::GetPrivateName,
+ params.unavailableRegisters(), JSValueRegs(baseGPR), JSValueRegs(propertyGPR), JSValueRegs(resultGPR));
+
+ CCallHelpers::Jump notCell;
+ if (!baseIsCell)
+ notCell = jit.branchIfNotCell(baseGPR);
+
+ generator->generateFastPath(jit);
+ CCallHelpers::Label done = jit.label();
+
+ params.addLatePath([=] (CCallHelpers& jit) {
+ AllowMacroScratchRegisterUsage allowScratch(jit);
+
+ if (notCell.isSet())
+ notCell.link(&jit);
+ generator->slowPathJump().link(&jit);
+ CCallHelpers::Label slowPathBegin = jit.label();
+ CCallHelpers::Call slowPathCall = callOperation(
+ *state, params.unavailableRegisters(), jit, node->origin.semantic,
+ exceptions.get(), operationGetPrivateNameOptimize, resultGPR,
+ jit.codeBlock()->globalObjectFor(node->origin.semantic),
+ CCallHelpers::TrustedImmPtr(generator->stubInfo()), baseGPR, propertyGPR).call();
+ jit.jump().linkTo(done, &jit);
+
+ generator->reportSlowPathCall(slowPathBegin, slowPathCall);
+
+ jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
+ generator->finalize(linkBuffer, linkBuffer);
+ });
+ });
+ });
+
+ return patchpoint;
+ }
+
+ void compileGetPrivateName()
+ {
+ if (m_node->child1().useKind() == CellUse)
+ setJSValue(getPrivateName(lowCell(m_node->child1()), lowSymbol(m_node->child2())));
+ else {
+ LValue base = lowJSValue(m_node->child1());
+ LValue property = lowSymbol(m_node->child2());
+
+ LBasicBlock baseCellCase = m_out.newBlock();
+ LBasicBlock notCellCase = m_out.newBlock();
+ LBasicBlock continuation = m_out.newBlock();
+
+ m_out.branch(
+ isCell(base, provenType(m_node->child1())), unsure(baseCellCase), unsure(notCellCase));
+
+ LBasicBlock lastNext = m_out.appendTo(baseCellCase, notCellCase);
+
+ ValueFromBlock cellResult = m_out.anchor(getPrivateName(base, property));
+ m_out.jump(continuation);
+
+ m_out.appendTo(notCellCase, continuation);
+ JSGlobalObject* globalObject = m_graph.globalObjectFor(m_origin.semantic);
+ ValueFromBlock notCellResult = m_out.anchor(vmCall(
+ Int64, operationGetPrivateName,
+ weakPointer(globalObject), m_out.constIntPtr(0), base,
+ property));
+ m_out.jump(continuation);
+
+ m_out.appendTo(continuation, lastNext);
+ setJSValue(m_out.phi(Int64, cellResult, notCellResult));
+ }
+ }
+
+ void compileGetPrivateNameById()
+ {
+ JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
+ if (m_node->child1().useKind() == CellUse)
+ setJSValue(getById(lowCell(m_node->child1()), AccessType::GetPrivateName));
+ else {
+ LValue base = lowJSValue(m_node->child1());
+
+ LBasicBlock baseCellCase = m_out.newBlock();
+ LBasicBlock notCellCase = m_out.newBlock();
+ LBasicBlock continuation = m_out.newBlock();
+
+ m_out.branch(
+ isCell(base, provenType(m_node->child1())), unsure(baseCellCase), unsure(notCellCase));
+
+ LBasicBlock lastNext = m_out.appendTo(baseCellCase, notCellCase);
+
+ ValueFromBlock cellResult = m_out.anchor(getById(base, AccessType::GetPrivateName));
+ m_out.jump(continuation);
+
+ m_out.appendTo(notCellCase, continuation);
+ ValueFromBlock notCellResult = m_out.anchor(vmCall(
+ Int64, operationGetPrivateNameByIdGeneric,
+ weakPointer(globalObject), base,
+ m_out.constIntPtr(m_node->cacheableIdentifier().rawBits())));
+ m_out.jump(continuation);
+
+ m_out.appendTo(continuation, lastNext);
+ setJSValue(m_out.phi(Int64, cellResult, notCellResult));
+ }
+ }
+
void compilePutByIdWithThis()
{
JSGlobalObject* globalObject = m_graph.globalObjectFor(m_origin.semantic);
Modified: trunk/Source/_javascript_Core/jit/ICStats.h (268793 => 268794)
--- trunk/Source/_javascript_Core/jit/ICStats.h 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/jit/ICStats.h 2020-10-21 14:06:02 UTC (rev 268794)
@@ -72,7 +72,10 @@
macro(InByIdSelfPatch) \
macro(DelByReplaceWithJump) \
macro(DelByReplaceWithGeneric) \
- macro(OperationGetPrivateNameOptimize)
+ macro(OperationGetPrivateNameOptimize) \
+ macro(OperationGetPrivateNameById) \
+ macro(OperationGetPrivateNameByIdOptimize) \
+ macro(OperationGetPrivateNameByIdGeneric)
class ICEvent {
public:
Modified: trunk/Source/_javascript_Core/jit/JITOperations.cpp (268793 => 268794)
--- trunk/Source/_javascript_Core/jit/JITOperations.cpp 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/jit/JITOperations.cpp 2020-10-21 14:06:02 UTC (rev 268794)
@@ -2261,6 +2261,24 @@
return slot.getValue(globalObject, fieldName);
}
+ALWAYS_INLINE static JSValue getPrivateName(JSGlobalObject* globalObject, CallFrame* callFrame, JSValue baseValue, Identifier fieldName)
+{
+ ASSERT(fieldName.isPrivateName());
+ UNUSED_PARAM(callFrame);
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ baseValue.requireObjectCoercible(globalObject);
+ RETURN_IF_EXCEPTION(scope, JSValue());
+
+ JSObject* base = baseValue.toObject(globalObject);
+ PropertySlot slot(base, PropertySlot::InternalMethodType::GetOwnProperty);
+ base->getPrivateField(globalObject, fieldName, slot);
+ RETURN_IF_EXCEPTION(scope, JSValue());
+
+ return slot.getValue(globalObject, fieldName);
+}
+
JSC_DEFINE_JIT_OPERATION(operationGetPrivateNameOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBase, EncodedJSValue encodedFieldName))
{
VM& vm = globalObject->vm();
@@ -2280,7 +2298,6 @@
ASSERT(fieldName.isSymbol());
JSObject* base = jsCast<JSObject*>(baseValue.asCell());
- RETURN_IF_EXCEPTION(scope, encodedJSValue());
PropertySlot slot(base, PropertySlot::InternalMethodType::GetOwnProperty);
base->getPrivateField(globalObject, fieldName, slot);
@@ -2306,11 +2323,83 @@
JSValue baseValue = JSValue::decode(encodedBase);
JSValue fieldNameValue = JSValue::decode(encodedFieldName);
- stubInfo->tookSlowPath = true;
+ if (stubInfo)
+ stubInfo->tookSlowPath = true;
return JSValue::encode(getPrivateName(globalObject, callFrame, baseValue, fieldNameValue));
}
+JSC_DEFINE_JIT_OPERATION(operationGetPrivateNameById, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier))
+{
+ SuperSamplerScope superSamplerScope(false);
+
+ VM& vm = globalObject->vm();
+ CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
+ JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
+
+ stubInfo->tookSlowPath = true;
+
+ JSValue baseValue = JSValue::decode(base);
+ CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier);
+ Identifier fieldName = Identifier::fromUid(vm, identifier.uid());
+
+ JSValue result = getPrivateName(globalObject, callFrame, baseValue, fieldName);
+
+ LOG_IC((ICEvent::OperationGetPrivateNameById, baseValue.classInfoOrNull(vm), fieldName, true));
+
+ return JSValue::encode(result);
+}
+
+JSC_DEFINE_JIT_OPERATION(operationGetPrivateNameByIdOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier))
+{
+ SuperSamplerScope superSamplerScope(false);
+
+ VM& vm = globalObject->vm();
+ CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
+ JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
+ CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier);
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ JSValue baseValue = JSValue::decode(base);
+ auto fieldName = Identifier::fromUid(vm, identifier.uid());
+
+ if (baseValue.isObject()) {
+ JSObject* base = jsCast<JSObject*>(baseValue.asCell());
+
+ PropertySlot slot(base, PropertySlot::InternalMethodType::GetOwnProperty);
+ base->getPrivateField(globalObject, fieldName, slot);
+ RETURN_IF_EXCEPTION(scope, encodedJSValue());
+
+ LOG_IC((ICEvent::OperationGetPrivateNameOptimize, baseValue.classInfoOrNull(vm), fieldName, true));
+
+ CodeBlock* codeBlock = callFrame->codeBlock();
+ if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(vm), identifier))
+ repatchGetBy(globalObject, codeBlock, baseValue, identifier, slot, *stubInfo, GetByKind::PrivateNameById);
+ return JSValue::encode(slot.getValue(globalObject, fieldName));
+ }
+
+ return JSValue::encode(getPrivateName(globalObject, callFrame, baseValue, fieldName));
+}
+
+JSC_DEFINE_JIT_OPERATION(operationGetPrivateNameByIdGeneric, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, uintptr_t rawCacheableIdentifier))
+{
+ SuperSamplerScope superSamplerScope(false);
+
+ VM& vm = globalObject->vm();
+ CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
+ JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
+
+ JSValue baseValue = JSValue::decode(base);
+ CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier);
+ Identifier fieldName = Identifier::fromUid(vm, identifier.uid());
+
+ JSValue result = getPrivateName(globalObject, callFrame, baseValue, fieldName);
+
+ LOG_IC((ICEvent::OperationGetPrivateNameByIdGeneric, baseValue.classInfoOrNull(vm), fieldName, true));
+
+ return JSValue::encode(result);
+}
+
JSC_DEFINE_JIT_OPERATION(operationHasIndexedPropertyDefault, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo* byValInfo))
{
VM& vm = globalObject->vm();
Modified: trunk/Source/_javascript_Core/jit/JITOperations.h (268793 => 268794)
--- trunk/Source/_javascript_Core/jit/JITOperations.h 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/jit/JITOperations.h 2020-10-21 14:06:02 UTC (rev 268794)
@@ -269,6 +269,9 @@
JSC_DECLARE_JIT_OPERATION(operationGetPrivateName, EncodedJSValue, (JSGlobalObject*, StructureStubInfo*, EncodedJSValue encodedBase, EncodedJSValue encodedFieldName));
JSC_DECLARE_JIT_OPERATION(operationGetPrivateNameOptimize, EncodedJSValue, (JSGlobalObject*, StructureStubInfo*, EncodedJSValue encodedBase, EncodedJSValue encodedFieldName));
+JSC_DECLARE_JIT_OPERATION(operationGetPrivateNameById, EncodedJSValue, (JSGlobalObject*, StructureStubInfo*, EncodedJSValue, uintptr_t));
+JSC_DECLARE_JIT_OPERATION(operationGetPrivateNameByIdOptimize, EncodedJSValue, (JSGlobalObject*, StructureStubInfo*, EncodedJSValue, uintptr_t));
+JSC_DECLARE_JIT_OPERATION(operationGetPrivateNameByIdGeneric, EncodedJSValue, (JSGlobalObject*, EncodedJSValue, uintptr_t));
JSC_DECLARE_JIT_OPERATION(operationSwitchCharWithUnknownKeyType, char*, (JSGlobalObject*, EncodedJSValue key, size_t tableIndex));
JSC_DECLARE_JIT_OPERATION(operationSwitchImmWithUnknownKeyType, char*, (VM*, EncodedJSValue key, size_t tableIndex));
Modified: trunk/Source/_javascript_Core/jit/Repatch.cpp (268793 => 268794)
--- trunk/Source/_javascript_Core/jit/Repatch.cpp 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/jit/Repatch.cpp 2020-10-21 14:06:02 UTC (rev 268794)
@@ -165,6 +165,8 @@
return operationGetByValOptimize;
case GetByKind::PrivateName:
return operationGetPrivateNameOptimize;
+ case GetByKind::PrivateNameById:
+ return operationGetPrivateNameByIdOptimize;
}
RELEASE_ASSERT_NOT_REACHED();
}
@@ -184,6 +186,8 @@
return operationGetByValGeneric;
case GetByKind::PrivateName:
return operationGetPrivateName;
+ case GetByKind::PrivateNameById:
+ return operationGetPrivateNameById;
}
RELEASE_ASSERT_NOT_REACHED();
}
@@ -203,7 +207,7 @@
if (!baseValue.isCell())
return GiveUpOnCache;
JSCell* baseCell = baseValue.asCell();
- const bool isPrivate = kind == GetByKind::PrivateName;
+ const bool isPrivate = kind == GetByKind::PrivateName || kind == GetByKind::PrivateNameById;
std::unique_ptr<AccessCase> newCase;
Modified: trunk/Source/_javascript_Core/jit/Repatch.h (268793 => 268794)
--- trunk/Source/_javascript_Core/jit/Repatch.h 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/jit/Repatch.h 2020-10-21 14:06:02 UTC (rev 268794)
@@ -40,6 +40,7 @@
WithThis,
Direct,
PrivateName,
+ PrivateNameById,
};
enum class DelByKind {
Modified: trunk/Source/_javascript_Core/runtime/OptionsList.h (268793 => 268794)
--- trunk/Source/_javascript_Core/runtime/OptionsList.h 2020-10-21 12:36:18 UTC (rev 268793)
+++ trunk/Source/_javascript_Core/runtime/OptionsList.h 2020-10-21 14:06:02 UTC (rev 268794)
@@ -571,7 +571,7 @@
};
#define FOR_EACH_JSC_EXPERIMENTAL_OPTION(v) \
- v(usePrivateClassFields, LLIntAndBaselineOnly, "https://bugs.webkit.org/show_bug.cgi?id=212781", "https://bugs.webkit.org/show_bug.cgi?id=212784")
+ v(usePrivateClassFields, SupportsFTL | SupportsDFG, "https://bugs.webkit.org/show_bug.cgi?id=212781", "https://bugs.webkit.org/show_bug.cgi?id=212784")
constexpr size_t countNumberOfJSCOptions()
{