[runtime] Stop using Map::unused_property_fields() byte.

The unused properties fields number is calculatable via used in-object
properties count and we can drop it now.

Bug: chromium:774644
Change-Id: I7388af7772a8e793593fabc46527886cf2e36095
Reviewed-on: https://chromium-review.googlesource.com/781465
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49542}
This commit is contained in:
Igor Sheludko 2017-11-21 14:24:51 +01:00 committed by Commit Bot
parent 572210e731
commit d8c355fcac
6 changed files with 76 additions and 91 deletions

View File

@ -2569,10 +2569,11 @@ void CodeStubAssembler::InitializeJSObjectBodyWithSlackTracking(
MachineRepresentation::kWord32);
STATIC_ASSERT(Map::kSlackTrackingCounterEnd == 1);
Node* unused_fields = LoadObjectField(map, Map::kUnusedPropertyFieldsOffset,
MachineType::Uint8());
Node* used_size = IntPtrSub(
instance_size, TimesPointerSize(ChangeUint32ToWord(unused_fields)));
// The object still has in-object slack therefore the |unsed_or_unused|
// field contain the "used" value.
Node* used_size = TimesPointerSize(ChangeUint32ToWord(
LoadObjectField(map, Map::kUsedOrUnusedInstanceSizeInWordsOffset,
MachineType::Uint8())));
Comment("iInitialize filler fields");
InitializeFieldsWithRoot(object, used_size, instance_size,

View File

@ -2396,6 +2396,7 @@ AllocationResult Heap::AllocatePartialMap(InstanceType instance_type,
map->set_map_after_allocation(reinterpret_cast<Map*>(root(kMetaMapRootIndex)),
SKIP_WRITE_BARRIER);
map->set_instance_type(instance_type);
WRITE_BYTE_FIELD(map, Map::kSoonToBeInstanceTypeTooOffset, 0);
map->set_instance_size(instance_size);
// Initialize to only containing tagged fields.
if (FLAG_unbox_double_fields) {
@ -2431,6 +2432,7 @@ AllocationResult Heap::AllocateMap(InstanceType instance_type,
map->set_prototype(null_value(), SKIP_WRITE_BARRIER);
map->set_constructor_or_backpointer(null_value(), SKIP_WRITE_BARRIER);
map->set_instance_size(instance_size);
WRITE_BYTE_FIELD(map, Map::kSoonToBeInstanceTypeTooOffset, 0);
if (map->IsJSObjectMap()) {
map->SetInObjectPropertiesStartInWords(instance_size / kPointerSize -
inobject_properties);

View File

@ -3140,33 +3140,33 @@ void Map::set_instance_type(InstanceType value) {
WRITE_BYTE_FIELD(this, kInstanceTypeOffset, value);
}
int Map::unused_property_fields() const {
return READ_BYTE_FIELD(this, kUnusedPropertyFieldsOffset);
}
int Map::UnusedPropertyFields() const {
VerifyUnusedPropertyFields();
return unused_property_fields();
int value = used_or_unused_instance_size_in_words();
DCHECK_IMPLIES(!IsJSObjectMap(), value == 0);
int unused;
if (value >= JSObject::kFieldsAdded) {
unused = instance_size_in_words() - value;
} else {
// For out of object properties "used_or_unused_instance_size_in_words"
// byte encodes the slack in the property array.
unused = value;
}
return unused;
}
void Map::set_unused_property_fields(int value) {
WRITE_BYTE_FIELD(this, kUnusedPropertyFieldsOffset, Min(value, 255));
int Map::used_or_unused_instance_size_in_words() const {
return READ_BYTE_FIELD(this, kUsedOrUnusedInstanceSizeInWordsOffset);
}
int Map::used_instance_size_in_words() const {
return READ_BYTE_FIELD(this, kUsedInstanceSizeInWordsOffset);
}
void Map::set_used_instance_size_in_words(int value) {
void Map::set_used_or_unused_instance_size_in_words(int value) {
DCHECK_LE(0, value);
DCHECK_LE(value, 255);
WRITE_BYTE_FIELD(this, kUsedInstanceSizeInWordsOffset,
WRITE_BYTE_FIELD(this, kUsedOrUnusedInstanceSizeInWordsOffset,
static_cast<byte>(value));
VerifyUnusedPropertyFields();
}
int Map::UsedInstanceSize() const {
int words = used_instance_size_in_words();
int words = used_or_unused_instance_size_in_words();
if (words < JSObject::kFieldsAdded) {
// All in-object properties are used and the words is tracking the slack
// in the property array.
@ -3179,55 +3179,53 @@ void Map::SetInObjectUnusedPropertyFields(int value) {
STATIC_ASSERT(JSObject::kFieldsAdded == JSObject::kHeaderSize / kPointerSize);
if (!IsJSObjectMap()) {
DCHECK_EQ(0, value);
set_unused_property_fields(0);
set_used_instance_size_in_words(0);
set_used_or_unused_instance_size_in_words(0);
DCHECK_EQ(0, UnusedPropertyFields());
return;
}
DCHECK_LE(0, value);
DCHECK_LE(value, GetInObjectProperties());
set_unused_property_fields(value);
int used_inobject_properties = GetInObjectProperties() - value;
set_used_instance_size_in_words(
set_used_or_unused_instance_size_in_words(
GetInObjectPropertyOffset(used_inobject_properties) / kPointerSize);
VerifyUnusedPropertyFields();
DCHECK_EQ(value, UnusedPropertyFields());
}
void Map::SetOutOfObjectUnusedPropertyFields(int value) {
STATIC_ASSERT(JSObject::kFieldsAdded == JSObject::kHeaderSize / kPointerSize);
DCHECK_LE(0, value);
DCHECK_LT(value, JSObject::kFieldsAdded);
set_unused_property_fields(value);
// For out of object properties "used_instance_size_in_words" byte encodes
// the slack in the property array.
set_used_instance_size_in_words(value);
VerifyUnusedPropertyFields();
set_used_or_unused_instance_size_in_words(value);
DCHECK_EQ(value, UnusedPropertyFields());
}
void Map::CopyUnusedPropertyFields(Map* map) {
set_unused_property_fields(map->unused_property_fields());
set_used_instance_size_in_words(map->used_instance_size_in_words());
VerifyUnusedPropertyFields();
set_used_or_unused_instance_size_in_words(
map->used_or_unused_instance_size_in_words());
DCHECK_EQ(UnusedPropertyFields(), map->UnusedPropertyFields());
}
void Map::AccountAddedPropertyField() {
// Update used instance size and unused property fields number.
STATIC_ASSERT(JSObject::kFieldsAdded == JSObject::kHeaderSize / kPointerSize);
// Update unused_property_fields.
int new_unused = unused_property_fields() - 1;
#ifdef DEBUG
int new_unused = UnusedPropertyFields() - 1;
if (new_unused < 0) new_unused += JSObject::kFieldsAdded;
set_unused_property_fields(new_unused);
// Update used_instance_size_in_words.
int value = used_instance_size_in_words();
#endif
int value = used_or_unused_instance_size_in_words();
if (value >= JSObject::kFieldsAdded) {
if (value == instance_size_in_words()) {
AccountAddedOutOfObjectPropertyField(0);
} else {
// The property is added in-object, so simply increment the counter.
set_used_instance_size_in_words(value + 1);
set_used_or_unused_instance_size_in_words(value + 1);
}
} else {
AccountAddedOutOfObjectPropertyField(value);
}
VerifyUnusedPropertyFields();
DCHECK_EQ(new_unused, UnusedPropertyFields());
}
void Map::AccountAddedOutOfObjectPropertyField(int unused_in_property_array) {
@ -3237,26 +3235,8 @@ void Map::AccountAddedOutOfObjectPropertyField(int unused_in_property_array) {
}
DCHECK_GE(unused_in_property_array, 0);
DCHECK_LT(unused_in_property_array, JSObject::kFieldsAdded);
set_used_instance_size_in_words(unused_in_property_array);
}
void Map::VerifyUnusedPropertyFields() const {
#ifdef DEBUG
if (!IsJSObjectMap()) {
DCHECK_EQ(unused_property_fields(), used_instance_size_in_words());
} else {
int value = used_instance_size_in_words();
int unused;
if (value >= JSObject::kFieldsAdded) {
unused = instance_size_in_words() - value;
} else {
// For out of object properties "used_instance_size_in_words" byte encodes
// the slack in the property array.
unused = value;
}
DCHECK_EQ(unused_property_fields(), unused);
}
#endif
set_used_or_unused_instance_size_in_words(unused_in_property_array);
DCHECK_EQ(unused_in_property_array, UnusedPropertyFields());
}
byte Map::bit_field() const { return READ_BYTE_FIELD(this, kBitFieldOffset); }

View File

@ -12317,15 +12317,16 @@ static void GetMinInobjectSlack(Map* map, void* data) {
static void ShrinkInstanceSize(Map* map, void* data) {
#ifdef DEBUG
int old_visitor_id = Map::GetVisitorId(map);
#endif
int slack = *reinterpret_cast<int*>(data);
DCHECK_GE(slack, 0);
map->set_unused_property_fields(map->unused_property_fields() - slack);
#ifdef DEBUG
int old_visitor_id = Map::GetVisitorId(map);
int new_unused = map->UnusedPropertyFields() - slack;
#endif
map->set_instance_size(map->instance_size() - slack * kPointerSize);
map->set_construction_counter(Map::kNoSlackTracking);
DCHECK_EQ(old_visitor_id, Map::GetVisitorId(map));
DCHECK_EQ(new_unused, map->UnusedPropertyFields());
}
static void StopSlackTracking(Map* map, void* data) {

View File

@ -95,10 +95,11 @@ typedef std::vector<Handle<Map>> MapHandles;
// | | If Map for an Object type: |
// | | inobject properties start offset in words |
// +----------+---------------------------------------------+
// | Byte | [used_instance_size_in_words] |
// | Byte | [used_or_unused_instance_size_in_words] |
// | | For JSObject in fast mode this byte encodes |
// | | the size of the object that includes only |
// | | the used property fields. |
// | | the used property fields or the slack size |
// | | in properties backing store. |
// +----------+---------------------------------------------+
// | Byte | [visitor_id] |
// +----+----------+---------------------------------------------+
@ -106,8 +107,8 @@ typedef std::vector<Handle<Map>> MapHandles;
// `---+----------+---------------------------------------------+
// | Byte | [instance_type] |
// +----------+---------------------------------------------+
// | Byte | [unused_property_fields] number of unused |
// | | property fields in JSObject (for fast-mode) |
// | Byte | Free byte, soon will be used as a second |
// | | byte of [instance_type]. |
// +----------+---------------------------------------------+
// | Byte | [bit_field] |
// | | - has_non_instance_prototype (bit 0) |
@ -199,24 +200,13 @@ class Map : public HeapObject {
inline InstanceType instance_type() const;
inline void set_instance_type(InstanceType value);
// Tells how many unused property fields are available in the
// instance (only used for JSObject in fast mode).
inline int unused_property_fields() const;
inline void set_unused_property_fields(int value);
// This byte encodes the instance size without the slack.
// Let H be JSObject::kHeaderSize / kPointerSize.
// If value >= H then:
// - all field properties are stored in the object.
// - there is no property array.
// - value * kPointerSize is the actual object size without the slack.
// Otherwise:
// - there is no slack in the object.
// - the property array has value slack slots.
// Note that this encoding requires that H = JSObject::kFieldsAdded.
DECL_INT_ACCESSORS(used_instance_size_in_words)
// Returns the size of the used in-object area including object header
// (only used for JSObject in fast mode, for the other kinds of objects it
// is equal to the instance size).
inline int UsedInstanceSize() const;
// Tells how many unused property fields (in-object or out-of object) are
// available in the instance (only used for JSObject in fast mode).
inline int UnusedPropertyFields() const;
// Updates the counters tracking unused fields in the object.
inline void SetInObjectUnusedPropertyFields(int unused_property_fields);
@ -226,7 +216,6 @@ class Map : public HeapObject {
inline void AccountAddedPropertyField();
inline void AccountAddedOutOfObjectPropertyField(
int unused_in_property_array);
inline void VerifyUnusedPropertyFields() const;
// Bit field.
inline byte bit_field() const;
@ -738,12 +727,11 @@ class Map : public HeapObject {
/* Raw data fields. */ \
V(kInstanceSizeInWordsOffset, kUInt8Size) \
V(kInObjectPropertiesStartOrConstructorFunctionIndexOffset, kUInt8Size) \
V(kUsedInstanceSizeInWordsOffset, kUInt8Size) \
V(kUsedOrUnusedInstanceSizeInWordsOffset, kUInt8Size) \
V(kVisitorIdOffset, kUInt8Size) \
V(kInstanceTypeOffset, kUInt8Size) \
/* TODO(ulan): Free this byte after unused_property_fields are */ \
/* computed using the used_instance_size_in_words() byte. */ \
V(kUnusedPropertyFieldsOffset, kUInt8Size) \
/* TODO(ishell): Extend kInstanceTypeOffset field to two bytes. */ \
V(kSoonToBeInstanceTypeTooOffset, kUInt8Size) \
V(kBitFieldOffset, kUInt8Size) \
V(kBitField2Offset, kUInt8Size) \
V(kBitField3Offset, kUInt32Size) \
@ -810,6 +798,19 @@ class Map : public HeapObject {
static VisitorId GetVisitorId(Map* map);
private:
// This byte encodes either the instance size without the in-object slack or
// the slack size in properties backing store.
// Let H be JSObject::kHeaderSize / kPointerSize.
// If value >= H then:
// - all field properties are stored in the object.
// - there is no property array.
// - value * kPointerSize is the actual object size without the slack.
// Otherwise:
// - there is no slack in the object.
// - the property array has value slack slots.
// Note that this encoding requires that H = JSObject::kFieldsAdded.
DECL_INT_ACCESSORS(used_or_unused_instance_size_in_words)
// Returns the map that this (root) map transitions to if its elements_kind
// is changed to |elements_kind|, or |nullptr| if no such map is cached yet.
Map* LookupElementsTransitionMap(ElementsKind elements_kind);

View File

@ -102,7 +102,7 @@ bool IsObjectShrinkable(JSObject* obj) {
CcTest::i_isolate()->factory()->one_pointer_filler_map();
int inobject_properties = obj->map()->GetInObjectProperties();
int unused = obj->map()->unused_property_fields();
int unused = obj->map()->UnusedPropertyFields();
if (unused == 0) return false;
for (int i = inobject_properties - unused; i < inobject_properties; i++) {
@ -227,13 +227,13 @@ TEST(JSObjectComplex) {
CHECK(!IsObjectShrinkable(*obj5));
CHECK_EQ(5, obj1->map()->GetInObjectProperties());
CHECK_EQ(4, obj1->map()->unused_property_fields());
CHECK_EQ(4, obj1->map()->UnusedPropertyFields());
CHECK_EQ(5, obj3->map()->GetInObjectProperties());
CHECK_EQ(2, obj3->map()->unused_property_fields());
CHECK_EQ(2, obj3->map()->UnusedPropertyFields());
CHECK_EQ(5, obj5->map()->GetInObjectProperties());
CHECK_EQ(0, obj5->map()->unused_property_fields());
CHECK_EQ(0, obj5->map()->UnusedPropertyFields());
// Since slack tracking is complete, the new objects should not be shrinkable.
obj1 = CompileRunI<JSObject>("new A(1);");