291 lines
10 KiB
C++
291 lines
10 KiB
C++
/*
|
|
* 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 "class_linker.h"
|
|
#include "driver/compiler_driver.h"
|
|
#include "driver/compiler_options.h"
|
|
#include "invoke_type.h"
|
|
#include "mirror/dex_cache-inl.h"
|
|
#include "nodes.h"
|
|
#include "scoped_thread_state_change-inl.h"
|
|
#include "thread-current-inl.h"
|
|
#include "utils.h"
|
|
|
|
namespace art {
|
|
|
|
// Function that returns whether an intrinsic is static/direct or virtual.
|
|
static inline InvokeType GetIntrinsicInvokeType(Intrinsics i) {
|
|
switch (i) {
|
|
case Intrinsics::kNone:
|
|
return kInterface; // Non-sensical for intrinsic.
|
|
#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
|
|
case Intrinsics::k ## Name: \
|
|
return IsStatic;
|
|
#include "intrinsics_list.h"
|
|
INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
|
|
#undef INTRINSICS_LIST
|
|
#undef OPTIMIZING_INTRINSICS
|
|
}
|
|
return kInterface;
|
|
}
|
|
|
|
// Function that returns whether an intrinsic needs an environment or not.
|
|
static inline IntrinsicNeedsEnvironmentOrCache NeedsEnvironmentOrCache(Intrinsics i) {
|
|
switch (i) {
|
|
case Intrinsics::kNone:
|
|
return kNeedsEnvironmentOrCache; // Non-sensical for intrinsic.
|
|
#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
|
|
case Intrinsics::k ## Name: \
|
|
return NeedsEnvironmentOrCache;
|
|
#include "intrinsics_list.h"
|
|
INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
|
|
#undef INTRINSICS_LIST
|
|
#undef OPTIMIZING_INTRINSICS
|
|
}
|
|
return kNeedsEnvironmentOrCache;
|
|
}
|
|
|
|
// Function that returns whether an intrinsic has side effects.
|
|
static inline IntrinsicSideEffects GetSideEffects(Intrinsics i) {
|
|
switch (i) {
|
|
case Intrinsics::kNone:
|
|
return kAllSideEffects;
|
|
#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
|
|
case Intrinsics::k ## Name: \
|
|
return SideEffects;
|
|
#include "intrinsics_list.h"
|
|
INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
|
|
#undef INTRINSICS_LIST
|
|
#undef OPTIMIZING_INTRINSICS
|
|
}
|
|
return kAllSideEffects;
|
|
}
|
|
|
|
// Function that returns whether an intrinsic can throw exceptions.
|
|
static inline IntrinsicExceptions GetExceptions(Intrinsics i) {
|
|
switch (i) {
|
|
case Intrinsics::kNone:
|
|
return kCanThrow;
|
|
#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
|
|
case Intrinsics::k ## Name: \
|
|
return Exceptions;
|
|
#include "intrinsics_list.h"
|
|
INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
|
|
#undef INTRINSICS_LIST
|
|
#undef OPTIMIZING_INTRINSICS
|
|
}
|
|
return kCanThrow;
|
|
}
|
|
|
|
static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) {
|
|
// Whenever the intrinsic is marked as static, report an error if we find an InvokeVirtual.
|
|
//
|
|
// Whenever the intrinsic is marked as direct and we find an InvokeVirtual, a devirtualization
|
|
// failure occured. We might be in a situation where we have inlined a method that calls an
|
|
// intrinsic, but that method is in a different dex file on which we do not have a
|
|
// verified_method that would have helped the compiler driver sharpen the call. In that case,
|
|
// make sure that the intrinsic is actually for some final method (or in a final class), as
|
|
// otherwise the intrinsics setup is broken.
|
|
//
|
|
// For the last direction, we have intrinsics for virtual functions that will perform a check
|
|
// inline. If the precise type is known, however, the instruction will be sharpened to an
|
|
// InvokeStaticOrDirect.
|
|
InvokeType intrinsic_type = GetIntrinsicInvokeType(intrinsic);
|
|
InvokeType invoke_type = invoke->GetInvokeType();
|
|
switch (intrinsic_type) {
|
|
case kStatic:
|
|
return (invoke_type == kStatic);
|
|
|
|
case kDirect:
|
|
if (invoke_type == kDirect) {
|
|
return true;
|
|
}
|
|
if (invoke_type == kVirtual) {
|
|
ArtMethod* art_method = invoke->GetResolvedMethod();
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
return (art_method->IsFinal() || art_method->GetDeclaringClass()->IsFinal());
|
|
}
|
|
return false;
|
|
|
|
case kVirtual:
|
|
// Call might be devirtualized.
|
|
return (invoke_type == kVirtual || invoke_type == kDirect);
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void IntrinsicsRecognizer::Run() {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
for (HBasicBlock* block : graph_->GetReversePostOrder()) {
|
|
for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
|
|
inst_it.Advance()) {
|
|
HInstruction* inst = inst_it.Current();
|
|
if (inst->IsInvoke()) {
|
|
HInvoke* invoke = inst->AsInvoke();
|
|
ArtMethod* art_method = invoke->GetResolvedMethod();
|
|
if (art_method != nullptr && art_method->IsIntrinsic()) {
|
|
Intrinsics intrinsic = static_cast<Intrinsics>(art_method->GetIntrinsic());
|
|
if (!CheckInvokeType(intrinsic, invoke)) {
|
|
LOG(WARNING) << "Found an intrinsic with unexpected invoke type: "
|
|
<< static_cast<uint32_t>(intrinsic) << " for "
|
|
<< art_method->PrettyMethod()
|
|
<< invoke->DebugName();
|
|
} else {
|
|
invoke->SetIntrinsic(intrinsic,
|
|
NeedsEnvironmentOrCache(intrinsic),
|
|
GetSideEffects(intrinsic),
|
|
GetExceptions(intrinsic));
|
|
MaybeRecordStat(MethodCompilationStat::kIntrinsicRecognized);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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";
|
|
return;
|
|
}
|
|
|
|
// The intrinsic will call if it needs to allocate a j.l.Integer.
|
|
LocationSummary* locations = new (invoke->GetBlock()->GetGraph()->GetArena()) LocationSummary(
|
|
invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
|
|
if (!invoke->InputAt(0)->IsConstant()) {
|
|
locations->SetInAt(0, Location::RequiresRegister());
|
|
}
|
|
locations->AddTemp(first_argument_location);
|
|
locations->SetOut(return_location);
|
|
}
|
|
|
|
IntrinsicVisitor::IntegerValueOfInfo IntrinsicVisitor::ComputeIntegerValueOfInfo() {
|
|
// 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.
|
|
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->FindSystemClass(self, "Ljava/lang/Integer$IntegerCache;");
|
|
if (info.integer_cache == nullptr) {
|
|
self->ClearException();
|
|
return info;
|
|
}
|
|
if (!heap->ObjectIsInBootImageSpace(info.integer_cache) || !info.integer_cache->IsInitialized()) {
|
|
// Optimization only works if the class is initialized and in the boot image.
|
|
return info;
|
|
}
|
|
info.integer = class_linker->FindSystemClass(self, "Ljava/lang/Integer;");
|
|
if (info.integer == nullptr) {
|
|
self->ClearException();
|
|
return info;
|
|
}
|
|
if (!heap->ObjectIsInBootImageSpace(info.integer) || !info.integer->IsInitialized()) {
|
|
// Optimization only works if the class is initialized and in the boot image.
|
|
return info;
|
|
}
|
|
|
|
ArtField* field = info.integer_cache->FindDeclaredStaticField("cache", "[Ljava/lang/Integer;");
|
|
if (field == nullptr) {
|
|
return info;
|
|
}
|
|
info.cache = static_cast<mirror::ObjectArray<mirror::Object>*>(
|
|
field->GetObject(info.integer_cache).Ptr());
|
|
if (info.cache == nullptr) {
|
|
return info;
|
|
}
|
|
|
|
if (!heap->ObjectIsInBootImageSpace(info.cache)) {
|
|
// Optimization only works if the object is in the boot image.
|
|
return info;
|
|
}
|
|
|
|
field = info.integer->FindDeclaredInstanceField("value", "I");
|
|
if (field == nullptr) {
|
|
return info;
|
|
}
|
|
info.value_offset = field->GetOffset().Int32Value();
|
|
|
|
field = info.integer_cache->FindDeclaredStaticField("low", "I");
|
|
if (field == nullptr) {
|
|
return info;
|
|
}
|
|
info.low = field->GetInt(info.integer_cache);
|
|
|
|
field = info.integer_cache->FindDeclaredStaticField("high", "I");
|
|
if (field == nullptr) {
|
|
return info;
|
|
}
|
|
info.high = field->GetInt(info.integer_cache);
|
|
|
|
DCHECK_EQ(info.cache->GetLength(), info.high - info.low + 1);
|
|
return info;
|
|
}
|
|
|
|
} // namespace art
|