Merge "Fix copy-paste nit"
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 8bdff21..be82956 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1283,14 +1283,13 @@
return IsImageClass(descriptor);
}
-bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file, uint32_t type_idx) {
+bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(Handle<mirror::DexCache> dex_cache,
+ uint32_t type_idx) {
bool result = false;
if ((IsBootImage() &&
- IsImageClass(dex_file.StringDataByIdx(dex_file.GetTypeId(type_idx).descriptor_idx_))) ||
+ IsImageClass(dex_cache->GetDexFile()->StringDataByIdx(
+ dex_cache->GetDexFile()->GetTypeId(type_idx).descriptor_idx_))) ||
Runtime::Current()->UseJit()) {
- ScopedObjectAccess soa(Thread::Current());
- mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(
- soa.Self(), dex_file, false);
mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
result = (resolved_class != nullptr);
}
@@ -1332,32 +1331,16 @@
return result;
}
-bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexFile& dex_file,
- uint32_t type_idx,
- bool* type_known_final, bool* type_known_abstract,
- bool* equals_referrers_class) {
- if (type_known_final != nullptr) {
- *type_known_final = false;
- }
- if (type_known_abstract != nullptr) {
- *type_known_abstract = false;
- }
- if (equals_referrers_class != nullptr) {
- *equals_referrers_class = false;
- }
- ScopedObjectAccess soa(Thread::Current());
- mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(
- soa.Self(), dex_file, false);
+bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx,
+ Handle<mirror::DexCache> dex_cache,
+ uint32_t type_idx) {
// Get type from dex cache assuming it was populated by the verifier
mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
if (resolved_class == nullptr) {
stats_->TypeNeedsAccessCheck();
return false; // Unknown class needs access checks.
}
- const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx);
- if (equals_referrers_class != nullptr) {
- *equals_referrers_class = (method_id.class_idx_ == type_idx);
- }
+ const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(referrer_idx);
bool is_accessible = resolved_class->IsPublic(); // Public classes are always accessible.
if (!is_accessible) {
mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_);
@@ -1371,12 +1354,6 @@
}
if (is_accessible) {
stats_->TypeDoesntNeedAccessCheck();
- if (type_known_final != nullptr) {
- *type_known_final = resolved_class->IsFinal() && !resolved_class->IsArrayClass();
- }
- if (type_known_abstract != nullptr) {
- *type_known_abstract = resolved_class->IsAbstract() && !resolved_class->IsArrayClass();
- }
} else {
stats_->TypeNeedsAccessCheck();
}
@@ -1384,12 +1361,9 @@
}
bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx,
- const DexFile& dex_file,
+ Handle<mirror::DexCache> dex_cache,
uint32_t type_idx,
bool* finalizable) {
- ScopedObjectAccess soa(Thread::Current());
- mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(
- soa.Self(), dex_file, false);
// Get type from dex cache assuming it was populated by the verifier.
mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
if (resolved_class == nullptr) {
@@ -1399,7 +1373,7 @@
return false; // Unknown class needs access checks.
}
*finalizable = resolved_class->IsFinalizable();
- const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx);
+ const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(referrer_idx);
bool is_accessible = resolved_class->IsPublic(); // Public classes are always accessible.
if (!is_accessible) {
mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_);
@@ -1587,53 +1561,6 @@
}
}
-bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit,
- bool is_put, MemberOffset* field_offset,
- uint32_t* storage_index, bool* is_referrers_class,
- bool* is_volatile, bool* is_initialized,
- Primitive::Type* type) {
- ScopedObjectAccess soa(Thread::Current());
- // Try to resolve the field and compiling method's class.
- ArtField* resolved_field;
- mirror::Class* referrer_class;
- Handle<mirror::DexCache> dex_cache(mUnit->GetDexCache());
- {
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::ClassLoader> class_loader_handle(
- hs.NewHandle(soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())));
- resolved_field =
- ResolveField(soa, dex_cache, class_loader_handle, mUnit, field_idx, true);
- referrer_class = resolved_field != nullptr
- ? ResolveCompilingMethodsClass(soa, dex_cache, class_loader_handle, mUnit) : nullptr;
- }
- bool result = false;
- if (resolved_field != nullptr && referrer_class != nullptr) {
- *is_volatile = IsFieldVolatile(resolved_field);
- std::pair<bool, bool> fast_path = IsFastStaticField(
- dex_cache.Get(), referrer_class, resolved_field, field_idx, storage_index);
- result = is_put ? fast_path.second : fast_path.first;
- }
- if (result) {
- *field_offset = GetFieldOffset(resolved_field);
- *is_referrers_class = IsStaticFieldInReferrerClass(referrer_class, resolved_field);
- // *is_referrers_class == true implies no worrying about class initialization.
- *is_initialized = (*is_referrers_class) ||
- (IsStaticFieldsClassInitialized(referrer_class, resolved_field) &&
- CanAssumeTypeIsPresentInDexCache(*mUnit->GetDexFile(), *storage_index));
- *type = resolved_field->GetTypeAsPrimitiveType();
- } else {
- // Conservative defaults.
- *is_volatile = true;
- *field_offset = MemberOffset(static_cast<size_t>(-1));
- *storage_index = -1;
- *is_referrers_class = false;
- *is_initialized = false;
- *type = Primitive::kPrimVoid;
- }
- ProcessedStaticField(result, *is_referrers_class);
- return result;
-}
-
void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type,
bool no_guarantee_of_dex_cache_entry,
const mirror::Class* referrer_class,
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 4308eac..d63dffa 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -195,25 +195,26 @@
// Callbacks from compiler to see what runtime checks must be generated.
- bool CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file, uint32_t type_idx);
+ bool CanAssumeTypeIsPresentInDexCache(Handle<mirror::DexCache> dex_cache,
+ uint32_t type_idx)
+ SHARED_REQUIRES(Locks::mutator_lock_);
bool CanAssumeStringIsPresentInDexCache(const DexFile& dex_file, uint32_t string_idx)
REQUIRES(!Locks::mutator_lock_);
// Are runtime access checks necessary in the compiled code?
- bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexFile& dex_file,
- uint32_t type_idx, bool* type_known_final = nullptr,
- bool* type_known_abstract = nullptr,
- bool* equals_referrers_class = nullptr)
- REQUIRES(!Locks::mutator_lock_);
+ bool CanAccessTypeWithoutChecks(uint32_t referrer_idx,
+ Handle<mirror::DexCache> dex_cache,
+ uint32_t type_idx)
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Are runtime access and instantiable checks necessary in the code?
// out_is_finalizable is set to whether the type is finalizable.
bool CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx,
- const DexFile& dex_file,
+ Handle<mirror::DexCache> dex_cache,
uint32_t type_idx,
bool* out_is_finalizable)
- REQUIRES(!Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
bool CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_idx,
bool* is_type_initialized, bool* use_direct_type_ptr,
@@ -368,14 +369,6 @@
SHARED_REQUIRES(Locks::mutator_lock_);
- // Can we fastpath static field access? Computes field's offset, volatility and whether the
- // field is within the referrer (which can avoid checking class initialization).
- bool ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put,
- MemberOffset* field_offset, uint32_t* storage_index,
- bool* is_referrers_class, bool* is_volatile, bool* is_initialized,
- Primitive::Type* type)
- REQUIRES(!Locks::mutator_lock_);
-
// Can we fastpath a interface, super class or virtual method call? Computes method's vtable
// index.
bool ComputeInvokeInfo(const DexCompilationUnit* mUnit, const uint32_t dex_pc,
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 06b3968..f5e49c2 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -889,8 +889,15 @@
}
bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
+ Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index)));
+ const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
+ Handle<mirror::DexCache> outer_dex_cache = outer_compilation_unit_->GetDexCache();
+
bool finalizable;
- bool can_throw = NeedsAccessCheck(type_index, &finalizable);
+ bool can_throw = NeedsAccessCheck(type_index, dex_cache, &finalizable);
// Only the non-resolved entrypoint handles the finalizable class case. If we
// need access checks, then we haven't resolved the method and the class may
@@ -899,16 +906,6 @@
? kQuickAllocObject
: kQuickAllocObjectInitialized;
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<3> hs(soa.Self());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->FindDexCache(
- soa.Self(), *dex_compilation_unit_->GetDexFile())));
- Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index)));
- const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
- Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
- outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file)));
-
if (outer_dex_cache.Get() != dex_cache.Get()) {
// We currently do not support inlining allocations across dex files.
return false;
@@ -921,7 +918,7 @@
IsOutermostCompilingClass(type_index),
dex_pc,
/*needs_access_check*/ can_throw,
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, type_index));
+ compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_cache, type_index));
AppendInstruction(load_class);
HInstruction* cls = load_class;
@@ -979,13 +976,9 @@
HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement) {
const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
Thread* self = Thread::Current();
- StackHandleScope<4> hs(self);
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->FindDexCache(
- self, *dex_compilation_unit_->GetDexFile())));
- Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
- outer_compilation_unit_->GetClassLinker()->FindDexCache(
- self, outer_dex_file)));
+ StackHandleScope<2> hs(self);
+ Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
+ Handle<mirror::DexCache> outer_dex_cache = outer_compilation_unit_->GetDexCache();
Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
Handle<mirror::Class> resolved_method_class(hs.NewHandle(resolved_method->GetDeclaringClass()));
@@ -1016,7 +1009,7 @@
is_outer_class,
dex_pc,
/*needs_access_check*/ false,
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, storage_index));
+ compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_cache, storage_index));
AppendInstruction(load_class);
clinit_check = new (arena_) HClinitCheck(load_class, dex_pc);
AppendInstruction(clinit_check);
@@ -1261,12 +1254,10 @@
static mirror::Class* GetClassFrom(CompilerDriver* driver,
const DexCompilationUnit& compilation_unit) {
ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<2> hs(soa.Self());
- const DexFile& dex_file = *compilation_unit.GetDexFile();
+ StackHandleScope<1> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
soa.Decode<mirror::ClassLoader*>(compilation_unit.GetClassLoader())));
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- compilation_unit.GetClassLinker()->FindDexCache(soa.Self(), dex_file)));
+ Handle<mirror::DexCache> dex_cache = compilation_unit.GetDexCache();
return driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, &compilation_unit);
}
@@ -1281,10 +1272,8 @@
bool HInstructionBuilder::IsOutermostCompilingClass(uint16_t type_index) const {
ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<4> hs(soa.Self());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->FindDexCache(
- soa.Self(), *dex_compilation_unit_->GetDexFile())));
+ StackHandleScope<3> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
Handle<mirror::Class> cls(hs.NewHandle(compiler_driver_->ResolveClass(
@@ -1324,10 +1313,8 @@
uint16_t field_index = instruction.VRegB_21c();
ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<5> hs(soa.Self());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->FindDexCache(
- soa.Self(), *dex_compilation_unit_->GetDexFile())));
+ StackHandleScope<3> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
ArtField* resolved_field = compiler_driver_->ResolveField(
@@ -1342,8 +1329,7 @@
Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
- Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
- outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file)));
+ Handle<mirror::DexCache> outer_dex_cache = outer_compilation_unit_->GetDexCache();
Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
// The index at which the field's class is stored in the DexCache's type array.
@@ -1371,7 +1357,7 @@
}
bool is_in_cache =
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, storage_index);
+ compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_cache, storage_index);
HLoadClass* constant = new (arena_) HLoadClass(graph_->GetCurrentMethod(),
storage_index,
outer_dex_file,
@@ -1634,21 +1620,16 @@
uint8_t reference,
uint16_t type_index,
uint32_t dex_pc) {
- bool type_known_final, type_known_abstract, use_declaring_class;
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
+ Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
+ Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index)));
+
bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
dex_compilation_unit_->GetDexMethodIndex(),
- *dex_compilation_unit_->GetDexFile(),
- type_index,
- &type_known_final,
- &type_known_abstract,
- &use_declaring_class);
-
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<2> hs(soa.Self());
- const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), dex_file)));
- Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index)));
+ dex_cache,
+ type_index);
HInstruction* object = LoadLocal(reference, Primitive::kPrimNot);
HLoadClass* cls = new (arena_) HLoadClass(
@@ -1658,7 +1639,7 @@
IsOutermostCompilingClass(type_index),
dex_pc,
!can_access,
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(dex_file, type_index));
+ compiler_driver_->CanAssumeTypeIsPresentInDexCache(dex_cache, type_index));
AppendInstruction(cls);
TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class);
@@ -1676,9 +1657,17 @@
}
}
-bool HInstructionBuilder::NeedsAccessCheck(uint32_t type_index, bool* finalizable) const {
+bool HInstructionBuilder::NeedsAccessCheck(uint32_t type_index,
+ Handle<mirror::DexCache> dex_cache,
+ bool* finalizable) const {
return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks(
- dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index, finalizable);
+ dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index, finalizable);
+}
+
+bool HInstructionBuilder::NeedsAccessCheck(uint32_t type_index, bool* finalizable) const {
+ ScopedObjectAccess soa(Thread::Current());
+ Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
+ return NeedsAccessCheck(type_index, dex_cache, finalizable);
}
bool HInstructionBuilder::CanDecodeQuickenedInfo() const {
@@ -2612,16 +2601,16 @@
case Instruction::CONST_CLASS: {
uint16_t type_index = instruction.VRegB_21c();
- bool type_known_final;
- bool type_known_abstract;
- bool dont_use_is_referrers_class;
// `CanAccessTypeWithoutChecks` will tell whether the method being
// built is trying to access its own class, so that the generated
// code can optimize for this case. However, the optimization does not
// work for inlining, so we use `IsOutermostCompilingClass` instead.
+ ScopedObjectAccess soa(Thread::Current());
+ Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
- dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
- &type_known_final, &type_known_abstract, &dont_use_is_referrers_class);
+ dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index);
+ bool is_in_dex_cache =
+ compiler_driver_->CanAssumeTypeIsPresentInDexCache(dex_cache, type_index);
AppendInstruction(new (arena_) HLoadClass(
graph_->GetCurrentMethod(),
type_index,
@@ -2629,7 +2618,7 @@
IsOutermostCompilingClass(type_index),
dex_pc,
!can_access,
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(*dex_file_, type_index)));
+ is_in_dex_cache));
UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
break;
}
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index f480b70..070f7da 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -97,6 +97,10 @@
// Returns whether the current method needs access check for the type.
// Output parameter finalizable is set to whether the type is finalizable.
+ bool NeedsAccessCheck(uint32_t type_index,
+ Handle<mirror::DexCache> dex_cache,
+ /*out*/bool* finalizable) const
+ SHARED_REQUIRES(Locks::mutator_lock_);
bool NeedsAccessCheck(uint32_t type_index, /*out*/bool* finalizable) const;
template<typename T>
diff --git a/runtime/Android.mk b/runtime/Android.mk
index c859079..aa12c83 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -106,7 +106,6 @@
jit/debugger_interface.cc \
jit/jit.cc \
jit/jit_code_cache.cc \
- jit/jit_instrumentation.cc \
jit/offline_profiling_info.cc \
jit/profiling_info.cc \
jit/profile_saver.cc \
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index d27d2f6..21725d3 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -20,7 +20,7 @@
#if defined(__cplusplus)
#include "art_method.h"
#include "gc/allocator/rosalloc.h"
-#include "jit/jit_instrumentation.h"
+#include "jit/jit.h"
#include "lock_word.h"
#include "mirror/class.h"
#include "mirror/string.h"
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index baf4afe..a432782 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -285,16 +285,19 @@
}
jit::Jit* jit = Runtime::Current()->GetJit();
- if (jit != nullptr && jit->CanInvokeCompiledCode(method)) {
- JValue result;
+ if (jit != nullptr) {
+ jit->MethodEntered(self, shadow_frame.GetMethod());
+ if (jit->CanInvokeCompiledCode(method)) {
+ JValue result;
- // Pop the shadow frame before calling into compiled code.
- self->PopShadowFrame();
- ArtInterpreterToCompiledCodeBridge(self, code_item, &shadow_frame, &result);
- // Push the shadow frame back as the caller will expect it.
- self->PushShadowFrame(&shadow_frame);
+ // Pop the shadow frame before calling into compiled code.
+ self->PopShadowFrame();
+ ArtInterpreterToCompiledCodeBridge(self, code_item, &shadow_frame, &result);
+ // Push the shadow frame back as the caller will expect it.
+ self->PushShadowFrame(&shadow_frame);
- return result;
+ return result;
+ }
}
}
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 19d971e..fb98175 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -34,6 +34,7 @@
#include "dex_instruction-inl.h"
#include "entrypoints/entrypoint_utils-inl.h"
#include "handle_scope-inl.h"
+#include "jit/jit.h"
#include "lambda/art_lambda_method.h"
#include "lambda/box_table.h"
#include "lambda/closure.h"
@@ -628,6 +629,15 @@
result->SetJ(0);
return false;
} else {
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr) {
+ if (type == kVirtual || type == kInterface) {
+ jit->InvokeVirtualOrInterface(
+ self, receiver, sf_method, shadow_frame.GetDexPC(), called_method);
+ }
+ jit->AddSamples(self, sf_method, 1);
+ }
+ // TODO: Remove the InvokeVirtualOrInterface instrumentation, as it was only used by the JIT.
if (type == kVirtual || type == kInterface) {
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasInvokeVirtualOrInterfaceListeners())) {
@@ -667,7 +677,14 @@
result->SetJ(0);
return false;
} else {
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr) {
+ jit->InvokeVirtualOrInterface(
+ self, receiver, shadow_frame.GetMethod(), shadow_frame.GetDexPC(), called_method);
+ jit->AddSamples(self, shadow_frame.GetMethod(), 1);
+ }
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ // TODO: Remove the InvokeVirtualOrInterface instrumentation, as it was only used by the JIT.
if (UNLIKELY(instrumentation->HasInvokeVirtualOrInterfaceListeners())) {
instrumentation->InvokeVirtualOrInterface(
self, receiver, shadow_frame.GetMethod(), shadow_frame.GetDexPC(), called_method);
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index ce698fb..c95af6f 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -22,7 +22,6 @@
#include "experimental_flags.h"
#include "interpreter_common.h"
#include "jit/jit.h"
-#include "jit/jit_instrumentation.h"
#include "safe_math.h"
#include <memory> // std::unique_ptr
@@ -67,7 +66,9 @@
#define BRANCH_INSTRUMENTATION(offset) \
do { \
- instrumentation->Branch(self, method, dex_pc, offset); \
+ if (UNLIKELY(instrumentation->HasBranchListeners())) { \
+ instrumentation->Branch(self, method, dex_pc, offset); \
+ } \
JValue result; \
if (jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, &result)) { \
return result; \
@@ -76,8 +77,8 @@
#define HOTNESS_UPDATE() \
do { \
- if (jit_instrumentation_cache != nullptr) { \
- jit_instrumentation_cache->AddSamples(self, method, 1); \
+ if (jit != nullptr) { \
+ jit->AddSamples(self, method, 1); \
} \
} while (false)
@@ -195,10 +196,6 @@
const auto* const instrumentation = Runtime::Current()->GetInstrumentation();
ArtMethod* method = shadow_frame.GetMethod();
jit::Jit* jit = Runtime::Current()->GetJit();
- jit::JitInstrumentationCache* jit_instrumentation_cache = nullptr;
- if (jit != nullptr) {
- jit_instrumentation_cache = jit->GetInstrumentationCache();
- }
// Jump to first instruction.
ADVANCE(0);
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 442e191..ca1d635 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -18,7 +18,6 @@
#include "experimental_flags.h"
#include "interpreter_common.h"
#include "jit/jit.h"
-#include "jit/jit_instrumentation.h"
#include "safe_math.h"
#include <memory> // std::unique_ptr
@@ -74,7 +73,9 @@
#define BRANCH_INSTRUMENTATION(offset) \
do { \
- instrumentation->Branch(self, method, dex_pc, offset); \
+ if (UNLIKELY(instrumentation->HasBranchListeners())) { \
+ instrumentation->Branch(self, method, dex_pc, offset); \
+ } \
JValue result; \
if (jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, &result)) { \
if (interpret_one_instruction) { \
@@ -87,8 +88,8 @@
#define HOTNESS_UPDATE() \
do { \
- if (jit_instrumentation_cache != nullptr) { \
- jit_instrumentation_cache->AddSamples(self, method, 1); \
+ if (jit != nullptr) { \
+ jit->AddSamples(self, method, 1); \
} \
} while (false)
@@ -115,10 +116,6 @@
uint16_t inst_data;
ArtMethod* method = shadow_frame.GetMethod();
jit::Jit* jit = Runtime::Current()->GetJit();
- jit::JitInstrumentationCache* jit_instrumentation_cache = nullptr;
- if (jit != nullptr) {
- jit_instrumentation_cache = jit->GetInstrumentationCache();
- }
// TODO: collapse capture-variable+create-lambda into one opcode, then we won't need
// to keep this live for the scope of the entire function call.
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index 32c45fc..f800683 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -20,8 +20,6 @@
#include "interpreter/interpreter_common.h"
#include "entrypoints/entrypoint_utils-inl.h"
#include "mterp.h"
-#include "jit/jit.h"
-#include "jit/jit_instrumentation.h"
#include "debugger.h"
namespace art {
@@ -652,10 +650,9 @@
int32_t countdown_value = jit::kJitHotnessDisabled;
jit::Jit* jit = Runtime::Current()->GetJit();
if (jit != nullptr) {
- jit::JitInstrumentationCache* cache = jit->GetInstrumentationCache();
- int32_t warm_threshold = cache->WarmMethodThreshold();
- int32_t hot_threshold = cache->HotMethodThreshold();
- int32_t osr_threshold = cache->OSRMethodThreshold();
+ int32_t warm_threshold = jit->WarmMethodThreshold();
+ int32_t hot_threshold = jit->HotMethodThreshold();
+ int32_t osr_threshold = jit->OSRMethodThreshold();
if (hotness_count < warm_threshold) {
countdown_value = warm_threshold - hotness_count;
} else if (hotness_count < hot_threshold) {
@@ -666,7 +663,7 @@
countdown_value = jit::kJitCheckForOSR;
}
if (jit::Jit::ShouldUsePriorityThreadWeight()) {
- int32_t priority_thread_weight = cache->PriorityThreadWeight();
+ int32_t priority_thread_weight = jit->PriorityThreadWeight();
countdown_value = std::min(countdown_value, countdown_value / priority_thread_weight);
}
}
@@ -692,7 +689,7 @@
jit::Jit* jit = Runtime::Current()->GetJit();
if (jit != nullptr) {
int16_t count = shadow_frame->GetCachedHotnessCountdown() - shadow_frame->GetHotnessCountdown();
- jit->GetInstrumentationCache()->AddSamples(self, method, count);
+ jit->AddSamples(self, method, count);
}
return MterpSetUpHotnessCountdown(method, shadow_frame);
}
@@ -705,7 +702,7 @@
uint32_t dex_pc = shadow_frame->GetDexPC();
jit::Jit* jit = Runtime::Current()->GetJit();
if ((jit != nullptr) && (offset <= 0)) {
- jit->GetInstrumentationCache()->AddSamples(self, method, 1);
+ jit->AddSamples(self, method, 1);
}
int16_t countdown_value = MterpSetUpHotnessCountdown(method, shadow_frame);
if (countdown_value == jit::kJitCheckForOSR) {
@@ -725,7 +722,7 @@
jit::Jit* jit = Runtime::Current()->GetJit();
if (offset <= 0) {
// Keep updating hotness in case a compilation request was dropped. Eventually it will retry.
- jit->GetInstrumentationCache()->AddSamples(self, method, 1);
+ jit->AddSamples(self, method, 1);
}
// Assumes caller has already determined that an OSR check is appropriate.
return jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, result);
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 3344346..4ba90c1 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -23,7 +23,6 @@
#include "entrypoints/runtime_asm_entrypoints.h"
#include "interpreter/interpreter.h"
#include "jit_code_cache.h"
-#include "jit_instrumentation.h"
#include "oat_file_manager.h"
#include "oat_quick_method_header.h"
#include "offline_profiling_info.h"
@@ -31,12 +30,15 @@
#include "runtime.h"
#include "runtime_options.h"
#include "stack_map.h"
+#include "thread_list.h"
#include "utils.h"
namespace art {
namespace jit {
static constexpr bool kEnableOnStackReplacement = true;
+// At what priority to schedule jit threads. 9 is the lowest foreground priority on device.
+static constexpr int kJitPoolThreadPthreadPriority = 9;
// JIT compiler
void* Jit::jit_library_handle_= nullptr;
@@ -146,6 +148,16 @@
<< ", max_capacity=" << PrettySize(options->GetCodeCacheMaxCapacity())
<< ", compile_threshold=" << options->GetCompileThreshold()
<< ", save_profiling_info=" << options->GetSaveProfilingInfo();
+
+
+ jit->hot_method_threshold_ = options->GetCompileThreshold();
+ jit->warm_method_threshold_ = options->GetWarmupThreshold();
+ jit->osr_method_threshold_ = options->GetOsrThreshold();
+
+ jit->CreateThreadPool();
+
+ // Notify native debugger about the classes already loaded before the creation of the jit.
+ jit->DumpTypeInfoForLoadedTypes(Runtime::Current()->GetClassLinker());
return jit.release();
}
@@ -233,13 +245,31 @@
}
void Jit::CreateThreadPool() {
- CHECK(instrumentation_cache_.get() != nullptr);
- instrumentation_cache_->CreateThreadPool();
+ // There is a DCHECK in the 'AddSamples' method to ensure the tread pool
+ // is not null when we instrument.
+ thread_pool_.reset(new ThreadPool("Jit thread pool", 1));
+ thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority);
+ thread_pool_->StartWorkers(Thread::Current());
}
void Jit::DeleteThreadPool() {
- if (instrumentation_cache_.get() != nullptr) {
- instrumentation_cache_->DeleteThreadPool(Thread::Current());
+ Thread* self = Thread::Current();
+ DCHECK(Runtime::Current()->IsShuttingDown(self));
+ if (thread_pool_ != nullptr) {
+ ThreadPool* cache = nullptr;
+ {
+ ScopedSuspendAll ssa(__FUNCTION__);
+ // Clear thread_pool_ field while the threads are suspended.
+ // A mutator in the 'AddSamples' method will check against it.
+ cache = thread_pool_.release();
+ }
+ cache->StopWorkers(self);
+ cache->RemoveAllTasks(self);
+ // We could just suspend all threads, but we know those threads
+ // will finish in a short period, so it's not worth adding a suspend logic
+ // here. Besides, this is only done for shutdown.
+ cache->Wait(self, false, false);
+ delete cache;
}
}
@@ -259,10 +289,7 @@
}
bool Jit::JitAtFirstUse() {
- if (instrumentation_cache_ != nullptr) {
- return instrumentation_cache_->HotMethodThreshold() == 0;
- }
- return false;
+ return HotMethodThreshold() == 0;
}
bool Jit::CanInvokeCompiledCode(ArtMethod* method) {
@@ -285,17 +312,6 @@
}
}
-void Jit::CreateInstrumentationCache(size_t compile_threshold,
- size_t warmup_threshold,
- size_t osr_threshold,
- uint16_t priority_thread_weight) {
- instrumentation_cache_.reset(
- new jit::JitInstrumentationCache(compile_threshold,
- warmup_threshold,
- osr_threshold,
- priority_thread_weight));
-}
-
void Jit::NewTypeLoadedIfUsingJit(mirror::Class* type) {
jit::Jit* jit = Runtime::Current()->GetJit();
if (jit != nullptr && jit->generate_debug_info_) {
@@ -480,5 +496,164 @@
memory_use_.AddValue(bytes);
}
+class JitCompileTask FINAL : public Task {
+ public:
+ enum TaskKind {
+ kAllocateProfile,
+ kCompile,
+ kCompileOsr
+ };
+
+ JitCompileTask(ArtMethod* method, TaskKind kind) : method_(method), kind_(kind) {
+ ScopedObjectAccess soa(Thread::Current());
+ // Add a global ref to the class to prevent class unloading until compilation is done.
+ klass_ = soa.Vm()->AddGlobalRef(soa.Self(), method_->GetDeclaringClass());
+ CHECK(klass_ != nullptr);
+ }
+
+ ~JitCompileTask() {
+ ScopedObjectAccess soa(Thread::Current());
+ soa.Vm()->DeleteGlobalRef(soa.Self(), klass_);
+ }
+
+ void Run(Thread* self) OVERRIDE {
+ ScopedObjectAccess soa(self);
+ if (kind_ == kCompile) {
+ VLOG(jit) << "JitCompileTask compiling method " << PrettyMethod(method_);
+ if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ false)) {
+ VLOG(jit) << "Failed to compile method " << PrettyMethod(method_);
+ }
+ } else if (kind_ == kCompileOsr) {
+ VLOG(jit) << "JitCompileTask compiling method osr " << PrettyMethod(method_);
+ if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ true)) {
+ VLOG(jit) << "Failed to compile method osr " << PrettyMethod(method_);
+ }
+ } else {
+ DCHECK(kind_ == kAllocateProfile);
+ if (ProfilingInfo::Create(self, method_, /* retry_allocation */ true)) {
+ VLOG(jit) << "Start profiling " << PrettyMethod(method_);
+ }
+ }
+ }
+
+ void Finalize() OVERRIDE {
+ delete this;
+ }
+
+ private:
+ ArtMethod* const method_;
+ const TaskKind kind_;
+ jobject klass_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask);
+};
+
+void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count) {
+ if (thread_pool_ == nullptr) {
+ // Should only see this when shutting down.
+ DCHECK(Runtime::Current()->IsShuttingDown(self));
+ return;
+ }
+
+ if (method->IsClassInitializer() || method->IsNative()) {
+ // We do not want to compile such methods.
+ return;
+ }
+ DCHECK(thread_pool_ != nullptr);
+ DCHECK_GT(warm_method_threshold_, 0);
+ DCHECK_GT(hot_method_threshold_, warm_method_threshold_);
+ DCHECK_GT(osr_method_threshold_, hot_method_threshold_);
+ DCHECK_GE(priority_thread_weight_, 1);
+ DCHECK_LE(priority_thread_weight_, hot_method_threshold_);
+
+ int32_t starting_count = method->GetCounter();
+ if (Jit::ShouldUsePriorityThreadWeight()) {
+ count *= priority_thread_weight_;
+ }
+ int32_t new_count = starting_count + count; // int32 here to avoid wrap-around;
+ if (starting_count < warm_method_threshold_) {
+ if (new_count >= warm_method_threshold_) {
+ bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false);
+ if (success) {
+ VLOG(jit) << "Start profiling " << PrettyMethod(method);
+ }
+
+ if (thread_pool_ == nullptr) {
+ // Calling ProfilingInfo::Create might put us in a suspended state, which could
+ // lead to the thread pool being deleted when we are shutting down.
+ DCHECK(Runtime::Current()->IsShuttingDown(self));
+ return;
+ }
+
+ if (!success) {
+ // We failed allocating. Instead of doing the collection on the Java thread, we push
+ // an allocation to a compiler thread, that will do the collection.
+ thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kAllocateProfile));
+ }
+ }
+ // Avoid jumping more than one state at a time.
+ new_count = std::min(new_count, hot_method_threshold_ - 1);
+ } else if (starting_count < hot_method_threshold_) {
+ if (new_count >= hot_method_threshold_) {
+ DCHECK(thread_pool_ != nullptr);
+ thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
+ }
+ // Avoid jumping more than one state at a time.
+ new_count = std::min(new_count, osr_method_threshold_ - 1);
+ } else if (starting_count < osr_method_threshold_) {
+ if (new_count >= osr_method_threshold_) {
+ DCHECK(thread_pool_ != nullptr);
+ thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr));
+ }
+ }
+ // Update hotness counter
+ method->SetCounter(new_count);
+}
+
+void Jit::MethodEntered(Thread* thread, ArtMethod* method) {
+ if (UNLIKELY(Runtime::Current()->GetJit()->JitAtFirstUse())) {
+ // The compiler requires a ProfilingInfo object.
+ ProfilingInfo::Create(thread, method, /* retry_allocation */ true);
+ JitCompileTask compile_task(method, JitCompileTask::kCompile);
+ compile_task.Run(thread);
+ return;
+ }
+
+ ProfilingInfo* profiling_info = method->GetProfilingInfo(sizeof(void*));
+ // Update the entrypoint if the ProfilingInfo has one. The interpreter will call it
+ // instead of interpreting the method.
+ // We avoid doing this if exit stubs are installed to not mess with the instrumentation.
+ // TODO(ngeoffray): Clean up instrumentation and code cache interactions.
+ if ((profiling_info != nullptr) &&
+ (profiling_info->GetSavedEntryPoint() != nullptr) &&
+ !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
+ method->SetEntryPointFromQuickCompiledCode(profiling_info->GetSavedEntryPoint());
+ } else {
+ AddSamples(thread, method, 1);
+ }
+}
+
+void Jit::InvokeVirtualOrInterface(Thread* thread,
+ mirror::Object* this_object,
+ ArtMethod* caller,
+ uint32_t dex_pc,
+ ArtMethod* callee ATTRIBUTE_UNUSED) {
+ ScopedAssertNoThreadSuspension ants(thread, __FUNCTION__);
+ DCHECK(this_object != nullptr);
+ ProfilingInfo* info = caller->GetProfilingInfo(sizeof(void*));
+ if (info != nullptr) {
+ // Since the instrumentation is marked from the declaring class we need to mark the card so
+ // that mod-union tables and card rescanning know about the update.
+ Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(caller->GetDeclaringClass());
+ info->AddInvokeInfo(dex_pc, this_object->GetClass());
+ }
+}
+
+void Jit::WaitForCompilationToFinish(Thread* self) {
+ if (thread_pool_ != nullptr) {
+ thread_pool_->Wait(self, false, false);
+ }
+}
+
} // namespace jit
} // namespace art
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index e212366..96f9608 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -34,9 +34,11 @@
namespace jit {
class JitCodeCache;
-class JitInstrumentationCache;
class JitOptions;
+static constexpr int16_t kJitCheckForOSR = -1;
+static constexpr int16_t kJitHotnessDisabled = -2;
+
class Jit {
public:
static constexpr bool kStressMode = kIsDebugBuild;
@@ -46,17 +48,16 @@
static Jit* Create(JitOptions* options, std::string* error_msg);
bool CompileMethod(ArtMethod* method, Thread* self, bool osr)
SHARED_REQUIRES(Locks::mutator_lock_);
- void CreateInstrumentationCache(size_t compile_threshold,
- size_t warmup_threshold,
- size_t osr_threshold,
- uint16_t priority_thread_weight);
void CreateThreadPool();
+
const JitCodeCache* GetCodeCache() const {
return code_cache_.get();
}
+
JitCodeCache* GetCodeCache() {
return code_cache_.get();
}
+
void DeleteThreadPool();
// Dump interesting info: #methods compiled, code vs data size, compile / verify cumulative
// loggers.
@@ -68,10 +69,39 @@
REQUIRES(!lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
- JitInstrumentationCache* GetInstrumentationCache() const {
- return instrumentation_cache_.get();
+ size_t OSRMethodThreshold() const {
+ return osr_method_threshold_;
}
+ size_t HotMethodThreshold() const {
+ return hot_method_threshold_;
+ }
+
+ size_t WarmMethodThreshold() const {
+ return warm_method_threshold_;
+ }
+
+ uint16_t PriorityThreadWeight() const {
+ return priority_thread_weight_;
+ }
+
+ // Wait until there is no more pending compilation tasks.
+ void WaitForCompilationToFinish(Thread* self);
+
+ // Profiling methods.
+ void MethodEntered(Thread* thread, ArtMethod* method)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ void AddSamples(Thread* self, ArtMethod* method, uint16_t samples)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ void InvokeVirtualOrInterface(Thread* thread,
+ mirror::Object* this_object,
+ ArtMethod* caller,
+ uint32_t dex_pc,
+ ArtMethod* callee)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
// Starts the profile saver if the config options allow profile recording.
// The profile will be stored in the specified `filename` and will contain
// information collected from the given `code_paths` (a set of dex locations).
@@ -137,11 +167,15 @@
Histogram<uint64_t> memory_use_ GUARDED_BY(lock_);
Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- std::unique_ptr<jit::JitInstrumentationCache> instrumentation_cache_;
std::unique_ptr<jit::JitCodeCache> code_cache_;
bool save_profiling_info_;
static bool generate_debug_info_;
+ uint16_t hot_method_threshold_;
+ uint16_t warm_method_threshold_;
+ uint16_t osr_method_threshold_;
+ uint16_t priority_thread_weight_;
+ std::unique_ptr<ThreadPool> thread_pool_;
DISALLOW_COPY_AND_ASSIGN(Jit);
};
diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc
deleted file mode 100644
index b2c0c20..0000000
--- a/runtime/jit/jit_instrumentation.cc
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright 2014 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 "jit_instrumentation.h"
-
-#include "art_method-inl.h"
-#include "jit.h"
-#include "jit_code_cache.h"
-#include "scoped_thread_state_change.h"
-#include "thread_list.h"
-
-namespace art {
-namespace jit {
-
-// At what priority to schedule jit threads. 9 is the lowest foreground priority on device.
-static constexpr int kJitPoolThreadPthreadPriority = 9;
-
-class JitCompileTask FINAL : public Task {
- public:
- enum TaskKind {
- kAllocateProfile,
- kCompile,
- kCompileOsr
- };
-
- JitCompileTask(ArtMethod* method, TaskKind kind) : method_(method), kind_(kind) {
- ScopedObjectAccess soa(Thread::Current());
- // Add a global ref to the class to prevent class unloading until compilation is done.
- klass_ = soa.Vm()->AddGlobalRef(soa.Self(), method_->GetDeclaringClass());
- CHECK(klass_ != nullptr);
- }
-
- ~JitCompileTask() {
- ScopedObjectAccess soa(Thread::Current());
- soa.Vm()->DeleteGlobalRef(soa.Self(), klass_);
- }
-
- void Run(Thread* self) OVERRIDE {
- ScopedObjectAccess soa(self);
- if (kind_ == kCompile) {
- VLOG(jit) << "JitCompileTask compiling method " << PrettyMethod(method_);
- if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ false)) {
- VLOG(jit) << "Failed to compile method " << PrettyMethod(method_);
- }
- } else if (kind_ == kCompileOsr) {
- VLOG(jit) << "JitCompileTask compiling method osr " << PrettyMethod(method_);
- if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ true)) {
- VLOG(jit) << "Failed to compile method osr " << PrettyMethod(method_);
- }
- } else {
- DCHECK(kind_ == kAllocateProfile);
- if (ProfilingInfo::Create(self, method_, /* retry_allocation */ true)) {
- VLOG(jit) << "Start profiling " << PrettyMethod(method_);
- }
- }
- }
-
- void Finalize() OVERRIDE {
- delete this;
- }
-
- private:
- ArtMethod* const method_;
- const TaskKind kind_;
- jobject klass_;
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask);
-};
-
-JitInstrumentationCache::JitInstrumentationCache(uint16_t hot_method_threshold,
- uint16_t warm_method_threshold,
- uint16_t osr_method_threshold,
- uint16_t priority_thread_weight)
- : hot_method_threshold_(hot_method_threshold),
- warm_method_threshold_(warm_method_threshold),
- osr_method_threshold_(osr_method_threshold),
- priority_thread_weight_(priority_thread_weight),
- listener_(this) {
-}
-
-void JitInstrumentationCache::CreateThreadPool() {
- // Create the thread pool before setting the instrumentation, so that
- // when the threads stopped being suspended, they can use it directly.
- // There is a DCHECK in the 'AddSamples' method to ensure the tread pool
- // is not null when we instrument.
- thread_pool_.reset(new ThreadPool("Jit thread pool", 1));
- thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority);
- thread_pool_->StartWorkers(Thread::Current());
- {
- // Add Jit interpreter instrumentation, tells the interpreter when
- // to notify the jit to compile something.
- ScopedSuspendAll ssa(__FUNCTION__);
- Runtime::Current()->GetInstrumentation()->AddListener(
- &listener_, JitInstrumentationListener::kJitEvents);
- }
-}
-
-void JitInstrumentationCache::DeleteThreadPool(Thread* self) {
- DCHECK(Runtime::Current()->IsShuttingDown(self));
- if (thread_pool_ != nullptr) {
- // First remove the listener, to avoid having mutators enter
- // 'AddSamples'.
- ThreadPool* cache = nullptr;
- {
- ScopedSuspendAll ssa(__FUNCTION__);
- Runtime::Current()->GetInstrumentation()->RemoveListener(
- &listener_, JitInstrumentationListener::kJitEvents);
- // Clear thread_pool_ field while the threads are suspended.
- // A mutator in the 'AddSamples' method will check against it.
- cache = thread_pool_.release();
- }
- cache->StopWorkers(self);
- cache->RemoveAllTasks(self);
- // We could just suspend all threads, but we know those threads
- // will finish in a short period, so it's not worth adding a suspend logic
- // here. Besides, this is only done for shutdown.
- cache->Wait(self, false, false);
- delete cache;
- }
-}
-
-void JitInstrumentationCache::AddSamples(Thread* self, ArtMethod* method, uint16_t count) {
- // Since we don't have on-stack replacement, some methods can remain in the interpreter longer
- // than we want resulting in samples even after the method is compiled. Also, if the
- // jit is no longer interested in hotness samples because we're shutting down, just return.
- if (method->IsClassInitializer() || method->IsNative() || (thread_pool_ == nullptr)) {
- if (thread_pool_ == nullptr) {
- // Should only see this when shutting down.
- DCHECK(Runtime::Current()->IsShuttingDown(self));
- }
- return;
- }
- DCHECK(thread_pool_ != nullptr);
- DCHECK_GT(warm_method_threshold_, 0);
- DCHECK_GT(hot_method_threshold_, warm_method_threshold_);
- DCHECK_GT(osr_method_threshold_, hot_method_threshold_);
- DCHECK_GE(priority_thread_weight_, 1);
- DCHECK_LE(priority_thread_weight_, hot_method_threshold_);
-
- int32_t starting_count = method->GetCounter();
- if (Jit::ShouldUsePriorityThreadWeight()) {
- count *= priority_thread_weight_;
- }
- int32_t new_count = starting_count + count; // int32 here to avoid wrap-around;
- if (starting_count < warm_method_threshold_) {
- if (new_count >= warm_method_threshold_) {
- bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false);
- if (success) {
- VLOG(jit) << "Start profiling " << PrettyMethod(method);
- }
-
- if (thread_pool_ == nullptr) {
- // Calling ProfilingInfo::Create might put us in a suspended state, which could
- // lead to the thread pool being deleted when we are shutting down.
- DCHECK(Runtime::Current()->IsShuttingDown(self));
- return;
- }
-
- if (!success) {
- // We failed allocating. Instead of doing the collection on the Java thread, we push
- // an allocation to a compiler thread, that will do the collection.
- thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kAllocateProfile));
- }
- }
- // Avoid jumping more than one state at a time.
- new_count = std::min(new_count, hot_method_threshold_ - 1);
- } else if (starting_count < hot_method_threshold_) {
- if (new_count >= hot_method_threshold_) {
- DCHECK(thread_pool_ != nullptr);
- thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
- }
- // Avoid jumping more than one state at a time.
- new_count = std::min(new_count, osr_method_threshold_ - 1);
- } else if (starting_count < osr_method_threshold_) {
- if (new_count >= osr_method_threshold_) {
- DCHECK(thread_pool_ != nullptr);
- thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr));
- }
- }
- // Update hotness counter
- method->SetCounter(new_count);
-}
-
-JitInstrumentationListener::JitInstrumentationListener(JitInstrumentationCache* cache)
- : instrumentation_cache_(cache) {
- CHECK(instrumentation_cache_ != nullptr);
-}
-
-void JitInstrumentationListener::MethodEntered(Thread* thread,
- mirror::Object* /*this_object*/,
- ArtMethod* method,
- uint32_t /*dex_pc*/) {
- if (UNLIKELY(Runtime::Current()->GetJit()->JitAtFirstUse())) {
- // The compiler requires a ProfilingInfo object.
- ProfilingInfo::Create(thread, method, /* retry_allocation */ true);
- JitCompileTask compile_task(method, JitCompileTask::kCompile);
- compile_task.Run(thread);
- return;
- }
-
- ProfilingInfo* profiling_info = method->GetProfilingInfo(sizeof(void*));
- // Update the entrypoint if the ProfilingInfo has one. The interpreter will call it
- // instead of interpreting the method.
- // We avoid doing this if exit stubs are installed to not mess with the instrumentation.
- // TODO(ngeoffray): Clean up instrumentation and code cache interactions.
- if ((profiling_info != nullptr) &&
- (profiling_info->GetSavedEntryPoint() != nullptr) &&
- !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
- method->SetEntryPointFromQuickCompiledCode(profiling_info->GetSavedEntryPoint());
- } else {
- instrumentation_cache_->AddSamples(thread, method, 1);
- }
-}
-
-void JitInstrumentationListener::Branch(Thread* thread,
- ArtMethod* method,
- uint32_t dex_pc ATTRIBUTE_UNUSED,
- int32_t dex_pc_offset) {
- if (dex_pc_offset < 0) {
- // Increment method hotness if it is a backward branch.
- instrumentation_cache_->AddSamples(thread, method, 1);
- }
-}
-
-void JitInstrumentationListener::InvokeVirtualOrInterface(Thread* thread,
- mirror::Object* this_object,
- ArtMethod* caller,
- uint32_t dex_pc,
- ArtMethod* callee ATTRIBUTE_UNUSED) {
- // We make sure we cannot be suspended, as the profiling info can be concurrently deleted.
- instrumentation_cache_->AddSamples(thread, caller, 1);
- DCHECK(this_object != nullptr);
- ProfilingInfo* info = caller->GetProfilingInfo(sizeof(void*));
- if (info != nullptr) {
- // Since the instrumentation is marked from the declaring class we need to mark the card so
- // that mod-union tables and card rescanning know about the update.
- Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(caller->GetDeclaringClass());
- info->AddInvokeInfo(dex_pc, this_object->GetClass());
- }
-}
-
-void JitInstrumentationCache::WaitForCompilationToFinish(Thread* self) {
- if (thread_pool_ != nullptr) {
- thread_pool_->Wait(self, false, false);
- }
-}
-
-} // namespace jit
-} // namespace art
diff --git a/runtime/jit/jit_instrumentation.h b/runtime/jit/jit_instrumentation.h
deleted file mode 100644
index d0545f8..0000000
--- a/runtime/jit/jit_instrumentation.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright 2014 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.
- */
-
-#ifndef ART_RUNTIME_JIT_JIT_INSTRUMENTATION_H_
-#define ART_RUNTIME_JIT_JIT_INSTRUMENTATION_H_
-
-#include <unordered_map>
-
-#include "instrumentation.h"
-
-#include "atomic.h"
-#include "base/macros.h"
-#include "base/mutex.h"
-#include "gc_root.h"
-#include "jni.h"
-#include "object_callbacks.h"
-#include "thread_pool.h"
-
-namespace art {
-namespace mirror {
- class Object;
- class Throwable;
-} // namespace mirror
-class ArtField;
-class ArtMethod;
-union JValue;
-class Thread;
-
-namespace jit {
-static constexpr int16_t kJitCheckForOSR = -1;
-static constexpr int16_t kJitHotnessDisabled = -2;
-
-class JitInstrumentationCache;
-
-class JitInstrumentationListener : public instrumentation::InstrumentationListener {
- public:
- explicit JitInstrumentationListener(JitInstrumentationCache* cache);
-
- void MethodEntered(Thread* thread, mirror::Object* /*this_object*/,
- ArtMethod* method, uint32_t /*dex_pc*/)
- OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_);
-
- void MethodExited(Thread* /*thread*/, mirror::Object* /*this_object*/,
- ArtMethod* /*method*/, uint32_t /*dex_pc*/,
- const JValue& /*return_value*/)
- OVERRIDE { }
- void MethodUnwind(Thread* /*thread*/, mirror::Object* /*this_object*/,
- ArtMethod* /*method*/, uint32_t /*dex_pc*/) OVERRIDE { }
- void FieldRead(Thread* /*thread*/, mirror::Object* /*this_object*/,
- ArtMethod* /*method*/, uint32_t /*dex_pc*/,
- ArtField* /*field*/) OVERRIDE { }
- void FieldWritten(Thread* /*thread*/, mirror::Object* /*this_object*/,
- ArtMethod* /*method*/, uint32_t /*dex_pc*/,
- ArtField* /*field*/, const JValue& /*field_value*/)
- OVERRIDE { }
- void ExceptionCaught(Thread* /*thread*/,
- mirror::Throwable* /*exception_object*/) OVERRIDE { }
-
- void DexPcMoved(Thread* /*self*/, mirror::Object* /*this_object*/,
- ArtMethod* /*method*/, uint32_t /*new_dex_pc*/) OVERRIDE { }
-
- void Branch(Thread* thread, ArtMethod* method, uint32_t dex_pc, int32_t dex_pc_offset)
- OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_);
-
- void InvokeVirtualOrInterface(Thread* thread,
- mirror::Object* this_object,
- ArtMethod* caller,
- uint32_t dex_pc,
- ArtMethod* callee)
- OVERRIDE
- REQUIRES(Roles::uninterruptible_)
- SHARED_REQUIRES(Locks::mutator_lock_);
-
- static constexpr uint32_t kJitEvents =
- instrumentation::Instrumentation::kMethodEntered |
- instrumentation::Instrumentation::kInvokeVirtualOrInterface;
-
- private:
- JitInstrumentationCache* const instrumentation_cache_;
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(JitInstrumentationListener);
-};
-
-// Keeps track of which methods are hot.
-class JitInstrumentationCache {
- public:
- JitInstrumentationCache(uint16_t hot_method_threshold,
- uint16_t warm_method_threshold,
- uint16_t osr_method_threshold,
- uint16_t priority_thread_weight);
- void AddSamples(Thread* self, ArtMethod* method, uint16_t samples)
- SHARED_REQUIRES(Locks::mutator_lock_);
- void CreateThreadPool();
- void DeleteThreadPool(Thread* self);
-
- size_t OSRMethodThreshold() const {
- return osr_method_threshold_;
- }
-
- size_t HotMethodThreshold() const {
- return hot_method_threshold_;
- }
-
- size_t WarmMethodThreshold() const {
- return warm_method_threshold_;
- }
-
- size_t PriorityThreadWeight() const {
- return priority_thread_weight_;
- }
-
- // Wait until there is no more pending compilation tasks.
- void WaitForCompilationToFinish(Thread* self);
-
- private:
- uint16_t hot_method_threshold_;
- uint16_t warm_method_threshold_;
- uint16_t osr_method_threshold_;
- uint16_t priority_thread_weight_;
- JitInstrumentationListener listener_;
- std::unique_ptr<ThreadPool> thread_pool_;
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(JitInstrumentationCache);
-};
-
-} // namespace jit
-} // namespace art
-
-#endif // ART_RUNTIME_JIT_JIT_INSTRUMENTATION_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 37bb4c1..2489e45 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1921,16 +1921,7 @@
}
std::string error_msg;
jit_.reset(jit::Jit::Create(jit_options_.get(), &error_msg));
- if (jit_.get() != nullptr) {
- jit_->CreateInstrumentationCache(jit_options_->GetCompileThreshold(),
- jit_options_->GetWarmupThreshold(),
- jit_options_->GetOsrThreshold(),
- jit_options_->GetPriorityThreadWeight());
- jit_->CreateThreadPool();
-
- // Notify native debugger about the classes already loaded before the creation of the jit.
- jit_->DumpTypeInfoForLoadedTypes(GetClassLinker());
- } else {
+ if (jit_.get() == nullptr) {
LOG(WARNING) << "Failed to create JIT " << error_msg;
}
}
diff --git a/test/141-class-unload/jni_unload.cc b/test/141-class-unload/jni_unload.cc
index d913efe..bbbb0a6 100644
--- a/test/141-class-unload/jni_unload.cc
+++ b/test/141-class-unload/jni_unload.cc
@@ -19,7 +19,6 @@
#include <iostream>
#include "jit/jit.h"
-#include "jit/jit_instrumentation.h"
#include "runtime.h"
#include "thread-inl.h"
@@ -29,7 +28,7 @@
extern "C" JNIEXPORT void JNICALL Java_IntHolder_waitForCompilation(JNIEnv*, jclass) {
jit::Jit* jit = Runtime::Current()->GetJit();
if (jit != nullptr) {
- jit->GetInstrumentationCache()->WaitForCompilationToFinish(Thread::Current());
+ jit->WaitForCompilationToFinish(Thread::Current());
}
}