diff options
158 files changed, 6637 insertions, 1230 deletions
diff --git a/build/Android.bp b/build/Android.bp index 2c959d46f5..6766dd6033 100644 --- a/build/Android.bp +++ b/build/Android.bp @@ -4,6 +4,7 @@ bootstrap_go_package { deps: [ "blueprint", "blueprint-pathtools", + "blueprint-proptools", "soong", "soong-android", "soong-cc", diff --git a/build/art.go b/build/art.go index 452b3485a3..4e48d2d932 100644 --- a/build/art.go +++ b/build/art.go @@ -19,6 +19,8 @@ import ( "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 debugDefaults(ctx android.LoadHookContext) { func customLinker(ctx android.LoadHookContext) { linker := envDefault(ctx, "CUSTOM_TARGET_LINKER", "") - if linker != "" { - type props struct { - DynamicLinker string - } + type props struct { + DynamicLinker string + } - p := &props{} + p := &props{} + if linker != "" { p.DynamicLinker = linker - ctx.AppendProperties(p) } + + 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 c5d3a6b454..2929f36d2e 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 547ffbcf62..135f9c7b47 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 @@ std::unique_ptr<const std::vector<uint8_t>> CompilerDriver::CreateQuickToInterpr } #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 @@ void CompilerDriver::CompileAll(jobject class_loader, // 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 9d0b5c865d..b8d1f52995 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -536,6 +536,7 @@ void CodeGenerator::GenerateInvokeStaticOrDirectRuntimeCall( break; case kVirtual: case kInterface: + case kPolymorphic: LOG(FATAL) << "Unexpected invoke type: " << invoke->GetInvokeType(); UNREACHABLE(); } @@ -563,6 +564,9 @@ void CodeGenerator::GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invok 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 c5a39ff882..7a8c0ad025 100644 --- a/compiler/optimizing/code_generator_vector_mips.cc +++ b/compiler/optimizing/code_generator_vector_mips.cc @@ -91,17 +91,61 @@ void InstructionCodeGeneratorMIPS::VisitVecReplicateScalar(HVecReplicateScalar* } 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 @@ static void CreateVecUnOpLocations(ArenaAllocator* allocator, HVecUnaryOperation 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 LocationsBuilderMIPS::VisitVecReduce(HVecReduce* instruction) { } 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 InstructionCodeGeneratorMIPS::VisitVecUShr(HVecUShr* instruction) { } 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 e606df2158..0c59b7344a 100644 --- a/compiler/optimizing/code_generator_vector_mips64.cc +++ b/compiler/optimizing/code_generator_vector_mips64.cc @@ -94,17 +94,58 @@ void InstructionCodeGeneratorMIPS64::VisitVecReplicateScalar(HVecReplicateScalar } 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 @@ static void CreateVecUnOpLocations(ArenaAllocator* allocator, HVecUnaryOperation 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 LocationsBuilderMIPS64::VisitVecReduce(HVecReduce* instruction) { } 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 InstructionCodeGeneratorMIPS64::VisitVecUShr(HVecUShr* instruction) { } 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 3851877ae5..f7fd9101fd 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -527,6 +527,10 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { 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 4429e6e5b7..bdeb261dbe 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -256,30 +256,63 @@ void IntrinsicCodeGenerator ## Arch::Visit ## Name(HInvoke* invoke) { \ 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 6a4faaf438..74de0773fc 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 @@ static inline void NormalizePackedType(/* inout */ DataType::Type* type, // 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 @@ static bool IsNarrowerOperand(HInstruction* a, } // 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 @@ static HVecReduce::ReductionKind GetReductionKind(HVecOperation* reduction) { } else if (reduction->IsVecMax()) { return HVecReduce::kMax; } - LOG(FATAL) << "Unsupported SIMD reduction"; + LOG(FATAL) << "Unsupported SIMD reduction " << reduction->GetId(); UNREACHABLE(); } @@ -446,7 +461,8 @@ HLoopOptimization::HLoopOptimization(HGraph* graph, 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 @@ bool HLoopOptimization::ShouldVectorize(LoopNode* node, HBasicBlock* block, int6 // 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 @@ bool HLoopOptimization::ShouldVectorize(LoopNode* node, HBasicBlock* block, int6 } } - // 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 @@ bool HLoopOptimization::ShouldVectorize(LoopNode* node, HBasicBlock* block, int6 // 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 @@ bool HLoopOptimization::ShouldVectorize(LoopNode* node, HBasicBlock* block, int6 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 @@ bool HLoopOptimization::ShouldVectorize(LoopNode* node, HBasicBlock* block, int6 } } } - } + // 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 + + // Find a suitable alignment strategy. + SetAlignmentStrategy(peeling_votes, peeling_candidate); - // Consider dynamic loop peeling for alignment. - SetPeelingCandidate(candidate, trip_count); + // Does vectorization seem profitable? + if (!IsVectorizationProfitable(trip_count)) { + return false; + } // Success! return true; @@ -828,9 +885,12 @@ void HLoopOptimization::Vectorize(LoopNode* node, 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 @@ void HLoopOptimization::Vectorize(LoopNode* node, 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 @@ void HLoopOptimization::Vectorize(LoopNode* node, 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 @@ void HLoopOptimization::Vectorize(LoopNode* node, 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 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node, 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 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node, 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 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node, 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 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict *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 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict *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 @@ void HLoopOptimization::GenerateVecMem(HInstruction* org, 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 @@ void HLoopOptimization::GenerateVecMem(HInstruction* org, 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 @@ void HLoopOptimization::GenerateVecReductionPhiInputs(HPhi* phi, HInstruction* r // 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 @@ void HLoopOptimization::GenerateVecReductionPhiInputs(HPhi* phi, HInstruction* r 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 @@ void HLoopOptimization::GenerateVecOp(HInstruction* org, break; } default: - LOG(FATAL) << "Unsupported SIMD intrinsic"; + LOG(FATAL) << "Unsupported SIMD intrinsic " << org->GetId(); UNREACHABLE(); } // switch invoke } else { @@ -2005,35 +2109,72 @@ bool HLoopOptimization::VectorizeSADIdiom(LoopNode* node, // 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 @@ uint32_t HLoopOptimization::GetUnrollingFactor(HBasicBlock* block, int64_t trip_ // - 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 @@ bool HLoopOptimization::TrySetPhiReduction(HPhi* phi) { 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::IsUsedOutsideLoop(HLoopInformation* loop_info, 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 @@ bool HLoopOptimization::TryAssignLastValue(HLoopInformation* loop_info, // 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 768fe554e3..51e0a986b8 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -94,20 +94,24 @@ class HLoopOptimization : public HOptimization { * 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 @@ class HLoopOptimization : public HOptimization { 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 @@ class HLoopOptimization : public HOptimization { 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 @@ class HLoopOptimization : public HOptimization { 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 @@ class HLoopOptimization : public HOptimization { // 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 affe639f8d..1cd9142464 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 0a5068112f..a02fbf862f 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 9f8ed77526..8427e7b8ce 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 dc570da832..d3d42b98bb 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 8c4ee6e9a1..23c3a5ca93 100644 --- a/dexlayout/dex_ir.cc +++ b/dexlayout/dex_ir.cc @@ -167,10 +167,17 @@ static bool GetIdsFromByteCode(Collections& collections, 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 6fcd6ffe9f..9927576400 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 f34e7ecd4b..38d3c6ec07 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -317,6 +317,30 @@ class DexLayoutTest : public CommonRuntimeTest { 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 @@ class DexLayoutTest : public CommonRuntimeTest { 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 @@ TEST_F(DexLayoutTest, DuplicateCodeItem) { 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 08d38d5925..2c57e40212 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 @@ class DexoptAnalyzer FINAL { 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 @@ class DexoptAnalyzer FINAL { 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 @@ class DexoptAnalyzer FINAL { 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 52fe973c1b..d0f05d9e66 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 eebfec48ab..c6090ef9fc 100644 --- a/openjdkjvmti/Android.bp +++ b/openjdkjvmti/Android.bp @@ -24,6 +24,7 @@ cc_defaults { 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 c2584e6944..5f726b16e0 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 @@ static jint GetEnvHandler(art::JavaVMExt* vm, /*out*/void** env, jint version) { 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_Initialize() { 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 3edefaf412..126346088c 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 @@ namespace openjdkjvmti { 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 0000000000..f843054681 --- /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 0000000000..b265fa8ec2 --- /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 d97916fa0a..7f77f90862 100644 --- a/openjdkjvmti/events-inl.h +++ b/openjdkjvmti/events-inl.h @@ -480,6 +480,7 @@ inline bool EventHandler::NeedsEventUpdate(ArtJvmTiEnv* env, 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 @@ inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env, 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 381dc1f036..6a64441a4a 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 uint32_t GetInstrumentationEventsFor(ArtJvmtiEvent event) { } } +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 @@ static void SetupTraceListener(JvmtiMethodTraceListener* listener, 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 @@ void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { } // 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 @@ jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env, 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 a062e1589e..aed24e59f3 100644 --- a/openjdkjvmti/events.h +++ b/openjdkjvmti/events.h @@ -232,6 +232,7 @@ class EventHandler { 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 9c3687fffd..8e5b56e9bf 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 @@ Breakpoint::Breakpoint(art::ArtMethod* m, jlocation loc) : method_(m), location_ } 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 @@ jvmtiError BreakpointUtil::SetBreakpoint(jvmtiEnv* jenv, jmethodID method, jloca 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 @@ jvmtiError BreakpointUtil::ClearBreakpoint(jvmtiEnv* jenv, jmethodID method, jlo 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 41679daaa1..5d63285825 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -86,21 +86,6 @@ struct TiMethodCallback : public art::MethodCallback { 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 @@ void MethodUtil::Register(EventHandler* handler) { 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 @@ void MethodUtil::Unregister() { 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 a615437985..ff5ae9f5f7 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -35,6 +35,7 @@ cc_defaults { "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 @@ cc_defaults { "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 @@ cc_defaults { "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 @@ cc_defaults { export_generated_headers: ["cpp-define-generator-asm-support"], include_dirs: [ "art/sigchainlib", - "art", ], header_libs: [ "art_cmdlineparser_headers", @@ -598,6 +600,7 @@ art_cc_test { "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 adfc88fd35..280e5937c6 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -2921,7 +2921,7 @@ ENTRY art_quick_invoke_polymorphic 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 ee3f17d06a..489c52c0d2 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -3228,7 +3228,7 @@ ENTRY art_quick_invoke_polymorphic 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 d4ad275f35..98ffe6504a 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -3028,7 +3028,7 @@ ENTRY art_quick_invoke_polymorphic 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 eecca58a41..25716dc1bb 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -2419,7 +2419,7 @@ DEFINE_FUNCTION art_quick_invoke_polymorphic 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 d4297df76f..80f5c348ee 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -166,6 +166,8 @@ InvokeType ArtMethod::GetInvokeType() { return kInterface; } else if (IsDirect()) { return kDirect; + } else if (IsPolymorphicSignature()) { + return kPolymorphic; } else { return kVirtual; } @@ -427,6 +429,12 @@ bool ArtMethod::IsAnnotatedWithCriticalNative() { /* 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 caef81c601..fe85cb4afa 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -271,6 +271,12 @@ class ArtMethod FINAL { 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 @@ class ArtMethod FINAL { // -- 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 1f86ee1917..16b555e1c6 100644 --- a/runtime/base/bit_struct.h +++ b/runtime/base/bit_struct.h @@ -130,6 +130,18 @@ struct BitStructField { 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 @@ using BitStructUint = #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 9fc9762054..a80d39eb91 100644 --- a/runtime/base/bit_struct_test.cc +++ b/runtime/base/bit_struct_test.cc @@ -255,4 +255,68 @@ TEST(BitStructs, Mixed) { 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 0000000000..323a06519d --- /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 0000000000..007f3b443d --- /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 189c0d0030..4b56d3b30c 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -664,7 +664,7 @@ class Locks { // 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 c76ea113d8..b46b921307 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 910473be3f..3c1b638f03 100644 --- a/runtime/cdex/compact_dex_file.h +++ b/runtime/cdex/compact_dex_file.h @@ -51,8 +51,9 @@ class CompactDexFile : public DexFile { 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 b199933ae4..5435c1145b 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 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b 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 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b 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 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { 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 @@ ClassLinker::~ClassLinker() { 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 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) { "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 eba202228c..2d9ec5a440 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -120,6 +120,11 @@ class ClassLinker { 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 bd736929d8..4d9282654a 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 CallSiteOffsets : public CheckOffsets<mirror::CallSite> { } }; +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 @@ TEST_F(ClassLinkerTest, ValidateFieldOrderOfJavaCppUnionClasses) { 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 @@ TEST_F(ClassLinkerTest, RegisterDexFileName) { 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 149c33fa87..f15acf980a 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 85df14a576..8898afe116 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -346,6 +346,10 @@ bool DebuggerActiveMethodInspectionCallback::IsMethodBeingInspected(ArtMethod* m 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 18126b1eed..ec37833f6d 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -55,6 +55,7 @@ class Thread; 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 57cef3de47..5f9b3cf6c4 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 f2c43f7f87..974c7acbb2 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 @@ uint32_t DexFile::CalculateChecksum() const { 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 @@ DexFile::DexFile(const uint8_t* base, 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 53e1f9ed96..c895e0d1da 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -39,6 +39,21 @@ class Signature; 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 @@ class DexFile { 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 @@ class DexFile { 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 @@ class DexFile { // 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 4375d7f799..c3fae15b14 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 26f7e7628c..06e3397754 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 @@ std::unique_ptr<const DexFile> DexFileLoader::Open(const uint8_t* base, 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 @@ std::unique_ptr<const DexFile> DexFileLoader::Open(const std::string& location, 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 @@ std::unique_ptr<const DexFile> DexFileLoader::OpenFile(int fd, 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 @@ std::unique_ptr<const DexFile> DexFileLoader::OpenOneDexFileFromZip( 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 @@ std::unique_ptr<const DexFile> DexFileLoader::OpenOneDexFileFromZip( } 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 @@ std::unique_ptr<DexFile> DexFileLoader::OpenCommon(const uint8_t* base, 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 a1167dc3fd..97c886ac7d 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 @@ class DexFileLoader { 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 7bda89dd8d..ee577e7d9a 100644 --- a/runtime/dex_file_verifier_test.cc +++ b/runtime/dex_file_verifier_test.cc @@ -57,7 +57,7 @@ static void FixUpChecksum(uint8_t* dex_file) { 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 99fe53bb71..e64c0f62c2 100644 --- a/runtime/dex_instruction.cc +++ b/runtime/dex_instruction.cc @@ -119,6 +119,26 @@ size_t Instruction::SizeInCodeUnitsComplexOpcode() const { } } +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 2f28dffa2b..09c78b2428 100644 --- a/runtime/dex_instruction.h +++ b/runtime/dex_instruction.h @@ -225,6 +225,12 @@ class Instruction { } } + // 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 @@ class Instruction { 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 280746e9dc..9e4dea34bb 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,50 +107,67 @@ class DexInstructionIterator : public std::iterator<std::forward_iterator_tag, I 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; } - private: - const value_type* inst_ = nullptr; -}; - -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); -} + const value_type& operator*() const { + AssertValid(); + return *inst_; + } -static inline bool operator<(const DexInstructionIterator& lhs, - const DexInstructionIterator& rhs) { - return lhs.Inst() < rhs.Inst(); -} + const value_type* operator->() const { + AssertValid(); + return &**this; + } -static inline bool operator>(const DexInstructionIterator& lhs, - const DexInstructionIterator& rhs) { - return rhs < lhs; -} + // 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_; + } -static inline bool operator<=(const DexInstructionIterator& lhs, - const DexInstructionIterator& rhs) { - return !(rhs < lhs); -} + private: + ALWAYS_INLINE void AssertValid() const { + DCHECK(!IsErrorState()); + DCHECK_LT(Inst(), end_); + } -static inline bool operator>=(const DexInstructionIterator& lhs, - const DexInstructionIterator& rhs) { - return !(lhs < rhs); -} + const value_type* end_ = nullptr; + bool error_state_ = false; +}; } // namespace art diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index cf5cc111b7..4d7c2a1acb 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -2574,7 +2574,7 @@ extern "C" TwoWordReturn artInvokeInterfaceTrampoline(ArtMethod* interface_metho // 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 @@ extern "C" uintptr_t artInvokePolymorphic( 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 @@ extern "C" uintptr_t artInvokePolymorphic( // 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 4d4d8ffb58..7beff960cc 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 2dc5acc14b..c6caf4b08e 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 4f5458212c..9f6266612a 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 f0eada3cb4..74813b4dd1 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 5999548d2b..bdb2edad35 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 beb43dfcf5..3cd04a61e9 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 071d1aedb7..74265bbf0b 100644 --- a/runtime/generated/asm_support_gen.h +++ b/runtime/generated/asm_support_gen.h @@ -39,23 +39,23 @@ DEFINE_CHECK_EQ(static_cast<size_t>(RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OF #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_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_INTERFACE), (static_ #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 2c82cb1acd..49f202182d 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -137,11 +137,12 @@ static void UpdateEntrypoints(ArtMethod* method, const void* quick_code) 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 9fb9fe7274..0a1ae36167 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 @@ void SetStringInitValueToAllAliases(ShadowFrame* shadow_frame, } 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 @@ bool DoInvokePolymorphic(Thread* self, 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 @@ static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self, // 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 @@ bool DoInvokeCustom(Thread* self, 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, false); 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(false); 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 e7f67ebb0d..f097bc71b9 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -222,7 +222,18 @@ static inline mirror::MethodType* ResolveMethodType(Thread* self, 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 26de6b4ff7..37593bc728 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 @@ static ALWAYS_INLINE bool MterpStringEquals(ShadowFrame* shadow_frame, 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(MterpVarHandleReleaseFence, std::memory_order_release) 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 @@ bool MterpHandleIntrinsic(ShadowFrame* shadow_frame, 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 7a8ae9a9db..31e7986770 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -1636,6 +1636,18 @@ void UnstartedRuntime::UnstartedSystemIdentityHashCode( 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 e7047c7372..c029e07432 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 index 55281812e4..d46d0cc00f 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 @@ std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic); } // 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 index bfefead394..d007728750 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_ - -// 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. +#ifndef ART_RUNTIME_INTRINSICS_LIST_H_ +#define ART_RUNTIME_INTRINSICS_LIST_H_ +// 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. - -// Note: Thread.interrupted is marked with kAllSideEffects due to the lack of finer grain -// side effects representation. +// 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") +// 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 a003f7fe9e..2b877e6f51 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 97a3b717e2..72b5a942fe 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -272,9 +272,12 @@ bool Jit::CompileMethod(ArtMethod* method, Thread* self, bool osr) { 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 19501de81b..805b9c185a 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 743604cc47..7f68d2faa0 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 65f39e4468..5a5d5713a8 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -355,15 +355,6 @@ inline bool ConvertAndCopyArgumentsFromCallerFrame( 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 @@ static inline bool IsCallerTransformer(Handle<mirror::MethodType> callsite_type) } 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 @@ static inline bool DoCallPolymorphic(ArtMethod* called_method, } 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 @@ bool DoInvokePolymorphicMethod(Thread* self, 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 @@ inline static void DoFieldGetForInvokePolymorphic(Thread* self, } // 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 @@ static JValue GetValueFromShadowFrame(const ShadowFrame& shadow_frame, } 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 @@ bool DoInvokePolymorphicFieldAccess(Thread* self, 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 @@ bool DoInvokePolymorphicFieldAccess(Thread* self, 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 @@ bool DoInvokePolymorphicFieldAccess(Thread* self, 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 @@ bool DoInvokePolymorphicFieldAccess(Thread* self, 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 @@ bool DoInvokePolymorphicFieldAccess(Thread* self, } 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 @@ static inline bool DoInvokePolymorphicNonExact(Thread* self, first_arg, result); } - return DoInvokePolymorphicMethod<is_range>(self, shadow_frame, method_handle, @@ -1022,27 +1008,32 @@ static inline bool DoInvokePolymorphicNonExact(Thread* self, } 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 @@ bool DoInvokePolymorphicExact(Thread* self, } // 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); + } +} + +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 DoInvokePolymorphicNonExact<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_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range) \ +#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 @@ bool DoInvokePolymorphic(Thread* self, 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 55680f09e7..8641918f1b 100644 --- a/runtime/method_handles.h +++ b/runtime/method_handles.h @@ -202,14 +202,23 @@ class ShadowFrameSetter { }; 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 a6129ccc5f..f82bfbfaef 100644 --- a/runtime/mirror/emulated_stack_frame.cc +++ b/runtime/mirror/emulated_stack_frame.cc @@ -289,7 +289,7 @@ void EmulatedStackFrame::VisitRoots(RootVisitor* visitor) { 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 0000000000..e7eac1a5ab --- /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 0000000000..a2a5d8c9ff --- /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 0000000000..0e1c994bf0 --- /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 4ab8908ff3..7d9d8be791 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 @@ static jint GetDexOptNeeded(JNIEnv* env, 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 @@ static jint GetDexOptNeeded(JNIEnv* env, 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 @@ static jint GetDexOptNeeded(JNIEnv* env, 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 @@ static jint DexFile_getDexOptNeeded(JNIEnv* env, jstring javaFilename, jstring javaInstructionSet, jstring javaTargetCompilerFilter, + jstring javaClassLoaderContext, jboolean newProfile, jboolean downgrade) { ScopedUtfChars filename(env, javaFilename); @@ -549,10 +568,16 @@ static jint DexFile_getDexOptNeeded(JNIEnv* env, 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 @@ static JNINativeMethod gMethods[] = { 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 dd98e25932..f5057b013a 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 7e16357376..f166714b79 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 1269dcad93..d64986e76d 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 a7fe9b1205..9f6bf695f0 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 @@ bool OatFileAssistant::OatFileInfo::ClassLoaderContextIsOkay(ClassLoaderContext* 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 d99036df7e..6d14971179 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -1466,6 +1466,33 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) { 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 940195c0f4..ee35d9cd12 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 71d7b6c34d..526f6d147a 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 c88799cc28..139de2b1f3 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 @@ bool Runtime::Start() { 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 f164f7c8ec..339fe822fd 100644 --- a/runtime/runtime_callbacks.cc +++ b/runtime/runtime_callbacks.cc @@ -43,6 +43,17 @@ void RuntimeCallbacks::RemoveMethodInspectionCallback(MethodInspectionCallback* 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 c9360491bb..c1ba9643a7 100644 --- a/runtime/runtime_callbacks.h +++ b/runtime/runtime_callbacks.h @@ -104,6 +104,11 @@ class MethodInspectionCallback { // 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 @@ class RuntimeCallbacks { // 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 940e4611f6..eb69d91dad 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 0000000000..f710ebeb4c --- /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/tools/ahat/src/main/com/android/ahat/heapdump/AhatField.java b/runtime/runtime_intrinsics.h index a25ee2869d..98dc9bc8c9 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatField.java +++ b/runtime/runtime_intrinsics.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * 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. @@ -14,29 +14,13 @@ * limitations under the License. */ -package com.android.ahat.heapdump; +#ifndef ART_RUNTIME_RUNTIME_INTRINSICS_H_ +#define ART_RUNTIME_RUNTIME_INTRINSICS_H_ -public class AhatField { - private final String mName; - private final String mType; +namespace art { - public AhatField(String name, String type) { - mName = name; - mType = type; - } +void InitializeIntrinsics(); - /** - * 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; - } -} +} // namespace art +#endif // ART_RUNTIME_RUNTIME_INTRINSICS_H_ diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc index a1f14be0f1..bf5d718113 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 4fa3efe343..906b0b7b8e 100644 --- a/runtime/standard_dex_file.h +++ b/runtime/standard_dex_file.h @@ -49,8 +49,9 @@ class StandardDexFile : public DexFile { 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 47ffb4e13f..065b6e2eb8 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::ClearSuspendBarrier(AtomicInteger* target) { } 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 42b38da0b9..3b917bab9b 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -1356,6 +1356,9 @@ class Thread { 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 cad164c251..88f1fc6991 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 @@ class DumpCheckpoint FINAL : public Closure { : 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 1f6bd742b6..f6533a7130 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 @@ std::string GetThreadName(pid_t tid) { 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 @@ void GetTaskStats(pid_t tid, char* state, int* utime, int* stime, int* task_cpu) *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 @@ void ParseDouble(const std::string& option, *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 fbf812a6b3..ede32dc57a 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 @@ bool IsValidDescriptor(const char* s); // "Ljava/lang/String;" // 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 @@ pid_t GetTid(); // 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 @@ static T GetRandomNumber(T min, T max) { 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 @@ inline static int32_t Signum(T opnd) { 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 e846c983af..efb20bae9e 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 0033167160..6555e14358 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -987,9 +987,17 @@ bool MethodVerifier::ComputeWidthsAndCountOps() { 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 @@ bool MethodVerifier::ComputeWidthsAndCountOps() { 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 bfcd95c846..829dea9107 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -52,6 +52,7 @@ jclass WellKnownClasses::java_lang_ClassNotFoundException; 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 @@ void WellKnownClasses::Init(JNIEnv* env) { 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 @@ void WellKnownClasses::Init(JNIEnv* env) { 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 @@ void WellKnownClasses::Clear() { 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 7deef636b1..b2fd4d6e4c 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -64,6 +64,7 @@ struct WellKnownClasses { 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 ad705c56d2..58b33be573 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 0000000000..e1e30e3f1d --- /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 0000000000..a96ba97d2a --- /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 0000000000..0799b6ed2d --- /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 0000000000..187f622730 --- /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 10ffcc537d..0000000000 --- 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 0000000000..659493611f --- /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 6e68f7c91e..7aab6598a1 100644 --- a/test/484-checker-register-hints/src/Main.java +++ b/test/484-checker-register-hints/src/Main.java @@ -98,18 +98,6 @@ public class Main { /// 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 3721955670..49292c9ac1 100755 --- a/test/593-checker-boolean-2-integral-conv/build +++ b/test/593-checker-boolean-2-integral-conv/build @@ -20,7 +20,4 @@ export USE_JACK=false # 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 00ebaaf451..494ab95434 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 3503b2e877..fdc0919f2b 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 @@ public class Main { 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 @@ public class Main { 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 @@ public class Main { 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 @@ public class Main { 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 10ffcc537d..0000000000 --- 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 57abf5d29e..1add0f1026 100644 --- a/test/661-checker-simd-reduc/src/Main.java +++ b/test/661-checker-simd-reduc/src/Main.java @@ -81,6 +81,16 @@ public class Main { /// 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 @@ public class Main { // /// 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 @@ public class Main { /// 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 @@ public class Main { /// 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 @@ public class Main { /// 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 @@ public class Main { /// 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 @@ public class Main { /// 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 @@ public class Main { /// 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 @@ public class Main { /// 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 @@ public class Main { /// 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 66eea642a4..6cd6d6465a 100644 --- a/test/665-checker-simd-zero/src/Main.java +++ b/test/665-checker-simd-zero/src/Main.java @@ -29,6 +29,12 @@ public class Main { /// 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 @@ public class Main { /// 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 @@ public class Main { /// 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 @@ public class Main { /// 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 @@ public class Main { /// 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 @@ public class Main { /// 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 @@ public class Main { /// 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 @@ public class Main { /// 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 0000000000..b0aad4deb5 --- /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 0000000000..a46bfaa005 --- /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 0000000000..a6235b8be8 --- /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 e6f7d7773f..76b23cb906 100644 --- a/test/910-methods/check +++ b/test/910-methods/check @@ -19,8 +19,14 @@ if [[ "$USE_JACK" == true ]]; then 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 8f1082ffbe..225f41b5b6 100755 --- a/test/988-method-trace/gen_srcs.py +++ b/test/988-method-trace/gen_srcs.py @@ -28,8 +28,8 @@ import sys 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 @@ IDX_CLASS_NAME = -3 # 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 @@ $initialize_classes } 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 099fbf2ce8..3069f1a2c3 100644 --- a/test/988-method-trace/src/art/Test988Intrinsics.java +++ b/test/988-method-trace/src/art/Test988Intrinsics.java @@ -44,7 +44,7 @@ class Test988Intrinsics { } 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 3734ce8634..e9cf3b32c6 100644 --- a/test/993-breakpoints/breakpoints.cc +++ b/test/993-breakpoints/breakpoints.cc @@ -49,6 +49,57 @@ jobject JNICALL Java_art_Test993_constructNative(JNIEnv* env, } 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 962154734b..1749a77e9d 100644 --- a/test/993-breakpoints/expected.txt +++ b/test/993-breakpoints/expected.txt @@ -552,6 +552,107 @@ Running private instance invoke 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 781ebffc0f..d6a6a676cd 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 class Test993 { } 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 class Test993 { 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 @@ public class Test993 { } 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 @@ public class Test993 { Thread.currentThread()); runMethodTests(); + runBCPMethodTests(); runConstructorTests(); Breakpoint.stopBreakpointWatch(Thread.currentThread()); @@ -302,6 +368,94 @@ public class Test993 { 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 16b30f988f..17ef1141df 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -359,6 +359,7 @@ cc_defaults { "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 e5da385a8a..a9a0492fe9 100644 --- a/tools/ahat/Android.mk +++ b/tools/ahat/Android.mk @@ -30,7 +30,52 @@ LOCAL_MODULE := ahat # 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 # EMMA_INSTRUMENT 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 0000000000..7920adae55 --- /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 0000000000..d0d04685d6 --- /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 0000000000..e69de29bb2 --- /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 5a22fc75fe..d5106dc1dd 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 @@ import java.util.List; /** * 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 75a68277d3..06ffca2792 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 a0fbf777dd..048573e915 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.text.ParseException; 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 50a4805bed..ccdd6e4df7 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 @@ public class AhatArrayInstance extends AhatInstance { 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 @@ public class AhatArrayInstance extends AhatInstance { } @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 94efa5049f..cb9d959508 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 @@ public class AhatClassInstance extends AhatInstance { // the field types and names to save memory. private Value[] mFields; - public AhatClassInstance(long id) { + AhatClassInstance(long id) { super(id); } @@ -36,7 +36,7 @@ public class AhatClassInstance extends AhatInstance { } @Override - protected long getExtraJavaSize() { + long getExtraJavaSize() { return 0; } @@ -244,7 +244,7 @@ public class AhatClassInstance extends AhatInstance { } @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 be0f71306e..3babf76842 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 @@ public class AhatClassObj extends AhatInstance { 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 @@ public class AhatClassObj extends AhatInstance { } @Override - protected long getExtraJavaSize() { + long getExtraJavaSize() { return mStaticFieldsSize; } 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 1a3d127fc9..a9f819f710 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 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, // 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 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, * 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 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, return null; } - public static class RegisteredNativeAllocation { + static class RegisteredNativeAllocation { public AhatInstance referent; public long size; }; @@ -397,7 +397,7 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, * 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 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, return null; } - public void setBaseline(AhatInstance baseline) { + void setBaseline(AhatInstance baseline) { mBaseline = baseline; } @@ -470,11 +470,11 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, 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 07f5b50012..b8cdbddaec 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 @@ package com.android.ahat.heapdump; * * 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 884940370d..d65642561c 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 @@ import java.util.List; * * 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 @@ public class AhatPlaceHolderInstance extends AhatInstance { 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 945966cec7..59ce5d1c6c 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 @@ public class AhatSnapshot implements Diffable<AhatSnapshot> { 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 0e128cd50a..256a3b46f6 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 756b7d2554..d7b1dd78d6 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 @@ public class Parser { } @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 980f2780b6..f1340bd07b 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 @@ package com.android.ahat.heapdump; * '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 af552ea2c9..734f889af6 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 @@ public enum RootType { 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 523550ad2c..4978d52830 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 @@ public class Site implements Diffable<Site> { /** * 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 5210e31167..b01cffff72 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.AbstractList; 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 726bc47cf2..40249615a2 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 @@ public enum Type { 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 01fd25057d..eea427774b 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 @@ public abstract class Value { /** * 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 @@ public abstract class Value { } @Override - protected Type getType() { + Type getType() { return Type.BOOLEAN; } @@ -184,7 +184,7 @@ public abstract class Value { } @Override - protected Type getType() { + Type getType() { return Type.BYTE; } @@ -215,7 +215,7 @@ public abstract class Value { } @Override - protected Type getType() { + Type getType() { return Type.CHAR; } @@ -241,7 +241,7 @@ public abstract class Value { } @Override - protected Type getType() { + Type getType() { return Type.DOUBLE; } @@ -267,7 +267,7 @@ public abstract class Value { } @Override - protected Type getType() { + Type getType() { return Type.FLOAT; } @@ -304,7 +304,7 @@ public abstract class Value { } @Override - protected Type getType() { + Type getType() { return Type.OBJECT; } @@ -345,7 +345,7 @@ public abstract class Value { } @Override - protected Type getType() { + Type getType() { return Type.INT; } @@ -381,7 +381,7 @@ public abstract class Value { } @Override - protected Type getType() { + Type getType() { return Type.LONG; } @@ -407,7 +407,7 @@ public abstract class Value { } @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 50c110aad4..131bbf3cf6 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 @@ public class ProguardMap { 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; |