| 1 | //===-- sanitizer_chained_origin_depot.cpp --------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // A storage for chained origins. |
| 10 | //===----------------------------------------------------------------------===// |
| 11 | |
| 12 | #include "sanitizer_chained_origin_depot.h" |
| 13 | |
| 14 | #include "sanitizer_stackdepotbase.h" |
| 15 | |
| 16 | namespace __sanitizer { |
| 17 | |
| 18 | namespace { |
| 19 | struct ChainedOriginDepotDesc { |
| 20 | u32 here_id; |
| 21 | u32 prev_id; |
| 22 | }; |
| 23 | |
| 24 | struct ChainedOriginDepotNode { |
| 25 | using hash_type = u32; |
| 26 | u32 link; |
| 27 | u32 here_id; |
| 28 | u32 prev_id; |
| 29 | |
| 30 | typedef ChainedOriginDepotDesc args_type; |
| 31 | |
| 32 | bool eq(hash_type hash, const args_type &args) const; |
| 33 | |
| 34 | static uptr allocated() { return 0; } |
| 35 | |
| 36 | static hash_type hash(const args_type &args); |
| 37 | |
| 38 | static bool is_valid(const args_type &args); |
| 39 | |
| 40 | void store(u32 id, const args_type &args, hash_type other_hash); |
| 41 | |
| 42 | args_type load(u32 id) const; |
| 43 | |
| 44 | struct Handle { |
| 45 | const ChainedOriginDepotNode *node_ = nullptr; |
| 46 | u32 id_ = 0; |
| 47 | Handle(const ChainedOriginDepotNode *node, u32 id) : node_(node), id_(id) {} |
| 48 | bool valid() const { return node_; } |
| 49 | u32 id() const { return id_; } |
| 50 | int here_id() const { return node_->here_id; } |
| 51 | int prev_id() const { return node_->prev_id; } |
| 52 | }; |
| 53 | |
| 54 | static Handle get_handle(u32 id); |
| 55 | |
| 56 | typedef Handle handle_type; |
| 57 | }; |
| 58 | |
| 59 | } // namespace |
| 60 | |
| 61 | static StackDepotBase<ChainedOriginDepotNode, 4, 20> depot; |
| 62 | |
| 63 | bool ChainedOriginDepotNode::eq(hash_type hash, const args_type &args) const { |
| 64 | return here_id == args.here_id && prev_id == args.prev_id; |
| 65 | } |
| 66 | |
| 67 | /* This is murmur2 hash for the 64->32 bit case. |
| 68 | It does not behave all that well because the keys have a very biased |
| 69 | distribution (I've seen 7-element buckets with the table only 14% full). |
| 70 | |
| 71 | here_id is built of |
| 72 | * (1 bits) Reserved, zero. |
| 73 | * (8 bits) Part id = bits 13..20 of the hash value of here_id's key. |
| 74 | * (23 bits) Sequential number (each part has each own sequence). |
| 75 | |
| 76 | prev_id has either the same distribution as here_id (but with 3:8:21) |
| 77 | split, or one of two reserved values (-1) or (-2). Either case can |
| 78 | dominate depending on the workload. |
| 79 | */ |
| 80 | ChainedOriginDepotNode::hash_type ChainedOriginDepotNode::hash( |
| 81 | const args_type &args) { |
| 82 | const u32 m = 0x5bd1e995; |
| 83 | const u32 seed = 0x9747b28c; |
| 84 | const u32 r = 24; |
| 85 | u32 h = seed; |
| 86 | u32 k = args.here_id; |
| 87 | k *= m; |
| 88 | k ^= k >> r; |
| 89 | k *= m; |
| 90 | h *= m; |
| 91 | h ^= k; |
| 92 | |
| 93 | k = args.prev_id; |
| 94 | k *= m; |
| 95 | k ^= k >> r; |
| 96 | k *= m; |
| 97 | h *= m; |
| 98 | h ^= k; |
| 99 | |
| 100 | h ^= h >> 13; |
| 101 | h *= m; |
| 102 | h ^= h >> 15; |
| 103 | return h; |
| 104 | } |
| 105 | |
| 106 | bool ChainedOriginDepotNode::is_valid(const args_type &args) { return true; } |
| 107 | |
| 108 | void ChainedOriginDepotNode::store(u32 id, const args_type &args, |
| 109 | hash_type other_hash) { |
| 110 | here_id = args.here_id; |
| 111 | prev_id = args.prev_id; |
| 112 | } |
| 113 | |
| 114 | ChainedOriginDepotNode::args_type ChainedOriginDepotNode::load(u32 id) const { |
| 115 | args_type ret = {.here_id: here_id, .prev_id: prev_id}; |
| 116 | return ret; |
| 117 | } |
| 118 | |
| 119 | ChainedOriginDepotNode::Handle ChainedOriginDepotNode::get_handle(u32 id) { |
| 120 | return Handle(&depot.nodes[id], id); |
| 121 | } |
| 122 | |
| 123 | ChainedOriginDepot::ChainedOriginDepot() {} |
| 124 | |
| 125 | StackDepotStats ChainedOriginDepot::GetStats() const { |
| 126 | return depot.GetStats(); |
| 127 | } |
| 128 | |
| 129 | bool ChainedOriginDepot::Put(u32 here_id, u32 prev_id, u32 *new_id) { |
| 130 | ChainedOriginDepotDesc desc = {.here_id: here_id, .prev_id: prev_id}; |
| 131 | bool inserted; |
| 132 | *new_id = depot.Put(args: desc, inserted: &inserted); |
| 133 | return inserted; |
| 134 | } |
| 135 | |
| 136 | u32 ChainedOriginDepot::Get(u32 id, u32 *other) { |
| 137 | ChainedOriginDepotDesc desc = depot.Get(id); |
| 138 | *other = desc.prev_id; |
| 139 | return desc.here_id; |
| 140 | } |
| 141 | |
| 142 | void ChainedOriginDepot::LockBeforeFork() { depot.LockBeforeFork(); } |
| 143 | |
| 144 | void ChainedOriginDepot::UnlockAfterFork(bool fork_child) { |
| 145 | depot.UnlockAfterFork(fork_child); |
| 146 | } |
| 147 | |
| 148 | void ChainedOriginDepot::TestOnlyUnmap() { depot.TestOnlyUnmap(); } |
| 149 | |
| 150 | } // namespace __sanitizer |
| 151 | |