diff options
| -rw-r--r-- | runtime/interpreter/interpreter.cc | 56 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter_common.cc | 43 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter_common.h | 6 | ||||
| -rw-r--r-- | test/597-deopt-new-string/src/Main.java | 13 |
4 files changed, 93 insertions, 25 deletions
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 1d0e600e4d..8c42b3abce 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -484,6 +484,36 @@ void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object* receive self->PopShadowFrame(); } +static bool IsStringInit(const Instruction* instr, ArtMethod* caller) + SHARED_REQUIRES(Locks::mutator_lock_) { + if (instr->Opcode() == Instruction::INVOKE_DIRECT || + instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE) { + // Instead of calling ResolveMethod() which has suspend point and can trigger + // GC, look up the callee method symbolically. + uint16_t callee_method_idx = (instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE) ? + instr->VRegB_3rc() : instr->VRegB_35c(); + const DexFile* dex_file = caller->GetDexFile(); + const DexFile::MethodId& method_id = dex_file->GetMethodId(callee_method_idx); + const char* class_name = dex_file->StringByTypeIdx(method_id.class_idx_); + const char* method_name = dex_file->GetMethodName(method_id); + // Compare method's class name and method name against string init. + // It's ok since it's not allowed to create your own java/lang/String. + // TODO: verify that assumption. + if ((strcmp(class_name, "Ljava/lang/String;") == 0) && + (strcmp(method_name, "<init>") == 0)) { + return true; + } + } + return false; +} + +static int16_t GetReceiverRegisterForStringInit(const Instruction* instr) { + DCHECK(instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE || + instr->Opcode() == Instruction::INVOKE_DIRECT); + return (instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE) ? + instr->VRegC_3rc() : instr->VRegC_35c(); +} + void EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame* shadow_frame, bool from_code, @@ -519,22 +549,38 @@ void EnterInterpreterFromDeoptimize(Thread* self, // TODO: should be tested more once b/17586779 is fixed. const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]); if (instr->IsInvoke()) { + if (IsStringInit(instr, shadow_frame->GetMethod())) { + uint16_t this_obj_vreg = GetReceiverRegisterForStringInit(instr); + // Move the StringFactory.newStringFromChars() result into the register representing + // "this object" when invoking the string constructor in the original dex instruction. + // Also move the result into all aliases. + DCHECK(value.GetL()->IsString()); + SetStringInitValueToAllAliases(shadow_frame, this_obj_vreg, value); + // Calling string constructor in the original dex code doesn't generate a result value. + value.SetJ(0); + } new_dex_pc = dex_pc + instr->SizeInCodeUnits(); } else if (instr->Opcode() == Instruction::NEW_INSTANCE) { // It's possible to deoptimize at a NEW_INSTANCE dex instruciton that's for a // java string, which is turned into a call into StringFactory.newEmptyString(); + // Move the StringFactory.newEmptyString() result into the destination register. + DCHECK(value.GetL()->IsString()); + shadow_frame->SetVRegReference(instr->VRegA_21c(), value.GetL()); + // new-instance doesn't generate a result value. + value.SetJ(0); + // Skip the dex instruction since we essentially come back from an invocation. + new_dex_pc = dex_pc + instr->SizeInCodeUnits(); if (kIsDebugBuild) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + // This is a suspend point. But it's ok since value has been set into shadow_frame. mirror::Class* klass = class_linker->ResolveType( instr->VRegB_21c(), shadow_frame->GetMethod()); DCHECK(klass->IsStringClass()); } - // Skip the dex instruction since we essentially come back from an invocation. - new_dex_pc = dex_pc + instr->SizeInCodeUnits(); } else { - DCHECK(false) << "Unexpected instruction opcode " << instr->Opcode() - << " at dex_pc " << dex_pc - << " of method: " << PrettyMethod(shadow_frame->GetMethod(), false); + CHECK(false) << "Unexpected instruction opcode " << instr->Opcode() + << " at dex_pc " << dex_pc + << " of method: " << PrettyMethod(shadow_frame->GetMethod(), false); } } else { // Nothing to do, the dex_pc is the one at which the code requested diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 12d70c5244..53d5e43989 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -540,6 +540,30 @@ void ArtInterpreterToCompiledCodeBridge(Thread* self, result, method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty()); } +void SetStringInitValueToAllAliases(ShadowFrame* shadow_frame, + uint16_t this_obj_vreg, + JValue result) + SHARED_REQUIRES(Locks::mutator_lock_) { + Object* existing = shadow_frame->GetVRegReference(this_obj_vreg); + 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(this_obj_vreg, result.GetL()); + return; + } + // Set the string init result into all aliases. + for (uint32_t i = 0, e = shadow_frame->NumberOfVRegs(); i < e; ++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))); + } + } +} + template <bool is_range, bool do_assignability_check, size_t kVarArgMax> @@ -739,24 +763,7 @@ static inline bool DoCallCommon(ArtMethod* called_method, } if (string_init && !self->IsExceptionPending()) { - 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))); - } - } - } + SetStringInitValueToAllAliases(&shadow_frame, string_init_vreg_this, *result); } return !self->IsExceptionPending(); diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 69376fd7a1..cc470f372b 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -1021,6 +1021,12 @@ void ArtInterpreterToCompiledCodeBridge(Thread* self, ShadowFrame* shadow_frame, JValue* result); +// Set string value created from StringFactory.newStringFromXXX() into all aliases of +// StringFactory.newEmptyString(). +void SetStringInitValueToAllAliases(ShadowFrame* shadow_frame, + uint16_t this_obj_vreg, + JValue result); + // Explicitly instantiate all DoInvoke functions. #define EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, _is_range, _do_check) \ template SHARED_REQUIRES(Locks::mutator_lock_) \ diff --git a/test/597-deopt-new-string/src/Main.java b/test/597-deopt-new-string/src/Main.java index 1224e407b0..e78f0d36ec 100644 --- a/test/597-deopt-new-string/src/Main.java +++ b/test/597-deopt-new-string/src/Main.java @@ -48,7 +48,12 @@ public class Main implements Runnable { throw new Error(); } char[] arr = {'a', 'b', 'c'}; - return new String(arr, 0, arr.length); + String str = new String(arr, 0, arr.length); + if (!str.equals("abc")) { + System.out.println("Failure 1! " + str); + System.exit(0); + } + return str; } public void run() { @@ -68,7 +73,11 @@ public class Main implements Runnable { } else { // This thread keeps doing new String() from a char array. while (!done) { - $noinline$run0(); + String str = $noinline$run0(); + if (!str.equals("abc")) { + System.out.println("Failure 2! " + str); + System.exit(0); + } } } } |