Revert of [turbofan] Initial support for monomorphic/polymorphic property loads. (patchset #3 id:100001 of https://codereview.chromium.org/1396333010/ )

Reason for revert:
Waterfall redness.

Original issue's description:
> [turbofan] Initial support for monomorphic/polymorphic property loads.
>
> Native context specialization now lowers monomorphic and
> polymorphic accesses to data and constant data properties on
> object and/or prototype chain. We don't deal with accessors
> yet, and we also completely ignore proxies (which is compatible
> with what Crankshaft does).
>
> The code is more or less the straightforward implementation. We
> will need to refactor that and extract common patterns once the
> remaining bits for full load/store support is in.
>
> CQ_INCLUDE_TRYBOTS=tryserver.v8:v8_linux_nosnap_rel
> R=jarin@chromium.org
> BUG=v8:4470
> LOG=n
>
> Committed: https://crrev.com/3a0bf860b7177f7abef01ff308a53603389d958e
> Cr-Commit-Position: refs/heads/master@{#31340}

TBR=bmeurer@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=v8:4470

Review URL: https://codereview.chromium.org/1408123002

Cr-Commit-Position: refs/heads/master@{#31341}
This commit is contained in:
jarin 2015-10-16 07:56:04 -07:00 committed by Commit bot
parent 3a0bf860b7
commit 5c53481233
12 changed files with 14 additions and 400 deletions

View File

@ -106,15 +106,6 @@ void CompilationDependencies::Rollback() {
}
void CompilationDependencies::AssumeMapStable(Handle<Map> map) {
DCHECK(map->is_stable());
// Do nothing if the map cannot transition.
if (map->CanTransition()) {
Insert(DependentCode::kPrototypeCheckGroup, map);
}
}
void CompilationDependencies::AssumeTransitionStable(
Handle<AllocationSite> site) {
// Do nothing if the object doesn't have any useful element transitions left.

View File

@ -31,7 +31,6 @@ class CompilationDependencies {
void AssumeFieldType(Handle<Map> map) {
Insert(DependentCode::kFieldTypeGroup, map);
}
void AssumeMapStable(Handle<Map> map);
void AssumePropertyCell(Handle<PropertyCell> cell) {
Insert(DependentCode::kPropertyCellChangedGroup, cell);
}

View File

@ -9,10 +9,8 @@
#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 {
@ -27,14 +25,12 @@ struct JSGlobalSpecialization::ScriptContextTableLookupResult {
JSGlobalSpecialization::JSGlobalSpecialization(
Editor* editor, JSGraph* jsgraph, Flags flags,
Handle<GlobalObject> global_object, CompilationDependencies* dependencies,
Zone* zone)
Handle<GlobalObject> global_object, CompilationDependencies* dependencies)
: AdvancedReducer(editor),
jsgraph_(jsgraph),
flags_(flags),
global_object_(global_object),
dependencies_(dependencies),
zone_(zone) {}
dependencies_(dependencies) {}
Reduction JSGlobalSpecialization::Reduce(Node* node) {
@ -43,8 +39,6 @@ Reduction JSGlobalSpecialization::Reduce(Node* node) {
return ReduceJSLoadGlobal(node);
case IrOpcode::kJSStoreGlobal:
return ReduceJSStoreGlobal(node);
case IrOpcode::kJSLoadNamed:
return ReduceJSLoadNamed(node);
default:
break;
}
@ -236,359 +230,11 @@ Reduction JSGlobalSpecialization::ReduceJSStoreGlobal(Node* node) {
}
// 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);
}
// 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.
exit_controls.push_back(fallthrough_control);
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);
// 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) {
// TODO(bmeurer): Move this to JSGraph::HeapConstant instead?
if (value->IsConsString()) {
value = String::Flatten(Handle<String>::cast(value), TENURED);
}
return Replace(node, jsgraph()->Constant(value));
}
@ -621,11 +267,6 @@ Isolate* JSGlobalSpecialization::isolate() const {
}
MachineOperatorBuilder* JSGlobalSpecialization::machine() const {
return jsgraph()->machine();
}
CommonOperatorBuilder* JSGlobalSpecialization::common() const {
return jsgraph()->common();
}

View File

@ -22,7 +22,6 @@ namespace compiler {
class CommonOperatorBuilder;
class JSGraph;
class JSOperatorBuilder;
class MachineOperatorBuilder;
// Specializes a given JSGraph to a given GlobalObject, potentially constant
@ -39,14 +38,13 @@ class JSGlobalSpecialization final : public AdvancedReducer {
JSGlobalSpecialization(Editor* editor, JSGraph* jsgraph, Flags flags,
Handle<GlobalObject> global_object,
CompilationDependencies* dependencies, Zone* zone);
CompilationDependencies* dependencies);
Reduction Reduce(Node* node) final;
private:
Reduction ReduceJSLoadGlobal(Node* node);
Reduction ReduceJSStoreGlobal(Node* node);
Reduction ReduceJSLoadNamed(Node* node);
Reduction Replace(Node* node, Node* value, Node* effect = nullptr,
Node* control = nullptr) {
@ -55,12 +53,6 @@ class JSGlobalSpecialization final : public AdvancedReducer {
}
Reduction Replace(Node* node, Handle<Object> value);
class PropertyAccessInfo;
bool ComputePropertyAccessInfo(Handle<Map> map, Handle<Name> name,
PropertyAccessInfo* access_info);
bool ComputePropertyAccessInfos(MapHandleList const& maps, Handle<Name> name,
ZoneVector<PropertyAccessInfo>* access_infos);
struct ScriptContextTableLookupResult;
bool LookupInScriptContextTable(Handle<Name> name,
ScriptContextTableLookupResult* result);
@ -71,17 +63,14 @@ class JSGlobalSpecialization final : public AdvancedReducer {
CommonOperatorBuilder* common() const;
JSOperatorBuilder* javascript() const;
SimplifiedOperatorBuilder* simplified() const;
MachineOperatorBuilder* machine() const;
Flags flags() const { return flags_; }
Handle<GlobalObject> global_object() const { return global_object_; }
CompilationDependencies* dependencies() const { return dependencies_; }
Zone* zone() const { return zone_; }
JSGraph* const jsgraph_;
Flags const flags_;
Handle<GlobalObject> global_object_;
CompilationDependencies* const dependencies_;
Zone* const zone_;
DISALLOW_COPY_AND_ASSIGN(JSGlobalSpecialization);
};

View File

@ -11,11 +11,8 @@ namespace v8 {
namespace internal {
namespace compiler {
Node* JSGraph::ImmovableHeapConstant(Handle<HeapObject> value) {
if (value->IsConsString()) {
value = String::Flatten(Handle<String>::cast(value), TENURED);
}
return graph()->NewNode(common()->HeapConstant(value));
Node* JSGraph::ImmovableHeapConstant(Handle<HeapObject> object) {
return graph()->NewNode(common()->HeapConstant(object));
}
@ -81,7 +78,7 @@ Node* JSGraph::HeapConstant(Handle<HeapObject> value) {
// TODO(titzer): We could also match against the addresses of immortable
// immovables here, even without access to the heap, thus always
// canonicalizing references to them.
return ImmovableHeapConstant(value);
return graph()->NewNode(common()->HeapConstant(value));
}

View File

@ -369,8 +369,7 @@ Reduction JSInliner::ReduceJSCallFunction(Node* node,
info.is_deoptimization_enabled()
? JSGlobalSpecialization::kDeoptimizationEnabled
: JSGlobalSpecialization::kNoFlags,
handle(info.global_object(), info.isolate()), info_->dependencies(),
local_zone_);
handle(info.global_object(), info.isolate()), info_->dependencies());
graph_reducer.AddReducer(&dead_code_elimination);
graph_reducer.AddReducer(&common_reducer);
graph_reducer.AddReducer(&global_specialization);

View File

@ -518,7 +518,7 @@ struct NativeContextSpecializationPhase {
? JSGlobalSpecialization::kDeoptimizationEnabled
: JSGlobalSpecialization::kNoFlags,
handle(data->info()->global_object(), data->isolate()),
data->info()->dependencies(), temp_zone);
data->info()->dependencies());
AddReducer(data, &graph_reducer, &dead_code_elimination);
AddReducer(data, &graph_reducer, &common_reducer);
AddReducer(data, &graph_reducer, &global_specialization);

View File

@ -19,8 +19,6 @@ class Map;
// index it was originally generated from.
class FieldIndex final {
public:
FieldIndex() : bit_field_(0) {}
static FieldIndex ForPropertyIndex(Map* map,
int index,
bool is_double = false);

View File

@ -4905,7 +4905,6 @@ bool Map::IsJSGlobalProxyMap() {
bool Map::IsJSGlobalObjectMap() {
return instance_type() == JS_GLOBAL_OBJECT_TYPE;
}
bool Map::IsJSTypedArrayMap() { return instance_type() == JS_TYPED_ARRAY_TYPE; }
bool Map::IsGlobalObjectMap() {
const InstanceType type = instance_type();
return type == JS_GLOBAL_OBJECT_TYPE || type == JS_BUILTINS_OBJECT_TYPE;

View File

@ -5877,7 +5877,6 @@ class Map: public HeapObject {
inline bool IsJSProxyMap();
inline bool IsJSGlobalProxyMap();
inline bool IsJSGlobalObjectMap();
inline bool IsJSTypedArrayMap();
inline bool IsGlobalObjectMap();
inline bool CanOmitMapChecks();

View File

@ -90,6 +90,7 @@ function f3(one) {
for (var j = 0; j < 5; ++j) f3(1);
%OptimizeFunctionOnNextCall(f3);
f3(1);
assertTrue(%GetOptimizationStatus(f3) != 2);

View File

@ -88,6 +88,7 @@ function f3(one) {
for (var j = 0; j < 5; ++j) f3(1);
%OptimizeFunctionOnNextCall(f3);
f3(1);
assertTrue(%GetOptimizationStatus(f3) != 2);