713ebae3b4
Introduces several new runtime mechanics for defining private fields, including: - Bytecode StaKeyedPropertyAsDefine - Builtins StoreOwnIC{Trampoline|Baseline|_NoFeedback} - Builtins KeyedDefineOwnIC{Trampoline|Baseline|_Megamorphic} - TurboFan IR opcode JSDefineProperty These new operations can reduce a runtime call per class field into a more traditional Store equivalent. In the microbenchmarks, this results in a substantial win over the status quo (~8x benchmark score for single fields with the changes, ~20x with multiple fields). The TurboFan JSDefineProperty op is lowered in JSNativeContextSpecialization, however this required some hacks. Because private fields are defined as DONT_ENUM when added to the object, we can't find a suitable transition using the typical data property (NONE) flags. I've added a mechanism to specify the required PropertyAttributes for the transition we want to look up. Details: New bytecodes: - StaKeyedPropertyAsDefine, which is essentially StaKeyedProperty but with a different IC builtin (KeyedDefineOwnIC). This is a bytecode rather than a flag for the existing StaKeyedProperty in order to avoid impacting typical keyed stores in any way due to additional branching and testing. New builtins: - StoreOwnIC{TTrampoline|Baseline|_NoFeedback} is now used for StaNamedOwnProperty. Unlike the regular StoreIC, this variant will no longer look up the property name in the prototype. In adddition, this CL changes an assumption that StoreNamedOwnProperty can't result in a map transition, as we can't rely on the property already being present in the Map due to an object literal boilerplate. In the context of class features, this replaces the runtime function %CreateDataProperty(). - KeyedDefineOwnIC{Trampoline|Baseline|_Megamorphic} is used by the new StaKeyedPropertyAsDefine bytecode. This is similar to an ordinary KeyedStoreIC, but will not check the prototype for setters, and for private fields, will take the slow path if the field already exists. In the context of class features, this replaces the runtime function %AddPrivateField(). TurboFan IR: - JSDefineProperty is introduced to represent a situation where we need to use "Define" semantics, in particular, it codifies that we do not consult the prototype chain, and the semantics relating to private fields are implied as well. R=leszeks@chromium.org, syg@chromium.org, rmcilroy@chromium.org Bug: v8:9888 Change-Id: Idcc947585c0e612f9e8533aa4e2e0f8f0df8875d Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2795831 Reviewed-by: Igor Sheludko <ishell@chromium.org> Reviewed-by: Michael Stanton <mvstanton@chromium.org> Reviewed-by: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Reviewed-by: Shu-yu Guo <syg@chromium.org> Commit-Queue: Joyee Cheung <joyee@igalia.com> Cr-Commit-Position: refs/heads/main@{#77377}
104 lines
2.9 KiB
JavaScript
104 lines
2.9 KiB
JavaScript
// Copyright 2021 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
//
|
|
// Flags: --allow-natives-syntax --opt
|
|
|
|
// Single structure, field is inline in the object
|
|
{
|
|
class C {
|
|
#x;
|
|
constructor(x) { this.#x = x; }
|
|
x() { return this.#x; }
|
|
}
|
|
%PrepareFunctionForOptimization(C);
|
|
assertSame(new C(1).x(), 1);
|
|
assertSame(new C(2).x(), 2);
|
|
assertSame(new C(3).x(), 3);
|
|
assertSame(new C(4).x(), 4);
|
|
assertSame(new C(5).x(), 5);
|
|
%OptimizeFunctionOnNextCall(C);
|
|
assertSame(new C(10).x(), 10);
|
|
}
|
|
|
|
// Single structure, field is out of line
|
|
{
|
|
class C {
|
|
a = (() => this.z = this.q = this.alphabet =
|
|
this[Symbol.toStringTag] = this["ninteen eighty four"] = 42)()
|
|
b = (() => this[47] = this.extra = this.fortran = this.derble =
|
|
this["high quality zebras"] = 73)()
|
|
c = (() => this.moreprops = this.jimbo = this["flabberghast"] = 198)()
|
|
#x;
|
|
constructor(x) { this.#x = x; }
|
|
x() { return this.#x; }
|
|
}
|
|
|
|
%PrepareFunctionForOptimization(C);
|
|
assertSame(new C(1).x(), 1);
|
|
assertSame(new C(6000).x(), 6000);
|
|
assertSame(new C(37).x(), 37);
|
|
assertSame(new C(-1).x(), -1);
|
|
assertSame(new C(4900).x(), 4900);
|
|
%OptimizeFunctionOnNextCall(C);
|
|
assertSame(new C(9999).x(), 9999);
|
|
}
|
|
|
|
// Multiple structures, field is inline (probably)
|
|
{
|
|
let i = 0;
|
|
class C {
|
|
a = (() => {
|
|
if (i > 1000)
|
|
this.tenEtwo = i;
|
|
if ((i & 0x7F) > 64)
|
|
this.boxing = i;
|
|
if (i > 9000)
|
|
this["It's over nine thousand!"] = i;
|
|
})()
|
|
#x;
|
|
constructor(x) { this.#x = x; }
|
|
x() { return this.#x; }
|
|
}
|
|
%PrepareFunctionForOptimization(C);
|
|
assertSame(new C(1).x(), 1);
|
|
assertSame(new C(256).x(), 256);
|
|
assertSame(new C(36).x(), 36);
|
|
assertSame(new C(16384).x(), 16384);
|
|
assertSame(new C(17).x(), 17);
|
|
%OptimizeFunctionOnNextCall(C);
|
|
assertSame(new C(74).x(), 74);
|
|
}
|
|
|
|
// Multiple structures, field should be out of object
|
|
{
|
|
let i = 0;
|
|
class C {
|
|
a = (() => this.z = this.q = this.alphabet =
|
|
this[Symbol.toStringTag] = this["ninteen eighty four"] = 42)()
|
|
b = (() => this[47] = this.extra = this.fortran = this.derble =
|
|
this["high quality zebras"] = 73)()
|
|
c = (() => this.moreprops = this.jimbo = this["flabberghast"] = 198)()
|
|
d = (() => {
|
|
if (i > 1000)
|
|
this.tenEtwo = i;
|
|
if ((i & 0x7F) > 64)
|
|
this.boxing = i;
|
|
if (i > 9000)
|
|
this["It's over nine thousand again!"] = i;
|
|
})()
|
|
#x;
|
|
constructor(x) { this.#x = x; }
|
|
x() { return this.#x; }
|
|
}
|
|
|
|
%PrepareFunctionForOptimization(C);
|
|
assertSame(new C(1).x(), 1);
|
|
assertSame(new C(38000).x(), 38000);
|
|
assertSame(new C(9080).x(), 9080);
|
|
assertSame(new C(92).x(), 92);
|
|
assertSame(new C(4000).x(), 4000);
|
|
%OptimizeFunctionOnNextCall(C);
|
|
assertSame(new C(10000).x(), 10000);
|
|
}
|