1 /*
2 * Copyright (C) 2011 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 "dex_to_dex_compiler.h"
18
19 #include <android-base/logging.h>
20 #include <android-base/stringprintf.h>
21
22 #include "art_field-inl.h"
23 #include "art_method-inl.h"
24 #include "base/logging.h" // For VLOG
25 #include "base/macros.h"
26 #include "base/mutex.h"
27 #include "compiled_method.h"
28 #include "dex/bytecode_utils.h"
29 #include "dex/dex_file-inl.h"
30 #include "dex/dex_instruction-inl.h"
31 #include "dex_to_dex_decompiler.h"
32 #include "driver/compiler_driver.h"
33 #include "driver/dex_compilation_unit.h"
34 #include "mirror/dex_cache.h"
35 #include "quicken_info.h"
36 #include "thread-current-inl.h"
37
38 namespace art {
39 namespace optimizer {
40
41 using android::base::StringPrintf;
42
43 // Controls quickening activation.
44 const bool kEnableQuickening = true;
45 // Control check-cast elision.
46 const bool kEnableCheckCastEllision = true;
47
48 // Holds the state for compiling a single method.
49 struct DexToDexCompiler::CompilationState {
50 struct QuickenedInfo {
QuickenedInfoart::optimizer::DexToDexCompiler::CompilationState::QuickenedInfo51 QuickenedInfo(uint32_t pc, uint16_t index) : dex_pc(pc), dex_member_index(index) {}
52
53 uint32_t dex_pc;
54 uint16_t dex_member_index;
55 };
56
57 CompilationState(DexToDexCompiler* compiler,
58 const DexCompilationUnit& unit,
59 const CompilationLevel compilation_level,
60 const std::vector<uint8_t>* quicken_data);
61
GetQuickenedInfoart::optimizer::DexToDexCompiler::CompilationState62 const std::vector<QuickenedInfo>& GetQuickenedInfo() const {
63 return quickened_info_;
64 }
65
66 // Returns the quickening info, or an empty array if it was not quickened.
67 // If already_quickened is true, then don't change anything but still return what the quicken
68 // data would have been.
69 std::vector<uint8_t> Compile();
70
71 const DexFile& GetDexFile() const;
72
73 // Compiles a RETURN-VOID into a RETURN-VOID-BARRIER within a constructor where
74 // a barrier is required.
75 void CompileReturnVoid(Instruction* inst, uint32_t dex_pc);
76
77 // Compiles a CHECK-CAST into 2 NOP instructions if it is known to be safe. In
78 // this case, returns the second NOP instruction pointer. Otherwise, returns
79 // the given "inst".
80 Instruction* CompileCheckCast(Instruction* inst, uint32_t dex_pc);
81
82 // Compiles a field access into a quick field access.
83 // The field index is replaced by an offset within an Object where we can read
84 // from / write to this field. Therefore, this does not involve any resolution
85 // at runtime.
86 // Since the field index is encoded with 16 bits, we can replace it only if the
87 // field offset can be encoded with 16 bits too.
88 void CompileInstanceFieldAccess(Instruction* inst, uint32_t dex_pc,
89 Instruction::Code new_opcode, bool is_put);
90
91 // Compiles a virtual method invocation into a quick virtual method invocation.
92 // The method index is replaced by the vtable index where the corresponding
93 // executable can be found. Therefore, this does not involve any resolution
94 // at runtime.
95 // Since the method index is encoded with 16 bits, we can replace it only if the
96 // vtable index can be encoded with 16 bits too.
97 void CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc,
98 Instruction::Code new_opcode, bool is_range);
99
100 // Return the next index.
101 uint16_t NextIndex();
102
103 // Returns the dequickened index if an instruction is quickened, otherwise return index.
104 uint16_t GetIndexForInstruction(const Instruction* inst, uint32_t index);
105
106 DexToDexCompiler* const compiler_;
107 CompilerDriver& driver_;
108 const DexCompilationUnit& unit_;
109 const CompilationLevel compilation_level_;
110
111 // Filled by the compiler when quickening, in order to encode that information
112 // in the .oat file. The runtime will use that information to get to the original
113 // opcodes.
114 std::vector<QuickenedInfo> quickened_info_;
115
116 // True if we optimized a return void to a return void no barrier.
117 bool optimized_return_void_ = false;
118
119 // If the code item was already quickened previously.
120 const bool already_quickened_;
121 const QuickenInfoTable existing_quicken_info_;
122 uint32_t quicken_index_ = 0u;
123
124 DISALLOW_COPY_AND_ASSIGN(CompilationState);
125 };
126
DexToDexCompiler(CompilerDriver * driver)127 DexToDexCompiler::DexToDexCompiler(CompilerDriver* driver)
128 : driver_(driver),
129 lock_("Quicken lock", kDexToDexCompilerLock) {
130 DCHECK(driver != nullptr);
131 }
132
ClearState()133 void DexToDexCompiler::ClearState() {
134 MutexLock lock(Thread::Current(), lock_);
135 active_dex_file_ = nullptr;
136 active_bit_vector_ = nullptr;
137 should_quicken_.clear();
138 shared_code_item_quicken_info_.clear();
139 }
140
NumCodeItemsToQuicken(Thread * self) const141 size_t DexToDexCompiler::NumCodeItemsToQuicken(Thread* self) const {
142 MutexLock lock(self, lock_);
143 return num_code_items_;
144 }
145
GetOrAddBitVectorForDex(const DexFile * dex_file)146 BitVector* DexToDexCompiler::GetOrAddBitVectorForDex(const DexFile* dex_file) {
147 if (active_dex_file_ != dex_file) {
148 active_dex_file_ = dex_file;
149 auto inserted = should_quicken_.emplace(dex_file,
150 BitVector(dex_file->NumMethodIds(),
151 /*expandable*/ false,
152 Allocator::GetMallocAllocator()));
153 active_bit_vector_ = &inserted.first->second;
154 }
155 return active_bit_vector_;
156 }
157
MarkForCompilation(Thread * self,const MethodReference & method_ref)158 void DexToDexCompiler::MarkForCompilation(Thread* self,
159 const MethodReference& method_ref) {
160 MutexLock lock(self, lock_);
161 BitVector* const bitmap = GetOrAddBitVectorForDex(method_ref.dex_file);
162 DCHECK(bitmap != nullptr);
163 DCHECK(!bitmap->IsBitSet(method_ref.index));
164 bitmap->SetBit(method_ref.index);
165 ++num_code_items_;
166 }
167
CompilationState(DexToDexCompiler * compiler,const DexCompilationUnit & unit,const CompilationLevel compilation_level,const std::vector<uint8_t> * quicken_data)168 DexToDexCompiler::CompilationState::CompilationState(DexToDexCompiler* compiler,
169 const DexCompilationUnit& unit,
170 const CompilationLevel compilation_level,
171 const std::vector<uint8_t>* quicken_data)
172 : compiler_(compiler),
173 driver_(*compiler->GetDriver()),
174 unit_(unit),
175 compilation_level_(compilation_level),
176 already_quickened_(quicken_data != nullptr),
177 existing_quicken_info_(already_quickened_
178 ? ArrayRef<const uint8_t>(*quicken_data) : ArrayRef<const uint8_t>()) {}
179
NextIndex()180 uint16_t DexToDexCompiler::CompilationState::NextIndex() {
181 DCHECK(already_quickened_);
182 if (kIsDebugBuild && quicken_index_ >= existing_quicken_info_.NumIndices()) {
183 for (const DexInstructionPcPair& pair : unit_.GetCodeItemAccessor()) {
184 LOG(ERROR) << pair->DumpString(nullptr);
185 }
186 LOG(FATAL) << "Mismatched number of quicken slots.";
187 }
188 const uint16_t ret = existing_quicken_info_.GetData(quicken_index_);
189 quicken_index_++;
190 return ret;
191 }
192
GetIndexForInstruction(const Instruction * inst,uint32_t index)193 uint16_t DexToDexCompiler::CompilationState::GetIndexForInstruction(const Instruction* inst,
194 uint32_t index) {
195 if (UNLIKELY(already_quickened_)) {
196 return inst->IsQuickened() ? NextIndex() : index;
197 }
198 DCHECK(!inst->IsQuickened());
199 return index;
200 }
201
ShouldCompileMethod(const MethodReference & ref)202 bool DexToDexCompiler::ShouldCompileMethod(const MethodReference& ref) {
203 // TODO: It's probably safe to avoid the lock here if the active_dex_file_ matches since we only
204 // only call ShouldCompileMethod on one dex at a time.
205 MutexLock lock(Thread::Current(), lock_);
206 return GetOrAddBitVectorForDex(ref.dex_file)->IsBitSet(ref.index);
207 }
208
Compile()209 std::vector<uint8_t> DexToDexCompiler::CompilationState::Compile() {
210 DCHECK_EQ(compilation_level_, CompilationLevel::kOptimize);
211 const CodeItemDataAccessor& instructions = unit_.GetCodeItemAccessor();
212 for (DexInstructionIterator it = instructions.begin(); it != instructions.end(); ++it) {
213 const uint32_t dex_pc = it.DexPc();
214 Instruction* inst = const_cast<Instruction*>(&it.Inst());
215
216 if (!already_quickened_) {
217 DCHECK(!inst->IsQuickened());
218 }
219
220 switch (inst->Opcode()) {
221 case Instruction::RETURN_VOID:
222 CompileReturnVoid(inst, dex_pc);
223 break;
224
225 case Instruction::CHECK_CAST:
226 inst = CompileCheckCast(inst, dex_pc);
227 if (inst->Opcode() == Instruction::NOP) {
228 // We turned the CHECK_CAST into two NOPs, avoid visiting the second NOP twice since this
229 // would add 2 quickening info entries.
230 ++it;
231 }
232 break;
233
234 case Instruction::IGET:
235 case Instruction::IGET_QUICK:
236 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_QUICK, false);
237 break;
238
239 case Instruction::IGET_WIDE:
240 case Instruction::IGET_WIDE_QUICK:
241 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_WIDE_QUICK, false);
242 break;
243
244 case Instruction::IGET_OBJECT:
245 case Instruction::IGET_OBJECT_QUICK:
246 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT_QUICK, false);
247 break;
248
249 case Instruction::IGET_BOOLEAN:
250 case Instruction::IGET_BOOLEAN_QUICK:
251 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BOOLEAN_QUICK, false);
252 break;
253
254 case Instruction::IGET_BYTE:
255 case Instruction::IGET_BYTE_QUICK:
256 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BYTE_QUICK, false);
257 break;
258
259 case Instruction::IGET_CHAR:
260 case Instruction::IGET_CHAR_QUICK:
261 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_CHAR_QUICK, false);
262 break;
263
264 case Instruction::IGET_SHORT:
265 case Instruction::IGET_SHORT_QUICK:
266 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_SHORT_QUICK, false);
267 break;
268
269 case Instruction::IPUT:
270 case Instruction::IPUT_QUICK:
271 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_QUICK, true);
272 break;
273
274 case Instruction::IPUT_BOOLEAN:
275 case Instruction::IPUT_BOOLEAN_QUICK:
276 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BOOLEAN_QUICK, true);
277 break;
278
279 case Instruction::IPUT_BYTE:
280 case Instruction::IPUT_BYTE_QUICK:
281 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BYTE_QUICK, true);
282 break;
283
284 case Instruction::IPUT_CHAR:
285 case Instruction::IPUT_CHAR_QUICK:
286 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_CHAR_QUICK, true);
287 break;
288
289 case Instruction::IPUT_SHORT:
290 case Instruction::IPUT_SHORT_QUICK:
291 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_SHORT_QUICK, true);
292 break;
293
294 case Instruction::IPUT_WIDE:
295 case Instruction::IPUT_WIDE_QUICK:
296 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE_QUICK, true);
297 break;
298
299 case Instruction::IPUT_OBJECT:
300 case Instruction::IPUT_OBJECT_QUICK:
301 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_OBJECT_QUICK, true);
302 break;
303
304 case Instruction::INVOKE_VIRTUAL:
305 case Instruction::INVOKE_VIRTUAL_QUICK:
306 CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_QUICK, false);
307 break;
308
309 case Instruction::INVOKE_VIRTUAL_RANGE:
310 case Instruction::INVOKE_VIRTUAL_RANGE_QUICK:
311 CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_RANGE_QUICK, true);
312 break;
313
314 case Instruction::NOP:
315 if (already_quickened_) {
316 const uint16_t reference_index = NextIndex();
317 quickened_info_.push_back(QuickenedInfo(dex_pc, reference_index));
318 if (reference_index == DexFile::kDexNoIndex16) {
319 // This means it was a normal nop and not a check-cast.
320 break;
321 }
322 const uint16_t type_index = NextIndex();
323 if (driver_.IsSafeCast(&unit_, dex_pc)) {
324 quickened_info_.push_back(QuickenedInfo(dex_pc, type_index));
325 }
326 ++it;
327 } else {
328 // We need to differentiate between check cast inserted NOP and normal NOP, put an invalid
329 // index in the map for normal nops. This should be rare in real code.
330 quickened_info_.push_back(QuickenedInfo(dex_pc, DexFile::kDexNoIndex16));
331 }
332 break;
333
334 default:
335 // Nothing to do.
336 break;
337 }
338 }
339
340 if (already_quickened_) {
341 DCHECK_EQ(quicken_index_, existing_quicken_info_.NumIndices());
342 }
343
344 // Even if there are no indicies, generate an empty quicken info so that we know the method was
345 // quickened.
346
347 std::vector<uint8_t> quicken_data;
348 if (kIsDebugBuild) {
349 // Double check that the counts line up with the size of the quicken info.
350 size_t quicken_count = 0;
351 for (const DexInstructionPcPair& pair : instructions) {
352 if (QuickenInfoTable::NeedsIndexForInstruction(&pair.Inst())) {
353 ++quicken_count;
354 }
355 }
356 CHECK_EQ(quicken_count, GetQuickenedInfo().size());
357 }
358
359 QuickenInfoTable::Builder builder(&quicken_data, GetQuickenedInfo().size());
360 // Length is encoded by the constructor.
361 for (const CompilationState::QuickenedInfo& info : GetQuickenedInfo()) {
362 // Dex pc is not serialized, only used for checking the instructions. Since we access the
363 // array based on the index of the quickened instruction, the indexes must line up perfectly.
364 // The reader side uses the NeedsIndexForInstruction function too.
365 const Instruction& inst = instructions.InstructionAt(info.dex_pc);
366 CHECK(QuickenInfoTable::NeedsIndexForInstruction(&inst)) << inst.Opcode();
367 builder.AddIndex(info.dex_member_index);
368 }
369 DCHECK(!quicken_data.empty());
370 return quicken_data;
371 }
372
CompileReturnVoid(Instruction * inst,uint32_t dex_pc)373 void DexToDexCompiler::CompilationState::CompileReturnVoid(Instruction* inst, uint32_t dex_pc) {
374 DCHECK_EQ(inst->Opcode(), Instruction::RETURN_VOID);
375 if (unit_.IsConstructor()) {
376 // Are we compiling a non clinit constructor which needs a barrier ?
377 if (!unit_.IsStatic() &&
378 driver_.RequiresConstructorBarrier(Thread::Current(), unit_.GetDexFile(),
379 unit_.GetClassDefIndex())) {
380 return;
381 }
382 }
383 // Replace RETURN_VOID by RETURN_VOID_NO_BARRIER.
384 VLOG(compiler) << "Replacing " << Instruction::Name(inst->Opcode())
385 << " by " << Instruction::Name(Instruction::RETURN_VOID_NO_BARRIER)
386 << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
387 << GetDexFile().PrettyMethod(unit_.GetDexMethodIndex(), true);
388 inst->SetOpcode(Instruction::RETURN_VOID_NO_BARRIER);
389 optimized_return_void_ = true;
390 }
391
CompileCheckCast(Instruction * inst,uint32_t dex_pc)392 Instruction* DexToDexCompiler::CompilationState::CompileCheckCast(Instruction* inst,
393 uint32_t dex_pc) {
394 if (!kEnableCheckCastEllision) {
395 return inst;
396 }
397 if (!driver_.IsSafeCast(&unit_, dex_pc)) {
398 return inst;
399 }
400 // Ok, this is a safe cast. Since the "check-cast" instruction size is 2 code
401 // units and a "nop" instruction size is 1 code unit, we need to replace it by
402 // 2 consecutive NOP instructions.
403 // Because the caller loops over instructions by calling Instruction::Next onto
404 // the current instruction, we need to return the 2nd NOP instruction. Indeed,
405 // its next instruction is the former check-cast's next instruction.
406 VLOG(compiler) << "Removing " << Instruction::Name(inst->Opcode())
407 << " by replacing it with 2 NOPs at dex pc "
408 << StringPrintf("0x%x", dex_pc) << " in method "
409 << GetDexFile().PrettyMethod(unit_.GetDexMethodIndex(), true);
410 if (!already_quickened_) {
411 quickened_info_.push_back(QuickenedInfo(dex_pc, inst->VRegA_21c()));
412 quickened_info_.push_back(QuickenedInfo(dex_pc, inst->VRegB_21c()));
413
414 // We are modifying 4 consecutive bytes.
415 inst->SetOpcode(Instruction::NOP);
416 inst->SetVRegA_10x(0u); // keep compliant with verifier.
417 // Get to next instruction which is the second half of check-cast and replace
418 // it by a NOP.
419 inst = const_cast<Instruction*>(inst->Next());
420 inst->SetOpcode(Instruction::NOP);
421 inst->SetVRegA_10x(0u); // keep compliant with verifier.
422 }
423 return inst;
424 }
425
CompileInstanceFieldAccess(Instruction * inst,uint32_t dex_pc,Instruction::Code new_opcode,bool is_put)426 void DexToDexCompiler::CompilationState::CompileInstanceFieldAccess(Instruction* inst,
427 uint32_t dex_pc,
428 Instruction::Code new_opcode,
429 bool is_put) {
430 if (!kEnableQuickening) {
431 return;
432 }
433 uint32_t field_idx = GetIndexForInstruction(inst, inst->VRegC_22c());
434 MemberOffset field_offset(0u);
435 bool is_volatile;
436 bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, is_put,
437 &field_offset, &is_volatile);
438 if (fast_path && !is_volatile && IsUint<16>(field_offset.Int32Value())) {
439 VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode())
440 << " to " << Instruction::Name(new_opcode)
441 << " by replacing field index " << field_idx
442 << " by field offset " << field_offset.Int32Value()
443 << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
444 << GetDexFile().PrettyMethod(unit_.GetDexMethodIndex(), true);
445 if (!already_quickened_) {
446 // We are modifying 4 consecutive bytes.
447 inst->SetOpcode(new_opcode);
448 // Replace field index by field offset.
449 inst->SetVRegC_22c(static_cast<uint16_t>(field_offset.Int32Value()));
450 }
451 quickened_info_.push_back(QuickenedInfo(dex_pc, field_idx));
452 }
453 }
454
GetDexFile() const455 const DexFile& DexToDexCompiler::CompilationState::GetDexFile() const {
456 return *unit_.GetDexFile();
457 }
458
CompileInvokeVirtual(Instruction * inst,uint32_t dex_pc,Instruction::Code new_opcode,bool is_range)459 void DexToDexCompiler::CompilationState::CompileInvokeVirtual(Instruction* inst,
460 uint32_t dex_pc,
461 Instruction::Code new_opcode,
462 bool is_range) {
463 if (!kEnableQuickening) {
464 return;
465 }
466 uint32_t method_idx = GetIndexForInstruction(inst,
467 is_range ? inst->VRegB_3rc() : inst->VRegB_35c());
468 ScopedObjectAccess soa(Thread::Current());
469
470 ClassLinker* class_linker = unit_.GetClassLinker();
471 ArtMethod* resolved_method =
472 class_linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
473 method_idx,
474 unit_.GetDexCache(),
475 unit_.GetClassLoader(),
476 /* referrer */ nullptr,
477 kVirtual);
478
479 if (UNLIKELY(resolved_method == nullptr)) {
480 // Clean up any exception left by type resolution.
481 soa.Self()->ClearException();
482 return;
483 }
484
485 uint32_t vtable_idx = resolved_method->GetMethodIndex();
486 DCHECK(IsUint<16>(vtable_idx));
487 VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode())
488 << "(" << GetDexFile().PrettyMethod(method_idx, true) << ")"
489 << " to " << Instruction::Name(new_opcode)
490 << " by replacing method index " << method_idx
491 << " by vtable index " << vtable_idx
492 << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
493 << GetDexFile().PrettyMethod(unit_.GetDexMethodIndex(), true);
494 if (!already_quickened_) {
495 // We are modifying 4 consecutive bytes.
496 inst->SetOpcode(new_opcode);
497 // Replace method index by vtable index.
498 if (is_range) {
499 inst->SetVRegB_3rc(static_cast<uint16_t>(vtable_idx));
500 } else {
501 inst->SetVRegB_35c(static_cast<uint16_t>(vtable_idx));
502 }
503 }
504 quickened_info_.push_back(QuickenedInfo(dex_pc, method_idx));
505 }
506
CompileMethod(const DexFile::CodeItem * code_item,uint32_t access_flags,InvokeType invoke_type ATTRIBUTE_UNUSED,uint16_t class_def_idx,uint32_t method_idx,Handle<mirror::ClassLoader> class_loader,const DexFile & dex_file,CompilationLevel compilation_level)507 CompiledMethod* DexToDexCompiler::CompileMethod(
508 const DexFile::CodeItem* code_item,
509 uint32_t access_flags,
510 InvokeType invoke_type ATTRIBUTE_UNUSED,
511 uint16_t class_def_idx,
512 uint32_t method_idx,
513 Handle<mirror::ClassLoader> class_loader,
514 const DexFile& dex_file,
515 CompilationLevel compilation_level) {
516 if (compilation_level == CompilationLevel::kDontDexToDexCompile) {
517 return nullptr;
518 }
519
520 ScopedObjectAccess soa(Thread::Current());
521 StackHandleScope<1> hs(soa.Self());
522 ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
523 art::DexCompilationUnit unit(
524 class_loader,
525 class_linker,
526 dex_file,
527 code_item,
528 class_def_idx,
529 method_idx,
530 access_flags,
531 driver_->GetVerifiedMethod(&dex_file, method_idx),
532 hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)));
533
534 std::vector<uint8_t> quicken_data;
535 // If the code item is shared with multiple different method ids, make sure that we quicken only
536 // once and verify that all the dequicken maps match.
537 if (UNLIKELY(shared_code_items_.find(code_item) != shared_code_items_.end())) {
538 // Avoid quickening the shared code items for now because the existing conflict detection logic
539 // does not currently handle cases where the code item is quickened in one place but
540 // compiled in another.
541 static constexpr bool kAvoidQuickeningSharedCodeItems = true;
542 if (kAvoidQuickeningSharedCodeItems) {
543 return nullptr;
544 }
545 // For shared code items, use a lock to prevent races.
546 MutexLock mu(soa.Self(), lock_);
547 auto existing = shared_code_item_quicken_info_.find(code_item);
548 QuickenState* existing_data = nullptr;
549 std::vector<uint8_t>* existing_quicken_data = nullptr;
550 if (existing != shared_code_item_quicken_info_.end()) {
551 existing_data = &existing->second;
552 if (existing_data->conflict_) {
553 return nullptr;
554 }
555 existing_quicken_data = &existing_data->quicken_data_;
556 }
557 bool optimized_return_void;
558 {
559 CompilationState state(this, unit, compilation_level, existing_quicken_data);
560 quicken_data = state.Compile();
561 optimized_return_void = state.optimized_return_void_;
562 }
563
564 // Already quickened, check that the data matches what was previously seen.
565 MethodReference method_ref(&dex_file, method_idx);
566 if (existing_data != nullptr) {
567 if (*existing_quicken_data != quicken_data ||
568 existing_data->optimized_return_void_ != optimized_return_void) {
569 VLOG(compiler) << "Quicken data mismatch, for method "
570 << dex_file.PrettyMethod(method_idx);
571 // Mark the method as a conflict to never attempt to quicken it in the future.
572 existing_data->conflict_ = true;
573 }
574 existing_data->methods_.push_back(method_ref);
575 } else {
576 QuickenState new_state;
577 new_state.methods_.push_back(method_ref);
578 new_state.quicken_data_ = quicken_data;
579 new_state.optimized_return_void_ = optimized_return_void;
580 bool inserted = shared_code_item_quicken_info_.emplace(code_item, new_state).second;
581 CHECK(inserted) << "Failed to insert " << dex_file.PrettyMethod(method_idx);
582 }
583
584 // Easy sanity check is to check that the existing stuff matches by re-quickening using the
585 // newly produced quicken data.
586 // Note that this needs to be behind the lock for this case since we may unquicken in another
587 // thread.
588 if (kIsDebugBuild) {
589 CompilationState state2(this, unit, compilation_level, &quicken_data);
590 std::vector<uint8_t> new_data = state2.Compile();
591 CHECK(new_data == quicken_data) << "Mismatch producing new quicken data";
592 }
593 } else {
594 CompilationState state(this, unit, compilation_level, /*quicken_data*/ nullptr);
595 quicken_data = state.Compile();
596
597 // Easy sanity check is to check that the existing stuff matches by re-quickening using the
598 // newly produced quicken data.
599 if (kIsDebugBuild) {
600 CompilationState state2(this, unit, compilation_level, &quicken_data);
601 std::vector<uint8_t> new_data = state2.Compile();
602 CHECK(new_data == quicken_data) << "Mismatch producing new quicken data";
603 }
604 }
605
606 if (quicken_data.empty()) {
607 return nullptr;
608 }
609
610 // Create a `CompiledMethod`, with the quickened information in the vmap table.
611 InstructionSet instruction_set = driver_->GetInstructionSet();
612 if (instruction_set == InstructionSet::kThumb2) {
613 // Don't use the thumb2 instruction set to avoid the one off code delta.
614 instruction_set = InstructionSet::kArm;
615 }
616 CompiledMethod* ret = CompiledMethod::SwapAllocCompiledMethod(
617 driver_,
618 instruction_set,
619 ArrayRef<const uint8_t>(), // no code
620 0,
621 0,
622 0,
623 ArrayRef<const uint8_t>(), // method_info
624 ArrayRef<const uint8_t>(quicken_data), // vmap_table
625 ArrayRef<const uint8_t>(), // cfi data
626 ArrayRef<const linker::LinkerPatch>());
627 DCHECK(ret != nullptr);
628 return ret;
629 }
630
SetDexFiles(const std::vector<const DexFile * > & dex_files)631 void DexToDexCompiler::SetDexFiles(const std::vector<const DexFile*>& dex_files) {
632 // Record what code items are already seen to detect when multiple methods have the same code
633 // item.
634 std::unordered_set<const DexFile::CodeItem*> seen_code_items;
635 for (const DexFile* dex_file : dex_files) {
636 for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) {
637 const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
638 const uint8_t* class_data = dex_file->GetClassData(class_def);
639 if (class_data == nullptr) {
640 continue;
641 }
642 ClassDataItemIterator it(*dex_file, class_data);
643 it.SkipAllFields();
644 for (; it.HasNextMethod(); it.Next()) {
645 const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
646 // Detect the shared code items.
647 if (!seen_code_items.insert(code_item).second) {
648 shared_code_items_.insert(code_item);
649 }
650 }
651 }
652 }
653 VLOG(compiler) << "Shared code items " << shared_code_items_.size();
654 }
655
UnquickenConflictingMethods()656 void DexToDexCompiler::UnquickenConflictingMethods() {
657 MutexLock mu(Thread::Current(), lock_);
658 size_t unquicken_count = 0;
659 for (const auto& pair : shared_code_item_quicken_info_) {
660 const DexFile::CodeItem* code_item = pair.first;
661 const QuickenState& state = pair.second;
662 CHECK_GE(state.methods_.size(), 1u);
663 if (state.conflict_) {
664 // Unquicken using the existing quicken data.
665 // TODO: Do we really need to pass a dex file in?
666 optimizer::ArtDecompileDEX(*state.methods_[0].dex_file,
667 *code_item,
668 ArrayRef<const uint8_t>(state.quicken_data_),
669 /* decompile_return_instruction*/ true);
670 ++unquicken_count;
671 // Go clear the vmaps for all the methods that were already quickened to avoid writing them
672 // out during oat writing.
673 for (const MethodReference& ref : state.methods_) {
674 CompiledMethod* method = driver_->RemoveCompiledMethod(ref);
675 if (method != nullptr) {
676 // There is up to one compiled method for each method ref. Releasing it leaves the
677 // deduped data intact, this means its safe to do even when other threads might be
678 // compiling.
679 CompiledMethod::ReleaseSwapAllocatedCompiledMethod(driver_, method);
680 }
681 }
682 }
683 }
684 }
685
686 } // namespace optimizer
687
688 } // namespace art
689