Merge "Add support for running ART gtests with Valgrind in local installations."
diff --git a/build/Android.bp b/build/Android.bp
index 2c959d4..6766dd6 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -4,6 +4,7 @@
deps: [
"blueprint",
"blueprint-pathtools",
+ "blueprint-proptools",
"soong",
"soong-android",
"soong-cc",
diff --git a/build/art.go b/build/art.go
index 452b348..4e48d2d 100644
--- a/build/art.go
+++ b/build/art.go
@@ -19,6 +19,8 @@
"android/soong/cc"
"fmt"
"sync"
+
+ "github.com/google/blueprint/proptools"
)
var supportedArches = []string{"arm", "arm64", "mips", "mips64", "x86", "x86_64"}
@@ -210,31 +212,33 @@
func customLinker(ctx android.LoadHookContext) {
linker := envDefault(ctx, "CUSTOM_TARGET_LINKER", "")
- if linker != "" {
- type props struct {
- DynamicLinker string
- }
-
- p := &props{}
- p.DynamicLinker = linker
- ctx.AppendProperties(p)
+ type props struct {
+ DynamicLinker string
}
+
+ p := &props{}
+ if linker != "" {
+ p.DynamicLinker = linker
+ }
+
+ ctx.AppendProperties(p)
}
func prefer32Bit(ctx android.LoadHookContext) {
- if envTrue(ctx, "HOST_PREFER_32_BIT") {
- type props struct {
- Target struct {
- Host struct {
- Compile_multilib string
- }
+ type props struct {
+ Target struct {
+ Host struct {
+ Compile_multilib *string
}
}
-
- p := &props{}
- p.Target.Host.Compile_multilib = "prefer32"
- ctx.AppendProperties(p)
}
+
+ p := &props{}
+ if envTrue(ctx, "HOST_PREFER_32_BIT") {
+ p.Target.Host.Compile_multilib = proptools.StringPtr("prefer32")
+ }
+
+ ctx.AppendProperties(p)
}
func testMap(config android.Config) map[string][]string {
diff --git a/cmdline/cmdline.h b/cmdline/cmdline.h
index c5d3a6b..2929f36 100644
--- a/cmdline/cmdline.h
+++ b/cmdline/cmdline.h
@@ -26,6 +26,7 @@
#include "android-base/stringprintf.h"
+#include "base/file_utils.h"
#include "base/logging.h"
#include "base/stringpiece.h"
#include "noop_compiler_callbacks.h"
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 547ffbc..135f9c7 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -66,6 +66,7 @@
#include "nativehelper/ScopedLocalRef.h"
#include "object_lock.h"
#include "runtime.h"
+#include "runtime_intrinsics.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
#include "thread_list.h"
@@ -365,28 +366,6 @@
}
#undef CREATE_TRAMPOLINE
-static void SetupIntrinsic(Thread* self,
- Intrinsics intrinsic,
- InvokeType invoke_type,
- const char* class_name,
- const char* method_name,
- const char* signature)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- PointerSize image_size = class_linker->GetImagePointerSize();
- ObjPtr<mirror::Class> cls = class_linker->FindSystemClass(self, class_name);
- if (cls == nullptr) {
- LOG(FATAL) << "Could not find class of intrinsic " << class_name;
- }
- ArtMethod* method = cls->FindClassMethod(method_name, signature, image_size);
- if (method == nullptr || method->GetDeclaringClass() != cls) {
- LOG(FATAL) << "Could not find method of intrinsic "
- << class_name << " " << method_name << " " << signature;
- }
- DCHECK_EQ(method->GetInvokeType(), invoke_type);
- method->SetIntrinsic(static_cast<uint32_t>(intrinsic));
-}
-
void CompilerDriver::CompileAll(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings) {
@@ -405,14 +384,7 @@
// We don't need to setup the intrinsics for non boot image compilation, as
// those compilations will pick up a boot image that have the ArtMethod already
// set with the intrinsics flag.
- ScopedObjectAccess soa(Thread::Current());
-#define SETUP_INTRINSICS(Name, InvokeType, NeedsEnvironmentOrCache, SideEffects, Exceptions, \
- ClassName, MethodName, Signature) \
- SetupIntrinsic(soa.Self(), Intrinsics::k##Name, InvokeType, ClassName, MethodName, Signature);
-#include "intrinsics_list.h"
- INTRINSICS_LIST(SETUP_INTRINSICS)
-#undef INTRINSICS_LIST
-#undef SETUP_INTRINSICS
+ InitializeIntrinsics();
}
// Compile:
// 1) Compile all classes and methods enabled for compilation. May fall back to dex-to-dex
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 9d0b5c8..b8d1f52 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -536,6 +536,7 @@
break;
case kVirtual:
case kInterface:
+ case kPolymorphic:
LOG(FATAL) << "Unexpected invoke type: " << invoke->GetInvokeType();
UNREACHABLE();
}
@@ -563,6 +564,9 @@
case kInterface:
entrypoint = kQuickInvokeInterfaceTrampolineWithAccessCheck;
break;
+ case kPolymorphic:
+ LOG(FATAL) << "Unexpected invoke type: " << invoke->GetInvokeType();
+ UNREACHABLE();
}
InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr);
}
diff --git a/compiler/optimizing/code_generator_vector_mips.cc b/compiler/optimizing/code_generator_vector_mips.cc
index c5a39ff..7a8c0ad 100644
--- a/compiler/optimizing/code_generator_vector_mips.cc
+++ b/compiler/optimizing/code_generator_vector_mips.cc
@@ -91,17 +91,61 @@
}
void LocationsBuilderMIPS::VisitVecExtractScalar(HVecExtractScalar* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
+ switch (instruction->GetPackedType()) {
+ case DataType::Type::kBool:
+ case DataType::Type::kUint8:
+ case DataType::Type::kInt8:
+ case DataType::Type::kUint16:
+ case DataType::Type::kInt16:
+ case DataType::Type::kInt32:
+ case DataType::Type::kInt64:
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresRegister());
+ break;
+ case DataType::Type::kFloat32:
+ case DataType::Type::kFloat64:
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void InstructionCodeGeneratorMIPS::VisitVecExtractScalar(HVecExtractScalar* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ VectorRegister src = VectorRegisterFrom(locations->InAt(0));
+ switch (instruction->GetPackedType()) {
+ case DataType::Type::kInt32:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Copy_sW(locations->Out().AsRegister<Register>(), src, 0);
+ break;
+ case DataType::Type::kInt64:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Mfc1(locations->Out().AsRegisterPairLow<Register>(),
+ locations->InAt(0).AsFpuRegister<FRegister>());
+ __ MoveFromFpuHigh(locations->Out().AsRegisterPairHigh<Register>(),
+ locations->InAt(0).AsFpuRegister<FRegister>());
+ break;
+ case DataType::Type::kFloat32:
+ case DataType::Type::kFloat64:
+ DCHECK_LE(2u, instruction->GetVectorLength());
+ DCHECK_LE(instruction->GetVectorLength(), 4u);
+ DCHECK(locations->InAt(0).Equals(locations->Out())); // no code required
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
// Helper to set up locations for vector unary operations.
static void CreateVecUnOpLocations(ArenaAllocator* allocator, HVecUnaryOperation* instruction) {
LocationSummary* locations = new (allocator) LocationSummary(instruction);
- switch (instruction->GetPackedType()) {
+ DataType::Type type = instruction->GetPackedType();
+ switch (type) {
case DataType::Type::kBool:
locations->SetInAt(0, Location::RequiresFpuRegister());
locations->SetOut(Location::RequiresFpuRegister(),
@@ -118,7 +162,8 @@
case DataType::Type::kFloat64:
locations->SetInAt(0, Location::RequiresFpuRegister());
locations->SetOut(Location::RequiresFpuRegister(),
- (instruction->IsVecNeg() || instruction->IsVecAbs())
+ (instruction->IsVecNeg() || instruction->IsVecAbs() ||
+ (instruction->IsVecReduce() && type == DataType::Type::kInt64))
? Location::kOutputOverlap
: Location::kNoOutputOverlap);
break;
@@ -133,7 +178,54 @@
}
void InstructionCodeGeneratorMIPS::VisitVecReduce(HVecReduce* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ VectorRegister src = VectorRegisterFrom(locations->InAt(0));
+ VectorRegister dst = VectorRegisterFrom(locations->Out());
+ VectorRegister tmp = static_cast<VectorRegister>(FTMP);
+ switch (instruction->GetPackedType()) {
+ case DataType::Type::kInt32:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ switch (instruction->GetKind()) {
+ case HVecReduce::kSum:
+ __ Hadd_sD(tmp, src, src);
+ __ IlvlD(dst, tmp, tmp);
+ __ AddvW(dst, dst, tmp);
+ break;
+ case HVecReduce::kMin:
+ __ IlvodW(tmp, src, src);
+ __ Min_sW(tmp, src, tmp);
+ __ IlvlW(dst, tmp, tmp);
+ __ Min_sW(dst, dst, tmp);
+ break;
+ case HVecReduce::kMax:
+ __ IlvodW(tmp, src, src);
+ __ Max_sW(tmp, src, tmp);
+ __ IlvlW(dst, tmp, tmp);
+ __ Max_sW(dst, dst, tmp);
+ break;
+ }
+ break;
+ case DataType::Type::kInt64:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ switch (instruction->GetKind()) {
+ case HVecReduce::kSum:
+ __ IlvlD(dst, src, src);
+ __ AddvD(dst, dst, src);
+ break;
+ case HVecReduce::kMin:
+ __ IlvlD(dst, src, src);
+ __ Min_sD(dst, dst, src);
+ break;
+ case HVecReduce::kMax:
+ __ IlvlD(dst, src, src);
+ __ Max_sD(dst, dst, src);
+ break;
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void LocationsBuilderMIPS::VisitVecCnv(HVecCnv* instruction) {
@@ -831,11 +923,79 @@
}
void LocationsBuilderMIPS::VisitVecSetScalars(HVecSetScalars* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
+
+ DCHECK_EQ(1u, instruction->InputCount()); // only one input currently implemented
+
+ HInstruction* input = instruction->InputAt(0);
+ bool is_zero = IsZeroBitPattern(input);
+
+ switch (instruction->GetPackedType()) {
+ case DataType::Type::kBool:
+ case DataType::Type::kUint8:
+ case DataType::Type::kInt8:
+ case DataType::Type::kUint16:
+ case DataType::Type::kInt16:
+ case DataType::Type::kInt32:
+ case DataType::Type::kInt64:
+ locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+ : Location::RequiresRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+ case DataType::Type::kFloat32:
+ case DataType::Type::kFloat64:
+ locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+ : Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void InstructionCodeGeneratorMIPS::VisitVecSetScalars(HVecSetScalars* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ VectorRegister dst = VectorRegisterFrom(locations->Out());
+
+ DCHECK_EQ(1u, instruction->InputCount()); // only one input currently implemented
+
+ // Zero out all other elements first.
+ __ FillW(dst, ZERO);
+
+ // Shorthand for any type of zero.
+ if (IsZeroBitPattern(instruction->InputAt(0))) {
+ return;
+ }
+
+ // Set required elements.
+ switch (instruction->GetPackedType()) {
+ case DataType::Type::kBool:
+ case DataType::Type::kUint8:
+ case DataType::Type::kInt8:
+ DCHECK_EQ(16u, instruction->GetVectorLength());
+ __ InsertB(dst, locations->InAt(0).AsRegister<Register>(), 0);
+ break;
+ case DataType::Type::kUint16:
+ case DataType::Type::kInt16:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ __ InsertH(dst, locations->InAt(0).AsRegister<Register>(), 0);
+ break;
+ case DataType::Type::kInt32:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ InsertW(dst, locations->InAt(0).AsRegister<Register>(), 0);
+ break;
+ case DataType::Type::kInt64:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Mtc1(locations->InAt(0).AsRegisterPairLow<Register>(),
+ locations->Out().AsFpuRegister<FRegister>());
+ __ MoveToFpuHigh(locations->InAt(0).AsRegisterPairHigh<Register>(),
+ locations->Out().AsFpuRegister<FRegister>());
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
// Helper to set up locations for vector accumulations.
diff --git a/compiler/optimizing/code_generator_vector_mips64.cc b/compiler/optimizing/code_generator_vector_mips64.cc
index e606df2..0c59b73 100644
--- a/compiler/optimizing/code_generator_vector_mips64.cc
+++ b/compiler/optimizing/code_generator_vector_mips64.cc
@@ -94,17 +94,58 @@
}
void LocationsBuilderMIPS64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
+ switch (instruction->GetPackedType()) {
+ case DataType::Type::kBool:
+ case DataType::Type::kUint8:
+ case DataType::Type::kInt8:
+ case DataType::Type::kUint16:
+ case DataType::Type::kInt16:
+ case DataType::Type::kInt32:
+ case DataType::Type::kInt64:
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresRegister());
+ break;
+ case DataType::Type::kFloat32:
+ case DataType::Type::kFloat64:
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void InstructionCodeGeneratorMIPS64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ VectorRegister src = VectorRegisterFrom(locations->InAt(0));
+ switch (instruction->GetPackedType()) {
+ case DataType::Type::kInt32:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Copy_sW(locations->Out().AsRegister<GpuRegister>(), src, 0);
+ break;
+ case DataType::Type::kInt64:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Copy_sD(locations->Out().AsRegister<GpuRegister>(), src, 0);
+ break;
+ case DataType::Type::kFloat32:
+ case DataType::Type::kFloat64:
+ DCHECK_LE(2u, instruction->GetVectorLength());
+ DCHECK_LE(instruction->GetVectorLength(), 4u);
+ DCHECK(locations->InAt(0).Equals(locations->Out())); // no code required
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
// Helper to set up locations for vector unary operations.
static void CreateVecUnOpLocations(ArenaAllocator* allocator, HVecUnaryOperation* instruction) {
LocationSummary* locations = new (allocator) LocationSummary(instruction);
- switch (instruction->GetPackedType()) {
+ DataType::Type type = instruction->GetPackedType();
+ switch (type) {
case DataType::Type::kBool:
locations->SetInAt(0, Location::RequiresFpuRegister());
locations->SetOut(Location::RequiresFpuRegister(),
@@ -121,7 +162,8 @@
case DataType::Type::kFloat64:
locations->SetInAt(0, Location::RequiresFpuRegister());
locations->SetOut(Location::RequiresFpuRegister(),
- (instruction->IsVecNeg() || instruction->IsVecAbs())
+ (instruction->IsVecNeg() || instruction->IsVecAbs() ||
+ (instruction->IsVecReduce() && type == DataType::Type::kInt64))
? Location::kOutputOverlap
: Location::kNoOutputOverlap);
break;
@@ -136,7 +178,54 @@
}
void InstructionCodeGeneratorMIPS64::VisitVecReduce(HVecReduce* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ VectorRegister src = VectorRegisterFrom(locations->InAt(0));
+ VectorRegister dst = VectorRegisterFrom(locations->Out());
+ VectorRegister tmp = static_cast<VectorRegister>(FTMP);
+ switch (instruction->GetPackedType()) {
+ case DataType::Type::kInt32:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ switch (instruction->GetKind()) {
+ case HVecReduce::kSum:
+ __ Hadd_sD(tmp, src, src);
+ __ IlvlD(dst, tmp, tmp);
+ __ AddvW(dst, dst, tmp);
+ break;
+ case HVecReduce::kMin:
+ __ IlvodW(tmp, src, src);
+ __ Min_sW(tmp, src, tmp);
+ __ IlvlW(dst, tmp, tmp);
+ __ Min_sW(dst, dst, tmp);
+ break;
+ case HVecReduce::kMax:
+ __ IlvodW(tmp, src, src);
+ __ Max_sW(tmp, src, tmp);
+ __ IlvlW(dst, tmp, tmp);
+ __ Max_sW(dst, dst, tmp);
+ break;
+ }
+ break;
+ case DataType::Type::kInt64:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ switch (instruction->GetKind()) {
+ case HVecReduce::kSum:
+ __ IlvlD(dst, src, src);
+ __ AddvD(dst, dst, src);
+ break;
+ case HVecReduce::kMin:
+ __ IlvlD(dst, src, src);
+ __ Min_sD(dst, dst, src);
+ break;
+ case HVecReduce::kMax:
+ __ IlvlD(dst, src, src);
+ __ Max_sD(dst, dst, src);
+ break;
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void LocationsBuilderMIPS64::VisitVecCnv(HVecCnv* instruction) {
@@ -835,11 +924,76 @@
}
void LocationsBuilderMIPS64::VisitVecSetScalars(HVecSetScalars* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
+
+ DCHECK_EQ(1u, instruction->InputCount()); // only one input currently implemented
+
+ HInstruction* input = instruction->InputAt(0);
+ bool is_zero = IsZeroBitPattern(input);
+
+ switch (instruction->GetPackedType()) {
+ case DataType::Type::kBool:
+ case DataType::Type::kUint8:
+ case DataType::Type::kInt8:
+ case DataType::Type::kUint16:
+ case DataType::Type::kInt16:
+ case DataType::Type::kInt32:
+ case DataType::Type::kInt64:
+ locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+ : Location::RequiresRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+ case DataType::Type::kFloat32:
+ case DataType::Type::kFloat64:
+ locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+ : Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void InstructionCodeGeneratorMIPS64::VisitVecSetScalars(HVecSetScalars* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ VectorRegister dst = VectorRegisterFrom(locations->Out());
+
+ DCHECK_EQ(1u, instruction->InputCount()); // only one input currently implemented
+
+ // Zero out all other elements first.
+ __ FillW(dst, ZERO);
+
+ // Shorthand for any type of zero.
+ if (IsZeroBitPattern(instruction->InputAt(0))) {
+ return;
+ }
+
+ // Set required elements.
+ switch (instruction->GetPackedType()) {
+ case DataType::Type::kBool:
+ case DataType::Type::kUint8:
+ case DataType::Type::kInt8:
+ DCHECK_EQ(16u, instruction->GetVectorLength());
+ __ InsertB(dst, locations->InAt(0).AsRegister<GpuRegister>(), 0);
+ break;
+ case DataType::Type::kUint16:
+ case DataType::Type::kInt16:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ __ InsertH(dst, locations->InAt(0).AsRegister<GpuRegister>(), 0);
+ break;
+ case DataType::Type::kInt32:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ InsertW(dst, locations->InAt(0).AsRegister<GpuRegister>(), 0);
+ break;
+ case DataType::Type::kInt64:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ InsertD(dst, locations->InAt(0).AsRegister<GpuRegister>(), 0);
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
// Helper to set up locations for vector accumulations.
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 3851877..f7fd910 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -527,6 +527,10 @@
StartAttributeStream("packed_type") << vec_operation->GetPackedType();
}
+ void VisitVecMemoryOperation(HVecMemoryOperation* vec_mem_operation) OVERRIDE {
+ StartAttributeStream("alignment") << vec_mem_operation->GetAlignment().ToString();
+ }
+
void VisitVecHalvingAdd(HVecHalvingAdd* hadd) OVERRIDE {
VisitVecBinaryOperation(hadd);
StartAttributeStream("unsigned") << std::boolalpha << hadd->IsUnsigned() << std::noboolalpha;
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 4429e6e..bdeb261 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -256,30 +256,63 @@
LOG(FATAL) << "Unreachable: intrinsic " << invoke->GetIntrinsic() \
<< " should have been converted to HIR"; \
}
-#define UNREACHABLE_INTRINSICS(Arch) \
-UNREACHABLE_INTRINSIC(Arch, FloatFloatToIntBits) \
-UNREACHABLE_INTRINSIC(Arch, DoubleDoubleToLongBits) \
-UNREACHABLE_INTRINSIC(Arch, FloatIsNaN) \
-UNREACHABLE_INTRINSIC(Arch, DoubleIsNaN) \
-UNREACHABLE_INTRINSIC(Arch, IntegerRotateLeft) \
-UNREACHABLE_INTRINSIC(Arch, LongRotateLeft) \
-UNREACHABLE_INTRINSIC(Arch, IntegerRotateRight) \
-UNREACHABLE_INTRINSIC(Arch, LongRotateRight) \
-UNREACHABLE_INTRINSIC(Arch, IntegerCompare) \
-UNREACHABLE_INTRINSIC(Arch, LongCompare) \
-UNREACHABLE_INTRINSIC(Arch, IntegerSignum) \
-UNREACHABLE_INTRINSIC(Arch, LongSignum) \
-UNREACHABLE_INTRINSIC(Arch, StringCharAt) \
-UNREACHABLE_INTRINSIC(Arch, StringIsEmpty) \
-UNREACHABLE_INTRINSIC(Arch, StringLength) \
-UNREACHABLE_INTRINSIC(Arch, UnsafeLoadFence) \
-UNREACHABLE_INTRINSIC(Arch, UnsafeStoreFence) \
-UNREACHABLE_INTRINSIC(Arch, UnsafeFullFence) \
-UNREACHABLE_INTRINSIC(Arch, VarHandleFullFence) \
-UNREACHABLE_INTRINSIC(Arch, VarHandleAcquireFence) \
-UNREACHABLE_INTRINSIC(Arch, VarHandleReleaseFence) \
-UNREACHABLE_INTRINSIC(Arch, VarHandleLoadLoadFence) \
-UNREACHABLE_INTRINSIC(Arch, VarHandleStoreStoreFence)
+#define UNREACHABLE_INTRINSICS(Arch) \
+UNREACHABLE_INTRINSIC(Arch, FloatFloatToIntBits) \
+UNREACHABLE_INTRINSIC(Arch, DoubleDoubleToLongBits) \
+UNREACHABLE_INTRINSIC(Arch, FloatIsNaN) \
+UNREACHABLE_INTRINSIC(Arch, DoubleIsNaN) \
+UNREACHABLE_INTRINSIC(Arch, IntegerRotateLeft) \
+UNREACHABLE_INTRINSIC(Arch, LongRotateLeft) \
+UNREACHABLE_INTRINSIC(Arch, IntegerRotateRight) \
+UNREACHABLE_INTRINSIC(Arch, LongRotateRight) \
+UNREACHABLE_INTRINSIC(Arch, IntegerCompare) \
+UNREACHABLE_INTRINSIC(Arch, LongCompare) \
+UNREACHABLE_INTRINSIC(Arch, IntegerSignum) \
+UNREACHABLE_INTRINSIC(Arch, LongSignum) \
+UNREACHABLE_INTRINSIC(Arch, StringCharAt) \
+UNREACHABLE_INTRINSIC(Arch, StringIsEmpty) \
+UNREACHABLE_INTRINSIC(Arch, StringLength) \
+UNREACHABLE_INTRINSIC(Arch, UnsafeLoadFence) \
+UNREACHABLE_INTRINSIC(Arch, UnsafeStoreFence) \
+UNREACHABLE_INTRINSIC(Arch, UnsafeFullFence) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleFullFence) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleAcquireFence) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleReleaseFence) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleLoadLoadFence) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleStoreStoreFence) \
+UNREACHABLE_INTRINSIC(Arch, MethodHandleInvokeExact) \
+UNREACHABLE_INTRINSIC(Arch, MethodHandleInvoke) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleCompareAndExchange) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleCompareAndExchangeAcquire) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleCompareAndExchangeRelease) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleCompareAndSet) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGet) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGetAcquire) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGetAndAdd) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGetAndAddAcquire) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGetAndAddRelease) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGetAndBitwiseAnd) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGetAndBitwiseAndAcquire) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGetAndBitwiseAndRelease) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGetAndBitwiseOr) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGetAndBitwiseOrAcquire) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGetAndBitwiseOrRelease) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGetAndBitwiseXor) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGetAndBitwiseXorAcquire) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGetAndBitwiseXorRelease) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGetAndSet) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGetAndSetAcquire) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGetAndSetRelease) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGetOpaque) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleGetVolatile) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleSet) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleSetOpaque) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleSetRelease) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleSetVolatile) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleWeakCompareAndSet) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleWeakCompareAndSetAcquire) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleWeakCompareAndSetPlain) \
+UNREACHABLE_INTRINSIC(Arch, VarHandleWeakCompareAndSetRelease)
template <typename IntrinsicLocationsBuilder, typename Codegenerator>
bool IsCallFreeIntrinsic(HInvoke* invoke, Codegenerator* codegen) {
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 6a4faaf..74de077 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -25,6 +25,8 @@
#include "arch/x86_64/instruction_set_features_x86_64.h"
#include "driver/compiler_driver.h"
#include "linear_order.h"
+#include "mirror/array-inl.h"
+#include "mirror/string.h"
namespace art {
@@ -71,12 +73,25 @@
// Enables vectorization (SIMDization) in the loop optimizer.
static constexpr bool kEnableVectorization = true;
-// All current SIMD targets want 16-byte alignment.
-static constexpr size_t kAlignedBase = 16;
-
// No loop unrolling factor (just one copy of the loop-body).
static constexpr uint32_t kNoUnrollingFactor = 1;
+//
+// Static helpers.
+//
+
+// Base alignment for arrays/strings guaranteed by the Android runtime.
+static uint32_t BaseAlignment() {
+ return kObjectAlignment;
+}
+
+// Hidden offset for arrays/strings guaranteed by the Android runtime.
+static uint32_t HiddenOffset(DataType::Type type, bool is_string_char_at) {
+ return is_string_char_at
+ ? mirror::String::ValueOffset().Uint32Value()
+ : mirror::Array::DataOffset(DataType::Size(type)).Uint32Value();
+}
+
// Remove the instruction from the graph. A bit more elaborate than the usual
// instruction removal, since there may be a cycle in the use structure.
static void RemoveFromCycle(HInstruction* instruction) {
@@ -288,7 +303,7 @@
}
// Compute relative vector length based on type difference.
-static size_t GetOtherVL(DataType::Type other_type, DataType::Type vector_type, size_t vl) {
+static uint32_t GetOtherVL(DataType::Type other_type, DataType::Type vector_type, uint32_t vl) {
DCHECK(DataType::IsIntegralType(other_type));
DCHECK(DataType::IsIntegralType(vector_type));
DCHECK_GE(DataType::SizeShift(other_type), DataType::SizeShift(vector_type));
@@ -395,7 +410,7 @@
} else if (reduction->IsVecMax()) {
return HVecReduce::kMax;
}
- LOG(FATAL) << "Unsupported SIMD reduction";
+ LOG(FATAL) << "Unsupported SIMD reduction " << reduction->GetId();
UNREACHABLE();
}
@@ -446,7 +461,8 @@
simplified_(false),
vector_length_(0),
vector_refs_(nullptr),
- vector_peeling_candidate_(nullptr),
+ vector_static_peeling_factor_(0),
+ vector_dynamic_peeling_candidate_(nullptr),
vector_runtime_test_a_(nullptr),
vector_runtime_test_b_(nullptr),
vector_map_(nullptr),
@@ -746,7 +762,8 @@
// Reset vector bookkeeping.
vector_length_ = 0;
vector_refs_->clear();
- vector_peeling_candidate_ = nullptr;
+ vector_static_peeling_factor_ = 0;
+ vector_dynamic_peeling_candidate_ = nullptr;
vector_runtime_test_a_ =
vector_runtime_test_b_= nullptr;
@@ -763,10 +780,17 @@
}
}
- // Does vectorization seem profitable?
- if (!IsVectorizationProfitable(trip_count)) {
- return false;
- }
+ // Prepare alignment analysis:
+ // (1) find desired alignment (SIMD vector size in bytes).
+ // (2) initialize static loop peeling votes (peeling factor that will
+ // make one particular reference aligned), never to exceed (1).
+ // (3) variable to record how many references share same alignment.
+ // (4) variable to record suitable candidate for dynamic loop peeling.
+ uint32_t desired_alignment = GetVectorSizeInBytes();
+ DCHECK_LE(desired_alignment, 16u);
+ uint32_t peeling_votes[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ uint32_t max_num_same_alignment = 0;
+ const ArrayReference* peeling_candidate = nullptr;
// Data dependence analysis. Find each pair of references with same type, where
// at least one is a write. Each such pair denotes a possible data dependence.
@@ -774,9 +798,10 @@
// aliased, as well as the property that references either point to the same
// array or to two completely disjoint arrays, i.e., no partial aliasing.
// Other than a few simply heuristics, no detailed subscript analysis is done.
- // The scan over references also finds a suitable dynamic loop peeling candidate.
- const ArrayReference* candidate = nullptr;
+ // The scan over references also prepares finding a suitable alignment strategy.
for (auto i = vector_refs_->begin(); i != vector_refs_->end(); ++i) {
+ uint32_t num_same_alignment = 0;
+ // Scan over all next references.
for (auto j = i; ++j != vector_refs_->end(); ) {
if (i->type == j->type && (i->lhs || j->lhs)) {
// Found same-typed a[i+x] vs. b[i+y], where at least one is a write.
@@ -790,6 +815,10 @@
if (x != y) {
return false;
}
+ // Count the number of references that have the same alignment (since
+ // base and offset are the same) and where at least one is a write, so
+ // e.g. a[i] = a[i] + b[i] counts a[i] but not b[i]).
+ num_same_alignment++;
} else {
// Found a[i+x] vs. b[i+y]. Accept if x == y (at worst loop-independent data dependence).
// Conservatively assume a potential loop-carried data dependence otherwise, avoided by
@@ -808,10 +837,38 @@
}
}
}
- }
+ // Update information for finding suitable alignment strategy:
+ // (1) update votes for static loop peeling,
+ // (2) update suitable candidate for dynamic loop peeling.
+ Alignment alignment = ComputeAlignment(i->offset, i->type, i->is_string_char_at);
+ if (alignment.Base() >= desired_alignment) {
+ // If the array/string object has a known, sufficient alignment, use the
+ // initial offset to compute the static loop peeling vote (this always
+ // works, since elements have natural alignment).
+ uint32_t offset = alignment.Offset() & (desired_alignment - 1u);
+ uint32_t vote = (offset == 0)
+ ? 0
+ : ((desired_alignment - offset) >> DataType::SizeShift(i->type));
+ DCHECK_LT(vote, 16u);
+ ++peeling_votes[vote];
+ } else if (BaseAlignment() >= desired_alignment &&
+ num_same_alignment > max_num_same_alignment) {
+ // Otherwise, if the array/string object has a known, sufficient alignment
+ // for just the base but with an unknown offset, record the candidate with
+ // the most occurrences for dynamic loop peeling (again, the peeling always
+ // works, since elements have natural alignment).
+ max_num_same_alignment = num_same_alignment;
+ peeling_candidate = &(*i);
+ }
+ } // for i
- // Consider dynamic loop peeling for alignment.
- SetPeelingCandidate(candidate, trip_count);
+ // Find a suitable alignment strategy.
+ SetAlignmentStrategy(peeling_votes, peeling_candidate);
+
+ // Does vectorization seem profitable?
+ if (!IsVectorizationProfitable(trip_count)) {
+ return false;
+ }
// Success!
return true;
@@ -828,9 +885,12 @@
uint32_t unroll = GetUnrollingFactor(block, trip_count);
uint32_t chunk = vector_length_ * unroll;
+ DCHECK(trip_count == 0 || (trip_count >= MaxNumberPeeled() + chunk));
+
// A cleanup loop is needed, at least, for any unknown trip count or
// for a known trip count with remainder iterations after vectorization.
- bool needs_cleanup = trip_count == 0 || (trip_count % chunk) != 0;
+ bool needs_cleanup = trip_count == 0 ||
+ ((trip_count - vector_static_peeling_factor_) % chunk) != 0;
// Adjust vector bookkeeping.
HPhi* main_phi = nullptr;
@@ -844,21 +904,40 @@
DCHECK(induc_type == DataType::Type::kInt32 || induc_type == DataType::Type::kInt64)
<< induc_type;
- // Generate dynamic loop peeling trip count, if needed, under the assumption
- // that the Android runtime guarantees at least "component size" alignment:
- // ptc = (ALIGN - (&a[initial] % ALIGN)) / type-size
+ // Generate the trip count for static or dynamic loop peeling, if needed:
+ // ptc = <peeling factor>;
HInstruction* ptc = nullptr;
- if (vector_peeling_candidate_ != nullptr) {
- DCHECK_LT(vector_length_, trip_count) << "dynamic peeling currently requires known trip count";
- //
- // TODO: Implement this. Compute address of first access memory location and
- // compute peeling factor to obtain kAlignedBase alignment.
- //
- needs_cleanup = true;
+ if (vector_static_peeling_factor_ != 0) {
+ // Static loop peeling for SIMD alignment (using the most suitable
+ // fixed peeling factor found during prior alignment analysis).
+ DCHECK(vector_dynamic_peeling_candidate_ == nullptr);
+ ptc = graph_->GetConstant(induc_type, vector_static_peeling_factor_);
+ } else if (vector_dynamic_peeling_candidate_ != nullptr) {
+ // Dynamic loop peeling for SIMD alignment (using the most suitable
+ // candidate found during prior alignment analysis):
+ // rem = offset % ALIGN; // adjusted as #elements
+ // ptc = rem == 0 ? 0 : (ALIGN - rem);
+ uint32_t shift = DataType::SizeShift(vector_dynamic_peeling_candidate_->type);
+ uint32_t align = GetVectorSizeInBytes() >> shift;
+ uint32_t hidden_offset = HiddenOffset(vector_dynamic_peeling_candidate_->type,
+ vector_dynamic_peeling_candidate_->is_string_char_at);
+ HInstruction* adjusted_offset = graph_->GetConstant(induc_type, hidden_offset >> shift);
+ HInstruction* offset = Insert(preheader, new (global_allocator_) HAdd(
+ induc_type, vector_dynamic_peeling_candidate_->offset, adjusted_offset));
+ HInstruction* rem = Insert(preheader, new (global_allocator_) HAnd(
+ induc_type, offset, graph_->GetConstant(induc_type, align - 1u)));
+ HInstruction* sub = Insert(preheader, new (global_allocator_) HSub(
+ induc_type, graph_->GetConstant(induc_type, align), rem));
+ HInstruction* cond = Insert(preheader, new (global_allocator_) HEqual(
+ rem, graph_->GetConstant(induc_type, 0)));
+ ptc = Insert(preheader, new (global_allocator_) HSelect(
+ cond, graph_->GetConstant(induc_type, 0), sub, kNoDexPc));
+ needs_cleanup = true; // don't know the exact amount
}
// Generate loop control:
// stc = <trip-count>;
+ // ptc = min(stc, ptc);
// vtc = stc - (stc - ptc) % chunk;
// i = 0;
HInstruction* stc = induction_range_.GenerateTripCount(node->loop_info, graph_, preheader);
@@ -867,6 +946,10 @@
DCHECK(IsPowerOfTwo(chunk));
HInstruction* diff = stc;
if (ptc != nullptr) {
+ if (trip_count == 0) {
+ HInstruction* cond = Insert(preheader, new (global_allocator_) HAboveOrEqual(stc, ptc));
+ ptc = Insert(preheader, new (global_allocator_) HSelect(cond, ptc, stc, kNoDexPc));
+ }
diff = Insert(preheader, new (global_allocator_) HSub(induc_type, stc, ptc));
}
HInstruction* rem = Insert(
@@ -889,9 +972,13 @@
needs_cleanup = true;
}
- // Generate dynamic peeling loop for alignment, if needed:
+ // Generate alignment peeling loop, if needed:
// for ( ; i < ptc; i += 1)
// <loop-body>
+ //
+ // NOTE: The alignment forced by the peeling loop is preserved even if data is
+ // moved around during suspend checks, since all analysis was based on
+ // nothing more than the Android runtime alignment conventions.
if (ptc != nullptr) {
vector_mode_ = kSequential;
GenerateNewLoop(node,
@@ -1118,7 +1205,7 @@
GenerateVecSub(index, offset);
GenerateVecMem(instruction, vector_map_->Get(index), nullptr, offset, type);
} else {
- vector_refs_->insert(ArrayReference(base, offset, type, /*lhs*/ false));
+ vector_refs_->insert(ArrayReference(base, offset, type, /*lhs*/ false, is_string_char_at));
}
return true;
}
@@ -1144,9 +1231,9 @@
DataType::Type from = conversion->GetInputType();
DataType::Type to = conversion->GetResultType();
if (DataType::IsIntegralType(from) && DataType::IsIntegralType(to)) {
- size_t size_vec = DataType::Size(type);
- size_t size_from = DataType::Size(from);
- size_t size_to = DataType::Size(to);
+ uint32_t size_vec = DataType::Size(type);
+ uint32_t size_from = DataType::Size(from);
+ uint32_t size_to = DataType::Size(to);
// Accept an integral conversion
// (1a) narrowing into vector type, "wider" operations cannot bring in higher order bits, or
// (1b) widening from at least vector type, and
@@ -1325,6 +1412,16 @@
return false;
}
+uint32_t HLoopOptimization::GetVectorSizeInBytes() {
+ switch (compiler_driver_->GetInstructionSet()) {
+ case kArm:
+ case kThumb2:
+ return 8; // 64-bit SIMD
+ default:
+ return 16; // 128-bit SIMD
+ }
+}
+
bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrictions) {
const InstructionSetFeatures* features = compiler_driver_->GetInstructionSetFeatures();
switch (compiler_driver_->GetInstructionSet()) {
@@ -1422,10 +1519,10 @@
*restrictions |= kNoDiv | kNoStringCharAt | kNoReduction | kNoSAD;
return TrySetVectorLength(8);
case DataType::Type::kInt32:
- *restrictions |= kNoDiv | kNoReduction | kNoSAD;
+ *restrictions |= kNoDiv | kNoSAD;
return TrySetVectorLength(4);
case DataType::Type::kInt64:
- *restrictions |= kNoDiv | kNoReduction | kNoSAD;
+ *restrictions |= kNoDiv | kNoSAD;
return TrySetVectorLength(2);
case DataType::Type::kFloat32:
*restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN)
@@ -1451,10 +1548,10 @@
*restrictions |= kNoDiv | kNoStringCharAt | kNoReduction | kNoSAD;
return TrySetVectorLength(8);
case DataType::Type::kInt32:
- *restrictions |= kNoDiv | kNoReduction | kNoSAD;
+ *restrictions |= kNoDiv | kNoSAD;
return TrySetVectorLength(4);
case DataType::Type::kInt64:
- *restrictions |= kNoDiv | kNoReduction | kNoSAD;
+ *restrictions |= kNoDiv | kNoSAD;
return TrySetVectorLength(2);
case DataType::Type::kFloat32:
*restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN)
@@ -1537,12 +1634,13 @@
HInstruction* vector = nullptr;
if (vector_mode_ == kVector) {
// Vector store or load.
+ bool is_string_char_at = false;
HInstruction* base = org->InputAt(0);
if (opb != nullptr) {
vector = new (global_allocator_) HVecStore(
global_allocator_, base, opa, opb, type, org->GetSideEffects(), vector_length_, dex_pc);
} else {
- bool is_string_char_at = org->AsArrayGet()->IsStringCharAt();
+ is_string_char_at = org->AsArrayGet()->IsStringCharAt();
vector = new (global_allocator_) HVecLoad(global_allocator_,
base,
opa,
@@ -1552,11 +1650,17 @@
is_string_char_at,
dex_pc);
}
- // Known dynamically enforced alignment?
- if (vector_peeling_candidate_ != nullptr &&
- vector_peeling_candidate_->base == base &&
- vector_peeling_candidate_->offset == offset) {
- vector->AsVecMemoryOperation()->SetAlignment(Alignment(kAlignedBase, 0));
+ // Known (forced/adjusted/original) alignment?
+ if (vector_dynamic_peeling_candidate_ != nullptr) {
+ if (vector_dynamic_peeling_candidate_->offset == offset && // TODO: diffs too?
+ DataType::Size(vector_dynamic_peeling_candidate_->type) == DataType::Size(type) &&
+ vector_dynamic_peeling_candidate_->is_string_char_at == is_string_char_at) {
+ vector->AsVecMemoryOperation()->SetAlignment( // forced
+ Alignment(GetVectorSizeInBytes(), 0));
+ }
+ } else {
+ vector->AsVecMemoryOperation()->SetAlignment( // adjusted/original
+ ComputeAlignment(offset, type, is_string_char_at, vector_static_peeling_factor_));
}
} else {
// Scalar store or load.
@@ -1612,7 +1716,7 @@
// a [initial, initial, .., initial] vector for min/max.
HVecOperation* red_vector = new_red->AsVecOperation();
HVecReduce::ReductionKind kind = GetReductionKind(red_vector);
- size_t vector_length = red_vector->GetVectorLength();
+ uint32_t vector_length = red_vector->GetVectorLength();
DataType::Type type = red_vector->GetPackedType();
if (kind == HVecReduce::ReductionKind::kSum) {
new_init = Insert(vector_preheader_,
@@ -1644,9 +1748,9 @@
HInstruction* HLoopOptimization::ReduceAndExtractIfNeeded(HInstruction* instruction) {
if (instruction->IsPhi()) {
HInstruction* input = instruction->InputAt(1);
- if (input->IsVecOperation()) {
+ if (input->IsVecOperation() && !input->IsVecExtractScalar()) {
HVecOperation* input_vector = input->AsVecOperation();
- size_t vector_length = input_vector->GetVectorLength();
+ uint32_t vector_length = input_vector->GetVectorLength();
DataType::Type type = input_vector->GetPackedType();
HVecReduce::ReductionKind kind = GetReductionKind(input_vector);
HBasicBlock* exit = instruction->GetBlock()->GetSuccessors()[0];
@@ -1774,7 +1878,7 @@
break;
}
default:
- LOG(FATAL) << "Unsupported SIMD intrinsic";
+ LOG(FATAL) << "Unsupported SIMD intrinsic " << org->GetId();
UNREACHABLE();
} // switch invoke
} else {
@@ -2005,35 +2109,72 @@
// Vectorization heuristics.
//
+Alignment HLoopOptimization::ComputeAlignment(HInstruction* offset,
+ DataType::Type type,
+ bool is_string_char_at,
+ uint32_t peeling) {
+ // Combine the alignment and hidden offset that is guaranteed by
+ // the Android runtime with a known starting index adjusted as bytes.
+ int64_t value = 0;
+ if (IsInt64AndGet(offset, /*out*/ &value)) {
+ uint32_t start_offset =
+ HiddenOffset(type, is_string_char_at) + (value + peeling) * DataType::Size(type);
+ return Alignment(BaseAlignment(), start_offset & (BaseAlignment() - 1u));
+ }
+ // Otherwise, the Android runtime guarantees at least natural alignment.
+ return Alignment(DataType::Size(type), 0);
+}
+
+void HLoopOptimization::SetAlignmentStrategy(uint32_t peeling_votes[],
+ const ArrayReference* peeling_candidate) {
+ // Current heuristic: pick the best static loop peeling factor, if any,
+ // or otherwise use dynamic loop peeling on suggested peeling candidate.
+ uint32_t max_vote = 0;
+ for (int32_t i = 0; i < 16; i++) {
+ if (peeling_votes[i] > max_vote) {
+ max_vote = peeling_votes[i];
+ vector_static_peeling_factor_ = i;
+ }
+ }
+ if (max_vote == 0) {
+ vector_dynamic_peeling_candidate_ = peeling_candidate;
+ }
+}
+
+uint32_t HLoopOptimization::MaxNumberPeeled() {
+ if (vector_dynamic_peeling_candidate_ != nullptr) {
+ return vector_length_ - 1u; // worst-case
+ }
+ return vector_static_peeling_factor_; // known exactly
+}
+
bool HLoopOptimization::IsVectorizationProfitable(int64_t trip_count) {
- // Current heuristic: non-empty body with sufficient number
- // of iterations (if known).
+ // Current heuristic: non-empty body with sufficient number of iterations (if known).
// TODO: refine by looking at e.g. operation count, alignment, etc.
+ // TODO: trip count is really unsigned entity, provided the guarding test
+ // is satisfied; deal with this more carefully later
+ uint32_t max_peel = MaxNumberPeeled();
if (vector_length_ == 0) {
return false; // nothing found
- } else if (0 < trip_count && trip_count < vector_length_) {
+ } else if (trip_count < 0) {
+ return false; // guard against non-taken/large
+ } else if ((0 < trip_count) && (trip_count < (vector_length_ + max_peel))) {
return false; // insufficient iterations
}
return true;
}
-void HLoopOptimization::SetPeelingCandidate(const ArrayReference* candidate,
- int64_t trip_count ATTRIBUTE_UNUSED) {
- // Current heuristic: none.
- // TODO: implement
- vector_peeling_candidate_ = candidate;
-}
-
static constexpr uint32_t ARM64_SIMD_MAXIMUM_UNROLL_FACTOR = 8;
static constexpr uint32_t ARM64_SIMD_HEURISTIC_MAX_BODY_SIZE = 50;
uint32_t HLoopOptimization::GetUnrollingFactor(HBasicBlock* block, int64_t trip_count) {
+ uint32_t max_peel = MaxNumberPeeled();
switch (compiler_driver_->GetInstructionSet()) {
case kArm64: {
// Don't unroll with insufficient iterations.
// TODO: Unroll loops with unknown trip count.
DCHECK_NE(vector_length_, 0u);
- if (trip_count < 2 * vector_length_) {
+ if (trip_count < (2 * vector_length_ + max_peel)) {
return kNoUnrollingFactor;
}
// Don't unroll for large loop body size.
@@ -2045,7 +2186,7 @@
// - At least one iteration of the transformed loop should be executed.
// - The loop body shouldn't be "too big" (heuristic).
uint32_t uf1 = ARM64_SIMD_HEURISTIC_MAX_BODY_SIZE / instruction_count;
- uint32_t uf2 = trip_count / vector_length_;
+ uint32_t uf2 = (trip_count - max_peel) / vector_length_;
uint32_t unroll_factor =
TruncToPowerOfTwo(std::min({uf1, uf2, ARM64_SIMD_MAXIMUM_UNROLL_FACTOR}));
DCHECK_GE(unroll_factor, 1u);
@@ -2112,7 +2253,7 @@
HInstruction* reduction = inputs[1];
if (HasReductionFormat(reduction, phi)) {
HLoopInformation* loop_info = phi->GetBlock()->GetLoopInformation();
- int32_t use_count = 0;
+ uint32_t use_count = 0;
bool single_use_inside_loop =
// Reduction update only used by phi.
reduction->GetUses().HasExactlyOneElement() &&
@@ -2205,7 +2346,7 @@
bool HLoopOptimization::IsOnlyUsedAfterLoop(HLoopInformation* loop_info,
HInstruction* instruction,
bool collect_loop_uses,
- /*out*/ int32_t* use_count) {
+ /*out*/ uint32_t* use_count) {
// Deal with regular uses.
for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
HInstruction* user = use.GetUser();
@@ -2276,7 +2417,7 @@
// Assigning the last value is always successful if there are no uses.
// Otherwise, it succeeds in a no early-exit loop by generating the
// proper last value assignment.
- int32_t use_count = 0;
+ uint32_t use_count = 0;
return IsOnlyUsedAfterLoop(loop_info, instruction, collect_loop_uses, &use_count) &&
(use_count == 0 ||
(!IsEarlyExit(loop_info) && TryReplaceWithLastValue(loop_info, instruction, block)));
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index 768fe55..51e0a98 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -94,20 +94,24 @@
* Representation of a unit-stride array reference.
*/
struct ArrayReference {
- ArrayReference(HInstruction* b, HInstruction* o, DataType::Type t, bool l)
- : base(b), offset(o), type(t), lhs(l) { }
+ ArrayReference(HInstruction* b, HInstruction* o, DataType::Type t, bool l, bool c = false)
+ : base(b), offset(o), type(t), lhs(l), is_string_char_at(c) { }
bool operator<(const ArrayReference& other) const {
return
(base < other.base) ||
(base == other.base &&
(offset < other.offset || (offset == other.offset &&
(type < other.type ||
- (type == other.type && lhs < other.lhs)))));
+ (type == other.type &&
+ (lhs < other.lhs ||
+ (lhs == other.lhs &&
+ is_string_char_at < other.is_string_char_at)))))));
}
- HInstruction* base; // base address
- HInstruction* offset; // offset + i
- DataType::Type type; // component type
- bool lhs; // def/use
+ HInstruction* base; // base address
+ HInstruction* offset; // offset + i
+ DataType::Type type; // component type
+ bool lhs; // def/use
+ bool is_string_char_at; // compressed string read
};
//
@@ -152,6 +156,7 @@
bool generate_code,
DataType::Type type,
uint64_t restrictions);
+ uint32_t GetVectorSizeInBytes();
bool TrySetVectorType(DataType::Type type, /*out*/ uint64_t* restrictions);
bool TrySetVectorLength(uint32_t length);
void GenerateVecInv(HInstruction* org, DataType::Type type);
@@ -183,8 +188,14 @@
uint64_t restrictions);
// Vectorization heuristics.
+ Alignment ComputeAlignment(HInstruction* offset,
+ DataType::Type type,
+ bool is_string_char_at,
+ uint32_t peeling = 0);
+ void SetAlignmentStrategy(uint32_t peeling_votes[],
+ const ArrayReference* peeling_candidate);
+ uint32_t MaxNumberPeeled();
bool IsVectorizationProfitable(int64_t trip_count);
- void SetPeelingCandidate(const ArrayReference* candidate, int64_t trip_count);
uint32_t GetUnrollingFactor(HBasicBlock* block, int64_t trip_count);
//
@@ -202,7 +213,7 @@
bool IsOnlyUsedAfterLoop(HLoopInformation* loop_info,
HInstruction* instruction,
bool collect_loop_uses,
- /*out*/ int32_t* use_count);
+ /*out*/ uint32_t* use_count);
bool IsUsedOutsideLoop(HLoopInformation* loop_info,
HInstruction* instruction);
bool TryReplaceWithLastValue(HLoopInformation* loop_info,
@@ -254,8 +265,9 @@
// Contents reside in phase-local heap memory.
ScopedArenaSet<ArrayReference>* vector_refs_;
- // Dynamic loop peeling candidate for alignment.
- const ArrayReference* vector_peeling_candidate_;
+ // Static or dynamic loop peeling for alignment.
+ uint32_t vector_static_peeling_factor_;
+ const ArrayReference* vector_dynamic_peeling_candidate_;
// Dynamic data dependence test of the form a != b.
HInstruction* vector_runtime_test_a_;
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index affe639..1cd9142 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -43,6 +43,7 @@
#include "art_method-inl.h"
#include "base/callee_save_type.h"
#include "base/dumpable.h"
+#include "base/file_utils.h"
#include "base/macros.h"
#include "base/scoped_flock.h"
#include "base/stl_util.h"
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index 0a50681..a02fbf8 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -24,6 +24,7 @@
#include "common_runtime_test.h"
+#include "base/file_utils.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/unix_file/fd_file.h"
diff --git a/dex2oat/linker/elf_writer_test.cc b/dex2oat/linker/elf_writer_test.cc
index 9f8ed77..8427e7b 100644
--- a/dex2oat/linker/elf_writer_test.cc
+++ b/dex2oat/linker/elf_writer_test.cc
@@ -16,6 +16,7 @@
#include "elf_file.h"
+#include "base/file_utils.h"
#include "base/unix_file/fd_file.h"
#include "common_compiler_test.h"
#include "elf_file.h"
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index dc570da..d3d42b9 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -26,6 +26,7 @@
#include "android-base/stringprintf.h"
#include "art_method-inl.h"
+#include "base/file_utils.h"
#include "base/unix_file/fd_file.h"
#include "class_linker-inl.h"
#include "common_compiler_test.h"
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index 8c4ee6e..23c3a5c 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -167,10 +167,17 @@
std::vector<MethodId*>* method_ids,
std::vector<FieldId*>* field_ids) {
bool has_id = false;
- for (const Instruction& instruction : code->Instructions()) {
- CHECK_GT(instruction.SizeInCodeUnits(), 0u);
+ IterationRange<DexInstructionIterator> instructions = code->Instructions();
+ SafeDexInstructionIterator it(instructions.begin(), instructions.end());
+ for (; !it.IsErrorState() && it < instructions.end(); ++it) {
+ // In case the instruction goes past the end of the code item, make sure to not process it.
+ SafeDexInstructionIterator next = it;
+ ++next;
+ if (next.IsErrorState() || next > instructions.end()) {
+ break;
+ }
has_id |= GetIdFromInstruction(collections,
- &instruction,
+ it.Inst(),
type_ids,
string_ids,
method_ids,
diff --git a/dexlayout/dexdiag_test.cc b/dexlayout/dexdiag_test.cc
index 6fcd6ff..9927576 100644
--- a/dexlayout/dexdiag_test.cc
+++ b/dexlayout/dexdiag_test.cc
@@ -19,6 +19,7 @@
#include "common_runtime_test.h"
+#include "base/file_utils.h"
#include "exec_utils.h"
#include "oat_file.h"
#include "os.h"
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index f34e7ec..38d3c6e 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -317,6 +317,30 @@
return true;
}
+ template <typename Mutator>
+ bool MutateDexFile(File* output_dex, const std::string& input_jar, const Mutator& mutator) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ std::string error_msg;
+ CHECK(DexFileLoader::Open(input_jar.c_str(),
+ input_jar.c_str(),
+ /*verify*/ true,
+ /*verify_checksum*/ true,
+ &error_msg,
+ &dex_files)) << error_msg;
+ EXPECT_EQ(dex_files.size(), 1u) << "Only one input dex is supported";
+ for (const std::unique_ptr<const DexFile>& dex : dex_files) {
+ CHECK(dex->EnableWrite()) << "Failed to enable write";
+ mutator(const_cast<DexFile*>(dex.get()));
+ if (!output_dex->WriteFully(dex->Begin(), dex->Size())) {
+ return false;
+ }
+ }
+ if (output_dex->Flush() != 0) {
+ PLOG(FATAL) << "Could not flush the output file.";
+ }
+ return true;
+ }
+
// Create a profile with some subset of methods and classes.
void CreateProfile(const std::string& input_dex,
const std::string& out_profile,
@@ -518,8 +542,10 @@
const char* dex_filename,
ScratchFile* profile_file,
std::vector<std::string>& dexlayout_exec_argv) {
- WriteBase64ToFile(dex_filename, dex_file->GetFile());
- EXPECT_EQ(dex_file->GetFile()->Flush(), 0);
+ if (dex_filename != nullptr) {
+ WriteBase64ToFile(dex_filename, dex_file->GetFile());
+ EXPECT_EQ(dex_file->GetFile()->Flush(), 0);
+ }
if (profile_file != nullptr) {
CreateProfile(dex_file->GetFilename(), profile_file->GetFilename(), dex_file->GetFilename());
}
@@ -673,4 +699,58 @@
dexlayout_exec_argv));
}
+// Test that instructions that go past the end of the code items don't cause crashes.
+TEST_F(DexLayoutTest, CodeItemOverrun) {
+ ScratchFile temp_dex;
+ MutateDexFile(temp_dex.GetFile(), GetTestDexFileName("ManyMethods"), [] (DexFile* dex) {
+ bool mutated_successfully = false;
+ // Change the dex instructions to make an opcode that spans past the end of the code item.
+ for (size_t i = 0; i < dex->NumClassDefs(); ++i) {
+ const DexFile::ClassDef& def = dex->GetClassDef(i);
+ const uint8_t* data = dex->GetClassData(def);
+ if (data == nullptr) {
+ continue;
+ }
+ ClassDataItemIterator it(*dex, data);
+ it.SkipAllFields();
+ while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) {
+ DexFile::CodeItem* item = const_cast<DexFile::CodeItem*>(it.GetMethodCodeItem());
+ if (item != nullptr) {
+ IterationRange<DexInstructionIterator> instructions = item->Instructions();
+ if (instructions.begin() != instructions.end()) {
+ DexInstructionIterator last_instruction = instructions.begin();
+ for (auto dex_it = instructions.begin(); dex_it != instructions.end(); ++dex_it) {
+ last_instruction = dex_it;
+ }
+ if (last_instruction->SizeInCodeUnits() == 1) {
+ // Set the opcode to something that will go past the end of the code item.
+ const_cast<Instruction*>(last_instruction.Inst())->SetOpcode(
+ Instruction::CONST_STRING_JUMBO);
+ mutated_successfully = true;
+ // Test that the safe iterator doesn't go past the end.
+ SafeDexInstructionIterator it2(instructions.begin(), instructions.end());
+ while (!it2.IsErrorState()) {
+ ++it2;
+ }
+ EXPECT_TRUE(it2 == last_instruction);
+ EXPECT_TRUE(it2 < instructions.end());
+ }
+ }
+ }
+ it.Next();
+ }
+ }
+ CHECK(mutated_successfully)
+ << "Failed to find candidate code item with only one code unit in last instruction.";
+ });
+ std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout";
+ EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path";
+ std::vector<std::string> dexlayout_exec_argv =
+ { dexlayout, "-i", "-o", "/dev/null", temp_dex.GetFilename() };
+ ASSERT_TRUE(DexLayoutExec(&temp_dex,
+ /*dex_filename*/ nullptr,
+ nullptr /* profile_file */,
+ dexlayout_exec_argv));
+}
+
} // namespace art
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
index 08d38d5..2c57e40 100644
--- a/dexoptanalyzer/dexoptanalyzer.cc
+++ b/dexoptanalyzer/dexoptanalyzer.cc
@@ -18,7 +18,9 @@
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
+#include "base/file_utils.h"
#include "compiler_filter.h"
+#include "class_loader_context.h"
#include "dex_file.h"
#include "noop_compiler_callbacks.h"
#include "oat_file_assistant.h"
@@ -175,7 +177,15 @@
oat_fd_ = std::stoi(option.substr(strlen("--oat-fd=")).ToString(), nullptr, 0);
} else if (option.starts_with("--vdex-fd")) {
vdex_fd_ = std::stoi(option.substr(strlen("--vdex-fd=")).ToString(), nullptr, 0);
- } else { Usage("Unknown argument '%s'", option.data()); }
+ } else if (option.starts_with("--class-loader-context=")) {
+ std::string context_str = option.substr(strlen("--class-loader-context=")).ToString();
+ class_loader_context_ = ClassLoaderContext::Create(context_str);
+ if (class_loader_context_ == nullptr) {
+ Usage("Invalid --class-loader-context '%s'", context_str.c_str());
+ }
+ } else {
+ Usage("Unknown argument '%s'", option.data());
+ }
}
if (image_.empty()) {
@@ -255,9 +265,8 @@
return kNoDexOptNeeded;
}
- // TODO(calin): Pass the class loader context as an argument to dexoptanalyzer. b/62269291.
int dexoptNeeded = oat_file_assistant->GetDexOptNeeded(
- compiler_filter_, assume_profile_changed_, downgrade_);
+ compiler_filter_, assume_profile_changed_, downgrade_, class_loader_context_.get());
// Convert OatFileAssitant codes to dexoptanalyzer codes.
switch (dexoptNeeded) {
@@ -280,6 +289,7 @@
std::string dex_file_;
InstructionSet isa_;
CompilerFilter::Filter compiler_filter_;
+ std::unique_ptr<ClassLoaderContext> class_loader_context_;
bool assume_profile_changed_;
bool downgrade_;
std::string image_;
diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h
index 52fe973..d0f05d9 100644
--- a/oatdump/oatdump_test.h
+++ b/oatdump/oatdump_test.h
@@ -24,6 +24,7 @@
#include "android-base/strings.h"
#include "arch/instruction_set.h"
+#include "base/file_utils.h"
#include "base/unix_file/fd_file.h"
#include "common_runtime_test.h"
#include "exec_utils.h"
diff --git a/openjdkjvmti/Android.bp b/openjdkjvmti/Android.bp
index eebfec4..c6090ef 100644
--- a/openjdkjvmti/Android.bp
+++ b/openjdkjvmti/Android.bp
@@ -24,6 +24,7 @@
defaults: ["art_defaults"],
host_supported: true,
srcs: [
+ "deopt_manager.cc",
"events.cc",
"fixed_up_dex_file.cc",
"object_tagging.cc",
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index c2584e6..5f726b1 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -71,6 +71,7 @@
namespace openjdkjvmti {
EventHandler gEventHandler;
+DeoptManager gDeoptManager;
#define ENSURE_NON_NULL(n) \
do { \
@@ -1711,6 +1712,7 @@
extern "C" bool ArtPlugin_Initialize() {
art::Runtime* runtime = art::Runtime::Current();
+ gDeoptManager.Setup();
if (runtime->IsStarted()) {
PhaseUtil::SetToLive();
} else {
@@ -1731,6 +1733,7 @@
extern "C" bool ArtPlugin_Deinitialize() {
gEventHandler.Shutdown();
+ gDeoptManager.Shutdown();
PhaseUtil::Unregister();
ThreadUtil::Unregister();
ClassUtil::Unregister();
diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h
index 3edefaf..1263460 100644
--- a/openjdkjvmti/art_jvmti.h
+++ b/openjdkjvmti/art_jvmti.h
@@ -39,6 +39,7 @@
#include <jni.h>
+#include "deopt_manager.h"
#include "base/casts.h"
#include "base/logging.h"
#include "base/macros.h"
@@ -60,9 +61,6 @@
class ObjectTagTable;
-// Make sure that the DEFAULT_MUTEX_ACQUIRED_AFTER macro works.
-using art::Locks;
-
// A structure that is a jvmtiEnv with additional information for the runtime.
struct ArtJvmTiEnv : public jvmtiEnv {
art::JavaVMExt* art_vm;
diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc
new file mode 100644
index 0000000..f843054
--- /dev/null
+++ b/openjdkjvmti/deopt_manager.cc
@@ -0,0 +1,322 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <functional>
+
+#include "deopt_manager.h"
+
+#include "art_jvmti.h"
+#include "art_method-inl.h"
+#include "base/enums.h"
+#include "base/mutex-inl.h"
+#include "dex_file_annotations.h"
+#include "events-inl.h"
+#include "jni_internal.h"
+#include "mirror/class-inl.h"
+#include "mirror/object_array-inl.h"
+#include "modifiers.h"
+#include "nativehelper/scoped_local_ref.h"
+#include "runtime_callbacks.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-current-inl.h"
+#include "thread_list.h"
+#include "ti_phase.h"
+
+namespace openjdkjvmti {
+
+// TODO We should make this much more selective in the future so we only return true when we
+// actually care about the method (i.e. had locals changed, have breakpoints, etc.). For now though
+// we can just assume that we care we are loaded at all.
+//
+// Even if we don't keep track of this at the method level we might want to keep track of it at the
+// level of enabled capabilities.
+bool JvmtiMethodInspectionCallback::IsMethodBeingInspected(
+ art::ArtMethod* method ATTRIBUTE_UNUSED) {
+ return true;
+}
+
+bool JvmtiMethodInspectionCallback::IsMethodSafeToJit(art::ArtMethod* method) {
+ return !manager_->MethodHasBreakpoints(method);
+}
+
+DeoptManager::DeoptManager()
+ : deoptimization_status_lock_("JVMTI_DeoptimizationStatusLock"),
+ deoptimization_condition_("JVMTI_DeoptimizationCondition", deoptimization_status_lock_),
+ performing_deoptimization_(false),
+ global_deopt_count_(0),
+ deopter_count_(0),
+ inspection_callback_(this) { }
+
+void DeoptManager::Setup() {
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Add method Inspection Callback");
+ art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks();
+ callbacks->AddMethodInspectionCallback(&inspection_callback_);
+}
+
+void DeoptManager::Shutdown() {
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("remove method Inspection Callback");
+ art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks();
+ callbacks->RemoveMethodInspectionCallback(&inspection_callback_);
+}
+
+bool DeoptManager::MethodHasBreakpoints(art::ArtMethod* method) {
+ art::MutexLock lk(art::Thread::Current(), deoptimization_status_lock_);
+ return MethodHasBreakpointsLocked(method);
+}
+
+bool DeoptManager::MethodHasBreakpointsLocked(art::ArtMethod* method) {
+ if (deopter_count_ == 0) {
+ return false;
+ }
+ auto elem = breakpoint_status_.find(method);
+ return elem != breakpoint_status_.end() && elem->second != 0;
+}
+
+void DeoptManager::RemoveDeoptimizeAllMethods() {
+ art::Thread* self = art::Thread::Current();
+ art::ScopedThreadSuspension sts(self, art::kSuspended);
+ deoptimization_status_lock_.ExclusiveLock(self);
+ RemoveDeoptimizeAllMethodsLocked(self);
+}
+
+void DeoptManager::AddDeoptimizeAllMethods() {
+ art::Thread* self = art::Thread::Current();
+ art::ScopedThreadSuspension sts(self, art::kSuspended);
+ deoptimization_status_lock_.ExclusiveLock(self);
+ AddDeoptimizeAllMethodsLocked(self);
+}
+
+void DeoptManager::AddMethodBreakpoint(art::ArtMethod* method) {
+ DCHECK(method->IsInvokable());
+ DCHECK(!method->IsProxyMethod()) << method->PrettyMethod();
+ DCHECK(!method->IsNative()) << method->PrettyMethod();
+
+ art::Thread* self = art::Thread::Current();
+ method = method->GetCanonicalMethod();
+ bool is_default = method->IsDefault();
+
+ art::ScopedThreadSuspension sts(self, art::kSuspended);
+ deoptimization_status_lock_.ExclusiveLock(self);
+
+ DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request";
+
+ if (MethodHasBreakpointsLocked(method)) {
+ // Don't need to do anything extra.
+ breakpoint_status_[method]++;
+ // Another thread might be deoptimizing the very method we just added new breakpoints for. Wait
+ // for any deopts to finish before moving on.
+ WaitForDeoptimizationToFinish(self);
+ return;
+ }
+ breakpoint_status_[method] = 1;
+ auto instrumentation = art::Runtime::Current()->GetInstrumentation();
+ if (instrumentation->IsForcedInterpretOnly()) {
+ // We are already interpreting everything so no need to do anything.
+ deoptimization_status_lock_.ExclusiveUnlock(self);
+ return;
+ } else if (is_default) {
+ AddDeoptimizeAllMethodsLocked(self);
+ } else {
+ PerformLimitedDeoptimization(self, method);
+ }
+}
+
+void DeoptManager::RemoveMethodBreakpoint(art::ArtMethod* method) {
+ DCHECK(method->IsInvokable()) << method->PrettyMethod();
+ DCHECK(!method->IsProxyMethod()) << method->PrettyMethod();
+ DCHECK(!method->IsNative()) << method->PrettyMethod();
+
+ art::Thread* self = art::Thread::Current();
+ method = method->GetCanonicalMethod();
+ bool is_default = method->IsDefault();
+
+ art::ScopedThreadSuspension sts(self, art::kSuspended);
+ // Ideally we should do a ScopedSuspendAll right here to get the full mutator_lock_ that we might
+ // need but since that is very heavy we will instead just use a condition variable to make sure we
+ // don't race with ourselves.
+ deoptimization_status_lock_.ExclusiveLock(self);
+
+ DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request";
+ DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without "
+ << "breakpoints present!";
+ auto instrumentation = art::Runtime::Current()->GetInstrumentation();
+ breakpoint_status_[method] -= 1;
+ if (UNLIKELY(instrumentation->IsForcedInterpretOnly())) {
+ // We don't need to do anything since we are interpreting everything anyway.
+ deoptimization_status_lock_.ExclusiveUnlock(self);
+ return;
+ } else if (breakpoint_status_[method] == 0) {
+ if (UNLIKELY(is_default)) {
+ RemoveDeoptimizeAllMethodsLocked(self);
+ } else {
+ PerformLimitedUndeoptimization(self, method);
+ }
+ } else {
+ // Another thread might be deoptimizing the very methods we just removed breakpoints from. Wait
+ // for any deopts to finish before moving on.
+ WaitForDeoptimizationToFinish(self);
+ }
+}
+
+void DeoptManager::WaitForDeoptimizationToFinishLocked(art::Thread* self) {
+ while (performing_deoptimization_) {
+ deoptimization_condition_.Wait(self);
+ }
+}
+
+void DeoptManager::WaitForDeoptimizationToFinish(art::Thread* self) {
+ WaitForDeoptimizationToFinishLocked(self);
+ deoptimization_status_lock_.ExclusiveUnlock(self);
+}
+
+class ScopedDeoptimizationContext : public art::ValueObject {
+ public:
+ ScopedDeoptimizationContext(art::Thread* self, DeoptManager* deopt)
+ RELEASE(deopt->deoptimization_status_lock_)
+ ACQUIRE(art::Locks::mutator_lock_)
+ ACQUIRE(art::Roles::uninterruptible_)
+ : self_(self), deopt_(deopt), uninterruptible_cause_(nullptr) {
+ deopt_->WaitForDeoptimizationToFinishLocked(self_);
+ DCHECK(!deopt->performing_deoptimization_)
+ << "Already performing deoptimization on another thread!";
+ // Use performing_deoptimization_ to keep track of the lock.
+ deopt_->performing_deoptimization_ = true;
+ deopt_->deoptimization_status_lock_.Unlock(self_);
+ art::Runtime::Current()->GetThreadList()->SuspendAll("JMVTI Deoptimizing methods",
+ /*long_suspend*/ false);
+ uninterruptible_cause_ = self_->StartAssertNoThreadSuspension("JVMTI deoptimizing methods");
+ }
+
+ ~ScopedDeoptimizationContext()
+ RELEASE(art::Locks::mutator_lock_)
+ RELEASE(art::Roles::uninterruptible_) {
+ // Can be suspended again.
+ self_->EndAssertNoThreadSuspension(uninterruptible_cause_);
+ // Release the mutator lock.
+ art::Runtime::Current()->GetThreadList()->ResumeAll();
+ // Let other threads know it's fine to proceed.
+ art::MutexLock lk(self_, deopt_->deoptimization_status_lock_);
+ deopt_->performing_deoptimization_ = false;
+ deopt_->deoptimization_condition_.Broadcast(self_);
+ }
+
+ private:
+ art::Thread* self_;
+ DeoptManager* deopt_;
+ const char* uninterruptible_cause_;
+};
+
+void DeoptManager::AddDeoptimizeAllMethodsLocked(art::Thread* self) {
+ global_deopt_count_++;
+ if (global_deopt_count_ == 1) {
+ PerformGlobalDeoptimization(self);
+ } else {
+ WaitForDeoptimizationToFinish(self);
+ }
+}
+
+void DeoptManager::RemoveDeoptimizeAllMethodsLocked(art::Thread* self) {
+ DCHECK_GT(global_deopt_count_, 0u) << "Request to remove non-existant global deoptimization!";
+ global_deopt_count_--;
+ if (global_deopt_count_ == 0) {
+ PerformGlobalUndeoptimization(self);
+ } else {
+ WaitForDeoptimizationToFinish(self);
+ }
+}
+
+void DeoptManager::PerformLimitedDeoptimization(art::Thread* self, art::ArtMethod* method) {
+ ScopedDeoptimizationContext sdc(self, this);
+ art::Runtime::Current()->GetInstrumentation()->Deoptimize(method);
+}
+
+void DeoptManager::PerformLimitedUndeoptimization(art::Thread* self, art::ArtMethod* method) {
+ ScopedDeoptimizationContext sdc(self, this);
+ art::Runtime::Current()->GetInstrumentation()->Undeoptimize(method);
+}
+
+void DeoptManager::PerformGlobalDeoptimization(art::Thread* self) {
+ ScopedDeoptimizationContext sdc(self, this);
+ art::Runtime::Current()->GetInstrumentation()->DeoptimizeEverything(
+ kDeoptManagerInstrumentationKey);
+}
+
+void DeoptManager::PerformGlobalUndeoptimization(art::Thread* self) {
+ ScopedDeoptimizationContext sdc(self, this);
+ art::Runtime::Current()->GetInstrumentation()->UndeoptimizeEverything(
+ kDeoptManagerInstrumentationKey);
+}
+
+
+void DeoptManager::RemoveDeoptimizationRequester() {
+ art::Thread* self = art::Thread::Current();
+ art::ScopedThreadStateChange sts(self, art::kSuspended);
+ deoptimization_status_lock_.ExclusiveLock(self);
+ DCHECK_GT(deopter_count_, 0u) << "Removing deoptimization requester without any being present";
+ deopter_count_--;
+ if (deopter_count_ == 0) {
+ ScopedDeoptimizationContext sdc(self, this);
+ // TODO Give this a real key.
+ art::Runtime::Current()->GetInstrumentation()->DisableDeoptimization("");
+ return;
+ } else {
+ deoptimization_status_lock_.ExclusiveUnlock(self);
+ }
+}
+
+void DeoptManager::AddDeoptimizationRequester() {
+ art::Thread* self = art::Thread::Current();
+ art::ScopedThreadStateChange stsc(self, art::kSuspended);
+ deoptimization_status_lock_.ExclusiveLock(self);
+ deopter_count_++;
+ if (deopter_count_ == 1) {
+ ScopedDeoptimizationContext sdc(self, this);
+ art::Runtime::Current()->GetInstrumentation()->EnableDeoptimization();
+ return;
+ } else {
+ deoptimization_status_lock_.ExclusiveUnlock(self);
+ }
+}
+
+void DeoptManager::DeoptimizeThread(art::Thread* target) {
+ art::Runtime::Current()->GetInstrumentation()->InstrumentThreadStack(target);
+}
+
+extern DeoptManager gDeoptManager;
+DeoptManager* DeoptManager::Get() {
+ return &gDeoptManager;
+}
+
+} // namespace openjdkjvmti
diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h
new file mode 100644
index 0000000..b265fa8
--- /dev/null
+++ b/openjdkjvmti/deopt_manager.h
@@ -0,0 +1,168 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_OPENJDKJVMTI_DEOPT_MANAGER_H_
+#define ART_OPENJDKJVMTI_DEOPT_MANAGER_H_
+
+#include <unordered_map>
+
+#include "jni.h"
+#include "jvmti.h"
+
+#include "base/mutex.h"
+#include "runtime_callbacks.h"
+#include "ti_breakpoint.h"
+
+namespace art {
+class ArtMethod;
+namespace mirror {
+class Class;
+} // namespace mirror
+} // namespace art
+
+namespace openjdkjvmti {
+
+class DeoptManager;
+
+struct JvmtiMethodInspectionCallback : public art::MethodInspectionCallback {
+ public:
+ explicit JvmtiMethodInspectionCallback(DeoptManager* manager) : manager_(manager) {}
+
+ bool IsMethodBeingInspected(art::ArtMethod* method)
+ OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ bool IsMethodSafeToJit(art::ArtMethod* method)
+ OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ private:
+ DeoptManager* manager_;
+};
+
+class ScopedDeoptimizationContext;
+
+class DeoptManager {
+ public:
+ DeoptManager();
+
+ void Setup();
+ void Shutdown();
+
+ void RemoveDeoptimizationRequester() REQUIRES(!deoptimization_status_lock_,
+ !art::Roles::uninterruptible_);
+ void AddDeoptimizationRequester() REQUIRES(!deoptimization_status_lock_,
+ !art::Roles::uninterruptible_);
+ bool MethodHasBreakpoints(art::ArtMethod* method)
+ REQUIRES(!deoptimization_status_lock_);
+
+ void RemoveMethodBreakpoint(art::ArtMethod* method)
+ REQUIRES(!deoptimization_status_lock_, !art::Roles::uninterruptible_)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ void AddMethodBreakpoint(art::ArtMethod* method)
+ REQUIRES(!deoptimization_status_lock_, !art::Roles::uninterruptible_)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ void AddDeoptimizeAllMethods()
+ REQUIRES(!deoptimization_status_lock_, !art::Roles::uninterruptible_)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ void RemoveDeoptimizeAllMethods()
+ REQUIRES(!deoptimization_status_lock_, !art::Roles::uninterruptible_)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ void DeoptimizeThread(art::Thread* target) REQUIRES_SHARED(art::Locks::mutator_lock_);
+ void DeoptimizeAllThreads() REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ static DeoptManager* Get();
+
+ private:
+ bool MethodHasBreakpointsLocked(art::ArtMethod* method)
+ REQUIRES(deoptimization_status_lock_);
+
+ // Wait until nothing is currently in the middle of deoptimizing/undeoptimizing something. This is
+ // needed to ensure that everything is synchronized since threads need to drop the
+ // deoptimization_status_lock_ while deoptimizing methods.
+ void WaitForDeoptimizationToFinish(art::Thread* self)
+ RELEASE(deoptimization_status_lock_) REQUIRES(!art::Locks::mutator_lock_);
+
+ void WaitForDeoptimizationToFinishLocked(art::Thread* self)
+ REQUIRES(deoptimization_status_lock_, !art::Locks::mutator_lock_);
+
+ void AddDeoptimizeAllMethodsLocked(art::Thread* self)
+ RELEASE(deoptimization_status_lock_)
+ REQUIRES(!art::Roles::uninterruptible_, !art::Locks::mutator_lock_);
+
+ void RemoveDeoptimizeAllMethodsLocked(art::Thread* self)
+ RELEASE(deoptimization_status_lock_)
+ REQUIRES(!art::Roles::uninterruptible_, !art::Locks::mutator_lock_);
+
+ void PerformGlobalDeoptimization(art::Thread* self)
+ RELEASE(deoptimization_status_lock_)
+ REQUIRES(!art::Roles::uninterruptible_, !art::Locks::mutator_lock_);
+
+ void PerformGlobalUndeoptimization(art::Thread* self)
+ RELEASE(deoptimization_status_lock_)
+ REQUIRES(!art::Roles::uninterruptible_, !art::Locks::mutator_lock_);
+
+ void PerformLimitedDeoptimization(art::Thread* self, art::ArtMethod* method)
+ RELEASE(deoptimization_status_lock_)
+ REQUIRES(!art::Roles::uninterruptible_, !art::Locks::mutator_lock_);
+
+ void PerformLimitedUndeoptimization(art::Thread* self, art::ArtMethod* method)
+ RELEASE(deoptimization_status_lock_)
+ REQUIRES(!art::Roles::uninterruptible_, !art::Locks::mutator_lock_);
+
+ static constexpr const char* kDeoptManagerInstrumentationKey = "JVMTI_DeoptManager";
+ // static constexpr const char* kDeoptManagerThreadName = "JVMTI_DeoptManagerWorkerThread";
+
+ art::Mutex deoptimization_status_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ art::ConditionVariable deoptimization_condition_ GUARDED_BY(deoptimization_status_lock_);
+ bool performing_deoptimization_ GUARDED_BY(deoptimization_status_lock_);
+
+ // Number of times we have gotten requests to deopt everything.
+ uint32_t global_deopt_count_ GUARDED_BY(deoptimization_status_lock_);
+
+ // Number of users of deoptimization there currently are.
+ uint32_t deopter_count_ GUARDED_BY(deoptimization_status_lock_);
+
+ // A map from methods to the number of breakpoints in them from all envs.
+ std::unordered_map<art::ArtMethod*, uint32_t> breakpoint_status_
+ GUARDED_BY(deoptimization_status_lock_);
+
+ // The MethodInspectionCallback we use to tell the runtime if we care about particular methods.
+ JvmtiMethodInspectionCallback inspection_callback_;
+
+ // Helper for setting up/tearing-down for deoptimization.
+ friend class ScopedDeoptimizationContext;
+};
+
+} // namespace openjdkjvmti
+#endif // ART_OPENJDKJVMTI_DEOPT_MANAGER_H_
diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h
index d97916f..7f77f90 100644
--- a/openjdkjvmti/events-inl.h
+++ b/openjdkjvmti/events-inl.h
@@ -480,6 +480,7 @@
ArtJvmtiEvent event = added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable
: ArtJvmtiEvent::kClassFileLoadHookRetransformable;
return (added && caps.can_access_local_variables == 1) ||
+ caps.can_generate_breakpoint_events == 1 ||
(caps.can_retransform_classes == 1 &&
IsEventEnabledAnywhere(event) &&
env->event_masks.IsEnabledAnywhere(event));
@@ -497,6 +498,9 @@
if (added && caps.can_access_local_variables == 1) {
HandleLocalAccessCapabilityAdded();
}
+ if (caps.can_generate_breakpoint_events == 1) {
+ HandleBreakpointEventsChanged(added);
+ }
}
}
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index 381dc1f..6a64441 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -37,6 +37,7 @@
#include "art_jvmti.h"
#include "art_method-inl.h"
#include "base/logging.h"
+#include "deopt_manager.h"
#include "dex_file_types.h"
#include "gc/allocation_listener.h"
#include "gc/gc_pause_listener.h"
@@ -810,9 +811,49 @@
}
}
+static bool EventNeedsFullDeopt(ArtJvmtiEvent event) {
+ switch (event) {
+ case ArtJvmtiEvent::kBreakpoint:
+ case ArtJvmtiEvent::kException:
+ return false;
+ // TODO We should support more of these or at least do something to make them discriminate by
+ // thread.
+ case ArtJvmtiEvent::kMethodEntry:
+ case ArtJvmtiEvent::kExceptionCatch:
+ case ArtJvmtiEvent::kMethodExit:
+ case ArtJvmtiEvent::kFieldModification:
+ case ArtJvmtiEvent::kFieldAccess:
+ case ArtJvmtiEvent::kSingleStep:
+ case ArtJvmtiEvent::kFramePop:
+ return true;
+ default:
+ LOG(FATAL) << "Unexpected event type!";
+ UNREACHABLE();
+ }
+}
+
static void SetupTraceListener(JvmtiMethodTraceListener* listener,
ArtJvmtiEvent event,
bool enable) {
+ bool needs_full_deopt = EventNeedsFullDeopt(event);
+ // Make sure we can deopt.
+ {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ DeoptManager* deopt_manager = DeoptManager::Get();
+ if (enable) {
+ deopt_manager->AddDeoptimizationRequester();
+ if (needs_full_deopt) {
+ deopt_manager->AddDeoptimizeAllMethods();
+ }
+ } else {
+ if (needs_full_deopt) {
+ deopt_manager->RemoveDeoptimizeAllMethods();
+ }
+ deopt_manager->RemoveDeoptimizationRequester();
+ }
+ }
+
+ // Add the actual listeners.
art::ScopedThreadStateChange stsc(art::Thread::Current(), art::ThreadState::kNative);
uint32_t new_events = GetInstrumentationEventsFor(event);
art::instrumentation::Instrumentation* instr = art::Runtime::Current()->GetInstrumentation();
@@ -821,11 +862,6 @@
art::gc::kCollectorTypeInstrumentation);
art::ScopedSuspendAll ssa("jvmti method tracing installation");
if (enable) {
- // TODO Depending on the features being used we should be able to avoid deoptimizing everything
- // like we do here.
- if (!instr->AreAllMethodsDeoptimized()) {
- instr->EnableMethodTracing("jvmti-tracing", /*needs_interpreter*/true);
- }
instr->AddListener(listener, new_events);
} else {
instr->RemoveListener(listener, new_events);
@@ -910,6 +946,7 @@
}
// FramePop can never be disabled once it's been turned on since we would either need to deal
// with dangling pointers or have missed events.
+ // TODO We really need to make this not the case anymore.
case ArtJvmtiEvent::kFramePop:
if (!enable || (enable && frame_pop_enabled)) {
break;
@@ -1046,6 +1083,14 @@
return ERR(NONE);
}
+void EventHandler::HandleBreakpointEventsChanged(bool added) {
+ if (added) {
+ DeoptManager::Get()->AddDeoptimizationRequester();
+ } else {
+ DeoptManager::Get()->RemoveDeoptimizationRequester();
+ }
+}
+
void EventHandler::Shutdown() {
// Need to remove the method_trace_listener_ if it's there.
art::Thread* self = art::Thread::Current();
diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h
index a062e15..aed24e5 100644
--- a/openjdkjvmti/events.h
+++ b/openjdkjvmti/events.h
@@ -232,6 +232,7 @@
void HandleEventType(ArtJvmtiEvent event, bool enable);
void HandleLocalAccessCapabilityAdded();
+ void HandleBreakpointEventsChanged(bool enable);
bool OtherMonitorEventsEnabledAnywhere(ArtJvmtiEvent event);
diff --git a/openjdkjvmti/ti_breakpoint.cc b/openjdkjvmti/ti_breakpoint.cc
index 9c3687f..8e5b56e 100644
--- a/openjdkjvmti/ti_breakpoint.cc
+++ b/openjdkjvmti/ti_breakpoint.cc
@@ -37,6 +37,7 @@
#include "art_method-inl.h"
#include "base/enums.h"
#include "base/mutex-inl.h"
+#include "deopt_manager.h"
#include "dex_file_annotations.h"
#include "events-inl.h"
#include "jni_internal.h"
@@ -63,18 +64,30 @@
}
void BreakpointUtil::RemoveBreakpointsInClass(ArtJvmTiEnv* env, art::mirror::Class* klass) {
- art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_);
std::vector<Breakpoint> to_remove;
- for (const Breakpoint& b : env->breakpoints) {
- if (b.GetMethod()->GetDeclaringClass() == klass) {
- to_remove.push_back(b);
+ {
+ art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_);
+ for (const Breakpoint& b : env->breakpoints) {
+ if (b.GetMethod()->GetDeclaringClass() == klass) {
+ to_remove.push_back(b);
+ }
+ }
+ for (const Breakpoint& b : to_remove) {
+ auto it = env->breakpoints.find(b);
+ DCHECK(it != env->breakpoints.end());
+ env->breakpoints.erase(it);
}
}
- for (const Breakpoint& b : to_remove) {
- auto it = env->breakpoints.find(b);
- DCHECK(it != env->breakpoints.end());
- env->breakpoints.erase(it);
+ if (!to_remove.empty()) {
+ LOG(WARNING) << "Methods with breakpoints potentially not being un-deoptimized.";
}
+ // TODO Figure out how to do this.
+ // DeoptManager* deopt = DeoptManager::Get();
+ // for (const Breakpoint& b : to_remove) {
+ // // TODO It might be good to send these all at once instead.
+ // // deopt->RemoveMethodBreakpointSuspended(b.GetMethod());
+ // LOG(WARNING) << "not un-deopting methods! :-0";
+ // }
}
jvmtiError BreakpointUtil::SetBreakpoint(jvmtiEnv* jenv, jmethodID method, jlocation location) {
@@ -82,20 +95,23 @@
if (method == nullptr) {
return ERR(INVALID_METHODID);
}
- // Need to get mutator_lock_ so we can find the interface version of any default methods.
art::ScopedObjectAccess soa(art::Thread::Current());
art::ArtMethod* art_method = art::jni::DecodeArtMethod(method)->GetCanonicalMethod();
- art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_);
if (location < 0 || static_cast<uint32_t>(location) >=
art_method->GetCodeItem()->insns_size_in_code_units_) {
return ERR(INVALID_LOCATION);
}
- auto res_pair = env->breakpoints.insert(/* Breakpoint */ {art_method, location});
- if (!res_pair.second) {
- // Didn't get inserted because it's already present!
- return ERR(DUPLICATE);
+ DeoptManager::Get()->AddMethodBreakpoint(art_method);
+ {
+ art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_);
+ auto res_pair = env->breakpoints.insert(/* Breakpoint */ {art_method, location});
+ if (LIKELY(res_pair.second)) {
+ return OK;
+ }
}
- return OK;
+ // Didn't get inserted because it's already present!
+ DeoptManager::Get()->RemoveMethodBreakpoint(art_method);
+ return ERR(DUPLICATE);
}
jvmtiError BreakpointUtil::ClearBreakpoint(jvmtiEnv* jenv, jmethodID method, jlocation location) {
@@ -103,15 +119,17 @@
if (method == nullptr) {
return ERR(INVALID_METHODID);
}
- // Need to get mutator_lock_ so we can find the interface version of any default methods.
art::ScopedObjectAccess soa(art::Thread::Current());
art::ArtMethod* art_method = art::jni::DecodeArtMethod(method)->GetCanonicalMethod();
- art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_);
- auto pos = env->breakpoints.find(/* Breakpoint */ {art_method, location});
- if (pos == env->breakpoints.end()) {
- return ERR(NOT_FOUND);
+ {
+ art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_);
+ auto pos = env->breakpoints.find(/* Breakpoint */ {art_method, location});
+ if (pos == env->breakpoints.end()) {
+ return ERR(NOT_FOUND);
+ }
+ env->breakpoints.erase(pos);
}
- env->breakpoints.erase(pos);
+ DeoptManager::Get()->RemoveMethodBreakpoint(art_method);
return OK;
}
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index 41679da..5d63285 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -86,21 +86,6 @@
TiMethodCallback gMethodCallback;
-// TODO We should make this much more selective in the future so we only return true when we
-// actually care about the method (i.e. had locals changed, have breakpoints, etc.). For now though
-// we can just assume that we care we are loaded at all.
-//
-// Even if we don't keep track of this at the method level we might want to keep track of it at the
-// level of enabled capabilities.
-struct TiMethodInspectionCallback : public art::MethodInspectionCallback {
- bool IsMethodBeingInspected(art::ArtMethod* method ATTRIBUTE_UNUSED)
- OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
- return true;
- }
-};
-
-TiMethodInspectionCallback gMethodInspectionCallback;
-
void MethodUtil::Register(EventHandler* handler) {
gMethodCallback.event_handler = handler;
art::ScopedThreadStateChange stsc(art::Thread::Current(),
@@ -108,7 +93,6 @@
art::ScopedSuspendAll ssa("Add method callback");
art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks();
callbacks->AddMethodCallback(&gMethodCallback);
- callbacks->AddMethodInspectionCallback(&gMethodInspectionCallback);
}
void MethodUtil::Unregister() {
@@ -117,7 +101,6 @@
art::ScopedSuspendAll ssa("Remove method callback");
art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks();
callbacks->RemoveMethodCallback(&gMethodCallback);
- callbacks->AddMethodInspectionCallback(&gMethodInspectionCallback);
}
jvmtiError MethodUtil::GetBytecodes(jvmtiEnv* env,
diff --git a/runtime/Android.bp b/runtime/Android.bp
index a615437..ff5ae9f 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -35,6 +35,7 @@
"base/arena_bit_vector.cc",
"base/bit_vector.cc",
"base/file_magic.cc",
+ "base/file_utils.cc",
"base/hex_dump.cc",
"base/logging.cc",
"base/mutex.cc",
@@ -152,6 +153,7 @@
"mirror/stack_trace_element.cc",
"mirror/string.cc",
"mirror/throwable.cc",
+ "mirror/var_handle.cc",
"monitor.cc",
"native_bridge_art_interface.cc",
"native_stack_dump.cc",
@@ -203,6 +205,7 @@
"runtime.cc",
"runtime_callbacks.cc",
"runtime_common.cc",
+ "runtime_intrinsics.cc",
"runtime_options.cc",
"scoped_thread_state_change.cc",
"signal_catcher.cc",
@@ -402,7 +405,6 @@
export_generated_headers: ["cpp-define-generator-asm-support"],
include_dirs: [
"art/sigchainlib",
- "art",
],
header_libs: [
"art_cmdlineparser_headers",
@@ -598,6 +600,7 @@
"mirror/dex_cache_test.cc",
"mirror/method_type_test.cc",
"mirror/object_test.cc",
+ "mirror/var_handle_test.cc",
"monitor_pool_test.cc",
"monitor_test.cc",
"oat_file_test.cc",
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index adfc88f..280e593 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -2921,7 +2921,7 @@
INCREASE_FRAME 16 // Reserve space for JValue result.
str xzr, [sp, #0] // Initialize result to zero.
mov x0, sp // Set r0 to point to result.
- bl artInvokePolymorphic // ArtInvokePolymorphic(result, receiver, thread, save_area)
+ bl artInvokePolymorphic // artInvokePolymorphic(result, receiver, thread, save_area)
uxtb w0, w0 // Result is the return type descriptor as a char.
sub w0, w0, 'A' // Convert to zero based index.
cmp w0, 'Z' - 'A'
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index ee3f17d..489c52c 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -3228,7 +3228,7 @@
sw $zero, 20($sp) # Initialize JValue result.
sw $zero, 16($sp)
la $t9, artInvokePolymorphic
- jalr $t9 # (result, receiver, Thread*, context)
+ jalr $t9 # artInvokePolymorphic(result, receiver, Thread*, context)
addiu $a0, $sp, 16 # Make $a0 a pointer to the JValue result
.macro MATCH_RETURN_TYPE c, handler
li $t0, \c
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index d4ad275..98ffe65 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -3028,7 +3028,7 @@
daddiu $sp, $sp, -8 # Reserve space for JValue result.
.cfi_adjust_cfa_offset 8
sd $zero, 0($sp) # Initialize JValue result.
- jal artInvokePolymorphic # (result, receiver, Thread*, context)
+ jal artInvokePolymorphic # artInvokePolymorphic(result, receiver, Thread*, context)
move $a0, $sp # Make $a0 a pointer to the JValue result
.macro MATCH_RETURN_TYPE c, handler
li $t0, \c
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index eecca58..25716dc 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -2419,7 +2419,7 @@
CFI_ADJUST_CFA_OFFSET(4)
PUSH ecx // pass receiver (method handle)
PUSH eax // pass JResult
- call SYMBOL(artInvokePolymorphic) // (result, receiver, Thread*, SP)
+ call SYMBOL(artInvokePolymorphic) // artInvokePolymorphic(result, receiver, Thread*, SP)
subl LITERAL('A'), %eax // Eliminate out of bounds options
cmpb LITERAL('Z' - 'A'), %al
ja .Lcleanup_and_return
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index d4297df..80f5c34 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -166,6 +166,8 @@
return kInterface;
} else if (IsDirect()) {
return kDirect;
+ } else if (IsPolymorphicSignature()) {
+ return kPolymorphic;
} else {
return kVirtual;
}
@@ -427,6 +429,12 @@
/* lookup_in_resolved_boot_classes */ true);
}
+bool ArtMethod::IsAnnotatedWithPolymorphicSignature() {
+ return IsAnnotatedWith(WellKnownClasses::java_lang_invoke_MethodHandle_PolymorphicSignature,
+ DexFile::kDexVisibilityRuntime,
+ /* lookup_in_resolved_boot_classes */ true);
+}
+
bool ArtMethod::IsAnnotatedWith(jclass klass,
uint32_t visibility,
bool lookup_in_resolved_boot_classes) {
diff --git a/runtime/art_method.h b/runtime/art_method.h
index caef81c..fe85cb4 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -271,6 +271,12 @@
bool IsProxyMethod() REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsPolymorphicSignature() REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Methods with a polymorphic signature have constraints that they
+ // are native and varargs. Check these first before possibly expensive call.
+ return IsNative() && IsVarargs() && IsAnnotatedWithPolymorphicSignature();
+ }
+
bool SkipAccessChecks() {
return (GetAccessFlags() & kAccSkipAccessChecks) != 0;
}
@@ -316,6 +322,10 @@
// -- Unrelated to the GC notion of "critical".
bool IsAnnotatedWithCriticalNative();
+ // Checks to see if the method was annotated with
+ // @java.lang.invoke.MethodHandle.PolymorphicSignature.
+ bool IsAnnotatedWithPolymorphicSignature();
+
// Returns true if this method could be overridden by a default method.
bool IsOverridableByDefaultMethod() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/base/bit_struct.h b/runtime/base/bit_struct.h
index 1f86ee1..16b555e 100644
--- a/runtime/base/bit_struct.h
+++ b/runtime/base/bit_struct.h
@@ -130,6 +130,18 @@
return kBitWidth;
}
+ BitStructField& operator=(const BitStructField& other) {
+ // Warning. The default operator= will overwrite the entire storage!
+ return *this = static_cast<T>(other);
+ }
+
+ BitStructField(const BitStructField& other) {
+ Assign(*this, static_cast<T>(other));
+ }
+
+ BitStructField() = default;
+ ~BitStructField() = default;
+
protected:
template <typename T2>
T2& Assign(T2& what, T value) {
@@ -265,7 +277,11 @@
#define BITSTRUCT_DEFINE_START(name, bitwidth) \
union name { \
art::detail::DefineBitStructSize<(bitwidth)> _; \
- static constexpr size_t BitStructSizeOf() { return (bitwidth); }
+ static constexpr size_t BitStructSizeOf() { return (bitwidth); } \
+ name& operator=(const name& other) { _ = other._; return *this; } \
+ name(const name& other) : _(other._) {} \
+ name() = default; \
+ ~name() = default;
// End the definition of a bitstruct, and insert a sanity check
// to ensure that the bitstruct did not exceed the specified size.
diff --git a/runtime/base/bit_struct_test.cc b/runtime/base/bit_struct_test.cc
index 9fc9762..a80d39e 100644
--- a/runtime/base/bit_struct_test.cc
+++ b/runtime/base/bit_struct_test.cc
@@ -255,4 +255,68 @@
EXPECT_EQ(0b10101010101010101011111010100111u, AsUint(tst));
}
+BITSTRUCT_DEFINE_START(TestBitStruct_u8, /* size */ 8)
+ BitStructInt</*lsb*/0, /*width*/3> i3;
+ BitStructUint</*lsb*/3, /*width*/4> u4;
+
+ BitStructUint</*lsb*/0, /*width*/8> alias_all;
+BITSTRUCT_DEFINE_END(TestBitStruct_u8);
+
+TEST(BitStructs, FieldAssignment) {
+ TestBitStruct_u8 all_1s{}; // NOLINT
+ all_1s.alias_all = 0xffu;
+
+ {
+ TestBitStruct_u8 tst{}; // NOLINT
+ tst.i3 = all_1s.i3;
+
+ // Copying a single bitfield does not copy all bitfields.
+ EXPECT_EQ(0b111, tst.alias_all);
+ }
+
+ {
+ TestBitStruct_u8 tst{}; // NOLINT
+ tst.u4 = all_1s.u4;
+
+ // Copying a single bitfield does not copy all bitfields.
+ EXPECT_EQ(0b1111000, tst.alias_all);
+ }
+}
+
+BITSTRUCT_DEFINE_START(NestedStruct, /* size */ 64)
+ BitStructField<MixedSizeBitStruct, /*lsb*/0> mixed_lower;
+ BitStructField<MixedSizeBitStruct, /*lsb*/32> mixed_upper;
+
+ BitStructUint</*lsb*/0, /*width*/64> alias_all;
+BITSTRUCT_DEFINE_END(NestedStruct);
+
+TEST(BitStructs, NestedFieldAssignment) {
+ MixedSizeBitStruct mixed_all_1s{}; // NOLINT
+ mixed_all_1s.alias_all = 0xFFFFFFFFu;
+
+ {
+ NestedStruct xyz{}; // NOLINT
+
+ NestedStruct other{}; // NOLINT
+ other.mixed_upper = mixed_all_1s;
+ other.mixed_lower = mixed_all_1s;
+
+ // Copying a single bitfield does not copy all bitfields.
+ xyz.mixed_lower = other.mixed_lower;
+ EXPECT_EQ(0xFFFFFFFFu, xyz.alias_all);
+ }
+
+ {
+ NestedStruct xyz{}; // NOLINT
+
+ NestedStruct other{}; // NOLINT
+ other.mixed_upper = mixed_all_1s;
+ other.mixed_lower = mixed_all_1s;
+
+ // Copying a single bitfield does not copy all bitfields.
+ xyz.mixed_upper = other.mixed_upper;
+ EXPECT_EQ(0xFFFFFFFF00000000u, xyz.alias_all);
+ }
+}
+
} // namespace art
diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc
new file mode 100644
index 0000000..323a065
--- /dev/null
+++ b/runtime/base/file_utils.cc
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2011 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 "file_utils.h"
+
+#include <inttypes.h>
+#include <pthread.h>
+#include <sys/mman.h> // For madvise
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+// We need dladdr.
+#ifndef __APPLE__
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#define DEFINED_GNU_SOURCE
+#endif
+#include <dlfcn.h>
+#include <libgen.h>
+#ifdef DEFINED_GNU_SOURCE
+#undef _GNU_SOURCE
+#undef DEFINED_GNU_SOURCE
+#endif
+#endif
+
+
+#include <memory>
+
+#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
+
+#include "base/stl_util.h"
+#include "base/unix_file/fd_file.h"
+#include "dex_file-inl.h"
+#include "dex_file_loader.h"
+#include "dex_instruction.h"
+#include "oat_quick_method_header.h"
+#include "os.h"
+#include "scoped_thread_state_change-inl.h"
+#include "utf-inl.h"
+
+#if defined(__APPLE__)
+#include <crt_externs.h>
+#include <sys/syscall.h>
+#include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED
+#endif
+
+#if defined(__linux__)
+#include <linux/unistd.h>
+#endif
+
+namespace art {
+
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
+bool ReadFileToString(const std::string& file_name, std::string* result) {
+ File file(file_name, O_RDONLY, false);
+ if (!file.IsOpened()) {
+ return false;
+ }
+
+ std::vector<char> buf(8 * KB);
+ while (true) {
+ int64_t n = TEMP_FAILURE_RETRY(read(file.Fd(), &buf[0], buf.size()));
+ if (n == -1) {
+ return false;
+ }
+ if (n == 0) {
+ return true;
+ }
+ result->append(&buf[0], n);
+ }
+}
+
+bool PrintFileToLog(const std::string& file_name, LogSeverity level) {
+ File file(file_name, O_RDONLY, false);
+ if (!file.IsOpened()) {
+ return false;
+ }
+
+ constexpr size_t kBufSize = 256; // Small buffer. Avoid stack overflow and stack size warnings.
+ char buf[kBufSize + 1]; // +1 for terminator.
+ size_t filled_to = 0;
+ while (true) {
+ DCHECK_LT(filled_to, kBufSize);
+ int64_t n = TEMP_FAILURE_RETRY(read(file.Fd(), &buf[filled_to], kBufSize - filled_to));
+ if (n <= 0) {
+ // Print the rest of the buffer, if it exists.
+ if (filled_to > 0) {
+ buf[filled_to] = 0;
+ LOG(level) << buf;
+ }
+ return n == 0;
+ }
+ // Scan for '\n'.
+ size_t i = filled_to;
+ bool found_newline = false;
+ for (; i < filled_to + n; ++i) {
+ if (buf[i] == '\n') {
+ // Found a line break, that's something to print now.
+ buf[i] = 0;
+ LOG(level) << buf;
+ // Copy the rest to the front.
+ if (i + 1 < filled_to + n) {
+ memmove(&buf[0], &buf[i + 1], filled_to + n - i - 1);
+ filled_to = filled_to + n - i - 1;
+ } else {
+ filled_to = 0;
+ }
+ found_newline = true;
+ break;
+ }
+ }
+ if (found_newline) {
+ continue;
+ } else {
+ filled_to += n;
+ // Check if we must flush now.
+ if (filled_to == kBufSize) {
+ buf[kBufSize] = 0;
+ LOG(level) << buf;
+ filled_to = 0;
+ }
+ }
+ }
+}
+
+std::string GetAndroidRootSafe(std::string* error_msg) {
+ // Prefer ANDROID_ROOT if it's set.
+ const char* android_dir = getenv("ANDROID_ROOT");
+ if (android_dir != nullptr) {
+ if (!OS::DirectoryExists(android_dir)) {
+ *error_msg = StringPrintf("Failed to find ANDROID_ROOT directory %s", android_dir);
+ return "";
+ }
+ return android_dir;
+ }
+
+ // Check where libart is from, and derive from there. Only do this for non-Mac.
+#ifndef __APPLE__
+ {
+ Dl_info info;
+ if (dladdr(reinterpret_cast<const void*>(&GetAndroidRootSafe), /* out */ &info) != 0) {
+ // Make a duplicate of the fname so dirname can modify it.
+ UniqueCPtr<char> fname(strdup(info.dli_fname));
+
+ char* dir1 = dirname(fname.get()); // This is the lib directory.
+ char* dir2 = dirname(dir1); // This is the "system" directory.
+ if (OS::DirectoryExists(dir2)) {
+ std::string tmp = dir2; // Make a copy here so that fname can be released.
+ return tmp;
+ }
+ }
+ }
+#endif
+
+ // Try "/system".
+ if (!OS::DirectoryExists("/system")) {
+ *error_msg = "Failed to find ANDROID_ROOT directory /system";
+ return "";
+ }
+ return "/system";
+}
+
+std::string GetAndroidRoot() {
+ std::string error_msg;
+ std::string ret = GetAndroidRootSafe(&error_msg);
+ if (ret.empty()) {
+ LOG(FATAL) << error_msg;
+ UNREACHABLE();
+ }
+ return ret;
+}
+
+
+static const char* GetAndroidDirSafe(const char* env_var,
+ const char* default_dir,
+ std::string* error_msg) {
+ const char* android_dir = getenv(env_var);
+ if (android_dir == nullptr) {
+ if (OS::DirectoryExists(default_dir)) {
+ android_dir = default_dir;
+ } else {
+ *error_msg = StringPrintf("%s not set and %s does not exist", env_var, default_dir);
+ return nullptr;
+ }
+ }
+ if (!OS::DirectoryExists(android_dir)) {
+ *error_msg = StringPrintf("Failed to find %s directory %s", env_var, android_dir);
+ return nullptr;
+ }
+ return android_dir;
+}
+
+static const char* GetAndroidDir(const char* env_var, const char* default_dir) {
+ std::string error_msg;
+ const char* dir = GetAndroidDirSafe(env_var, default_dir, &error_msg);
+ if (dir != nullptr) {
+ return dir;
+ } else {
+ LOG(FATAL) << error_msg;
+ return nullptr;
+ }
+}
+
+const char* GetAndroidData() {
+ return GetAndroidDir("ANDROID_DATA", "/data");
+}
+
+const char* GetAndroidDataSafe(std::string* error_msg) {
+ return GetAndroidDirSafe("ANDROID_DATA", "/data", error_msg);
+}
+
+std::string GetDefaultBootImageLocation(std::string* error_msg) {
+ std::string android_root = GetAndroidRootSafe(error_msg);
+ if (android_root.empty()) {
+ return "";
+ }
+ return StringPrintf("%s/framework/boot.art", android_root.c_str());
+}
+
+void GetDalvikCache(const char* subdir, const bool create_if_absent, std::string* dalvik_cache,
+ bool* have_android_data, bool* dalvik_cache_exists, bool* is_global_cache) {
+ CHECK(subdir != nullptr);
+ std::string error_msg;
+ const char* android_data = GetAndroidDataSafe(&error_msg);
+ if (android_data == nullptr) {
+ *have_android_data = false;
+ *dalvik_cache_exists = false;
+ *is_global_cache = false;
+ return;
+ } else {
+ *have_android_data = true;
+ }
+ const std::string dalvik_cache_root(StringPrintf("%s/dalvik-cache/", android_data));
+ *dalvik_cache = dalvik_cache_root + subdir;
+ *dalvik_cache_exists = OS::DirectoryExists(dalvik_cache->c_str());
+ *is_global_cache = strcmp(android_data, "/data") == 0;
+ if (create_if_absent && !*dalvik_cache_exists && !*is_global_cache) {
+ // Don't create the system's /data/dalvik-cache/... because it needs special permissions.
+ *dalvik_cache_exists = ((mkdir(dalvik_cache_root.c_str(), 0700) == 0 || errno == EEXIST) &&
+ (mkdir(dalvik_cache->c_str(), 0700) == 0 || errno == EEXIST));
+ }
+}
+
+std::string GetDalvikCache(const char* subdir) {
+ CHECK(subdir != nullptr);
+ const char* android_data = GetAndroidData();
+ const std::string dalvik_cache_root(StringPrintf("%s/dalvik-cache/", android_data));
+ const std::string dalvik_cache = dalvik_cache_root + subdir;
+ if (!OS::DirectoryExists(dalvik_cache.c_str())) {
+ // TODO: Check callers. Traditional behavior is to not abort.
+ return "";
+ }
+ return dalvik_cache;
+}
+
+bool GetDalvikCacheFilename(const char* location, const char* cache_location,
+ std::string* filename, std::string* error_msg) {
+ if (location[0] != '/') {
+ *error_msg = StringPrintf("Expected path in location to be absolute: %s", location);
+ return false;
+ }
+ std::string cache_file(&location[1]); // skip leading slash
+ if (!android::base::EndsWith(location, ".dex") &&
+ !android::base::EndsWith(location, ".art") &&
+ !android::base::EndsWith(location, ".oat")) {
+ cache_file += "/";
+ cache_file += DexFileLoader::kClassesDex;
+ }
+ std::replace(cache_file.begin(), cache_file.end(), '/', '@');
+ *filename = StringPrintf("%s/%s", cache_location, cache_file.c_str());
+ return true;
+}
+
+std::string GetVdexFilename(const std::string& oat_location) {
+ return ReplaceFileExtension(oat_location, "vdex");
+}
+
+static void InsertIsaDirectory(const InstructionSet isa, std::string* filename) {
+ // in = /foo/bar/baz
+ // out = /foo/bar/<isa>/baz
+ size_t pos = filename->rfind('/');
+ CHECK_NE(pos, std::string::npos) << *filename << " " << isa;
+ filename->insert(pos, "/", 1);
+ filename->insert(pos + 1, GetInstructionSetString(isa));
+}
+
+std::string GetSystemImageFilename(const char* location, const InstructionSet isa) {
+ // location = /system/framework/boot.art
+ // filename = /system/framework/<isa>/boot.art
+ std::string filename(location);
+ InsertIsaDirectory(isa, &filename);
+ return filename;
+}
+
+bool FileExists(const std::string& filename) {
+ struct stat buffer;
+ return stat(filename.c_str(), &buffer) == 0;
+}
+
+bool FileExistsAndNotEmpty(const std::string& filename) {
+ struct stat buffer;
+ if (stat(filename.c_str(), &buffer) != 0) {
+ return false;
+ }
+ return buffer.st_size > 0;
+}
+
+std::string ReplaceFileExtension(const std::string& filename, const std::string& new_extension) {
+ const size_t last_ext = filename.find_last_of('.');
+ if (last_ext == std::string::npos) {
+ return filename + "." + new_extension;
+ } else {
+ return filename.substr(0, last_ext + 1) + new_extension;
+ }
+}
+
+int64_t GetFileSizeBytes(const std::string& filename) {
+ struct stat stat_buf;
+ int rc = stat(filename.c_str(), &stat_buf);
+ return rc == 0 ? stat_buf.st_size : -1;
+}
+
+int MadviseLargestPageAlignedRegion(const uint8_t* begin, const uint8_t* end, int advice) {
+ DCHECK_LE(begin, end);
+ begin = AlignUp(begin, kPageSize);
+ end = AlignDown(end, kPageSize);
+ if (begin < end) {
+ int result = madvise(const_cast<uint8_t*>(begin), end - begin, advice);
+ if (result != 0) {
+ PLOG(WARNING) << "madvise failed " << result;
+ }
+ return result;
+ }
+ return 0;
+}
+
+} // namespace art
diff --git a/runtime/base/file_utils.h b/runtime/base/file_utils.h
new file mode 100644
index 0000000..007f3b4
--- /dev/null
+++ b/runtime/base/file_utils.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef ART_RUNTIME_BASE_FILE_UTILS_H_
+#define ART_RUNTIME_BASE_FILE_UTILS_H_
+
+#include <stdlib.h>
+
+#include <string>
+
+#include "arch/instruction_set.h"
+#include "base/logging.h"
+
+namespace art {
+
+bool ReadFileToString(const std::string& file_name, std::string* result);
+bool PrintFileToLog(const std::string& file_name, LogSeverity level);
+
+// Find $ANDROID_ROOT, /system, or abort.
+std::string GetAndroidRoot();
+// Find $ANDROID_ROOT, /system, or return an empty string.
+std::string GetAndroidRootSafe(std::string* error_msg);
+
+// Find $ANDROID_DATA, /data, or abort.
+const char* GetAndroidData();
+// Find $ANDROID_DATA, /data, or return null.
+const char* GetAndroidDataSafe(std::string* error_msg);
+
+// Returns the default boot image location (ANDROID_ROOT/framework/boot.art).
+// Returns an empty string if ANDROID_ROOT is not set.
+std::string GetDefaultBootImageLocation(std::string* error_msg);
+
+// Returns the dalvik-cache location, with subdir appended. Returns the empty string if the cache
+// could not be found.
+std::string GetDalvikCache(const char* subdir);
+// Return true if we found the dalvik cache and stored it in the dalvik_cache argument.
+// have_android_data will be set to true if we have an ANDROID_DATA that exists,
+// dalvik_cache_exists will be true if there is a dalvik-cache directory that is present.
+// The flag is_global_cache tells whether this cache is /data/dalvik-cache.
+void GetDalvikCache(const char* subdir, bool create_if_absent, std::string* dalvik_cache,
+ bool* have_android_data, bool* dalvik_cache_exists, bool* is_global_cache);
+
+// Returns the absolute dalvik-cache path for a DexFile or OatFile. The path returned will be
+// rooted at cache_location.
+bool GetDalvikCacheFilename(const char* file_location, const char* cache_location,
+ std::string* filename, std::string* error_msg);
+
+// Returns the system location for an image
+std::string GetSystemImageFilename(const char* location, InstructionSet isa);
+
+// Returns the vdex filename for the given oat filename.
+std::string GetVdexFilename(const std::string& oat_filename);
+
+// Returns true if the file exists.
+bool FileExists(const std::string& filename);
+bool FileExistsAndNotEmpty(const std::string& filename);
+
+// Returns `filename` with the text after the last occurrence of '.' replaced with
+// `extension`. If `filename` does not contain a period, returns a string containing `filename`,
+// a period, and `new_extension`.
+// Example: ReplaceFileExtension("foo.bar", "abc") == "foo.abc"
+// ReplaceFileExtension("foo", "abc") == "foo.abc"
+std::string ReplaceFileExtension(const std::string& filename, const std::string& new_extension);
+
+// Return the file size in bytes or -1 if the file does not exists.
+int64_t GetFileSizeBytes(const std::string& filename);
+
+// Madvise the largest page aligned region within begin and end.
+int MadviseLargestPageAlignedRegion(const uint8_t* begin, const uint8_t* end, int advice);
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_FILE_UTILS_H_
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 189c0d0..4b56d3b 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -664,7 +664,7 @@
// When declaring any Mutex add DEFAULT_MUTEX_ACQUIRED_AFTER to use annotalysis to check the code
// doesn't try to hold a higher level Mutex.
- #define DEFAULT_MUTEX_ACQUIRED_AFTER ACQUIRED_AFTER(Locks::classlinker_classes_lock_)
+ #define DEFAULT_MUTEX_ACQUIRED_AFTER ACQUIRED_AFTER(art::Locks::classlinker_classes_lock_)
static Mutex* allocated_monitor_ids_lock_ ACQUIRED_AFTER(classlinker_classes_lock_);
diff --git a/runtime/base/safe_copy.cc b/runtime/base/safe_copy.cc
index c76ea11..b46b921 100644
--- a/runtime/base/safe_copy.cc
+++ b/runtime/base/safe_copy.cc
@@ -24,7 +24,7 @@
#include <android-base/macros.h>
-#include "runtime/base/bit_utils.h"
+#include "bit_utils.h"
namespace art {
diff --git a/runtime/cdex/compact_dex_file.h b/runtime/cdex/compact_dex_file.h
index 910473b..3c1b638 100644
--- a/runtime/cdex/compact_dex_file.h
+++ b/runtime/cdex/compact_dex_file.h
@@ -51,8 +51,9 @@
size_t size,
const std::string& location,
uint32_t location_checksum,
- const OatDexFile* oat_dex_file)
- : DexFile(base, size, location, location_checksum, oat_dex_file) {}
+ const OatDexFile* oat_dex_file,
+ DexFileContainer* container)
+ : DexFile(base, size, location, location_checksum, oat_dex_file, container) {}
friend class DexFile;
friend class DexFileLoader;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index b199933..5435c11 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -98,6 +98,7 @@
#include "mirror/reference-inl.h"
#include "mirror/stack_trace_element.h"
#include "mirror/string-inl.h"
+#include "mirror/var_handle.h"
#include "native/dalvik_system_DexFile.h"
#include "nativehelper/scoped_local_ref.h"
#include "oat.h"
@@ -698,6 +699,12 @@
SetClassRoot(kJavaLangReflectMethodArrayClass, class_root);
mirror::Method::SetArrayClass(class_root);
+ // Create java.lang.invoke.CallSite.class root
+ class_root = FindSystemClass(self, "Ljava/lang/invoke/CallSite;");
+ CHECK(class_root != nullptr);
+ SetClassRoot(kJavaLangInvokeCallSite, class_root);
+ mirror::CallSite::SetClass(class_root);
+
// Create java.lang.invoke.MethodType.class root
class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodType;");
CHECK(class_root != nullptr);
@@ -716,11 +723,35 @@
SetClassRoot(kJavaLangInvokeMethodHandlesLookup, class_root);
mirror::MethodHandlesLookup::SetClass(class_root);
- // Create java.lang.invoke.CallSite.class root
- class_root = FindSystemClass(self, "Ljava/lang/invoke/CallSite;");
+ // Create java.lang.invoke.VarHandle.class root
+ class_root = FindSystemClass(self, "Ljava/lang/invoke/VarHandle;");
CHECK(class_root != nullptr);
- SetClassRoot(kJavaLangInvokeCallSite, class_root);
- mirror::CallSite::SetClass(class_root);
+ SetClassRoot(kJavaLangInvokeVarHandle, class_root);
+ mirror::VarHandle::SetClass(class_root);
+
+ // Create java.lang.invoke.FieldVarHandle.class root
+ class_root = FindSystemClass(self, "Ljava/lang/invoke/FieldVarHandle;");
+ CHECK(class_root != nullptr);
+ SetClassRoot(kJavaLangInvokeFieldVarHandle, class_root);
+ mirror::FieldVarHandle::SetClass(class_root);
+
+ // Create java.lang.invoke.ArrayElementVarHandle.class root
+ class_root = FindSystemClass(self, "Ljava/lang/invoke/ArrayElementVarHandle;");
+ CHECK(class_root != nullptr);
+ SetClassRoot(kJavaLangInvokeArrayElementVarHandle, class_root);
+ mirror::ArrayElementVarHandle::SetClass(class_root);
+
+ // Create java.lang.invoke.ByteArrayViewVarHandle.class root
+ class_root = FindSystemClass(self, "Ljava/lang/invoke/ByteArrayViewVarHandle;");
+ CHECK(class_root != nullptr);
+ SetClassRoot(kJavaLangInvokeByteArrayViewVarHandle, class_root);
+ mirror::ByteArrayViewVarHandle::SetClass(class_root);
+
+ // Create java.lang.invoke.ByteBufferViewVarHandle.class root
+ class_root = FindSystemClass(self, "Ljava/lang/invoke/ByteBufferViewVarHandle;");
+ CHECK(class_root != nullptr);
+ SetClassRoot(kJavaLangInvokeByteBufferViewVarHandle, class_root);
+ mirror::ByteBufferViewVarHandle::SetClass(class_root);
class_root = FindSystemClass(self, "Ldalvik/system/EmulatedStackFrame;");
CHECK(class_root != nullptr);
@@ -988,10 +1019,15 @@
mirror::Constructor::SetArrayClass(GetClassRoot(kJavaLangReflectConstructorArrayClass));
mirror::Method::SetClass(GetClassRoot(kJavaLangReflectMethod));
mirror::Method::SetArrayClass(GetClassRoot(kJavaLangReflectMethodArrayClass));
- mirror::MethodType::SetClass(GetClassRoot(kJavaLangInvokeMethodType));
+ mirror::CallSite::SetClass(GetClassRoot(kJavaLangInvokeCallSite));
mirror::MethodHandleImpl::SetClass(GetClassRoot(kJavaLangInvokeMethodHandleImpl));
mirror::MethodHandlesLookup::SetClass(GetClassRoot(kJavaLangInvokeMethodHandlesLookup));
- mirror::CallSite::SetClass(GetClassRoot(kJavaLangInvokeCallSite));
+ mirror::MethodType::SetClass(GetClassRoot(kJavaLangInvokeMethodType));
+ mirror::VarHandle::SetClass(GetClassRoot(kJavaLangInvokeVarHandle));
+ mirror::FieldVarHandle::SetClass(GetClassRoot(kJavaLangInvokeFieldVarHandle));
+ mirror::ArrayElementVarHandle::SetClass(GetClassRoot(kJavaLangInvokeArrayElementVarHandle));
+ mirror::ByteArrayViewVarHandle::SetClass(GetClassRoot(kJavaLangInvokeByteArrayViewVarHandle));
+ mirror::ByteBufferViewVarHandle::SetClass(GetClassRoot(kJavaLangInvokeByteBufferViewVarHandle));
mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference));
mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass));
mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass));
@@ -2083,10 +2119,15 @@
mirror::IntArray::ResetArrayClass();
mirror::LongArray::ResetArrayClass();
mirror::ShortArray::ResetArrayClass();
+ mirror::CallSite::ResetClass();
mirror::MethodType::ResetClass();
mirror::MethodHandleImpl::ResetClass();
mirror::MethodHandlesLookup::ResetClass();
- mirror::CallSite::ResetClass();
+ mirror::VarHandle::ResetClass();
+ mirror::FieldVarHandle::ResetClass();
+ mirror::ArrayElementVarHandle::ResetClass();
+ mirror::ByteArrayViewVarHandle::ResetClass();
+ mirror::ByteBufferViewVarHandle::ResetClass();
mirror::EmulatedStackFrame::ResetClass();
Thread* const self = Thread::Current();
for (const ClassLoaderData& data : class_loaders_) {
@@ -8484,6 +8525,11 @@
"Ljava/lang/invoke/MethodHandleImpl;",
"Ljava/lang/invoke/MethodHandles$Lookup;",
"Ljava/lang/invoke/MethodType;",
+ "Ljava/lang/invoke/VarHandle;",
+ "Ljava/lang/invoke/FieldVarHandle;",
+ "Ljava/lang/invoke/ArrayElementVarHandle;",
+ "Ljava/lang/invoke/ByteArrayViewVarHandle;",
+ "Ljava/lang/invoke/ByteBufferViewVarHandle;",
"Ljava/lang/ClassLoader;",
"Ljava/lang/Throwable;",
"Ljava/lang/ClassNotFoundException;",
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index eba2022..2d9ec5a 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -120,6 +120,11 @@
kJavaLangInvokeMethodHandleImpl,
kJavaLangInvokeMethodHandlesLookup,
kJavaLangInvokeMethodType,
+ kJavaLangInvokeVarHandle,
+ kJavaLangInvokeFieldVarHandle,
+ kJavaLangInvokeArrayElementVarHandle,
+ kJavaLangInvokeByteArrayViewVarHandle,
+ kJavaLangInvokeByteBufferViewVarHandle,
kJavaLangClassLoader,
kJavaLangThrowable,
kJavaLangClassNotFoundException,
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index bd73692..4d92826 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -48,6 +48,7 @@
#include "mirror/reference.h"
#include "mirror/stack_trace_element.h"
#include "mirror/string-inl.h"
+#include "mirror/var_handle.h"
#include "scoped_thread_state_change-inl.h"
#include "standard_dex_file.h"
#include "thread-current-inl.h"
@@ -777,6 +778,39 @@
}
};
+struct VarHandleOffsets : public CheckOffsets<mirror::VarHandle> {
+ VarHandleOffsets() : CheckOffsets<mirror::VarHandle>(
+ false, "Ljava/lang/invoke/VarHandle;") {
+ addOffset(OFFSETOF_MEMBER(mirror::VarHandle, access_modes_bit_mask_), "accessModesBitMask");
+ addOffset(OFFSETOF_MEMBER(mirror::VarHandle, coordinate_type0_), "coordinateType0");
+ addOffset(OFFSETOF_MEMBER(mirror::VarHandle, coordinate_type1_), "coordinateType1");
+ addOffset(OFFSETOF_MEMBER(mirror::VarHandle, var_type_), "varType");
+ }
+};
+
+struct FieldVarHandleOffsets : public CheckOffsets<mirror::FieldVarHandle> {
+ FieldVarHandleOffsets() : CheckOffsets<mirror::FieldVarHandle>(
+ false, "Ljava/lang/invoke/FieldVarHandle;") {
+ addOffset(OFFSETOF_MEMBER(mirror::FieldVarHandle, art_field_), "artField");
+ }
+};
+
+struct ByteArrayViewVarHandleOffsets : public CheckOffsets<mirror::ByteArrayViewVarHandle> {
+ ByteArrayViewVarHandleOffsets() : CheckOffsets<mirror::ByteArrayViewVarHandle>(
+ false, "Ljava/lang/invoke/ByteArrayViewVarHandle;") {
+ addOffset(OFFSETOF_MEMBER(mirror::ByteArrayViewVarHandle, native_byte_order_),
+ "nativeByteOrder");
+ }
+};
+
+struct ByteBufferViewVarHandleOffsets : public CheckOffsets<mirror::ByteBufferViewVarHandle> {
+ ByteBufferViewVarHandleOffsets() : CheckOffsets<mirror::ByteBufferViewVarHandle>(
+ false, "Ljava/lang/invoke/ByteBufferViewVarHandle;") {
+ addOffset(OFFSETOF_MEMBER(mirror::ByteBufferViewVarHandle, native_byte_order_),
+ "nativeByteOrder");
+ }
+};
+
// C++ fields must exactly match the fields in the Java classes. If this fails,
// reorder the fields in the C++ class. Managed class fields are ordered by
// ClassLinker::LinkFields.
@@ -802,6 +836,10 @@
EXPECT_TRUE(MethodHandlesLookupOffsets().Check());
EXPECT_TRUE(EmulatedStackFrameOffsets().Check());
EXPECT_TRUE(CallSiteOffsets().Check());
+ EXPECT_TRUE(VarHandleOffsets().Check());
+ EXPECT_TRUE(FieldVarHandleOffsets().Check());
+ EXPECT_TRUE(ByteArrayViewVarHandleOffsets().Check());
+ EXPECT_TRUE(ByteBufferViewVarHandleOffsets().Check());
}
TEST_F(ClassLinkerTest, FindClassNonexistent) {
@@ -1466,6 +1504,7 @@
old_dex_file->Size(),
location->ToModifiedUtf8(),
0u,
+ nullptr,
nullptr));
{
WriterMutexLock mu(soa.Self(), *Locks::dex_lock_);
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 149c33f..f15acf9 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -27,6 +27,7 @@
#include "android-base/stringprintf.h"
#include "art_field-inl.h"
+#include "base/file_utils.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/stl_util.h"
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 85df14a..8898afe 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -346,6 +346,10 @@
return Dbg::IsDebuggerActive();
}
+bool DebuggerActiveMethodInspectionCallback::IsMethodSafeToJit(ArtMethod* m) {
+ return !Dbg::MethodHasAnyBreakpoints(m);
+}
+
// Breakpoints.
static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_);
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 18126b1..ec37833 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -55,6 +55,7 @@
struct DebuggerActiveMethodInspectionCallback : public MethodInspectionCallback {
bool IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED)
OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsMethodSafeToJit(ArtMethod* m) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
};
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index 57cef3d..5f9b3cf 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -23,6 +23,7 @@
#include <gtest/gtest.h>
+#include "base/file_utils.h"
#include "base/stl_util.h"
#include "common_runtime_test.h"
#include "compiler_callbacks.h"
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index f2c43f7..974c7ac 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -16,13 +16,10 @@
#include "dex_file.h"
-#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/file.h>
-#include <sys/mman.h> // For the PROT_* and MAP_* constants.
#include <zlib.h>
#include <memory>
@@ -35,11 +32,7 @@
#include "base/logging.h"
#include "base/stl_util.h"
#include "dex_file-inl.h"
-#include "dex_file_loader.h"
-#include "jvalue.h"
#include "leb128.h"
-#include "mem_map.h"
-#include "os.h"
#include "standard_dex_file.h"
#include "utf-inl.h"
#include "utils.h"
@@ -59,46 +52,32 @@
return adler32(adler32(0L, Z_NULL, 0), non_sum_ptr, Size() - non_sum);
}
-struct DexFile::AnnotationValue {
- JValue value_;
- uint8_t type_;
-};
-
int DexFile::GetPermissions() const {
- if (mem_map_.get() == nullptr) {
- return 0;
- } else {
- return mem_map_->GetProtect();
- }
+ CHECK(container_.get() != nullptr);
+ return container_->GetPermissions();
}
bool DexFile::IsReadOnly() const {
- return GetPermissions() == PROT_READ;
+ CHECK(container_.get() != nullptr);
+ return container_->IsReadOnly();
}
bool DexFile::EnableWrite() const {
- CHECK(IsReadOnly());
- if (mem_map_.get() == nullptr) {
- return false;
- } else {
- return mem_map_->Protect(PROT_READ | PROT_WRITE);
- }
+ CHECK(container_.get() != nullptr);
+ return container_->EnableWrite();
}
bool DexFile::DisableWrite() const {
- CHECK(!IsReadOnly());
- if (mem_map_.get() == nullptr) {
- return false;
- } else {
- return mem_map_->Protect(PROT_READ);
- }
+ CHECK(container_.get() != nullptr);
+ return container_->DisableWrite();
}
DexFile::DexFile(const uint8_t* base,
size_t size,
const std::string& location,
uint32_t location_checksum,
- const OatDexFile* oat_dex_file)
+ const OatDexFile* oat_dex_file,
+ DexFileContainer* container)
: begin_(base),
size_(size),
location_(location),
@@ -114,7 +93,8 @@
num_method_handles_(0),
call_site_ids_(nullptr),
num_call_site_ids_(0),
- oat_dex_file_(oat_dex_file) {
+ oat_dex_file_(oat_dex_file),
+ container_(container) {
CHECK(begin_ != nullptr) << GetLocation();
CHECK_GT(size_, 0U) << GetLocation();
// Check base (=header) alignment.
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 53e1f9e..c895e0d 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -39,6 +39,21 @@
class StringPiece;
class ZipArchive;
+// Some instances of DexFile own the storage referred to by DexFile. Clients who create
+// such management do so by subclassing Container.
+class DexFileContainer {
+ public:
+ DexFileContainer() { }
+ virtual ~DexFileContainer() { }
+ virtual int GetPermissions() = 0;
+ virtual bool IsReadOnly() = 0;
+ virtual bool EnableWrite() = 0;
+ virtual bool DisableWrite() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DexFileContainer);
+};
+
// Dex file is the API that exposes native dex files (ordinary dex files) and CompactDex.
// Originally, the dex file format used by ART was mostly the same as APKs. The only change was
// quickened opcodes and layout optimizations.
@@ -993,7 +1008,8 @@
size_t size,
const std::string& location,
uint32_t location_checksum,
- const OatDexFile* oat_dex_file);
+ const OatDexFile* oat_dex_file,
+ DexFileContainer* container);
// Top-level initializer that calls other Init methods.
bool Init(std::string* error_msg);
@@ -1018,9 +1034,6 @@
const uint32_t location_checksum_;
- // Manages the underlying memory allocation.
- std::unique_ptr<MemMap> mem_map_;
-
// Points to the header section.
const Header* const header_;
@@ -1059,6 +1072,9 @@
// null.
mutable const OatDexFile* oat_dex_file_;
+ // Manages the underlying memory allocation.
+ std::unique_ptr<DexFileContainer> container_;
+
friend class DexFileLoader;
friend class DexFileVerifierTest;
friend class OatWriter;
diff --git a/runtime/dex_file_layout.cc b/runtime/dex_file_layout.cc
index 4375d7f..c3fae15 100644
--- a/runtime/dex_file_layout.cc
+++ b/runtime/dex_file_layout.cc
@@ -18,6 +18,7 @@
#include <sys/mman.h>
+#include "base/file_utils.h"
#include "dex_file.h"
#include "utils.h"
diff --git a/runtime/dex_file_loader.cc b/runtime/dex_file_loader.cc
index 26f7e76..06e3397 100644
--- a/runtime/dex_file_loader.cc
+++ b/runtime/dex_file_loader.cc
@@ -33,6 +33,50 @@
namespace art {
+namespace {
+
+class MemMapContainer : public DexFileContainer {
+ public:
+ explicit MemMapContainer(std::unique_ptr<MemMap>&& mem_map) : mem_map_(std::move(mem_map)) { }
+ virtual ~MemMapContainer() OVERRIDE { }
+
+ int GetPermissions() OVERRIDE {
+ if (mem_map_.get() == nullptr) {
+ return 0;
+ } else {
+ return mem_map_->GetProtect();
+ }
+ }
+
+ bool IsReadOnly() OVERRIDE {
+ return GetPermissions() == PROT_READ;
+ }
+
+ bool EnableWrite() OVERRIDE {
+ CHECK(IsReadOnly());
+ if (mem_map_.get() == nullptr) {
+ return false;
+ } else {
+ return mem_map_->Protect(PROT_READ | PROT_WRITE);
+ }
+ }
+
+ bool DisableWrite() OVERRIDE {
+ CHECK(!IsReadOnly());
+ if (mem_map_.get() == nullptr) {
+ return false;
+ } else {
+ return mem_map_->Protect(PROT_READ);
+ }
+ }
+
+ private:
+ std::unique_ptr<MemMap> mem_map_;
+ DISALLOW_COPY_AND_ASSIGN(MemMapContainer);
+};
+
+} // namespace
+
using android::base::StringPrintf;
static constexpr OatDexFile* kNoOatDexFile = nullptr;
@@ -151,7 +195,9 @@
oat_dex_file,
verify,
verify_checksum,
- error_msg);
+ error_msg,
+ /*container*/ nullptr,
+ /*verify_result*/ nullptr);
}
std::unique_ptr<const DexFile> DexFileLoader::Open(const std::string& location,
@@ -177,10 +223,9 @@
kNoOatDexFile,
verify,
verify_checksum,
- error_msg);
- if (dex_file != nullptr) {
- dex_file->mem_map_ = std::move(map);
- }
+ error_msg,
+ new MemMapContainer(std::move(map)),
+ /*verify_result*/ nullptr);
return dex_file;
}
@@ -296,10 +341,9 @@
kNoOatDexFile,
verify,
verify_checksum,
- error_msg);
- if (dex_file != nullptr) {
- dex_file->mem_map_ = std::move(map);
- }
+ error_msg,
+ new MemMapContainer(std::move(map)),
+ /*verify_result*/ nullptr);
return dex_file;
}
@@ -365,6 +409,7 @@
verify,
verify_checksum,
error_msg,
+ new MemMapContainer(std::move(map)),
&verify_result);
if (dex_file == nullptr) {
if (verify_result == VerifyResult::kVerifyNotAttempted) {
@@ -374,7 +419,6 @@
}
return nullptr;
}
- dex_file->mem_map_ = std::move(map);
if (!dex_file->DisableWrite()) {
*error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str());
*error_code = ZipOpenErrorCode::kMakeReadOnlyError;
@@ -465,15 +509,18 @@
bool verify,
bool verify_checksum,
std::string* error_msg,
+ DexFileContainer* container,
VerifyResult* verify_result) {
if (verify_result != nullptr) {
*verify_result = VerifyResult::kVerifyNotAttempted;
}
std::unique_ptr<DexFile> dex_file;
if (StandardDexFile::IsMagicValid(base)) {
- dex_file.reset(new StandardDexFile(base, size, location, location_checksum, oat_dex_file));
+ dex_file.reset(
+ new StandardDexFile(base, size, location, location_checksum, oat_dex_file, container));
} else if (CompactDexFile::IsMagicValid(base)) {
- dex_file.reset(new CompactDexFile(base, size, location, location_checksum, oat_dex_file));
+ dex_file.reset(
+ new CompactDexFile(base, size, location, location_checksum, oat_dex_file, container));
}
if (dex_file == nullptr) {
*error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
diff --git a/runtime/dex_file_loader.h b/runtime/dex_file_loader.h
index a1167dc..97c886a 100644
--- a/runtime/dex_file_loader.h
+++ b/runtime/dex_file_loader.h
@@ -25,6 +25,7 @@
namespace art {
class DexFile;
+class DexFileContainer;
class MemMap;
class OatDexFile;
class ZipArchive;
@@ -190,7 +191,8 @@
bool verify,
bool verify_checksum,
std::string* error_msg,
- VerifyResult* verify_result = nullptr);
+ DexFileContainer* container,
+ VerifyResult* verify_result);
};
} // namespace art
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index 7bda89d..ee577e7 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -57,7 +57,7 @@
class DexFileVerifierTest : public CommonRuntimeTest {
protected:
DexFile* GetDexFile(const uint8_t* dex_bytes, size_t length) {
- return new StandardDexFile(dex_bytes, length, "tmp", 0, nullptr);
+ return new StandardDexFile(dex_bytes, length, "tmp", 0, nullptr, nullptr);
}
void VerifyModification(const char* dex_file_base64_content,
diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc
index 99fe53b..e64c0f6 100644
--- a/runtime/dex_instruction.cc
+++ b/runtime/dex_instruction.cc
@@ -119,6 +119,26 @@
}
}
+size_t Instruction::CodeUnitsRequiredForSizeOfComplexOpcode() const {
+ const uint16_t* insns = reinterpret_cast<const uint16_t*>(this);
+ // Handle special NOP encoded variable length sequences.
+ switch (*insns) {
+ case kPackedSwitchSignature:
+ FALLTHROUGH_INTENDED;
+ case kSparseSwitchSignature:
+ return 2;
+ case kArrayDataSignature:
+ return 4;
+ default:
+ if ((*insns & 0xFF) == 0) {
+ return 1; // NOP.
+ } else {
+ LOG(FATAL) << "Unreachable: " << DumpString(nullptr);
+ UNREACHABLE();
+ }
+ }
+}
+
std::string Instruction::DumpHex(size_t code_units) const {
size_t inst_length = SizeInCodeUnits();
if (inst_length > code_units) {
diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h
index 2f28dff..09c78b2 100644
--- a/runtime/dex_instruction.h
+++ b/runtime/dex_instruction.h
@@ -225,6 +225,12 @@
}
}
+ // Code units required to calculate the size of the instruction.
+ size_t CodeUnitsRequiredForSizeComputation() const {
+ const int8_t result = kInstructionDescriptors[Opcode()].size_in_code_units;
+ return UNLIKELY(result < 0) ? CodeUnitsRequiredForSizeOfComplexOpcode() : 1;
+ }
+
// Reads an instruction out of the stream at the specified address.
static const Instruction* At(const uint16_t* code) {
DCHECK(code != nullptr);
@@ -638,6 +644,9 @@
private:
size_t SizeInCodeUnitsComplexOpcode() const;
+ // Return how many code unit words are required to compute the size of the opcode.
+ size_t CodeUnitsRequiredForSizeOfComplexOpcode() const;
+
uint32_t Fetch32(size_t offset) const {
return (Fetch16(offset) | ((uint32_t) Fetch16(offset + 1) << 16));
}
diff --git a/runtime/dex_instruction_iterator.h b/runtime/dex_instruction_iterator.h
index 280746e..9e4dea3 100644
--- a/runtime/dex_instruction_iterator.h
+++ b/runtime/dex_instruction_iterator.h
@@ -24,19 +24,68 @@
namespace art {
-class DexInstructionIterator : public std::iterator<std::forward_iterator_tag, Instruction> {
+// Base helper class to prevent duplicated comparators.
+class DexInstructionIteratorBase : public std::iterator<std::forward_iterator_tag, Instruction> {
public:
using value_type = std::iterator<std::forward_iterator_tag, Instruction>::value_type;
using difference_type = std::iterator<std::forward_iterator_tag, value_type>::difference_type;
- DexInstructionIterator() = default;
- DexInstructionIterator(const DexInstructionIterator&) = default;
- DexInstructionIterator(DexInstructionIterator&&) = default;
- DexInstructionIterator& operator=(const DexInstructionIterator&) = default;
- DexInstructionIterator& operator=(DexInstructionIterator&&) = default;
+ DexInstructionIteratorBase() = default;
+ explicit DexInstructionIteratorBase(const value_type* inst) : inst_(inst) {}
- explicit DexInstructionIterator(const value_type* inst) : inst_(inst) {}
- explicit DexInstructionIterator(const uint16_t* inst) : inst_(value_type::At(inst)) {}
+ const value_type* Inst() const {
+ return inst_;
+ }
+
+ // Return the dex pc for an iterator compared to the code item begin.
+ uint32_t GetDexPC(const DexInstructionIteratorBase& code_item_begin) {
+ return reinterpret_cast<const uint16_t*>(inst_) -
+ reinterpret_cast<const uint16_t*>(code_item_begin.inst_);
+ }
+
+ protected:
+ const value_type* inst_ = nullptr;
+};
+
+
+static ALWAYS_INLINE inline bool operator==(const DexInstructionIteratorBase& lhs,
+ const DexInstructionIteratorBase& rhs) {
+ return lhs.Inst() == rhs.Inst();
+}
+
+static inline bool operator!=(const DexInstructionIteratorBase& lhs,
+ const DexInstructionIteratorBase& rhs) {
+ return !(lhs == rhs);
+}
+
+static inline bool operator<(const DexInstructionIteratorBase& lhs,
+ const DexInstructionIteratorBase& rhs) {
+ return lhs.Inst() < rhs.Inst();
+}
+
+static inline bool operator>(const DexInstructionIteratorBase& lhs,
+ const DexInstructionIteratorBase& rhs) {
+ return rhs < lhs;
+}
+
+static inline bool operator<=(const DexInstructionIteratorBase& lhs,
+ const DexInstructionIteratorBase& rhs) {
+ return !(rhs < lhs);
+}
+
+static inline bool operator>=(const DexInstructionIteratorBase& lhs,
+ const DexInstructionIteratorBase& rhs) {
+ return !(lhs < rhs);
+}
+
+class DexInstructionIterator : public DexInstructionIteratorBase {
+ public:
+ using value_type = std::iterator<std::forward_iterator_tag, Instruction>::value_type;
+ using difference_type = std::iterator<std::forward_iterator_tag, value_type>::difference_type;
+ using DexInstructionIteratorBase::DexInstructionIteratorBase;
+
+ explicit DexInstructionIterator(const uint16_t* inst)
+ : DexInstructionIteratorBase(value_type::At(inst)) {}
// Value after modification.
DexInstructionIterator& operator++() {
@@ -58,51 +107,68 @@
const value_type* operator->() const {
return &**this;
}
+};
- // Return the dex pc for an iterator compared to the code item begin.
- uint32_t GetDexPC(const DexInstructionIterator& code_item_begin) {
- return reinterpret_cast<const uint16_t*>(inst_) -
- reinterpret_cast<const uint16_t*>(code_item_begin.inst_);
+class SafeDexInstructionIterator : public DexInstructionIteratorBase {
+ public:
+ explicit SafeDexInstructionIterator(const DexInstructionIteratorBase& start,
+ const DexInstructionIteratorBase& end)
+ : DexInstructionIteratorBase(start.Inst())
+ , end_(end.Inst()) {}
+
+ // Value after modification, does not read past the end of the allowed region. May increment past
+ // the end of the code item though.
+ SafeDexInstructionIterator& operator++() {
+ AssertValid();
+ const size_t size_code_units = Inst()->CodeUnitsRequiredForSizeComputation();
+ const size_t available = reinterpret_cast<const uint16_t*>(end_) -
+ reinterpret_cast<const uint16_t*>(Inst());
+ if (UNLIKELY(size_code_units > available)) {
+ error_state_ = true;
+ return *this;
+ }
+ const size_t instruction_size = inst_->SizeInCodeUnits();
+ if (UNLIKELY(instruction_size > available)) {
+ error_state_ = true;
+ return *this;
+ }
+ inst_ = inst_->RelativeAt(instruction_size);
+ return *this;
}
- const value_type* Inst() const {
- return inst_;
+ // Value before modification.
+ SafeDexInstructionIterator operator++(int) {
+ SafeDexInstructionIterator temp = *this;
+ ++*this;
+ return temp;
+ }
+
+ const value_type& operator*() const {
+ AssertValid();
+ return *inst_;
+ }
+
+ const value_type* operator->() const {
+ AssertValid();
+ return &**this;
+ }
+
+ // Returns true if the iterator is in an error state. This occurs when an instruction couldn't
+ // have its size computed without reading past the end iterator.
+ bool IsErrorState() const {
+ return error_state_;
}
private:
- const value_type* inst_ = nullptr;
+ ALWAYS_INLINE void AssertValid() const {
+ DCHECK(!IsErrorState());
+ DCHECK_LT(Inst(), end_);
+ }
+
+ const value_type* end_ = nullptr;
+ bool error_state_ = false;
};
-static ALWAYS_INLINE inline bool operator==(const DexInstructionIterator& lhs,
- const DexInstructionIterator& rhs) {
- return lhs.Inst() == rhs.Inst();
-}
-
-static inline bool operator!=(const DexInstructionIterator& lhs,
- const DexInstructionIterator& rhs) {
- return !(lhs == rhs);
-}
-
-static inline bool operator<(const DexInstructionIterator& lhs,
- const DexInstructionIterator& rhs) {
- return lhs.Inst() < rhs.Inst();
-}
-
-static inline bool operator>(const DexInstructionIterator& lhs,
- const DexInstructionIterator& rhs) {
- return rhs < lhs;
-}
-
-static inline bool operator<=(const DexInstructionIterator& lhs,
- const DexInstructionIterator& rhs) {
- return !(rhs < lhs);
-}
-
-static inline bool operator>=(const DexInstructionIterator& lhs,
- const DexInstructionIterator& rhs) {
- return !(lhs < rhs);
-}
-
} // namespace art
#endif // ART_RUNTIME_DEX_INSTRUCTION_ITERATOR_H_
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index cf5cc11..4d7c2a1 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -2574,7 +2574,7 @@
// each type.
extern "C" uintptr_t artInvokePolymorphic(
JValue* result,
- mirror::Object* raw_method_handle,
+ mirror::Object* raw_receiver,
Thread* self,
ArtMethod** sp)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -2602,26 +2602,29 @@
RememberForGcArgumentVisitor gc_visitor(sp, kMethodIsStatic, shorty, shorty_length, &soa);
gc_visitor.VisitArguments();
- // Wrap raw_method_handle in a Handle for safety.
- StackHandleScope<2> hs(self);
- Handle<mirror::MethodHandle> method_handle(
- hs.NewHandle(ObjPtr<mirror::MethodHandle>::DownCast(MakeObjPtr(raw_method_handle))));
- raw_method_handle = nullptr;
+ // Wrap raw_receiver in a Handle for safety.
+ StackHandleScope<3> hs(self);
+ Handle<mirror::Object> receiver_handle(hs.NewHandle(raw_receiver));
+ raw_receiver = nullptr;
self->EndAssertNoThreadSuspension(old_cause);
- // Resolve method - it's either MethodHandle.invoke() or MethodHandle.invokeExact().
+ // Resolve method.
ClassLinker* linker = Runtime::Current()->GetClassLinker();
ArtMethod* resolved_method = linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
self, inst.VRegB(), caller_method, kVirtual);
- DCHECK((resolved_method ==
- jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact)) ||
- (resolved_method ==
- jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invoke)));
- if (UNLIKELY(method_handle.IsNull())) {
+
+ if (UNLIKELY(receiver_handle.IsNull())) {
ThrowNullPointerExceptionForMethodAccess(resolved_method, InvokeType::kVirtual);
return static_cast<uintptr_t>('V');
}
+ // TODO(oth): Ensure this path isn't taken for VarHandle accessors (b/65872996).
+ DCHECK_EQ(resolved_method->GetDeclaringClass(),
+ WellKnownClasses::ToClass(WellKnownClasses::java_lang_invoke_MethodHandle));
+
+ Handle<mirror::MethodHandle> method_handle(hs.NewHandle(
+ ObjPtr<mirror::MethodHandle>::DownCast(MakeObjPtr(receiver_handle.Get()))));
+
Handle<mirror::MethodType> method_type(
hs.NewHandle(linker->ResolveMethodType(self, proto_idx, caller_method)));
@@ -2662,16 +2665,28 @@
// consecutive order.
uint32_t unused_args[Instruction::kMaxVarArgRegs] = {};
uint32_t first_callee_arg = first_arg + 1;
- if (!DoInvokePolymorphic<true /* is_range */>(self,
- resolved_method,
- *shadow_frame,
- method_handle,
- method_type,
- unused_args,
- first_callee_arg,
- result)) {
- DCHECK(self->IsExceptionPending());
+
+ bool isExact = (jni::EncodeArtMethod(resolved_method) ==
+ WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact);
+ bool success = false;
+ if (isExact) {
+ success = MethodHandleInvokeExact<true/*is_range*/>(self,
+ *shadow_frame,
+ method_handle,
+ method_type,
+ unused_args,
+ first_callee_arg,
+ result);
+ } else {
+ success = MethodHandleInvoke<true/*is_range*/>(self,
+ *shadow_frame,
+ method_handle,
+ method_type,
+ unused_args,
+ first_callee_arg,
+ result);
}
+ DCHECK(success || self->IsExceptionPending());
// Pop transition record.
self->PopManagedStackFragment(fragment);
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 4d4d8ff..7beff96 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -18,6 +18,7 @@
#include "art_field-inl.h"
#include "base/enums.h"
+#include "base/file_utils.h"
#include "base/histogram-inl.h"
#include "base/stl_util.h"
#include "base/systrace.h"
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 2dc5acc..c6caf4b 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -24,6 +24,7 @@
#include "base/bounded_fifo.h"
#include "base/enums.h"
+#include "base/file_utils.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/mutex-inl.h"
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 4f54582..9f62666 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -28,6 +28,7 @@
#include "base/allocator.h"
#include "base/arena_allocator.h"
#include "base/dumpable.h"
+#include "base/file_utils.h"
#include "base/histogram-inl.h"
#include "base/memory_tool.h"
#include "base/stl_util.h"
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index f0eada3..74813b4 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -30,6 +30,7 @@
#include "art_method-inl.h"
#include "base/callee_save_type.h"
#include "base/enums.h"
+#include "base/file_utils.h"
#include "base/macros.h"
#include "base/scoped_flock.h"
#include "base/stl_util.h"
diff --git a/runtime/gc/space/image_space_fs.h b/runtime/gc/space/image_space_fs.h
index 5999548..bdb2eda 100644
--- a/runtime/gc/space/image_space_fs.h
+++ b/runtime/gc/space/image_space_fs.h
@@ -22,6 +22,7 @@
#include "android-base/stringprintf.h"
+#include "base/file_utils.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/unix_file/fd_file.h"
diff --git a/runtime/gc/verification.cc b/runtime/gc/verification.cc
index beb43df..3cd04a6 100644
--- a/runtime/gc/verification.cc
+++ b/runtime/gc/verification.cc
@@ -20,6 +20,7 @@
#include <sstream>
#include "art_field-inl.h"
+#include "base/file_utils.h"
#include "mirror/class-inl.h"
#include "mirror/object-refvisitor-inl.h"
diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h
index 071d1ae..74265bb 100644
--- a/runtime/generated/asm_support_gen.h
+++ b/runtime/generated/asm_support_gen.h
@@ -39,23 +39,23 @@
#define RUNTIME_SAVE_EVERYTHING_FOR_SUSPEND_CHECK_METHOD_OFFSET 0x28
DEFINE_CHECK_EQ(static_cast<size_t>(RUNTIME_SAVE_EVERYTHING_FOR_SUSPEND_CHECK_METHOD_OFFSET), (static_cast<size_t>(art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveEverythingForSuspendCheck))))
#define THREAD_FLAGS_OFFSET 0
-DEFINE_CHECK_EQ(static_cast<int32_t>(THREAD_FLAGS_OFFSET), (static_cast<int32_t>(art::Thread:: ThreadFlagsOffset<art::kRuntimePointerSize>().Int32Value())))
+DEFINE_CHECK_EQ(static_cast<int32_t>(THREAD_FLAGS_OFFSET), (static_cast<int32_t>(art::Thread::ThreadFlagsOffset<art::kRuntimePointerSize>().Int32Value())))
#define THREAD_ID_OFFSET 12
-DEFINE_CHECK_EQ(static_cast<int32_t>(THREAD_ID_OFFSET), (static_cast<int32_t>(art::Thread:: ThinLockIdOffset<art::kRuntimePointerSize>().Int32Value())))
+DEFINE_CHECK_EQ(static_cast<int32_t>(THREAD_ID_OFFSET), (static_cast<int32_t>(art::Thread::ThinLockIdOffset<art::kRuntimePointerSize>().Int32Value())))
#define THREAD_IS_GC_MARKING_OFFSET 52
-DEFINE_CHECK_EQ(static_cast<int32_t>(THREAD_IS_GC_MARKING_OFFSET), (static_cast<int32_t>(art::Thread:: IsGcMarkingOffset<art::kRuntimePointerSize>().Int32Value())))
+DEFINE_CHECK_EQ(static_cast<int32_t>(THREAD_IS_GC_MARKING_OFFSET), (static_cast<int32_t>(art::Thread::IsGcMarkingOffset<art::kRuntimePointerSize>().Int32Value())))
#define THREAD_CARD_TABLE_OFFSET 136
-DEFINE_CHECK_EQ(static_cast<int32_t>(THREAD_CARD_TABLE_OFFSET), (static_cast<int32_t>(art::Thread:: CardTableOffset<art::kRuntimePointerSize>().Int32Value())))
+DEFINE_CHECK_EQ(static_cast<int32_t>(THREAD_CARD_TABLE_OFFSET), (static_cast<int32_t>(art::Thread::CardTableOffset<art::kRuntimePointerSize>().Int32Value())))
#define CODEITEM_INSNS_OFFSET 16
DEFINE_CHECK_EQ(static_cast<int32_t>(CODEITEM_INSNS_OFFSET), (static_cast<int32_t>(__builtin_offsetof(art::DexFile::CodeItem, insns_))))
#define MIRROR_CLASS_DEX_CACHE_OFFSET 16
-DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_CLASS_DEX_CACHE_OFFSET), (static_cast<int32_t>(art::mirror::Class:: DexCacheOffset().Int32Value())))
+DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_CLASS_DEX_CACHE_OFFSET), (static_cast<int32_t>(art::mirror::Class::DexCacheOffset().Int32Value())))
#define MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET 48
-DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET), (static_cast<int32_t>(art::mirror::DexCache:: ResolvedMethodsOffset().Int32Value())))
+DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET), (static_cast<int32_t>(art::mirror::DexCache::ResolvedMethodsOffset().Int32Value())))
#define MIRROR_OBJECT_CLASS_OFFSET 0
-DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_OBJECT_CLASS_OFFSET), (static_cast<int32_t>(art::mirror::Object:: ClassOffset().Int32Value())))
+DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_OBJECT_CLASS_OFFSET), (static_cast<int32_t>(art::mirror::Object::ClassOffset().Int32Value())))
#define MIRROR_OBJECT_LOCK_WORD_OFFSET 4
-DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_OBJECT_LOCK_WORD_OFFSET), (static_cast<int32_t>(art::mirror::Object:: MonitorOffset().Int32Value())))
+DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_OBJECT_LOCK_WORD_OFFSET), (static_cast<int32_t>(art::mirror::Object::MonitorOffset().Int32Value())))
#define MIRROR_CLASS_STATUS_INITIALIZED 0xb
DEFINE_CHECK_EQ(static_cast<uint32_t>(MIRROR_CLASS_STATUS_INITIALIZED), (static_cast<uint32_t>((art::mirror::Class::kStatusInitialized))))
#define ACCESS_FLAGS_CLASS_IS_FINALIZABLE 0x80000000
@@ -65,17 +65,17 @@
#define ACCESS_FLAGS_CLASS_IS_FINALIZABLE_BIT 0x1f
DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_FINALIZABLE_BIT), (static_cast<uint32_t>((art::MostSignificantBit(art::kAccClassIsFinalizable)))))
#define ART_METHOD_JNI_OFFSET_32 20
-DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_JNI_OFFSET_32), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromJniOffset(art::PointerSize::k32).Int32Value())))
+DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_JNI_OFFSET_32), (static_cast<int32_t>(art::ArtMethod::EntryPointFromJniOffset(art::PointerSize::k32).Int32Value())))
#define ART_METHOD_JNI_OFFSET_64 24
-DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_JNI_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromJniOffset(art::PointerSize::k64).Int32Value())))
+DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_JNI_OFFSET_64), (static_cast<int32_t>(art::ArtMethod::EntryPointFromJniOffset(art::PointerSize::k64).Int32Value())))
#define ART_METHOD_QUICK_CODE_OFFSET_32 24
-DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_QUICK_CODE_OFFSET_32), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromQuickCompiledCodeOffset(art::PointerSize::k32).Int32Value())))
+DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_QUICK_CODE_OFFSET_32), (static_cast<int32_t>(art::ArtMethod::EntryPointFromQuickCompiledCodeOffset(art::PointerSize::k32).Int32Value())))
#define ART_METHOD_QUICK_CODE_OFFSET_64 32
-DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_QUICK_CODE_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromQuickCompiledCodeOffset(art::PointerSize::k64).Int32Value())))
+DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_QUICK_CODE_OFFSET_64), (static_cast<int32_t>(art::ArtMethod::EntryPointFromQuickCompiledCodeOffset(art::PointerSize::k64).Int32Value())))
#define ART_METHOD_DECLARING_CLASS_OFFSET 0
-DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DECLARING_CLASS_OFFSET), (static_cast<int32_t>(art::ArtMethod:: DeclaringClassOffset().Int32Value())))
+DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DECLARING_CLASS_OFFSET), (static_cast<int32_t>(art::ArtMethod::DeclaringClassOffset().Int32Value())))
#define ART_METHOD_ACCESS_FLAGS_OFFSET 4
-DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_ACCESS_FLAGS_OFFSET), (static_cast<int32_t>(art::ArtMethod:: AccessFlagsOffset().Int32Value())))
+DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_ACCESS_FLAGS_OFFSET), (static_cast<int32_t>(art::ArtMethod::AccessFlagsOffset().Int32Value())))
#define STRING_DEX_CACHE_ELEMENT_SIZE_SHIFT 3
DEFINE_CHECK_EQ(static_cast<int32_t>(STRING_DEX_CACHE_ELEMENT_SIZE_SHIFT), (static_cast<int32_t>(art::WhichPowerOf2(sizeof(art::mirror::StringDexCachePair)))))
#define STRING_DEX_CACHE_SIZE_MINUS_ONE 1023
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 2c82cb1..49f2021 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -137,11 +137,12 @@
method->SetEntryPointFromQuickCompiledCode(quick_code);
}
-bool Instrumentation::NeedDebugVersionFor(ArtMethod* method) const REQUIRES_SHARED(Locks::mutator_lock_) {
- return Dbg::IsDebuggerActive() &&
- Runtime::Current()->IsJavaDebuggable() &&
+bool Instrumentation::NeedDebugVersionFor(ArtMethod* method) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return Runtime::Current()->IsJavaDebuggable() &&
!method->IsNative() &&
- !method->IsProxyMethod();
+ !method->IsProxyMethod() &&
+ Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method);
}
void Instrumentation::InstallStubsForMethod(ArtMethod* method) {
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 9fb9fe7..0a1ae36 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -22,6 +22,7 @@
#include "debugger.h"
#include "dex_file_types.h"
#include "entrypoints/runtime_asm_entrypoints.h"
+#include "intrinsics_enum.h"
#include "jit/jit.h"
#include "jvalue.h"
#include "method_handles-inl.h"
@@ -588,11 +589,12 @@
}
template<bool is_range>
-bool DoInvokePolymorphic(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result)
+static bool DoMethodHandleInvokeCommon(Thread* self,
+ ShadowFrame& shadow_frame,
+ bool invoke_exact,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result)
REQUIRES_SHARED(Locks::mutator_lock_) {
// Make sure to check for async exceptions
if (UNLIKELY(self->ObserveAsyncException())) {
@@ -638,41 +640,381 @@
return false;
}
- ArtMethod* invoke_method =
- class_linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
- self, invoke_method_idx, shadow_frame.GetMethod(), kVirtual);
-
// There is a common dispatch method for method handles that takes
// arguments either from a range or an array of arguments depending
// on whether the DEX instruction is invoke-polymorphic/range or
// invoke-polymorphic. The array here is for the latter.
uint32_t args[Instruction::kMaxVarArgRegs] = {};
- if (is_range) {
+ if (UNLIKELY(is_range)) {
// VRegC is the register holding the method handle. Arguments passed
// to the method handle's target do not include the method handle.
uint32_t first_arg = inst->VRegC_4rcc() + 1;
- return DoInvokePolymorphic<is_range>(self,
- invoke_method,
- shadow_frame,
- method_handle,
- callsite_type,
- args /* unused */,
- first_arg,
- result);
+ static const bool kIsRange = true;
+ if (invoke_exact) {
+ return art::MethodHandleInvokeExact<kIsRange>(self,
+ shadow_frame,
+ method_handle,
+ callsite_type,
+ args /* unused */,
+ first_arg,
+ result);
+ } else {
+ return art::MethodHandleInvoke<kIsRange>(self,
+ shadow_frame,
+ method_handle,
+ callsite_type,
+ args /* unused */,
+ first_arg,
+ result);
+ }
} else {
// Get the register arguments for the invoke.
inst->GetVarArgs(args, inst_data);
// Drop the first register which is the method handle performing the invoke.
memmove(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1));
args[Instruction::kMaxVarArgRegs - 1] = 0;
- return DoInvokePolymorphic<is_range>(self,
- invoke_method,
- shadow_frame,
- method_handle,
- callsite_type,
- args,
- args[0],
- result);
+ static const bool kIsRange = false;
+ if (invoke_exact) {
+ return art::MethodHandleInvokeExact<kIsRange>(self,
+ shadow_frame,
+ method_handle,
+ callsite_type,
+ args,
+ args[0],
+ result);
+ } else {
+ return art::MethodHandleInvoke<kIsRange>(self,
+ shadow_frame,
+ method_handle,
+ callsite_type,
+ args,
+ args[0],
+ result);
+ }
+ }
+}
+
+bool DoMethodHandleInvokeExact(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (inst->Opcode() == Instruction::INVOKE_POLYMORPHIC) {
+ static const bool kIsRange = false;
+ return DoMethodHandleInvokeCommon<kIsRange>(
+ self, shadow_frame, true /* is_exact */, inst, inst_data, result);
+ } else {
+ DCHECK_EQ(inst->Opcode(), Instruction::INVOKE_POLYMORPHIC_RANGE);
+ static const bool kIsRange = true;
+ return DoMethodHandleInvokeCommon<kIsRange>(
+ self, shadow_frame, true /* is_exact */, inst, inst_data, result);
+ }
+}
+
+bool DoMethodHandleInvoke(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (inst->Opcode() == Instruction::INVOKE_POLYMORPHIC) {
+ static const bool kIsRange = false;
+ return DoMethodHandleInvokeCommon<kIsRange>(
+ self, shadow_frame, false /* is_exact */, inst, inst_data, result);
+ } else {
+ DCHECK_EQ(inst->Opcode(), Instruction::INVOKE_POLYMORPHIC_RANGE);
+ static const bool kIsRange = true;
+ return DoMethodHandleInvokeCommon<kIsRange>(
+ self, shadow_frame, false /* is_exact */, inst, inst_data, result);
+ }
+}
+
+static bool UnimplementedSignaturePolymorphicMethod(Thread* self ATTRIBUTE_UNUSED,
+ ShadowFrame& shadow_frame ATTRIBUTE_UNUSED,
+ const Instruction* inst ATTRIBUTE_UNUSED,
+ uint16_t inst_data ATTRIBUTE_UNUSED,
+ JValue* result ATTRIBUTE_UNUSED)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ UNIMPLEMENTED(FATAL) << "TODO(oth): b/65872996";
+ return false;
+}
+
+bool DoVarHandleCompareAndExchange(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleCompareAndExchangeAcquire(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleCompareAndExchangeRelease(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleCompareAndSet(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGet(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGetAcquire(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGetAndAdd(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGetAndAddAcquire(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGetAndAddRelease(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGetAndBitwiseAnd(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGetAndBitwiseAndAcquire(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGetAndBitwiseAndRelease(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGetAndBitwiseOr(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGetAndBitwiseOrAcquire(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGetAndBitwiseOrRelease(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGetAndBitwiseXor(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGetAndBitwiseXorAcquire(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGetAndBitwiseXorRelease(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGetAndSet(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGetAndSetAcquire(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGetAndSetRelease(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGetOpaque(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleGetVolatile(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleSet(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleSetOpaque(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleSetRelease(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleSetVolatile(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleWeakCompareAndSet(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleWeakCompareAndSetAcquire(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleWeakCompareAndSetPlain(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+bool DoVarHandleWeakCompareAndSetRelease(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+}
+
+template<bool is_range>
+bool DoInvokePolymorphic(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) {
+ const int invoke_method_idx = inst->VRegB();
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ArtMethod* invoke_method =
+ class_linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
+ self, invoke_method_idx, shadow_frame.GetMethod(), kVirtual);
+
+ // Ensure intrinsic identifiers are initialized.
+ DCHECK(invoke_method->IsIntrinsic());
+
+ // Dispatch based on intrinsic identifier associated with method.
+ switch (static_cast<art::Intrinsics>(invoke_method->GetIntrinsic())) {
+#define CASE_SIGNATURE_POLYMORPHIC_INTRINSIC(Name, ...) \
+ case Intrinsics::k##Name: \
+ return Do ## Name(self, shadow_frame, inst, inst_data, result);
+#include "intrinsics_list.h"
+ SIGNATURE_POLYMORPHIC_INTRINSICS_LIST(CASE_SIGNATURE_POLYMORPHIC_INTRINSIC)
+#undef INTRINSICS_LIST
+#undef SIGNATURE_POLYMORPHIC_INTRINSICS_LIST
+#undef CASE_SIGNATURE_POLYMORPHIC_INTRINSIC
+ default:
+ LOG(FATAL) << "Unreachable: " << invoke_method->GetIntrinsic();
+ UNREACHABLE();
+ return false;
}
}
@@ -839,19 +1181,16 @@
// Invoke the bootstrap method handle.
JValue result;
- // This array of arguments is unused. DoInvokePolymorphic() operates on either a
+ // This array of arguments is unused. DoMethodHandleInvokeExact() operates on either a
// an argument array or a range, but always takes an array argument.
uint32_t args_unused[Instruction::kMaxVarArgRegs];
- ArtMethod* invoke_exact =
- jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact);
- bool invoke_success = DoInvokePolymorphic<true /* is_range */>(self,
- invoke_exact,
- *bootstrap_frame,
- bootstrap,
- bootstrap_method_type,
- args_unused,
- 0,
- &result);
+ bool invoke_success = art::MethodHandleInvokeExact<true /* is_range */>(self,
+ *bootstrap_frame,
+ bootstrap,
+ bootstrap_method_type,
+ args_unused,
+ 0,
+ &result);
if (!invoke_success) {
DCHECK(self->IsExceptionPending());
return nullptr;
@@ -942,16 +1281,13 @@
inst->GetVarArgs(args, inst_data);
}
- ArtMethod* invoke_exact =
- jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact);
- return DoInvokePolymorphic<is_range>(self,
- invoke_exact,
- shadow_frame,
- target,
- target_method_type,
- args,
- args[0],
- result);
+ return art::MethodHandleInvokeExact<is_range>(self,
+ shadow_frame,
+ target,
+ target_method_type,
+ args,
+ args[0],
+ result);
}
template <bool is_range>
@@ -1344,16 +1680,6 @@
EXPLICIT_DO_CALL_TEMPLATE_DECL(true, true);
#undef EXPLICIT_DO_CALL_TEMPLATE_DECL
-// Explicit DoInvokeCustom template function declarations.
-#define EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(_is_range) \
- template REQUIRES_SHARED(Locks::mutator_lock_) \
- bool DoInvokeCustom<_is_range>( \
- Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, \
- uint16_t inst_data, JValue* result)
-EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(false);
-EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(true);
-#undef EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL
-
// Explicit DoInvokePolymorphic template function declarations.
#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range) \
template REQUIRES_SHARED(Locks::mutator_lock_) \
@@ -1364,6 +1690,16 @@
EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true);
#undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL
+// Explicit DoInvokeCustom template function declarations.
+#define EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(_is_range) \
+ template REQUIRES_SHARED(Locks::mutator_lock_) \
+ bool DoInvokeCustom<_is_range>( \
+ Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, \
+ uint16_t inst_data, JValue* result)
+EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(false);
+EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(true);
+#undef EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL
+
// Explicit DoFilledNewArray template function declarations.
#define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check, _transaction_active) \
template REQUIRES_SHARED(Locks::mutator_lock_) \
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index e7f67eb..f097bc7 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -222,7 +222,18 @@
return class_linker->ResolveMethodType(self, method_type_index, referrer);
}
-// Performs a signature polymorphic invoke (invoke-polymorphic/invoke-polymorphic-range).
+#define DECLARE_SIGNATURE_POLYMORPHIC_HANDLER(Name, ...) \
+bool Do ## Name(Thread* self, \
+ ShadowFrame& shadow_frame, \
+ const Instruction* inst, \
+ uint16_t inst_data, \
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_);
+#include "intrinsics_list.h"
+INTRINSICS_LIST(DECLARE_SIGNATURE_POLYMORPHIC_HANDLER)
+#undef INTRINSICS_LIST
+#undef DECLARE_SIGNATURE_POLYMORPHIC_HANDLER
+
+// Performs a invoke-polymorphic or invoke-polymorphic-range.
template<bool is_range>
bool DoInvokePolymorphic(Thread* self,
ShadowFrame& shadow_frame,
diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc
index 26de6b4..37593bc 100644
--- a/runtime/interpreter/interpreter_intrinsics.cc
+++ b/runtime/interpreter/interpreter_intrinsics.cc
@@ -16,8 +16,8 @@
#include "interpreter/interpreter_intrinsics.h"
-#include "compiler/intrinsics_enum.h"
#include "dex_instruction.h"
+#include "intrinsics_enum.h"
#include "interpreter/interpreter_common.h"
namespace art {
@@ -323,14 +323,14 @@
return true;
}
-#define VARHANDLE_FENCE_INTRINSIC(name, std_memory_operation) \
-static ALWAYS_INLINE bool name(ShadowFrame* /* shadow_frame */, \
- const Instruction* /* inst */, \
- uint16_t /* inst_data */, \
- JValue* /* result_register */) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- std::atomic_thread_fence(std_memory_operation); \
- return true; \
+#define VARHANDLE_FENCE_INTRINSIC(name, std_memory_operation) \
+static ALWAYS_INLINE bool name(ShadowFrame* shadow_frame ATTRIBUTE_UNUSED, \
+ const Instruction* inst ATTRIBUTE_UNUSED, \
+ uint16_t inst_data ATTRIBUTE_UNUSED, \
+ JValue* result_register ATTRIBUTE_UNUSED) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ std::atomic_thread_fence(std_memory_operation); \
+ return true; \
}
// The VarHandle fence methods are static (unlike sun.misc.Unsafe versions).
@@ -342,6 +342,63 @@
VARHANDLE_FENCE_INTRINSIC(MterpVarHandleLoadLoadFence, std::memory_order_acquire)
VARHANDLE_FENCE_INTRINSIC(MterpVarHandleStoreStoreFence, std::memory_order_release)
+#define METHOD_HANDLE_INVOKE_INTRINSIC(name) \
+static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \
+ const Instruction* inst, \
+ uint16_t inst_data, \
+ JValue* result) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ if (inst->Opcode() == Instruction::INVOKE_POLYMORPHIC) { \
+ return DoInvokePolymorphic<false>(Thread::Current(), *shadow_frame, inst, inst_data, result); \
+ } else { \
+ return DoInvokePolymorphic<true>(Thread::Current(), *shadow_frame, inst, inst_data, result); \
+ } \
+}
+
+METHOD_HANDLE_INVOKE_INTRINSIC(MethodHandleInvokeExact)
+METHOD_HANDLE_INVOKE_INTRINSIC(MethodHandleInvoke)
+
+#define VAR_HANDLE_ACCESSOR_INTRINSIC(name) \
+static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \
+ const Instruction* inst, \
+ uint16_t inst_data, \
+ JValue* result) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return Do##name(Thread::Current(), *shadow_frame, inst, inst_data, result); \
+}
+
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndExchange)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndExchangeAcquire)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndExchangeRelease)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndSet)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGet);
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAcquire)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndAdd)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndAddAcquire)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndAddRelease)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseAnd)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseAndAcquire)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseAndRelease)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseOr)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseOrAcquire)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseOrRelease)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseXor)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseXorAcquire)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseXorRelease)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndSet)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndSetAcquire)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndSetRelease)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetOpaque)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetVolatile)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSet)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSetOpaque)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSetRelease)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSetVolatile)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSet)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetAcquire)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetPlain)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetRelease)
+
// Macro to help keep track of what's left to implement.
#define UNIMPLEMENTED_CASE(name) \
case Intrinsics::k##name: \
@@ -494,6 +551,39 @@
INTRINSIC_CASE(VarHandleReleaseFence)
INTRINSIC_CASE(VarHandleLoadLoadFence)
INTRINSIC_CASE(VarHandleStoreStoreFence)
+ INTRINSIC_CASE(MethodHandleInvokeExact)
+ INTRINSIC_CASE(MethodHandleInvoke)
+ INTRINSIC_CASE(VarHandleCompareAndExchange)
+ INTRINSIC_CASE(VarHandleCompareAndExchangeAcquire)
+ INTRINSIC_CASE(VarHandleCompareAndExchangeRelease)
+ INTRINSIC_CASE(VarHandleCompareAndSet)
+ INTRINSIC_CASE(VarHandleGet)
+ INTRINSIC_CASE(VarHandleGetAcquire)
+ INTRINSIC_CASE(VarHandleGetAndAdd)
+ INTRINSIC_CASE(VarHandleGetAndAddAcquire)
+ INTRINSIC_CASE(VarHandleGetAndAddRelease)
+ INTRINSIC_CASE(VarHandleGetAndBitwiseAnd)
+ INTRINSIC_CASE(VarHandleGetAndBitwiseAndAcquire)
+ INTRINSIC_CASE(VarHandleGetAndBitwiseAndRelease)
+ INTRINSIC_CASE(VarHandleGetAndBitwiseOr)
+ INTRINSIC_CASE(VarHandleGetAndBitwiseOrAcquire)
+ INTRINSIC_CASE(VarHandleGetAndBitwiseOrRelease)
+ INTRINSIC_CASE(VarHandleGetAndBitwiseXor)
+ INTRINSIC_CASE(VarHandleGetAndBitwiseXorAcquire)
+ INTRINSIC_CASE(VarHandleGetAndBitwiseXorRelease)
+ INTRINSIC_CASE(VarHandleGetAndSet)
+ INTRINSIC_CASE(VarHandleGetAndSetAcquire)
+ INTRINSIC_CASE(VarHandleGetAndSetRelease)
+ INTRINSIC_CASE(VarHandleGetOpaque)
+ INTRINSIC_CASE(VarHandleGetVolatile)
+ INTRINSIC_CASE(VarHandleSet)
+ INTRINSIC_CASE(VarHandleSetOpaque)
+ INTRINSIC_CASE(VarHandleSetRelease)
+ INTRINSIC_CASE(VarHandleSetVolatile)
+ INTRINSIC_CASE(VarHandleWeakCompareAndSet)
+ INTRINSIC_CASE(VarHandleWeakCompareAndSetAcquire)
+ INTRINSIC_CASE(VarHandleWeakCompareAndSetPlain)
+ INTRINSIC_CASE(VarHandleWeakCompareAndSetRelease)
case Intrinsics::kNone:
res = false;
break;
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 7a8ae9a..31e7986 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -1636,6 +1636,18 @@
result->SetI((obj != nullptr) ? obj->IdentityHashCode() : 0);
}
+// Checks whether the runtime is s64-bit. This is needed for the clinit of
+// java.lang.invoke.VarHandle clinit. The clinit determines sets of
+// available VarHandle accessors and these differ based on machine
+// word size.
+void UnstartedRuntime::UnstartedJNIVMRuntimeIs64Bit(
+ Thread* self ATTRIBUTE_UNUSED, ArtMethod* method ATTRIBUTE_UNUSED,
+ mirror::Object* receiver ATTRIBUTE_UNUSED, uint32_t* args ATTRIBUTE_UNUSED, JValue* result) {
+ PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ jboolean is64bit = (pointer_size == PointerSize::k64) ? JNI_TRUE : JNI_FALSE;
+ result->SetZ(is64bit);
+}
+
void UnstartedRuntime::UnstartedJNIVMRuntimeNewUnpaddedArray(
Thread* self, ArtMethod* method ATTRIBUTE_UNUSED, mirror::Object* receiver ATTRIBUTE_UNUSED,
uint32_t* args, JValue* result) {
diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h
index e7047c7..c029e07 100644
--- a/runtime/interpreter/unstarted_runtime_list.h
+++ b/runtime/interpreter/unstarted_runtime_list.h
@@ -80,6 +80,7 @@
// Methods that are native.
#define UNSTARTED_RUNTIME_JNI_LIST(V) \
+ V(VMRuntimeIs64Bit, "boolean dalvik.system.VMRuntime.is64Bit()") \
V(VMRuntimeNewUnpaddedArray, "java.lang.Object dalvik.system.VMRuntime.newUnpaddedArray(java.lang.Class, int)") \
V(VMStackGetCallingClassLoader, "java.lang.ClassLoader dalvik.system.VMStack.getCallingClassLoader()") \
V(VMStackGetStackClass2, "java.lang.Class dalvik.system.VMStack.getStackClass2()") \
diff --git a/compiler/intrinsics_enum.h b/runtime/intrinsics_enum.h
similarity index 88%
rename from compiler/intrinsics_enum.h
rename to runtime/intrinsics_enum.h
index 5528181..d46d0cc 100644
--- a/compiler/intrinsics_enum.h
+++ b/runtime/intrinsics_enum.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_INTRINSICS_ENUM_H_
-#define ART_COMPILER_INTRINSICS_ENUM_H_
+#ifndef ART_RUNTIME_INTRINSICS_ENUM_H_
+#define ART_RUNTIME_INTRINSICS_ENUM_H_
namespace art {
@@ -32,4 +32,4 @@
} // namespace art
-#endif // ART_COMPILER_INTRINSICS_ENUM_H_
+#endif // ART_RUNTIME_INTRINSICS_ENUM_H_
diff --git a/compiler/intrinsics_list.h b/runtime/intrinsics_list.h
similarity index 71%
rename from compiler/intrinsics_list.h
rename to runtime/intrinsics_list.h
index bfefead..d007728 100644
--- a/compiler/intrinsics_list.h
+++ b/runtime/intrinsics_list.h
@@ -14,23 +14,76 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_INTRINSICS_LIST_H_
-#define ART_COMPILER_INTRINSICS_LIST_H_
+#ifndef ART_RUNTIME_INTRINSICS_LIST_H_
+#define ART_RUNTIME_INTRINSICS_LIST_H_
-// All intrinsics supported by ART. Format is name, then whether it is expected
-// to be a HInvokeStaticOrDirect node (compared to HInvokeVirtual), then whether it requires an
-// environment, may have side effects, or may throw exceptions.
-
+// This file defines the set of intrinsics that are supported by ART
+// in the compiler and runtime. Neither compiler nor runtime has
+// intrinsics for all methods here.
+//
+// The entries in the INTRINSICS_LIST below have the following format:
+//
+// 1. name
+// 2. invocation-type (art::InvokeType value).
+// 3. needs-environment (art::IntrinsicNeedsEnvironmentOrCache value)
+// 4. side-effects (art::IntrinsicSideEffects value)
+// 5. exception-info (art::::IntrinsicExceptions value)
+// 6. declaring class descriptor
+// 7. method name
+// 8. method descriptor
+//
+// The needs-environment, side-effects and exception-info are compiler
+// related properties (compiler/optimizing/nodes.h) that should not be
+// used outside of the compiler.
+//
// Note: adding a new intrinsic requires an art image version change,
// as the modifiers flag for some ArtMethods will need to be changed.
+//
+// Note: j.l.Integer.valueOf says kNoThrow even though it could throw an
+// OOME. The kNoThrow should be renamed to kNoVisibleThrow, as it is ok to
+// GVN Integer.valueOf (kNoSideEffects), and it is also OK to remove it if
+// it's unused.
+//
+// Note: Thread.interrupted is marked with kAllSideEffects due to the lack
+// of finer grain side effects representation.
-// Note: j.l.Integer.valueOf says kNoThrow even though it could throw an OOME.
-// The kNoThrow should be renamed to kNoVisibleThrow, as it is ok to GVN Integer.valueOf
-// (kNoSideEffects), and it is also OK to remove it if it's unused.
+// Intrinsics for methods with signature polymorphic behaviours.
+#define SIGNATURE_POLYMORPHIC_INTRINSICS_LIST(V) \
+ V(MethodHandleInvokeExact, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/MethodHandle;", "invokeExact", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(MethodHandleInvoke, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/MethodHandle;", "invoke", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleCompareAndExchange, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "compareAndExchange", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleCompareAndExchangeAcquire, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "compareAndExchangeAcquire", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleCompareAndExchangeRelease, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "compareAndExchangeRelease", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleCompareAndSet, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "compareAndSet", "([Ljava/lang/Object;)Z") \
+ V(VarHandleGet, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "get", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleGetAcquire, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "getAcquire", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleGetAndAdd, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "getAndAdd", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleGetAndAddAcquire, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "getAndAddAcquire", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleGetAndAddRelease, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "getAndAddRelease", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleGetAndBitwiseAnd, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "getAndBitwiseAnd", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleGetAndBitwiseAndAcquire, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "getAndBitwiseAndAcquire", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleGetAndBitwiseAndRelease, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "getAndBitwiseAndRelease", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleGetAndBitwiseOr, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "getAndBitwiseOr", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleGetAndBitwiseOrAcquire, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "getAndBitwiseOrAcquire", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleGetAndBitwiseOrRelease, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "getAndBitwiseOrRelease", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleGetAndBitwiseXor, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "getAndBitwiseXor", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleGetAndBitwiseXorAcquire, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "getAndBitwiseXorAcquire", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleGetAndBitwiseXorRelease, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "getAndBitwiseXorRelease", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleGetAndSet, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "getAndSet", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleGetAndSetAcquire, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "getAndSetAcquire", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleGetAndSetRelease, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "getAndSetRelease", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleGetOpaque, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "getOpaque", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleGetVolatile, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "getVolatile", "([Ljava/lang/Object;)Ljava/lang/Object;") \
+ V(VarHandleSet, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "set", "([Ljava/lang/Object;)V") \
+ V(VarHandleSetOpaque, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "setOpaque", "([Ljava/lang/Object;)V") \
+ V(VarHandleSetRelease, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "setRelease", "([Ljava/lang/Object;)V") \
+ V(VarHandleSetVolatile, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "setVolatile", "([Ljava/lang/Object;)V") \
+ V(VarHandleWeakCompareAndSet, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "weakCompareAndSet", "([Ljava/lang/Object;)Z") \
+ V(VarHandleWeakCompareAndSetAcquire, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "weakCompareAndSetAcquire", "([Ljava/lang/Object;)Z") \
+ V(VarHandleWeakCompareAndSetPlain, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "weakCompareAndSetPlain", "([Ljava/lang/Object;)Z") \
+ V(VarHandleWeakCompareAndSetRelease, kPolymorphic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/invoke/VarHandle;", "weakCompareAndSetRelease", "([Ljava/lang/Object;)Z")
-// Note: Thread.interrupted is marked with kAllSideEffects due to the lack of finer grain
-// side effects representation.
-
+// The complete list of intrinsics.
#define INTRINSICS_LIST(V) \
V(DoubleDoubleToRawLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Double;", "doubleToRawLongBits", "(D)J") \
V(DoubleDoubleToLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Double;", "doubleToLongBits", "(D)J") \
@@ -164,6 +217,7 @@
V(VarHandleReleaseFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "releaseFence", "()V") \
V(VarHandleLoadLoadFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "loadLoadFence", "()V") \
V(VarHandleStoreStoreFence, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "storeStoreFence", "()V") \
+ SIGNATURE_POLYMORPHIC_INTRINSICS_LIST(V)
-#endif // ART_COMPILER_INTRINSICS_LIST_H_
-#undef ART_COMPILER_INTRINSICS_LIST_H_ // #define is only for lint.
+#endif // ART_RUNTIME_INTRINSICS_LIST_H_
+#undef ART_RUNTIME_INTRINSICS_LIST_H_ // #define is only for lint.
diff --git a/runtime/invoke_type.h b/runtime/invoke_type.h
index a003f7f..2b877e6 100644
--- a/runtime/invoke_type.h
+++ b/runtime/invoke_type.h
@@ -22,12 +22,13 @@
namespace art {
enum InvokeType : uint32_t {
- kStatic, // <<static>>
- kDirect, // <<direct>>
- kVirtual, // <<virtual>>
- kSuper, // <<super>>
- kInterface, // <<interface>>
- kMaxInvokeType = kInterface
+ kStatic, // <<static>>
+ kDirect, // <<direct>>
+ kVirtual, // <<virtual>>
+ kSuper, // <<super>>
+ kInterface, // <<interface>>
+ kPolymorphic, // <<polymorphic>>
+ kMaxInvokeType = kPolymorphic
};
std::ostream& operator<<(std::ostream& os, const InvokeType& rhs);
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 97a3b71..72b5a94 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -272,9 +272,12 @@
DCHECK(Runtime::Current()->UseJitCompilation());
DCHECK(!method->IsRuntimeMethod());
+ RuntimeCallbacks* cb = Runtime::Current()->GetRuntimeCallbacks();
// Don't compile the method if it has breakpoints.
- if (Dbg::IsDebuggerActive() && Dbg::MethodHasAnyBreakpoints(method)) {
- VLOG(jit) << "JIT not compiling " << method->PrettyMethod() << " due to breakpoint";
+ if (cb->IsMethodBeingInspected(method) && !cb->IsMethodSafeToJit(method)) {
+ VLOG(jit) << "JIT not compiling " << method->PrettyMethod()
+ << " due to not being safe to jit according to runtime-callbacks. For example, there"
+ << " could be breakpoints in this method.";
return false;
}
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 19501de..805b9c1 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -34,6 +34,7 @@
#include "base/arena_allocator.h"
#include "base/dumpable.h"
+#include "base/file_utils.h"
#include "base/mutex.h"
#include "base/scoped_flock.h"
#include "base/stl_util.h"
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 743604c..7f68d2f 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -34,6 +34,7 @@
#include "base/allocator.h"
#include "base/bit_utils.h"
+#include "base/file_utils.h"
#include "base/memory_tool.h"
#include "globals.h"
#include "utils.h"
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 65f39e4..5a5d571 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -355,15 +355,6 @@
num_method_params);
}
-inline bool IsMethodHandleInvokeExact(const ArtMethod* const method) {
- if (method == jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact)) {
- return true;
- } else {
- DCHECK_EQ(method, jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invoke));
- return false;
- }
-}
-
inline bool IsInvoke(const mirror::MethodHandle::Kind handle_kind) {
return handle_kind <= mirror::MethodHandle::Kind::kLastInvokeKind;
}
@@ -416,15 +407,14 @@
}
template <bool is_range>
-static inline bool DoCallPolymorphic(ArtMethod* called_method,
- Handle<mirror::MethodType> callsite_type,
- Handle<mirror::MethodType> target_type,
- Thread* self,
- ShadowFrame& shadow_frame,
- const uint32_t (&args)[Instruction::kMaxVarArgRegs],
- uint32_t first_arg,
- JValue* result)
- REQUIRES_SHARED(Locks::mutator_lock_) {
+static inline bool MethodHandleInvokeMethod(ArtMethod* called_method,
+ Handle<mirror::MethodType> callsite_type,
+ Handle<mirror::MethodType> target_type,
+ Thread* self,
+ ShadowFrame& shadow_frame,
+ const uint32_t (&args)[Instruction::kMaxVarArgRegs],
+ uint32_t first_arg,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
// Compute method information.
const DexFile::CodeItem* code_item = called_method->GetCodeItem();
@@ -552,15 +542,15 @@
}
template <bool is_range>
-static inline bool DoCallTransform(ArtMethod* called_method,
- Handle<mirror::MethodType> callsite_type,
- Handle<mirror::MethodType> callee_type,
- Thread* self,
- ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandle> receiver,
- const uint32_t (&args)[Instruction::kMaxVarArgRegs],
- uint32_t first_arg,
- JValue* result)
+static inline bool MethodHandleInvokeTransform(ArtMethod* called_method,
+ Handle<mirror::MethodType> callsite_type,
+ Handle<mirror::MethodType> callee_type,
+ Thread* self,
+ ShadowFrame& shadow_frame,
+ Handle<mirror::MethodHandle> receiver,
+ const uint32_t (&args)[Instruction::kMaxVarArgRegs],
+ uint32_t first_arg,
+ JValue* result)
REQUIRES_SHARED(Locks::mutator_lock_) {
// This can be fixed to two, because the method we're calling here
// (MethodHandle.transformInternal) doesn't have any locals and the signature
@@ -753,34 +743,34 @@
Handle<mirror::MethodType> callee_type =
(handle_kind == mirror::MethodHandle::Kind::kInvokeCallSiteTransform) ? callsite_type
: handle_type;
- return DoCallTransform<is_range>(called_method,
- callsite_type,
- callee_type,
- self,
- shadow_frame,
- method_handle /* receiver */,
- args,
- first_arg,
- result);
+ return MethodHandleInvokeTransform<is_range>(called_method,
+ callsite_type,
+ callee_type,
+ self,
+ shadow_frame,
+ method_handle /* receiver */,
+ args,
+ first_arg,
+ result);
} else {
- return DoCallPolymorphic<is_range>(called_method,
- callsite_type,
- handle_type,
- self,
- shadow_frame,
- args,
- first_arg,
- result);
+ return MethodHandleInvokeMethod<is_range>(called_method,
+ callsite_type,
+ handle_type,
+ self,
+ shadow_frame,
+ args,
+ first_arg,
+ result);
}
}
// Helper for getters in invoke-polymorphic.
-inline static void DoFieldGetForInvokePolymorphic(Thread* self,
- const ShadowFrame& shadow_frame,
- ObjPtr<mirror::Object>& obj,
- ArtField* field,
- Primitive::Type field_type,
- JValue* result)
+inline static void MethodHandleFieldGet(Thread* self,
+ const ShadowFrame& shadow_frame,
+ ObjPtr<mirror::Object>& obj,
+ ArtField* field,
+ Primitive::Type field_type,
+ JValue* result)
REQUIRES_SHARED(Locks::mutator_lock_) {
switch (field_type) {
case Primitive::kPrimBoolean:
@@ -817,12 +807,12 @@
}
// Helper for setters in invoke-polymorphic.
-inline bool DoFieldPutForInvokePolymorphic(Thread* self,
- ShadowFrame& shadow_frame,
- ObjPtr<mirror::Object>& obj,
- ArtField* field,
- Primitive::Type field_type,
- JValue& value)
+inline bool MethodHandleFieldPut(Thread* self,
+ ShadowFrame& shadow_frame,
+ ObjPtr<mirror::Object>& obj,
+ ArtField* field,
+ Primitive::Type field_type,
+ JValue& value)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(!Runtime::Current()->IsActiveTransaction());
static const bool kTransaction = false; // Not in a transaction.
@@ -895,14 +885,13 @@
}
template <bool is_range, bool do_conversions>
-bool DoInvokePolymorphicFieldAccess(Thread* self,
- ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandle> method_handle,
- Handle<mirror::MethodType> callsite_type,
- const uint32_t (&args)[Instruction::kMaxVarArgRegs],
- uint32_t first_arg,
- JValue* result)
- REQUIRES_SHARED(Locks::mutator_lock_) {
+bool MethodHandleFieldAccess(Thread* self,
+ ShadowFrame& shadow_frame,
+ Handle<mirror::MethodHandle> method_handle,
+ Handle<mirror::MethodType> callsite_type,
+ const uint32_t (&args)[Instruction::kMaxVarArgRegs],
+ uint32_t first_arg,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
StackHandleScope<1> hs(self);
Handle<mirror::MethodType> handle_type(hs.NewHandle(method_handle->GetMethodType()));
const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind();
@@ -913,7 +902,7 @@
case mirror::MethodHandle::kInstanceGet: {
size_t obj_reg = is_range ? first_arg : args[0];
ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(obj_reg);
- DoFieldGetForInvokePolymorphic(self, shadow_frame, obj, field, field_type, result);
+ MethodHandleFieldGet(self, shadow_frame, obj, field, field_type, result);
if (do_conversions && !ConvertReturnValue(callsite_type, handle_type, result)) {
DCHECK(self->IsExceptionPending());
return false;
@@ -926,7 +915,7 @@
DCHECK(self->IsExceptionPending());
return false;
}
- DoFieldGetForInvokePolymorphic(self, shadow_frame, obj, field, field_type, result);
+ MethodHandleFieldGet(self, shadow_frame, obj, field, field_type, result);
if (do_conversions && !ConvertReturnValue(callsite_type, handle_type, result)) {
DCHECK(self->IsExceptionPending());
return false;
@@ -951,7 +940,7 @@
return false;
}
ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(obj_reg);
- return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value);
+ return MethodHandleFieldPut(self, shadow_frame, obj, field, field_type, value);
}
case mirror::MethodHandle::kStaticPut: {
ObjPtr<mirror::Object> obj = GetAndInitializeDeclaringClass(self, field);
@@ -974,7 +963,7 @@
DCHECK(self->IsExceptionPending());
return false;
}
- return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value);
+ return MethodHandleFieldPut(self, shadow_frame, obj, field, field_type, value);
}
default:
LOG(FATAL) << "Unreachable: " << handle_kind;
@@ -983,26 +972,24 @@
}
template <bool is_range>
-static inline bool DoInvokePolymorphicNonExact(Thread* self,
- ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandle> method_handle,
- Handle<mirror::MethodType> callsite_type,
- const uint32_t (&args)[Instruction::kMaxVarArgRegs],
- uint32_t first_arg,
- JValue* result)
+static inline bool MethodHandleInvokeInternal(Thread* self,
+ ShadowFrame& shadow_frame,
+ Handle<mirror::MethodHandle> method_handle,
+ Handle<mirror::MethodType> callsite_type,
+ const uint32_t (&args)[Instruction::kMaxVarArgRegs],
+ uint32_t first_arg,
+ JValue* result)
REQUIRES_SHARED(Locks::mutator_lock_) {
const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind();
- ObjPtr<mirror::MethodType> handle_type(method_handle->GetMethodType());
- CHECK(handle_type != nullptr);
-
if (IsFieldAccess(handle_kind)) {
+ ObjPtr<mirror::MethodType> handle_type(method_handle->GetMethodType());
DCHECK(!callsite_type->IsExactMatch(handle_type.Ptr()));
if (!callsite_type->IsConvertible(handle_type.Ptr())) {
ThrowWrongMethodTypeException(handle_type.Ptr(), callsite_type.Get());
return false;
}
const bool do_convert = true;
- return DoInvokePolymorphicFieldAccess<is_range, do_convert>(
+ return MethodHandleFieldAccess<is_range, do_convert>(
self,
shadow_frame,
method_handle,
@@ -1011,7 +998,6 @@
first_arg,
result);
}
-
return DoInvokePolymorphicMethod<is_range>(self,
shadow_frame,
method_handle,
@@ -1022,27 +1008,32 @@
}
template <bool is_range>
-bool DoInvokePolymorphicExact(Thread* self,
- ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandle> method_handle,
- Handle<mirror::MethodType> callsite_type,
- const uint32_t (&args)[Instruction::kMaxVarArgRegs],
- uint32_t first_arg,
- JValue* result)
+static inline bool MethodHandleInvokeExactInternal(
+ Thread* self,
+ ShadowFrame& shadow_frame,
+ Handle<mirror::MethodHandle> method_handle,
+ Handle<mirror::MethodType> callsite_type,
+ const uint32_t (&args)[Instruction::kMaxVarArgRegs],
+ uint32_t first_arg,
+ JValue* result)
REQUIRES_SHARED(Locks::mutator_lock_) {
StackHandleScope<1> hs(self);
- const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind();
Handle<mirror::MethodType> method_handle_type(hs.NewHandle(method_handle->GetMethodType()));
+ if (!callsite_type->IsExactMatch(method_handle_type.Get())) {
+ ThrowWrongMethodTypeException(method_handle_type.Get(), callsite_type.Get());
+ return false;
+ }
+
+ const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind();
if (IsFieldAccess(handle_kind)) {
const bool do_convert = false;
- return DoInvokePolymorphicFieldAccess<is_range, do_convert>(
- self,
- shadow_frame,
- method_handle,
- callsite_type,
- args,
- first_arg,
- result);
+ return MethodHandleFieldAccess<is_range, do_convert>(self,
+ shadow_frame,
+ method_handle,
+ callsite_type,
+ args,
+ first_arg,
+ result);
}
// Slow-path check.
@@ -1120,77 +1111,77 @@
} // namespace
template <bool is_range>
-bool DoInvokePolymorphic(Thread* self,
- ArtMethod* invoke_method,
- ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandle> method_handle,
- Handle<mirror::MethodType> callsite_type,
- const uint32_t (&args)[Instruction::kMaxVarArgRegs],
- uint32_t first_arg,
- JValue* result)
+inline bool MethodHandleInvoke(Thread* self,
+ ShadowFrame& shadow_frame,
+ Handle<mirror::MethodHandle> method_handle,
+ Handle<mirror::MethodType> callsite_type,
+ const uint32_t (&args)[Instruction::kMaxVarArgRegs],
+ uint32_t first_arg,
+ JValue* result)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ObjPtr<mirror::MethodType> method_handle_type = method_handle->GetMethodType();
- if (IsMethodHandleInvokeExact(invoke_method)) {
- // We need to check the nominal type of the handle in addition to the
- // real type. The "nominal" type is present when MethodHandle.asType is
- // called any handle, and results in the declared type of the handle
- // changing.
- ObjPtr<mirror::MethodType> nominal_type(method_handle->GetNominalType());
- if (UNLIKELY(nominal_type != nullptr)) {
- if (UNLIKELY(!callsite_type->IsExactMatch(nominal_type.Ptr()))) {
- ThrowWrongMethodTypeException(nominal_type.Ptr(), callsite_type.Get());
- return false;
- }
-
- if (LIKELY(!nominal_type->IsExactMatch(method_handle_type.Ptr()))) {
- // Different nominal type means we have to treat as non-exact.
- return DoInvokePolymorphicNonExact<is_range>(self,
+ if (UNLIKELY(callsite_type->IsExactMatch(method_handle->GetMethodType()))) {
+ // A non-exact invoke that can be invoked exactly.
+ return MethodHandleInvokeExactInternal<is_range>(self,
shadow_frame,
method_handle,
callsite_type,
args,
first_arg,
result);
- }
- }
-
- if (!callsite_type->IsExactMatch(method_handle_type.Ptr())) {
- ThrowWrongMethodTypeException(method_handle_type.Ptr(), callsite_type.Get());
- return false;
- }
- return DoInvokePolymorphicExact<is_range>(self,
- shadow_frame,
- method_handle,
- callsite_type,
- args,
- first_arg,
- result);
} else {
- if (UNLIKELY(callsite_type->IsExactMatch(method_handle_type.Ptr()))) {
- // A non-exact invoke that can be invoked exactly.
- return DoInvokePolymorphicExact<is_range>(self,
+ return MethodHandleInvokeInternal<is_range>(self,
shadow_frame,
method_handle,
callsite_type,
args,
first_arg,
result);
- }
- return DoInvokePolymorphicNonExact<is_range>(self,
- shadow_frame,
- method_handle,
- callsite_type,
- args,
- first_arg,
- result);
}
}
-#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range) \
+template <bool is_range>
+bool MethodHandleInvokeExact(Thread* self,
+ ShadowFrame& shadow_frame,
+ Handle<mirror::MethodHandle> method_handle,
+ Handle<mirror::MethodType> callsite_type,
+ const uint32_t (&args)[Instruction::kMaxVarArgRegs],
+ uint32_t first_arg,
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // We need to check the nominal type of the handle in addition to the
+ // real type. The "nominal" type is present when MethodHandle.asType is
+ // called any handle, and results in the declared type of the handle
+ // changing.
+ ObjPtr<mirror::MethodType> nominal_type(method_handle->GetNominalType());
+ if (UNLIKELY(nominal_type != nullptr)) {
+ if (UNLIKELY(!callsite_type->IsExactMatch(nominal_type.Ptr()))) {
+ ThrowWrongMethodTypeException(nominal_type.Ptr(), callsite_type.Get());
+ return false;
+ }
+ if (LIKELY(!nominal_type->IsExactMatch(method_handle->GetMethodType()))) {
+ // Different nominal type means we have to treat as non-exact.
+ return MethodHandleInvokeInternal<is_range>(self,
+ shadow_frame,
+ method_handle,
+ callsite_type,
+ args,
+ first_arg,
+ result);
+ }
+ }
+ return MethodHandleInvokeExactInternal<is_range>(self,
+ shadow_frame,
+ method_handle,
+ callsite_type,
+ args,
+ first_arg,
+ result);
+}
+
+#define EXPLICIT_DO_METHOD_HANDLE_METHOD(_name, _is_range) \
template REQUIRES_SHARED(Locks::mutator_lock_) \
- bool DoInvokePolymorphic<_is_range>( \
+ bool MethodHandle##_name<_is_range>( \
Thread* self, \
- ArtMethod* invoke_method, \
ShadowFrame& shadow_frame, \
Handle<mirror::MethodHandle> method_handle, \
Handle<mirror::MethodType> callsite_type, \
@@ -1198,8 +1189,10 @@
uint32_t first_arg, \
JValue* result)
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false);
-#undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL
+EXPLICIT_DO_METHOD_HANDLE_METHOD(Invoke, true);
+EXPLICIT_DO_METHOD_HANDLE_METHOD(Invoke, false);
+EXPLICIT_DO_METHOD_HANDLE_METHOD(InvokeExact, true);
+EXPLICIT_DO_METHOD_HANDLE_METHOD(InvokeExact, false);
+#undef EXPLICIT_DO_METHOD_HANDLE_METHOD
} // namespace art
diff --git a/runtime/method_handles.h b/runtime/method_handles.h
index 55680f0..8641918 100644
--- a/runtime/method_handles.h
+++ b/runtime/method_handles.h
@@ -202,14 +202,23 @@
};
template <bool is_range>
-bool DoInvokePolymorphic(Thread* self,
- ArtMethod* invoke_method,
- ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandle> method_handle,
- Handle<mirror::MethodType> callsite_type,
- const uint32_t (&args)[Instruction::kMaxVarArgRegs],
- uint32_t first_arg,
- JValue* result)
+bool MethodHandleInvoke(Thread* self,
+ ShadowFrame& shadow_frame,
+ Handle<mirror::MethodHandle> method_handle,
+ Handle<mirror::MethodType> callsite_type,
+ const uint32_t (&args)[Instruction::kMaxVarArgRegs],
+ uint32_t first_arg,
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+template <bool is_range>
+bool MethodHandleInvokeExact(Thread* self,
+ ShadowFrame& shadow_frame,
+ Handle<mirror::MethodHandle> method_handle,
+ Handle<mirror::MethodType> callsite_type,
+ const uint32_t (&args)[Instruction::kMaxVarArgRegs],
+ uint32_t first_arg,
+ JValue* result)
REQUIRES_SHARED(Locks::mutator_lock_);
} // namespace art
diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc
index a6129cc..f82bfbf 100644
--- a/runtime/mirror/emulated_stack_frame.cc
+++ b/runtime/mirror/emulated_stack_frame.cc
@@ -289,7 +289,7 @@
static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
}
-// Explicit DoInvokePolymorphic template function declarations.
+// Explicit CreateFromShadowFrameAndArgs template function declarations.
#define EXPLICIT_CREATE_FROM_SHADOW_FRAME_AND_ARGS_DECL(_is_range) \
template REQUIRES_SHARED(Locks::mutator_lock_) \
mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs<_is_range>( \
diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc
new file mode 100644
index 0000000..e7eac1a
--- /dev/null
+++ b/runtime/mirror/var_handle.cc
@@ -0,0 +1,399 @@
+/*
+ * 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 "var_handle.h"
+
+#include "class-inl.h"
+#include "class_linker.h"
+#include "gc_root-inl.h"
+#include "method_type.h"
+
+namespace art {
+namespace mirror {
+
+namespace {
+
+// Enumeration for describing the parameter and return types of an AccessMode.
+enum class AccessModeTemplate : uint32_t {
+ kGet, // T Op(C0..CN)
+ kSet, // void Op(C0..CN, T)
+ kCompareAndSet, // boolean Op(C0..CN, T, T)
+ kCompareAndExchange, // T Op(C0..CN, T, T)
+ kGetAndUpdate, // T Op(C0..CN, T)
+};
+
+// Look up the AccessModeTemplate for a given VarHandle
+// AccessMode. This simplifies finding the correct signature for a
+// VarHandle accessor method.
+AccessModeTemplate GetAccessModeTemplate(VarHandle::AccessMode access_mode) {
+ switch (access_mode) {
+ case VarHandle::AccessMode::kGet:
+ return AccessModeTemplate::kGet;
+ case VarHandle::AccessMode::kSet:
+ return AccessModeTemplate::kSet;
+ case VarHandle::AccessMode::kGetVolatile:
+ return AccessModeTemplate::kGet;
+ case VarHandle::AccessMode::kSetVolatile:
+ return AccessModeTemplate::kSet;
+ case VarHandle::AccessMode::kGetAcquire:
+ return AccessModeTemplate::kGet;
+ case VarHandle::AccessMode::kSetRelease:
+ return AccessModeTemplate::kSet;
+ case VarHandle::AccessMode::kGetOpaque:
+ return AccessModeTemplate::kGet;
+ case VarHandle::AccessMode::kSetOpaque:
+ return AccessModeTemplate::kSet;
+ case VarHandle::AccessMode::kCompareAndSet:
+ return AccessModeTemplate::kCompareAndSet;
+ case VarHandle::AccessMode::kCompareAndExchange:
+ return AccessModeTemplate::kCompareAndExchange;
+ case VarHandle::AccessMode::kCompareAndExchangeAcquire:
+ return AccessModeTemplate::kCompareAndExchange;
+ case VarHandle::AccessMode::kCompareAndExchangeRelease:
+ return AccessModeTemplate::kCompareAndExchange;
+ case VarHandle::AccessMode::kWeakCompareAndSetPlain:
+ return AccessModeTemplate::kCompareAndSet;
+ case VarHandle::AccessMode::kWeakCompareAndSet:
+ return AccessModeTemplate::kCompareAndSet;
+ case VarHandle::AccessMode::kWeakCompareAndSetAcquire:
+ return AccessModeTemplate::kCompareAndSet;
+ case VarHandle::AccessMode::kWeakCompareAndSetRelease:
+ return AccessModeTemplate::kCompareAndSet;
+ case VarHandle::AccessMode::kGetAndSet:
+ return AccessModeTemplate::kGetAndUpdate;
+ case VarHandle::AccessMode::kGetAndSetAcquire:
+ return AccessModeTemplate::kGetAndUpdate;
+ case VarHandle::AccessMode::kGetAndSetRelease:
+ return AccessModeTemplate::kGetAndUpdate;
+ case VarHandle::AccessMode::kGetAndAdd:
+ return AccessModeTemplate::kGetAndUpdate;
+ case VarHandle::AccessMode::kGetAndAddAcquire:
+ return AccessModeTemplate::kGetAndUpdate;
+ case VarHandle::AccessMode::kGetAndAddRelease:
+ return AccessModeTemplate::kGetAndUpdate;
+ case VarHandle::AccessMode::kGetAndBitwiseOr:
+ return AccessModeTemplate::kGetAndUpdate;
+ case VarHandle::AccessMode::kGetAndBitwiseOrRelease:
+ return AccessModeTemplate::kGetAndUpdate;
+ case VarHandle::AccessMode::kGetAndBitwiseOrAcquire:
+ return AccessModeTemplate::kGetAndUpdate;
+ case VarHandle::AccessMode::kGetAndBitwiseAnd:
+ return AccessModeTemplate::kGetAndUpdate;
+ case VarHandle::AccessMode::kGetAndBitwiseAndRelease:
+ return AccessModeTemplate::kGetAndUpdate;
+ case VarHandle::AccessMode::kGetAndBitwiseAndAcquire:
+ return AccessModeTemplate::kGetAndUpdate;
+ case VarHandle::AccessMode::kGetAndBitwiseXor:
+ return AccessModeTemplate::kGetAndUpdate;
+ case VarHandle::AccessMode::kGetAndBitwiseXorRelease:
+ return AccessModeTemplate::kGetAndUpdate;
+ case VarHandle::AccessMode::kGetAndBitwiseXorAcquire:
+ return AccessModeTemplate::kGetAndUpdate;
+ }
+}
+
+// Returns the number of parameters associated with an
+// AccessModeTemplate and the supplied coordinate types.
+int32_t GetParameterCount(AccessModeTemplate access_mode_template,
+ ObjPtr<Class> coordinateType0,
+ ObjPtr<Class> coordinateType1) {
+ int32_t index = 0;
+ if (!coordinateType0.IsNull()) {
+ index++;
+ if (!coordinateType1.IsNull()) {
+ index++;
+ }
+ }
+
+ switch (access_mode_template) {
+ case AccessModeTemplate::kGet:
+ return index;
+ case AccessModeTemplate::kSet:
+ case AccessModeTemplate::kGetAndUpdate:
+ return index + 1;
+ case AccessModeTemplate::kCompareAndSet:
+ case AccessModeTemplate::kCompareAndExchange:
+ return index + 2;
+ }
+ UNREACHABLE();
+}
+
+// Writes the parameter types associated with the AccessModeTemplate
+// into an array. The parameter types are derived from the specified
+// variable type and coordinate types. Returns the number of
+// parameters written.
+int32_t BuildParameterArray(ObjPtr<Class> (¶meters)[VarHandle::kMaxAccessorParameters],
+ AccessModeTemplate access_mode_template,
+ ObjPtr<Class> varType,
+ ObjPtr<Class> coordinateType0,
+ ObjPtr<Class> coordinateType1)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(varType != nullptr);
+ int32_t index = 0;
+ if (!coordinateType0.IsNull()) {
+ parameters[index++] = coordinateType0;
+ if (!coordinateType1.IsNull()) {
+ parameters[index++] = coordinateType1;
+ }
+ } else {
+ DCHECK(coordinateType1.IsNull());
+ }
+
+ switch (access_mode_template) {
+ case AccessModeTemplate::kCompareAndExchange:
+ case AccessModeTemplate::kCompareAndSet:
+ parameters[index++] = varType;
+ parameters[index++] = varType;
+ return index;
+ case AccessModeTemplate::kGet:
+ return index;
+ case AccessModeTemplate::kGetAndUpdate:
+ case AccessModeTemplate::kSet:
+ parameters[index++] = varType;
+ return index;
+ }
+ return -1;
+}
+
+// Returns the return type associated with an AccessModeTemplate based
+// on the template and the variable type specified.
+Class* GetReturnType(AccessModeTemplate access_mode_template, ObjPtr<Class> varType)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(varType != nullptr);
+ switch (access_mode_template) {
+ case AccessModeTemplate::kCompareAndSet:
+ return Runtime::Current()->GetClassLinker()->FindPrimitiveClass('Z');
+ case AccessModeTemplate::kCompareAndExchange:
+ case AccessModeTemplate::kGet:
+ case AccessModeTemplate::kGetAndUpdate:
+ return varType.Ptr();
+ case AccessModeTemplate::kSet:
+ return Runtime::Current()->GetClassLinker()->FindPrimitiveClass('V');
+ }
+ return nullptr;
+}
+
+ObjectArray<Class>* NewArrayOfClasses(Thread* self, int count)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ Runtime* const runtime = Runtime::Current();
+ ClassLinker* const class_linker = runtime->GetClassLinker();
+ ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass();
+ ObjPtr<mirror::Class> array_of_class = class_linker->FindArrayClass(self, &class_type);
+ return ObjectArray<Class>::Alloc(Thread::Current(), array_of_class, count);
+}
+
+} // namespace
+
+Class* VarHandle::GetVarType() {
+ return GetFieldObject<Class>(VarTypeOffset());
+}
+
+Class* VarHandle::GetCoordinateType0() {
+ return GetFieldObject<Class>(CoordinateType0Offset());
+}
+
+Class* VarHandle::GetCoordinateType1() {
+ return GetFieldObject<Class>(CoordinateType1Offset());
+}
+
+int32_t VarHandle::GetAccessModesBitMask() {
+ return GetField32(AccessModesBitMaskOffset());
+}
+
+bool VarHandle::IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type) {
+ ScopedAssertNoThreadSuspension ants(__FUNCTION__);
+
+ AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode);
+ // Check return types first.
+ ObjPtr<Class> var_type = GetVarType();
+ ObjPtr<Class> vh_rtype = GetReturnType(access_mode_template, var_type);
+ ObjPtr<Class> void_type = Runtime::Current()->GetClassLinker()->FindPrimitiveClass('V');
+ ObjPtr<Class> mt_rtype = method_type->GetRType();
+
+ // If the mt_rtype is void, the result of the operation will be discarded (okay).
+ if (mt_rtype != void_type && mt_rtype != vh_rtype) {
+ return false;
+ }
+
+ // Check the number of parameters matches.
+ ObjPtr<Class> vh_ptypes[VarHandle::kMaxAccessorParameters];
+ const int32_t vh_ptypes_count = BuildParameterArray(vh_ptypes,
+ access_mode_template,
+ var_type,
+ GetCoordinateType0(),
+ GetCoordinateType1());
+ if (vh_ptypes_count != method_type->GetPTypes()->GetLength()) {
+ return false;
+ }
+
+ // Check the parameter types match.
+ ObjPtr<ObjectArray<Class>> mt_ptypes = method_type->GetPTypes();
+ for (int32_t i = 0; i < vh_ptypes_count; ++i) {
+ if (mt_ptypes->Get(i) != vh_ptypes[i].Ptr()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self,
+ ObjPtr<VarHandle> var_handle,
+ AccessMode access_mode) {
+ // This is a static as the var_handle might be moved by the GC during it's execution.
+ AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode);
+
+ StackHandleScope<3> hs(self);
+ Handle<VarHandle> vh = hs.NewHandle(var_handle);
+ Handle<Class> rtype = hs.NewHandle(GetReturnType(access_mode_template, vh->GetVarType()));
+ const int32_t ptypes_count =
+ GetParameterCount(access_mode_template, vh->GetCoordinateType0(), vh->GetCoordinateType1());
+ Handle<ObjectArray<Class>> ptypes = hs.NewHandle(NewArrayOfClasses(self, ptypes_count));
+ if (ptypes == nullptr) {
+ return nullptr;
+ }
+
+ ObjPtr<Class> ptypes_array[VarHandle::kMaxAccessorParameters];
+ BuildParameterArray(ptypes_array,
+ access_mode_template,
+ vh->GetVarType(),
+ vh->GetCoordinateType0(),
+ vh->GetCoordinateType1());
+ for (int32_t i = 0; i < ptypes_count; ++i) {
+ ptypes->Set(i, ptypes_array[i].Ptr());
+ }
+ return MethodType::Create(self, rtype, ptypes);
+}
+
+MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, AccessMode access_mode) {
+ return GetMethodTypeForAccessMode(self, this, access_mode);
+}
+
+void VarHandle::SetClass(Class* klass) {
+ CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+ CHECK(klass != nullptr);
+ static_class_ = GcRoot<Class>(klass);
+}
+
+void VarHandle::ResetClass() {
+ CHECK(!static_class_.IsNull());
+ static_class_ = GcRoot<Class>(nullptr);
+}
+
+void VarHandle::VisitRoots(RootVisitor* visitor) {
+ static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+GcRoot<Class> VarHandle::static_class_;
+
+ArtField* FieldVarHandle::GetField() {
+ uintptr_t opaque_field = static_cast<uintptr_t>(GetField64(ArtFieldOffset()));
+ return reinterpret_cast<ArtField*>(opaque_field);
+}
+
+Class* FieldVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return static_class_.Read();
+}
+
+void FieldVarHandle::SetClass(Class* klass) {
+ CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+ CHECK(klass != nullptr);
+ static_class_ = GcRoot<Class>(klass);
+}
+
+void FieldVarHandle::ResetClass() {
+ CHECK(!static_class_.IsNull());
+ static_class_ = GcRoot<Class>(nullptr);
+}
+
+void FieldVarHandle::VisitRoots(RootVisitor* visitor) {
+ static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+GcRoot<Class> FieldVarHandle::static_class_;
+
+Class* ArrayElementVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return static_class_.Read();
+}
+
+void ArrayElementVarHandle::SetClass(Class* klass) {
+ CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+ CHECK(klass != nullptr);
+ static_class_ = GcRoot<Class>(klass);
+}
+
+void ArrayElementVarHandle::ResetClass() {
+ CHECK(!static_class_.IsNull());
+ static_class_ = GcRoot<Class>(nullptr);
+}
+
+void ArrayElementVarHandle::VisitRoots(RootVisitor* visitor) {
+ static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+GcRoot<Class> ArrayElementVarHandle::static_class_;
+
+bool ByteArrayViewVarHandle::GetNativeByteOrder() {
+ return GetFieldBoolean(NativeByteOrderOffset());
+}
+
+Class* ByteArrayViewVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return static_class_.Read();
+}
+
+void ByteArrayViewVarHandle::SetClass(Class* klass) {
+ CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+ CHECK(klass != nullptr);
+ static_class_ = GcRoot<Class>(klass);
+}
+
+void ByteArrayViewVarHandle::ResetClass() {
+ CHECK(!static_class_.IsNull());
+ static_class_ = GcRoot<Class>(nullptr);
+}
+
+void ByteArrayViewVarHandle::VisitRoots(RootVisitor* visitor) {
+ static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+GcRoot<Class> ByteArrayViewVarHandle::static_class_;
+
+bool ByteBufferViewVarHandle::GetNativeByteOrder() {
+ return GetFieldBoolean(NativeByteOrderOffset());
+}
+
+Class* ByteBufferViewVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return static_class_.Read();
+}
+
+void ByteBufferViewVarHandle::SetClass(Class* klass) {
+ CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+ CHECK(klass != nullptr);
+ static_class_ = GcRoot<Class>(klass);
+}
+
+void ByteBufferViewVarHandle::ResetClass() {
+ CHECK(!static_class_.IsNull());
+ static_class_ = GcRoot<Class>(nullptr);
+}
+
+void ByteBufferViewVarHandle::VisitRoots(RootVisitor* visitor) {
+ static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+GcRoot<Class> ByteBufferViewVarHandle::static_class_;
+
+} // namespace mirror
+} // namespace art
diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h
new file mode 100644
index 0000000..a2a5d8c
--- /dev/null
+++ b/runtime/mirror/var_handle.h
@@ -0,0 +1,255 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_MIRROR_VAR_HANDLE_H_
+#define ART_RUNTIME_MIRROR_VAR_HANDLE_H_
+
+#include "handle.h"
+#include "gc_root.h"
+#include "object.h"
+
+namespace art {
+
+template<class T> class Handle;
+struct VarHandleOffsets;
+struct FieldVarHandleOffsets;
+struct ByteArrayViewVarHandleOffsets;
+struct ByteBufferViewVarHandleOffsets;
+
+namespace mirror {
+
+class MethodType;
+class VarHandleTest;
+
+// C++ mirror of java.lang.invoke.VarHandle
+class MANAGED VarHandle : public Object {
+ public:
+ // The maximum number of parameters a VarHandle accessor method can
+ // take. The Worst case is equivalent to a compare-and-swap
+ // operation on an array element which requires four parameters
+ // (array, index, old, new).
+ static constexpr int kMaxAccessorParameters = 4;
+
+ // Enumeration of the possible access modes. This mirrors the enum
+ // in java.lang.invoke.VarHandle.
+ enum class AccessMode : uint32_t {
+ kGet,
+ kSet,
+ kGetVolatile,
+ kSetVolatile,
+ kGetAcquire,
+ kSetRelease,
+ kGetOpaque,
+ kSetOpaque,
+ kCompareAndSet,
+ kCompareAndExchange,
+ kCompareAndExchangeAcquire,
+ kCompareAndExchangeRelease,
+ kWeakCompareAndSetPlain,
+ kWeakCompareAndSet,
+ kWeakCompareAndSetAcquire,
+ kWeakCompareAndSetRelease,
+ kGetAndSet,
+ kGetAndSetAcquire,
+ kGetAndSetRelease,
+ kGetAndAdd,
+ kGetAndAddAcquire,
+ kGetAndAddRelease,
+ kGetAndBitwiseOr,
+ kGetAndBitwiseOrRelease,
+ kGetAndBitwiseOrAcquire,
+ kGetAndBitwiseAnd,
+ kGetAndBitwiseAndRelease,
+ kGetAndBitwiseAndAcquire,
+ kGetAndBitwiseXor,
+ kGetAndBitwiseXorRelease,
+ kGetAndBitwiseXorAcquire,
+ };
+
+ // Returns true if the AccessMode specified is a supported operation.
+ bool IsAccessModeSupported(AccessMode accessMode) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return (GetAccessModesBitMask() & (1u << static_cast<uint32_t>(accessMode))) != 0;
+ }
+
+ // Returns true if the MethodType specified is compatible with the
+ // method type associated with the specified AccessMode. The
+ // supplied MethodType is assumed to be from the point of invocation
+ // so it is valid for the supplied MethodType to have a void return
+ // value when the return value for the AccessMode is non-void. This
+ // corresponds to the result of the accessor being discarded.
+ bool IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Allocates and returns the MethodType associated with the
+ // AccessMode. No check is made for whether the AccessMode is a
+ // supported operation so the MethodType can be used when raising a
+ // WrongMethodTypeException exception.
+ MethodType* GetMethodTypeForAccessMode(Thread* self, AccessMode accessMode)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
+ static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return static_class_.Read();
+ }
+
+ static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+ static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
+ static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ Class* GetVarType() REQUIRES_SHARED(Locks::mutator_lock_);
+ Class* GetCoordinateType0() REQUIRES_SHARED(Locks::mutator_lock_);
+ Class* GetCoordinateType1() REQUIRES_SHARED(Locks::mutator_lock_);
+ int32_t GetAccessModesBitMask() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ static MethodType* GetMethodTypeForAccessMode(Thread* self,
+ ObjPtr<VarHandle> var_handle,
+ AccessMode access_mode)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ static MemberOffset VarTypeOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(VarHandle, var_type_));
+ }
+
+ static MemberOffset CoordinateType0Offset() {
+ return MemberOffset(OFFSETOF_MEMBER(VarHandle, coordinate_type0_));
+ }
+
+ static MemberOffset CoordinateType1Offset() {
+ return MemberOffset(OFFSETOF_MEMBER(VarHandle, coordinate_type1_));
+ }
+
+ static MemberOffset AccessModesBitMaskOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(VarHandle, access_modes_bit_mask_));
+ }
+
+ HeapReference<mirror::Class> coordinate_type0_;
+ HeapReference<mirror::Class> coordinate_type1_;
+ HeapReference<mirror::Class> var_type_;
+ int32_t access_modes_bit_mask_;
+
+ // Root representing java.lang.invoke.VarHandle.class.
+ static GcRoot<mirror::Class> static_class_;
+
+ friend class VarHandleTest; // for testing purposes
+ friend struct art::VarHandleOffsets; // for verifying offset information
+ DISALLOW_IMPLICIT_CONSTRUCTORS(VarHandle);
+};
+
+// Represents a VarHandle to a static or instance field.
+// The corresponding managed class in libart java.lang.invoke.FieldVarHandle.
+class MANAGED FieldVarHandle : public VarHandle {
+ public:
+ ArtField* GetField() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
+ static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+ static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
+ static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ static MemberOffset ArtFieldOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(FieldVarHandle, art_field_));
+ }
+
+ // ArtField instance corresponding to variable for accessors.
+ int64_t art_field_;
+
+ // Root representing java.lang.invoke.FieldVarHandle.class.
+ static GcRoot<mirror::Class> static_class_;
+
+ friend class VarHandleTest; // for var_handle_test.
+ friend struct art::FieldVarHandleOffsets; // for verifying offset information
+ DISALLOW_IMPLICIT_CONSTRUCTORS(FieldVarHandle);
+};
+
+// Represents a VarHandle providing accessors to an array.
+// The corresponding managed class in libart java.lang.invoke.ArrayElementVarHandle.
+class MANAGED ArrayElementVarHandle : public VarHandle {
+ public:
+ static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
+ static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+ static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
+ static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ // Root representing java.lang.invoke.ArrayElementVarHandle.class.
+ static GcRoot<mirror::Class> static_class_;
+
+ friend class VarHandleTest;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ArrayElementVarHandle);
+};
+
+// Represents a VarHandle providing accessors to a view of a ByteArray.
+// The corresponding managed class in libart java.lang.invoke.ByteArrayViewVarHandle.
+class MANAGED ByteArrayViewVarHandle : public VarHandle {
+ public:
+ bool GetNativeByteOrder() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
+ static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+ static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
+ static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ static MemberOffset NativeByteOrderOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(ByteArrayViewVarHandle, native_byte_order_));
+ }
+
+ // Flag indicating that accessors should use native byte-ordering.
+ uint8_t native_byte_order_;
+
+ // Root representing java.lang.invoke.ByteArrayViewVarHandle.class.
+ static GcRoot<mirror::Class> static_class_;
+
+ friend class VarHandleTest; // for var_handle_test.
+ friend struct art::ByteArrayViewVarHandleOffsets; // for verifying offset information
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ByteArrayViewVarHandle);
+};
+
+// Represents a VarHandle providing accessors to a view of a ByteBuffer
+// The corresponding managed class in libart java.lang.invoke.ByteBufferViewVarHandle.
+class MANAGED ByteBufferViewVarHandle : public VarHandle {
+ public:
+ bool GetNativeByteOrder() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ static ByteBufferViewVarHandle* Create(Thread* const self, bool native_byte_order)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
+ static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
+ static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+ static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
+ static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ static MemberOffset NativeByteOrderOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(ByteBufferViewVarHandle, native_byte_order_));
+ }
+
+ // Flag indicating that accessors should use native byte-ordering.
+ uint8_t native_byte_order_;
+
+ // Root representing java.lang.invoke.ByteBufferViewVarHandle.class.
+ static GcRoot<mirror::Class> static_class_;
+
+ friend class VarHandleTest; // for var_handle_test.
+ friend struct art::ByteBufferViewVarHandleOffsets; // for verifying offset information
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ByteBufferViewVarHandle);
+};
+
+} // namespace mirror
+} // namespace art
+
+#endif // ART_RUNTIME_MIRROR_VAR_HANDLE_H_
diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc
new file mode 100644
index 0000000..0e1c994
--- /dev/null
+++ b/runtime/mirror/var_handle_test.cc
@@ -0,0 +1,991 @@
+/*
+ * 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 "var_handle.h"
+
+#include <string>
+#include <vector>
+
+#include "art_field-inl.h"
+#include "class-inl.h"
+#include "class_linker-inl.h"
+#include "class_loader.h"
+#include "common_runtime_test.h"
+#include "handle_scope-inl.h"
+#include "jvalue-inl.h"
+#include "method_type.h"
+#include "object_array-inl.h"
+#include "reflection.h"
+#include "scoped_thread_state_change-inl.h"
+
+namespace art {
+namespace mirror {
+
+// Tests for mirror::VarHandle and it's descendents.
+class VarHandleTest : public CommonRuntimeTest {
+ public:
+ static FieldVarHandle* CreateFieldVarHandle(Thread* const self,
+ ArtField* art_field,
+ int32_t access_modes_bit_mask)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
+ StackHandleScope<4> hs(self);
+ Handle<FieldVarHandle> fvh = hs.NewHandle(
+ ObjPtr<FieldVarHandle>::DownCast(FieldVarHandle::StaticClass()->AllocObject(self)));
+ Handle<Class> var_type = hs.NewHandle(art_field->GetType<true>().Ptr());
+
+ if (art_field->IsStatic()) {
+ InitializeVarHandle(fvh.Get(), var_type, access_modes_bit_mask);
+ } else {
+ Handle<Class> declaring_type = hs.NewHandle(art_field->GetDeclaringClass().Ptr());
+ InitializeVarHandle(fvh.Get(),
+ var_type,
+ declaring_type,
+ access_modes_bit_mask);
+ }
+ uintptr_t opaque_field = reinterpret_cast<uintptr_t>(art_field);
+ fvh->SetField64<false>(FieldVarHandle::ArtFieldOffset(), opaque_field);
+ return fvh.Get();
+ }
+
+ static ArrayElementVarHandle* CreateArrayElementVarHandle(Thread* const self,
+ Handle<Class> array_class,
+ int32_t access_modes_bit_mask)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
+ StackHandleScope<3> hs(self);
+ Handle<ArrayElementVarHandle> vh = hs.NewHandle(
+ ObjPtr<ArrayElementVarHandle>::DownCast(
+ ArrayElementVarHandle::StaticClass()->AllocObject(self)));
+
+ // Initialize super class fields
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Handle<Class> var_type = hs.NewHandle(array_class->GetComponentType());
+ Handle<Class> index_type = hs.NewHandle(class_linker->FindPrimitiveClass('I'));
+ InitializeVarHandle(vh.Get(), var_type, array_class, index_type, access_modes_bit_mask);
+ return vh.Get();
+ }
+
+ static ByteArrayViewVarHandle* CreateByteArrayViewVarHandle(Thread* const self,
+ Handle<Class> view_array_class,
+ bool native_byte_order,
+ int32_t access_modes_bit_mask)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
+ StackHandleScope<4> hs(self);
+ Handle<ByteArrayViewVarHandle> bvh = hs.NewHandle(
+ ObjPtr<ByteArrayViewVarHandle>::DownCast(
+ ByteArrayViewVarHandle::StaticClass()->AllocObject(self)));
+
+ // Initialize super class fields
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Handle<Class> var_type = hs.NewHandle(view_array_class->GetComponentType());
+ Handle<Class> index_type = hs.NewHandle(class_linker->FindPrimitiveClass('I'));
+ ObjPtr<mirror::Class> byte_class = class_linker->FindPrimitiveClass('B');
+ Handle<Class> byte_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &byte_class)));
+ InitializeVarHandle(bvh.Get(), var_type, byte_array_class, index_type, access_modes_bit_mask);
+ bvh->SetFieldBoolean<false>(ByteArrayViewVarHandle::NativeByteOrderOffset(), native_byte_order);
+ return bvh.Get();
+ }
+
+ static ByteBufferViewVarHandle* CreateByteBufferViewVarHandle(Thread* const self,
+ Handle<Class> view_array_class,
+ bool native_byte_order,
+ int32_t access_modes_bit_mask)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
+ StackHandleScope<5> hs(self);
+ Handle<ByteBufferViewVarHandle> bvh = hs.NewHandle(
+ ObjPtr<ByteBufferViewVarHandle>::DownCast(
+ ByteArrayViewVarHandle::StaticClass()->AllocObject(self)));
+ // Initialize super class fields
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Handle<Class> var_type = hs.NewHandle(view_array_class->GetComponentType());
+ Handle<Class> index_type = hs.NewHandle(class_linker->FindPrimitiveClass('I'));
+ Handle<ClassLoader> boot_class_loader;
+ Handle<Class> byte_buffer_class = hs.NewHandle(
+ class_linker->FindSystemClass(self, "Ljava/nio/ByteBuffer;"));
+ InitializeVarHandle(bvh.Get(), var_type, byte_buffer_class, index_type, access_modes_bit_mask);
+ bvh->SetFieldBoolean<false>(ByteBufferViewVarHandle::NativeByteOrderOffset(),
+ native_byte_order);
+ return bvh.Get();
+ }
+
+ static int32_t AccessModesBitMask(VarHandle::AccessMode mode) {
+ return 1 << static_cast<int32_t>(mode);
+ }
+
+ template<typename... Args>
+ static int32_t AccessModesBitMask(VarHandle::AccessMode first, Args... args) {
+ return AccessModesBitMask(first) | AccessModesBitMask(args...);
+ }
+
+ // Helper to get the VarType of a VarHandle.
+ static Class* GetVarType(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return vh->GetVarType();
+ }
+
+ // Helper to get the CoordinateType0 of a VarHandle.
+ static Class* GetCoordinateType0(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return vh->GetCoordinateType0();
+ }
+
+ // Helper to get the CoordinateType1 of a VarHandle.
+ static Class* GetCoordinateType1(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return vh->GetCoordinateType1();
+ }
+
+ // Helper to get the AccessModesBitMask of a VarHandle.
+ static int32_t GetAccessModesBitMask(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return vh->GetAccessModesBitMask();
+ }
+
+ private:
+ static void InitializeVarHandle(VarHandle* vh,
+ Handle<Class> var_type,
+ int32_t access_modes_bit_mask)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ vh->SetFieldObject<false>(VarHandle::VarTypeOffset(), var_type.Get());
+ vh->SetField32<false>(VarHandle::AccessModesBitMaskOffset(), access_modes_bit_mask);
+ }
+
+ static void InitializeVarHandle(VarHandle* vh,
+ Handle<Class> var_type,
+ Handle<Class> coordinate_type0,
+ int32_t access_modes_bit_mask)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ InitializeVarHandle(vh, var_type, access_modes_bit_mask);
+ vh->SetFieldObject<false>(VarHandle::CoordinateType0Offset(), coordinate_type0.Get());
+ }
+
+ static void InitializeVarHandle(VarHandle* vh,
+ Handle<Class> var_type,
+ Handle<Class> coordinate_type0,
+ Handle<Class> coordinate_type1,
+ int32_t access_modes_bit_mask)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ InitializeVarHandle(vh, var_type, access_modes_bit_mask);
+ vh->SetFieldObject<false>(VarHandle::CoordinateType0Offset(), coordinate_type0.Get());
+ vh->SetFieldObject<false>(VarHandle::CoordinateType1Offset(), coordinate_type1.Get());
+ }
+};
+
+// Convenience method for constructing MethodType instances from
+// well-formed method descriptors.
+static MethodType* MethodTypeOf(const std::string& method_descriptor) {
+ std::vector<std::string> descriptors;
+
+ auto it = method_descriptor.cbegin();
+ if (*it++ != '(') {
+ LOG(FATAL) << "Bad descriptor: " << method_descriptor;
+ }
+
+ bool returnValueSeen = false;
+ const char* prefix = "";
+ for (; it != method_descriptor.cend() && !returnValueSeen; ++it) {
+ switch (*it) {
+ case ')':
+ descriptors.push_back(std::string(++it, method_descriptor.cend()));
+ returnValueSeen = true;
+ break;
+ case '[':
+ prefix = "[";
+ break;
+ case 'Z':
+ case 'B':
+ case 'C':
+ case 'S':
+ case 'I':
+ case 'J':
+ case 'F':
+ case 'D':
+ descriptors.push_back(prefix + std::string(it, it + 1));
+ prefix = "";
+ break;
+ case 'L': {
+ auto last = it + 1;
+ while (*last != ';') {
+ ++last;
+ }
+ descriptors.push_back(prefix + std::string(it, last + 1));
+ prefix = "";
+ it = last;
+ break;
+ }
+ default:
+ LOG(FATAL) << "Bad descriptor: " << method_descriptor;
+ }
+ }
+
+ Runtime* const runtime = Runtime::Current();
+ ClassLinker* const class_linker = runtime->GetClassLinker();
+ Thread* const self = Thread::Current();
+
+ ScopedObjectAccess soa(self);
+ StackHandleScope<3> hs(self);
+ int ptypes_count = static_cast<int>(descriptors.size()) - 1;
+ ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass();
+ ObjPtr<mirror::Class> array_of_class = class_linker->FindArrayClass(self, &class_type);
+ Handle<ObjectArray<Class>> ptypes = hs.NewHandle(
+ ObjectArray<Class>::Alloc(Thread::Current(), array_of_class, ptypes_count));
+ Handle<mirror::ClassLoader> boot_class_loader = hs.NewHandle<mirror::ClassLoader>(nullptr);
+ for (int i = 0; i < ptypes_count; ++i) {
+ ptypes->Set(i, class_linker->FindClass(self, descriptors[i].c_str(), boot_class_loader));
+ }
+ Handle<Class> rtype =
+ hs.NewHandle(class_linker->FindClass(self, descriptors.back().c_str(), boot_class_loader));
+ return MethodType::Create(self, rtype, ptypes);
+}
+
+TEST_F(VarHandleTest, InstanceFieldVarHandle) {
+ Thread * const self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ ObjPtr<Object> i = BoxPrimitive(Primitive::kPrimInt, JValue::FromPrimitive<int32_t>(37));
+ ArtField* value = mirror::Class::FindField(self, i->GetClass(), "value", "I");
+ int32_t mask = AccessModesBitMask(VarHandle::AccessMode::kGet,
+ VarHandle::AccessMode::kGetAndSet,
+ VarHandle::AccessMode::kGetAndBitwiseXor);
+ StackHandleScope<1> hs(self);
+ Handle<mirror::FieldVarHandle> fvh(hs.NewHandle(CreateFieldVarHandle(self, value, mask)));
+ EXPECT_FALSE(fvh.IsNull());
+ EXPECT_EQ(value, fvh->GetField());
+
+ // Check access modes
+ EXPECT_TRUE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGet));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kSet));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetVolatile));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kSetVolatile));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAcquire));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kSetRelease));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetOpaque));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kSetOpaque));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndSet));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchange));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeAcquire));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeRelease));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetPlain));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSet));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetAcquire));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetRelease));
+ EXPECT_TRUE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSet));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetAcquire));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetRelease));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAdd));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddAcquire));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddRelease));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOr));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrRelease));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrAcquire));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAnd));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndRelease));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndAcquire));
+ EXPECT_TRUE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXor));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorRelease));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorAcquire));
+
+ // Check compatibility - "Get" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
+ EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)I")));
+ EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)V")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+ }
+
+ // Check compatibility - "Set" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
+ EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)V")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)V")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+ }
+
+ // Check compatibility - "CompareAndSet" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
+ EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)Z")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode,
+ MethodTypeOf("(Ljava/lang/Integer;II)I")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+ }
+
+ // Check compatibility - "CompareAndExchange" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
+ EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)I")));
+ EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)V")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+ }
+
+ // Check compatibility - "GetAndUpdate" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
+ EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)I")));
+ EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)V")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+ }
+
+ // Check synthesized method types match expected forms.
+ {
+ MethodType* get = MethodTypeOf("(Ljava/lang/Integer;)I");
+ MethodType* set = MethodTypeOf("(Ljava/lang/Integer;I)V");
+ MethodType* compareAndSet = MethodTypeOf("(Ljava/lang/Integer;II)Z");
+ MethodType* compareAndExchange = MethodTypeOf("(Ljava/lang/Integer;II)I");
+ MethodType* getAndUpdate = MethodTypeOf("(Ljava/lang/Integer;I)I");
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGet)->IsExactMatch(get));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSet)->IsExactMatch(set));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetVolatile)->IsExactMatch(get));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetVolatile)->IsExactMatch(set));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAcquire)->IsExactMatch(get));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetRelease)->IsExactMatch(set));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetOpaque)->IsExactMatch(get));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetOpaque)->IsExactMatch(set));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndSet)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchange)->IsExactMatch(compareAndExchange));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeAcquire)->IsExactMatch(compareAndExchange));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeRelease)->IsExactMatch(compareAndExchange));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetPlain)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSet)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetAcquire)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetRelease)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSet)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAdd)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOr)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAnd)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXor)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorAcquire)->IsExactMatch(getAndUpdate));
+ }
+}
+
+TEST_F(VarHandleTest, StaticFieldVarHandle) {
+ Thread * const self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ ObjPtr<Object> i = BoxPrimitive(Primitive::kPrimInt, JValue::FromPrimitive<int32_t>(37));
+ ArtField* value = mirror::Class::FindField(self, i->GetClass(), "MIN_VALUE", "I");
+ int32_t mask = AccessModesBitMask(VarHandle::AccessMode::kSet,
+ VarHandle::AccessMode::kGetOpaque,
+ VarHandle::AccessMode::kGetAndBitwiseAndRelease);
+ StackHandleScope<1> hs(self);
+ Handle<mirror::FieldVarHandle> fvh(hs.NewHandle(CreateFieldVarHandle(self, value, mask)));
+ EXPECT_FALSE(fvh.IsNull());
+ EXPECT_EQ(value, fvh->GetField());
+
+ // Check access modes
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGet));
+ EXPECT_TRUE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kSet));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetVolatile));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kSetVolatile));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAcquire));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kSetRelease));
+ EXPECT_TRUE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetOpaque));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kSetOpaque));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndSet));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchange));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeAcquire));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeRelease));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetPlain));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSet));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetAcquire));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetRelease));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSet));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetAcquire));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetRelease));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAdd));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddAcquire));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddRelease));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOr));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrRelease));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrAcquire));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAnd));
+ EXPECT_TRUE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndRelease));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndAcquire));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXor));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorRelease));
+ EXPECT_FALSE(fvh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorAcquire));
+
+ // Check compatibility - "Get" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
+ EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()I")));
+ EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()V")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+ }
+
+ // Check compatibility - "Set" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
+ EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)V")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()V")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(F)V")));
+ }
+
+ // Check compatibility - "CompareAndSet" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
+ EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)Z")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode,
+ MethodTypeOf("(II)Ljava/lang/String;")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+ }
+
+ // Check compatibility - "CompareAndExchange" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
+ EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)I")));
+ EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(ID)I")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)D")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+ }
+
+ // Check compatibility - "GetAndUpdate" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
+ EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)I")));
+ EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)V")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)Z")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+ }
+
+ // Check synthesized method types match expected forms.
+ {
+ MethodType* get = MethodTypeOf("()I");
+ MethodType* set = MethodTypeOf("(I)V");
+ MethodType* compareAndSet = MethodTypeOf("(II)Z");
+ MethodType* compareAndExchange = MethodTypeOf("(II)I");
+ MethodType* getAndUpdate = MethodTypeOf("(I)I");
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGet)->IsExactMatch(get));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSet)->IsExactMatch(set));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetVolatile)->IsExactMatch(get));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetVolatile)->IsExactMatch(set));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAcquire)->IsExactMatch(get));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetRelease)->IsExactMatch(set));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetOpaque)->IsExactMatch(get));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetOpaque)->IsExactMatch(set));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndSet)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchange)->IsExactMatch(compareAndExchange));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeAcquire)->IsExactMatch(compareAndExchange));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeRelease)->IsExactMatch(compareAndExchange));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetPlain)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSet)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetAcquire)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetRelease)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSet)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAdd)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOr)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAnd)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXor)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(fvh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorAcquire)->IsExactMatch(getAndUpdate));
+ }
+}
+
+TEST_F(VarHandleTest, ArrayElementVarHandle) {
+ Thread * const self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ StackHandleScope<2> hs(self);
+
+ int32_t mask = AccessModesBitMask(VarHandle::AccessMode::kGet,
+ VarHandle::AccessMode::kSet,
+ VarHandle::AccessMode::kGetVolatile,
+ VarHandle::AccessMode::kSetVolatile,
+ VarHandle::AccessMode::kGetAcquire,
+ VarHandle::AccessMode::kSetRelease,
+ VarHandle::AccessMode::kGetOpaque,
+ VarHandle::AccessMode::kSetOpaque,
+ VarHandle::AccessMode::kCompareAndSet,
+ VarHandle::AccessMode::kCompareAndExchange,
+ VarHandle::AccessMode::kCompareAndExchangeAcquire,
+ VarHandle::AccessMode::kCompareAndExchangeRelease,
+ VarHandle::AccessMode::kWeakCompareAndSetPlain,
+ VarHandle::AccessMode::kWeakCompareAndSet,
+ VarHandle::AccessMode::kWeakCompareAndSetAcquire,
+ VarHandle::AccessMode::kWeakCompareAndSetRelease,
+ VarHandle::AccessMode::kGetAndSet,
+ VarHandle::AccessMode::kGetAndSetAcquire,
+ VarHandle::AccessMode::kGetAndSetRelease,
+ VarHandle::AccessMode::kGetAndAdd,
+ VarHandle::AccessMode::kGetAndAddAcquire,
+ VarHandle::AccessMode::kGetAndAddRelease,
+ VarHandle::AccessMode::kGetAndBitwiseOr,
+ VarHandle::AccessMode::kGetAndBitwiseOrRelease,
+ VarHandle::AccessMode::kGetAndBitwiseOrAcquire,
+ VarHandle::AccessMode::kGetAndBitwiseAnd,
+ VarHandle::AccessMode::kGetAndBitwiseAndRelease,
+ VarHandle::AccessMode::kGetAndBitwiseAndAcquire,
+ VarHandle::AccessMode::kGetAndBitwiseXor,
+ VarHandle::AccessMode::kGetAndBitwiseXorRelease,
+ VarHandle::AccessMode::kGetAndBitwiseXorAcquire);
+
+ ObjPtr<mirror::Class> string_class = mirror::String::GetJavaLangString();
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Handle<Class> string_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &string_class)));
+ Handle<mirror::ArrayElementVarHandle> vh(hs.NewHandle(CreateArrayElementVarHandle(self, string_array_class, mask)));
+ EXPECT_FALSE(vh.IsNull());
+
+ // Check access modes
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGet));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSet));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetVolatile));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSetVolatile));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAcquire));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSetRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetOpaque));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSetOpaque));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndSet));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchange));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeAcquire));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetPlain));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSet));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetAcquire));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSet));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetAcquire));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAdd));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddAcquire));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOr));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrAcquire));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAnd));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndAcquire));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXor));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorAcquire));
+
+ // Check compatibility - "Get" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Ljava/lang/String;")));
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)V")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;Ljava/lang/String;)Z")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+ }
+
+ // Check compatibility - "Set" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)V")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)V")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Z")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+ }
+
+ // Check compatibility - "CompareAndSet" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
+ EXPECT_TRUE(
+ vh->IsMethodTypeCompatible(
+ access_mode,
+ MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Z")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode,
+ MethodTypeOf("([Ljava/lang/String;III)I")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Z")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+ }
+
+ // Check compatibility - "CompareAndExchange" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;")));
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;II)Z")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+ }
+
+ // Check compatibility - "GetAndUpdate" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;")));
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)V")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)Z")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+ }
+
+ // Check synthesized method types match expected forms.
+ {
+ MethodType* get = MethodTypeOf("([Ljava/lang/String;I)Ljava/lang/String;");
+ MethodType* set = MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)V");
+ MethodType* compareAndSet = MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Z");
+ MethodType* compareAndExchange = MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
+ MethodType* getAndUpdate = MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;");
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGet)->IsExactMatch(get));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSet)->IsExactMatch(set));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetVolatile)->IsExactMatch(get));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetVolatile)->IsExactMatch(set));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAcquire)->IsExactMatch(get));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetRelease)->IsExactMatch(set));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetOpaque)->IsExactMatch(get));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetOpaque)->IsExactMatch(set));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndSet)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchange)->IsExactMatch(compareAndExchange));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeAcquire)->IsExactMatch(compareAndExchange));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeRelease)->IsExactMatch(compareAndExchange));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetPlain)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSet)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetAcquire)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetRelease)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSet)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAdd)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOr)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAnd)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXor)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorAcquire)->IsExactMatch(getAndUpdate));
+ }
+}
+
+TEST_F(VarHandleTest, ByteArrayViewVarHandle) {
+ Thread * const self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ StackHandleScope<2> hs(self);
+
+ int32_t mask = AccessModesBitMask(VarHandle::AccessMode::kGet,
+ VarHandle::AccessMode::kGetVolatile,
+ VarHandle::AccessMode::kGetAcquire,
+ VarHandle::AccessMode::kGetOpaque,
+ VarHandle::AccessMode::kCompareAndSet,
+ VarHandle::AccessMode::kCompareAndExchangeAcquire,
+ VarHandle::AccessMode::kWeakCompareAndSetPlain,
+ VarHandle::AccessMode::kWeakCompareAndSetAcquire,
+ VarHandle::AccessMode::kGetAndSet,
+ VarHandle::AccessMode::kGetAndSetRelease,
+ VarHandle::AccessMode::kGetAndAddAcquire,
+ VarHandle::AccessMode::kGetAndBitwiseOr,
+ VarHandle::AccessMode::kGetAndBitwiseOrAcquire,
+ VarHandle::AccessMode::kGetAndBitwiseAndRelease,
+ VarHandle::AccessMode::kGetAndBitwiseXor,
+ VarHandle::AccessMode::kGetAndBitwiseXorAcquire);
+
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ObjPtr<mirror::Class> char_class = class_linker->FindPrimitiveClass('C');
+ Handle<Class> char_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &char_class)));
+ const bool native_byte_order = true;
+ Handle<mirror::ByteArrayViewVarHandle> vh(hs.NewHandle(CreateByteArrayViewVarHandle(self, char_array_class, native_byte_order, mask)));
+ EXPECT_FALSE(vh.IsNull());
+ EXPECT_EQ(native_byte_order, vh->GetNativeByteOrder());
+
+ // Check access modes
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGet));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSet));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetVolatile));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSetVolatile));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAcquire));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSetRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetOpaque));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSetOpaque));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndSet));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchange));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeAcquire));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetPlain));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSet));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetAcquire));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSet));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetAcquire));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetRelease));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAdd));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddAcquire));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOr));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrAcquire));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAnd));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndRelease));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndAcquire));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXor));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorAcquire));
+
+ // Check compatibility - "Get" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)C")));
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)V")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BC)Z")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+ }
+
+ // Check compatibility - "Set" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)V")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)V")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)Z")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+ }
+
+ // Check compatibility - "CompareAndSet" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
+ EXPECT_TRUE(
+ vh->IsMethodTypeCompatible(
+ access_mode,
+ MethodTypeOf("([BICC)Z")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode,
+ MethodTypeOf("([BIII)I")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)Z")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+ }
+
+ // Check compatibility - "CompareAndExchange" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BICC)C")));
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BICC)V")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BII)Z")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+ }
+
+ // Check compatibility - "GetAndUpdate" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)C")));
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)V")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)Z")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+ }
+
+ // Check synthesized method types match expected forms.
+ {
+ MethodType* get = MethodTypeOf("([BI)C");
+ MethodType* set = MethodTypeOf("([BIC)V");
+ MethodType* compareAndSet = MethodTypeOf("([BICC)Z");
+ MethodType* compareAndExchange = MethodTypeOf("([BICC)C");
+ MethodType* getAndUpdate = MethodTypeOf("([BIC)C");
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGet)->IsExactMatch(get));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSet)->IsExactMatch(set));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetVolatile)->IsExactMatch(get));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetVolatile)->IsExactMatch(set));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAcquire)->IsExactMatch(get));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetRelease)->IsExactMatch(set));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetOpaque)->IsExactMatch(get));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetOpaque)->IsExactMatch(set));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndSet)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchange)->IsExactMatch(compareAndExchange));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeAcquire)->IsExactMatch(compareAndExchange));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeRelease)->IsExactMatch(compareAndExchange));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetPlain)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSet)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetAcquire)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetRelease)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSet)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAdd)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOr)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAnd)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXor)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorAcquire)->IsExactMatch(getAndUpdate));
+ }
+}
+
+TEST_F(VarHandleTest, ByteBufferViewVarHandle) {
+ Thread * const self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ StackHandleScope<2> hs(self);
+
+ int32_t mask = AccessModesBitMask(VarHandle::AccessMode::kGet,
+ VarHandle::AccessMode::kGetVolatile,
+ VarHandle::AccessMode::kGetAcquire,
+ VarHandle::AccessMode::kGetOpaque,
+ VarHandle::AccessMode::kCompareAndSet,
+ VarHandle::AccessMode::kCompareAndExchangeAcquire,
+ VarHandle::AccessMode::kWeakCompareAndSetPlain,
+ VarHandle::AccessMode::kWeakCompareAndSetAcquire,
+ VarHandle::AccessMode::kGetAndSet,
+ VarHandle::AccessMode::kGetAndSetRelease,
+ VarHandle::AccessMode::kGetAndAddAcquire,
+ VarHandle::AccessMode::kGetAndBitwiseOr,
+ VarHandle::AccessMode::kGetAndBitwiseOrAcquire,
+ VarHandle::AccessMode::kGetAndBitwiseAndRelease,
+ VarHandle::AccessMode::kGetAndBitwiseXor,
+ VarHandle::AccessMode::kGetAndBitwiseXorAcquire);
+
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ObjPtr<mirror::Class> double_class = class_linker->FindPrimitiveClass('D');
+ Handle<Class> double_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &double_class)));
+ const bool native_byte_order = false;
+ Handle<mirror::ByteBufferViewVarHandle> vh(hs.NewHandle(CreateByteBufferViewVarHandle(self, double_array_class, native_byte_order, mask)));
+ EXPECT_FALSE(vh.IsNull());
+ EXPECT_EQ(native_byte_order, vh->GetNativeByteOrder());
+
+ // Check access modes
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGet));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSet));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetVolatile));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSetVolatile));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAcquire));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSetRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetOpaque));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kSetOpaque));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndSet));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchange));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeAcquire));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kCompareAndExchangeRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetPlain));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSet));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetAcquire));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kWeakCompareAndSetRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSet));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetAcquire));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndSetRelease));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAdd));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddAcquire));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndAddRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOr));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseOrAcquire));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAnd));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndRelease));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseAndAcquire));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXor));
+ EXPECT_FALSE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorRelease));
+ EXPECT_TRUE(vh->IsAccessModeSupported(VarHandle::AccessMode::kGetAndBitwiseXorAcquire));
+
+ // Check compatibility - "Get" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)D")));
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)V")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;D)Z")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+ }
+
+ // Check compatibility - "Set" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)V")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)V")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)Z")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+ }
+
+ // Check compatibility - "CompareAndSet" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
+ EXPECT_TRUE(
+ vh->IsMethodTypeCompatible(
+ access_mode,
+ MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)Z")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode,
+ MethodTypeOf("(Ljava/nio/ByteBuffer;IDI)D")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)Z")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+ }
+
+ // Check compatibility - "CompareAndExchange" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)D")));
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)V")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;II)Z")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+ }
+
+ // Check compatibility - "GetAndUpdate" pattern
+ {
+ const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)D")));
+ EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)V")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)Z")));
+ EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+ }
+
+ // Check synthesized method types match expected forms.
+ {
+ MethodType* get = MethodTypeOf("(Ljava/nio/ByteBuffer;I)D");
+ MethodType* set = MethodTypeOf("(Ljava/nio/ByteBuffer;ID)V");
+ MethodType* compareAndSet = MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)Z");
+ MethodType* compareAndExchange = MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)D");
+ MethodType* getAndUpdate = MethodTypeOf("(Ljava/nio/ByteBuffer;ID)D");
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGet)->IsExactMatch(get));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSet)->IsExactMatch(set));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetVolatile)->IsExactMatch(get));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetVolatile)->IsExactMatch(set));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAcquire)->IsExactMatch(get));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetRelease)->IsExactMatch(set));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetOpaque)->IsExactMatch(get));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kSetOpaque)->IsExactMatch(set));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndSet)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchange)->IsExactMatch(compareAndExchange));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeAcquire)->IsExactMatch(compareAndExchange));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kCompareAndExchangeRelease)->IsExactMatch(compareAndExchange));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetPlain)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSet)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetAcquire)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kWeakCompareAndSetRelease)->IsExactMatch(compareAndSet));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSet)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndSetRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAdd)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndAddRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOr)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseOrAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAnd)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseAndAcquire)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXor)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorRelease)->IsExactMatch(getAndUpdate));
+ EXPECT_TRUE(vh->GetMethodTypeForAccessMode(self, VarHandle::AccessMode::kGetAndBitwiseXorAcquire)->IsExactMatch(getAndUpdate));
+ }
+}
+
+} // namespace mirror
+} // namespace art
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 4ab8908..7d9d8be 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -20,9 +20,11 @@
#include "android-base/stringprintf.h"
+#include "base/file_utils.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "class_linker.h"
+#include <class_loader_context.h>
#include "common_throws.h"
#include "compiler_filter.h"
#include "dex_file-inl.h"
@@ -459,6 +461,7 @@
const char* filename,
const char* instruction_set,
const char* compiler_filter_name,
+ const char* class_loader_context,
bool profile_changed,
bool downgrade) {
if ((filename == nullptr) || !OS::FileExists(filename)) {
@@ -485,6 +488,19 @@
return -1;
}
+ std::unique_ptr<ClassLoaderContext> context = nullptr;
+ if (class_loader_context != nullptr) {
+ context = ClassLoaderContext::Create(class_loader_context);
+
+ if (context == nullptr) {
+ ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
+ std::string message(StringPrintf("Class loader context '%s' is invalid.",
+ class_loader_context));
+ env->ThrowNew(iae.get(), message.c_str());
+ return -1;
+ }
+ }
+
// TODO: Verify the dex location is well formed, and throw an IOException if
// not?
@@ -495,8 +511,10 @@
return OatFileAssistant::kNoDexOptNeeded;
}
- // TODO(calin): Extend DexFile.getDexOptNeeded to accept the class loader context. b/62269291.
- return oat_file_assistant.GetDexOptNeeded(filter, profile_changed, downgrade);
+ return oat_file_assistant.GetDexOptNeeded(filter,
+ profile_changed,
+ downgrade,
+ context.get());
}
static jstring DexFile_getDexFileStatus(JNIEnv* env,
@@ -532,6 +550,7 @@
jstring javaFilename,
jstring javaInstructionSet,
jstring javaTargetCompilerFilter,
+ jstring javaClassLoaderContext,
jboolean newProfile,
jboolean downgrade) {
ScopedUtfChars filename(env, javaFilename);
@@ -549,10 +568,16 @@
return -1;
}
+ NullableScopedUtfChars class_loader_context(env, javaClassLoaderContext);
+ if (env->ExceptionCheck()) {
+ return -1;
+ }
+
return GetDexOptNeeded(env,
filename.c_str(),
instruction_set.c_str(),
target_compiler_filter.c_str(),
+ class_loader_context.c_str(),
newProfile == JNI_TRUE,
downgrade == JNI_TRUE);
}
@@ -731,7 +756,7 @@
NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"),
NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(DexFile, getDexOptNeeded,
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZ)I"),
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZ)I"),
NATIVE_METHOD(DexFile, openDexFileNative,
"(Ljava/lang/String;"
"Ljava/lang/String;"
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
index dd98e25..f5057b0 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -16,6 +16,7 @@
#include "org_apache_harmony_dalvik_ddmc_DdmVmInternal.h"
+#include "base/file_utils.h"
#include "base/logging.h"
#include "base/mutex.h"
#include "debugger.h"
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index 7e16357..f166714 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -40,6 +40,7 @@
#include "android-base/stringprintf.h"
#include "arch/instruction_set.h"
+#include "base/file_utils.h"
#include "base/memory_tool.h"
#include "base/mutex.h"
#include "base/unix_file/fd_file.h"
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 1269dca..d64986e 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -38,6 +38,7 @@
#include "art_method.h"
#include "base/bit_vector.h"
#include "base/enums.h"
+#include "base/file_utils.h"
#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/unix_file/fd_file.h"
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index a7fe9b1..9f6bf69 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -23,6 +23,7 @@
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
+#include "base/file_utils.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "class_linker.h"
@@ -1168,12 +1169,13 @@
const OatFile* file = GetFile();
if (file == nullptr) {
- return false;
+ // No oat file means we have nothing to verify.
+ return true;
}
- size_t dir_index = file->GetLocation().rfind('/');
+ size_t dir_index = oat_file_assistant_->dex_location_.rfind('/');
std::string classpath_dir = (dir_index != std::string::npos)
- ? file->GetLocation().substr(0, dir_index)
+ ? oat_file_assistant_->dex_location_.substr(0, dir_index)
: "";
if (!context->OpenDexFiles(oat_file_assistant_->isa_, classpath_dir)) {
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index d99036d..6d14971 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -1466,6 +1466,33 @@
default_filter, false, false, updated_context.get()));
}
+TEST_F(OatFileAssistantTest, GetDexOptNeededWithUpToDateContextRelative) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string context_location = GetScratchDir() + "/ContextDex.jar";
+ Copy(GetDexSrc1(), dex_location);
+ Copy(GetDexSrc2(), context_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+ const CompilerFilter::Filter default_filter =
+ OatFileAssistant::kDefaultCompilerFilterForDexLoading;
+ std::string error_msg;
+ std::string context_str = "PCL[" + context_location + "]";
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str);
+ ASSERT_TRUE(context != nullptr);
+ ASSERT_TRUE(context->OpenDexFiles(kRuntimeISA, ""));
+
+ int status = oat_file_assistant.MakeUpToDate(false, context.get(), &error_msg);
+ EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+
+ // A relative context simulates a dependent split context.
+ std::unique_ptr<ClassLoaderContext> relative_context =
+ ClassLoaderContext::Create("PCL[ContextDex.jar]");
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(
+ default_filter, false, false, relative_context.get()));
+}
+
// TODO: More Tests:
// * Test class linker falls back to unquickened dex for DexNoOat
// * Test class linker falls back to unquickened dex for MultiDexNoOat
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 940195c..ee35d9c 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -25,6 +25,7 @@
#include "art_field-inl.h"
#include "base/bit_vector-inl.h"
+#include "base/file_utils.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/systrace.h"
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 71d7b6c..526f6d1 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -18,6 +18,7 @@
#include <sstream>
+#include "base/file_utils.h"
#include "base/logging.h"
#include "base/stringpiece.h"
#include "debugger.h"
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index c88799c..139de2b 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -60,6 +60,7 @@
#include "base/arena_allocator.h"
#include "base/dumpable.h"
#include "base/enums.h"
+#include "base/file_utils.h"
#include "base/memory_tool.h"
#include "base/stl_util.h"
#include "base/systrace.h"
@@ -143,6 +144,7 @@
#include "quick/quick_method_frame_info.h"
#include "reflection.h"
#include "runtime_callbacks.h"
+#include "runtime_intrinsics.h"
#include "runtime_options.h"
#include "scoped_thread_state_change-inl.h"
#include "sigchain.h"
@@ -738,6 +740,11 @@
InitNativeMethods();
}
+ // IntializeIntrinsics needs to be called after the WellKnownClasses::Init in InitNativeMethods
+ // because in checking the invocation types of intrinsic methods ArtMethod::GetInvokeType()
+ // needs the SignaturePolymorphic annotation class which is initialized in WellKnownClasses::Init.
+ InitializeIntrinsics();
+
// Initialize well known thread group values that may be accessed threads while attaching.
InitThreadGroups(self);
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index f164f7c..339fe82 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -43,6 +43,17 @@
Remove(cb, &method_inspection_callbacks_);
}
+bool RuntimeCallbacks::IsMethodSafeToJit(ArtMethod* m) {
+ for (MethodInspectionCallback* cb : method_inspection_callbacks_) {
+ if (!cb->IsMethodSafeToJit(m)) {
+ DCHECK(cb->IsMethodBeingInspected(m))
+ << "Contract requires that !IsMethodSafeToJit(m) -> IsMethodBeingInspected(m)";
+ return false;
+ }
+ }
+ return true;
+}
+
bool RuntimeCallbacks::IsMethodBeingInspected(ArtMethod* m) {
for (MethodInspectionCallback* cb : method_inspection_callbacks_) {
if (cb->IsMethodBeingInspected(m)) {
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index c936049..c1ba964 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -104,6 +104,11 @@
// Returns true if the method is being inspected currently and the runtime should not modify it in
// potentially dangerous ways (i.e. replace with compiled version, JIT it, etc).
virtual bool IsMethodBeingInspected(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+
+ // Returns true if the method is safe to Jit, false otherwise.
+ // Note that '!IsMethodSafeToJit(m) implies IsMethodBeingInspected(m)'. That is that if this
+ // method returns false IsMethodBeingInspected must return true.
+ virtual bool IsMethodSafeToJit(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
};
class RuntimeCallbacks {
@@ -167,6 +172,11 @@
// on by some code.
bool IsMethodBeingInspected(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Returns false if some MethodInspectionCallback indicates the method cannot be safetly jitted
+ // (which implies that it is being Inspected). Returns true otherwise. If it returns false the
+ // entrypoint should not be changed to JITed code.
+ bool IsMethodSafeToJit(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+
void AddMethodInspectionCallback(MethodInspectionCallback* cb)
REQUIRES_SHARED(Locks::mutator_lock_);
void RemoveMethodInspectionCallback(MethodInspectionCallback* cb)
diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc
index 940e461..eb69d91 100644
--- a/runtime/runtime_common.cc
+++ b/runtime/runtime_common.cc
@@ -25,6 +25,7 @@
#include "android-base/stringprintf.h"
+#include "base/file_utils.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/mutex.h"
diff --git a/runtime/runtime_intrinsics.cc b/runtime/runtime_intrinsics.cc
new file mode 100644
index 0000000..f710ebe
--- /dev/null
+++ b/runtime/runtime_intrinsics.cc
@@ -0,0 +1,84 @@
+/*
+ * 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 "runtime_intrinsics.h"
+
+#include "art_method-inl.h"
+#include "class_linker.h"
+#include "intrinsics_enum.h"
+#include "invoke_type.h"
+#include "mirror/class.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread.h"
+
+namespace art {
+
+namespace {
+
+// Initialize an intrinsic. Returns true if the intrinsic is already
+// initialized, false otherwise.
+bool InitializeIntrinsic(Thread* self,
+ Intrinsics intrinsic,
+ InvokeType invoke_type,
+ const char* class_name,
+ const char* method_name,
+ const char* signature)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ PointerSize image_size = class_linker->GetImagePointerSize();
+ ObjPtr<mirror::Class> cls = class_linker->FindSystemClass(self, class_name);
+ if (cls == nullptr) {
+ LOG(FATAL) << "Could not find class of intrinsic " << class_name;
+ }
+
+ ArtMethod* method = cls->FindClassMethod(method_name, signature, image_size);
+ if (method == nullptr || method->GetDeclaringClass() != cls) {
+ LOG(FATAL) << "Could not find method of intrinsic "
+ << class_name << " " << method_name << " " << signature;
+ }
+
+ CHECK_EQ(method->GetInvokeType(), invoke_type);
+ if (method->IsIntrinsic()) {
+ CHECK_EQ(method->GetIntrinsic(), static_cast<uint32_t>(intrinsic));
+ return true;
+ } else {
+ method->SetIntrinsic(static_cast<uint32_t>(intrinsic));
+ return false;
+ }
+}
+
+} // namespace
+
+void InitializeIntrinsics() {
+ ScopedObjectAccess soa(Thread::Current());
+ // Initialization here uses the short-circuit operator || to stop
+ // initializing if there's an already initialized intrinsic.
+#define SETUP_INTRINSICS(Name, InvokeType, _, __, ___, ClassName, MethodName, Signature) \
+ InitializeIntrinsic(soa.Self(), \
+ Intrinsics::k##Name, \
+ InvokeType, \
+ ClassName, \
+ MethodName, \
+ Signature) ||
+#include "intrinsics_list.h"
+ INTRINSICS_LIST(SETUP_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef SETUP_INTRINSICS
+ true;
+}
+
+} // namespace art
diff --git a/runtime/runtime_intrinsics.h b/runtime/runtime_intrinsics.h
new file mode 100644
index 0000000..98dc9bc
--- /dev/null
+++ b/runtime/runtime_intrinsics.h
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_RUNTIME_INTRINSICS_H_
+#define ART_RUNTIME_RUNTIME_INTRINSICS_H_
+
+namespace art {
+
+void InitializeIntrinsics();
+
+} // namespace art
+
+#endif // ART_RUNTIME_RUNTIME_INTRINSICS_H_
diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc
index a1f14be..bf5d718 100644
--- a/runtime/signal_catcher.cc
+++ b/runtime/signal_catcher.cc
@@ -34,6 +34,7 @@
#endif
#include "arch/instruction_set.h"
+#include "base/file_utils.h"
#include "base/time_utils.h"
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
diff --git a/runtime/standard_dex_file.h b/runtime/standard_dex_file.h
index 4fa3efe..906b0b7 100644
--- a/runtime/standard_dex_file.h
+++ b/runtime/standard_dex_file.h
@@ -49,8 +49,9 @@
size_t size,
const std::string& location,
uint32_t location_checksum,
- const OatDexFile* oat_dex_file)
- : DexFile(base, size, location, location_checksum, oat_dex_file) {}
+ const OatDexFile* oat_dex_file,
+ DexFileContainer* container)
+ : DexFile(base, size, location, location_checksum, oat_dex_file, container) {}
friend class DexFileLoader;
friend class DexFileVerifierTest;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 47ffb4e..065b6e2 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -39,6 +39,7 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/bit_utils.h"
+#include "base/file_utils.h"
#include "base/memory_tool.h"
#include "base/mutex.h"
#include "base/systrace.h"
@@ -1346,36 +1347,26 @@
}
void Thread::RunCheckpointFunction() {
- bool done = false;
- do {
- // Grab the suspend_count lock and copy the checkpoints one by one. When the last checkpoint is
- // copied, clear the list and the flag. The RequestCheckpoint function will also grab this lock
- // to prevent a race between setting the kCheckpointRequest flag and clearing it.
- Closure* checkpoint = nullptr;
- {
- MutexLock mu(this, *Locks::thread_suspend_count_lock_);
- if (tlsPtr_.checkpoint_function != nullptr) {
- checkpoint = tlsPtr_.checkpoint_function;
- if (!checkpoint_overflow_.empty()) {
- // Overflow list not empty, copy the first one out and continue.
- tlsPtr_.checkpoint_function = checkpoint_overflow_.front();
- checkpoint_overflow_.pop_front();
- } else {
- // No overflow checkpoints, this means that we are on the last pending checkpoint.
- tlsPtr_.checkpoint_function = nullptr;
- AtomicClearFlag(kCheckpointRequest);
- done = true;
- }
- } else {
- LOG(FATAL) << "Checkpoint flag set without pending checkpoint";
- }
+ // Grab the suspend_count lock, get the next checkpoint and update all the checkpoint fields. If
+ // there are no more checkpoints we will also clear the kCheckpointRequest flag.
+ Closure* checkpoint;
+ {
+ MutexLock mu(this, *Locks::thread_suspend_count_lock_);
+ checkpoint = tlsPtr_.checkpoint_function;
+ if (!checkpoint_overflow_.empty()) {
+ // Overflow list not empty, copy the first one out and continue.
+ tlsPtr_.checkpoint_function = checkpoint_overflow_.front();
+ checkpoint_overflow_.pop_front();
+ } else {
+ // No overflow checkpoints. Clear the kCheckpointRequest flag
+ tlsPtr_.checkpoint_function = nullptr;
+ AtomicClearFlag(kCheckpointRequest);
}
-
- // Outside the lock, run the checkpoint functions that we collected.
- ScopedTrace trace("Run checkpoint function");
- DCHECK(checkpoint != nullptr);
- checkpoint->Run(this);
- } while (!done);
+ }
+ // Outside the lock, run the checkpoint function.
+ ScopedTrace trace("Run checkpoint function");
+ CHECK(checkpoint != nullptr) << "Checkpoint flag set without pending checkpoint";
+ checkpoint->Run(this);
}
void Thread::RunEmptyCheckpoint() {
diff --git a/runtime/thread.h b/runtime/thread.h
index 42b38da..3b917ba 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -1356,6 +1356,9 @@
WARN_UNUSED
REQUIRES(Locks::thread_suspend_count_lock_);
+ // Runs a single checkpoint function. If there are no more pending checkpoint functions it will
+ // clear the kCheckpointRequest flag. The caller is responsible for calling this in a loop until
+ // the kCheckpointRequest flag is cleared.
void RunCheckpointFunction();
void RunEmptyCheckpoint();
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index cad164c..88f1fc6 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -21,6 +21,7 @@
#include <unistd.h>
#include <sstream>
+#include <vector>
#include "android-base/stringprintf.h"
#include "backtrace/BacktraceMap.h"
@@ -204,7 +205,11 @@
: os_(os),
barrier_(0),
backtrace_map_(dump_native_stack ? BacktraceMap::Create(getpid()) : nullptr),
- dump_native_stack_(dump_native_stack) {}
+ dump_native_stack_(dump_native_stack) {
+ if (backtrace_map_ != nullptr) {
+ backtrace_map_->SetSuffixesToIgnore(std::vector<std::string> { "oat", "odex" });
+ }
+ }
void Run(Thread* thread) OVERRIDE {
// Note thread and self may not be equal if thread was already suspended at the point of the
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 1f6bd74..f6533a7 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -18,41 +18,20 @@
#include <inttypes.h>
#include <pthread.h>
-#include <sys/mman.h> // For madvise
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
-// We need dladdr.
-#ifndef __APPLE__
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#define DEFINED_GNU_SOURCE
-#endif
-#include <dlfcn.h>
-#include <libgen.h>
-#ifdef DEFINED_GNU_SOURCE
-#undef _GNU_SOURCE
-#undef DEFINED_GNU_SOURCE
-#endif
-#endif
-
-
#include <memory>
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
-#include "base/stl_util.h"
-#include "base/unix_file/fd_file.h"
+#include "base/file_utils.h"
#include "dex_file-inl.h"
-#include "dex_file_loader.h"
-#include "dex_instruction.h"
-#include "oat_quick_method_header.h"
#include "os.h"
-#include "scoped_thread_state_change-inl.h"
#include "utf-inl.h"
#if defined(__APPLE__)
@@ -92,78 +71,6 @@
return result;
}
-bool ReadFileToString(const std::string& file_name, std::string* result) {
- File file(file_name, O_RDONLY, false);
- if (!file.IsOpened()) {
- return false;
- }
-
- std::vector<char> buf(8 * KB);
- while (true) {
- int64_t n = TEMP_FAILURE_RETRY(read(file.Fd(), &buf[0], buf.size()));
- if (n == -1) {
- return false;
- }
- if (n == 0) {
- return true;
- }
- result->append(&buf[0], n);
- }
-}
-
-bool PrintFileToLog(const std::string& file_name, LogSeverity level) {
- File file(file_name, O_RDONLY, false);
- if (!file.IsOpened()) {
- return false;
- }
-
- constexpr size_t kBufSize = 256; // Small buffer. Avoid stack overflow and stack size warnings.
- char buf[kBufSize + 1]; // +1 for terminator.
- size_t filled_to = 0;
- while (true) {
- DCHECK_LT(filled_to, kBufSize);
- int64_t n = TEMP_FAILURE_RETRY(read(file.Fd(), &buf[filled_to], kBufSize - filled_to));
- if (n <= 0) {
- // Print the rest of the buffer, if it exists.
- if (filled_to > 0) {
- buf[filled_to] = 0;
- LOG(level) << buf;
- }
- return n == 0;
- }
- // Scan for '\n'.
- size_t i = filled_to;
- bool found_newline = false;
- for (; i < filled_to + n; ++i) {
- if (buf[i] == '\n') {
- // Found a line break, that's something to print now.
- buf[i] = 0;
- LOG(level) << buf;
- // Copy the rest to the front.
- if (i + 1 < filled_to + n) {
- memmove(&buf[0], &buf[i + 1], filled_to + n - i - 1);
- filled_to = filled_to + n - i - 1;
- } else {
- filled_to = 0;
- }
- found_newline = true;
- break;
- }
- }
- if (found_newline) {
- continue;
- } else {
- filled_to += n;
- // Check if we must flush now.
- if (filled_to == kBufSize) {
- buf[kBufSize] = 0;
- LOG(level) << buf;
- filled_to = 0;
- }
- }
- }
-}
-
void AppendPrettyDescriptor(const char* descriptor, std::string* result) {
// Count the number of '['s to get the dimensionality.
const char* c = descriptor;
@@ -718,197 +625,6 @@
*task_cpu = strtoull(fields[36].c_str(), nullptr, 10);
}
-std::string GetAndroidRootSafe(std::string* error_msg) {
- // Prefer ANDROID_ROOT if it's set.
- const char* android_dir = getenv("ANDROID_ROOT");
- if (android_dir != nullptr) {
- if (!OS::DirectoryExists(android_dir)) {
- *error_msg = StringPrintf("Failed to find ANDROID_ROOT directory %s", android_dir);
- return "";
- }
- return android_dir;
- }
-
- // Check where libart is from, and derive from there. Only do this for non-Mac.
-#ifndef __APPLE__
- {
- Dl_info info;
- if (dladdr(reinterpret_cast<const void*>(&GetAndroidRootSafe), /* out */ &info) != 0) {
- // Make a duplicate of the fname so dirname can modify it.
- UniqueCPtr<char> fname(strdup(info.dli_fname));
-
- char* dir1 = dirname(fname.get()); // This is the lib directory.
- char* dir2 = dirname(dir1); // This is the "system" directory.
- if (OS::DirectoryExists(dir2)) {
- std::string tmp = dir2; // Make a copy here so that fname can be released.
- return tmp;
- }
- }
- }
-#endif
-
- // Try "/system".
- if (!OS::DirectoryExists("/system")) {
- *error_msg = "Failed to find ANDROID_ROOT directory /system";
- return "";
- }
- return "/system";
-}
-
-std::string GetAndroidRoot() {
- std::string error_msg;
- std::string ret = GetAndroidRootSafe(&error_msg);
- if (ret.empty()) {
- LOG(FATAL) << error_msg;
- UNREACHABLE();
- }
- return ret;
-}
-
-
-static const char* GetAndroidDirSafe(const char* env_var,
- const char* default_dir,
- std::string* error_msg) {
- const char* android_dir = getenv(env_var);
- if (android_dir == nullptr) {
- if (OS::DirectoryExists(default_dir)) {
- android_dir = default_dir;
- } else {
- *error_msg = StringPrintf("%s not set and %s does not exist", env_var, default_dir);
- return nullptr;
- }
- }
- if (!OS::DirectoryExists(android_dir)) {
- *error_msg = StringPrintf("Failed to find %s directory %s", env_var, android_dir);
- return nullptr;
- }
- return android_dir;
-}
-
-static const char* GetAndroidDir(const char* env_var, const char* default_dir) {
- std::string error_msg;
- const char* dir = GetAndroidDirSafe(env_var, default_dir, &error_msg);
- if (dir != nullptr) {
- return dir;
- } else {
- LOG(FATAL) << error_msg;
- return nullptr;
- }
-}
-
-const char* GetAndroidData() {
- return GetAndroidDir("ANDROID_DATA", "/data");
-}
-
-const char* GetAndroidDataSafe(std::string* error_msg) {
- return GetAndroidDirSafe("ANDROID_DATA", "/data", error_msg);
-}
-
-std::string GetDefaultBootImageLocation(std::string* error_msg) {
- std::string android_root = GetAndroidRootSafe(error_msg);
- if (android_root.empty()) {
- return "";
- }
- return StringPrintf("%s/framework/boot.art", android_root.c_str());
-}
-
-void GetDalvikCache(const char* subdir, const bool create_if_absent, std::string* dalvik_cache,
- bool* have_android_data, bool* dalvik_cache_exists, bool* is_global_cache) {
- CHECK(subdir != nullptr);
- std::string error_msg;
- const char* android_data = GetAndroidDataSafe(&error_msg);
- if (android_data == nullptr) {
- *have_android_data = false;
- *dalvik_cache_exists = false;
- *is_global_cache = false;
- return;
- } else {
- *have_android_data = true;
- }
- const std::string dalvik_cache_root(StringPrintf("%s/dalvik-cache/", android_data));
- *dalvik_cache = dalvik_cache_root + subdir;
- *dalvik_cache_exists = OS::DirectoryExists(dalvik_cache->c_str());
- *is_global_cache = strcmp(android_data, "/data") == 0;
- if (create_if_absent && !*dalvik_cache_exists && !*is_global_cache) {
- // Don't create the system's /data/dalvik-cache/... because it needs special permissions.
- *dalvik_cache_exists = ((mkdir(dalvik_cache_root.c_str(), 0700) == 0 || errno == EEXIST) &&
- (mkdir(dalvik_cache->c_str(), 0700) == 0 || errno == EEXIST));
- }
-}
-
-std::string GetDalvikCache(const char* subdir) {
- CHECK(subdir != nullptr);
- const char* android_data = GetAndroidData();
- const std::string dalvik_cache_root(StringPrintf("%s/dalvik-cache/", android_data));
- const std::string dalvik_cache = dalvik_cache_root + subdir;
- if (!OS::DirectoryExists(dalvik_cache.c_str())) {
- // TODO: Check callers. Traditional behavior is to not abort.
- return "";
- }
- return dalvik_cache;
-}
-
-bool GetDalvikCacheFilename(const char* location, const char* cache_location,
- std::string* filename, std::string* error_msg) {
- if (location[0] != '/') {
- *error_msg = StringPrintf("Expected path in location to be absolute: %s", location);
- return false;
- }
- std::string cache_file(&location[1]); // skip leading slash
- if (!android::base::EndsWith(location, ".dex") &&
- !android::base::EndsWith(location, ".art") &&
- !android::base::EndsWith(location, ".oat")) {
- cache_file += "/";
- cache_file += DexFileLoader::kClassesDex;
- }
- std::replace(cache_file.begin(), cache_file.end(), '/', '@');
- *filename = StringPrintf("%s/%s", cache_location, cache_file.c_str());
- return true;
-}
-
-std::string GetVdexFilename(const std::string& oat_location) {
- return ReplaceFileExtension(oat_location, "vdex");
-}
-
-static void InsertIsaDirectory(const InstructionSet isa, std::string* filename) {
- // in = /foo/bar/baz
- // out = /foo/bar/<isa>/baz
- size_t pos = filename->rfind('/');
- CHECK_NE(pos, std::string::npos) << *filename << " " << isa;
- filename->insert(pos, "/", 1);
- filename->insert(pos + 1, GetInstructionSetString(isa));
-}
-
-std::string GetSystemImageFilename(const char* location, const InstructionSet isa) {
- // location = /system/framework/boot.art
- // filename = /system/framework/<isa>/boot.art
- std::string filename(location);
- InsertIsaDirectory(isa, &filename);
- return filename;
-}
-
-bool FileExists(const std::string& filename) {
- struct stat buffer;
- return stat(filename.c_str(), &buffer) == 0;
-}
-
-bool FileExistsAndNotEmpty(const std::string& filename) {
- struct stat buffer;
- if (stat(filename.c_str(), &buffer) != 0) {
- return false;
- }
- return buffer.st_size > 0;
-}
-
-std::string ReplaceFileExtension(const std::string& filename, const std::string& new_extension) {
- const size_t last_ext = filename.find_last_of('.');
- if (last_ext == std::string::npos) {
- return filename + "." + new_extension;
- } else {
- return filename.substr(0, last_ext + 1) + new_extension;
- }
-}
-
std::string PrettyDescriptor(Primitive::Type type) {
return PrettyDescriptor(Primitive::Descriptor(type));
}
@@ -952,30 +668,10 @@
*parsed_value = value;
}
-int64_t GetFileSizeBytes(const std::string& filename) {
- struct stat stat_buf;
- int rc = stat(filename.c_str(), &stat_buf);
- return rc == 0 ? stat_buf.st_size : -1;
-}
-
void SleepForever() {
while (true) {
usleep(1000000);
}
}
-int MadviseLargestPageAlignedRegion(const uint8_t* begin, const uint8_t* end, int advice) {
- DCHECK_LE(begin, end);
- begin = AlignUp(begin, kPageSize);
- end = AlignDown(end, kPageSize);
- if (begin < end) {
- int result = madvise(const_cast<uint8_t*>(begin), end - begin, advice);
- if (result != 0) {
- PLOG(WARNING) << "madvise failed " << result;
- }
- return result;
- }
- return 0;
-}
-
} // namespace art
diff --git a/runtime/utils.h b/runtime/utils.h
index fbf812a..ede32dc 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -20,12 +20,8 @@
#include <pthread.h>
#include <stdlib.h>
-#include <limits>
-#include <memory>
#include <random>
#include <string>
-#include <type_traits>
-#include <vector>
#include "arch/instruction_set.h"
#include "base/casts.h"
@@ -118,9 +114,6 @@
// additionally allowing names that begin with '<' and end with '>'.
bool IsValidMemberName(const char* s);
-bool ReadFileToString(const std::string& file_name, std::string* result);
-bool PrintFileToLog(const std::string& file_name, LogSeverity level);
-
// Splits a string using the given separator character into a vector of
// strings. Empty strings will be omitted.
void Split(const std::string& s, char separator, std::vector<std::string>* result);
@@ -131,58 +124,12 @@
// Returns the given thread's name.
std::string GetThreadName(pid_t tid);
-// Reads data from "/proc/self/task/${tid}/stat".
-void GetTaskStats(pid_t tid, char* state, int* utime, int* stime, int* task_cpu);
-
// Sets the name of the current thread. The name may be truncated to an
// implementation-defined limit.
void SetThreadName(const char* thread_name);
-// Find $ANDROID_ROOT, /system, or abort.
-std::string GetAndroidRoot();
-// Find $ANDROID_ROOT, /system, or return an empty string.
-std::string GetAndroidRootSafe(std::string* error_msg);
-
-// Find $ANDROID_DATA, /data, or abort.
-const char* GetAndroidData();
-// Find $ANDROID_DATA, /data, or return null.
-const char* GetAndroidDataSafe(std::string* error_msg);
-
-// Returns the default boot image location (ANDROID_ROOT/framework/boot.art).
-// Returns an empty string if ANDROID_ROOT is not set.
-std::string GetDefaultBootImageLocation(std::string* error_msg);
-
-// Returns the dalvik-cache location, with subdir appended. Returns the empty string if the cache
-// could not be found.
-std::string GetDalvikCache(const char* subdir);
-// Return true if we found the dalvik cache and stored it in the dalvik_cache argument.
-// have_android_data will be set to true if we have an ANDROID_DATA that exists,
-// dalvik_cache_exists will be true if there is a dalvik-cache directory that is present.
-// The flag is_global_cache tells whether this cache is /data/dalvik-cache.
-void GetDalvikCache(const char* subdir, bool create_if_absent, std::string* dalvik_cache,
- bool* have_android_data, bool* dalvik_cache_exists, bool* is_global_cache);
-
-// Returns the absolute dalvik-cache path for a DexFile or OatFile. The path returned will be
-// rooted at cache_location.
-bool GetDalvikCacheFilename(const char* file_location, const char* cache_location,
- std::string* filename, std::string* error_msg);
-
-// Returns the system location for an image
-std::string GetSystemImageFilename(const char* location, InstructionSet isa);
-
-// Returns the vdex filename for the given oat filename.
-std::string GetVdexFilename(const std::string& oat_filename);
-
-// Returns true if the file exists.
-bool FileExists(const std::string& filename);
-bool FileExistsAndNotEmpty(const std::string& filename);
-
-// Returns `filename` with the text after the last occurrence of '.' replaced with
-// `extension`. If `filename` does not contain a period, returns a string containing `filename`,
-// a period, and `new_extension`.
-// Example: ReplaceFileExtension("foo.bar", "abc") == "foo.abc"
-// ReplaceFileExtension("foo", "abc") == "foo.abc"
-std::string ReplaceFileExtension(const std::string& filename, const std::string& new_extension);
+// Reads data from "/proc/self/task/${tid}/stat".
+void GetTaskStats(pid_t tid, char* state, int* utime, int* stime, int* task_cpu);
class VoidFunctor {
public:
@@ -273,9 +220,6 @@
return dist(rng);
}
-// Return the file size in bytes or -1 if the file does not exists.
-int64_t GetFileSizeBytes(const std::string& filename);
-
// Sleep forever and never come back.
NO_RETURN void SleepForever();
@@ -335,9 +279,6 @@
return (opnd < 0) ? -1 : ((opnd == 0) ? 0 : 1);
}
-// Madvise the largest page aligned region within begin and end.
-int MadviseLargestPageAlignedRegion(const uint8_t* begin, const uint8_t* end, int advice);
-
template <typename Func, typename... Args>
static inline void CheckedCall(const Func& function, const char* what, Args... args) {
int rc = function(args...);
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index e846c98..efb20ba 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -20,6 +20,7 @@
#include <stdlib.h>
#include "base/enums.h"
+#include "base/file_utils.h"
#include "base/stl_util.h"
#include "class_linker-inl.h"
#include "common_runtime_test.h"
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 0033167..6555e14 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -987,9 +987,17 @@
size_t monitor_enter_count = 0;
IterationRange<DexInstructionIterator> instructions = code_item_->Instructions();
- DexInstructionIterator inst = instructions.begin();
- for ( ; inst < instructions.end(); ++inst) {
- Instruction::Code opcode = inst->Opcode();
+ // We can't assume the instruction is well formed, handle the case where calculating the size
+ // goes past the end of the code item.
+ SafeDexInstructionIterator it(instructions.begin(), instructions.end());
+ for ( ; !it.IsErrorState() && it < instructions.end(); ++it) {
+ // In case the instruction goes past the end of the code item, make sure to not process it.
+ SafeDexInstructionIterator next = it;
+ ++next;
+ if (next.IsErrorState() || next > instructions.end()) {
+ break;
+ }
+ Instruction::Code opcode = it->Opcode();
switch (opcode) {
case Instruction::APUT_OBJECT:
case Instruction::CHECK_CAST:
@@ -1010,13 +1018,13 @@
default:
break;
}
- GetInstructionFlags(inst.GetDexPC(instructions.begin())).SetIsOpcode();
+ GetInstructionFlags(it.GetDexPC(instructions.begin())).SetIsOpcode();
}
- if (inst != instructions.end()) {
+ if (it != instructions.end()) {
const size_t insns_size = code_item_->insns_size_in_code_units_;
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "code did not end where expected ("
- << inst.GetDexPC(instructions.begin()) << " vs. "
+ << it.GetDexPC(instructions.begin()) << " vs. "
<< insns_size << ")";
return false;
}
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index bfcd95c..829dea9 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -52,6 +52,7 @@
jclass WellKnownClasses::java_lang_Daemons;
jclass WellKnownClasses::java_lang_Error;
jclass WellKnownClasses::java_lang_invoke_MethodHandle;
+jclass WellKnownClasses::java_lang_invoke_MethodHandle_PolymorphicSignature;
jclass WellKnownClasses::java_lang_IllegalAccessError;
jclass WellKnownClasses::java_lang_NoClassDefFoundError;
jclass WellKnownClasses::java_lang_Object;
@@ -298,6 +299,7 @@
java_lang_Error = CacheClass(env, "java/lang/Error");
java_lang_IllegalAccessError = CacheClass(env, "java/lang/IllegalAccessError");
java_lang_invoke_MethodHandle = CacheClass(env, "java/lang/invoke/MethodHandle");
+ java_lang_invoke_MethodHandle_PolymorphicSignature = CacheClass(env, "java/lang/invoke/MethodHandle$PolymorphicSignature");
java_lang_NoClassDefFoundError = CacheClass(env, "java/lang/NoClassDefFoundError");
java_lang_reflect_Constructor = CacheClass(env, "java/lang/reflect/Constructor");
java_lang_reflect_Executable = CacheClass(env, "java/lang/reflect/Executable");
@@ -334,6 +336,7 @@
java_lang_invoke_MethodHandle_invokeExact = CacheMethod(env, java_lang_invoke_MethodHandle, false, "invokeExact", "([Ljava/lang/Object;)Ljava/lang/Object;");
java_lang_invoke_MethodHandles_lookup = CacheMethod(env, "java/lang/invoke/MethodHandles", true, "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;");
java_lang_invoke_MethodHandles_Lookup_findConstructor = CacheMethod(env, "java/lang/invoke/MethodHandles$Lookup", false, "findConstructor", "(Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;");
+
java_lang_ref_FinalizerReference_add = CacheMethod(env, "java/lang/ref/FinalizerReference", true, "add", "(Ljava/lang/Object;)V");
java_lang_ref_ReferenceQueue_add = CacheMethod(env, "java/lang/ref/ReferenceQueue", true, "add", "(Ljava/lang/ref/Reference;)V");
@@ -434,6 +437,7 @@
java_lang_Error = nullptr;
java_lang_IllegalAccessError = nullptr;
java_lang_invoke_MethodHandle = nullptr;
+ java_lang_invoke_MethodHandle_PolymorphicSignature = nullptr;
java_lang_NoClassDefFoundError = nullptr;
java_lang_Object = nullptr;
java_lang_OutOfMemoryError = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 7deef63..b2fd4d6 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -64,6 +64,7 @@
static jclass java_lang_Error;
static jclass java_lang_IllegalAccessError;
static jclass java_lang_invoke_MethodHandle;
+ static jclass java_lang_invoke_MethodHandle_PolymorphicSignature;
static jclass java_lang_NoClassDefFoundError;
static jclass java_lang_Object;
static jclass java_lang_OutOfMemoryError;
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index ad705c5..58b33be 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -28,6 +28,7 @@
#include <backtrace/Backtrace.h>
#include "android-base/stringprintf.h"
+#include "base/file_utils.h"
#include "base/logging.h"
#include "base/macros.h"
#include "gc/heap.h"
diff --git a/test/203-multi-checkpoint/expected.txt b/test/203-multi-checkpoint/expected.txt
new file mode 100644
index 0000000..e1e30e3
--- /dev/null
+++ b/test/203-multi-checkpoint/expected.txt
@@ -0,0 +1,5 @@
+JNI_OnLoad called
+Other thread running
+pushing checkpoints
+checkpoints pushed
+Passed!
diff --git a/test/203-multi-checkpoint/info.txt b/test/203-multi-checkpoint/info.txt
new file mode 100644
index 0000000..a96ba97
--- /dev/null
+++ b/test/203-multi-checkpoint/info.txt
@@ -0,0 +1,4 @@
+Test that we correctly handle checkpoints that suspend.
+
+This could cause problems with asserts when there were multiple checkpoints
+queued and earlier ones suspended.
diff --git a/test/203-multi-checkpoint/multi_checkpoint.cc b/test/203-multi-checkpoint/multi_checkpoint.cc
new file mode 100644
index 0000000..0799b6e
--- /dev/null
+++ b/test/203-multi-checkpoint/multi_checkpoint.cc
@@ -0,0 +1,90 @@
+/*
+ * 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 "art_method-inl.h"
+#include "base/mutex-inl.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+#include "thread_pool.h"
+
+namespace art {
+
+struct TestClosure : public Closure {
+ bool first_run_start;
+ bool first_run_end;
+ bool second_run;
+ bool second_run_interleaved;
+
+ void Run(Thread* self) OVERRIDE {
+ CHECK_EQ(self, Thread::Current()) << "Not running on target thread!";
+ if (!first_run_start) {
+ CHECK(!second_run);
+ first_run_start = true;
+ // Suspend ourself so that we will perform the second run.
+ {
+ ScopedObjectAccess soa(self);
+ self->FullSuspendCheck();
+ }
+ first_run_end = true;
+ } else {
+ CHECK(!second_run);
+ CHECK(first_run_start);
+ second_run = true;
+ second_run_interleaved = !first_run_end;
+ }
+ }
+
+ void Check() {
+ CHECK(first_run_start);
+ CHECK(first_run_end);
+ CHECK(second_run);
+ CHECK(second_run_interleaved);
+ }
+};
+
+static TestClosure gTestClosure = {};
+
+extern "C" JNIEXPORT void JNICALL Java_Main_checkCheckpointsRun(JNIEnv*, jclass) {
+ gTestClosure.Check();
+}
+
+struct SetupClosure : public Closure {
+ void Run(Thread* self) OVERRIDE {
+ CHECK_EQ(self, Thread::Current()) << "Not running on target thread!";
+ ScopedObjectAccess soa(self);
+ MutexLock tscl_mu(self, *Locks::thread_suspend_count_lock_);
+ // Both should succeed since we are in runnable and have the lock.
+ CHECK(self->RequestCheckpoint(&gTestClosure)) << "Could not set first checkpoint.";
+ CHECK(self->RequestCheckpoint(&gTestClosure)) << "Could not set second checkpoint.";
+ }
+};
+
+static SetupClosure gSetupClosure = {};
+
+extern "C" JNIEXPORT void JNICALL Java_Main_pushCheckpoints(JNIEnv*, jclass, jobject thr) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ MutexLock tll_mu(self, *Locks::thread_list_lock_);
+ Thread* target = Thread::FromManagedThread(soa, thr);
+ while (true) {
+ MutexLock tscl_mu(self, *Locks::thread_suspend_count_lock_);
+ if (target->RequestCheckpoint(&gSetupClosure)) {
+ break;
+ }
+ }
+}
+
+} // namespace art
diff --git a/test/203-multi-checkpoint/src/Main.java b/test/203-multi-checkpoint/src/Main.java
new file mode 100644
index 0000000..187f622
--- /dev/null
+++ b/test/203-multi-checkpoint/src/Main.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+import java.util.concurrent.Semaphore;
+
+public class Main {
+ static final Semaphore start = new Semaphore(0);
+ static volatile boolean continue_loop = true;
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ Thread t = new Thread(Main::runTargetThread, "Target Thread");
+
+ t.start();
+ // Wait for other thread to start.
+ start.acquire();
+
+ System.out.println("pushing checkpoints");
+ pushCheckpoints(t);
+
+ System.out.println("checkpoints pushed");
+ continue_loop = false;
+
+ t.join();
+
+ checkCheckpointsRun();
+
+ System.out.println("Passed!");
+ }
+
+ public static native void pushCheckpoints(Thread t);
+ public static native boolean checkCheckpointsRun();
+
+ public static void doNothing() {}
+ public static void runTargetThread() {
+ System.out.println("Other thread running");
+ try {
+ start.release();
+ while (continue_loop) {
+ doNothing();
+ }
+ } catch (Exception e) {
+ throw new Error("Exception occurred!", e);
+ }
+ }
+}
diff --git a/test/484-checker-register-hints/build b/test/484-checker-register-hints/build
deleted file mode 100644
index 10ffcc5..0000000
--- a/test/484-checker-register-hints/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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.
-
-# See b/65168732
-export USE_D8=false
-
-./default-build "$@"
diff --git a/test/484-checker-register-hints/smali/Smali.smali b/test/484-checker-register-hints/smali/Smali.smali
new file mode 100644
index 0000000..6594936
--- /dev/null
+++ b/test/484-checker-register-hints/smali/Smali.smali
@@ -0,0 +1,143 @@
+# 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 LSmali;
+.super Ljava/lang/Object;
+
+## CHECK-START: void Smali.test3Order1(boolean, int, int, int, int, int) register (after)
+## CHECK: name "B0"
+## CHECK-NOT: ParallelMove
+## CHECK: name "B1"
+## CHECK-NOT: end_block
+## CHECK: If
+## CHECK-NOT: ParallelMove
+## CHECK: name "B6"
+## CHECK-NOT: end_block
+## CHECK: InstanceFieldSet
+# We could check here that there is a parallel move, but it's only valid
+# for some architectures (for example x86), as other architectures may
+# not do move at all.
+## CHECK: end_block
+## CHECK-NOT: ParallelMove
+.method public static test3Order1(ZIIIII)V
+ .registers 14
+
+ sget v0, LMain;->live1:I
+ sget v1, LMain;->live2:I
+ sget v2, LMain;->live3:I
+ sget v5, LMain;->live0:I
+ if-eqz p0, :cond_13
+
+ sput v0, LMain;->live1:I
+
+ :goto_c
+ add-int v6, v0, v1
+ add-int/2addr v6, v2
+ add-int/2addr v6, v5
+ sput v6, LMain;->live1:I
+
+ return-void
+
+ :cond_13
+ sget-boolean v6, LMain;->y:Z
+
+ if-eqz v6, :cond_1a
+ sput v0, LMain;->live1:I
+ goto :goto_c
+
+ :cond_1a
+ sget v3, LMain;->live4:I
+
+ sget v4, LMain;->live5:I
+ sget-object v6, LMain;->foo:LMain$Foo;
+ add-int v7, v0, v4
+ add-int/2addr v7, v3
+ iput v7, v6, LMain$Foo;->field2:I
+ sget-object v6, LMain;->foo:LMain$Foo;
+ add-int v7, v1, v4
+ add-int/2addr v7, v3
+ iput v7, v6, LMain$Foo;->field3:I
+ sget-object v6, LMain;->foo:LMain$Foo;
+ add-int v7, v2, v4
+ add-int/2addr v7, v3
+ iput v7, v6, LMain$Foo;->field4:I
+ sget-object v6, LMain;->foo:LMain$Foo;
+ iput v3, v6, LMain$Foo;->field0:I
+ sget-object v6, LMain;->foo:LMain$Foo;
+ add-int v7, v4, v3
+ iput v7, v6, LMain$Foo;->field1:I
+ goto :goto_c
+.end method
+
+## CHECK-START: void Smali.test3Order2(boolean, int, int, int, int, int) register (after)
+## CHECK: name "B0"
+## CHECK-NOT: ParallelMove
+## CHECK: name "B1"
+## CHECK-NOT: end_block
+## CHECK: If
+## CHECK-NOT: ParallelMove
+## CHECK: name "B5"
+## CHECK-NOT: end_block
+## CHECK: InstanceFieldSet
+# We could check here that there is a parallel move, but it's only valid
+# for some architectures (for example x86), as other architectures may
+# not do move at all.
+## CHECK: end_block
+## CHECK-NOT: ParallelMove
+.method public static test3Order2(ZIIIII)V
+ .registers 14
+
+ sget v0, LMain;->live1:I
+ sget v1, LMain;->live2:I
+ sget v2, LMain;->live3:I
+ sget v3, LMain;->live0:I
+ if-eqz p0, :cond_d
+
+ sput v0, LMain;->live1:I
+ goto :goto_37
+
+ :cond_d
+ sget-boolean v4, LMain;->y:Z
+ if-eqz v4, :cond_14
+
+ sput v0, LMain;->live1:I
+ goto :goto_37
+
+ :cond_14
+ sget v4, LMain;->live4:I
+ sget v5, LMain;->live5:I
+ sget-object v6, LMain;->foo:LMain$Foo;
+ add-int v7, v0, v5
+ add-int/2addr v7, v4
+ iput v7, v6, LMain$Foo;->field2:I
+ sget-object v6, LMain;->foo:LMain$Foo;
+ add-int v7, v1, v5
+ add-int/2addr v7, v4
+ iput v7, v6, LMain$Foo;->field3:I
+ sget-object v6, LMain;->foo:LMain$Foo;
+ add-int v7, v2, v5
+ add-int/2addr v7, v4
+ iput v7, v6, LMain$Foo;->field4:I
+ sget-object v6, LMain;->foo:LMain$Foo;
+ iput v4, v6, LMain$Foo;->field0:I
+ sget-object v6, LMain;->foo:LMain$Foo;
+ add-int v7, v5, v4
+ iput v7, v6, LMain$Foo;->field1:I
+ :goto_37
+
+ add-int v4, v0, v1
+ add-int/2addr v4, v2
+ add-int/2addr v4, v3
+ sput v4, LMain;->live1:I
+ return-void
+.end method
diff --git a/test/484-checker-register-hints/src/Main.java b/test/484-checker-register-hints/src/Main.java
index 6e68f7c..7aab659 100644
--- a/test/484-checker-register-hints/src/Main.java
+++ b/test/484-checker-register-hints/src/Main.java
@@ -98,18 +98,6 @@
/// CHECK: name "B0"
/// CHECK-NOT: ParallelMove
/// CHECK: name "B1"
- /// CHECK-NOT: end_block
- /// CHECK: If
- /// CHECK-NOT: ParallelMove
- /// CHECK: name "B6"
- /// CHECK-NOT: end_block
- /// CHECK: InstanceFieldSet
- // We could check here that there is a parallel move, but it's only valid
- // for some architectures (for example x86), as other architectures may
- // not do move at all.
- /// CHECK: end_block
- /// CHECK-NOT: ParallelMove
-
public static void test3(boolean z, int a, int b, int c, int d, int m) {
// Same version as test2, but with branches reversed, to ensure
// whatever linear order is computed, we will get the same results.
diff --git a/test/593-checker-boolean-2-integral-conv/build b/test/593-checker-boolean-2-integral-conv/build
index 3721955..49292c9 100755
--- a/test/593-checker-boolean-2-integral-conv/build
+++ b/test/593-checker-boolean-2-integral-conv/build
@@ -20,7 +20,4 @@
# Also disable desugar because it is missing in jack platform builds.
export DESUGAR=false
-# See b/65168732
-export USE_D8=false
-
./default-build "$@"
diff --git a/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali b/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali
index 00ebaaf..494ab95 100644
--- a/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali
+++ b/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali
@@ -30,6 +30,143 @@
return-void
.end method
+## CHECK-START: byte SmaliTests.booleanToByte(boolean) builder (after)
+## CHECK: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Zero>>]
+## CHECK-DAG: If [<<Cond>>]
+## CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
+## CHECK-DAG: <<IToS:b\d+>> TypeConversion [<<Phi>>]
+## CHECK-DAG: Return [<<IToS>>]
+
+## CHECK-START: byte SmaliTests.booleanToByte(boolean) select_generator (after)
+## CHECK: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
+## CHECK-DAG: <<IToS:b\d+>> TypeConversion [<<Sel>>]
+## CHECK-DAG: Return [<<IToS>>]
+
+## CHECK-START: byte SmaliTests.booleanToByte(boolean) instruction_simplifier$after_bce (after)
+## CHECK: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: Return [<<Arg>>]
+.method static booleanToByte(Z)B
+ .registers 2
+ if-eqz p0, :cond_5
+ const/4 v0, 0x1
+
+ :goto_3
+ int-to-byte v0, v0
+ return v0
+
+ :cond_5
+ const/4 v0, 0x0
+ goto :goto_3
+.end method
+
+## CHECK-START: short SmaliTests.booleanToShort(boolean) builder (after)
+## CHECK: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Zero>>]
+## CHECK-DAG: If [<<Cond>>]
+## CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
+## CHECK-DAG: <<IToS:s\d+>> TypeConversion [<<Phi>>]
+## CHECK-DAG: Return [<<IToS>>]
+
+## CHECK-START: short SmaliTests.booleanToShort(boolean) select_generator (after)
+## CHECK: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
+## CHECK-DAG: <<IToS:s\d+>> TypeConversion [<<Sel>>]
+## CHECK-DAG: Return [<<IToS>>]
+
+## CHECK-START: short SmaliTests.booleanToShort(boolean) instruction_simplifier$after_bce (after)
+## CHECK: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: Return [<<Arg>>]
+.method static booleanToShort(Z)S
+ .registers 2
+ if-eqz p0, :cond_5
+ const/4 v0, 0x1
+
+ :goto_3
+ int-to-short v0, v0
+ return v0
+
+ :cond_5
+ const/4 v0, 0x0
+ goto :goto_3
+.end method
+
+## CHECK-START: char SmaliTests.booleanToChar(boolean) builder (after)
+## CHECK: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Zero>>]
+## CHECK-DAG: If [<<Cond>>]
+## CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
+## CHECK-DAG: <<IToC:c\d+>> TypeConversion [<<Phi>>]
+## CHECK-DAG: Return [<<IToC>>]
+
+## CHECK-START: char SmaliTests.booleanToChar(boolean) select_generator (after)
+## CHECK: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
+## CHECK-DAG: <<IToC:c\d+>> TypeConversion [<<Sel>>]
+## CHECK-DAG: Return [<<IToC>>]
+
+## CHECK-START: char SmaliTests.booleanToChar(boolean) instruction_simplifier$after_bce (after)
+## CHECK: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: Return [<<Arg>>]
+.method static booleanToChar(Z)C
+ .registers 2
+ if-eqz p0, :cond_5
+ const/4 v0, 0x1
+
+ :goto_3
+ int-to-char v0, v0
+ return v0
+
+ :cond_5
+ const/4 v0, 0x0
+ goto :goto_3
+.end method
+
+## CHECK-START: int SmaliTests.booleanToInt(boolean) builder (after)
+## CHECK: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Zero>>]
+## CHECK-DAG: If [<<Cond>>]
+## CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
+## CHECK-DAG: Return [<<Phi>>]
+
+## CHECK-START: int SmaliTests.booleanToInt(boolean) select_generator (after)
+## CHECK: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
+## CHECK-DAG: Return [<<Sel>>]
+
+## CHECK-START: int SmaliTests.booleanToInt(boolean) instruction_simplifier$after_bce (after)
+## CHECK: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: Return [<<Arg>>]
+.method static booleanToInt(Z)I
+ .registers 2
+ if-eqz p0, :cond_4
+ const/4 v0, 0x1
+
+ :goto_3
+ return v0
+
+ :cond_4
+ const/4 v0, 0x0
+ goto :goto_3
+.end method
+
## CHECK-START: long SmaliTests.booleanToLong(boolean) builder (after)
## CHECK-DAG: <<Arg:z\d+>> ParameterValue
## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
diff --git a/test/593-checker-boolean-2-integral-conv/src/Main.java b/test/593-checker-boolean-2-integral-conv/src/Main.java
index 3503b2e..fdc0919 100644
--- a/test/593-checker-boolean-2-integral-conv/src/Main.java
+++ b/test/593-checker-boolean-2-integral-conv/src/Main.java
@@ -32,24 +32,6 @@
System.out.println("passed");
}
- /// CHECK-START: byte Main.booleanToByte(boolean) builder (after)
- /// CHECK: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Zero>>]
- /// CHECK-DAG: If [<<Cond>>]
- /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
- /// CHECK-DAG: <<IToS:b\d+>> TypeConversion [<<Phi>>]
- /// CHECK-DAG: Return [<<IToS>>]
-
- /// CHECK-START: byte Main.booleanToByte(boolean) select_generator (after)
- /// CHECK: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
- /// CHECK-DAG: <<IToS:b\d+>> TypeConversion [<<Sel>>]
- /// CHECK-DAG: Return [<<IToS>>]
-
/// CHECK-START: byte Main.booleanToByte(boolean) instruction_simplifier$after_bce (after)
/// CHECK: <<Arg:z\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
@@ -58,24 +40,6 @@
return (byte)(b ? 1 : 0);
}
- /// CHECK-START: short Main.booleanToShort(boolean) builder (after)
- /// CHECK: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Zero>>]
- /// CHECK-DAG: If [<<Cond>>]
- /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
- /// CHECK-DAG: <<IToS:s\d+>> TypeConversion [<<Phi>>]
- /// CHECK-DAG: Return [<<IToS>>]
-
- /// CHECK-START: short Main.booleanToShort(boolean) select_generator (after)
- /// CHECK: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
- /// CHECK-DAG: <<IToS:s\d+>> TypeConversion [<<Sel>>]
- /// CHECK-DAG: Return [<<IToS>>]
-
/// CHECK-START: short Main.booleanToShort(boolean) instruction_simplifier$after_bce (after)
/// CHECK: <<Arg:z\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
@@ -84,24 +48,6 @@
return (short)(b ? 1 : 0);
}
- /// CHECK-START: char Main.booleanToChar(boolean) builder (after)
- /// CHECK: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Zero>>]
- /// CHECK-DAG: If [<<Cond>>]
- /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
- /// CHECK-DAG: <<IToC:c\d+>> TypeConversion [<<Phi>>]
- /// CHECK-DAG: Return [<<IToC>>]
-
- /// CHECK-START: char Main.booleanToChar(boolean) select_generator (after)
- /// CHECK: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
- /// CHECK-DAG: <<IToC:c\d+>> TypeConversion [<<Sel>>]
- /// CHECK-DAG: Return [<<IToC>>]
-
/// CHECK-START: char Main.booleanToChar(boolean) instruction_simplifier$after_bce (after)
/// CHECK: <<Arg:z\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
@@ -110,22 +56,6 @@
return (char)(b ? 1 : 0);
}
- /// CHECK-START: int Main.booleanToInt(boolean) builder (after)
- /// CHECK: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Zero>>]
- /// CHECK-DAG: If [<<Cond>>]
- /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
- /// CHECK-DAG: Return [<<Phi>>]
-
- /// CHECK-START: int Main.booleanToInt(boolean) select_generator (after)
- /// CHECK: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
- /// CHECK-DAG: Return [<<Sel>>]
-
/// CHECK-START: int Main.booleanToInt(boolean) instruction_simplifier$after_bce (after)
/// CHECK: <<Arg:z\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
diff --git a/test/611-checker-simplify-if/build b/test/611-checker-simplify-if/build
deleted file mode 100644
index 10ffcc5..0000000
--- a/test/611-checker-simplify-if/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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.
-
-# See b/65168732
-export USE_D8=false
-
-./default-build "$@"
diff --git a/test/661-checker-simd-reduc/src/Main.java b/test/661-checker-simd-reduc/src/Main.java
index 57abf5d..1add0f1 100644
--- a/test/661-checker-simd-reduc/src/Main.java
+++ b/test/661-checker-simd-reduc/src/Main.java
@@ -81,6 +81,16 @@
/// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none
/// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none
+ //
+ /// CHECK-START-MIPS64: int Main.reductionInt(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none
+ /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecAdd [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none
+ /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none
private static int reductionInt(int[] x) {
int sum = 0;
for (int i = 0; i < x.length; i++) {
@@ -144,6 +154,25 @@
//
/// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>"
//
+ /// CHECK-START-MIPS64: int Main.reductionIntChain() loop_optimization (after)
+ /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none
+ /// CHECK-DAG: <<Set1:d\d+>> VecSetScalars [{{i\d+}}] loop:none
+ /// CHECK-DAG: <<Phi1:d\d+>> Phi [<<Set1>>,{{d\d+}}] loop:<<Loop1:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<I1:i\d+>>] loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: VecAdd [<<Phi1>>,<<Load1>>] loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: Add [<<I1>>,<<Cons4>>] loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: <<Red1:d\d+>> VecReduce [<<Phi1>>] loop:none
+ /// CHECK-DAG: <<Extr1:i\d+>> VecExtractScalar [<<Red1>>] loop:none
+ /// CHECK-DAG: <<Set2:d\d+>> VecSetScalars [{{i\d+}}] loop:none
+ /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set2>>,{{d\d+}}] loop:<<Loop2:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<I2:i\d+>>] loop:<<Loop2>> outer_loop:none
+ /// CHECK-DAG: VecAdd [<<Phi2>>,<<Load2>>] loop:<<Loop2>> outer_loop:none
+ /// CHECK-DAG: Add [<<I2>>,<<Cons4>>] loop:<<Loop2>> outer_loop:none
+ /// CHECK-DAG: <<Red2:d\d+>> VecReduce [<<Phi2>>] loop:none
+ /// CHECK-DAG: <<Extr2:i\d+>> VecExtractScalar [<<Red2>>] loop:none
+ //
+ /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>"
+ //
// NOTE: pattern is robust with respect to vector loop unrolling and peeling.
private static int reductionIntChain() {
int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
@@ -189,6 +218,16 @@
/// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop1>> outer_loop:none
/// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none
/// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none
+ //
+ /// CHECK-START-MIPS64: int Main.reductionIntToLoop(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none
+ /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop1:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: VecAdd [<<Phi>>,<<Load>>] loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none
+ /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none
private static int reductionIntToLoop(int[] x) {
int r = 0;
for (int i = 0; i < 4; i++) {
@@ -220,6 +259,16 @@
/// CHECK-DAG: Add [<<I>>,<<Cons2>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none
/// CHECK-DAG: <<Extr:j\d+>> VecExtractScalar [<<Red>>] loop:none
+ //
+ /// CHECK-START-MIPS64: long Main.reductionLong(long[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{j\d+}}] loop:none
+ /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecAdd [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<I>>,<<Cons2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none
+ /// CHECK-DAG: <<Extr:j\d+>> VecExtractScalar [<<Red>>] loop:none
private static long reductionLong(long[] x) {
long sum = 0;
for (int i = 0; i < x.length; i++) {
@@ -282,6 +331,16 @@
/// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none
/// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none
+ //
+ /// CHECK-START-MIPS64: int Main.reductionIntM1(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none
+ /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecAdd [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none
+ /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none
private static int reductionIntM1(int[] x) {
int sum = -1;
for (int i = 0; i < x.length; i++) {
@@ -310,6 +369,16 @@
/// CHECK-DAG: Add [<<I>>,<<Cons2>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none
/// CHECK-DAG: <<Extr:j\d+>> VecExtractScalar [<<Red>>] loop:none
+ //
+ /// CHECK-START-MIPS64: long Main.reductionLongM1(long[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{j\d+}}] loop:none
+ /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecAdd [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<I>>,<<Cons2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none
+ /// CHECK-DAG: <<Extr:j\d+>> VecExtractScalar [<<Red>>] loop:none
private static long reductionLongM1(long[] x) {
long sum = -1L;
for (int i = 0; i < x.length; i++) {
@@ -371,6 +440,16 @@
/// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none
/// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none
+ //
+ /// CHECK-START-MIPS64: int Main.reductionMinusInt(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none
+ /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecSub [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none
+ /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none
private static int reductionMinusInt(int[] x) {
int sum = 0;
for (int i = 0; i < x.length; i++) {
@@ -399,6 +478,16 @@
/// CHECK-DAG: Add [<<I>>,<<Cons2>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none
/// CHECK-DAG: <<Extr:j\d+>> VecExtractScalar [<<Red>>] loop:none
+ //
+ /// CHECK-START-MIPS64: long Main.reductionMinusLong(long[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{j\d+}}] loop:none
+ /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecSub [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<I>>,<<Cons2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none
+ /// CHECK-DAG: <<Extr:j\d+>> VecExtractScalar [<<Red>>] loop:none
private static long reductionMinusLong(long[] x) {
long sum = 0;
for (int i = 0; i < x.length; i++) {
@@ -461,6 +550,16 @@
/// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none
/// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none
+ //
+ /// CHECK-START-MIPS64: int Main.reductionMinInt(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecReplicateScalar [{{i\d+}}] loop:none
+ /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecMin [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none
+ /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none
private static int reductionMinInt(int[] x) {
int min = Integer.MAX_VALUE;
for (int i = 0; i < x.length; i++) {
@@ -531,6 +630,16 @@
/// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none
/// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none
+ //
+ /// CHECK-START-MIPS64: int Main.reductionMaxInt(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecReplicateScalar [{{i\d+}}] loop:none
+ /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecMax [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none
+ /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none
private static int reductionMaxInt(int[] x) {
int max = Integer.MIN_VALUE;
for (int i = 0; i < x.length; i++) {
diff --git a/test/665-checker-simd-zero/src/Main.java b/test/665-checker-simd-zero/src/Main.java
index 66eea64..6cd6d64 100644
--- a/test/665-checker-simd-zero/src/Main.java
+++ b/test/665-checker-simd-zero/src/Main.java
@@ -29,6 +29,12 @@
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-MIPS64: void Main.zeroz(boolean[]) loop_optimization (after)
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none
private static void zeroz(boolean[] x) {
for (int i = 0; i < x.length; i++) {
x[i] = false;
@@ -45,6 +51,12 @@
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-MIPS64: void Main.zerob(byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none
private static void zerob(byte[] x) {
for (int i = 0; i < x.length; i++) {
x[i] = 0;
@@ -61,6 +73,12 @@
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-MIPS64: void Main.zeroc(char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none
private static void zeroc(char[] x) {
for (int i = 0; i < x.length; i++) {
x[i] = 0;
@@ -77,6 +95,12 @@
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-MIPS64: void Main.zeros(short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none
private static void zeros(short[] x) {
for (int i = 0; i < x.length; i++) {
x[i] = 0;
@@ -93,6 +117,12 @@
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-MIPS64: void Main.zeroi(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none
private static void zeroi(int[] x) {
for (int i = 0; i < x.length; i++) {
x[i] = 0;
@@ -109,6 +139,12 @@
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-MIPS64: void Main.zerol(long[]) loop_optimization (after)
+ /// CHECK-DAG: <<Zero:j\d+>> LongConstant 0 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none
private static void zerol(long[] x) {
for (int i = 0; i < x.length; i++) {
x[i] = 0;
@@ -125,6 +161,12 @@
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-MIPS64: void Main.zerof(float[]) loop_optimization (after)
+ /// CHECK-DAG: <<Zero:f\d+>> FloatConstant 0 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none
private static void zerof(float[] x) {
for (int i = 0; i < x.length; i++) {
x[i] = 0;
@@ -141,6 +183,12 @@
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-MIPS64: void Main.zerod(double[]) loop_optimization (after)
+ /// CHECK-DAG: <<Zero:d\d+>> DoubleConstant 0 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none
private static void zerod(double[] x) {
for (int i = 0; i < x.length; i++) {
x[i] = 0;
diff --git a/test/667-checker-simd-alignment/expected.txt b/test/667-checker-simd-alignment/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/667-checker-simd-alignment/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/667-checker-simd-alignment/info.txt b/test/667-checker-simd-alignment/info.txt
new file mode 100644
index 0000000..a46bfaa
--- /dev/null
+++ b/test/667-checker-simd-alignment/info.txt
@@ -0,0 +1 @@
+Test SIMD vectorization alignment optimizations.
diff --git a/test/667-checker-simd-alignment/src/Main.java b/test/667-checker-simd-alignment/src/Main.java
new file mode 100644
index 0000000..a6235b8
--- /dev/null
+++ b/test/667-checker-simd-alignment/src/Main.java
@@ -0,0 +1,337 @@
+/*
+ * 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 zero vectorization.
+ */
+public class Main {
+
+ /// CHECK-START: void Main.staticallyAligned(int[]) loop_optimization (before)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet [<<Par>>,<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get>>,<<One>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Par>>,<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM: void Main.staticallyAligned(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Vl:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<One>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Nrm:i\d+>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [<<Par>>,<<Nrm>>] alignment:ALIGN(8,0) loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:d\d+>> VecAdd [<<Load>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [<<Par>>,<<Nrm>>,<<Add>>] alignment:ALIGN(8,0) loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<Vl>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-NOT: ArrayGet
+ /// CHECK-NOT: ArraySet
+ static void staticallyAligned(int[] a) {
+ // Starts at offset 12 (hidden) + 1 * 4 relative to base alignment.
+ // So no peeling, aligned vector, no cleanup.
+ for (int i = 1; i < 9; i++) {
+ a[i] += 1;
+ }
+ }
+
+ /// CHECK-START: void Main.staticallyAlignedN(int[]) loop_optimization (before)
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Par:l\d+>> NullCheck loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet [<<Par>>,<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get>>,<<One>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Par>>,<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM: void Main.staticallyAlignedN(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Vl:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<Par:l\d+>> NullCheck loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<One>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Nrm:i\d+>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [<<Par>>,<<Nrm>>] alignment:ALIGN(8,0) loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:d\d+>> VecAdd [<<Load>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [<<Par>>,<<Nrm>>,<<Add>>] alignment:ALIGN(8,0) loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<Vl>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<PhiC:i\d+>> Phi [<<Phi>>,<<AddIC:i\d+>>] loop:<<Clean:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<NrmC:i\d+>> Add [<<PhiC>>,<<One>>] loop:<<Clean>> outer_loop:none
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet [<<Par>>,<<NrmC>>] loop:<<Clean>> outer_loop:none
+ /// CHECK-DAG: <<AddC:i\d+>> Add [<<Get>>,<<One>>] loop:<<Clean>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Par>>,<<NrmC>>,<<AddC>>] loop:<<Clean>> outer_loop:none
+ /// CHECK-DAG: <<AddIC>> Add [<<PhiC>>,<<One>>] loop:<<Clean>> outer_loop:none
+ static void staticallyAlignedN(int[] a) {
+ // Starts at offset 12 (hidden) + 1 * 4 relative to base alignment.
+ // So no peeling, aligned vector, cleanup.
+ for (int i = 1; i < a.length; i++) {
+ a[i] += 1;
+ }
+ }
+
+ /// CHECK-START: void Main.staticallyMisaligned(int[]) loop_optimization (before)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet [<<Par>>,<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get>>,<<One>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Par>>,<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM: void Main.staticallyMisaligned(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Vl:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<PhiP:i\d+>> Phi [<<Zero>>,<<AddIP:i\d+>>] loop:<<Peel:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet [<<Par>>,<<PhiP>>] loop:<<Peel>> outer_loop:none
+ /// CHECK-DAG: <<AddP:i\d+>> Add [<<Get>>,<<One>>] loop:<<Peel>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Par>>,<<PhiP>>,<<AddP>>] loop:<<Peel>> outer_loop:none
+ /// CHECK-DAG: <<AddIP>> Add [<<PhiP>>,<<One>>] loop:<<Peel>> outer_loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<One>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<PhiP>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [<<Par>>,<<Phi>>] alignment:ALIGN(8,0) loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:d\d+>> VecAdd [<<Load>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [<<Par>>,<<Phi>>,<<Add>>] alignment:ALIGN(8,0) loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<Vl>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-NOT: ArrayGet
+ /// CHECK-NOT: ArraySet
+ static void staticallyMisaligned(int[] a) {
+ // Starts at offset 12 (hidden) + 0 * 4 relative to base alignment.
+ // Yes, Art runtime misaligns the most common access pattern :-(
+ // Static peeling to the rescue, aligned vector, no cleanup.
+ for (int i = 0; i < 9; i++) {
+ a[i] += 1;
+ }
+ }
+
+ /// CHECK-START: void Main.staticallyMisalignedN(int[]) loop_optimization (before)
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Par:l\d+>> NullCheck loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet [<<Par>>,<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get>>,<<One>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Par>>,<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM: void Main.staticallyMisalignedN(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Vl:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<Par:l\d+>> NullCheck loop:none
+ /// CHECK-DAG: <<PhiP:i\d+>> Phi [<<Zero>>,<<AddIP:i\d+>>] loop:<<Peel:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<GetP:i\d+>> ArrayGet [<<Par>>,<<PhiP>>] loop:<<Peel>> outer_loop:none
+ /// CHECK-DAG: <<AddP:i\d+>> Add [<<GetP>>,<<One>>] loop:<<Peel>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Par>>,<<PhiP>>,<<AddP>>] loop:<<Peel>> outer_loop:none
+ /// CHECK-DAG: <<AddIP>> Add [<<PhiP>>,<<One>>] loop:<<Peel>> outer_loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<One>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<PhiP>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [<<Par>>,<<Phi>>] alignment:ALIGN(8,0) loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:d\d+>> VecAdd [<<Load>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [<<Par>>,<<Phi>>,<<Add>>] alignment:ALIGN(8,0) loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<Vl>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<PhiC:i\d+>> Phi [<<Phi>>,<<AddIC:i\d+>>] loop:<<Clean:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<GetC:i\d+>> ArrayGet [<<Par>>,<<PhiC>>] loop:<<Clean>> outer_loop:none
+ /// CHECK-DAG: <<AddC:i\d+>> Add [<<GetC>>,<<One>>] loop:<<Clean>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Par>>,<<PhiC>>,<<AddC>>] loop:<<Clean>> outer_loop:none
+ /// CHECK-DAG: <<AddIC>> Add [<<PhiC>>,<<One>>] loop:<<Clean>> outer_loop:none
+ static void staticallyMisalignedN(int[] a) {
+ // Starts at offset 12 (hidden) + 0 * 4 relative to base alignment.
+ // Yes, Art runtime misaligns the most common access pattern :-(
+ // Static peeling to the rescue, aligned vector, cleanup.
+ for (int i = 0; i < a.length; i++) {
+ a[i] += 1;
+ }
+ }
+
+ /// CHECK-START: void Main.staticallyUnknownAligned(int[], int) loop_optimization (before)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Off:i\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Nrm:i\d+>> Add [<<Off>>,<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet [<<Par>>,<<Nrm>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get>>,<<One>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Par>>,<<Nrm>>,<<Add>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM: void Main.staticallyUnknownAligned(int[], int) loop_optimization (after)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Off:i\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Vl:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<PhiP:i\d+>> Phi [<<Zero>>,<<AddIP:i\d+>>] loop:<<Peel:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<NrmP:i\d+>> Add [<<PhiP>>,<<Off>>] loop:<<Peel>> outer_loop:none
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet [<<Par>>,<<NrmP>>] loop:<<Peel>> outer_loop:none
+ /// CHECK-DAG: <<AddP:i\d+>> Add [<<Get>>,<<One>>] loop:<<Peel>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Par>>,<<NrmP>>,<<AddP>>] loop:<<Peel>> outer_loop:none
+ /// CHECK-DAG: <<AddIP>> Add [<<PhiP>>,<<One>>] loop:<<Peel>> outer_loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<One>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<PhiP>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Nrm:i\d+>> Add [<<Phi>>,<<Off>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [<<Par>>,<<Nrm>>] alignment:ALIGN(8,0) loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:d\d+>> VecAdd [<<Load>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [<<Par>>,<<Nrm>>,<<Add>>] alignment:ALIGN(8,0) loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<Vl>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<PhiC:i\d+>> Phi [<<Phi>>,<<AddIC:i\d+>>] loop:<<Clean:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<NrmC:i\d+>> Add [<<PhiC>>,<<Off>>] loop:<<Clean>> outer_loop:none
+ /// CHECK-DAG: <<GetC:i\d+>> ArrayGet [<<Par>>,<<NrmC>>] loop:<<Clean>> outer_loop:none
+ /// CHECK-DAG: <<AddC:i\d+>> Add [<<GetC>>,<<One>>] loop:<<Clean>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Par>>,<<NrmC>>,<<AddC>>] loop:<<Clean>> outer_loop:none
+ /// CHECK-DAG: <<AddIC>> Add [<<PhiC>>,<<One>>] loop:<<Clean>> outer_loop:none
+ static void staticallyUnknownAligned(int[] a, int off) {
+ // Starts at an unknown offset due to parameter off.
+ // Dynamic peeling to the rescue, aligned vector, cleanup.
+ for (int i = 0; i < 9; i++) {
+ a[off + i] += 1;
+ }
+ }
+
+ /// CHECK-START: void Main.staticallyUnknownAlignedN(int[], int, int) loop_optimization (before)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Off:i\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Nrm:i\d+>> Add [<<Off>>,<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet [<<Par>>,<<Nrm>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get>>,<<One>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Par>>,<<Nrm>>,<<Add>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM: void Main.staticallyUnknownAlignedN(int[], int, int) loop_optimization (after)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Off:i\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Vl:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<PhiP:i\d+>> Phi [<<Zero>>,<<AddIP:i\d+>>] loop:<<Peel:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<NrmP:i\d+>> Add [<<PhiP>>,<<Off>>] loop:<<Peel>> outer_loop:none
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet [<<Par>>,<<NrmP>>] loop:<<Peel>> outer_loop:none
+ /// CHECK-DAG: <<AddP:i\d+>> Add [<<Get>>,<<One>>] loop:<<Peel>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Par>>,<<NrmP>>,<<AddP>>] loop:<<Peel>> outer_loop:none
+ /// CHECK-DAG: <<AddIP>> Add [<<PhiP>>,<<One>>] loop:<<Peel>> outer_loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<One>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<PhiP>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Nrm:i\d+>> Add [<<Phi>>,<<Off>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [<<Par>>,<<Nrm>>] alignment:ALIGN(8,0) loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:d\d+>> VecAdd [<<Load>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [<<Par>>,<<Nrm>>,<<Add>>] alignment:ALIGN(8,0) loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<Vl>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<PhiC:i\d+>> Phi [<<Phi>>,<<AddIC:i\d+>>] loop:<<Clean:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<NrmC:i\d+>> Add [<<PhiC>>,<<Off>>] loop:<<Clean>> outer_loop:none
+ /// CHECK-DAG: <<GetC:i\d+>> ArrayGet [<<Par>>,<<NrmC>>] loop:<<Clean>> outer_loop:none
+ /// CHECK-DAG: <<AddC:i\d+>> Add [<<GetC>>,<<One>>] loop:<<Clean>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Par>>,<<NrmC>>,<<AddC>>] loop:<<Clean>> outer_loop:none
+ /// CHECK-DAG: <<AddIC>> Add [<<PhiC>>,<<One>>] loop:<<Clean>> outer_loop:none
+ static void staticallyUnknownAlignedN(int[] a, int off, int n) {
+ // Starts at an unknown offset due to parameter off.
+ // Dynamic peeling to the rescue, aligned vector, cleanup.
+ for (int i = 0; i < n; i++) {
+ a[off + i] += 1;
+ }
+ }
+
+ //
+ // Test drivers.
+ //
+
+ private static void test1() {
+ int[] a = new int[9];
+ staticallyAligned(a);
+ for (int i = 0; i < a.length; i++) {
+ int e = i > 0 ? 1 : 0;
+ expectEquals(e, a[i]);
+ }
+ }
+
+ private static void test2() {
+ for (int n = 0; n <= 71; n++) {
+ int[] a = new int[n];
+ staticallyAlignedN(a);
+ for (int i = 0; i < a.length; i++) {
+ int e = i > 0 ? 1 : 0;
+ expectEquals(e, a[i]);
+ }
+ }
+ }
+
+ private static void test3() {
+ int[] a = new int[9];
+ staticallyMisaligned(a);
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(1, a[i]);
+ }
+ }
+
+ private static void test4() {
+ for (int n = 0; n <= 71; n++) {
+ int[] a = new int[n];
+ staticallyMisalignedN(a);
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(1, a[i]);
+ }
+ }
+ }
+
+ private static void test5() {
+ for (int off = 0; off <= 8; off++) {
+ int[] a = new int[17];
+ staticallyUnknownAligned(a, off);
+ for (int i = 0; i < a.length; i++) {
+ int e = (off <= i && i < off + 9) ? 1 : 0;
+ expectEquals(e, a[i]);
+ }
+ }
+ }
+
+ private static void test6() {
+ for (int off = 0; off <= 8; off++) {
+ for (int n = 0; n <= 9; n++) {
+ int[] a = new int[17];
+ staticallyUnknownAlignedN(a, off, n);
+ for (int i = 0; i < a.length; i++) {
+ int e = (off <= i && i < off + n) ? 1 : 0;
+ expectEquals(e, a[i]);
+ }
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ test1();
+ test2();
+ test4();
+ test5();
+ test6();
+ 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/910-methods/check b/test/910-methods/check
index e6f7d77..76b23cb 100644
--- a/test/910-methods/check
+++ b/test/910-methods/check
@@ -19,8 +19,14 @@
patch -p0 expected.txt < expected_jack.diff
fi
-if [[ "$USE_D8" == true ]]; then
- patch -p0 expected.txt < expected_d8.diff
+./default-check "$@"
+if [[ "$?" == "0" ]]; then
+ exit 0;
fi
+# We cannot always correctly determine if D8 was used because of (b/68406220).
+# So we are just going to try to see it matches the expect output of D8 no
+# matter what.
+patch -p0 expected.txt < expected_d8.diff
+
./default-check "$@"
diff --git a/test/988-method-trace/gen_srcs.py b/test/988-method-trace/gen_srcs.py
index 8f1082f..225f41b 100755
--- a/test/988-method-trace/gen_srcs.py
+++ b/test/988-method-trace/gen_srcs.py
@@ -28,8 +28,8 @@
from string import Template
-# Relative path to art/compiler/intrinsics_list.h
-INTRINSICS_LIST_H = os.path.dirname(os.path.realpath(__file__)) + "/../../compiler/intrinsics_list.h"
+# Relative path to art/runtime/intrinsics_list.h
+INTRINSICS_LIST_H = os.path.dirname(os.path.realpath(__file__)) + "/../../runtime/intrinsics_list.h"
# Macro parameter index to V(). Negative means from the end.
IDX_STATIC_OR_VIRTUAL = 1
@@ -39,7 +39,8 @@
# Exclude all hidden API.
KLASS_BLACK_LIST = ['sun.misc.Unsafe', 'libcore.io.Memory', 'java.lang.StringFactory',
- 'java.lang.invoke.VarHandle' ] # TODO(b/65872996): Enable when VarHandle is visible.
+ 'java.lang.invoke.MethodHandle', # invokes are tested by 956-method-handles
+ 'java.lang.invoke.VarHandle' ] # TODO(b/65872996): will tested separately
METHOD_BLACK_LIST = [('java.lang.ref.Reference', 'getReferent'),
('java.lang.String', 'getCharsNoCheck'),
('java.lang.System', 'arraycopy')] # arraycopy has a manual test.
@@ -90,7 +91,7 @@
}
static void test() {
- // Call each intrinsic from art/compiler/intrinsics_list.h to make sure they are traced.
+ // Call each intrinsic from art/runtime/intrinsics_list.h to make sure they are traced.
$test_body
}
}
diff --git a/test/988-method-trace/src/art/Test988Intrinsics.java b/test/988-method-trace/src/art/Test988Intrinsics.java
index 099fbf2..3069f1a 100644
--- a/test/988-method-trace/src/art/Test988Intrinsics.java
+++ b/test/988-method-trace/src/art/Test988Intrinsics.java
@@ -44,7 +44,7 @@
}
static void test() {
- // Call each intrinsic from art/compiler/intrinsics_list.h to make sure they are traced.
+ // Call each intrinsic from art/runtime/intrinsics_list.h to make sure they are traced.
java.lang.Double.doubleToRawLongBits(0.0);
java.lang.Double.doubleToLongBits(0.0);
java.lang.Double.isInfinite(0.0);
diff --git a/test/993-breakpoints/breakpoints.cc b/test/993-breakpoints/breakpoints.cc
index 3734ce8..e9cf3b3 100644
--- a/test/993-breakpoints/breakpoints.cc
+++ b/test/993-breakpoints/breakpoints.cc
@@ -49,6 +49,57 @@
}
extern "C" JNIEXPORT
+void JNICALL Java_art_Test993_invokeNativeObject(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jobject target,
+ jclass clazz,
+ jobject thizz) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ if (thizz == nullptr) {
+ env->CallStaticObjectMethod(clazz, method);
+ } else {
+ env->CallObjectMethod(thizz, method);
+ }
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test993_invokeNativeBool(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jobject target,
+ jclass clazz,
+ jobject thizz) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ if (thizz == nullptr) {
+ env->CallStaticBooleanMethod(clazz, method);
+ } else {
+ env->CallBooleanMethod(thizz, method);
+ }
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test993_invokeNativeLong(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jobject target,
+ jclass clazz,
+ jobject thizz) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ if (thizz == nullptr) {
+ env->CallStaticLongMethod(clazz, method);
+ } else {
+ env->CallLongMethod(thizz, method);
+ }
+}
+
+extern "C" JNIEXPORT
void JNICALL Java_art_Test993_invokeNative(JNIEnv* env,
jclass klass ATTRIBUTE_UNUSED,
jobject target,
diff --git a/test/993-breakpoints/expected.txt b/test/993-breakpoints/expected.txt
index 9621547..1749a77 100644
--- a/test/993-breakpoints/expected.txt
+++ b/test/993-breakpoints/expected.txt
@@ -552,6 +552,107 @@
Breakpoint: private void art.Test993$TestClass4.privateMethod() @ line=118
Invoking "new TestClass4().callPrivateMethod()"
Breakpoint: private void art.Test993$TestClass4.privateMethod() @ line=118
+Running Vector constructor
+ Breaking on []
+ Native constructor: public java.util.Vector(), type: class java.util.Vector
+ Created: []
+ Reflective constructor: public java.util.Vector()
+ Created: []
+ Constructing: new Vector()
+ Created: []
+ Breaking on [public java.util.Vector() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Vector(), type: class java.util.Vector
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Vector()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Vector()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+Running Stack constructor
+ Breaking on []
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Created: []
+ Constructing: new Stack()
+ Created: []
+ Breaking on [public java.util.Stack() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Breaking on [public java.util.Vector() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Stack()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Breaking on [public java.util.Stack() @ <NON-DETERMINISTIC>, public java.util.Vector() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+Running bcp static invoke
+ Breaking on []
+ Native invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Reflective invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Invoking "Optional::empty"
+ Breaking on [public static java.util.Optional java.util.Optional.empty() @ <NON-DETERMINISTIC>]
+ Native invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Breakpoint: public static java.util.Optional java.util.Optional.empty() @ line=<NON-DETERMINISTIC>
+ Reflective invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Breakpoint: public static java.util.Optional java.util.Optional.empty() @ line=<NON-DETERMINISTIC>
+ Invoking "Optional::empty"
+ Breakpoint: public static java.util.Optional java.util.Optional.empty() @ line=<NON-DETERMINISTIC>
+Running bcp private static invoke
+ Breaking on []
+ Native invoking: private static long java.util.Random.seedUniquifier() args: [this: null]
+ Invoking "Random::seedUniquifier"
+ Breaking on [private static long java.util.Random.seedUniquifier() @ <NON-DETERMINISTIC>]
+ Native invoking: private static long java.util.Random.seedUniquifier() args: [this: null]
+ Breakpoint: private static long java.util.Random.seedUniquifier() @ line=<NON-DETERMINISTIC>
+ Invoking "Random::seedUniquifier"
+ Breakpoint: private static long java.util.Random.seedUniquifier() @ line=<NON-DETERMINISTIC>
+Running bcp private invoke
+ Breaking on []
+ Native invoking: private java.math.BigDecimal java.time.Duration.toSeconds() args: [this: PT336H]
+ Invoking "Duration::toSeconds"
+ Breaking on [private java.math.BigDecimal java.time.Duration.toSeconds() @ <NON-DETERMINISTIC>]
+ Native invoking: private java.math.BigDecimal java.time.Duration.toSeconds() args: [this: PT336H]
+ Breakpoint: private java.math.BigDecimal java.time.Duration.toSeconds() @ line=<NON-DETERMINISTIC>
+ Invoking "Duration::toSeconds"
+ Breakpoint: private java.math.BigDecimal java.time.Duration.toSeconds() @ line=<NON-DETERMINISTIC>
+Running bcp invoke
+ Breaking on []
+ Native invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test]]
+ Reflective invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test2]]
+ Invoking "Optional::isPresent"
+ Breaking on [public boolean java.util.Optional.isPresent() @ <NON-DETERMINISTIC>]
+ Native invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test]]
+ Breakpoint: public boolean java.util.Optional.isPresent() @ line=<NON-DETERMINISTIC>
+ Reflective invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test2]]
+ Breakpoint: public boolean java.util.Optional.isPresent() @ line=<NON-DETERMINISTIC>
+ Invoking "Optional::isPresent"
+ Breakpoint: public boolean java.util.Optional.isPresent() @ line=<NON-DETERMINISTIC>
Running TestClass1 constructor
Breaking on []
Native constructor: public art.Test993$TestClass1(), type: class art.Test993$TestClass1
diff --git a/test/993-breakpoints/src/art/Test993.java b/test/993-breakpoints/src/art/Test993.java
index 781ebff..d6a6a67 100644
--- a/test/993-breakpoints/src/art/Test993.java
+++ b/test/993-breakpoints/src/art/Test993.java
@@ -16,20 +16,20 @@
package art;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.Arrays;
import java.lang.reflect.Executable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
-import java.util.List;
-import java.util.Set;
-import java.util.Spliterator;
-import java.util.Spliterators;
-import java.util.Collection;
+
+import java.time.Duration;
+
import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.function.IntUnaryOperator;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.Random;
+import java.util.Stack;
+import java.util.Vector;
+
import java.util.function.Supplier;
public class Test993 {
@@ -120,7 +120,13 @@
}
public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
- System.out.println("\t\t\tBreakpoint: " + e + " @ line=" + Breakpoint.locationToLine(e, loc));
+ String line;
+ if (e.getDeclaringClass().getPackage().equals(Test993.class.getPackage())) {
+ line = Integer.valueOf(Breakpoint.locationToLine(e, loc)).toString();
+ } else {
+ line = "<NON-DETERMINISTIC>";
+ }
+ System.out.println("\t\t\tBreakpoint: " + e + " @ line=" + line);
}
public static interface ThrowRunnable extends Runnable {
@@ -180,6 +186,57 @@
public static native void invokeNative(Method m, Class<?> clazz, Object thizz);
+ public static class InvokeNativeBool implements Runnable {
+ Method m;
+ Object this_arg;
+ public InvokeNativeBool(Method m, Object this_arg) {
+ this.m = m;
+ this.this_arg = this_arg;
+ }
+
+ @Override
+ public void run() {
+ System.out.println("\t\tNative invoking: " + m + " args: [this: " + this_arg + "]");
+ invokeNativeBool(m, m.getDeclaringClass(), this_arg);
+ }
+ }
+
+ public static native void invokeNativeBool(Method m, Class<?> clazz, Object thizz);
+
+ public static class InvokeNativeObject implements Runnable {
+ Method m;
+ Object this_arg;
+ public InvokeNativeObject(Method m, Object this_arg) {
+ this.m = m;
+ this.this_arg = this_arg;
+ }
+
+ @Override
+ public void run() {
+ System.out.println("\t\tNative invoking: " + m + " args: [this: " + this_arg + "]");
+ invokeNativeObject(m, m.getDeclaringClass(), this_arg);
+ }
+ }
+
+ public static native void invokeNativeObject(Method m, Class<?> clazz, Object thizz);
+
+ public static class InvokeNativeLong implements Runnable {
+ Method m;
+ Object this_arg;
+ public InvokeNativeLong(Method m, Object this_arg) {
+ this.m = m;
+ this.this_arg = this_arg;
+ }
+
+ @Override
+ public void run() {
+ System.out.println("\t\tNative invoking: " + m + " args: [this: " + this_arg + "]");
+ invokeNativeLong(m, m.getDeclaringClass(), this_arg);
+ }
+ }
+
+ public static native void invokeNativeLong(Method m, Class<?> clazz, Object thizz);
+
public static class ConstructDirect implements Runnable {
String msg;
Supplier<Object> s;
@@ -258,7 +315,15 @@
}
private static Breakpoint.Manager.BP BP(Executable m) {
- return new Breakpoint.Manager.BP(m);
+ return new Breakpoint.Manager.BP(m) {
+ public String toString() {
+ if (method.getDeclaringClass().getPackage().equals(Test993.class.getPackage())) {
+ return super.toString();
+ } else {
+ return method.toString() + " @ <NON-DETERMINISTIC>";
+ }
+ }
+ };
}
public static void run() throws Exception {
@@ -271,6 +336,7 @@
Thread.currentThread());
runMethodTests();
+ runBCPMethodTests();
runConstructorTests();
Breakpoint.stopBreakpointWatch(Thread.currentThread());
@@ -302,6 +368,94 @@
runTestGroups("TestClass1ext constructor", tc1ext_constructors, tc1ext_bps);
}
+ // These test to make sure we are able to break on functions that might have been quickened or
+ // inlined from the boot-image. These were all chosen for being in the bootclasspath, not being
+ // long enough to prevent inlining, and not being used for the testing framework.
+ public static void runBCPMethodTests() throws Exception {
+ // The methods we will be breaking on.
+ Method bcp_private_method = Duration.class.getDeclaredMethod("toSeconds");
+ Method bcp_virtual_method = Optional.class.getDeclaredMethod("isPresent");
+ Method bcp_static_method = Optional.class.getDeclaredMethod("empty");
+ Method bcp_private_static_method = Random.class.getDeclaredMethod("seedUniquifier");
+
+ // Some constructors we will break on.
+ Constructor<?> bcp_stack_constructor = Stack.class.getConstructor();
+ Constructor<?> bcp_vector_constructor = Vector.class.getConstructor();
+ if (!(Vector.class.isAssignableFrom(Stack.class))) {
+ throw new Error("Expected Stack to extend Vector!");
+ }
+
+ // BCP constructors.
+ Runnable[] vector_constructors = new Runnable[] {
+ new ConstructNative(bcp_vector_constructor),
+ new ConstructReflect(bcp_vector_constructor),
+ new ConstructDirect("new Vector()", Vector::new),
+ };
+ Breakpoint.Manager.BP[] vector_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(bcp_vector_constructor),
+ };
+ runTestGroups("Vector constructor", vector_constructors, vector_breakpoints);
+
+ Runnable[] stack_constructors = new Runnable[] {
+ new ConstructNative(bcp_stack_constructor),
+ new ConstructReflect(bcp_stack_constructor),
+ new ConstructDirect("new Stack()", Stack::new),
+ };
+ Breakpoint.Manager.BP[] stack_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(bcp_stack_constructor), BP(bcp_vector_constructor),
+ };
+ runTestGroups("Stack constructor", stack_constructors, stack_breakpoints);
+
+ // Static function
+ Runnable[] static_invokes = new Runnable[] {
+ new InvokeNativeObject(bcp_static_method, null),
+
+ new InvokeReflect(bcp_static_method, null),
+
+ new InvokeDirect("Optional::empty", () -> { Optional.empty(); }),
+ };
+ Breakpoint.Manager.BP[] static_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(bcp_static_method)
+ };
+ runTestGroups("bcp static invoke", static_invokes, static_breakpoints);
+
+ // Static private class function
+ Runnable[] private_static_invokes = new Runnable[] {
+ new InvokeNativeLong(bcp_private_static_method, null),
+
+ new InvokeDirect("Random::seedUniquifier", () -> { new Random(); }),
+ };
+ Breakpoint.Manager.BP[] private_static_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(bcp_private_static_method)
+ };
+ runTestGroups("bcp private static invoke", private_static_invokes, private_static_breakpoints);
+
+ // private class method
+ Duration test_duration = Duration.ofDays(14);
+ Runnable[] private_invokes = new Runnable[] {
+ new InvokeNativeObject(bcp_private_method, test_duration),
+
+ new InvokeDirect("Duration::toSeconds", () -> { test_duration.multipliedBy(2); }),
+ };
+ Breakpoint.Manager.BP[] private_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(bcp_private_method)
+ };
+ runTestGroups("bcp private invoke", private_invokes, private_breakpoints);
+
+ // class method
+ Runnable[] public_invokes = new Runnable[] {
+ new InvokeNativeBool(bcp_virtual_method, Optional.of("test")),
+
+ new InvokeReflect(bcp_virtual_method, Optional.of("test2")),
+
+ new InvokeDirect("Optional::isPresent", () -> { Optional.of("test3").isPresent(); }),
+ };
+ Breakpoint.Manager.BP[] public_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(bcp_virtual_method)
+ };
+ runTestGroups("bcp invoke", public_invokes, public_breakpoints);
+ }
+
public static void runMethodTests() throws Exception {
// The methods we will be breaking on.
Method breakpoint_method = Test993.class.getDeclaredMethod("breakpoint");
diff --git a/test/Android.bp b/test/Android.bp
index 16b30f9..17ef114 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -359,6 +359,7 @@
"141-class-unload/jni_unload.cc",
"148-multithread-gc-annotations/gc_coverage.cc",
"149-suspend-all-stress/suspend_all.cc",
+ "203-multi-checkpoint/multi_checkpoint.cc",
"154-gc-loop/heap_interface.cc",
"454-get-vreg/get_vreg_jni.cc",
"457-regs/regs_jni.cc",
diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk
index e5da385..a9a0492 100644
--- a/tools/ahat/Android.mk
+++ b/tools/ahat/Android.mk
@@ -30,7 +30,52 @@
# Let users with Java 7 run ahat (b/28303627)
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+# Make this available on the classpath of the general-tests tradefed suite.
+# It is used by libcore tests that run there.
+LOCAL_COMPATIBILITY_SUITE := general-tests
+
include $(BUILD_HOST_JAVA_LIBRARY)
+AHAT_JAR := $(LOCAL_BUILT_MODULE)
+AHAT_API := $(intermediates.COMMON)/ahat_api.txt
+AHAT_REMOVED_API := $(intermediates.COMMON)/ahat_removed_api.txt
+
+# --- api check for ahat.jar ----------
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under, src/main)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_MODULE := ahat
+LOCAL_DROIDDOC_OPTIONS := \
+ -stubpackages com.android.ahat:com.android.ahat.* \
+ -api $(AHAT_API) \
+ -removedApi $(AHAT_REMOVED_API)
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := external/doclava/res/assets/templates-sdk
+include $(BUILD_DROIDDOC)
+$(AHAT_API): $(full_target)
+
+$(eval $(call check-api, \
+ ahat-check-api, \
+ $(LOCAL_PATH)/etc/ahat_api.txt, \
+ $(AHAT_API), \
+ $(LOCAL_PATH)/etc/ahat_removed_api.txt, \
+ $(AHAT_REMOVED_API), \
+ -error 2 -error 3 -error 4 -error 5 -error 6 -error 7 -error 8 -error 9 -error 10 -error 11 \
+ -error 12 -error 13 -error 14 -error 15 -error 16 -error 17 -error 18 -error 19 -error 20 \
+ -error 21 -error 23 -error 24 -error 25 -error 26 -error 27, \
+ cat $(LOCAL_PATH)/etc/ahat_api_msg.txt, \
+ $(AHAT_JAR),))
+
+.PHONY: ahat-update-api
+ahat-update-api: PRIVATE_AHAT_API := $(AHAT_API)
+ahat-update-api: PRIVATE_AHAT_REMOVED_API := $(AHAT_REMOVED_API)
+ahat-update-api: PRIVATE_AHAT_ETC_API := $(LOCAL_PATH)/etc/ahat_api.txt
+ahat-update-api: PRIVATE_AHAT_ETC_REMOVED_API := $(LOCAL_PATH)/etc/ahat_removed_api.txt
+ahat-update-api: ahat-docs
+ @echo Copying ahat_api.txt
+ cp $(PRIVATE_AHAT_API) $(PRIVATE_AHAT_ETC_API)
+ @echo Copying ahat_removed_api.txt
+ cp $(PRIVATE_AHAT_REMOVED_API) $(PRIVATE_AHAT_ETC_REMOVED_API)
# --- ahat script ----------------
include $(CLEAR_VARS)
@@ -125,6 +170,9 @@
endif # linux
# Clean up local variables.
+AHAT_JAR :=
+AHAT_API :=
+AHAT_REMOVED_API :=
AHAT_TEST_JAR :=
AHAT_TEST_DUMP_JAR :=
AHAT_TEST_DUMP_HPROF :=
diff --git a/tools/ahat/etc/ahat_api.txt b/tools/ahat/etc/ahat_api.txt
new file mode 100644
index 0000000..7920ada
--- /dev/null
+++ b/tools/ahat/etc/ahat_api.txt
@@ -0,0 +1,327 @@
+package com.android.ahat {
+
+ public class Main {
+ method public static void main(java.lang.String[]);
+ }
+
+}
+
+package com.android.ahat.dominators {
+
+ public class DominatorsComputation {
+ ctor public DominatorsComputation();
+ method public static void computeDominators(com.android.ahat.dominators.DominatorsComputation.Node);
+ }
+
+ public static abstract interface DominatorsComputation.Node {
+ method public abstract java.lang.Object getDominatorsComputationState();
+ method public abstract java.lang.Iterable<? extends com.android.ahat.dominators.DominatorsComputation.Node> getReferencesForDominators();
+ method public abstract void setDominator(com.android.ahat.dominators.DominatorsComputation.Node);
+ method public abstract void setDominatorsComputationState(java.lang.Object);
+ }
+
+}
+
+package com.android.ahat.heapdump {
+
+ public class AhatArrayInstance extends com.android.ahat.heapdump.AhatInstance {
+ method public int getLength();
+ method public com.android.ahat.heapdump.Value getValue(int);
+ method public java.util.List<com.android.ahat.heapdump.Value> getValues();
+ method public java.lang.String toString();
+ }
+
+ public class AhatClassInstance extends com.android.ahat.heapdump.AhatInstance {
+ method public java.lang.Iterable<com.android.ahat.heapdump.FieldValue> getInstanceFields();
+ method public java.lang.String toString();
+ }
+
+ public class AhatClassObj extends com.android.ahat.heapdump.AhatInstance {
+ method public com.android.ahat.heapdump.AhatInstance getClassLoader();
+ method public com.android.ahat.heapdump.Field[] getInstanceFields();
+ method public long getInstanceSize();
+ method public java.lang.String getName();
+ method public java.util.List<com.android.ahat.heapdump.FieldValue> getStaticFieldValues();
+ method public com.android.ahat.heapdump.AhatClassObj getSuperClassObj();
+ method public java.lang.String toString();
+ }
+
+ public class AhatHeap implements com.android.ahat.heapdump.Diffable {
+ method public com.android.ahat.heapdump.AhatHeap getBaseline();
+ method public java.lang.String getName();
+ method public com.android.ahat.heapdump.Size getSize();
+ method public boolean isPlaceHolder();
+ }
+
+ public abstract class AhatInstance implements com.android.ahat.heapdump.Diffable com.android.ahat.dominators.DominatorsComputation.Node {
+ method public com.android.ahat.heapdump.AhatArrayInstance asArrayInstance();
+ method public java.awt.image.BufferedImage asBitmap();
+ method public com.android.ahat.heapdump.AhatClassInstance asClassInstance();
+ method public com.android.ahat.heapdump.AhatClassObj asClassObj();
+ method public java.lang.String asString(int);
+ method public java.lang.String asString();
+ method public com.android.ahat.heapdump.AhatInstance getAssociatedBitmapInstance();
+ method public com.android.ahat.heapdump.AhatInstance getBaseline();
+ method public java.lang.String getClassName();
+ method public com.android.ahat.heapdump.AhatClassObj getClassObj();
+ method public java.lang.String getDexCacheLocation(int);
+ method public java.util.List<com.android.ahat.heapdump.AhatInstance> getDominated();
+ method public java.lang.Object getDominatorsComputationState();
+ method public com.android.ahat.heapdump.Value getField(java.lang.String);
+ method public java.util.List<com.android.ahat.heapdump.AhatInstance> getHardReverseReferences();
+ method public com.android.ahat.heapdump.AhatHeap getHeap();
+ method public long getId();
+ method public com.android.ahat.heapdump.AhatInstance getImmediateDominator();
+ method public java.util.List<com.android.ahat.heapdump.PathElement> getPathFromGcRoot();
+ method public com.android.ahat.heapdump.AhatInstance getRefField(java.lang.String);
+ method public java.lang.Iterable<? extends com.android.ahat.dominators.DominatorsComputation.Node> getReferencesForDominators();
+ method public com.android.ahat.heapdump.AhatInstance getReferent();
+ method public com.android.ahat.heapdump.Size getRetainedSize(com.android.ahat.heapdump.AhatHeap);
+ method public java.util.Collection<com.android.ahat.heapdump.RootType> getRootTypes();
+ method public com.android.ahat.heapdump.Site getSite();
+ method public com.android.ahat.heapdump.Size getSize();
+ method public java.util.List<com.android.ahat.heapdump.AhatInstance> getSoftReverseReferences();
+ method public com.android.ahat.heapdump.Size getTotalRetainedSize();
+ method public boolean isArrayInstance();
+ method public boolean isClassInstance();
+ method public boolean isClassObj();
+ method public boolean isPlaceHolder();
+ method public boolean isRoot();
+ method public boolean isStronglyReachable();
+ method public boolean isUnreachable();
+ method public boolean isWeaklyReachable();
+ method public void setDominator(com.android.ahat.dominators.DominatorsComputation.Node);
+ method public void setDominatorsComputationState(java.lang.Object);
+ method public abstract java.lang.String toString();
+ }
+
+ public class AhatSnapshot implements com.android.ahat.heapdump.Diffable {
+ method public com.android.ahat.heapdump.AhatClassObj findClassObj(long);
+ method public com.android.ahat.heapdump.AhatInstance findInstance(long);
+ method public com.android.ahat.heapdump.AhatSnapshot getBaseline();
+ method public com.android.ahat.heapdump.AhatHeap getHeap(java.lang.String);
+ method public java.util.List<com.android.ahat.heapdump.AhatHeap> getHeaps();
+ method public com.android.ahat.heapdump.Site getRootSite();
+ method public java.util.List<com.android.ahat.heapdump.AhatInstance> getRooted();
+ method public com.android.ahat.heapdump.Site getSite(long);
+ method public boolean isDiffed();
+ method public boolean isPlaceHolder();
+ }
+
+ public class Diff {
+ ctor public Diff();
+ method public static void snapshots(com.android.ahat.heapdump.AhatSnapshot, com.android.ahat.heapdump.AhatSnapshot);
+ }
+
+ public class DiffFields {
+ ctor public DiffFields();
+ method public static java.util.List<com.android.ahat.heapdump.DiffedFieldValue> diff(java.lang.Iterable<com.android.ahat.heapdump.FieldValue>, java.lang.Iterable<com.android.ahat.heapdump.FieldValue>);
+ }
+
+ public abstract interface Diffable<T> {
+ method public abstract T getBaseline();
+ method public abstract boolean isPlaceHolder();
+ }
+
+ public class DiffedFieldValue {
+ method public static com.android.ahat.heapdump.DiffedFieldValue added(com.android.ahat.heapdump.FieldValue);
+ method public static com.android.ahat.heapdump.DiffedFieldValue deleted(com.android.ahat.heapdump.FieldValue);
+ method public static com.android.ahat.heapdump.DiffedFieldValue matched(com.android.ahat.heapdump.FieldValue, com.android.ahat.heapdump.FieldValue);
+ field public final com.android.ahat.heapdump.Value baseline;
+ field public final com.android.ahat.heapdump.Value current;
+ field public final java.lang.String name;
+ field public final com.android.ahat.heapdump.DiffedFieldValue.Status status;
+ field public final com.android.ahat.heapdump.Type type;
+ }
+
+ public static final class DiffedFieldValue.Status extends java.lang.Enum {
+ method public static com.android.ahat.heapdump.DiffedFieldValue.Status valueOf(java.lang.String);
+ method public static final com.android.ahat.heapdump.DiffedFieldValue.Status[] values();
+ enum_constant public static final com.android.ahat.heapdump.DiffedFieldValue.Status ADDED;
+ enum_constant public static final com.android.ahat.heapdump.DiffedFieldValue.Status DELETED;
+ enum_constant public static final com.android.ahat.heapdump.DiffedFieldValue.Status MATCHED;
+ }
+
+ public class Field {
+ ctor public Field(java.lang.String, com.android.ahat.heapdump.Type);
+ field public final java.lang.String name;
+ field public final com.android.ahat.heapdump.Type type;
+ }
+
+ public class FieldValue {
+ ctor public FieldValue(java.lang.String, com.android.ahat.heapdump.Type, com.android.ahat.heapdump.Value);
+ field public final java.lang.String name;
+ field public final com.android.ahat.heapdump.Type type;
+ field public final com.android.ahat.heapdump.Value value;
+ }
+
+ public class HprofFormatException extends java.lang.Exception {
+ }
+
+ public class Parser {
+ ctor public Parser();
+ method public static com.android.ahat.heapdump.AhatSnapshot parseHeapDump(java.io.File, com.android.ahat.proguard.ProguardMap) throws com.android.ahat.heapdump.HprofFormatException, java.io.IOException;
+ method public static com.android.ahat.heapdump.AhatSnapshot parseHeapDump(java.nio.ByteBuffer, com.android.ahat.proguard.ProguardMap) throws com.android.ahat.heapdump.HprofFormatException, java.io.IOException;
+ }
+
+ public class PathElement implements com.android.ahat.heapdump.Diffable {
+ ctor public PathElement(com.android.ahat.heapdump.AhatInstance, java.lang.String);
+ method public com.android.ahat.heapdump.PathElement getBaseline();
+ method public boolean isPlaceHolder();
+ field public final java.lang.String field;
+ field public final com.android.ahat.heapdump.AhatInstance instance;
+ field public boolean isDominator;
+ }
+
+ public final class RootType extends java.lang.Enum {
+ method public static com.android.ahat.heapdump.RootType valueOf(java.lang.String);
+ method public static final com.android.ahat.heapdump.RootType[] values();
+ enum_constant public static final com.android.ahat.heapdump.RootType DEBUGGER;
+ enum_constant public static final com.android.ahat.heapdump.RootType FINALIZING;
+ enum_constant public static final com.android.ahat.heapdump.RootType INTERNED_STRING;
+ enum_constant public static final com.android.ahat.heapdump.RootType JAVA_FRAME;
+ enum_constant public static final com.android.ahat.heapdump.RootType JNI_GLOBAL;
+ enum_constant public static final com.android.ahat.heapdump.RootType JNI_LOCAL;
+ enum_constant public static final com.android.ahat.heapdump.RootType JNI_MONITOR;
+ enum_constant public static final com.android.ahat.heapdump.RootType MONITOR;
+ enum_constant public static final com.android.ahat.heapdump.RootType NATIVE_STACK;
+ enum_constant public static final com.android.ahat.heapdump.RootType STICKY_CLASS;
+ enum_constant public static final com.android.ahat.heapdump.RootType THREAD;
+ enum_constant public static final com.android.ahat.heapdump.RootType THREAD_BLOCK;
+ enum_constant public static final com.android.ahat.heapdump.RootType UNKNOWN;
+ enum_constant public static final com.android.ahat.heapdump.RootType VM_INTERNAL;
+ }
+
+ public class Site implements com.android.ahat.heapdump.Diffable {
+ method public com.android.ahat.heapdump.Site findSite(long);
+ method public com.android.ahat.heapdump.Site getBaseline();
+ method public java.util.List<com.android.ahat.heapdump.Site> getChildren();
+ method public java.lang.String getFilename();
+ method public long getId();
+ method public int getLineNumber();
+ method public java.lang.String getMethodName();
+ method public void getObjects(java.lang.String, java.lang.String, java.util.Collection<com.android.ahat.heapdump.AhatInstance>);
+ method public java.util.List<com.android.ahat.heapdump.Site.ObjectsInfo> getObjectsInfos();
+ method public com.android.ahat.heapdump.Site getParent();
+ method public java.lang.String getSignature();
+ method public com.android.ahat.heapdump.Size getSize(com.android.ahat.heapdump.AhatHeap);
+ method public com.android.ahat.heapdump.Size getTotalSize();
+ method public boolean isPlaceHolder();
+ }
+
+ public static class Site.ObjectsInfo implements com.android.ahat.heapdump.Diffable {
+ ctor public Site.ObjectsInfo(com.android.ahat.heapdump.AhatHeap, com.android.ahat.heapdump.AhatClassObj);
+ method public com.android.ahat.heapdump.Site.ObjectsInfo getBaseline();
+ method public java.lang.String getClassName();
+ method public boolean isPlaceHolder();
+ method public void setBaseline(com.android.ahat.heapdump.Site.ObjectsInfo);
+ field public com.android.ahat.heapdump.AhatClassObj classObj;
+ field public com.android.ahat.heapdump.AhatHeap heap;
+ field public com.android.ahat.heapdump.Size numBytes;
+ field public long numInstances;
+ }
+
+ public class Size {
+ ctor public Size(long, long);
+ method public long getJavaSize();
+ method public long getRegisteredNativeSize();
+ method public long getSize();
+ method public boolean isZero();
+ method public com.android.ahat.heapdump.Size plus(com.android.ahat.heapdump.Size);
+ method public com.android.ahat.heapdump.Size plusRegisteredNativeSize(long);
+ field public static com.android.ahat.heapdump.Size ZERO;
+ }
+
+ public class Sort {
+ ctor public Sort();
+ method public static java.util.Comparator<com.android.ahat.heapdump.AhatInstance> defaultInstanceCompare(com.android.ahat.heapdump.AhatSnapshot);
+ method public static java.util.Comparator<com.android.ahat.heapdump.Site> defaultSiteCompare(com.android.ahat.heapdump.AhatSnapshot);
+ field public static final java.util.Comparator<com.android.ahat.heapdump.FieldValue> FIELD_VALUE_BY_NAME;
+ field public static final java.util.Comparator<com.android.ahat.heapdump.FieldValue> FIELD_VALUE_BY_TYPE;
+ field public static final java.util.Comparator<com.android.ahat.heapdump.AhatInstance> INSTANCE_BY_TOTAL_RETAINED_SIZE;
+ field public static final java.util.Comparator<com.android.ahat.heapdump.Site.ObjectsInfo> OBJECTS_INFO_BY_CLASS_NAME;
+ field public static final java.util.Comparator<com.android.ahat.heapdump.Site.ObjectsInfo> OBJECTS_INFO_BY_HEAP_NAME;
+ field public static final java.util.Comparator<com.android.ahat.heapdump.Site.ObjectsInfo> OBJECTS_INFO_BY_SIZE;
+ field public static final java.util.Comparator<com.android.ahat.heapdump.Site> SITE_BY_TOTAL_SIZE;
+ field public static final java.util.Comparator<com.android.ahat.heapdump.Size> SIZE_BY_SIZE;
+ }
+
+ public static class Sort.InstanceByHeapRetainedSize implements java.util.Comparator {
+ ctor public Sort.InstanceByHeapRetainedSize(com.android.ahat.heapdump.AhatHeap);
+ method public int compare(com.android.ahat.heapdump.AhatInstance, com.android.ahat.heapdump.AhatInstance);
+ }
+
+ public static class Sort.SiteByHeapSize implements java.util.Comparator {
+ ctor public Sort.SiteByHeapSize(com.android.ahat.heapdump.AhatHeap);
+ method public int compare(com.android.ahat.heapdump.Site, com.android.ahat.heapdump.Site);
+ }
+
+ public static class Sort.WithPriority<T> implements java.util.Comparator {
+ ctor public Sort.WithPriority(java.util.Comparator<T>...);
+ ctor public Sort.WithPriority(java.util.List<java.util.Comparator<T>>);
+ method public int compare(T, T);
+ }
+
+ public final class Type extends java.lang.Enum {
+ method public static com.android.ahat.heapdump.Type valueOf(java.lang.String);
+ method public static final com.android.ahat.heapdump.Type[] values();
+ enum_constant public static final com.android.ahat.heapdump.Type BOOLEAN;
+ enum_constant public static final com.android.ahat.heapdump.Type BYTE;
+ enum_constant public static final com.android.ahat.heapdump.Type CHAR;
+ enum_constant public static final com.android.ahat.heapdump.Type DOUBLE;
+ enum_constant public static final com.android.ahat.heapdump.Type FLOAT;
+ enum_constant public static final com.android.ahat.heapdump.Type INT;
+ enum_constant public static final com.android.ahat.heapdump.Type LONG;
+ enum_constant public static final com.android.ahat.heapdump.Type OBJECT;
+ enum_constant public static final com.android.ahat.heapdump.Type SHORT;
+ field public final java.lang.String name;
+ }
+
+ public abstract class Value {
+ ctor public Value();
+ method public com.android.ahat.heapdump.AhatInstance asAhatInstance();
+ method public java.lang.Byte asByte();
+ method public java.lang.Character asChar();
+ method public java.lang.Integer asInteger();
+ method public java.lang.Long asLong();
+ method public abstract boolean equals(java.lang.Object);
+ method public com.android.ahat.heapdump.Value getBaseline();
+ method public static com.android.ahat.heapdump.Value getBaseline(com.android.ahat.heapdump.Value);
+ method public static com.android.ahat.heapdump.Type getType(com.android.ahat.heapdump.Value);
+ method public boolean isAhatInstance();
+ method public boolean isInteger();
+ method public boolean isLong();
+ method public static com.android.ahat.heapdump.Value pack(com.android.ahat.heapdump.AhatInstance);
+ method public static com.android.ahat.heapdump.Value pack(boolean);
+ method public static com.android.ahat.heapdump.Value pack(char);
+ method public static com.android.ahat.heapdump.Value pack(float);
+ method public static com.android.ahat.heapdump.Value pack(double);
+ method public static com.android.ahat.heapdump.Value pack(byte);
+ method public static com.android.ahat.heapdump.Value pack(short);
+ method public static com.android.ahat.heapdump.Value pack(int);
+ method public static com.android.ahat.heapdump.Value pack(long);
+ method public abstract java.lang.String toString();
+ }
+
+}
+
+package com.android.ahat.proguard {
+
+ public class ProguardMap {
+ ctor public ProguardMap();
+ method public java.lang.String getClassName(java.lang.String);
+ method public java.lang.String getFieldName(java.lang.String, java.lang.String);
+ method public com.android.ahat.proguard.ProguardMap.Frame getFrame(java.lang.String, java.lang.String, java.lang.String, java.lang.String, int);
+ method public void readFromFile(java.io.File) throws java.io.FileNotFoundException, java.io.IOException, java.text.ParseException;
+ method public void readFromReader(java.io.Reader) throws java.io.IOException, java.text.ParseException;
+ }
+
+ public static class ProguardMap.Frame {
+ field public final java.lang.String filename;
+ field public final int line;
+ field public final java.lang.String method;
+ field public final java.lang.String signature;
+ }
+
+}
+
diff --git a/tools/ahat/etc/ahat_api_msg.txt b/tools/ahat/etc/ahat_api_msg.txt
new file mode 100644
index 0000000..d0d0468
--- /dev/null
+++ b/tools/ahat/etc/ahat_api_msg.txt
@@ -0,0 +1,5 @@
+The public API for ahat.jar has changed.
+
+Please verify whether this change to the API is intentional and
+whether it may break any current users of the API. If the API change
+is intentional, run 'm ahat-update-api' to update the recorded API.
diff --git a/tools/ahat/etc/ahat_removed_api.txt b/tools/ahat/etc/ahat_removed_api.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/ahat/etc/ahat_removed_api.txt
diff --git a/tools/ahat/src/main/com/android/ahat/HtmlDoc.java b/tools/ahat/src/main/com/android/ahat/HtmlDoc.java
index 5a22fc7..d5106dc 100644
--- a/tools/ahat/src/main/com/android/ahat/HtmlDoc.java
+++ b/tools/ahat/src/main/com/android/ahat/HtmlDoc.java
@@ -23,7 +23,7 @@
/**
* An Html implementation of Doc.
*/
-public class HtmlDoc implements Doc {
+class HtmlDoc implements Doc {
private PrintStream ps;
private Column[] mCurrentTableColumns;
diff --git a/tools/ahat/src/main/com/android/ahat/HtmlEscaper.java b/tools/ahat/src/main/com/android/ahat/HtmlEscaper.java
index 75a6827..06ffca2 100644
--- a/tools/ahat/src/main/com/android/ahat/HtmlEscaper.java
+++ b/tools/ahat/src/main/com/android/ahat/HtmlEscaper.java
@@ -16,7 +16,7 @@
package com.android.ahat;
-public class HtmlEscaper {
+class HtmlEscaper {
/**
* Escape html characters in the input string.
*/
diff --git a/tools/ahat/src/main/com/android/ahat/Main.java b/tools/ahat/src/main/com/android/ahat/Main.java
index a0fbf77..048573e 100644
--- a/tools/ahat/src/main/com/android/ahat/Main.java
+++ b/tools/ahat/src/main/com/android/ahat/Main.java
@@ -31,8 +31,10 @@
import java.util.concurrent.Executors;
public class Main {
+ private Main() {
+ }
- public static void help(PrintStream out) {
+ private static void help(PrintStream out) {
out.println("java -jar ahat.jar [OPTIONS] FILE");
out.println(" Launch an http server for viewing the given Android heap dump FILE.");
out.println("");
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java
index 50a4805..ccdd6e4 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java
@@ -34,7 +34,7 @@
private byte[] mByteArray; // null if not a byte array.
private char[] mCharArray; // null if not a char array.
- public AhatArrayInstance(long id) {
+ AhatArrayInstance(long id) {
super(id);
}
@@ -176,7 +176,7 @@
}
@Override
- protected long getExtraJavaSize() {
+ long getExtraJavaSize() {
int length = getLength();
if (length == 0) {
return 0;
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java
index 94efa50..cb9d959 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java
@@ -27,7 +27,7 @@
// the field types and names to save memory.
private Value[] mFields;
- public AhatClassInstance(long id) {
+ AhatClassInstance(long id) {
super(id);
}
@@ -36,7 +36,7 @@
}
@Override
- protected long getExtraJavaSize() {
+ long getExtraJavaSize() {
return 0;
}
@@ -244,7 +244,7 @@
}
@Override
- public RegisteredNativeAllocation asRegisteredNativeAllocation() {
+ RegisteredNativeAllocation asRegisteredNativeAllocation() {
if (!isInstanceOfClass("sun.misc.Cleaner")) {
return null;
}
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java
index be0f713..3babf76 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java
@@ -29,7 +29,7 @@
private long mStaticFieldsSize;
private long mInstanceSize;
- public AhatClassObj(long id, String className) {
+ AhatClassObj(long id, String className) {
super(id);
mClassName = className;
}
@@ -50,7 +50,7 @@
}
@Override
- protected long getExtraJavaSize() {
+ long getExtraJavaSize() {
return mStaticFieldsSize;
}
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatField.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatField.java
deleted file mode 100644
index a25ee28..0000000
--- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatField.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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 com.android.ahat.heapdump;
-
-public class AhatField {
- private final String mName;
- private final String mType;
-
- public AhatField(String name, String type) {
- mName = name;
- mType = type;
- }
-
- /**
- * Returns the name of the field.
- */
- public String getName() {
- return mName;
- }
-
- /**
- * Returns a description of the type of the field.
- */
- public String getType() {
- return mType;
- }
-}
-
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java
index 1a3d127..a9f819f 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java
@@ -64,7 +64,7 @@
// 2. During dominators computation, to store the dominators computation state.
private Object mTemporaryUserData;
- public AhatInstance(long id) {
+ AhatInstance(long id) {
mId = id;
mBaseline = this;
}
@@ -101,7 +101,7 @@
* For example, class objects will have extra size for static fields and
* array objects will have extra size for the array elements.
*/
- protected abstract long getExtraJavaSize();
+ abstract long getExtraJavaSize();
/**
* Returns the number of bytes belonging to the given heap that this instance
@@ -388,7 +388,7 @@
return null;
}
- public static class RegisteredNativeAllocation {
+ static class RegisteredNativeAllocation {
public AhatInstance referent;
public long size;
};
@@ -397,7 +397,7 @@
* Return the registered native allocation that this instance represents, if
* any. This is relevant for instances of sun.misc.Cleaner.
*/
- public RegisteredNativeAllocation asRegisteredNativeAllocation() {
+ RegisteredNativeAllocation asRegisteredNativeAllocation() {
return null;
}
@@ -451,7 +451,7 @@
return null;
}
- public void setBaseline(AhatInstance baseline) {
+ void setBaseline(AhatInstance baseline) {
mBaseline = baseline;
}
@@ -470,11 +470,11 @@
return new AhatPlaceHolderInstance(this);
}
- public void setTemporaryUserData(Object state) {
+ void setTemporaryUserData(Object state) {
mTemporaryUserData = state;
}
- public Object getTemporaryUserData() {
+ Object getTemporaryUserData() {
return mTemporaryUserData;
}
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatPlaceHolderClassObj.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatPlaceHolderClassObj.java
index 07f5b50..b8cdbdd 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatPlaceHolderClassObj.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatPlaceHolderClassObj.java
@@ -22,7 +22,7 @@
*
* This should be created through a call to newPlaceHolder();
*/
-public class AhatPlaceHolderClassObj extends AhatClassObj {
+class AhatPlaceHolderClassObj extends AhatClassObj {
AhatPlaceHolderClassObj(AhatClassObj baseline) {
super(-1, baseline.getClassName());
setBaseline(baseline);
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatPlaceHolderInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatPlaceHolderInstance.java
index 8849403..d656425 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatPlaceHolderInstance.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatPlaceHolderInstance.java
@@ -25,7 +25,7 @@
*
* This should be created through a call to AhatInstance.newPlaceHolder();
*/
-public class AhatPlaceHolderInstance extends AhatInstance {
+class AhatPlaceHolderInstance extends AhatInstance {
AhatPlaceHolderInstance(AhatInstance baseline) {
super(-1);
setBaseline(baseline);
@@ -36,7 +36,7 @@
return Size.ZERO;
}
- @Override protected long getExtraJavaSize() {
+ @Override long getExtraJavaSize() {
return 0;
}
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java
index 945966c..59ce5d1 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java
@@ -122,7 +122,7 @@
return site == null ? mRootSite : site;
}
- public void setBaseline(AhatSnapshot baseline) {
+ void setBaseline(AhatSnapshot baseline) {
mBaseline = baseline;
}
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/HprofFormatException.java b/tools/ahat/src/main/com/android/ahat/heapdump/HprofFormatException.java
index 0e128cd..256a3b4 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/HprofFormatException.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/HprofFormatException.java
@@ -17,11 +17,11 @@
package com.android.ahat.heapdump;
public class HprofFormatException extends Exception {
- public HprofFormatException(String msg) {
+ HprofFormatException(String msg) {
super(msg);
}
- public HprofFormatException(String msg, Exception cause) {
+ HprofFormatException(String msg, Exception cause) {
super(msg, cause);
}
}
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java b/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java
index 756b7d2..d7b1dd7 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java
@@ -630,7 +630,7 @@
}
@Override
- protected Type getType() {
+ Type getType() {
return Type.OBJECT;
}
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Reference.java b/tools/ahat/src/main/com/android/ahat/heapdump/Reference.java
index 980f278..f1340bd 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/Reference.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/Reference.java
@@ -23,7 +23,7 @@
* 'strong' is true if this is a strong reference, false if it is a
* weak/soft/other reference.
*/
-public class Reference {
+class Reference {
public final AhatInstance src;
public final String field;
public final AhatInstance ref;
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/RootType.java b/tools/ahat/src/main/com/android/ahat/heapdump/RootType.java
index af552ea..734f889 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/RootType.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/RootType.java
@@ -32,7 +32,7 @@
JNI_MONITOR (1 << 12),
FINALIZING (1 << 13);
- public final int mask;
+ final int mask;
RootType(int mask) {
this.mask = mask;
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Site.java b/tools/ahat/src/main/com/android/ahat/heapdump/Site.java
index 523550a..4978d52 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/Site.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/Site.java
@@ -103,7 +103,7 @@
/**
* Construct a root site.
*/
- public Site(String name) {
+ Site(String name) {
this(null, name, "", "", 0);
}
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/SuperRoot.java b/tools/ahat/src/main/com/android/ahat/heapdump/SuperRoot.java
index 5210e31..b01cfff 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/SuperRoot.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/SuperRoot.java
@@ -21,20 +21,20 @@
import java.util.ArrayList;
import java.util.List;
-public class SuperRoot extends AhatInstance implements DominatorsComputation.Node {
+class SuperRoot extends AhatInstance implements DominatorsComputation.Node {
private List<AhatInstance> mRoots = new ArrayList<AhatInstance>();
private Object mDominatorsComputationState;
- public SuperRoot() {
+ SuperRoot() {
super(0);
}
- public void addRoot(AhatInstance root) {
+ void addRoot(AhatInstance root) {
mRoots.add(root);
}
@Override
- protected long getExtraJavaSize() {
+ long getExtraJavaSize() {
return 0;
}
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Type.java b/tools/ahat/src/main/com/android/ahat/heapdump/Type.java
index 726bc47..4024961 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/Type.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/Type.java
@@ -28,7 +28,7 @@
LONG("long", 8);
public final String name;
- public final int size;
+ final int size;
Type(String name, int size) {
this.name = name;
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Value.java b/tools/ahat/src/main/com/android/ahat/heapdump/Value.java
index 01fd250..eea4277 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/Value.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/Value.java
@@ -67,7 +67,7 @@
/**
* Return the type of the given value.
*/
- protected abstract Type getType();
+ abstract Type getType();
/**
* Returns true if the Value is an AhatInstance, as opposed to a Java
@@ -153,7 +153,7 @@
}
@Override
- protected Type getType() {
+ Type getType() {
return Type.BOOLEAN;
}
@@ -184,7 +184,7 @@
}
@Override
- protected Type getType() {
+ Type getType() {
return Type.BYTE;
}
@@ -215,7 +215,7 @@
}
@Override
- protected Type getType() {
+ Type getType() {
return Type.CHAR;
}
@@ -241,7 +241,7 @@
}
@Override
- protected Type getType() {
+ Type getType() {
return Type.DOUBLE;
}
@@ -267,7 +267,7 @@
}
@Override
- protected Type getType() {
+ Type getType() {
return Type.FLOAT;
}
@@ -304,7 +304,7 @@
}
@Override
- protected Type getType() {
+ Type getType() {
return Type.OBJECT;
}
@@ -345,7 +345,7 @@
}
@Override
- protected Type getType() {
+ Type getType() {
return Type.INT;
}
@@ -381,7 +381,7 @@
}
@Override
- protected Type getType() {
+ Type getType() {
return Type.LONG;
}
@@ -407,7 +407,7 @@
}
@Override
- protected Type getType() {
+ Type getType() {
return Type.SHORT;
}
diff --git a/tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java b/tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java
index 50c110a..131bbf3 100644
--- a/tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java
+++ b/tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java
@@ -101,7 +101,7 @@
private Map<String, ClassData> mClassesFromObfuscatedName = new HashMap<String, ClassData>();
public static class Frame {
- public Frame(String method, String signature, String filename, int line) {
+ Frame(String method, String signature, String filename, int line) {
this.method = method;
this.signature = signature;
this.filename = filename;