}
-static bool CanInlinePropertyAccess(Map* type) {
- return type->IsJSObjectMap() &&
- !type->is_dictionary_map() &&
- !type->has_named_interceptor();
+static bool CanInlinePropertyAccess(Handle<HeapType> type) {
+ if (type->Is(HeapType::NumberOrString())) return true;
+ if (!type->IsClass()) return false;
+ Handle<Map> map = type->AsClass();
+ return map->IsJSObjectMap() &&
+ !map->is_dictionary_map() &&
+ !map->has_named_interceptor();
}
LookupResult* lookup) {
while (map->prototype()->IsJSObject()) {
Handle<JSObject> holder(JSObject::cast(map->prototype()));
- map = Handle<Map>(holder->map());
- if (!CanInlinePropertyAccess(*map)) break;
+ map = handle(holder->map());
+ if (!CanInlinePropertyAccess(IC::MapToType(map))) break;
map->LookupDescriptor(*holder, *name, lookup);
if (lookup->IsFound()) return;
}
LookupResult* lookup,
bool lookup_transition = true) {
ASSERT(!type->is_observed());
- if (!CanInlinePropertyAccess(*type)) {
+ if (!CanInlinePropertyAccess(IC::MapToType(type))) {
lookup->NotFound();
return false;
}
bool HOptimizedGraphBuilder::PropertyAccessInfo::IsCompatibleForLoad(
PropertyAccessInfo* info) {
- if (!CanInlinePropertyAccess(*map_)) return false;
+ if (!CanInlinePropertyAccess(type_)) return false;
+
+ // Currently only handle HeapType::Number as a polymorphic case.
+ // TODO(verwaest): Support monomorphic handling of numbers with a HCheckNumber
+ // instruction.
+ if (type_->Is(HeapType::Number())) return false;
+
+ // Values are only compatible for monomorphic load if they all behave the same
+ // regarding value wrappers.
+ if (type_->Is(HeapType::NumberOrString())) {
+ if (!info->type_->Is(HeapType::NumberOrString())) return false;
+ } else {
+ if (info->type_->Is(HeapType::NumberOrString())) return false;
+ }
if (!LookupDescriptor()) return false;
if (!lookup_.IsFound()) {
return (!info->lookup_.IsFound() || info->has_holder()) &&
- map_->prototype() == info->map_->prototype();
+ map()->prototype() == info->map()->prototype();
}
// Mismatch if the other access info found the property in the prototype
bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupDescriptor() {
- map_->LookupDescriptor(NULL, *name_, &lookup_);
- return LoadResult(map_);
+ if (!type_->IsClass()) return true;
+ map()->LookupDescriptor(NULL, *name_, &lookup_);
+ return LoadResult(map());
}
bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupInPrototypes() {
- Handle<Map> map = map_;
+ Handle<Map> map = this->map();
+
while (map->prototype()->IsJSObject()) {
holder_ = handle(JSObject::cast(map->prototype()));
if (holder_->map()->is_deprecated()) {
JSObject::TryMigrateInstance(holder_);
}
map = Handle<Map>(holder_->map());
- if (!CanInlinePropertyAccess(*map)) {
+ if (!CanInlinePropertyAccess(IC::MapToType(map))) {
lookup_.NotFound();
return false;
}
bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadMonomorphic() {
- if (!CanInlinePropertyAccess(*map_)) return IsStringLength();
+ if (!CanInlinePropertyAccess(type_)) return false;
if (IsJSObjectFieldAccessor()) return true;
if (!LookupDescriptor()) return false;
if (lookup_.IsFound()) return true;
bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadAsMonomorphic(
SmallMapList* types) {
- ASSERT(map_.is_identical_to(types->first()));
+ ASSERT(type_->Is(IC::MapToType(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());
+ 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;
return true;
}
- if (IsJSObjectFieldAccessor()) {
- InstanceType instance_type = map_->instance_type();
+ HObjectAccess access = HObjectAccess::ForMap(); // bogus default
+ if (GetJSObjectFieldAccess(&access)) {
for (int i = 1; i < types->length(); ++i) {
- if (types->at(i)->instance_type() != instance_type) return false;
+ PropertyAccessInfo test_info(
+ builder_, IC::MapToType(types->at(i)), name_);
+ HObjectAccess test_access = HObjectAccess::ForMap(); // bogus default
+ if (!test_info.GetJSObjectFieldAccess(&test_access)) return false;
+ if (!access.Equals(test_access)) return false;
}
return true;
}
+ // Currently only handle HeapType::Number as a polymorphic case.
+ // TODO(verwaest): Support monomorphic handling of numbers with a HCheckNumber
+ // instruction.
+ if (type_->Is(HeapType::Number())) return false;
+
for (int i = 1; i < types->length(); ++i) {
- PropertyAccessInfo test_info(isolate(), types->at(i), name_);
+ PropertyAccessInfo test_info(builder_, IC::MapToType(types->at(i)), name_);
if (!test_info.IsCompatibleForLoad(this)) return false;
}
}
+static bool NeedsWrappingFor(Handle<HeapType> type, Handle<JSFunction> target) {
+ return type->Is(HeapType::NumberOrString()) &&
+ target->shared()->is_classic_mode() &&
+ !target->shared()->native();
+}
+
+
HInstruction* HOptimizedGraphBuilder::BuildLoadMonomorphic(
PropertyAccessInfo* info,
HValue* object,
- HInstruction* checked_object,
+ HValue* checked_object,
BailoutId ast_id,
BailoutId return_id,
bool can_inline_accessor) {
}
if (info->lookup()->IsPropertyCallbacks()) {
- Push(checked_object);
- if (FLAG_inline_accessors &&
- can_inline_accessor &&
- TryInlineGetter(info->accessor(), ast_id, return_id)) {
- return NULL;
+ if (NeedsWrappingFor(info->type(), info->accessor())) {
+ return New<HLoadNamedGeneric>(checked_object, info->name());
+ // HValue* function = Add<HConstant>(info->accessor());
+ // Add<HPushArgument>(checked_object);
+ // return New<HCallFunction>(function, 1, WRAP_AND_CALL);
+ } else {
+ Push(checked_object);
+ if (FLAG_inline_accessors &&
+ can_inline_accessor &&
+ TryInlineGetter(info->accessor(), ast_id, return_id)) {
+ return NULL;
+ }
+ Add<HPushArgument>(Pop());
+ return BuildCallConstantFunction(info->accessor(), 1);
}
- Add<HPushArgument>(Pop());
- return BuildCallConstantFunction(info->accessor(), 1);
}
ASSERT(info->lookup()->IsConstant());
// Something did not match; must use a polymorphic load.
int count = 0;
HBasicBlock* join = NULL;
+ HBasicBlock* number_block = NULL;
+
+ bool handle_smi = false;
for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) {
- PropertyAccessInfo info(isolate(), types->at(i), name);
+ PropertyAccessInfo info(this, IC::MapToType(types->at(i)), name);
if (info.CanLoadMonomorphic()) {
- if (count == 0) {
- BuildCheckHeapObject(object);
- join = graph()->CreateBasicBlock();
+ count++;
+ if (info.type()->Is(HeapType::Number())) {
+ handle_smi = true;
+ break;
}
- ++count;
- HBasicBlock* if_true = graph()->CreateBasicBlock();
- HBasicBlock* if_false = graph()->CreateBasicBlock();
- HCompareMap* compare = New<HCompareMap>(
- object, info.map(), if_true, if_false);
- FinishCurrentBlock(compare);
+ }
+ }
- set_current_block(if_true);
+ count = 0;
+ bool handled_string = false;
+ HControlInstruction* smi_check = NULL;
+
+ for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) {
+ PropertyAccessInfo info(this, IC::MapToType(types->at(i)), name);
+ if (info.type()->Is(HeapType::String())) {
+ if (handled_string) continue;
+ handled_string = true;
+ }
+ if (!info.CanLoadMonomorphic()) continue;
- HInstruction* load = BuildLoadMonomorphic(
- &info, object, compare, ast_id, return_id, FLAG_polymorphic_inlining);
- if (load == NULL) {
- if (HasStackOverflow()) return;
+ if (count == 0) {
+ join = graph()->CreateBasicBlock();
+ if (handle_smi) {
+ HBasicBlock* empty_smi_block = graph()->CreateBasicBlock();
+ HBasicBlock* not_smi_block = graph()->CreateBasicBlock();
+ number_block = graph()->CreateBasicBlock();
+ smi_check = New<HIsSmiAndBranch>(
+ object, empty_smi_block, not_smi_block);
+ FinishCurrentBlock(smi_check);
+ Goto(empty_smi_block, number_block);
+ set_current_block(not_smi_block);
} else {
- if (!load->IsLinked()) {
- AddInstruction(load);
- }
- if (!ast_context()->IsEffect()) Push(load);
+ BuildCheckHeapObject(object);
}
+ }
+ ++count;
+ HBasicBlock* if_true = graph()->CreateBasicBlock();
+ HBasicBlock* if_false = graph()->CreateBasicBlock();
+ HUnaryControlInstruction* compare;
- if (current_block() != NULL) Goto(join);
- set_current_block(if_false);
+ HValue* dependency;
+ if (info.type()->Is(HeapType::Number())) {
+ Handle<Map> heap_number_map = isolate()->factory()->heap_number_map();
+ compare = New<HCompareMap>(object, heap_number_map, if_true, if_false);
+ dependency = smi_check;
+ } else if (info.type()->Is(HeapType::String())) {
+ compare = New<HIsStringAndBranch>(object, if_true, if_false);
+ dependency = compare;
+ } else {
+ compare = New<HCompareMap>(object, info.map(), if_true, if_false);
+ dependency = compare;
}
+ FinishCurrentBlock(compare);
+
+ if (info.type()->Is(HeapType::Number())) {
+ Goto(if_true, number_block);
+ if_true = number_block;
+ number_block->SetJoinId(ast_id);
+ }
+
+ set_current_block(if_true);
+
+ HInstruction* load = BuildLoadMonomorphic(
+ &info, object, dependency, ast_id,
+ return_id, FLAG_polymorphic_inlining);
+ if (load == NULL) {
+ if (HasStackOverflow()) return;
+ } else {
+ if (!load->IsLinked()) {
+ AddInstruction(load);
+ }
+ if (!ast_context()->IsEffect()) Push(load);
+ }
+
+ if (current_block() != NULL) Goto(join);
+ set_current_block(if_false);
}
// Finish up. Unconditionally deoptimize if we've handled all the maps we
types->FilterForPossibleTransitions(root_map);
monomorphic = types->length() == 1;
}
- return monomorphic && CanInlinePropertyAccess(*types->first());
+ return monomorphic && CanInlinePropertyAccess(IC::MapToType(types->first()));
}
Handle<JSFunction> setter;
Handle<JSObject> holder;
if (LookupSetter(map, name, &setter, &holder)) {
- AddCheckConstantFunction(holder, object, map);
- if (FLAG_inline_accessors &&
- TryInlineSetter(setter, ast_id, return_id, value)) {
+ AddCheckMap(object, map);
+ AddCheckPrototypeMaps(holder, map);
+ bool needs_wrapping = NeedsWrappingFor(IC::MapToType(map), setter);
+ bool try_inline = FLAG_inline_accessors && !needs_wrapping;
+ if (try_inline && TryInlineSetter(setter, ast_id, return_id, value)) {
return;
}
Drop(2);
- Add<HPushArgument>(object);
- Add<HPushArgument>(value);
- instr = BuildCallConstantFunction(setter, 2);
+ if (needs_wrapping) {
+ instr = BuildStoreNamedGeneric(object, name, value);
+ } else {
+ Add<HPushArgument>(object);
+ Add<HPushArgument>(value);
+ instr = BuildCallConstantFunction(setter, 2);
+ }
} else {
Drop(2);
CHECK_ALIVE(instr = BuildStoreNamedMonomorphic(object,
ASSERT(types != NULL);
if (types->length() > 0) {
- PropertyAccessInfo info(isolate(), types->first(), name);
+ PropertyAccessInfo info(this, IC::MapToType(types->first()), name);
if (!info.CanLoadAsMonomorphic(types)) {
return HandlePolymorphicLoadNamedField(
ast_id, expr->LoadId(), object, types, name);
}
+ HValue* checked_object;
+ // HeapType::Number() is only supported by polymorphic load/call handling.
+ ASSERT(!info.type()->Is(HeapType::Number()));
BuildCheckHeapObject(object);
- HInstruction* checked_object;
if (AreStringTypes(types)) {
checked_object =
Add<HCheckInstanceType>(object, HCheckInstanceType::IS_STRING);
Handle<String> name) {
if (types->length() > kMaxCallPolymorphism) return false;
- PropertyAccessInfo info(isolate(), types->at(0), name);
+ PropertyAccessInfo info(this, IC::MapToType(types->at(0)), name);
if (!info.CanLoadAsMonomorphic(types)) return false;
if (!expr->ComputeTarget(info.map(), name)) return false;
class PropertyAccessInfo {
public:
- PropertyAccessInfo(Isolate* isolate, Handle<Map> map, Handle<String> name)
- : lookup_(isolate),
- map_(map),
+ PropertyAccessInfo(HOptimizedGraphBuilder* builder,
+ Handle<HeapType> type,
+ Handle<String> name)
+ : lookup_(builder->isolate()),
+ builder_(builder),
+ type_(type),
name_(name),
access_(HObjectAccess::ForMap()) { }
// PropertyAccessInfo is built for types->first().
bool CanLoadAsMonomorphic(SmallMapList* types);
+ Handle<Map> map() {
+ if (type_->Is(HeapType::Number())) {
+ Context* context = current_info()->closure()->context();
+ context = context->native_context();
+ return handle(context->number_function()->initial_map());
+ } else if (type_->Is(HeapType::String())) {
+ Context* context = current_info()->closure()->context();
+ context = context->native_context();
+ return handle(context->string_function()->initial_map());
+ } else {
+ return type_->AsClass();
+ }
+ }
+ Handle<HeapType> type() const { return type_; }
+ Handle<String> name() const { return name_; }
+
bool IsJSObjectFieldAccessor() {
int offset; // unused
- return Accessors::IsJSObjectFieldAccessor(map_, name_, &offset);
+ return Accessors::IsJSObjectFieldAccessor(type_, name_, &offset);
}
bool GetJSObjectFieldAccess(HObjectAccess* access) {
- if (IsStringLength()) {
- *access = HObjectAccess::ForStringLength();
- return true;
- } else if (IsArrayLength()) {
- *access = HObjectAccess::ForArrayLength(map_->elements_kind());
+ if (IsArrayLength()) {
+ *access = HObjectAccess::ForArrayLength(map()->elements_kind());
return true;
- } else {
- int offset;
- if (Accessors::IsJSObjectFieldAccessor(map_, name_, &offset)) {
+ }
+ int offset;
+ if (Accessors::IsJSObjectFieldAccessor(type_, name_, &offset)) {
+ if (type_->Is(HeapType::String())) {
+ ASSERT(name_->Equals(isolate()->heap()->length_string()));
+ *access = HObjectAccess::ForStringLength();
+ } else {
*access = HObjectAccess::ForJSObjectOffset(offset);
- return true;
}
- return false;
+ return true;
}
+ return false;
}
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_; }
private:
Isolate* isolate() { return lookup_.isolate(); }
-
- bool IsStringLength() {
- return map_->instance_type() < FIRST_NONSTRING_TYPE &&
- name_->Equals(isolate()->heap()->length_string());
- }
+ CompilationInfo* current_info() { return builder_->current_info(); }
bool IsArrayLength() {
- return map_->instance_type() == JS_ARRAY_TYPE &&
+ return map()->instance_type() == JS_ARRAY_TYPE &&
name_->Equals(isolate()->heap()->length_string());
}
}
LookupResult lookup_;
- Handle<Map> map_;
+ HOptimizedGraphBuilder* builder_;
+ Handle<HeapType> type_;
Handle<String> name_;
Handle<JSObject> holder_;
Handle<JSFunction> accessor_;
HInstruction* BuildLoadMonomorphic(PropertyAccessInfo* info,
HValue* object,
- HInstruction* checked_object,
+ HValue* checked_object,
BailoutId ast_id,
BailoutId return_id,
bool can_inline_accessor = true);
--- /dev/null
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// When calling user-defined accessors on strings, booleans or
+// numbers, we should create a wrapper object in classic-mode.
+
+// Flags: --allow-natives-syntax
+
+function test(object, prototype) {
+ var result;
+ Object.defineProperty(prototype, "nonstrict", {
+ get: function() { result = this; },
+ set: function(v) { result = this; }
+ });
+ Object.defineProperty(prototype, "strict", {
+ get: function() { "use strict"; result = this; },
+ set: function(v) { "use strict"; result = this; }
+ });
+
+ (function() {
+ function nonstrict(s) {
+ return s.nonstrict;
+ }
+ function strict(s) {
+ return s.strict;
+ }
+
+ nonstrict(object);
+ nonstrict(object);
+ %OptimizeFunctionOnNextCall(nonstrict);
+ result = undefined;
+ nonstrict(object);
+ assertEquals("object", typeof result);
+
+ strict(object);
+ strict(object);
+ %OptimizeFunctionOnNextCall(strict);
+ result = undefined;
+ strict(object);
+ assertEquals(typeof object, typeof result);
+ })();
+
+ (function() {
+ function nonstrict(s) {
+ return s.nonstrict = 10;
+ }
+ function strict(s) {
+ return s.strict = 10;
+ }
+
+ nonstrict(object);
+ nonstrict(object);
+ %OptimizeFunctionOnNextCall(nonstrict);
+ result = undefined;
+ nonstrict(object);
+ // TODO(1475): Support storing to primitive values.
+ // This should return "object" once storing to primitive values is
+ // supported.
+ assertEquals("undefined", typeof result);
+
+ strict(object);
+ strict(object);
+ %OptimizeFunctionOnNextCall(strict);
+ result = undefined;
+ strict(object);
+ // TODO(1475): Support storing to primitive values.
+ // This should return "object" once storing to primitive values is
+ // supported.
+ assertEquals("undefined", typeof result);
+ })();
+}
+
+test(1, Number.prototype);
+test("string", String.prototype);
+test(true, Boolean.prototype);