LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
DiscoveryStorage.h
Go to the documentation of this file.
1//===-- DiscoveryStorage.h --------------------------------------*- C++ -*-===//
2//
3// Part of the LLZK Project, under the Apache License v2.0.
4// See LICENSE.txt for license information.
5// Copyright 2026 Project LLZK
6// SPDX-License-Identifier: Apache-2.0
7//
8//===----------------------------------------------------------------------===//
9
10#pragma once
11
12#include <mlir/IR/Builders.h>
13
14#include <llvm/ADT/DenseMap.h>
15
16#include <memory>
17
19
21using DiscoveryLeaves = llvm::DenseMap<mlir::ArrayAttr, mlir::Attribute>;
22
26 llvm::DenseMap<mlir::ArrayAttr, llvm::SmallVector<int64_t>> shapes;
27
29 void invalidate() {
30 leaves.clear();
31 shapes.remove_if([](const auto &entry) { return !entry.first.empty(); });
32 }
33};
34
37 mlir::Attribute constant;
38 std::shared_ptr<DiscoveryAggregate> aggregate;
39
40 DiscoveryValue() = default;
41 DiscoveryValue(mlir::Attribute value) : constant(value) {}
42
43 bool operator==(const DiscoveryValue &other) const {
44 return constant == other.constant && aggregate == other.aggregate;
45 }
46};
47
53 static constexpr size_t allocationCleanupInterval = 4096;
54
55 mlir::Builder builder;
56 llvm::SmallVector<std::weak_ptr<DiscoveryAggregate>> allocations;
57
58 static bool startsWith(mlir::ArrayAttr path, llvm::ArrayRef<mlir::Attribute> prefix) {
59 return path.size() >= prefix.size() &&
60 llvm::equal(path.getValue().take_front(prefix.size()), prefix);
61 }
62
63public:
64 explicit DiscoveryStorage(mlir::MLIRContext *context) : builder(context) {}
65
68 if (allocations.size() >= allocationCleanupInterval &&
69 allocations.size() % allocationCleanupInterval == 0) {
70 llvm::erase_if(allocations, [](const auto &allocation) { return allocation.expired(); });
71 }
72 DiscoveryValue value;
73 value.aggregate = std::make_shared<DiscoveryAggregate>();
74 allocations.push_back(value.aggregate);
75 return value;
76 }
77
80 void invalidate() {
81 llvm::erase_if(allocations, [](auto &allocation) {
82 if (auto leaves = allocation.lock()) {
83 leaves->invalidate();
84 return false;
85 }
86 return true;
87 });
88 }
89
91 void forget(const DiscoveryValue &value) {
92 if (value.aggregate) {
93 value.aggregate->invalidate();
94 } else {
95 // An untracked reference can alias any local allocation.
96 invalidate();
97 }
98 }
99
102 read(const DiscoveryValue &source, llvm::ArrayRef<mlir::Attribute> path, bool aggregate) {
103 if (!source.aggregate) {
104 return {};
105 }
106 if (!aggregate) {
107 return source.aggregate->leaves.lookup(builder.getArrayAttr(path));
108 }
109 auto result = create();
110 for (auto [key, value] : source.aggregate->leaves) {
111 if (startsWith(key, path)) {
112 result.aggregate->leaves[builder.getArrayAttr(key.getValue().drop_front(path.size()))] =
113 value;
114 }
115 }
116 for (const auto &[key, shape] : source.aggregate->shapes) {
117 if (startsWith(key, path)) {
118 result.aggregate->shapes[builder.getArrayAttr(key.getValue().drop_front(path.size()))] =
119 shape;
120 }
121 }
122 // A prefix of an array's coordinates selects a lower-rank array.
123 auto root = source.aggregate->shapes.find(builder.getArrayAttr({}));
124 if (root != source.aggregate->shapes.end() && path.size() < root->second.size() &&
125 llvm::all_of(path, [](mlir::Attribute part) {
126 return llvm::isa<mlir::IntegerAttr>(part);
127 })) {
128 result.aggregate->shapes[builder.getArrayAttr({})] =
129 llvm::SmallVector<int64_t>(llvm::ArrayRef(root->second).drop_front(path.size()));
130 }
131 return result;
132 }
133
136 const DiscoveryValue &value, llvm::ArrayRef<int64_t> shape,
137 llvm::ArrayRef<mlir::Attribute> path = {}
138 ) {
139 value.aggregate->shapes[builder.getArrayAttr(path)] = llvm::SmallVector<int64_t>(shape);
140 }
141
143 llvm::ArrayRef<int64_t> getShape(const DiscoveryValue &value) {
144 if (value.aggregate) {
145 auto it = value.aggregate->shapes.find(builder.getArrayAttr({}));
146 if (it != value.aggregate->shapes.end()) {
147 return it->second;
148 }
149 }
150 return {};
151 }
152
154 void write(
155 const DiscoveryValue &destination, llvm::ArrayRef<mlir::Attribute> path,
156 const DiscoveryValue &value
157 ) {
158 if (!destination.aggregate) {
159 invalidate();
160 return;
161 }
162 DiscoveryAggregate snapshot = value.aggregate ? *value.aggregate : DiscoveryAggregate();
163 destination.aggregate->leaves.remove_if([path](const auto &entry) {
164 return startsWith(entry.first, path);
165 });
166 if (value.constant) {
167 destination.aggregate->leaves[builder.getArrayAttr(path)] = value.constant;
168 }
169 destination.aggregate->shapes.remove_if([path](const auto &entry) {
170 return startsWith(entry.first, path);
171 });
172 for (const auto &[key, shape] : snapshot.shapes) {
173 llvm::SmallVector<mlir::Attribute> target(path);
174 llvm::append_range(target, key.getValue());
175 destination.aggregate->shapes[builder.getArrayAttr(target)] = shape;
176 }
177 for (auto [key, constant] : snapshot.leaves) {
178 llvm::SmallVector<mlir::Attribute> target(path);
179 llvm::append_range(target, key.getValue());
180 destination.aggregate->leaves[builder.getArrayAttr(target)] = constant;
181 }
182 }
183};
184
185} // namespace llzk::polymorphic::detail
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for and distribution as defined by Sections through of this document Licensor shall mean the copyright owner or entity authorized by the copyright owner that is granting the License Legal Entity shall mean the union of the acting entity and all other entities that control are controlled by or are under common control with that entity For the purposes of this definition control direct or to cause the direction or management of such whether by contract or including but not limited to software source documentation source
Definition LICENSE.txt:28
void write(const DiscoveryValue &destination, llvm::ArrayRef< mlir::Attribute > path, const DiscoveryValue &value)
Replace a subtree, clearing old facts even when the new value is unknown.
DiscoveryStorage(mlir::MLIRContext *context)
DiscoveryValue read(const DiscoveryValue &source, llvm::ArrayRef< mlir::Attribute > path, bool aggregate)
Read a scalar leaf or snapshot an aggregate subtree into an independent root.
DiscoveryValue create()
Allocate an aggregate with no known leaves.
void forget(const DiscoveryValue &value)
Forget the contents of a potentially modified or escaping local aggregate.
void invalidate()
Drop memory facts at an unknown control-flow or call boundary.
llvm::ArrayRef< int64_t > getShape(const DiscoveryValue &value)
Return concrete root dimensions when available, including after value copies.
void setShape(const DiscoveryValue &value, llvm::ArrayRef< int64_t > shape, llvm::ArrayRef< mlir::Attribute > path={})
Record dimensions at an array root or a nested POD record path.
Common interface for constant parameter-like symbol operations that can be defined within a TemplateO...
llvm::DenseMap< mlir::ArrayAttr, mlir::Attribute > DiscoveryLeaves
Sparse known scalar leaves of a local aggregate, indexed by record/array paths.
Scalar contents and concrete array shapes, including nested aggregate roots.
void invalidate()
Writes may replace nested arrays, but cannot resize the root allocation.
llvm::DenseMap< mlir::ArrayAttr, llvm::SmallVector< int64_t > > shapes
A scalar constant or a local aggregate identity. Missing leaves are unknown.
std::shared_ptr< DiscoveryAggregate > aggregate
bool operator==(const DiscoveryValue &other) const