Home | History | Annotate | Download | only in interpreter
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #ifndef ART_RUNTIME_INTERPRETER_INTERPRETER_CACHE_H_
     18 #define ART_RUNTIME_INTERPRETER_INTERPRETER_CACHE_H_
     19 
     20 #include <array>
     21 #include <atomic>
     22 
     23 #include "base/bit_utils.h"
     24 #include "base/macros.h"
     25 
     26 namespace art {
     27 
     28 class Thread;
     29 
     30 // Small fast thread-local cache for the interpreter.
     31 // It can hold arbitrary pointer-sized key-value pair.
     32 // The interpretation of the value depends on the key.
     33 // Presence of entry might imply some pre-conditions.
     34 // All operations must be done from the owning thread,
     35 // or at a point when the owning thread is suspended.
     36 //
     37 // The key-value pairs stored in the cache currently are:
     38 //   iget/iput: The field offset. The field must be non-volatile.
     39 //   sget/sput: The ArtField* pointer. The field must be non-volitile.
     40 //   invoke: The ArtMethod* pointer (before vtable indirection, etc).
     41 //
     42 // We ensure consistency of the cache by clearing it
     43 // whenever any dex file is unloaded.
     44 //
     45 // Aligned to 16-bytes to make it easier to get the address of the cache
     46 // from assembly (it ensures that the offset is valid immediate value).
     47 class ALIGNED(16) InterpreterCache {
     48   // Aligned since we load the whole entry in single assembly instruction.
     49   typedef std::pair<const void*, size_t> Entry ALIGNED(2 * sizeof(size_t));
     50 
     51  public:
     52   // 2x size increase/decrease corresponds to ~0.5% interpreter performance change.
     53   // Value of 256 has around 75% cache hit rate.
     54   static constexpr size_t kSize = 256;
     55 
     56   InterpreterCache() {
     57     // We can not use the Clear() method since the constructor will not
     58     // be called from the owning thread.
     59     data_.fill(Entry{});
     60   }
     61 
     62   // Clear the whole cache. It requires the owning thread for DCHECKs.
     63   void Clear(Thread* owning_thread);
     64 
     65   ALWAYS_INLINE bool Get(const void* key, /* out */ size_t* value) {
     66     DCHECK(IsCalledFromOwningThread());
     67     Entry& entry = data_[IndexOf(key)];
     68     if (LIKELY(entry.first == key)) {
     69       *value = entry.second;
     70       return true;
     71     }
     72     return false;
     73   }
     74 
     75   ALWAYS_INLINE void Set(const void* key, size_t value) {
     76     DCHECK(IsCalledFromOwningThread());
     77     data_[IndexOf(key)] = Entry{key, value};
     78   }
     79 
     80  private:
     81   bool IsCalledFromOwningThread();
     82 
     83   static ALWAYS_INLINE size_t IndexOf(const void* key) {
     84     static_assert(IsPowerOfTwo(kSize), "Size must be power of two");
     85     size_t index = (reinterpret_cast<uintptr_t>(key) >> 2) & (kSize - 1);
     86     DCHECK_LT(index, kSize);
     87     return index;
     88   }
     89 
     90   std::array<Entry, kSize> data_;
     91 };
     92 
     93 }  // namespace art
     94 
     95 #endif  // ART_RUNTIME_INTERPRETER_INTERPRETER_CACHE_H_
     96