| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * Inlined native functions. These definitions replace interpreted or |
| * native implementations at runtime; "intrinsic" might be a better word. |
| */ |
| #include "Dalvik.h" |
| |
| #include <math.h> |
| |
| #ifdef HAVE__MEMCMP16 |
| /* hand-coded assembly implementation, available on some platforms */ |
| //#warning "trying memcmp16" |
| //#define CHECK_MEMCMP16 |
| /* "count" is in 16-bit units */ |
| extern u4 __memcmp16(const u2* s0, const u2* s1, size_t count); |
| #endif |
| |
| /* |
| * Some notes on "inline" functions. |
| * |
| * These are NOT simply native implementations. A full method definition |
| * must still be provided. Depending on the flags passed into the VM |
| * at runtime, the original or inline version may be selected by the |
| * DEX optimizer. |
| * |
| * PLEASE DO NOT use this as the default location for native methods. |
| * The difference between this and an "internal native" static method |
| * call on a 200MHz ARM 9 is roughly 370ns vs. 700ns. The code here |
| * "secretly replaces" the other method, so you can't avoid having two |
| * implementations. Since the DEX optimizer mode can't be known ahead |
| * of time, both implementations must be correct and complete. |
| * |
| * The only stuff that really needs to be here are methods that |
| * are high-volume or must be low-overhead, e.g. certain String/Math |
| * methods and some java.util.concurrent.atomic operations. |
| * |
| * Normally, a class is loaded and initialized the first time a static |
| * method is invoked. This property is NOT preserved here. If you need |
| * to access a static field in a class, you must ensure initialization |
| * yourself (cheap/easy way is to check the resolved-methods table, and |
| * resolve the method if it hasn't been). |
| * |
| * DO NOT replace "synchronized" methods. We do not support method |
| * synchronization here. |
| * |
| * DO NOT perform any allocations or do anything that could cause a |
| * garbage collection. The method arguments are not visible to the GC |
| * and will not be pinned or updated when memory blocks move. You are |
| * allowed to allocate and throw an exception so long as you only do so |
| * immediately before returning. |
| * |
| * Remember that these functions are executing while the thread is in |
| * the "RUNNING" state, not the "NATIVE" state. If you perform a blocking |
| * operation you can stall the entire VM if the GC or debugger wants to |
| * suspend the thread. Since these are arguably native implementations |
| * rather than VM internals, prefer NATIVE to VMWAIT if you want to change |
| * the thread state. |
| * |
| * Always write results to 32-bit or 64-bit fields in "pResult", e.g. do |
| * not write boolean results to pResult->z. The interpreter expects |
| * 32 or 64 bits to be set. |
| * |
| * Inline op methods return "false" if an exception was thrown, "true" if |
| * everything went well. |
| * |
| * DO NOT provide implementations of methods that can be overridden by a |
| * subclass, as polymorphism does not work correctly. For safety you should |
| * only provide inline functions for classes/methods declared "final". |
| * |
| * It's best to avoid inlining the overridden version of a method. For |
| * example, String.hashCode() is inherited from Object.hashCode(). Code |
| * calling String.hashCode() through an Object reference will run the |
| * "slow" version, while calling it through a String reference gets |
| * the inlined version. It's best to have just one version unless there |
| * are clear performance gains. |
| * |
| * Because the actual method is not called, debugger breakpoints on these |
| * methods will not happen. (TODO: have the code here find the original |
| * method and call it when the debugger is active.) Additional steps have |
| * been taken to allow method profiling to produce correct results. |
| */ |
| |
| |
| /* |
| * =========================================================================== |
| * org.apache.harmony.dalvik.NativeTestTarget |
| * =========================================================================== |
| */ |
| |
| /* |
| * public static void emptyInlineMethod |
| * |
| * This exists only for benchmarks. |
| */ |
| static bool org_apache_harmony_dalvik_NativeTestTarget_emptyInlineMethod( |
| u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult) |
| { |
| // do nothing |
| return true; |
| } |
| |
| |
| /* |
| * =========================================================================== |
| * java.lang.String |
| * =========================================================================== |
| */ |
| |
| /* |
| * public char charAt(int index) |
| */ |
| static bool javaLangString_charAt(u4 arg0, u4 arg1, u4 arg2, u4 arg3, |
| JValue* pResult) |
| { |
| int count, offset; |
| ArrayObject* chars; |
| |
| /* null reference check on "this" */ |
| if (!dvmValidateObject((Object*) arg0)) |
| return false; |
| |
| //LOGI("String.charAt this=0x%08x index=%d\n", arg0, arg1); |
| count = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT); |
| if ((s4) arg1 < 0 || (s4) arg1 >= count) { |
| dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL); |
| return false; |
| } else { |
| offset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET); |
| chars = (ArrayObject*) |
| dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE); |
| |
| pResult->i = ((const u2*) chars->contents)[arg1 + offset]; |
| return true; |
| } |
| } |
| |
| #ifdef CHECK_MEMCMP16 |
| /* |
| * Utility function when we're evaluating alternative implementations. |
| */ |
| static void badMatch(StringObject* thisStrObj, StringObject* compStrObj, |
| int expectResult, int newResult, const char* compareType) |
| { |
| ArrayObject* thisArray; |
| ArrayObject* compArray; |
| const char* thisStr; |
| const char* compStr; |
| int thisOffset, compOffset, thisCount, compCount; |
| |
| thisCount = |
| dvmGetFieldInt((Object*) thisStrObj, STRING_FIELDOFF_COUNT); |
| compCount = |
| dvmGetFieldInt((Object*) compStrObj, STRING_FIELDOFF_COUNT); |
| thisOffset = |
| dvmGetFieldInt((Object*) thisStrObj, STRING_FIELDOFF_OFFSET); |
| compOffset = |
| dvmGetFieldInt((Object*) compStrObj, STRING_FIELDOFF_OFFSET); |
| thisArray = (ArrayObject*) |
| dvmGetFieldObject((Object*) thisStrObj, STRING_FIELDOFF_VALUE); |
| compArray = (ArrayObject*) |
| dvmGetFieldObject((Object*) compStrObj, STRING_FIELDOFF_VALUE); |
| |
| thisStr = dvmCreateCstrFromString(thisStrObj); |
| compStr = dvmCreateCstrFromString(compStrObj); |
| |
| LOGE("%s expected %d got %d\n", compareType, expectResult, newResult); |
| LOGE(" this (o=%d l=%d) '%s'\n", thisOffset, thisCount, thisStr); |
| LOGE(" comp (o=%d l=%d) '%s'\n", compOffset, compCount, compStr); |
| dvmPrintHexDumpEx(ANDROID_LOG_INFO, LOG_TAG, |
| ((const u2*) thisArray->contents) + thisOffset, thisCount*2, |
| kHexDumpLocal); |
| dvmPrintHexDumpEx(ANDROID_LOG_INFO, LOG_TAG, |
| ((const u2*) compArray->contents) + compOffset, compCount*2, |
| kHexDumpLocal); |
| dvmAbort(); |
| } |
| #endif |
| |
| /* |
| * public int compareTo(String s) |
| */ |
| static bool javaLangString_compareTo(u4 arg0, u4 arg1, u4 arg2, u4 arg3, |
| JValue* pResult) |
| { |
| /* |
| * Null reference check on "this". Normally this is performed during |
| * the setup of the virtual method call. We need to do it before |
| * anything else. While we're at it, check out the other string, |
| * which must also be non-null. |
| */ |
| if (!dvmValidateObject((Object*) arg0) || |
| !dvmValidateObject((Object*) arg1)) |
| { |
| return false; |
| } |
| |
| /* quick test for comparison with itself */ |
| if (arg0 == arg1) { |
| pResult->i = 0; |
| return true; |
| } |
| |
| /* |
| * This would be simpler and faster if we promoted StringObject to |
| * a full representation, lining up the C structure fields with the |
| * actual object fields. |
| */ |
| int thisCount, thisOffset, compCount, compOffset; |
| ArrayObject* thisArray; |
| ArrayObject* compArray; |
| const u2* thisChars; |
| const u2* compChars; |
| int minCount, countDiff; |
| |
| thisCount = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT); |
| compCount = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_COUNT); |
| countDiff = thisCount - compCount; |
| minCount = (countDiff < 0) ? thisCount : compCount; |
| thisOffset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET); |
| compOffset = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_OFFSET); |
| thisArray = (ArrayObject*) |
| dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE); |
| compArray = (ArrayObject*) |
| dvmGetFieldObject((Object*) arg1, STRING_FIELDOFF_VALUE); |
| thisChars = ((const u2*) thisArray->contents) + thisOffset; |
| compChars = ((const u2*) compArray->contents) + compOffset; |
| |
| #ifdef HAVE__MEMCMP16 |
| /* |
| * Use assembly version, which returns the difference between the |
| * characters. The annoying part here is that 0x00e9 - 0xffff != 0x00ea, |
| * because the interpreter converts the characters to 32-bit integers |
| * *without* sign extension before it subtracts them (which makes some |
| * sense since "char" is unsigned). So what we get is the result of |
| * 0x000000e9 - 0x0000ffff, which is 0xffff00ea. |
| */ |
| int otherRes = __memcmp16(thisChars, compChars, minCount); |
| # ifdef CHECK_MEMCMP16 |
| int i; |
| for (i = 0; i < minCount; i++) { |
| if (thisChars[i] != compChars[i]) { |
| pResult->i = (s4) thisChars[i] - (s4) compChars[i]; |
| if (pResult->i != otherRes) { |
| badMatch((StringObject*) arg0, (StringObject*) arg1, |
| pResult->i, otherRes, "compareTo"); |
| } |
| return true; |
| } |
| } |
| # endif |
| if (otherRes != 0) { |
| pResult->i = otherRes; |
| return true; |
| } |
| |
| #else |
| /* |
| * Straightforward implementation, examining 16 bits at a time. Compare |
| * the characters that overlap, and if they're all the same then return |
| * the difference in lengths. |
| */ |
| int i; |
| for (i = 0; i < minCount; i++) { |
| if (thisChars[i] != compChars[i]) { |
| pResult->i = (s4) thisChars[i] - (s4) compChars[i]; |
| return true; |
| } |
| } |
| #endif |
| |
| pResult->i = countDiff; |
| return true; |
| } |
| |
| /* |
| * public boolean equals(Object anObject) |
| */ |
| static bool javaLangString_equals(u4 arg0, u4 arg1, u4 arg2, u4 arg3, |
| JValue* pResult) |
| { |
| /* |
| * Null reference check on "this". |
| */ |
| if (!dvmValidateObject((Object*) arg0)) |
| return false; |
| |
| /* quick test for comparison with itself */ |
| if (arg0 == arg1) { |
| pResult->i = true; |
| return true; |
| } |
| |
| /* |
| * See if the other object is also a String. |
| * |
| * str.equals(null) is expected to return false, presumably based on |
| * the results of the instanceof test. |
| */ |
| if (arg1 == 0 || ((Object*) arg0)->clazz != ((Object*) arg1)->clazz) { |
| pResult->i = false; |
| return true; |
| } |
| |
| /* |
| * This would be simpler and faster if we promoted StringObject to |
| * a full representation, lining up the C structure fields with the |
| * actual object fields. |
| */ |
| int thisCount, thisOffset, compCount, compOffset; |
| ArrayObject* thisArray; |
| ArrayObject* compArray; |
| const u2* thisChars; |
| const u2* compChars; |
| |
| /* quick length check */ |
| thisCount = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT); |
| compCount = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_COUNT); |
| if (thisCount != compCount) { |
| pResult->i = false; |
| return true; |
| } |
| |
| thisOffset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET); |
| compOffset = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_OFFSET); |
| thisArray = (ArrayObject*) |
| dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE); |
| compArray = (ArrayObject*) |
| dvmGetFieldObject((Object*) arg1, STRING_FIELDOFF_VALUE); |
| thisChars = ((const u2*) thisArray->contents) + thisOffset; |
| compChars = ((const u2*) compArray->contents) + compOffset; |
| |
| #ifdef HAVE__MEMCMP16 |
| pResult->i = (__memcmp16(thisChars, compChars, thisCount) == 0); |
| # ifdef CHECK_MEMCMP16 |
| int otherRes = (memcmp(thisChars, compChars, thisCount * 2) == 0); |
| if (pResult->i != otherRes) { |
| badMatch((StringObject*) arg0, (StringObject*) arg1, |
| otherRes, pResult->i, "equals-1"); |
| } |
| # endif |
| #else |
| /* |
| * Straightforward implementation, examining 16 bits at a time. The |
| * direction of the loop doesn't matter, and starting at the end may |
| * give us an advantage when comparing certain types of strings (e.g. |
| * class names). |
| * |
| * We want to go forward for benchmarks against __memcmp16 so we get a |
| * meaningful comparison when the strings don't match (could also test |
| * with palindromes). |
| */ |
| int i; |
| //for (i = 0; i < thisCount; i++) |
| for (i = thisCount-1; i >= 0; --i) |
| { |
| if (thisChars[i] != compChars[i]) { |
| pResult->i = false; |
| return true; |
| } |
| } |
| pResult->i = true; |
| #endif |
| |
| return true; |
| } |
| |
| /* |
| * public int length() |
| */ |
| static bool javaLangString_length(u4 arg0, u4 arg1, u4 arg2, u4 arg3, |
| JValue* pResult) |
| { |
| //LOGI("String.length this=0x%08x pResult=%p\n", arg0, pResult); |
| |
| /* null reference check on "this" */ |
| if (!dvmValidateObject((Object*) arg0)) |
| return false; |
| |
| pResult->i = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT); |
| return true; |
| } |
| |
| /* |
| * public boolean isEmpty() |
| */ |
| static bool javaLangString_isEmpty(u4 arg0, u4 arg1, u4 arg2, u4 arg3, |
| JValue* pResult) |
| { |
| //LOGI("String.isEmpty this=0x%08x pResult=%p\n", arg0, pResult); |
| |
| /* null reference check on "this" */ |
| if (!dvmValidateObject((Object*) arg0)) |
| return false; |
| |
| pResult->i = (dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT) == 0); |
| return true; |
| } |
| |
| /* |
| * Determine the index of the first character matching "ch". The string |
| * to search is described by "chars", "offset", and "count". |
| * |
| * The character must be <= 0xffff. Supplementary characters are handled in |
| * Java. |
| * |
| * The "start" parameter must be clamped to [0..count]. |
| * |
| * Returns -1 if no match is found. |
| */ |
| static inline int indexOfCommon(Object* strObj, int ch, int start) |
| { |
| //if ((ch & 0xffff) != ch) /* 32-bit code point */ |
| // return -1; |
| |
| /* pull out the basic elements */ |
| ArrayObject* charArray = |
| (ArrayObject*) dvmGetFieldObject(strObj, STRING_FIELDOFF_VALUE); |
| const u2* chars = (const u2*) charArray->contents; |
| int offset = dvmGetFieldInt(strObj, STRING_FIELDOFF_OFFSET); |
| int count = dvmGetFieldInt(strObj, STRING_FIELDOFF_COUNT); |
| //LOGI("String.indexOf(0x%08x, 0x%04x, %d) off=%d count=%d\n", |
| // (u4) strObj, ch, start, offset, count); |
| |
| /* factor out the offset */ |
| chars += offset; |
| |
| if (start < 0) |
| start = 0; |
| |
| #if 0 |
| /* 16-bit loop, simple */ |
| while (start < count) { |
| if (chars[start] == ch) |
| return start; |
| start++; |
| } |
| #else |
| /* 16-bit loop, slightly better on ARM */ |
| const u2* ptr = chars + start; |
| const u2* endPtr = chars + count; |
| while (ptr < endPtr) { |
| if (*ptr++ == ch) |
| return (ptr-1) - chars; |
| } |
| #endif |
| |
| return -1; |
| } |
| |
| /* |
| * public int indexOf(int c, int start) |
| * |
| * Scan forward through the string for a matching character. |
| * The character must be <= 0xffff; this method does not handle supplementary |
| * characters. |
| */ |
| static bool javaLangString_fastIndexOf_II(u4 arg0, u4 arg1, u4 arg2, u4 arg3, |
| JValue* pResult) |
| { |
| /* null reference check on "this" */ |
| if (!dvmValidateObject((Object*) arg0)) |
| return false; |
| |
| pResult->i = indexOfCommon((Object*) arg0, arg1, arg2); |
| return true; |
| } |
| |
| |
| /* |
| * =========================================================================== |
| * java.lang.Math |
| * =========================================================================== |
| */ |
| |
| typedef union { |
| u4 arg; |
| float ff; |
| } Convert32; |
| |
| typedef union { |
| u4 arg[2]; |
| s8 ll; |
| double dd; |
| } Convert64; |
| |
| /* |
| * public static int abs(int) |
| */ |
| static bool javaLangMath_abs_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3, |
| JValue* pResult) |
| { |
| s4 val = (s4) arg0; |
| pResult->i = (val >= 0) ? val : -val; |
| return true; |
| } |
| |
| /* |
| * public static long abs(long) |
| */ |
| static bool javaLangMath_abs_long(u4 arg0, u4 arg1, u4 arg2, u4 arg3, |
| JValue* pResult) |
| { |
| Convert64 convert; |
| convert.arg[0] = arg0; |
| convert.arg[1] = arg1; |
| s8 val = convert.ll; |
| pResult->j = (val >= 0) ? val : -val; |
| return true; |
| } |
| |
| /* |
| * public static float abs(float) |
| */ |
| static bool javaLangMath_abs_float(u4 arg0, u4 arg1, u4 arg2, u4 arg3, |
| JValue* pResult) |
| { |
| Convert32 convert; |
| /* clear the sign bit; assumes a fairly common fp representation */ |
| convert.arg = arg0 & 0x7fffffff; |
| pResult->f = convert.ff; |
| return true; |
| } |
| |
| /* |
| * public static double abs(double) |
| */ |
| static bool javaLangMath_abs_double(u4 arg0, u4 arg1, u4 arg2, u4 arg3, |
| JValue* pResult) |
| { |
| Convert64 convert; |
| convert.arg[0] = arg0; |
| convert.arg[1] = arg1; |
| /* clear the sign bit in the (endian-dependent) high word */ |
| convert.ll &= 0x7fffffffffffffffULL; |
| pResult->d = convert.dd; |
| return true; |
| } |
| |
| /* |
| * public static int min(int) |
| */ |
| static bool javaLangMath_min_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3, |
| JValue* pResult) |
| { |
| pResult->i = ((s4) arg0 < (s4) arg1) ? arg0 : arg1; |
| return true; |
| } |
| |
| /* |
| * public static int max(int) |
| */ |
| static bool javaLangMath_max_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3, |
| JValue* pResult) |
| { |
| pResult->i = ((s4) arg0 > (s4) arg1) ? arg0 : arg1; |
| return true; |
| } |
| |
| /* |
| * public static double sqrt(double) |
| * |
| * With ARM VFP enabled, gcc turns this into an fsqrtd instruction, followed |
| * by an fcmpd of the result against itself. If it doesn't match (i.e. |
| * it's NaN), the libm sqrt() is invoked. |
| */ |
| static bool javaLangMath_sqrt(u4 arg0, u4 arg1, u4 arg2, u4 arg3, |
| JValue* pResult) |
| { |
| Convert64 convert; |
| convert.arg[0] = arg0; |
| convert.arg[1] = arg1; |
| pResult->d = sqrt(convert.dd); |
| return true; |
| } |
| |
| /* |
| * public static double cos(double) |
| */ |
| static bool javaLangMath_cos(u4 arg0, u4 arg1, u4 arg2, u4 arg3, |
| JValue* pResult) |
| { |
| Convert64 convert; |
| convert.arg[0] = arg0; |
| convert.arg[1] = arg1; |
| pResult->d = cos(convert.dd); |
| return true; |
| } |
| |
| /* |
| * public static double sin(double) |
| */ |
| static bool javaLangMath_sin(u4 arg0, u4 arg1, u4 arg2, u4 arg3, |
| JValue* pResult) |
| { |
| Convert64 convert; |
| convert.arg[0] = arg0; |
| convert.arg[1] = arg1; |
| pResult->d = sin(convert.dd); |
| return true; |
| } |
| |
| /* |
| * =========================================================================== |
| * java.lang.Float |
| * =========================================================================== |
| */ |
| |
| static bool javaLangFloat_floatToIntBits(u4 arg0, u4 arg1, u4 arg2, u4 arg, |
| JValue* pResult) |
| { |
| Convert32 convert; |
| convert.arg = arg0; |
| pResult->i = isnanf(convert.ff) ? 0x7fc00000 : arg0; |
| return true; |
| } |
| |
| static bool javaLangFloat_floatToRawIntBits(u4 arg0, u4 arg1, u4 arg2, u4 arg, |
| JValue* pResult) |
| { |
| pResult->i = arg0; |
| return true; |
| } |
| |
| static bool javaLangFloat_intBitsToFloat(u4 arg0, u4 arg1, u4 arg2, u4 arg, |
| JValue* pResult) |
| { |
| Convert32 convert; |
| convert.arg = arg0; |
| pResult->f = convert.ff; |
| return true; |
| } |
| |
| /* |
| * =========================================================================== |
| * java.lang.Double |
| * =========================================================================== |
| */ |
| |
| static bool javaLangDouble_doubleToLongBits(u4 arg0, u4 arg1, u4 arg2, u4 arg, |
| JValue* pResult) |
| { |
| Convert64 convert; |
| convert.arg[0] = arg0; |
| convert.arg[1] = arg1; |
| pResult->j = isnan(convert.dd) ? 0x7ff8000000000000LL : convert.ll; |
| return true; |
| } |
| |
| static bool javaLangDouble_doubleToRawLongBits(u4 arg0, u4 arg1, u4 arg2, |
| u4 arg, JValue* pResult) |
| { |
| Convert64 convert; |
| convert.arg[0] = arg0; |
| convert.arg[1] = arg1; |
| pResult->j = convert.ll; |
| return true; |
| } |
| |
| static bool javaLangDouble_longBitsToDouble(u4 arg0, u4 arg1, u4 arg2, u4 arg, |
| JValue* pResult) |
| { |
| Convert64 convert; |
| convert.arg[0] = arg0; |
| convert.arg[1] = arg1; |
| pResult->d = convert.dd; |
| return true; |
| } |
| |
| /* |
| * =========================================================================== |
| * Infrastructure |
| * =========================================================================== |
| */ |
| |
| /* |
| * Table of methods. |
| * |
| * The DEX optimizer uses the class/method/signature string fields to decide |
| * which calls it can trample. The interpreter just uses the function |
| * pointer field. |
| * |
| * IMPORTANT: you must update DALVIK_VM_BUILD in DalvikVersion.h if you make |
| * changes to this table. |
| * |
| * NOTE: If present, the JIT will also need to know about changes |
| * to this table. Update the NativeInlineOps enum in InlineNative.h and |
| * the dispatch code in compiler/codegen/<target>/Codegen.c. |
| */ |
| const InlineOperation gDvmInlineOpsTable[] = { |
| { org_apache_harmony_dalvik_NativeTestTarget_emptyInlineMethod, |
| "Lorg/apache/harmony/dalvik/NativeTestTarget;", |
| "emptyInlineMethod", "()V" }, |
| |
| { javaLangString_charAt, |
| "Ljava/lang/String;", "charAt", "(I)C" }, |
| { javaLangString_compareTo, |
| "Ljava/lang/String;", "compareTo", "(Ljava/lang/String;)I" }, |
| { javaLangString_equals, |
| "Ljava/lang/String;", "equals", "(Ljava/lang/Object;)Z" }, |
| { javaLangString_fastIndexOf_II, |
| "Ljava/lang/String;", "fastIndexOf", "(II)I" }, |
| { javaLangString_isEmpty, |
| "Ljava/lang/String;", "isEmpty", "()Z" }, |
| { javaLangString_length, |
| "Ljava/lang/String;", "length", "()I" }, |
| |
| { javaLangMath_abs_int, |
| "Ljava/lang/Math;", "abs", "(I)I" }, |
| { javaLangMath_abs_long, |
| "Ljava/lang/Math;", "abs", "(J)J" }, |
| { javaLangMath_abs_float, |
| "Ljava/lang/Math;", "abs", "(F)F" }, |
| { javaLangMath_abs_double, |
| "Ljava/lang/Math;", "abs", "(D)D" }, |
| { javaLangMath_min_int, |
| "Ljava/lang/Math;", "min", "(II)I" }, |
| { javaLangMath_max_int, |
| "Ljava/lang/Math;", "max", "(II)I" }, |
| { javaLangMath_sqrt, |
| "Ljava/lang/Math;", "sqrt", "(D)D" }, |
| { javaLangMath_cos, |
| "Ljava/lang/Math;", "cos", "(D)D" }, |
| { javaLangMath_sin, |
| "Ljava/lang/Math;", "sin", "(D)D" }, |
| |
| { javaLangFloat_floatToIntBits, |
| "Ljava/lang/Float;", "floatToIntBits", "(F)I" }, |
| { javaLangFloat_floatToRawIntBits, |
| "Ljava/lang/Float;", "floatToRawIntBits", "(F)I" }, |
| { javaLangFloat_intBitsToFloat, |
| "Ljava/lang/Float;", "intBitsToFloat", "(I)F" }, |
| |
| { javaLangDouble_doubleToLongBits, |
| "Ljava/lang/Double;", "doubleToLongBits", "(D)J" }, |
| { javaLangDouble_doubleToRawLongBits, |
| "Ljava/lang/Double;", "doubleToRawLongBits", "(D)J" }, |
| { javaLangDouble_longBitsToDouble, |
| "Ljava/lang/Double;", "longBitsToDouble", "(J)D" }, |
| }; |
| |
| /* |
| * Allocate some tables. |
| */ |
| bool dvmInlineNativeStartup(void) |
| { |
| gDvm.inlinedMethods = |
| (Method**) calloc(NELEM(gDvmInlineOpsTable), sizeof(Method*)); |
| if (gDvm.inlinedMethods == NULL) |
| return false; |
| |
| return true; |
| } |
| |
| /* |
| * Free generated tables. |
| */ |
| void dvmInlineNativeShutdown(void) |
| { |
| free(gDvm.inlinedMethods); |
| } |
| |
| |
| /* |
| * Get a pointer to the inlineops table. |
| */ |
| const InlineOperation* dvmGetInlineOpsTable(void) |
| { |
| return gDvmInlineOpsTable; |
| } |
| |
| /* |
| * Get the number of entries in the inlineops table. |
| */ |
| int dvmGetInlineOpsTableLength(void) |
| { |
| return NELEM(gDvmInlineOpsTable); |
| } |
| |
| Method* dvmFindInlinableMethod(const char* classDescriptor, |
| const char* methodName, const char* methodSignature) |
| { |
| /* |
| * Find the class. |
| */ |
| ClassObject* clazz = dvmFindClassNoInit(classDescriptor, NULL); |
| if (clazz == NULL) { |
| LOGE("dvmFindInlinableMethod: can't find class '%s'", |
| classDescriptor); |
| dvmClearException(dvmThreadSelf()); |
| return NULL; |
| } |
| |
| /* |
| * Method could be virtual or direct. Try both. Don't use |
| * the "hier" versions. |
| */ |
| Method* method = dvmFindDirectMethodByDescriptor(clazz, methodName, |
| methodSignature); |
| if (method == NULL) { |
| method = dvmFindVirtualMethodByDescriptor(clazz, methodName, |
| methodSignature); |
| } |
| if (method == NULL) { |
| LOGE("dvmFindInlinableMethod: can't find method %s.%s %s", |
| clazz->descriptor, methodName, methodSignature); |
| return NULL; |
| } |
| |
| /* |
| * Check that the method is appropriate for inlining. |
| */ |
| if (!dvmIsFinalClass(clazz) && !dvmIsFinalMethod(method)) { |
| LOGE("dvmFindInlinableMethod: can't inline non-final method %s.%s", |
| clazz->descriptor, method->name); |
| return NULL; |
| } |
| if (dvmIsSynchronizedMethod(method) || |
| dvmIsDeclaredSynchronizedMethod(method)) { |
| LOGE("dvmFindInlinableMethod: can't inline synchronized method %s.%s", |
| clazz->descriptor, method->name); |
| return NULL; |
| } |
| |
| return method; |
| } |
| |
| /* |
| * Populate the methods table on first use. It's possible the class |
| * hasn't been resolved yet, so we need to do the full "calling the |
| * method for the first time" routine. (It's probably okay to skip |
| * the access checks.) |
| * |
| * Currently assuming that we're only inlining stuff loaded by the |
| * bootstrap class loader. This is a safe assumption for many reasons. |
| */ |
| static Method* resolveInlineNative(int opIndex) |
| { |
| assert(opIndex >= 0 && opIndex < NELEM(gDvmInlineOpsTable)); |
| Method* method = gDvm.inlinedMethods[opIndex]; |
| if (method != NULL) { |
| return method; |
| } |
| |
| method = dvmFindInlinableMethod( |
| gDvmInlineOpsTable[opIndex].classDescriptor, |
| gDvmInlineOpsTable[opIndex].methodName, |
| gDvmInlineOpsTable[opIndex].methodSignature); |
| |
| if (method == NULL) { |
| /* We already reported the error. */ |
| return NULL; |
| } |
| |
| gDvm.inlinedMethods[opIndex] = method; |
| IF_LOGV() { |
| char* desc = dexProtoCopyMethodDescriptor(&method->prototype); |
| LOGV("Registered for profile: %s.%s %s\n", |
| method->clazz->descriptor, method->name, desc); |
| free(desc); |
| } |
| |
| return method; |
| } |
| |
| /* |
| * Make an inline call for the "debug" interpreter, used when the debugger |
| * or profiler is active. |
| */ |
| bool dvmPerformInlineOp4Dbg(u4 arg0, u4 arg1, u4 arg2, u4 arg3, |
| JValue* pResult, int opIndex) |
| { |
| Method* method = resolveInlineNative(opIndex); |
| if (method == NULL) { |
| return (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3, |
| pResult); |
| } |
| |
| Thread* self = dvmThreadSelf(); |
| TRACE_METHOD_ENTER(self, method); |
| bool result = (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3, |
| pResult); |
| TRACE_METHOD_EXIT(self, method); |
| return result; |
| } |
| |
| /* |
| * Check that we can resolve every inline native. |
| */ |
| bool dvmInlineNativeCheck(void) |
| { |
| int op; |
| for (op = 0; op < NELEM(gDvmInlineOpsTable); ++op) { |
| if (resolveInlineNative(op) == NULL) { |
| dvmAbort(); |
| } |
| } |
| return true; |
| } |