[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",

View File

@ -13,6 +13,7 @@
#include "src/compiler/js-operator.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/property-access-builder.h"
#include "src/compiler/type-cache.h"
#include "src/feedback-vector.h"
#include "src/field-index-inl.h"
@ -38,13 +39,6 @@ bool HasOnlyJSArrayMaps(MapHandles const& maps) {
return true;
}
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;
@ -52,16 +46,6 @@ bool HasOnlyStringMaps(MapHandles const& maps) {
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
struct JSNativeContextSpecialization::ScriptContextTableLookupResult {
@ -193,6 +177,8 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
return NoChange();
}
PropertyAccessBuilder access_builder(jsgraph(), dependencies());
if (access_info.IsNotFound()) {
// If there's no @@hasInstance handler, the OrdinaryHasInstance operation
// takes over, but that requires the {receiver} to be callable.
@ -200,11 +186,12 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
// Determine actual holder and perform prototype chain checks.
Handle<JSObject> holder;
if (access_info.holder().ToHandle(&holder)) {
AssumePrototypesStable(access_info.receiver_maps(), holder);
access_builder.AssumePrototypesStable(
native_context(), access_info.receiver_maps(), holder);
}
// Monomorphic property access.
effect = BuildCheckMaps(constructor, effect, control,
access_builder.BuildCheckMaps(constructor, &effect, control,
access_info.receiver_maps());
// Lower to OrdinaryHasInstance(C, O).
@ -220,7 +207,8 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
// Determine actual holder and perform prototype chain checks.
Handle<JSObject> holder;
if (access_info.holder().ToHandle(&holder)) {
AssumePrototypesStable(access_info.receiver_maps(), holder);
access_builder.AssumePrototypesStable(
native_context(), access_info.receiver_maps(), holder);
} else {
holder = receiver;
}
@ -241,7 +229,7 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
DCHECK(constant->IsCallable());
// Monomorphic property access.
effect = BuildCheckMaps(constructor, effect, control,
access_builder.BuildCheckMaps(constructor, &effect, control,
access_info.receiver_maps());
// Call the @@hasInstance handler.
@ -641,28 +629,20 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
if_exceptions = &if_exception_nodes;
}
PropertyAccessBuilder access_builder(jsgraph(), dependencies());
// Check for the monomorphic cases.
if (access_infos.size() == 1) {
PropertyAccessInfo access_info = access_infos.front();
if (HasOnlyStringMaps(access_info.receiver_maps())) {
if (HasOnlySequentialStringMaps(access_info.receiver_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);
}
} else if (HasOnlyNumberMaps(access_info.receiver_maps())) {
// Monomorphic number access (we also deal with Smis here).
receiver = effect = graph()->NewNode(simplified()->CheckNumber(),
receiver, effect, control);
} else {
// Monomorphic property access.
receiver = BuildCheckHeapObject(receiver, &effect, control);
effect = BuildCheckMaps(receiver, effect, control,
// Try to build string check or number check if possible.
// Otherwise build a map check.
if (!access_builder.TryBuildStringCheck(access_info.receiver_maps(),
&receiver, &effect, control) &&
!access_builder.TryBuildNumberCheck(access_info.receiver_maps(),
&receiver, &effect, control)) {
receiver =
access_builder.BuildCheckHeapObject(receiver, &effect, control);
access_builder.BuildCheckMaps(receiver, &effect, control,
access_info.receiver_maps());
}
@ -699,7 +679,8 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch);
receiverissmi_effect = effect;
} else {
receiver = BuildCheckHeapObject(receiver, &effect, control);
receiver =
access_builder.BuildCheckHeapObject(receiver, &effect, control);
}
// Load the {receiver} map. The resulting effect is the dominating effect
@ -726,7 +707,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
if (j == access_infos.size() - 1) {
// Last map check on the fallthrough control path, do a
// conditional eager deoptimization exit here.
this_effect = BuildCheckMaps(receiver, this_effect, this_control,
access_builder.BuildCheckMaps(receiver, &this_effect, this_control,
receiver_maps);
this_effects.push_back(this_effect);
this_controls.push_back(fallthrough_control);
@ -1030,7 +1011,8 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
}
// Ensure that {receiver} is a heap object.
receiver = BuildCheckHeapObject(receiver, &effect, control);
PropertyAccessBuilder access_builder(jsgraph(), dependencies());
receiver = access_builder.BuildCheckHeapObject(receiver, &effect, control);
// Check for the monomorphic case.
if (access_infos.size() == 1) {
@ -1059,7 +1041,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
control);
// Perform map check on the {receiver}.
effect = BuildCheckMaps(receiver, effect, control,
access_builder.BuildCheckMaps(receiver, &effect, control,
access_info.receiver_maps());
// Access the actual element.
@ -1111,7 +1093,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
if (j == access_infos.size() - 1) {
// Last map check on the fallthrough control path, do a
// conditional eager deoptimization exit here.
this_effect = BuildCheckMaps(receiver, this_effect, this_control,
access_builder.BuildCheckMaps(receiver, &this_effect, this_control,
receiver_maps);
fallthrough_control = nullptr;
} else {
@ -1347,169 +1329,236 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreProperty(Node* node) {
p.language_mode(), store_mode);
}
Node* JSNativeContextSpecialization::InlinePropertyGetterCall(
Node* receiver, Node* context, Node* frame_state, Node** effect,
Node** control, ZoneVector<Node*>* if_exceptions,
PropertyAccessInfo const& access_info) {
Node* target = jsgraph()->Constant(access_info.constant());
FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
Handle<SharedFunctionInfo> shared_info =
frame_info.shared_info().ToHandleChecked();
// We need a FrameState for the getter stub to restore the correct
// context before returning to fullcodegen.
FrameStateFunctionInfo const* frame_info0 =
common()->CreateFrameStateFunctionInfo(FrameStateType::kGetterStub, 1, 0,
shared_info);
Node* frame_state0 = graph()->NewNode(
common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(),
frame_info0),
graph()->NewNode(common()->StateValues(1, SparseInputMask::Dense()),
receiver),
jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(), context,
target, frame_state);
// Introduce the call to the getter function.
Node* value;
if (access_info.constant()->IsJSFunction()) {
value = *effect = *control = graph()->NewNode(
jsgraph()->javascript()->Call(2, CallFrequency(), VectorSlotPair(),
ConvertReceiverMode::kNotNullOrUndefined),
target, receiver, context, frame_state0, *effect, *control);
} else {
DCHECK(access_info.constant()->IsFunctionTemplateInfo());
Handle<FunctionTemplateInfo> function_template_info(
Handle<FunctionTemplateInfo>::cast(access_info.constant()));
DCHECK(!function_template_info->call_code()->IsUndefined(isolate()));
value = InlineApiCall(receiver, context, target, frame_state0, nullptr,
effect, control, shared_info, function_template_info);
}
// Remember to rewire the IfException edge if this is inside a try-block.
if (if_exceptions != nullptr) {
// Create the appropriate IfException/IfSuccess projections.
Node* const if_exception =
graph()->NewNode(common()->IfException(), *control, *effect);
Node* const if_success = graph()->NewNode(common()->IfSuccess(), *control);
if_exceptions->push_back(if_exception);
*control = if_success;
}
return value;
}
Node* JSNativeContextSpecialization::InlinePropertySetterCall(
Node* receiver, Node* value, Node* context, Node* frame_state,
Node** effect, Node** control, ZoneVector<Node*>* if_exceptions,
PropertyAccessInfo const& access_info) {
Node* target = jsgraph()->Constant(access_info.constant());
FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
Handle<SharedFunctionInfo> shared_info =
frame_info.shared_info().ToHandleChecked();
// We need a FrameState for the setter stub to restore the correct
// context and return the appropriate value to fullcodegen.
FrameStateFunctionInfo const* frame_info0 =
common()->CreateFrameStateFunctionInfo(FrameStateType::kSetterStub, 2, 0,
shared_info);
Node* frame_state0 = graph()->NewNode(
common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(),
frame_info0),
graph()->NewNode(common()->StateValues(2, SparseInputMask::Dense()),
receiver, value),
jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(), context,
target, frame_state);
// Introduce the call to the setter function.
if (access_info.constant()->IsJSFunction()) {
*effect = *control = graph()->NewNode(
jsgraph()->javascript()->Call(3, CallFrequency(), VectorSlotPair(),
ConvertReceiverMode::kNotNullOrUndefined),
target, receiver, value, context, frame_state0, *effect, *control);
} else {
DCHECK(access_info.constant()->IsFunctionTemplateInfo());
Handle<FunctionTemplateInfo> function_template_info(
Handle<FunctionTemplateInfo>::cast(access_info.constant()));
DCHECK(!function_template_info->call_code()->IsUndefined(isolate()));
value = InlineApiCall(receiver, context, target, frame_state0, value,
effect, control, shared_info, function_template_info);
}
// Remember to rewire the IfException edge if this is inside a try-block.
if (if_exceptions != nullptr) {
// Create the appropriate IfException/IfSuccess projections.
Node* const if_exception =
graph()->NewNode(common()->IfException(), *control, *effect);
Node* const if_success = graph()->NewNode(common()->IfSuccess(), *control);
if_exceptions->push_back(if_exception);
*control = if_success;
}
return value;
}
Node* JSNativeContextSpecialization::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) {
Handle<CallHandlerInfo> call_handler_info = handle(
CallHandlerInfo::cast(function_template_info->call_code()), isolate());
Handle<Object> call_data_object(call_handler_info->data(), isolate());
// Only setters have a value.
int const argc = value == nullptr ? 0 : 1;
// The stub always expects the receiver as the first param on the stack.
CallApiCallbackStub stub(
isolate(), argc,
true /* FunctionTemplateInfo doesn't have an associated context. */);
CallInterfaceDescriptor call_interface_descriptor =
stub.GetCallInterfaceDescriptor();
CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), call_interface_descriptor,
call_interface_descriptor.GetStackParameterCount() + argc +
1 /* implicit receiver */,
CallDescriptor::kNeedsFrameState, Operator::kNoProperties,
MachineType::AnyTagged(), 1);
Node* data = jsgraph()->Constant(call_data_object);
ApiFunction function(v8::ToCData<Address>(call_handler_info->callback()));
Node* function_reference =
graph()->NewNode(common()->ExternalConstant(ExternalReference(
&function, ExternalReference::DIRECT_API_CALL, isolate())));
Node* code = jsgraph()->HeapConstant(stub.GetCode());
// Add CallApiCallbackStub's register argument as well.
Node* inputs[11] = {
code, target, data, receiver /* holder */, function_reference, receiver};
int index = 6 + argc;
inputs[index++] = context;
inputs[index++] = frame_state;
inputs[index++] = *effect;
inputs[index++] = *control;
// This needs to stay here because of the edge case described in
// http://crbug.com/675648.
if (value != nullptr) {
inputs[6] = value;
}
return *effect = *control =
graph()->NewNode(common()->Call(call_descriptor), index, inputs);
}
JSNativeContextSpecialization::ValueEffectControl
JSNativeContextSpecialization::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) {
// Determine actual holder and perform prototype chain checks.
Handle<JSObject> holder;
PropertyAccessBuilder access_builder(jsgraph(), dependencies());
if (access_info.holder().ToHandle(&holder)) {
access_builder.AssumePrototypesStable(native_context(),
access_info.receiver_maps(), holder);
}
// Generate the actual property access.
Node* value;
if (access_info.IsNotFound()) {
value = jsgraph()->UndefinedConstant();
} else if (access_info.IsDataConstant()) {
DCHECK(!FLAG_track_constant_fields);
value = jsgraph()->Constant(access_info.constant());
} else if (access_info.IsAccessorConstant()) {
value = InlinePropertyGetterCall(receiver, context, frame_state, &effect,
&control, if_exceptions, access_info);
} else {
DCHECK(access_info.IsDataField() || access_info.IsDataConstantField());
value = access_builder.BuildLoadDataField(name, access_info, receiver,
&effect, &control);
}
return ValueEffectControl(value, effect, control);
}
JSNativeContextSpecialization::ValueEffectControl
JSNativeContextSpecialization::BuildPropertyAccess(
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) {
switch (access_mode) {
case AccessMode::kLoad:
return BuildPropertyLoad(receiver, context, frame_state, effect, control,
name, if_exceptions, access_info, language_mode);
case AccessMode::kStore:
case AccessMode::kStoreInLiteral:
return BuildPropertyStore(receiver, value, context, frame_state, effect,
control, name, if_exceptions, access_info,
access_mode, language_mode);
}
UNREACHABLE();
return ValueEffectControl();
}
JSNativeContextSpecialization::ValueEffectControl
JSNativeContextSpecialization::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) {
// Determine actual holder and perform prototype chain checks.
Handle<JSObject> holder;
PropertyAccessBuilder access_builder(jsgraph(), dependencies());
if (access_info.holder().ToHandle(&holder)) {
DCHECK_NE(AccessMode::kStoreInLiteral, access_mode);
AssumePrototypesStable(access_info.receiver_maps(), holder);
access_builder.AssumePrototypesStable(native_context(),
access_info.receiver_maps(), holder);
}
DCHECK(!access_info.IsNotFound());
// Generate the actual property access.
if (access_info.IsNotFound()) {
DCHECK_EQ(AccessMode::kLoad, access_mode);
value = jsgraph()->UndefinedConstant();
} else if (access_info.IsDataConstant()) {
if (access_info.IsDataConstant()) {
DCHECK(!FLAG_track_constant_fields);
Node* constant_value = jsgraph()->Constant(access_info.constant());
if (access_mode == AccessMode::kStore) {
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), value,
constant_value);
effect =
graph()->NewNode(simplified()->CheckIf(), check, effect, control);
}
Node* check =
graph()->NewNode(simplified()->ReferenceEqual(), value, constant_value);
effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control);
value = constant_value;
} else if (access_info.IsAccessorConstant()) {
Node* target = jsgraph()->Constant(access_info.constant());
FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
Handle<SharedFunctionInfo> shared_info =
frame_info.shared_info().ToHandleChecked();
switch (access_mode) {
case AccessMode::kLoad: {
// We need a FrameState for the getter stub to restore the correct
// context before returning to unoptimized code.
FrameStateFunctionInfo const* frame_info0 =
common()->CreateFrameStateFunctionInfo(FrameStateType::kGetterStub,
1, 0, shared_info);
Node* frame_state0 = graph()->NewNode(
common()->FrameState(BailoutId::None(),
OutputFrameStateCombine::Ignore(),
frame_info0),
graph()->NewNode(common()->StateValues(1, SparseInputMask::Dense()),
receiver),
jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(),
context, target, frame_state);
// Introduce the call to the getter function.
if (access_info.constant()->IsJSFunction()) {
value = effect = control = graph()->NewNode(
javascript()->Call(2, CallFrequency(), VectorSlotPair(),
ConvertReceiverMode::kNotNullOrUndefined),
target, receiver, context, frame_state0, effect, control);
} else {
DCHECK(access_info.constant()->IsFunctionTemplateInfo());
Handle<FunctionTemplateInfo> function_template_info(
Handle<FunctionTemplateInfo>::cast(access_info.constant()));
DCHECK(!function_template_info->call_code()->IsUndefined(isolate()));
ValueEffectControl value_effect_control = InlineApiCall(
receiver, context, target, frame_state0, nullptr, effect, control,
shared_info, function_template_info);
value = value_effect_control.value();
effect = value_effect_control.effect();
control = value_effect_control.control();
}
break;
}
case AccessMode::kStore: {
// We need a FrameState for the setter stub to restore the correct
// context and return the appropriate value to unoptimized code.
FrameStateFunctionInfo const* frame_info0 =
common()->CreateFrameStateFunctionInfo(FrameStateType::kSetterStub,
2, 0, shared_info);
Node* frame_state0 = graph()->NewNode(
common()->FrameState(BailoutId::None(),
OutputFrameStateCombine::Ignore(),
frame_info0),
graph()->NewNode(common()->StateValues(2, SparseInputMask::Dense()),
receiver, value),
jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(),
context, target, frame_state);
// Introduce the call to the setter function.
if (access_info.constant()->IsJSFunction()) {
effect = control = graph()->NewNode(
javascript()->Call(3, CallFrequency(), VectorSlotPair(),
ConvertReceiverMode::kNotNullOrUndefined),
target, receiver, value, context, frame_state0, effect, control);
} else {
DCHECK(access_info.constant()->IsFunctionTemplateInfo());
Handle<FunctionTemplateInfo> function_template_info(
Handle<FunctionTemplateInfo>::cast(access_info.constant()));
DCHECK(!function_template_info->call_code()->IsUndefined(isolate()));
ValueEffectControl value_effect_control = InlineApiCall(
receiver, context, target, frame_state0, value, effect, control,
shared_info, function_template_info);
value = value_effect_control.value();
effect = value_effect_control.effect();
control = value_effect_control.control();
}
break;
}
case AccessMode::kStoreInLiteral: {
UNREACHABLE();
break;
}
}
// Remember to rewire the IfException edge if this is inside a try-block.
if (if_exceptions != nullptr) {
// Create the appropriate IfException/IfSuccess projections.
Node* const if_exception =
graph()->NewNode(common()->IfException(), control, effect);
Node* const if_success = graph()->NewNode(common()->IfSuccess(), control);
if_exceptions->push_back(if_exception);
control = if_success;
}
value =
InlinePropertySetterCall(receiver, value, context, frame_state, &effect,
&control, if_exceptions, access_info);
} else {
DCHECK(access_info.IsDataField() || access_info.IsDataConstantField());
FieldIndex const field_index = access_info.field_index();
Type* const field_type = access_info.field_type();
MachineRepresentation const field_representation =
access_info.field_representation();
if (access_mode == AccessMode::kLoad) {
if (access_info.holder().ToHandle(&holder)) {
receiver = jsgraph()->Constant(holder);
}
// 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 ValueEffectControl(value, effect, control);
}
}
}
}
Node* storage = receiver;
if (!field_index.is_inobject()) {
storage = effect = graph()->NewNode(
@ -1524,38 +1573,6 @@ JSNativeContextSpecialization::BuildPropertyAccess(
field_type,
MachineType::TypeForRepresentation(field_representation),
kFullWriteBarrier};
if (access_mode == AccessMode::kLoad) {
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;
}
}
}
value = effect = graph()->NewNode(simplified()->LoadField(field_access),
storage, effect, control);
} else {
bool store_to_constant_field = FLAG_track_constant_fields &&
(access_mode == AccessMode::kStore) &&
access_info.IsDataConstantField();
@ -1610,14 +1627,13 @@ JSNativeContextSpecialization::BuildPropertyAccess(
DCHECK(!access_info.HasTransitionMap());
// If the field is constant check that the value we are going
// to store matches current value.
Node* current_value = effect =
graph()->NewNode(simplified()->LoadField(field_access), storage,
effect, control);
Node* current_value = effect = graph()->NewNode(
simplified()->LoadField(field_access), storage, effect, control);
Node* check = graph()->NewNode(simplified()->NumberEqual(),
current_value, value);
effect = graph()->NewNode(simplified()->CheckIf(), check, effect,
control);
effect =
graph()->NewNode(simplified()->CheckIf(), check, effect, control);
return ValueEffectControl(value, effect, control);
}
break;
@ -1629,14 +1645,13 @@ JSNativeContextSpecialization::BuildPropertyAccess(
DCHECK(!access_info.HasTransitionMap());
// If the field is constant check that the value we are going
// to store matches current value.
Node* current_value = effect =
graph()->NewNode(simplified()->LoadField(field_access), storage,
effect, control);
Node* current_value = effect = graph()->NewNode(
simplified()->LoadField(field_access), storage, effect, control);
Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
current_value, value);
effect = graph()->NewNode(simplified()->CheckIf(), check, effect,
control);
effect =
graph()->NewNode(simplified()->CheckIf(), check, effect, control);
return ValueEffectControl(value, effect, control);
}
@ -1648,7 +1663,7 @@ JSNativeContextSpecialization::BuildPropertyAccess(
} else if (field_representation ==
MachineRepresentation::kTaggedPointer) {
// Ensure that {value} is a HeapObject.
value = BuildCheckHeapObject(value, &effect, control);
value = access_builder.BuildCheckHeapObject(value, &effect, control);
Handle<Map> field_map;
if (access_info.field_map().ToHandle(&field_map)) {
// Emit a map check for the value.
@ -1702,15 +1717,14 @@ JSNativeContextSpecialization::BuildPropertyAccess(
effect = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForMap()), receiver,
jsgraph()->Constant(transition_map), effect, control);
effect = graph()->NewNode(simplified()->StoreField(field_access),
storage, value, effect, control);
effect = graph()->NewNode(simplified()->StoreField(field_access), storage,
value, effect, control);
effect = graph()->NewNode(common()->FinishRegion(),
jsgraph()->UndefinedConstant(), effect);
} else {
// Regular non-transitioning field store.
effect = graph()->NewNode(simplified()->StoreField(field_access),
storage, value, effect, control);
}
effect = graph()->NewNode(simplified()->StoreField(field_access), storage,
value, effect, control);
}
}
@ -1763,10 +1777,10 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreDataPropertyInLiteral(
Node* control = NodeProperties::GetControlInput(node);
// Monomorphic property access.
receiver = BuildCheckHeapObject(receiver, &effect, control);
effect =
BuildCheckMaps(receiver, effect, control, access_info.receiver_maps());
PropertyAccessBuilder access_builder(jsgraph(), dependencies());
receiver = access_builder.BuildCheckHeapObject(receiver, &effect, control);
access_builder.BuildCheckMaps(receiver, &effect, control,
access_info.receiver_maps());
// Ensure that {name} matches the cached name.
Node* name = NodeProperties::GetValueInput(node, 1);
@ -2107,112 +2121,6 @@ JSNativeContextSpecialization::BuildElementAccess(
return ValueEffectControl(value, effect, control);
}
JSNativeContextSpecialization::ValueEffectControl
JSNativeContextSpecialization::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) {
Handle<CallHandlerInfo> call_handler_info = handle(
CallHandlerInfo::cast(function_template_info->call_code()), isolate());
Handle<Object> call_data_object(call_handler_info->data(), isolate());
// Only setters have a value.
int const argc = value == nullptr ? 0 : 1;
// The stub always expects the receiver as the first param on the stack.
CallApiCallbackStub stub(
isolate(), argc,
true /* FunctionTemplateInfo doesn't have an associated context. */);
CallInterfaceDescriptor call_interface_descriptor =
stub.GetCallInterfaceDescriptor();
CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), call_interface_descriptor,
call_interface_descriptor.GetStackParameterCount() + argc +
1 /* implicit receiver */,
CallDescriptor::kNeedsFrameState, Operator::kNoProperties,
MachineType::AnyTagged(), 1);
Node* data = jsgraph()->Constant(call_data_object);
ApiFunction function(v8::ToCData<Address>(call_handler_info->callback()));
Node* function_reference =
graph()->NewNode(common()->ExternalConstant(ExternalReference(
&function, ExternalReference::DIRECT_API_CALL, isolate())));
Node* code = jsgraph()->HeapConstant(stub.GetCode());
// Add CallApiCallbackStub's register argument as well.
Node* inputs[11] = {
code, target, data, receiver /* holder */, function_reference, receiver};
int index = 6 + argc;
inputs[index++] = context;
inputs[index++] = frame_state;
inputs[index++] = effect;
inputs[index++] = control;
// This needs to stay here because of the edge case described in
// http://crbug.com/675648.
if (value != nullptr) {
inputs[6] = value;
}
Node* control0;
Node* effect0;
Node* value0 = effect0 = control0 =
graph()->NewNode(common()->Call(call_descriptor), index, inputs);
return ValueEffectControl(value0, effect0, control0);
}
Node* JSNativeContextSpecialization::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);
}
}
}
Node* JSNativeContextSpecialization::BuildCheckMaps(
Node* receiver, Node* effect, Node* control,
MapHandles 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 effect;
}
}
}
}
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;
}
}
return graph()->NewNode(simplified()->CheckMaps(flags, maps), receiver,
effect, control);
}
Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore(
Handle<Map> map, Node* properties, Node* effect, Node* control) {
// TODO(bmeurer/jkummerow): Property deletions can undo map transitions
@ -2261,21 +2169,6 @@ Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore(
return graph()->NewNode(common()->FinishRegion(), new_properties, effect);
}
void JSNativeContextSpecialization::AssumePrototypesStable(
MapHandles 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(), isolate());
}
dependencies()->AssumePrototypeMapsStable(map, holder);
}
}
bool JSNativeContextSpecialization::CanTreatHoleAsUndefined(
MapHandles const& receiver_maps) {
// Check if the array prototype chain is intact.

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',