Commit 2c3e6b41 authored by verwaest@chromium.org's avatar verwaest@chromium.org

Use PropertyAccessInfo to compute stores in crankshaft.

BUG=
R=dcarney@chromium.org

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19085 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 7de9fc0a
...@@ -4345,7 +4345,8 @@ HObjectAccess HObjectAccess::ForBackingStoreOffset(int offset, ...@@ -4345,7 +4345,8 @@ HObjectAccess HObjectAccess::ForBackingStoreOffset(int offset,
HObjectAccess HObjectAccess::ForField(Handle<Map> map, HObjectAccess HObjectAccess::ForField(Handle<Map> map,
LookupResult *lookup, Handle<String> name) { LookupResult* lookup,
Handle<String> name) {
ASSERT(lookup->IsField() || lookup->IsTransitionToField(*map)); ASSERT(lookup->IsField() || lookup->IsTransitionToField(*map));
int index; int index;
Representation representation; Representation representation;
......
...@@ -4928,80 +4928,6 @@ static bool CanInlinePropertyAccess(Type* type) { ...@@ -4928,80 +4928,6 @@ static bool CanInlinePropertyAccess(Type* type) {
} }
static void LookupInPrototypes(Handle<Map> map,
Handle<String> name,
LookupResult* lookup,
Zone* zone) {
while (map->prototype()->IsJSObject()) {
Handle<JSObject> holder(JSObject::cast(map->prototype()));
map = handle(holder->map());
if (!CanInlinePropertyAccess(IC::MapToType<Type>(map, zone))) break;
map->LookupDescriptor(*holder, *name, lookup);
if (lookup->IsFound()) return;
}
lookup->NotFound();
}
// Tries to find a JavaScript accessor of the given name in the prototype chain
// starting at the given map. Return true iff there is one, including the
// corresponding AccessorPair plus its holder (which could be null when the
// accessor is found directly in the given map).
static bool LookupAccessorPair(Handle<Map> map,
Handle<String> name,
Handle<AccessorPair>* accessors,
Handle<JSObject>* holder,
Zone* zone) {
Isolate* isolate = map->GetIsolate();
LookupResult lookup(isolate);
// Check for a JavaScript accessor directly in the map.
map->LookupDescriptor(NULL, *name, &lookup);
if (lookup.IsPropertyCallbacks()) {
Handle<Object> callback(lookup.GetValueFromMap(*map), isolate);
if (!callback->IsAccessorPair()) return false;
*accessors = Handle<AccessorPair>::cast(callback);
*holder = Handle<JSObject>();
return true;
}
// Everything else, e.g. a field, can't be an accessor call.
if (lookup.IsFound()) return false;
// Check for a JavaScript accessor somewhere in the proto chain.
LookupInPrototypes(map, name, &lookup, zone);
if (lookup.IsPropertyCallbacks()) {
Handle<Object> callback(lookup.GetValue(), isolate);
if (!callback->IsAccessorPair()) return false;
*accessors = Handle<AccessorPair>::cast(callback);
*holder = Handle<JSObject>(lookup.holder());
return true;
}
// We haven't found a JavaScript accessor anywhere.
return false;
}
static bool LookupSetter(Handle<Map> map,
Handle<String> name,
Handle<JSFunction>* setter,
Handle<JSObject>* holder,
Zone* zone) {
Handle<AccessorPair> accessors;
if (LookupAccessorPair(map, name, &accessors, holder, zone) &&
accessors->setter()->IsJSFunction()) {
Handle<JSFunction> func(JSFunction::cast(accessors->setter()));
CallOptimization call_optimization(func);
// TODO(dcarney): temporary hack unless crankshaft can handle api calls.
if (call_optimization.is_simple_api_call()) return false;
*setter = func;
return true;
}
return false;
}
// Determines whether the given array or object literal boilerplate satisfies // Determines whether the given array or object literal boilerplate satisfies
// all limits to be considered for fast deep-copying and computes the total // all limits to be considered for fast deep-copying and computes the total
// size of all objects that are part of the graph. // size of all objects that are part of the graph.
...@@ -5147,15 +5073,17 @@ void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { ...@@ -5147,15 +5073,17 @@ void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
// If we don't know the monomorphic type, do a generic store. // If we don't know the monomorphic type, do a generic store.
CHECK_ALIVE(store = BuildStoreNamedGeneric(literal, name, value)); CHECK_ALIVE(store = BuildStoreNamedGeneric(literal, name, value));
} else { } else {
#if DEBUG PropertyAccessInfo info(this, STORE, ToType(map), name);
Handle<JSFunction> setter; if (info.CanAccessMonomorphic()) {
Handle<JSObject> holder; HValue* checked_literal = BuildCheckMap(literal, map);
ASSERT(!LookupSetter(map, name, &setter, &holder, zone())); ASSERT(!info.lookup()->IsPropertyCallbacks());
#endif store = BuildStoreMonomorphic(
CHECK_ALIVE(store = BuildStoreNamedMonomorphic(literal, &info, checked_literal, value,
name, BailoutId::None(), BailoutId::None());
value, } else {
map)); CHECK_ALIVE(
store = BuildStoreNamedGeneric(literal, name, value));
}
} }
AddInstruction(store); AddInstruction(store);
if (store->HasObservableSideEffects()) { if (store->HasObservableSideEffects()) {
...@@ -5331,43 +5259,13 @@ HCheckMaps* HOptimizedGraphBuilder::AddCheckMap(HValue* object, ...@@ -5331,43 +5259,13 @@ HCheckMaps* HOptimizedGraphBuilder::AddCheckMap(HValue* object,
HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField( HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField(
PropertyAccessInfo* info,
HValue* checked_object, HValue* checked_object,
Handle<String> name, HValue* value) {
HValue* value, bool transition_to_field = info->lookup()->IsTransition();
Handle<Map> map, // TODO(verwaest): Move this logic into PropertyAccessInfo.
LookupResult* lookup) { HObjectAccess field_access = HObjectAccess::ForField(
ASSERT(lookup->IsFound()); info->map(), info->lookup(), info->name());
// If the property does not exist yet, we have to check that it wasn't made
// readonly or turned into a setter by some meanwhile modifications on the
// prototype chain.
if (!lookup->IsProperty() && map->prototype()->IsJSReceiver()) {
Object* proto = map->prototype();
// First check that the prototype chain isn't affected already.
LookupResult proto_result(isolate());
proto->Lookup(*name, &proto_result);
if (proto_result.IsProperty()) {
// If the inherited property could induce readonly-ness, bail out.
if (proto_result.IsReadOnly() || !proto_result.IsCacheable()) {
Bailout(kImproperObjectOnPrototypeChainForStore);
return NULL;
}
// We only need to check up to the preexisting property.
proto = proto_result.holder();
} else {
// Otherwise, find the top prototype.
while (proto->GetPrototype(isolate())->IsJSObject()) {
proto = proto->GetPrototype(isolate());
}
ASSERT(proto->GetPrototype(isolate())->IsNull());
}
ASSERT(proto->IsJSObject());
BuildCheckPrototypeMaps(
Handle<JSObject>(JSObject::cast(map->prototype())),
Handle<JSObject>(JSObject::cast(proto)));
}
HObjectAccess field_access = HObjectAccess::ForField(map, lookup, name);
bool transition_to_field = lookup->IsTransitionToField(*map);
HStoreNamedField *instr; HStoreNamedField *instr;
if (FLAG_track_double_fields && field_access.representation().IsDouble()) { if (FLAG_track_double_fields && field_access.representation().IsDouble()) {
...@@ -5408,11 +5306,8 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField( ...@@ -5408,11 +5306,8 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField(
} }
if (transition_to_field) { if (transition_to_field) {
Handle<Map> transition(lookup->GetTransitionMapFromMap(*map)); HConstant* transition_constant = Add<HConstant>(info->transition());
HConstant* transition_constant = Add<HConstant>(transition);
instr->SetTransition(transition_constant, top_info()); instr->SetTransition(transition_constant, top_info());
// TODO(fschneider): Record the new map type of the object in the IR to
// enable elimination of redundant checks after the transition store.
instr->SetGVNFlag(kChangesMaps); instr->SetGVNFlag(kChangesMaps);
} }
return instr; return instr;
...@@ -5422,7 +5317,13 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField( ...@@ -5422,7 +5317,13 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField(
HInstruction* HOptimizedGraphBuilder::BuildStoreNamedGeneric( HInstruction* HOptimizedGraphBuilder::BuildStoreNamedGeneric(
HValue* object, HValue* object,
Handle<String> name, Handle<String> name,
HValue* value) { HValue* value,
bool is_uninitialized) {
if (is_uninitialized) {
Add<HDeoptimize>("Insufficient type feedback for property assignment",
Deoptimizer::SOFT);
}
return New<HStoreNamedGeneric>( return New<HStoreNamedGeneric>(
object, object,
name, name,
...@@ -5431,46 +5332,7 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedGeneric( ...@@ -5431,46 +5332,7 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedGeneric(
} }
// Sets the lookup result and returns true if the load/store can be inlined. bool HOptimizedGraphBuilder::PropertyAccessInfo::IsCompatible(
static bool ComputeStoreField(Type* type,
Handle<String> name,
LookupResult* lookup,
bool lookup_transition = true) {
if (!CanInlinePropertyAccess(type) || !type->IsClass()) {
lookup->NotFound();
return false;
}
Handle<Map> map = type->AsClass();
ASSERT(!map->is_observed());
// If we directly find a field, the access can be inlined.
map->LookupDescriptor(NULL, *name, lookup);
if (lookup->IsField()) return true;
if (!lookup_transition) return false;
map->LookupTransition(NULL, *name, lookup);
return lookup->IsTransitionToField(*map) && map->unused_property_fields() > 0;
}
HInstruction* HOptimizedGraphBuilder::BuildStoreNamedMonomorphic(
HValue* object,
Handle<String> name,
HValue* value,
Handle<Map> map) {
// Handle a store to a known field.
LookupResult lookup(isolate());
if (ComputeStoreField(ToType(map), name, &lookup)) {
HCheckMaps* checked_object = AddCheckMap(object, map);
return BuildStoreNamedField(checked_object, name, value, map, &lookup);
}
// No luck, do a generic store.
return BuildStoreNamedGeneric(object, name, value);
}
bool HOptimizedGraphBuilder::PropertyAccessInfo::IsCompatibleForLoad(
PropertyAccessInfo* info) { PropertyAccessInfo* info) {
if (!CanInlinePropertyAccess(type_)) return false; if (!CanInlinePropertyAccess(type_)) return false;
...@@ -5510,7 +5372,11 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::IsCompatibleForLoad( ...@@ -5510,7 +5372,11 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::IsCompatibleForLoad(
if (!info->lookup_.IsField()) return false; if (!info->lookup_.IsField()) return false;
Representation r = access_.representation(); Representation r = access_.representation();
if (!info->access_.representation().IsCompatibleForLoad(r)) return false; if (IsLoad()) {
if (!info->access_.representation().IsCompatibleForLoad(r)) return false;
} else {
if (!info->access_.representation().IsCompatibleForStore(r)) return false;
}
if (info->access_.offset() != access_.offset()) return false; if (info->access_.offset() != access_.offset()) return false;
if (info->access_.IsInobject() != access_.IsInobject()) return false; if (info->access_.IsInobject() != access_.IsInobject()) return false;
info->GeneralizeRepresentation(r); info->GeneralizeRepresentation(r);
...@@ -5526,14 +5392,21 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupDescriptor() { ...@@ -5526,14 +5392,21 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupDescriptor() {
bool HOptimizedGraphBuilder::PropertyAccessInfo::LoadResult(Handle<Map> map) { bool HOptimizedGraphBuilder::PropertyAccessInfo::LoadResult(Handle<Map> map) {
if (!IsLoad() && lookup_.IsProperty() &&
(lookup_.IsReadOnly() || !lookup_.IsCacheable())) {
return false;
}
if (lookup_.IsField()) { if (lookup_.IsField()) {
access_ = HObjectAccess::ForField(map, &lookup_, name_); access_ = HObjectAccess::ForField(map, &lookup_, name_);
} else if (lookup_.IsPropertyCallbacks()) { } else if (lookup_.IsPropertyCallbacks()) {
Handle<Object> callback(lookup_.GetValueFromMap(*map), isolate()); Handle<Object> callback(lookup_.GetValueFromMap(*map), isolate());
if (!callback->IsAccessorPair()) return false; if (!callback->IsAccessorPair()) return false;
Object* getter = Handle<AccessorPair>::cast(callback)->getter(); Object* raw_accessor = IsLoad()
if (!getter->IsJSFunction()) return false; ? Handle<AccessorPair>::cast(callback)->getter()
Handle<JSFunction> accessor = handle(JSFunction::cast(getter)); : Handle<AccessorPair>::cast(callback)->setter();
if (!raw_accessor->IsJSFunction()) return false;
Handle<JSFunction> accessor = handle(JSFunction::cast(raw_accessor));
CallOptimization call_optimization(accessor); CallOptimization call_optimization(accessor);
// TODO(dcarney): temporary hack unless crankshaft can handle api calls. // TODO(dcarney): temporary hack unless crankshaft can handle api calls.
if (call_optimization.is_simple_api_call()) return false; if (call_optimization.is_simple_api_call()) return false;
...@@ -5567,19 +5440,38 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupInPrototypes() { ...@@ -5567,19 +5440,38 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupInPrototypes() {
} }
bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadMonomorphic() { bool HOptimizedGraphBuilder::PropertyAccessInfo::CanAccessMonomorphic() {
if (!CanInlinePropertyAccess(type_)) return false; if (!CanInlinePropertyAccess(type_)) return false;
if (IsJSObjectFieldAccessor()) return true; if (IsJSObjectFieldAccessor()) {
// We should never have gathered typefeedback for JSObjectFieldAccessor
// stores.
ASSERT(IsLoad());
return true;
}
if (!LookupDescriptor()) return false; if (!LookupDescriptor()) return false;
if (lookup_.IsFound()) return true; if (lookup_.IsFound()) {
return LookupInPrototypes(); if (IsLoad()) return true;
return !lookup_.IsReadOnly() && lookup_.IsCacheable();
}
if (!LookupInPrototypes()) return false;
if (IsLoad()) return true;
if (lookup_.IsPropertyCallbacks()) return true;
Handle<Map> map = this->map();
map->LookupTransition(NULL, *name_, &lookup_);
if (lookup_.IsTransitionToField(*map) && map->unused_property_fields() > 0) {
transition_ = handle(lookup_.GetTransitionMapFromMap(*map));
return true;
}
return false;
} }
bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadAsMonomorphic( bool HOptimizedGraphBuilder::PropertyAccessInfo::CanAccessAsMonomorphic(
SmallMapList* types) { SmallMapList* types) {
ASSERT(type_->Is(ToType(types->first()))); ASSERT(type_->Is(ToType(types->first())));
if (!CanLoadMonomorphic()) return false; if (!CanAccessMonomorphic()) return false;
STATIC_ASSERT(kMaxLoadPolymorphism == kMaxStorePolymorphism);
if (types->length() > kMaxLoadPolymorphism) return false; if (types->length() > kMaxLoadPolymorphism) return false;
if (IsArrayLength()) { if (IsArrayLength()) {
...@@ -5597,7 +5489,8 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadAsMonomorphic( ...@@ -5597,7 +5489,8 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadAsMonomorphic(
HObjectAccess access = HObjectAccess::ForMap(); // bogus default HObjectAccess access = HObjectAccess::ForMap(); // bogus default
if (GetJSObjectFieldAccess(&access)) { if (GetJSObjectFieldAccess(&access)) {
for (int i = 1; i < types->length(); ++i) { for (int i = 1; i < types->length(); ++i) {
PropertyAccessInfo test_info(builder_, ToType(types->at(i)), name_); PropertyAccessInfo test_info(
builder_, access_type_, ToType(types->at(i)), name_);
HObjectAccess test_access = HObjectAccess::ForMap(); // bogus default HObjectAccess test_access = HObjectAccess::ForMap(); // bogus default
if (!test_info.GetJSObjectFieldAccess(&test_access)) return false; if (!test_info.GetJSObjectFieldAccess(&test_access)) return false;
if (!access.Equals(test_access)) return false; if (!access.Equals(test_access)) return false;
...@@ -5610,9 +5503,14 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadAsMonomorphic( ...@@ -5610,9 +5503,14 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadAsMonomorphic(
// instruction. // instruction.
if (type_->Is(Type::Number())) return false; if (type_->Is(Type::Number())) return false;
// Multiple maps cannot transition to the same target map.
ASSERT(!IsLoad() || !lookup_.IsTransition());
if (lookup_.IsTransition() && types->length() > 1) return false;
for (int i = 1; i < types->length(); ++i) { for (int i = 1; i < types->length(); ++i) {
PropertyAccessInfo test_info(builder_, ToType(types->at(i)), name_); PropertyAccessInfo test_info(
if (!test_info.IsCompatibleForLoad(this)) return false; builder_, access_type_, ToType(types->at(i)), name_);
if (!test_info.IsCompatible(this)) return false;
} }
return true; return true;
...@@ -5674,6 +5572,49 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadMonomorphic( ...@@ -5674,6 +5572,49 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadMonomorphic(
} }
HInstruction* HOptimizedGraphBuilder::BuildStoreMonomorphic(
PropertyAccessInfo* info,
HValue* checked_object,
HValue* value,
BailoutId ast_id,
BailoutId return_id,
bool can_inline_accessor) {
ASSERT(!info->IsJSObjectFieldAccessor());
if (info->has_holder()) {
Handle<JSObject> prototype(JSObject::cast(info->map()->prototype()));
BuildCheckPrototypeMaps(prototype, info->holder());
}
if (info->lookup()->IsPropertyCallbacks()) {
if (NeedsWrappingFor(info->type(), info->accessor())) {
HValue* function = Add<HConstant>(info->accessor());
Add<HPushArgument>(checked_object);
Add<HPushArgument>(value);
return New<HCallFunction>(function, 2, WRAP_AND_CALL);
} else {
Push(checked_object);
Push(value);
if (FLAG_inline_accessors &&
can_inline_accessor &&
TryInlineSetter(info->accessor(), ast_id, return_id, value)) {
return NULL;
}
PushArgumentsFromEnvironment(2);
return BuildCallConstantFunction(info->accessor(), 2);
}
}
if (info->lookup()->IsConstant()) {
// Check whether we are trying to store the same constant.
return New<HCheckValue>(value, Handle<JSFunction>::cast(info->constant()));
}
ASSERT(info->lookup()->IsField() || info->lookup()->IsTransition());
return BuildStoreNamedField(info, checked_object, value);
}
void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField( void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField(
BailoutId ast_id, BailoutId ast_id,
BailoutId return_id, BailoutId return_id,
...@@ -5688,12 +5629,12 @@ void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField( ...@@ -5688,12 +5629,12 @@ void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField(
bool handle_smi = false; bool handle_smi = false;
for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) { for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) {
PropertyAccessInfo info(this, ToType(types->at(i)), name); PropertyAccessInfo info(this, LOAD, ToType(types->at(i)), name);
if (info.type()->Is(Type::String())) { if (info.type()->Is(Type::String())) {
if (handled_string) continue; if (handled_string) continue;
handled_string = true; handled_string = true;
} }
if (info.CanLoadMonomorphic()) { if (info.CanAccessMonomorphic()) {
count++; count++;
if (info.type()->Is(Type::Number())) { if (info.type()->Is(Type::Number())) {
handle_smi = true; handle_smi = true;
...@@ -5707,12 +5648,12 @@ void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField( ...@@ -5707,12 +5648,12 @@ void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField(
handled_string = false; handled_string = false;
for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) { for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) {
PropertyAccessInfo info(this, ToType(types->at(i)), name); PropertyAccessInfo info(this, LOAD, ToType(types->at(i)), name);
if (info.type()->Is(Type::String())) { if (info.type()->Is(Type::String())) {
if (handled_string) continue; if (handled_string) continue;
handled_string = true; handled_string = true;
} }
if (!info.CanLoadMonomorphic()) continue; if (!info.CanAccessMonomorphic()) continue;
if (count == 0) { if (count == 0) {
join = graph()->CreateBasicBlock(); join = graph()->CreateBasicBlock();
...@@ -5801,84 +5742,19 @@ void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField( ...@@ -5801,84 +5742,19 @@ void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField(
} }
bool HOptimizedGraphBuilder::TryStorePolymorphicAsMonomorphic(
BailoutId assignment_id,
HValue* object,
HValue* value,
SmallMapList* types,
Handle<String> name) {
// Use monomorphic store if property lookup results in the same field index
// for all maps. Requires special map check on the set of all handled maps.
if (types->length() > kMaxStorePolymorphism) return false;
LookupResult lookup(isolate());
int count;
Representation representation = Representation::None();
HObjectAccess access = HObjectAccess::ForMap(); // initial value unused.
for (count = 0; count < types->length(); ++count) {
Handle<Map> map = types->at(count);
// Pass false to ignore transitions.
if (!ComputeStoreField(ToType(map), name, &lookup, false)) break;
ASSERT(!map->is_observed());
HObjectAccess new_access = HObjectAccess::ForField(map, &lookup, name);
Representation new_representation = new_access.representation();
if (count == 0) {
// First time through the loop; set access and representation.
access = new_access;
representation = new_representation;
} else if (!representation.IsCompatibleForStore(new_representation)) {
// Representations did not match.
break;
} else if (access.offset() != new_access.offset()) {
// Offsets did not match.
break;
} else if (access.IsInobject() != new_access.IsInobject()) {
// In-objectness did not match.
break;
}
}
if (count != types->length()) return false;
// Everything matched; can use monomorphic store.
BuildCheckHeapObject(object);
HCheckMaps* checked_object = Add<HCheckMaps>(object, types);
HInstruction* store;
CHECK_ALIVE_OR_RETURN(
store = BuildStoreNamedField(
checked_object, name, value, types->at(count - 1), &lookup),
true);
if (!ast_context()->IsEffect()) Push(value);
AddInstruction(store);
Add<HSimulate>(assignment_id);
if (!ast_context()->IsEffect()) Drop(1);
ast_context()->ReturnValue(value);
return true;
}
void HOptimizedGraphBuilder::HandlePolymorphicStoreNamedField( void HOptimizedGraphBuilder::HandlePolymorphicStoreNamedField(
BailoutId assignment_id, BailoutId assignment_id,
BailoutId return_id,
HValue* object, HValue* object,
HValue* value, HValue* value,
SmallMapList* types, SmallMapList* types,
Handle<String> name) { Handle<String> name) {
if (TryStorePolymorphicAsMonomorphic(
assignment_id, object, value, types, name)) {
return;
}
// TODO(ager): We should recognize when the prototype chains for different
// maps are identical. In that case we can avoid repeatedly generating the
// same prototype map checks.
int count = 0; int count = 0;
HBasicBlock* join = NULL; HBasicBlock* join = NULL;
// TODO(verwaest): Unify with polymorphic load handling.
for (int i = 0; i < types->length() && count < kMaxStorePolymorphism; ++i) { for (int i = 0; i < types->length() && count < kMaxStorePolymorphism; ++i) {
Handle<Map> map = types->at(i); PropertyAccessInfo info(this, STORE, ToType(types->at(i)), name);
LookupResult lookup(isolate()); if (info.CanAccessMonomorphic()) {
if (ComputeStoreField(ToType(map), name, &lookup)) {
if (count == 0) { if (count == 0) {
BuildCheckHeapObject(object); BuildCheckHeapObject(object);
join = graph()->CreateBasicBlock(); join = graph()->CreateBasicBlock();
...@@ -5886,18 +5762,26 @@ void HOptimizedGraphBuilder::HandlePolymorphicStoreNamedField( ...@@ -5886,18 +5762,26 @@ void HOptimizedGraphBuilder::HandlePolymorphicStoreNamedField(
++count; ++count;
HBasicBlock* if_true = graph()->CreateBasicBlock(); HBasicBlock* if_true = graph()->CreateBasicBlock();
HBasicBlock* if_false = graph()->CreateBasicBlock(); HBasicBlock* if_false = graph()->CreateBasicBlock();
HCompareMap* compare = New<HCompareMap>(object, map, if_true, if_false); HCompareMap* compare = New<HCompareMap>(
object, info.map(), if_true, if_false);
FinishCurrentBlock(compare); FinishCurrentBlock(compare);
set_current_block(if_true); set_current_block(if_true);
HInstruction* instr;
CHECK_ALIVE(instr = BuildStoreNamedField(
compare, name, value, map, &lookup));
// Goto will add the HSimulate for the store.
AddInstruction(instr);
if (!ast_context()->IsEffect()) Push(value);
Goto(join);
HInstruction* store;
store = BuildStoreMonomorphic(
&info, compare, value, assignment_id, return_id,
FLAG_polymorphic_inlining);
if (store == NULL) {
if (HasStackOverflow()) return;
} else {
ASSERT(!store->IsLinked());
AddInstruction(store);
if (!ast_context()->IsEffect()) Push(value);
}
if (current_block() != NULL) Goto(join);
set_current_block(if_false); set_current_block(if_false);
} }
} }
...@@ -5959,15 +5843,22 @@ static bool ComputeReceiverTypes(Expression* expr, ...@@ -5959,15 +5843,22 @@ static bool ComputeReceiverTypes(Expression* expr,
} }
static bool AreStringTypes(SmallMapList* types) {
for (int i = 0; i < types->length(); i++) {
if (types->at(i)->instance_type() >= FIRST_NONSTRING_TYPE) return false;
}
return true;
}
void HOptimizedGraphBuilder::BuildStore(Expression* expr, void HOptimizedGraphBuilder::BuildStore(Expression* expr,
Property* prop, Property* prop,
BailoutId ast_id, BailoutId ast_id,
BailoutId return_id, BailoutId return_id,
bool is_uninitialized) { bool is_uninitialized) {
HValue* value = environment()->ExpressionStackAt(0);
if (!prop->key()->IsPropertyName()) { if (!prop->key()->IsPropertyName()) {
// Keyed store. // Keyed store.
HValue* value = environment()->ExpressionStackAt(0);
HValue* key = environment()->ExpressionStackAt(1); HValue* key = environment()->ExpressionStackAt(1);
HValue* object = environment()->ExpressionStackAt(2); HValue* object = environment()->ExpressionStackAt(2);
bool has_side_effects = false; bool has_side_effects = false;
...@@ -5981,12 +5872,8 @@ void HOptimizedGraphBuilder::BuildStore(Expression* expr, ...@@ -5981,12 +5872,8 @@ void HOptimizedGraphBuilder::BuildStore(Expression* expr,
} }
// Named store. // Named store.
HValue* object = environment()->ExpressionStackAt(1); HValue* value = Pop();
HValue* object = Pop();
if (is_uninitialized) {
Add<HDeoptimize>("Insufficient type feedback for property assignment",
Deoptimizer::SOFT);
}
Literal* key = prop->key()->AsLiteral(); Literal* key = prop->key()->AsLiteral();
Handle<String> name = Handle<String>::cast(key->value()); Handle<String> name = Handle<String>::cast(key->value());
...@@ -5995,42 +5882,30 @@ void HOptimizedGraphBuilder::BuildStore(Expression* expr, ...@@ -5995,42 +5882,30 @@ void HOptimizedGraphBuilder::BuildStore(Expression* expr,
HInstruction* instr = NULL; HInstruction* instr = NULL;
SmallMapList* types; SmallMapList* types;
bool monomorphic = ComputeReceiverTypes(expr, object, &types, zone()); ComputeReceiverTypes(expr, object, &types, zone());
if (monomorphic) { if (types->length() > 0) {
Handle<Map> map = types->first(); PropertyAccessInfo info(this, STORE, ToType(types->first()), name);
Handle<JSFunction> setter; if (!info.CanAccessAsMonomorphic(types)) {
Handle<JSObject> holder; return HandlePolymorphicStoreNamedField(
if (LookupSetter(map, name, &setter, &holder, zone())) { ast_id, return_id, object, value, types, name);
AddCheckMap(object, map); }
AddCheckPrototypeMaps(holder, map);
bool needs_wrapping = NeedsWrappingFor(ToType(map), setter); ASSERT(!info.type()->Is(Type::Number()));
bool try_inline = FLAG_inline_accessors && !needs_wrapping; BuildCheckHeapObject(object);
if (try_inline && TryInlineSetter(setter, ast_id, return_id, value)) { HValue* checked_object;
return; if (AreStringTypes(types)) {
} checked_object = Add<HCheckInstanceType>(
Drop(2); object, HCheckInstanceType::IS_STRING);
Add<HPushArgument>(object);
Add<HPushArgument>(value);
if (needs_wrapping) {
HValue* function = Add<HConstant>(setter);
instr = New<HCallFunction>(function, 2, WRAP_AND_CALL);
} else {
instr = BuildCallConstantFunction(setter, 2);
}
} else { } else {
Drop(2); checked_object = Add<HCheckMaps>(object, types);
CHECK_ALIVE(instr = BuildStoreNamedMonomorphic(object, }
name, instr = BuildStoreMonomorphic(
value, &info, checked_object, value, ast_id, return_id);
map)); if (instr == NULL) return;
} ASSERT(!instr->IsLinked());
} else if (types != NULL && types->length() > 1) {
Drop(2);
return HandlePolymorphicStoreNamedField(ast_id, object, value, types, name);
} else { } else {
Drop(2); instr = BuildStoreNamedGeneric(object, name, value, is_uninitialized);
instr = BuildStoreNamedGeneric(object, name, value);
} }
if (!ast_context()->IsEffect()) Push(value); if (!ast_context()->IsEffect()) Push(value);
...@@ -6838,14 +6713,6 @@ void HOptimizedGraphBuilder::PushLoad(Property* expr, ...@@ -6838,14 +6713,6 @@ void HOptimizedGraphBuilder::PushLoad(Property* expr,
} }
static bool AreStringTypes(SmallMapList* types) {
for (int i = 0; i < types->length(); i++) {
if (types->at(i)->instance_type() >= FIRST_NONSTRING_TYPE) return false;
}
return true;
}
void HOptimizedGraphBuilder::BuildLoad(Property* expr, void HOptimizedGraphBuilder::BuildLoad(Property* expr,
BailoutId ast_id) { BailoutId ast_id) {
HInstruction* instr = NULL; HInstruction* instr = NULL;
...@@ -6870,8 +6737,8 @@ void HOptimizedGraphBuilder::BuildLoad(Property* expr, ...@@ -6870,8 +6737,8 @@ void HOptimizedGraphBuilder::BuildLoad(Property* expr,
ASSERT(types != NULL); ASSERT(types != NULL);
if (types->length() > 0) { if (types->length() > 0) {
PropertyAccessInfo info(this, ToType(types->first()), name); PropertyAccessInfo info(this, LOAD, ToType(types->first()), name);
if (!info.CanLoadAsMonomorphic(types)) { if (!info.CanAccessAsMonomorphic(types)) {
return HandlePolymorphicLoadNamedField( return HandlePolymorphicLoadNamedField(
ast_id, expr->LoadId(), object, types, name); ast_id, expr->LoadId(), object, types, name);
} }
...@@ -7078,8 +6945,8 @@ void HOptimizedGraphBuilder::HandlePolymorphicCallNamed( ...@@ -7078,8 +6945,8 @@ void HOptimizedGraphBuilder::HandlePolymorphicCallNamed(
for (int i = 0; for (int i = 0;
i < types->length() && ordered_functions < kMaxCallPolymorphism; i < types->length() && ordered_functions < kMaxCallPolymorphism;
++i) { ++i) {
PropertyAccessInfo info(this, ToType(types->at(i)), name); PropertyAccessInfo info(this, LOAD, ToType(types->at(i)), name);
if (info.CanLoadMonomorphic() && if (info.CanAccessMonomorphic() &&
info.lookup()->IsConstant() && info.lookup()->IsConstant() &&
info.constant()->IsJSFunction()) { info.constant()->IsJSFunction()) {
if (info.type()->Is(Type::String())) { if (info.type()->Is(Type::String())) {
...@@ -7108,13 +6975,13 @@ void HOptimizedGraphBuilder::HandlePolymorphicCallNamed( ...@@ -7108,13 +6975,13 @@ void HOptimizedGraphBuilder::HandlePolymorphicCallNamed(
for (int fn = 0; fn < ordered_functions; ++fn) { for (int fn = 0; fn < ordered_functions; ++fn) {
int i = order[fn].index(); int i = order[fn].index();
PropertyAccessInfo info(this, ToType(types->at(i)), name); PropertyAccessInfo info(this, LOAD, ToType(types->at(i)), name);
if (info.type()->Is(Type::String())) { if (info.type()->Is(Type::String())) {
if (handled_string) continue; if (handled_string) continue;
handled_string = true; handled_string = true;
} }
// Reloads the target. // Reloads the target.
info.CanLoadMonomorphic(); info.CanAccessMonomorphic();
Handle<JSFunction> target = Handle<JSFunction>::cast(info.constant()); Handle<JSFunction> target = Handle<JSFunction>::cast(info.constant());
expr->set_target(target); expr->set_target(target);
...@@ -8101,8 +7968,8 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) { ...@@ -8101,8 +7968,8 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
if (prop->key()->IsPropertyName() && types->length() > 0) { if (prop->key()->IsPropertyName() && types->length() > 0) {
Handle<String> name = prop->key()->AsLiteral()->AsPropertyName(); Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
PropertyAccessInfo info(this, ToType(types->first()), name); PropertyAccessInfo info(this, LOAD, ToType(types->first()), name);
if (!info.CanLoadAsMonomorphic(types)) { if (!info.CanAccessAsMonomorphic(types)) {
HandlePolymorphicCallNamed(expr, receiver, types, name); HandlePolymorphicCallNamed(expr, receiver, types, name);
return; return;
} }
......
...@@ -2264,13 +2264,16 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { ...@@ -2264,13 +2264,16 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
void VisitDataViewInitialize(CallRuntime* expr); void VisitDataViewInitialize(CallRuntime* expr);
enum PropertyAccessType { LOAD, STORE };
class PropertyAccessInfo { class PropertyAccessInfo {
public: public:
PropertyAccessInfo(HOptimizedGraphBuilder* builder, PropertyAccessInfo(HOptimizedGraphBuilder* builder,
PropertyAccessType access_type,
Type* type, Type* type,
Handle<String> name) Handle<String> name)
: lookup_(builder->isolate()), : lookup_(builder->isolate()),
builder_(builder), builder_(builder),
access_type_(access_type),
type_(type), type_(type),
name_(name), name_(name),
access_(HObjectAccess::ForMap()) { } access_(HObjectAccess::ForMap()) { }
...@@ -2278,7 +2281,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { ...@@ -2278,7 +2281,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
// Checkes whether this PropertyAccessInfo can be handled as a monomorphic // Checkes whether this PropertyAccessInfo can be handled as a monomorphic
// load named. It additionally fills in the fields necessary to generate the // load named. It additionally fills in the fields necessary to generate the
// lookup code. // lookup code.
bool CanLoadMonomorphic(); bool CanAccessMonomorphic();
// Checks whether all types behave uniform when loading name. If all maps // Checks whether all types behave uniform when loading name. If all maps
// behave the same, a single monomorphic load instruction can be emitted, // behave the same, a single monomorphic load instruction can be emitted,
...@@ -2286,7 +2289,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { ...@@ -2286,7 +2289,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
// an instance of any of the types. // an instance of any of the types.
// This method skips the first type in types, assuming that this // This method skips the first type in types, assuming that this
// PropertyAccessInfo is built for types->first(). // PropertyAccessInfo is built for types->first().
bool CanLoadAsMonomorphic(SmallMapList* types); bool CanAccessAsMonomorphic(SmallMapList* types);
Handle<Map> map() { Handle<Map> map() {
if (type_->Is(Type::Number())) { if (type_->Is(Type::Number())) {
...@@ -2337,6 +2340,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { ...@@ -2337,6 +2340,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
Handle<JSObject> holder() { return holder_; } Handle<JSObject> holder() { return holder_; }
Handle<JSFunction> accessor() { return accessor_; } Handle<JSFunction> accessor() { return accessor_; }
Handle<Object> constant() { return constant_; } Handle<Object> constant() { return constant_; }
Handle<Object> transition() { return transition_; }
HObjectAccess access() { return access_; } HObjectAccess access() { return access_; }
private: private:
...@@ -2352,7 +2356,8 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { ...@@ -2352,7 +2356,8 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
bool LoadResult(Handle<Map> map); bool LoadResult(Handle<Map> map);
bool LookupDescriptor(); bool LookupDescriptor();
bool LookupInPrototypes(); bool LookupInPrototypes();
bool IsCompatibleForLoad(PropertyAccessInfo* other); bool IsCompatible(PropertyAccessInfo* other);
bool IsLoad() const { return access_type_ == LOAD; }
void GeneralizeRepresentation(Representation r) { void GeneralizeRepresentation(Representation r) {
access_ = access_.WithRepresentation( access_ = access_.WithRepresentation(
...@@ -2361,11 +2366,13 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { ...@@ -2361,11 +2366,13 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
LookupResult lookup_; LookupResult lookup_;
HOptimizedGraphBuilder* builder_; HOptimizedGraphBuilder* builder_;
PropertyAccessType access_type_;
Type* type_; Type* type_;
Handle<String> name_; Handle<String> name_;
Handle<JSObject> holder_; Handle<JSObject> holder_;
Handle<JSFunction> accessor_; Handle<JSFunction> accessor_;
Handle<Object> constant_; Handle<Object> constant_;
Handle<Map> transition_;
HObjectAccess access_; HObjectAccess access_;
}; };
...@@ -2376,12 +2383,15 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { ...@@ -2376,12 +2383,15 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
BailoutId return_id, BailoutId return_id,
bool can_inline_accessor = true); bool can_inline_accessor = true);
HInstruction* BuildStoreMonomorphic(PropertyAccessInfo* info,
HValue* checked_object,
HValue* value,
BailoutId ast_id,
BailoutId return_id,
bool can_inline_accessor = true);
void HandlePolymorphicStoreNamedField(BailoutId assignment_id, void HandlePolymorphicStoreNamedField(BailoutId assignment_id,
HValue* object, BailoutId return_id,
HValue* value,
SmallMapList* types,
Handle<String> name);
bool TryStorePolymorphicAsMonomorphic(BailoutId assignment_id,
HValue* object, HValue* object,
HValue* value, HValue* value,
SmallMapList* types, SmallMapList* types,
...@@ -2472,18 +2482,13 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { ...@@ -2472,18 +2482,13 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
BailoutId return_id, BailoutId return_id,
bool is_uninitialized = false); bool is_uninitialized = false);
HInstruction* BuildStoreNamedField(HValue* object, HInstruction* BuildStoreNamedField(PropertyAccessInfo* info,
Handle<String> name, HValue* checked_object,
HValue* value, HValue* value);
Handle<Map> map,
LookupResult* lookup);
HInstruction* BuildStoreNamedGeneric(HValue* object, HInstruction* BuildStoreNamedGeneric(HValue* object,
Handle<String> name, Handle<String> name,
HValue* value); HValue* value,
HInstruction* BuildStoreNamedMonomorphic(HValue* object, bool is_uninitialized = false);
Handle<String> name,
HValue* value,
Handle<Map> map);
HInstruction* BuildStoreKeyedGeneric(HValue* object, HInstruction* BuildStoreKeyedGeneric(HValue* object,
HValue* key, HValue* key,
HValue* value); HValue* value);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment