Enable GVN for polymorphic loads by not expanding them at the HIR level.

This change adds a new IR instruction for polymorphic loads. It performs
map compares and loads in one IR instruction instead of splitting each
load into a graph of map-compares and field loads.
 
The advantage is a smaller IR and less basic blocks, plus it allows to
do GVN on polymorphic loads.
Review URL: http://codereview.chromium.org/6708085

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7336 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
fschneider@chromium.org 2011-03-24 10:11:51 +00:00
parent 956bd6f7bf
commit 7d8e6c929d
16 changed files with 383 additions and 71 deletions

View File

@ -1769,6 +1769,21 @@ LInstruction* LChunkBuilder::DoLoadNamedField(HLoadNamedField* instr) {
}
LInstruction* LChunkBuilder::DoLoadNamedFieldPolymorphic(
HLoadNamedFieldPolymorphic* instr) {
ASSERT(instr->representation().IsTagged());
if (instr->need_generic()) {
LOperand* obj = UseFixed(instr->object(), r0);
LLoadNamedFieldPolymorphic* result = new LLoadNamedFieldPolymorphic(obj);
return MarkAsCall(DefineFixed(result, r0), instr);
} else {
LOperand* obj = UseRegisterAtStart(instr->object());
LLoadNamedFieldPolymorphic* result = new LLoadNamedFieldPolymorphic(obj);
return AssignEnvironment(DefineAsRegister(result));
}
}
LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) {
LOperand* object = UseFixed(instr->object(), r0);
LInstruction* result = DefineFixed(new LLoadNamedGeneric(object), r0);

View File

@ -123,6 +123,7 @@ class LCodeGen;
V(LoadKeyedFastElement) \
V(LoadKeyedGeneric) \
V(LoadNamedField) \
V(LoadNamedFieldPolymorphic) \
V(LoadNamedGeneric) \
V(LoadPixelArrayElement) \
V(ModI) \
@ -1148,6 +1149,19 @@ class LLoadNamedField: public LTemplateInstruction<1, 1, 0> {
};
class LLoadNamedFieldPolymorphic: public LTemplateInstruction<1, 1, 0> {
public:
explicit LLoadNamedFieldPolymorphic(LOperand* object) {
inputs_[0] = object;
}
DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field-polymorphic")
DECLARE_HYDROGEN_ACCESSOR(LoadNamedFieldPolymorphic)
LOperand* object() { return inputs_[0]; }
};
class LLoadNamedGeneric: public LTemplateInstruction<1, 1, 0> {
public:
explicit LLoadNamedGeneric(LOperand* object) {

View File

@ -2203,6 +2203,70 @@ void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
}
void LCodeGen::EmitLoadField(Register result,
Register object,
Handle<Map> type,
Handle<String> name) {
LookupResult lookup;
type->LookupInDescriptors(NULL, *name, &lookup);
ASSERT(lookup.IsProperty() && lookup.type() == FIELD);
int index = lookup.GetLocalFieldIndexFromMap(*type);
int offset = index * kPointerSize;
if (index < 0) {
// Negative property indices are in-object properties, indexed
// from the end of the fixed part of the object.
__ ldr(result, FieldMemOperand(object, offset + type->instance_size()));
} else {
// Non-negative property indices are in the properties array.
__ ldr(result, FieldMemOperand(object, JSObject::kPropertiesOffset));
__ ldr(result, FieldMemOperand(result, offset + FixedArray::kHeaderSize));
}
}
void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) {
Register object = ToRegister(instr->object());
Register result = ToRegister(instr->result());
Register scratch = scratch0();
int map_count = instr->hydrogen()->types()->length();
Handle<String> name = instr->hydrogen()->name();
if (map_count == 0) {
ASSERT(instr->hydrogen()->need_generic());
__ mov(r2, Operand(name));
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
} else {
Label done;
__ ldr(scratch, FieldMemOperand(object, HeapObject::kMapOffset));
for (int i = 0; i < map_count - 1; ++i) {
Handle<Map> map = instr->hydrogen()->types()->at(i);
Label next;
__ cmp(scratch, Operand(map));
__ b(ne, &next);
EmitLoadField(result, object, map, name);
__ b(&done);
__ bind(&next);
}
Handle<Map> map = instr->hydrogen()->types()->last();
__ cmp(scratch, Operand(map));
if (instr->hydrogen()->need_generic()) {
Label generic;
__ b(ne, &generic);
EmitLoadField(result, object, map, name);
__ b(&done);
__ bind(&generic);
__ mov(r2, Operand(name));
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
} else {
DeoptimizeIf(ne, instr->environment());
EmitLoadField(result, object, map, name);
}
__ bind(&done);
}
}
void LCodeGen::DoLoadNamedGeneric(LLoadNamedGeneric* instr) {
ASSERT(ToRegister(instr->object()).is(r0));
ASSERT(ToRegister(instr->result()).is(r0));

View File

@ -264,6 +264,11 @@ class LCodeGen BASE_EMBEDDED {
// Caller should branch on equal condition.
void EmitIsConstructCall(Register temp1, Register temp2);
void EmitLoadField(Register result,
Register object,
Handle<Map> type,
Handle<String> name);
LChunk* const chunk_;
MacroAssembler* const masm_;
CompilationInfo* const info_;

View File

@ -1156,6 +1156,60 @@ void HLoadNamedField::PrintDataTo(StringStream* stream) {
}
HLoadNamedFieldPolymorphic::HLoadNamedFieldPolymorphic(HValue* object,
ZoneMapList* types,
Handle<String> name)
: HUnaryOperation(object),
types_(Min(types->length(), kMaxLoadPolymorphism)),
name_(name),
need_generic_(false) {
set_representation(Representation::Tagged());
SetFlag(kDependsOnMaps);
for (int i = 0;
i < types->length() && types_.length() < kMaxLoadPolymorphism;
++i) {
Handle<Map> map = types->at(i);
LookupResult lookup;
map->LookupInDescriptors(NULL, *name, &lookup);
if (lookup.IsProperty() && lookup.type() == FIELD) {
types_.Add(types->at(i));
int index = lookup.GetLocalFieldIndexFromMap(*map);
if (index < 0) {
SetFlag(kDependsOnInobjectFields);
} else {
SetFlag(kDependsOnBackingStoreFields);
}
}
}
if (types_.length() == types->length() && FLAG_deoptimize_uncommon_cases) {
SetFlag(kUseGVN);
} else {
SetAllSideEffects();
need_generic_ = true;
}
}
bool HLoadNamedFieldPolymorphic::DataEquals(HValue* value) {
HLoadNamedFieldPolymorphic* other = HLoadNamedFieldPolymorphic::cast(value);
if (types_.length() != other->types()->length()) return false;
if (!name_.is_identical_to(other->name())) return false;
if (need_generic_ != other->need_generic_) return false;
for (int i = 0; i < types_.length(); i++) {
bool found = false;
for (int j = 0; j < types_.length(); j++) {
if (types_.at(j).is_identical_to(other->types()->at(i))) {
found = true;
break;
}
}
if (!found) return false;
}
return true;
}
void HLoadKeyedFastElement::PrintDataTo(StringStream* stream) {
object()->PrintNameTo(stream);
stream->Add("[");

View File

@ -129,6 +129,7 @@ class LChunkBuilder;
V(LoadKeyedGeneric) \
V(LoadNamedField) \
V(LoadNamedGeneric) \
V(LoadNamedFieldPolymorphic) \
V(LoadPixelArrayElement) \
V(Mod) \
V(Mul) \
@ -2972,6 +2973,37 @@ class HLoadNamedField: public HUnaryOperation {
};
class HLoadNamedFieldPolymorphic: public HUnaryOperation {
public:
HLoadNamedFieldPolymorphic(HValue* object,
ZoneMapList* types,
Handle<String> name);
HValue* object() { return OperandAt(0); }
ZoneMapList* types() { return &types_; }
Handle<String> name() { return name_; }
bool need_generic() { return need_generic_; }
virtual Representation RequiredInputRepresentation(int index) const {
return Representation::Tagged();
}
DECLARE_CONCRETE_INSTRUCTION(LoadNamedFieldPolymorphic,
"load_named_field_polymorphic")
static const int kMaxLoadPolymorphism = 4;
protected:
virtual bool DataEquals(HValue* value);
private:
ZoneMapList types_;
Handle<String> name_;
bool need_generic_;
};
class HLoadNamedGeneric: public HBinaryOperation {
public:
HLoadNamedGeneric(HValue* context, HValue* object, Handle<Object> name)

View File

@ -3445,69 +3445,6 @@ void HGraphBuilder::VisitThrow(Throw* expr) {
}
void HGraphBuilder::HandlePolymorphicLoadNamedField(Property* expr,
HValue* object,
ZoneMapList* types,
Handle<String> name) {
// 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;
HBasicBlock* join = NULL;
for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) {
Handle<Map> map = types->at(i);
LookupResult lookup;
map->LookupInDescriptors(NULL, *name, &lookup);
if (lookup.IsProperty() && lookup.type() == FIELD) {
if (count == 0) {
AddInstruction(new HCheckNonSmi(object)); // Only needed once.
join = graph()->CreateBasicBlock();
}
++count;
HBasicBlock* if_true = graph()->CreateBasicBlock();
HBasicBlock* if_false = graph()->CreateBasicBlock();
HCompareMap* compare = new HCompareMap(object, map, if_true, if_false);
current_block()->Finish(compare);
set_current_block(if_true);
HLoadNamedField* instr =
BuildLoadNamedField(object, expr, map, &lookup, false);
instr->set_position(expr->position());
instr->ClearFlag(HValue::kUseGVN);
AddInstruction(instr);
if (!ast_context()->IsEffect()) Push(instr);
current_block()->Goto(join);
set_current_block(if_false);
}
}
// Finish up. Unconditionally deoptimize if we've handled all the maps we
// know about and do not want to handle ones we've never seen. Otherwise
// use a generic IC.
if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
current_block()->FinishExitWithDeoptimization();
} else {
HInstruction* instr = BuildLoadNamedGeneric(object, expr);
instr->set_position(expr->position());
if (join != NULL) {
AddInstruction(instr);
if (!ast_context()->IsEffect()) Push(instr);
current_block()->Goto(join);
} else {
ast_context()->ReturnInstruction(instr, expr->id());
return;
}
}
ASSERT(join != NULL);
join->SetJoinId(expr->id());
set_current_block(join);
if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop());
}
HLoadNamedField* HGraphBuilder::BuildLoadNamedField(HValue* object,
Property* expr,
Handle<Map> type,
@ -3748,9 +3685,8 @@ void HGraphBuilder::VisitProperty(Property* expr) {
if (expr->IsMonomorphic()) {
instr = BuildLoadNamed(obj, expr, types->first(), name);
} else if (types != NULL && types->length() > 1) {
HandlePolymorphicLoadNamedField(expr, obj, types, name);
return;
AddInstruction(new HCheckNonSmi(obj));
instr = new HLoadNamedFieldPolymorphic(obj, types, name);
} else {
instr = BuildLoadNamedGeneric(obj, expr);
}

View File

@ -794,10 +794,6 @@ class HGraphBuilder: public AstVisitor {
void HandlePropertyAssignment(Assignment* expr);
void HandleCompoundAssignment(Assignment* expr);
void HandlePolymorphicLoadNamedField(Property* expr,
HValue* object,
ZoneMapList* types,
Handle<String> name);
void HandlePolymorphicStoreNamedField(Assignment* expr,
HValue* object,
HValue* value,

View File

@ -2109,7 +2109,7 @@ void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
Register object = ToRegister(instr->InputAt(0));
Register object = ToRegister(instr->object());
Register result = ToRegister(instr->result());
if (instr->hydrogen()->is_in_object()) {
__ mov(result, FieldOperand(object, instr->hydrogen()->offset()));
@ -2120,6 +2120,69 @@ void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
}
void LCodeGen::EmitLoadField(Register result,
Register object,
Handle<Map> type,
Handle<String> name) {
LookupResult lookup;
type->LookupInDescriptors(NULL, *name, &lookup);
ASSERT(lookup.IsProperty() && lookup.type() == FIELD);
int index = lookup.GetLocalFieldIndexFromMap(*type);
int offset = index * kPointerSize;
if (index < 0) {
// Negative property indices are in-object properties, indexed
// from the end of the fixed part of the object.
__ mov(result, FieldOperand(object, offset + type->instance_size()));
} else {
// Non-negative property indices are in the properties array.
__ mov(result, FieldOperand(object, JSObject::kPropertiesOffset));
__ mov(result, FieldOperand(result, offset + FixedArray::kHeaderSize));
}
}
void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) {
Register object = ToRegister(instr->object());
Register result = ToRegister(instr->result());
int map_count = instr->hydrogen()->types()->length();
Handle<String> name = instr->hydrogen()->name();
if (map_count == 0) {
ASSERT(instr->hydrogen()->need_generic());
__ mov(ecx, name);
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr, false);
} else {
NearLabel done;
for (int i = 0; i < map_count - 1; ++i) {
Handle<Map> map = instr->hydrogen()->types()->at(i);
NearLabel next;
__ cmp(FieldOperand(object, HeapObject::kMapOffset), map);
__ j(not_equal, &next);
EmitLoadField(result, object, map, name);
__ jmp(&done);
__ bind(&next);
}
Handle<Map> map = instr->hydrogen()->types()->last();
__ cmp(FieldOperand(object, HeapObject::kMapOffset), map);
if (instr->hydrogen()->need_generic()) {
NearLabel generic;
__ j(not_equal, &generic);
EmitLoadField(result, object, map, name);
__ jmp(&done);
__ bind(&generic);
__ mov(ecx, name);
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr, false);
} else {
DeoptimizeIf(not_equal, instr->environment());
EmitLoadField(result, object, map, name);
}
__ bind(&done);
}
}
void LCodeGen::DoLoadNamedGeneric(LLoadNamedGeneric* instr) {
ASSERT(ToRegister(instr->context()).is(esi));
ASSERT(ToRegister(instr->object()).is(eax));

View File

@ -246,6 +246,10 @@ class LCodeGen BASE_EMBEDDED {
// Caller should branch on equal condition.
void EmitIsConstructCall(Register temp);
void EmitLoadField(Register result,
Register object,
Handle<Map> type,
Handle<String> name);
LChunk* const chunk_;
MacroAssembler* const masm_;

View File

@ -1790,6 +1790,21 @@ LInstruction* LChunkBuilder::DoLoadNamedField(HLoadNamedField* instr) {
}
LInstruction* LChunkBuilder::DoLoadNamedFieldPolymorphic(
HLoadNamedFieldPolymorphic* instr) {
ASSERT(instr->representation().IsTagged());
if (instr->need_generic()) {
LOperand* obj = UseFixed(instr->object(), eax);
LLoadNamedFieldPolymorphic* result = new LLoadNamedFieldPolymorphic(obj);
return MarkAsCall(DefineFixed(result, eax), instr);
} else {
LOperand* obj = UseRegisterAtStart(instr->object());
LLoadNamedFieldPolymorphic* result = new LLoadNamedFieldPolymorphic(obj);
return AssignEnvironment(DefineAsRegister(result));
}
}
LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) {
LOperand* context = UseFixed(instr->context(), esi);
LOperand* object = UseFixed(instr->object(), eax);

View File

@ -125,6 +125,7 @@ class LCodeGen;
V(LoadKeyedFastElement) \
V(LoadKeyedGeneric) \
V(LoadNamedField) \
V(LoadNamedFieldPolymorphic) \
V(LoadNamedGeneric) \
V(LoadPixelArrayElement) \
V(ModI) \
@ -1171,6 +1172,21 @@ class LLoadNamedField: public LTemplateInstruction<1, 1, 0> {
DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field")
DECLARE_HYDROGEN_ACCESSOR(LoadNamedField)
LOperand* object() { return inputs_[0]; }
};
class LLoadNamedFieldPolymorphic: public LTemplateInstruction<1, 1, 0> {
public:
explicit LLoadNamedFieldPolymorphic(LOperand* object) {
inputs_[0] = object;
}
DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field-polymorphic")
DECLARE_HYDROGEN_ACCESSOR(LoadNamedFieldPolymorphic)
LOperand* object() { return inputs_[0]; }
};

View File

@ -2091,6 +2091,70 @@ void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
}
void LCodeGen::EmitLoadField(Register result,
Register object,
Handle<Map> type,
Handle<String> name) {
LookupResult lookup;
type->LookupInDescriptors(NULL, *name, &lookup);
ASSERT(lookup.IsProperty() && lookup.type() == FIELD);
int index = lookup.GetLocalFieldIndexFromMap(*type);
int offset = index * kPointerSize;
if (index < 0) {
// Negative property indices are in-object properties, indexed
// from the end of the fixed part of the object.
__ movq(result, FieldOperand(object, offset + type->instance_size()));
} else {
// Non-negative property indices are in the properties array.
__ movq(result, FieldOperand(object, JSObject::kPropertiesOffset));
__ movq(result, FieldOperand(result, offset + FixedArray::kHeaderSize));
}
}
void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) {
Register object = ToRegister(instr->object());
Register result = ToRegister(instr->result());
int map_count = instr->hydrogen()->types()->length();
Handle<String> name = instr->hydrogen()->name();
if (map_count == 0) {
ASSERT(instr->hydrogen()->need_generic());
__ Move(rcx, instr->hydrogen()->name());
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
} else {
NearLabel done;
for (int i = 0; i < map_count - 1; ++i) {
Handle<Map> map = instr->hydrogen()->types()->at(i);
NearLabel next;
__ Cmp(FieldOperand(object, HeapObject::kMapOffset), map);
__ j(not_equal, &next);
EmitLoadField(result, object, map, name);
__ jmp(&done);
__ bind(&next);
}
Handle<Map> map = instr->hydrogen()->types()->last();
__ Cmp(FieldOperand(object, HeapObject::kMapOffset), map);
if (instr->hydrogen()->need_generic()) {
NearLabel generic;
__ j(not_equal, &generic);
EmitLoadField(result, object, map, name);
__ jmp(&done);
__ bind(&generic);
__ Move(rcx, instr->hydrogen()->name());
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
} else {
DeoptimizeIf(not_equal, instr->environment());
EmitLoadField(result, object, map, name);
}
__ bind(&done);
}
}
void LCodeGen::DoLoadNamedGeneric(LLoadNamedGeneric* instr) {
ASSERT(ToRegister(instr->object()).is(rax));
ASSERT(ToRegister(instr->result()).is(rax));

View File

@ -241,6 +241,11 @@ class LCodeGen BASE_EMBEDDED {
// Caller should branch on equal condition.
void EmitIsConstructCall(Register temp);
void EmitLoadField(Register result,
Register object,
Handle<Map> type,
Handle<String> name);
// Emits code for pushing a constant operand.
void EmitPushConstantOperand(LOperand* operand);

View File

@ -1763,6 +1763,21 @@ LInstruction* LChunkBuilder::DoLoadNamedField(HLoadNamedField* instr) {
}
LInstruction* LChunkBuilder::DoLoadNamedFieldPolymorphic(
HLoadNamedFieldPolymorphic* instr) {
ASSERT(instr->representation().IsTagged());
if (instr->need_generic()) {
LOperand* obj = UseFixed(instr->object(), rax);
LLoadNamedFieldPolymorphic* result = new LLoadNamedFieldPolymorphic(obj);
return MarkAsCall(DefineFixed(result, rax), instr);
} else {
LOperand* obj = UseRegisterAtStart(instr->object());
LLoadNamedFieldPolymorphic* result = new LLoadNamedFieldPolymorphic(obj);
return AssignEnvironment(DefineAsRegister(result));
}
}
LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) {
LOperand* object = UseFixed(instr->object(), rax);
LLoadNamedGeneric* result = new LLoadNamedGeneric(object);

View File

@ -122,6 +122,7 @@ class LCodeGen;
V(LoadKeyedFastElement) \
V(LoadKeyedGeneric) \
V(LoadNamedField) \
V(LoadNamedFieldPolymorphic) \
V(LoadNamedGeneric) \
V(LoadFunctionPrototype) \
V(LoadPixelArrayElement) \
@ -1134,6 +1135,19 @@ class LLoadNamedField: public LTemplateInstruction<1, 1, 0> {
};
class LLoadNamedFieldPolymorphic: public LTemplateInstruction<1, 1, 0> {
public:
explicit LLoadNamedFieldPolymorphic(LOperand* object) {
inputs_[0] = object;
}
DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field-polymorphic")
DECLARE_HYDROGEN_ACCESSOR(LoadNamedFieldPolymorphic)
LOperand* object() { return inputs_[0]; }
};
class LLoadNamedGeneric: public LTemplateInstruction<1, 1, 0> {
public:
explicit LLoadNamedGeneric(LOperand* object) {