summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/optimizing/code_generator_vector_arm.cc24
-rw-r--r--compiler/optimizing/code_generator_vector_arm64.cc57
-rw-r--r--compiler/optimizing/code_generator_vector_arm_vixl.cc24
-rw-r--r--compiler/optimizing/code_generator_vector_mips.cc24
-rw-r--r--compiler/optimizing/code_generator_vector_mips64.cc24
-rw-r--r--compiler/optimizing/code_generator_vector_x86.cc45
-rw-r--r--compiler/optimizing/code_generator_vector_x86_64.cc41
-rw-r--r--compiler/optimizing/graph_visualizer.cc5
-rw-r--r--compiler/optimizing/induction_var_range.cc28
-rw-r--r--compiler/optimizing/intrinsics_mips64.cc194
-rw-r--r--compiler/optimizing/loop_optimization.cc204
-rw-r--r--compiler/optimizing/loop_optimization.h23
-rw-r--r--compiler/optimizing/nodes.h19
-rw-r--r--compiler/optimizing/nodes_vector.h80
-rw-r--r--compiler/utils/mips64/assembler_mips64.cc1
-rw-r--r--compiler/verifier_deps_test.cc15
-rw-r--r--dex2oat/dex2oat.cc25
-rw-r--r--runtime/Android.bp2
-rw-r--r--runtime/arch/x86/fault_handler_x86.cc57
-rw-r--r--runtime/art_method.cc12
-rw-r--r--runtime/art_method.h16
-rw-r--r--runtime/base/safe_copy.cc81
-rw-r--r--runtime/base/safe_copy.h (renamed from test/912-classes/src-ex/C.java)17
-rw-r--r--runtime/base/safe_copy_test.cc104
-rw-r--r--runtime/entrypoints/jni/jni_entrypoints.cc7
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc6
-rw-r--r--runtime/fault_handler.cc85
-rw-r--r--runtime/jni_internal.cc3
-rw-r--r--runtime/oat_file.cc11
-rw-r--r--runtime/oat_file.h4
-rw-r--r--runtime/oat_file_manager.cc6
-rw-r--r--runtime/openjdkjvmti/OpenjdkJvmTi.cc2
-rw-r--r--runtime/openjdkjvmti/art_jvmti.h2
-rw-r--r--runtime/openjdkjvmti/events-inl.h21
-rw-r--r--runtime/openjdkjvmti/ti_method.cc45
-rw-r--r--runtime/openjdkjvmti/ti_method.h5
-rw-r--r--runtime/runtime_callbacks.cc22
-rw-r--r--runtime/runtime_callbacks.h14
-rw-r--r--runtime/verifier/verifier_deps.cc6
-rw-r--r--test/646-checker-hadd-alt-byte/expected.txt1
-rw-r--r--test/646-checker-hadd-alt-byte/info.txt1
-rw-r--r--test/646-checker-hadd-alt-byte/src/Main.java241
-rw-r--r--test/646-checker-hadd-alt-char/expected.txt1
-rw-r--r--test/646-checker-hadd-alt-char/info.txt1
-rw-r--r--test/646-checker-hadd-alt-char/src/Main.java251
-rw-r--r--test/646-checker-hadd-alt-short/expected.txt1
-rw-r--r--test/646-checker-hadd-alt-short/info.txt1
-rw-r--r--test/646-checker-hadd-alt-short/src/Main.java242
-rw-r--r--test/646-checker-hadd-byte/expected.txt1
-rw-r--r--test/646-checker-hadd-byte/info.txt1
-rw-r--r--test/646-checker-hadd-byte/src/Main.java236
-rw-r--r--test/646-checker-hadd-char/expected.txt1
-rw-r--r--test/646-checker-hadd-char/info.txt1
-rw-r--r--test/646-checker-hadd-char/src/Main.java246
-rw-r--r--test/646-checker-hadd-short/expected.txt1
-rw-r--r--test/646-checker-hadd-short/info.txt1
-rw-r--r--test/646-checker-hadd-short/src/Main.java237
-rw-r--r--test/912-classes/classes.cc219
-rw-r--r--test/912-classes/classes_art.cc146
-rw-r--r--test/912-classes/expected.txt75
-rw-r--r--test/912-classes/src/B.java18
-rw-r--r--test/912-classes/src/Main.java447
-rw-r--r--test/912-classes/src/art/DexData.java100
-rw-r--r--test/912-classes/src/art/Test912.java454
-rw-r--r--test/912-classes/src/art/Test912Art.java78
-rw-r--r--test/913-heaps/heaps.cc4
-rw-r--r--test/924-threads/expected.txt11
-rw-r--r--test/924-threads/src/art/Test924.java72
-rw-r--r--test/924-threads/threads.cc37
-rw-r--r--test/986-native-method-bind/expected.txt8
-rw-r--r--test/986-native-method-bind/info.txt1
-rw-r--r--test/986-native-method-bind/native_bind.cc110
-rwxr-xr-xtest/986-native-method-bind/run17
-rw-r--r--test/986-native-method-bind/src/Main.java (renamed from test/912-classes/src-ex/A.java)5
-rw-r--r--test/986-native-method-bind/src/art/Main.java28
-rw-r--r--test/986-native-method-bind/src/art/Redefinition.java96
-rw-r--r--test/986-native-method-bind/src/art/Test986.java82
-rw-r--r--test/Android.bp4
-rw-r--r--test/Android.run-test-jvmti-java-library.mk3
-rw-r--r--test/Android.run-test.mk64
-rw-r--r--test/VerifierDeps/MyClassExtendingInterface.smali16
-rw-r--r--test/common/stack_inspect.cc13
-rw-r--r--test/knownfailures.json11
83 files changed, 4127 insertions, 818 deletions
diff --git a/compiler/optimizing/code_generator_vector_arm.cc b/compiler/optimizing/code_generator_vector_arm.cc
index e7f7b3019c..6e82123e56 100644
--- a/compiler/optimizing/code_generator_vector_arm.cc
+++ b/compiler/optimizing/code_generator_vector_arm.cc
@@ -124,6 +124,14 @@ void InstructionCodeGeneratorARM::VisitVecAdd(HVecAdd* instruction) {
LOG(FATAL) << "No SIMD for " << instruction->GetId();
}
+void LocationsBuilderARM::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
void LocationsBuilderARM::VisitVecSub(HVecSub* instruction) {
CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
}
@@ -148,6 +156,22 @@ void InstructionCodeGeneratorARM::VisitVecDiv(HVecDiv* instruction) {
LOG(FATAL) << "No SIMD for " << instruction->GetId();
}
+void LocationsBuilderARM::VisitVecMin(HVecMin* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM::VisitVecMin(HVecMin* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARM::VisitVecMax(HVecMax* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM::VisitVecMax(HVecMax* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
void LocationsBuilderARM::VisitVecAnd(HVecAnd* instruction) {
CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
}
diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc
index 0923920366..2dfccfff85 100644
--- a/compiler/optimizing/code_generator_vector_arm64.cc
+++ b/compiler/optimizing/code_generator_vector_arm64.cc
@@ -318,6 +318,47 @@ void InstructionCodeGeneratorARM64::VisitVecAdd(HVecAdd* instruction) {
}
}
+void LocationsBuilderARM64::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ VRegister lhs = VRegisterFrom(locations->InAt(0));
+ VRegister rhs = VRegisterFrom(locations->InAt(1));
+ VRegister dst = VRegisterFrom(locations->Out());
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimByte:
+ DCHECK_EQ(16u, instruction->GetVectorLength());
+ if (instruction->IsUnsigned()) {
+ instruction->IsRounded()
+ ? __ Urhadd(dst.V16B(), lhs.V16B(), rhs.V16B())
+ : __ Uhadd(dst.V16B(), lhs.V16B(), rhs.V16B());
+ } else {
+ instruction->IsRounded()
+ ? __ Srhadd(dst.V16B(), lhs.V16B(), rhs.V16B())
+ : __ Shadd(dst.V16B(), lhs.V16B(), rhs.V16B());
+ }
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ if (instruction->IsUnsigned()) {
+ instruction->IsRounded()
+ ? __ Urhadd(dst.V8H(), lhs.V8H(), rhs.V8H())
+ : __ Uhadd(dst.V8H(), lhs.V8H(), rhs.V8H());
+ } else {
+ instruction->IsRounded()
+ ? __ Srhadd(dst.V8H(), lhs.V8H(), rhs.V8H())
+ : __ Shadd(dst.V8H(), lhs.V8H(), rhs.V8H());
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
+}
+
void LocationsBuilderARM64::VisitVecSub(HVecSub* instruction) {
CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
}
@@ -420,6 +461,22 @@ void InstructionCodeGeneratorARM64::VisitVecDiv(HVecDiv* instruction) {
}
}
+void LocationsBuilderARM64::VisitVecMin(HVecMin* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecMin(HVecMin* instruction) {
+ LOG(FATAL) << "Unsupported SIMD instruction " << instruction->GetId();
+}
+
+void LocationsBuilderARM64::VisitVecMax(HVecMax* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecMax(HVecMax* instruction) {
+ LOG(FATAL) << "Unsupported SIMD instruction " << instruction->GetId();
+}
+
void LocationsBuilderARM64::VisitVecAnd(HVecAnd* instruction) {
CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
}
diff --git a/compiler/optimizing/code_generator_vector_arm_vixl.cc b/compiler/optimizing/code_generator_vector_arm_vixl.cc
index 74fa584e09..990178b31b 100644
--- a/compiler/optimizing/code_generator_vector_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_vector_arm_vixl.cc
@@ -124,6 +124,14 @@ void InstructionCodeGeneratorARMVIXL::VisitVecAdd(HVecAdd* instruction) {
LOG(FATAL) << "No SIMD for " << instruction->GetId();
}
+void LocationsBuilderARMVIXL::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
void LocationsBuilderARMVIXL::VisitVecSub(HVecSub* instruction) {
CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
}
@@ -148,6 +156,22 @@ void InstructionCodeGeneratorARMVIXL::VisitVecDiv(HVecDiv* instruction) {
LOG(FATAL) << "No SIMD for " << instruction->GetId();
}
+void LocationsBuilderARMVIXL::VisitVecMin(HVecMin* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecMin(HVecMin* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARMVIXL::VisitVecMax(HVecMax* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecMax(HVecMax* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
void LocationsBuilderARMVIXL::VisitVecAnd(HVecAnd* instruction) {
CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
}
diff --git a/compiler/optimizing/code_generator_vector_mips.cc b/compiler/optimizing/code_generator_vector_mips.cc
index 6969abd422..8ea1ca7d90 100644
--- a/compiler/optimizing/code_generator_vector_mips.cc
+++ b/compiler/optimizing/code_generator_vector_mips.cc
@@ -124,6 +124,14 @@ void InstructionCodeGeneratorMIPS::VisitVecAdd(HVecAdd* instruction) {
LOG(FATAL) << "No SIMD for " << instruction->GetId();
}
+void LocationsBuilderMIPS::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
void LocationsBuilderMIPS::VisitVecSub(HVecSub* instruction) {
CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
}
@@ -148,6 +156,22 @@ void InstructionCodeGeneratorMIPS::VisitVecDiv(HVecDiv* instruction) {
LOG(FATAL) << "No SIMD for " << instruction->GetId();
}
+void LocationsBuilderMIPS::VisitVecMin(HVecMin* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecMin(HVecMin* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS::VisitVecMax(HVecMax* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecMax(HVecMax* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
void LocationsBuilderMIPS::VisitVecAnd(HVecAnd* instruction) {
CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
}
diff --git a/compiler/optimizing/code_generator_vector_mips64.cc b/compiler/optimizing/code_generator_vector_mips64.cc
index 87118cefa5..a484bb4774 100644
--- a/compiler/optimizing/code_generator_vector_mips64.cc
+++ b/compiler/optimizing/code_generator_vector_mips64.cc
@@ -124,6 +124,14 @@ void InstructionCodeGeneratorMIPS64::VisitVecAdd(HVecAdd* instruction) {
LOG(FATAL) << "No SIMD for " << instruction->GetId();
}
+void LocationsBuilderMIPS64::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
void LocationsBuilderMIPS64::VisitVecSub(HVecSub* instruction) {
CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
}
@@ -148,6 +156,22 @@ void InstructionCodeGeneratorMIPS64::VisitVecDiv(HVecDiv* instruction) {
LOG(FATAL) << "No SIMD for " << instruction->GetId();
}
+void LocationsBuilderMIPS64::VisitVecMin(HVecMin* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecMin(HVecMin* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS64::VisitVecMax(HVecMax* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecMax(HVecMax* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
void LocationsBuilderMIPS64::VisitVecAnd(HVecAnd* instruction) {
CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
}
diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc
index 8dabb4d08f..a86d060821 100644
--- a/compiler/optimizing/code_generator_vector_x86.cc
+++ b/compiler/optimizing/code_generator_vector_x86.cc
@@ -350,6 +350,35 @@ void InstructionCodeGeneratorX86::VisitVecAdd(HVecAdd* instruction) {
}
}
+void LocationsBuilderX86::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ DCHECK(locations->InAt(0).Equals(locations->Out()));
+ XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>();
+ XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+
+ DCHECK(instruction->IsRounded());
+ DCHECK(instruction->IsUnsigned());
+
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimByte:
+ DCHECK_EQ(16u, instruction->GetVectorLength());
+ __ pavgb(dst, src);
+ return;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ __ pavgw(dst, src);
+ return;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
+}
+
void LocationsBuilderX86::VisitVecSub(HVecSub* instruction) {
CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
}
@@ -448,6 +477,22 @@ void InstructionCodeGeneratorX86::VisitVecDiv(HVecDiv* instruction) {
}
}
+void LocationsBuilderX86::VisitVecMin(HVecMin* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86::VisitVecMin(HVecMin* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderX86::VisitVecMax(HVecMax* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86::VisitVecMax(HVecMax* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
void LocationsBuilderX86::VisitVecAnd(HVecAnd* instruction) {
CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
}
diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc
index e95608839b..696735367e 100644
--- a/compiler/optimizing/code_generator_vector_x86_64.cc
+++ b/compiler/optimizing/code_generator_vector_x86_64.cc
@@ -343,6 +343,31 @@ void InstructionCodeGeneratorX86_64::VisitVecAdd(HVecAdd* instruction) {
}
}
+void LocationsBuilderX86_64::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ DCHECK(locations->InAt(0).Equals(locations->Out()));
+ XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>();
+ XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimByte:
+ DCHECK_EQ(16u, instruction->GetVectorLength());
+ __ pavgb(dst, src);
+ return;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ __ pavgw(dst, src);
+ return;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
+}
+
void LocationsBuilderX86_64::VisitVecSub(HVecSub* instruction) {
CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
}
@@ -441,6 +466,22 @@ void InstructionCodeGeneratorX86_64::VisitVecDiv(HVecDiv* instruction) {
}
}
+void LocationsBuilderX86_64::VisitVecMin(HVecMin* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecMin(HVecMin* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderX86_64::VisitVecMax(HVecMax* instruction) {
+ CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecMax(HVecMax* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
void LocationsBuilderX86_64::VisitVecAnd(HVecAnd* instruction) {
CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
}
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index cc3c143b15..1b2b9f80ac 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -509,6 +509,11 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
StartAttributeStream("kind") << deoptimize->GetKind();
}
+ void VisitVecHalvingAdd(HVecHalvingAdd* hadd) OVERRIDE {
+ StartAttributeStream("unsigned") << std::boolalpha << hadd->IsUnsigned() << std::noboolalpha;
+ StartAttributeStream("rounded") << std::boolalpha << hadd->IsRounded() << std::noboolalpha;
+ }
+
#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64)
void VisitMultiplyAccumulate(HMultiplyAccumulate* instruction) OVERRIDE {
StartAttributeStream("kind") << instruction->GetOpKind();
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index 1c8674d522..7c833cf70c 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -45,18 +45,6 @@ static bool IsSafeDiv(int32_t c1, int32_t c2) {
return c2 != 0 && CanLongValueFitIntoInt(static_cast<int64_t>(c1) / static_cast<int64_t>(c2));
}
-/** Returns true for 32/64-bit constant instruction. */
-static bool IsIntAndGet(HInstruction* instruction, int64_t* value) {
- if (instruction->IsIntConstant()) {
- *value = instruction->AsIntConstant()->GetValue();
- return true;
- } else if (instruction->IsLongConstant()) {
- *value = instruction->AsLongConstant()->GetValue();
- return true;
- }
- return false;
-}
-
/** Computes a * b for a,b > 0 (at least until first overflow happens). */
static int64_t SafeMul(int64_t a, int64_t b, /*out*/ bool* overflow) {
if (a > 0 && b > 0 && a > (std::numeric_limits<int64_t>::max() / b)) {
@@ -106,7 +94,7 @@ static bool IsGEZero(HInstruction* instruction) {
}
}
int64_t value = -1;
- return IsIntAndGet(instruction, &value) && value >= 0;
+ return IsInt64AndGet(instruction, &value) && value >= 0;
}
/** Hunts "under the hood" for a suitable instruction at the hint. */
@@ -149,7 +137,7 @@ static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v, HInstruc
int64_t value;
if (v.instruction->IsDiv() &&
v.instruction->InputAt(0)->IsArrayLength() &&
- IsIntAndGet(v.instruction->InputAt(1), &value) && v.a_constant == value) {
+ IsInt64AndGet(v.instruction->InputAt(1), &value) && v.a_constant == value) {
return InductionVarRange::Value(v.instruction->InputAt(0), 1, v.b_constant);
}
// If a == 1, the most suitable one suffices as maximum value.
@@ -444,7 +432,7 @@ bool InductionVarRange::IsConstant(HInductionVarAnalysis::InductionInfo* info,
// any of the three requests (kExact, kAtMost, and KAtLeast).
if (info->induction_class == HInductionVarAnalysis::kInvariant &&
info->operation == HInductionVarAnalysis::kFetch) {
- if (IsIntAndGet(info->fetch, value)) {
+ if (IsInt64AndGet(info->fetch, value)) {
return true;
}
}
@@ -635,7 +623,7 @@ InductionVarRange::Value InductionVarRange::GetGeometric(HInductionVarAnalysis::
int64_t f = 0;
if (IsConstant(info->op_a, kExact, &a) &&
CanLongValueFitIntoInt(a) &&
- IsIntAndGet(info->fetch, &f) && f >= 1) {
+ IsInt64AndGet(info->fetch, &f) && f >= 1) {
// Conservative bounds on a * f^-i + b with f >= 1 can be computed without
// trip count. Other forms would require a much more elaborate evaluation.
const bool is_min_a = a >= 0 ? is_min : !is_min;
@@ -663,7 +651,7 @@ InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction,
// Unless at a constant or hint, chase the instruction a bit deeper into the HIR tree, so that
// it becomes more likely range analysis will compare the same instructions as terminal nodes.
int64_t value;
- if (IsIntAndGet(instruction, &value) && CanLongValueFitIntoInt(value)) {
+ if (IsInt64AndGet(instruction, &value) && CanLongValueFitIntoInt(value)) {
// Proper constant reveals best information.
return Value(static_cast<int32_t>(value));
} else if (instruction == chase_hint_) {
@@ -671,10 +659,10 @@ InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction,
return Value(instruction, 1, 0);
} else if (instruction->IsAdd()) {
// Incorporate suitable constants in the chased value.
- if (IsIntAndGet(instruction->InputAt(0), &value) && CanLongValueFitIntoInt(value)) {
+ if (IsInt64AndGet(instruction->InputAt(0), &value) && CanLongValueFitIntoInt(value)) {
return AddValue(Value(static_cast<int32_t>(value)),
GetFetch(instruction->InputAt(1), trip, in_body, is_min));
- } else if (IsIntAndGet(instruction->InputAt(1), &value) && CanLongValueFitIntoInt(value)) {
+ } else if (IsInt64AndGet(instruction->InputAt(1), &value) && CanLongValueFitIntoInt(value)) {
return AddValue(GetFetch(instruction->InputAt(0), trip, in_body, is_min),
Value(static_cast<int32_t>(value)));
}
@@ -1074,7 +1062,7 @@ bool InductionVarRange::GenerateLastValueGeometric(HInductionVarAnalysis::Induct
// Detect known base and trip count (always taken).
int64_t f = 0;
int64_t m = 0;
- if (IsIntAndGet(info->fetch, &f) && f >= 1 && IsConstant(trip->op_a, kExact, &m) && m >= 1) {
+ if (IsInt64AndGet(info->fetch, &f) && f >= 1 && IsConstant(trip->op_a, kExact, &m) && m >= 1) {
HInstruction* opa = nullptr;
HInstruction* opb = nullptr;
if (GenerateCode(info->op_a, nullptr, graph, block, &opa, false, false) &&
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 82d0567ef9..b57b41f686 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -2093,6 +2093,199 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
__ Bind(&done);
}
+// static void java.lang.System.arraycopy(Object src, int srcPos,
+// Object dest, int destPos,
+// int length)
+void IntrinsicLocationsBuilderMIPS64::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(Mips64Assembler* assembler,
+ GpuRegister length_input_minus_pos,
+ Location length,
+ SlowPathCodeMIPS64* 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);
+ __ Bnezc(TMP, slow_path->GetEntryLabel());
+ } else {
+ __ LoadConst32(TMP, length_constant);
+ __ Bltc(length_input_minus_pos, TMP, slow_path->GetEntryLabel());
+ }
+ } else {
+ __ Bltc(length_input_minus_pos, length.AsRegister<GpuRegister>(), slow_path->GetEntryLabel());
+ }
+}
+
+static void CheckPosition(Mips64Assembler* assembler,
+ Location pos,
+ GpuRegister input,
+ Location length,
+ SlowPathCodeMIPS64* 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);
+ __ Bltzc(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.
+ GpuRegister pos_reg = pos.AsRegister<GpuRegister>();
+ __ Bnezc(pos_reg, slow_path->GetEntryLabel());
+ } else {
+ // Verify that pos >= 0.
+ GpuRegister pos_reg = pos.AsRegister<GpuRegister>();
+ __ Bltzc(pos_reg, slow_path->GetEntryLabel());
+
+ // Check that (length(input) - pos) >= zero.
+ __ LoadFromOffset(kLoadWord, AT, input, length_offset);
+ __ Subu(AT, AT, pos_reg);
+ __ Bltzc(AT, slow_path->GetEntryLabel());
+
+ // Verify that (length(input) - pos) >= length.
+ EnoughItems(assembler, AT, length, slow_path);
+ }
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitSystemArrayCopyChar(HInvoke* invoke) {
+ Mips64Assembler* assembler = GetAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ GpuRegister src = locations->InAt(0).AsRegister<GpuRegister>();
+ Location src_pos = locations->InAt(1);
+ GpuRegister dest = locations->InAt(2).AsRegister<GpuRegister>();
+ Location dest_pos = locations->InAt(3);
+ Location length = locations->InAt(4);
+
+ Mips64Label loop;
+
+ GpuRegister dest_base = locations->GetTemp(0).AsRegister<GpuRegister>();
+ GpuRegister src_base = locations->GetTemp(1).AsRegister<GpuRegister>();
+ GpuRegister count = locations->GetTemp(2).AsRegister<GpuRegister>();
+
+ SlowPathCodeMIPS64* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS64(invoke);
+ codegen_->AddSlowPath(slow_path);
+
+ // Bail out if the source and destination are the same (to handle overlap).
+ __ Beqc(src, dest, slow_path->GetEntryLabel());
+
+ // Bail out if the source is null.
+ __ Beqzc(src, slow_path->GetEntryLabel());
+
+ // Bail out if the destination is null.
+ __ Beqzc(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.
+ __ Bltzc(length.AsRegister<GpuRegister>(), slow_path->GetEntryLabel());
+
+ __ Move(count, length.AsRegister<GpuRegister>());
+ }
+
+ // 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.
+ __ Beqzc(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();
+
+ __ Daddiu64(src_base, src, data_offset + char_size * src_pos_const, TMP);
+ } else {
+ __ Daddiu64(src_base, src, data_offset, TMP);
+ __ Dlsa(src_base, src_pos.AsRegister<GpuRegister>(), src_base, char_shift);
+ }
+ if (dest_pos.IsConstant()) {
+ int32_t dest_pos_const = dest_pos.GetConstant()->AsIntConstant()->GetValue();
+
+ __ Daddiu64(dest_base, dest, data_offset + char_size * dest_pos_const, TMP);
+ } else {
+ __ Daddiu64(dest_base, dest, data_offset, TMP);
+ __ Dlsa(dest_base, dest_pos.AsRegister<GpuRegister>(), dest_base, char_shift);
+ }
+
+ __ Bind(&loop);
+ __ Lh(TMP, src_base, 0);
+ __ Daddiu(src_base, src_base, char_size);
+ __ Daddiu(count, count, -1);
+ __ Sh(TMP, dest_base, 0);
+ __ Daddiu(dest_base, dest_base, char_size);
+ __ Bnezc(count, &loop);
+
+ __ Bind(slow_path->GetExitLabel());
+}
+
static void GenHighestOneBit(LocationSummary* locations,
Primitive::Type type,
Mips64Assembler* assembler) {
@@ -2372,7 +2565,6 @@ void IntrinsicCodeGeneratorMIPS64::VisitMathTanh(HInvoke* invoke) {
}
UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent)
-UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy)
UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOf);
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 8e88c1ec7f..5a95abdb50 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -63,12 +63,122 @@ static bool IsEarlyExit(HLoopInformation* loop_info) {
return false;
}
+// Detect a sign extension from the given type. Returns the promoted operand on success.
+static bool IsSignExtensionAndGet(HInstruction* instruction,
+ Primitive::Type type,
+ /*out*/ HInstruction** operand) {
+ // Accept any already wider constant that would be handled properly by sign
+ // extension when represented in the *width* of the given narrower data type
+ // (the fact that char normally zero extends does not matter here).
+ int64_t value = 0;
+ if (IsInt64AndGet(instruction, &value)) {
+ switch (type) {
+ case Primitive::kPrimByte:
+ if (std::numeric_limits<int8_t>::min() <= value &&
+ std::numeric_limits<int8_t>::max() >= value) {
+ *operand = instruction;
+ return true;
+ }
+ return false;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ if (std::numeric_limits<int16_t>::min() <= value &&
+ std::numeric_limits<int16_t>::max() <= value) {
+ *operand = instruction;
+ return true;
+ }
+ return false;
+ default:
+ return false;
+ }
+ }
+ // An implicit widening conversion of a signed integer to an integral type sign-extends
+ // the two's-complement representation of the integer value to fill the wider format.
+ if (instruction->GetType() == type && (instruction->IsArrayGet() ||
+ instruction->IsStaticFieldGet() ||
+ instruction->IsInstanceFieldGet())) {
+ switch (type) {
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ *operand = instruction;
+ return true;
+ default:
+ return false;
+ }
+ }
+ // TODO: perhaps explicit conversions later too?
+ // (this may return something different from instruction)
+ return false;
+}
+
+// Detect a zero extension from the given type. Returns the promoted operand on success.
+static bool IsZeroExtensionAndGet(HInstruction* instruction,
+ Primitive::Type type,
+ /*out*/ HInstruction** operand) {
+ // Accept any already wider constant that would be handled properly by zero
+ // extension when represented in the *width* of the given narrower data type
+ // (the fact that byte/short normally sign extend does not matter here).
+ int64_t value = 0;
+ if (IsInt64AndGet(instruction, &value)) {
+ switch (type) {
+ case Primitive::kPrimByte:
+ if (std::numeric_limits<uint8_t>::min() <= value &&
+ std::numeric_limits<uint8_t>::max() >= value) {
+ *operand = instruction;
+ return true;
+ }
+ return false;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ if (std::numeric_limits<uint16_t>::min() <= value &&
+ std::numeric_limits<uint16_t>::max() <= value) {
+ *operand = instruction;
+ return true;
+ }
+ return false;
+ default:
+ return false;
+ }
+ }
+ // An implicit widening conversion of a char to an integral type zero-extends
+ // the representation of the char value to fill the wider format.
+ if (instruction->GetType() == type && (instruction->IsArrayGet() ||
+ instruction->IsStaticFieldGet() ||
+ instruction->IsInstanceFieldGet())) {
+ if (type == Primitive::kPrimChar) {
+ *operand = instruction;
+ return true;
+ }
+ }
+ // A sign (or zero) extension followed by an explicit removal of just the
+ // higher sign bits is equivalent to a zero extension of the underlying operand.
+ if (instruction->IsAnd()) {
+ int64_t mask = 0;
+ HInstruction* a = instruction->InputAt(0);
+ HInstruction* b = instruction->InputAt(1);
+ // In (a & b) find (mask & b) or (a & mask) with sign or zero extension on the non-mask.
+ if ((IsInt64AndGet(a, /*out*/ &mask) && (IsSignExtensionAndGet(b, type, /*out*/ operand) ||
+ IsZeroExtensionAndGet(b, type, /*out*/ operand))) ||
+ (IsInt64AndGet(b, /*out*/ &mask) && (IsSignExtensionAndGet(a, type, /*out*/ operand) ||
+ IsZeroExtensionAndGet(a, type, /*out*/ operand)))) {
+ switch ((*operand)->GetType()) {
+ case Primitive::kPrimByte: return mask == std::numeric_limits<uint8_t>::max();
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort: return mask == std::numeric_limits<uint16_t>::max();
+ default: return false;
+ }
+ }
+ }
+ // TODO: perhaps explicit conversions later too?
+ return false;
+}
+
// Test vector restrictions.
static bool HasVectorRestrictions(uint64_t restrictions, uint64_t tested) {
return (restrictions & tested) != 0;
}
-// Inserts an instruction.
+// Insert an instruction.
static HInstruction* Insert(HBasicBlock* block, HInstruction* instruction) {
DCHECK(block != nullptr);
DCHECK(instruction != nullptr);
@@ -713,6 +823,10 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node,
return true;
}
} else if (instruction->IsShl() || instruction->IsShr() || instruction->IsUShr()) {
+ // Recognize vectorization idioms.
+ if (VectorizeHalvingAddIdiom(node, instruction, generate_code, type, restrictions)) {
+ return true;
+ }
// Deal with vector restrictions.
if ((HasVectorRestrictions(restrictions, kNoShift)) ||
(instruction->IsShr() && HasVectorRestrictions(restrictions, kNoShr))) {
@@ -806,11 +920,11 @@ bool HLoopOptimization::TrySetVectorType(Primitive::Type type, uint64_t* restric
switch (type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
- *restrictions |= kNoMul | kNoDiv | kNoShift | kNoAbs;
+ *restrictions |= kNoMul | kNoDiv | kNoShift | kNoAbs | kNoSignedHAdd | kNoUnroundedHAdd;
return TrySetVectorLength(16);
case Primitive::kPrimChar:
case Primitive::kPrimShort:
- *restrictions |= kNoDiv | kNoAbs;
+ *restrictions |= kNoDiv | kNoAbs | kNoSignedHAdd | kNoUnroundedHAdd;
return TrySetVectorLength(8);
case Primitive::kPrimInt:
*restrictions |= kNoDiv;
@@ -1039,6 +1153,90 @@ void HLoopOptimization::GenerateVecOp(HInstruction* org,
#undef GENERATE_VEC
//
+// Vectorization idioms.
+//
+
+// Method recognizes the following idioms:
+// rounding halving add (a + b + 1) >> 1 for unsigned/signed operands a, b
+// regular halving add (a + b) >> 1 for unsigned/signed operands a, b
+// Provided that the operands are promoted to a wider form to do the arithmetic and
+// then cast back to narrower form, the idioms can be mapped into efficient SIMD
+// implementation that operates directly in narrower form (plus one extra bit).
+// TODO: current version recognizes implicit byte/short/char widening only;
+// explicit widening from int to long could be added later.
+bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node,
+ HInstruction* instruction,
+ bool generate_code,
+ Primitive::Type type,
+ uint64_t restrictions) {
+ // Test for top level arithmetic shift right x >> 1 or logical shift right x >>> 1
+ // (note whether the sign bit in higher precision is shifted in has no effect
+ // on the narrow precision computed by the idiom).
+ int64_t value = 0;
+ if ((instruction->IsShr() ||
+ instruction->IsUShr()) &&
+ IsInt64AndGet(instruction->InputAt(1), &value) && value == 1) {
+ //
+ // TODO: make following code less sensitive to associativity and commutativity differences.
+ //
+ HInstruction* x = instruction->InputAt(0);
+ // Test for an optional rounding part (x + 1) >> 1.
+ bool is_rounded = false;
+ if (x->IsAdd() && IsInt64AndGet(x->InputAt(1), &value) && value == 1) {
+ x = x->InputAt(0);
+ is_rounded = true;
+ }
+ // Test for a core addition (a + b) >> 1 (possibly rounded), either unsigned or signed.
+ if (x->IsAdd()) {
+ HInstruction* a = x->InputAt(0);
+ HInstruction* b = x->InputAt(1);
+ HInstruction* r = nullptr;
+ HInstruction* s = nullptr;
+ bool is_unsigned = false;
+ if (IsZeroExtensionAndGet(a, type, &r) && IsZeroExtensionAndGet(b, type, &s)) {
+ is_unsigned = true;
+ } else if (IsSignExtensionAndGet(a, type, &r) && IsSignExtensionAndGet(b, type, &s)) {
+ is_unsigned = false;
+ } else {
+ return false;
+ }
+ // Deal with vector restrictions.
+ if ((!is_unsigned && HasVectorRestrictions(restrictions, kNoSignedHAdd)) ||
+ (!is_rounded && HasVectorRestrictions(restrictions, kNoUnroundedHAdd))) {
+ return false;
+ }
+ // Accept recognized halving add for vectorizable operands. Vectorized code uses the
+ // shorthand idiomatic operation. Sequential code uses the original scalar expressions.
+ DCHECK(r != nullptr && s != nullptr);
+ if (VectorizeUse(node, r, generate_code, type, restrictions) &&
+ VectorizeUse(node, s, generate_code, type, restrictions)) {
+ if (generate_code) {
+ if (vector_mode_ == kVector) {
+ vector_map_->Put(instruction, new (global_allocator_) HVecHalvingAdd(
+ global_allocator_,
+ vector_map_->Get(r),
+ vector_map_->Get(s),
+ type,
+ vector_length_,
+ is_unsigned,
+ is_rounded));
+ } else {
+ VectorizeUse(node, instruction->InputAt(0), generate_code, type, restrictions);
+ VectorizeUse(node, instruction->InputAt(1), generate_code, type, restrictions);
+ GenerateVecOp(instruction,
+ vector_map_->Get(instruction->InputAt(0)),
+ vector_map_->Get(instruction->InputAt(1)),
+ type);
+ }
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+//
// Helpers.
//
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index d8f50aab28..4a7da86e32 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -62,13 +62,15 @@ class HLoopOptimization : public HOptimization {
* Vectorization restrictions (bit mask).
*/
enum VectorRestrictions {
- kNone = 0, // no restrictions
- kNoMul = 1, // no multiplication
- kNoDiv = 2, // no division
- kNoShift = 4, // no shift
- kNoShr = 8, // no arithmetic shift right
- kNoHiBits = 16, // "wider" operations cannot bring in higher order bits
- kNoAbs = 32, // no absolute value
+ kNone = 0, // no restrictions
+ kNoMul = 1, // no multiplication
+ kNoDiv = 2, // no division
+ kNoShift = 4, // no shift
+ kNoShr = 8, // no arithmetic shift right
+ kNoHiBits = 16, // "wider" operations cannot bring in higher order bits
+ kNoSignedHAdd = 32, // no signed halving add
+ kNoUnroundedHAdd = 64, // no unrounded halving add
+ kNoAbs = 128, // no absolute value
};
/*
@@ -136,6 +138,13 @@ class HLoopOptimization : public HOptimization {
Primitive::Type type);
void GenerateVecOp(HInstruction* org, HInstruction* opa, HInstruction* opb, Primitive::Type type);
+ // Vectorization idioms.
+ bool VectorizeHalvingAddIdiom(LoopNode* node,
+ HInstruction* instruction,
+ bool generate_code,
+ Primitive::Type type,
+ uint64_t restrictions);
+
// Helpers.
bool TrySetPhiInduction(HPhi* phi, bool restrict_uses);
bool TrySetSimpleLoopHeader(HBasicBlock* block);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index c109369106..6be237e612 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1369,9 +1369,12 @@ class HLoopInformationOutwardIterator : public ValueObject {
M(VecAbs, VecUnaryOperation) \
M(VecNot, VecUnaryOperation) \
M(VecAdd, VecBinaryOperation) \
+ M(VecHalvingAdd, VecBinaryOperation) \
M(VecSub, VecBinaryOperation) \
M(VecMul, VecBinaryOperation) \
M(VecDiv, VecBinaryOperation) \
+ M(VecMin, VecBinaryOperation) \
+ M(VecMax, VecBinaryOperation) \
M(VecAnd, VecBinaryOperation) \
M(VecAndNot, VecBinaryOperation) \
M(VecOr, VecBinaryOperation) \
@@ -6845,6 +6848,7 @@ class HBlocksInLoopReversePostOrderIterator : public ValueObject {
DISALLOW_COPY_AND_ASSIGN(HBlocksInLoopReversePostOrderIterator);
};
+// Returns int64_t value of a properly typed constant.
inline int64_t Int64FromConstant(HConstant* constant) {
if (constant->IsIntConstant()) {
return constant->AsIntConstant()->GetValue();
@@ -6856,6 +6860,21 @@ inline int64_t Int64FromConstant(HConstant* constant) {
}
}
+// Returns true iff instruction is an integral constant (and sets value on success).
+inline bool IsInt64AndGet(HInstruction* instruction, /*out*/ int64_t* value) {
+ if (instruction->IsIntConstant()) {
+ *value = instruction->AsIntConstant()->GetValue();
+ return true;
+ } else if (instruction->IsLongConstant()) {
+ *value = instruction->AsLongConstant()->GetValue();
+ return true;
+ } else if (instruction->IsNullConstant()) {
+ *value = 0;
+ return true;
+ }
+ return false;
+}
+
#define INSTRUCTION_TYPE_CHECK(type, super) \
inline bool HInstruction::Is##type() const { return GetKind() == k##type; } \
inline const H##type* HInstruction::As##type() const { \
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
index 0cbbf2a215..bff58d0910 100644
--- a/compiler/optimizing/nodes_vector.h
+++ b/compiler/optimizing/nodes_vector.h
@@ -338,6 +338,42 @@ class HVecAdd FINAL : public HVecBinaryOperation {
DISALLOW_COPY_AND_ASSIGN(HVecAdd);
};
+// Performs halving add on every component in the two vectors, viz.
+// rounded [ x1, .. , xn ] hradd [ y1, .. , yn ] = [ (x1 + y1 + 1) >> 1, .. , (xn + yn + 1) >> 1 ]
+// or [ x1, .. , xn ] hadd [ y1, .. , yn ] = [ (x1 + y1) >> 1, .. , (xn + yn ) >> 1 ]
+// for signed operands x, y (sign extension) or unsigned operands x, y (zero extension).
+class HVecHalvingAdd FINAL : public HVecBinaryOperation {
+ public:
+ HVecHalvingAdd(ArenaAllocator* arena,
+ HInstruction* left,
+ HInstruction* right,
+ Primitive::Type packed_type,
+ size_t vector_length,
+ bool is_unsigned,
+ bool is_rounded,
+ uint32_t dex_pc = kNoDexPc)
+ : HVecBinaryOperation(arena, packed_type, vector_length, dex_pc),
+ is_unsigned_(is_unsigned),
+ is_rounded_(is_rounded) {
+ DCHECK(left->IsVecOperation() && right->IsVecOperation());
+ DCHECK_EQ(left->AsVecOperation()->GetPackedType(), packed_type);
+ DCHECK_EQ(right->AsVecOperation()->GetPackedType(), packed_type);
+ SetRawInputAt(0, left);
+ SetRawInputAt(1, right);
+ }
+
+ bool IsUnsigned() const { return is_unsigned_; }
+ bool IsRounded() const { return is_rounded_; }
+
+ DECLARE_INSTRUCTION(VecHalvingAdd);
+
+ private:
+ bool is_unsigned_;
+ bool is_rounded_;
+
+ DISALLOW_COPY_AND_ASSIGN(HVecHalvingAdd);
+};
+
// Subtracts every component in the two vectors,
// viz. [ x1, .. , xn ] - [ y1, .. , yn ] = [ x1 - y1, .. , xn - yn ].
class HVecSub FINAL : public HVecBinaryOperation {
@@ -404,6 +440,50 @@ class HVecDiv FINAL : public HVecBinaryOperation {
DISALLOW_COPY_AND_ASSIGN(HVecDiv);
};
+// Takes minimum of every component in the two vectors,
+// viz. MIN( [ x1, .. , xn ] , [ y1, .. , yn ]) = [ min(x1, y1), .. , min(xn, yn) ].
+class HVecMin FINAL : public HVecBinaryOperation {
+ public:
+ HVecMin(ArenaAllocator* arena,
+ HInstruction* left,
+ HInstruction* right,
+ Primitive::Type packed_type,
+ size_t vector_length,
+ uint32_t dex_pc = kNoDexPc)
+ : HVecBinaryOperation(arena, packed_type, vector_length, dex_pc) {
+ DCHECK(left->IsVecOperation() && right->IsVecOperation());
+ DCHECK_EQ(left->AsVecOperation()->GetPackedType(), packed_type);
+ DCHECK_EQ(right->AsVecOperation()->GetPackedType(), packed_type);
+ SetRawInputAt(0, left);
+ SetRawInputAt(1, right);
+ }
+ DECLARE_INSTRUCTION(VecMin);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HVecMin);
+};
+
+// Takes maximum of every component in the two vectors,
+// viz. MAX( [ x1, .. , xn ] , [ y1, .. , yn ]) = [ max(x1, y1), .. , max(xn, yn) ].
+class HVecMax FINAL : public HVecBinaryOperation {
+ public:
+ HVecMax(ArenaAllocator* arena,
+ HInstruction* left,
+ HInstruction* right,
+ Primitive::Type packed_type,
+ size_t vector_length,
+ uint32_t dex_pc = kNoDexPc)
+ : HVecBinaryOperation(arena, packed_type, vector_length, dex_pc) {
+ DCHECK(left->IsVecOperation() && right->IsVecOperation());
+ DCHECK_EQ(left->AsVecOperation()->GetPackedType(), packed_type);
+ DCHECK_EQ(right->AsVecOperation()->GetPackedType(), packed_type);
+ SetRawInputAt(0, left);
+ SetRawInputAt(1, right);
+ }
+ DECLARE_INSTRUCTION(VecMax);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HVecMax);
+};
+
// Bitwise-ands every component in the two vectors,
// viz. [ x1, .. , xn ] & [ y1, .. , yn ] = [ x1 & y1, .. , xn & yn ].
class HVecAnd FINAL : public HVecBinaryOperation {
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
index 0cff44d830..57223b52a3 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -1703,6 +1703,7 @@ void Mips64Assembler::Addiu32(GpuRegister rt, GpuRegister rs, int32_t value) {
// TODO: don't use rtmp, use daui, dahi, dati.
void Mips64Assembler::Daddiu64(GpuRegister rt, GpuRegister rs, int64_t value, GpuRegister rtmp) {
+ CHECK_NE(rs, rtmp);
if (IsInt<16>(value)) {
Daddiu(rt, rs, value);
} else {
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index fa7e98586c..3b6b9cc7f0 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -207,9 +207,9 @@ class VerifierDepsTest : public CommonCompilerTest {
ScopedObjectAccess soa(Thread::Current());
LoadDexFile(&soa);
mirror::Class* klass_dst = FindClassByName(dst, &soa);
- DCHECK(klass_dst != nullptr);
+ DCHECK(klass_dst != nullptr) << dst;
mirror::Class* klass_src = FindClassByName(src, &soa);
- DCHECK(klass_src != nullptr);
+ DCHECK(klass_src != nullptr) << src;
verifier_deps_->AddAssignability(*primary_dex_file_,
klass_dst,
klass_src,
@@ -1536,5 +1536,16 @@ TEST_F(VerifierDepsTest, NotAssignable_InterfaceWithClassInBoot) {
ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "LIface;", false));
}
+TEST_F(VerifierDepsTest, Assignable_Arrays) {
+ ASSERT_TRUE(TestAssignabilityRecording(/* dst */ "[LIface;",
+ /* src */ "[LMyClassExtendingInterface;",
+ /* is_strict */ false,
+ /* is_assignable */ true));
+ ASSERT_FALSE(HasAssignable(
+ "LIface;", "LMyClassExtendingInterface;", /* expected_is_assignable */ true));
+ ASSERT_FALSE(HasAssignable(
+ "LIface;", "LMyClassExtendingInterface;", /* expected_is_assignable */ false));
+}
+
} // namespace verifier
} // namespace art
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 81566c40e9..a9108e0eb0 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -385,6 +385,8 @@ NO_RETURN static void Usage(const char* fmt, ...) {
UsageError(" This option is incompatible with read barriers (e.g., if dex2oat has been");
UsageError(" built with the environment variable `ART_USE_READ_BARRIER` set to `true`).");
UsageError("");
+ UsageError(" --classpath-dir=<directory-path>: directory used to resolve relative class paths.");
+ UsageError("");
std::cerr << "See log for usage error information\n";
exit(EXIT_FAILURE);
}
@@ -1234,6 +1236,8 @@ class Dex2Oat FINAL {
Usage("Cannot use --force-determinism with read barriers or non-CMS garbage collector");
}
force_determinism_ = true;
+ } else if (option.starts_with("--classpath-dir=")) {
+ classpath_dir_ = option.substr(strlen("--classpath-dir=")).data();
} else if (!compiler_options_->ParseCompilerOption(option, Usage)) {
Usage("Unknown argument %s", option.data());
}
@@ -1486,12 +1490,13 @@ class Dex2Oat FINAL {
}
// Open dex files for class path.
- const std::vector<std::string> class_path_locations =
+ std::vector<std::string> class_path_locations =
GetClassPathLocations(runtime_->GetClassPathString());
OpenClassPathFiles(class_path_locations,
&class_path_files_,
&opened_oat_files_,
- runtime_->GetInstructionSet());
+ runtime_->GetInstructionSet(),
+ classpath_dir_);
// Store the classpath we have right now.
std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_);
@@ -1501,7 +1506,7 @@ class Dex2Oat FINAL {
// When passing the special shared library as the classpath, it is the only path.
encoded_class_path = OatFile::kSpecialSharedLibrary;
} else {
- encoded_class_path = OatFile::EncodeDexFileDependencies(class_path_files);
+ encoded_class_path = OatFile::EncodeDexFileDependencies(class_path_files, classpath_dir_);
}
key_value_store_->Put(OatHeader::kClassPathKey, encoded_class_path);
}
@@ -2180,18 +2185,23 @@ class Dex2Oat FINAL {
// Opens requested class path files and appends them to opened_dex_files. If the dex files have
// been stripped, this opens them from their oat files and appends them to opened_oat_files.
- static void OpenClassPathFiles(const std::vector<std::string>& class_path_locations,
+ static void OpenClassPathFiles(std::vector<std::string>& class_path_locations,
std::vector<std::unique_ptr<const DexFile>>* opened_dex_files,
std::vector<std::unique_ptr<OatFile>>* opened_oat_files,
- InstructionSet isa) {
+ InstructionSet isa,
+ std::string& classpath_dir) {
DCHECK(opened_dex_files != nullptr) << "OpenClassPathFiles dex out-param is nullptr";
DCHECK(opened_oat_files != nullptr) << "OpenClassPathFiles oat out-param is nullptr";
- for (const std::string& location : class_path_locations) {
+ for (std::string& location : class_path_locations) {
// Stop early if we detect the special shared library, which may be passed as the classpath
// for dex2oat when we want to skip the shared libraries check.
if (location == OatFile::kSpecialSharedLibrary) {
break;
}
+ // If path is relative, append it to the provided base directory.
+ if (!classpath_dir.empty() && location[0] != '/') {
+ location = classpath_dir + '/' + location;
+ }
static constexpr bool kVerifyChecksum = true;
std::string error_msg;
if (!DexFile::Open(
@@ -2743,6 +2753,9 @@ class Dex2Oat FINAL {
// See CompilerOptions.force_determinism_.
bool force_determinism_;
+ // Directory of relative classpaths.
+ std::string classpath_dir_;
+
// Whether the given input vdex is also the output.
bool update_input_vdex_ = false;
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 6c3bc0450b..8972e91321 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -37,6 +37,7 @@ cc_defaults {
"base/hex_dump.cc",
"base/logging.cc",
"base/mutex.cc",
+ "base/safe_copy.cc",
"base/scoped_arena_allocator.cc",
"base/scoped_flock.cc",
"base/stringpiece.cc",
@@ -522,6 +523,7 @@ art_cc_test {
"base/hex_dump_test.cc",
"base/histogram_test.cc",
"base/mutex_test.cc",
+ "base/safe_copy_test.cc",
"base/scoped_flock_test.cc",
"base/time_utils_test.cc",
"base/timing_logger_test.cc",
diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc
index f407ebf1d1..014cc15a40 100644
--- a/runtime/arch/x86/fault_handler_x86.cc
+++ b/runtime/arch/x86/fault_handler_x86.cc
@@ -25,6 +25,7 @@
#include "globals.h"
#include "base/logging.h"
#include "base/hex_dump.h"
+#include "base/safe_copy.h"
#include "thread.h"
#include "thread-inl.h"
@@ -78,6 +79,30 @@ extern "C" void art_quick_test_suspend();
// Get the size of an instruction in bytes.
// Return 0 if the instruction is not handled.
static uint32_t GetInstructionSize(const uint8_t* pc) {
+ // Don't segfault if pc points to garbage.
+ char buf[15]; // x86/x86-64 have a maximum instruction length of 15 bytes.
+ ssize_t bytes = SafeCopy(buf, pc, sizeof(buf));
+
+ if (bytes == 0) {
+ // Nothing was readable.
+ return 0;
+ }
+
+ if (bytes == -1) {
+ // SafeCopy not supported, assume that the entire range is readable.
+ bytes = 16;
+ } else {
+ pc = reinterpret_cast<uint8_t*>(buf);
+ }
+
+#define INCREMENT_PC() \
+ do { \
+ pc++; \
+ if (pc - startpc > bytes) { \
+ return 0; \
+ } \
+ } while (0)
+
#if defined(__x86_64)
const bool x86_64 = true;
#else
@@ -86,7 +111,8 @@ static uint32_t GetInstructionSize(const uint8_t* pc) {
const uint8_t* startpc = pc;
- uint8_t opcode = *pc++;
+ uint8_t opcode = *pc;
+ INCREMENT_PC();
uint8_t modrm;
bool has_modrm = false;
bool two_byte = false;
@@ -118,7 +144,8 @@ static uint32_t GetInstructionSize(const uint8_t* pc) {
// Group 4
case 0x67:
- opcode = *pc++;
+ opcode = *pc;
+ INCREMENT_PC();
prefix_present = true;
break;
}
@@ -128,13 +155,15 @@ static uint32_t GetInstructionSize(const uint8_t* pc) {
}
if (x86_64 && opcode >= 0x40 && opcode <= 0x4f) {
- opcode = *pc++;
+ opcode = *pc;
+ INCREMENT_PC();
}
if (opcode == 0x0f) {
// Two byte opcode
two_byte = true;
- opcode = *pc++;
+ opcode = *pc;
+ INCREMENT_PC();
}
bool unhandled_instruction = false;
@@ -147,7 +176,8 @@ static uint32_t GetInstructionSize(const uint8_t* pc) {
case 0xb7:
case 0xbe: // movsx
case 0xbf:
- modrm = *pc++;
+ modrm = *pc;
+ INCREMENT_PC();
has_modrm = true;
break;
default:
@@ -166,28 +196,32 @@ static uint32_t GetInstructionSize(const uint8_t* pc) {
case 0x3c:
case 0x3d:
case 0x85: // test.
- modrm = *pc++;
+ modrm = *pc;
+ INCREMENT_PC();
has_modrm = true;
break;
case 0x80: // group 1, byte immediate.
case 0x83:
case 0xc6:
- modrm = *pc++;
+ modrm = *pc;
+ INCREMENT_PC();
has_modrm = true;
immediate_size = 1;
break;
case 0x81: // group 1, word immediate.
case 0xc7: // mov
- modrm = *pc++;
+ modrm = *pc;
+ INCREMENT_PC();
has_modrm = true;
immediate_size = operand_size_prefix ? 2 : 4;
break;
case 0xf6:
case 0xf7:
- modrm = *pc++;
+ modrm = *pc;
+ INCREMENT_PC();
has_modrm = true;
switch ((modrm >> 3) & 7) { // Extract "reg/opcode" from "modr/m".
case 0: // test
@@ -222,7 +256,7 @@ static uint32_t GetInstructionSize(const uint8_t* pc) {
// Check for SIB.
if (mod != 3U /* 0b11 */ && (modrm & 7U /* 0b111 */) == 4) {
- ++pc; // SIB
+ INCREMENT_PC(); // SIB
}
switch (mod) {
@@ -238,6 +272,9 @@ static uint32_t GetInstructionSize(const uint8_t* pc) {
pc += displacement_size + immediate_size;
VLOG(signals) << "x86 instruction length calculated as " << (pc - startpc);
+ if (pc - startpc > bytes) {
+ return 0;
+ }
return pc - startpc;
}
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 5a71be6eb9..76fdd43992 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -43,6 +43,7 @@
#include "mirror/object-inl.h"
#include "mirror/string.h"
#include "oat_file-inl.h"
+#include "runtime_callbacks.h"
#include "scoped_thread_state_change-inl.h"
#include "well_known_classes.h"
@@ -372,20 +373,25 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue*
self->PopManagedStackFragment(fragment);
}
-void ArtMethod::RegisterNative(const void* native_method, bool is_fast) {
+const void* ArtMethod::RegisterNative(const void* native_method, bool is_fast) {
CHECK(IsNative()) << PrettyMethod();
CHECK(!IsFastNative()) << PrettyMethod();
CHECK(native_method != nullptr) << PrettyMethod();
if (is_fast) {
AddAccessFlags(kAccFastNative);
}
- SetEntryPointFromJni(native_method);
+ void* new_native_method = nullptr;
+ Runtime::Current()->GetRuntimeCallbacks()->RegisterNativeMethod(this,
+ native_method,
+ /*out*/&new_native_method);
+ SetEntryPointFromJni(new_native_method);
+ return new_native_method;
}
void ArtMethod::UnregisterNative() {
CHECK(IsNative() && !IsFastNative()) << PrettyMethod();
// restore stub to lookup native pointer via dlsym
- RegisterNative(GetJniDlsymLookupStub(), false);
+ SetEntryPointFromJni(GetJniDlsymLookupStub());
}
bool ArtMethod::IsOverridableByDefaultMethod() {
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 51b65760a1..b01b344bda 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -398,8 +398,10 @@ class ArtMethod FINAL {
pointer_size);
}
- void RegisterNative(const void* native_method, bool is_fast)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ // Registers the native method and returns the new entry point. NB The returned entry point might
+ // be different from the native_method argument if some MethodCallback modifies it.
+ const void* RegisterNative(const void* native_method, bool is_fast)
+ REQUIRES_SHARED(Locks::mutator_lock_) WARN_UNUSED;
void UnregisterNative() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -744,6 +746,16 @@ class ArtMethod FINAL {
DISALLOW_COPY_AND_ASSIGN(ArtMethod); // Need to use CopyFrom to deal with 32 vs 64 bits.
};
+class MethodCallback {
+ public:
+ virtual ~MethodCallback() {}
+
+ virtual void RegisterNativeMethod(ArtMethod* method,
+ const void* original_implementation,
+ /*out*/void** new_implementation)
+ REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+};
+
} // namespace art
#endif // ART_RUNTIME_ART_METHOD_H_
diff --git a/runtime/base/safe_copy.cc b/runtime/base/safe_copy.cc
new file mode 100644
index 0000000000..06249acb44
--- /dev/null
+++ b/runtime/base/safe_copy.cc
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "safe_copy.h"
+
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/user.h>
+
+#include <algorithm>
+
+#include <android-base/macros.h>
+
+#include "runtime/base/bit_utils.h"
+
+namespace art {
+
+ssize_t SafeCopy(void *dst, const void *src, size_t len) {
+#if defined(__linux__)
+ struct iovec dst_iov = {
+ .iov_base = dst,
+ .iov_len = len,
+ };
+
+ // Split up the remote read across page boundaries.
+ // From the manpage:
+ // A partial read/write may result if one of the remote_iov elements points to an invalid
+ // memory region in the remote process.
+ //
+ // Partial transfers apply at the granularity of iovec elements. These system calls won't
+ // perform a partial transfer that splits a single iovec element.
+ constexpr size_t kMaxIovecs = 64;
+ struct iovec src_iovs[kMaxIovecs];
+ size_t iovecs_used = 0;
+
+ const char* cur = static_cast<const char*>(src);
+ while (len > 0) {
+ if (iovecs_used == kMaxIovecs) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ src_iovs[iovecs_used].iov_base = const_cast<char*>(cur);
+ if (!IsAlignedParam(cur, PAGE_SIZE)) {
+ src_iovs[iovecs_used].iov_len = AlignUp(cur, PAGE_SIZE) - cur;
+ } else {
+ src_iovs[iovecs_used].iov_len = PAGE_SIZE;
+ }
+
+ src_iovs[iovecs_used].iov_len = std::min(src_iovs[iovecs_used].iov_len, len);
+
+ len -= src_iovs[iovecs_used].iov_len;
+ cur += src_iovs[iovecs_used].iov_len;
+ ++iovecs_used;
+ }
+
+ ssize_t rc = process_vm_readv(getpid(), &dst_iov, 1, src_iovs, iovecs_used, 0);
+ if (rc == -1) {
+ return 0;
+ }
+ return rc;
+#else
+ UNUSED(dst, src, len);
+ return -1;
+#endif
+}
+
+} // namespace art
diff --git a/test/912-classes/src-ex/C.java b/runtime/base/safe_copy.h
index 97f8021486..d0f497c0bd 100644
--- a/test/912-classes/src-ex/C.java
+++ b/runtime/base/safe_copy.h
@@ -14,5 +14,18 @@
* limitations under the License.
*/
-public class C extends A {
-}
+#ifndef ART_RUNTIME_BASE_SAFE_COPY_H_
+#define ART_RUNTIME_BASE_SAFE_COPY_H_
+
+#include <sys/types.h>
+
+namespace art {
+
+// Safely dereference a pointer.
+// Returns -1 if safe copy isn't implemented on the platform, or if the transfer is too large.
+// Returns 0 if src is unreadable.
+ssize_t SafeCopy(void *dst, const void *src, size_t len);
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_SAFE_COPY_H_
diff --git a/runtime/base/safe_copy_test.cc b/runtime/base/safe_copy_test.cc
new file mode 100644
index 0000000000..987895e6b7
--- /dev/null
+++ b/runtime/base/safe_copy_test.cc
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "safe_copy.h"
+
+#include "common_runtime_test.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/user.h>
+
+namespace art {
+
+#if defined(__linux__)
+
+TEST(SafeCopyTest, smoke) {
+ // Map four pages, mark the second one as PROT_NONE, unmap the last one.
+ void* map = mmap(nullptr, PAGE_SIZE * 4, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ ASSERT_NE(MAP_FAILED, map);
+ char* page1 = static_cast<char*>(map);
+ char* page2 = page1 + PAGE_SIZE;
+ char* page3 = page2 + PAGE_SIZE;
+ char* page4 = page3 + PAGE_SIZE;
+ ASSERT_EQ(0, mprotect(page1 + PAGE_SIZE, PAGE_SIZE, PROT_NONE));
+ ASSERT_EQ(0, munmap(page4, PAGE_SIZE));
+
+ page1[0] = 'a';
+ page1[PAGE_SIZE - 1] = 'z';
+
+ page3[0] = 'b';
+ page3[PAGE_SIZE - 1] = 'y';
+
+ char buf[PAGE_SIZE];
+
+ // Completely valid read.
+ memset(buf, 0xCC, sizeof(buf));
+ EXPECT_EQ(static_cast<ssize_t>(PAGE_SIZE), SafeCopy(buf, page1, PAGE_SIZE)) << strerror(errno);
+ EXPECT_EQ(0, memcmp(buf, page1, PAGE_SIZE));
+
+ // Reading into a guard page.
+ memset(buf, 0xCC, sizeof(buf));
+ EXPECT_EQ(static_cast<ssize_t>(PAGE_SIZE - 1), SafeCopy(buf, page1 + 1, PAGE_SIZE));
+ EXPECT_EQ(0, memcmp(buf, page1 + 1, PAGE_SIZE - 1));
+
+ // Reading from a guard page into a real page.
+ memset(buf, 0xCC, sizeof(buf));
+ EXPECT_EQ(0, SafeCopy(buf, page2 + PAGE_SIZE - 1, PAGE_SIZE));
+
+ // Reading off of the end of a mapping.
+ memset(buf, 0xCC, sizeof(buf));
+ EXPECT_EQ(static_cast<ssize_t>(PAGE_SIZE), SafeCopy(buf, page3, PAGE_SIZE * 2));
+ EXPECT_EQ(0, memcmp(buf, page3, PAGE_SIZE));
+
+ // Completely invalid.
+ EXPECT_EQ(0, SafeCopy(buf, page1 + PAGE_SIZE, PAGE_SIZE));
+
+ // Clean up.
+ ASSERT_EQ(0, munmap(map, PAGE_SIZE * 3));
+}
+
+TEST(SafeCopyTest, alignment) {
+ // Copy the middle of a mapping to the end of another one.
+ void* src_map = mmap(nullptr, PAGE_SIZE * 3, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ ASSERT_NE(MAP_FAILED, src_map);
+
+ // Add a guard page to make sure we don't write past the end of the mapping.
+ void* dst_map = mmap(nullptr, PAGE_SIZE * 4, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ ASSERT_NE(MAP_FAILED, dst_map);
+
+ char* src = static_cast<char*>(src_map);
+ char* dst = static_cast<char*>(dst_map);
+ ASSERT_EQ(0, mprotect(dst + 3 * PAGE_SIZE, PAGE_SIZE, PROT_NONE));
+
+ src[512] = 'a';
+ src[PAGE_SIZE * 3 - 512 - 1] = 'z';
+
+ EXPECT_EQ(static_cast<ssize_t>(PAGE_SIZE * 3 - 1024),
+ SafeCopy(dst + 1024, src + 512, PAGE_SIZE * 3 - 1024));
+ EXPECT_EQ(0, memcmp(dst + 1024, src + 512, PAGE_SIZE * 3 - 1024));
+
+ ASSERT_EQ(0, munmap(src_map, PAGE_SIZE * 3));
+ ASSERT_EQ(0, munmap(dst_map, PAGE_SIZE * 4));
+}
+
+#endif // defined(__linux__)
+
+} // namespace art
diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc
index fd23ced7f7..546e59d223 100644
--- a/runtime/entrypoints/jni/jni_entrypoints.cc
+++ b/runtime/entrypoints/jni/jni_entrypoints.cc
@@ -25,10 +25,10 @@ namespace art {
// Used by the JNI dlsym stub to find the native method to invoke if none is registered.
#if defined(__arm__) || defined(__aarch64__)
-extern "C" void* artFindNativeMethod() {
+extern "C" const void* artFindNativeMethod() {
Thread* self = Thread::Current();
#else
-extern "C" void* artFindNativeMethod(Thread* self) {
+extern "C" const void* artFindNativeMethod(Thread* self) {
DCHECK_EQ(self, Thread::Current());
#endif
Locks::mutator_lock_->AssertNotHeld(self); // We come here as Native.
@@ -45,8 +45,7 @@ extern "C" void* artFindNativeMethod(Thread* self) {
return nullptr;
} else {
// Register so that future calls don't come here
- method->RegisterNative(native_code, false);
- return native_code;
+ return method->RegisterNative(native_code, false);
}
}
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 354ae205e8..2b349e39a0 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -2025,9 +2025,9 @@ void BuildGenericJniFrameVisitor::FinalizeHandleScope(Thread* self) {
}
#if defined(__arm__) || defined(__aarch64__)
-extern "C" void* artFindNativeMethod();
+extern "C" const void* artFindNativeMethod();
#else
-extern "C" void* artFindNativeMethod(Thread* self);
+extern "C" const void* artFindNativeMethod(Thread* self);
#endif
static uint64_t artQuickGenericJniEndJNIRef(Thread* self,
@@ -2126,7 +2126,7 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod**
}
// Retrieve the stored native code.
- void* nativeCode = called->GetEntryPointFromJni();
+ void const* nativeCode = called->GetEntryPointFromJni();
// There are two cases for the content of nativeCode:
// 1) Pointer to the native function.
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 4220250c38..7f738bf5a9 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -21,8 +21,10 @@
#include <sys/ucontext.h>
#include "art_method-inl.h"
+#include "base/safe_copy.h"
#include "base/stl_util.h"
#include "mirror/class.h"
+#include "mirror/object_reference.h"
#include "oat_quick_method_header.h"
#include "sigchain.h"
#include "thread-inl.h"
@@ -42,6 +44,82 @@ static bool art_fault_handler(int sig, siginfo_t* info, void* context) {
return fault_manager.HandleFault(sig, info, context);
}
+#if defined(__linux__)
+
+// Change to verify the safe implementations against the original ones.
+constexpr bool kVerifySafeImpls = false;
+
+// Provide implementations of ArtMethod::GetDeclaringClass and VerifyClassClass that use SafeCopy
+// to safely dereference pointers which are potentially garbage.
+// Only available on Linux due to availability of SafeCopy.
+
+static mirror::Class* SafeGetDeclaringClass(ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ char* method_declaring_class =
+ reinterpret_cast<char*>(method) + ArtMethod::DeclaringClassOffset().SizeValue();
+
+ // ArtMethod::declaring_class_ is a GcRoot<mirror::Class>.
+ // Read it out into as a CompressedReference directly for simplicity's sake.
+ mirror::CompressedReference<mirror::Class> cls;
+ ssize_t rc = SafeCopy(&cls, method_declaring_class, sizeof(cls));
+ CHECK_NE(-1, rc);
+
+ if (kVerifySafeImpls) {
+ mirror::Class* actual_class = method->GetDeclaringClassUnchecked<kWithoutReadBarrier>();
+ CHECK_EQ(actual_class, cls.AsMirrorPtr());
+ }
+
+ if (rc != sizeof(cls)) {
+ return nullptr;
+ }
+
+ return cls.AsMirrorPtr();
+}
+
+static mirror::Class* SafeGetClass(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+ char* obj_cls = reinterpret_cast<char*>(obj) + mirror::Object::ClassOffset().SizeValue();
+
+ mirror::HeapReference<mirror::Class> cls =
+ mirror::HeapReference<mirror::Class>::FromMirrorPtr(nullptr);
+ ssize_t rc = SafeCopy(&cls, obj_cls, sizeof(cls));
+ CHECK_NE(-1, rc);
+
+ if (kVerifySafeImpls) {
+ mirror::Class* actual_class = obj->GetClass<kVerifyNone>();
+ CHECK_EQ(actual_class, cls.AsMirrorPtr());
+ }
+
+ if (rc != sizeof(cls)) {
+ return nullptr;
+ }
+
+ return cls.AsMirrorPtr();
+}
+
+static bool SafeVerifyClassClass(mirror::Class* cls) REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::Class* c_c = SafeGetClass(cls);
+ bool result = c_c != nullptr && c_c == SafeGetClass(c_c);
+
+ if (kVerifySafeImpls) {
+ CHECK_EQ(VerifyClassClass(cls), result);
+ }
+
+ return result;
+}
+
+#else
+
+static mirror::Class* SafeGetDeclaringClass(ArtMethod* method_obj)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return method_obj->GetDeclaringClassUnchecked<kWithoutReadBarrier>();
+}
+
+static bool SafeVerifyClassClass(mirror::Class* cls) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return VerifyClassClass(cls);
+}
+#endif
+
+
FaultManager::FaultManager() : initialized_(false) {
sigaction(SIGSEGV, nullptr, &oldaction_);
}
@@ -191,20 +269,19 @@ bool FaultManager::IsInGeneratedCode(siginfo_t* siginfo, void* context, bool che
// Verify that the potential method is indeed a method.
// TODO: check the GC maps to make sure it's an object.
// Check that the class pointer inside the object is not null and is aligned.
- // TODO: Method might be not a heap address, and GetClass could fault.
// No read barrier because method_obj may not be a real object.
- mirror::Class* cls = method_obj->GetDeclaringClassUnchecked<kWithoutReadBarrier>();
+ mirror::Class* cls = SafeGetDeclaringClass(method_obj);
if (cls == nullptr) {
VLOG(signals) << "not a class";
return false;
}
+
if (!IsAligned<kObjectAlignment>(cls)) {
VLOG(signals) << "not aligned";
return false;
}
-
- if (!VerifyClassClass(cls)) {
+ if (!SafeVerifyClassClass(cls)) {
VLOG(signals) << "not a class class";
return false;
}
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 5418d3569e..b146b51033 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -2277,7 +2277,8 @@ class JNI {
// TODO: make this a hard register error in the future.
}
- m->RegisterNative(fnPtr, is_fast);
+ const void* final_function_ptr = m->RegisterNative(fnPtr, is_fast);
+ UNUSED(final_function_ptr);
}
return JNI_OK;
}
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 493da271d1..a00674a9fe 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1497,11 +1497,18 @@ CompilerFilter::Filter OatFile::GetCompilerFilter() const {
static constexpr char kDexClassPathEncodingSeparator = '*';
-std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files) {
+std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files,
+ std::string& base_dir) {
std::ostringstream out;
for (const DexFile* dex_file : dex_files) {
- out << dex_file->GetLocation().c_str();
+ const std::string& location = dex_file->GetLocation();
+ // Find paths that were relative and convert them back from absolute.
+ if (!base_dir.empty() && location.substr(0, base_dir.length()) == base_dir) {
+ out << location.substr(base_dir.length() + 1).c_str();
+ } else {
+ out << dex_file->GetLocation().c_str();
+ }
out << kDexClassPathEncodingSeparator;
out << dex_file->GetLocationChecksum();
out << kDexClassPathEncodingSeparator;
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index d24283afee..06c76b5464 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -288,7 +288,9 @@ class OatFile {
const char* abs_dex_location, const std::string& rel_dex_location);
// Create a dependency list (dex locations and checksums) for the given dex files.
- static std::string EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files);
+ // Removes dex file paths prefixed with base_dir to convert them back to relative paths.
+ static std::string EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files,
+ std::string& base_dir);
// Finds the associated oat class for a dex_file and descriptor. Returns an invalid OatClass on
// error and sets found to false.
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index a950980e6b..139022210c 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -440,8 +440,12 @@ static bool AreSharedLibrariesOk(const std::string& shared_libraries,
return false;
}
+ // Check that the loaded dex files have the same order and checksums as the shared libraries.
for (size_t i = 0; i < dex_files.size(); ++i) {
- if (dex_files[i]->GetLocation() != shared_libraries_split[i * 2]) {
+ std::string absolute_library_path =
+ OatFile::ResolveRelativeEncodedDexLocation(dex_files[i]->GetLocation().c_str(),
+ shared_libraries_split[i * 2]);
+ if (dex_files[i]->GetLocation() != absolute_library_path) {
return false;
}
char* end;
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 39e603e1e7..c3a94b93a0 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -1556,6 +1556,7 @@ extern "C" bool ArtPlugin_Initialize() {
ThreadUtil::Register(&gEventHandler);
ClassUtil::Register(&gEventHandler);
DumpUtil::Register(&gEventHandler);
+ MethodUtil::Register(&gEventHandler);
SearchUtil::Register();
HeapUtil::Register();
@@ -1569,6 +1570,7 @@ extern "C" bool ArtPlugin_Deinitialize() {
ThreadUtil::Unregister();
ClassUtil::Unregister();
DumpUtil::Unregister();
+ MethodUtil::Unregister();
SearchUtil::Unregister();
HeapUtil::Unregister();
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 2ff3a478c4..2a2aa4c199 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -223,7 +223,7 @@ const jvmtiCapabilities kPotentialCapabilities = {
.can_generate_compiled_method_load_events = 0,
.can_generate_monitor_events = 0,
.can_generate_vm_object_alloc_events = 1,
- .can_generate_native_method_bind_events = 0,
+ .can_generate_native_method_bind_events = 1,
.can_generate_garbage_collection_events = 1,
.can_generate_object_free_events = 1,
.can_force_early_return = 0,
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index 233b45cda8..57abf3142d 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -191,6 +191,27 @@ inline void EventHandler::DispatchEvent(ArtJvmTiEnv* env, art::Thread* thread, A
}
}
+// Need to give a custom specialization for NativeMethodBind since it has to deal with an out
+// variable.
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>(art::Thread* thread,
+ JNIEnv* jnienv,
+ jthread jni_thread,
+ jmethodID method,
+ void* cur_method,
+ void** new_method) const {
+ *new_method = cur_method;
+ for (ArtJvmTiEnv* env : envs) {
+ if (env != nullptr && ShouldDispatch<ArtJvmtiEvent::kNativeMethodBind>(env, thread)) {
+ auto callback = impl::GetCallback<ArtJvmtiEvent::kNativeMethodBind>(env);
+ (*callback)(env, jnienv, jni_thread, method, cur_method, new_method);
+ if (*new_method != nullptr) {
+ cur_method = *new_method;
+ }
+ }
+ }
+}
+
// C++ does not allow partial template function specialization. The dispatch for our separated
// ClassFileLoadHook event types is the same, and in the DispatchClassFileLoadHookEvent helper.
// The following two DispatchEvent specializations dispatch to it.
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index 01bf21d53e..2adabbaff4 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -35,14 +35,59 @@
#include "art_method-inl.h"
#include "base/enums.h"
#include "dex_file_annotations.h"
+#include "events-inl.h"
#include "jni_internal.h"
#include "mirror/object_array-inl.h"
#include "modifiers.h"
+#include "runtime_callbacks.h"
#include "scoped_thread_state_change-inl.h"
+#include "ScopedLocalRef.h"
#include "thread-inl.h"
+#include "thread_list.h"
namespace openjdkjvmti {
+struct TiMethodCallback : public art::MethodCallback {
+ void RegisterNativeMethod(art::ArtMethod* method,
+ const void* cur_method,
+ /*out*/void** new_method)
+ OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kNativeMethodBind)) {
+ art::Thread* thread = art::Thread::Current();
+ ScopedLocalRef<jthread> thread_jni(
+ thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jthread>(thread->GetPeer()));
+ art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
+ event_handler->DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>(
+ thread,
+ static_cast<JNIEnv*>(thread->GetJniEnv()),
+ thread_jni.get(),
+ art::jni::EncodeArtMethod(method),
+ const_cast<void*>(cur_method),
+ new_method);
+ }
+ }
+
+ EventHandler* event_handler = nullptr;
+};
+
+TiMethodCallback gMethodCallback;
+
+void MethodUtil::Register(EventHandler* handler) {
+ gMethodCallback.event_handler = handler;
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Add method callback");
+ art::Runtime::Current()->GetRuntimeCallbacks()->AddMethodCallback(&gMethodCallback);
+}
+
+void MethodUtil::Unregister() {
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Remove method callback");
+ art::Runtime* runtime = art::Runtime::Current();
+ runtime->GetRuntimeCallbacks()->RemoveMethodCallback(&gMethodCallback);
+}
+
jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED,
jmethodID method,
jint* size_ptr) {
diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h
index e5c1705ada..cc161c8fed 100644
--- a/runtime/openjdkjvmti/ti_method.h
+++ b/runtime/openjdkjvmti/ti_method.h
@@ -37,8 +37,13 @@
namespace openjdkjvmti {
+class EventHandler;
+
class MethodUtil {
public:
+ static void Register(EventHandler* event_handler);
+ static void Unregister();
+
static jvmtiError GetArgumentsSize(jvmtiEnv* env, jmethodID method, jint* size_ptr);
static jvmtiError GetMaxLocals(jvmtiEnv* env, jmethodID method, jint* max_ptr);
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index 25324b52d1..16d6c13722 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -18,6 +18,7 @@
#include <algorithm>
+#include "art_method.h"
#include "base/macros.h"
#include "class_linker.h"
#include "thread.h"
@@ -131,4 +132,25 @@ void RuntimeCallbacks::NextRuntimePhase(RuntimePhaseCallback::RuntimePhase phase
}
}
+void RuntimeCallbacks::AddMethodCallback(MethodCallback* cb) {
+ method_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveMethodCallback(MethodCallback* cb) {
+ Remove(cb, &method_callbacks_);
+}
+
+void RuntimeCallbacks::RegisterNativeMethod(ArtMethod* method,
+ const void* in_cur_method,
+ /*out*/void** new_method) {
+ void* cur_method = const_cast<void*>(in_cur_method);
+ *new_method = cur_method;
+ for (MethodCallback* cb : method_callbacks_) {
+ cb->RegisterNativeMethod(method, cur_method, new_method);
+ if (*new_method != nullptr) {
+ cur_method = *new_method;
+ }
+ }
+}
+
} // namespace art
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index d321254e17..e8f1824262 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -31,8 +31,10 @@ class Class;
class ClassLoader;
} // namespace mirror
+class ArtMethod;
class ClassLoadCallback;
class Thread;
+class MethodCallback;
class ThreadLifecycleCallback;
// Note: RuntimeCallbacks uses the mutator lock to synchronize the callback lists. A thread must
@@ -110,6 +112,14 @@ class RuntimeCallbacks {
/*out*/DexFile::ClassDef const** final_class_def)
REQUIRES_SHARED(Locks::mutator_lock_);
+ void AddMethodCallback(MethodCallback* cb) REQUIRES(Locks::mutator_lock_);
+ void RemoveMethodCallback(MethodCallback* cb) REQUIRES(Locks::mutator_lock_);
+
+ void RegisterNativeMethod(ArtMethod* method,
+ const void* original_implementation,
+ /*out*/void** new_implementation)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
std::vector<ThreadLifecycleCallback*> thread_callbacks_
GUARDED_BY(Locks::mutator_lock_);
@@ -118,7 +128,9 @@ class RuntimeCallbacks {
std::vector<RuntimeSigQuitCallback*> sigquit_callbacks_
GUARDED_BY(Locks::mutator_lock_);
std::vector<RuntimePhaseCallback*> phase_callbacks_
- GUARDED_BY(Locks::mutator_lock_);
+ GUARDED_BY(Locks::mutator_lock_);
+ std::vector<MethodCallback*> method_callbacks_
+ GUARDED_BY(Locks::mutator_lock_);
};
} // namespace art
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 8e4c166492..55e793527e 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -428,8 +428,6 @@ void VerifierDeps::AddAssignability(const DexFile& dex_file,
return;
}
- DCHECK_EQ(is_assignable, destination->IsAssignableFrom(source));
-
if (destination->IsArrayClass() && source->IsArrayClass()) {
// Both types are arrays. Break down to component types and add recursively.
// This helps filter out destinations from compiled DEX files (see below)
@@ -447,6 +445,10 @@ void VerifierDeps::AddAssignability(const DexFile& dex_file,
is_assignable);
return;
}
+ } else {
+ // We only do this check for non-array types, as arrays might have erroneous
+ // component types which makes the IsAssignableFrom check unreliable.
+ DCHECK_EQ(is_assignable, destination->IsAssignableFrom(source));
}
DexFileDeps* dex_deps = GetDexFileDeps(dex_file);
diff --git a/test/646-checker-hadd-alt-byte/expected.txt b/test/646-checker-hadd-alt-byte/expected.txt
new file mode 100644
index 0000000000..b0aad4deb5
--- /dev/null
+++ b/test/646-checker-hadd-alt-byte/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/646-checker-hadd-alt-byte/info.txt b/test/646-checker-hadd-alt-byte/info.txt
new file mode 100644
index 0000000000..46e73345d8
--- /dev/null
+++ b/test/646-checker-hadd-alt-byte/info.txt
@@ -0,0 +1 @@
+Functional tests on halving-add SIMD vectorization.
diff --git a/test/646-checker-hadd-alt-byte/src/Main.java b/test/646-checker-hadd-alt-byte/src/Main.java
new file mode 100644
index 0000000000..d1b33ea0da
--- /dev/null
+++ b/test/646-checker-hadd-alt-byte/src/Main.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Tests for halving-add idiomatic vectorization.
+ *
+ * Alternative version expressed with logical shift right
+ * in the higher precision (has no impact on idiom).
+ */
+public class Main {
+
+ private static final int N = 256;
+ private static final int M = N * N + 15;
+
+ static byte[] sB1 = new byte[M];
+ static byte[] sB2 = new byte[M];
+ static byte[] sBo = new byte[M];
+
+ /// CHECK-START: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_signed(byte[] b1, byte[] b2, byte[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (byte) ((b1[i] + b2[i]) >>> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<I255:i\d+>> IntConstant 255 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And1:i\d+>> And [<<Get1>>,<<I255>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And2:i\d+>> And [<<Get2>>,<<I255>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<And1>>,<<And2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_unsigned(byte[] b1, byte[] b2, byte[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (byte) (((b1[i] & 0xff) + (b2[i] & 0xff)) >>> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Add1>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Add2>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void rounding_halving_add_signed(byte[] b1, byte[] b2, byte[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (byte) ((b1[i] + b2[i] + 1) >>> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<I255:i\d+>> IntConstant 255 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And1:i\d+>> And [<<Get1>>,<<I255>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And2:i\d+>> And [<<Get2>>,<<I255>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<And1>>,<<And2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Add1>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Add2>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void rounding_halving_add_unsigned(byte[] b1, byte[] b2, byte[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (byte) (((b1[i] & 0xff) + (b2[i] & 0xff) + 1) >>> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<I127:i\d+>> IntConstant 127 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get>>,<<I127>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<I127:i\d+>> IntConstant 127 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I127>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:false rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_signed_constant(byte[] b1, byte[] bo) {
+ int min_length = Math.min(bo.length, b1.length);
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (byte) ((b1[i] + 0x7f) >>> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<I255:i\d+>> IntConstant 255 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And:i\d+>> And [<<Get>>,<<I255>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<And>>,<<I255>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<I255:i\d+>> IntConstant 255 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I255>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_unsigned_constant(byte[] b1, byte[] bo) {
+ int min_length = Math.min(bo.length, b1.length);
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (byte) (((b1[i] & 0xff) + 0xff) >>> 1);
+ }
+ }
+
+ public static void main(String[] args) {
+ // Initialize cross-values to test all cases, and also
+ // set up some extra values to exercise the cleanup loop.
+ int k = 0;
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ sB1[k] = (byte) i;
+ sB2[k] = (byte) j;
+ k++;
+ }
+ }
+ for (int i = 0; i < 15; i++) {
+ sB1[k] = (byte) i;
+ sB2[k] = 100;
+ k++;
+ }
+ expectEquals(k, M);
+
+ // Test halving add idioms. Note that the expected result is computed
+ // with the arithmetic >> to demonstrate the computed narrower result
+ // does not depend on the wider >> or >>>.
+ halving_add_signed(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ byte e = (byte) ((sB1[i] + sB2[i]) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ halving_add_unsigned(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ byte e = (byte) (((sB1[i] & 0xff) + (sB2[i] & 0xff)) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ rounding_halving_add_signed(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ byte e = (byte) ((sB1[i] + sB2[i] + 1) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ rounding_halving_add_unsigned(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ byte e = (byte) (((sB1[i] & 0xff) + (sB2[i] & 0xff) + 1) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ halving_add_signed_constant(sB1, sBo);
+ for (int i = 0; i < M; i++) {
+ byte e = (byte) ((sB1[i] + 0x7f) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ halving_add_unsigned_constant(sB1, sBo);
+ for (int i = 0; i < M; i++) {
+ byte e = (byte) (((sB1[i] & 0xff) + 0xff) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/646-checker-hadd-alt-char/expected.txt b/test/646-checker-hadd-alt-char/expected.txt
new file mode 100644
index 0000000000..b0aad4deb5
--- /dev/null
+++ b/test/646-checker-hadd-alt-char/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/646-checker-hadd-alt-char/info.txt b/test/646-checker-hadd-alt-char/info.txt
new file mode 100644
index 0000000000..46e73345d8
--- /dev/null
+++ b/test/646-checker-hadd-alt-char/info.txt
@@ -0,0 +1 @@
+Functional tests on halving-add SIMD vectorization.
diff --git a/test/646-checker-hadd-alt-char/src/Main.java b/test/646-checker-hadd-alt-char/src/Main.java
new file mode 100644
index 0000000000..1ea8d3fe07
--- /dev/null
+++ b/test/646-checker-hadd-alt-char/src/Main.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Tests for halving-add idiomatic vectorization.
+ *
+ * Alternative version expressed with logical shift right
+ * in the higher precision (has no impact on idiom).
+ */
+public class Main {
+
+ private static final int N = 64 * 1024;
+ private static final int M = N + 31;
+
+ static char[] sB1 = new char[M];
+ static char[] sB2 = new char[M];
+ static char[] sBo = new char[M];
+
+ /// CHECK-START: void Main.halving_add_unsigned(char[], char[], char[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_unsigned(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_unsigned(char[] b1, char[] b2, char[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (char) ((b1[i] + b2[i]) >>> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.halving_add_also_unsigned(char[], char[], char[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<IMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And1:i\d+>> And [<<Get1>>,<<IMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And2:i\d+>> And [<<Get2>>,<<IMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<And1>>,<<And2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
+ // Note: HAnd has no impact (already a zero extension).
+ //
+ private static void halving_add_also_unsigned(char[] b1, char[] b2, char[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (char) (((b1[i] & 0xffff) + (b2[i] & 0xffff)) >>> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.rounding_halving_add_unsigned(char[], char[], char[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Add1>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Add2>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void rounding_halving_add_unsigned(char[] b1, char[] b2, char[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (char) ((b1[i] + b2[i] + 1) >>> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.rounding_halving_add_also_unsigned(char[], char[], char[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<IMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And1:i\d+>> And [<<Get1>>,<<IMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And2:i\d+>> And [<<Get2>>,<<IMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<And1>>,<<And2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Add1>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Add2>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.rounding_halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
+ // Note: HAnd has no impact (already a zero extension).
+ //
+ private static void rounding_halving_add_also_unsigned(char[] b1, char[] b2, char[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (char) (((b1[i] & 0xffff) + (b2[i] & 0xffff) + 1) >>> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.halving_add_unsigned_constant(char[], char[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get>>,<<UMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_unsigned_constant(char[] b1, char[] bo) {
+ int min_length = Math.min(bo.length, b1.length);
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (char) ((b1[i] + 0xffff) >>> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.halving_add_also_unsigned_constant(char[], char[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And:i\d+>> And [<<Get>>,<<UMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<And>>,<<UMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_also_unsigned_constant(char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
+ // Note: HAnd has no impact (already a zero extension).
+ //
+ private static void halving_add_also_unsigned_constant(char[] b1, char[] bo) {
+ int min_length = Math.min(bo.length, b1.length);
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (char) (((b1[i] & 0xffff) + 0xffff) >>> 1);
+ }
+ }
+
+ public static void main(String[] args) {
+ // Some interesting values.
+ char[] interesting = {
+ (char) 0x0000,
+ (char) 0x0001,
+ (char) 0x0002,
+ (char) 0x1234,
+ (char) 0x8000,
+ (char) 0x8001,
+ (char) 0x7fff,
+ (char) 0xffff
+ };
+ // Initialize cross-values to test all cases, and also
+ // set up some extra values to exercise the cleanup loop.
+ for (int i = 0; i < M; i++) {
+ sB1[i] = (char) i;
+ sB2[i] = interesting[i & 7];
+ }
+
+ // Test halving add idioms. Note that the expected result is computed
+ // with the arithmetic >> to demonstrate the computed narrower result
+ // does not depend on the wider >> or >>>.
+ halving_add_unsigned(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ char e = (char) ((sB1[i] + sB2[i]) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ halving_add_also_unsigned(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ char e = (char) ((sB1[i] + sB2[i]) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ rounding_halving_add_unsigned(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ char e = (char) ((sB1[i] + sB2[i] + 1) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ rounding_halving_add_also_unsigned(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ char e = (char) ((sB1[i] + sB2[i] + 1) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ halving_add_unsigned_constant(sB1, sBo);
+ for (int i = 0; i < M; i++) {
+ char e = (char) ((sB1[i] + 0xffff) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ halving_add_also_unsigned_constant(sB1, sBo);
+ for (int i = 0; i < M; i++) {
+ char e = (char) ((sB1[i] + 0xffff) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/646-checker-hadd-alt-short/expected.txt b/test/646-checker-hadd-alt-short/expected.txt
new file mode 100644
index 0000000000..b0aad4deb5
--- /dev/null
+++ b/test/646-checker-hadd-alt-short/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/646-checker-hadd-alt-short/info.txt b/test/646-checker-hadd-alt-short/info.txt
new file mode 100644
index 0000000000..46e73345d8
--- /dev/null
+++ b/test/646-checker-hadd-alt-short/info.txt
@@ -0,0 +1 @@
+Functional tests on halving-add SIMD vectorization.
diff --git a/test/646-checker-hadd-alt-short/src/Main.java b/test/646-checker-hadd-alt-short/src/Main.java
new file mode 100644
index 0000000000..269e6183b4
--- /dev/null
+++ b/test/646-checker-hadd-alt-short/src/Main.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Tests for halving-add idiomatic vectorization.
+ *
+ * Alternative version expressed with logical shift right
+ * in the higher precision (has no impact on idiom).
+ */
+public class Main {
+
+ private static final int N = 64 * 1024;
+ private static final int M = N + 31;
+
+ static short[] sB1 = new short[M];
+ static short[] sB2 = new short[M];
+ static short[] sBo = new short[M];
+
+ /// CHECK-START: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_signed(short[] b1, short[] b2, short[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (short) ((b1[i] + b2[i]) >>> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And1:i\d+>> And [<<Get1>>,<<UMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And2:i\d+>> And [<<Get2>>,<<UMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<And1>>,<<And2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_unsigned(short[] b1, short[] b2, short[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (short) (((b1[i] & 0xffff) + (b2[i] & 0xffff)) >>> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Add1>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Add2>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void rounding_halving_add_signed(short[] b1, short[] b2, short[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (short) ((b1[i] + b2[i] + 1) >>> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And1:i\d+>> And [<<Get1>>,<<UMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And2:i\d+>> And [<<Get2>>,<<UMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<And1>>,<<And2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Add1>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Add2>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void rounding_halving_add_unsigned(short[] b1, short[] b2, short[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (short) (((b1[i] & 0xffff) + (b2[i] & 0xffff) + 1) >>> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<SMAX:i\d+>> IntConstant 32767 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get>>,<<SMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<SMAX:i\d+>> IntConstant 32767 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<SMAX>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:false rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_signed_constant(short[] b1, short[] bo) {
+ int min_length = Math.min(bo.length, b1.length);
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (short) ((b1[i] + 0x7fff) >>> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And:i\d+>> And [<<Get>>,<<UMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<And>>,<<UMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_unsigned_constant(short[] b1, short[] bo) {
+ int min_length = Math.min(bo.length, b1.length);
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (short) (((b1[i] & 0xffff) + 0xffff) >>> 1);
+ }
+ }
+
+ public static void main(String[] args) {
+ // Some interesting values.
+ short[] interesting = {
+ (short) 0x0000,
+ (short) 0x0001,
+ (short) 0x0002,
+ (short) 0x1234,
+ (short) 0x8000,
+ (short) 0x8001,
+ (short) 0x7fff,
+ (short) 0xffff
+ };
+ // Initialize cross-values to test all cases, and also
+ // set up some extra values to exercise the cleanup loop.
+ for (int i = 0; i < M; i++) {
+ sB1[i] = (short) i;
+ sB2[i] = interesting[i & 7];
+ }
+
+ // Test halving add idioms. Note that the expected result is computed
+ // with the arithmetic >> to demonstrate the computed narrower result
+ // does not depend on the wider >> or >>>.
+ halving_add_signed(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ short e = (short) ((sB1[i] + sB2[i]) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ halving_add_unsigned(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ short e = (short) (((sB1[i] & 0xffff) + (sB2[i] & 0xffff)) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ rounding_halving_add_signed(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ short e = (short) ((sB1[i] + sB2[i] + 1) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ rounding_halving_add_unsigned(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ short e = (short) (((sB1[i] & 0xffff) + (sB2[i] & 0xffff) + 1) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ halving_add_signed_constant(sB1, sBo);
+ for (int i = 0; i < M; i++) {
+ short e = (short) ((sB1[i] + 0x7fff) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ halving_add_unsigned_constant(sB1, sBo);
+ for (int i = 0; i < M; i++) {
+ short e = (short) (((sB1[i] & 0xffff) + 0xffff) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/646-checker-hadd-byte/expected.txt b/test/646-checker-hadd-byte/expected.txt
new file mode 100644
index 0000000000..b0aad4deb5
--- /dev/null
+++ b/test/646-checker-hadd-byte/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/646-checker-hadd-byte/info.txt b/test/646-checker-hadd-byte/info.txt
new file mode 100644
index 0000000000..46e73345d8
--- /dev/null
+++ b/test/646-checker-hadd-byte/info.txt
@@ -0,0 +1 @@
+Functional tests on halving-add SIMD vectorization.
diff --git a/test/646-checker-hadd-byte/src/Main.java b/test/646-checker-hadd-byte/src/Main.java
new file mode 100644
index 0000000000..7e29a7e60b
--- /dev/null
+++ b/test/646-checker-hadd-byte/src/Main.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Tests for halving-add idiomatic vectorization.
+ */
+public class Main {
+
+ private static final int N = 256;
+ private static final int M = N * N + 15;
+
+ static byte[] sB1 = new byte[M];
+ static byte[] sB2 = new byte[M];
+ static byte[] sBo = new byte[M];
+
+ /// CHECK-START: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_signed(byte[] b1, byte[] b2, byte[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (byte) ((b1[i] + b2[i]) >> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<I255:i\d+>> IntConstant 255 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And1:i\d+>> And [<<Get1>>,<<I255>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And2:i\d+>> And [<<Get2>>,<<I255>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<And1>>,<<And2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_unsigned(byte[] b1, byte[] b2, byte[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (byte) (((b1[i] & 0xff) + (b2[i] & 0xff)) >> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Add1>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Add2>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void rounding_halving_add_signed(byte[] b1, byte[] b2, byte[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (byte) ((b1[i] + b2[i] + 1) >> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<I255:i\d+>> IntConstant 255 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And1:i\d+>> And [<<Get1>>,<<I255>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And2:i\d+>> And [<<Get2>>,<<I255>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<And1>>,<<And2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Add1>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Add2>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void rounding_halving_add_unsigned(byte[] b1, byte[] b2, byte[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (byte) (((b1[i] & 0xff) + (b2[i] & 0xff) + 1) >> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<I127:i\d+>> IntConstant 127 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get>>,<<I127>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<I127:i\d+>> IntConstant 127 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I127>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:false rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_signed_constant(byte[] b1, byte[] bo) {
+ int min_length = Math.min(bo.length, b1.length);
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (byte) ((b1[i] + 0x7f) >> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<I255:i\d+>> IntConstant 255 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And:i\d+>> And [<<Get>>,<<I255>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<And>>,<<I255>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<I255:i\d+>> IntConstant 255 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I255>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_unsigned_constant(byte[] b1, byte[] bo) {
+ int min_length = Math.min(bo.length, b1.length);
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (byte) (((b1[i] & 0xff) + 0xff) >> 1);
+ }
+ }
+
+ public static void main(String[] args) {
+ // Initialize cross-values to test all cases, and also
+ // set up some extra values to exercise the cleanup loop.
+ int k = 0;
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ sB1[k] = (byte) i;
+ sB2[k] = (byte) j;
+ k++;
+ }
+ }
+ for (int i = 0; i < 15; i++) {
+ sB1[k] = (byte) i;
+ sB2[k] = 100;
+ k++;
+ }
+ expectEquals(k, M);
+
+ // Test halving add idioms.
+ halving_add_signed(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ byte e = (byte) ((sB1[i] + sB2[i]) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ halving_add_unsigned(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ byte e = (byte) (((sB1[i] & 0xff) + (sB2[i] & 0xff)) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ rounding_halving_add_signed(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ byte e = (byte) ((sB1[i] + sB2[i] + 1) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ rounding_halving_add_unsigned(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ byte e = (byte) (((sB1[i] & 0xff) + (sB2[i] & 0xff) + 1) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ halving_add_signed_constant(sB1, sBo);
+ for (int i = 0; i < M; i++) {
+ byte e = (byte) ((sB1[i] + 0x7f) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ halving_add_unsigned_constant(sB1, sBo);
+ for (int i = 0; i < M; i++) {
+ byte e = (byte) (((sB1[i] & 0xff) + 0xff) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/646-checker-hadd-char/expected.txt b/test/646-checker-hadd-char/expected.txt
new file mode 100644
index 0000000000..b0aad4deb5
--- /dev/null
+++ b/test/646-checker-hadd-char/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/646-checker-hadd-char/info.txt b/test/646-checker-hadd-char/info.txt
new file mode 100644
index 0000000000..46e73345d8
--- /dev/null
+++ b/test/646-checker-hadd-char/info.txt
@@ -0,0 +1 @@
+Functional tests on halving-add SIMD vectorization.
diff --git a/test/646-checker-hadd-char/src/Main.java b/test/646-checker-hadd-char/src/Main.java
new file mode 100644
index 0000000000..d24608f5af
--- /dev/null
+++ b/test/646-checker-hadd-char/src/Main.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Tests for halving-add idiomatic vectorization.
+ */
+public class Main {
+
+ private static final int N = 64 * 1024;
+ private static final int M = N + 31;
+
+ static char[] sB1 = new char[M];
+ static char[] sB2 = new char[M];
+ static char[] sBo = new char[M];
+
+ /// CHECK-START: void Main.halving_add_unsigned(char[], char[], char[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_unsigned(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_unsigned(char[] b1, char[] b2, char[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (char) ((b1[i] + b2[i]) >> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.halving_add_also_unsigned(char[], char[], char[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<IMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And1:i\d+>> And [<<Get1>>,<<IMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And2:i\d+>> And [<<Get2>>,<<IMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<And1>>,<<And2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
+ // Note: HAnd has no impact (already a zero extension).
+ //
+ private static void halving_add_also_unsigned(char[] b1, char[] b2, char[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (char) (((b1[i] & 0xffff) + (b2[i] & 0xffff)) >> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.rounding_halving_add_unsigned(char[], char[], char[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Add1>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Add2>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void rounding_halving_add_unsigned(char[] b1, char[] b2, char[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (char) ((b1[i] + b2[i] + 1) >> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.rounding_halving_add_also_unsigned(char[], char[], char[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<IMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And1:i\d+>> And [<<Get1>>,<<IMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And2:i\d+>> And [<<Get2>>,<<IMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<And1>>,<<And2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Add1>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Add2>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.rounding_halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
+ // Note: HAnd has no impact (already a zero extension).
+ //
+ private static void rounding_halving_add_also_unsigned(char[] b1, char[] b2, char[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (char) (((b1[i] & 0xffff) + (b2[i] & 0xffff) + 1) >> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.halving_add_unsigned_constant(char[], char[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get>>,<<UMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_unsigned_constant(char[] b1, char[] bo) {
+ int min_length = Math.min(bo.length, b1.length);
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (char) ((b1[i] + 0xffff) >> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.halving_add_also_unsigned_constant(char[], char[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And:i\d+>> And [<<Get>>,<<UMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<And>>,<<UMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_also_unsigned_constant(char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
+ // Note: HAnd has no impact (already a zero extension).
+ //
+ private static void halving_add_also_unsigned_constant(char[] b1, char[] bo) {
+ int min_length = Math.min(bo.length, b1.length);
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (char) (((b1[i] & 0xffff) + 0xffff) >> 1);
+ }
+ }
+
+ public static void main(String[] args) {
+ // Some interesting values.
+ char[] interesting = {
+ (char) 0x0000,
+ (char) 0x0001,
+ (char) 0x0002,
+ (char) 0x1234,
+ (char) 0x8000,
+ (char) 0x8001,
+ (char) 0x7fff,
+ (char) 0xffff
+ };
+ // Initialize cross-values to test all cases, and also
+ // set up some extra values to exercise the cleanup loop.
+ for (int i = 0; i < M; i++) {
+ sB1[i] = (char) i;
+ sB2[i] = interesting[i & 7];
+ }
+
+ // Test halving add idioms.
+ halving_add_unsigned(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ char e = (char) ((sB1[i] + sB2[i]) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ halving_add_also_unsigned(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ char e = (char) ((sB1[i] + sB2[i]) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ rounding_halving_add_unsigned(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ char e = (char) ((sB1[i] + sB2[i] + 1) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ rounding_halving_add_also_unsigned(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ char e = (char) ((sB1[i] + sB2[i] + 1) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ halving_add_unsigned_constant(sB1, sBo);
+ for (int i = 0; i < M; i++) {
+ char e = (char) ((sB1[i] + 0xffff) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ halving_add_also_unsigned_constant(sB1, sBo);
+ for (int i = 0; i < M; i++) {
+ char e = (char) ((sB1[i] + 0xffff) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/646-checker-hadd-short/expected.txt b/test/646-checker-hadd-short/expected.txt
new file mode 100644
index 0000000000..b0aad4deb5
--- /dev/null
+++ b/test/646-checker-hadd-short/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/646-checker-hadd-short/info.txt b/test/646-checker-hadd-short/info.txt
new file mode 100644
index 0000000000..46e73345d8
--- /dev/null
+++ b/test/646-checker-hadd-short/info.txt
@@ -0,0 +1 @@
+Functional tests on halving-add SIMD vectorization.
diff --git a/test/646-checker-hadd-short/src/Main.java b/test/646-checker-hadd-short/src/Main.java
new file mode 100644
index 0000000000..db495f6433
--- /dev/null
+++ b/test/646-checker-hadd-short/src/Main.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Tests for halving-add idiomatic vectorization.
+ */
+public class Main {
+
+ private static final int N = 64 * 1024;
+ private static final int M = N + 31;
+
+ static short[] sB1 = new short[M];
+ static short[] sB2 = new short[M];
+ static short[] sBo = new short[M];
+
+ /// CHECK-START: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_signed(short[] b1, short[] b2, short[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (short) ((b1[i] + b2[i]) >> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And1:i\d+>> And [<<Get1>>,<<UMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And2:i\d+>> And [<<Get2>>,<<UMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<And1>>,<<And2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_unsigned(short[] b1, short[] b2, short[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (short) (((b1[i] & 0xffff) + (b2[i] & 0xffff)) >> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Add1>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Add2>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void rounding_halving_add_signed(short[] b1, short[] b2, short[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (short) ((b1[i] + b2[i] + 1) >> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And1:i\d+>> And [<<Get1>>,<<UMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And2:i\d+>> And [<<Get2>>,<<UMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<And1>>,<<And2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Add1>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Add2>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void rounding_halving_add_unsigned(short[] b1, short[] b2, short[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (short) (((b1[i] & 0xffff) + (b2[i] & 0xffff) + 1) >> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<SMAX:i\d+>> IntConstant 32767 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get>>,<<SMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<SMAX:i\d+>> IntConstant 32767 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<SMAX>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:false rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_signed_constant(short[] b1, short[] bo) {
+ int min_length = Math.min(bo.length, b1.length);
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (short) ((b1[i] + 0x7fff) >> 1);
+ }
+ }
+
+ /// CHECK-START: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<And:i\d+>> And [<<Get>>,<<UMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<And>>,<<UMAX>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Add>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void halving_add_unsigned_constant(short[] b1, short[] bo) {
+ int min_length = Math.min(bo.length, b1.length);
+ for (int i = 0; i < min_length; i++) {
+ bo[i] = (short) (((b1[i] & 0xffff) + 0xffff) >> 1);
+ }
+ }
+
+ public static void main(String[] args) {
+ // Some interesting values.
+ short[] interesting = {
+ (short) 0x0000,
+ (short) 0x0001,
+ (short) 0x0002,
+ (short) 0x1234,
+ (short) 0x8000,
+ (short) 0x8001,
+ (short) 0x7fff,
+ (short) 0xffff
+ };
+ // Initialize cross-values to test all cases, and also
+ // set up some extra values to exercise the cleanup loop.
+ for (int i = 0; i < M; i++) {
+ sB1[i] = (short) i;
+ sB2[i] = interesting[i & 7];
+ }
+
+ // Test halving add idioms.
+ halving_add_signed(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ short e = (short) ((sB1[i] + sB2[i]) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ halving_add_unsigned(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ short e = (short) (((sB1[i] & 0xffff) + (sB2[i] & 0xffff)) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ rounding_halving_add_signed(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ short e = (short) ((sB1[i] + sB2[i] + 1) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ rounding_halving_add_unsigned(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ short e = (short) (((sB1[i] & 0xffff) + (sB2[i] & 0xffff) + 1) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ halving_add_signed_constant(sB1, sBo);
+ for (int i = 0; i < M; i++) {
+ short e = (short) ((sB1[i] + 0x7fff) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+ halving_add_unsigned_constant(sB1, sBo);
+ for (int i = 0; i < M; i++) {
+ short e = (short) (((sB1[i] & 0xffff) + 0xffff) >> 1);
+ expectEquals(e, sBo[i]);
+ }
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc
index 2636367548..869eacd82c 100644
--- a/test/912-classes/classes.cc
+++ b/test/912-classes/classes.cc
@@ -16,50 +16,39 @@
#include <stdio.h>
+#include <mutex>
+#include <vector>
+
#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
-#include "class_linker.h"
#include "jni.h"
-#include "mirror/class_loader.h"
#include "jvmti.h"
-#include "runtime.h"
-#include "scoped_local_ref.h"
-#include "scoped_utf_chars.h"
-#include "scoped_thread_state_change-inl.h"
-#include "thread-inl.h"
// Test infrastructure
#include "jni_helper.h"
#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "scoped_utf_chars.h"
#include "test_env.h"
namespace art {
namespace Test912Classes {
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_isModifiableClass(
- JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+extern "C" JNIEXPORT jboolean JNICALL Java_art_Test912_isModifiableClass(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
jboolean res = JNI_FALSE;
jvmtiError result = jvmti_env->IsModifiableClass(klass, &res);
- if (result != JVMTI_ERROR_NONE) {
- char* err;
- jvmti_env->GetErrorName(result, &err);
- printf("Failure running IsModifiableClass: %s\n", err);
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
- return JNI_FALSE;
- }
+ JvmtiErrorToException(env, jvmti_env, result);
return res;
}
-extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassSignature(
+extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassSignature(
JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
char* sig;
char* gen;
jvmtiError result = jvmti_env->GetClassSignature(klass, &sig, &gen);
- if (result != JVMTI_ERROR_NONE) {
- char* err;
- jvmti_env->GetErrorName(result, &err);
- printf("Failure running GetClassSignature: %s\n", err);
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+ if (JvmtiErrorToException(env, jvmti_env, result)) {
return nullptr;
}
@@ -83,57 +72,36 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassSignature(
return ret;
}
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInterface(
- JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+extern "C" JNIEXPORT jboolean JNICALL Java_art_Test912_isInterface(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
jboolean is_interface = JNI_FALSE;
jvmtiError result = jvmti_env->IsInterface(klass, &is_interface);
- if (result != JVMTI_ERROR_NONE) {
- char* err;
- jvmti_env->GetErrorName(result, &err);
- printf("Failure running IsInterface: %s\n", err);
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
- return JNI_FALSE;
- }
+ JvmtiErrorToException(env, jvmti_env, result);
return is_interface;
}
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_isArrayClass(
- JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+extern "C" JNIEXPORT jboolean JNICALL Java_art_Test912_isArrayClass(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
jboolean is_array_class = JNI_FALSE;
jvmtiError result = jvmti_env->IsArrayClass(klass, &is_array_class);
- if (result != JVMTI_ERROR_NONE) {
- char* err;
- jvmti_env->GetErrorName(result, &err);
- printf("Failure running IsArrayClass: %s\n", err);
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
- return JNI_FALSE;
- }
+ JvmtiErrorToException(env, jvmti_env, result);
return is_array_class;
}
-extern "C" JNIEXPORT jint JNICALL Java_Main_getClassModifiers(
- JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+extern "C" JNIEXPORT jint JNICALL Java_art_Test912_getClassModifiers(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
jint mod;
jvmtiError result = jvmti_env->GetClassModifiers(klass, &mod);
- if (result != JVMTI_ERROR_NONE) {
- char* err;
- jvmti_env->GetErrorName(result, &err);
- printf("Failure running GetClassModifiers: %s\n", err);
- return JNI_FALSE;
- }
+ JvmtiErrorToException(env, jvmti_env, result);
return mod;
}
-extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassFields(
+extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassFields(
JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
jint count = 0;
jfieldID* fields = nullptr;
jvmtiError result = jvmti_env->GetClassFields(klass, &count, &fields);
- if (result != JVMTI_ERROR_NONE) {
- char* err;
- jvmti_env->GetErrorName(result, &err);
- printf("Failure running GetClassFields: %s\n", err);
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+ if (JvmtiErrorToException(env, jvmti_env, result)) {
return nullptr;
}
@@ -153,15 +121,12 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassFields(
return ret;
}
-extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassMethods(
+extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassMethods(
JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
jint count = 0;
jmethodID* methods = nullptr;
jvmtiError result = jvmti_env->GetClassMethods(klass, &count, &methods);
- if (result != JVMTI_ERROR_NONE) {
- char* err;
- jvmti_env->GetErrorName(result, &err);
- printf("Failure running GetClassMethods: %s\n", err);
+ if (JvmtiErrorToException(env, jvmti_env, result)) {
return nullptr;
}
@@ -181,15 +146,12 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassMethods(
return ret;
}
-extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getImplementedInterfaces(
+extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getImplementedInterfaces(
JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
jint count = 0;
jclass* classes = nullptr;
jvmtiError result = jvmti_env->GetImplementedInterfaces(klass, &count, &classes);
- if (result != JVMTI_ERROR_NONE) {
- char* err;
- jvmti_env->GetErrorName(result, &err);
- printf("Failure running GetImplementedInterfaces: %s\n", err);
+ if (JvmtiErrorToException(env, jvmti_env, result)) {
return nullptr;
}
@@ -203,35 +165,23 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getImplementedInterfaces(
return ret;
}
-extern "C" JNIEXPORT jint JNICALL Java_Main_getClassStatus(
- JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+extern "C" JNIEXPORT jint JNICALL Java_art_Test912_getClassStatus(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
jint status;
jvmtiError result = jvmti_env->GetClassStatus(klass, &status);
- if (result != JVMTI_ERROR_NONE) {
- char* err;
- jvmti_env->GetErrorName(result, &err);
- printf("Failure running GetClassStatus: %s\n", err);
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
- return JNI_FALSE;
- }
+ JvmtiErrorToException(env, jvmti_env, result);
return status;
}
-extern "C" JNIEXPORT jobject JNICALL Java_Main_getClassLoader(
- JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+extern "C" JNIEXPORT jobject JNICALL Java_art_Test912_getClassLoader(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
jobject classloader;
jvmtiError result = jvmti_env->GetClassLoader(klass, &classloader);
- if (result != JVMTI_ERROR_NONE) {
- char* err;
- jvmti_env->GetErrorName(result, &err);
- printf("Failure running GetClassLoader: %s\n", err);
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
- return nullptr;
- }
+ JvmtiErrorToException(env, jvmti_env, result);
return classloader;
}
-extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassLoaderClasses(
+extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassLoaderClasses(
JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jobject jclassloader) {
jint count = 0;
jclass* classes = nullptr;
@@ -250,7 +200,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassLoaderClasses(
return ret;
}
-extern "C" JNIEXPORT jintArray JNICALL Java_Main_getClassVersion(
+extern "C" JNIEXPORT jintArray JNICALL Java_art_Test912_getClassVersion(
JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
jint major, minor;
jvmtiError result = jvmti_env->GetClassVersionNumbers(klass, &minor, &major);
@@ -325,6 +275,22 @@ static void EnableEvents(JNIEnv* env,
JvmtiErrorToException(env, jvmti_env, ret);
}
+static std::mutex gEventsMutex;
+static std::vector<std::string> gEvents;
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassLoadMessages(
+ 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;
+}
+
class ClassLoadPreparePrinter {
public:
static void JNICALL ClassLoadCallback(jvmtiEnv* jenv,
@@ -339,7 +305,14 @@ class ClassLoadPreparePrinter {
if (thread_name == "") {
return;
}
- printf("Load: %s on %s\n", name.c_str(), thread_name.c_str());
+ if (thread_name_filter_ != "" && thread_name_filter_ != thread_name) {
+ return;
+ }
+
+ std::lock_guard<std::mutex> guard(gEventsMutex);
+ gEvents.push_back(android::base::StringPrintf("Load: %s on %s",
+ name.c_str(),
+ thread_name.c_str()));
}
static void JNICALL ClassPrepareCallback(jvmtiEnv* jenv,
@@ -354,14 +327,18 @@ class ClassLoadPreparePrinter {
if (thread_name == "") {
return;
}
- std::string cur_thread_name = GetThreadName(Thread::Current());
- printf("Prepare: %s on %s (cur=%s)\n",
- name.c_str(),
- thread_name.c_str(),
- cur_thread_name.c_str());
+ if (thread_name_filter_ != "" && thread_name_filter_ != thread_name) {
+ return;
+ }
+ std::string cur_thread_name = GetThreadName(jenv, jni_env, nullptr);
+
+ std::lock_guard<std::mutex> guard(gEventsMutex);
+ gEvents.push_back(android::base::StringPrintf("Prepare: %s on %s (cur=%s)",
+ name.c_str(),
+ thread_name.c_str(),
+ cur_thread_name.c_str()));
}
- private:
static std::string GetThreadName(jvmtiEnv* jenv, JNIEnv* jni_env, jthread thread) {
jvmtiThreadInfo info;
jvmtiError result = jenv->GetThreadInfo(thread, &info);
@@ -382,60 +359,28 @@ class ClassLoadPreparePrinter {
return tmp;
}
- static std::string GetThreadName(Thread* thread) {
- std::string tmp;
- thread->GetThreadName(tmp);
- return tmp;
- }
+ static std::string thread_name_filter_;
};
+std::string ClassLoadPreparePrinter::thread_name_filter_;
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test912_enableClassLoadPreparePrintEvents(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean enable, jthread thread) {
+ if (thread != nullptr) {
+ ClassLoadPreparePrinter::thread_name_filter_ =
+ ClassLoadPreparePrinter::GetThreadName(jvmti_env, env, thread);
+ } else {
+ ClassLoadPreparePrinter::thread_name_filter_ = "";
+ }
-extern "C" JNIEXPORT void JNICALL Java_Main_enableClassLoadPreparePrintEvents(
- JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean enable) {
EnableEvents(env,
enable,
ClassLoadPreparePrinter::ClassLoadCallback,
ClassLoadPreparePrinter::ClassPrepareCallback);
}
-struct ClassLoadSeen {
- static void JNICALL ClassLoadSeenCallback(jvmtiEnv* jenv ATTRIBUTE_UNUSED,
- JNIEnv* jni_env ATTRIBUTE_UNUSED,
- jthread thread ATTRIBUTE_UNUSED,
- jclass klass ATTRIBUTE_UNUSED) {
- saw_event = true;
- }
-
- static bool saw_event;
-};
-bool ClassLoadSeen::saw_event = false;
-
-extern "C" JNIEXPORT void JNICALL Java_Main_enableClassLoadSeenEvents(
- JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) {
- EnableEvents(env, b, ClassLoadSeen::ClassLoadSeenCallback, nullptr);
-}
-
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_hadLoadEvent(
- JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED) {
- return ClassLoadSeen::saw_event ? JNI_TRUE : JNI_FALSE;
-}
-
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_isLoadedClass(
- JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring class_name) {
- ScopedUtfChars name(env, class_name);
- ScopedObjectAccess soa(Thread::Current());
- Runtime* current = Runtime::Current();
- ClassLinker* class_linker = current->GetClassLinker();
- bool found =
- class_linker->LookupClass(
- soa.Self(),
- name.c_str(),
- soa.Decode<mirror::ClassLoader>(current->GetSystemClassLoader())) != nullptr;
- return found ? JNI_TRUE : JNI_FALSE;
-}
-
class ClassLoadPrepareEquality {
public:
- static constexpr const char* kClassName = "LMain$ClassE;";
+ static constexpr const char* kClassName = "Lart/Test912$ClassE;";
static constexpr const char* kStorageFieldName = "STATIC";
static constexpr const char* kStorageFieldSig = "Ljava/lang/Object;";
static constexpr const char* kStorageWeakFieldName = "WEAK";
@@ -553,13 +498,13 @@ jobject ClassLoadPrepareEquality::local_stored_class_ = nullptr;
bool ClassLoadPrepareEquality::found_ = false;
bool ClassLoadPrepareEquality::compared_ = false;
-extern "C" JNIEXPORT void JNICALL Java_Main_setEqualityEventStorageClass(
+extern "C" JNIEXPORT void JNICALL Java_art_Test912_setEqualityEventStorageClass(
JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
ClassLoadPrepareEquality::storage_class_ =
reinterpret_cast<jclass>(env->NewGlobalRef(klass));
}
-extern "C" JNIEXPORT void JNICALL Java_Main_enableClassLoadPrepareEqualityEvents(
+extern "C" JNIEXPORT void JNICALL Java_art_Test912_enableClassLoadPrepareEqualityEvents(
JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) {
EnableEvents(env,
b,
diff --git a/test/912-classes/classes_art.cc b/test/912-classes/classes_art.cc
new file mode 100644
index 0000000000..de2e456a53
--- /dev/null
+++ b/test/912-classes/classes_art.cc
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include <stdio.h>
+
+#include <mutex>
+#include <vector>
+
+#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
+
+#include "jni.h"
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "scoped_utf_chars.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test912ArtClasses {
+
+static void EnableEvents(JNIEnv* env,
+ jboolean enable,
+ decltype(jvmtiEventCallbacks().ClassLoad) class_load,
+ decltype(jvmtiEventCallbacks().ClassPrepare) class_prepare) {
+ if (enable == JNI_FALSE) {
+ jvmtiError ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_CLASS_LOAD,
+ nullptr);
+ if (JvmtiErrorToException(env, jvmti_env, ret)) {
+ return;
+ }
+ ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_CLASS_PREPARE,
+ nullptr);
+ JvmtiErrorToException(env, jvmti_env, ret);
+ return;
+ }
+
+ jvmtiEventCallbacks callbacks;
+ memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
+ callbacks.ClassLoad = class_load;
+ callbacks.ClassPrepare = class_prepare;
+ jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
+ if (JvmtiErrorToException(env, jvmti_env, ret)) {
+ return;
+ }
+
+ ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_CLASS_LOAD,
+ nullptr);
+ if (JvmtiErrorToException(env, jvmti_env, ret)) {
+ return;
+ }
+ ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_CLASS_PREPARE,
+ nullptr);
+ JvmtiErrorToException(env, jvmti_env, ret);
+}
+
+struct ClassLoadSeen {
+ static void JNICALL ClassLoadSeenCallback(jvmtiEnv* jenv ATTRIBUTE_UNUSED,
+ JNIEnv* jni_env ATTRIBUTE_UNUSED,
+ jthread thread ATTRIBUTE_UNUSED,
+ jclass klass ATTRIBUTE_UNUSED) {
+ saw_event = true;
+ }
+
+ static bool saw_event;
+};
+bool ClassLoadSeen::saw_event = false;
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test912Art_enableClassLoadSeenEvents(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) {
+ EnableEvents(env, b, ClassLoadSeen::ClassLoadSeenCallback, nullptr);
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_art_Test912Art_hadLoadEvent(
+ JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED) {
+ return ClassLoadSeen::saw_event ? JNI_TRUE : JNI_FALSE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_art_Test912Art_isLoadedClass(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring class_name) {
+ ScopedUtfChars name(env, class_name);
+
+ jint class_count;
+ jclass* classes;
+ jvmtiError res = jvmti_env->GetLoadedClasses(&class_count, &classes);
+ if (JvmtiErrorToException(env, jvmti_env, res)) {
+ return JNI_FALSE;
+ }
+
+ bool found = false;
+ for (jint i = 0; !found && i < class_count; ++i) {
+ char* sig;
+ jvmtiError res2 = jvmti_env->GetClassSignature(classes[i], &sig, nullptr);
+ if (JvmtiErrorToException(env, jvmti_env, res2)) {
+ return JNI_FALSE;
+ }
+
+ found = strcmp(name.c_str(), sig) == 0;
+
+ CheckJvmtiError(jvmti_env, jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig)));
+ }
+
+ CheckJvmtiError(jvmti_env, jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(classes)));
+
+ return found;
+}
+
+// We use the implementations from runtime_state.cc.
+
+extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env,
+ jclass,
+ jclass cls,
+ jstring method_name);
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJit(JNIEnv*, jclass);
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test912Art_ensureJitCompiled(
+ JNIEnv* env, jclass klass, jclass test_class, jstring name) {
+ Java_Main_ensureJitCompiled(env, klass, test_class, name);
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_art_Test912Art_hasJit(JNIEnv* env, jclass klass) {
+ return Java_Main_hasJit(env, klass);
+}
+
+} // namespace Test912ArtClasses
+} // namespace art
diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt
index 0f2920a0c2..9dcc5f9c90 100644
--- a/test/912-classes/expected.txt
+++ b/test/912-classes/expected.txt
@@ -6,14 +6,14 @@
11
[Ljava/util/List;, <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>;]
601
-[L$Proxy0;, null]
+[L$Proxy20;, null]
11
[I, null]
411
[[D, null]
411
int interface=false array=false modifiable=false
-$Proxy0 interface=false array=false modifiable=false
+$Proxy20 interface=false array=false modifiable=false
java.lang.Runnable interface=true array=false modifiable=false
java.lang.String interface=false array=false modifiable=false
java.util.ArrayList interface=false array=false modifiable=true
@@ -29,70 +29,65 @@ java.util.ArrayList interface=false array=false modifiable=true
int 100000
class [Ljava.lang.String; 10000
class java.lang.Object 111
-class Main$TestForNonInit 11
-class Main$TestForInitFail 1011
+class art.Test912$TestForNonInit 11
+class art.Test912$TestForInitFail 1011
int []
class [Ljava.lang.String; []
class java.lang.Object []
-interface Main$InfA []
-interface Main$InfB [interface Main$InfA]
-interface Main$InfC [interface Main$InfB]
-class Main$ClassA [interface Main$InfA]
-class Main$ClassB [interface Main$InfB]
-class Main$ClassC [interface Main$InfA, interface Main$InfC]
+interface art.Test912$InfA []
+interface art.Test912$InfB [interface art.Test912$InfA]
+interface art.Test912$InfC [interface art.Test912$InfB]
+class art.Test912$ClassA [interface art.Test912$InfA]
+class art.Test912$ClassB [interface art.Test912$InfB]
+class art.Test912$ClassC [interface art.Test912$InfA, interface art.Test912$InfC]
class java.lang.String null
class [Ljava.lang.String; null
-interface Main$InfA dalvik.system.PathClassLoader
-class $Proxy0 dalvik.system.PathClassLoader
+interface art.Test912$InfA dalvik.system.PathClassLoader
+class $Proxy20 dalvik.system.PathClassLoader
-boot <- src <- src-ex (A,B)
-912-classes-ex.jar+ -> 912-classes.jar+ ->
+boot <- (B) <- (A,C)
[class A, class B, class java.lang.Object]
-912-classes.jar+ ->
[class B, class java.lang.Object]
-boot <- src (B) <- src-ex (A, List)
-912-classes-ex.jar+ -> 912-classes.jar+ ->
+boot <- (B) <- (A, List)
[class A, class java.lang.Object, interface java.util.List]
-912-classes.jar+ ->
[class B, class java.lang.Object]
-boot <- src+src-ex (A,B)
-912-classes.jar+ ->
+boot <- 1+2 (A,B)
[class A, class B, class java.lang.Object]
[37, 0]
B, false
-Load: LB; on main
-Prepare: LB; on main (cur=main)
+Load: LB; on ClassEvents
+Prepare: LB; on ClassEvents (cur=ClassEvents)
B, true
-Load: LB; on main
-Prepare: LB; on main (cur=main)
+Load: LB; on ClassEvents
+Prepare: LB; on ClassEvents (cur=ClassEvents)
C, false
-Load: LA; on main
-Prepare: LA; on main (cur=main)
-Load: LC; on main
-Prepare: LC; on main (cur=main)
+Load: LA; on ClassEvents
+Prepare: LA; on ClassEvents (cur=ClassEvents)
+Load: LC; on ClassEvents
+Prepare: LC; on ClassEvents (cur=ClassEvents)
A, false
C, true
-Load: LA; on main
-Prepare: LA; on main (cur=main)
-Load: LC; on main
-Prepare: LC; on main (cur=main)
+Load: LA; on ClassEvents
+Prepare: LA; on ClassEvents (cur=ClassEvents)
+Load: LC; on ClassEvents
+Prepare: LC; on ClassEvents (cur=ClassEvents)
A, true
A, true
-Load: LA; on main
-Prepare: LA; on main (cur=main)
+Load: LA; on ClassEvents
+Prepare: LA; on ClassEvents (cur=ClassEvents)
C, true
-Load: LC; on main
-Prepare: LC; on main (cur=main)
+Load: LC; on ClassEvents
+Prepare: LC; on ClassEvents (cur=ClassEvents)
C, true
Load: LA; on TestRunner
Prepare: LA; on TestRunner (cur=TestRunner)
Load: LC; on TestRunner
Prepare: LC; on TestRunner (cur=TestRunner)
-Load: L$Proxy1; on main
-Prepare: L$Proxy1; on main (cur=main)
-Load: [LMain; on main
-Prepare: [LMain; on main (cur=main)
+Load: L$Proxy21; on ClassEvents
+Prepare: L$Proxy21; on ClassEvents (cur=ClassEvents)
+Load: [Lart/Test912; on ClassEvents
+Prepare: [Lart/Test912; on ClassEvents (cur=ClassEvents)
diff --git a/test/912-classes/src/B.java b/test/912-classes/src/B.java
deleted file mode 100644
index 52ce4dd58e..0000000000
--- a/test/912-classes/src/B.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-public class B {
-}
diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java
index 6c8858ab65..395cf6fb98 100644
--- a/test/912-classes/src/Main.java
+++ b/test/912-classes/src/Main.java
@@ -14,452 +14,9 @@
* limitations under the License.
*/
-import java.lang.ref.Reference;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Proxy;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
-
public class Main {
public static void main(String[] args) throws Exception {
- art.Main.bindAgentJNIForClass(Main.class);
- doTest();
- }
-
- public static void doTest() throws Exception {
- testClass("java.lang.Object");
- testClass("java.lang.String");
- testClass("java.lang.Math");
- testClass("java.util.List");
-
- testClass(getProxyClass());
-
- testClass(int.class);
- testClass(double[].class);
-
- testClassType(int.class);
- testClassType(getProxyClass());
- testClassType(Runnable.class);
- testClassType(String.class);
- testClassType(ArrayList.class);
-
- testClassType(int[].class);
- testClassType(Runnable[].class);
- testClassType(String[].class);
-
- testClassFields(Integer.class);
- testClassFields(int.class);
- testClassFields(String[].class);
-
- testClassMethods(Integer.class);
- testClassMethods(int.class);
- testClassMethods(String[].class);
-
- testClassStatus(int.class);
- testClassStatus(String[].class);
- testClassStatus(Object.class);
- testClassStatus(TestForNonInit.class);
- try {
- System.out.println(TestForInitFail.dummy);
- } catch (ExceptionInInitializerError e) {
- }
- testClassStatus(TestForInitFail.class);
-
- testInterfaces(int.class);
- testInterfaces(String[].class);
- testInterfaces(Object.class);
- testInterfaces(InfA.class);
- testInterfaces(InfB.class);
- testInterfaces(InfC.class);
- testInterfaces(ClassA.class);
- testInterfaces(ClassB.class);
- testInterfaces(ClassC.class);
-
- testClassLoader(String.class);
- testClassLoader(String[].class);
- testClassLoader(InfA.class);
- testClassLoader(getProxyClass());
-
- testClassLoaderClasses();
-
- System.out.println();
-
- testClassVersion();
-
- System.out.println();
-
- testClassEvents();
- }
-
- 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 testClass(String className) throws Exception {
- Class<?> base = Class.forName(className);
- testClass(base);
- }
-
- private static void testClass(Class<?> base) throws Exception {
- String[] result = getClassSignature(base);
- System.out.println(Arrays.toString(result));
- int mod = getClassModifiers(base);
- if (mod != base.getModifiers()) {
- throw new RuntimeException("Unexpected modifiers: " + base.getModifiers() + " vs " + mod);
- }
- System.out.println(Integer.toHexString(mod));
- }
-
- private static void testClassType(Class<?> c) throws Exception {
- boolean isInterface = isInterface(c);
- boolean isArray = isArrayClass(c);
- boolean isModifiable = isModifiableClass(c);
- System.out.println(c.getName() + " interface=" + isInterface + " array=" + isArray +
- " modifiable=" + isModifiable);
- }
-
- private static void testClassFields(Class<?> c) throws Exception {
- System.out.println(Arrays.toString(getClassFields(c)));
- }
-
- private static void testClassMethods(Class<?> c) throws Exception {
- System.out.println(Arrays.toString(getClassMethods(c)));
- }
-
- private static void testClassStatus(Class<?> c) {
- System.out.println(c + " " + Integer.toBinaryString(getClassStatus(c)));
- }
-
- private static void testInterfaces(Class<?> c) {
- System.out.println(c + " " + Arrays.toString(getImplementedInterfaces(c)));
- }
-
- private static boolean IsBootClassLoader(ClassLoader l) {
- // Hacky check for Android's fake boot classloader.
- return l.getClass().getName().equals("java.lang.BootClassLoader");
- }
-
- private static void testClassLoader(Class<?> c) {
- Object cl = getClassLoader(c);
- System.out.println(c + " " + (cl != null ? cl.getClass().getName() : "null"));
- if (cl == null) {
- if (c.getClassLoader() != null && !IsBootClassLoader(c.getClassLoader())) {
- throw new RuntimeException("Expected " + c.getClassLoader() + ", but got null.");
- }
- } else {
- if (!(cl instanceof ClassLoader)) {
- throw new RuntimeException("Unexpected \"classloader\": " + cl + " (" + cl.getClass() +
- ")");
- }
- if (cl != c.getClassLoader()) {
- throw new RuntimeException("Unexpected classloader: " + c.getClassLoader() + " vs " + cl);
- }
- }
- }
-
- private static void testClassLoaderClasses() throws Exception {
- ClassLoader boot = ClassLoader.getSystemClassLoader().getParent();
- while (boot.getParent() != null) {
- boot = boot.getParent();
- }
-
- System.out.println();
- System.out.println("boot <- src <- src-ex (A,B)");
- ClassLoader cl1 = create(create(boot, DEX1), DEX2);
- Class.forName("B", false, cl1);
- Class.forName("A", false, cl1);
- printClassLoaderClasses(cl1);
-
- System.out.println();
- System.out.println("boot <- src (B) <- src-ex (A, List)");
- ClassLoader cl2 = create(create(boot, DEX1), DEX2);
- Class.forName("A", false, cl2);
- Class.forName("java.util.List", false, cl2);
- Class.forName("B", false, cl2.getParent());
- printClassLoaderClasses(cl2);
-
- System.out.println();
- System.out.println("boot <- src+src-ex (A,B)");
- ClassLoader cl3 = create(boot, DEX1, DEX2);
- Class.forName("B", false, cl3);
- Class.forName("A", false, cl3);
- printClassLoaderClasses(cl3);
-
- // Check that the boot classloader dumps something non-empty.
- Class<?>[] bootClasses = getClassLoaderClasses(boot);
- if (bootClasses.length == 0) {
- throw new RuntimeException("No classes initiated by boot classloader.");
- }
- // Check that at least java.util.List is loaded.
- boolean foundList = false;
- for (Class<?> c : bootClasses) {
- if (c == java.util.List.class) {
- foundList = true;
- break;
- }
- }
- if (!foundList) {
- System.out.println(Arrays.toString(bootClasses));
- throw new RuntimeException("Could not find class java.util.List.");
- }
- }
-
- private static void testClassVersion() {
- System.out.println(Arrays.toString(getClassVersion(Main.class)));
- }
-
- private static void testClassEvents() throws Exception {
- ClassLoader cl = Main.class.getClassLoader();
- while (cl.getParent() != null) {
- cl = cl.getParent();
- }
- final ClassLoader boot = cl;
-
- // The JIT may deeply inline and load some classes. Preload these for test determinism.
- final String PRELOAD_FOR_JIT[] = {
- "java.nio.charset.CoderMalfunctionError",
- "java.util.NoSuchElementException"
- };
- for (String s : PRELOAD_FOR_JIT) {
- Class.forName(s);
- }
-
- Runnable r = new Runnable() {
- @Override
- public void run() {
- try {
- ClassLoader cl6 = create(boot, DEX1, DEX2);
- System.out.println("C, true");
- Class.forName("C", true, cl6);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- };
-
- Thread dummyThread = new Thread();
- dummyThread.start();
- dummyThread.join();
-
- ensureJitCompiled(Main.class, "testClassEvents");
-
- enableClassLoadPreparePrintEvents(true);
-
- ClassLoader cl1 = create(boot, DEX1, DEX2);
- System.out.println("B, false");
- Class.forName("B", false, cl1);
-
- ClassLoader cl2 = create(boot, DEX1, DEX2);
- System.out.println("B, true");
- Class.forName("B", true, cl2);
-
- ClassLoader cl3 = create(boot, DEX1, DEX2);
- System.out.println("C, false");
- Class.forName("C", false, cl3);
- System.out.println("A, false");
- Class.forName("A", false, cl3);
-
- ClassLoader cl4 = create(boot, DEX1, DEX2);
- System.out.println("C, true");
- Class.forName("C", true, cl4);
- System.out.println("A, true");
- Class.forName("A", true, cl4);
-
- ClassLoader cl5 = create(boot, DEX1, DEX2);
- System.out.println("A, true");
- Class.forName("A", true, cl5);
- System.out.println("C, true");
- Class.forName("C", true, cl5);
-
- Thread t = new Thread(r, "TestRunner");
- t.start();
- t.join();
-
- // Check creation of arrays and proxies.
- Proxy.getProxyClass(Main.class.getClassLoader(), new Class[] { Comparable.class });
- Class.forName("[LMain;");
-
- enableClassLoadPreparePrintEvents(false);
-
- // Note: the JIT part of this test is about the JIT pulling in a class not yet touched by
- // anything else in the system. This could be the verifier or the interpreter. We
- // block the interpreter by calling ensureJitCompiled. The verifier, however, must
- // run in configurations where dex2oat didn't verify the class itself. So explicitly
- // check whether the class has been already loaded, and skip then.
- // TODO: Add multiple configurations to the run script once that becomes easier to do.
- if (hasJit() && !isLoadedClass("Main$ClassD")) {
- testClassEventsJit();
- }
-
- testClassLoadPrepareEquality();
- }
-
- private static void testClassEventsJit() throws Exception {
- enableClassLoadSeenEvents(true);
-
- testClassEventsJitImpl();
-
- enableClassLoadSeenEvents(false);
-
- if (!hadLoadEvent()) {
- throw new RuntimeException("Did not get expected load event.");
- }
- }
-
- private static void testClassEventsJitImpl() throws Exception {
- ensureJitCompiled(Main.class, "testClassEventsJitImpl");
-
- if (ClassD.x != 1) {
- throw new RuntimeException("Unexpected value");
- }
- }
-
- private static void testClassLoadPrepareEquality() throws Exception {
- setEqualityEventStorageClass(ClassF.class);
-
- enableClassLoadPrepareEqualityEvents(true);
-
- Class.forName("Main$ClassE");
-
- enableClassLoadPrepareEqualityEvents(false);
- }
-
- private static void printClassLoaderClasses(ClassLoader cl) {
- for (;;) {
- if (cl == null || !cl.getClass().getName().startsWith("dalvik.system")) {
- break;
- }
-
- ClassLoader saved = cl;
- for (;;) {
- if (cl == null || !cl.getClass().getName().startsWith("dalvik.system")) {
- break;
- }
- String s = cl.toString();
- int index1 = s.indexOf("zip file");
- int index2 = s.indexOf(']', index1);
- if (index2 < 0) {
- throw new RuntimeException("Unexpected classloader " + s);
- }
- String zip_file = s.substring(index1, index2);
- int index3 = zip_file.indexOf('"');
- int index4 = zip_file.indexOf('"', index3 + 1);
- if (index4 < 0) {
- throw new RuntimeException("Unexpected classloader " + s);
- }
- String paths = zip_file.substring(index3 + 1, index4);
- String pathArray[] = paths.split(":");
- for (String path : pathArray) {
- int index5 = path.lastIndexOf('/');
- System.out.print(path.substring(index5 + 1));
- System.out.print('+');
- }
- System.out.print(" -> ");
- cl = cl.getParent();
- }
- System.out.println();
- Class<?> classes[] = getClassLoaderClasses(saved);
- Arrays.sort(classes, new ClassNameComparator());
- System.out.println(Arrays.toString(classes));
-
- cl = saved.getParent();
- }
- }
-
- private static native boolean isModifiableClass(Class<?> c);
- private static native String[] getClassSignature(Class<?> c);
-
- private static native boolean isInterface(Class<?> c);
- private static native boolean isArrayClass(Class<?> c);
-
- private static native int getClassModifiers(Class<?> c);
-
- private static native Object[] getClassFields(Class<?> c);
- private static native Object[] getClassMethods(Class<?> c);
- private static native Class<?>[] getImplementedInterfaces(Class<?> c);
-
- private static native int getClassStatus(Class<?> c);
-
- private static native Object getClassLoader(Class<?> c);
-
- private static native Class<?>[] getClassLoaderClasses(ClassLoader cl);
-
- private static native int[] getClassVersion(Class<?> c);
-
- private static native void enableClassLoadPreparePrintEvents(boolean b);
-
- private static native void ensureJitCompiled(Class<?> c, String name);
-
- private static native boolean hasJit();
- private static native boolean isLoadedClass(String name);
- private static native void enableClassLoadSeenEvents(boolean b);
- private static native boolean hadLoadEvent();
-
- private static native void setEqualityEventStorageClass(Class<?> c);
- private static native void enableClassLoadPrepareEqualityEvents(boolean b);
-
- private static class TestForNonInit {
- public static double dummy = Math.random(); // So it can't be compile-time initialized.
- }
-
- private static class TestForInitFail {
- public static int dummy = ((int)Math.random())/0; // So it throws when initializing.
- }
-
- public static interface InfA {
- }
- public static interface InfB extends InfA {
- }
- public static interface InfC extends InfB {
- }
-
- public abstract static class ClassA implements InfA {
- }
- public abstract static class ClassB extends ClassA implements InfB {
- }
- public abstract static class ClassC implements InfA, InfC {
- }
-
- public static class ClassD {
- static int x = 1;
- }
-
- public static class ClassE {
- public void foo() {
- }
- public void bar() {
- }
- }
-
- public static class ClassF {
- public static Object STATIC = null;
- public static Reference<Object> WEAK = null;
- }
-
- private static final String DEX1 = System.getenv("DEX_LOCATION") + "/912-classes.jar";
- private static final String DEX2 = System.getenv("DEX_LOCATION") + "/912-classes-ex.jar";
-
- private static ClassLoader create(ClassLoader parent, String... elements) throws Exception {
- // Note: We use a PathClassLoader, as we do not care about code performance. We only load
- // the classes, and they're empty.
- Class<?> pathClassLoaderClass = Class.forName("dalvik.system.PathClassLoader");
- Constructor<?> pathClassLoaderInit = pathClassLoaderClass.getConstructor(String.class,
- ClassLoader.class);
- String path = String.join(":", elements);
- return (ClassLoader) pathClassLoaderInit.newInstance(path, parent);
- }
-
- private static class ClassNameComparator implements Comparator<Class<?>> {
- public int compare(Class<?> c1, Class<?> c2) {
- return c1.getName().compareTo(c2.getName());
- }
+ art.Test912.run();
+ art.Test912Art.run();
}
}
diff --git a/test/912-classes/src/art/DexData.java b/test/912-classes/src/art/DexData.java
new file mode 100644
index 0000000000..7d150322ca
--- /dev/null
+++ b/test/912-classes/src/art/DexData.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.nio.ByteBuffer;
+import java.util.Base64;
+
+import dalvik.system.InMemoryDexClassLoader;
+
+public class DexData {
+ public static ClassLoader getBootClassLoader() {
+ ClassLoader cl = DexData.class.getClassLoader();
+ while (cl.getParent() != null) {
+ cl = cl.getParent();
+ }
+ return cl;
+ }
+
+ public static ClassLoader create1() {
+ return create1(getBootClassLoader());
+ }
+ public static ClassLoader create1(ClassLoader parent) {
+ return create(parent, DEX_DATA_B);
+ }
+
+ public static ClassLoader create2() {
+ return create2(getBootClassLoader());
+ }
+ public static ClassLoader create2(ClassLoader parent) {
+ return create(parent, DEX_DATA_AC);
+ }
+
+ public static ClassLoader create12() {
+ return create12(getBootClassLoader());
+ }
+ public static ClassLoader create12(ClassLoader parent) {
+ return create(parent, DEX_DATA_AC, DEX_DATA_B);
+ }
+
+ private static ClassLoader create(ClassLoader parent, String... stringData) {
+ ByteBuffer byteBuffers[] = new ByteBuffer[stringData.length];
+ for (int i = 0; i < stringData.length; i++) {
+ byteBuffers[i] = ByteBuffer.wrap(Base64.getDecoder().decode(stringData[i]));
+ }
+ return new InMemoryDexClassLoader(byteBuffers, parent);
+ }
+
+ /*
+ * Derived from:
+ *
+ * public class A {
+ * }
+ *
+ * public class C extends A {
+ * }
+ *
+ */
+ private final static String DEX_DATA_AC =
+ "ZGV4CjAzNQD5KyH7WmGuqVEyL+2aKG1nyb27UJaCjFwQAgAAcAAAAHhWNBIAAAAAAAAAAIgBAAAH" +
+ "AAAAcAAAAAQAAACMAAAAAQAAAJwAAAAAAAAAAAAAAAMAAACoAAAAAgAAAMAAAAAQAQAAAAEAADAB" +
+ "AAA4AQAAQAEAAEgBAABNAQAAUgEAAGYBAAADAAAABAAAAAUAAAAGAAAABgAAAAMAAAAAAAAAAAAA" +
+ "AAAAAAABAAAAAAAAAAIAAAAAAAAAAAAAAAEAAAACAAAAAAAAAAEAAAAAAAAAcwEAAAAAAAABAAAA" +
+ "AQAAAAAAAAAAAAAAAgAAAAAAAAB9AQAAAAAAAAEAAQABAAAAaQEAAAQAAABwEAIAAAAOAAEAAQAB" +
+ "AAAAbgEAAAQAAABwEAAAAAAOAAY8aW5pdD4ABkEuamF2YQAGQy5qYXZhAANMQTsAA0xDOwASTGph" +
+ "dmEvbGFuZy9PYmplY3Q7AAFWABEABw4AEQAHDgAAAAEAAIGABIACAAABAAGBgASYAgALAAAAAAAA" +
+ "AAEAAAAAAAAAAQAAAAcAAABwAAAAAgAAAAQAAACMAAAAAwAAAAEAAACcAAAABQAAAAMAAACoAAAA" +
+ "BgAAAAIAAADAAAAAASAAAAIAAAAAAQAAAiAAAAcAAAAwAQAAAyAAAAIAAABpAQAAACAAAAIAAABz" +
+ "AQAAABAAAAEAAACIAQAA";
+
+ /*
+ * Derived from:
+ *
+ * public class B {
+ * }
+ *
+ */
+ private final static String DEX_DATA_B =
+ "ZGV4CjAzNQBgKV6iWFG4aOm5WEy8oGtDZjqsftBgwJ2oAQAAcAAAAHhWNBIAAAAAAAAAACABAAAF" +
+ "AAAAcAAAAAMAAACEAAAAAQAAAJAAAAAAAAAAAAAAAAIAAACcAAAAAQAAAKwAAADcAAAAzAAAAOQA" +
+ "AADsAAAA9AAAAPkAAAANAQAAAgAAAAMAAAAEAAAABAAAAAIAAAAAAAAAAAAAAAAAAAABAAAAAAAA" +
+ "AAAAAAABAAAAAQAAAAAAAAABAAAAAAAAABUBAAAAAAAAAQABAAEAAAAQAQAABAAAAHAQAQAAAA4A" +
+ "Bjxpbml0PgAGQi5qYXZhAANMQjsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgARAAcOAAAAAQAAgYAE" +
+ "zAEACwAAAAAAAAABAAAAAAAAAAEAAAAFAAAAcAAAAAIAAAADAAAAhAAAAAMAAAABAAAAkAAAAAUA" +
+ "AAACAAAAnAAAAAYAAAABAAAArAAAAAEgAAABAAAAzAAAAAIgAAAFAAAA5AAAAAMgAAABAAAAEAEA" +
+ "AAAgAAABAAAAFQEAAAAQAAABAAAAIAEAAA==";
+}
diff --git a/test/912-classes/src/art/Test912.java b/test/912-classes/src/art/Test912.java
new file mode 100644
index 0000000000..f3ff2b0668
--- /dev/null
+++ b/test/912-classes/src/art/Test912.java
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.lang.ref.Reference;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+
+public class Test912 {
+ public static void run() throws Exception {
+ art.Main.bindAgentJNIForClass(Test912.class);
+ doTest();
+ }
+
+ public static void doTest() throws Exception {
+ testClass("java.lang.Object");
+ testClass("java.lang.String");
+ testClass("java.lang.Math");
+ testClass("java.util.List");
+
+ testClass(getProxyClass());
+
+ testClass(int.class);
+ testClass(double[].class);
+
+ testClassType(int.class);
+ testClassType(getProxyClass());
+ testClassType(Runnable.class);
+ testClassType(String.class);
+ testClassType(ArrayList.class);
+
+ testClassType(int[].class);
+ testClassType(Runnable[].class);
+ testClassType(String[].class);
+
+ testClassFields(Integer.class);
+ testClassFields(int.class);
+ testClassFields(String[].class);
+
+ testClassMethods(Integer.class);
+ testClassMethods(int.class);
+ testClassMethods(String[].class);
+
+ testClassStatus(int.class);
+ testClassStatus(String[].class);
+ testClassStatus(Object.class);
+ testClassStatus(TestForNonInit.class);
+ try {
+ System.out.println(TestForInitFail.dummy);
+ } catch (ExceptionInInitializerError e) {
+ }
+ testClassStatus(TestForInitFail.class);
+
+ testInterfaces(int.class);
+ testInterfaces(String[].class);
+ testInterfaces(Object.class);
+ testInterfaces(InfA.class);
+ testInterfaces(InfB.class);
+ testInterfaces(InfC.class);
+ testInterfaces(ClassA.class);
+ testInterfaces(ClassB.class);
+ testInterfaces(ClassC.class);
+
+ testClassLoader(String.class);
+ testClassLoader(String[].class);
+ testClassLoader(InfA.class);
+ testClassLoader(getProxyClass());
+
+ testClassLoaderClasses();
+
+ System.out.println();
+
+ testClassVersion();
+
+ System.out.println();
+
+ // Use a dedicated thread to have a well-defined current thread.
+ Thread classEventsThread = new Thread("ClassEvents") {
+ @Override
+ public void run() {
+ try {
+ testClassEvents();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+ classEventsThread.start();
+ classEventsThread.join();
+ }
+
+ private static void testClass(String className) throws Exception {
+ Class<?> base = Class.forName(className);
+ testClass(base);
+ }
+
+ private static void testClass(Class<?> base) throws Exception {
+ String[] result = getClassSignature(base);
+ System.out.println(Arrays.toString(result));
+ int mod = getClassModifiers(base);
+ if (mod != base.getModifiers()) {
+ throw new RuntimeException("Unexpected modifiers: " + base.getModifiers() + " vs " + mod);
+ }
+ System.out.println(Integer.toHexString(mod));
+ }
+
+ private static void testClassType(Class<?> c) throws Exception {
+ boolean isInterface = isInterface(c);
+ boolean isArray = isArrayClass(c);
+ boolean isModifiable = isModifiableClass(c);
+ System.out.println(c.getName() + " interface=" + isInterface + " array=" + isArray +
+ " modifiable=" + isModifiable);
+ }
+
+ private static void testClassFields(Class<?> c) throws Exception {
+ System.out.println(Arrays.toString(getClassFields(c)));
+ }
+
+ private static void testClassMethods(Class<?> c) throws Exception {
+ System.out.println(Arrays.toString(getClassMethods(c)));
+ }
+
+ private static void testClassStatus(Class<?> c) {
+ System.out.println(c + " " + Integer.toBinaryString(getClassStatus(c)));
+ }
+
+ private static void testInterfaces(Class<?> c) {
+ System.out.println(c + " " + Arrays.toString(getImplementedInterfaces(c)));
+ }
+
+ private static boolean IsBootClassLoader(ClassLoader l) {
+ // Hacky check for Android's fake boot classloader.
+ return l.getClass().getName().equals("java.lang.BootClassLoader");
+ }
+
+ private static void testClassLoader(Class<?> c) {
+ Object cl = getClassLoader(c);
+ System.out.println(c + " " + (cl != null ? cl.getClass().getName() : "null"));
+ if (cl == null) {
+ if (c.getClassLoader() != null && !IsBootClassLoader(c.getClassLoader())) {
+ throw new RuntimeException("Expected " + c.getClassLoader() + ", but got null.");
+ }
+ } else {
+ if (!(cl instanceof ClassLoader)) {
+ throw new RuntimeException("Unexpected \"classloader\": " + cl + " (" + cl.getClass() +
+ ")");
+ }
+ if (cl != c.getClassLoader()) {
+ throw new RuntimeException("Unexpected classloader: " + c.getClassLoader() + " vs " + cl);
+ }
+ }
+ }
+
+ private static void testClassLoaderClasses() throws Exception {
+ System.out.println();
+ System.out.println("boot <- (B) <- (A,C)");
+ ClassLoader cl1 = DexData.create2(DexData.create1());
+ Class.forName("B", false, cl1);
+ Class.forName("A", false, cl1);
+ printClassLoaderClasses(cl1);
+
+ System.out.println();
+ System.out.println("boot <- (B) <- (A, List)");
+ ClassLoader cl2 = DexData.create2(DexData.create1());
+ Class.forName("A", false, cl2);
+ Class.forName("java.util.List", false, cl2);
+ Class.forName("B", false, cl2.getParent());
+ printClassLoaderClasses(cl2);
+
+ System.out.println();
+ System.out.println("boot <- 1+2 (A,B)");
+ ClassLoader cl3 = DexData.create12();
+ Class.forName("B", false, cl3);
+ Class.forName("A", false, cl3);
+ printClassLoaderClasses(cl3);
+
+ // Check that the boot classloader dumps something non-empty.
+ ClassLoader boot = ClassLoader.getSystemClassLoader().getParent();
+ while (boot.getParent() != null) {
+ boot = boot.getParent();
+ }
+
+ Class<?>[] bootClasses = getClassLoaderClasses(boot);
+ if (bootClasses.length == 0) {
+ throw new RuntimeException("No classes initiated by boot classloader.");
+ }
+ // Check that at least java.util.List is loaded.
+ boolean foundList = false;
+ for (Class<?> c : bootClasses) {
+ if (c == java.util.List.class) {
+ foundList = true;
+ break;
+ }
+ }
+ if (!foundList) {
+ System.out.println(Arrays.toString(bootClasses));
+ throw new RuntimeException("Could not find class java.util.List.");
+ }
+ }
+
+ private static void testClassVersion() {
+ System.out.println(Arrays.toString(getClassVersion(Main.class)));
+ }
+
+ private static void testClassEvents() throws Exception {
+ ClassLoader cl = Main.class.getClassLoader();
+ while (cl.getParent() != null) {
+ cl = cl.getParent();
+ }
+ final ClassLoader boot = cl;
+
+ // The JIT may deeply inline and load some classes. Preload these for test determinism.
+ final String PRELOAD_FOR_JIT[] = {
+ "java.nio.charset.CoderMalfunctionError",
+ "java.util.NoSuchElementException"
+ };
+ for (String s : PRELOAD_FOR_JIT) {
+ Class.forName(s);
+ }
+
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ ClassLoader cl6 = DexData.create12();
+ System.out.println("C, true");
+ Class.forName("C", true, cl6);
+ printClassLoadMessages();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
+ Thread dummyThread = new Thread();
+ dummyThread.start();
+ dummyThread.join();
+
+ enableClassLoadPreparePrintEvents(true, Thread.currentThread());
+
+ ClassLoader cl1 = DexData.create12();
+ System.out.println("B, false");
+ Class.forName("B", false, cl1);
+ printClassLoadMessages();
+
+ ClassLoader cl2 = DexData.create12();
+ System.out.println("B, true");
+ Class.forName("B", true, cl2);
+ printClassLoadMessages();
+
+ ClassLoader cl3 = DexData.create12();
+ System.out.println("C, false");
+ Class.forName("C", false, cl3);
+ printClassLoadMessages();
+ System.out.println("A, false");
+ Class.forName("A", false, cl3);
+ printClassLoadMessages();
+
+ ClassLoader cl4 = DexData.create12();
+ System.out.println("C, true");
+ Class.forName("C", true, cl4);
+ printClassLoadMessages();
+ System.out.println("A, true");
+ Class.forName("A", true, cl4);
+ printClassLoadMessages();
+
+ ClassLoader cl5 = DexData.create12();
+ System.out.println("A, true");
+ Class.forName("A", true, cl5);
+ printClassLoadMessages();
+ System.out.println("C, true");
+ Class.forName("C", true, cl5);
+ printClassLoadMessages();
+
+ enableClassLoadPreparePrintEvents(false, null);
+
+ Thread t = new Thread(r, "TestRunner");
+ enableClassLoadPreparePrintEvents(true, t);
+ t.start();
+ t.join();
+ enableClassLoadPreparePrintEvents(false, null);
+
+ enableClassLoadPreparePrintEvents(true, Thread.currentThread());
+
+ // Check creation of arrays and proxies.
+ Proxy.getProxyClass(Main.class.getClassLoader(), new Class[] { Comparable.class, I0.class });
+ Class.forName("[Lart.Test912;");
+ printClassLoadMessages();
+
+ enableClassLoadPreparePrintEvents(false, null);
+
+ testClassLoadPrepareEquality();
+ }
+
+ private static void testClassLoadPrepareEquality() throws Exception {
+ setEqualityEventStorageClass(ClassF.class);
+
+ enableClassLoadPrepareEqualityEvents(true);
+
+ Class.forName("art.Test912$ClassE");
+
+ enableClassLoadPrepareEqualityEvents(false);
+ }
+
+ private static void printClassLoaderClasses(ClassLoader cl) {
+ for (;;) {
+ if (cl == null || !cl.getClass().getName().startsWith("dalvik.system")) {
+ break;
+ }
+
+ Class<?> classes[] = getClassLoaderClasses(cl);
+ Arrays.sort(classes, new ClassNameComparator());
+ System.out.println(Arrays.toString(classes));
+
+ cl = cl.getParent();
+ }
+ }
+
+ private static void printClassLoadMessages() {
+ for (String s : getClassLoadMessages()) {
+ System.out.println(s);
+ }
+ }
+
+ private static native boolean isModifiableClass(Class<?> c);
+ private static native String[] getClassSignature(Class<?> c);
+
+ private static native boolean isInterface(Class<?> c);
+ private static native boolean isArrayClass(Class<?> c);
+
+ private static native int getClassModifiers(Class<?> c);
+
+ private static native Object[] getClassFields(Class<?> c);
+ private static native Object[] getClassMethods(Class<?> c);
+ private static native Class<?>[] getImplementedInterfaces(Class<?> c);
+
+ private static native int getClassStatus(Class<?> c);
+
+ private static native Object getClassLoader(Class<?> c);
+
+ private static native Class<?>[] getClassLoaderClasses(ClassLoader cl);
+
+ private static native int[] getClassVersion(Class<?> c);
+
+ private static native void enableClassLoadPreparePrintEvents(boolean b, Thread filter);
+ private static native String[] getClassLoadMessages();
+
+ private static native void setEqualityEventStorageClass(Class<?> c);
+ private static native void enableClassLoadPrepareEqualityEvents(boolean b);
+
+ private static class TestForNonInit {
+ public static double dummy = Math.random(); // So it can't be compile-time initialized.
+ }
+
+ private static class TestForInitFail {
+ public static int dummy = ((int)Math.random())/0; // So it throws when initializing.
+ }
+
+ public static interface InfA {
+ }
+ public static interface InfB extends InfA {
+ }
+ public static interface InfC extends InfB {
+ }
+
+ public abstract static class ClassA implements InfA {
+ }
+ public abstract static class ClassB extends ClassA implements InfB {
+ }
+ public abstract static class ClassC implements InfA, InfC {
+ }
+
+ public static class ClassE {
+ public void foo() {
+ }
+ public void bar() {
+ }
+ }
+
+ public static class ClassF {
+ public static Object STATIC = null;
+ public static Reference<Object> WEAK = null;
+ }
+
+ private static class ClassNameComparator implements Comparator<Class<?>> {
+ public int compare(Class<?> c1, Class<?> c2) {
+ return c1.getName().compareTo(c2.getName());
+ }
+ }
+
+ // See run-test 910 for an explanation.
+
+ 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.Test912$I" + bitIndex);
+ }
+ i >>>= 1;
+ bitIndex++;
+ }
+ return Proxy.getProxyClass(Test912.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 {
+ }
+}
diff --git a/test/912-classes/src/art/Test912Art.java b/test/912-classes/src/art/Test912Art.java
new file mode 100644
index 0000000000..e4384734d8
--- /dev/null
+++ b/test/912-classes/src/art/Test912Art.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.lang.ref.Reference;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+
+public class Test912Art {
+ public static void run() throws Exception {
+ art.Main.bindAgentJNIForClass(Test912Art.class);
+ doTest();
+ }
+
+ public static void doTest() throws Exception {
+ testClassEvents();
+ }
+
+ private static void testClassEvents() throws Exception {
+ // Note: the JIT part of this test is about the JIT pulling in a class not yet touched by
+ // anything else in the system. This could be the verifier or the interpreter. We
+ // block the interpreter by calling ensureJitCompiled. The verifier, however, must
+ // run in configurations where dex2oat didn't verify the class itself. So explicitly
+ // check whether the class has been already loaded, and skip then.
+ // TODO: Add multiple configurations to the run script once that becomes easier to do.
+ if (hasJit() && !isLoadedClass("art.Test912Art$ClassD")) {
+ testClassEventsJit();
+ }
+ }
+
+ private static void testClassEventsJit() throws Exception {
+ enableClassLoadSeenEvents(true);
+
+ testClassEventsJitImpl();
+
+ enableClassLoadSeenEvents(false);
+
+ if (!hadLoadEvent()) {
+ throw new RuntimeException("Did not get expected load event.");
+ }
+ }
+
+ private static void testClassEventsJitImpl() throws Exception {
+ ensureJitCompiled(Test912Art.class, "testClassEventsJitImpl");
+
+ if (ClassD.x != 1) {
+ throw new RuntimeException("Unexpected value");
+ }
+ }
+
+ private static native void ensureJitCompiled(Class<?> c, String name);
+
+ private static native boolean hasJit();
+ private static native boolean isLoadedClass(String name);
+ private static native void enableClassLoadSeenEvents(boolean b);
+ private static native boolean hadLoadEvent();
+
+ public static class ClassD {
+ static int x = 1;
+ }
+}
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index 19e12ae731..e319f7d98c 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -137,9 +137,9 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test913_followReferences(
if (reference_kind == JVMTI_HEAP_REFERENCE_JNI_GLOBAL && class_tag == 0) {
return 0;
}
- // Ignore classes (1000-1002@0) for thread objects. These can be held by the JIT.
+ // Ignore classes (1000 <= tag < 3000) for thread objects. These can be held by the JIT.
if (reference_kind == JVMTI_HEAP_REFERENCE_THREAD && class_tag == 0 &&
- (1000 <= *tag_ptr && *tag_ptr <= 1002)) {
+ (1000 <= *tag_ptr && *tag_ptr < 3000)) {
return 0;
}
// Ignore stack-locals of untagged threads. That is the environment.
diff --git a/test/924-threads/expected.txt b/test/924-threads/expected.txt
index 4c0f4eaa5b..1eb2e1bd52 100644
--- a/test/924-threads/expected.txt
+++ b/test/924-threads/expected.txt
@@ -1,10 +1,10 @@
currentThread OK
-main
+TestThread
5
false
java.lang.ThreadGroup[name=main,maxpri=10]
class dalvik.system.PathClassLoader
-main
+TestThread
5
false
java.lang.ThreadGroup[name=main,maxpri=10]
@@ -33,10 +33,11 @@ class dalvik.system.PathClassLoader
e1 = ALIVE|WAITING_WITH_TIMEOUT|SLEEPING|WAITING
5 = ALIVE|RUNNABLE
2 = TERMINATED
-[Thread[FinalizerDaemon,5,system], Thread[FinalizerWatchdogDaemon,5,system], Thread[HeapTaskDaemon,5,system], Thread[ReferenceQueueDaemon,5,system], Thread[Signal Catcher,5,system], Thread[main,5,main]]
+[Thread[FinalizerDaemon,5,system], Thread[FinalizerWatchdogDaemon,5,system], Thread[HeapTaskDaemon,5,system], Thread[ReferenceQueueDaemon,5,system], Thread[TestThread,5,main], Thread[main,5,main]]
JVMTI_ERROR_THREAD_NOT_ALIVE
JVMTI_ERROR_THREAD_NOT_ALIVE
Constructed thread
-Thread(EventTestThread): start
-Thread(EventTestThread): end
+[]
+[Thread(EventTestThread): start]
+[Thread(EventTestThread): end]
Thread joined
diff --git a/test/924-threads/src/art/Test924.java b/test/924-threads/src/art/Test924.java
index 160bf8ea67..5445939cbc 100644
--- a/test/924-threads/src/art/Test924.java
+++ b/test/924-threads/src/art/Test924.java
@@ -25,17 +25,35 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
public class Test924 {
public static void run() throws Exception {
Main.bindAgentJNIForClass(Test924.class);
- doTest();
+
+ // Run the test on its own thread, so we have a known state for the "current" thread.
+ Thread t = new Thread("TestThread") {
+ @Override
+ public void run() {
+ try {
+ doTest();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+ t.start();
+ t.join();
}
private static void doTest() throws Exception {
Thread t1 = Thread.currentThread();
Thread t2 = getCurrentThread();
+ // Need to adjust priority, as on-device this may be unexpected (and we prefer not
+ // to special-case this.)
+ t1.setPriority(5);
+
if (t1 != t2) {
throw new RuntimeException("Expected " + t1 + " but got " + t2);
}
@@ -188,7 +206,32 @@ public class Test924 {
}
Collections.sort(threadList, THREAD_COMP);
- System.out.println(threadList);
+
+ List<Thread> expectedList = new ArrayList<>();
+ Set<Thread> threadsFromTraces = Thread.getAllStackTraces().keySet();
+
+ expectedList.add(findThreadByName(threadsFromTraces, "FinalizerDaemon"));
+ expectedList.add(findThreadByName(threadsFromTraces, "FinalizerWatchdogDaemon"));
+ expectedList.add(findThreadByName(threadsFromTraces, "HeapTaskDaemon"));
+ expectedList.add(findThreadByName(threadsFromTraces, "ReferenceQueueDaemon"));
+ // We can't get the signal catcher through getAllStackTraces. So ignore it.
+ // expectedList.add(findThreadByName(threadsFromTraces, "Signal Catcher"));
+ expectedList.add(findThreadByName(threadsFromTraces, "TestThread"));
+ expectedList.add(findThreadByName(threadsFromTraces, "main"));
+
+ if (!threadList.containsAll(expectedList)) {
+ throw new RuntimeException("Expected " + expectedList + " as subset, got " + threadList);
+ }
+ System.out.println(expectedList);
+ }
+
+ private static Thread findThreadByName(Set<Thread> threads, String name) {
+ for (Thread t : threads) {
+ if (t.getName().equals(name)) {
+ return t;
+ }
+ }
+ throw new RuntimeException("Did not find thread " + name + ": " + threads);
}
private static void doTLSTests() throws Exception {
@@ -256,13 +299,35 @@ public class Test924 {
private static void doTestEvents() throws Exception {
enableThreadEvents(true);
- Thread t = new Thread("EventTestThread");
+ final CountDownLatch cdl1 = new CountDownLatch(1);
+ final CountDownLatch cdl2 = new CountDownLatch(1);
+
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ cdl1.countDown();
+ cdl2.await();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+ Thread t = new Thread(r, "EventTestThread");
System.out.println("Constructed thread");
Thread.yield();
+ Thread.sleep(100);
+ System.out.println(Arrays.toString(getThreadEventMessages()));
t.start();
+ cdl1.await();
+
+ System.out.println(Arrays.toString(getThreadEventMessages()));
+
+ cdl2.countDown();
t.join();
+ System.out.println(Arrays.toString(getThreadEventMessages()));
System.out.println("Thread joined");
@@ -337,4 +402,5 @@ public class Test924 {
private static native void setTLS(Thread t, long l);
private static native long getTLS(Thread t);
private static native void enableThreadEvents(boolean b);
+ private static native String[] getThreadEventMessages();
}
diff --git a/test/924-threads/threads.cc b/test/924-threads/threads.cc
index 701ab1def3..e21dcc240e 100644
--- a/test/924-threads/threads.cc
+++ b/test/924-threads/threads.cc
@@ -16,6 +16,10 @@
#include <stdio.h>
+#include <mutex>
+#include <string>
+#include <vector>
+
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
#include "jni.h"
@@ -139,17 +143,27 @@ extern "C" JNIEXPORT void JNICALL Java_art_Test924_setTLS(
JvmtiErrorToException(env, jvmti_env, result);
}
+static std::mutex gEventsMutex;
+static std::vector<std::string> gEvents;
+
static void JNICALL ThreadEvent(jvmtiEnv* jvmti_env,
JNIEnv* jni_env,
jthread thread,
bool is_start) {
jvmtiThreadInfo info;
- jvmtiError result = jvmti_env->GetThreadInfo(thread, &info);
- if (result != JVMTI_ERROR_NONE) {
- printf("Error getting thread info");
- return;
+ {
+ std::lock_guard<std::mutex> guard(gEventsMutex);
+
+ jvmtiError result = jvmti_env->GetThreadInfo(thread, &info);
+ if (result != JVMTI_ERROR_NONE) {
+ gEvents.push_back("Error getting thread info");
+ return;
+ }
+
+ gEvents.push_back(android::base::StringPrintf("Thread(%s): %s",
+ info.name,
+ is_start ? "start" : "end"));
}
- printf("Thread(%s): %s\n", info.name, is_start ? "start" : "end");
jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(info.name));
jni_env->DeleteLocalRef(info.thread_group);
@@ -205,5 +219,18 @@ extern "C" JNIEXPORT void JNICALL Java_art_Test924_enableThreadEvents(
JvmtiErrorToException(env, jvmti_env, ret);
}
+extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test924_getThreadEventMessages(
+ 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 Test924Threads
} // namespace art
diff --git a/test/986-native-method-bind/expected.txt b/test/986-native-method-bind/expected.txt
new file mode 100644
index 0000000000..189217d761
--- /dev/null
+++ b/test/986-native-method-bind/expected.txt
@@ -0,0 +1,8 @@
+private static native void art.Test986$Transform.sayHi() = Java_art_Test986_00024Transform_sayHi -> Java_art_Test986_00024Transform_sayHi
+Hello
+private static native void art.Test986$Transform.sayHi() = Java_art_Test986_00024Transform_sayHi -> NoReallySayGoodbye
+Bye
+public static native void art.Main.bindAgentJNI(java.lang.String,java.lang.ClassLoader) = Java_art_Main_bindAgentJNI -> Java_art_Main_bindAgentJNI
+public static native void art.Main.bindAgentJNIForClass(java.lang.Class) = Java_art_Main_bindAgentJNIForClass -> Java_art_Main_bindAgentJNIForClass
+private static native void art.Test986.setNativeBindNotify(boolean) = Java_art_Test986_setNativeBindNotify -> Java_art_Test986_setNativeBindNotify
+private static native void art.Test986.setupNativeBindNotify() = Java_art_Test986_setupNativeBindNotify -> Java_art_Test986_setupNativeBindNotify
diff --git a/test/986-native-method-bind/info.txt b/test/986-native-method-bind/info.txt
new file mode 100644
index 0000000000..1939936ca9
--- /dev/null
+++ b/test/986-native-method-bind/info.txt
@@ -0,0 +1 @@
+Tests native-method-bind callback and native method replacement.
diff --git a/test/986-native-method-bind/native_bind.cc b/test/986-native-method-bind/native_bind.cc
new file mode 100644
index 0000000000..4f93f87dfe
--- /dev/null
+++ b/test/986-native-method-bind/native_bind.cc
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <inttypes.h>
+#include <memory>
+#include <stdio.h>
+#include <dlfcn.h>
+
+#include "android-base/stringprintf.h"
+#include "jni.h"
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jni_binder.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+#include "scoped_local_ref.h"
+
+namespace art {
+namespace Test986NativeBind {
+
+static void doUpPrintCall(JNIEnv* env, const char* function) {
+ ScopedLocalRef<jclass> klass(env, env->FindClass("art/Test986"));
+ jmethodID targetMethod = env->GetStaticMethodID(klass.get(), function, "()V");
+ env->CallStaticVoidMethod(klass.get(), targetMethod);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test986_00024Transform_sayHi(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ doUpPrintCall(env, "doSayHi");
+}
+
+extern "C" JNIEXPORT void JNICALL NoReallySayGoodbye(JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ doUpPrintCall(env, "doSayBye");
+}
+
+static void doJvmtiMethodBind(jvmtiEnv* jvmtienv ATTRIBUTE_UNUSED,
+ JNIEnv* env,
+ jthread thread ATTRIBUTE_UNUSED,
+ jmethodID m,
+ void* address,
+ /*out*/void** out_address) {
+ ScopedLocalRef<jclass> method_class(env, env->FindClass("java/lang/reflect/Method"));
+ ScopedLocalRef<jobject> method_obj(env, env->ToReflectedMethod(method_class.get(), m, false));
+ Dl_info addr_info;
+ if (dladdr(address, &addr_info) == 0 || addr_info.dli_sname == nullptr) {
+ ScopedLocalRef<jclass> exception_class(env, env->FindClass("java/lang/Exception"));
+ env->ThrowNew(exception_class.get(), "dladdr failure!");
+ return;
+ }
+ ScopedLocalRef<jstring> sym_name(env, env->NewStringUTF(addr_info.dli_sname));
+ ScopedLocalRef<jclass> klass(env, env->FindClass("art/Test986"));
+ jmethodID upcallMethod = env->GetStaticMethodID(
+ klass.get(),
+ "doNativeMethodBind",
+ "(Ljava/lang/reflect/Method;Ljava/lang/String;)Ljava/lang/String;");
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ ScopedLocalRef<jstring> new_symbol(env,
+ reinterpret_cast<jstring>(
+ env->CallStaticObjectMethod(klass.get(),
+ upcallMethod,
+ method_obj.get(),
+ sym_name.get())));
+ const char* new_symbol_chars = env->GetStringUTFChars(new_symbol.get(), nullptr);
+ if (strcmp(new_symbol_chars, addr_info.dli_sname) != 0) {
+ *out_address = dlsym(RTLD_DEFAULT, new_symbol_chars);
+ if (*out_address == nullptr) {
+ ScopedLocalRef<jclass> exception_class(env, env->FindClass("java/lang/Exception"));
+ env->ThrowNew(exception_class.get(), "dlsym failure!");
+ return;
+ }
+ }
+ env->ReleaseStringUTFChars(new_symbol.get(), new_symbol_chars);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test986_setupNativeBindNotify(
+ JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) {
+ jvmtiEventCallbacks cb;
+ memset(&cb, 0, sizeof(cb));
+ cb.NativeMethodBind = doJvmtiMethodBind;
+ jvmti_env->SetEventCallbacks(&cb, sizeof(cb));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test986_setNativeBindNotify(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
+ jvmtiError res = jvmti_env->SetEventNotificationMode(enable ? JVMTI_ENABLE : JVMTI_DISABLE,
+ JVMTI_EVENT_NATIVE_METHOD_BIND,
+ nullptr);
+ if (res != JVMTI_ERROR_NONE) {
+ JvmtiErrorToException(env, jvmti_env, res);
+ }
+}
+
+} // namespace Test986NativeBind
+} // namespace art
diff --git a/test/986-native-method-bind/run b/test/986-native-method-bind/run
new file mode 100755
index 0000000000..e92b873956
--- /dev/null
+++ b/test/986-native-method-bind/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+./default-run "$@" --jvmti
diff --git a/test/912-classes/src-ex/A.java b/test/986-native-method-bind/src/Main.java
index 2c43cfbd79..fac9d8e2a9 100644
--- a/test/912-classes/src-ex/A.java
+++ b/test/986-native-method-bind/src/Main.java
@@ -14,5 +14,8 @@
* limitations under the License.
*/
-public class A {
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test986.run();
+ }
}
diff --git a/test/986-native-method-bind/src/art/Main.java b/test/986-native-method-bind/src/art/Main.java
new file mode 100644
index 0000000000..8b01920638
--- /dev/null
+++ b/test/986-native-method-bind/src/art/Main.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+// Binder class so the agent's C code has something that can be bound and exposed to tests.
+// In a package to separate cleanly and work around CTS reference issues (though this class
+// should be replaced in the CTS version).
+public class Main {
+ // Load the given class with the given classloader, and bind all native methods to corresponding
+ // C methods in the agent. Will abort if any of the steps fail.
+ public static native void bindAgentJNI(String className, ClassLoader classLoader);
+ // Same as above, giving the class directly.
+ public static native void bindAgentJNIForClass(Class<?> klass);
+}
diff --git a/test/986-native-method-bind/src/art/Redefinition.java b/test/986-native-method-bind/src/art/Redefinition.java
new file mode 100644
index 0000000000..0350ab42ad
--- /dev/null
+++ b/test/986-native-method-bind/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+ // Bind native functions.
+ static {
+ Main.bindAgentJNIForClass(Redefinition.class);
+ }
+
+ public static final class CommonClassDefinition {
+ public final Class<?> target;
+ public final byte[] class_file_bytes;
+ public final byte[] dex_file_bytes;
+
+ public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+ this.target = target;
+ this.class_file_bytes = class_file_bytes;
+ this.dex_file_bytes = dex_file_bytes;
+ }
+ }
+
+ // A set of possible test configurations. Test should set this if they need to.
+ // This must be kept in sync with the defines in ti-agent/common_helper.cc
+ public static enum Config {
+ COMMON_REDEFINE(0),
+ COMMON_RETRANSFORM(1),
+ COMMON_TRANSFORM(2);
+
+ private final int val;
+ private Config(int val) {
+ this.val = val;
+ }
+ }
+
+ public static void setTestConfiguration(Config type) {
+ nativeSetTestConfiguration(type.val);
+ }
+
+ private static native void nativeSetTestConfiguration(int type);
+
+ // Transforms the class
+ public static native void doCommonClassRedefinition(Class<?> target,
+ byte[] classfile,
+ byte[] dexfile);
+
+ public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+ ArrayList<Class<?>> classes = new ArrayList<>();
+ ArrayList<byte[]> class_files = new ArrayList<>();
+ ArrayList<byte[]> dex_files = new ArrayList<>();
+
+ for (CommonClassDefinition d : defs) {
+ classes.add(d.target);
+ class_files.add(d.class_file_bytes);
+ dex_files.add(d.dex_file_bytes);
+ }
+ doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+ class_files.toArray(new byte[0][]),
+ dex_files.toArray(new byte[0][]));
+ }
+
+ public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+ for (CommonClassDefinition d : defs) {
+ addCommonTransformationResult(d.target.getCanonicalName(),
+ d.class_file_bytes,
+ d.dex_file_bytes);
+ }
+ }
+
+ public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+ byte[][] classfiles,
+ byte[][] dexfiles);
+ public static native void doCommonClassRetransformation(Class<?>... target);
+ public static native void setPopRetransformations(boolean pop);
+ public static native void popTransformationFor(String name);
+ public static native void enableCommonRetransformation(boolean enable);
+ public static native void addCommonTransformationResult(String target_name,
+ byte[] class_bytes,
+ byte[] dex_bytes);
+}
diff --git a/test/986-native-method-bind/src/art/Test986.java b/test/986-native-method-bind/src/art/Test986.java
new file mode 100644
index 0000000000..edd2e9d79f
--- /dev/null
+++ b/test/986-native-method-bind/src/art/Test986.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+public class Test986 {
+ static {
+ // NB This is called before any setup is done so we don't need to worry about getting bind
+ // events.
+ Main.bindAgentJNIForClass(Test986.class);
+ }
+
+
+ private static final HashMap<Method, String> SymbolMap = new HashMap<>();
+
+ // A class with a native method we can play with.
+ static class Transform {
+ private static native void sayHi();
+ }
+
+ public static void run() throws Exception {
+ setupNativeBindNotify();
+ setNativeBindNotify(true);
+ doTest();
+ }
+
+ private static void setNativeTransform(Method method, String dest) {
+ SymbolMap.put(method, dest);
+ }
+
+ /**
+ * Notifies java that a native method bind has occurred and requests the new symbol to bind to.
+ */
+ public static String doNativeMethodBind(Method method, String nativeSym) {
+ // Disable native bind notify for now to avoid infinite loops.
+ setNativeBindNotify(false);
+ String transSym = SymbolMap.getOrDefault(method, nativeSym);
+ System.out.println(method + " = " + nativeSym + " -> " + transSym);
+ setNativeBindNotify(true);
+ return transSym;
+ }
+
+ public static void doTest() throws Exception {
+ Method say_hi_method = Transform.class.getDeclaredMethod("sayHi");
+ // TODO We should test auto-binding but due to the way this is run that will be annoying.
+ Main.bindAgentJNIForClass(Transform.class);
+ Transform.sayHi();
+ setNativeTransform(say_hi_method, "NoReallySayGoodbye");
+ Main.bindAgentJNIForClass(Transform.class);
+ Transform.sayHi();
+ Main.bindAgentJNIForClass(Main.class);
+ Main.bindAgentJNIForClass(Test986.class);
+ }
+
+ // Functions called from native code.
+ public static void doSayHi() {
+ System.out.println("Hello");
+ }
+
+ public static void doSayBye() {
+ System.out.println("Bye");
+ }
+
+ private static native void setNativeBindNotify(boolean enable);
+ private static native void setupNativeBindNotify();
+}
diff --git a/test/Android.bp b/test/Android.bp
index 1522f07556..93fd607c24 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -261,6 +261,7 @@ art_cc_defaults {
"908-gc-start-finish/gc_callbacks.cc",
"910-methods/methods.cc",
"911-get-stack-trace/stack_trace.cc",
+ "912-classes/classes.cc",
"913-heaps/heaps.cc",
"918-fields/fields.cc",
"920-objects/objects.cc",
@@ -275,6 +276,7 @@ art_cc_defaults {
"933-misc-events/misc_events.cc",
"945-obsolete-native/obsolete_native.cc",
"984-obsolete-invoke/obsolete_invoke.cc",
+ "986-native-method-bind/native_bind.cc",
],
shared_libs: [
"libbase",
@@ -295,7 +297,7 @@ art_cc_defaults {
// make this list smaller.
"901-hello-ti-agent/basics.cc",
"909-attach-agent/attach.cc",
- "912-classes/classes.cc",
+ "912-classes/classes_art.cc",
"936-search-onload/search_onload.cc",
"983-source-transform-verify/source_transform.cc",
],
diff --git a/test/Android.run-test-jvmti-java-library.mk b/test/Android.run-test-jvmti-java-library.mk
index dcb238cd9f..70ee693a60 100644
--- a/test/Android.run-test-jvmti-java-library.mk
+++ b/test/Android.run-test-jvmti-java-library.mk
@@ -45,6 +45,8 @@ LOCAL_SRC_FILES += \
911-get-stack-trace/src/art/Recurse.java \
911-get-stack-trace/src/art/SameThread.java \
911-get-stack-trace/src/art/ThreadListTraces.java \
+ 912-classes/src/art/Test912.java \
+ 912-classes/src/art/DexData.java \
913-heaps/src/art/Test913.java \
914-hello-obsolescence/src/art/Test914.java \
915-obsolete-2/src/art/Test915.java \
@@ -84,6 +86,7 @@ JVMTI_RUN_TEST_GENERATED_NUMBERS := \
908 \
910 \
911 \
+ 912 \
913 \
914 \
915 \
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 4415b2ccaa..afd914432e 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -17,22 +17,6 @@ LOCAL_PATH := $(call my-dir)
include art/build/Android.common_test.mk
-# List of all tests of the form 003-omnibus-opcodes.
-TEST_ART_RUN_TESTS := $(wildcard $(LOCAL_PATH)/[0-9]*)
-TEST_ART_RUN_TESTS := $(subst $(LOCAL_PATH)/,, $(TEST_ART_RUN_TESTS))
-
-########################################################################
-# The art-run-tests module, used to build all run-tests into an image.
-
-# The path where build only targets will be output, e.g.
-# out/target/product/generic_x86_64/obj/PACKAGING/art-run-tests_intermediates/DATA
-art_run_tests_build_dir := $(call intermediates-dir-for,JAVA_LIBRARIES,art-run-tests)/DATA
-art_run_tests_install_dir := $(call intermediates-dir-for,PACKAGING,art-run-tests)/DATA
-
-# A generated list of prerequisites that call 'run-test --build-only', the actual prerequisite is
-# an empty file touched in the intermediate directory.
-TEST_ART_RUN_TEST_BUILD_RULES :=
-
# Dependencies for actually running a run-test.
TEST_ART_RUN_TEST_DEPENDENCIES := \
$(DX) \
@@ -41,54 +25,6 @@ TEST_ART_RUN_TEST_DEPENDENCIES := \
$(HOST_OUT_EXECUTABLES)/dexmerger \
$(JACK)
-TEST_ART_RUN_TEST_ORDERONLY_DEPENDENCIES := setup-jack-server
-
-# Helper to create individual build targets for tests. Must be called with $(eval).
-# $(1): the test number
-define define-build-art-run-test
- dmart_target := $(art_run_tests_build_dir)/art-run-tests/$(1)/touch
- dmart_install_target := $(art_run_tests_install_dir)/art-run-tests/$(1)/touch
- run_test_options = --build-only
- ifeq ($(ART_TEST_QUIET),true)
- run_test_options += --quiet
- endif
-$$(dmart_target): PRIVATE_RUN_TEST_OPTIONS := $$(run_test_options)
-$$(dmart_target): $(TEST_ART_RUN_TEST_DEPENDENCIES) | $(TEST_ART_RUN_TEST_ORDERONLY_DEPENDENCIES)
-ifeq ($(ANDROID_COMPILE_WITH_JACK),true)
-$$(dmart_target): $(TARGET_JACK_CLASSPATH_DEPENDENCIES)
-endif
-$$(dmart_target):
- $(hide) rm -rf $$(dir $$@) && mkdir -p $$(dir $$@)
- $(hide) DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) \
- SMALI=$(abspath $(HOST_OUT_EXECUTABLES)/smali) \
- DXMERGER=$(abspath $(HOST_OUT_EXECUTABLES)/dexmerger) \
- JACK_VERSION=$(JACK_DEFAULT_VERSION) \
- JACK=$(abspath $(JACK)) \
- JACK_VERSION=$(JACK_DEFAULT_VERSION) \
- JACK_CLASSPATH=$(TARGET_JACK_CLASSPATH) \
- $(LOCAL_PATH)/run-test $$(PRIVATE_RUN_TEST_OPTIONS) --output-path $$(abspath $$(dir $$@)) $(1)
- $(hide) touch $$@
-
-$$(dmart_install_target): $$(dmart_target)
- $(hide) rm -rf $$(dir $$@) && mkdir -p $$(dir $$@)
- $(hide) cp $$(dir $$<)/* $$(dir $$@)/
-
- TEST_ART_RUN_TEST_BUILD_RULES += $$(dmart_install_target)
- dmart_target :=
- dmart_install_target :=
- run_test_options :=
-endef
-$(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call define-build-art-run-test,$(test))))
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE := art-run-tests
-LOCAL_ADDITIONAL_DEPENDENCIES := $(TEST_ART_RUN_TEST_BUILD_RULES)
-# The build system use this flag to pick up files generated by declare-make-art-run-test.
-LOCAL_PICKUP_FILES := $(art_run_tests_install_dir)
-
-include $(BUILD_PHONY_PACKAGE)
-
# Convert's a rule name to the form used in variables, e.g. no-relocate to NO_RELOCATE
define name-to-var
$(shell echo $(1) | tr '[:lower:]' '[:upper:]' | tr '-' '_')
diff --git a/test/VerifierDeps/MyClassExtendingInterface.smali b/test/VerifierDeps/MyClassExtendingInterface.smali
new file mode 100644
index 0000000000..43cf13bb30
--- /dev/null
+++ b/test/VerifierDeps/MyClassExtendingInterface.smali
@@ -0,0 +1,16 @@
+# Copyright (C) 2017 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.
+
+.class public LMyClassExtendingInterface;
+.super LIface;
diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc
index df7fa20226..ceb4ba241b 100644
--- a/test/common/stack_inspect.cc
+++ b/test/common/stack_inspect.cc
@@ -144,22 +144,11 @@ extern "C" JNIEXPORT void JNICALL Java_Main_assertIsInterpreted(JNIEnv* env, jcl
}
}
-static jboolean IsManaged(JNIEnv* env, jclass cls, size_t level) {
+static jboolean IsManaged(JNIEnv* env, jclass, size_t level) {
ScopedObjectAccess soa(env);
-
- ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls);
- const DexFile& dex_file = klass->GetDexFile();
- const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
- if (oat_dex_file == nullptr) {
- // No oat file, this must be a test configuration that doesn't compile at all. Ignore that the
- // result will be that we're running the interpreter.
- return JNI_FALSE;
- }
-
NthCallerVisitor caller(soa.Self(), level, false);
caller.WalkStack();
CHECK(caller.caller != nullptr);
-
return caller.GetCurrentShadowFrame() != nullptr ? JNI_FALSE : JNI_TRUE;
}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index e7343a0fd5..0e42a29bf6 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -220,7 +220,6 @@
"tests": ["604-hot-static-interface",
"612-jit-dex-cache",
"613-inlining-dex-cache",
- "616-cha",
"626-set-resolved-string"],
"variant": "trace | stream",
"description": ["These tests expect JIT compilation, which is",
@@ -330,14 +329,8 @@
"variant": "interpreter | optimizing | regalloc_gc | jit"
},
{
- "tests": ["912-classes",
- "616-cha",
- "616-cha-abstract",
- "616-cha-interface",
- "616-cha-interface-default",
- "616-cha-miranda",
- "616-cha-proxy-method-inline"],
- "bug": "http://b/36344364 http://b/36344221",
+ "tests": ["912-classes"],
+ "bug": "http://b/36344364",
"variant": "no-dex2oat | relocate-npatchoat"
},
{