You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
418 lines
19 KiB
418 lines
19 KiB
/*
|
|
* 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<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;
|
|
}
|
|
|
|
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<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();
|
|
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 (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<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 = 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<mirror::Class> 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<mirror::Class> 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<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);
|
|
|
|
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 (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<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);
|
|
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<mirror::Reference>()->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<mirror::Reference>()->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<mirror::Class> string_class = GetClassRoot<mirror::String>();
|
|
CHECK(!art::Runtime::Current()->GetHeap()->IsMovableObject(string_class));
|
|
}
|
|
}
|
|
|
|
} // namespace art
|