Title: [268794] trunk
Revision
268794
Author
ca...@igalia.com
Date
2020-10-21 07:06:02 -0700 (Wed, 21 Oct 2020)

Log Message

[JSC] support op_get_private_name in DFG and FTL
https://bugs.webkit.org/show_bug.cgi?id=214861

Reviewed by Filip Pizlo.

JSTests:

* 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.

Source/_javascript_Core:

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:

Modified Paths

Added Paths

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()
 {
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to