Commit b127c341 authored by Victor Gomes's avatar Victor Gomes Committed by V8 LUCI CQ

[base] Lazily ensure ThreadList invariant

Since next() comes from ThreadedListTraits, the users of this class
can modify the list by modifying the next pointer. This however breaks
the invariant that `tail_` points to the last element of the list.

We ensure this invariant lazily. This should be _almost_ no effect
for users that do not manually modify the next pointer.

Change-Id: If46283ab4fc5036a81f353b25823b0fd39b3e232
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3578803Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Victor Gomes <victorgomes@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80042}
parent 7f407650
...@@ -23,9 +23,15 @@ struct ThreadedListTraits { ...@@ -23,9 +23,15 @@ struct ThreadedListTraits {
// Represents a linked list that threads through the nodes in the linked list. // Represents a linked list that threads through the nodes in the linked list.
// Entries in the list are pointers to nodes. By default nodes need to have a // Entries in the list are pointers to nodes. By default nodes need to have a
// T** next() method that returns the location where the next value is stored. // T** next() method that returns the location where the next value is stored.
// The kSupportsUnsafeInsertion flag defines whether the list supports insertion
// of new elements into the list by just rewiring the next pointers without
// updating the list object itself. Such an insertion might invalidate the
// pointer to list tail and thus requires additional steps to recover the
// pointer to the tail.
// The default can be overwritten by providing a ThreadedTraits class. // The default can be overwritten by providing a ThreadedTraits class.
template <typename T, typename BaseClass, template <typename T, typename BaseClass,
typename TLTraits = ThreadedListTraits<T>> typename TLTraits = ThreadedListTraits<T>,
bool kSupportsUnsafeInsertion = false>
class ThreadedListBase final : public BaseClass { class ThreadedListBase final : public BaseClass {
public: public:
ThreadedListBase() : head_(nullptr), tail_(&head_) {} ThreadedListBase() : head_(nullptr), tail_(&head_) {}
...@@ -33,6 +39,7 @@ class ThreadedListBase final : public BaseClass { ...@@ -33,6 +39,7 @@ class ThreadedListBase final : public BaseClass {
ThreadedListBase& operator=(const ThreadedListBase&) = delete; ThreadedListBase& operator=(const ThreadedListBase&) = delete;
void Add(T* v) { void Add(T* v) {
EnsureValidTail();
DCHECK_NULL(*tail_); DCHECK_NULL(*tail_);
DCHECK_NULL(*TLTraits::next(v)); DCHECK_NULL(*TLTraits::next(v));
*tail_ = v; *tail_ = v;
...@@ -51,6 +58,15 @@ class ThreadedListBase final : public BaseClass { ...@@ -51,6 +58,15 @@ class ThreadedListBase final : public BaseClass {
head_ = v; head_ = v;
} }
// This temporarily breaks the tail_ invariant, and it should only be called
// if we support unsafe insertions.
static void AddAfter(T* after_this, T* v) {
DCHECK(kSupportsUnsafeInsertion);
DCHECK_NULL(*TLTraits::next(v));
*TLTraits::next(v) = *TLTraits::next(after_this);
*TLTraits::next(after_this) = v;
}
void DropHead() { void DropHead() {
DCHECK_NOT_NULL(head_); DCHECK_NOT_NULL(head_);
...@@ -70,6 +86,7 @@ class ThreadedListBase final : public BaseClass { ...@@ -70,6 +86,7 @@ class ThreadedListBase final : public BaseClass {
void Append(ThreadedListBase&& list) { void Append(ThreadedListBase&& list) {
if (list.is_empty()) return; if (list.is_empty()) return;
EnsureValidTail();
*tail_ = list.head_; *tail_ = list.head_;
tail_ = list.tail_; tail_ = list.tail_;
list.Clear(); list.Clear();
...@@ -78,6 +95,7 @@ class ThreadedListBase final : public BaseClass { ...@@ -78,6 +95,7 @@ class ThreadedListBase final : public BaseClass {
void Prepend(ThreadedListBase&& list) { void Prepend(ThreadedListBase&& list) {
if (list.head_ == nullptr) return; if (list.head_ == nullptr) return;
EnsureValidTail();
T* new_head = list.head_; T* new_head = list.head_;
*list.tail_ = head_; *list.tail_ = head_;
if (head_ == nullptr) { if (head_ == nullptr) {
...@@ -116,6 +134,7 @@ class ThreadedListBase final : public BaseClass { ...@@ -116,6 +134,7 @@ class ThreadedListBase final : public BaseClass {
return true; return true;
} }
EnsureValidTail();
while (current != nullptr) { while (current != nullptr) {
T* next = *TLTraits::next(current); T* next = *TLTraits::next(current);
if (next == v) { if (next == v) {
...@@ -213,10 +232,16 @@ class ThreadedListBase final : public BaseClass { ...@@ -213,10 +232,16 @@ class ThreadedListBase final : public BaseClass {
}; };
Iterator begin() { return Iterator(TLTraits::start(&head_)); } Iterator begin() { return Iterator(TLTraits::start(&head_)); }
Iterator end() { return Iterator(tail_); } Iterator end() {
EnsureValidTail();
return Iterator(tail_);
}
ConstIterator begin() const { return ConstIterator(TLTraits::start(&head_)); } ConstIterator begin() const { return ConstIterator(TLTraits::start(&head_)); }
ConstIterator end() const { return ConstIterator(tail_); } ConstIterator end() const {
EnsureValidTail();
return ConstIterator(tail_);
}
// Rewinds the list's tail to the reset point, i.e., cutting of the rest of // Rewinds the list's tail to the reset point, i.e., cutting of the rest of
// the list, including the reset_point. // the list, including the reset_point.
...@@ -253,7 +278,7 @@ class ThreadedListBase final : public BaseClass { ...@@ -253,7 +278,7 @@ class ThreadedListBase final : public BaseClass {
return *t; return *t;
} }
bool Verify() { bool Verify() const {
T* last = this->first(); T* last = this->first();
if (last == nullptr) { if (last == nullptr) {
CHECK_EQ(&head_, tail_); CHECK_EQ(&head_, tail_);
...@@ -266,7 +291,19 @@ class ThreadedListBase final : public BaseClass { ...@@ -266,7 +291,19 @@ class ThreadedListBase final : public BaseClass {
return true; return true;
} }
void RevalidateTail() { inline void EnsureValidTail() const {
if (!kSupportsUnsafeInsertion) {
DCHECK_EQ(*tail_, nullptr);
return;
}
// If kSupportsUnsafeInsertion, then we support adding a new element by
// using the pointer to a certain element. E.g., imagine list A -> B -> C,
// we can add D after B, by just moving the pointer of B to D and D to
// whatever B used to point to. We do not need to know the beginning of the
// list (ie. to have a pointer to the ThreadList class). This however might
// break the tail_ invariant. We ensure this here, by manually looking for
// the tail of the list.
if (*tail_ == nullptr) return;
T* last = *tail_; T* last = *tail_;
if (last != nullptr) { if (last != nullptr) {
while (*TLTraits::next(last) != nullptr) { while (*TLTraits::next(last) != nullptr) {
...@@ -274,19 +311,26 @@ class ThreadedListBase final : public BaseClass { ...@@ -274,19 +311,26 @@ class ThreadedListBase final : public BaseClass {
} }
tail_ = TLTraits::next(last); tail_ = TLTraits::next(last);
} }
SLOW_DCHECK(Verify());
} }
private: private:
T* head_; T* head_;
T** tail_; mutable T** tail_; // We need to ensure a valid `tail_` even when using a
// const Iterator.
}; };
struct EmptyBase {}; struct EmptyBase {};
// Check ThreadedListBase::EnsureValidTail.
static constexpr bool kUnsafeInsertion = true;
template <typename T, typename TLTraits = ThreadedListTraits<T>> template <typename T, typename TLTraits = ThreadedListTraits<T>>
using ThreadedList = ThreadedListBase<T, EmptyBase, TLTraits>; using ThreadedList = ThreadedListBase<T, EmptyBase, TLTraits>;
template <typename T, typename TLTraits = ThreadedListTraits<T>>
using ThreadedListWithUnsafeInsertions =
ThreadedListBase<T, EmptyBase, TLTraits, kUnsafeInsertion>;
} // namespace base } // namespace base
} // namespace v8 } // namespace v8
......
...@@ -35,14 +35,6 @@ class MaglevGraphBuilder { ...@@ -35,14 +35,6 @@ class MaglevGraphBuilder {
// TODO(v8:7700): Clean up after all bytecodes are supported. // TODO(v8:7700): Clean up after all bytecodes are supported.
if (found_unsupported_bytecode()) break; if (found_unsupported_bytecode()) break;
} }
// During InterpreterFrameState merge points, we might emit CheckedSmiTags
// and add them unsafely to the basic blocks. This addition might break a
// list invariant (namely `tail_` might not point to the last element).
// We revalidate this invariant here in all basic blocks.
for (BasicBlock* block : *graph_) {
block->nodes().RevalidateTail();
}
} }
Graph* graph() const { return graph_; } Graph* graph() const { return graph_; }
......
...@@ -388,7 +388,7 @@ class MergePointInterpreterFrameState { ...@@ -388,7 +388,7 @@ class MergePointInterpreterFrameState {
Node::New<CheckedSmiTag, std::initializer_list<ValueNode*>>( Node::New<CheckedSmiTag, std::initializer_list<ValueNode*>>(
compilation_unit.zone(), compilation_unit, compilation_unit.zone(), compilation_unit,
value->eager_deopt_info()->state, {value}); value->eager_deopt_info()->state, {value});
value->AddNodeAfter(tagged); Node::List::AddAfter(value, tagged);
compilation_unit.RegisterNodeInGraphLabeller(tagged); compilation_unit.RegisterNodeInGraphLabeller(tagged);
return tagged; return tagged;
} }
......
...@@ -659,19 +659,10 @@ constexpr bool NodeBase::Is<UnconditionalControlNode>() const { ...@@ -659,19 +659,10 @@ constexpr bool NodeBase::Is<UnconditionalControlNode>() const {
// The Node class hierarchy contains all non-control nodes. // The Node class hierarchy contains all non-control nodes.
class Node : public NodeBase { class Node : public NodeBase {
public: public:
using List = base::ThreadedList<Node>; using List = base::ThreadedListWithUnsafeInsertions<Node>;
inline ValueLocation& result(); inline ValueLocation& result();
// This might break ThreadedList invariants.
// Run ThreadedList::RevalidateTail afterwards.
void AddNodeAfter(Node* node) {
DCHECK_NOT_NULL(node);
DCHECK_NULL(node->next_);
node->next_ = next_;
next_ = node;
}
Node* NextNode() const { return next_; } Node* NextNode() const { return next_; }
protected: protected:
......
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