| Index: src/compiler/js-global-specialization.cc
|
| diff --git a/src/compiler/js-global-specialization.cc b/src/compiler/js-global-specialization.cc
|
| deleted file mode 100644
|
| index 8cdf412bc24cd4783d888f8e1cdd509c43b19530..0000000000000000000000000000000000000000
|
| --- a/src/compiler/js-global-specialization.cc
|
| +++ /dev/null
|
| @@ -1,661 +0,0 @@
|
| -// 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.
|
| -
|
| -#include "src/compiler/js-global-specialization.h"
|
| -
|
| -#include "src/compilation-dependencies.h"
|
| -#include "src/compiler/access-builder.h"
|
| -#include "src/compiler/js-graph.h"
|
| -#include "src/compiler/js-operator.h"
|
| -#include "src/contexts.h"
|
| -#include "src/field-index-inl.h"
|
| -#include "src/lookup.h"
|
| -#include "src/objects-inl.h" // TODO(mstarzinger): Temporary cycle breaker!
|
| -#include "src/type-feedback-vector.h"
|
| -
|
| -namespace v8 {
|
| -namespace internal {
|
| -namespace compiler {
|
| -
|
| -struct JSGlobalSpecialization::ScriptContextTableLookupResult {
|
| - Handle<Context> context;
|
| - bool immutable;
|
| - int index;
|
| -};
|
| -
|
| -
|
| -JSGlobalSpecialization::JSGlobalSpecialization(
|
| - Editor* editor, JSGraph* jsgraph, Flags flags,
|
| - Handle<GlobalObject> global_object, CompilationDependencies* dependencies,
|
| - Zone* zone)
|
| - : AdvancedReducer(editor),
|
| - jsgraph_(jsgraph),
|
| - flags_(flags),
|
| - global_object_(global_object),
|
| - dependencies_(dependencies),
|
| - zone_(zone) {}
|
| -
|
| -
|
| -Reduction JSGlobalSpecialization::Reduce(Node* node) {
|
| - switch (node->opcode()) {
|
| - case IrOpcode::kJSLoadGlobal:
|
| - return ReduceJSLoadGlobal(node);
|
| - case IrOpcode::kJSStoreGlobal:
|
| - return ReduceJSStoreGlobal(node);
|
| - case IrOpcode::kJSLoadNamed:
|
| - return ReduceJSLoadNamed(node);
|
| - default:
|
| - break;
|
| - }
|
| - return NoChange();
|
| -}
|
| -
|
| -
|
| -Reduction JSGlobalSpecialization::ReduceJSLoadGlobal(Node* node) {
|
| - DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode());
|
| - Handle<Name> name = LoadGlobalParametersOf(node->op()).name();
|
| - Node* effect = NodeProperties::GetEffectInput(node);
|
| - Node* control = NodeProperties::GetControlInput(node);
|
| -
|
| - // Try to lookup the name on the script context table first (lexical scoping).
|
| - ScriptContextTableLookupResult result;
|
| - if (LookupInScriptContextTable(name, &result)) {
|
| - Node* context = jsgraph()->Constant(result.context);
|
| - Node* value = effect = graph()->NewNode(
|
| - javascript()->LoadContext(0, result.index, result.immutable), context,
|
| - context, effect);
|
| - return Replace(node, value, effect);
|
| - }
|
| -
|
| - // Lookup on the global object instead. We only deal with own data
|
| - // properties of the global object here (represented as PropertyCell).
|
| - LookupIterator it(global_object(), name, LookupIterator::OWN);
|
| - if (it.state() != LookupIterator::DATA) return NoChange();
|
| - Handle<PropertyCell> property_cell = it.GetPropertyCell();
|
| - PropertyDetails property_details = property_cell->property_details();
|
| - Handle<Object> property_cell_value(property_cell->value(), isolate());
|
| -
|
| - // Load from non-configurable, read-only data property on the global
|
| - // object can be constant-folded, even without deoptimization support.
|
| - if (!property_details.IsConfigurable() && property_details.IsReadOnly()) {
|
| - return Replace(node, property_cell_value);
|
| - }
|
| -
|
| - // Load from constant/undefined global property can be constant-folded
|
| - // with deoptimization support, by adding a code dependency on the cell.
|
| - if ((property_details.cell_type() == PropertyCellType::kConstant ||
|
| - property_details.cell_type() == PropertyCellType::kUndefined) &&
|
| - (flags() & kDeoptimizationEnabled)) {
|
| - dependencies()->AssumePropertyCell(property_cell);
|
| - return Replace(node, property_cell_value);
|
| - }
|
| -
|
| - // Load from constant type global property can benefit from representation
|
| - // (and map) feedback with deoptimization support (requires code dependency).
|
| - if (property_details.cell_type() == PropertyCellType::kConstantType &&
|
| - (flags() & kDeoptimizationEnabled)) {
|
| - dependencies()->AssumePropertyCell(property_cell);
|
| - // Compute proper type based on the current value in the cell.
|
| - Type* property_cell_value_type;
|
| - if (property_cell_value->IsSmi()) {
|
| - property_cell_value_type = Type::Intersect(
|
| - Type::SignedSmall(), Type::TaggedSigned(), graph()->zone());
|
| - } else if (property_cell_value->IsNumber()) {
|
| - property_cell_value_type = Type::Intersect(
|
| - Type::Number(), Type::TaggedPointer(), graph()->zone());
|
| - } else {
|
| - property_cell_value_type = Type::Of(property_cell_value, graph()->zone());
|
| - }
|
| - Node* value = effect = graph()->NewNode(
|
| - simplified()->LoadField(
|
| - AccessBuilder::ForPropertyCellValue(property_cell_value_type)),
|
| - jsgraph()->Constant(property_cell), effect, control);
|
| - return Replace(node, value, effect);
|
| - }
|
| -
|
| - // Load from non-configurable, data property on the global can be lowered to
|
| - // a field load, even without deoptimization, because the property cannot be
|
| - // deleted or reconfigured to an accessor/interceptor property.
|
| - if (property_details.IsConfigurable()) {
|
| - // With deoptimization support, we can lower loads even from configurable
|
| - // data properties on the global object, by adding a code dependency on
|
| - // the cell.
|
| - if (!(flags() & kDeoptimizationEnabled)) return NoChange();
|
| - dependencies()->AssumePropertyCell(property_cell);
|
| - }
|
| - Node* value = effect = graph()->NewNode(
|
| - simplified()->LoadField(AccessBuilder::ForPropertyCellValue()),
|
| - jsgraph()->Constant(property_cell), effect, control);
|
| - return Replace(node, value, effect);
|
| -}
|
| -
|
| -
|
| -Reduction JSGlobalSpecialization::ReduceJSStoreGlobal(Node* node) {
|
| - DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode());
|
| - Handle<Name> name = StoreGlobalParametersOf(node->op()).name();
|
| - Node* value = NodeProperties::GetValueInput(node, 2);
|
| - Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
|
| - Node* effect = NodeProperties::GetEffectInput(node);
|
| - Node* control = NodeProperties::GetControlInput(node);
|
| -
|
| - // Try to lookup the name on the script context table first (lexical scoping).
|
| - ScriptContextTableLookupResult result;
|
| - if (LookupInScriptContextTable(name, &result)) {
|
| - if (result.immutable) return NoChange();
|
| - Node* context = jsgraph()->Constant(result.context);
|
| - effect = graph()->NewNode(javascript()->StoreContext(0, result.index),
|
| - context, value, context, effect, control);
|
| - return Replace(node, value, effect, control);
|
| - }
|
| -
|
| - // Lookup on the global object instead. We only deal with own data
|
| - // properties of the global object here (represented as PropertyCell).
|
| - LookupIterator it(global_object(), name, LookupIterator::OWN);
|
| - if (it.state() != LookupIterator::DATA) return NoChange();
|
| - Handle<PropertyCell> property_cell = it.GetPropertyCell();
|
| - PropertyDetails property_details = property_cell->property_details();
|
| - Handle<Object> property_cell_value(property_cell->value(), isolate());
|
| -
|
| - // Don't even bother trying to lower stores to read-only data properties.
|
| - if (property_details.IsReadOnly()) return NoChange();
|
| - switch (property_details.cell_type()) {
|
| - case PropertyCellType::kUndefined: {
|
| - return NoChange();
|
| - }
|
| - case PropertyCellType::kConstant: {
|
| - // Store to constant property cell requires deoptimization support,
|
| - // because we might even need to eager deoptimize for mismatch.
|
| - if (!(flags() & kDeoptimizationEnabled)) return NoChange();
|
| - dependencies()->AssumePropertyCell(property_cell);
|
| - Node* check =
|
| - graph()->NewNode(simplified()->ReferenceEqual(Type::Tagged()), value,
|
| - jsgraph()->Constant(property_cell_value));
|
| - Node* branch =
|
| - graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
|
| - Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
|
| - Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state,
|
| - effect, if_false);
|
| - // TODO(bmeurer): This should be on the AdvancedReducer somehow.
|
| - NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
|
| - control = graph()->NewNode(common()->IfTrue(), branch);
|
| - return Replace(node, value, effect, control);
|
| - }
|
| - case PropertyCellType::kConstantType: {
|
| - // Store to constant-type property cell requires deoptimization support,
|
| - // because we might even need to eager deoptimize for mismatch.
|
| - if (!(flags() & kDeoptimizationEnabled)) return NoChange();
|
| - dependencies()->AssumePropertyCell(property_cell);
|
| - Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value);
|
| - if (property_cell_value->IsHeapObject()) {
|
| - Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
|
| - check, control);
|
| - Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
|
| - Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state,
|
| - effect, if_true);
|
| - // TODO(bmeurer): This should be on the AdvancedReducer somehow.
|
| - NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
|
| - control = graph()->NewNode(common()->IfFalse(), branch);
|
| - Node* value_map =
|
| - graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
|
| - value, effect, control);
|
| - Handle<Map> property_cell_value_map(
|
| - Handle<HeapObject>::cast(property_cell_value)->map(), isolate());
|
| - check = graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()),
|
| - value_map,
|
| - jsgraph()->Constant(property_cell_value_map));
|
| - }
|
| - Node* branch =
|
| - graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
|
| - Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
|
| - Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state,
|
| - effect, if_false);
|
| - // TODO(bmeurer): This should be on the AdvancedReducer somehow.
|
| - NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
|
| - control = graph()->NewNode(common()->IfTrue(), branch);
|
| - break;
|
| - }
|
| - case PropertyCellType::kMutable: {
|
| - // Store to non-configurable, data property on the global can be lowered
|
| - // to a field store, even without deoptimization, because the property
|
| - // cannot be deleted or reconfigured to an accessor/interceptor property.
|
| - if (property_details.IsConfigurable()) {
|
| - // With deoptimization support, we can lower stores even to configurable
|
| - // data properties on the global object, by adding a code dependency on
|
| - // the cell.
|
| - if (!(flags() & kDeoptimizationEnabled)) return NoChange();
|
| - dependencies()->AssumePropertyCell(property_cell);
|
| - }
|
| - break;
|
| - }
|
| - }
|
| - effect = graph()->NewNode(
|
| - simplified()->StoreField(AccessBuilder::ForPropertyCellValue()),
|
| - jsgraph()->Constant(property_cell), value, effect, control);
|
| - return Replace(node, value, effect, control);
|
| -}
|
| -
|
| -
|
| -// This class encapsulates all information required to access a certain
|
| -// object property, either on the object itself or on the prototype chain.
|
| -class JSGlobalSpecialization::PropertyAccessInfo final {
|
| - public:
|
| - enum Kind { kInvalid, kData, kDataConstant };
|
| -
|
| - static PropertyAccessInfo DataConstant(Type* receiver_type,
|
| - Handle<Object> constant,
|
| - MaybeHandle<JSObject> holder) {
|
| - return PropertyAccessInfo(holder, constant, receiver_type);
|
| - }
|
| - static PropertyAccessInfo Data(Type* receiver_type, FieldIndex field_index,
|
| - Representation field_representation,
|
| - MaybeHandle<JSObject> holder) {
|
| - return PropertyAccessInfo(holder, field_index, field_representation,
|
| - receiver_type);
|
| - }
|
| -
|
| - PropertyAccessInfo() : kind_(kInvalid) {}
|
| - PropertyAccessInfo(MaybeHandle<JSObject> holder, Handle<Object> constant,
|
| - Type* receiver_type)
|
| - : kind_(kDataConstant),
|
| - receiver_type_(receiver_type),
|
| - constant_(constant),
|
| - holder_(holder) {}
|
| - PropertyAccessInfo(MaybeHandle<JSObject> holder, FieldIndex field_index,
|
| - Representation field_representation, Type* receiver_type)
|
| - : kind_(kData),
|
| - receiver_type_(receiver_type),
|
| - holder_(holder),
|
| - field_index_(field_index),
|
| - field_representation_(field_representation) {}
|
| -
|
| - bool IsDataConstant() const { return kind() == kDataConstant; }
|
| - bool IsData() const { return kind() == kData; }
|
| -
|
| - Kind kind() const { return kind_; }
|
| - MaybeHandle<JSObject> holder() const { return holder_; }
|
| - Handle<Object> constant() const { return constant_; }
|
| - FieldIndex field_index() const { return field_index_; }
|
| - Representation field_representation() const { return field_representation_; }
|
| - Type* receiver_type() const { return receiver_type_; }
|
| -
|
| - private:
|
| - Kind kind_;
|
| - Type* receiver_type_;
|
| - Handle<Object> constant_;
|
| - MaybeHandle<JSObject> holder_;
|
| - FieldIndex field_index_;
|
| - Representation field_representation_;
|
| -};
|
| -
|
| -
|
| -namespace {
|
| -
|
| -bool CanInlinePropertyAccess(Handle<Map> map) {
|
| - // TODO(bmeurer): Do something about the number stuff.
|
| - if (map->instance_type() == HEAP_NUMBER_TYPE) return false;
|
| - if (map->instance_type() < FIRST_NONSTRING_TYPE) return true;
|
| - return map->IsJSObjectMap() && !map->is_dictionary_map() &&
|
| - !map->has_named_interceptor() &&
|
| - // TODO(verwaest): Whitelist contexts to which we have access.
|
| - !map->is_access_check_needed();
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -
|
| -bool JSGlobalSpecialization::ComputePropertyAccessInfo(
|
| - Handle<Map> map, Handle<Name> name, PropertyAccessInfo* access_info) {
|
| - MaybeHandle<JSObject> holder;
|
| - Type* receiver_type = Type::Class(map, graph()->zone());
|
| - while (CanInlinePropertyAccess(map)) {
|
| - // Lookup the named property on the {map}.
|
| - Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
|
| - int const number = descriptors->SearchWithCache(*name, *map);
|
| - if (number != DescriptorArray::kNotFound) {
|
| - PropertyDetails const details = descriptors->GetDetails(number);
|
| - if (details.type() == DATA_CONSTANT) {
|
| - *access_info = PropertyAccessInfo::DataConstant(
|
| - receiver_type, handle(descriptors->GetValue(number), isolate()),
|
| - holder);
|
| - return true;
|
| - } else if (details.type() == DATA) {
|
| - int index = descriptors->GetFieldIndex(number);
|
| - Representation field_representation = details.representation();
|
| - FieldIndex field_index = FieldIndex::ForPropertyIndex(
|
| - *map, index, field_representation.IsDouble());
|
| - *access_info = PropertyAccessInfo::Data(receiver_type, field_index,
|
| - field_representation, holder);
|
| - return true;
|
| - } else {
|
| - // TODO(bmeurer): Add support for accessors.
|
| - break;
|
| - }
|
| - }
|
| -
|
| - // Don't search on the prototype chain for special indices in case of
|
| - // integer indexed exotic objects (see ES6 section 9.4.5).
|
| - if (map->IsJSTypedArrayMap() && name->IsString() &&
|
| - IsSpecialIndex(isolate()->unicode_cache(), String::cast(*name))) {
|
| - break;
|
| - }
|
| -
|
| - // Walk up the prototype chain.
|
| - if (!map->prototype()->IsJSObject()) {
|
| - // TODO(bmeurer): Handle the not found case if the prototype is null.
|
| - break;
|
| - }
|
| - Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate());
|
| - if (map_prototype->map()->is_deprecated()) {
|
| - // Try to migrate the prototype object so we don't embed the deprecated
|
| - // map into the optimized code.
|
| - JSObject::TryMigrateInstance(map_prototype);
|
| - }
|
| - map = handle(map_prototype->map(), isolate());
|
| - holder = map_prototype;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -
|
| -bool JSGlobalSpecialization::ComputePropertyAccessInfos(
|
| - MapHandleList const& maps, Handle<Name> name,
|
| - ZoneVector<PropertyAccessInfo>* access_infos) {
|
| - for (Handle<Map> map : maps) {
|
| - PropertyAccessInfo access_info;
|
| - if (!ComputePropertyAccessInfo(map, name, &access_info)) return false;
|
| - access_infos->push_back(access_info);
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -
|
| -Reduction JSGlobalSpecialization::ReduceJSLoadNamed(Node* node) {
|
| - DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode());
|
| - LoadNamedParameters const p = LoadNamedParametersOf(node->op());
|
| - Handle<Name> name = p.name();
|
| - Node* receiver = NodeProperties::GetValueInput(node, 0);
|
| - Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
|
| - Node* effect = NodeProperties::GetEffectInput(node);
|
| - Node* control = NodeProperties::GetControlInput(node);
|
| -
|
| - // Not much we can do if deoptimization support is disabled.
|
| - if (!(flags() & kDeoptimizationEnabled)) return NoChange();
|
| -
|
| - // Extract receiver maps from the LOAD_IC using the LoadICNexus.
|
| - MapHandleList receiver_maps;
|
| - if (!p.feedback().IsValid()) return NoChange();
|
| - LoadICNexus nexus(p.feedback().vector(), p.feedback().slot());
|
| - if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange();
|
| - DCHECK_LT(0, receiver_maps.length());
|
| -
|
| - // Compute property access infos for the receiver maps.
|
| - ZoneVector<PropertyAccessInfo> access_infos(zone());
|
| - if (!ComputePropertyAccessInfos(receiver_maps, name, &access_infos)) {
|
| - return NoChange();
|
| - }
|
| - DCHECK(!access_infos.empty());
|
| -
|
| - // The final states for every polymorphic branch. We join them with
|
| - // Merge+Phi+EffectPhi at the bottom.
|
| - ZoneVector<Node*> values(zone());
|
| - ZoneVector<Node*> effects(zone());
|
| - ZoneVector<Node*> controls(zone());
|
| -
|
| - // The list of "exiting" controls, which currently go to a single deoptimize.
|
| - // TODO(bmeurer): Consider using an IC as fallback.
|
| - Node* const exit_effect = effect;
|
| - ZoneVector<Node*> exit_controls(zone());
|
| -
|
| - // Ensure that {receiver} is a heap object.
|
| - Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
|
| - Node* branch =
|
| - graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
|
| - exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
|
| - control = graph()->NewNode(common()->IfFalse(), branch);
|
| -
|
| - // Load the {receiver} map. The resulting effect is the dominating effect for
|
| - // all (polymorphic) branches.
|
| - Node* receiver_map = effect =
|
| - graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
|
| - receiver, effect, control);
|
| -
|
| - // Generate code for the various different property access patterns.
|
| - Node* fallthrough_control = control;
|
| - for (PropertyAccessInfo const& access_info : access_infos) {
|
| - Node* this_value = receiver;
|
| - Node* this_effect = effect;
|
| - Node* this_control;
|
| -
|
| - // Perform map check on {receiver}.
|
| - Type* receiver_type = access_info.receiver_type();
|
| - if (receiver_type->Is(Type::String())) {
|
| - // Emit an instance type check for strings.
|
| - Node* receiver_instance_type = this_effect = graph()->NewNode(
|
| - simplified()->LoadField(AccessBuilder::ForMapInstanceType()),
|
| - receiver_map, this_effect, fallthrough_control);
|
| - Node* check =
|
| - graph()->NewNode(machine()->Uint32LessThan(), receiver_instance_type,
|
| - jsgraph()->Uint32Constant(FIRST_NONSTRING_TYPE));
|
| - Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
|
| - check, fallthrough_control);
|
| - fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
|
| - this_control = graph()->NewNode(common()->IfTrue(), branch);
|
| - } else {
|
| - // Emit a (sequence of) map checks for other properties.
|
| - ZoneVector<Node*> this_controls(zone());
|
| - for (auto i = access_info.receiver_type()->Classes(); !i.Done();
|
| - i.Advance()) {
|
| - Handle<Map> map = i.Current();
|
| - Node* check =
|
| - graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()),
|
| - receiver_map, jsgraph()->Constant(map));
|
| - Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
|
| - check, fallthrough_control);
|
| - this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
|
| - fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
|
| - }
|
| - int const this_control_count = static_cast<int>(this_controls.size());
|
| - this_control =
|
| - (this_control_count == 1)
|
| - ? this_controls.front()
|
| - : graph()->NewNode(common()->Merge(this_control_count),
|
| - this_control_count, &this_controls.front());
|
| - }
|
| -
|
| - // Determine actual holder and perform prototype chain checks.
|
| - Handle<JSObject> holder;
|
| - if (access_info.holder().ToHandle(&holder)) {
|
| - this_value = jsgraph()->Constant(holder);
|
| - for (auto i = access_info.receiver_type()->Classes(); !i.Done();
|
| - i.Advance()) {
|
| - Handle<Map> map = i.Current();
|
| - PrototypeIterator j(map);
|
| - while (true) {
|
| - // Check that the {prototype} still has the same map. For stable
|
| - // maps, we can add a stability dependency on the prototype map;
|
| - // for everything else we need to perform a map check at runtime.
|
| - Handle<JSReceiver> prototype =
|
| - PrototypeIterator::GetCurrent<JSReceiver>(j);
|
| - if (prototype->map()->is_stable()) {
|
| - dependencies()->AssumeMapStable(
|
| - handle(prototype->map(), isolate()));
|
| - } else {
|
| - Node* prototype_map = this_effect = graph()->NewNode(
|
| - simplified()->LoadField(AccessBuilder::ForMap()),
|
| - jsgraph()->Constant(prototype), this_effect, this_control);
|
| - Node* check = graph()->NewNode(
|
| - simplified()->ReferenceEqual(Type::Internal()), prototype_map,
|
| - jsgraph()->Constant(handle(prototype->map(), isolate())));
|
| - Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
|
| - check, this_control);
|
| - exit_controls.push_back(
|
| - graph()->NewNode(common()->IfFalse(), branch));
|
| - this_control = graph()->NewNode(common()->IfTrue(), branch);
|
| - }
|
| - // Stop once we get to the holder.
|
| - if (prototype.is_identical_to(holder)) break;
|
| - j.Advance();
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Generate the actual property access.
|
| - if (access_info.IsDataConstant()) {
|
| - this_value = jsgraph()->Constant(access_info.constant());
|
| - } else {
|
| - // TODO(bmeurer): This is sort of adhoc, and must be refactored into some
|
| - // common code once we also have support for stores.
|
| - DCHECK(access_info.IsData());
|
| - FieldIndex const field_index = access_info.field_index();
|
| - Representation const field_representation =
|
| - access_info.field_representation();
|
| - if (!field_index.is_inobject()) {
|
| - this_value = this_effect = graph()->NewNode(
|
| - simplified()->LoadField(AccessBuilder::ForJSObjectProperties()),
|
| - this_value, this_effect, this_control);
|
| - }
|
| - FieldAccess field_access;
|
| - field_access.base_is_tagged = kTaggedBase;
|
| - field_access.offset = field_index.offset();
|
| - field_access.name = name;
|
| - field_access.type = Type::Any();
|
| - field_access.machine_type = kMachAnyTagged;
|
| - if (field_representation.IsSmi()) {
|
| - field_access.type = Type::Intersect(
|
| - Type::SignedSmall(), Type::TaggedSigned(), graph()->zone());
|
| - } else if (field_representation.IsDouble()) {
|
| - if (!field_index.is_inobject() || field_index.is_hidden_field() ||
|
| - !FLAG_unbox_double_fields) {
|
| - this_value = this_effect =
|
| - graph()->NewNode(simplified()->LoadField(field_access),
|
| - this_value, this_effect, this_control);
|
| - field_access.offset = HeapNumber::kValueOffset;
|
| - field_access.name = MaybeHandle<Name>();
|
| - }
|
| - field_access.type = Type::Intersect(
|
| - Type::Number(), Type::UntaggedFloat64(), graph()->zone());
|
| - field_access.machine_type = kMachFloat64;
|
| - } else if (field_representation.IsHeapObject()) {
|
| - field_access.type = Type::TaggedPointer();
|
| - }
|
| - this_value = this_effect =
|
| - graph()->NewNode(simplified()->LoadField(field_access), this_value,
|
| - this_effect, this_control);
|
| - }
|
| -
|
| - // Remember the final state for this property access.
|
| - values.push_back(this_value);
|
| - effects.push_back(this_effect);
|
| - controls.push_back(this_control);
|
| - }
|
| -
|
| - // Collect the fallthru control as final "exit" control.
|
| - exit_controls.push_back(fallthrough_control);
|
| -
|
| - // TODO(bmeurer/mtrofin): Splintering cannot currently deal with deferred
|
| - // blocks that contain only a single non-deoptimize instruction (i.e. a
|
| - // jump). Generating a single Merge here, which joins all the deoptimizing
|
| - // controls would generate a lot of these basic blocks, however. So this
|
| - // is disabled for now until splintering is fixed.
|
| -#if 0
|
| - // Generate the single "exit" point, where we get if either all map/instance
|
| - // type checks failed, or one of the assumptions inside one of the cases
|
| - // failes (i.e. failing prototype chain check).
|
| - // TODO(bmeurer): Consider falling back to IC here if deoptimization is
|
| - // disabled.
|
| - int const exit_control_count = static_cast<int>(exit_controls.size());
|
| - Node* exit_control =
|
| - (exit_control_count == 1)
|
| - ? exit_controls.front()
|
| - : graph()->NewNode(common()->Merge(exit_control_count),
|
| - exit_control_count, &exit_controls.front());
|
| - Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state,
|
| - exit_effect, exit_control);
|
| - // TODO(bmeurer): This should be on the AdvancedReducer somehow.
|
| - NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
|
| -#else
|
| - for (Node* const exit_control : exit_controls) {
|
| - Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state,
|
| - exit_effect, exit_control);
|
| - // TODO(bmeurer): This should be on the AdvancedReducer somehow.
|
| - NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
|
| - }
|
| -#endif
|
| -
|
| - // Generate the final merge point for all (polymorphic) branches.
|
| - Node* value;
|
| - int const control_count = static_cast<int>(controls.size());
|
| - if (control_count == 1) {
|
| - value = values.front();
|
| - effect = effects.front();
|
| - control = controls.front();
|
| - } else {
|
| - control = graph()->NewNode(common()->Merge(control_count), control_count,
|
| - &controls.front());
|
| - values.push_back(control);
|
| - value = graph()->NewNode(common()->Phi(kMachAnyTagged, control_count),
|
| - control_count + 1, &values.front());
|
| - effects.push_back(control);
|
| - effect = graph()->NewNode(common()->EffectPhi(control_count),
|
| - control_count + 1, &effects.front());
|
| - }
|
| - return Replace(node, value, effect, control);
|
| -}
|
| -
|
| -
|
| -Reduction JSGlobalSpecialization::Replace(Node* node, Handle<Object> value) {
|
| - return Replace(node, jsgraph()->Constant(value));
|
| -}
|
| -
|
| -
|
| -bool JSGlobalSpecialization::LookupInScriptContextTable(
|
| - Handle<Name> name, ScriptContextTableLookupResult* result) {
|
| - if (!name->IsString()) return false;
|
| - Handle<ScriptContextTable> script_context_table(
|
| - global_object()->native_context()->script_context_table());
|
| - ScriptContextTable::LookupResult lookup_result;
|
| - if (!ScriptContextTable::Lookup(script_context_table,
|
| - Handle<String>::cast(name), &lookup_result)) {
|
| - return false;
|
| - }
|
| - Handle<Context> script_context = ScriptContextTable::GetContext(
|
| - script_context_table, lookup_result.context_index);
|
| - if (script_context->is_the_hole(lookup_result.slot_index)) return false;
|
| - result->context = script_context;
|
| - result->immutable = IsImmutableVariableMode(lookup_result.mode);
|
| - result->index = lookup_result.slot_index;
|
| - return true;
|
| -}
|
| -
|
| -
|
| -Graph* JSGlobalSpecialization::graph() const { return jsgraph()->graph(); }
|
| -
|
| -
|
| -Isolate* JSGlobalSpecialization::isolate() const {
|
| - return jsgraph()->isolate();
|
| -}
|
| -
|
| -
|
| -MachineOperatorBuilder* JSGlobalSpecialization::machine() const {
|
| - return jsgraph()->machine();
|
| -}
|
| -
|
| -
|
| -CommonOperatorBuilder* JSGlobalSpecialization::common() const {
|
| - return jsgraph()->common();
|
| -}
|
| -
|
| -
|
| -JSOperatorBuilder* JSGlobalSpecialization::javascript() const {
|
| - return jsgraph()->javascript();
|
| -}
|
| -
|
| -
|
| -SimplifiedOperatorBuilder* JSGlobalSpecialization::simplified() const {
|
| - return jsgraph()->simplified();
|
| -}
|
| -
|
| -} // namespace compiler
|
| -} // namespace internal
|
| -} // namespace v8
|
|
|