1 /** 2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef LIBPANDAFILE_PANDA_CACHE_H_ 17 #define LIBPANDAFILE_PANDA_CACHE_H_ 18 19 #include "file.h" 20 #include "os/mutex.h" 21 #include "libpandabase/utils/math_helpers.h" 22 23 #include <atomic> 24 #include <vector> 25 26 namespace ark { 27 28 class Method; 29 class Field; 30 class Class; 31 32 namespace panda_file { 33 34 // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions) 35 class PandaCache { 36 public: 37 struct MethodCachePair { 38 File::EntityId id; 39 Method *ptr {nullptr}; 40 }; 41 42 struct FieldCachePair { 43 File::EntityId id; 44 Field *ptr {nullptr}; 45 }; 46 47 struct ClassCachePair { 48 File::EntityId id; 49 Class *ptr {nullptr}; 50 }; 51 PandaCache()52 PandaCache() 53 : methodCacheSize_(DEFAULT_METHOD_CACHE_SIZE), 54 fieldCacheSize_(DEFAULT_FIELD_CACHE_SIZE), 55 classCacheSize_(DEFAULT_CLASS_CACHE_SIZE) 56 { 57 methodCache_.resize(methodCacheSize_, MethodCachePair()); 58 fieldCache_.resize(fieldCacheSize_, FieldCachePair()); 59 classCache_.resize(classCacheSize_, ClassCachePair()); 60 } 61 62 ~PandaCache() = default; 63 GetMethodIndex(File::EntityId id)64 inline uint32_t GetMethodIndex(File::EntityId id) const 65 { 66 return ark::helpers::math::PowerOfTwoTableSlot(id.GetOffset(), methodCacheSize_); 67 } 68 GetFieldIndex(File::EntityId id)69 inline uint32_t GetFieldIndex(File::EntityId id) const 70 { 71 // lowest one or two bits is very likely same between different fields 72 return ark::helpers::math::PowerOfTwoTableSlot(id.GetOffset(), fieldCacheSize_, 2U); 73 } 74 GetClassIndex(File::EntityId id)75 inline uint32_t GetClassIndex(File::EntityId id) const 76 { 77 return ark::helpers::math::PowerOfTwoTableSlot(id.GetOffset(), classCacheSize_); 78 } 79 GetMethodFromCache(File::EntityId id)80 inline Method *GetMethodFromCache(File::EntityId id) const 81 { 82 // Emulator target doesn't support atomic operations with 128bit structures like MethodCachePair. 83 // Compiler __atomic_load call which is not implemented in emulator target. 84 #ifndef PANDA_TARGET_EMULATOR 85 uint32_t index = GetMethodIndex(id); 86 auto *pairPtr = 87 reinterpret_cast<std::atomic<MethodCachePair> *>(reinterpret_cast<uintptr_t>(&(methodCache_[index]))); 88 // Atomic with acquire order reason: fixes a data race with method_cache_ 89 auto pair = pairPtr->load(std::memory_order_acquire); 90 TSAN_ANNOTATE_HAPPENS_AFTER(pairPtr); 91 if (pair.id == id) { 92 return pair.ptr; 93 } 94 #endif 95 return nullptr; 96 } 97 SetMethodCache(File::EntityId id,Method * method)98 inline void SetMethodCache(File::EntityId id, Method *method) 99 { 100 // Emulator target doesn't support atomic operations with 128bit structures like MethodCachePair. 101 // Compiler __atomic_load call which is not implemented in emulator target. 102 #ifndef PANDA_TARGET_EMULATOR 103 MethodCachePair pair; 104 pair.id = id; 105 pair.ptr = method; 106 uint32_t index = GetMethodIndex(id); 107 auto *pairPtr = 108 reinterpret_cast<std::atomic<MethodCachePair> *>(reinterpret_cast<uintptr_t>(&(methodCache_[index]))); 109 TSAN_ANNOTATE_HAPPENS_BEFORE(pairPtr); 110 // Atomic with release order reason: fixes a data race with method_cache_ 111 pairPtr->store(pair, std::memory_order_release); 112 #endif 113 } 114 GetFieldFromCache(File::EntityId id)115 inline Field *GetFieldFromCache(File::EntityId id) const 116 { 117 // Emulator target doesn't support atomic operations with 128bit structures like FieldCachePair. 118 // Compiler __atomic_load call which is not implemented in emulator target. 119 #ifndef PANDA_TARGET_EMULATOR 120 uint32_t index = GetFieldIndex(id); 121 auto *pairPtr = 122 reinterpret_cast<std::atomic<FieldCachePair> *>(reinterpret_cast<uintptr_t>(&(fieldCache_[index]))); 123 // Atomic with acquire order reason: fixes a data race with field_cache_ 124 auto pair = pairPtr->load(std::memory_order_acquire); 125 TSAN_ANNOTATE_HAPPENS_AFTER(pairPtr); 126 if (pair.id == id) { 127 return pair.ptr; 128 } 129 #endif 130 return nullptr; 131 } 132 SetFieldCache(File::EntityId id,Field * field)133 inline void SetFieldCache(File::EntityId id, Field *field) 134 { 135 // Emulator target doesn't support atomic operations with 128bit structures like FieldCachePair. 136 // Compiler __atomic_load call which is not implemented in emulator target. 137 #ifndef PANDA_TARGET_EMULATOR 138 uint32_t index = GetFieldIndex(id); 139 auto *pairPtr = 140 reinterpret_cast<std::atomic<FieldCachePair> *>(reinterpret_cast<uintptr_t>(&(fieldCache_[index]))); 141 FieldCachePair pair; 142 pair.id = id; 143 pair.ptr = field; 144 TSAN_ANNOTATE_HAPPENS_BEFORE(pairPtr); 145 // Atomic with release order reason: fixes a data race with field_cache_ 146 pairPtr->store(pair, std::memory_order_release); 147 #endif 148 } 149 GetClassFromCache(File::EntityId id)150 inline Class *GetClassFromCache(File::EntityId id) const 151 { 152 // Emulator target doesn't support atomic operations with 128bit structures like ClassCachePair. 153 // Compiler __atomic_load call which is not implemented in emulator target. 154 #ifndef PANDA_TARGET_EMULATOR 155 uint32_t index = GetClassIndex(id); 156 auto *pairPtr = 157 reinterpret_cast<std::atomic<ClassCachePair> *>(reinterpret_cast<uintptr_t>(&(classCache_[index]))); 158 // Atomic with acquire order reason: fixes a data race with class_cache_ 159 auto pair = pairPtr->load(std::memory_order_acquire); 160 TSAN_ANNOTATE_HAPPENS_AFTER(pairPtr); 161 if (pair.id == id) { 162 return pair.ptr; 163 } 164 #endif 165 return nullptr; 166 } 167 SetClassCache(File::EntityId id,Class * clazz)168 inline void SetClassCache(File::EntityId id, Class *clazz) 169 { 170 // Emulator target doesn't support atomic operations with 128bit structures like ClassCachePair. 171 // Compiler __atomic_load call which is not implemented in emulator target. 172 #ifndef PANDA_TARGET_EMULATOR 173 ClassCachePair pair; 174 pair.id = id; 175 pair.ptr = clazz; 176 uint32_t index = GetClassIndex(id); 177 auto *pairPtr = 178 reinterpret_cast<std::atomic<ClassCachePair> *>(reinterpret_cast<uintptr_t>(&(classCache_[index]))); 179 TSAN_ANNOTATE_HAPPENS_BEFORE(pairPtr); 180 // Atomic with release order reason: fixes a data race with class_cache_ 181 pairPtr->store(pair, std::memory_order_release); 182 #endif 183 } 184 Clear()185 inline void Clear() 186 { 187 methodCache_.clear(); 188 fieldCache_.clear(); 189 classCache_.clear(); 190 191 methodCache_.resize(methodCacheSize_, MethodCachePair()); 192 fieldCache_.resize(fieldCacheSize_, FieldCachePair()); 193 classCache_.resize(classCacheSize_, ClassCachePair()); 194 } 195 196 template <class Callback> EnumerateCachedClasses(const Callback & cb)197 bool EnumerateCachedClasses(const Callback &cb) 198 { 199 for (uint32_t i = 0; i < classCacheSize_; i++) { 200 auto *pairPtr = 201 reinterpret_cast<std::atomic<ClassCachePair> *>(reinterpret_cast<uintptr_t>(&(classCache_[i]))); 202 // Atomic with acquire order reason: fixes a data race with class_cache_ 203 auto pair = pairPtr->load(std::memory_order_acquire); 204 TSAN_ANNOTATE_HAPPENS_AFTER(pairPtr); 205 if (pair.ptr != nullptr) { 206 if (!cb(pair.ptr)) { 207 return false; 208 } 209 } 210 } 211 return true; 212 } 213 214 private: 215 static constexpr uint32_t DEFAULT_FIELD_CACHE_SIZE = 1024U; 216 static constexpr uint32_t DEFAULT_METHOD_CACHE_SIZE = 1024U; 217 static constexpr uint32_t DEFAULT_CLASS_CACHE_SIZE = 1024U; 218 static_assert(ark::helpers::math::IsPowerOfTwo(DEFAULT_FIELD_CACHE_SIZE)); 219 static_assert(ark::helpers::math::IsPowerOfTwo(DEFAULT_METHOD_CACHE_SIZE)); 220 static_assert(ark::helpers::math::IsPowerOfTwo(DEFAULT_CLASS_CACHE_SIZE)); 221 222 const uint32_t methodCacheSize_; 223 const uint32_t fieldCacheSize_; 224 const uint32_t classCacheSize_; 225 226 std::vector<MethodCachePair> methodCache_; 227 std::vector<FieldCachePair> fieldCache_; 228 std::vector<ClassCachePair> classCache_; 229 }; 230 231 } // namespace panda_file 232 } // namespace ark 233 234 #endif 235