/* * Copyright (C) 2015 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 "intrinsics.h" #include "art_field-inl.h" #include "art_method-inl.h" #include "base/utils.h" #include "class_linker.h" #include "class_root-inl.h" #include "code_generator.h" #include "dex/invoke_type.h" #include "driver/compiler_options.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" namespace art { std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) { switch (intrinsic) { case Intrinsics::kNone: os << "None"; break; #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \ case Intrinsics::k ## Name: \ os << # Name; \ break; #include "intrinsics_list.h" INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef STATIC_INTRINSICS_LIST #undef VIRTUAL_INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS } return os; } static const char kIntegerCacheDescriptor[] = "Ljava/lang/Integer$IntegerCache;"; static const char kIntegerDescriptor[] = "Ljava/lang/Integer;"; static const char kIntegerArrayDescriptor[] = "[Ljava/lang/Integer;"; static const char kLowFieldName[] = "low"; static const char kHighFieldName[] = "high"; static const char kValueFieldName[] = "value"; static ObjPtr> GetBootImageLiveObjects() REQUIRES_SHARED(Locks::mutator_lock_) { gc::Heap* heap = Runtime::Current()->GetHeap(); const std::vector& boot_image_spaces = heap->GetBootImageSpaces(); DCHECK(!boot_image_spaces.empty()); const ImageHeader& main_header = boot_image_spaces[0]->GetImageHeader(); ObjPtr> boot_image_live_objects = ObjPtr>::DownCast( main_header.GetImageRoot(ImageHeader::kBootImageLiveObjects)); DCHECK(boot_image_live_objects != nullptr); DCHECK(heap->ObjectIsInBootImageSpace(boot_image_live_objects)); return boot_image_live_objects; } static ObjPtr LookupInitializedClass(Thread* self, ClassLinker* class_linker, const char* descriptor) REQUIRES_SHARED(Locks::mutator_lock_) { ObjPtr klass = class_linker->LookupClass(self, descriptor, /* class_loader= */ nullptr); DCHECK(klass != nullptr); DCHECK(klass->IsInitialized()); return klass; } static ObjPtr> GetIntegerCacheArray( ObjPtr cache_class) REQUIRES_SHARED(Locks::mutator_lock_) { ArtField* cache_field = cache_class->FindDeclaredStaticField("cache", kIntegerArrayDescriptor); DCHECK(cache_field != nullptr); return ObjPtr>::DownCast(cache_field->GetObject(cache_class)); } static int32_t GetIntegerCacheField(ObjPtr cache_class, const char* field_name) REQUIRES_SHARED(Locks::mutator_lock_) { ArtField* field = cache_class->FindDeclaredStaticField(field_name, "I"); DCHECK(field != nullptr); return field->GetInt(cache_class); } static bool CheckIntegerCache(Thread* self, ClassLinker* class_linker, ObjPtr> boot_image_live_objects, ObjPtr> 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 cache_class = LookupInitializedClass(self, class_linker, kIntegerCacheDescriptor); ObjPtr integer_class = LookupInitializedClass(self, class_linker, kIntegerDescriptor); // Check that the current cache is the same as the `boot_image_cache`. ObjPtr> current_cache = GetIntegerCacheArray(cache_class); if (current_cache != boot_image_cache) { return false; // Messed up IntegerCache.cache. } // Check that the range matches the boot image cache length. int32_t low = GetIntegerCacheField(cache_class, kLowFieldName); int32_t high = GetIntegerCacheField(cache_class, kHighFieldName); 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(kValueFieldName, "I"); DCHECK(value_field != nullptr); for (int32_t i = 0, len = boot_image_cache->GetLength(); i != len; ++i) { ObjPtr 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 current_object = boot_image_cache->GetWithoutChecks(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; } static bool CanReferenceBootImageObjects(HInvoke* invoke, const CompilerOptions& compiler_options) { // Piggyback on the method load kind to determine whether we can use PC-relative addressing // for AOT. This should cover both the testing config (non-PIC boot image) and codegens that // reject PC-relative load kinds and fall back to the runtime call. if (compiler_options.IsAotCompiler() && !invoke->AsInvokeStaticOrDirect()->HasPcRelativeMethodLoadKind()) { return false; } if (!compiler_options.IsBootImage() && Runtime::Current()->GetHeap()->GetBootImageSpaces().empty()) { DCHECK(compiler_options.IsJitCompiler()); return false; // Running without boot image, cannot use required boot image objects. } return true; } void IntrinsicVisitor::ComputeIntegerValueOfLocations(HInvoke* invoke, CodeGenerator* codegen, Location return_location, Location first_argument_location) { // The intrinsic will call if it needs to allocate a j.l.Integer. LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly; const CompilerOptions& compiler_options = codegen->GetCompilerOptions(); if (!CanReferenceBootImageObjects(invoke, compiler_options)) { return; } if (compiler_options.IsBootImage()) { if (!compiler_options.IsImageClass(kIntegerCacheDescriptor) || !compiler_options.IsImageClass(kIntegerDescriptor)) { return; } ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Thread* self = Thread::Current(); ScopedObjectAccess soa(self); ObjPtr cache_class = class_linker->LookupClass( self, kIntegerCacheDescriptor, /* class_loader= */ nullptr); DCHECK(cache_class != nullptr); if (UNLIKELY(!cache_class->IsInitialized())) { LOG(WARNING) << "Image class " << cache_class->PrettyDescriptor() << " is uninitialized."; return; } ObjPtr integer_class = class_linker->LookupClass(self, kIntegerDescriptor, /* class_loader= */ nullptr); DCHECK(integer_class != nullptr); if (UNLIKELY(!integer_class->IsInitialized())) { LOG(WARNING) << "Image class " << integer_class->PrettyDescriptor() << " is uninitialized."; return; } int32_t low = GetIntegerCacheField(cache_class, kLowFieldName); int32_t high = GetIntegerCacheField(cache_class, kHighFieldName); if (kIsDebugBuild) { ObjPtr> current_cache = GetIntegerCacheArray(cache_class); CHECK(current_cache != nullptr); CHECK_EQ(current_cache->GetLength(), high - low + 1); ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I"); CHECK(value_field != nullptr); for (int32_t i = 0, len = current_cache->GetLength(); i != len; ++i) { ObjPtr current_object = current_cache->GetWithoutChecks(i); CHECK(current_object != nullptr); CHECK_EQ(value_field->GetInt(current_object), low + i); } } if (invoke->InputAt(0)->IsIntConstant()) { int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue(); if (static_cast(value) - static_cast(low) < static_cast(high - low + 1)) { // No call, we shall use direct pointer to the Integer object. call_kind = LocationSummary::kNoCall; } } } else { Runtime* runtime = Runtime::Current(); Thread* self = Thread::Current(); ScopedObjectAccess soa(self); ObjPtr> boot_image_live_objects = GetBootImageLiveObjects(); ObjPtr> cache = IntrinsicObjects::GetIntegerValueOfCache(boot_image_live_objects); if (cache == nullptr) { return; // No cache in the boot image. } if (compiler_options.IsJitCompiler()) { if (!CheckIntegerCache(self, runtime->GetClassLinker(), boot_image_live_objects, cache)) { return; // The cache was somehow messed up, probably by using reflection. } } else { DCHECK(compiler_options.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 low_integer = IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, 0u); ObjPtr integer_class = low_integer->GetClass(); ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I"); DCHECK(value_field != nullptr); int32_t low = value_field->GetInt(low_integer); if (static_cast(value) - static_cast(low) < static_cast(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 = codegen->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()); } } static int32_t GetIntegerCacheLowFromIntegerCache(Thread* self, ClassLinker* class_linker) REQUIRES_SHARED(Locks::mutator_lock_) { ObjPtr cache_class = LookupInitializedClass(self, class_linker, kIntegerCacheDescriptor); return GetIntegerCacheField(cache_class, kLowFieldName); } inline IntrinsicVisitor::IntegerValueOfInfo::IntegerValueOfInfo() : value_offset(0), low(0), length(0u), value_boot_image_reference(kInvalidReference) {} IntrinsicVisitor::IntegerValueOfInfo IntrinsicVisitor::ComputeIntegerValueOfInfo( HInvoke* invoke, const CompilerOptions& compiler_options) { // 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. // 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. ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Thread* self = Thread::Current(); ScopedObjectAccess soa(self); IntegerValueOfInfo info; if (compiler_options.IsBootImage()) { ObjPtr integer_class = invoke->GetResolvedMethod()->GetDeclaringClass(); DCHECK(integer_class->DescriptorEquals(kIntegerDescriptor)); ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I"); DCHECK(value_field != nullptr); info.value_offset = value_field->GetOffset().Uint32Value(); ObjPtr cache_class = LookupInitializedClass(self, class_linker, kIntegerCacheDescriptor); info.low = GetIntegerCacheField(cache_class, kLowFieldName); int32_t high = GetIntegerCacheField(cache_class, kHighFieldName); info.length = dchecked_integral_cast(high - info.low + 1); if (invoke->InputAt(0)->IsIntConstant()) { int32_t input_value = invoke->InputAt(0)->AsIntConstant()->GetValue(); uint32_t index = static_cast(input_value) - static_cast(info.low); if (index < static_cast(info.length)) { info.value_boot_image_reference = IntrinsicObjects::EncodePatch( IntrinsicObjects::PatchType::kIntegerValueOfObject, index); } else { // Not in the cache. info.value_boot_image_reference = IntegerValueOfInfo::kInvalidReference; } } else { info.array_data_boot_image_reference = IntrinsicObjects::EncodePatch(IntrinsicObjects::PatchType::kIntegerValueOfArray); } } else { ObjPtr> boot_image_live_objects = GetBootImageLiveObjects(); ObjPtr low_integer = IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, 0u); ObjPtr integer_class = low_integer->GetClass(); ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I"); DCHECK(value_field != nullptr); info.value_offset = value_field->GetOffset().Uint32Value(); if (compiler_options.IsJitCompiler()) { // 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, class_linker); } else { // For app 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, class_linker)); } // Do not look at `IntegerCache.high`, use the immutable length of the cache array instead. info.length = dchecked_integral_cast( 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(input_value) - static_cast(info.low); if (index < static_cast(info.length)) { ObjPtr integer = IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, index); info.value_boot_image_reference = CodeGenerator::GetBootImageOffset(integer); } else { // Not in the cache. info.value_boot_image_reference = IntegerValueOfInfo::kInvalidReference; } } else { info.array_data_boot_image_reference = CodeGenerator::GetBootImageOffset(boot_image_live_objects) + IntrinsicObjects::GetIntegerValueOfArrayDataOffset(boot_image_live_objects).Uint32Value(); } } return info; } MemberOffset IntrinsicVisitor::GetReferenceDisableIntrinsicOffset() { ScopedObjectAccess soa(Thread::Current()); // The "disableIntrinsic" is the first static field. ArtField* field = GetClassRoot()->GetStaticField(0); DCHECK_STREQ(field->GetName(), "disableIntrinsic"); return field->GetOffset(); } MemberOffset IntrinsicVisitor::GetReferenceSlowPathEnabledOffset() { ScopedObjectAccess soa(Thread::Current()); // The "slowPathEnabled" is the second static field. ArtField* field = GetClassRoot()->GetStaticField(1); DCHECK_STREQ(field->GetName(), "slowPathEnabled"); return field->GetOffset(); } void IntrinsicVisitor::CreateReferenceGetReferentLocations(HInvoke* invoke, CodeGenerator* codegen) { if (!CanReferenceBootImageObjects(invoke, codegen->GetCompilerOptions())) { return; } ArenaAllocator* allocator = codegen->GetGraph()->GetAllocator(); LocationSummary* locations = new (allocator) LocationSummary(invoke, LocationSummary::kCallOnSlowPath, kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); locations->SetOut(Location::RequiresRegister()); } void IntrinsicVisitor::CreateReferenceRefersToLocations(HInvoke* invoke) { if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) { // Unimplemented for non-Baker read barrier. return; } ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator(); LocationSummary* locations = new (allocator) LocationSummary(invoke, LocationSummary::kCallOnSlowPath, kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); locations->SetOut(Location::RequiresRegister()); } void IntrinsicVisitor::AssertNonMovableStringClass() { if (kIsDebugBuild) { ScopedObjectAccess soa(Thread::Current()); ObjPtr string_class = GetClassRoot(); CHECK(!art::Runtime::Current()->GetHeap()->IsMovableObject(string_class)); } } } // namespace art