summaryrefslogtreecommitdiffstats
path: root/compiler/optimizing/builder.cc
diff options
context:
space:
mode:
authorRoland Levillain <rpl@google.com>2015-04-24 16:43:49 +0100
committerRoland Levillain <rpl@google.com>2015-04-24 16:43:49 +0100
commit4c0eb42259d790fddcd9978b66328dbb3ab65615 (patch)
tree9d1ac505dfd4d0225f479d860b72a58747c8f6ce /compiler/optimizing/builder.cc
parent223f2f5b2a20ca8246da1523494900a2424d5956 (diff)
downloadart-4c0eb42259d790fddcd9978b66328dbb3ab65615.zip
art-4c0eb42259d790fddcd9978b66328dbb3ab65615.tar.gz
art-4c0eb42259d790fddcd9978b66328dbb3ab65615.tar.bz2
Ensure inlined static calls perform clinit checks in Optimizing.
Calls to static methods have implicit class initialization (clinit) checks of the method's declaring class in Optimizing. However, when such a static call is inlined, the implicit clinit check vanishes, possibly leading to an incorrect behavior. To ensure that inlining static methods does not change the behavior of a program, add explicit class initialization checks (art::HClinitCheck) as well as load class instructions (art::HLoadClass) as last input of static calls (art::HInvokeStaticOrDirect) in Optimizing' control flow graphs, when the declaring class is reachable and not known to be already initialized. Then when considering the inlining of a static method call, proceed only if the method has no implicit clinit check requirement. The added explicit clinit checks are already removed by the art::PrepareForRegisterAllocation visitor. This CL also extends this visitor to turn explicit clinit checks from static invokes into implicit ones after the inlining step, by removing the added art::HLoadClass nodes mentioned hereinbefore. Change-Id: I9ba452b8bd09ae1fdd9a3797ef556e3e7e19c651
Diffstat (limited to 'compiler/optimizing/builder.cc')
-rw-r--r--compiler/optimizing/builder.cc88
1 files changed, 84 insertions, 4 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 818d671..bb20807 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -21,6 +21,7 @@
#include "class_linker.h"
#include "dex_file-inl.h"
#include "dex_instruction-inl.h"
+#include "dex/verified_method.h"
#include "driver/compiler_driver-inl.h"
#include "driver/compiler_options.h"
#include "mirror/class_loader.h"
@@ -587,7 +588,7 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction,
const char* descriptor = dex_file_->StringDataByIdx(proto_id.shorty_idx_);
Primitive::Type return_type = Primitive::GetType(descriptor[0]);
bool is_instance_call = invoke_type != kStatic;
- const size_t number_of_arguments = strlen(descriptor) - (is_instance_call ? 0 : 1);
+ size_t number_of_arguments = strlen(descriptor) - (is_instance_call ? 0 : 1);
MethodReference target_method(dex_file_, method_idx);
uintptr_t direct_code;
@@ -605,7 +606,15 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction,
}
DCHECK(optimized_invoke_type != kSuper);
+ // By default, consider that the called method implicitly requires
+ // an initialization check of its declaring method.
+ HInvokeStaticOrDirect::ClinitCheckRequirement clinit_check_requirement =
+ HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit;
+ // Potential class initialization check, in the case of a static method call.
+ HClinitCheck* clinit_check = nullptr;
+
HInvoke* invoke = nullptr;
+
if (optimized_invoke_type == kVirtual) {
invoke = new (arena_) HInvokeVirtual(
arena_, number_of_arguments, return_type, dex_pc, method_idx, table_index);
@@ -620,9 +629,75 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction,
bool is_recursive =
(target_method.dex_method_index == dex_compilation_unit_->GetDexMethodIndex());
DCHECK(!is_recursive || (target_method.dex_file == dex_compilation_unit_->GetDexFile()));
+
+ if (optimized_invoke_type == kStatic) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<4> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+ dex_compilation_unit_->GetClassLinker()->FindDexCache(
+ *dex_compilation_unit_->GetDexFile())));
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
+ mirror::ArtMethod* resolved_method = compiler_driver_->ResolveMethod(
+ soa, dex_cache, class_loader, dex_compilation_unit_, method_idx,
+ optimized_invoke_type);
+
+ if (resolved_method == nullptr) {
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedMethod);
+ return false;
+ }
+
+ const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
+ Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
+ outer_compilation_unit_->GetClassLinker()->FindDexCache(outer_dex_file)));
+ Handle<mirror::Class> referrer_class(hs.NewHandle(GetOutermostCompilingClass()));
+
+ // The index at which the method's class is stored in the DexCache's type array.
+ uint32_t storage_index = DexFile::kDexNoIndex;
+ bool is_referrer_class = (resolved_method->GetDeclaringClass() == referrer_class.Get());
+ if (is_referrer_class) {
+ storage_index = referrer_class->GetDexTypeIndex();
+ } else if (outer_dex_cache.Get() == dex_cache.Get()) {
+ // Get `storage_index` from IsClassOfStaticMethodAvailableToReferrer.
+ compiler_driver_->IsClassOfStaticMethodAvailableToReferrer(outer_dex_cache.Get(),
+ referrer_class.Get(),
+ resolved_method,
+ method_idx,
+ &storage_index);
+ }
+
+ if (is_referrer_class) {
+ // If the declaring class is the referrer class, no class
+ // initialization is needed before the static method call.
+ clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone;
+ } else if (storage_index != DexFile::kDexNoIndex) {
+ // If the method's class type index is available, check
+ // whether we should add an explicit class initialization
+ // check for its declaring class before the static method call.
+
+ // TODO: find out why this check is needed.
+ bool is_in_dex_cache = compiler_driver_->CanAssumeTypeIsPresentInDexCache(
+ *outer_compilation_unit_->GetDexFile(), storage_index);
+ bool is_initialized =
+ resolved_method->GetDeclaringClass()->IsInitialized() && is_in_dex_cache;
+
+ if (is_initialized) {
+ clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone;
+ } else {
+ clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit;
+ HLoadClass* load_class =
+ new (arena_) HLoadClass(storage_index, is_referrer_class, dex_pc);
+ current_block_->AddInstruction(load_class);
+ clinit_check = new (arena_) HClinitCheck(load_class, dex_pc);
+ current_block_->AddInstruction(clinit_check);
+ ++number_of_arguments;
+ }
+ }
+ }
+
invoke = new (arena_) HInvokeStaticOrDirect(
arena_, number_of_arguments, return_type, dex_pc, target_method.dex_method_index,
- is_recursive, invoke_type, optimized_invoke_type);
+ is_recursive, invoke_type, optimized_invoke_type, clinit_check_requirement);
}
size_t start_index = 0;
@@ -655,6 +730,12 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction,
}
}
+ if (clinit_check_requirement == HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit) {
+ // Add the class initialization check as last input of `invoke`.
+ DCHECK(clinit_check != nullptr);
+ invoke->SetArgumentAt(argument_index++, clinit_check);
+ }
+
DCHECK_EQ(argument_index, number_of_arguments);
current_block_->AddInstruction(invoke);
latest_result_ = invoke;
@@ -732,7 +813,6 @@ bool HGraphBuilder::IsOutermostCompilingClass(uint16_t type_index) const {
return compiling_class.Get() == cls.Get();
}
-
bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction,
uint32_t dex_pc,
bool is_put) {
@@ -764,7 +844,7 @@ bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction,
if (is_referrer_class) {
storage_index = referrer_class->GetDexTypeIndex();
} else if (outer_dex_cache.Get() != dex_cache.Get()) {
- // The compiler driver cannot currently understand multple dex caches involved. Just bailout.
+ // The compiler driver cannot currently understand multiple dex caches involved. Just bailout.
return false;
} else {
std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField(