ART: Intrinsic implementation for java.lang.System.arraycopy.
Implements intrinsic for java.lang.System.arraycopy(char[], int, char[], int, int) -
this method is internal to android class libraries and used in such classes as StringBuffer and
StringBuilder. It is not possible to call it from application code. The intrinsic for
this method is implemented as inline method (assembly code is generated manually).
The intrinsic is x86 32 bit only.
Change-Id: Id1b1e0a20d5f6d5f5ebfe1fdc2447b6d8a515432
Signed-off-by: Daniil Sokolov <daniil.y.sokolov@intel.com>
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index b699bd3..e240d73 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -84,6 +84,8 @@
"Ljava/lang/Thread;", // kClassCacheJavaLangThread
"Llibcore/io/Memory;", // kClassCacheLibcoreIoMemory
"Lsun/misc/Unsafe;", // kClassCacheSunMiscUnsafe
+ "Ljava/lang/System;", // kClassCacheJavaLangSystem
+ "[C" // kClassCacheJavaLangCharArray
};
const char* const DexFileMethodInliner::kNameCacheNames[] = {
@@ -129,6 +131,7 @@
"putObject", // kNameCachePutObject
"putObjectVolatile", // kNameCachePutObjectVolatile
"putOrderedObject", // kNameCachePutOrderedObject
+ "arraycopy", // kNameCacheArrayCopy
};
const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = {
@@ -204,6 +207,9 @@
// kProtoCacheObjectJObject_V
{ kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong,
kClassCacheJavaLangObject } },
+ // kProtoCacheCharArrayICharArrayII_V
+ { kClassCacheVoid, 5, {kClassCacheJavaLangCharArray, kClassCacheInt,
+ kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheInt}}
};
const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods[] = {
@@ -291,6 +297,10 @@
UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject),
#undef UNSAFE_GET_PUT
+ INTRINSIC(JavaLangSystem, ArrayCopy, CharArrayICharArrayII_V , kIntrinsicSystemArrayCopyCharArray,
+ 0),
+
+
#undef INTRINSIC
};
@@ -387,6 +397,8 @@
intrinsic.d.data & kIntrinsicFlagIsObject,
intrinsic.d.data & kIntrinsicFlagIsVolatile,
intrinsic.d.data & kIntrinsicFlagIsOrdered);
+ case kIntrinsicSystemArrayCopyCharArray:
+ return backend->GenInlinedArrayCopyCharArray(info);
default:
LOG(FATAL) << "Unexpected intrinsic opcode: " << intrinsic.opcode;
return false; // avoid warning "control reaches end of non-void function"
diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h
index c7a3b83..5b3b104 100644
--- a/compiler/dex/quick/dex_file_method_inliner.h
+++ b/compiler/dex/quick/dex_file_method_inliner.h
@@ -118,6 +118,8 @@
kClassCacheJavaLangThread,
kClassCacheLibcoreIoMemory,
kClassCacheSunMiscUnsafe,
+ kClassCacheJavaLangSystem,
+ kClassCacheJavaLangCharArray,
kClassCacheLast
};
@@ -170,6 +172,7 @@
kNameCachePutObject,
kNameCachePutObjectVolatile,
kNameCachePutOrderedObject,
+ kNameCacheArrayCopy,
kNameCacheLast
};
@@ -214,6 +217,7 @@
kProtoCacheObjectJJ_V,
kProtoCacheObjectJ_Object,
kProtoCacheObjectJObject_V,
+ kProtoCacheCharArrayICharArrayII_V,
kProtoCacheLast
};
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 4cc1375..02f39ac 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -1527,6 +1527,11 @@
return true;
}
+bool Mir2Lir::GenInlinedArrayCopyCharArray(CallInfo* info) {
+ return false;
+}
+
+
/*
* Fast String.indexOf(I) & (II). Tests for simple case of char <= 0xFFFF,
* otherwise bails to standard library code.
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index cfcc5c8..4885501 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -992,6 +992,7 @@
virtual bool GenInlinedAbsDouble(CallInfo* info);
bool GenInlinedFloatCvt(CallInfo* info);
bool GenInlinedDoubleCvt(CallInfo* info);
+ virtual bool GenInlinedArrayCopyCharArray(CallInfo* info);
virtual bool GenInlinedIndexOf(CallInfo* info, bool zero_based);
bool GenInlinedStringCompareTo(CallInfo* info);
bool GenInlinedCurrentThread(CallInfo* info);
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index 123fe90..55e5993 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -517,6 +517,7 @@
* @returns true if a register is byte addressable.
*/
bool IsByteRegister(RegStorage reg);
+ bool GenInlinedArrayCopyCharArray(CallInfo* info) OVERRIDE;
/*
* @brief generate inline code for fast case of Strng.indexOf.
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index 72e47d0..43882c2 100755
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -1023,6 +1023,123 @@
Mir2Lir::InstallLiteralPools();
}
+bool X86Mir2Lir::GenInlinedArrayCopyCharArray(CallInfo* info) {
+ if (cu_->target64) {
+ // TODO: Implement ArrayCOpy intrinsic for x86_64
+ return false;
+ }
+
+ RegLocation rl_src = info->args[0];
+ RegLocation rl_srcPos = info->args[1];
+ RegLocation rl_dst = info->args[2];
+ RegLocation rl_dstPos = info->args[3];
+ RegLocation rl_length = info->args[4];
+ if (rl_srcPos.is_const && (mir_graph_->ConstantValue(rl_srcPos) < 0)) {
+ return false;
+ }
+ if (rl_dstPos.is_const && (mir_graph_->ConstantValue(rl_dstPos) < 0)) {
+ return false;
+ }
+ ClobberCallerSave();
+ LockCallTemps(); // Using fixed registers
+ LoadValueDirectFixed(rl_src , rs_rAX);
+ LoadValueDirectFixed(rl_dst , rs_rCX);
+ LIR* src_dst_same = OpCmpBranch(kCondEq, rs_rAX , rs_rCX, nullptr);
+ LIR* src_null_branch = OpCmpImmBranch(kCondEq, rs_rAX , 0, nullptr);
+ LIR* dst_null_branch = OpCmpImmBranch(kCondEq, rs_rCX , 0, nullptr);
+ LoadValueDirectFixed(rl_length , rs_rDX);
+ LIR* len_negative = OpCmpImmBranch(kCondLt, rs_rDX , 0, nullptr);
+ LIR* len_too_big = OpCmpImmBranch(kCondGt, rs_rDX , 128, nullptr);
+ LoadValueDirectFixed(rl_src , rs_rAX);
+ LoadWordDisp(rs_rAX , mirror::Array::LengthOffset().Int32Value(), rs_rAX);
+ LIR* src_bad_len = nullptr;
+ LIR* srcPos_negative = nullptr;
+ if (!rl_srcPos.is_const) {
+ LoadValueDirectFixed(rl_srcPos , rs_rBX);
+ srcPos_negative = OpCmpImmBranch(kCondLt, rs_rBX , 0, nullptr);
+ OpRegReg(kOpAdd, rs_rBX, rs_rDX);
+ src_bad_len = OpCmpBranch(kCondLt, rs_rAX , rs_rBX, nullptr);
+ } else {
+ int pos_val = mir_graph_->ConstantValue(rl_srcPos.orig_sreg);
+ if (pos_val == 0) {
+ src_bad_len = OpCmpBranch(kCondLt, rs_rAX , rs_rDX, nullptr);
+ } else {
+ OpRegRegImm(kOpAdd, rs_rBX, rs_rDX, pos_val);
+ src_bad_len = OpCmpBranch(kCondLt, rs_rAX , rs_rBX, nullptr);
+ }
+ }
+ LIR* dstPos_negative = nullptr;
+ LIR* dst_bad_len = nullptr;
+ LoadValueDirectFixed(rl_dst, rs_rAX);
+ LoadWordDisp(rs_rAX, mirror::Array::LengthOffset().Int32Value(), rs_rAX);
+ if (!rl_dstPos.is_const) {
+ LoadValueDirectFixed(rl_dstPos , rs_rBX);
+ dstPos_negative = OpCmpImmBranch(kCondLt, rs_rBX , 0, nullptr);
+ OpRegRegReg(kOpAdd, rs_rBX, rs_rBX, rs_rDX);
+ dst_bad_len = OpCmpBranch(kCondLt, rs_rAX , rs_rBX, nullptr);
+ } else {
+ int pos_val = mir_graph_->ConstantValue(rl_dstPos.orig_sreg);
+ if (pos_val == 0) {
+ dst_bad_len = OpCmpBranch(kCondLt, rs_rAX , rs_rDX, nullptr);
+ } else {
+ OpRegRegImm(kOpAdd, rs_rBX, rs_rDX, pos_val);
+ dst_bad_len = OpCmpBranch(kCondLt, rs_rAX , rs_rBX, nullptr);
+ }
+ }
+ // everything is checked now
+ LoadValueDirectFixed(rl_src , rs_rAX);
+ LoadValueDirectFixed(rl_dst , rs_rBX);
+ LoadValueDirectFixed(rl_srcPos , rs_rCX);
+ NewLIR5(kX86Lea32RA, rs_rAX.GetReg(), rs_rAX.GetReg(),
+ rs_rCX.GetReg() , 1, mirror::Array::DataOffset(2).Int32Value());
+ // RAX now holds the address of the first src element to be copied
+
+ LoadValueDirectFixed(rl_dstPos , rs_rCX);
+ NewLIR5(kX86Lea32RA, rs_rBX.GetReg(), rs_rBX.GetReg(),
+ rs_rCX.GetReg() , 1, mirror::Array::DataOffset(2).Int32Value() );
+ // RBX now holds the address of the first dst element to be copied
+
+ // check if the number of elements to be copied is odd or even. If odd
+ // then copy the first element (so that the remaining number of elements
+ // is even).
+ LoadValueDirectFixed(rl_length , rs_rCX);
+ OpRegImm(kOpAnd, rs_rCX, 1);
+ LIR* jmp_to_begin_loop = OpCmpImmBranch(kCondEq, rs_rCX, 0, nullptr);
+ OpRegImm(kOpSub, rs_rDX, 1);
+ LoadBaseIndexedDisp(rs_rAX, rs_rDX, 1, 0, rs_rCX, kSignedHalf);
+ StoreBaseIndexedDisp(rs_rBX, rs_rDX, 1, 0, rs_rCX, kSignedHalf);
+
+ // since the remaining number of elements is even, we will copy by
+ // two elements at a time.
+ LIR *beginLoop = NewLIR0(kPseudoTargetLabel);
+ LIR* jmp_to_ret = OpCmpImmBranch(kCondEq, rs_rDX , 0, nullptr);
+ OpRegImm(kOpSub, rs_rDX, 2);
+ LoadBaseIndexedDisp(rs_rAX, rs_rDX, 1, 0, rs_rCX, kSingle);
+ StoreBaseIndexedDisp(rs_rBX, rs_rDX, 1, 0, rs_rCX, kSingle);
+ OpUnconditionalBranch(beginLoop);
+ LIR *check_failed = NewLIR0(kPseudoTargetLabel);
+ LIR* launchpad_branch = OpUnconditionalBranch(nullptr);
+ LIR *return_point = NewLIR0(kPseudoTargetLabel);
+ jmp_to_ret->target = return_point;
+ jmp_to_begin_loop->target = beginLoop;
+ src_dst_same->target = check_failed;
+ len_negative->target = check_failed;
+ len_too_big->target = check_failed;
+ src_null_branch->target = check_failed;
+ if (srcPos_negative != nullptr)
+ srcPos_negative ->target = check_failed;
+ if (src_bad_len != nullptr)
+ src_bad_len->target = check_failed;
+ dst_null_branch->target = check_failed;
+ if (dstPos_negative != nullptr)
+ dstPos_negative->target = check_failed;
+ if (dst_bad_len != nullptr)
+ dst_bad_len->target = check_failed;
+ AddIntrinsicSlowPath(info, launchpad_branch, return_point);
+ return true;
+}
+
+
/*
* Fast string.index_of(I) & (II). Inline check for simple case of char <= 0xffff,
* otherwise bails to standard library code.