summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dex2oat/linker/image_writer.cc3
-rw-r--r--libartbase/base/zip_archive.h3
-rw-r--r--runtime/arch/arm/instruction_set_features_arm.cc1
-rw-r--r--runtime/arch/arm64/instruction_set_features_arm64.cc254
-rw-r--r--runtime/arch/arm64/instruction_set_features_arm64.h43
-rw-r--r--runtime/arch/arm64/instruction_set_features_arm64_test.cc116
-rw-r--r--runtime/class_linker.cc137
-rw-r--r--runtime/class_linker.h2
-rw-r--r--runtime/class_table.cc8
-rw-r--r--runtime/entrypoints/entrypoint_utils-inl.h8
-rw-r--r--runtime/gc/space/image_space.cc20
-rw-r--r--runtime/intern_table-inl.h62
-rw-r--r--runtime/intern_table.cc40
-rw-r--r--runtime/intern_table.h128
-rw-r--r--runtime/interpreter/interpreter_cache.h22
-rw-r--r--runtime/runtime.cc6
-rw-r--r--runtime/thread.cc19
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);