Commit 94b4240f authored by titzer@chromium.org's avatar titzer@chromium.org

Introduce ObjectAccess, which is used by LoadNamedField and StoreNamedField to...

Introduce ObjectAccess, which is used by LoadNamedField and StoreNamedField to denote what parts of an object are referred to by a given load or store. Refactor HGraphBuilder to use ObjectAccess, which removes the need to manually set GVN flags and simplifies the code as well.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14791 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 1d393554
......@@ -369,8 +369,7 @@ void LAccessArgumentsAt::PrintDataTo(StringStream* stream) {
void LStoreNamedField::PrintDataTo(StringStream* stream) {
object()->PrintTo(stream);
stream->Add(".");
stream->Add(*String::cast(*name())->ToCString());
hydrogen()->access().PrintTo(stream);
stream->Add(" <- ");
value()->PrintTo(stream);
}
......
......@@ -358,7 +358,6 @@ Handle<Code> FastCloneShallowArrayStub::GenerateCode() {
template <>
HValue* CodeStubGraphBuilder<FastCloneShallowObjectStub>::BuildCodeStub() {
Zone* zone = this->zone();
Factory* factory = isolate()->factory();
HValue* undefined = graph()->GetConstantUndefined();
HInstruction* boilerplate =
......@@ -387,20 +386,13 @@ HValue* CodeStubGraphBuilder<FastCloneShallowObjectStub>::BuildCodeStub() {
flags = static_cast<HAllocate::Flags>(
flags | HAllocate::CAN_ALLOCATE_IN_OLD_POINTER_SPACE);
}
HInstruction* object =
AddInstruction(new(zone) HAllocate(context(),
size_in_bytes,
HType::JSObject(),
flags));
HInstruction* object = AddInstruction(new(zone)
HAllocate(context(), size_in_bytes, HType::JSObject(), flags));
for (int i = 0; i < size; i += kPointerSize) {
HInstruction* value =
AddInstruction(new(zone) HLoadNamedField(
boilerplate, true, Representation::Tagged(), i));
AddInstruction(new(zone) HStoreNamedField(object,
factory->empty_string(),
value, true,
Representation::Tagged(), i));
HObjectAccess access = HObjectAccess::ForJSObjectOffset(i);
AddStore(object, access, AddLoad(boilerplate, access));
}
checker.ElseDeopt();
......@@ -430,11 +422,11 @@ Handle<Code> KeyedLoadFastElementStub::GenerateCode() {
template<>
HValue* CodeStubGraphBuilder<LoadFieldStub>::BuildCodeStub() {
Representation representation = casted_stub()->representation();
HInstruction* load = AddInstruction(DoBuildLoadNamedField(
GetParameter(0), casted_stub()->is_inobject(),
representation, casted_stub()->offset()));
return load;
HObjectAccess access = casted_stub()->is_inobject() ?
HObjectAccess::ForJSObjectOffset(casted_stub()->offset()) :
HObjectAccess::ForBackingStoreOffset(casted_stub()->offset());
return AddInstruction(BuildLoadNamedField(GetParameter(0), access,
casted_stub()->representation()));
}
......@@ -445,11 +437,11 @@ Handle<Code> LoadFieldStub::GenerateCode() {
template<>
HValue* CodeStubGraphBuilder<KeyedLoadFieldStub>::BuildCodeStub() {
Representation representation = casted_stub()->representation();
HInstruction* load = AddInstruction(DoBuildLoadNamedField(
GetParameter(0), casted_stub()->is_inobject(),
representation, casted_stub()->offset()));
return load;
HObjectAccess access = casted_stub()->is_inobject() ?
HObjectAccess::ForJSObjectOffset(casted_stub()->offset()) :
HObjectAccess::ForBackingStoreOffset(casted_stub()->offset());
return AddInstruction(BuildLoadNamedField(GetParameter(0), access,
casted_stub()->representation()));
}
......@@ -487,8 +479,8 @@ HValue* CodeStubGraphBuilder<TransitionElementsKindStub>::BuildCodeStub() {
AddInstruction(new(zone) HTrapAllocationMemento(js_array));
HInstruction* array_length =
AddInstruction(HLoadNamedField::NewArrayLength(
zone, js_array, js_array, HType::Smi()));
AddLoad(js_array, HObjectAccess::ForArrayLength());
array_length->set_type(HType::Smi());
ElementsKind to_kind = casted_stub()->to_kind();
BuildNewSpaceArrayCheck(array_length, to_kind);
......@@ -514,20 +506,12 @@ HValue* CodeStubGraphBuilder<TransitionElementsKindStub>::BuildCodeStub() {
casted_stub()->from_kind(), new_elements,
to_kind, array_length, elements_length);
Factory* factory = isolate()->factory();
AddInstruction(new(zone) HStoreNamedField(js_array,
factory->elements_field_string(),
new_elements, true,
Representation::Tagged(),
JSArray::kElementsOffset));
AddStore(js_array, HObjectAccess::ForElementsPointer(), new_elements);
if_builder.End();
AddInstruction(new(zone) HStoreNamedField(js_array, factory->length_string(),
map, true,
Representation::Tagged(),
JSArray::kMapOffset));
AddStore(js_array, HObjectAccess::ForMap(), map);
return js_array;
}
......
......@@ -2483,7 +2483,7 @@ void HParameter::PrintDataTo(StringStream* stream) {
void HLoadNamedField::PrintDataTo(StringStream* stream) {
object()->PrintNameTo(stream);
stream->Add(" @%d%s", offset(), is_in_object() ? "[in-object]" : "");
access_.PrintTo(stream);
if (HasTypeCheck()) {
stream->Add(" ");
typecheck()->PrintNameTo(stream);
......@@ -2805,11 +2805,9 @@ void HStoreNamedGeneric::PrintDataTo(StringStream* stream) {
void HStoreNamedField::PrintDataTo(StringStream* stream) {
object()->PrintNameTo(stream);
stream->Add(".");
stream->Add(*String::cast(*name())->ToCString());
access_.PrintTo(stream);
stream->Add(" = ");
value()->PrintNameTo(stream);
stream->Add(" @%d%s", offset(), is_in_object() ? "[in-object]" : "");
if (NeedsWriteBarrier()) {
stream->Add(" (write-barrier)");
}
......@@ -3669,4 +3667,141 @@ void HCheckFunction::Verify() {
#endif
HObjectAccess HObjectAccess::ForFixedArrayHeader(int offset) {
ASSERT(offset >= 0);
ASSERT(offset < FixedArray::kHeaderSize);
if (offset == FixedArray::kLengthOffset) return ForFixedArrayLength();
return HObjectAccess(kInobject, offset);
}
HObjectAccess HObjectAccess::ForJSObjectOffset(int offset) {
ASSERT(offset >= 0);
Portion portion = kInobject;
if (offset == JSObject::kElementsOffset) {
portion = kElementsPointer;
} else if (offset == JSObject::kMapOffset) {
portion = kMaps;
}
return HObjectAccess(portion, offset, Handle<String>::null());
}
HObjectAccess HObjectAccess::ForJSArrayOffset(int offset) {
ASSERT(offset >= 0);
Portion portion = kInobject;
if (offset == JSObject::kElementsOffset) {
portion = kElementsPointer;
} else if (offset == JSArray::kLengthOffset) {
portion = kArrayLengths;
} else if (offset == JSObject::kMapOffset) {
portion = kMaps;
}
return HObjectAccess(portion, offset, Handle<String>::null());
}
HObjectAccess HObjectAccess::ForBackingStoreOffset(int offset) {
ASSERT(offset >= 0);
return HObjectAccess(kBackingStore, offset, Handle<String>::null());
}
HObjectAccess HObjectAccess::ForField(Handle<Map> map,
LookupResult *lookup, Handle<String> name) {
ASSERT(lookup->IsField() || lookup->IsTransitionToField(*map));
int index;
if (lookup->IsField()) {
index = lookup->GetLocalFieldIndexFromMap(*map);
} else {
Map* transition = lookup->GetTransitionMapFromMap(*map);
int descriptor = transition->LastAdded();
index = transition->instance_descriptors()->GetFieldIndex(descriptor) -
map->inobject_properties();
}
if (index < 0) {
// Negative property indices are in-object properties, indexed
// from the end of the fixed part of the object.
int offset = (index * kPointerSize) + map->instance_size();
return HObjectAccess(kInobject, offset);
} else {
// Non-negative property indices are in the properties array.
int offset = (index * kPointerSize) + FixedArray::kHeaderSize;
return HObjectAccess(kBackingStore, offset, name);
}
}
void HObjectAccess::SetGVNFlags(HValue *instr, bool is_store) {
// set the appropriate GVN flags for a given load or store instruction
if (is_store) {
// track dominating allocations in order to eliminate write barriers
instr->SetGVNFlag(kDependsOnNewSpacePromotion);
instr->SetFlag(HValue::kTrackSideEffectDominators);
instr->SetFlag(HValue::kDeoptimizeOnUndefined);
} else {
// try to GVN loads, but don't hoist above map changes
instr->SetFlag(HValue::kUseGVN);
instr->SetGVNFlag(kDependsOnMaps);
}
switch (portion()) {
case kArrayLengths:
instr->SetGVNFlag(is_store
? kChangesArrayLengths : kDependsOnArrayLengths);
break;
case kInobject:
instr->SetGVNFlag(is_store
? kChangesInobjectFields : kDependsOnInobjectFields);
break;
case kDouble:
instr->SetGVNFlag(is_store
? kChangesDoubleFields : kDependsOnDoubleFields);
break;
case kBackingStore:
instr->SetGVNFlag(is_store
? kChangesBackingStoreFields : kDependsOnBackingStoreFields);
break;
case kElementsPointer:
instr->SetGVNFlag(is_store
? kChangesElementsPointer : kDependsOnElementsPointer);
break;
case kMaps:
instr->SetGVNFlag(is_store
? kChangesMaps : kDependsOnMaps);
break;
}
}
void HObjectAccess::PrintTo(StringStream* stream) {
stream->Add(".");
switch (portion()) {
case kArrayLengths:
stream->Add("%length");
break;
case kElementsPointer:
stream->Add("%elements");
break;
case kMaps:
stream->Add("%map");
break;
case kDouble: // fall through
case kInobject:
if (!name_.is_null()) stream->Add(*String::cast(*name_)->ToCString());
stream->Add("[in-object]");
break;
case kBackingStore:
if (!name_.is_null()) stream->Add(*String::cast(*name_)->ToCString());
stream->Add("[backing-store]");
break;
}
stream->Add("@%d", offset());
}
} } // namespace v8::internal
......@@ -5201,14 +5201,114 @@ class HStoreContextSlot: public HTemplateInstruction<2> {
};
// Represents an access to a portion of an object, such as the map pointer,
// array elements pointer, etc, but not accesses to array elements themselves.
class HObjectAccess {
public:
inline bool IsInobject() const {
return portion() != kBackingStore;
}
inline int offset() const {
return OffsetField::decode(value_);
}
inline Handle<String> name() const {
return name_;
}
static HObjectAccess ForHeapNumberValue() {
return HObjectAccess(kDouble, HeapNumber::kValueOffset);
}
static HObjectAccess ForElementsPointer() {
return HObjectAccess(kElementsPointer, JSObject::kElementsOffset);
}
static HObjectAccess ForArrayLength() {
return HObjectAccess(kArrayLengths, JSArray::kLengthOffset);
}
static HObjectAccess ForFixedArrayLength() {
return HObjectAccess(kArrayLengths, FixedArray::kLengthOffset);
}
static HObjectAccess ForPropertiesPointer() {
return HObjectAccess(kInobject, JSObject::kPropertiesOffset);
}
static HObjectAccess ForMap() {
return HObjectAccess(kMaps, JSObject::kMapOffset);
}
static HObjectAccess ForAllocationSitePayload() {
return HObjectAccess(kInobject, AllocationSiteInfo::kPayloadOffset);
}
// Create an access to an offset in a fixed array header.
static HObjectAccess ForFixedArrayHeader(int offset);
// Create an access to an in-object property in a JSObject.
static HObjectAccess ForJSObjectOffset(int offset);
// Create an access to an in-object property in a JSArray.
static HObjectAccess ForJSArrayOffset(int offset);
// Create an access to the backing store of an object.
static HObjectAccess ForBackingStoreOffset(int offset);
// Create an access to a resolved field (in-object or backing store).
static HObjectAccess ForField(Handle<Map> map,
LookupResult *lookup, Handle<String> name = Handle<String>::null());
void PrintTo(StringStream* stream);
protected:
void SetGVNFlags(HValue *instr, bool is_store);
private:
// internal use only; different parts of an object or array
enum Portion {
kMaps, // map of an object
kArrayLengths, // the length of an array
kElementsPointer, // elements pointer
kBackingStore, // some field in the backing store
kDouble, // some double field
kInobject // some other in-object field
};
HObjectAccess(Portion portion, int offset,
Handle<String> name = Handle<String>::null())
: value_(PortionField::encode(portion) | OffsetField::encode(offset)),
name_(name) {
ASSERT(this->offset() == offset); // offset should decode correctly
ASSERT(this->portion() == portion); // portion should decode correctly
}
class PortionField : public BitField<Portion, 0, 3> {};
class OffsetField : public BitField<int, 3, 29> {};
uint32_t value_; // encodes both portion and offset
Handle<String> name_;
friend class HLoadNamedField;
friend class HStoreNamedField;
inline Portion portion() const {
return PortionField::decode(value_);
}
};
class HLoadNamedField: public HTemplateInstruction<2> {
public:
HLoadNamedField(HValue* object, bool is_in_object,
Representation field_representation,
int offset, HValue* typecheck = NULL)
: is_in_object_(is_in_object),
field_representation_(field_representation),
offset_(offset) {
HLoadNamedField(HValue* object,
HObjectAccess access,
HValue* typecheck = NULL,
Representation field_representation
= Representation::Tagged())
: access_(access),
field_representation_(field_representation) {
ASSERT(object != NULL);
SetOperandAt(0, object);
SetOperandAt(1, typecheck != NULL ? typecheck : object);
......@@ -5225,31 +5325,7 @@ class HLoadNamedField: public HTemplateInstruction<2> {
} else {
set_representation(Representation::Tagged());
}
SetFlag(kUseGVN);
if (FLAG_track_double_fields && representation().IsDouble()) {
ASSERT(is_in_object);
ASSERT(offset == HeapNumber::kValueOffset);
SetGVNFlag(kDependsOnDoubleFields);
} else if (is_in_object) {
SetGVNFlag(kDependsOnInobjectFields);
SetGVNFlag(kDependsOnMaps);
} else {
SetGVNFlag(kDependsOnBackingStoreFields);
SetGVNFlag(kDependsOnMaps);
}
}
static HLoadNamedField* NewArrayLength(Zone* zone, HValue* object,
HValue* typecheck,
HType type = HType::Tagged()) {
Representation representation =
type.IsSmi() ? Representation::Smi() : Representation::Tagged();
HLoadNamedField* result = new(zone) HLoadNamedField(
object, true, representation, JSArray::kLengthOffset, typecheck);
result->set_type(type);
result->SetGVNFlag(kDependsOnArrayLengths);
result->ClearGVNFlag(kDependsOnInobjectFields);
return result;
access.SetGVNFlags(this, false);
}
HValue* object() { return OperandAt(0); }
......@@ -5259,9 +5335,10 @@ class HLoadNamedField: public HTemplateInstruction<2> {
}
bool HasTypeCheck() const { return OperandAt(0) != OperandAt(1); }
bool is_in_object() const { return is_in_object_; }
HObjectAccess access() const { return access_; }
bool is_in_object() const { return access_.IsInobject(); }
Representation field_representation() const { return representation_; }
int offset() const { return offset_; }
int offset() const { return access_.offset(); }
virtual Representation RequiredInputRepresentation(int index) {
return Representation::Tagged();
......@@ -5273,15 +5350,14 @@ class HLoadNamedField: public HTemplateInstruction<2> {
protected:
virtual bool DataEquals(HValue* other) {
HLoadNamedField* b = HLoadNamedField::cast(other);
return is_in_object_ == b->is_in_object_ && offset_ == b->offset_;
return is_in_object() == b->is_in_object() && offset() == b->offset();
}
private:
virtual bool IsDeletable() const { return true; }
bool is_in_object_;
HObjectAccess access_;
Representation field_representation_;
int offset_;
};
......@@ -5578,30 +5654,17 @@ class HLoadKeyedGeneric: public HTemplateInstruction<3> {
class HStoreNamedField: public HTemplateInstruction<2> {
public:
HStoreNamedField(HValue* obj,
Handle<Name> name,
HObjectAccess access,
HValue* val,
bool in_object,
Representation field_representation,
int offset)
: name_(name),
is_in_object_(in_object),
Representation field_representation
= Representation::Tagged())
: access_(access),
field_representation_(field_representation),
offset_(offset),
transition_unique_id_(),
new_space_dominator_(NULL) {
SetOperandAt(0, obj);
SetOperandAt(1, val);
SetFlag(kTrackSideEffectDominators);
if (FLAG_track_double_fields && field_representation.IsDouble()) {
SetGVNFlag(kChangesDoubleFields);
} else if (is_in_object_) {
SetGVNFlag(kChangesInobjectFields);
SetGVNFlag(kDependsOnNewSpacePromotion);
} else {
SetGVNFlag(kChangesBackingStoreFields);
SetGVNFlag(kDependsOnNewSpacePromotion);
}
SetFlag(kDeoptimizeOnUndefined);
access.SetGVNFlags(this, true);
}
DECLARE_CONCRETE_INSTRUCTION(StoreNamedField)
......@@ -5625,9 +5688,10 @@ class HStoreNamedField: public HTemplateInstruction<2> {
HValue* object() { return OperandAt(0); }
HValue* value() { return OperandAt(1); }
Handle<Name> name() const { return name_; }
bool is_in_object() const { return is_in_object_; }
int offset() const { return offset_; }
HObjectAccess access() const { return access_; }
Handle<String> name() const { return access_.name(); }
bool is_in_object() const { return access_.IsInobject(); }
int offset() const { return access_.offset(); }
Handle<Map> transition() const { return transition_; }
UniqueValueId transition_unique_id() const { return transition_unique_id_; }
void set_transition(Handle<Map> map) { transition_ = map; }
......@@ -5656,10 +5720,8 @@ class HStoreNamedField: public HTemplateInstruction<2> {
}
private:
Handle<Name> name_;
bool is_in_object_;
HObjectAccess access_;
Representation field_representation_;
int offset_;
Handle<Map> transition_;
UniqueValueId transition_unique_id_;
HValue* new_space_dominator_;
......
This diff is collapsed.
......@@ -992,11 +992,6 @@ class HGraphBuilder {
HValue* BuildCheckMap(HValue* obj, Handle<Map> map);
// Building common constructs
HLoadNamedField* DoBuildLoadNamedField(HValue* object,
bool inobject,
Representation representation,
int offset);
HInstruction* BuildExternalArrayElementAccess(
HValue* external_elements,
HValue* checked_key,
......@@ -1039,8 +1034,24 @@ class HGraphBuilder {
KeyedAccessStoreMode store_mode,
Representation checked_index_representation = Representation::None());
HInstruction* BuildStoreMap(HValue* object, HValue* map);
HInstruction* BuildStoreMap(HValue* object, Handle<Map> map);
HLoadNamedField* AddLoad(
HValue *object,
HObjectAccess access,
HValue *typecheck = NULL,
Representation representation = Representation::Tagged());
HLoadNamedField* BuildLoadNamedField(
HValue* object,
HObjectAccess access,
Representation representation);
HStoreNamedField* AddStore(
HValue *object,
HObjectAccess access,
HValue *val,
Representation representation = Representation::Tagged());
HStoreNamedField* AddStoreMapConstant(HValue *object, Handle<Map>);
HLoadNamedField* AddLoadElements(HValue *object, HValue *typecheck = NULL);
......@@ -1692,9 +1703,6 @@ class HOptimizedGraphBuilder: public HGraphBuilder, public AstVisitor {
bool is_store,
bool* has_side_effects);
HLoadNamedField* BuildLoadNamedField(HValue* object,
Handle<Map> map,
LookupResult* result);
HInstruction* BuildLoadNamedGeneric(HValue* object,
Handle<String> name,
Property* expr);
......
......@@ -416,8 +416,7 @@ LOperand* LPlatformChunk::GetNextSpillSlot(bool is_double) {
void LStoreNamedField::PrintDataTo(StringStream* stream) {
object()->PrintTo(stream);
stream->Add(".");
stream->Add(*String::cast(*name())->ToCString());
hydrogen()->access().PrintTo(stream);
stream->Add(" <- ");
value()->PrintTo(stream);
}
......
......@@ -373,8 +373,7 @@ void LAccessArgumentsAt::PrintDataTo(StringStream* stream) {
void LStoreNamedField::PrintDataTo(StringStream* stream) {
object()->PrintTo(stream);
stream->Add(".");
stream->Add(*String::cast(*name())->ToCString());
hydrogen()->access().PrintTo(stream);
stream->Add(" <- ");
value()->PrintTo(stream);
}
......
......@@ -341,6 +341,7 @@ Handle<Code> StubCache::ComputeKeyedLoadField(Handle<Name> name,
PropertyIndex field,
Representation representation) {
if (receiver.is_identical_to(holder)) {
// TODO(titzer): this should use an HObjectAccess
KeyedLoadFieldStub stub(field.is_inobject(holder),
field.translate(holder),
representation);
......
......@@ -391,8 +391,7 @@ LOperand* LPlatformChunk::GetNextSpillSlot(bool is_double) {
void LStoreNamedField::PrintDataTo(StringStream* stream) {
object()->PrintTo(stream);
stream->Add(".");
stream->Add(*String::cast(*name())->ToCString());
hydrogen()->access().PrintTo(stream);
stream->Add(" <- ");
value()->PrintTo(stream);
}
......
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