blob: 31062fb39038b336a3a68c7693e8ae6b1ef56743 [file] [log] [blame]
Vladimir Marko35831e82015-09-11 11:59:18 +01001/*
2 * Copyright (C) 2015 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#include <algorithm>
18#include <ostream>
19
20#include "compiled_method_storage.h"
21
Andreas Gampe57943812017-12-06 21:39:13 -080022#include <android-base/logging.h>
23
Vladimir Marko54159c62018-06-20 14:30:08 +010024#include "base/data_hash.h"
David Sehrc431b9d2018-03-02 12:01:51 -080025#include "base/utils.h"
Vladimir Marko35831e82015-09-11 11:59:18 +010026#include "compiled_method.h"
Vladimir Markod8dbc8d2017-09-20 13:37:47 +010027#include "linker/linker_patch.h"
Andreas Gampeb486a982017-06-01 13:45:54 -070028#include "thread-current-inl.h"
Vladimir Marko35831e82015-09-11 11:59:18 +010029#include "utils/dedupe_set-inl.h"
30#include "utils/swap_space.h"
31
32namespace art {
33
34namespace { // anonymous namespace
35
36template <typename T>
37const LengthPrefixedArray<T>* CopyArray(SwapSpace* swap_space, const ArrayRef<const T>& array) {
38 DCHECK(!array.empty());
39 SwapAllocator<uint8_t> allocator(swap_space);
40 void* storage = allocator.allocate(LengthPrefixedArray<T>::ComputeSize(array.size()));
41 LengthPrefixedArray<T>* array_copy = new(storage) LengthPrefixedArray<T>(array.size());
42 std::copy(array.begin(), array.end(), array_copy->begin());
43 return array_copy;
44}
45
46template <typename T>
47void ReleaseArray(SwapSpace* swap_space, const LengthPrefixedArray<T>* array) {
48 SwapAllocator<uint8_t> allocator(swap_space);
49 size_t size = LengthPrefixedArray<T>::ComputeSize(array->size());
50 array->~LengthPrefixedArray<T>();
51 allocator.deallocate(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(array)), size);
52}
53
54} // anonymous namespace
55
56template <typename T, typename DedupeSetType>
57inline const LengthPrefixedArray<T>* CompiledMethodStorage::AllocateOrDeduplicateArray(
58 const ArrayRef<const T>& data,
59 DedupeSetType* dedupe_set) {
60 if (data.empty()) {
61 return nullptr;
62 } else if (!DedupeEnabled()) {
63 return CopyArray(swap_space_.get(), data);
64 } else {
65 return dedupe_set->Add(Thread::Current(), data);
66 }
67}
68
69template <typename T>
70inline void CompiledMethodStorage::ReleaseArrayIfNotDeduplicated(
71 const LengthPrefixedArray<T>* array) {
72 if (array != nullptr && !DedupeEnabled()) {
73 ReleaseArray(swap_space_.get(), array);
74 }
75}
76
77template <typename ContentType>
78class CompiledMethodStorage::DedupeHashFunc {
79 private:
80 static constexpr bool kUseMurmur3Hash = true;
81
82 public:
83 size_t operator()(const ArrayRef<ContentType>& array) const {
Vladimir Marko54159c62018-06-20 14:30:08 +010084 return DataHash()(array);
Vladimir Marko35831e82015-09-11 11:59:18 +010085 }
86};
87
88template <typename T>
89class CompiledMethodStorage::LengthPrefixedArrayAlloc {
90 public:
91 explicit LengthPrefixedArrayAlloc(SwapSpace* swap_space)
92 : swap_space_(swap_space) {
93 }
94
95 const LengthPrefixedArray<T>* Copy(const ArrayRef<const T>& array) {
96 return CopyArray(swap_space_, array);
97 }
98
99 void Destroy(const LengthPrefixedArray<T>* array) {
100 ReleaseArray(swap_space_, array);
101 }
102
103 private:
104 SwapSpace* const swap_space_;
105};
106
Vladimir Markoca1e0382018-04-11 09:58:41 +0000107class CompiledMethodStorage::ThunkMapKey {
108 public:
109 ThunkMapKey(linker::LinkerPatch::Type type, uint32_t custom_value1, uint32_t custom_value2)
110 : type_(type), custom_value1_(custom_value1), custom_value2_(custom_value2) {}
111
112 bool operator<(const ThunkMapKey& other) const {
113 if (custom_value1_ != other.custom_value1_) {
114 return custom_value1_ < other.custom_value1_;
115 }
116 if (custom_value2_ != other.custom_value2_) {
117 return custom_value2_ < other.custom_value2_;
118 }
119 return type_ < other.type_;
120 }
121
122 private:
123 linker::LinkerPatch::Type type_;
124 uint32_t custom_value1_;
125 uint32_t custom_value2_;
126};
127
128class CompiledMethodStorage::ThunkMapValue {
129 public:
130 ThunkMapValue(std::vector<uint8_t, SwapAllocator<uint8_t>>&& code,
131 const std::string& debug_name)
132 : code_(std::move(code)), debug_name_(debug_name) {}
133
134 ArrayRef<const uint8_t> GetCode() const {
135 return ArrayRef<const uint8_t>(code_);
136 }
137
138 const std::string& GetDebugName() const {
139 return debug_name_;
140 }
141
142 private:
143 std::vector<uint8_t, SwapAllocator<uint8_t>> code_;
144 std::string debug_name_;
145};
146
Vladimir Marko35831e82015-09-11 11:59:18 +0100147CompiledMethodStorage::CompiledMethodStorage(int swap_fd)
148 : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)),
149 dedupe_enabled_(true),
150 dedupe_code_("dedupe code", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
Vladimir Marko35831e82015-09-11 11:59:18 +0100151 dedupe_vmap_table_("dedupe vmap table",
152 LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
Vladimir Marko35831e82015-09-11 11:59:18 +0100153 dedupe_cfi_info_("dedupe cfi info", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
154 dedupe_linker_patches_("dedupe cfi info",
Vladimir Markoca1e0382018-04-11 09:58:41 +0000155 LengthPrefixedArrayAlloc<linker::LinkerPatch>(swap_space_.get())),
156 thunk_map_lock_("thunk_map_lock"),
157 thunk_map_(std::less<ThunkMapKey>(), SwapAllocator<ThunkMapValueType>(swap_space_.get())) {
Vladimir Marko35831e82015-09-11 11:59:18 +0100158}
159
160CompiledMethodStorage::~CompiledMethodStorage() {
161 // All done by member destructors.
162}
163
164void CompiledMethodStorage::DumpMemoryUsage(std::ostream& os, bool extended) const {
165 if (swap_space_.get() != nullptr) {
Anton Kirilovdd9473b2016-01-28 15:08:01 +0000166 const size_t swap_size = swap_space_->GetSize();
167 os << " swap=" << PrettySize(swap_size) << " (" << swap_size << "B)";
Vladimir Marko35831e82015-09-11 11:59:18 +0100168 }
169 if (extended) {
170 Thread* self = Thread::Current();
171 os << "\nCode dedupe: " << dedupe_code_.DumpStats(self);
Vladimir Marko35831e82015-09-11 11:59:18 +0100172 os << "\nVmap table dedupe: " << dedupe_vmap_table_.DumpStats(self);
Vladimir Marko35831e82015-09-11 11:59:18 +0100173 os << "\nCFI info dedupe: " << dedupe_cfi_info_.DumpStats(self);
174 }
175}
176
177const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCode(
178 const ArrayRef<const uint8_t>& code) {
179 return AllocateOrDeduplicateArray(code, &dedupe_code_);
180}
181
182void CompiledMethodStorage::ReleaseCode(const LengthPrefixedArray<uint8_t>* code) {
183 ReleaseArrayIfNotDeduplicated(code);
184}
185
Vladimir Marko35831e82015-09-11 11:59:18 +0100186const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateVMapTable(
187 const ArrayRef<const uint8_t>& table) {
188 return AllocateOrDeduplicateArray(table, &dedupe_vmap_table_);
189}
190
191void CompiledMethodStorage::ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table) {
192 ReleaseArrayIfNotDeduplicated(table);
193}
194
Vladimir Marko35831e82015-09-11 11:59:18 +0100195const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCFIInfo(
196 const ArrayRef<const uint8_t>& cfi_info) {
197 return AllocateOrDeduplicateArray(cfi_info, &dedupe_cfi_info_);
198}
199
200void CompiledMethodStorage::ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info) {
201 ReleaseArrayIfNotDeduplicated(cfi_info);
202}
203
Vladimir Markod8dbc8d2017-09-20 13:37:47 +0100204const LengthPrefixedArray<linker::LinkerPatch>* CompiledMethodStorage::DeduplicateLinkerPatches(
205 const ArrayRef<const linker::LinkerPatch>& linker_patches) {
Vladimir Marko35831e82015-09-11 11:59:18 +0100206 return AllocateOrDeduplicateArray(linker_patches, &dedupe_linker_patches_);
207}
208
209void CompiledMethodStorage::ReleaseLinkerPatches(
Vladimir Markod8dbc8d2017-09-20 13:37:47 +0100210 const LengthPrefixedArray<linker::LinkerPatch>* linker_patches) {
Vladimir Marko35831e82015-09-11 11:59:18 +0100211 ReleaseArrayIfNotDeduplicated(linker_patches);
212}
213
Vladimir Markoca1e0382018-04-11 09:58:41 +0000214CompiledMethodStorage::ThunkMapKey CompiledMethodStorage::GetThunkMapKey(
215 const linker::LinkerPatch& linker_patch) {
216 uint32_t custom_value1 = 0u;
217 uint32_t custom_value2 = 0u;
218 switch (linker_patch.GetType()) {
219 case linker::LinkerPatch::Type::kBakerReadBarrierBranch:
220 custom_value1 = linker_patch.GetBakerCustomValue1();
221 custom_value2 = linker_patch.GetBakerCustomValue2();
222 break;
223 case linker::LinkerPatch::Type::kCallRelative:
224 // No custom values.
225 break;
226 default:
227 LOG(FATAL) << "Unexpected patch type: " << linker_patch.GetType();
228 UNREACHABLE();
229 }
230 return ThunkMapKey(linker_patch.GetType(), custom_value1, custom_value2);
231}
232
233ArrayRef<const uint8_t> CompiledMethodStorage::GetThunkCode(const linker::LinkerPatch& linker_patch,
234 /*out*/ std::string* debug_name) {
235 ThunkMapKey key = GetThunkMapKey(linker_patch);
236 MutexLock lock(Thread::Current(), thunk_map_lock_);
237 auto it = thunk_map_.find(key);
238 if (it != thunk_map_.end()) {
239 const ThunkMapValue& value = it->second;
240 if (debug_name != nullptr) {
241 *debug_name = value.GetDebugName();
242 }
243 return value.GetCode();
244 } else {
245 if (debug_name != nullptr) {
246 *debug_name = std::string();
247 }
248 return ArrayRef<const uint8_t>();
249 }
250}
251
252void CompiledMethodStorage::SetThunkCode(const linker::LinkerPatch& linker_patch,
253 ArrayRef<const uint8_t> code,
254 const std::string& debug_name) {
255 DCHECK(!code.empty());
256 ThunkMapKey key = GetThunkMapKey(linker_patch);
257 std::vector<uint8_t, SwapAllocator<uint8_t>> code_copy(
258 code.begin(), code.end(), SwapAllocator<uint8_t>(swap_space_.get()));
259 ThunkMapValue value(std::move(code_copy), debug_name);
260 MutexLock lock(Thread::Current(), thunk_map_lock_);
261 // Note: Multiple threads can try and compile the same thunk, so this may not create a new entry.
262 thunk_map_.emplace(key, std::move(value));
263}
264
Vladimir Marko35831e82015-09-11 11:59:18 +0100265} // namespace art