blob: 49025a6f2813ba8b423350ababb0e046c1b9cc56 [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
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 * Inlined native functions. These definitions replace interpreted or
19 * native implementations at runtime; "intrinsic" might be a better word.
20 */
21#include "Dalvik.h"
22
23#ifdef HAVE__MEMCMP16
24/* hand-coded assembly implementation, available on some platforms */
25//#warning "trying memcmp16"
26//#define CHECK_MEMCMP16
27/* "count" is in 16-bit units */
28extern u4 __memcmp16(const u2* s0, const u2* s1, size_t count);
29#endif
30
31/*
32 * Some notes on "inline" functions.
33 *
34 * These are NOT simply native implementations. A full method definition
35 * must still be provided. Depending on the flags passed into the VM
36 * at runtime, the original or inline version may be selected by the
37 * DEX optimizer.
38 *
39 * PLEASE DO NOT use this as the default location for native methods.
40 * The difference between this and an "internal native" static method
41 * call on a 200MHz ARM 9 is roughly 370ns vs. 700ns. The code here
42 * "secretly replaces" the other method, so you can't avoid having two
43 * implementations. Since the DEX optimizer mode can't be known ahead
44 * of time, both implementations must be correct and complete.
45 *
46 * The only stuff that really needs to be here are methods that
47 * are high-volume or must be low-overhead, e.g. certain String/Math
48 * methods and some java.util.concurrent.atomic operations.
49 *
50 * Normally, a class is loaded and initialized the first time a static
51 * method is invoked. This property is NOT preserved here. If you need
52 * to access a static field in a class, you must ensure initialization
53 * yourself (cheap/easy way is to check the resolved-methods table, and
54 * resolve the method if it hasn't been).
55 *
56 * DO NOT replace "synchronized" methods. We do not support method
57 * synchronization here.
58 *
59 * Remember that these functions are executing while the thread is in
60 * the "RUNNING" state, not the "NATIVE" state. If you perform a blocking
61 * operation you can stall the entire VM if the GC or debugger wants to
62 * suspend the thread. Since these are arguably native implementations
63 * rather than VM internals, prefer NATIVE to VMWAIT if you want to change
64 * the thread state.
65 *
66 * Always write results to 32-bit or 64-bit fields in "pResult", e.g. do
67 * not write boolean results to pResult->z. The interpreter expects
68 * 32 or 64 bits to be set.
69 *
70 * Inline op methods return "false" if an exception was thrown, "true" if
71 * everything went well.
72 *
73 * DO NOT provide implementations of methods that can be overridden by a
74 * subclass, as polymorphism does not work correctly. For safety you should
75 * only provide inline functions for classes/methods declared "final".
76 *
77 * It's best to avoid inlining the overridden version of a method. For
78 * example, String.hashCode() is inherited from Object.hashCode(). Code
79 * calling String.hashCode() through an Object reference will run the
80 * "slow" version, while calling it through a String reference gets
81 * the inlined version. It's best to have just one version unless there
82 * are clear performance gains.
83 *
84 * Because the actual method is not called, debugger breakpoints on these
85 * methods will not happen. (TODO: have the code here find the original
86 * method and call it when the debugger is active.) Additional steps have
87 * been taken to allow method profiling to produce correct results.
88 */
89
90
91/*
92 * ===========================================================================
93 * org.apache.harmony.dalvik.NativeTestTarget
94 * ===========================================================================
95 */
96
97/*
98 * public static void emptyInlineMethod
99 *
100 * This exists only for benchmarks.
101 */
102static bool org_apache_harmony_dalvik_NativeTestTarget_emptyInlineMethod(
103 u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
104{
105 // do nothing
106 return true;
107}
108
109
110/*
111 * ===========================================================================
112 * java.lang.String
113 * ===========================================================================
114 */
115
116/*
117 * public char charAt(int index)
118 */
119static bool javaLangString_charAt(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
120 JValue* pResult)
121{
122 int count, offset;
123 ArrayObject* chars;
124
125 /* null reference check on "this" */
126 if (!dvmValidateObject((Object*) arg0))
127 return false;
128
129 //LOGI("String.charAt this=0x%08x index=%d\n", arg0, arg1);
130 count = dvmGetFieldInt((Object*) arg0, gDvm.offJavaLangString_count);
131 if ((s4) arg1 < 0 || (s4) arg1 >= count) {
132 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
133 return false;
134 } else {
135 offset = dvmGetFieldInt((Object*) arg0, gDvm.offJavaLangString_offset);
136 chars = (ArrayObject*)
137 dvmGetFieldObject((Object*) arg0, gDvm.offJavaLangString_value);
138
139 pResult->i = ((const u2*) chars->contents)[arg1 + offset];
140 return true;
141 }
142}
143
144#ifdef CHECK_MEMCMP16
145/*
146 * Utility function when we're evaluating alternative implementations.
147 */
148static void badMatch(StringObject* thisStrObj, StringObject* compStrObj,
149 int expectResult, int newResult, const char* compareType)
150{
151 ArrayObject* thisArray;
152 ArrayObject* compArray;
153 const char* thisStr;
154 const char* compStr;
155 int thisOffset, compOffset, thisCount, compCount;
156
157 thisCount =
158 dvmGetFieldInt((Object*) thisStrObj, gDvm.offJavaLangString_count);
159 compCount =
160 dvmGetFieldInt((Object*) compStrObj, gDvm.offJavaLangString_count);
161 thisOffset =
162 dvmGetFieldInt((Object*) thisStrObj, gDvm.offJavaLangString_offset);
163 compOffset =
164 dvmGetFieldInt((Object*) compStrObj, gDvm.offJavaLangString_offset);
165 thisArray = (ArrayObject*)
166 dvmGetFieldObject((Object*) thisStrObj, gDvm.offJavaLangString_value);
167 compArray = (ArrayObject*)
168 dvmGetFieldObject((Object*) compStrObj, gDvm.offJavaLangString_value);
169
170 thisStr = dvmCreateCstrFromString(thisStrObj);
171 compStr = dvmCreateCstrFromString(compStrObj);
172
173 LOGE("%s expected %d got %d\n", compareType, expectResult, newResult);
174 LOGE(" this (o=%d l=%d) '%s'\n", thisOffset, thisCount, thisStr);
175 LOGE(" comp (o=%d l=%d) '%s'\n", compOffset, compCount, compStr);
176 dvmPrintHexDumpEx(ANDROID_LOG_INFO, LOG_TAG,
177 ((const u2*) thisArray->contents) + thisOffset, thisCount*2,
178 kHexDumpLocal);
179 dvmPrintHexDumpEx(ANDROID_LOG_INFO, LOG_TAG,
180 ((const u2*) compArray->contents) + compOffset, compCount*2,
181 kHexDumpLocal);
182 dvmAbort();
183}
184#endif
185
186/*
187 * public int compareTo(String s)
188 */
189static bool javaLangString_compareTo(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
190 JValue* pResult)
191{
192 /*
193 * Null reference check on "this". Normally this is performed during
194 * the setup of the virtual method call. We need to do it before
195 * anything else. While we're at it, check out the other string,
196 * which must also be non-null.
197 */
198 if (!dvmValidateObject((Object*) arg0) ||
199 !dvmValidateObject((Object*) arg1))
200 {
201 return false;
202 }
203
204 /* quick test for comparison with itself */
205 if (arg0 == arg1) {
206 pResult->i = 0;
207 return true;
208 }
209
210 /*
211 * This would be simpler and faster if we promoted StringObject to
212 * a full representation, lining up the C structure fields with the
213 * actual object fields.
214 */
215 int thisCount, thisOffset, compCount, compOffset;
216 ArrayObject* thisArray;
217 ArrayObject* compArray;
218 const u2* thisChars;
219 const u2* compChars;
220 int i, minCount, countDiff;
221
222 thisCount = dvmGetFieldInt((Object*) arg0, gDvm.offJavaLangString_count);
223 compCount = dvmGetFieldInt((Object*) arg1, gDvm.offJavaLangString_count);
224 countDiff = thisCount - compCount;
225 minCount = (countDiff < 0) ? thisCount : compCount;
226 thisOffset = dvmGetFieldInt((Object*) arg0, gDvm.offJavaLangString_offset);
227 compOffset = dvmGetFieldInt((Object*) arg1, gDvm.offJavaLangString_offset);
228 thisArray = (ArrayObject*)
229 dvmGetFieldObject((Object*) arg0, gDvm.offJavaLangString_value);
230 compArray = (ArrayObject*)
231 dvmGetFieldObject((Object*) arg1, gDvm.offJavaLangString_value);
232 thisChars = ((const u2*) thisArray->contents) + thisOffset;
233 compChars = ((const u2*) compArray->contents) + compOffset;
234
235#ifdef HAVE__MEMCMP16
236 /*
237 * Use assembly version, which returns the difference between the
238 * characters. The annoying part here is that 0x00e9 - 0xffff != 0x00ea,
239 * because the interpreter converts the characters to 32-bit integers
240 * *without* sign extension before it subtracts them (which makes some
241 * sense since "char" is unsigned). So what we get is the result of
242 * 0x000000e9 - 0x0000ffff, which is 0xffff00ea.
243 */
244 int otherRes = __memcmp16(thisChars, compChars, minCount);
245# ifdef CHECK_MEMCMP16
246 for (i = 0; i < minCount; i++) {
247 if (thisChars[i] != compChars[i]) {
248 pResult->i = (s4) thisChars[i] - (s4) compChars[i];
249 if (pResult->i != otherRes) {
250 badMatch((StringObject*) arg0, (StringObject*) arg1,
251 pResult->i, otherRes, "compareTo");
252 }
253 return true;
254 }
255 }
256# endif
257 if (otherRes != 0) {
258 pResult->i = otherRes;
259 return true;
260 }
261
262#else
263 /*
264 * Straightforward implementation, examining 16 bits at a time. Compare
265 * the characters that overlap, and if they're all the same then return
266 * the difference in lengths.
267 */
268 for (i = 0; i < minCount; i++) {
269 if (thisChars[i] != compChars[i]) {
270 pResult->i = (s4) thisChars[i] - (s4) compChars[i];
271 return true;
272 }
273 }
274#endif
275
276 pResult->i = countDiff;
277 return true;
278}
279
280/*
281 * public boolean equals(Object anObject)
282 */
283static bool javaLangString_equals(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
284 JValue* pResult)
285{
286 /*
287 * Null reference check on "this".
288 */
289 if (!dvmValidateObject((Object*) arg0))
290 return false;
291
292 /* quick test for comparison with itself */
293 if (arg0 == arg1) {
294 pResult->i = true;
295 return true;
296 }
297
298 /*
299 * See if the other object is also a String.
300 *
301 * str.equals(null) is expected to return false, presumably based on
302 * the results of the instanceof test.
303 */
304 if (arg1 == 0 || ((Object*) arg0)->clazz != ((Object*) arg1)->clazz) {
305 pResult->i = false;
306 return true;
307 }
308
309 /*
310 * This would be simpler and faster if we promoted StringObject to
311 * a full representation, lining up the C structure fields with the
312 * actual object fields.
313 */
314 int thisCount, thisOffset, compCount, compOffset;
315 ArrayObject* thisArray;
316 ArrayObject* compArray;
317 const u2* thisChars;
318 const u2* compChars;
319 int i;
320
321 /* quick length check */
322 thisCount = dvmGetFieldInt((Object*) arg0, gDvm.offJavaLangString_count);
323 compCount = dvmGetFieldInt((Object*) arg1, gDvm.offJavaLangString_count);
324 if (thisCount != compCount) {
325 pResult->i = false;
326 return true;
327 }
328
329 thisOffset = dvmGetFieldInt((Object*) arg0, gDvm.offJavaLangString_offset);
330 compOffset = dvmGetFieldInt((Object*) arg1, gDvm.offJavaLangString_offset);
331 thisArray = (ArrayObject*)
332 dvmGetFieldObject((Object*) arg0, gDvm.offJavaLangString_value);
333 compArray = (ArrayObject*)
334 dvmGetFieldObject((Object*) arg1, gDvm.offJavaLangString_value);
335 thisChars = ((const u2*) thisArray->contents) + thisOffset;
336 compChars = ((const u2*) compArray->contents) + compOffset;
337
338#ifdef HAVE__MEMCMP16
339 pResult->i = (__memcmp16(thisChars, compChars, thisCount) == 0);
340# ifdef CHECK_MEMCMP16
341 int otherRes = (memcmp(thisChars, compChars, thisCount * 2) == 0);
342 if (pResult->i != otherRes) {
343 badMatch((StringObject*) arg0, (StringObject*) arg1,
344 otherRes, pResult->i, "equals-1");
345 }
346# endif
347#else
348 /*
349 * Straightforward implementation, examining 16 bits at a time. The
350 * direction of the loop doesn't matter, and starting at the end may
351 * give us an advantage when comparing certain types of strings (e.g.
352 * class names).
353 *
354 * We want to go forward for benchmarks against __memcmp16 so we get a
355 * meaningful comparison when the strings don't match (could also test
356 * with palindromes).
357 */
358 //for (i = 0; i < thisCount; i++)
359 for (i = thisCount-1; i >= 0; --i)
360 {
361 if (thisChars[i] != compChars[i]) {
362 pResult->i = false;
363 return true;
364 }
365 }
366 pResult->i = true;
367#endif
368
369 return true;
370}
371
372/*
373 * public int length()
374 */
375static bool javaLangString_length(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
376 JValue* pResult)
377{
378 //LOGI("String.length this=0x%08x pResult=%p\n", arg0, pResult);
379
380 /* null reference check on "this" */
381 if (!dvmValidateObject((Object*) arg0))
382 return false;
383
384 pResult->i = dvmGetFieldInt((Object*) arg0, gDvm.offJavaLangString_count);
385 return true;
386}
387
388
389/*
390 * ===========================================================================
391 * Infrastructure
392 * ===========================================================================
393 */
394
395/*
396 * Table of methods.
397 *
398 * The DEX optimizer uses the class/method/signature string fields to decide
399 * which calls it can trample. The interpreter just uses the function
400 * pointer field.
401 *
402 * IMPORTANT: you must update DALVIK_VM_BUILD in DalvikVersion.h if you make
403 * changes to this table.
404 */
405const InlineOperation gDvmInlineOpsTable[] = {
406 { org_apache_harmony_dalvik_NativeTestTarget_emptyInlineMethod,
407 "Lorg/apache/harmony/dalvik/NativeTestTarget;",
408 "emptyInlineMethod", "()V" },
409 { javaLangString_charAt,
410 "Ljava/lang/String;", "charAt", "(I)C" },
411 { javaLangString_compareTo,
412 "Ljava/lang/String;", "compareTo", "(Ljava/lang/String;)I" },
413 { javaLangString_equals,
414 "Ljava/lang/String;", "equals", "(Ljava/lang/Object;)Z" },
415 { javaLangString_length,
416 "Ljava/lang/String;", "length", "()I" },
417};
418
419
420/*
421 * Allocate some tables.
422 */
423bool dvmInlineNativeStartup(void)
424{
425#ifdef WITH_PROFILER
426 gDvm.inlinedMethods =
427 (Method**) calloc(NELEM(gDvmInlineOpsTable), sizeof(Method*));
428 if (gDvm.inlinedMethods == NULL)
429 return false;
430#endif
431
432 return true;
433}
434
435/*
436 * Free generated tables.
437 */
438void dvmInlineNativeShutdown(void)
439{
440#ifdef WITH_PROFILER
441 free(gDvm.inlinedMethods);
442#endif
443}
444
445
446/*
447 * Get a pointer to the inlineops table.
448 */
449const InlineOperation* dvmGetInlineOpsTable(void)
450{
451 return gDvmInlineOpsTable;
452}
453
454/*
455 * Get the number of entries in the inlineops table.
456 */
457int dvmGetInlineOpsTableLength(void)
458{
459 return NELEM(gDvmInlineOpsTable);
460}
461
462/*
463 * Make an inline call for the "debug" interpreter, used when the debugger
464 * or profiler is active.
465 */
466bool dvmPerformInlineOp4Dbg(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
467 JValue* pResult, int opIndex)
468{
469 Thread* self = dvmThreadSelf();
470 bool result;
471
472 assert(opIndex >= 0 && opIndex < NELEM(gDvmInlineOpsTable));
473
474#ifdef WITH_PROFILER
475 /*
476 * Populate the methods table on first use. It's possible the class
477 * hasn't been resolved yet, so we need to do the full "calling the
478 * method for the first time" routine. (It's probably okay to skip
479 * the access checks.)
480 *
481 * Currently assuming that we're only inlining stuff loaded by the
482 * bootstrap class loader. This is a safe assumption for many reasons.
483 */
484 Method* method = gDvm.inlinedMethods[opIndex];
485 if (method == NULL) {
486 ClassObject* clazz;
487
488 clazz = dvmFindClassNoInit(
489 gDvmInlineOpsTable[opIndex].classDescriptor, NULL);
490 if (clazz == NULL) {
491 LOGW("Warning: can't find class '%s'\n", clazz->descriptor);
492 goto skip_prof;
493 }
494 method = dvmFindDirectMethodByDescriptor(clazz,
495 gDvmInlineOpsTable[opIndex].methodName,
496 gDvmInlineOpsTable[opIndex].methodSignature);
497 if (method == NULL)
498 method = dvmFindVirtualMethodByDescriptor(clazz,
499 gDvmInlineOpsTable[opIndex].methodName,
500 gDvmInlineOpsTable[opIndex].methodSignature);
501 if (method == NULL) {
502 LOGW("Warning: can't find method %s.%s %s\n",
503 clazz->descriptor,
504 gDvmInlineOpsTable[opIndex].methodName,
505 gDvmInlineOpsTable[opIndex].methodSignature);
506 goto skip_prof;
507 }
508
509 gDvm.inlinedMethods[opIndex] = method;
510 IF_LOGV() {
511 char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
512 LOGV("Registered for profile: %s.%s %s\n",
513 method->clazz->descriptor, method->name, desc);
514 free(desc);
515 }
516 }
517
518 TRACE_METHOD_ENTER(self, method);
519 result = (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3,
520 pResult);
521 TRACE_METHOD_EXIT(self, method);
522 return result;
523
524skip_prof:
525#endif
526 return (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3, pResult);
527}
528