fix(shader): move a cached program's use lists to the heap and drop its arena

A program kept in a SourceEntry held its use-list arena until the entry
was destroyed, so the buffers of values ReclaimUnusedValues dropped, and
the buffers every use list outgrew, stayed allocated for as long as the
program was cached. Before a program is cached, ReleaseUseArena now
copies each value's use list into a heap vector and frees the arena.
Values created after that draw from a new arena.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
milescovielloandClaude Opus 5.5 authored and Adrià committed 2026-10-29 21:17:43 +01:00
1 parent a661d4367f
commit c01d0bb24f
6 files changed
+30

No files matched your search

@@ -96,6 +96,23 @@ int main() {
keptReader.AddArgument(&kept);
replaced = std::move(replacement);
if (kept.UseCount() != 1u || kept.OperandUses().front().user != &keptReader) throw std::runtime_error("move assignment invalidated the assigned program's use lists");
IrProgram cached;
auto& source = cached.CreateValue(IrOpcode::IAdd32, IrType::U32);
std::vector<IrValue*> users;
for (std::uint32_t number = 0; number < 40u; ++number) {
auto& user = cached.CreateValue(IrOpcode::IAdd32, IrType::U32);
user.AddArgument(&source);
users.push_back(&user);
}
cached.ReleaseUseArena();
if (source.OperandUses().get_allocator().Arena() != nullptr || users.front()->OperandUses().get_allocator().Arena() != nullptr) throw std::runtime_error("releasing the use arena left a use list in it");
if (source.UseCount() != users.size()) throw std::runtime_error("releasing the use arena lost a use");
for (std::size_t index = 0; index < users.size(); ++index) {
if (source.OperandUses()[index].user != users[index] || users[index]->Argument(0) != &source) throw std::runtime_error("releasing the use arena reordered a use list");
}
auto& late = cached.CreateValue(IrOpcode::IAdd32, IrType::U32);
late.AddArgument(&source);
if (late.OperandUses().get_allocator().Arena() == nullptr || source.UseCount() != users.size() + 1u || source.OperandUses().back().user != &late) throw std::runtime_error("a value created after releasing the use arena did not get a new one");
IrProgram moved = std::move(program);
if (value.Id() != id || value.ImmediateU32() != 42u || moved.ReclaimUnusedValues() != 1u) throw std::runtime_error("moving a program invalidated its allocation owner");
IrProgram assigned;
@@ -32,6 +32,7 @@ public:
IrValue& CreateValue(IrOpcode opcode, IrType type, std::uint64_t flags);
void SetEntryBlock(IrBlock& block);
std::size_t ReclaimUnusedValues();
void ReleaseUseArena();
[[nodiscard]] std::vector<IrBlock*>& BlockOrder();
[[nodiscard]] const std::vector<IrBlock*>& BlockOrder() const;
@@ -184,6 +184,7 @@ public:
[[nodiscard]] IrValue* Argument(std::size_t index) const;
[[nodiscard]] IrBlock* PhiBlock(std::size_t index) const;
[[nodiscard]] const IrUseList& OperandUses() const;
void MoveUsesToHeap();
[[nodiscard]] std::uint64_t ImmediateU64() const;
[[nodiscard]] std::uint16_t ImmediateF16Bits() const;
[[nodiscard]] float ImmediateF32() const;
@@ -274,6 +274,11 @@ std::size_t IrProgram::ReclaimUnusedValues() {
return removed;
}
void IrProgram::ReleaseUseArena() {
for (const auto& value : values) value->MoveUsesToHeap();
useArena = IrUseArena{};
}
std::vector<std::unique_ptr<IrBlock>>& IrProgram::Blocks() {
return blocks;
}
@@ -125,6 +125,11 @@ const IrUseList& IrValue::OperandUses() const {
return operandUses;
}
void IrValue::MoveUsesToHeap() {
if (operandUses.get_allocator().Arena() == nullptr) return;
operandUses = IrUseList(operandUses.begin(), operandUses.end(), IrUseAllocator<IrUse>{});
}
std::uint64_t IrValue::ImmediateU64() const {
return immediateBits;
}
+1
View File
@@ -536,6 +536,7 @@ std::shared_ptr<SourceEntry> getSource(const RecompileRequest& request, ShaderPr
ResourceProgram resource(request, preparation, reclaim);
source->plan = std::move(resource.plan);
if (!hasPreparedArtifact && std::ranges::none_of(source->variants, [&](const auto& variant) { return sameLayout(variant->layout, request.layout); })) {
resource.program->ReleaseUseArena();
source->program = std::move(resource.program);
}
} catch (...) {