blob: 6e04cfe86496640868a31bb415c8e14c1ef367d2 [file] [log] [blame]
Ben Cheng7a2697d2010-06-07 13:44:23 -07001/*
2 * Copyright (C) 2010 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 "Dalvik.h"
18#include "Dataflow.h"
19#include "libdex/OpCodeNames.h"
20
21/* Convert the reg id from the callee to the original id passed by the caller */
22static inline u4 convertRegId(const DecodedInstruction *invoke,
23 const Method *calleeMethod,
24 int calleeRegId, bool isRange)
25{
26 /* The order in the original arg passing list */
27 int rank = calleeRegId -
28 (calleeMethod->registersSize - calleeMethod->insSize);
29 assert(rank >= 0);
30 if (!isRange) {
31 return invoke->arg[rank];
32 } else {
33 return invoke->vC + rank;
34 }
35}
36
37static void inlineGetter(CompilationUnit *cUnit,
38 const Method *calleeMethod,
39 MIR *invokeMIR,
40 BasicBlock *invokeBB,
41 bool isPredicted,
42 bool isRange)
43{
44 BasicBlock *moveResultBB = invokeBB->fallThrough;
45 MIR *moveResultMIR = moveResultBB->firstMIRInsn;
46 MIR *newGetterMIR = dvmCompilerNew(sizeof(MIR), true);
47 DecodedInstruction getterInsn;
48
49 dexDecodeInstruction(gDvm.instrFormat, calleeMethod->insns, &getterInsn);
50
51 if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &getterInsn))
52 return;
53
54 /*
55 * Some getters (especially invoked through interface) are not followed
56 * by a move result.
57 */
58 if ((moveResultMIR == NULL) ||
59 (moveResultMIR->dalvikInsn.opCode != OP_MOVE_RESULT &&
60 moveResultMIR->dalvikInsn.opCode != OP_MOVE_RESULT_OBJECT &&
61 moveResultMIR->dalvikInsn.opCode != OP_MOVE_RESULT_WIDE)) {
62 return;
63 }
64
65 int dfFlags = dvmCompilerDataFlowAttributes[getterInsn.opCode];
66
67 /* Expecting vA to be the destination register */
68 if (dfFlags & DF_UA) {
69 LOGE("opcode %d has DF_UA set (not expected)", getterInsn.opCode);
70 dvmAbort();
71 }
72
73 if (dfFlags & DF_UB) {
74 getterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
75 getterInsn.vB, isRange);
76 }
77
78 if (dfFlags & DF_UC) {
79 getterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
80 getterInsn.vC, isRange);
81 }
82
83 getterInsn.vA = moveResultMIR->dalvikInsn.vA;
84
85 /* Now setup the Dalvik instruction with converted src/dst registers */
86 newGetterMIR->dalvikInsn = getterInsn;
87
88 newGetterMIR->width = gDvm.instrWidth[getterInsn.opCode];
89
90 newGetterMIR->OptimizationFlags |= MIR_CALLEE;
91
92 /*
93 * If the getter instruction is about to raise any exception, punt to the
94 * interpreter and re-execute the invoke.
95 */
96 newGetterMIR->offset = invokeMIR->offset;
97
98 newGetterMIR->meta.calleeMethod = calleeMethod;
99
100 dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newGetterMIR);
101
102 if (isPredicted) {
103 MIR *invokeMIRSlow = dvmCompilerNew(sizeof(MIR), true);
104 *invokeMIRSlow = *invokeMIR;
105 invokeMIR->dalvikInsn.opCode = kMirOpCheckInlinePrediction;
106
107 /* Use vC to denote the first argument (ie this) */
108 if (!isRange) {
109 invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0];
110 }
111
112 moveResultMIR->OptimizationFlags |= MIR_INLINED_PRED;
113
114 dvmCompilerInsertMIRAfter(invokeBB, newGetterMIR, invokeMIRSlow);
115 invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
116#if defined(WITH_JIT_TUNING)
117 gDvmJit.invokePolyGetterInlined++;
118#endif
119 } else {
120 invokeMIR->OptimizationFlags |= MIR_INLINED;
121 moveResultMIR->OptimizationFlags |= MIR_INLINED;
122#if defined(WITH_JIT_TUNING)
123 gDvmJit.invokeMonoGetterInlined++;
124#endif
125 }
126
127 return;
128}
129
130static void inlineSetter(CompilationUnit *cUnit,
131 const Method *calleeMethod,
132 MIR *invokeMIR,
133 BasicBlock *invokeBB,
134 bool isPredicted,
135 bool isRange)
136{
137 MIR *newSetterMIR = dvmCompilerNew(sizeof(MIR), true);
138 DecodedInstruction setterInsn;
139
140 dexDecodeInstruction(gDvm.instrFormat, calleeMethod->insns, &setterInsn);
141
142 if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &setterInsn))
143 return;
144
145 int dfFlags = dvmCompilerDataFlowAttributes[setterInsn.opCode];
146
147 if (dfFlags & DF_UA) {
148 setterInsn.vA = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
149 setterInsn.vA, isRange);
150
151 }
152
153 if (dfFlags & DF_UB) {
154 setterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
155 setterInsn.vB, isRange);
156
157 }
158
159 if (dfFlags & DF_UC) {
160 setterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
161 setterInsn.vC, isRange);
162 }
163
164 /* Now setup the Dalvik instruction with converted src/dst registers */
165 newSetterMIR->dalvikInsn = setterInsn;
166
167 newSetterMIR->width = gDvm.instrWidth[setterInsn.opCode];
168
169 newSetterMIR->OptimizationFlags |= MIR_CALLEE;
170
171 /*
172 * If the setter instruction is about to raise any exception, punt to the
173 * interpreter and re-execute the invoke.
174 */
175 newSetterMIR->offset = invokeMIR->offset;
176
177 newSetterMIR->meta.calleeMethod = calleeMethod;
178
179 dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newSetterMIR);
180
181 if (isPredicted) {
182 MIR *invokeMIRSlow = dvmCompilerNew(sizeof(MIR), true);
183 *invokeMIRSlow = *invokeMIR;
184 invokeMIR->dalvikInsn.opCode = kMirOpCheckInlinePrediction;
185
186 /* Use vC to denote the first argument (ie this) */
187 if (!isRange) {
188 invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0];
189 }
190
191 dvmCompilerInsertMIRAfter(invokeBB, newSetterMIR, invokeMIRSlow);
192 invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
193#if defined(WITH_JIT_TUNING)
194 gDvmJit.invokePolySetterInlined++;
195#endif
196 } else {
197 invokeMIR->OptimizationFlags |= MIR_INLINED;
198#if defined(WITH_JIT_TUNING)
199 gDvmJit.invokeMonoSetterInlined++;
200#endif
201 }
202
203 return;
204}
205
206static void tryInlineSingletonCallsite(CompilationUnit *cUnit,
207 const Method *calleeMethod,
208 MIR *invokeMIR,
209 BasicBlock *invokeBB,
210 bool isRange)
211{
212 /* Not a Java method */
213 if (dvmIsNativeMethod(calleeMethod)) return;
214
215 CompilerMethodStats *methodStats =
216 dvmCompilerAnalyzeMethodBody(calleeMethod, true);
217
218 /* Empty callee - do nothing */
219 if (methodStats->attributes & METHOD_IS_EMPTY) {
220 /* The original invoke instruction is effectively turned into NOP */
221 invokeMIR->OptimizationFlags |= MIR_INLINED;
222 return;
223 }
224
225 if (methodStats->attributes & METHOD_IS_GETTER) {
226 inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, false, isRange);
227 return;
228 } else if (methodStats->attributes & METHOD_IS_SETTER) {
229 inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, false, isRange);
230 return;
231 }
232}
233
234static void inlineEmptyVirtualCallee(CompilationUnit *cUnit,
235 const Method *calleeMethod,
236 MIR *invokeMIR,
237 BasicBlock *invokeBB)
238{
239 MIR *invokeMIRSlow = dvmCompilerNew(sizeof(MIR), true);
240 *invokeMIRSlow = *invokeMIR;
241 invokeMIR->dalvikInsn.opCode = kMirOpCheckInlinePrediction;
242
243 dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, invokeMIRSlow);
244 invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
245}
246
247static void tryInlineVirtualCallsite(CompilationUnit *cUnit,
248 const Method *calleeMethod,
249 MIR *invokeMIR,
250 BasicBlock *invokeBB,
251 bool isRange)
252{
253 /* Not a Java method */
254 if (dvmIsNativeMethod(calleeMethod)) return;
255
256 CompilerMethodStats *methodStats =
257 dvmCompilerAnalyzeMethodBody(calleeMethod, true);
258
259 /* Empty callee - do nothing by checking the clazz pointer */
260 if (methodStats->attributes & METHOD_IS_EMPTY) {
261 inlineEmptyVirtualCallee(cUnit, calleeMethod, invokeMIR, invokeBB);
262 return;
263 }
264
265 if (methodStats->attributes & METHOD_IS_GETTER) {
266 inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, true, isRange);
267 return;
268 } else if (methodStats->attributes & METHOD_IS_SETTER) {
269 inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, true, isRange);
270 return;
271 }
272}
273
274
275void dvmCompilerInlineMIR(CompilationUnit *cUnit)
276{
277 int i;
278 bool isRange = false;
279
280 /*
281 * Analyze the basic block containing an invoke to see if it can be inlined
282 */
283 for (i = 0; i < cUnit->numBlocks; i++) {
284 BasicBlock *bb = cUnit->blockList[i];
285 if (bb->blockType != kDalvikByteCode)
286 continue;
287 MIR *lastMIRInsn = bb->lastMIRInsn;
288 int opCode = lastMIRInsn->dalvikInsn.opCode;
289 int flags = dexGetInstrFlags(gDvm.instrFlags, opCode);
290
291 /* No invoke - continue */
292 if ((flags & kInstrInvoke) == 0)
293 continue;
294
295 /* Not a real invoke - continue */
296 if (opCode == OP_INVOKE_DIRECT_EMPTY)
297 continue;
298
299 const Method *calleeMethod;
300
301 switch (opCode) {
302 case OP_INVOKE_SUPER:
303 case OP_INVOKE_DIRECT:
304 case OP_INVOKE_STATIC:
305 case OP_INVOKE_SUPER_QUICK:
306 calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
307 break;
308 case OP_INVOKE_SUPER_RANGE:
309 case OP_INVOKE_DIRECT_RANGE:
310 case OP_INVOKE_STATIC_RANGE:
311 case OP_INVOKE_SUPER_QUICK_RANGE:
312 isRange = true;
313 calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
314 break;
315 default:
316 calleeMethod = NULL;
317 break;
318 }
319
320 if (calleeMethod) {
321 tryInlineSingletonCallsite(cUnit, calleeMethod, lastMIRInsn, bb,
322 isRange);
323 return;
324 }
325
326 switch (opCode) {
327 case OP_INVOKE_VIRTUAL:
328 case OP_INVOKE_VIRTUAL_QUICK:
329 case OP_INVOKE_INTERFACE:
330 isRange = false;
331 calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
332 break;
333 case OP_INVOKE_VIRTUAL_RANGE:
334 case OP_INVOKE_VIRTUAL_QUICK_RANGE:
335 case OP_INVOKE_INTERFACE_RANGE:
336 isRange = true;
337 calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
338 break;
339 default:
340 break;
341 }
342
343 if (calleeMethod) {
344 tryInlineVirtualCallsite(cUnit, calleeMethod, lastMIRInsn, bb,
345 isRange);
346 return;
347 }
348 }
349}