Skip to content

Commit 738388c

Browse files
committed
test: hash tree and collect hashes
1 parent 36ae3b8 commit 738388c

9 files changed

+491
-317
lines changed

src/collect-mcycle-hashes-state-access.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class collect_mcycle_hashes_state_access :
6969
using fast_addr_type = host_addr;
7070

7171
void mark_dirty_word(uint64_t address) const {
72-
constexpr uint64_t word_mask = ~UINT64_C(HASH_TREE_WORD_SIZE - 1);
72+
constexpr uint64_t word_mask = ~(HASH_TREE_WORD_SIZE - 1);
7373
m_c.dirty_words.insert(address & word_mask);
7474
}
7575

src/collect-uarch-cycle-hashes-state-access.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class collect_uarch_cycle_hashes_state_access :
5959

6060
private:
6161
void mark_dirty_word(uint64_t address) const {
62-
constexpr uint64_t word_mask = ~UINT64_C(HASH_TREE_WORD_SIZE - 1);
62+
constexpr uint64_t word_mask = ~(HASH_TREE_WORD_SIZE - 1);
6363
m_c.dirty_words.insert(address & word_mask);
6464
}
6565

src/hash-tree.cpp

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -130,22 +130,22 @@ hash_tree::proof_type hash_tree::get_proof(address_ranges ars, uint64_t address,
130130
// hit pristine node
131131
if (is_pristine(node_index)) {
132132
get_pristine_proof(curr_log2_size, proof);
133-
return proof;
133+
break;
134134
}
135135
const auto &node = m_sparse_nodes[node_index];
136136
assert(static_cast<int>(node.log2_size) == curr_log2_size && "incorrect node log2_size");
137137
// hit sparse tree node
138138
if (curr_log2_size == log2_size) {
139139
proof.set_target_hash(node.hash);
140-
return proof;
140+
break;
141141
}
142142
// transition to dense tree
143143
if (is_ar_node(node)) {
144144
auto &ar = ars[node.right];
145145
const int ar_log2_size = HASH_TREE_LOG2_PAGE_SIZE + ar.get_level_count() - 1;
146146
assert(curr_log2_size == ar_log2_size && "incorrect ar node log2_size");
147147
get_dense_proof(ar, ar_log2_size, address, proof);
148-
return proof;
148+
break;
149149
}
150150
// go down left or right on sparse tree depending on address
151151
--curr_log2_size;
@@ -157,6 +157,7 @@ hash_tree::proof_type hash_tree::get_proof(address_ranges ars, uint64_t address,
157157
node_index = node.right;
158158
}
159159
}
160+
return proof;
160161
}
161162

