diff options
author | Vladimir Marko <vmarko@google.com> | 2018-06-22 02:16:08 -0700 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2018-06-22 02:16:08 -0700 |
commit | 5f7748b6ec5b51afe8314f2c169f99a1f93a2053 (patch) | |
tree | 25879b7002fd0601f9ed0ac9b6ef0b7d84134f55 /compiler/optimizing/intrinsics.cc | |
parent | 0ad09c4b396d37f57eecca8b1f841fb99a1a0baf (diff) | |
parent | 077cc1c59c32f08c752c603bf991160c22eae9bb (diff) |
Merge "Implement Integer.valueOf() intrinsic for PIC." am: ccfc88af4a am: 8e73527acf
am: 077cc1c59c
Change-Id: If3a8ed8679e4fa8ef851b0c92e303473ef74a910
Diffstat (limited to 'compiler/optimizing/intrinsics.cc')
-rw-r--r-- | compiler/optimizing/intrinsics.cc | 276 |
1 files changed, 198 insertions, 78 deletions
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index f0c91f3ba00..81b2b7b5814 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -21,10 +21,12 @@ #include "base/utils.h" #include "class_linker.h" #include "dex/invoke_type.h" -#include "driver/compiler_driver.h" #include "driver/compiler_options.h" -#include "mirror/dex_cache-inl.h" +#include "gc/space/image_space.h" +#include "image-inl.h" +#include "intrinsic_objects.h" #include "nodes.h" +#include "obj_ptr-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" @@ -221,105 +223,223 @@ std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) { return os; } +static ObjPtr<mirror::ObjectArray<mirror::Object>> GetBootImageLiveObjects() + REQUIRES_SHARED(Locks::mutator_lock_) { + gc::Heap* heap = Runtime::Current()->GetHeap(); + const std::vector<gc::space::ImageSpace*>& boot_image_spaces = heap->GetBootImageSpaces(); + DCHECK(!boot_image_spaces.empty()); + const ImageHeader& main_header = boot_image_spaces[0]->GetImageHeader(); + ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects = + ObjPtr<mirror::ObjectArray<mirror::Object>>::DownCast( + main_header.GetImageRoot<kWithoutReadBarrier>(ImageHeader::kBootImageLiveObjects)); + DCHECK(boot_image_live_objects != nullptr); + DCHECK(heap->ObjectIsInBootImageSpace(boot_image_live_objects)); + return boot_image_live_objects; +} + +static bool CheckIntegerCache(Thread* self, + ClassLinker* class_linker, + ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects, + ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_cache) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(boot_image_cache != nullptr); + + // Since we have a cache in the boot image, both java.lang.Integer and + // java.lang.Integer$IntegerCache must be initialized in the boot image. + ObjPtr<mirror::Class> cache_class = class_linker->LookupClass( + self, "Ljava/lang/Integer$IntegerCache;", /* class_loader */ nullptr); + DCHECK(cache_class != nullptr); + DCHECK(cache_class->IsInitialized()); + ObjPtr<mirror::Class> integer_class = + class_linker->LookupClass(self, "Ljava/lang/Integer;", /* class_loader */ nullptr); + DCHECK(integer_class != nullptr); + DCHECK(integer_class->IsInitialized()); + + // Check that the current cache is the same as the `boot_image_cache`. + ArtField* cache_field = cache_class->FindDeclaredStaticField("cache", "[Ljava/lang/Integer;"); + DCHECK(cache_field != nullptr); + ObjPtr<mirror::ObjectArray<mirror::Object>> current_cache = + ObjPtr<mirror::ObjectArray<mirror::Object>>::DownCast(cache_field->GetObject(cache_class)); + if (current_cache != boot_image_cache) { + return false; // Messed up IntegerCache.cache. + } + + // Check that the range matches the boot image cache length. + ArtField* low_field = cache_class->FindDeclaredStaticField("low", "I"); + DCHECK(low_field != nullptr); + int32_t low = low_field->GetInt(cache_class); + ArtField* high_field = cache_class->FindDeclaredStaticField("high", "I"); + DCHECK(high_field != nullptr); + int32_t high = high_field->GetInt(cache_class); + if (boot_image_cache->GetLength() != high - low + 1) { + return false; // Messed up IntegerCache.low or IntegerCache.high. + } + + // Check that the elements match the boot image intrinsic objects and check their values as well. + ArtField* value_field = integer_class->FindDeclaredInstanceField("value", "I"); + DCHECK(value_field != nullptr); + for (int32_t i = 0, len = boot_image_cache->GetLength(); i != len; ++i) { + ObjPtr<mirror::Object> boot_image_object = + IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, i); + DCHECK(Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boot_image_object)); + // No need for read barrier for comparison with a boot image object. + ObjPtr<mirror::Object> current_object = + boot_image_cache->GetWithoutChecks<kVerifyNone, kWithoutReadBarrier>(i); + if (boot_image_object != current_object) { + return false; // Messed up IntegerCache.cache[i] + } + if (value_field->GetInt(boot_image_object) != low + i) { + return false; // Messed up IntegerCache.cache[i].value. + } + } + + return true; +} + void IntrinsicVisitor::ComputeIntegerValueOfLocations(HInvoke* invoke, CodeGenerator* codegen, Location return_location, Location first_argument_location) { - if (Runtime::Current()->IsAotCompiler()) { - if (codegen->GetCompilerOptions().IsBootImage() || - codegen->GetCompilerOptions().GetCompilePic()) { - // TODO(ngeoffray): Support boot image compilation. - return; - } - } - - IntegerValueOfInfo info = ComputeIntegerValueOfInfo(); - - // Most common case is that we have found all we needed (classes are initialized - // and in the boot image). Bail if not. - if (info.integer_cache == nullptr || - info.integer == nullptr || - info.cache == nullptr || - info.value_offset == 0 || - // low and high cannot be 0, per the spec. - info.low == 0 || - info.high == 0) { - LOG(INFO) << "Integer.valueOf will not be optimized"; + if (codegen->GetCompilerOptions().IsBootImage()) { + // TODO: Implement for boot image. We need access to CompilerDriver::IsImageClass() + // to verify that the IntegerCache shall be in the image. return; } + Runtime* runtime = Runtime::Current(); + gc::Heap* heap = runtime->GetHeap(); + if (heap->GetBootImageSpaces().empty()) { + return; // Running without boot image, cannot use required boot image objects. + } // The intrinsic will call if it needs to allocate a j.l.Integer. - LocationSummary* locations = new (invoke->GetBlock()->GetGraph()->GetAllocator()) LocationSummary( - invoke, LocationSummary::kCallOnMainOnly, kIntrinsified); - if (!invoke->InputAt(0)->IsConstant()) { - locations->SetInAt(0, Location::RequiresRegister()); + LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly; + { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects = GetBootImageLiveObjects(); + ObjPtr<mirror::ObjectArray<mirror::Object>> cache = + IntrinsicObjects::GetIntegerValueOfCache(boot_image_live_objects); + if (cache == nullptr) { + return; // No cache in the boot image. + } + if (runtime->UseJitCompilation()) { + if (!CheckIntegerCache(self, runtime->GetClassLinker(), boot_image_live_objects, cache)) { + return; // The cache was somehow messed up, probably by using reflection. + } + } else { + DCHECK(runtime->IsAotCompiler()); + DCHECK(CheckIntegerCache(self, runtime->GetClassLinker(), boot_image_live_objects, cache)); + if (invoke->InputAt(0)->IsIntConstant()) { + int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue(); + // Retrieve the `value` from the lowest cached Integer. + ObjPtr<mirror::Object> low_integer = + IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, 0u); + ObjPtr<mirror::Class> integer_class = + low_integer->GetClass<kVerifyNone, kWithoutReadBarrier>(); + ArtField* value_field = integer_class->FindDeclaredInstanceField("value", "I"); + DCHECK(value_field != nullptr); + int32_t low = value_field->GetInt(low_integer); + if (static_cast<uint32_t>(value) - static_cast<uint32_t>(low) < + static_cast<uint32_t>(cache->GetLength())) { + // No call, we shall use direct pointer to the Integer object. Note that we cannot + // do this for JIT as the "low" can change through reflection before emitting the code. + call_kind = LocationSummary::kNoCall; + } + } + } + } + + ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator(); + LocationSummary* locations = new (allocator) LocationSummary(invoke, call_kind, kIntrinsified); + if (call_kind == LocationSummary::kCallOnMainOnly) { + locations->SetInAt(0, Location::RegisterOrConstant(invoke->InputAt(0))); + locations->AddTemp(first_argument_location); + locations->SetOut(return_location); + } else { + locations->SetInAt(0, Location::ConstantLocation(invoke->InputAt(0)->AsConstant())); + locations->SetOut(Location::RequiresRegister()); } - locations->AddTemp(first_argument_location); - locations->SetOut(return_location); } -IntrinsicVisitor::IntegerValueOfInfo IntrinsicVisitor::ComputeIntegerValueOfInfo() { +static int32_t GetIntegerCacheLowFromIntegerCache(Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr<mirror::Class> cache_class = Runtime::Current()->GetClassLinker()->LookupClass( + self, "Ljava/lang/Integer$IntegerCache;", /* class_loader */ nullptr); + DCHECK(cache_class != nullptr); + DCHECK(cache_class->IsInitialized()); + ArtField* low_field = cache_class->FindDeclaredStaticField("low", "I"); + DCHECK(low_field != nullptr); + return low_field->GetInt(cache_class); +} + +static uint32_t CalculateBootImageOffset(ObjPtr<mirror::Object> object) + REQUIRES_SHARED(Locks::mutator_lock_) { + gc::Heap* heap = Runtime::Current()->GetHeap(); + DCHECK(heap->ObjectIsInBootImageSpace(object)); + return reinterpret_cast<const uint8_t*>(object.Ptr()) - heap->GetBootImageSpaces()[0]->Begin(); +} + +inline IntrinsicVisitor::IntegerValueOfInfo::IntegerValueOfInfo() + : integer_boot_image_offset(0u), + value_offset(0), + low(0), + length(0u), + value_boot_image_offset(0u) {} + +IntrinsicVisitor::IntegerValueOfInfo IntrinsicVisitor::ComputeIntegerValueOfInfo(HInvoke* invoke) { // Note that we could cache all of the data looked up here. but there's no good // location for it. We don't want to add it to WellKnownClasses, to avoid creating global // jni values. Adding it as state to the compiler singleton seems like wrong // separation of concerns. // The need for this data should be pretty rare though. - // The most common case is that the classes are in the boot image and initialized, - // which is easy to generate code for. We bail if not. + // Note that at this point we can no longer abort the code generation. Therefore, + // we need to provide data that shall not lead to a crash even if the fields were + // modified through reflection since ComputeIntegerValueOfLocations() when JITting. + + Runtime* runtime = Runtime::Current(); Thread* self = Thread::Current(); ScopedObjectAccess soa(self); - Runtime* runtime = Runtime::Current(); - ClassLinker* class_linker = runtime->GetClassLinker(); - gc::Heap* heap = runtime->GetHeap(); - IntegerValueOfInfo info; - info.integer_cache = class_linker->LookupClass(self, - "Ljava/lang/Integer$IntegerCache;", - /* class_loader */ nullptr).Ptr(); - if (info.integer_cache == nullptr || !info.integer_cache->IsInitialized()) { - // Optimization only works if the class is initialized. - return info; - } - if (!heap->ObjectIsInBootImageSpace(info.integer_cache)) { - // Optimization only works if the class is in the boot image. - // TODO: Implement the intrinsic for boot image compilation. - return info; - } - info.integer = - class_linker->LookupClass(self, "Ljava/lang/Integer;", /* class_loader */ nullptr).Ptr(); - DCHECK(info.integer != nullptr); - DCHECK(info.integer->IsInitialized()); // Must be initialized since IntegerCache is initialized. - if (!heap->ObjectIsInBootImageSpace(info.integer)) { - // Optimization only works if the class is in the boot image. - return info; - } + ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects = GetBootImageLiveObjects(); + ObjPtr<mirror::Object> low_integer = + IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, 0u); + ObjPtr<mirror::Class> integer_class = low_integer->GetClass<kVerifyNone, kWithoutReadBarrier>(); + ArtField* value_field = integer_class->FindDeclaredInstanceField("value", "I"); + DCHECK(value_field != nullptr); - ArtField* field = info.integer_cache->FindDeclaredStaticField("cache", "[Ljava/lang/Integer;"); - CHECK(field != nullptr); - info.cache = static_cast<mirror::ObjectArray<mirror::Object>*>( - field->GetObject(info.integer_cache).Ptr()); - if (info.cache == nullptr) { - return info; // Did someone mess up the IntegerCache using reflection? + IntegerValueOfInfo info; + info.integer_boot_image_offset = CalculateBootImageOffset(integer_class); + info.value_offset = value_field->GetOffset().Uint32Value(); + if (runtime->UseJitCompilation()) { + // Use the current `IntegerCache.low` for JIT to avoid truly surprising behavior if the + // code messes up the `value` field in the lowest cached Integer using reflection. + info.low = GetIntegerCacheLowFromIntegerCache(self); + } else { + // For AOT, the `low_integer->value` should be the same as `IntegerCache.low`. + info.low = value_field->GetInt(low_integer); + DCHECK_EQ(info.low, GetIntegerCacheLowFromIntegerCache(self)); } - - if (!heap->ObjectIsInBootImageSpace(info.cache)) { - // Optimization only works if the object is in the boot image. - return info; + // Do not look at `IntegerCache.high`, use the immutable length of the cache array instead. + info.length = dchecked_integral_cast<uint32_t>( + IntrinsicObjects::GetIntegerValueOfCache(boot_image_live_objects)->GetLength()); + + if (invoke->InputAt(0)->IsIntConstant()) { + int32_t input_value = invoke->InputAt(0)->AsIntConstant()->GetValue(); + uint32_t index = static_cast<uint32_t>(input_value) - static_cast<uint32_t>(info.low); + if (index < static_cast<uint32_t>(info.length)) { + ObjPtr<mirror::Object> integer = + IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, index); + DCHECK(runtime->GetHeap()->ObjectIsInBootImageSpace(integer)); + info.value_boot_image_offset = CalculateBootImageOffset(integer); + } else { + info.value_boot_image_offset = 0u; // Not in the cache. + } + } else { + info.array_data_boot_image_offset = + CalculateBootImageOffset(boot_image_live_objects) + + IntrinsicObjects::GetIntegerValueOfArrayDataOffset(boot_image_live_objects).Uint32Value(); } - field = info.integer->FindDeclaredInstanceField("value", "I"); - CHECK(field != nullptr); - info.value_offset = field->GetOffset().Int32Value(); - - field = info.integer_cache->FindDeclaredStaticField("low", "I"); - CHECK(field != nullptr); - info.low = field->GetInt(info.integer_cache); - - field = info.integer_cache->FindDeclaredStaticField("high", "I"); - CHECK(field != nullptr); - info.high = field->GetInt(info.integer_cache); - - DCHECK_EQ(info.cache->GetLength(), info.high - info.low + 1); return info; } |