• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/class_accessor-inl.h"
30 #include "dex/dex_file-inl.h"
31 #include "dex/dex_instruction-inl.h"
32 #include "dex_to_dex_decompiler.h"
33 #include "driver/compiler_driver.h"
34 #include "driver/compiler_options.h"
35 #include "driver/dex_compilation_unit.h"
36 #include "mirror/dex_cache.h"
37 #include "quicken_info.h"
38 #include "thread-current-inl.h"
39 
40 namespace art {
41 namespace optimizer {
42 
43 using android::base::StringPrintf;
44 
45 // Controls quickening activation.
46 const bool kEnableQuickening = true;
47 // Control check-cast elision.
48 const bool kEnableCheckCastEllision = true;
49 
50 // Holds the state for compiling a single method.
51 struct DexToDexCompiler::CompilationState {
52   struct QuickenedInfo {
QuickenedInfoart::optimizer::DexToDexCompiler::CompilationState::QuickenedInfo53     QuickenedInfo(uint32_t pc, uint16_t index) : dex_pc(pc), dex_member_index(index) {}
54 
55     uint32_t dex_pc;
56     uint16_t dex_member_index;
57   };
58 
59   CompilationState(DexToDexCompiler* compiler,
60                    const DexCompilationUnit& unit,
61                    const CompilationLevel compilation_level,
62                    const std::vector<uint8_t>* quicken_data);
63 
GetQuickenedInfoart::optimizer::DexToDexCompiler::CompilationState64   const std::vector<QuickenedInfo>& GetQuickenedInfo() const {
65     return quickened_info_;
66   }
67 
68   // Returns the quickening info, or an empty array if it was not quickened.
69   // If already_quickened is true, then don't change anything but still return what the quicken
70   // data would have been.
71   std::vector<uint8_t> Compile();
72 
73   const DexFile& GetDexFile() const;
74 
75   // Compiles a RETURN-VOID into a RETURN-VOID-BARRIER within a constructor where
76   // a barrier is required.
77   void CompileReturnVoid(Instruction* inst, uint32_t dex_pc);
78 
79   // Compiles a CHECK-CAST into 2 NOP instructions if it is known to be safe. In
80   // this case, returns the second NOP instruction pointer. Otherwise, returns
81   // the given "inst".
82   Instruction* CompileCheckCast(Instruction* inst, uint32_t dex_pc);
83 
84   // Compiles a field access into a quick field access.
85   // The field index is replaced by an offset within an Object where we can read
86   // from / write to this field. Therefore, this does not involve any resolution
87   // at runtime.
88   // Since the field index is encoded with 16 bits, we can replace it only if the
89   // field offset can be encoded with 16 bits too.
90   void CompileInstanceFieldAccess(Instruction* inst, uint32_t dex_pc,
91                                   Instruction::Code new_opcode, bool is_put);
92 
93   // Compiles a virtual method invocation into a quick virtual method invocation.
94   // The method index is replaced by the vtable index where the corresponding
95   // executable can be found. Therefore, this does not involve any resolution
96   // at runtime.
97   // Since the method index is encoded with 16 bits, we can replace it only if the
98   // vtable index can be encoded with 16 bits too.
99   void CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc,
100                             Instruction::Code new_opcode, bool is_range);
101 
102   // Return the next index.
103   uint16_t NextIndex();
104 
105   // Returns the dequickened index if an instruction is quickened, otherwise return index.
106   uint16_t GetIndexForInstruction(const Instruction* inst, uint32_t index);
107 
108   DexToDexCompiler* const compiler_;
109   CompilerDriver& driver_;
110   const DexCompilationUnit& unit_;
111   const CompilationLevel compilation_level_;
112 
113   // Filled by the compiler when quickening, in order to encode that information
114   // in the .oat file. The runtime will use that information to get to the original
115   // opcodes.
116   std::vector<QuickenedInfo> quickened_info_;
117 
118   // True if we optimized a return void to a return void no barrier.
119   bool optimized_return_void_ = false;
120 
121   // If the code item was already quickened previously.
122   const bool already_quickened_;
123   const QuickenInfoTable existing_quicken_info_;
124   uint32_t quicken_index_ = 0u;
125 
126   DISALLOW_COPY_AND_ASSIGN(CompilationState);
127 };
128 
DexToDexCompiler(CompilerDriver * driver)129 DexToDexCompiler::DexToDexCompiler(CompilerDriver* driver)
130     : driver_(driver),
131       lock_("Quicken lock", kDexToDexCompilerLock) {
132   DCHECK(driver != nullptr);
133 }
134 
ClearState()135 void DexToDexCompiler::ClearState() {
136   MutexLock lock(Thread::Current(), lock_);
137   active_dex_file_ = nullptr;
138   active_bit_vector_ = nullptr;
139   should_quicken_.clear();
140   shared_code_item_quicken_info_.clear();
141 }
142 
NumCodeItemsToQuicken(Thread * self) const143 size_t DexToDexCompiler::NumCodeItemsToQuicken(Thread* self) const {
144   MutexLock lock(self, lock_);
145   return num_code_items_;
146 }
147 
GetOrAddBitVectorForDex(const DexFile * dex_file)148 BitVector* DexToDexCompiler::GetOrAddBitVectorForDex(const DexFile* dex_file) {
149   if (active_dex_file_ != dex_file) {
150     active_dex_file_ = dex_file;
151     auto inserted = should_quicken_.emplace(dex_file,
152                                             BitVector(dex_file->NumMethodIds(),
153                                                       /*expandable*/ false,
154                                                       Allocator::GetMallocAllocator()));
155     active_bit_vector_ = &inserted.first->second;
156   }
157   return active_bit_vector_;
158 }
159 
MarkForCompilation(Thread * self,const MethodReference & method_ref)160 void DexToDexCompiler::MarkForCompilation(Thread* self,
161                                           const MethodReference& method_ref) {
162   MutexLock lock(self, lock_);
163   BitVector* const bitmap = GetOrAddBitVectorForDex(method_ref.dex_file);
164   DCHECK(bitmap != nullptr);
165   DCHECK(!bitmap->IsBitSet(method_ref.index));
166   bitmap->SetBit(method_ref.index);
167   ++num_code_items_;
168 }
169 
CompilationState(DexToDexCompiler * compiler,const DexCompilationUnit & unit,const CompilationLevel compilation_level,const std::vector<uint8_t> * quicken_data)170 DexToDexCompiler::CompilationState::CompilationState(DexToDexCompiler* compiler,
171                                                      const DexCompilationUnit& unit,
172                                                      const CompilationLevel compilation_level,
173                                                      const std::vector<uint8_t>* quicken_data)
174     : compiler_(compiler),
175       driver_(*compiler->GetDriver()),
176       unit_(unit),
177       compilation_level_(compilation_level),
178       already_quickened_(quicken_data != nullptr),
179       existing_quicken_info_(already_quickened_
180           ? ArrayRef<const uint8_t>(*quicken_data) : ArrayRef<const uint8_t>()) {}
181 
NextIndex()182 uint16_t DexToDexCompiler::CompilationState::NextIndex() {
183   DCHECK(already_quickened_);
184   if (kIsDebugBuild && quicken_index_ >= existing_quicken_info_.NumIndices()) {
185     for (const DexInstructionPcPair& pair : unit_.GetCodeItemAccessor()) {
186       LOG(ERROR) << pair->DumpString(nullptr);
187     }
188     LOG(FATAL) << "Mismatched number of quicken slots.";
189   }
190   const uint16_t ret = existing_quicken_info_.GetData(quicken_index_);
191   quicken_index_++;
192   return ret;
193 }
194 
GetIndexForInstruction(const Instruction * inst,uint32_t index)195 uint16_t DexToDexCompiler::CompilationState::GetIndexForInstruction(const Instruction* inst,
196                                                                     uint32_t index) {
197   if (UNLIKELY(already_quickened_)) {
198     return inst->IsQuickened() ? NextIndex() : index;
199   }
200   DCHECK(!inst->IsQuickened());
201   return index;
202 }
203 
ShouldCompileMethod(const MethodReference & ref)204 bool DexToDexCompiler::ShouldCompileMethod(const MethodReference& ref) {
205   // TODO: It's probably safe to avoid the lock here if the active_dex_file_ matches since we only
206   // only call ShouldCompileMethod on one dex at a time.
207   MutexLock lock(Thread::Current(), lock_);
208   return GetOrAddBitVectorForDex(ref.dex_file)->IsBitSet(ref.index);
209 }
210 
Compile()211 std::vector<uint8_t> DexToDexCompiler::CompilationState::Compile() {
212   DCHECK_EQ(compilation_level_, CompilationLevel::kOptimize);
213   const CodeItemDataAccessor& instructions = unit_.GetCodeItemAccessor();
214   for (DexInstructionIterator it = instructions.begin(); it != instructions.end(); ++it) {
215     const uint32_t dex_pc = it.DexPc();
216     Instruction* inst = const_cast<Instruction*>(&it.Inst());
217 
218     if (!already_quickened_) {
219       DCHECK(!inst->IsQuickened());
220     }
221 
222     switch (inst->Opcode()) {
223       case Instruction::RETURN_VOID:
224         CompileReturnVoid(inst, dex_pc);
225         break;
226 
227       case Instruction::CHECK_CAST:
228         inst = CompileCheckCast(inst, dex_pc);
229         if (inst->Opcode() == Instruction::NOP) {
230           // We turned the CHECK_CAST into two NOPs, avoid visiting the second NOP twice since this
231           // would add 2 quickening info entries.
232           ++it;
233         }
234         break;
235 
236       case Instruction::IGET:
237       case Instruction::IGET_QUICK:
238         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_QUICK, false);
239         break;
240 
241       case Instruction::IGET_WIDE:
242       case Instruction::IGET_WIDE_QUICK:
243         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_WIDE_QUICK, false);
244         break;
245 
246       case Instruction::IGET_OBJECT:
247       case Instruction::IGET_OBJECT_QUICK:
248         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT_QUICK, false);
249         break;
250 
251       case Instruction::IGET_BOOLEAN:
252       case Instruction::IGET_BOOLEAN_QUICK:
253         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BOOLEAN_QUICK, false);
254         break;
255 
256       case Instruction::IGET_BYTE:
257       case Instruction::IGET_BYTE_QUICK:
258         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BYTE_QUICK, false);
259         break;
260 
261       case Instruction::IGET_CHAR:
262       case Instruction::IGET_CHAR_QUICK:
263         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_CHAR_QUICK, false);
264         break;
265 
266       case Instruction::IGET_SHORT:
267       case Instruction::IGET_SHORT_QUICK:
268         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_SHORT_QUICK, false);
269         break;
270 
271       case Instruction::IPUT:
272       case Instruction::IPUT_QUICK:
273         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_QUICK, true);
274         break;
275 
276       case Instruction::IPUT_BOOLEAN:
277       case Instruction::IPUT_BOOLEAN_QUICK:
278         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BOOLEAN_QUICK, true);
279         break;
280 
281       case Instruction::IPUT_BYTE:
282       case Instruction::IPUT_BYTE_QUICK:
283         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BYTE_QUICK, true);
284         break;
285 
286       case Instruction::IPUT_CHAR:
287       case Instruction::IPUT_CHAR_QUICK:
288         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_CHAR_QUICK, true);
289         break;
290 
291       case Instruction::IPUT_SHORT:
292       case Instruction::IPUT_SHORT_QUICK:
293         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_SHORT_QUICK, true);
294         break;
295 
296       case Instruction::IPUT_WIDE:
297       case Instruction::IPUT_WIDE_QUICK:
298         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE_QUICK, true);
299         break;
300 
301       case Instruction::IPUT_OBJECT:
302       case Instruction::IPUT_OBJECT_QUICK:
303         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_OBJECT_QUICK, true);
304         break;
305 
306       case Instruction::INVOKE_VIRTUAL:
307       case Instruction::INVOKE_VIRTUAL_QUICK:
308         CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_QUICK, false);
309         break;
310 
311       case Instruction::INVOKE_VIRTUAL_RANGE:
312       case Instruction::INVOKE_VIRTUAL_RANGE_QUICK:
313         CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_RANGE_QUICK, true);
314         break;
315 
316       case Instruction::NOP:
317         if (already_quickened_) {
318           const uint16_t reference_index = NextIndex();
319           quickened_info_.push_back(QuickenedInfo(dex_pc, reference_index));
320           if (reference_index == DexFile::kDexNoIndex16) {
321             // This means it was a normal nop and not a check-cast.
322             break;
323           }
324           const uint16_t type_index = NextIndex();
325           if (driver_.IsSafeCast(&unit_, dex_pc)) {
326             quickened_info_.push_back(QuickenedInfo(dex_pc, type_index));
327           }
328           ++it;
329         } else {
330           // We need to differentiate between check cast inserted NOP and normal NOP, put an invalid
331           // index in the map for normal nops. This should be rare in real code.
332           quickened_info_.push_back(QuickenedInfo(dex_pc, DexFile::kDexNoIndex16));
333         }
334         break;
335 
336       default:
337         // Nothing to do.
338         break;
339     }
340   }
341 
342   if (already_quickened_) {
343     DCHECK_EQ(quicken_index_, existing_quicken_info_.NumIndices());
344   }
345 
346   // Even if there are no indices, generate an empty quicken info so that we know the method was
347   // quickened.
348 
349   std::vector<uint8_t> quicken_data;
350   if (kIsDebugBuild) {
351     // Double check that the counts line up with the size of the quicken info.
352     size_t quicken_count = 0;
353     for (const DexInstructionPcPair& pair : instructions) {
354       if (QuickenInfoTable::NeedsIndexForInstruction(&pair.Inst())) {
355         ++quicken_count;
356       }
357     }
358     CHECK_EQ(quicken_count, GetQuickenedInfo().size());
359   }
360 
361   QuickenInfoTable::Builder builder(&quicken_data, GetQuickenedInfo().size());
362   // Length is encoded by the constructor.
363   for (const CompilationState::QuickenedInfo& info : GetQuickenedInfo()) {
364     // Dex pc is not serialized, only used for checking the instructions. Since we access the
365     // array based on the index of the quickened instruction, the indexes must line up perfectly.
366     // The reader side uses the NeedsIndexForInstruction function too.
367     const Instruction& inst = instructions.InstructionAt(info.dex_pc);
368     CHECK(QuickenInfoTable::NeedsIndexForInstruction(&inst)) << inst.Opcode();
369     builder.AddIndex(info.dex_member_index);
370   }
371   DCHECK(!quicken_data.empty());
372   return quicken_data;
373 }
374 
CompileReturnVoid(Instruction * inst,uint32_t dex_pc)375 void DexToDexCompiler::CompilationState::CompileReturnVoid(Instruction* inst, uint32_t dex_pc) {
376   DCHECK_EQ(inst->Opcode(), Instruction::RETURN_VOID);
377   if (unit_.IsConstructor()) {
378     // Are we compiling a non clinit constructor which needs a barrier ?
379     if (!unit_.IsStatic() && unit_.RequiresConstructorBarrier()) {
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 dex::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 dex::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_->GetCompilerOptions().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_->GetCompilerOptions().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_->GetCompiledMethodStorage(),
618       instruction_set,
619       ArrayRef<const uint8_t>(),                   // no code
620       ArrayRef<const uint8_t>(quicken_data),       // vmap_table
621       ArrayRef<const uint8_t>(),                   // cfi data
622       ArrayRef<const linker::LinkerPatch>());
623   DCHECK(ret != nullptr);
624   return ret;
625 }
626 
SetDexFiles(const std::vector<const DexFile * > & dex_files)627 void DexToDexCompiler::SetDexFiles(const std::vector<const DexFile*>& dex_files) {
628   // Record what code items are already seen to detect when multiple methods have the same code
629   // item.
630   std::unordered_set<const dex::CodeItem*> seen_code_items;
631   for (const DexFile* dex_file : dex_files) {
632     for (ClassAccessor accessor : dex_file->GetClasses()) {
633       for (const ClassAccessor::Method& method : accessor.GetMethods()) {
634         const dex::CodeItem* code_item = method.GetCodeItem();
635         // Detect the shared code items.
636         if (!seen_code_items.insert(code_item).second) {
637           shared_code_items_.insert(code_item);
638         }
639       }
640     }
641   }
642   VLOG(compiler) << "Shared code items " << shared_code_items_.size();
643 }
644 
UnquickenConflictingMethods()645 void DexToDexCompiler::UnquickenConflictingMethods() {
646   MutexLock mu(Thread::Current(), lock_);
647   size_t unquicken_count = 0;
648   for (const auto& pair : shared_code_item_quicken_info_) {
649     const dex::CodeItem* code_item = pair.first;
650     const QuickenState& state = pair.second;
651     CHECK_GE(state.methods_.size(), 1u);
652     if (state.conflict_) {
653       // Unquicken using the existing quicken data.
654       // TODO: Do we really need to pass a dex file in?
655       optimizer::ArtDecompileDEX(*state.methods_[0].dex_file,
656                                  *code_item,
657                                  ArrayRef<const uint8_t>(state.quicken_data_),
658                                  /* decompile_return_instruction*/ true);
659       ++unquicken_count;
660       // Go clear the vmaps for all the methods that were already quickened to avoid writing them
661       // out during oat writing.
662       for (const MethodReference& ref : state.methods_) {
663         CompiledMethod* method = driver_->RemoveCompiledMethod(ref);
664         if (method != nullptr) {
665           // There is up to one compiled method for each method ref. Releasing it leaves the
666           // deduped data intact, this means its safe to do even when other threads might be
667           // compiling.
668           CompiledMethod::ReleaseSwapAllocatedCompiledMethod(driver_->GetCompiledMethodStorage(),
669                                                              method);
670         }
671       }
672     }
673   }
674 }
675 
676 }  // namespace optimizer
677 
678 }  // namespace art
679