Skip to content

Commit 532c16e

Browse files
camillobruniCommit bot
authored andcommitted
[runtime] Object.create(null) creates a slow object
Object.create(null) is most likely to be used for dictionary-like objects. Hence it would be beneficial to directly create a slow-mode object and avoid additional overhead later-on. BUG= Review-Url: https://codereview.chromium.org/2430273007 Cr-Commit-Position: refs/heads/master@{#40551}
1 parent 763dd40 commit 532c16e

File tree

15 files changed

+224
-29
lines changed

15 files changed

+224
-29
lines changed

src/bootstrapper.cc

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3528,12 +3528,13 @@ bool Genesis::InstallNatives(GlobalContextType context_type) {
35283528
HeapObject::cast(object_function->initial_map()->prototype())->map());
35293529

35303530
// Set up the map for Object.create(null) instances.
3531-
Handle<Map> object_with_null_prototype_map =
3531+
Handle<Map> slow_object_with_null_prototype_map =
35323532
Map::CopyInitialMap(handle(object_function->initial_map(), isolate()));
3533-
Map::SetPrototype(object_with_null_prototype_map,
3533+
slow_object_with_null_prototype_map->set_dictionary_map(true);
3534+
Map::SetPrototype(slow_object_with_null_prototype_map,
35343535
isolate()->factory()->null_value());
3535-
native_context()->set_object_with_null_prototype_map(
3536-
*object_with_null_prototype_map);
3536+
native_context()->set_slow_object_with_null_prototype_map(
3537+
*slow_object_with_null_prototype_map);
35373538

35383539
// Store the map for the %StringPrototype% after the natives has been compiled
35393540
// and the String function has been set up.

src/builtins/builtins-object.cc

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -494,20 +494,24 @@ void Builtins::Generate_ObjectCreate(CodeStubAssembler* a) {
494494
a->Bind(&no_properties);
495495
{
496496
Variable map(a, MachineRepresentation::kTagged);
497+
Variable properties(a, MachineRepresentation::kTagged);
497498
Label non_null_proto(a), instantiate_map(a), good(a);
498499

499500
a->Branch(a->WordEqual(prototype, a->NullConstant()), &good,
500501
&non_null_proto);
501502

502503
a->Bind(&good);
503504
{
504-
map.Bind(a->LoadContextElement(context,
505-
Context::OBJECT_WITH_NULL_PROTOTYPE_MAP));
505+
map.Bind(a->LoadContextElement(
506+
context, Context::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP));
507+
properties.Bind(
508+
a->AllocateNameDictionary(NameDictionary::kInitialCapacity));
506509
a->Goto(&instantiate_map);
507510
}
508511

509512
a->Bind(&non_null_proto);
510513
{
514+
properties.Bind(a->EmptyFixedArrayConstant());
511515
Node* object_function =
512516
a->LoadContextElement(context, Context::OBJECT_FUNCTION_INDEX);
513517
Node* object_function_map = a->LoadObjectField(
@@ -528,7 +532,8 @@ void Builtins::Generate_ObjectCreate(CodeStubAssembler* a) {
528532

529533
a->Bind(&instantiate_map);
530534
{
531-
Node* instance = a->AllocateJSObjectFromMap(map.value());
535+
Node* instance =
536+
a->AllocateJSObjectFromMap(map.value(), properties.value());
532537
a->Return(instance);
533538
}
534539
}

src/code-stub-assembler.cc

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,24 @@ Node* CodeStubAssembler::IntPtrSubFoldConstants(Node* left, Node* right) {
127127
return IntPtrSub(left, right);
128128
}
129129

130+
Node* CodeStubAssembler::IntPtrRoundUpToPowerOfTwo32(Node* value) {
131+
Comment("IntPtrRoundUpToPowerOfTwo32");
132+
CSA_ASSERT(UintPtrLessThanOrEqual(value, IntPtrConstant(0x80000000u)));
133+
value = IntPtrSub(value, IntPtrConstant(1));
134+
for (int i = 1; i <= 16; i *= 2) {
135+
value = WordOr(value, WordShr(value, IntPtrConstant(i)));
136+
}
137+
return IntPtrAdd(value, IntPtrConstant(1));
138+
}
139+
140+
Node* CodeStubAssembler::WordIsPowerOfTwo(Node* value) {
141+
// value && !(value & (value - 1))
142+
return WordEqual(
143+
Select(WordEqual(value, IntPtrConstant(0)), IntPtrConstant(1),
144+
WordAnd(value, IntPtrSub(value, IntPtrConstant(1)))),
145+
IntPtrConstant(0));
146+
}
147+
130148
Node* CodeStubAssembler::Float64Round(Node* x) {
131149
Node* one = Float64Constant(1.0);
132150
Node* one_half = Float64Constant(0.5);
@@ -775,6 +793,7 @@ Node* CodeStubAssembler::AllocateRawAligned(Node* size_in_bytes,
775793
}
776794

777795
Node* CodeStubAssembler::Allocate(Node* size_in_bytes, AllocationFlags flags) {
796+
Comment("Allocate");
778797
bool const new_space = !(flags & kPretenured);
779798
Node* top_address = ExternalConstant(
780799
new_space
@@ -1105,6 +1124,12 @@ Node* CodeStubAssembler::IsSpecialReceiverInstanceType(Node* instance_type) {
11051124
Int32Constant(LAST_SPECIAL_RECEIVER_TYPE));
11061125
}
11071126

1127+
Node* CodeStubAssembler::IsDictionaryMap(Node* map) {
1128+
Node* bit_field3 = LoadMapBitField3(map);
1129+
return Word32NotEqual(IsSetWord32<Map::DictionaryMap>(bit_field3),
1130+
Int32Constant(0));
1131+
}
1132+
11081133
Node* CodeStubAssembler::LoadNameHashField(Node* name) {
11091134
return LoadObjectField(name, Name::kHashFieldOffset, MachineType::Uint32());
11101135
}
@@ -1659,6 +1684,57 @@ Node* CodeStubAssembler::AllocateRegExpResult(Node* context, Node* length,
16591684
return result;
16601685
}
16611686

1687+
Node* CodeStubAssembler::AllocateNameDictionary(int at_least_space_for) {
1688+
return AllocateNameDictionary(IntPtrConstant(at_least_space_for));
1689+
}
1690+
1691+
Node* CodeStubAssembler::AllocateNameDictionary(Node* at_least_space_for) {
1692+
CSA_ASSERT(UintPtrLessThanOrEqual(
1693+
at_least_space_for, IntPtrConstant(NameDictionary::kMaxCapacity)));
1694+
1695+
Node* capacity = HashTableComputeCapacity(at_least_space_for);
1696+
CSA_ASSERT(WordIsPowerOfTwo(capacity));
1697+
1698+
Node* length = EntryToIndex<NameDictionary>(capacity);
1699+
Node* store_size =
1700+
IntPtrAddFoldConstants(WordShl(length, IntPtrConstant(kPointerSizeLog2)),
1701+
IntPtrConstant(NameDictionary::kHeaderSize));
1702+
1703+
Node* result = Allocate(store_size);
1704+
Comment("Initialize NameDictionary");
1705+
// Initialize FixedArray fields.
1706+
StoreObjectFieldRoot(result, FixedArray::kMapOffset,
1707+
Heap::kHashTableMapRootIndex);
1708+
StoreObjectFieldNoWriteBarrier(result, FixedArray::kLengthOffset,
1709+
SmiFromWord(length));
1710+
// Initialized HashTable fields.
1711+
Node* zero = SmiConstant(0);
1712+
StoreFixedArrayElement(result, NameDictionary::kNumberOfElementsIndex, zero,
1713+
SKIP_WRITE_BARRIER);
1714+
StoreFixedArrayElement(result, NameDictionary::kNumberOfDeletedElementsIndex,
1715+
zero, SKIP_WRITE_BARRIER);
1716+
StoreFixedArrayElement(result, NameDictionary::kCapacityIndex,
1717+
SmiTag(capacity), SKIP_WRITE_BARRIER);
1718+
// Initialize Dictionary fields.
1719+
Node* filler = LoadRoot(Heap::kUndefinedValueRootIndex);
1720+
StoreFixedArrayElement(result, NameDictionary::kMaxNumberKeyIndex, filler,
1721+
SKIP_WRITE_BARRIER);
1722+
StoreFixedArrayElement(result, NameDictionary::kNextEnumerationIndexIndex,
1723+
SmiConstant(PropertyDetails::kInitialIndex),
1724+
SKIP_WRITE_BARRIER);
1725+
1726+
// Initialize NameDictionary elements.
1727+
Node* start_address = IntPtrAdd(
1728+
result, IntPtrConstant(NameDictionary::OffsetOfElementAt(
1729+
NameDictionary::kElementsStartIndex) -
1730+
kHeapObjectTag));
1731+
Node* end_address = IntPtrAdd(
1732+
result,
1733+
IntPtrSubFoldConstants(store_size, IntPtrConstant(kHeapObjectTag)));
1734+
StoreFieldsNoWriteBarrier(start_address, end_address, filler);
1735+
return result;
1736+
}
1737+
16621738
Node* CodeStubAssembler::AllocateJSObjectFromMap(Node* map, Node* properties,
16631739
Node* elements) {
16641740
Node* size =
@@ -1676,6 +1752,7 @@ void CodeStubAssembler::InitializeJSObjectFromMap(Node* object, Node* map,
16761752
// This helper assumes that the object is in new-space, as guarded by the
16771753
// check in AllocatedJSObjectFromMap.
16781754
if (properties == nullptr) {
1755+
CSA_ASSERT(Word32BinaryNot(IsDictionaryMap((map))));
16791756
StoreObjectFieldRoot(object, JSObject::kPropertiesOffset,
16801757
Heap::kEmptyFixedArrayRootIndex);
16811758
} else {
@@ -3835,6 +3912,19 @@ Node* CodeStubAssembler::EntryToIndex(Node* entry, int field_index) {
38353912
field_index));
38363913
}
38373914

3915+
template Node* CodeStubAssembler::EntryToIndex<NameDictionary>(Node*, int);
3916+
template Node* CodeStubAssembler::EntryToIndex<GlobalDictionary>(Node*, int);
3917+
3918+
Node* CodeStubAssembler::HashTableComputeCapacity(Node* at_least_space_for) {
3919+
Node* capacity = IntPtrRoundUpToPowerOfTwo32(
3920+
WordShl(at_least_space_for, IntPtrConstant(1)));
3921+
return IntPtrMax(capacity, IntPtrConstant(HashTableBase::kMinCapacity));
3922+
}
3923+
3924+
Node* CodeStubAssembler::IntPtrMax(Node* left, Node* right) {
3925+
return Select(IntPtrGreaterThanOrEqual(left, right), left, right);
3926+
}
3927+
38383928
template <typename Dictionary>
38393929
void CodeStubAssembler::NameDictionaryLookup(Node* dictionary,
38403930
Node* unique_name, Label* if_found,

src/code-stub-assembler.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
108108
compiler::Node* right);
109109
compiler::Node* IntPtrSubFoldConstants(compiler::Node* left,
110110
compiler::Node* right);
111+
// Round the 32bits payload of the provided word up to the next power of two.
112+
compiler::Node* IntPtrRoundUpToPowerOfTwo32(compiler::Node* value);
113+
compiler::Node* IntPtrMax(compiler::Node* left, compiler::Node* right);
111114

112115
// Float64 operations.
113116
compiler::Node* Float64Ceil(compiler::Node* x);
@@ -167,6 +170,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
167170
compiler::Node* WordIsPositiveSmi(compiler::Node* a);
168171
// Check that a word has a word-aligned address.
169172
compiler::Node* WordIsWordAligned(compiler::Node* word);
173+
compiler::Node* WordIsPowerOfTwo(compiler::Node* value);
170174

171175
void BranchIfSmiEqual(compiler::Node* a, compiler::Node* b, Label* if_true,
172176
Label* if_false) {
@@ -286,6 +290,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
286290
// JSProxy or an object with interceptors.
287291
compiler::Node* IsSpecialReceiverMap(compiler::Node* map);
288292
compiler::Node* IsSpecialReceiverInstanceType(compiler::Node* instance_type);
293+
// Check if the map is set for slow properties.
294+
compiler::Node* IsDictionaryMap(compiler::Node* map);
289295

290296
// Load the hash field of a name as an uint32 value.
291297
compiler::Node* LoadNameHashField(compiler::Node* name);
@@ -359,6 +365,14 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
359365
compiler::Node* StoreObjectFieldRoot(compiler::Node* object, int offset,
360366
Heap::RootListIndex root);
361367
// Store an array element to a FixedArray.
368+
compiler::Node* StoreFixedArrayElement(
369+
compiler::Node* object, int index, compiler::Node* value,
370+
WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER,
371+
ParameterMode parameter_mode = INTEGER_PARAMETERS) {
372+
return StoreFixedArrayElement(object, Int32Constant(index), value,
373+
barrier_mode, parameter_mode);
374+
}
375+
362376
compiler::Node* StoreFixedArrayElement(
363377
compiler::Node* object, compiler::Node* index, compiler::Node* value,
364378
WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER,
@@ -431,6 +445,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
431445
compiler::Node* index,
432446
compiler::Node* input);
433447

448+
compiler::Node* AllocateNameDictionary(int capacity);
449+
compiler::Node* AllocateNameDictionary(compiler::Node* capacity);
450+
434451
compiler::Node* AllocateJSObjectFromMap(compiler::Node* map,
435452
compiler::Node* properties = nullptr,
436453
compiler::Node* elements = nullptr);
@@ -722,6 +739,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
722739
compiler::Node* EntryToIndex(compiler::Node* entry) {
723740
return EntryToIndex<Dictionary>(entry, Dictionary::kEntryKeyIndex);
724741
}
742+
// Calculate a valid size for the a hash table.
743+
compiler::Node* HashTableComputeCapacity(compiler::Node* at_least_space_for);
725744

726745
// Looks up an entry in a NameDictionaryBase successor. If the entry is found
727746
// control goes to {if_found} and {var_name_index} contains an index of the

src/compiler/code-assembler.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ Node* CodeAssembler::SmiConstant(Smi* value) {
110110
return BitcastWordToTaggedSigned(IntPtrConstant(bit_cast<intptr_t>(value)));
111111
}
112112

113+
Node* CodeAssembler::SmiConstant(int value) {
114+
return SmiConstant(Smi::FromInt(value));
115+
}
116+
113117
Node* CodeAssembler::HeapConstant(Handle<HeapObject> object) {
114118
return raw_assembler_->HeapConstant(object);
115119
}

src/compiler/code-assembler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ class V8_EXPORT_PRIVATE CodeAssembler {
222222
Node* IntPtrConstant(intptr_t value);
223223
Node* NumberConstant(double value);
224224
Node* SmiConstant(Smi* value);
225+
Node* SmiConstant(int value);
225226
Node* HeapConstant(Handle<HeapObject> object);
226227
Node* BooleanConstant(bool value);
227228
Node* ExternalConstant(ExternalReference address);

src/contexts.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,8 @@ enum ContextLookupFlags {
263263
V(NORMALIZED_MAP_CACHE_INDEX, Object, normalized_map_cache) \
264264
V(NUMBER_FUNCTION_INDEX, JSFunction, number_function) \
265265
V(OBJECT_FUNCTION_INDEX, JSFunction, object_function) \
266-
V(OBJECT_WITH_NULL_PROTOTYPE_MAP, Map, object_with_null_prototype_map) \
266+
V(SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP, Map, \
267+
slow_object_with_null_prototype_map) \
267268
V(OBJECT_FUNCTION_PROTOTYPE_MAP_INDEX, Map, object_function_prototype_map) \
268269
V(OPAQUE_REFERENCE_FUNCTION_INDEX, JSFunction, opaque_reference_function) \
269270
V(PROXY_CALLABLE_MAP_INDEX, Map, proxy_callable_map) \

src/objects-inl.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3173,7 +3173,6 @@ void HashTableBase::ElementsRemoved(int n) {
31733173

31743174
// static
31753175
int HashTableBase::ComputeCapacity(int at_least_space_for) {
3176-
const int kMinCapacity = 4;
31773176
int capacity = base::bits::RoundUpToPowerOfTwo32(at_least_space_for * 2);
31783177
return Max(capacity, kMinCapacity);
31793178
}
@@ -7577,7 +7576,7 @@ void JSReceiver::initialize_properties() {
75777576

75787577

75797578
bool JSReceiver::HasFastProperties() {
7580-
DCHECK(properties()->IsDictionary() == map()->is_dictionary_map());
7579+
DCHECK_EQ(properties()->IsDictionary(), map()->is_dictionary_map());
75817580
return !properties()->IsDictionary();
75827581
}
75837582

src/objects.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3432,7 +3432,8 @@ void MigrateFastToSlow(Handle<JSObject> object, Handle<Map> new_map,
34323432
if (expected_additional_properties > 0) {
34333433
property_count += expected_additional_properties;
34343434
} else {
3435-
property_count += 2; // Make space for two more properties.
3435+
// Make space for two more properties.
3436+
property_count += NameDictionary::kInitialCapacity;
34363437
}
34373438
Handle<NameDictionary> dictionary =
34383439
NameDictionary::New(isolate, property_count);
@@ -16774,7 +16775,8 @@ Handle<Derived> HashTable<Derived, Shape, Key>::New(
1677416775
MinimumCapacity capacity_option,
1677516776
PretenureFlag pretenure) {
1677616777
DCHECK(0 <= at_least_space_for);
16777-
DCHECK(!capacity_option || base::bits::IsPowerOfTwo32(at_least_space_for));
16778+
DCHECK_IMPLIES(capacity_option == USE_CUSTOM_MINIMUM_CAPACITY,
16779+
base::bits::IsPowerOfTwo32(at_least_space_for));
1677816780

1677916781
int capacity = (capacity_option == USE_CUSTOM_MINIMUM_CAPACITY)
1678016782
? at_least_space_for

src/objects.h

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3489,6 +3489,9 @@ class HashTableBase : public FixedArray {
34893489
// Constant used for denoting a absent entry.
34903490
static const int kNotFound = -1;
34913491

3492+
// Minimum capacity for newly created hash tables.
3493+
static const int kMinCapacity = 4;
3494+
34923495
protected:
34933496
// Update the number of elements in the hash table.
34943497
inline void SetNumberOfElements(int nof);
@@ -3566,8 +3569,11 @@ class HashTable : public HashTableBase {
35663569
static const int kEntryKeyIndex = 0;
35673570
static const int kElementsStartOffset =
35683571
kHeaderSize + kElementsStartIndex * kPointerSize;
3569-
static const int kCapacityOffset =
3570-
kHeaderSize + kCapacityIndex * kPointerSize;
3572+
// Maximal capacity of HashTable. Based on maximal length of underlying
3573+
// FixedArray. Staying below kMaxCapacity also ensures that EntryToIndex
3574+
// cannot overflow.
3575+
static const int kMaxCapacity =
3576+
(FixedArray::kMaxLength - kElementsStartIndex) / kEntrySize;
35713577

35723578
// Returns the index for an entry (of the key)
35733579
static inline int EntryToIndex(int entry) {
@@ -3604,12 +3610,6 @@ class HashTable : public HashTableBase {
36043610
set(kCapacityIndex, Smi::FromInt(capacity));
36053611
}
36063612

3607-
// Maximal capacity of HashTable. Based on maximal length of underlying
3608-
// FixedArray. Staying below kMaxCapacity also ensures that EntryToIndex
3609-
// cannot overflow.
3610-
static const int kMaxCapacity =
3611-
(FixedArray::kMaxLength - kElementsStartOffset) / kEntrySize;
3612-
36133613
private:
36143614
// Returns _expected_ if one of entries given by the first _probe_ probes is
36153615
// equal to _expected_. Otherwise, returns the entry given by the probe
@@ -3838,23 +3838,22 @@ class Dictionary: public HashTable<Derived, Shape, Key> {
38383838
static Handle<FixedArray> BuildIterationIndicesArray(
38393839
Handle<Derived> dictionary);
38403840

3841+
static const int kMaxNumberKeyIndex = DerivedHashTable::kPrefixStartIndex;
3842+
static const int kNextEnumerationIndexIndex = kMaxNumberKeyIndex + 1;
3843+
38413844
protected:
38423845
// Generic at put operation.
38433846
MUST_USE_RESULT static Handle<Derived> AtPut(
38443847
Handle<Derived> dictionary,
38453848
Key key,
38463849
Handle<Object> value);
3847-
38483850
// Add entry to dictionary. Returns entry value.
38493851
static int AddEntry(Handle<Derived> dictionary, Key key, Handle<Object> value,
38503852
PropertyDetails details, uint32_t hash);
3851-
38523853
// Generate new enumeration indices to avoid enumeration index overflow.
38533854
// Returns iteration indices array for the |dictionary|.
38543855
static Handle<FixedArray> GenerateNewEnumerationIndices(
38553856
Handle<Derived> dictionary);
3856-
static const int kMaxNumberKeyIndex = DerivedHashTable::kPrefixStartIndex;
3857-
static const int kNextEnumerationIndexIndex = kMaxNumberKeyIndex + 1;
38583857
};
38593858

38603859

@@ -3926,6 +3925,7 @@ class NameDictionary
39263925

39273926
static const int kEntryValueIndex = 1;
39283927
static const int kEntryDetailsIndex = 2;
3928+
static const int kInitialCapacity = 2;
39293929
};
39303930

39313931

0 commit comments

Comments
 (0)