Andreas Gampe | 75a7db6 | 2016-09-26 12:04:26 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 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_IMT_CONFLICT_TABLE_H_ |
| 18 | #define ART_RUNTIME_IMT_CONFLICT_TABLE_H_ |
| 19 | |
| 20 | #include <cstddef> |
| 21 | |
| 22 | #include "base/casts.h" |
| 23 | #include "base/enums.h" |
| 24 | #include "base/macros.h" |
| 25 | |
| 26 | namespace art { |
| 27 | |
| 28 | class ArtMethod; |
| 29 | |
| 30 | // Table to resolve IMT conflicts at runtime. The table is attached to |
| 31 | // the jni entrypoint of IMT conflict ArtMethods. |
| 32 | // The table contains a list of pairs of { interface_method, implementation_method } |
| 33 | // with the last entry being null to make an assembly implementation of a lookup |
| 34 | // faster. |
| 35 | class ImtConflictTable { |
| 36 | enum MethodIndex { |
| 37 | kMethodInterface, |
| 38 | kMethodImplementation, |
| 39 | kMethodCount, // Number of elements in enum. |
| 40 | }; |
| 41 | |
| 42 | public: |
| 43 | // Build a new table copying `other` and adding the new entry formed of |
| 44 | // the pair { `interface_method`, `implementation_method` } |
| 45 | ImtConflictTable(ImtConflictTable* other, |
| 46 | ArtMethod* interface_method, |
| 47 | ArtMethod* implementation_method, |
| 48 | PointerSize pointer_size) { |
| 49 | const size_t count = other->NumEntries(pointer_size); |
| 50 | for (size_t i = 0; i < count; ++i) { |
| 51 | SetInterfaceMethod(i, pointer_size, other->GetInterfaceMethod(i, pointer_size)); |
| 52 | SetImplementationMethod(i, pointer_size, other->GetImplementationMethod(i, pointer_size)); |
| 53 | } |
| 54 | SetInterfaceMethod(count, pointer_size, interface_method); |
| 55 | SetImplementationMethod(count, pointer_size, implementation_method); |
| 56 | // Add the null marker. |
| 57 | SetInterfaceMethod(count + 1, pointer_size, nullptr); |
| 58 | SetImplementationMethod(count + 1, pointer_size, nullptr); |
| 59 | } |
| 60 | |
| 61 | // num_entries excludes the header. |
| 62 | ImtConflictTable(size_t num_entries, PointerSize pointer_size) { |
| 63 | SetInterfaceMethod(num_entries, pointer_size, nullptr); |
| 64 | SetImplementationMethod(num_entries, pointer_size, nullptr); |
| 65 | } |
| 66 | |
| 67 | // Set an entry at an index. |
| 68 | void SetInterfaceMethod(size_t index, PointerSize pointer_size, ArtMethod* method) { |
| 69 | SetMethod(index * kMethodCount + kMethodInterface, pointer_size, method); |
| 70 | } |
| 71 | |
| 72 | void SetImplementationMethod(size_t index, PointerSize pointer_size, ArtMethod* method) { |
| 73 | SetMethod(index * kMethodCount + kMethodImplementation, pointer_size, method); |
| 74 | } |
| 75 | |
| 76 | ArtMethod* GetInterfaceMethod(size_t index, PointerSize pointer_size) const { |
| 77 | return GetMethod(index * kMethodCount + kMethodInterface, pointer_size); |
| 78 | } |
| 79 | |
| 80 | ArtMethod* GetImplementationMethod(size_t index, PointerSize pointer_size) const { |
| 81 | return GetMethod(index * kMethodCount + kMethodImplementation, pointer_size); |
| 82 | } |
| 83 | |
Mathieu Chartier | 8c19d24 | 2017-03-06 12:35:10 -0800 | [diff] [blame] | 84 | void** AddressOfInterfaceMethod(size_t index, PointerSize pointer_size) { |
| 85 | return AddressOfMethod(index * kMethodCount + kMethodInterface, pointer_size); |
| 86 | } |
| 87 | |
| 88 | void** AddressOfImplementationMethod(size_t index, PointerSize pointer_size) { |
| 89 | return AddressOfMethod(index * kMethodCount + kMethodImplementation, pointer_size); |
| 90 | } |
| 91 | |
Andreas Gampe | 75a7db6 | 2016-09-26 12:04:26 -0700 | [diff] [blame] | 92 | // Return true if two conflict tables are the same. |
| 93 | bool Equals(ImtConflictTable* other, PointerSize pointer_size) const { |
| 94 | size_t num = NumEntries(pointer_size); |
| 95 | if (num != other->NumEntries(pointer_size)) { |
| 96 | return false; |
| 97 | } |
| 98 | for (size_t i = 0; i < num; ++i) { |
| 99 | if (GetInterfaceMethod(i, pointer_size) != other->GetInterfaceMethod(i, pointer_size) || |
| 100 | GetImplementationMethod(i, pointer_size) != |
| 101 | other->GetImplementationMethod(i, pointer_size)) { |
| 102 | return false; |
| 103 | } |
| 104 | } |
| 105 | return true; |
| 106 | } |
| 107 | |
| 108 | // Visit all of the entries. |
| 109 | // NO_THREAD_SAFETY_ANALYSIS for calling with held locks. Visitor is passed a pair of ArtMethod* |
| 110 | // and also returns one. The order is <interface, implementation>. |
| 111 | template<typename Visitor> |
| 112 | void Visit(const Visitor& visitor, PointerSize pointer_size) NO_THREAD_SAFETY_ANALYSIS { |
| 113 | uint32_t table_index = 0; |
| 114 | for (;;) { |
| 115 | ArtMethod* interface_method = GetInterfaceMethod(table_index, pointer_size); |
| 116 | if (interface_method == nullptr) { |
| 117 | break; |
| 118 | } |
| 119 | ArtMethod* implementation_method = GetImplementationMethod(table_index, pointer_size); |
| 120 | auto input = std::make_pair(interface_method, implementation_method); |
| 121 | std::pair<ArtMethod*, ArtMethod*> updated = visitor(input); |
| 122 | if (input.first != updated.first) { |
| 123 | SetInterfaceMethod(table_index, pointer_size, updated.first); |
| 124 | } |
| 125 | if (input.second != updated.second) { |
| 126 | SetImplementationMethod(table_index, pointer_size, updated.second); |
| 127 | } |
| 128 | ++table_index; |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | // Lookup the implementation ArtMethod associated to `interface_method`. Return null |
| 133 | // if not found. |
| 134 | ArtMethod* Lookup(ArtMethod* interface_method, PointerSize pointer_size) const { |
| 135 | uint32_t table_index = 0; |
| 136 | for (;;) { |
| 137 | ArtMethod* current_interface_method = GetInterfaceMethod(table_index, pointer_size); |
| 138 | if (current_interface_method == nullptr) { |
| 139 | break; |
| 140 | } |
| 141 | if (current_interface_method == interface_method) { |
| 142 | return GetImplementationMethod(table_index, pointer_size); |
| 143 | } |
| 144 | ++table_index; |
| 145 | } |
| 146 | return nullptr; |
| 147 | } |
| 148 | |
| 149 | // Compute the number of entries in this table. |
| 150 | size_t NumEntries(PointerSize pointer_size) const { |
| 151 | uint32_t table_index = 0; |
| 152 | while (GetInterfaceMethod(table_index, pointer_size) != nullptr) { |
| 153 | ++table_index; |
| 154 | } |
| 155 | return table_index; |
| 156 | } |
| 157 | |
| 158 | // Compute the size in bytes taken by this table. |
| 159 | size_t ComputeSize(PointerSize pointer_size) const { |
| 160 | // Add the end marker. |
| 161 | return ComputeSize(NumEntries(pointer_size), pointer_size); |
| 162 | } |
| 163 | |
| 164 | // Compute the size in bytes needed for copying the given `table` and add |
| 165 | // one more entry. |
| 166 | static size_t ComputeSizeWithOneMoreEntry(ImtConflictTable* table, PointerSize pointer_size) { |
| 167 | return table->ComputeSize(pointer_size) + EntrySize(pointer_size); |
| 168 | } |
| 169 | |
| 170 | // Compute size with a fixed number of entries. |
| 171 | static size_t ComputeSize(size_t num_entries, PointerSize pointer_size) { |
| 172 | return (num_entries + 1) * EntrySize(pointer_size); // Add one for null terminator. |
| 173 | } |
| 174 | |
| 175 | static size_t EntrySize(PointerSize pointer_size) { |
| 176 | return static_cast<size_t>(pointer_size) * static_cast<size_t>(kMethodCount); |
| 177 | } |
| 178 | |
| 179 | private: |
Mathieu Chartier | 8c19d24 | 2017-03-06 12:35:10 -0800 | [diff] [blame] | 180 | void** AddressOfMethod(size_t index, PointerSize pointer_size) { |
| 181 | if (pointer_size == PointerSize::k64) { |
| 182 | return reinterpret_cast<void**>(&data64_[index]); |
| 183 | } else { |
| 184 | return reinterpret_cast<void**>(&data32_[index]); |
| 185 | } |
| 186 | } |
| 187 | |
Andreas Gampe | 75a7db6 | 2016-09-26 12:04:26 -0700 | [diff] [blame] | 188 | ArtMethod* GetMethod(size_t index, PointerSize pointer_size) const { |
| 189 | if (pointer_size == PointerSize::k64) { |
| 190 | return reinterpret_cast<ArtMethod*>(static_cast<uintptr_t>(data64_[index])); |
| 191 | } else { |
| 192 | return reinterpret_cast<ArtMethod*>(static_cast<uintptr_t>(data32_[index])); |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | void SetMethod(size_t index, PointerSize pointer_size, ArtMethod* method) { |
| 197 | if (pointer_size == PointerSize::k64) { |
| 198 | data64_[index] = dchecked_integral_cast<uint64_t>(reinterpret_cast<uintptr_t>(method)); |
| 199 | } else { |
| 200 | data32_[index] = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(method)); |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | // Array of entries that the assembly stubs will iterate over. Note that this is |
| 205 | // not fixed size, and we allocate data prior to calling the constructor |
| 206 | // of ImtConflictTable. |
| 207 | union { |
| 208 | uint32_t data32_[0]; |
| 209 | uint64_t data64_[0]; |
| 210 | }; |
| 211 | |
| 212 | DISALLOW_COPY_AND_ASSIGN(ImtConflictTable); |
| 213 | }; |
| 214 | |
| 215 | } // namespace art |
| 216 | |
| 217 | #endif // ART_RUNTIME_IMT_CONFLICT_TABLE_H_ |