diff options
| -rw-r--r-- | dex2oat/linker/image_writer.cc | 3 | ||||
| -rw-r--r-- | libartbase/base/zip_archive.h | 3 | ||||
| -rw-r--r-- | runtime/arch/arm/instruction_set_features_arm.cc | 1 | ||||
| -rw-r--r-- | runtime/arch/arm64/instruction_set_features_arm64.cc | 254 | ||||
| -rw-r--r-- | runtime/arch/arm64/instruction_set_features_arm64.h | 43 | ||||
| -rw-r--r-- | runtime/arch/arm64/instruction_set_features_arm64_test.cc | 116 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 137 | ||||
| -rw-r--r-- | runtime/class_linker.h | 2 | ||||
| -rw-r--r-- | runtime/class_table.cc | 8 | ||||
| -rw-r--r-- | runtime/entrypoints/entrypoint_utils-inl.h | 8 | ||||
| -rw-r--r-- | runtime/gc/space/image_space.cc | 20 | ||||
| -rw-r--r-- | runtime/intern_table-inl.h | 62 | ||||
| -rw-r--r-- | runtime/intern_table.cc | 40 | ||||
| -rw-r--r-- | runtime/intern_table.h | 128 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter_cache.h | 22 | ||||
| -rw-r--r-- | runtime/runtime.cc | 6 | ||||
| -rw-r--r-- | runtime/thread.cc | 19 |
17 files changed, 662 insertions, 210 deletions
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 2eb562ced6..6410c7a6d9 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -53,6 +53,7 @@ #include "handle_scope-inl.h" #include "image.h" #include "imt_conflict_table.h" +#include "intern_table-inl.h" #include "jni/jni_internal.h" #include "linear_alloc.h" #include "lock_word.h" @@ -2610,7 +2611,7 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { // the VisitRoots() will update the memory directly rather than the copies. // This also relies on visit roots not doing any verification which could fail after we update // the roots to be the image addresses. - temp_intern_table.AddTableFromMemory(intern_table_memory_ptr); + temp_intern_table.AddTableFromMemory(intern_table_memory_ptr, VoidFunctor()); CHECK_EQ(temp_intern_table.Size(), intern_table->Size()); temp_intern_table.VisitRoots(&root_visitor, kVisitRootFlagAllRoots); // Record relocations. (The root visitor does not get to see the slot addresses.) diff --git a/libartbase/base/zip_archive.h b/libartbase/base/zip_archive.h index 8fc8b54d2c..d326a9e9d7 100644 --- a/libartbase/base/zip_archive.h +++ b/libartbase/base/zip_archive.h @@ -30,8 +30,9 @@ #include "unix_file/random_access_file.h" // system/core/zip_archive definitions. +struct ZipArchive; struct ZipEntry; -typedef void* ZipArchiveHandle; +typedef ZipArchive* ZipArchiveHandle; namespace art { diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc index 608999b3bf..e97d2cbd92 100644 --- a/runtime/arch/arm/instruction_set_features_arm.cc +++ b/runtime/arch/arm/instruction_set_features_arm.cc @@ -51,6 +51,7 @@ ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromVariant( "cortex-a72", "cortex-a73", "cortex-a75", + "cortex-a76", "exynos-m1", "denver", "kryo" diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc index d0f61c946c..7796ca7745 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64.cc +++ b/runtime/arch/arm64/instruction_set_features_arm64.cc @@ -16,6 +16,11 @@ #include "instruction_set_features_arm64.h" +#if defined(ART_TARGET_ANDROID) && defined(__aarch64__) +#include <asm/hwcap.h> +#include <sys/auxv.h> +#endif + #include <fstream> #include <sstream> @@ -31,6 +36,10 @@ using android::base::StringPrintf; Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromVariant( const std::string& variant, std::string* error_msg) { + // The CPU variant string is passed to ART through --instruction-set-variant option. + // During build, such setting is from TARGET_CPU_VARIANT in device BoardConfig.mk, for example: + // TARGET_CPU_VARIANT := cortex-a75 + // Look for variants that need a fix for a53 erratum 835769. static const char* arm64_variants_with_a53_835769_bug[] = { // Pessimistically assume all generic CPUs are cortex-a53. @@ -39,14 +48,70 @@ Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromVariant( "cortex-a53", "cortex-a53.a57", "cortex-a53.a72", - // Pessimistically assume all "big" cortex CPUs are paired with a cortex-a53. + // Pessimistically assume following "big" cortex CPUs are paired with a cortex-a53. "cortex-a57", "cortex-a72", "cortex-a73", }; + + static const char* arm64_variants_with_crc[] = { + "default", + "generic", + "kryo", + "exynos-m1", + "exynos-m2", + "exynos-m3", + "cortex-a35", + "cortex-a53", + "cortex-a53.a57", + "cortex-a53.a72", + "cortex-a57", + "cortex-a72", + "cortex-a73", + "cortex-a55", + "cortex-a75", + "cortex-a76", + }; + + static const char* arm64_variants_with_lse[] = { + "cortex-a55", + "cortex-a75", + "cortex-a76", + }; + + static const char* arm64_variants_with_fp16[] = { + "cortex-a55", + "cortex-a75", + "cortex-a76", + }; + + static const char* arm64_variants_with_dotprod[] = { + "cortex-a55", + "cortex-a75", + "cortex-a76", + }; + bool needs_a53_835769_fix = FindVariantInArray(arm64_variants_with_a53_835769_bug, arraysize(arm64_variants_with_a53_835769_bug), variant); + // The variants that need a fix for 843419 are the same that need a fix for 835769. + bool needs_a53_843419_fix = needs_a53_835769_fix; + + bool has_crc = FindVariantInArray(arm64_variants_with_crc, + arraysize(arm64_variants_with_crc), + variant); + + bool has_lse = FindVariantInArray(arm64_variants_with_lse, + arraysize(arm64_variants_with_lse), + variant); + + bool has_fp16 = FindVariantInArray(arm64_variants_with_fp16, + arraysize(arm64_variants_with_fp16), + variant); + + bool has_dotprod = FindVariantInArray(arm64_variants_with_dotprod, + arraysize(arm64_variants_with_dotprod), + variant); if (!needs_a53_835769_fix) { // Check to see if this is an expected variant. @@ -54,6 +119,7 @@ Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromVariant( "cortex-a35", "cortex-a55", "cortex-a75", + "cortex-a76", "exynos-m1", "exynos-m2", "exynos-m3", @@ -68,31 +134,91 @@ Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromVariant( } } - // The variants that need a fix for 843419 are the same that need a fix for 835769. - bool needs_a53_843419_fix = needs_a53_835769_fix; - - return Arm64FeaturesUniquePtr( - new Arm64InstructionSetFeatures(needs_a53_835769_fix, needs_a53_843419_fix)); + return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(needs_a53_835769_fix, + needs_a53_843419_fix, + has_crc, + has_lse, + has_fp16, + has_dotprod)); } Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromBitmap(uint32_t bitmap) { bool is_a53 = (bitmap & kA53Bitfield) != 0; - return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(is_a53, is_a53)); + bool has_crc = (bitmap & kCRCBitField) != 0; + bool has_lse = (bitmap & kLSEBitField) != 0; + bool has_fp16 = (bitmap & kFP16BitField) != 0; + bool has_dotprod = (bitmap & kDotProdBitField) != 0; + return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(is_a53, + is_a53, + has_crc, + has_lse, + has_fp16, + has_dotprod)); } Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromCppDefines() { - const bool is_a53 = true; // Pessimistically assume all ARM64s are A53s. - return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(is_a53, is_a53)); + // For more details about ARM feature macros, refer to + // Arm C Language Extensions Documentation (ACLE). + // https://developer.arm.com/docs/101028/latest + bool needs_a53_835769_fix = false; + bool needs_a53_843419_fix = needs_a53_835769_fix; + bool has_crc = false; + bool has_lse = false; + bool has_fp16 = false; + bool has_dotprod = false; + +#if defined (__ARM_FEATURE_CRC32) + has_crc = true; +#endif + +#if defined (__ARM_ARCH_8_1A__) || defined (__ARM_ARCH_8_2A__) + // There is no specific ACLE macro defined for ARMv8.1 LSE features. + has_lse = true; +#endif + +#if defined (__ARM_FEATURE_FP16_SCALAR_ARITHMETIC) || defined (__ARM_FEATURE_FP16_VECTOR_ARITHMETIC) + has_fp16 = true; +#endif + +#if defined (__ARM_FEATURE_DOTPROD) + has_dotprod = true; +#endif + + return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(needs_a53_835769_fix, + needs_a53_843419_fix, + has_crc, + has_lse, + has_fp16, + has_dotprod)); } Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromCpuInfo() { - const bool is_a53 = true; // Conservative default. - return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(is_a53, is_a53)); + UNIMPLEMENTED(WARNING); + return FromCppDefines(); } Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromHwcap() { - const bool is_a53 = true; // Pessimistically assume all ARM64s are A53s. - return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(is_a53, is_a53)); + bool needs_a53_835769_fix = false; // No HWCAP for this. + bool needs_a53_843419_fix = false; // No HWCAP for this. + bool has_crc = false; + bool has_lse = false; + bool has_fp16 = false; + bool has_dotprod = false; + +#if defined(ART_TARGET_ANDROID) && defined(__aarch64__) + uint64_t hwcaps = getauxval(AT_HWCAP); + has_crc = hwcaps & HWCAP_CRC32 ? true : false; + has_lse = hwcaps & HWCAP_ATOMICS ? true : false; + has_fp16 = hwcaps & HWCAP_FPHP ? true : false; + has_dotprod = hwcaps & HWCAP_ASIMDDP ? true : false; +#endif + + return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(needs_a53_835769_fix, + needs_a53_843419_fix, + has_crc, + has_lse, + has_fp16, + has_dotprod)); } Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromAssembly() { @@ -106,11 +232,33 @@ bool Arm64InstructionSetFeatures::Equals(const InstructionSetFeatures* other) co } const Arm64InstructionSetFeatures* other_as_arm64 = other->AsArm64InstructionSetFeatures(); return fix_cortex_a53_835769_ == other_as_arm64->fix_cortex_a53_835769_ && - fix_cortex_a53_843419_ == other_as_arm64->fix_cortex_a53_843419_; + fix_cortex_a53_843419_ == other_as_arm64->fix_cortex_a53_843419_ && + has_crc_ == other_as_arm64->has_crc_ && + has_lse_ == other_as_arm64->has_lse_ && + has_fp16_ == other_as_arm64->has_fp16_ && + has_dotprod_ == other_as_arm64->has_dotprod_; +} + +bool Arm64InstructionSetFeatures::HasAtLeast(const InstructionSetFeatures* other) const { + if (InstructionSet::kArm64 != other->GetInstructionSet()) { + return false; + } + // Currently 'default' feature is cortex-a53 with fixes 835769 and 843419. + // Newer CPUs are not required to have such features, + // so these two a53 fix features are not tested for HasAtLeast. + const Arm64InstructionSetFeatures* other_as_arm64 = other->AsArm64InstructionSetFeatures(); + return (has_crc_ || !other_as_arm64->has_crc_) + && (has_lse_ || !other_as_arm64->has_lse_) + && (has_fp16_ || !other_as_arm64->has_fp16_) + && (has_dotprod_ || !other_as_arm64->has_dotprod_); } uint32_t Arm64InstructionSetFeatures::AsBitmap() const { - return (fix_cortex_a53_835769_ ? kA53Bitfield : 0); + return (fix_cortex_a53_835769_ ? kA53Bitfield : 0) + | (has_crc_ ? kCRCBitField : 0) + | (has_lse_ ? kLSEBitField: 0) + | (has_fp16_ ? kFP16BitField: 0) + | (has_dotprod_ ? kDotProdBitField : 0); } std::string Arm64InstructionSetFeatures::GetFeatureString() const { @@ -120,26 +268,100 @@ std::string Arm64InstructionSetFeatures::GetFeatureString() const { } else { result += "-a53"; } + if (has_crc_) { + result += ",crc"; + } else { + result += ",-crc"; + } + if (has_lse_) { + result += ",lse"; + } else { + result += ",-lse"; + } + if (has_fp16_) { + result += ",fp16"; + } else { + result += ",-fp16"; + } + if (has_dotprod_) { + result += ",dotprod"; + } else { + result += ",-dotprod"; + } return result; } std::unique_ptr<const InstructionSetFeatures> Arm64InstructionSetFeatures::AddFeaturesFromSplitString( const std::vector<std::string>& features, std::string* error_msg) const { + // This 'features' string is from '--instruction-set-features=' option in ART. + // These ARMv8.x feature strings align with those introduced in other compilers: + // https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html + // User can also use armv8.x-a to select group of features: + // armv8.1-a is equivalent to crc,lse + // armv8.2-a is equivalent to crc,lse,fp16 + // armv8.3-a is equivalent to crc,lse,fp16 + // armv8.4-a is equivalent to crc,lse,fp16,dotprod + // For detailed optional & mandatory features support in armv8.x-a, + // please refer to section 'A1.7 ARMv8 architecture extensions' in + // ARM Architecture Reference Manual ARMv8 document: + // https://developer.arm.com/products/architecture/cpu-architecture/a-profile/docs/ddi0487/latest/ + // arm-architecture-reference-manual-armv8-for-armv8-a-architecture-profile/ bool is_a53 = fix_cortex_a53_835769_; + bool has_crc = has_crc_; + bool has_lse = has_lse_; + bool has_fp16 = has_fp16_; + bool has_dotprod = has_dotprod_; for (auto i = features.begin(); i != features.end(); i++) { std::string feature = android::base::Trim(*i); if (feature == "a53") { is_a53 = true; } else if (feature == "-a53") { is_a53 = false; + } else if (feature == "crc") { + has_crc = true; + } else if (feature == "-crc") { + has_crc = false; + } else if (feature == "lse") { + has_lse = true; + } else if (feature == "-lse") { + has_lse = false; + } else if (feature == "fp16") { + has_fp16 = true; + } else if (feature == "-fp16") { + has_fp16 = false; + } else if (feature == "dotprod") { + has_dotprod = true; + } else if (feature == "-dotprod") { + has_dotprod = false; + } else if (feature == "armv8.1-a") { + has_crc = true; + has_lse = true; + } else if (feature == "armv8.2-a") { + has_crc = true; + has_lse = true; + has_fp16 = true; + } else if (feature == "armv8.3-a") { + has_crc = true; + has_lse = true; + has_fp16 = true; + } else if (feature == "armv8.4-a") { + has_crc = true; + has_lse = true; + has_fp16 = true; + has_dotprod = true; } else { *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str()); return nullptr; } } return std::unique_ptr<const InstructionSetFeatures>( - new Arm64InstructionSetFeatures(is_a53, is_a53)); + new Arm64InstructionSetFeatures(is_a53, // erratum 835769 + is_a53, // erratum 843419 + has_crc, + has_lse, + has_fp16, + has_dotprod)); } } // namespace art diff --git a/runtime/arch/arm64/instruction_set_features_arm64.h b/runtime/arch/arm64/instruction_set_features_arm64.h index 163a2d8eba..4ec8fa2362 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64.h +++ b/runtime/arch/arm64/instruction_set_features_arm64.h @@ -49,6 +49,11 @@ class Arm64InstructionSetFeatures final : public InstructionSetFeatures { bool Equals(const InstructionSetFeatures* other) const override; + // Note that newer CPUs do not have a53 erratum 835769 and 843419, + // so the two a53 fix features (fix_cortex_a53_835769 and fix_cortex_a53_843419) + // are not tested for HasAtLeast. + bool HasAtLeast(const InstructionSetFeatures* other) const override; + InstructionSet GetInstructionSet() const override { return InstructionSet::kArm64; } @@ -68,6 +73,23 @@ class Arm64InstructionSetFeatures final : public InstructionSetFeatures { return fix_cortex_a53_843419_; } + bool HasCRC() const { + return has_crc_; + } + + bool HasLSE() const { + return has_lse_; + } + + bool HasFP16() const { + return has_fp16_; + } + + // Are Dot Product instructions (UDOT/SDOT) available? + bool HasDotProd() const { + return has_dotprod_; + } + virtual ~Arm64InstructionSetFeatures() {} protected: @@ -77,19 +99,36 @@ class Arm64InstructionSetFeatures final : public InstructionSetFeatures { std::string* error_msg) const override; private: - Arm64InstructionSetFeatures(bool needs_a53_835769_fix, bool needs_a53_843419_fix) + Arm64InstructionSetFeatures(bool needs_a53_835769_fix, + bool needs_a53_843419_fix, + bool has_crc, + bool has_lse, + bool has_fp16, + bool has_dotprod) : InstructionSetFeatures(), fix_cortex_a53_835769_(needs_a53_835769_fix), - fix_cortex_a53_843419_(needs_a53_843419_fix) { + fix_cortex_a53_843419_(needs_a53_843419_fix), + has_crc_(has_crc), + has_lse_(has_lse), + has_fp16_(has_fp16), + has_dotprod_(has_dotprod) { } // Bitmap positions for encoding features as a bitmap. enum { kA53Bitfield = 1 << 0, + kCRCBitField = 1 << 1, + kLSEBitField = 1 << 2, + kFP16BitField = 1 << 3, + kDotProdBitField = 1 << 4, }; const bool fix_cortex_a53_835769_; const bool fix_cortex_a53_843419_; + const bool has_crc_; // optional in ARMv8.0, mandatory in ARMv8.1. + const bool has_lse_; // ARMv8.1 Large System Extensions. + const bool has_fp16_; // ARMv8.2 FP16 extensions. + const bool has_dotprod_; // optional in ARMv8.2, mandatory in ARMv8.4. DISALLOW_COPY_AND_ASSIGN(Arm64InstructionSetFeatures); }; diff --git a/runtime/arch/arm64/instruction_set_features_arm64_test.cc b/runtime/arch/arm64/instruction_set_features_arm64_test.cc index b946f4f637..99d6b0dc59 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64_test.cc +++ b/runtime/arch/arm64/instruction_set_features_arm64_test.cc @@ -28,32 +28,37 @@ TEST(Arm64InstructionSetFeaturesTest, Arm64Features) { ASSERT_TRUE(arm64_features.get() != nullptr) << error_msg; EXPECT_EQ(arm64_features->GetInstructionSet(), InstructionSet::kArm64); EXPECT_TRUE(arm64_features->Equals(arm64_features.get())); - EXPECT_STREQ("a53", arm64_features->GetFeatureString().c_str()); - EXPECT_EQ(arm64_features->AsBitmap(), 1U); + EXPECT_STREQ("a53,crc,-lse,-fp16,-dotprod", arm64_features->GetFeatureString().c_str()); + EXPECT_EQ(arm64_features->AsBitmap(), 3U); std::unique_ptr<const InstructionSetFeatures> cortex_a57_features( InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a57", &error_msg)); ASSERT_TRUE(cortex_a57_features.get() != nullptr) << error_msg; EXPECT_EQ(cortex_a57_features->GetInstructionSet(), InstructionSet::kArm64); EXPECT_TRUE(cortex_a57_features->Equals(cortex_a57_features.get())); - EXPECT_STREQ("a53", cortex_a57_features->GetFeatureString().c_str()); - EXPECT_EQ(cortex_a57_features->AsBitmap(), 1U); + EXPECT_TRUE(cortex_a57_features->HasAtLeast(arm64_features.get())); + EXPECT_STREQ("a53,crc,-lse,-fp16,-dotprod", cortex_a57_features->GetFeatureString().c_str()); + EXPECT_EQ(cortex_a57_features->AsBitmap(), 3U); std::unique_ptr<const InstructionSetFeatures> cortex_a73_features( InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a73", &error_msg)); ASSERT_TRUE(cortex_a73_features.get() != nullptr) << error_msg; EXPECT_EQ(cortex_a73_features->GetInstructionSet(), InstructionSet::kArm64); EXPECT_TRUE(cortex_a73_features->Equals(cortex_a73_features.get())); - EXPECT_STREQ("a53", cortex_a73_features->GetFeatureString().c_str()); - EXPECT_EQ(cortex_a73_features->AsBitmap(), 1U); + EXPECT_TRUE(cortex_a73_features->AsArm64InstructionSetFeatures()->HasCRC()); + EXPECT_FALSE(cortex_a73_features->AsArm64InstructionSetFeatures()->HasLSE()); + EXPECT_FALSE(cortex_a73_features->AsArm64InstructionSetFeatures()->HasFP16()); + EXPECT_FALSE(cortex_a73_features->AsArm64InstructionSetFeatures()->HasDotProd()); + EXPECT_STREQ("a53,crc,-lse,-fp16,-dotprod", cortex_a73_features->GetFeatureString().c_str()); + EXPECT_EQ(cortex_a73_features->AsBitmap(), 3U); std::unique_ptr<const InstructionSetFeatures> cortex_a35_features( InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a35", &error_msg)); ASSERT_TRUE(cortex_a35_features.get() != nullptr) << error_msg; EXPECT_EQ(cortex_a35_features->GetInstructionSet(), InstructionSet::kArm64); EXPECT_TRUE(cortex_a35_features->Equals(cortex_a35_features.get())); - EXPECT_STREQ("-a53", cortex_a35_features->GetFeatureString().c_str()); - EXPECT_EQ(cortex_a35_features->AsBitmap(), 0U); + EXPECT_STREQ("-a53,crc,-lse,-fp16,-dotprod", cortex_a35_features->GetFeatureString().c_str()); + EXPECT_EQ(cortex_a35_features->AsBitmap(), 2U); std::unique_ptr<const InstructionSetFeatures> kryo_features( InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "kryo", &error_msg)); @@ -62,28 +67,107 @@ TEST(Arm64InstructionSetFeaturesTest, Arm64Features) { EXPECT_TRUE(kryo_features->Equals(kryo_features.get())); EXPECT_TRUE(kryo_features->Equals(cortex_a35_features.get())); EXPECT_FALSE(kryo_features->Equals(cortex_a57_features.get())); - EXPECT_STREQ("-a53", kryo_features->GetFeatureString().c_str()); - EXPECT_EQ(kryo_features->AsBitmap(), 0U); + EXPECT_STREQ("-a53,crc,-lse,-fp16,-dotprod", kryo_features->GetFeatureString().c_str()); + EXPECT_EQ(kryo_features->AsBitmap(), 2U); std::unique_ptr<const InstructionSetFeatures> cortex_a55_features( InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a55", &error_msg)); ASSERT_TRUE(cortex_a55_features.get() != nullptr) << error_msg; EXPECT_EQ(cortex_a55_features->GetInstructionSet(), InstructionSet::kArm64); EXPECT_TRUE(cortex_a55_features->Equals(cortex_a55_features.get())); - EXPECT_TRUE(cortex_a55_features->Equals(cortex_a35_features.get())); + EXPECT_FALSE(cortex_a55_features->Equals(cortex_a35_features.get())); EXPECT_FALSE(cortex_a55_features->Equals(cortex_a57_features.get())); - EXPECT_STREQ("-a53", cortex_a55_features->GetFeatureString().c_str()); - EXPECT_EQ(cortex_a55_features->AsBitmap(), 0U); + EXPECT_TRUE(cortex_a35_features->HasAtLeast(arm64_features.get())); + EXPECT_STREQ("-a53,crc,lse,fp16,dotprod", cortex_a55_features->GetFeatureString().c_str()); + EXPECT_EQ(cortex_a55_features->AsBitmap(), 30U); std::unique_ptr<const InstructionSetFeatures> cortex_a75_features( InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a75", &error_msg)); ASSERT_TRUE(cortex_a75_features.get() != nullptr) << error_msg; EXPECT_EQ(cortex_a75_features->GetInstructionSet(), InstructionSet::kArm64); EXPECT_TRUE(cortex_a75_features->Equals(cortex_a75_features.get())); - EXPECT_TRUE(cortex_a75_features->Equals(cortex_a35_features.get())); + EXPECT_FALSE(cortex_a75_features->Equals(cortex_a35_features.get())); EXPECT_FALSE(cortex_a75_features->Equals(cortex_a57_features.get())); - EXPECT_STREQ("-a53", cortex_a75_features->GetFeatureString().c_str()); - EXPECT_EQ(cortex_a75_features->AsBitmap(), 0U); + EXPECT_TRUE(cortex_a75_features->HasAtLeast(arm64_features.get())); + EXPECT_TRUE(cortex_a75_features->HasAtLeast(cortex_a55_features.get())); + EXPECT_FALSE(cortex_a35_features->HasAtLeast(cortex_a75_features.get())); + EXPECT_FALSE(cortex_a75_features->AsArm64InstructionSetFeatures()->NeedFixCortexA53_835769()); + EXPECT_FALSE(cortex_a75_features->AsArm64InstructionSetFeatures()->NeedFixCortexA53_843419()); + EXPECT_TRUE(cortex_a75_features->AsArm64InstructionSetFeatures()->HasCRC()); + EXPECT_TRUE(cortex_a75_features->AsArm64InstructionSetFeatures()->HasLSE()); + EXPECT_TRUE(cortex_a75_features->AsArm64InstructionSetFeatures()->HasFP16()); + EXPECT_TRUE(cortex_a75_features->AsArm64InstructionSetFeatures()->HasDotProd()); + EXPECT_STREQ("-a53,crc,lse,fp16,dotprod", cortex_a75_features->GetFeatureString().c_str()); + EXPECT_EQ(cortex_a75_features->AsBitmap(), 30U); + + std::unique_ptr<const InstructionSetFeatures> cortex_a76_features( + InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a76", &error_msg)); + ASSERT_TRUE(cortex_a76_features.get() != nullptr) << error_msg; + EXPECT_EQ(cortex_a76_features->GetInstructionSet(), InstructionSet::kArm64); + EXPECT_TRUE(cortex_a76_features->Equals(cortex_a76_features.get())); + EXPECT_FALSE(cortex_a76_features->Equals(cortex_a35_features.get())); + EXPECT_FALSE(cortex_a76_features->Equals(cortex_a57_features.get())); + EXPECT_TRUE(cortex_a76_features->Equals(cortex_a75_features.get())); + EXPECT_TRUE(cortex_a76_features->HasAtLeast(arm64_features.get())); + EXPECT_TRUE(cortex_a76_features->HasAtLeast(cortex_a55_features.get())); + EXPECT_FALSE(cortex_a35_features->HasAtLeast(cortex_a76_features.get())); + EXPECT_FALSE(cortex_a76_features->AsArm64InstructionSetFeatures()->NeedFixCortexA53_835769()); + EXPECT_FALSE(cortex_a76_features->AsArm64InstructionSetFeatures()->NeedFixCortexA53_843419()); + EXPECT_TRUE(cortex_a76_features->AsArm64InstructionSetFeatures()->HasCRC()); + EXPECT_TRUE(cortex_a76_features->AsArm64InstructionSetFeatures()->HasLSE()); + EXPECT_TRUE(cortex_a76_features->AsArm64InstructionSetFeatures()->HasFP16()); + EXPECT_TRUE(cortex_a76_features->AsArm64InstructionSetFeatures()->HasDotProd()); + EXPECT_STREQ("-a53,crc,lse,fp16,dotprod", cortex_a76_features->GetFeatureString().c_str()); + EXPECT_EQ(cortex_a76_features->AsBitmap(), 30U); +} + +TEST(Arm64InstructionSetFeaturesTest, Arm64AddFeaturesFromString) { + std::string error_msg; + std::unique_ptr<const InstructionSetFeatures> base_features( + InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "generic", &error_msg)); + ASSERT_TRUE(base_features.get() != nullptr) << error_msg; + + // Build features for a Cortex-A76 processor (with ARMv8.2 and Dot Product exentions support). + std::unique_ptr<const InstructionSetFeatures> a76_features( + base_features->AddFeaturesFromString("-a53,armv8.2-a,dotprod", &error_msg)); + ASSERT_TRUE(a76_features.get() != nullptr) << error_msg; + ASSERT_EQ(a76_features->GetInstructionSet(), InstructionSet::kArm64); + EXPECT_TRUE(a76_features->Equals(a76_features.get())); + EXPECT_FALSE(a76_features->AsArm64InstructionSetFeatures()->NeedFixCortexA53_835769()); + EXPECT_FALSE(a76_features->AsArm64InstructionSetFeatures()->NeedFixCortexA53_843419()); + EXPECT_TRUE(a76_features->AsArm64InstructionSetFeatures()->HasCRC()); + EXPECT_TRUE(a76_features->AsArm64InstructionSetFeatures()->HasLSE()); + EXPECT_TRUE(a76_features->AsArm64InstructionSetFeatures()->HasFP16()); + EXPECT_TRUE(a76_features->AsArm64InstructionSetFeatures()->HasDotProd()); + EXPECT_STREQ("-a53,crc,lse,fp16,dotprod", a76_features->GetFeatureString().c_str()); + EXPECT_EQ(a76_features->AsBitmap(), 30U); + + // Build features for a default ARM64 processor. + std::unique_ptr<const InstructionSetFeatures> generic_features( + base_features->AddFeaturesFromString("default", &error_msg)); + ASSERT_TRUE(generic_features.get() != nullptr) << error_msg; + ASSERT_EQ(generic_features->GetInstructionSet(), InstructionSet::kArm64); + EXPECT_TRUE(generic_features->Equals(generic_features.get())); + EXPECT_FALSE(generic_features->AsArm64InstructionSetFeatures()->HasLSE()); + EXPECT_FALSE(generic_features->AsArm64InstructionSetFeatures()->HasFP16()); + EXPECT_FALSE(generic_features->AsArm64InstructionSetFeatures()->HasDotProd()); + EXPECT_STREQ("a53,crc,-lse,-fp16,-dotprod", generic_features->GetFeatureString().c_str()); + EXPECT_EQ(generic_features->AsBitmap(), 3U); + + // Build features for a ARM64 processor that supports up to ARMv8.2. + std::unique_ptr<const InstructionSetFeatures> armv8_2a_cpu_features( + base_features->AddFeaturesFromString("-a53,armv8.2-a", &error_msg)); + ASSERT_TRUE(armv8_2a_cpu_features.get() != nullptr) << error_msg; + ASSERT_EQ(armv8_2a_cpu_features->GetInstructionSet(), InstructionSet::kArm64); + EXPECT_TRUE(armv8_2a_cpu_features->Equals(armv8_2a_cpu_features.get())); + EXPECT_FALSE(armv8_2a_cpu_features->AsArm64InstructionSetFeatures()->NeedFixCortexA53_835769()); + EXPECT_FALSE(armv8_2a_cpu_features->AsArm64InstructionSetFeatures()->NeedFixCortexA53_843419()); + EXPECT_TRUE(armv8_2a_cpu_features->AsArm64InstructionSetFeatures()->HasCRC()); + EXPECT_TRUE(armv8_2a_cpu_features->AsArm64InstructionSetFeatures()->HasLSE()); + EXPECT_TRUE(armv8_2a_cpu_features->AsArm64InstructionSetFeatures()->HasFP16()); + EXPECT_FALSE(armv8_2a_cpu_features->AsArm64InstructionSetFeatures()->HasDotProd()); + EXPECT_STREQ("-a53,crc,lse,fp16,-dotprod", armv8_2a_cpu_features->GetFeatureString().c_str()); + EXPECT_EQ(armv8_2a_cpu_features->AsBitmap(), 14U); } } // namespace art diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index f778bc5b1c..7549c04b6f 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -79,7 +79,7 @@ #include "image-inl.h" #include "imt_conflict_table.h" #include "imtable-inl.h" -#include "intern_table.h" +#include "intern_table-inl.h" #include "interpreter/interpreter.h" #include "jit/debugger_interface.h" #include "jit/jit.h" @@ -1279,7 +1279,7 @@ bool VerifyStringInterning(gc::space::ImageSpace& space) REQUIRES_SHARED(Locks:: // new_class_set is the set of classes that were read from the class table section in the image. // If there was no class table section, it is null. // Note: using a class here to avoid having to make ClassLinker internals public. -class AppImageClassLoadersAndDexCachesHelper { +class AppImageLoadingHelper { public: static void Update( ClassLinker* class_linker, @@ -1289,9 +1289,17 @@ class AppImageClassLoadersAndDexCachesHelper { ClassTable::ClassSet* new_class_set) REQUIRES(!Locks::dex_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + + static void AddImageInternTable(gc::space::ImageSpace* space) + REQUIRES_SHARED(Locks::mutator_lock_); + + static void UpdateInternStrings( + gc::space::ImageSpace* space, + const SafeMap<mirror::String*, mirror::String*>& intern_remap) + REQUIRES_SHARED(Locks::mutator_lock_); }; -void AppImageClassLoadersAndDexCachesHelper::Update( +void AppImageLoadingHelper::Update( ClassLinker* class_linker, gc::space::ImageSpace* space, Handle<mirror::ClassLoader> class_loader, @@ -1366,57 +1374,96 @@ void AppImageClassLoadersAndDexCachesHelper::Update( } if (ClassLinker::kAppImageMayContainStrings) { - // Iterate over the string reference offsets stored in the image and intern - // the strings they point to. + AddImageInternTable(space); + DCHECK(VerifyStringInterning(*space)); + } - ScopedTrace timing("AppImage:InternString"); - const auto& image_header = space->GetImageHeader(); - const uint8_t* target_base = space->GetMemMap()->Begin(); - const ImageSection& sro_section = image_header.GetImageStringReferenceOffsetsSection(); + if (kVerifyArtMethodDeclaringClasses) { + ScopedTrace timing("AppImage:VerifyDeclaringClasses"); + ReaderMutexLock rmu(self, *Locks::heap_bitmap_lock_); + VerifyDeclaringClassVisitor visitor; + header.VisitPackedArtMethods(&visitor, space->Begin(), kRuntimePointerSize); + } +} - size_t num_string_offsets = sro_section.Size() / sizeof(uint32_t); +void AppImageLoadingHelper::UpdateInternStrings( + gc::space::ImageSpace* space, + const SafeMap<mirror::String*, mirror::String*>& intern_remap) { + const uint8_t* target_base = space->Begin(); + const ImageSection& sro_section = space->GetImageHeader().GetImageStringReferenceOffsetsSection(); + const size_t num_string_offsets = sro_section.Size() / sizeof(uint32_t); + + VLOG(image) + << "ClassLinker:AppImage:InternStrings:imageStringReferenceOffsetCount = " + << num_string_offsets; + + const uint32_t* sro_base = + reinterpret_cast<const uint32_t*>(target_base + sro_section.Offset()); + + for (size_t offset_index = 0; offset_index < num_string_offsets; ++offset_index) { + if (HasNativeRefTag(sro_base[offset_index])) { + void* raw_field_addr = space->Begin() + ClearNativeRefTag(sro_base[offset_index]); + mirror::CompressedReference<mirror::Object>* objref_addr = + reinterpret_cast<mirror::CompressedReference<mirror::Object>*>(raw_field_addr); + mirror::String* referred_string = objref_addr->AsMirrorPtr()->AsString(); + DCHECK(referred_string != nullptr); + + auto it = intern_remap.find(referred_string); + if (it != intern_remap.end()) { + objref_addr->Assign(it->second); + } + } else { + void* raw_field_addr = space->Begin() + sro_base[offset_index]; + mirror::HeapReference<mirror::Object>* objref_addr = + reinterpret_cast<mirror::HeapReference<mirror::Object>*>(raw_field_addr); + mirror::String* referred_string = objref_addr->AsMirrorPtr()->AsString(); + DCHECK(referred_string != nullptr); - if (kIsDebugBuild) { - LOG(INFO) - << "ClassLinker:AppImage:InternStrings:imageStringReferenceOffsetCount = " - << num_string_offsets; + auto it = intern_remap.find(referred_string); + if (it != intern_remap.end()) { + objref_addr->Assign<false>(it->second); + } } + } +} - DCHECK(Runtime::Current()->GetInternTable() != nullptr); - - InternTable& intern_table = *Runtime::Current()->GetInternTable(); - const uint32_t* sro_base = - reinterpret_cast<const uint32_t*>(target_base + sro_section.Offset()); - - for (size_t offset_index = 0; offset_index < num_string_offsets; ++offset_index) { - if (HasNativeRefTag(sro_base[offset_index])) { - void* raw_field_addr = space->Begin() + ClearNativeRefTag(sro_base[offset_index]); - mirror::CompressedReference<mirror::Object>* objref_addr = - reinterpret_cast<mirror::CompressedReference<mirror::Object>*>(raw_field_addr); - mirror::String* referred_string = objref_addr->AsMirrorPtr()->AsString(); - DCHECK(referred_string != nullptr); +void AppImageLoadingHelper::AddImageInternTable(gc::space::ImageSpace* space) { + // Iterate over the string reference offsets stored in the image and intern + // the strings they point to. + ScopedTrace timing("AppImage:InternString"); - objref_addr->Assign(intern_table.InternStrong(referred_string)); + Thread* const self = Thread::Current(); + Runtime* const runtime = Runtime::Current(); + InternTable* const intern_table = runtime->GetInternTable(); + // Add the intern table, removing any conflicts. For conflicts, store the new address in a map + // for faster lookup. + // TODO: Optimize with a bitmap or bloom filter + SafeMap<mirror::String*, mirror::String*> intern_remap; + intern_table->AddImageStringsToTable(space, [&](InternTable::UnorderedSet& interns) + REQUIRES_SHARED(Locks::mutator_lock_) { + VLOG(image) << "AppImage:StringsInInternTable = " << interns.size(); + for (auto it = interns.begin(); it != interns.end(); ) { + ObjPtr<mirror::String> string = it->Read(); + ObjPtr<mirror::String> existing = intern_table->LookupWeak(self, string); + if (existing == nullptr) { + existing = intern_table->LookupStrong(self, string); + } + if (existing != nullptr) { + intern_remap.Put(string.Ptr(), existing.Ptr()); + it = interns.erase(it); } else { - void* raw_field_addr = space->Begin() + sro_base[offset_index]; - mirror::HeapReference<mirror::Object>* objref_addr = - reinterpret_cast<mirror::HeapReference<mirror::Object>*>(raw_field_addr); - mirror::String* referred_string = objref_addr->AsMirrorPtr()->AsString(); - DCHECK(referred_string != nullptr); - - objref_addr->Assign<false>(intern_table.InternStrong(referred_string)); + ++it; } } + }); - DCHECK(VerifyStringInterning(*space)); - } + VLOG(image) << "AppImage:ConflictingInternStrings = " << intern_remap.size(); - if (kVerifyArtMethodDeclaringClasses) { - ScopedTrace timing("AppImage:VerifyDeclaringClasses"); - ReaderMutexLock rmu(self, *Locks::heap_bitmap_lock_); - VerifyDeclaringClassVisitor visitor; - header.VisitPackedArtMethods(&visitor, space->Begin(), kRuntimePointerSize); + // For debug builds, always run the code below to get coverage. + if (kIsDebugBuild || !intern_remap.empty()) { + // Slow path case is when there are conflicting intern strings to fix up. + UpdateInternStrings(space, intern_remap); } } @@ -1907,11 +1954,7 @@ bool ClassLinker::AddImageSpace( VLOG(image) << "Adding class table classes took " << PrettyDuration(NanoTime() - start_time2); } if (app_image) { - AppImageClassLoadersAndDexCachesHelper::Update(this, - space, - class_loader, - dex_caches, - &temp_set); + AppImageLoadingHelper::Update(this, space, class_loader, dex_caches, &temp_set); // Update class loader and resolved strings. If added_class_table is false, the resolved // strings were forwarded UpdateAppImageClassLoadersAndDexCaches. UpdateClassLoaderVisitor visitor(space, class_loader.Get()); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 0e276cd439..996427e7c6 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -1355,7 +1355,7 @@ class ClassLinker { class FindVirtualMethodHolderVisitor; - friend class AppImageClassLoadersAndDexCachesHelper; + friend class AppImageLoadingHelper; friend class ImageDumper; // for DexLock friend struct linker::CompilationHelper; // For Compile in ImageTest. friend class linker::ImageWriter; // for GetClassRoots diff --git a/runtime/class_table.cc b/runtime/class_table.cc index a233357249..8d8e93aab0 100644 --- a/runtime/class_table.cc +++ b/runtime/class_table.cc @@ -57,12 +57,6 @@ mirror::Class* ClassTable::LookupByDescriptor(ObjPtr<mirror::Class> klass) { return nullptr; } -// To take into account http://b/35845221 -#pragma clang diagnostic push -#if __clang_major__ < 4 -#pragma clang diagnostic ignored "-Wunreachable-code" -#endif - mirror::Class* ClassTable::UpdateClass(const char* descriptor, mirror::Class* klass, size_t hash) { WriterMutexLock mu(Thread::Current(), lock_); // Should only be updating latest table. @@ -88,8 +82,6 @@ mirror::Class* ClassTable::UpdateClass(const char* descriptor, mirror::Class* kl return existing; } -#pragma clang diagnostic pop - size_t ClassTable::CountDefiningLoaderClasses(ObjPtr<mirror::ClassLoader> defining_loader, const ClassSet& set) const { size_t count = 0; diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 62b3d001ba..35bfa91aed 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -530,7 +530,13 @@ ALWAYS_INLINE ArtMethod* FindMethodToCall(uint32_t method_idx, UNREACHABLE(); } case kInterface: { - uint32_t imt_index = ImTable::GetImtIndex(resolved_method); + size_t imt_index; + InterpreterCache* tls_cache = self->GetInterpreterCache(); + if (UNLIKELY(!tls_cache->Get(resolved_method, &imt_index))) { + imt_index = ImTable::GetImtIndex(resolved_method); + tls_cache->Set(resolved_method, imt_index); + } + DCHECK_EQ(imt_index, ImTable::GetImtIndex(resolved_method)); PointerSize pointer_size = class_linker->GetImagePointerSize(); ObjPtr<mirror::Class> klass = (*this_object)->GetClass(); ArtMethod* imt_method = klass->GetImt(pointer_size)->Get(imt_index, pointer_size); diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index fa36835bac..16359aca10 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -46,7 +46,7 @@ #include "gc/accounting/space_bitmap-inl.h" #include "image-inl.h" #include "image_space_fs.h" -#include "intern_table.h" +#include "intern_table-inl.h" #include "mirror/class-inl.h" #include "mirror/executable.h" #include "mirror/object-inl.h" @@ -1223,6 +1223,24 @@ class ImageSpace::Loader { FixupRootVisitor root_visitor(boot_image, boot_oat, app_image, app_oat); temp_table.VisitRoots(root_visitor); } + // Fix up the intern table. + const auto& intern_table_section = image_header.GetInternedStringsSection(); + if (intern_table_section.Size() > 0u) { + TimingLogger::ScopedTiming timing("Fixup intern table", &logger); + ScopedObjectAccess soa(Thread::Current()); + // Fixup the pointers in the newly written intern table to contain image addresses. + InternTable temp_intern_table; + // Note that we require that ReadFromMemory does not make an internal copy of the elements + // so that the VisitRoots() will update the memory directly rather than the copies. + FixupRootVisitor root_visitor(boot_image, boot_oat, app_image, app_oat); + temp_intern_table.AddTableFromMemory(target_base + intern_table_section.Offset(), + [&](InternTable::UnorderedSet& strings) + REQUIRES_SHARED(Locks::mutator_lock_) { + for (GcRoot<mirror::String>& root : strings) { + root = GcRoot<mirror::String>(fixup_adapter(root.Read<kWithoutReadBarrier>())); + } + }); + } } if (VLOG_IS_ON(image)) { logger.Dump(LOG_STREAM(INFO)); diff --git a/runtime/intern_table-inl.h b/runtime/intern_table-inl.h new file mode 100644 index 0000000000..d8e5da8209 --- /dev/null +++ b/runtime/intern_table-inl.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 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. + */ + +#ifndef ART_RUNTIME_INTERN_TABLE_INL_H_ +#define ART_RUNTIME_INTERN_TABLE_INL_H_ + +#include "intern_table.h" + +namespace art { + +template <typename Visitor> +inline void InternTable::AddImageStringsToTable(gc::space::ImageSpace* image_space, + const Visitor& visitor) { + DCHECK(image_space != nullptr); + // Only add if we have the interned strings section. + const ImageSection& section = image_space->GetImageHeader().GetInternedStringsSection(); + if (section.Size() > 0) { + AddTableFromMemory(image_space->Begin() + section.Offset(), visitor); + } +} + +template <typename Visitor> +inline size_t InternTable::AddTableFromMemory(const uint8_t* ptr, const Visitor& visitor) { + size_t read_count = 0; + UnorderedSet set(ptr, /*make copy*/false, &read_count); + // Visit the unordered set, may remove elements. + visitor(set); + if (!set.empty()) { + MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); + strong_interns_.AddInternStrings(std::move(set)); + } + return read_count; +} + +inline void InternTable::Table::AddInternStrings(UnorderedSet&& intern_strings) { + static constexpr bool kCheckDuplicates = kIsDebugBuild; + if (kCheckDuplicates) { + for (GcRoot<mirror::String>& string : intern_strings) { + CHECK(Find(string.Read()) == nullptr) + << "Already found " << string.Read()->ToModifiedUtf8() << " in the intern table"; + } + } + // Insert at the front since we add new interns into the back. + tables_.insert(tables_.begin(), std::move(intern_strings)); +} + +} // namespace art + +#endif // ART_RUNTIME_INTERN_TABLE_INL_H_ diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc index c8aaa21589..6fbfbdd539 100644 --- a/runtime/intern_table.cc +++ b/runtime/intern_table.cc @@ -177,18 +177,6 @@ void InternTable::RemoveWeakFromTransaction(ObjPtr<mirror::String> s) { RemoveWeak(s); } -void InternTable::AddImagesStringsToTable(const std::vector<gc::space::ImageSpace*>& image_spaces) { - MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); - for (gc::space::ImageSpace* image_space : image_spaces) { - const ImageHeader* const header = &image_space->GetImageHeader(); - // Check if we have the interned strings section. - const ImageSection& section = header->GetInternedStringsSection(); - if (section.Size() > 0) { - AddTableFromMemoryLocked(image_space->Begin() + section.Offset()); - } - } -} - void InternTable::BroadcastForNewInterns() { Thread* self = Thread::Current(); MutexLock mu(self, *Locks::intern_table_lock_); @@ -303,15 +291,6 @@ void InternTable::SweepInternTableWeaks(IsMarkedVisitor* visitor) { weak_interns_.SweepWeaks(visitor); } -size_t InternTable::AddTableFromMemory(const uint8_t* ptr) { - MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); - return AddTableFromMemoryLocked(ptr); -} - -size_t InternTable::AddTableFromMemoryLocked(const uint8_t* ptr) { - return strong_interns_.AddTableFromMemory(ptr); -} - size_t InternTable::WriteToMemory(uint8_t* ptr) { MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); return strong_interns_.WriteToMemory(ptr); @@ -363,25 +342,6 @@ bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a, } } -size_t InternTable::Table::AddTableFromMemory(const uint8_t* ptr) { - size_t read_count = 0; - UnorderedSet set(ptr, /*make copy*/false, &read_count); - if (set.empty()) { - // Avoid inserting empty sets. - return read_count; - } - // TODO: Disable this for app images if app images have intern tables. - static constexpr bool kCheckDuplicates = kIsDebugBuild; - if (kCheckDuplicates) { - for (GcRoot<mirror::String>& string : set) { - CHECK(Find(string.Read()) == nullptr) << "Already found " << string.Read()->ToModifiedUtf8(); - } - } - // Insert at the front since we add new interns into the back. - tables_.insert(tables_.begin(), std::move(set)); - return read_count; -} - size_t InternTable::Table::WriteToMemory(uint8_t* ptr) { if (tables_.empty()) { return 0; diff --git a/runtime/intern_table.h b/runtime/intern_table.h index 00b947a5a7..1bc89a1048 100644 --- a/runtime/intern_table.h +++ b/runtime/intern_table.h @@ -59,6 +59,54 @@ class Transaction; */ class InternTable { public: + // Modified UTF-8-encoded string treated as UTF16. + class Utf8String { + public: + Utf8String(uint32_t utf16_length, const char* utf8_data, int32_t hash) + : hash_(hash), utf16_length_(utf16_length), utf8_data_(utf8_data) { } + + int32_t GetHash() const { return hash_; } + uint32_t GetUtf16Length() const { return utf16_length_; } + const char* GetUtf8Data() const { return utf8_data_; } + + private: + int32_t hash_; + uint32_t utf16_length_; + const char* utf8_data_; + }; + + class StringHashEquals { + public: + std::size_t operator()(const GcRoot<mirror::String>& root) const NO_THREAD_SAFETY_ANALYSIS; + bool operator()(const GcRoot<mirror::String>& a, const GcRoot<mirror::String>& b) const + NO_THREAD_SAFETY_ANALYSIS; + + // Utf8String can be used for lookup. + std::size_t operator()(const Utf8String& key) const { + // A cast to prevent undesired sign extension. + return static_cast<uint32_t>(key.GetHash()); + } + + bool operator()(const GcRoot<mirror::String>& a, const Utf8String& b) const + NO_THREAD_SAFETY_ANALYSIS; + }; + + class GcRootEmptyFn { + public: + void MakeEmpty(GcRoot<mirror::String>& item) const { + item = GcRoot<mirror::String>(); + } + bool IsEmpty(const GcRoot<mirror::String>& item) const { + return item.IsNull(); + } + }; + + using UnorderedSet = HashSet<GcRoot<mirror::String>, + GcRootEmptyFn, + StringHashEquals, + StringHashEquals, + TrackingAllocator<GcRoot<mirror::String>, kAllocatorTagInternTable>>; + InternTable(); // Interns a potentially new string in the 'strong' table. May cause thread suspension. @@ -119,10 +167,12 @@ class InternTable { void BroadcastForNewInterns(); - // Adds all of the resolved image strings from the image spaces into the intern table. The - // advantage of doing this is preventing expensive DexFile::FindStringId calls. Sets - // images_added_to_intern_table_ to true. - void AddImagesStringsToTable(const std::vector<gc::space::ImageSpace*>& image_spaces) + // Add all of the strings in the image's intern table into this intern table. This is required so + // the intern table is correct. + // The visitor arg type is UnorderedSet + template <typename Visitor> + void AddImageStringsToTable(gc::space::ImageSpace* image_space, + const Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::intern_table_lock_); // Add a new intern table for inserting to, previous intern tables are still there but no @@ -130,11 +180,6 @@ class InternTable { void AddNewTable() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::intern_table_lock_); - // Read the intern table from memory. The elements aren't copied, the intern hash set data will - // point to somewhere within ptr. Only reads the strong interns. - size_t AddTableFromMemory(const uint8_t* ptr) REQUIRES(!Locks::intern_table_lock_) - REQUIRES_SHARED(Locks::mutator_lock_); - // Write the post zygote intern table to a pointer. Only writes the strong interns since it is // expected that there is no weak interns since this is called from the image writer. size_t WriteToMemory(uint8_t* ptr) REQUIRES_SHARED(Locks::mutator_lock_) @@ -145,52 +190,6 @@ class InternTable { REQUIRES(!Locks::intern_table_lock_); private: - // Modified UTF-8-encoded string treated as UTF16. - class Utf8String { - public: - Utf8String(uint32_t utf16_length, const char* utf8_data, int32_t hash) - : hash_(hash), utf16_length_(utf16_length), utf8_data_(utf8_data) { } - - int32_t GetHash() const { return hash_; } - uint32_t GetUtf16Length() const { return utf16_length_; } - const char* GetUtf8Data() const { return utf8_data_; } - - private: - int32_t hash_; - uint32_t utf16_length_; - const char* utf8_data_; - }; - - class StringHashEquals { - public: - std::size_t operator()(const GcRoot<mirror::String>& root) const NO_THREAD_SAFETY_ANALYSIS; - bool operator()(const GcRoot<mirror::String>& a, const GcRoot<mirror::String>& b) const - NO_THREAD_SAFETY_ANALYSIS; - - // Utf8String can be used for lookup. - std::size_t operator()(const Utf8String& key) const { - // A cast to prevent undesired sign extension. - return static_cast<uint32_t>(key.GetHash()); - } - - bool operator()(const GcRoot<mirror::String>& a, const Utf8String& b) const - NO_THREAD_SAFETY_ANALYSIS; - }; - class GcRootEmptyFn { - public: - void MakeEmpty(GcRoot<mirror::String>& item) const { - item = GcRoot<mirror::String>(); - } - bool IsEmpty(const GcRoot<mirror::String>& item) const { - return item.IsNull(); - } - }; - using UnorderedSet = HashSet<GcRoot<mirror::String>, - GcRootEmptyFn, - StringHashEquals, - StringHashEquals, - TrackingAllocator<GcRoot<mirror::String>, kAllocatorTagInternTable>>; - // Table which holds pre zygote and post zygote interned strings. There is one instance for // weak interns and strong interns. class Table { @@ -214,8 +213,10 @@ class InternTable { // Read and add an intern table from ptr. // Tables read are inserted at the front of the table array. Only checks for conflicts in // debug builds. Returns how many bytes were read. - size_t AddTableFromMemory(const uint8_t* ptr) - REQUIRES(Locks::intern_table_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + // NO_THREAD_SAFETY_ANALYSIS for the visitor that may require locks. + template <typename Visitor> + size_t AddTableFromMemory(const uint8_t* ptr, const Visitor& visitor) + REQUIRES(!Locks::intern_table_lock_) REQUIRES_SHARED(Locks::mutator_lock_); // Write the intern tables to ptr, if there are multiple tables they are combined into a single // one. Returns how many bytes were written. size_t WriteToMemory(uint8_t* ptr) @@ -225,10 +226,15 @@ class InternTable { void SweepWeaks(UnorderedSet* set, IsMarkedVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_); + // Add a table to the front of the tables vector. + void AddInternStrings(UnorderedSet&& intern_strings) + REQUIRES(Locks::intern_table_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + // We call AddNewTable when we create the zygote to reduce private dirty pages caused by // modifying the zygote intern table. The back of table is modified when strings are interned. std::vector<UnorderedSet> tables_; + friend class InternTable; friend class linker::ImageWriter; ART_FRIEND_TEST(InternTableTest, CrossHash); }; @@ -239,6 +245,11 @@ class InternTable { ObjPtr<mirror::String> Insert(ObjPtr<mirror::String> s, bool is_strong, bool holding_locks) REQUIRES(!Locks::intern_table_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + // Add a table from memory to the strong interns. + template <typename Visitor> + size_t AddTableFromMemory(const uint8_t* ptr, const Visitor& visitor) + REQUIRES(!Locks::intern_table_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr<mirror::String> LookupStrongLocked(ObjPtr<mirror::String> s) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_); ObjPtr<mirror::String> LookupWeakLocked(ObjPtr<mirror::String> s) @@ -262,9 +273,6 @@ class InternTable { void RemoveWeakFromTransaction(ObjPtr<mirror::String> s) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_); - size_t AddTableFromMemoryLocked(const uint8_t* ptr) - REQUIRES(Locks::intern_table_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - // Change the weak root state. May broadcast to waiters. void ChangeWeakRootStateLocked(gc::WeakRootState new_state) REQUIRES(Locks::intern_table_lock_); diff --git a/runtime/interpreter/interpreter_cache.h b/runtime/interpreter/interpreter_cache.h index b4966fd615..355058f4f6 100644 --- a/runtime/interpreter/interpreter_cache.h +++ b/runtime/interpreter/interpreter_cache.h @@ -25,25 +25,29 @@ namespace art { -class Instruction; class Thread; // Small fast thread-local cache for the interpreter. -// The key for the cache is the dex instruction pointer. -// The interpretation of the value depends on the opcode. -// Presence of entry might imply some performance pre-conditions. +// It can hold arbitrary pointer-sized key-value pair. +// The interpretation of the value depends on the key. +// Presence of entry might imply some pre-conditions. // All operations must be done from the owning thread, // or at a point when the owning thread is suspended. // -// The values stored for opcodes in the cache currently are: +// The key-value pairs stored in the cache currently are: // iget/iput: The field offset. The field must be non-volatile. // sget/sput: The ArtField* pointer. The field must be non-volitile. +// invoke: The ArtMethod* pointer (before vtable indirection, etc). +// ArtMethod*: The ImtIndex of the method. +// +// We ensure consistency of the cache by clearing it +// whenever any dex file is unloaded. // // Aligned to 16-bytes to make it easier to get the address of the cache // from assembly (it ensures that the offset is valid immediate value). class ALIGNED(16) InterpreterCache { // Aligned since we load the whole entry in single assembly instruction. - typedef std::pair<const Instruction*, size_t> Entry ALIGNED(2 * sizeof(size_t)); + typedef std::pair<const void*, size_t> Entry ALIGNED(2 * sizeof(size_t)); public: // 2x size increase/decrease corresponds to ~0.5% interpreter performance change. @@ -59,7 +63,7 @@ class ALIGNED(16) InterpreterCache { // Clear the whole cache. It requires the owning thread for DCHECKs. void Clear(Thread* owning_thread); - ALWAYS_INLINE bool Get(const Instruction* key, /* out */ size_t* value) { + ALWAYS_INLINE bool Get(const void* key, /* out */ size_t* value) { DCHECK(IsCalledFromOwningThread()); Entry& entry = data_[IndexOf(key)]; if (LIKELY(entry.first == key)) { @@ -69,7 +73,7 @@ class ALIGNED(16) InterpreterCache { return false; } - ALWAYS_INLINE void Set(const Instruction* key, size_t value) { + ALWAYS_INLINE void Set(const void* key, size_t value) { DCHECK(IsCalledFromOwningThread()); data_[IndexOf(key)] = Entry{key, value}; } @@ -77,7 +81,7 @@ class ALIGNED(16) InterpreterCache { private: bool IsCalledFromOwningThread(); - static ALWAYS_INLINE size_t IndexOf(const Instruction* key) { + static ALWAYS_INLINE size_t IndexOf(const void* key) { static_assert(IsPowerOfTwo(kSize), "Size must be power of two"); size_t index = (reinterpret_cast<uintptr_t>(key) >> 2) & (kSize - 1); DCHECK_LT(index, kSize); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 027193765a..b3a2bdd936 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -85,7 +85,7 @@ #include "hidden_api.h" #include "image-inl.h" #include "instrumentation.h" -#include "intern_table.h" +#include "intern_table-inl.h" #include "interpreter/interpreter.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" @@ -1484,7 +1484,9 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { } { ScopedTrace trace2("AddImageStringsToTable"); - GetInternTable()->AddImagesStringsToTable(heap_->GetBootImageSpaces()); + for (gc::space::ImageSpace* image_space : heap_->GetBootImageSpaces()) { + GetInternTable()->AddImageStringsToTable(image_space, VoidFunctor()); + } } if (IsJavaDebuggable()) { // Now that we have loaded the boot image, deoptimize its methods if we are running diff --git a/runtime/thread.cc b/runtime/thread.cc index 68c82295a6..51775c69a2 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -3374,8 +3374,13 @@ void Thread::QuickDeliverException() { // Note: we do this *after* reporting the exception to instrumentation in case it now requires // deoptimization. It may happen if a debugger is attached and requests new events (single-step, // breakpoint, ...) when the exception is reported. + // + // Note we need to check for both force_frame_pop and force_retry_instruction. The first is + // expected to happen fairly regularly but the second can only happen if we are using + // instrumentation trampolines (for example with DDMS tracing). That forces us to do deopt later + // and see every frame being popped. We don't need to handle it any differently. ShadowFrame* cf; - bool force_frame_pop = false; + bool force_deopt; { NthCallerVisitor visitor(this, 0, false); visitor.WalkStack(); @@ -3383,7 +3388,8 @@ void Thread::QuickDeliverException() { if (cf == nullptr) { cf = FindDebuggerShadowFrame(visitor.GetFrameId()); } - force_frame_pop = cf != nullptr && cf->GetForcePopFrame(); + bool force_frame_pop = cf != nullptr && cf->GetForcePopFrame(); + bool force_retry_instr = cf != nullptr && cf->GetForceRetryInstruction(); if (kIsDebugBuild && force_frame_pop) { NthCallerVisitor penultimate_visitor(this, 1, false); penultimate_visitor.WalkStack(); @@ -3396,8 +3402,9 @@ void Thread::QuickDeliverException() { << "Force pop frame without retry instruction found. penultimate frame is null: " << (penultimate_frame == nullptr ? "true" : "false"); } + force_deopt = force_frame_pop || force_retry_instr; } - if (Dbg::IsForcedInterpreterNeededForException(this) || force_frame_pop) { + if (Dbg::IsForcedInterpreterNeededForException(this) || force_deopt) { NthCallerVisitor visitor(this, 0, false); visitor.WalkStack(); if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.caller_pc)) { @@ -3405,16 +3412,18 @@ void Thread::QuickDeliverException() { const DeoptimizationMethodType method_type = DeoptimizationMethodType::kDefault; // Save the exception into the deoptimization context so it can be restored // before entering the interpreter. - if (force_frame_pop) { + if (force_deopt) { VLOG(deopt) << "Deopting " << cf->GetMethod()->PrettyMethod() << " for frame-pop"; DCHECK(Runtime::Current()->AreNonStandardExitsEnabled()); // Get rid of the exception since we are doing a framepop instead. + LOG(WARNING) << "Suppressing pending exception for retry-instruction/frame-pop: " + << exception->Dump(); ClearException(); } PushDeoptimizationContext( JValue(), false /* is_reference */, - (force_frame_pop ? nullptr : exception), + (force_deopt ? nullptr : exception), false /* from_code */, method_type); artDeoptimize(this); |