diff options
| -rw-r--r-- | compiler/optimizing/intrinsics_mips.cc | 194 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/ti_method.cc | 15 | ||||
| -rw-r--r-- | test/904-object-allocation/expected.txt | 10 | ||||
| -rw-r--r-- | test/904-object-allocation/src/art/Test904.java | 10 | ||||
| -rw-r--r-- | test/904-object-allocation/tracking.cc | 27 | ||||
| -rw-r--r-- | test/910-methods/expected.txt | 14 | ||||
| -rw-r--r-- | test/910-methods/src/art/Test910.java | 69 |
7 files changed, 306 insertions, 33 deletions
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 429797976c..41df56b514 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -2940,6 +2940,199 @@ void IntrinsicCodeGeneratorMIPS::VisitMathTanh(HInvoke* invoke) { GenFPToFPCall(invoke, codegen_, kQuickTanh); } +// static void java.lang.System.arraycopy(Object src, int srcPos, +// Object dest, int destPos, +// int length) +void IntrinsicLocationsBuilderMIPS::VisitSystemArrayCopyChar(HInvoke* invoke) { + HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant(); + HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant(); + HIntConstant* length = invoke->InputAt(4)->AsIntConstant(); + + // As long as we are checking, we might as well check to see if the src and dest + // positions are >= 0. + if ((src_pos != nullptr && src_pos->GetValue() < 0) || + (dest_pos != nullptr && dest_pos->GetValue() < 0)) { + // We will have to fail anyways. + return; + } + + // And since we are already checking, check the length too. + if (length != nullptr) { + int32_t len = length->GetValue(); + if (len < 0) { + // Just call as normal. + return; + } + } + + // Okay, it is safe to generate inline code. + LocationSummary* locations = + new (arena_) LocationSummary(invoke, LocationSummary::kCallOnSlowPath, kIntrinsified); + // arraycopy(Object src, int srcPos, Object dest, int destPos, int length). + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetInAt(3, Location::RegisterOrConstant(invoke->InputAt(3))); + locations->SetInAt(4, Location::RegisterOrConstant(invoke->InputAt(4))); + + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); +} + +// Utility routine to verify that "length(input) - pos >= length" +static void EnoughItems(MipsAssembler* assembler, + Register length_input_minus_pos, + Location length, + SlowPathCodeMIPS* slow_path) { + if (length.IsConstant()) { + int32_t length_constant = length.GetConstant()->AsIntConstant()->GetValue(); + + if (IsInt<16>(length_constant)) { + __ Slti(TMP, length_input_minus_pos, length_constant); + __ Bnez(TMP, slow_path->GetEntryLabel()); + } else { + __ LoadConst32(TMP, length_constant); + __ Blt(length_input_minus_pos, TMP, slow_path->GetEntryLabel()); + } + } else { + __ Blt(length_input_minus_pos, length.AsRegister<Register>(), slow_path->GetEntryLabel()); + } +} + +static void CheckPosition(MipsAssembler* assembler, + Location pos, + Register input, + Location length, + SlowPathCodeMIPS* slow_path, + bool length_is_input_length = false) { + // Where is the length in the Array? + const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value(); + + // Calculate length(input) - pos. + if (pos.IsConstant()) { + int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue(); + if (pos_const == 0) { + if (!length_is_input_length) { + // Check that length(input) >= length. + __ LoadFromOffset(kLoadWord, AT, input, length_offset); + EnoughItems(assembler, AT, length, slow_path); + } + } else { + // Check that (length(input) - pos) >= zero. + __ LoadFromOffset(kLoadWord, AT, input, length_offset); + DCHECK_GT(pos_const, 0); + __ Addiu32(AT, AT, -pos_const, TMP); + __ Bltz(AT, slow_path->GetEntryLabel()); + + // Verify that (length(input) - pos) >= length. + EnoughItems(assembler, AT, length, slow_path); + } + } else if (length_is_input_length) { + // The only way the copy can succeed is if pos is zero. + Register pos_reg = pos.AsRegister<Register>(); + __ Bnez(pos_reg, slow_path->GetEntryLabel()); + } else { + // Verify that pos >= 0. + Register pos_reg = pos.AsRegister<Register>(); + __ Bltz(pos_reg, slow_path->GetEntryLabel()); + + // Check that (length(input) - pos) >= zero. + __ LoadFromOffset(kLoadWord, AT, input, length_offset); + __ Subu(AT, AT, pos_reg); + __ Bltz(AT, slow_path->GetEntryLabel()); + + // Verify that (length(input) - pos) >= length. + EnoughItems(assembler, AT, length, slow_path); + } +} + +void IntrinsicCodeGeneratorMIPS::VisitSystemArrayCopyChar(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register src = locations->InAt(0).AsRegister<Register>(); + Location src_pos = locations->InAt(1); + Register dest = locations->InAt(2).AsRegister<Register>(); + Location dest_pos = locations->InAt(3); + Location length = locations->InAt(4); + + MipsLabel loop; + + Register dest_base = locations->GetTemp(0).AsRegister<Register>(); + Register src_base = locations->GetTemp(1).AsRegister<Register>(); + Register count = locations->GetTemp(2).AsRegister<Register>(); + + SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke); + codegen_->AddSlowPath(slow_path); + + // Bail out if the source and destination are the same (to handle overlap). + __ Beq(src, dest, slow_path->GetEntryLabel()); + + // Bail out if the source is null. + __ Beqz(src, slow_path->GetEntryLabel()); + + // Bail out if the destination is null. + __ Beqz(dest, slow_path->GetEntryLabel()); + + // Load length into register for count. + if (length.IsConstant()) { + __ LoadConst32(count, length.GetConstant()->AsIntConstant()->GetValue()); + } else { + // If the length is negative, bail out. + // We have already checked in the LocationsBuilder for the constant case. + __ Bltz(length.AsRegister<Register>(), slow_path->GetEntryLabel()); + + __ Move(count, length.AsRegister<Register>()); + } + + // Validity checks: source. + CheckPosition(assembler, src_pos, src, Location::RegisterLocation(count), slow_path); + + // Validity checks: dest. + CheckPosition(assembler, dest_pos, dest, Location::RegisterLocation(count), slow_path); + + // If count is zero, we're done. + __ Beqz(count, slow_path->GetExitLabel()); + + // Okay, everything checks out. Finally time to do the copy. + // Check assumption that sizeof(Char) is 2 (used in scaling below). + const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar); + DCHECK_EQ(char_size, 2u); + + const size_t char_shift = Primitive::ComponentSizeShift(Primitive::kPrimChar); + + const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value(); + + // Calculate source and destination addresses. + if (src_pos.IsConstant()) { + int32_t src_pos_const = src_pos.GetConstant()->AsIntConstant()->GetValue(); + + __ Addiu32(src_base, src, data_offset + char_size * src_pos_const, TMP); + } else { + __ Addiu32(src_base, src, data_offset, TMP); + __ ShiftAndAdd(src_base, src_pos.AsRegister<Register>(), src_base, char_shift); + } + if (dest_pos.IsConstant()) { + int32_t dest_pos_const = dest_pos.GetConstant()->AsIntConstant()->GetValue(); + + __ Addiu32(dest_base, dest, data_offset + char_size * dest_pos_const, TMP); + } else { + __ Addiu32(dest_base, dest, data_offset, TMP); + __ ShiftAndAdd(dest_base, dest_pos.AsRegister<Register>(), dest_base, char_shift); + } + + __ Bind(&loop); + __ Lh(TMP, src_base, 0); + __ Addiu(src_base, src_base, char_size); + __ Addiu(count, count, -1); + __ Sh(TMP, dest_base, 0); + __ Addiu(dest_base, dest_base, char_size); + __ Bnez(count, &loop); + + __ Bind(slow_path->GetExitLabel()); +} + // Unimplemented intrinsics. UNIMPLEMENTED_INTRINSIC(MIPS, MathCeil) @@ -2951,7 +3144,6 @@ UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLongVolatile); UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASLong) UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent) -UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopy) UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOf); diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc index bc73029f41..01bf21d53e 100644 --- a/runtime/openjdkjvmti/ti_method.cc +++ b/runtime/openjdkjvmti/ti_method.cc @@ -61,8 +61,13 @@ jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED, art::ScopedObjectAccess soa(art::Thread::Current()); if (art_method->IsProxyMethod() || art_method->IsAbstract()) { - // This isn't specified as an error case, so return 0. - *size_ptr = 0; + // Use the shorty. + art::ArtMethod* base_method = art_method->GetInterfaceMethodIfProxy(art::kRuntimePointerSize); + size_t arg_count = art::ArtMethod::NumArgRegisters(base_method->GetShorty()); + if (!base_method->IsStatic()) { + arg_count++; + } + *size_ptr = static_cast<jint>(arg_count); return ERR(NONE); } @@ -203,9 +208,9 @@ jvmtiError MethodUtil::GetMethodLocation(jvmtiEnv* env ATTRIBUTE_UNUSED, art::ScopedObjectAccess soa(art::Thread::Current()); if (art_method->IsProxyMethod() || art_method->IsAbstract()) { - // This isn't specified as an error case, so return 0/0. - *start_location_ptr = 0; - *end_location_ptr = 0; + // This isn't specified as an error case, so return -1/-1 as the RI does. + *start_location_ptr = -1; + *end_location_ptr = -1; return ERR(NONE); } diff --git a/test/904-object-allocation/expected.txt b/test/904-object-allocation/expected.txt index 371d2b7593..fdec470529 100644 --- a/test/904-object-allocation/expected.txt +++ b/test/904-object-allocation/expected.txt @@ -1,8 +1,8 @@ -ObjectAllocated type java.lang.Object/java.lang.Object size 8 -ObjectAllocated type java.lang.Integer/java.lang.Integer size 16 -ObjectAllocated type java.lang.Short/java.lang.Short size 16 +[] +[ObjectAllocated type java.lang.Object/java.lang.Object size 8, ObjectAllocated type java.lang.Integer/java.lang.Integer size 16, ObjectAllocated type java.lang.Short/java.lang.Short size 16] Tracking on same thread -ObjectAllocated type java.lang.Double/java.lang.Double size 16 +[ObjectAllocated type java.lang.Double/java.lang.Double size 16] Tracking on same thread, not disabling tracking -ObjectAllocated type java.lang.Double/java.lang.Double size 16 +[ObjectAllocated type java.lang.Double/java.lang.Double size 16] Tracking on different thread +[] diff --git a/test/904-object-allocation/src/art/Test904.java b/test/904-object-allocation/src/art/Test904.java index 31e0c8c1ae..70a4b9847f 100644 --- a/test/904-object-allocation/src/art/Test904.java +++ b/test/904-object-allocation/src/art/Test904.java @@ -17,6 +17,7 @@ package art; import java.util.ArrayList; +import java.util.Arrays; public class Test904 { public static void run() throws Exception { @@ -48,6 +49,8 @@ public class Test904 { // Enable actual logging callback. setupObjectAllocCallback(true); + System.out.println(Arrays.toString(getTrackingEventMessages())); + enableAllocationTracking(null, true); l.add(new Object()); @@ -65,16 +68,19 @@ public class Test904 { l.add(new Byte((byte)0)); + System.out.println(Arrays.toString(getTrackingEventMessages())); System.out.println("Tracking on same thread"); testThread(l, true, true); l.add(new Byte((byte)0)); + System.out.println(Arrays.toString(getTrackingEventMessages())); System.out.println("Tracking on same thread, not disabling tracking"); testThread(l, true, false); + System.out.println(Arrays.toString(getTrackingEventMessages())); System.out.println("Tracking on different thread"); testThread(l, false, true); @@ -84,6 +90,9 @@ public class Test904 { // Disable actual logging callback and re-enable tracking, so we can keep the event enabled and // check that shutdown works correctly. setupObjectAllocCallback(false); + + System.out.println(Arrays.toString(getTrackingEventMessages())); + enableAllocationTracking(null, true); } @@ -142,4 +151,5 @@ public class Test904 { private static native void setupObjectAllocCallback(boolean enable); private static native void enableAllocationTracking(Thread thread, boolean enable); + private static native String[] getTrackingEventMessages(); } diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc index 8de350b06c..20b53281a2 100644 --- a/test/904-object-allocation/tracking.cc +++ b/test/904-object-allocation/tracking.cc @@ -15,11 +15,13 @@ */ #include <iostream> +#include <mutex> #include <pthread.h> #include <stdio.h> #include <vector> #include "android-base/logging.h" +#include "android-base/stringprintf.h" #include "jni.h" #include "jvmti.h" #include "scoped_local_ref.h" @@ -41,6 +43,9 @@ static std::string GetClassName(JNIEnv* jni_env, jclass cls) { return utf_chars.c_str(); } +static std::mutex gEventsMutex; +static std::vector<std::string> gEvents; + static void JNICALL ObjectAllocated(jvmtiEnv* ti_env ATTRIBUTE_UNUSED, JNIEnv* jni_env, jthread thread ATTRIBUTE_UNUSED, @@ -51,10 +56,11 @@ static void JNICALL ObjectAllocated(jvmtiEnv* ti_env ATTRIBUTE_UNUSED, ScopedLocalRef<jclass> object_klass2(jni_env, jni_env->GetObjectClass(object)); std::string object_klass_descriptor2 = GetClassName(jni_env, object_klass2.get()); - printf("ObjectAllocated type %s/%s size %zu\n", - object_klass_descriptor.c_str(), - object_klass_descriptor2.c_str(), - static_cast<size_t>(size)); + std::lock_guard<std::mutex> guard(gEventsMutex); + gEvents.push_back(android::base::StringPrintf("ObjectAllocated type %s/%s size %zu", + object_klass_descriptor.c_str(), + object_klass_descriptor2.c_str(), + static_cast<size_t>(size))); } extern "C" JNIEXPORT void JNICALL Java_art_Test904_setupObjectAllocCallback( @@ -76,5 +82,18 @@ extern "C" JNIEXPORT void JNICALL Java_art_Test904_enableAllocationTracking( JvmtiErrorToException(env, jvmti_env, ret); } +extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test904_getTrackingEventMessages( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + std::lock_guard<std::mutex> guard(gEventsMutex); + jobjectArray ret = CreateObjectArray(env, + static_cast<jint>(gEvents.size()), + "java/lang/String", + [&](jint i) { + return env->NewStringUTF(gEvents[i].c_str()); + }); + gEvents.clear(); + return ret; +} + } // namespace Test904ObjectAllocation } // namespace art diff --git a/test/910-methods/expected.txt b/test/910-methods/expected.txt index 8e6b6e76d9..c14c6c49e1 100644 --- a/test/910-methods/expected.txt +++ b/test/910-methods/expected.txt @@ -32,19 +32,19 @@ Is synthetic: false interface java.util.List 1025 Max locals: 0 -Argument size: 0 -Location start: 0 -Location end: 0 +Argument size: 2 +Location start: -1 +Location end: -1 Is native: false Is obsolete: false Is synthetic: false [run, ()V, null] -class $Proxy0 +class $Proxy20 17 Max locals: 0 -Argument size: 0 -Location start: 0 -Location end: 0 +Argument size: 1 +Location start: -1 +Location end: -1 Is native: false Is obsolete: false Is synthetic: false diff --git a/test/910-methods/src/art/Test910.java b/test/910-methods/src/art/Test910.java index b3490e9518..aa6d13af9a 100644 --- a/test/910-methods/src/art/Test910.java +++ b/test/910-methods/src/art/Test910.java @@ -39,17 +39,6 @@ public class Test910 { testMethod(findSyntheticMethod(), NestedSynthetic.class, false); } - private static Class<?> proxyClass = null; - - private static Class<?> getProxyClass() throws Exception { - if (proxyClass != null) { - return proxyClass; - } - - proxyClass = Proxy.getProxyClass(Main.class.getClassLoader(), new Class[] { Runnable.class }); - return proxyClass; - } - private static void testMethod(String className, String methodName, Class<?>... types) throws Exception { Class<?> base = Class.forName(className); @@ -145,4 +134,62 @@ public class Test910 { private static native boolean isMethodNative(Method m); private static native boolean isMethodObsolete(Method m); private static native boolean isMethodSynthetic(Method m); + + // We need this machinery for a consistent proxy name. Names of proxy classes include a + // unique number (derived by counting). This means that a simple call to getProxyClass + // depends on the test environment. + // + // To work around this, we assume that at most twenty proxies have been created before + // the test is run, and canonicalize on "$Proxy20". We add infrastructure to create + // as many proxy classes but cycling through subsets of the test-provided interfaces + // I0...I4. + // + // + // (This is made under the judgment that we do not want to have proxy-specific behavior + // for testMethod.) + + private static Class<?> proxyClass = null; + + private static Class<?> getProxyClass() throws Exception { + if (proxyClass != null) { + return proxyClass; + } + + for (int i = 1; i <= 21; i++) { + proxyClass = createProxyClass(i); + String name = proxyClass.getName(); + if (name.equals("$Proxy20")) { + return proxyClass; + } + } + return proxyClass; + } + + private static Class<?> createProxyClass(int i) throws Exception { + int count = Integer.bitCount(i); + Class<?>[] input = new Class<?>[count + 1]; + input[0] = Runnable.class; + int inputIndex = 1; + int bitIndex = 0; + while (i != 0) { + if ((i & 1) != 0) { + input[inputIndex++] = Class.forName("art.Test910$I" + bitIndex); + } + i >>>= 1; + bitIndex++; + } + return Proxy.getProxyClass(Test910.class.getClassLoader(), input); + } + + // Need this for the proxy naming. + public static interface I0 { + } + public static interface I1 { + } + public static interface I2 { + } + public static interface I3 { + } + public static interface I4 { + } } |