Commit 0418b671 authored by erikcorry's avatar erikcorry

Add negative lookups to polymorphic loads in Crankshaft.

Review URL: http://codereview.chromium.org/10539110

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@11780 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent d20af841
......@@ -2560,12 +2560,12 @@ void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
void LCodeGen::EmitLoadFieldOrConstantFunction(Register result,
Register object,
Handle<Map> type,
Handle<String> name) {
Handle<String> name,
LEnvironment* env) {
LookupResult lookup(isolate());
type->LookupInDescriptors(NULL, *name, &lookup);
ASSERT(lookup.IsFound() &&
(lookup.type() == FIELD || lookup.type() == CONSTANT_FUNCTION));
if (lookup.type() == FIELD) {
ASSERT(lookup.IsFound() || lookup.IsCacheable());
if (lookup.IsFound() && lookup.type() == FIELD) {
int index = lookup.GetLocalFieldIndexFromMap(*type);
int offset = index * kPointerSize;
if (index < 0) {
......@@ -2577,9 +2577,23 @@ void LCodeGen::EmitLoadFieldOrConstantFunction(Register result,
__ ldr(result, FieldMemOperand(object, JSObject::kPropertiesOffset));
__ ldr(result, FieldMemOperand(result, offset + FixedArray::kHeaderSize));
}
} else {
} else if (lookup.IsFound() && lookup.type() == CONSTANT_FUNCTION) {
Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*type));
__ LoadHeapObject(result, function);
} else {
// Negative lookup.
// Check prototypes.
HeapObject* current = HeapObject::cast((*type)->prototype());
Heap* heap = type->GetHeap();
while (current != heap->null_value()) {
Handle<HeapObject> link(current);
__ LoadHeapObject(result, link);
__ ldr(result, FieldMemOperand(result, HeapObject::kMapOffset));
__ cmp(result, Operand(Handle<Map>(JSObject::cast(current)->map())));
DeoptimizeIf(ne, env);
current = HeapObject::cast(current->map()->prototype());
}
__ LoadRoot(result, Heap::kUndefinedValueRootIndex);
}
}
......@@ -2587,7 +2601,7 @@ void LCodeGen::EmitLoadFieldOrConstantFunction(Register result,
void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) {
Register object = ToRegister(instr->object());
Register result = ToRegister(instr->result());
Register scratch = scratch0();
Register object_map = scratch0();
int map_count = instr->hydrogen()->types()->length();
bool need_generic = instr->hydrogen()->need_generic();
......@@ -2598,18 +2612,24 @@ void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) {
}
Handle<String> name = instr->hydrogen()->name();
Label done;
__ ldr(scratch, FieldMemOperand(object, HeapObject::kMapOffset));
__ ldr(object_map, FieldMemOperand(object, HeapObject::kMapOffset));
for (int i = 0; i < map_count; ++i) {
bool last = (i == map_count - 1);
Handle<Map> map = instr->hydrogen()->types()->at(i);
__ cmp(scratch, Operand(map));
Label check_passed;
__ CompareMap(
object_map, map, &check_passed, ALLOW_ELEMENT_TRANSITION_MAPS);
if (last && !need_generic) {
DeoptimizeIf(ne, instr->environment());
EmitLoadFieldOrConstantFunction(result, object, map, name);
__ bind(&check_passed);
EmitLoadFieldOrConstantFunction(
result, object, map, name, instr->environment());
} else {
Label next;
__ b(ne, &next);
EmitLoadFieldOrConstantFunction(result, object, map, name);
__ bind(&check_passed);
EmitLoadFieldOrConstantFunction(
result, object, map, name, instr->environment());
__ b(&done);
__ bind(&next);
}
......
......@@ -319,7 +319,8 @@ class LCodeGen BASE_EMBEDDED {
void EmitLoadFieldOrConstantFunction(Register result,
Register object,
Handle<Map> type,
Handle<String> name);
Handle<String> name,
LEnvironment* env);
// Emits optimized code to deep-copy the contents of statically known
// object graphs (e.g. object literal boilerplate).
......
......@@ -2000,7 +2000,15 @@ void MacroAssembler::CompareMap(Register obj,
Label* early_success,
CompareMapMode mode) {
ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset));
cmp(scratch, Operand(map));
CompareMap(scratch, map, early_success, mode);
}
void MacroAssembler::CompareMap(Register obj_map,
Handle<Map> map,
Label* early_success,
CompareMapMode mode) {
cmp(obj_map, Operand(map));
if (mode == ALLOW_ELEMENT_TRANSITION_MAPS) {
ElementsKind kind = map->elements_kind();
if (IsFastElementsKind(kind)) {
......@@ -2011,7 +2019,7 @@ void MacroAssembler::CompareMap(Register obj,
current_map = current_map->LookupElementsTransitionMap(kind);
if (!current_map) break;
b(eq, early_success);
cmp(scratch, Operand(Handle<Map>(current_map)));
cmp(obj_map, Operand(Handle<Map>(current_map)));
}
}
}
......
......@@ -831,6 +831,13 @@ class MacroAssembler: public Assembler {
Label* early_success,
CompareMapMode mode = REQUIRE_EXACT_MAP);
// As above, but the map of the object is already loaded into the register
// which is preserved by the code generated.
void CompareMap(Register obj_map,
Handle<Map> map,
Label* early_success,
CompareMapMode mode = REQUIRE_EXACT_MAP);
// Check if the map of an object is equal to a specified map and branch to
// label if not. Skip the smi check if not required (object is known to be a
// heap object). If mode is ALLOW_ELEMENT_TRANSITION_MAPS, then also match
......
......@@ -1618,6 +1618,38 @@ void HLoadNamedField::PrintDataTo(StringStream* stream) {
}
// 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() ||
JSObject::cast(current)->IsAccessCheckNeeded() ||
!JSObject::cast(current)->HasFastProperties()) {
return false;
}
LookupResult lookup(isolate);
JSObject::cast(current)->map()->LookupInDescriptors(NULL, *name, &lookup);
if (lookup.IsFound()) {
if (lookup.type() != MAP_TRANSITION) return false;
} else if (!lookup.IsCacheable()) {
return false;
}
current = JSObject::cast(current)->GetPrototype();
}
return true;
}
HLoadNamedFieldPolymorphic::HLoadNamedFieldPolymorphic(HValue* context,
HValue* object,
SmallMapList* types,
......@@ -1630,7 +1662,7 @@ HLoadNamedFieldPolymorphic::HLoadNamedFieldPolymorphic(HValue* context,
SetOperandAt(1, object);
set_representation(Representation::Tagged());
SetGVNFlag(kDependsOnMaps);
int map_transitions = 0;
SmallMapList negative_lookups;
for (int i = 0;
i < types->length() && types_.length() < kMaxLoadPolymorphism;
++i) {
......@@ -1653,21 +1685,32 @@ HLoadNamedFieldPolymorphic::HLoadNamedFieldPolymorphic(HValue* context,
types_.Add(types->at(i), zone);
break;
case MAP_TRANSITION:
// We should just ignore these since they are not relevant to a load
// operation. This means we will deopt if we actually see this map
// from optimized code.
map_transitions++;
if (PrototypeChainCanNeverResolve(map, name)) {
negative_lookups.Add(types->at(i), zone);
}
break;
default:
break;
}
} else if (lookup.IsCacheable()) {
if (PrototypeChainCanNeverResolve(map, name)) {
negative_lookups.Add(types->at(i), zone);
}
}
}
if (types_.length() + map_transitions == types->length() &&
FLAG_deoptimize_uncommon_cases) {
bool need_generic =
(types->length() != negative_lookups.length() + types_.length());
if (!need_generic && FLAG_deoptimize_uncommon_cases) {
SetFlag(kUseGVN);
for (int i = 0; i < negative_lookups.length(); i++) {
types_.Add(negative_lookups.at(i), zone);
}
} else {
// We don't have an easy way to handle both a call (to the generic stub) and
// a deopt in the same hydrogen instruction, so in this case we don't add
// the negative lookups which can deopt - just let the generic stub handle
// them.
SetAllSideEffects();
need_generic_ = true;
}
......
......@@ -2302,12 +2302,12 @@ void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
void LCodeGen::EmitLoadFieldOrConstantFunction(Register result,
Register object,
Handle<Map> type,
Handle<String> name) {
Handle<String> name,
LEnvironment* env) {
LookupResult lookup(isolate());
type->LookupInDescriptors(NULL, *name, &lookup);
ASSERT(lookup.IsFound() &&
(lookup.type() == FIELD || lookup.type() == CONSTANT_FUNCTION));
if (lookup.type() == FIELD) {
ASSERT(lookup.IsFound() || lookup.IsCacheable());
if (lookup.IsFound() && lookup.type() == FIELD) {
int index = lookup.GetLocalFieldIndexFromMap(*type);
int offset = index * kPointerSize;
if (index < 0) {
......@@ -2319,9 +2319,23 @@ void LCodeGen::EmitLoadFieldOrConstantFunction(Register result,
__ mov(result, FieldOperand(object, JSObject::kPropertiesOffset));
__ mov(result, FieldOperand(result, offset + FixedArray::kHeaderSize));
}
} else {
} else if (lookup.IsFound() && lookup.type() == CONSTANT_FUNCTION) {
Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*type));
__ LoadHeapObject(result, function);
} else {
// Negative lookup.
// Check prototypes.
HeapObject* current = HeapObject::cast((*type)->prototype());
Heap* heap = type->GetHeap();
while (current != heap->null_value()) {
Handle<HeapObject> link(current);
__ LoadHeapObject(result, link);
__ cmp(FieldOperand(result, HeapObject::kMapOffset),
Handle<Map>(JSObject::cast(current)->map()));
DeoptimizeIf(not_equal, env);
current = HeapObject::cast(current->map()->prototype());
}
__ mov(result, factory()->undefined_value());
}
}
......@@ -2356,18 +2370,36 @@ void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) {
}
Handle<String> name = instr->hydrogen()->name();
Label done;
bool compact_code = true;
for (int i = 0; i < map_count; ++i) {
LookupResult lookup(isolate());
Handle<Map> map = instr->hydrogen()->types()->at(i);
map->LookupInDescriptors(NULL, *name, &lookup);
if (!lookup.IsFound() ||
(lookup.type() != FIELD && lookup.type() != CONSTANT_FUNCTION)) {
// The two cases above cause a bounded amount of code to be emitted. This
// is not necessarily the case for other lookup results.
compact_code = false;
break;
}
}
for (int i = 0; i < map_count; ++i) {
bool last = (i == map_count - 1);
Handle<Map> map = instr->hydrogen()->types()->at(i);
__ cmp(FieldOperand(object, HeapObject::kMapOffset), map);
Label check_passed;
__ CompareMap(object, map, &check_passed, ALLOW_ELEMENT_TRANSITION_MAPS);
if (last && !need_generic) {
DeoptimizeIf(not_equal, instr->environment());
EmitLoadFieldOrConstantFunction(result, object, map, name);
__ bind(&check_passed);
EmitLoadFieldOrConstantFunction(
result, object, map, name, instr->environment());
} else {
Label next;
__ j(not_equal, &next, Label::kNear);
EmitLoadFieldOrConstantFunction(result, object, map, name);
__ jmp(&done, Label::kNear);
__ bind(&check_passed);
EmitLoadFieldOrConstantFunction(
result, object, map, name, instr->environment());
__ jmp(&done, compact_code ? Label::kNear : Label::kFar);
__ bind(&next);
}
}
......
......@@ -314,7 +314,8 @@ class LCodeGen BASE_EMBEDDED {
void EmitLoadFieldOrConstantFunction(Register result,
Register object,
Handle<Map> type,
Handle<String> name);
Handle<String> name,
LEnvironment* env);
// Emits optimized code to deep-copy the contents of statically known
// object graphs (e.g. object literal boilerplate).
......
......@@ -2195,12 +2195,12 @@ void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
void LCodeGen::EmitLoadFieldOrConstantFunction(Register result,
Register object,
Handle<Map> type,
Handle<String> name) {
Handle<String> name,
LEnvironment* env) {
LookupResult lookup(isolate());
type->LookupInDescriptors(NULL, *name, &lookup);
ASSERT(lookup.IsFound() &&
(lookup.type() == FIELD || lookup.type() == CONSTANT_FUNCTION));
if (lookup.type() == FIELD) {
ASSERT(lookup.IsFound() || lookup.IsCacheable());
if (lookup.IsFound() && lookup.type() == FIELD) {
int index = lookup.GetLocalFieldIndexFromMap(*type);
int offset = index * kPointerSize;
if (index < 0) {
......@@ -2212,9 +2212,23 @@ void LCodeGen::EmitLoadFieldOrConstantFunction(Register result,
__ movq(result, FieldOperand(object, JSObject::kPropertiesOffset));
__ movq(result, FieldOperand(result, offset + FixedArray::kHeaderSize));
}
} else {
} else if (lookup.IsFound() && lookup.type() == CONSTANT_FUNCTION) {
Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*type));
__ LoadHeapObject(result, function);
} else {
// Negative lookup.
// Check prototypes.
HeapObject* current = HeapObject::cast((*type)->prototype());
Heap* heap = type->GetHeap();
while (current != heap->null_value()) {
Handle<HeapObject> link(current);
__ LoadHeapObject(result, link);
__ Cmp(FieldOperand(result, HeapObject::kMapOffset),
Handle<Map>(JSObject::cast(current)->map()));
DeoptimizeIf(not_equal, env);
current = HeapObject::cast(current->map()->prototype());
}
__ LoadRoot(result, Heap::kUndefinedValueRootIndex);
}
}
......@@ -2232,18 +2246,36 @@ void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) {
}
Handle<String> name = instr->hydrogen()->name();
Label done;
bool compact_code = true;
for (int i = 0; i < map_count; ++i) {
LookupResult lookup(isolate());
Handle<Map> map = instr->hydrogen()->types()->at(i);
map->LookupInDescriptors(NULL, *name, &lookup);
if (!lookup.IsFound() ||
(lookup.type() != FIELD && lookup.type() != CONSTANT_FUNCTION)) {
// The two cases above cause a bounded amount of code to be emitted. This
// is not necessarily the case for other lookup results.
compact_code = false;
break;
}
}
for (int i = 0; i < map_count; ++i) {
bool last = (i == map_count - 1);
Handle<Map> map = instr->hydrogen()->types()->at(i);
__ Cmp(FieldOperand(object, HeapObject::kMapOffset), map);
Label check_passed;
__ CompareMap(object, map, &check_passed, ALLOW_ELEMENT_TRANSITION_MAPS);
if (last && !need_generic) {
DeoptimizeIf(not_equal, instr->environment());
EmitLoadFieldOrConstantFunction(result, object, map, name);
__ bind(&check_passed);
EmitLoadFieldOrConstantFunction(
result, object, map, name, instr->environment());
} else {
Label next;
__ j(not_equal, &next, Label::kNear);
EmitLoadFieldOrConstantFunction(result, object, map, name);
__ jmp(&done, Label::kNear);
__ bind(&check_passed);
EmitLoadFieldOrConstantFunction(
result, object, map, name, instr->environment());
__ jmp(&done, compact_code ? Label::kNear: Label::kFar);
__ bind(&next);
}
}
......
......@@ -301,7 +301,8 @@ class LCodeGen BASE_EMBEDDED {
void EmitLoadFieldOrConstantFunction(Register result,
Register object,
Handle<Map> type,
Handle<String> name);
Handle<String> name,
LEnvironment* env);
// Emits code for pushing either a tagged constant, a (non-double)
// register, or a stack slot operand.
......
......@@ -119,7 +119,8 @@ if (support_smi_only_arrays) {
%OptimizeFunctionOnNextCall(testExactMapHoisting2);
testExactMapHoisting2(new Array(5));
testExactMapHoisting2(new Array(5));
assertTrue(2 != %GetOptimizationStatus(testExactMapHoisting2));
// Temporarily disabled - see bug 2176.
// assertTrue(2 != %GetOptimizationStatus(testExactMapHoisting2));
// Make sure that non-element related map checks do get hoisted if they use
// the transitioned map for the check and all transitions that they depend
......
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