bool IsNterpSupported() { #ifdef ART_USE_RESTRICTED_MODE // TODO(Simulator): Support Nterp. // Nterp uses the native stack and quick stack frame layout; this will be a complication // for the simulator mode. We should use switch interpreter only for now. returnfalse; #else switch (kRuntimeQuickCodeISA) { case InstructionSet::kArm: case InstructionSet::kThumb2: case InstructionSet::kArm64: return kReserveMarkingRegister && !kUseTableLookupReadBarrier; case InstructionSet::kRiscv64: returntrue; case InstructionSet::kX86: case InstructionSet::kX86_64: return !kUseTableLookupReadBarrier; default: returnfalse;
} #endif// #ifdef ART_USE_RESTRICTED_MODE
}
bool CanRuntimeUseNterp() REQUIRES_SHARED(Locks::mutator_lock_) {
Runtime* runtime = Runtime::Current();
instrumentation::Instrumentation* instr = runtime->GetInstrumentation(); // If the runtime is interpreter only, we currently don't use nterp as some // parts of the runtime (like instrumentation) make assumption on an // interpreter-only runtime to always be in a switch-like interpreter. return IsNterpSupported() && !runtime->IsJavaDebuggable() && !instr->EntryExitStubsInstalled() &&
!instr->InterpretOnly() && !runtime->IsAotCompiler() &&
!instr->NeedsSlowInterpreterForListeners() && // An async exception has been thrown. We need to go to the switch interpreter. nterp // doesn't know how to deal with these so we could end up never dealing with it if we are // in an infinite loop.
!runtime->AreAsyncExceptionsThrown() &&
(runtime->GetJit() == nullptr || !runtime->GetJit()->JitAtFirstUse());
}
// The entrypoint for nterp, which ArtMethods can directly point to. extern"C"void ExecuteNterpImpl() REQUIRES_SHARED(Locks::mutator_lock_); extern"C"void EndExecuteNterpImpl() REQUIRES_SHARED(Locks::mutator_lock_);
// Another entrypoint, which does a clinit check at entry. extern"C"void ExecuteNterpWithClinitImpl() REQUIRES_SHARED(Locks::mutator_lock_); extern"C"void EndExecuteNterpWithClinitImpl() REQUIRES_SHARED(Locks::mutator_lock_);
/* *Verifysomeconstantsusedbythenterpinterpreter.
*/ void CheckNterpAsmConstants() { /* *Ifwe'reusingcomputedgotoinstructiontransitions,makesure *noneofthehandlersoverflowsthebytelimit.Thiswon'ttell *whichonedid,butifanyoneistoobigthetotalsizewill *overflow.
*/
constexpr size_t width = kNterpHandlerSize;
ptrdiff_t interp_size = reinterpret_cast<uintptr_t>(artNterpAsmInstructionEnd) - reinterpret_cast<uintptr_t>(artNterpAsmInstructionStart);
static_assert(kNumPackedOpcodes * width != 0); // For arm/arm64, we have four sets of opcode handlers, 20KiB apart, to get a handler set with // 16KiB alignment for quick opcode dispatch. Each handler set is 16KiB and the 4KiB gaps // hold slow paths for the handler sets. Slow paths for the last handler set are located // after the `artNterpAsmInstructionEnd`. static constexpr size_t kNumHandlerSets =
(kRuntimeISA == InstructionSet::kArm64 || kRuntimeISA == InstructionSet::kArm) ? 4 : 1; static constexpr size_t kExpectedSize =
kNumHandlerSets * kNumPackedOpcodes * width + (kNumHandlerSets - 1) * 4 * KB; if (interp_size != kExpectedSize) {
LOG(FATAL) << "ERROR: unexpected asm interp size " << interp_size
<< "(did an instruction handler exceed " << width << " bytes?)";
}
}
inlinevoid UpdateHotness(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
jit::Jit* jit = Runtime::Current()->GetJit(); if (jit == nullptr || !jit->UseFastCompiler()) { // The hotness we will add to a method when we perform a // field/method/class/string lookup. static constexpr int kHotnessCounter = 0xf;
method->UpdateCounter(kHotnessCounter);
}
}
static constexpr uint8_t GetOpcodeInvokeType(uint8_t opcode) { switch (opcode) { case Instruction::INVOKE_DIRECT: case Instruction::INVOKE_DIRECT_RANGE: returnstatic_cast<uint8_t>(kDirect); case Instruction::INVOKE_INTERFACE: case Instruction::INVOKE_INTERFACE_RANGE: returnstatic_cast<uint8_t>(kInterface); case Instruction::INVOKE_STATIC: case Instruction::INVOKE_STATIC_RANGE: returnstatic_cast<uint8_t>(kStatic); case Instruction::INVOKE_SUPER: case Instruction::INVOKE_SUPER_RANGE: returnstatic_cast<uint8_t>(kSuper); case Instruction::INVOKE_VIRTUAL: case Instruction::INVOKE_VIRTUAL_RANGE: returnstatic_cast<uint8_t>(kVirtual);
if (type == kInterface || type == kSuper) { // The case of finding the method through the caller's class // are too rare for super and interface invokes to be worth it. return nullptr;
}
// For non-interface instance calls, check in the receiver's class. if (registers != nullptr && type != kStatic) {
uint16_t this_reg =
(opcode >= Instruction::INVOKE_VIRTUAL_RANGE) ? inst->VRegC_3rc() : inst->VRegC_35c();
mirror::Object* obj = reinterpret_cast32<mirror::Object*>(registers[this_reg]); if (obj != nullptr) {
mirror::Class* obj_cls = obj->GetClass(); if (obj_cls->GetDexTypeIndex() == method_id.class_idx_ &&
obj_cls->GetDexCache() == cls->GetDexCache()) { // Passing `registers` is only done in nterp, which isn't used for // cross-compilation.
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
ArtMethod* method =
obj_cls->FindDeclaredClassMethod</* kOnlyLookAtIndex= */ false, kRuntimePointerSize>(
method_index); if (caller->SkipAccessChecks() || method == nullptr) { return method;
} // No need to check for invoke class mismatch, as this only applies for // interface class, and we know the declaring class of `method` is not // an interface.
DCHECK(!method->GetDeclaringClass<kWithoutReadBarrier>()->IsInterface()); if (method->IsPublic() &&
method->GetDeclaringClass<kWithoutReadBarrier>()->IsPublic() &&
!method->CheckIncompatibleClassChange(type)) { return method;
}
}
}
}
if (caller->SkipAccessChecks()) { return caller->GetDexCache()->GetResolvedMethod(method_index);
}
// In release mode, this is just a simple load. // In debug mode, this checks that we're using the correct instruction format.
uint16_t method_index =
(opcode >= Instruction::INVOKE_VIRTUAL_RANGE) ? inst->VRegB_3rc() : inst->VRegB_35c();
if (invoke_type == kInterface) {
size_t result = 0u; if (resolved_method->GetDeclaringClass()->IsObjectClass()) { // Set the low bit to notify the interpreter it should do a vtable call.
DCHECK_LT(resolved_method->GetMethodIndex(), 0x10000);
result = (resolved_method->GetMethodIndex() << 16) | 1U;
} else {
DCHECK(resolved_method->GetDeclaringClass()->IsInterface());
DCHECK(!resolved_method->IsCopied()); if (!resolved_method->IsAbstract()) { // Set the second bit to notify the interpreter this is a default // method.
result = reinterpret_cast<size_t>(resolved_method) | 2U;
} else {
result = reinterpret_cast<size_t>(resolved_method);
}
}
UpdateCache(self, dex_pc_ptr, result); return result;
} elseif (resolved_method->IsStringConstructor()) {
CHECK_NE(invoke_type, kSuper);
resolved_method = WellKnownClasses::StringInitToStringFactory(resolved_method); // Or the result with 1 to notify to nterp this is a string init method. We // also don't cache the result as we don't want nterp to have its fast path always // check for it, and we expect a lot more regular calls than string init // calls. returnreinterpret_cast<size_t>(resolved_method) | 1;
} elseif (invoke_type == kVirtual) {
UpdateCache(self, dex_pc_ptr, resolved_method->GetMethodIndex()); return resolved_method->GetMethodIndex();
} else {
UpdateCache(self, dex_pc_ptr, resolved_method); returnreinterpret_cast<size_t>(resolved_method);
}
}
ObjPtr<mirror::Class> cls = caller->GetDeclaringClass(); const dex::FieldId& field_id = cls->GetDexFile().GetFieldId(field_index); if (cls->GetDexTypeIndex() == field_id.class_idx_) { // Field is in the same class as the caller, no need to do access checks. return cls->FindDeclaredField(field_index);
}
if (!kStatic) {
mirror::Object* obj = reinterpret_cast32<mirror::Object*>(registers[inst->VRegB_22c()]); if (obj != nullptr) {
mirror::Class* obj_cls = obj->GetClass(); if (obj_cls->GetDexTypeIndex() == field_id.class_idx_ &&
obj_cls->GetDexCache() == cls->GetDexCache()) {
ArtField* resolved_field = obj_cls->FindDeclaredField(field_index); if (CanAccessFast<kStatic>(resolved_field, caller, inst)) { return resolved_field;
}
}
}
}
ArtField* field = caller->GetDexCache()->GetResolvedField(field_index); if (CanAccessFast<kStatic>(field, caller, inst)) { return field;
}
// For sput-object, try to resolve the field type even if we were not requested to. // Only if the field type is successfully resolved can we update the cache. If we // fail to resolve the type, we clear the exception to keep interpreter // semantics of not throwing when null is stored. bool update_cache = true; if (opcode == Instruction::SPUT_OBJECT &&
caller->GetDeclaringClass()->HasTypeChecksFailure() &&
resolved_field->ResolveType() == nullptr) {
DCHECK(self->IsExceptionPending()); if (resolve_field_type) { return0;
}
self->ClearException();
update_cache = false;
}
if (resolved_field->IsVolatile()) { // Or the result with 1 to notify to nterp this is a volatile field. We // also don't cache the result as we don't want nterp to have its fast path always // check for it. return EncodeField(resolved_field, /* is_volatile= */ true);
}
uint64_t result = EncodeField(resolved_field); if (update_cache) {
self->GetInterpreterCache()->SetInt64(self, dex_pc_ptr, result);
} return result;
}
// For faster execution, `cls` can be a from-space reference which is OK, as // we're only using native fields from that object, and checking for state // invariants that don't roll back (ie that the class is initialized).
ALWAYS_INLINE FLATTEN static uint64_t NterpGetLocalStaticFieldInternal(mirror::Class* cls, const uint16_t* dex_pc_ptr)
REQUIRES_SHARED(Locks::mutator_lock_) { // We're checking if we're accessing a field of the currently executing class. // We only need to check that the class is initialized, and don't need // a synchrnonization barrier for this. if (cls->GetStatus<kVerifyNone, /*kWithSynchronizationBarrier=*/ false>()
< ClassStatus::kInitialized) { return0u;
}
ArtField* resolved_field = cls->FindDeclaredField</*kOnlyLookAtIndex=*/true>(
Instruction::At(dex_pc_ptr)->VRegB_21c()); if (resolved_field != nullptr && resolved_field->IsStatic() && !resolved_field->IsVolatile()) { // Note we don't store in the thread interpreter cache to leave the cache // for instructions which do not have a fast path like this one. return EncodeField(resolved_field);
} return0u;
}
// `cls` can be a from-space, see comment in `NterpGetLocalStaticFieldInternal`.
FLATTEN extern"C" uint64_t NterpGetLocalStaticField(mirror::Class* cls, const uint16_t* dex_pc_ptr)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedAssertNoThreadSuspension sants("In nterp"); return NterpGetLocalStaticFieldInternal(cls, dex_pc_ptr);
}
// `cls` can be a from-space, see comment in `NterpGetLocalStaticFieldInternal`.
FLATTEN extern"C" uint64_t NterpGetLocalStaticFieldForSPutObject(mirror::Class* cls, const uint16_t* dex_pc_ptr)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedAssertNoThreadSuspension sants("In nterp"); // For object store in methods that may have type check failures, we need to // resolve the type of the field. In such a case, go to slow path. if (cls->HasTypeChecksFailure()) { return0u;
} return NterpGetLocalStaticFieldInternal(cls, dex_pc_ptr);
}
// For faster execution, `cls` can be a from-space reference which is OK, as // we're only using native fields from that object.
ALWAYS_INLINE FLATTEN static size_t NterpGetLocalInstanceFieldInternal(mirror::Class* cls, const uint16_t* dex_pc_ptr)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedAssertNoThreadSuspension sants("In nterp"); const Instruction* inst = Instruction::At(dex_pc_ptr);
uint16_t field_index = inst->VRegC_22c();
ArtField* resolved_field = cls->FindDeclaredField</*kOnlyLookAtIndex=*/true>(field_index); if (resolved_field != nullptr && !resolved_field->IsStatic() && !resolved_field->IsVolatile()) { // Note we don't store in the thread interpreter cache to leave the cache // for instructions which do not have a fast path like this one. return resolved_field->GetOffset().Uint32Value();
}
ArtField* resolved_field = FindFieldFast</*kStatic=*/false>(caller, field_index, registers, inst); if (resolved_field == nullptr || resolved_field->IsStatic()) {
resolved_field = FindFieldSlow(
self, caller, field_index, /*is_static=*/ false, IsInstructionIPut(opcode)); if (resolved_field == nullptr) {
DCHECK(self->IsExceptionPending()); return0;
} // Only update hotness for slow lookups.
UpdateHotness(caller);
}
// For iput-object, try to resolve the field type even if we were not requested to. // Only if the field type is successfully resolved can we update the cache. If we // fail to resolve the type, we clear the exception to keep interpreter // semantics of not throwing when null is stored. bool update_cache = true; if (opcode == Instruction::IPUT_OBJECT &&
caller->GetDeclaringClass()->HasTypeChecksFailure() &&
resolved_field->ResolveType() == nullptr) {
DCHECK(self->IsExceptionPending()); if (registers[inst->VRegA_22c()] != 0u) { return0;
}
self->ClearException();
update_cache = false;
}
if (resolved_field->IsVolatile()) { // Don't cache for a volatile field, and return a negative offset as marker // of volatile. return -resolved_field->GetOffset().Uint32Value();
} if (update_cache) {
UpdateCache(self, dex_pc_ptr, resolved_field->GetOffset().Uint32Value());
} return resolved_field->GetOffset().Uint32Value();
}
// In release mode, this is just a simple load. // In debug mode, this checks that we're using the correct instruction format.
dex::TypeIndex index = dex::TypeIndex(
(opcode == Instruction::CHECK_CAST || opcode == Instruction::CONST_CLASS)
? inst->VRegB_21c()
: inst->VRegC_22c());
gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); if (UNLIKELY(c->IsStringClass())) { // We don't cache the class for strings as we need to special case their // allocation. return mirror::String::AllocEmptyString(self, allocator_type).Ptr();
} else { if (!c->IsFinalizable() && c->IsInstantiable()) { // Cache non-finalizable classes for next calls.
UpdateCache(self, dex_pc_ptr, c.Ptr());
} return AllocObjectFromCode(c, self, allocator_type).Ptr();
}
}
extern"C" mirror::Object* NterpLoadObject(Thread* self, ArtMethod* caller, uint16_t* dex_pc_ptr)
REQUIRES_SHARED(Locks::mutator_lock_) { const Instruction* inst = Instruction::At(dex_pc_ptr);
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); switch (inst->Opcode()) { case Instruction::CONST_STRING: case Instruction::CONST_STRING_JUMBO: {
UpdateHotness(caller);
dex::StringIndex string_index(
(inst->Opcode() == Instruction::CONST_STRING)
? inst->VRegB_21c()
: inst->VRegB_31c());
ObjPtr<mirror::String> str = class_linker->ResolveString(string_index, caller); if (str == nullptr) {
DCHECK(self->IsExceptionPending()); return nullptr;
}
UpdateCache(self, dex_pc_ptr, str.Ptr()); return str.Ptr();
} case Instruction::CONST_METHOD_HANDLE: { // Don't cache: we don't expect this to be performance sensitive, and we // don't want the cache to conflict with a performance sensitive entry. return class_linker->ResolveMethodHandle(self, inst->VRegB_21c(), caller).Ptr();
} case Instruction::CONST_METHOD_TYPE: { // Don't cache: we don't expect this to be performance sensitive, and we // don't want the cache to conflict with a performance sensitive entry. return class_linker->ResolveMethodType(
self, dex::ProtoIndex(inst->VRegB_21c()), caller).Ptr();
} default:
LOG(FATAL) << "Unreachable";
} return nullptr;
}
extern"C" jit::OsrData* NterpHotMethod(ArtMethod* method, uint16_t* dex_pc_ptr, uint32_t* vregs)
REQUIRES_SHARED(Locks::mutator_lock_) { // It is important this method is not suspended because it can be called on // method entry and async deoptimization does not expect runtime methods other than the // suspend entrypoint before executing the first instruction of a Java // method.
ScopedAssertNoThreadSuspension sants("In nterp");
Runtime* runtime = Runtime::Current(); if (method->IsMemorySharedMethod()) { if (!method->IsIntrinsic()) { // Intrinsics are special and will be considered hot from the first call.
DCHECK_EQ(Thread::Current()->GetSharedMethodHotness(), 0u);
Thread::Current()->ResetSharedMethodHotness();
}
} else { // Move the counter to the initial threshold in case we have to re-JIT it.
method->ResetCounter(runtime->GetJITOptions()->GetWarmupThreshold());
}
jit::Jit* jit = runtime->GetJit(); if (jit != nullptr && jit->UseJitCompilation()) { // Nterp passes null on entry where we don't want to OSR. if (dex_pc_ptr != nullptr) { // This could be a loop back edge, check if we can OSR.
CodeItemInstructionAccessor accessor(method->DexInstructions());
uint32_t dex_pc = dex_pc_ptr - accessor.Insns();
jit::OsrData* osr_data = jit->PrepareForOsr(
method->GetInterfaceMethodIfProxy(kRuntimePointerSize), dex_pc, vregs); if (osr_data != nullptr) { return osr_data;
}
}
jit->MaybeEnqueueCompilation(method, Thread::Current());
} return nullptr;
}
/* The keys are guaranteed to be aligned on a 32-bit boundary; *wecantreatthemasanativeintarray.
*/
keys = reinterpret_cast<const int32_t*>(switchData);
/* The entries are guaranteed to be aligned on a 32-bit boundary; *wecantreatthemasanativeintarray.
*/
entries = keys + size;
/* *Binary-searchthroughthearrayofkeys,whichareguaranteedto *besortedlow-to-high.
*/ int lo = 0; int hi = size - 1; while (lo <= hi) { int mid = (lo + hi) >> 1;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.