[turbofan] Refactor property access building.

This is in preparation for lowering monomorphic loads during graph building.

This essentially moves the parts that will be shared to a separate class/file
(proparty-access-builder.(cc|h)).

I should say that we will not want to do accessor inlining during graph
building because that would require us to create frame states
(which is the thing we would like to avoid doing).

Review-Url: https://codereview.chromium.org/2936673005
Cr-Commit-Position: refs/heads/master@{#45973}
This commit is contained in:
jarin 2017-06-16 02:34:04 -07:00 committed by Commit Bot
parent e47f37ebd0
commit 126451d319
6 changed files with 776 additions and 516 deletions

View File

@ -1398,6 +1398,8 @@ v8_source_set("v8_base") {
"src/compiler/pipeline-statistics.h",
"src/compiler/pipeline.cc",
"src/compiler/pipeline.h",
"src/compiler/property-access-builder.cc",
"src/compiler/property-access-builder.h",
"src/compiler/raw-machine-assembler.cc",
"src/compiler/raw-machine-assembler.h",
"src/compiler/redundancy-elimination.cc",

File diff suppressed because it is too large Load Diff

View File

@ -96,6 +96,8 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
// 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) {}
@ -104,9 +106,9 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
Node* control() const { return control_; }
private:
Node* const value_;
Node* const effect_;
Node* const control_;
Node* value_;
Node* effect_;
Node* control_;
};
// Construct the appropriate subgraph for property access.
@ -115,6 +117,34 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
Node* effect, Node* control, Handle<Name> name,
ZoneVector<Node*>* if_exceptions, PropertyAccessInfo const& access_info,
AccessMode access_mode, LanguageMode language_mode);
ValueEffectControl BuildPropertyLoad(Node* receiver, Node* context,
Node* frame_state, Node* effect,
Node* control, Handle<Name> name,
ZoneVector<Node*>* if_exceptions,
PropertyAccessInfo const& access_info,
LanguageMode language_mode);
ValueEffectControl BuildPropertyStore(
Node* receiver, Node* value, Node* context, Node* frame_state,
Node* effect, Node* control, Handle<Name> name,
ZoneVector<Node*>* if_exceptions, PropertyAccessInfo const& access_info,
AccessMode access_mode, LanguageMode language_mode);
// Helpers for accessor inlining.
Node* InlinePropertyGetterCall(Node* receiver, Node* context,
Node* frame_state, Node** effect,
Node** control,
ZoneVector<Node*>* if_exceptions,
PropertyAccessInfo const& access_info);
Node* 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* context, Node* target,
Node* frame_state, Node* value, Node** effect,
Node** control, Handle<SharedFunctionInfo> shared_info,
Handle<FunctionTemplateInfo> function_template_info);
// Construct the appropriate subgraph for element access.
ValueEffectControl BuildElementAccess(Node* receiver, Node* index,
@ -124,22 +154,10 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
AccessMode access_mode,
KeyedAccessStoreMode store_mode);
// Construct an appropriate heap object check.
Node* BuildCheckHeapObject(Node* receiver, Node** effect, Node* control);
// Construct an appropriate map check.
Node* BuildCheckMaps(Node* receiver, Node* effect, Node* control,
MapHandles const& maps);
// Construct appropriate subgraph to extend properties backing store.
Node* BuildExtendPropertiesBackingStore(Handle<Map> map, Node* properties,
Node* effect, Node* control);
// Adds stability dependencies on all prototypes of every class in
// {receiver_type} up to (and including) the {holder}.
void AssumePrototypesStable(MapHandles const& receiver_maps,
Handle<JSObject> holder);
// 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.
@ -161,12 +179,6 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
// program location.
MaybeHandle<Map> InferReceiverRootMap(Node* receiver);
ValueEffectControl InlineApiCall(
Node* receiver, Node* context, Node* target, Node* frame_state,
Node* parameter, Node* effect, Node* control,
Handle<SharedFunctionInfo> shared_info,
Handle<FunctionTemplateInfo> function_template_info);
// Script context lookup logic.
struct ScriptContextTableLookupResult;
bool LookupInScriptContextTable(Handle<Name> name,

View File

@ -0,0 +1,271 @@
// Copyright 2017 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.
#include "src/compiler/property-access-builder.h"
#include "src/compilation-dependencies.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/access-info.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/simplified-operator.h"
#include "src/lookup.h"
#include "src/field-index-inl.h"
#include "src/isolate-inl.h"
namespace v8 {
namespace internal {
namespace compiler {
Graph* PropertyAccessBuilder::graph() const { return jsgraph()->graph(); }
Isolate* PropertyAccessBuilder::isolate() const { return jsgraph()->isolate(); }
CommonOperatorBuilder* PropertyAccessBuilder::common() const {
return jsgraph()->common();
}
SimplifiedOperatorBuilder* PropertyAccessBuilder::simplified() const {
return jsgraph()->simplified();
}
namespace {
bool HasOnlyNumberMaps(MapHandles const& maps) {
for (auto map : maps) {
if (map->instance_type() != HEAP_NUMBER_TYPE) return false;
}
return true;
}
bool HasOnlyStringMaps(MapHandles const& maps) {
for (auto map : maps) {
if (!map->IsStringMap()) return false;
}
return true;
}
bool HasOnlySequentialStringMaps(MapHandles const& maps) {
for (auto map : maps) {
if (!map->IsStringMap()) return false;
if (!StringShape(map->instance_type()).IsSequential()) {
return false;
}
}
return true;
}
} // namespace
bool PropertyAccessBuilder::TryBuildStringCheck(MapHandles const& maps,
Node** receiver, Node** effect,
Node* control) {
if (HasOnlyStringMaps(maps)) {
if (HasOnlySequentialStringMaps(maps)) {
*receiver = *effect = graph()->NewNode(simplified()->CheckSeqString(),
*receiver, *effect, control);
} else {
// Monormorphic string access (ignoring the fact that there are multiple
// String maps).
*receiver = *effect = graph()->NewNode(simplified()->CheckString(),
*receiver, *effect, control);
}
return true;
}
return false;
}
bool PropertyAccessBuilder::TryBuildNumberCheck(MapHandles const& maps,
Node** receiver, Node** effect,
Node* control) {
if (HasOnlyNumberMaps(maps)) {
// Monomorphic number access (we also deal with Smis here).
*receiver = *effect = graph()->NewNode(simplified()->CheckNumber(),
*receiver, *effect, control);
return true;
}
return false;
}
Node* PropertyAccessBuilder::BuildCheckHeapObject(Node* receiver, Node** effect,
Node* control) {
switch (receiver->opcode()) {
case IrOpcode::kHeapConstant:
case IrOpcode::kJSCreate:
case IrOpcode::kJSCreateArguments:
case IrOpcode::kJSCreateArray:
case IrOpcode::kJSCreateClosure:
case IrOpcode::kJSCreateIterResultObject:
case IrOpcode::kJSCreateLiteralArray:
case IrOpcode::kJSCreateLiteralObject:
case IrOpcode::kJSCreateLiteralRegExp:
case IrOpcode::kJSConvertReceiver:
case IrOpcode::kJSToName:
case IrOpcode::kJSToString:
case IrOpcode::kJSToObject:
case IrOpcode::kJSTypeOf: {
return receiver;
}
default: {
return *effect = graph()->NewNode(simplified()->CheckHeapObject(),
receiver, *effect, control);
}
}
UNREACHABLE();
return nullptr;
}
void PropertyAccessBuilder::BuildCheckMaps(
Node* receiver, Node** effect, Node* control,
std::vector<Handle<Map>> const& receiver_maps) {
HeapObjectMatcher m(receiver);
if (m.HasValue()) {
Handle<Map> receiver_map(m.Value()->map(), isolate());
if (receiver_map->is_stable()) {
for (Handle<Map> map : receiver_maps) {
if (map.is_identical_to(receiver_map)) {
dependencies()->AssumeMapStable(receiver_map);
return;
}
}
}
}
ZoneHandleSet<Map> maps;
CheckMapsFlags flags = CheckMapsFlag::kNone;
for (Handle<Map> map : receiver_maps) {
maps.insert(map, graph()->zone());
if (map->is_migration_target()) {
flags |= CheckMapsFlag::kTryMigrateInstance;
}
}
*effect = graph()->NewNode(simplified()->CheckMaps(flags, maps), receiver,
*effect, control);
}
void PropertyAccessBuilder::AssumePrototypesStable(
Handle<Context> native_context,
std::vector<Handle<Map>> const& receiver_maps, Handle<JSObject> holder) {
// Determine actual holder and perform prototype chain checks.
for (auto map : receiver_maps) {
// Perform the implicit ToObject for primitives here.
// Implemented according to ES6 section 7.3.2 GetV (V, P).
Handle<JSFunction> constructor;
if (Map::GetConstructorFunction(map, native_context)
.ToHandle(&constructor)) {
map = handle(constructor->initial_map(), holder->GetIsolate());
}
dependencies()->AssumePrototypeMapsStable(map, holder);
}
}
Node* PropertyAccessBuilder::ResolveHolder(
PropertyAccessInfo const& access_info, Node* receiver) {
Handle<JSObject> holder;
if (access_info.holder().ToHandle(&holder)) {
return jsgraph()->Constant(holder);
}
return receiver;
}
Node* PropertyAccessBuilder::TryBuildLoadConstantDataField(
Handle<Name> name, PropertyAccessInfo const& access_info, Node* receiver) {
// Optimize immutable property loads.
HeapObjectMatcher m(receiver);
if (m.HasValue() && m.Value()->IsJSObject()) {
// TODO(ishell): Use something simpler like
//
// Handle<Object> value =
// JSObject::FastPropertyAt(Handle<JSObject>::cast(m.Value()),
// Representation::Tagged(), field_index);
//
// here, once we have the immutable bit in the access_info.
// TODO(turbofan): Given that we already have the field_index here, we
// might be smarter in the future and not rely on the LookupIterator,
// but for now let's just do what Crankshaft does.
LookupIterator it(m.Value(), name, LookupIterator::OWN_SKIP_INTERCEPTOR);
if (it.state() == LookupIterator::DATA) {
bool is_reaonly_non_configurable =
it.IsReadOnly() && !it.IsConfigurable();
if (is_reaonly_non_configurable ||
(FLAG_track_constant_fields && access_info.IsDataConstantField())) {
Node* value = jsgraph()->Constant(JSReceiver::GetDataProperty(&it));
if (!is_reaonly_non_configurable) {
// It's necessary to add dependency on the map that introduced
// the field.
DCHECK(access_info.IsDataConstantField());
DCHECK(!it.is_dictionary_holder());
Handle<Map> field_owner_map = it.GetFieldOwnerMap();
dependencies()->AssumeFieldOwner(field_owner_map);
}
return value;
}
}
}
return nullptr;
}
Node* PropertyAccessBuilder::BuildLoadDataField(
Handle<Name> name, PropertyAccessInfo const& access_info, Node* receiver,
Node** effect, Node** control) {
DCHECK(access_info.IsDataField() || access_info.IsDataConstantField());
receiver = ResolveHolder(access_info, receiver);
if (Node* value =
TryBuildLoadConstantDataField(name, access_info, receiver)) {
return value;
}
FieldIndex const field_index = access_info.field_index();
Type* const field_type = access_info.field_type();
MachineRepresentation const field_representation =
access_info.field_representation();
Node* storage = receiver;
if (!field_index.is_inobject()) {
storage = *effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSObjectProperties()),
storage, *effect, *control);
}
FieldAccess field_access = {
kTaggedBase,
field_index.offset(),
name,
MaybeHandle<Map>(),
field_type,
MachineType::TypeForRepresentation(field_representation),
kFullWriteBarrier};
if (field_representation == MachineRepresentation::kFloat64) {
if (!field_index.is_inobject() || field_index.is_hidden_field() ||
!FLAG_unbox_double_fields) {
FieldAccess const storage_access = {kTaggedBase,
field_index.offset(),
name,
MaybeHandle<Map>(),
Type::OtherInternal(),
MachineType::TaggedPointer(),
kPointerWriteBarrier};
storage = *effect = graph()->NewNode(
simplified()->LoadField(storage_access), storage, *effect, *control);
field_access.offset = HeapNumber::kValueOffset;
field_access.name = MaybeHandle<Name>();
}
} else if (field_representation == MachineRepresentation::kTaggedPointer) {
// Remember the map of the field value, if its map is stable. This is
// used by the LoadElimination to eliminate map checks on the result.
Handle<Map> field_map;
if (access_info.field_map().ToHandle(&field_map)) {
if (field_map->is_stable()) {
dependencies()->AssumeMapStable(field_map);
field_access.map = field_map;
}
}
}
Node* value = *effect = graph()->NewNode(
simplified()->LoadField(field_access), storage, *effect, *control);
return value;
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,80 @@
// Copyright 2017 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_PROPERTY_ACCESS_BUILDER_H_
#define V8_COMPILER_PROPERTY_ACCESS_BUILDER_H_
#include <vector>
#include "src/handles.h"
#include "src/objects/map.h"
#include "src/zone/zone-containers.h"
namespace v8 {
namespace internal {
class CompilationDependencies;
namespace compiler {
class CommonOperatorBuilder;
class Graph;
class JSGraph;
class Node;
class PropertyAccessInfo;
class SimplifiedOperatorBuilder;
class PropertyAccessBuilder {
public:
PropertyAccessBuilder(JSGraph* jsgraph, CompilationDependencies* dependencies)
: jsgraph_(jsgraph), dependencies_(dependencies) {}
// Builds the appropriate string check if the maps are only string
// maps.
bool TryBuildStringCheck(MapHandles const& maps, Node** receiver,
Node** effect, Node* control);
// Builds a number check if all maps are number maps.
bool TryBuildNumberCheck(MapHandles const& maps, Node** receiver,
Node** effect, Node* control);
Node* BuildCheckHeapObject(Node* receiver, Node** effect, Node* control);
void BuildCheckMaps(Node* receiver, Node** effect, Node* control,
std::vector<Handle<Map>> const& receiver_maps);
// Adds stability dependencies on all prototypes of every class in
// {receiver_type} up to (and including) the {holder}.
void AssumePrototypesStable(Handle<Context> native_context,
std::vector<Handle<Map>> const& receiver_maps,
Handle<JSObject> holder);
// Builds the actual load for data-field and data-constant-field
// properties (without heap-object or map checks).
Node* BuildLoadDataField(Handle<Name> name,
PropertyAccessInfo const& access_info,
Node* receiver, Node** effect, Node** control);
private:
JSGraph* jsgraph() const { return jsgraph_; }
CompilationDependencies* dependencies() const { return dependencies_; }
Graph* graph() const;
Isolate* isolate() const;
CommonOperatorBuilder* common() const;
SimplifiedOperatorBuilder* simplified() const;
Node* TryBuildLoadConstantDataField(Handle<Name> name,
PropertyAccessInfo const& access_info,
Node* receiver);
// Returns a node with the holder for the property access described by
// {access_info}.
Node* ResolveHolder(PropertyAccessInfo const& access_info, Node* receiver);
JSGraph* jsgraph_;
CompilationDependencies* dependencies_;
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_PROPERTY_ACCESS_BUILDER_H_

View File

@ -843,6 +843,8 @@
'compiler/pipeline.h',
'compiler/pipeline-statistics.cc',
'compiler/pipeline-statistics.h',
'compiler/property-access-builder.cc',
'compiler/property-access-builder.h',
'compiler/raw-machine-assembler.cc',
'compiler/raw-machine-assembler.h',
'compiler/redundancy-elimination.cc',