v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
descriptor-array-inl.h
Go to the documentation of this file.
1// Copyright 2018 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_OBJECTS_DESCRIPTOR_ARRAY_INL_H_
6#define V8_OBJECTS_DESCRIPTOR_ARRAY_INL_H_
7
9// Include the non-inl header before the rest of the headers.
10
14#include "src/heap/heap.h"
26
27// Has to be the last include (doesn't have include guards):
29
30namespace v8 {
31namespace internal {
32
33#include "torque-generated/src/objects/descriptor-array-tq-inl.inc"
34
35TQ_OBJECT_CONSTRUCTORS_IMPL(DescriptorArray)
37
38RELAXED_INT16_ACCESSORS(DescriptorArray, number_of_all_descriptors,
39 kNumberOfAllDescriptorsOffset)
41 kNumberOfDescriptorsOffset)
42RELAXED_UINT32_ACCESSORS(DescriptorArray, raw_gc_state, kRawGcStateOffset)
43
44inline int16_t DescriptorArray::number_of_slack_descriptors() const {
45 return number_of_all_descriptors() - number_of_descriptors();
46}
47
49 return number_of_descriptors();
50}
51
53 set_enum_cache(array->enum_cache());
54}
55
57 bool concurrent_search) {
58 DCHECK(IsUniqueName(name));
59 SLOW_DCHECK_IMPLIES(!concurrent_search, IsSortedNoDuplicates());
60
61 if (valid_descriptors == 0) {
63 }
64
65 // Do linear search for small arrays, and for searches in the background
66 // thread.
67 const int kMaxElementsForLinearSearch = 8;
68 if (valid_descriptors <= kMaxElementsForLinearSearch || concurrent_search) {
69 return LinearSearch(name, valid_descriptors);
70 }
71
72 return BinarySearch(name, valid_descriptors);
73}
74
76 int valid_descriptors) {
77 // We have to binary search all descriptors, not just valid ones, since the
78 // binary search ordering is across all descriptors.
80 uint32_t hash = name->hash();
81
82 // Find the first descriptor whose key's hash is greater-than-or-equal-to the
83 // search hash.
84 int number = *std::ranges::lower_bound(std::views::iota(0, end), hash,
85 std::less<>(), [&](int i) {
87 return entry->hash();
88 });
89
90 // There may have been hash collisions, so search for the name from the first
91 // index until the first non-matching hash.
92 for (; number < end; ++number) {
94 Tagged<Name> entry = GetKey(index);
95 if (entry == name) {
96 // If we found the entry, but it's outside the owned descriptors of the
97 // caller, return not found.
98 if (index.as_int() >= valid_descriptors) {
100 }
101 return index;
102 }
103 if (entry->hash() != hash) {
105 }
106 }
107
109}
110
112 int valid_descriptors) {
113 DCHECK_LE(valid_descriptors, number_of_descriptors());
114 for (int i = 0; i < valid_descriptors; ++i) {
116 if (GetKey(index) == name) return index;
117 }
119}
120
122 bool concurrent_search) {
123 DCHECK(IsUniqueName(name));
124 int number_of_own_descriptors = map->NumberOfOwnDescriptors();
125 if (number_of_own_descriptors == 0) return InternalIndex::NotFound();
126 return Search(name, number_of_own_descriptors, concurrent_search);
127}
128
129InternalIndex DescriptorArray::Search(int field_index, int valid_descriptors) {
130 for (int desc_index = field_index; desc_index < valid_descriptors;
131 ++desc_index) {
132 PropertyDetails details = GetDetails(InternalIndex(desc_index));
133 if (details.location() != PropertyLocation::kField) continue;
134 if (field_index == details.field_index()) {
135 return InternalIndex(desc_index);
136 }
137 DCHECK_LT(details.field_index(), field_index);
138 }
140}
141
143 int number_of_own_descriptors = map->NumberOfOwnDescriptors();
144 if (number_of_own_descriptors == 0) return InternalIndex::NotFound();
145 return Search(field_index, number_of_own_descriptors);
146}
147
149 Tagged<Name> name,
150 Tagged<Map> map) {
151 DCHECK(IsUniqueName(name));
152 int number_of_own_descriptors = map->NumberOfOwnDescriptors();
153 if (number_of_own_descriptors == 0) return InternalIndex::NotFound();
154
155 DescriptorLookupCache* cache = isolate->descriptor_lookup_cache();
156 int number = cache->Lookup(map, name);
157
158 if (number == DescriptorLookupCache::kAbsent) {
159 InternalIndex result = Search(name, number_of_own_descriptors);
160 number = result.is_found() ? result.as_int() : DescriptorArray::kNotFound;
161 cache->Update(map, name, number);
162 }
164 return InternalIndex(number);
165}
166
168 static_assert(kEndOfStrongFieldsOffset == kStartOfWeakFieldsOffset,
169 "Weak and strong fields are continuous.");
170 static_assert(kEndOfWeakFieldsOffset == kHeaderSize,
171 "Weak fields extend up to the end of the header.");
172 return RawField(DescriptorArray::kStartOfStrongFieldsOffset);
173}
174
176 // Allow descriptor == number_of_all_descriptors() for computing the slot
177 // address that comes after the last descriptor (for iterating).
178 DCHECK_LE(descriptor, number_of_all_descriptors());
179 return RawField(OffsetOfDescriptorAt(descriptor));
180}
181
183 InternalIndex descriptor_number) const {
184 DCHECK_LT(descriptor_number.as_int(), number_of_descriptors());
185 int entry_offset = OffsetOfDescriptorAt(descriptor_number.as_int());
186 PtrComprCageBase cage_base = GetPtrComprCageBase(*this);
187 Tagged<Object> maybe_name =
188 EntryKeyField::Relaxed_Load(cage_base, *this, entry_offset);
189 bool is_initialized = !IsUndefined(maybe_name);
190 DCHECK_IMPLIES(is_initialized,
191 IsSmi(EntryDetailsField::Relaxed_Load(*this, entry_offset)));
192 return is_initialized;
193}
194
196 PtrComprCageBase cage_base = GetPtrComprCageBase(*this);
197 return GetKey(cage_base, descriptor_number);
198}
199
201 InternalIndex descriptor_number) const {
202 DCHECK_LT(descriptor_number.as_int(), number_of_descriptors());
203 int entry_offset = OffsetOfDescriptorAt(descriptor_number.as_int());
204 return Cast<Name>(
205 EntryKeyField::Relaxed_Load(cage_base, *this, entry_offset));
206}
207
210 DCHECK_LT(descriptor_number.as_int(), number_of_descriptors());
211 int entry_offset = OffsetOfDescriptorAt(descriptor_number.as_int());
212 EntryKeyField::Relaxed_Store(*this, entry_offset, key);
213 WRITE_BARRIER(*this, entry_offset + kEntryKeyOffset, key);
214}
215
216int DescriptorArray::GetSortedKeyIndex(int descriptor_number) {
217 return GetDetails(InternalIndex(descriptor_number)).pointer();
218}
219
221 PtrComprCageBase cage_base = GetPtrComprCageBase(*this);
222 return GetSortedKey(cage_base, descriptor_number);
223}
224
226 int descriptor_number) {
227 return GetKey(cage_base, InternalIndex(GetSortedKeyIndex(descriptor_number)));
228}
229
230void DescriptorArray::SetSortedKey(int descriptor_number, int pointer) {
231 PropertyDetails details = GetDetails(InternalIndex(descriptor_number));
232 SetDetails(InternalIndex(descriptor_number), details.set_pointer(pointer));
233}
234
236 InternalIndex descriptor_number) {
237 PtrComprCageBase cage_base = GetPtrComprCageBase(*this);
238 return Cast<Object>(GetStrongValue(cage_base, descriptor_number));
239}
240
242 PtrComprCageBase cage_base, InternalIndex descriptor_number) {
243 return Cast<Object>(GetValue(cage_base, descriptor_number));
244}
245
247 Tagged<MaybeObject> value) {
248 DCHECK_LT(descriptor_number.as_int(), number_of_descriptors());
249 int entry_offset = OffsetOfDescriptorAt(descriptor_number.as_int());
250 EntryValueField::Relaxed_Store(*this, entry_offset, value);
251 WRITE_BARRIER(*this, entry_offset + kEntryValueOffset, value);
252}
253
255 PtrComprCageBase cage_base = GetPtrComprCageBase(*this);
256 return GetValue(cage_base, descriptor_number);
257}
258
260 InternalIndex descriptor_number) {
261 DCHECK_LT(descriptor_number.as_int(), number_of_descriptors());
262 int entry_offset = OffsetOfDescriptorAt(descriptor_number.as_int());
263 return EntryValueField::Relaxed_Load(cage_base, *this, entry_offset);
264}
265
267 DCHECK_LT(descriptor_number.as_int(), number_of_descriptors());
268 int entry_offset = OffsetOfDescriptorAt(descriptor_number.as_int());
269 Tagged<Smi> details = EntryDetailsField::Relaxed_Load(*this, entry_offset);
270 return PropertyDetails(details);
271}
272
274 PropertyDetails details) {
275 DCHECK_LT(descriptor_number.as_int(), number_of_descriptors());
276 int entry_offset = OffsetOfDescriptorAt(descriptor_number.as_int());
277 EntryDetailsField::Relaxed_Store(*this, entry_offset, details.AsSmi());
278}
279
281 DCHECK_EQ(GetDetails(descriptor_number).location(), PropertyLocation::kField);
282 return GetDetails(descriptor_number).field_index();
283}
284
286 InternalIndex descriptor_number) {
287 PtrComprCageBase cage_base = GetPtrComprCageBase(*this);
288 return GetFieldType(cage_base, descriptor_number);
289}
290
292 PtrComprCageBase cage_base, InternalIndex descriptor_number) {
293 DCHECK_EQ(GetDetails(descriptor_number).location(), PropertyLocation::kField);
294 Tagged<MaybeObject> wrapped_type = GetValue(cage_base, descriptor_number);
295 return Map::UnwrapFieldType(wrapped_type);
296}
297
299 Tagged<MaybeObject> value, PropertyDetails details) {
300 CHECK_LT(descriptor_number.as_int(), number_of_descriptors());
301 SetKey(descriptor_number, key);
302 SetDetails(descriptor_number, details);
303 SetValue(descriptor_number, value);
304}
305
306void DescriptorArray::Set(InternalIndex descriptor_number, Descriptor* desc) {
307 Tagged<Name> key = *desc->GetKey();
308 Tagged<MaybeObject> value = *desc->GetValue();
309 Set(descriptor_number, key, value, desc->GetDetails());
310}
311
314 int descriptor_number = number_of_descriptors();
315 DCHECK_LE(descriptor_number + 1, number_of_all_descriptors());
316 set_number_of_descriptors(descriptor_number + 1);
317 Set(InternalIndex(descriptor_number), desc);
318
319 uint32_t desc_hash = desc->GetKey()->hash();
320 // Hash value can't be zero, see String::ComputeAndSetHash()
321 uint32_t collision_hash = 0;
322
323 int insertion;
324
325 for (insertion = descriptor_number; insertion > 0; --insertion) {
326 Tagged<Name> key = GetSortedKey(insertion - 1);
327 collision_hash = key->hash();
328 if (collision_hash <= desc_hash) break;
329 SetSortedKey(insertion, GetSortedKeyIndex(insertion - 1));
330 }
331
332 SetSortedKey(insertion, descriptor_number);
333
334 if (V8_LIKELY(collision_hash != desc_hash)) return;
335
336 CheckNameCollisionDuringInsertion(desc, desc_hash, insertion);
337}
338
340 int first_key = GetSortedKeyIndex(first);
342 SetSortedKey(second, first_key);
343}
344
345// static
347 unsigned gc_epoch, Tagged<DescriptorArray> array,
348 DescriptorIndex index_to_mark) {
349 const auto current_epoch = gc_epoch & Epoch::kMask;
350 while (true) {
351 const RawGCStateType raw_gc_state = array->raw_gc_state(kRelaxedLoad);
352 const auto epoch_from_state = Epoch::decode(raw_gc_state);
353 RawGCStateType new_raw_gc_state = 0;
354 if (current_epoch != epoch_from_state) {
355 // If the epochs do not match, then either the raw_gc_state is zero
356 // (freshly allocated descriptor array) or the epoch from value lags
357 // by 1.
358 DCHECK_IMPLIES(raw_gc_state != 0,
359 Epoch::decode(epoch_from_state + 1) == current_epoch);
360 new_raw_gc_state = NewState(current_epoch, 0, index_to_mark);
361 } else {
362 const DescriptorIndex already_marked = Marked::decode(raw_gc_state);
363 const DescriptorIndex delta = Delta::decode(raw_gc_state);
364 if ((already_marked + delta) >= index_to_mark) {
365 return false;
366 }
367 new_raw_gc_state = NewState(current_epoch, already_marked,
368 index_to_mark - already_marked);
369 }
370 if (SwapState(array, raw_gc_state, new_raw_gc_state)) {
371 return true;
372 }
373 }
374}
375
376// static
380 unsigned gc_epoch, Tagged<DescriptorArray> array) {
381 const auto current_epoch = gc_epoch & Epoch::kMask;
382 while (true) {
383 const RawGCStateType raw_gc_state = array->raw_gc_state(kRelaxedLoad);
384 const DescriptorIndex marked = Marked::decode(raw_gc_state);
385 const DescriptorIndex delta = Delta::decode(raw_gc_state);
386 // We may encounter an array here that was merely pushed to the marker. In
387 // such a case, we process all descriptors (if we succeed). The cases to
388 // check are:
389 // 1. Epoch mismatch: Happens when descriptors survive a GC cycle.
390 // 2. Epoch matches but marked/delta is 0: Can happen when descriptors are
391 // newly allocated in the current cycle.
392 if (current_epoch != Epoch::decode(raw_gc_state) || (marked + delta) == 0) {
393 // In case number of descriptors is 0 and we reach the array through roots
394 // marking, mark also slack to get a proper transition from 0 marked to X
395 // marked. Otherwise, we would need to treat the state [0,0[ for marked
396 // and delta as valid state which leads to double-accounting through the
397 // marking barrier (when nof>1 in the barrier).
398 const int16_t number_of_descriptors =
399 array->number_of_descriptors() ? array->number_of_descriptors()
400 : array->number_of_all_descriptors();
402 if (SwapState(array, raw_gc_state,
403 NewState(current_epoch, number_of_descriptors, 0))) {
404 return {0, number_of_descriptors};
405 }
406 continue;
407 }
408
409 // The delta is 0, so everything has been processed. Return the marked
410 // indices.
411 if (delta == 0) {
412 return {marked, marked};
413 }
414
415 if (SwapState(array, raw_gc_state,
416 NewState(current_epoch, marked + delta, 0))) {
417 return {marked, marked + delta};
418 }
419 }
420}
421
422} // namespace internal
423} // namespace v8
424
426
427#endif // V8_OBJECTS_DESCRIPTOR_ARRAY_INL_H_
#define SLOW_DCHECK_IMPLIES(v1, v2)
Definition checks.h:22
static std::pair< DescriptorIndex, DescriptorIndex > AcquireDescriptorRangeToMark(unsigned gc_epoch, Tagged< DescriptorArray > array)
static constexpr RawGCStateType NewState(unsigned masked_epoch, DescriptorIndex marked, DescriptorIndex delta)
static bool SwapState(Tagged< DescriptorArray > array, RawGCStateType old_state, RawGCStateType new_state)
static bool TryUpdateIndicesToMark(unsigned gc_epoch, Tagged< DescriptorArray > array, DescriptorIndex index_to_mark)
PropertyDetails GetDetails(InternalIndex descriptor_number)
bool IsInitializedDescriptor(InternalIndex descriptor_number) const
Tagged< FieldType > GetFieldType(InternalIndex descriptor_number)
int GetFieldIndex(InternalIndex descriptor_number)
V8_INLINE InternalIndex SearchWithCache(Isolate *isolate, Tagged< Name > name, Tagged< Map > map)
void Set(InternalIndex descriptor_number, Descriptor *desc)
int GetSortedKeyIndex(int descriptor_number)
ObjectSlot GetDescriptorSlot(int descriptor)
V8_INLINE InternalIndex Search(Tagged< Name > name, int number_of_own_descriptors, bool concurrent_search=false)
Tagged< Name > GetKey(InternalIndex descriptor_number) const
void SetSortedKey(int pointer, int descriptor_number)
Tagged< MaybeObject > GetValue(InternalIndex descriptor_number)
void SwapSortedKeys(int first, int second)
void SetDetails(InternalIndex descriptor_number, PropertyDetails details)
V8_INLINE InternalIndex BinarySearch(Tagged< Name > name, int number_of_own_descriptors)
Tagged< Name > GetSortedKey(int descriptor_number)
V8_EXPORT_PRIVATE void CheckNameCollisionDuringInsertion(Descriptor *desc, uint32_t descriptor_hash, int insertion_index)
Definition objects.cc:3989
void SetKey(InternalIndex descriptor_number, Tagged< Name > key)
static constexpr int OffsetOfDescriptorAt(int descriptor)
void SetValue(InternalIndex descriptor_number, Tagged< MaybeObject > value)
V8_INLINE InternalIndex LinearSearch(Tagged< Name > name, int number_of_own_descriptors)
void CopyEnumCacheFrom(Tagged< DescriptorArray > array)
Tagged< Object > GetStrongValue(InternalIndex descriptor_number)
int Lookup(Tagged< Map > source, Tagged< Name > name)
static InternalIndex NotFound()
constexpr int as_int() const
static V8_EXPORT_PRIVATE Tagged< FieldType > UnwrapFieldType(Tagged< MaybeObject > wrapped_type)
Definition map.cc:495
PropertyLocation location() const
Tagged< Smi > AsSmi() const
Definition objects-inl.h:79
PropertyDetails set_pointer(int i) const
static void Relaxed_Store(Tagged< HeapObject > host, PtrType value)
static PtrType Relaxed_Load(Tagged< HeapObject > host, int offset=0)
int end
double second
ZoneVector< RpoNumber > & result
V8_INLINE constexpr bool IsSmi(TaggedImpl< kRefType, StorageType > obj)
Definition objects.h:665
V8_INLINE PtrComprCageBase GetPtrComprCageBase()
bool IsUniqueName(Tagged< Name > obj)
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
static constexpr RelaxedLoadTag kRelaxedLoad
Definition globals.h:2909
#define RELAXED_INT16_ACCESSORS(holder, name, offset)
#define TQ_OBJECT_CONSTRUCTORS_IMPL(Type)
#define WRITE_BARRIER(object, offset, value)
#define RELAXED_UINT32_ACCESSORS(holder, name, offset)
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define CHECK_LT(lhs, rhs)
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define DCHECK_GT(v1, v2)
Definition logging.h:487
#define V8_LIKELY(condition)
Definition v8config.h:661