162163
hash_tree_stats hash_tree::get_stats(bool clear) noexcept {
@@ -211,7 +212,7 @@ bool hash_tree::enqueue_hash_dirty_page(page_hash_tree_cache::simd_page_hasher<v
211212
bool hash_tree::return_updated_dirty_pages(address_ranges ars, dirty_pages &batch,
212213
changed_address_ranges &changed_ars) {
213214
if (batch.empty()) {
214-
return true;
215+
return false;
215216
}
216217
//??D The batch size past which we switch to parallel updates needs to be tuned empirically
217218
const int batch_size = static_cast<int>(batch.size());
@@ -577,6 +578,7 @@ bool hash_tree::verify(address_ranges ars) const {
577578
auto actually_dirty = !std::ranges::equal(page_hash, dht.node_hash_view(offset, HASH_TREE_LOG2_PAGE_SIZE));
578579
ar_actually_dirty = ar_actually_dirty || actually_dirty;
579580
auto marked_dirty = dpt.is_node_dirty(offset, HASH_TREE_LOG2_PAGE_SIZE);
581+
// LCOV_EXCL_START
580582
if (actually_dirty != marked_dirty) {
581583
if (!marked_dirty) {
582584
ret = false;
@@ -621,6 +623,7 @@ bool hash_tree::verify(address_ranges ars) const {
621623
if (!std::ranges::equal(ar_node.hash, dht.node_hash_view(0, ar_log2_size))) {
622624
std::cerr << ar.get_description() << " dense hash tree root does not match sparse node hash\n";
623625
}
626+
// LCOV_EXCL_STOP
624627
++ar_node_index;
625628
}
626629
return ret;
@@ -641,7 +644,7 @@ bool hash_tree::update(address_ranges ars) {
641644
}
642645

643646
bool hash_tree::update_words(address_ranges ars, dirty_words_type dirty_words) {
644-
constexpr auto page_mask = ~UINT64_C(HASH_TREE_PAGE_SIZE - 1);
647+
constexpr auto page_mask = ~(HASH_TREE_PAGE_SIZE - 1);
645648
// Dirty_words contains an unordered set of the address of all words that became dirty since last update
646649
// We copy these to a sorted vector "words"
647650
// Every word in the same page must be updated by the same thread
@@ -671,9 +674,6 @@ bool hash_tree::update_words(address_ranges ars, dirty_words_type dirty_words) {
671674
uint64_t update_failures{0}; // NOLINT(misc-const-correctness)
672675
int ar_index = 0;
673676
int max_level_count = 0;
674-
variant_hasher h{m_hash_function};
675-
std::vector<uint64_t> curr_page_node;
676-
std::vector<uint64_t> next_page_node;
677677
dense_node_entries curr_dense_node;
678678
dense_node_entries next_dense_node;
679679
changed_address_ranges changed_ars;
@@ -688,13 +688,15 @@ bool hash_tree::update_words(address_ranges ars, dirty_words_type dirty_words) {
688688
continue;
689689
}
690690
auto &cache_entry = opt_br->get();
691-
while (!ars[ar_index].contains_absolute(paddr_page, HASH_TREE_PAGE_SIZE)) {
692-
if (ar_index >= static_cast<int>(ars.size())) {
693-
++update_failures;
694-
m_page_cache.return_entry(cache_entry);
695-
continue;
691+
for (; ar_index < static_cast<int>(ars.size()); ++ar_index) {
692+
if (ars[ar_index].contains_absolute(paddr_page, HASH_TREE_PAGE_SIZE)) {
693+
break;
696694
}
697-
++ar_index;
695+
}
696+
if (ar_index >= static_cast<int>(ars.size())) {
697+
++update_failures;
698+
m_page_cache.return_entry(cache_entry);
699+
continue;
698700
}
699701
auto &ar = ars[ar_index];
700702
if (changed_ars.empty() || changed_ars.back() != ar_index) {
@@ -710,41 +712,30 @@ bool hash_tree::update_words(address_ranges ars, dirty_words_type dirty_words) {
710712
const auto page_offset = paddr_page - ar.get_start();
711713
// if we had a cache hit, update only the words that changed in the cache
712714
if (hit) {
713-
auto page = std::span<const unsigned char, HASH_TREE_PAGE_SIZE>{base + page_offset, HASH_TREE_PAGE_SIZE};
715+
variant_hasher h{m_hash_function};
716+
page_hash_tree_cache::simd_page_hasher<variant_hasher> queue(h);
717+
const auto page =
718+
std::span<const unsigned char, HASH_TREE_PAGE_SIZE>{base + page_offset, HASH_TREE_PAGE_SIZE};
714719
auto &page_tree = cache_entry.get_page_hash_tree();
715720
const page_hash_tree_cache::page_view entry_page{cache_entry.get_page()};
716-
curr_page_node.clear();
717721
for (auto word_address : std::span(words).subspan(begin, end - begin)) {
718722
const auto word_offset = word_address - paddr_page;
719-
const auto page_word = page.subspan(word_offset, HASH_TREE_WORD_SIZE);
720-
const auto entry_word = entry_page.subspan(word_offset, HASH_TREE_WORD_SIZE);
721-
auto index = (HASH_TREE_PAGE_SIZE + word_offset) / HASH_TREE_WORD_SIZE;
722-
get_hash(h, page_word, page_tree[index]);
723+
const auto page_word =
724+
std::span<const unsigned char, HASH_TREE_WORD_SIZE>{page.subspan(word_offset, HASH_TREE_WORD_SIZE)};
725+
const auto entry_word =
726+
std::span<unsigned char, HASH_TREE_WORD_SIZE>{entry_page.subspan(word_offset, HASH_TREE_WORD_SIZE)};
727+
const auto index = static_cast<int>((HASH_TREE_PAGE_SIZE + word_offset) / HASH_TREE_WORD_SIZE);
728+
queue.enqueue_leaf(page_word, page_tree, index);
723729
std::ranges::copy(page_word, entry_word.begin());
724-
auto parent = index / 2;
725-
if (curr_page_node.empty() || curr_page_node.back() != parent) {
726-
curr_page_node.push_back(parent);
727-
}
728-
}
729-
for (int log2_size = HASH_TREE_LOG2_WORD_SIZE + 1; log2_size <= HASH_TREE_LOG2_PAGE_SIZE; ++log2_size) {
730-
next_page_node.clear();
731-
for (auto index : curr_page_node) {
732-
auto left = index * 2;
733-
auto right = (index * 2) + 1;
734-
get_concat_hash(h, page_tree[left], page_tree[right], page_tree[index]);
735-
auto parent = index / 2;
736-
if (next_page_node.empty() || next_page_node.back() != parent) { // We know parent != 0
737-
next_page_node.push_back(parent);
738-
}
739-
}
740-
std::swap(curr_page_node, next_page_node);
741730
}
731+
queue.flush();
742732
// otherwise, the update from scratch
743733
} else {
744734
bool changed = false;
745735
if (!update_dirty_page(ar, cache_entry, changed)) {
746736
++update_failures;
747737
m_page_cache.return_entry(cache_entry);
738+
continue;
748739
}
749740
}
750741
auto &dht = ar.get_dense_hash_tree();
@@ -760,17 +751,20 @@ bool hash_tree::update_words(address_ranges ars, dirty_words_type dirty_words) {
760751
for (int level = 1; level < max_level_count; ++level) {
761752
auto log2_size = HASH_TREE_LOG2_PAGE_SIZE + level;
762753
next_dense_node.clear();
754+
variant_hasher h{m_hash_function};
755+
simd_concat_hasher<variant_hasher, const_machine_hash_view> queue(h);
763756
for (auto &[dht, offset] : curr_dense_node) {
764757
auto child_size = UINT64_C(1) << (log2_size - 1);
765758
auto parent = dht.node_hash_view(offset, log2_size);
766759
auto left = dht.node_hash_view(offset, log2_size - 1);
767760
auto right = dht.node_hash_view(offset + child_size, log2_size - 1);
768-
get_concat_hash(h, left, right, parent);
761+
queue.enqueue(left, right, parent);
769762
auto dense_node = dense_node_entry{.dht = dht, .offset = get_aligned_address(offset, log2_size + 1)};
770763
if (next_dense_node.empty() || next_dense_node.back() != dense_node) {
771764
next_dense_node.push_back(dense_node);
772765
}
773766
}
767+
queue.flush();
774768
std::swap(curr_dense_node, next_dense_node);
775769
}
776770
return !static_cast<bool>(update_failures > 0) && update_sparse_tree(ars, changed_ars);
@@ -990,6 +984,7 @@ hash_tree::nodes_type hash_tree::create_nodes(const_address_ranges ars) {
990984
return nodes;
991985
}
992986

987+
// LCOV_EXCL_START
993988
void hash_tree::dump(const_address_ranges ars, std::ostream &out) {
994989
out << "digraph HashTree {\n";
995990
out << " rankdir=TB;\n";
@@ -1059,5 +1054,6 @@ void hash_tree::dump(const_address_ranges ars, std::ostream &out) {
10591054
}
10601055
out << "}\n";
10611056
}
1057+
// LCOV_EXCL_STOP
10621058

10631059
} // namespace cartesi

src/page-hash-tree-cache.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,9 @@ class page_hash_tree_cache {
408408
m_pristine_page_hash_tree{entry::get_pristine_page_hash_tree(std::forward<H>(h))},
409409
m_entries{num_entries, entry{m_pristine_page_hash_tree}} {
410410
m_map.reserve(num_entries);
411+
if (num_entries == 0) {
412+
throw std::invalid_argument{"page hash-tree cache must have at least one entry"};
413+
}
411414
}
412415

413416
page_hash_tree_cache(const page_hash_tree_cache &other) = delete;

tests/Makefile

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,6 @@ test-machine-with-log-step:
188188
test-uarch:
189189
$(LUA) ./lua/cartesi-machine-tests.lua --jobs=$(NUM_JOBS) run_uarch
190190

191-
test-hash-tree:
192-
$(LUA) ./lua/hash-tree.lua
193-
194191
test-uarch-compare:
195192
$(LUA) ./lua/cartesi-machine-tests.lua --test="^rv64ui.*$$" --concurrency=update_hash_tree:1 --jobs=$(NUM_JOBS) run_host_and_uarch
196193

@@ -257,7 +254,7 @@ coverage-report: $(COVERAGE_OUTPUT_DIR)
257254
export LLVM_PROFILE_FILE=coverage-%p.profraw
258255
endif
259256

260-
test: test-save-and-load test-yield-and-save test-machine test-uarch test-uarch-rv64ui test-uarch-interpreter test-hash-tree test-lua test-jsonrpc test-c-api test-hash test-cmio test-machine-with-log-step
257+
test: test-save-and-load test-yield-and-save test-machine test-uarch test-uarch-rv64ui test-uarch-interpreter test-lua test-jsonrpc test-c-api test-hash test-cmio test-machine-with-log-step
261258

262259
lint format check-format:
263260
@$(MAKE) -C misc $@

0 commit comments

Comments
 (0)