staticchar TranslateArgToJniShorty(char ch) { // Byte, char, int, short, boolean are treated the same(e.g., Wx registers for arm64) when // generating JNI stub, so their JNI shorty characters are same. // ABCDEFGHIJKLMNOPQRSTUVWXYZ static constexpr char kTranslations[] = ".PPD.F..PJ.L......P......P";
DCHECK_GE(ch, 'A');
DCHECK_LE(ch, 'Z');
DCHECK_NE(kTranslations[ch - 'A'], '.'); return kTranslations[ch - 'A'];
}
staticchar TranslateReturnTypeToJniShorty(char ch, InstructionSet isa = InstructionSet::kNone) { // For all archs, reference type has a different JNI shorty character than others as it needs to // be decoded in stub. // For arm64, small return types need sign-/zero-extended. // For x86_64, small return types need sign-/zero-extended, and RAX needs to be preserved and // restored when thread state changes. // Other archs keeps untranslated for simplicity. // TODO: support riscv64 with an optimized version. // ABCDEFGHIJKLMNOPQRSTUVWXYZ static constexpr char kArm64Translations[] = ".BCP.P..PP.L......S..P...Z"; static constexpr char kX86_64Translations[] = ".BCP.P..RR.L......S..P...Z"; static constexpr char kOtherTranslations[] = ".BCD.F..IJ.L......S..V...Z";
DCHECK_GE(ch, 'A');
DCHECK_LE(ch, 'Z'); switch (isa) { case InstructionSet::kArm64:
DCHECK_NE(kArm64Translations[ch - 'A'], '.'); return kArm64Translations[ch - 'A']; case InstructionSet::kX86_64:
DCHECK_NE(kX86_64Translations[ch - 'A'], '.'); return kX86_64Translations[ch - 'A']; default:
DCHECK_NE(kOtherTranslations[ch - 'A'], '.'); return kOtherTranslations[ch - 'A'];
}
}
static constexpr size_t GetMaxIntLikeRegisterArgs(InstructionSet isa) { switch (isa) { case InstructionSet::kArm64: return arm64::kMaxIntLikeRegisterArguments; case InstructionSet::kX86_64: return x86_64::kMaxIntLikeRegisterArguments; default:
LOG(FATAL) << "Unrecognized isa: " << isa << " for " << __FUNCTION__;
UNREACHABLE();
}
}
static constexpr size_t GetMaxFloatOrDoubleRegisterArgs(InstructionSet isa) { switch (isa) { case InstructionSet::kArm64: return arm64::kMaxFloatOrDoubleRegisterArguments; case InstructionSet::kX86_64: return x86_64::kMaxFloatOrDoubleRegisterArguments; default:
LOG(FATAL) << "Unrecognized isa: " << isa << " for " << __FUNCTION__;
UNREACHABLE();
}
}
template<InstructionSet kIsa>
size_t JniStubKeyOptimizedHash(const JniStubKey& key) { bool is_static = key.Flags() & kAccStatic;
std::string_view shorty = key.Shorty();
size_t result = key.Flags();
result ^= TranslateReturnTypeToJniShorty(shorty[0], kIsa);
constexpr size_t kMaxFloatOrDoubleRegisterArgs = GetMaxFloatOrDoubleRegisterArgs(kIsa);
constexpr size_t kMaxIntLikeRegisterArgs = GetMaxIntLikeRegisterArgs(kIsa);
size_t float_or_double_args = 0; // ArtMethod* and 'Object* this' for non-static method. // ArtMethod* for static method.
size_t int_like_args = is_static ? 1 : 2;
size_t stack_offset = 0; for (char c : shorty.substr(1u)) { bool stack_offset_matters = false;
stack_offset += StackOffset(c); if (IsFloatOrDoubleArg(c)) {
++float_or_double_args; if (float_or_double_args > kMaxFloatOrDoubleRegisterArgs) { // Stack offset matters if we run out of float-like (float, double) argument registers // because the subsequent float-like args should be passed on the stack.
stack_offset_matters = true;
} else { // Floating-point register arguments are not touched when generating JNI stub, so could be // ignored when calculating hash value. continue;
}
} else {
++int_like_args; if (int_like_args > kMaxIntLikeRegisterArgs || IsReferenceArg(c)) { // Stack offset matters if we run out of integer-like (pointer, object, long, int, short, // bool, etc) argument registers because the subsequent integer-like args should be passed // on the stack. It also matters if current arg is reference type because it needs to be // spilled as raw data even if it's in a register.
stack_offset_matters = true;
} elseif (!is_static) { // For non-static method, two managed arguments 'ArtMethod*' and 'Object* this' correspond // to two native arguments 'JNIEnv*' and 'jobject'. So trailing integral (long, int, short, // bool, etc) arguments will remain in the same registers, which do not need any generated // code. // But for static method, we have only one leading managed argument 'ArtMethod*' but two // native arguments 'JNIEnv*' and 'jclass'. So trailing integral arguments are always // shuffled around and affect the generated code. continue;
}
} // int_like_args is needed for reference type because it will determine from which register // we take the value to construct jobject. if (IsReferenceArg(c)) {
result = result * 31u * int_like_args ^ TranslateArgToJniShorty(c);
} else {
result = result * 31u ^ TranslateArgToJniShorty(c);
} if (stack_offset_matters) {
result += stack_offset;
}
} return result;
}
size_t JniStubKeyGenericHash(const JniStubKey& key) {
std::string_view shorty = key.Shorty();
size_t result = key.Flags();
result ^= TranslateReturnTypeToJniShorty(shorty[0]); for (char c : shorty.substr(1u)) {
result = result * 31u ^ TranslateArgToJniShorty(c);
} return result;
}
JniStubKeyHash::JniStubKeyHash(InstructionSet isa) { switch (isa) { case InstructionSet::kArm: case InstructionSet::kThumb2: case InstructionSet::kRiscv64: case InstructionSet::kX86:
hash_func_ = JniStubKeyGenericHash; break; case InstructionSet::kArm64:
hash_func_ = JniStubKeyOptimizedHash<InstructionSet::kArm64>; break; case InstructionSet::kX86_64:
hash_func_ = JniStubKeyOptimizedHash<InstructionSet::kX86_64>; break; case InstructionSet::kNone:
LOG(FATAL) << "No instruction set given for " << __FUNCTION__;
UNREACHABLE();
}
}
if (should_skip) { continue;
} if (TranslateArgToJniShorty(ch_lhs) != TranslateArgToJniShorty(ch_rhs)) { returnfalse;
} if (stack_offset_matters && stack_offset_lhs != stack_offset_rhs) { returnfalse;
} // int_like_args needs to be compared for reference type because it will determine from // which register we take the value to construct jobject. if (IsReferenceArg(ch_lhs) && int_like_args_lhs != int_like_args_rhs) { returnfalse;
} // Passed character comparison.
++i;
++j;
stack_offset_lhs += StackOffset(ch_lhs);
stack_offset_rhs += StackOffset(ch_rhs);
DCHECK_EQ(IsFloatOrDoubleArg(ch_lhs), IsFloatOrDoubleArg(ch_rhs)); if (IsFloatOrDoubleArg(ch_lhs)) {
++float_or_double_args_lhs;
++float_or_double_args_rhs;
} else {
++int_like_args_lhs;
++int_like_args_rhs;
}
} auto remaining_shorty =
i < shorty_lhs.length() ? shorty_lhs.substr(i) : shorty_rhs.substr(j);
size_t float_or_double_args =
i < shorty_lhs.length() ? float_or_double_args_lhs : float_or_double_args_rhs;
size_t int_like_args = i < shorty_lhs.length() ? int_like_args_lhs : int_like_args_rhs; for (char c : remaining_shorty) { if (IsFloatOrDoubleArg(c) && float_or_double_args < kMaxFloatOrDoubleRegisterArgs) {
++float_or_double_args; continue;
} if (!is_static && IsIntegralArg(c) && int_like_args < kMaxIntLikeRegisterArgs) {
++int_like_args; continue;
} returnfalse;
} returntrue;
}
bool JniStubKeyGenericEquals(const JniStubKey& lhs, const JniStubKey& rhs) { if (lhs.Flags() != rhs.Flags()) { returnfalse;
}
std::string_view shorty_lhs = lhs.Shorty();
std::string_view shorty_rhs = rhs.Shorty(); if (TranslateReturnTypeToJniShorty(shorty_lhs[0]) !=
TranslateReturnTypeToJniShorty(shorty_rhs[0])) { returnfalse;
} if (shorty_lhs.length() != shorty_rhs.length()) { returnfalse;
} for (size_t i = 1; i < shorty_lhs.length(); ++i) { if (TranslateArgToJniShorty(shorty_lhs[i]) != TranslateArgToJniShorty(shorty_rhs[i])) { returnfalse;
}
} returntrue;
}
JniStubKeyEquals::JniStubKeyEquals(InstructionSet isa) { switch (isa) { case InstructionSet::kArm: case InstructionSet::kThumb2: case InstructionSet::kRiscv64: case InstructionSet::kX86:
equals_func_ = JniStubKeyGenericEquals; break; case InstructionSet::kArm64:
equals_func_ = JniStubKeyOptimizedEquals<InstructionSet::kArm64>; break; case InstructionSet::kX86_64:
equals_func_ = JniStubKeyOptimizedEquals<InstructionSet::kX86_64>; break; case InstructionSet::kNone:
LOG(FATAL) << "No instruction set given for " << __FUNCTION__;
UNREACHABLE();
}
}
} // namespace art
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-06-29)
¤
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.