Avoid compile time rewriting of dex code by verifier.
Compile time rewriting of dex code leads to dead code that is causing
issues with the LLVM compiler. Make instantiation and incompatible class
change errors be detected in slow path field and invoke logic so its
safe for the compile time verification just to softly fail the effected
classes.
This change places incompatible class change logic into the class
linkers ResolveMethod and consequently changes a number of APIs.
Change-Id: Ifb25f09accea348d15180f6ff041e38dfe0d536e
diff --git a/src/oat/runtime/support_field.cc b/src/oat/runtime/support_field.cc
index 99e3a94..289553e 100644
--- a/src/oat/runtime/support_field.cc
+++ b/src/oat/runtime/support_field.cc
@@ -24,12 +24,12 @@
extern "C" uint32_t artGet32StaticFromCode(uint32_t field_idx, const Method* referrer,
Thread* self, Method** sp)
SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
- Field* field = FindFieldFast(field_idx, referrer, true, false, sizeof(int32_t));
+ Field* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead, sizeof(int32_t));
if (LIKELY(field != NULL)) {
return field->Get32(NULL);
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, true, true, false, sizeof(int32_t));
+ field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveRead, sizeof(int32_t));
if (LIKELY(field != NULL)) {
return field->Get32(NULL);
}
@@ -39,12 +39,12 @@
extern "C" uint64_t artGet64StaticFromCode(uint32_t field_idx, const Method* referrer,
Thread* self, Method** sp)
SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
- Field* field = FindFieldFast(field_idx, referrer, true, false, sizeof(int64_t));
+ Field* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead, sizeof(int64_t));
if (LIKELY(field != NULL)) {
return field->Get64(NULL);
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, true, true, false, sizeof(int64_t));
+ field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveRead, sizeof(int64_t));
if (LIKELY(field != NULL)) {
return field->Get64(NULL);
}
@@ -54,12 +54,12 @@
extern "C" Object* artGetObjStaticFromCode(uint32_t field_idx, const Method* referrer,
Thread* self, Method** sp)
SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
- Field* field = FindFieldFast(field_idx, referrer, false, false, sizeof(Object*));
+ Field* field = FindFieldFast(field_idx, referrer, StaticObjectRead, sizeof(Object*));
if (LIKELY(field != NULL)) {
return field->GetObj(NULL);
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, true, false, false, sizeof(Object*));
+ field = FindFieldFromCode(field_idx, referrer, self, StaticObjectRead, sizeof(Object*));
if (LIKELY(field != NULL)) {
return field->GetObj(NULL);
}
@@ -69,12 +69,12 @@
extern "C" uint32_t artGet32InstanceFromCode(uint32_t field_idx, Object* obj,
const Method* referrer, Thread* self, Method** sp)
SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
- Field* field = FindFieldFast(field_idx, referrer, true, false, sizeof(int32_t));
+ Field* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(int32_t));
if (LIKELY(field != NULL && obj != NULL)) {
return field->Get32(obj);
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, false, true, false, sizeof(int32_t));
+ field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveRead, sizeof(int32_t));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
ThrowNullPointerExceptionForFieldAccess(self, field, true);
@@ -88,12 +88,12 @@
extern "C" uint64_t artGet64InstanceFromCode(uint32_t field_idx, Object* obj,
const Method* referrer, Thread* self, Method** sp)
SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
- Field* field = FindFieldFast(field_idx, referrer, true, false, sizeof(int64_t));
+ Field* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(int64_t));
if (LIKELY(field != NULL && obj != NULL)) {
return field->Get64(obj);
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, false, true, false, sizeof(int64_t));
+ field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveRead, sizeof(int64_t));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
ThrowNullPointerExceptionForFieldAccess(self, field, true);
@@ -107,12 +107,12 @@
extern "C" Object* artGetObjInstanceFromCode(uint32_t field_idx, Object* obj,
const Method* referrer, Thread* self, Method** sp)
SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
- Field* field = FindFieldFast(field_idx, referrer, false, false, sizeof(Object*));
+ Field* field = FindFieldFast(field_idx, referrer, InstanceObjectRead, sizeof(Object*));
if (LIKELY(field != NULL && obj != NULL)) {
return field->GetObj(obj);
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, false, false, false, sizeof(Object*));
+ field = FindFieldFromCode(field_idx, referrer, self, InstanceObjectRead, sizeof(Object*));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
ThrowNullPointerExceptionForFieldAccess(self, field, true);
@@ -126,13 +126,13 @@
extern "C" int artSet32StaticFromCode(uint32_t field_idx, uint32_t new_value,
const Method* referrer, Thread* self, Method** sp)
SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
- Field* field = FindFieldFast(field_idx, referrer, true, true, sizeof(int32_t));
+ Field* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(int32_t));
if (LIKELY(field != NULL)) {
field->Set32(NULL, new_value);
return 0; // success
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, true, true, true, sizeof(int32_t));
+ field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveWrite, sizeof(int32_t));
if (LIKELY(field != NULL)) {
field->Set32(NULL, new_value);
return 0; // success
@@ -143,13 +143,13 @@
extern "C" int artSet64StaticFromCode(uint32_t field_idx, const Method* referrer,
uint64_t new_value, Thread* self, Method** sp)
SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
- Field* field = FindFieldFast(field_idx, referrer, true, true, sizeof(int64_t));
+ Field* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(int64_t));
if (LIKELY(field != NULL)) {
field->Set64(NULL, new_value);
return 0; // success
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, true, true, true, sizeof(int64_t));
+ field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveWrite, sizeof(int64_t));
if (LIKELY(field != NULL)) {
field->Set64(NULL, new_value);
return 0; // success
@@ -160,7 +160,7 @@
extern "C" int artSetObjStaticFromCode(uint32_t field_idx, Object* new_value,
const Method* referrer, Thread* self, Method** sp)
SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
- Field* field = FindFieldFast(field_idx, referrer, false, true, sizeof(Object*));
+ Field* field = FindFieldFast(field_idx, referrer, StaticObjectWrite, sizeof(Object*));
if (LIKELY(field != NULL)) {
if (LIKELY(!FieldHelper(field).IsPrimitiveType())) {
field->SetObj(NULL, new_value);
@@ -168,7 +168,7 @@
}
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, true, false, true, sizeof(Object*));
+ field = FindFieldFromCode(field_idx, referrer, self, StaticObjectWrite, sizeof(Object*));
if (LIKELY(field != NULL)) {
field->SetObj(NULL, new_value);
return 0; // success
@@ -179,13 +179,13 @@
extern "C" int artSet32InstanceFromCode(uint32_t field_idx, Object* obj, uint32_t new_value,
const Method* referrer, Thread* self, Method** sp)
SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
- Field* field = FindFieldFast(field_idx, referrer, true, true, sizeof(int32_t));
+ Field* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(int32_t));
if (LIKELY(field != NULL && obj != NULL)) {
field->Set32(obj, new_value);
return 0; // success
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, false, true, true, sizeof(int32_t));
+ field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveWrite, sizeof(int32_t));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
ThrowNullPointerExceptionForFieldAccess(self, field, false);
@@ -202,14 +202,14 @@
SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
Method* callee_save = Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsOnly);
Method* referrer = sp[callee_save->GetFrameSizeInBytes() / sizeof(Method*)];
- Field* field = FindFieldFast(field_idx, referrer, true, true, sizeof(int64_t));
+ Field* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(int64_t));
if (LIKELY(field != NULL && obj != NULL)) {
field->Set64(obj, new_value);
return 0; // success
}
*sp = callee_save;
self->SetTopOfStack(sp, 0);
- field = FindFieldFromCode(field_idx, referrer, self, false, true, true, sizeof(int64_t));
+ field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveWrite, sizeof(int64_t));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
ThrowNullPointerExceptionForFieldAccess(self, field, false);
@@ -224,13 +224,13 @@
extern "C" int artSetObjInstanceFromCode(uint32_t field_idx, Object* obj, Object* new_value,
const Method* referrer, Thread* self, Method** sp)
SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
- Field* field = FindFieldFast(field_idx, referrer, false, true, sizeof(Object*));
+ Field* field = FindFieldFast(field_idx, referrer, InstanceObjectWrite, sizeof(Object*));
if (LIKELY(field != NULL && obj != NULL)) {
field->SetObj(obj, new_value);
return 0; // success
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, false, false, true, sizeof(Object*));
+ field = FindFieldFromCode(field_idx, referrer, self, InstanceObjectWrite, sizeof(Object*));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
ThrowNullPointerExceptionForFieldAccess(self, field, false);
diff --git a/src/oat/runtime/support_stubs.cc b/src/oat/runtime/support_stubs.cc
index 8b12973..510df3b 100644
--- a/src/oat/runtime/support_stubs.cc
+++ b/src/oat/runtime/support_stubs.cc
@@ -89,8 +89,7 @@
// Compute details about the called method (avoid GCs)
ClassLinker* linker = Runtime::Current()->GetClassLinker();
Method* caller = *caller_sp;
- bool is_static;
- bool is_virtual;
+ InvokeType invoke_type;
uint32_t dex_method_idx;
#if !defined(__i386__)
const char* shorty;
@@ -104,14 +103,27 @@
CHECK_LT(dex_pc, code->insns_size_in_code_units_);
const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
Instruction::Code instr_code = instr->Opcode();
- is_static = (instr_code == Instruction::INVOKE_STATIC) ||
- (instr_code == Instruction::INVOKE_STATIC_RANGE);
- is_virtual = (instr_code == Instruction::INVOKE_VIRTUAL) ||
- (instr_code == Instruction::INVOKE_VIRTUAL_RANGE) ||
- (instr_code == Instruction::INVOKE_SUPER) ||
- (instr_code == Instruction::INVOKE_SUPER_RANGE);
- DCHECK(is_static || is_virtual || (instr_code == Instruction::INVOKE_DIRECT) ||
- (instr_code == Instruction::INVOKE_DIRECT_RANGE));
+ switch (instr_code) {
+ case Instruction::INVOKE_DIRECT: // Fall-through.
+ case Instruction::INVOKE_DIRECT_RANGE:
+ invoke_type = kDirect;
+ break;
+ case Instruction::INVOKE_STATIC: // Fall-through.
+ case Instruction::INVOKE_STATIC_RANGE:
+ invoke_type = kStatic;
+ break;
+ case Instruction::INVOKE_SUPER: // Fall-through.
+ case Instruction::INVOKE_SUPER_RANGE:
+ invoke_type = kSuper;
+ break;
+ case Instruction::INVOKE_VIRTUAL: // Fall-through.
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ invoke_type = kVirtual;
+ break;
+ default:
+ LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(NULL);
+ invoke_type = kDirect; // Avoid used uninitialized warnings.
+ }
DecodedInstruction dec_insn(instr);
dex_method_idx = dec_insn.vB;
#if !defined(__i386__)
@@ -119,8 +131,7 @@
#endif
} else {
DCHECK(!called->IsRuntimeMethod());
- is_static = type == Runtime::kStaticMethod;
- is_virtual = false;
+ invoke_type = (type == Runtime::kStaticMethod) ? kStatic : kDirect;
dex_method_idx = called->GetDexMethodIndex();
#if !defined(__i386__)
MethodHelper mh(called);
@@ -141,7 +152,7 @@
}
// Place into local references incoming arguments from the caller's register arguments
size_t cur_arg = 1; // skip method_idx in R0, first arg is in R1
- if (!is_static) {
+ if (invoke_type != kStatic) {
Object* obj = reinterpret_cast<Object*>(regs[cur_arg]);
cur_arg++;
if (args_in_regs < 3) {
@@ -176,33 +187,28 @@
#endif
// Resolve method filling in dex cache
if (type == Runtime::kUnknownMethod) {
- called = linker->ResolveMethod(dex_method_idx, caller, !is_virtual);
+ called = linker->ResolveMethod(dex_method_idx, caller, invoke_type);
}
const void* code = NULL;
if (LIKELY(!thread->IsExceptionPending())) {
- if (LIKELY(called->IsDirect() == !is_virtual)) {
- // Ensure that the called method's class is initialized.
- Class* called_class = called->GetDeclaringClass();
- linker->EnsureInitialized(called_class, true, true);
- if (LIKELY(called_class->IsInitialized())) {
- code = called->GetCode();
- } else if (called_class->IsInitializing()) {
- if (is_static) {
- // Class is still initializing, go to oat and grab code (trampoline must be left in place
- // until class is initialized to stop races between threads).
- code = linker->GetOatCodeFor(called);
- } else {
- // No trampoline for non-static methods.
- code = called->GetCode();
- }
+ // Incompatible class change should have been handled in resolve method.
+ CHECK(!called->CheckIncompatibleClassChange(invoke_type));
+ // Ensure that the called method's class is initialized.
+ Class* called_class = called->GetDeclaringClass();
+ linker->EnsureInitialized(called_class, true, true);
+ if (LIKELY(called_class->IsInitialized())) {
+ code = called->GetCode();
+ } else if (called_class->IsInitializing()) {
+ if (invoke_type == kStatic) {
+ // Class is still initializing, go to oat and grab code (trampoline must be left in place
+ // until class is initialized to stop races between threads).
+ code = linker->GetOatCodeFor(called);
} else {
- DCHECK(called_class->IsErroneous());
+ // No trampoline for non-static methods.
+ code = called->GetCode();
}
} else {
- // Direct method has been made virtual
- thread->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
- "Expected direct method but found virtual: %s",
- PrettyMethod(called, true).c_str());
+ DCHECK(called_class->IsErroneous());
}
}
if (UNLIKELY(code == NULL)) {
@@ -229,8 +235,7 @@
Method* caller = thread->GetCurrentMethod(&dex_pc);
ClassLinker* linker = Runtime::Current()->GetClassLinker();
- bool is_static;
- bool is_virtual;
+ InvokeType invoke_type;
uint32_t dex_method_idx;
if (type == Runtime::kUnknownMethod) {
DCHECK(called->IsRuntimeMethod());
@@ -238,32 +243,59 @@
CHECK_LT(dex_pc, code->insns_size_in_code_units_);
const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
Instruction::Code instr_code = instr->Opcode();
- is_static = (instr_code == Instruction::INVOKE_STATIC) ||
- (instr_code == Instruction::INVOKE_STATIC_RANGE);
- is_virtual = (instr_code == Instruction::INVOKE_VIRTUAL) ||
- (instr_code == Instruction::INVOKE_VIRTUAL_RANGE) ||
- (instr_code == Instruction::INVOKE_SUPER) ||
- (instr_code == Instruction::INVOKE_SUPER_RANGE);
- DCHECK(is_static || is_virtual || (instr_code == Instruction::INVOKE_DIRECT) ||
- (instr_code == Instruction::INVOKE_DIRECT_RANGE));
+ switch (instr_code) {
+ case Instruction::INVOKE_DIRECT: // Fall-through.
+ case Instruction::INVOKE_DIRECT_RANGE:
+ invoke_type = kDirect;
+ break;
+ case Instruction::INVOKE_STATIC: // Fall-through.
+ case Instruction::INVOKE_STATIC_RANGE:
+ invoke_type = kStatic;
+ break;
+ case Instruction::INVOKE_SUPER: // Fall-through.
+ case Instruction::INVOKE_SUPER_RANGE:
+ invoke_type = kSuper;
+ break;
+ case Instruction::INVOKE_VIRTUAL: // Fall-through.
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ invoke_type = kVirtual;
+ break;
+ default:
+ LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(NULL);
+ invoke_type = kDirect; // Avoid used uninitialized warnings.
+ }
DecodedInstruction dec_insn(instr);
dex_method_idx = dec_insn.vB;
} else {
DCHECK(!called->IsRuntimeMethod());
- is_static = type == Runtime::kStaticMethod;
- is_virtual = false;
+ invoke_type = (type == Runtime::kStaticMethod) ? kStatic : kDirect;
dex_method_idx = called->GetDexMethodIndex();
}
if (type == Runtime::kUnknownMethod) {
- called = linker->ResolveMethod(dex_method_idx, caller, !is_virtual);
+ called = linker->ResolveMethod(dex_method_idx, caller, invoke_type);
}
const void* code = NULL;
if (LIKELY(!thread->IsExceptionPending())) {
- if (LIKELY(called->IsDirect() == !is_virtual)) {
- // Ensure that the called method's class is initialized.
- Class* called_class = called->GetDeclaringClass();
- linker->EnsureInitialized(called_class, true, true);
- if (LIKELY(called_class->IsInitialized())) {
+ // Incompatible class change should have been handled in resolve method.
+ CHECK(!called->CheckIncompatibleClassChange(invoke_type));
+ // Ensure that the called method's class is initialized.
+ Class* called_class = called->GetDeclaringClass();
+ linker->EnsureInitialized(called_class, true, true);
+ if (LIKELY(called_class->IsInitialized())) {
+ code = called->GetCode();
+ // TODO: remove this after we solve the link issue.
+ { // for lazy link.
+ if (code == NULL) {
+ code = linker->GetOatCodeFor(called);
+ }
+ }
+ } else if (called_class->IsInitializing()) {
+ if (invoke_type == kStatic) {
+ // Class is still initializing, go to oat and grab code (trampoline must be left in place
+ // until class is initialized to stop races between threads).
+ code = linker->GetOatCodeFor(called);
+ } else {
+ // No trampoline for non-static methods.
code = called->GetCode();
// TODO: remove this after we solve the link issue.
{ // for lazy link.
@@ -271,29 +303,9 @@
code = linker->GetOatCodeFor(called);
}
}
- } else if (called_class->IsInitializing()) {
- if (is_static) {
- // Class is still initializing, go to oat and grab code (trampoline must be left in place
- // until class is initialized to stop races between threads).
- code = linker->GetOatCodeFor(called);
- } else {
- // No trampoline for non-static methods.
- code = called->GetCode();
- // TODO: remove this after we solve the link issue.
- { // for lazy link.
- if (code == NULL) {
- code = linker->GetOatCodeFor(called);
- }
- }
- }
- } else {
- DCHECK(called_class->IsErroneous());
}
} else {
- // Direct method has been made virtual
- thread->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
- "Expected direct method but found virtual: %s",
- PrettyMethod(called, true).c_str());
+ DCHECK(called_class->IsErroneous());
}
}
if (LIKELY(code != NULL)) {