InlineTransformation.cpp revision c1a4ab9c313d8a3d12007f2dbef7b5a6fa4ac2ef
1/*
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/DexOpcodes.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 bool 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 = (MIR *)dvmCompilerNew(sizeof(MIR), true);
47    DecodedInstruction getterInsn;
48
49    /*
50     * Not all getter instructions have vC but vC will be read by
51     * dvmCompilerGetDalvikDisassembly unconditionally.
52     * Initialize it here to get Valgrind happy.
53     */
54    getterInsn.vC = 0;
55
56    dexDecodeInstruction(calleeMethod->insns, &getterInsn);
57
58    if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &getterInsn))
59        return false;
60
61    /*
62     * Some getters (especially invoked through interface) are not followed
63     * by a move result.
64     */
65    if ((moveResultMIR == NULL) ||
66        (moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT &&
67         moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT_OBJECT &&
68         moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT_WIDE)) {
69        return false;
70    }
71
72    int dfFlags = dvmCompilerDataFlowAttributes[getterInsn.opcode];
73
74    /* Expecting vA to be the destination register */
75    if (dfFlags & (DF_UA | DF_UA_WIDE)) {
76        ALOGE("opcode %d has DF_UA set (not expected)", getterInsn.opcode);
77        dvmAbort();
78    }
79
80    if (dfFlags & DF_UB) {
81        getterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
82                                     getterInsn.vB, isRange);
83    }
84
85    if (dfFlags & DF_UC) {
86        getterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
87                                     getterInsn.vC, isRange);
88    }
89
90    getterInsn.vA = moveResultMIR->dalvikInsn.vA;
91
92    /* Now setup the Dalvik instruction with converted src/dst registers */
93    newGetterMIR->dalvikInsn = getterInsn;
94
95    newGetterMIR->width = dexGetWidthFromOpcode(getterInsn.opcode);
96
97    newGetterMIR->OptimizationFlags |= MIR_CALLEE;
98
99    /*
100     * If the getter instruction is about to raise any exception, punt to the
101     * interpreter and re-execute the invoke.
102     */
103    newGetterMIR->offset = invokeMIR->offset;
104
105    newGetterMIR->meta.calleeMethod = calleeMethod;
106
107    dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newGetterMIR);
108
109    if (isPredicted) {
110        MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true);
111        *invokeMIRSlow = *invokeMIR;
112        invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction;
113
114        /* Use vC to denote the first argument (ie this) */
115        if (!isRange) {
116            invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0];
117        }
118
119        moveResultMIR->OptimizationFlags |= MIR_INLINED_PRED;
120
121        dvmCompilerInsertMIRAfter(invokeBB, newGetterMIR, invokeMIRSlow);
122        invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
123#if defined(WITH_JIT_TUNING)
124        gDvmJit.invokePolyGetterInlined++;
125#endif
126    } else {
127        invokeMIR->OptimizationFlags |= MIR_INLINED;
128        moveResultMIR->OptimizationFlags |= MIR_INLINED;
129#if defined(WITH_JIT_TUNING)
130        gDvmJit.invokeMonoGetterInlined++;
131#endif
132    }
133
134    return true;
135}
136
137static bool inlineSetter(CompilationUnit *cUnit,
138                         const Method *calleeMethod,
139                         MIR *invokeMIR,
140                         BasicBlock *invokeBB,
141                         bool isPredicted,
142                         bool isRange)
143{
144    MIR *newSetterMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
145    DecodedInstruction setterInsn;
146
147    /*
148     * Not all setter instructions have vC but vC will be read by
149     * dvmCompilerGetDalvikDisassembly unconditionally.
150     * Initialize it here to get Valgrind happy.
151     */
152    setterInsn.vC = 0;
153
154    dexDecodeInstruction(calleeMethod->insns, &setterInsn);
155
156    if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &setterInsn))
157        return false;
158
159    int dfFlags = dvmCompilerDataFlowAttributes[setterInsn.opcode];
160
161    if (dfFlags & (DF_UA | DF_UA_WIDE)) {
162        setterInsn.vA = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
163                                     setterInsn.vA, isRange);
164
165    }
166
167    if (dfFlags & DF_UB) {
168        setterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
169                                     setterInsn.vB, isRange);
170
171    }
172
173    if (dfFlags & DF_UC) {
174        setterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
175                                     setterInsn.vC, isRange);
176    }
177
178    /* Now setup the Dalvik instruction with converted src/dst registers */
179    newSetterMIR->dalvikInsn = setterInsn;
180
181    newSetterMIR->width = dexGetWidthFromOpcode(setterInsn.opcode);
182
183    newSetterMIR->OptimizationFlags |= MIR_CALLEE;
184
185    /*
186     * If the setter instruction is about to raise any exception, punt to the
187     * interpreter and re-execute the invoke.
188     */
189    newSetterMIR->offset = invokeMIR->offset;
190
191    newSetterMIR->meta.calleeMethod = calleeMethod;
192
193    dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newSetterMIR);
194
195    if (isPredicted) {
196        MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true);
197        *invokeMIRSlow = *invokeMIR;
198        invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction;
199
200        /* Use vC to denote the first argument (ie this) */
201        if (!isRange) {
202            invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0];
203        }
204
205        dvmCompilerInsertMIRAfter(invokeBB, newSetterMIR, invokeMIRSlow);
206        invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
207#if defined(WITH_JIT_TUNING)
208        gDvmJit.invokePolySetterInlined++;
209#endif
210    } else {
211        /*
212         * The invoke becomes no-op so it needs an explicit branch to jump to
213         * the chaining cell.
214         */
215        invokeBB->needFallThroughBranch = true;
216        invokeMIR->OptimizationFlags |= MIR_INLINED;
217#if defined(WITH_JIT_TUNING)
218        gDvmJit.invokeMonoSetterInlined++;
219#endif
220    }
221
222    return true;
223}
224
225static bool tryInlineSingletonCallsite(CompilationUnit *cUnit,
226                                       const Method *calleeMethod,
227                                       MIR *invokeMIR,
228                                       BasicBlock *invokeBB,
229                                       bool isRange)
230{
231    /* Not a Java method */
232    if (dvmIsNativeMethod(calleeMethod)) return false;
233
234    CompilerMethodStats *methodStats =
235        dvmCompilerAnalyzeMethodBody(calleeMethod, true);
236
237    /* Empty callee - do nothing */
238    if (methodStats->attributes & METHOD_IS_EMPTY) {
239        /* The original invoke instruction is effectively turned into NOP */
240        invokeMIR->OptimizationFlags |= MIR_INLINED;
241        /*
242         * Need to insert an explicit branch to catch the falling knife (into
243         * the PC reconstruction or chaining cell).
244         */
245        invokeBB->needFallThroughBranch = true;
246        return true;
247    }
248
249    if (methodStats->attributes & METHOD_IS_GETTER) {
250        return inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, false,
251                            isRange);
252    } else if (methodStats->attributes & METHOD_IS_SETTER) {
253        return inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, false,
254                            isRange);
255    }
256    return false;
257}
258
259static bool inlineEmptyVirtualCallee(CompilationUnit *cUnit,
260                                     const Method *calleeMethod,
261                                     MIR *invokeMIR,
262                                     BasicBlock *invokeBB)
263{
264    MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true);
265    *invokeMIRSlow = *invokeMIR;
266    invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction;
267
268    dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, invokeMIRSlow);
269    invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
270    return true;
271}
272
273static bool tryInlineVirtualCallsite(CompilationUnit *cUnit,
274                                     const Method *calleeMethod,
275                                     MIR *invokeMIR,
276                                     BasicBlock *invokeBB,
277                                     bool isRange)
278{
279    /* Not a Java method */
280    if (dvmIsNativeMethod(calleeMethod)) return false;
281
282    CompilerMethodStats *methodStats =
283        dvmCompilerAnalyzeMethodBody(calleeMethod, true);
284
285    /* Empty callee - do nothing by checking the clazz pointer */
286    if (methodStats->attributes & METHOD_IS_EMPTY) {
287        return inlineEmptyVirtualCallee(cUnit, calleeMethod, invokeMIR,
288                                        invokeBB);
289    }
290
291    if (methodStats->attributes & METHOD_IS_GETTER) {
292        return inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, true,
293                            isRange);
294    } else if (methodStats->attributes & METHOD_IS_SETTER) {
295        return inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, true,
296                            isRange);
297    }
298    return false;
299}
300
301
302void dvmCompilerInlineMIR(CompilationUnit *cUnit, JitTranslationInfo *info)
303{
304    bool isRange = false;
305    GrowableListIterator iterator;
306
307    dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
308    /*
309     * Analyze the basic block containing an invoke to see if it can be inlined
310     */
311    while (true) {
312        BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
313        if (bb == NULL) break;
314        if (bb->blockType != kDalvikByteCode)
315            continue;
316        MIR *lastMIRInsn = bb->lastMIRInsn;
317        Opcode opcode = lastMIRInsn->dalvikInsn.opcode;
318        int flags = (int)dexGetFlagsFromOpcode(opcode);
319
320        /* No invoke - continue */
321        if ((flags & kInstrInvoke) == 0)
322            continue;
323
324        /* Disable inlining when doing method tracing */
325        if (gDvmJit.methodTraceSupport)
326            continue;
327
328        /*
329         * If the invoke itself is selected for single stepping, don't bother
330         * to inline it.
331         */
332        if (SINGLE_STEP_OP(opcode))
333            continue;
334
335        const Method *calleeMethod;
336
337        switch (opcode) {
338            case OP_INVOKE_SUPER:
339            case OP_INVOKE_DIRECT:
340            case OP_INVOKE_STATIC:
341            case OP_INVOKE_SUPER_QUICK:
342                calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
343                break;
344            case OP_INVOKE_SUPER_RANGE:
345            case OP_INVOKE_DIRECT_RANGE:
346            case OP_INVOKE_STATIC_RANGE:
347            case OP_INVOKE_SUPER_QUICK_RANGE:
348                isRange = true;
349                calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
350                break;
351            default:
352                calleeMethod = NULL;
353                break;
354        }
355
356        if (calleeMethod) {
357            bool inlined = tryInlineSingletonCallsite(cUnit, calleeMethod,
358                                                      lastMIRInsn, bb, isRange);
359            if (!inlined &&
360                !(gDvmJit.disableOpt & (1 << kMethodJit)) &&
361                !dvmIsNativeMethod(calleeMethod)) {
362                CompilerMethodStats *methodStats =
363                    dvmCompilerAnalyzeMethodBody(calleeMethod, true);
364                if ((methodStats->attributes & METHOD_IS_LEAF) &&
365                    !(methodStats->attributes & METHOD_CANNOT_COMPILE)) {
366                    /* Callee has been previously compiled */
367                    if (dvmJitGetMethodAddr(calleeMethod->insns)) {
368                        lastMIRInsn->OptimizationFlags |= MIR_INVOKE_METHOD_JIT;
369                    } else {
370                        /* Compile the callee first */
371                        dvmCompileMethod(calleeMethod, info);
372                        if (dvmJitGetMethodAddr(calleeMethod->insns)) {
373                            lastMIRInsn->OptimizationFlags |=
374                                MIR_INVOKE_METHOD_JIT;
375                        } else {
376                            methodStats->attributes |= METHOD_CANNOT_COMPILE;
377                        }
378                    }
379                }
380            }
381            return;
382        }
383
384        switch (opcode) {
385            case OP_INVOKE_VIRTUAL:
386            case OP_INVOKE_VIRTUAL_QUICK:
387            case OP_INVOKE_INTERFACE:
388                isRange = false;
389                calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
390                break;
391            case OP_INVOKE_VIRTUAL_RANGE:
392            case OP_INVOKE_VIRTUAL_QUICK_RANGE:
393            case OP_INVOKE_INTERFACE_RANGE:
394                isRange = true;
395                calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
396                break;
397            default:
398                break;
399        }
400
401        if (calleeMethod) {
402            bool inlined = tryInlineVirtualCallsite(cUnit, calleeMethod,
403                                                    lastMIRInsn, bb, isRange);
404            if (!inlined &&
405                !(gDvmJit.disableOpt & (1 << kMethodJit)) &&
406                !dvmIsNativeMethod(calleeMethod)) {
407                CompilerMethodStats *methodStats =
408                    dvmCompilerAnalyzeMethodBody(calleeMethod, true);
409                if ((methodStats->attributes & METHOD_IS_LEAF) &&
410                    !(methodStats->attributes & METHOD_CANNOT_COMPILE)) {
411                    /* Callee has been previously compiled */
412                    if (dvmJitGetMethodAddr(calleeMethod->insns)) {
413                        lastMIRInsn->OptimizationFlags |= MIR_INVOKE_METHOD_JIT;
414                    } else {
415                        /* Compile the callee first */
416                        dvmCompileMethod(calleeMethod, info);
417                        if (dvmJitGetMethodAddr(calleeMethod->insns)) {
418                            lastMIRInsn->OptimizationFlags |=
419                                MIR_INVOKE_METHOD_JIT;
420                        } else {
421                            methodStats->attributes |= METHOD_CANNOT_COMPILE;
422                        }
423                    }
424                }
425            }
426            return;
427        }
428    }
429}
430