| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2008 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 | /* |
| 18 | * Perform some simple bytecode optimizations, chiefly "quickening" of |
| 19 | * opcodes. |
| 20 | */ |
| 21 | #include "Dalvik.h" |
| 22 | #include "libdex/InstrUtils.h" |
| 23 | |
| 24 | #include <zlib.h> |
| 25 | |
| 26 | #include <stdlib.h> |
| 27 | |
| 28 | /* |
| 29 | * Virtual/direct calls to "method" are replaced with an execute-inline |
| 30 | * instruction with index "idx". |
| 31 | */ |
| Andy McFadden | cb3c542 | 2010-04-07 15:56:16 -0700 | [diff] [blame] | 32 | struct InlineSub { |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 33 | Method* method; |
| 34 | int inlineIdx; |
| Andy McFadden | cb3c542 | 2010-04-07 15:56:16 -0700 | [diff] [blame] | 35 | }; |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 36 | |
| 37 | |
| 38 | /* fwd */ |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 39 | static void optimizeMethod(Method* method, bool essentialOnly); |
| 40 | static bool rewriteInstField(Method* method, u2* insns, OpCode quickOpc, |
| 41 | OpCode volatileOpc); |
| 42 | static bool rewriteStaticField(Method* method, u2* insns, OpCode volatileOpc); |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 43 | static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc); |
| 44 | static bool rewriteEmptyDirectInvoke(Method* method, u2* insns); |
| 45 | static bool rewriteExecuteInline(Method* method, u2* insns, |
| Andy McFadden | cb3c542 | 2010-04-07 15:56:16 -0700 | [diff] [blame] | 46 | MethodType methodType); |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 47 | static bool rewriteExecuteInlineRange(Method* method, u2* insns, |
| Andy McFadden | cb3c542 | 2010-04-07 15:56:16 -0700 | [diff] [blame] | 48 | MethodType methodType); |
| Andy McFadden | 3f4b63f | 2010-09-13 14:04:02 -0700 | [diff] [blame] | 49 | static void rewriteReturnVoid(Method* method, u2* insns); |
| 50 | static bool needsReturnBarrier(Method* method); |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 51 | |
| 52 | |
| 53 | /* |
| 54 | * Create a table of inline substitutions. |
| 55 | * |
| 56 | * TODO: this is currently just a linear array. We will want to put this |
| 57 | * into a hash table as the list size increases. |
| 58 | */ |
| Andy McFadden | cb3c542 | 2010-04-07 15:56:16 -0700 | [diff] [blame] | 59 | InlineSub* dvmCreateInlineSubsTable(void) |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 60 | { |
| 61 | const InlineOperation* ops = dvmGetInlineOpsTable(); |
| 62 | const int count = dvmGetInlineOpsTableLength(); |
| 63 | InlineSub* table; |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 64 | int i, tableIndex; |
| 65 | |
| 66 | /* |
| 67 | * Allocate for optimism: one slot per entry, plus an end-of-list marker. |
| 68 | */ |
| Elliott Hughes | fe70026 | 2010-09-14 17:42:07 -0700 | [diff] [blame] | 69 | table = calloc(count + 1, sizeof(InlineSub)); |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 70 | |
| 71 | tableIndex = 0; |
| 72 | for (i = 0; i < count; i++) { |
| Elliott Hughes | fe70026 | 2010-09-14 17:42:07 -0700 | [diff] [blame] | 73 | Method* method = dvmFindInlinableMethod(ops[i].classDescriptor, |
| 74 | ops[i].methodName, ops[i].methodSignature); |
| 75 | if (method != NULL) { |
| 76 | table[tableIndex].method = method; |
| 77 | table[tableIndex].inlineIdx = i; |
| 78 | tableIndex++; |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 79 | |
| Elliott Hughes | fe70026 | 2010-09-14 17:42:07 -0700 | [diff] [blame] | 80 | LOGV("DexOpt: will inline %d: %s.%s %s\n", i, |
| 81 | ops[i].classDescriptor, ops[i].methodName, |
| 82 | ops[i].methodSignature); |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 83 | } |
| 84 | } |
| 85 | |
| 86 | /* mark end of table */ |
| 87 | table[tableIndex].method = NULL; |
| 88 | LOGV("DexOpt: inline table has %d entries\n", tableIndex); |
| 89 | |
| 90 | return table; |
| 91 | } |
| 92 | |
| 93 | /* |
| Andy McFadden | cb3c542 | 2010-04-07 15:56:16 -0700 | [diff] [blame] | 94 | * Release inline sub data structure. |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 95 | */ |
| Andy McFadden | cb3c542 | 2010-04-07 15:56:16 -0700 | [diff] [blame] | 96 | void dvmFreeInlineSubsTable(InlineSub* inlineSubs) |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 97 | { |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 98 | free(inlineSubs); |
| 99 | } |
| 100 | |
| Andy McFadden | cb3c542 | 2010-04-07 15:56:16 -0700 | [diff] [blame] | 101 | |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 102 | /* |
| 103 | * Optimize the specified class. |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 104 | * |
| 105 | * If "essentialOnly" is true, we only do essential optimizations. For |
| 106 | * example, accesses to volatile 64-bit fields must be replaced with |
| 107 | * "-wide-volatile" instructions or the program could behave incorrectly. |
| 108 | * (Skipping non-essential optimizations makes us a little bit faster, and |
| 109 | * more importantly avoids dirtying DEX pages.) |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 110 | */ |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 111 | void dvmOptimizeClass(ClassObject* clazz, bool essentialOnly) |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 112 | { |
| 113 | int i; |
| 114 | |
| 115 | for (i = 0; i < clazz->directMethodCount; i++) { |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 116 | optimizeMethod(&clazz->directMethods[i], essentialOnly); |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 117 | } |
| 118 | for (i = 0; i < clazz->virtualMethodCount; i++) { |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 119 | optimizeMethod(&clazz->virtualMethods[i], essentialOnly); |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 120 | } |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 121 | } |
| 122 | |
| 123 | /* |
| 124 | * Optimize instructions in a method. |
| 125 | * |
| Andy McFadden | 228a6b0 | 2010-05-04 15:02:32 -0700 | [diff] [blame] | 126 | * This does a single pass through the code, examining each instruction. |
| 127 | * |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 128 | * This is not expected to fail if the class was successfully verified. |
| 129 | * The only significant failure modes occur when an "essential" update fails, |
| 130 | * but we can't generally identify those: if we can't look up a field, |
| 131 | * we can't know if the field access was supposed to be handled as volatile. |
| 132 | * |
| 133 | * Instead, we give it our best effort, and hope for the best. For 100% |
| 134 | * reliability, only optimize a class after verification succeeds. |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 135 | */ |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 136 | static void optimizeMethod(Method* method, bool essentialOnly) |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 137 | { |
| 138 | u4 insnsSize; |
| 139 | u2* insns; |
| 140 | u2 inst; |
| 141 | |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 142 | if (!gDvm.optimizing && !essentialOnly) { |
| 143 | /* unexpected; will force copy-on-write of a lot of pages */ |
| 144 | LOGD("NOTE: doing full bytecode optimization outside dexopt\n"); |
| 145 | } |
| 146 | |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 147 | if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method)) |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 148 | return; |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 149 | |
| Andy McFadden | 3f4b63f | 2010-09-13 14:04:02 -0700 | [diff] [blame] | 150 | /* compute this once per method */ |
| 151 | bool needRetBar = needsReturnBarrier(method); |
| 152 | |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 153 | insns = (u2*) method->insns; |
| 154 | assert(insns != NULL); |
| 155 | insnsSize = dvmGetMethodInsnsSize(method); |
| 156 | |
| 157 | while (insnsSize > 0) { |
| Andy McFadden | 139516e | 2010-07-07 14:09:09 -0700 | [diff] [blame] | 158 | OpCode quickOpc, volatileOpc = OP_NOP; |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 159 | int width; |
| Andy McFadden | 139516e | 2010-07-07 14:09:09 -0700 | [diff] [blame] | 160 | bool notMatched = false; |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 161 | |
| 162 | inst = *insns & 0xff; |
| 163 | |
| Andy McFadden | c58b9ef | 2010-09-09 12:54:43 -0700 | [diff] [blame] | 164 | /* "essential" substitutions, always checked */ |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 165 | switch (inst) { |
| 166 | case OP_IGET: |
| 167 | case OP_IGET_BOOLEAN: |
| 168 | case OP_IGET_BYTE: |
| 169 | case OP_IGET_CHAR: |
| 170 | case OP_IGET_SHORT: |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 171 | quickOpc = OP_IGET_QUICK; |
| Andy McFadden | c58b9ef | 2010-09-09 12:54:43 -0700 | [diff] [blame] | 172 | if (gDvm.dexOptForSmp) |
| Andy McFadden | 139516e | 2010-07-07 14:09:09 -0700 | [diff] [blame] | 173 | volatileOpc = OP_IGET_VOLATILE; |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 174 | goto rewrite_inst_field; |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 175 | case OP_IGET_WIDE: |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 176 | quickOpc = OP_IGET_WIDE_QUICK; |
| 177 | volatileOpc = OP_IGET_WIDE_VOLATILE; |
| 178 | goto rewrite_inst_field; |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 179 | case OP_IGET_OBJECT: |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 180 | quickOpc = OP_IGET_OBJECT_QUICK; |
| Andy McFadden | c58b9ef | 2010-09-09 12:54:43 -0700 | [diff] [blame] | 181 | if (gDvm.dexOptForSmp) |
| Andy McFadden | 139516e | 2010-07-07 14:09:09 -0700 | [diff] [blame] | 182 | volatileOpc = OP_IGET_OBJECT_VOLATILE; |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 183 | goto rewrite_inst_field; |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 184 | case OP_IPUT: |
| 185 | case OP_IPUT_BOOLEAN: |
| 186 | case OP_IPUT_BYTE: |
| 187 | case OP_IPUT_CHAR: |
| 188 | case OP_IPUT_SHORT: |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 189 | quickOpc = OP_IPUT_QUICK; |
| Andy McFadden | c58b9ef | 2010-09-09 12:54:43 -0700 | [diff] [blame] | 190 | if (gDvm.dexOptForSmp) |
| Andy McFadden | 139516e | 2010-07-07 14:09:09 -0700 | [diff] [blame] | 191 | volatileOpc = OP_IPUT_VOLATILE; |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 192 | goto rewrite_inst_field; |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 193 | case OP_IPUT_WIDE: |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 194 | quickOpc = OP_IPUT_WIDE_QUICK; |
| 195 | volatileOpc = OP_IPUT_WIDE_VOLATILE; |
| 196 | goto rewrite_inst_field; |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 197 | case OP_IPUT_OBJECT: |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 198 | quickOpc = OP_IPUT_OBJECT_QUICK; |
| Andy McFadden | c58b9ef | 2010-09-09 12:54:43 -0700 | [diff] [blame] | 199 | if (gDvm.dexOptForSmp) |
| Andy McFadden | 139516e | 2010-07-07 14:09:09 -0700 | [diff] [blame] | 200 | volatileOpc = OP_IPUT_OBJECT_VOLATILE; |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 201 | rewrite_inst_field: |
| 202 | if (essentialOnly) |
| 203 | quickOpc = OP_NOP; |
| Andy McFadden | 139516e | 2010-07-07 14:09:09 -0700 | [diff] [blame] | 204 | if (quickOpc != OP_NOP || volatileOpc != OP_NOP) |
| 205 | rewriteInstField(method, insns, quickOpc, volatileOpc); |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 206 | break; |
| 207 | |
| Andy McFadden | 139516e | 2010-07-07 14:09:09 -0700 | [diff] [blame] | 208 | case OP_SGET_WIDE: |
| 209 | volatileOpc = OP_SGET_WIDE_VOLATILE; |
| 210 | goto rewrite_static_field; |
| 211 | case OP_SPUT_WIDE: |
| 212 | volatileOpc = OP_SPUT_WIDE_VOLATILE; |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 213 | rewrite_static_field: |
| 214 | rewriteStaticField(method, insns, volatileOpc); |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 215 | break; |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 216 | default: |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 217 | notMatched = true; |
| Andy McFadden | c58b9ef | 2010-09-09 12:54:43 -0700 | [diff] [blame] | 218 | break; |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 219 | } |
| 220 | |
| Andy McFadden | c58b9ef | 2010-09-09 12:54:43 -0700 | [diff] [blame] | 221 | if (notMatched && gDvm.dexOptForSmp) { |
| 222 | /* additional "essential" substitutions for an SMP device */ |
| 223 | switch (inst) { |
| 224 | case OP_SGET: |
| 225 | case OP_SGET_BOOLEAN: |
| 226 | case OP_SGET_BYTE: |
| 227 | case OP_SGET_CHAR: |
| 228 | case OP_SGET_SHORT: |
| 229 | volatileOpc = OP_SGET_VOLATILE; |
| 230 | goto rewrite_static_field2; |
| 231 | case OP_SGET_OBJECT: |
| 232 | volatileOpc = OP_SGET_OBJECT_VOLATILE; |
| 233 | goto rewrite_static_field2; |
| 234 | case OP_SPUT: |
| 235 | case OP_SPUT_BOOLEAN: |
| 236 | case OP_SPUT_BYTE: |
| 237 | case OP_SPUT_CHAR: |
| 238 | case OP_SPUT_SHORT: |
| 239 | volatileOpc = OP_SPUT_VOLATILE; |
| 240 | goto rewrite_static_field2; |
| 241 | case OP_SPUT_OBJECT: |
| 242 | volatileOpc = OP_SPUT_OBJECT_VOLATILE; |
| 243 | rewrite_static_field2: |
| 244 | rewriteStaticField(method, insns, volatileOpc); |
| 245 | notMatched = false; |
| 246 | break; |
| Andy McFadden | 3f4b63f | 2010-09-13 14:04:02 -0700 | [diff] [blame] | 247 | case OP_RETURN_VOID: |
| 248 | if (needRetBar) |
| 249 | rewriteReturnVoid(method, insns); |
| 250 | notMatched = false; |
| 251 | break; |
| Andy McFadden | c58b9ef | 2010-09-09 12:54:43 -0700 | [diff] [blame] | 252 | default: |
| 253 | assert(notMatched); |
| 254 | break; |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | /* non-essential substitutions */ |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 259 | if (notMatched && !essentialOnly) { |
| 260 | switch (inst) { |
| 261 | case OP_INVOKE_VIRTUAL: |
| 262 | if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL)) { |
| 263 | rewriteVirtualInvoke(method, insns, |
| 264 | OP_INVOKE_VIRTUAL_QUICK); |
| 265 | } |
| 266 | break; |
| 267 | case OP_INVOKE_VIRTUAL_RANGE: |
| 268 | if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL)) { |
| 269 | rewriteVirtualInvoke(method, insns, |
| 270 | OP_INVOKE_VIRTUAL_QUICK_RANGE); |
| 271 | } |
| 272 | break; |
| 273 | case OP_INVOKE_SUPER: |
| 274 | rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK); |
| 275 | break; |
| 276 | case OP_INVOKE_SUPER_RANGE: |
| 277 | rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE); |
| 278 | break; |
| 279 | |
| 280 | case OP_INVOKE_DIRECT: |
| 281 | if (!rewriteExecuteInline(method, insns, METHOD_DIRECT)) { |
| 282 | rewriteEmptyDirectInvoke(method, insns); |
| 283 | } |
| 284 | break; |
| 285 | case OP_INVOKE_DIRECT_RANGE: |
| 286 | rewriteExecuteInlineRange(method, insns, METHOD_DIRECT); |
| 287 | break; |
| 288 | |
| 289 | case OP_INVOKE_STATIC: |
| 290 | rewriteExecuteInline(method, insns, METHOD_STATIC); |
| 291 | break; |
| 292 | case OP_INVOKE_STATIC_RANGE: |
| 293 | rewriteExecuteInlineRange(method, insns, METHOD_STATIC); |
| 294 | break; |
| 295 | |
| 296 | default: |
| 297 | /* nothing to do for this instruction */ |
| 298 | ; |
| 299 | } |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 300 | } |
| 301 | |
| Dan Bornstein | 5432239 | 2010-11-17 14:16:56 -0800 | [diff] [blame] | 302 | width = dexGetInstrOrTableWidth(insns); |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 303 | assert(width > 0); |
| 304 | |
| 305 | insns += width; |
| 306 | insnsSize -= width; |
| 307 | } |
| 308 | |
| 309 | assert(insnsSize == 0); |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 310 | } |
| 311 | |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 312 | /* |
| 313 | * Update a 16-bit code unit in "meth". |
| 314 | */ |
| Andy McFadden | 3f4b63f | 2010-09-13 14:04:02 -0700 | [diff] [blame] | 315 | static inline void updateCodeUnit(const Method* meth, u2* ptr, u2 newVal) |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 316 | { |
| 317 | if (gDvm.optimizing) { |
| 318 | /* dexopt time, alter the output directly */ |
| 319 | *ptr = newVal; |
| 320 | } else { |
| 321 | /* runtime, toggle the page read/write status */ |
| 322 | dvmDexChangeDex2(meth->clazz->pDvmDex, ptr, newVal); |
| 323 | } |
| 324 | } |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 325 | |
| 326 | /* |
| Andy McFadden | 3f4b63f | 2010-09-13 14:04:02 -0700 | [diff] [blame] | 327 | * Update the 8-bit opcode portion of a 16-bit code unit in "meth". |
| 328 | */ |
| 329 | static inline void updateOpCode(const Method* meth, u2* ptr, OpCode opCode) |
| 330 | { |
| 331 | updateCodeUnit(meth, ptr, (ptr[0] & 0xff00) | (u2) opCode); |
| 332 | } |
| 333 | |
| 334 | /* |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 335 | * If "referrer" and "resClass" don't come from the same DEX file, and |
| 336 | * the DEX we're working on is not destined for the bootstrap class path, |
| 337 | * tweak the class loader so package-access checks work correctly. |
| 338 | * |
| 339 | * Only do this if we're doing pre-verification or optimization. |
| 340 | */ |
| 341 | static void tweakLoader(ClassObject* referrer, ClassObject* resClass) |
| 342 | { |
| 343 | if (!gDvm.optimizing) |
| 344 | return; |
| 345 | assert(referrer->classLoader == NULL); |
| 346 | assert(resClass->classLoader == NULL); |
| 347 | |
| 348 | if (!gDvm.optimizingBootstrapClass) { |
| 349 | /* class loader for an array class comes from element type */ |
| 350 | if (dvmIsArrayClass(resClass)) |
| 351 | resClass = resClass->elementClass; |
| 352 | if (referrer->pDvmDex != resClass->pDvmDex) |
| 353 | resClass->classLoader = (Object*) 0xdead3333; |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | /* |
| 358 | * Undo the effects of tweakLoader. |
| 359 | */ |
| 360 | static void untweakLoader(ClassObject* referrer, ClassObject* resClass) |
| 361 | { |
| 362 | if (!gDvm.optimizing || gDvm.optimizingBootstrapClass) |
| 363 | return; |
| 364 | |
| 365 | if (dvmIsArrayClass(resClass)) |
| 366 | resClass = resClass->elementClass; |
| 367 | resClass->classLoader = NULL; |
| 368 | } |
| 369 | |
| 370 | |
| 371 | /* |
| 372 | * Alternate version of dvmResolveClass for use with verification and |
| 373 | * optimization. Performs access checks on every resolve, and refuses |
| 374 | * to acknowledge the existence of classes defined in more than one DEX |
| 375 | * file. |
| 376 | * |
| 377 | * Exceptions caused by failures are cleared before returning. |
| 378 | * |
| 379 | * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. |
| 380 | */ |
| 381 | ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx, |
| 382 | VerifyError* pFailure) |
| 383 | { |
| 384 | DvmDex* pDvmDex = referrer->pDvmDex; |
| 385 | ClassObject* resClass; |
| 386 | |
| 387 | /* |
| 388 | * Check the table first. If not there, do the lookup by name. |
| 389 | */ |
| 390 | resClass = dvmDexGetResolvedClass(pDvmDex, classIdx); |
| 391 | if (resClass == NULL) { |
| 392 | const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx); |
| 393 | if (className[0] != '\0' && className[1] == '\0') { |
| 394 | /* primitive type */ |
| 395 | resClass = dvmFindPrimitiveClass(className[0]); |
| 396 | } else { |
| 397 | resClass = dvmFindClassNoInit(className, referrer->classLoader); |
| 398 | } |
| 399 | if (resClass == NULL) { |
| 400 | /* not found, exception should be raised */ |
| 401 | LOGV("DexOpt: class %d (%s) not found\n", |
| 402 | classIdx, |
| 403 | dexStringByTypeIdx(pDvmDex->pDexFile, classIdx)); |
| 404 | if (pFailure != NULL) { |
| 405 | /* dig through the wrappers to find the original failure */ |
| 406 | Object* excep = dvmGetException(dvmThreadSelf()); |
| 407 | while (true) { |
| 408 | Object* cause = dvmGetExceptionCause(excep); |
| 409 | if (cause == NULL) |
| 410 | break; |
| 411 | excep = cause; |
| 412 | } |
| 413 | if (strcmp(excep->clazz->descriptor, |
| 414 | "Ljava/lang/IncompatibleClassChangeError;") == 0) |
| 415 | { |
| 416 | *pFailure = VERIFY_ERROR_CLASS_CHANGE; |
| 417 | } else { |
| 418 | *pFailure = VERIFY_ERROR_NO_CLASS; |
| 419 | } |
| 420 | } |
| 421 | dvmClearOptException(dvmThreadSelf()); |
| 422 | return NULL; |
| 423 | } |
| 424 | |
| 425 | /* |
| 426 | * Add it to the resolved table so we're faster on the next lookup. |
| 427 | */ |
| 428 | dvmDexSetResolvedClass(pDvmDex, classIdx, resClass); |
| 429 | } |
| 430 | |
| 431 | /* multiple definitions? */ |
| 432 | if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) { |
| 433 | LOGI("DexOpt: not resolving ambiguous class '%s'\n", |
| 434 | resClass->descriptor); |
| 435 | if (pFailure != NULL) |
| 436 | *pFailure = VERIFY_ERROR_NO_CLASS; |
| 437 | return NULL; |
| 438 | } |
| 439 | |
| 440 | /* access allowed? */ |
| 441 | tweakLoader(referrer, resClass); |
| 442 | bool allowed = dvmCheckClassAccess(referrer, resClass); |
| 443 | untweakLoader(referrer, resClass); |
| 444 | if (!allowed) { |
| 445 | LOGW("DexOpt: resolve class illegal access: %s -> %s\n", |
| 446 | referrer->descriptor, resClass->descriptor); |
| 447 | if (pFailure != NULL) |
| 448 | *pFailure = VERIFY_ERROR_ACCESS_CLASS; |
| 449 | return NULL; |
| 450 | } |
| 451 | |
| 452 | return resClass; |
| 453 | } |
| 454 | |
| 455 | /* |
| 456 | * Alternate version of dvmResolveInstField(). |
| 457 | * |
| 458 | * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. |
| 459 | */ |
| 460 | InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx, |
| 461 | VerifyError* pFailure) |
| 462 | { |
| 463 | DvmDex* pDvmDex = referrer->pDvmDex; |
| 464 | InstField* resField; |
| 465 | |
| 466 | resField = (InstField*) dvmDexGetResolvedField(pDvmDex, ifieldIdx); |
| 467 | if (resField == NULL) { |
| 468 | const DexFieldId* pFieldId; |
| 469 | ClassObject* resClass; |
| 470 | |
| 471 | pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx); |
| 472 | |
| 473 | /* |
| 474 | * Find the field's class. |
| 475 | */ |
| 476 | resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure); |
| 477 | if (resClass == NULL) { |
| 478 | //dvmClearOptException(dvmThreadSelf()); |
| 479 | assert(!dvmCheckException(dvmThreadSelf())); |
| 480 | if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); } |
| 481 | return NULL; |
| 482 | } |
| 483 | |
| 484 | resField = (InstField*)dvmFindFieldHier(resClass, |
| 485 | dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx), |
| 486 | dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); |
| 487 | if (resField == NULL) { |
| 488 | LOGD("DexOpt: couldn't find field %s.%s\n", |
| 489 | resClass->descriptor, |
| 490 | dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); |
| 491 | if (pFailure != NULL) |
| 492 | *pFailure = VERIFY_ERROR_NO_FIELD; |
| 493 | return NULL; |
| 494 | } |
| 495 | if (dvmIsStaticField(&resField->field)) { |
| 496 | LOGD("DexOpt: wanted instance, got static for field %s.%s\n", |
| 497 | resClass->descriptor, |
| 498 | dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); |
| 499 | if (pFailure != NULL) |
| 500 | *pFailure = VERIFY_ERROR_CLASS_CHANGE; |
| 501 | return NULL; |
| 502 | } |
| 503 | |
| 504 | /* |
| 505 | * Add it to the resolved table so we're faster on the next lookup. |
| 506 | */ |
| 507 | dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*) resField); |
| 508 | } |
| 509 | |
| 510 | /* access allowed? */ |
| 511 | tweakLoader(referrer, resField->field.clazz); |
| 512 | bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField); |
| 513 | untweakLoader(referrer, resField->field.clazz); |
| 514 | if (!allowed) { |
| 515 | LOGI("DexOpt: access denied from %s to field %s.%s\n", |
| 516 | referrer->descriptor, resField->field.clazz->descriptor, |
| 517 | resField->field.name); |
| 518 | if (pFailure != NULL) |
| 519 | *pFailure = VERIFY_ERROR_ACCESS_FIELD; |
| 520 | return NULL; |
| 521 | } |
| 522 | |
| 523 | return resField; |
| 524 | } |
| 525 | |
| 526 | /* |
| 527 | * Alternate version of dvmResolveStaticField(). |
| 528 | * |
| 529 | * Does not force initialization of the resolved field's class. |
| 530 | * |
| 531 | * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. |
| 532 | */ |
| 533 | StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx, |
| 534 | VerifyError* pFailure) |
| 535 | { |
| 536 | DvmDex* pDvmDex = referrer->pDvmDex; |
| 537 | StaticField* resField; |
| 538 | |
| 539 | resField = (StaticField*)dvmDexGetResolvedField(pDvmDex, sfieldIdx); |
| 540 | if (resField == NULL) { |
| 541 | const DexFieldId* pFieldId; |
| 542 | ClassObject* resClass; |
| 543 | |
| 544 | pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx); |
| 545 | |
| 546 | /* |
| 547 | * Find the field's class. |
| 548 | */ |
| 549 | resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure); |
| 550 | if (resClass == NULL) { |
| 551 | //dvmClearOptException(dvmThreadSelf()); |
| 552 | assert(!dvmCheckException(dvmThreadSelf())); |
| 553 | if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); } |
| 554 | return NULL; |
| 555 | } |
| 556 | |
| 557 | resField = (StaticField*)dvmFindFieldHier(resClass, |
| 558 | dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx), |
| 559 | dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); |
| 560 | if (resField == NULL) { |
| 561 | LOGD("DexOpt: couldn't find static field\n"); |
| 562 | if (pFailure != NULL) |
| 563 | *pFailure = VERIFY_ERROR_NO_FIELD; |
| 564 | return NULL; |
| 565 | } |
| 566 | if (!dvmIsStaticField(&resField->field)) { |
| 567 | LOGD("DexOpt: wanted static, got instance for field %s.%s\n", |
| 568 | resClass->descriptor, |
| 569 | dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); |
| 570 | if (pFailure != NULL) |
| 571 | *pFailure = VERIFY_ERROR_CLASS_CHANGE; |
| 572 | return NULL; |
| 573 | } |
| 574 | |
| 575 | /* |
| 576 | * Add it to the resolved table so we're faster on the next lookup. |
| 577 | * |
| 578 | * We can only do this if we're in "dexopt", because the presence |
| 579 | * of a valid value in the resolution table implies that the class |
| 580 | * containing the static field has been initialized. |
| 581 | */ |
| 582 | if (gDvm.optimizing) |
| 583 | dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField); |
| 584 | } |
| 585 | |
| 586 | /* access allowed? */ |
| 587 | tweakLoader(referrer, resField->field.clazz); |
| 588 | bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField); |
| 589 | untweakLoader(referrer, resField->field.clazz); |
| 590 | if (!allowed) { |
| 591 | LOGI("DexOpt: access denied from %s to field %s.%s\n", |
| 592 | referrer->descriptor, resField->field.clazz->descriptor, |
| 593 | resField->field.name); |
| 594 | if (pFailure != NULL) |
| 595 | *pFailure = VERIFY_ERROR_ACCESS_FIELD; |
| 596 | return NULL; |
| 597 | } |
| 598 | |
| 599 | return resField; |
| 600 | } |
| 601 | |
| 602 | |
| 603 | /* |
| 604 | * Rewrite an iget/iput instruction. These all have the form: |
| 605 | * op vA, vB, field@CCCC |
| 606 | * |
| 607 | * Where vA holds the value, vB holds the object reference, and CCCC is |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 608 | * the field reference constant pool offset. For a non-volatile field, |
| 609 | * we want to replace the opcode with "quickOpc" and replace CCCC with |
| 610 | * the byte offset from the start of the object. For a volatile field, |
| 611 | * we just want to replace the opcode with "volatileOpc". |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 612 | * |
| Andy McFadden | 139516e | 2010-07-07 14:09:09 -0700 | [diff] [blame] | 613 | * If "volatileOpc" is OP_NOP we don't check to see if it's a volatile |
| 614 | * field. If "quickOpc" is OP_NOP, and this is a non-volatile field, |
| 615 | * we don't do anything. |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 616 | * |
| 617 | * "method" is the referring method. |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 618 | */ |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 619 | static bool rewriteInstField(Method* method, u2* insns, OpCode quickOpc, |
| 620 | OpCode volatileOpc) |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 621 | { |
| 622 | ClassObject* clazz = method->clazz; |
| 623 | u2 fieldIdx = insns[1]; |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 624 | InstField* instField; |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 625 | |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 626 | instField = dvmOptResolveInstField(clazz, fieldIdx, NULL); |
| 627 | if (instField == NULL) { |
| 628 | LOGI("DexOpt: unable to optimize instance field ref " |
| 629 | "0x%04x at 0x%02x in %s.%s\n", |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 630 | fieldIdx, (int) (insns - method->insns), clazz->descriptor, |
| 631 | method->name); |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 632 | return false; |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 633 | } |
| 634 | |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 635 | if (instField->byteOffset >= 65536) { |
| 636 | LOGI("DexOpt: field offset exceeds 64K (%d)\n", instField->byteOffset); |
| 637 | return false; |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 638 | } |
| 639 | |
| Andy McFadden | 139516e | 2010-07-07 14:09:09 -0700 | [diff] [blame] | 640 | if (volatileOpc != OP_NOP && dvmIsVolatileField(&instField->field)) { |
| Andy McFadden | 3f4b63f | 2010-09-13 14:04:02 -0700 | [diff] [blame] | 641 | updateOpCode(method, insns, volatileOpc); |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 642 | LOGV("DexOpt: rewrote ifield access %s.%s --> volatile\n", |
| 643 | instField->field.clazz->descriptor, instField->field.name); |
| 644 | } else if (quickOpc != OP_NOP) { |
| Andy McFadden | 3f4b63f | 2010-09-13 14:04:02 -0700 | [diff] [blame] | 645 | updateOpCode(method, insns, quickOpc); |
| 646 | updateCodeUnit(method, insns+1, (u2) instField->byteOffset); |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 647 | LOGV("DexOpt: rewrote ifield access %s.%s --> %d\n", |
| 648 | instField->field.clazz->descriptor, instField->field.name, |
| 649 | instField->byteOffset); |
| 650 | } else { |
| 651 | LOGV("DexOpt: no rewrite of ifield access %s.%s\n", |
| 652 | instField->field.clazz->descriptor, instField->field.name); |
| 653 | } |
| 654 | |
| 655 | return true; |
| 656 | } |
| 657 | |
| 658 | /* |
| 659 | * Rewrite an sget/sput instruction. These all have the form: |
| 660 | * op vAA, field@BBBB |
| 661 | * |
| 662 | * Where vAA holds the value, and BBBB is the field reference constant |
| 663 | * pool offset. There is no "quick" form of static field accesses, so |
| 664 | * this is only useful for volatile fields. |
| 665 | * |
| 666 | * "method" is the referring method. |
| 667 | */ |
| 668 | static bool rewriteStaticField(Method* method, u2* insns, OpCode volatileOpc) |
| 669 | { |
| 670 | ClassObject* clazz = method->clazz; |
| 671 | u2 fieldIdx = insns[1]; |
| 672 | StaticField* staticField; |
| 673 | |
| Andy McFadden | 139516e | 2010-07-07 14:09:09 -0700 | [diff] [blame] | 674 | assert(volatileOpc != OP_NOP); |
| 675 | |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 676 | staticField = dvmOptResolveStaticField(clazz, fieldIdx, NULL); |
| 677 | if (staticField == NULL) { |
| 678 | LOGI("DexOpt: unable to optimize static field ref " |
| 679 | "0x%04x at 0x%02x in %s.%s\n", |
| 680 | fieldIdx, (int) (insns - method->insns), clazz->descriptor, |
| 681 | method->name); |
| 682 | return false; |
| 683 | } |
| 684 | |
| 685 | if (dvmIsVolatileField(&staticField->field)) { |
| Andy McFadden | 3f4b63f | 2010-09-13 14:04:02 -0700 | [diff] [blame] | 686 | updateOpCode(method, insns, volatileOpc); |
| Andy McFadden | fb119e6 | 2010-06-28 16:21:20 -0700 | [diff] [blame] | 687 | LOGV("DexOpt: rewrote sfield access %s.%s --> volatile\n", |
| 688 | staticField->field.clazz->descriptor, staticField->field.name); |
| 689 | } |
| 690 | |
| 691 | return true; |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 692 | } |
| 693 | |
| 694 | /* |
| 695 | * Alternate version of dvmResolveMethod(). |
| 696 | * |
| 697 | * Doesn't throw exceptions, and checks access on every lookup. |
| 698 | * |
| 699 | * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. |
| 700 | */ |
| 701 | Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx, |
| 702 | MethodType methodType, VerifyError* pFailure) |
| 703 | { |
| 704 | DvmDex* pDvmDex = referrer->pDvmDex; |
| 705 | Method* resMethod; |
| 706 | |
| 707 | assert(methodType == METHOD_DIRECT || |
| 708 | methodType == METHOD_VIRTUAL || |
| 709 | methodType == METHOD_STATIC); |
| 710 | |
| 711 | LOGVV("--- resolving method %u (referrer=%s)\n", methodIdx, |
| 712 | referrer->descriptor); |
| 713 | |
| 714 | resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx); |
| 715 | if (resMethod == NULL) { |
| 716 | const DexMethodId* pMethodId; |
| 717 | ClassObject* resClass; |
| 718 | |
| 719 | pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx); |
| 720 | |
| 721 | resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, pFailure); |
| 722 | if (resClass == NULL) { |
| 723 | /* |
| 724 | * Can't find the class that the method is a part of, or don't |
| 725 | * have permission to access the class. |
| 726 | */ |
| 727 | LOGV("DexOpt: can't find called method's class (?.%s)\n", |
| 728 | dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx)); |
| 729 | if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); } |
| 730 | return NULL; |
| 731 | } |
| 732 | if (dvmIsInterfaceClass(resClass)) { |
| 733 | /* method is part of an interface; this is wrong method for that */ |
| 734 | LOGW("DexOpt: method is in an interface\n"); |
| 735 | if (pFailure != NULL) |
| 736 | *pFailure = VERIFY_ERROR_GENERIC; |
| 737 | return NULL; |
| 738 | } |
| 739 | |
| 740 | /* |
| 741 | * We need to chase up the class hierarchy to find methods defined |
| 742 | * in super-classes. (We only want to check the current class |
| 743 | * if we're looking for a constructor.) |
| 744 | */ |
| 745 | DexProto proto; |
| 746 | dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId); |
| 747 | |
| 748 | if (methodType == METHOD_DIRECT) { |
| 749 | resMethod = dvmFindDirectMethod(resClass, |
| 750 | dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto); |
| 751 | } else { |
| 752 | /* METHOD_STATIC or METHOD_VIRTUAL */ |
| 753 | resMethod = dvmFindMethodHier(resClass, |
| 754 | dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto); |
| 755 | } |
| 756 | |
| 757 | if (resMethod == NULL) { |
| 758 | LOGV("DexOpt: couldn't find method '%s'\n", |
| 759 | dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx)); |
| 760 | if (pFailure != NULL) |
| 761 | *pFailure = VERIFY_ERROR_NO_METHOD; |
| 762 | return NULL; |
| 763 | } |
| 764 | if (methodType == METHOD_STATIC) { |
| 765 | if (!dvmIsStaticMethod(resMethod)) { |
| 766 | LOGD("DexOpt: wanted static, got instance for method %s.%s\n", |
| 767 | resClass->descriptor, resMethod->name); |
| 768 | if (pFailure != NULL) |
| 769 | *pFailure = VERIFY_ERROR_CLASS_CHANGE; |
| 770 | return NULL; |
| 771 | } |
| 772 | } else if (methodType == METHOD_VIRTUAL) { |
| 773 | if (dvmIsStaticMethod(resMethod)) { |
| 774 | LOGD("DexOpt: wanted instance, got static for method %s.%s\n", |
| 775 | resClass->descriptor, resMethod->name); |
| 776 | if (pFailure != NULL) |
| 777 | *pFailure = VERIFY_ERROR_CLASS_CHANGE; |
| 778 | return NULL; |
| 779 | } |
| 780 | } |
| 781 | |
| 782 | /* see if this is a pure-abstract method */ |
| 783 | if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) { |
| 784 | LOGW("DexOpt: pure-abstract method '%s' in %s\n", |
| 785 | dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), |
| 786 | resClass->descriptor); |
| 787 | if (pFailure != NULL) |
| 788 | *pFailure = VERIFY_ERROR_GENERIC; |
| 789 | return NULL; |
| 790 | } |
| 791 | |
| 792 | /* |
| 793 | * Add it to the resolved table so we're faster on the next lookup. |
| 794 | * |
| 795 | * We can only do this for static methods if we're not in "dexopt", |
| 796 | * because the presence of a valid value in the resolution table |
| 797 | * implies that the class containing the static field has been |
| 798 | * initialized. |
| 799 | */ |
| 800 | if (methodType != METHOD_STATIC || gDvm.optimizing) |
| 801 | dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod); |
| 802 | } |
| 803 | |
| 804 | LOGVV("--- found method %d (%s.%s)\n", |
| 805 | methodIdx, resMethod->clazz->descriptor, resMethod->name); |
| 806 | |
| 807 | /* access allowed? */ |
| 808 | tweakLoader(referrer, resMethod->clazz); |
| 809 | bool allowed = dvmCheckMethodAccess(referrer, resMethod); |
| 810 | untweakLoader(referrer, resMethod->clazz); |
| 811 | if (!allowed) { |
| 812 | IF_LOGI() { |
| 813 | char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype); |
| 814 | LOGI("DexOpt: illegal method access (call %s.%s %s from %s)\n", |
| 815 | resMethod->clazz->descriptor, resMethod->name, desc, |
| 816 | referrer->descriptor); |
| 817 | free(desc); |
| 818 | } |
| 819 | if (pFailure != NULL) |
| 820 | *pFailure = VERIFY_ERROR_ACCESS_METHOD; |
| 821 | return NULL; |
| 822 | } |
| 823 | |
| 824 | return resMethod; |
| 825 | } |
| 826 | |
| 827 | /* |
| 828 | * Rewrite invoke-virtual, invoke-virtual/range, invoke-super, and |
| 829 | * invoke-super/range. These all have the form: |
| 830 | * op vAA, meth@BBBB, reg stuff @CCCC |
| 831 | * |
| 832 | * We want to replace the method constant pool index BBBB with the |
| 833 | * vtable index. |
| 834 | */ |
| 835 | static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc) |
| 836 | { |
| 837 | ClassObject* clazz = method->clazz; |
| 838 | Method* baseMethod; |
| 839 | u2 methodIdx = insns[1]; |
| 840 | |
| 841 | baseMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_VIRTUAL, NULL); |
| 842 | if (baseMethod == NULL) { |
| 843 | LOGD("DexOpt: unable to optimize virt call 0x%04x at 0x%02x in %s.%s\n", |
| 844 | methodIdx, |
| 845 | (int) (insns - method->insns), clazz->descriptor, |
| 846 | method->name); |
| 847 | return false; |
| 848 | } |
| 849 | |
| 850 | assert((insns[0] & 0xff) == OP_INVOKE_VIRTUAL || |
| 851 | (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE || |
| 852 | (insns[0] & 0xff) == OP_INVOKE_SUPER || |
| 853 | (insns[0] & 0xff) == OP_INVOKE_SUPER_RANGE); |
| 854 | |
| 855 | /* |
| 856 | * Note: Method->methodIndex is a u2 and is range checked during the |
| 857 | * initial load. |
| 858 | */ |
| Andy McFadden | 3f4b63f | 2010-09-13 14:04:02 -0700 | [diff] [blame] | 859 | updateOpCode(method, insns, newOpc); |
| 860 | updateCodeUnit(method, insns+1, baseMethod->methodIndex); |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 861 | |
| 862 | //LOGI("DexOpt: rewrote call to %s.%s --> %s.%s\n", |
| 863 | // method->clazz->descriptor, method->name, |
| 864 | // baseMethod->clazz->descriptor, baseMethod->name); |
| 865 | |
| 866 | return true; |
| 867 | } |
| 868 | |
| 869 | /* |
| 870 | * Rewrite invoke-direct, which has the form: |
| 871 | * op vAA, meth@BBBB, reg stuff @CCCC |
| 872 | * |
| 873 | * There isn't a lot we can do to make this faster, but in some situations |
| 874 | * we can make it go away entirely. |
| 875 | * |
| 876 | * This must only be used when the invoked method does nothing and has |
| 877 | * no return value (the latter being very important for verification). |
| 878 | */ |
| 879 | static bool rewriteEmptyDirectInvoke(Method* method, u2* insns) |
| 880 | { |
| 881 | ClassObject* clazz = method->clazz; |
| 882 | Method* calledMethod; |
| 883 | u2 methodIdx = insns[1]; |
| 884 | |
| 885 | calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL); |
| 886 | if (calledMethod == NULL) { |
| 887 | LOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s\n", |
| 888 | methodIdx, |
| 889 | (int) (insns - method->insns), clazz->descriptor, |
| 890 | method->name); |
| 891 | return false; |
| 892 | } |
| 893 | |
| 894 | /* TODO: verify that java.lang.Object() is actually empty! */ |
| 895 | if (calledMethod->clazz == gDvm.classJavaLangObject && |
| 896 | dvmCompareNameDescriptorAndMethod("<init>", "()V", calledMethod) == 0) |
| 897 | { |
| 898 | /* |
| 899 | * Replace with "empty" instruction. DO NOT disturb anything |
| 900 | * else about it, as we want it to function the same as |
| 901 | * OP_INVOKE_DIRECT when debugging is enabled. |
| 902 | */ |
| 903 | assert((insns[0] & 0xff) == OP_INVOKE_DIRECT); |
| Andy McFadden | 3f4b63f | 2010-09-13 14:04:02 -0700 | [diff] [blame] | 904 | updateOpCode(method, insns, OP_INVOKE_DIRECT_EMPTY); |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 905 | |
| 906 | //LOGI("DexOpt: marked-empty call to %s.%s --> %s.%s\n", |
| 907 | // method->clazz->descriptor, method->name, |
| 908 | // calledMethod->clazz->descriptor, calledMethod->name); |
| 909 | } |
| 910 | |
| 911 | return true; |
| 912 | } |
| 913 | |
| 914 | /* |
| 915 | * Resolve an interface method reference. |
| 916 | * |
| 917 | * No method access check here -- interface methods are always public. |
| 918 | * |
| 919 | * Returns NULL if the method was not found. Does not throw an exception. |
| 920 | */ |
| 921 | Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx) |
| 922 | { |
| 923 | DvmDex* pDvmDex = referrer->pDvmDex; |
| 924 | Method* resMethod; |
| 925 | int i; |
| 926 | |
| 927 | LOGVV("--- resolving interface method %d (referrer=%s)\n", |
| 928 | methodIdx, referrer->descriptor); |
| 929 | |
| 930 | resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx); |
| 931 | if (resMethod == NULL) { |
| 932 | const DexMethodId* pMethodId; |
| 933 | ClassObject* resClass; |
| 934 | |
| 935 | pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx); |
| 936 | |
| 937 | resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, NULL); |
| 938 | if (resClass == NULL) { |
| 939 | /* can't find the class that the method is a part of */ |
| 940 | dvmClearOptException(dvmThreadSelf()); |
| 941 | return NULL; |
| 942 | } |
| 943 | if (!dvmIsInterfaceClass(resClass)) { |
| 944 | /* whoops */ |
| 945 | LOGI("Interface method not part of interface class\n"); |
| 946 | return NULL; |
| 947 | } |
| 948 | |
| 949 | const char* methodName = |
| 950 | dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx); |
| 951 | DexProto proto; |
| 952 | dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId); |
| 953 | |
| 954 | LOGVV("+++ looking for '%s' '%s' in resClass='%s'\n", |
| 955 | methodName, methodSig, resClass->descriptor); |
| 956 | resMethod = dvmFindVirtualMethod(resClass, methodName, &proto); |
| 957 | if (resMethod == NULL) { |
| 958 | /* scan superinterfaces and superclass interfaces */ |
| 959 | LOGVV("+++ did not resolve immediately\n"); |
| 960 | for (i = 0; i < resClass->iftableCount; i++) { |
| 961 | resMethod = dvmFindVirtualMethod(resClass->iftable[i].clazz, |
| 962 | methodName, &proto); |
| 963 | if (resMethod != NULL) |
| 964 | break; |
| 965 | } |
| 966 | |
| 967 | if (resMethod == NULL) { |
| 968 | LOGVV("+++ unable to resolve method %s\n", methodName); |
| 969 | return NULL; |
| 970 | } |
| 971 | } else { |
| 972 | LOGVV("+++ resolved immediately: %s (%s %d)\n", resMethod->name, |
| 973 | resMethod->clazz->descriptor, (u4) resMethod->methodIndex); |
| 974 | } |
| 975 | |
| 976 | /* we're expecting this to be abstract */ |
| 977 | if (!dvmIsAbstractMethod(resMethod)) { |
| 978 | char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype); |
| 979 | LOGW("Found non-abstract interface method %s.%s %s\n", |
| 980 | resMethod->clazz->descriptor, resMethod->name, desc); |
| 981 | free(desc); |
| 982 | return NULL; |
| 983 | } |
| 984 | |
| 985 | /* |
| 986 | * Add it to the resolved table so we're faster on the next lookup. |
| 987 | */ |
| 988 | dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod); |
| 989 | } |
| 990 | |
| 991 | LOGVV("--- found interface method %d (%s.%s)\n", |
| 992 | methodIdx, resMethod->clazz->descriptor, resMethod->name); |
| 993 | |
| 994 | /* interface methods are always public; no need to check access */ |
| 995 | |
| 996 | return resMethod; |
| 997 | } |
| 998 | |
| 999 | /* |
| 1000 | * See if the method being called can be rewritten as an inline operation. |
| 1001 | * Works for invoke-virtual, invoke-direct, and invoke-static. |
| 1002 | * |
| 1003 | * Returns "true" if we replace it. |
| 1004 | */ |
| 1005 | static bool rewriteExecuteInline(Method* method, u2* insns, |
| Andy McFadden | cb3c542 | 2010-04-07 15:56:16 -0700 | [diff] [blame] | 1006 | MethodType methodType) |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 1007 | { |
| Andy McFadden | cb3c542 | 2010-04-07 15:56:16 -0700 | [diff] [blame] | 1008 | const InlineSub* inlineSubs = gDvm.inlineSubs; |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 1009 | ClassObject* clazz = method->clazz; |
| 1010 | Method* calledMethod; |
| 1011 | u2 methodIdx = insns[1]; |
| 1012 | |
| 1013 | //return false; |
| 1014 | |
| 1015 | calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL); |
| 1016 | if (calledMethod == NULL) { |
| 1017 | LOGV("+++ DexOpt inline: can't find %d\n", methodIdx); |
| 1018 | return false; |
| 1019 | } |
| 1020 | |
| 1021 | while (inlineSubs->method != NULL) { |
| 1022 | /* |
| 1023 | if (extra) { |
| 1024 | LOGI("comparing %p vs %p %s.%s %s\n", |
| 1025 | inlineSubs->method, calledMethod, |
| 1026 | inlineSubs->method->clazz->descriptor, |
| 1027 | inlineSubs->method->name, |
| 1028 | inlineSubs->method->signature); |
| 1029 | } |
| 1030 | */ |
| 1031 | if (inlineSubs->method == calledMethod) { |
| 1032 | assert((insns[0] & 0xff) == OP_INVOKE_DIRECT || |
| 1033 | (insns[0] & 0xff) == OP_INVOKE_STATIC || |
| 1034 | (insns[0] & 0xff) == OP_INVOKE_VIRTUAL); |
| Andy McFadden | 3f4b63f | 2010-09-13 14:04:02 -0700 | [diff] [blame] | 1035 | updateOpCode(method, insns, OP_EXECUTE_INLINE); |
| 1036 | updateCodeUnit(method, insns+1, (u2) inlineSubs->inlineIdx); |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 1037 | |
| 1038 | //LOGI("DexOpt: execute-inline %s.%s --> %s.%s\n", |
| 1039 | // method->clazz->descriptor, method->name, |
| 1040 | // calledMethod->clazz->descriptor, calledMethod->name); |
| 1041 | return true; |
| 1042 | } |
| 1043 | |
| 1044 | inlineSubs++; |
| 1045 | } |
| 1046 | |
| 1047 | return false; |
| 1048 | } |
| 1049 | |
| 1050 | /* |
| 1051 | * See if the method being called can be rewritten as an inline operation. |
| 1052 | * Works for invoke-virtual/range, invoke-direct/range, and invoke-static/range. |
| 1053 | * |
| 1054 | * Returns "true" if we replace it. |
| 1055 | */ |
| 1056 | static bool rewriteExecuteInlineRange(Method* method, u2* insns, |
| Andy McFadden | cb3c542 | 2010-04-07 15:56:16 -0700 | [diff] [blame] | 1057 | MethodType methodType) |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 1058 | { |
| Andy McFadden | cb3c542 | 2010-04-07 15:56:16 -0700 | [diff] [blame] | 1059 | const InlineSub* inlineSubs = gDvm.inlineSubs; |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 1060 | ClassObject* clazz = method->clazz; |
| 1061 | Method* calledMethod; |
| 1062 | u2 methodIdx = insns[1]; |
| 1063 | |
| 1064 | calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL); |
| 1065 | if (calledMethod == NULL) { |
| 1066 | LOGV("+++ DexOpt inline/range: can't find %d\n", methodIdx); |
| 1067 | return false; |
| 1068 | } |
| 1069 | |
| 1070 | while (inlineSubs->method != NULL) { |
| 1071 | if (inlineSubs->method == calledMethod) { |
| 1072 | assert((insns[0] & 0xff) == OP_INVOKE_DIRECT_RANGE || |
| 1073 | (insns[0] & 0xff) == OP_INVOKE_STATIC_RANGE || |
| 1074 | (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE); |
| Andy McFadden | 3f4b63f | 2010-09-13 14:04:02 -0700 | [diff] [blame] | 1075 | updateOpCode(method, insns, OP_EXECUTE_INLINE_RANGE); |
| 1076 | updateCodeUnit(method, insns+1, (u2) inlineSubs->inlineIdx); |
| Andy McFadden | 2e1ee50 | 2010-03-24 13:25:53 -0700 | [diff] [blame] | 1077 | |
| 1078 | //LOGI("DexOpt: execute-inline/range %s.%s --> %s.%s\n", |
| 1079 | // method->clazz->descriptor, method->name, |
| 1080 | // calledMethod->clazz->descriptor, calledMethod->name); |
| 1081 | return true; |
| 1082 | } |
| 1083 | |
| 1084 | inlineSubs++; |
| 1085 | } |
| 1086 | |
| 1087 | return false; |
| 1088 | } |
| Andy McFadden | 3f4b63f | 2010-09-13 14:04:02 -0700 | [diff] [blame] | 1089 | |
| 1090 | /* |
| 1091 | * Returns "true" if the return-void instructions in this method should |
| 1092 | * be converted to return-void-barrier. |
| 1093 | * |
| 1094 | * This is needed to satisfy a Java Memory Model requirement regarding |
| 1095 | * the construction of objects with final fields. (This does not apply |
| 1096 | * to <clinit> or static fields, since appropriate barriers are guaranteed |
| 1097 | * by the class initialization process.) |
| 1098 | */ |
| 1099 | static bool needsReturnBarrier(Method* method) |
| 1100 | { |
| 1101 | if (!gDvm.dexOptForSmp) |
| 1102 | return false; |
| 1103 | if (strcmp(method->name, "<init>") != 0) |
| 1104 | return false; |
| 1105 | |
| 1106 | /* |
| 1107 | * Check to see if the class has any final fields. If not, we don't |
| 1108 | * need to generate a barrier instruction. |
| 1109 | */ |
| 1110 | const ClassObject* clazz = method->clazz; |
| 1111 | int idx = clazz->ifieldCount; |
| 1112 | while (--idx >= 0) { |
| 1113 | if (dvmIsFinalField(&clazz->ifields[idx].field)) |
| 1114 | break; |
| 1115 | } |
| 1116 | if (idx < 0) |
| 1117 | return false; |
| 1118 | |
| 1119 | /* |
| 1120 | * In theory, we only need to do this if the method actually modifies |
| 1121 | * a final field. In practice, non-constructor methods are allowed |
| 1122 | * to modify final fields by the VM, and there are tools that rely on |
| 1123 | * this behavior. (The compiler does not allow it.) |
| 1124 | * |
| 1125 | * If we alter the verifier to restrict final-field updates to |
| 1126 | * constructors, we can tighten this up as well. |
| 1127 | */ |
| 1128 | |
| 1129 | return true; |
| 1130 | } |
| 1131 | |
| 1132 | /* |
| 1133 | * Convert a return-void to a return-void-barrier. |
| 1134 | */ |
| 1135 | static void rewriteReturnVoid(Method* method, u2* insns) |
| 1136 | { |
| 1137 | assert((insns[0] & 0xff) == OP_RETURN_VOID); |
| 1138 | updateOpCode(method, insns, OP_RETURN_VOID_BARRIER); |
| 1139 | } |