Unify load handling into CanLoad and Load.

R=titzer@chromium.org

Review URL: https://chromiumcodereview.appspot.com/24088003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16792 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
verwaest@chromium.org 2013-09-18 14:56:18 +00:00
parent ad69e19d67
commit 4da303d937
2 changed files with 257 additions and 262 deletions

View File

@ -4065,20 +4065,6 @@ static bool LookupAccessorPair(Handle<Map> map,
}
static bool LookupGetter(Handle<Map> map,
Handle<String> name,
Handle<JSFunction>* getter,
Handle<JSObject>* holder) {
Handle<AccessorPair> accessors;
if (LookupAccessorPair(map, name, &accessors, holder) &&
accessors->getter()->IsJSFunction()) {
*getter = Handle<JSFunction>(JSFunction::cast(accessors->getter()));
return true;
}
return false;
}
static bool LookupSetter(Handle<Map> map,
Handle<String> name,
Handle<JSFunction>* setter,
@ -4546,140 +4532,179 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedMonomorphic(
}
static bool CanLoadPropertyFromPrototype(Handle<Map> map,
Handle<Name> name,
LookupResult* lookup) {
if (!CanInlinePropertyAccess(*map)) return false;
map->LookupDescriptor(NULL, *name, lookup);
if (lookup->IsFound()) return false;
bool HOptimizedGraphBuilder::PropertyAccessInfo::IsCompatibleForLoad(
PropertyAccessInfo* info) {
if (!CanInlinePropertyAccess(*map_)) return false;
if (!LookupDescriptor()) return false;
if (!lookup_.IsFound()) {
return (!info->lookup_.IsFound() || !info->holder_.is_null()) &&
map_->prototype() == info->map_->prototype();
}
if (lookup_.IsPropertyCallbacks()) {
return accessor_.is_identical_to(info->accessor_);
}
if (lookup_.IsConstant()) {
return constant_.is_identical_to(info->constant_);
}
ASSERT(lookup_.IsField());
if (!info->lookup_.IsField()) return false;
Representation r = access_.representation();
if (!info->access_.representation().IsCompatibleForLoad(r)) return false;
if (info->access_.offset() != access_.offset()) return false;
if (info->access_.IsInobject() != access_.IsInobject()) return false;
info->GeneralizeRepresentation(r);
return true;
}
HInstruction* HOptimizedGraphBuilder::TryLoadPolymorphicAsMonomorphic(
HValue* object,
SmallMapList* types,
Handle<String> name) {
// Use monomorphic load 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() > kMaxLoadPolymorphism) return NULL;
LookupResult lookup(isolate());
int count;
HObjectAccess access = HObjectAccess::ForMap(); // initial value unused.
for (count = 0; count < types->length(); ++count) {
Handle<Map> map = types->at(count);
if (!ComputeLoadStoreField(map, name, &lookup, false)) break;
HObjectAccess new_access = HObjectAccess::ForField(map, &lookup, name);
if (count == 0) {
// First time through the loop; set access and representation.
access = new_access;
} else if (!access.representation().IsCompatibleForLoad(
new_access.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;
}
access = access.WithRepresentation(
access.representation().generalize(new_access.representation()));
}
if (count == types->length()) {
// Everything matched; can use monomorphic load.
BuildCheckHeapObject(object);
HCheckMaps* checked_object = Add<HCheckMaps>(object, types);
return BuildLoadNamedField(checked_object, access);
}
if (count != 0) return NULL;
// Second chance: the property is on the prototype and all maps have the
// same prototype.
Handle<Map> map(types->at(0));
if (!CanLoadPropertyFromPrototype(map, name, &lookup)) return NULL;
Handle<Object> prototype(map->prototype(), isolate());
for (count = 1; count < types->length(); ++count) {
Handle<Map> test_map(types->at(count));
if (!CanLoadPropertyFromPrototype(test_map, name, &lookup)) return NULL;
if (test_map->prototype() != *prototype) return NULL;
}
LookupInPrototypes(map, name, &lookup);
if (!lookup.IsField()) return NULL;
BuildCheckHeapObject(object);
Add<HCheckMaps>(object, types);
Handle<JSObject> holder(lookup.holder());
Handle<Map> holder_map(holder->map());
HValue* checked_holder = BuildCheckPrototypeMaps(
Handle<JSObject>::cast(prototype), holder);
return BuildLoadNamedField(checked_holder,
HObjectAccess::ForField(holder_map, &lookup, name));
bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupDescriptor() {
map_->LookupDescriptor(NULL, *name_, &lookup_);
return LoadResult(map_);
}
// Returns true if an instance of this map can never find a property with this
// name in its prototype chain. This means all prototypes up to the top are
// fast and don't have the name in them. It would be good if we could optimize
// polymorphic loads where the property is sometimes found in the prototype
// chain.
static bool PrototypeChainCanNeverResolve(
Handle<Map> map, Handle<String> name) {
Isolate* isolate = map->GetIsolate();
Object* current = map->prototype();
while (current != isolate->heap()->null_value()) {
if (current->IsJSGlobalProxy() ||
current->IsGlobalObject() ||
!current->IsJSObject() ||
!CanInlinePropertyAccess(JSObject::cast(current)->map()) ||
JSObject::cast(current)->IsAccessCheckNeeded()) {
bool HOptimizedGraphBuilder::PropertyAccessInfo::LoadResult(Handle<Map> map) {
if (lookup_.IsField()) {
access_ = HObjectAccess::ForField(map, &lookup_, name_);
} else if (lookup_.IsPropertyCallbacks()) {
Handle<Object> callback(lookup_.GetValueFromMap(*map), isolate());
if (!callback->IsAccessorPair()) return false;
Object* getter = Handle<AccessorPair>::cast(callback)->getter();
if (!getter->IsJSFunction()) return false;
accessor_ = handle(JSFunction::cast(getter));
} else if (lookup_.IsConstant()) {
constant_ = handle(lookup_.GetConstantFromMap(*map), isolate());
}
return true;
}
bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupInPrototypes() {
Handle<Map> map = map_;
while (map->prototype()->IsJSObject()) {
holder_ = handle(JSObject::cast(map->prototype()));
map = Handle<Map>(holder_->map());
if (!CanInlinePropertyAccess(*map)) {
lookup_.NotFound();
return false;
}
LookupResult lookup(isolate);
Map* map = JSObject::cast(current)->map();
map->LookupDescriptor(NULL, *name, &lookup);
if (lookup.IsFound()) return false;
if (!lookup.IsCacheable()) return false;
current = JSObject::cast(current)->GetPrototype();
map->LookupDescriptor(*holder_, *name_, &lookup_);
if (lookup_.IsFound()) return LoadResult(map);
}
lookup_.NotFound();
return true;
}
bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadMonomorphic() {
if (!CanInlinePropertyAccess(*map_)) return IsStringLength();
if (IsArrayLength()) return true;
if (!LookupDescriptor()) return false;
if (lookup_.IsFound()) return true;
return LookupInPrototypes();
}
bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadAsMonomorphic(
SmallMapList* types) {
ASSERT(map_.is_identical_to(types->first()));
if (!CanLoadMonomorphic()) return false;
if (types->length() > kMaxLoadPolymorphism) return false;
if (IsStringLength()) {
for (int i = 1; i < types->length(); ++i) {
if (types->at(i)->instance_type() >= FIRST_NONSTRING_TYPE) return false;
}
return true;
}
if (IsArrayLength()) {
bool is_fast = IsFastElementsKind(map_->elements_kind());
for (int i = 1; i < types->length(); ++i) {
Handle<Map> test_map = types->at(i);
if (test_map->instance_type() != JS_ARRAY_TYPE) return false;
if (IsFastElementsKind(test_map->elements_kind()) != is_fast) {
return false;
}
}
return true;
}
for (int i = 1; i < types->length(); ++i) {
PropertyAccessInfo test_info(isolate(), types->at(i), name_);
if (!test_info.IsCompatibleForLoad(this)) return false;
}
return true;
}
HInstruction* HOptimizedGraphBuilder::BuildLoadMonomorphic(
PropertyAccessInfo* info,
HValue* object,
HInstruction* checked_object,
BailoutId ast_id,
BailoutId return_id,
bool can_inline_accessor) {
if (info->IsStringLength()) {
return New<HLoadNamedField>(
checked_object, HObjectAccess::ForStringLength());
}
if (info->IsArrayLength()) {
return New<HLoadNamedField>(
checked_object, HObjectAccess::ForArrayLength(
info->map()->elements_kind()));
}
HValue* checked_holder = checked_object;
if (info->has_holder()) {
Handle<JSObject> prototype(JSObject::cast(info->map()->prototype()));
checked_holder = BuildCheckPrototypeMaps(prototype, info->holder());
}
if (!info->lookup()->IsFound()) return graph()->GetConstantUndefined();
if (info->lookup()->IsField()) {
return BuildLoadNamedField(checked_holder, info->access());
}
if (info->lookup()->IsPropertyCallbacks()) {
Push(checked_object);
if (FLAG_inline_accessors &&
can_inline_accessor &&
TryInlineGetter(info->accessor(), ast_id, return_id)) {
return NULL;
}
Add<HPushArgument>(Pop());
return new(zone()) HCallConstantFunction(info->accessor(), 1);
}
ASSERT(info->lookup()->IsConstant());
return New<HConstant>(info->constant());
}
void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField(
int position,
BailoutId ast_id,
BailoutId return_id,
HValue* object,
SmallMapList* types,
Handle<String> name) {
HInstruction* instr = TryLoadPolymorphicAsMonomorphic(object, types, name);
if (instr != NULL) {
instr->set_position(position);
return ast_context()->ReturnInstruction(instr, ast_id);
}
// Something did not match; must use a polymorphic load.
int count = 0;
HBasicBlock* join = NULL;
for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) {
Handle<Map> map = types->at(i);
LookupResult lookup(isolate());
if (ComputeLoadStoreField(map, name, &lookup, false) ||
(lookup.IsCacheable() &&
CanInlinePropertyAccess(*map) &&
(lookup.IsConstant() ||
(!lookup.IsFound() &&
PrototypeChainCanNeverResolve(map, name))))) {
PropertyAccessInfo info(isolate(), types->at(i), name);
if (info.CanLoadMonomorphic()) {
if (count == 0) {
BuildCheckHeapObject(object);
join = graph()->CreateBasicBlock();
@ -4687,37 +4712,25 @@ void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField(
++count;
HBasicBlock* if_true = graph()->CreateBasicBlock();
HBasicBlock* if_false = graph()->CreateBasicBlock();
HCompareMap* compare =
new(zone()) HCompareMap(object, map, if_true, if_false);
HCompareMap* compare = new(zone()) HCompareMap(
object, info.map(), if_true, if_false);
current_block()->Finish(compare);
set_current_block(if_true);
// TODO(verwaest): Merge logic with BuildLoadNamedMonomorphic.
if (lookup.IsField()) {
HObjectAccess access = HObjectAccess::ForField(map, &lookup, name);
HLoadNamedField* load = BuildLoadNamedField(compare, access);
load->set_position(position);
AddInstruction(load);
if (!ast_context()->IsEffect()) Push(load);
} else if (lookup.IsConstant()) {
Handle<Object> constant(lookup.GetConstantFromMap(*map), isolate());
HConstant* hconstant = Add<HConstant>(constant);
if (!ast_context()->IsEffect()) Push(hconstant);
HInstruction* load = BuildLoadMonomorphic(
&info, object, compare, ast_id, return_id, FLAG_polymorphic_inlining);
if (load == NULL) {
if (HasStackOverflow()) return;
} else {
ASSERT(!lookup.IsFound());
if (map->prototype()->IsJSObject()) {
Handle<JSObject> prototype(JSObject::cast(map->prototype()));
Handle<JSObject> holder = prototype;
while (holder->map()->prototype()->IsJSObject()) {
holder = handle(JSObject::cast(holder->map()->prototype()));
}
BuildCheckPrototypeMaps(prototype, holder);
if (!load->IsLinked()) {
load->set_position(position);
AddInstruction(load);
}
if (!ast_context()->IsEffect()) Push(graph()->GetConstantUndefined());
if (!ast_context()->IsEffect()) Push(load);
}
current_block()->Goto(join);
if (current_block() != NULL) current_block()->Goto(join);
set_current_block(if_false);
}
}
@ -4761,8 +4774,6 @@ bool HOptimizedGraphBuilder::TryStorePolymorphicAsMonomorphic(
// for all maps. Requires special map check on the set of all handled maps.
if (types->length() > kMaxStorePolymorphism) return false;
// TODO(verwaest): Merge the checking logic with the code in
// TryLoadPolymorphicAsMonomorphic.
LookupResult lookup(isolate());
int count;
Representation representation = Representation::None();
@ -5361,73 +5372,6 @@ HInstruction* HOptimizedGraphBuilder::BuildCallGetter(
}
HInstruction* HOptimizedGraphBuilder::BuildLoadNamedMonomorphic(
HValue* object,
Handle<String> name,
Handle<Map> map) {
// Handle a load from a known field.
ASSERT(!map->is_dictionary_map());
// Handle access to various length properties
if (name->Equals(isolate()->heap()->length_string())) {
if (map->instance_type() == JS_ARRAY_TYPE) {
HCheckMaps* checked_object = AddCheckMap(object, map);
return New<HLoadNamedField>(
checked_object, HObjectAccess::ForArrayLength(map->elements_kind()));
}
}
LookupResult lookup(isolate());
map->LookupDescriptor(NULL, *name, &lookup);
if (lookup.IsField()) {
HCheckMaps* checked_object = AddCheckMap(object, map);
ASSERT(map->IsJSObjectMap());
return BuildLoadNamedField(
checked_object, HObjectAccess::ForField(map, &lookup, name));
}
// Handle a load of a constant known function.
if (lookup.IsConstant()) {
AddCheckMap(object, map);
Handle<Object> constant(lookup.GetConstantFromMap(*map), isolate());
return New<HConstant>(constant);
}
if (lookup.IsFound()) {
// Cannot handle the property, do a generic load instead.
HValue* context = environment()->context();
return new(zone()) HLoadNamedGeneric(context, object, name);
}
// Handle a load from a known field somewhere in the prototype chain.
LookupInPrototypes(map, name, &lookup);
if (lookup.IsField()) {
Handle<JSObject> prototype(JSObject::cast(map->prototype()));
Handle<JSObject> holder(lookup.holder());
Handle<Map> holder_map(holder->map());
AddCheckMap(object, map);
HValue* checked_holder = BuildCheckPrototypeMaps(prototype, holder);
return BuildLoadNamedField(
checked_holder, HObjectAccess::ForField(holder_map, &lookup, name));
}
// Handle a load of a constant function somewhere in the prototype chain.
if (lookup.IsConstant()) {
Handle<JSObject> prototype(JSObject::cast(map->prototype()));
Handle<JSObject> holder(lookup.holder());
Handle<Map> holder_map(holder->map());
AddCheckMap(object, map);
BuildCheckPrototypeMaps(prototype, holder);
Handle<Object> constant(lookup.GetConstantFromMap(*holder_map), isolate());
return New<HConstant>(constant);
}
// No luck, do a generic load.
HValue* context = environment()->context();
return new(zone()) HLoadNamedGeneric(context, object, name);
}
HInstruction* HOptimizedGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
HValue* key) {
HValue* context = environment()->context();
@ -5827,7 +5771,6 @@ void HOptimizedGraphBuilder::PushLoad(Property* expr,
static bool AreStringTypes(SmallMapList* types) {
if (types == NULL || types->length() == 0) return false;
for (int i = 0; i < types->length(); i++) {
if (types->at(i)->instance_type() >= FIRST_NONSTRING_TYPE) return false;
}
@ -5855,37 +5798,33 @@ void HOptimizedGraphBuilder::BuildLoad(Property* expr,
} else if (expr->key()->IsPropertyName()) {
Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
HValue* object = Top();
HValue* object = Pop();
SmallMapList* types;
bool monomorphic = ComputeReceiverTypes(expr, object, &types);
ComputeReceiverTypes(expr, object, &types);
ASSERT(types != NULL);
if (monomorphic) {
Handle<Map> map = types->first();
Handle<JSFunction> getter;
Handle<JSObject> holder;
if (LookupGetter(map, name, &getter, &holder)) {
AddCheckConstantFunction(holder, Top(), map);
if (FLAG_inline_accessors &&
TryInlineGetter(getter, ast_id, expr->LoadId())) {
return;
}
Add<HPushArgument>(Pop());
instr = new(zone()) HCallConstantFunction(getter, 1);
} else {
instr = BuildLoadNamedMonomorphic(Pop(), name, map);
if (types->length() > 0) {
PropertyAccessInfo info(isolate(), types->first(), name);
if (!info.CanLoadAsMonomorphic(types)) {
return HandlePolymorphicLoadNamedField(
position, ast_id, expr->LoadId(), object, types, name);
}
} else if (AreStringTypes(types) &&
name->Equals(isolate()->heap()->length_string())) {
BuildCheckHeapObject(Pop());
HValue* checked_object =
AddInstruction(HCheckInstanceType::NewIsString(object, zone()));
instr = BuildLoadStringLength(object, checked_object);
} else if (types != NULL && types->length() > 1) {
return HandlePolymorphicLoadNamedField(
position, ast_id, Pop(), types, name);
BuildCheckHeapObject(object);
HInstruction* checked_object;
if (AreStringTypes(types)) {
checked_object =
AddInstruction(HCheckInstanceType::NewIsString(object, zone()));
} else {
checked_object = Add<HCheckMaps>(object, types);
}
instr = BuildLoadMonomorphic(
&info, object, checked_object, ast_id, expr->LoadId());
if (instr == NULL) return;
if (instr->IsLinked()) return ast_context()->ReturnValue(instr);
} else {
instr = BuildLoadNamedGeneric(Pop(), name, expr);
instr = BuildLoadNamedGeneric(object, name, expr);
}
} else {
@ -6020,22 +5959,13 @@ bool HOptimizedGraphBuilder::TryCallPolymorphicAsMonomorphic(
Handle<String> name) {
if (types->length() > kMaxCallPolymorphism) return false;
Handle<Map> map(types->at(0));
LookupResult lookup(isolate());
if (!CanLoadPropertyFromPrototype(map, name, &lookup)) return false;
Handle<Object> prototype(map->prototype(), isolate());
for (int count = 1; count < types->length(); ++count) {
Handle<Map> test_map(types->at(count));
if (!CanLoadPropertyFromPrototype(test_map, name, &lookup)) return false;
if (test_map->prototype() != *prototype) return false;
}
if (!expr->ComputeTarget(map, name)) return false;
PropertyAccessInfo info(isolate(), types->at(0), name);
if (!info.CanLoadAsMonomorphic(types)) return false;
if (!expr->ComputeTarget(info.map(), name)) return false;
BuildCheckHeapObject(receiver);
Add<HCheckMaps>(receiver, types);
AddCheckPrototypeMaps(expr->holder(), map);
AddCheckPrototypeMaps(expr->holder(), info.map());
if (FLAG_trace_inlining) {
Handle<JSFunction> caller = current_info()->closure();
SmartArrayPointer<char> caller_name =

View File

@ -1945,13 +1945,81 @@ class HOptimizedGraphBuilder V8_FINAL
void HandlePropertyAssignment(Assignment* expr);
void HandleCompoundAssignment(Assignment* expr);
void HandlePolymorphicLoadNamedField(int position,
BailoutId ast_id,
BailoutId return_id,
HValue* object,
SmallMapList* types,
Handle<String> name);
HInstruction* TryLoadPolymorphicAsMonomorphic(HValue* object,
SmallMapList* types,
Handle<String> name);
class PropertyAccessInfo {
public:
PropertyAccessInfo(Isolate* isolate, Handle<Map> map, Handle<String> name)
: lookup_(isolate),
map_(map),
name_(name),
access_(HObjectAccess::ForMap()) { }
// Checkes whether this PropertyAccessInfo can be handled as a monomorphic
// load named. It additionally fills in the fields necessary to generate the
// lookup code.
bool CanLoadMonomorphic();
// Checks whether all types behave uniform when loading name. If all maps
// behave the same, a single monomorphic load instruction can be emitted,
// guarded by a single map-checks instruction that whether the receiver is
// an instance of any of the types.
// This method skips the first type in types, assuming that this
// PropertyAccessInfo is built for types->first().
bool CanLoadAsMonomorphic(SmallMapList* types);
bool IsStringLength() {
return map_->instance_type() < FIRST_NONSTRING_TYPE &&
name_->Equals(isolate()->heap()->length_string());
}
bool IsArrayLength() {
return map_->instance_type() == JS_ARRAY_TYPE &&
name_->Equals(isolate()->heap()->length_string());
}
bool has_holder() { return !holder_.is_null(); }
LookupResult* lookup() { return &lookup_; }
Handle<Map> map() { return map_; }
Handle<JSObject> holder() { return holder_; }
Handle<JSFunction> accessor() { return accessor_; }
Handle<Object> constant() { return constant_; }
HObjectAccess access() { return access_; }
private:
Isolate* isolate() { return lookup_.isolate(); }
bool LoadResult(Handle<Map> map);
bool LookupDescriptor();
bool LookupInPrototypes();
bool IsCompatibleForLoad(PropertyAccessInfo* other);
void GeneralizeRepresentation(Representation r) {
access_ = access_.WithRepresentation(
access_.representation().generalize(r));
}
LookupResult lookup_;
Handle<Map> map_;
Handle<String> name_;
Handle<JSObject> holder_;
Handle<JSFunction> accessor_;
Handle<Object> constant_;
HObjectAccess access_;
};
HInstruction* BuildLoadMonomorphic(PropertyAccessInfo* info,
HValue* object,
HInstruction* checked_object,
BailoutId ast_id,
BailoutId return_id,
bool can_inline_accessor = true);
void HandlePolymorphicStoreNamedField(int position,
BailoutId assignment_id,
HValue* object,
@ -2030,9 +2098,6 @@ class HOptimizedGraphBuilder V8_FINAL
Handle<Map> map,
Handle<JSFunction> getter,
Handle<JSObject> holder);
HInstruction* BuildLoadNamedMonomorphic(HValue* object,
Handle<String> name,
Handle<Map> map);
HCheckMaps* AddCheckMap(HValue* object, Handle<Map> map);