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}
279 lines
12 KiB
C++
279 lines
12 KiB
C++
// Copyright 2015 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.
|
|
|
|
#ifndef V8_COMPILER_JS_NATIVE_CONTEXT_SPECIALIZATION_H_
|
|
#define V8_COMPILER_JS_NATIVE_CONTEXT_SPECIALIZATION_H_
|
|
|
|
#include "src/base/flags.h"
|
|
#include "src/base/optional.h"
|
|
#include "src/compiler/graph-reducer.h"
|
|
#include "src/compiler/js-heap-broker.h"
|
|
#include "src/deoptimizer/deoptimize-reason.h"
|
|
#include "src/objects/map.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
// Forward declarations.
|
|
class Factory;
|
|
class JSGlobalObject;
|
|
class JSGlobalProxy;
|
|
class StringConstantBase;
|
|
|
|
namespace compiler {
|
|
|
|
// Forward declarations.
|
|
enum class AccessMode;
|
|
class CommonOperatorBuilder;
|
|
class CompilationDependencies;
|
|
class ElementAccessInfo;
|
|
class JSGraph;
|
|
class JSHeapBroker;
|
|
class JSOperatorBuilder;
|
|
class MachineOperatorBuilder;
|
|
class PropertyAccessInfo;
|
|
class SimplifiedOperatorBuilder;
|
|
class TypeCache;
|
|
|
|
// Specializes a given JSGraph to a given native context, potentially constant
|
|
// folding some {LoadGlobal} nodes or strength reducing some {StoreGlobal}
|
|
// nodes. And also specializes {LoadNamed} and {StoreNamed} nodes according
|
|
// to type feedback (if available).
|
|
class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
|
|
: public AdvancedReducer {
|
|
public:
|
|
// Flags that control the mode of operation.
|
|
enum Flag {
|
|
kNoFlags = 0u,
|
|
kBailoutOnUninitialized = 1u << 0,
|
|
};
|
|
using Flags = base::Flags<Flag>;
|
|
|
|
JSNativeContextSpecialization(Editor* editor, JSGraph* jsgraph,
|
|
JSHeapBroker* broker, Flags flags,
|
|
CompilationDependencies* dependencies,
|
|
Zone* zone, Zone* shared_zone);
|
|
JSNativeContextSpecialization(const JSNativeContextSpecialization&) = delete;
|
|
JSNativeContextSpecialization& operator=(
|
|
const JSNativeContextSpecialization&) = delete;
|
|
|
|
const char* reducer_name() const override {
|
|
return "JSNativeContextSpecialization";
|
|
}
|
|
|
|
Reduction Reduce(Node* node) final;
|
|
|
|
// Utility for folding string constant concatenation.
|
|
// Supports JSAdd nodes and nodes typed as string or number.
|
|
// Public for the sake of unit testing.
|
|
static base::Optional<size_t> GetMaxStringLength(JSHeapBroker* broker,
|
|
Node* node);
|
|
|
|
private:
|
|
Reduction ReduceJSAdd(Node* node);
|
|
Reduction ReduceJSAsyncFunctionEnter(Node* node);
|
|
Reduction ReduceJSAsyncFunctionReject(Node* node);
|
|
Reduction ReduceJSAsyncFunctionResolve(Node* node);
|
|
Reduction ReduceJSGetSuperConstructor(Node* node);
|
|
Reduction ReduceJSInstanceOf(Node* node);
|
|
Reduction ReduceJSHasInPrototypeChain(Node* node);
|
|
Reduction ReduceJSOrdinaryHasInstance(Node* node);
|
|
Reduction ReduceJSPromiseResolve(Node* node);
|
|
Reduction ReduceJSResolvePromise(Node* node);
|
|
Reduction ReduceJSLoadGlobal(Node* node);
|
|
Reduction ReduceJSStoreGlobal(Node* node);
|
|
Reduction ReduceJSLoadNamed(Node* node);
|
|
Reduction ReduceJSLoadNamedFromSuper(Node* node);
|
|
Reduction ReduceJSGetIterator(Node* node);
|
|
Reduction ReduceJSStoreNamed(Node* node);
|
|
Reduction ReduceJSHasProperty(Node* node);
|
|
Reduction ReduceJSLoadProperty(Node* node);
|
|
Reduction ReduceJSStoreProperty(Node* node);
|
|
Reduction ReduceJSDefineProperty(Node* node);
|
|
Reduction ReduceJSStoreNamedOwn(Node* node);
|
|
Reduction ReduceJSStoreDataPropertyInLiteral(Node* node);
|
|
Reduction ReduceJSStoreInArrayLiteral(Node* node);
|
|
Reduction ReduceJSToObject(Node* node);
|
|
|
|
Reduction ReduceElementAccess(Node* node, Node* index, Node* value,
|
|
ElementAccessFeedback const& feedback);
|
|
// In the case of non-keyed (named) accesses, pass the name as {static_name}
|
|
// and use {nullptr} for {key} (load/store modes are irrelevant).
|
|
Reduction ReducePropertyAccess(Node* node, Node* key,
|
|
base::Optional<NameRef> static_name,
|
|
Node* value, FeedbackSource const& source,
|
|
AccessMode access_mode);
|
|
Reduction ReduceNamedAccess(Node* node, Node* value,
|
|
NamedAccessFeedback const& feedback,
|
|
AccessMode access_mode, Node* key = nullptr);
|
|
Reduction ReduceMinimorphicPropertyAccess(
|
|
Node* node, Node* value,
|
|
MinimorphicLoadPropertyAccessFeedback const& feedback,
|
|
FeedbackSource const& source);
|
|
Reduction ReduceGlobalAccess(Node* node, Node* lookup_start_object,
|
|
Node* receiver, Node* value, NameRef const& name,
|
|
AccessMode access_mode, Node* key,
|
|
PropertyCellRef const& property_cell,
|
|
Node* effect = nullptr);
|
|
Reduction ReduceElementLoadFromHeapConstant(Node* node, Node* key,
|
|
AccessMode access_mode,
|
|
KeyedAccessLoadMode load_mode);
|
|
Reduction ReduceElementAccessOnString(Node* node, Node* index, Node* value,
|
|
KeyedAccessMode const& keyed_mode);
|
|
|
|
Reduction ReduceSoftDeoptimize(Node* node, DeoptimizeReason reason);
|
|
Reduction ReduceJSToString(Node* node);
|
|
|
|
Reduction ReduceJSLoadPropertyWithEnumeratedKey(Node* node);
|
|
|
|
base::Optional<const StringConstantBase*> CreateDelayedStringConstant(
|
|
Node* node);
|
|
|
|
// A triple of nodes that represents a continuation.
|
|
class ValueEffectControl final {
|
|
public:
|
|
ValueEffectControl()
|
|
: value_(nullptr), effect_(nullptr), control_(nullptr) {}
|
|
ValueEffectControl(Node* value, Node* effect, Node* control)
|
|
: value_(value), effect_(effect), control_(control) {}
|
|
|
|
Node* value() const { return value_; }
|
|
Node* effect() const { return effect_; }
|
|
Node* control() const { return control_; }
|
|
|
|
private:
|
|
Node* value_;
|
|
Node* effect_;
|
|
Node* control_;
|
|
};
|
|
|
|
// Construct the appropriate subgraph for property access. Return {} if the
|
|
// property access couldn't be built.
|
|
base::Optional<ValueEffectControl> BuildPropertyAccess(
|
|
Node* lookup_start_object, Node* receiver, Node* value, Node* context,
|
|
Node* frame_state, Node* effect, Node* control, NameRef const& name,
|
|
ZoneVector<Node*>* if_exceptions, PropertyAccessInfo const& access_info,
|
|
AccessMode access_mode);
|
|
base::Optional<ValueEffectControl> BuildPropertyLoad(
|
|
Node* lookup_start_object, Node* receiver, Node* context,
|
|
Node* frame_state, Node* effect, Node* control, NameRef const& name,
|
|
ZoneVector<Node*>* if_exceptions, PropertyAccessInfo const& access_info);
|
|
|
|
ValueEffectControl BuildPropertyStore(Node* receiver, Node* value,
|
|
Node* context, Node* frame_state,
|
|
Node* effect, Node* control,
|
|
NameRef const& name,
|
|
ZoneVector<Node*>* if_exceptions,
|
|
PropertyAccessInfo const& access_info,
|
|
AccessMode access_mode);
|
|
|
|
ValueEffectControl BuildPropertyTest(Node* effect, Node* control,
|
|
PropertyAccessInfo const& access_info);
|
|
|
|
// Helpers for accessor inlining.
|
|
Node* InlinePropertyGetterCall(Node* receiver,
|
|
ConvertReceiverMode receiver_mode,
|
|
Node* context, Node* frame_state,
|
|
Node** effect, Node** control,
|
|
ZoneVector<Node*>* if_exceptions,
|
|
PropertyAccessInfo const& access_info);
|
|
void InlinePropertySetterCall(Node* receiver, Node* value, Node* context,
|
|
Node* frame_state, Node** effect,
|
|
Node** control,
|
|
ZoneVector<Node*>* if_exceptions,
|
|
PropertyAccessInfo const& access_info);
|
|
Node* InlineApiCall(Node* receiver, Node* holder, Node* frame_state,
|
|
Node* value, Node** effect, Node** control,
|
|
FunctionTemplateInfoRef const& function_template_info);
|
|
|
|
// Construct the appropriate subgraph for element access.
|
|
ValueEffectControl BuildElementAccess(Node* receiver, Node* index,
|
|
Node* value, Node* effect,
|
|
Node* control,
|
|
ElementAccessInfo const& access_info,
|
|
KeyedAccessMode const& keyed_mode);
|
|
|
|
// Construct appropriate subgraph to load from a String.
|
|
Node* BuildIndexedStringLoad(Node* receiver, Node* index, Node* length,
|
|
Node** effect, Node** control,
|
|
KeyedAccessLoadMode load_mode);
|
|
|
|
// Construct appropriate subgraph to extend properties backing store.
|
|
Node* BuildExtendPropertiesBackingStore(const MapRef& map, Node* properties,
|
|
Node* effect, Node* control);
|
|
|
|
// Construct appropriate subgraph to check that the {value} matches
|
|
// the previously recorded {name} feedback.
|
|
Node* BuildCheckEqualsName(NameRef const& name, Node* value, Node* effect,
|
|
Node* control);
|
|
|
|
// Checks if we can turn the hole into undefined when loading an element
|
|
// from an object with one of the {receiver_maps}; sets up appropriate
|
|
// code dependencies and might use the array protector cell.
|
|
bool CanTreatHoleAsUndefined(ZoneVector<MapRef> const& receiver_maps);
|
|
|
|
void RemoveImpossibleMaps(Node* object, ZoneVector<MapRef>* maps) const;
|
|
|
|
ElementAccessFeedback const& TryRefineElementAccessFeedback(
|
|
ElementAccessFeedback const& feedback, Node* receiver,
|
|
Effect effect) const;
|
|
|
|
// Try to infer maps for the given {object} at the current {effect}.
|
|
bool InferMaps(Node* object, Effect effect, ZoneVector<MapRef>* maps) const;
|
|
|
|
// Try to infer a root map for the {object} independent of the current program
|
|
// location.
|
|
base::Optional<MapRef> InferRootMap(Node* object) const;
|
|
|
|
// Checks if we know at compile time that the {receiver} either definitely
|
|
// has the {prototype} in it's prototype chain, or the {receiver} definitely
|
|
// doesn't have the {prototype} in it's prototype chain.
|
|
enum InferHasInPrototypeChainResult {
|
|
kIsInPrototypeChain,
|
|
kIsNotInPrototypeChain,
|
|
kMayBeInPrototypeChain
|
|
};
|
|
InferHasInPrototypeChainResult InferHasInPrototypeChain(
|
|
Node* receiver, Effect effect, HeapObjectRef const& prototype);
|
|
|
|
Node* BuildLoadPrototypeFromObject(Node* object, Node* effect, Node* control);
|
|
|
|
Graph* graph() const;
|
|
JSGraph* jsgraph() const { return jsgraph_; }
|
|
|
|
JSHeapBroker* broker() const { return broker_; }
|
|
Isolate* isolate() const;
|
|
Factory* factory() const;
|
|
CommonOperatorBuilder* common() const;
|
|
JSOperatorBuilder* javascript() const;
|
|
SimplifiedOperatorBuilder* simplified() const;
|
|
Flags flags() const { return flags_; }
|
|
Handle<JSGlobalObject> global_object() const { return global_object_; }
|
|
Handle<JSGlobalProxy> global_proxy() const { return global_proxy_; }
|
|
NativeContextRef native_context() const {
|
|
return broker()->target_native_context();
|
|
}
|
|
CompilationDependencies* dependencies() const { return dependencies_; }
|
|
Zone* zone() const { return zone_; }
|
|
Zone* shared_zone() const { return shared_zone_; }
|
|
|
|
JSGraph* const jsgraph_;
|
|
JSHeapBroker* const broker_;
|
|
Flags const flags_;
|
|
Handle<JSGlobalObject> global_object_;
|
|
Handle<JSGlobalProxy> global_proxy_;
|
|
CompilationDependencies* const dependencies_;
|
|
Zone* const zone_;
|
|
Zone* const shared_zone_;
|
|
TypeCache const* type_cache_;
|
|
};
|
|
|
|
DEFINE_OPERATORS_FOR_FLAGS(JSNativeContextSpecialization::Flags)
|
|
|
|
} // namespace compiler
|
|
} // namespace internal
|
|
} // namespace v8
|
|
|
|
#endif // V8_COMPILER_JS_NATIVE_CONTEXT_SPECIALIZATION_H_
|