Skip to content

Rewrite IAG::vector to follow the implementation of llvm::SmallVector - #85

Open
jcmosc wants to merge 14 commits into
mainfrom
rewrite-vector
Open

jcmosc wants to merge 14 commits into
mainfrom
rewrite-vector

Conversation

@jcmosc

@jcmosc jcmosc commented Oct 21, 2026

Copy link
Copy Markdown
Owner

No description provided.

jcmosc and others added 14 commits October 22, 2026 18:27
vector declared its inline buffer as `T _inline_buffer[N]`, so the
compiler default-constructed every inline element and destroyed all of
them again after ~vector() had already destroyed the live ones. Once the
vector grew past its inline capacity, the elements were memcpy'd to the
heap but the stale copies left in the inline buffer were still destroyed.

For the `cf_ptr<IAGSubgraphRef>` stack in Subgraph::update() this
released up to 32 subgraphs an extra time whenever more than 32
subgraphs were pending, eventually freeing subgraphs that were still in
use.

Store inline elements in raw, aligned storage so their lifetime is
managed solely by the vector.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	Sources/ComputeCxx/Vector/Vector.h
Also remove the redundant `*&` from the other element accessors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rage

shrink_to_fit() on a vector with no inline capacity always reallocated
to zero elements, freeing the buffer even when it still held elements.
Shrink to the current size instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The move assignment operator called `~T()` on each unique_ptr element,
where T is the pointee type, which fails to compile once instantiated.
Reset the elements instead, matching the destructor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…'ing them

Growing or shrinking a vector relocated elements with memcpy/realloc
regardless of their type, bypassing their move constructors and
destructors. Like llvm::SmallVector, only use memcpy/realloc for
trivially copyable elements, and otherwise move each element into the
new buffer and destroy the original.

Compare::Enum was non-movable but stored in a growable vector, so give
it a move constructor that transfers ownership of its copies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Grow to at least 2 * capacity + 1 instead of 1.5 * capacity, computing
the new capacity in integer arithmetic and reporting overflow of
size_type instead of silently truncating.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lements

insert() move-assigned into the uninitialized slot past the end, then
placement-constructed the value over a live, moved-from element without
destroying it. erase() destroyed the erased elements and then swapped
live elements with them.

Like llvm::SmallVector, insert() now move-constructs the new last
element, shifts the rest with move assignment and assigns the value,
and erase() move-assigns the tail down before destroying the leftover
elements. Inserting at the end goes through push_back(), which also
avoids reading back() of an empty vector.

erase() still returns the new end() rather than the iterator following
the erased elements, since Graph::invalidate_subgraphs() relies on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Use std::destroy and the uninitialized memory algorithms for element
lifetimes in the destructor, move operations, clear(), pop_back() and
resize(), and only reserve in resize() when growing.

Like llvm::SmallVector, moving from a vector with inline elements into
a vector that has a heap buffer now keeps that buffer rather than
freeing it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
push_back(), insert(), resize(count, value) and emplace_back() read
their argument after growing the vector, so passing an element of the
same vector (e.g. `v.push_back(v[0])` when full) read from the freed
buffer. insert() could also read the argument after shifting it.

Like llvm::SmallVector, recompute the argument's address after growing
when it refers to the vector's storage, and adjust it if insert()
shifted it. emplace_back() constructs the new element before growing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both were declared by the unique_ptr specialization but never defined.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It was declared by the unique_ptr specialization but never defined.
Implemented like the other specializations, following llvm::SmallVector.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It was declared by the unique_ptr specialization but never defined. New
elements are value-initialized to nullptr.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It was declared by the unique_ptr specialization but never defined.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
insert() and resize() taking a `const std::unique_ptr &` were declared
but never defined, and can't be, since unique_ptr is not copyable. Mark
them deleted like push_back(const std::unique_ptr &), so that misuse is
a compile error rather than a link error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant