Replace String CharArray with internal uint16_t array.
Summary of high level changes:
- Adds compiler inliner support to identify string init methods
- Adds compiler support (quick & optimizing) with new invoke code path
that calls method off the thread pointer
- Adds thread entrypoints for all string init methods
- Adds map to verifier to log when receiver of string init has been
copied to other registers. used by compiler and interpreter
Change-Id: I797b992a8feb566f9ad73060011ab6f51eb7ce01
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index 6ba4016..2b2d6af 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -21,6 +21,7 @@
#include "arm_lir.h"
#include "base/logging.h"
#include "dex/mir_graph.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
@@ -619,13 +620,31 @@
* Bit of a hack here - in the absence of a real scheduling pass,
* emit the next instruction in static & direct invoke sequences.
*/
-int ArmMir2Lir::ArmNextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSED,
+int ArmMir2Lir::ArmNextSDCallInsn(CompilationUnit* cu, CallInfo* info,
int state, const MethodReference& target_method,
uint32_t unused_idx ATTRIBUTE_UNUSED,
uintptr_t direct_code, uintptr_t direct_method,
InvokeType type) {
ArmMir2Lir* cg = static_cast<ArmMir2Lir*>(cu->cg.get());
- if (direct_code != 0 && direct_method != 0) {
+ if (info->string_init_offset != 0) {
+ RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
+ switch (state) {
+ case 0: { // Grab target method* from thread pointer
+ cg->LoadRefDisp(rs_rARM_SELF, info->string_init_offset, arg0_ref, kNotVolatile);
+ break;
+ }
+ case 1: // Grab the code from the method*
+ if (direct_code == 0) {
+ // kInvokeTgt := arg0_ref->entrypoint
+ cg->LoadWordDisp(arg0_ref,
+ mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kArmPointerSize).Int32Value(), cg->TargetPtrReg(kInvokeTgt));
+ }
+ break;
+ default:
+ return -1;
+ }
+ } else if (direct_code != 0 && direct_method != 0) {
switch (state) {
case 0: // Get the current Method* [sets kArg0]
if (direct_code != static_cast<uintptr_t>(-1)) {
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index 9a7c2ad..e49e40d 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -21,6 +21,7 @@
#include "arm64_lir.h"
#include "base/logging.h"
#include "dex/mir_graph.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
@@ -460,7 +461,25 @@
InvokeType type) {
UNUSED(info, unused_idx);
Arm64Mir2Lir* cg = static_cast<Arm64Mir2Lir*>(cu->cg.get());
- if (direct_code != 0 && direct_method != 0) {
+ if (info->string_init_offset != 0) {
+ RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
+ switch (state) {
+ case 0: { // Grab target method* from thread pointer
+ cg->LoadRefDisp(rs_xSELF, info->string_init_offset, arg0_ref, kNotVolatile);
+ break;
+ }
+ case 1: // Grab the code from the method*
+ if (direct_code == 0) {
+ // kInvokeTgt := arg0_ref->entrypoint
+ cg->LoadWordDisp(arg0_ref,
+ mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kArm64PointerSize).Int32Value(), cg->TargetPtrReg(kInvokeTgt));
+ }
+ break;
+ default:
+ return -1;
+ }
+ } else if (direct_code != 0 && direct_method != 0) {
switch (state) {
case 0: // Get the current Method* [sets kArg0]
if (direct_code != static_cast<uintptr_t>(-1)) {
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index f5e6c09..2568ee3 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -55,8 +55,12 @@
false, // kIntrinsicReferenceGetReferent
false, // kIntrinsicCharAt
false, // kIntrinsicCompareTo
+ false, // kIntrinsicGetCharsNoCheck
false, // kIntrinsicIsEmptyOrLength
false, // kIntrinsicIndexOf
+ true, // kIntrinsicNewStringFromBytes
+ true, // kIntrinsicNewStringFromChars
+ true, // kIntrinsicNewStringFromString
true, // kIntrinsicCurrentThread
true, // kIntrinsicPeek
true, // kIntrinsicPoke
@@ -88,8 +92,15 @@
static_assert(!kIntrinsicIsStatic[kIntrinsicReferenceGetReferent], "Get must not be static");
static_assert(!kIntrinsicIsStatic[kIntrinsicCharAt], "CharAt must not be static");
static_assert(!kIntrinsicIsStatic[kIntrinsicCompareTo], "CompareTo must not be static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicGetCharsNoCheck], "GetCharsNoCheck must not be static");
static_assert(!kIntrinsicIsStatic[kIntrinsicIsEmptyOrLength], "IsEmptyOrLength must not be static");
static_assert(!kIntrinsicIsStatic[kIntrinsicIndexOf], "IndexOf must not be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicNewStringFromBytes],
+ "NewStringFromBytes must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicNewStringFromChars],
+ "NewStringFromChars must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicNewStringFromString],
+ "NewStringFromString must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicCurrentThread], "CurrentThread must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicPeek], "Peek must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicPoke], "Poke must be static");
@@ -137,9 +148,15 @@
"F", // kClassCacheFloat
"D", // kClassCacheDouble
"V", // kClassCacheVoid
+ "[B", // kClassCacheJavaLangByteArray
+ "[C", // kClassCacheJavaLangCharArray
+ "[I", // kClassCacheJavaLangIntArray
"Ljava/lang/Object;", // kClassCacheJavaLangObject
- "Ljava/lang/ref/Reference;", // kClassCacheJavaLangRefReference
+ "Ljava/lang/ref/Reference;", // kClassCacheJavaLangRefReference
"Ljava/lang/String;", // kClassCacheJavaLangString
+ "Ljava/lang/StringBuffer;", // kClassCacheJavaLangStringBuffer
+ "Ljava/lang/StringBuilder;", // kClassCacheJavaLangStringBuilder
+ "Ljava/lang/StringFactory;", // kClassCacheJavaLangStringFactory
"Ljava/lang/Double;", // kClassCacheJavaLangDouble
"Ljava/lang/Float;", // kClassCacheJavaLangFloat
"Ljava/lang/Integer;", // kClassCacheJavaLangInteger
@@ -148,10 +165,10 @@
"Ljava/lang/Math;", // kClassCacheJavaLangMath
"Ljava/lang/StrictMath;", // kClassCacheJavaLangStrictMath
"Ljava/lang/Thread;", // kClassCacheJavaLangThread
+ "Ljava/nio/charset/Charset;", // kClassCacheJavaNioCharsetCharset
"Llibcore/io/Memory;", // kClassCacheLibcoreIoMemory
"Lsun/misc/Unsafe;", // kClassCacheSunMiscUnsafe
"Ljava/lang/System;", // kClassCacheJavaLangSystem
- "[C" // kClassCacheJavaLangCharArray
};
const char* const DexFileMethodInliner::kNameCacheNames[] = {
@@ -172,9 +189,14 @@
"getReferent", // kNameCacheReferenceGet
"charAt", // kNameCacheCharAt
"compareTo", // kNameCacheCompareTo
+ "getCharsNoCheck", // kNameCacheGetCharsNoCheck
"isEmpty", // kNameCacheIsEmpty
"indexOf", // kNameCacheIndexOf
"length", // kNameCacheLength
+ "<init>", // kNameCacheInit
+ "newStringFromBytes", // kNameCacheNewStringFromBytes
+ "newStringFromChars", // kNameCacheNewStringFromChars
+ "newStringFromString", // kNameCacheNewStringFromString
"currentThread", // kNameCacheCurrentThread
"peekByte", // kNameCachePeekByte
"peekIntNative", // kNameCachePeekIntNative
@@ -282,7 +304,53 @@
kClassCacheJavaLangObject } },
// kProtoCacheCharArrayICharArrayII_V
{ kClassCacheVoid, 5, {kClassCacheJavaLangCharArray, kClassCacheInt,
- kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheInt}}
+ kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheInt} },
+ // kProtoCacheIICharArrayI_V
+ { kClassCacheVoid, 4, { kClassCacheInt, kClassCacheInt, kClassCacheJavaLangCharArray,
+ kClassCacheInt } },
+ // kProtoCacheByteArrayIII_String
+ { kClassCacheJavaLangString, 4, { kClassCacheJavaLangByteArray, kClassCacheInt, kClassCacheInt,
+ kClassCacheInt } },
+ // kProtoCacheIICharArray_String
+ { kClassCacheJavaLangString, 3, { kClassCacheInt, kClassCacheInt,
+ kClassCacheJavaLangCharArray } },
+ // kProtoCacheString_String
+ { kClassCacheJavaLangString, 1, { kClassCacheJavaLangString } },
+ // kProtoCache_V
+ { kClassCacheVoid, 0, { } },
+ // kProtoCacheByteArray_V
+ { kClassCacheVoid, 1, { kClassCacheJavaLangByteArray } },
+ // kProtoCacheByteArrayI_V
+ { kClassCacheVoid, 2, { kClassCacheJavaLangByteArray, kClassCacheInt } },
+ // kProtoCacheByteArrayII_V
+ { kClassCacheVoid, 3, { kClassCacheJavaLangByteArray, kClassCacheInt, kClassCacheInt } },
+ // kProtoCacheByteArrayIII_V
+ { kClassCacheVoid, 4, { kClassCacheJavaLangByteArray, kClassCacheInt, kClassCacheInt,
+ kClassCacheInt } },
+ // kProtoCacheByteArrayIIString_V
+ { kClassCacheVoid, 4, { kClassCacheJavaLangByteArray, kClassCacheInt, kClassCacheInt,
+ kClassCacheJavaLangString } },
+ // kProtoCacheByteArrayString_V
+ { kClassCacheVoid, 2, { kClassCacheJavaLangByteArray, kClassCacheJavaLangString } },
+ // kProtoCacheByteArrayIICharset_V
+ { kClassCacheVoid, 4, { kClassCacheJavaLangByteArray, kClassCacheInt, kClassCacheInt,
+ kClassCacheJavaNioCharsetCharset } },
+ // kProtoCacheByteArrayCharset_V
+ { kClassCacheVoid, 2, { kClassCacheJavaLangByteArray, kClassCacheJavaNioCharsetCharset } },
+ // kProtoCacheCharArray_V
+ { kClassCacheVoid, 1, { kClassCacheJavaLangCharArray } },
+ // kProtoCacheCharArrayII_V
+ { kClassCacheVoid, 3, { kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheInt } },
+ // kProtoCacheIICharArray_V
+ { kClassCacheVoid, 3, { kClassCacheInt, kClassCacheInt, kClassCacheJavaLangCharArray } },
+ // kProtoCacheIntArrayII_V
+ { kClassCacheVoid, 3, { kClassCacheJavaLangIntArray, kClassCacheInt, kClassCacheInt } },
+ // kProtoCacheString_V
+ { kClassCacheVoid, 1, { kClassCacheJavaLangString } },
+ // kProtoCacheStringBuffer_V
+ { kClassCacheVoid, 1, { kClassCacheJavaLangStringBuffer } },
+ // kProtoCacheStringBuilder_V
+ { kClassCacheVoid, 1, { kClassCacheJavaLangStringBuilder } },
};
const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods[] = {
@@ -343,6 +411,7 @@
INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0),
INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0),
+ INTRINSIC(JavaLangString, GetCharsNoCheck, IICharArrayI_V, kIntrinsicGetCharsNoCheck, 0),
INTRINSIC(JavaLangString, IsEmpty, _Z, kIntrinsicIsEmptyOrLength, kIntrinsicFlagIsEmpty),
INTRINSIC(JavaLangString, IndexOf, II_I, kIntrinsicIndexOf, kIntrinsicFlagNone),
INTRINSIC(JavaLangString, IndexOf, I_I, kIntrinsicIndexOf, kIntrinsicFlagBase0),
@@ -386,8 +455,29 @@
INTRINSIC(JavaLangSystem, ArrayCopy, CharArrayICharArrayII_V , kIntrinsicSystemArrayCopyCharArray,
0),
-
#undef INTRINSIC
+
+#define SPECIAL(c, n, p, o, d) \
+ { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, kInlineSpecial, { d } } }
+
+ SPECIAL(JavaLangString, Init, _V, kInlineStringInit, 0),
+ SPECIAL(JavaLangString, Init, ByteArray_V, kInlineStringInit, 1),
+ SPECIAL(JavaLangString, Init, ByteArrayI_V, kInlineStringInit, 2),
+ SPECIAL(JavaLangString, Init, ByteArrayII_V, kInlineStringInit, 3),
+ SPECIAL(JavaLangString, Init, ByteArrayIII_V, kInlineStringInit, 4),
+ SPECIAL(JavaLangString, Init, ByteArrayIIString_V, kInlineStringInit, 5),
+ SPECIAL(JavaLangString, Init, ByteArrayString_V, kInlineStringInit, 6),
+ SPECIAL(JavaLangString, Init, ByteArrayIICharset_V, kInlineStringInit, 7),
+ SPECIAL(JavaLangString, Init, ByteArrayCharset_V, kInlineStringInit, 8),
+ SPECIAL(JavaLangString, Init, CharArray_V, kInlineStringInit, 9),
+ SPECIAL(JavaLangString, Init, CharArrayII_V, kInlineStringInit, 10),
+ SPECIAL(JavaLangString, Init, IICharArray_V, kInlineStringInit, 11),
+ SPECIAL(JavaLangString, Init, IntArrayII_V, kInlineStringInit, 12),
+ SPECIAL(JavaLangString, Init, String_V, kInlineStringInit, 13),
+ SPECIAL(JavaLangString, Init, StringBuffer_V, kInlineStringInit, 14),
+ SPECIAL(JavaLangString, Init, StringBuilder_V, kInlineStringInit, 15),
+
+#undef SPECIAL
};
DexFileMethodInliner::DexFileMethodInliner()
@@ -491,11 +581,19 @@
return backend->GenInlinedCharAt(info);
case kIntrinsicCompareTo:
return backend->GenInlinedStringCompareTo(info);
+ case kIntrinsicGetCharsNoCheck:
+ return backend->GenInlinedStringGetCharsNoCheck(info);
case kIntrinsicIsEmptyOrLength:
return backend->GenInlinedStringIsEmptyOrLength(
info, intrinsic.d.data & kIntrinsicFlagIsEmpty);
case kIntrinsicIndexOf:
return backend->GenInlinedIndexOf(info, intrinsic.d.data & kIntrinsicFlagBase0);
+ case kIntrinsicNewStringFromBytes:
+ return backend->GenInlinedStringFactoryNewStringFromBytes(info);
+ case kIntrinsicNewStringFromChars:
+ return backend->GenInlinedStringFactoryNewStringFromChars(info);
+ case kIntrinsicNewStringFromString:
+ return backend->GenInlinedStringFactoryNewStringFromString(info);
case kIntrinsicCurrentThread:
return backend->GenInlinedCurrentThread(info);
case kIntrinsicPeek:
@@ -574,6 +672,8 @@
move_result = mir_graph->FindMoveResult(bb, invoke);
result = GenInlineIPut(mir_graph, bb, invoke, move_result, method);
break;
+ case kInlineStringInit:
+ return false;
default:
LOG(FATAL) << "Unexpected inline op: " << method.opcode;
break;
@@ -921,4 +1021,21 @@
return true;
}
+uint32_t DexFileMethodInliner::GetOffsetForStringInit(uint32_t method_index, size_t pointer_size) {
+ ReaderMutexLock mu(Thread::Current(), lock_);
+ auto it = inline_methods_.find(method_index);
+ if (it != inline_methods_.end() && (it->second.opcode == kInlineStringInit)) {
+ uint32_t string_init_base_offset = Thread::QuickEntryPointOffsetWithSize(
+ OFFSETOF_MEMBER(QuickEntryPoints, pNewEmptyString), pointer_size);
+ return string_init_base_offset + it->second.d.data * pointer_size;
+ }
+ return 0;
+}
+
+bool DexFileMethodInliner::IsStringInitMethodIndex(uint32_t method_index) {
+ ReaderMutexLock mu(Thread::Current(), lock_);
+ auto it = inline_methods_.find(method_index);
+ return (it != inline_methods_.end()) && (it->second.opcode == kInlineStringInit);
+}
+
} // namespace art
diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h
index d1e5621..26b41bf 100644
--- a/compiler/dex/quick/dex_file_method_inliner.h
+++ b/compiler/dex/quick/dex_file_method_inliner.h
@@ -96,6 +96,17 @@
LOCKS_EXCLUDED(lock_);
/**
+ * Gets the thread pointer entrypoint offset for a string init method index and pointer size.
+ */
+ uint32_t GetOffsetForStringInit(uint32_t method_index, size_t pointer_size)
+ LOCKS_EXCLUDED(lock_);
+
+ /**
+ * Check whether a particular method index is a string init.
+ */
+ bool IsStringInitMethodIndex(uint32_t method_index) LOCKS_EXCLUDED(lock_);
+
+ /**
* To avoid multiple lookups of a class by its descriptor, we cache its
* type index in the IndexCache. These are the indexes into the IndexCache
* class_indexes array.
@@ -111,9 +122,15 @@
kClassCacheFloat,
kClassCacheDouble,
kClassCacheVoid,
+ kClassCacheJavaLangByteArray,
+ kClassCacheJavaLangCharArray,
+ kClassCacheJavaLangIntArray,
kClassCacheJavaLangObject,
kClassCacheJavaLangRefReference,
kClassCacheJavaLangString,
+ kClassCacheJavaLangStringBuffer,
+ kClassCacheJavaLangStringBuilder,
+ kClassCacheJavaLangStringFactory,
kClassCacheJavaLangDouble,
kClassCacheJavaLangFloat,
kClassCacheJavaLangInteger,
@@ -122,10 +139,10 @@
kClassCacheJavaLangMath,
kClassCacheJavaLangStrictMath,
kClassCacheJavaLangThread,
+ kClassCacheJavaNioCharsetCharset,
kClassCacheLibcoreIoMemory,
kClassCacheSunMiscUnsafe,
kClassCacheJavaLangSystem,
- kClassCacheJavaLangCharArray,
kClassCacheLast
};
@@ -153,9 +170,14 @@
kNameCacheReferenceGetReferent,
kNameCacheCharAt,
kNameCacheCompareTo,
+ kNameCacheGetCharsNoCheck,
kNameCacheIsEmpty,
kNameCacheIndexOf,
kNameCacheLength,
+ kNameCacheInit,
+ kNameCacheNewStringFromBytes,
+ kNameCacheNewStringFromChars,
+ kNameCacheNewStringFromString,
kNameCacheCurrentThread,
kNameCachePeekByte,
kNameCachePeekIntNative,
@@ -230,6 +252,26 @@
kProtoCacheObjectJ_Object,
kProtoCacheObjectJObject_V,
kProtoCacheCharArrayICharArrayII_V,
+ kProtoCacheIICharArrayI_V,
+ kProtoCacheByteArrayIII_String,
+ kProtoCacheIICharArray_String,
+ kProtoCacheString_String,
+ kProtoCache_V,
+ kProtoCacheByteArray_V,
+ kProtoCacheByteArrayI_V,
+ kProtoCacheByteArrayII_V,
+ kProtoCacheByteArrayIII_V,
+ kProtoCacheByteArrayIIString_V,
+ kProtoCacheByteArrayString_V,
+ kProtoCacheByteArrayIICharset_V,
+ kProtoCacheByteArrayCharset_V,
+ kProtoCacheCharArray_V,
+ kProtoCacheCharArrayII_V,
+ kProtoCacheIICharArray_V,
+ kProtoCacheIntArrayII_V,
+ kProtoCacheString_V,
+ kProtoCacheStringBuffer_V,
+ kProtoCacheStringBuilder_V,
kProtoCacheLast
};
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 1eb3a5f..ab011fc 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -375,6 +375,18 @@
CallHelper(r_tgt, trampoline, safepoint_pc);
}
+void Mir2Lir::CallRuntimeHelperRegLocationRegLocationRegLocationRegLocation(
+ QuickEntrypointEnum trampoline, RegLocation arg0, RegLocation arg1, RegLocation arg2,
+ RegLocation arg3, bool safepoint_pc) {
+ RegStorage r_tgt = CallHelperSetup(trampoline);
+ LoadValueDirectFixed(arg0, TargetReg(kArg0, arg0));
+ LoadValueDirectFixed(arg1, TargetReg(kArg1, arg1));
+ LoadValueDirectFixed(arg2, TargetReg(kArg2, arg2));
+ LoadValueDirectFixed(arg3, TargetReg(kArg3, arg3));
+ ClobberCallerSave();
+ CallHelper(r_tgt, trampoline, safepoint_pc);
+}
+
/*
* If there are any ins passed in registers that have not been promoted
* to a callee-save register, flush them to the frame. Perform initial
@@ -966,14 +978,10 @@
}
bool Mir2Lir::GenInlinedCharAt(CallInfo* info) {
- // Location of reference to data array
+ // Location of char array data
int value_offset = mirror::String::ValueOffset().Int32Value();
// Location of count
int count_offset = mirror::String::CountOffset().Int32Value();
- // Starting offset within data array
- int offset_offset = mirror::String::OffsetOffset().Int32Value();
- // Start of char data with array_
- int data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value();
RegLocation rl_obj = info->args[0];
RegLocation rl_idx = info->args[1];
@@ -983,38 +991,21 @@
GenNullCheck(rl_obj.reg, info->opt_flags);
bool range_check = (!(info->opt_flags & MIR_IGNORE_RANGE_CHECK));
LIR* range_check_branch = nullptr;
- RegStorage reg_off;
- RegStorage reg_ptr;
- reg_off = AllocTemp();
- reg_ptr = AllocTempRef();
if (range_check) {
reg_max = AllocTemp();
Load32Disp(rl_obj.reg, count_offset, reg_max);
MarkPossibleNullPointerException(info->opt_flags);
- }
- Load32Disp(rl_obj.reg, offset_offset, reg_off);
- MarkPossibleNullPointerException(info->opt_flags);
- LoadRefDisp(rl_obj.reg, value_offset, reg_ptr, kNotVolatile);
- if (range_check) {
- // Set up a slow path to allow retry in case of bounds violation */
+ // Set up a slow path to allow retry in case of bounds violation
OpRegReg(kOpCmp, rl_idx.reg, reg_max);
FreeTemp(reg_max);
range_check_branch = OpCondBranch(kCondUge, nullptr);
}
- OpRegImm(kOpAdd, reg_ptr, data_offset);
- if (rl_idx.is_const) {
- OpRegImm(kOpAdd, reg_off, mir_graph_->ConstantValue(rl_idx.orig_sreg));
- } else {
- OpRegReg(kOpAdd, reg_off, rl_idx.reg);
- }
+ RegStorage reg_ptr = AllocTempRef();
+ OpRegRegImm(kOpAdd, reg_ptr, rl_obj.reg, value_offset);
FreeTemp(rl_obj.reg);
- if (rl_idx.location == kLocPhysReg) {
- FreeTemp(rl_idx.reg);
- }
RegLocation rl_dest = InlineTarget(info);
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
- LoadBaseIndexed(reg_ptr, reg_off, rl_result.reg, 1, kUnsignedHalf);
- FreeTemp(reg_off);
+ LoadBaseIndexed(reg_ptr, rl_idx.reg, rl_result.reg, 1, kUnsignedHalf);
FreeTemp(reg_ptr);
StoreValue(rl_dest, rl_result);
if (range_check) {
@@ -1025,6 +1016,59 @@
return true;
}
+bool Mir2Lir::GenInlinedStringGetCharsNoCheck(CallInfo* info) {
+ if (cu_->instruction_set == kMips) {
+ // TODO - add Mips implementation
+ return false;
+ }
+ size_t char_component_size = Primitive::ComponentSize(Primitive::kPrimChar);
+ // Location of data in char array buffer
+ int data_offset = mirror::Array::DataOffset(char_component_size).Int32Value();
+ // Location of char array data in string
+ int value_offset = mirror::String::ValueOffset().Int32Value();
+
+ RegLocation rl_obj = info->args[0];
+ RegLocation rl_start = info->args[1];
+ RegLocation rl_end = info->args[2];
+ RegLocation rl_buffer = info->args[3];
+ RegLocation rl_index = info->args[4];
+
+ ClobberCallerSave();
+ LockCallTemps(); // Using fixed registers
+ RegStorage reg_dst_ptr = TargetReg(kArg0, kRef);
+ RegStorage reg_src_ptr = TargetReg(kArg1, kRef);
+ RegStorage reg_length = TargetReg(kArg2, kNotWide);
+ RegStorage reg_tmp = TargetReg(kArg3, kNotWide);
+ RegStorage reg_tmp_ptr = RegStorage(RegStorage::k64BitSolo, reg_tmp.GetRawBits() & RegStorage::kRegTypeMask);
+
+ LoadValueDirectFixed(rl_buffer, reg_dst_ptr);
+ OpRegImm(kOpAdd, reg_dst_ptr, data_offset);
+ LoadValueDirectFixed(rl_index, reg_tmp);
+ OpRegRegImm(kOpLsl, reg_tmp, reg_tmp, 1);
+ OpRegReg(kOpAdd, reg_dst_ptr, cu_->instruction_set == kArm64 ? reg_tmp_ptr : reg_tmp);
+
+ LoadValueDirectFixed(rl_start, reg_tmp);
+ LoadValueDirectFixed(rl_end, reg_length);
+ OpRegReg(kOpSub, reg_length, reg_tmp);
+ OpRegRegImm(kOpLsl, reg_length, reg_length, 1);
+ LoadValueDirectFixed(rl_obj, reg_src_ptr);
+
+ OpRegImm(kOpAdd, reg_src_ptr, value_offset);
+ OpRegRegImm(kOpLsl, reg_tmp, reg_tmp, 1);
+ OpRegReg(kOpAdd, reg_src_ptr, cu_->instruction_set == kArm64 ? reg_tmp_ptr : reg_tmp);
+
+ RegStorage r_tgt;
+ if (cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64) {
+ r_tgt = LoadHelper(kQuickMemcpy);
+ } else {
+ r_tgt = RegStorage::InvalidReg();
+ }
+ // NOTE: not a safepoint
+ CallHelper(r_tgt, kQuickMemcpy, false, true);
+
+ return true;
+}
+
// Generates an inlined String.is_empty or String.length.
bool Mir2Lir::GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty) {
if (cu_->instruction_set == kMips || cu_->instruction_set == kMips64) {
@@ -1058,6 +1102,58 @@
return true;
}
+bool Mir2Lir::GenInlinedStringFactoryNewStringFromBytes(CallInfo* info) {
+ if (cu_->instruction_set == kMips) {
+ // TODO - add Mips implementation
+ return false;
+ }
+ RegLocation rl_data = info->args[0];
+ RegLocation rl_high = info->args[1];
+ RegLocation rl_offset = info->args[2];
+ RegLocation rl_count = info->args[3];
+ rl_data = LoadValue(rl_data, kRefReg);
+ LIR* data_null_check_branch = OpCmpImmBranch(kCondEq, rl_data.reg, 0, nullptr);
+ AddIntrinsicSlowPath(info, data_null_check_branch);
+ CallRuntimeHelperRegLocationRegLocationRegLocationRegLocation(
+ kQuickAllocStringFromBytes, rl_data, rl_high, rl_offset, rl_count, true);
+ RegLocation rl_return = GetReturn(kRefReg);
+ RegLocation rl_dest = InlineTarget(info);
+ StoreValue(rl_dest, rl_return);
+ return true;
+}
+
+bool Mir2Lir::GenInlinedStringFactoryNewStringFromChars(CallInfo* info) {
+ if (cu_->instruction_set == kMips) {
+ // TODO - add Mips implementation
+ return false;
+ }
+ RegLocation rl_offset = info->args[0];
+ RegLocation rl_count = info->args[1];
+ RegLocation rl_data = info->args[2];
+ CallRuntimeHelperRegLocationRegLocationRegLocation(
+ kQuickAllocStringFromChars, rl_offset, rl_count, rl_data, true);
+ RegLocation rl_return = GetReturn(kRefReg);
+ RegLocation rl_dest = InlineTarget(info);
+ StoreValue(rl_dest, rl_return);
+ return true;
+}
+
+bool Mir2Lir::GenInlinedStringFactoryNewStringFromString(CallInfo* info) {
+ if (cu_->instruction_set == kMips) {
+ // TODO - add Mips implementation
+ return false;
+ }
+ RegLocation rl_string = info->args[0];
+ rl_string = LoadValue(rl_string, kRefReg);
+ LIR* string_null_check_branch = OpCmpImmBranch(kCondEq, rl_string.reg, 0, nullptr);
+ AddIntrinsicSlowPath(info, string_null_check_branch);
+ CallRuntimeHelperRegLocation(kQuickAllocStringFromString, rl_string, true);
+ RegLocation rl_return = GetReturn(kRefReg);
+ RegLocation rl_dest = InlineTarget(info);
+ StoreValue(rl_dest, rl_return);
+ return true;
+}
+
bool Mir2Lir::GenInlinedReverseBytes(CallInfo* info, OpSize size) {
if (cu_->instruction_set == kMips || cu_->instruction_set == kMips64) {
// TODO: add Mips and Mips64 implementations.
@@ -1451,9 +1547,22 @@
LockCallTemps();
const MirMethodLoweringInfo& method_info = mir_graph_->GetMethodLoweringInfo(info->mir);
+ MethodReference target_method = method_info.GetTargetMethod();
cu_->compiler_driver->ProcessedInvoke(method_info.GetInvokeType(), method_info.StatsFlags());
InvokeType original_type = static_cast<InvokeType>(method_info.GetInvokeType());
info->type = method_info.GetSharpType();
+ bool is_string_init = false;
+ if (method_info.IsSpecial()) {
+ DexFileMethodInliner* inliner = cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(
+ target_method.dex_file);
+ if (inliner->IsStringInitMethodIndex(target_method.dex_method_index)) {
+ is_string_init = true;
+ size_t pointer_size = GetInstructionSetPointerSize(cu_->instruction_set);
+ info->string_init_offset = inliner->GetOffsetForStringInit(target_method.dex_method_index,
+ pointer_size);
+ info->type = kStatic;
+ }
+ }
bool fast_path = method_info.FastPath();
bool skip_this;
@@ -1478,7 +1587,6 @@
next_call_insn = fast_path ? NextVCallInsn : NextVCallInsnSP;
skip_this = fast_path;
}
- MethodReference target_method = method_info.GetTargetMethod();
call_state = GenDalvikArgs(info, call_state, p_null_ck,
next_call_insn, target_method, method_info.VTableIndex(),
method_info.DirectCode(), method_info.DirectMethod(),
@@ -1495,7 +1603,7 @@
FreeCallTemps();
if (info->result.location != kLocInvalid) {
// We have a following MOVE_RESULT - do it now.
- RegisterClass reg_class =
+ RegisterClass reg_class = is_string_init ? kRefReg :
ShortyToRegClass(mir_graph_->GetShortyFromMethodReference(info->method_ref)[0]);
if (info->result.wide) {
RegLocation ret_loc = GetReturnWide(reg_class);
diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc
index 39b9cc7..3d25384 100644
--- a/compiler/dex/quick/mips/call_mips.cc
+++ b/compiler/dex/quick/mips/call_mips.cc
@@ -20,7 +20,9 @@
#include "base/logging.h"
#include "dex/mir_graph.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "dex/quick/mir_to_lir-inl.h"
+#include "driver/compiler_driver.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "gc/accounting/card_table.h"
#include "mips_lir.h"
@@ -397,11 +399,28 @@
* Bit of a hack here - in the absence of a real scheduling pass,
* emit the next instruction in static & direct invoke sequences.
*/
-static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSED, int state,
+static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, int state,
const MethodReference& target_method, uint32_t, uintptr_t direct_code,
uintptr_t direct_method, InvokeType type) {
Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
- if (direct_code != 0 && direct_method != 0) {
+ if (info->string_init_offset != 0) {
+ RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
+ switch (state) {
+ case 0: { // Grab target method* from thread pointer
+ cg->LoadRefDisp(cg->TargetPtrReg(kSelf), info->string_init_offset, arg0_ref, kNotVolatile);
+ break;
+ }
+ case 1: // Grab the code from the method*
+ if (direct_code == 0) {
+ int32_t offset = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ InstructionSetPointerSize(cu->instruction_set)).Int32Value();
+ cg->LoadWordDisp(arg0_ref, offset, cg->TargetPtrReg(kInvokeTgt));
+ }
+ break;
+ default:
+ return -1;
+ }
+ } else if (direct_code != 0 && direct_method != 0) {
switch (state) {
case 0: // Get the current Method* [sets kArg0]
if (direct_code != static_cast<uintptr_t>(-1)) {
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 6f227fc..a07274f 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -897,6 +897,10 @@
RegLocation arg0, RegLocation arg1,
RegLocation arg2,
bool safepoint_pc);
+ void CallRuntimeHelperRegLocationRegLocationRegLocationRegLocation(
+ QuickEntrypointEnum trampoline, RegLocation arg0, RegLocation arg1,
+ RegLocation arg2, RegLocation arg3, bool safepoint_pc);
+
void GenInvoke(CallInfo* info);
void GenInvokeNoInline(CallInfo* info);
virtual NextCallInsn GetNextSDCallInsn() = 0;
@@ -937,7 +941,11 @@
bool GenInlinedReferenceGetReferent(CallInfo* info);
virtual bool GenInlinedCharAt(CallInfo* info);
+ bool GenInlinedStringGetCharsNoCheck(CallInfo* info);
bool GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty);
+ bool GenInlinedStringFactoryNewStringFromBytes(CallInfo* info);
+ bool GenInlinedStringFactoryNewStringFromChars(CallInfo* info);
+ bool GenInlinedStringFactoryNewStringFromString(CallInfo* info);
virtual bool GenInlinedReverseBits(CallInfo* info, OpSize size);
bool GenInlinedReverseBytes(CallInfo* info, OpSize size);
virtual bool GenInlinedAbsInt(CallInfo* info);
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index e2364d8..2495757 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -19,6 +19,7 @@
#include "codegen_x86.h"
#include "base/logging.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
@@ -343,11 +344,20 @@
int X86Mir2Lir::X86NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
int state, const MethodReference& target_method,
uint32_t,
- uintptr_t direct_code, uintptr_t direct_method,
+ uintptr_t direct_code ATTRIBUTE_UNUSED, uintptr_t direct_method,
InvokeType type) {
- UNUSED(info, direct_code);
X86Mir2Lir* cg = static_cast<X86Mir2Lir*>(cu->cg.get());
- if (direct_method != 0) {
+ if (info->string_init_offset != 0) {
+ RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
+ switch (state) {
+ case 0: { // Grab target method* from thread pointer
+ cg->NewLIR2(kX86Mov32RT, arg0_ref.GetReg(), info->string_init_offset);
+ break;
+ }
+ default:
+ return -1;
+ }
+ } else if (direct_method != 0) {
switch (state) {
case 0: // Get the current Method* [sets kArg0]
if (direct_method != static_cast<uintptr_t>(-1)) {
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index b460379..2f211da 100755
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -1302,10 +1302,6 @@
int value_offset = mirror::String::ValueOffset().Int32Value();
// Location of count within the String object.
int count_offset = mirror::String::CountOffset().Int32Value();
- // Starting offset within data array.
- int offset_offset = mirror::String::OffsetOffset().Int32Value();
- // Start of char data with array_.
- int data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value();
// Compute the number of words to search in to rCX.
Load32Disp(rs_rDX, count_offset, rs_rCX);
@@ -1388,15 +1384,13 @@
// Load the address of the string into EDI.
// In case of start index we have to add the address to existing value in EDI.
- // The string starts at VALUE(String) + 2 * OFFSET(String) + DATA_OFFSET.
if (zero_based || (!zero_based && rl_start.is_const && start_value == 0)) {
- Load32Disp(rs_rDX, offset_offset, rs_rDI);
+ OpRegRegImm(kOpAdd, rs_rDI, rs_rDX, value_offset);
} else {
- OpRegMem(kOpAdd, rs_rDI, rs_rDX, offset_offset);
+ OpRegImm(kOpLsl, rs_rDI, 1);
+ OpRegReg(kOpAdd, rs_rDI, rs_rDX);
+ OpRegImm(kOpAdd, rs_rDI, value_offset);
}
- OpRegImm(kOpLsl, rs_rDI, 1);
- OpRegMem(kOpAdd, rs_rDI, rs_rDX, value_offset);
- OpRegImm(kOpAdd, rs_rDI, data_offset);
// EDI now contains the start of the string to be searched.
// We are all prepared to do the search for the character.
@@ -2423,24 +2417,15 @@
int value_offset = mirror::String::ValueOffset().Int32Value();
// Location of count
int count_offset = mirror::String::CountOffset().Int32Value();
- // Starting offset within data array
- int offset_offset = mirror::String::OffsetOffset().Int32Value();
- // Start of char data with array_
- int data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value();
RegLocation rl_obj = info->args[0];
RegLocation rl_idx = info->args[1];
rl_obj = LoadValue(rl_obj, kRefReg);
- // X86 wants to avoid putting a constant index into a register.
- if (!rl_idx.is_const) {
- rl_idx = LoadValue(rl_idx, kCoreReg);
- }
+ rl_idx = LoadValue(rl_idx, kCoreReg);
RegStorage reg_max;
GenNullCheck(rl_obj.reg, info->opt_flags);
bool range_check = (!(info->opt_flags & MIR_IGNORE_RANGE_CHECK));
LIR* range_check_branch = nullptr;
- RegStorage reg_off;
- RegStorage reg_ptr;
if (range_check) {
// On x86, we can compare to memory directly
// Set up a launch pad to allow retry in case of bounds violation */
@@ -2456,24 +2441,11 @@
range_check_branch = OpCondBranch(kCondUge, nullptr);
}
}
- reg_off = AllocTemp();
- reg_ptr = AllocTempRef();
- Load32Disp(rl_obj.reg, offset_offset, reg_off);
- LoadRefDisp(rl_obj.reg, value_offset, reg_ptr, kNotVolatile);
- if (rl_idx.is_const) {
- OpRegImm(kOpAdd, reg_off, mir_graph_->ConstantValue(rl_idx.orig_sreg));
- } else {
- OpRegReg(kOpAdd, reg_off, rl_idx.reg);
- }
- FreeTemp(rl_obj.reg);
- if (rl_idx.location == kLocPhysReg) {
- FreeTemp(rl_idx.reg);
- }
RegLocation rl_dest = InlineTarget(info);
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
- LoadBaseIndexedDisp(reg_ptr, reg_off, 1, data_offset, rl_result.reg, kUnsignedHalf);
- FreeTemp(reg_off);
- FreeTemp(reg_ptr);
+ LoadBaseIndexedDisp(rl_obj.reg, rl_idx.reg, 1, value_offset, rl_result.reg, kUnsignedHalf);
+ FreeTemp(rl_idx.reg);
+ FreeTemp(rl_obj.reg);
StoreValue(rl_dest, rl_result);
if (range_check) {
DCHECK(range_check_branch != nullptr);