blob: a0e2c1e9aad7e31344543aafd1dbb5e7eb63929c [file] [log] [blame]
Brian Carlstrom7940e442013-07-12 13:46:57 -07001/*
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 "base/logging.h"
18#include "base/mutex.h"
19#include "dex_file-inl.h"
20#include "dex_instruction-inl.h"
21#include "driver/compiler_driver.h"
22#include "driver/dex_compilation_unit.h"
Brian Carlstromea46f952013-07-30 01:26:50 -070023#include "mirror/art_field-inl.h"
24#include "mirror/art_method-inl.h"
Brian Carlstrom7940e442013-07-12 13:46:57 -070025#include "mirror/class-inl.h"
26#include "mirror/dex_cache.h"
Brian Carlstrom7940e442013-07-12 13:46:57 -070027
28namespace art {
29namespace optimizer {
30
31// Controls quickening activation.
32const bool kEnableQuickening = true;
Sebastien Hertz543959c2013-07-03 12:00:19 +020033// Control check-cast elision.
34const bool kEnableCheckCastEllision = true;
Brian Carlstrom7940e442013-07-12 13:46:57 -070035
36class DexCompiler {
37 public:
38 DexCompiler(art::CompilerDriver& compiler,
Sebastien Hertz75021222013-07-16 18:34:50 +020039 const DexCompilationUnit& unit,
40 DexToDexCompilationLevel dex_to_dex_compilation_level)
Brian Carlstrom7940e442013-07-12 13:46:57 -070041 : driver_(compiler),
Sebastien Hertz75021222013-07-16 18:34:50 +020042 unit_(unit),
43 dex_to_dex_compilation_level_(dex_to_dex_compilation_level) {}
Brian Carlstrom7940e442013-07-12 13:46:57 -070044
Brian Carlstrom9b7085a2013-07-18 15:15:21 -070045 ~DexCompiler() {}
Brian Carlstrom7940e442013-07-12 13:46:57 -070046
47 void Compile();
48
49 private:
50 const DexFile& GetDexFile() const {
51 return *unit_.GetDexFile();
52 }
53
54 // TODO: since the whole compilation pipeline uses a "const DexFile", we need
55 // to "unconst" here. The DEX-to-DEX compiler should work on a non-const DexFile.
56 DexFile& GetModifiableDexFile() {
57 return *const_cast<DexFile*>(unit_.GetDexFile());
58 }
59
Sebastien Hertz75021222013-07-16 18:34:50 +020060 bool PerformOptimizations() const {
61 return dex_to_dex_compilation_level_ >= kOptimize;
62 }
63
Brian Carlstrom7940e442013-07-12 13:46:57 -070064 // Compiles a RETURN-VOID into a RETURN-VOID-BARRIER within a constructor where
65 // a barrier is required.
66 void CompileReturnVoid(Instruction* inst, uint32_t dex_pc);
67
Sebastien Hertz543959c2013-07-03 12:00:19 +020068 // Compiles a CHECK-CAST into 2 NOP instructions if it is known to be safe. In
69 // this case, returns the second NOP instruction pointer. Otherwise, returns
70 // the given "inst".
71 Instruction* CompileCheckCast(Instruction* inst, uint32_t dex_pc);
72
Brian Carlstrom7940e442013-07-12 13:46:57 -070073 // Compiles a field access into a quick field access.
74 // The field index is replaced by an offset within an Object where we can read
75 // from / write to this field. Therefore, this does not involve any resolution
76 // at runtime.
77 // Since the field index is encoded with 16 bits, we can replace it only if the
78 // field offset can be encoded with 16 bits too.
79 void CompileInstanceFieldAccess(Instruction* inst, uint32_t dex_pc,
80 Instruction::Code new_opcode, bool is_put);
81
82 // Compiles a virtual method invocation into a quick virtual method invocation.
83 // The method index is replaced by the vtable index where the corresponding
84 // AbstractMethod can be found. Therefore, this does not involve any resolution
85 // at runtime.
86 // Since the method index is encoded with 16 bits, we can replace it only if the
87 // vtable index can be encoded with 16 bits too.
88 void CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc,
89 Instruction::Code new_opcode, bool is_range);
90
91 CompilerDriver& driver_;
92 const DexCompilationUnit& unit_;
Sebastien Hertz75021222013-07-16 18:34:50 +020093 const DexToDexCompilationLevel dex_to_dex_compilation_level_;
Brian Carlstrom7940e442013-07-12 13:46:57 -070094
95 DISALLOW_COPY_AND_ASSIGN(DexCompiler);
96};
97
98// Ensures write access to a part of DEX file.
99//
100// If a DEX file is read-only, it modifies its protection (mprotect) so it allows
101// write access to the part of DEX file defined by an address and a length.
102// In this case, it also takes the DexFile::modification_lock to prevent from
103// concurrent protection modification from a parallel DEX-to-DEX compilation on
104// the same DEX file.
105// When the instance is destroyed, it recovers original protection and releases
106// the lock.
107// TODO: as this scoped class is similar to a MutexLock we should use annotalysis
108// to capture the locking behavior.
109class ScopedDexWriteAccess {
110 public:
111 ScopedDexWriteAccess(DexFile& dex_file, Instruction* inst,
112 size_t length)
113 : dex_file_(dex_file),
114 address_(reinterpret_cast<uint8_t*>(inst)),
115 length_(length),
116 is_read_only_(dex_file_.IsReadOnly()) {
117 if (is_read_only_) {
118 // We need to enable DEX write access. To avoid concurrent DEX write access
119 // modification, we take the DexFile::modification_lock before.
120 dex_file_.GetModificationLock().ExclusiveLock(Thread::Current());
121 bool success = dex_file_.EnableWrite(address_, length_);
122 DCHECK(success) << "Failed to enable DEX write access";
123 }
124 }
125
126 ~ScopedDexWriteAccess() {
127 DCHECK_EQ(is_read_only_, dex_file_.IsReadOnly());
128 if (is_read_only_) {
129 bool success = dex_file_.DisableWrite(address_, length_);
130 DCHECK(success) << "Failed to disable DEX write access";
131 // Now we recovered original read-only protection, we can release the
132 // DexFile::modification_lock.
133 dex_file_.GetModificationLock().ExclusiveUnlock(Thread::Current());
134 }
135 }
136
137 private:
138 DexFile& dex_file_;
139 // TODO: make address_ const.
140 uint8_t* address_;
141 const size_t length_;
142 const bool is_read_only_;
143
144 DISALLOW_COPY_AND_ASSIGN(ScopedDexWriteAccess);
145};
146
147void DexCompiler::Compile() {
Sebastien Hertz75021222013-07-16 18:34:50 +0200148 DCHECK_GE(dex_to_dex_compilation_level_, kRequired);
Brian Carlstrom7940e442013-07-12 13:46:57 -0700149 const DexFile::CodeItem* code_item = unit_.GetCodeItem();
150 const uint16_t* insns = code_item->insns_;
151 const uint32_t insns_size = code_item->insns_size_in_code_units_;
152 Instruction* inst = const_cast<Instruction*>(Instruction::At(insns));
153
154 for (uint32_t dex_pc = 0; dex_pc < insns_size;
155 inst = const_cast<Instruction*>(inst->Next()), dex_pc = inst->GetDexPc(insns)) {
156 switch (inst->Opcode()) {
157 case Instruction::RETURN_VOID:
158 CompileReturnVoid(inst, dex_pc);
159 break;
160
Sebastien Hertz543959c2013-07-03 12:00:19 +0200161 case Instruction::CHECK_CAST:
162 inst = CompileCheckCast(inst, dex_pc);
163 break;
164
Brian Carlstrom7940e442013-07-12 13:46:57 -0700165 case Instruction::IGET:
166 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_QUICK, false);
167 break;
168
169 case Instruction::IGET_WIDE:
170 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_WIDE_QUICK, false);
171 break;
172
173 case Instruction::IGET_OBJECT:
174 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT_QUICK, false);
175 break;
176
177 case Instruction::IPUT:
178 case Instruction::IPUT_BOOLEAN:
179 case Instruction::IPUT_BYTE:
180 case Instruction::IPUT_CHAR:
181 case Instruction::IPUT_SHORT:
182 // These opcodes have the same implementation in interpreter so group
183 // them under IPUT_QUICK.
184 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_QUICK, true);
185 break;
186
187 case Instruction::IPUT_WIDE:
188 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE_QUICK, true);
189 break;
190
191 case Instruction::IPUT_OBJECT:
192 CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_OBJECT_QUICK, true);
193 break;
194
195 case Instruction::INVOKE_VIRTUAL:
196 CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_QUICK, false);
197 break;
198
199 case Instruction::INVOKE_VIRTUAL_RANGE:
200 CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_RANGE_QUICK, true);
201 break;
202
203 default:
204 // Nothing to do.
205 break;
206 }
207 }
208}
209
210void DexCompiler::CompileReturnVoid(Instruction* inst, uint32_t dex_pc) {
211 DCHECK(inst->Opcode() == Instruction::RETURN_VOID);
Ian Rogers9fc16eb2013-07-31 14:49:16 -0700212 // Are we compiling a non-clinit constructor?
213 if (!unit_.IsConstructor() || unit_.IsStatic()) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700214 return;
215 }
216 // Do we need a constructor barrier ?
217 if (!driver_.RequiresConstructorBarrier(Thread::Current(), unit_.GetDexFile(),
218 unit_.GetClassDefIndex())) {
219 return;
220 }
221 // Replace RETURN_VOID by RETURN_VOID_BARRIER.
Sebastien Hertz543959c2013-07-03 12:00:19 +0200222 VLOG(compiler) << "Replacing " << Instruction::Name(inst->Opcode())
223 << " by " << Instruction::Name(Instruction::RETURN_VOID_BARRIER)
224 << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
225 << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
Brian Carlstrom7940e442013-07-12 13:46:57 -0700226 ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 2u);
227 inst->SetOpcode(Instruction::RETURN_VOID_BARRIER);
228}
229
Sebastien Hertz543959c2013-07-03 12:00:19 +0200230Instruction* DexCompiler::CompileCheckCast(Instruction* inst, uint32_t dex_pc) {
Sebastien Hertz75021222013-07-16 18:34:50 +0200231 if (!kEnableCheckCastEllision || !PerformOptimizations()) {
Sebastien Hertz543959c2013-07-03 12:00:19 +0200232 return inst;
233 }
234 MethodReference referrer(&GetDexFile(), unit_.GetDexMethodIndex());
235 if (!driver_.IsSafeCast(referrer, dex_pc)) {
236 return inst;
237 }
238 // Ok, this is a safe cast. Since the "check-cast" instruction size is 2 code
239 // units and a "nop" instruction size is 1 code unit, we need to replace it by
240 // 2 consecutive NOP instructions.
241 // Because the caller loops over instructions by calling Instruction::Next onto
242 // the current instruction, we need to return the 2nd NOP instruction. Indeed,
243 // its next instruction is the former check-cast's next instruction.
244 VLOG(compiler) << "Removing " << Instruction::Name(inst->Opcode())
245 << " by replacing it with 2 NOPs at dex pc "
246 << StringPrintf("0x%x", dex_pc) << " in method "
247 << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
248 // We are modifying 4 consecutive bytes.
249 ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u);
250 inst->SetOpcode(Instruction::NOP);
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700251 inst->SetVRegA_10x(0u); // keep compliant with verifier.
Sebastien Hertz543959c2013-07-03 12:00:19 +0200252 // Get to next instruction which is the second half of check-cast and replace
253 // it by a NOP.
254 inst = const_cast<Instruction*>(inst->Next());
255 inst->SetOpcode(Instruction::NOP);
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700256 inst->SetVRegA_10x(0u); // keep compliant with verifier.
Sebastien Hertz543959c2013-07-03 12:00:19 +0200257 return inst;
258}
259
Brian Carlstrom7940e442013-07-12 13:46:57 -0700260void DexCompiler::CompileInstanceFieldAccess(Instruction* inst,
261 uint32_t dex_pc,
262 Instruction::Code new_opcode,
263 bool is_put) {
Sebastien Hertz75021222013-07-16 18:34:50 +0200264 if (!kEnableQuickening || !PerformOptimizations()) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700265 return;
266 }
267 uint32_t field_idx = inst->VRegC_22c();
268 int field_offset;
269 bool is_volatile;
270 bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, field_offset,
271 is_volatile, is_put);
272 if (fast_path && !is_volatile && IsUint(16, field_offset)) {
Sebastien Hertz543959c2013-07-03 12:00:19 +0200273 VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode())
274 << " to " << Instruction::Name(new_opcode)
275 << " by replacing field index " << field_idx
276 << " by field offset " << field_offset
277 << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
278 << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
Brian Carlstrom7940e442013-07-12 13:46:57 -0700279 // We are modifying 4 consecutive bytes.
280 ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u);
281 inst->SetOpcode(new_opcode);
282 // Replace field index by field offset.
283 inst->SetVRegC_22c(static_cast<uint16_t>(field_offset));
284 }
285}
286
287void DexCompiler::CompileInvokeVirtual(Instruction* inst,
288 uint32_t dex_pc,
289 Instruction::Code new_opcode,
290 bool is_range) {
Sebastien Hertz75021222013-07-16 18:34:50 +0200291 if (!kEnableQuickening || !PerformOptimizations()) {
Brian Carlstrom7940e442013-07-12 13:46:57 -0700292 return;
293 }
294 uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
295 MethodReference target_method(&GetDexFile(), method_idx);
296 InvokeType invoke_type = kVirtual;
297 InvokeType original_invoke_type = invoke_type;
298 int vtable_idx;
299 uintptr_t direct_code;
300 uintptr_t direct_method;
301 bool fast_path = driver_.ComputeInvokeInfo(&unit_, dex_pc, invoke_type,
302 target_method, vtable_idx,
303 direct_code, direct_method,
304 false);
305 // TODO: support devirtualization.
306 if (fast_path && original_invoke_type == invoke_type) {
307 if (vtable_idx >= 0 && IsUint(16, vtable_idx)) {
Sebastien Hertz543959c2013-07-03 12:00:19 +0200308 VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode())
309 << "(" << PrettyMethod(method_idx, GetDexFile(), true) << ")"
310 << " to " << Instruction::Name(new_opcode)
311 << " by replacing method index " << method_idx
312 << " by vtable index " << vtable_idx
313 << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
314 << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
Brian Carlstrom7940e442013-07-12 13:46:57 -0700315 // We are modifying 4 consecutive bytes.
316 ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u);
317 inst->SetOpcode(new_opcode);
318 // Replace method index by vtable index.
319 if (is_range) {
320 inst->SetVRegB_3rc(static_cast<uint16_t>(vtable_idx));
321 } else {
322 inst->SetVRegB_35c(static_cast<uint16_t>(vtable_idx));
323 }
324 }
325 }
326}
327
328} // namespace optimizer
329} // namespace art
330
Sebastien Hertz75021222013-07-16 18:34:50 +0200331extern "C" void ArtCompileDEX(art::CompilerDriver& compiler, const art::DexFile::CodeItem* code_item,
Brian Carlstrom7940e442013-07-12 13:46:57 -0700332 uint32_t access_flags, art::InvokeType invoke_type,
333 uint32_t class_def_idx, uint32_t method_idx, jobject class_loader,
Sebastien Hertz75021222013-07-16 18:34:50 +0200334 const art::DexFile& dex_file,
335 art::DexToDexCompilationLevel dex_to_dex_compilation_level) {
336 if (dex_to_dex_compilation_level != art::kDontDexToDexCompile) {
337 art::DexCompilationUnit unit(NULL, class_loader, art::Runtime::Current()->GetClassLinker(),
338 dex_file, code_item, class_def_idx, method_idx, access_flags);
339 art::optimizer::DexCompiler dex_compiler(compiler, unit, dex_to_dex_compilation_level);
340 dex_compiler.Compile();
341 }
Brian Carlstrom7940e442013-07-12 13:46:57 -0700342}