17a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng/* 27a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * Copyright (C) 2010 The Android Open Source Project 37a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * 47a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * Licensed under the Apache License, Version 2.0 (the "License"); 57a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * you may not use this file except in compliance with the License. 67a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * You may obtain a copy of the License at 77a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * 87a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * http://www.apache.org/licenses/LICENSE-2.0 97a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * 107a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * Unless required by applicable law or agreed to in writing, software 117a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * distributed under the License is distributed on an "AS IS" BASIS, 127a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 137a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * See the License for the specific language governing permissions and 147a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * limitations under the License. 157a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng */ 167a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 177a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng#include "Dalvik.h" 187a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng#include "Dataflow.h" 19df4daaf8f41e3dcaa8221f54273338160dd43138Dan Bornstein#include "libdex/DexOpcodes.h" 207a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 217a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng/* Convert the reg id from the callee to the original id passed by the caller */ 227a2697d327936e20ef5484f7819e2e4bf91c891fBen Chengstatic inline u4 convertRegId(const DecodedInstruction *invoke, 237a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng const Method *calleeMethod, 247a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng int calleeRegId, bool isRange) 257a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng{ 267a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng /* The order in the original arg passing list */ 277a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng int rank = calleeRegId - 287a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng (calleeMethod->registersSize - calleeMethod->insSize); 297a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng assert(rank >= 0); 307a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if (!isRange) { 317a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng return invoke->arg[rank]; 327a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } else { 337a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng return invoke->vC + rank; 347a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 357a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng} 367a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 37cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Chengstatic bool inlineGetter(CompilationUnit *cUnit, 387a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng const Method *calleeMethod, 397a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng MIR *invokeMIR, 407a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng BasicBlock *invokeBB, 417a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng bool isPredicted, 427a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng bool isRange) 437a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng{ 447a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng BasicBlock *moveResultBB = invokeBB->fallThrough; 457a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng MIR *moveResultMIR = moveResultBB->firstMIRInsn; 46fc75f3ed87b55d625b6054e18645da5cbdba31c6Carl Shapiro MIR *newGetterMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true); 477a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng DecodedInstruction getterInsn; 487a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 49a18e6d135a869f9c62f9ec8bac8b9e78d92c697fBen Cheng /* 50a18e6d135a869f9c62f9ec8bac8b9e78d92c697fBen Cheng * Not all getter instructions have vC but vC will be read by 51a18e6d135a869f9c62f9ec8bac8b9e78d92c697fBen Cheng * dvmCompilerGetDalvikDisassembly unconditionally. 52a18e6d135a869f9c62f9ec8bac8b9e78d92c697fBen Cheng * Initialize it here to get Valgrind happy. 53a18e6d135a869f9c62f9ec8bac8b9e78d92c697fBen Cheng */ 54a18e6d135a869f9c62f9ec8bac8b9e78d92c697fBen Cheng getterInsn.vC = 0; 55a18e6d135a869f9c62f9ec8bac8b9e78d92c697fBen Cheng 56543223954993a19fa96670692bc7aa55d851966bDan Bornstein dexDecodeInstruction(calleeMethod->insns, &getterInsn); 577a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 587a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &getterInsn)) 59cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng return false; 607a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 617a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng /* 627a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * Some getters (especially invoked through interface) are not followed 637a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * by a move result. 647a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng */ 657a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if ((moveResultMIR == NULL) || 669a1f81699cc05b58378ffb9aadb4e97677943791Dan Bornstein (moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT && 679a1f81699cc05b58378ffb9aadb4e97677943791Dan Bornstein moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT_OBJECT && 689a1f81699cc05b58378ffb9aadb4e97677943791Dan Bornstein moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT_WIDE)) { 69cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng return false; 707a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 717a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 729a1f81699cc05b58378ffb9aadb4e97677943791Dan Bornstein int dfFlags = dvmCompilerDataFlowAttributes[getterInsn.opcode]; 737a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 747a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng /* Expecting vA to be the destination register */ 7552d4cd28a5d6d946934704e11e1d41450c10aa05Ben Cheng if (dfFlags & (DF_UA | DF_UA_WIDE)) { 76c1a4ab9c313d8a3d12007f2dbef7b5a6fa4ac2efSteve Block ALOGE("opcode %d has DF_UA set (not expected)", getterInsn.opcode); 777a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng dvmAbort(); 787a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 797a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 807a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if (dfFlags & DF_UB) { 817a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng getterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, 827a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng getterInsn.vB, isRange); 837a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 847a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 857a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if (dfFlags & DF_UC) { 867a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng getterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, 877a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng getterInsn.vC, isRange); 887a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 897a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 907a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng getterInsn.vA = moveResultMIR->dalvikInsn.vA; 917a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 927a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng /* Now setup the Dalvik instruction with converted src/dst registers */ 937a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng newGetterMIR->dalvikInsn = getterInsn; 947a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 95e485276c6ba778cafa373b3b5c867f84e91b0bfdDan Bornstein newGetterMIR->width = dexGetWidthFromOpcode(getterInsn.opcode); 967a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 977a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng newGetterMIR->OptimizationFlags |= MIR_CALLEE; 987a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 997a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng /* 1007a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * If the getter instruction is about to raise any exception, punt to the 1017a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * interpreter and re-execute the invoke. 1027a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng */ 1037a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng newGetterMIR->offset = invokeMIR->offset; 1047a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 1057a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng newGetterMIR->meta.calleeMethod = calleeMethod; 1067a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 1077a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newGetterMIR); 1087a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 1097a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if (isPredicted) { 110fc75f3ed87b55d625b6054e18645da5cbdba31c6Carl Shapiro MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true); 1117a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng *invokeMIRSlow = *invokeMIR; 1125d5b94c8d14b166af580d5dd5906db4f9527d6caCarl Shapiro invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction; 1137a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 1147a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng /* Use vC to denote the first argument (ie this) */ 1157a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if (!isRange) { 1167a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0]; 1177a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 1187a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 1197a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng moveResultMIR->OptimizationFlags |= MIR_INLINED_PRED; 1207a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 1217a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng dvmCompilerInsertMIRAfter(invokeBB, newGetterMIR, invokeMIRSlow); 1227a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED; 1237a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng#if defined(WITH_JIT_TUNING) 1247a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng gDvmJit.invokePolyGetterInlined++; 1257a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng#endif 1267a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } else { 1277a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng invokeMIR->OptimizationFlags |= MIR_INLINED; 1287a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng moveResultMIR->OptimizationFlags |= MIR_INLINED; 1297a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng#if defined(WITH_JIT_TUNING) 1307a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng gDvmJit.invokeMonoGetterInlined++; 1317a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng#endif 1327a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 1337a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 134cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng return true; 1357a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng} 1367a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 137cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Chengstatic bool inlineSetter(CompilationUnit *cUnit, 1387a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng const Method *calleeMethod, 1397a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng MIR *invokeMIR, 1407a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng BasicBlock *invokeBB, 1417a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng bool isPredicted, 1427a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng bool isRange) 1437a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng{ 144fc75f3ed87b55d625b6054e18645da5cbdba31c6Carl Shapiro MIR *newSetterMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true); 1457a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng DecodedInstruction setterInsn; 1467a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 147a18e6d135a869f9c62f9ec8bac8b9e78d92c697fBen Cheng /* 148a18e6d135a869f9c62f9ec8bac8b9e78d92c697fBen Cheng * Not all setter instructions have vC but vC will be read by 149a18e6d135a869f9c62f9ec8bac8b9e78d92c697fBen Cheng * dvmCompilerGetDalvikDisassembly unconditionally. 150a18e6d135a869f9c62f9ec8bac8b9e78d92c697fBen Cheng * Initialize it here to get Valgrind happy. 151a18e6d135a869f9c62f9ec8bac8b9e78d92c697fBen Cheng */ 152a18e6d135a869f9c62f9ec8bac8b9e78d92c697fBen Cheng setterInsn.vC = 0; 153a18e6d135a869f9c62f9ec8bac8b9e78d92c697fBen Cheng 154543223954993a19fa96670692bc7aa55d851966bDan Bornstein dexDecodeInstruction(calleeMethod->insns, &setterInsn); 1557a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 1567a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &setterInsn)) 157cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng return false; 1587a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 1599a1f81699cc05b58378ffb9aadb4e97677943791Dan Bornstein int dfFlags = dvmCompilerDataFlowAttributes[setterInsn.opcode]; 1607a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 16152d4cd28a5d6d946934704e11e1d41450c10aa05Ben Cheng if (dfFlags & (DF_UA | DF_UA_WIDE)) { 1627a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng setterInsn.vA = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, 1637a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng setterInsn.vA, isRange); 1647a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 1657a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 1667a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 1677a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if (dfFlags & DF_UB) { 1687a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng setterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, 1697a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng setterInsn.vB, isRange); 1707a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 1717a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 1727a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 1737a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if (dfFlags & DF_UC) { 1747a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng setterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, 1757a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng setterInsn.vC, isRange); 1767a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 1777a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 1787a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng /* Now setup the Dalvik instruction with converted src/dst registers */ 1797a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng newSetterMIR->dalvikInsn = setterInsn; 1807a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 181e485276c6ba778cafa373b3b5c867f84e91b0bfdDan Bornstein newSetterMIR->width = dexGetWidthFromOpcode(setterInsn.opcode); 1827a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 1837a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng newSetterMIR->OptimizationFlags |= MIR_CALLEE; 1847a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 1857a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng /* 1867a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * If the setter instruction is about to raise any exception, punt to the 1877a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * interpreter and re-execute the invoke. 1887a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng */ 1897a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng newSetterMIR->offset = invokeMIR->offset; 1907a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 1917a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng newSetterMIR->meta.calleeMethod = calleeMethod; 1927a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 1937a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newSetterMIR); 1947a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 1957a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if (isPredicted) { 196fc75f3ed87b55d625b6054e18645da5cbdba31c6Carl Shapiro MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true); 1977a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng *invokeMIRSlow = *invokeMIR; 1985d5b94c8d14b166af580d5dd5906db4f9527d6caCarl Shapiro invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction; 1997a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 2007a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng /* Use vC to denote the first argument (ie this) */ 2017a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if (!isRange) { 2027a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0]; 2037a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 2047a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 2057a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng dvmCompilerInsertMIRAfter(invokeBB, newSetterMIR, invokeMIRSlow); 2067a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED; 2077a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng#if defined(WITH_JIT_TUNING) 2087a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng gDvmJit.invokePolySetterInlined++; 2097a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng#endif 2107a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } else { 21133c1cf94f41ee042709e3c7b83f189970a637da2Ben Cheng /* 21233c1cf94f41ee042709e3c7b83f189970a637da2Ben Cheng * The invoke becomes no-op so it needs an explicit branch to jump to 21333c1cf94f41ee042709e3c7b83f189970a637da2Ben Cheng * the chaining cell. 21433c1cf94f41ee042709e3c7b83f189970a637da2Ben Cheng */ 21533c1cf94f41ee042709e3c7b83f189970a637da2Ben Cheng invokeBB->needFallThroughBranch = true; 2167a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng invokeMIR->OptimizationFlags |= MIR_INLINED; 2177a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng#if defined(WITH_JIT_TUNING) 2187a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng gDvmJit.invokeMonoSetterInlined++; 2197a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng#endif 2207a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 2217a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 222cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng return true; 2237a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng} 2247a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 225cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Chengstatic bool tryInlineSingletonCallsite(CompilationUnit *cUnit, 2267a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng const Method *calleeMethod, 2277a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng MIR *invokeMIR, 2287a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng BasicBlock *invokeBB, 2297a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng bool isRange) 2307a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng{ 2317a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng /* Not a Java method */ 232cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng if (dvmIsNativeMethod(calleeMethod)) return false; 2337a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 2347a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng CompilerMethodStats *methodStats = 2357a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng dvmCompilerAnalyzeMethodBody(calleeMethod, true); 2367a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 2377a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng /* Empty callee - do nothing */ 2387a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if (methodStats->attributes & METHOD_IS_EMPTY) { 2397a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng /* The original invoke instruction is effectively turned into NOP */ 2407a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng invokeMIR->OptimizationFlags |= MIR_INLINED; 24123608ab40900463fc5c8461671ba3aa5d0a4260eBen Cheng /* 24223608ab40900463fc5c8461671ba3aa5d0a4260eBen Cheng * Need to insert an explicit branch to catch the falling knife (into 24323608ab40900463fc5c8461671ba3aa5d0a4260eBen Cheng * the PC reconstruction or chaining cell). 24423608ab40900463fc5c8461671ba3aa5d0a4260eBen Cheng */ 24523608ab40900463fc5c8461671ba3aa5d0a4260eBen Cheng invokeBB->needFallThroughBranch = true; 246cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng return true; 2477a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 2487a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 2497a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if (methodStats->attributes & METHOD_IS_GETTER) { 250cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng return inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, false, 251cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng isRange); 2527a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } else if (methodStats->attributes & METHOD_IS_SETTER) { 253cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng return inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, false, 254cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng isRange); 2557a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 256cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng return false; 2577a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng} 2587a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 259cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Chengstatic bool inlineEmptyVirtualCallee(CompilationUnit *cUnit, 2607a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng const Method *calleeMethod, 2617a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng MIR *invokeMIR, 2627a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng BasicBlock *invokeBB) 2637a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng{ 264fc75f3ed87b55d625b6054e18645da5cbdba31c6Carl Shapiro MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true); 2657a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng *invokeMIRSlow = *invokeMIR; 2665d5b94c8d14b166af580d5dd5906db4f9527d6caCarl Shapiro invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction; 2677a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 2687a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, invokeMIRSlow); 2697a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED; 270cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng return true; 2717a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng} 2727a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 273cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Chengstatic bool tryInlineVirtualCallsite(CompilationUnit *cUnit, 2747a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng const Method *calleeMethod, 2757a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng MIR *invokeMIR, 2767a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng BasicBlock *invokeBB, 2777a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng bool isRange) 2787a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng{ 2797a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng /* Not a Java method */ 280cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng if (dvmIsNativeMethod(calleeMethod)) return false; 2817a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 2827a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng CompilerMethodStats *methodStats = 2837a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng dvmCompilerAnalyzeMethodBody(calleeMethod, true); 2847a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 2857a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng /* Empty callee - do nothing by checking the clazz pointer */ 2867a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if (methodStats->attributes & METHOD_IS_EMPTY) { 287cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng return inlineEmptyVirtualCallee(cUnit, calleeMethod, invokeMIR, 288cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng invokeBB); 2897a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 2907a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 2917a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if (methodStats->attributes & METHOD_IS_GETTER) { 292cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng return inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, true, 293cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng isRange); 2947a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } else if (methodStats->attributes & METHOD_IS_SETTER) { 295cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng return inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, true, 296cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng isRange); 2977a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 298cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng return false; 2997a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng} 3007a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 3017a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 302cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Chengvoid dvmCompilerInlineMIR(CompilationUnit *cUnit, JitTranslationInfo *info) 3037a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng{ 3047a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng bool isRange = false; 30500603079b8723b32c955513eae63a8f97898074dBen Cheng GrowableListIterator iterator; 3067a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 30700603079b8723b32c955513eae63a8f97898074dBen Cheng dvmGrowableListIteratorInit(&cUnit->blockList, &iterator); 3087a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng /* 3097a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng * Analyze the basic block containing an invoke to see if it can be inlined 3107a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng */ 31100603079b8723b32c955513eae63a8f97898074dBen Cheng while (true) { 31200603079b8723b32c955513eae63a8f97898074dBen Cheng BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator); 31300603079b8723b32c955513eae63a8f97898074dBen Cheng if (bb == NULL) break; 3147a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if (bb->blockType != kDalvikByteCode) 3157a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng continue; 3167a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng MIR *lastMIRInsn = bb->lastMIRInsn; 3175d5b94c8d14b166af580d5dd5906db4f9527d6caCarl Shapiro Opcode opcode = lastMIRInsn->dalvikInsn.opcode; 3185d5b94c8d14b166af580d5dd5906db4f9527d6caCarl Shapiro int flags = (int)dexGetFlagsFromOpcode(opcode); 3197a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 3207a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng /* No invoke - continue */ 3217a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if ((flags & kInstrInvoke) == 0) 3227a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng continue; 3237a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 32418fba346582c08d81aa96d9508c0e935bad5f36fbuzbee /* Disable inlining when doing method tracing */ 32518fba346582c08d81aa96d9508c0e935bad5f36fbuzbee if (gDvmJit.methodTraceSupport) 32618fba346582c08d81aa96d9508c0e935bad5f36fbuzbee continue; 32718fba346582c08d81aa96d9508c0e935bad5f36fbuzbee 3287eb3f7aaf43f07caf0de05ba4ae59e8ea6add796Ben Cheng /* 3297eb3f7aaf43f07caf0de05ba4ae59e8ea6add796Ben Cheng * If the invoke itself is selected for single stepping, don't bother 3307eb3f7aaf43f07caf0de05ba4ae59e8ea6add796Ben Cheng * to inline it. 3317eb3f7aaf43f07caf0de05ba4ae59e8ea6add796Ben Cheng */ 3329a1f81699cc05b58378ffb9aadb4e97677943791Dan Bornstein if (SINGLE_STEP_OP(opcode)) 3337eb3f7aaf43f07caf0de05ba4ae59e8ea6add796Ben Cheng continue; 3347eb3f7aaf43f07caf0de05ba4ae59e8ea6add796Ben Cheng 3357a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng const Method *calleeMethod; 3367a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 3379a1f81699cc05b58378ffb9aadb4e97677943791Dan Bornstein switch (opcode) { 3387a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng case OP_INVOKE_SUPER: 3397a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng case OP_INVOKE_DIRECT: 3407a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng case OP_INVOKE_STATIC: 3417a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng case OP_INVOKE_SUPER_QUICK: 3427a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng calleeMethod = lastMIRInsn->meta.callsiteInfo->method; 3437a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng break; 3447a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng case OP_INVOKE_SUPER_RANGE: 3457a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng case OP_INVOKE_DIRECT_RANGE: 3467a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng case OP_INVOKE_STATIC_RANGE: 3477a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng case OP_INVOKE_SUPER_QUICK_RANGE: 3487a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng isRange = true; 3497a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng calleeMethod = lastMIRInsn->meta.callsiteInfo->method; 3507a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng break; 3517a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng default: 3527a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng calleeMethod = NULL; 3537a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng break; 3547a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 3557a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 3567a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if (calleeMethod) { 357cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng bool inlined = tryInlineSingletonCallsite(cUnit, calleeMethod, 358cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng lastMIRInsn, bb, isRange); 359cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng if (!inlined && 360cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng !(gDvmJit.disableOpt & (1 << kMethodJit)) && 361cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng !dvmIsNativeMethod(calleeMethod)) { 362cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng CompilerMethodStats *methodStats = 363cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng dvmCompilerAnalyzeMethodBody(calleeMethod, true); 364cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng if ((methodStats->attributes & METHOD_IS_LEAF) && 365cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng !(methodStats->attributes & METHOD_CANNOT_COMPILE)) { 366cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng /* Callee has been previously compiled */ 367cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng if (dvmJitGetMethodAddr(calleeMethod->insns)) { 368cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng lastMIRInsn->OptimizationFlags |= MIR_INVOKE_METHOD_JIT; 369cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng } else { 370cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng /* Compile the callee first */ 371cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng dvmCompileMethod(calleeMethod, info); 372cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng if (dvmJitGetMethodAddr(calleeMethod->insns)) { 373cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng lastMIRInsn->OptimizationFlags |= 374cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng MIR_INVOKE_METHOD_JIT; 375cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng } else { 376cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng methodStats->attributes |= METHOD_CANNOT_COMPILE; 377cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng } 378cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng } 379cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng } 380cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng } 3817a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng return; 3827a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 3837a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 3849a1f81699cc05b58378ffb9aadb4e97677943791Dan Bornstein switch (opcode) { 3857a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng case OP_INVOKE_VIRTUAL: 3867a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng case OP_INVOKE_VIRTUAL_QUICK: 3877a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng case OP_INVOKE_INTERFACE: 3887a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng isRange = false; 3897a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng calleeMethod = lastMIRInsn->meta.callsiteInfo->method; 3907a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng break; 3917a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng case OP_INVOKE_VIRTUAL_RANGE: 3927a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng case OP_INVOKE_VIRTUAL_QUICK_RANGE: 3937a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng case OP_INVOKE_INTERFACE_RANGE: 3947a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng isRange = true; 3957a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng calleeMethod = lastMIRInsn->meta.callsiteInfo->method; 3967a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng break; 3977a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng default: 3987a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng break; 3997a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 4007a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng 4017a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng if (calleeMethod) { 402cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng bool inlined = tryInlineVirtualCallsite(cUnit, calleeMethod, 403cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng lastMIRInsn, bb, isRange); 404cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng if (!inlined && 405cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng !(gDvmJit.disableOpt & (1 << kMethodJit)) && 406cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng !dvmIsNativeMethod(calleeMethod)) { 407cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng CompilerMethodStats *methodStats = 408cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng dvmCompilerAnalyzeMethodBody(calleeMethod, true); 409cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng if ((methodStats->attributes & METHOD_IS_LEAF) && 410cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng !(methodStats->attributes & METHOD_CANNOT_COMPILE)) { 411cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng /* Callee has been previously compiled */ 412cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng if (dvmJitGetMethodAddr(calleeMethod->insns)) { 413cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng lastMIRInsn->OptimizationFlags |= MIR_INVOKE_METHOD_JIT; 414cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng } else { 415cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng /* Compile the callee first */ 416cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng dvmCompileMethod(calleeMethod, info); 417cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng if (dvmJitGetMethodAddr(calleeMethod->insns)) { 418cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng lastMIRInsn->OptimizationFlags |= 419cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng MIR_INVOKE_METHOD_JIT; 420cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng } else { 421cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng methodStats->attributes |= METHOD_CANNOT_COMPILE; 422cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng } 423cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng } 424cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng } 425cfdeca37fcaa27c37bad5077223e4d1e87f1182eBen Cheng } 4267a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng return; 4277a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 4287a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng } 4297a2697d327936e20ef5484f7819e2e4bf91c891fBen Cheng} 430