diff options
author | 2016-02-16 18:42:15 +0000 | |
---|---|---|
committer | 2016-02-18 09:51:40 +0000 | |
commit | 98e6ce44c700abd9375fe17f0aa31fea1e1e938b (patch) | |
tree | aa15b4398290918e5eb5227781b5aef5ecff9e0b | |
parent | a1f65135cd2315159ac302f904ba0c5ba0d7fd0e (diff) |
Remove string init map.
Partial revert of the String init change.
- Make Quick bailout in the presence of String allocation.
- Rely on the compiler for knowing when dex registers alias.
bug:27173201
Change-Id: I0bf58ba3825c71cef110b53f3a0a6f567cb2ef9a
-rw-r--r-- | compiler/dex/mir_optimization.cc | 45 | ||||
-rw-r--r-- | compiler/dex/quick/quick_compiler.cc | 12 | ||||
-rw-r--r-- | compiler/dex/quick/quick_compiler.h | 2 | ||||
-rw-r--r-- | compiler/dex/verified_method.cc | 10 | ||||
-rw-r--r-- | compiler/dex/verified_method.h | 12 | ||||
-rw-r--r-- | compiler/optimizing/ssa_builder.cc | 30 | ||||
-rw-r--r-- | runtime/base/mutex.cc | 4 | ||||
-rw-r--r-- | runtime/base/mutex.h | 1 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_common.cc | 48 | ||||
-rw-r--r-- | runtime/runtime.h | 8 | ||||
-rw-r--r-- | runtime/verifier/method_verifier.cc | 20 | ||||
-rw-r--r-- | runtime/verifier/method_verifier.h | 12 | ||||
-rw-r--r-- | runtime/verifier/register_line.cc | 13 | ||||
-rw-r--r-- | runtime/verifier/register_line.h | 5 | ||||
-rw-r--r-- | test/575-checker-string-init-alias/expected.txt | 1 | ||||
-rw-r--r-- | test/575-checker-string-init-alias/info.txt | 2 | ||||
-rw-r--r-- | test/575-checker-string-init-alias/smali/TestCase.smali | 72 | ||||
-rw-r--r-- | test/575-checker-string-init-alias/src/Main.java | 68 |
18 files changed, 207 insertions, 158 deletions
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index eb4915b821..6f9dd6d268 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -1679,9 +1679,7 @@ void MIRGraph::StringChange() { if (opcode == Instruction::NEW_INSTANCE) { uint32_t type_idx = mir->dalvikInsn.vB; if (cu_->compiler_driver->IsStringTypeIndex(type_idx, cu_->dex_file)) { - // Change NEW_INSTANCE into CONST_4 of 0 - mir->dalvikInsn.opcode = Instruction::CONST_4; - mir->dalvikInsn.vB = 0; + LOG(FATAL) << "Quick cannot compile String allocations"; } } else if ((opcode == Instruction::INVOKE_DIRECT) || (opcode == Instruction::INVOKE_DIRECT_RANGE)) { @@ -1689,52 +1687,13 @@ void MIRGraph::StringChange() { DexFileMethodInliner* inliner = cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file); if (inliner->IsStringInitMethodIndex(method_idx)) { - bool is_range = (opcode == Instruction::INVOKE_DIRECT_RANGE); - uint32_t orig_this_reg = is_range ? mir->dalvikInsn.vC : mir->dalvikInsn.arg[0]; - // Remove this pointer from string init and change to static call. - mir->dalvikInsn.vA--; - if (!is_range) { - mir->dalvikInsn.opcode = Instruction::INVOKE_STATIC; - for (uint32_t i = 0; i < mir->dalvikInsn.vA; i++) { - mir->dalvikInsn.arg[i] = mir->dalvikInsn.arg[i + 1]; - } - } else { - mir->dalvikInsn.opcode = Instruction::INVOKE_STATIC_RANGE; - mir->dalvikInsn.vC++; - } - // Insert a move-result instruction to the original this pointer reg. - MIR* move_result_mir = static_cast<MIR *>(arena_->Alloc(sizeof(MIR), kArenaAllocMIR)); - move_result_mir->dalvikInsn.opcode = Instruction::MOVE_RESULT_OBJECT; - move_result_mir->dalvikInsn.vA = orig_this_reg; - move_result_mir->offset = mir->offset; - move_result_mir->m_unit_index = mir->m_unit_index; - bb->InsertMIRAfter(mir, move_result_mir); - // Add additional moves if this pointer was copied to other registers. - const VerifiedMethod* verified_method = - cu_->compiler_driver->GetVerifiedMethod(cu_->dex_file, cu_->method_idx); - DCHECK(verified_method != nullptr); - const SafeMap<uint32_t, std::set<uint32_t>>& string_init_map = - verified_method->GetStringInitPcRegMap(); - auto map_it = string_init_map.find(mir->offset); - if (map_it != string_init_map.end()) { - const std::set<uint32_t>& reg_set = map_it->second; - for (auto set_it = reg_set.begin(); set_it != reg_set.end(); ++set_it) { - MIR* move_mir = static_cast<MIR *>(arena_->Alloc(sizeof(MIR), kArenaAllocMIR)); - move_mir->dalvikInsn.opcode = Instruction::MOVE_OBJECT; - move_mir->dalvikInsn.vA = *set_it; - move_mir->dalvikInsn.vB = orig_this_reg; - move_mir->offset = mir->offset; - move_mir->m_unit_index = mir->m_unit_index; - bb->InsertMIRAfter(move_result_mir, move_mir); - } - } + LOG(FATAL) << "Quick cannot compile String allocations"; } } } } } - bool MIRGraph::EliminateSuspendChecksGate() { if (kLeafOptimization || // Incompatible (could create loops without suspend checks). (cu_->disable_opt & (1 << kSuspendCheckElimination)) != 0 || // Disabled. diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc index 027290f9b1..49768ded46 100644 --- a/compiler/dex/quick/quick_compiler.cc +++ b/compiler/dex/quick/quick_compiler.cc @@ -509,7 +509,8 @@ static bool CanCompileShorty(const char* shorty, InstructionSet instruction_set) } bool QuickCompiler::CanCompileInstruction(const MIR* mir, - const DexFile& dex_file) const { + const DexFile& dex_file, + CompilationUnit* cu) const { switch (mir->dalvikInsn.opcode) { // Quick compiler won't support new instruction semantics to invoke-super into an interface // method @@ -522,6 +523,13 @@ bool QuickCompiler::CanCompileInstruction(const MIR* mir, // False if we are an interface i.e. !(java_access_flags & kAccInterface) return class_def != nullptr && ((class_def->GetJavaAccessFlags() & kAccInterface) == 0); } + case Instruction::NEW_INSTANCE: { + uint32_t type_idx = mir->dalvikInsn.vB; + if (cu->compiler_driver->IsStringTypeIndex(type_idx, cu->dex_file)) { + return false; + } + return true; + } default: return true; } @@ -567,7 +575,7 @@ bool QuickCompiler::CanCompileMethod(uint32_t method_idx, << MIRGraph::extended_mir_op_names_[opcode - kMirOpFirst]; } return false; - } else if (!CanCompileInstruction(mir, dex_file)) { + } else if (!CanCompileInstruction(mir, dex_file, cu)) { VLOG(compiler) << "Cannot compile dalvik opcode : " << mir->dalvikInsn.opcode; return false; } diff --git a/compiler/dex/quick/quick_compiler.h b/compiler/dex/quick/quick_compiler.h index 55f45f1ab0..f32cf866ca 100644 --- a/compiler/dex/quick/quick_compiler.h +++ b/compiler/dex/quick/quick_compiler.h @@ -75,7 +75,7 @@ class QuickCompiler : public Compiler { explicit QuickCompiler(CompilerDriver* driver); private: - bool CanCompileInstruction(const MIR* mir, const DexFile& dex_file) const; + bool CanCompileInstruction(const MIR* mir, const DexFile& dex_file, CompilationUnit* cu) const; std::unique_ptr<PassManager> pre_opt_pass_manager_; std::unique_ptr<PassManager> post_opt_pass_manager_; diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc index 0355f116f1..9ae21648bf 100644 --- a/compiler/dex/verified_method.cc +++ b/compiler/dex/verified_method.cc @@ -37,20 +37,16 @@ namespace art { -VerifiedMethod::VerifiedMethod(uint32_t encountered_error_types, - bool has_runtime_throw, - const SafeMap<uint32_t, std::set<uint32_t>>& string_init_pc_reg_map) +VerifiedMethod::VerifiedMethod(uint32_t encountered_error_types, bool has_runtime_throw) : encountered_error_types_(encountered_error_types), - has_runtime_throw_(has_runtime_throw), - string_init_pc_reg_map_(string_init_pc_reg_map) { + has_runtime_throw_(has_runtime_throw) { } const VerifiedMethod* VerifiedMethod::Create(verifier::MethodVerifier* method_verifier, bool compile) { std::unique_ptr<VerifiedMethod> verified_method( new VerifiedMethod(method_verifier->GetEncounteredFailureTypes(), - method_verifier->HasInstructionThatWillThrow(), - method_verifier->GetStringInitPcRegMap())); + method_verifier->HasInstructionThatWillThrow())); if (compile) { /* Generate a register map. */ diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h index 74fcb07d27..12d0219058 100644 --- a/compiler/dex/verified_method.h +++ b/compiler/dex/verified_method.h @@ -83,14 +83,8 @@ class VerifiedMethod { return has_runtime_throw_; } - const SafeMap<uint32_t, std::set<uint32_t>>& GetStringInitPcRegMap() const { - return string_init_pc_reg_map_; - } - private: - VerifiedMethod(uint32_t encountered_error_types, - bool has_runtime_throw, - const SafeMap<uint32_t, std::set<uint32_t>>& string_init_pc_reg_map); + VerifiedMethod(uint32_t encountered_error_types, bool has_runtime_throw); /* * Generate the GC map for a method that has just been verified (i.e. we're doing this as part of @@ -129,10 +123,6 @@ class VerifiedMethod { const uint32_t encountered_error_types_; const bool has_runtime_throw_; - - // Copy of mapping generated by verifier of dex PCs of string init invocations - // to the set of other registers that the receiver has been copied into. - const SafeMap<uint32_t, std::set<uint32_t>> string_init_pc_reg_map_; }; } // namespace art diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index 43f2499b24..09ca8b7b44 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -422,6 +422,34 @@ bool SsaBuilder::FixAmbiguousArrayOps() { return true; } +static bool HasAliasInEnvironments(HInstruction* instruction) { + for (HUseIterator<HEnvironment*> use_it(instruction->GetEnvUses()); + !use_it.Done(); + use_it.Advance()) { + HEnvironment* use = use_it.Current()->GetUser(); + HUseListNode<HEnvironment*>* next = use_it.Current()->GetNext(); + if (next != nullptr && next->GetUser() == use) { + return true; + } + } + + if (kIsDebugBuild) { + // Do a quadratic search to ensure same environment uses are next + // to each other. + for (HUseIterator<HEnvironment*> use_it(instruction->GetEnvUses()); + !use_it.Done(); + use_it.Advance()) { + HUseListNode<HEnvironment*>* current = use_it.Current(); + HUseListNode<HEnvironment*>* next = current->GetNext(); + while (next != nullptr) { + DCHECK(next->GetUser() != current->GetUser()); + next = next->GetNext(); + } + } + } + return false; +} + void SsaBuilder::RemoveRedundantUninitializedStrings() { if (GetGraph()->IsDebuggable()) { // Do not perform the optimization for consistency with the interpreter @@ -433,7 +461,7 @@ void SsaBuilder::RemoveRedundantUninitializedStrings() { // Replace NewInstance of String with NullConstant if not used prior to // calling StringFactory. In case of deoptimization, the interpreter is // expected to skip null check on the `this` argument of the StringFactory call. - if (!new_instance->HasNonEnvironmentUses()) { + if (!new_instance->HasNonEnvironmentUses() && !HasAliasInEnvironments(new_instance)) { new_instance->ReplaceWith(GetGraph()->GetNullConstant()); new_instance->GetBlock()->RemoveInstruction(new_instance); diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 82a5f9611c..6972b3ef3f 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -1009,10 +1009,6 @@ void Locks::Init() { DCHECK(alloc_tracker_lock_ == nullptr); alloc_tracker_lock_ = new Mutex("AllocTracker lock", current_lock_level); - UPDATE_CURRENT_LOCK_LEVEL(kInterpreterStringInitMapLock); - DCHECK(interpreter_string_init_map_lock_ == nullptr); - interpreter_string_init_map_lock_ = new Mutex("Interpreter String initializer reference map lock", current_lock_level); - UPDATE_CURRENT_LOCK_LEVEL(kThreadListLock); DCHECK(thread_list_lock_ == nullptr); thread_list_lock_ = new Mutex("thread list lock", current_lock_level); diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index f674a6f3c8..e72f2a2e7b 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -102,7 +102,6 @@ enum LockLevel { kMonitorListLock, kJniLoadLibraryLock, kThreadListLock, - kInterpreterStringInitMapLock, kAllocTrackerLock, kDeoptimizationLock, kProfilerLock, diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index cbaa8173d2..3453abcd64 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -733,39 +733,21 @@ static inline bool DoCallCommon(ArtMethod* called_method, } if (string_init && !self->IsExceptionPending()) { - // Set the new string result of the StringFactory. - shadow_frame.SetVRegReference(string_init_vreg_this, result->GetL()); - // Overwrite all potential copies of the original result of the new-instance of string with the - // new result of the StringFactory. Use the verifier to find this set of registers. - ArtMethod* method = shadow_frame.GetMethod(); - MethodReference method_ref = method->ToMethodReference(); - SafeMap<uint32_t, std::set<uint32_t>>* string_init_map_ptr = nullptr; - MethodRefToStringInitRegMap& method_to_string_init_map = Runtime::Current()->GetStringInitMap(); - { - MutexLock mu(self, *Locks::interpreter_string_init_map_lock_); - auto it = method_to_string_init_map.find(method_ref); - if (it != method_to_string_init_map.end()) { - string_init_map_ptr = &it->second; - } - } - if (string_init_map_ptr == nullptr) { - SafeMap<uint32_t, std::set<uint32_t>> string_init_map = - verifier::MethodVerifier::FindStringInitMap(method); - MutexLock mu(self, *Locks::interpreter_string_init_map_lock_); - auto it = method_to_string_init_map.lower_bound(method_ref); - if (it == method_to_string_init_map.end() || - method_to_string_init_map.key_comp()(method_ref, it->first)) { - it = method_to_string_init_map.PutBefore(it, method_ref, std::move(string_init_map)); - } - string_init_map_ptr = &it->second; - } - if (string_init_map_ptr->size() != 0) { - uint32_t dex_pc = shadow_frame.GetDexPC(); - auto map_it = string_init_map_ptr->find(dex_pc); - if (map_it != string_init_map_ptr->end()) { - const std::set<uint32_t>& reg_set = map_it->second; - for (auto set_it = reg_set.begin(); set_it != reg_set.end(); ++set_it) { - shadow_frame.SetVRegReference(*set_it, result->GetL()); + mirror::Object* existing = shadow_frame.GetVRegReference(string_init_vreg_this); + if (existing == nullptr) { + // If it's null, we come from compiled code that was deoptimized. Nothing to do, + // as the compiler verified there was no alias. + // Set the new string result of the StringFactory. + shadow_frame.SetVRegReference(string_init_vreg_this, result->GetL()); + } else { + // Replace the fake string that was allocated with the StringFactory result. + for (uint32_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) { + if (shadow_frame.GetVRegReference(i) == existing) { + DCHECK_EQ(shadow_frame.GetVRegReference(i), + reinterpret_cast<mirror::Object*>(shadow_frame.GetVReg(i))); + shadow_frame.SetVRegReference(i, result->GetL()); + DCHECK_EQ(shadow_frame.GetVRegReference(i), + reinterpret_cast<mirror::Object*>(shadow_frame.GetVReg(i))); } } } diff --git a/runtime/runtime.h b/runtime/runtime.h index 1956bae52a..cbb3e89444 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -94,8 +94,6 @@ struct TraceConfig; class Transaction; typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions; -typedef SafeMap<MethodReference, SafeMap<uint32_t, std::set<uint32_t>>, - MethodReferenceComparator> MethodRefToStringInitRegMap; // Not all combinations of flags are valid. You may not visit all roots as well as the new roots // (no logical reason to do this). You also may not start logging new roots and stop logging new @@ -574,10 +572,6 @@ class Runtime { return jit_options_.get(); } - MethodRefToStringInitRegMap& GetStringInitMap() { - return method_ref_string_init_reg_map_; - } - bool IsDebuggable() const; // Returns the build fingerprint, if set. Otherwise an empty string is returned. @@ -803,8 +797,6 @@ class Runtime { // Experimental opcodes should not be used by other production code. ExperimentalFlags experimental_flags_; - MethodRefToStringInitRegMap method_ref_string_init_reg_map_; - // Contains the build fingerprint, if given as a parameter. std::string fingerprint_; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index a6cf9eaf86..0c6060e4e8 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -617,23 +617,6 @@ ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(uint32_t dex_pc) { return GetQuickInvokedMethod(inst, register_line, is_range, false); } -SafeMap<uint32_t, std::set<uint32_t>> MethodVerifier::FindStringInitMap(ArtMethod* m) { - Thread* self = Thread::Current(); - StackHandleScope<2> hs(self); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader())); - MethodVerifier verifier(self, m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(), - m->GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), - true, true, false, true); - // Avoid copying: The map is moved out of the verifier before the verifier is destroyed. - return std::move(verifier.FindStringInitMap()); -} - -SafeMap<uint32_t, std::set<uint32_t>>& MethodVerifier::FindStringInitMap() { - Verify(); - return GetStringInitPcRegMap(); -} - bool MethodVerifier::Verify() { // Some older code doesn't correctly mark constructors as such. Test for this case by looking at // the name. @@ -2865,8 +2848,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * Replace the uninitialized reference with an initialized one. We need to do this for all * registers that have the same object instance in them, not just the "this" register. */ - const uint32_t this_reg = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); - work_line_->MarkRefsAsInitialized(this, this_type, this_reg, work_insn_idx_); + work_line_->MarkRefsAsInitialized(this, this_type); } if (return_type == nullptr) { return_type = ®_types_.FromDescriptor(GetClassLoader(), return_type_descriptor, false); diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index b53a45cf41..6d8e1ab6ee 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -213,9 +213,6 @@ class MethodVerifier { static ArtMethod* FindInvokedMethodAtDexPc(ArtMethod* m, uint32_t dex_pc) SHARED_REQUIRES(Locks::mutator_lock_); - static SafeMap<uint32_t, std::set<uint32_t>> FindStringInitMap(ArtMethod* m) - SHARED_REQUIRES(Locks::mutator_lock_); - static void Init() SHARED_REQUIRES(Locks::mutator_lock_); static void Shutdown(); @@ -294,10 +291,6 @@ class MethodVerifier { ArtField* GetQuickFieldAccess(const Instruction* inst, RegisterLine* reg_line) SHARED_REQUIRES(Locks::mutator_lock_); - SafeMap<uint32_t, std::set<uint32_t>>& GetStringInitPcRegMap() { - return string_init_pc_reg_map_; - } - uint32_t GetEncounteredFailureTypes() { return encountered_failure_types_; } @@ -875,11 +868,6 @@ class MethodVerifier { friend class art::Thread; - // Map of dex pcs of invocations of java.lang.String.<init> to the set of other registers that - // contain the uninitialized this pointer to that invoke. Will contain no entry if there are - // no other registers. - SafeMap<uint32_t, std::set<uint32_t>> string_init_pc_reg_map_; - DISALLOW_COPY_AND_ASSIGN(MethodVerifier); }; std::ostream& operator<<(std::ostream& os, const MethodVerifier::FailureKind& rhs); diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc index b7cde995c7..82c371dec5 100644 --- a/runtime/verifier/register_line.cc +++ b/runtime/verifier/register_line.cc @@ -91,25 +91,14 @@ bool RegisterLine::VerifyRegisterTypeWide(MethodVerifier* verifier, uint32_t vsr return true; } -void RegisterLine::MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type, - uint32_t this_reg, uint32_t dex_pc) { +void RegisterLine::MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type) { DCHECK(uninit_type.IsUninitializedTypes()); - bool is_string = !uninit_type.IsUnresolvedTypes() && uninit_type.GetClass()->IsStringClass(); const RegType& init_type = verifier->GetRegTypeCache()->FromUninitialized(uninit_type); size_t changed = 0; for (uint32_t i = 0; i < num_regs_; i++) { if (GetRegisterType(verifier, i).Equals(uninit_type)) { line_[i] = init_type.GetId(); changed++; - if (is_string && i != this_reg) { - auto it = verifier->GetStringInitPcRegMap().find(dex_pc); - if (it != verifier->GetStringInitPcRegMap().end()) { - it->second.insert(i); - } else { - std::set<uint32_t> reg_set = { i }; - verifier->GetStringInitPcRegMap().Put(dex_pc, reg_set); - } - } } } // Is this initializing "this"? diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h index 9ea9cb763b..15ae202301 100644 --- a/runtime/verifier/register_line.h +++ b/runtime/verifier/register_line.h @@ -161,10 +161,7 @@ class RegisterLine { * reference type. This is called when an appropriate constructor is invoked -- all copies of * the reference must be marked as initialized. */ - void MarkRefsAsInitialized(MethodVerifier* verifier, - const RegType& uninit_type, - uint32_t this_reg, - uint32_t dex_pc) + void MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type) SHARED_REQUIRES(Locks::mutator_lock_); /* diff --git a/test/575-checker-string-init-alias/expected.txt b/test/575-checker-string-init-alias/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/575-checker-string-init-alias/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/575-checker-string-init-alias/info.txt b/test/575-checker-string-init-alias/info.txt new file mode 100644 index 0000000000..a91ea645e7 --- /dev/null +++ b/test/575-checker-string-init-alias/info.txt @@ -0,0 +1,2 @@ +Test for the String.<init> change and deoptimization: make +sure the compiler knows how to handle dex aliases. diff --git a/test/575-checker-string-init-alias/smali/TestCase.smali b/test/575-checker-string-init-alias/smali/TestCase.smali new file mode 100644 index 0000000000..ff04b278a4 --- /dev/null +++ b/test/575-checker-string-init-alias/smali/TestCase.smali @@ -0,0 +1,72 @@ +# Copyright (C) 2016 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. + +.class public LTestCase; + +.super Ljava/lang/Object; + +.field public static staticField:Ljava/lang/String; + +## CHECK-START: void TestCase.testNoAlias(int[], java.lang.String) register (after) +## CHECK: <<Null:l\d+>> NullConstant +## CHECK: Deoptimize env:[[<<Null>>,{{.*]]}} +## CHECK: InvokeStaticOrDirect method_name:java.lang.String.<init> +.method public static testNoAlias([ILjava/lang/String;)V + .registers 6 + const v1, 0 + const v2, 1 + new-instance v0, Ljava/lang/String; + + # Will deoptimize. + aget v3, p0, v1 + + # Check that we're being executed by the interpreter. + invoke-static {}, LMain;->assertIsInterpreted()V + + invoke-direct {v0, p1}, Ljava/lang/String;-><init>(Ljava/lang/String;)V + + sput-object v0, LTestCase;->staticField:Ljava/lang/String; + + # Will throw AIOOBE. + aget v3, p0, v2 + + return-void +.end method + +## CHECK-START: void TestCase.testAlias(int[], java.lang.String) register (after) +## CHECK: <<New:l\d+>> NewInstance +## CHECK: Deoptimize env:[[<<New>>,<<New>>,{{.*]]}} +## CHECK: InvokeStaticOrDirect method_name:java.lang.String.<init> +.method public static testAlias([ILjava/lang/String;)V + .registers 7 + const v2, 0 + const v3, 1 + new-instance v0, Ljava/lang/String; + move-object v1, v0 + + # Will deoptimize. + aget v4, p0, v2 + + # Check that we're being executed by the interpreter. + invoke-static {}, LMain;->assertIsInterpreted()V + + invoke-direct {v1, p1}, Ljava/lang/String;-><init>(Ljava/lang/String;)V + + sput-object v1, LTestCase;->staticField:Ljava/lang/String; + + # Will throw AIOOBE. + aget v4, p0, v3 + + return-void +.end method diff --git a/test/575-checker-string-init-alias/src/Main.java b/test/575-checker-string-init-alias/src/Main.java new file mode 100644 index 0000000000..1ab320748a --- /dev/null +++ b/test/575-checker-string-init-alias/src/Main.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2016 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. + */ + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; + +public class Main { + // Workaround for b/18051191. + class Inner {} + + public static native void assertIsInterpreted(); + + private static void assertEqual(String expected, String actual) { + if (!expected.equals(actual)) { + throw new Error("Assertion failed: " + expected + " != " + actual); + } + } + + public static void main(String[] args) throws Throwable { + System.loadLibrary(args[0]); + Class<?> c = Class.forName("TestCase"); + int[] array = new int[1]; + + { + Method m = c.getMethod("testNoAlias", int[].class, String.class); + try { + m.invoke(null, new Object[] { array , "foo" }); + throw new Error("Expected AIOOBE"); + } catch (InvocationTargetException e) { + if (!(e.getCause() instanceof ArrayIndexOutOfBoundsException)) { + throw new Error("Expected AIOOBE"); + } + // Ignore + } + Field field = c.getField("staticField"); + assertEqual("foo", (String)field.get(null)); + } + + { + Method m = c.getMethod("testAlias", int[].class, String.class); + try { + m.invoke(null, new Object[] { array, "bar" }); + throw new Error("Expected AIOOBE"); + } catch (InvocationTargetException e) { + if (!(e.getCause() instanceof ArrayIndexOutOfBoundsException)) { + throw new Error("Expected AIOOBE"); + } + // Ignore + } + Field field = c.getField("staticField"); + assertEqual("bar", (String)field.get(null)); + } + } +} |