7#include <unordered_set>
26using HeapObjectSet = std::unordered_set<Tagged<HeapObject>, Object::Hasher,
27 Object::KeyEqualSafe>;
29 Object::Hasher, Object::KeyEqualSafe>;
30using HeapObjectList = std::vector<Tagged<HeapObject>>;
33 return s.count(o) != 0;
36 return s.count(o) != 0;
39class Committee final {
41 static HeapObjectList DeterminePromotees(
43 const SafepointScope& safepoint_scope) {
44 return Committee(isolate).DeterminePromotees(safepoint_scope);
48 explicit Committee(Isolate* isolate)
51 const ExternalReferenceEncoder& ref_encoder()
const {
return ref_encoder_; }
53 HeapObjectList DeterminePromotees(
const SafepointScope& safepoint_scope) {
65 HeapObjectList promo_accepted_list;
69 HeapObjectIterator it(
isolate_->heap(), safepoint_scope);
78 HeapObjectSet accepted_subgraph;
79 HeapObjectList accepted_subgraph_list;
80 HeapObjectSet visited;
81 if (!EvaluateSubgraph(o, &accepted_subgraph, &visited,
82 &accepted_subgraph_list)) {
85 if (accepted_subgraph.empty()) {
90 LogAcceptedPromotionSet(accepted_subgraph);
93 accepted_subgraph.end());
94 promo_accepted_list.insert(promo_accepted_list.end(),
95 accepted_subgraph_list.begin(),
96 accepted_subgraph_list.end());
102 HeapObjectSet seen_promotees;
103 HeapObjectList promotees;
104 promotees.reserve(promo_accepted_list.size());
106 if (Contains(seen_promotees, o))
continue;
107 seen_promotees.insert(o);
108 promotees.push_back(o);
119 HeapObjectSet* visited, HeapObjectList* promotees) {
121 if (Contains(promo_rejected_, o))
return false;
122 if (Contains(promo_accepted_, o))
return true;
123 if (Contains(*visited, o))
return true;
125 if (!IsPromoCandidate(
this, isolate_, o)) {
128 LogRejectedPromotionForFailedPredicate(o);
133 CandidateVisitor v(
this, accepted_subgraph, visited, promotees);
135 if (!v.all_slots_are_promo_candidates()) {
138 LogRejectedPromotionForInvalidSubgraph(o,
139 v.first_rejected_slot_offset());
143 "v8::Template was asked to be promoted to "
144 "read only space but it wasn't possible. "
145 "Use --trace-read-only-promotion for debugging.");
150 accepted_subgraph->insert(o);
151 promotees->push_back(o);
155#define PROMO_CANDIDATE_TYPE_LIST(V) \
160 V(JSExternalObject) \
161 V(FunctionTemplateInfo) \
164 V(SharedFunctionInfo) \
169 static bool IsPromoCandidate(Committee* committee, Isolate* isolate,
171 const InstanceType itype = o->map(isolate)->instance_type();
173 if (InstanceTypeChecker::Is##TYPE(itype)) { \
174 return IsPromoCandidate##TYPE(committee, isolate, Cast<TYPE>(o)); \
184#undef PROMO_CANDIDATE_TYPE_LIST
186#define DEF_PROMO_CANDIDATE(Type) \
187 static bool IsPromoCandidate##Type(Committee* committee, Isolate* isolate, \
195 static bool IsPromoCandidateJSExternalObject(Committee* committee,
199 DCHECK(
IsNull(o->map()->map()->native_context_or_null()));
201 auto maybe_index = committee->ref_encoder().TryEncode(address);
202 return !maybe_index.IsNothing();
204 static bool IsPromoCandidateFunctionTemplateInfo(
208 return o->should_promote_to_read_only();
210 static bool IsPromoCandidateCode(Committee* committee, Isolate* isolate,
214 static bool IsPromoCandidateCodeWrapper(Committee* committee,
217 return IsPromoCandidateCode(committee, isolate, o->code(isolate));
221 static bool IsPromoCandidateSharedFunctionInfo(Committee* committee,
234 if (o->HasBuiltinId() && o->builtin_id() != Builtin::kIllegal) {
238 if (o->IsApiFunction())
return true;
243#undef DEF_PROMO_CANDIDATE
247 class CandidateVisitor :
public ObjectVisitor {
249 CandidateVisitor(Committee* committee, HeapObjectSet* accepted_subgraph,
250 HeapObjectSet* visited, HeapObjectList* promotees)
256 int first_rejected_slot_offset()
const {
259 bool all_slots_are_promo_candidates()
const {
265 if (!all_slots_are_promo_candidates())
return;
269 if (!maybe_object.GetHeapObject(&heap_object))
continue;
270 if (!
committee_->EvaluateSubgraph(heap_object, accepted_subgraph_,
271 visited_, promotees_)) {
273 static_cast<int>(slot.address() - host.address());
274 DCHECK_GE(first_rejected_slot_offset_, 0);
285 DCHECK(host->is_builtin());
289 VisitPointers(host, slot, slot + 1);
300 static void LogAcceptedPromotionSet(
const HeapObjectSet& os) {
301 std::cout <<
"ro-promotion: accepted set {";
303 std::cout << reinterpret_cast<void*>(o.ptr()) <<
", ";
309 std::cout <<
"ro-promotion: rejected due to failed predicate "
310 <<
reinterpret_cast<void*
>(o.ptr()) <<
" ("
311 << o->map()->instance_type() <<
")"
316 int first_rejected_slot_offset) {
317 std::cout <<
"ro-promotion: rejected due to rejected subgraph "
318 <<
reinterpret_cast<void*
>(o.ptr()) <<
" ("
319 << o->map()->instance_type() <<
")"
320 <<
" at slot offset " << first_rejected_slot_offset <<
" ";
322 MaybeObjectSlot slot = o->RawMaybeWeakField(first_rejected_slot_offset);
325 if (maybe_object.GetHeapObject(&heap_object)) {
326 std::cout << reinterpret_cast<void*>(heap_object.ptr()) <<
" ("
327 << heap_object->map()->instance_type() <<
")"
330 std::cout <<
"<cleared weak object>\n";
340class ReadOnlyPromotionImpl final :
public AllStatic {
342 static void CopyToReadOnlyHeap(
344 HeapObjectMap* moves) {
345 ReadOnlySpace* rospace = isolate->heap()->read_only_space();
347 const int size = src->Size(isolate);
350 Heap::CopyBlock(dst.address(), src.address(), size);
351 moves->emplace(src, dst);
354 LogPromotedObject(src, dst);
359 static void UpdatePointers(Isolate* isolate,
360 const SafepointScope& safepoint_scope,
361 const HeapObjectMap& moves) {
362 Heap*
heap = isolate->heap();
363#ifdef V8_COMPRESS_POINTERS
364 ExternalPointerTable::UnsealReadOnlySegmentScope unseal_scope(
365 &isolate->external_pointer_table());
367 UpdatePointersVisitor v(isolate, &moves);
370 EmbedderStackStateScope stack_scope(
372 StackState::kNoHeapPointers);
373 heap->IterateRoots(&v, base::EnumSet<SkipRoot>{});
378 HeapObjectIterator it(
heap, safepoint_scope);
384 for (
auto [src, dst] : moves) {
388#ifdef V8_ENABLE_LEAPTIERING
392 jdt->IterateActiveEntriesIn(
heap->js_dispatch_table_space(),
394 Tagged<Code> old_code = jdt->GetCode(handle);
395 auto it = moves.find(old_code);
396 if (it == moves.end()) return;
397 Tagged<HeapObject> new_code = it->second;
398 CHECK(IsCode(new_code));
401 jdt->SetCodeNoWriteBarrier(
402 handle, Cast<Code>(new_code));
407 static void DeleteDeadObjects(Isolate* isolate,
408 const SafepointScope& safepoint_scope,
409 const HeapObjectMap& moves) {
416 for (
auto [src, dst] : moves) {
418 isolate->heap()->CreateFillerObjectAt(src.address(), src->Size(isolate));
422 static void Verify(Isolate* isolate,
const SafepointScope& safepoint_scope) {
427 Heap*
heap = isolate->heap();
429 heap->promise_all_resolve_element_closure_shared_fun()));
438 Builtins* builtins = isolate->builtins();
441 builtins->code(
static_cast<Builtin>(
i))));
448 class UpdatePointersVisitor final :
public ObjectVisitor,
public RootVisitor {
450 UpdatePointersVisitor(Isolate* isolate,
const HeapObjectMap* moves)
452#ifdef V8_ENABLE_SANDBOX
453 for (
auto [_src, dst] : *moves_) {
454 promoted_objects_.emplace(dst);
463 void VisitRootPointers(
Root root,
const char* description,
464 FullObjectSlot
start, FullObjectSlot
end)
final {
465 for (FullObjectSlot slot =
start; slot <
end; slot++) {
466 ProcessSlot(root, slot);
474 ProcessSlot(host, slot);
489 ExternalPointerSlot slot)
final {
490#ifdef V8_ENABLE_SANDBOX
491 if (promoted_objects_.find(host) == promoted_objects_.end())
return;
497 RecordProcessedSlotIfDebug(slot.address());
498 Address slot_value = slot.load(isolate_);
499 DCHECK(slot.ExactTagIsKnown());
500 slot.init(isolate_, host, slot_value, slot.exact_tag());
503 LogUpdatedExternalPointerTableEntry(host, slot, slot_value);
509#ifdef V8_ENABLE_SANDBOX
510 if (slot.tag() == kCodeIndirectPointerTag) {
511 VisitCodePointer(host, slot);
516 IndirectPointerSlot slot)
final {
517#ifdef V8_ENABLE_SANDBOX
518 if (slot.tag() == kCodeIndirectPointerTag) {
519 VisitCodePointer(host, slot);
523 void VisitRootPointers(
Root root,
const char* description,
537 void ProcessSlot(
Root root, FullObjectSlot slot) {
539#ifdef V8_ENABLE_DIRECT_HANDLE
544 auto it =
moves_->find(old_slot_value);
545 if (it ==
moves_->end())
return;
547 slot.store(new_slot_value);
549 LogUpdatedPointer(root, slot, old_slot_value, new_slot_value);
554 if (!slot.load(isolate_).GetHeapObject(&old_slot_value))
return;
555 auto it =
moves_->find(old_slot_value);
556 if (it ==
moves_->end())
return;
558 slot.store(new_slot_value);
560 LogUpdatedPointer(host, slot, old_slot_value, new_slot_value);
564#ifdef V8_ENABLE_SANDBOX
566 CHECK_EQ(kCodeIndirectPointerTag, slot.tag());
568 auto it = code_pointer_moves_.find(old_handle);
569 if (it == code_pointer_moves_.end())
return;
574 RecordProcessedSlotIfDebug(slot.address());
576 slot.Relaxed_StoreHandle(new_handle);
579 LogUpdatedCodePointerTableEntry(host, slot, old_handle, new_handle);
587 IndirectPointerSlot slot = code->RawIndirectPointerField(
588 Code::kSelfIndirectPointerOffset, kCodeIndirectPointerTag);
597 CodePointerTable::Space* space =
598 IsolateForSandbox(isolate_).GetCodePointerTableSpaceFor(
601 space, code.address(), cpt->GetEntrypoint(old_handle, entrypoint_tag),
604 code_pointer_moves_.emplace(old_handle, new_handle);
607 LogPromotedCodePointerTableEntry(code, old_handle, new_handle);
612 void LogUpdatedPointer(
Root root, FullObjectSlot slot,
615 std::cout <<
"ro-promotion: updated pointer {root "
616 <<
static_cast<int>(root) <<
" slot "
617 <<
reinterpret_cast<void*
>(slot.address()) <<
" from "
618 <<
reinterpret_cast<void*
>(old_slot_value.ptr()) <<
" to "
619 <<
reinterpret_cast<void*
>(new_slot_value.ptr()) <<
"}\n";
624 std::cout <<
"ro-promotion: updated pointer {host "
625 <<
reinterpret_cast<void*
>(host.address()) <<
" slot "
626 <<
reinterpret_cast<void*
>(slot.address()) <<
" from "
627 <<
reinterpret_cast<void*
>(old_slot_value.ptr()) <<
" to "
628 <<
reinterpret_cast<void*
>(new_slot_value.ptr()) <<
"}\n";
631 ExternalPointerSlot slot,
633 std::cout <<
"ro-promotion: updated external pointer slot {host "
634 <<
reinterpret_cast<void*
>(host.address()) <<
" slot "
635 <<
reinterpret_cast<void*
>(slot.address()) <<
" slot_value "
636 <<
reinterpret_cast<void*
>(slot_value) <<
"}\n";
639 IndirectPointerSlot slot,
642 std::cout <<
"ro-promotion: updated code pointer table entry {host "
643 <<
reinterpret_cast<void*
>(host.address()) <<
" slot "
644 <<
reinterpret_cast<void*
>(slot.address()) <<
" from "
645 << AsHex(old_handle, 8,
true) <<
" to "
646 << AsHex(new_handle, 8,
true) <<
"}\n";
650 void RecordProcessedSlotIfDebug(
Address slot_address) {
652 CHECK_EQ(processed_slots_.count(slot_address), 0);
653 processed_slots_.insert(slot_address);
655 std::unordered_set<Address> processed_slots_;
657 void RecordProcessedSlotIfDebug(
Address slot_address)
const {}
663#ifdef V8_ENABLE_SANDBOX
664 HeapObjectSet promoted_objects_;
671 using IndirectPointerHandleMap =
672 std::unordered_map<IndirectPointerHandle, IndirectPointerHandle>;
673 IndirectPointerHandleMap code_pointer_moves_;
679 std::cout <<
"ro-promotion: promoted object {from "
680 <<
reinterpret_cast<void*
>(src.ptr()) <<
" to "
681 <<
reinterpret_cast<void*
>(dst.ptr()) <<
"}\n";
684 static void LogPromotedCodePointerTableEntry(
687 std::cout <<
"ro-promotion: promoted code pointer table entry {code "
688 <<
reinterpret_cast<void*
>(code.ptr()) <<
" slot "
689 << AsHex(old_handle, 8,
true) <<
" to "
690 << AsHex(new_handle, 8,
true) <<
"}\n";
702 std::vector<Tagged<HeapObject>> promotees =
703 Committee::DeterminePromotees(isolate, no_gc, safepoint_scope);
706 ReadOnlyPromotionImpl::CopyToReadOnlyHeap(isolate, promotees, &moves);
709 ReadOnlyPromotionImpl::UpdatePointers(isolate, safepoint_scope, moves);
710 ReadOnlyPromotionImpl::DeleteDeadObjects(isolate, safepoint_scope, moves);
711 ReadOnlyPromotionImpl::Verify(isolate, safepoint_scope);
static constexpr int kBuiltinCount
static constexpr bool kCodeObjectsAreInROSpace
Tagged< MaybeObject > load() const
static V8_INLINE bool InReadOnlySpace(Tagged< HeapObject > object)
static constexpr int kMapOffset
static IsolateGroup * current()
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
bool TryCast(Tagged< From > value, Tagged< To > *out)
constexpr Address kTaggedNullAddress
PerThreadAssertScopeDebugOnly< false, SAFEPOINTS_ASSERT, HEAP_ALLOCATION_ASSERT > DisallowGarbageCollection
SlotTraits::TObjectSlot ObjectSlot
Tagged(T object) -> Tagged< T >
UnionOf< Undefined, FunctionTemplateInfo > UnionOf< Undefined, InterceptorInfo > UnionOf< Undefined, ObjectTemplateInfo > AccessCheckInfo
kInterpreterTrampolineOffset Tagged< HeapObject >
base::StrongAlias< JSDispatchHandleAliasTag, uint32_t > JSDispatchHandle
SlotTraits::TInstructionStreamSlot InstructionStreamSlot
void VisitObject(Isolate *isolate, Tagged< HeapObject > object, ObjectVisitor *visitor)
uint32_t IndirectPointerHandle
V8_INLINE constexpr bool IsHeapObject(TaggedImpl< kRefType, StorageType > obj)
V8_EXPORT_PRIVATE FlagValues v8_flags
SlotTraits::TOffHeapObjectSlot OffHeapObjectSlot
SlotTraits::TMaybeObjectSlot MaybeObjectSlot
kInstanceDescriptorsOffset kTransitionsOrPrototypeInfoOffset IsNull(value)||IsJSProxy(value)||IsWasmObject(value)||(IsJSObject(value) &&(HeapLayout
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
static constexpr AcquireLoadTag kAcquireLoad
#define CHECK_WITH_MSG(condition, message)
#define DCHECK_GE(v1, v2)
#define CHECK_EQ(lhs, rhs)
#define DCHECK(condition)
#define V8_UNLIKELY(condition)