blob: 619cd8ed414f90fef68167ff760a947e9fd9a000 [file] [log] [blame]
/*
* 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 "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<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 ObjPtr<mirror::Class> LookupInitializedClass(Thread* self,
ClassLinker* class_linker,
const char* descriptor)
REQUIRES_SHARED(Locks::mutator_lock_) {
ObjPtr<mirror::Class> klass =
class_linker->LookupClass(self, descriptor, /* class_loader */ nullptr);
DCHECK(klass != nullptr);
DCHECK(klass->IsInitialized());
return klass;
}
static ObjPtr<mirror::ObjectArray<mirror::Object>> GetIntegerCacheArray(
ObjPtr<mirror::Class> cache_class) REQUIRES_SHARED(Locks::mutator_lock_) {
ArtField* cache_field = cache_class->FindDeclaredStaticField("cache", kIntegerArrayDescriptor);
DCHECK(cache_field != nullptr);
return ObjPtr<mirror::ObjectArray<mirror::Object>>::DownCast(cache_field->GetObject(cache_class));
}
static int32_t GetIntegerCacheField(ObjPtr<mirror::Class> 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<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 =
LookupInitializedClass(self, class_linker, kIntegerCacheDescriptor);
ObjPtr<mirror::Class> integer_class =
LookupInitializedClass(self, class_linker, kIntegerDescriptor);
// Check that the current cache is the same as the `boot_image_cache`.
ObjPtr<mirror::ObjectArray<mirror::Object>> 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<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) {
// 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 (compiler_options.IsBootImage()) {
// Piggyback on the method load kind to determine whether we can use PC-relative addressing.
// 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 (!invoke->AsInvokeStaticOrDirect()->HasPcRelativeMethodLoadKind()) {
return;
}
if (!compiler_options.IsImageClass(kIntegerCacheDescriptor) ||
!compiler_options.IsImageClass(kIntegerDescriptor)) {
return;
}
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
ObjPtr<mirror::Class> 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<mirror::Class> 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<mirror::ObjectArray<mirror::Object>> 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<mirror::Object> 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<uint32_t>(value) - static_cast<uint32_t>(low) <
static_cast<uint32_t>(high - low + 1)) {
// No call, we shall use direct pointer to the Integer object.
call_kind = LocationSummary::kNoCall;
}
}
} else {
Runtime* runtime = Runtime::Current();
if (runtime->GetHeap()->GetBootImageSpaces().empty()) {
return; // Running without boot image, cannot use required boot image objects.
}
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(kValueFieldName, "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());
}
}
static int32_t GetIntegerCacheLowFromIntegerCache(Thread* self, ClassLinker* class_linker)
REQUIRES_SHARED(Locks::mutator_lock_) {
ObjPtr<mirror::Class> cache_class =
LookupInitializedClass(self, class_linker, kIntegerCacheDescriptor);
return GetIntegerCacheField(cache_class, kLowFieldName);
}
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()
: value_offset(0),
low(0),
length(0u),
integer_boot_image_offset(kInvalidReference),
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.
Runtime* runtime = Runtime::Current();
ClassLinker* class_linker = runtime->GetClassLinker();
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
IntegerValueOfInfo info;
if (compiler_options.IsBootImage()) {
ObjPtr<mirror::Class> integer_class =
LookupInitializedClass(self, class_linker, kIntegerDescriptor);
ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I");
DCHECK(value_field != nullptr);
info.value_offset = value_field->GetOffset().Uint32Value();
ObjPtr<mirror::Class> 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<uint32_t>(high - info.low + 1);
info.integer_boot_image_offset = IntegerValueOfInfo::kInvalidReference;
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)) {
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<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(kValueFieldName, "I");
DCHECK(value_field != nullptr);
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, 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<uint32_t>(
IntrinsicObjects::GetIntegerValueOfCache(boot_image_live_objects)->GetLength());
info.integer_boot_image_offset = CalculateBootImageOffset(integer_class);
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);
info.value_boot_image_reference = CalculateBootImageOffset(integer);
} else {
// Not in the cache.
info.value_boot_image_reference = IntegerValueOfInfo::kInvalidReference;
}
} else {
info.array_data_boot_image_reference =
CalculateBootImageOffset(boot_image_live_objects) +
IntrinsicObjects::GetIntegerValueOfArrayDataOffset(boot_image_live_objects).Uint32Value();
}
}
return info;
}
} // namespace art